现在随着各种技术的发展,用户手中的数据体量越来越大,但很多用户仍然希望像以前对待小数据那样简单的使用它们,最基本的需求就是尽快看到,简称为快显。这一需求看似简单,但却几乎是在向整个GIS技术体系提出挑战,很多业内人士都致力解决这一问题,或许已经有了初步的成果,我们也做了一定的研究,尝试分析一下问题。以下研究都是基于形如地类图斑或建筑底面这类数据开展的,对于其他如LBS或自然地物等数据类型并不一定适用。
探索
首先定位一下问题,基本的需求大致是这样:面对一份数百万甚至上亿条记录的矢量数据,采用传统的GIS数据格式存储,如何能尽快让其他人在浏览器上基本流畅的查看。还有一些常见的附加需求:访问者可以在浏览器中调整数据的符号或颜色;有多份这个量级的数据可以单独或叠加在一起显示;数据的更新也能尽快看到。每个用户的需求不尽相同,细节还有很多,其实用户还会有一些隐含的疑问:随着记录数量、数据份数、以及访问人数的增长,硬件资源的增长曲线是怎样的?软件系统的易用性和稳定性如何?
然后分析一下数据量,只用记录条数这个指标不太准确,不同的几何类型(点、线、面)实际存储容量差别较大。根据我手中的一份模拟地类图斑的测试数据,该数据.shp文件大小约为1299MB,约237.8万条记录,单条记录的平均节点数为32.3个。转换成FileGDB格式其中数据文件大小约566MB,这其中包含了属性字段,原.dbf文件约100MB。将FileGDB转换为只读压缩格式(简称为CDF格式),数据文件大小约403MB。 如果用户的数据有大约一亿条记录,其几何部分大约有10GB-50GB。
接下来分析一下在本地桌面应用中显示这份数据的情况,采用的是多种符号类型中耗时最少的一种。在ArcMap中简单渲染时,shp格式20秒,FGDB格式40秒,CDF格式25秒;唯一值渲染时,shp格式80秒,FGDB格式48秒,CDF格式29秒。由于shapefile格式将属性和几何分开存储,导致唯一值渲染所需时间极大的增加。
在ArcGIS Pro中简单渲染时,shp格式12秒,FGDB格式14秒,CDF格式25秒;唯一值渲染时,shp格式80秒,FGDB格式20秒,CDF格式23秒。ArcGIS Pro由于采用了多线程和GPU的技术,渲染效率有一定程度的提高,尤其是针对FGDB格式提升非常大。需要指出的是,Pro渲染虽然效率有提升,但对硬件资源的占用也大很多,在本地桌面应用时这点区别不明显,但如果想将这种方式迁移到服务端应用,产生的影响就会放大。
接下来讨论一下优化思路,我将能想到的各种优化方式大致分为三类:一是重复使用之前的计算或请求结果,常见的就是各种缓存;二是分散计算,可以服务端并行计算,也可以让客户端参与计算;三是优化每次计算的消耗,这个比较宽泛,改算法、加索引都可以算做此类。
第一种优化我们接触得最多,其中又以缓存地图服务最为熟知,让我多啰嗦几句,缓存地图俗称切片,本质就是预先将地图渲染成图片,每次请求都返回一张图片,达到一次计算重复使用的目的。至于图片的格式、存储方式、组织形式等细节会有很多变化,但不影响整体思路。主要优点就是每次请求的资源消耗极小,缺点是占用空间大,预处理时间长,不能动态改变渲染颜色符号。典型场景是底图服务,只读型服务,数据更新频率低,访问频繁。对于缓存地图的分析,下文将详细展开,这里先略过。
现在硬件资源很丰富,因此有一个很常规的想法就是服务端通过资源横向扩展即并行计算来应对数据量的增长。用上面最小的渲染时间线性增长来计算,一亿条左右的数据,若想满足5秒内的使用体验,所需的并行度约100倍。而实际上并行度与效率提升远远不是反比的关系,200倍也不一定能够实现。虽然目前几百个CPU参与计算并非不能接受,但这只是针对一份数据,只响应了一次请求,而且不能重复利用,这样的代价实在是太大了。其适用场景应该是如果一个计算改成以十几倍的并行度实现,就能得到满意的性能结果,面对一些低频请求,这样可以避免对系统整体设计的改动。而我们需要面对的可能是数百倍的并行度,这明显是不现实的。
由于空间索引是GIS数据的标配,在范围上分块请求,可以保证每个请求都能快速的获得数据。因此基于范围分块是服务端并行最简单的方式,对于地图显示就是前端分块请求动态地图服务。如果以此再结合缓存的方案,实际上就是对缓存地图服务中按需切片功能的自定义实现(性能方面会有优化,按需切片由于每次请求都是按一个bundle的范围生成缓存,因此速度较慢;而自定义实现可以针对一个小瓦片范围,因此速度较快),虽然简单实现这一功能的开发代价并不高,但曾经散列式切片导致的管理问题依然会存在。有人提出使用各种NoSQL数据库存放缓存,或许可以在一定程度缓解,但也引入了额外的运维问题。更关键的问题是,自定义按需缓存只能解决重复请求的问题,但全图范围小比例尺的第一次请求,或许会慢到服务超时。如果加入预先生成的方案,则又回归到缓存地图的道路上。并且,面对前端动态改变符号的需求,由于缓存的是只是图片不能修改,所以预生成也无能为力。
要素服务则是一种客户端渲染的思路,服务端负责数据的读取,格式转换,传输到客户端,使用客户端的资源进行渲染。后来传输格式从json改成pbf格式,从数据压缩方面进行了优化(也综合考虑了基于WebGL的GPU渲染算法优化)。但对于我们的目标一亿条数据来说,无论怎样压缩,仅几何部分也需要GB级别的大小,对于网络传输来说太大了。
由于小比例尺下,数据的细节视觉已经无法分辨,因此数据优化的第二步是在节点数量上进行抽稀,Polygon最少由三个节点构成,数据理论上可以得到十倍的压缩,大小达到数百MB的级别。这样的大小对于网络传输或许勉强可以接受,但对于浏览器前端,无论是内存占用还是计算能力,还是显得过大了。再考虑到多图层叠加的需求,还需要进一步的优化。
数据优化的第三步是对记录数量进行优化,对于重叠或共边的小图斑可以使用合并的方式(Union或Dissolve算法)减少数据量。但不共边的图斑则没有简单的算法用于合并,强行减少记录数量的后果就是随机删除部分数据,有可能导致在视觉可见的程度内发现数据丢失的情况。
目前ArcGIS矢量切片服务就是一种综合的优化方案,在数据优化方面大概已经考虑了上述三种方式,并综合了客户端GPU渲染和服务端缓存,除了服务端并行之外的手段都使用了。而且由于是客户端渲染,可以实现在客户端修改渲染符号。但目前矢量切片服务的问题是在小比例尺下,面对百万条记录的数据量时,浏览器前端已经出现性能瓶颈,并且对于不共边的图斑会出现严重的丢失现象。
也有人提出一种思路,对矢量切片的方案做一些改动,将客户但渲染调整到服务端,由浏览器前端提交渲染请求,服务端采用GPU渲染后返回图片结果。这样即减少了大量的数据传输,而且通过分块并行就可以使用服务端强大的资源并行渲染,相对于客户端的渲染能力自然大大提升。这种方案的理论上应该具备了实现本需求的能力,但我认为也存在两大挑战:1,这一思路需要使用几个开源类库组合搭建系统,而现有开源方案没有针对我们的目标数据量进行优化,从而对系统建设者的工程能力提出了很大的考验。2,这一方案随着数据量的增长,对于硬件资源的需求曲线如何?我没有相关的经验数据,无法给出预测,但有一点可以明确,这类服务端并行方案能够承受的并发数一定不高。
对于数据层的优化,我还有一个想法:在适当的比例尺下,用Point对象来代表Polygon对象,并考虑Point对象的合并。这样做应该可以实现最大限度的基于视觉效果压缩数据,但这样做需要从前端到后端到存储格式的整体配合,对系统的改动非常大。
回归
不知道我想像中的这种优化方式实现的难度有多大,但仔细想想似乎会回归到一个基本的问题,如果所有数据都以点来显示,那么矢量和栅格又有什么区别呢?这个点不是传统的点数据,而是像素点,是一个多边形缩小到只用一个像素点来代表,这时的符号已经失去了意义,只剩下一个颜色信息(当然,矢量数据附带属性的方式与栅格是完全不同的,后文还会涉及)。每个像素一个颜色,这不正是栅格数据所呈现的效果么。而矢量数据也完全可以转换成栅格数据,并且在一定的比例尺范围内,从视觉角度几乎无法分辨其区别。
这时思路又回归到最开始的栅格切片,即缓存地图,通过矢量转栅格将计算结果保存下来,为后来的请求重复使用。略有不同的是,这个栅格不是以前的那种RGB图片,而是一个单波段的栅格,类似于DEM。再通过栅格渲染,可以实现几乎与矢量渲染相同的效果。
前文提到传统的缓存地图服务在实际工作种造成了一些困扰,其主要缺点在于占用磁盘空间大,生成时间长,以及不能动态改变渲染颜色,但现在我们的目标场景与之前又有了一些区别:
-
通常切片的时间长是由于进行底图生产时,图层多、符号和标注较复杂,渲染时计算量大、耗时长。而我们现在面对的是单一图层的海量业务数据,矢量转栅格也不是渲染,其速度很快,并且该操作完全符合分块并行的条件,时间消耗则可以大大降低。
-
通常切片占用磁盘空间大主要是由于每提高一个切片级别,所需空间要成倍(2~4倍)增加,而不同的比例尺所显示的地图内容可能完全不同,因此每个级别都要单独生成,最大比例级别又比较大,因此所需要的磁盘空间非常大。而我们现在的主要目标是解决在小比例尺下显示地图的问题,这时的栅格数据体积比较小,并且这类业务数据不同于底图,每个级别显示的内容是一致的,因此不用在每个级别独立创建切片,可以直接使用栅格重采样或金字塔。
-
切片地图是完全静态的,是由于生成的是基于RGB三波段的图片文件(或增加透明度波段),这样的图片修改颜色的算法很复杂(参考PS功能)。从信息的角度看,这是将矢量的属性信息基于渲染方案直接转化为色彩信息,如果动态调整渲染,等于是需要从色彩信息反推属性信息,但这并不是一个可逆操作。而矢量转栅格,并非渲染,实质是将属性信息转换成另一种格式,色彩信息还需要通过栅格渲染再次转换。
在对比了缓存地图的缺点之后,再从头来梳理一下这个方案:在小比例尺下,将原来矢量的渲染过程分解成两部分,矢量转栅格和栅格渲染。栅格作为一种中间格式存在,就意味着每次请求都只需进行第二阶段的计算,实现了第一阶段计算的重用。第二阶段的具体的实现可以直接使用影像服务。在大比例尺时,可以仍然使用动态地图服务,或由前端分块请求动态地图服务。这一方案在部署时,在服务端提供一个影像服务、一个动态地图服务;前端加载一个影像图层、一个地图图层,分别设置适当的比例尺可见性和渲染方案。可见这一方案软件架构简单,相对于并行计算对资源消耗小,能提供更多的并发访问能力。
对于快显这一目标,这一方案最受关注的关键点通常是矢量转栅格这一步骤的时间消耗如何?通过空间分块并行执行,能提高到多少?通过对比直接执行FeatureToRaster工具和利用Python多进程并行转换再镶嵌的两种情况,发现在数据量较大时并行还是能明显的提高效率。上述那份数据,生成的TIF行列数为1万5千,基于LZW压缩后的大小为4.4MB。直接使用工具耗费87秒,4核并行耗费75秒,8核并行耗费78秒。将该数据放大6倍(1426万条记录),使用同样的像元大小,行列数为2万1.5万,生成的TIF为26MB,直接使用工具转换(单核计算)耗费542秒,8核并行耗费217秒。
除了预处理的时间消耗,这一方案还有一些问题需要解决:
- 转换时只能基于数值型字段,对于文本字段还需要考虑转换成数据字段的方法。
- 转换只能针对一个字段进行,意味着对于用户使用时只能针对这一个字段进行渲染。如果需要更换渲染字段,只能针对不同的字段执行多次转换,但这会导致处理时间线性增长。对于多字段的栅格结果,可以考虑以波段的形式组合到一起。
- 前端在渲染时需要针对影像图层和地图图层分别处理,增加了前端的处理逻辑,可以考虑通过代理服务等形式实现统一化设置。
网友评论