Executando verificação de segurança...
4

Calculando a distância entre dois pontos através da latitude e longitude

https://www.lucasmantuan.com.br
https://github.com/lucasmantuan
https://www.linkedin.com/in/lucasmantuan

O objetivo deste tutorial é calcular a distância entre determinados pontos e a centroide de diversas cidades, e com isso estabelecer a quais cidades aqueles pontos pertencem.

Podemos utilizar algumas abordagens diferentes para o cálculo da distância através da latitude e longitude. Entretanto, como estes cálculos geralmente envolvem uma alta complexidade, faremos um primeiro filtro, com uma precisão mais baixa e baseado em uma região menor, para então fazer o cálculo preciso da distância levando em consideração a curvatura da Terra.

Bounding Box

Neste método criamos uma área retangular de extensão D (em km) ao redor do ponto central, depois podemos verificar se as coordenadas do ponto estão dentro dessa área.

  • Primeiro convertemos a distância D (em km) em graus de latitude e longitude. Onde 1 grau de latitude é aproximadamente 111 \text{ km } e 1 grau de longitude é aproximadamente 111 \text{ km } \times cos(\phi_{central}).
\begin{gather} \Delta_\phi = \frac{D}{111} \\ \\ \Delta_\lambda = \frac{D}{111 \times cos(\phi_{central})} \end{gather}
  • Em seguida usamos as variações calculadas para definir os limites da caixa em torno do ponto central.
\begin{gather} \phi_{min} = \phi_{central} - \Delta_\phi \\ \phi_{max} = \phi_{central} + \Delta_\phi \\ \\ \lambda_{min} = \lambda_{central} - \Delta_\lambda \\ \lambda_{max} = \lambda_{central} + \Delta_\lambda \end{gather}
  • Finalizamos verificando se o ponto está dentro desta caixa.
\begin{gather} \phi_{min} \le \phi_{ponto} \le \phi_{max} \\ \lambda_{min} \le \lambda_{ponto} \le \lambda_{max} \end{gather}
  • Por exemplo, se desejarmos calcular se a cidade de Santa Terezinha do Itaipu (-25.4447825, -54.4091065) está a 10 km de distância da cidade de Foz do Iguaçu (-25.5513318, -54.4578273) fazemos os seguintes cálculos:
\begin{gather} \Delta_\phi = \frac{10}{111} \approx 0.0900901 \\ \\ \Delta_\lambda = \frac{10}{111 \times cos(−25.5513318)} \approx \frac{10}{111 \times 0.913663} \approx 0.0986032 \\ \\ \phi_{min} = −25.5513318 - 0.0900901 \approx -25.6414219 \\ \phi_{max} = −25.5513318 + 0.0900901 \approx -25.4612417 \\ \\ \lambda_{min} = -54.4578273 - 0.0986032 \approx -54.5564305 \\ \lambda_{max} = -54.4578273 + 0.0986032 \approx -54.3592241 \\ \\ -25.6414219 \le -25.5513318 \le -25.4612417 \to Falso \\ -54.5564305 \le -54.4578273 \le -54.3592241 \to Verdadeiro \end{gather}
  • Abaixo o exemplo de como implementar as fórmulas no Python:
import math

D = 10
lat_central = -25.5513318
lon_central = -54.4578273
lat_ponto = -25.4447825
lon_ponto = -54.4091065

delta_phi = D / 111
delta_lambda = D / (111 * math.cos(math.radians(lat_central)))

phi_min = lat_central - delta_phi
phi_max = lat_central + delta_phi
lambda_min = lon_central - delta_lambda
lambda_max = lon_central + delta_lambda

dentro_da_caixa = (phi_min <= lat_ponto <= phi_max) and (lambda_min <= lon_ponto <= lambda_max)

print(dentro_da_caixa)

Bounding Circle

Este método consiste em determinar se um ponto está dentro de um círculo de raio R (em km) ao redor de um ponto central. Podemos calcular a distância aproximada entre o ponto central e o ponto em questão usando uma fórmula simplificada (uma adaptação do Teorema de Pitágoras), adequada para pequenas distância.

  • Primeiro calculamos a diferença em latitude e longitude em relação ao ponto central e em seguida convertemos a latitude central para radianos, o que nos ajuda a criar um círculo com o raio desejado ao redor do ponto central.
\begin{gather} \Delta_{\phi_{ponto}} = \phi_{ponto} - \phi_{central} \\ \\ \Delta_{\lambda_{ponto}} = \lambda_{ponto} - \lambda_{central} \\ \\ \phi_{central_{rad}}= \phi_{central} \times \frac{\pi}{180} \end{gather}
  • Finalizamos calculando a distância aproximada entre o ponto central e o ponto e verificando se a distância é menor ou igual ao raio (R) desejado.
\begin{gather} \Delta_{\phi_{ponto}} = \phi_{ponto} - \phi_{central} \\ \\ \Delta_{\lambda_{ponto}} = \lambda_{ponto} - \lambda_{central} \\ \\ Distância \approx 111 \times \sqrt{(\Delta_{\phi_{ponto}})^2 + (\Delta_{\lambda_{ponto}} \times cos(\phi_{central_{rad}}))^2} \end{gather}
  • Por exemplo, se desejarmos calcular se a cidade de Santa Terezinha do Itaipu (-25.4447825, -54.4091065) está a 10 km de distância da cidade de Foz do Iguaçu (-25.5513318, -54.4578273) fazemos os seguintes cálculos:
\begin{gather} \Delta_{\phi_{ponto}} = -25.5513318 - (-25.4447825) = -0.1065493 \\ \\ \Delta_{\lambda_{ponto}} = -54.4578273 - (-54.4091065) = -0.0487208 \\ \\ \phi_{central_{rad}}= -25.5513318 \times \frac{\pi}{180} \approx -0.445955 \\ \\ Distância \approx 111 \times \sqrt{(-0.1065493)^2 + (-0.0487208 \times cos(-0.445955))^2} \\ \\ \approx 111 \times \sqrt{0.0113528 + (-0.0487208 \times 0.902199)^2} \\ \\ \approx 111 \times \sqrt{0.01328492}\\ \\ \approx 12.79389 \text{ km } \end{gather}
  • Abaixo o exemplo de como implementar as fórmulas no Python.
import math

R = 10
lat_central = -25.5513318
lon_central = -54.4578273
lat_ponto = -25.4447825
lon_ponto = -54.4091065

delta_phi_ponto = lat_ponto - lat_central
delta_lambda_ponto = lon_ponto - lon_central

phi_central_rad = lat_central * math.pi / 180

distancia = 111 * math.sqrt((delta_phi_ponto)**2 + (delta_lambda_ponto * math.cos(phi_central_rad))**2)

dentro_do_circulo = distancia <= R

print(dentro_do_circulo)

Em seguida, podemos utilizar algum outro método para um cálculo mais preciso da distância entre os pontos. Dentre as abordagens existentes temos a fórmula de Haversine, ideal para distâncias curtas e moderadas, a fórmula da Lei dos Cossenos Esférico, com precisão similar a de Haversine, mas, mais simples e direta de implementar, o algoritmo de Great Circle Distance, um pouco mais precisa que as anteriores para distâncias longas e a fórmula de Vincenty, sendo precisa em qualquer distância.

Como em nosso exemplo o cálculo exige uma precisão baixa, de menos de 1000 km, utilizaremos a fórmula da Lei dos Cossenos Esféricos e de Haversine

Lei dos Cossenos Esféricos

Esta fórmula é uma generalização da Lei dos Cossenos aplicada a esferas, utilizada para calcular distância entre dois pontos na superfície de uma esfera. Sua fórmula é:

d = r \times arccos(sin(\phi_1) \times sin(\phi_2) + cos(\phi_1) \times cos(\phi_2) \times cos(\Delta \lambda)) \\ \\

Onde:

  • d é a distância entre os dois pontos.
  • r é o raio da Terra (aproximadamente 6.371 km)
  • \phi_1 e \phi_2 são as latitudes dos dois pontos (em radianos)
  • \lambda_1 e \lambda_2 são as longitudes dos dois pontos (em radianos)
  • \Delta \lambda é a diferença entre as longitudes dos dois pontos

Vamos continuar em nosso exemplo fazendo o cálculo entre a cidade de Santa Terezinha do Itaipu (-25.4447825, -54.4091065) e Foz do Iguaçu (-25.5513318, -54.4578273):

  • Começamos convertendo as coordenadas em radianos:
\begin{gather} \phi_1 = -25.4447825 \times \frac{\pi}{180} \approx -0.444095 \text{ rad } \\ \lambda_1 = -54.4091065 \times \frac{\pi}{180} \approx -0.949618 \text{ rad } \\ \\ \phi_2 = -25.5513318 \times \frac{\pi}{180} \approx -0.445955 \text{ rad } \\ \lambda_2 = -54.4578273 \times \frac{\pi}{180} \approx -0.950468 \text{ rad } \\ \\ \end{gather}
  • Para depois calcular a diferença de longitude:
\Delta \lambda = -0.950468-(-0.949618) \approx -0.00085 \text{ rad }
  • E por último, aplicar a Lei dos Cossenos Esféricos:
\begin{gather} d \approx 6371 \times arccos(sin(-0.444095) \times sin(-0.445955) \\ + cos(-0.444095) \times cos(-0.445955) \times cos(-0.00085)) \\ \\ d \approx 6371 \times arccos(-0.429641 \times -0.431319 + 0.902999 \times 0.902199 \times 0.999999) \\ \\ d \approx 6371 \times arccos(0.185312588294 + 0.81468538804) \\ \\ d \approx 6371 \times 0.00201179 \\ \\ d \approx 12.8172 \end{gather}
  • Abaixo o exemplo de como implementar as fórmulas no Python:
import math

r = 6371
lat1 = -25.4447825
lon1 = -54.4091065
lat2 = -25.5513318
lon2 = -54.4578273

phi1 = math.radians(lat1)
lambda1 = math.radians(lon1)
phi2 = math.radians(lat2)
lambda2 = math.radians(lon2)

delta_lambda = lambda2 - lambda1

d = r * math.acos(math.sin(phi1) * math.sin(phi2) + math.cos(phi1) * math.cos(phi2) * math.cos(delta_lambda))

print(d)

Haversine

Esta fórmula considera a Terra como uma esfera e fornece a distância ao longo da superfície, levando em consideração a sua curvatura. Sua fórmula é:

d = 2r \times arcsin(\sqrt{sin^2(\frac{\Delta \phi}{2}) + cos(\phi_1) \times cos(\phi_2) \times sin^2(\frac{\Delta \lambda}{2})}) \\ \\

Onde:

  • d é a distância entre os dois pontos.
  • r é o raio da Terra (aproximadamente 6.371 km)
  • \phi_1 e \phi_2 são as latitudes dos dois pontos (em radianos)
  • \lambda_1 e \lambda_2 são as longitudes dos dois pontos (em radianos)
  • \Delta \phi é a diferença entre as latitudes dos dois pontos
  • \Delta \lambda é a diferença entre as longitudes dos dois pontos

Vamos, também neste exemplo fazer o cálculo entre a cidade de Santa Terezinha do Itaipu (-25.4447825, -54.4091065) e Foz do Iguaçu (-25.5513318, -54.4578273):

  • Começamos convertendo as coordenadas em radianos:
\begin{gather} \phi_1 = -25.4447825 \times \frac{\pi}{180} \approx -0.444095 \text{ rad } \\ \lambda_1 = -54.4091065 \times \frac{\pi}{180} \approx -0.949618 \text{ rad } \\ \\ \phi_2 = -25.5513318 \times \frac{\pi}{180} \approx -0.445955 \text{ rad } \\ \lambda_2 = -54.4578273 \times \frac{\pi}{180} \approx -0.950468 \text{ rad } \\ \\ \end{gather}
  • Para depois calculamos a diferença de latitude e longitude:
\begin{gather} \Delta \phi = -0.445955 -(-0.444095) \approx -0.00186 \text{ rad } \\ \Delta \lambda = -0.950468-(-0.949618) \approx -0.00085 \text{ rad } \\ \end{gather}
  • E por último, aplicar a fórmula de Haversine:
\begin{gather} d = 2 \times 6371 \times arcsin(\sqrt{sin^2(\frac{-0.00186}{2}) + cos(-0.444095) \times cos(-0.445955) \times sin^2(\frac{-0.00085}{2})}) \\ \\ d = 2 \times 6371 \times arcsin(\sqrt{0.903000 \times 1.630894 \times 10^{-7}}) \\ \\ d = 2 \times 6371 \times arcsin(\sqrt{1.011831 \times 10^{-6}}) \\ \\ d = 2 \times 6371 \times arcsin(0.00100589) \\ \\ d = 2 \times 6371 \times 0.00100589 \\ \\ d = 12.8171 \\ \\ \end{gather}
  • Abaixo o exemplo de como implementar os cálculos em Python.
import math

r = 6371
lat1 = -25.4447825
lon1 = -54.4091065
lat2 = -25.5513318
lon2 = -54.4578273

phi1 = math.radians(lat1)
lambda1 = math.radians(lon1)
phi2 = math.radians(lat2)
lambda2 = math.radians(lon2)

delta_phi = phi2 - phi1
delta_lambda = lambda2 - lambda1

a = math.sin(delta_phi / 2)**2 + math.cos(phi1) * math.cos(phi2) * math.sin(delta_lambda / 2)**2
c = 2 * math.asin(math.sqrt(a))
d = r * c

print(d)

Well-known Text e Well-known Binary

Para encerrar, é importante falar um pouco sobre 2 formatos utilizados para representar dados geométricos. O Well-known Text (WKT) que é uma notação textual que descreve formas geométricas usando coordenadas sendo amplamente utilizado em bancos de dados geoespaciais e em sistemas de informação geográficas (GIS) e o Well-known Binary (WKB) que é a representação binária equivalente de dados geométricos, utilizado para armazenamento e transferência de forma mais compacta e eficiente. Ambos os formatos são definidos pelo Open Geospatial Consortium (OGC) e são compatíveis com diversas ferramentas e bibliotecas geoespaciais.

Carregando publicação patrocinada...
0