/** * Utility functions for Polyline */ export interface LatLng { latitude: number; longitude: number; } /** * Tìm điểm ở giữa của polyline */ export const getMiddlePointOfPolyline = (coordinates: LatLng[]): LatLng => { if (coordinates.length === 0) { return { latitude: 0, longitude: 0 }; } if (coordinates.length === 1) { return coordinates[0]; } const middleIndex = Math.floor(coordinates.length / 2); return coordinates[middleIndex]; }; /** * Tính toán điểm ở giữa của 2 điểm */ export const getMidpoint = (point1: LatLng, point2: LatLng): LatLng => { return { latitude: (point1.latitude + point2.latitude) / 2, longitude: (point1.longitude + point2.longitude) / 2, }; }; /** * Tính khoảng cách giữa 2 điểm (Haversine formula) * Trả về khoảng cách theo km */ export const calculateDistance = (point1: LatLng, point2: LatLng): number => { const R = 6371; // Bán kính trái đất (km) const dLat = (point2.latitude - point1.latitude) * (Math.PI / 180); const dLon = (point2.longitude - point1.longitude) * (Math.PI / 180); const a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.cos(point1.latitude * (Math.PI / 180)) * Math.cos(point2.latitude * (Math.PI / 180)) * Math.sin(dLon / 2) * Math.sin(dLon / 2); const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); return R * c; }; /** * Tính tổng khoảng cách của polyline */ export const calculateTotalDistance = (coordinates: LatLng[]): number => { if (coordinates.length < 2) return 0; let totalDistance = 0; for (let i = 0; i < coordinates.length - 1; i++) { totalDistance += calculateDistance(coordinates[i], coordinates[i + 1]); } return totalDistance; }; /** * Tính heading (hướng) giữa 2 điểm * Trả về góc độ (0-360) */ export const calculateHeading = (point1: LatLng, point2: LatLng): number => { const dLon = point2.longitude - point1.longitude; const lat1 = point1.latitude * (Math.PI / 180); const lat2 = point2.latitude * (Math.PI / 180); const dLonRad = dLon * (Math.PI / 180); const y = Math.sin(dLonRad) * Math.cos(lat2); const x = Math.cos(lat1) * Math.sin(lat2) - Math.sin(lat1) * Math.cos(lat2) * Math.cos(dLonRad); const bearing = Math.atan2(y, x) * (180 / Math.PI); return (bearing + 360) % 360; }; /** * Tính điểm trung tâm (centroid) của polygon * Sử dụng thuật toán Shoelace formula để tính centroid chính xác * Thuật toán này tính centroid dựa trên diện tích, phù hợp với polygon bất kỳ */ export const getPolygonCenter = (coordinates: LatLng[]): LatLng => { if (coordinates.length === 0) { return { latitude: 0, longitude: 0 }; } if (coordinates.length === 1) { return coordinates[0]; } if (coordinates.length === 2) { return { latitude: (coordinates[0].latitude + coordinates[1].latitude) / 2, longitude: (coordinates[0].longitude + coordinates[1].longitude) / 2, }; } let area = 0; let centroidLat = 0; let centroidLon = 0; // Đảm bảo polygon đóng (điểm đầu = điểm cuối) const coords = [...coordinates]; if ( coords[0].latitude !== coords[coords.length - 1].latitude || coords[0].longitude !== coords[coords.length - 1].longitude ) { coords.push(coords[0]); } // Tính diện tích và centroid sử dụng Shoelace formula for (let i = 0; i < coords.length - 1; i++) { const lat1 = coords[i].latitude; const lon1 = coords[i].longitude; const lat2 = coords[i + 1].latitude; const lon2 = coords[i + 1].longitude; const cross = lat1 * lon2 - lon1 * lat2; area += cross; centroidLat += (lat1 + lat2) * cross; centroidLon += (lon1 + lon2) * cross; } area = area / 2; // Nếu diện tích quá nhỏ (polygon suy biến), dùng trung bình đơn giản if (Math.abs(area) < 0.0000001) { let latSum = 0; let lonSum = 0; for (const coord of coordinates) { latSum += coord.latitude; lonSum += coord.longitude; } return { latitude: latSum / coordinates.length, longitude: lonSum / coordinates.length, }; } centroidLat = centroidLat / (6 * area); centroidLon = centroidLon / (6 * area); return { latitude: centroidLat, longitude: centroidLon, }; };