SQL Server - Selecione todos os polígonos aninhados em um polígono maior


9

Esta é uma questão de geometria do SQL Server aparentemente simples que eu pensei que teria uma solução pronta para uso, mas não estou tendo sorte em encontrar uma.

Minha intenção é selecionar todos os registros em uma tabela que tenham polígonos aninhados (contidos) em um polígono maior de outra tabela. Eu esperava funções STWithine, STContainscomo sendo as soluções necessárias, mas, infelizmente, ambas identificam apenas os polígonos internos dentro daqueles aninhados no polígono maior, não aqueles polígonos aninhados que estão tocando o limite do polígono maior. Veja a imagem por exemplo.Resultado das funções STWithin e STContains

Uma opção alternativa que atendeu às minhas necessidades foi STIntersection. O problema com esta função, no entanto, é que ela retorna apenas a coluna geométrica! Gostaria de obter o ID do registro. Alguém tem alguma sugestão de como isso pode ser feito?

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'

Editar:

Uma sugestão era omitir STIntersectione usar apenas STIntersectso seguinte:

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'

O problema dessa abordagem é que STIntersectsparece selecionar todos os polígonos dentro ou fora e tocando o polígono maior, não apenas aqueles estritamente internos. Veja a imagem por exemplo.Resultado da função STIntersects


Você pode tentar fazer um buffer mínimo no polígono que o contém e usar um STContainsou outro STWithin. Não é realmente um truque agradável, mas você obtém os resultados desejados. A outra opção seria fazer as conexões STI com uma comparação da área de interseção e da área de polígonos.
MickyT

Eu comecei a trabalhar em uma área de comparar, mas entrou em um buraco de coelho com comparando a geometria convertida em área para um número, etc. etc ...
DPSSpatial

Respostas:


8

Em teoria, as consultas que você fez devem retornar os polígonos que você disse que não foram retornados. Isso me faz suspeitar que você possa estar enfrentando problemas de erro de ponto flutuante que o SQL Server possui com esses tipos de dados espaciais. Daí o meu comentário sobre o buffer do polígono delimitador com uma quantidade mínima.
Portanto, algo como o seguinte deve obter os resultados desejados.

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'

Aqui está um exemplo rápido do comportamento esperado de alguns dos métodos espaciais.

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

Resultados

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

Isso funciona muito bem! Eu tive que reduzir o tamanho do buffer para 0,001, mas o conceito funcionou. Suspeito que o problema é que as geometrias da tabela gis.usa_10_mkt_definition não são derivadas da mesma topologia que gis.usa_10_block_group, explicando o motivo pelo qual ele se desvia do resultado esperado que você mencionou. Testei o uso do STWithin usando duas tabelas que compartilham a mesma topologia e nenhum buffer foi necessário.
user1185790

2

A consulta de interseção deve ficar assim (supondo que você queira que todos os registros retornem 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'

Se você deseja apenas as áreas de a que cruzam b (ou seja, recortar a para b), adicione a seção 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'

Mas isso não dá a você os polígonos que estão dentro de b ainda ...

Esse tipo de polígono-em-polígono é muito irritante com os limites e sua conincidência - para serem 'Internos', os limites de a não podem ser compatíveis com os limites de b - o mesmo vale para 'Contém'.

Por essas definições, quantos de seus polígonos em a estão realmente dentro de b ...?

Então, você deseja armazenar em buffer b antes de selecionar polígonos em um que esteja dentro? Ou fazer um buffer negativo em um?

Não tenho certeza qual é a resposta exata aqui ...


Ver edição para explicação completa por que isso não é bem o que eu estou atrás
user1185790

Eu vejo o que você está tentando fazer ... trabalhando em algo agora ...
DPSSpatial
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.