Menggunakan ST_Difference untuk menghapus fitur yang tumpang tindih?

11

Saya mencoba menggunakan ST_Difference untuk membuat satu set poligon (processing.trimmedparcelsnew) yang tidak mengandung area yang dicakup oleh set poligon lain (test.single_geometry_1) menggunakan PostGis 2.1 (dan Postgres SQL 9.3). Ini pertanyaan saya:

CREATE TABLE processing.trimmedparcelsnew AS
SELECT
    orig.id, ST_Difference(orig.geom, cont.geom) AS difference
FROM 
    test.single_geometry_1 cont,
    test.multi_geometry_1 orig;

Tetapi poligon yang dihasilkan belum dipangkas, sebaliknya mereka tampaknya telah terbelah di mana mereka berpotongan dengan lapisan lainnya. Saya telah mencoba menjalankan pemilihan tanpa meletakkan hasilnya dalam tabel dan semua hal lain yang dapat saya pikirkan, tetapi sepertinya saya tidak dapat menjalankan fungsi ini.

Saya telah melampirkan gambar hasilnya

masukkan deskripsi gambar di sini


Setelah komentar, saya telah mencoba menambahkan klausa WHERE. Saya ingin parsel yang tidak memiliki persimpangan, dan area berpotongan dari parsel lainnya dihapus (layer test.single_geometry mewakili kontaminasi yang ingin saya hapus dari paket saya). Saya mencoba persimpangan tetapi tentu saja saya benar-benar ingin persimpangan bukan jadi saya sekarang mencoba memisahkan. Saya juga telah mencoba menambahkan orig ke meja saya tetapi dokumentasi untuk ST_Difference ( http://postgis.net/docs/ST_Difference.html ) mengatakan ia mengembalikan geometri tepat yang saya butuhkan (geometri yang mewakili bagian geometri A yang tidak berpotongan dengan geometri B), jadi saya bingung mengapa saya ingin poligon asli di meja saya sebagai gantinya. Bagaimanapun, ini adalah kode saya yang dimodifikasi:

CREATE TABLE processing.trimmedparcelsnew AS
SELECT
    orig.id, ST_Difference(orig.geom, cont.geom) AS difference, orig.geom AS geom
FROM 
    test.single_geometry_1 cont,
    test.multi_geometry_1 orig
WHERE ST_Disjoint(orig.geom, cont.geom);

Mengikuti dari jawaban dbaston sekarang saya telah mencoba:

CREATE TABLE processing.parcels_trimmed AS
SELECT id, COALESCE(ST_Difference(geom, (SELECT ST_Union(b.geom) 
                                         FROM test.single_geometry_1 b
                                         WHERE ST_Intersects(a.geom, b.geom)
                                         AND a.id != b.id)), a.geom)
FROM test.multi_geometry_1 a;

Hasil ini hanyalah salinan test.multi_geometry_1. Padahal sekarang perpecahan tidak lagi terjadi.

Saya mencoba versi sebelumnya, tetapi sekali lagi hanya mendapatkan salinan test.multi_geometry_1:

CREATE TABLE processing.parcels_trimmed_no_coalesce AS
SELECT id, COALESCE(ST_Difference(geom, (SELECT ST_Union(b.geom) 
                                         FROM test.single_geometry_1 b
                                         WHERE ST_Intersects(a.geom, b.geom)
                                         AND a.id != b.id)), a.geom)
FROM test.multi_geometry_1 a;

Saya mulai bertanya-tanya apakah ada hal lain yang saya lakukan salah? Pernyataan yang dilanjutkan adalah:

DROP TABLE IF EXISTS processing.parcels_trimmed_no_coalesce;

Dan saya menjalankan kueri dari jendela kueri PostgreSQL SQL dan Openjump.

Pernyataan yang saya gunakan untuk melihat tabel adalah:

SELECT * FROM processing.parcels_trimmed_no_coalesce;

Untuk kepentingan penyederhanaan, sekarang saya mengurangi kueri ini menjadi:

SELECT id, COALESCE(ST_Difference(geom, (SELECT ST_Union(b.geom) 
                                         FROM test.geometriestocutagainst b
                                         WHERE ST_Intersects(a.geom, b.geom)
                                         AND a.id != b.id)), a.geom)
FROM test.geometriestocut a;

Ini masih menghasilkan hanya poligon asli (test.geometriestocut) ketika hasil yang diinginkan adalah yang asli dipangkas terhadap test.geometriestocutagainst.

Pasar
sumber
Anda tidak menentukan WHEREklausa, jadi Anda mungkin memiliki ekspansi polinomial di tabel yang dihasilkan. Ada berapa baris trimmedparcelsnew?
Vince
Jika Anda hanya ingin perbedaan tempat perpotongannya, Anda dapat mencoba menambahkan WHERE ST_Intersects (orig.geom, cont.geom). Kalau tidak, perbedaan dua poligon yang tidak berpotongan, adalah poligon asli.
John Powell
Ada 24 baris dalam parsel terpangkas baru, saya ingin perbedaannya bahkan ketika mereka tidak berpotongan, jadi apakah saya benar bahwa saya perlu menggunakan orig.geom dalam tabel daripada perbedaan?
Mart
Tes disjoint harus menghasilkan ekspansi polinomial - setiap fitur dalam kont muncul muncul sekali untuk setiap fitur aslinya yang tidak tumpang tindih, dan perbedaan tidak akan pernah mengubah geometri input
Vince
Ok terima kasih atas klarifikasi, tetapi saya masih tidak yakin mengapa kode asli tidak berfungsi. Jika ST_Difference (orig.geom, cont.geom) mengembalikan geometri dalam yang tidak bersinggungan dengan b, maka mengapa tabel tersebut berisi geometri perpecahan dan bukan geometri dalam yang tidak berpotongan b.
Mart

Jawaban:

14

Gabung sendiri memungkinkan Anda untuk beroperasi pada hubungan antara pasangan dari dua fitur. Tapi saya tidak berpikir Anda tertarik berpasangan: untuk setiap fitur, Anda ingin beroperasi pada hubungan antara fitur itu dan semua fitur lainnya dalam dataset Anda. Anda dapat melakukannya dengan ekspresi subquery:

CREATE TABLE parcels_trimmed AS
SELECT id, ST_Difference(geom, (SELECT ST_Union(b.geom) 
                                FROM parcels b
                                WHERE ST_Intersects(a.geom, b.geom)
                                  AND a.id != b.id))
FROM parcels a;

Anda mungkin melihat sesuatu yang aneh dalam hasilnya. Paket yang tidak memiliki tumpang tindih sedang dibatalkan seluruhnya! Itu karena ST_Unionagregat pada recordset kosong akan menjadi NULL, dan ST_Difference(geom, NULL)sekarang NULL. Untuk memperlancar hal ini, Anda harus membungkus ST_Differencepanggilan Anda dalam COALESCE:

CREATE TABLE parcels_trimmed AS
SELECT id, COALESCE(ST_Difference(geom, (SELECT ST_Union(b.geom) 
                                         FROM parcels b
                                         WHERE ST_Intersects(a.geom, b.geom)
                                         AND a.id != b.id)), a.geom)
FROM parcels a;

Ini berarti bahwa jika hasilnya ST_Differenceadalah NULL, ekspresi yang digabungkan akan mengevaluasi ke geometri asli.

Kueri di atas akan menghapus area yang tumpang tindih dari domain Anda sepenuhnya. Jika Anda ingin memilih pemenang, Anda bisa melakukannya a.id < b.id, atau kriteria lain, alih-alih a.id != b.id.

dbaston
sumber
Terima kasih atas tanggapannya, sayangnya saya kesulitan membuat ini berfungsi untuk saya, alih-alih hanya berakhir dengan poligon asli (a). Saya akan mengedit pertanyaan saya dengan info lebih lanjut. Terima kasih lagi.
Mart
2

Saya memiliki masalah yang sama seperti Anda. Saya tidak tahu apakah Anda sudah menemukan solusi untuk masalah Anda, tetapi saya memodifikasi jawaban yang diterima di atas dan saya mendapatkan apa yang saya inginkan.

CREATE TABLE parcels_trimmed AS
SELECT id, COALESCE(ST_Difference(geom, (SELECT ST_Collect(b.geom) 
                                         FROM parcels b
                                         WHERE ST_Intersects(a.geom, b.geom)
                                         )), a.geom)
FROM parcels a;
Dag
sumber
1

Saya menggunakan ST_DifferenceAgg () dari AddG PostGIS . Anda harus menggabungkan kedua tabel bersama, memiliki pengidentifikasi unik dan indeks pada kolom geometri. Ini adalah contoh singkat:

WITH overlappingtable AS (
  SELECT 1 id, ST_GeomFromText('POLYGON((0 1, 3 2, 3 0, 0 1), (1.5 1.333, 2 1.333, 2 0.666, 1.5 0.666, 1.5 1.333))') geom
  UNION ALL
  SELECT 2 id, ST_GeomFromText('POLYGON((1 1, 3.8 2, 4 0, 1 1))')
  UNION ALL
  SELECT 3 id, ST_GeomFromText('POLYGON((2 1, 4.6 2, 5 0, 2 1))')
  UNION ALL
  SELECT 4 id, ST_GeomFromText('POLYGON((3 1, 5.4 2, 6 0, 3 1))')
  UNION ALL
  SELECT 5 id, ST_GeomFromText('POLYGON((3 1, 5.4 2, 6 0, 3 1))')
)
SELECT a.id, ST_DifferenceAgg(a.geom, b.geom) geom
FROM overlappingtable a,
     overlappingtable b
WHERE a.id = b.id OR -- Make sure to pass at least once the polygon with itself
      ((ST_Contains(a.geom, b.geom) OR -- Select all the containing, contained and overlapping polygons
        ST_Contains(b.geom, a.geom) OR
        ST_Overlaps(a.geom, b.geom)) AND
       (ST_Area(a.geom) < ST_Area(b.geom) OR -- Make sure bigger polygons are removed from smaller ones
        (ST_Area(a.geom) = ST_Area(b.geom) AND -- If areas are equal, arbitrarily remove one from the other but in a determined order so it's not done twice.
         a.id < b.id)))
GROUP BY a.id
HAVING ST_Area(ST_DifferenceAgg(a.geom, b.geom)) > 0 AND NOT ST_IsEmpty(ST_DifferenceAgg(a.geom, b.geom));

Ini akan menggabungkan bagian yang tumpang tindih dengan poligon tumpang tindih terbesar. Jika Anda ingin menjaga agar bagian yang tumpang tindih terlihat terpisah pada contoh ST_splitAgg ().

Pierre Racine
sumber