• 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 {
             100%;
            height: 100%;
            padding: 0;
            margin: 0;
          }
          #viewDiv {
             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],
                     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: {
                         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>
    
  • 相关阅读:
    angularJS指令--在各自的控制器里调用不同的函数
    npm install时的一个小问题
    按特定形式生成当前日期的函数
    js判断对象是否是数组的方法
    转正考试的几个考点
    JS 对象转化为数组
    requireJS随笔
    使用bootstrap-select插件,赋初始值
    理解Stream(一)——串行与终止操作
    python requests 库 首次使用
  • 原文地址:https://www.cnblogs.com/ingen42/p/13794187.html
Copyright © 2020-2023  润新知