You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

deck.gl中IconLayer在3D地图导航时无法锚定指定经纬度

问题描述

我使用deck.gl的Tile3DLayer渲染3D地图,同时叠加IconLayer实现图标覆盖,期望图标在地图导航时始终锚定到特定建筑。但平移或缩放地图时,图标并未随3D图层同步移动,似乎未考虑Tile3DLayer的3D变换,无法保持在指定经纬度位置。

已做配置:给IconLayer设置了billboard: true确保图标始终朝向相机,图标放置在[INITIAL_VIEW_STATE.longitude, INITIAL_VIEW_STATE.latitude]位置。

代码示例

import React, { useState } from 'react';
import { scaleLinear } from 'd3-scale';
import DeckGL from '@deck.gl/react';
import { GeoJsonLayer, IconLayer } from '@deck.gl/layers';
import { Tile3DLayer } from '@deck.gl/geo-layers';
import { DataFilterExtension, _TerrainExtension as TerrainExtension } from '@deck.gl/extensions';

const ICON_MAPPING = {
    marker: { x: 0, y: 0, width: 128, height: 128, mask: false }
};

const ICON_ATLAS = 'https://raw.githubusercontent.com/visgl/deck.gl-data/master/website/icon-atlas.png';

const GOOGLE_MAPS_API_KEY = 'AIzaSyBYchwzbcKzBDjxrSAEcxOCcDWLQKD5FAx4s'; // eslint-disable-line
const TILESET_URL = 'https://tile.googleapis.com/v1/3dtiles/root.json';
export const COLORS = [
    [254, 235, 226],
    [251, 180, 185],
    [247, 104, 161],
    [197, 27, 138],
    [122, 1, 119]
];
const colorScale = scaleLinear().clamp(true).domain([0, 50, 100, 200, 300]).range(COLORS);
const INITIAL_VIEW_STATE = {
    latitude: 48.8681586,
    longitude: 2.30278,
    zoom: 16,
    minZoom: 14,
    maxZoom: 16.5,
    bearing: 90,
    pitch: 60
};
const BUILDING_DATA =
    'https://raw.githubusercontent.com/visgl/deck.gl-data/master/examples/google-3d-tiles/buildings.geojson';
export default function App({ data = TILESET_URL, distance = 0, opacity = 0.2 }) {
    const [credits, setCredits] = useState('');
    const iconLayer = new IconLayer({
        id: 'icon-layer',
        data: [{
            coordinates: [INITIAL_VIEW_STATE.longitude, INITIAL_VIEW_STATE.latitude],
            icon: 'marker'
        }],
        pickable: true,
        iconAtlas: ICON_ATLAS,
        iconMapping: ICON_MAPPING,
        getIcon: d => d.icon,
        getSize: 60,
        sizeScale: 0.5,
        getPosition: d => d.coordinates,
        getColor: [255, 0, 0, 255],
        getAngle: 0,
        billboard: true,
        zIndex: 10,
        parameters: {
            depthTest: false
        }
    })
    const layers = [
        new Tile3DLayer({
            id: 'google-3d-tiles',
            data: TILESET_URL,
            onTilesetLoad: tileset3d => {
                tileset3d.options.onTraversalComplete = selectedTiles => {
                    const uniqueCredits = new Set();
                    selectedTiles.forEach(tile => {
                        const { copyright } = tile.content.gltf.asset;
                        copyright.split(';').forEach(uniqueCredits.add, uniqueCredits);
                    });
                    setCredits([...uniqueCredits].join('; '));
                    return selectedTiles;
                };
            },
            loadOptions: {
                fetch: { headers: { 'X-GOOG-API-KEY': GOOGLE_MAPS_API_KEY } }
            },
            operation: 'terrain+draw'
        }),
        new GeoJsonLayer({
            id: 'buildings',
            data: BUILDING_DATA,
            extensions: [new DataFilterExtension({ filterSize: 1 }), new TerrainExtension()],
            stroked: false,
            filled: true,
            getFillColor: ({ properties }) => colorScale(properties.distance_to_nearest_tree),
            opacity,
            getFilterValue: f => f.properties.distance_to_nearest_tree,
            filterRange: [distance, 500]
        }),
        iconLayer
    ];
    return (
        <div style={{ position: 'relative', width: '300px', height: '300px', margin: 'auto' }}>
            <DeckGL
                style={{ backgroundColor: '#061714' }}
                initialViewState={INITIAL_VIEW_STATE}
                controller={{ touchRotate: true, inertia: 250 }}
                layers={layers}
            />
        </div>
    );
}
解决方案

问题根源在于IconLayer默认将图标放置在海拔0的平面,未适配Tile3DLayer中3D建筑的高度与空间变换,导致视角变化时出现偏移。以下是具体修复步骤:

  • 添加TerrainExtension适配3D地形/建筑高度
    给IconLayer引入地形扩展,让图标自动贴合3D表面的海拔高度,修改后的IconLayer配置如下:

    const iconLayer = new IconLayer({
      id: 'icon-layer',
      data: [{
          coordinates: [INITIAL_VIEW_STATE.longitude, INITIAL_VIEW_STATE.latitude],
          icon: 'marker'
      }],
      pickable: true,
      iconAtlas: ICON_ATLAS,
      iconMapping: ICON_MAPPING,
      getIcon: d => d.icon,
      getSize: 60,
      sizeScale: 0.5,
      getPosition: d => d.coordinates,
      getColor: [255, 0, 0, 255],
      getAngle: 0,
      billboard: true,
      zIndex: 10,
      extensions: [new TerrainExtension()], // 加入地形扩展,自动适配3D高度
      getElevation: d => 0, // 若需锚定建筑顶部,可替换为对应建筑的高度值
      parameters: {
          depthTest: true // 打开深度测试,让图标在3D空间中正确排序
      }
    });
    
  • 确保坐标精准匹配
    验证图标的经纬度是否对应目标建筑的准确位置,可从BUILDING_DATA的GeoJSON数据中提取目标建筑的坐标,替换当前硬编码的初始视角坐标,避免因坐标偏差导致的错位。

  • 动态获取建筑高度(可选)
    若需要精准锚定到建筑顶部,可通过Tile3DLayer的onTilesetLoad回调遍历3D瓦片数据,提取目标建筑的高度信息,将其赋值给getElevation属性。

内容的提问来源于stack exchange,提问作者pam

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 00:05:55