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 DD (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 DD (em km) em graus de latitude e longitude. Onde 1 grau de latitude é aproximadamente 111 km 111 \text{ km } e 1 grau de longitude é aproximadamente 111 km ×cos(ϕcentral)111 \text{ km } \times cos(\phi_{central}).
Δϕ=D111Δλ=D111×cos(ϕ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.
ϕmin=ϕcentralΔϕϕmax=ϕcentral+Δϕλmin=λcentralΔλλmax=λ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.
ϕminϕpontoϕmaxλminλpontoλmax\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:
Δϕ=101110.0900901Δλ=10111×cos(25.5513318)10111×0.9136630.0986032ϕmin=25.55133180.090090125.6414219ϕmax=25.5513318+0.090090125.4612417λmin=54.45782730.098603254.5564305λmax=54.4578273+0.098603254.359224125.641421925.551331825.4612417Falso54.556430554.457827354.3592241Verdadeiro\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 RR (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.
Δϕponto=ϕpontoϕcentralΔλponto=λpontoλcentralϕcentralrad=ϕcentral×π180\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 (RR) desejado.
Δϕponto=ϕpontoϕcentralΔλponto=λpontoλcentralDista^ncia111×(Δϕponto)2+(Δλponto×cos(ϕcentralrad))2\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:
Δϕponto=25.5513318(25.4447825)=0.1065493Δλponto=54.4578273(54.4091065)=0.0487208ϕcentralrad=25.5513318×π1800.445955Dista^ncia111×(0.1065493)2+(0.0487208×cos(0.445955))2111×0.0113528+(0.0487208×0.902199)2111×0.0132849212.79389 km \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×arccos(sin(ϕ1)×sin(ϕ2)+cos(ϕ1)×cos(ϕ2)×cos(Δλ))d = r \times arccos(sin(\phi_1) \times sin(\phi_2) + cos(\phi_1) \times cos(\phi_2) \times cos(\Delta \lambda)) \\ \\

Onde:

  • dd é a distância entre os dois pontos.
  • rr é o raio da Terra (aproximadamente 6.371 km)
  • ϕ1\phi_1 e ϕ2\phi_2 são as latitudes dos dois pontos (em radianos)
  • λ1\lambda_1 e λ2\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:
ϕ1=25.4447825×π1800.444095 rad λ1=54.4091065×π1800.949618 rad ϕ2=25.5513318×π1800.445955 rad λ2=54.4578273×π1800.950468 rad \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:
Δλ=0.950468(0.949618)0.00085 rad \Delta \lambda = -0.950468-(-0.949618) \approx -0.00085 \text{ rad }
  • E por último, aplicar a Lei dos Cossenos Esféricos:
d6371×arccos(sin(0.444095)×sin(0.445955)+cos(0.444095)×cos(0.445955)×cos(0.00085))d6371×arccos(0.429641×0.431319+0.902999×0.902199×0.999999)d6371×arccos(0.185312588294+0.81468538804)d6371×0.00201179d12.8172\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×arcsin(sin2(Δϕ2)+cos(ϕ1)×cos(ϕ2)×sin2(Δλ2))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:

  • dd é a distância entre os dois pontos.
  • rr é o raio da Terra (aproximadamente 6.371 km)
  • ϕ1\phi_1 e ϕ2\phi_2 são as latitudes dos dois pontos (em radianos)
  • λ1\lambda_1 e λ2\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:
ϕ1=25.4447825×π1800.444095 rad λ1=54.4091065×π1800.949618 rad ϕ2=25.5513318×π1800.445955 rad λ2=54.4578273×π1800.950468 rad \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:
Δϕ=0.445955(0.444095)0.00186 rad Δλ=0.950468(0.949618)0.00085 rad \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:
d=2×6371×arcsin(sin2(0.001862)+cos(0.444095)×cos(0.445955)×sin2(0.000852))d=2×6371×arcsin(0.903000×1.630894×107)d=2×6371×arcsin(1.011831×106)d=2×6371×arcsin(0.00100589)d=2×6371×0.00100589d=12.8171\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