ArcGIS Server提供了丰富的GIS功能,但是对于一个相对简单的项目来说,可能只是使用到了Arcgis Server提供的
地图服务MapServer,这种情况下使用Arcgis Server感觉有些资源浪费,而且Arcgis Server的价格不便宜。这种情况下,
我们可以考虑通过自定义图层的方式来加载Arcgis Server的切片。下面就介绍基于Silverlight Api如何去加载松散切片:
在ArcgisCache缓存目录下可以找到地图瓦片。如下图,有两个文件conf.xml和conf.cdi,这两个文件里存储了地图
瓦片的基本参数信息。conf.cdi中存储了地图的初始外包范围,conf.xml中存储了坐标系、瓦片和瓦片每个级别的基本参
数。不难发现ArcGISTiledMapServiceLayer图层也就是在内部通过对这两个文件的解析,然后实现加载瓦片的,所以自
定义图层的关键就这解析这两个文件。
*第一步:继承TiledMapServiceLayer,重写GetTileUrl方法。打开切片目录,可以发现瓦片的等级是以L开头的,比
较特殊的是行和列,分别是以R和C开头的8位16进制值表示的(不够8位补零),所以在拼路径的时候需要对十进制进行转
换。
public override string GetTileUrl(int level, int row, int col)
{
return string.Format("{0}{1}/L0{2}/R{3}/C{4}.png", this.TiledUrl, _TILEDS, level, AppendChar(row), AppendChar(col));
}
//16进制转换
private string AppendChar(int num)
{
string str16 = Convert.ToString(num, 16);
StringBuilder str = new StringBuilder();
for (int i = str16.Length; i < 8; i++)
str.Append("0");
str.Append(str16);
return str.ToString();
}
*第二步:读取conf.cdi和conf.xml文件内的参数。读取的内容主要是三部分,地图外包(Extent)、坐标系(SpatialReference)、
瓦片(TileInfo)和瓦片每级(Lod)的信息。在使用WebClient读取conf.cdi文件的时候,由于不识别这个文件的后缀cdi,可以将其
改成常见的文本格式,例如:txt、ini等。
//初始化图层外包
private void InitFullExtent()
{
WebClient client = new WebClient();
client.OpenReadCompleted += (s, e) =>
{
try
{
XDocument doc = XDocument.Load(e.Result);
XElement env = doc.Element("EnvelopeN");
this.FullExtent = new Envelope
(
Convert.ToDouble(env.Element("XMin").Value),
Convert.ToDouble(env.Element("YMin").Value),
Convert.ToDouble(env.Element("XMax").Value),
Convert.ToDouble(env.Element("YMax").Value)
);
_CdiIsLoaded = true;
}
catch { }
finally { NotifyLoad(); }
};
client.OpenReadAsync(new Uri(string.Format("{0}{1}", this.TiledUrl, _CON_CDI), UriKind.RelativeOrAbsolute));
}
//初始化瓦片信息
private void InitTiledInfo()
{
WebClient client = new WebClient();
client.OpenReadCompleted += (s, e) =>
{
try
{
XDocument doc = XDocument.Load(e.Result);
XElement tileInfo = doc.Element("CacheInfo").Element("TileCacheInfo");
IEnumerable lodsInfo = tileInfo.Element("LODInfos").Elements("LODInfo");
//初始化坐标系
this.SpatialReference = new SpatialReference(Convert.ToInt32(tileInfo.Element("SpatialReference").Element("WKID").Value));
//初始化瓦片信息
this.TileInfo = new TileInfo()
{
Height = Convert.ToInt32(tileInfo.Element("TileCols").Value),
Width = Convert.ToInt32(tileInfo.Element("TileRows").Value),
Origin = new MapPoint()
{
X = Convert.ToDouble(tileInfo.Element("TileOrigin").Element("X").Value),
Y = Convert.ToDouble(tileInfo.Element("TileOrigin").Element("Y").Value),
SpatialReference = new ESRI.ArcGIS.Client.Geometry.SpatialReference(this.SpatialReference.WKID)
},
Lods = new Lod[lodsInfo.Count()]
};
//初始化瓦片级别信息
foreach (XElement lod in lodsInfo)
{
int index = Convert.ToInt32(lod.Element("LevelID").Value);
this.TileInfo.Lods[index] = new Lod()
{
Resolution = Convert.ToDouble(lod.Element("Resolution").Value),
};
}
_XmlIsLoaded = true;
}
catch { }
finally { NotifyLoad(); }
};
client.OpenReadAsync(new Uri(string.Format("{0}{1}", this.TiledUrl, _CON_XML), UriKind.RelativeOrAbsolute));
}
注意:必须在conf.cdi和conf.xml文件内的配置信息读取完毕之后,才能Add
到Map中。
此处,我是将瓦片拷贝到Silverlight的宿主网站的目录下,在Silverlight端使用WebClient读取。对于紧凑的切片,
在GetTileUrl中每一次请求时需要对bundlx和bundle格式的文件进行解析,那么切片就必须放在xap包内部,考虑到xap不
易过大,所以就没有使用这种方式实现。