由于数据量巨大和几何图形复杂程度高,在构建GIS应用时很容易碰到性能问题。查询中常常设计空间相交操作,这和一般数据库的join不一样。如果有一个复杂几何体的大数据集,查询给定的AOI和数据及中哪些几何体相交,计算量会非常大。
下面用PostGIS的geometry(Polygon,4326)类型的河流数据单独存储在一张表作为目标表。
1 | CREATE TABLE river ( |
用ST_MakeEnvelope构建给定AOI矩形查询相交的河流,会进行全表扫描,测试耗时25.78ms。
1 | SELECT * |
策略一:空间索引
添加空间索引后,从序列扫描变为索引扫描,耗时减少97%,为0.752ms。
1 | CREATE INDEX river_shape_idx ON river USING gist(shape); |
添加空间索引后空间索引PostGIS只需要对表中的一小部分多边形进行相交计算。
在处理相对简单和比较小的多边形(比如建筑物polygon)的时候,通常添加空间索引就已经足够了。大多数相交的外边框都会产生实际的多边形相交,很少误报,所以性能足够高。
策略二:边界分割
考虑另外一种情况,比如横跨整个大陆的河流,这个时候AOI大概率会和河流的外边框相交,空间索引就不起作用了。

解决方案是对河流数据进行分割,把复杂的大型多边形切分成简单的多边形另外存储。这里将river中的数据每10个点一组拆分到river_segment表中。
这里可以设置数据库触发器,在river更新时自动同步。
1 | CREATE TABLE river_segment ( |
在分割后的表中进行查询:
1 | SELECT * |
这种优化适合大型的复杂多边形,查询性能提升超过插入的分割操作,能有效优化读取性能。
策略三:只用索引查询
用&&操作符进行索引查询可能是最快的方式,会返回外边框与AOI外边框相交的所有geometry。只检查R-Tree索引,而不验证geometry是否真的相交。
1 | SELECT * |
这种方法的弊端在于,查询结果可能并不真正相交,尤其在道路、河流这类实际geometry只在外边框占据很小部分的。而对于建筑物这类会比较适用,不在意少量误报的情况下,查询性能会明显提升。
策略四:多边形简化
对于大型多边形,比如岛屿、主要道路这类,可以在查询时简化多边形。比如按照缩放级别对多边形进行简化,缩放级别通常在0-23之间,3级大约是一个大陆的大小,15级展示单个建筑物,超过20级展示地图细节。下面计算每个缩放级别中大概的分辨率,每向下缩放一个级别,分别率减半。
1 | pixelSize = basePixelSize / (2 ^ zoomLevel) |
basePixelSize是缩放级别0时的像素大小,大约是78公里。根据使用的SRID,我们需要将其转换为SRID的单位。在上面的例子中,我们使用了4326,它使用度作为单位。可以通过除以111公里/像素将这78公里转换为度数,结果是在缩放级别0时每像素0.7度。
1 | pixelSize = 0.7 / (2 ^ zoomLevel) |
使用ST_Simplify查询简化后的多边形:
1 | SELECT * |
两种应用方法:
- 实时–每次用户发出请求时,确定所请求的缩放级别所需的详细程度,并在将多边形返回给客户端之前对其进行简化。
- 预先计算–已知缩放级别的范围,可以预先计算并存储原始多边形的所有简化版本,然后根据请求的缩放级别检索它们。
如果以上策略应用得当,可以极大地提高地理空间应用的性能,处理大量或复杂计算的几何图形。
参考链接:https://medium.com/symphonyis/boosting-postgis-performance-c68a478daa0a