Spaces:
Sleeping
Sleeping
File size: 1,752 Bytes
57a889c | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | import { db } from '../db/database';
// Great-circle distance between two points in kilometres.
function haversineKm(lat1: number, lng1: number, lat2: number, lng2: number): number {
const R = 6371;
const toRad = (deg: number) => (deg * Math.PI) / 180;
const dLat = toRad(lat2 - lat1);
const dLng = toRad(lng2 - lng1);
const a =
Math.sin(dLat / 2) ** 2 +
Math.cos(toRad(lat1)) * Math.cos(toRad(lat2)) * Math.sin(dLng / 2) ** 2;
return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
}
/**
* Total flight distance a user has covered, summed across every non-cancelled
* flight reservation in their trips. Each flight stores its waypoints in
* reservation_endpoints (from → stops → to, ordered by sequence); we add up the
* legs between consecutive points so multi-stop flights count correctly.
*/
export function getFlightDistanceKm(userId: number): number {
const rows = db.prepare(`
SELECT re.reservation_id, re.lat, re.lng
FROM reservation_endpoints re
JOIN reservations r ON r.id = re.reservation_id
JOIN trips t ON t.id = r.trip_id
LEFT JOIN trip_members tm ON tm.trip_id = t.id AND tm.user_id = ?
WHERE (t.user_id = ? OR tm.user_id IS NOT NULL)
AND r.type = 'flight'
AND r.status != 'cancelled'
ORDER BY re.reservation_id, re.sequence
`).all(userId, userId) as { reservation_id: number; lat: number; lng: number }[];
let total = 0;
let prev: { id: number; lat: number; lng: number } | null = null;
for (const point of rows) {
if (prev && prev.id === point.reservation_id) {
total += haversineKm(prev.lat, prev.lng, point.lat, point.lng);
}
prev = { id: point.reservation_id, lat: point.lat, lng: point.lng };
}
return Math.round(total);
}
|