vlambda博客
学习文章列表

采用threejs加载geojson案例


几年前,在做3D地图时,使用到了threejs加载已有的geojson数据,实现立体地图。当然,现在常用的做法,基本上都是采用 ceisium 来实现3D GIS 可视化。

本文展示基本思路和主要代码块。

  • 首先下载一套geojson数据,如: china.json


  • 加载核心threejs脚本,官方文档 https://threejs.org/

  • 定义核心变量


// 渲染器,相机,场景var renderer, camera, scene = null;
// 包裹画布domconst dom = document.getElementById('container');
// 地图正面颜色const faceColor = '#345fa6';// 地图侧边颜色const sideColor = '#1e90ff';
// orbit 对象let orbitcontrols = null;
// 地图缩放比例const shapeScaleSize = 1;
// 中国经纬度对象let shapeGeometry = {};let mapGrop = new THREE.Group();let axes;
初始化3D
// 初始化3Dfunction initThree() { // 初始化场景 scene = new THREE.Scene(); scene.rotation.x = -0.8;  scene.add(mapGrop); //坐标轴辅助器,X,Y,Z长度100 axes = new THREE.AxesHelper(100); this.scene.add(axes); // 初始化相机 camera = new THREE.PerspectiveCamera(60, dom.clientWidth / dom.clientHeight, 1, 10000); // 设置相机位置 camera.position.set(22, -7, 136); renderer = new THREE.WebGLRenderer({ alpha: true, antialias: true }); // 设置窗口尺寸 renderer.setSize(dom.clientWidth, dom.clientHeight); // 初始化控制器 orbitcontrols = new THREE.OrbitControls(camera, renderer.domElement); dom.appendChild(renderer.domElement);
// 绘制地图 drapMapShape(); // 渲染 render();
window.addEventListener('resize', function () { // 重绘之前先将原先的移除 clearShape(); // 重新初始化尺寸 camera.aspect = dom.clientWidth / dom.clientHeight; renderer.setSize(dom.clientWidth, dom.clientHeight); // 重绘地图 drapMapShape(); });}
// 清空 map shapefunction clearShape() { // mapGrop.traverse(function(child){ // if (child.isMesh){ // mapGrop.remove(child); // } // });    scene.remove(mapGrop); mapGrop = new THREE.Group(); scene.add(mapGrop);}

将geojson存放在本地,获取json数据,本示例直接采用 fetch 进行数据请求。
// 获取中国经纬度信息函数function getShapeGeometry() { fetch('/resources/china.json').then(res => res.json()).then(data => { //返回数据根据结果进行相应的处理 shapeGeometry = data; initThree(); });}
绘制地图方法
// 绘制地图function drapMapShape() { // 绘制中国地图 shapeGeometry.features.forEach(function (worldItem, worldIndex) { const length = worldItem.geometry.coordinates.length; const multipleBool = length > 1 ? true : false; //这里指的是原点缩定位的位置 109.01373,34.381819 const averageX = 109.01373; const averageY = 34.381819; worldItem.geometry.coordinates.forEach(function (worldChildItem, wordItemIndex) { if (multipleBool) { // 值界可以使用的经纬度信息 if (worldChildItem.length && worldChildItem[0].length == 2) { shapeTo3D(drawShape(worldChildItem, averageX, averageY), '' + worldIndex + wordItemIndex); } // 需要转换才可以使用的经纬度信息 if (worldChildItem.length && worldChildItem[0].length > 2) { worldChildItem.forEach(function (countryItem, countryIndex) { shapeTo3D(drawShape(countryItem, averageX, averageY), '' + worldIndex + wordItemIndex + countryIndex); }); } } else { let countryPos = null; if (worldChildItem.length > 1) { countryPos = worldChildItem; } else { countryPos = worldChildItem[0]; } if (countryPos) { shapeTo3D(drawShape(countryPos, averageX, averageY), '' + worldIndex + wordItemIndex); } } }); });}
// ExturdeGeometry配置参数const options = { depth: 3, // 定义图形拉伸的深度,默认100 steps: 0, // 拉伸面方向分为多少级,默认为1 bevelEnabled: true, // 表示是否有斜角,默认为true bevelThickness: 0, // 斜角的深度,默认为6 bevelSize: 0, // 表示斜角的高度,高度会叠加到正常高度 bebelSegments: 0, // 斜角的分段数,分段数越高越平滑,默认为1 curveSegments: 0 // 拉伸体沿深度方向分为多少段,默认为1}; // 将shape转换为ExtrudeGeometryfunction shapeTo3D(shapeObj, identify) { const geometry = new THREE.ExtrudeGeometry(shapeObj, options); const matFace = new THREE.MeshBasicMaterial({ color: faceColor, transparent: true, opacity: 0.5 }); const matEdge = new THREE.MeshBasicMaterial({ color: sideColor, transparent: true, opacity: 1 }); // 绘制地图 const mapShape = new THREE.Mesh(geometry, [matFace, matEdge]); mapShape.name = identify; // 将地图加入场景 mapGrop.add(mapShape);}
// 绘制地图 shapefunction drawShape(pos, averageX, averageY) { // debugger; const shape = new THREE.Shape(); // 计算平均每格占比 let average = 0; if (dom.clientWidth > dom.clientHeight) { average = dom.clientHeight / 180; } else { average = dom.clientWidth / 360; } shape.moveTo(((pos[0][0] - averageX) * average) / shapeScaleSize, ((pos[0][1] - averageY) * average) / shapeScaleSize); pos.forEach(function (item) { shape.lineTo(((item[0] - averageX) * average) / shapeScaleSize, ((item[1] - averageY) * average) / shapeScaleSize); }); return shape;}

添加 render 方法
// 执行函数function render({ renderer.render(scene, camera); orbitcontrols.update(); requestAnimationFrame(render);}
初始化执行
// 页面资源加载完毕之后执行创建window.onload = function () { getShapeGeometry();};

总结:
核心其实很简单,就是将geo数据转化为 ExtrudeGeometry,然后创建mesh添加到场景即可。

最后展示一下效果