下面给你一份基于 Three.js + GeoJSON 实现三维地图显示的完整示例思路和示范代码,帮你快速入门三维地图可视化开发。


Three.js + GeoJSON 实现三维地图三维显示

核心思路

  1. 加载 GeoJSON
    GeoJSON 是一种存储地理数据的 JSON 格式,包含区域的经纬度坐标。
  2. 经纬度坐标投影转换
    地理坐标是经纬度,Three.js 需要用笛卡尔坐标系,通常用墨卡托投影或简单转换将经纬度转为 X, Y 平面坐标。
  3. 创建二维地图轮廓 (Shape)
    使用 Three.js 的 THREE.Shape 将每个区域的边界坐标连接成二维路径。
  4. 拉伸生成三维几何体
    通过 THREE.ExtrudeGeometry 给二维区域形状加高度,形成立体地图块。
  5. 添加材质和灯光
    让三维地图更有视觉效果。
  6. 添加交互控制
    利用 OrbitControls 实现鼠标旋转缩放。

示例代码

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Three.js + GeoJSON 三维地图</title>
<style>
  body { margin: 0; overflow: hidden; }
  canvas { display: block; }
</style>
</head>
<body>
<script src="https://cdn.jsdelivr.net/npm/three@0.157.0/build/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.157.0/examples/js/controls/OrbitControls.js"></script>

<script>
let scene, camera, renderer, controls;

init();
loadGeoJSON();

function init() {
  scene = new THREE.Scene();
  scene.background = new THREE.Color(0xf0f0f0);

  camera = new THREE.PerspectiveCamera(45, window.innerWidth/window.innerHeight, 1, 1000);
  camera.position.set(0, -200, 200);

  renderer = new THREE.WebGLRenderer({antialias:true});
  renderer.setSize(window.innerWidth, window.innerHeight);
  document.body.appendChild(renderer.domElement);

  controls = new THREE.OrbitControls(camera, renderer.domElement);
  controls.minDistance = 50;
  controls.maxDistance = 500;

  // 光源
  const ambientLight = new THREE.AmbientLight(0x888888);
  scene.add(ambientLight);

  const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
  directionalLight.position.set(100, 100, 200);
  scene.add(directionalLight);

  window.addEventListener('resize', onWindowResize, false);

  animate();
}

function onWindowResize() {
  camera.aspect = window.innerWidth/window.innerHeight;
  camera.updateProjectionMatrix();
  renderer.setSize(window.innerWidth, window.innerHeight);
}

function animate() {
  requestAnimationFrame(animate);
  controls.update();
  renderer.render(scene, camera);
}

// 经纬度转墨卡托平面坐标,适当缩放
function lngLatToXY(lng, lat) {
  const x = lng * 20037508.34 / 180;
  let y = Math.log(Math.tan((90 + lat) * Math.PI / 360)) / (Math.PI / 180);
  y = y * 20037508.34 / 180;
  return [x / 100000, y / 100000]; // 缩放比例调整
}

function loadGeoJSON() {
  fetch('china_provinces.geojson')  // 请替换成你的GeoJSON文件路径
    .then(res => res.json())
    .then(data => {
      data.features.forEach(feature => {
        const coords = feature.geometry.coordinates;
        const geomType = feature.geometry.type;

        if (geomType === 'MultiPolygon') {
          coords.forEach(polygon => {
            polygon.forEach(ring => {
              addShape(ring);
            });
          });
        } else if (geomType === 'Polygon') {
          coords.forEach(ring => {
            addShape(ring);
          });
        }
      });
    })
    .catch(err => {
      console.error('加载 GeoJSON 失败:', err);
    });
}

function addShape(coordArray) {
  const shape = new THREE.Shape();
  coordArray.forEach((lnglat, i) => {
    const [x, y] = lngLatToXY(lnglat[0], lnglat[1]);
    if (i === 0) shape.moveTo(x, y);
    else shape.lineTo(x, y);
  });

  // 拉伸高度,模拟三维效果
  const extrudeSettings = { depth: 5, bevelEnabled: false };
  const geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings);
  const material = new THREE.MeshPhongMaterial({
    color: 0x66ccff,
    transparent: true,
    opacity: 0.7,
    side: THREE.DoubleSide,
  });
  const mesh = new THREE.Mesh(geometry, material);
  mesh.castShadow = true;
  mesh.receiveShadow = true;

  scene.add(mesh);
}

</script>
</body>
</html>

说明

  • 该示例使用了简单墨卡托投影进行坐标转换,适合展示区域范围地图。
  • china_provinces.geojson 是中国省级边界的 GeoJSON 文件,可以在网上公开数据源下载。
  • 地图高度固定为5单位,也可以根据数据(如人口、GDP)动态调整,实现数据可视化。
  • 使用了 OrbitControls 实现旋转、缩放操作,方便观察地图。
  • 材质设置为半透明蓝色,可以根据需要调整颜色、光照效果。

你可以扩展的方向

  • 加载城市、道路、河流等不同类型的 GeoJSON 数据叠加显示
  • 结合热力图、飞线动画、点聚合等效果
  • 动态更新地图区域高度,实现数据变化实时反映
  • 加载真实建筑模型(GLTF)与地理坐标对齐
  • 性能优化:合并几何体、减少多边形点数、使用 BufferGeometry 等