本文引用的文件
本说明聚焦前端离线地图与 PMTiles 的配置方式,重点解释以下内容:
china.pmtiles。offlineMap.ts 如何构建瓦片源、选择本机文件或内置静态资源、读取自定义样式 JSON。MapLocalSettingModal.vue 如何管理“离线地图瓦片目录”,以及保存后如何通知地图重新加载。readme.md 中栅格地图模板、PMTiles 转换命令和 regInfo.exe 激活说明的含义。public/resource/map 的结构、如何替换 PMTiles 文件或样式,以及如何新增图层或更换离线地图源。该文档既面向开发者,也面向需要调整离线地图数据或样式的业务维护人员。
与离线地图直接相关的代码和资源分布如下:
| 类别 | 路径 | 作用 |
|---|---|---|
| 离线地图工具模块 | ai-electron/frontend/src/core/utils/map/offlineMap.ts |
提供本机离线地图路径、PMTiles Source 实现、样式读取、目录缓存等能力 |
| 地图设置弹窗 | ai-electron/frontend/src/core/layouts/default/setting/components/MapLocalSettingModal.vue |
让用户设置本机离线地图目录 |
| 行为时空地图页面 | ai-electron/frontend/src/trans/views/behaviorSpacetime/index.vue |
使用 PMTiles 加载离线地图,并处理样式解析 |
| 案件站点地图弹窗 | ai-electron/frontend/src/case/views/dm/components/StationMapModal.vue |
在案件场景中复用离线地图能力 |
| 基站查询地图组件 | ai-electron/frontend/src/trans/views/aiBaseStation/components/BaseStationQueryMap.vue |
另一个复用离线地图能力的地图组件 |
| 默认矢量样式 | ai-electron/frontend/public/resource/map/behavior-spacetime-style.json |
内置矢量样式,引用 pmtiles://resource/map/china.pmtiles |
| 栅格地图示例 | readme.md |
提供栅格地图模板、PMTiles 转换命令和 regInfo.exe 使用说明 |
| 授权入口页 | ai-electron/frontend/src/plat/views/authorization/index.vue |
负责设备绑定、机器码获取、离线授权文件和授权校验流程 |
graph TB
User["用户"] --> AuthorizationPage["授权入口页<br/>authorization/index.vue"]
AuthorizationPage --> System["后端服务"]
AuthorizationPage --> BusinessPages["业务页面<br/>behaviorSpacetime/index.vue、StationMapModal.vue 等"]
BusinessPages --> OfflineMapUtils["离线地图工具<br/>offlineMap.ts"]
OfflineMapUtils --> LocalDisk["本机磁盘<br/>china.pmtiles、style.json"]
OfflineMapUtils --> StaticResource["内置静态资源<br/>public/resource/map"]
OfflineMapUtils --> ElectronIPC["Electron IPC<br/>读二进制范围、读文本文件"]
BusinessPages --> MapLibre["MapLibre GL + PMTiles"]
图表来源
章节来源
offlineMap.ts 是离线地图的前端核心工具模块,主要职责包括:
定义离线地图目录键和事件名
OFFLINE_MAP_DIR__offline-map-dir-changed计算内置 PMTiles 地址
getBuiltinPmtilesUrl() 返回 public/resource/map/china.pmtiles 对应的 Web URL。构造本机离线地图路径
getOfflineMapFilePath(fileName) 根据当前配置的离线地图目录拼接文件名。getOfflineMapPmtilesPath() 固定取 china.pmtiles。getOfflineMapStylePath() 固定取 behavior-spacetime-style.json。实现两种 PMTiles Source
ElectronPmtilesSource:用于本机绝对路径,通过 IPC readBinaryFileRange 分块读取 .pmtiles 文件。HttpPmtilesSource:用于 Web 静态资源 URL,通过 HTTP Range 请求加载瓦片。选择 Source 类型
isLocalFilePath() 判断目标是否为本机绝对路径(Windows 盘符、UNC 路径或 POSIX 路径)。createPmtilesSource(target) 按路径类型创建对应 Source。注册离线 PMTiles 源
resolveOfflinePmtilesSources() 优先使用本机目录中的 china.pmtiles,否则使用内置静态资源。读取自定义样式文本
readOfflineMapStyleText() 通过 IPC readTextFile 读取本机目录下的 behavior-spacetime-style.json。管理离线地图目录缓存
getOfflineMapDir() / setOfflineMapDir(path) / clearOfflineMapDir() 操作 localStorage。offline-map-dir-changed 事件,已打开的地图组件监听该事件后重新加载。路径规范化与 URL 转换
isValidOfflineMapPath() 校验路径格式。buildFileUrl() / toLocalMapUrl() 把本机路径转换为内部协议或 file:// URL,供样式源使用。章节来源
该弹窗负责管理“离线地图瓦片目录”,但不直接读写服务端配置:
china.pmtiles,可选放置 behavior-spacetime-style.json。getLocalMapDir() 读取本机配置。saveLocalMapDir(next),成功后调用 setOfflineMapDir(saved) 更新前端缓存。保存成功后派发 offline-map-dir-changed 事件,使已打开的地图组件重新登记瓦片源并刷新。
flowchart TD
Open["打开地图设置弹窗"] --> Load["读取本机地图目录"]
Load --> Pick["用户选择目录或手动输入"]
Pick --> Save["调用 saveLocalMapDir 保存到本机"]
Save --> Cache["setOfflineMapDir 更新前端缓存"]
Cache --> Event["派发 offline-map-dir-changed 事件"]
Event --> Refresh["地图组件重新加载离线地图"]
图表来源
章节来源
该样式文件位于 public/resource/map/behavior-spacetime-style.json,是一个符合 MapLibre Style Spec v8 的样式:
behavior-spacetime-offline-template。behavior-basemap,类型为矢量源。pmtiles://resource/map/china.pmtiles。source-layer 必须与 china.pmtiles 中矢量层一致。注意:仓库根目录 readme.md 中还包含一个“栅格地图配置”示例,其 sources.behavior-basemap.type 是 raster,这与实际默认样式中的 vector 不同。实际项目中应以 public/resource/map/behavior-spacetime-style.json 为准;readme.md 更适合作为生成 PMTiles 时的参考模板。
章节来源
下图展示从用户访问系统到加载离线地图的关键流程:
sequenceDiagram
participant User as "用户"
participant Auth as "授权入口页"
participant Server as "后端服务"
participant Page as "地图业务页面"
participant Utils as "offlineMap.ts"
participant Disk as "本机磁盘或静态资源"
participant Map as "MapLibre GL + PMTiles"
User->>Auth: 打开应用
Auth->>Server: 检查授权状态
Server-->>Auth: 返回授权结果
alt 授权无效或未授权
Auth-->>User: 显示授权卡片
else 授权有效
Auth->>Page: 进入业务页面
Page->>Utils: 获取离线地图目录和 PMTiles 路径
Utils->>Disk: 读取 china.pmtiles 或 style.json
Page->>Map: 注册 PMTiles Source 并加载样式
Map-->>User: 渲染离线地图
end
图表来源
offlineMap.ts 提供了两套 PMTiles Source 实现,并根据目标路径自动选择:
| 场景 | 路径形式 | Source 类型 | 数据来源 |
|---|---|---|---|
| 本机离线地图 | Windows 盘符、UNC、POSIX 绝对路径 | ElectronPmtilesSource |
IPC readBinaryFileRange 从磁盘分块读取 |
| 内置静态资源 | http(s):// 或相对 URL |
HttpPmtilesSource |
HTTP Range 请求 public/resource/map/china.pmtiles |
resolveOfflinePmtilesSources() 的优先级如下:
getOfflineMapDir() 有值且能拼出 china.pmtiles 路径,则注册本机 PMTiles 源。public/resource/map/china.pmtiles 作为兜底源。这样做的意义是:未配置本机目录时,仍可加载内置离线地图;配置了本机目录后,优先使用用户指定的本地 PMTiles 文件。
flowchart TD
Start["开始构建离线 PMTiles 源"] --> CheckDir["检查 getOfflineMapDir()"]
CheckDir --> HasDir{"是否配置本机目录?"}
HasDir -->|是| AddLocal["添加本机 china.pmtiles 源"]
HasDir -->|否| SkipLocal["跳过本机源"]
AddLocal --> AddBuiltIn["添加内置 resource/map/china.pmtiles 源"]
SkipLocal --> AddBuiltIn
AddBuiltIn --> End["返回 PMTiles 源列表"]
图表来源
章节来源
地图页面在 resolveStyle() 中采用以下顺序选择样式:
优先尝试本机自定义样式
china.pmtiles 路径和样式路径,则调用 readOfflineMapStyleText() 读取 behavior-spacetime-style.json。回退到外部模板样式
china.pmtiles。再回退到脚本注入或静态样式 URL
public/resource/map/behavior-spacetime-style.json 拉取默认样式。最终回退到最小背景样式
这种设计确保即使离线样式不可用,地图也不会完全崩溃,而是至少显示一个空白背景。
flowchart TD
Start["解析地图样式"] --> TryOfflineStyle["尝试读取本机 behavior-spacetime-style.json"]
TryOfflineStyle --> OfflineOk{"解析成功?"}
OfflineOk -->|是| UseOffline["使用本机样式"]
OfflineOk -->|否| BuildExternal["构造外部地图样式"]
BuildExternal --> ExternalOk{"是否有本机 PMTiles?"}
ExternalOk -->|是| UseExternal["使用外部模板样式"]
ExternalOk -->|否| TryCustom["尝试脚本注入或静态样式 URL"]
TryCustom --> CustomOk{"可获取样式?"}
CustomOk -->|是| UseCustom["使用自定义样式"]
CustomOk -->|否| Fallback["返回最小背景样式"]
图表来源
章节来源
样式中的 PMTiles 地址通常形如:
pmtiles://resource/map/china.pmtiles
normalizePmtilesSources() 会将样式中的 pmtiles:// 地址转换为:
关键规则:
source.url 必须以 pmtiles:// 开头才会被替换。resolveOfflinePmtilesSources() 注册的 source key 一致。本机路径会被包装成内部协议或 file:// URL,再由 ElectronPmtilesSource 或 HttpPmtilesSource 消费。
flowchart TD
Style["样式中的 pmtiles:// 地址"] --> Normalize["normalizePmtilesSources 处理"]
Normalize --> PathType{"是否配置本机目录?"}
PathType -->|是| LocalKey["使用本机绝对路径作为 key"]
PathType -->|否| StaticKey["使用内置静态资源 URL 作为 key"]
LocalKey --> Register["注册 ElectronPmtilesSource"]
StaticKey --> Register
Register --> MapLoad["MapLibre 加载瓦片"]
图表来源
章节来源
readme.md 中包含三类内容:
栅格地图配置示例
提供一个栅格地图模板,其中 sources.behavior-basemap.type 为 raster,瓦片地址为 pmtiles://resource/map/china.pmtiles。这是生成 PMTiles 时的参考模板,不是本项目默认样式。
metadata.json 示例
描述 MBTiles 元数据字段,如名称、版本、格式、最小缩放级别、最大缩放级别。
PMTiles 转换命令
使用 pmtiles.exe convert light.mbtiles china.pmtiles 将 MBTiles 转为 PMTiles,并用 pmtiles.exe show china.pmtiles 查看内容。
regInfo.exe 激活说明
提供机器码查询、许可证读取、字段查看和有效性验证命令,并列出常见错误码:
0:许可证有效。10001:许可证文件无效。10002:许可证过期。10004:机器码不匹配。前端授权入口页 authorization/index.vue 的流程如下:
checkAuthApi() 检查设备授权状态。.xlts 授权文件后调用 offlineAuthorizationApi 提交离线授权。因此,“许可证有效”是进入业务页面的前置条件。虽然离线地图加载本身由 offlineMap.ts 控制,但用户只有在授权通过后才能到达使用离线地图的业务页面。
flowchart TD
Start["应用启动"] --> BindServer["绑定后端服务地址"]
BindServer --> CheckAuth["调用 checkAuthApi 检查授权"]
CheckAuth --> Authorized{"授权有效?"}
Authorized -->|否| ShowLicenseCard["显示离线授权卡片"]
ShowLicenseCard --> UploadLicense["上传 .xlts 授权文件"]
UploadLicense --> OfflineAuth["调用 offlineAuthorizationApi"]
OfflineAuth --> Recheck["刷新授权摘要"]
Recheck --> LoginOrBusiness["无 token 进入登录页<br/>有 token 进入业务页"]
Authorized -->|是| TokenCheck{"已有登录令牌?"}
TokenCheck -->|是| Business["进入业务页面"]
TokenCheck -->|否| Login["进入登录页"]
图表来源
章节来源
graph LR
OfflineMap["offlineMap.ts"] --> Ipc["Electron IPC"]
OfflineMap --> LocalStorage["localStorage"]
MapLocalSetting["MapLocalSettingModal.vue"] --> OfflineMap
BehaviorSpacetime["behaviorSpacetime/index.vue"] --> OfflineMap
StationMap["StationMapModal.vue"] --> OfflineMap
BaseStation["BaseStationQueryMap.vue"] --> OfflineMap
Authorization["authorization/index.vue"] --> Business["业务页面"]
Business --> OfflineMap
图表来源
offlineMap.ts 是低层工具模块,被多个地图组件复用。MapLocalSettingModal.vue 只负责持久化本机目录,不关心地图具体渲染逻辑。offline-map-dir-changed 事件解耦目录变更与地图刷新。章节来源
建议保持以下结构:
| 路径 | 用途 |
|---|---|
public/resource/map/china.pmtiles |
内置离线 PMTiles 数据文件 |
public/resource/map/behavior-spacetime-style.json |
内置矢量样式文件 |
如果需要在运行时使用本机离线地图,则在用户选择的目录中放置:
| 文件 | 必需性 |
|---|---|
china.pmtiles |
启用本机离线地图时必须存在 |
behavior-spacetime-style.json |
可选,用于覆盖默认样式 |
步骤:
public/resource/map/china.pmtiles。source.url 指向该 PMTiles 文件。source-layer 与新数据中的图层名称一致。注意:readme.md 提供的栅格地图模板是 type: raster,而默认样式是 type: vector。替换矢量 PMTiles 时应以矢量样式为准;替换栅格 PMTiles 时需要改用栅格样式模板。
如果需要让某个地图页面加载不同的 PMTiles 源,可以在样式中修改对应 source 的 url,并在 normalizePmtilesSources() 处理时确保 key 与注册源一致。典型做法:
pmtiles://... 引用瓦片。resolveOfflinePmtilesSources() 注册该 key 对应的 Source。normalizePmtilesSources() 的 overridePmtilesUrl 参数中传入新地址。新增图层时需要注意:
behavior-basemap。source-layer。示例思路:
layers 数组中添加新图层。ElectronPmtilesSource 或 HttpPmtilesSource。推荐按以下顺序排查:
china.pmtiles。source-layer 是否与样式一致。章节来源
离线地图相关初始化通常发生在地图页面或地图组件挂载时:
ensureOfflinePmtilesArchive() 或类似逻辑,将 resolveOfflinePmtilesSources() 返回的源注册到 PMTiles 协议管理器。resolveStyle() 解析样式。offline-map-dir-changed 事件后重新执行上述步骤。ElectronPmtilesSource 使用 readBinaryFileRange 分块读取,避免一次性加载整个 .pmtiles 文件。HttpPmtilesSource 使用 HTTP Range 请求,适合部署环境下的静态资源访问。章节来源
可能原因:
china.pmtiles。public/resource/map/china.pmtiles 缺失。排查建议:
getOfflineMapDir() 是否返回有效路径。getOfflineMapPmtilesPath() 是否指向真实文件。pmtiles.exe show china.pmtiles 验证 PMTiles 文件可读。resolveOfflinePmtilesSources() 返回的 key 一致。章节来源
可能原因:
behavior-spacetime-style.json 语法非法。source-layer。排查建议:
JSON.parse 是否抛出异常。source-layer 是否与 PMTiles 实际图层一致。章节来源
可能原因:
排查建议:
resolveStyle() 是否返回 fallback 样式。章节来源
这里的“禁用”应理解为:用户停留在授权入口页,无法进入包含地图的业务页面。
可能原因:
checkAuthApi() 返回非 200。.xlts 授权文件。排查建议:
checkAuthApi() 响应。regInfo.exe -f licensefile -verify 验证许可证。.xlts。章节来源
可能原因:
isLocalFilePath() 识别。pmtiles:// 地址没有被正确替换。排查建议:
getOfflineMapDir() 返回值。getOfflineMapPmtilesPath() 返回值。isLocalFilePath() 判定结果。pmtiles:// 开头。normalizePmtilesSources() 是否正确重写地址。章节来源
离线地图功能由三个层次组成:
authorization/index.vue 完成设备绑定、机器码获取和离线授权校验。只有授权有效,用户才能进入业务页面。MapLocalSettingModal.vue 和本地存储保存本机离线地图目录;offlineMap.ts 负责路径规范化、Source 选择和样式读取。要扩展或替换离线地图,核心原则是:
source-layer 一致。resolveOfflinePmtilesSources() 注册的 key 一致。isLocalFilePath() 识别。