Mysql
 sql >> Base de Dados >  >> RDS >> Mysql

Como posso lidar com consultas de sobreposição de polígonos do MySQL?


violino SQL

Criar tabela com coluna de polígono

Observe que, para usar índices espaciais, você não pode usar o InnoDB. Você pode usar a geometria sem índices espaciais, mas o desempenho diminui normalmente.
CREATE TABLE IF NOT EXISTS `spatial` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `poly` geometry NOT NULL,
  UNIQUE KEY `id` (`id`),
  SPATIAL INDEX `poly` (`poly`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8;

Insira 3 quadrados e um triângulo
INSERT INTO `spatial` (`poly`) VALUES (GeomFromText('POLYGON((0 0,10 0,10 10,0 10,0 0))',0));
INSERT INTO `spatial` (`poly`) VALUES (GeomFromText('POLYGON((10 50,50 50,50 10,10 10,10 50))',0));
INSERT INTO `spatial` (`poly`) VALUES (GeomFromText('POLYGON((1 15,5 15,5 11,1 11,1 15))',0));
INSERT INTO `spatial` (`poly`) VALUES (GeomFromText('POLYGON((11 5,15 5,15 1,11 5))',0));

Selecione tudo que cruza o quadrado pequeno no canto inferior esquerdo (quadrado roxo nº 1)
SELECT id,AsText(poly) FROM  `spatial` 
    WHERE 
        ST_Intersects(`poly`,
            GEOMFROMTEXT('POLYGON((0 0,2 0,2 2,0 2,0 0))', 0 )
        )
;

Selecione tudo que cruza o triângulo, desde o canto inferior esquerdo até o canto inferior direito e o canto superior direito) (quadrados nº 1 e nº 2 e triângulo nº 4.)
SELECT id,AsText(poly) FROM  `spatial` 
    WHERE 
        ST_Intersects(`poly`,
            GEOMFROMTEXT('POLYGON((0 0,50 50,50 0,0 0))', 0 )
        )
;

Seleciona tudo no quadrado que está fora da nossa imagem (nada)
SELECT id,AsText(poly) FROM  `spatial` 
    WHERE 
        ST_Intersects(`poly`,
            GEOMFROMTEXT('POLYGON((100 100,200 100,200 200,100 200,100 100))', 0 )
        )
;

Editar nº 1:

Reli a pergunta e acho que você confundiu um pouco as relações espaciais. Se o que você quer é encontrar tudo o que cabe inteiramente dentro de um quadrado (polígono), então você precisa usar Contains/ST_Contains. Consulte funções espaciais na documentação do MySQL para descobrir qual função faz o trabalho para você. Observe a seguinte diferença entre as funções ST/MBR:

Seleciona tudo o que está completamente dentro de um quadrado (#0 abaixo) (quadrados #1, #2, triângulo #4)
SELECT id,AsText(poly) FROM  `spatial` 
    WHERE 
        Contains(
          GEOMFROMTEXT('POLYGON((0 0,20 0,20 20,0 20,0 0))', 0 ),
          `poly`
        )
;

Seleciona tudo o que está completamente dentro de um quadrado (#0 abaixo) e não compartilha bordas (quadrado nº 2, triângulo nº 4)
SELECT id,AsText(poly) FROM  `spatial` 
    WHERE 
        ST_Contains(
          GEOMFROMTEXT('POLYGON((0 0,20 0,20 20,0 20,0 0))', 0 ),
          `poly`
        )
;

Editar nº 2:

Adição muito boa de @StephanB (violino SQL )

Selecione quaisquer objetos sobrepostos
SELECT s1.id,AsText(s1.poly), s2.id, AsText(s2.poly)
FROM  `spatial` s1, `spatial` s2
    WHERE 
        ST_Intersects(s1.poly, s2.poly)
    AND s1.id < s2.id
;

(apenas observe que você deve remover o AND s1.id < s2.id se você estiver trabalhando com CONTAINS , como CONTAINS(a,b) <> CONTAINS(b,a) while Intersects(a,b) = Intersects(b,a) )

Na imagem a seguir (lista não exaustiva):
  • 2 cruza o nº 6.

  • 6 cruza o nº 2

  • 0 cruza nº 1, nº 2, nº 3, nº 4, nº 5

  • 1 cruza #0, #5

  • 0 contém #1, #3, #4 e #5 (#1, #3, #4 e #5 estão dentro de #0)

  • 1 contém #5 (#5 está dentro de #1)

  • 0 st_contém #3, #4 e #5

  • 1 st_contém #5




Edição nº 3:Pesquisando por distância/Trabalhando em (com) círculos

O MySQL não suporta diretamente o círculo como uma geometria, mas você pode usar a função espacial Buffer(geometry,distance) para contornar isso. O que Buffer() faz, está criando um buffer da referida distância em torno da geometria. Se você começar com o ponto geométrico, o buffer é de fato um círculo.

Você pode ver o que o buffer realmente faz chamando apenas:
SELECT ASTEXT(BUFFER(GEOMFROMTEXT('POINT(5 5)'),3))

(o resultado é muito longo, então não vou postá-lo aqui) Na verdade, ele cria um polígono que representa o buffer - neste caso (e no meu MariaDB) o resultado é um polígono de 126 pontos, que se aproxima de um círculo. Com esse polígono, você pode trabalhar como faria com qualquer outro polígono. Portanto, não deve haver penalidade de desempenho.

Então, se você quiser selecionar todos os polígonos que se enquadram em um círculo você pode enxaguar e repetir o exemplo anterior (isso encontrará apenas o quadrado nº 3)
SELECT id,AsText(poly) FROM  `spatial` 
    WHERE 
        ST_Contains(
          Buffer(GEOMFROMTEXT('POINT(6 15)'), 10),
          `poly`
        )
;

Selecione todos os polígonos que se cruzam com um círculo
SELECT id,AsText(poly) FROM  `spatial` 
    WHERE 
        ST_Intersects(
          Buffer(GEOMFROMTEXT('POINT(6 15)'), 10),
          `poly`
        )
;

Ao trabalhar com formas diferentes de retângulos, você deve usar o ST_* funções. Funções sem ST_ use um retângulo delimitador. Assim, o exemplo anterior seleciona o triângulo #4 mesmo que não esteja no círculo.

Como Buffer() cria polígonos muito grandes, definitivamente haverá alguma penalidade de desempenho ao usar o ST_Distance() método. Infelizmente não consigo quantificar. Você terá que fazer algum benchmarking.



Outra maneira de encontrar objetos por distância é usando o ST_Distance() função.

Selecione todos os elementos da tabela e calcule sua distância do ponto POINT(6 15)
SELECT id, AsText(`poly`), 
    ST_Distance(poly, GeomFromText('POINT(6 15)')) 
    FROM `spatial`
;

Você pode usar ST_Distance em WHERE cláusula também.

Selecione todos os elementos cuja distância de POINT(0 0) seja menor ou igual a 10 (seleciona #1, #2 e #3)
SELECT id, AsText(`poly`), 
    ST_Distance(poly, GeomFromText('POINT(6 15)')) 
    FROM `spatial`
    WHERE ST_Distance(poly, GeomFromText('POINT(6 15)')) <= 10
;

Embora a distância seja calculada do ponto mais próximo ao ponto mais próximo. Tornando-o semelhante ao ST_Intersect . Portanto, o exemplo acima selecionará #2 mesmo que não caiba totalmente dentro do círculo.

E sim, o segundo argumento (0) para GeomFromText(text,srid) , não desempenha nenhum papel, você pode ignorá-lo com segurança. Eu peguei de alguma amostra e meio que ficou na minha resposta. Deixei de fora nas minhas edições posteriores.

por falar nisso. phpMyAdmin o suporte para extensão espacial não é perfeito, mas ajuda bastante ver o que está em seu banco de dados. Me ajudou com essas imagens que anexei.