十一、几何运算
在上一章中,我们研究了地理处理操作及其作为开发人员为您提供的能力,您可以使用熟悉的桌面软件定义自己的地理空间任务,将它们发布为 ArcGIS Server 服务,并在网络地图应用中使用它们。
但是,有一些地理处理操作非常常见。每次应用开发人员需要地理处理任务时,都要为其创建地理处理任务,这将是一种全新的尝试。考虑到这一点,Esri 已经通过几何服务及其更近的客户端对应物几何引擎提供了一些常用的操作。
在本章中,我们将涵盖以下主题:
- 几何服务
- 几何引擎
- 几何引擎练习时间
几何服务
与我们到目前为止看到的所有服务不同,几何服务不依赖于一些底层资源。也就是说,它不是必须首先在 ArcGIS Pro 中创建,然后发布到 ArcGIS Server 的东西。
你可能会说,你是对的,你不必为我们在本书中使用的任何服务做这些。然而,关键是有人在某个地方这样做了。到目前为止,您在创建的网络应用中使用的底图都是从某处的地图文档开始的,并且由于有人将该文档发布到了公开可用的 ArcGIS Server 实例中,因此您可以使用这些底图。您访问的地理处理服务在模型构建器或 Python 脚本中也有类似的构想。
几何服务并非如此。要创建几何服务,您只需让您的 ArcGIS Server 管理员启用它,您就可以开始了!
运行几何服务后,请在服务目录中查看其条目。您将看到它提供了广泛的实用方法,支持常用的几何操作:
几何服务操作
截至 ArcGIS Server 10.3,几何服务支持的完整操作列表如下:
Project
:将一组几何图形投影到新的空间参考中。Simplify
:改变给定的几何图形,使其定义相对于几何图形类型在拓扑上合法。Buffer
:在所提供的几何图形周围的指定距离处创建缓冲多边形。Areas
和Lengths
:计算输入多边形的面积和长度。Lengths
:计算提供的多段线的长度。Label Points
:计算每个指定多边形的内点。您可以使用这些点来标记面特征。Relation
:从属于指定关系的输入几何数组中计算几何对的集合。假设两个阵列在同一空间参考中。在 2D 计算关系,忽略任何 z 坐标值。Densify
:通过绘制现有顶点之间的点来增加几何图形的密度。Distance
:测量几何图形之间的平面或测地线距离。Intersect
:这个操作构造了一组几何图形和另一个几何图形之间的集合论交集。Difference
:构建一组几何图形和另一组几何图形之间的集合论差异。Cut
:分割输入折线或多边形。TrimExtend
:使用用户指定的引导折线修剪或延伸输入折线。修剪特征时,切割线左侧的部分将保留在输出中,其余部分将被丢弃。如果输入多段线未被切割或延伸,则一条空多段线将添加到输出数组中。Offset
:构建输入几何图形的偏移。需要偏移距离。如果指定距离为正,则构建的偏移将位于几何图形的右侧,反之亦然。Generalize
:使用道格拉斯·普克算法概括输入几何图形。Autocomplete
:构建填充现有多边形和一组多段线之间间隙的多边形。Reshape
:使用重塑线重塑折线或多边形的一部分。ConvexHull
:返回输入几何的凸包。输入几何可以是点、多点、折线或多边形。外壳通常是多边形,但也可以是折线或点。FromGeoCoordinateString
:根据用户提供的转换类型和空间参考,将知名字符串数组转换为 x、y 坐标。仅适用于 ArcGIS Server 10.3 或更高版本。ToGeoCoordinateString
:根据提供的转换类型和空间参考,将 x,y 坐标的数组转换为众所周知的字符串。Union
:构建输入数组中几何图形的集合论并集。所有输入必须是相同的几何类型。
查看该列表,您可能会认识到一些您设想经常使用的操作:如Buffer
、Simplify
或Project
等操作。其他的可能看起来有点神秘:例如ConvexHull
,或者Autocomplete
。
事实是,用于 JavaScript 的 ArcGIS 应用编程接口在内部使用其中一些操作来支持自己的任务和小部件,并且为了应用编程接口的方便,这些操作包含在几何服务中。例如,测量和编辑器小部件都需要几何服务才能运行。但是,它们存在的事实意味着您可以在自己的应用中使用它们,而不必创建执行相同操作的地理处理任务。
使用几何服务
上一节列出的每个操作对应于esri/tasks/GeometryService
类上的一个方法。要在应用中使用操作,只需创建GeometryService
的实例,并调用适当的方法。
每个方法在它期望从您那里得到的输入参数以及您期望它返回的数据类型方面都是唯一的。此外,每个方法都需要一个回调函数,当几何服务操作成功完成时,它将执行该回调函数,如果出现问题,它还需要一个错误回调函数。
有关每种方法的详细信息,请查看 https://developers.arcgis.com/javascript/3/jsapi/的原料药文档。
但是,为了让您了解如何在应用中使用几何服务操作,请考虑以下代码提取,它允许用户绘制多边形特征,然后使用simplify()
和labelPoints()
几何服务方法简化多边形,然后创建一个点来锚定标签:
// create and instantiate the map and perform other initialization
...
// create a toolbar for the map
toolbar = new Draw(map);
toolbar.on("draw-end", addToMap);
// activate a drawing tool when a button is clicked
registry.byId("btnDrawPolygon").on("click", function () {
toolbar.activate(Draw.POLYGON);
});
registry.byId("btnDrawFreehand").on("click", function () {
});
registry.byId("btnClear").on("click", function () {
map.graphics.clear();
});
geometryService = new GeometryService("https://utility.arcgisonline.com/ArcGIS/rest/services/Geometry/GeometryServer");
function addToMap(evtObj) {
map.graphics.clear();
var geometry = evtObj.geometry;
// add the drawn graphic to the map
var symbol = new SimpleFillSymbol(
SimpleFillSymbol.STYLE_SOLID,
new SimpleLineSymbol(SimpleLineSymbol.STYLE_SOLID,
new Color([0, 0, 0]), 2),
new Color([0, 0, 255, 0.5]));
var graphic = new Graphic(geometry, symbol);
map.graphics.add(graphic);
// simplify polygon to use it in the get label points request
geometryService.simplify([geometry], getLabelPoints);
}
function getLabelPoints(geometries) {
if (geometries[0].rings.length > 0) {
geometryService.labelPoints(
geometries, function (labelPoints) {
// callback
toolbar.deactivate();
var font = new Font("20px",
Font.STYLE_NORMAL,
Font.VARIANT_NORMAL,
Font.WEIGHT_BOLDER);
array.forEach(labelPoints, function (labelPoint) {
// create a text symbol
var textSymbol = new TextSymbol(
"X: " + number.format(labelPoint.x) +
", Y: " + number.format(labelPoint.y),
font,
new Color([0, 0, 0]));
var labelPointGraphic = new Graphic(
labelPoint, textSymbol);
// add the label point graphic to the map
map.graphics.add(labelPointGraphic);
});
});
} else {
alert("Invalid Polygon - must have at least 3 points");
}
}
另一种你可能会发现有用的几何服务方法是project()
方法。如果您在一个空间参考中有一堆特征想要转换成另一个,可能是为了计算或显示,那么project()
方法将是您最好的朋友。
这包括提供一种称为ProjectParameters
的特殊类型的对象作为project()
的输入。ProjectParameters
对象具有以下属性:
geometries
:包含要投影的几何图形的数组。outSR
:目的地SpatialReference
。transformation
:应用于投影几何的基准变换的已知 ID ( WKID )或已知文本 ( WKT )。此参数是可选的:如果不提供,ArcGIS Server 将使用默认转换,这可能是也可能不是您所需要的。如果指定了变换,则必须设置transformForward
属性。transformForward
:转型的方向。正向是true
,反向是false
。
下面的代码示例指定了一个基准转换,用于将点特征从 NAD 1983 投影到 WGS 1984。NAD 1983 与北美、太平洋(适用于夏威夷、关岛等)和马里亚纳构造板块相连。WGS 1984 与独立于构造板块的国际陆地参考系统 ( ITRF )相连。随着时间的推移,这两个坐标系变得越来越不同,通常需要重新投影才能正确显示:
var transformation = 1188; //NAD_1983_To_WGS_1984_1
require([
"esri/tasks/ProjectParameters", ...
], function(ProjectParameters, ... ) {
var params = new ProjectParameters();
params.geometries = [point];
params.outSR = outSR;
params.transformation = transformation;
params.transformationForward = true;
gsvc.project(params);
...
});
几何引擎
几何引擎是相对较新的应用编程接口,已经在 3.13 版本中引入,并在随后的迭代中得到扩展。然而,就几何运算的性能而言,它完全是一个游戏规则的改变者。
几何引擎实际上是几何服务的客户端实现,因此不需要向服务器发出任何网络请求。这样做的结果是性能好得多。它包括几何服务提供的几乎所有操作,并增加了一些操作。
Esri 有一个很好的示例应用,展示了几何服务和几何引擎之间的性能差异。它使用这两种方法在 500 个城市周围生成一个 500 英里的缓冲区,并记录使用每种方法花费的时间。在接下来的运行中,几何服务花了将近半分钟来完成任务,但几何引擎在两秒钟内完成了任务:
If you want to test the difference in performance yourself, you can access the application at http://ekenes.github.io/esri-js-samples/ge-gs/.
几何引擎位于esri/geometry
命名空间中,有两种风格:geometryEngine
和geometryEngineAsync
。两者的区别在于geometryEngineAsync
函数返回一个 JavaScript 承诺,使你能够在浏览器中作为后台线程执行它们,并要求你的浏览器支持 HTML5 网页工作者来实现这一点。promise 是一种帮助您避免将回调函数作为参数提供给方法的构造,在 API 的第 4 版中被广泛使用,我们将在附录 b 中简要介绍
We won't go into detail about web workers, JavaScript promises, or the geometryEngineAsync
functions here. However, they are worth looking into, and the following Mozilla Developer Network articles should help. For information about web workers, see here: http://preview.tinyurl.com/nuvtonm. For promises, see http://preview.tinyurl.com/k55xqy6 , and consult the geometryEngineAsync
docs at https://developers.arcgis.com/javascript/3/jsapi/esri.geometry.geometryengineasync-amd.html.
通常,您应该尽可能使用几何引擎而不是几何服务。
几何引擎练习时间
在本练习中,您将创建一个展示几何引擎性能的应用,方法是在地图上用户光标的位置周围创建一个缓冲区,并在光标移动时更新该缓冲区。
- 转到用于 JavaScript 沙盒的 ArcGIS 应用编程接口,清除以下
<script>
块的内容:
<script>
var map;
require(["esri/map", "dojo/domReady!"], function(Map) {
map = new Map("map", {
basemap: "topo", //For full list of ...
center: [-122.45, 37.75], // longitude, latitude
zoom: 13
});
});
</script>
- 创建一个名为
map
的变量来存储esri/Map
对象:
<script>
var map;
</script>
- 创建
require()
函数导入必要的模块,如下图所示:
<script>
var map;
require(["esri/map",
"esri/Color",
"esri/symbols/SimpleMarkerSymbol",
"esri/symbols/SimpleFillSymbol",
"esri/symbols/SimpleLineSymbol",
"esri/graphic",
"esri/geometry/geometryEngine",
"dojo/on", "dojo/domReady!"],
function (Map, Color, SimpleMarkerSymbol, SimpleFillSymbol, SimpleLineSymbol, Graphic, geometryEngine, on) {
});
</script>
- 编写代码在
map
分区创建地图。地图应该以纬度:0,经度:0 为中心,缩放级别为 2,以显示整个世界:
map = new Map("map", {
basemap: "topo",
center: [0, 0],
zoom: 2
});
- 在创建地图的代码下面,创建一个
SimpleMarkerSymbol
来显示光标的位置,创建一个SimpleFillSymbol
来表示缓冲区:
var line = new SimpleLineSymbol();
line.setWidth(2);
var marker = new SimpleMarkerSymbol();
marker.setColor(new Color([0, 197, 255, 1]));
marker.setOutline(line);
marker.setSize(12);
var fill = new SimpleFillSymbol();
fill.setColor(new Color([255, 0, 0, 0.50]));
fill.setOutline(line);
- 然后,为
Map
对象的mouse-move
事件截取一个事件处理程序。这将在每次光标在地图上移动时触发:
on(map, 'mouse-move', function (evt) {
});
每次鼠标移动时,我们都希望移除任何现有图形,然后提供两个新图形:一个表示光标位置的标记和一个表示该点周围 500 英里缓冲区的填充符号。
- 从每次调用事件处理程序时清除图形层开始:
on(map, 'mouse-move', function (evt) {
map.graphics.clear();
});
- 然后,添加一个表示当前光标位置的图形。该信息在传递给处理程序的
evt
对象中可用:
on(map, 'mouse-move', function (evt) {
map.graphics.clear();
var ptGraphic = new Graphic(evt.mapPoint, marker);
map.graphics.add(ptGraphic);
});
接下来我们需要做的是计算光标位置周围的 500 英里缓冲区。geometryEngine
类有两种方法可以实现这一点:buffer()
和geodesicBuffer().``buffer()
函数在输入几何图形周围的指定距离处创建一个平面(或欧几里德)多边形,而geodesicBuffer()
函数,顾名思义,创建一个测地线缓冲多边形。创建缓冲区时,测地线缓冲区会考虑地球的球形特性,而平面缓冲区则不会。在投影平面地图上绘制时,平面缓冲区显示为完美圆,而测地线缓冲区仅在地球仪上显示为完美圆。geodesicBuffer()
功能仅在 WGS-84 (WKID: 4326)和网络墨卡托(WKID: 102100 或 3857)中有效。如果您在地图中使用这些空间参考,就像我们在这里一样,您将通过使用geodesicBuffer()
获得更准确的结果。
- 使用
geometryEngine
类上的geodesicBuffer()
函数计算代表光标位置的点的 500 英里半径内的测地线缓冲区:
on(map, 'mouse-move', function (evt) {
map.graphics.clear();
var ptGraphic = new Graphic(evt.mapPoint, marker);
map.graphics.add(ptGraphic);
var bufferPoly = geometryEngine.geodesicBuffer(ptGraphic.geometry, 500, "miles");
});
- 最后,创建一个图形来表示几何引擎创建的缓冲区,并将其添加到地图中:
on(map, 'mouse-move', function (evt) {
map.graphics.clear();
var ptGraphic = new Graphic(evt.mapPoint, marker);
map.graphics.add(ptGraphic);
var bufferPoly = geometryEngine.geodesicBuffer(ptGraphic.geometry, 500, "miles");
var bufferGraphic = new Graphic(bufferPoly, fill);
map.graphics.add(bufferGraphic);
});
- 通过单击沙箱中的刷新并在地图上移动光标来测试您的应用。
请注意,当光标移动时,缓冲区几乎是即时更新的。几何服务无法实现这种效果。此外,请注意当您将光标移近极点时,缓冲区的外观会如何变化。这很好地证明了地图投影总是会导致的那种失真:
摘要
在本章中,您了解了几何服务及其支持的各种功能。简而言之,它可以帮助您在无需创建地理处理服务的情况下为经常需要的几何操作获得复杂的结果。
您还了解到了更新的、性能大大提高的几何引擎,它让您可以访问相同的操作,而您的应用不必往返于服务器。
到目前为止,我们已经对 ArcGIS API 的 JavaScript 功能进行了越来越深入的挖掘,现在您应该对使用它可以实现的事情有了相当好的了解。在下一章中,我们将后退一步,看看 ArcGIS Online,以及如何使用 ArcGIS for JavaScript 应用编程接口来使用您在 ArcGIS Online 平台上创建的地图。
版权属于:月萌API www.moonapi.com,转载请注明出处