• ArcGIS API for JavaScript 4.2学习笔记[30] 点和线高程查询(第八章完结)


    终于到最后一篇了,可喜可贺。

    本例先说明了如何进行单点的高程差分析,然后说明了道路的起伏分析。前者很直观地比较了两个年份的高程数据之间的差值,体现山区的高程变化(有啥用啊?)后者,一条路上的起点终点起伏多少,可以给驾驶导航提供更多样化的数据。

    本例使用了高程图层和RouteTask。

    本例对应的官方例子是:Query Elevation (Points)Query Elevation (Lines)

    1. 点高程差查询

    1.1 结果显示

    选了一个明显的点,绿色的是地形变化前的高程点,红色的球是当前的高程点,代表地表起伏变化的是红色的线(即高程差)。

    图中随便点击一个点,稍等几秒钟后就会出现高程查询结果。显示的高程图层是当前的高程图层。

    且不管view上方的白色提示区html构成如何(这个比较简单,html代码没什么逻辑性,一看就懂),来看看其引用:

    require([
      "esri/Map",
      "esri/views/SceneView",
      "esri/Graphic",
      "esri/geometry/Polyline",
      "esri/layers/ElevationLayer",
      "esri/symbols/PointSymbol3D",
      "esri/symbols/ObjectSymbol3DLayer",
      "esri/symbols/LineSymbol3D",
      "esri/symbols/LineSymbol3DLayer",
      "dojo/promise/all",
      "dojo/domReady!"
      ], 
       function(
         Map, SceneView, Graphic, Polyline, ElevationLayer, PointSymbol3D,
         ObjectSymbol3DLayer,
         LineSymbol3D, LineSymbol3DLayer, all){
        ...
      }
    )

    用于填充用的符号和符号图层不说,重点是ElevationLayer。

    1.2 骨架

    function(...){
        var beforeLandslideUrl = "http://sampleserver6.arcgisonline.com/arcgis/rest/services/OsoLandslide/OsoLandslide_Before_3DTerrain/ImageServer/";
        var afterLandslideUrl = "http://sampleserver6.arcgisonline.com/arcgis/rest/services/OsoLandslide/OsoLandslide_After_3DTerrain/ImageServer/";
        var beforeLandslideLayer = new ElevationLayer({url: beforeLandslideUrl});
        var afterLandslideLayer = new ElevationLayer({url: afterLandslideUrl});
    
        var map = new Map({... , ground:{layers:[beforeLandslideLayer, afterLandslideLayer]}});
        var view = new View({...});
        
        var afterPointSymbol  = new PointSymbol3D({...});
        var beforePointSymbol = new PointSymbol3d({...});
        var lineSymbol = new LineSymbol3D({...});
    
        var resultsContainer = document.getElementById("resultsDiv");
    
        view.on("click", function(event){...});
        document.getElementById("elevAfter").addEventListener("change", function(evt){...});
    }

    一开头就是两个使用ImageServer的两个高程图层;

    然后,将map的ground属性设置为这两个高程图层;

    设置前后点符号和连接他们的线符号样式;

    主要的是view的click事件,以及是否显示新高程图层(afterLandslideLayer)复选框的change监听事件。

    1.3 所以重点就在两个事件上了

    1.3.1 click事件

    由于click事件比较大,缩写成骨架形式:

    view.on("click",function(event){
        resultsContainer.innerHTML = "Query elevation ...";
        
        var position = event.mapPoint;
        var queryBeforeLandslide = beforeLandslideLayer.queryElevation(position);
        var queryAfterLandslide = afterLandslideLayer.queryElevation(position);
        
        all([queryBeforeLandslide,queryAfterLandslide])
        .then(function(results){...})
        otherwise(function(error){...});
        }
    );

    首先,把DOM面板上的提示信息写为Query elevation ...,然后获取当前点击的点位置信息position(Point类)。

    根据这个Point,使用高程图层的空间查询方法queryElevation(Point),返回一个简单几何体信息。

    利用这两个返回的的“东西”,使用dojo提供的all方法进行异步操作,如果成功则执行回调函数1,否则执行回调函数2。

    otherwise的回调函数比较短,仅仅为DOM元素写入“查询失败”的提示信息。所以重点就在回调函数1:

    .then(function(results) {
      var posBeforeLandslide = results[0].geometry;
      var posAfterLandslide = results[1].geometry;
    
      view.graphics.removeAll();
    
      view.graphics.add(new Graphic({
        geometry: posBeforeLandslide,
        symbol: beforePointSymbol
      }));
    
      view.graphics.add(new Graphic({
        geometry: posAfterLandslide,
        symbol: afterPointSymbol
      }));
    
      var lineGeometry = new Polyline({
        spatialReference: posBeforeLandslide.spatialReference
      });
      lineGeometry.addPath([posBeforeLandslide,
        posAfterLandslide
      ]);
      view.graphics.add(new Graphic({
        geometry: lineGeometry,
        symbol: lineSymbol
      }));
    
      var elevationDifference = Math.abs(posBeforeLandslide.z -
        posAfterLandslide.z);
      resultsContainer.innerHTML = "Elevation difference: " +
        elevationDifference.toFixed(2) + " m";
    })

    从两个返回的“东西”中获得geometry属性,官方还是没写明白results是什么东西...

    清除view的图形信息。

    先添加两个点几何体,然后根据这俩点实例化一条Polyline(使用addPath()方法),把Polyline添加到视图的图形属性中。

    最后刷新DOM面板上的高程查询信息即可。

    1.3.2 复选框的change监听事件

    这个就比较简单了,也挺有趣。它打勾就代表新的高程图层显示。

    document.getElementById("elevAfter").addEventListener("change",
      function(evt) {
        afterLandslideLayer.visible = evt.target.checked;
        beforeOrAfter = evt.target.checked ? "after" : "before";
      });

    仅仅是改动了afterLandslideLayer的visible属性而已。

    最后给出这例的完整HTML代码:

    <!DOCTYPE html>
    <html>
    <head>
      <meta charset="utf-8">
      <meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no">
      <title>Query Elevation (Points) - 4.2</title>
      <style>
        html,
        body,
        #viewDiv {
          padding: 0;
          margin: 0;
          height: 100%;
          width: 100%;
        }
        
        #paneDiv {
          position: absolute;
          top: 12px;
          left: 62px;
          width: 80%;
          padding: 0 12px 0 12px;
          background-color: rgba(255, 255, 255, 0.85);
          border: 1px solid white;
          color: black;
        }
        
        #resultsDiv {
          font-size: 1.2em;
          text-align: center;
          border-bottom: 1px solid gray;
          padding: 10px 0px;
        }
        
        #activeElevationLayerDiv {
          margin: 10px 0;
        }
        
        ul #red {
          color: rgb(150, 26, 15);
        }
        
        ul #green {
          color: rgb(21, 150, 15);
        }
        
        ul span {
          color: black;
        }
        
        ul {
          margin: 0 0 10px 0;
        }
      </style>
    
      <link rel="stylesheet" href="https://js.arcgis.com/4.2/esri/css/main.css">
      <script src="https://js.arcgis.com/4.2/"></script>
    
      <script>
        require([
          "esri/Map",
          "esri/views/SceneView",
          "esri/Graphic",
          "esri/geometry/Polyline",
          "esri/layers/ElevationLayer",
          "esri/symbols/PointSymbol3D",
          "esri/symbols/ObjectSymbol3DLayer",
          "esri/symbols/LineSymbol3D",
          "esri/symbols/LineSymbol3DLayer",
          "dojo/promise/all",
          "dojo/domReady!"
        ], function(
          Map, SceneView, Graphic, Polyline, ElevationLayer, PointSymbol3D,
          ObjectSymbol3DLayer,
          LineSymbol3D, LineSymbol3DLayer, all
        ) {
    
          // Create elevation layers
          var beforeLandslideUrl =
            "http://sampleserver6.arcgisonline.com/arcgis/rest/services/OsoLandslide/OsoLandslide_Before_3DTerrain/ImageServer/";
          var afterLandslideUrl =
            "http://sampleserver6.arcgisonline.com/arcgis/rest/services/OsoLandslide/OsoLandslide_After_3DTerrain/ImageServer/";
    
          var beforeLandslideLayer = new ElevationLayer({
            url: beforeLandslideUrl
          });
          var afterLandslideLayer = new ElevationLayer({
            url: afterLandslideUrl
          });
    
          // Create Map and View
          var map = new Map({
            basemap: "satellite",
            ground: {
              layers: [beforeLandslideLayer, afterLandslideLayer]
            }
          });
    
          var view = new SceneView({
            container: "viewDiv",
            map: map,
            camera: {
              // initial view:
              heading: 332.8,
              tilt: 65.5,
              position: {
                x: -13563643,
                y: 6153016,
                z: 577,
                spatialReference: {
                  wkid: 3857
                }
              }
            }
          });
    
          // Initialize symbols
          var afterPointSymbol = new PointSymbol3D({
            symbolLayers: [new ObjectSymbol3DLayer({
              material: {
                color: [150, 26, 15]
              },
              resources: {
                primitive: "sphere"
              },
               8
            })]
          });
    
          var beforePointSymbol = new PointSymbol3D({
            symbolLayers: [new ObjectSymbol3DLayer({
              material: {
                color: [21, 150, 15]
              },
              resources: {
                primitive: "sphere"
              },
               8
            })]
          });
    
          var lineSymbol = new LineSymbol3D({
            symbolLayers: [new LineSymbol3DLayer({
              material: {
                color: [150, 26, 15]
              },
              size: 1.5
            })]
          });
    
          var resultsContainer = document.getElementById("resultsDiv");
    
          view.on("click", function(event) {
            resultsContainer.innerHTML = "Querying elevation...";
    
            // Query both elevation layers for the elevation at the clicked map position
            var position = event.mapPoint;
            var queryBeforeLandslide = beforeLandslideLayer.queryElevation(
              position);
            var queryAfterLandslide = afterLandslideLayer.queryElevation(
              position);
    
            // When both query promises resolve execute the following code
            all([queryBeforeLandslide, queryAfterLandslide])
              .then(function(results) {
                var posBeforeLandslide = results[0].geometry;
                var posAfterLandslide = results[1].geometry;
    
                // Clear graphics from previous result (if applicable)
                view.graphics.removeAll();
    
                // Draw a point graphic for position before landslide
                view.graphics.add(new Graphic({
                  geometry: posBeforeLandslide,
                  symbol: beforePointSymbol
                }));
    
                // Draw a point graphic for position after landslide
                view.graphics.add(new Graphic({
                  geometry: posAfterLandslide,
                  symbol: afterPointSymbol
                }));
    
                // Draw a vertical line that illustrates the elevation difference
                var lineGeometry = new Polyline({
                  spatialReference: posBeforeLandslide.spatialReference
                });
                lineGeometry.addPath([posBeforeLandslide,
                  posAfterLandslide
                ]);
                view.graphics.add(new Graphic({
                  geometry: lineGeometry,
                  symbol: lineSymbol
                }));
    
                // Compute and display the difference in elevation
                var elevationDifference = Math.abs(posBeforeLandslide.z -
                  posAfterLandslide.z);
                resultsContainer.innerHTML = "Elevation difference: " +
                  elevationDifference.toFixed(2) + " m";
              })
              .otherwise(function(error) {
                resultsContainer.innerHTML = "Elevation query failed (" +
                  error.message + ")";
              });
          });
    
          // When both elevation layers are set "visible", the surface is defined by the latter layer (afterLandslideLayer).
          // Thus we can toggle between "before" and "after" by toggling the visibility of afterLandslideLayer.
          document.getElementById("elevAfter").addEventListener("change",
            function(evt) {
              afterLandslideLayer.visible = evt.target.checked;
              beforeOrAfter = evt.target.checked ? "after" : "before";
            });
        });
      </script>
    </head>
    <body>
      <div id="viewDiv"></div>
      <div id="paneDiv">
        <div id="resultsDiv">Click on the map to see the difference in elevation before and after the landslide.</div>
        <div id="activeElevationLayerDiv">
          Legend:
          <ul>
            <li id="green"><span>Surface point before landslide</span></li>
            <li id="red"><span>Surface point after landslide</span></li>
          </ul>
          <input type="checkbox" id="elevAfter" checked><label for="elevAfter">Show surface after landslide</label>
        </div>
      </div>
    </body>
    </html>
    Query Elevation (Points)

    2. 线路高程查询

    2.1 先看看结果

    点击2个以上的点,生成一条最短路径,途径的路线的总长度、总上升和总下降高程均有显示。total ascent和total descent的差值即为起始点终点的高程差。

    所以这一例是基于最短路径分析的(RouteTask)。

    2.2 重点代码(与RouteTask有别的部分)

    在线和点符号的设置上,和地图和场景的设置上和RouteTask那篇文章有点改动外,几乎是照搬了预设,仅仅对view的click事件进行了修改。重点就放在了:

    on(view, "click", addStop);
    
    function addStop(event){...}
    function onRouteUpdate(data){...}

    这段代码上,前一个方法体是view的click事件,后一个方法体是对查询结果的绘制和路线的更新。

    先看简单的addStop()方法:

    function addStop(event) {
      if (!event.mapPoint) {
        return;
      }
    
      var stop = new Graphic({
        geometry: event.mapPoint,
        symbol: markerSymbol
      });
      routeLayer.add(stop);
    
      routeParams.stops.features.push(stop);
      if (routeParams.stops.features.length >= 2) {
        routeTask.solve(routeParams)
          .then(onRouteUpdated)
          .otherwise(function(err) {
            routeLayer.remove(stop);
            routeParams.stops.features.pop();
            console.error(err);
          });
      }
    }

    先检测点击是否产生了mapPoint,是则继续,实例化一个Graphic对象,添加到routeLayer中。

    然后设置RouteTask必须的RouteParameters参数。如果点击的点数>=2个,执行RouteTask.solve()方法。

    紧接着异步操作链,成功则继续执行onRouteUpdated(),失败则移除刚刚生成的点(从RouteParameters和GraphicsLayer中删除)。

    来看看onRouteUpdate()是如何获取高程信息的:

    function onRouteUpdated(data) {
      var route = data.routeResults[0].route
      var geometry = route.geometry;
    
      var elevationPromise = map.ground.queryElevation(geometry);
    
      elevationPromise.then(function(result) {
        var path = result.geometry.paths[0];
        var ascent = 0;
        var descent = 0;
    
        for (var i = 1; i < path.length; i++) {
          var d = path[i][2] - path[i - 1][2];
          if (d > 0) {
            ascent += d;
          }
          else {
            descent -= d;
          }
        }
    
        document.getElementById("distanceDiv").innerHTML =
          "<p>total distance: " + Math.round(route.attributes.Total_Kilometers *
            1000) / 1000 + " km</p>";
        document.getElementById("ascDiv").innerHTML =
          "<p>total ascent: " + Math.round(ascent * 100) / 100 +
          " m</p>";
        document.getElementById("descDiv").innerHTML =
          "<p>total descent: " + Math.round(descent * 100) / 100 +
          " m</p>";
    
        routeLayer.add(new Graphic({
          geometry: result.geometry,
          symbol: pathSymbol
        }));
    
      }, function(error) {
        console.error(error);
      })
    }
    onRouteUpdate()

    从RouteTask.solve()中获取返回值中的route信息,再从route中获取geometry信息。

    然后利用这个geometry,使用Map对象的ground属性的queryElevation(高程图层的高程查询方法)进行高程查询。

    对高程查询的结果进行异步操作then(),如果异步操作成功则执行如下的回调函数:

    elevationPromise.then(function(result) {
        var path = result.geometry.paths[0];
        var ascent = 0;
        var descent = 0;
    
        for (var i = 1; i < path.length; i++) {...}
        
        document.getElementById("distanceDiv").innerHTML = ... ;
        document.getElementById("ascDiv").innerHTML = ... ;
        document.getElementById("descDiv").innerHTML = ... ;
    
        routeLayer.add(new Graphic({
          geometry: result.geometry,
          symbol: pathSymbol
        }));
      }, 
      function(error) {
        console.error(error);
      }
    )

    获取查询结果中的geometry中的path信息,使用一个for循环统计高程的上下变化,输出到DOM元素上显示,最后在GraphicsLayer中添加这个线要素查询结果。

    3. 总结

    两个高程查询的例子都是基于高程图层的queryElevation()方法的,而最关键的就是获取需要查询的Geometry。前者是通过点击事件,后者则是通过RouteTask的分析结果。

    这一例可以放到第七章的,只不过包装得看起来像空间分析了~


    AJS4.2 基础部分学习结语

    有点拖沓啊。本来一个月能完成的事情非得拖两个月。老师指定要看的章节我都看了,在我实际学习中发现需要加强学习的Layer章节和Graphic章节会在以后慢慢更新的。

    总之感谢一路看过来我的博客的人,国内第一个对AJS完整解读的博文系列终于写完了——第一部分。对于初学者来说,前面30篇博客算是能成功入门了,接下来的学习任务,

    就是对AJS4.2剩余重要章节的补充和学习中遇到的零碎知识总结,以及对AJS4.3及以后更高版本的新特性的学习了。

    博客还会继续更新,欢迎大家继续交流学习。

    本人邮箱:onsummer@foxmail.com,请注明来意。

  • 相关阅读:
    一个简单实现的string类
    Python基础(二)
    Python基础(一)
    区块链初探
    某电商平台开发记要——客服系统
    某电商平台开发记要
    Upload files to aliyunOSS with bootstrap-fileinput
    jquery.validate[.unobtrusive]和Bootstrap实现tooltip错误提示
    PostgreSQL笔记
    天冷了,那些树还好吗?
  • 原文地址:https://www.cnblogs.com/onsummer/p/6682677.html
Copyright © 2020-2023  润新知