Three.js + GIS:打造三维地理信息系统的教程
Three.js 是构建 WebGL 3D 应用的强大工具,而结合 GIS(地理信息系统)后,可以实现炫酷的三维地理可视化效果。本教程将从基础入手,教你如何用 Three.js 打造一个简单的三维 GIS 应用。
一、项目规划
1. 目标功能
我们希望实现以下功能:
- 加载三维地图:加载全球地图或指定区域。
- 添加三维数据:在地图上展示建筑、标注等 3D 数据。
- 交互功能:
- 用户可以缩放、旋转地图。
- 点击地图显示详细信息。
- 数据支持:支持 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);
}
});
四、性能优化
- 简化数据:GeoJSON 数据的多边形点数较多时,可使用工具(如
mapshaper
)简化。 - 按需加载:对大型数据集,按视图范围动态加载数据。
- 减少绘制次数:合并相邻区域为单一几何体。
五、总结
通过本教程,你已经了解了如何结合 Three.js 和 GeoJSON 构建一个简单的 3D GIS 系统。接下来可以进一步拓展功能,比如:
- 实现动态数据更新(如人口密度热力图)。
- 加入飞行动画视角。
- 集成卫星图层和 DEM 数据。
需要更高级的功能或进一步优化?随时告诉我! 🌍