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

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

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

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

1. 点高程差查询

1.1 结果显示

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

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

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

  1. require([
  2. "esri/Map",
  3. "esri/views/SceneView",
  4. "esri/Graphic",
  5. "esri/geometry/Polyline",
  6. "esri/layers/ElevationLayer",
  7. "esri/symbols/PointSymbol3D",
  8. "esri/symbols/ObjectSymbol3DLayer",
  9. "esri/symbols/LineSymbol3D",
  10. "esri/symbols/LineSymbol3DLayer",
  11. "dojo/promise/all",
  12. "dojo/domReady!"
  13. ],
  14. function(
  15. Map, SceneView, Graphic, Polyline, ElevationLayer, PointSymbol3D,
  16. ObjectSymbol3DLayer,
  17. LineSymbol3D, LineSymbol3DLayer, all){
  18. ...
  19. }
  20. )

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

1.2 骨架

  1. function(...){
  2. var beforeLandslideUrl = "http://sampleserver6.arcgisonline.com/arcgis/rest/services/OsoLandslide/OsoLandslide_Before_3DTerrain/ImageServer/";
  3. var afterLandslideUrl = "http://sampleserver6.arcgisonline.com/arcgis/rest/services/OsoLandslide/OsoLandslide_After_3DTerrain/ImageServer/";
  4. var beforeLandslideLayer = new ElevationLayer({url: beforeLandslideUrl});
  5. var afterLandslideLayer = new ElevationLayer({url: afterLandslideUrl});
  6.  
  7. var map = new Map({... , ground:{layers:[beforeLandslideLayer, afterLandslideLayer]}});
  8. var view = new View({...});
  9.  
  10. var afterPointSymbol = new PointSymbol3D({...});
  11. var beforePointSymbol = new PointSymbol3d({...});
  12. var lineSymbol = new LineSymbol3D({...});
  13.  
  14. var resultsContainer = document.getElementById("resultsDiv");
  15.  
  16. view.on("click", function(event){...});
  17. document.getElementById("elevAfter").addEventListener("change", function(evt){...});
  18. }

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

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

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

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

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

1.3.1 click事件

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

  1. view.on("click",function(event){
  2. resultsContainer.innerHTML = "Query elevation ...";
  3.  
  4. var position = event.mapPoint;
  5. var queryBeforeLandslide = beforeLandslideLayer.queryElevation(position);
  6. var queryAfterLandslide = afterLandslideLayer.queryElevation(position);
  7.  
  8. all([queryBeforeLandslide,queryAfterLandslide])
  9. .then(function(results){...})
  10. otherwise(function(error){...});
  11. }
  12. );

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

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

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

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

  1. .then(function(results) {
  2. var posBeforeLandslide = results[0].geometry;
  3. var posAfterLandslide = results[1].geometry;
  4.  
  5. view.graphics.removeAll();
  6.  
  7. view.graphics.add(new Graphic({
  8. geometry: posBeforeLandslide,
  9. symbol: beforePointSymbol
  10. }));
  11.  
  12. view.graphics.add(new Graphic({
  13. geometry: posAfterLandslide,
  14. symbol: afterPointSymbol
  15. }));
  16.  
  17. var lineGeometry = new Polyline({
  18. spatialReference: posBeforeLandslide.spatialReference
  19. });
  20. lineGeometry.addPath([posBeforeLandslide,
  21. posAfterLandslide
  22. ]);
  23. view.graphics.add(new Graphic({
  24. geometry: lineGeometry,
  25. symbol: lineSymbol
  26. }));
  27.  
  28. var elevationDifference = Math.abs(posBeforeLandslide.z -
  29. posAfterLandslide.z);
  30. resultsContainer.innerHTML = "Elevation difference: " +
  31. elevationDifference.toFixed(2) + " m";
  32. })

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

清除view的图形信息。

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

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

1.3.2 复选框的change监听事件

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

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

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

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

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="utf-8">
  5. <meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no">
  6. <title>Query Elevation (Points) - 4.2</title>
  7. <style>
  8. html,
  9. body,
  10. #viewDiv {
  11. padding: 0;
  12. margin: 0;
  13. height: 100%;
  14. width: 100%;
  15. }
  16.  
  17. #paneDiv {
  18. position: absolute;
  19. top: 12px;
  20. left: 62px;
  21. width: 80%;
  22. padding: 0 12px 0 12px;
  23. background-color: rgba(255, 255, 255, 0.85);
  24. border: 1px solid white;
  25. color: black;
  26. }
  27.  
  28. #resultsDiv {
  29. font-size: 1.2em;
  30. text-align: center;
  31. border-bottom: 1px solid gray;
  32. padding: 10px 0px;
  33. }
  34.  
  35. #activeElevationLayerDiv {
  36. margin: 10px 0;
  37. }
  38.  
  39. ul #red {
  40. color: rgb(150, 26, 15);
  41. }
  42.  
  43. ul #green {
  44. color: rgb(21, 150, 15);
  45. }
  46.  
  47. ul span {
  48. color: black;
  49. }
  50.  
  51. ul {
  52. margin: 0 0 10px 0;
  53. }
  54. </style>
  55.  
  56. <link rel="stylesheet" href="https://js.arcgis.com/4.2/esri/css/main.css">
  57. <script src="https://js.arcgis.com/4.2/"></script>
  58.  
  59. <script>
  60. require([
  61. "esri/Map",
  62. "esri/views/SceneView",
  63. "esri/Graphic",
  64. "esri/geometry/Polyline",
  65. "esri/layers/ElevationLayer",
  66. "esri/symbols/PointSymbol3D",
  67. "esri/symbols/ObjectSymbol3DLayer",
  68. "esri/symbols/LineSymbol3D",
  69. "esri/symbols/LineSymbol3DLayer",
  70. "dojo/promise/all",
  71. "dojo/domReady!"
  72. ], function(
  73. Map, SceneView, Graphic, Polyline, ElevationLayer, PointSymbol3D,
  74. ObjectSymbol3DLayer,
  75. LineSymbol3D, LineSymbol3DLayer, all
  76. ) {
  77.  
  78. // Create elevation layers
  79. var beforeLandslideUrl =
  80. "http://sampleserver6.arcgisonline.com/arcgis/rest/services/OsoLandslide/OsoLandslide_Before_3DTerrain/ImageServer/";
  81. var afterLandslideUrl =
  82. "http://sampleserver6.arcgisonline.com/arcgis/rest/services/OsoLandslide/OsoLandslide_After_3DTerrain/ImageServer/";
  83.  
  84. var beforeLandslideLayer = new ElevationLayer({
  85. url: beforeLandslideUrl
  86. });
  87. var afterLandslideLayer = new ElevationLayer({
  88. url: afterLandslideUrl
  89. });
  90.  
  91. // Create Map and View
  92. var map = new Map({
  93. basemap: "satellite",
  94. ground: {
  95. layers: [beforeLandslideLayer, afterLandslideLayer]
  96. }
  97. });
  98.  
  99. var view = new SceneView({
  100. container: "viewDiv",
  101. map: map,
  102. camera: {
  103. // initial view:
  104. heading: 332.8,
  105. tilt: 65.5,
  106. position: {
  107. x: -13563643,
  108. y: 6153016,
  109. z: 577,
  110. spatialReference: {
  111. wkid: 3857
  112. }
  113. }
  114. }
  115. });
  116.  
  117. // Initialize symbols
  118. var afterPointSymbol = new PointSymbol3D({
  119. symbolLayers: [new ObjectSymbol3DLayer({
  120. material: {
  121. color: [150, 26, 15]
  122. },
  123. resources: {
  124. primitive: "sphere"
  125. },
  126. width: 8
  127. })]
  128. });
  129.  
  130. var beforePointSymbol = new PointSymbol3D({
  131. symbolLayers: [new ObjectSymbol3DLayer({
  132. material: {
  133. color: [21, 150, 15]
  134. },
  135. resources: {
  136. primitive: "sphere"
  137. },
  138. width: 8
  139. })]
  140. });
  141.  
  142. var lineSymbol = new LineSymbol3D({
  143. symbolLayers: [new LineSymbol3DLayer({
  144. material: {
  145. color: [150, 26, 15]
  146. },
  147. size: 1.5
  148. })]
  149. });
  150.  
  151. var resultsContainer = document.getElementById("resultsDiv");
  152.  
  153. view.on("click", function(event) {
  154. resultsContainer.innerHTML = "Querying elevation...";
  155.  
  156. // Query both elevation layers for the elevation at the clicked map position
  157. var position = event.mapPoint;
  158. var queryBeforeLandslide = beforeLandslideLayer.queryElevation(
  159. position);
  160. var queryAfterLandslide = afterLandslideLayer.queryElevation(
  161. position);
  162.  
  163. // When both query promises resolve execute the following code
  164. all([queryBeforeLandslide, queryAfterLandslide])
  165. .then(function(results) {
  166. var posBeforeLandslide = results[0].geometry;
  167. var posAfterLandslide = results[1].geometry;
  168.  
  169. // Clear graphics from previous result (if applicable)
  170. view.graphics.removeAll();
  171.  
  172. // Draw a point graphic for position before landslide
  173. view.graphics.add(new Graphic({
  174. geometry: posBeforeLandslide,
  175. symbol: beforePointSymbol
  176. }));
  177.  
  178. // Draw a point graphic for position after landslide
  179. view.graphics.add(new Graphic({
  180. geometry: posAfterLandslide,
  181. symbol: afterPointSymbol
  182. }));
  183.  
  184. // Draw a vertical line that illustrates the elevation difference
  185. var lineGeometry = new Polyline({
  186. spatialReference: posBeforeLandslide.spatialReference
  187. });
  188. lineGeometry.addPath([posBeforeLandslide,
  189. posAfterLandslide
  190. ]);
  191. view.graphics.add(new Graphic({
  192. geometry: lineGeometry,
  193. symbol: lineSymbol
  194. }));
  195.  
  196. // Compute and display the difference in elevation
  197. var elevationDifference = Math.abs(posBeforeLandslide.z -
  198. posAfterLandslide.z);
  199. resultsContainer.innerHTML = "Elevation difference: " +
  200. elevationDifference.toFixed(2) + " m";
  201. })
  202. .otherwise(function(error) {
  203. resultsContainer.innerHTML = "Elevation query failed (" +
  204. error.message + ")";
  205. });
  206. });
  207.  
  208. // When both elevation layers are set "visible", the surface is defined by the latter layer (afterLandslideLayer).
  209. // Thus we can toggle between "before" and "after" by toggling the visibility of afterLandslideLayer.
  210. document.getElementById("elevAfter").addEventListener("change",
  211. function(evt) {
  212. afterLandslideLayer.visible = evt.target.checked;
  213. beforeOrAfter = evt.target.checked ? "after" : "before";
  214. });
  215. });
  216. </script>
  217. </head>
  218. <body>
  219. <div id="viewDiv"></div>
  220. <div id="paneDiv">
  221. <div id="resultsDiv">Click on the map to see the difference in elevation before and after the landslide.</div>
  222. <div id="activeElevationLayerDiv">
  223. Legend:
  224. <ul>
  225. <li id="green"><span>Surface point before landslide</span></li>
  226. <li id="red"><span>Surface point after landslide</span></li>
  227. </ul>
  228. <input type="checkbox" id="elevAfter" checked><label for="elevAfter">Show surface after landslide</label>
  229. </div>
  230. </div>
  231. </body>
  232. </html>

Query Elevation (Points)

2. 线路高程查询

2.1 先看看结果

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

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

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

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

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

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

先看简单的addStop()方法:

  1. function addStop(event) {
  2. if (!event.mapPoint) {
  3. return;
  4. }
  5.  
  6. var stop = new Graphic({
  7. geometry: event.mapPoint,
  8. symbol: markerSymbol
  9. });
  10. routeLayer.add(stop);
  11.  
  12. routeParams.stops.features.push(stop);
  13. if (routeParams.stops.features.length >= 2) {
  14. routeTask.solve(routeParams)
  15. .then(onRouteUpdated)
  16. .otherwise(function(err) {
  17. routeLayer.remove(stop);
  18. routeParams.stops.features.pop();
  19. console.error(err);
  20. });
  21. }
  22. }

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

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

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

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

  1. function onRouteUpdated(data) {
  2. var route = data.routeResults[0].route
  3. var geometry = route.geometry;
  4.  
  5. var elevationPromise = map.ground.queryElevation(geometry);
  6.  
  7. elevationPromise.then(function(result) {
  8. var path = result.geometry.paths[0];
  9. var ascent = 0;
  10. var descent = 0;
  11.  
  12. for (var i = 1; i < path.length; i++) {
  13. var d = path[i][2] - path[i - 1][2];
  14. if (d > 0) {
  15. ascent += d;
  16. }
  17. else {
  18. descent -= d;
  19. }
  20. }
  21.  
  22. document.getElementById("distanceDiv").innerHTML =
  23. "<p>total distance: " + Math.round(route.attributes.Total_Kilometers *
  24. 1000) / 1000 + " km</p>";
  25. document.getElementById("ascDiv").innerHTML =
  26. "<p>total ascent: " + Math.round(ascent * 100) / 100 +
  27. " m</p>";
  28. document.getElementById("descDiv").innerHTML =
  29. "<p>total descent: " + Math.round(descent * 100) / 100 +
  30. " m</p>";
  31.  
  32. routeLayer.add(new Graphic({
  33. geometry: result.geometry,
  34. symbol: pathSymbol
  35. }));
  36.  
  37. }, function(error) {
  38. console.error(error);
  39. })
  40. }

onRouteUpdate()

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

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

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

  1. elevationPromise.then(function(result) {
  2. var path = result.geometry.paths[0];
  3. var ascent = 0;
  4. var descent = 0;
  5.  
  6. for (var i = 1; i < path.length; i++) {...}
  7.  
  8. document.getElementById("distanceDiv").innerHTML = ... ;
  9. document.getElementById("ascDiv").innerHTML = ... ;
  10. document.getElementById("descDiv").innerHTML = ... ;
  11.  
  12. routeLayer.add(new Graphic({
  13. geometry: result.geometry,
  14. symbol: pathSymbol
  15. }));
  16. },
  17. function(error) {
  18. console.error(error);
  19. }
  20. )

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

3. 总结

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

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


AJS4.2 基础部分学习结语

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

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

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

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

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

ArcGIS API for JavaScript 4.2学习笔记[30] 点和线高程查询(第八章完结)的更多相关文章

  1. ArcGIS API for JavaScript 4.2学习笔记[20] 使用参数查询要素(油井和地震关系)

    这个例子相当复杂.我先简单说说这个例子是干啥的. 在UI上,提供了一个下拉框.两个滑动杆,以确定三个参数,使用这三个参数进行空间查询.这个例子就颇带空间查询的意思了. 这个例子是干嘛的呢?第一个参数是 ...

  2. ArcGIS API for JavaScript 4.2学习笔记[0] AJS4.2概述、新特性、未来产品线计划与AJS笔记目录

    放着好好的成熟的AJS 3.19不学,为什么要去碰乳臭未干的AJS 4.2? 4.2全线基础学习请点击[直达] 4.3及更高版本的补充学习请关注我的博客. ArcGIS API for JavaScr ...

  3. ArcGIS API for JavaScript 4.2学习笔记[1] 显示地图

    ArcGIS API for JavaScript 4.2直接从官网的Sample中学习,API Reference也是从官网翻译理解过来,鉴于网上截稿前还没有人发布过4.2的学习笔记,我就试试吧. ...

  4. ArcGIS API for JavaScript 4.2学习笔记[5] 官方API大章节概述与内容转译

    内容如上,截图自ESRI官网,连接:ArcGIS API for JavaScript 4.2 [Get Started] 类似于绪论一样的东西,抽取了最需要关注的几个例子.如:加载Map和View, ...

  5. ArcGIS API for JavaScript 4.2学习笔记[21] 对3D场景上的3D要素进行点击查询【Query类学习】

    有人问我怎么这个系列没有写自己做的东西呢? 大哥大姐,这是"学习笔记"啊!当然主要以解读和笔记为主咯. 也有人找我要实例代码(不是示例),我表示AJS尚未成熟,现在数据编辑功能才简 ...

  6. ArcGIS API for JavaScript 4.2学习笔记[25] 官方第八章Analysis(空间查询)概览与解释

    开森,最关注的空间分析章节终于到了,在空间查询那节逻辑性的代码简直要命(呵呵,空间分析的代码也要命...). 上目录截图: [Geodesic buffers(GeometryEngine)] 使用G ...

  7. ArcGIS API for JavaScript 4.2学习笔记[16] 弹窗自定义功能按钮及为要素自定义按钮(第五章完结)

    这节对Popups这一章的最后两个例子进行介绍和解析. 第一个[Popup Actions]介绍了弹窗中如何自定义工具按钮(名为actions),以PopupTemplate+FeatureLayer ...

  8. ArcGIS API for JavaScript 4.2学习笔记[7] 鹰眼(缩略图的实现及异步处理、Promise、回调函数、监听的笔记)

    文前说明:关于style就是页面的css暂时不做评论,因为官方给的例子的样式实在太简单了,照抄阅读即可. 这篇文章有着大量AJS 4.x版本添加的内容,如监听watch.Promise对象.回调函数. ...

  9. ArcGIS API for JavaScript 4.2学习笔记[19] 搜索小部件——使用更多数据源

    上一篇中提到,空间搜索小部件是Search这个类的实例化,作为视图的ui属性添加进去后,视图就会出现搜索框了. 这节的主体代码和上篇几乎一致,区别就在上篇提及的sources属性. 先看看结果: 由于 ...

随机推荐

  1. Linux下自动备份MySQL

    使用expect和mysqldump备份 expect expect是一个免费的编程工具语言,用来实现自动和交互式任务进行通信,而无需人的干预. 例如,执行shell脚本的过程中,需要输入用户名.密码 ...

  2. [Hadoop] - Protocol Buffer安装

    Hadoop从2.x版本开始,底层的RPC远程调用使用ProtocolBuffer格式来传递数据,所以在编译Hadoop的过程中有可能出现提示缺少Protocol服务的异常信息,类似:'protoc ...

  3. Error: Cannot find module 'gulp-clone'问题的解决

    安装完gulp环境,并且配置好gulpfile.js,执行静态文件压缩和代码混淆时,出现如下错误: Error: Cannot find module 'gulp-clone' Error: Cann ...

  4. webpack(四)处理 css\less\sass 样式

    (一) 处理普通的.css 文件,需要安装 css-loader,style-loader .less 文件,需要安装 less-loader .sass 文件,需安装  less-loader np ...

  5. 利用CSS3 animation绘制动态卡通人物,无需使用JS代码

    此外博主原创,转载请注明出处:谢谢~ 效果图: 其中云.风车.尾巴是动态的: 以下是代码: <!DOCTYPE html> <html lang="en"> ...

  6. session 与 cookie的区别用法

    //设置cookie方法 setcookie("name",'zhangsan'); setcookie("name",'zhangsan',time()+60 ...

  7. H5移动端开发入门知识以及CSS的单位汇总与用法

    说到css的单位,大家应该首先想到的是px,也就是像素,我们在网页布局中一般都是用px,但是近年来自适应网页布局越来越多,em和百分比也经常用到了.然后随着手机的流行,web app和hybrid a ...

  8. maven lean install 的时候出错 Failed to clean project

    问题解决1 : 这种情况是属于  本地有多个  java  线程,关掉其中不用的,或者 都关闭就可以了. 问题解决 2 : Caused by: org.springframework.beans.f ...

  9. Visual Studio 2017离线安装包,百度云分流

    Visual Studio正式版发布了,然而只能在线安装.虽然官方有提供了离线的方法,但还是蛮复杂的,所以我打包了两个版本发布至百度云分享. 离线分流 地址:http://pan.baidu.com/ ...

  10. 如何更新 OpenStack 组件?- 每天5分钟玩转 OpenStack(161)

    这是 OpenStack 实施经验分享系列的第 11 篇. 本节教大家更新 OpenStack 组件的方法.请注意,是更新(Update)而不是升级(Upgrade).更新是给组件打补丁,版本不变:而 ...