Como calcular a distância entre dois pontos em uma esfera usando programação

Nota editorial: Este post fazia parte de uma versão antiga do meu blog e foi republicado aqui como forma de preservar o histórico do que já escrevi. Mantive a essência do conteúdo original, com ajustes pontuais apenas quando necessário.

Utilizando geometria esférica, escrevi um exemplo de código em PHP de como calcular a distância entre dois pontos em uma esfera como o nosso planeta Terra. Neste exemplo utilizei o PHP, mas poderia ser escrito em qualquer outra linguagem de programação.

Sabemos que o raio da Terra, em média, tem aproximadamente 6371.005076123 km’s, e isso será importante para usarmos no nosso exemplo. Logo abaixo explico um pouco sobre a fórmula utilizada.

O que é a Fórmula de Haversine?

A fórmula de Haversine é o método mais popular e numericamente estável para calcular a distância entre dois pontos na superfície de uma esfera (como a Terra) a partir de suas coordenadas de latitude e longitude.

Ela usa o conceito de haversine (half-versine), que é uma função trigonométrica definida como:

haversin(θ)=sin2(θ2)\text{haversin}(\theta) = \sin^2\left(\frac{\theta}{2}\right)

Fórmula Matemática Completa

Dadas duas coordenadas:

  • Ponto 1: (lat1,lon1)(lat_1, lon_1)
  • Ponto 2: (lat2,lon2)(lat_2, lon_2)

A distância é calculada assim:

a=sin2(Δlat2)+cos(lat1)cos(lat2)sin2(Δlon2)a = \sin^2\left(\frac{\Delta lat}{2}\right) + \cos(lat_1) \cdot \cos(lat_2) \cdot \sin^2\left(\frac{\Delta lon}{2}\right)

c=2\atan2(a,1a)c = 2 \cdot \atan2\left(\sqrt{a}, \sqrt{1 – a}\right)

d=Rcd = R \cdot c

Onde:

  • Δlat=lat2lat1\Delta lat = lat_2 – lat_1
  • Δlon=lon2lon1\Delta lon = lon_2 – lon_1
  • RR = raio da esfera (Terra ≈ 6371 km)

Código em PHP

Eu criei uma classe em PHP para um exemplo mais próximo a uma implementação real:

<?php

/**
 * Cálculo entre dois pontos GPS
 */
class GeoDistance
{
	const RADIUS = 6371.005076123; // raio da Terra em km

	private float $latitudeFrom;

	private float $longitudeFrom;

	private float $latitudeTo;

	private float $longitudeTo;
	
	/**
	 * Gps constructor
	 * 
	 * Exemplos:
	 * 	new GeoDistance(-12.9927866, -38.4544938, -25.4356954, -49.2832619)
	 */
	public function __construct(float $latitudeFrom, float $longitudeFrom, float $latitudeTo, float $longitudeTo)
	{
		$this->setFrom($latitudeFrom, $longitudeFrom);
		$this->setTo($latitudeTo, $longitudeTo);
	}
	
	/**
	 * Calcula a distância entre dois pontos GPS.
	 */
	public function calculate($returnIn = 'km', $decimalDigits = 2): float
	{
		$p1Lat = $this->getLatitudeFrom() * pi() / 180.0;
		$p1Lng = $this->getLongitudeFrom() * pi() / 180.0;
		$p2Lat = $this->getLatitudeTo() * pi() / 180.0;
		$p2Lng = $this->getLongitudeTo() * pi() / 180.0;
	
		$dLat = $p2Lat - $p1Lat;
		$dLng = $p2Lng - $p1Lng;
	
		$angles = sin($dLat / 2) * sin($dLat / 2) + cos($p1Lat) * cos($p2Lat) * sin($dLng / 2) * sin($dLng / 2);
		$arcCos = 2 * atan2(sqrt($angles), sqrt(1 - $angles));

		$distance = self::RADIUS * $arcCos;

		if ($returnIn == 'm') {
			$distance = $distance * 1000;
		}
	
		return round($distance, $decimalDigits);
	}
	
	/**
	 * Define a latitude e longitude de origem
	 */
	public function setFrom(float $latitude, float $longitude): self
	{
		$this->latitudeFrom = $latitude;
		$this->longitudeFrom = $longitude;

		return $this;
	}

	/**
	 * Define a latitude e longitude de destino
	 */
	public function setTo(float $latitude, float $longitude): self
	{
		$this->latitudeTo = $latitude;
		$this->longitudeTo = $longitude;
		
		return $this;
	}
	
	public function getLatitudeFrom(): float
	{
		return $this->latitudeFrom;
	}
	
	public function getLongitudeFrom(): float
	{
		return $this->longitudeFrom;
	}
	
	public function getLatitudeTo(): float
	{
		return $this->latitudeTo;
	}
	
	public function getLongitudeTo(): float
	{
		return $this->longitudeTo;
	}
}

Exemplo de uso:

<?php

$gps = new GeoDistance(-23.061599, -47.224603, -23.615940, -46.622572);
echo $gps->calculate() . ' km';

Espero ter ajudado em algo!

Caso você queira o mesmo exemplo escrito em alguma outra linguagem ou tenha alguma dúvida, deixe nos comentários.

Abraços!

Leave a Reply

Your email address will not be published. Required fields are marked *