Three.js - 可视化地图下钻功能(二十二)

简介

地图下钻听起来是很厉害的技术,当你明白后会发现,实现它非常的简单。

实现思路

  1. 获取GeoJSON格式的全国数据,绘制中国地图场景。
  2. 监听点击事件,判断下钻的省市。
  3. 获取所点击省份GeoJSON格式的数据,绘制省份地图场景。
  4. 切换场景展示省份地图。

绘制中国地图

  • 前面的章节已经绘制过了,这里只需要进行一些小改动。
  • 之前的公用方法中projection()函数是全局方法,当地图下钻后中心点是要修改的,所以使用参数的方式传入。
  • drawExtrudeMesh()方法生成的立体几何添加,唯一标识.properties属性。

鼠标拾取

  • 使用.Raycaster()射线追踪法,监听鼠标事件。
  1. 创建公用方法归一化坐标。
  // 计算 以画布 开始为(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 中归一化坐标
   * */
  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
  }
  • 监听鼠标点击事件。
      // 监听鼠标
      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('yellow')
              lastPick = null
            }
          }
          if (intersects[0].object.properties) {
            intersects[0].object.material.color.set('blue')
          }
          lastPick = intersects[0]
        } else {
          if (lastPick) {
            // 复原
            if (lastPick.object.properties) {
              lastPick.object.material.color.set('yellow')
              lastPick = null
            }
          }
        }
      }

1.gif

绘制省份场景

  • 和绘制地图同样的原理,唯一的区别就是设置重庆市为中心。
  1. 创建新的场景,加载数据在新场景中绘制地图。
  2. 省份地图坐标位置间隔小,使用.scale放大场景。
  // 重庆市为中心
  const projection2 = d3.geoMercator().center([106.557691, 29.559296]).translate([0, 0])
  // 重庆市地图
  loader.load('./file/500000_full.json', (data) => {
    const jsondata = JSON.parse(data)
    operationData2(jsondata)
  })
  // 场景2
  const scene2 = new THREE.Scene()
  const map2 = new THREE.Object3D()
  // 解析数据
  function operationData2(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 = 'yellow'
      // const color = ['重庆市', '上海市'].includes(feature.properties.name) ? 'blue' : 'yellow'

      if (feature.geometry.type === 'MultiPolygon') {
        // 多个,多边形
        coordinates.forEach((coordinate) => {
          // coordinate 多边形数据
          coordinate.forEach((rows) => {
            const mesh = drawExtrudeMesh(rows, color, projection2)
            const line = lineDraw(rows, color, projection2)
            // 唯一标识
            mesh.properties = feature.properties.name

            province.add(line)
            province.add(mesh)
          })
        })
      }

      if (feature.geometry.type === 'Polygon') {
        // 多边形
        coordinates.forEach((coordinate) => {
          const mesh = drawExtrudeMesh(coordinate, color, projection2)
          const line = lineDraw(coordinate, color, projection2)
          // 唯一标识
          mesh.properties = feature.properties.name

          province.add(line)
          province.add(mesh)
        })
      }
      map2.add(province)
    })
    // 放大
    map2.scale.set(8, 8, 1)
    scene2.add(map2)
  }
  • 这里只绘制了重庆市,其他省市的绘制是一样的。
  • 修改渲染函数加载新场景看看效果。
// renderer.render(scene, camera)
renderer.render(scene2, camera)

1.gif

切换场景

  • 基础条件都创建好了,我们只需要修改渲染函数。在函数中判断唯一标识是哪个省市,就切换到展示那个省市。
// 渲染
function render() {
    // 根据唯一标识 判断展示省份
    if (lastPick && lastPick.object.properties === '重庆市') {
      renderer.render(scene2, camera)
    } else {
      renderer.render(scene, camera)
    }

    requestAnimationFrame(render)
}

1.gif

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值