表达式与属性驱动样式
样式里的颜色、宽度、半径都可以写成表达式,让同一个图层按要素属性或缩放级别自动变化。 表达式是 JSON 数组,第一个元素是操作符,后面是参数,嵌套起来就是一棵求值树。
两种输入:属性与缩放
按要素属性变化叫 data-driven,按缩放变化叫 camera-driven。两者的写法不同, 能用在哪些属性上也不同:
| 输入 | 代表写法 | 常见属性 |
|---|---|---|
| 要素属性 | ['get', 'level']、['match', ...] | fill-color、circle-radius、text-field |
| 缩放级别 | ['interpolate', ['linear'], ['zoom'], ...] | line-width、circle-radius、fill-opacity |
| 几何类型 | ['geometry-type'] | 过滤条件、text-field |
| 要素状态 | ['feature-state', 'hover'] | 悬停、选中高亮 |
| 热力密度 | ['heatmap-density'] | 只在 heatmap 图层的 color 属性里可用 |
不能把两种输入混在同一个数值位置。例如用 interpolate 按缩放插值、
插值结果又写成 ['get', 'x'],规范层会拒绝,控制台报
Expected number but found ...,该属性回退到默认值。
确实需要两者同时参与时,拆成两个图层,用 minzoom / maxzoom 分工。
分类着色:match 与 case
match 适合枚举值到样式的映射,比一串嵌套 case 更好读,
也更快。最后一个参数是兜底值,漏写会得到默认样式。
map.addLayer({
id: 'landuse-fill',
type: 'fill',
source: 'base',
'source-layer': 'landuse',
paint: {
'fill-color': ['match', ['get', 'class'],
'forest', '#2f6b45',
'farmland','#c9c07a',
'urban', '#c8c2ba',
'#e8eef4' // 兜底
],
'fill-opacity': ['case',
['==', ['get', 'class'], 'urban'], 0.55,
0.35
]
}
});
case 是「条件, 值, 条件, 值, 兜底」的平铺结构。
条件必须是布尔表达式,例如 ['==', ...]、['has', ...]、
['>', ['get', 'flow'], 1000]。
连续插值:interpolate 与 step
interpolate 在断点之间做线性插值,适合半径、宽度、透明度;
step 是阶梯式跳变,适合「级别到了就换一档」的离散样式。
map.addLayer({
id: 'sites-circle',
type: 'circle',
source: 'sites',
paint: {
'circle-radius': ['interpolate', ['linear'], ['zoom'],
8, 3,
12, 6,
16, 14
],
'circle-color': ['step', ['get', 'level'],
'#9ecae1', // level 小于 2
2, '#4292c6',
4, '#08519c'
],
'circle-stroke-width': 1,
'circle-stroke-color': '#ffffff'
}
});
step 的读法是:先给兜底值,然后「阈值, 结果」成对出现。
上例中 level 为 2 时进入第二档,为 4 时进入第三档。
补默认值:coalesce
数据里的字段可能缺失或为 null。直接 ['get', 'name']
会让 text-field 变成空字符串,注记整片消失。
coalesce 返回第一个非空值。
map.addLayer({
id: 'sites-label',
type: 'symbol',
source: 'sites',
layout: {
'text-field': ['coalesce', ['get', 'name'], ['get', 'code'], '未命名'],
'text-size': ['interpolate', ['linear'], ['zoom'], 10, 11, 16, 15],
'text-offset': [0, 1.2],
'text-anchor': 'top'
},
paint: {
'text-color': '#12202c',
'text-halo-color': '#ffffff',
'text-halo-width': 1.5
}
});
feature-state:悬停与选中
feature-state 不写进样式,而是运行时通过
map.setFeatureState() 设置。用它做高亮,不必为每个要素重建图层。
var hoveredId = null;
map.on('mousemove', 'sites-circle', function (e) {
if (e.features.length === 0) return;
if (hoveredId !== null) {
map.setFeatureState({ source: 'sites', id: hoveredId }, { hover: false });
}
hoveredId = e.features[0].id;
map.setFeatureState({ source: 'sites', id: hoveredId }, { hover: true });
});
map.addLayer({
id: 'sites-hover',
type: 'circle',
source: 'sites',
paint: {
'circle-radius': ['case', ['boolean', ['feature-state', 'hover'], false], 10, 0],
'circle-color': '#c2410c'
}
});
e.features[0].id 只有在源里存在要素 id 时才有值。
GeoJSON 源加 generateId: true,或者用 promoteId
把某个属性提升为 id。
注意事项
颜色必须能被解析
['get', 'color'] 直接当颜色用,遇到
"red" 以外的非法值会触发校验错误。数值转颜色用
['to-color'] 包裹,或改用 match 映射。
layout 里很多属性不支持数据驱动
text-field、icon-image 可以按属性变化,
line-cap、text-anchor 这类只能用常量。
写错时报 data expressions not supported,图层不显示。
表达式里的类型要显式
['==', ['get', 'level'], 2] 在 level 是字符串 "2" 时结果为
false。用 ['to-number'] 或 ['to-string']
统一类型,比依赖隐式转换可靠。
经纬志(2026)。《表达式与属性驱动样式》。https://www.jingweizhi.com/maplibre/tutorial/expressions.html(访问日期 2026-11-03)。