唯杰地图 logo唯杰地图
VJMAP
EN

data-driven

vjmap/references/map-style/data-driven.md 5.1 KB216 行
原文

本文回答:数据驱动样式 —— 根据 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);

十三、常见坑

  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。