When developing location-based applications, calculating the distance between two points on Earth is essential. Haversine formula, a popular mathematical formula used to compute the shortest distance between two points on a sphere, specifically when dealing with geographical coordinates like latitude and longitude.
Let’s explore the Haversine formula, its significance, and how to implement it using JavaScript and MongoDB. Whether you’re a web developer building geolocation features or just curious about the geolocation and distance calculation, this article will get you into the workings of the Haversine formula so you can grasp the concepts and put it to use yourself.
What is the Haversine Formula?
We all are aware of the 3 trigonometric functions sine, cosine and, tangent? Did you know that there are more trigonometric functions that are now obsolete?
The additional trigonometric functions were versine, haversine, coversine, hacoversine, exsecant, and excosecant. All of these can be expressed simply in terms of the more familiar trigonometric functions.
For example:
haversine(θ) = sin²(θ/2)
The Haversine formula calculates the great-circle distance between two points on the Earth’s surface. Given two points specified by their latitude and longitude, the formula helps you determine the shortest distance “as the crow flies” between them.
The “Ha” in “Haversine” stands for “half versed sine” where haversine(θ) = versin(θ)/2.
The Formula
The formula is as follows:
Where:
ϕ1​, ϕ2​ are the latitudes of the two points (in radians).
λ1​, λ2​ are the longitudes of the two points (in radians).
R is the Earth’s radius (mean radius = 6,371 km).
d is the distance between the two points.
The formula gives you the distance between two coordinates in kilometers.
Implementation in JavaScript
Step-by-Step Explanation
Let’s implement the Haversine formula in JavaScript to calculate the distance between two geographical points. I will take example of tow Indian Railways stations I travel quite frequently, NDLS and HWH:
function toRadians(degree) {
return degree * (Math.PI / 180);
}
function haversineDistance(NDLS_LAT, NDLS_LONG, HWH_LAT, HWH_LONG) {
const R = 6371; // Radius of the Earth in km
// Convert latitude and longitude from degrees to radians
const delta_lat = toRadians(HWH_LAT - NDLS_LAT);
const delta_long = toRadians(HWH_LONG - NDLS_LONG);
const NDLS_LAT_RAD = toRadians(NDLS_LAT);
const HWH_LAT_RAD = toRadians(HWH_LAT);
// Apply the Haversine formula
const a = Math.sin(delta_lat / 2) * Math.sin(delta_lat / 2) +
Math.cos(NDLS_LAT_RAD) * Math.cos(HWH_LAT_RAD) *
Math.sin(delta_long / 2) * Math.sin(delta_long / 2);
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
const distance = R * c; // Distance in kilometers
return distance;
}
// Example usage:
const NDLS_LAT = 28.6428962; // NDLS Station
const NDLS_LONG = 77.2165145;
const HWH_LAT = 22.5817578; // HWH Station
const HWH_LONG = 88.3326765;
console.log(`Distance between NDLS and HWH is: ${haversineDistance(NDLS_LAT, NDLS_LONG, HWH_LAT, HWH_LONG)} KM`);
Output:
Distance between NDLS and HWH is: 1301.4940386211492 KM
Explanation
toRadians: Converts degrees to radians.
haversineDistance: This function applies the Haversine formula to calculate the distance between two points in kilometers.
Example: We’ve used NDLS and HWH stations as example coordinates to calculate the distance.
This implementation is lightweight and fast, making it suitable for web applications where performance is critical.
Using the Haversine Formula in MongoDB
MongoDB grants a useful functionality towards geospatial queries with the $geoNear operator or the $geoWithin query, by determining distances required based on geospatial data contained within MongoDB collections.
Also, when it comes to geospatial queries, you can use any one of its implementing features, for example, finding locations close to your destination nearby from the location using MongoDB’s geospatial indexing.
Here’s an example of how you can use MongoDB’s geospatial indexing to find nearby locations.
In MongoDB, locations are generally represented as GeoJSON objects. Here’s an example document:
{
"name": "Central Park",
"location": {
"type": "Point",
"coordinates": [77.2165145, 28.6428962]
}
}
Important Note:
GeoJSON format stores coordinates in [longitude, latitude] order. This is known as GeoJSON convention; hence one should always ensure to provide coordinates in the order of [longitude, latitude] while creating documents and doing geospatial queries in MongoDB.
If the coordinates are provided in reverse order [latitude, longitude], the geospatial queries may not work as they are intended, since MongoDB will interpret the values incorrectly.
Creating a Geospatial Index
To use geospatial queries, you must create an index on the location field. If you do not create the index the queries will not work properly and will throw an error asking you to create the index if it is missing.
db.places.createIndex({ location: "2dsphere" });
Running a Geospatial Query
Once the index is created, you can run a query to find locations within a certain radius using the $geoNear operator. Here’s an example:
db.places.aggregate([
{
$geoNear: {
near: { type: "Point", coordinates: [77.2165145, 28.6428962] }, // NDLS coordinates
distanceField: "dist.calculated",
maxDistance: 5000, // 5 kilometers
spherical: true
}
}
]);
Explanation
- near: Specifies the point from which distances will be calculated.
- distanceField: Field in which MongoDB will store the calculated distance.
- maxDistance: Limits the query to places. Specify the distance in meters.
- spherical: When true, MongoDB uses $nearSphere semantics and calculates distances using spherical geometry.
When false, MongoDB uses $near semantics: spherical geometry for 2dsphere indexes and planar geometry for 2d indexes.
Using $geoWithin with a Polygon
One can use the $geoWithin operator with the $polygon operator to find all locations within a specific polygonal area. Here is an example query that achieves this:
The query retrieves all places within a polygon defined by four coordinate points:
db.places.find({
location: {
$geoWithin: {
$polygon: [
[-73.973, 40.764], // Point 1 (longitude, latitude)
[-73.981, 40.764], // Point 2
[-73.981, 40.770], // Point 3
[-73.973, 40.770], // Point 4
[-73.973, 40.764] // Closing point (same as Point 1 to close the shape)
]
}
}
});
Explanation
- $geoWithin: Restrict the results of the query so that only those whose point lies inside the area defined will be returned.
- $polygon: This type of query defines a polygon by giving coordinates in longitude, latitude order. The last coordinate must be the same as the first coordinate to close the polygon.
This query returns documents for which location field lies within the polygon defined by these coordinates.
Using $geoWithin with a Circle
You can use the $geoWithin operator in conjunction with $centerSphere to find points in a circular area. This is beneficial when you want to find locations within a given radius.
This query retrieves all places within a radius of 2 kilometers about a given center point.
db.places.find({
location: {
$geoWithin: {
$centerSphere: [[-73.935242, 40.730610], 2 / 6371]
}
}
});
Explanation
- $centerSphere: it specifies a spherical area with a center at [longitude, latitude] with a certain radius.
- The radius should, as a natural consequence, be specified in radians (kilometers divided by approximately the mean radius of the Earth, 6,371 km).
- In this case, 2 cardinally divided by 6371 (2/6371) gives a radius of 2 kilometers.
Conclusion
The Haversine formula is a helpful distance calculator of geographical coordinates, greatly useful for location-based applications. Whether implemented in JavaScript or used through MongoDB’s geospatial features, the formula offers an efficient way to compute distances on a spherical Earth.
You now know and can therefore better improve and refine your own applications, so they are going to be livelier and more location-aware.