Pergunta

Eu criei a seguinte tabela MySQL para armazenar coordenadas de latitude/longitude junto com um nome para cada ponto:

CREATE TABLE `points` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(128) NOT NULL,
  `location` point NOT NULL,
  PRIMARY KEY (`id`),
  SPATIAL KEY `location` (`location`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=1;

Estou tentando consultar:

  • Todos os pontos dentro de um n raio de milha de um determinado ponto;
  • a distância de cada ponto retornado do ponto dado

Todos os exemplos que encontrei, referem -se ao uso de um retângulo delimitado mínimo (MBR) em vez de um raio. A tabela contém aproximadamente 1 milhão de pontos; portanto, essa necessidade precisa ser o mais eficiente possível.

Foi útil?

Solução 2

Obrigado a ambos pelas suas respostas.

Eu finalmente encontrei a solução em http://www.movable-type.co.uk/scripts/latlong-db.html.

Outras dicas

Para MySQL 5.7+

Dado que temos a seguinte tabela simples,

create table example (
  id bigint not null auto_increment primary key,
  lnglat point not null
);

create spatial index example_lnglat 
    on example (lnglat);

Com os seguintes dados simples,

insert into example (lnglat) 
values
(point(-2.990435, 53.409246)),
(point(-2.990037, 53.409471)),
(point(-2.989736, 53.409676)),
(point(-2.989554, 53.409797)),
(point(-2.989350, 53.409906)),
(point(-2.989178, 53.410085)),
(point(-2.988739, 53.410309)),
(point(-2.985874, 53.412656)),
(point(-2.758019, 53.635928));

Você obteria os pontos dentro de um determinado intervalo de outro ponto (Nota: temos que pesquisar dentro de um polígono) com a seguinte combinação de funções ST:

set @px = -2.990497;
set @py = 53.410943;
set @range = 150; -- meters
set @rangeKm = @range / 1000;

set @search_area = st_makeEnvelope (
  point((@px + @rangeKm / 111), (@py + @rangeKm / 111)),
  point((@px - @rangeKm / 111), (@py - @rangeKm / 111))
);

select id, 
       st_x(lnglat) lng, 
       st_y(lnglat) lat,
       st_distance_sphere(point(@px, @py), lnglat) as distance
  from example
 where st_contains(@search_area, lnglat);

Você deve ver algo assim como resultado:

3   -2.989736   53.409676   149.64084252776277
4   -2.989554   53.409797   141.93232714661812
5   -2.98935    53.409906   138.11516275402533
6   -2.989178   53.410085   129.40289289527473

Para referência na distância, se removermos a restrição, o resultado do ponto de teste se parece com o seguinte:

1   -2.990435   53.409246   188.7421181457556
2   -2.990037   53.409471   166.49406509160158
3   -2.989736   53.409676   149.64084252776277
4   -2.989554   53.409797   141.93232714661812
5   -2.98935    53.409906   138.11516275402533
6   -2.989178   53.410085   129.40289289527473
7   -2.988739   53.410309   136.1875540498202
8   -2.985874   53.412656   360.78532732013963
9   -2.758019   53.635928   29360.27797292756

Nota 1: O campo é chamado de lnglat, pois essa é a ordem correta se você pensa em pontos como (x, y) e também é a ordem que a maioria das funções (como ponto) aceita o parâmetro

Nota 2: Você não pode realmente tirar proveito dos índices espaciais se você usar círculos; Observe também que o campo Point pode ser definido para aceitar índices nulos, mas espaciais, não podem indexá-lo se for anulável (todos os campos no índice precisam ser não nulos).

Nota 3: ST_Buffer é considerado (pela documentação) como ruim para este caso de uso

Nota 4: As funções acima (em particular st_distance_sphere) são documentadas como rápidas, mas não necessariamente super precisas; Se seus dados forem super sensíveis a que adicione um pouco de espaço de manobra à pesquisa e faça algum ajuste fino ao conjunto de resultados

O raio não é eficientemente indexível. Você deve usar o retângulo delimitado para obter rapidamente os pontos que provavelmente está procurando e filtrar os pontos fora do raio.

Eu fiz isso por um ponto dentro do círculo com raio

SELECT 
    *
FROM 
    `locator`
WHERE
    SQRT(POW(X(`center`) - 49.843317 , 2) + POW(Y(`center`) - 24.026642, 2)) * 100 < `radius`

Detalhes e mais uma amostra de consulta aqui http://dexxtr.com/post/83498801191/how-to-determine-point-inside-circle-using-mysql, espero que isto ajude

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top