一、threejs地图-绘制中国地图
1. 中国地图
地图数据的获取 阿里提供的数据可视化平台
导入数据,在
geojson.io
上显示数据结构
- 查看数据结构发现,算上九段线,有
34
个MultiPolygon
和 1个Polygon
- 观察其数据结构
- MultiPolygonjson5
{ coordinates: [ [ [ ['经度', '纬度'], ['经度', '纬度'], ['经度', '纬度'], ] ], [ [ ['经度', '纬度'], ['经度', '纬度'], ['经度', '纬度'], ] ], [ [ ['经度', '纬度'], ['经度', '纬度'], ['经度', '纬度'], ] ] ] }
Polygon
的数据结构json5{ coordinates: [ [ ['经度', '纬度'], ['经度', '纬度'], ['经度', '纬度'], ] ] }
- MultiPolygon
- 查看地图的数据结构发现:
Polygon
和MultiPolygon
两者数据结构MultiPolygon
的coordinates
节点的数据节点多一层
- 查看数据结构发现,算上九段线,有
二、绘制中国地图
1. 核心代码
- 数据解析js
/** * 处理地图经纬度数据 */ initChinaMap() { this.#mapData?.features?.map((item) => { const areas = item.geometry.coordinates; const type = item.geometry.type; // 将其他属性依旧放在 properties 中,将 coordinates 中的坐标转成 空间坐标 const areaVector = { ...item.properties, coordinates: [] }; areas.map((area, index) => { if (type === POLYGON) { areaVector.coordinates[index] = []; area.map((point) => { areaVector.coordinates[index].push(this.lnglatToVector(point)) }) } else if (type === MULTI_POLYGON) { areaVector.coordinates[index] = []; area.map((point) => { point?.map(pointInner => { areaVector.coordinates[index].push(this.lnglatToVector(pointInner)) }) }) } }) this.#vector3Json.push(areaVector); }) // 绘制模块 const group = new Group(); this.#vector3Json.map(vectors => { vectors.coordinates.map(vector => { const mesh = this.drawChina(vector); group.add(mesh); }) }) group.rotation.y = Math.PI; this.scene.add(group); }
2. 完整示例
代码示例
jsimport ThreeBase from "./ThreeBase"; import * as D3 from 'd3-geo'; import axios from "axios"; import { Group, Shape, ExtrudeGeometry, MeshBasicMaterial, Mesh } from 'three'; const MULTI_POLYGON = 'MultiPolygon'; const POLYGON = 'Polygon' /** * 绘制多多边形 */ export default class ThreeMultiplePolygonMap extends ThreeBase { #projection; #mapData; #vector3Json = []; constructor() { super(); } initObj() { this.initMapData().then(() => { this.initChinaMap() }) } initMapData() { return axios.get('geojson/china.json').then(res => { this.#mapData = res.data; }) } /** * 绘制网格 */ drawChina(points= []) { const shape = new Shape(); points?.map((point, index) => { const [x, y] = point; if (index === 0) { shape.moveTo(x, y); } else if (index === points.length - 1) { // 二次曲线 shape.quadraticCurveTo(x, y, x, y) } else { shape.lineTo(x, y, x, y) } }) // bevelEnabled 是否启用斜角 const geometry = new ExtrudeGeometry( shape, { depth: -2, bevelEnabled: false } ) // 材质 const material = new MeshBasicMaterial({ color: "#007cff", transparent: true, opacity: 0.5 }); // 合并 const mesh = new Mesh(geometry, material); mesh.rotateX(Math.PI /2 ) return mesh; } /** * 处理地图经纬度数据 */ initChinaMap() { this.#mapData?.features?.map((item) => { const areas = item.geometry.coordinates; const type = item.geometry.type; const areaVector = { ...item.properties, coordinates: [] }; areas.map((area, index) => { if (type === POLYGON) { areaVector.coordinates[index] = []; area.map((point) => { areaVector.coordinates[index].push(this.lnglatToVector(point)) }) } else if (type === MULTI_POLYGON) { areaVector.coordinates[index] = []; area.map((point) => { point?.map(pointInner => { areaVector.coordinates[index].push(this.lnglatToVector(pointInner)) }) }) } }) this.#vector3Json.push(areaVector); }) // 绘制模块 const group = new Group(); this.#vector3Json.map(vectors => { vectors.coordinates.map(vector => { const mesh = this.drawChina(vector); group.add(mesh); }) }) group.rotation.y = Math.PI; this.scene.add(group); } lnglatToVector(lnglat) { if (!this.#projection) { this.#projection = D3.geoMercator() .center([112.946332, 28.236672]) .scale(80) .translate([0,0]); } const [y, x] = this.#projection([...lnglat]); const z = 0; return [y, x, z]; } }
效果展示