SQL Server - Pilih semua poligon yang bersarang dalam poligon yang lebih besar

9

Ini adalah pertanyaan geometri SQL Server yang tampaknya sederhana yang saya pikir akan memiliki solusi out-of-the-box, tapi saya tidak beruntung menemukan satu.

Maksud saya adalah untuk memilih semua rekaman dalam satu tabel yang memiliki poligon yang bersarang (terkandung) dalam poligon yang lebih besar dari tabel lain. Saya memiliki fungsi yang diharapkan STWithindan STContainssebagai solusi yang saya butuhkan, tetapi sayangnya, keduanya hanya mengidentifikasi poligon bagian dalam yang bersarang dalam poligon yang lebih besar, bukan poligon bersarang yang menyentuh batas poligon yang lebih besar. Lihat gambar misalnya.Hasil dari kedua fungsi STWithin dan STContains

Pilihan alternatif yang sesuai dengan kebutuhan saya adalah STIntersection. Masalah dengan fungsi ini, bagaimanapun, adalah hanya mengembalikan kolom geometri! Saya ingin mendapatkan ID rekaman sebagai gantinya. Adakah yang punya saran tentang bagaimana ini bisa dilakukan?

STWithin:

select a.bg10 from
gis.usa_10_block_group a
join gis.usa_10_mkt_definition b
on a.shape.STWithin(b.shape) = 1
where b.mktname = 'Loop'

STContains:

select a.bg10 from
gis.usa_10_block_group a
join gis.usa_10_mkt_definition b
on b.shape.STContains(a.shape) = 1
where b.mktname = 'Loop'

STIntersection:

select a.shape.STIntersection(b.shape)
from gis.usa_10_block_group a
join gis.usa_10_mkt_definition b
on a.shape.STIntersects(b.shape) = 1
where b.mktname = 'Loop'

Edit:

Satu saran adalah untuk menghilangkan STIntersectiondan menggunakan hanya STIntersectssebagai berikut:

STIntersects:

select a.bg10
from gis.usa_10_block_group a
join gis.usa_10_mkt_definition b
on a.shape.STIntersects(b.shape) = 1
where b.mktname = 'Loop'

Masalah dengan pendekatan ini, adalah yang STIntersectsmuncul untuk memilih semua poligon baik di dalam maupun di luar dan menyentuh poligon yang lebih besar, bukan hanya yang ketat di dalamnya. Lihat gambar misalnya.Hasil dari fungsi STIntersects

pengguna1185790
sumber
Anda dapat mencoba melakukan buffer minimal pada poligon yang mengandung Anda dan kemudian gunakan salah satu STContainsatau STWithin. Bukan hack yang bagus, tetapi akan memberi Anda hasil yang Anda inginkan. Pilihan lain adalah melakukan STIntersect dengan perbandingan area titik-temu dan area poligon.
MickyT
Saya mulai mengerjakan perbandingan area tetapi masuk ke lubang kelinci dengan membandingkan geometri yang dikonversi ke area menjadi angka, dll. Dll ...
DPSSpatial

Jawaban:

8

Secara teori pertanyaan yang telah Anda lakukan harus mengembalikan poligon yang Anda katakan belum dikembalikan. Itu membuat saya curiga bahwa Anda mungkin menghadapi masalah kesalahan floating point yang SQL Server miliki dengan itu tipe data spasial. Maka komentar saya tentang buffering poligon terikat dengan jumlah minimal.
Jadi sesuatu seperti yang berikut ini harus mendapatkan hasil yang Anda inginkan.

SELECT a.bg10 
FROM gis.usa_10_block_group a
    JOIN gis.usa_10_mkt_definition b
        ON a.shape.STWithin(b.shape.STBuffer(0.0001)) = 1
WHERE b.mktname = 'Loop'

Berikut adalah contoh cepat perilaku yang diharapkan dari beberapa metode spasial.

SELECT Geometry::STGeomFromText(WKT,0), Description
    , Geometry::STGeomFromText('POLYGON((0 0, 100 0, 100 100, 0 100, 0 0))',0).STIntersects(Geometry::STGeomFromText(WKT,0)) Intersects
    , Geometry::STGeomFromText('POLYGON((0 0, 100 0, 100 100, 0 100, 0 0))',0).STContains(Geometry::STGeomFromText(WKT,0)) Contained
    , Geometry::STGeomFromText('POLYGON((0 0, 100 0, 100 100, 0 100, 0 0))',0).STOverlaps(Geometry::STGeomFromText(WKT,0)) Overlaps
    , Geometry::STGeomFromText('POLYGON((0 0, 100 0, 100 100, 0 100, 0 0))',0).STTouches(Geometry::STGeomFromText(WKT,0)) Touches
FROM (VALUES
    ('POLYGON((0 0, 20 0, 20 20, 0 20, 0 0))'            ,'Interior corner')
    ,('POLYGON((90 90, 100 90, 100 100, 90 100, 90 90))' ,'Interior corner')
    ,('POLYGON((20 20, 40 20, 40 40, 20 40, 20 20))'     ,'Interior')
    ,('POLYGON((50 0, 70 0, 70 20, 50 20, 50 0))'        ,'Interior edge')
    ,('POLYGON((50 80, 70 80, 70 100, 50 100, 50 80))'   ,'Interior edge')
    ,('POLYGON((80 50, 100 50, 100 70, 80 70, 80 50))'   ,'Interior edge')
    ,('POLYGON((90 0, 110 0, 110 20, 90 20, 90 0))'      ,'Overlap')
    ,('POLYGON((100 50, 120 50, 120 70, 100 70, 100 50))','Exterior edge')
    )P(WKT,Description)
UNION ALL 
SELECT Geometry::STGeomFromText('POLYGON((0 0, 100 0, 100 100, 0 100, 0 0))',0),'Bounding Area',null,null,null,null

Hasil

Description     Intersects Contained Overlaps Touches
--------------- ---------- --------- -------- -------
Interior corner 1          1         0        0
Interior corner 1          1         0        0
Interior        1          1         0        0
Interior edge   1          1         0        0
Interior edge   1          1         0        0
Interior edge   1          1         0        0
Overlap         1          0         1        0
Exterior edge   1          0         0        1
Bounding Area   NULL       NULL      NULL     NULL
MickyT
sumber
Ini sangat bagus! Saya harus mengurangi ukuran buffer menjadi 0,001, tetapi konsepnya berhasil. Saya menduga masalahnya adalah bahwa geometri tabel gis.usa_10_mkt_definition tidak berasal dari topologi yang sama dengan gis.usa_10_block_group, menjelaskan alasan mengapa ia menyimpang dari hasil yang diharapkan yang Anda sebutkan. Saya menguji penggunaan STWithin menggunakan dua tabel yang DO berbagi topologi yang sama, dan tidak diperlukan buffer.
user1185790
2

Kueri persimpangan akan terlihat seperti ini (dengan asumsi Anda ingin semua catatan kembali dari 'a'):

select a.* --get all columns from table 'a'
from gis.usa_10_block_group a
join gis.usa_10_mkt_definition b
on a.shape.STIntersects(b.shape) = 1
where b.mktname = 'Loop'

Jika Anda ingin hanya area a yang berpotongan b (mis. Kliping ke b), Anda kemudian menambahkan STIntersection

select a.bg10
, a.STIntersection(b.geom) --clipped geometry from a against b
    from gis.usa_10_block_group a
    join gis.usa_10_mkt_definition b
    on a.shape.STIntersects(b.shape) = 1
    where b.mktname = 'Loop'

Tapi ini tidak membuatmu poligon yang ada dalam b dulu ...

Jenis polygon-in-polygon ini sangat testy dengan batas-batas dan keyakinannya - untuk menjadi 'Dalam', batas-batas a tidak dapat sesuai dengan batas-batas b - sama dengan 'Berisi'.

Dengan definisi ini, berapa banyak poligon Anda dalam yang benar-benar dalam b ...?

Jadi, apakah Anda ingin buffer b sebelum Anda memilih poligon di dalam yang ada? Atau apakah buffer negatif pada a?

Tidak yakin apa jawaban tepatnya di sini ...

DPSSpatial
sumber
Lihat sunting untuk penjelasan lengkap mengapa ini bukan yang saya cari
user1185790
Saya melihat apa yang Anda coba lakukan ... mengerjakan sesuatu sekarang ...
DPSSpatial