> **本文回答**：数据驱动样式 —— 根据 feature 属性改变颜色、大小、图标、可见性等。

## 一、什么是数据驱动样式

把 `paint` / `layout` 的值写成表达式，使其随 feature 属性变化，即 "data-driven styling"（数据驱动样式）。

## 二、最简单的数据驱动

GeoJSON：

```js
{
  type: "FeatureCollection",
  features: [
    { type: "Feature", properties: { color: "#f00", size: 10 }, geometry: { type: "Point", coordinates: [0,0] } },
    { type: "Feature", properties: { color: "#0f0", size: 20 }, geometry: { type: "Point", coordinates: [1,1] } }
  ]
}
```

Layer：

```js
{
  id: "pts", type: "circle", source: "src",
  paint: {
    "circle-color":  ["get", "color"],
    "circle-radius": ["get", "size"]
  }
}
```

## 三、离散分类（match）

```js
"fill-color": [
  "match",
  ["get", "layerName"],
  "WATER", "#3fa9ff",
  "ROAD",  "#333",
  "BLDG",  "#aa8",
  "GRASS", "#8c0",
  "#ddd"                   // default
]
```

## 四、连续变色（interpolate）

按 GDP 染色：

```js
"fill-color": [
  "interpolate", ["linear"], ["get", "gdp"],
  0,      "#eef",
  5000,   "#88c",
  50000,  "#246",
  200000, "#000"
]
```

## 五、按 zoom 变大小

```js
"circle-radius": [
  "interpolate", ["linear"], ["zoom"],
  10, 2,
  16, 10
]
```

## 六、按属性和 zoom 组合（嵌套）

```js
"circle-radius": [
  "interpolate", ["linear"], ["zoom"],
  10, ["*", 1, ["get", "size"]],
  16, ["*", 3, ["get", "size"]]
]
```

## 七、按属性动态图标

```js
"icon-image": [
  "concat", "icon-", ["get", "type"]
]
// feature.properties.type = "car" → "icon-car"
```

## 八、过滤显示（filter）

```js
filter: [
  "all",
  [">=", ["to-number", ["get", "h"]], 50],
  ["==", ["get", "country"], "CN"]
]
```

## 九、Cluster（聚合）

```js
map.addSource("earthquakes", {
  type: "geojson",
  data: "https://xxx.geojson",
  cluster: true,
  clusterMaxZoom: 14,
  clusterRadius: 50,
  clusterProperties: {
    sum: ["+", ["get", "mag"]]
  }
});

map.addLayer({
  id: "clusters", type: "circle", source: "earthquakes",
  filter: ["has", "point_count"],
  paint: {
    "circle-color": [
      "step", ["get", "point_count"],
      "#51bbd6", 100,
      "#f1f075", 750,
      "#f28cb1"
    ],
    "circle-radius": [
      "step", ["get", "point_count"],
      20, 100,
      30, 750,
      40
    ]
  }
});

map.addLayer({
  id: "cluster-count", type: "symbol", source: "earthquakes",
  filter: ["has", "point_count"],
  layout: {
    "text-field": ["get", "point_count_abbreviated"],
    "text-size": 12
  }
});

map.addLayer({
  id: "unclustered", type: "circle", source: "earthquakes",
  filter: ["!", ["has", "point_count"]],
  paint: {
    "circle-color": "#11b4da",
    "circle-radius": 4
  }
});
```

## 十、动态 feature-state（hover / click）

```js
map.addLayer({
  id: "bldg", type: "fill", source: "src",
  paint: {
    "fill-color": ["case",
      ["boolean", ["feature-state", "hover"], false], "#ff0",
      ["get", "color"]
    ]
  }
});

let hoveredId = null;
map.on("mousemove", "bldg", (e) => {
  if (hoveredId !== null) {
    map.setFeatureState({ source: "src", id: hoveredId }, { hover: false });
  }
  hoveredId = e.features[0].id;
  map.setFeatureState({ source: "src", id: hoveredId }, { hover: true });
});
map.on("mouseleave", "bldg", () => {
  if (hoveredId !== null) {
    map.setFeatureState({ source: "src", id: hoveredId }, { hover: false });
    hoveredId = null;
  }
});
```

`feature.id` 必须有值（整数 / 可转整数字符串）。GeoJSON 没 id 时：

```js
map.addSource("src", { type: "geojson", data, generateId: true });
```

## 十一、性能建议

- **paint 表达式** 不要嵌太深（每帧 evaluate）。
- **layout 表达式**：只在整数 zoom 级变化时 evaluate，OK。
- **filter**：在大数据量时重要；尽量用索引属性（id 最好）。

## 十二、vjmap 封装类的数据驱动

`Polygon / Polyline / CircleFill` 等都接受 paint 中的表达式：

```js
new vjmap.Polygon({
  data: geojson,
  fillColor: ["case",
    ["boolean", ["feature-state", "hover"], false], "#f00",
    ["get", "color"]
  ],
  fillOpacity: 0.8,
  isHoverFeatureState: true,   // vjmap 扩展：自动处理 hover feature-state
  isHoverPointer: true         // 自动改 cursor
}).addTo(map);
```

## 十三、常见坑

1. **feature.id 没有或为 0** → feature-state / `isHoverFeatureState` 不生效（`hoverFeatureState` 忽略 id 0）。封装类不透传 `generateId`：FeatureCollection 自带非 0 `id`，或传 `[{points, properties}]`（自动 id = 序号 + 1）。
2. **表达式语法错** → 走 `map.on("error")`，报错形如 `layers.<id>.paint.<prop>: …`；`addLayer` 只拒绝这一个图层，`setStyle` 则整个更新被拒。
3. **color 是 css 颜色字符串**：比如 `"red"` / `"#ff0000"` / `"rgba(255,0,0,0.5)"`。
4. **插值曲线的 "exponential"**：常用于对非线性（如密度、面积）做 visual mapping。
