前文已经明确数据用天地图,这样的选择一来是免费,二来各种来源的数据都大同小异,天地图用的2000坐标系是常见的经纬度,方便专题数据叠加。
闲话少说,看一下arcgis api for flex是如何吃定天地图的吧。首先要明确的是我们需要用到四个天地图瓦片服务,分别是经纬度地图底图、经纬度地图中文注记、经纬度影像底图、经纬度影像中文注记,详情可以参阅http://www.tianditu.com/guide/index.html。各种服务形式上都差不多,我们来写一个天地图图层类。
首先新建一个类,这个类必须是com.esri.ags.layers.TiledMapServiceLayer的扩展。
public class TDTTiledMapLayer extends TiledMapServiceLayer { }
然后,定义两个变量,_tileInfo、_baseUrl和layerId,_tileInfo是arcgis api瓦片图层的基本参数,_baseUrl是url的基本构成,_baseUrl只是定义了一些必要参数,完整的url还应包括基本地址和瓦片动态参数,再定义一个set属性,用于传入基本地址,瓦片动态参数后面再说,layerId是图层名,这个是天地图服务一个讨厌的参数,同样也要给它一个set属性把这个参数加到地址上。
private var _tileInfo:TileInfo = new TileInfo(); private var _baseURL:String = "?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&STYLE=default&TILEMATRIXSET=c&FORMAT=tiles"; private var _layerId:String = "vec"; public function set baseUrl(value:String):void { _baseURL = value + _baseURL; } public function set layerId(value:String):void { _baseURL = _baseURL + "&LAYER=" + value; }
然后就是定义_tileInfo了,这是我们这个类最为重要的一步,写一个函数buildTileInfo(),具体内容就不解释了,大致是定义瓦片规格,坐标参考系,以及地图级别。
private function buildTileInfo():void { _tileInfo.height = 256; _tileInfo.width = 256; _tileInfo.origin = new MapPoint(-180, 90); _tileInfo.spatialReference = new SpatialReference(4490); _tileInfo.lods = [ new LOD(1 , 0.703125, 2.958293554545656E8), new LOD(2 , 0.351563, 1.479146777272828E8), new LOD(3 , 0.175781, 7.39573388636414E7), new LOD(4 , 0.0878906, 3.69786694318207E7), new LOD(5 , 0.0439453, 1.848933471591035E7), new LOD(6 , 0.0219727, 9244667.357955175), new LOD(7 , 0.0109863, 4622333.678977588), new LOD(8 , 0.00549316, 2311166.839488794), new LOD(9 , 0.00274658, 1155583.419744397), new LOD(10, 0.00137329, 577791.7098721985), new LOD(11, 0.000686646, 288895.85493609926), new LOD(12, 0.000343323, 144447.92746804963), new LOD(13, 0.000171661, 72223.96373402482), new LOD(14, 8.58307e-005, 36111.98186701241), new LOD(15, 4.29153e-005, 18055.990933506204), new LOD(16, 2.14577e-005, 9027.995466753102), new LOD(17, 1.07289e-005, 4513.997733376551), new LOD(18, 5.36445e-006, 2256.998866688275) ]; }
最后,需要实现一下getTileURL方法,这个方法是把每一张瓦片的动态参数和_baseUrl合并形成每一张瓦片的参数。
override protected function getTileURL(level:Number, row:Number, col:Number):URLRequest { var url:String = _baseURL + "&TILEMATRIX=" + level + "&TILEROW=" + row + "&TILECOL=" + col; return new URLRequest(url); }
然后,这个类要求我们override几个get方法,照单全收。唯一要注意的是initialExtent,这个可以依据当前地图需要的范围来设定,比如我现在就设成了安徽的。当然也可以随便设,调用时map的extent会覆盖它。
override public function get fullExtent():Extent { return new Extent(-180, -90, 180, 90, new SpatialReference(4326)); } override public function get initialExtent():Extent { return new Extent(114.76,27.59,120.26, 35.64,new SpatialReference(4326)); } override public function get spatialReference():SpatialReference { return new SpatialReference(4490); } override public function get tileInfo():com.esri.ags.layers.supportClasses.TileInfo { return _tileInfo; }
最后在构造函数里写几笔,主要是要把buildTileInfo()加上。
public function TDTTiledMapLayer() { super(); buildTileInfo(); setLoaded(true); }
好了。类构造完毕,下面我们去调用它。回到我们的index.html,给map加上图层。
<esri:Map id="map" left="0" right="0" top="0" bottom="0" logoVisible="false" scaleBarVisible="false"> <tool:TDTTiledMapLayer id="vec_c" baseUrl="http://t0.tianditu.com/vec_c/wmts" visible="true"/> <tool:TDTTiledMapLayer id="cva_c" baseUrl="http://t0.tianditu.com/cva_c/wmts" visible="true"/> <tool:TDTTiledMapLayer id="img_c" baseUrl="http://t0.tianditu.com/img_c/wmts" visible="false"/> <tool:TDTTiledMapLayer id="cia_c" baseUrl="http://t0.tianditu.com/cia_c/wmts" visible="false"/> </esri:Map>
影像底图和注记visible置为false是为了地图切换的需要,初始默认的是线划地图。下面来看效果。
再利用一点篇幅,把地图类型切换加上吧。两个按钮是toggleButton,其实有很多办法去做这个按钮,但是我们先简单实现一下。加上一个Bool型的Bindable属性IsVector,用它来标识当前地图类型以及控制按钮的状态。
[Bindable] private var IsVector:Boolean = true;
然后,我们在mxml里做一些改动,主要是把ToggleButton的选中状态以及图层可见性绑定过来。
<esri:Map id="map" left="0" right="0" top="0" bottom="0" logoVisible="false" scaleBarVisible="false"> <tool:TDTTiledMapLayer id="vec_c" baseUrl="http://t0.tianditu.com/vec_c/wmts" layerId="vec" visible="{IsVector}"/> <tool:TDTTiledMapLayer id="cva_c" baseUrl="http://t0.tianditu.com/cva_c/wmts" layerId="cva" visible="{IsVector}"/> <tool:TDTTiledMapLayer id="img_c" baseUrl="http://t0.tianditu.com/img_c/wmts" layerId="img" visible="{!IsVector}"/> <tool:TDTTiledMapLayer id="cia_c" baseUrl="http://t0.tianditu.com/cia_c/wmts" layerId="cia" visible="{!IsVector}"/> </esri:Map> <s:HGroup right="10" top="10" height="22" gap="0" verticalAlign="middle"> <s:ToggleButton width="60" label="地图" cornerRadius="0" selected="{IsVector}" click="mapType_clickHandler(event)"/> <s:ToggleButton width="60" label="影像" cornerRadius="0" selected="{!IsVector}" click="mapType_clickHandler(event)"/> </s:HGroup>
下面就把ToggleButton的Click事件绑定上,mxml里我已经写好了,下面看as代码,就一句。
private function mapType_clickHandler(event:MouseEvent):void { IsVector = !IsVector; }
这样就好了,两个按钮就和地图的类型绑定在了一起。这里使用flex开发中非常重要的绑定机制,这一机制可以让代码变得非常优雅,但这不是重点,更重要的是,它让逻辑变得简单,也更安全。安全这一点后面遇到类似情况我会提出来。