st_linesubstring 无法解释的行为

Unexplained behavior of st_linesubstring

鉴于 ST_LINESUBSTRING (https://postgis.net/docs/ST_LineSubstring.html) 的文档,您会认为将 2d 线串分成两个相等的部分会很简单。

见下文:

SELECT ST_LENGTH( ST_LINESUBSTRING( geom, 0.0, 0.5 )::GEOGRAPHY ),
       ST_LENGTH( ST_LINESUBSTRING( geom, 0.5, 1.0 )::GEOGRAPHY )
FROM data.street WHERE id = '11242';

    st_length     |    st_length
------------------+------------------
2003.80912128768 | 2153.56113185375 (1 row)

请注意,上面执行的命令应该产生等长的两半。但是,两半的长度不同。

鉴于以下情况,您预计每一半都是 ~2078:

SELECT ST_LENGTH( geom::GEOGRAPHY )
FROM data.street WHERE id = '11242';
    st_length    
-----------------
4157.37025314031
(1 row)

至少,如果将每一半加在一起,就会得到 4157.370253,这是正确的长度。因此,ST_LINESUBSTRING 并未缩小或扩大几何图形。

所以,有人有答案吗?为什么 ST_LINESUBSTRING 会这样?

此行为的原因是 ST_LineSubstring 拆分几何图形,而不是地理图形。换句话说,它在存储 LineStrings 的投影坐标中进行拆分。

例如,假设一条特定的线连接地球上的两点 A、B。现在,如果将这些点投影到 lon/lat 坐标并计算中点,结果将不同于投影连接 A 和 B 的大圆的中点。

一种解决方案是使用 ST_Segmentize:

CREATE TEMPORARY TABLE lines(
  geom GEOMETRY
);

INSERT INTO lines VALUES
  (ST_MakeLine(ST_MakePoint(-13.4, 52.25), ST_MakePoint(-11.582, 48.1351)))
;

SELECT ST_Length(geom::geography) FROM lines;

SELECT
  ST_Length( ST_LineSubstring(geom, 0.0, 0.5 )::GEOGRAPHY ),
  ST_Length( ST_LineSubstring(geom, 0.5, 1.0 )::GEOGRAPHY )
FROM lines;

SELECT
  ST_AsText(ST_MakeLine(start_point, end_point)) AS segment,
  ST_Length(ST_MakeLine(start_point, end_point)::geography) AS len
FROM
(
    SELECT
        ST_Pointn(geom, generate_series(1, ST_NumPoints(geom)-1)) as start_point,
        ST_Pointn(geom, generate_series(2, ST_NumPoints(geom))) as end_point
    FROM (
        SELECT ST_Segmentize(geom::geography, ST_Length(geom::geography)/2)::geometry AS geom FROM lines
    ) AS line
) AS tmp;

这会产生:

CREATE TABLE
INSERT 0 1
    st_length     
------------------
 475724.617118868
(1 row)

    st_length    |    st_length     
-----------------+------------------
 237537.46004128 | 238220.186078105
(1 row)

                            segment                             |       len        
----------------------------------------------------------------+------------------
 LINESTRING(-13.4 52.25,-12.4518126471826 50.1960897877664)     |  237904.63855338
 LINESTRING(-12.4518126471826 50.1960897877664,-11.582 48.1351) | 237819.978632575
(2 rows)

使用后一种方法,段的长度确实大致相等。