SQL Server - Wybierz wszystkie wielokąty zagnieżdżone w większym wielokącie

9

To pozornie proste pytanie dotyczące geometrii programu SQL Server, które - jak myślałem - miałoby gotowe rozwiązanie, ale nie mam szczęścia go znaleźć.

Moim zamiarem jest wybranie wszystkich rekordów w jednej tabeli, które mają wielokąty zagnieżdżone (zawarte) w większym wielokącie z innej tabeli. Oczekiwałem funkcji STWithini STContainsjako rozwiązań, których potrzebowałem, ale niestety oba identyfikują tylko wewnętrzne wielokąty w obrębie tych zagnieżdżonych w większym wielokącie, a nie te zagnieżdżone wielokąty, które dotykają granicy większego wielokąta. Zobacz na przykład obraz.Wynik zarówno funkcji STWithin, jak i STContains

Alternatywą, która spełniła moje potrzeby, była STIntersection. Problem z tą funkcją polega jednak na tym, że zwraca tylko kolumnę geometrii! Zamiast tego chciałbym uzyskać identyfikator rekordu. Czy ktoś ma sugestie, jak to zrobić?

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'

Edytować:

Jedną z sugestii było pominięcie STIntersectioni użycie wyłącznie STIntersectsw następujący sposób:

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'

Problem z tym podejściem polega na tym, że STIntersectswydaje się, że zaznacza się wszystkie wielokąty wewnątrz lub na zewnątrz i dotyka większego wielokąta, nie tylko te ściśle wewnątrz. Zobacz na przykład obraz.Wynik funkcji STIntersects

użytkownik1185790
źródło
Można spróbować zrobić minimalny bufor na dysku zawierającym wielokąta, a następnie użyj jednej STContainslub STWithin. Niezbyt fajny hack, ale zapewni Ci oczekiwane rezultaty. Inną opcją byłoby wykonanie STIntersects z porównaniem obszaru przecięcia i obszaru wielokątów.
MickyT,
Zacząłem pracować nad porównaniem powierzchni, ale dostałem się do króliczej dziury z porównaniem geometrii zamienionej na powierzchnię na liczbę itp. Itd.
DPSSpatial

Odpowiedzi:

8

Teoretycznie zadane przez Ciebie zapytania powinny zwrócić wielokąty, o których mówiłeś, że nie zostały zwrócone. To sprawia, że ​​podejrzewam, że możesz napotkać problemy z błędami zmiennoprzecinkowymi, które występują w SQL Server z typami danych przestrzennych. Stąd mój komentarz na temat buforowania ograniczającego wielokąta minimalną ilością.
Więc coś takiego jak poniżej powinno uzyskać pożądane wyniki.

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'

Oto szybki przykład oczekiwanego zachowania kilku metod przestrzennych.

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

Wyniki

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
źródło
To działa świetnie! Musiałem zmniejszyć rozmiar bufora do 0,001, ale koncepcja zadziałała. Podejrzewam, że problemem jest to, że geometrie tabeli gis.usa_10_mkt_definition nie pochodzą z tej samej topologii co gis.usa_10_block_group, co wyjaśnia powód, dla którego odbiega ona od oczekiwanego wyniku, o którym wspomniałeś. Przetestowałem użycie STWithin przy użyciu dwóch tabel, które mają tę samą topologię, i nie był potrzebny bufor.
user1185790,
2

Zapytanie o przecięcie powinno wyglądać tak (zakładając, że chcesz odzyskać wszystkie rekordy z „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'

Jeśli chcesz tylko te obszary, które przecinają b (tj. Przycinanie a do b), dodajesz przecięcie STI

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'

Ale to nie daje ci wielokątów, które są jeszcze w obrębie b ...

Ten typ wielokąta w wielokącie jest bardzo wrażliwy na granice i ich zbieżność - aby być „wewnątrz”, granice a nie mogą być zbieżne z granicami b - to samo dotyczy „Contains”.

Według tych definicji, ile twoich wielokątów w a faktycznie znajduje się w obrębie b ...?

Czy chcesz buforować b, zanim wybierzesz wielokąty znajdujące się w środku? Czy zrobić bufor ujemny na?

Nie jestem pewien, jaka jest dokładna odpowiedź tutaj ...

DPSSpatial
źródło
Zobacz edycję, aby uzyskać pełne wyjaśnienie, dlaczego nie jest to dokładnie to, czego szukam
1185790,
Widzę, co próbujesz zrobić ... pracując nad czymś teraz ...
DPSSpatial