关于矢量瓦片技术支持前端渲染带来的思考

前言 书接上回,此前提到地图瓦片切片技术的发展。矢量切片技术将瓦片的渲染由服务端迁移到客户端,此操作带来的影响力不可谓不大,基于此,完全可以随心所欲的定义地图的表达。那么在实际的应用当中,当渲染从服务端迁移后客户端后,是否会带来一些其他的问题? 超20M的瓦片数据 此事发生在2023年,当时我们的技术组合是:空间数据库+GeoServer(vector tile plugin)+ Mapbox GL JS,基于此提供矢量瓦片服务。某一天,在某位心细如发的大佬的察觉下,突然发现提供的地图服务在层级为10级时,出现一个大小大于20M的瓦片,顿感惊人。经过多次核对后,确定在该份数据下,GeoServer在小比例尺下(约莫10级往下)生产的大部分瓦片尺寸都比较大,而数据密度越大的地方尤其严重。最终,我们认为:是由于GeoServer没有提供矢量分层抽稀简化的能力,所以导致在小比例尺+高密度的双重叠加下,瓦片大小暴增。 在一段时间后,为了快速处理该问题,我曾提出:可以基于目前配图的思想(解析配图文件),在服务端去往数据库读取数据的时候便进行数据过滤(其实就是分层分级),避免大量不需要的数据经查询、传输、编码再传输带来的影响。比如我们按照口径进行约定,在13级往下,只显示大于xx口径的数据的数据,那么此时所有小于该口径的数据都不需要被传递。 但我们前端配图人员提出一个说法:说如果你这般处理,那么意味着配图的时候数据就不是完整的,我无法自行选择数据,不同的项目需求也不尽相同,灵活性自然大打折扣。同时,也就意味着该项配置与瓦片缓存是绑定的,一旦存在配图项的变更,便意味着此前缓存均是失效的。 配图的边界 时至今日,已过半年。目前想来,这里确定存在这么几个问题: 到底什么是配图,配图配的又是什么? 数据发布过程中,配图应该处于什么阶段? 对于什么是配图,我目前还给不出一个十分恰当的回答,因为此前确实没有做过这方面的内容。但私以为狭义情况下,地图的符号化过程应该称之为配图。配图内容如下: 要素符号化 文字标注 那么分层显示控制能力(属性分级)是否应该归属到配图中呢?在图像切片时代中,想来配图都是提前确定好的,而且受益于影像金字塔自带多层次分辨率的能力,所以在配图中压根不需要考虑分级问题(假定数据都是栅格数据)。但是矢量数据并没有分辨率的说法,所以无法从影像金字塔模型获利。在面对大数据量或高密度区域的情况时,需自行处理分级问题。 对于矢量数据来说,在矢量瓦片技术出现之前,所有瓦片都是在服务端渲染,那么属性分级控制也是在服务端进行的。而在矢量瓦片技术出现之后,完全可以直接在客户端实现属性分层控制了。且目前开源GIS技术方案大多都选择:GeoServer+Mapbox,但在实际使用中感觉GeoServer并不理会什么属性分级和空间特征简化,而是一股脑的将数据丢给客户端,客户端自己筛选。 从结果来看,好像在应用上确实是行得通的。实际上确实如此,或许大多数公司都是这样做的吧。但是我认为这是有问题的,这就好像是得益于矢量瓦片渲染能力后移的特点,服务端将原本应该自己管控的数据一股脑的丢到了客户端,由客户端自行控制。 那么问题来了,数据的分层分级控制权到底应该给谁,是瓦片生产端还是客户端呢? 我认为应该优先厘清整个生产流程,明确各节点能力边界 数据的分类、分层肯定是在最前面,且分类、分层与金字塔分层息息相关。且应该形成公知,瓦片的生产端与应用的客户端都应该清晰的知道【数据分层分级控制策略 → 矢量金字塔分层规则定义】 其次是矢量数据的发布,此中应该完整的实现矢量数据分层,即形成矢量金字塔结构【矢量金字塔模型实现】 因矢量瓦片技术带来的配图后置,数据的符号化在此处完成【地图符号化】 其次,可以想一下数据分层控制权限归属到客户端后会带来什么样的影响: 在大数据量或高密度区域的情况下,小比例尺级别瓦片尺寸可能爆炸 计算资源、存储资源(缓存)占用增多 对带宽要求高,移动端的话流量嗖嗖的跑 客户端性能下降 所以我认为,数据控制能力应该归属于瓦片生产端,客户端可以支持分层控制能力,但其对于数据的控制只能在全局统一的分类、分层策略所提供的范围内活动。也就是说客户端可以实现的分层范围是全局规定的范围的子集。 对于目前在配图中进行全局数据的分层分级行为,我认为是矢量瓦片技术带来的配图动作后置产生的影响。同时在GeoServer + Mapbox 客户端控制数据操作的长期影响下,给后来人一种错觉便是数据控制也变更到客户端控制。 论GeoServer的正确使用? 最近在自己实现一个矢量瓦片服务,由于自己目前只会写Java,所以不得不对GeoServer进行借鉴,所以就进一步的研究了GeoServer的源码。 GeoServer可以看作是GeoTools,GeoWebCache以及OGC API Implement的结合体。其中,GeoWebCache提供了金字塔结构的定义和缓存能力;GeoTools提供了空间数据相关的定义和操作。在本次研究后,我确定我需要向GeoServer道歉。在此前的描述中,我认为GeoServer是没有提供数据分层和空间简化的能力的,但是我错了。 我拉取的是GeoServer的2.22.x分支,因为这个版本用的比较多,相对而言更具有代表性,所以没有选择最新版本。 空间简化能力 也就是说,在GeoServer vector tile的生产过程中,已经集成了Simplify功能。 还有一个情况,其实GeoServer Vector Tile Plugin采用的矢量瓦片编码器(java-vector-tile)中其实也提供了Simplify的功能,只不过默认是关闭的。 数据分层(属性分级) GeoServer的vector tile实现中,居然是尝试从Style(SLD)中获取到Filter的信息,用以减少数据的检索(数据库中的Filter比Java基于内存的Filter更高效),同时还提供了一个将Mapbox style转换为SLD的拓展模块。 看到这里的我很激动,感觉找到了知己一般,哈哈哈。 在明白真相之后的我,只能是感叹GeoServer的历史包袱太重了,但应该被敬佩。 而此时再次回头看我之前提出解析配图文件的想法,发现这种做法也是有问题的。 矢量瓦片技术出现,实现了一套瓦片数据可以有N种配图方案 矢量瓦片技术完美的分离了数据与渲染,边界清晰 所以,基于配图剥离属性分级的方式不就又将渲染与数据耦合起来,配图也就会和缓存绑定了,也就是一种配图方案一套瓦片数据了(若一旦涉及到分级部分的变化),自然是有问题的。 结论 综上所述,私以为: 在矢量切片技术下,数据当有数据本身的分层规则,不应该依赖于配图,也不应该由配图来定义。 分层控制当属于数据控制权限,应该归属于瓦片的生产端控制 在矢量瓦片时代,配图就是地图符号化的过程,对应矢量切片技术下,渲染后移到客户端的部分 全局分层分级策略应该是首先定义的,且应该形成公知的形态。服务端的数据控制应当严格遵循该策略,客户端可控分层范围是该策略的子集 参考 GeoServer Branch 2....

May 3, 2024 · 1 min · Fuyi

切片技术发展

前言 本文80%内容节选自:《WebGIS数据不切片或是时代必然》,后在其基础上添加了部分内容。 数据切片是解决大规模大体积地理数据在Web前端轻量化传输和显示的关键技术,是每一个开发者几乎每天都在使用的技术,有时候将服务端底图切成xyz图片,有时候将大影像数据切成xyz图片,也有时候将矢量数据切成xyz的矢量切片。 数据切片起源–图像切片时代(WMS → WMTS,服务端渲染+预处理) Web上古时代,人们浏览在线地图,一般是服务器端将页面地图要显示的地理范围内的地理数据都查询出来,然后在服务端按照专题地图的配置样式,渲染成地图图片,再返回客户端浏览,这个时代诞生的OGC标准就是WMS(Web Map Service)服务,该服务一直沿用至今。 类似WMS这种服务存在很多致命的缺点:由于地理范围的不可控,获取范围内的数据不可控,数据有时候会很大,数据通常在数据库中,IO和网络传输就耗时很严重;服务端获取数据后会进行数据渲染成地图图片,占用大量CPU资源;克服一系列的困难终于成图传输给前端浏览。可以想象,这个过程如此艰难,用户可能等的花儿都开了。 为了解决这个痛点,谷歌地图开创性提出了基于Web墨卡托投影,预先在服务端全量渲染,然后按照地图不同的显示级别(金字塔原理),切成了xyz的图片。 该技术成为Web2.0时代使用最广泛的技术,成为WebGIS标准,诞生了一系列如基于工业标准的TMS服务,基于OGC规范的WMTS服务。很快,百度地图,高德地图、天地图都使用该技术原理建设了自己的切片地图服务。广大GIS从业者也开始了“项目一启动,先把底图切”的套路。 基于该技术原理,对大的影像数据如几十G的GeoTiff数据也进行了切片,形成影像底图给客户端使用。 这个时期的开源GIS主要技术是基于GeoServer的WMS、WFS、TMS、WMTS服务。 核心技术:Web墨卡托投影、栅格金字塔 数据切片发展–矢量切片时代(WMTS,客户端渲染+预处理) 虽然基于XYZ的图片切片技术很成功,但是也存在不少问题: 全图预切耗时长:通常切图zoom级别从0到20,通过金字塔原理图可知,上一级别的一张图片,下一级别会裂变成4张,级别越大,图片越多,同时地理范围越大,图片也越多,图片多了,切图耗时就很长,资源无论是基础底图切片还是影像底图切片都要耗时漫长。 资源要求高:需要庞大的服务端渲染和切图的计算资源,满足分布式渲染、切图、存储需求。 存储冗余:原始矢量和栅格数据,同时冗余存储了数据切片,存储压力大,数据切片转储IO压力大。ArcGIS为了提升转储性能,开创了离散性切片和紧凑型切片两个格式,紧凑型切片在转储时性能较高。 数据更新不及时:由于切图耗时长,通常很少会定期更新地图切片数据,导致数据显示落后于实际生活现状,不能很好的服务用户。 地图千篇一律:这个时期项目最常采用的是天地图地图服务,不管啥项目,不管什么地区,似乎地图都长一个样子。甲方可能审美疲惫了,他们希望底图能是定制化的,能不能暗黑一点?科幻一点? 既然存在问题,自然有人想解决问题,从15年左右,Mapbox受够了传统地图显示风格了,它提出了MVT矢量切片技术,该技术一经推出就大受欢迎,开启了WebGIS底图定制化时代。 很显然,矢量切片和图像切片最大的区别就是:渲染从服务端迁移到客户端了,换句话说,服务端减负了,带来的好处也是不言而喻: 资源要求降低:服务器资源要求可见性降低,渲染很耗资源的。 数据更新比较及时:如果数据更新,简单更新下局部变更地区的数据的矢量切片即可,由于不用全图渲染,耗时极大减少,数据的有效性极大提高。 存储冗余降低:采用MbTiles的设计规范,将数据和序号索引剥离,复用重复区域数据,极大降低切片数据量。但是数据冗余还是存在的。 地图不再千篇一律:渲染是客户端的,那么甲方各种优秀的创意都能得到释放了,配置大屏的感觉已经是很容易的事情了。各行各业的地图五彩斑斓各有千秋,地图设计者们的春天来了。高德、百度跟的很快,目前也都提供矢量切片底图定制化服务了 这个时期的开源GIS主要技术是基于GeoServer的矢量切片服务,基于Mapbox的底图切图工具tippecanoe等。这个时期,3D GIS蓬勃发展,诞生了3DTiles、I3S等3D切片ogc规范,并大量在工程中使用。 矢量切片解决了不少问题,但仍遗留问题是:全图预切耗时长,切片数据冗余占用存储空间。这个问题同样也是栅格切片、3D切片共同的问题。于是有人问,能不能数据不切片?也就不存在预切耗时长,切片数据冗余的问题了?答案是肯定的。 核心技术:矢量金字塔,MVT 正所谓尾大不掉,虽然已经出现了矢量切片技术,但是整体的工艺流程还是在预切路线。这可能和矢量切片解决的问题相关,因为矢量切片解决的是渲染的问题,可以更自由的进行渲染了,而且只需要提供一份切片数据即可。 数据切片方向–动态切片时代(WMTS,客户端渲染) 那么上一个阶段遗留的问题还有什么:预处理,也就是不管怎么样,我先切数据 笔者(原文作者:遥想公瑾当年)曾经大量使用PostGIS+并行计算+动态矢量切片技术实现动态业务数据的快速前端矢量呈现,PostGIS是目前开源架构里唯一能支持动态矢量切片的数据技术。 动态切片技术不再预先切图,也不会有大量切片的文件存储,将切片技术诞生以来所遗留的问题都一次清空。由于通常数据量很大,动态矢量切片技术基本上数据不出库,而由数据库汇总组织数据并直接生成切片结果出去,后台几乎啥也不做,时空数据库的地位越来越重要。 这是一个崭新的时代,预示着数据不切片时代的来临,毕竟矢量和栅格不切片技术理论上还是比较成熟的,未来3DTiles这些3D切片理论也会逐渐成熟的。 核心技术:动态矢量切片+矢量金字塔+MVT 在这里我想要补充一下上面的示意图,这里不是说动态矢量切片技术只能应用在数据库中,前文中也提到当前开源架构中PostGIS是唯一能支持动态矢量切片的数据技术。这里是基于传统矢量切片技术与PostGIS的动态矢量切片技术的对比,更多的是想表达流程的变化(预切 → 动态,以及切片产生的位置),以及流程变化带来的性能提升和相关影响;还有就是原文作者认为的后续的发展方向,Based on spatial database。 关于动态矢量切片技术 动态矢量切片技术可以说是传统矢量切片的动态应用(更广义的可以认为:是可以按需动态生成的,具备多层次模型,且每个层次包含适当选取及简化的数据的切片技术)。强调不再预切,而是按需生成,且应该同时满足可以快速显示的要求。 按需动态生成(与数据源是链接着的或是可链接的,即可达成随时可访问),不做预切 多层次结构(如:矢量金字塔) 数据选取与简化 MVT支持 从发展进程上面看,技术的升级是为了解决此前存在的痛点问题。动态矢量切片技术是为了解决预切耗时,且会产生大量的瓦片存储(预切),数据越大,产生的瓦片越多。 从实际的应用上看,此前的矢量切片技术我更愿意将之称为传统矢量切片技术,用以与矢量切片做一定的区分。传统矢量切片技术还是走的预处理的路子,产生的是静态矢量瓦片数据,即在流程上是无法做到快速更新的。与之对应的便是动态实时生产的路线,可称为动态矢量瓦片技术。私以为,两者更大的区别在于矢量瓦片生产的思路,也就是是否需要预切。 所以,动态矢量切片技术可以有各种各样的实现,而PostGIS中的动态矢量瓦片技术便是其中的一种。且到目前,GeoServer同样实现了动态矢量瓦片技术(只不过其历史包袱过重 🫡)。 值得注意的是,基于数据库的动态矢量切片有一个很大的特点:缩短了切片的传导路径(也就是所谓的数据不出库,出库即为切片)。 参考 WebGIS数据不切片或是时代必然 PostGIS动态矢量切片(原理+实现)

May 3, 2024 · 1 min · Fuyi

如何构建一个矢量瓦片服务

前言 关于矢量瓦片(节选) 地图瓦片技术是在线地图服务常用的瓦片技术,瓦片就是地图瓦片的具体存储形态,提前切好的瓦片可以大大提高在线地图的访问效率。 栅格瓦片 以图片为介质的栅格瓦片使得在线地图得以迅速普及,优势在于显示效率高、方便传输。但是,随着地图的移动化和应用的逐渐深入,栅格瓦片占用带宽和存储都较大,不利于地图在移动设备的应用。 矢量瓦片 矢量瓦片的产生弥补了栅格瓦片的不足。矢量瓦片数据以矢量形式存在。矢量瓦片体积下,可高度压缩,占用的存储空间比栅格瓦片要小上千倍。数据传输体量小,地图更新的代价小 常见的矢量瓦片制作工具(节选) 目前开源的矢量切片工具还是非常多的,列出一些主流的阐述下: 基于GeoServer的矢量切片插件,适合熟悉GeoServer的用户,操作还比较简单,缺点是切片的行列号与一般的XYZ编号不同不容易单独部署。 基于tippecanoe的矢量切片工具方案,该工具提供了很多高级功能在数据定制化上有很强的优势,但只能部署在Linux,并不是跨平台,只能读取geojson文件,不能直连数据库,不是很好,如果有幸您是c++开发大神,可以改下库的编译绑定平台,使其支持windows,再更改下数据源底层,使其能支持空间数据库,那么该工具会有更多的应用空间。 基于PostGIS的矢量切片方案,该方案在熟悉PostGIS的用户中应该很受欢迎,优势是支持动态矢量切片,有PG社区的系统级加成。 总的来说,工具虽然很多,但是没有一款可以说覆盖一切场景的,具体应用还是看场景的,比如前两个方案都是做底图数据时比较有用,都是静态矢量切片方案,geoserver能直连数据库,tippecanoe有强数据定制性要求,那么如果用户侧重点是简单点的话geoserver够了,用户侧重点是希望对数据做很多高级过滤什么的操作用tippecanoe,但步骤麻烦点。这些矢量切片工具仅仅在处理很久不变的数据,就是切一次用很久的数据,如果数据频繁变化,这种静态数据切片工具就很不好用了。 与其他方案相比,PostGIS方案的好处主要有两大点: 资源开销低:空间数据一般存空间数据库中,传统工具会先从数据库中捞数据,这个数据通常很大,网络开销和服务器端内存都要很大,查询慢计算慢是肯定的。而PostGIS是在数据库中把数据处理完,只把结果传给后台转前台,可以很方便的使用数据库的索引,并行计算等,优化查询和处理速度。 动态矢量切片,数据时效性高:每当根据xyz请求时,数据库会动态查询范围内数据,裁剪简化并输出pbf格式的二进制数据出去,在数据变化频繁的场景下,可以保证用户看到的是最新的数据。 💡 GeoServer、Tippecanoe 皆为静态矢量切片方案,需提前准备切片数据,并进行持久化(GeoServer也可以在使用时进行切片,同时进行持久化)。PostGIS支持动态矢量切片方案,即实时计算生成切片,且不进行切片的持久化。 为什么要自己写一个服务 于我个人而言,我目前仅接触和使用到了GeoServer,且对其中的实现细节并不太清楚,所以想通过参考模仿的方式实现一个示例服务。其次,还想测试在没有如GeoWebcache此类的瓦片缓存的情况下,服务的性能如何。综上所述,其实也就是为了如下这几方面的目的: 学习,了解其中的实现细节 更好的适配 比如说,WMTS服务很明确存在缓存,WMS性能又不够好。如果使用WMTS服务确实可以提升服务的性能,但是对于源数据存在编辑的场景下,缓存问题还是会让人头疼。 那么是否存在动态的矢量瓦片服务?既能解决缓存的问题,同时还没有太大的性能问题。 你或许会提到基于PostGIS的动态矢量瓦片服务,但是有些历史的原因,短时间内没有变法变更数据库。当然也可以基于类如CDC这样的功能进行缓存的更新,但其实还是会存在缓存的问题,只是说可以通过一些手段降低缓存问题出现的概率,并无法从根本上解决问题 所以,基于此,既然PostGIS可以实现动态矢量瓦片服务,我们自然也可以。公瑾大佬曾发文说过,当下地图服务去服务化、数据不切片基本上已经是必然的趋势,那么我为什么还要去做一个服务化的东西。大概就是下面这几个原因了: 数据库技术,在某些特定的因素下,短时间内无法切换到PostGIS 数据量级 性能容忍度 小厂,我不思进取 🙄 后续知识储备 矢量瓦片标准 参见:矢量瓦片标准 在这里贴几个关键点(对于目前使用上来说): 文件格式 矢量瓦片文件采用Google Protocol Buffers进行编码。Google Protocol Buffers是一种兼容多语言、多平台、易扩展的数据序列化格式。 投影和范围 矢量瓦片表示的是投影在正方形区块上的数据。矢量瓦片不应该包含范围和投影信息。解码方被假定知道矢量瓦片的范围和投影信息。 Web Mercator是默认的投影方式,Google tile scheme是默认的瓦片编号方式。两者一起完成了与任意范围、任意精度的地理区域的一一对应,例如https://example.com/17/65535/43602.mvt。 矢量瓦片可以用来表示任意投影方式、任意瓦片编号方案的数据。 内部结构 图层 每块矢量瓦片应该至少包含一个图层。每个图层应该至少包含一个要素。 几何图形编码 矢量瓦片中的几何数据被定义为屏幕坐标系。瓦片的左上角(显示默认如此)是坐标系的原点。X轴向右为正,Y轴向下为正。几何图形中的坐标必须为整数。 矢量瓦片服务构建 在这里,我选择抄GeoServer的作业。众所周知,PostGIS是开源的,那为什么没有选择抄PostGIS的作业呢? 当下水平不够 想快速验证想法 想基于GeoServer做二次开发,或者说是基于现存的地图服务相关的实现,集各家之大成,合并成一个组在功能上可自由搭配的、较高性能服务端组件 接着说当前的事情。要实现一个动态矢量瓦片服务,我们需要先分析一下实现内容,在此先做出如下拆解: 动态矢量瓦片服务可以理解为没有瓦片缓存的,实时生成的矢量瓦片服务,所以核心还是矢量瓦片服务(@time 20210503: 动态矢量瓦片技术是相对矢量瓦片技术提出的,而矢量瓦片技术的大规模应用还是以预切为主,所以动态矢量瓦片要解决的是不再预切动态生成,同时避免一下子生成大规模瓦片文件的问题) 矢量瓦片服务也就是根据调用端传递的参数,从数据源获取对应的数据,并将其转换为矢量瓦片格式,最终返回给调用端 这里选择瓦片坐标作为检索参数,可以便于服务降级(缓存)和性能优化(瓦片坐标值相对来说更加准确和可固定,且便于降维) 需要实现瓦片坐标系到数据源坐标系下数据范围的相互转换 需要实现数据源的范围查询 需要实现矢量数据到矢量瓦片的编解码 那么大体的实现路径可归结于如下所示: 其中的核心要点总结如下:...

May 23, 2023 · 5 min · Fuyi