線分の最長セグメントを削除し、セグメントを再結合します

Aug 21 2020

線分から最も長いセグメントを削除し、それらの間の最も短いセグメントによって残っている2つのセグメントグループを結合しようとしています。

それは簡単で、線分とその最長のセグメントの間でST_DifferenceST_Split機能します)使用ST_ShorestLineし、その後、結果として得られた両方のセグメントのグループを結合していました。

しかし、主要なセグメントが線分の多くのセグメントと交差するときに最大の問題を見つけました。セグメントの3つ以上のグループの結果ST_DifferenceまたはST_Split戻り値(ポイントタイプの交差も考慮されるため)。

これにより、その後のグループの結合が困難になります。

私が役立つと思うのは、ST_Differenceor ST_Split(または別の関数IDK)は線文字列タイプの交差のみを考慮しているが、正しいクエリを作成できないことです。

または、最も長いセグメントを削除して、隣接する2つの線分を再結合する簡単な方法があるでしょうか。

何か案は?

回答

1 dr_jts Aug 24 2020 at 02:26

これがソリューションの最初の部分です-SQLは、最長のセグメントの両側の行の部分を検索します。

WITH data(id, geom) AS (VALUES
    ( 1, 'LINESTRING (0 0, 1 1, 2.1 2, 3 3, 4 4)'::geometry )
),
longest AS (SELECT i AS iLongest, geom,
    ST_Distance(  ST_PointN( data.geom, s.i ),
                  ST_PointN( data.geom, s.i+1 ) ) AS dist
   FROM data JOIN LATERAL (
        SELECT i FROM generate_series(1, ST_NumPoints( data.geom )-1) AS gs(i)
     ) AS s(i) ON true
   ORDER BY dist LIMIT 1
)
SELECT 
  CASE WHEN iLongest > 2 THEN ST_AsText( ST_MakeLine(
    (ARRAY( SELECT (ST_DumpPoints(geom)).geom FROM longest))[1 : iLongest - 1]
  )) ELSE null END AS line1,
  CASE WHEN iLongest < ST_NumPoints(geom) - 1 THEN ST_AsText( ST_MakeLine(
    (ARRAY( SELECT (ST_DumpPoints(geom)).geom FROM longest))[iLongest + 1: ST_NumPoints(geom)]
  )) ELSE null END AS line2
FROM longest;

このコードの便利なパターン:

  • JOIN LATERAL generate_series 線分を抽出するには
  • 元の行のセクションを含むサブ行を抽出するための配列スライシング

(潜在的な)2つのラインを結合するための戦略が不明確であるため、それは実装されていません。

このSQLの一部が関数によって提供されているかどうかはより明確になります(たとえば、ST_LineSlice関数、およびST_DumpSegments関数-おそらくいつかPostGISの一部になるでしょう)。

2 CyrilMikhalchenko Aug 24 2020 at 17:45

線に自己交差がある状況に遭遇する人のために、Postgre / PostGISSQLでオリジナルの楽しいソリューションを公開します。

したがって、初期の状況は、line(LineString EPSG:4326)という名前の初期テーブルの図に示されています。

新しい元の関数を作成し、それを実行するリクエストを起動します。

CREATE OR REPLACE FUNCTION ST_SelfIntersectingLineRectifier(
geom GEOMETRY
)
RETURNS GEOMETRY AS
$BODY$
WITH
    tbla AS (SELECT (ST_Dump(geom)).geom geom),
    tblb AS (SELECT ST_MakeLine(pt1, pt2) geom FROM (SELECT ST_PointN(geom, generate_series(1, ST_NPoints(geom)-1)) pt1,
      ST_PointN(geom, generate_series(2, ST_NPoints(geom))) pt2 FROM tbla) AS geom),
    tblc AS (SELECT DISTINCT ST_Union(a.geom) geom FROM tblb a, tblb b WHERE ST_Length(a.geom)<ST_Length(b.geom)),
    tbld AS (SELECT DISTINCT (ST_Dump(ST_Difference(a.geom, b.geom))).geom geom FROM tblb a JOIN tblc b ON ST_Intersects(a.geom, b.geom)),
    tble AS (SELECT ST_MakeLine(ST_LineSubstring(geom, 0.1, 0.9)) geom FROM tbld),
    tblf AS (SELECT (a.geom) geom FROM tblb a JOIN tble b ON ST_Intersects(a.geom, b.geom)), 
    tblg AS (SELECT (a.geom) geom FROM tblf a WHERE NOT EXISTS (SELECT 1 FROM tbld b WHERE ST_Intersects(a.geom, ST_StartPoint(b.geom)))),
    tblh AS (SELECT (a.geom) geom FROM tblb a JOIN tbld b ON ST_Touches(a.geom, b.geom) LIMIT 1),
    tbli AS (SELECT ST_MakeLine(ST_EndPoint(a.geom), ST_EndPoint(b.geom)) geom FROM tblg a, tblh b)
    SELECT ST_Union(geom) geom FROM (SELECT * FROM tblc UNION SELECT * FROM tbli) foo
$BODY$
LANGUAGE SQL

SELECT ST_SelfIntersectingLineRectifier(geom) geom FROM line

次の図で結果を確認できます。

スクリプトは呼び出されます ST_SelfIntersectingLineRectifier

この問題を分析的に解決することは実際には本当に難しいですが、幾何学的に解決することは非常に簡単です...

解決策を見つけるのに役立つ節点または節線を定義することが常に重要であることを忘れないでください...

「最初はあなたの会議に駆け寄りますが、それからあなたは私にあなたから逃げるように強制します...」🙂

www.DeepL.com/Translator(無料版)で翻訳