唯杰地图 logo唯杰地图
VJMAP
EN

extrusion-3d

vjmap/references/map-style/extrusion-3d.md 4.1 KB123 行
原文

本文回答:fill-extrusion 建筑物 3D 拉伸与动画。

一、基础

map.addSource("bldg", {
  type: "geojson",
  data: featureCollectionWithHeight
});

map.addLayer({
  id: "bldg-3d",
  type: "fill-extrusion",
  source: "bldg",
  paint: {
    "fill-extrusion-color":  ["get", "color"],
    "fill-extrusion-height": ["get", "height"],
    "fill-extrusion-base":   ["get", "base"],     // 起始高度
    "fill-extrusion-opacity": 0.9
  }
});

需要 map.getPitch() > 0(或构造时 pitch: 60)才能看到 3D 效果。

二、vjmap 封装

new vjmap.FillExtrusion({
  data: { type: "FeatureCollection", features: [...] },
  fillExtrusionColor: ["get", "color"],
  fillExtrusionHeight: ["get", "height"],
  fillExtrusionBase: 0,
  fillExtrusionOpacity: 0.85
}).addTo(map);

三、Draw 内的 extrusionHeight

对 Draw 里的多边形实体,设置 extrusionHeight 自动变 3D:

draw.setFeatureProperty(featureId, "extrusionHeight", 100);  // 引擎墨卡托"米";按 CAD 长度给高度用 map.geoLengthToHeight(cadLen)
// 取消
draw.setFeatureProperty(featureId, "extrusionHeight", undefined);

四、zoom / pitch 联动

当 zoom 小的时候隐藏 3D(性能):

map.setLayerZoomRange("bldg-3d", 14, 22);

或用表达式:

"fill-extrusion-opacity": [
  "interpolate", ["linear"], ["zoom"],
  13, 0,
  14, 1
]

五、动画 —— 高度 grow

let t = 0;
function animate() {
  t += 0.02;
  map.setPaintProperty("bldg-3d", "fill-extrusion-height", [
    "*", ["get", "height"], Math.min(t, 1)
  ]);
  if (t < 1) requestAnimationFrame(animate);
}
animate();

六、光照 light

map.setLight({
  anchor: "viewport",
  color: "#fff8e0",
  intensity: 0.6,
  position: [1.15, 210, 30]       // radial, azimuth, polar
});
  • anchor: "map":光源随地图旋转。
  • anchor: "viewport":光源固定到屏幕。

七、阴影效果(高度差分层)

vjmap 的 vjmapext.fx 有 shadow / glow 等 GPU 效果(属于 mapext,不在 vjmap core)。详见 mapext 文档。

八、点击拾取

map.on("click", "bldg-3d", (e) => {
  const h = e.features[0].properties.height;
  alert(`高度:${h} 米`);
});

map.on("mouseenter", "bldg-3d", () => map.getCanvas().style.cursor = "pointer");
map.on("mouseleave", "bldg-3d", () => map.getCanvas().style.cursor = "");

九、与 CAD 图结合

CAD 面状图层(如 POLYGON 实体)可以通过 svrStyleVar 或 svc.conditionQueryFeature(矢量瓦片模式,见 backend/query-sql)取得高度属性,再做 fill-extrusion。

十、常见坑

  1. pitch=0 看不到 3D:必须设 pitch: 60 之类。
  2. 顶层 fill 遮住底层:多个 fill-extrusion 叠加会有深度冲突,统一放同一图层。
  3. height 单位是引擎的墨卡托"米",不是 CAD 单位:GeoProjection 把整张 CAD 图映射到整个墨卡托世界,高度值与 CAD 长度的比例取决于图的范围(高度 = CAD 长度 × 赤道周长 / 图宽),简单 /1000 换算不对。要"高度 = CAD 里的 L"用 map.geoLengthToHeight(L);要"高度 = 当前 zoom 下 N 像素"用 map.pixelToHeight(N, map.getZoom())。
  4. 移动时卡:fill-extrusion-pattern 用贴图很慢,尽量用纯色。
  5. 透明度叠加怪异:fill-extrusion-opacity < 1 时 depth sorting 不完美。
  6. 按 type 找层会撞上 Draw 内置层:Draw 控件自带 gl-draw-fill-extrusion-inactive/-static 层(fill-extrusion-opacity 恒 0.9)。用 getStyle().layers.find(l => l.type === "fill-extrusion") 找到的多半是它而不是你的层——vjmap.FillExtrusion 生成的层名是 custom_fillExtrusion_layer_<rand>、源名 custom_fillExtrusion_<rand>,按 layerId/sourceId 匹配。
  7. FillExtrusion 的 options 键是 camelCase(fillExtrusionOpacity),内部 kebab 化进 paint;getter/setter 委托 map.getPaintProperty / map.setPaintProperty(回读值建议 Number() 归一后再严格比较)。