/** * Utility class for geographic calculations */ export class GeographicUtils { /** * Check if a point is inside a polygon using ray-casting algorithm * @param point - [longitude, latitude] * @param polygon - Array of [longitude, latitude] pairs */ static isPointInPolygon(point: [number, number], polygon: [number, number][]): boolean { const x = point[0]; const y = point[1]; let inside = false; for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) { const xi = polygon[i][0]; const yi = polygon[i][1]; const xj = polygon[j][0]; const yj = polygon[j][1]; const intersect = yi > y !== yj > y && x < ((xj - xi) * (y - yi)) / (yj - yi + Number.EPSILON) + xi; if (intersect) inside = !inside; } return inside; } /** * Convert degrees to radians */ private static toRad(value: number): number { return (value * Math.PI) / 180; } /** * Calculate distance between two points in kilometers using Haversine formula */ static getDistanceKm(lat1: number, lng1: number, lat2: number, lng2: number): number { const R = 6371; // Earth radius in KM const dLat = this.toRad(lat2 - lat1); const dLng = this.toRad(lng2 - lng1); const a = Math.sin(dLat / 2) ** 2 + Math.cos(this.toRad(lat1)) * Math.cos(this.toRad(lat2)) * Math.sin(dLng / 2) ** 2; const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); return R * c; } /** * Calculate distance between two points in kilometers (rounded) */ static getDistanceKmRounded( lat1: number, lng1: number, lat2: number, lng2: number, precision = 2, ): number { const distance = this.getDistanceKm(lat1, lng1, lat2, lng2); return Number(distance.toFixed(precision)); } }