自定義圖層載入Arcgis Server鬆散切片
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<XElement> 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不
易過大,所以就沒有使用這種方式實現。