aboutsummaryrefslogtreecommitdiffziptar.gz
path: root/viewer/tests/flatmap.test.mjs
diff options
context:
space:
mode:
authorgodosa <godosa@godosa.eu>2026-10-07 00:14:38 +0200
committergodosa <godosa@godosa.eu>2026-10-07 00:14:38 +0200
commit3443c1c65e9f1753e1e656b35d08416c1fa298f2 (patch)
tree4e43236f460145a4d75d1b4616dcb7aa6ef08f51 /viewer/tests/flatmap.test.mjs
downloadworldmap-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.mjs83
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);
+});