『公告』 预祝您龙年大吉,万事如意, 过节期间, 大家如需数据服务,请拨打400 或直接添加客服微信,再祝大家龙年,心想事成。
关注我们 新浪 腾讯

利用ArcGIS Engine编写切图工具的一些探讨

利用ArcGIS Engine编写切图工具的一些探讨
如何使用AE来开发切图工具。最初的想法是直接调用GP服务,利用CreateMapServerCache 、ManageMapServerCacheTiles 和Geoprocessor 这样三个类来做。但是这个思路有个巨大的弊端就是必须先发布地图服务。于是接下来又马上转换思路,想能否通过瓦片选址算法以及AE的一些细粒度类来实现这个功能。在经过了三个晚上的算法编写和功能编写后,整个工具基本成型,其中包括了对算法的优化以及一些具体问题的解决。这里跟大家大致分享下。

        1.前言

       这周利用晚上在家时间研究了下如何使用AE来开发切图工具。最初的想法是直接调用GP服务,利用CreateMapServerCache ManageMapServerCacheTiles Geoprocessor 这样三个类来做。但是这个思路有个巨大的弊端就是必须先发布地图服务。于是接下来又马上转换思路,想能否通过瓦片选址算法以及AE的一些细粒度类来实现这个功能。在经过了三个晚上的算法编写和功能编写后,整个工具基本成型,其中包括了对算法的优化以及一些具体问题的解决。这里跟大家大致分享下。

       2.切图的基本原理和实现

       如果对瓦片和瓦片寻址的相关算法不熟悉的朋友,请参考我写的从底层探究WebGIS的原理系列:

http://www.cnblogs.com/naaoveGIS/category/600559.html

      这里我直接给出所涉及到的几个公式。

       2.1Resolution转换公式

       resolution=scale*inch2centimeter/dpi。其中scale是地图比例尺,inch2centimeter为英寸转厘米的参数,dpi1英寸所包含的像素。

       2.2行列号获取公式

       假设,地图切图的原点是(originX,oringinY),地图的瓦片大小是tileSize,地图屏幕上1像素代表的实际距离是resolution。计算坐标点(x,y)所在的瓦片的行列号的公式是:

col  = floor((originX- x)/( tileSize*resolution))

row = floor((oringinY - y)/( tileSize*resolution))

       2.3AGS瓦片的特点

       本工具的目标是切出与AGS瓦片相同格式的瓦片。AGS的瓦片具有以下特点(在http://www.cnblogs.com/naaoveGIS/p/3903270.html我做了详细的讲解):

       (a).L开头的代表了LevelR开头的代表了rowC开头的代表了Col

       (b). L后的数字是两位字符串,R后的是八位字符串,C后的也是八位字符串。

       (c).英文后的数字均是16进制数,然后不足位数的用0补充。

       2.3基于以上公式流程

       我们首先获得用户输入的切图级别数组levelScaleArr,瓦片大小(imgWidth,imgHeight),切图原点(originX,originY)还有像素值DPI。同时我们还要通过接口获得此时地图的范围(dXMin,dYMin,dXMax,dYMax)

       流程的盒模型如下所示:


       2.4实现

       实现上,主要使用了AE做了这样几个功能:

       (a).使用IMapControl类获得mxd的四角坐标。

       (b).使用IActiveViewExportPNGClassEnvelopeClass实现将地图局部导出功能。

       其他均按照上述流程图实现。

       3.功能优化

       3.1导出图片种类的优化

       在AE中可以导出多种格式的图片。利用ExportJPEGClass()ExportBMPClass()ExportEMFClass()ExportGIFClass()等即可实现。

       3.2图片透明的优化

       通过上面的类直接导出的图片其背景色默认为了白色。而AGS切图中,背景色是透明的,所以这里还要做一个图片透明度优化过程。C#中转成Bitmap后,利用该类自带的MakeTransparent即可实现。

       4.算法的优化

       4.1 缩小切图范围

       在流程中,我们默认的切图是从切图原点开始的,这样会切成很多很多的无用图。我们可以直接从离地图DXminDYmax最近处开始切图即可。

startXByLevel = (int)Math.Abs((Math.Floor((DXmin-originX) / dImageWidth)));

startYByLevel = (int)Math.Abs((Math.Floor((originY-DYMax) / dImageHeight)));

startXByLevelstartYByLevel即为X轴和Y轴的切图初始点。

       4.2不切无效图

       我们经常会切出整张图都是透明的空白图。但是在AGS的切图中,是看不到这样的无效图的。我们可以在切图时先判断此范围内是否有要素存在,有的话就切,没有的话,continue掉。这样也可以减少切图的数目。

       5.算法的进一步优化——支持经纬度地图切图

       目前上面的所有过程,均只对做了投影转换的mxd有效,但是如果我们的mxd中的坐标系无投影转换只有一个地理坐标系呢,也就是当地图未经纬度坐标时,此时该如何实现切图?

       其实思路也很简单,如果我们真正理解以上resolution的所代表实际意义,那么解决这个问题的思路就应该有了。

       当地图为经纬度时,我们切图的比例尺设置应该改为切图的分辨率设置。这样我们就直接得到了每个级别的resolution,然后用resolution来切图即可。不用再做以上的将比例尺转换为resolution的步骤。

       6.注意

       在levelScaleArr中,里面的比例尺数字是随着index增加而增加的,但是比例尺数字越大,其对应的Level是越小的。所以我们在遍历Level层时,应该是一个递减的遍历,这样生成的L文件夹的编号才是正确的。

       7.效果图

       以下是效果图:


       8.不足

       (a).目前无法切出紧凑型(Compact)瓦片。解决思路,用上面的方法导出图后,需要把图变成二进制然后按照bundle的格式重新生成,并且还要生成索引文件bundleX

       (b).所用的AE毕竟是封装的很好的组件库了。用GDAL的话,由于封装层次低一些,效率应该会更好一些。

      京ICP备2025132830号-1 京公网安备 号