> **本文回答**：用表达式驱动 Symbol —— icon / color / size / filter。

## 一、数据驱动 iconImage

按属性选图标：

```js
new vjmap.Symbol({
  data,
  iconImage: ["get", "icon"]           // properties.icon = "car" / "bus" / ...
});
```

动态拼名：

```js
iconImage: ["concat", "icon-", ["get", "type"]]    // icon-car
```

按 fallback 用 `coalesce`：

```js
iconImage: [
  "coalesce",
  ["image", ["concat", "icon-", ["get", "type"]]],   // 不能写 "icon-" + [...]（JS 字符串拼接，得到 "icon-get,type"）
  ["image", "icon-default"]
]
```

## 二、按 zoom 变大小

```js
iconSize: [
  "interpolate", ["linear"], ["zoom"],
  10, 0.5,
  14, 1.0,
  18, 2.0
]
```

## 三、按属性变色（SDF）

```js
iconImage: "pin-sdf",
iconColor: [
  "match", ["get", "status"],
  "ok", "#0f0",
  "warn", "#ff0",
  "err", "#f00",
  "#888"
]
```

## 四、hover feature-state 变色

```js
const symbol = new vjmap.Symbol({
  data,
  iconImage: "pin-sdf",
  iconColor: [
    "case",
    ["boolean", ["feature-state", "hover"], false], "#ff0",
    "#0af"
  ],
  isHoverFeatureState: true     // vjmap 扩展，自动 setFeatureState
});
symbol.addTo(map);
```

要求要素有非 0 的 id。封装类不透传 `generateId`（且 generateId 从 0 编号，而 `hoverFeatureState` 忽略 id 0）：FeatureCollection 须自带非 0 `id`；或传 `[{points, properties}]` 形式（自动 `id = 序号 + 1`）；或先自建源（`promoteId`）再传 `sourceId`：

```js
map.addGeoJSONSource("src", data, { promoteId: "code" });   // properties.code 非 0
new vjmap.Symbol({ sourceId: "src", iconImage: "pin-sdf", isHoverFeatureState: true }).addTo(map);
```

## 五、按属性组合文字

```js
textField: [
  "format",
  ["get", "name"], { "font-scale": 1.2, "text-color": "#000" },
  " — ", {},
  ["to-string", ["get", "count"]], { "font-scale": 0.9, "text-color": "#888" }
]
```

## 六、filter 过滤

```js
new vjmap.Symbol({
  data,
  iconImage: "pin",
  filter: ["all",
    [">=", ["to-number", ["get", "level"]], 3],
    ["==", ["get", "country"], "CN"]
  ]
});
```

运行时：

```js
symbol.setFilter(["==", ["get", "type"], "POI"]);
```

## 七、symbolSortKey 控制绘制顺序

```js
new vjmap.Symbol({
  data,
  symbolSortKey: ["-", 0, ["get", "priority"]],   // key 越小越先放置：priority 大的碰撞时优先保留；允许重叠时 key 大的画在上面（此时 priority 大的反而在下）
  textField: ["get", "name"]
});
```

## 八、textVariableAnchor 智能避让

```js
new vjmap.Symbol({
  data,
  textField: ["get", "name"],
  textVariableAnchor: ["top", "bottom", "left", "right"],
  textJustify: "auto",
  textRadialOffset: 0.8
});
```

渲染引擎会根据碰撞自动换方向。

## 九、动态改属性

```js
symbol.setIconColor(["get", "newColor"]);
symbol.setTextSize(16);
symbol.setIconSize([
  "interpolate", ["linear"], ["zoom"], 10, 0.3, 16, 1.5
]);
```

## 十、常见坑

1. **iconImage 不能写 `["sprite", ...]`**：用 `["image", name]` 作 coalesce 备选；或直接字符串 / `["get"]`。
2. **字符串与数字比较 filter 失败** → `["to-number", ["get", "x"]]`。
3. **feature-state 无 id 不生效** → `generateId: true`。
4. **表达式嵌套太深卡顿** → 预处理属性；layout 属性尽量少表达式。
