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

user1185790
источник
Вы можете попробовать создать минимальный буфер на содержащем полигоне и затем использовать либо 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
MickyT
источник
Это прекрасно работает! Мне пришлось уменьшить размер буфера до 0,001, но концепция сработала. Я подозреваю, что проблема в том, что геометрия таблицы gis.usa_10_mkt_definition не основана на той же топологии, что и gis.usa_10_block_group, и объясняет причину, по которой она отклоняется от ожидаемого результата, который вы упомянули. Я протестировал использование STW с использованием двух таблиц, которые имеют одинаковую топологию, и буфер не был необходим.
user1185790 26.02.16
2

Запрос на пересечение должен выглядеть следующим образом (при условии, что вы хотите вернуть все записи из «а»):

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'

Если вы хотите, чтобы только области a пересекались с 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 - то же самое относится и к «содержит».

По этим определениям, сколько ваших полигонов в a на самом деле находятся в пределах b ...?

Итак, вы хотите буферизовать b, прежде чем выбрать полигоны в a, которые находятся внутри? Или сделать отрицательный буфер на?

Не уверен, что точный ответ здесь ...

DPSSpatial
источник
Смотрите редактирование для полного объяснения, почему это не совсем то, что я ищу
user1185790
Я вижу, что вы пытаетесь сделать ... работаете над чем-то сейчас ...
DPSSpatial