SQL Server - Виберіть усі полігони, які вкладені у більший багатокутник


9

Це, здавалося б, просте питання геометрії сервера SQL Server, яке, на мою думку, матиме нестандартне рішення, але мені не пощастить знайти його.

Моя мета - вибрати всі записи в одній таблиці, які містять багатокутники, які вкладені (містяться) у більшому багатокутнику з іншої таблиці. Я очікував функції STWithinі STContainsяк потрібні мені рішення, але, на жаль, обидва ідентифікують лише внутрішні багатокутники в межах тих, які вкладені у більший багатокутник, а не ті вкладені багатокутники, які торкаються межі великого багатокутника. Дивіться, наприклад, зображення.Результат від обох функцій STWithin і STContains

Альтернативний варіант, який працював на мої потреби, був STIntersection. Проблема з цією функцією полягає в тому, що вона лише повертає стовпчик геометрії! Я хотів би отримати замість ідентифікатора запису. Хтось має пропозицію, як це можна зробити?

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'

Редагувати:

Одне із пропозицій полягало в тому, щоб пропустити STIntersectionта використовувати виключно STIntersectsнаступне:

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'

Проблема такого підходу полягає в тому, що, STIntersectsздається, вибираються всі полігони всередині або зовні і торкаються великого багатокутника, а не тільки строго всередині. Дивіться, наприклад, зображення.Результат від функції STIntersects


Ви можете спробувати зробити мінімальний буфер на своєму багатокутнику, а потім використати STContainsабо STWithin. Насправді не симпатичний злом, але ви отримаєте бажані результати. Іншим варіантом було б зробити STIntersects зі порівнянням області перетину та площі полігонів.
MickyT

Я почав працювати над порівнянням площі, але потрапив у кролячу нору, порівнюючи геометрію, перетворену на площу, до числа тощо, тощо.
DPSSpatial

Відповіді:


8

Теоретично запити, які ви виконували, повинні повертати багатокутники, про які ви сказали, що не були повернуті. Це змушує мене підозрювати, що у вас можуть виникнути проблеми з помилками з плаваючою комою, які мають у ньому SQL Server з просторовими типами даних. Звідси мій коментар щодо буферування обмежуючого багатокутника мінімальною кількістю.
Отже, щось на зразок наступного має отримати бажані результати.

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'

Ось короткий приклад очікуваної поведінки кількох просторових методів.

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

Результати

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

Це чудово працює! Мені довелося зменшити розмір буфера до 0,001, але концепція спрацювала. Я підозрюю, що проблема полягає в тому, що геометрії таблиці gis.usa_10_mkt_definition не походять з тієї ж топології, що і gis.usa_10_block_group, пояснюючи причину, чому вона відхиляється від очікуваного результату, який ви згадали. Я перевірив використання STWithin за допомогою двох таблиць, які DO мають однакову топологію, і ніякий буфер не потрібен.
користувач1185790

2

Запит перехрестя повинен виглядати приблизно так (припускаючи, що ви хочете, щоб усі записи були повернені з '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'

Якщо ви хочете просто ділянки, що перетинаються b (тобто відсікання a до b), ви додаєте 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'

Але це не отримає тобі багатокутників, які знаходяться в межах b ще ...

Цей тип багатокутника в багатокутнику дуже перевірений з межами та їх збіжністю - для того, щоб бути "Внутрішньою", межі значень не можуть бути суперечними з межами b - те саме стосується "Містить".

За цими визначеннями, скільки ваших многокутників у a є насправді в межах b ...?

Отже, ви хочете передати буфер b перед тим, як вибрати багатокутники в межах, що знаходяться всередині? Або зробити негативний буфер на a?

Не впевнений, яка точна відповідь тут ...


Дивіться правки для повного пояснення, чому це не зовсім те, що я хочу
користувач1185790

Я бачу, що ви намагаєтесь зробити ... над чимось зараз працюю ...
DPSSpatial
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.