今天我们来实现在Threejs中根据JSON格式数据创建三维地图的效果,先看下最终项目完成后的效果展示
通过读取JSON格式地图数据,获取地图边界及各区域边界经坐标,使用使用 D3.js 库中的 geoMercator() 方法进行墨卡托投影转换,将经纬度坐标转换为平面直角坐标系中的像素坐标,根据转换后的像素坐标,使用Threejs提供的ExtrudeGeometry方法对形状进行拉伸,形成三维立体效果;然后通过使用Threejs提供的Raycaster射线,获取鼠标与Mesh的焦点,并监听鼠标的mousemove移动事件和click点击事件,在mousemove移动事件处理Mesh变色,在click事件中对点击的边界进行拉伸,最终形成如图所示效果
JSON格式数据可以从阿里云提供的DataV工具中下载,具体下载地址为:;lat=30.332329214580188&lng=106.72278672066881&zoom=3.5,界面如下图所示:
打开页面后,选择你需要下载的省、市或者区县的边界地图,点击右面的下载即可,我这里选取的是杭州及其下属区县的地图JSON格式数据。
<template><MapView></MapView>
</template>
<script setup>
import MapView from './components/MapView.vue';
</script>
<style scoped>
</style>
style.css中的样式代码如下:
*{ margin: 0; padding: 0; list-style: none; }
<template><div id="scene"></div>
</template>
import * as THREE from 'three'
import * as d3 from 'd3'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
import { CSS2DRenderer,CSS2DObject } from 'three/examples/jsm/renderers/CSS2DRenderer'
import { onMounted } from 'vue';
import hangzhou from '../js/json/hangzhou.json'
let renderer, scene, camera, orbitControls, map, labelRenderer, mouse
let projectpos = d3.geoMercator() .center([120.21,30.25]) .scale(1600) .translate([0,0])
function init() {
}
onMounted(() => {init()
})
const initScene = () => {scene = new THREE.Scene()
}
2 、initCamera方法 代码const initCamera = () => {camera = new THREE.PerspectiveCamera(45,document.querySelector('#scene').clientWidth/document.querySelector('#scene').clientHeight)camera.position.set(50,50,50)camera.lookAt(scene.position)
}
3、initGeoJson方法代码const initGeoJson() => {map = new THREE.Group()initMap(hangzhou)
}
这里定义了一个initMap方法,该方法用于处理JSON数据,核心代码如下const initMap = (hangzhou) => {hangzhou.features.forEach(element => {// 定义一个province 的省份3D对象const province = new THREE.Object3D()// 结构hangzhou.json中ry中的coordinates// 对应的是每个点的坐标数组let { coordinates } = ryinitText(element.properties)// 循环坐标数组coordinates.forEach(multiPolygon => {multiPolygon.forEach(polygon => {// 定义shape对象const shape = new THREE.Shape()const lineMaterial = new THREE.LineBasicMaterial({color: '#ffffff', })const lineGeometry = new THREE.BufferGeometry()const pointsArray = new Array()for (let i = 0; i < polygon.length; i++) {let [x, y] = projectpos(polygon[i])if (!isNaN(x)) {veTo(x, -y)}shape.lineTo(x, -y)pointsArray.push(new THREE.Vector3(x, -y, meshHeight))}lineGeometry.setFromPoints(pointsArray)const extrudsSettings = {depth: meshHeight,bevelEnabled: false,// 对挤出的形状应用是否斜角}const geometry = new THREE.ExtrudeGeometry(shape, extrudsSettings)const material = new THREE.MeshPhongMaterial({color: '#4161ff',transparent: true,opacity: 0.7,side: THREE.FrontSide})const material1 = new THREE.MeshLambertMaterial({color: '#cfc5de',transparent: true,opacity: 0.7,side: THREE.FrontSide})const mesh = new THREE.Mesh(geometry, [material, material1])const line = new THREE.Line(lineGeometry, lineMaterial)province.properties = element.properties// 将城市信息方到模型中,后续做动画用if (id) {const [x, y] = projectpos(id)province.properties._centroid = [x, y]}province.add(mesh)province.add(line)})})map.add(province)})scene.add(map)
}
4、initLight方法代码const initLight = () => {const ambientLight = new THREE.AmbientLight(0x404040, 1.2)scene.add(ambientLight)// 平行光const directionalLight = new THREE.DirectionalLight(0xffffff, 1.0)scene.add(directionalLight)// 点光源 - 照模型const test = new THREE.PointLight("#ffffff", 1.8, 20)test.position.set(1, -7, 7)scene.add(test)const testHelperMap = new THREE.PointLightHelper(test)scene.add(testHelperMap)
}
5、setRaycaster方法代码const setRaycaster = () => {rayCaster = new THREE.Raycaster()mouse = new THREE.Vector2() const onMouseMove = (event) => {mouse.x = (event.clientX / document.querySelector('#scene').clientWidth) * 2 - 1mouse.y = - (event.clientY / document.querySelector('#scene').clientHeight) * 2 + 1} // 点击地图事件const onClick = (event) => { if(selectedMesh === lastPick?.object){return}// 恢复上一次清空的 if (lastPick && 'point' in lastPick) {// 单击在Mesh上const mesh = lastPick.objectif(selectedMesh) { selectedMesh === mesh ? (resetMeshHeight(selectedMesh),selectedMesh = null) : (resetMeshHeight(selectedMesh),stretchMesh(mesh),selectedMesh = mesh) } else {// 没有选定的 Mesh,将选定的 Mesh 高度增加 // 伸展 meshstretchMesh(mesh)selectedMesh = mesh } } else { // 单击在 Mesh 区域外if(selectedMesh) { // 重置被选中的 mesh 的高度resetMeshHeight(selectedMesh)selectedMesh = null}// resetCameraTween()}}window.addEventListener('mousemove', onMouseMove, false);window.addEventListener('click', onClick, false);
}
6、initControls代码如下const initControls = () => {orbitControls = new OrbitControls(camera, renderer.domElement)orbitControls.minDistance = 2 //距离屏幕最近的距离orbitControls.maxDistance = 5.5 //距屏幕最远距离// 鼠标左右旋转幅度orbitControls.minAzimuthAngle = -Math.PI / 4orbitControls.maxAzimuthAngle = Math.PI / 4// 鼠标上下转动幅度// 鼠标上下转动幅度orbitControls.minPolarAngle = Math.PI / 2orbitControls.maxPolarAngle = Math.PI - 0.1// 阻尼(惯性ableDamping = trueorbitControls.dampingFactor = 0.04
}
7、initRenderer代码如下const initRenderer = () => {renderer = new THREE.WebGLRenderer({antialias: true,})
8、animate代码如下const animate = () => {requestAnimationFrame(animate)animationMouseover()// cityanimationCityWave()animationCityEdgeLight()animationCityMarker()orbitControls.update()der(scene, der(scene, camera)
}
以上就是根据JSON格式数据创建三维地图的核心代码,所有代码完成后,刷新浏览器,可以看到效果如下:本文发布于:2024-01-28 18:40:16,感谢您对本站的认可!
本文链接:https://www.4u4v.net/it/17064384199457.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
留言与评论(共有 0 条评论) |