/** * Coordinate validation and utility functions * Ensures coordinates are within valid ranges and handles edge cases */ /** * Validates if a latitude is within valid range [-90, 90] */ export function isValidLatitude(lat: number): boolean { return typeof lat === 'number' && !isNaN(lat) && lat >= -90 && lat <= 90; } /** * Validates if a longitude is within valid range [-180, 180] */ export function isValidLongitude(lng: number): boolean { return typeof lng === 'number' && !isNaN(lng) && lng >= -180 && lng <= 180; } /** * Validates if a coordinate pair [lat, lng] is valid */ export function isValidCoordinate(lat: number, lng: number): boolean { return isValidLatitude(lat) && isValidLongitude(lng); } /** * Validates and normalizes a coordinate pair * Returns null if invalid, otherwise returns normalized [lat, lng] */ export function normalizeCoordinate(lat: number, lng: number): [number, number] | null { if (!isValidCoordinate(lat, lng)) { return null; } return [lat, lng]; } /** * Checks if a coordinate is within viewport bounds */ export function isCoordinateInBounds( lat: number, lng: number, bounds: { north: number; south: number; east: number; west: number } ): boolean { if (!isValidCoordinate(lat, lng)) return false; // Handle longitude wrapping (e.g., -180 to 180 crossing) const lngInBounds = (bounds.west <= bounds.east && lng >= bounds.west && lng <= bounds.east) || (bounds.west > bounds.east && (lng >= bounds.west || lng <= bounds.east)); return lat >= bounds.south && lat <= bounds.north && lngInBounds; } /** * Calculates distance between two coordinates using Haversine formula * Returns distance in kilometers */ export function calculateDistance(lat1: number, lng1: number, lat2: number, lng2: number): number { if (!isValidCoordinate(lat1, lng1) || !isValidCoordinate(lat2, lng2)) { return Infinity; } const R = 6371; // Earth's radius in km const dLat = ((lat2 - lat1) * Math.PI) / 180; const dLng = ((lng2 - lng1) * Math.PI) / 180; const a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.cos((lat1 * Math.PI) / 180) * Math.cos((lat2 * Math.PI) / 180) * Math.sin(dLng / 2) * Math.sin(dLng / 2); const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); return R * c; }