使用-JavaScript-构建-web-和移动-ArcGIS-服务器应用-全-

使用 JavaScript 构建 web 和移动 ArcGIS 服务器应用(全)

原文:zh.annas-archive.org/md5/D4C4E9CDA66F2E731D34B3C600414B4D

译者:飞龙

协议:CC BY-NC-SA 4.0

前言

ArcGIS Server 是用于开发 Web 的 GIS 应用程序的主要平台。您可以使用多种编程语言来开发 ArcGIS Server 应用程序,包括 JavaScript,Flex 和 Silverlight。JavaScript 已成为在此平台上开发应用程序的首选语言,因为它既可用于 Web 应用程序,也可用于移动应用程序,并且不需要在浏览器中安装插件。Flex 和 Silverlight 在移动开发方面都表现不佳,并且都需要在浏览器中运行应用程序时使用插件。

本书将教会您如何使用 ArcGIS API for JavaScript 构建基于 Web 的 GIS 应用程序。通过实际的,动手学习方式,您将学习如何使用 ArcGIS Server 开发完全功能的应用程序,并开发一套高需求的技能。

您将学习如何从各种来源创建地图并添加地理图层,包括瓦片和动态地图服务。此外,您还将学习如何向地图添加图形,并使用FeatureLayer将地理要素流式传输到浏览器。大多数应用程序还包括由 ArcGIS Server 实现的特定功能。您将学习如何使用 ArcGIS Server 提供的各种任务,包括查询,通过属性查找要素,地理处理任务等。最后,您将了解使用 ArcGIS API for JavaScript 开发移动应用程序有多么容易。

本书涵盖内容

第一章,HTML,CSS 和 JavaScript 简介,介绍了在使用 ArcGIS API for JavaScript 开发 GIS 应用程序之前的基本 HTML,CSS 和 JavaScript 概念。

第二章,创建地图和添加图层,教会你如何创建地图并向地图添加图层。您将学习如何创建Map类的实例,向地图添加数据图层,并在网页上显示这些信息。Map类是 API 中最基本的类,因为它为数据图层提供了画布,以及应用程序中发生的任何后续活动。但是,在添加数据图层之前,您的地图是无用的。可以向地图添加几种类型的数据图层,包括瓦片,动态和要素。读者将在本章中了解更多关于这些图层类型的信息。

第三章,向地图添加图形,教会读者如何在地图上显示临时点,线和多边形,并在GraphicsLayer中存储。GraphicsLayer是一个单独的图层,始终位于其他图层的顶部,并存储与地图相关的所有图形。

第四章,要素图层,除了继承自GraphicsLayer的其他功能,还具有执行查询和选择的能力。要素图层还用于在线编辑要素。要素图层与瓦片和动态地图服务图层不同,因为要素图层将几何信息带到客户端计算机,由 Web 浏览器绘制和存储。要素图层可能减少了对服务器的往返。客户端可以请求所需的要素,并对这些要素执行选择和查询,而无需从服务器请求更多信息。

第五章,使用小部件和工具栏,介绍了可以将其放入应用程序以提高生产力的开箱即用小部件。包括 BasemapGallery、Bookmarks、Print、Geocoding、Legend、Measurement、Scalebar、Gauge 和 Overview map 小部件。此外,ArcGIS API for JavaScript 还包括用于向应用程序添加各种工具栏的辅助类,包括导航和绘图工具栏。

第六章,执行空间和属性查询,介绍了 ArcGIS Server 查询任务,允许您对已公开的地图服务中的数据图层执行属性和空间查询。您还可以结合这些查询类型执行组合属性和空间查询。

第七章,识别和查找要素,介绍了在任何 GIS 应用程序中都可以找到的两个常见操作。这些操作要求用户在识别的情况下在地图上单击要素,或者在查找要素的情况下执行查询。在任何情况下,都会返回有关特定要素的信息。在本章中,读者将学习如何使用IdentifyTaskFindTask对象获取有关要素的信息。

第八章,将地址转换为点,将点转换为地址,介绍了使用 Locator 任务执行地理编码和反向地理编码。地理编码是将坐标分配给地址的过程,而反向地理编码是将地址分配给坐标的过程。

第九章,网络分析任务,允许您对街道网络执行分析,例如从一个地址到另一个地址找到最佳路线,找到最近的学校,识别位置周围的服务区域,或者用一组服务车辆响应一组订单。

第十章,地理处理任务,允许您执行在 ArcGIS Desktop 中使用 ModelBuilder 构建的自定义模型。模型可以在桌面环境或通过通过 Web 应用程序访问的集中服务器中以自动方式运行。ArcToolbox 中的任何工具,无论是您的 ArcGIS 许可级别的工具还是您构建的自定义工具,都可以在模型中使用,并与其他工具链接在一起。构建完成后,这些模型可以在集中服务器上运行,并通过 Web 应用程序访问。在本章中,我们将探讨如何通过 ArcGIS API for JavaScript 访问这些地理处理任务。

第十一章,与 ArcGIS Online 集成,详细介绍了如何使用 ArcGIS API for JavaScript 访问使用ArcGIS.com创建的数据和地图。网站ArcGIS.com用于处理地图和其他类型的地理信息。在这个网站上,您将找到用于构建和共享地图的应用程序。您还将找到有用的底图、数据、应用程序和工具,可以查看和使用,以及可以加入的社区。对于应用程序开发人员来说,真正令人兴奋的消息是您可以使用 ArcGIS API for JavaScript 将ArcGIS.com内容集成到您的自定义开发的应用程序中。在本章中,您将探索如何将ArcGIS.com地图添加到您的应用程序中。

第十二章,创建移动应用程序,详细介绍了如何使用 ArcGIS API for JavaScript 构建移动 GIS 应用程序。ArcGIS Server 目前支持 iOS、Android 和 BlackBerry 操作系统。该 API 与 dojox/mobile 集成。在本章中,您将了解到 API 的紧凑构建,使得通过 web-kit 浏览器以及内置手势支持成为可能。

附录,使用 ArcGIS 模板和 Dojo 设计应用程序,涵盖了许多 Web 开发人员最困难的任务之一,即设计和创建用户界面。ArcGIS API for JavaScript 和 Dojo 极大地简化了这项任务。Dojo 的布局 dijits 提供了一种简单、高效的方式来创建应用程序布局,Esri 提供了许多示例应用程序布局和模板,您可以使用它们快速启动。在本附录中,读者将学习快速设计应用程序的技巧。

您需要为这本书做好准备

要完成本书中的练习,您需要访问一个 Web 浏览器,最好是 Google Chrome 或 Firefox。每一章都包含了旨在补充所呈现材料的练习。练习将使用 ArcGIS API for JavaScript Sandbox 来编写和测试您的代码。Sandbox 可以在developers.arcgis.com/en/javascript/sandbox/sandbox.html找到。练习将访问 ArcGIS Server 的公开实例,因此您不需要安装 ArcGIS Server。

这本书是为谁准备的

如果您是一名应用程序开发人员,希望使用 ArcGIS Server 和 JavaScript API 开发 Web 和移动 GIS 应用程序,那么这本书非常适合您。它主要面向初学者和中级 GIS 开发人员或应用程序开发人员,他们可能更传统,以前可能没有开发过 GIS 应用程序,但现在被要求在这个平台上实施解决方案。不需要具有 ArcGIS Server、JavaScript、HTML 或 CSS 的先前经验,但这当然是有帮助的。

约定

在本书中,您将找到一些区分不同信息类型的文本样式。以下是这些样式的一些示例,以及它们的含义解释。

文本中的代码词、数据库表名、文件夹名、文件名、文件扩展名、路径名、虚拟 URL、用户输入和 Twitter 句柄显示如下:"将onorientationchange()事件添加到<body>标签。"

代码块设置如下:

routeParams = new RouteParameters();
routeParams.stops = new FeatureSet();
routeParams.outSpatialReference = {wkid:4326};
routeParams.stops.features.push(stop1);
routeParams.stops.features.push(stop2);

当我们希望引起您对代码块的特定部分的注意时,相关行或项目将以粗体显示:

**function computeServiceArea(evt) {**
 **map.graphics.clear();**
 **var pointSymbol = new SimpleMarkerSymbol();**
 **pointSymbol.setOutline = new SimpleLineSymbol(SimpleLineSymbol.STYLE_SOLID, new Color([255, 0, 0]), 1);**
 **pointSymbol.setSize(14);**
 **pointSymbol.setColor(new Color([0, 255, 0, 0.25]));** 
}

新术语重要单词以粗体显示。您在屏幕上看到的单词,比如菜单或对话框中的单词,会以这样的形式出现在文本中:"单击运行按钮"。

注意

警告或重要说明会出现在这样的框中。

提示

提示和技巧会以这样的形式出现。

第一章:HTML、CSS 和 JavaScript 简介

在开始使用 ArcGIS API for JavaScript 开发 GIS 应用程序之前,您需要了解某些基本概念。对于那些已经熟悉 HTML、JavaScript 和 CSS 的人,您可能希望跳到下一章。但是,如果您对这些概念中的任何一个都不熟悉,请继续阅读。我们将以非常基本的水平来介绍这些主题,仅仅足够让您开始。关于这些主题的更高级的教程资源有很多,包括书籍和在线教程。您可以参考附录,使用 ArcGIS 模板和 Dojo 设计应用程序,获取更全面的资源列表。

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

  • 基本 HTML 页面概念

  • JavaScript 基础知识

  • 基本 CSS 原则

基本 HTML 页面概念

在我们深入讨论创建地图和添加信息层的细节之前,您需要了解在使用 ArcGIS API for JavaScript 开发应用程序时代码将放置的上下文。您编写的代码将放置在 HTML 页面或 JavaScript 文件中。HTML 文件通常具有.html.htm文件扩展名,JavaScript 文件具有.js扩展名。创建基本 HTML 页面后,您可以按照使用 ArcGIS API for JavaScript 创建基本地图所需的步骤进行操作。

网页的核心是一个 HTML 文件。编写这个基本文件非常重要,因为它构成了您的应用程序的基础。在基本 HTML 编码中犯的错误可能会导致问题,当您的 JavaScript 代码尝试访问这些 HTML 标签时会出现问题。

以下是一个非常简单的 HTML 页面的代码示例。这个示例是一个 HTML 页面可以变得多么简单。它只包含了主要的 HTML 标签<DOCTYPE><html><head><title><body>。使用您喜欢的文本或网络编辑器输入以下代码。我使用 Notepad++,但还有许多其他好的编辑器可用。将此示例保存为helloworld.html

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">

<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>Topographic Map</title>

  </head>
  <body>
      Hello World
  </body>
</html>

目前有不同类型的 HTML 正在使用。新的 HTML5 受到了很多关注,您可能会看到几乎专门用于开发新应用程序的这种实现;因此,我们将在整本书中专注于 HTML5。但是,我想让您知道还有其他正在使用的 HTML 版本,最常见的是 HTML 4.01(在上面的代码示例中看到)和 XHTML 1.0。

提示

下载示例代码

您可以从www.packtpub.com的帐户中下载您购买的所有 Packt 图书的示例代码文件。如果您在其他地方购买了本书,可以访问www.packtpub.com/support并注册以直接通过电子邮件接收文件。

HTML DOCTYPE 声明

您的 HTML 页面的第一行将包含DOCTYPE声明。这用于告诉浏览器应如何解释 HTML 页面。在本书中,我们将专注于 HTML5,因此您将看到的以下示例使用 HTML5 的DOCTYPE声明。另外两个常见的DOCTYPE声明是 HTML 4.01 Strict 和 XHTML 1.0 Strict:

  • HTML 5 使用以下代码:
<!DOCTYPE html>
  • HTML 4.01 Strict 使用以下代码:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
  • XHTML 1.0 Strict 使用以下代码:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

主要标签

至少,您的所有网页都需要包含<html><head><body>标签。<html>标签定义整个 HTML 文档。所有其他标签必须放在此标签内。定义网页在浏览器中显示方式的标签放在<body>标签内。例如,您的地图应用程序将包含一个<div>标签,该标签位于<body>标签内,用作显示地图的容器。

在浏览器中加载helloworld.html页面将产生您在下面截图中看到的内容。您编写的大部分 ArcGIS API for JavaScript 代码将放置在<head></head>标签之间,并在<script>标签内或单独的 JavaScript 文件中。随着经验的积累,您可能会开始将 JavaScript 代码放在一个或多个 JavaScript 文件中,然后从 JavaScript 部分引用它们。我们将在后面探讨这个话题。现在,只需专注于将您的代码放在<head>标签内。

主要标签

验证 HTML 代码

正如前面提到的,非常重要的是您的 HTML 标签被正确编码。你可能会说,这都很好,但我怎么知道我的 HTML 已经被正确编码了呢?嗯,有许多 HTML 代码验证器可以用来检查您的 HTML。W3C HTML 验证器(validator.w3.org/)如下面的截图所示,可以通过 URI、文件上传或直接输入来验证 HTML 代码:

验证 HTML 代码

假设您的 HTML 代码成功验证自身,您将会看到一个屏幕上显示成功验证的消息,如下面的截图所示:

验证 HTML 代码

另一方面,它将用红色显示的错误消息来识别任何问题。错误会被详细描述,这样更容易纠正问题。通常一个错误会导致许多其他错误,所以看到一个长长的错误列表并不罕见。如果是这种情况,不要惊慌。修复一个错误通常会解决许多其他错误。

验证 HTML 代码

要纠正前面文档中的错误,您需要将文本Hello World用类似<p>Hello World</p>的段落标签括起来。

JavaScript 基础知识

正如其名称所暗示的,ArcGIS API for JavaScript 要求您在开发应用程序时使用 JavaScript 语言。在开始构建应用程序之前,您需要了解一些基本的 JavaScript 编程概念。

JavaScript 是一种轻量级的脚本语言,嵌入在所有现代的 Web 浏览器中。尽管 JavaScript 当然可以存在于 Web 浏览器环境之外的其他应用程序中,但它最常用于与 Web 应用程序的集成。

所有现代的 Web 浏览器,包括 Internet Explorer,Firefox 和 Chrome,都内置了 JavaScript。在 Web 应用程序中使用 JavaScript 使我们能够创建动态应用程序,而无需往返服务器获取数据,因此应用程序更具响应性和用户友好性。然而,JavaScript 确实有能力向服务器提交请求,并且是异步 JavaScript 和 XMLAJAX)堆栈中的核心技术。

注意

关于 JavaScript 的一个常见误解是它是 Java 的简化版本。这两种语言实际上是无关的,除了名字以外。

代码中的注释

始终通过注释来记录您的 JavaScript 代码是最佳实践。至少,这些注释应包括代码的作者、最后修订日期和代码的一般目的。此外,在代码的各个部分,您应该包括注释部分,定义应用程序特定部分的目的。这些文档的目的是使您或任何其他程序员在需要以某种方式更新代码时更容易快速上手。

您在代码中包含的任何注释都不会被执行。JavaScript 解释器只是简单地忽略它们。在 JavaScript 中,可以通过几种方式进行注释,包括单行和多行注释。单行注释以//开头,以及您添加到该行的任何其他字符。以下代码示例显示了如何创建单行注释:

//this is a single line comment.  This line will not be executed

JavaScript 中的多行注释以/*开头,以*/结尾。之间的任何行都被视为注释,不会被执行。以下代码示例显示了多行注释的示例:

**/***
 Copyright 2012 Google Inc.

 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

 http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
***/**

变量

变量的概念是您在使用任何编程语言时需要了解的基本概念。变量只是我们用来与某种数据值关联的名称。在较低级别上,这些变量是计算机内存中划出的存储数据的空间。

您可以将变量视为具有名称并包含某种数据的盒子。当我们最初创建变量时,它是空的,直到分配数据。基本上,变量使我们能够存储和操作数据。在下图中,我们创建了一个名为ssn的变量。最初,此变量为空,但然后被赋予值450-63-3567。分配给变量的数据值可以是各种类型,包括数字、字符串、布尔值、对象和数组。

变量

在 JavaScript 中,变量使用var关键字声明。一般来说,您分配给变量的名称完全取决于您。但是,在创建变量时,有一些规则需要遵循。变量可以包含文本和数字,但不应以数字开头。始终使用字母或下划线开头变量名。此外,变量名中不允许包含空格,也不允许包含特殊字符,如百分号和和号。除此之外,您可以自由创建变量名,但应尽量分配描述变量将被分配的数据的变量名。使用相同的var关键字声明多个变量也是完全合法的,如下面的代码示例所示:

var i, j, k;

您还可以将变量声明与数据分配结合在一起,如以下示例所示:

var i = 10;
var j = 20;
var k = 30;

您可能还注意到每个 JavaScript 语句都以分号结束。分号表示 JavaScript 语句的结束,并且应始终包含在 JavaScript 中。

JavaScript 和大小写敏感性

我需要强调的一个非常重要的观点是,JavaScript 是一种大小写敏感的语言,您需要非常小心,因为这可能会在您的代码中引入一些难以跟踪的错误。所有变量、关键字、函数和标识符都必须以一致的大写字母拼写。当您考虑到 HTML 不区分大小写时,这会变得更加令人困惑。这往往是新 JavaScript 开发人员的绊脚石。在下面的代码片段中,我创建了三个变量,拼写相同。但是,由于它们没有遵循相同的大写规则,您最终会得到三个不同的变量:

Var myName = 'Eric';
var myname = 'John';
var MyName = 'Joe';

变量数据类型

JavaScript 支持各种类型的数据,可以分配给您的变量。与.NET 或 C++等强类型语言不同,JavaScript 是一种弱类型语言。这意味着您不必指定将占用变量的数据类型。JavaScript 解释器会在运行时为您执行此操作。您可以将文本字符串、数字、布尔值、数组或对象分配给您的变量。

数字和字符串在大多数情况下都很简单。字符串只是由单引号或双引号括起来的文本。例如:

varbaseMapLayer = "Terrain";
varoperationalLayer = 'Parcels';

数字不包含在引号内,可以是整数或浮点数:

var currentMonth = 12;
var layered = 3;
var speed = 34.35;

我要指出的一件事是,新程序员可能会感到困惑的一点是,可以通过用单引号或双引号括起来的值将数值赋给字符串变量。例如,没有单引号或双引号的值 3.14 是一个数值数据类型,而带有单引号或双引号的值 3.14 被分配了一个字符串数据类型。

其他数据类型包括布尔值,它们只是真或假的值,以及数组,它们是数据值的集合。数组基本上用作多个值的容器。例如,您可以在数组中存储地理数据图层名称的列表,并根据需要单独访问它们。

数组允许您在单个变量中存储多个值。例如,您可能希望存储要添加到地图中的所有图层的名称。您可以使用数组将它们全部存储在一个变量中,而不是为每个图层创建单独的变量。然后,您可以使用for循环通过索引号循环访问数组中的单个值。下面的代码示例展示了在 JavaScript 中创建数组的一种方法:

var myLayers=new Array(); 
myLayers[0]="Parcels";       
myLayers[1]="Streets";
myLayers[2]="Streams";

您还可以简化创建数组变量的过程,就像下面的代码示例中所示,数组已经创建为括号括起来的逗号分隔列表:

var myLayers = ["Parcels", "Streets", "Streams"];

您可以通过使用索引来访问数组中的元素,就像下面的代码示例中所示。数组访问是从零开始的,这意味着数组中的第一个项目占据0位置,数组中的每个后续项目都增加了一个:

var layerName = myLayers[0];  //returns Parcels

决策支持语句

JavaScript 和其他编程语言中的if/else语句是一种控制语句,允许在代码中进行决策。这种类型的语句在语句的顶部执行测试。如果测试返回true,则与if块关联的语句将运行。如果测试返回false,则执行跳转到第一个else if块。这种模式将继续,直到测试返回true或执行到达else语句。下面的代码示例显示了这种语句的工作原理:

var layerName = 'streets';
if (layerName == 'aerial') {
    alert("An aerial map");
}
else if (layerName == "hybrid") {
    alert("A hybrid map");
}
else {
    alert("A street map");
}

循环语句

循环语句使您能够一遍又一遍地运行相同的代码块。JavaScript 中有两种基本的循环机制。for循环执行指定次数的代码块,而while循环在条件为真时执行代码块。一旦条件变为假,循环机制就会停止。

下面的代码示例显示了for循环的语法。您会注意到它需要一个起始值,这将是一个整数和一个条件语句。您还可以提供一个增量。for循环内的代码块将在给定条件下执行,而该值小于结束值时:

for (start value; condition statement; increment)
{
  the code block to be executed
 }

在下面的例子中,起始值设置为0并分配给一个名为i的变量。条件语句是当i小于或等于10时,i的值每次循环都会增加1,使用++运算符。每次通过循环时,都会打印i的值:

var i = 0;
for (i = 0; i <= 10; i++) {
    document.write("The number is " + i);
    document.write("<br/>");
}

JavaScript 中的另一种基本循环机制是while循环。当您想要在条件为真时执行代码块时,可以使用此循环。一旦条件设置为假,执行就会停止。while循环接受一个参数,即将被测试的条件。在下面的例子中,当i小于或等于10时,代码块将被执行。最初,i被设置为0的值。在代码块的末尾,您会注意到i增加了一个(i = i + 1):

var i = 0;
while (i <= 10)
{
    document.write("The number is " + i);
    document.write("<br/>");
    i = i + 1;
}

函数

现在让我们来讨论非常重要的函数主题。函数只是在调用时执行的命名代码块。您在本书和开发工作中编写的绝大部分代码都将出现在函数中。

最佳实践要求您将代码分成执行小的、离散的操作单元的函数。这些代码块通常在网页的<head>部分内部的<script>标记中定义,但也可以在<body>部分中定义。然而,在大多数情况下,您会希望将函数定义在<head>部分,以便在页面加载后确保它们可用。

要创建一个函数,您需要使用function关键字,后面跟着您定义的函数名称,以及作为参数变量传递的执行函数所需的任何变量。如果您的函数需要将一个值返回给调用代码,您将需要使用return关键字,与您想要传回的数据一起使用。

函数还可以接受参数,这些参数只是用于将信息传递到函数中的变量。在下面的代码示例中,prod()函数传递了两个变量:ab。这些信息以变量的形式可以在函数内部使用:

var x;
function multiplyValues(a,b){
    x = a * b;return x;
}

对象

现在我们已经了解了一些基本的 JavaScript 概念,我们将解决本节中最重要的概念。为了有效地使用 ArcGIS API for JavaScript 编程地图应用程序,您需要对对象有一个良好的基本理解。因此,这是一个您需要掌握的关键概念,以了解如何开发 Web 地图应用程序。

ArcGIS API for JavaScript 广泛使用对象。我们将详细介绍这个编程库的细节,但现在我们将介绍高级概念。对象是复杂的结构,能够将多个数据值和动作聚合到一个结构中。这与我们的原始数据类型(如数字、字符串和布尔值)有很大的不同,后者只能保存一个值。对象是更复杂的结构。

对象由数据和动作组成。数据以属性的形式包含有关对象的信息。例如,在 ArcGIS API for JavaScript 中找到的Map对象中有许多属性,包括地图范围、与地图相关的图形、地图的高度和宽度、与地图相关的图层 ID 等。这些属性包含有关对象的信息。

对象还有我们通常称为方法的动作,但我们也可以将构造函数和事件分为这个类别。方法是地图可以执行的操作,比如添加图层、设置地图范围或获取地图比例。

构造函数是用于创建对象的新实例的特殊用途函数。对于某些对象,还可以将参数传递到构造函数中,以便更好地控制所创建的对象。以下代码示例显示了如何使用构造函数创建Map对象的新实例。您可以通过我突出显示的new关键字来判断这个方法是一个构造函数。new关键字后面跟着对象的名称和用于控制new对象的任何参数,定义了对象的构造函数。在这种情况下,我们创建了一个新的Map对象,并将其存储在一个名为map的变量中。传递了三个参数到构造函数中,以控制Map对象的各个方面,包括basemap、地图的centerzoom比例级别:

var map = **new** Map("mapDiv", { 
  basemap: "streets",
  center:[-117.148, 32.706], //long, lat
  zoom: 12
});

事件是在对象上发生的动作,由最终用户或应用程序触发。这包括诸如地图点击、鼠标移动或图层添加到地图等事件。

属性和方法通过点表示法访问,其中对象实例名称与属性或方法之间用点分隔。例如,要访问当前地图范围,您可以在代码中输入map.extent。以下是显示如何访问对象属性的一些代码示例:

var theExtent = map.extent;
var graphics = map.graphics;

方法也是如此,只是方法名称的末尾有括号。数据可以通过参数传递到方法中。在以下代码的第一行中,我们将一个名为pt的变量传递给map.centerAt(pt)方法:

map.centerAt(pt);
map.panRight();

基本 CSS 原则

层叠样式表CSS)是一种用于描述 HTML 元素在网页上应如何显示的语言。例如,CSS 通常用于定义页面或一组页面的常见样式元素,如字体、背景颜色、字体大小、链接颜色以及与网页视觉设计相关的许多其他内容。看一下以下代码片段:

<style>
  html, body {
    height: 100%;
    width: 100%;
    margin: 0;
    padding: 0;
  }

  #map{

    padding:0;
    border:solid 2px #94C7BA;
    margin:5px;
  }
  #header {
    border: solid 2px #94C7BA;
    padding-top:5px;
    padding-left:10px;
    background-color:white;

    color:#594735;

    font-size:14pt;
    text-align:left;
    font-weight:bold;
    height:35px;
    margin:5px;
    overflow:hidden;
  }
  .roundedCorners{
    -webkit-border-radius: 4px;
    -moz-border-radius: 4px;
    border-radius: 4px;
  }
  .shadow{

    -webkit-box-shadow: 0px 4px 8px #adadad;
    -moz-box-shadow: 0px 4px 8px #adadad;
    -o-box-shadow: 0px 4px 8px #adadad;
    box-shadow: 0px 4px 8px #adadad;
  }
</style>

CSS 语法

CSS 遵循一定的规则,定义了要选择哪个 HTML 元素以及如何对该元素进行样式设置。CSS 规则有两个主要部分:选择器和一个或多个声明。选择器通常是您要设置样式的 HTML 元素。在下图中,选择器是p。HTML 中的<p>元素表示段落。CSS 规则的第二部分由一个或多个声明组成,每个声明都包括属性和值。属性表示要更改的样式属性。在我们的示例中,我们将color属性设置为red。实际上,我们通过这个 CSS 规则定义了段落中的所有文本应该是红色的。

我们使用了p {color:red},如下图所示:

CSS 语法

您可以在 CSS 规则中包含多个声明,就像在以下示例中所示的那样。声明始终用大括号括起来,每个声明以分号结束。此外,属性和值之间应该放置一个冒号。在这个特定的例子中,已经做出了两个声明:一个是段落的颜色,另一个是段落的文本对齐。请注意,声明之间用分号分隔:

p {color:red;text-align:center}

CSS 注释用于解释您的代码。您应该养成像在任何其他编程语言中一样总是对 CSS 代码进行注释的习惯。注释始终被浏览器忽略。注释以斜杠后跟一个星号开始,并以一个星号后跟一个斜杠结束。之间的所有内容都被视为注释并被忽略。

/*
h1 {font-size:200%;}
h2 {font-size:140%;}
h3 {font-size:110%;}
*/

除了为特定 HTML 元素指定选择器之外,您还可以使用id选择器为任何具有与id选择器匹配的id值的 HTML 元素定义样式。通过井号(#)定义 CSS 中的id选择器,后跟id值。

例如,在以下代码示例中,您会看到三个id选择器:rightPaneleftPanemap。在 ArcGIS API for JavaScript 应用程序中,您几乎总是有一个地图。当您定义一个将用作地图容器的<div>标记时,您定义一个id选择器并为其分配一个值,通常是单词map。在这种情况下,我们使用 CSS 来定义地图的几种样式,包括 5 像素的边距以及特定颜色的实心样式边框和边框半径:

#rightPane {
    background-color:white;
    color:#3f3f3f;
    border: solid 2px #224a54;
    width: 20%;
}
#leftPane {
    margin: 5px;
    padding: 2px;
    background-color:white;
    color:#3f3f3f;
    border: solid 2px #224a54;
    width: 20%;
}
#map {
    margin: 5px;
    border: solid 4px #224a54;
    -mox-border-radius: 4px;
}

CSS 语法

与用于为单个元素分配样式的id选择器不同,class选择器用于指定一组具有相同 HTML 类属性的元素的样式。类选择器用句点定义,后跟类名。您还可以指定只有具有特定类的特定 HTML 元素应受样式影响。以下是示例:

.center {text-align:center;}
p.center {text-align:center;}

你的 HTML 代码将引用类选择器如下:

<p class="center">This is a paragraph</p>

有三种方法可以将 CSS 插入到应用程序中:内联、内部样式表和外部样式表。

内联样式

为 HTML 元素定义 CSS 规则的第一种方法是通过使用内联样式。这种方法并不推荐,因为它混合了样式和表示,并且难以维护。在某些情况下,需要定义一组非常有限的 CSS 规则时,这是一个选项。要使用内联样式,只需在相关的 HTML 标记内放置style属性:

<p style="color:sienna;margin-left:20px">This is a paragraph.</p>

内部样式表

内部样式表将所有的 CSS 规则移动到特定的网页中。只有该特定页面内的 HTML 元素才能访问这些规则。所有的 CSS 规则都在<head>标记内定义,并且被包含在<style>标记内,如下面的代码示例所示:

<head>
    <style type="text/css">
        hr {color:sienna;}
        p {margin-left:20px;}
        body {background-image:url("images/back40.gif");}
    </style>
</head>

外部样式表

外部样式表只是一个包含 CSS 规则的文本文件,并且保存为.css文件扩展名。然后通过 HTML 的<link>标记将该文件链接到想要实现外部样式表中定义的样式的所有网页。这是一种常用的方法,用于将样式与主网页分禅,并且使你能够通过使用单个外部样式表来改变整个网站的外观。

现在让我们着重讨论层叠样式表中的“层叠”部分。正如你现在所知道的,样式可以在外部样式表、内部样式表或内联中定义。还有一个我们没有讨论的第四个级别,那就是浏览器默认样式。不过你对此没有任何控制。在 CSS 中,内联样式具有最高优先级,这意味着它将覆盖在内部样式表、外部样式表或浏览器默认样式中定义的样式。如果没有定义内联样式,那么在内部样式表中定义的任何样式规则将优先于外部样式表中定义的样式。这里的一个警告是,如果在 HTML 的<head>中将外部样式表的链接放在内部样式表之后,外部样式表将覆盖内部样式表!

这些都是需要记住的很多内容!只需记住,层叠样式表中定义的样式规则会覆盖层次结构中较高位置定义的样式规则,如下图所示:

外部样式表

这些是你需要了解的关于 CSS 的基本概念。你可以使用 CSS 来定义网页上几乎任何东西的样式,包括背景、文本、字体、链接、列表、图像、表格、地图和任何其他可见对象。

分离 HTML、CSS 和 JavaScript

你可能想知道所有这些代码放在哪里。你应该把所有的 HTML、CSS 和 JavaScript 代码放在同一个文件中,还是分成不同的文件?对于非常简单的应用程序和示例,将所有代码放在一个扩展名为.html.htm的单个文件中并不罕见。在这种情况下,CSS 和 JavaScript 代码将驻留在 HTML 页面的<head>部分。然而,使用这些代码堆栈创建应用程序的首选方法是将表示与内容和行为分开。应用程序的用户界面项目应该驻留在一个 HTML 页面中,该页面只包含用于定义应用程序内容的标签,以及应用程序的任何 CSS(表示)或 JavaScript(行为)文件的引用。最终结果是一个单独的 HTML 页面和一个或多个 CSS 和 JavaScript 文件。这将导致类似于以下截图所示的文件夹结构,其中我们有一个名为index.html的单个文件和几个包含 CSS、JavaScript 和其他资源(如图像)的文件夹。cssjs文件夹将包含一个或多个文件。

分离 HTML、CSS 和 JavaScript

CSS 文件可以通过<link>标签链接到 HTML 页面中。在下面的代码示例中,您将看到一个代码示例,展示了如何使用<link>标签导入 CSS 文件。CSS 文件的链接应该在 HTML 页面的<head>标签中定义:

<!DOCTYPE html>

<html>
  <head>
    <title>GeoRanch Client Portal</title>
    <meta name="viewport" content="initial-scale=1.0, user-scalable=no">
    <link rel="stylesheet" href="bootstrap/css/bootstrap.css">
  </head>
  <body>
  </body>
</html>

JavaScript 文件可以通过<script>标签导入到您的 HTML 页面中,就像下面的代码示例中所示。这些<script>标签可以放在您网页的<head>标签中,就像下面的 JavaScript 代码中引用 ArcGIS API 一样,或者可以放在页面末尾的</body>标签之前,就像creategeometries.js文件中所做的那样。通常建议将 JavaScript 文件导入到接近</body>标签的位置,因为当浏览器下载 JavaScript 文件时,在下载完成之前不会下载其他任何内容。这可能会导致应用程序加载缓慢的情况。

在头部添加<script>标签是推荐的做法,用于 JavaScript 库,比如需要在与 body 中的 HTML 元素交互之前解析的 Dojo。这就是为什么 ArcGIS API for JavaScript 在头部加载的原因:

<!DOCTYPE html>
<html>
  <head>
    <title>GeoRanch Client Portal</title>
    <meta name="viewport" content="initial-scale=1.0, user-scalable=no">

    **<script src="http://js.arcgis.com/3.7/"></script>**
  </head>
  <body>
    **<script src="js/creategeometries.js"></script>**
  </body>
</html>

将您的代码拆分成多个文件可以清晰地分离您的代码,而且维护起来应该更容易。

摘要

在开始详细讨论 ArcGIS API for JavaScript 之前,您需要了解一些基本的 HTML、CSS 和 JavaScript 概念。本章已经提供了这些内容,但您需要继续学习与这些主题相关的许多其他概念。现在,您已经知道足够多,可以开始尝试了。

您开发的 HTML 和 CSS 代码定义了应用程序的外观,而应用程序提供的功能是通过 JavaScript 控制的。这些是非常不同的技能集,许多人擅长其中一种,但不一定擅长另一种。大多数应用程序开发人员将专注于通过 JavaScript 开发应用程序的功能,并将 HTML 和 CSS 留给设计师!然而,重要的是您至少对所有这些主题的基本概念有很好的理解。在下一章中,我们将深入学习 ArcGIS API for JavaScript,并开始学习如何创建Map对象以及如何向地图添加动态和瓦片地图服务图层。

第二章:创建地图和添加图层

既然我们已经了解了 HTML、CSS 和 JavaScript 的一些基础知识,现在是时候真正开始工作,学习如何构建一些出色的 GIS Web 应用程序了!本章的内容将向您介绍一些基本概念,这些概念定义了您如何创建地图并以图层的形式添加信息。

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

  • JavaScript API for ArcGIS 沙盒

  • 使用 ArcGIS JavaScript API 创建应用程序的基本步骤

  • 关于地图的更多信息

  • 使用地图服务图层

  • 切片地图服务图层

  • 动态地图服务图层

  • 地图导航

  • 使用地图范围

介绍

在学习新的编程语言或应用程序编程接口(API)时,我们都必须从某个地方开始。在使用 ArcGIS JavaScript API 创建 Web 地图应用程序时也是如此。您不仅需要了解一些基本的 JavaScript 概念,还需要掌握 HTML、CSS,当然还有 ArcGIS JavaScript API,它实际上是建立在 Dojo JavaScript 框架之上的。一下子就要掌握这么多知识,所以在本章中,我将让您创建一个非常基本的应用程序,这将成为您在接下来的章节中可以构建的基础。模仿是学习编程技能的一种绝佳方式,因此在本章中,我只会让您输入您看到的代码,并且我会在途中提供一些解释。我将把对代码的详细描述留到以后的章节中。

为了让您对 ArcGIS JavaScript API 有所了解,您将在本章中创建一个简单的地图应用程序,该应用程序创建地图,添加了一些数据图层,并提供了一些基本的地图导航功能。

使用 ArcGIS JavaScript API 创建任何 Web 地图应用程序都必须遵循一些基本步骤。您将在本章中首次看到这些步骤的每一个,并且我们将在本书的后面更详细地描述它们。每次使用 JavaScript API 创建新应用程序时,都将遵循这些基本步骤。在创建应用程序的最初几次,这些步骤可能会显得有些奇怪,但您很快就会理解它们的作用和必要性。很快,您就可以将这些步骤视为您在每个应用程序中使用的模板。

让我们开始吧!

ArcGIS JavaScript API 沙盒

在本书中,您将使用 ArcGIS JavaScript API 沙盒来编写和测试您的代码。沙盒可以在developers.arcgis.com/en/javascript/sandbox/sandbox.html找到,并且加载后将显示如下屏幕截图所示。您将在左窗格中编写代码,并单击“运行”按钮以在右窗格中查看结果,如下屏幕截图所示:

ArcGIS JavaScript API 沙盒

使用 ArcGIS JavaScript API 创建应用程序的基本步骤

创建任何 GIS Web 应用程序都需要遵循几个步骤,这些步骤将始终需要执行,如果您打算将地图作为应用程序的一部分。考虑到您正在阅读本书,我无法想象您不想这样做!简而言之,您需要遵循以下几个步骤:

  1. 为页面创建 HTML 代码。

  2. 引用 ArcGIS JavaScript API 和样式表。

  3. 加载模块。

  4. 确保 DOM 可用。

  5. 创建地图。

  6. 定义页面内容。

  7. 页面样式。

这只是对需要做的事情的简要描述。我们将在接下来的页面中更详细地讨论这些步骤。

为 Web 页面创建 HTML 代码

在上一章中,您学习了 HTML、CSS 和 JavaScript 的基本概念。现在,您将开始将这些技能付诸实践。您首先需要创建一个简单的 HTML 文档,最终将作为地图的容器。由于我们使用 ArcGIS API for JavaScript Sandbox,这一步已经为您完成。但是,我希望您花一些时间来检查代码,以便对概念有一个良好的理解。在 Sandbox 的左窗格中,您可以看到以下代码示例中突出显示的代码引用了网页的基本 HTML 代码。显然,其中还有其他 HTML 和 JavaScript 代码,但以下代码构成了网页的基本组件。这段代码包括了几个基本标签,包括<html><head><title><body>和其他一些标签:

<!DOCTYPE html>
**<html>**
**<head>**
 **<title>Create a Map</title>**
 **<meta http-equiv="Content-Type" content="text/html; charset=utf-8">**
 **<meta name="viewport" content="initial-scale=1, maximum-scale=1,user-scalable=no">**
  <link rel="stylesheet" href="http://js.arcgis.com/3.7/js/dojo/dijit/themes/claro/claro.css">
  <link rel="stylesheet" href="http://js.arcgis.com/3.7/js/esri/css/esri.css">
 **<style>**
 **html, body, #mapDiv {**
 **padding: 0;**
 **margin: 0;**
 **height: 100%;**
 **}**
 **</style>**

  <script src="http://js.arcgis.com/3.7/"></script>
  <script>
    dojo.require("esri.map");

    function init(){
     var map = new esri.Map("mapDiv", {
        center: [-56.049, 38.485],
        zoom: 3,
        basemap: "streets"
      });
    }
    dojo.ready(init);
  </script>

**</head>**
**<body class="claro">**
 **<div id="mapDiv"></div>**
**</body>**
**</html>**

引用 ArcGIS API for JavaScript

要开始使用 ArcGIS API for JavaScript,您需要添加对样式表和 API 的引用。在 Sandbox 中,以下代码已经添加到<head>标签内:

  <link rel="stylesheet" href="http://js.arcgis.com/3.7/js/esri/css/esri.css">

<script src="http://js.arcgis.com/3.7/"></script>

<script>标签加载了 ArcGIS API for JavaScript。在撰写本章时,当前版本为 3.7。当 API 的新版本发布时,您需要相应地更新这个数字。<link>标签加载了esri.css样式表,其中包含了 Esri 小部件和组件的特定样式。

可选地,您可以包含对 Dojo Dijit 主题之一的样式表的引用。ArcGIS API for JavaScript 直接构建在 Dojo JavaScript 框架上。Dojo 带有四个预定义的主题,控制着添加到您的应用程序中的用户界面小部件的外观:Claro、Tundra、Soria 和 Nihilo。在以下代码示例中,我引用了 Claro 主题:

<link rel="stylesheet" href="http://js.arcgis.com/3.7/js/dojo/dijit/themes/claro/claro.css">

其他可用的样式表可以像以下代码示例中所示进行引用。您不必引用任何样式表,但如果您打算添加 Dojo 用户界面组件(Dijits),那么您需要加载其中一个样式表来控制组件的样式:

<link rel="stylesheet" href="http://js.arcgis.com/3.7/js/dojo/dijit/themes/tundra/tundra.css">
<link rel="stylesheet" href="http://js.arcgis.com/3.7/js/dojo/dijit/themes/nihilo/nihilo.css">
<link rel="stylesheet" href="http://js.arcgis.com/3.7/js/dojo/dijit/themes/soria/soria.css">

网站www.dojotoolkit.org提供了一个主题测试器,您可以使用它来感受每个主题对用户界面组件显示的影响。主题测试器位于archive.dojotoolkit.org/nightly/dojotoolkit/dijit/themes/themeTester.html。以下截图显示了 Dijit 主题测试器界面:

引用 ArcGIS API for JavaScript

加载模块

在创建Map对象之前,您必须首先引用提供地图的资源。这是通过使用require()函数来实现的。

遗留样式还是 AMD Dojo?

使用旧的 Dojo 遗留样式还是新的 AMD 目前是许多开发人员的挫折之源。异步模型定义AMD)是在 Dojo 的 1.7 版本中引入的。ArcGIS Server API for JavaScript 的 3.4 版本发布是第一个使用新的 AMD 样式重写所有模块的版本。目前,旧的遗留样式和 AMD 样式都可以正常工作,但建议使用新的 AMD 样式编写任何新的应用程序。我们将在本书中遵循这个惯例,但请记住,在 3.4 版本发布之前编写的应用程序和一些 Esri 示例仍反映了旧的编码风格。

require()函数用于将资源导入到您的网页中。ArcGIS API for JavaScript 提供了各种资源,包括esri/map资源,必须在创建地图或处理几何、图形和符号之前提供。一旦提供了对资源的引用,您就可以使用Map构造函数来创建Map。以下几点展示了如何在 Sandbox 中运行代码:

  • 在开始向沙盒添加代码之前,如果需要,请删除以下突出显示的代码。我让您删除的代码来自对 ArcGIS API for JavaScript 的传统编码风格。我们将使用新的 AMD 风格。在将来的 Sandbox 版本中,可能不需要删除这些代码行。我预计 Esri 最终将基本代码块迁移到更新的 AMD 风格:
<script>
 **dojo.require("esri.map");**

 **function init(){**
 **var map = new esri.Map("mapDiv", {**
 **center: [-56.049, 38.485],**
 **zoom: 3,**
 **basemap: "streets"**
 **});**
 **}**
 **dojo.ready(init);**
  </script>
  • 您导入的资源需要包含在新的<script>标签中。将以下突出显示的代码行添加到<script>标签内的沙盒中。require()函数内部使用的参数名称可以是任何您喜欢的名称。但是,Esri 和 Dojo 都提供了一组首选参数。我建议在为require回调函数传递参数时使用 Esri 首选参数列表。Dojo 也使用其首选参数别名。例如,在您添加的以下代码中,我们提供了对esri/map资源的引用,然后在匿名函数内部,我们提供了一个首选参数Map。在require()函数中引用的每个资源都将有一个相关的参数,这将为该资源提供一个对象的钩子:
<script>
**require(["esri/map", "dojo/domReady!"], function(Map) {**

 **});**

</script>

确保文档对象模型可用

当网页加载时,组成页面的所有 HTML 元素都会被加载和解释。这被称为文档对象模型DOM)。非常重要的是,您的 JavaScript 在所有元素加载之前不要尝试访问任何这些元素。显然,如果您的 JavaScript 代码尝试访问尚未加载的元素,将会导致错误。为了控制这一点,Dojo 有一个ready()函数,您可以将其包含在require()函数内部,这样它将仅在所有 HTML 元素和任何模块加载后执行。或者,您可以使用dojo/domReady!插件来确保所有 HTML 元素都已加载。我们将使用第二种方法进行此练习。

在前面的代码中,我们已经使用了带有dojo/domReady!的插件添加到require()函数中。

注意

虽然在基本的 HTML 文件中直接添加 JavaScript 代码是完全可能的,但最好的做法是创建一个单独的 JavaScript 文件(.js)。我们在本书中编写的大部分代码都将在 HTML 文件中完成,以简化操作,但随着您的应用程序变得更加复杂,您将希望遵循将 JavaScript 代码编写到单独文件的做法。

创建地图

通过esri/map创建新地图,这是您在先前步骤中导入的esri/map模块中找到的Map类的引用。在require()函数内部,您将使用构造函数创建一个新的Map对象。Map对象的构造函数接受两个参数,包括一个引用到网页上放置地图的<div>标签以及一个可用于定义各种地图设置选项的选项参数。options参数被定义为一个包含一组键/值对的 JSON 对象。

也许最显眼的选项是basemap,它允许您从ArcGIS.com选择预定义的底图,可以包括streetssatellitehybridtopograyoceansnational-geographicosmzoom选项用于定义地图的起始缩放级别,可以是与预定义缩放比例级别对应的整数值。minZoommaxZoom选项定义地图的最小和最大比例缩放级别。center选项定义地图的中心点,最初将显示并使用包含纬度/经度坐标对的Point对象。还有许多其他选项,您可以将其作为参数传递给Map对象的构造函数。

首先,我们将通过添加以下代码的突出显示行来创建一个名为map的全局变量以及require()函数:

<script>
 **var map;**
 **require(["esri/map", "dojo/domReady!"], function(Map) {**
 **});**
 </script>

将以下突出显示的代码块添加到require()函数中。这行代码是新Map对象的构造函数。传递给构造函数的第一个参数是指向地图将放置的<div>标签的 ID 的引用。我们还没有定义这个<div>标签,但我们将在下一步中这样做。传递给Map构造函数的第二个参数是一个定义选项的 JSON 对象,包括将作为地图中心的地理坐标、缩放级别和topo底图。

basemap.require(["esri/map", "dojo/domReady!"], function(Map) {
 **map = new Map("mapDiv", {**
 **basemap: "topo",**
 **center: [-122.45,37.75], // long, lat**
 **zoom: 13,**
 **sliderStyle: "small"**
 **});**
});

创建页面内容

最后一步之一是创建 HTML <div>标签,作为地图的容器。您总是希望为<div>标签分配一个唯一的 ID,以便您的 JavaScript 代码可以引用该位置。在 Sandbox 中,这个带有唯一标识符mapDiv<div>标签已经为您创建。您可以在下面的代码的突出显示行中看到这一点。此外,您还需要为<body>标签定义类属性,该属性应引用您引用的 Dojo 样式表。

在下面的代码中,您可以看到 Sandbox 中已经创建的<body>标签完成了前面两个任务:

<body class="claro">
 **<div id="mapDiv"></div>**
</body>

为页面添加样式

您可以向<head>标签添加样式信息,以定义网页的各种样式方面。在这种情况下,样式已经在 Sandbox 中为您创建,如下面的代码所示。在这种情况下,样式包括设置地图以填满整个浏览器窗口:

<style>
    html, body, #mapDiv {
      padding:0;
      margin:0;
      height:100%;
    }
</style>

完整的代码

这个简单应用程序的代码应该如下所示:

<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=7, IE=9, IE=10">
    <meta name="viewport" content="initial-scale=1, maximum-scale=1,user-scalable=no"/>
    <title>Simple Map</title>
    <link rel="stylesheet" href="http://js.arcgis.com/3.7/js/esri/css/esri.css">
    <link rel="stylesheet" href="http://js.arcgis.com/3.7/js/dojo/dijit/themes/claro/claro.css">
    <style>
      html, body, #map {
        height: 100%;
        width: 100%;
        margin: 0;
        padding: 0;
      }    
    </style>
    <script src="http://js.arcgis.com/3.7/"></script>
    <script>
      var map;

      require(["esri/map", "dojo/domReady!"], function(Map) {
        map = new Map("map", {
          basemap: "topo",
          center: [-122.45,37.75], // long, lat
          zoom: 13,
          sliderStyle: "small"
        });
      });
    </script>
  </head>

  <body class="claro">
    <div id="map"></div>
  </body>
</html>

点击Run按钮执行代码,如果一切编码正确,您应该看到以下输出:

完整的代码

关于地图的更多信息

在前面描述的过程中,我们介绍了使用 ArcGIS API for JavaScript 构建每个应用程序时需要遵循的流程。您学会了如何创建一个初始化 JavaScript 函数。初始化脚本的目的是创建地图,添加图层,并执行任何其他必要的设置例程,以启动应用程序。创建地图通常是您要做的第一件事,在本节中,我们将更仔细地看看您创建Map类实例的各种选项。

在面向对象编程中,通过构造函数来创建类实例是经常通过构造函数来完成的。构造函数是用于创建或初始化新对象的函数。在这种情况下,构造函数用于创建新的Map对象。构造函数通常接受一个或多个参数,这些参数可用于设置对象的初始状态。

Map构造函数可以接受两个参数,包括地图应该驻留的容器和地图的各种选项。但是,在调用地图的构造函数之前,您必须首先引用提供地图的资源。这是通过导入esri/map资源来实现的。一旦提供了对资源的引用,您就可以使用构造函数来创建地图。<div> ID 是构造函数的必需参数,用于指定地图的容器。此外,您还可以传递多个选项,以控制地图的各个方面,包括底图图层、地图中心的初始显示、导航控件的显示、平移期间的图形显示、滑块的控制、详细级别等等。

让我们更仔细地看一下在地图构造函数中如何指定选项。选项是构造函数中的第二个参数,总是用括号括起来。这定义了 JSON 对象的内容。在括号内,每个选项都有一个特定的名称,后面跟着一个冒号,然后是控制该选项的数据值。如果您需要向构造函数提交多个选项,每个选项之间用逗号分隔。以下代码示例显示了如何向Map构造函数提交选项:

      var map = new Map("mapDiv", {
        center: [-56.049, 38.485],
        zoom: 3,
        basemap: "streets"
      });

在这种情况下,我们正在为地图坐标定义选项,该坐标将作为地图的中心,以及缩放级别和街道的底图图层。这些选项用花括号括起来,并用逗号分隔。

使用地图服务层

没有数据层的地图有点像一块空白的画布。您添加到地图上的数据层赋予了它意义,并为分析设置了舞台。提供可以添加到地图上的数据层的两种主要类型的地图服务:动态地图服务层和瓦片地图服务层。

动态地图服务层引用创建地图图像并将图像返回给应用程序的地图服务。这种地图服务可能由一个或多个信息层组成。例如,以下屏幕截图中显示的人口统计地图服务由九个不同的图层组成,代表不同地理级别的人口统计信息:

使用地图服务层

虽然它们在客户端应用程序中显示可能需要更长的时间,因为它们必须即时生成,但动态地图服务层比瓦片地图服务层更灵活。在动态地图服务层中,您可以通过图层定义控制显示的要素,设置服务内各个图层的可见性,并为图层定义时间信息。例如,在前面屏幕截图中详细介绍的人口统计地图服务层中,您可能选择在应用程序中仅显示人口普查区组。这就是动态地图服务层提供的灵活性,而这是瓦片地图服务层所不具备的。

瓦片地图服务层引用预定义的地图瓦片缓存,而不是动态渲染的图像。理解瓦片地图服务的概念最简单的方法是将其想象成覆盖在地图表面上的网格。网格中的每个单元格大小相同,并将用于将地图切割成称为瓦片的单个图像文件。这些单独的瓦片作为图像文件存储在服务器上,并根据地图范围和比例尺的需要进行检索。这个过程通常在各种地图比例尺上重复。最终结果是生成了各种地图比例尺的瓦片集缓存。当地图在应用程序中显示时,它看起来是无缝的,即使它由许多单独的瓦片组成。

使用地图服务层

这些瓦片或缓存地图层通常用作包括图像、街道地图、地形图在内的底图,或者用于不经常更改的数据层。瓦片地图服务往往显示更快,因为它们没有每次请求地图时都要创建图像的开销。

操作层通常覆盖在瓦片地图的顶部,这些层通常是动态层。虽然它们在性能方面可能会慢一些,但动态地图服务层具有能够动态定义外观的优势。

使用图层类

使用 JavaScript API 中的图层类,您可以引用由 ArcGIS Server 和其他地图服务器托管的地图服务。所有图层类都继承自Layer基类。Layer类没有构造函数,因此您不能从这个类中专门创建对象。这个类只是定义了所有从Layer继承的类的属性、方法和事件。

如下图所示,DynamicMapServiceLayerTiledMapServiceLayerGraphicsLayer都直接继承自Layer类。DynamicMapServiceLayerTiledMapserviceLayer也充当基类。DynamicMapServiceLayer是动态地图服务的基类,而TiledMapServiceLayer是平铺地图服务的基类。第三章,“向地图添加图形”,完全致力于图形和GraphicsLayer,因此我们将在本书的后面讨论这种类型的图层。LayerDynamicMapServiceLayerTiledMapServiceLayer都是基类,这意味着您不能在应用程序中从这些类中专门创建对象。

使用图层类

平铺地图服务图层

如前所述,平铺地图服务图层引用了预定义图像的缓存,这些图像被平铺在一起以创建无缝的地图显示。这些通常用作基础地图。

平铺地图服务图层

当引用由 ArcGIS Server 公开的平铺(缓存)地图服务时,使用ArcGISTiledMapServiceLayer类。由于这种类型的对象针对已缓存的平铺地图集工作,因此通常可以提高性能。ArcGISTiledMapServiceLayer的构造函数需要一个指向地图服务的 URL 指针,以及允许您为地图服务分配 ID 并控制透明度和可见性的选项。

在下面的代码示例中,请注意ArcGISTiledMapServiceLayer的构造函数需要一个参数,该参数引用地图服务。在创建图层实例后,可以使用Map.addLayer()方法将其添加到地图中,该方法接受一个包含对平铺地图服务图层的引用的变量:

var basemap = new ArcGISTiledMapServiceLayer("http://server.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer");
map.addLayer(basemap);

ArcGISTiledMapServiceLayer主要用于快速显示缓存的地图数据。您还可以控制数据显示的级别。例如,您可能希望在用户缩放到 0-6 级时显示来自概括的ArcGISTiledMapService的数据,显示州际和高速公路,然后在用户进一步放大时切换到更详细的ArcGISTiledMapService。您还可以控制添加到地图的每个图层的透明度。

动态地图服务图层

正如其名称所示,ArcGISDynamicMapServiceLayer类用于创建由 ArcGIS Server 提供的动态地图。与ArcGISTiledMapServiceLayer一样,ArcGISDynamicMapServiceLayer的构造函数需要一个指向地图服务的 URL,以及可选参数,用于为服务分配 ID,确定地图图像的透明度,以及设置图层的初始可见性为 true 或 false 的可见性选项。ArcGISDynamicMapServiceLayer类名可能有些误导。尽管它似乎是指一个单独的数据图层,但实际上并非如此。它指的是地图服务而不是数据图层。地图服务内的单独图层可以通过setVisibleLayers()方法打开/关闭。

动态地图服务图层

创建ArcGISDynamicMapServiceLayer的实例看起来与ArcGISTiledMapServiceLayer非常相似。以下代码示例说明了这一点。构造函数接受一个指向地图服务的 URL。第二个参数定义了可选参数,您可以提供以控制透明度、可见性和图像参数:

var operationalLayer = new ArcGISDynamicMapServiceLayer("http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/Demographics/ESRI_Population_World/MapServer",{"opacity":0.5});
map.addLayer(operationalLayer);

将上述两行代码添加到 ArcGIS API for JavaScript Sandbox 中,如下所示的代码:

  <script>
    var map;
    require(["esri/map", **"esri/layers/ArcGISDynamicMapServiceLayer"**, "dojo/domReady!"], function(Map, **ArcGISDynamicMapServiceLayer**) {
      map = new Map("mapDiv", {
        basemap: "topo",
        center: [-122.45,37.75], // long, lat
        **zoom: 5,**
        sliderStyle: "small"
      });
      **var operationalLayer = new ArcGISDynamicMapServiceLayer("http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/Demographics/ESRI_Population_World/MapServer",{"opacity":0.5});**
 **map.addLayer(operationalLayer);**
    });
  </script>

运行上述代码,查看动态图层添加到地图中,如下截图所示:

动态地图服务图层

使用ArcGISDynamicMapServiceLayer的实例,您可以执行许多操作。显然,您可以创建显示服务中数据的地图,但您还可以从服务中的图层查询数据,通过图层定义控制要素显示,控制单个图层的可见性,设置与时间相关的信息,将地图导出为图像,控制背景透明度等等。

向地图添加图层

addLayer()方法将图层的实例(ArcGISDynamicMapServiceLayerArcGISTiledMapServiceLayer)作为第一个参数,并且可选的索引指定它应该放置在哪里。在下面的代码示例中,我们创建了一个指向服务 URL 的ArcGISDynamicMapServiceLayer的新实例。然后调用Map.addLayer()来传递图层的新实例。服务中的图层现在将在地图上可见。

var operationalLayer = new ArcGISDynamicMapServiceLayer("http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/Demographics/ESRI_Population_World/MapServer");
map.addLayer(operationalLayer);

addLayers()方法接受一个图层对象数组,并一次性添加它们。

除了能够向地图添加图层,您还可以使用Map.removeLayer()Map.removeAllLayers()从地图中删除图层。

从地图服务设置可见图层

您可以使用setVisibleLayers()方法控制动态地图服务图层中各个图层的可见性。这仅适用于动态地图服务图层,而不适用于瓦片地图服务图层。该方法接受一个整数数组,对应于地图服务中的数据图层。

这个数组是从零开始的,所以地图服务中的第一个图层占据位置0。在下面的截图中,人口统计地图服务中的Demographics/ESRI_Census_USA占据索引0

设置地图服务中可见图层

因此,如果我们只想显示来自该服务的人口普查区块点人口普查区块组要素,我们可以使用setVisibleLayers(),如下面的代码示例所示:

var dynamicMapServiceLayer = new ArcGISDynamicMapServiceLayer("https://gis.sanantonio.gov/ArcGIS/rest/services/Demographics/MapServer");
dynamicMapServiceLayer.setVisibleLayers([1,2]);
map.addLayer(dynamicMapServiceLayer);

设置定义表达式

在 ArcGIS for Desktop 中,您可以使用定义表达式来限制将显示的数据图层中的要素。定义表达式只是针对图层中的列和行设置的 SQL 查询。只有满足查询的属性的要素才会显示。例如,如果您只想显示人口超过一百万的城市,表达式将是类似于POPULATION > 1000000。ArcGIS API for JavaScript 包含一个setLayerDefinitions()方法,接受一个可以应用于ArcGISDynamicMapServiceLayer的定义数组,以控制生成地图中要素的显示。下面的代码示例显示了如何做到这一点:

设置定义表达式

首先创建一个数组,用于保存多个where子句,这些子句将作为每个图层的定义表达式。在这种情况下,我们为第一层和第六层定义了图层定义。数组是从零开始的,所以第一个数组位于索引0。然后将where子句放入数组中,然后传递到setLayerDefinitions()方法中。然后 ArcGIS Server 会根据每个图层的where子句渲染匹配的要素。

地图导航

现在您已经了解了一些关于地图和驻留在其中的图层的知识,是时候学习如何在应用程序中控制地图导航了。在大多数情况下,您的用户将需要能够使用平移和缩放功能在地图周围导航。ArcGIS API for JavaScript 提供了许多用户界面小部件和工具栏,您可以使用这些小部件和工具栏来允许用户使用缩放和平移功能更改当前地图范围。地图导航也可以通过键盘导航和鼠标导航进行。除了这些用户界面组件和硬件接口之外,地图导航也可以通过编程方式进行控制。

地图导航小部件和工具栏

向您的应用程序提供地图导航控制的最简单方法是通过添加各种小部件和工具栏。创建新地图并添加图层时,默认情况下会包括一个缩放滑块。此滑块允许用户放大和缩小地图。缩放滑块如下截图所示。您无需在程序上做任何事情即可使缩放滑块出现在地图上;它默认存在。但是,如果需要,您可以通过在创建Map对象的实例时将滑块选项设置为false来简单地删除应用程序中的滑块:

{"slider":false,"nav":true,"opacity":0.5,"imageParameters":imageParameters}

以下截图显示了带有缩放滑块的地图:

地图导航小部件和工具栏

您还可以添加平移按钮,单击时将地图平移到箭头指向的方向。默认情况下,平移按钮不会出现在地图上。创建Map对象时,必须明确将nav选项设置为true

{"nav":true,"opacity":0.5,"imageParameters":imageParameters}

以下截图显示了平移选项:

地图导航小部件和工具栏

ArcGIS API for JavaScript 还为您提供了向应用程序添加几种类型的工具栏的能力,包括包含放大和缩小、平移、全范围、下一个范围和上一个范围按钮的导航工具栏。工具栏的创建将在后面的章节中详细介绍,因此我们将保存该讨论以供以后讨论。

地图导航小部件和工具栏

使用鼠标和键盘进行地图导航

用户还可以使用鼠标和/或键盘设备控制地图导航。默认情况下,用户可以执行以下操作:

  • 拖动鼠标进行平移

  • 使用鼠标向前滚动以放大

  • 使用鼠标向后滚动以缩小

  • 按下Shift并拖动鼠标以放大

  • 按下Shift + Ctrl并拖动鼠标以缩小

  • 按下Shift并单击以恢复到中心

  • 双击以居中和放大

  • 按下Shift并双击以居中和放大

  • 使用箭头键进行平移

  • 使用+键放大到某个级别

  • 使用-键缩小一个级别

可以使用多个Map方法之一来禁用前述选项。例如,要禁用滚轮缩放,您将使用Map.disableScrollWheelZoom()方法。这些导航功能也可以在地图加载后移除。

获取和设置地图范围

您要掌握的第一件事情之一是获取和设置地图范围。默认情况下,应用程序中地图的初始范围是创建地图服务时地图文档文件(.mxd)上次保存时的地图范围。在某些情况下,这可能正是您想要的,但是如果您需要设置除默认值之外的地图范围,您将有几个选项。

可以在Map对象的构造函数中定义的可选参数之一是中心参数。您可以将此可选参数与缩放对象一起使用,以设置初始地图范围。在下面的代码示例中,您将看到这一点,我们为地图的中心定义了一个坐标对,以及一个缩放级别为3

var map = new Map("mapDiv", {
        center: [-56.049, 38.485],
        zoom: 3,
        basemap: "streets"
      });

地图的初始范围不是必需的参数,因此如果您省略此信息,地图将简单地使用默认范围。在下面的代码示例中,只指定了容器的 ID:

var map = new Map("map");

创建Map对象后,还可以使用Map.setExtent()方法来更改范围,方法是传入一个Extent对象,如下面的代码示例所示:

var extent = new Extent(-95.271, 38.933, -95.228, 38.976);
map.setExtent(extent);

或者,您可以像下面的代码示例中那样单独设置Extent属性。

var extent = new Extent();
extent.xmin = -95.271;
extent.ymin = 38.933;
extent.xmax = -95.228;
extent.ymax = 38.976;
map.setExtent(extent);

在应用程序中使用多个地图服务时,可以通过地图的构造函数或使用其中一个服务的Map.fullExtent方法来设置初始地图范围。例如,通常使用提供基础图层功能的地图服务,其中包含航空影像以及包含自己本地操作数据源的地图服务。下面的代码示例使用了fullExtent()方法:

map = new Map("mapDiv", {extent:esri.geometry.geographicToWebMercator(myService2.fullExtent) });

当前范围可以通过Map.extent属性或onExtentChange事件来获取。请注意,Map.setExtent属性是只读的,因此不要尝试通过此属性设置地图范围。

地图事件

在编程世界中,事件是应用程序中发生的动作。通常,这些事件是由最终用户触发的,可以包括鼠标点击、鼠标拖动和键盘操作,但也可以包括数据的发送和接收、组件修改等。

JavaScript 的 ArcGIS API 是一个异步 API,遵循发布/订阅模式,应用程序向监听器注册(发布)事件。下图说明了这个过程。监听器负责监视应用程序的这些事件,然后触发响应事件的handler函数。可以将多个事件注册到同一个监听器上。dojo on()方法作为事件到处理程序的功能。

地图事件

正如您可能记得的那样,ArcGIS Server JavaScript API 是建立在 Dojo 之上的。使用 Dojo,事件通过dojo on()方法注册到处理程序。此方法需要三个参数。请看下面截图中显示的代码示例,以更好地理解如何注册事件:

地图事件

我们使用on()方法并传入mapclickdisplayCoordinates等参数。前两个参数表示我们要注册的对象和事件。在这种情况下,这意味着我们正在注册Map对象上找到的click事件。每次用户在地图范围内点击鼠标时,都会触发此事件。最后一个参数displayCoordinates表示事件的监听器。因此,每当Map对象上的click事件被触发时,它将触发displayCoordinates函数,该函数将运行并报告地图的当前范围。尽管事件和它们注册的处理程序会根据您的情况而改变,但注册的方法是相同的。

每次事件发生时,都会生成一个Event对象。这个Event对象包含额外的事件信息,比如点击的鼠标按钮或者按下的键盘按键。这个对象会自动传递到事件处理程序中,可以进行检查。在下面的代码示例中,您可以看到Event对象作为参数传递到处理程序中。这是一个动态对象,其属性将根据触发的事件类型而改变。

function addPoint(evt) {
    alert(evt.mapPoint.x, evt.mapPoint.y);
}

API 中许多不同的对象上都有许多不同的事件可用。但是,重要的是要记住,您不必为每个事件注册监听器。只有对应用程序必要的事件才应该注册。当发生一个未注册监听器的事件时,该事件将被简单地忽略。

Map对象包含许多不同的事件,您可以对其做出响应,包括各种鼠标事件、范围更改事件、底图更改事件、键盘事件、图层事件、平移和缩放事件等。您的应用程序可以对任何这些事件做出响应。在接下来的章节中,我们将研究其他对象上可用的事件。

在不再需要时,将事件与其处理程序断开连接是一种良好的编程实践。通常在用户从页面导航离开或关闭浏览器窗口时执行此操作。以下代码示例显示了如何通过简单调用remove()方法来实现这一点:

var mapClickEvent = on(myMap, "click", displayCoordinates);
mapClickEvent.remove();

总结

在本章中,我们涵盖了很多内容。使用 ArcGIS API for JavaScript 创建的所有应用程序都需要一定的步骤。我们将其称为样板代码。这包括定义对 API 和样式表的引用、加载模块、创建初始化函数以及其他一些步骤。在initialization函数中,您很可能会创建地图、添加各种图层,并执行其他在应用程序使用之前需要执行的设置操作。在本章中,您学会了如何执行这些任务。

此外,我们还研究了可以添加到地图的各种图层类型,包括切片地图服务图层和动态地图服务图层。切片地图服务图层是预先创建并缓存在服务器上的,通常用作应用程序中的底图。动态地图服务图层必须在每次请求时动态创建,因此可能需要更长时间来生成。但是,动态地图服务图层可以用于执行许多类型的操作,包括查询、设置定义表达式等。

此外,您还学习了如何以编程方式控制地图范围。最后,我们介绍了事件的主题,您学会了如何将事件连接到事件处理程序,这只是一个在特定事件触发时运行的 JavaScript 函数。在下一章中,我们将仔细研究如何向应用程序添加图形。

第三章:将图形添加到地图

图形是在地图的图层上绘制的点、线或多边形,这些图层独立于与地图服务相关的任何其他数据图层。大多数人将图形对象与在地图上显示图形的符号相关联。然而,在 ArcGIS Server 中,每个图形可以由多达四个对象组成,包括图形的几何、与图形相关的符号、描述图形的属性和定义当单击图形时出现的信息窗口格式的信息模板。尽管图形可以由多达四个对象组成,但并不总是有必要这样做。您选择与图形关联的对象将取决于您正在构建的应用程序的需求。例如,在显示 GPS 坐标的地图应用程序中,您可能不需要关联属性或显示图形的信息窗口。然而,在大多数情况下,您将为图形定义几何和符号。

图形是存储在地图上单独图层中的临时对象。它们在应用程序使用时显示,并在会话完成时删除。名为图形图层的单独图层存储与您的地图相关的所有图形。在第二章中,创建地图和添加图层,我们讨论了各种类型的图层,包括动态地图服务图层和切片地图服务图层。与其他类型的图层一样,GraphicsLayer也继承自Layer类。因此,Layer类中找到的所有属性、方法和事件也将存在于GraphicsLayer中。

图形显示在应用程序中存在的任何其他图层的顶部。以下屏幕截图显示了点和多边形图形的示例。这些图形可以由用户创建,也可以由应用程序根据已提交的任务绘制。例如,商业分析应用程序可能提供一个工具,允许用户绘制自由手绘多边形来表示潜在的贸易区域。

多边形图形将显示在地图的顶部,并且可以用作拉取与潜在贸易区域相关的人口统计信息的地理处理任务的输入。

将图形添加到地图

许多 ArcGIS Server 任务将它们的结果作为图形返回。QueryTask对象可以执行属性和空间查询。然后,查询的结果以FeatureSet对象的形式返回到应用程序中,它只是一个要素数组。然后,您可以访问每个要素作为图形,并使用循环结构在地图上绘制它们。也许您想要查找并显示所有与百年洪水平原相交的土地地块。QueryTask对象可以执行空间查询,然后将结果返回到您的应用程序中,然后它们将显示为地图上的多边形图形。

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

  • 图形的四个部分

  • 为图形创建几何

  • 符号化图形

  • 为图形分配属性

  • 在信息窗口中显示图形属性

  • 创建图形

  • 将图形添加到图形图层

图形的四个部分

图形由四个部分组成:几何符号属性信息模板,如下图所示:

图形的四个部分

图形具有描述其位置的几何表示。几何与符号一起定义了图形的显示方式。图形还可以具有提供有关图形的描述信息的属性。属性被定义为一组名称-值对。例如,描绘野火位置的图形可以具有描述火灾名称以及烧毁的英亩数的属性。信息模板定义了在图形出现时应显示哪些属性以及它们应该如何显示。创建后,图形对象必须存储在GraphicsLayer对象中,然后才能显示在地图上。这个GraphicsLayer对象作为将要显示的所有图形的容器。

图形的所有元素都是可选的。但是,图形的几何和符号几乎总是被分配的。如果没有这两个项目,地图上就没有东西可以显示,而且没有显示图形的意义。

下图显示了创建图形并将其添加到图形图层的典型过程。在这种情况下,我们应用了图形的几何以及一个符号来描绘图形。但是,我们还没有专门为这个图形分配属性或信息模板。

图形的四个部分

为图形创建几何

图形几乎总是有一个几何组件,这对于它们在地图上的放置是必要的。这些几何对象可以是点、多点、折线、多边形或范围,并且可以通过这些对象的构造函数进行程序化创建,或者可以作为查询等任务的输出返回。

在创建任何这些几何类型之前,需要导入esri/geometry资源。这个几何资源包含了GeometryPointMultipointPolylinePolygonExtent的类。

Geometry是由PointMultiPointPolylinePolygonExtent继承的基类。

如下代码行所示,Point类通过 X 和 Y 坐标定义位置,并且可以定义为地图单位或屏幕单位:

new Point(-118.15, 33.80);

符号化图形

您创建的每个图形都可以通过 API 中找到的各种符号类之一进行符号化。点图形通过SimpleMarkerSymbol类进行符号化,可用形状包括圆圈、十字、菱形、正方形和 X。还可以通过PictureMarkerSymbol类对点进行符号化,该类使用图像来显示图形。线性特征通过SimpleLineSymbol类进行符号化,可以包括实线、虚线、点线或组合。多边形通过SimpleFillSymbol类进行符号化,可以是实心、透明或斜纹。如果您希望在多边形中使用图像进行重复图案,可以使用PictureFillSymbol类。文本也可以添加到图形图层,并通过TextSymbol类进行符号化。

点或多点可以通过SimpleMarkerSymbol类进行符号化,该类具有各种可以设置的属性,包括样式、大小、轮廓和颜色。样式是通过SimpleMarkerSymbol.setStyle()方法设置的,该方法接受以下常量之一,对应于绘制的符号类型(圆圈、十字、菱形等):

  • STYLE_CIRCLE

  • STYLE_CROSS

  • STYLE_DIAMOND

  • STYLE_PATH

  • STYLE_SQUARE

  • STYLE_X

点图形也可以有轮廓颜色,这是通过SimpleLineSymbol类创建的。还可以设置图形的大小和颜色。查看以下代码示例,了解如何完成这些操作:

var markerSymbol = new SimpleMarkerSymbol();
markerSymbol.setStyle(SimpleMarkerSymbol.STYLE_CIRCLE);
markerSymbol.setSize(12);
markerSymbol.setColor(new Color([255,0,0,0.5]));

符号化图形

线性特征使用SimpleLineSymbol类进行符号化,可以是实线或点划线的组合。其他属性包括颜色,使用dojo/Color定义,以及setWidth属性设置线条的粗细。以下代码示例详细解释了该过程:

var polyline = new Polyline(msr);
//a path is an array of points
var path = [new Point(-123.123, 45.45, msr),…..];
polyline.addPath(path);
var lineSymbol = new SimpleLineSymbol().setWidth(5);

//create polyline graphic using polyline and line symbol
var polylineGraphic = new Graphic(polyline, lineSymbol);
map.graphics.add(polylineGraphic);

运行上述代码时获得以下屏幕截图:

符号化图形

多边形通过SimpleFillSymbol类进行符号化,允许以实线、透明或交叉图案绘制多边形。多边形还可以具有由SimpleLineSymbol对象指定的轮廓。以下代码示例详细解释了该过程。

var polygon = new Polygon(msr);
//a polygon is composed of rings
var ring = [[-122.98, 45.55], [-122.21, 45.21], [-122.13, 45.53],……];
polygon.addRing(ring);
var fillSymbol = new SimpleFillSymbol().setColor(new Color([255,0,0,0.25]));
//create polygon graphic using polygon and fill symbol
var polygonGraphic = new Graphic(polygon, fillSymbol);
//add graphics to map's graphics layer
map.graphics.add(polygonGraphic);

运行上述代码时获得以下屏幕截图:

符号化图形

为图形分配属性

图形的属性是描述该对象的名称-值对。在许多情况下,图形是作为QueryTask等任务操作的结果生成的。在这种情况下,每个图形由几何和属性组成,然后您需要相应地对每个图形进行符号化。与图层关联的字段属性成为图形的属性。在某些情况下,可以通过outFields等属性限制属性。如果您的图形是以编程方式创建的,您需要使用Graphic.setAttributes()方法在代码中分配属性,如以下代码示例所示:

Graphic.setAttributes( {"XCoord":evt.mapPoint.x, "YCoord".evt.mapPoint.y,"Plant":"Mesa Mint"});

在信息模板中显示图形属性

除了属性之外,图形还可以具有定义属性数据在弹出窗口中显示方式的信息模板。在以下代码示例中定义了一个点属性变量,其中包含键-值对。在这种特殊情况下,我们有包括地址、城市和州的键。每个名称或键都有一个值。该变量是新点图形构造函数的第三个参数。信息模板定义了弹出窗口的格式,并包含一个标题和一个可选的内容模板字符串。

var pointESRI = new Point(Number(theX), Number(theY),msr);
var markerSymbol = new SimpleMarkerSymbol();
markerSymbol.setStyle(SimpleMarkerSymbol.STYLE_SQUARE);
markerSymbol.setSize(12);
markerSymbol.setColor(new Color([255,0,0]));
var pointAttributes = {address:"101 Main Street", city:"Portland", state:"Oregon"};
var pointInfoTemplate = new InfoTemplate("Geocoding Results");
//create point graphic using point and marker symbol
var pointGraphic = new Graphic(pointESRI, markerSymbol, pointAttributes).setInfoTemplate(pointInfoTemplate);
//add graphics to maps' graphics layer
map.graphics.add(pointGraphic);

上述代码生成以下屏幕截图:

在信息模板中显示图形属性

创建图形

一旦您定义了图形的几何、符号和属性,就可以使用这些参数创建一个新的图形对象,并将其作为Graphic对象的构造函数的输入。在以下代码示例中,我们将为几何(pointESRI)、符号(markerSymbol)、点属性(pointAttributes)和信息模板(pointInfoTemplate)创建变量,然后将这些变量作为输入应用于我们的名为pointGraphic的新图形的构造函数。最后,将该图形添加到图形图层中。

var pointESRI = new Point(Number(theX), Number(theY, msr);
var markerSymbol = new SimpleMarkerSymbol();
markerSymbol.setStyle(SimpleMarkerSymbol.STYLE_SQUARE);
markerSymbol.setSize(12);
markerSymbol.setColor(new Color([255,0,0]));

var pointAttributes = {address:"101 Main Street", city:"Portland", state:"Oregon"};
var pointInfoTemplate = new InfoTemplate("Geocoding Results");
//create the point graphic using point and marker symbol
var pointGraphic = new Graphic(pointESRI, markerSymbol, pointAttributes).setInfoTemplate(pointTemplate);

//add graphics to maps' graphics layer
map.graphics.add(pointGraphic);

将图形添加到图形图层

在地图上显示任何图形之前,您必须将它们添加到图形图层中。每个地图都有一个图形图层,其中包含一个最初为空的图形数组,直到您添加图形为止。该图层可以包含任何类型的图形对象。这意味着您可以同时混合点、线和多边形。图形通过add()方法添加到图层中,也可以通过remove()方法单独删除。如果需要同时删除所有图形,则可以使用clear()方法。图形图层还具有可以注册的多个事件,包括clickmouse-down等。

多个图形图层

API 支持多个图形图层,这样可以更轻松地组织不同类型的图形。图层可以根据需要轻松添加或删除。例如,您可以将代表县的多边形图形放在一个图形图层中,将代表交通事故的点图形放在另一个图形图层中。然后,您可以根据需要轻松添加或删除任一图层。

是时候练习图形了。

在这个练习中,您将学习如何在地图上创建和显示图形。我们将创建一个专题地图,显示科罗拉多州按县的人口密度。您还将介绍查询任务。正如您将在以后的章节中学到的那样,任务可以在 ArcGIS Server 中执行,并包括空间和属性查询、要素识别和地理编码等内容。最后,您将学习如何将属性附加到您的图形要素并在信息窗口中显示它们:

  1. developers.arcgis.com/en/javascript/sandbox/sandbox.html上打开 JavaScript 沙盒。

  2. 从以下代码块中突出显示的<script>标记中删除 JavaScript 内容:

  <script>
 **dojo.require("esri.map");**

 **function init(){**
 **var map = new esri.Map("mapDiv", {**
 **center: [-56.049, 38.485],**
 **zoom: 3,**
 **basemap: "streets"**
 **});**
 **}**
 **dojo.ready(init);**
  </script>
  1. 创建您将在应用程序中使用的变量。
<script>
 **var map, defPopSymbol, onePopSymbol, twoPopSymbol,** threePopSymbol, fourPopSymbol, fivePopSymbol;
</script>
  1. 添加如下突出显示的代码中所见的require()函数:
<script>
  var map, defPopSymbol, onePopSymbol, twoPopSymbol, threePopSymbol, fourPopSymbol, fivePopSymbol;
 **require(["esri/map", "esri/tasks/query", "esri/tasks/QueryTask", "esri/symbols/SimpleFillSymbol", "esri/InfoTemplate", "dojo/domReady!"],**
 **function(Map, Query, QueryTask, SimpleFillSymbol, InfoTemplate) {** 

 **});**
</script>

我们在以前的练习中介绍了esri/map资源,因此不需要额外的解释。esri/tasks/queryesri/tasks/QueryTask资源是新的,我们将在以后的章节中介绍它们。然而,为了完成这个练习,有必要在这一点上向您介绍这些资源。这些资源使您能够在数据图层上执行空间和属性查询。

  1. require()函数内部,您需要创建一个Map对象,并通过添加以下突出显示的代码来添加一个basemap: streets图层。您将设置初始地图范围以显示科罗拉多州的状态:
<script>
  var map, defPopSymbol, onePopSymbol, twoPopSymbol, threePopSymbol, fourPopSymbol, fivePopSymbol;
    require(["esri/map", "esri/tasks/query", "esri/tasks/QueryTask", "esri/symbols/SimpleFillSymbol", "esri/InfoTemplate", "dojo/_base/Color", "dojo/domReady!"], 
      function(Map, Query, QueryTask, SimpleFillSymbol, InfoTemplate, Color) { 
 **map = new Map("map", {**
 **basemap: "streets",**
 **center: [-105.498,38.981], // long, lat**
 **zoom: 6,**
 **sliderStyle: "small"**
 **});**
      });
</script>
  1. require()函数内部,在创建Map对象的代码块正下方,添加突出显示的代码行以创建一个新的透明多边形符号。这将创建一个新的SimpleFillSymbol对象并将其分配给defPopSymbol变量。我们使用255,255,255,和 0的 RGB 值来确保填充颜色完全透明。这是通过值0来实现的,它确保我们的着色将完全透明。稍后,我们将添加额外的符号对象,以便我们可以显示一个按县人口密度着色的地图。但现在,我们只是想创建一个符号,以便您可以理解在地图上创建和显示图形的基本过程。以下代码详细解释了这个过程:
map = new Map("mapDiv", {
  basemap: "streets",
  center: [-105.498,38.981], // long, lat
  zoom: 6,
  sliderStyle: "small"
});
**defPopSymbol = new SimpleFillSymbol().setColor(new Color([255,255,255, 0])); //transparent**

在下一步中,您将预览Query任务如何在应用程序中使用。我们将在以后的章节中详细介绍这个任务,但现在,这是一个介绍。Query任务可用于在地图服务中的数据图层上执行空间和属性查询。在这个练习中,我们将使用Query任务对通过 ESRI 服务提供的县边界图层执行属性查询。

  1. 让我们首先检查我们将在查询中使用的地图服务和图层。打开一个网络浏览器,转到sampleserver1.arcgisonline.com/ArcGIS/rest/services/Specialty/ESRI_StateCityHighway_USA/MapServer。该地图服务提供美国各州和县的人口普查信息,还包括一条高速公路图层。在这个练习中,我们对具有索引号为 2 的县图层感兴趣。单击counties选项以获取有关此图层的详细信息。该图层中有许多字段,但我们实际上只对能够按州名查询的字段和提供每个县人口密度信息的字段感兴趣。STATE_NAME字段提供每个县的州名,POP90_SQMI字段提供每个县的人口密度。

  2. 返回沙盒。在创建符号的代码行的下面,通过添加以下一行代码来初始化一个新的QueryTask对象来创建一个新的QueryTask对象。这行代码的作用是创建一个指向我们在浏览器中刚刚检查的ESRI_StateCityHighway_USA地图服务的新QueryTask对象,并明确指向索引为2的图层,即我们的县图层。以下代码详细解释了这个过程。

var queryTask = new QueryTask("http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/Specialty/ESRI_StateCityHighway_USA/MapServer/2");
  1. 所有QueryTask对象都需要输入参数,以便它们知道要针对图层执行什么。这是通过Query对象实现的。在刚刚输入的行的下面添加以下一行代码:
var query = new Query();
  1. 现在,我们将定义新的Query对象上的一些属性,这些属性将使我们能够执行属性查询。在创建query变量的行的下面添加以下三行代码:
var query = new Query();
**query.where = "STATE_NAME = 'Colorado'";**
**query.returnGeometry = true;**
**query.outFields = ["POP90_SQMI"];**

  1. where属性用于创建一个 SQL 语句,该语句将针对该图层执行。在这种情况下,我们声明我们只想返回那些州名为Colorado的县记录。将returnGeometry属性设置为true表示我们希望 ArcGIS Server 返回与我们的查询匹配的所有要素的几何定义。这是必要的,因为我们需要在地图上将这些要素绘制为图形。最后,outFields属性用于定义我们希望与几何一起返回的字段。稍后在创建县人口密度的色彩编码地图时将使用这些信息。

  2. 最后,我们将使用queryTask上的execute方法来执行针对我们已指定的图层(counties)的查询,使用我们query对象上定义的参数。添加以下一行代码:

queryTask.execute(query, addPolysToMap);

除了将query对象传递给 ArcGIS Server 之外,我们还指示addPolysToMap将作为回调函数。此函数将在 ArcGIS Server 执行查询并返回结果后执行。addPolysToMap函数负责使用返回给它的featureSet对象绘制记录。

  1. 正如我在上一步中提到的,当 ArcGIS Server 返回featureSet对象时,回调函数addPolysToMap将被执行,该对象包含与我们的属性查询匹配的记录。在创建回调函数之前,让我们首先讨论代码将实现的内容。addPolysToMap函数将接受一个名为featureSet的参数。当执行queryTask对象时,ArcGIS Server 会将一个featureSet对象返回给您的代码。featureSet对象包含查询返回的图形对象。在addPolysToMap函数内部,您将看到一行var features = featureSet.features;features属性返回一个包含其中所有图形的数组。在定义了一个新的 feature 变量之后,我们创建了一个for循环,用于循环遍历这些图形并将其绘制到地图上。通过添加以下代码块来创建回调函数:
function addPolysToMap(featureSet) {
  var features = featureSet.features;
  var feature;
  for (var i=0, il=features.length; i<il; i++) {
    feature = features[i];
    map.graphics.add(features[i].setSymbol(defPopSymbol));
  }
}

正如我之前提到的,您必须将创建的每个图形添加到GraphicsLayer对象中。这是通过add()方法完成的,就像您在前面的代码块中看到的那样。您还会注意到,我们将之前创建的符号附加到每个图形(县边界)上。

  1. 通过单击运行按钮执行代码,如果一切编码正确,您应该看到以下截图作为输出。请注意,每个县都用我们定义的符号轮廓化了。

练习图形时间

现在,我们将向应用程序添加额外的代码,根据人口对每个县进行颜色编码。在require()函数内注释掉defPopSymbol变量,并添加五个新符号,如下所示:

//defPopSymbol = new SimpleFillSymbol().setColor(new Color([255,255,255, 0])); //transparent
onePopSymbol = new SimpleFillSymbol().setColor(new Color([255,255,128, .85])); //yellow
twoPopSymbol = new SimpleFillSymbol().setColor(new Color([250,209,85, .85])); 
threePopSymbol = new SimpleFillSymbol().setColor(new Color([242,167,46, .85])); //orange
fourPopSymbol = new SimpleFillSymbol().setColor(new Color([173,83,19, .85])); 
fivePopSymbol = new SimpleFillSymbol().setColor(new Color([107,0,0, .85])); //dark maroon

我们在这里所做的基本上是创建一个基于人口密度为每个县分配符号的颜色渐变。我们还对每个符号应用了透明度值为 0.85,以便我们能够透过每个县。这将使我们能够看到放置在包含城市名称的图层下面的底图。

回想一下,在之前的练习中,我们创建了queryTaskQuery对象,并在Query上定义了一个outFields属性,以返回POP90_SQMI字段。现在,我们将使用在该字段中返回的值来确定应用于每个县的符号,该符号基于该县的人口密度。更新addPolysToMap函数,使其出现在以下代码块中,然后我们将讨论我们所做的事情:

function addPolysToMap(featureSet) {
  var features = featureSet.features;
  var feature;
  for (var i=0, il=features.length; i<il; i++) {
    feature = features[i];
    attributes = feature.attributes;
    pop = attributes.POP90_SQMI;

    if (pop < 10)
    {
                            map.graphics.add(features[i].setSymbol(onePopSymbol));
    }
    else if (pop >= 10 && pop < 95)
   {                      map.graphics.add(features[i].setSymbol(twoPopSymbol));
   }
   else if (pop >= 95 && pop < 365)
   {                  map.graphics.add(features[i].setSymbol(threePopSymbol));
   }
   else if (pop >= 365 && pop < 1100)
   {                map.graphics.add(features[i].setSymbol(fourPopSymbol));
   }
   else
   {                map.graphics.add(features[i].setSymbol(fivePopSymbol));
   }
  }
}

在前面的代码块中,我们所做的是从每个图形中获取人口密度信息,并将其保存到名为pop的变量中。然后使用if/else代码块根据该县的人口密度为图形分配符号。例如,具有人口密度(如POP90_SQMI字段中定义的)为400的县将被分配为由fourPopSymbol定义的符号。因为我们在一个for循环中检查科罗拉多州的每个县,所以每个县图形都将被分配一个符号。

通过单击运行按钮执行代码,如果一切编码正确,您应该看到以下截图作为输出。请注意,每个县都已根据我们之前定义的符号进行了颜色编码。

练习图形时间

现在,您将学习如何将属性附加到图形,并在单击图形时在信息窗口中显示它们。

信息窗口是在单击图形时显示的 HTML 弹出窗口。通常,它包含单击图形的属性,但也可以包含您作为开发人员指定的自定义内容。这些窗口的内容是通过指定窗口标题和要在窗口中显示的内容的InfoTemplate对象指定的。创建InfoTemplate对象的最简单方法是使用通配符,该通配符将自动将数据集的所有字段插入到信息窗口中。我们将添加一些额外的输出字段,以便在信息窗口中显示更多内容。修改query.outFields行,以包括以下代码行中突出显示的字段:

query.outFields = ["**NAME**","POP90_SQMI","**HOUSEHOLDS**","**MALES**","**FEMALES**","**WHITE**","**BLACK**","**HISPANIC**"];

然后,在queryTask.execute行的下面添加以下代码行:

resultTemplate = InfoTemplate("County Attributes", "${*}");

传递给构造函数的第一个参数("County Attributes")是窗口的标题。第二个参数是一个通配符,表示应在窗口中打印属性的所有名称-值对。因此,我们添加到query.outFields的新字段应全部包含在单击图形时的信息窗口中。

最后,我们使用Graphic.setInfoTemplate()方法将新创建的InfoTemplate对象分配给图形。通过添加以下突出显示的代码来修改您的if/else语句:

if (pop < 10)
{
                        map.graphics.add(features[i].setSymbol(onePopSymbol).**setInfoTemplate(resultTemplate)**);
}
else if (pop >= 10 && pop < 95)
{
                        map.graphics.add(features[i].setSymbol(twoPopSymbol).**setInfoTemplate(resultTemplate)**);
}
else if (pop >= 95 && pop < 365)
{
                        map.graphics.add(features[i].setSymbol(threePopSymbol).**setInfoTemplate(resultTemplate)**);
}
else if (pop >= 365 && pop < 1100)
{
                        map.graphics.add(features[i].setSymbol(fourPopSymbol).**setInfoTemplate(resultTemplate)**);
}
else
{
                        map.graphics.add(features[i].setSymbol(fivePopSymbol).**setInfoTemplate(resultTemplate)**);
}

通过单击运行按钮执行代码。单击地图中的任何县,您应该看到类似以下屏幕截图的信息窗口:

练习图形

您可以在ArcGISJavaScriptAPI文件夹的graphicexercise.html文件中查看此练习的解决方案代码,以验证您的代码是否已正确编写。

摘要

在本章中,您了解到图形通常用于表示作为工作应用程序内执行操作的结果生成的信息。通常,这些图形是作为已执行的任务的结果返回的,例如属性或空间查询。这可以包括点、线、多边形和文本。这些都是临时对象,仅在当前浏览器会话期间显示。每个图形可以由几何、符号、属性和信息模板组成,并通过图形图层添加到地图中,该图层始终是应用程序中最顶层的图层。这确保了图层的内容始终可见。在下一章中,我们将向您介绍要素图层,它可以执行图形图层可以执行的所有操作以及更多!

第四章:要素图层

ArcGIS API for JavaScript 提供了一个用于处理客户端图形要素的要素图层。这个FeatureLayer对象继承自GraphicsLayer对象,但也提供了额外的功能,比如执行查询和选择,以及支持定义表达式。它也可以用于 Web 编辑。您应该已经熟悉了之前章节中的图形图层。

要素图层与瓦片和动态地图服务图层不同,它将要素的几何信息从 ArcGIS Server 传输到 Web 浏览器,然后在地图上绘制。它还可以用于表示来自非空间表的数据,以及包含几何的要素类。

从 ArcGIS Server 流式传输数据到浏览器可能会减少与服务器的往返次数,并提高应用程序的性能。客户端可以请求其需要的要素,并对这些要素执行选择和查询,而无需从服务器请求更多信息。FeatureLayer对象特别适用于响应用户交互的图层,如鼠标点击或悬停。这样做的折衷是,如果您使用包含大量要素的要素图层,最初将要素传输到客户端可能需要很长时间。要素图层支持几种显示模式,可以帮助减轻处理大量要素的负担。我们将在本章中研究每种显示模式。

要素图层遵守地图服务中图层上配置的任何定义表达式、比例依赖和其他属性。使用要素图层,您可以访问相关表,执行查询,显示时间切片,处理要素附件,以及执行其他有用的操作。

要素图层

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

  • 创建 FeatureLayer 对象

  • 定义显示模式

  • 设置定义表达式

  • 要素选择

  • 渲染要素图层

  • 练习使用 FeatureLayer

创建 FeatureLayer 对象

要素图层必须引用地图服务或要素服务中的图层。如果您只想从服务器检索几何和属性并自行符号化,可以使用地图服务。如果您想要从服务的源地图文档中受益于符号,则使用要素服务。此外,如果您计划使用要素图层进行编辑,则使用要素服务。要素图层遵守源地图文档中配置的任何要素编辑模板。

在下面的代码示例中,您将了解如何使用其构造函数创建FeatureLayer对象的详细信息。对于瓦片和动态图层,您只需提供指向 rest 端点的指针,但对于要素图层,您需要指向服务中的特定图层。在下面的代码示例中,我们将从服务中的第一个图层创建一个FeatureLayer对象,该图层由数字0表示。FeatureLayer的构造函数还接受选项,如显示模式、输出字段和信息模板。在这里,显示模式设置为SNAPSHOT,这可能表示我们正在处理一个相当小的数据集。我们将在下一节中讨论可以为要素图层定义的各种显示模式以及何时应该使用它们:

var earthquakes = new FeatureLayer("http://servicesbeta.esri.com/ArcGIS/rest/services/Earthquakes/Since_1970/MapServer/0",{ mode: FeatureLayer.MODE_SNAPSHOT, outFields: ["Magnitude"]});

可选的构造函数参数

除了将地图或要素服务中的必需图层作为第一个参数传递给FeatureLayer对象之外,还可以将定义各种选项的 JSON 对象传递给构造函数。可以传递各种各样的选项给构造函数。我将讨论最常用的选项。

outFields属性可用于限制与FeatureLayer对象一起返回的字段。出于性能原因,最好只包括应用程序所需的字段,而不是接受默认的返回所有字段。只返回绝对需要的字段,这将确保应用程序的性能更好。在以下突出显示的代码中,我们已经定义了outFields属性,只返回DateMagnitude字段:

var earthquakes = new FeatureLayer("http://servicesbeta.esri.com/ArcGIS/rest/services/Earthquakes/Since_1970/MapServer/0",{ mode: FeatureLayer.MODE_SNAPSHOT, **outFields: ["Date", "Magnitude"]**});

refreshInterval属性定义了刷新图层的频率(以分钟为单位)。当您有包含经常更改的数据的FeatureLayer对象时,包括新记录,或者可能已更新或删除的记录时,可以使用此属性。以下突出显示的代码设置了 5 分钟的刷新间隔:

var earthquakes = new FeatureLayer("http://servicesbeta.esri.com/ArcGIS/rest/services/Earthquakes/Since_1970/MapServer/0",{ mode: FeatureLayer.MODE_SNAPSHOT, outFields: ["Magnitude"], **refreshInterval: 5**});

要定义在单击要素时应在信息窗口中显示的属性和样式,您可以设置infoTemplate属性,如下面的代码示例所示:

function initOperationalLayer**() {var infoTemplate = new InfoTemplate("${state_name}", "Population (2000):  ${pop2000:NumberFormat}")**;
  var featureLayer = new FeatureLayer("http://sampleserver6.arcgisonline.com/arcgis/rest/services/USA/MapServer/2",{mode: FeatureLayer.MODE_ONDEMAND,outFields: ["*"], **infoTemplate: infoTemplate**});

   map.addLayer(featureLayer);
   map.infoWindow.resize(155,75);
 **}**

如果您知道 Internet Explorer 将是应用程序的主要浏览器,您可能希望将displayOnPan属性设置为false。默认情况下,此属性设置为true,但将其设置为false将在平移操作期间关闭图形,从而提高 Internet Explorer 上应用程序的性能。以下代码块详细解释了这个过程:

var earthquakes = new FeatureLayer("http://servicesbeta.esri.com/ArcGIS/rest/services/Earthquakes/Since_1970/MapServer/0",{ mode: FeatureLayer.MODE_SNAPSHOT, outFields: ["Magnitude"], **displayOnPan: false}**);

显示模式,由mode参数定义,可能是最重要的可选参数。因此,我们将在接下来的几节中更详细地介绍这个内容。

定义显示模式

创建要素图层时,您需要指定检索要素的模式。因为模式决定了何时以及如何将要素从服务器传输到客户端,您的选择会影响应用程序的速度和外观。您可以在以下图表中看到模式选择:

定义显示模式

快照模式

快照模式检索图层中的所有要素,并将它们流式传输到客户端浏览器,然后将它们添加到地图中。因此,在使用此模式之前,您需要仔细考虑图层的大小。通常情况下,您只会在处理小型数据集时使用此模式。快照模式下的大型数据集可能会显著降低应用程序的性能。快照模式的好处是,由于从图层返回了所有要素到客户端,因此无需返回服务器以获取额外数据。这提高了应用程序性能的潜力。

ArcGIS 对一次最多返回的要素数量施加了限制,尽管这个数字可以通过 ArcGIS Server 管理进行配置。在实际操作中,您只会在处理小型数据集时使用此模式:

var earthquakes = new FeatureLayer("http://servicesbeta.esri.com/ArcGIS/rest/services/Earthquakes/Since_1970/MapServer/0",{ **mode: FeatureLayer.MODE_SNAPSHOT**, outFields: ["Magnitude"]});

按需模式

按需模式仅在需要时检索要素。这意味着当前视图范围内的所有要素都会被返回。因此,每次进行缩放或平移操作时,要素都会从服务器流式传输到客户端。这在大型数据集中效果很好,因为在快照模式下效率不高。这确实需要往返服务器以获取每次地图范围变化时的要素,但对于大型数据集来说,这是可取的。以下代码示例向您展示了如何将FeatureLayer对象设置为ONDEMAND模式:

var earthquakes = new FeatureLayer("http://servicesbeta.esri.com/ArcGIS/rest/services/Earthquakes/Since_1970/MapServer/0",{ **mode: FeatureLayer.MODE_ONDEMAND**, outFields: ["Magnitude"]});

仅选择模式

仅选择模式不会最初请求要素。相反,只有在客户端进行选择时才返回要素。所选要素会从服务器流式传输到客户端,然后在客户端上保存。以下代码示例向您展示了如何将FeatureLayer对象设置为SELECTION模式:

var earthquakes = new FeatureLayer("http://servicesbeta.esri.com/ArcGIS/rest/services/Earthquakes/Since_1970/MapServer/0",{ **mode: FeatureLayer.MODE_SELECTION**, outFields: ["Magnitude"]});

设置定义表达式

定义表达式用于限制流向客户端的要素,仅限于符合属性约束的要素。FeatureLayer包含一个setDefinitionExpression()方法,用于创建定义表达式。满足指定条件的所有要素将返回以在地图上显示。表达式是使用传统的 SQL 表达式构建的,如以下代码示例所示:

FeatureLayer.setDefinitionExpression("PROD_GAS='Yes'");

您可以使用FeatureLayer.getDefinitionExpression()方法检索当前设置的定义表达式,该方法返回包含表达式的字符串。

要素选择

要素图层还支持要素选择,这只是图层中用于查看、编辑、分析或输入到其他操作的要素子集。使用空间或属性条件将要素添加到选择集或从选择集中删除,并且可以轻松地用不同于图层正常显示中使用的符号绘制。FeatureLayer上的selectFeatures(query)方法用于创建选择集,并以Query对象作为输入。这在以下代码示例中已经解释过了:

var selectQuery = new Query();
selectQuery.geometry = geometry;
**featureLayer.selectFeatures(selectQuery,FeatureLayer.SELECTION_NEW);**

我们还没有讨论Query对象,但是您可以想象,它用于定义属性或空间查询的输入参数。在此特定代码示例中,已定义了空间查询。

以下屏幕截图显示了已选择的要素。已将选择符号应用于所选要素:

要素选择

在图层上设置的任何定义表达式,无论是通过应用程序还是在地图文档文件内的图层上设置,都将受到尊重。设置用于所选要素的符号非常简单,只需创建一个符号,然后在 FeatureLayer 上使用setSelectionSymbol()方法。所选要素将自动分配此符号。您可以选择定义新的选择集,将要素添加到现有选择集,或从选择集中删除要素,通过各种常量,包括SELECTION_NEWSELECTION_ADDSELECTION_SUBTRACT。新的选择集在以下代码示例中定义:

featureLayer.selectFeatures(selectQuery,**FeatureLayer.SELECTION_NEW**);

此外,您可以定义回调和错误处理函数来处理返回的要素或处理任何错误。

渲染要素图层

渲染器可用于为要素图层或图形图层定义一组符号。这些符号可以基于属性具有不同的颜色和/或大小。ArcGIS Server API for JavaScript 中的五种渲染器类型包括SimpleRendererClassBreaksRendererUniqueValueRendererDotDensityRendererTemporalRenderer。我们将在本节中检查每个渲染器。

无论您使用何种类型的渲染器,渲染过程都将是相同的。您首先需要创建渲染器的实例,为渲染器定义符号,最后将渲染器应用于要素图层。此渲染过程已在以下图表中说明:

渲染要素图层

以下代码示例显示了创建和应用渲染器到FeatureLayer对象的基本编程结构:

var renderer = new ClassBreaksRenderer(symbol, "POPSQMI");
renderer.addBreak(0, 5, new SimpleFillSymbol().setColor(new Color([255, 0, 0, 0.5])));
renderer.addBreak(5.01, 10, new SimpleFillSymbol().setColor(new Color([255, 255, 0, 0.5])));
renderer.addBreak(10.01, 25, new SimpleFillSymbol().setColor(new Color([0, 255, 0, 0.5])));
renderer.addBreak(25.01, Infinity, new SimpleFillSymbol().setColor(new Color([255, 128, 0, 0.5])));
featureLayer.setRenderer(renderer);

最简单的渲染器类型是SimpleRenderer,它只是为所有图形应用相同的符号。

UniqueValueRenderer可用于根据通常包含字符串数据的匹配属性对图形进行符号化。

渲染要素图层

例如,如果您有一个州要素类,您可能希望根据区域名称对每个要素进行符号化。每个区域都将有不同的符号。以下代码示例显示了如何以编程方式创建UniqueValueRenderer并向结构添加值和符号:

var renderer = new UniqueValueRenderer(defaultSymbol, "REGIONNAME");
renderer.addValue("West", new SimpleLineSymbol().setColor(new Color([255, 255, 0, 0.5])));
renderer.addValue("South", new SimpleLineSymbol().setColor(new Color([128, 0, 128, 0.5])));
renderer.addValue("Mountain", new SimpleLineSymbol().setColor(new Color([255, 0, 0, 0.5])));

ClassBreaksRenderer用于处理存储为数值属性的数据。每个图形将根据该特定属性的值以及数据中的断点进行符号化。在下面的屏幕截图中,您可以看到已应用于堪萨斯县级数据的ClassBreaksRenderer的示例:

Rendering a feature layer

断点定义了符号将发生变化的值。例如,对于包裹要素类,您可能希望根据PROPERTYVALUE字段中的值对包裹进行符号化。您首先需要创建ClassBreaksRenderer的新实例,然后为数据定义断点。如果需要,可以使用Infinity-Infinity值作为数据的下限和上限边界,如下面的代码示例所示,我们在这里使用Infinity关键字来表示大于 250,000 的任何值的类断点:

var renderer = new ClassBreaksRenderer(symbol, "PROPERTYVALUE");
renderer.addBreak(0, 50000, new SimpleFillSymbol().setColor(new Color([255, 0, 0, 0.5])));
renderer.addBreak(50001, 100000, new SimpleFillSymbol().setColor(new Color([255, 255, 0, 0.5])));
renderer.addBreak(100001, 250000, 50000, new SimpleFillSymbol().setColor(new Color([0, 255, 0, 0.5])));
renderer.addBreak(250001, Infinity, new SimpleFillSymbol().setColor(new Color([255, 128, 0, 0.5])));

TemporalRenderer提供了基于时间的要素渲染。这种类型的渲染器通常用于显示历史信息或近实时数据。它允许您定义如何渲染观测和轨迹。

以下代码示例解释了如何使用ClassBreaksRenderer创建TemporalRenderer并将其应用于featureLayer对象。ClassBreaksRenderer用于按大小定义符号;大小越大,符号越大:

// temporal renderer
var observationRenderer = new ClassBreaksRenderer(new SimpleMarkerSymbol(), "magnitude");

observationRenderer.addBreak(7, 12, new SimpleMarkerSymbol(SimpleMarkerSymbol.STYLE_SQUARE, 24, new SimpleLineSymbol().setStyle(SimpleLineSymbol.STYLE_SOLID).setColor(new Color([100,100,100])),new Color([0,0,0,0])));

observationRenderer.addBreak(6, 7, new SimpleMarkerSymbol(SimpleMarkerSymbol.STYLE_SQUARE, 21, new SimpleLineSymbol().setStyle(SimpleLineSymbol.STYLE_SOLID).setColor(new Color([100,100,100])),new Color([0,0,0,0])));

observationRenderer.addBreak(5, 6, new SimpleMarkerSymbol(SimpleMarkerSymbol.STYLE_SQUARE, 18,new SimpleLineSymbol().setStyle(SimpleLineSymbol.STYLE_SOLID).setColor(new Color([100,100,100])),new Color([0,0,0,0])));

observationRenderer.addBreak(4, 5, new SimpleMarkerSymbol(SimpleMarkerSymbol.STYLE_SQUARE, 15,new SimpleLineSymbol().setStyle(SimpleLineSymbol.STYLE_SOLID).setColor(new Color([100,100,100])),new Color([0,0,0,0])));

observationRenderer.addBreak(3, 4, new SimpleMarkerSymbol(SimpleMarkerSymbol.STYLE_SQUARE, 12,new SimpleLineSymbol().setStyle(SimpleLineSymbol.STYLE_SOLID).setColor(new Color([100,100,100])),new Color([0,0,0,0])));

observationRenderer.addBreak(2, 3, new SimpleMarkerSymbol(SimpleMarkerSymbol.STYLE_SQUARE, 9,new SimpleLineSymbol().setStyle(SimpleLineSymbol.STYLE_SOLID).setColor(new Color([100,100,100])),new Color([0,0,0,0])));

observationRenderer.addBreak(0, 2, new SimpleMarkerSymbol(SimpleMarkerSymbol.STYLE_SQUARE, 6,new SimpleLineSymbol().setStyle(SimpleLineSymbol.STYLE_SOLID).setColor(new Color([100,100,100])),new Color([0,0,0,0])));

var infos = [{ minAge: 0, maxAge: 1, color: new Color([255,0,0])},{ minAge: 1, maxAge: 24, color: new Color([49,154,255])},{ minAge: 24, maxAge: Infinity, color: new Color([255,255,8])}];

var ager = new TimeClassBreaksAger(infos, TimeClassBreaksAger.UNIT_HOURS);
var renderer = new TemporalRenderer(observationRenderer, null, null, ager);
featureLayer.setRenderer(renderer);

这里定义了一个ager符号,它确定随着时间的推移特征符号的变化。

我们将讨论的最后一种渲染器是DotDensityRenderer。以下屏幕截图显示了使用DotDensityRenderer创建的地图:

Rendering a feature layer

这种类型的渲染器使您能够创建数据的点密度可视化,显示离散空间现象的空间密度,如人口密度。

以下代码示例解释了基于pop字段创建DotDensityRenderer,并定义了dotValue为 1000 和dotSize等于 2。这将为 1000 人口创建每两个像素大小的一个点:

var dotDensityRenderer = new DotDensityRenderer({fields: [{name: "pop",color: new Color([52, 114, 53])}],dotValue: 1000,dotSize: 2});

layer.setRenderer(dotDensityRenderer);

练习使用 FeatureLayer

在这个练习中,您将使用FeatureLayer对象在图层上设置定义表达式,将匹配定义表达式的要素绘制为图形,并响应要素上的悬停事件。

执行以下步骤完成练习:

  1. developers.arcgis.com/en/javascript/sandbox/sandbox.html中打开 JavaScript 沙箱。

  2. 从我在下面的代码块中突出显示的<script>标签中删除 JavaScript 内容:

  <script>
 **dojo.require("esri.map");**

**functioninit(){**
**var map = new esri.Map("mapDiv", {**
 **center: [-56.049, 38.485],**
 **zoom: 3,**
 **basemap: "streets"**
 **});**
 **}**
 **dojo.ready(init);**
  </script>
  1. <script>标签内创建应用程序中将使用的变量:
<script>
 **var map;**
</script>
  1. 创建require()函数,定义在此应用程序中将使用的资源:
<script type="text/javascript" language="Javascript">
  var map;
 **require(["esri/map", "esri/layers/FeatureLayer",    "esri/symbols/SimpleFillSymbol",** 
**"esri/symbols/SimpleLineSymbol", "esri/renderers/SimpleRenderer", "esri/InfoTemplate", "esri/graphic", "dojo/on",** 
**"dojo/_base/Color", "dojo/domReady!"],** 
 **function(Map,FeatureLayer, SimpleFillSymbol,** 
 **SimpleLineSymbol, SimpleRenderer, InfoTemplate,  Graphic, on, Color) {**

 **});**

</script>
  1. 在您的网络浏览器中,导航到sampleserver1.arcgisonline.com/ArcGIS/rest/services/Demographics/ESRI_Census_USA/MapServer/5

我们将在此练习中使用states图层。我们要做的是对states图层应用定义表达式,只显示那些具有中位年龄大于36的州。这些州将显示为地图上的图形,并在用户将鼠标悬停在满足定义表达式的州上时,将显示包含该州的中位年龄、男性中位年龄和女性中位年龄的信息窗口。此外,该州将用红色轮廓显示。我们将从states图层中使用的字段包括STATE_NAMEMED_AGEMED_AGE_MMED_AGE_F

  1. 创建Map对象如下代码示例所示:
<script type="text/javascript" language="Javascript">
              var map;
        require(["esri/map", "esri/layers/FeatureLayer",  "esri/symbols/SimpleFillSymbol", 
             "esri/symbols/SimpleLineSymbol", "esri/renderers/SimpleRenderer", "esri/InfoTemplate", "esri/graphic", "dojo/on", 
             "dojo/_base/Color", "dojo/domReady!"], 
          function(Map,FeatureLayer, SimpleFillSymbol, 
                  SimpleLineSymbol, SimpleRenderer, InfoTemplate, Graphic, on, Color) {
 **map = new Map("mapDiv", {**
 **basemap: "streets",**
 **center: [-96.095,39.726], // long, lat**
 **zoom: 4,**
 **sliderStyle: "small"**
 **});** 

            });

    </script>
  1. 添加一个map.load事件,触发创建map.graphics.mouse-out事件,清除任何现有的图形和信息窗口。以下代码示例详细解释了这一点:
map = new Map("map", {
     basemap: "streets",
     center: [-96.095,39.726], // long, lat
     zoom: 4,
     sliderStyle: "small"
});

 **map.on("load", function() {**
 **map.graphics.on("mouse-out", function(evt) {**
 **map.graphics.clear();**
 **map.infoWindow.hide();**
 **});**
 **});**

  1. 创建一个指向您之前检查过的states图层的新FeatureLayer对象。您还将指定使用SNAPSHOT模式返回要素,定义输出字段,并设置定义表达式。为此,将以下代码添加到您的应用程序中:
map.on("load", function() {
  map.graphics.on("mouse-out", function(evt) {
    map.graphics.clear();
    map.infoWindow.hide();
  });
}); 

**var olderStates = new FeatureLayer("http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/Demographics/ESRI_Census_USA/MapServer/5", {**
 **mode: FeatureLayer.MODE_SNAPSHOT,**
 **outFields: ["STATE_NAME", "MED_AGE", "MED_AGE_M", "MED_AGE_F"]**
**});**
**olderStates.setDefinitionExpression("MED_AGE > 36");**

在这里,我们使用new关键字定义了一个指向代码中指定的rest端点上的states图层的新FeatureLayer实例。在定义FeatureLayer的新实例时,我们包括了一些属性,包括modeoutFields。mode 属性可以设置为SNAPSHOTONDEMANDSELECTION。由于states图层包含相对较少的要素,我们可以在这种情况下使用SNAPSHOT模式。这种模式在将图层添加到地图时检索图层中的所有要素,因此不需要额外的服务器访问来检索图层中的其他要素。我们还指定了outFields属性,这是一个将被返回的字段数组。当用户悬停在州上时,我们将显示这些字段在信息窗口中。最后,我们在图层上设置了我们的定义表达式,只显示那些中位年龄大于36的要素(州)。

  1. 在这一步中,您将创建一个符号并将渲染器应用到从定义表达式返回的要素(州)上。您还将将FeatureLayer添加到地图中。将以下代码添加到您在上一步中添加的代码的下方:
var olderStates = new FeatureLayer("http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/Demographics/ESRI_Census_USA/MapServer/5", {
  mode: FeatureLayer.MODE_SNAPSHOT,
  outFields: ["STATE_NAME", "MED_AGE", "MED_AGE_M", "MED_AGE_F"]
 });
 olderStates.setDefinitionExpression("MED_AGE > 36");

**var symbol = new SimpleFillSymbol(SimpleFillSymbol.STYLE_SOLID, new SimpleLineSymbol(SimpleLineSymbol.STYLE_SOLID, new Color([255,255,255,0.35]), 1),new Color([125,125,125,0.35]));**
 **olderStates.setRenderer(new SimpleRenderer(symbol));**
**map.addLayer(olderStates);**

  1. 使用您之前定义的输出字段,创建一个InfoTemplate对象。将以下代码添加到您在上一步中添加的代码的下方。注意嵌在括号内并以美元符号开头的输出字段的包含:
var infoTemplate = new InfoTemplate();
infoTemplate.setTitle("${STATE_NAME}");
infoTemplate.setContent("<b>Median Age: </b>${MED_AGE_M}<br/>"
  + "<b>Median Age - Male: </b>${MED_AGE_M}<br/>"
  + "<b>Median Age - Female: </b>${MED_AGE_F}");
map.infoWindow.resize(245,125);
  1. 然后,添加以下代码以创建一个图形,当用户将鼠标悬停在一个州上时将显示该图形:
var highlightSymbol = new SimpleFillSymbol(SimpleFillSymbol.STYLE_SOLID, 
new SimpleLineSymbol(SimpleLineSymbol.STYLE_SOLID,
  new Color([255,0,0]), new Color([125,125,125,0.35])));
  1. 最后一步是显示我们在前面步骤中创建的高亮符号和信息模板。每当用户将鼠标悬停在一个州上时,就会发生这种情况。在您之前输入的代码的最后一行下面添加以下代码块。在这里,我们使用on()将事件(鼠标悬停)与一个函数连接起来,每次事件发生时都会做出响应。在这种情况下,mouse-over事件处理程序将清除GraphicsLayer对象中的任何现有图形,创建您在上一步中创建的信息模板,创建高亮符号并将其添加到GraphicsLayer,然后显示InfoWindow对象。这在以下代码块中已经解释过了:
olderStates.on("mouse-over", function(evt) {
  map.graphics.clear();
  evt.graphic.setInfoTemplate(infoTemplate);
  var content = evt.graphic.getContent();
  map.infoWindow.setContent(content);
  var title = evt.graphic.getTitle();
  map.infoWindow.setTitle(title);
  var highlightGraphic = new  Graphic(evt.graphic.geometry,highlightSymbol);
  map.graphics.add(highlightGraphic);
  map.infoWindow.show(evt.screenPoint,map.getInfoWindowAnchor(evt.screenPoint));
});

您可能希望查看解决方案文件(featurelayer.html)中的ArcGISJavaScriptAPI文件夹,以验证您的代码是否已正确编写。

单击运行按钮执行代码,如果一切编码正确,您应该看到以下输出。您应该看到一个类似以下截图的地图。将鼠标悬停在其中一个高亮显示的州上,就会看到一个信息窗口,如下截图所示:

使用 FeatureLayer 进行练习的时间

摘要

ArcGIS Server 的 JavaScript API 提供了一个FeatureLayer对象,用于处理客户端图形要素。这个对象继承自图形图层,但也提供了额外的功能,比如执行查询和选择以及支持定义表达式。特征图层还可以用于 Web 编辑。它与瓦片和动态地图服务图层不同,因为特征图层将几何信息传输到客户端计算机,由 Web 浏览器绘制。这可能会减少与服务器之间的往返次数,并且可以提高服务器端应用程序的性能。客户端可以请求所需的要素,并对这些要素执行选择和查询,而无需从服务器请求更多信息。FeatureLayer对象特别适用于响应用户交互的图层,如鼠标点击或悬停。

第五章:使用小部件和工具栏

作为 GIS Web 应用程序开发人员,您希望专注于构建特定于您正在构建的应用程序的功能。花费宝贵的时间和精力添加基本的 GIS 功能,如缩放和平移到您的应用程序中,会分散您的主要关注点。许多应用程序还需要添加概览地图、图例或比例尺到用户界面中。幸运的是,API 提供了用户界面小部件,您可以直接将其放入您的应用程序中,并进行一些配置,它们就可以使用了。

ArcGIS API for JavaScript 还包括辅助类,用于向您的应用程序添加导航和绘图工具栏。在本章中,您将学习如何将这些用户界面组件轻松添加到应用程序中。

让我们首先来看一下 Esri 在其资源中心网站上放置的一个导航示例。打开一个 Web 浏览器,转到developers.arcgis.com/en/javascript/samples/toolbar_draw/。看一下以下的屏幕截图:

使用小部件和工具栏

首先浏览前面的屏幕截图,您可能会认为绘图工具栏只是一个您可以放入应用程序中的用户界面组件,但实际情况并非如此。ArcGIS API for JavaScript 提供了一个名为esri/toolbars/Draw的工具栏辅助类,以帮助完成此任务。此外,API 还提供了一个处理导航任务的类。这些辅助类的作用是为您节省绘制缩放框、捕获鼠标点击和其他用户发起的事件的工作。任何有经验的 GIS Web 开发人员都会告诉您,这并不是一件小事。将这些基本导航功能添加到 API 提供的辅助类中可以轻松节省数小时的开发工作。

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

  • 将工具栏添加到应用程序中

  • 用户界面小部件

  • 要素编辑

将工具栏添加到应用程序中

使用辅助类NavigationDraw,API 提供了两种基本类型的工具栏,您可以将其添加到应用程序中。还有一个编辑工具栏,可用于通过 Web 浏览器编辑要素或图形。我们将在后面的章节中讨论这个工具栏。

创建工具栏的步骤

NavigationDraw工具栏不仅仅是您可以放入应用程序中的用户界面组件。它们是辅助类,您需要采取几个步骤才能实际创建具有适当按钮的工具栏。对于工具栏的待办事项清单可能看起来有点令人生畏,但做一两次后,它就变得非常简单。以下是执行此操作的步骤,我们将详细讨论每一项:

  1. 为每个按钮定义 CSS 样式。

  2. 在工具栏内创建按钮。

  3. 创建esri/toolbars/Navigationesri/toolbars/Draw的实例。

  4. 将按钮事件连接到处理程序函数。

定义 CSS 样式

您需要做的第一件事是为您打算在工具栏上包含的每个按钮定义 CSS 样式。工具栏上的每个按钮都需要一个图像、文本或两者,以及按钮的宽度和高度。这些属性都在 CSS 中定义在<style>标签内,如下面的代码片段所示。在下面的代码示例中,为Navigation工具栏定义了许多按钮。让我们来看一下缩小按钮,并跟随整个过程,以使事情变得更简单一些。我在下面的代码中突出显示了缩小按钮。与所有其他按钮一样,我们定义了一个用于按钮的图像(nav_zoomout.png),以及按钮的宽度和高度。此外,此样式的标识符被定义为.zoomoutIcon

<style type="text/css">
  @import"http://js.arcgis.com/3.7/js/dojo/dijit/themes/claro/claro.css";
    .zoominIcon{ background-image:url(images/nav_zoomin.png);width:16px; height:16px; }
 **.zoomoutIcon{ background-image:url(images/nav_zoomout.png);width:16px; height:16px; }**
    .zoomfullextIcon{ background-image:url(images/nav_fullextent.png); width:16px;height:16px; }
    .zoomprevIcon{ background-image:url(images/nav_previous.png); width:16px;height:16px; }
    .zoomnextIcon{ background-image:url(images/nav_next.png);width:16px; height:16px; }
    .panIcon{ background-image:url(images/nav_pan.png);width:16px; height:16px; }
    .deactivateIcon{ background-image:url(images/nav_decline.png); width:16px;height:16px; }
</style>

创建按钮

按钮可以在<div>容器内定义,该容器具有BorderContainerdata-dojo-typeContentPane dijit,如下面的代码示例所示。在创建每个按钮时,你需要定义它应该引用的 CSS 样式以及按钮被点击时应该发生什么。按钮使用iconClass属性来引用 CSS 样式。在我们的示例中,缩小按钮的iconClass属性引用了我们之前定义的zoomoutIcon样式。zoomoutIcon样式定义了要用于按钮的图像以及按钮的宽度和高度。看一下下面的代码片段:

<div id="mainWindow" data-dojo-type="dijit/layout/BorderContainer"data-dojo-props="design:'headline'">
  <div id="header"data-dojo-type="dijit/layout/ContentPane"data-dojo-props="region:'top'">
    <button data-dojo-type="dijit/form/Button"iconClass="zoominIcon">Zoom In</button>
 **<button data-dojo-type="dijit/form/Button"iconClass="zoomoutIcon" >Zoom Out</button>**
    <button data-dojo-type="dijit/form/Button"iconClass="zoomfullextIcon" >Full Extent</button>
    <button data-dojo-type ="dijit/form/Button"iconClass="zoomprevIcon" >Prev Extent</button>
    <button data-dojo-type="dijit/form/Button"iconClass="zoomnextIcon" >Next Extent</button>
    <button data-dojo-type="dijit/form/Button"iconClass="panIcon">Pan</button>
    <button data-dojo-type="dijit/form/Button"iconClass="deactivateIcon" >Deactivate</button>
  </div>
</div>

前面的代码块定义了工具栏上的按钮。每个按钮都是使用 Dijit(Dojo 的一个子项目)提供的Button用户界面控件创建的。每个控件都包含在网页的<body>标签内的<button>标签中,所有按钮都被包含在包含ContentPane dijit 的<div>标签中。

创建Navigation工具栏的实例

现在按钮的视觉界面已经完成,我们需要创建一个esri/toolbars/Navigation的实例,并连接事件和事件处理程序。创建Navigation类的实例就像调用构造函数并传入对Map的引用一样简单,很快你就会看到。但是,首先要确保添加对esri/toolbars/navigation的引用。以下代码示例添加了对Navigation工具栏的引用,创建了工具栏,将点击事件连接到按钮,并激活了工具。相关的代码行已经被突出显示和注释,以便你理解每个部分:

<script>
  var map, **toolbar**, symbol, geomTask;

    require([
      "esri/map", 
      **"esri/toolbars/navigation",**
      "dojo/parser", "dijit/registry",

    "dijit/layout/BorderContainer", "dijit/layout/ContentPane", 
      "dijit/form/Button", "dojo/domReady!"
      ], function(
      Map, **Navigation**,
      parser, registry
    ) {
      parser.parse();

    map = new Map("map", {
      basemap: "streets",
      center: [-15.469, 36.428],
      zoom: 3
      });

      map.on("load", createToolbar);

    **// loop through all dijits, connect onClick event**
 **// listeners for buttons to activate navigation tools**
      **registry.forEach(function(d) {**
 **// d is a reference to a dijit**
 **// could be a layout container or a button**
 **if ( d.declaredClass === "dijit.form.Button" ) {**
 **d.on("click", activateTool);**
 **}**
 **});**

    **//activate tools**
      **function activateTool() {**
 **var tool = this.label.toUpperCase().replace(/ /g, "_");**
 **toolbar.activate(Navigation[tool]);**
 **}**

      **//create the Navigation toolbar**
      **function createToolbar(themap) {**
 **toolbar = new Navigation(map);**

      });
    </script>

希望前面的Navigation工具栏示例已经说明了通过 JavaScript API 向你的 Web 地图应用程序添加导航工具栏的步骤。你不再需要担心添加 JavaScript 代码来绘制和处理范围矩形或捕获鼠标坐标进行平移操作。此外,工具栏的用户界面组件可以通过 Dijit 库提供的各种用户界面控件轻松创建。Draw类同样可以轻松支持在类似工具栏中绘制点、线和多边形。

用户界面小部件

JavaScript API 提供了许多开箱即用的小部件,可以在应用程序中使用以提高生产力。包括BasemapGalleryBookmarksPrintGeocoderGaugeMeasurementPopupLegendScalebarOverviewMapEditorDirectionsHistogramTimeSliderHomeButtonLayerSwipeLocateButtonTimeSliderAnalysis小部件。小部件与我们之前讨论的NavigationDraw工具栏的按钮和工具不同。这些小部件是开箱即用的功能,你只需几行代码就可以将它们放入应用程序中,而不是工具栏,后者只是需要大量 HTML、CSS 和 JavaScript 代码的辅助类。

BasemapGallery小部件

BasemapGallery小部件显示了来自ArcGIS.com的基础地图集合和/或用户定义的地图或图像服务。从集合中选择一个基础地图时,当前的基础地图将被移除,新选择的基础地图将出现。当向基础地图库添加自定义地图时,它们需要与库中的其他图层具有相同的空间参考。当使用ArcGIS.com的图层时,这将是 Web Mercator 参考,wkids 为 102100、102113 或 3857(wkids 是空间参考系统的唯一标识符)。出于性能原因,还建议所有基础地图都是切片图层。

BasemapGallery 小部件

创建BasemapGallery小部件时,可以在构造函数中提供一些参数,如前面的屏幕截图所示,包括显示 ArcGIS 底图、定义一个或多个自定义底图以包含在库中、提供 Bing 地图密钥以及地图的引用等。创建BasemapGallery小部件后,需要调用startup()方法来准备用户交互。看一下以下的代码片段:

require(["esri/dijit/Basemap", ... 
], function(Basemap, ... ) {
     var basemaps = [];
     var waterBasemap = new Basemap({
       layers: [waterTemplateLayer],
       title: "Water Template",
       thumbnailUrl: "images/waterThumb.png"
     });
     basemaps.push(waterBasemap);
...
});

在上一个代码示例中,创建了一个新的Basemap对象,其中包含标题、缩略图图像和一个包含单个图层的数组。然后将该Basemap对象推送到将添加到小部件中的底图数组中。

书签小部件

Bookmarks小部件用于向最终用户显示一组命名的地理范围。从小部件中点击书签名称将自动将地图范围设置为书签提供的范围。使用该小部件,您可以添加新书签,删除现有书签和更新书签。书签在 JavaScript 代码中定义为 JSON 对象,其中包含定义书签名称、范围和边界坐标的属性。要将书签添加到小部件中,您需要调用Bookmark.addBookmark()。看一下以下的屏幕截图:

书签小部件

然后看一下以下的代码片段:

require([
"esri/map", "esri/dijit/Bookmarks", "dojo/dom", ... 
], function(Map, Bookmarks, dom, ... ) {
     var map = new Map( ... );
     var bookmarks = new Bookmarks({
       map: map, 
       bookmarks: bookmarks
     }, dom.byId('bookmarks'));
...
});

在上一个代码示例中,创建了一个新的Bookmarks对象。它附加到地图,并添加了一个 JSON 格式的书签列表。

打印小部件

Print小部件是一个备受欢迎的工具,它简化了从 Web 应用程序打印地图的过程。它使用默认或用户定义的地图布局。该小部件需要使用 ArcGIS Server 10.1 或更高版本的导出 Web 地图任务。看一下以下的图:

打印小部件

然后看一下以下的代码片段:

require([
"esri/map", "esri/dijit/Print", "dojo/dom"... 
], function(Map, Print, dom, ... ) {
     var map = new Map( ... );
     var printer = new Print({
       map: map,
       url: "    http://servicesbeta4.esri.com/arcgis/rest/services/Utilities/ExportWebMap/GPServer/Export%20Web%20Map%20Task"
    }, dom.byId("printButton"));
...
});

在上一个代码示例中,创建了一个新的Print小部件。使用 URL 属性将小部件指向Print任务,并将小部件附加到页面上的 HTML 元素。

地理编码器小部件

地理编码器小部件允许您轻松地向应用程序添加地理编码功能。该小部件包括一个文本框,当最终用户开始输入地址时,结果会自动过滤。通过将autoComplete属性设置为true来启用自动完成。默认情况下,Geocoder小部件使用 ESRI World Locator 服务。您可以通过设置geocoder属性来更改这一点。看一下以下的屏幕截图:

地理编码器小部件

您还可以自动将值附加到用户输入的任何字符串中。例如,在本地应用程序中,您可能希望始终将特定城市和州附加到输入的任何地址中。这是通过suffix属性完成的。要使地图显示地理编码地址的位置,您可以将autoNavigate设置为true。从定位器返回多个潜在位置是完全可能的。您可以通过设置maxLocations属性来设置返回位置的最大数量。在接下来的练习中,您将学习如何将Geocoder小部件添加到您的应用程序中。

练习使用地理编码器小部件

在这个练习中,您将学习如何将Geocoder小部件添加到应用程序中。

  1. 打开 ArcGIS JavaScript API Sandbox,网址为developers.arcgis.com/en/javascript/sandbox/sandbox.html

  2. 修改<style>标签,使其显示如下:

<style>
html, body, #mapDiv {
height:100%;
width:100%;
margin:0;
padding:0;
}
body {
background-color:#FFF;
overflow:hidden;
font-family:"Trebuchet MS";
}
#search {
display: block;
position: absolute;
z-index: 2;
top: 20px;
left: 75px;
}
</style>
  1. <script>标签中删除以下 JavaScript 内容,如下所示:
<script>
**dojo.require("esri.map");**

**function init(){**
**var map = new esri.Map("mapDiv", {**
**center: [-56.049, 38.485],**
**zoom: 3,**
**basemap: "streets"**
 **});**
 **}**
**dojo.ready(init);**
</script>
  1. 您已经有一个用于地图的<div>容器。在此步骤中,您将创建第二个<div>标记,用作“地理编码”小部件的容器。按照以下突出显示的代码添加小部件的容器。确保为<div>标记指定特定的idsearch。这对应于我们在文件顶部定义的 CSS 样式,并在以下代码片段中突出显示。它将 HTML 的<div>标记连接到 CSS:
<body class="tundra">
  <**div id="search"></div>**
  <div id="mapDiv"></div>
</body>
  1. 创建变量来保存地图和geocoder对象,如下所示:
<script>
**var map, geocoder;**
</script>
  1. <script>标签中,添加require()函数并创建Map对象,如下所示:
<script>
var map, geocoder;

**require([**
 **"esri/map", "esri/dijit/Geocoder", "dojo/domReady!"**
 **], function(Map, Geocoder) {**
**map = new Map("mapDiv",{**
**basemap: "streets",**
**center:[-98.496,29.430], //long, lat**
**zoom: 13** 
 **});**
 **});**
</script>
  1. 按照以下方式创建地理编码小部件:
require([
    "esri/map", "esri/dijit/Geocoder", "dojo/domReady!"
  ], function(Map, Geocoder) {
    map = new Map("map",{
        basemap: "streets",
        center:[-98.496,29.430], //long, lat
        zoom: 13 
    });

 **var geocoder = new Geocoder({**
 **map: map,**
 **autoComplete: true,**
 **arcgisGeocoder: {**
 **name: "Esri World Geocoder",**
 **suffix: " San Antonio, TX"**
 **}**
 **},"search");**
 **geocoder.startup();**

});

整个脚本应如下所示:

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=7, IE=9,IE=10">
<meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no"/>
<title>Geocoding Widget API for JavaScript | SimpleGeocoding</title>
<link rel="stylesheet"href="http://js.arcgis.com/3.7/js/esri/css/esri.css">
<style>
html, body, #mapDiv {
height:100%;
width:100%;
margin:0;
padding:0;
      }
      #search {
display: block;
position: absolute;
z-index: 2;
top: 20px;
left: 74px;
      }
</style>
<script src="http://js.arcgis.com/3.7/"></script>
<script>
var map, geocoder;

require([
        "esri/map", "esri/dijit/Geocoder", "dojo/domReady!"
      ], function(Map, Geocoder) {
map = new Map("mapDiv",{
basemap: "streets",
center:[-98.496,29.430], //long, lat
zoom: 13 
        });

var geocoder = new Geocoder({
map: map,
autoComplete: true,
arcgisGeocoder: {
name: "Esri World Geocoder",
suffix: " San Antonio, TX"
          }
        },"search");
geocoder.startup();

      });
</script>
</head>
<body>
<div id="search"></div>
<div id="mapDiv"></div>
</body>
</html>
  1. 单击“运行”按钮执行代码。您应该看到类似以下屏幕截图的内容。注意“地理编码器”小部件。练习使用地理编码器小部件的时间到了

  2. 开始输入“圣安东尼奥,德克萨斯州”的地址。您可以使用1202 Sand Wedge作为示例。在开始输入地址时,自动完成应该开始。当您看到地址时,请从列表中选择它。小部件将对地址进行地理编码,并将地图定位,使地址位于地图的中心,如下面的屏幕截图所示:练习使用地理编码器小部件的时间到了

仪表小部件

“仪表”小部件在半圆仪表界面中显示来自FeatureLayerGraphicsLayer的数字数据。您可以定义仪表指示器的颜色、驱动仪表的数字数据的字段、标签字段、引用的图层、最大数据值、标题等。请查看以下屏幕截图:

仪表小部件

然后查看以下代码片段:

require([
  "esri/dijit/Gauge", ... 
], function(Gauge, ... ) {
var gaugeParams = {
    "caption": "Hurricane windspeed.",
    "color": "#c0c",
    "dataField": "WINDSPEED", 
    "dataFormat": "value",
    "dataLabelField": "EVENTID",
    "layer": fl, //fl previously defined as FeatureLayer
    "maxDataValue": 120, 
    "noFeatureLabel": "No name",
    "title": "Atlantic Hurricanes(2000)",
    "unitLabel": "MPH"
  };
var gauge = new Gauge(gaugeParams, "gaugeDiv");
  ...
});

前面的代码示例显示了创建“仪表”小部件。许多参数被传递到仪表的构造函数中,包括标题、颜色、数据字段、图层、最大数据值等。

测量小部件

“测量”小部件提供了三种工具,使最终用户能够测量长度和面积,并获取鼠标的坐标。请查看以下屏幕截图:

测量小部件

“测量”小部件还允许您更改测量单位,如下所示:

var measurement = new Measurement({
  map: map
}, dom.byId("measurementDiv"));
measurement.startup();

前面的代码示例显示了如何创建“测量”小部件的实例并将其添加到应用程序中。

弹出小部件

“弹出”小部件在功能上类似于默认的信息窗口,用于显示有关要素或图形的属性信息。实际上,从 API 的 3.4 版本开始,该小部件现在是显示属性的默认窗口,而不是infoWindow参数。但是,它还包含其他功能,如缩放和突出显示要素、处理多个选择以及最大化窗口的按钮。界面还可以使用 CSS 进行样式设置。请参考以下屏幕截图,作为“弹出”小部件中可以显示的内容的示例。

弹出小部件

从版本 3.4 开始,“弹出”小部件支持以从右到左RTL)方向呈现文本,以支持希伯来语和阿拉伯语等 RTL 语言。如果页面方向使用dir属性设置为 RTL,则 RTL 支持将自动应用。默认值为从左到右LTR)。请查看以下代码片段:

//define custom popup options
var popupOptions = {
  markerSymbol: new SimpleMarkerSymbol("circle", 32, null, new Color([0, 0, 0, 0.25])),
  marginLeft: "20", 
  marginTop: "20"
};
//create a popup to replace the map's info window
var popup = new Popup(popupOptions, dojo.create("div"));

map = new Map("map", {
  basemap: "topo",
  center: [-122.448, 37.788],
  zoom: 17,
  infoWindow: popup
});

在上一个代码示例中,创建了一个 JSONpopupOptions对象来定义弹出窗口的符号和边距。然后将此popupOptions对象传递给Popup对象的构造函数。最后,将Popup对象传递给infoWindow参数,该参数指定应将Popup对象用作信息窗口。

图例小部件

Legend小部件显示地图中一些或所有图层的标签和符号。它具有尊重比例依赖性的能力,以便在缩放应用程序时,图例值更新以反映各种比例范围下的图层可见性。Legend小部件支持ArcGISDynamicMapServiceLayerArcGISTiledMapServiceLayerFeatureLayer

图例小部件

创建Legend小部件的新实例时,可以指定控制图例内容和显示特性的各种参数。arrangement参数可用于指定图例在其容器 HTML 元素中的对齐方式,并可定义为左对齐或右对齐。autoUpdate属性可设置为truefalse,如果设置为true,则当地图比例尺发生变化或图层被添加或从地图中移除时,图例将自动更新其参数。layerInfos参数用于指定要在图例中使用的图层子集,respectCurrentMapScale可以设置为true,以根据每个图层的比例范围触发自动图例更新。最后,需要调用startup()方法来显示新创建的图例:

var layerInfo = dojo.map(results, function(layer,index){return {layer: layer.layer,title: layer.layer.name};});if(layerInfo.length > 0){var legendDijit = new Legend({map: map,layerInfos: layerInfo},"legendDiv");legendDijit.startup();}

上述代码示例显示了如何创建一个图例小部件并将其添加到应用程序中。

概览地图小部件

OverviewMap小部件用于在较大区域的上下文中显示主地图的当前范围。此概览地图在主地图范围更改时更新。主地图的范围在概览地图中表示为一个矩形。此范围矩形也可以拖动以更改主地图的范围。

概览地图可以显示在主地图的一个角落,并在不使用时隐藏。它也可以放置在主地图窗口之外的<div>元素内,或者临时最大化,以便轻松访问感兴趣的远程区域。看一下下面的屏幕截图:

概览地图小部件

OverviewMap小部件在对象的构造函数中接受许多可选参数。这些参数允许您控制概览地图相对于主地图的放置位置、用于概览地图的基础图层、范围矩形的填充颜色、最大化按钮的外观以及概览地图的初始可见性。看一下下面的代码片段:

var overviewMapDijit = new OverviewMap({map:map, visible:true});
overviewMapDijit.startup();

上述代码示例说明了创建OverviewMap小部件。

比例尺小部件

Scalebar小部件用于向地图或特定的 HTML 节点添加比例尺。Scalebar小部件以英制或公制值显示单位。从 API 的 3.4 版本开始,如果将scalebarUnits属性设置为dual,它可以同时显示英制和公制值。您还可以通过attachTo参数控制比例尺的位置。默认情况下,比例尺位于地图的左下角。看一下下面的屏幕截图:

比例尺小部件

然后看一下下面的代码片段:

var scalebar = new esri.dijit.Scalebar({map:map,scalebarUnit:'english'});

上述代码示例说明了使用英制单位创建Scalebar小部件。

方向小部件

Directions小部件使得计算两个或多个输入位置之间的方向变得容易。生成的方向,在下面的屏幕截图中显示,显示了详细的逐步转向说明和可选地图。如果地图与小部件关联,方向的路线和停靠点将显示在地图上。地图上显示的停靠点是交互式的,因此您可以单击它们以显示带有停靠点详细信息的弹出窗口,或者将停靠点拖动到新位置以重新计算路线。看一下下面的屏幕截图:

方向小部件

看一下下面的代码片段:

var directions = new Directions({
map: map
},"dir");

directions.startup();

上一个代码示例显示了创建Directions对象。

HistogramTimeSlider dijit

HistogramTimeSlider dijit 为地图上启用时间的图层提供了数据的直方图图表表示。通过 UI,用户可以使用TimeSlider小部件的扩展来临时控制数据的显示。

![HistogramTimeSlider dijit](graphics/7965OT_05_15.jpg)

看一下以下代码片段:

require(["esri/dijit/HistogramTimeSlider", ... ],
function(HistogramTimeSlider, ... ){
  var slider = new HistogramTimeSlider({
    dateFormat: "DateFormat(selector: 'date', fullYear: true)",
    layers : [ layer ],
    mode: "show_all",
    timeInterval: "esriTimeUnitsYears"
  }, dojo.byId("histogram"));
  map.setTimeSlider(slider);
});

在上一个代码示例中,创建了一个HistogramTimeSlider对象并将其与地图关联。

HomeButton 小部件

HomeButton小部件只是一个按钮,您可以将其添加到应用程序中,它将地图返回到初始范围。看一下以下截图:

![HomeButton 小部件](graphics/7965OT_05_16.jpg)

然后看一下以下代码片段:

require([
      "esri/map", 
**"esri/dijit/HomeButton"**,
      "dojo/domReady!"
    ], function(
      Map, **HomeButton**
    )  {

var map = new Map("map", {
center: [-56.049, 38.485],
zoom: 3,
basemap: "streets"
      });

**var home = new HomeButton({**
**map: map**
 **}, "HomeButton");**
**home.startup();**

    });

上一个代码示例显示了创建HomeButton小部件。

LocateButton 小部件

LocateButton小部件可用于查找并缩放到用户当前位置。此小部件使用地理位置 API 来查找用户当前位置。找到位置后,地图将缩放到该位置。该小部件提供选项,允许开发人员定义以下内容:

  • HTML5 地理位置提供了查找位置的选项,如maximumAgetimeouttimeout属性定义了用于确定设备位置的最长时间,而maximumAge属性定义了在找到设备的新位置之前的最长时间。

  • 定义自定义符号,用于在地图上突出显示用户当前位置的能力。

  • 找到位置后要缩放的比例。![LocateButton 小部件](graphics/7965OT_05_17.jpg)

看一下以下代码片段:

geoLocate = new LocateButton({
map: map,
highlightLocation: false
}, "LocateButton");
geoLocate.startup();

上一个代码示例显示了如何创建LocateButton小部件的实例并将其添加到地图中。

TimeSlider 小部件

TimeSlider小部件用于可视化启用时间的图层。 TimeSlider小部件配置为具有两个拇指,因此仅显示两个拇指位置的时间范围内的数据。 setThumbIndexes()方法确定每个拇指的初始位置。在这种情况下,在初始开始时间添加了一个拇指,另一个拇指定位在更高的时间步骤。看一下以下屏幕截图:

![TimeSlider 小部件](graphics/7965OT_05_18.jpg)

看一下以下代码片段:

var timeSlider = new TimeSlider({
style: "width: 100%;"
}, dom.byId("timeSliderDiv"));
map.setTimeSlider(timeSlider);

var timeExtent = new TimeExtent();
timeExtent.startTime = new Date("1/1/1921 UTC");
timeExtent.endTime = new Date("12/31/2009 UTC");
timeSlider.setThumbCount(2);
timeSlider.createTimeStopsByTimeInterval(timeExtent, 2, "esriTimeUnitsYears");
timeSlider.setThumbIndexes([0,1]);
timeSlider.setThumbMovingRate(2000);
timeSlider.startup

上面的代码示例说明了如何创建TimeSlider对象的实例并设置各种属性,包括开始和结束时间。

图层滑动小部件

LayerSwipe小部件提供了一个简单的工具,用于在地图顶部显示图层或图层的一部分。您可以使用此小部件在地图上显示一个或多个图层的内容,以便比较多个图层的内容。该小部件提供水平,垂直和范围查看模式。

![LayerSwipe 小部件](graphics/7965OT_05_19.jpg)

看一下以下代码片段:

varswipeWidget = new LayerSwipe({
type: "vertical",
map: map,
layers: [swipeLayer]
}, "swipeDiv");
swipeWidget.startup();

上一个代码示例显示了如何创建LayerSwipe的实例并将其添加到地图中。

分析小部件

在 ArcGIS API for JavaScript 的 3.7 版本中引入了许多新的分析小部件。分析小部件提供对 ArcGIS 空间分析服务的访问,允许您通过 API 对托管数据执行常见的空间分析。上一个屏幕截图显示了SummarizeNearby小部件的一部分,这是 12 个分析小部件之一。分析小部件包括以下 12 个小部件:

  • AnalysisBase

  • 聚合点

  • 创建缓冲区

  • 创建驾驶时间区域

  • 溶解边界

  • 丰富图层

  • 提取数据

  • 查找热点

  • 查找最近

  • 合并图层

  • 图层叠加

  • 附近总结

  • SummarizeWithin

需要ArcGIS.com订阅才能使用这些小部件。您不仅需要使用您的ArcGIS.com帐户存储数据,还需要登录以作为基于信用的服务运行分析作业。执行分析任务和托管要素服务对个人帐户用户不可用。

特性编辑

当使用企业地理数据库格式存储的数据时,ArcGIS API for JavaScript 支持简单要素编辑。这意味着您的数据需要存储在由 ArcSDE 管理的企业地理数据库中。

编辑工作基于“最后提交者获胜”的概念。例如,如果两个人正在编辑图层中的同一要素,并且两者都提交了修改,最后提交更改的编辑者将覆盖第一个编辑者所做的任何更改。显然,在某些情况下,这可能会造成问题,因此在实现应用程序中的编辑之前,您需要检查您的数据可能受到的影响。

编辑的其他特性包括对域和子类型的支持,模板样式编辑以及编辑独立表和附件的能力。要使用编辑选项,您需要使用FeatureServiceFeatureLayer。编辑请求通过 HTTP post 请求提交到服务器,大多数情况下需要使用代理。

编辑支持包括要素编辑,包括创建和删除简单要素,以及通过移动、切割、联合或重塑来修改要素的能力。此外,要素属性可以被编辑,文档可以附加到要素,并且可以向要素添加评论。

要素服务

Web 编辑需要要素服务来提供数据的符号和要素几何。要素服务只是启用了要素访问功能的地图服务。此功能允许地图服务以便于 Web 应用程序使用和更新的方式公开要素几何和它们的符号。

在构建 Web 编辑应用程序之前,您需要做一些工作来创建一个公开要进行编辑的图层的要素服务。这涉及设置地图文档,并可选择定义一些编辑模板。模板允许您预先配置一些常用要素类型的符号和属性。例如,为了准备编辑流,您可以为“主要河流”、“次要河流”、“小溪”和“支流”配置模板。模板是可选的,但它们使应用程序的最终用户轻松创建常见要素。

完成地图后,您需要将其发布到启用了要素访问功能的 ArcGIS Server。这将创建 REST URL 或端点,用于地图服务和要素服务。您将使用这些 URL 在应用程序中引用服务。

通过FeatureLayer对象,Web API 可以访问要素服务,我们在之前的章节中已经进行了检查。要素图层可以执行各种操作,并且可以引用地图服务或要素服务。但是,当您将FeatureLayer用于编辑目的时,您需要引用要素服务。

通过编辑功能,您的 Web 应用程序告诉FeatureLayer哪些属性已更改,以及(如果适用)几何图形如何更改。FeatureLayer对象还在编辑后显示更新的要素。您可以在要素图层上调用applyEdits()方法来应用编辑,然后将其提交到数据库。

编辑小部件

ArcGIS API for JavaScript 提供了小部件,使您更容易将编辑功能添加到您的 Web 应用程序中。这些小部件包括EditorTemplatePickerAttributeInspectorAttachmentEditor小部件。Editor小部件是默认的编辑界面,包括您编辑图层所需的一切,并允许您选择可用的工具的数量和类型。TemplatePicker显示一个预配置的模板,其中包含地图文档中每个图层的符号。这种模板样式编辑允许用户简单地选择一个图层并开始编辑。AttributeInspector小部件提供了一个界面,用于编辑要素的属性,并确保有效的数据输入。最后,AttachmentEditor将可下载文件与要素关联起来。我们将更详细地研究这些小部件。

编辑器小部件

Editor小部件显示在以下截图中,提供了 API 包含的默认编辑界面。它结合了其他小部件的功能,为您提供了编辑图层所需的一切。您可以选择小部件上可用的工具的数量和类型。

Editor小部件在进行编辑后立即保存您的编辑,例如,当您完成绘制一个点时。如果您决定不使用Editor小部件,您必须确定何时以及多久应用编辑。看一下以下截图:

编辑器小部件

在以下代码示例中,通过将params对象传递到构造函数中来创建一个新的Editor对象。输入的params对象是开发人员定义编辑应用程序功能的地方。在这种情况下,只定义了必需的选项。必需的选项是地图、要编辑的要素图层和几何服务的 URL。看一下以下代码片段:

var settings = {map: map,
  geometryService: new GeometryService("http://servicesbeta.esri.com/arcgis/rest/services/Geometry/GeometryServer"),layerInfos:featureLayerInfos};

var params = {settings: settings};**var editorWidget = new Editor(params);editorWidget.startup();**

Editor小部件使用要素服务中的可编辑图层提供开箱即用的编辑功能。它结合了开箱即用的TemplatePickerAttachmentEditorAttributeInspectorGeometryService,以提供要素和属性编辑。对于大多数编辑应用程序,您应该利用Editor小部件。该小部件允许您执行以下图表中列出的所有功能:

编辑器小部件

要在您的代码中使用Editor小部件,您需要首先使用dojo.require加载小部件。创建Editor的新实例所需的参数包括对Map对象和几何服务的引用。

模板选择器小部件

TemplatePicker小部件向用户显示了一组预配置的要素,每个要素都代表服务中的一个图层。通过从模板中选择一个符号,然后单击地图来添加要素,编辑可以非常简单地启动。模板中显示的符号来自您在要素服务的源地图中定义的编辑模板或应用程序中定义的符号。TemplatePicker也可以用作简单的图例。看一下以下截图:

模板选择器小部件

看一下以下代码片段:

function initEditing(results) {var templateLayers = dojo.map(results,function(result){return result.layer;});**var templatePicker = new TemplatePicker({featureLayers: templateLayers,grouping: false,rows: 'auto',columns: 3},'editorDiv');templatePicker.startup();**var layerInfos = dojo.map(results, function(result) {return {'featureLayer':result.layer};});var settings = {map: map,**templatePicker: templatePicker,**layerInfos:layerInfos};var params = {settings: settings};var editorWidget = new Editor(params);editorWidget.startup();}

在上一个代码示例中,创建了一个新的TemplatePicker对象并将其附加到Editor小部件上。

属性检查器小部件

如下截图所示,AttributeInspector小部件为在 Web 上编辑要素属性提供了一个界面。它还通过将输入与预期数据类型进行匹配来确保用户输入的数据有效。还支持域。例如,如果对字段应用了编码值域,则允许的值会出现在下拉列表中,限制了输入其他值的可能性。如果字段需要日期值,则会出现一个日历,帮助用户提供有效的日期。看一下以下截图:

AttributeInspector 小部件

AttributeInspector小部件公开了图层上所有可用的属性以供编辑。如果要限制可用属性,必须编写自己的界面来输入和验证值。看一下以下代码片段:

var layerInfos = [{
  'featureLayer': petroFieldsFL,
  'showAttachments': false,
  'isEditable': true,
  'fieldInfos': [
  {'fieldName': 'activeprod', 'isEditable':true, 'tooltip': 'Current Status', 'label':'Status:'},
  {'fieldName': 'field_name', 'isEditable':true, 'tooltip': 'The name of this oil field', 'label':'Field Name:'},
  {'fieldName': 'approxacre', 'isEditable':false,'label':'Acreage:'},
  {'fieldName': 'avgdepth', 'isEditable':false,'label':'Average Depth:'},
  {'fieldName': 'cumm_oil', 'isEditable':false,'label':'Cummulative Oil:'},
  {'fieldName': 'cumm_gas', 'isEditable':false,'label':'Cummulative Gas:'}
]
  }];

 **var attInspector = new AttributeInspector({**
 **layerInfos:layerInfos**
 **}, domConstruct.create("div"));**

  //add a save button next to the delete button
  var saveButton = new Button({ label: "Save", "class":"saveButton"});
 domConstruct.place(saveButton.domNode,attInspector.deleteBtn.domNode, "after");

saveButton.on("click", function(){
  updateFeature.getLayer().applyEdits(null, [updateFeature], null);    
});

**attInspector.on("attribute-change", function(evt) {**
 **//store the updates to apply when the save button is clicked** 
 **updateFeature.attributes[evt.fieldName] = evt.fieldValue;**
**});**

**attInspector.on("next", function(evt) {**
 **updateFeature = evt.feature;**
 **console.log("Next " + updateFeature.attributes.objectid);**
**});**

**attInspector.on("delete", function(evt){**
 **evt.feature.getLayer().applyEdits(null,null,[feature]);**
 **map.infoWindow.hide();**
**});**

map.infoWindow.setContent(attInspector.domNode);
map.infoWindow.resize(350, 240);

在上面的代码示例中,创建了一个AttributeInspector小部件并将其添加到应用程序中。此外,设置了几个事件处理程序,包括属性changenextdelete,以处理各种属性更改。

AttachmentEditor 小部件

在某些情况下,您可能希望将可下载文件与要素关联起来。例如,您可能希望用户能够单击代表水表的要素并看到指向水表图像的链接。在 ArcGIS Web API 中,这样一个关联的可下载文件称为要素附件。

如下面的屏幕截图所示,AttachmentEditor小部件是一个帮助用户上传和查看要素附件的小部件。AttachmentEditor小部件包括当前附件的列表(带有删除按钮),以及一个浏览按钮,可用于上传更多附件。AttachmentEditor小部件在信息窗口内工作良好,但也可以放置在页面的其他位置。

AttachmentEditor 小部件

为了使用要素附件,必须在源要素类上启用附件。您可以在 ArcCatalog 或 ArcMap 中的目录窗口中为要素类启用附件。如果Editor小部件检测到附件已启用,它将包括AttachmentEditor。看一下以下代码片段:

   var map;require(["esri/map","esri/layers/FeatureLayer",**"esri/dijit/editing/AttachmentEditor",**"esri/config","dojo/parser", "dojo/dom","dijit/layout/BorderContainer", "dijit/layout/ContentPane", "dojo/domReady!"], function(Map, FeatureLayer, **AttachmentEditor**, esriConfig,parser, dom) {parser.parse();// a proxy page is required to upload attachments// refer to "Using the Proxy Page" for more information:https://developers.arcgis.com/en/javascript/jshelp/ags_proxy.htmlesriConfig.defaults.io.proxyUrl = "/proxy";map = new Map("map", { basemap: "streets",center: [-122.427, 37.769],zoom: 17});map.on("load", mapLoaded);function mapLoaded() {var featureLayer = new FeatureLayer("http://sampleserver3.arcgisonline.com/ArcGIS/rest/services/SanFrancisco/311Incidents/FeatureServer/0",{mode: FeatureLayer.MODE_ONDEMAND});map.infoWindow.setContent("<div id='content' style='width:100%'></div>");map.infoWindow.resize(350,200);**var attachmentEditor = new AttachmentEditor({}, dom.byId("content"));attachmentEditor.startup();**featureLayer.on("click", function(evt) {var objectId = evt.graphic.attributes[featureLayer.objectIdField];map.infoWindow.setTitle(objectId);**attachmentEditor.showAttachments(evt.graphic,featureLayer);**map.infoWindow.show(evt.screenPoint, map.getInfoWindowAnchor(evt.screenPoint));});map.addLayer(featureLayer);}});

上面的代码显示了如何创建一个AttachmentEditor对象并将其添加到应用程序中。

编辑工具栏

有时您可能不想使用默认的Editor小部件,如下面的屏幕截图所示:

编辑工具栏

这些情况包括您希望编写自己的编辑逻辑,特别是关于客户端显示要素和图形的情况。在这些情况下,您可以使用编辑工具栏。编辑工具栏只是 API 的一部分,是一个 JavaScript 辅助类。它有助于放置和移动顶点和图形。这个工具栏类似于我们在本书前面讨论过的导航绘图工具栏。

总结

小部件和工具栏为您的应用程序提供了一种简单的方式来添加预构建的功能,而无需编写大量代码。可用小部件的范围在 API 的各个版本中不断增加,预计在未来的版本中将提供许多新的小部件。工具栏与小部件类似,是提供导航、绘图功能和编辑工具功能的辅助类。但是,开发人员需要定义工具栏和按钮的外观。在下一章中,您将学习如何使用QueryQueryTask类创建空间和属性查询。

第六章:执行空间和属性查询

使用 ArcGIS Server 查询任务,您可以对地图服务中已公开的数据图层执行属性和空间查询。您还可以组合这些查询类型以执行组合的属性和空间查询。例如,您可能需要找到所有土地地块的评估价值大于 10 万美元并与百年洪水平面相交的情况。这将是一个包含空间和属性组件的组合查询的示例。在本章中,您将学习如何使用 ArcGIS API for JavaScript 中的QueryQueryTaskFeatureSet对象执行属性和空间查询。

本章将涵盖以下主题:

  • 在 ArcGIS Server 中引入任务

  • 属性和空间查询概述

  • 查询对象

  • 使用 QueryTask 执行查询

  • 是时候练习空间查询了

在 ArcGIS Server 中引入任务

在本书的接下来的几章中,我们将讨论可以使用 ArcGIS API for JavaScript 执行的许多类型的任务。任务使您能够执行空间和属性查询,基于文本搜索查找要素,对地址进行地理编码,识别要素,并执行包括缓冲和距离测量在内的各种几何操作。所有任务都可以通过esri/tasks资源访问。

ArcGIS API for JavaScript 中的所有任务都遵循相同的模式。一旦您使用了一个或多个任务一段时间后,这种模式就很容易识别。输入对象用于向任务提供输入参数。使用这些输入参数,任务执行其特定功能,然后返回一个包含任务结果的输出对象。下图说明了每个任务如何接受输入参数对象并返回可在您的应用程序中使用的输出对象。

在 ArcGIS Server 中引入任务

属性和空间查询概述

正如您将在其他任务中看到的那样,查询是使用一系列对象执行的,这些对象通常包括任务的输入、任务的执行以及从任务返回的结果集。属性或空间查询的输入参数存储在一个包含可以为查询设置的各种参数的Query对象中。QueryTask对象使用Query对象中提供的输入执行任务,并以FeatureSet对象的形式返回结果集,其中包含一系列Graphic要素,然后您可以在地图上绘制这些要素。

Query对象作为QueryTask的输入,由包括geometrywheretext在内的属性定义。geometry属性用于输入将在空间查询中使用的几何,可以是点、线或多边形几何。where属性用于定义属性查询,而text属性用于执行包含like运算符的where子句。Query对象还可以包含许多可选属性,包括定义作为查询结果返回的字段、返回几何的输出空间参考以及满足查询条件的要素的实际几何。

属性和空间查询概述

上图定义了创建属性和空间查询时将使用的对象序列。

查询对象

为了使QueryTask对象对地图服务中的图层执行查询,它需要使用Query对象定义的输入参数。输入参数定义查询是空间、属性还是两者的组合。属性查询可以由wheretext属性定义。这些属性用于定义 SQL 属性查询。我们将在后面的部分中查看Query.whereQuery.text之间的区别。

空间查询要求您设置Query.geometry属性以定义要在空间查询中使用的输入几何形状。

可以通过构造函数创建Query对象的新实例,如下面的代码示例所示:

var query = new Query();

定义查询属性

正如我在本节的介绍中提到的,您可以在Query对象上设置各种参数。必须要么为属性查询(Query.whereQuery.text)定义属性,要么为空间查询定义Query.geometry属性。您还可以同时使用属性和空间查询属性。

属性查询

Query对象提供了两个属性,可以在属性查询中使用:Query.whereQuery.text。在下面的代码示例中,我设置了Query.where属性,以便只返回STATE_NAME字段等于'Texas'的记录。这只是一个标准的 SQL 查询。请注意,我用引号括起了 Texas 这个词。在对文本列执行属性查询时,您需要用单引号或双引号括起要评估的文本。如果您对包含其他数据类型(如数字或布尔值)的列执行属性查询,则不需要这样做:

query.where = "STATE_NAME = 'Texas'";

您还可以使用Query.text属性执行属性查询。这是一种使用like创建where子句的简便方法。查询中使用的字段是地图文档中定义的图层的显示字段。您可以在服务目录中确定图层的显示字段。下面的屏幕截图中说明了ZONING_NAME是显示字段。使用Query.text属性查询的就是这个显示字段。

属性查询

//Query.text uses the Display Name for the layer
query.text= stateName;

在下面的代码示例中,我们使用query.text执行属性查询,返回用户在网页上的表单字段中输入的州名的所有字段:

query = new Query();
query.returnGeometry = false;
query.outFields = ['*'];
query.text = dom.byId("stateName").value;
queryTask.execute(query, showResults);

空间查询

要对图层执行空间查询,您需要传递一个有效的几何对象用于空间过滤器,以及空间关系。有效的几何包括ExtentPointPolylinePolygon的实例。空间关系通过Query.spatialRelationship属性设置,并在查询期间应用。空间关系是通过以下常量值之一来定义的:SPATIAL_REL_INTERESECTSSPATIAL_REL_CONTAINSSPATIAL_REL_CROSSESSPATIAL_REL_ENVELOPE_INTERSECTSSPATIAL_REL_OVERLAPSSPATIAL_REL_TOUCHESSPATIAL_REL_WITHINSPATIAL_REL_RELATION。以下屏幕截图中的表描述了每个空间关系值:

空间查询

以下代码示例将Point对象设置为传递到空间过滤器中的几何体,并设置空间关系:

query.geometry = evt.mapPoint;
query.spatialRelationship = SPATIAL_REL_INTERSECTS;

限制返回的字段

出于性能原因,您应该限制在FeatureSet对象中返回的字段,只返回应用程序中需要的字段。附加到FeatureSet对象的每一列信息都是必须从服务器传递到浏览器的额外数据,这可能导致您的应用程序执行速度比应该慢。要限制返回的字段,您可以将包含应该返回的字段列表的数组分配给Query.outFields属性,如下面的代码示例所示。要返回所有字段,可以使用outFields = ['*']

此外,您可以通过Query.returnGeometry属性控制每个要素的几何返回。默认情况下,将返回几何;但是,在某些情况下,您的应用程序可能不需要几何。例如,如果您需要使用图层的属性信息填充表格,则不一定需要几何。在这种情况下,您可以设置Query.returnGeometry = false

query.outFields = ["NAME", "POP2000", "POP2007", "POP00_SQMI", "POP07_SQMI"];
query.returnGeometry = false;

使用 QueryTask 执行查询

一旦您在Query对象中定义了输入属性,就可以使用QueryTask执行查询。在执行查询之前,必须首先创建QueryTask对象的实例。通过在对象的构造函数中传递要对其执行查询的图层的 URL 来创建QueryTask对象。以下代码示例显示了如何创建QueryTask对象。请注意,它在 URL 的末尾包含一个索引编号,该索引编号引用地图服务中的特定图层进行查询:

myQueryTask = new QueryTask("http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/Demographics/ESRI_CENSUS_USA/MapServer/5");

创建后,QueryTask对象可用于使用QueryTask.execute()方法对具有输入Query对象的图层执行查询。QueryTask.execute()接受三个参数,包括输入的Query对象以及成功和错误回调函数。QueryTask.execute()的语法在以下代码中提供。输入的Query对象作为第一个参数传递:

QueryTask.execute(parameters,callback?,errback?)

假设查询在没有任何错误的情况下执行,将调用成功的回调函数,并将FeatureSet对象传递到函数中。如果在执行查询期间发生错误,则会执行错误回调函数。成功和错误回调函数都是可选的;但是,您应该始终定义函数来处理这两种情况。

此时,您可能想知道这些callbackerrback函数。ArcGIS Server 中的大多数任务返回dojo/Deferred的实例。Deferred对象是一个类,用作在Dojo中管理异步线程的基础。ArcGIS Server 中的任务可以是同步的,也可以是异步的。

异步和同步定义了客户端(使用任务的应用程序)与服务器交互并从任务中获取结果的方式。当服务设置为同步时,客户端等待任务完成。通常,同步任务执行速度快(几秒钟或更短)。异步任务通常需要更长时间来执行,客户端不等待任务完成。用户可以在任务执行时继续使用应用程序。当服务器上的任务完成时,它调用回调函数并将结果传递到该函数中,然后可以以某种方式使用这些结果。它们通常显示在地图上。

让我们看一个更完整的代码示例。在以下代码示例中,请注意我们首先创建一个名为myQueryTask的新变量,它指向ESRI_CENSUS_USA地图服务中的第 6 层(索引编号基于0)。然后,我们创建包含查询输入属性的Query对象,最后,我们使用QueryTask上的execute()方法执行查询。execute()方法返回一个包含查询结果的FeatureSet对象,并且这些要素通过在execute()方法中指定的showResults回调函数进行处理。如果在执行任务期间发生错误,则将调用errorCallback()函数:

**myQueryTask = new QueryTask("http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/Demographics/ESRI_CENSUS_USA/MapServer/5");**
//build query filter
myQuery = new Query();
myQuery.returnGeometry = false;
myQuery.outFields = ["STATE_NAME", "POP2007", "MALES", "FEMALES"];
myQuery.text = 'Oregon';
//execute query
**myQueryTask.execute(myQuery, showResults, errorCallback);**
function showResults(fs) {
    //do something with the results
    //they are returned as a featureset object
}

function errorCallback() {
  alert("An error occurred during task execution");
}

获取查询结果

正如我之前提到的,查询的结果存储在包含图形数组的FeatureSet对象中,如果需要,您可以在地图上绘制这些图形。

数组中的每个要素(图形)都可以包含几何、属性、符号和信息模板,如第三章中所述,“将图形添加到地图”。通常,这些要素被绘制在地图上作为图形。以下代码示例显示了在查询完成执行时执行的回调函数。FeatureSet对象被传递到回调函数中,并在地图上绘制图形:

function addPolysToMap(featureSet) {
  var features = featureSet.features;
  var feature;
  for (i=0, il=features.length; i<il; i++) {
    feature = features[i];
    attributes = feature.attributes;
    pop = attributes.POP90_SQMI;
    map.graphics.add(features[i].setSymbol(sym));
  }
}

练习空间查询的时间

在这个练习中,您将学习如何使用 ArcGIS API for JavaScript 中的QueryQueryTaskFeatureSet对象执行空间查询。使用波特兰市的 Zoning 图层,您将查询地块记录并在地图上显示结果。

执行以下步骤完成练习:

  1. developers.arcgis.com/en/javascript/sandbox/sandbox.html上打开 JavaScript 沙盒。

  2. 从以下代码片段中我标记的<script>标签中删除 JavaScript 内容:

<script>
**dojo.require("esri.map");**

**function init(){**
**var map = new esri.Map("mapDiv", {**
**center: [-56.049, 38.485],**
**zoom: 3,**
**basemap: "streets"**
 **});**
 **}**
**dojo.ready(init);**
</script>
  1. 创建应用程序中将使用的变量。
<script>
**var map, query, queryTask;**
**var symbol, infoTemplate**;
</script>
  1. 添加如下标记的require()函数:
<script>
  var map, query, queryTask;
  var symbol, infoTemplate;

 **require([**
 **"esri/map", "esri/tasks/query", "esri/tasks/QueryTask","esri/tasks/FeatureSet", "esri/symbols/SimpleFillSymbol",**
 **"esri/symbols/SimpleLineSymbol", "esri/InfoTemplate","dojo/_base/Color", "dojo/on", "dojo/domReady!"**
 **], function(Map, Query, QueryTask, FeatureSet,SimpleFillSymbol, SimpleLineSymbol, InfoTemplate, Color,on) {**

 **});**

</script>
  1. require()函数内部,创建将在应用程序中使用的Map对象。地图将以肯塔基州路易斯维尔市为中心:
require([
    "esri/map", "esri/tasks/query", "esri/tasks/QueryTask", "esri/tasks/FeatureSet", "esri/symbols/SimpleFillSymbol",
    "esri/symbols/SimpleLineSymbol", "esri/InfoTemplate", "dojo/_base/Color", "dojo/on", "dojo/domReady!"
  ], function(Map, Query, QueryTask, FeatureSet, SimpleFillSymbol, SimpleLineSymbol, InfoTemplate, Color, on) {

 **map = new Map("mapDiv",{**
 **basemap: "streets",**
 **center:[-85.748, 38.249], //long, lat**
 **zoom: 13** 
 **});**

})
  1. 创建将用于显示查询结果的符号:
require([
    "esri/map", "esri/tasks/query", "esri/tasks/QueryTask", "esri/tasks/FeatureSet", "esri/symbols/SimpleFillSymbol",
    "esri/symbols/SimpleLineSymbol", "esri/InfoTemplate", "dojo/_base/Color", "dojo/on", "dojo/domReady!"
  ], function(Map, Query, QueryTask, FeatureSet, SimpleFillSymbol, SimpleLineSymbol, InfoTemplate, Color, on) {
    map = new Map("map",{
      basemap: "streets",
      center:[-85.748, 38.249], //long, lat
      zoom: 13 
    });

  **symbol = new SimpleFillSymbol(SimpleFillSymbol.STYLE_SOLID,** 
 **new SimpleLineSymbol(SimpleLineSymbol.STYLE_SOLID, new Color([111, 0, 255]), 2), new Color([255,255,0,0.25]));**
 **infoTemplate = new InfoTemplate("${OBJECTID}", "${*}");**

});
  1. 现在,在require()函数内部,我们将初始化queryTask变量,然后注册QueryTask.complete事件。添加以下标记的代码行:
require([
    "esri/map", "esri/tasks/query", "esri/tasks/QueryTask", "esri/tasks/FeatureSet", "esri/symbols/SimpleFillSymbol",
    "esri/symbols/SimpleLineSymbol", "esri/InfoTemplate", "dojo/_base/Color", "dojo/on", "dojo/domReady!"
  ], function(Map, Query, QueryTask, FeatureSet, SimpleFillSymbol, SimpleLineSymbol, InfoTemplate, Color, on) {

    map = new Map("mapDiv",{
        basemap: "streets",
        center:[-85.748, 38.249], //long, lat
        zoom: 13 
    });

    symbol = new SimpleFillSymbol(SimpleFillSymbol.STYLE_SOLID,
    new SimpleLineSymbol(SimpleLineSymbol.STYLE_SOLID, new Color([111, 0, 255]), 2), new Color([255,255,0,0.25]));
    infoTemplate = new InfoTemplate("${OBJECTID}", "${*}");

 **queryTask = new QueryTask("http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/Louisville/LOJIC_LandRecords_Louisville/MapServer/2");**
 **queryTask.on("complete", addToMap);**

});

QueryTask的构造函数必须是指向通过地图服务公开的数据图层的有效 URL 指针。在这种情况下,我们正在创建对 LOJIC_LandRecords_Louisville 地图服务中的 Zoning 图层的引用。这表明我们将对该图层执行查询。如果您还记得之前的章节,dojo.on()用于注册事件。在这种情况下,我们正在为我们的新QueryTask对象注册complete事件。当查询完成时,此事件将触发,并且在这种情况下将调用作为on()参数指定的addToMap()函数。

  1. 现在我们将通过创建Query对象来定义任务的输入参数。在第一行中,我们创建一个新的Query实例,然后设置Query.returnGeometryQuery.outFields属性。将Query.returnGeometry设置为true表示 ArcGIS Server 应返回与查询匹配的要素的几何定义,而在Query.outFields中,我们指定了一个通配符,表示应返回与查询结果相关的 Zoning 图层的所有字段。在上一步中输入的代码下面添加以下标记的代码行:
require([
"esri/map", "esri/tasks/query", "esri/tasks/QueryTask", "esri/tasks/FeatureSet", "esri/symbols/SimpleFillSymbol",
"esri/symbols/SimpleLineSymbol", "esri/InfoTemplate", "dojo/_base/Color", "dojo/on", "dojo/domReady!"
], function(Map, Query, QueryTask, FeatureSet, SimpleFillSymbol, SimpleLineSymbol, InfoTemplate, Color, on) {
  map = new Map("mapDiv",{
      basemap: "streets",
      center:[-85.748, 38.249], //long, lat
      zoom: 13 
  });

  symbol = new SimpleFillSymbol(SimpleFillSymbol.STYLE_SOLID,
  new SimpleLineSymbol(SimpleLineSymbol.STYLE_SOLID, new Color([111, 0, 255]), 2), new Color([255,255,0,0.25]));
  infoTemplate = new InfoTemplate("${OBJECTID}", "${*}");

    queryTask = new QueryTask("http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/Louisville/LOJIC_LandRecords_Louisville/MapServer/2");
    queryTask.on("complete", addToMap);

 **query = new Query();**
 **query.returnGeometry = true;**
 **query.outFields = ["*"];**

});
  1. 添加一行代码,将Map.click事件注册到doQuery函数。doQuery函数将接收用户在地图上点击的点。这个地图点将被用作空间查询中的几何体。在下一步中,我们将创建doQuery函数,该函数将接受地图上点击的点:
require([
        "esri/map", "esri/tasks/query", "esri/tasks/QueryTask", "esri/tasks/FeatureSet", "esri/symbols/SimpleFillSymbol", 
        "esri/symbols/SimpleLineSymbol", "esri/InfoTemplate",  "dojo/_base/Color", "dojo/on", "dojo/domReady!"
        ], function(Map, Query, QueryTask, FeatureSet, SimpleFillSymbol, SimpleLineSymbol, InfoTemplate, Color, on) {

map = new Map("mapDiv",{
  basemap: "streets",
  center:[-85.748, 38.249], //long, lat
  zoom: 13 
});

symbol = new SimpleFillSymbol(SimpleFillSymbol.STYLE_SOLID, 
    new SimpleLineSymbol(SimpleLineSymbol.STYLE_SOLID, newColor([111, 0, 255]), 2), new Color([255,255,0,0.25]));
infoTemplate = new InfoTemplate("${OBJECTID}", "${*}");

**map.on("click", doQuery);**

queryTask = new QueryTask("http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/Louisville/LOJIC_LandRecords_Louisville/MapServer/2");
queryTask.on("complete", addToMap);

**query = new Query();**
**query.returnGeometry = true;**
**query.outFields = ["*"];**

});
  1. 现在我们将创建doQuery函数,该函数使用在require()函数中设置的Query属性以及用户在地图上点击的地图点执行QueryTaskdoQuery函数接受在地图上点击的点,可以使用mapPoint属性检索。mapPoint属性返回一个Point对象,然后用于设置Query.geometry属性,该属性将用于查找用户在地图上点击的分区地块。最后,执行QueryTask.execute()方法。任务执行后,将返回包含与查询匹配的记录的FeatureSet对象。现在的问题是结果返回在哪里?在require()函数的闭合大括号下面添加以下代码块:
function doQuery(evt) {
    //clear currently displayed results
    map.graphics.clear();

    query.geometry = evt.mapPoint;
    query.outSpatialReference = map.spatialReference;
    queryTask.execute(query);
}
  1. 记住,我们注册了QueryTask.complete事件来运行addToMap()函数。我们还没有创建这个函数。添加以下代码来创建addToMap()函数。此函数将接受作为查询结果返回的FeatureSet对象,并在地图上绘制要素。还要注意为要素定义了信息模板。这将创建一个InfoWindow对象来显示返回要素的属性:
function addToMap(results) {
  var featureArray = results.featureSet.features;
  var feature = featureArray[0];
  map.graphics.add(feature.setSymbol(symbol).setInfoTemplate(infoTemplate));
}

您可以在spatialquery.html文件中查看此练习的解决方案代码。

  1. 单击运行按钮来执行代码。您应该会看到以下截图中的地图。如果没有,请检查您的代码是否准确。

单击地图上的任意位置来运行查询。您应该会看到高亮显示的分区多边形,类似于您在以下截图中看到的:

Time to practice with spatial queries

现在,单击高亮显示的分区多边形以显示详细的信息窗口,其中包含与多边形相关联的属性。

Time to practice with spatial queries

在刚刚完成的任务中,您学会了如何使用QueryQueryTask对象创建一个空间查询,以定位用户在地图上点击的点所相交的分区多边形。

摘要

在这一章中,我们介绍了 ArcGIS Server 中任务的概念。ArcGIS Server 为 Web 地图应用程序中常用操作提供了许多任务。属性和空间查询是 Web 地图应用程序中常见的操作。为了支持这些查询,ArcGIS API for JavaScript 提供了一个QueryTask对象,可以用来在服务器上执行这些查询。创建时,QueryTask对象接受一个指向地图服务器中将被查询的图层的 URL。通过Query对象提供了QueryTask的各种输入参数。输入参数可以包括where属性来执行属性查询,geometry属性来执行空间查询,outFields属性来定义应该返回的字段集,以及其他一些支持属性。在服务器上完成查询后,将FeatureSet对象返回给应用程序中定义的回调函数。回调函数可以显示FeatureSet(它只是一个Graphic对象数组)在地图上。在下一章中,您将学习如何使用另外两个任务:IdentifyTaskFindTask。两者都可以用来返回要素的属性。

第七章:识别和查找要素

在本章中,我们将介绍与返回要素属性相关的两个 ArcGIS Server 任务:IdentifyTask 和 FindTask。识别要素是 GIS 应用程序中的另一个常见操作。此任务返回在地图上单击的要素的属性。属性信息通常显示在弹出窗口中。通过 ArcGIS API for JavaScript 的 IdentifyTask 类实现此功能。与我们所见的其他任务过程一样,IdentifyTask 对象使用输入参数对象,本例中称为 IdentifyParameters。IdentifyParameters 对象包含各种参数,用于控制识别操作的结果。这些参数使您能够对单个图层、服务中的最顶层图层、服务中的所有可见图层或服务中的所有图层以及搜索容差执行识别。IdentifyResult 的实例用于保存任务的结果。

您可以使用 ArcGIS API for JavaScript 执行一些在 ArcGIS Desktop 中最常用的功能的任务。FindTask 就是这样一个工具。与 ArcGIS 桌面版本一样,此任务可用于在图层中查找与字符串值匹配的要素。在使用 FindTask 对象执行查找操作之前,您需要在 FindParameters 的实例中设置操作的各种参数。FindParameters 使您能够设置各种选项,包括搜索文本、要搜索的字段等。使用 FindParameters 对象,FindTask 然后针对一个或多个图层和字段执行其任务,然后返回包含与搜索字符串匹配的 layerID、layerName 和要素的 FindResult 对象。

在本章中,我们将涉及以下主题:

  • 使用 IdentifyTask 获取要素属性

  • 使用 FindTask 获取要素属性

使用 IdentifyTask 获取要素属性

使用 IdentifyTask 可以返回图层中字段的属性到您的应用程序。在本节中,您将学习如何使用与 IdentifyTask 相关的各种对象来返回此信息。

介绍 IdentifyTask

与 ArcGIS Server 中的其他任务一样,IdentifyTask 功能在 API 中分为三个不同的类,包括 IdentifyParameters,IdentifyTask 和 IdentifyResult。这三个类如下图所示:

介绍 IdentifyTask

IdentifyParameters 对象

IdentifyTask 的输入参数对象是 IdentifyParameters。使用 IdentifyParameters 类可以为您的识别操作设置多个属性。参数包括用于选择要素的几何(IdentifyParameters.geometry)、要执行识别的图层 ID(IdentifyParameters.layerIds)以及在其中执行识别的指定几何的容差(IdentifyParameters.tolerance)。

在您可以使用 ArcGIS Server 提供的识别功能之前,您需要导入如下所示的识别资源。

require(["esri/tasks/IdentifyTask", ... ], function(IdentifyTask,... ){ ... });

在 IdentifyParameters 对象上设置各种参数之前,您需要首先创建此对象的实例。可以使用如下所示的代码完成此操作。此构造函数的代码不接受任何参数:

var identifyParams = new IdentifyParameters();

现在您已经创建了 IdentifyParameters 的新实例,可以设置如下所示的各种属性:

identifyParams.geometry = evt.MapPoint;identifyParams.layerIds[0,1,2]; 
identifyParams.returnGeometry = true;identifyParams.tolerance = 3; 

在大多数情况下,使用用户在地图上单击的点执行识别操作。您可以使用从地图单击事件返回的点来获取这一点,就像在前面的代码示例中所看到的那样。应该搜索的图层可以使用图层 ID 数组来定义,这些 ID 被传递到IdentifyParameters.layerIds属性中。数组应包含引用要搜索的图层的数值。您可以通过查看服务目录来获取图层索引号。容差属性尤为重要。它设置了几何图形周围的像素距离。请记住,大多数情况下几何图形将是一个点,因此您可以将其视为在您设置的任何容差值周围放置的圆。该值将以屏幕像素为单位。执行IdentifyTask属性时,将返回任何在或与圆内的要识别的图层中的要素。

您可能需要尝试不同的容差值,以获得最适合您的应用程序的值。如果值设置得太低,您可能面临识别操作未识别任何要素的风险,反之,如果值设置得太高,您可能会得到太多的要素返回。找到合适的平衡可能很困难,对一个应用程序有效的容差值可能对另一个应用程序无效。

IdentifyTask属性

IdentifyTask使用IdentifyParameters中指定的参数在一个或多个图层上执行识别操作。与我们已经检查过的其他任务一样,IdentifyTask需要一个指向标识要在识别操作中使用的地图服务的 URL 的指针。

IdentifyTask的新实例可以使用以下代码示例创建。该任务的构造函数简单地接受一个指向包含可以执行识别操作的图层的地图服务的 URL。

var identify =new IdentifyTask("http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/Specialty/ESRI_StatesCitiesRivers_USA/MapServer");

一旦创建了IdentifyTask对象的新实例,您可以通过IdentifyTask.execute()方法启动执行此任务,该方法接受一个IdentifyParameters对象以及可选的success回调和error回调函数。在以下代码示例中,调用了IdentifyTask.execute()方法。将IdentifyParameters的实例作为参数传递到该方法中,并引用一个addToMap()方法,该方法将处理返回给该方法的结果。

identifyParams = new IdentifyParameters();
identifyParams.tolerance = 3;
identifyParams.returnGeometry = true;
identifyParams.layerIds = [0,2];
identifyParams.geometry = evt.mapPoint;

**identifyTask.execute(identifyParams, function(idResults) { addToMap(idResults, evt); });**

**function addToMap(idResults, evt) {**
 **//add the results to the map**
**}**

使用IdentifyTask执行的识别操作的结果存储在IdentifyResult的实例中。我们将在下一节中检查这个结果对象。

IdentifyResult

IdentifyTask操作返回的结果是IdentifyResult对象的数组。每个IdentifyResult对象包含从识别操作返回的要素,以及找到该要素的图层 ID 和图层名称。以下代码说明了如何通过回调函数处理IdentifyResult对象数组:

function addToMap(**idResults**, evt) {
  bldgResults = {displayFieldName:null,features:[]};
  parcelResults = {displayFieldName:null,features:[]};
  for (vari=0, **i<idResults.length**; i++) {
 **var idResult = idResults[i];**
    if (**idResult.layerId === 0**) {
      if (!bldgResults.displayFieldName) 
        {bldgResults.displayFieldName = idResult.displayFieldName};
        bldgResults.features.push(**idResult.feature**);
      }
    else if (**idResult.layerId === 2**) {
        if (!parcelResults.displayFieldName)
         {parcelResults.displayFieldName = idResult.displayFieldName};
         parcelResults.features.push(**idResult.feature**);
       }
    }
dijit.byId("bldgTab").setContent(layerTabContent(bldgResults,"bldgResults"));
dijit.byId("parcelTab").setContent(layerTabContent(parcelResults,"parcelResults"));
map.infoWindow.show(evt.screenPoint,
map.getInfoWindowAnchor(evt.screenPoint));
}

练习时间-实现标识功能

在这个练习中,您将学习如何在应用程序中实现标识功能。您将创建一个简单的应用程序,当用户单击地图时,它将在信息窗口中显示建筑物和土地包裹的属性信息。我们已经为您预先编写了一些代码,这样您就可以专注于与要素识别直接相关的功能。在我们开始之前,我会让您将预先编写的代码复制并粘贴到沙箱中。

执行以下步骤完成练习:

  1. developers.arcgis.com/en/javascript/sandbox/sandbox.html打开 JavaScript 沙箱。

  2. 从我在以下代码片段中突出显示的<script>标签中删除 JavaScript 内容:

<script>
**dojo.require("esri.map");**

**function init(){**
**var map = new esri.Map("mapDiv", {**
**center: [-56.049, 38.485],**
**zoom: 3,**
**basemap: "streets"**
 **});**
 **}**
**dojo.ready(init);**
</script>
  1. 创建您将在应用程序中使用的变量:
<script>
**var map;**
**var identifyTask, identifyParams;**
</script>
  1. 创建require()函数,定义您将在此应用程序中使用的资源:
<script>
  var map;
var identifyTask, identifyParams;
**require([**
 **"esri/map",  "esri/dijit/Popup","esri/layers/ArcGISDynamicMapServiceLayer","esri/tasks/IdentifyTask",** 
 **"esri/tasks/IdentifyResult","esri/tasks/IdentifyParameters","esri/dijit/InfoWindow","esri/symbols/SimpleFillSymbol",** 
 **"esri/symbols/SimpleLineSymbol","esri/InfoTemplate", "dojo/_base/Color" ,"dojo/on",**
 **"dojo/domReady!"**
 **], function(Map, Popup, ArcGISDynamicMapServiceLayer,IdentifyTask, IdentifyResult, IdentifyParameters,InfoWindow,** 
 **SimpleFillSymbol, SimpleLineSymbol, InfoTemplate,Color, on) {**

 **});**
</script>
  1. 创建Map对象的新实例:
<script>
  var map;
var identifyTask, identifyParams;
require([
    "esri/map",  "esri/dijit/Popup","esri/layers/ArcGISDynamicMapServiceLayer","esri/tasks/IdentifyTask", 
    "esri/tasks/IdentifyResult","esri/tasks/IdentifyParameters","esri/dijit/InfoWindow","esri/symbols/SimpleFillSymbol", 
    "esri/symbols/SimpleLineSymbol", "esri/InfoTemplate", "dojo/_base/Color" ,"dojo/on",
      "dojo/domReady!"
      ], function(Map, Popup, ArcGISDynamicMapServiceLayer,IdentifyTask, IdentifyResult, IdentifyParameters,InfoWindow, 
  SimpleFillSymbol, SimpleLineSymbol, InfoTemplate, Color,on) {
    //setup the popup window 
var popup = new Popup({
fillSymbol: new SimpleFillSymbol(SimpleFillSymbol.STYLE_SOLID,new SimpleLineSymbol(SimpleLineSymbol.STYLE_SOLID,new Color([255,0,0]), 2), new Color([255,255,0,0.25]))
        }, dojo.create("div"));

**map = new Map("map", {**
 **basemap: "streets",**
 **center: [-83.275, 42.573],**
 **zoom: 18,**
 **infoWindow: popup**
**});**

    });
</script>
  1. 创建一个新的动态地图服务图层并将其添加到地图中:
map = new Map("map", {
  basemap: "streets",
  center: [-83.275, 42.573],
  zoom: 18,
  infoWindow: popup
});

var landBaseLayer = new ArcGISDynamicMapServiceLayer("http://sampleserver3.arcgisonline.com/ArcGIS/rest/services/BloomfieldHillsMichigan/Parcels/MapServer",{opacity:.55});
map.addLayer(landBaseLayer);
  1. 添加一个Map.click事件,将触发执行一个函数,当地图被点击时将会响应:
map = new Map("map", {
  basemap: "streets",
  center: [-83.275, 42.573],
  zoom: 18,
  infoWindow: popup
});

varlandBaseLayer = new ArcGISDynamicMapServiceLayer("http://sampleserver3.arcgisonline.com/ArcGIS/rest/services/BloomfieldHillsMichigan/Parcels/MapServer",{opacity:.55});
map.addLayer(landBaseLayer);

**map.on("click", executeIdentifyTask);**

  1. 创建一个IdentifyTask对象:
  identifyTask = newIdentifyTask("http://sampleserver3.arcgisonline.com/ArcGIS/rest/services/BloomfieldHillsMichigan/Parcels/MapServer");
  1. 创建一个IdentifyParameters对象并设置各种属性:
identifyTask = newIdentifyTask("http://sampleserver3.arcgisonline.com/ArcGIS/rest/services/BloomfieldHillsMichigan/Parcels/MapServer");

**identifyParams = new IdentifyParameters();**
**identifyParams.tolerance = 3;**
**identifyParams.returnGeometry = true;**
**identifyParams.layerIds = [0,2];**
**identifyParams.layerOption = IdentifyParameters.LAYER_OPTION_ALL;**
**identifyParams.width  = map.width;**
**identifyParams.height = map.height;**

  1. 创建executeIdentifyTask()函数,该函数是响应Map.click事件的函数。在之前的步骤中,您已经为Map.click事件设置了事件处理程序。executeIdentifyTask()函数被指定为处理此事件发生时的 JavaScript 函数。在此步骤中,您将通过添加以下代码来创建此函数。executeIdentifyTask()函数接受一个参数,即Event对象的实例。每个事件都会生成一个Event对象,该对象具有各种属性。在Map.click事件的情况下,此Event对象具有包含被点击的点的属性。这可以通过Event.mapPoint属性检索,并在设置IdentifyParameters.geometry属性时使用。IdentifyTask.execute()方法还返回一个Deferred对象。然后,您将一个回调函数添加到此Deferred对象中,该函数解析结果。添加以下代码以创建executeIdentifyTask()函数。此函数应该在require()函数之外创建:
function executeIdentifyTask(evt) {
        identifyParams.geometry = evt.mapPoint;
        identifyParams.mapExtent = map.extent;

        var deferred = identifyTask.execute(identifyParams);

        deferred.addCallback(function(response) {     
          // response is an array of identify result objects    
          // Let's return an array of features.
          return dojo.map(response, function(result) {
            var feature = result.feature;
            feature.attributes.layerName = result.layerName;
            if(result.layerName === 'Tax Parcels'){
              console.log(feature.attributes.PARCELID);
              var template = new esri.InfoTemplate("", "${PostalAddress} <br/> Owner of record: ${First OwnerName}");
              feature.setInfoTemplate(template);
            }
            else if (result.layerName === 'Building Footprints'){
              var template = new esri.InfoTemplate("", "Parcel ID:${PARCELID}");
              feature.setInfoTemplate(template);
            }
            return feature;
          });
        });

// InfoWindow expects an array of features from each deferred
// object that you pass. If the response from the task execution 
// above is not an array of features, then you need to add acallback
// like the one above to post-process the response and return an
        // array of features.
        map.infoWindow.setFeatures([ deferred ]);
        map.infoWindow.show(evt.mapPoint);
      }
  1. 您可能希望查看您的ArcGISJavaScriptAPI文件夹中的解决方案文件(identify.html),以验证您的代码是否已正确编写。

  2. 通过单击Run按钮执行代码,如果一切编码正确,您应该看到以下输出:练习时间-实现识别功能

使用FindTask获取要素属性

您可以使用FindTask根据字符串值搜索由 ArcGIS Server REST API 公开的地图服务。搜索可以在单个图层的单个字段上进行,也可以在图层的多个字段上进行,或者在多个图层的多个字段上进行。与我们已经检查过的其他任务一样,查找操作由三个互补的对象组成,包括FindParametersFindTaskFindResultFindParameters对象充当输入参数对象,由FindTask用于完成其工作,而FindResult包含任务返回的结果。看一下以下图:

使用获取要素属性

FindParameters

FindParameters对象用于指定查找操作的搜索条件,并包括一个searchText属性,其中包括将要搜索的文本,以及指定要搜索的字段和图层的属性。除此之外,将returnGeometry属性设置为true表示您希望返回与查找操作匹配的要素的几何,并可用于突出显示结果。

以下代码示例显示了如何创建FindParameters的新实例并分配各种属性。在使用与查找操作相关的任何对象之前,您需要导入esri/tasks/find resourcesearchText属性定义了将在字段之间搜索的字符串值,该字段在searchFields属性中定义。将要搜索的图层通过分配给layerIds属性的索引号数组来定义。索引号对应于地图服务中的图层。geometry属性定义了是否应在结果中返回要素的几何定义。有时您可能不需要要素的几何,例如当属性只需在表内填充时。在这种情况下,您将把geometry属性设置为false

var findParams = new FindParameters();
findParams.searchText = dom.byId("ownerName").value;
findParams.searchFields = ["LEGALDESC","ADDRESS"]; //fields to search
findParams.returnGeometry = true;
findParams.layerIds = [0]; //layers to use in the find
findParams.outSpatialReference = map.spatialReference;

您可以使用contains属性来确定是否要查找搜索文本的精确匹配。如果设置为true,它将搜索包含searchText属性的值。这是一个不区分大小写的搜索。如果设置为false,它将搜索searchText字符串的精确匹配。精确匹配是区分大小写的。

FindTask

FindTask在上图中执行了对FindParameters中指定的图层和字段进行查找操作,并返回一个FindResult对象,其中包含找到的记录。看一下以下代码片段:

findTask = new FindTask("http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/TaxParcel/TaxParcelQuery/MapServer/");
findTask.execute(findParams,showResults);

function showResults(results) {
    //This function processes the results
}

就像QueryTask一样,您必须指定一个指向将在查找操作中使用的地图服务的 URL 指针,但您不需要包括指定要使用的确切数据图层的整数值。这是不必要的,因为在FindParameters对象中定义了要在查找操作中使用的图层和字段。创建后,您可以调用FindTask.execute()方法来启动查找操作。FindParameters对象作为第一个参数传递到此方法中,您还可以定义可选的successerror回调函数。这在上面的代码示例中显示。success回调函数传递了一个FindResults的实例,其中包含了查找操作的结果。

FindResult

FindResult包含FindTask操作的结果,还包含可以表示为图形的要素,找到要素的图层 ID 和名称,以及包含搜索字符串的字段名称。看一下以下代码片段:

function showResults(results) {
//This function works with an array of FindResult that the taskreturns
  map.graphics.clear();
  var symbol = new SimpleFillSymbol(SimpleFillSymbol.STYLE_SOLID, 
  new SimpleLineSymbol(SimpleLineSymbol.STYLE_SOLID,
  new Color([98,194,204]), 2), new Color([98,194,204,0.5]));
  //create array of attributes
  var items = array.map(results,function(result){
    var graphic = result.feature;
    graphic.setSymbol(symbol);
    map.graphics.add(graphic);
    return result.feature.attributes;
  });
  //Create data object to be used in store
  var data = {
    identifier: "PARCELID", //This field needs to have unique values
    label: "PARCELID", //Name field for display. Not pertinent toagrid but may be used elsewhere.
    items: items
  };
  //Create data store and bind to grid.
  store = new ItemFileReadStore({ data:data });
  var grid = dijit.byId('grid');
  grid.setStore(store);
  //Zoom back to the initial map extent
  map.centerAndZoom(center, zoom);
}

摘要

与要素相关的属性的返回是 GIS 中最常见的操作之一。ArcGIS Server 有两个可以返回属性的任务:IdentifyTaskFindTaskIdentifyTask属性用于返回在地图上单击的要素的属性。FindTask也返回要素的属性,但使用简单的属性查询来返回属性。在本章中,您学习了如何使用 ArcGIS API for JavaScript 来使用这两个任务。在下一章中,您将学习如何使用Locator任务执行地理编码和反向地理编码。

第八章:将地址转换为点和将点转换为地址

在 Web 地图应用程序中,绘制地址或感兴趣点在地图上是最常用的功能之一。要在地图上将地址绘制为一个点,首先需要获取纬度和经度坐标。地理编码是将物理地址转换为地理坐标的过程。为了将您的地址添加到地图上,它们必须经过一个将坐标分配给地址的地理编码过程。在 ArcGIS Server 中,地理编码是通过使用定位器服务来实现的,并通过 ArcGIS Server JavaScript API 中的Locator类来执行,该类访问这些服务以提供地址匹配功能以及反向地理编码。与 ArcGIS Server 提供的其他任务一样,地理编码需要各种输入参数,包括一个Address对象来匹配地址,或者在反向地理编码的情况下是一个Point对象。然后,这些信息被提交到地理编码服务,并返回一个包含地址匹配的AddressCandidate对象,然后可以在地图上绘制。

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

  • 介绍地理编码

  • 在 ArcGIS API for JavaScript 中使用定位器服务进行地理编码

  • 地理编码过程

  • 反向地理编码过程

  • 练习定位器服务的时间

介绍地理编码

我们首先来看一个地理编码的例子,以便让您更好地了解这个过程。如果您有一个位于 Main St 150 号的地址,您必须先对该地址进行地理编码,然后才能将其绘制为地图上的一个点。如果 150 Main St 位于一个地址范围为 100 到 200 Main St 的街道段上,地理编码过程将会插值 150 Main St 的位置,使其正好位于这个街道段的中间。然后,地理编码软件将 150 Main St 分配给对应于 100 和 200 Main St 之间中点的地理位置。现在您已经有了该地址的坐标,可以在地图上绘制它。这个过程在下图中描述:

介绍地理编码

最常见的地理编码级别是街道段地理编码,它根据已知的地理编码在包含地址的街区或街道段的交叉口上分配纬度/经度坐标。这种地理编码方法使用了前面描述的插值过程。这种方法在地址间隔规则的城市地区中最准确。然而,它在准确地地理编码间隔不规则的地址和位于死胡同中的地址时存在问题。农村地区的坐标也因为不完整而臭名昭著,这导致这些地区的地理编码率较低。

在 ArcGIS API for JavaScript 中使用定位器服务进行地理编码

ArcGIS Server 的Locator服务可以执行地理编码和反向地理编码。使用 ArcGIS Server API for JavaScript,您可以将地址提交给Locator服务,并检索地址的地理坐标,然后可以在地图上绘制。以下图示了这个过程。一个由 JavaScript 中的 JSON 对象定义的地址是Locator对象的输入,它对地址进行地理编码,并将结果返回到一个AddressCandidate对象中,然后可以在地图上显示为一个点。这种模式与我们在前几章中看到的其他任务相同,其中一个输入对象(Address对象)为任务(Locator)提供输入参数,该任务将作业提交给 ArcGIS Server。然后,结果对象(AddressCandidate)被返回到一个回调函数中,该函数处理返回的数据。

在 ArcGIS API for JavaScript 中使用定位器服务进行地理编码

输入参数对象

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

输入 JSON 地址对象

Locator服务可以接受Point(用于反向地理编码)或代表地址的JSON对象。JSON 对象定义了一个以对象形式格式化的地址,如下面的代码示例所示。该地址被定义为一系列在括号内定义的名称/值对,在这种情况下,名称/值对为街道、城市、州和邮政编码,但名称/值对将根据您在定位器中定义的地理编码服务的类型而变化。

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

输入 Point 对象

对于反向地理编码,Locator服务的输入采用esri/geometry/Point对象的形式,通常是通过用户在地图上的点击或应用程序逻辑来定义。Point对象通过Map.click事件返回,可以被检索并用作Locator服务的输入对象。

定位器对象

Locator类包含可用于使用输入PointAddress对象执行地理编码或反向地理编码操作的方法和事件。Locator需要一个指向您在 ArcGIS Server 中定义的地理编码服务的 URL 指针。下面是一个代码示例,展示了如何创建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 对象

AddressCandidate对象是Locator操作的结果。该对象中存储了各种属性,包括地址、属性、位置和分数。属性属性包含字段名称和值的名称/值对。位置是候选地址的 x 和 y 坐标。分数属性是一个介于 0 和 100 之间的数值,表示地址的质量,得分越高,表示匹配度越好。多个地址可以存储在该对象中作为候选对象数组。

现在,我们将更仔细地查看用于提交地址和点的定位器方法。Locator.addressToLocations()方法发送一个请求来对单个地址进行地理编码。创建一个输入地址对象,并将其用作Locator对象上找到的addressToLocations()方法的参数。地理编码操作的结果以AddressCandidate对象的形式返回。然后可以将地址作为图形绘制在地图上。

反向地理编码也可以通过Locator对象的locationToAddress()方法执行。通过地图上的用户点击或应用程序逻辑创建的Point对象被创建并作为参数传递到locationToAddress()方法中。还会传递第二个参数到该方法中,指示应从距离点多少米的地方找到匹配的地址。与addressToLocations()方法一样,Locator返回一个AddressCandidate对象,并包含一个地址(如果找到的话)。

地理编码过程

我们可以用 ArcGIS API for JavaScript 总结地理编码过程。通过引用 ArcGIS Server 实例上的地理编码服务,创建了一个Locator对象。然后,以 JSON 对象形式创建的输入地址通过addressToLocations()方法提交给Locator对象。这将返回一个或多个AddressCandidate对象,然后可以在地图上绘制。看一下下面的图表:

地理编码过程

反向地理编码过程

让我们也来回顾一下反向地理编码过程。这个过程也使用了一个Locator对象,它引用了一个地理编码服务的 URL。Point几何对象是通过在地图上点击位置或其他应用程序生成的事件而创建的。然后,通过locationToAddress()方法将这个Point对象与一个距离值一起提交给Locator。以米为单位提供的distance属性确定了Locator将尝试在其中找到地址的半径。

如果在半径范围内找到地址,则会创建一个AddressCandidate对象,并且可以将其解码为地址。看一下下面的图表:

反向地理编码过程

练习使用 Locator 服务的时间

在这个练习中,您将学习如何使用Locator类对地址进行地理编码,并将结果叠加在 ArcGIS Online 提供的底图上。打开 JavaScript 沙箱,网址为developers.arcgis.com/en/javascript/sandbox/sandbox.html,然后执行以下步骤:

  1. 在您的ArcGISJavaScriptAPI文件夹中,用文本编辑器打开名为geocode_begin.html的文件。我使用的是 Notepad++,但您可以使用您最熟悉的任何文本编辑器。本练习的一些代码已经为您编写,这样您就可以专注于地理编码功能。

  2. 复制并粘贴文件中的代码,以完全替换沙箱中当前的代码。

  3. 添加以下引用,用于本练习中将要使用的对象:

var map, **locator**;
require([
        "esri/map", **"esri/tasks/locator", "esri/graphic",**
 **"esri/InfoTemplate", "esri/symbols/SimpleMarkerSymbol",**
 **"esri/symbols/Font", "esri/symbols/TextSymbol",**
 **"dojo/_base/array", "dojo/_base/Color",**
 **"dojo/number", "dojo/parser", "dojo/dom"**, **"dijit/registry"**,"dijit/form/Button", "dijit/form/Textarea",
        "dijit/layout/BorderContainer","dijit/layout/ContentPane", "dojo/domReady!"
      ], function(
        Map, **Locator, Graphic,**
 **InfoTemplate, SimpleMarkerSymbol,** 
 **Font, TextSymbol,**
 **arrayUtils, Color,**
 **number, parser, dom, registry**
      ) {
        parser.parse();
  1. 现在在require()函数中,我们将初始化locator变量,然后将其注册到Locator.address-to-locations-complete。在用于创建Map对象的代码块之后,添加以下两行代码:
locator = newLocator("http://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer");
locator.on("address-to-locations-complete", showResults);

Locator的构造函数必须是一个有效的 URL 指针,指向一个定位器服务。在这种情况下,我们使用的是 World Geocoding Service。我们还为Locator对象注册了Locator.address-to-locations-complete事件。当地理编码完成时,此事件将触发,并在这种情况下调用作为on()参数指定的showResults()函数。

  1. 让我们还为将触发地理编码的按钮注册click事件,只需在刚刚创建的两行代码之后添加以下代码。这将触发一个名为locate()的 JavaScript 函数的执行,我们将在下一步中创建:
registry.byId("locate").on("click", locate);
  1. 在这一步中,您将创建一个locate()函数,该函数将执行多项任务,包括清除任何现有图形,从网页上的输入文本框创建一个Address JSON 对象,定义几个选项,并调用Locator.addressToLocations()方法。在您输入的最后一行代码之后,添加以下代码块,如下所示:
function locate() {
  map.graphics.clear();
  var address = {
    "SingleLine": dom.byId("address").value
  };
locator.outSpatialReference = map.spatialReference;
var options = {
  address: address,
  outFields: ["Loc_name"]
}
locator.addressToLocations(options);
}

此函数中的第一行代码清除了地图上的任何现有图形。当用户在一个会话中输入多个地址时,这是必需的。接下来,我们将创建一个名为address的变量,它是一个包含用户输入地址的 JSON 对象。然后,我们设置输出空间参考,并创建一个包含地址和输出字段的options变量,作为 JSON 对象。最后,我们调用Locator.addressToLocations()方法,并传入options变量。

  1. showResults()函数将接收Locator服务返回的结果,并在地图上绘制它们。在这种情况下,我们将仅显示得分在 0 到 100 之间的地址大于 80 的地址。showResults()函数的一部分已经为您编写了。通过添加以下突出显示的代码行来创建一个新变量来保存AddressCandidate对象:
function showResults(evt) {
 **var candidate;**
  var symbol = new SimpleMarkerSymbol();
  var infoTemplate = new InfoTemplate(
    "Location", 
    "Address: ${address}<br />Score: ${score}<br />Sourcelocator: ${locatorName}"
   );
   symbol.setStyle(SimpleMarkerSymbol.STYLE_SQUARE);
   symbol.setColor(new Color([153,0,51,0.75]));
  1. 在创建geom变量的代码行后,开始一个循环,该循环将遍历从Locator返回的每个地址:
arrayUtils.every(evt.addresses, function(candidate) {

 });
  1. 开始一个if语句,检查AddressCandidate.score属性是否大于 80 的值。我们只想显示匹配值高的地址:
arrayUtils.every(evt.addresses, function(candidate) {
 **if (candidate.score > 80) {**

 **}**
});
  1. if块内,创建一个包含地址、得分和AddressCandidate对象的字段值的新属性的 JSON 变量。除此之外,location属性将保存到geom变量中:
arrayUtils.every(evt.addresses, function(candidate) {
     if (candidate.score > 80) {
       var attributes = { 
         address: candidate.address, 
         score: candidate.score, 
         locatorName: candidate.attributes.Loc_name 
       };   
       geom = candidate.location;

     }
});
  1. 使用您之前创建的或为您创建的geometrysymbolattributesinfoTemplate变量创建一个新的Graphic对象,并将它们添加到GraphicsLayer
arrayUtils.every(evt.addresses, function(candidate) {
     if (candidate.score > 80) {
       var attributes = { 
         address: candidate.address, 
         score: candidate.score, 
         locatorName: candidate.attributes.Loc_name 
       };   
       geom = candidate.location;
 **var graphic = new Graphic(geom, symbol, attributes, infoTemplate);**
 **//add a graphic to the map at the geocoded location**
 **map.graphics.add(graphic);**

     }
    });
  1. 为位置添加文本符号:
arrayUtils.every(evt.addresses, function(candidate) {
     if (candidate.score > 80) {
       var attributes = { 
         address: candidate.address, 
         score: candidate.score, 
         locatorName: candidate.attributes.Loc_name 
       };   
       geom = candidate.location;
var graphic = new Graphic(geom, symbol, attributes, infoTemplate);
       //add a graphic to the map at the geocoded location
       map.graphics.add(graphic);
**//add a text symbol to the map listing the location of the matchedaddress.**
 **var displayText = candidate.address;**
 **var font = new Font(**
 **"16pt",**
 **Font.STYLE_NORMAL,** 
 **Font.VARIANT_NORMAL,**
 **Font.WEIGHT_BOLD,**
 **"Helvetica"**
 **);** 

 **var textSymbol = new TextSymbol(**
 **displayText,**
 **font,**
 **new Color("#666633")**
 **);**
 **textSymbol.setOffset(0,8);**
 **map.graphics.add(new Graphic(geom, textSymbol));**

     }
    });
  1. 在找到一个得分大于 80 的地址后跳出循环。许多地址将有多个匹配项,这可能会令人困惑。看一下以下代码片段:
arrayUtils.every(evt.addresses, function(candidate) {
     if (candidate.score > 80) {
       var attributes = { 
         address: candidate.address, 
         score: candidate.score, 
         locatorName: candidate.attributes.Loc_name 
       };   
       geom = candidate.location;
var graphic = new Graphic(geom, symbol, attributes,infoTemplate);
       //add a graphic to the map at the geocoded location
       map.graphics.add(graphic);
//add a text symbol to the map listing the location of thematched address.
       var displayText = candidate.address;
       var font = new Font(
         "16pt",
         Font.STYLE_NORMAL, 
         Font.VARIANT_NORMAL,
         Font.WEIGHT_BOLD,
         "Helvetica"
       );          

        var textSymbol = new TextSymbol(
          displayText,
          font,
          new Color("#666633")
         );
         textSymbol.setOffset(0,8);
         map.graphics.add(new Graphic(geom, textSymbol));
         **return false; //break out of loop after one candidate with score greater  than 80 is found.**
     }
    });
  1. 您可能需要通过检查位于your ArcGISJavaScriptAPI/solution文件夹中的解决方案文件geocode_end.html来仔细检查您的代码。

  2. 当您单击运行按钮时,您应该看到以下地图。如果没有,请检查您的代码是否准确。Time to practice with the Locator service

  3. 输入一个地址或接受默认值,然后单击定位,如下面的屏幕截图所示:Time to practice with the Locator service

摘要

ArcGIS Server 的Locator服务可以执行地理编码和反向地理编码。使用 ArcGIS API for JavaScript,您可以将地址提交给Locator服务,并检索地址的地理坐标,然后可以在地图上绘制出来。地址由 JavaScript 中的 JSON 对象定义,是Locator对象的输入,Locator对象对地址进行地理编码,并将结果返回为AddressCandidate对象,然后可以将其显示为地图上的点。这种模式与我们在前几章中看到的其他任务相同,其中输入对象(Address对象)为任务(Locator)提供输入参数,任务将作业提交给 ArcGIS Server。然后将结果对象(AddressCandidate)返回给回调函数,该函数处理返回的数据。在下一章中,您将学习如何使用各种网络分析任务。

第九章:网络分析任务

网络分析服务允许您在街道网络上执行分析,例如从一个地址到另一个地址找到最佳路线,找到最近的学校,确定位置周围的服务区域,或者使用一组服务车辆响应一组订单。可以使用它们的 REST 端点访问这些服务。可以执行服务的三种类型的分析:路由、最近设施和服务区域。我们将在本章中检查每种服务类型。所有网络分析服务都要求您在 ArcGIS Server 上安装网络分析插件。

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

  • RouteTask

  • 练习路由的时间

  • 最近设施任务

  • 服务区域任务

RouteTask

在 JavaScript API 中进行路由允许您使用RouteTask对象在两个或多个位置之间找到路线,并可选择获取驾驶方向。RouteTask对象使用网络分析服务计算路线,可以包括简单和复杂的路线,如多个停靠点、障碍和时间窗口。

RouteTask对象在网络中的多个位置之间使用最小成本路径。网络上的阻抗可以包括时间和距离变量。以下截图显示了RouteTask实现的输出:

RouteTask

与我们在本课程中研究的其他任务一样,路由是通过一系列对象完成的,包括RouteParametersRouteTaskRouteResult。以下图示说明了这三个路由对象:

RouteTask

RouteParameters对象提供了输入参数给RouteTaskRouteTask使用输入参数向 ArcGIS Server 提交路由请求。结果以RouteResult对象的形式从 ArcGIS Server 返回。

RouteParameters对象作为RouteTask对象的输入,并可以包括停靠和障碍位置、阻抗、是否返回驾驶方向和路线等。您可以在 JavaScript API 的developers.arcgis.com/en/javascript/jsapi/routeparameters-amd.html获取所有参数的完整列表。还提供了一个简短的代码示例,显示如何创建RouteParameters的实例,添加停靠点并定义输出空间参考:

routeParams = new RouteParameters();
routeParams.stops = new FeatureSet();
routeParams.outSpatialReference = {wkid:4326};
routeParams.stops.features.push(stop1);
routeParams.stops.features.push(stop2);

RouteTask对象使用RouteParameters提供的输入参数执行路由操作。RouteTask的构造函数接受一个指向标识要用于分析的网络服务的 URL 的指针。调用RouteTask上的solve()方法执行路由任务,使用提供的输入参数对网络分析服务执行路由任务:

routeParams = new RouteParameters();
routeParams.stops = new FeatureSet();
routeParams.outSpatialReference = {wkid:4326};
routeParams.stops.features.push(stop1);
routeParams.stops.features.push(stop2);
**routeTask.solve(routeParams);**

RouteResult对象从网络分析服务返回给RouteTask提供的回调函数。然后回调函数通过向用户显示数据来处理数据。返回的数据在很大程度上取决于提供给RouteParameters对象的输入。RouteParameters上最重要的属性之一是stops属性。这些是要包括在点之间最佳路线分析中的点。停靠点被定义为DataLayerFeatureSet的实例,并且是要包括在分析中的一组停靠点。

障碍的概念在路由操作中也很重要。障碍在规划路线时限制移动。障碍可以包括车祸、街道段上的施工工作或其他延误,如铁路道口。障碍被定义为FeatureSetDataLayer,并通过RouteParameters.barriers属性指定。以下代码显示了如何在您的代码中创建障碍的示例:

var routeParameters = new RouteParameters();
//Add barriers as a FeatureSet
routeParameters.barriers = new FeatureSet();
routeParameters.barriers.features.push(map.graphics.add(new Graphic(evt.mapPoint, barrierSymbol)));

只有当RouteParameters.returnDirections设置为true时,才会返回方向。当你选择返回方向时,你还可以使用各种属性来控制返回的方向。你可以控制方向的语言(RouteParameters.directionsLanguage)、长度单位(RouteParameters.directionsLengthUnits)、输出类型(RouteParameters.directionsOutputType)、样式名称(RouteParameters.StyleName)和时间属性(RouteParameters.directionsTimeAttribute)。除了方向之外返回的数据还包括点之间的路线、路线名称和停靠点数组。

还可以指定如果其中一个停靠点无法到达,则任务应该失败。这是通过RouteParameters.ignoreInvalidLocations属性来实现的。这个属性可以设置为truefalse。你还可以通过诸如RouteParameters.startTime(指定路线开始的时间)和RouteParameters.useTimeWindows(定义分析中应该使用时间范围)等属性将时间引入到分析中。

练习路由

在这个练习中,你将学习如何在你的应用程序中实现路由。你将创建一个RouteParameters的实例,通过允许用户在地图上点击点来添加停靠点,并解决路线。返回的路线将显示为地图上的线符号。按照以下指示创建一个包括路由的应用程序:

  1. developers.arcgis.com/en/javascript/sandbox/sandbox.html打开 JavaScript 沙盒。

  2. 从我在下面的代码片段中突出显示的<script>标签中删除 JavaScript 内容:

  <script>
 **dojo.require("esri.map");**

 **function init(){**
 **var map = new esri.Map("mapDiv", {**
 **center: [-56.049, 38.485],**
 **zoom: 3,**
 **basemap: "streets"**
 **});**
 **}**
 **dojo.ready(init);**
  </script>
  1. 为我们在这个练习中将要使用的对象添加以下引用:
  <script>
    require([
        "esri/map",
        "esri/tasks/RouteParameters",
        "esri/tasks/RouteTask",

        "esri/tasks/FeatureSet",
        "esri/symbols/SimpleMarkerSymbol",
        "esri/symbols/SimpleLineSymbol",
        "esri/graphic",
        "dojo/_base/Color"
      ],
      function(Map, RouteParameters, RouteTask, FeatureSet, SimpleMarkerSymbol, SimpleLineSymbol, Graphic, Color ){

    });
  </script>
  1. require()函数内,创建Map对象,如下面的代码片段所示,并定义变量来保存用于显示目的的路线对象和符号:
  <script>
    require([
        "esri/map",
        "esri/tasks/RouteParameters",
        "esri/tasks/RouteTask",
        "esri/tasks/RouteResult",
        "esri/tasks/FeatureSet",
        "esri/symbols/SimpleMarkerSymbol",
        "esri/symbols/SimpleLineSymbol",
        "esri/graphic",
        "dojo/_base/Color"
      ],
      function(Map, RouteParameters, RouteTask, RouteResult, FeatureSet, SimpleMarkerSymbol, SimpleLineSymbol, Graphic, Color ){
 **var map, routeTask, routeParams;**
 **var stopSymbol, routeSymbol, lastStop;**

 **map = new Map("mapDiv", {** 
 **basemap: "streets",**
 **center:[-123.379, 48.418], //long, lat**
 **zoom: 14**
 **});**
      });
    </script>
  1. 在创建Map对象的代码块的下方,为Map.click()事件添加事件处理程序。这个操作应该触发addStop()函数:
map = new Map("mapDiv", { 
    basemap: "streets",
    center:[-123.379, 48.418], //long, lat
    zoom: 14
});
**map.on("click", addStop);**

  1. 创建RouteTaskRouteParameters对象。将RouteParameters.stops属性设置为一个新的FeatureSet对象。同时,设置RouteParameters.outSpatialReference属性:
map = new Map("mapDiv", { 
    basemap: "streets",
    center:[-123.379, 48.418], //long, lat
    zoom: 14
});
map.on("click", addStop);
**routeTask = new RouteTask("http://tasks.arcgisonline.com/ArcGIS/rest/services/NetworkAnalysis/ESRI_Route_NA/NAServer/Route");**
**routeParams = new RouteParameters();**
**routeParams.stops = new FeatureSet();**
**routeParams.outSpatialReference = {"wkid":4326};**

以下是包含这个网络分析服务的服务目录的屏幕截图:

练习路由

  1. RouteTask.solve-complete()事件的完成和RouteTask.error()事件添加事件处理程序。路由任务成功完成应该触发showRoute()函数的执行。任何错误应该触发errorHandler()函数的执行:
       routeParams = new RouteParameters();
       routeParams.stops = new FeatureSet();
       routeParams.outSpatialReference = {"wkid":4326};

       **routeTask.on("solve-complete", showRoute);**
 **routeTask.on("error", errorHandler);**

  1. 为路线的起点和终点创建符号对象,以及定义这些点之间路线的线。以下代码应该添加在你在上一步中添加的两行代码的下方:
stopSymbol = new SimpleMarkerSymbol().setStyle(SimpleMarkerSymbol.STYLE_CROSS).setSize(15);
stopSymbol.outline.setWidth(4);
routeSymbol = new SimpleLineSymbol().setColor(new Color([0,0,255,0.5])).setWidth(5);
  1. 创建addStop()函数,当用户在地图上点击时将被触发。这个函数将接受一个Event对象作为它唯一的参数。可以从这个对象中提取在地图上点击的点。这个函数将在地图上添加一个点图形,并将图形添加到RouteParameters.stops属性;在第二次地图点击时,它将调用RouteTask.solve()方法,传入一个RouteParameters的实例:
function addStop(evt) {
     var stop = map.graphics.add(new Graphic(evt.mapPoint, stopSymbol));
     routeParams.stops.features.push(stop);

     if (routeParams.stops.features.length >= 2) {
       routeTask.solve(routeParams);
       lastStop = routeParams.stops.features.splice(0, 1)[0];
     }
  }
  1. 创建showRoute()函数,接受一个RouteResult的实例。在这个函数中,你需要做的唯一的事情就是将路线作为线添加到GraphicsLayer中:
**function showRoute(solveResult) {**
 **map.graphics.add(solveResult.result.routeResults[0].route.setSymbol(routeSymbol));**
 **}**

  1. 最后,添加错误回调函数,以防路由出现问题。这个函数应该向用户显示错误消息,并删除任何剩余的图形:
function errorHandler(err) {
  alert("An error occurred\n" + err.message + "\n" + err.details.join("\n"));

  routeParams.stops.features.splice(0, 0, lastStop);
  map.graphics.remove(routeParams.stops.features.splice(1,   1)[0]);
}
  1. 你可能想要在ArcGISJavaScriptAPI文件夹中查看解决方案文件(routing.html),以验证你的代码是否已经正确编写。

  2. 单击运行按钮。您应该看到地图如下截图所示。如果没有,您可能需要重新检查代码的准确性。练习路由时间

  3. 在地图上的某个地方单击。您应该看到一个点标记,如下截图所示:练习路由时间

  4. 在地图上的其他地方单击。这应该显示第二个标记以及两点之间的最佳路线,如下截图所示:练习路由时间

最近设施任务

ClosestFacility任务测量了事件和设施之间的旅行成本,并确定彼此之间最近的事件和设施。在寻找最近的设施时,您可以指定要找到多少个以及旅行方向是朝向还是远离它们。最近设施求解器显示事件和设施之间的最佳路线,报告它们的旅行成本,并返回驾驶方向。

最近设施任务

解决最近设施操作涉及的类包括ClosestFacilityParametersClosestFacilityTaskClosestFacilitySolveResults,如下所示:

最近设施任务

ClosestFacilityParameters类包括默认截止、是否返回事件、路线和方向等输入参数。这些参数用作ClosestFacilityTask类的输入,该类包含一个solve()方法。最后,结果以ClosestFacilitySolveResults对象的形式从 ArcGIS 服务器传递回客户端。

ClosestFacilityParameters对象用作ClosestFacilityTask的输入。现在将讨论此对象上一些常用的属性。incidentsfacilities属性用于设置分析的位置。任务返回的数据可以通过returnIncidentsreturnRoutesreturnDirections属性进行控制,这些属性只是truefalse值,指示是否应在结果中返回信息。travelDirection参数指定是否应该到设施或从设施出发旅行,defaultCutoff是分析将停止遍历的截止值。以下代码示例显示了如何创建ClosestFacilityParameters的实例并应用各种属性:

params = new ClosestFacilityParameters();
params.defaultCutoff = 3.0;
params.returnIncidents = false;
params.returnRoutes = true;
params.returnDirections = true;

当您创建一个新的ClosestFacilityTask实例时,您需要指向代表网络分析服务的 REST 资源。创建后,ClosestFacilityTask类接受ClosestFacilityParameters提供的输入参数,并使用solve()方法将它们提交给网络分析服务。

这是通过以下代码示例来说明的。solve()方法还接受回调和错误回调函数:

**cfTask = new ClosestFacilityTask("http://<domain>/arcgis/rest/services/network/ClosestFacility");**
params = new ClosestFacilityParameters();
params.defaultCutoff = 3.0;
params.returnIncidents = false;
params.returnRoutes = true;
params.returnDirections = true;
**cfTask.solve(params, processResults);**

ClosestFacilityTask操作返回的结果是一个ClosestFacilitySolveResult对象。此对象可以包含各种属性,包括DirectionsFeatureSet对象,这是一个方向数组。这个DirectionsFeatureSet对象包含路线的逐步方向文本和几何信息。每个要素的属性提供与相应路段相关的信息。返回的属性包括方向文本、路段长度、沿路段行驶的时间以及到达路段的预计到达时间。ClosestFacilitySolveResults中包含的其他属性包括包含设施和事件的数组,表示返回的路线的折线数组,返回的任何消息以及包含障碍的数组。

服务区任务

新的 ServiceArea 任务在下面的截图中进行了说明,计算了输入位置周围的服务区域。该服务区域以分钟为单位定义,是一个包含在该时间范围内所有可访问街道的区域。

服务区域任务

涉及服务区域操作的类包括 ServiceAreaParameters、ServiceAreaTask 和 ServiceAreaSolveResults。这些对象在下图中进行了说明:

服务区域任务

ServiceAreaParameters 类包括诸如默认中断、涉及的设施、障碍和限制、行进方向等输入参数。这些参数用作 ServiceAreaTask 类的输入,该类调用 solve()。在 ServiceAreaParameters 中定义的参数传递给 ServiceAreaTask。最后,结果以 ServiceAreaSolveResults 对象的形式从 ArcGIS Server 传递回客户端。ServiceAreaParameters 对象用作 ServiceAreaTask 的输入。本章的这一部分讨论了该对象上一些常用的属性。defaultBreaks 属性是定义服务区域的数字数组。例如,在以下代码示例中,提供了一个值为 2 的单个值,表示我们希望返回设施周围的 2 分钟服务区域。returnFacilities 属性设置为 true 时,表示设施应与结果一起返回。还可以通过 barriers 属性设置各种点、折线和多边形障碍。分析的行进方向可以是到设施或从设施,通过 travelDirection 属性进行设置。ServiceAreaParameters 上还可以设置许多其他属性。以下提供了一个代码示例:

params = new ServiceAreaParameters();
params.defaultBreaks = [2];
params.outSpatialReference = map.spatialReference;
params.returnFacilities = false;

ServiceAreaTask 类使用街道网络在位置周围找到服务区域。ServiceAreaTask 的构造函数应该指向代表网络分析服务的 REST 资源。要提交解决服务区域任务的请求,您需要在 ServiceAreaTask 上调用 solve()方法。

ServiceAreaTask 操作返回的结果是一个 ServiceAreaSolveResult 对象。该对象可以包含各种属性,包括 ServiceAreaPolygons 属性,这是从分析中返回的服务区域多边形数组。此外,其他属性包括设施、消息和障碍。

总结

路由使您能够向应用程序添加在两个或多个位置之间找到路径并生成驾驶路线的功能。此功能是通过执行网络分析的 RouteTask 对象来实现的。这种功能以及其他网络分析服务需要使用 ArcGIS Server 的网络分析插件。其他网络分析任务包括最近设施任务,它允许您测量事件和设施之间的旅行成本,并确定彼此之间最近的设施,以及服务区域任务,它计算了输入位置周围的服务区域。在下一章中,您将学习如何从应用程序执行地理处理任务。

第十章:地理处理任务

地理处理是指以逻辑方式自动化和链接 GIS 操作,以完成某种 GIS 任务。例如,您可能希望对流图层进行缓冲,然后将植被图层裁剪到这个新创建的缓冲区。在 ArcGIS for Desktop 中可以构建模型,并且可以在桌面环境或通过 Web 应用程序访问的集中服务器上以自动化方式运行。ArcToolbox 中的任何工具,无论是您 ArcGIS 许可级别的内置工具还是您构建的自定义工具,都可以在模型中使用,并与其他工具链接在一起。本章将探讨如何通过 ArcGIS API for JavaScript 访问这些地理处理任务。

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

  • ArcGIS Server 中的模型

  • 使用地理处理器-你需要知道

  • 理解地理处理任务的服务页面

  • 地理处理任务

  • 运行任务

  • 练习地理处理任务的时间到了!地理处理任务

上图显示了使用 ModelBuilder 构建的模型的组件。这些模型可以作为地理处理任务发布到 ArcGIS Server,然后通过你的应用程序访问。

ArcGIS Server 中的模型

在 ArcGIS for Desktop 中使用 ModelBuilder 构建模型。构建完成后,这些模型可以作为地理处理任务发布到 ArcGIS Server。然后,Web 应用程序使用在 ArcGIS API for JavaScript 中找到的Geoprocessor对象来访问这些任务并检索信息。由于这些模型和工具需要计算密集型和 ArcGIS 软件,它们在 ArcGIS Server 上运行。作业通过您的应用程序提交到服务器,服务完成后会获取结果。通过Geoprocessor对象可以提交作业和检索结果。这个过程在下图中有所说明:

ArcGIS Server 中的模型

使用地理处理器-你需要知道的

在使用地理处理服务时,有三件事情是你需要知道的:

  • 首先,您需要知道模型或工具所在的 URL。一个示例 URL 是sampleserver1.arcgisonline.com/ArcGIS/rest/services/Demographics/ESRI_Population_World/GPServer/PopulationSummary

  • 当您访问此链接时,您还可以找到有关输入和输出参数的信息,任务是异步还是同步,以及更多信息。说到输入和输出参数,您需要知道与这些参数相关的数据类型以及每个参数是否是必需的。

  • 最后,您需要知道任务是异步还是同步,以及根据这一知识如何配置您的代码。所有这些信息都可以在地理处理任务的服务页面上找到。使用地理处理器-你需要知道的

理解地理处理任务的服务页面

地理处理服务的服务页面包括有关服务的元数据信息。这包括执行类型,可以是同步或异步。在下面的屏幕截图中看到的服务的情况下,PopulationSummary服务是一个同步任务,这表明应用程序将等待结果返回。这种执行类型通常用于执行速度快的任务。异步任务被提交为作业,然后应用程序可以在地理处理服务执行其工作时继续运行。任务完成时,它会通知您的应用程序处理已完成并且结果已准备就绪。

其他信息包括参数名称、参数数据类型、参数是输入还是输出类型、参数是必需还是可选、几何类型、空间参考和字段。

了解地理处理任务的服务页面

输入参数

关于提交给地理处理任务的输入参数,您必须记住一些细节。几乎所有地理处理任务都需要一个或多个参数。这些参数可以是必需的或可选的,并且被创建为 JSON 对象。在本节中,您将看到一个代码示例,向您展示如何创建这些 JSON 对象。在创建参数作为 JSON 对象时,您必须记住按照它们在服务页面上出现的确切顺序创建它们。参数名称也必须与服务页面上的名称完全相同。请参阅以下屏幕截图,了解如何阅读服务的输入参数的示例:

输入参数

以下代码示例是正确的,因为参数名称拼写与服务页面上看到的完全相同(还要注意大小写相同),并且按正确顺序提供:

var params = {
    Input_Observation_Point: featureSetPoints,
    Viewshed_Distance: 250
};

相比之下,以下代码示例将是不正确的,因为参数是以相反顺序提供的:

var params = {
    Viewshed_Distance: 250,
    Input_Observation_Point: featureSetPoints
};

前面的屏幕截图显示了提交给地理处理任务的输入参数。在编写 JSON 输入参数对象时,非常重要的是您提供与服务页面上给出的确切参数名称,并且按照页面上出现的顺序提供参数。请注意,在我们的代码示例中,我们提供了两个参数:Input_Observation_PointViewshed_Distance。这两个参数都是必需的,并且我们已经将它们命名为它们在服务页面上出现的名称,并且它们的顺序是正确的。

地理处理任务

ArcGIS API for JavaScript 中的Geoprocessor类表示 GP 任务资源,这是地理处理服务中的单个任务。输入参数通过调用Geoprocessor.execute()Geoprocessor.submitJob()传递给Geoprocessor类。我们将在后面讨论这两个调用之间的区别。执行地理处理任务后,结果将返回到Geoprocessor对象,然后由回调函数处理。创建Geoprocessor类的实例只需要传入指向 ArcGIS Server 暴露的地理处理服务的 URL。它需要您导入esri/tasks/gp。以下代码示例向您展示如何创建Geoprocessor对象的实例:

gp = new Geoprocessor(url);

运行任务

一旦您了解了 ArcGIS Server 实例可用的地理处理模型和工具以及输入和输出参数,您就可以开始编写执行任务的代码。地理处理作业被提交到 ArcGIS Server 以进行同步或异步执行。同步执行意味着客户端调用执行任务,然后在继续应用程序代码之前等待结果。在异步执行中,客户端提交作业,继续运行其他函数,并稍后检查作业的完成情况。默认情况下,客户端每秒检查一次作业是否完成。服务页面告诉您如何为每个地理处理任务提交作业。只需在服务页面上查找执行类型。执行类型在模型发布为服务时设置。作为开发人员,在发布后,您无法控制类型。

同步任务

同步任务需要您的应用程序代码提交作业并等待响应,然后才能继续。因为您的最终用户必须等待结果返回才能继续与应用程序交互,所以这种类型的任务应该仅用于返回数据非常快的任务。如果任务需要的时间超过几秒钟,应将其定义为异步而不是同步。当数据在非常短的时间内返回时,用户很快就会对应用程序感到沮丧。

您需要使用Geoprocessor.execute()方法,其中包括属性输入参数和提供的回调函数。当地理处理任务返回提交的作业结果时,将执行回调函数。这些结果存储在ParameterValue数组中。

异步任务

异步任务需要您提交作业,同时继续在等待过程完成时处理其他函数,然后定期与 ArcGIS Server 检查以检索结果。异步任务的优势在于它不会强迫您的最终用户等待结果。相反,任务被提交,您的最终用户继续与应用程序交互,直到任务完成处理。处理完成后,将在您的应用程序中触发回调函数,您可以处理返回的结果。

Geoprocessor.submitJob()方法用于提交地理处理任务的作业。您需要提供输入参数、回调函数和状态回调函数。状态回调函数每次应用程序检查结果时执行。默认情况下,每秒检查一次状态。但是,可以使用Geoprocessor.setUpdateDelay()方法更改此间隔。每次检查状态时,都会返回一个JobInfo对象,其中包含指示作业状态的信息。当JobInfo.jobStatus设置为STATUS_SUCCEEDED时,将调用完成回调函数。

提供了异步任务流程的可视化图表,可能有助于加强这些类型任务的操作方式。创建输入参数并将其输入到Geoprocessor对象中,该对象使用这些参数向 ArcGIS Server 提交地理处理作业。然后,Geoprocessor对象以固定间隔执行statusCallback()函数。此函数检查地理处理服务,以查看作业是否已完成。返回一个JobInfo对象,其中包含指示其完成状态的状态指示器。此过程持续进行,直到作业完成,此时将调用完成回调函数,并传递作业的结果。

异步任务

练习地理处理任务的时间

在本练习中,您将编写一个简单的应用程序,通过访问 Esri 提供的CreateDriveTimePolygons模型,在地图上显示行驶时间多边形。该应用程序将在地图上点击的点周围创建 1、2 和 3 分钟的行驶时间多边形。

  1. developers.arcgis.com/en/javascript/sandbox/sandbox.html上打开 JavaScript 沙箱。

  2. 从我在以下代码片段中突出显示的<script>标签中删除 JavaScript 内容:

<script>
**dojo.require("esri.map");**

**function init(){**
**var map = new esri.Map("mapDiv", {**
**center: [-56.049, 38.485],**
**zoom: 3,**
**basemap: "streets"**
 **});**
 **}**
**dojo.ready(init);**
</script>
  1. 为本练习中将使用的对象添加以下引用:
<script>
**require([**
 **"esri/map",**
 **"esri/graphic",**
 **"esri/graphicsUtils",**
 **"esri/tasks/Geoprocessor",**
 **"esri/tasks/FeatureSet",**
 **"esri/symbols/SimpleMarkerSymbol",**
 **"esri/symbols/SimpleLineSymbol",**
 **"esri/symbols/SimpleFillSymbol",**
**"dojo/_base/Color"],**
**function(Map, Graphic, graphicsUtils, Geoprocessor, FeatureSet, SimpleMarkerSymbol, SimpleLineSymbol, SimpleFillSymbol, Color){**

 **});**
</script>
  1. 按照以下代码片段中所示创建Map对象,并定义变量以保存Geoprocessor对象和行驶时间:
<script>
require([
      "esri/map",
      "esri/graphic",
      "esri/graphicsUtils",
      "esri/tasks/Geoprocessor",
      "esri/tasks/FeatureSet",
      "esri/symbols/SimpleMarkerSymbol",
      "esri/symbols/SimpleLineSymbol",
      "esri/symbols/SimpleFillSymbol",
"dojo/_base/Color"],
function(Map, Graphic, graphicsUtils, Geoprocessor, FeatureSet, SimpleMarkerSymbol, SimpleLineSymbol, SimpleFillSymbol, Color){
**var map, gp;**
**var driveTimes = "1 2 3";**

**// Initialize map, GP and image params**
**map = new Map("mapDiv", {** 
 **basemap: "streets",**
 **center:[-117.148, 32.706], //long, lat**
 **zoom: 12**
**});**    });
</script>
  1. require()函数内部,创建新的Geoprocessor对象并设置输出空间参考:
// Initialize map, GP and image params
map = new Map("mapDiv", { 
  basemap: "streets",
  center:[-117.148, 32.706], //long, lat
  zoom: 12
});

**gp = newGeoprocessor("http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/Network/ESRI_DriveTime_US/GPServer/CreateDriveTimePolygons");**
**gp.setOutputSpatialReference({wkid:102100});**

  1. Map.click()事件设置事件监听器。每次用户在地图上单击时,都会触发计算行驶时间的地理处理任务的执行:
gp = new Geoprocessor("http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/Network/ESRI_DriveTime_US/GPServer/CreateDriveTimePolygons");
gp.setOutputSpatialReference({wkid:102100});
**map.on("click", computeServiceArea);**

  1. 现在,您将创建computeServiceArea()函数,该函数作为Map.click()事件的处理程序。此函数将清除任何现有的图形,创建一个新的点图形,表示用户在地图上单击的点,并执行地理处理任务。首先,在定义处理程序的代码行的下方创建computeServiceArea()函数的存根:
gp = new Geoprocessor("http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/Network/ESRI_DriveTime_US/GPServer/CreateDriveTimePolygons");
gp.setOutputSpatialReference({wkid:102100});
map.on("click", computeServiceArea);

**function computeServiceArea(evt) {**

**}**

  1. 清除任何现有的图形,并创建将表示在地图上单击的点的新SimpleMarkerSymbol
function computeServiceArea(evt) {
 **map.graphics.clear();**
 **var pointSymbol = new SimpleMarkerSymbol();**
 **pointSymbol.setOutline = new SimpleLineSymbol(SimpleLineSymbol.STYLE_SOLID, new Color([255, 0, 0]), 1);**
 **pointSymbol.setSize(14);**
 **pointSymbol.setColor(new Color([0, 255, 0, 0.25]));**
}
  1. 当触发Map.click()事件时,将创建一个Event对象并将其传递给computeServiceArea()函数。此对象在我们的代码中由evt变量表示。在此步骤中,您将通过传递Event.mapPoint属性创建一个新的Graphic对象,该属性包含从地图单击返回的Point几何以及您在上一步中创建的SimpleMarkerSymbol实例。然后,将此新图形添加到GraphicsLayer中,以便在地图上显示:
function computeServiceArea(evt) {
  map.graphics.clear();
  varpointSymbol = new SimpleMarkerSymbol();
  pointSymbol.setOutline = new SimpleLineSymbol(SimpleLineSymbol.STYLE_SOLID, new Color([255, 0, 0]), 1);
  pointSymbol.setSize(14);
  pointSymbol.setColor(new Color([0, 255, 0, 0.25]));

 **var graphic = new Graphic(evt.mapPoint,pointSymbol);**
 **map.graphics.add(graphic);**
}
  1. 现在,创建一个名为features的数组,并将graphic对象放入数组中。这些图形的数组最终将被传递到将传递给地理处理任务的FeatureSet对象中:
functioncomputeServiceArea(evt) {
  map.graphics.clear();
  var pointSymbol = new SimpleMarkerSymbol();
  pointSymbol.setOutline = new SimpleLineSymbol(SimpleLineSymbol.STYLE_SOLID, new Color([255, 0, 0]), 1);
  pointSymbol.setSize(14);
  pointSymbol.setColor(new Color([0, 255, 0, 0.25]));

  var graphic = new Graphic(evt.mapPoint,pointSymbol);
  map.graphics.add(graphic);

 **var features= [];**
 **features.push(graphic);**
}
  1. 创建一个新的FeatureSet对象,并将图形数组添加到FeatureSet.features属性中:
function computeServiceArea(evt) {
  map.graphics.clear();
  var pointSymbol = new SimpleMarkerSymbol();
  pointSymbol.setOutline = new SimpleLineSymbol(SimpleLineSymbol.STYLE_SOLID, new Color([255, 0, 0]), 1);
  pointSymbol.setSize(14);
  pointSymbol.setColor(new Color([0, 255, 0, 0.25]));

  var graphic = new Graphic(evt.mapPoint,pointSymbol);
  map.graphics.add(graphic);

  var features= [];
  features.push(graphic);
 **var featureSet = new FeatureSet();**
 **featureSet.features = features;**
}
  1. 创建一个 JSON 对象,该对象将保存要传递给地理处理任务的输入参数,并调用Geoprocessor.execute()方法。输入参数包括Input_LocationDrive_Times。请记住,每个输入参数必须拼写与服务页面中看到的完全相同,包括大小写。参数的顺序也非常重要,并且也在服务页面上定义。我们将Input_Location参数定义为FeatureSet对象。FeatureSet对象包含一个图形数组,在本例中只有一个单个图形点。Drive_Times对象已经使用我们之前创建的driveTimes变量硬编码为 1、2 和 3 的值。最后,我们调用Geoprocessor.execute()方法,传入输入参数以及将处理结果的回调函数。接下来我们将创建这个回调函数:
function computeServiceArea(evt) {
map.graphics.clear();
varpointSymbol = new SimpleMarkerSymbol();
pointSymbol.setOutline = new SimpleLineSymbol(SimpleLineSymbol.STYLE_SOLID, new Color([255, 0, 0]), 1);
pointSymbol.setSize(14);
pointSymbol.setColor(new Color([0, 255, 0, 0.25]));

var graphic = new Graphic(evt.mapPoint,pointSymbol);
map.graphics.add(graphic);

var features= [];
features.push(graphic);
varfeatureSet = new FeatureSet();
featureSet.features = features;
**var params = { "Input_Location":featureSet, "Drive_Times":driveTimes };**
**gp.execute(params, getDriveTimePolys);**
}
  1. 在最后一步中,我们定义了一个名为getDriveTimePolys()的回调函数,当地理处理任务完成行驶时间分析时将被触发。让我们创建这个getDriveTimePolys()函数。在computeServiceArea()函数的结束大括号下方,开始getDriveTimePolys()的存根:
**function getDriveTimePolys(results, messages) {**

**}**

  1. getDriveTimePolys()函数接受两个参数,包括结果对象和返回的任何消息。定义一个新的features变量,其中包含地理处理任务返回的FeatureSet对象:
function getDriveTimePolys(results, messages) {
 **var features = results[0].value.features;**
}
  1. 地理处理任务将返回三个Polygon图形。每个Polygon图形表示我们硬编码为输入参数的行驶时间(1、2 和 3 分钟)。创建一个for循环来处理每个多边形:
function getDriveTimePolys(results, messages) {
  var features = results[0].value.features;

 **for (var f=0, fl=features.length; f<fl; f++) {**

 **}**
}
  1. for循环内,使用不同的颜色对每个多边形进行符号化。第一个图形将是红色,第二个是绿色,第三个是蓝色。FeatureSet对象中将有三个多边形。使用以下代码块为每个定义不同的多边形符号,并将图形添加到GraphicsLayer中:
function getDriveTimePolys(results, messages) {
var features = results[0].value.features;

for (var f=0, fl=features.length; f<fl; f++) {
 **var feature = features[f];**
 **if(f == 0) {**
 **var polySymbolRed = new SimpleFillSymbol();**
 **polySymbolRed.setOutline(new SimpleLineSymbol(SimpleLineSymbol.STYLE_SOLID, new Color([0,0,0,0.5]), 1));**
 **polySymbolRed.setColor(new Color([255,0,0,0.7]));**
 **feature.setSymbol(polySymbolRed);**
 **}**
 **else if(f == 1) {**
 **var polySymbolGreen = new SimpleFillSymbol();**
 **polySymbolGreen.setOutline(new SimpleLineSymbol(SimpleLineSymbol.STYLE_SOLID, new Color([0,0,0,0.5]), 1));**
 **polySymbolGreen.setColor(new Color([0,255,0,0.7]));**
 **feature.setSymbol(polySymbolGreen);**
 **}**
 **else if(f == 2) {**
 **var polySymbolBlue = new SimpleFillSymbol();**
 **polySymbolBlue.setOutline(new SimpleLineSymbol(SimpleLineSymbol.STYLE_SOLID, new Color([0,0,0,0.5]), 1));**
 **polySymbolBlue.setColor(new Color([0,0,255,0.7]));**
 **feature.setSymbol(polySymbolBlue);**
 **}**
 **map.graphics.add(feature);** 
}
  1. 将地图范围设置为GraphicsLayer的范围,该范围现在包含您刚刚创建的三个多边形:
function getDriveTimePolys(results, messages) {
  var features = results[0].value.features;

  for (var f=0, fl=features.length; f<fl; f++) {
    var feature = features[f];
    if(f === 0) {
      var polySymbolRed = new SimpleFillSymbol();
      polySymbolRed.setOutline(new SimpleLineSymbol(SimpleLineSymbol.STYLE_SOLID, new Color([0,0,0,0.5]), 1));
      polySymbolRed.setColor(new Color([255,0,0,0.7]));
      feature.setSymbol(polySymbolRed);
    }
    else if(f == 1) {
      var polySymbolGreen = new SimpleFillSymbol();
      polySymbolGreen.setOutline(new SimpleLineSymbol(SimpleLineSymbol.STYLE_SOLID, new Color([0,0,0,0.5]), 1));
      polySymbolGreen.setColor(new Color([0,255,0,0.7]));
      feature.setSymbol(polySymbolGreen);
    }
    else if(f == 2) {
      var polySymbolBlue = new SimpleFillSymbol();
      polySymbolBlue.setOutline(new SimpleLineSymbol(SimpleLineSymbol.STYLE_SOLID, new Color([0,0,0,0.5]), 1));
      polySymbolBlue.setColor(new Color([0,0,255,0.7]));
      feature.setSymbol(polySymbolBlue);
    }
    map.graphics.add(feature);
  }
 **map.setExtent(graphicsUtils.graphicsExtent(map.graphics.graphics), true);**
}
  1. 添加一个<div>标签,用于保存应用程序的说明:
<body>
<div id="mapDiv"></div>
**<div id="info" class="esriSimpleSlider">**
 **Click on the map to use a Geoprocessing(GP) task to generate and zoom to drive time polygons. The drive time polygons are 1, 2, and 3 minutes.**
**</div>**
</body>
  1. 修改代码顶部的<style>标签,如以下代码的突出显示部分所示:
<style>
**html, body, #mapDiv {**
**height: 100%;**
**margin: 0;**
**padding: 0;**
**width: 100%;**
 **}**
 **#info {**
**bottom: 20px;**
**color: #444;**
**height: auto;**
**font-family: arial;**
**left: 20px;**
**margin: 5px;**
**padding: 10px;**
**position: absolute;**
**text-align: left;**
**width: 200px;**
**z-index: 40;**
 **}**
</style>
  1. 您可能希望在ArcGISJavaScriptAPI文件夹中查看解决方案文件(drivetimes.html),以验证您的代码是否已正确编写。

  2. 点击运行按钮。您应该在以下截图中看到地图。如果没有,您可能需要重新检查代码的准确性。练习地理处理任务的时间

  3. 在地图上的某个地方点击。只需片刻,您应该看到行驶时间多边形显示出来。请耐心等待。有时这可能需要一点时间。练习地理处理任务的时间

摘要

ArcGIS Server 可以公开地理处理服务,如模型和工具,您的应用程序可以访问。这些工具在 ArcGIS Server 上运行,因为它们需要进行计算密集型的操作,并且需要 ArcGIS 软件。作业通过您的应用程序提交到服务器,任务完成后返回结果。地理处理任务可以是同步或异步的,并且由 ArcGIS Server 管理员配置为其中一种类型运行。作为应用程序员,重要的是要了解您正在访问的地理处理服务的类型,因为您对服务的方法调用取决于这些信息。此外,要知道任务是同步还是异步,您还需要知道地理处理模型或工具的 URL 以及输入和输出参数。在下一章中,您将学习如何将 ArcGIS Online 的数据和地图添加到您的应用程序中。

第十一章:与 ArcGIS Online 集成

ArcGIS Online 是一个专为处理地图和其他类型地理信息而设计的网站。在这个网站上,您将找到用于构建和共享地图的应用程序。您还将找到有用的底图、数据、应用程序和工具,您可以查看和使用,以及您可以加入的社区。对于应用程序开发人员来说,真正令人兴奋的消息是,您可以使用 ArcGIS Server JavaScript API 将 ArcGIS Online 内容集成到您的自定义开发的应用程序中。在本章中,您将探索如何将 ArcGIS Online 地图添加到您的应用程序中。

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

  • 使用 webmap ID 将 ArcGIS Online 地图添加到您的应用程序

  • 使用 JSON 将 ArcGIS Online 地图添加到您的应用程序

  • 是时候练习 ArcGIS Online 了

使用 webmap ID 将 ArcGIS Online 地图添加到您的应用程序

ArcGIS Server JavaScript API 包括两个用于处理 ArcGIS Online 地图的实用方法。这两种方法都可以在esri/arcgis/utils资源中找到。createMap()方法用于从 ArcGIS Online 项目创建地图。

ArcGIS Online 图库中的每张地图都有一个唯一的 ID。当您开始创建集成来自 ArcGIS Online 的地图的自定义应用程序时,这个唯一的 ID,称为 webmap,将变得重要。要获取要添加到 JavaScript API 应用程序中的地图的 webmap ID,只需单击在 ArcGIS Online 中找到的共享地图。地址栏将包含地图的 webmap ID。您需要记下这个 ID。以下截图显示了如何从浏览器的地址栏中获取特定地图的 webmap ID:

使用 webmap ID 将 ArcGIS Online 地图添加到您的应用程序

一旦您获得了要集成到自定义 JavaScript API 应用程序中的 ArcGIS Online 地图的 webmap ID,您将需要调用getItem()方法,传入 webmap ID。getItem()方法返回一个dojo/Deferred对象。Deferred对象专门用于可能不会立即完成的任务。它允许您定义在任务完成时将执行的successfailure回调函数。在这种情况下,成功完成将向success函数传递一个itemInfo对象。

这个itemInfo对象将用于在您的自定义应用程序中从 ArcGIS Online 创建地图。您将看到一个代码示例,说明了这些主题中的一些内容。

**var agoId = "fc160a96a98d4052ae191cc486961b61";**
**var itemDeferred = arcgisUtils.getItem(agoId);**

**itemDeferred.addCallback(function(itemInfo) {**
var mapDeferred = arcgisUtils.createMap(itemInfo, "map", {
mapOptions: {
  slider: true
  },
  geometryServiceURL: "http://sampleserver3.arcgisonline.com/ArcGIS/rest/services/Geometry/GeometryServer"
  });
mapDeferred.addCallback(function(response) {
map = response.map;
  map.on("resize", resizeMap);
  });
mapDeferred.addErrback(function(error) {
console.log("Map creation failed: " , json.stringify(error));
  });
**itemDeferred.addErrback(function(error) {**
console.log("getItem failed: ", json.stringify(error));
  });
}

我们将在两个单独的示例中涵盖整个功能。现在我们将检查getItem()方法的使用以及为成功或失败设置回调函数。这些代码行在前面的代码示例中有所突出。在第一行代码中,我们创建一个名为agoId的变量,并将其分配给我们想要使用的 webmap ID。接下来,我们调用getItem(),传入包含我们的 webmap ID 的agoId变量。这将创建一个新的dojo/Deferred对象,我们将其分配给一个名为itemDeferred的变量。使用这个对象,我们可以创建successerror回调函数。success函数称为addCallback,它传递一个itemInfo对象,我们将使用它来创建我们的地图。我们将在下一节中介绍地图的实际创建过程。在某种错误条件发生时,将调用addErrback函数。现在让我们看看地图是如何创建的。以下代码片段的突出显示行说明了地图的创建:

var agoId = "fc160a96a98d4052ae191cc486961b61";
var itemDeferred = arcgisUtils.getItem(agoId);

itemDeferred.addCallback(function(itemInfo) {
**varmapDeferred = arcgisUtils.createMap(itemInfo, "map", {**
**mapOptions: {**
 **slider: true**
 **},**
 **geometryServiceURL: "http://sampleserver3.arcgisonline.com/ArcGIS/rest/services/Geometry/GeometryServer"**
 **});**
**mapDeferred.addCallback(function(response) {**
**map = response.map;**
 **map.on("resize", resizeMap);**
 **});**
**mapDeferred.addErrback(function(error) {**
**console.log("Map creation failed: " , json.stringify(error));**
 **});**
itemDeferred.addErrback(function(error) {
console.log("getItem failed: ", json.stringify(error));
  });
}

createMap()方法用于实际从 ArcGIS Online 创建地图。此方法接受itemInfo的实例,该实例是从成功调用getItem()返回的;或者,您可以简单地提供 webmap ID。与我们之前检查的getItem()方法一样,createMap()也返回一个dojo/Deferred对象,您可以使用它来分配成功和错误回调函数。成功函数接受一个包含我们用来检索实际地图的map属性的response对象。当发生阻止地图创建的错误时,错误函数运行。

使用 JSON 将 ArcGIS Online 地图添加到您的应用程序

使用 webmap ID 创建地图的替代方法是使用 JSON 对象创建地图,该对象是 web 地图的表示。这在应用程序无法访问 ArcGIS Online 的情况下非常有用。看一下下面的代码片段:

var webmap = {};
webmap.item = {
  "title":"Census Map of USA",
  "snippet": "Detailed description of data",
  "extent": [[-139.4916, 10.7191],[-52.392, 59.5199]]
};

接下来,指定组成地图的图层。在前面的片段中,添加了来自 ArcGIS Online 的世界地形底图,以及一个叠加层,该叠加层向地图添加了额外的信息,如边界、城市、水体和地标以及道路。添加了一个操作图层,显示美国人口普查数据:

webmap.itemData = {
"operationalLayers": [{
  "url": " http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/Demographics/ESRI_Census_USA/MapServer",
  "visibility": true,
  "opacity": 0.75,
  "title": "US Census Map",
  "itemId": "204d94c9b1374de9a21574c9efa31164"
}],
"baseMap": {
  "baseMapLayers": [{
  "opacity": 1,
  "visibility": true,
  "url": "http://services.arcgisonline.com/ArcGIS/rest/services/World_Terrain_Base/MapServer"
  },{
  "isReference": true,
  "opacity": 1,
  "visibility": true,
  "url": "http://services.arcgisonline.com/ArcGIS/rest/services/Reference/World_Reference_Overlay/MapServer"
  }],
  "title": "World_Terrain_Base"
},
"version": "1.1"
};

一旦webmap被定义,使用createMap()从定义构建地图:

var mapDeferred = arcgisUtils.createMap(webmap, "map", {
mapOptions: {
slider: true
  }
});

在 ArcGIS Online 中练习的时间

在这个练习中,您将学习如何将 ArcGIS Online 地图集成到您的应用程序中。这个简单的应用程序将显示来自 ArcGIS Online 的美国超市访问公共地图。这张地图显示了整个美国的数据。分析中包括的超市年销售额为 100 万美元或更多。贫困人口通过从人口普查中获取的街区组贫困率(例如,10%)来表示,然后根据该百分比对该街区组中的每个街区进行符号化。看一下下面的截图:

在 ArcGIS Online 中练习的时间

绿点代表生活在距离超市一英里内的贫困人口。红点代表生活在超过一英里步行距离的贫困人口,但可能在 10 分钟的车程内,假设他们有车。灰点代表给定区域的总人口。执行以下步骤:

  1. 在编写应用程序之前,让我们探索 ArcGIS Online,看看如何找到地图并检索它们的唯一标识符。打开一个网页浏览器,转到arcgis.com

  2. 在搜索框中,输入超市,如下面的截图所示:在 ArcGIS Online 中练习的时间

  3. 这将返回一个结果列表。我们将把超市访问地图结果添加到我们的应用程序中:在 ArcGIS Online 中练习的时间

  4. 单击地图缩略图下的打开链接。在 ArcGIS Online 中练习的时间

  5. 这将在 ArcGIS Online 查看器中打开地图。您需要复制下面截图中显示的 web 地图编号。我建议您要么在某个地方写下这个编号,要么复制并粘贴到记事本中。这是地图的唯一 ID:在 ArcGIS Online 中练习的时间

  6. developers.arcgis.com/en/javascript/sandbox/sandbox.html打开 JavaScript 沙盒。

  7. 从我以下划线标记的<script>标签中删除 JavaScript 内容:

<script>
**dojo.require("esri.map");**

**function init(){**
**var map = new esri.Map("mapDiv", {**
**center: [-56.049, 38.485],**
**zoom: 3,**
**basemap: "streets"**
 **});**
 **}**
**dojo.ready(init);**
</script>
  1. 添加我们在这个练习中将使用的对象的以下引用:
<script>
**require([**
 **"dojo/parser",**
 **"dojo/ready",**
 **"dojo/dom",**
 **"esri/map",** 
 **"esri/arcgis/utils",**
 **"esri/dijit/Scalebar",**
 **"dojo/domReady!"**
 **], function(**
**parser,ready,dom,Map,arcgisUtils,Scalebar) {**
 **});**
</script>
  1. 在这个简单的例子中,我们将在应用程序中硬编码 webmap ID。在require()函数内部,创建一个名为agoId的新变量,并将其分配给您获取的 webmap ID,如下所示:
<script>
require([
        "dojo/parser",
        "dojo/ready",
        "dojo/dom",
        "esri/map", 
        "esri/arcgis/utils",
        "esri/dijit/Scalebar",
        "dojo/domReady!"
      ], function(
parser,ready,dom,Map,arcgisUtils,Scalebar) {

 **var agoId = "153c17de00914039bb28f6f6efe6d322";** 

    });

</script>
  1. 在这个练习的最后两个步骤中,我们将处理arcgisUtils.getItem()arcgisUtils.createMap()方法。这两种方法都返回所谓的Dojo/Deferred对象。您需要对Deferred对象有基本的了解,否则代码就不会有太多意义。dojo/Deferred对象专门用于可能不会立即完成的任务。它允许您定义成功和失败的回调函数,当任务完成时将执行这些函数。成功的回调函数将由Deferred.addCallback()调用,而失败函数将采用Deferred.errCallback()的形式。在getItem()的情况下,成功完成将向成功函数传递一个itemInfo对象。这个itemInfo对象将用于在您的自定义应用程序中从 ArcGIS Online 创建地图。由于某种原因未能完成将导致生成一个错误被传递给Deferred.addErrback()函数。将以下代码块添加到您的应用程序中,然后我们将进一步讨论其细节:
<script>
require([
        "dojo/parser",
        "dojo/ready",
        "dojo/dom",
        "esri/map", 
        "esri/arcgis/utils",
        "esri/dijit/Scalebar",
        "dojo/domReady!"
      ], function(
parser,ready,dom,Map,arcgisUtils,Scalebar) {

    var agoId = "153c17de00914039bb28f6f6efe6d322";
 **var itemDeferred = arcgisUtils.getItem(agoId);**

 **itemDeferred.addCallback(function(itemInfo) {**
 **var mapDeferred = arcgisUtils.createMap(itemInfo,"mapDiv", {**
 **mapOptions: {**
 **slider: true,**
 **nav:true**
 **}**
 **});**

 **});**
 **itemDeferred.addErrback(function(error) {**
 **console.log("getItem failed: ",json.stringify(error));**
 **});**

 **});**

</script>

在第一行代码中,我们调用getItem()函数,传入agoId变量,该变量引用来自 ArcGIS Online 的超市访问地图。此方法返回一个Dojo/Deferred对象,存储在名为itemDeferred的变量中。

getItem()函数获取有关 ArcGIS Online 项目(webmap)的详细信息。传递给回调的对象是一个具有以下规范的通用对象:

{
item: <Object>,
itemData: <Object>
}

假设对getItem()的调用成功,然后将这个通用的项目对象传递给addCallback()函数。在回调函数内部,我们然后调用getMap()方法,传入itemInfo对象,地图容器的引用以及定义地图功能的任何可选参数。在这种情况下,地图参数包括导航滑块和导航按钮的存在。getMap()方法然后返回另一个Dojo/Deferred对象,存储在mapDeferred变量中。在下一步中,您将定义处理将被传回的Deferred对象的代码块。

  1. 传递给mapDeferred.addCallback()函数的对象将采用以下形式:
{
  Map: <esri/Map>,
itemInfo: {
item: <Object>,
itemData: <Object>
  }
}
  1. 添加以下代码来处理返回的信息:
<script>
require([
        "dojo/parser",
        "dojo/ready",
        "dojo/dom",
        "esri/map", 
        "esri/arcgis/utils",
        "esri/dijit/Scalebar",
        "dojo/domReady!"
      ], function(
parser,ready,dom,Map,arcgisUtils,Scalebar) {

    var agoId = "153c17de00914039bb28f6f6efe6d322";
    var itemDeferred = arcgisUtils.getItem(agoId);

    itemDeferred.addCallback(function(itemInfo) {
    var mapDeferred = arcgisUtils.createMap(itemInfo,"mapDiv", {
      mapOptions: {
      slider: true,
      nav:true
        }
      });
          **mapDeferred.addCallback(function(response) {**
 **map = response.map;**
 **});**
 **mapDeferred.addErrback(function(error) {**
 **console.log("Map creation failed: ", json.stringify(error));**
 **});**

      });
      itemDeferred.addErrback(function(error) {
          console.log("getItem failed: ",json.stringify(error));
      });

  });

</script>

成功函数(mapDeferred.addCallback)从响应中提取地图并将其分配给地图容器。

  1. 您可能希望查看解决方案文件(arcgisdotcom.html)在您的ArcGISJavaScriptAPI文件夹中,以验证您的代码是否已正确编写。

  2. 单击运行按钮后,您应该看到以下地图。如果没有,您可能需要重新检查代码的准确性:在 ArcGIS Online 上练习的时间

总结

ArcGIS Online 正变得越来越重要,作为创建和共享地图和其他资源的平台。作为开发人员,您可以将这些地图集成到自定义应用程序中。每张地图都有一个唯一的标识符,您可以使用它来将地图拉入使用 ArcGIS Server 和 JavaScript API 开发的自定义应用程序中。因为从 ArcGIS Online 返回这些地图可能需要一些时间,getItem()createMap()方法返回Dojo/Deferred对象,这些对象提供了成功和失败的回调函数。一旦成功从 ArcGIS Online 获取地图,它们就可以像任何其他地图服务一样在您的应用程序中呈现。在下一章中,您将学习如何在 JavaScript 中使用 ArcGIS API 进行移动应用程序开发。

第十二章:创建移动应用程序

JavaScript 的 ArcGIS Server API 提供对移动平台的支持。目前支持 iOS、Android 和 BlackBerry 操作系统。API 与dojox/mobile集成。在本章中,您将了解使 Web 映射应用程序通过 WebKit 浏览器以及内置手势支持成为可能的 API 的紧凑版。请记住,这与 ArcGIS API for iOS 或 Android 不同,后者用于构建可以通过应用商店提供的本机应用程序。JavaScript API 应用程序通过移动设备的 WebKit 浏览器呈现。

我们还将介绍地理位置 API 以及如何将其集成到您的 ArcGIS Server 应用程序中。地理位置 API 是 HTML5 的一部分,用于获取移动设备的位置。大多数移动浏览器都支持地理位置 API 规范,该规范提供了与托管设备关联的地理位置信息的脚本访问。

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

  • JavaScript 的 ArcGIS API – 紧凑版

  • 设置视口比例

  • 练习紧凑版的时间

  • 集成地理位置 API

  • 练习地理位置 API 的时间

JavaScript 的 ArcGIS API – 紧凑版

JavaScript 的 ArcGIS API 有一个紧凑版,可用于限制 API 的占用空间,从而使移动设备的下载速度更快。这种较小的占用空间对于移动应用程序(包括 iPhone 和 iPad)是一个很好的选择。API 标准版和紧凑版之间有两个主要区别:

  • 第一个区别是紧凑版仅加载应用程序所需的对象。例如,如果您不需要Calendar小部件,则不会加载它。

  • 第二个区别是紧凑版仅加载 32 个代码模块,而标准版加载 80 个模块。如果您需要使用紧凑版未下载的代码模块,则可以使用require()函数加载您使用的特定模块。

引用紧凑版就像在对 API 的引用末尾添加单词compact一样简单。稍后您将看到一个例子。在移动应用程序中使用 API 与您学习创建 Web 应用程序的技术没有任何不同。但是,您需要学习一些新的技术来创建移动应用程序的用户界面。有许多用于完成此任务的良好 JavaScript 移动框架,包括 Dojox Mobile 和 jQuery Mobile。移动框架会对 Web 内容进行样式处理,使其看起来像移动应用程序。Safari 浏览器看起来像 iPhone 应用程序,而 Android 浏览器看起来像 Android 应用程序。创建移动用户界面超出了本文的范围,但印刷和在线都有许多良好的资源可用。在以下代码示例中,您将看到如何添加对 JavaScript 的 ArcGIS API 紧凑版的引用。请注意在 API 末尾包含紧凑关键字。

<script src="http://js.arcgis.com/3.7compact/"></script>

设置视口比例

您将希望使用viewport <meta>标签为应用程序设置一些初始显示特性。<meta>标签应包含在网页的<head>部分中。建议初始比例为1.0,将填充整个屏幕的视口。值可以在01.0之间设置。如果不设置宽度,则在纵向模式下,移动浏览器将使用device-width。如果不设置高度,则在横向模式下,浏览器将使用device-height

<meta name="viewport" content="width=device-width, initial-scale=1" maximum-scale=1.0 user-scalable=0>

练习紧凑版的时间

在这个练习中,您将构建可能的最基本的移动地图应用程序。我们只是简单地使用 ArcGIS Server API for JavaScript 的紧凑版本来创建一个以加拿大艾伯塔省班夫镇为中心的地图应用程序。该应用程序除了缩放和平移之外将无法执行任何其他操作。除了地图之外,不会有任何用户界面。目标只是为了说明使用 API for JavaScript 构建的移动应用程序的基本结构。

这个练习将与您在之前章节中进行的练习有些不同。您将不会使用 ArcGIS API for JavaScript 沙盒。相反,您将在文本编辑器中编写代码(我推荐 Notepad++),并使用移动模拟器进行测试。

  1. 在开始这个练习之前,您需要确保您可以访问一个 Web 服务器。如果您没有访问 Web 服务器,或者您的计算机上尚未安装 Web 服务器,您可以下载并安装开源 Web 服务器 Apache(httpd.apache.org/download.cgi)。Microsoft IIS 是另一个常用的 Web 服务器,还有许多其他可以使用的 Web 服务器。在本练习中,我将假设您正在使用 Apache Web 服务器。

  2. 在您的本地计算机上安装的 Web 服务器将通过 URL http://localhost进行引用,用于访问 Web 服务器。如果您在 Windows 平台上安装了 Apache,则此 URL 指向C:\Program Files\Apache Software Foundation\Apache2.2下的htdocs文件夹。

  3. 在您的ArcGISJavaScriptAPI文件夹中,您会找到一个名为mobile_map.html的文件。我已经预先编写了一些您将在此步骤中使用的代码,这样您就可以专注于添加对紧凑版本的引用以及与移动开发相关的其他项目。将此文件用作起点,并将其复制到您的 Web 服务器的根目录下(如果您在 Windows 上使用 Apache,则为C:\Program Files\Apache Software Foundation\Apache2.2\htdocs)。

  4. 在您喜欢的文本编辑器中打开mobile_map.html。我推荐 Notepad++,但您可以使用任何文本编辑器。

  5. 添加对 API 的紧凑版本以及 Esri 样式表的引用。将以下突出显示的代码行添加到您的应用程序中:

<head>  

  <meta http-equiv="Content-Type" content="text/html; charset-utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=7,IE=9, IE=10" />

  <title>Simple Map</title>

  **<link rel="stylesheet" href="http://js.arcgis.com/3.7/js/esri/css/esri.css">**
  <link rel="stylesheet" href="http://code.jquery.com/mobile/1.1.0-rc.1/jquery.mobile-1.1.0-rc.1.min.css" />
  <script src="http://code.jquery.com/jquery-1.7.1.min.js"></script>
  <script src="http://code.jquery.com/mobile/1.1.0-rc.1/jquery.mobile-1.1.0-rc.1.min.js"></script>
  **<script src="http://js.arcgis.com/3.7compact/"></script>**

  1. 您将需要使用viewport <meta>标签属性来为您的应用程序设置一些初始的显示特性。建议初始缩放值为1.0,将填充整个屏幕的视口。值可以在01.0之间设置。如果您不设置宽度,移动浏览器在纵向模式下将使用device-width,如果您不设置高度,它们在横向模式下将使用device-height。在代码开头的<head>标签下添加以下代码行:
<meta name="viewport" content="width=device-width, initial-scale=1">
  1. <script>标签中,添加在以下代码片段中突出显示的require()函数以及我们将在本练习中使用的引用:
<script>
   **require([**
 **"esri/map",**
 **"dojo/domReady!"**
 **], function(Map) {** 
 **});**
 </script>
  1. 与使用 API for JavaScript 构建的传统 Web 地图应用程序一样,您将创建一个<div>标签来容纳移动应用程序的地图。对于移动应用程序,您将希望样式化地图,使其占据移动应用程序的整个视口。通过分别将宽度和高度设置为100%来实现这一点。将<div>地图容器添加到您的应用程序中。确保将宽度和高度的样式设置为100%
<div data-role="page">
  <div data-role="header">
    <h1>Simple Map</h1>
  </div><!-- /header -->
  <div data-role="content">
       **<div id="mapDiv" style="width:100%; height:100%;"></div>**
  </div><!-- /content -->

  <div data-role="footer">
    <h4>Page Footer</h4>
  </div><!-- /footer -->
</div><!-- /page -->
  1. 移动设备可以通过旋转设备简单地在标准模式或横向模式下显示其视口。您的应用程序将需要在发生这些事件时处理这些事件。在<body>标签中添加onorientationchange()事件。onorientationchange()事件引用了一个名为orientationChanged()的 JavaScript 函数,我们还没有定义。我们将在下一步中进行定义:
<body **onorientationchange="orientationChanged();"**>
  1. 创建一个新的Map对象,设置底图,并将地图居中以及缩放比例级别:
<script type="text/javascript">
  require([
      "esri/map",
      "dojo/domReady!"
      ], function(Map) {
        **map = new Map("mapDiv", {**
 **basemap: "streets",**
 **center:[-115.570, 51.178], //long, lat**
 **zoom: 12**
 **});**
});
</script>
  1. 创建orientationChanged()JavaScript 函数,如下面的代码所示。此函数可以添加到<script>标记的任何位置:
<script type="text/javascript">
      require([
        "esri/map",
        "dojo/domReady!"
      ], function(Map) {

        map = new Map("mapDiv", { 
          basemap: "streets",
          center:[-115.570, 51.178], //long, lat
          zoom: 12
        });

    **function orientationChanged() {**
 **if(map) {**
 **map.reposition();**
 **map.resize();**
 **}**
 **}**
      });
    </script>
  1. 保存文件。

  2. 打开 Web 浏览器并加载模拟器。我推荐iphone4simulator.com,但还有许多其他可以使用的。这些网站模拟网站或应用程序的外观和行为。

注意

如果您愿意,也可以将这些练习文件上传到防火墙外的 Web 服务器上,以便在实际移动设备而不是模拟器上查看它们。

  1. 如果您使用 Apache,那么您很可能已将文件保存在 Web 服务器的根位置,即C:\Program Files\Apache Software Foundation\Apache2.2\htdocs。然后可以通过 URLhttp://localhost/mobile_map.html在 Web 浏览器中访问该文件。在模拟器地址栏中输入http://localhost/mobile_map.html,如下面的屏幕截图所示。您应该会看到地图出现。练习紧凑版本构建

JavaScript API 的紧凑版本创建了缩放比例滑块的压缩版本。这是一个简单的地图应用程序,但希望它能说明构建移动地图应用程序的基本特征。

  1. 您可以使用缩放滑块放大和缩小,并记住 ArcGIS Server JavaScript API 还支持手势,因此您也可以使用捏合手势放大和缩小。但是,请记住这在模拟器中不起作用。使用应用程序界面上的放大和缩小按钮进行放大和缩小,如下面的屏幕截图所示:练习紧凑版本构建

  2. 您可能需要查看解决方案文件(mobile_map_solution.html)在您的ArcGISJavaScriptAPI文件夹中,以验证您的代码是否已正确编写。

集成地理位置 API

地理位置 API 可以与您的 ArcGIS Server 应用程序集成,以获取移动设备的位置。它也可以用于从基于 Web 的应用程序获取位置,但这并不像使用 IP 地址而不是 GPS 或基站三角测量那样准确。

此 API 具有内置安全性,需要在应用程序中使用此功能之前从最终用户获得明确许可。移动和 Web 应用程序都将显示一个提示,请求权限获取设备的当前位置。此提示将类似于以下屏幕截图所示:

集成地理位置 API

大多数浏览器都支持地理位置 API 规范,该规范提供了与托管设备关联的地理位置信息的脚本访问。地理位置 API 的主要目的是识别移动设备的位置。移动设备可以通过多种方式定位,包括基站三角测量、IP 地址和 GPS 位置。Geolocation.getCurrentPosition()方法返回移动设备的当前位置。您可以轻松使用此 API 在地图应用程序上放置与当前用户位置对应的点。Geolocation.watchPosition()方法可用于跟踪位置随着位置变化而变化,每次位置变化时都会触发回调方法。因此,如果您的应用程序需要能够随时间跟踪设备的位置,则应使用watchPosition()而不是getCurrentPosition(),后者仅在单个时间点获取位置。

以下代码片段包含了一个简单的示例,详细说明了 Geolocation API 的基本用法。我们首先要做的是检查浏览器是否支持 Geolocation API。这是通过 navigator.geolocation 属性完成的,它返回一个值为 truefalse。通常,这将提示用户允许应用程序收集当前位置,并确保浏览器支持 Geolocation。

注意

要查看你的浏览器是否支持 Geolocation 或任何其他 HTML5 功能,请转到 caniuse.com/

如果浏览器支持 Geolocation API 并且最终用户允许其收集位置,那么我们就调用 geolocation.getCurrentPosition() 方法。传递给该方法的第一个参数表示成功的回调函数,如果设备成功定位,则将执行该函数。类似地,也可以提供一个错误的回调函数(locationError)。Position 对象将传递给成功的回调函数。然后可以检查这个 Position 对象以获取位置的纬度/经度坐标。这就是我们在 zoomToLocation() 函数中所做的,它接受 Position 对象作为唯一参数。然后该函数获取纬度/经度坐标并在地图上绘制点:

if (navigator.geolocation){  
  navigator.geolocation.getCurrentPosition(zoomToLocation, locationError);
}

function zoomToLocation(location) {
  var symbol = new SimpleMarkerSymbol();

  symbol.setStyle(SimpleMarkerSymbol.STYLE_SQUARE);
  symbol.setColor(new Color([153,0,51,0.75]));

  var pt = esri.geometry.geographicToWebMercator(new   Point(location.coords.longitude, location.coords.latitude));
  var graphic = new Graphic(pt, symbol);
  map.graphics.add(graphic);
  map.centerAndZoom(pt, 16);
}

function locationError(error) {
  switch (error.code) {
    case error.PERMISSION_DENIED:
      alert("Location not provided");
      break;
    case error.POSITION_UNAVAILABLE:
      alert("Current location not available");
      break;
    case error.TIMEOUT:
      alert("Timeout");
      break;
    default:
      alert("unknown error");
      break;
    }
}

练习使用 Geolocation API 的时间

在这个练习中,你将学习如何将 Geolocation API 集成到 ArcGIS Server API for JavaScript 应用程序中。

  1. developers.ArcGIS.com/en/javascript/sandbox/sandbox.html 打开 JavaScript 沙盒。

  2. 从我在以下代码片段中突出显示的 <script> 标签中删除 JavaScript 内容:

  <script>
    **dojo.require("esri.map");**

 **function init(){**
 **var map = new esri.Map("mapDiv", {**
 **center: [-56.049, 38.485],**
 **zoom: 3,**
 **basemap: "streets"**
 **});**
 **}**
 **dojo.ready(init);**
  </script>
  1. 添加我们将在本练习中使用的对象的以下引用:
<script>
  **require([**
 **"dojo/dom",**
 **"esri/map",** 
 **"esri/geometry/Point",**
 **"esri/symbols/SimpleMarkerSymbol",**
 **"esri/graphic",**
 **"esri/geometry/webMercatorUtils",**
 **"dojo/_base/Color",**
 **"dojo/domReady!"**
 **], function(dom, Map, Point, SimpleMarkerSymbol, Graphic, webMercatorUtils, Color) {** 
 **});**
</script>
  1. 创建一个以加利福尼亚州圣迭戈为中心的新 Map 对象,带有街道作为底图图层。如果你正在使用的浏览器不支持 Geolocation API,或者没有提供访问当前设备位置的权限,这将作为默认地图和缩放范围。
<script>
 require([
      "dojo/dom",
      "esri/map", 
      "esri/geometry/Point",
      "esri/symbols/SimpleMarkerSymbol",
      "esri/graphic",
      "esri/geometry/webMercatorUtils",
      "dojo/_base/Color",
      "dojo/domReady!"
    ], function(dom, Map, Point, SimpleMarkerSymbol, Graphic, webMercatorUtils, Color) {

 **map = new Map("mapDiv", {**
 **basemap: "streets",**
 **center:[-117.148, 32.706], //long, lat**
 **zoom: 12**
 **});** 
  });
</script>
  1. 创建一个 if 语句,检查浏览器是否支持 Geolocation API 并获得访问当前设备位置的权限。Navigator.geolocation 属性将返回一个 truefalse 的值。如果浏览器支持 Geolocation API 并且最终用户给予了权限,那么这个属性将包含一个 true 的值:
map = new Map("mapDiv", { 
  basemap: "streets",
  center:[-117.148, 32.706], //long, lat
  zoom: 12
});
**if (navigator.geolocation){** 
 **navigator.geolocation.getCurrentPosition(zoomToLocation,** 
**locationError);**
}
  1. 从你在上一步中添加的代码中可以看出,Geolocation.getCurrentPosition() 函数定义了两个回调函数——一个用于成功(zoomToLocation),一个用于失败(locationError)。在这一步中,你将通过添加以下代码块来创建成功的回调函数。成功的回调函数名为 zoomToLocation,将会缩放到移动设备的位置:
if (navigator.geolocation){  
  navigator.geolocation.getCurrentPosition(zoomToLocation, locationError);
        }

 **function zoomToLocation(location) {**
 **var symbol = new SimpleMarkerSymbol();**

 **symbol.setStyle(SimpleMarkerSymbol.STYLE_SQUARE);**
 **symbol.setColor(new dojo.Color([153,0,51,0.75]));**

 **var pt = webMercatorUtils.geographicToWebMercator(new Point(location.coords.longitude, location.coords.latitude));**
 **var graphic = new Graphic(pt, symbol);**
 **map.graphics.add(graphic);**
 **map.centerAndZoom(pt, 16);**
 **}**

  1. 现在,让我们添加名为 locationError() 的错误回调函数。这个函数将测试与无法找到设备当前位置相关的各种类型的错误。在你在上一步中创建的成功回调函数的下面添加以下函数:
function locationError(error) {
  switch (error.code) {
    case error.PERMISSION_DENIED:
      alert("Location not provided");
      break;
    case error.POSITION_UNAVAILABLE:
      alert("Current location not available");
      break;
    case error.TIMEOUT:
      alert("Timeout");
      break;
    default:
      alert("unknown error");
      break;
    }
}
  1. 你可能想要查看解决方案文件(geolocation.html)在你的 ArcGISJavaScriptAPI 文件夹中,以验证你的代码是否已经正确编写。

  2. 点击 Run 按钮。最初,你应该会看到类似于以下截图中显示的消息:

  3. 点击 Share Location,如果你正在使用的浏览器支持 Geolocation API,那么一个新的地图应该会显示你当前的位置,用一个符号表示。你的位置显然会与我的不同。

摘要

移动 GIS 应用程序变得非常流行,ArcGIS Server API for JavaScript 可以用于快速开发在 Web 和移动应用程序中都受支持的应用程序。该 API 具有内置的手势支持,并支持 iOS、Android 和 BlackBerry 平台。API 的紧凑版本提供了较小的占地面积,在移动平台上可以快速下载。此外,您可以将地理位置 API 结合到您的应用程序中,以便定位设备并更新地图以显示当前位置。在下一章中,您将学习用于设计和创建应用程序布局的基本技术。

附录 A. 使用 ArcGIS 模板和 Dojo 设计应用程序

对于许多 Web 开发人员来说,构建 GIS 应用程序时最困难的任务之一是设计和创建用户界面。ArcGIS JavaScript API 和 Dojo 极大地简化了这项任务。Dojo 的布局 dijits 提供了一种简单高效的方式来创建应用程序布局,并且 Esri 提供了许多示例应用程序布局和模板,您可以使用这些示例快速启动。在本附录中,读者将学习快速设计应用程序布局的技巧。

Dojo BorderContainer dijit

由于 AGIS JavaScript API 直接构建在 Dojo JavaScript 框架之上,因此您自动可以访问用户界面 Dojo 库,包括布局 dijits,如BorderContainer。布局 dijits 是一组用户界面元素,您可以将其添加到应用程序中,以便对应用程序的布局进行控制。BorderContainer dijit 主要用作其他子容器的容器,并且可以是这两种设计类型之一:标题或侧边栏。您可以使用design属性定义设计类型。设计类型可以是headlinesidebar,并且都可以分为多达 5 个不同的区域:topbottomrightleftcenter。每个区域通常由 Dojo 布局元素填充。还可以嵌套区域,以更好地控制应用程序的布局。例如,您可以在主BorderContainercenter区域内包含第二个BorderContainer。使用这个第二个BorderContainer,您可以进一步划分center区域。

Dojo BorderContainer dijit

在以下代码示例中,我们将design定义为headline类型。这将导致您在代码中看到的一般配置,topbottom区域横跨整个屏幕空间的宽度。在这种情况下,您只需要为topbottom区域设置height属性:

<div id="main-pane" dojoType="dijit.layout.BorderContainer" design="headline">

在以下代码示例中,我们将design定义为sidebar。使用sidebar设计,leftright区域会扩展以占据窗口高度的100%,从而牺牲了topbottom区域的可用区域。在这种情况下,您只需要定义width样式属性,因为高度将始终为100%

<div id="main-pane" dojoType="dijit.layout.BorderContainer" design="sidebar">

在任一情况下,中心区域将根据其他区域的大小调整以适应可用的空间。您将看到的以下截图展示了BorderContainer可用的两种设计类型。第一种显示了headline样式,而第二种显示了sidebar样式。

Dojo BorderContainer dijitDojo BorderContainer dijit

其他 Dojo 布局元素

BorderContainer的每个区域(顶部、底部、左侧、右侧和中心)都可以由 Dojo 布局元素填充。这些元素包括AccordionContainerSplitContainerStackContainerTabContainer。您还可以创建一组嵌套的BorderContainer对象,以进一步划分可用的布局空间。

通过使用region属性将子元素放置在区域内,如以下代码示例所示。请注意,在突出显示的部分中,region属性设置为left。这将在left区域创建ContentPaneContentPane是一个非常基本的布局元素,用作其他小部件的容器。在这种情况下,它将容纳TabContainer(突出显示),其中包含其他ContentPane对象。

<div dojotype="dijit.layout.ContentPane" id="leftPane" region="left">
 **<div dojotype = "dijit.layout.TabContainer">**
    <div dojotype="dijit.layout.ContentPane" title = "Tab 1" selected="true">
      Content for the first tab
    </div>
    <div dojotype="dijit.layout.ContentPane" title = "Tab 2" >
      Content for the second tab
    </div>
  </div>
</div>

以下截图说明了使用ContentPaneTabContainer生成的位置和内容:

其他 Dojo 布局元素

AccordionContainer包含一组窗格,其标题可见,但一次只有一个窗格的内容可见。当用户点击标题时,窗格内容变得可见。这些是可以在小区域内容纳大量信息的优秀用户界面容器。

Esri 提供了许多示例布局,您可以使用这些布局来开始设计应用程序的布局。ArcGIS API for JavaScript 的帮助页面包含一个Samples选项卡,其中包含数十个示例脚本,您可以在应用程序中使用,包括各种布局示例。在下一节中,您将学习如何将这些示例布局之一集成到您的应用程序中。

练习使用示例布局

在此练习中,您将下载 Esri 提供的示例布局。然后,您将检查布局,以了解 Dojo 提供的基本布局元素。最后,您将对布局进行一些更改。

  1. 在开始此练习之前,您需要确保可以访问 Web 服务器。如果您无法访问 Web 服务器,或者计算机上尚未安装 Web 服务器,您可以下载并安装开源 Web 服务器 Apache(httpd.apache.org/download.cgi)。Microsoft IIS 是另一个常用的 Web 服务器,还有许多其他可供选择。在本练习中,我将假设您正在使用 Apache Web 服务器。

  2. 在本地计算机上安装的 Web 服务器将通过 URL 称为http://localhost。如果您在 Windows 平台上安装了 Apache,则指向C:\Program Files\Apache Software Foundation\Apache2.2\下的htdocs文件夹。

  3. 在 ArcGIS API for JavaScript 网站的Samples选项卡(developers.arcgis.com/en/javascript/jssamples/)中,在搜索框中搜索Layouts以生成可用布局示例的列表。

  4. 在搜索结果列表中向下滚动,直到看到以下截图中显示的带有左侧窗格的布局示例。点击这个项目:Time to practice with sample layouts

  5. 点击Download as a zip file链接下载示例。

  6. C:\Program Files\Apache Software Foundation\Apache2.2\文件夹下的htdocs文件夹中创建一个名为layout的新文件夹。将下载的文件解压缩到这个文件夹中。这将创建一个名为index.html的文件,以及cssimages文件夹。

  7. 打开 Web 浏览器,转到 URLhttp://localhost/layout/index.html,以便查看当前布局。您应该看到类似以下截图的内容:Time to practice with sample layouts

  8. 在您喜爱的文本或 Web 编辑器中打开index.html

  9. 滚动到文件底部,直到看到<body>标签。

  10. 最高级别的布局容器是BorderContainer。一个<div>标签将包含BorderContainer,所有其他子布局元素都需要位于这个<div>标签内。检查以下代码。突出显示的部分是用于定义我们顶级BorderContainer的代码。请注意,设计已设置为headline,这意味着顶部和底部区域将在整个屏幕宽度上滚动:

<body class="claro">
  **<div id="mainWindow" data-dojo-type="dijit.layout.BorderContainer" data-dojo-props="design:'headline'"**
 **style="width:100%; height:100%;">**

    <div id="header" data-dojo-type="dijit.layout.ContentPane" data-dojo-props="region:'top'">
        <div id="title">
      </div>
    </div>

    <div data-dojo-type="dijit.layout.ContentPane" id="leftPane" data-dojo-props="region:'left'">
      <div data-dojo-type="dijit.layout.TabContainer">
        <div data-dojo-type="dijit.layout.ContentPane" data-dojo-props="title:'Tab 1', selected:'true'">
          Content for the first tab
        </div>
        <div data-dojo-type="dijit.layout.ContentPane" data-dojo-props="title:'Tab 2'">
          Content for the second tab
        </div>
      </div>
    </div>

    <div id="map" data-dojo-type="dijit.layout.ContentPane" data-dojo-props="region:'center'"></div>

    <div id="footer" data-dojo-type="dijit.layout.ContentPane" data-dojo-props="region:'bottom'">
      <span id="dataSource">
      </span>
    </div>

  </div>
</body>
  1. BorderContainer内,您将找到使用ContentPane dijit 定义的几个子布局元素。ContentPane是一个非常通用的布局元素,只是包含文本或其他布局元素,例如TabContainerAccordionContainer
<body class="claro">
  <div id="mainWindow" data-dojo-type="dijit.layout.BorderContainer" data-dojo-props="design:'headline'"
    style="width:100%; height:100%;">

    **<div id="header" data-dojo-type="dijit.layout.ContentPane" data-dojo-props="region:'top'">**
      <div id="title">
      </div>
    </div>

    **<div data-dojo-type="dijit.layout.ContentPane" id="leftPane" data-dojo-props="region:'left'">**
      <div data-dojo-type="dijit.layout.TabContainer">
        **<div data-dojo-type="dijit.layout.ContentPane" data-dojo-props="title:'Tab 1', selected:'true'">**
          Content for the first tab
        </div>
 **<div data-dojo-type="dijit.layout.ContentPane" data-dojo-props="title:'Tab 2'">**
          Content for the second tab
        </div>
      </div>
    </div>

    **<div id="map" data-dojo-type="dijit.layout.ContentPane" data-dojo-props="region:'center'"></div>**

    **<div id="footer" data-dojo-type="dijit.layout.ContentPane" data-dojo-props="region:'bottom'">**
      <span id="dataSource">
      </span>
    </div>

  </div>
</body>

提示

请注意,在上一个代码示例中,每个ContentPane布局元素都有一个为每个布局元素设计的区域。在这种情况下,我们已定义了所有可用区域,除了right区域。这在以下截图中有所说明:

Time to practice with sample layouts

  1. 接下来,检查下面突出显示的代码。这段代码定义了left区域的内容。定义了一个简单的ContentPane布局元素,正如我之前提到的,它是其他布局元素或文本的一个非常简单的容器。在这个ContentPane中,我们创建了一个TabContainer布局元素并分配了两个选项卡。每个选项卡都被创建为ContentPane
<body class="claro">
  <div id="mainWindow" data-dojo-type="dijit.layout.BorderContainer" data-dojo-props="design:'headline'"
    style="width:100%; height:100%;">

      <div id="header" data-dojo-type="dijit.layout.ContentPane" data-dojo-props="region:'top'">
        <div id="title">
        </div>
      </div>

      <div data-dojo-type="dijit.layout.ContentPane" id="leftPane" data-dojo-props="region:'left'">
 **<div data-dojo-type="dijit.layout.TabContainer">**
 **<div data-dojo-type="dijit.layout.ContentPane" data-dojo-props="title:'Tab 1', selected:'true'">**
 **Content for the first tab**
 **</div>**
 **<div data-dojo-type="dijit.layout.ContentPane" data-dojo-props="title:'Tab 2'">**
 **Content for the second tab**
 **</div>**
 **</div>**
      </div>

      <div id="map" data-dojo-type="dijit.layout.ContentPane" data-dojo-props="region:'center'"></div>

      <div id="footer" data-dojo-type="dijit.layout.ContentPane" data-dojo-props="region:'bottom'">
      <span id="dataSource">
      </span>
    </div>

  </div>
</body>
  1. 一个常见的情景是创建一个包含地图图例的选项卡容器,如下面的屏幕截图所示:

  2. 现在你已经理解了创建布局元素的基本概念,你可以为right区域添加内容。添加以下突出显示的代码:

<body class="claro">
  <div id="mainWindow" data-dojo-type="dijit.layout.BorderContainer" data-dojo-props="design:'headline'"
    style="width:100%; height:100%;">

    <div id="header" data-dojo-type="dijit.layout.ContentPane" data-dojo-props="region:'top'">
      <div id="title">
      </div>
    </div>

    <div data-dojo-type="dijit.layout.ContentPane" id="leftPane" data-dojo-props="region:'left'">
      <div data-dojo-type="dijit.layout.TabContainer">
        <div data-dojo-type="dijit.layout.ContentPane" data-dojo-props="title:'Tab 1', selected:'true'">
          Content for the first tab
        </div>
        <div data-dojo-type="dijit.layout.ContentPane" data-dojo-props="title:'Tab 2'">
          Content for the second tab
        </div>
      </div>
    </div>

    **<div data-dojo-type="dijit.layout.ContentPane" id="rightPane" data-dojo-props="region:'right'">**
 **Content for right pane**
 **</div>**

    <div id="map" data-dojo-type="dijit.layout.ContentPane" data-dojo-props="region:'center'"></div>

    <div id="footer" data-dojo-type="dijit.layout.ContentPane" data-dojo-props="region:'bottom'">
      <span id="dataSource">
      </span>
    </div>

  </div>
</body>
  1. 在之前的练习中提取的css文件夹中,有一个名为layout.css的文件。这个文件包含了我们应用程序的样式信息。在文本编辑器中打开这个文件。

  2. 在下面的代码示例中找到文本#rightPane。为区域的背景颜色、前景颜色、边框样式和宽度定义了属性:

#rightPane {
  background-color:#FFF;
  color:#3f3f3f;
  border:solid 2px #224a54;
  width:20%;
}
  1. 回想一下,在你添加的上一个代码块中,我们给right区域的id设置为rightPane。CSS 部分将通过给它设置背景颜色(白色)、前景颜色、宽度和边框来设置我们的窗格。

  2. 保存文件。

  3. 如果需要,打开你的网络浏览器并重新加载http://localhost/layout/index.html,或者如果你已经打开了该页面,只需刷新页面。现在你应该看到应用程序right区域的新内容。目前,它只包含一些文本作为内容,但你可以很容易地添加额外的内容,包括用户界面小部件(dijits)。在下一步中,当我们添加AccordionContainer时,我们将这样做。

  4. 接下来,我们将AccordionContainer添加到right区域。

  5. 首先,添加对AccordionContainer资源的引用,如下面突出显示的代码所示:

dojo.require("dijit.layout.BorderContainer");
dojo.require("dijit.layout.ContentPane");
dojo.require("dijit.layout.TabContainer");
dojo.require("esri.map");
dojo.require("esri.arcgis.utils");
dojo.require("esri.IdentityManager");
**dojo.require("dijit.layout.AccordionContainer");**

  1. 现在,在right区域的ContentPane中添加AccordionContainer以及每个窗格的内容。下面的突出显示的代码应该添加到你在第 14 步中创建的ContentPane中:
<div data-dojo-type="dijit.layout.ContentPane" id="rightPane" data-dojo-props="region:'right'">
  **<div data-dojo-type="dijit.layout.AccordionContainer" >**
 **<div data-dojo-type="dijit.layout.ContentPane" title="Pane 1">**
 **Content for Pane 1**
 **</div>**
 **<div data-dojo-type="dijit.layout.ContentPane" title="Pane 2">**
 **Content for Pane 2**
 **</div>**
 **<div data-dojo-type="dijit.layout.ContentPane" title="Pane 3">**
 **Content for Pane 3**
 **</div>**
 **</div>**
</div>
  1. 保存文件。

  2. 刷新你的浏览器,看到新的AccordionContainer布局元素,如下面的屏幕截图所示。在这个练习中,你学会了如何使用 Esri 示例布局快速创建应用程序布局。

总结

设计和实现 GIS 网络地图应用程序的外观通常是开发人员的一项困难任务。设计和开发是两种非常不同的技能。大多数人在这两方面都不擅长。然而,Dojo 的布局小部件和 Esri 示例模板使得用很少的编码就能更容易地构建复杂的设计。在这个附录中,你学会了如何使用 Esri 示例快速定义和构建应用程序的布局。

posted @ 2024-05-22 12:09  绝不原创的飞龙  阅读(11)  评论(0编辑  收藏  举报