49 lines
1.8 KiB
TypeScript
49 lines
1.8 KiB
TypeScript
import { describe, it, expect } from 'vitest';
|
|
import { parseEnsemble, ellipseRing } from '$domain';
|
|
import type { EnsembleFootprint } from '$domain';
|
|
|
|
describe('parseEnsemble', () => {
|
|
it('narrows an ensemble payload', () => {
|
|
const parsed = parseEnsemble({
|
|
ensemble: { members: [{ member: 0, landing: { lat: 1, lng: 2, alt: 0 } }], failed: [] }
|
|
});
|
|
expect(parsed?.members).toHaveLength(1);
|
|
expect(parsed?.footprint).toBeNull();
|
|
});
|
|
|
|
it('rejects a plain single-run result', () => {
|
|
expect(parseEnsemble({ prediction: [] })).toBeNull();
|
|
expect(parseEnsemble(null)).toBeNull();
|
|
expect(parseEnsemble({ ensemble: {} })).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe('ellipseRing', () => {
|
|
const footprint: EnsembleFootprint = {
|
|
count: 5,
|
|
mean: { lat: 62, lng: 129 },
|
|
radius_km: 10,
|
|
ellipse: { semi_major_km: 20, semi_minor_km: 10, bearing_deg: 0 }
|
|
};
|
|
|
|
it('returns a closed ring centred on the mean', () => {
|
|
const ring = ellipseRing(footprint, 32);
|
|
expect(ring).toHaveLength(33);
|
|
expect(ring[0][0]).toBeCloseTo(ring[32][0], 9);
|
|
expect(ring[0][1]).toBeCloseTo(ring[32][1], 9);
|
|
const lats = ring.map((p) => p[0]);
|
|
expect((Math.min(...lats) + Math.max(...lats)) / 2).toBeCloseTo(62, 6);
|
|
});
|
|
|
|
it('corrects longitude for latitude so the ellipse is not stretched', () => {
|
|
const ring = ellipseRing(footprint, 64);
|
|
const lats = ring.map((p) => p[0]);
|
|
const lngs = ring.map((p) => p[1]);
|
|
// 20 km north-south at bearing 0 => ~0.18° of latitude.
|
|
expect(Math.max(...lats) - 62).toBeCloseTo(20 / 111.32, 3);
|
|
// 10 km east-west spans more degrees at 62°N than it would at the equator.
|
|
const halfSpanDeg = Math.max(...lngs) - 129;
|
|
expect(halfSpanDeg).toBeGreaterThan(10 / 111.32);
|
|
expect(halfSpanDeg).toBeCloseTo(10 / (111.32 * Math.cos((62 * Math.PI) / 180)), 3);
|
|
});
|
|
});
|