| |
| |
| |
| |
| |
| |
|
|
| const EARTH_RADIUS_KM = 6371; |
|
|
| |
| |
| |
| |
| |
| |
| 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; |
| } |
|
|
| |
| |
| |
| |
| |
| 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; |
| } |
|
|
| |
| |
| |
| 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]; |
| } |
|
|
| |
| |
| |
| 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 }; |
| } |
|
|
| |
| |
| |
| export function isWithinRadius( |
| lat1: number, |
| lng1: number, |
| lat2: number, |
| lng2: number, |
| radiusKm: number, |
| ): boolean { |
| return calculateDistance(lat1, lng1, lat2, lng2) <= radiusKm; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| 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, |
| }; |
| } |
|
|