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.