Rimozione del segmento più lungo di linea e segmenti che si ricongiungono

Aug 21 2020

Sto cercando di rimuovere il segmento che ha la lunghezza più lunga da una linea e unire i due gruppi di segmenti che rimangono dal segmento più corto tra di loro.

È stato facile, usare ST_Difference( ST_Splitfunziona anche) tra la stringa di linea e il suo segmento più lungo, e poi ho usato ST_ShorestLineper unire entrambi i gruppi di segmenti risultanti.

Ma ho trovato il problema più grande quando il segmento principale attraversa molti segmenti della stringa di linea. il risultato ST_Differenceo ST_Splitrestituisce più di due gruppi di segmenti (poiché vengono prese in considerazione anche le intersezioni di tipo punto).

Ciò rende difficile la successiva unione dei gruppi.

Quello che potrebbe aiutarmi, penso, è che ST_Differenceor ST_Split(o un'altra funzione, IDK) prende in considerazione solo le intersezioni del tipo di stringa di linea ma non riesco a creare la query corretta.

O forse esiste un modo più semplice per rimuovere il segmento più lungo e far ricongiungere le due linee adiacenti?

Qualche idea?

Risposte

1 dr_jts Aug 24 2020 at 02:26

Ecco la prima parte di una soluzione: SQL per trovare le parti della linea su entrambi i lati del segmento più lungo.

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;

Modelli utili in questo codice:

  • JOIN LATERAL generate_series per estrarre i segmenti di linea
  • sezionamento dell'array per estrarre una sottolinea contenente una sezione della riga originale

Poiché la strategia per unire le (potenziali) due linee non è chiara, questa non è implementata.

Sarebbe più chiaro se parti di questo SQL fossero fornite da funzioni (ad esempio una ST_LineSlicefunzione e una ST_DumpSegmentsfunzione - che forse un giorno faranno parte di PostGIS).

2 CyrilMikhalchenko Aug 24 2020 at 17:45

Pubblico la mia soluzione originale e divertente su Postgre / PostGIS SQL per coloro che incontreranno una situazione in cui le linee hanno autointersezioni,

Quindi, la situazione iniziale è mostrata nella figura, la tabella iniziale denominata line (LineString EPSG: 4326):

Crea una nuova funzione originale e avvia una richiesta per eseguirla:

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

Puoi vedere il risultato nella foto qui sotto:

Lo script si chiama ST_SelfIntersectingLineRectifier

In realtà è davvero difficile risolvere questo problema analiticamente, ma è abbastanza facile risolverlo geometricamente ...

Ricorda, è sempre importante definire un punto o una linea nodale che ti aiuterà a trovare una soluzione ...

"All'inizio corro al tuo incontro, ma poi mi costringi a scappare da te ..." 🙂

Tradotto con www.DeepL.com/Translator (versione gratuita)