“This is the 22nd day of my participation in the First Challenge 2022. For details: First Challenge 2022”

Introduction to the

Drill-down sounds like a pretty cool technique, but it’s pretty simple to implement once you get the hang of it.

Implementation approach

  1. Obtain national data in GeoJSON format and draw Chinese map scenes.
  2. Monitor click events and judge the provinces and cities of drilling.
  3. Gets the province clickedGeoJSONFormat the data to draw the province map scene.
  4. Switch scenes to show province maps.

Mapping China

  • The previous chapters have already been drawn, so only a few minor changes are needed.
  • In the common method beforeprojection()The function is a global method, and the center point of the map is to be modified after drilling, so it is passed in as a parameter.
  • todrawExtrudeMesh()Method generates solid geometry to add, uniquely identified.propertiesProperties.

The mouse to pick up

  • use.Raycaster()Ray tracing, listening for mouse events.
  1. Create common method normalized coordinates.
  // compute the mouse coordinates starting with the canvas at (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
    }
  }
  /** * Get mouse normalized coordinates in three.js ** /
  function setPickPosition(event) {
    let pickPosition = { x: 0.y: 0 }
    // start with canvas (0,0) after calculation
    const pos = getCanvasRelativePosition(event)
    // Data normalization
    pickPosition.x = (pos.x / canvas.width) * 2 - 1
    pickPosition.y = (pos.y / canvas.height) * -2 + 1
    return pickPosition
  }
Copy the code
  • Listen for mouse click events.
      // Monitor the mouse
      window.addEventListener('click', onRay)
      // Global objects
      let lastPick = null
      function onRay(event) {
        let pickPosition = setPickPosition(event)
        const raycaster = new THREE.Raycaster()
        raycaster.setFromCamera(pickPosition, camera)
        // Calculate the intersection of the object and the ray
        const intersects = raycaster.intersectObjects([map], true)
        // If the array is greater than 0, there are intersecting objects
        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) {
            / / recovery
            if (lastPick.object.properties) {
              lastPick.object.material.color.set('yellow')
              lastPick = null}}}}Copy the code

Draw provincial scenes

  • The only difference is that the city of Chongqing is set as the center.
  1. Create a new scene and load the data to draw the map in the new scene.
  2. Province map coordinate position interval is small, use.scaleZoom in.
  // With Chongqing municipality as the center
  const projection2 = d3.geoMercator().center([106.557691.29.559296]).translate([0.0])
  // Map of Chongqing
  loader.load('./file/500000_full.json'.(data) = > {
    const jsondata = JSON.parse(data)
    operationData2(jsondata)
  })
  / / scenario 2
  const scene2 = new THREE.Scene()
  const map2 = new THREE.Object3D()
  // Parse the data
  function operationData2(jsondata) {
    // National information
    const features = jsondata.features

    features.forEach((feature) = > {
      // A single province object
      const province = new THREE.Object3D()
      / / address
      province.properties = feature.properties.name
      const coordinates = feature.geometry.coordinates
      const color = 'yellow'
      // const color = [' admin ', 'admin ']. Includes (feature.properties. Name)?

      if (feature.geometry.type === 'MultiPolygon') {
        // Multiple, polygon
        coordinates.forEach((coordinate) = > {
          // coordinate polygon data
          coordinate.forEach((rows) = > {
            const mesh = drawExtrudeMesh(rows, color, projection2)
            const line = lineDraw(rows, color, projection2)
            // Unique identifier
            mesh.properties = feature.properties.name

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

      if (feature.geometry.type === 'Polygon') {
        / / polygon
        coordinates.forEach((coordinate) = > {
          const mesh = drawExtrudeMesh(coordinate, color, projection2)
          const line = lineDraw(coordinate, color, projection2)
          // Unique identifier
          mesh.properties = feature.properties.name

          province.add(line)
          province.add(mesh)
        })
      }
      map2.add(province)
    })
    Enlarge / /
    map2.scale.set(8.8.1)
    scene2.add(map2)
  }
Copy the code
  • Only Chongqing is drawn here, the other provinces are drawn the same way.
  • Modify the render function to load the new scene and see what it looks like.
// renderer.render(scene, camera)
renderer.render(scene2, camera)
Copy the code

Switching scenarios

  • With the basics set up, we just need to modify the render function. Determine which province is the unique identifier in the function and switch to show that province.
/ / rendering
function render() {
    // Display the province according to the unique identifier
    if (lastPick && lastPick.object.properties === Chongqing Municipality) {
      renderer.render(scene2, camera)
    } else {
      renderer.render(scene, camera)
    }

    requestAnimationFrame(render)
}
Copy the code

  • The code address