ArcGIS api for JS 实现三维飞行漫游功能

说明

基于arcgis api for js 4.17

在arcgis api for js中实现三维飞行,同时视角要跟随飞行方向变化。实现此功能,主要使用Camera对象和goTo方法。

Camera对象主要包含四个属性:fov(视角场,默认55度);heading;tilt和position。其中heading代表偏北角或方位角,当视角朝向正北时为0度,顺时针旋转增加,在0°~360°之间。tilt代表俯仰角,为90度时平行于水平面,0度时垂直俯视,180度时垂直仰视。position代表位置,有三个属性,一般为经度、纬度、高度。其中,要实现视角的变化,需要根据起点和终点的坐标来计算heading和tilt。

View对象的goTo()方法返回一个Promise对象,一个goTo执行一个动作,在前一个动作成功完成后,在.then()中再调用goTo()执行下一个动作。通过这种Promise链式调用的方式实现连续的运动。

代码

代码如下:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title></title>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="https://js.arcgis.com/4.17/esri/themes/light/main.css"/>
    <style>
      html, body {
        width: 100%;
        height: 100%;
        padding: 0;
        margin: 0;
      }
      #viewDiv {
        width: 100%;
        height: 100%;
      }
    </style>
  </head>
  <body>
    <div id="viewDiv"></div>
    <script src="https://js.arcgis.com/4.17/"></script>
    <script>
      require(["esri/Map",
        "esri/views/MapView",
        "esri/views/SceneView",
        "esri/Camera",
        "esri/Graphic",
        "esri/layers/GraphicsLayer"], function (Map, MapView, SceneView, Camera, Graphic, GraphicsLayer) {
          
          let map = new Map({
            basemap: "hybrid",
            ground: "world-elevation"
          })
          // const paths = [
          //   [-0.3, 51.4879, 3000],
          //   [-0.4, 50.4879, 10000],
          //   [-0.178, 49.4879, 1000],
          //   [-0.5, 51.4879, 1000],
          //   [-0.078, 51.4879, 8000],
          //   [-0.178, 52.4879, 5000]
          // ]

          const paths = [
            [120, 31, 3000],
            [119, 31, 13000],
            [119, 31, 40000],
            [119, 32, 10000],
            [121, 32, 1000],
            [119, 33, 8000]
          ]

          let view = new SceneView({
            map: map,
            container: "viewDiv",
            viewingMode: 'global',  // local, global
            camera: {
              position: {
                x: paths[0][0],
                y: paths[0][1],
                z: paths[0][2],
                spatialReference: { wkid: 4326 }
              }
            }
          })

          let gLayer = new GraphicsLayer()
          map.add(gLayer)

          // 创建图形
          function createGeometry(paths) {
            let polyline = {
              type: 'polyline',
              paths: paths
            }
            let polylineGraphic = new Graphic({
              geometry: polyline,
              symbol: {
                type: 'simple-line',
                color: [226, 119, 40],
                width: 4
              }
            })
            gLayer.add(polylineGraphic)
            paths.forEach(item => {
              let point = {
                type: 'point',
                x: item[0],
                y: item[1],
                z: item[2]
              }
              let pointGraphic = new Graphic({
                geometry: point,
                symbol: {
                  type: "simple-marker",
                  color: "blue",
                  size: 12,
                  outline: {
                    width: 1,
                    color: "white"
                  }
                }
              })
              gLayer.add(pointGraphic)
            })
          }

          // 递归函数, 实现连续飞行方法
          // 每次执行两次view.goTo()方法,第一次会将视角转向,第二次转向后会前进到指定位置
          function fly(i){
            if(i+1 == paths.length) {
              return
            }

            let startPoint = paths[i]
            let endPoint = paths[i+1]
            let heading = calcHeading(startPoint[0], startPoint[1], endPoint[0], endPoint[1])
            let tilt = calcTilt(startPoint[0], startPoint[1], startPoint[2], endPoint[0], endPoint[1], endPoint[2])
            console.log('拐弯', i)
            console.log(startPoint, endPoint)
            console.log(heading, tilt)
            let cam = view.camera.clone()
            cam.heading = heading
            cam.tilt = tilt
            cam.position = {
              longitude: startPoint[0],
              latitude: startPoint[1],
              z: startPoint[2],
              spatialReference: { wkid: 4326 }
            }

            view.goTo(cam, {
              speedFactor: 1,
              easing: 'linear'
            })
            .then(() => {
              console.log('前进', i)
              cam.position = {
                longitude: endPoint[0],
                latitude: endPoint[1],
                z: endPoint[2],
                spatialReference: { wkid: 4326 }
              }
              return view.goTo(cam, {
                speedFactor: 0.2,
                easing: "linear"
              })
            })
            .then(() => {
              setTimeout(() => {
                i++
                fly(i)
              }, 1000)
            })
          }

          view.when(() => {
            createGeometry(paths)
            view.watch('camera', e => {
              // console.log(e.tilt, e.heading, e.position.z)
            })
            fly(0)
          })

          // 计算距离
          function calcDistance(lon1, lat1, lon2, lat2) {
            let radlat1 = lat1 * Math.PI / 180.0
            let radlat2 = lat2 * Math.PI / 180.0
            let a = radlat1 - radlat2
            let b = lon1 * Math.PI / 180.0 - lon2 * Math.PI / 180.0
            let distance = 2 * 6378137.0 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2) + Math.cos(radlat2) * Math.cos(radlat2) * Math.pow(Math.sin(b / 2), 2)))
            return distance
          }

          // 计算偏北角,0指向正北,90指向正东,顺时针旋转
          function calcHeading(lon1, lat1, lon2, lat2) {
            let radlat1 = lat1 * Math.PI / 180.0
            let radlat2 = lat2 * Math.PI / 180.0
            let radlon1 = lon1 * Math.PI / 180.0
            let radlon2 = lon2 * Math.PI / 180.0
            let y = Math.sin(radlon2 - radlon1) * Math.cos(radlat2)
            let x = Math.cos(radlat1) * Math.sin(radlat2) - Math.sin(radlat1) * Math.cos(radlat2) * Math.cos(radlon2 - radlon1)
            let bearing = Math.atan2(y, x) * 180.0 / Math.PI
            return bearing < 0 ? bearing + 360.0 : bearing
          }

          // 计算俯仰角, 90度时平行于水平面,0度时自上向下垂直俯视, 180度时自下向上仰视
          function calcTilt(lon1, lat1, alt1, lon2, lat2, alt2) {
            let distance = calcDistance(lon1, lat1, lon2, lat2)
            let angle = Math.atan2((alt2 -alt1), distance) * 180.0 / Math.PI
            let tilt = angle + 90
            return tilt
          }
      })
    </script>
  </body>
</html>
上一篇:获取文件夹下所有文件名


下一篇:2020年第十届蓝桥杯省级考试题目