Files
TREK/client/src/utils/geo.ts
T
Maurice e64ade010f fix(roadtrip): a flight day ends at the stay it lands at, and no drive runs where nobody drives
On a day a flight, train, ferry, cruise or bus leaves and lands, tonight's stay was seated
by its check-in time. A hotel in Munich checked into from 15:00 stood ahead of a 15:15 flight
out of Hamburg, so the drive went from the Munich hotel to Hamburg airport and reported the
flight as nine hours late. A check-in is the earliest arrival, not an appointment: such a
stay now waits behind the landing when it lies nearer the arrival than the departure, and
its check-in only holds it when the ride lands before the room is ready. A time typed on the
stop keeps its seat.

With the stays at both ends of a day switched on:
- a day with nothing but the next stay's check-in and a ride booked across it gets no drive
  from one hotel to the other, the same rule the day plan follows since #2476;
- a stay is never joined by road to a terminal more than 2000 km away. The check moved to
  shared/roadtrip so Days and the road trip read one copy.

A departure terminal's pin is dated: reaching it more than half a day late no longer moves
the flight to the next day without a word, it is a late warning with the full minutes.
A check-in evening also carries the stay's check-in time, as a label only.

calculate_roadtrip runs the same code and its description says all of the above.
2026-09-24 11:32:52 +02:00

34 lines
1.6 KiB
TypeScript

/**
* Straight-line distance between two coordinates, in kilometres.
*
* Lifted out of useTransportRoutes so the day-route builder can share the one
* implementation instead of adding a fourth copy. The map overlays keep their own
* `[lat, lng]`-tuple variants — different call shape, and they only sum arc lengths.
*/
export function haversineKm(a: { lat: number; lng: number }, b: { lat: number; lng: number }): number {
const R = 6371
const dLat = (b.lat - a.lat) * Math.PI / 180
const dLng = (b.lng - a.lng) * Math.PI / 180
const la1 = a.lat * Math.PI / 180
const la2 = b.lat * Math.PI / 180
const h = Math.sin(dLat / 2) ** 2 + Math.cos(la1) * Math.cos(la2) * Math.sin(dLng / 2) ** 2
return 2 * R * Math.asin(Math.sqrt(h))
}
/**
* How far a day out of a hotel and back plausibly reaches, straight-line.
*
* Consulted where a stay records no check-in/check-out time and the shape of the day is
* therefore a guess (#2157): a stop inside this radius is somewhere the hotel sent you out
* to for the day, a stop outside it is where you travelled from or to. It has to be a
* road-day radius rather than the booking-geometry MAX_DRIVE_KM in @trek/shared: 2000 km
* spans the whole of Europe, so every car journey on the continent passed as a day trip
* and kept the phantom hotel leg the issue reported.
*/
export const MAX_DAY_TRIP_KM = 150
/** Whether a stop is near enough a hotel to be a day out from it and back. */
export function withinDayTripRange(a: { lat: number; lng: number }, b: { lat: number; lng: number }): boolean {
return haversineKm(a, b) <= MAX_DAY_TRIP_KM
}