Files
TREK/client/src/utils/geo.test.ts
T
Maurice 0246e5c317 fix(map): a distance between near antipodal points is half the globe instead of NaN
haversineKm in src/utils/geo.ts answered NaN for some near antipodal pairs,
for example 47.3812, -10.8827 and -47.3812001, 169.1173. Rounding pushed the
haversine term a hair past 1, and asin of a value above 1 is NaN, which then
spread into route sums and range checks.

Cause: unlike the Studio legKm and the roadtrip haversineKm in
@trek/shared, the shared client helper had no clamp before Math.asin.

Fix: clamp with Math.min(1, ...) before Math.asin. With the clamp the helper
runs exactly the operations of Studio legKm in the same order (2 * 6371 is
12742), so Studio now uses haversineKm and legKm goes, with its cases carried
into the new geo tests. The roadtrip variant multiplies its terms in another
order and gives different last bits, so it stays as it is.

Red test: src/utils/geo.test.ts FE-UTIL-GEO-002 failed on the unfixed code
with "AssertionError: expected NaN to be close to 20015.086796020572,
received difference is NaN, but expected 0.0005".

Removed tests: src/components/Studio/legKm.test.ts went with legKm. Its four
cases (zero for one point, a degree of latitude about 111.19 km, the same
both ways between 8700 and 8900 km, 0,0 to 0,180 equal to 12742 * PI / 2) now
run against haversineKm as FE-UTIL-GEO-001, 003 and 004.
2026-10-09 19:00:45 +02:00

36 lines
1.5 KiB
TypeScript

import { describe, expect, it } from 'vitest';
import { haversineKm, withinDayTripRange } from './geo';
// FE-UTIL-GEO-001 to FE-UTIL-GEO-005
describe('haversineKm', () => {
it('FE-UTIL-GEO-001: measures a degree of latitude and nothing between a point and itself', () => {
expect(haversineKm({ lat: 48.1, lng: 11.6 }, { lat: 48.1, lng: 11.6 })).toBe(0);
expect(haversineKm({ lat: 0, lng: 0 }, { lat: 1, lng: 0 })).toBeCloseTo(111.19, 1);
});
it('FE-UTIL-GEO-002: a near antipodal pair is half the globe, not NaN', () => {
// Rounding pushes the haversine term a hair past 1 for this pair, where asin is undefined.
const km = haversineKm({ lat: 47.3812, lng: -10.8827 }, { lat: -47.3812001, lng: 169.1173 });
expect(km).toBeCloseTo(6371 * Math.PI, 3);
});
it('FE-UTIL-GEO-003: is the same both ways', () => {
const a = { lat: 35.68, lng: 139.69 };
const b = { lat: 34.05, lng: -118.24 };
expect(haversineKm(a, b)).toBeCloseTo(haversineKm(b, a), 9);
expect(haversineKm(a, b)).toBeGreaterThan(8700);
expect(haversineKm(a, b)).toBeLessThan(8900);
});
it('FE-UTIL-GEO-004: an exact antipodal pair is half the globe', () => {
expect(haversineKm({ lat: 0, lng: 0 }, { lat: 0, lng: 180 })).toBeCloseTo(6371 * Math.PI, 3);
});
it('FE-UTIL-GEO-005: a stop counts as a day trip up to 150 km away', () => {
expect(withinDayTripRange({ lat: 0, lng: 0 }, { lat: 1, lng: 0 })).toBe(true);
expect(withinDayTripRange({ lat: 0, lng: 0 }, { lat: 2, lng: 0 })).toBe(false);
});
});