ST_Buffer in Postgis

Postgis 一直没仔细用过,总是临到用时看看文档,这不又遇到问题了,折腾半天。

我们最近有一个需求是,需要把一些原有的多边形范围扩大 50 公里,然后和其他的图形比较看是否有包含关系。是否有包含这个不用看也知道,gis 肯定有现成的函数,那么问题就在于怎么扩大一个多边形的范围。

主要查到几个函数, ST_ExpandST_Buffer。st_expand 可以从 x,y,z 方向扩展,显然不适合我这里的情况,我这不是正南正北的多边形。那就只有 st_buffer 了。

ST_Buffer

ST_Buffer 的语法如下

geometry ST_Buffer(geometry g1, float radius_of_buffer, text buffer_style_parameters='');

geometry ST_Buffer(geometry g1, float radius_of_buffer, integer num_seg_quarter_circle);

geography ST_Buffer(geography g1, float radius_of_buffer, text buffer_style_parameters);

geography ST_Buffer(geography g1, float radius_of_buffer, integer num_seg_quarter_circle);

第一个参数是 geometry 或者 geography 类型的。这又是什么?找到一篇参考文章,下面会详细一点说。 第二个参数是 radius_of_buffer,就是扩大的范围了,但是单位是啥?然后文档里面的解释是 Units of radius are measured in units of the spatial reference system ,这又是啥意思呢,就是取决于你的投影系统是什么。

Geometry 和 Geography

地理坐标系不像是 Mercator(墨卡托)坐标系,不是笛卡尔坐标(就是我们常见的横竖轴 xy 坐标那种)。地理坐标系表达的是一个点在球体上面的位置,通过他和子午线(经度)以及赤道(纬度)的角度来表示。

地球毕竟不是一个完美的球体,所以就算是地理坐标系,也有不同的大地测量系统(Geodetic datum),这样同一个位置,不通的测量系统下,坐标会有一些差别。所以给一个 GPS 坐标点还需要指定对应的测量体系。我们常见的就是 WGS84 (EPSG:4326)。

地理坐标系只能通过地球仪这样的数据展示,不是二维的。大家也不可能都抱着地球仪跑,所以就有了投影映射到平面的需求。

常见的平面投影方式就是 Mercator,也就是 EPSG:3857。球体映射到平面肯定会有地方有失真啦,具体问题可以参考投影方式的说明吧,我们日常使用一般可以忽略这个问题。

Postgis 里面两种类型 geometry 和 geography。一般使用 geometry, 4326 存数据,使用 geography 计算距离。

那么上面的 radius_of_buffer 来说,对于 geometry 数据,是度数。对于 geography 数据,是我们常见的距离(米)。

使用 ST_Buffer 扩展多边形

我们数据库里面存的数据是 GeometryField,所以一种方法如下,把数据 cast 成 Geography 类型的做计算。

st_buffer(gis_data::Geography, 50000, 'endcap=round join=mitre mitre_limit=2')

另一种方法如下,使用 st_transform 转成 3857 再做计算。

st_buffer(st_transform(gis_data, 3857), 50000, 'endcap=round join=mitre mitre_limit=2')

计算完毕得到的是 Geography 数据,如果想要存回去,还需要转成 Geometry,通过 cast 或者 st_transform 都可以完成。