Bootstrap

Three.js + GIS:打造三维地理信息系统的教程

Three.js + GIS:打造三维地理信息系统的教程

Three.js 是构建 WebGL 3D 应用的强大工具,而结合 GIS(地理信息系统)后,可以实现炫酷的三维地理可视化效果。本教程将从基础入手,教你如何用 Three.js 打造一个简单的三维 GIS 应用。


一、项目规划

1. 目标功能

我们希望实现以下功能:

  1. 加载三维地图:加载全球地图或指定区域。
  2. 添加三维数据:在地图上展示建筑、标注等 3D 数据。
  3. 交互功能
    • 用户可以缩放、旋转地图。
    • 点击地图显示详细信息。
  4. 数据支持:支持 GeoJSON、栅格和矢量数据加载。

2. 技术选型

  • Three.js:用于渲染 3D 地图和模型。
  • Turf.js(可选):用于地理空间数据处理。
  • Mapbox 或 OpenLayers(可选):用于底图加载与结合。

二、项目初始化

1. 创建项目

mkdir threejs-gis
cd threejs-gis
npm init -y
npm install three

2. 设置文件结构

threejs-gis/
├── src/
│   ├── index.html   # HTML 入口文件
│   ├── main.js      # 主逻辑文件
│   ├── data/
│   │   └── world.geojson # 示例 GeoJSON 数据
├── package.json
└── node_modules/

三、实现 3D GIS 系统

1. 初始化基础场景

创建 HTML 文件

src/index.html 中设置基础页面:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Three.js GIS</title>
  <style>
    body { margin: 0; overflow: hidden; }
  </style>
</head>
<body>
  <canvas id="mapCanvas"></canvas>
  <script type="module" src="main.js"></script>
</body>
</html>
初始化 Three.js 场景

src/main.js 中设置基础场景:

import * as THREE from 'three';

// 创建场景
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x87ceeb); // 天空蓝背景

// 摄像机
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(0, 50, 100); // 初始位置

// 渲染器
const renderer = new THREE.WebGLRenderer({ canvas: document.getElementById('mapCanvas') });
renderer.setSize(window.innerWidth, window.innerHeight);

// 地板模拟地图
const planeGeometry = new THREE.PlaneGeometry(200, 200);
const planeMaterial = new THREE.MeshStandardMaterial({ color: 0x007b43, side: THREE.DoubleSide });
const plane = new THREE.Mesh(planeGeometry, planeMaterial);
plane.rotation.x = -Math.PI / 2; // 设置为水平面
scene.add(plane);

// 光源
const light = new THREE.DirectionalLight(0xffffff, 1);
light.position.set(10, 20, 10);
scene.add(light);

// 渲染循环
function animate() {
  requestAnimationFrame(animate);
  renderer.render(scene, camera);
}
animate();

// 监听窗口大小变化
window.addEventListener('resize', () => {
  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();
  renderer.setSize(window.innerWidth, window.innerHeight);
});

2. 加载 GeoJSON 数据

添加 GeoJSON 数据

src/data/world.geojson 中添加示例数据:

{
  "type": "FeatureCollection",
  "features": [
    {
      "type": "Feature",
      "geometry": {
        "type": "Polygon",
        "coordinates": [[[0, 0], [10, 0], [10, 10], [0, 10], [0, 0]]]
      },
      "properties": { "name": "Test Region" }
    }
  ]
}
解析 GeoJSON 并绘制多边形

main.js 中使用 Three.js 绘制 GeoJSON 数据:

import { Shape, ShapeGeometry, Mesh, MeshStandardMaterial } from 'three';

async function loadGeoJSON(scene) {
  const response = await fetch('./data/world.geojson');
  const geojson = await response.json();

  geojson.features.forEach((feature) => {
    const shape = new Shape();

    // 解析多边形坐标
    const coords = feature.geometry.coordinates[0];
    coords.forEach(([x, y], index) => {
      if (index === 0) shape.moveTo(x, y);
      else shape.lineTo(x, y);
    });

    // 创建 3D 几何体
    const geometry = new ShapeGeometry(shape);
    const material = new MeshStandardMaterial({ color: 0x228b22 });
    const mesh = new Mesh(geometry, material);
    mesh.rotation.x = -Math.PI / 2; // 设置为水平面
    mesh.position.y = 0.1; // 抬高一点防止与地板重叠
    scene.add(mesh);
  });
}

// 加载并绘制 GeoJSON
loadGeoJSON(scene);

3. 添加交互功能

添加 OrbitControls

引入 OrbitControls 来实现交互控制:

import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';

const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true; // 平滑阻尼
点击事件

实现点击地图显示区域信息:

renderer.domElement.addEventListener('click', (event) => {
  const mouse = new THREE.Vector2(
    (event.clientX / window.innerWidth) * 2 - 1,
    -(event.clientY / window.innerHeight) * 2 + 1
  );

  const raycaster = new THREE.Raycaster();
  raycaster.setFromCamera(mouse, camera);

  const intersects = raycaster.intersectObjects(scene.children);
  if (intersects.length > 0) {
    console.log('Clicked on:', intersects[0].object.name);
  }
});

四、性能优化

  1. 简化数据:GeoJSON 数据的多边形点数较多时,可使用工具(如 mapshaper)简化。
  2. 按需加载:对大型数据集,按视图范围动态加载数据。
  3. 减少绘制次数:合并相邻区域为单一几何体。

五、总结

通过本教程,你已经了解了如何结合 Three.jsGeoJSON 构建一个简单的 3D GIS 系统。接下来可以进一步拓展功能,比如:

  • 实现动态数据更新(如人口密度热力图)。
  • 加入飞行动画视角。
  • 集成卫星图层和 DEM 数据。

需要更高级的功能或进一步优化?随时告诉我! 🌍

;