转自:http://blog.csdn.net/melodyf/article/details/5895034
问题:项目需要,弄弄Flex的开发
解决方案:
ArcGIS RIA开发实践【Flex篇】
I、 ArcGIS Flex API 基础
一 Flex的历史和现状
Flex的前身是Flash,Flash是极为流行的互联网矢量动画解决方案,目前据统计97%的浏览器都安装有Flash Player插件用以播放swf动画,其中未安装的3%还有很多是因为某些场合的安全限制导致的,可以说Flash是极为普及的RIA平台。
但是由于Flash是一个动画制作软件,其中有很多比如时间轴、影片剪辑等程序员不容易理解的概念,因此Macromedia公司推出了Flex。Flex抛弃了许多动画设计术语,转而使用程序员喜欢的方式开发RIA应用,并且Flex能编译生成可以在Flash Player中运行的swf文件,这无疑吸引了大量程序员,并且实现了和Flash平台的无缝拼接,从而利用Flash平台多年积累的大量素材、美工和设计者。
Adobe公司在2005年收购了Macromedia公司,并在第一时间将Flex/Flash冠以Adobe的商标推向市场,可见其对RIA市场和Flex/Flash的重视。
目前,可以说Adobe Flex/Flash是最流行且最成熟的RIA平台。
二 ArcGIS Flex API概述
ArcGIS Flex API是我自己使用的ArcGIS API for Flex的简称,在本文中将一直使用这个名称。
ArcGIS Flex API是ArcGIS在RIA领域的第一个产品,因此选择了最为成熟的Flex/Flash平台。使用ArcGIS Flex API可以开发运行于浏览器中的Web应用或者运行于桌面的AIR应用,它基于ArcGIS Server的REST接口,所有的功能都可以在REST SDK中找到影子。
使用ArcGIS Flex API开发的Flex应用可以非常便捷地使用地图功能和REST接口提供的GIS查询及分析功能;同时,ArcGIS Flex API专注于GIS功能的实现而不重复地创造组件,因此可以让你的业务和GIS方便地结合起来。
具体关于ArcGIS Flex API可以实现哪些功能,我们在下面的章节中将进行详细的讲解。
三 一些基本概念
首先,让我们先来了解一些ArcGIS Flex API中的基本概念。事实上你会发现这里的概念和桌面或ADF开发都有相通之处,不过为了强调一些重点,顺便介绍些ArcGIS Flex API中的特殊之处,这里还是先对这些内容做点介绍。
1 MapServiceLayer
MapServiceLayer对应的是ArcGIS发布的地图服务,它们是一对一的关系,每个地图服务在ArcGIS Flex API中都会以一个图层的形式出现。不同种类的地图服务将会对应不同的MapServiceLayer,ArcGIS Flex API中主要有以下几种MapServiceLayer:
ArcGISDynamicMapServiceLayer | ArcGIS Server发布的动态地图服务 |
ArcGISTiledMapServiceLayer | ArcGIS Server发布的切片地图服务 |
ArcGISImageServiceLayer | ArcGIS Image Server发布的影像服务 |
ArcIMSMapServiceLayer | ArcIMS发布的地图服务 |
2 GraphicLayer与Graphic
GraphicLayer并不对应到服务器端的某个地图服务,它完全是客户端的图层。因此,GraphicLayer在客户端数据表达方面有非常重要的作用,它可以根据各种情况动态地在客户端显示一些符号化的几何对象——Graphic。
ArcGIS Flex API中的Graphic是一个继承于UIComponent的类,因此它的表现和Canvas、Button等普通的Flex组件一样,在地图中Graphic就是一个可视化的、可响应鼠标事件的客户端要素。
Graphic有3个很重要的属性:geometry、symbol和attributes。其中geometry属性定义的是一个几何对象,它是Graphic的基础,因为Graphic要表达的就是这个几何对象;symbol属性则定义了这个几何对象通过什么符号表现出来;attributes则可以存放一系列与空间信息无关的属性数据。从上面可以发现,Graphic可以被理解成是一个在客户端符号化的要素。关于Graphic的geometry和symbol属性所对应的Geometry类和Symbol类,接下来在这里做一点更详细的说明。
3 Geometry
Geometry是GIS中很基本的概念,ArcGIS Flex API中定义了如下几何对象:
MapPoint | 点 |
Multipoint | 多点 |
Polyline | 多段线 |
Polygon | 多边形 |
Extent | 边界范围 |
我们可以发现ArcGIS Flex API主要定义了点、线、面这些简单对象,事实上这和REST SDK中的几何对象是一一对应的,当ArcGIS Flex API和服务器交互的时候,这些几何对象都会被转化成字符串形式的JSON格式发送到服务器。
4 Symbol
Symbol定义了Geometry使用什么符号表现出来。在ArcGIS Flex API中,一般使用MarkerSymbol定义点的符号、LineSymbol定义线的符号、FillSymbol定义填充的符号,另外还有TextSymbol、InfoSymbol等其它符号帮助进行其它信息的辅助表达。下面列出的是ArcGIS Flex API中提供的常用的Symbol:
SimpleMarkerSymbol | 简单点符号 |
SimpleLineSymbol | 简单线符号 |
SimpleFillSymbol | 简单填充符号 |
CartographicLineSymbol | 制图线符号,可定义端点、折点样式 |
PictureMarkerSymbol | 图片[1]点符号 |
PictureFillSymbol | 图片填充符号 |
TextSymbol | 文字符号 |
InfoSymbol | 信息提示符号 |
CompositeSymbol | 复合符号[2] |
下面是上面这些符号的预览:
四 地图功能组件
在这小节中,我们主要介绍ArcGIS Flex API中对于一般地图功能的实现和使用。在这里让我们暂时先抛开“GIS”,去熟悉一下“地图”,毕竟地图是GIS可视化和功能交互的基石。
1 Map组件
Map是ArcGIS Flex API中唯一的可视化组件,它可以直接被放置到Flex应用中进行布局、展现。
Map可以被看作是一个地图, ArcGIS Flex API中所有的操作和功能都会在Map中进行,因此Map是一个特别重要的对象。如果在应用中有别的业务组件或模块需要和地图交互(比如把业务数据标到图上),那么只要它们可以获得Map对象的引用就可以了(比如一种做法就是在它们的构造函数中设置),所有和GIS相关的功能都可以通过Map来实现。
Map中包含各种Layer,这在GIS中非常容易理解——地图中需要加入若干个图层。在ArcGIS Flex API中,Map中的Layer有两种,一种是对应某个地图服务的MapServiceLayer、一种是用以绘制自定义要素的GraphicLayer,这里我们先看一下如何在Map中加载某个地图服务(MapServiceLayer)及其效果。
<esri:Map id="map">
<esri:ArcGISTiledMapServiceLayer
url="http://localhost:8399/arcgis/rest/services/China/map/MapServer"/>
</esri:Map>
图 6 Map和MapServiceLayer
默认的Map中还有几个其它的元素,包括控制地图缩放的zoomSlider、地图比例尺scaleBar以及ESRI的logo。如果你不需要这些元素,都可以通过Map中对应的属性对它们进行隐藏,下面我们看一下代码和隐藏后的效果:
<esri:Map id="map" zoomSliderVisible="false" scaleBarVisible="false" logoVisible="false">
<esri:ArcGISTiledMapServiceLayer
url="http://localhost:8399/arcgis/rest/services/China/map/MapServer"/>
</esri:Map>
图 7 隐藏不需要的元素后的Map
需要顺便提一下的是,ESRI的logo能被隐藏掉的前提条件是你使用的是本地的ArcGIS Server服务。另外,zoomSlider和scaleBar除了可以被隐藏掉,还可以通过定制样式来改变外观,或许你仅仅是讨厌默认的外观但却需要这个功能。
以下的CSS代码将Map的zoomSlider移到了右边,并且对它按钮的样式、滑块位置等一系列外观做了定制,接着我们就可以看到新的zoomSlider的样子了:
<mx:Style>
.mapStyle{ navigation-style-name: navigationStyle;}
.navigationStyle{
top: 6;
right: 6;
navigation-minus-button-style-name: navigationMinusButton;
navigation-plus-button-style-name: navigationPlusButton;
navigation-slider-style-name: navigationSlider;
}
.navigationMinusButton {
disabledSkin: Embed(source="assets/zoomout.png");
downSkin: Embed(source="assets/zoomout.png");
overSkin: Embed(source="assets/zoomout.png");
upSkin: Embed(source="assets/zoomout.png");
}
.navigationPlusButton{
disabledSkin: Embed(source="assets/zoomin.png");
downSkin: Embed(source="assets/zoomin.png");
overSkin: Embed(source="assets/zoomin.png");
upSkin: Embed(source="assets/zoomin.png");
}
.navigationSlider{
dataTipPlacement: "left";
tickColor: #FFFFFF;
tickLength: 10;
tickOffset: 13;
tickThickness: 3;
}
</mx:Style>
<esri:Map id="map" styleName="mapStyle"></esri:Map>
图 8 改变zoomSlider样式后的Map
另外,Map还有一些默认隐藏的外观设置,比如地图四周的导航按钮、地图中心的十字符号,包括拉框缩放的方框样式等都可以进行设置,下面是修改了一些样式的Map:
图 9 更改一些隐藏外观样式后的Map
从上面我们可以看到,Map有很灵活的可配置性,它的各种元素的外观也很容易定制;当然,如果实在有特殊的需要,Map也可以通过隐藏所有元素来提供给你一个非常干净的地图组件,你则可以在此基础上加上自己的内容。
在有了一个令自己满意的Map的基础上,你就可以使用ArcGIS Flex API中很多激动人心的功能了——同时你会慢慢发现,所有的功能都是紧紧围绕着这个Map的。
2 Navigation工具
当准备好一个满意的Map的时候,我们已经可以在地图中看到我们在ArcGIS Server中发布的服务了。在这个时候,我们可能马上想到的是如何去操作地图,比如缩放、漫游、前进、后退等等地图导航功能。在这个时候你或许已经发现,当前地图默认的操作是漫游(Pan);同时,你滚动鼠标滚轮的时候地图会进行缩放;如果按住shift键并在地图上拖动时会出现一个矩形框,松开鼠标后地图会缩放到框中的范围。似乎Map已经可以进行地图导航了。
然而,当你想要切换地图到拉框缩小状态或者别的什么操作,或许就感到有点无所适从了。这时,我们友好的Navigation工具就正式登场了。
Navigation提供了对Map进行导航的诸多功能,通过它可以实现漫游、拉框放大、拉框缩小等类似ADF中Tool的功能和前一视图、后一视图、全图等类似ADF中Command的功能。但是,它并不是一个工具条,而是提供了一些功能,你可以自定义工具条和一些按钮然后绑定到Navigation的功能上。
首先让我们看一下Navigation中漫游、拉框放大、拉框缩小等功能和工具条按钮的绑定。这些类似Tool的功能有个特点就是同时只有一个按钮需要被激活,它指示了当前Map的操作状态,因此我们可以借助Flex中的ToggleButtonBar作为这些功能的宿主。下面的代码定义了一个ToggleButtonBar并绑定了Navigation的上面几个功能:
<mx:Script>
<![CDATA[
private function itemClickHandler(event:ItemClickEvent):void
{
switch (event.index)
{
case 0:{ nav.activate(Navigation.PAN , true);break; }
case 1: {nav.activate(Navigation.ZOOM_IN , true); break; }
case 2: {nav.activate(Navigation.ZOOM_OUT , true); break; }
}
}
]]>
</mx:Script>
<esri:Navigation id="nav" map="{map}"/>
<esri:Map id="map">
<esri:ArcGISTiledMapServiceLayer url="http://server.arcgisonline.com/ArcGIS/rest/services/ESRI_Imagery_World_2D/MapServer"/>
</esri:Map>
<mx:HBox horizontalAlign="center" width="100%">
<mx:ToggleButtonBar id="tbb" itemClick="itemClickHandler(event)">
<mx:dataProvider>
<mx:Array>
<mx:String>漫游</mx:String>
<mx:String>拉框放大</mx:String>
<mx:String>拉框缩小</mx:String>
</mx:Array>
</mx:dataProvider>
</mx:ToggleButtonBar>
</mx:HBox>
当我们点击ToggleButtonBar中某个按钮时,itemClickHandler这个监听被触发,于是Navigation就会根据当前ToggleButtonBar所选中的按钮来设置Map的操作状态——调用Navigation的activate方法。而Navigation之所以可以和Map联动,是因为Navigation有个map属性会存放Map的引用。
下面是添加了ToggleButtonBar的效果:
图 10 绑定了漫游、拉框放大、拉框缩小等功能的ToggleButtonBar
下面再让我们看一下Navigation中前一视图、后一视图、全图等功能和工具条按钮的绑定。这些类似Command的按钮每个都会实现单一的功能,不过,需要注意的是,前一视图、后一视图的按钮并不是一直可用,比如如果用户从来没有点击过前一视图按钮,那么后一视图按钮肯定是不可用的对吧?这些细节Navigation也已经为你考虑到了,下面我们看一下如何定义这些按钮并绑定到Navigation的功能上:
<mx:Button label="前一视图"
click="nav.zoomToPrevExtent()"
enabled="{!nav.isFirstExtent}"/>
<mx:Button label="后一视图"
click="nav.zoomToNextExtent()"
enabled="{!nav.isLastExtent}"/>
<mx:Button label="全图"
click="nav.zoomToFullExtent()"/>
从这里可以看到,Navigation不仅可以改变Map的操作状态,还有直接操作Map的方法,通过对这些方法的调用,Navigation就可以是Map根据你的需要切换视图。下面是添加了这几个功能按钮的效果:
图 11 绑定了前一视图、后一视图、全图等功能的按钮
到现在为止,我们已经知道怎么样使用Map来展现服务器端的地图服务,然后使用Navigation去导航地图,所以对地图的浏览已经基本可以说无所不能了。下面,或许你已经开始更多地考虑用户怎么和地图进行更多的交互了,比如客户端绘制。
3 Draw工具
Draw是ArcGIS Flex API另外一个和Navigation性质类似的工具。Draw工具提供了在客户端绘制各种几何对象的功能,类似Navigation的使用那样,我们用一个ToggleButtonBar去绑定Draw工具的功能:
<mx:Script>
<![CDATA[
private function itemClickHandler(event:ItemClickEvent):void
{
switch (event.index)
{
case 0:{draw.activate(Draw.MAPPOINT);break;}
case 1:{draw.activate(Draw.MULTIPOINT);break;}
case 2:{draw.activate(Draw.LINE);break;}
case 3:{draw.activate(Draw.POLYLINE);break;}
case 4:{draw.activate(Draw.FREEHAND_POLYLINE);break;}
case 5:{draw.activate(Draw.POLYGON);break;}
case 6:{draw.activate(Draw.FREEHAND_POLYGON);break;}
case 7:{draw.activate(Draw.EXTENT);break;}
}
}
]]>
</mx:Script>
<esri:Draw id="draw" map="{map}" graphicsLayer="{drawGraphicsLayer}" markerSymbol="{sms}" lineSymbol="{sls}" fillSymbol="{sfs}" />
<esri:Map id="map">
<esri:ArcGISTiledMapServiceLayer url="http://server.arcgisonline.com/ArcGIS/rest/services/ESRI_Imagery_World_2D/MapServer" />
<esri:GraphicsLayer id="drawGraphicsLayer"/>
</esri:Map>
<mx:HBox width="100%" horizontalAlign="center">
<mx:ToggleButtonBar id="tbb" itemClick="itemClickHandler(event)">
<mx:dataProvider>
<mx:Array>
<mx:String>点</mx:String>
<mx:String>多点</mx:String>
<mx:String>直线</mx:String>
<mx:String>多线</mx:String>
<mx:String>手绘多线</mx:String>
<mx:String>多边形</mx:String>
<mx:String>手绘多边形</mx:String>
<mx:String>矩形</mx:String>
</mx:Array>
</mx:dataProvider>
</mx:ToggleButtonBar>
</mx:HBox>
<esri:SimpleMarkerSymbol id="sms" style="{SimpleMarkerSymbol.STYLE_DIAMOND}" color="0xFFFF00" size="12"/>
<esri:SimpleLineSymbol id="sls" style="{SimpleLineSymbol.STYLE_DASHDOT}" color="0x00FF00" width="2"/>
<esri:SimpleFillSymbol id="sfs" style="{SimpleFillSymbol.STYLE_DIAGONAL_CROSS}">
<esri:outline>
<esri:SimpleLineSymbol color="0xFF0000" width="2"/>
</esri:outline>
</esri:SimpleFillSymbol>
同样,当我们点击ToggleButtonBar中某个按钮时,Draw工具会根据当前ToggleButtonBar所选中的按钮来激活某个绘制功能。可选的几何对象类型包括点、多点、直线、多线、手绘多线、多边形、手绘多边形、矩形。同时,对这些绘制对象的样式我们也可以进行设置,总的来说,我们可以通过ArcGIS Flex API绘制各种不同类型、样式可设置的几何对象。
下面是使用Draw工具绘制一些几何对象的效果:
图 12 Draw工具可以绘制的几何对象
当我们可以在客户端绘制各种几何对象以后,加上ArcGIS Server后台提供的诸多功能,很快我们就可以实现很多业务逻辑了。
4 InfoWindow窗口
InfoWindow窗口主要提供在Map上弹出信息提示的功能,比如当我点击一个要素的时候,可能同时需要查询这个要素的属性值并在地图上显示出来,InfoWindow窗口主要就是实现这样的功能。
当定义一个Map的时候,Map会生成一个自带的InfoWindow并放置在自己的infoWindow属性中,因此InfoWindow窗口并不需要你去定义。你只需要知道InfoWindow应该弹出的位置(地图上的某个坐标点),那么在这个地方会弹出一个提示窗口,并且不会随着地图移动、缩放而移动位置或消失。
下面我们在地图上弹出一个InfoWindow,并在其中放上一个图片:
var image:Image = new Image();
image.source = "http://www.esrichina-bj.cn/images/in_flashRig.jpg";
map.infoWindow.content = image;
map.infoWindow.show(new MapPoint(116, 40));
InfoWindow使用的重点在于其content属性,它是InfoWindow窗口中的信息内容,它对应一个DisplayObject的对象,言下之意任何Flex中的可视组件都可以放置于其中。在这里我在其中放置了一张图片,当然,你可以使用任何按钮、文本、图表、视频等元素及其组合。
下面是在北京弹出一个InfoWindow的效果:
图 13 弹出的InfoWindow窗口
从上面的内容,我们知道了如何使用一个Map来获得和显示ArcGIS Server发布的服务,然后使用Navigation工具来进行导航,用Draw工具在客户端绘制几何对象,用InfoWindow来显示提示信息。对于地图操作的浏览、交互我们都已经涉及到了。下面,我们将会在地图的基础上使用更多的ArcGIS Server的GIS功能。
五 常用功能
通过前面的内容,我们已经知道ArcGIS Flex API怎么实现并使用一个“地图”。当然,对于GIS应用来说,仅仅实现地图功能是不够的,我们需要融入各自独特的业务逻辑和空间分析功能。那么,接着我们就来看一下ArcGIS Flex API中一些GIS的常用功能。
1 QueryTask
QueryTask是一个进行空间和属性查询的功能类,它可以在某个地图服务的某个子图层内进行查询,顺便需要提一下的是,QueryTask进行查询的地图服务并不必须加载到Map中进行显示。
QueryTask的执行需要两个先决条件:一个是需要查询的图层URL、一个是进行查询的过滤条件,下面让我们通过代码看一下这两个条件应该怎样设置:
<mx:Script>
<![CDATA[
private function doQuery():void
{
var query:Query = new Query();
query.geometry = map.extent;
query.spatialRelationship = Query.SPATIAL_REL_WITHIN;
query.where = "Name like '%"+keyword.text+"%'";
query.outFields = ["Name", "Address"];
query.returnGeometry = true;
queryTask.execute(query);
}
private function onResult(event:QueryEvent):void
{
var featureSet:FeatureSet = event.featureSet;
resultLayer.clear();
for each (var g:Graphic in featureSet.features)
{
resultLayer.add(g);
}
}
]]>
</mx:Script>
<esri:QueryTask id="queryTask"
url="http://localhost:8399/arcgis/rest/services/Beijing/beijing/MapServer/0"
executeComplete="onResult(event)" />
QueryTask有个url属性指向需要查询的子图层的REST URL(索引从顶部图层为0开始);当需要进行查询的时候我们需要调用QueryTask的execute方法,execute方法的第一个参数就是对查询进行设置的Query对象,在这个Query对象的属性中我们可以设置过滤条件和返回内容。
在Query对象的属性中,geometry和spatialRelationship属性用于定义空间查询的条件、where属性用于定义属性查询的条件。在上面的代码中,我们设置的查询条件是:在当前地图范围内,所有“Name”字段中包含关键字字符串的对象。
当execute方法被调用,服务器收到请求并进行查询后,服务器会将结果发给ArcGIS Flex API,因此QueryTask对象的executeComplete监听器将会被触发,接下来我们可以在监听的onResult方法中对结果进行处理。
从onResult方法的QueryEvent参数中我们可以获得查询返回的所有结果集featureSet,featureSet的features属性中存放了所有查询结果的Graphic对象。实际上ArcGIS Server查询到结果并输出响应给ArcGIS Flex API的时候,ArcGIS Flex API就自动将这些结果构造成了一个FeatureSet对象,在FeatureSet对象中不仅包含了和查询结果相对应的Graphic对象,还包含比如几何对象类型、字段别名列表等相关信息。
图 14 QueryTask的使用
2 FindTask
FindTask是在某个地图服务中进行属性查询的功能类。FindTask与QueryTask的使用很类似,当然,QueryTask在execute的时候需要给一个Query对象作为参数,FindTask则是给一个FindParameters对象作为参数。另外,FindTask的url属性需要指向所查询的地图服务的REST URL,而不像QueryTask需要指定子图层的URL。
下面我们看一段FindTask使用的代码:
<mx:Script>
<![CDATA[
private function doFind():void
{
var params:FindParameters = new FindParameters();
params.contains = true;
params.layerIds = [0,1,2];
params.returnGeometry = true;
params.searchFields = ["Name", "Address"];
params.searchText = keyword.text;
findTask.execute(params);
}
private function onResult(event:FindEvent):void
{
var results:Array = event.findResults;
var resultCount:int = results.length;
resultLayer.clear();
for (var i:Number=0; i<resultCount; i++)
{
var g:Graphic = results[i].feature;
resultLayer.add(g);
}
}
]]>
</mx:Script>
<esri:FindTask id="findTask"
url="http://localhost:8399/arcgis/rest/services/Beijing/beijing/MapServer"
executeComplete="onResult(event)" />
FindParameters对象有一些很重要的属性设置了FindTask执行的过滤条件,contains属性设置是否进行模糊查询、layerIds属性设置了需要查询的子图层、searchFields属性设置了需要查询的字段、searchText属性设置了需要查询的字符串。通过以上的设置,我们就可以从一个地图服务的若干个图层的若干个字段中找到我们需要查询的属性信息了。
同样,在查询完成以后,FindTask的executeComplete监听器被触发,onResult方法被调用,从它的FindEvent事件中可以获得所有查询到的结果,这个结果是一个Array类型的集合,这个集合的每个对象都有一个feature属性,它就是该结果对应Graphic对象,我们可以直接把这些Graphic对象标注到地图上去:
图 15 FindTask的使用
3 IdentifyTask
IdentifyTask是一个在地图服务中识别要素的功能类。当用户在客户端使用Draw工具绘制了一个几何对象以后,这个几何对象就可以作为IdentifyTask的参数发送到服务器进行识别,满足条件的要素将会被输出到ArcGIS Flex API中,同样,这些要素都可以作为Graphic被添加到地图上。
下面我们看一段实现IdentifyTask功能的代码:
<mx:Script>
<![CDATA[
private function onDrawEnd(event:DrawEvent):void
{
var identifyParams:IdentifyParameters = new IdentifyParameters();
identifyParams.returnGeometry = true;
identifyParams.width = map.width;
identifyParams.height = map.height;
identifyParams.mapExtent = map.extent;
identifyParams.geometry = event.graphic.geometry;
identifyParams.tolerance = 3;
identifyParams.layerOption = IdentifyParameters.LAYER_OPTION_VISIBLE;
identifyTask.execute(identifyParams);
}
private function onIdentifyComplete(event:IdentifyEvent):void
{
var results:Array = event.identifyResults;
for each (var result:IdentifyResult in results)
{
resultLayer.add(result.feature);
}
}
]]>
</mx:Script>
<esri:IdentifyTask id="identifyTask"
url="http://localhost:8399/arcgis/rest/services/China/map/MapServer"
identifyComplete="onIdentifyComplete(event)" />
与QueryTask和FindTask相仿,IdentifyTask在调用execute方法的时候需要提供的参数是一个IdentifyParameters对象。这个对象最重要的属性是geometry、layerOption和tolerance,geometry属性设置的是用于要素识别的几何对象、layerOption属性设置的要素识别的选项(最顶层还是所有可见图层等)、tolerance属性设置了识别的误差范围(基于屏幕像素)。通过它们的组合,IdentifyTask就可以把满足条件的要素查询出来,并返回给ArcGIS Flex API进行处理。
对于identifyComplete监听器的处理,因为和QueryTask和FindTask的executeComplete监听器类似,这里就不赘述了。下面是分别用点、线、面进行要素识别的结果:
图 16 IdentifyTask的使用
4 GeometryService
每个ArcGIS Server都可以发布一个几何服务(Geometry Service)用以处理几何对象,比如投影转换、简化对象、缓冲、量测、判断空间关系、计算标注点等功能。ArcGIS Flex API中对应地定义了一个GeometryService功能类来使用几何服务提供的功能。
由于GeometryService提供的功能比较多,因此它实现了很多监听器供不同的功能使用,比如投影转换有projectComplete监听、判断空间关系有relationComplete监听等等。在使用的时候,分别针对不同的功能,调用不同的project方法、relation方法等(与上面的监听对应)。当然,GeometryService的功能并不仅限于上面提到的两个方法,下表给出了Geometry的主要功能:
project | 投影转换 |
simplify | 简化几何对象 |
buffer | 缓冲 |
areasAndLengths | 量测多边形的面积与周长 |
lengths | 量测多段线的长度 |
relation | 判断空间关系 |
labelPoints | 计算标注点 |
由于GeometryService不同功能的使用有类似性,我们这里就挑一个功能进行了解,如下的代码调用了Geometry的relation方法,实现了判断几何对象空间关系的功能:
<mx:Script>
<![CDATA[
private var graphics1:Array;
private var graphics2:Array;
private function onQueryCity(event:QueryEvent):void
{
graphics1 = new Array();
var featureSet:FeatureSet = event.featureSet;
var count:int = featureSet.features.length;
for (var i:int=0; i<count; i++)
{
var g:Graphic = featureSet.features[i];
graphics1.push(g);
}
}
private function onQueryProvice(event:QueryEvent):void
{
var featureSet:FeatureSet = event.featureSet;
var g:Graphic = featureSet.features[0];
graphics2 = [g];
geometryService.relation(graphics1, graphics2,
GeometryService.SPATIAL_REL_INTERSECTION);
}
private function onRelationComplete(event:GeometryServiceEvent):void
{
var relations:Array = event.relations;
resultLayer.clear();
for (var i:int=0; i<relations.length; i++)
{
var g:Graphic = relations[i].graphic1;
resultLayer.add(g);
}
}
]]>
</mx:Script>
<esri:GeometryService id="geometryService"
url="http://localhost:8399/arcgis/rest/services/Geometry/GeometryServer"
relationComplete="onRelationComplete(event)" />
以上的GeometryService调用了relation方法,判断了两个Graphic对象集合之间,取出满足“INTERSECTION”条件的所有对象。下图是实际运行的结果,实现的功能是“得到江苏省内所有地级市”。
图 17 GeometryService的使用
5 Geoprocessor
GP服务(Geoprocessing Service)可以使用户能够使用高级的模型功能,ArcGIS Flex API中的Geoprocessor功能类对应的就是GP服务的使用。
Geoprocessor功能的调用有两种方式:execute方法和submitJob方法,其中execute对应同步调用(synchronous)、submitJob对应异步调用(asynchronous)[3],当你知道一个GP服务是同步还是异步以后,你就知道应该使用execute还是submitJob方法来使用这个GP服务了。
图 18 发布GP服务时选择同步或异步调用
关于怎么准备数据、建立模型、发布GP服务,这里就不详细讲述了,下面我们先通过一个“漂流瓶之旅”的例子来看一下Geoprocessor怎样调用ArcGIS Server发布的GP服务。这个例子调用的是ArcGIS Online上的一个GP服务,服务的地址是http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/Specialty/ESRI_Currents_World/GPServer/MessageInABottle,你可以在浏览器中打开这个地址,查看关于这个服务的详细内容,在这里我们可以看到,这个服务是同步的,需要输入两个条件参数:Input_Point(GPFeatureRecordSetLayer类型)和Days(GPDouble类型),输出的结果是Output(GPFeatureRecordSetLayer类型)。
下面就我们看一下调用这个GP服务的代码:
<mx:Script>
<![CDATA[
private function startGeoProcess(event:MapMouseEvent):void
{
this.cursorManager.setBusyCursor();
var mapPoint:Geometry = event.mapPoint;
var startGraphic:Graphic = new Graphic(mapPoint, sms);
resultLayer.add(startGraphic);
var featureSet:FeatureSet = new FeatureSet();
featureSet.features = [ { geometry:mapPoint } ];
var parms:Object = new Object();
parms.Input_Point = featureSet;
parms.Days = Number(numberOfDays.text);
geoprocessTask.execute(parms);
}
private function onExecuteComplete(event:GeoprocessorEvent):void
{
for each (var g:Graphic in event.executeResult.parameterValues[0].value.features)
{
g.symbol = sls;
resultLayer.add(g);
}
this.cursorManager.removeBusyCursor();
}
]]>
</mx:Script>
<esri:Geoprocessor id="geoprocessTask"
url="http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/Specialty/ESRI_Currents_World/GPServer/MessageInABottle"
executeComplete="onExecuteComplete(event)" />
这个GP服务可以计算海洋中洋流的活动,假设我们在地图上点击了一下,表示在这个地方投了一个漂流瓶,调用这个GP服务可以计算若干天内这个漂流瓶的漂流轨迹。
在调用这个GP服务之前,首先我们需要获得两个参数:Input_Point和Days。Input_Point是一个FeatureSet对象,“[ { geometry:mapPoint } ]”代表的是Feature对象的集合,当然,在这里只存放了一个Feature对象,这个Feature对象的几何对象是鼠标点击处的mapPoint;Days则很简单,直接获得文本框中的文字后转成数值即可。当已经获得这两个参数,并把这两个参数放到一个Object对象中做为Geoprocessor执行参数使用后,服务器上的GP服务就可以开始处理这个计算了。
当GP服务计算完成并返回结果时,onExecuteComplete监听将会被触发,我们可以把计算结果绘制到一个GraphicLayer上。
下面是这个Geoprocessor执行的效果:
图 19 Geoprocessor的使用
6 RouteTask
RouteTask对应的是路径分析功能,它需要ArcGIS Server 9.3.1的支持,因此我们这里使用Sample中使用的在线网络分析服务:http://tasks.arcgisonline.com/ArcGIS/rest/services/NetworkAnalysis/ESRI_Route_NA/NAServer/Route。
从这个服务的REST目录我们可以看到,网络分析服务的主要操作是Solve Route,在ArcGIS Flex API中对应调用的就是RouteTask的solve方法,下面让我们从代码看一下RouteTask的使用:
<mx:Script>
<![CDATA[
[Bindable]private var stops:FeatureSet = new FeatureSet([]);
[Bindable]private var barriers:FeatureSet = new FeatureSet([]);
[Bindable] private var lastRoute:Graphic;
private function mapClickHandler(event:MapMouseEvent):void
{
if (selectedBtn == addStopsBtn)
{
var stop:Graphic = new Graphic(event.mapPoint, stopSymbol);
inputsLayer.add(stop);
stops.features.push(stop);
}
else
{
var barrier:Graphic = new Graphic(event.mapPoint, barrierSymbol);
inputsLayer.add(barrier);
barriers.features.push(barrier);
}
if (stops.features.length > 1)
{
routeTask.solve(routeParams);
}
}
private function solveCompleteHandler(event:RouteEvent):void
{
var routeResult:RouteResult = event.routeSolveResult.routeResults[0];
lastRoute = routeResult.route;
}
]]>
</mx:Script>
<esri:RouteTask id="routeTask"
concurrency="last" requestTimeout="30" showBusyCursor="true"
solveComplete="solveCompleteHandler(event)" url="http://tasks.arcgisonline.com/ArcGIS/rest/services/NetworkAnalysis/ESRI_Route_NA/NAServer/Route"/>
<esri:RouteParameters id="routeParams" stops="{stops}" barriers="{barriers}"/>
<esri:Map mapClick="mapClickHandler(event)">
<esri:GraphicsLayer graphicProvider="{lastRoute}" symbol="{routeSymbol}"/>
<esri:GraphicsLayer id="inputsLayer"/>
</esri:Map>
在这个例子中,每次在地图上点击会添加一个停靠点或路障点,每次点击后都会调用RouteTask进行计算,计算的条件就是前面在地图上点击添加的停靠点及路障点;当然,由于设置了concurrency属性,因此只有最后一次调用可以返回结果。在这里顺便说一下concurrency属性,其实前面的那些功能类也都有这个属性,它是BaseTask基类的一个属性,它指示的是该功能是否允许多次调用,你可以选择多次调用、单次调用或者取消前面的调用仅执行最后一次调用。
下面让我们看一下路径分析的结果:
图 20 RouteTask的使用
7 Locator
Locator对应使用的是ArcGIS Server发布的地理编码服务(Geocode Service),它可以从地址找到坐标或从坐标找到地址。比如下面的URL对应的就是一个Geocode Service的REST接口:http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/Locators/ESRI_Geocode_NA/GeocodeServer,从浏览器打开这个链接我们可以看到关于这个服务相关信息。
从服务的描述中可以看到,Geocode Service包含两个操作:Find address candidates和Reverse geocode,前者从地址找到坐标,后者则可以从坐标找到地址,它们在ArcGIS Flex API中分别对应了Locator功能类的addressToLocations方法和locationToAddress方法。
下面我们使用Locator的locationToAddress方法从地图的点击点找到相应的地址信息:
<mx:Script>
<![CDATA[
private function onMapClick(event:MapMouseEvent):void
{
locator.locationToAddress(event.mapPoint, 100);
}
private function onLocationToAddressComplete(event:LocatorEvent):void
{
var candidate:AddressCandidate = event.addressCandidate;
if (candidate != null && candidate.address)
{
var g:Graphic = new Graphic(candidate.location, sms, candidate.address);
graphicLayer.clear();
graphicLayer.add(g);
var text:TextArea = new TextArea();
text.htmlText = "<b>地址:</b>/n"
+ candidate.address.Address.toString() + "/n"
+ candidate.address.City.toString() + "," + candidate.address.State.toString();
map.infoWindow.content = text;
map.infoWindow.show(g.geometry as MapPoint);
}
}
]]>
</mx:Script>
<esri:Locator
id="locator"
url="http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/Locators/ESRI_Geocode_NA/GeocodeServer"
concurrency="last"
locationToAddressComplete="onLocationToAddressComplete(event)"/>
Locator的方法调用很简单,经过前面这么多功能的使用,我们基本上也是熟稔于胸了。从上面我们可以看到,调用locationToAddress方法需要两个参数,一个是坐标点、另一个是距离(单位:米),当Geocode Service处理完并发回结果时,我们就可以把这个结果标到地图上了。
图 21 Locator的使用
六 ArcGIS Server中配置安全策略文件
Flash Player对访问外部资源有比较严格的限制,因此如果你需要访问跨域的ArcGIS Server服务(比如本地非本机、不同域等),一定要注意Flash的安全策略问题。当你在使用ArcGIS Flex API的时候出现了安全沙箱错误(注意,这个错误在调试状态下不会出现,因为IDE已经忽略了安全策略);或者运行的时候出不来地图,但是在浏览器中查看REST服务目录是正常的,那么这个时候就需要注意下你是不是进行了跨域访问。
配置服务器允许被Flash进行跨域访问一般需要在远程的Web服务器根目录下放置一个安全策略文件。当我们开发一个ArcGIS Flex API应用,同时它需要访问跨域的ArcGIS Server的时候,一定要在ArcGIS Server的REST Web根目录下放置如下名为“crossdomain.xml”的安全策略文件:
<?xml version="1.0"?>
<!DOCTYPE cross-domain-policy SYSTEM "http://www.adobe.com/xml/dtds/cross-domain-policy.dtd">
<cross-domain-policy>
<site-control permitted-cross-domain-policies="all"/>
<allow-access-from domain="*"/>
</cross-domain-policy>
以上的安全策略是允许所有的连接,这个安全策略文件需要被放到特定的位置,比如ArcGIS Server for .Net应该是IIS的根目录(如:C:/Inetpub/wwwroot);ArcGIS Server for Java应该是web_output目录(如:C:/Program Files/ArcGIS/java/web_output)。
[1] 支持JPG、GIF、PNG、SWF、SVG格式
[2] 上面若干种符号的组合
[3] 同步调用时用户必须等待调用结束后方可继续操作,异步则不需要