ArcGIS Server 10中的切图/缓存机制深入

  两年前我写过一篇关于ArcGIS地图切图/缓存原理的文章,《ArcGIS Server的切图原理深入》,里面以tiling scheme为主,讲了缓存图片的存储结构以及相关坐标的计算。那时还是ArcGIS 9.3版本,现在ArcGIS 10已经推出快一年多了,地图缓存/切图方面有了很大改进,比如增加了compact缓存格式,增加了import/export map server cache工具,增加了mixed图片模式等。这次就在上篇文章的基础上,以ArcGIS 10为例,看看其中地图切图/缓存制作的工作机制。
  注:本文旨在深入了解ArcGIS目前切图(即制作地图服务的缓存)的机制,以帮助工作中有需要的朋友们提高工作效率,因为切图,尤其是大比例尺,耗时耗资源,实在是一件伤不起的工作。这里已经假设你知道了如何为缓存地图服务配置地图如何检查发布地图服务的地图文档的性能,开始动手前务必进行小范围、全比例尺切图试验等等注意事项,如否,请查阅相关资料。

supertile和bundle

  要深入了解ArcGIS在切图时的工作机制,有两个概念必须明白,就是supertile和bundle。
  假设一个tile(切片)的大小是256*256,在切图时如果按照这个大小直接exportmap,开启了动态标注的地图服务上线图层和面图层就会有很多重复的标注。因为exportmap时,每个tile都会包含一个要素的标注,如果一个要素横跨了多个tile(这在大比例尺下基本是肯定的),那么这些tile上就会出现相同的标注,exportmap时并不知道相邻的tile上已经有了该要素的标注。

image

  为了解决这个问题,ArcGIS在切图时引入了supertile的概念,开启抗锯齿时supertile大小为2048*2048,反之为4096*4096。真正切图时会首先exportmap出一个supertile,然后将它们分割成指定大小的tile。对于每个要素,每个supertile中只包含一次标注,这样保证一个superfile大小的范围内不会出现重复标注。事实上在绘制supertile时,会尽可能多地包含周围的标注,所以在每个supertile的边界周围仍会有重复标注。解决重复标注的根本办法就是使用annotation,而不是label。
  ArcGIS 10中推出了新的compact缓存格式,将原来离散的每个tile图片(exploded格式),保存成连续的二进制文件(.bundle),每个.bundle文件最多可存储128*128个tile。相比以前成千上万的tile文件,这样做有明显的好处:易于缓存迁移,减少占用磁盘空间,减少硬盘i/o等,esri在任何时候都推荐使用compact格式来创建缓存,除非你需要自己读取每个tile(技术上来说这点可以不成立)。ArcGIS 10在切图时,也采用了bundle的概念:对于每一级比例尺,首先从tiling scheme origin开始,将切图范围分成若干个bundle,每个bundle覆盖的范围是128*128个tile大小。比如在1:4096比例尺时,如果tile大小是256*256,DPI为96,那么每个bundle范围的大小是:((4096*2.54/96)/100/1000*256*128)^2=1261.086平方千米。之后,有且仅有(这么做是为了避免多个进程同时读写一个磁盘文件)一个服务实例/一个arcsoc.exe进程(如果是high isolated)来负责此bundle范围的切图,先输出supertile,然后再切成tile。即使选择了exploded缓存格式,ArcGIS 10中也会采用这种机制来切图。

image

  9.3.1及以前版本中,每个服务实例工作的单位是supertile。即一个arcsoc.exe负责生成一个supertile,然后切成tile,再继续生成下一个supertile……相比supertile这种处理单位来说,采用bundle作为处理单位的ArcGIS 10中的服务实例可以将更多精力投入到连续切图中去,而不是频繁切换自己负责的范围。在大比例尺切图中,这种新机制能很大程度上减少切图时间;但在小比例尺切图中,这种新机制有可能反而增加切图时间。因为小比例尺时可能全图范围只有不到一个bundle范围大小,这样只会有一个服务实例来切图,其他实例都处于空闲状态,而9.3.1及以前版本中,所有服务实例都会“蜂拥而上”。

image

网格切图和按featureclass范围切图

  有过大比例尺范围切图经验的朋友肯定会知道,一般切图策略是:小比例尺全图直接切,较小比例尺可能按照某个featureclass范围来切,而大比例尺一般是按照包含网格(grid)的featureclass范围来切。以我国全范围切图为例,小比例尺时全图切没有问题,大比例尺时如果仍然全图范围(包络矩形)切的话,会将周边其他的国家也包含进来,这并不需要的额外工作量在大比例尺时会是一场噩梦。

image

  而之所以不仅要按指定featureclass范围切图,而且featureclass里要包含网格的原因在于,便于细化和跟踪切图进度。切图工具会给你指定的featureclass创建一个新的Cached字段,将已经切好的feature标记为YES,以便在选择Recreate Empty Cache时避免重复切图,从而可以将切图工作分为多次来进行,或者以便在切图失败后排查原因,继续切图工作。
  在指定featureclass范围切图时,是顺序处理该featureclass中的所有feature的。所有的服务实例会首先集体处理一个feature范围,切出该feature范围内所有要求的比例尺级别的结果。此时ArcGIS Server会重启该服务。然后所有服务实例再去切下一个feature范围……所以与每个feature边界相交的supertile可能会被创建两次或多次(多个feature的相交处),这也是为什么在使用featureclass切图前,需要分别对它进行GeneralizeAggregateDissolve的原因。

image

  结合之前bundle的机制我们知道,如果一个feature范围恰好只包含一个bundle,那么就杯具了,因为对该feature切图时,只会有一个服务实例进行工作(一个bundle同时由且仅由一个服务实例处理),其他服务实例全部处于空闲状态。因此,比较理想的情况是,每个feature至少包含比服务实例数更多的bundle时,才能充分利用硬件资源来对其切图。
  这就会引出一个问题,就是如何来确定某个比例尺下一个bundle的大小,从而生成这样的featureclass呢?ArcGIS 10中,给我们提供了一个新的GP工具Map Server Cache Tiling Scheme To Polygons,利用它,我们可以针对某个地图服务,生成每个需要切图比例尺下所有的supertile,进而得到以每个supertile为一个feature的featureclass。以全国地图为例,在1:288,895比例尺(ArcGIS Online Tiling Scheme的第12级)下生成的supertile是这样的:

image

  我们需要的是某个比例尺下bundle的范围,如何根据supertile来确定bundle的大小呢?在N级比例尺时,一个supertile是16*16个tile,第N+1级比例尺时,该supertile会覆盖32*32个tile,第N+2级比例尺时,覆盖64*64个tile,第N+3级比例尺时,这个第N级的supertile会覆盖128*128个tile,眼熟吧,这正是一个bundle的大小。由于supertile和bundle都是从tiling scheme origin往右下角算起的,因此第N级一个supertile的范围正是第N+3级一个bundle的范围。由此如果我们要生成第15级的bundle网格,只需要用Map Server Cache Tiling Scheme To Polygons工具生成第12级supertile的网格即可,如上图。
  以此为例,我们来说明如何有效进行大比例尺切图的问题。假设我们需要对1:36,111(ArcGIS Online Tiling Scheme的第15级)这个比例尺进行切图,我们首先生成该范围的bundle网格,恰好是第12级的supertile网格,如上图。为了简便起见,我们只取其中8个feature来做说明,地图服务的最大实例数是4个(机器是单cpu,4核)。如果每个feature恰好是一个bundle,那么一个feature只能被一个arcsoc.exe处理,而其他3个服务实例均处在空闲状态(占用内存最少的那个arcsoc.exe是用来清空工作目录的,与具体服务无关):

image

  而我们对这个featureclass做一个处理,将4个feature(恰好是一个bundle)合并成一个feature(bundle cluster),这样每个feature就恰好包含了4个bundle,如此我们所开启的4个服务实例就可全速工作了,发挥了机器的最大性能:

image

  ps:Map Server Cache Tiling Scheme To Polygons工具生成的supertile是从tiling scheme origin开始计算的,而不是地图服务的fullextent左上角,因此利用它生成的supertile合并出来的bundle是最合理的,恰好与理想的bundle分界处一致。因此在大比例尺下利用featureclass切图时,应当利用Map Server Cache Tiling Scheme To Polygons来生成网格,而不是fishnet工具。在ArcGIS 10.1中,将会推出新的GP工具,会根据cpu核数来生成合理的包含bundle cluster大小feature的featureclass。
  其他方面还有一些问题,比如切图时最大服务实例数设置多少为好(一般是cpu核数+1),即使所有实例全部工作cpu占用率依然低于90%(有可能是内存不足)等问题,与切图机制无关,就不在此讨论了。
  总之,切图是一个技术活,要求还比较高,需要考虑的问题很多。如果你只把它当一项体力活来看的话,只能说明认识还不够全面,那就不能怪ArcGIS Server不好。毕竟,人家ArcGIS Online全球的19级缓存都7*24小时上线两年了,还有什么理由说产品不好呢?

15 thoughts on “ArcGIS Server 10中的切图/缓存机制深入”

  1. Simo

    您好!
    Excellent! thank you for this very practical blog, well done!
    Just a quick question, is it possible to cache a map with diffirent levels to diffirent area? for example, I have a South Australia map with other staes as backgound data, which provides states and country boundaries,main road networks, water systems and etc. Since I have much more detailed map content in the South Australian extent, So I would like to generate 17 levels of tiles for it, but for the other states, it seems to me nothing but a waste of time to generate 17 levels of tiles for the area that I am not concerned about, so, I only want to generate 7 levels for the rest of the area. Is there a way to achieve this in ArcGIS server 10.x or I am daydreaming 😉 I run into this senario back in ArcGIS 9.3 last year and got stucked. It turned out with 2 caches from two diffirent maps, one was South Australia , the other was Australia … I mashed them up in my application, it worked for me but left the question in my mind. Cheers, Simo

    1. 菩提老王 Post Author

      @Simo
      你好,Simo
      Of course you can, whether in arcgis server 10.x or arcgis server 9.3. All you need to do is creating your tiling scheme first without any cache tiles, then use the Manage Map Server Cache Tiles(http://help.arcgis.com/en/arcgisdesktop/10.0/help/index.html#//00540000000p000000.htm) tool to create tiles in each scales you want, separately. In the bigger scales which you have fine map data in small extents rather than full extent of the whole map, just check the Update by featureclass extent option.
      For instance, creating your tiling scheme in Caching tab of your service. Then open that Update Tiles dialog again, just check the first 7 small levels, with update tiles by featureclass extent uncheck, then click OK, the process will just create tiles for your first 7 scales. After that, open Update Tiles dialog again, this time, you need to only select the rest 10 scales, in which you have more accurate map data, and check update tiles by featureclass extent option, set your featureclass which only contains South Australian boundary extent as input, click OK.
      The whole process will give you the result of whole extent caches at first 7 scales, then only South Australian tiles in 8-17 scales.
      Hope this will help:)

      1. Simo

        你好。
        Thank you very much, I sounds like a good idea, and it should work this way. but I don’t know why it is soooo slow when arcgis server starts cleaning up stuff after the tiles are generated. I have no idea what it was doing there, just unbearable slow, so I cancelled it twice. I will try this a bit later. thank you again for your reply. Cheers, Simo

        1. diligentpig

          Simo,你好。
          When you say “cleaning up stuff”, what exactly did you mean? Do you mean to delete the cache already created? In this case, use the Delete button on Caching tab of service properties, this seems work faster than Manage Map Server Cache tool.

  2. 陈斌

    王老师您好,请问一下,arcgis for silverlight中的Map可否自定义显示区域? 就像ArcEngine中Map.ClipGeometry这个属性一样。

    1. diligentpig

      你好,Map控件继承自UIElement,而后者有一个Clip属性(需要的是屏幕坐标),应该可完成你想要的工作;如果是featurelayer或者arcgisdynamicmapservicelayer,可直接设置其layerdefinition属性即可。

      1. CB

        谢谢王老师的答复,我尝试过Clip属性,是windows.meida.geometry类型的,好像只能设置一些基本的几何图形,如椭圆、矩形等,请问能设置多边形吗?

        1. diligentpig

          跟ESRI.ArcGIS.Client.Geometry类似,只要是System.Windows.Media.Geometry都可以。http://msdn.microsoft.com/zh-cn/library/system.windows.media.pathgeometry(v=vs.95).aspx 用pathgeometry就可以构造多边形了。

          多边形可直接在blend中画好,拷贝xaml即可。

  3. 颠倒女人

    王老师,看过您这篇文章后,学到了很多,但是还是有一些不太明白,不知道具体实施起来该如何操作,我现在碰到的问题是切图切到80%多的时候出现错误:Failed to manage tiles for the extent (-136.850648, 0.478979, -127.180519, 10.149109) at level 11
    Failed to manage tiles for the extent (89.028958, -4.158419, 93.980064, 0.792687) at level 12
    我能单独对这两个级别在原来的基础上进行更新么。该怎么做呢?我的思路是这样的:我首先把了L11,L12的boundle导出成shp文件,然后用这个shp来更新,这样可行么?这个时候的切图比例尺是不是只需要选择该boundle对应的比例尺就可以了??如果您方便的话能QQ请教么?763445517

    1. 菩提老王 Post Author

      你说的这种思路完全可行。只需要在切图时选择update mode为recreate empty cache(参考http://resources.arcgis.com/en/help/main/10.1/0154/0154000004mn000000.htm),然后输入你导出的shp文件作为指定的更新范围就可以了。
      另外有现成的工具可根据出错的范围来生成对应feature class用以更新失败的缓存:http://resources.arcgis.com/zh-cn/gallery/file//geoprocessing/details?entryID=5E1F2FE3-1422-2418-88F7-285291B8031D
      当然最根本的问题还是找到你切图失败的原因,比如缩放到以上出错范围和比例尺,看看那里的配图或数据源是不是有什么问题;此外打最新版本的补丁,有时候也能解决类似的问题。

  4. zuochen

    王老师 用你帖子的方法 解决了工作中切片的大问题 感谢+膜拜!
    有一个切片的问题是,有某一个固定的地图范围,不管怎么选取涵盖它的范围进行补片,都不出结果。是什么问题呢?是影像本身有问题吗。 切图模式选的“receate empty tiles” 谢谢您

Leave a Reply to 菩提老王 Cancel Reply

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