0


threejs创建3d交互地图

文章目录


前言

基于react-hooks创建的三维地图,只实现了基本的交互展示,可根据个人喜好增加各种交互和展示效果,效果如下。
在这里插入图片描述


关键点

使用threejs创建3d地图注意的组要是以下几点。

  • GeoJson数据规范,尤其是面状Feature的数据结构特点,可参考官网:https://geojson.org/。
  • 地图生成和交互主要是使用THREE.ExtrudeBufferGeometry和THREE.Raycaster()方法。
  • 中国政区GeoJSON数据可从阿里云数据平台下载。将经纬度坐标转移到屏幕上使用d3.js的geoMercator()方法d3官网。

源码

完整代码如下,附注释。

import{ useRef, useEffect, useCallback, useState }from'react'import*asTHREEfrom'three'import*asD3from'd3'import OrbitControls from'three-orbitcontrols';import'./index.scss'constView=()=>{const page =useRef();// useRef不会导致重新渲染/**
   * 场景、相机、渲染器作为threejs的基本结构,需要在页面进入时渲染完毕
   */const scence =useRef(newTHREE.Scene()).current;//场景const camera =useRef(newTHREE.PerspectiveCamera()).current;//摄像机(透视投影)const render =useRef(newTHREE.WebGLRenderer()).current;//渲染器const controls =newOrbitControls(camera, render.domElement);//创建控件对象const timer =useRef(null)// 定义定时器const handleProj =D3.geoMercator().center([109,34.5]).scale(80).translate([0,0])// d3投影转换函数const mapContainer =useRef(newTHREE.Object3D()).current;// 存储地图Object3D对象const lastPickedProvince =useRef(null)// 上次选中的省份useEffect(()=>{
    page.current.appendChild(render.domElement);initScene();initLight();initGeom();renderScene();},[])useEffect(()=>{bindEvent();},[])constbindEvent=()=>{
    window.addEventListener('mousemove',(event)=>{const pointer =newTHREE.Vector2();// 像素坐标=>规范化设备坐标系 [-1,1]
      pointer.x =(event.clientX / window.innerWidth)*2-1;
      pointer.y =-(event.clientY / window.innerHeight)*2+1;// 获取鼠标点击的位置生成射线const raycaster =newTHREE.Raycaster();
      raycaster.setFromCamera(pointer, camera);// 获取射线相交的物体集合// debuggerconst intersects = raycaster.intersectObjects(mapContainer.children,true);if(intersects.length){const pcickedProvice = intersects[0].object;// 选中了新的省份if(lastPickedProvince.current?.properties !== pcickedProvice.properties){// 上次选中的恢复半透明if(lastPickedProvince.current){
            lastPickedProvince.current.material.opacity =0.5}
          pcickedProvice.material.opacity =1;// 新选中的设置为不透明
          lastPickedProvince.current = pcickedProvice;}}else{// 鼠标移开地图,之前选中的省份回复半透明if(lastPickedProvince.current){
          lastPickedProvince.current.material.opacity =0.5}
        lastPickedProvince.current =null;}},false)}// 初始化场景const initScene =useCallback(()=>{
    render.setSize(page.current.offsetWidth, page.current.offsetHeight);// 渲染器设置尺寸// 设置背景颜色
    render.setClearColor(newTHREE.Color(0x000000));// 设置背景颜色和透明度
    render.shadowMap.enabled =true;// 渲染器允许渲染阴影⭐/**
     * 设置摄像机的属性
     */
    camera.aspect =(page.current.offsetWidth / page.current.offsetHeight)// 摄像机设置屏幕宽高比
    camera.fov =45;// 摄像机的视角
    camera.near =0.01;// 近面距离
    camera.far =1001;// 远面距离
    camera.position.set(2,2,200)// 设置摄像机在threejs坐标系中的位置
    camera.lookAt(0,0,0)// 摄像机的指向
    camera.updateProjectionMatrix();// 更新摄像机投影矩阵,在任何参数被改变以后必须被调用},[render, scence])// 初始化环境光constinitLight=()=>{const ambLight =newTHREE.AmbientLight('#ffffff',0.3)// 基本光源/**
     * 设置聚光灯相关的的属性,详情见P54
     */const spotLight =newTHREE.SpotLight(0xFFFFFF);// 聚光灯
    spotLight.position.set(40,200,10);
    spotLight.castShadow =true;// 只有该属性为true时,该点光源允许产生阴影,并且下列属性可用
    scence.add(ambLight, spotLight);// 向场景中添加光源}// 初始化地理数据集constinitGeom=()=>{// 加载中国地区的geoJson数据集const fileLoader =newTHREE.FileLoader();
    fileLoader.load('https://geo.datav.aliyun.com/areas_v3/bound/100000_full.json',(data)=>{const chinaJson =JSON.parse(data)handleData(chinaJson)})}// 处理GeoJson dataconsthandleData=(jsonData)=>{const feaureList = jsonData.features;
    feaureList.forEach((feature)=>{// 每个feature都代表一个省份const province =newTHREE.Object3D;
      province.properties = feature.properties.name // 省份名称
      province.name = feature.properties.name // 省份名称const coordinates = feature.geometry.coordinates // 省份坐标信息if(feature.geometry.type ==='MultiPolygon'){
        coordinates.forEach((coord)=>{
          coord.forEach((coordinate)=>{const extrudeMesh =creatDepthPolygon(coordinate)
            extrudeMesh.properties = feature.properties.name
            province.add(extrudeMesh)})})}if(feature.geometry.type ==='Polygon'){
        coordinates.forEach((coordinate)=>{const extrudeMesh =creatDepthPolygon(coordinate)
          extrudeMesh.properties = feature.properties.name
          province.add(extrudeMesh)})}
      mapContainer.add(province)})
    scence.add(mapContainer)}// 创建三维多边形constcreatDepthPolygon=(coordinate)=>{const shape =newTHREE.Shape();

    coordinate.forEach((item, index)=>{// 每一个item都是MultiPolygon中的一个polygonconst[x_XYZ, y_XYZ]=handleProj(item)if(index ===0){
        shape.moveTo(x_XYZ,-y_XYZ)}else{
        shape.lineTo(x_XYZ,-y_XYZ)}})const geometry =newTHREE.ExtrudeBufferGeometry(shape,{ depth:1, bevelEnabled:false,})const material =newTHREE.MeshBasicMaterial({
      color:newTHREE.Color(Math.random()*0xffffff),// 每个省随机赋色
      transparent:true,
      opacity:0.5})returnnewTHREE.Mesh(geometry, material)}// 渲染器执行渲染const renderScene =useCallback(()=>{
    timer.current = window.requestAnimationFrame(()=>renderScene())
    controls.update();
    render.render(scence, camera);},[render])return(<><div className='page' ref={page}><div style={{ position:'fixed', top:'0', right:'0'}}><button onClick={()=>{console.log(scence)}} style={{ marginRight:'10px'}}>打印场景</button></div></div></>)};exportdefault View

总结

three实现3d交互地图的注意点

  • GeoJson面状Feature的数据结构
  • 地图生成和交互核心方法
  • 原始数据来源
标签: 3d react.js javascript

本文转载自: https://blog.csdn.net/qq_37987033/article/details/127017944
版权归原作者 迦南giser 所有, 如有侵权,请联系我们删除。

“threejs创建3d交互地图”的评论:

还没有评论