ArcGIS客户端API中另一种图层类型的探讨:DynamicTileMapServiceLayer

    ArcGIS客户端API(Javascript/Flex/Silverlight)中,我们最常打交道的是ArcGISDynamicMapServiceLayer和ArcGISTiledMapServiceLayer两个类,基本每个地图中都要用到。它们都可以直接将服务器端发布的地图服务(MapService)作为图层,加载到客户端程序中,分别对应了动态地图服务和缓存地图服务。这两种图层类型各有优缺点。
    ArcGISDynamicMapServiceLayer(动态地图服务)通常用于实时显示经常变化的数据,支持控制单个图层可见性,可动态投影。但缺点是显示效果较差,整个服务出图较慢;ArcGISTiledMapServiceLayer可以直接加载服务器端的缓存地图服务,显示效果好,速度快,但它的缺点正是ArcGISDynamicMapServiceLayer的优点,即不支持动态投影,不能控制图层可见性,服务器端需要提前生成缓存等。
    这里尝试自己来在客户端封装一个类,创建一种新的客户端图层类型。它能够综合以上两个图层的优点,而克服其各自的缺点。大致总结一下我们要达到的目的:

ArcGISDynamicMap

ServiceLayer

ArcGISTiledMap

ServiceLayer

自定义的客户端图层

实时获取最新数据

Y

N

Y

切片方式显示服务

N

Y

Y

需要提前生成缓存

Y

N

客户端缓存切片加快显示速度

N

Y

Y

利用subdomain加速缓存加载

N

Y

Y

支持动态投影

Y

N

Y

动态指定图像输出格式

Y

N

Y

控制子图层可见性

Y

N

Y

利用LayerDefination过滤数据

Y

N

Y

利用TimeExtent显示时态数据

Y

N

Y

其它缓存服务特性

N

Y

Y

其它动态服务特性

Y

N

Y

 
    由于这种图层类型三种客户端API(Javascript/Flex/Silverlight)均可使用,因此在这里我们就不讨论具体的实现代码,只说明一下实现思路。
    首先来解决以缓存服务的方式来显示动态服务的问题。《ArcGIS客户端API中加载大量数据的几种解决办法(以Silverlight API为例)》一文中其实已经提到,主要是继承TiledMapServiceLayer,其中获取切片的GetUrl()方法,返回值是利用REST SDK中的ExportMap拼接的Url。这样,我们就并不需要提前切图,输入动态地图服务,从而达到缓存地图服务的显示效果。
    举一个例子,如果我想让自己的服务可以和Google Maps/Bing Maps/ArcGIS Online的服务相叠加(WKID 102100/3857),那么GetUrl()方法中看其来应该是这样(里面包括了如何根据level,row,col来计算一个切片的四个角点坐标):
   1:  public override string GetTileUrl(int level, int row, int col)
   2:          {
   3:              string baseUrl = @"{0}/export?dpi=96&transparent=true&format=png8&bbox={1}%2C{2}%2C{3}%2C{4}&bboxSR=102100&imageSR=102100&size=256%2C256&f=image";
   4:   
   5:              double cornerCoordinate = 20037508.3427892;
   6:              double originResolution = cornerCoordinate * 2 / 256;
   7:              double resolution = originResolution;
   8:              for (int i = 0; i < level; i++)
   9:              {
  10:                  resolution /= 2;
  11:              }
  12:              double xmin, ymin, xmax, ymax;
  13:              //double resolution = 39135.7584820001;
  14:              xmin = -cornerCoordinate + resolution * 256 * col;
  15:              ymin = cornerCoordinate - resolution * 256 * (row + 1);
  16:              xmax = -cornerCoordinate + resolution * 256 * (col + 1);
  17:              ymax = cornerCoordinate - resolution * 256 * row;
  18:   
  19:              return string.Format(baseUrl, Url, xmin, ymin, xmax, ymax);
  20:          }

    然后再来看下我们自定义图层实现ArcGISDynamicMapServiceLayer功能的可行性。

    关于动态投影。如果想要叠加到WGS84坐标的底图上,只需要将bboxSR和imageSR改成4326即可。这样便支持了动态投影。

    关于“切片”格式ImageFormat。只需修改上面baseUrl中的format参数。这样一来既不需要在服务器端提前切图,也能动态改变“切片”的格式。

    关于服务加载速度。如果对于出图速度不满意,则可以在服务器端发布若干个相同的服务,轮询使用每个服务来出图,可以达到并行加速的目的。

    关于DisableClientCache。默认情况下,与ArcGISTiledMapServiceLayer一样,这些“切片”会缓存在客户端,便于再次浏览。如果服务器端数据有变,那么就无法看到最新的变化情况,这是缓存服务的一个缺点。在自定义的图层类型中,我们可以在exportmap操作的Url最后再加一个时间戳参数,比如_ts=DateTime.Now.Ticks.ToString(),那么就达到动态地图服务每次都能看到最新结果的目的。

    关于控制子图层的可见性。REST SDK的ExportMap操作中有layers参数可以控制。比如layers=show:2,4,7,则只会显示第3、5、8图层内容。

    关于图层内容过滤。ArcGISDynamicMapServiceLayer有LayerDefinitions属性可以用SQL语句来筛选地图服务的输出内容,而REST中的ExportMap方法也提供了layerDefs供我们调用。比如{"0":"POP2000 > 1000000","5":"AREA > 100000"} ,只输出第一个图层中POP2000字段大于1000000的要素,第六个图层中AREA>100000的要素。

    关于ArcGIS Server 10中的TimeExtent。ArcGISDynamicMapServiceLayer有一个TimeExtent属性用来显示一定时间范围内的数据,而ExportMap方法也给我们提供了Time参数来实现这个功能;并且还有layerTimeOptions参数来控制每个图层的时间段(偏移)。

    关于服务的元数据。ArcGISDynamicMapServiceLayer和ArcGISTiledMapServiceLayer中,都有一些关于服务元数据的属性。比如CapabilitiesCopyrightTextDescriptionInitialExtentFullExtent等,这些在REST的MapService资源中都已经暴露了出来,因此我们可以通过发送请求的方式,在自定义图层初始化的时候顺便取回这些数据。

    关于ArcGISTiledMapServiceLayer中的TileInfo。继承TiledMapServiceLayer类的第一个条件就是得知Tiling Scheme。自然TileInfo也是探囊取物。

    细心的朋友已经看出来,其实和ArcGISDynamicMapServiceLayer、ArcGISTiledMapServiceLayer一样,我们这里自定的图层也是对ArcGIS Server REST SDK的一个封装,但在显示方式和显示速度上有所改进。输入的是动态地图服务,有缓存服务的效果,但没有真正做过缓存,所以姑且把它叫做DynamicTileMapServiceLayer。

    一贯风格,要有真相。还是来看一个实际例子感受一下吧:http://newnaw.com/pub/sl/dynamictilemapservicelayer/

    最后还要提一下这种图层的一个硬伤:面图层的重复标注。具体原因请参见:How do I avoid duplicate labels in my cache?解决办法有两种,1、采用Annotation代替Label;2、不要使用Label。

    源程序下载:http://www.arcgis.com/home/item.html?id=5e32a79350b241f38032f9ca0321ccde

12 thoughts on “ArcGIS客户端API中另一种图层类型的探讨:DynamicTileMapServiceLayer”

  1. xq

    写的很好 very good~
    对于服务器端业务数据更新频繁,数据量大的情况非常实用。

    不仅仅是重复标注,这种新型图层在表达点状图层的时候也不是很好用
    有些点恰好位于两个切片相邻的地方,虽不至于丢失点,但是出来的点符号便只有一半了

    1. 菩提老王 Post Author

      这个问题当初考虑过。当时测试了较大的点符号,这种情况没有发生。如果你遇到这种情况,希望能与我联系,谢谢。

  2. Pingback: PortableBasemapServer:多数据源多客户端的底图服务器 - 菩提老王的葡萄架

  3. ljj

    楼主能否具体说明一下,如果想要叠加到WGS84大地坐标系的底图上该如何处理呢?
    this.FullExtent = new ESRI.ArcGIS.Client.Geometry.Envelope(-180.0, -90, 180.0, 90);
    {
    SpatialReference = new ESRI.ArcGIS.Client.Geometry.SpatialReference(4326);
    };
    this.SpatialReference = new ESRI.ArcGIS.Client.Geometry.SpatialReference(4326);
    Origin = new ESRI.ArcGIS.Client.Geometry.MapPoint(-180.0, 90)
    {
    SpatialReference = new ESRI.ArcGIS.Client.Geometry.SpatialReference(4326)
    }

    仅仅改这些貌似不行。

    1. diligentpig

      这有一个能用的,天地图的scheme供参考:
      public override void Initialize()
      {
      this.FullExtent = new ESRI.ArcGIS.Client.Geometry.Envelope(-180,-90,180,90)
      {
      SpatialReference = new SpatialReference(WKID)
      };
      // This layer’s spatial reference
      this.SpatialReference = new SpatialReference(WKID);
      // Set up tile information. Each tile is 256x256px, 19 levels.
      this.TileInfo = new TileInfo()
      {
      Height = 256,
      Width = 256,
      Origin = new MapPoint(-180, 90) { SpatialReference = new ESRI.ArcGIS.Client.Geometry.SpatialReference(WKID) },
      Lods = new Lod[17]
      };
      // Set the resolutions for each level. Each level is half the resolution of the previous one.
      double resolution = 90 / 256d;//最小比例尺有4*2=8(width*height)个256切片,每个切片表示90个经纬度
      for (int i = 0; i < TileInfo.Lods.Length; i++)
      {
      TileInfo.Lods[i] = new Lod() { Resolution = resolution };
      resolution /= 2;
      }
      // Call base initialize to raise the initialization event
      base.Initialize();
      }

    1. diligentpig

      文中是针对wgs 1984 web mercator(wkid 3857)坐标系的进行初始化的,这个坐标系的所有参数是公开的,cornerCoordinate也包括在内,指切图坐标原点(tiling scheme origin)。

Leave a Comment

Your email address will not be published. Required fields are marked *