Bagaimana cara membuat garis untuk memvisualisasikan perbedaan antara fitur poligon di PostGIS?


15

Saya punya tabel PostGIS polygon_bdengan beberapa fitur poligon. Ada juga tabel polygon_ayang berisi poligon yang sama polygon_btetapi dengan perubahan kecil. Sekarang saya ingin membuat garis untuk memvisualisasikan perbedaan antara fitur poligon.

masukkan deskripsi gambar di sini masukkan deskripsi gambar di sini masukkan deskripsi gambar di sini

Saya kira itu ST_ExteriorRingdan ST_Differenceakan melakukan pekerjaan tetapi klausa WHERE tampaknya cukup rumit.

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;

Ada yang bisa bantu saya?

EDIT 1

Seperti yang diposting oleh 'tilt' Saya sudah mencoba ST_Overlaps(polygon_a.geom, polygon_b.geom) AND NOT ST_Touches(polygon_a.geom, polygon_b.geom)tetapi hasilnya tidak seperti yang diharapkan.

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;

masukkan deskripsi gambar di sini

EDIT 2

workupload.com/file/J0WBvRBb (contoh dataset)


Saya sudah mencoba mengubah poligon menjadi multilines sebelum menggunakan ST_Difference, tetapi hasilnya masih aneh.

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;

masukkan deskripsi gambar di sini


Lebih mirip pertanyaan topologi. Anda ingin mengidentifikasi segmen yang tidak tercakup oleh lapisan lainnya. Saya belum banyak bekerja dengan topologi PostGIS dan tidak bisa memberi Anda jawaban langsung tetapi saya sarankan Anda melihat lebih dalam.
Thomas

Menarik, apakah Anda memiliki contoh dataset untuk diunduh?
huckfinn

Jawaban:


10

Berikut adalah beberapa trik baru, menggunakan:

  • EXCEPTuntuk menghapus geometri dari salah satu tabel yang sama, sehingga kami hanya dapat fokus pada geometri yang unik untuk setiap tabel ( A_onlydan B_only).
  • ST_Snap untuk mendapatkan noding yang tepat untuk operator overlay.
  • Gunakan ST_SymDifferenceoperator overlay untuk menemukan perbedaan simetris antara dua set geometri untuk menunjukkan perbedaan. Pembaruan : ST_Differencemenunjukkan hasil yang sama untuk contoh ini. Anda dapat mencoba salah satu fungsi untuk melihat apa yang mereka dapatkan.

Ini harus mendapatkan apa yang Anda harapkan:

-- 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)

tiga baris


Untuk membongkar jawaban ini sedikit lebih banyak, langkah pertama dengan ST_Boundarymendapatkan batas setiap poligon, bukan hanya bagian luar. Misalnya, jika ada lubang, ini akan dilacak oleh batas.

The EXCEPTklausul digunakan untuk menghapus geometri dari A yang merupakan bagian dari B, dan baris dari B yang merupakan bagian dari A. Hal ini akan mengurangi jumlah baris yang merupakan bagian dari A saja, dan bagian dari B saja. Misalnya, untuk mendapatkan A_only:

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

Berikut adalah 6 baris A_only, dan 3 baris B_only: A_only B_only

Lanjut, ST_Union(DISTINCT A_only.geom) digunakan untuk menggabungkan garis ke dalam geometri tunggal, biasanya MultiLineString.

ST_Snap digunakan untuk mengambil node dari satu geometri ke geometri lainnya. Misalnya ST_Snap(A, B, tol)akan mengambil geometri A, dan menambahkan lebih banyak node dari geometri B, atau memindahkannya ke geometri B, jika berada dalam toljarak. Mungkin ada beberapa cara untuk menggunakan fungsi-fungsi ini, tetapi idenya adalah untuk mendapatkan koordinat dari setiap geometri yang tepat satu sama lain. Jadi dua geometri setelah memotret terlihat seperti ini:

Bentak Bentak B

Dan untuk menunjukkan perbedaan, Anda dapat memilih untuk menggunakan salah satu ST_SymDifferenceatau ST_Difference. Keduanya menunjukkan hasil yang sama untuk contoh ini.


Jawaban bagus. Saya bertanya-tanya apa yang Anda gunakan untuk memvisualisasikan hasil pertanyaan perantara Anda. Itu tidak langsung terlihat seperti qgis, dan saya mungkin itu sesuatu yang membuat sedikit lebih cepat?
RoperMaps

1
Saya menggunakan JTS Testbuilder untuk melihat dan memproses geometri. Ini adalah mesin geometri terkait dengan GEOS dan Shapely, tetapi memiliki GUI berbasis Java.
Mike T

Apakah ada cara untuk mengabaikan / melompati masalah 'Tidak ada anggukan antara LINESTRING'? Semua poligon masukan tampaknya baik-baik saja (diperiksa dengan pemeriksa geometri QGIS).
eclipsed_by_the_moon

1
'ST_Boundary (ST_SnapToGrid (geom, 0,001))' alih-alih 'ST_Boundary (geom)' memecahkan masalah.
eclipsed_by_the_moon

6

Saya pikir ini sedikit rumit, karena set simpul yang berbeda dari kedua poligon Anda (poligon hijau A, segmen polon B yang berbeda merah). Membandingkan segmen dari kedua poligon memberikan petunjuk mana segmen poligon B yang akan dimodifikasi.

Node poligon A

poli a

Node dari segmen "berbeda" poligon B

seg diff

Sayangnya ini hanya menunjukkan perbedaan dalam struktur segmen, tapi saya harap ini adalah titik awal dan berfungsi seperti ini:

Setelah proses unduh dan unzip, saya mengimpor set data menggunakan PostgrSQL 9.46, PostGIS 2.1 di bawah Debian Linux Jessie dengan perintah.

$ 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

Dengan asumsi bahwa segmen poligon A tidak dalam B dan sebaliknya, saya mencoba untuk membangun perbedaan antara segmen dari kedua set poligon, mengabaikan keanggotaan segmen ke poligon di setiap kelompok (A atau B). Untuk alasan didaktik saya merumuskan hal-hal SQL dalam beberapa tampilan.

Sesuai dengan posting GIS-SE ini , saya menguraikan kedua poligon menjadi tabel segmen segments_adansegments_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;

Tabel segmen poligon 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

Prosedur yang sama diterapkan pada poligon 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;

Tabel segmen poligon 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)
...                        

Saya dapat membangun tampilan tabel perbedaan bernama segments_diff_{a,b}. Perbedaan tersebut diberikan oleh tidak adanya titik awal atau titik akhir yang diurutkan dalam segmen set A dan 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

Dan hal-hal yang saling melengkapi:

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

Kesimpulan: Untuk mendapatkan hasil yang tepat untuk segmen kecil kecil yang Anda tandai dengan panah merah, kedua poligon harus memiliki struktur simpul yang sama dan langkah persimpangan pada tingkat simpul (diperlukan pemasukan simpul dari poligon A dalam B). Persimpangan dapat dilakukan dengan:

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;

Tetapi dengan hasil yang aneh ...

versi memotong


Terima kasih atas usaha Anda. Nah, hasilnya aneh. Saya hanya ingin tahu apakah ST_HausdorffDistance () dapat membantu menjawab pertanyaan: gis.stackexchange.com/questions/180593/...
Lunar Sea

Hm, st_haudorffdistance memberi Anda ukuran kesamaan bukan segmen yang diinginkan (panah merah menunjuk).
huckfinn

Itu hanya sebuah ide, ST_HausdorffDistance dapat digunakan untuk membandingkan geometri dari kedua tabel. Kalau poligonnya tidak sama dengan spasi, saya harus membuat garis. Saya hanya tidak tahu bagaimana melakukan ini.
Laut Bulan

Tampaknya menjadi masalah ketepatan dan topologi ( gis.stackexchange.com/a/182838/26213 dan webhelp.esri.com/arcgisdesktop/9.2/… )
huckfinn

1

Melihat contoh, perubahan menyiratkan bahwa fitur dari tabel baru yang telah diubah akan selalu tumpang tindih fitur dari tabel lama. Karena itu Anda akan selesai dengan

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

Negasi pada sentuhan adalah karena fitur juga tumpang tindih jika perbatasan mereka berbagi lokasi simpul yang sama.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.