Skip to content

Fence

Example

Example Source Code
tsx
import React, { useCallback, useEffect, useRef, useState } from 'react'
import ReactDOM from 'react-dom'
import * as THREE from 'three'
import { MbMap, MbTiandituLayer } from '@mapbox-react/core'

const App = () => {
  const [mapCenter] = useState([116.390799, 39.915876])
  const [zoom, setZoom] = useState(13)
  const [pitch, setPitch] = useState(45)
  const mapInst = useRef<any>()

  let map: any
  const altitude = 0

  const mapCreated = (mapbox: any) => {
    map = mapbox
  }
  const add3DLayer = () => {
    loaderModels()
  }

  const loaderModels = () => {
    const boundary1 = [
      [116.385394, 39.92098],
      [116.38591, 39.911632],
      [116.389942, 39.911565],
      [116.390457, 39.908734],
      [116.392174, 39.908733],
      [116.39226, 39.911959],
      [116.396294, 39.911894],
      [116.395691, 39.921438],
      [116.385394, 39.92098],
    ]

    addThreeLayer(boundary1)
  }
  const addThreeLayer = (boundary1: number[][]) => {
    const threeLayer = new map.mapboxgl.supermap.ThreeLayer('threeLayer')
    threeLayer.on('initialized', render)
    let light: THREE.PointLight

    function render(this: any) {
      const renderer = threeLayer.getThreeRenderer(),
        scene = threeLayer.getScene(),
        camera = threeLayer.getCamera()

      light = new THREE.PointLight(0xffffff, 0.8)
      light.position.copy(camera.position)
      scene.add(light)
      scene.add(new THREE.AmbientLight(0xffffff))

      const centerBoundary = this.getCoordinatesCenter(boundary1)
      const centerPoint = this.lngLatToPosition(centerBoundary)
      const outer = boundary1.map((coords) =>
        this.lngLatToPosition({
          lng: coords[0],
          lat: coords[1],
        }).sub(centerPoint)
      )

      const polygonMesh = addFencing(outer.map((p) => [p.x, 0, p.y]))
      polygonMesh.rotation.x = Math.PI / 2
      polygonMesh.rotation.y = Math.PI * 2.98

      threeLayer.setPosition(polygonMesh, [116.390714, 39.916535])

      scene.add(polygonMesh)
      renderer.render(scene, camera)
    }

    threeLayer.on('render', () => {
      light && light.position.copy(threeLayer.renderer.camera.position)
    })
    map.addLayer(threeLayer)
  }
  const addFencing = (points: number[][]) => {
    const height = 1500 // 高度 height
    // 围栏距离 fence distance
    const pointDistance: number[] = []
    const distance = points.reduce((totalDistance, point, index) => {
      let segmentDistance = 0
      if (index > 0) {
        const lastPoint = new THREE.Vector3(...points[index - 1])
        const currPoint = new THREE.Vector3(...point)
        segmentDistance = lastPoint.distanceTo(currPoint)
      }
      totalDistance += segmentDistance
      pointDistance.push(totalDistance)
      return totalDistance
    }, 0)

    const geometry = new THREE.BufferGeometry()
    const posArr: number[] = []
    const uvArr: number[] = []

    points.forEach((point, index) => {
      if (index == 0) return
      const lastPoint = points[index - 1]

      // 三角面1 triangle1
      posArr.push(...lastPoint)
      uvArr.push(pointDistance[index - 1] / distance, 0)
      posArr.push(...point)
      uvArr.push(pointDistance[index] / distance, 0)
      posArr.push(lastPoint[0], lastPoint[1] + height, lastPoint[2])
      uvArr.push(pointDistance[index - 1] / distance, 1)

      // 三角面2 triangle2
      posArr.push(...point)
      uvArr.push(pointDistance[index] / distance, 0)
      posArr.push(point[0], point[1] + height, point[2])
      uvArr.push(pointDistance[index] / distance, 1)
      posArr.push(lastPoint[0], lastPoint[1] + height, lastPoint[2])
      uvArr.push(pointDistance[index - 1] / distance, 1)
    })
    geometry.setAttribute(
      'position',
      new THREE.BufferAttribute(new Float32Array(posArr), 3)
    )
    geometry.setAttribute(
      'uv',
      new THREE.BufferAttribute(new Float32Array(uvArr), 2)
    )
    geometry.computeVertexNormals()

    const texture = new THREE.TextureLoader().load(
      `https://mapbox-web.github.io/mapbox-react/images/wall.png`
    )
    texture.wrapS = THREE.RepeatWrapping
    texture.wrapT = THREE.RepeatWrapping

    const material = new THREE.MeshBasicMaterial({
      color: 0x00ff00,
      map: texture,
      transparent: true,
      opacity: 0.9,
      depthWrite: false,
      side: THREE.DoubleSide,
    })

    const mesh = new THREE.Mesh(geometry, material)
    return mesh
  }

  return (
    <div className="map-wrapper">
      <MbMap
        ref={mapInst}
        center={mapCenter}
        zoom={zoom}
        pitch={pitch}
        onCreated={mapCreated}
      >
        <MbTiandituLayer types={['vec', 'cva']} onCreated={add3DLayer} />
      </MbMap>
    </div>
  )
}

ReactDOM.render(<App />, document.querySelector('#root'))