# map-2d —— 2D 地图（builder 型，vjmap 内核）

> 地图本体 / 数据源 / 图层目录 / 服务叠加 / 地图控件组件 / 事件交互 / 示例库。
> 返回目录：[index.md](index.md)；全量选项字典（最权威）：[widget-schema.generated.json](../widget-schema.generated.json)。

## 6. map-2d —— 2D 地图（builder 型，vjmap 内核）

- **type**: `map-2d`　**默认尺寸**: `[640,420]`　**默认名**: "2D地图"
- 自定义触发器 14 个 + `itemClicked`：要素类 `marker-click`（点）/`region-click`（面）/`map-select`/`map-unselect`；交互类 `map-blank-click`/`map-click`/`map-dblclick`/`map-hover`/`map-contextmenu`/`map-menu-item`(右键菜单项)；生命周期类 `map-ready`/`map-view-change`(视角变化)/`map-source-loaded`/`map-error`。事件**挂在 `map-2d` 自身**的 `options.interactions` 上（不是图层 piece 上）：运行态从地图 widget 读取并按命中图层分流派发（见 §6.8）。
- 自定义动作 17 个（`mapFlyTo`/`mapFitData`/`mapLayerVisible`/… 见 §6.8）；地图也可被通用 `changeOption` 直改选项（center/zoom/mapid/basemap/图层 enabled 等都有实时 watcher）。
- **结构**：`map-2d` 节点必须有 `pieces`，按以下三类排列（同一 `pieces` 数组）：
  1. **地图本体 `map-body`**——单例、不可删、建议 `uid:"map-body"`、放第一个（§6.1）。
  2. **数据源 `map-source-*`**——不渲染，把数据归一化供图层引用（§6.2，可选）。
  3. **图层 `map-*`**——真正在地图上出图的图元（标绘/特效/服务，§6.4）。

```jsonc
{
  "type": "map-2d", "uid": "w_map",
  "options": { "position": [0,0], "size": [1894,1078], "interactions": { /* §6.8 */ } },
  "pieces": [
    { "type": "map-body",          "uid": "map-body", "enabled": true, "options": { "coordinate-system": "geographic", "basemap": "gaode-vec", "map-center": [116.4,39.9], "map-zoom": 10 } },
    { "type": "map-source-geojson", "uid": "src_geo",  "enabled": true, "options": { "geojson-text": "{...FeatureCollection...}" } },
    { "type": "map-circle",        "uid": "ly_pts",   "enabled": true, "options": { "data-input": "source", "data-source-uid": [{ "refType": "element", "targetPath": ["src_geo"] }], "point-color": "#38BDF8" } }
  ]
}
```

- 每个 piece 是同一种节点：`{ uid, type, enabled, options, stateOverrides? }`。**`enabled` 写在节点顶层**（与 `options` 平级，非 option）：`false`=该图层/源隐藏（图层树灰显）。
- **图层叠放(z-order)** = `pieces` 数组顺序：靠后的图层在上。例外：服务图层开 `ov-as-base:true`（见 §6.5/6.6）会被移到 CAD 底图之下作背景。
- **坐标系**由 map-body 的 `coordinate-system` 决定：`geographic`(互联网经纬度底图) / `cad`(vjmap 工程图)。**编辑器新建地图默认 `cad`**。各数据源/图层的坐标自动跟随本体坐标系，无需各自再选。

### 6.1 piece: map-body（地图本体 / 场景，单例不可删）

- **type**: `map-body`（建议 `uid:"map-body"`），`enabled:true`，无数据绑定字段。先定 `coordinate-system`，再按模式填底图(地理)或 CAD 服务。
- 中心/缩放/朝向/俯仰会随用户拖动**回写**到选项（不入历史）；改这些选项也会 `easeTo` 应用。

**基础（`style.base`）**：

| name | 类型 | 默认 | 说明 |
|---|---|---|---|
| `coordinate-system` | `select(radioGroup)` | **`cad`** | `cad`(工程图) / `geographic`(地理·互联网底图) |
| `map-center` | `vector<经度,纬度>` | `[0,0]` | 中心点（CAD 模式为 X,Y） |
| `map-zoom` | `number(0~24,step .1)` | `10` | 缩放级别 |
| `map-bearing` | `number(0~360,°)` | `0` | 旋转角（互联网图叠 CAD 时取四参数让路网朝上） |
| `map-pitch` | `number(0~85,°)` | `0` | 俯仰角（看 3D 立体面/线/柱时用） |
| `background-color` | `color` | `#0b1a2b` | 背景色 |

**底图（`style.basemap`，仅 `coordinate-system=geographic`）**：

| name | 默认 | 说明 |
|---|---|---|
| `basemap` | `gaode-vec` | `blank`/`tianditu-vec`/`tianditu-img`/`tianditu-img-label`/`gaode-vec`/`gaode-img`/`gaode-img-label`/`osm`/`xyz` |
| `basemap-xyz-url` | `''` | `basemap=xyz` 时的自定义 `{z}/{x}/{y}` 瓦片 URL |
| `tianditu-token` | `''` | 天地图底图所需 token |
| `glyphs-url` | `''` | 字体(glyphs)地址；地理模式要文字/聚合计数图层时用。留空沿用地图服务自带字体（CAD 模式自带） |

**CAD 服务（`style.cad`，仅 `coordinate-system=cad`，对接 vjmap）**：

| name | 默认 | 说明 |
|---|---|---|
| `service-url` | `''` | 服务地址（如 `https://vjmap.com/server/api/v1`）；留空继承「项目设置→地图」与全局默认 |
| `service-token` | `''` | 访问 token；同样可级联继承 |
| `cad-map-mode` | `blank` | `blank`(空白地图：按 `cad-blank-bounds` 正方形范围建图，**不加载 DWG**) / `uploaded`(展示已上传图纸，需 `mapid`) |
| `cad-blank-bounds` | `[-1000,-1000,1000,1000]` | 空白模式的 CAD 坐标范围(米)，自动扩为正方形（仅 `blank`） |
| `mapid` | `''` | 已上传 DWG 的图 ID（仅 `uploaded`；可用属性面板「选择地图」挑选） |
| `map-version` | `''` | 图版本，留空取最新（仅 `uploaded`） |
| `map-open-way` | `raster` | `vector`(矢量·可无级缩放/交互) / `raster`(栅格瓦片·加载快) / `memory`(内存·频繁更新) |
| `cad-clip-zoom` | `0` | **放大层级**：`openMap` 时 `clipbounds=2^n`。互联网瓦片叠 CAD 时常用 **8**（加深瓦片金字塔）；WMTS「坐标系不变」叠加则用 **3**（对照 28wmtsztMultimap，预留 8 倍范围给叠加图）。0=不放大。 |

**控件（`style.controls`，默认全关，各控件独立开关+停靠角 `*-position`：`top-left/top-right/bottom-left/bottom-right`）**：
`control-scale`(比例尺)、`control-zoom`(缩放按钮)、`control-compass`(指南针)、`control-fullscreen`(全屏)、`control-mouseposition`(坐标位置，配 `-digits/-latlng/-zoom`)、`control-minimap`(缩略图鹰眼)、`control-draw`(CAD 绘图工具栏，配 `-theme/-direction/-corner`)。

**视图/特效/图像/格网**：

| 组 | 关键 option | 说明 |
|---|---|---|
| 视图 | `auto-fit-data`(false) | 数据更新后自动缩放到全部要素范围 |
| 视图 | `perspectives`(array 簇) | 「视角预设」：每条含 `name`+`lng/lat/zoom/bearing/pitch`；`mapFlyTo` 动作可按名飞到 |
| 特效 | `fx-quality` | `lite`/`standard`(默认)/`cinematic`：全图 GPU 特效(fx)质量档 |
| 图像 | `map-images`(array 簇) | 图标库：每条 `key`(引用名)+`type`(`url`/`builtin`)+`url`/`builtin`+`pixel-ratio`/`sdf`；图层「图标」按 `key` 引用 |
| 格网 | `grid-display`(`auto`/`on`/`off`) | 仅 CAD：坐标参考网格+坐标标签（`auto`=空白地图默认显示）；配 `grid-density/grid-line-color/grid-opacity/grid-show-label`… |

### 6.2 数据源 piece（`map-source-*`，数据驱动图层的推荐方式）

数据源**不渲染**，只把不同来源的数据**归一化**成要素/记录供图层引用（图层 watch 源版本，源刷新即重渲）。5 种：

| type | 名称 | 关键 option | 说明 |
|---|---|---|---|
| `map-source-geojson` | GeoJSON 源 | `geojson-text`(内联 FeatureCollection JSON) | 本地静态数据：粘贴/编辑文本或对话框随机生成。**远程数据用动态源** |
| `map-source-query` | CAD 查询源 | 服务默认继承本体；查询条件经对话框 | 从 vjmap CAD 图按图层/范围/属性查询图形要素 |
| `map-source-dynamic` | 动态数据 | HTTP GET/POST、轮询、处理脚本、引用其它源/工程连接 | 远程/接口/实时；与工程「连接子系统」对接 |
| `map-source-draw` | 图形绘制 | 地图上交互绘制点/线/面 → GeoJSON | 编辑器属性面板的「绘制工具」画完写回 `draw-features`；**运行态**的二次开发用 `VpMapHandle.startDraw()`（见 §6.8） |
| `map-source-mapcad` | CAD 绘制 | vjmapext MapCadLayer 自渲染 | CAD 标绘文档 |

**发布为项目数据源**（GeoJSON/查询/动态源有）：`source-publish:true` + `source-publish-name` + `source-publish-row-limit`(默认 500)——把源字段并入**工程数据树**，可被任意组件（含 marker 嵌入窗口里的组件）按字段绑定。发布后产生一条 `type:"map-source"` 的连接（`connUid` 形如 `__vp_map_<源piece的uid>`，表 uid 固定 `records`），见 §6.9 图层示例。

### 6.3 图层的数据来源（`data-input`：字段绑定 / 引用数据源）

**几乎所有标绘/特效图层共享**这套数据绑定（点/线/面/起讫各有映射子集）：

- `data-input`：`bind-fields`(默认，绑**工程连接**字段) / `source`(引用本地图下的 `map-source-*`)。
- `data-source-uid`：`data-input=source` 时必填，element 引用形如 `[{ "refType": "element", "targetPath": ["源piece的uid"] }]`。

**A) 字段绑定档（`bind-fields`）——直接绑工程连接三元组**：

| 图层类 | 绑定 option | 约束 |
|---|---|---|
| 点类(marker/circle/symbol/cluster…) | `marker-lng`,`marker-lat` | 各 1（地理填经纬度；CAD 填 X/Y） |
| 点类 | `marker-name`,`marker-value` | 0~1（名称/数值，供选中、弹窗、标签） |
| 线类(polyline/curve/arrowline…) | `line-lng`,`line-lat` | 各 1 |
| 线类 | `line-group`(分组连线),`line-order`(组内排序) | 0~1 |
| 起讫(flyline) | `fly-from-lng`,`fly-from-lat`,`fly-to-lng`,`fly-to-lat` + `fly-name`(0~1) | bind-fields 档用 `fly-*` 系列（**不是** `marker-*`/`line-*`）；source 档用 `src-from-*`/`src-to-*` |

未绑定字段时点类回退 `marker-points`(`"lng,lat;lng,lat"`)、线类回退 `line-points`（演示占位，不参与「数据自适应」）。

**B) 数据源档（`source`）——`src-*` 把源记录字段映射成几何/属性**（源自带几何时坐标字段可留空）：

| 映射 option | 适用 | 说明 |
|---|---|---|
| `src-lng-field`/`src-lat-field` | 点/线 | 源记录的经度/X、纬度/Y 字段名 |
| `src-name-prop`/`src-value-prop` | 点/面 | 名称/数值属性键（选中、弹窗、外部选中匹配） |
| `src-key-field` | 点/面 | 唯一键字段：刷新前后稳定要素 id、保留选中/悬停态 |
| `src-group-field`/`src-order-field` | 线 | 同组连成一条线 / 组内排序 |
| `src-from-lng-field`/`src-from-lat-field`/`src-to-lng-field`/`src-to-lat-field` | 起讫(flyline) | 每条记录的起点/终点经纬度 |

**C) 图层级通用 option**（写在图层 `options` 里；覆盖面按类分组，以 schema 为准）：

| option | 覆盖 | 默认 | 说明 |
|---|---|---|---|
| `enable-zoom` + `min-zoom`/`max-zoom` | 全部渲染图层（含服务/特效） | false / 0 / 24 | 仅在指定缩放区间显示该图层 |
| `feature-panel-uid` + `feature-panel-offset` | 28 种数据图层（标绘/聚合/动画/特效点线等，不含服务层与 map-popup） | [] / [0,0] | 点击要素打开一个「锚定窗口」并贴要素位置跟随显示（窗口内字段用「响应组件筛选」即显示该要素数据） |
| `tooltip-enable` + `tooltip-fields` + `tooltip-title-field` | 16 种 GL 要素图层（circle/symbol/线类/flyline/polygon/region/geojson/text/fill-extrusion/bar3d/geocircle/symbol-cluster/fill-extrusion-animate/fx-symbol；**不含** DOM 类 map-marker/popup/divoverlay/marker-cluster、heatmap、动画系、effect-marker） | false | 悬停命中要素浮层显示字段值 |
| `filter-expr` + `filter-combine`(all/any) | 同上 16 种 | '' / all | 属性过滤 DSL：每行「字段 运算符 值」，运算符 `== != > >= < <= in !in has !has` |
| 数据驱动样式（表达式）`*-mode`/`*-field`/`*-stops` | 各样式标量所在图层 | — | 任一样式标量(如 `point-color`/`line-width`)可按属性/缩放映射；`*-mode`=`const`/`field`/`zoom`，`*-stops` 每行「输入 => 值」。`zoom` 按地图缩放插值（不是要素字段）；GL 规定 zoom 只能作顶层 `interpolate`/`step` 的输入，故该属性在 zoom 模式下不再叠加悬停/选中覆盖 |

> DOM 标记 `map-marker` 的颜色同样支持这套写法（`marker-color-mode`/`marker-color-field`/`marker-color-stops`），但**只有 `const`/`field` 两档**（逐要素建 DOM，无法按缩放实时重算）。`marker-kind:'default'` 时改的是 vjmap 内置标记那段 SVG 的填充色，`pin`/`dot` 改自绘图形色；`icon` 与自定义 HTML 不吃颜色。

### 6.4 图层目录（按类别；样式项详见 widget-schema.generated.json）

**标绘类（数据 → 几何，支持上面 §6.3 的数据来源与通用项；点/面类可点选、派发 marker-click/region-click）**：

| type | 用途 | 关键样式 option |
|---|---|---|
| `map-marker` | DOM 标记（样式灵活，中小数量） | `marker-kind`(default/pin/dot/icon，自定义 HTML 用 `marker-custom-html`)、`marker-color` + `marker-color-mode/-field/-stops`(按等级配色)、`marker-size`、`marker-anchor`、`marker-label` |
| `map-circle` | WebGL 圆点（高性能，可聚合） | `point-color`、`point-radius`、`point-stroke-color/width`、`cluster`、`cluster-radius` |
| `map-symbol` | WebGL 图标+文字（海量点） | `symbol-color`、`symbol-size`(px)、`symbol-label`(bool)+`symbol-label-size/-color`、`icon-halo-color/-width`、图标引 `map-images` |
| `map-cluster` | 圆点聚合 | `cluster-radius`、`cluster-max-zoom`、`cluster-t1/t2` 分档 + `cluster-color-1/2/3`、`cluster-radius-1/2/3`、`cluster-count-size/-color` |
| `map-marker-cluster` / `map-symbol-cluster` | 标记/符号聚合 | 标记聚合用 `cluster-cell`+`cluster-color`+`point-kind/point-color/point-size`（**不是** `marker-*`）；符号聚合用 `cluster-radius`+`symbol-*` |
| `map-polyline` / `map-curve` / `map-arrowline` | 折线 / 平滑曲线 / 箭头线 | `line-color`、`line-width`、`line-opacity`、`line-cap/join`、`line-blur`、`line-dasharray`；曲线 `curve-tension`+`curve-segments`、箭头 `arrow-color/-size/-spacing` |
| `map-flyline` | 飞线（起讫 → 弧） | 用通用 `line-color`/`line-width`/`line-opacity` + `fly-arc`(**boolean**)；起讫见 §6.3-B |
| `map-animate-line` | 流动线（流光） | `flow-color`、`flow-width`、`flow-dash`、`flow-gap`、`flow-period`(ms，越小越快)、`flow-reverse`、`base-color`/`base-opacity`(静态轨道) |
| `map-path-animate` | 路径动画（圆点巡游） | `dot-color`、`dot-radius`、`dot-blur`、`dot-count`、`dot-period`(ms)、`dot-reverse`、`path-color`/`path-width`/`path-opacity` |
| `map-symbol-path-animate` | 符号沿路径 | `arrow-shape`(arrow/triangle/chevron/navigation/diamond/circle)、`arrow-color`、`arrow-size`、`arrow-count`、`move-period`(ms)、`path-*` |
| `map-polygon` / `map-region` / `map-geojson` | 多边形 / 区域填色(choropleth) / 综合点线面 | `fill-color`、`fill-opacity`、`line-color`；区域 `region-color-ramp`、`region-agg`(sum/avg/max/count)、`region-name-prop`，数值走 `src-value-prop` |
| `map-geocircle` | 地理圆/扇形 | `geocircle-shape`(circle/sector)、`geocircle-radius-mode`(fixed 公里 / value)、`geocircle-radius`/`-radius-factor`、`geocircle-fill-color`/`-fill-opacity`、`geocircle-line-color`/`-line-width` |
| `map-heatmap` | 热力（点密度+权重） | `heat-radius`、`heat-intensity`、`heat-opacity`、`heat-color-preset`/`heat-color-ramp`、`heat-weight-max`；权重取 `src-value-prop` |
| `map-text` | 文本标注 | `text-content`(name/value/fixed)+`text-fixed`、`text-size`、`text-color`、`text-halo-color/-width/-blur`、`text-anchor`/`text-offset` |
| `map-fill-extrusion` / `map-bar3d` | 立体面 / 三维柱 | 面：`extrusion-height-mode`(fixed/value)+`extrusion-height`/`-height-factor`、`extrusion-color/-opacity/-base`、`extrusion-vertical-gradient`；柱：`bar-height-mode`+`bar-height-factor`+`bar-size`。需 `map-pitch>0` |
| `map-line-extrusion` | 立体线（光墙/围栏） | `wall-color`、`wall-opacity`、`wall-height`、`wall-base`、`wall-width`（**不是** `line-*`/`extrusion-*`）。需 `map-pitch>0` |
| `map-animate-fill` | 呼吸面 | `fill-color`、`breath-min`/`breath-max`(不透明度上下限)、`breath-period`(ms)、`outline-color/-width/-opacity` |
| `map-animate-symbol` | 跳动符号 | `symbol-color`、`symbol-size`(px)、`bounce-amplitude`(%)、`bounce-period`(ms)、`symbol-label` |
| `map-fill-extrusion-animate` | 生长立体面 | `grow-min-height`/`grow-max-height`/`grow-period`/`grow-repeat-type`(reverse/loop) + `extrusion-color/-opacity`（**没有** `extrusion-height`） |
| `map-popup` / `map-divoverlay` | 信息窗 / DIV 覆盖物 | `popup-title-prop`、`popup-show-all`；`overlay-html`(模板 `${字段}`)、`overlay-anchor` |

**特效类（vjmapext fx，GPU 渲染，万级不掉帧；多为独立图层类型）**：

- 特效点(11)：`map-fx-fire`、`map-fx-pulse-ring`、`map-fx-halo`、`map-fx-radar`、`map-fx-smoke`、`map-fx-lightning`、`map-fx-meteor`、`map-fx-vortex`、`map-fx-shield`、`map-fx-firework`、`map-fx-crosshair`——关键 `fx-color`、`fx-opacity`、`fx-size`、`fx-size-units`。
- 特效线(5)：`map-fx-flowline`、`map-fx-meteor-line`、`map-fx-lightning-line`、`map-fx-arrow-flow`、`map-fx-dash-flow`——关键 `fx-line-color`、`fx-line-width`、`fx-line-speed`、`fx-line-dash`。
- 其它：`map-effect-marker`(DOM 呼吸光圈等，`effect-type`/`effect-colors`(**复数**，多色用 `;` 分隔)/`effect-width`/`effect-height`/`effect-text`+`effect-text-field`)、`map-fx-symbol`(fx 离屏渲成 symbol 图标，`fx-symbol-preset` 取 `radarSweep` 等 11 种)。

> **没有 `animate-speed` 这个 option**（它只属于 `scene3d-line`）。地图动画类图层各自用 `flow-period` / `dot-period` / `move-period` / `breath-period` / `bounce-period` / `grow-period` 控制速度，单位都是毫秒，越小越快。
> 同样不存在的还有：`flyline-color`/`flyline-width`（用 `line-*`）、`text-field`（用 `text-content`）、`bar-height-field`（用 `bar-height-mode`+`src-value-prop`）、`region-value-prop`（用 `src-value-prop`）、`effect-color`（用 `effect-colors`）、`symbol-text-field`（用 `symbol-label`）。
> 写之前对 `reference/widget-schema.generated.json` 核一遍：设了不存在的路径不会报错，只是**静默不生效**。
- 着色器图层（自定义 Shadertoy `mainImage` 片元）：`map-fx-shadertoy-point`(点)、`map-fx-shadertoy-line`(线，`fx-line-width`+`st-frag`)、`map-fx-shadertoy-fullscreen`(全屏叠加，默认 `fx-bucket:"topmost"`)——共用 `st-frag`(GLSL 源)、`st-alpha-mode`(`premul`/`opaque`/`additive`/`luma`亮度抠图/`colorKey`)、`st-res`(虚拟分辨率)、`fx-bucket`(绘制层级 `above`/`below`/`topmost`)。

**服务/环境类（多数无业务数据源，直接配 URL/参数；详见 §6.5）**：
`map-tile`(XYZ 瓦片底图)、`map-wms`、`map-wmts`、`map-image`(四角贴静态影像)、`map-video`(四角贴视频)、`map-mvt`(矢量瓦片)、`map-background`(背景色)、`map-sky`(天空)、`map-hillshade`(山体阴影)、`map-canvas`(自定义 Canvas)、`map-svgoverlay`(经纬度贴 SVG)。

### 6.5 服务图层详解：map-tile / map-wms / map-wmts

- **`map-tile`** XYZ 栅格底图：`tile-url`(`{z}/{x}/{y}` 模板)、`tile-size`(256)、`tile-opacity`。
- **`map-wms`** —— `wms-source-kind` 二选一（默认 `vjmap`）：
  - `ogc`：标准 OGC WMS。`wms-url`(基址)、`wms-layers`(LAYERS,逗号分隔)、`wms-version`(`1.3.0`/`1.1.1`)、`wms-format`(`image/png`)、`wms-transparent`。仅地理(Web 墨卡托)底图对齐。
  - `vjmap`：**把另一张 vjmap 服务图按坐标系叠加**到当前底图（含把互联网瓦片叠到 CAD）。叠加由对话框「选择叠加图」统一管理并写入隐藏簇 `ov-maps`（每图：`mapid`/`version`/`crs`/`ox`/`oy`/`scale`/`rotate`/`pts`(公共点)/`webtype`+`web`(互联网瓦片)）；`ov-mode`=`none`/`direct`(同坐标系)/`auto`(EPSG+偏移)/`param`(公共点四参数)/`arrange`(排列组合·网格平铺，配 `ov-arrange-cols`)；`ov-as-base:true`=置于 CAD 底图之下作背景。`ov-click-pick:true`=播放态点击叠加 CAD 栅格，按与瓦片同一套四参数反算后 `pointQueryFeature` 高亮实体并触发地图 `map-select`（对照 vjmap `06wmsselentity`；互联网底图需 `param` 公共点）。
  - 共用显示/影像调节：`wms-opacity`、`wms-tile-size`、`wms-saturation`/`wms-hue`/`wms-brightness`/`wms-contrast`/`wms-resampling`(linear/nearest)/`wms-fade-duration`。
- **`map-wmts`** —— `wmts-source-kind` 二选一（默认 `vjmap`）：
  - `ogc`：标准 OGC WMTS。`wmts-url`、`wmts-layer`、`wmts-matrix-set`(如 `w`)、`wmts-style`(`default`)、`wmts-format`、`wmts-version`(`1.0.0`)。
  - `vjmap`：**CAD 多图叠加**——把多张 vjmap 图(`wmts-maps`：`mapid`/`version`/`layernames`)各渲成栅格瓦片叠到 CAD 底图。写法二选一：编辑器数组簇 `{ indexes:["m0"], m0:{ mapid, version, layernames } }`，或工程 JSON / playground 数组 `[{ mapid, version, layernames }]`（运行时都认）。`wmts-clip-mode`=`base`(底图投影范围,非侵入；先有底图且坐标不变时本体需 `cad-clip-zoom:3`，对照 28wmtsztMultimap)/`union`(叠加图合并范围,会重渲底图并对齐取景)/`custom`(`wmts-clip-bounds`)；`wmts-vj-dark`(深色背景)。
  - 共用显示/影像调节同 WMS（前缀 `wmts-`）。
- 叠加图的对话框选项（`ov-*`/`wmts-*` 隐藏簇）一般**由编辑器「选择叠加图」对话框写入**，手写时照搬 §6.6 示例结构即可。叠加示例库见 `data/地图/叠加/`（01~15：WMS/WMTS 排列组合、坐标系自动变化、CAD↔互联网互叠、叠加图上点选 CAD 实体）。

### 6.6 互联网地图 ↔ CAD 叠加（headline 配方）

把**互联网瓦片（高德路网等）叠到 CAD 底图**（CAD 作底）：map-body 用 CAD 模式 + 一个 `map-wms`(vjmap 源) 图层。核心三件套：本体 `cad-clip-zoom:8`（加深瓦片金字塔）+ 图层 `ov-as-base:true`（互联网瓦片置于 CAD 线划之下作底）+ 本体 `map-bearing`（取四参数旋转角使路网朝上）。对照 vjmap demo `05fourparamrotatecad.js`：

```jsonc
"pieces": [
  { "type": "map-body", "uid": "map-body", "enabled": true, "options": {
      "coordinate-system": "cad", "cad-map-mode": "uploaded", "mapid": "sys_zp",
      "service-url": "https://vjmap.com/server/api/v1", "service-token": "<token>",
      "cad-clip-zoom": 8, "map-zoom": 8, "map-bearing": -39.39 } },
  { "type": "map-wms", "uid": "layer-wms", "enabled": true, "options": {
      "wms-source-kind": "vjmap", "ov-mode": "param", "ov-as-base": true, "ov-transparent": true,
      "ov-maps": { "indexes": ["m0"], "m0": {
        "mapid": "gaode-road", "webtype": "GCJ02",
        "web": "https://webrd0{1-4}.is.autonavi.com/appmaptile?...&x={x}&y={y}&z={z}",
        "pts": "[{\"x1\":...,\"y1\":...,\"x2\":116.48,\"y2\":39.96}, ...4 点...]" } } } }
]
```

反过来（**CAD 叠到互联网底图**）：map-body 用 `geographic` + 互联网底图，叠加 `map-wms`/`map-wmts`(vjmap 源) 指向 CAD `mapid`，`ov-mode:auto`(按 EPSG) 或 `param`(公共点)。完整可导入示例：`data/地图/叠加/11-互联网地图旋转叠加至CAD图`、`12-CAD图旋转叠加至互联网地图`、`13-多个CAD图旋转叠加`、`10-互联网地图叠加多张CAD图`。

### 6.7 地图控件组件（`map-control-*`，独立放置、关联地图）

独立顶层 widget（不在地图 pieces 内），共 12 个；都用 `target-map`(element，types=`map-2d`，留空=关联本页第一个 2D 地图) 绑定。外观项**按控件形态分两套**：面板类控件（coordinate/basemap/cad-layers/layers/overview/measure/legend/view）共享 `panel-bg`/`panel-border-color`/`panel-radius` + 多数带 `panel-color`/`panel-font-size` + `accent` 主题色；按钮/刻度/罗盘类（scale/zoom/compass/export）用自有键（`scale-*`/`btn-*`/`compass-*`，export 用 `btn-accent` 而非 `accent`）：

| type | 名称 | 默认尺寸 | 特有关键 options |
|---|---|---|---|
| `map-control-coordinate` | 坐标位置（随鼠标显示坐标） | `[196,70]` | — |
| `map-control-scale` | 比例尺 | `[120,32]` | — |
| `map-control-zoom` | 缩放按钮 | `[34,99]` | — |
| `map-control-compass` | 指南针（点击复位正北） | `[64,64]` | — |
| `map-control-export` | 导出图片/PDF | `[220,40]` | — |
| `map-control-view` | 视角+全屏一体条 | `[248,46]` | `view-show-pitch`(俯仰滑杆)+`view-pitch-max`、`view-show-rotate`/`view-show-north`/`view-show-reset`/`view-show-fullscreen`、`view-layout`(横/竖) |
| `map-control-basemap` | 底图切换 | `[232,150]` | `basemap-layout`(`grid`缩略卡/`segmented`/`list`/`dropdown`)、`basemap-allowed`(可选底图逗号串)、`basemap-default`、`basemap-xyz-url`/`basemap-tianditu-token` |
| `map-control-cad-layers` | CAD 图纸图层显隐 | `[240,320]` | `cad-show-search`、`cad-show-toolbar`(全选/反选)、`cad-default-all`、`cad-dark` |
| `map-control-layers` | 数据图层/数据源管理 | `[288,380]` | `layers-show-sources`、`layers-allow-edit`(运行态改图层属性)+`props-scope`(`style`/`style+data`)、`layers-allow-reorder` |
| `map-control-overview` | 鹰眼概览 | `[240,200]` | `overview-click-nav`(拖动回中)、`overview-show-cross`、`overview-margin`(概览缩小级数)、`rect-color`/`rect-fill`(视口框) |
| `map-control-measure` | 测距/测面 | `[232,132]` | `measure-mode`(`distance`/`area`)、`measure-distance-unit`(`auto`/`m`/`km`/`mi`)/`measure-area-unit`(`auto`/`m2`/`km2`/`ha`/`mu`=亩)、`measure-unit-label`(CAD 单位名)、`measure-line-color` |
| `map-control-legend` | 数据图例 | `[196,176]` | `legend-mode`(`manual`手动项/`ramp`色带分段/`size`气泡分级/`gradient`连续渐变)、`legend-items`(手动：每行`名称 => 颜色`)、`legend-colors`(色带)、`legend-min`/`legend-max`/`legend-count` |

> 与 map-body 自带的 `control-*`(内置控件，停靠地图四角) 区别：控件组件是**独立可摆放/可换肤**的画布元素。示例 `data/地图/图层与源/60~64`。

### 6.8 地图事件交互（触发器 → 动作）

- **触发器 14 个**（写在 `map-2d.options.interactions.groups`，按命中图层分流）：要素 `marker-click`/`region-click`/`map-select`/`map-unselect`；交互 `map-blank-click`/`map-click`/`map-dblclick`/`map-hover`/`map-contextmenu`/`map-menu-item`；生命周期 `map-ready`/`map-view-change`/`map-source-loaded`/`map-error`；另有通用 `itemClicked`。要素类 payload 为命中要素的属性行，公式 `{:字段}` 取值（如 `{:name}`、`{:value}`）。
- **动作**（17 个，目标元素=地图；列表/按钮等也可跨调地图）：`mapFlyTo`(飞到视角/经纬度)、`mapFitData`(缩放到数据范围)、`mapFitWhole`(缩放到全图)、`mapZoom`(放大/缩小)、`mapSetZoom`、`mapResetNorth`(复位方向)、`mapOrientation`(旋转+俯仰)、`mapPanBy`(像素平移)、`mapStop`(停止动画)、`mapLayerVisible`(显隐图层,选 `layer`+`mode`)、`mapSourceVisible`(显隐数据源)、`mapSwitchBasemap`(切互联网底图)、`mapSwitchMap`(切 CAD 图 `mapid`)、`mapClearSelection`(清除选中)、`mapRefreshData`(刷新数据源)、`mapSetLayerStyle`(运行态改某图层某属性)、`mapSetSourceData`(向数据源注入 GeoJSON/触发数据)。
- **运行态右键菜单**：`contextmenu-enable: true` + `contextmenu-items`（数组 `[{ label }]` 或编辑器簇 `{ indexes, [key]: { label } }`）+ 触发器 `map-menu-item` 按项 `triggerTarget` 分流。只写 items 不开 enable 时，底图会吞掉浏览器菜单、自定义菜单也不弹。
- ⚠️ 交互写成 `{ version: 2, groups: [...] }` 数组、按 `trigger` 内容匹配（无需键/哈希）。完整写法见 [interactions.md](../interactions.md)。
- ⚠️ **`mapFlyTo` 的 `zoom` 单给是 no-op**：执行顺序为 `view`(视角名)→`use-row-coords` 触发行 bbox（`fitGeoBBox`，`zoom` 只作 maxZoom）→`lng`+`lat` 都解析到才 `flyTo`。三个定位源都没有时静默 return——只想变 zoom 请用 `mapSetZoom`/`mapZoom`。`map-ready→mapZoom(mode:'in')` zoom 5→6 生效；`mapFlyTo({zoom:9})` zoom 停在 5。
- 运行态动作注册表不可达：`project/viewer` 上都无 `actionRegistry`/`actions` 公共入口，动作名只能靠文档核对。
- **`map-click`/`map-view-change` 程序化可派发**：`raw.map.fire('click', {lngLat,point})`（vjmap 事件系统合成）可派发 `map-click` 链；`flyTo()` 程序化移图可派发 `map-view-change`；容器 DOM `dispatchEvent(click)` 也走通（经 map 内部 click→地图事件）。要素级 `marker-click` 仍需真实命中检测，合成事件到不了。
- `map-control-*` 控件类 piece（`map-control-legend`/`map-control-view` 等）不进 `listLayers()` 且不回显在 `raw.pieces`——它们由地图内部控件槽位托管，别用 piece 数组回读校验控件存在性。`map-*` 图层 / 数据源 / 特效类 piece 则会正常回显。
- **二次开发交互绘制**（不是交互动作，是 SDK 句柄能力）：`ctx.getMap(uid).startDraw({ mode:'polygon', toolbar:['polygon','trash'] })`
  开一个 vjmap Draw 会话，在图上画点/线/面并拖顶点编辑，`onChange` 拿变化后的 GeoJSON。
  想让用户「在地图上选坐标 / 画分区边界」时用它，别自己接 `map-click` 逐点拼几何。详见 [platform/sdk.md](../platform/sdk.md) §4。

### 6.9 地图示例库（`data/地图/图层与源/`，全部可导入对照）

- 数据源/图层基础：`11 GeoJSON静态源一源驱动点线面`、`15 五类数据源总览`、`04 CAD一源三层`、`05 API-GeoJSON源`、`08/09 地图源发布`、`16/17 列表绑地图源点击行定位`。
- 单图层全集：`19~58` 每种图层一个示例（`数据源与事件·百分比`），`13 标绘图层全集`、`59 特效预设墙`。
- 服务/叠加：`14 服务瓦片影像图层组`、`50~57` 服务图层、`叠加示例/01~15`（WMS/WMTS/CAD↔互联网互叠）。
- 控件/样式：`60~64` 控件组件与内置控件、`65/66` 数据驱动样式（按属性/按缩放）。
- **生成器**：`scripts/generate-map-layer-examples.mjs` 批量产出单图层示例（CAD 空白 + GeoJSON 源驱动 + 点击事件 + 缩放/显隐/清除按钮，百分比自适应）。

### 6.10 VP-TSX 写法

`pieces` 是节点**顶层 prop**（不是 children）；模块顶层构造时标注 `VpMap2dPiece[]` 获得编译期选项键/枚举校验（映射总规则见 [vptsx.md](../vptsx.md)）：

```tsx
import { defineProject, h, type VpMap2dPiece } from '<...>/runtime'

const mapPieces: VpMap2dPiece[] = [
  { uid: 'map-body', type: 'map-body', enabled: true, options: { 'coordinate-system': 'geographic', basemap: 'gaode-vec', 'map-center': [116.4, 39.9], 'map-zoom': 10 } },
  { uid: 'src_geo', type: 'map-source-geojson', enabled: true, options: { 'geojson-text': '{...FeatureCollection...}' } },
  { uid: 'ly_pts', type: 'map-circle', enabled: true, options: { 'data-input': 'source', 'data-source-uid': [{ refType: 'element', targetPath: ['src_geo'] }], 'point-color': '#38BDF8' } },
]
// <map-2d uid="w_map" position={[0, 0]} size={[1894, 1078]} pieces={mapPieces}
//   interactions={interactions(on('marker-click', act('mapFlyTo', { … })))} />
```
