1. 程式人生 > >自定義圖層載入Arcgis Server鬆散切片

自定義圖層載入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不

易過大,所以就沒有使用這種方式實現。