@3clear/basegis
v0.1.9
Published
BaseGIS map engine adapter and GIS layer utilities.
Readme
@3clear/basegis
@3clear/basegis 是 3clear 一张图项目抽出的 GIS 能力包,通过统一入口 BaseGIS 封装 Cesium / Leaflet 常用 API。业务页面可复用同一套地图逻辑,复杂图层通过 methods 控制器组合公共方法;三维专有能力的支持范围见能力支持说明。
当前包包含:
BaseGIS:初始化、引擎切换、视角与底图、地图截图导出、基础图形、Canvas 图标与扩散 Marker、GeoJSON、DEM、点击事件,以及统一风场、源解析传输、GPU 风场、三维体与剖面。methods:多地图实时视角联动、独立线图层、台风路径、图片图层、网格图层、海量点、点位聚合、点位抽稀、等值线、风场等高级控制器。layers:天地图、GeoServer 金字塔瓦片、WMS、WMTS 图层配置快捷构造器。assets:站点、工厂、信息标记和台风中心 SVG 资源。style.css:BaseGIS 自有图层、标注和截图框选样式,不内嵌 Leaflet 官方 CSS。
能力总览
以下按类型展示 @3clear/basegis 的主要业务能力;工具型辅助函数不单独作为能力卡展示。点击名称可跳转到下方详细说明,完整导入方式见出口。
快速开始
地图工具(2)
通用点位图层(5)
行政区划边界图层(1)
轨迹路径图层(2)
格点填色图层(2)
等值线图层(2)
三维渲染(不支持2维)
风场图层(2)
影像瓦片图层(4)
Assets 示例资源(4)
安装
npm install @3clear/basegis leaflet axiosd3-contour、pixi.js、leaflet-pixi-overlay 和 html2canvas 已随 BaseGIS 构建产物发布,业务项目不需要单独安装。其中 Pixi 相关代码只在首次使用 Leaflet 海量点能力时按需加载,html2canvas 只在首次调用 Leaflet 截图时按需加载。
在应用入口引入 BaseGIS 与自有样式:
import { BaseGIS } from '@3clear/basegis'
import '@3clear/basegis/style.css'外部依赖版本以 package.json 为准:当前为 leaflet ^1.9.4、axios ^1.15.1。
当前限制:完整 BaseGIS 入口中的剖面工具仍在模块加载时访问 Cesium.Cartesian3,因此即使只创建 Leaflet 地图,也需在导入 BaseGIS 前加载下文的 Cesium 脚本,否则会报 Cesium is not defined。这是既有的引擎依赖问题,与本次 Leaflet CSS 共用处理无关;纯原生 Leaflet 不受此限制。
Leaflet JS 和 leaflet/dist/leaflet.css 都由宿主依赖提供。BaseGIS 保留对官方 CSS 的导入,因此上面的原有用法仍然有效;官方规则不再另行内嵌到包的 style.css 中。
如果项目同时使用原生 Leaflet,可以在应用入口统一引入:
import L from 'leaflet'
import 'leaflet/dist/leaflet.css'
import { BaseGIS } from '@3clear/basegis'
import '@3clear/basegis/style.css'确保 BaseGIS 与业务代码解析到同一份安装的 Leaflet,宿主构建器即可复用同路径的 JS / CSS。不同地图使用各自容器;不需要挂载 window.L 或注册 Vue 插件,其他模块需要使用 L / BaseGIS 时仍按需 import。纯原生页面若不导入 BaseGIS JS,必须自行引入 leaflet/dist/leaflet.css,仅引入包的 style.css 不包含官方规则。
Cesium 不随 npm 包发布。使用 Cesium 时,宿主项目需自行加载 Cesium.js 和 Widgets/widgets.css,并保留 Workers、Assets 等完整静态目录;初始化前必须能访问 window.Cesium。config.engine.cesium.scriptUrl/cssUrl 不会自动注入脚本和样式。
例如,将完整 Cesium 资源放入 public/lib/Cesium 后,在 Vite 的 index.html 中、应用入口脚本之前添加:
<script>window.CESIUM_BASE_URL = '%BASE_URL%lib/Cesium/'</script>
<link rel="stylesheet" href="%BASE_URL%lib/Cesium/Widgets/widgets.css">
<script src="%BASE_URL%lib/Cesium/Cesium.js"></script>使用 GeoTIFF 数据时,宿主还需提前提供 window.GeoTIFF。本文 /data/...、/mock/... 均为示例数据地址,不包含在 npm 包中,请替换为项目真实地址;部署在子路径时,应结合宿主的 import.meta.env.BASE_URL 生成静态资源 URL。
出口
// 主入口
import { BaseGIS } from '@3clear/basegis'
// 高级能力控制器
import {
LineLayerController,
MapViewLinkController,
TyphoonPathController,
ImageLayerController,
toImageLayerArea,
GridLayerController,
PointLargeLayerController,
PointClusterController,
PointDensityController,
ContourLayerController,
RasterContourController,
WindFieldMethods,
} from '@3clear/basegis/methods'
// 图层配置构造器
import {
createTiandituLayer,
createTiandituTileSource,
createGeoserverPyramidLayer,
createWmsLayer,
createWmtsLayer,
} from '@3clear/basegis/layers'
// 示例资源
import {
gisMarkerSample,
gisFactoryMarker,
gisInfoMarker,
typhoonPathIcon,
} from '@3clear/basegis/assets'快速开始
<template>
<div id="map" class="map"></div>
</template>
<script setup>
import { onBeforeUnmount, onMounted } from 'vue'
import { BaseGIS } from '@3clear/basegis'
import '@3clear/basegis/style.css'
let mapCore = null
onMounted(() => {
mapCore = new BaseGIS({
// 可选 cesium / leaflet。不传时使用默认配置 active: 'cesium'。
engineType: 'cesium',
// 也可以传 container: HTMLElement。
containerId: 'map',
config: {
// 地图 / 底图投影与无元数据栅格默认源投影分开配置。
crs: 'EPSG:3857',
sourceProjectionCrs: 'EPSG:3857',
view: {
// 可选 2d / 2.5d / 3d。
defaultSceneMode: '3d',
initialView: {
center: [104, 35],
// Cesium 使用 height,Leaflet 使用 zoom。
height: 5000000,
zoom: 5,
// Cesium 默认保持垂直俯视。
pitch: -90,
},
},
basemap: {
// 默认内置值:tianditu-imagery。
defaultVisibleId: 'tianditu-imagery',
},
},
})
const result = mapCore.init()
if (!result.success) {
console.warn(result.message)
}
})
onBeforeUnmount(() => {
mapCore?.destroy()
})
</script>
<style scoped lang="scss">
.map {
width: 100%;
height: 100vh;
}
</style>以下 API 示例默认 mapCore 已初始化成功;示例中的业务数据、图片和服务地址需由页面准备。组件卸载时先销毁控制器、移除页面监听,再调用 mapCore.destroy()。
返回值约定
操作方法通常返回统一结果对象;图片、GeoJSON、风场等异步加载方法应使用 await:
{
success: true,
message: 'operation message',
data: {}
}失败时:
{
success: false,
message: 'error message',
code: 'NOT_INITIALIZED'
}getEngineType()、getConfig()、getMapInstance() 等读取方法直接返回值;createMarkerIcon() 直接返回图标参数或 null。控制器的 getState() 也返回状态对象,不能一律按 result.data 读取。
地图初始化与运行时配置
BaseGIS 的初始化参数分为两类:需要长期保留的默认配置放在构造函数的 config 中;只想覆盖本次初始化时,传给 init(options)。地图已经创建后,setConfig() 只更新实例保存的配置,不会直接改变当前画面;初始化默认值通常需要重新 init() 才会生效。
| 配置入口 | 适用场景 | 是否写入运行时配置 | 是否立即重建地图 |
| --- | --- | --- | --- |
| new BaseGIS({ ... }) | 创建实例并设置默认引擎、容器和整套配置。 | 是 | 否,仍需调用 init()。 |
| init(options) | 创建或重新创建地图,并单次覆盖容器、引擎、CRS、场景模式或初始视角。 | engineType 会写入;crs / sceneMode / initialView 不写入。 | 是 |
| setConfig(config) | 修改实例保存的配置,供后续初始化或配置查询使用。 | 是 | 否 |
构造参数
const mapCore = new BaseGIS({
engineType: 'cesium',
containerId: 'map',
config: {
crs: 'EPSG:3857',
sourceProjectionCrs: 'EPSG:3857',
view: {
defaultSceneMode: '3d',
initialView: {
center: [104, 35],
height: 5000000,
zoom: 5,
heading: 0,
pitch: -90,
roll: 0,
},
},
},
})| 参数 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| engineType | 'cesium' \| 'leaflet' | config.engine.active,内置为 cesium | 默认引擎。该参数最终覆盖 config.engine.active。 |
| container | HTMLElement \| string | - | 地图容器 DOM,也兼容传容器 id 字符串。 |
| containerId | string | - | 地图容器 id。与 container 二选一即可。 |
| config | Object | 内置配置 | 推荐的配置入口,与内置配置做深合并。 |
| crs / sourceProjectionCrs | string | 见下文 | 分别表示地图 / 底图投影和无元数据栅格的默认源投影,支持 EPSG:4326 / EPSG:3857。 |
| engine / view / basemap / dem / sourceTransport | Object | - | 兼容直接写在构造参数顶层;crs / sourceProjectionCrs 也可如此传入。推荐统一放入 config,同名时 config 中的值优先。 |
init(options) 参数与优先级
const result = mapCore.init({
// 都是选填;构造时已经设置过的内容不需要重复传。
containerId: 'map',
engineType: 'cesium',
crs: 'EPSG:3857',
sceneMode: '3d',
initialView: {
center: [104, 35],
height: 5000000,
zoom: 5,
pitch: -90,
},
})
if (!result.success) {
console.warn(result.message)
}| 参数 | 类型 | 说明 |
| --- | --- | --- |
| container | HTMLElement \| string | 本次使用的容器 DOM 或容器 id,优先级最高。 |
| containerId | string | 本次使用的容器 id。 |
| engineType | 'cesium' \| 'leaflet' | 本次使用的引擎,同时更新实例保存的 engine.active。 |
| crs | 'EPSG:4326' \| 'EPSG:3857' | 本次创建地图使用的 CRS;只覆盖本次初始化,不写回 config.crs。 |
| sceneMode | '2d' \| '2.5d' \| '3d' | 本次初始化的场景模式;Leaflet 只支持 2d。不会写回 config.view.defaultSceneMode。 |
| initialView | Object | 本次初始化的视角。不会写回 config.view.initialView。 |
init({ config: ... }) 不是有效写法;init() 不会合并 config。持久配置必须在构造函数中传入,或先调用 setConfig()。
各参数的实际取值顺序如下,左侧优先级更高:
| 项目 | 取值优先级 |
| --- | --- |
| 容器 | init.container → init.containerId → 构造函数 container → 构造函数 containerId |
| 引擎 | init.engineType → 实例当前引擎(由构造函数 engineType 或 config.engine.active 得到)→ cesium |
| 地图 CRS | init.crs → config.crs → 旧版引擎默认(Leaflet EPSG:4326,Cesium EPSG:3857) |
| 场景模式 | Cesium:init.sceneMode → config.view.defaultSceneMode → 3d;Leaflet 始终为 2d |
| 初始视角 | init.initialView → config.view.initialView → 引擎内置安全视角 |
重复调用 init() 时会先销毁旧 adapter,再创建新地图;BaseGIS 托管图层会异步恢复。init() 本身同步返回初始化结果,后续逻辑依赖图层恢复时还需等待:
const result = mapCore.init()
if (result.success) {
const restore = await mapCore.whenReady()
console.log(restore.restored, restore.failed)
}完整配置分组
config 顶层包含两个 CRS 值和以下五个配置分组。页面不要直接依赖包内部的 src/gis/config 文件;需要查看当前实例最终合并后的配置时,使用 mapCore.getConfig()。
| 配置项 / 组 | 用途 | 主要生效时机 |
| --- | --- | --- |
| crs | 地图平面投影与底图瓦片矩阵。 | init() / 重新 init() |
| sourceProjectionCrs | 无 CRS 元数据栅格的默认源投影。 | 后续创建或更新栅格图层时 |
| engine | 默认引擎、引擎接入元数据及 Cesium 渲染质量。 | init() / 重新 init() |
| view | 默认场景模式和初始视角。 | init() / 重新 init() |
| basemap | 初始底图、可切换底图列表及内置服务元数据。 | init();列表也供 setBasemapById() 查询 |
| dem | Cesium 默认地形及可切换地形列表。 | Cesium init();列表也供 loadDEMById() 查询 |
| sourceTransport | 源解析传输图层的实例级默认视觉参数。 | 创建 adapter 时锁定;setConfig() 后需重新 init(),新 adapter 创建图层时才会读取 |
crs 与 sourceProjectionCrs
| 配置项 | 内置默认值 | 说明 |
| --- | --- | --- |
| crs | '' | 地图平面和底图瓦片矩阵;空值保留旧版引擎默认,Leaflet 为 EPSG:4326,Cesium 为 EPSG:3857。 |
| sourceProjectionCrs | 'EPSG:4326' | PNG / JPG / 灰度图 / 数值网格不携带 CRS 元数据时的默认源投影。 |
两个值独立:修改底图 crs 不会重新解释栅格数据,修改 sourceProjectionCrs 也不会改变底图矩阵。area、视角以及点线面业务坐标始终使用 WGS84 经纬度。Cesium 中 crs 控制 2D / Columbus 平面投影和内置底图矩阵,不改变 3D 地球的 WGS84 坐标。
栅格源投影的取值顺序是:单个数据源的 sourceProjectionCrs(兼容旧名 sourceProjection)→ GeoTIFF 可识别 GeoKey → BaseGIS sourceProjectionCrs → 内置 EPSG:4326。GeoTIFF 已声明但当前不支持的 CRS 会返回错误,不会被全局默认值覆盖。修改 crs 后必须重新 init();修改 sourceProjectionCrs 只影响之后新建或重新加载且没有显式投影的栅格,不会自动重解释已加载数据。
engine 引擎配置
| 配置项 | 内置默认值 | 说明 |
| --- | --- | --- |
| engine.active | 'cesium' | 默认引擎;构造参数 engineType 或 init({ engineType }) 的优先级更高。 |
| engine.cesium.sourceType | 'local-script' | Cesium 接入方式说明字段。 |
| engine.cesium.scriptUrl | '/lib/Cesium/Cesium.js' | Cesium 脚本地址说明字段;BaseGIS 不会自动加载该脚本。 |
| engine.cesium.cssUrl | '/lib/Cesium/Widgets/widgets.css' | Cesium 样式地址说明字段;BaseGIS 不会自动加载该样式。 |
| engine.cesium.renderQuality.maximumDevicePixelRatio | 2 | Cesium 最大设备像素比,实际值限制在 1~3;越高越清晰,也越耗 GPU。 |
| engine.cesium.renderQuality.fxaa | true | 是否启用 Cesium FXAA。 |
| engine.cesium.renderQuality.msaaSamples | 4 | Cesium MSAA 采样数,取整并限制在 1~8。 |
| engine.leaflet.sourceType | 'npm' | Leaflet 接入方式说明字段。 |
| engine.leaflet.packageName | 'leaflet' | Leaflet 依赖包名说明字段。 |
scriptUrl / cssUrl 只是接入元数据。Cesium 资源的实际加载方法见安装。渲染质量在创建 Cesium Viewer 时读取,修改后需要重新 init()。
view 视角配置
| 配置项 | 内置默认值 | Cesium | Leaflet | 说明 |
| --- | --- | --- | --- | --- |
| view.defaultSceneMode | '3d' | 使用 | 只接受 2d | 默认场景,可选 2d / 2.5d / 3d。 |
| view.initialView.center | [121.4737, 31.2304] | 使用 | 使用 | [经度, 纬度]。 |
| view.initialView.height | 1800000 | 使用 | 忽略 | Cesium 相机高度,单位米。 |
| view.initialView.zoom | 7 | 忽略 | 使用 | Leaflet 缩放级别。 |
| view.initialView.heading | 0 | 使用 | 忽略 | Cesium 航向角,单位度。 |
| view.initialView.pitch | -90 | 使用 | 忽略 | Cesium 俯仰角,单位度;-90 表示垂直俯视。 |
| view.initialView.roll | 0 | 使用 | 忽略 | Cesium 翻滚角,单位度。 |
basemap 底图配置
| 配置项 | 内置默认值 | 说明 |
| --- | --- | --- |
| basemap.defaultVisibleId | 'tianditu-imagery' | 初始化时从 basemap.list 选择的底图 id。 |
| basemap.defaultAnnotationId | 'tianditu-vector-label' | 当前为保留字段,初始化流程尚未读取;注记应通过所选底图项的 annotationResourceKey 配置,天地图也会按底图资源自动推断注记。 |
| basemap.annotationOnTop | false | 设为 true 后,底图注记显示在图片图层上方;双引擎的图片更新、底图切换和引擎恢复均自动维持顺序。 |
| basemap.list | 内置天地图、WMS、WMTS 示例列表 | 底图资源数组;完整默认项和字段见下方默认底图配置。外部传入数组会整体替换内置数组。 |
| basemap.providers | 内置天地图 provider | 内置服务的地址、子域名、token 池及资源映射。当前适配器读取包内 provider,构造参数或 setConfig() 中的覆盖值尚不会生效;自定义服务请在 basemap.list 中配置 URL。 |
把地名、边界等瓦片注记显示在天气图片上方:
const mapCore = new BaseGIS({
containerId: 'map',
config: {
basemap: {
defaultVisibleId: 'tianditu-imagery',
annotationOnTop: true,
},
},
})Leaflet 将注记放到独立的 440 层,位于图片与等值线、Marker 之间,且不拦截鼠标交互。Cesium 在影像集合变化时将当前注记置顶;开启此项后,区域灰度图也使用 Canvas 着色后的影像管线,避免 GroundPrimitive 覆盖整个影像栈。该设置只控制底图瓦片注记与图片的关系,不会将注记抬到三维物体或后处理效果之上。配置在 init() 时读取;修改后需重新 init(),单独 setConfig() 不立即改变当前地图。
离线回归页面:启动开发服务后访问 /lgmap/scripts/test-basemap-annotations.html,使用合成瓦片和真实截图像素验证开关、图片替换、底图切换、引擎恢复及销毁清理。
dem 地形配置
| 配置项 | 内置默认值 | 说明 |
| --- | --- | --- |
| dem.defaultEnabled | true | Cesium 初始化时是否加载默认 DEM;false 时跳过。 |
| dem.defaultVisibleId | 'ellipsoid-flat' | 默认 DEM id,必须能在 dem.list 中找到、visible !== false 且支持 Cesium,否则初始化失败。 |
| dem.list | 平面地形、Cesium World Terrain、天地图 DEM 占位项 | DEM 资源数组;外部传入时整体替换。列表项常用字段为 id / name / sourceType / factory / url / visible / engineSupport / options,详细用法见DEM 地形。 |
Leaflet 不加载 DEM。cesium-world-terrain 需要 Cesium Ion 能力;tianditu-dem 是待补真实服务地址的占位项。
常用地图初始化配置示例
下面示例把真正参与地图创建的常用配置集中写在一起。未传的字段继续使用上表中的内置默认值:
const mapCore = new BaseGIS({
containerId: 'map',
config: {
// 地图瓦片与无元数据栅格的默认投影分开管理。
crs: 'EPSG:3857',
sourceProjectionCrs: 'EPSG:3857',
engine: {
active: 'cesium',
cesium: {
renderQuality: {
maximumDevicePixelRatio: 2,
fxaa: true,
msaaSamples: 4,
},
},
},
view: {
defaultSceneMode: '3d',
initialView: {
center: [104, 35],
height: 5000000,
zoom: 5,
heading: 0,
pitch: -90,
roll: 0,
},
},
basemap: {
defaultVisibleId: 'tianditu-imagery',
},
dem: {
defaultEnabled: true,
defaultVisibleId: 'ellipsoid-flat',
},
},
})
const result = mapCore.init()
if (!result.success) {
console.warn(result.message)
}sourceTransport 源解析传输默认配置
这一组不是地图容器或相机初始化参数,而是 Cesium 源解析传输图层的实例级默认值。
| 配置项 | 内置默认值 | 说明 |
| --- | --- | --- |
| sourceTransport.visible | true | 新建图层默认是否显示。 |
| sourceTransport.running | true | 新建图层默认是否播放烟羽动画。 |
| sourceTransport.colors | 内置 7 色数组 | 多来源默认色板。 |
| sourceTransport.path | 内置对象 | 弧线路径分段、高度与弯曲参数。 |
| sourceTransport.line | 内置对象 | 线宽、透明度、辉光、收尖和命中宽度。 |
| sourceTransport.smoke | 内置对象 | 烟羽数量、大小、透明度、扩散和速度。 |
| sourceTransport.sourceNode | 内置对象 | 来源节点大小与标签数量。 |
| sourceTransport.targetNode | 内置对象 | 目标节点颜色与环半径。 |
| sourceTransport.volume | 内置对象 | 目标体云开关、网格尺寸、渲染参数及色带。 |
| sourceTransport.interaction | 内置对象 | 交互开关、命中容差和鼠标样式。 |
| sourceTransport.fog | 内置对象 | 雾效开关与密度。 |
| sourceTransport.camera | 内置对象 | 定位图层时的俯仰角、高度和动画时长。 |
单个图层传入的同名参数优先于这里的实例默认值。
配置合并规则
构造函数和 setConfig() 使用同一套合并规则:
| 数据类型 | 合并方式 |
| --- | --- |
| 普通对象 | 按层级递归合并,只传一个叶子字段不会删除同组其他字段。 |
| 数组 | 整体替换,不会追加;basemap.list、dem.list、sourceTransport.colors 都遵循此规则。 |
| 基本类型、函数及其他值 | 使用新值替换旧值。 |
| 未传字段 | 保留当前配置中的值。 |
配置会被克隆后保存,不会直接修改包内默认配置。getConfig() 返回当前实例持有的配置对象引用,建议只读;修改配置统一调用 setConfig()。
例如,只覆盖 Cesium 像素比不会丢失 fxaa 和 msaaSamples:
mapCore.setConfig({
engine: {
cesium: {
renderQuality: {
maximumDevicePixelRatio: 1.5,
},
},
},
})如果要在现有底图列表后追加一项,必须自行保留原数组:
const currentConfig = mapCore.getConfig()
mapCore.setConfig({
basemap: {
list: [
...currentConfig.basemap.list,
{
id: 'custom-xyz',
name: '自定义 XYZ',
category: 'basemap',
type: 'xyz',
provider: 'custom',
url: 'https://example.com/tiles/{z}/{x}/{y}.png',
engineSupport: ['cesium', 'leaflet'],
},
],
},
})setConfig() 怎么用
setConfig(overrideConfig) 是同步方法,没有统一结果对象或其他返回值。它只更新 BaseGIS 实例保存的配置,不会把新配置推送给已经创建的 adapter,也不会自动改变当前场景、视角、底图、DEM 或渲染质量。crs / sourceProjectionCrs 会归一化为 BaseGIS 支持的名称;修改 crs 后需重新 init(),修改 sourceProjectionCrs 只作为后续新数据的回退值。engine.active 是另一个需要特别注意的字段:它会同步实例记录的引擎类型,但不会真正替换当前底层地图。
需要让整套初始化配置生效时,直接再次 init(),不要先调用 destroy();否则托管图层快照会被清空:
mapCore.setConfig({
engine: {
cesium: {
renderQuality: {
maximumDevicePixelRatio: 1.5,
fxaa: true,
msaaSamples: 4,
},
},
},
view: {
defaultSceneMode: '2d',
initialView: {
center: [116.4, 39.9],
height: 1800000,
zoom: 7,
pitch: -90,
},
},
})
const result = mapCore.init()
if (result.success) {
await mapCore.whenReady()
}如果目标只是立即操作当前地图,不要用 setConfig() + init() 代替已有的实时 API:
| 目标 | 推荐 API | 是否重建地图 |
| --- | --- | --- |
| 切换 Cesium / Leaflet | await mapCore.setEngine(engineType) | 是,并自动恢复托管图层 |
| 切换当前场景模式 | mapCore.setSceneMode({ mode }) | 否 |
| 移动当前视角 | mapCore.setInitialView(view) | 否 |
| 切换当前底图 | mapCore.setBasemapById(id) 或 mapCore.setBasemap(payload) | 否 |
| 切换当前 DEM | mapCore.loadDEMById(id)、mapCore.loadDefaultDEM() 或 mapCore.loadDEM(payload) | 否 |
| 修改当前或新建源解析传输层 | mapCore.updateSourceTransportLayer(payload) 或 mapCore.upsertSourceTransportLayer(payload) | 否 |
| 修改 Cesium 渲染质量或整套初始化默认值 | setConfig() 后重新 init() | 是 |
特别注意:不要用 setConfig({ engine: { active: 'leaflet' } }) 切换已经显示的地图;应直接调用 setEngine('leaflet')。前者会让 getEngineType() 变成 leaflet,但底层仍可能是 Cesium,随后调用相同目标的 setEngine('leaflet') 还会被当作“已经是当前引擎”而跳过。若已经这样修改,应调用 init() 重建并校正地图实例。
默认底图配置
不传 config.basemap 时,内置默认配置如下:
basemap: {
defaultVisibleId: 'tianditu-imagery',
// 当前为保留字段,初始化时不会读取。
defaultAnnotationId: 'tianditu-vector-label',
annotationOnTop: false,
}默认底图列表:
| id | 名称 | category | enabled | type | provider | resourceKey | 默认用途 | 引擎支持 |
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
| tianditu-vector | 天地图矢量底图 | basemap | true | wmts | tianditu | vector | 可作为 defaultVisibleId | Cesium / Leaflet |
| tianditu-imagery | 天地图影像底图 | basemap | true | wmts | tianditu | imagery | 内置 defaultVisibleId | Cesium / Leaflet |
| tianditu-terrain | 天地图地形底图 | basemap | true | wmts | tianditu | terrain | 可作为 defaultVisibleId | Cesium / Leaflet |
| tianditu-vector-label | 天地图矢量注记 | annotation | true | wmts | tianditu | vectorLabel | 可作为底图的 annotationResourceKey | Cesium / Leaflet |
| tianditu-terrain-label | 天地图地形注记 | annotation | true | wmts | tianditu | terrainLabel | 可作为底图的 annotationResourceKey | Cesium / Leaflet |
说明:
defaultVisibleId应指向category: 'basemap'的底图。defaultVisibleId找不到可用项时,会回退到basemap.list中第一个enabled !== false、分类和引擎均匹配的底图;仍找不到时使用内置天地图矢量兜底配置。defaultAnnotationId当前只是保留字段,不参与初始化。要指定注记,给底图项设置annotationResourceKey;天地图矢量、影像和地形底图未显式设置时,也会分别推断对应注记资源。tianditu-terrain是天地图地形底图瓦片,不是 Cesium 的 DEM 高程地形;如果要控制 Cesium terrainProvider,请看后文 DEM。geoserver-wmts-sample和geoserver-wms-sample默认enabled: false,只是配置格式示例;如果要作为默认底图,需要替换真实服务地址并改为enabled: true。- 天地图 provider 内置资源还包括
imageryLabel;默认basemap.list没有单独注册影像注记 id,但resourceKey: 'imagery'会自动推断使用imageryLabel注记。 - 内置天地图会根据地图
crs选择_c或_w瓦片矩阵;自定义 XYZ / WMTS / WMS 的 URL 和矩阵必须由业务保证与地图crs一致。地图已创建后不能通过普通底图切换改变 CRS,必须修改配置并重新init()。
底图切换
底图可以来自 config.basemap.list,也可以直接传配置对象。当前适配层支持:
- 天地图:
provider: 'tianditu'或传resourceKey - URL 模板瓦片:
type: 'wmts' / 'xyz' / 'tile' - WMS:
type: 'wms'
按配置 id 切换:
const result = mapCore.setBasemapById('tianditu-imagery')
if (!result.success) {
console.warn(result.message)
}直接传底图配置:
mapCore.setBasemap({
id: 'custom-xyz',
name: '自定义 XYZ',
category: 'basemap',
type: 'xyz',
provider: 'custom',
url: 'https://example.com/tiles/{z}/{x}/{y}.png',
minZoom: 0,
maxZoom: 18,
subdomains: ['a', 'b', 'c'],
})
mapCore.setBasemap({
id: 'custom-wms',
name: '自定义 WMS',
category: 'basemap',
type: 'wms',
provider: 'custom',
serviceUrl: 'https://example.com/geoserver/wms',
layers: 'workspace:layer',
parameters: {
transparent: true,
format: 'image/png',
version: '1.1.1',
},
})方法说明:
| 方法 | 参数 | 说明 |
| --- | --- | --- |
| setBasemapById(id) | 底图 id | 从 config.basemap.list 查找底图并切换。会校验 category / engineSupport / enabled。 |
| setBasemap(payload) | 底图配置对象 | 直接切换到底图配置。 |
常用底图参数:
| 参数 | 是否必填 | 说明 |
| --- | --- | --- |
| id | 建议必填 | 底图唯一 id。 |
| name | 选填 | 底图名称。 |
| category | 建议传 basemap | setBasemapById 会拒绝非 basemap 分类。 |
| type | 自定义服务必填 | wmts、xyz、tile、wms。 |
| provider | 选填 | 天地图传 tianditu,自定义服务传 custom。 |
| resourceKey | 天地图必填 | vector、imagery、terrain 等。 |
| annotationResourceKey | 选填 | 天地图注记资源,如 vectorLabel、imageryLabel。 |
| url | URL 模板必填 | wmts/xyz/tile 使用,支持 {z}/{x}/{y} 模板。 |
| serviceUrl | WMS 必填 | WMS 服务地址;也可用 url。 |
| layers | WMS 必填 | WMS 图层名。 |
| parameters | 选填 | WMS 附加参数。 |
| engineSupport | 选填 | 支持的引擎列表,如 ['cesium', 'leaflet']。 |
图层配置构造器
layers 出口提供四个快捷构造器,只负责生成标准图层配置,不会直接操作地图。构造结果可以放入 config.basemap.list,也可以传给 setBasemap()。
import { BaseGIS } from '@3clear/basegis'
import {
createGeoserverPyramidLayer,
createTiandituLayer,
createWmsLayer,
createWmtsLayer,
} from '@3clear/basegis/layers'
const basemapList = [
createTiandituLayer({
id: 'tianditu-imagery',
name: '天地图影像',
category: 'basemap',
resourceKey: 'imagery',
annotationResourceKey: 'imageryLabel',
}),
createGeoserverPyramidLayer({
id: 'geoserver-pyramid',
name: 'GeoServer 金字塔瓦片',
category: 'basemap',
url: 'https://example.com/tiles/{z}/{x}/{y}.png',
}),
createWmsLayer({
id: 'weather-wms',
name: '气象 WMS',
category: 'basemap',
serviceUrl: 'https://example.com/geoserver/wms',
layers: 'workspace:weather',
parameters: {
transparent: true,
format: 'image/png',
},
}),
createWmtsLayer({
id: 'weather-wmts',
name: '气象 WMTS',
category: 'basemap',
engineSupport: ['cesium'],
url: 'https://example.com/geoserver/gwc/service/wmts?' +
'SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=workspace:weather&' +
'STYLE=default&TILEMATRIXSET=EPSG:3857&FORMAT=image/png&' +
'TILEMATRIX=EPSG:3857:{z}&TILEROW={y}&TILECOL={x}',
}),
]
const mapCore = new BaseGIS({
containerId: 'map',
config: {
basemap: {
list: basemapList,
defaultVisibleId: 'tianditu-imagery',
},
},
})四个构造器都会保留额外传入字段,便于继续配置 engineSupport、缩放级别、注记资源或服务参数。
createWmtsLayer() 只生成配置,不会根据 layer / tileMatrixSet 自动拼接请求。自定义 WMTS 需要完整的 GetTile URL 模板,矩阵标识应按服务实际配置填写;XYZ 瓦片同样使用包含 {z}/{x}/{y} 的 URL。
模板瓦片不会自动重投影。上面的 EPSG:3857 WMTS 示例限定用于 Cesium;当前 Leaflet 使用 EPSG:4326,自定义瓦片需匹配地图的坐标系和瓦片矩阵。
layers 还导出辅助函数 createTiandituTileSource(resourceKey = 'imagery'),返回 { url, subdomains },资源不可用时返回 null。它供需要直接操作引擎的特殊页面使用,普通页面仍优先调用 createTiandituLayer() 和 BaseGIS。
BaseGIS 基础能力
这一节列的是 BaseGIS 主入口直接提供的基础能力。业务页面优先调用这些方法;图片图层、网格图层、海量点、点位聚合、点位抽稀、等值线、风场等更复杂能力,建议使用后文 methods 中对应的 Controller。
1. 生命周期与实例
BaseGIS 负责地图实例的初始化、状态读取、尺寸刷新和销毁。构造参数、完整配置项、参数优先级及 setConfig() 的生效规则见地图初始化与运行时配置。
const mapCore = new BaseGIS({
engineType: 'cesium',
containerId: 'map',
})
const initResult = mapCore.init()
if (!initResult.success) {
console.warn(initResult.message)
}
const engineType = mapCore.getEngineType()
const config = mapCore.getConfig() // 只读使用,不要直接修改。
const mapInstance = mapCore.getMapInstance()
mapCore.resize()
mapCore.destroy()方法说明:
| 方法 | 参数 | 说明 |
| --- | --- | --- |
| init(options) | { container, containerId, engineType, sceneMode, initialView } | 初始化地图。重复调用会重建地图并异步恢复 BaseGIS 托管图层。 |
| whenReady() | 无 | 等待最近一次 init() 触发的托管图层恢复完成。 |
| destroy() | 无 | 销毁当前地图实例和 adapter,并清空托管图层快照。 |
| setConfig(config) | Object | 合并实例配置,无返回值且不自动重建地图;查看完整规则。 |
| getEngineType() | 无 | 返回当前引擎类型。 |
| getConfig() | 无 | 返回当前配置对象引用,建议只读。 |
| getMapInstance() | 无 | 返回底层地图实例:Cesium viewer 或 Leaflet map。 |
| getMapContainer() | 无 | 返回统一结果,data.container 为地图容器 DOM。 |
| resize() | 无 | 在容器尺寸改变、隐藏面板重新显示后刷新地图尺寸。 |
2. 切换引擎
BaseGIS 支持 cesium 和 leaflet 两种引擎。调用 setEngine() 可以在运行时切换引擎、保留当前视野,并恢复 BaseGIS 托管图层。
| 方法 | 参数 | 说明 |
| --- | --- | --- |
| setEngine(engineType, options) | 引擎类型、初始化参数 | 未初始化时记录默认引擎;已初始化时自动切换、保留视野并恢复托管图层。 |
| switchEngine(engineType, options) | 引擎类型、初始化参数 | setEngine() 的兼容别名。 |
目标引擎与当前引擎相同时,setEngine() 会直接返回,不会应用 options.sceneMode / initialView / container。同一引擎下调整场景或视角应调用 setSceneMode()、setInitialView();需要重建地图时直接调用 init()。
发生实际切换时,setEngine() 会把目标场景模式,以及显式传入或从当前地图保留的视角写回 config.view,供之后再次初始化使用。
推荐写法
页面上做 Cesium / Leaflet 切换时,直接调用 setEngine()。它会保留当前视野、重建目标引擎,并等待托管图层自动恢复。
async function changeEngine(engineType) {
if (!mapCore || mapCore.getEngineType() === engineType) {
return
}
const result = await mapCore.setEngine(engineType)
if (!result.success) {
console.warn(result.message)
return
}
}直接重复调用 init({ engineType }) 也会自动开始恢复;如果后续逻辑依赖恢复完成,需要再等待 whenReady():
const result = mapCore.init({
engineType: 'leaflet',
containerId: 'map',
})
if (result.success) {
await mapCore.whenReady()
}switchEngine() 作为兼容别名保留,行为与 setEngine() 一致。
切换后的图层处理
引擎切换不是把 Cesium 图层对象“搬到” Leaflet,也不是把 Leaflet 图层对象“搬到” Cesium。BaseGIS 会保存托管图层的业务参数,并在新 adapter 中重新创建图层。
- 自动恢复范围包括图片、网格、海量点、点位聚合、点位抽稀、独立线、源解析传输、台风路径、等值线、统一风场和三维体图层。
- 图层最新的数据参数、显隐、清空、删除、海量点删除和高亮状态会同步到 BaseGIS 快照。
- 快照只保存业务参数引用,不复制大数组,不保存任何底层引擎对象。
- 三维体、源解析传输切到 Leaflet 时会返回不支持结果,但快照仍保留,切回 Cesium 后会继续恢复。
- Cesium 专有能力在 Leaflet 下不可用,例如 DEM、三维体渲染、三维切片/剖面渲染。
- 基础绘制对象、GeoJSON、点击/视图监听、GPU 专用风场、独立
WindFieldMethods、剖面图层和页面直接创建的底层引擎对象不在托管范围内,需要业务重新加载或绑定。
切换结果中可以查看恢复明细:
const result = await mapCore.setEngine('leaflet')
console.log(result.data?.restore?.restored)
console.log(result.data?.restore?.failed)result.success 表示引擎初始化/切换是否成功,不保证每个图层都恢复成功;应同时检查 data.restore.failed。不要在切换前先调用 destroy(),它会清空托管快照。
3. 视角控制与场景模式
视角控制分为缩放、重置视角、设置初始视角、场景模式切换、视图状态读取和视图变化监听。
// 放大 / 缩小。
// Cesium 可传 { distance },Leaflet 可传 { step }。
mapCore.zoomIn()
mapCore.zoomOut()
mapCore.zoomIn({ distance: 300000 })
mapCore.zoomOut({ step: 1 })
// 回到配置中的 initialView,也可以传入目标视角覆盖。
mapCore.resetView()
mapCore.resetView({
center: [104, 35],
height: 3000000,
zoom: 5,
pitch: -90,
})
// 设置视角。center / position 均为 [经度, 纬度]。
mapCore.setInitialView({
center: [104, 35],
// Cesium 使用 height。
height: 3000000,
// Leaflet 使用 zoom。
zoom: 5,
// Cesium 使用 heading / pitch / roll,单位是度。
heading: 0,
pitch: -90,
roll: 0,
})
// 场景模式。Cesium 支持 2d / 2.5d / 3d;Leaflet 只支持 2d。
mapCore.setSceneMode('3d')
mapCore.setSceneMode({
mode: '2d',
// Cesium morph 动画时长,单位秒。
duration: 0.4,
// Cesium 场景切换后是否尽量恢复原视野,默认 true。
preserveView: true,
})
mapCore.getSceneMode()
// 获取当前视图边界。
const boundsResult = mapCore.getViewBounds()
// boundsResult.data: { west, south, east, north }
// 获取当前视图状态。
const viewStateResult = mapCore.getViewState()
// Cesium 通常包含 center / bounds / height / heading / pitch / roll / engineType。
// Leaflet 通常包含 center / bounds / zoom / engineType。
// 让地图适配指定经纬度范围,可用于多地图首次统一范围。
mapCore.fitViewBounds({
bounds: { west: 73, south: 18, east: 135, north: 54 },
animate: false,
padding: 0,
})
// 经纬度转地图容器像素坐标,常用于自定义 HTML 浮层定位。
const pointResult = mapCore.projectToContainerPoint({
longitude: 104,
latitude: 35,
height: 0,
})
// pointResult.data: { x, y }视图变化监听:
const viewListener = mapCore.onViewChange({
// Cesium: camera.moveStart / morphStart;Leaflet: movestart / zoomstart。
onStart() {},
// Cesium: camera.changed;Leaflet: move / zoom / resize。
// 缩放过程中要实时刷新点位样式时,优先用 onChange。
onChange() {},
// Cesium 交互期间逐渲染帧检测相机变化;适合多地图实时联动。
// 默认 false,普通业务监听无需开启。
continuous: true,
// Cesium: camera.moveEnd / morphComplete;Leaflet: moveend / zoomend / resize。
onEnd() {},
// Cesium onEnd 延迟,默认 120ms。
endDelay: 120,
})
// 组件卸载时移除监听。
viewListener.data?.off?.()方法说明:
| 方法 | 参数 | 说明 |
| --- | --- | --- |
| zoomIn(payload) | Cesium { distance };Leaflet { step } | 放大地图。 |
| zoomOut(payload) | Cesium { distance };Leaflet { step } | 缩小地图。 |
| resetView(payload) | 视角对象,可选 | 回到初始视角或传入的目标视角。 |
| setInitialView(payload) | { center, height, zoom, heading, pitch, roll } | 设置当前视角。 |
| setSceneMode(payload) | '2d'/'2.5d'/'3d' 或 { mode, duration, preserveView } | 切换场景模式。 |
| getSceneMode() | 无 | 获取当前场景模式。 |
| getViewBounds() | 无 | 获取当前视图经纬度边界。 |
| getViewState(payload) | 可选 {includeBounds:false} | 获取当前视图状态;实时同引擎联动可关闭较重的范围采样。 |
| fitViewBounds(payload) | {bounds:{west,south,east,north},animate?,padding?,duration?} | 适配指定经纬度范围;duration 仅用于 Cesium 动画。 |
| onViewChange(payload) | { onStart, onChange, onEnd, endDelay, continuous? } | 监听视图变化,返回 { off };Cesium 开启 continuous 后交互期间逐帧检测。 |
| projectToContainerPoint(payload) | { longitude, latitude, height } | 经纬度投影到地图容器像素坐标。 |
4. 地图截图与导出
screenshot() 用于导出当前 Cesium 或 Leaflet 地图,两个引擎使用完全相同的调用方式、参数和结果对象。默认截取当前可视范围(地图视口)并下载 PNG,不会自动拼接视口外尚未渲染的地图内容;只截取地图内容,不包含页面工具栏、弹窗等地图容器外的 DOM 浮层。
框选遮罩样式包含在 @3clear/basegis/style.css 中,使用 npm 包时应按安装章节导入该样式文件。
在线示例:地图截图与 PNG 导出。
直接下载 PNG
默认 download: true,页面不需要自己创建下载链接或处理 Blob。fileName 可指定文件名;不传时使用前缀和时间戳自动命名。
// 截取当前可视范围并直接下载。
const result = await mapCore.screenshot({ fileName: 'weather-map.png' })
if (!result.success) {
console.warn('截图导出失败', result.code, result.message)
}
// 左键拖拽框选区域,松开后下载;右键或 Esc 取消。
const selectResult = await mapCore.screenshot({
mode: 'select',
namePrefix: 'map-select',
})
if (!selectResult.success && selectResult.code !== 'CANCELLED') {
console.warn('框选导出失败', selectResult.message)
}按范围裁剪导出
// 按当前视野中的经纬度范围裁剪。
await mapCore.screenshot({
namePrefix: 'east-china',
bounds: { west: 115, south: 25, east: 123, north: 36 },
})
// 按相对当前地图视口左上角的 CSS 像素区域裁剪。
await mapCore.screenshot({
rect: { left: 120, top: 80, width: 640, height: 360 },
})获取 Blob,不自动下载
const captureResult = await mapCore.screenshot({
download: false,
fileName: 'weather-map.png',
})
if (captureResult.success) {
const { blob, fileName, width, height } = captureResult.data
// 页面可将 blob 交给自己的预览或上传逻辑,BaseGIS 不会自动上传。
console.log(blob, fileName, width, height)
}方法与参数
| 方法 | 参数 | 说明 |
| --- | --- | --- |
| screenshot(payload) | { mode?, download?, fileName?, namePrefix?, bounds?, area?, rect?, screenRect? } | 截取 Cesium / Leaflet 地图;mode: 'select' 进入交互框选,download 默认为 true。 |
| cancelScreenshotSelection(reason?) | 可选取消原因字符串 | 主动取消正在进行的框选;返回布尔值,表示是否取消了现有框选。 |
| 参数 | 默认值 | 说明 |
| --- | --- | --- |
| mode | 省略 | 默认直接截图;'select' 进入鼠标框选,也可简写为 screenshot('select')。 |
| download | true | 自动触发浏览器下载;传 false 只生成截图结果。 |
| fileName | 自动生成 | 优先于 namePrefix;未以 .png 结尾时自动追加扩展名。当前仅导出 PNG。 |
| namePrefix | map-screenshot | 自动文件名前缀;未指定名称的框选截图默认使用 map-select。 |
| bounds / area | 当前视口 | 经纬度范围别名,支持 { west, south, east, north } 或 { startLon, startLat, endLon, endLat }。 |
| rect / screenRect | 当前视口 | 屏幕区域别名,支持 { left, top, width, height },也可用 x / y 代替 left / top;单位为相对地图视口左上角的 CSS 像素。 |
取消框选时,原 screenshot() Promise 返回 success: false、code: 'CANCELLED' 和 data.cancelled: true;页面通常无需按错误弹窗处理。
成功结果的 data 包含 blob、width、height、mimeType、fileName、downloaded、screenRect 和 pixelRect。Cesium 读取 WebGL 场景画布;Leaflet 在首次调用时按需加载 DOM 渲染器,合成瓦片、SVG、Canvas 和 DOM Marker,并排除 Leaflet 自带控件。跨域地图资源需要服务端允许 CORS,否则对应资源可能缺失或截图返回 FAILED。为保证 WebGL 截图可靠,Cesium 初始化时会保留 drawing buffer;在超高分辨率大屏上会增加一定显存占用。
width / height / pixelRect 使用导出图片的实际像素,可能与 CSS 像素不同;downloaded: true 仅表示已触发下载,不代表浏览器已确认文件保存到磁盘。导出范围始终限制在当前视口内,不是整页长截图,也不支持自动加载并拼接屏幕外瓦片。
5. 绘制点、线、面、文字和 Marker
这些方法用于轻量绘制和样例验证。大量点位请优先使用后文的 PointLargeLayerController、PointClusterController、PointDensityController 等控制器。
点:
mapCore.drawPoint({
id: 'point-1',
name: '点位',
// 必填建议:[经度, 纬度]。
position: [104, 35],
height: 0,
// Cesium 使用 pixelSize;Leaflet 使用 radius。
pixelSize: 10,
radius: 7,
color: '#ff4d4f',
})线:
mapCore.drawLine({
id: 'line-1',
name: '连线',
positions: [
[103, 34],
[105, 36],
],
width: 3,
color: '#1677ff',
})面:
mapCore.drawPolygon({
id: 'polygon-1',
name: '区域',
positions: [
[102, 33],
[106, 33],
[106, 36],
[102, 36],
],
// Cesium 使用 fillColor;Leaflet 主要使用 color / fillOpacity。
fillColor: 'rgba(22, 119, 255, 0.25)',
color: '#1677ff',
outlineWidth: 2,
fillOpacity: 0.35,
// Cesium 默认贴地;显式传 height 时按非贴地面绘制。
height: 0,
})文字:
mapCore.drawText({
id: 'text-1',
name: '文字',
position: [104, 35],
text: '示例文字',
font: '16px Microsoft YaHei',
color: '#0f2d4d',
backgroundColor: 'rgba(255,255,255,0.7)',
// 默认 center/bottom,也可以传 left/top/right/bottom。
textAnchor: {
horizontal: 'center',
vertical: 'bottom',
},
textOffset: [0, 0],
})图片 Marker:
mapCore.addMarker({
id: 'marker-1',
name: '站点',
position: [104, 35],
iconUrl: '/marker.png',
iconSize: [32, 32],
// 也支持 iconWidth / iconHeight。
iconAnchor: [16, 32],
iconOffset: [0, 0],
label: '站点名称',
})Canvas 数据图标与扩散效果
mapCore.createMarkerIcon(options, data) 支持圆点、“数值 + 名称”、气象风向、“图片 + 名称”和“图片 + 数值 + 名称”五种 Canvas 样式。圆点用法:
const station = {
id: 'station-1',
name: '示例站点',
position: [104, 35],
value: 28.6,
status: 'normal',
windDirection: 90,
windSpeed: 6,
}
const markerIcon = mapCore.createMarkerIcon({
type: 'dot',
size: 18,
color: '#64748b',
getColor(data) {
if (data.status === 'normal') return '#00e600'
if (data.status === 'warning') return '#a67824'
return '#6b7280'
},
}, station)
mapCore.addMarker({
id: station.id,
name: station.name,
position: station.position,
data: station,
...markerIcon,
})“数值 + 名称”用法:
const markerIcon = mapCore.createMarkerIcon({
type: 'value-label',
value: station.value,
label: station.name,
showLabel: station.showName !== false,
color: '#64748b',
getColor(data) {
if (data.status === 'normal') return '#00e600'
if (data.status === 'warning') return '#a67824'
return '#6b7280'
},
}, station)
mapCore.addMarker({
id: station.id,
name: station.name,
position: station.position,
data: station,
...markerIcon,
})气象风向用法:
const markerIcon = mapCore.createMarkerIcon({
type: 'wind',
direction: station.windDirection,
label: station.name,
showLabel: station.showName !== false,
color: '#64748b',
getColor(data) {
if (data.windSpeed >= 17.2) return '#e5484d'
if (data.windSpeed >= 10.8) return '#f59e0b'
if (data.windSpeed >= 5.5) return '#2f80ed'
return '#00e600'
},
}, station)
mapCore.addMarker({
id: station.id,
position: station.position,
data: station,
...markerIcon,
})“图片 + 名称”用法:
import { gisInfoMarker } from '@3clear/basegis/assets'
const stationImage = new Image()
// 跨域图片需由服务端允许 CORS,并在设置 src 前配置 crossOrigin。
stationImage.src = gisInfoMarker
await stationImage.decode()
const markerIcon = mapCore.createMarkerIcon({
type: 'image-label',
image: stationImage,
imageSize: [20, 20],
label: station.name,
showLabel: station.showName !== false,
}, station)
mapCore.addMarker({
id: station.id,
position: station.position,
data: station,
...markerIcon,
})“图片 + 数值 + 名称”用法:
import { gisFactoryMarker } from '@3clear/basegis/assets'
const company = { id: 'factory-1', name: '示例企业', emissionValue: 12.5 }
const factoryImage = new Image()
factoryImage.src = gisFactoryMarker
await factoryImage.decode()
const markerIcon = mapCore.createMarkerIcon({
type: 'image-value-label',
image: factoryImage,
imageSize: [14, 14],
value: company.emissionValue,
label: company.name,
showValue: company.showValue !== false,
showLabel: company.showName !== false,
}, company)
mapCore.addMarker({
id: company.id,
position: [104.2, 35.1],
data: company,
...markerIcon,
})getColor(data) 同步接收 createMarkerIcon 第二个参数传入的原始数据并返回 CSS 颜色。回调异常、返回非法颜色或空值时使用 options.color,再无有效静态颜色时使用默认灰色。圆点 size 默认 18px,允许 4~96px,并使用中心锚点。value-label 的 value 位于上方彩色色块,label 位于下方白色名称块;showLabel: false 时只显示数值块,图标宽高和锚点会自动更新。wind 的 direction 使用角度,0 指向正上方(北)、90 指向右侧(东),按顺时针方向增加;label 位于下方白色名称块,showLabel: false 时可隐藏,风速颜色分级完全由业务回调决定。image-label 的 image 接收已加载的 CanvasImageSource,imageSize 控制图片尺寸;URL 图片应先加载一次再传入,以保持抽稀回调同步。image-value-label 在同一规则上增加右侧 value 和蓝色圆角色块;showValue: false 时不绘制数值底板,只保留图片本身,showLabel: false 可独立隐藏名称。两者都隐藏时得到纯图片图标,画布尺寸和锚点会按照 imageSize 自动收缩。该类型使用整数锚点减少低 DPR 下的文字模糊。返回的 iconUrl/iconSize/iconAnchor 可用于普通 Marker、点位抽稀及其他图片点位能力。
Cesium / Leaflet 双引擎扩散 Marker:
mapCore.addMarker({
id: 'alarm-station',
name: '告警站点',
position: [121.48, 31.23],
data: station,
iconUrl: '/marker.png',
iconSize: [34, 40],
pulse: {
color: '#22d3ee',
size: 88,
duration: 2,
ringCount: 2,
ringWidth: 2,
ringOpacity: 0.72,
},
clampToGround: true,
onClick({ data }) {
console.log(data)
},
})pulse 只是图标下方的效果层,不会生成中心点。pulse.size 是最大扩散直径(像素),ringWidth 是独立的屏幕像素线宽,ringOpacity 是圆环不透明度;duration 是单个圆环的周期(秒),ringCount 支持 1~3。页面不需要判断当前引擎;Leaflet 使用 SVG 动画,Cesium 使用 Billboard 缩放动画,两端保持相同的半径、线宽、透明度和相位变化曲线。只传 pulse 时仅显示扩散环;与 iconUrl 同时传入时,图标显示在扩散环上层。
使用 SVG 资源
assets 导出的资源均为可直接用于 iconUrl 的 SVG URL;传给 Canvas 图标的 image 时则需要先加载为图片对象。
| 导出 | 用途 |
| --- | --- |
| gisMarkerSample | 基础 Marker 示例。 |
| gisFactoryMarker | 工厂标记,适合企业排放数据。 |
| gisInfoMarker | 信息标记,适合站点名称与数值。 |
| typhoonPathIcon | 台风路径默认中心图标。 |
import { gisMarkerSample } from '@3clear/basegis/assets'
mapCore.addMarker({
id: 'marker-sample',
name: '示例站点',
position: [104, 35],
iconUrl: gisMarkerSample,
iconSize: [42, 50],
iconAnchor: [21, 50],
})清理图形:
mapCore.removeGraphic({ id: 'marker-1' })
mapCore.removeGraphic('marker-1')
mapCore.clearGraphics()方法说明:
| 方法 | 参数 | 说明 |
| --- | --- | --- |
| drawPoint(payload) | 点配置 | 绘制点。 |
| drawLine(payload) | 线配置 | 绘制线。 |
| drawPolygon(payload) | 面配置 | 绘制面。 |
| drawText(payload) | 文字配置 | 绘制文字。 |
| createMarkerIcon(options, data) | 图标配置、原始数据 | 直接返回 iconUrl/iconSize/iconAnchor;未知 type 返回 null。 |
| addMarker(payload) | Marker 配置 | 绘制图片或 pulse 扩散 Marker;未传图片或扩散参数时,Cesium 使用点、Leaflet 使用默认 Marker。 |
| removeGraphic(payload) | 图形 id 或 { id } | 删除指定图形。 |
| clearGraphics() | 无 | 清空通过基础绘制方法创建的图形。 |
6. GeoJSON 图层
upsertGeoJsonLayer() 用同一个 layerId 创建或替换 GeoJSON 点、线、面图层,两个引擎共用数据与样式参数。图层基础样式使用 style,按要素覆盖的完整样式由 styleCallback(feature, index) 返回。坐标按 GeoJSON 顺序传入 [经度, 纬度, 可选高度]。
const result = await mapCore.upsertGeoJsonLayer({
layerId: 'region-boundaries',
data: {
type: 'FeatureCollection',
features: [{
type: 'Feature',
id: 'region-1',
properties: {
name: '示例区域',
},
geometry: {
type: 'Polygon',
coordinates: [[[103, 34], [105, 34], [105, 36], [103, 36], [103, 34]]],
},
}],
},
styleCallback(feature) {
return {
color: '#36d3ff',
weight: 3,
opacity: 1,
dashArray: '8 4',
fillColor: '#1677ff',
fillOpacity: 0.4,
}
},
onClick({ feature, properties }) {
console.log(properties.name, feature)
},
})
if (result.success && result.data.bounds) {
mapCore.fitViewBounds({ bounds: result.data.bounds, animate: false })
}| 参数 | 说明 |
| --- | --- |
| layerId | 必填,图层唯一 id。 |
| data | 必填;支持 FeatureCollection、Feature、Geometry、Feature 数组、JSON 字符串或 URL。也兼容 geojson / geoJson / source / url。 |
| fetchOptions | 使用 URL 时传给 Fetch 的请求参数。业务接口通常由宿主 api/modules 获取后再传入 data。 |
| style | 统一样式:color / weight / opacity / dashArray / fillColor / fillOpacity / stroke / fill;Leaflet 圆点大小用 radius,Cesium 标记大小用 markerSize。 |
| styleCallback(feature, index) | 按数据返回样式覆盖;线、面支持全部 style 字段,点还支持 radius / markerSize;异常时使用基础样式。 |
| onClick(event) | 返回 layerId / feature / properties / data / event / target / engineType;data 为原始 Feature。 |
| visible | 默认 true。 |
| clampToGround | Cesium 是否贴地,默认 true。 |
mapCore.hideGeoJsonLayer({ layerId: 'region-boundaries' })
mapCore.showGeoJsonLayer({ layerId: 'region-boundaries' })
const stateResult = mapCore.getGeoJsonLayerState({ layerId: 'region-boundaries' })
console.log(stateResult.data) // layerId / layerType / visible / featureCount / bounds
mapCore.clearGeoJsonLayer({ layerId: 'region-boundaries' }) // 清空要素,保留图层。
mapCore.removeGeoJsonLayer({ layerId: 'region-boundaries' }) // 删除图层。styleCallback 可直接返回完整样式;需要按要素变化时,在回调中读取 feature.properties 后决定返回值即可。opacity: 0、fillOpacity: 0、stroke: false、fill: false 都会按原值保留。两个引擎都会渲染虚线,但 dashArray 的具体节奏是近似效果。
上述管理方法也可直接传 id 字符串;方法名兼容大写 GeoJSON,例如 upsertGeoJSONLayer()。更新时需重新提供完整数据和样式。GeoJSON 当前不在自动恢复清单中,切换引擎后需重新调用 upsertGeoJsonLayer()。
7. DEM 地形
仅 Cesium 支持。默认使用 ellipsoid-flat 椭球地形;tianditu-terrain 是底图瓦片,不会提供高程。当前 DEM 接口支持椭球和 Cesium World Terrain,配置中的 tianditu-dem 仍是占位项,不能作为已实现地形源使用。
// 使用 World Terrain 前,宿主需准备 Cesium Ion 等所需凭据与网络访问。
const result = mapCore.loadDEMById('cesium-world-terrain')
if (!result.success) {
console.warn(result.message)
} else if (result.data.loading) {
// 某些 Cesium 版本异步加载地形;调用返回成功不等于地形已经就绪。
const provider = await result.data.readyPromise
if (!provider) console.warn('World Terrain 加载失败')
}
mapCore.setTerrainExaggeration({ factor: 2 })
console.log(mapCore.getDEMState().data)| 方法 | 说明 |
| --- | --- |
| loadDEMById(id) | 从 config.dem.list 查找地形配置,常用 ellipsoid-flat / cesium-world-terrain。 |
| loadDEM(payload) | 直接传配置;支持 { id, sourceType: 'ellipsoid-flat' } 或 { id, sourceType: 'cesium-official', factory: 'worldTerrain' }。 |
| loadDefaultDEM() | 按当前 config.dem 加载默认地形。 |
| getDEMState() | data 返回 { id, enabled, exaggeration };enabled 按 id !== 'ellipsoid-flat' 判断,不代表异步地形已就绪。 |
| setTerrainExaggeration(payload) | 传数字或 { factor },范围 1~10。 |
恢复平面地形可调用 mapCore.loadDEMById('ellipsoid-flat')。Leaflet 不提供 DEM;调用失败时应检查 success / code / message,不要直接假定结果包含地形状态。
8. 点击事件
onClick 注册地图点击事件;offClick 移除当前点击监听。当前每个 adapter 只保留一个基础点击监听,重复调用 onClick 会先移除旧监听。
mapCore.onClick({
callback(event) {
console.log(event)
},
})
mapCore.offClick()Cesium 点击空地时的事件结构:
{
clickType: 'coordinate',
engineType: 'cesium',
coordinates: {
longitude: 104,
latitude: 35,
height: 0,
},
}Cesium 点击 Entity 时的事件结构:
{
clickType: 'entity',
engineType: 'cesium',
coordinates: {
longitude: 104,
latitude: 35,
height: 0,
},
target: {
id: 'point-1',
name: '点位',
entityType: 'entity',
},
}Leaflet 当前返回坐标点击:
{
clickType: 'coordinate',
engineType: 'leaflet',
coordinates: {
longitude: 104,
latitude: 35,
height: 0,
},
}说明:
- 基础
onClick适合地图空白点击、简单 Entity 点击。 - 海量点、点位聚合、点位抽稀等图层自己的点击事件,应使用对应 Controller 的点击回调参数。
多地图联动(双屏联动)
MapViewLinkController 注册多个已经初始化的 BaseGIS,并实时同步拖动、缩放和视角。每个地图仍可独立加载不同模式、时次和图层;控制器只负责视角,不创建地图,也不会在 unregisterMap()、clear() 或 destroy() 时销毁 BaseGIS。
在线示例:查看分屏布局中的页面地图联动。布局可混合地图、图表和表格,地图实例与联动由页面管理。
import { BaseGIS } from '@3clear/basegis'
import { MapViewLinkController } from '@3clear/basegis/methods'
const mapA = new BaseGIS({
engineType: 'leaflet',
containerId: 'map-a',
})
const mapB = new BaseGIS({
engineType: 'leaflet',
containerId: 'map-b',
})
mapA.init()
mapB.init()
const viewLinks = new MapViewLinkController({
enabled: true,
realtime: true,
mode: 'all', // all | leader | group
strategy: 'auto', // auto | view | bounds
leaderId: 'map-a',
syncInterval: 32, // 实时同步间隔,约 30fps
})
viewLinks.registerMap('map-a', mapA, { group: 'forecast' })
viewLinks.registerMap('map-b', mapB, { group: 'forecast' })
// 获取 BaseGIS、底层地图实例和全部 id -> BaseGIS 映射。
const registeredMapA = viewLinks.getMap('map-a')
const leafletMapA = viewLinks.getNativeMap('map-a')
const allMaps = viewLinks.getMaps()
// 以 map-a 为操作源放大,并实时同步其他地图。
viewLinks.zoomIn({ mapId: 'map-a', step: 1 })
// 程序化控制全部注册地图。
viewLinks.setView({ center: [104, 34], zoom: 5, height: 5000000 })
viewLinks.fitBounds({ bounds: { west: 73, south: 18, east: 135, north: 54 } })
// 页面卸载:控制器先解绑,地图仍由页面自己销毁。
viewLinks.destroy()
mapA.destroy()
mapB.destroy()构造参数:
| 参数 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| enabled | Boolean | true | 是否开启联动。 |
| realtime | Boolean | true | true 按动画帧同步移动/缩放;false 只在操作结束时同步。 |
| mode | String | 'all' | all 任意图控制;leader 仅主图;group 仅同组地图。 |
| strategy | String | 'auto' | auto 自动选择;view 优先中心和层级/高度;bounds 使用视图范围。 |
| leaderId | String | '' | 主图 id;空值会使用首个注册地图。 |
| syncInterval | Number | 32 | 实时同步的最小间隔,单位 ms;默认约 30fps,降低多 Cesium 实例同时渲染的压力。 |
| suppressionMs | Number | 220 | 被同步地图的事件抑制时长,避免反向循环。 |
| endDelay | Number | 80 | Cesium 操作结束事件延迟,单位 ms。 |
| onStateChange | Function | null | 注册、模式、主图和操作结束后的回调,参数为 { reason, state }。 |
联动与实例方法:
| 方法 | 参数 | 返回 / 说明 |
| --- | --- | --- |
| registerMap(id,mapCore,options) | options:{group?,enabled?,replace?} | 注册已初始化的 BaseGIS 并绑定实时视图监听。 |
| unregisterMap(id) | 地图 id | 解除监听和注册,不销毁 BaseGIS。 |
| refreshMap(id) | 地图 id | BaseGIS.setEngine() 后重新绑定新 adapter 的监听。 |
| getMap(id) | 地图 id | 返回 BaseGIS 或 null。 |
| getNativeMap(id) | 地图 id | 返回 Cesium Viewer / Leaflet Map 或 null。 |
| getMaps() | 无 | 返回新的 Map<id, BaseGIS>,修改它不会改变控制器注册表。 |
| getMapIds() / hasMap(id) | 可选 id | 查询注册状态。 |
| forEachMap(callback) | (mapCore,id,entry) | 遍历已注册 BaseGIS。 |
| getLeaderMap() / getActiveMap() | 无 | 获取主图或最近操作地图。 |
| setLeader(id) | 地图 id | 设置主图。 |
| setMode(mode) | all/leader/group | 更新联动模式。 |
| setStrategy(strategy) | auto/view/bounds | 更新视角映射策略。 |
| setMapGroup(id,group) | 地图 id、组名 | 更新地图所在联动组。 |
| setMapEnabled(id,enabled) | 地图 id、布尔值 | 单独启停某张地图的联动。 |
| setEnabled(enabled) / pause() / resume() | 可选布尔值 | 整体启停联动。 |
| setRealtime(realtime) | 布尔值 | 切换实时或操作结束后同步。 |
| syncFrom(id,options) | 地图 id、force/strategy 等 | 立即把指定地图视角同步给目标地图。 |
统一视角操作:
| 方法 | 参数 | 说明 |
| --- | --- | --- |
| zoomIn(payload) | {mapId?,step?,distance?} | 按显式 mapId、最近活动地图、主图的顺序选取操作源,再按联动规则同步。 |
| zoomOut(payload) | 同上 | 缩小并同步。 |
| setView(payload) | center,zoom,height,heading,pitch,roll | 设置全部注册地图视角。 |
| fitBounds(payload) | {bounds,animate?,padding?,duration?} | 让全部地图适配相同范围。 |
| resetView(payload) | 可选视角 | 重置全部地图。 |
| resizeAll(payload) | 可选参数 | 刷新全部地图容器尺寸。 |
| getState() | 无 | 返回模式、主图、注册地图、最近同步来源等状态。 |
| clear() / destroy() | 无 | 解绑全部监听并释放引用,不销毁 BaseGIS。 |
setView / fitBounds / resetView / resizeAll 始终作用于所有注册地图,不受联动分组或暂停状态限制。
同为 Leaflet 时会同步 center + zoom;同为 Cesium 时会直接同步相机经纬度、height + heading/pitch/roll;两种视图模型无法直接对应时,auto 才会计算并使用 bounds。Cesium 交互期间会逐帧检测相机变化,再按 syncInterval 合并为最新状态写入目标地图,避免目标地图追赶稀疏跳点,也避免四五个 Cesium 实例每帧重复计算完整视域。一次拖动期间会锁定唯一交互源,目标地图的程序化相机事件不会反向接管并形成反馈循环。
BaseGIS 切换引擎会重建 adapter,原视图监听随旧 adapter 销毁。切换完成后调用
viewLinks.refreshMap(id);控制器不会劫持或改写BaseGIS.setEngine()。
LineLayerController
独立管理一组折线,支持整体更新、显隐、清空、销毁和引擎切换恢复。每条线必须提供唯一 id 和至少两个 [longitude, latitude, height?] 坐标点;同一控制器同一时刻只播放一条线的逐步出线动画。
逐顶点色专题线与时间裁剪
lines[].colors 是六位 HEX 数组,长度必须与 positions 完全一致,颜色沿每两个相邻顶点连续插值,优先于 style.color:
import { LineLayerController } from '@3clear/basegis/methods'
const lineLayer = new LineLayerController({ mapCore, layerId: 'sampling-tracks' })
const startTime = Date.parse('2026-08-28T08:00:00+08:00')
await lineLayer.load({
lines: [{
id: 'sampling-track',
positions: [[113.30, 22.60], [113.31, 22.61]],
colors: ['#00FF00', '#FF3B30'],
times: [startTime, startTime + 60000],
}],
style: { width: 5, opacity: 1, clampToGround: false },
animation: { enabled: false },
currentTime: startTime,
})
lineLayer.setTime(startTime + 30000) // 显示前 30 秒已走过的路线。
lineLayer.setTime(null) // 恢复完整路线。需要行驶回放时,为各条线附加 times: [起点毫秒时间戳, 终点毫秒时间戳, ...],长度与坐标相同且严格递增。load 可设置 currentTime,加载后调用 lineLayer.setTime(time) 裁掉未来部分;setTime(null) 恢复完整路线。不带 times 的线不参与裁剪。成功返回 { success: true, data: { layerId, currentTime } },非法时间返回友好失败结果。
Cesium 使用一个批量 Primitive 做顶点色插值,时间用相对秒数存入顶点,回放每帧仅改材质参数。Leaflet 使用共享 Canvas 重绘已走过的线段,不重新投影或创建图层。这是实线专题叠加,不能与虚线、流动、单线 reveal 或贴地混用;Cesium 关闭深度测试,带时间的采样按直线连接,不用于地形遮挡/贴合,位置必须不同。数据/配色更新仍为全量替换,显隐和引擎切换保留时间。浓度映射、分车/断线、时钟和小车属于业务层;参考项目 /test-page-43。
实线、虚线与纯色
import { LineLayerController } from '@3clear/basegis/methods'
const lineLayer = new LineLayerController({
mapCore,
layerId: 'flight-route',
style: {
color: '#00d8ff',
width: 4,
opacity: 0.9,
pattern: 'dashed', // solid | dashed
dashLength: 14,
gapLength: 8,
},
})
await lineLayer.load({
lines: [{
id: 'route-1',
positions: [
[116.4, 39.9],
[117.8, 37.5],
[121.5, 31.2],
],
}],
})单条线的 style 可以覆盖控制器的顶层默认样式。
固定渐变与流动渐变
style.color 可以是 CSS 颜色字符串,也可以是渐变配置。stops 的 offset 按 0~1 升序排列;配置 flow 后渐变会沿线路循环流动。
const gradientStyle = {
width: 5,
pattern: 'solid',
color: {
type: 'gradient',
stops: [
{ offset: 0, color: '#00e5ff' },
{ offset: 0.5, color: '#2563eb' },
{ offset: 1, color: '#a855f7' },
],
flow: {
enabled: true,
durationMs: 2400,
direction: 'forward', // forward | reverse
},
},
}渐变与虚线可以同时配置。Cesium 和 Leaflet 的底层绘制机制不同,虚线端点和颜色交界处可能有轻微视觉差异。
飞机引领的逐步出线
飞机不是单独的类,而是通用 animation.icon 配置。省略 icon 时只播放线路逐步出现。
// BaseGIS 不内置飞机图片,替换成业务项目自己的资源地址。
const planeIconUrl = '/your-app/plane.svg'
const flightLine = new LineLayerController({
mapCore,
layerId: 'flight-route',
style: gradientStyle,
animation: {
enabled: true,
lineId: 'route-1',
mode: 'reveal',
durationMs: 12000,
autoplay: true,
loop: true,
icon: {
url: planeIconUrl,
size: [36, 36],
rotateToPath: true,
rotationOffsetDeg: 0,
},
},
})
await flightLine.load({
lines: [{
id: 'route-1',
positions: [
[116.4, 39.9],
[117.8, 37.5],
[121.5, 31.2],
],
}],
})
flightLine.pauseAnimation()
flightLine.playAnimation()
flightLine.restartAnimation()动画按照各段实际地理距离插值。hide() 暂停帧更新并记录播放状态,show() 恢复隐藏前正在运行的动画;clear() 和 destroy() 会取消动画。切换引擎会恢复播放/暂停意图,但不会保存逐帧进度,动画从起点重新计算。
参数
| 参数 | 默认值 | 说明 |
| --- | --- | --- |
| layerId | line-default | 独立线图层 id。 |
| lines | [] | 折线数组,每项包含唯一 id 和至少两个 positions 坐标。 |
| lines[].colors | - | 与坐标等长的六位 HEX 数组,优先于 style.color;用于实线专题叠加。 |
| lines[].times | - | 与坐标等长、严格递增的有限毫秒时间戳;必须与 colors 配合。 |
| currentTime | null | 路线时间裁剪位置;null 显示全部。 |
| visible | true | 初始是否可见。 |
| style.color | #2f80ff | 纯色字符串或渐变对象。 |
| style.width / style.opacity | 3 / 1 | 线宽和透明度。 |
| style.pattern | solid | solid 或 dashed。 |
| style.dashLength / gapLength | 12 / 8 | 虚线实段和间隔长度。 |
| animation.enabled | false | 是否启用逐步出线。 |
| animation.lineId | 单线时自动选择 | 多条线启用动画时必填。 |
| animation.durationMs | 10000 | 从起点到终点的时长。 |
| animation.autoplay / loop | true / false | 自动播放与循环。 |
| animation.icon | - | 可选的飞机、车辆或船舶图标配置。 |
方法
| 方法 | 说明 |
| --- | --- |
| mount(mapCore) | 后挂载 BaseGIS。 |
| load(payload, options?) / update(payload) | 加载或更新线、样式和动画。 |
| setTime(currentTime) | 更新路线时间裁剪;传 null 恢复完整路线,不重建全部线数据。 |
| show() / hide() / toggle() | 控制显隐。 |
| playAnimation() / pauseAnimation() / restartAnimation() | 控制沿线动画。 |
| clear() | 清空线和动画,保留控制器。 |
| refreshState() / getState() | 获取线数量、点数和动画状态。 |
| destroy() | 删除图层、动画和引擎恢复快照。 |
页面卸载时先执行 lineLayer.destroy(),再执行 mapCore.destroy()。
在线示例:查看 LineLayerController 双引擎应用 Demo。
TyphoonPathController
把已经整理好的台风实况和预报数据渲染为独立专题图层,统一支持 Cesium / Leaflet。控制器负责实况路径、强度着色节点、生命周期标签、移动中心、当前点 7/10/12 级四象限风圈、多机构预报、不确定性圆和播放定位。
BaseGIS 不请求台风接口,也不推算强度、风圈、预报或预警等级。分页筛选、接口字段转换、ECharts 和业务面板仍由应用层负责。24/48 小时警戒线属于普通业务折线,推荐与 LineLayerController 组合使用。
最小用法
import { TyphoonPathController } from '@3clear/