Web技术正在迅速变化,ArcGISJavaScriptAPI也是如此。无论您的开发经验如何,ArcGIS都提供了一种简单的方式来创建和管理地理空间应用程序。它为您提供了地图和可视化、分析、3D、数据管理以及对实时数据的支持。
第一章,“API基础”,旨在为整本书涉及的主题奠定坚实的基础。本章设置了跟随进一步解释的主题所需的基本环境,以及开发专业外观代码的基础。提供了对dojo和JavaScript编码的模块化模式的介绍,以及对基本ArcGIS概念的解释。用户将在需要时看到有关基本概念的简要解释,包括代码片段或图表。
第二章,“图层和小部件”,涉及API中使用的不同类型的图层以及每种类型的理想上下文。我们还将介绍Esri提供的一些最常用的内置小部件,供我们在应用程序中使用。
第三章,“编写查询”,将深入研究编写不同类型的查询、检索结果并显示它。我们将开发一个野火应用程序,以了解诸如识别、查找和查询任务等查询操作类型。我们还将学习如何使用FeatureTable小部件显示表格信息,并使用Infotemplates格式化弹出内容。
第四章,“构建自定义小部件”,将解释如何将所有代码组织成模块化小部件,并在我们的应用程序中使用它。我们将讨论如何全局配置dojo以及如何提供国际化支持。我们将通过构建涉及使用绘图工具栏的空间查询来扩展我们在上一章中开发的野火应用程序。
第五章,“使用渲染器”,深入探讨了颜色、符号、渲染器以及每种情况下如何有效使用它们的主题。本章还将处理数据可视化技术的微妙之处,以及创建符号和图片标记符号的技巧和窍门。我们将通过开发一个流量计应用程序来演示三种基本渲染器的效用:简单渲染器、唯一值渲染器和类别断点渲染器。
第六章,“处理实时数据”,将详细介绍什么构成实时数据,还将介绍如何可视化数据并获取最新更新的数据。我们将构建一个飓风追踪应用程序来演示这一点,并将利用API提供的几何引擎功能和现代浏览器提供的地理位置功能添加全球风数据仪表和天气小部件。
第七章,“地图分析和可视化技术”,将使您更接近成为地图数据科学家。本章将涵盖很多内容,从一些基础统计概念的复习开始。我们将看到代码的实际运行,并了解统计定义和要素图层统计模块如何为我们提供宝贵的统计量,这些统计量可以用于有意义地呈现地图数据。然后,我们将评估如何在渲染器中有效使用视觉变量,如colorInfo、opacityInfo、rotationInfo和sizeInfo。我们将利用所学知识开始构建一个人口统计分析门户。
第八章,“高级地图可视化和图表库”,将使用三种不同的图表库,如dojo、D3.js和Cedar,来扩展我们在上一章开始构建的人口统计门户,并为用户提供更多的视觉分析信息。
对于本书,我们需要NotePad++/Brackets编辑器,GoogleChrome/MozillaFirefox或任何现代浏览器,VisualStudioCommunityEdition2015和Node.jsforWindows。
本书是为希望使用ArcGISJavaScriptAPI开发出色的地图应用程序的JavaScript开发人员而编写的,但更重要的是,空间思维将帮助用户走得更远。
在本书中,您会发现许多文本样式,用于区分不同类型的信息。以下是一些这些样式的示例及其含义的解释。
文本中的代码词、数据库表名、文件夹名、文件名、文件扩展名、路径名、虚拟URL、用户输入和Twitter句柄显示如下:“安装IIS后,您可以在ProgramFiles文件夹内的IISExpress文件夹中找到可执行文件”
代码块设置如下:
**on(map,"layers-add-result",function(evt){**console.log("1.",earthQuakeLayer.id);...console.log("5.",worldCities.layerInfos);});任何命令行输入或输出都是这样写的:
**1\.EarthquakeLayer****2\.[Object,****Object,****...****Object]****3\.esriGeometryPoint****4\.1000****5\.[Object,Object,Object]**新术语和重要单词以粗体显示。例如,在屏幕上看到的单词,比如菜单或对话框中的单词,会出现在文本中,就像这样:“单击IISExpress应用程序名称旁边的添加按钮,然后单击安装按钮。”
警告或重要说明会以这样的方式出现在一个框中。
提示和技巧会以这种方式出现。
您可能正在阅读这本书,因为您想要使用ArcGISJavaScriptAPI将空间能力集成到您的Web应用程序中,并使其变得更加令人惊叹,或者您希望很快成为一名Web地图数据科学家。无论是什么,我们都与您同在。但是在着手实际项目之前,我们不认为需要一些基础工作。本章就是关于这个的——为本书后面使用的概念奠定坚实的基础。本章在内容上设计多样,涵盖了以下主题的许多内容:
这本书是一本示例书,我们将通过开发的应用程序来解释概念。因此,在本章开始时,确保您的开发环境已经运行起来是至关重要的。以下部分提到的大多数环境只是我们的偏好,可能不是必须的,以实现本书提供的代码示例。所有的代码示例都针对运行在基于Windows的操作系统和名为Brackets的集成开发环境(IDE)。如果您有不同的操作系统和IDE选择,我们欢迎您在您最舒适的环境中开发。
为了开发、部署和执行任何Web应用程序,我们需要以下组件:
我们在整本书中都使用了GoogleChrome,因为它提供了一些很棒的开发者工具和HTML检查工具。我们认为Mozilla也是一个很好的用于开发的浏览器。
本书中开发的应用程序是使用IISExpress进行托管的。IISExpress是一个轻量级的Web服务器,主要用于托管.NETWeb应用程序。尽管本书中的所有项目都是使用纯HTML、CSS和JavaScript开发的,但我们将使用Esri.NET资源代理来访问ArcGIS在线安全内容,并避免跨域问题。
读者可以通过安装Web平台安装程序或直接从Microsoft下载页面安装IISExpress,如下步骤所示:
"C:\ProgramFiles\IISExpress\iisexpress.exe"/path:
如果你和我们一样,你可能想立刻用编码的方式制作你的第一张地图。所以在这里。尝试将这些代码添加到BracketsIDE中的新HTML文件中。您还可以从代码存储库下载名为B04959_01_CODE01的HTML源代码,并双击HTML文件来运行它。
观察前面的代码行时,您可能已经注意到了这两件事:
单击并拖动或按任何箭头键会导致平移,但不会改变详细级别。
Shift+左键拖动、鼠标滚动、双击或单击地图上的+或*-*按钮会导致缩放和显示的详细级别发生变化。
还有其他方法可以实现缩放/平移功能。这里提到的方法只是为了获得初步的理解。
首先要做的是引用API来开发基于ArcGISJavaScriptAPI的应用程序。Esri是拥有该API的组织,但该API是免费的,可供公众使用。截至2016年3月,API的最新版本是3.15,相应的dojo工具包版本是1.10。
以下库可能是您可能需要引用的唯一库,以使用ArcGISJavaScriptAPI的功能以及许多dojo工具包,如coredojo、dijit、dgrid等:
当您访问上述URL时,您将看到一个网页,提供了API的完整文档,包括API参考、指南、示例代码、论坛和主页等多个选项卡。
下载示例代码
你可以按照以下步骤下载代码文件:
文件下载后,请确保使用最新版本的解压缩软件解压文件夹:
API参考列出了API下所有模块的详细信息、属性、方法和可用的事件。左侧窗格将大多数模块分组以便参考。例如,名为esri/layers的分组有多个从中继承的模块。以下截图显示了从esri/layers继承的不同模块的分组情况:
指南部分提供了重要主题的详细说明,比如使用查询任务,使用ArcGIS在线小部件,以及使用符号和渲染器。以下截图显示了设置地图范围的详细指南:
示例代码选项卡是另一个有用的部分,其中包含数百个示例应用程序,用于演示API中的不同概念。这些示例代码最好的部分是它们带有一个沙盒设施,你可以用来通过修改代码来玩耍。
如果你观察了代码结构,它可能如下所示:
如果你不熟悉JavaScript编码的这种模式,它被称为AMD编码模式,ArcGISJavaScriptAPI强调使用这种编码模式。在最初的章节中,我们将介绍很多关于这个的内容,以便熟悉dojo和AMD。从代码结构中,你可能已经了解到代码需要某些模块,加载这些模块的函数要求它们按照相同的顺序。在我们的情况下,一些模块是Esri模块(esri/..)和dojo模块(dojo/..)。如果你想知道是否可以需要自定义定义的模块,答案绝对是肯定的,这将是我们在本书中的主要部分。
代码中的高亮行构成了我们快速启动代码的核心:
**varmap=newMap("mapDiv",{****basemap:"national-geographic"****});**map模块接受两个参数。第一个参数是包含map对象的div容器。第二个参数是一个可选对象,它接受许多属性,可以用来设置地图的属性。
在我们的快速入门代码中,可选对象中的basemap属性设置了Esri提供的基础地图代码之一,名为national-geographic,用作背景地图显示。我们建议您尝试使用其他Esri提供的基础地图,例如以下内容:
有时,当应用程序打开时,您可能希望将其缩放到特定感兴趣的区域,而不是首先以世界范围显示地图,然后再缩放到您想要查看的区域。为了实现这一点,地图模块提供了一个属性来设置其初始范围,并在任何时候以编程方式更改其范围。
在此之前,让我们看看在地图的上下文中范围是什么。
范围是包围地图上感兴趣区域的最小边界矩形。
要了解范围,了解坐标几何将有所帮助。我们将把黄色的线段称为折线。蓝色线代表多边形(在我们的例子中是矩形):
现在,试着观察前述图表和以下图表之间的差异。
以下是关于前述图表的一些说明:
除了坐标和坐标轴之外,你可能已经发现两个图形的形状是相同的。这可能意味着两件事:
第二种可能对我们来说更重要,因为它意味着图表的实际位置没有改变,只是原点或坐标轴改变了位置。因此,相对于坐标轴,图表形状(矩形、点和线)的坐标也发生了变化。
相同的形状可以根据参考坐标系统的不同具有不同的坐标。在GIS的上下文中,这种坐标系统称为空间参考。
让我们测试一下我们的知识。尝试解决以下测验:
Q1.如果原点(矩形的左下角)是(100000,100000),点(三角形符号)的坐标将是什么?
Q2.由于多边形和折线都由一系列坐标表示,我们如何得出结论,给定一系列坐标,形状是多边形还是折线?
Q3.需要多少个坐标来表示一个矩形?
思考一下,我们很快就会给出答案。
在数字屏幕上显示世界或世界的一部分作为地图时,我们需要使用空间参考系统来识别地图上位置的坐标,这是一个与我们的图表一样的二维表面。有许多标准的空间参考系统在使用中。我们需要知道的最基本的是,每个参考系统都有一个唯一的识别号,被API所识别。用于定义特定空间参考系统的完整参数(如使用的基准、原点坐标、使用的测量单位等)也可以用来识别特定的空间参考系统。
用于识别SRS的唯一ID称为Well-knownID(wkid)。
列出用于定义空间参考系统的参数的字符串称为Well-knownText(wkt)。
例如,4326是全球坐标系统WGS84的wkid。该参考系统的测量单位是十进制度。
102100是另一个全局坐标系统的wkid,其测量单位为米。
A1.(100002,100002)—相对于原点,该点在正x方向上离开2个单位,在正y方向上离开2个单位。
A2.除非在几何对象中明确提到,否则坐标序列可以是折线或多边形。但是多边形具有一个使它与折线不同的属性——第一个和最后一个坐标必须相同。折线可以具有相同的第一个和最后一个坐标,但并非所有折线都满足这一标准。
A3.如果你的答案是4,那太棒了!但如果你的答案是2,那你太棒了。
是的。只需要两个坐标就足以定义矩形,这要归功于它的垂直性质。这两个坐标可以是任意一对对角坐标,但为了API的方便起见,我们将采用左下角坐标和右上角坐标。左下角坐标在4个坐标值对中具有最小的x和y坐标值,而右上角坐标具有最大的x和y坐标值:
这意味着我们可以在浏览器控制台中访问地图的属性。在缩放地图和所需的范围作为地图初始范围之后,使用Ctrl+Shift+I命令(在Chrome中)打开开发者工具。在JavaScript浏览器控制台中,尝试访问地图属性,getMaxScale()、getMinZoom()、getMinScale()、getMaxZoom()、getScale()和extent:
比例实际上是地图测量从现实世界测量中缩小的因素。最大比例显示地图上的最大细节,而地图的最小比例显示最少的细节。map.getMaxScale()的值小于map.getMinScale()的值,因为比例值代表倒数。因此1/591657527<1/9027(在我们的实例中分别为1/9027.977411和1/591657527.59…)。
map.getScale()给出了当前的比例,map.extent给出了地图的当前范围。我们可以使用这个extent对象来使用地图的setExtent()方法设置地图的范围。参考map模块的API文档,并导航到地图的setExtent方法。setExtent()方法接受两个参数——Extent对象和一个可选的fit对象。当我们点击文档中提供的超链接Extent对象时,它会将我们重定向到Extent模块的API文档页面:
Extent的构造函数接受一个JSON对象并将其转换为范围对象。我们可以从地图范围的JSON字符串中获取这个JSON对象:
前面的图像向我们展示了地图的范围的JSON字符串,我们已经放大到了地图的范围。以下的截图显示了坐标与我们打算放大的地图区域的关系(用矩形标出):
现在,我们可以复制JSON对象,创建一个Extent对象,并将其分配给地图的setExtent方法。但在此之前,我们需要导入Extent模块(esri/geometry/Extent)。以下截图解释了如何实现这一点。
现在当我们刷新地图时,地图将自动放大到我们设置的范围。
以下是应用程序的截图:
我们require函数所需的模块可以在应用程序顶部提供的文本框中输入。有两个多选列表框:一个列出Esri模块,另一个列出dojo模块。一旦我们开始输入我们应用程序所需的模块名称,列表就会显示与我们输入的名称匹配的建议模块。一旦我们从任一列表框中选择所需的模块,它就会被添加到require函数的模块列表中,并且适当的别名会作为参数添加到回调函数中。一旦选择了所有所需的模块,我们就可以使用右侧生成的基本模板。要设置地图的初始范围,可以通过搜索以下名称来加载所需的模块:
顾名思义,AMD编码模式依赖于将JavaScript代码模块化。有很多原因你可能需要开始编写模块化代码或模块:
虽然有许多编写模块化JavaScript的格式,比如CommonJS和ESHarmony,但我们只会处理AMD,因为最新版本的ArcGISJavaScriptAPI和基于其上的dojo工具包使用AMD风格的编码。Dojo加载器解析依赖项并在运行应用程序时异步加载模块。
在本节中,我们将看一下define和require方法,这是AMD的关键组件。
define方法定义了一个模块。一个模块可以有自己的私有变量和函数,只有被define函数返回的变量和函数才会被导入该模块的其他函数所暴露。define方法的一个示例如下:
请注意我们代码示例中的以下内容:
require方法使用自定义定义的模块或在外部库中定义的模块。让我们使用刚刚定义的模块和require方法:
你已经了解了两个dojo模块,即dojo/dom和dojo/domReady。现在,是时候熟悉一些其他很棒的dojo模块了,你应该尽可能在编写ArcGISJSAPI应用程序时使用它们。坚持使用纯dojo和EsriJS模块将在代码完整性和跨浏览器统一性方面产生巨大的回报。更重要的是,Dojo在常用的JavaScript功能方面为你带来了一些惊喜,其中一些我们将很快介绍。
你已经使用了dojo/dom模块。但是还有其他的dojodom模块,它们可以让你操作和处理dom节点:
dojo/on模块是一个由大多数浏览器支持的事件处理程序模块。dojo/on模块可以处理大多数类型对象的事件。
你应该优先使用dojo的数组模块,而不是原生JavaScript数组函数,原因有很多。Dojo的数组模块名为dojo/_base/array。
正如您所期望的那样,作为数组模块的一部分,有一个称为forEach()的迭代器方法,以及indexOf()和lastIndexOf()方法。现在来看最好的部分。有一个filter()方法,它返回一个根据特定条件过滤的数组。我们认为map()方法是一个宝石,因为它不仅遍历数组,还允许我们修改回调函数中的项目并返回修改后的数组。您是否曾想过检查数组的每个元素或至少一个元素是否满足特定条件?请查看此模块中的every()和some()方法。
这个示例代码解释了dojo数组模块的两个主要方法:
上述代码将以下内容打印到浏览器的控制台窗口中:
Day#1isMondayDay#2isTuesdayDay#3isWednesdayDay#4isThursdayDay#5isFridayDay#6isSaturdayDay#7isSunday了解ArcGIS服务器和RESTAPIArcGIS服务器是Esri公司的产品,通过在网上共享地理空间数据来实现WebGIS。我们的JavaScriptAPI能够通过RESTAPI消耗这个服务器提供的许多服务。这意味着ArcGIS服务器提供的所有这些服务都可以通过URL访问。现在,让我们看看RESTAPI接口对开发人员有多么有帮助。
这意味着通过ArcGIS服务器发布并可通过API访问的任何资源都是通过URL,如下面的屏幕截图所示:
ArcGIS服务端点的格式将是:
ArcGIS服务器提供了一个用户界面来查看这些REST端点。这个界面通常被称为服务目录。
在计划使用特定的GIS服务之前,开发人员需要查看服务目录。服务目录支持多种格式,如JSON和HTML,HTML是默认格式。如果您无法查看服务目录,您需要联系您的GIS管理员,以启用您感兴趣的服务的服务浏览功能。
让我们探索Esri提供的一个名为sampleserver3.arcgisonline.com的示例GIS服务器。
要查看任何GIS服务器的服务目录,语法是
您将在浏览器中看到这个屏幕:
服务目录中感兴趣的项目是文件夹标题标签下的链接列表和服务标题标签下的链接列表。我们鼓励您导航到这些链接中的每一个,并查看它们公开的服务类型。您将找到以下类型的服务:
我们有没有提到服务目录支持多种格式,比如JSON?我们鼓励您在URL的末尾添加一个查询字符串参数,比如f=json。要将服务目录视为HTML,只需从URL中删除查询字符串参数。
地图服务器将GIS数据公开为REST端点。
此URL中列出的所有标题标签都值得思考。我们将讨论其中一些:
UI提供了我们可以使用该特定图层(建筑足迹)进行查询的所有可能方式。查询操作似乎支持空间查询和平面表SQL查询。目前,让我们只讨论平面表查询。Where字段和ReturnFields(CommaSeparated)是处理平面表查询的字段。Where字段接受标准的SQLwhere子句作为输入,ReturnFields接受一个以逗号分隔的字段名称值,需要作为输出。但在开发的这个阶段,我们只是探索者,我们只需要看到这个接口返回的数据类型。将以下值输入到相应的文本框中:
点击查询(GET)按钮并滚动到屏幕底部。
现在让我们通过缩小Where子句来更详细地分析结果数据。注意结果中第一个要素的OBJECTID字段:
现在,结果看起来非常详细。我们正在查看的是单个要素的数据。JSON返回了几个特征键值对,其中包括displayFieldName、fieldAliases、geometryType、spatialReference、fields和features等键。
让我们看看feature键值对。features键的值是对象数组。每个对象都有名为attributes和geometry的键。属性保存对象的值,列出字段名称和其值的键值。在我们的案例中,PARCELID是字段名,"1916101009"是它的值:
几何对象表示具有环对象数组的多边形要素。每个环都是浮点数数组。我们之前处理多边形时只是一个坐标数组。但是ArcGISServer将多边形视为环的数组。要理解环的概念,请查看以下插图:
在前面的插图中,我们处理了两个不相交的多边形,但在现实世界中被视为单个单位,比如房屋和车库。ArcGIS用两个环表示多边形要素。第一个环由坐标组成,称为[[x1,y1],[x2,y2],…[x6,y6]],第二个环由坐标组成,称为[[x7,y7],..[x10,y10]]。
我们使用了ArcGISJSAPI的CDN来访问API,并尝试理解地图和Esri几何模块。我们试图通过复习坐标几何知识来更好地理解范围和空间参考。我们现在知道,范围只是一个可以使用两个坐标定义的最小边界矩形,空间参考系统类似于图表上的坐标轴。我们试图查看道具工具包提供的一些令人惊叹的模块,我们必须考虑在我们的代码中使用它们。ArcGISServer将其GIS数据和其他资源公开为RESTAPI,也就是说,它可以作为URL使用。您还了解到开发人员在开始通过API消耗任何服务之前,必须始终参考服务目录。我们在这本书中为通过项目工作制定了开发环境的偏好。下一章将涉及API中使用的不同类型的图层以及每种类型使用的理想上下文。我们还将介绍Esri提供的一些最常用的内置小部件,并在我们的应用程序中使用它们。
ArcGISJavaScriptAPI是一种功能强大且灵活的客户端地图软件,它提供了对各种空间数据源的集成支持,目前正在生产中。它还支持可视化平面文件格式,如CSV,其中包含一些纬度和经度信息。
为了充分利用ArcGISJavaScriptAPI提供的全部功能,了解它支持的数据源列表以及其公开的属性和方法是很重要的。
截至版本3.14,ArcGISJavaScriptAPI支持的数据源可以大致分为以下几类:
让我们回顾不同的数据源格式,并了解如何获取有关数据的必要信息,以在ArcGISJavaScriptAPI中使用。
API提供原生支持以渲染KML和CSV等平面文件格式。
Keyhole标记语言(KML)是一种空间文件格式,最初由Google开发,目前由OGC维护。它支持点、线和多边形几何图形,甚至图像叠加。KML是一种广为人知的XML,以其多功能性而闻名,但它非常冗长,并且在Google地图中使用。KML文件可以在任何文本编辑器中打开,如Notepad++。
CSV文件是一种存储以逗号分隔的字段值的表格数据的纯文本文件格式。CSV文件包含有关纬度和经度或坐标值(如X和Y坐标)的信息。API可以读取CSV文件,并将位置信息转换为API上的位置。
ArcGISServer可用于在Web上共享空间数据。在我们的情况下,如果我们有形状文件、个人地理数据库、文件地理数据库或企业地理数据库,我们可以使用ArcGISServer将数据作为REST服务在Web上提供。ArcGISJavaScript能够消费这些服务并将其显示在地图上。在其他空间格式的情况下,例如DWG,我们可以使用ArcGIS桌面或特征操作引擎(FME),这是一个用于转换为Esri文件格式并通过ArcGISServer发布的空间ETL工具。
通常将底图图层放在底部
这被称为图形图层,并且始终位于顶层
在处理不同类型的图层之前,我们将讨论如何将任何图层添加到地图对象中,因为这个过程对于任何图层类型都是相同的,而且非常简单。在下图中,我们可以看到所有类型的图层:
有两种方法可以将任何图层添加到地图对象中。假设prjMap是定义的地图对象的名称,我们需要添加一个图层;你可以采用以下两种方法之一:
//Method1prjMap.addLayer(layer1);/*layer1isthelayerobjectwewouldliketoaddtothemap.*///Method2prjMap.addLayers([layer1]);就是这么简单!第二种方法是首选方法,因为某些小部件或功能必须等到地图中的所有图层加载完毕才能使用。使用第二种方法将使我们能够使用在所有图层加载完毕后触发的事件处理程序。我们将在本章末讨论这些事件处理程序。
从功能上讲,可以将添加到地图的不同类型的图层分类如下:
让我们分别讨论每一个。
底图图层是可以用作参考背景地图的图层。通常,卫星图像、地形图(显示海拔高度的地图)或街道地图可以起到这个作用。底图通常是缓存的图像瓦片。这意味着底图是一个静态资源。由于它们是静态的,并且作为图像瓦片提供,我们无法与底图上看到的要素进行交互(例如查询或选择)。而且由于这是底图,这也是最底层的图层,也是首先添加到地图中的图层。
现在,API提供了不同的方法来向地图添加basemap属性:
varmap=newMap("mapDiv",{basemap:"streets"});这使我们能够在多个底图之间切换,例如卫星图像、街道地图、地形图、国家地理地图、OpenStreetMaps等。
下载名为B04959_02_CODE_01的项目文件夹,并打开index.html以了解底图库小部件的使用方法:
功能图层显示所有最新更改,因此在性质上是动态的,而不同于底图或缓存瓦片图层的相对静态性质。功能图层是可以与之交互的图层。API提供了对大多数这些图层执行不同操作的选项,例如:
功能图层将动态重投影,基于Basemap的空间参考。这意味着功能图层的空间参考系统可能与Basemap不同,它们仍然会与Basemap对齐,因为API将从服务器请求功能图层的重投影数据。有不同类型的功能图层,例如动态图层和要素图层,我们将很快处理。
图形图层在操作方面具有最大的灵活性。在这里,您可以向属性对象添加尽可能多的数据。您可以分配或修改其几何(使用绘图工具栏或甚至以编程方式),添加符号,查询它(对于功能图层,查询或更新操作可能被禁用),删除它,用它来选择功能图层中的要素,或者仅将其用作标注工具。但是图形图层的寿命也是最短的,因为它在会话结束后不会持久存在-这些只是存储在客户端上。由于这些属性,将图形图层作为最顶层是有意义的,不是吗?
处理图形图层时,开发人员需要注意输入数据源的空间参考。esri/geometry/webMercatorUtils是一个方便的模块,可以让我们将Web墨卡托坐标转换为地理坐标,反之亦然。
我们对图层的功能分类有了一瞥。API提供了一系列模块,用于从不同数据源加载图层,这些数据源通常属于我们研究过的功能分类之一。我们将回顾API提供的一些最重要的图层类型以及它公开的方法和属性。
这是由ArcGISServer提供的缓存Tiledmap图层:
首选别名
为什么我们需要使用不同的Basemap,当我们已经有Esri提供的很多选项呢?嗯,我们发现了来自NOAA的美学和视觉信息丰富的瓦片地图服务,显示了世界地形和海底地形(海底高程差异)的彩色阴影浮雕:
您可以考虑将其用作Basemap,用于显示任何全球现象,如灾害或地震。我们该如何做呢?如果您查看此模块的构造函数,它会查找一个必需的URL参数和一个可选的options参数。
现在,让我们尝试将其作为ArcGISTiledMapLayer来使用(代码参考:B04959_02_CODE1.html):
Tiledmap服务的服务目录为我们提供了许多有用的信息,开发人员在使用应用程序中的Tiledmap服务之前应该考虑。让我们查看先前提到的ArcGISTiledMapServiceLayer的服务目录。在下一节提供的服务目录截图中,开发人员可以了解有关数据源的许多信息:
瓦片地图服务或底图的空间参考是开发人员在编码的初始阶段经常忽视的重要属性之一。瓦片地图服务的空间参考设置为整个地图的空间参考。添加到地图中的操作图层,如动态地图服务和要素图层,符合此空间参考,无论它们各自的空间参考是什么。
TileInfo提供了有关TiledMapService遵循的平铺方案的信息。详细级别可用于设置地图的缩放范围。
范围和比例信息为我们提供了有关可见瓦片范围的信息。
从项目文件夹B04959_02_CODE_02下载完整的代码,并查看您美丽的瓦片地图的效果。
这个模块,顾名思义,是来自ArcGISServerRESTAPI的动态托管资源:
动态地图图层实际上代表了非缓存地图服务公开的所有数据。出于同样的原因,动态地图图层是一种复合图层,因为地图服务通常具有多个图层。
我们将在一会儿看到这意味着什么。我们将参考服务目录(是的,这是一个花哨的术语,用于指导我们导航到地图服务URL时出现的界面)。
您将能够看到地图服务公开的所有数据图层。因此,当您使用此地图服务时,所有数据将作为单个DynamicMapService图层的一部分显示在地图上:
如果您无法看到先前显示的任何服务的服务目录,则并不意味着该服务已离线;可能是在生产机器上关闭了服务浏览。
确保尝试通过附加名为f值为json的查询参数来尝试URL,例如,{{url}}f=json。
之前,我们讨论了如何将ArcGISTiledMapServiceLayer添加到地图中。以下代码在现有的瓦片图层上添加了ArcGISDynamicMapService图层:
区分缓存瓦片地图服务和非缓存地图服务的属性称为TileInfo。
TileInfo包含有关详细信息的信息。详细级别确定地图将显示的离散比例级别。这些详细级别也称为缩放级别,地图的缩放控件中的标记与这些缩放级别对应。
现在,瓦片地图服务和动态地图服务响应的提供方式是相似的。两者都是作为图像提供的。瓦片地图服务为每个范围提供多个图像瓦片,而动态地图服务仅为给定范围提供一个图像。
观察前面GET请求的查询字符串中的名称-值对。您会注意到以下字段名:
练习
在前面的GET请求中,将f字段名的值从image更改为html,然后查看您会得到什么。
如果您查看API页面,您会发现此模块提供了许多属性和方法。以下表格显示了一些最重要的方法:
现在,请确保您具备以下要求以显示DynamicMapService:
以下代码片段指导您如何完成此操作(代码参考:B04959_02_CODE2.html):
setLayerDefinitions(stringArrayofLayerdefintions)方法接受where子句的字符串数组。在传递动态地图服务的图层定义时,请记住以下几点。
定义表达式(where子句)的索引应与应用表达式的图层的索引相匹配。例如,如果Cities图层在前面的地图服务中的索引为5,则图层定义将如下所示:
VarlayerDefintion=[];**layerDefinition[5]="POP>1000000";**worldCities.setLayerDefinitions(layerDefinition);一旦满足这些条件,生成的地图将如下所示。半透明的蓝色点是人口超过一百万的世界城市:
要素图层是非常灵活的实体,因为它支持高级查询、选择、渲染,有时甚至支持编辑功能。要素图层(或栅格图层)是通过它所属的地图服务中的索引来识别的:
将要素图层/图层添加到地图上与添加DynamicMapService图层或Tiledmap服务图层相同:
define(["esri/map","esri/layers/FeatureLayer"],function(Map,FeatureLayer){varmap=newMap("mapDiv");varfeatureLayer1=newFeatureLayer(featureLayer1URL);varfeatureLayer2=newFeatureLayer(featureLayer2URL);**map.addLayers([featureLayer1,featureLayer2]);**});要素图层构造函数FeatureLayer构造函数有两个参数——FeatureLayerURL和一个可选的options对象。options对象提供了一堆选项来配置FeatureLayer构造函数。其中最重要的options属性之一被命名为mode。
mode属性定义了要素图层在地图上的渲染方式。由于要素图层流式传输要素的实际几何,不像地图服务(提供动态生成的图像)或Tiledmap服务(只提供预先渲染的缓存瓦片),要素图层在地图上的渲染有一些性能考虑。要素图层可以通过四种模式进行渲染。这四种模式是API提供的常量的数值值。如果要素图层模块的回调函数别名是要素图层,那么可以使用以下装饰来访问这四种模式:
地震图层是地图服务中的第五个图层。但你也可以尝试其他要素图层。以下是一个代码片段,让你将要素图层添加到地图对象中(代码参考:B04959_02_CODE3.html):
这是FeatureLayer叠加在DynamicMapService图层和Tiledmap服务图层上的屏幕截图。半透明的彩色圆圈代表曾经发生过任何地震的地点,其震级超过6里氏标度:
当您在地图上平移或缩放时,将获取要素并触发相应的GET请求,该请求将按需获取要素。如果在加载要素图层后立即打开开发者控制台中的网络选项卡,您将能够了解很多事情:
我们将在下一章中讨论如何查询和选择要素图层中的要素。目前,我们最好知道以下方法对要素图层对象做了什么:
Infotemplates提供了一种简单的方式来提供HTML弹出窗口,显示有关要素的信息,当我们点击时。我们将在下一章中详细讨论Infotemplates。
我们已经讨论了一些关于图形图层的内容。我们知道地图对象默认包含一个图形图层,并且可以使用地图对象的graphics属性引用它。我们还可以创建自己的图形图层并将其添加到地图中。但是,地图提供的默认图形图层始终位于顶部。
让我们更多地了解图形图层和添加到图形图层的Graphic对象。图形图层是Graphic对象的容器。
Graphic对象具有以下值:
几何将具有类型(点、多点、折线、多边形和范围)、空间参考和构成几何图形的坐标。
让我们回顾一小段代码,以更好地理解这一点。这是一个为多边形构造符号的简单代码段:
图形的属性是一个键值对对象,用于存储有关图形的信息。
尝试通过将属性记录到控制台来探索这些属性。例如,如果您需要打印要素图层中的字段列表,请使用要素图层的fields属性。
以下是将有关要素图层和DynamicMapService图层的某些信息记录到控制台的代码片段(代码参考:B04959_02_CODE5.html):
on(map,"layers-add-result",function(evt){console.log("1.",earthQuakeLayer.id);console.log("2.",earthQuakeLayer.fields);console.log("3.",earthQuakeLayer.geometryType);console.log("4.",earthQuakeLayer.maxRecordCount);console.log("5.",worldCities.layerInfos);});以下是您将在控制台中获得的屏幕输出:
**1\.EarthquakeLayer****2\.[Object,****Object,****...****Object]****3\.esriGeometryPoint****4\.1000****5\.[Object,Object,Object]**Featurelayer.fields返回一个字段对象数组。每个对象包含alias、length、name、nullable和type等属性。DynamicLayer.layerInfos返回一个layerInfo对象数组。layerInfo对象提供有关图层的信息:
改变地图的范围,向地图添加图层,向地图添加一组图层,甚至点击地图或鼠标-API对所有这些都有事件处理程序。在使用事件时,让我们坚持使用dojo的on模块来处理事件。找到使用dojo的"dojo/on"模块处理事件的原型:
在前面的代码片段中,记录了某些图层属性,您可能已经注意到整个代码片段都包含在一个on语句中:
小部件是dojo的基石。小部件是可以在dojo中构建、配置和扩展的UI组件,以执行特定任务。因此,当有人为我们提供了一个完成我们需要做的任务的小部件时,我们只需稍微配置一下,然后将其提供给小部件应该驻留的容器节点引用即可实例化它。
所以,好消息是Esri为我们提供了内置小部件,可以完成很多事情,比如查询要素、地理编码地址(将文本地址转换为地图上的位置)、添加小部件以显示地图图例、添加小部件以搜索属性,甚至添加小部件以在多个基础地图之间切换。所有Esri构建的小部件都可以在API参考页面的目录部分中找到,位于esri/dijits下。
嗯,你一定不会惊讶这个小部件确实存在吧?在本章开头处理TiledMapLayers时,我们已经提醒过你了。Basemap图层小部件为我们提供了一个小部件,我们可以从基础地图库中切换基础地图。查看以下集成基础地图到我们应用程序的原型代码(代码参考:B04959_02_CODE6):
require(["esri/map","esri/dijit/BasemapGallery"],function(Map,BasemapGallery){varbasemapGallery=newBasemapGallery({showArcGISBasemaps:true,map:map},"basemapGalleryDiv");});图例小部件地图图例列出了地图中的图层和所有图层使用的符号。自己构建图例涉及获取layerinfos和drawinginfos,并将它们列在div中——这个过程听起来很麻烦。幸运的是,Esri为我们提供了dijit(可能是dojo和widget的混成词)来构建图例:
我们使用以下代码来启动图例小部件(代码参考:B04959_02_CODE6)
require(["esri/map","esri/dijit/Legend"],function(Map,Legend){on(map,"layers-add-result",function(evt){varlegendDijit=newLegend({map:map,},"legendDiv");legendDijit.startup();});});摘要本章我们涵盖了很多内容。我们试图确定数据添加到地图的过程。我们确定了数据源,如ArcGIS服务器服务、OGC数据、CSV、KML等。然后,我们介绍了支持显示和进一步操作三种主要ArcGISREST服务数据源的API提供的模块,即ArcGISTiledmap服务图层、ArcGISDynamicMapService图层和要素图层。您还学会了如何实例化图层以及如何浏览它们的属性和事件。我们还处理了一种特殊类型的图层,即图形图层,它是地图中最顶层的图层,并且用作地图中所有图形的容器对象。我们品尝了Esri提供的大量内置小部件。在下一章中,我们将深入研究编写空间查询和检索结果。您还将学习如何使用几何服务和几何引擎来处理几何操作。
查询是通过API向地图提出问题的门户。它们在API术语中被视为任务,因为形成查询并获取答案的过程是一系列必须正确执行的操作。在本章中,我们将开发一个野火位置应用程序,以了解以下概念:
在本章中,我们将开发一个应用程序,该应用程序将显示美国的活跃野火位置,并显示任何位置的野火潜在风险的背景地图。我们还将尝试通过利用API提供的组件来提供搜索/查询功能。以下屏幕截图提供了我们在本章结束时将开发的最终应用程序的大致呈现:
该应用程序将具有以下组件:
您将被重定向到一个需要您输入令牌的页面。当您提供在上一个屏幕中获得的令牌时,您可以看到我们打算查看的地图服务的服务目录。以下屏幕截图解释了这个过程:
我们将只使用包含以下重要文件的资源代理的ASP.NET变体:
proxy.ashx文件包含用于发出请求并将响应转发回客户端的服务器端代码逻辑。我们需要配置proxy.config并在其中包含我们的ArcGIS开发者凭据。以下是proxy.config页面的示例截图:
要配置proxy.config文件,请执行以下步骤:
esriConfig.defaults.io.proxyUrl="/proxy/proxy.ashx";esriConfig.defaults.io.alwaysUseProxy=true;在我们的应用程序中,proxy.ashx页面位于应用程序根目录下的proxy文件夹中。如果代理页面位于不同的应用程序中,我们需要更改esriConfig.defaults.io.proxyUrl变量的值。当我们将esriConfig.defaults.io.alwaysUseProxy值设置为true时,所有请求都将由代理处理。如果我们只需要特定的URL由代理处理,我们可能需要添加几行代码,如下所示:
urlUtils.addProxyRule({urlPrefix:"route.arcgis.com",proxyUrl:"/proxy/proxy.ashx"});urlUtils函数由esri/urlUtils模块提供。
以下图表显示了从客户端到安全ArcGISServer服务的HTTPREST请求的流程:
下载所需的库后,我们需要将以下CSS和JavaScript库添加到我们的应用程序中:
将此库作为require函数中的模块添加时,我们需要省略文件扩展名。以下屏幕截图说明了这一说法:
因此,我们将使用bootstrapmap模块而不是esri/map模块来创建地图。bootstrapmap模块接受esri/map提供的所有属性和方法,因为bootstrapmap模块只是esri/map模块的包装。
在ArcGISServer提供的数据上可以进行各种类型的查询操作。在本章中,我们将处理API提供的三种最重要的查询操作:
查询任务让我们只操作一个图层,因此查询任务的构造函数要求我们提供要素图层的URL。查询任务让我们使用属性(字段值;例如,查询人口超过200万的城市)或使用位置(例如,查找所有位于地图当前范围或自定义绘制范围内的加油站)。当满足查询条件的要素数量大于服务器设置的限制(ArcGISServer中的maxRecordCount设置)时,我们可以使用名为分页的功能以批处理模式检索所有要素。
查找任务可以在地图服务和多个字段上操作。查找任务基本上在给定地图服务中的所有图层的所有字段中搜索给定的文本。当我们不知道要搜索的字段,因此无法构造适当的SQLwhere子句来查询数据时,这是一个理想的操作依赖。
识别任务主要是基于位置的搜索操作,返回与给定几何图形(例如地图点击点)相交的给定地图服务中所有图层的所有数据。
在所有前面的任务中,我们可以限制进行搜索操作的字段或图层。以下矩阵总结了三种不同类型的查询操作可用的所有选项:
查询任务旨在查询featureLayer。因此,要实例化querytask,我们需要提供featurelayer的URL。在API的3.15版本中,该模块被命名为esri/tasks/QueryTask。
QueryTask构造函数的语法如下:
newQueryTask(url,options)QueryTask构造函数的示例如下:
require(["esri/tasks/QueryTask",...],function(QueryTask,...){varqueryTask=newQueryTask("
例如,在我们处理的ActiveWildfire地图服务中,我们想要查询包含有关ActiveWildfire图层的数据的图层。地图服务中只有一个图层,因此要素图层的图层索引为0。
当我们访问此链接并滚动到页面底部找到支持的操作部分时,我们将看到查询操作被列在那里:
使用Query任务执行查询涉及以下步骤:
查询任务基于ActiveWildfire要素图层。因此,我们将使用要素图层的URL来实例化QueryTask对象。以下代码行解释了如何实例化QueryTask:
查询对象执行以下操作:
查询对象有一个名为where的属性。该属性接受SQL的where子句,并获取满足where子句的数据。
where子句的格式如下:
query.where="
以下代码片段演示了where子句的使用:
query.where="STATE='OK'ORSTATE='WY'";当我们想要从feature类中检索所有要素时,where子句需要设置为真表达式,例如1=1。真表达式是在所有情况下都评估为true的表达式。
您可以使用真表达式检索所有要素:
query.where="1=1";注意在实践中,使用此表达式返回的要素数量由服务器设置MaxRecordCount确定,如下截图所示。默认值为1000。可以在ArcGIS服务器设置中更改此限制。
在评估字符串时,请记住将字符串值括在单引号内:
locQuery.where="STATE_NAME='OK'";输出要素集中所需的字段可以作为字段名称数组传递给query对象参数outFields:
query.outFields=["FIRE_NAME","STATE","LATITUDE","LONGITUDE"];我们可以通过将值true或false传递给query对象参数returnGeometry来指示是否需要要素的几何信息:
query.returnGeometry=true;以下截图显示了如何构造一个完整的Query对象,该对象可以从Query任务对象中设置的要素图层中检索所有要素:
我们可以从具有与另一个输入几何体的空间关系的要素图层中获取要素。我们需要定义输入几何体和要检索的要素之间的空间关系。当未定义时,默认空间关系变为交集。这意味着我们正在尝试获取与输入几何体相交的要素。
查询对象还提供了其他类型的空间关系作为常量:
通常,输入几何体可能是来自另一个要素、类或draw对象的几何体,或者在我们的情况下,当前地图的范围,如下面的代码片段所示:
varquery=newQuery();query.outFields=["FIRE_NAME","STATE","LATITUDE","LONGITUDE"];query.returnGeometry=false;query.where="1=1";query.geometry=map.extent;query.returnGeometry=false;执行查询当我们需要执行查询并检索结果时,我们需要在查询任务对象中调用查询执行方法。可以执行查询任务以获取满足查询条件的实际特征。在某些情况下,我们可能只需要满足查询条件的特征数量或查询结果的空间范围。可以在查询任务对象上执行五种类型的查询操作:
所有操作都接受查询对象作为第一个参数,并返回一个延迟对象。让我们了解三个最重要的查询任务操作的用途:查询数量,查询范围和查询特征。
当我们只想要满足查询条件的特征数量时,可以使用这个操作。以下屏幕截图显示了在一组查询特征上进行数量查询操作,给定一个查询对象(带有查询范围)。结果将是满足查询对象的特征数量:
通过数量查询特征的图示
当我们使用查询任务的executeForCount()方法时,仍然使用查询对象作为方法参数。这可以是属性查询、空间查询或两者的组合。这个方法的主要目的是快速评估查询操作返回的特征数量。有时,这可能是您需要向用户显示的唯一信息。
让我们继续创建一个用户界面来获取满足我们查询条件的特征数量。以下屏幕截图显示了一个带有文本框输入查询文本和获取数量按钮的引导面板。我们还提供了另一个隐藏的div。div包含一个显示特征数量的标签。
点击获取数量按钮时应该执行查询操作。当在查询文本框中没有提供输入时,查询将评估为真值表达式;也就是说,将返回地图范围内所有特征的数量。以下代码就是实现这一点的:
on(dom.byId("queryBtn"),"click",function(){query.outFields=["FIRE_NAME","STATE","LATITUDE","LONGITUDE"];query.returnGeometry=false;query.where=dom.byId("queryTxt").value||"1=1";query.geometry=map.extent;varqueryCountDeferred=queryTask.executeForCount(query);queryCountDeferred.then(function(count){dom.byId("FeatCountDiv").style.display="block";dom.byId("featCountLbl").innerHTML="Result:"+count+"Features";},function(err){console.log(err);});});在上述代码片段中,on是由dojo提供的事件处理程序模块(dojo/on)。dom模块的byId()方法用于获取具有ID—queryBtn的dom元素的引用。我们在queryBtn的click事件上执行了上述代码片段。请注意,在突出显示的代码中,我们处理了当查询文本框没有输入时的情况。executeForCount()方法返回一个延迟对象。当Deferred对象被解析时,使用.then()方法触发回调。在.then方法中,我们定义了两个函数;第一个函数在操作成功时触发,第二个函数在操作抛出错误时触发。我们还可以在queryTask对象上使用execute-for-count-complete事件来检索结果。
result对象只返回数量。
我们在地图上的操作结果将如下屏幕截图所示:
我们还在用户界面中引入了获取特征按钮,以检索满足查询条件的实际特征记录,并在HTML表格中显示它们。我们将在queryTask对象上执行execute()方法来实现这一点。
这种方法提供了有关正在查询的特征的最大信息。
查询特征操作的图示如下:
QueryTask对象中的execute()方法用于查询要素。此方法返回一个Deferred对象。此成功事件处理程序返回一个Featureset对象。Featureset对象返回一个包含要素数组的数组,以及有关要素的几何类型和空间参考的其他辅助信息。
要素集包含以下内容:
在我们的应用程序中,我们将尝试在单击按钮时调用execute方法,并构造一个HTML字符串,该字符串将使用称为FeatureSet的结果将其显示为HTML表。以下屏幕截图演示了如何遍历结果要素集并创建HTML表字符串:
单击获取要素按钮时,用于获取要素计数的查询对象也用于执行此查询操作。因此,理想情况下,每次更改查询文本或地图范围时,获取要素按钮和HTML查询结果将被隐藏,我们需要在单击获取要素按钮之前单击获取计数按钮。我们编写了一个函数,隐藏显示要素计数的div,并清除HTML表。代码如下所示:
functionclearQueryTbl(){dom.byId("FeatCountDiv").style.display="none";dom.byId("QueryTbl").innerHTML='';}以下屏幕截图展示了我们在地图上的代码运行情况:
当我们想要知道满足查询的要素的范围时,我们可以使用这种方法。这将在许多方面帮助我们:
以下图表说明了“范围”操作的查询:
IdentifyTask可以在地图服务中操作多个图层,并从与给定几何图形相交的所有要素中获取信息。我们将使用IdentifyTask在地图上单击并获取单击位置处的潜在野火价值。要执行IdentifyTask,我们需要遵循三个步骤:
实例化IdentifyTask涉及加载所需的模块并使用地图服务URL进行实例化。执行IdentifyTask所需的模块如下:
我们将在野火潜力地图服务上操作IdentifyTask。地图服务包含单个栅格图层和表示野火潜力级别的像素值。以下代码片段显示了如何实例化IdentifyTask:
在下面的截图中,我们构造了一个识别参数对象,该对象被地图click事件处理程序包裹。地图click事件处理程序的mapPoint属性为识别操作提供了输入几何对象:
execute()方法可以用于执行IdentifyTask。execute()方法返回Deferred对象,Deferred对象的成功回调返回IdentifyResult数组对象。
识别结果表示地图服务中一个图层中的单个已识别要素。该对象具有以下属性:
由于识别结果是一个数组对象,我们只显示一个值,我们将从识别结果对象中取出第一个值(result[0]),如下截图所示。我们需要显示的值在一个名为CLASS_DESC的属性字段中。由于这个值是由一个以冒号(:)分隔的类代码前缀和类描述组成的(例如,5:非常高),我们将根据冒号分隔字符串并仅使用描述部分。
以下截图显示了用于执行识别操作以及将识别结果显示为map点击位置的标签的代码,该位置由指针光标表示:
查找任务基本上是对地图服务中所有字段进行基于属性的搜索。查找任务的结果与IdentifyTask的结果相同,只是多了一个foundFieldName的值,表示搜索文本所在的字段名称。与Query任务和IdentifyTask类似,执行查找任务的三个步骤如下:
让我们逐一讨论这三个步骤。
要执行查找任务,需要加载以下模块:
我们需要提供地图服务的URL来实例化查找任务。以下代码段显示了我们将如何在应用程序中执行此操作:
Find任务的execute()方法可用于执行它。调用此方法将返回一个Deferred对象,在其成功回调函数中返回一个查找结果对象。我们将尝试构建一个HTML表格,就像我们为Query任务结果所做的那样,并在FindTbldiv中显示它。以下代码行用于完成此操作:
varfindTaskDeferred=find.execute(findParams);findTaskDeferred.then(function(result){vartblString=' 要素表构建一个表,显示给定要素图层的所有信息,并将其放置在给定的dom元素中。要素表是Esri小部件,可以通过加载esri/dijit/FeatureTable模块来使用。该模块允许我们选择要显示的字段。下面的截图显示了如何构建要素表以及它在应用程序中的显示方式: 当您的Web应用程序的用户点击感兴趣的要素时,他们应该看到有关他们点击的要素的一系列有用信息。弹出窗口是向用户显示特定上下文属性信息的媒介。弹出窗口补充了地图的空间信息。 最简单的弹出窗口只显示所有或选定的属性值。更高级和直观的弹出窗口在弹出窗口中使用图表和图像。 帮助创建弹出窗口的模块包括esri/InfoTemplate、esri/dijit/PopupTemplate、esri/dijit/InfoWindow和esri/dijit/Popup。 esri/dijit/PopupTemplate扩展了esri/InfoTemplate,而esri/dijit/Popup扩展了esri/dijit/InfoWindow。因此,让我们简要介绍一下InfoTemplate,然后转向Popup模板。 可以使用占位符创建InfoTemplate对象。占位符通常是属性字段名,以美元符号($)开头,用大括号({})括起来,例如${Fieldname}。 当我们需要检索感兴趣要素提供的所有字段时,字段名可以被*替换,例如${*}。 要素图层和图形对象都有InfoTemplate属性。创建的infotemplate可以设置为这些图层。InfoTemplate构造函数接受两个参数,标题和内容: 下面的截图为Activewildfire要素图层创建了infotemplate,并在弹出窗口中显示了州名、火灾名称和被点击的野火要素的面积范围等字段。Infotemplate的标题也是由占位符创建的: 本章的代码清单可以在名为B04959_03_CODE的代码文件夹中找到。 本章解释了搜索和查询数据的不同方法。我们构建了一个应用程序,可以执行查询任务、查找任务,以及识别任务。我们还发现了名为dijit的要素表以及Infotemplates的实用性。在下一章中,我们将看到如何将所有代码组织成模块化小部件,并在应用程序中使用它。我们还将讨论涉及使用绘图工具栏的空间查询的构造,以及创建由应用程序用户定义的输入几何体。 本章的主要目标是开发一个自定义小部件,可以执行空间查询并在简单的HTML表格中显示结果。在构建自定义小部件的过程中,您将学习以下主题: Dojo有一个名为dojoConfig的全局对象,其中包含所有的配置参数。我们可以修改dojoConfig对象来配置选项和默认行为,以适应道场工具包的各个方面。 dojoConfig对象让我们定义在我们的Web应用程序中定义的自定义模块的位置,并使用包名标记它。因此,当我们需要加载这些自定义模块时,我们可以使用包名来引用文件夹位置。 让我们看看这些选项对我们有什么作用: 在dojoConfig对象中将cacheBust配置为True的效果 在道场中编写类的主要目的是开发独立的小部件。道场专门为我们提供了一个支持小部件开发的模块:dijit/_WidgetBase。我们还需要其他辅助模块,如dijit模板模块、道场解析和道场国际化模块,以在Web应用程序中开发一个完整的小部件。 WidgetBase模块关联的关键方面是小部件的生命周期概念。小部件的生命周期为我们提供了在小部件的不同阶段使用的方法,即从小部件的初始化,到其dom节点完全加载并可被应用程序利用,直到小部件的销毁。 define([//class"dojo/_base/declare",//widgitclass"dijit/_WidgetBase","dojo/domReady!"],function(declare,_WidgetBase){**returndeclare([_WidgetBase],{**/*Classdeclarationinherits"dijit/_WidgetBase"module*/constructor:function(){}});});dijit生命周期_WidgetBase选项提供了程序流将按特定顺序执行的几种方法。按顺序执行的一些最重要的方法如下信息图所示: 小部件生命周期信息图 上述图表可以描述如下: constructor:function(options,srcRefNode){this.domNode=srcRefNode;}postCreate:function(){**this.inherited(arguments);**}创建模板化小部件模板化小部件允许开发人员在运行时将HTML文件作为模板字符串加载。所有特定于小部件的dom节点都应在此HTML模板中定义。Dojo提供了另外两个模块,以使我们使用模板更轻松、更高效。这些模块名为dijit/_TemplatedMixin和dijit/_WidgetsInTemplateMixin。除了这两个模块,我们还需要加载一个名为dojo/text!的dojo插件,它实际上将HTML页面加载为模板字符串。插件的工作方式是在dojo/text!中的感叹号(!)后附加HTML文件路径: "dojo/text!app_widgets/widgettemplate/template/_widget.html"类属性应包括一个名为templateString的特定属性。此属性的值将是用于表示dojo/text! 让我们看一个基本的代码片段,涵盖了之前讨论的所有主题,并尝试开发一个模板小部件: 我们的模板文件非常无害,只包含一个简单的h1标题标签。正是templateString属性保存了这个HTML字符串。 app_widgets/widgettemplate/template/_widget.html文件的内容如下: /js/widgets/app.js的内容 这个文件将从index.html文件中调用,该文件具有名为templatedWidgetDiv的dom元素: 创建项目文件夹的指南如下图所示: 让我们详细讨论每一个。 我们不需要在索引页面上拥挤所有小部件的实例化。最好是我们可以定义一个模块,可以作为实例化我们需要的所有小部件的单一入口点。 我们的HTML页面将只包含对JSAPI和这个单一入口JavaScript模块的引用: 已经创建了一个基于dojo模块的示例config文件供您参考: 例如,dojo/i18n!app_widgets/widget_i18n/nls/strings指的是在app_widgets/widget_i18n/nls文件夹中定义的strings模块(请记住app_widgets是指向/js/widgets位置的包名称)。 当前的区域设置由用户的浏览器确定。在dojo中,locale通常是一个五个字母的字符串;前两个字符代表语言,第三个字符是连字符,最后两个字符代表国家。 例如,看一下以下内容: 提供国际化支持的步骤如下: "zh-cn":true,"de-at":truedefine([//class"dojo/_base/declare","dojo/_base/lang",//widgitclass"dijit/_WidgetBase",//templatedwidgit"dijit/_TemplatedMixin",//localization**"dojo/i18n!app_widgets/widget_i18n/nls/strings",**//loadingtemplatefile"dojo/text!app_widgets/widget_i18n/template/_widget.html","dojo/domReady!"],function(declare,lang,_WidgetBase,_TemplatedMixin,nls,dijitTemplate){returndeclare([_WidgetBase,_TemplatedMixin],{//assigninghtmltemplatetotemplatestringtemplateString:dijitTemplate,constructor:function(options,srcRefNode){console.log('constructorcalled');//widgetnodethis.domNode=srcRefNode;**this.nls=nls;**},//startwidget.calledbyuserstartup:function(){console.log('startupcalled');}});});小部件文件夹结构概述让我们再次审查widget文件夹结构,以便在开始任何项目之前将其用作模板: 我们将扩展上一章中开发的应用程序,添加高级功能和模块化的代码重构。让我们在应用程序中创建一个自定义小部件,该小部件可以执行以下操作: 让我们列出定义类及其相应预期回调函数装饰所需的模块。 returndeclare([_WidgetBase,_TemplatedMixin],{//assigninghtmltemplatetotemplatestringtemplateString:dijitTemplate,isDrawActive:false,map:null,**tbDraw:null,**constructor:function(options,srcRefNode){this.map=options.map;},startup:function(){},postCreate:function(){this.inherited(arguments);**this.tbDraw=newDraw(this.map,{showTooltips:true});**}...绘图工具栏可以在按钮的“单击”或“触摸”事件(在智能手机或平板电脑上)上激活,该按钮旨在指示“绘制”事件的开始。Dojo提供了一个模块,可以处理“触摸”和“单击”事件。该模块名为dijit/a11yclick。 要激活绘图工具栏,我们需要提供要绘制的符号类型。绘图工具栏提供了一组常量,对应于绘制符号的类型。这些常量是POINT,POLYGON,LINE,POLYLINE,FREEHAND_POLYGON,FREEHAND_POLYLINE,MULTI_POINT,RECTANGLE,TRIANGLE,CIRCLE,ELLIPSE,ARROW,UP_ARROW,DOWN_ARROW,LEFT_ARROW和RIGHT_ARROW。 激活绘图工具栏时,必须使用这些常量来定义所需的绘图操作类型。我们的目标是在单击绘图按钮时绘制多边形。代码如下截图所示: 一旦激活绘图工具栏,绘图操作就开始了。对于点几何,绘图操作只需单击一次。对于折线和多边形,单击会向折线添加一个顶点,双击结束草图。对于自由手折线或多边形,“单击”和“拖动”操作绘制几何图形,“松开鼠标”操作结束绘制。 绘图操作完成后,我们需要一个事件处理程序来处理绘图工具栏绘制的形状。API提供了一个draw-end事件,该事件在绘图操作完成后触发。必须将此事件处理程序连接到绘图工具栏。此事件处理程序将在小部件的postCreate()方法中的this.own()函数内定义。事件结果可以传递给命名或匿名函数: 让我们回顾一小段代码,以更好地理解这一点。这是一个简单的代码片段,用于构造一个具有半透明实心红色填充和黄色虚线的多边形符号: 在前面的截图中,SimpleFillSymbol.STYLE_SOLID和SimpleLineSymbol.STYLE_DASHDOT是SimpleFIllSymbol和SimpleLineSymbol模块提供的常量,用于为多边形和线条设置样式。 在符号的构造中定义了两种颜色:一种用于填充多边形,另一种用于着色轮廓。颜色可以由四个组件定义。它们如下: 红色、绿色和蓝色分量的取值范围为0到255,不透明度的取值范围为0到1。根据RGB颜色理论,可以使用红色、绿色和蓝色分量的组合来产生任何颜色。因此,要创建黄色,我们使用红色分量的最大值(255)和绿色分量的最大值(255);我们不希望蓝色分量对我们的颜色产生影响,所以使用0。不透明度值为0表示100%透明,不透明度值为1表示100%不透明。我们使用0.2作为填充颜色。这意味着我们的多边形需要20%的不透明度,或者80%的透明度。该组件的默认值为1。 符号只是一个通用对象。这意味着任何多边形几何体都可以使用该符号来渲染自己。现在,我们需要一个容器对象在地图上显示以前定义的符号绘制的几何体。由esri/Graphic模块提供的图形对象充当容器对象,可以接受几何体和符号。图形对象可以添加到地图的图形图层中。 地图对象中始终存在一个图形图层,可以通过使用地图的graphics属性(this.map.graphics)来访问。 小部件的主要功能是根据用户的绘制输入定义和执行查询。以下图像将为我们提供构造querytask和处理执行的一般方法: 我们将使用在上一章中使用的ActiveWildfire要素图层。在提供输入几何体时,我们将使用从draw-end事件中获取的几何体,而不是使用地图的当前范围几何体,就像我们在上一章中所做的那样。我们将获取绘制几何体内的所有要素,因此我们将使用真值表达式(1=1)作为where子句。以下代码解释了如何构造query对象以及如何执行和存储queryTask作为延迟变量: varqueryTask=newQueryTask(this.wildFireActivityURL);varquery=newQuery();query.where="1=1";query.geometry=geometryInput;query.returnGeometry=true;query.outFields=["FIRE_NAME","AREA_","AREA_MEAS"];varqueryDeferred=queryTask.execute(query);查询事件处理程序QueryTask对象上的execute方法返回一个延迟变量。这意味着我们应该使用.then()操作来引出任务执行结果。成功处理程序返回一个featureset。featureset是一组要素。要素包含图形以及一些属性。 现在,有两个操作需要执行以显示查询结果: 我们需要一个HTML模板来渲染小部件。该小部件将具有以下组件: 以下屏幕截图解释了HTML模板的构造方式: 应该使用dojo/text!插件将此HTML文件作为插件加载。完成后,可以使用此符号访问代码中由dojo-attach-point引用的所有dom元素。还应该实现处理toggleDraw按钮和clear按钮的点击事件的函数。以下屏幕截图显示了这个的基本实现: 查询返回的要素是野火位置,所有位置都具有点几何。我们可以使用SimpleMarkerSymbol或PictureMarkerSymbol来对查询返回的要素进行符号化。PictureMarker符号接受以下属性: 我们将使用应用程序的PNG资源来定义PictureMarkerSymbol: varsymbolSelected=newPictureMarkerSymbol({"angle":0,"xoffset":0,"yoffset":0,"type":"esriPMS","url":"images/fire_sel.png","contentType":"image/png","width":24,"height":24});将图形添加到地图将所有查询结果特征转换为具有我们刚刚定义的PictureMarkerSymbol的图形。此外,我们还将为每个图形添加一个infotemplate。infotemplate的内容将从查询结果属性中获取。可以通过迭代查询结果对象返回的要素来构建HTML表。以下屏幕截图清楚地说明了整个过程: 完整的代码清单可以在名为B049549_04_CODE02的文件夹中找到。 渲染器为我们提供了一种直观地使用不同符号和颜色来可视化数据的媒介。渲染器不仅是一种数据可视化技术,而且越来越被认为是一种数据分析工具。正确使用渲染器将帮助我们看到数据中的空间模式,并显示各种现象的地理分布。对基本制图学、色彩理论甚至统计学的理解将帮助我们创建更好的渲染器,最终更好地洞察可用数据。本章将涵盖以下主题: 处理颜色的Esri模块称为esri/Color。在处理颜色模块之前,让我们对颜色有一个基本的了解。 可见光谱中的任何颜色(紫罗兰到红色之间的颜色范围)都可以用红(R)、绿(G)或蓝(B)颜色的组合来表示。这就是RGB颜色模型。还有其他颜色模型,但让我们现在先使用RGB颜色模型。每种颜色R、G或B都可以用0到255的比例来表示。 以下图片显示了三种原色(R、G和B)及其叠加效果之间的关系: 当三种颜色(R、G和B)以相等比例混合时,产生的颜色总是位于灰度范围内的某个位置。以下几点值得注意: 例如,如果R=125,G=125,B=125,它将是灰色。 要使用RGB颜色模型定义颜色,可以使用以下格式: varr=g=b=125;varcolor=newColor([r,g,b]);在上面的片段中,color是esri/Color模块的一个实例,r、g和b分别是红色、绿色和蓝色的值。颜色应始终按照(r、g和b)的顺序添加为数组对象。如预期的那样,color变量存储了灰色。如果我们需要向颜色添加透明度,我们可以定义透明度值,称为alpha,它是一个介于0和1.0之间的整数,其中0表示完全透明,1.0表示不透明。透明度值将作为数组中的第四个值添加: define(["esri/Color"],function(Color){varr=g=b=100;varalpha=0.5;//50%transparencyvarcolor2=newColor([r,g,b,alpha]);})RGB值可以表示为十六进制数。例如,[255,0,0]可以表示为#FF0000。API还允许我们通过其英文命名字符串来表示颜色,例如blue: define(["esri/Color"],function(Color){varcolorString="red";varcolorHex="#FF0000";varcolor1=newColor(colorString);varcolor2=newColor(colorHex);使用符号符号是基于它们试图符号化的几何图形。因此,用于表示点、线和多边形的符号彼此不同。除了几何图形之外,定义符号所需的三个重要参数是以下: 通常提供风格作为模块常量。例如,SimpleLineSymbol.STYLE_DASHDOT、SimpleFillSymbol.STYLE_SOLID和SimpleMarkerSymbol.STYLE_CIRCLE,其中SimpleLineSymbol、SimpleFillSymbol和SimpleMarkerSymbol分别用于符号化线、多边形和点要素: 让我们先讨论基于三角形的几何符号,然后再处理非基于几何的和特殊符号。 基于几何的符号如下: 线符号构造函数是最简单的,因为它只需用样式、颜色和宽度三个参数来定义。 style是一个模块常量。该模块提供以下样式: 该模块提供了其他样式常量,如STYLE_LONGDASH,STYLE_LONGDASHDOT,STYLE_NULL,STYLE_SHORTDASH,STYLE_SHORTDASHDOT,STYLE_SHORTDASHDOTDOT,STYLE_SHORTDOT,和STYLE_SOLID。 STYLE_SOLID是默认样式,提供了一个连续的实线。 我们可以使用simpleLineSymbol.setColor(color)方法设置线的颜色;这里,color是EsriColor对象,simpleLineSymbol是SimpleLineSymbol对象的一个实例。style常量可以使用setStyle(style)方法设置。SimpleLineSymbol.toJson()是一个重要的方法,它将SimpleLineSymbol转换为ArcGIS服务器JSON表示。 以下代码片段将创建一条红色实线: varsimpleLineSymbol=newSimpleLineSymbol();varcolor=newColor("red");simpleLineSymbol.setColor(color);simpleLineSymbol.setWidth(2);SimpleMarkerSymbolSimpleMarkerSymbol方法用于表示一个点。与表示线的复杂性相比,表示点几何有一个额外的复杂性,即它接受一个轮廓参数,该参数本身是一个SimpleLineSymbol对象。 该模块提供了以下样式常量: setAngle(angle)方法按指定角度顺时针旋转符号。setColor(color)方法设置符号的颜色。setOffset(x和y)设置屏幕单位中标记的x和y偏移量。setOutline(outline)设置标记符号的轮廓。setSize(size)允许我们以像素为单位设置标记的大小。setStyle(style)设置标记符号样式。toJson()将对象转换为其ArcGIS服务器JSON表示。 导航到此URL将使您进入类似以下截图的页面。我们可以选择几乎任何类型的符号: 选择其中一个将导航到另一个页面,您可以在该页面上选择属性并生成符号代码。 嗯,我们很容易生成了生成半透明、红色、菱形SimpleMarkerSymbol(无轮廓)所需的代码: //Modulesrequired://esri/symbols/SimpleMarkerSymbol//esri/symbols/SimpleLineSymbolvarmarker=newSimpleMarkerSymbol();marker.setStyle(SimpleMarkerSymbol.STYLE_DIAMOND);marker.setColor(newColor([255,0,0,0.55]));marker.setSize(25);SimpleFillSymbolSimpleFillSymbol模块帮助我们为多边形生成符号。 STYLE参数的一些模块常量如下: SimpleFillSymbol.STYLE_SOLID是默认样式。 当我们需要描绘一个图标来象征一个点几何体时,我们可以使用这个模块。我们不需要将颜色信息作为参数提供,而是需要一个图像URL来显示一个图片作为标记符号。 导航到此URL将打开下面显示的页面。当选择图片图标时,下方会生成代码。可以重用此代码来重新创建在网页中选择的PictureMarkerSymbology。 生成的代码是PictureMarkerSymbol的JSON表示。JSON对象提供以下属性: 在这些中,imageData和url是多余的,所以如果我们可以使用URL属性,我们可以避免imageData属性。imageData属性只是图像的Base64表示。为了避免这种情况,我们可以取消网页右上角的一个框,上面写着启用Base64编码之类的字样。 此外,如果angle、xoffset和yoffset的值为0,我们也可以省略这些。 使用此网页提供的图标的URL以及ArcGISSymbolPlayground将使我们能够进一步自定义PictureMarkerSymbol。 要自定义PictureMakerSymbol,请使用以下内容: 文本符号可以用来代替标签。文本符号缺乏几何信息,因此需要附加到几何体上。 从ArcGISSymbolPlayground生成的以下片段演示了生成TextSymbol的组件: //Modulesrequired://esri/symbols/TextSymbol//esri/symbols/Fontvarfont=newFont();font.setWeight(Font.WEIGHT_BOLD);font.setSize(65);vartextSym=newTextSymbol();textSym.setFont(font);textSym.setColor(newColor([255,0,0,1]));textSym.setText("SampleText");使用渲染器当应用程序使用从Web地图或GIS服务引用的图层时,Web地图或服务本身提供了默认的绘图属性,确定图层的绘制方式。开发人员可以选择通过使用颜色、符号和渲染器来改变和增强要素的显示方式来覆盖这种行为。 您可以使用setSymbol()方法将符号应用于单个图形。当您想要将符号应用于动态、要素或图形图层中的所有图形时,可以使用渲染器。 渲染器使得可以快速地对许多要素进行符号化,可以使用单个符号或基于属性值使用多个符号。 ArcGISAPIforJavaScript中提供的一些渲染器如下: UniqueValueRenderer和ClassBreaksRenderer是基于属性的渲染器。这意味着属性值决定了要素的符号化方式。要确定在特定情况下使用UniqueValueRenderer还是ClassBreaksRenderer,需要考虑需要进行分类的字段值的性质。 如果要渲染的字段上的唯一值集合较小且离散,请考虑使用UniqueValueRenderer。 如果要渲染的字段上的唯一值集合具有广泛范围和/或是连续的,请考虑使用ClassBreaksRenderer。 UniqueValueRenderer和ClassBreaksRenderer具有defaultSymbol属性,当值或断点无法匹配时会使用。在开发过程中,您可以使用具有高对比度颜色的默认符号,快速验证是否有任何要素未能匹配渲染器的标准。 我们将开发一个流量计应用程序,以演示如何使用以下渲染器: 地图服务提供了美国各地的流量计读数,显示了测量区域的当前水位。我们试图开发的应用程序旨在演示不同的渲染技术在流量计数据上的应用。下一节中即将呈现的快照提供了我们在本章结束时将开发的最终应用程序的初步呈现。 如果您没有ArcGIS开发者帐户,请参考第三章编写查询,了解如何注册帐户并在应用程序代理中使用凭据的说明。 SimpleRenderer由esri/renderers/SimpleRenderer模块提供,其构造函数接受任何适当的符号或JSON。由于所有的流量计位置都是点位置,我们将使用SimpleMarkerSymbol来对其进行符号化。 由于我们已经讨论了如何从相应的模块构建PictureMarkerSymbol,我们将看到如何使用符号的JSON形式。使用符号的JSON表示意味着我们不再需要为每个符号和颜色单独加载模块。以下快照显示了JSON是如何形成并在SimpleRenderer构造函数中使用的: 在前面的代码中,在渲染器分配为SimpleRenderer之后,必须使用setRenderer()方法将渲染器对象设置为要素图层。此外,一旦渲染应用到要素图层上,图例应该被刷新: streamLyr.setRenderer(renderer);streamLyr.redraw();legend.refresh();应用独特值渲染器唯一值渲染器由esri/renderers/UniqueValueRenderer模块提供。唯一值渲染器允许我们为数据中一组唯一值定义不同的符号。最多可以提供三个属性字段来确定数据的唯一性。唯一值渲染器期望uniqueValueInfos对象。该对象基本上是唯一值和用于表示该值的符号之间的映射。因此,所有具有特定值的要素将由相应的映射符号渲染。我们可以为渲染器提供defaultSymbol对象,该对象将用于表示在uniqueValueInfos对象中未定义的任何值。以下是JSON表示唯一值渲染器对象,用于表示洪水阶段的唯一值。我们要表示的洪水阶段的唯一值如下: varrendererJson={"type":"uniqueValue","field1":"STAGE","defaultSymbol":{},"uniqueValueInfos":[{"value":"major","symbol":{"color":[163,193,163],"size":6,"type":"esriSMS","style":"esriSMSCircle"}},{"value":"moderate","symbol":{"color":[253,237,178],"size":6,"type":"esriSMS","style":"esriSMSCircle"}},{"value":"minor","symbol":{"color":[242,226,206],"size":6,"type":"esriSMS","style":"esriSMSCircle"}},{"value":"action","symbol":{"color":[210,105,30],"size":6,"type":"esriSMS","style":"esriSMSCircle"}}]};varrenderer=newUniqueValueRenderer(rendererJson);上述代码在应用程序中呈现如下: 以下属性可用于特征图层,根据多个视觉属性(如颜色、旋转、大小和不透明度)进行渲染: 当字段被分类和视觉区分时,它会分布在一系列值上,我们可以使用ClassBreaksRenderer。可以通过加载esri/renderers/ClassBreaksRenderer模块来使用ClassBreaksRenderer。 类别分隔渲染器与唯一值渲染器非常相似,因为类别分隔渲染器的构造函数期望一个classBreakInfos对象,这与uniqueValueInfos对象类似。 classBreakInfos是一个classBreakInfo对象数组,它将类范围和符号进行了映射。类范围由类的最小值(classMinValue)和类的最大值(classMaxValue)定义。 以下快照显示了如何使用classBreakInfo数组构建ClassBreakRendererJSON对象,并在地图上呈现: HeatmapRenderer将点数据渲染成突出显示更高密度或加权值的光栅可视化。该渲染器使用正态分布曲线在垂直和水平方向上分布值。 这个平均函数被水平和垂直应用,以产生一个模糊的影响区域,而不是一个特定的单一点。 HeatmapRenderer模块构造函数接受一个颜色数组。第一种颜色用于表示最小影响的区域,数组中的最后一种颜色用于表示像素的最高影响。我们还可以为HeatmapRenderer构造函数定义其他参数,如blurRadius、最大像素强度和最小像素强度。以下代码快照用于生成HeatmapRenderer: DotDensityRenderer提供了创建数据的点密度可视化的能力。点密度地图可以用来可视化离散空间现象的空间密度变化。我们可以使用多个字段以不同颜色在同一地图上可视化多个变量。例如,我们可以使用不同的颜色来显示各种族群的分布。地图上的密度随着用户的放大或缩小而变化。使用ScaleDependentRenderer为每个比例或缩放范围设置唯一的点密度渲染器,以便dotValue和dotSize可以在多个比例范围内变化。 ClassBreakRenderer或UniqueValueRenderer的问题在于,您必须为任何给定值分配特定的颜色。当基于明确的边界值分配离散颜色不可取时,我们可以使用BlendRenderer。 BlendRenderer让您对数据进行模糊分类。它允许您为不同字段的值分配不同的颜色,并使用一些不透明度来表示值的大小。由于我们对每个字段使用了不透明度,最终的渲染将是这些颜色的混合。这张图显示了如何混合颜色和不透明度变量以提供渲染: 以下地图显示了美国各地主要少数民族群体的地图。这样的插图可以给出主要特征的感觉,同时不完全压制其他细节: SmartMapping模块提供了许多辅助方法,帮助我们选择最佳的渲染方法。以下插图显示了SmartMapping模块提供的方法列表: 智能映射模块:esri/renderers/smartMapping 类别渲染器辅助方法,如createClassedColorRenderer()和createClassedSizeRenderer(),需要classificationMethod作为参数。如果我们需要理解每个值的重要性,选择这个值是非常重要的。 以下分类方法可用: 默认方法是等间隔的。 等间隔分类将数据平均分成预定义数量的类别。这样的分类可能不一定反映数据的偏斜。例如,如果数据范围是0-100万,而大部分数据集中在30万-50万之间,那么与其将数据分类为0-25万、25万-50万、50万-75万和75万-100万,更好的分类方案是在30万-50万之间有更多的分类范围。 自然断点、四分位数和标准差等分类方法有助于更好地分隔数据;因此,我们的数据可视化技术将在统计上更加准确。这个主题将在第七章中进行更详细的讨论,地图分析和可视化技术。 不断更新的数据给我们在检索和渲染它们方面带来了重大挑战。在本章中,我们将通过开发一个旨在跟踪飓风的应用程序来处理实时数据的两种基本方法。在本章中,您将学习以下主题: 地图服务提供以下数据: 预测和观测位置代表飓风的中心,而路径代表连接的预测和观测位置,以便了解飓风的移动方向。 在服务目录标题下,单击ArcGIS.com地图以全面了解地图服务中的数据。 ArcGISOnline是可视化和使用ArcGISServer上托管的数据的有效媒介。在ArcGISOnline中打开地图服务时,会显示默认的符号,并且我们可以了解我们在应用程序中将要使用的数据的范围。 在以下截图中,我们可以看到预测位置要素图层及其默认符号。使用的符号是PictureMarkerSymbol,它可以让我们了解过去三天(72小时)飓风的强度。 以下截图全面展示了地图服务中的所有数据,包括预测位置和路径,以及观测位置: 关闭目录中的所有图层,只打开观测位置图层。观测位置图层只是由简单的渲染器渲染的。符号不会根据任何字段值的大小而变化。它只显示过去72小时内测得的风暴活动的位置。 现在ArcGISOnline为我们提供了各种设置其符号的选项。当我们在目录中点击图层的名称时,会打开以下屏幕。它显示了基于哪些样式可以更改符号。在以下截图中,强度的风暴被选择为显示的字段,并且符号的大小基于强度值的数量: 数据可以根据各种分类技术进行分类,例如等间隔、分位数、自然间隔等。 最后,观测路径实际上显示了过去72小时飓风所经过的路径,并使用唯一值渲染器来渲染数据。 现在我们已经通过ArcGISOnline服务了解了我们的数据,我们可以使用地图服务URL构建自己的网络地图应用程序。在我们的应用程序中,我们打算包括以下内容: 我们有多个要处理的要素层。让我们尝试构建一个图层字典。在以下代码片段中,我们将尝试创建一个对象数组,其中每个对象都具有诸如URL和标题之类的属性。URL是指要素层的URL,标题属性是指我们想要引用要素层的标题: 以下代码片段解释了这个过程: varlayerDict=array.map(layerDict,function(item){varfeatLayer=newFeatureLayer(item.URL,{mode:FeatureLayer.MODE_ONDEMAND,outFields:["*"]//infoTemplate:infoTemplate});map.addLayer(featLayer);item.layer=featLayer;returnitem;});现在layerDict数组中的每个对象都将具有一个额外的图层属性,该属性保存了由URL引用的要素层。 要检索要素层,我们可以使用dojo/_base/array模块提供的array.filter()方法中的图层名称。filter方法()遍历每个对象项,并根据我们的谓词条件返回一个过滤后的数组。 以下代码行返回标题为“预测误差锥”的要素层,并将其保存在名为foreCastErrorConeFeatureLayer的变量中: varforeCastErrorConeFeatureLayer=array.filter(layerDict,function(item){returnitem.title=="ForecastErrorCone";})[0].layer;我们正在尝试对一些要素层中的要素进行符号化。我们将从过去的位置开始。过去的位置特征层默认情况下由一个带有中心点的圆表示。我们将尝试使用红旗来表示它。将采取以下方法来对其进行符号化: 以下代码行清楚地解释了这个过程: 让我们创建一个唯一值渲染器,并根据FCSTPRD字段名称的值以不同的方式对每种类型的多边形进行符号化。要创建唯一值渲染器,我们需要采取以下方法: **//GettheForecastErrorConefeaturelayer**varforeCastErrorConeFeatureLayer=array.filter(layerDict,function(item){returnitem.title=="ForecastErrorCone";})[0].layer;**//CreateaNullSimpleFillSymbol**vardefaultSymbol=newSimpleFillSymbol().setStyle(SimpleFillSymbol.STYLE_NULL);**//WithanullLineSymbolasitsoutline**defaultSymbol.outline.setStyle(SimpleLineSymbol.STYLE_NULL);varrenderer=newUniqueValueRenderer(defaultSymbol,"FCSTPRD");**//addsymbolforeachpossiblevalue**renderer.addValue('72',newSimpleFillSymbol().setColor(newColor([255,0,0,0.5])));renderer.addValue('120',newSimpleFillSymbol().setColor(newColor([255,255,0,0.5])));**//SetRenderer**foreCastErrorConeFeatureLayer.setRenderer(renderer);我们已经尝试使用PictureMarkerSymbol标志化要素图层,并使用SimpleRenderer进行渲染。对于另一个要素图层,我们使用了唯一值渲染器,以不同的方式渲染具有特定字段不同值的要素。现在让我们尝试一种称为CartographicLineSymbol的特殊符号。 我们想要使用CartographicLineSymbol来标志预测轨迹要素图层。以下显示了如何使用该符号并渲染特定要素图层: 以下代码片段对先前的方法进行了编码: varlineSymbol=newCartographicLineSymbol(CartographicLineSymbol.STYLE_DASHDOT,newColor([255,255,0]),5,CartographicLineSymbol.CAP_ROUND,CartographicLineSymbol.JOIN_MITER,5);varCartoLineRenderer=newSimpleRenderer(lineSymbol);forecastTrackLayer.setRenderer(CartoLineRenderer);当先前的渲染器应用于过去位置图层、预测轨迹和预测误差锥体图层时,我们的地图如下所示: 全球风数据也是ArcGIS实时数据提供的地图服务,提供各个位置的全球级风数据。我们的目标是合并一个仪表部件,根据悬停的风位置改变其仪表读数。风数据已经被适当地默认标志化。 以下屏幕截图显示了基于我们的全球风数据的仪表部件。地图中的箭头是风特征位置,箭头的方向表示风的方向,箭头的颜色和大小表示风的速度。两个示例中的仪表读数表示悬停在其上的特征(由一个粗黄色圆圈突出显示)。 风数据的URL已在我们先前的代码片段中提供,并已添加到layerDict数组中: varwindFeatureLayer=array.filter(layerDict,function(item){returnitem.title=="WindData";})[0].layer;现在让我们添加一个仪表部件,可以利用来自该图层的数据。该仪表由Esri的dijit(dojo部件)esri/dijit/Gauge提供。仪表构造函数非常简单。它接受一个GaugeParameter对象和容器domID。 GaugeParameter对象需要我们构建。在创建GaugeParameter对象之前,请记住以下几点: 以下代码是我们用来创建你在之前截图中看到的风速计小部件的代码: varwindGaugeParams={caption:"WindSpeedMeter",dataFormat:"value",dataField:'WIND_SPEED',dataLabelField:"STATION_NAME",layer:windFeatureLayer,color:"#F00",maxDataValue:80,title:'StationName',unitLabel:"mph"};varwindGauge=newGauge(windGaugeParams,"gauge");windGauge.startup();跟踪最新的活跃飓风让我们创建一个小部件来跟踪最新的活跃飓风。我们已经有了代表活跃飓风位置的所有图层。我们的目标是获取所有活跃飓风的最新位置,并在小部件中显示出来。 以下截图显示了我们的小部件在开发后的样子: 小部件中的下拉框列出了所有流行的活跃飓风的名称。以下网格显示了所选飓风的详情。 以下思路已经纳入到了这个小部件的开发中: 为了获取我们数据中的唯一值,查询对象有一个名为returnDistinctValues的属性,其值应为布尔值true。以下代码片段解释了该属性的用法: query.returnDistinctValues=true;此外,查询对象的outfield属性应该只列出那些需要唯一值的字段。在我们的情况下,字段名是STORMNAME。请参考以下代码片段以了解这一点: query.outFields=["STORMNAME"];为了每次都能获得更新的结果,我们需要避免缓存的查询结果。所以我们可能需要使用一个类似于1=1的模式,而不是使用一个真值表达式。 var_bust_cache_query_string:function(){varnum=Math.random();returnnum+"="+num;}现在我们可以在每次需要为查询对象的where属性分配一个值时使用这个函数: query.where=this._bust_cache_query_string();在查询对象中使用returnDistinctValues属性时,我们需要将returnGeometry属性设置为布尔值false。以下代码解释了如何形成查询任务和查询对象,以及如何使用查询结果来填充下拉框。在代码的结尾,我们将调用一个_update_hutticane_details()方法。这个方法获取所选StormName的最新详情: query.orderByFields=["DTGDESC"];以下代码行演示了如何构建所需的查询对象以及如何使用结果来填充小部件中关于最新风暴的信息: featureLayer.refreshInterval=5;//inminutes这是我们之前处理的缓存破坏技术的补充。 我们将尝试在我们的应用程序中创建一个天气小部件,该小部件显示用户所在位置的当前天气状况。用户的位置实际上是指现代浏览器中地理位置API识别的浏览器位置。当浏览器无法找到用户的位置时,我们将尝试找到地图中心的天气数据。创建天气小部件为我们提供了以下机会和挑战: 我们需要找到一个数据源来获取最新的天气数据。幸运的是,开放天气API是获取不同格式的天气数据的简单免费选项。付费计划提供更大的使用级别。对于我们的目的,免费版本效果很好。 该API提供REST端点,可提供以下类型的数据: 我们将使用当前天气数据端点来获取给定位置的天气详情。 我们将使用的基本URL是这个: varrequest=esriRequest({//Locationofthedataurl:this.url+'lat='+this.lat+'&lon='+this.lon+'&appid='+this.apikey,handleAs:"json"});如果观察正在构建的URL,它需要三个参数,即lat、lon和appid。 appid参数接受我们之前生成的应用程序密钥。我们将遵循两种方法来获取纬度和经度值: 使用地理位置API就像调用导航器对象的geolocation.getCurrentPosition()方法一样简单。该方法返回一个回调对象,其中包含浏览器的位置。以下代码行显示了如何调用geolocationAPI以获取浏览器的当前位置: getLocation:function(){if(navigator.geolocation){navigator.geolocation.getCurrentPosition(lang.hitch(this,this.showPosition));}else{console.log("Geolocationisnotsupportedbythisbrowser.");}}在上述代码中,调用对象是一个名为showPosition()的函数。showPosition()函数将位置作为回调对象。可以使用coords属性访问位置的坐标。 coords对象具有三个属性,即: 我们清楚地了解了纬度和经度,但精度是什么?精度是表示由API提供的坐标可能存在的误差的数值数量,单位为米。换句话说,位置在一个误差圆内是准确的。当我们提到它是一个误差圆时,能否在地图上将其可视化,这样我们就可以知道浏览器的大致位置,也许可以证实结果。我们尝试了一下;看起来相当准确。为了创建一个误差圆,我们采取了以下方法: 以下代码行清楚地解释了前面的过程: showPosition:function(position){console.log(position);this.accuracy=position.coords.accuracy;this.lat=position.coords.latitude;this.lon=position.coords.longitude;//errorcirclevarlocation_geom=newPoint(this.lon,this.lat,newSpatialReference({wkid:4326}));varloc_geom_proj=webMercatorUtils.geographicToWebMercator(location_geom);varlocation_buffer=geometryEngine.geodesicBuffer(loc_geom_proj,this.accuracy,"meters",false);console.log(location_buffer);varsymbol=newSimpleFillSymbol().setColor(newColor([255,0,0,0.5]));this.map.graphics.add(newGraphic(location_buffer,symbol));//this.map.setExtent(location_buffer.getExtent());this.getWeatherData();}我们将使用从showPosition()方法获取的纬度和经度来获取该位置的天气数据。 我们之前讨论了如何使用esriRequest模块向天气API发出HTTPGET请求,并请求获取浏览器提供的纬度和经度的当前天气数据。该请求是一个promise,我们将使用then方法来解析它。 下面的代码块演示了esriRequestpromise是如何被解析以及如何用来显示当前天气数据的: 我们编写的_processDate()函数如下所示: 如果你好奇之前小部件的HTML模板会是什么样子,我们有一件事要说——我们让你失望了吗?在这里: 在本章中,我们详细介绍了实时数据的构成以及如何可视化和获取最新特性。我们将讨论如何处理时态图层以及如何在后续章节中可视化时空图层。因此,我们将能够构建持续刷新的有效网络应用程序。在接下来的章节中,我们将讨论使用要素图层的统计功能的高级可视化技术,并学习有关图表库的知识。 对地图数据进行分析将揭示许多空间模式,否则这些模式将保持隐藏。API提供了许多方法来使用数据上的高级统计查询来获取这些信息。结合API提供的直观数据可视化方法,您将更接近成为地图数据科学家。在本章中,我们将首先尝试了解一些基本的统计概念,然后通过在API提供的分析和渲染模块的帮助下在代码中实际应用这些概念。具体来说,我们将涵盖以下主题: 这些数据是Esri的LivingAtlas项目的一部分。要使用这些数据,您将需要ArcGISOnline组织订阅或ArcGIS开发人员帐户。要访问此项目,您需要执行以下操作之一: 让我们讨论一些基本统计数据,以便我们可以充分利用API提供的统计功能。在进一步进行之前,我们可能需要清楚地了解五个基本统计参数: 顾名思义,这意味着数据集中的最小值。在我们的案例中,对于街区级别的家庭收入,最小统计数据表示具有最低家庭收入中位数的街区。 与最小类似,最大统计量定义了所有考虑的街区中的最大家庭收入中位数值。 Sum是一个简单而有效的统计量,它给出了所有考虑的数据的总值。 Average统计定义了所有值的算术平均值。平均值是通过将Sum统计量除以用于计算的数据值的计数来推导的。 Average=Sum/Count标准差标准差可能是从任何给定数据中推导出的最重要的统计量。标准差是数据的分散程度或数据偏离平均值或平均值的度量。当我们知道标准差时,通常可以观察到: 这是基于大多数数据遵循正态分布曲线的事实。当我们对数据进行排序并绘制数值时,直方图看起来像钟形曲线。 了解标准差和平均值的概念后,我们可以对数据进行标准化。这个过程被称为标准化,从这个过程中得到的统计量被称为标准分数(z-score)。当我们有大量值的数据集时,标准化是总结数据和量化数据的有效方法。 因此,要将任何值转换为标准分数(z-score),我们需要首先从平均值中减去该值,然后除以标准差。 z-score=(Value–Mean)/Standard_DeviationAPI提供的统计功能让我们调查API在这些基本统计量方面提供了什么。稍后我们将在我们的应用程序中使用这些统计量,以更好地了解数据。我们还将在我们的可视化技术中使用这些技术。 API提供了一个名为StatisticalDefinition的模块,可以与查询任务和查询模块一起使用,提取我们刚刚讨论的基本统计量。 模块名称:esri/tasks/StatisticDefinition 以下是用于定义统计定义对象的属性: 让我们尝试在本章开头提供的人口统计图层URL上使用这些并推导这些统计量。 以下截图显示了一个代码片段,并解释了如何为人口统计地图服务中的县级图层推导这些统计量: 可以使用这个简单的代码片段提取所需的统计数据。 代码执行后,控制台屏幕应该如下所示: **Object{MAX_MEDHINC_CY:113282,MIN_MEDHINC_CY:18549,STDDEV_MEDHINC_CY:10960.43202775655,AVG_MEDHINC_CY:42115.877187400576}****Object{Plus1StdDev:53076.309215157125,Plus2StdDev:64036.741242913675,Plus3StdDev:74997.17327067023,Minus1StdDev:31155.445159644027,Mius2StdDev:20195.013131887477…}**稍后将使用推导的统计量,如Plus1StdDev、Plus2StdDev、Plus3StdDev、Minus1StdDev、Minus2StdDev和Minus3StdDev来更好地呈现数据。 当我们有大量数据时,我们使用渲染方法对其进行分类。我们需要确定一个适当的分类方法来创建类别间断点。API支持以下分类方法: 让我们简要讨论使用每种分类方法的影响。 这种分类方法将数据分成相等的部分。我们需要知道数据范围以使用这种分类方法。当数据分散且分布良好时,应使用此方法。 自然断点是基于Jenks断点算法的分类方法。基本上,该算法在数据更加聚集的位置创建更多的断点。这是通过寻求最小化每个类的平均偏差来实现的,同时最大化每个类与其他组的平均值的偏差。换句话说,该方法旨在减少类内的方差,并最大化类间的方差。 这种方法对数据进行分类,使每个组中的数据点数量相等。 如前所述,标准差是数据偏离平均值的度量。使用这种分类方法,我们可以找出数据偏离平均值超出三个标准差的程度(异常值的情况),在两个和三个标准差之间的数据(较高和较低的值),以及距离平均值一个标准差内的数据。 对数据值进行归一化对于计算许多事情都很有用。考虑以下情景: 同样,如果我们需要传达年轻人口(年龄<35岁)占总人口的百分比,我们需要将拥有年轻人口的字段除以显示总人口的字段。 许多渲染器都有normalizationField和normalizationType属性来实现这种归一化。 normalizationField让我们定义用于归一化的字段。例如,对于Case1,Area字段和TotalPopulation字段是normalizationField。 normalizationType是需要对值执行的归一化类型。normalizationType的三个可能值是field、log和percent-of-total。例如,对于Case1,我们需要使用normalizationType作为field。对于Case2,我们需要使用log,对于Case3,我们需要使用percent-of-total作为normalizationType。 在API的3.13版本中,引入了这个插件,可以方便地计算要素图层的统计信息。使用要素图层统计插件,我们可以计算以下统计信息: 可以使用以下代码片段将插件添加到要素图层中: varfeatureLayerStats=newFeatureLayerStatistics({layer:CountyDemogrpahicsLayer});在前面的代码片段中,CountyDemogrpahicsLayer是要添加FeatureLayerStatistics插件的要素图层的名称。 插件中使用的方法所期望的通常参数是field和classificationMethod。field插件是指根据其计算统计数据的属性字段的名称。classificationMethod是指根据先前讨论的分类方法之一,计算统计数据的方法: varfeatureLayerStatsParams={field:"MEDHINC_CY",classificationMethod:'natural-breaks'};插件上的方法总是返回一个promise。以下代码片段计算了在featureLayerStatsParams中定义的字段上的基本统计值: featureLayerStats.getFieldStatistics(featureLayerStatsParams).then(function(result){console.log("Successfullycalculated%sforfield%s,%o","fieldstatistics",featureLayerStatsParams.field,result);}).otherwise(function(error){console.log("Anerroroccurredwhilecalculating%s,Error:%o","fieldstatistics",error);});结果在浏览器控制台中如下所示: **SuccessfullycalculatedfieldstatisticsforfieldMEDHINC_CY,****Object{****source:"service-query",****min:20566,****max:130615,****avg:46193.26694241171,****stddev:12564.308382029049,****count:3143,****sum:145185438,****variance:157861845.1187254****}**之前的结果提供了与我们之前使用的统计定义模块得到的相同或更多的信息。 以下代码片段计算了在featureLayerStatsParams中定义的字段上的类别分隔值: featureLayerStats.getClassBreaks(featureLayerStatsParams).then(function(result){console.log("Successfullycalculated%sforfield%s,%o","classbreaks",featureLayerStatsParams["field"],JSON.stringify(result));}).otherwise(function(error){console.log("Anerroroccurredwhilecalculating%s,Error:%o","classbreaks",error);});美化后的结果如下: 利用可用的统计数据,我们可以使用API提供的ClassBreaksRenderer轻松创建分级和连续渲染器。ClassBreaksRenderer根据某些数值属性的值对每个图形进行符号化。 模块名称:esri/renderers/ClassBreaksRenderer 通过使用属性如colorInfo、opacityInfo和sizeInfo,可以在此模块上设置颜色、大小或透明度。ClassBreaksRenderer上提供了以下方法: 让我们更详细地讨论这些。以下图表提供了一个开发渲染器的简要指南: ColorInfo是一个用于定义图层颜色渐变的对象。我们只需要在stops处提供离散的颜色值,有时也可以在渐变中只提供颜色值: 一个简单的ColorInfo对象示例如下: renderer.setColorInfo({field:"MEDHINC_CY",minDataValue:featureLayerStats.min,maxDataValue:featureLayerStats.max,colors:[newColor([255,255,255]),newColor([127,127,0])]});要创建一个分级颜色渲染器,我们需要定义一个stops对象来定义离散颜色,而不是连续颜色。一个stops对象将包含每个stop处的颜色。在定义stops时,我们不需要定义minDataValue或maxDataValue。让我们讨论一下在哪里可以获得适合我们渲染器的合适颜色方案。 在这个网站上,你可以做以下事情: 如前所述,要创建一个分级颜色渲染器,我们需要定义一个stops对象来定义离散颜色,而不是连续颜色。stops对象将包含每个停止点的颜色。stops对象是分配给渲染器对象的数组对象。stops数组对象包含具有以下属性的对象: stops对象大多看起来像这样: **varstops=****[****{****"value":27349.802772469,****"color":{"b":226,"g":235,"r":254,"a":1},****"label":"<-1.5Std.Dev."****},****{****"value":39912.112219098,****"color":{"b":185,"g":180,"r":251,"a":1},****"label":"-1.5--0.50Std.Dev."****},****{****"value":52474.421665726,****"color":{"b":161,"g":104,"r":247,"a":1},****"label":"-0.50-0.50Std.Dev."****},****{****"value":65036.731112354,****"color":{"b":138,"g":27,"r":197,"a":1},****"label":"0.50-1.5Std.Dev."****},****{****"value":77599.040558982,****"color":{"b":119,"g":1,"r":122,"a":1},****"label":"1.5-2.5Std.Dev."****}****]**现在让我们找到一种自动填充stops对象的方法。记住,我们可以从colorbrewer2.org网站上选择的颜色方案中获取颜色数组。color数组可以用来填充stops对象中每个对象的color属性。stops对象中每个对象的value属性可以从featureLayerStatistics计算的返回对象中派生出来。featureLayerStatistics计算为每个类提供最小值、最大值和标签值。我们可以将每个类的最大值分配给stops对象中每个对象的value属性: opacityInfo是一个定义特征不透明度如何计算的对象。opacityInfo对象可用于为ClassBreaksRenderer中的类设置不透明度级别。opacityInfo对象也可用于设置连续不透明度渲染器。 与colorInfo对象类似,您可以指定不透明度值作为数组,以及最小和最大数据值,或者您可以定义stops对象,在其中可以定义不透明度值。 使用opacityInfo创建一个连续渲染器: varminOpacity=0.2;varmaxOpacity=1;varopacityInfo={field:"DIVINDX_CY",minDataValue:0,maxDataValue:100,opacityValues:[minOpacity,maxOpacity]};使用opacityInfo创建一个类别不透明度渲染器让我们使用opacityInfo来渲染另一个字段,表示每个县的多样性指数。多样性指数在从0到100的范围内测量多样性。多样性指数是Esri专有的指数,定义为从同一地区随机选择的两个人属于不同种族或民族群体的可能性。多样性指数仅测量区域的多样性程度,而不是其种族构成。 我们的目标是以更高的不透明度值显示多样性指数较高的县,以及以较低的不透明度值显示多样性指数较低的县。可以使用以下代码段将不透明度值在最小值和最大值之间分割: varopacity=minOpacity+i*maxOpacity/(opacity_stat_result.classBreakInfos.length-1);在上一段代码中,opacity_stat_result是FeatureLayerSatistics模块的getClassBreaks()方法的承诺结果: SizeInfo对象定义了特征大小与数据值成比例的符号大小。 API帮助页面提到符号大小可以代表两种不同类型的数据——距离和非距离。距离数据类型指的是字段上的实际距离,非距离数据类型指的是符号的地图大小。使用sizeInfo根据树冠的实际直径表示树冠是距离数据类型的一个例子。根据交通密度表示道路大小,或者根据人口密度或中位收入表示州的大小,可以增强要素的地图呈现。 RotationInfo可用于定义标记符号的旋转方式。RotationInfo可用于表示风向、车辆方向等。必须存在指定旋转角度的字段来定义RotationInfo。允许使用两种旋转角度单位。 以下图显示了地理和算术角度之间的差异: 到目前为止,我们一直在讨论使用单个字段名称或变量来渲染要素的功能。我们还讨论了可以用来渲染要素的各种视觉变量,如颜色、不透明度、大小、旋转等。如果我们能够结合这些视觉变量,并根据多个字段值来渲染要素呢? 例如,在县级别进行映射时,我们可以考虑使用颜色来表示人口密度,使用不透明度来表示家庭收入中位数,并使用大小来表示联邦教育支出占人口字段的百分比。我们选择使用的字段数量限于四个视觉变量,即:颜色、不透明度、大小和旋转。 多变量映射是由ClassBreaksRenderer中的visualVariables属性启用的。让我们尝试使用两个视觉变量,即colorInfo和opacityInfo,我们用它们来演示两个不同的人口统计参数,即家庭收入中位数和多样性指数。我们当前的目标是使用颜色来表示家庭收入中位数,并同时根据多样性指数确定要素的不透明度值: 有了所有这些统计数据的知识,现在是时候使用API提供的智能映射模块进行智能映射了。想象一下,一个模块可以根据一些基本输入自动调用渲染器参数,例如需要生成渲染器的要素图层和分类方法。 模块名称:esri/renderers/smartMapping 智能映射模块提供了几种方法,每种方法都会生成一个渲染器。智能映射模块可以生成的渲染器包括: 智能映射甚至可以根据底图进行渲染。例如,某些颜色或不透明度渲染器在较暗的底图(如卫星图)上效果良好,而某些渲染器在较亮的底图(如街道地图)上效果良好。 通过三个简单的步骤,您可以让API决定颜色方案,并为您创建类颜色渲染器: 以下代码解释了如何使用智能映射创建一个分类颜色渲染器: 我们可以通过编辑scheme对象来手动定义颜色方案,该对象是createClassedColorRenderer()方法的参数对象中的一个属性。 智能制图 我们离成为地图数据科学家又近了一步。在本章中,我们涵盖了很多内容,从简要统计概念的复习开始。然后我们看到了代码如何运行,统计定义和要素图层统计模块如何给我们提供宝贵的统计量,可以用来有意义地渲染地图数据。然后我们评估了如何有效地使用视觉变量,如colorInfo、opacityInfo、rotationInfo和sizeInfo在渲染器中。我们还尝试结合这些视觉变量进行多变量渲染。最后,我们尝试使用智能制图模块进行自动渲染。在下一章中,我们将讨论图表和其他高级可视化技术,为用户提供分析信息。 在地图上渲染可能不是可视化空间数据的唯一方式。为了让数据有所侧重,我们可能需要借助于非空间分析和图表功能,这些功能由dojo和其他流行的库提供,以补充地图的空间可视化功能。在本章中,我们将通过图表库和其他可视化方法(如数据聚类)扩展我们在上一章开始构建的人口统计分析门户网站。本章涉及以下主要主题: ArcGISAPI与dojo的图表绘制非常好地集成在一起。图表功能由dojo的实验模块提供,因此称为dojox,其中的x指的是模块的实验性质。然而,这些模块足够稳定,可以集成到任何生产环境中。以下模块被认为是使用dojo开发图表功能的最基本模块: dojox图表库提供的主题如下: 基本的图表功能可以使用popup模板的mediaInfos属性在要素图层的弹出窗口中显示。我们将使用上一章中使用的县级人口统计要素图层来创建此图表。我们对以下字段感兴趣: 创建mediaInfos对象涉及构建fieldInfos对象,如果需要更改字段名称或在图表中为它们指定别名。mediaInfos对象接受一个theme属性。提到一个dojo图表主题名称或您创建的自定义主题: 我们已经看到了饼图的效果。让我们讨论一些dojox模块提供的更多图表类型以及一些更受欢迎的图表类型的实用性。注意柱状图和柱形图之间的差异,以及散点图和仅标记的图之间的差异。 图表模块有四个重要的方法,将帮助我们创建图表。它们是: 使用addPlot()方法可以定义您的情节。情节接受名称和参数数组: varchart1=newChart2D(chartDomNode);chart1.addPlot("default",plotArguments);让我们看看plotArguments对象包括什么。plotArguments的属性根据我们选择使用的图表类型而变化。如果我们选择使用线条、区域或数据点来定义数据的图表类型,则应将线条、区域或标记等属性设置为布尔值。线条选项确定是否使用线条连接数据点。如果选择区域类型,则将填充数据线下的区域。标记选项将确定是否在数据点处放置标记。 plotArguments可以接受以下属性: 对于诸如堆叠线或堆叠区域的图表类型,我们可以使用张力和阴影等属性来增强图表的可视化效果。张力平滑连接数据点的线条,阴影属性将在线条上添加阴影。shadow属性本身是一个接受名为dx、dy和dw的三个属性的对象,它定义了阴影线的x偏移、y偏移和宽度: chart1.addPlot("default",{type:"StackedLines",lines:true,markers:false,tension:3,shadows:{dx:2,dy:2,dw:2}});在渲染条形图时,使用gap属性表示条形之间的像素数: chart1.addPlot("default",{type:"Bars",gap:3});定义主题使用前面提到的主题列表,我们可以使用setTheme()方法为我们的图表设置主题: chart.setTheme(dojoxTheme);推送数据我们可以使用addSeries()方法将数据推送到图表中: chart.addSeries("PopulationSplit",chartSeriesArray);addSeries()方法接受两个参数。第一个参数提到数据的名称,第二个参数。第二个参数是一个包含实际数据的数组对象。它可以是一维数据,例如[10,20,30,40,50],也可以是二维数据,在这种情况下,可以提到数据的x和y属性: chart.addSeries("Students",[{x:1,y:200},{x:2,y:185}]});如果是饼图,可以省略x分量。 有一些插件可以添加到道场的图表模块中,为图表功能增加价值。这些插件为图表数据提供交互性,大多数插件会显示有关数据项的额外信息,或者强调悬停在其上的数据项。有些插件通过可视化元素(如图例)提供对数据的整体感知。插件完成的一些功能包括: 插件模块,如dojox/charting/widget/Legend,提供了对Legend小部件的支持。dojox/charting/action2d/Tooltip模块支持图表数据的工具提示支持。包括dojox/charting/action2d/Magnify模块将放大悬停在其上的图表数据,从而增强了与图表的交互性。dojox/charting/action2d/MoveSlice模块将图表数据视为一个切片,并移动悬停在其上的图表数据的位置。这与Magnify插件一起,有助于有效地给出用户与图表数据的交互感。dojox/charting/action2d/Highlight模块用不同的高亮颜色(如青色)突出显示悬停在其上的数据。 实施插件也非常容易。以下代码行实现了诸如Highlight、Tooltip和MoveSlice的插件在dojo图表对象上的使用: newHighlight(chart,"default");newTooltip(chart,"default");newMoveSlice(chart,"default");让我们在要素图层的infotemplate属性上的动态div中创建一个完整的图表。 我们也将在此演示中使用县级人口统计特征图层。我们的目标是创建一个饼图,以显示我们单击的任何县的种族分布。我们将调用一个函数来动态创建每个要素的Infowindow内容: 现在让我们尝试在代码中实现这些步骤。我们将编写一个函数来构建图表,并将图表内容作为dom元素返回。我们将使用infotemplate的setContent()方法来设置以下函数返回的dom元素: 弹出容器的第一个选项卡如下截图所示: 弹出窗口的第二个选项卡显示了dojo图表。我们的图表上有一个图例元素。当我们在饼图中悬停在任何数据上时,它会被切片,稍微放大,并突出显示。 D3.js是一个用于基于数据操作文档的JavaScript库。D3代表数据驱动文档,该库提供了强大的可视化组件和基于数据驱动的DOM操作方法。 或者我们可以在我们的脚本标签中使用CDN: 以下是将D3作为dojoConfig的包添加的片段: define(["dojo/_base/declare","d3","dojo/domReady!"],function(declare,d3){//KeepCalmanduseD3withdojo});使用D3创建柱状图让我们使用县级人口统计数据使用D3创建一个柱状图。我们的目标是使用柱状图显示围绕感兴趣县的家庭收入中位数的四个度量。这四个度量是: 以下图片是我们打算构建图表的模拟: 有几个原因我们选择使用D3来演示构建这个图表。D3完全由数据驱动,因此灵活,特别适合数据可视化。许多可视化库都是基于D3构建的,了解D3甚至可以帮助我们构建直观的图表和数据可视化。 d3.select("body")要选择所有具有名为chart的特定样式类的div标签,请使用以下代码片段: d3.select(".chart").selectAll("div")要将svg(可缩放矢量图形)标签或任何其他HTML标签附加到div或body标签,使用append方法。SVG元素用于呈现大多数图形元素: d3.select("body").append("svg")与enter()方法一起使用,表示元素接受输入: d3.select("body").enter().append("svg")D3数据D3由数据驱动,正如其名称所示。我们只需要向D3选择提供数据,就可以渲染一个简单的图表。数据可以简单到一个数组: vardata=[45,87,15,16,23,11];vard3Selection=d3.select(".chart").selectAll("div").data(data).enter().append("div");d3Selection.style("width",function(d){returnd*3+"px";}).text(function(d){returnd;});在上一个代码片段中,我们所做的就是为D3选择的样式对象设置宽度属性。然后我们得到了这个: 每个div的宽度值以像素为单位,取自数据数组中每个元素的值乘以20,柱内的文本值再次取自各个数据的值。在得到这个美丽的图表之前,有一些事情需要做——我们需要为div设置CSS样式。这是我们使用的一个简单的CSS代码片段: .chartdiv{font:10pxsans-serif;background-color:steelblue;text-align:right;padding:3px;margin:1px;color:white;}D3缩放在上一个代码片段中,为了显示一个简单的D3图表,我们使用了一个乘数值20,用于每个数据值获取div宽度的像素值。由于我们的容器div大约有400像素宽,这个乘数值是合适的。但是对于动态数据,我们应该使用什么样的乘数值呢?经验法则是我们应该使用某种缩放机制来缩放像素值,以便我们的最大数据值舒适地适应图表容器div中。D3提供了一种机制来缩放我们的数据并计算缩放因子,我们可以方便地使用它来缩放我们的数据。 D3提供了一个scale.linear()方法来计算缩放因子。此外,我们还需要使用另外两个方法,即domain()和range(),来实际计算缩放因子。domain()方法接受一个包含两个元素的数组。第一个元素应该提到最小的数据值或0(适当的话),第二个元素应该提到数据的最大值。我们可以使用D3函数d3.max来找到数据的最大值: d3.max(data)range函数还接受一个包含两个元素的数组,应列出容器div元素的像素范围: varx=d3.scale.linear().domain([0,d3.max(data)]).range([0,750]);一旦我们找到缩放因子x,我们就可以将其用作数据项值的乘数,以得出像素值: d3.select(".chart").selectAll("div").data(data).enter().append("div").style("width",function(d){returnx(d)+"px";}).text(function(d){returnd;});将SVG集成到D3图表中SVG,虽然在其整体上令人生畏,但在处理数据可视化时提供了几个优势,并支持在HTML中呈现许多原始形状。需要注意的一点是SVG坐标系统从左上角开始,我们在计算元素所需位置时需要记住这一点。 附加SVG元素类似于将div附加到我们的图表类: varsvg=d3.select(".chart").append("svg").attr("width",500).attr("height",500).append("g").attr("transform","translate(20,20)";在前面的片段中,我们实际上可以设置样式和其他属性,例如宽度和高度。transform是一个重要的属性,通过它我们可以移动svg元素的位置(记住SVG坐标系原点在左上角)。 由于我们将构建一个柱状图,因此在计算D3线性缩放时,由range()方法接受的数组中的第一个元素不应是最小值,而应是像素中的最大高度值。数组中的第二个元素是最小像素值: vary=d3.scale.linear().range([700,0]);相反,x缩放因子应基于序数比例(意思是,我们不使用数字来计算条的宽度和间距): varx=d3.scale.ordinal().rangeRoundBands([0,width],.1);从之前讨论的特征统计模块中,我们应该能够得到特征层中特定字段的平均值和标准差。 根据前面的两条信息,我们知道如何计算2.5^(th)百分位数(底部2.5%的收入)和97.5^(th)百分位数(顶部2.5%的收入水平)。我们打算将所选特征的收入中位数与这些值进行比较。计算2.5^(th)和97.5^(th)百分位数的公式如下所示: 根据以前的统计计算,我们知道以下数据: mean=$46193SD=$12564我们需要计算如下的2.5^(th)和97.5^(th)百分位数: 2.5thpercentilevalue=mean–1.96*SD=46193–1.96*(12564)=21567.56而对于97.5^(th): 97.5thpercentile=mean+1.96*SD=46193+1.96*(12564)=70818.44因此,这将是我们图表的数据: vardata=[{"label":"Top2.5%ile","Income":70818},{"label":"Bottom2.5%ile","Income":21568},{"label":"NationalAvg","Income":46193},{"label":"SelectedValue","Income":0}];Income值为SelectedValue标签设置为0。当我们点击feature类中的特征时,此值将被更新。我们还将定义一个margin对象以及用于图表的width和height变量。我们定义的margin对象如下所示: varmargin={top:20,right:20,bottom:30,left:60},width=400-margin.left-margin.right,height=400-margin.top-margin.bottom;在构建图表时,我们将考虑以下步骤: 我们将在一个函数中编写功能,并根据需要调用该函数: //map.jsfiledefine("dojo/topic",..){on(CountyDemogrpahicsLayer,"click",function(evt){topic.publish("app/feature/selected",evt.graphic);});}可以通过主题模块下的subscribe()方法在任何其他文件中访问结果。在前面的片段中,可以通过引用名为app/feature/selected的名称来访问结果: //chart_d3.jsfiletopic.subscribe("app/feature/selected",function(){varval=arguments[0].attributes.MEDHINC_CY;vartitle=arguments[0].attributes.NAME+','+arguments[0].attributes.STATE_NAME;;array.forEach(data,function(item){if(item.label==="SelectedValue"){item.Income=val;}});drawChart(title);console.log(JSON.stringify(data));});以下截图是我们代码的输出的表示。D3图表代表一个具有四个柱的典型柱状图。前三个数据值根据我们的代码是静态的,因为我们可以从特征层数据中计算出顶部和底部的2.5^(th)百分位数以及国家平均值。最后一栏是特征层中所选特征的实际值。在下面的快照中,我们点击了纽约州的拿骚县,数据值略高于10万美元,远高于顶部的2.5^(th)百分位数标记: 在下面的截图中,我们选择了一个收入中位数最低的县。请注意Y轴如何根据数据的最大值重新校准自身。 使用D3进行SVG组件的图表绘制可能会很麻烦,但对这些的基本了解在需要进行高级定制时会大有裨益。 Cedar是由Esri提供的一个基于ArcGISServer数据创建和共享数据可视化的beta版本库。它是建立在D3和Vega图形库之上的。Cedar让我们可以使用简单的模板创建高效的数据可视化和图表。 我们可以使用两种方法加载Cedar。我们可以使用脚本标签,也可以使用AMD模式。后一种方法更受推荐。 通过包含脚本标签加载Cedar及其依赖项。这将使Cedar全局可用于我们的应用程序: 我们需要在先前提到的URL找到的所有文件。将这些文件放在应用程序的/js/cedar文件夹中。 现在我们可以在我们自己的定义函数中加载Cedar模块,就像以下代码片段中所示的那样: define(["cedar","dojo/domReady!"],function(Cedar){varchart=newCedar({...});chart.show({elementId:"#cedarchartdiv",width:900});});要创建一个简单的图表,我们只需要定义两个属性: 对于条形图,映射属性需要两个对象,x和y。让我们尝试为我们的县级人口统计图创建一个总结。在这里,我们试图总结按州分组的所有县的家庭收入中位数的平均值。以下简单的代码可以做到这一切,并显示一个简单的条形图: 这种类型的图表给我们提供了数据的整体图片。让我们动手尝试构建一个散点图,它可以让我们映射多个变量。 我们的目标是将所有州的收入水平沿X轴进行映射,将多样性指数沿Y轴进行映射,并根据州对数据点进行不同的着色。 映射对象应该有一个名为color的额外参数: 创建气泡图表会提供一个额外的处理方式——使用气泡的大小表示第三个变量: varbubble_chart=newCedar({"type":"bubble","dataset":{"data":data,"mappings":{"x":{"field":"MEDHINC_CY","label":"MedianHouseoldIncome"},"y":{"field":"DIVINDX_CY","label":"DiversityIndex"},"size":{"field":"TOTPOP_CY","label":"Population"}}}});bubble_chart.tooltip={"title":"{NAME}","content":"MedianIncome:{MEDHINC_CY} 我们从在Infotemplate中创建一个简单的可定制图表开始,它可以可视化一个变量,到一个可以同时可视化三个变量的图表,从而增强我们对数据的理解,并增加其价值。 查看图像中的TimeInfo属性: 服务目录中TimeInfo信息的快照 让我们讨论TimeInfo对象的组件,这与我们在之前的图像中看到的类似。TimeInfo属性为我们提供以下信息: 您可能需要ArcGIS开发人员帐户才能访问这些数据。 发出HTTPGET请求以生成之前看到的动态图像的URL,其中每个查询参数都用新行分隔: 在实现代码之前,有一些重要的概念我们需要理解。 //definesbrushvarbrush=d3.svg.brush().x(timeScale).extent([startingValue,startingValue]).on("brush",brushed);我们需要了解的另一个重要方面是关于刷子的事件,比如mousedown。当用户移动刷子(在mousedown后的mousemove)时,将通过调用timescale.invert重新计算范围。这将让我们设置刷子的新范围。下面的代码解释了这一方面: topic.subscribe("application/d3slider/timeChanged",function(){console.log("received:",arguments);varstartDate=arguments[0];if(startDate){vartimeExtent=newTimeExtent();timeExtent.startTime=startDate;map.setTimeExtent(timeExtent);}});查找构建D3滑块的完整代码清单: 我们的目标是获取所选要素的所有数据。可以按照以下步骤来实现我们的目标: 以下代码片段解释了如何形成识别参数。在每次地图点击时执行识别任务: time类型的Cedar图表需要在字段映射中包含以下类型的字段:FIRENAME ';tblString+='STATE ';tblString+='LOCATION ';array.forEach(result,function(searchitem){tblString+=' ';});tblString+='';dom.byId("FindTbl").innerHTML=tblString;},function(err){console.log(err);});在下面的截图中,我们可以看到当我们插入搜索文本W时,搜索文本已从两个不同的字段FireName和State中获取:'+searchitem.feature.attributes["FireName"]+' ';tblString+=''+searchitem.feature.attributes["State"]+' ';tblString+='('+searchitem.feature.attributes["Longitude"]+','+searchitem.feature.attributes["Latitude"]+') ThisisTemplatedwidget
现在,让我们看看如何实例化这个小部件。如前所述,我们需要在小部件中调用startup方法来执行此小部件。我们将从另一个JavaScript文件中调用这个方法,该文件将传递一个对dom节点的引用,其中我们的小部件将被放置:Wind Cloudiness Pressure Humidity Sunrise Sunset 无害的HTML模板是我们开发天气小部件所需的全部内容,我们用它来显示我们所在位置的当前天气数据。Geocoords
Diversity:{DIVINDX_CY}"}bubble_chart.show({elementId:"#cedarBubblePlotDiv"});以下截图显示了一个气泡图;气泡的x位置代表县的家庭收入中位数,气泡的y位置代表县的多样性指数,气泡的半径或大小代表县的总人口: