第08章 - 渲染引擎与三维可视化

GeoLibre 有四种渲染引擎:MapLibre GL JS(默认)、Mapbox GL JS、CesiumJS、ArcGIS Maps SDK for JavaScript。同一个工程可以用其中任意一种绘制主图,也可以用任意一种绘制分屏里的任一面板。

这件事之所以成立,是因为状态仓库与渲染引擎彻底解耦:store 里只有普通的 GeoLibreLayer 记录、偏好设置与 MapViewState,没有任何一个 SDK 自己的对象。四个引擎各自实现同一个 MapEngine 接口,负责相机、能力声明、拾取、控件、落点、框选与截图。

本章先讲这套引擎模型,再逐个讲四种引擎的凭据、能力与限制,然后进入三维内容(3D Tiles、点云、高斯泼溅、地形、球面与椭球、Cesium 的时间与相机),最后是插件适配、性能、排错与验证方式。

说明:本章基于 GeoLibre 3.4。菜单与参数名保留英文原文。素材来自 docs/user-guide/rendering-engines.md、docs/maplibre-renderer.md、docs/mapbox-renderer.md、docs/cesium-renderer.md、docs/arcgis-renderer.md、docs/cesium-parity-testing.md、docs/architecture.md、packages/map/src/map-engine.ts 与 apps/geolibre-desktop/src/lib/add-data-renderer.ts。


8.1 引擎模型:一个接口,四个实现

8.1.1 为什么能力要用布尔量声明

packages/map/src/map-engine.ts 里的 MapEngine 接口开头写着一句关键注释:“以 capabilities 为准,不要按 kind 分支”。原因是按引擎名判断会同时错两个方向——它会把 Cesium 原生就能做的相机与地形工作藏起来,也会在出现第五个引擎时立刻失效。

能力集是冻结的常量对象(Object.freeze),因为它是关于引擎的事实而不是设置:所有 MapController 实例共享同一个对象,一次写入会把所有实例的认知一起改掉。

能力字段 含义 谁在读它
styleSpec Mapbox Style Spec 可用:setStyle、paint 属性编辑、样式图层 id、样式导入导出 样式面板、SLD/QML 导出
nativeMapInstance getMap() 返回活的 MapLibre 地图 AI 目标检测、segment-everything、直接写 maplibre-gl 的插件
customLayers 能承载 MapLibre CustomLayerInterface 图层 自定义图层插件
deckOverlay 能承载 @deck.gl/mapbox 的 MapboxOverlay Deck.gl Layer、glTF、DuckDB、3D Tiles、LiDAR
terrain 三维地形可开关、可夸张 Controls → Terrain
picking identifyFeatures 能返回要素 Identify
onMapDrawing 能在地图表面拖放/绘制 手动落点、栅格子集框选
domControls 能挂载 IControl DOM 控件 内置控件、插件面板
screenOverlays 屏幕空间投影足够精确,可用 SVG 自绘覆盖框 栅格子集面板
flatProjection 工程要求 mercator 时确实画平面 球面专属功能的开关判断
terrainSource 能装自定义 DEM(COG URL / 本地文件 / 已加载的栅格图层) 地形设置里的 “Terrain source”
ionTerrain 能用 Cesium Ion 地形资产 地形对话框里的 Ion 资产字段
nativeZarr 引擎自己从图层记录画 Zarr Zarr 面板是否挂载
nativeDataSources 用自家数据源加载器加载 KML/KMZ、CZML、ion 资产 Add Data 的 3D layers 组
deferredEngineReady 首次加载完成才对外声明就绪 需要等待引擎的 UI
measureTool 内置测量控件的草图能画出来 Controls → Measure
controlLayerPanels 栅格与 PMTiles 的 IControl 面板可用 Add Raster / PMTiles 面板
supportedLayerKinds 每种图层类型的处置:native / plugin / unsupported 图层支持矩阵

接口本身就是这份能力表(节选自 packages/map/src/map-engine.ts,字段顺序与原文一致):

export interface MapEngineCapabilities {
  readonly styleSpec: boolean;
  readonly nativeMapInstance: boolean;
  readonly customLayers: boolean;
  readonly deckOverlay: boolean;
  readonly terrain: boolean;
  readonly picking: boolean;
  readonly onMapDrawing: boolean;
  readonly domControls: boolean;
  readonly screenOverlays: boolean;
  readonly flatProjection: boolean;
  readonly terrainSource: boolean;
  readonly ionTerrain: boolean;
  readonly nativeZarr: boolean;
  readonly nativeDataSources: boolean;
  readonly deferredEngineReady: boolean;
  readonly measureTool: boolean;
  readonly controlLayerPanels: boolean;
  readonly supportedLayerKinds: SupportedLayerKinds;
}

MapLibre 的能力常量几乎全开,只有四项为 false:ionTerrain、nativeZarr、nativeDataSources、deferredEngineReady——因为它通过自己的控件与同步加载路径达成同样的效果,而不是靠原生加载器。常量被 Object.freeze 冻结,因为 readonly 只在类型层生效,而外部插件是以纯 JavaScript 加载、通过插件 API 拿到这个对象的。

8.1.2 主图与分屏的引擎选择

引擎不是全局开关,而是两个层级的字段:

字段 位置 作用
primaryRenderer 工程顶层 主画布用哪个引擎;默认值不写入工程
SecondaryMapView.viewKind 分屏面板记录 该面板用哪个引擎
MapGridLayout 工程 rows、cols、syncView

切换任一个值都会卸载上一个引擎(而不是把它藏起来继续吃 GPU),再把新引擎接到同一份工程状态上。所以:

  • 相机、图层、分组、可见性、透明度与大部分样式在切换后仍在;
  • 不支持的图层留在工程里,切回兼容引擎时回来;
  • 每个面板的引擎与视图状态都随工程保存。

面板 0 永远是主图,其余面板共享同一份底图与同一份 layers,但可以各自覆盖哪些图层可见;syncView 打开时,平移缩放任一面板会镜像相机到其他面板。网格布局在记录里长这样:

export interface MapGridLayout {
  /** Grid rows (>= 1). */
  rows: number;
  /** Grid columns (>= 1). `rows * cols` is the total number of panes. */
  cols: number;
  /** When true, all panes share a synchronized camera. */
  syncView: boolean;
}

提示:四种引擎的自动化名称是固定的:maplibre、mapbox、cesium、arcgis。Python API、MCP 工具、iframe API、工程格式与插件清单都接受这四个名字,写脚本时不要用界面上的显示名。

8.1.3 引擎边界是编译期强制的

packages/map/src 里,以引擎命名的文件(arcgis-*、cesium-*、mapbox-*,以及 MapLibre 的 layer-sync、map-controller、MapCanvas)不允许 import 另一个引擎的文件;两个引擎共用的代码放在按功能命名的中立模块里(pmtiles-archive.ts、protocol-tiles.ts、gl-style-compiler.ts、feature-style.ts 等)。

这条规则由 ESLint 的 local/no-cross-engine-imports 强制(eslint-rules/no-cross-engine-imports.mjs)。它解释了一个常见现象:为什么某个功能只在部分引擎上有——跨引擎共享需要显式抽取到中立模块,而不是顺手 import 一下就完事。


8.2 四种引擎能力对比

8.2.1 总览

维度 MapLibre Mapbox Cesium ArcGIS
定位 默认、功能与插件支持最广 Mapbox 样式与服务 原生三维球 ArcGIS 服务与 Esri 场景
是否随包分发 是 否(选中时加载) 否(首个画布挂载时加载) 否(首个面板挂载时从 CDN 加载)
凭据 不需要 必须 Mapbox access token 可选 ion token 可选 ArcGIS API key
Style Spec 有 有(共享大部分) 无 无(逐要素求值后烘焙)
平面三维地形 有 有(Mapbox 地形) 有 有(World Elevation 或 COG)
自定义 COG 地形源 有 无(设置对话框隐藏该节并说明原因) 有(含 Ion 地形资产) 有(EPSG:3857/4326)
原生 3D Tiles deck.gl 叠加层 deck.gl 叠加层 原生 deck.gl 叠加层(限主平面图/局部场景)
点云 LiDAR 图层 有 有 无该面板(原生走 3D Tiles/COPC) 有(deck.gl)
高斯泼溅 有 无 原生(KHR_gaussian_splatting 瓦片) 无
KML/KMZ 宿主导入器 宿主导入器 原生 KmlDataSource 宿主导入器
CZML / Cesium ion 无 无 有 无
Zarr 原生绘制 无(控件) 控件(@carbonplan/zarr-layer) 原生(zarr-cesium) 原生(平铺影像)
非 Web Mercator 平面投影 有(任意平面 CRS) 无 无(球面/2D/Columbus) 有(WKID)
混合模式(Blend) 有 不复现(面板会说明) 无 有(SDK blendMode)
分屏承载 有 有 有 有(叠加层受限)
开源许可 MIT Mapbox ToS(非开源) Apache 2.0 Esri 条款

8.2.2 数据源支持矩阵

这张表来自 add-data-renderer.ts 的三个”不支持集合”(详细解析见第 5 章 5.5.5):

数据源 MapLibre Mapbox Cesium ArcGIS
MBTiles 有 无 有 有
高斯泼溅 有 无 无 无
Cesium ion / CZML 无 无 有 无
STAC 有 有 有 无
Video 有 有 有 无
LiDAR 有 有 无 有
3D Tiles / glTF / DuckDB / Deck.gl 有 有 视能力 需 deck.gl 叠加层

ArcGIS 的 deck.gl 源还有一个附加条件:只在主平面图或局部三维场景生效;全局球面场景与分屏面板不承载该叠加层,菜单与图层徽标会反映这条限制。


8.3 切换主图与分屏混排

8.3.1 切换主图

View → Rendering engine → MapLibre / Mapbox / ArcGIS / Cesium。

GeoLibre 会替换活跃画布,而不是把旧引擎藏在后面——旧引擎被卸载,显存随之释放。切换后:

  • 相机、图层、分组、可见性、透明度与大部分样式保留;
  • 所选引擎写进工程,重开时恢复;
  • 引擎专属的图层类型与工具保持原状,切回兼容引擎时重新出现。

8.3.2 分屏混排

View → Split View 创建多图布局。每个面板自己的渲染引擎菜单可以为该面板选四种引擎之一。

选项 行为
相机同步 所有面板跟随主图相机(syncView)
相机独立 各面板保持自己的视图
引擎独立 每个面板可用不同引擎

典型用法是把 2D 与 3D 并排比较,或同一份数据在两个引擎下对照渲染差异。每个面板的引擎选择与视图状态随工程保存。

8.3.3 相机同步按地面分辨率

Cesium 的相机与 MapLibre 的 MapViewState 不是同一套参数:前者是米制距离与相对地平线的俯仰,后者是缩放级别与相对天底的俯仰、加上方位角。换算模块是 packages/map/src/cesium-camera.ts。

关键取舍:按地面分辨率(米/像素)对齐,而不是按 zoom 级别对齐。这样即使两个面板高度不同,屏幕比例尺也能保持一致。

同步是双向的:面板从共享 mapView 取初值,把 store 变化应用到球面,也把球面自己的移动写回;同时有一个容差检查抑制 “应用 → moveEnd → 又应用” 的回声,避免抖动。


8.4 凭据与加载方式

8.4.1 三个凭据字段

Settings → Environment Variables 提供三个字段:

字段 引擎 是否必需
Mapbox token Mapbox 必需;缺失时显示设置说明
Cesium Ion token Cesium 可选
ArcGIS API key ArcGIS 可选

三条共同的约定:

  1. 存在当前设备上,不写进共享的工程文件(桌面走系统凭据存储,Web 走 localStorage)。
  2. 改动会重建对应引擎的地图实例。
  3. 报错信息里会抹掉访问令牌。

8.4.2 各引擎的加载时机

引擎 何时加载 体积/来源 离线行为
MapLibre 随应用打包 无外部请求 完全可用
Mapbox 首个 Mapbox 面板挂载时 独立 chunk,约 1.91 MB(gzip 532 KB,Mapbox GL JS 3.30.0) 首次需网络
Cesium 首个 Cesium 画布挂载时 Workers/Assets/Widgets 随应用暂存,由 CESIUM_BASE_URL 解析 首次需取暂存资源
ArcGIS 首个 ArcGIS 面板挂载时 从 https://js.arcgis.com/<version>/@arcgis/core/… 拉模块 首次需网络;之后 Service Worker 缓存

Mapbox 的 JS 与 CSS 在生产构建里单独分块,并排除在 PWA 的初次预缓存之外——因此 MapLibre 启动不会下载 Mapbox 引擎。但桌面与 Web 的发行产物里仍然包含它(@geolibre/map 的运行时依赖),只是没有打开 Mapbox 面板之前不会运行任何 Mapbox 代码、也不会联系任何 Mapbox 服务。


8.5 MapLibre:默认引擎

8.5.1 定位

MapLibre GL JS 随每个发行版打包,不需要账号或访问令牌,主图与分屏面板都能用。新工程默认用它;分屏面板省略 viewKind 时也意味着 MapLibre。

它的功能与插件兼容性最广,因为应用里的很多工具本来就写在 MapLibre 的样式 API 之上。

8.5.2 支持的路径

  • GeoJSON 与导入的矢量数据:单值、分类、分级、规则、表达式、比例、热图、聚合、图案、标注、拉伸;
  • 栅格与影像源:XYZ、WMS、WMTS、COG/GeoTIFF、图像与视频叠加、栅格 PMTiles、桌面 MBTiles;
  • 矢量瓦片与矢量 PMTiles/MBTiles,含导入的 Mapbox 样式与 ArcGIS 矢量瓦片服务;
  • 3D Tiles、I3S、LiDAR/COPC 点云、高斯泼溅、三维模型、Zarr、NetCDF/HDF、deck.gl 可视化图层——通过 GeoLibre 的专用渲染器与叠加层;
  • 要素拾取、选择、编辑、测量、范围绘制、可拖动落点、打印与截图、以及完整的 MapLibre 向插件面。

8.5.3 投影:平面、球面与任意 CRS

在默认的 Web Mercator 之外,MapLibre 引擎还有两条路:

① 球面:Controls → Globe 在墨卡托与球面之间切换;Controls → Terrain 给两种视图都加上高程。这是一个 MapLibre 样式地图,不是原生三维球——要原生球面与 Cesium 原生场景格式请用 Cesium 引擎。

② 任意平面 CRS(MapLibre GL JS 6.15,issue #1128):Settings → Map → Map coordinate reference system 选一个,保存为 preferences.map.mapCrs;缺省即默认的 Web Mercator 地图或球面。

预设覆盖:

天体/投影 预设
地球、月球、火星 极射赤面投影 + 简单圆柱投影
水星、金星、Io、Europa、Ganymede、Callisto、Titan、Pluto、Charon 简单圆柱投影

每个预设都带一份服务端已按该格网渲染的底图:地球用 NASA GIBS,其他天体用 USGS Astrogeology,直接读取而不是重投影到 Web Mercator。选预设同时会设定天体,因此量算使用该天体的半径。Custom EPSG code… 接受由内置 EPSG 表解析出的任意代码,但它没有底图,瓦片格网会拟合到它所映射的世界区域。

在其他 CRS 下,行为有明确的分界:

图层 其他 CRS 下
GeoJSON 绘制,由 MapLibre 的 Worker 里用 proj4 投影
WMS 与 ArcGIS export 图层 按瓦片在该 CRS 下的框请求(WMS 1.1.1,SRS=<code>);服务必须支持该 CRS
行星预设 按服务能读的标签请求:简单圆柱用 EPSG:4326,极地格网用 EPSG:32661/EPSG:32761
图像与视频叠加 用四角坐标绘制
XYZ、矢量瓦片、MBTiles、Wayback 不绘制(瓦片是 Web Mercator)
共享底图 不绘制
自己加瓦片或 deck.gl 叠加的插件 未适配

此外,球面按钮会被隐藏,地形关闭,受限边界设置被搁置(MapLibre 会把视图限制在 CRS 内)。切换 CRS 会重建样式并框住新地图的区域;打开工程仍保留其保存的视图。

注意:其他 CRS 下的 WMS 请求是按瓦片框逐块请求再拼接的,所以服务端必须支持你选的 CRS。一个只支持 EPSG:3857 的 WMS 在 Robinson 投影下会整片空白,这不是网络问题。

8.5.4 架构

MapController 是 MapEngine 的 MapLibre 实现。packages/map/src/layer-sync.ts 把工程图层记录协调成原生 source 与样式图层;为 MapLibre 不直接绘制的格式注册协议或共享叠加层的专用控件则另在其上。可复用的无头(headless)部分发布为 @geolibre/map。


8.6 Mapbox GL JS

8.6.1 凭据与底图

把 token 粘进 Settings → Environment Variables → Mapbox token,点 Save Settings。它与 Cesium token 一样存在本机、在工程文件之外。也可以在开发服务器环境里设置 MAPBOX_TOKEN。

  • token 变化会重建 Mapbox 地图;
  • 缺 token 时显示设置说明;
  • 地图加载错误会抹掉访问令牌;
  • 使用受该 token 所属账号约束,并适用 Mapbox 的条款与用量计费。

新工程的 Mapbox 渲染器默认用 Mapbox Standard。从 Layers 面板或 Add Data 打开共享的 Basemaps 面板可以选择 Mapbox 样式(含 Streets)、公开样式或堆叠栅格底图。Mapbox 激活时,所选样式保存为 preferences.map.mapboxStyleUrl,不影响共享的 MapLibre/Cesium 底图;所有 Mapbox 面板共享该选择。

8.6.2 支持的路径(要点)

  • 原生 GeoJSON,含矢量导入器物化出的数据:点、线、面、拉伸、标注、数据驱动颜色、不透明度与过滤;
  • 远程矢量 PMTiles(Mapbox GL JS 原生档案读取器);
  • LiDAR(LAS/LAZ、COPC)与标准 3D Tiles(deck.gl 叠加层);
  • Add Data → Deck.gl Layer 的每种类型,以及 3D Model 背后的 scenegraph 类型、矢量图层的 Z 值与图表渲染——都走同一个共享的交错 deck.gl 叠加层;
  • Add Data → DuckDB 的查询图层(面板自己的 deck.gl 叠加层);
  • Zarr(含 STAC Zarr 资产)与带时间轴的 NetCDF/HDF、Kerchunk 立方,由 @carbonplan/zarr-layer 绘制——一个同时面向 Mapbox 与 MapLibre(球面与墨卡托)的 CustomLayerInterface 实现;
  • HTTP(S) 栅格瓦片(XYZ、WMS、WMTS)、带命名 source layer 的矢量瓦片、地理配准图像/视频源;
  • 共享的图层/分组可见性、不透明度与顺序;同步或独立的相机;墨卡托/球面投影与 Mapbox 地形;
  • 要素拾取、选择高亮、范围绘制、可拖动落点、引擎级图像捕获。

8.6.3 Mapbox 专属的取舍

项 行为
MBTiles / 高斯泼溅 Add Data 里可见但禁用,带 Mapbox 兼容性提示
KML/KMZ 走宿主 KML 导入器(与拖拽同一条路径)
离线 PMTiles 底图 仅有 MapLibre 实现;Mapbox 面板会回退到默认底图并打控制台警告
自定义地形源(COG) 无 raster-dem 源可承载,地形设置里的 “Terrain source” 一节被隐藏并说明原因
混合模式 不复现,Style 面板在设置了混合模式的图层上会说明
大 GeoJSON(> 50,000 要素) 保留 mapbox-gl 自己的 GeoJSON source(它已在 Worker 里用 geojson-vt 分块)
球面按钮 Mapbox 没有自己的球面控件,引擎挂一个替身复刻 MapLibre 的按钮标记
归属控件 不可隐藏;Mapbox 画自己的 logo;Maptoolkit logo 仅 MapLibre
Add Data 时序 菜单会等 Mapbox 加载完成再接受导入,早期点击不会丢请求

性能对照(仓库文档给出的真实测量,200,000 个点):

操作 Mapbox MapLibre
添加图层 约 2.1 s 约 1.5 s
重设样式 约 0.9 s 约 0.8 s
数据编辑 约 2.1 s 约 1.2 s
最长主线程卡顿 约 0.8 s 约 0.85 s

警告:GeoLibre 自身是 MIT,但 Mapbox 渲染器依赖的 Mapbox GL JS v3 不是开源软件,按 Mapbox 服务条款分发与计费。它是 @geolibre/map 的运行时依赖,因此 npm 消费者与桌面/Web 发行版都包含它——但在你打开 Mapbox 面板之前不会有任何 Mapbox 代码运行。商用前请自行核对条款。


8.7 CesiumJS:原生三维球

8.7.1 无 token 也能用

球面不需要 Cesium ion token 就能工作:

  • 把工程底图当作基础影像铺上去;
  • 用无密钥的 Terrarium 高程(或配置的 COG)作地形;
  • 显示一条指向 token 设置的提示。

token 带来三样东西:Cesium World Terrain、Ion World Imagery(作为”底图没有栅格形式”时的回退),以及 ion 托管资产的访问权。Web 版内置一个演示 token;桌面与移动版需要你自己的。

token 填在 Settings → Environment Variables → Cesium Ion token,存在本机。开发者也可以在构建时设置 CESIUM_TOKEN 或 VITE_CESIUM_TOKEN,或在运行时通过环境变量界面注入而不重新构建。

8.7.2 支持的路径

  • 带样式的 GeoJSON:分类、分级、规则、表达式、比例、标记、聚合、标注、图案、拉伸、Z 值感知;标注遵循锚点、偏移、最大宽度与数据定义的字号/颜色/不透明度/可见性;
  • XYZ、WMS、WMTS、COG、栅格 PMTiles、本地 MBTiles、图像叠加,以及其他通过 GeoLibre 协议桥接的瓦片源;
  • 矢量瓦片、矢量 PMTiles、矢量 MBTiles——通过一个隐藏的 MapLibre 渲染器”披挂”上去,以保留其 Style Specification 输出;
  • 原生 3D Tiles、Cesium ion 瓦片集与影像、CZML、KML/KMZ、I3S 场景图层、受支持的点云、高斯泼溅瓦片集;
  • 球面原生拾取与高亮、范围绘制、可拖动落点、截图、打印布局、视频与相机巡游、地形感知的高程剖面、环境效果。

8.7.3 标注与符号的边界

能力 球面上
字段/表达式标注、光晕、锚点、偏移、最大宽度 有
数据定义的字号、颜色、不透明度、可见性 有
旋转 无
沿线绘制文字 无(线标注落在中点)
碰撞避让 无(始终允许重叠)
优先级表达式 无
地球背面的点与标注 隐藏(按地平线做深度测试,见 cesium-horizon.ts)
热图渲染器 无球面形态,按普通圆绘制

8.7.4 图层同步与逐要素求值

CesiumLayerSync(packages/map/src/cesium-layer-sync.ts)像 MapController.syncLayers 那样把 GeoLibreLayer[] 协调到球面上,并复用同一套逐面板可见性覆盖与分组效果。

GeoJSON 的逐要素样式由 createFeatureStyleResolver(packages/map/src/feature-style.ts,issue #2278)求值:它用样式规范引擎评估与 2D 地图相同的那批 MapLibre 表达式——单值、分类、分级、带逐规则符号覆盖的规则、表达式模式、simplestyle 属性、比例尺寸、米制描边——因此面填充与轮廓、线宽与箭头装饰、圆半径、分类标记精灵、填充图案、图层缩放范围(共享的 DistanceDisplayCondition)都与 2D 地图一致。

几个实现上的取舍值得知道:

  • 纯点图层若 pointRenderer: "cluster",用数据源的 EntityCluster 聚合,气泡与缩写计数按 2D 聚合图层的样式绘制,并在超过 clusterMaxZoom 后关闭;
  • 纯点图层超过 50,000 要素时不再用实体,改用一个 PointPrimitiveCollection,图元在 id 上携带要素引用,因此拾取、高亮与过滤仍然可用(cesium-points.ts,issue #2282);
  • 瓦片化的矢量图层通过一只隐藏的单瓦片 MapLibre 地图披挂:逐瓦片 jumpTo 到中心、等 idle、读回画布,再经 ProtocolImageryProvider 喂给 Cesium,于是 Style Spec 从未被重新实现;
  • 披挂内容是平面的,标注按瓦片放置,字形来自远程字体主机——因此离线查看本地档案时披挂没有文字,披挂内容上的拾取也被跳过。

8.7.5 场景、时间与相机

  • 球面提供 3D、2D 与 Columbus 三种场景模式,以及地形与原生球面光照;
  • 相机被翻译成与 GeoLibre 共享的经度、纬度、缩放、方位角、俯仰;
  • 混合面板按地面分辨率同步(见 8.3.3)。

时间维度上,CZML 是 Cesium 原生的”动态场景”格式:轨道、轨迹、车辆路径这类随时间变化的场景以文档形式载入,由球面的时钟驱动。时间轴插件在各引擎下的适配不同(见 8.10)。

8.7.6 插件控件在球面上的落法

球面上的插件控件拿到的是一张形状像 MapLibre 的地图(issue #3088):相机、指针事件与 DOM 作用于球面,而它的 Style Spec 调用(addSource、addLayer、setPaintProperty、getStyle…)被记录进一份从不真正绘制的样式。这份样式通过两条路到达球面:

  1. 插件镜像进 store 的图层(Web Services 的 WMS 或瓦片图层、STAC 足迹)由球面的图层同步按 store 记录绘制;
  2. 其他 GeoJSON 图层(格网、经纬网、选择轮廓、绘制预览)作为贴地实体绘制:填充、线、圆、文字都支持过滤、缩放范围、数据驱动与缩放表达式、以及逐要素的文字锚点与偏移;线与面的边遵循等角航线,与 2D 地图上的直段一致;越过地平线的点与标注不画。

不支持的:图标、填充图案、拉伸、沿线文字;标注没有碰撞处理。自定义图层没有 WebGL 上下文可渲染,addLayer({ type: "custom" }) 会抛错,需要它的控件无法挂载——这正是 LiDAR 与高斯泼溅面板在球面上被禁用的原因。

图层级事件(map.on("click", layerId, …)、mouseenter、mouseleave)与点的 queryRenderedFeatures 由实际绘制它的那条路回答,包括 MapLibre 为 store 图层派生的 layer-<id>-fill 这类 id。dragPan.disable() 与 scrollZoom.disable() 会暂停球面自己的相机输入,好让控件的框选或画线拿到拖拽。

8.7.7 加载与架构

Cesium 的 JS 只在第一个 Cesium 画布挂载时加载。它的 Workers、Assets、Widgets 与应用一起暂存,由 CESIUM_BASE_URL 解析;切换到别的引擎会卸载画布并释放渲染资源。

CesiumEngine 实现共享的 MapEngine 接口,CesiumLayerSync 把工程图层翻译成 Cesium 数据源、影像图层与图元。

CesiumJS 是 Apache 2.0 开源;Cesium ion 是独立托管服务,有自己的账号条款与用量限制。


8.8 ArcGIS Maps SDK for JavaScript

8.8.1 什么都不打包

@arcgis/core 解包后 84 MB、18,810 个文件。打包进构建会让 Web 构建、Python wheel 与桌面安装包各涨几十兆。因此引擎在第一个 ArcGIS 面板挂载时,从 Esri 的版本化 ES 模块 CDN 导入所需模块(https://js.arcgis.com/<version>/@arcgis/core/…),随包只发 packages/map/src/arcgis-*.ts 的适配代码。

由此产生四条后果:

  1. 第一个 ArcGIS 面板需要网络,桌面版也一样。之后 Service Worker 会缓存 SDK 模块与样式表(geolibre-arcgis-sdk),SDK 本身可以离线启动;Esri 底图、瓦片服务与远程图层仍然需要网络。
  2. SDK 版本钉在 packages/map/src/arcgis-sdk.ts 的 ARCGIS_SDK_VERSION,升级是一次有意的改动。
  3. Esri 把 ES 模块 CDN 定位为原型路径,每个会话会打印一次 “Only use ES modules from ArcGIS CDN for testing”;其受支持的生产路径是 npm 构建,而这正是本集成为控制体积所回避的。首次加载会拉几百个小模块,因此第一个 ArcGIS 面板比 Mapbox 慢。
  4. 桌面应用(tauri.conf.json)与 Docker 镜像(docker/nginx.conf)的 CSP 在 script-src 与 font-src 里放行 https://js.arcgis.com/;SDK 的样式表以文本形式取回后内联。

若 SDK 无法从 CDN 加载,地图横幅提供 Retry,它会重载页面——浏览器会把失败的模块导入保留到页面生命周期结束,只有重载才能重新取。

8.8.2 有 key 与无 key

渲染器不需要 key 就能工作:

  • 新工程显示无密钥的 Esri World Imagery 作 ArcGIS 底图;
  • 从共享 Basemaps 面板选的底图会被翻译成 SDK 能绘制的瓦片(与三维球用的是同一套翻译),没有瓦片形式的样式以 World Imagery 回退;
  • 你自己的图层走 SDK 自己的图层类。

ArcGIS API key 带来 Esri 的底图样式。创建路径:ArcGIS 账号(ArcGIS Online 或 ArcGIS Location Platform)→ Content → New item → Developer credentials → API key credentials,勾选 Basemaps 权限,然后把 key 粘进 Settings → Environment Variables → ArcGIS API key。

有 key 时,新工程的 ArcGIS 渲染器用 ArcGIS Streets;Change background 提供 Streets、Navigation、Topographic、Light Gray、Dark Gray、Imagery、Imagery (no labels)、Oceans、Outdoor、OpenStreetMap。选择保存为 preferences.map.arcgisBasemap(例如 arcgis/streets、arcgis/imagery、osm/standard);若此时又从共享 Basemaps 面板选底图,这个覆盖会被清除,面板重新跟随共享底图。

8.8.3 支持的路径与”烘焙”策略

SDK 没有 MapLibre Style Spec,所以引擎的做法的逐要素求值后烘焙:它用样式规范引擎评估 @geolibre/core 为 2D 地图构建的同一批 MapLibre 表达式,把答案烘进要素,再把图层的渲染器设为”按符号键取值”的唯一值渲染器。于是:

  • 点、线、面符号,填充与描边不透明度,圆半径,标注(字段或表达式、字号、颜色、光晕、位置、偏移、旋转、大小写变换),以及分类、分级、规则、表达式、simplestyle 五种数据驱动颜色都能渲染;
  • 图层过滤、快速过滤、时间轴过滤与嵌入过滤在要素到达 SDK 之前同样被逐要素求值;
  • 依赖缩放的表达式(米制描边、逐规则缩放范围)在整数缩放变化时重新求值;
  • 符号化”附加包”用伴生图层绘制:反向填充做成”世界蒙版挖洞”(并被夹到 Web Mercator 纬度上限)、几何生成器(质心、包围盒、凸包、缓冲区)自成一套填充/描边/比例圆、线装饰用 CIM 标记线(仅平面 MapView,场景里 SDK 不画 CIM 线符号)、标注去重用一条”仅标注”图层标注聚合点。

服务能力的对接也有明确规则:

服务/形态 处理
FeatureServer 图层 走 SDK 的 FeatureLayer;过滤(快速/表达式/时间/嵌入)变成服务的 SQL definitionExpression
服务无 SQL 形式的过滤 在地图横幅报告,服务按无过滤绘制
MapServer 单个子图层(MapServer/2) 也当 FeatureLayer
缓存 MapServer / ImageServer 存为瓦片模板;动态的(子图层、渲染规则、无可用缓存)存为 /export / /exportImage 的 bbox 模板
要素回读 越过服务单次记录上限分页,最多 50 次请求
矢量瓦片模板无法表达的({bbox-epsg-3857}、TMS、{-y}、{quadkey}、512 px、源 minzoom/maxzoom、多模板) 走自定义瓦片图层,像 MapLibre 那样逐瓦片请求,裁剪 512 px 瓦片并在 maxzoom 之后过缩放

8.8.4 二维与三维

SDK 用两个不同的视图类画平面地图与三维场景,因此 ArcGIS 面板会按工程的地图偏好挑一个,并在选择变化时重建视图(相机随之带过去):

投影 地形 视图
球面 开或关 SceneView,global(三维球)
墨卡托 开 SceneView,local(平面三维图)
墨卡托 关 MapView(平面二维图)

新工程用球面投影,所以 ArcGIS 面板默认打开就是三维球。 指北针下方的球面按钮切换投影(与 MapLibre 一样,分屏面板的按钮只切自己),Controls → Terrain 开关地形。

场景里的关键行为:

  • 相机可俯仰(右键拖动或工程保存的 pitch),受工程最大 pitch 限制;状态栏显示相机海拔;
  • 光照跟随相机(SDK 的虚拟光照),因此可见地图整体被照亮——SDK 默认的模拟太阳会让地球一部分停在夜面;
  • 地形铺在 Esri 的 World Elevation 服务上,不需要 API key;
  • Controls → Terrain exaggeration 既缩放高度,也接受 EPSG:3857 或 EPSG:4326 的 COG DEM(本地或远程,与其它引擎共用读取器;本地文件留在设备上)。该来源与夸张系数在本次会话内跨平面图、局部场景、球面切换保持,但不保存进工程;DEM 缺像素与 COG 之外区域按 0 米处理;Use global terrain 恢复 Esri 的 World Elevation;
  • 样式开了 3D extrusion 的面图层画成三维体块,高度、基高与颜色与 MapLibre 的 fill-extrusion 一致;在二维 MapView 上仍是平涂;
  • 3D (Z values) 把矢量坐标放到其绝对海拔,按配置的垂直缩放与偏移;选择高亮使用同一套变换后的坐标。

8.8.5 ArcGIS 的投影

MapLibre 与 Mapbox 只画 Web Mercator 与球面,而 SDK 能在它按 well-known ID 认识的任意投影坐标系里画平面 MapView(issue #2708)。

Settings → Map → ArcGIS map projection 提供:

投影 WKID
Spilhaus 54099
Equal Earth 8857
Robinson 54030
Winkel Tripel 54042
Mollweide 54009
Plate Carrée 4326
北极极射赤面 3995
南极极射赤面 3031
Custom WKID… 任意其他 Esri 或 EPSG 代码

选择保存为 preferences.map.arcgisWkid;缺省即 Web Mercator。选投影会关掉球面(只有平面图用得上这个投影),球面按钮仍可在球面与投影之间来回切。

其他投影下的行为:

  • 矢量图层(GeoJSON、要素服务、符号化附加包的伴生图层)由 SDK 的 projectOperator 在客户端重投影(该算子只随投影地图一起加载);
  • COG 会被”翘曲”进该投影(arcgis-reprojected-tiles.ts):SDK 不能重投影瓦片图层,所以 COG 图层改为按视图投影切片,再用 Web Mercator 地图上 cog-tiler-wasm 渲染出的同一批瓦片填充每片——把瓦片上 33×33 个点投影到经纬度,点之间插值;跨越投影接缝的格子(Spilhaus 会把陆地切开)逐像素投影;每片按其被投影拉伸的程度选择源缩放级别。Web Mercator 瓦片止于 ±85.05°,因此该纬度以上的极冠区域留空;
  • 不画底图。Esri 底图样式与 XYZ 瓦片不会被翘曲,显示的是 Blank 背景色;其他瓦片数据图层(XYZ、WMS 瓦片、PMTiles、Zarr)也不画;
  • 相机、指针坐标、识别、选择与范围绘制在投影与经纬度之间换算,因此共享相机、其他分屏面板与保存的视图仍以度为单位;
  • 平面图开着地形时仍是 MapView(局部场景不能用该投影),地形只在地球或 Web Mercator 下恢复作用;
  • 未知 WKID 会在图上显示 SDK 的错误;
  • MapLibre、Mapbox、Cesium 面板忽略这个设置。

8.8.6 deck.gl 与”未支持”清单

Add Data → Deck.gl Layer / 3D Model 在主平面图与局部三维场景上可用。共享叠加层渲染保存的可视化与模型,跟随可见性、不透明度与 deck.gl 层之间的顺序,并在切换渲染器时释放 GPU 资源。但它是一个原生 SDK 图层,deck.gl 图层无法与原生 ArcGIS 图层逐个交错。适配器用 @deck.gl/arcgis 9.4.0 里 MIT 许可的合成器,避免打包 @arcgis/core;局部三维渲染用的是上游实验性相机近似,在极端相机角度会漂,且不与地形深度缓冲共享。全局场景与分屏面板不承载该叠加层,菜单与图层徽标会反映这条限制。

明确尚不支持的:

  • 高斯泼溅、Cesium 专属源、STAC 搜索结果、视频叠加(Add Data 置灰,图层面板标 No ArcGIS);
  • Esri 矢量瓦片服务自己的图标与标注图层(其存档样式里没有 sprite 与 glyphs),任何视图下都不行;
  • 测量(Controls 里置灰)与几何编辑(会提示渲染器不支持);
  • 图表、标注优先级表达式(SDK 没有逐要素标签优先级)、SceneView 里的聚合、填充图案、混合模式与线装饰、平面图上的拉伸;
  • 任意 MapLibre 自定义图层与渲染 API。

导航限制也与 MapLibre 略有差异:越过缩放范围或受限边界的视图会缓回范围内(平面上滚轮也会停在限制处),边界约束的是视图中心而不是整个视口,且 SDK 总会跨 180° 经线环绕——关闭世界复制只是钳制应用自己施加的视图。

注意:ArcGIS Maps SDK 由 Esri 按其自身条款分发,不是开源许可。GeoLibre 不再分发它;SDK 在运行时由用户浏览器从 Esri 的 CDN 取回,Esri 底图与位置服务的使用受 API key 所属账号约束。


8.9 三维内容:瓦片、点云、泼溅、地形与球面

8.9.1 3D Tiles 与 I3S

引擎 3D Tiles 的实现 I3S
MapLibre deck.gl Tile3DLayer loaders.gl I3SLoader(填 .../SceneServer)
Mapbox 共享交错 deck.gl 叠加层 同上
Cesium 原生 Cesium3DTileset,直接消费 URL、请求头与高度偏移 Cesium 自己的 I3SDataProvider
ArcGIS deck.gl 叠加层(限主平面图/局部场景) 需其他渲染器

Cesium 的瓦片集还有一层样式翻译(issue #2290):图层的颜色表达式与组合后的要素过滤会被翻译成 3D Tiles 的样式语言,作为 Cesium3DTileStyle 应用,于是瓦片集能像拉伸矢量图层那样分类、分级或按规则树着色;图层不透明度作为一次白色相乘,让带纹理的瓦片淡出而不染色。无法翻译的颜色或过滤会让瓦片集保留自己的颜色并显示全部要素——而不是猜一个。样式面板列出的属性名来自第一个渲染出来的瓦片(瓦片集把 schema 放在瓦片里而不是图层记录里),发布到 metadata.fields。

提示:3D Tiles 在 Mapbox 与 ArcGIS 上都要求墨卡托。加入这类叠加层时球面视图会切回墨卡托。

8.9.2 点云

点云有两条完全不同的路径,别混淆:

形态 入口 说明
LiDAR 图层 Add Data → LiDAR Layer / 拖入 LAS、LAZ、COPC / EPT 的 ept.json 走 maplibre-gl-lidar + deck.gl,MapLibre/Mapbox 可用,ArcGIS 走 deck.gl 适配
3D Tiles 形式 Add Data → 3D Tiles Layer Cesium 原生(含 pointCloudShading 的眼穹光照)
Cesium 原生点云 图层记录 COPC 在浏览器里用 copc 包解码、按档案 WKT 重投影;EPT 按视图流式;纯 LAS/LAZ 整份下载

Cesium 点云解码的关键数字(来自 architecture.md):

项 值/行为
纯 LAS/LAZ 上限 512 MB(含解码后大小)
抽稀 同一预算内每 n 个点取一个
COPC/EPT 流式 相机停稳后按”最需要”顺序(按节点点间距在该距离下的粗糙度)选节点;倾斜视图优先细化脚下而非地平线
解码节点缓存 300 万点,平移回来免费
图元上限 150 万点
LASzip 节点 每个上限 64 MB,一次并发取 4 个
拒绝的 EPT 形式 binary 与 zstandard
噪声剔除 丢弃 ASPRS 噪声类 7 与 18
高度色带 铺在解码高度的第 1–99 百分位,避免离群值压平颜色
无 CRS 的点云 拒绝,不会当成度来读

8.9.3 高斯泼溅

  • MapLibre:maplibre-gl-splat 控件,存储层类型 gaussian-splat;
  • Cesium:当资产本身就是 3D Tiles 瓦片集时,走原生 KHR_gaussian_splatting 分支;
  • Mapbox 与 ArcGIS:不支持(Add Data 置灰;ArcGIS 另在图层上标 No ArcGIS);
  • 原始 .ply / .splat 文件在地球上被跳过,在 2D 面板里渲染。

8.9.4 地形

引擎 默认地形 自定义 DEM 夸张 备注
MapLibre GeoLibre 全局源 COG URL / 本地文件 / 已加载栅格图层 Controls → Terrain 双击设夸张 球面投影下也可用
Mapbox Mapbox 地形 不支持(Terrain source 一节被隐藏) 有 地形是场景设置,Controls 直接切换、没有按钮
Cesium 无密钥 Terrarium,或配置的 COG COG;ion 地形资产 有 ion token 才有 World Terrain
ArcGIS Esri World Elevation(无需 key) COG(EPSG:3857/4326) Controls → Terrain exaggeration 来源与夸张不写进工程

8.9.5 行星与椭球

天体切换在两个地方出现:Layers 面板标题栏的行星切换器(轨道图标),以及 Change background / New project 对话框里的天体分组。

切天体的效果是工程级的:

  1. 换底图(月球与火星来自 OpenPlanetaryMap,其他来自 USGS Astrogeology 并实时重投影到 Web Mercator);
  2. 设定工程的椭球体,此后距离、面积、比例尺都按该天体半径计算;
  3. 在 MapLibre 的 mapCrs 预设里选行星投影时也会同时设定天体。

相关常量在 packages/core/src/ellipsoids.ts(ELLIPSOIDS)与 map-crs.ts。地球用 WGS 84;其他天体用 IAU 采用的数值,其中只有地球与火星明显扁(两极压扁),其余建模为球体:

天体 长半轴(m) 1/f
Earth (WGS 84) 6,378,137 298.257223563
Moon 1,737,400 0(完美球)
Mars (IAU 2000) 3,396,190 169.894447
Mercury 2,439,400 0
Venus 6,051,800 0

Cesium 与 ArcGIS 的球面/场景也读同一份工程状态。

警告:换天体后量测”不对”,九成是椭球体没跟着换,或者工程里残留了另一颗星球的投影预设。先看 Layers 面板的行星图标,再看 Settings → Map 里的 CRS/WKID。

8.9.6 Cesium 的时间与相机

时间维度在四种引擎里的落点差别很大:

引擎 时间相关能力
MapLibre 时间轴插件(COG、XYZ/WMTS、WMS-Time、按时间过滤的 GeoJSON),图层上的 metadata.timeBinding 与瞬态 timeFilter
Mapbox 与 MapLibre 同源的时间轴插件;绑定到 gpu/wasm 引擎的 COG 会通过 TiTiler 重新加入;mosaic 清单会被丢弃并打控制台警告
Cesium 原生 CZML 动态场景(轨道、轨迹、路径),球面时钟驱动;KML 的 <TimeSpan>/<TimeStamp> 与 GroundOverlay 动画
ArcGIS 时间轴过滤会变成要素服务的 SQL definitionExpression;不支持的过滤在横幅报告

相机方面,Cesium 的换算已在 8.3.3 说明。还有两条实用差异:

  • Cesium 需要 ion token 才能用 Ion 地形资产与 ion 影像;没有 token 时球面仍能打开,只是少了这两项;
  • ArcGIS 在球面上把工程的缩放范围映射成高度范围,而平面图上仍是缩放范围。

8.10 插件在各引擎的适配

8.10.1 声明式适配

插件通过清单里的 engines 列表声明支持哪些渲染器:

  • 省略 engines 时默认为 MapLibre;
  • Plugins 菜单与命令面板会把不支持的项置灰;
  • 不支持的插件被挂起,而不是让它在运行中失败;
  • 球面与 ArcGIS 上的控件拿到一张”形状像 MapLibre”的地图(getControlMap,见 packages/plugins/src/plugins/style-map.ts),Style Spec 调用被记录进影子样式(见 8.7.6 与 8.8.6 的对应段落)。

插件 API 里的相关成员(节选自 packages/plugins/src/types.ts):

export interface PluginManifest {
  // ...
  engines?: MapRendererKind[];   // 省略时默认为 MapLibre
}

// 插件拿到的应用门面(同文件):
//   getMap?: () => MapLibreMap | null;
//   getMapRenderer?: () => MapRendererKind;   // 包含画布正在被替换的瞬间
//   getViewBounds?: () => [number, number, number, number] | null;

app.getMap() 只对 MapLibre 有效,在球面上恒为 null。因此可移植插件应改读 getStyleMap(app)(回退到 app.getMapboxMap()),停留在两个引擎共享的 Style Spec 面上;需要视口范围时用 getViewBounds() 而不是 getMap()?.getBounds()——后者在球面上静默失败,会让”只搜当前视图”的勾选框形同虚设。具体规则与审计方式见插件 API 文档与第21章 插件系统与插件开发。

8.10.2 常见插件的引擎覆盖

插件/控件 MapLibre Mapbox Cesium ArcGIS
Layer Control 有 有(同一 host) 无(无样式文档) 有(SDK 原生图层列表)
Basemaps 有 有 有 有
Web Services 目录族 有 有(GeoLens 私有栅格除外) 有(镜像/实体) 有(控件代理)
USGS 3DEP 有 声明支持(未在浏览器验证) 有 有
Gridlines / DGGS 网格 有 有 有(实体) 有
Time Slider 有 有 原生时间能力 转为 definitionExpression
Geo Editor 有 有(geo-editor-mapbox.ts) 有 无(几何编辑不支持)
Measure 有 有 有 置灰
Elements / 标注 有 有(annotation-marker.ts) 有 有(文字)
Street View 有 有(createMarker 工厂) 有(cesium-dom-marker.ts) 置灰
Layer Swipe 有 有(已知缺陷 #2430) — 置灰
Atmospheric Effects / Sun / Flight Simulator 有 有 有(原生环境插件) 置灰
LiDAR / 高斯泼溅面板 有 无(高斯泼溅)/ 有(LiDAR) 无法挂载 有(deck.gl)
私有 GeoLens 栅格 有 需 MapLibre(请求头注入) 有 有

注意:这张表是”能不能用”的粗略索引,具体到某个插件版本仍以其清单里的 engines 为准。菜单置灰时通常会带一句原因,那比这张表更新。

8.10.3 一次真实审计的数字

仓库文档记录了一次 2026 年 9 月的 Mapbox 插件审计:逐个打开了 Add Data 的 37 个启用条目,并对公开数据路径做了实际导入验证(例如 US states GeoJSON 52 个要素、Monaco OSM PBF 得到 4,249 点 / 4,002 线 / 2,341 面、Autzen COPC 的 10,653,336 个点)。验证分级明确写在表里——“面板打开了”不算导入成功。

这些检查作为可选的回归套件保留:

# 需要环境里有公开 token
npm exec -- playwright test e2e/mapbox-add-data.spec.ts

# PMTiles / COPC / 3D Tiles 的存取与重开
npx playwright test e2e/mapbox-archives-3d.spec.ts

# ArcGIS:需要 ARCGIS_API_KEY(或 ARCGIS_E2E=1 跑无密钥覆盖)
npx playwright test e2e/arcgis-renderer.spec.ts

8.11 性能与限制

8.11.1 逐引擎要点

引擎 要点
MapLibre 要素超过 50,000 时改在客户端切成矢量瓦片(geojson-vt + vt-pbf,自定义协议喂给地图);瓦片索引刻意不进 store
Mapbox 大 GeoJSON 用 mapbox-gl 自己的 source(它已在 Worker 里分块);带来更短的最长主线程卡顿
Cesium 纯点图层 > 50,000 要素改用单个图元集合;点云有 300 万点缓存与 150 万图元上限;LAS 上限 512 MB
ArcGIS 每个矢量源图层各自一个原生 VectorTileLayer,源图层多的档案解码与内存开销更大——只启当前视图需要的层
共享 deck.gl 叠加层必须共用一个实例,否则后创建的可能覆盖前一个管理的图层

8.11.2 引擎切换的成本

切换引擎不是换一层皮:

  • 旧引擎卸载,画布与 WebGL 上下文释放(不是隐藏);
  • 新引擎若为 Mapbox/Cesium/ArcGIS,可能触发首次加载(见 8.4.2);
  • 不支持当前引擎的图层留在工程里、在图层上标记(MapLibre 侧常见 2D only,ArcGIS 侧 No ArcGIS),切回兼容引擎后回来;
  • 分屏面板各自保持独立的图层可见性覆盖。

8.11.3 已知的引擎特有性能问题

  • Linux/WebKitGTK 桌面版:空白地图稳定 60 FPS,但只要加载瓦片图层,加载期间帧率会掉到个位数,加载完立刻回到 60。根因是 WebKitGTK 在主线程处理每张新瓦片的纹理上传(一个瓦片集成周期约 125 ms,而 Chromium 只要几毫秒)。缓解方向(加大瓦片缓存、512 px 栅格瓦片、关掉淡入)在文档里写明尚未实现——详见第25章 性能优化与故障排查。
  • ArcGIS 首次加载:从 CDN 拉几百个小模块,第一个面板明显慢于 Mapbox;这是打包体积与首屏时间的取舍。
  • ArcGIS 矢量瓦片档案:源图层多时解码工作按层数放大。
  • Cesium 披挂内容:大量瓦片矢量图层会触发隐藏 MapLibre 地图的逐瓦片渲染与回读,代价高于 2D 直接绘制。

8.11.4 测试与门禁

位置 覆盖内容
tests/layer-support-matrix.test.ts 把每张图层支持表钉到其引擎的派发逻辑上
tests/arcgis-layers.test.ts 图层编译器与底图规划
tests/arcgis-engine.test.ts 用假 SDK 驱动引擎
tests/arcgis-renderer.test.ts 工程格式、设置与加载器边界
e2e/arcgis-renderer.spec.ts 对真实 CDN 的可选浏览器检查
e2e/arcgis-offline.spec.ts 生产 CSP 下的无密钥冷启动、缓存启动、CDN 不可用时的可见错误
e2e/mapbox-add-data.spec.ts Mapbox 下的 Add Data 边界与四个集成路径
e2e/mapbox-archives-3d.spec.ts PMTiles / COPC / 3D Tiles 的存取与重开

前四个不触网;后四个是可选的浏览器检查,缺凭据时自动跳过。


8.12 排错

现象 最可能的原因 处理
ArcGIS 面板一片空白,横幅有错误 首次加载需要网络;CSP 或 CDN 不可达 点横幅的 Retry(会重载页面);检查 CSP 是否放行 js.arcgis.com
Mapbox 地图不显示并提示设置 缺 access token 填 Settings → Environment Variables → Mapbox token 并保存
Cesium 能打开但没有世界地形 没有 ion token 接受无 token 的 Terrarium 地形,或填 token
图层上有 2D only 标记 当前引擎不支持该图层类型 切回 MapLibre;或改用该引擎支持的等价形态(如把点云作为 3D Tiles 加入)
图层上有 No ArcGIS 标记 ArcGIS 明确不支持(高斯泼溅、STAC 结果、视频等) 换引擎
切换引擎后图层”消失” 该图层在当前引擎不支持,仍在工程里 切回兼容引擎;看图层徽标
分屏相机抖动 相机同步的回落检查被绕过 关掉 syncView 或用 View → Split View 重新建布局
Mapbox 下混合模式无效 Mapbox 不复现 Blend 在 MapLibre 下配置,或接受平涂
ArcGIS 球面下 Measure 置灰 测量走 MapLibre/Mapbox 的样式 API 切到平面引擎测量
ArcGIS 换投影后底图消失、极冠空白 其他投影下不画底图,Web Mercator 瓦片止于 ±85.05° 属预期行为
Cesium 上标注不旋转、不避让 球面没有旋转与碰撞处理 属预期;需要精细标注用 2D 引擎
3D Tiles 一加入球面就切回平面 deck.gl 叠加层要求墨卡托 用 Cesium 的原生 3D Tiles 分支

8.13 本章小结

  1. 一个 MapEngine 接口、四个实现;能力用冻结的布尔量声明,UI 按能力而不是按引擎名判断。
  2. 主图用 primaryRenderer、分屏用 SecondaryMapView.viewKind;切换会卸载旧引擎,不支持的图层留在工程里。
  3. 相机同步按地面分辨率(米/像素),不是按 zoom,因此不同高度/尺寸的面板比例尺仍然对齐。
  4. 凭据只存本机:Mapbox token 必需、Cesium ion token 可选、ArcGIS API key 可选;三者都不写进工程文件。
  5. 加载策略各不同:MapLibre 随包;Mapbox 独立分块(约 1.91 MB,gzip 532 KB);Cesium 首个画布挂载时加载并暂存资源;ArcGIS 从 CDN 取模块并缓存。
  6. MapLibre 是默认与最全,也是唯一支持任意平面 CRS(含行星投影)的引擎;其他 CRS 下 XYZ/矢量瓦片/底图不画。
  7. Mapbox 的边界:MBTiles、高斯泼溅、ion/CZML 不可用;混合模式不复现;自定义 COG 地形源被隐藏;Mapbox GL JS v3 非开源。
  8. Cesium 是原生球:GeoJSON 逐要素求值、瓦片矢量”披挂”、3D Tiles 样式翻译、点云流式与上限、标注无旋转与碰撞。
  9. ArcGIS 逐要素烘焙 + 唯一值渲染器;默认打开就是三维球;支持 WKID 投影但投影下不画底图;测量与几何编辑不可用。
  10. 三维内容按引擎分流:3D Tiles 四种引擎都有但实现不同;点云分 LiDAR 图层与 3D Tiles 两条路;高斯泼溅只有 MapLibre 与 Cesium(原生瓦片)。
  11. 地形四套来源,其中 Mapbox 不支持自定义 COG 地形,ArcGIS 的 COG DEM 不写进工程。
  12. 插件用 engines 声明,不支持时挂起而非失败;app.getMap() 仅 MapLibre,可移植插件走 getStyleMap / getControlMap。

8.14 实践练习

  1. 能力对照:把一个含 GeoJSON、COG、3D Tiles、LiDAR 与 KML 的工程在四种引擎间切换,记录每种引擎下哪些图层可用、哪些被标记,并对照 8.2.2 的矩阵解释差异。

  2. 分屏比例尺:用 View → Split View 建 2×1 布局,左 MapLibre、右 Cesium,打开 syncView,在两侧分别量同一段距离,记录结果并解释为什么按”地面分辨率”同步能让比例尺对齐。

  3. 凭据与加载:在没有 Mapbox token 的情况下切到 Mapbox,记录界面提示;填入 token 后观察引擎是否重建。再用浏览器开发者工具观察首次切到 ArcGIS 时对 js.arcgis.com 的请求,说明为什么第一次明显更慢。

  4. Cesium 点云预算:加载一个超过 500 MB 的 LAS 文件与一个同区域的 COPC 档案(Cesium 引擎),分别记录加载行为与内存表现,并解释 512 MB 上限、300 万点缓存与 150 万图元上限各自在什么阶段起作用。

  5. ArcGIS 投影:把 ArcGIS 引擎的投影依次设为 Robinson(54030)、Plate Carrée(4326)与 Spilhaus(54099),记录底图、COG 与极区显示的变化,并说明为什么底图在非 Web Mercator 投影下会消失。

  6. 插件置灰排查:选三个插件分别在四种引擎下打开 Plugins 菜单,记录哪些被置灰,并从 engines 声明与能力集两个角度解释原因。


延伸阅读

  • 仓库文档:docs/user-guide/rendering-engines.md、docs/maplibre-renderer.md、docs/mapbox-renderer.md、docs/cesium-renderer.md、docs/arcgis-renderer.md、docs/cesium-parity-testing.md、docs/architecture.md(Rendering-engine model 与 3D globe view 两节)
  • 源码:packages/map/src/map-engine.ts(接口与能力集)、map-controller.ts、mapbox-engine.ts、cesium-engine.ts、cesium-layer-sync.ts、cesium-camera.ts、arcgis-engine.ts、feature-style.ts、apps/geolibre-desktop/src/lib/add-data-renderer.ts
  • 项目主页:https://geolibre.app