/** * Qibla direction calculation utilities. * * Computes the bearing (direction) from a user's location to the Kaaba in Mecca. * Also provides distance to Mecca using the haversine formula. * * Kaaba coordinates: 21.4225° N, 39.8262° E */ const KAABA_LAT = 21.4225; const KAABA_LNG = 39.8262; /** * Convert degrees to radians. */ function toRadians(deg: number): number { return (deg * Math.PI) / 180; } /** * Convert radians to degrees. */ function toDegrees(rad: number): number { return (rad * 180) / Math.PI; } /** * Calculate the bearing (direction) from a location to the Kaaba in Mecca. * * Uses the standard great-circle bearing formula: * θ = atan2(sin(Δλ)·cos(φ₂), cos(φ₁)·sin(φ₂) − sin(φ₁)·cos(φ₂)·cos(Δλ)) * * @param lat User's latitude (degrees) * @param lng User's longitude (degrees) * @returns Bearing in degrees clockwise from North (0–360) */ export function qiblaBearing(lat: number, lng: number): number { const φ1 = toRadians(lat); const φ2 = toRadians(KAABA_LAT); const Δλ = toRadians(KAABA_LNG - lng); const y = Math.sin(Δλ) * Math.cos(φ2); const x = Math.cos(φ1) * Math.sin(φ2) - Math.sin(φ1) * Math.cos(φ2) * Math.cos(Δλ); let bearing = toDegrees(Math.atan2(y, x)); // Normalize to 0–360 return (bearing + 360) % 360; } /** * Calculate the distance from a location to the Kaaba in Mecca. * * @param lat User's latitude (degrees) * @param lng User's longitude (degrees) * @returns Distance in kilometres, rounded to 1 decimal place */ export function distanceToMecca(lat: number, lng: number): number { const EARTH_RADIUS_KM = 6371; const dLat = toRadians(KAABA_LAT - lat); const dLng = toRadians(KAABA_LNG - lng); const a = Math.sin(dLat / 2) ** 2 + Math.cos(toRadians(lat)) * Math.cos(toRadians(KAABA_LAT)) * Math.sin(dLng / 2) ** 2; const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); return Math.round(EARTH_RADIUS_KM * c * 10) / 10; } /** * Get a human-readable cardinal direction from a bearing. * * @param bearing Bearing in degrees (0–360) * @returns Cardinal direction string (e.g., "NE") */ export function bearingToDirection(bearing: number): string { const dirs = ["N", "NE", "E", "SE", "S", "SW", "W", "NW"]; const index = Math.round(bearing / 45) % 8; return dirs[index]; } /** * Format distance in a human-readable way. */ export function formatDistance(km: number): string { if (km < 1) return `${Math.round(km * 1000)} m`; if (km < 100) return `${km.toFixed(1)} km`; return `${Math.round(km).toLocaleString()} km`; } export { KAABA_LAT, KAABA_LNG };