Calculando distâncias com Geolocalização pelo MySQL



Geolocalização está no topo das tecnologias atuais pois junto com os dispositivos móveis vieram também inúmeras oportunidades de aplicativos que utilizam a localização do usuário para uma infinidade de coisas, de dizer se um amigo está por perto a indicar restaurantes ou serviços próximos a onde o usuário está.

Sendo assim é imprescindível aprendermos a trabalhar com geolocalização.

A Geolocalização é fundamentalmente um serviço fornecido pelo "Front End" ou falando de modo mais geral do lado cliente, pois é o browser ou o aparelho móvel quem disponibiliza esta informação. Apesar disto existem  coisas relacionadas com Geolocalização que sim podem (e devem) ser feitas do lado servidor (Server Side).

Se você está criando alguma aplicação relacionada a encontrar locais próximos, por exemplo, caso não esteja usando um web service de terceiros, no mínimo um banco de dados com as coordenadas geográficas de vários locais você provavelmente deverá possuir.

Vamos supor que você tenha uma tabela em seu banco de dados assim:

Latitude   | Longitude | Nome do Restaurante | Endereço
-45.7855   | -46.3590   | Quinta da Boa Mesa   | Rua Avaré 55
-44.5697   | -47.6885   | São Salvador                | Avenida Moreira Filho 10

A questão é: como encontrar o restaurante  mais próximo de onde o usuário está?

A resposta óbvia será medir a distância entre onde você está e os restaurantes de sua lista, porém fazer isto não é simplesmente fazer um cálculo de triângulos, como aprendemos na escola, este cálculo é bem mais complicado pois devemos lembrar que estamos na superfície de uma ESFERA (sim, a terra é redonda).

Sendo assim o cálculo trigonométrico deve levar em consideração esta curvatura da Terra. Em resumo as distâncias serão sempre um pouco maiores do que se fosse feito um cálculo trigonométrico em uma superfície plana.

Para efetuar estes cálculos temos que trabalhar com quatro coordenadas: latitude e longitude de onde o usuário está e latitude e longitude de sua lista de restaurantes.

Podemos adotar várias abordagens para fazer este cálculo. Podemos fazer o cálculo diretamente no lado cliente (pegando os valores do banco e efetuando os cálculos pelo javascript, Java ou Objective C), podemos fazer do lado Servidor pelo PHP, Ruby, JSP, ASPX, etc. ou do lado Server mas diretamente no Banco de dados.

A abordagem que quero mostrar é aquela utilizando justamente o Banco de Dados pois, além de o considerar mais rápido e limpo, o DB nos permite fazer coisas muito interessantes e de maneira mais prática como ordenar as distância de forma muito tranquila, além de, no meu ponto de vista, fazer mais sentido tratar estas distâncias dentro de um banco de dados.

Para isso vamos criar uma função dentro do DB que efetuará os cálculos de distância e os trará como um campo extra.

Segue a função completa e logo abaixo dela explico os pontos importantes:

 delimiter //  
 CREATE FUNCTION Geo(lat_ini FLOAT(18,10), lon_ini FLOAT(18,10),lat_fim FLOAT(18,10), lon_fim FLOAT(18,10))  
 RETURNS FLOAT(18,10)  
 NOT DETERMINISTIC  
 BEGIN  
 DECLARE Theta FLOAT(18,10);  
 DECLARE Dist FLOAT(18,10);  
 DECLARE Miles FLOAT(18,10);  
 DECLARE kilometers FLOAT(18,10);  
 SET Theta = lon_ini - lon_fim;  
 SET Dist = SIN(RADIANS(lat_ini)) * SIN(RADIANS(lat_fim)) + COS(RADIANS(lat_ini)) * COS(RADIANS(lat_fim)) * COS(RADIANS(Theta));  
 SET Dist = ACOS(Dist);  
 SET Dist = DEGREES(Dist);  
 SET Miles = Dist * 60 * 1.1515;  
 SET kilometers = Miles * 1.609344;  
 RETURN kilometers;  
 END;//  
 delimiter ;  


  • O comando delimiter // muda o delimitador padrão de ; para //. Isto nos permite escrever a função utilizando o separador de comando correto. Ao concluir a função retornamos o delimitador padrão para ;.
  • Ao criar a função 'CREATE FUNCTION' damos um nome a ela ('Geo') e declaramos os parâmetros de entrada, inclusive os tipos e espaço em memória.
  • 'lat_ini' e 'lon_ini' são as coordenadas de origem. No caso de uma aplicação em que utilizamos as coordenadas do usuário para calcular distâncias, 'lat_ini' e 'lon_ini' são as variáveis que receberão estes dados e 'lat_fim' e 'lon_fim' são as coordenadas dos estabelecimentos ou locais até onde você deseja fazer o cálculo. Neste contexto 'lat_ini' e 'lon_ini' serão fixos de um dado usuário e 'lat_fim' e 'lon_fim' poderão ser coordenadas presentes no banco de dados para vários estabelecimentos.
  • 'RETURNS FLOAT' define o tipo que será retornado em 'RETURN kilometers'
  • Os DECLARE também instanciam as variáveis assim como na declaração da função, a diferença é que estas variáveis são internas.
  • SET atribui o valor dos cálculos a uma variável. No caso de nossa função os cinco primeiros comandos SET fazem o cálculo de geolocalização (em milhas) e o ultimo SET converte milhas para quilômetros.

Aplicar esta função é muito simples. Basta incluí-la em seu SELECT.


 SELECT Geo(-34.50696,-33.438673,Latitude, Longitude) AS Distancia, Restaurante FROM tabela  

Como resposta vc deve ter algo como:

Distancia   | Restaurante
9.6585      | Quinta da Boa Mesa
5.9656      | São Salvador

É isso ai!
Se esta dica foi útil a você compartilhe o link nas redes sociais.

Comentários

  1. Ola amigo, esta dando erro na criação da funcao, nesta linha:

    DECLARE Theta FLOAT(18,10);

    ResponderExcluir
  2. Este comentário foi removido por um administrador do blog.

    ResponderExcluir
  3. PARABÉNS PELA FUNÇÃO, SIMPLISMENTE SALVOU MINHA VIDA

    MUITO OBRIGADO

    ResponderExcluir
  4. porque só uma loja apareceu os dados corretos, e as outras com os dados da distancia "0.0000000000", fiz o teste com 4 lojas, e só uma fica com a distancia aparecendo aparece "4.3991804123".... Obrigado desde já

    ResponderExcluir
    Respostas
    1. Estou com o mesmo problema. Aparece "0.000000" nos registros

      Excluir
    2. Resolvi o problema simplesmente trocando todos os FLOAt por DOUBLE

      Excluir
    3. Vlw, trocar por Double resolveu aqui também =D

      Excluir
  5. Olá Sasuke e obrigado pelo comentário.
    De uma revisada nos dados de latitude e longitude de seu banco. Verifique também se a latitude e longitude da localização de referência está correta.

    Abraço

    ResponderExcluir
    Respostas
    1. Estou com o mesmo problema do sasuke, mas não tem como os dados estarem errados, peguei no google maps

      Excluir
    2. Resolvi o problema simplesmente trocando todos os FLOAt por DOUBLE

      Excluir
    3. Excelente! Obrigado pela contribuição!

      Excluir

Postar um comentário

Postagens mais visitadas deste blog

PHP - Utilizando proxy e CURL para acessar servidores ou sites

MySQL - Cálculo de período de tempo entre duas datas com TIMESTAMPDIFF

MySQL - Completando quantidades fixas de caracteres com as funções LPAD() e RPAD()