如何构建一个矢量瓦片服务
前言 关于矢量瓦片(节选) 地图瓦片技术是在线地图服务常用的瓦片技术,瓦片就是地图瓦片的具体存储形态,提前切好的瓦片可以大大提高在线地图的访问效率。 栅格瓦片 以图片为介质的栅格瓦片使得在线地图得以迅速普及,优势在于显示效率高、方便传输。但是,随着地图的移动化和应用的逐渐深入,栅格瓦片占用带宽和存储都较大,不利于地图在移动设备的应用。 矢量瓦片 矢量瓦片的产生弥补了栅格瓦片的不足。矢量瓦片数据以矢量形式存在。矢量瓦片体积下,可高度压缩,占用的存储空间比栅格瓦片要小上千倍。数据传输体量小,地图更新的代价小 常见的矢量瓦片制作工具(节选) 目前开源的矢量切片工具还是非常多的,列出一些主流的阐述下: 基于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: 动态矢量瓦片技术是相对矢量瓦片技术提出的,而矢量瓦片技术的大规模应用还是以预切为主,所以动态矢量瓦片要解决的是不再预切动态生成,同时避免一下子生成大规模瓦片文件的问题) 矢量瓦片服务也就是根据调用端传递的参数,从数据源获取对应的数据,并将其转换为矢量瓦片格式,最终返回给调用端 这里选择瓦片坐标作为检索参数,可以便于服务降级(缓存)和性能优化(瓦片坐标值相对来说更加准确和可固定,且便于降维) 需要实现瓦片坐标系到数据源坐标系下数据范围的相互转换 需要实现数据源的范围查询 需要实现矢量数据到矢量瓦片的编解码 那么大体的实现路径可归结于如下所示: 其中的核心要点总结如下:...