FGMap默认支持Web墨卡托投影后的瓦片地图数据,也就是现在我们看到的Google Map、Bing Map、百度地图这样的瓦片数据,只要解析所对应的请求地址路径就可以,这个方法在之前的文章中介绍过。今天我们加载一个“经纬度直投”的地图数据类型--天地图数据,因为投影的方式不一样,所以加载的方式可能会不一样些。“经纬度等间隔直投”和“Web墨卡托”之间的区别大家可以参考一下《天地图-地图投影技术剖析与思考》。
需要加载不同投影下的地图数据,我们需要自己来定义一个投影计算的方式,可以继承IProjection类,IProjection中有两个关键的方法:
fromPixelToLatLng:将像素点转换成对应的经纬度坐标。
fromLatLngToPixel:将经纬度坐标转换成对应的像素。
因为天地图使用的是“经纬度直投”的方式进行投影,所以换算方式比较简单:
1 public function fromPixelToLatLng(pixel:Point, zoom:Number, nowrap:Boolean = false) : LatLng
2 {
3 var x:Number = 0;
4 var y:Number = 0;
5
6 x = pixel.x / (Math.pow(2,zoom) * titeSize) * 360 - 180;
7 y = 90 - pixel.y / (Math.pow(2,zoom-1) * titeSize) * 180;
8
9 var latlng:LatLng = new LatLng(y, x, nowrap);
10 return latlng;
11 }
12
13 public function fromLatLngToPixel(latLng:LatLng, zoom:Number) : Point
14 {
15 var point:Point = new Point(0,0);
16 var x:Number = 0;
17 var y:Number = 0;
18
19 latLng = new LatLng(latLng.lat(),latLng.lng());
20
21 point.x = (latLng.lng() + 180) / 360 * (Math.pow(2,zoom) * titeSize);
22 point.y = (90 - latLng.lat()) / 180 * (Math.pow(2,zoom-1) * titeSize);
23
24 return point;
25 }
大家也可以参考一下这篇文章:http://lt.cjdby.net/thread-1003128-1-1.html
有了这个方法后,我们可以自己定义一个投影,具体代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 package com.fgmap.expand.core
2 {
3 import com.fgmap.expand.*;
4 import com.fgmap.maps.LatLng;
5 import com.fgmap.maps.interfaces.IProjection;
6
7 import flash.geom.Point;
8
9 public class TiandituProjection implements IProjection
10 {
11 private var _wrapper:Object;
12 private var pixelRange:Array;
13 private var pixelsPerLonDegree:Array;
14 private var pixelOrigo:Array;
15 private var pixelsPerLonRadian:Array;
16 public static const MERCATOR_ZOOM_LEVEL_ZERO_RANGE:Number = 256;
17 private var titeSize:int = 256;
18
19 public function TiandituProjection(zoom:int)
20 {
21 var mapSize:Number = MERCATOR_ZOOM_LEVEL_ZERO_RANGE;
22 var i:int = 0;
23 var mapOrigo:Number = 0;
24 pixelsPerLonDegree = [];
25 pixelsPerLonRadian = [];
26 pixelOrigo = [];
27 pixelRange = [];
28 while (i < zoom)
29 {
30 mapOrigo = mapSize / 2;
31 pixelsPerLonDegree.push(mapSize / 360);
32 pixelsPerLonRadian.push(mapSize / (2 * Math.PI));
33 pixelOrigo.push(new Point(mapOrigo, mapOrigo));
34 pixelRange.push(mapSize);
35 mapSize = mapSize * 2;
36 i++;
37 }
38 }
39
40 public function fromPixelToLatLng(pixel:Point, zoom:Number, nowrap:Boolean = false) : LatLng
41 {
42 var x:Number = 0;
43 var y:Number = 0;
44
45 x = pixel.x / (Math.pow(2,zoom) * titeSize) * 360 - 180;
46 y = 90 - pixel.y / (Math.pow(2,zoom-1) * titeSize) * 180;
47
48 var latlng:LatLng = new LatLng(y, x, nowrap);
49 return latlng;
50 }
51
52 public function fromLatLngToPixel(latLng:LatLng, zoom:Number) : Point
53 {
54 var point:Point = new Point(0,0);
55 var x:Number = 0;
56 var y:Number = 0;
57
58 latLng = new LatLng(latLng.lat(),latLng.lng());
59
60 point.x = (latLng.lng() + 180) / 360 * (Math.pow(2,zoom) * titeSize);
61 point.y = (90 - latLng.lat()) / 180 * (Math.pow(2,zoom-1) * titeSize);
62
63 return point;
64 }
65
66 public function get interfaceChain() : Array
67 {
68 return ["IProjection"];
69 }
70
71 public function get wrapper() : Object
72 {
73 return this._wrapper;
74 }
75
76 public function tileCheckRange(param1:Point, param2:Number, param3:Number) : Boolean
77 {
78 var x:Number = 0;
79 var y:Number = 0;
80 x = pixelRange[param2];
81 if (param1.y < 0 || param1.y * param3 >= x)
82 {
83 return false;
84 }
85 if (param1.x < 0 || param1.x * param3 >= x)
86 {
87 y = Math.floor(x / param3);
88 param1.x = param1.x % y;
89 if (param1.x < 0)
90 {
91 param1.x = param1.x + y;
92 }
93 }
94 return true;
95 }
96
97 public function getWrapWidth(param1:Number) : Number
98 {
99 return pixelRange[param1];
100 }
101
102 public function set wrapper(param1:Object) : void
103 {
104 this._wrapper = param1;
105 return;
106 }
107
108 }
109 }
当然,这里会做一些处理,因为天地图的图数据分为“线划地图”和“影像地图”两种,而每一类中,又分为底图和标注,所以我们需要在程序中加以区分,天地图资源的分类可以参见:http://www.tianditu.com/guide/resource.jsp
有了这些信息后,,我们在自定义一个地图类型对数据进行处理:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 package com.fgmap.expand.core
2 {
3 import com.fgmap.maps.Copyright;
4 import com.fgmap.maps.CopyrightCollection;
5 import com.fgmap.maps.LatLng;
6 import com.fgmap.maps.LatLngBounds;
7 import com.fgmap.maps.TileLayerBase;
8 import com.fgmap.maps.interfaces.ICopyrightCollection;
9
10 import flash.display.DisplayObject;
11 import flash.display.Loader;
12 import flash.events.IOErrorEvent;
13 import flash.geom.Point;
14 import flash.net.URLRequest;
15
16 public class TiandituTileLayer extends TileLayerBase
17 {
18 public static const EMAP:String = "EMap";//矢量地图
19 public static const ANNO:String = "Anno";//矢量标注
20 public static const IMGANNO:String = "ImgAnno";//影像标注
21 public static const IMG:String = "Img";//影像地图
22
23 private var mapMinZoom:int = 1; //最小显示等级
24 private var mapMaxZoom:int = 18;//最大显示等级
25 private var _mapType:String = EMAP;
26
27 public function TiandituTileLayer(tileSize:Number,mapMinZoom:Number,mapMaxZoom:Number,alpha:Number)
28 {
29 var copyrightCollection:CopyrightCollection = new CopyrightCollection();
30 //创建一个自己的版权说明
31 copyrightCollection.addCopyright(
32 new Copyright("TiandituData" + MapType,
33 new LatLngBounds(new LatLng(-180, -90),
34 new LatLng(180, 90)), 0,
35 "天地图数据",18));
36
37 super(copyrightCollection, mapMinZoom, mapMaxZoom, alpha); //调用父类的方法
38 }
39
40 public function get MapType():String
41 {
42 return _mapType;
43 }
44
45 public function set MapType(value:String):void
46 {
47 _mapType = value;
48 }
49
50 //覆盖加载地图数据的方法,这个很重要,地图数据从这里读取
51 override public function loadTile(tilePos:Point, zoom:Number):DisplayObject {
52 var testLoader:Loader = new Loader();
53
54 var z:Number = Math.pow(2,zoom - 1) - Math.pow(2,zoom - 2) - 1;
55 var x:Number = tilePos.x;
56 z = Math.pow(2,zoom - 2);
57 var y:Number = tilePos.y;
58
59 var layerName:String = "";
60 var strURL:String = "";
61 switch(_mapType){
62 case EMAP:
63 if (zoom <= 10) {
64 layerName = "A0512_EMap";
65 } else if (zoom == 11 || zoom == 12) {
66 layerName = "B0627_EMap1112";
67 } else if (zoom >= 13 && zoom <= 18) {
68 layerName = "siwei0608";
69 }
70 break;
71 case IMG:
72 if (zoom <= 10) {
73 layerName = "sbsm0210";
74 } else if (zoom == 11) {
75 layerName = "e11";
76 } else if (zoom == 12) {
77 layerName = "e12";
78 } else if (zoom == 13) {
79 layerName = "e13";
80 } else if (zoom == 14) {
81 layerName = "eastdawnall";
82 } else if (zoom >= 15 && zoom <= 18) {
83 layerName = "sbsm1518";
84 }
85 break;
86 case ANNO:
87 if (zoom <= 10) {
88 layerName = "AB0512_Anno";
89 } else {
90 strURL = "http://www.tianditu.com/js/GeoSurfJSAPI/img/blank.gif";
91 }
92 break;
93 case IMGANNO:
94 if (zoom <= 10) {
95 layerName = "A0610_ImgAnno";
96 } else if(zoom >= 11 && zoom <= 14){
97 layerName = "B0530_eImgAnno";
98 } else if(zoom > 14 && zoom < 19){
99 layerName = "siweiAnno68";
100 } else {
101 strURL = "http://www.tianditu.com/js/GeoSurfJSAPI/img/blank.gif";
102 }
103 break;
104 }
105
106 var num:int = Math.random()* 3;
107 if(strURL == ""){
108 strURL = encodeURI("http://tile" + num + ".tianditu.com/DataServer?" +
109 "T=" + layerName +
110 "&X=" + x +
111 "&Y=" + y +
112 "&L=" + zoom +
113 "&d=2009-10-22T20:25:15&cd=9999-12-31T00:00:00");
114 }
115 var urlRequest:URLRequest;
116 urlRequest = new URLRequest(strURL); //没有地图时显示的内容
117
118 testLoader.load(urlRequest);
119 testLoader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
120
121 return testLoader;
122 }
123
124 //出错处理
125 private function ioErrorHandler(event:IOErrorEvent):void {
126 trace("ioErrorHandler: " + event);
127 }
128 }
129 }
最后,我们将“线划地图”和“影像地图”加载到我们的地图中来:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <?xml version="1.0" encoding="utf-8"?>
2 <s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
3 xmlns:s="library://ns.adobe.com/flex/spark"
4 xmlns:mx="library://ns.adobe.com/flex/mx"
5 xmlns:maps="com.fgmap.maps.*"
6 minWidth="955" minHeight="600">
7 <fx:Declarations>
8 <!-- 将非可视元素(例如服务、值对象)放在此处 -->
9 </fx:Declarations>
10 <s:Panel width="800" height="600" title="FGMap加载天地图数据">
11 <maps:Map id="map" width="100%" height="100%" mapevent_mapready="onMapreadyHandler(event)"/>
12 </s:Panel>
13
14 <fx:Script>
15 <![CDATA[
16 import com.fgmap.expand.core.*;
17 import com.fgmap.maps.*;
18 import com.fgmap.maps.MapMouseEvent;
19 import com.fgmap.maps.controls.MapTypeControl;
20 import com.fgmap.maps.controls.NavigationControl;
21 import com.fgmap.maps.controls.OverviewMapControl;
22 import com.fgmap.maps.controls.ScaleControl;
23 import com.fgmap.maps.interfaces.IMapType;
24 import com.fgmap.maps.overlays.*;
25
26 private var marker:Marker;
27
28 private var centreLatlng:LatLng = new LatLng(39.90509127978099,116.39769769279956818);//人民英雄纪念碑。
29
30 private var tiandituAnno:TiandituTileLayer = null;
31 private var TiandituAnno:TileLayerOverlay = null;
32 //地图加载完成后执行的方法
33 protected function onMapreadyHandler(event:MapEvent):void
34 {
35 map.enableContinuousZoom(); //启用连续平滑缩放。
36 map.enableScrollWheelZoom(); //启用使用鼠标滚轮缩放。
37 map.addControl(new MapTypeControl()); //供用户在地图类型之间进行切换的按钮。
38 map.addControl(new NavigationControl());//供用户更改地图的各项导航参数,包括缩放级别、中心位置和空间方位角。
39 map.addControl(new ScaleControl()); //比例控件是用于指示当前地图的分辨率和缩放级别的可视指示器。
40
41 var normalMapType:IMapType = MapType.NORMAL_MAP_TYPE; //定义一个地图类型
42 var tileLayers:Array = new Array();
43
44 //天地图矢量
45
46 var tdtemaplTileLayer:TiandituTileLayer = new TiandituTileLayer(normalMapType.getTileSize(),1,18,1)
47 tdtemaplTileLayer.MapType = TiandituTileLayer.EMAP;
48 tileLayers.push(tdtemaplTileLayer); //地图类型
49 var TdtEmapMapType:IMapType = new MapType(tileLayers,new TiandituProjection(18),"矢量");//创建自己的地图类型
50 map.addMapType(TdtEmapMapType); //增加到地图上
51
52 tiandituAnno = new TiandituTileLayer(normalMapType.getTileSize(),1,10,1)
53 tiandituAnno.MapType = TiandituTileLayer.ANNO;
54 TiandituAnno = new TileLayerOverlay(tiandituAnno,256,new TiandituProjection(18));//创建自己的地图类型
55 //tileLayers.push(tiandituAnno);
56 //var TdtAnnoMapType:IMapType = new MapType(tileLayers,new TiandituProjection(18),"标注");//创建自己的地图类型
57 //map.addMapType(TdtAnnoMapType); //增加到地图上
58
59 //天地图影像
60 var tdtimgTileLayer:TiandituTileLayer = new TiandituTileLayer(normalMapType.getTileSize(),1,18,1)
61 tdtimgTileLayer.MapType = TiandituTileLayer.IMG;
62 tileLayers = new Array();
63 tileLayers.push(tdtimgTileLayer); //地图类型
64 var TdtImgMapType:IMapType = new MapType(tileLayers,new TiandituProjection(18),"影像");//创建自己的地图类型
65 map.addMapType(TdtImgMapType); //增加到地图上
66
67 map.setMapType(TdtEmapMapType); //设置自己的地图可见
68
69 map.addEventListener(MapEvent.MAPTYPE_CHANGED,onMapTypeChanged);
70
71 map.addOverlay(TiandituAnno); //加载标注覆盖层
72
73 //移除原来的地图类型
74 // for(var i:int=0;i < MapType.DEFAULT_MAP_TYPES.length;i++){
75 // map.removeMapType(MapType.DEFAULT_MAP_TYPES[i]);
76 // }
77
78 map.setCenter(centreLatlng,5); //设置地图的中心点。
79 marker = new Marker(centreLatlng); //建立一个标注。
80 //map.addOverlay(marker); //在地图上显示此标注。
81 BindMarker(marker);
82 }
83
84 private function onMapTypeChanged(e:MapEvent):void {
85 map.removeOverlay(TiandituAnno);
86 if(map.getCurrentMapType().getName() == "矢量"){
87 tiandituAnno = new TiandituTileLayer(256,1,10,1)
88 tiandituAnno.MapType = TiandituTileLayer.ANNO;
89 TiandituAnno = new TileLayerOverlay(tiandituAnno,256,new TiandituProjection(18));
90 map.addOverlay(TiandituAnno); //加载标注覆盖层
91 }else if(map.getCurrentMapType().getName() == "影像"){
92 tiandituAnno = new TiandituTileLayer(256,1,18,1)
93 tiandituAnno.MapType = TiandituTileLayer.IMGANNO;
94 TiandituAnno = new TileLayerOverlay(tiandituAnno,256,new TiandituProjection(18));
95 map.addOverlay(TiandituAnno); //加载标注覆盖层
96 }else{
97 map.removeOverlay(TiandituAnno); //移除标注覆盖层
98 }
99 }
100
101 private function BindMarker(marker:Marker):void {
102 var markerOptions:MarkerOptions = new MarkerOptions();
103 markerOptions.draggable = true;//设置标注可以拖动
104
105 //定义一个标注
106 //var marker:Marker = new Marker(map.getCenter(),markerOptions);
107
108 marker.setOptions(markerOptions);
109
110 var infoOptions:InfoWindowOptions = new InfoWindowOptions();
111 infoOptions.title = "我的坐标是";
112 infoOptions.content = marker.getLatLng().toString();
113 //标注拖动时关闭提示框
114 marker.addEventListener(MapMouseEvent.DRAG_START,function(e:Event):void{
115 marker.closeInfoWindow();
116 });
117 //拖动接受时显示提示框
118 marker.addEventListener(MapMouseEvent.DRAG_END,function(e:Event):void{
119 infoOptions.content = marker.getLatLng().toString();
120 marker.openInfoWindow(infoOptions);
121 });
122 //点击标注时显示提示框
123 marker.addEventListener(MapMouseEvent.CLICK, function(e:Event):void {
124 infoOptions.content = marker.getLatLng().toString();
125 marker.openInfoWindow(infoOptions);
126 });
127
128 map.addOverlay(marker);//在地图上显示这个标注
129 marker.openInfoWindow(infoOptions);//打开对话框
130 }
131 ]]>
132 </fx:Script>
133 </s:Application>
下面就是我们看到的运行结果,线划地图:
影像地图:
到此,我们就可以完全的加载上天地图的数据了。
但是,细心的童鞋可能会发现同一个经纬度,在不同的地图下,位置不对,这是因为各地图厂商都对自己的地图数据进行了偏移,是人为加上去的,所以会有偏差,如果真实使用中,这个需要进行偏移纠正的,这是另一个话题了。
这里只给出一个加载的方式,如果正式使用各地图商的数据,请与他们联系!
完整的项目代码从这里下载:https://files.cnblogs.com/liongis/FGMapTianditu.zip