八、把地址变成点,把点变成地址

网络地图应用中经常需要的一个功能是能够搜索街道地址或地名,并将其转换为地理坐标,以便在地图上高亮显示或以其他方式进行操作。这个将地址转化为点的过程叫做地理编码。有时,用户希望能够点击一个地图位置,并让您的应用告诉他们该位置与哪个物理地址相关。这就是所谓的反向地理编码

地理编码通过使用定位器服务在 ArcGIS Server 中完成,定位器服务通过Locator类在 ArcGIS Server JavaScript 应用编程接口中使用。与 ArcGIS Server 提供的其他任务一样,地理编码需要各种输入参数,包括用于地址匹配的Address对象或反向地理编码情况下的Point对象。该信息然后被提交给定位器服务。如果匹配,服务将返回一个AddressCandidate对象,其中包含在地图上绘制地址所需的信息。

在本章中,我们将涵盖以下主题:

  • 地理编码简介
  • 使用 ArcGIS 应用编程接口中的定位器服务进行地理编码
  • 地理编码过程
  • 反向地理编码过程
  • 定位器服务的练习时间

地理编码简介

我们将首先看一个地理编码的例子,让您更好地感受这个过程。如果您的地址位于主街 150 号,您必须首先对该地址进行地理编码,以确定其地理坐标。

如果 150 Main St 位于地址范围为 100 到 200 Main St 的街道段上,地理编码过程会将 150 Main St 的位置插值为正好位于该街道段的中间。然后,它将 150 号主街分配给对应于 100 号和 200 号主街之间中点的地理位置。下图描述了该过程。

现在您已经有了地址的坐标,您可以在地图上绘制它:

这是对街道地址进行地理编码的最常见方式。它在城市地区效果最好,因为那里的地址往往是有规律的。然而,它在地址间隔不规则的区域和死胡同中的地址不太可靠。在非常偏远的地方,这是出了名的不可靠。

还有其他算法可以提高地理编码的质量,但这不是我们要讨论的内容。

使用 ArcGIS 应用编程接口中的定位器服务进行地理编码

ArcGIS Server 定位器服务可以执行地理编码和反向地理编码操作。使用用于 JavaScript 的 ArcGIS 应用编程接口,您向Locator任务提交一个地址,并让它返回任何匹配位置的地理坐标。

下图说明了这一过程。由 JavaScript 中的 JSON 对象定义的地址作为参数输入到Locator任务对象。Locator任务对地址进行地理编码,并将结果返回到一个AddressCandidate对象中,然后该对象可以在地图上显示为一个点。这种模式与我们在前面章节中看到的其他任务相同,其中输入对象(Address对象)向任务(Locator)提供输入参数,该任务将作业提交给 ArcGIS Server。结果对象(AddressCandidate)然后返回到callback功能进行处理:

输入参数对象

Locator任务的输入参数对象要么是用于地理编码的 JSON 地址对象,要么是用于反向地理编码的Point对象。从编程的角度来看,这些对象的创建是不同的。我们将在下一节讨论每个对象。

输入 JSON 地址对象

在简单的地理编码(地址点到点)中,您对Locator任务的输入是一个 JSON 对象,表示您希望搜索的一个或多个地址。每个地址在这个对象中被指定为一系列名称/值对,它们是对象的属性。

您必须定义的确切属性取决于您正在使用的定位器服务的性质。在以下示例中,我们提供了streetcitystatezip:

var address = { 
    street: "380 New York", 
    city: "Redlands", 
    state: "CA", 
    zip: "92373" 
} 

输入点对象

对于反向地理编码,Locator任务的输入是一个esri/geometry/Point对象。这通常是由用户点击地图生成的。您通过收听Map.click事件来检索用户点击的点。该事件提供一个Point对象,该对象代表用户感兴趣的位置,然后您可以将其传递给Locator任务。

有时Point对象会从应用执行的其他操作中派生出来。例如,您的应用可能会根据查询任务选择一个位置,然后需要与该位置相关的地址信息。

定位器对象

Locator类包含可以使用您提供的输入数据执行地理编码或反向地理编码操作的方法和事件。

Locator任务构造器需要一个运行在 ArcGIS Server 实例上的定位器服务的 URL 端点。以下代码使用 ArcGIS Online 上的ESRI_Geocode_USA定位器服务创建了一个Locator任务:

var locator = new Locator("http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/Locators/ESRI_Geocode_USA/GeocodeServer") 

一旦有了Locator的实例,就可以调用addressToLocations()方法对地址进行地理编码,或者调用locationToAddress()方法执行反向地理编码。

当操作完成时,会触发一个事件。在地址地理编码的情况下,事件是address-to-locations-complete。对于反向地理编码,事件为on-location-to-address-complete。两个事件都将AddressCandidate对象返回到处理事件的函数。

地址候选对象

每个AddressCandidate对象包含地理编码操作的一个结果。这个对象有几个属性,包括地址、属性、位置和分数。

attributes属性包含字段名和值的名称/值对。位置是候选地址的 xy 坐标,score属性是 0-100 之间的数值,表示匹配的质量。更高的分数代表更好的匹配。

地理编码过程

让我们总结一下使用 ArcGIS 应用编程接口对地址进行地理编码的过程。

首先,通过引用运行在 ArcGIS Server 实例上的定位器任务的 URL 来创建Locator任务对象。然后,您将输入地址创建为 JSON 对象,并使用addressToLocations()方法将其提交给Locator任务。操作完成后,它会返回一个由AddressCandidate对象组成的数组,然后您可以在地图上绘制这些对象。由您决定使用数组中的哪个元素。通常情况下,得分最高的是:

Note that, for batch geocoding operations, you can use the addressesToLocations() method, which allows you to specify multiple input addresses.

反向地理编码过程

反向地理编码过程类似,但需要不同的输入,并以略有不同的格式返回结果。

该过程还使用一个Locator任务对象,该对象引用一个定位器服务的网址。您创建了一个Point几何对象,这可能是用户单击地图的结果,也可能是应用生成的其他事件。

然后,使用任务的locationToAddress()方法,将Point对象与表示距离的值一起提交给Locator任务。以米为单位提供的distance属性决定了Locator尝试查找地址的半径。如果定位器服务在指定半径内找到一个地址,它会向您的callback功能返回一个单独的AddressCandidate对象:

定位器服务的练习时间

在本练习中,您将学习如何使用 https://geocode.arcgis.com 的 ArcGIS Online 提供的定位器服务使用Locator任务对地址进行地理编码。您将使用户能够搜索地址,找到最匹配的地址,并显示在地图上。

首先,在您选择的文本编辑器中检查示例代码的Chapter08文件夹中geocoding-begin.html文件的内容。本练习的一些代码已经为您预先编写好,因此您可以专注于地理编码功能。处于当前状态的应用请求练习所需的所有模块,并显示一个地图和一个文本框,允许用户搜索地址,如下图所示:

从示例代码中获取geocoding-begin.html文件后,在网络浏览器中打开它,然后按照给定的步骤操作:

  1. 点按文本框旁边的“定位”按钮,您将看到什么都没有发生。由您提供地址搜索功能。
  2. 请访问上的 JavaScript 沙盒的 ArcGIS API。复制并粘贴geocoding-begin.html页面的代码,使其完全替换沙盒中当前的代码。

  3. 添加一个名为locator的变量声明,用于存储Locator任务对象:

var map, locator; 
  1. 就在创建Map对象的代码下面,通过在 ArcGIS Online 上提供世界地理编码服务的 URL 端点来实例化Locator任务:
locator = new Locator( "https://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer"); 
  1. 接下来,当用户单击搜索文本框旁边的定位按钮时,您需要某种方式来触发地理编码功能。在实例化Locator任务的那一行后面输入下面一行代码。这将在道场dijit注册表中查找“定位”按钮,并监听按钮的click事件。当按钮被点击时,它调用一个名为locate()的函数:
registry.byId("locate").on("click", locate); 
  1. 现在您将定义locate()功能。该功能的目的是获取用户输入的搜索地址,并将其打包,以便将其作为输入参数提供给Locator任务。我们正在使用的地理编码服务通过在一个名为SingleLine的单一对象属性中提供要搜索的完整地址,允许进行非常简单的地址搜索。或者,您可以在多个字段中提供此信息,以提高准确性,但我们在这里将保持简单。有关此特定定位器服务接受的参数的更多信息,请参见:https://developers . ArcGIS . com/rest/geocode/API-reference/geocode-find-address-候选者. htm 。我们的代码将从文本框中获取地址,并从中创建一个对象,然后可以在我们提供给任务的对象的addresses属性中使用该对象。我们还将指定希望结果中的每个字段都返回给我们的应用,尽管在本例中,我们只对第一个结果的几何感兴趣。最后,我们使用我们的对象作为输入参数来执行任务的addressToLocations()方法。创建如下代码所示的locate()功能:
function locate() { 
    var address = { 
        SingleLine: dom.byId("address").value 
    }; 
    var options = { 
        address: address, 
        outFields: ["*"] 
    }; 
    locator.addressToLocations(options); 
} 
  1. 接下来,我们需要处理Locator任务的address-to-locations-complete事件,以便我们的应用知道地理编码操作的结果何时可用。使用匿名callback函数创建事件处理程序,如下所示:
locator.on("address-to-locations-complete", function (evt) { 

}); 
  1. 我们想在这个事件处理程序中做的第一件事是从地图中清除任何现有的地理编码结果:
map.graphics.clear(); 
  1. 成功完成后,addressToLocations()方法返回一组AddressCandidate对象。我们只对这个练习中的第一个感兴趣,所以我们称之为topMatch:
var topMatch = evt.addresses[0]; 
  1. 我们想在地图上显示这个结果,所以我们将为它定义一个SimpleMarkerSymbol:
var symbol = new SimpleMarkerSymbol(SimpleMarkerSymbol.STYLE_CIRCLE, 20, 
    new SimpleLineSymbol(SimpleLineSymbol.STYLE_SOLID, 
        new Color([0, 0, 0]), 2), 
    new Color([255, 0, 0, 0.6]));
  1. 我们的定位器服务在 WGS84 地理投影中返回地理编码结果。然而,我们的 ArcGIS Online 地形底图的空间参考是网络墨卡托。所以我们需要重新规划我们的结果。有一个叫做esri/geometry/webMercatorUtils的便捷模块,我们可以使用它将几何图形从地理位置转换为网络墨卡托,反之亦然,我们将使用它的geographicToWebMercator()方法来执行转换:
var point = webMercatorUtils.geographicToWebMercator(topMatch.location);
  1. 现在我们有了一个Point几何图形,我们可以从中创建一个图形,将其添加到地图中,然后缩放并居中地图以突出显示我们的结果。在事件处理程序中输入以下代码:
var locationGraphic = new Graphic(point, symbol); 
map.graphics.add(locationGraphic); 
map.centerAndZoom(locationGraphic.geometry, 18); 
  1. 单击沙箱中的刷新按钮,从表面上看,我们的应用看起来与练习开始时完全相同:

  1. 但是,如果我们正确编写了代码,我们的地理编码功能现在应该可以工作了。将默认地址保留在文本框中(Esri 在加利福尼亚州雷德兰兹的总部),然后单击定位按钮。您应该会看到地图中间出现一个标记,该标记现在位于 Esri 校园的中心:

  1. 尝试使用该应用查找其他地址或地名。例如,输入“谷歌,加利福尼亚州山景城”定位谷歌总部:

  1. 尝试搜索您的家庭地址。根据您的居住地,您的结果可能会有所不同。然而,我(马克·勒温)住在英格兰的农村,那里的门牌号很少,除了最有经验的邮递员,其他人都感到困惑,尽管如此,我的结果还是很准确:

搜索小部件

另一种可能简单得多的将地理编码功能纳入网络地图应用的方法是使用搜索小部件。

搜索小部件取代了 3.13 版应用编程接口中不推荐使用的早期地理编码器小部件,并提供了额外的功能,因为它可以用于跨多个数据源进行搜索,不仅是定位器服务,还有特征图层。

此外,它为用户提供了更好的界面,因为它可以在用户键入搜索标准时提供建议。为此,您需要使用运行在 10.3 或更高版本的 ArcGIS Server 实例上的定位器服务,并加载suggest功能。

“搜索”小部件可用于地理和网络墨卡托空间参考。如果您的地图使用另一个空间参考,则必须设置默认的几何服务,以便服务器可以动态地为您重新投影输入几何。您可以使用esri/config模块设置默认几何服务,如下所示:

require(["esri/config"], function(esriConfig) { 
  esriConfig.defaults.geometryService = "http://www.example.com/arcgis/rest/services/Utilities/Geometry/GeometryServer"; 
}); 

搜索小部件上有许多属性和方法,我们在这里不会讨论其中的大部分。但是,如果您的应用严重依赖于搜索和地理编码,您会发现搜索小部件非常适合您的特定需求。

要将一个非常基本的搜索小部件添加到您的地图中,您所需要做的就是require``esri/dijit/Search模块,然后在小部件必须出现的页面上用地图和div的引用实例化它:

      require([ 

        "esri/map", 
        "esri/dijit/Search", 
        "dojo/domReady!" 

      ], function (Map, Search) { 
         var map = new Map("map", { 
            basemap: "gray", 
            center: [-120.435, 46.159], // lon, lat 
            zoom: 7 
         }); 

         var search = new Search({ 
            map: map 
         }, "search"); 
         search.startup(); 

      }); 

然后,搜索框会在您键入时显示并提供搜索建议。例如,在下面的输出中,我开始键入kennew来搜索华盛顿州的肯尼维克镇,并看到以下建议:

当我单击其中一个搜索建议时,地图会显示一个标记和一个弹出窗口,允许我与结果进一步交互:

这是默认行为,除了已经显示的代码之外,不需要更多的代码。然而,这几乎没有触及这个非常有用的小部件的表面,所以我敦促您查看文档和示例。

Find out more about the Search widget in the ArcGIS API for JavaScript documentation at https://developers.arcgis.com/javascript/3/jsapi/search-amd.html.

摘要

任务Locator允许您与 ArcGIS Server 定位器服务进行交互,以执行地理编码和反向地理编码操作。它使用addressToLocation()locationToAddress()方法来做到这一点。第一个需要指定要搜索的地址的 JSON 对象,第二个需要搜索的Point位置和数字距离。

当您针对所需的地理编码操作类型执行适当的方法时,结果将在AddressCandidate对象中返回,您可以处理和绘制结果。

这个工作流现在对您来说应该很熟悉了,因为它与您在 ArcGIS for JavaScript API 中使用的其他任务非常相似。

在本章中,我们学习了如何识别位置。在下一章中,我们将能够通过使用 API 的方向和路由功能告诉用户如何到达这些位置。