diff options
| author | godosa <godosa@godosa.eu> | 2026-10-07 00:14:38 +0200 |
|---|---|---|
| committer | godosa <godosa@godosa.eu> | 2026-10-07 00:14:38 +0200 |
| commit | 3443c1c65e9f1753e1e656b35d08416c1fa298f2 (patch) | |
| tree | 4e43236f460145a4d75d1b4616dcb7aa6ef08f51 /viewer/tests/flatmap.test.mjs | |
| download | worldmap-viewer-3443c1c65e9f1753e1e656b35d08416c1fa298f2.tar.gz worldmap-viewer-3443c1c65e9f1753e1e656b35d08416c1fa298f2.zip | |
worldmap-viewer: initial public history
Diffstat (limited to 'viewer/tests/flatmap.test.mjs')
| -rw-r--r-- | viewer/tests/flatmap.test.mjs | 83 |
1 files changed, 83 insertions, 0 deletions
diff --git a/viewer/tests/flatmap.test.mjs b/viewer/tests/flatmap.test.mjs new file mode 100644 index 0000000..2cafb36 --- /dev/null +++ b/viewer/tests/flatmap.test.mjs @@ -0,0 +1,83 @@ +import './_planet.mjs'; +import { test } from 'node:test'; +import assert from 'node:assert/strict'; +import { fakeRenderer } from './importmap.mjs'; + +const { FlatView, MAX_ZOOM } = await import('../js/flatmap.js'); +const near = (a, b, eps) => assert.ok(Math.abs(a - b) <= eps, `${a} vs ${b}`); +const ev = (type, props) => Object.assign(new Event(type, { cancelable: true }), { button: 0, pointerId: 1, deltaMode: 0, ...props }); + +function flat(kind, st = { lat: 20, lon: 30, zoom: 3 }) { + const r = fakeRenderer(); + const v = new FlatView(r, kind, () => {}); + v.resize(800, 400); + v.setState(st); + v.activate(); + return { v, canvas: r.domElement }; +} + +for (const kind of ['equal_earth', 'plate_carree']) { + test(`${kind}: state round trip, zoom clamped`, () => { + const s = flat(kind).v.getState(); + near(s.lat, 20, 1e-9); near(s.lon, 30, 1e-9); + assert.equal(s.zoom, 3); + assert.equal(flat(kind, { lat: 0, lon: 0, zoom: 1e9 }).v.getState().zoom, MAX_ZOOM); + assert.equal(MAX_ZOOM, 20000); + assert.equal(flat(kind, { lat: 0, lon: 0, zoom: 0 }).v.getState().zoom, 1); + }); + + test(`${kind}: pick and toScreen agree, incl. near the seam and poles`, () => { + const { v } = flat(kind, { lat: 0, lon: 0, zoom: 1 }); + for (const [lat, lon] of [[0, 0], [45, 179.9], [-89, -120], [60, -179.9]]) { + const q = v.toScreen(lat, lon), p = v.pick(q.x, q.y); + near(p.lat, lat, 1e-6); near(p.lon, lon, 1e-6); + } + }); + + test(`${kind}: wheel zoom keeps the point under the cursor`, () => { + const { v, canvas } = flat(kind, { lat: 10, lon: 10, zoom: 2 }); + const before = v.pick(600, 120); + canvas.dispatchEvent(ev('wheel', { deltaY: -300, clientX: 600, clientY: 120 })); + assert.ok(v.getState().zoom > 2); + const after = v.pick(600, 120); + near(after.lat, before.lat, 1e-6); near(after.lon, before.lon, 1e-6); + }); + + test(`${kind}: drag pans with the cursor; an inactive view ignores input`, () => { + const { v, canvas } = flat(kind, { lat: 0, lon: 0, zoom: 4 }); + const p = v.pick(400, 200); + canvas.dispatchEvent(ev('pointerdown', { clientX: 400, clientY: 200 })); + canvas.dispatchEvent(ev('pointermove', { clientX: 300, clientY: 200 })); + canvas.dispatchEvent(ev('pointerup', { clientX: 300, clientY: 200 })); + const q = v.pick(300, 200); + near(q.lat, p.lat, 1e-6); near(q.lon, p.lon, 1e-6); + v.deactivate(); + const s = v.getState(); + canvas.dispatchEvent(ev('wheel', { deltaY: -300, clientX: 10, clientY: 10 })); + assert.deepEqual(v.getState(), s); + }); +} + +test('equal earth: outside the outline picks null', () => { + assert.equal(flat('equal_earth', { lat: 0, lon: 0, zoom: 1 }).v.pick(2, 2), null); +}); + +test('long lines are drawn along the great circle (bowing poleward), not straight in the projection', () => { + const { v } = flat('plate_carree', { lat: 0, lon: 0, zoom: 1 }); + v.setLines('m', [[{ lat: 40, lon: -60 }, { lat: 40, lon: 60 }]], 0xffffff); + const pos = v.lines.objects.get('m').geometry.attributes.position.array; + assert.ok(pos.length / 6 > 10, `${pos.length / 6} segments`); + const maxLat = Math.max(...pos.filter((_, i) => i % 3 === 1)) * 180 / Math.PI; + assert.ok(maxLat > 50, `peak latitude ${maxLat}`); +}); + +test('equal earth: the centre cannot leave the outline, so the saved state reproduces the view', () => { + const { v, canvas } = flat('equal_earth', { lat: 60, lon: 170, zoom: 8 }); + canvas.dispatchEvent(ev('pointerdown', { clientX: 400, clientY: 200 })); + canvas.dispatchEvent(ev('pointermove', { clientX: 100, clientY: 200 })); // pan east past lon 180 + canvas.dispatchEvent(ev('pointerup', { clientX: 100, clientY: 200 })); + const s = v.getState(), before = v.toWorld(400, 200); + v.setState(s); + const after = v.toWorld(400, 200); + near(after.X, before.X, 1e-9); near(after.Y, before.Y, 1e-9); +}); |
