> **本文回答**：vjmap 样式表达式（get / case / match / interpolate / step / feature-state / zoom）实战。

## 一、表达式是什么

任何 `paint` / `layout` / `filter` 的值都可以写成表达式（一个 JSON 数组，第一个元素是操作符）。

```js
// "line-width": 2
// 改为：按缩放级别变宽
"line-width": ["interpolate", ["linear"], ["zoom"], 10, 1, 16, 5]
```

## 二、常用操作

| 类别 | 典型 |
|---|---|
| Lookup | `["get", "name"]`, `["has", "color"]`, `["at", 0, arr]`, `["length", arr]`, `["properties"]`, `["id"]`, `["geometry-type"]` |
| Feature-state | `["feature-state", "hover"]` |
| Decision | `["case", cond1, val1, cond2, val2, defaultVal]`, `["match", input, val1, out1, val2, out2, default]` |
| Ramps | `["interpolate", ["linear"], ["zoom"], 0, a, 10, b]`, `["step", ["zoom"], a, 5, b, 10, c]` |
| Comparison | `["==", ..]`, `["!=", ..]`, `[">", ..]`, `["<=", ..]`, `["in", ..]` |
| Boolean | `["all", a, b]`, `["any", a, b]`, `["!", cond]` |
| Arithmetic | `["+", a, b]`, `["-", a, b]`, `["*", a, b]`, `["/", a, b]`, `["%", a, b]`, `["^", a, b]`, `["abs", x]`, `["min", ...]`, `["max", ...]` |
| Type | `["to-string", x]`, `["to-number", x]`, `["to-boolean", x]`, `["to-color", x]`, `["typeof", x]` |
| String | `["concat", a, b]`, `["upcase", s]`, `["downcase", s]` |
| Color | `["rgb", r, g, b]`, `["rgba", r, g, b, a]`, `["to-color", "#f00"]` |
| Let/Var | `["let", "x", 1, ["+", ["var", "x"], 2]]` |

## 三、典型用法

### 按属性变色

```js
"fill-color": ["get", "color"]
```

### 按属性分类

```js
"fill-color": [
  "match", ["get", "type"],
  "road", "#0af",
  "building", "#f80",
  "park", "#8c0",
  "#aaa"                 // default
]
```

### 按 zoom 插值

```js
"circle-radius": [
  "interpolate", ["linear"], ["zoom"],
  8,  2,
  14, 6,
  18, 12
]
```

### 按值插值（热力等级）

```js
"fill-color": [
  "interpolate", ["linear"], ["get", "heat"],
  0,   "#00f",
  50,  "#0f0",
  100, "#f00"
]
```

### step 分段

```js
"line-color": [
  "step", ["zoom"],
  "#aaa",     // zoom < 10
  10, "#888", // 10 <= zoom < 14
  14, "#333"  // zoom >= 14
]
```

### 条件（hover 状态）

```js
"fill-color": [
  "case",
  ["boolean", ["feature-state", "hover"], false], "#f00",
  ["get", "color"]
]
```

### 组合过滤 filter

```js
filter: ["all",
  ["==", ["geometry-type"], "Polygon"],
  [">=", ["to-number", ["get", "h"]], 50]
]
```

## 四、feature-state 用法

feature-state 不是数据的属性，而是运行时附加的状态。

```js
// feature 的 id 必须是整数或可以转整数的字符串
map.setFeatureState({ source: "states", id: 54 }, { hover: true });

// 样式中
"fill-opacity": [
  "case",
  ["boolean", ["feature-state", "hover"], false], 1,
  0.5
]

// 移除
map.removeFeatureState({ source: "states", id: 54 });
```

feature-state **只能用在 paint**（支持数据驱动）的属性里。

## 五、zoom 与 properties 的差别

- `["zoom"]`：只能在 `interpolate` / `step` 的输入里；不能直接参与其他算术。违规写法（如 `["+", ["zoom"], 1]` 直接给 `line-width`）**不抛异常**，而是走 `map.on("error")` 事件：`layers.*.paint.*: "zoom" expression may only be used as input to...`——静默失败，调试时记得挂 error 监听。
- `["get", "name"]`：任何地方都能用。
- `["heatmap-density"]`：只能在 `heatmap-color` 属性里。

## 六、样式规范约束

- **paint** 随小数 zoom 插值（`*-pattern`、`line-dasharray` 例外，只按整数 zoom 求值）。
- **layout** 多数按瓦片的整数 zoom 求值；但 `text-size` / `icon-size` 的 zoom 表达式会在小数 zoom 上插值（4.2→4.6 字号会连续变化）。

## 七、后端表达式（CAD 栅格样式）

CAD 栅格图渲染在后端，不是样式表达式，而是**服务端表达式语法**：

```
gOutColorRed := 255;   // 输出红色分量
gOutColorGreen := 0;
gOutColorBlue := 0;
gOutColorAlpha := 255;   // 透明度分量 0~255（不是 0~1，写 1.0 几乎全透明）
```

变量前缀：`gIn*` 是输入（层名、颜色、线型…），`gOut*` 是输出。详见 `svrStyleVar` / `expr.md`。

前端通过 `map.updateStyle(svc, { expression: "..." })` 写入（内部 `svc.cmdUpdateStyle` + 刷新瓦片；单独调 `svc.cmdUpdateStyle` 地图不会刷新，还得 `map.changeSourceTiles(svc.rasterTileUrl())`）。

## 八、调试表达式

```js
// 控制台会报错；开 Chrome DevTools
// 常用 console.log(JSON.stringify(style, null, 2)) 查看当前生效的样式
```

## 九、常见坑

1. **类型错误**：`["==", ["get", "n"], 1]` 里 n 如果是字符串会不等；显式 `to-number`。
2. **filter 中不支持 interpolate**：只 paint/layout 支持 zoom 插值，filter 不行。
3. **feature-state 在 layout 不生效**：只能用在 paint。
4. **id 不是整数** → feature-state 不生效；`generateId: true` 让 GeoJSON source 自动生成 id。
