目录

1.GeoJSON

1.1 GeoJSON介绍

1.2 GeoJSON数据获取

2. Three加载GeoJSON数据

2.1 加载并解析GeoJSON

2.2 对JSON数据中的地理坐标进行转换

2.3 操作数据并生成三维地图

2.4 添加点击事件实现点击地图切换颜色

2.5 main.js源码


1.GeoJSON

1.1 GeoJSON介绍

GeoJSON是一种对各种地理数据结构进行编码的格式,基于Javascript对象表示法(JavaScript Object Notation, 简称JSON)的地理空间信息数据交换格式。GeoJSON对象可以表示几何、特征或者特征集合。GeoJSON支持下面几何类型:点、线、面、多点、多线、多面和几何集合。GeoJSON里的特征包含一个几何对象和其他属性,特征集合表示一系列特征。

{"type": "Feature","geometry": {"type": "Point","coordinates": [125.6, 10.1]},"properties": {"name": "Dinagat Islands"}
}

一个完整的GeoJSON数据结构总是一个(JSON术语里的)对象。在GeoJSON里,对象由名/值对--也称作成员的集合组成。对每个成员来说,名字总是字符串。成员的值要么是字符串、数字、对象、数组,要么是下面文本常量中的一个:"true","false"和"null"。数组的值是上面所说的元素组成。

GeoJSON总是由一个单独的对象组成。这个对象(指的是下面的GeoJSON对象)表示几何、特征或者特征集合。

GeoJSON对象可能有任何数目成员(名/值对)。

GeoJSON对象必须有一个名字为"type"的成员。这个成员的值是由GeoJSON对象的类型所确定的字符串。

type成员的值必须是下面之一:"Point", "MultiPoint", "LineString", "MultiLineString", "Polygon", "MultiPolygon", "GeometryCollection", "Feature", 或者 "FeatureCollection"。

GeoJSON对象可能有一个可选的"crs"成员,它的值必须是一个坐标参考系统的对象。

GeoJSON对象可能有一个"bbox"成员,它的值必须是边界框数组。

GeoJSON特征集合:

{"type": "FeatureCollection","features": [{"type": "Feature","geometry": {"type": "Point","coordinates": [102.0, 0.5]},"properties": {"prop0": "value0"}}, {"type": "Feature","geometry": {"type": "LineString","coordinates": [[102.0, 0.0], [103.0, 1.0], [104.0, 0.0], [105.0, 1.0]]},"properties": {"prop0": "value0","prop1": 0.0}}, {"type": "Feature","geometry": {"type": "Polygon","coordinates": [[100.0, 0.0], [101.0, 0.0], [101.0, 1.0], [100.0, 1.0], [100.0, 0.0]]},"properties": {"prop0": "value0","prop1": {"this": "that"}}}]
}

1.2 GeoJSON数据获取

可以通过阿里云DataV获取GeoJSON数据,也可以在其他地理信息平台获取数据并转换为GeoJson数据:
DataV.GeoAtlas地理小工具系列由阿里云DataV数据可视化团队出品,多年深耕数据可视化领域,数据大屏业务开拓者和领航者。致力用震撼而清晰的视觉语言,让更多人读懂大数据,受惠数据驱动的决策方式。http://datav.aliyun.com/portal/school/atlas/area_selector#&lat=33.521903996156105&lng=104.29849999999999&zoom=4

2. Three加载GeoJSON数据

2.1 加载并解析GeoJSON

使用Three提供的FileLoader加载数据并对JSON数据进行解析:

const loader = new THREE.FileLoader();
loader.load("https://geo.datav.aliyun.com/areas_v3/bound/100000_full.json", (data) => {console.log(data);const jsonData = JSON.parse(data);operationData(jsonData);console.log(jsonData);
});

控制台输出如下:

2.2 对JSON数据中的地理坐标进行转换

安装d3并使用该插件将地理坐标转换为Three所支持的xyz坐标系:

1)d3.js安装

yarn add d3
or
npm install d3

2)导入d3

import * as d3 from "d3";

3)使用d3转换坐标

// 以经纬度116,39为中心,进行投影的函数转换函数
const projection1 = d3.geoMercator().center([116, 39]).translate([0, 0, 0]);

2.3 操作数据并生成三维地图

function operationData(jsondata) {// 全国信息const features = jsondata.features;features.forEach((feature) => {// 单个省份 对象const province = new THREE.Object3D();// 地址province.properties = feature.properties.name;const coordinates = feature.geometry.coordinates;const color = "#99ff99";if (feature.geometry.type === "MultiPolygon") {// 多个,多边形coordinates.forEach((coordinate) => {// console.log(coordinate);// coordinate 多边形数据coordinate.forEach((rows) => {const mesh = drawExtrudeMesh(rows, color, projection1);const line = lineDraw(rows, color, projection1);// 唯一标识mesh.properties = feature.properties.name;province.add(line);province.add(mesh);});});}if (feature.geometry.type === "Polygon") {// 多边形coordinates.forEach((coordinate) => {const mesh = drawExtrudeMesh(coordinate, color, projection1);const line = lineDraw(coordinate, color, projection1);// 唯一标识mesh.properties = feature.properties.name;province.add(line);province.add(mesh);});}map.add(province);});scene.add(map);
}function lineDraw(polygon, color, projection) {const lineGeometry = new THREE.BufferGeometry();const pointsArray = new Array();polygon.forEach((row) => {const [x, y] = projection(row);// 创建三维点pointsArray.push(new THREE.Vector3(x, -y, 9));});// 放入多个点lineGeometry.setFromPoints(pointsArray);// 生成随机颜色const lineColor = new THREE.Color(Math.random() * 0.5 + 0.5,Math.random() * 0.5 + 0.5,Math.random() * 0.5 + 0.5);const lineMaterial = new THREE.LineBasicMaterial({color: lineColor,});return new THREE.Line(lineGeometry, lineMaterial);
}// 根据经纬度坐标生成物体
function drawExtrudeMesh(polygon, color, projection) {const shape = new THREE.Shape();// console.log(polygon, projection);polygon.forEach((row, i) => {const [x, y] = projection(row);// console.log(row, [x, y]);if (i === 0) {// 创建起点,使用moveTo方法// 因为计算出来的y是反过来,所以要进行颠倒shape.moveTo(x, -y);}shape.lineTo(x, -y);});// 拉伸const geometry = new THREE.ExtrudeGeometry(shape, {depth: 5,bevelEnabled: true,});// 随机颜色const randomColor = (0.5 + Math.random() * 0.5) * 0xffffff;const material = new THREE.MeshBasicMaterial({color: randomColor,transparent: true,opacity: 0.5,});return new THREE.Mesh(geometry, material);
}

实现效果:

2.4 添加点击事件实现点击地图切换颜色

// 监听鼠标
window.addEventListener("click", onRay);
// 全局对象
let lastPick = null;
function onRay(event) {let pickPosition = setPickPosition(event);const raycaster = new THREE.Raycaster();raycaster.setFromCamera(pickPosition, camera);// 计算物体和射线的交点const intersects = raycaster.intersectObjects([map], true);// 数组大于0 表示有相交对象if (intersects.length > 0) {if (lastPick) {if (lastPick.object.properties !== intersects[0].object.properties) {lastPick.object.material.color.set("#99ff99");lastPick = null;}}if (intersects[0].object.properties) {intersects[0].object.material.color.set("red");}lastPick = intersects[0];} else {if (lastPick) {// 复原if (lastPick.object.properties) {lastPick.object.material.color.set("yellow");lastPick = null;}}}
}/*** 获取鼠标在three.js 中归一化坐标* */
function setPickPosition(event) {let pickPosition = { x: 0, y: 0 };// 计算后 以画布 开始为 (0,0)点const pos = getCanvasRelativePosition(event);// 数据归一化pickPosition.x = (pos.x / canvas.width) * 2 - 1;pickPosition.y = (pos.y / canvas.height) * -2 + 1;return pickPosition;
}// 计算 以画布 开始为(0,0)点 的鼠标坐标
function getCanvasRelativePosition(event) {const rect = canvas.getBoundingClientRect();return {x: ((event.clientX - rect.left) * canvas.width) / rect.width,y: ((event.clientY - rect.top) * canvas.height) / rect.height,};
}

实现效果:

2.5 main.js源码

import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import * as d3 from "d3";
import Stats from "three/examples/jsm/libs/stats.module.js";const stats = new Stats();
document.body.appendChild(stats.dom);
// console.log(THREE);
// 初始化场景
const scene = new THREE.Scene();// console.log(d3);
// 创建透视相机
const camera = new THREE.PerspectiveCamera(90,window.innerHeight / window.innerHeight,0.1,100000
);
// 设置相机位置
// object3d具有position,属性是1个3维的向量
camera.position.set(0, 0, 1000);
// 更新摄像头
camera.aspect = window.innerWidth / window.innerHeight;
//   更新摄像机的投影矩阵
camera.updateProjectionMatrix();
scene.add(camera);// 加入辅助轴,帮助我们查看3维坐标轴
const axesHelper = new THREE.AxesHelper(5);
scene.add(axesHelper);// 加载纹理
const map = new THREE.Object3D();const directionalLight = new THREE.DirectionalLight(0xffffff, 0.5);
scene.add(directionalLight);
const light = new THREE.AmbientLight(0xffffff, 0.5); // soft white light
scene.add(light);
// 初始化渲染器
const renderer = new THREE.WebGLRenderer({ alpha: true });
// renderer.shadowMap.enabled = true;
// renderer.shadowMap.type = THREE.BasicShadowMap;
// renderer.shadowMap.type = THREE.VSMShadowMap;// 设置渲染尺寸大小
renderer.setSize(window.innerWidth, window.innerHeight);// 监听屏幕大小改变的变化,设置渲染的尺寸
window.addEventListener("resize", () => {//   console.log("resize");// 更新摄像头camera.aspect = window.innerWidth / window.innerHeight;//   更新摄像机的投影矩阵camera.updateProjectionMatrix();//   更新渲染器renderer.setSize(window.innerWidth, window.innerHeight);//   设置渲染器的像素比例renderer.setPixelRatio(window.devicePixelRatio);
});// 将渲染器添加到body
document.body.appendChild(renderer.domElement);
const canvas = renderer.domElement;// 初始化控制器
const controls = new OrbitControls(camera, renderer.domElement);
// 设置控制器阻尼
controls.enableDamping = true;
// 设置自动旋转
// controls.autoRotate = true;const clock = new THREE.Clock();
function animate(t) {controls.update();stats.update();const deltaTime = clock.getDelta();requestAnimationFrame(animate);// 使用渲染器渲染相机看这个场景的内容渲染出来renderer.render(scene, camera);
}animate();// 创建纹理加载器对象
const textureLoader = new THREE.TextureLoader();const loader = new THREE.FileLoader();
loader.load("https://geo.datav.aliyun.com/areas_v3/bound/100000_full.json", (data) => {//console.log(data);const jsonData = JSON.parse(data);operationData(jsonData);console.log(jsonData);
});
// 以经纬度116,39为中心,进行投影的函数转换函数
const projection1 = d3.geoMercator().center([116, 39]).translate([0, 0, 0]);function operationData(jsondata) {// 全国信息const features = jsondata.features;features.forEach((feature) => {// 单个省份 对象const province = new THREE.Object3D();// 地址province.properties = feature.properties.name;const coordinates = feature.geometry.coordinates;const color = "#99ff99";if (feature.geometry.type === "MultiPolygon") {// 多个,多边形coordinates.forEach((coordinate) => {// console.log(coordinate);// coordinate 多边形数据coordinate.forEach((rows) => {const mesh = drawExtrudeMesh(rows, color, projection1);const line = lineDraw(rows, color, projection1);// 唯一标识mesh.properties = feature.properties.name;province.add(line);province.add(mesh);});});}if (feature.geometry.type === "Polygon") {// 多边形coordinates.forEach((coordinate) => {const mesh = drawExtrudeMesh(coordinate, color, projection1);const line = lineDraw(coordinate, color, projection1);// 唯一标识mesh.properties = feature.properties.name;province.add(line);province.add(mesh);});}map.add(province);});scene.add(map);
}function lineDraw(polygon, color, projection) {const lineGeometry = new THREE.BufferGeometry();const pointsArray = new Array();polygon.forEach((row) => {const [x, y] = projection(row);// 创建三维点pointsArray.push(new THREE.Vector3(x, -y, 9));});// 放入多个点lineGeometry.setFromPoints(pointsArray);// 生成随机颜色const lineColor = new THREE.Color(Math.random() * 0.5 + 0.5,Math.random() * 0.5 + 0.5,Math.random() * 0.5 + 0.5);const lineMaterial = new THREE.LineBasicMaterial({color: lineColor,});return new THREE.Line(lineGeometry, lineMaterial);
}// 根据经纬度坐标生成物体
function drawExtrudeMesh(polygon, color, projection) {const shape = new THREE.Shape();// console.log(polygon, projection);polygon.forEach((row, i) => {const [x, y] = projection(row);// console.log(row, [x, y]);if (i === 0) {// 创建起点,使用moveTo方法// 因为计算出来的y是反过来,所以要进行颠倒shape.moveTo(x, -y);}shape.lineTo(x, -y);});// 拉伸const geometry = new THREE.ExtrudeGeometry(shape, {depth: 5,bevelEnabled: true,});// 随机颜色const randomColor = (0.5 + Math.random() * 0.5) * 0xffffff;const material = new THREE.MeshBasicMaterial({color: randomColor,transparent: true,opacity: 0.5,});return new THREE.Mesh(geometry, material);
}// 监听鼠标
window.addEventListener("click", onRay);
// 全局对象
let lastPick = null;
function onRay(event) {let pickPosition = setPickPosition(event);const raycaster = new THREE.Raycaster();raycaster.setFromCamera(pickPosition, camera);// 计算物体和射线的交点const intersects = raycaster.intersectObjects([map], true);// 数组大于0 表示有相交对象if (intersects.length > 0) {if (lastPick) {if (lastPick.object.properties !== intersects[0].object.properties) {lastPick.object.material.color.set("#99ff99");lastPick = null;}}if (intersects[0].object.properties) {intersects[0].object.material.color.set("red");}lastPick = intersects[0];} else {if (lastPick) {// 复原if (lastPick.object.properties) {lastPick.object.material.color.set("yellow");lastPick = null;}}}
}/*** 获取鼠标在three.js 中归一化坐标* */
function setPickPosition(event) {let pickPosition = { x: 0, y: 0 };// 计算后 以画布 开始为 (0,0)点const pos = getCanvasRelativePosition(event);// 数据归一化pickPosition.x = (pos.x / canvas.width) * 2 - 1;pickPosition.y = (pos.y / canvas.height) * -2 + 1;return pickPosition;
}// 计算 以画布 开始为(0,0)点 的鼠标坐标
function getCanvasRelativePosition(event) {const rect = canvas.getBoundingClientRect();return {x: ((event.clientX - rect.left) * canvas.width) / rect.width,y: ((event.clientY - rect.top) * canvas.height) / rect.height,};
}

Three.js+GeoJSON实现三维地图显示相关推荐

  1. 2d与2.5d坐标转换_Three.js 地理坐标和三维空间坐标的转换

    奇技指南 本文作者高峰,360奇舞团前端工程师,W3C性能工作组/WOT工作组成员 本文转载自奇舞周刊 引言 在实现3D地球时,球面是通过地理贴图渲染的.所以我们所说的地理坐标和三维空间坐标的转换,是 ...

  2. 三维坐标转经纬度_Three.js 地理坐标和三维空间坐标的转换

    编者按:本文作者高峰,360奇舞团前端工程师,W3C性能工作组/WOT工作组成员. 一.引言 在实现3D地球时,球面是通过地理贴图渲染的.所以我们所说的地理坐标和三维空间坐标的转换,是指将地理贴图上的 ...

  3. 使用three.js + geojson 完成广西地图的绘制(上篇)

    一.效果如下: three.js + geojson 绘制广西地图 二.事先准备好要使用的数据 首先要准备好广西地图的geojson数据,可以通过阿里云的Data.GeoAtlas地理小工具获取想要的 ...

  4. Three.js 地理坐标和三维空间坐标的转换

    奇技指南 本文作者高峰,360奇舞团前端工程师,W3C性能工作组/WOT工作组成员 本文转载自奇舞周刊 引言 在实现3D地球时,球面是通过地理贴图渲染的.所以我们所说的地理坐标和三维空间坐标的转换,是 ...

  5. mapbox-gl使用geojson实现三维建筑物效果

    1 引入mapbox-gl.js.mapbox-gl.css 2 vue中初始化地图 import {getJson} from "./geojson/json"async ini ...

  6. Three.js加载三维管线的简单思路

    three.js中能够加载一定量的三维模型数据,当然也能够加载一定量的管线数据,three.js的鼠标操控,会影响到管线的摆放. 大多数三维平台中,加载管线的原理都差不多,只不过对应三维引擎中的API ...

  7. 如何在前端用js实现画三维饼图和三维柱形图

    echarts没有三维饼图,但是有时候又需要在前端绘制三维饼图怎么办?这个时候可以考虑用threejs来实现功能. 现在我们来实现图示效果:         1:首先在项目里引入threejs文件 t ...

  8. Three.js教程:三维坐标系

    推荐:将 NSDT场景编辑器 加入你的3D工具链 其他系列工具: NSDT简石数字孪生 三维坐标系 本节课的目的就是为了加强大家对threejs三维空间的认识. 辅助观察坐标系 THREE.AxesH ...

  9. 基于Node.js的3DTiles三维倾斜摄影模型爬虫

    随着小型无人机的普及,乡村级的倾斜摄影模型构建已经越来越简单.一个无人机和一名飞手2个小时内就可以完成.在做WebGIS和Cesium开发时,3DTiles是一种常用的倾斜摄影三维模型的切片格式.3D ...

最新文章

  1. 大学 University
  2. Linux实现开机自动运行普通用户脚本
  3. QQ for Linux下载、安装、运行、卸载
  4. opengles 顶点数组 android,OpenGLES顶点属性、顶点数组和缓冲区对象
  5. iphone的生命周期
  6. 【Python】Python实战从入门到精通之七 -- 教你深入理解异常处理
  7. 面试官:简单说说Java8中的HashMap到底有啥变化?
  8. python通过代理发送邮件_使用Python通过SMTP发送邮件
  9. Eclipse启动项目报启动上下文失败问题解决方案总结
  10. stringByReplacingCharactersInRange: withString: 实现字符串删除,替换
  11. C++ 练习题(一:布尔表达式与真值表图文详解)
  12. Flink on Zeppelin (3) - Streaming 篇
  13. spark中的广播变量与累加器
  14. IDEA中Maven项目中报错:10 unmapped Spring configuration files
  15. 天下会 - 搜索经验之网络搜索技巧总结
  16. 【BUUCTF】[WUSTCTF2020]alison_likes_jojo
  17. 实现一个简单的记事本APP
  18. IOS获取UDID添加到开发者账号用来安装测试包
  19. 【Linux 中国】Simula 诞生之前的面向对象程序设计
  20. python爬虫爬取的数据与浏览器获取的数据不一样 爬虫爬取到的数据一直不变

热门文章

  1. JavaScript中的逗号操作符(基础使用和拓展用法)
  2. ifstream java_ifstream :: seekg给出了错误的结果
  3. win10下安装Ubuntu子系统
  4. FlyAI小课堂:深度学习论文翻译解析(3):丰富的特征层次结构,可实现准确的目标检测和语义分割
  5. Numbers Everyone Should Know
  6. 网上打印文本资料应该去哪里打印
  7. 常见的计算机系统可靠性数学模型,系统可靠性与失效率(串并联)的计算题
  8. JS async 函数
  9. 【无标题】Win10 打开Office慢解决方式
  10. 唱歌的方法与技巧[收集]