data-driven
本文回答:数据驱动样式 —— 根据 feature 属性改变颜色、大小、图标、可见性等。
一、什么是数据驱动样式
把 paint / layout 的值写成表达式,使其随 feature 属性变化,即 "data-driven styling"(数据驱动样式)。
二、最简单的数据驱动
GeoJSON:
{
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:
{
id: "pts", type: "circle", source: "src",
paint: {
"circle-color": ["get", "color"],
"circle-radius": ["get", "size"]
}
}
三、离散分类(match)
"fill-color": [
"match",
["get", "layerName"],
"WATER", "#3fa9ff",
"ROAD", "#333",
"BLDG", "#aa8",
"GRASS", "#8c0",
"#ddd" // default
]
四、连续变色(interpolate)
按 GDP 染色:
"fill-color": [
"interpolate", ["linear"], ["get", "gdp"],
0, "#eef",
5000, "#88c",
50000, "#246",
200000, "#000"
]
五、按 zoom 变大小
"circle-radius": [
"interpolate", ["linear"], ["zoom"],
10, 2,
16, 10
]
六、按属性和 zoom 组合(嵌套)
"circle-radius": [
"interpolate", ["linear"], ["zoom"],
10, ["*", 1, ["get", "size"]],
16, ["*", 3, ["get", "size"]]
]
七、按属性动态图标
"icon-image": [
"concat", "icon-", ["get", "type"]
]
// feature.properties.type = "car" → "icon-car"
八、过滤显示(filter)
filter: [
"all",
[">=", ["to-number", ["get", "h"]], 50],
["==", ["get", "country"], "CN"]
]
九、Cluster(聚合)
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)
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 时:
map.addSource("src", { type: "geojson", data, generateId: true });
十一、性能建议
- paint 表达式 不要嵌太深(每帧 evaluate)。
- layout 表达式:只在整数 zoom 级变化时 evaluate,OK。
- filter:在大数据量时重要;尽量用索引属性(id 最好)。
十二、vjmap 封装类的数据驱动
Polygon / Polyline / CircleFill 等都接受 paint 中的表达式:
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);
十三、常见坑
- feature.id 没有或为 0 → feature-state /
isHoverFeatureState不生效(hoverFeatureState忽略 id 0)。封装类不透传generateId:FeatureCollection 自带非 0id,或传[{points, properties}](自动 id = 序号 + 1)。 - 表达式语法错 → 走
map.on("error"),报错形如layers.<id>.paint.<prop>: …;addLayer只拒绝这一个图层,setStyle则整个更新被拒。 - color 是 css 颜色字符串:比如
"red"/"#ff0000"/"rgba(255,0,0,0.5)"。 - 插值曲线的 "exponential":常用于对非线性(如密度、面积)做 visual mapping。