1// The Ground descriptor and the vocabulary it reads.2//3// These are the clean design's own intent tests (issue #5 ยง6).4// What they document: a ground is data, the fallback ladder is three branches5// deep, and a malformed declaration is answered with silence rather than a6// plausible number.78import assert from "node:assert/strict";9import test from "node:test";1011import { extentArea, surfaceExtent, surfaceWidth } from "../cellsystems/ground.ts";12import { equirectMapping, geoMapping, mercatorMapping, worldSurface } from "../semconv/geometry.ts";13import { mercatorCity, nothing, sphere, square, squareFromBounds, squareNoLens } from "./grounds.ts";1415test("the surface extent has three branches, in order", () => {16 assert.deepEqual([...surfaceExtent(square)], [0, -1024, 1024, -0]);17 assert.deepEqual([...surfaceExtent(squareFromBounds)], [128, -512, 640, -256]);18 assert.deepEqual([...surfaceExtent(squareNoLens)], [0, -1024, 1024, -0]);19});2021test("a ground with nothing on it is still a ground", () => {22 // No volume and no lens: an empty extent rather than a thrown error, because23 // the app asks this question before it has opened anything.24 assert.deepEqual([...surfaceExtent(nothing)], [0, -0, 0, -0]);25 assert.equal(surfaceWidth(nothing), 0);26});2728test("a lens's declared surface wins over the window it fills", () => {29 const both = {30 tileGridSize: 8192,31 lens: {32 surface: { x: 100, y: 200, width: 300, height: 400 },33 bounds: { x: 0, y: 0, width: 8192, height: 8192 },34 },35 world: { attrs: {} },36 };37 assert.deepEqual([...surfaceExtent(both)], [100, -600, 400, -200],38 "the surface is the ground; the bounds are only where the raster reaches");39});4041test("width and area read the extent the same way", () => {42 assert.equal(surfaceWidth(square), 1024);43 assert.equal(extentArea(surfaceExtent(square)), 1024 * 1024);44});4546test("a world that says nothing is a plane", () => {47 assert.equal(worldSurface(square.world), "plane");48 assert.equal(worldSurface(sphere.world), "sphere");49 assert.equal(worldSurface(null), "plane");50 assert.equal(worldSurface({ attrs: { "atlas.geometry.surface": "" } }), "plane");51});5253test("the equirect mapping inverts", () => {54 const mapping = equirectMapping(sphere.world);55 assert.ok(mapping);56 assert.deepEqual([...mapping.toWorld(90, -180)], [0, 0], "the north-west corner is the origin");57 assert.deepEqual([...mapping.toWorld(0, 0)], [4096, 2048], "the origin is the centre");58 const [lat, lng] = mapping.toLatLng(4096, 2048);59 assert.equal(lat, 0);60 assert.equal(lng, 0);61});6263test("the mercator mapping inverts, non-linearly in latitude", () => {64 const mapping = mercatorMapping(mercatorCity.world);65 assert.ok(mapping);66 const [x, y] = mapping.toWorld(44.05, -121.3);67 const [lat, lng] = mapping.toLatLng(x, y);68 assert.ok(Math.abs(lat - 44.05) < 1e-9, `latitude round-trips: ${lat}`);69 assert.ok(Math.abs(lng - -121.3) < 1e-9, `longitude round-trips: ${lng}`);70 // Linear in longitude, not in latitude: the halfway parallel is not halfway71 // down the picture.72 const [middleX] = mapping.toWorld(44.05, -121.3);73 assert.ok(Math.abs(middleX - 2048) < 1e-9, "longitude is linear");74 const [, middleY] = mapping.toWorld((44.15 + 43.99) / 2, -121.3);75 assert.notEqual(middleY, 2048);76});7778test("geoMapping prefers equirect, and falls through to mercator", () => {79 assert.ok(geoMapping(sphere.world));80 assert.ok(geoMapping(mercatorCity.world));81 assert.equal(geoMapping(square.world), null, "a plane declares no flattening");82});8384test("a malformed declaration is answered with silence, not a plausible number", () => {85 const cases: Record<string, string>[] = [86 { "atlas.geometry.projection": "equirect" },87 { "atlas.geometry.projection": "equirect", "atlas.geometry.equirect.px": "0,0,8192" },88 {89 "atlas.geometry.projection": "equirect",90 "atlas.geometry.equirect.px": "0,0,8192,4096",91 "atlas.geometry.equirect.deg": "-180,90,180",92 },93 {94 "atlas.geometry.projection": "equirect",95 "atlas.geometry.equirect.px": "0,0,8192,4096",96 "atlas.geometry.equirect.deg": "-180,90,x,-90",97 },98 {99 // A window with no height would invert by dividing by zero.100 "atlas.geometry.projection": "equirect",101 "atlas.geometry.equirect.px": "0,0,8192,0",102 "atlas.geometry.equirect.deg": "-180,90,180,-90",103 },104 {105 // Degenerate degrees: north equals south.106 "atlas.geometry.projection": "equirect",107 "atlas.geometry.equirect.px": "0,0,8192,4096",108 "atlas.geometry.equirect.deg": "-180,0,180,0",109 },110 ];111 for (const attrs of cases) {112 assert.equal(geoMapping({ attrs }), null, `${JSON.stringify(attrs)} is not a mapping`);113 }114});