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)
使用后一种方法,段的长度确实大致相等。
鉴于 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)
使用后一种方法,段的长度确实大致相等。