SQL Server - Sélectionnez tous les polygones imbriqués dans un polygone plus grand

9

Il s'agit d'une question de géométrie SQL Server apparemment simple qui, selon moi, aurait une solution prête à l'emploi, mais je n'ai pas de chance d'en trouver une.

Mon intention est de sélectionner tous les enregistrements d'une table contenant des polygones imbriqués (contenus) dans un polygone plus grand d'une autre table. J'avais prévu des fonctions STWithinet STContainscomme étant les solutions dont j'avais besoin, mais malheureusement, les deux n'identifient que les polygones intérieurs à l'intérieur de ceux imbriqués dans le plus grand polygone, pas les polygones imbriqués qui touchent la limite du plus grand polygone. Voir l'image par exemple.Résultat des fonctions STWithin et STContains

Une option alternative qui a fonctionné à mes besoins était STIntersection. Le problème avec cette fonction, cependant, est qu'elle ne renvoie que la colonne de géométrie! Je voudrais plutôt obtenir l'ID d'enregistrement. Quelqu'un at-il une suggestion sur la façon dont cela peut être fait?

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'

Éditer:

Une suggestion était d'omettre STIntersectionet d'utiliser uniquement STIntersectscomme suit:

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'

Le problème avec cette approche, c'est qu'il STIntersectssemble sélectionner tous les polygones à l'intérieur ou à l'extérieur et toucher le plus grand polygone, pas seulement ceux strictement à l'intérieur. Voir l'image par exemple.Résultat de la fonction STIntersects

user1185790
la source
Vous pouvez essayer de faire un tampon minimal sur votre polygone contenant, puis utiliser soit STContainsou STWithin. Pas vraiment un bon hack, mais vous obtiendrez les résultats que vous souhaitez. L'autre option serait de faire les STIntersect avec une comparaison de la zone d'intersection et de la zone des polygones.
MickyT
J'ai commencé à travailler sur une comparaison de zone mais je suis entré dans un trou de lapin en comparant la géométrie convertie en zone en nombre, etc. etc ...
DPSSpatial

Réponses:

8

En théorie, les requêtes que vous avez effectuées doivent renvoyer les polygones que vous avez dit ne pas avoir été renvoyés. Cela me fait penser que vous pourriez rencontrer des problèmes d'erreur en virgule flottante que SQL Server a avec ses types de données spatiales. D'où mon commentaire sur la mise en mémoire tampon du polygone englobant avec une quantité minimale.
Donc, quelque chose comme ce qui suit devrait obtenir les résultats souhaités.

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'

Voici un exemple rapide du comportement attendu de quelques-unes des méthodes spatiales.

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

Résultats

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
la source
Cela fonctionne très bien! J'ai dû réduire la taille du tampon à 0,001, mais le concept a fonctionné. Je soupçonne que le problème est que les géométries de la table gis.usa_10_mkt_definition ne sont pas dérivées de la même topologie que gis.usa_10_block_group, expliquant la raison pour laquelle elle s'écarte du résultat attendu que vous avez mentionné. J'ai testé l'utilisation de STWithin en utilisant deux tables qui partagent la même topologie, et aucun tampon n'était nécessaire.
user1185790
2

La requête d'intersection devrait ressembler à ceci (en supposant que vous vouliez que tous les enregistrements reviennent de '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'

Si vous voulez juste les zones de a qui coupent b (c.-à-d. Coupure de a à b), vous ajoutez alors la 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'

Mais cela ne vous donne pas encore les polygones qui se trouvent dans b ...

Ce type de polygone en polygone est très tatillon avec les limites et leur coïncidence - pour être `` à l'intérieur '', les limites de a ne peuvent pas être confondues avec les limites de b - il en va de même pour `` contient ''.

D'après ces définitions, combien de vos polygones dans a sont réellement dans b ...?

Voulez-vous donc mettre en mémoire tampon b avant de sélectionner des polygones dans a qui se trouvent à l'intérieur? Ou faire un tampon négatif sur un?

Je ne sais pas quelle est la réponse exacte ici ...

DPSSpatial
la source
Voir la modification pour une explication complète pourquoi ce n'est pas tout à fait ce que je recherche
user1185790
Je vois ce que vous essayez de faire ... travailler sur quelque chose maintenant ...
DPSSpatial