Melakukan drilldown / overlay poligon di PostGIS?

8

Saya menghadapi tantangan dengan PostGIS yang sepertinya tidak bisa saya lindungi. Saya tahu saya bisa menyelesaikan ini menggunakan bahasa pemrograman (dan itu adalah rencana cadangan saya), tapi saya benar-benar ingin menyelesaikan ini di PostGIS. Saya sudah mencoba mencari, tetapi tidak dapat menemukan jawaban yang cocok dengan masalah saya, ini mungkin karena saya tidak yakin tentang istilah pencarian saya, jadi tolong permisi dan arahkan saya ke arah yang benar karena memang ada jawaban.

Masalah saya adalah ini:

  • Saya punya meja dengan Polygons / MultiPolygons campuran
  • Setiap poligon (multi) memiliki atribut yang memeringkatnya (prioritas)
  • Setiap poligon juga memiliki nilai yang ingin saya ketahui
  • Saya memiliki area pencarian (poligon)
  • Untuk area kueri saya, saya ingin menemukan area yang dicakup oleh setiap nilai poligon

Contoh:

Katakanlah saya memiliki tiga poligon yang digambarkan merah, hijau, dan nila di sini: Contoh

Dan bahwa, persegi panjang biru yang lebih kecil adalah kueri poligon saya

Selain itu, atributnya adalah

geom   | rank | value
-------|------|----  
red    |  3   | 0.1
green  |  1   | 0.2
indigo |  2   | 0.2

Apa yang saya inginkan adalah memilih geometri ini, sehingga peringkat tertinggi (hijau) mengisi semua area yang ia dapat (yaitu persimpangan antara geom kueri dan geom itu), lalu tertinggi berikutnya (nila) mengisi persimpangan antara geom kueri dan geom MINUS yang sudah dibahas) dll.

Sesuatu seperti ini: masukkan deskripsi gambar di sini

Saya menemukan pertanyaan ini: Menggunakan ST_Difference untuk menghapus fitur yang tumpang tindih? tetapi tampaknya tidak melakukan apa yang saya inginkan.

Saya sendiri dapat mengetahui cara menghitung area dan semacamnya, jadi pertanyaan yang memberi saya tiga geometri seperti yang digambarkan dalam gambar kedua baik-baik saja!

Info tambahan: - Ini bukan tabel besar (~ 2000 baris) - mungkin ada nol atau beberapa tumpang tindih (bukan hanya tiga) - mungkin tidak ada poligon di area kueri saya (atau hanya sebagian saja) - i ' m menjalankan postgis 2.3 di postgres 9.6.6

Solusi fallback saya adalah melakukan kueri seperti ini:

SELECT 
ST_Intersection(geom, querygeom) as intersection, rank, value
FROM mytable
WHERE ST_Intersects(geom, querygeom)
ORDER by rank asc

Dan secara iteratif "potong" bagian geometri dalam kode. Tetapi, seperti yang saya katakan, saya benar-benar ingin melakukan ini di PostGIS

atlefren
sumber
2
tidak dapat memberi Anda jawaban sekarang, tetapi jika Anda bersedia mencobanya sendiri: Anda sedang mencari WITH RECURSIVE ...CTE ( dokumen dan tutorial umum )
geozelot
1
oh dan periksa ini
geozelot
Terima kasih! Saya akan coba ini besok jika tidak ada orang lain yang merasa terdorong untuk memberikan solusi lengkap.
atlefren

Jawaban:

7

Saya pikir ini berhasil.

Ini adalah fungsi windowing, mendapatkan perbedaan antara persimpangan setiap persimpangan geometri dengan kotak kueri dan penyatuan geometri sebelumnya.

Koalesensi diperlukan karena penyatuan geometri sebelumnya untuk geometri pertama adalah nol yang memberikan hasil nol, bukan apa yang diinginkan.

WITH a(geom, rank, val) AS
(
    VALUES
    ('POLYGON((1 1, 1 5, 5 5, 5 1, 1 1))'::geometry,3,0.1),
    ('POLYGON((2 3, 2 8, 4 8, 5 3,2 3))'::geometry,1,0.2),
    ('POLYGON((4 4, 4 6, 6 6, 6 4,4 4))'::geometry,2,0.2)
)
,q AS
(
    SELECT 'POLYGON((3 3, 3 4.5, 12 4.5,12 3, 3 3))'::geometry geom
) 
SELECT 
  ST_Difference(
    ST_Intersection(a.geom, q.geom), 
    COALESCE(ST_Union(a.geom) 
           OVER (ORDER BY rank ROWS BETWEEN UNBOUNDED PRECEDING and 1 PRECEDING),
       'POLYGON EMPTY'::geometry)
  ) geom 
FROM a,q
WHERE ST_Intersects(a.geom, q.geom);

Saya tidak yakin bagaimana kinerjanya. Tetapi karena ST_Union dan ST_Intersection ditandai tidak berubah, mungkin tidak terlalu buruk.

Nicklas Avén
sumber
Ini bekerja seperti pesona! Hanya harus membungkus kueri Anda dalam kueri lain untuk menghapus
koleksi
5

Sedikit pendekatan yang berbeda untuk ini. Ada peringatan bahwa saya tidak tahu bagaimana hal itu akan meningkatkan kinerja, tetapi pada tabel yang diindeks seharusnya ok. Kerjanya hampir sama dengan permintaan Nicklas (sedikit lebih lambat?), Tetapi pengukuran pada sampel sekecil itu penuh.

Itu terlihat jauh lebih jelek daripada permintaan Nicklas, tetapi ia menghindari rekursi dalam kueri.

WITH 
    a(geom, rank, val) AS
    (
        VALUES
        ('POLYGON((1 1, 1 5, 5 5, 5 1, 1 1))'::geometry,3,0.1),
        ('POLYGON((2 3, 2 8, 4 8, 5 3, 2 3))'::geometry,1,0.2),
        ('POLYGON((4 4, 4 6, 6 6, 6 4, 4 4))'::geometry,2,0.2)
    ),
    polygonized AS (
        -- get the basic building block polygons
        SELECT (ST_Dump(         -- 5. Dump out the polygons
            ST_Polygonize(line)) -- 4. Polygonise the linework
            ).geom AS mypoly
        FROM (
            SELECT 
                ST_Node(                  -- 3. Node lines on intersection
                    ST_Union(             -- 2. Union them for noding
                        ST_Boundary(geom) -- 1. Change to linestrings
                    ) 
                ) 
                AS line
            FROM a
        ) line
    ),
    overlayed AS ( 
        -- Overlay with original polygons and get minimum rank.
        -- Union and dissolve interior boundaries for like ranks.
        SELECT (ST_Dump(ST_UnaryUnion(geom))).geom, rank 
        FROM ( 
            -- union up the polygons by rank, unary union doesn't count as an aggregate function?
            SELECT ST_Union(mypoly) geom, rank 
            FROM ( 
                -- get the minimum rank for each of the polygons
                SELECT p.mypoly, min(rank) rank
                FROM polygonized p 
                    INNER JOIN a ON ST_Contains(a.geom,ST_PointOnSurface(p.mypoly))
                GROUP BY p.mypoly
                ) g
            GROUP BY rank
            ) r
    )
-- get the intersection of the query area with the overlayed polygons
SELECT ST_Intersection(o.geom,'POLYGON((3 3, 3 4.5, 12 4.5,12 3, 3 3))'::Geometry), rank
FROM overlayed o
WHERE ST_Intersects(o.geom,'POLYGON((3 3, 3 4.5, 12 4.5,12 3, 3 3))'::Geometry) and
    -- Intersection can do funky things
    GeometryType(ST_Intersection(o.geom,'POLYGON((3 3, 3 4.5, 12 4.5,12 3, 3 3))'::Geometry)) like '%POLYGON';
MickyT
sumber
1

Karena saya mengoceh tentang saya WITH RECURSIVEakan menambahkan jawaban cepat dan kotor menggunakannya.

Ini berkinerja sebaik solusi @ NicklasAvén pada tiga Poligon, tidak bisa menguji ketika ditingkatkan.
Karena kedua solusi berdiri, yang satu ini memiliki satu manfaat kecil di atas yang lain: jika, misalnya, Poligon dengan peringkat = 2 terkandung oleh peringkat = 1 , ...WHERE GeometryType = 'POLYGON'filter yang keluar sementara jika tidak akan ada GEOMETRYCOLLECTION EMPTY(saya mengubah geometri). dari masing-masing Poligon dalam solusi saya sesuai untuk memberikan contoh; ini juga berlaku untuk kasus lain ketika tidak ada persimpangan dengan perbedaan yang ditemukan). Ini mudah dimasukkan dalam solusi lain, dan mungkin bahkan tidak menjadi perhatian.

WITH RECURSIVE
    a(geom, rank, val) AS (
        VALUES
           ('POLYGON((1 1, 1 5, 5 5, 5 1, 1 1))'::geometry,3,0.1),
           ('POLYGON((2 3, 2 8, 4 8, 5 3,2 3))'::geometry,1,0.2),
           ('POLYGON((2.1 3.1, 2.1 7.9, 3.9 7.9, 4.9 3.1,2.1 3.1))'::geometry,2,0.2)
    ),
    q AS (
        SELECT 'POLYGON((3 3, 3 4.5, 12 4.5,12 3, 3 3))'::geometry geom
    ),
    src AS (
        SELECT ROW_NUMBER() OVER(ORDER BY rank) AS rn,
               ST_Intersection(q.geom, a.geom) AS geom,
               rank,
               val
        FROM a
        JOIN q
           ON ST_Intersects(a.geom, q.geom)
    ),
    res AS (
        SELECT s.geom AS its,
               ST_Difference(q.geom, s.geom) AS dif,
               s.rank,
               s.val,
               2 AS icr
        FROM src AS s,
             q
        WHERE s.rn = 1
        UNION ALL
        SELECT ST_Intersection(s.geom, r.dif) AS its,
               ST_Difference(r.dif, s.geom) AS dif,
               s.rank,
               s.val,
               icr + 1 AS icr
        FROM src AS s,
             res AS r
        WHERE s.rank = r.icr
    )

SELECT its AS geom,
       rank,
       val
FROM res
WHERE GeometryType(its) = 'POLYGON'
geozelot
sumber