> **本文回答**：circle / symbol / heatmap 图层选型 —— 三种"点图层"对比。

## 一、对比表

| 图层 | 形状 | 驱动 | 图标支持 | 文字支持 | 碰撞避让 | 典型场景 |
|---|---|---|---|---|---|---|
| `circle` | 圆 | WebGL | ✗ | ✗ | ✗（always show） | 大数据点云 / 散点图 |
| `symbol` | 图标 + 文字 | WebGL | ✓（sprite / addImage） | ✓ | ✓（碰撞自动剔除） | POI / 图表标注 |
| `heatmap` | 密度色斑 | WebGL | ✗ | ✗ | ✗ | 热力 |

## 二、Circle 层

```js
map.addLayer({
  id: "pts", type: "circle", source: "src",
  paint: {
    "circle-radius": 5,
    "circle-color": "#f00",
    "circle-stroke-width": 1,
    "circle-stroke-color": "#fff"
  }
});
```

优点：最轻；几百万点不卡。

## 三、Symbol 层

```js
map.addLayer({
  id: "poi", type: "symbol", source: "src",
  layout: {
    "icon-image": "pin",
    "icon-size": 0.6,
    "text-field": ["get", "name"],
    "text-size": 12,
    "text-offset": [0, 1],
    "text-anchor": "top"
  },
  paint: {
    "text-color": "#000",
    "text-halo-color": "#fff",
    "text-halo-width": 1
  }
});
```

优点：支持碰撞避让、多字体、图标变色（SDF）、旋转、随 zoom 插值。

## 四、Heatmap 层

```js
map.addLayer({
  id: "heat", type: "heatmap", source: "src",
  paint: {
    "heatmap-weight": ["get", "weight"],
    "heatmap-intensity": 1,
    "heatmap-radius": 30,
    "heatmap-color": [
      "interpolate", ["linear"], ["heatmap-density"],
      0, "rgba(0,0,255,0)",
      0.5, "rgba(0,255,0,0.5)",
      1, "rgba(255,0,0,1)"
    ]
  }
});
```

## 五、vjmap 的封装类

| 原生 layer type | vjmap 类 |
|---|---|
| `circle` | `vjmap.Circle` |
| `symbol` | `vjmap.Symbol` |
| `heatmap` | `vjmap.Heatmap` |
| `line` | `vjmap.Polyline` |
| `fill` | `vjmap.Polygon` |
| `fill-extrusion` | `vjmap.FillExtrusion` |

好处：

- 不区分 paint/layout，一个配置写完
- 内建 `clickLayer` / `hoverLayer` / `hoverPopup` / `hoverFeatureState`
- 接受 CAD 坐标（需 `map.toLngLat`）

## 六、同一份 GeoJSON 三种渲染

```js
map.addSource("src", { type: "geojson", data: features, generateId: true });

// 小 zoom：heatmap
map.addLayer({
  id: "heat", type: "heatmap", source: "src",
  maxzoom: 12,
  paint: { "heatmap-radius": 30 }
});

// 中 zoom：cluster circle
map.addSource("src-cluster", {
  type: "geojson", data: features, cluster: true, clusterRadius: 50
});
map.addLayer({
  id: "clusters", type: "circle", source: "src-cluster",
  filter: ["has", "point_count"],
  paint: { "circle-color": "#0af", "circle-radius": 20 }
});

// 大 zoom：symbol
map.addLayer({
  id: "poi", type: "symbol", source: "src",
  minzoom: 14,
  layout: { "icon-image": "pin", "text-field": ["get", "name"], "text-size": 10 }
});
```

## 七、常见坑

1. **circle-radius 单位是像素**：与 zoom 无关；要随 zoom 变大用 `interpolate`。
2. **symbol 碰撞导致文字消失**：设 `text-allow-overlap: true` 硬显示。
3. **heatmap 看起来平**：heatmap-weight 太低；提权 `"heatmap-weight": ["*", ["get", "weight"], 2]`。
4. **同层大量 feature 导致卡顿**：开 cluster，或按 zoom 分层。
