SQL Server - Wählen Sie alle Polygone aus, die in einem größeren Polygon verschachtelt sind

9

Dies ist eine scheinbar einfache SQL Server-Geometriefrage, von der ich dachte, dass sie eine sofort einsatzbereite Lösung hätte, aber ich habe kein Glück, eine zu finden.

Meine Absicht ist es, alle Datensätze in einer Tabelle mit Polygonen auszuwählen, die in einem größeren Polygon aus einer anderen Tabelle verschachtelt (enthalten) sind. Ich hatte Funktionen erwartet STWithinund STContainsals die Lösungen, die ich brauchte, aber leider identifizieren beide nur die inneren Polygone innerhalb derjenigen, die im größeren Polygon verschachtelt sind, nicht jene verschachtelten Polygone, die die Grenze des größeren Polygons berühren. Siehe Bild zum Beispiel.Ergebnis der Funktionen STWithin und STContains

Eine alternative Option, die meinen Bedürfnissen entsprach, war STIntersection. Das Problem bei dieser Funktion ist jedoch, dass nur die Geometriespalte zurückgegeben wird! Ich möchte stattdessen die Datensatz-ID erhalten. Hat jemand einen Vorschlag, wie dies getan werden kann?

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'

Bearbeiten:

Ein Vorschlag war, STIntersectionausschließlich STIntersectsFolgendes wegzulassen und zu verwenden :

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'

Das Problem bei diesem Ansatz besteht darin, dass STIntersectsanscheinend alle Polygone entweder innerhalb oder außerhalb ausgewählt werden und das größere Polygon berührt wird, nicht nur die ausschließlich innerhalb. Siehe Bild zum Beispiel.Ergebnis der STIntersects-Funktion

user1185790
quelle
Sie können versuchen, einen minimalen Puffer für Ihr enthaltenes Polygon zu erstellen und dann entweder STContainsoder verwenden STWithin. Kein wirklich guter Hack, aber Sie erhalten die gewünschten Ergebnisse. Die andere Möglichkeit wäre, die STIntersects mit einem Vergleich des Intersection-Bereichs und des Polygons-Bereichs durchzuführen.
MickyT
Ich fing an, an einem Flächenvergleich zu arbeiten, geriet aber in ein Kaninchenloch mit dem Vergleich der Geometrie, die in einen Bereich umgewandelt wurde, in eine Zahl usw. usw.
DPSSpatial

Antworten:

8

Theoretisch sollten die von Ihnen durchgeführten Abfragen die Polygone zurückgeben, von denen Sie sagten, dass sie nicht zurückgegeben wurden. Das lässt mich vermuten, dass Sie möglicherweise auf Gleitkommafehlerprobleme stoßen, die SQL Server mit räumlichen Datentypen hat. Daher mein Kommentar zum Puffern des Begrenzungspolygons mit einem minimalen Betrag.
So etwas wie das Folgende sollte also die gewünschten Ergebnisse erzielen.

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'

Hier ist ein kurzes Beispiel für das erwartete Verhalten einiger räumlicher Methoden.

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

Ergebnisse

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
quelle
Das funktioniert super! Ich musste die Puffergröße auf 0,001 reduzieren, aber das Konzept funktionierte. Ich vermute, dass das Problem darin besteht, dass die Tabellengeometrien von gis.usa_10_mkt_definition nicht aus derselben Topologie wie gis.usa_10_block_group abgeleitet sind, was den Grund dafür erklärt, warum sie vom erwarteten Ergebnis abweichen, das Sie erwähnt haben. Ich habe die Verwendung von STWithin anhand von zwei Tabellen getestet, die dieselbe Topologie verwenden, und es wurde kein Puffer benötigt.
user1185790
2

Die Schnittpunktabfrage sollte folgendermaßen aussehen (vorausgesetzt, Sie möchten alle Datensätze von 'a' zurück):

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'

Wenn Sie nur die Bereiche von a möchten, die b schneiden (dh a bis b abschneiden), fügen Sie den STIntersection hinzu

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'

Aber das bringt dir noch nicht die Polygone, die innerhalb von b sind ...

Diese Art von Polygon-in-Polygon ist sehr gereizt mit den Grenzen und ihrer Übereinstimmung - um "innerhalb" zu sein, können die Grenzen von a nicht mit den Grenzen von b übereinstimmen - dasselbe gilt für "Enthält".

Wie viele Ihrer Polygone in a befinden sich nach diesen Definitionen tatsächlich innerhalb von b ...?

Möchten Sie also b puffern, bevor Sie Polygone in a auswählen, die sich darin befinden? Oder einen negativen Puffer auf a machen?

Ich bin mir nicht sicher, wie die genaue Antwort hier lautet ...

DPSSpatial
quelle
Siehe Bearbeiten für eine vollständige Erklärung, warum dies nicht ganz das ist, wonach ich suche
user1185790
Ich sehe, was Sie versuchen ... jetzt an etwas zu arbeiten ...
DPSSpatial