three.js通过GEO数据生成3D地图

GeoJSON是一种对地理数据结构进行编码的格式

地图下载 https://datav.aliyun.com/portal/school/atlas/area_selector

生成3D地图

在这里插入图片描述

<script setup>
import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import * as d3 from 'd3';

// 场景
const scene = new THREE.Scene();

// 相机
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(0, 0, 10);

// 辅助线
const axesHelper = new THREE.AxesHelper(5); // 长度
scene.add(axesHelper);

// 初始化渲染器
const renderer = new THREE.WebGLRenderer({
  antialias: true // 锯齿模糊
});
// renderer.setClearColor(0x444444, 1); // 设置背景颜色
renderer.setSize(window.innerWidth, window.innerHeight); //设置three.js渲染区域的尺寸(像素px)
document.body.appendChild(renderer.domElement);
// renderer.render(scene, camera);

// 相机围绕目标进行轨道运动; 注意OrbitControls会影响 camera.lookAt(x,y,z)失效,需设置controls.target.set(x,y,z)才能生效
const controls = new OrbitControls(camera, renderer.domElement);
// 可设置控制器阻尼,让控制器更有真实效果,必须在你的动画循环里调用.update()
controls.enableDamping = true

// 浏览器动画
const clock = new THREE.Clock()
function render(time) {

  controls.update() // 阻尼控制器更新
  renderer.render(scene, camera);
  requestAnimationFrame(render); // 请求动画帧
}
render()

const loader = new THREE.FileLoader(); // 加载文件
loader.load('./100000_full.json', (data) => {
  const jsonData = JSON.parse(data); // 解析json文件
  operationData(jsonData)
})

const map = new THREE.Object3D()
const operationData = (json) => {
  // console.log(json);
  const features = json.features; // 数据
  features.forEach((feature) => {
    // 创建省份的物体
    const province = new THREE.Object3D()
    province.name = feature.properties.name
    // 获取经纬度坐标
    const coordinates = feature.geometry.coordinates
    // console.log(feature.geometry);
    if (feature.geometry.type === 'Polygon') { // 多边形
      coordinates.forEach((item) => {
        const mesh = createMesh(item) // 创建多边形
        mesh.name = feature.properties.name
        province.add(mesh) // 添加到省份
      })
    } else if (feature.geometry.type === 'MultiPolygon') { // 多多边形
      coordinates.forEach((item) => {
        item.forEach((item) => {
          const mesh = createMesh(item) // 创建多边形
          mesh.name = feature.properties.name
          province.add(mesh) // 添加到省份
        })
      })
    }
    map.add(province) // 添加到地图
  })
  // console.log(map);
  scene.add(map)
}

const projection = d3.geoMercator().center([116.4074, 39.9042]).translate([0, 0, 0]) // 以北京为中心,转换后的坐标(本身是一个球, 转换为平面)
const createMesh = (polygon) => {
  // console.log(polygon);
  // 根据经纬度创建平面
  const shape = new THREE.Shape();
  polygon.forEach((row, i) => {
    // console.log(row, projection(row));
    const [longitude, latitude] = projection(row) // 转换后的坐标
    if (i === 0) {
      shape.moveTo(longitude, -latitude) // 移动到第一个点
    } else {
      shape.lineTo(longitude, -latitude) // 连接到第二个点
    }
  })

  // 根据形状挤出几何体
  const geometry = new THREE.ExtrudeGeometry(shape, {
    depth: 5, // 挤出的深度
  })
  const color = new THREE.Color(Math.random() * 0xffffff) // 随机颜色
  const material = new THREE.MeshBasicMaterial({
    color, // 颜色
    transparent: true, // 透明
    opacity: 0.5, // 透明度
  })
  return new THREE.Mesh(geometry, material)
}

// 监听画面变化,更新渲染画面
window.addEventListener('resize', () => {
  // 更新摄像头
  camera.aspect = window.innerWidth / window.innerHeight;
  // 更新摄像机的投影矩阵
  camera.updateProjectionMatrix();
  // 更新渲染器
  renderer.setSize(window.innerWidth, window.innerHeight);
  // 设置渲染器的像素比
  renderer.setPixelRatio(window.devicePixelRatio)
})
</script>

选中高亮效果

let lastPicker = null; // 上一次选中的物体
window.addEventListener('click', (e) => {
  // 获取鼠标的位置
  const mouse = new THREE.Vector2();
  mouse.x = (e.clientX / window.innerWidth) * 2 - 1; // 横坐标
  mouse.y = -(e.clientY / window.innerHeight) * 2 + 1; // 纵坐标
  // 获取鼠标点击的位置
  const raycaster = new THREE.Raycaster(); // 射线
  raycaster.setFromCamera(mouse, camera); // 设置射线
  const intersects = raycaster.intersectObjects(map.children); // 获取射线与物体的交点
  if (intersects.length > 0) {
    // console.log(intersects[0].object.name); // 输出交点的物体名称
    if (lastPicker) { // 上一次选中的物体
      lastPicker.material.color.copy(lastPicker.material.oldColor) // 恢复原来的颜色
    }
    lastPicker = intersects[0].object; // 上一次选中的物体
    lastPicker.material.oldColor = lastPicker.material.color.clone() // 保存原来的颜色
    lastPicker.material.color.set(0xffffff) // 选中的物体颜色(此时设置的白色)
  } else {
    if (lastPicker) { // 上一次选中的物体
      lastPicker.material.color.copy(lastPicker.material.oldColor) // 恢复原来的颜色
    }
  }
})

生成地图3D线

在这里插入图片描述

// 根据经纬度画线
const createLine = (polygon) => {
  const lineGeometry = new THREE.BufferGeometry(); // 缓冲区
  const pointsArray = []; // 点
  polygon.forEach((row) => {
    const [longitude, latitude] = projection(row) // 转换后的坐标
    pointsArray.push(new THREE.Vector3(longitude, -latitude, 10)); // 点
  })
  lineGeometry.setFromPoints(pointsArray); // 设置点
  const color = new THREE.Color(Math.random() * 0xffffff) // 随机颜色 
  // 线
  const lineMaterial = new THREE.LineBasicMaterial({
    color, // 颜色
  }); // 材质
  return new THREE.Line(lineGeometry, lineMaterial); // 线
}

这里需要改

    if (feature.geometry.type === 'Polygon') { // 多边形
      coordinates.forEach((item) => {
        const mesh = createMesh(item) // 创建多边形
        mesh.name = feature.properties.name
        province.add(mesh) // 添加到省份
        
        const line = createLine(item) // 创建线
        province.add(line) // 添加到省份
      })
    } else if (feature.geometry.type === 'MultiPolygon') { // 多多边形
      coordinates.forEach((item) => {
        item.forEach((item) => {
          const mesh = createMesh(item) // 创建多边形
          mesh.name = feature.properties.name
          province.add(mesh) // 添加到省份
          
          const line = createLine(item) // 创建线
          province.add(line) // 添加到省份
        })
      })
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值