mobileapp / src /utils /geoUtils.ts
Antaram Dev Bot
feat: complete ANTARAM.ORG ride-sharing app frontend
5c876be
/**
* Geographic utility functions.
*
* All calculations use the Haversine formula and operate on
* WGS-84 (standard GPS) coordinates.
*/
const EARTH_RADIUS_KM = 6371;
/**
* Calculate the straight-line distance between two points using the
* Haversine formula.
*
* @returns Distance in kilometers
*/
export function calculateDistance(
lat1: number,
lng1: number,
lat2: number,
lng2: number,
): number {
const toRad = (deg: number) => (deg * Math.PI) / 180;
const dLat = toRad(lat2 - lat1);
const dLng = toRad(lng2 - lng1);
const a =
Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.cos(toRad(lat1)) *
Math.cos(toRad(lat2)) *
Math.sin(dLng / 2) *
Math.sin(dLng / 2);
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
return EARTH_RADIUS_KM * c;
}
/**
* Calculate the initial bearing (forward azimuth) from point 1 to point 2.
*
* @returns Bearing in degrees (0–360), where 0/360 = North, 90 = East, etc.
*/
export function calculateBearing(
lat1: number,
lng1: number,
lat2: number,
lng2: number,
): number {
const toRad = (deg: number) => (deg * Math.PI) / 180;
const toDeg = (rad: number) => (rad * 180) / Math.PI;
const dLng = toRad(lng2 - lng1);
const y = Math.sin(dLng) * Math.cos(toRad(lat2));
const x =
Math.cos(toRad(lat1)) * Math.sin(toRad(lat2)) -
Math.sin(toRad(lat1)) * Math.cos(toRad(lat2)) * Math.cos(dLng);
const bearing = toDeg(Math.atan2(y, x));
return (bearing + 360) % 360;
}
/**
* Get a simple compass direction label from a bearing in degrees.
*/
export function bearingToCompass(bearing: number): string {
const dirs = ['N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW'];
const index = Math.round(bearing / 45) % 8;
return dirs[index];
}
/**
* Calculate the geographic midpoint between two coordinates.
*/
export function midpoint(
lat1: number,
lng1: number,
lat2: number,
lng2: number,
): { lat: number; lng: number } {
const toRad = (deg: number) => (deg * Math.PI) / 180;
const toDeg = (rad: number) => (rad * 180) / Math.PI;
const dLng = toRad(lng2 - lng1);
const lat1Rad = toRad(lat1);
const lat2Rad = toRad(lat2);
const lng1Rad = toRad(lng1);
const bx = Math.cos(lat2Rad) * Math.cos(dLng);
const by = Math.cos(lat2Rad) * Math.sin(dLng);
const lat =
toDeg(
Math.atan2(
Math.sin(lat1Rad) + Math.sin(lat2Rad),
Math.sqrt(
(Math.cos(lat1Rad) + bx) * (Math.cos(lat1Rad) + bx) + by * by,
),
),
);
const lng = toDeg(lng1Rad + Math.atan2(by, Math.cos(lat1Rad) + bx));
return { lat, lng };
}
/**
* Check if a point is within a given radius of another point.
*/
export function isWithinRadius(
lat1: number,
lng1: number,
lat2: number,
lng2: number,
radiusKm: number,
): boolean {
return calculateDistance(lat1, lng1, lat2, lng2) <= radiusKm;
}
/**
* Calculate approximate bounding box around a center point.
*
* @param lat - Center latitude
* @param lng - Center longitude
* @param radiusKm - Radius in kilometers
* @returns Bounding box { north, south, east, west }
*/
export function boundingBox(
lat: number,
lng: number,
radiusKm: number,
): { north: number; south: number; east: number; west: number } {
const toRad = (deg: number) => (deg * Math.PI) / 180;
const toDeg = (rad: number) => (rad * 180) / Math.PI;
const latDelta = toDeg(radiusKm / EARTH_RADIUS_KM);
const lngDelta = toDeg(
radiusKm / (EARTH_RADIUS_KM * Math.cos(toRad(lat))),
);
return {
north: lat + latDelta,
south: lat - latDelta,
east: lng + lngDelta,
west: lng - lngDelta,
};
}