Imagine that you are working on a task where your goal is to create a function that receives a latitude, longitude, and max distance and returns places nearby. Without directions on where to get started, you decide to make research on how to achieve this goal. After a time you realize that you only discovered websites saying to you to use Google Maps API.
But if I tell you that exists a mathematical formula that can help you with this task? That’s what you will learn in this tutorial.
The Haversine Formula
The Haversine Formula is a mathematical formula used to calculate the distance between two points on a sphere, such as the Earth. It takes into account the radius of the sphere as well as the latitude and longitude of the two points and calculates the great-circle distance, which is the shortest distance between the two points along the surface of the sphere.
The Haversine formula is particularly useful for calculating distances between two points on the Earth’s surface, as it takes into account the fact that the Earth is not a perfect sphere, but rather an oblate spheroid. It is commonly used in applications such as GPS navigation, geodesy, and astronomy.
The formula is expressed as follows:
d = 2r * arcsin(sqrt(sin^2((lat2-lat1)/2) + cos(lat1) * cos(lat2) * sin^2((lon2-lon1)/2)))
where:
d
= distance between the two points in units of the sphere’s radius (typically kilometers or miles)
r
= radius of the sphere, in this case Earth (in the same units as d)
lat1
, lat2
= latitude of point 1 and point 2 (in radians)
lon1
, lon2
= longitude of point 1 and point 2 (in radians)
Let’s code
As our goal is to create a function that calculates places nearby, I decided not to show how to initialize the project and keep the focus only on the function.
Furthermore, it could be said that I’ll be using Go programming language for this particular tutorial.
Let’s start by creating the Place struct
, the variables and constants and the main
function:
type Place struct {
Name string `json:"name"`
Latitude float64 `json:"latitude"`
Longitude float64 `json:"longitude"`
}
const EARTH_RADIUS = 6371.0
const MAX_DISTANCE = 50.0
func main() {
myLatitude := -27.600852
myLongitude := -48.505479
places := []Place{
{Name: "Ramiro Ruediger Park", Latitude: -26.9134, Longitude: -49.0855},
{Name: "Corrego Grande Municipal Park", Latitude: -27.5998, Longitude: -48.5114},
{Name: "Corrego Grande Linear Park", Latitude: -27.6028, Longitude: -48.5037},
{Name: "Coqueiros Park", Latitude: -27.6016, Longitude: -48.5745},
}
}
Great, now let’s create the function that will uses the Haversine Formula
func getNearbyPlaces(latitude float64, longitude float64, maxDistance float64, places []Place) []Place {
var nearbyPlaces []Place
for _, place := range places {
lat2 := radians(place.Latitude)
long2 := radians(place.Longitude)
dlat := lat2 - radians(latitude)
dlong := long2 - radians(longitude)
a := math.Pow(math.Sin(dlat/2), 2) + math.Cos(radians(latitude))*math.Cos(lat2)*math.Pow(math.Sin(dlong/2), 2)
c := 2 * math.Atan2(math.Sqrt(a), math.Sqrt(1-a))
distance := EARTH_RADIUS * c
if distance <= maxDistance {
nearbyPlaces = append(nearbyPlaces, place)
}
}
return nearbyPlaces
}
The only missing thing that we have to create is the radians
function. This function will be responsible to convert the latitude and longitude, which is in degrees unit, to radians unit.
func radians(degrees float64) float64 {
return degrees * (math.Pi / 180)
}
And that’s it, we made it 🔥.
Let’s test
I will start by using the latitude -27.600852
and the longitude -48.505479
, which is a point near Corrego Grande Municipal Park
and Corrego Grande Linear Park
. Furthermore, I will set the max distance of 1.5 kilometers, so I expect that the getNearbyPlaces
function returns to me only Corrego Grande Municipal Park
and Corrego Grande Linear Park
as they are the closest places of the current latitude and longitude.
type Place struct {
Name string `json:"name"`
Latitude float64 `json:"latitude"`
Longitude float64 `json:"longitude"`
}
const EARTH_RADIUS = 6371.0
const MAX_DISTANCE = 1.5
func main() {
myLatitude := -27.600852
myLongitude := -48.505479
places := []Place{
{Name: "Ramiro Ruediger Park", Latitude: -26.9134, Longitude: -49.0855},
{Name: "Corrego Grande Municipal Park", Latitude: -27.5998, Longitude: -48.5114},
{Name: "Corrego Grande Linear Park", Latitude: -27.6028, Longitude: -48.5037},
{Name: "Coqueiros Park", Latitude: -27.6016, Longitude: -48.5745},
}
nearbyPlaces := getNearbyPlaces(myLatitude, myLongitude, MAX_DISTANCE, places)
fmt.Println(nearbyPlaces)
// [{Corrego Grande Municipal Park -27.5998 -48.5114} {Corrego Grande Linear Park -27.6028 -48.5037}]
}
Great, it’s working. Let’s increase the max distance to ensure that we receive also Coqueiros Park
.
type Place struct {
Name string `json:"name"`
Latitude float64 `json:"latitude"`
Longitude float64 `json:"longitude"`
}
const EARTH_RADIUS = 6371.0
const MAX_DISTANCE = 15.0
func main() {
myLatitude := -27.600852
myLongitude := -48.505479
places := []Place{
{Name: "Ramiro Ruediger Park", Latitude: -26.9134, Longitude: -49.0855},
{Name: "Corrego Grande Municipal Park", Latitude: -27.5998, Longitude: -48.5114},
{Name: "Corrego Grande Linear Park", Latitude: -27.6028, Longitude: -48.5037},
{Name: "Coqueiros Park", Latitude: -27.6016, Longitude: -48.5745},
}
nearbyPlaces := getNearbyPlaces(myLatitude, myLongitude, MAX_DISTANCE, places)
fmt.Println(nearbyPlaces)
// [{Corrego Grande Municipal Park -27.5998 -48.5114} {Corrego Grande Linear Park -27.6028 -48.5037} {Coqueiros Park -27.6016 -48.5745}]
}
As expected, the function returned three places, Corrego Grande Municipal Park, Corrego Grande Linear Park, and Coqueiros Park. Now is your turn, copy the code, add your places and make some tests.