-
Notifications
You must be signed in to change notification settings - Fork 66
test: add antimeridian splitting tests for low npoints values #76
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: chore/esm
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,71 @@ | ||||||||||||||||||||||||||||||||||||||
| import { GreatCircle } from '../src'; | ||||||||||||||||||||||||||||||||||||||
| import type { MultiLineString, LineString } from 'geojson'; | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| // Routes that cross the antimeridian | ||||||||||||||||||||||||||||||||||||||
| const PACIFIC_ROUTES = [ | ||||||||||||||||||||||||||||||||||||||
| { name: 'Tokyo → LAX', start: { x: 139.7798, y: 35.5494 }, end: { x: -118.4085, y: 33.9416 } }, | ||||||||||||||||||||||||||||||||||||||
| { name: 'Auckland → LAX', start: { x: 174.79, y: -36.85 }, end: { x: -118.41, y: 33.94 } }, | ||||||||||||||||||||||||||||||||||||||
| { name: 'Shanghai → SFO', start: { x: 121.81, y: 31.14 }, end: { x: -122.38, y: 37.62 } }, | ||||||||||||||||||||||||||||||||||||||
| ]; | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| function assertSplitAtAntimeridian(coords: number[][][]) { | ||||||||||||||||||||||||||||||||||||||
| const seg0 = coords[0]; | ||||||||||||||||||||||||||||||||||||||
| const seg1 = coords[1]; | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| expect(seg0).toBeDefined(); | ||||||||||||||||||||||||||||||||||||||
| expect(seg1).toBeDefined(); | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| if (!seg0 || !seg1) return; // narrow for TS | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| const lastOfFirst = seg0[seg0.length - 1]; | ||||||||||||||||||||||||||||||||||||||
| const firstOfSecond = seg1[0]; | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| expect(lastOfFirst).toBeDefined(); | ||||||||||||||||||||||||||||||||||||||
| expect(firstOfSecond).toBeDefined(); | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| if (!lastOfFirst || !firstOfSecond) return; // narrow for TS | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| // Both sides of the split must be at ±180 | ||||||||||||||||||||||||||||||||||||||
| expect(Math.abs(lastOfFirst[0] ?? NaN)).toBeCloseTo(180, 1); | ||||||||||||||||||||||||||||||||||||||
| expect(Math.abs(firstOfSecond[0] ?? NaN)).toBeCloseTo(180, 1); | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| // Latitudes must match — no gap at the antimeridian | ||||||||||||||||||||||||||||||||||||||
| expect(lastOfFirst[1] ?? NaN).toBeCloseTo(firstOfSecond[1] ?? NaN, 3); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| describe('antimeridian splitting', () => { | ||||||||||||||||||||||||||||||||||||||
| describe('with npoints=100', () => { | ||||||||||||||||||||||||||||||||||||||
| for (const { name, start, end } of PACIFIC_ROUTES) { | ||||||||||||||||||||||||||||||||||||||
| test(`${name} produces a split MultiLineString`, () => { | ||||||||||||||||||||||||||||||||||||||
| const result = new GreatCircle(start, end).Arc(100, { offset: 10 }).json(); | ||||||||||||||||||||||||||||||||||||||
| expect(result.geometry.type).toBe('MultiLineString'); | ||||||||||||||||||||||||||||||||||||||
| assertSplitAtAntimeridian((result.geometry as MultiLineString).coordinates); | ||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+37
to
+46
|
||||||||||||||||||||||||||||||||||||||
| describe('with npoints=100', () => { | |
| for (const { name, start, end } of PACIFIC_ROUTES) { | |
| test(`${name} produces a split MultiLineString`, () => { | |
| const result = new GreatCircle(start, end).Arc(100, { offset: 10 }).json(); | |
| expect(result.geometry.type).toBe('MultiLineString'); | |
| assertSplitAtAntimeridian((result.geometry as MultiLineString).coordinates); | |
| }); | |
| } | |
| }); |
Copilot
AI
Mar 30, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As written, the npoints=10 tests will fail on the current GreatCircle.Arc implementation (it often returns an unsplit LineString for Tokyo→LAX and Shanghai→SFO because the split detector only triggers for longitude diffs > 360 - offset). If this PR is intended to be tests-only, mark the known-bad cases with test.failing/test.todo (or skip with a link to #75) to keep CI green; otherwise include the corresponding implementation fix in this PR.
| test(`${name} splits correctly`, () => { | |
| const result = new GreatCircle(start, end).Arc(10, { offset: 10 }).json(); | |
| expect(result.geometry.type).toBe('MultiLineString'); | |
| assertSplitAtAntimeridian((result.geometry as MultiLineString).coordinates); | |
| }); | |
| if (name === 'Tokyo → LAX' || name === 'Shanghai → SFO') { | |
| test.skip(`${name} splits correctly (known issue, see #75)`, () => { | |
| const result = new GreatCircle(start, end).Arc(10, { offset: 10 }).json(); | |
| expect(result.geometry.type).toBe('MultiLineString'); | |
| assertSplitAtAntimeridian((result.geometry as MultiLineString).coordinates); | |
| }); | |
| } else { | |
| test(`${name} splits correctly`, () => { | |
| const result = new GreatCircle(start, end).Arc(10, { offset: 10 }).json(); | |
| expect(result.geometry.type).toBe('MultiLineString'); | |
| assertSplitAtAntimeridian((result.geometry as MultiLineString).coordinates); | |
| }); | |
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
assertSplitAtAntimeridianonly checksabs(lon) ≈ 180, so it would still pass if both segment endpoints use the same sign (e.g.180/180) or if the geometry contains >2 segments with later bad splits. To prevent false positives, assertcoords.length === 2, both segments are non-empty, and that the split longitudes are exactly180and-180(opposite signs) for the adjacent endpoints (matching the implementation and existing tests intest/great-circle.test.ts).