计算点距离线最近的投影点

之前用惯了ArcGIS,再者是其忠实的拥趸,因此每次遇到稍微拐点弯的问题,都会不厌其烦地去秋名山,。。。。
秋名山

哦,拐错了,是不厌其烦地将数据从数据库导出到ArcGIS中计算然后再将结果导回来。

然而再怎么熟练使用ArcGIS,总觉得导入导出有些影响效率,考虑postgresql本身的空间计算也不弱(丰富的空间函数),于是这几天尝试将一些流程直接迁移至数据库中处理。
这里举一个场景:已知一公交站点空间位置,以及其所在的道路,计算公交站点在该道路上的投影,以及距离道路起点的距离
看到这个问题,可能有些同学说这还不简单,直接用ArcGIS的near(邻近分析)和SplitLineAtPoint(在点处分割线)工具不解ok了么。对,我之前经常就是这么解决的。
上车
实际我觉得再pg中用空间查询计算更简单,主要用到ST_LineLocatePoint(geometry a_linestring, geometry a_point)ST_LineInterpolatePoint(geometry a_linestring, float8 a_fraction)这两个函数;

学过GIS基础理论的同学都知道,线上一点距离起点的长度叫做M值,也叫空间线性参考,比如我们看新闻有提到某个道路发生追尾事故,位于302国道666公里处,这个666就是M值。

所以我们可以先通过ST_LineLocatePoint计算站点到道路起始位置的长度百分比及其长度,然后通过长度百分比计算投影点。
如下所示:
计算长度

1
2
3
4
5
6
7
8
9
10
11
update stop_space_attr set milepost=
case is_same_dir
when 1
then ST_Line_Locate_Point (
st_transform ( st_geomfromtext ( REPLACE ( REPLACE ( st_astext ( road_geom ), 'MULTILINESTRING((', 'LINESTRING(' ), '))', ')' ), 4326 ), 3857 ),
ST_transform ( geom, 3857 )) * st_length(st_transform(geom,3857))
else
(1-ST_Line_Locate_Point (
st_transform ( st_geomfromtext ( REPLACE ( REPLACE ( st_astext ( road_geom ), 'MULTILINESTRING((', 'LINESTRING(' ), '))', ')' ), 4326 ), 3857 ),
ST_transform ( geom, 3857 ))) * st_length(st_transform(geom,3857))
end

计算投影点(这里贴出官网的例子)

1
2
SELECT ST_AsText(ST_LineInterpolatePoint(foo.the_line, ST_LineLocatePoint(foo.the_line, ST_GeomFromText('POINT(4 3)'))))
FROM (SELECT ST_GeomFromText('LINESTRING(1 2, 4 5, 6 7)') As the_line) As foo;

注意:

(1)这两个参数中,线的空间几何必须是简单的linestring,不能是多部件线段multilinestring;

(2)线和点的空间参考最好是投影坐标(如3857);

(3)如果数据量大,使用ST_transform,会影响计算效率,建议提前新建字段,统一空间参考。

孤独求败