Come creare linee per visualizzare le differenze tra le funzioni poligonali in PostGIS?


15

Ho una tabella PostGIS polygon_bcon alcune funzionalità poligonali. C'è anche una tabella polygon_ache contiene gli stessi poligoni polygon_bma con lievi modifiche. Ora voglio creare linee per visualizzare le differenze tra le caratteristiche del poligono.

inserisci qui la descrizione dell'immagine inserisci qui la descrizione dell'immagine inserisci qui la descrizione dell'immagine

Suppongo che ST_ExteriorRinge ST_Differencefarà il lavoro, ma la clausola WHERE sembra essere abbastanza difficile.

CREATE VIEW line_difference AS SELECT
row_number() over() AS gid,
g.geom::geometry(LineString, yourSRID) AS geom
FROM 
    (SELECT
    (ST_Dump(COALESCE(ST_Difference(ST_ExteriorRing(polygon_a.geom), ST_ExteriorRing(polygon_b.geom))))).geom AS geom
    FROM polygon_a, polygon_b
    WHERE 
    -- ?
    ) AS g;

Qualcuno può aiutarmi?

MODIFICA 1

Come indicato da 'tilt', ho provato ST_Overlaps(polygon_a.geom, polygon_b.geom) AND NOT ST_Touches(polygon_a.geom, polygon_b.geom)ma il risultato non è come previsto.

CREATE VIEW line_difference AS SELECT
row_number() over() AS gid,
g.geom::geometry(LineString, your_SRID) AS geom
FROM 
    (SELECT
    (ST_Dump(COALESCE(ST_Difference(ST_ExteriorRing(polygon_a.geom), ST_ExteriorRing(polygon_b.geom))))).geom AS geom
    FROM polygon_a, polygon_b
    WHERE 
    ST_Overlaps(polygon_a.geom, polygon_b.geom) AND NOT ST_Touches(polygon_a.geom, polygon_b.geom))
     AS g;

inserisci qui la descrizione dell'immagine

MODIFICA 2

workupload.com/file/J0WBvRBb (set di dati di esempio)


Ho provato a trasformare i poligoni in multilinea prima di usare ST_Difference, ma i risultati sono ancora strani.

CREATE VIEW multiline_a AS SELECT
row_number() over() as gid,
ST_Union(ST_ExteriorRIng(polygon_a.geom))::geometry(multilinestring, 4326) AS geom
FROM
polygon_a;

CREATE VIEW multiline_b AS SELECT
row_number() over() as gid,
ST_Union(ST_ExteriorRIng(polygon_b.geom))::geometry(multilinestring, 4326) AS geom
FROM
polygon_b;

CREATE VIEW line_difference AS SELECT
row_number() over() as gid,
g.geom
FROM
    (SELECT
    (ST_Dump(COALESCE(ST_Difference(multiline_a.geom, multiline_b.geom)))).geom::geometry(linestring, 4326) AS geom
    FROM
    multiline_a, multiline_b)
As g;

inserisci qui la descrizione dell'immagine


Sembra più una domanda di topologia. Si desidera identificare i segmenti che non sono coperti dall'altro livello. Non ho lavorato molto con la topologia PostGIS e non posso darti una risposta diretta, ma ti suggerisco di approfondire questo aspetto.
Thomas,

Interessante, hai un set di dati di esempio per il download?
huckfinn,

Risposte:


10

Ecco alcuni nuovi trucchi, usando:

  • EXCEPTper rimuovere le geometrie da entrambe le tabelle che sono uguali, quindi possiamo concentrarci solo su geometrie uniche per ciascuna tabella ( A_onlye B_only).
  • ST_Snap per ottenere la codifica esatta per gli operatori di overlay
  • Utilizzare l' ST_SymDifferenceoperatore overlay per trovare la differenza simmetrica tra i due set di geometrie per mostrare le differenze. Aggiornamento : ST_Differencemostra lo stesso risultato per questo esempio. Puoi provare entrambe le funzioni per vedere cosa ottengono.

Questo dovrebbe ottenere quello che ti aspetti:

-- CREATE OR REPLACE VIEW polygon_SymDifference AS
SELECT row_number() OVER () rn, *
FROM (
  SELECT (ST_Dump(ST_SymDifference(ST_Snap(A, B, tol), ST_Snap(B, A, tol)))).*
  FROM (
    SELECT ST_Union(DISTINCT A_only.geom) A, ST_Union(DISTINCT B_only.geom) B, 1e-5 tol
    FROM (
      SELECT ST_Boundary(geom) geom FROM polygon_a
      EXCEPT SELECT ST_Boundary(geom) geom FROM polygon_b
    ) A_only,
    (
      SELECT ST_Boundary(geom) geom FROM polygon_b
      EXCEPT SELECT ST_Boundary(geom) geom FROM polygon_a
    ) B_only
  ) s
) s;

 rn |                                        geom
----+-------------------------------------------------------------------------------------
  1 | LINESTRING(206.234028204842 -92.0360704110685,219.846021625456 -92.5340701703592)
  2 | LINESTRING(18.556700448873 -36.4496098325257,44.44438533894 -40.5104231486146)
  3 | LINESTRING(-131.974995802602 -38.6145334122719,-114.067738329597 -39.0215165366584)
(3 rows)

tre righe


Per decomprimere un po 'di più questa risposta, il primo passo con ST_Boundaryottiene il confine di ciascun poligono, piuttosto che solo l'esterno. Ad esempio, se ci fossero buchi, questi sarebbero tracciati dal confine.

La EXCEPTclausola viene utilizzata per rimuovere le geometrie da A che fanno parte di B e le righe da B che fanno parte di A. Ciò riduce il numero di righe che fanno parte solo di A e solo di parte B. Ad esempio, per ottenere A_only:

SELECT ST_Boundary(geom) geom FROM polygon_a
EXCEPT SELECT ST_Boundary(geom) geom FROM polygon_b

Ecco le 6 righe di A_only e 3 righe di B_only: A_only B_only

Il prossimo, ST_Union(DISTINCT A_only.geom) viene utilizzato per combinare le linee in una singola geometria, in genere un MultiLineString.

ST_Snap viene utilizzato per agganciare i nodi da una geometria all'altra. Ad esempio ST_Snap(A, B, tol), prenderà la geometria A e aggiungerà più nodi dalla geometria B o li sposterà sulla geometria B, se sono a toldistanza. Esistono probabilmente diversi modi per utilizzare queste funzioni, ma l'idea è quella di ottenere coordinate da ciascuna geometria che siano esatte l'una con l'altra. Quindi le due geometrie dopo lo snap sembrano così:

A scatto B scattò

E per mostrare le differenze, puoi scegliere di utilizzare ST_SymDifferenceo ST_Difference. Entrambi mostrano lo stesso risultato per questo esempio.


Bella risposta. Mi chiedevo cosa hai usato per visualizzare i risultati delle tue domande intermedie. Non sembrava immediatamente qgis, e forse è qualcosa che rende un po 'più veloce?
RoperMaps

1
Uso JTS Testbuilder per visualizzare ed elaborare le geometrie. È un motore di geometria correlato a GEOS e Shapely, ma ha una GUI basata su Java.
Mike T,

Esiste un modo per ignorare / saltare i problemi di intersezione senza nodi tra LINESTRING? Tutti i poligoni di input sembrano andare bene (controllati con il controllo della geometria QGIS).
eclipsed_by_the_moon

1
'ST_Boundary (ST_SnapToGrid (geom, 0.001))' invece di 'ST_Boundary (geom)' risolve il problema.
eclipsed_by_the_moon

6

Penso che sia un po 'complicato, a causa delle diverse serie di nodi di entrambi i poligoni (poligono verde A, segmenti rossi diversi di polione B). Il confronto dei segmenti di entrambi i poligoni fornisce un indizio su quali segmenti del poligono B verranno modificati.

Poligono dei nodi A

poli a

Nodi del "diverso" segmento poligono B

seg diff

Sfortunatamente questo mostra solo la differenza nella struttura del segmento, ma spero che sia un punto di partenza e funzioni così:

Dopo un processo di download e decompressione ho importato il set di dati usando PostgrSQL 9.46, PostGIS 2.1 sotto Debian Linux Jessie con i comandi.

$ createdb gis-se
$ psql gis-se < /usr/share/postgis-2.1/postgis.sql
$ psql gis-se < /usr/share/postgis-2.1/spatial_ref_sys.sql
$ shp2pgsql -S polygon_a | psql gis-se
$ shp2pgsql -S polygon_b | psql gis-se

Supponendo che i segmenti del poligono A non siano in B e vice vera, provo a costruire la differenza tra i segmenti di entrambi i set di poligoni, trascurando l'appartenenza al segmento ai poligoni in ciascun gruppo (A o B). Per motivi didattici, ho formulato il materiale SQL in diverse viste.

Corrispondente a questo post GIS-SE , scompongo entrambi i poligoni in tabelle di segmenti segments_aesegments_b

-- Segments of the polygon A
CREATE VIEW segments_a AS SELECT sp, ep
FROM
   -- extract the endpoints for every 2-point line segment for each linestring
   (SELECT
      ST_PointN(geom, generate_series(1, ST_NPoints(geom)-1)) as sp,
      ST_PointN(geom, generate_series(2, ST_NPoints(geom)  )) as ep
    FROM
    -- extract the individual linestrings
     (SELECT (ST_Dump(ST_Boundary(geom))).geom
      FROM polygon_a
     ) AS linestrings
    -- be sure that nothing is scrambled
    ORDER BY sp, ep
) AS segments;

Il poligono della tabella dei segmenti A:

SELECT 
  st_astext(sp) AS sp, 
  st_astext(ep) AS ep 
FROM segments_a 
LIMIT 3;
                    sp                     |                 ep
-------------------------------------------+--------------------------------------------
POINT(-292.268907321861 95.0342877387557)  | POINT(-287.118411917425 99.4165242769195)
POINT(-287.118411917425 99.4165242769195)  | POINT(-264.62129248575 93.2470010145007)
POINT(-277.459563916327 -44.5629543976138) | POINT(-292.268907321861 95.03428773875

La stessa procedura è stata applicata al poligono B.

-- Segments of the polygon B
CREATE VIEW segments_b AS SELECT sp, ep
FROM
   -- extract the endpoints for every 2-point line segment for each linestring
   (SELECT
      ST_PointN(geom, generate_series(1, ST_NPoints(geom)-1)) as sp,
      ST_PointN(geom, generate_series(2, ST_NPoints(geom)  )) as ep
    FROM
    -- extract the individual linestrings
     (SELECT (ST_Dump(ST_Boundary(geom))).geom
      FROM polygon_b
     ) AS linestrings
    -- be sure that nothing is scrambled
    ORDER BY sp, ep
) AS segments;

Il poligono della tabella dei segmenti B

SELECT
  st_astext(sp) AS sp, 
  st_astext(ep) AS ep 
FROM segments_b 
LIMIT 3;
                    sp                     |                    ep
-------------------------------------------+-------------------------------------------
POINT(-292.268907321861 95.0342877387557)  | POINT(-287.118411917425 99.4165242769195)
POINT(-287.118411917425 99.4165242769195)  | POINT(-264.62129248575 93.2470010145007)
POINT(-277.459563916327 -44.5629543976138) | POINT(-292.268907321861 95.0342877387557)
...                        

Posso creare una vista tabella differenze denominata segments_diff_{a,b}. La differenza è data dalla non occorrenza dei punti di inizio o fine ordinati negli insiemi di segmenti A e B.

CREATE VIEW segments_diff_a AS
SELECT st_makeline(b.sp, b.ep) as geom
FROM segments_b as b
LEFT JOIN segments_a as a ON (a.sp=b.sp and a.ep = b.ep)
-- filter segments without corresponding stuff in polygon A
WHERE a.sp IS NULL;

segs diff b

E le cose complementari:

CREATE VIEW segments_diff_b AS
SELECT st_makeline(a.sp, a.ep) as geom
FROM segments_a as a
LEFT JOIN segments_b as b ON (a.sp=b.sp and a.ep = b.ep)
-- filter segments without corresponding stuff in polygon B
WHERE b.sp IS NULL;

segs diff a

Conclusione: per ottenere un risultato corretto per i piccoli segmenti contrassegnati con la freccia rossa, entrambi i poligoni devono avere la stessa struttura del nodo e un passaggio di intersezione a livello di nodo (inserimento dei vertici del poligono A in B). L'intersezione potrebbe essere effettuata da:

CREATE VIEW segments_bi AS 
SELECT distinct sp, ep
FROM (
 SELECT
      ST_PointN(geom, generate_series(1, ST_NPoints(geom)-1)) as sp,
      ST_PointN(geom, generate_series(2, ST_NPoints(geom)  )) as ep
 FROM (
   SELECT st_difference(b.seg, a.seg) as geom FROM 
      segments_diff_a as a, segments_diff_b as b 
      WHERE st_intersects(a.seg, b.seg)
    ) as cut
  ) as segments
  WHERE sp IS NOT NULL AND ep IS NOT NULL
ORDER BY sp, ep;

Ma con strani risultati ...

versione tagliata


Grazie per il tuo impegno. Bene, i risultati sono strani. Mi chiedo solo se ST_HausdorffDistance () può aiutare a rispondere alla domanda: gis.stackexchange.com/questions/180593/…
Lunar Sea

Hm, st_haudorffdistance ti dà una misura di somiglianza non i segmenti desiderati (frecce rosse che puntano verso).
huckfinn,

È solo un'idea, ST_HausdorffDistance può essere utilizzata per confrontare le geometrie di entrambe le tabelle. Se i poligoni non fossero spazialmente uguali, devo creare delle linee. Non so proprio come fare.
Mare lunare,

Sembra essere una questione di precisione e la topologia ( gis.stackexchange.com/a/182838/26213 e webhelp.esri.com/arcgisdesktop/9.2/... )
huckfinn

1

Guardando l'esempio, la modifica implica che le funzionalità della nuova tabella che sono state modificate saranno sempre sovrapposte a quelle della vecchia tabella. Quindi avresti finito

ST_Overlaps (geoma, geomb) AND! ST_Touches (geoma, geomb)

La negazione al tocco è perché le caratteristiche si sovrappongono anche se solo i loro bordi condividono le stesse posizioni dei vertici.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.