> **本文回答**：`vjmap.Symbol` 图标符号图层 —— iconImage / iconSize / iconOffset / 碰撞。

## 一、最小示例

```js
await map.loadImageEx("pin", "/icons/pin.png");

const symbol = new vjmap.Symbol({
  data: map.toLngLat(features),
  iconImage: "pin",
  iconSize: 1,
  iconAnchor: "bottom",
  iconAllowOverlap: true
});
symbol.addTo(map);
```

## 二、核心参数（图标）

| 参数 | 默认 | 说明 |
|---|---|---|
| `iconImage` | — | 图标名；需 sprite 或 loadImage |
| `iconSize` | 1 | 缩放比例 |
| `iconRotate` | 0 | 旋转角（度） |
| `iconAnchor` | "center" | 9 向 |
| `iconOffset` | `[0,0]` | 像素偏移 |
| `iconOpacity` | 1 | 透明度 |
| `iconColor` | — | 只对 SDF 图标有效 |
| `iconHaloColor` | — | 光晕色 |
| `iconHaloWidth` | 0 | 光晕宽度 |
| `iconAllowOverlap` | false | 是否允许重叠 |
| `iconIgnorePlacement` | false | 是否忽略位置冲突 |
| `iconRotationAlignment` | "auto" | map / viewport |
| `iconPitchAlignment` | "auto" | map / viewport |
| `iconPadding` | 2 | 碰撞的像素间距 |
| `iconTextFit` | "none" | none / width / height / both |
| `iconTextFitPadding` | `[0,0,0,0]` | text-fit 的内边距 |

## 三、加载图标

```js
// Promise 式（vjmap 扩展）
await map.loadImageEx("pin", "/icons/pin.png");
await map.loadImageEx("star", "data:image/svg+xml;base64,...");

// SDF（可改色）
map.loadImage("/icons/pin.png", (err, img) => {
  if (!err) map.addImage("pin-sdf", img, { sdf: true });
});
```

多图标批量：

```js
const icons = { pin: "/a.png", star: "/b.png", dot: "/c.png" };
await Promise.all(Object.entries(icons).map(
  ([name, url]) => map.loadImageEx(name, url)
));
```

## 四、数据驱动图标

```js
new vjmap.Symbol({
  data,
  iconImage: ["get", "icon"],                     // 按属性选图标
  iconSize: ["interpolate", ["linear"], ["zoom"], 10, 0.5, 16, 1.5],
  iconRotate: ["get", "bearing"]
});
```

## 五、旋转对齐 / 倾斜对齐

```js
// 图标随地图旋转（道路指示最常用）
iconRotationAlignment: "map",
iconPitchAlignment: "map"

// 图标始终正对屏幕（默认）
iconRotationAlignment: "viewport",
iconPitchAlignment: "viewport"
```

## 六、碰撞与优先级

- `iconAllowOverlap: false`：同层图标挤压时自动剔除；
- `iconAllowOverlap: true`：全部显示；
- `symbolSortKey: ["get", "priority"]`：小值优先显示。

## 七、图标 + 文字组合

```js
new vjmap.Symbol({
  data,
  iconImage: "pin",
  iconAnchor: "bottom",
  textField: ["get", "name"],
  textFont: ["Arial Unicode MS Regular"],   // 官方示例用的 fontstack
  textSize: 12,
  textOffset: [0, 1],
  textAnchor: "top",
  textColor: "#000",
  textHaloColor: "#fff",
  textHaloWidth: 1
}).addTo(map);
```

## 八、可拉伸图标 icon-text-fit

让气泡图标自动适应文字宽度：

```js
new vjmap.Symbol({
  data,
  iconImage: "bubble",
  iconTextFit: "both",
  iconTextFitPadding: [4, 8, 4, 8],   // top/right/bottom/left
  textField: ["get", "label"]
});
```

## 九、hover / click

```js
symbol.clickLayer(e => alert(e.features[0].properties.name));
symbol.hoverPopup(f => `<b>${f.properties.name}</b>`);
symbol.hoverFeatureState();
```

## 十、更新

```js
symbol.setData(newFc);
symbol.setIconImage(["get", "icon2"]);
symbol.setIconSize(1.5);
symbol.setProperty("iconOpacity", 0.5);
```

## 十一、常见坑

1. **图标不显示** → sprite 里没这个名字；或 `loadImage` 还没完成。
2. **iconColor 无效** → 必须 SDF 图标。
3. **挤压看不见** → `iconAllowOverlap: true` 硬显示。
4. **图标在旋转地图后歪** → 要用 `iconRotationAlignment: "viewport"` 始终正对屏幕。
5. **点击无响应** → hit-test 忽略了图标的边缘；扩 `iconPadding`。
