The Haversine formula is a mathematical equation used to calculate the great-circle distance between two points on the Earth's surface, given their latitude and longitude in decimal degrees. This is particularly useful for applications like yours, where you need to filter vouchers based on their proximity to a user's location.
The Haversine formula calculates the great-circle distance ( d ) between two points on the Earth's surface:
-
Convert Degrees to Radians:
- The formula requires latitude and longitude to be in radians, so you’ll need to convert them from degrees.
-
Calculate the Distance:
- Use the Haversine formula to compute the distance between the two points.
-
Filter Vouchers:
- Compare the calculated distance to the user’s specified radius and include the voucher in the results if it falls within the radius.
Here’s how you can code the Haversine formula into a user-defined filter for your app:
function toRadians(degrees: number): number {
return degrees * (Math.PI / 180);
}
function haversineDistance(lat1: number, lon1: number, lat2: number, lon2: number): number {
const R = 6371; // Earth's radius in kilometers
// Convert latitude and longitude from degrees to radians
const lat1Rad = toRadians(lat1);
const lon1Rad = toRadians(lon1);
const lat2Rad = toRadians(lat2);
const lon2Rad = toRadians(lon2);
// Differences in coordinates
const dLat = lat2Rad - lat1Rad;
const dLon = lon2Rad - lon1Rad;
// Haversine formula
const a =
Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.cos(lat1Rad) * Math.cos(lat2Rad) * Math.sin(dLon / 2) * Math.sin(dLon / 2);
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
const distance = R * c; // Distance in kilometers
return distance;
}
Use the Haversine function to filter vouchers based on the user’s location and radius.
function filterVouchersByLocation(
vouchers: Array<{ metadata: { location: { lat: number; lon: number } } }>,
userLocation: { lat: number; lon: number },
radiusKm: number
): Array<any> {
return vouchers.filter((voucher) => {
const voucherLocation = voucher.metadata.location;
const distance = haversineDistance(
userLocation.lat,
userLocation.lon,
voucherLocation.lat,
voucherLocation.lon
);
return distance <= radiusKm;
});
}
// Example vouchers
const vouchers = [
{
metadata: {
location: { lat: 37.7749, lon: -122.4194 }, // San Francisco
title: "10% Off Coffee",
},
},
{
metadata: {
location: { lat: 34.0522, lon: -118.2437 }, // Los Angeles
title: "Free Pastry with Coffee",
},
},
];
// User's location and filter radius
const userLocation = { lat: 37.7749, lon: -122.4194 }; // San Francisco
const radiusKm = 500; // 500 km
// Filter vouchers
const nearbyVouchers = filterVouchersByLocation(vouchers, userLocation, radiusKm);
console.log("Nearby Vouchers:", nearbyVouchers);
For the example above, the output will include only the vouchers within 500 km of San Francisco:
[
{
"metadata": {
"location": { "lat": 37.7749, "lon": -122.4194 },
"title": "10% Off Coffee"
}
}
]
To integrate this filter with OrbitDB:
-
Query Vouchers:
- Fetch all vouchers from the OrbitDB store.
- Use the
filterVouchersByLocation
function to filter them based on the user’s location and radius.
-
Dynamic Updates:
- Use OrbitDB’s event listeners to re-run the filter whenever new vouchers are added or updated.
// Fetch all vouchers from OrbitDB
const allVouchers = voucherDB.get('');
// Filter vouchers by location
const nearbyVouchers = filterVouchersByLocation(allVouchers, userLocation, radiusKm);
// Listen for updates and re-run the filter
voucherDB.events.on('write', () => {
const updatedVouchers = voucherDB.get('');
const updatedNearbyVouchers = filterVouchersByLocation(updatedVouchers, userLocation, radiusKm);
console.log("Updated Nearby Vouchers:", updatedNearbyVouchers);
});
- Accuracy: The Haversine formula provides precise distance calculations.
- Flexibility: Users can dynamically adjust their location and radius filters.
- Efficiency: Only vouchers within the specified radius are included in the results.
-
Performance:
- Calculating distances for a large number of vouchers can be computationally expensive.
- Mitigation: Use indexing or precompute distances for frequently queried locations.
-
Dynamic Locations:
- If the user’s location changes frequently, the filter must be re-run often.
- Mitigation: Use throttling or debouncing to limit the frequency of recalculations.
By integrating the Haversine formula into your app, you can provide users with a powerful and accurate way to filter vouchers based on their proximity to a given location. This enhances the user experience and ensures that only relevant vouchers are displayed.