目的:
1.arcgis server9.2 ADF中把Virtual Earth地图图片数据作为数据源,这样可以直接使用MicroSoft的卫星图片。
准备工作:
1.参考DeveloperKit\SamplesNET\Server\Web_Applications目录下的Common_CustomDataSourceCSharp.zip。
完成后的效果图:

开始:
1.把Virtual Earth地图图片数据作为地图数据源,和(九、一)例子一样也是需要通过自定义数据源实现了,具体的关于地图数据、地图控件、Data Source、Resource、Functionatily之间的关系地图以及地图从数据到显示的整个过程说明可以参考(九、一)例子,这里不做详细的说明。
2.新建名为CustomDataSource的ASP.NET Web应用程序,然后添加一个Default_TileMapData.aspx页面用来具体功能的展示。
3.在CustomDataSource的解决方案中在添加一个名为TiledMapDataSource的类库工程,用来实现自定义DataSource类型和MapResource的功能。同时在CustomDataSource的ASP.NET Web应用程序中添加对这个TiledMapDataSource的类库工程的引用。
4.同(九、一)例子一样自定义数据源的要在ADF中使用需要在ArcGisServer安装目录的DotNet文件内添加配置文件,关于配置文件的说明可以看(九、一)例子,添加名为ESRI.ArcGIS.ADF.Web.DataSources.TiledMap.config的配置文件,内容如下:
5.从上面配置文件看,我们需要实现TiledMapDataSource.GISDataSource、TiledMapDataSource.MapResource这2个类,新建GISDataSource.cs和MapResource.cs两个文件。
6.TiledMapDataSource.GISDataSource类和(九、一)例子一样了这里不详细说明了,同时也可以参考Common_CustomDataSourceCSharp.zip的源代码,这样需要对在TiledMapDataSource.GISDataSource类中使用到的MapInformation.cs类需要进行说明,与上例不同这次是采用Virtual Earth瓦块图片数据,所以MapInformation.cs类的实现较上例有些不同,主要区别是当MapInformation类实例化时,读取配置了Virtual Earth地图数据的一些配置参数对TileCacheInfo属性进行数据设置,主要是在方法parseConfig()中。
Virtual Earth地图数据配置参数文件VirtualEarth_original.xml,具体节点说明可以对照parseConfig()方法,内容具体如下:
MapInformation类具体的代码和说明如下:
namespace TiledMapDataSource2
{3
public class MapInformation : IMapInformation4
{5
public MapInformation(string dataSourceDefinition, string resourceDefinition)6
{7
this.dataSourceConfig = dataSourceDefinition;8
resourceConfig = resourceDefinition;9
//读取对Virtual Earth地图数据的配置定义信心,包括名称、坐标系、图层、各个比例尺等信息10
parseConfig();11
}12

13
private string dataSourceConfig = string.Empty;14
private string resourceConfig = string.Empty;15
private SpatialReference defaultSpatialReference = null;16
private TileCacheInfo tileCacheInfo = null;17
private Envelope defaultExtent, fullExtent;18

19
//IMapInformation成员20
public string DataFrame21
{22
get { return "(default)"; }23
}24
//IMapInformation成员25
public SpatialReference DefaultSpatialReference26
{27
get { return defaultSpatialReference; }28
}29
//IMapInformation成员30
public Envelope DefaultExtent31
{32
get33
{34
return defaultExtent;35
}36
}37
//IMapInformation成员38
public Envelope FullExtent39
{40
get41
{42
return fullExtent;43
}44
}45
//IMapInformation成员46
public ESRI.ArcGIS.ADF.Web.DataSources.TileCacheInfo TileCacheInfo47
{48
get { return tileCacheInfo; }49
set { tileCacheInfo = new TileCacheInfo(value); }50
}51

52
private void parseConfig()53
{54
//读取xml文档55
XmlDocument doc = new XmlDocument();56
doc.Load(dataSourceConfig);57
XmlNode root = doc.DocumentElement;58
//读取TileCacheInfo节点59
XmlNodeList tileCacheInfoNodes = root.SelectNodes("TileCacheInfo");60
if (tileCacheInfoNodes == null || tileCacheInfoNodes.Count < 1)61
{62
throw new Exception("Could not find configuration for resource " + resourceConfig);63
} 64

65
XmlNode tileCacheNode = null;66
System.Collections.Generic.Dictionary<int, string> tileUrls = new Dictionary<int, string>();67
System.Collections.Generic.Dictionary<string, string> layers = new Dictionary<string, string>();68

69
//如果Name节点为resourceConfig70
for (int t = 0; t < tileCacheInfoNodes.Count; ++t)71
{ 72
if (tileCacheInfoNodes[t].Attributes["Name"].InnerText == resourceConfig)73
{74
tileCacheNode = tileCacheInfoNodes[t];75
break;76
}77
}78
//获取第一个节点79
if (tileCacheNode == null)80
{81
tileCacheNode = tileCacheInfoNodes[0];82
}83
//获取SpatialReference节点内容,坐标系统定义字符串84
string srtext = tileCacheNode.Attributes["SpatialReference"].InnerText;85
int srid;86
//设置defaultSpatialReference87
if (Int32.TryParse(srtext, out srid))88
{89
defaultSpatialReference = new SpatialReference(srid);90
}91
else92
{93
defaultSpatialReference = new SpatialReference(srtext);94
}95

96
//获取图片解像度97
int dpi = Convert.ToInt32(tileCacheNode.Attributes["DPI"].InnerText);98
//瓦块图的高99
int height = Convert.ToInt32(tileCacheNode.Attributes["Height"].InnerText);100
//瓦块图的宽101
int width = Convert.ToInt32(tileCacheNode.Attributes["Width"].InnerText);102
//原点坐标103
string[] originCoords = tileCacheNode.Attributes["TileOrigin"].InnerText.Split(new char[] { ',' });104
//最小x坐标105
double xmin = Convert.ToDouble(originCoords[0]);106
//最小y坐标107
double ymin = Convert.ToDouble(originCoords[1]);108
//原点109
Point origin = new Point(xmin, ymin);110

111
//全图范围112
XmlNode extentNode = tileCacheNode.SelectSingleNode("FullExtent");113
xmin = Convert.ToDouble(extentNode.Attributes["XMin"].InnerText);114
ymin = Convert.ToDouble(extentNode.Attributes["YMin"].InnerText);115
double xmax = Convert.ToDouble(extentNode.Attributes["XMax"].InnerText);116
double ymax = Convert.ToDouble(extentNode.Attributes["YMax"].InnerText);117
fullExtent = new Envelope(xmin, ymin, xmax, ymax);118
fullExtent.SpatialReference = defaultSpatialReference;119

120
//默认显示范围121
extentNode = tileCacheNode.SelectSingleNode("DefaultExtent");122
xmin = Convert.ToDouble(extentNode.Attributes["XMin"].InnerText);123
ymin = Convert.ToDouble(extentNode.Attributes["YMin"].InnerText);124
xmax = Convert.ToDouble(extentNode.Attributes["XMax"].InnerText);125
ymax = Convert.ToDouble(extentNode.Attributes["YMax"].InnerText);126
defaultExtent = new Envelope(xmin, ymin, xmax, ymax);127
defaultExtent.SpatialReference = defaultSpatialReference;128

129
//获取图层id和名称130
XmlNode layersNode = tileCacheNode.SelectSingleNode("Layers");131
if (layersNode != null)132
{133
XmlNodeList layerNodes = layersNode.SelectNodes("Layer");134
if (layerNodes != null)135
{136
for (int l = 0; l < layerNodes.Count; ++l)137
{138
string layerId = layerNodes[l].Attributes["LayerID"].InnerText;139
string layerName = layerNodes[l].Attributes["Name"].InnerText;140
layers.Add(layerId, layerName);141
}142
}143
}144
145
//获取各个比例尺的信息146
System.Collections.Generic.List<LodInfo> lodInfos = new List<LodInfo>();147
System.Collections.Generic.Dictionary<int, int> levels = new Dictionary<int, int>();148
XmlNode lodInfoNode = tileCacheNode.SelectSingleNode("LodInfos");149
XmlNodeList lodInfoNodes = lodInfoNode.SelectNodes("LodInfo");150
for (int l = 0; l < lodInfoNodes.Count; ++l)151
{152
int levelid = Convert.ToInt32(lodInfoNodes[l].Attributes["LevelID"].InnerText);153
levels.Add(l, levelid);154
//瓦片图行数量155
int rows = Convert.ToInt32(lodInfoNodes[l].Attributes["Rows"].InnerText);156
//瓦片图列数量157
int columns = Convert.ToInt32(lodInfoNodes[l].Attributes["Columns"].InnerText);158
//瓦片图路径159
string tileUrl = lodInfoNodes[l].Attributes["TileUrl"].InnerText;160
//瓦片图地理宽度161
double tilewidth = Convert.ToDouble(lodInfoNodes[l].Attributes["TileExtentWidth"].InnerText);162
//瓦片图地理高度163
double tileheight = Convert.ToDouble(lodInfoNodes[l].Attributes["TileExtentHeight"].InnerText);164
//比例尺165
double scale = Convert.ToDouble(lodInfoNodes[l].Attributes["Scale"].InnerText);166
//分辨率167
double resolution = Convert.ToDouble(lodInfoNodes[l].Attributes["Resolution"].InnerText);168
LodInfo lodInfo = new LodInfo(levelid, resolution, scale, columns, rows, tilewidth, tileheight);169
lodInfos.Add(lodInfo);170
tileUrls.Add(levelid, tileUrl);171
}172

173
//设置TileCacheInfo174
tileCacheInfo = new TileCacheInfo(width, height,dpi, origin, lodInfos.ToArray());175
tileCacheInfo.TileUrls = tileUrls;176
tileCacheInfo.Layers = layers;177
tileCacheInfo.Levels = levels;178

179
//获取Virtual Earth瓦块图片路径处理类的Assembly和名称180
XmlNode urlGenNode = tileCacheNode.SelectSingleNode("TileUrlGenerator");181
if (urlGenNode != null)182
{183
tileCacheInfo.TileUrlGeneratorAssembly =urlGenNode.Attributes["Assembly"].InnerText;184
tileCacheInfo.TileUrlGeneratorClass =urlGenNode.Attributes["Class"].InnerText;185
}186

187
}188
}189
}190

namespace TiledMapDataSource2
{3
public class TileCacheInfo : ESRI.ArcGIS.ADF.Web.DataSources.TileCacheInfo4
{5
public TileCacheInfo(int width, int height, int dpi, Point origin, LodInfo[] lodInfos): base(width, height, dpi, origin, lodInfos)6
{ }7
public TileCacheInfo(ESRI.ArcGIS.ADF.Web.DataSources.TileCacheInfo tci): base(tci.Width, tci.Height, tci.Dpi, tci.Origin, tci.LodInfos)8
{ }9
internal System.Collections.Generic.Dictionary<int, int> Levels = new Dictionary<int, int>();10
internal System.Collections.Generic.Dictionary<int, string> TileUrls = new Dictionary<int, string>();11
internal System.Collections.Generic.Dictionary<string, string> Layers = new Dictionary<string, string>();12
internal string TileUrlGeneratorAssembly;13
internal string TileUrlGeneratorClass;14
}15
}16

7.MapResource类和(九、一)例子差不多了这里不详细说明了,具体的可以看Common_CustomDataSourceCSharp.zip的源代码,与上例不同的地方是这个Resource实现了TileFunctionality,因为本来的Virtual Earth地图数据源是切成小块的瓦块图,所以需要实现TileFunctionality,关于TileFunctionality的具体代码和说明如下:
namespace TiledMapDataSource2
{3
class TileFunctionality : ITileFunctionality4
{5
ITileFunctionality 成员65

66
IGISFunctionality 成员133

134
private MapFunctionality GetMapFunctionality(string name)135
{136
MapFunctionality mapFunctionality = resource.Functionalities.Find(name) as MapFunctionality;137
return mapFunctionality;138
}139

140
private object getInstanceOfType(string assemblyName, string type)141
{142
object o = null;143
try144
{145
System.Reflection.Assembly assembly = System.Reflection.Assembly.Load(assemblyName);146
o = assembly.CreateInstance(type);147
}148
catch (Exception e)149
{150
string mess = e.Message;151
}152
return o;153
}154
}155
}156

8.在TileFunctionality类中有用到ITileUrlGenerator接口,新建ITileUrlGenerator.cs文件,代码如下:
namespace TiledMapDataSource2
{3
public interface ITileUrlGenerator4
{5
//获取瓦块图路径的的方法6
string GetTileUrl(long column, long row, int level, string defaultUrl, Dictionary<string, bool> layerVisibility);7
}8
}9.在TileFunctionality类中有用到VirtualEarthTileUrlGenerator类,它实现了ITileUrlGenerator接口。VirtualEarthTileUrlGenerator方法是获取Virtual Earth的图片,所以有必要对VirtualEarth的图片算法进行了解,看如下的参考资料:
卫星图和微软普通地图瓦片算法是一样的,区别在于卫星图要是a或者h开头,而微普通地图则是r开头,服务器地址不同下面举例:
http://a1.ortho.tiles.virtualearth.net/tiles/a1322111.jpeg?g=1 这是不带label的微软卫星图
http://h1.ortho.tiles.virtualearth.net/tiles/h1322111.jpeg?g=1 这是带label(如显示省份、地市)的微软卫星图
http://r1.tiles.ditu.live.com/tiles/r1322111.png?g=1 这微软普通地图
看到http://后面,红色的a、h、r分别代表不带label卫星图、带label卫星图、微软普通地图。
10.有了上面的资料后就能知道如何取地图图片了,新建VirtualEarthTileUrlGenerator.cs文件,代码如下:
namespace TiledMapDataSource2
{3
public class VirtualEarthTileUrlGenerator : ITileUrlGenerator4
{5
ITileUrlGenerator Members13

14
//获取瓦块图路径,参数:瓦块列值,瓦块的行值,比例等级,可视图层15
private string GetUrl(int column, int row, int level, Dictionary<string, bool> layerVisibility)16
{17
string mapType = null;18
string mapExtension = null;19
//如果同时包括Road图层和Aerial图层20
if (layerVisibility["r"] && layerVisibility["a"])21
{22
mapType = "h";23
mapExtension = ".jpeg";24
}25
//只包括Road图层26
else if (layerVisibility["r"])27
{28
mapType = "r";29
mapExtension = ".png";30
}31
//只包括Aerial图层32
else if (layerVisibility["a"])33
{34
mapType = "a";35
mapExtension = ".jpeg";36
}37
else38
{39
return null;40
}41

42
string quadKey = TileToQuadKey(column, row, level);43

44
string url = String.Concat(new object[] { "http://", mapType, quadKey[quadKey.Length - 1], ".ortho.tiles.virtualearth.net/tiles/", mapType, quadKey, mapExtension, "?g=", 1 });45
return url;46
}47

48
//Virtual Earth图片名称的算法49
private static string TileToQuadKey(int tx, int ty, int zl)50
{51
string quad = "";52
for (int i = zl; i > 0; i--)53
{54
int mask = 1 << (i - 1);55
int cell = 0;56
if ((tx & mask) != 0)57
{58
cell++;59
}60
if ((ty & mask) != 0)61
{62
cell += 2;63
}64
quad += cell;65
}66
return quad;67
}68
}69
}12.这样自定义数据源完成了,接下在就是测试这个数据源,在CustomDataSource的ASP.NET Web应用程序的Default_TileMapData.aspx页面中添加Map1、MapResourceManager1、Toc13个控件,然后做相应的设置,具体设置可以参考前面的文章。
13.接下来在MapResourceManager1中在Server Resource上面在新增加名为Virtual Earth,在弹出的Map Resource Definition Editor中Type:选择TiledMap就是上面添加到DotNet文件内的ESRI.ArcGIS.ADF.Web.DataSources.TiledMap.config中定义的名字,在Data Source:输入上面的那个xml的地址,如:D:\VirtualEarth_original.xml,然后确定就完成了设置。这里的Data Source为了方便是输入的方式了,最好的做法可以参考Common_CustomDataSourceCSharp.zip样例为它做一个xml文件选择界面,具体的参考CustomDataSource_CSharp\REXMLDataSource_CSharp\Design\DataSourceDefinitionEditorFormREXML.cs,是一个Winform的选择节目,只有在config文件配置一下,就可以在 Map Resource Definition Editor中调用自己定制的编辑器。
14.运行查看效果,这样成功的把Virtual Earth的卫星图片显示在我们的页面中了。

