선 스트링의 가장 긴 세그먼트 제거 및 세그먼트 재결합

Aug 21 2020

선 스트링에서 가장 긴 세그먼트를 제거하고 그 사이에 가장 짧은 세그먼트로 남아있는 두 세그먼트 그룹을 결합하려고합니다.

선 스트링과 가장 긴 세그먼트 사이에 ST_Difference(도 ST_Split작동 함)을 사용하는 것은 쉬웠으며, ST_ShorestLine결과적으로 두 세그먼트 그룹을 결합하는 데 사용 했습니다.

그러나 주요 세그먼트가 선 스트링의 많은 세그먼트를 교차 할 때 가장 큰 문제를 발견했습니다. ST_Difference또는 결과 ST_Split가 두 개 이상의 세그먼트 그룹을 반환합니다 (점 유형 교차도 고려되기 때문).

이로 인해 그룹의 후속 결합이 어려워집니다.

나에게 도움이 될 수있는 것은 ST_Differenceor ST_Split(또는 다른 함수 인 IDK)가 선 스트링 유형의 교차점 만 고려하지만 올바른 쿼리를 만들 수 없다는 것입니다.

아니면 가장 긴 세그먼트를 제거하고 두 개의 인접한 선 스트링을 다시 결합하는 더 쉬운 방법이 있을까요?

어떤 아이디어?

답변

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 선분 추출
  • 원래 라인의 섹션을 포함하는 하위 라인을 추출하기위한 배열 슬라이싱

(잠재적 인) 두 라인을 연결하기위한 전략이 명확하지 않기 때문에 구현되지 않았습니다.

이 SQL의 일부가 함수 (예 : ST_LineSlice함수 및 ST_DumpSegments함수-아마도 언젠가 PostGIS의 일부가 될 것임)에 의해 제공되는 경우 더 명확 할 것입니다.

2 CyrilMikhalchenko Aug 24 2020 at 17:45

선이 자기 교차하는 상황에 직면하게 될 사람들을 위해 Postgre / PostGIS SQL에 저의 독창적이고 재미있는 솔루션을 게시합니다.

따라서 초기 상황은 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 로 번역됨 (무료 버전)