clone

an offline map explorer where each world, from a game map to a city's open data, ships as one .atlas file

main
atlas / render / test / raster.test.ts
raster.test.ts 8.2K
1// What the two raster layers are configured with, judged against the lens.2//3// The tile *inventory* tests next door ask what the pair requests; these ask4// what it draws, which is the other half of the same contract. Three of the5// layer options are the lens's own declarations and two were being dropped:6//7// `background` the colour the sheet was drawn on. The corner locator has8// always painted it under its thumbnail, so a map that9// ignored it disagreed with the shelf beside it about what10// the same lens looks like. It belongs to the base layer11// alone: on the detail layer it would paint over the complete12// pyramid wherever the deep capture has a gap.13//14// `extent` the window the pyramid fills. Coverage and `wrapX: false`15// refuse most requests outside it, but an unclipped layer16// still draws out there — a hole, or a neighbour's ground, in17// the margin of a sparse pyramid or a piece of a split sheet.18//19// `interpolate` how an overzoomed tile is stretched: smooth for a20// photograph, nearest-neighbour for pixel art. The seam only21// ever repeats what the lens declares.22//23// The cases: a lens with a background and no bounds (bend-or's Basemap, on24// the whole world square), one with bounds and no background (mars, whose25// window is the top half of the square), one bounded well inside the square26// with margin on every side (the invented city's Streets), and one with a27// surface, no bounds and `interpolate: false` (its Hand-Drawn sheet) — the28// nearest-neighbour declaration no corpus lens carries.2930import test from "node:test";31import { strict as assert } from "node:assert";32import type { Extent } from "ol/extent.js";33import { DataPlane } from "../data/plane.ts";34import type { Lens, TileGrid as GridSpec } from "../data/payload.ts";35import { atlasProjection, lensExtent } from "../chart/projection.ts";36import { TileCounter, buildRaster } from "../chart/raster.ts";37import { payloads, tileGrid, volumes } from "./fixtures.ts";38import { gamePlane, splitSheet } from "./models.ts";3940function lensOf(slug: string, world: string, name: string): Lens {41 const found = payloads(slug).get(world)?.lenses.find((lens) => lens.name === name);42 assert.ok(found, `${slug}/${world} carries the ${name} lens`);43 return found;44}4546function invented(volume: ReturnType<typeof gamePlane>, world: string, name: string): Lens {47 const found = volume.worlds.get(world)?.lenses.find((lens) => lens.name === name);48 assert.ok(found, `${volume.slug}/${world} carries the ${name} lens`);49 return found;50}5152function raster(lens: Lens, grid: GridSpec) {53 return buildRaster(54 new DataPlane(), "/data/v/x/000000000000", lens, grid, atlasProjection(grid),55 new TileCounter());56}5758/** OL hands back its own array; compare the numbers. */59function extent(value: Extent | undefined): number[] | undefined {60 return value === undefined ? undefined : [...value];61}6263/**64 * "No backdrop", as OL spells it.65 *66 * A layer that was never given one answers `undefined` — the declared return67 * type says `false`, which is only what an explicit *refusal* looks like. The68 * two are the same thing to a renderer and this test set does not care which69 * arrives, so both read as "nothing was painted".70 */71function backdrop(layer: { getBackground(): unknown }): unknown {72 const value = layer.getBackground();73 return value === false ? undefined : value;74}7576test("the base layer carries the backdrop the lens declares, and the detail layer never does", () => {77 const grid = tileGrid("bend-or");78 const lens = lensOf("bend-or", "2026-08-02", "Basemap");79 assert.equal(lens.background, "#14181d", "the corpus declares a backdrop");8081 const pair = raster(lens, grid);82 assert.equal(backdrop(pair.base), "#14181d", "the map paints what the shelf paints");83 assert.equal(backdrop(pair.detail), undefined, "the patchy layer lets the whole one through");84});8586test("a lens with no backdrop leaves the layer with none", () => {87 const grid = tileGrid("mars");88 const lens = lensOf("mars", "global", "Viking MDIM 2.1");89 assert.equal(lens.background, undefined, "the corpus declares no backdrop");9091 const pair = raster(lens, grid);92 assert.equal(backdrop(pair.base), undefined, "and nothing is invented for it");93 assert.equal(backdrop(pair.detail), undefined);94});9596test("both layers are clipped to the window a bounded lens fills", () => {97 // The invented Streets pyramid is a 4,159 × 4,378 rectangle anchored at98 // (2016, 2020) of an 8,192 square: a quarter of the world, with real99 // margin on every side for an unclipped layer to draw into. By hand:100 // [2016, −(2020 + 4378), 2016 + 4159, −2020].101 const city = gamePlane();102 const lens = invented(city, "old-quarter", "Streets");103 assert.ok(lens.bounds, "the lens declares a window");104105 const window = [2016, -6398, 6175, -2020];106 assert.deepEqual([...lensExtent(lens, city.tileGrid)], window,107 "the camera's window and the layers' agree");108109 const pair = raster(lens, city.tileGrid);110 assert.deepEqual(extent(pair.base.getExtent()), window);111 assert.deepEqual(extent(pair.detail.getExtent()), window);112 assert.notDeepEqual(window, [0, -city.tileGrid.size, city.tileGrid.size, 0],113 "and it is not the whole square");114});115116test("a lens with no bounds is clipped to the whole world square", () => {117 const city = gamePlane();118 const drawn = invented(city, "old-quarter", "Hand-Drawn");119 assert.equal(drawn.bounds, undefined, "the lens declares a surface and no window");120 const pair = raster(drawn, city.tileGrid);121 assert.deepEqual(extent(pair.base.getExtent()), [0, -8192, 8192, 0]);122 assert.deepEqual(extent(pair.detail.getExtent()), [0, -8192, 8192, 0]);123124 // And the real city agrees: bend-or's Basemap declares a surface — the125 // whole square, as it happens — and no bounds.126 const grid = tileGrid("bend-or");127 const basemap = raster(lensOf("bend-or", "2026-08-02", "Basemap"), grid);128 assert.deepEqual(extent(basemap.base.getExtent()), [0, -grid.size, grid.size, 0]);129});130131test("the resampling a lens declares is the resampling both sources are built with", () => {132 // Nearest-neighbour for pixel art, smooth for photographs — the lens says133 // which, and the seam only ever repeats it. Every corpus lens declares134 // `interpolate: true`, so the nearest-neighbour branch is exercised by the135 // invented hand-drawn sheet and by nothing real; that hole is the corpus's,136 // named in docs/render-seam.md, not the seam's.137 const mars = raster(lensOf("mars", "global", "MOLA Elevation"), tileGrid("mars"));138 assert.equal(mars.base.getSource()?.getInterpolate(), true, "a photograph, smoothly");139 assert.equal(mars.detail.getSource()?.getInterpolate(), true);140141 const city = gamePlane();142 const drawn = invented(city, "old-quarter", "Hand-Drawn");143 assert.equal(drawn.interpolate, false, "the invented sheet is pixel art");144 const pair = raster(drawn, city.tileGrid);145 assert.equal(pair.base.getSource()?.getInterpolate(), false,146 "magnified with bilinear smoothing, a drawing stops being a drawing");147 assert.equal(pair.detail.getSource()?.getInterpolate(), false);148});149150test("every lens, corpus and invented, gets the extent its own camera fits, on both layers", () => {151 const cases: [string, Lens, GridSpec][] = [];152 for (const slug of volumes()) {153 const grid = tileGrid(slug);154 for (const payload of payloads(slug).values()) {155 for (const lens of payload.lenses) cases.push([`${slug}/${lens.name}`, lens, grid]);156 }157 }158 for (const volume of [splitSheet(), gamePlane()]) {159 for (const [world, payload] of volume.worlds) {160 for (const lens of payload.lenses) {161 cases.push([`${volume.slug}/${world}/${lens.name}`, lens, volume.tileGrid]);162 }163 }164 }165 for (const [name, lens, grid] of cases) {166 const pair = raster(lens, grid);167 const window = [...lensExtent(lens, grid)];168 assert.deepEqual(extent(pair.base.getExtent()), window, name);169 assert.deepEqual(extent(pair.detail.getExtent()), window, name);170 assert.equal(backdrop(pair.base), lens.background, name);171 assert.equal(backdrop(pair.detail), undefined, name);172 }173 assert.equal(cases.length, 8,174 "three corpus lenses and five invented ones: every lens this suite knows");175});