SOURCE / PINNED RELEASE
Made of little things.
Atlas map viewer
- Release
- 1d2807e80b44…
- Author-recorded commit
- 43810ed2b368…
- License
- LICENSE
- Author’s source reference
- nostr://npub182jczunncwe0jn6frpqwq3e0qjws7yqqnc3auccqv9nte2dnd63scjm4rf/wss%3A%2F%2Fgit.napplet.soy%2F/n-4757dbabc86
Archive hash verified: 8db6876d96c460ac…. The source-to-build association is the author’s claim; it has not been independently rebuilt.
import { project, type Place } from "../../format/src/index";
import type { AtlasReader } from "../../format/src/reader";
import { bounds, inside, type Polygon } from "../../offline/src/geometry";
import { decodePlaces, encodePlaces, PLACE_LABEL_KEY } from "./places";
export const PLACE_PAGE_ZOOM = 8;
export async function placesInView(
reader: AtlasReader,
world: [number, number, number, number],
zoom: number,
) {
const bytes = await reader.block(PLACE_LABEL_KEY),
root = JSON.parse(new TextDecoder().decode(bytes));
if (root.schema !== "places-paged-v1") return decodePlaces(bytes);
if (root.zoom !== PLACE_PAGE_ZOOM)
throw new Error("Invalid settlement page grid");
if (zoom < 8) return decodePlaces(encodePlaces(root.overview));
const n = 2 ** PLACE_PAGE_ZOOM,
values: Place[] = [];
const blocks = await reader.spatial(
"labels/settlements",
PLACE_PAGE_ZOOM,
Math.max(0, Math.floor(world[0] * n)),
Math.min(n - 1, Math.floor(world[2] * n)),
Math.max(0, Math.floor(world[1] * n)),
Math.min(n - 1, Math.floor(world[3] * n)),
);
if (blocks.length > 256) throw new Error("Settlement viewport is too large");
for (const b of blocks)
values.push(...decodePlaces(await reader.block(b.key)));
return values;
}
export async function selectedPlaces(reader: AtlasReader, polygon: Polygon) {
const b = bounds(polygon),
nw = project(b[0], b[3]),
se = project(b[2], b[1]);
return (await placesInView(reader, [...nw, ...se], 8)).filter((p) =>
inside(p.coordinates, polygon),
);
}
/** Small view controller shared by standalone and composed renderers. */
export class PlacesView {
values: Place[] = [];
loading = false;
missing = false;
private signature = "";
private dead = false;
constructor(
readonly reader: AtlasReader,
readonly invalidate: () => void,
readonly error: (message: string) => void,
) {}
update(world: [number, number, number, number], zoom: number) {
if (!this.reader.index.blocks.some((b) => b.key === PLACE_LABEL_KEY)) {
this.missing = false;
return this.reader.index.manifest.places;
}
const sig =
zoom < 8
? "overview"
: world.map((v) => Math.floor(v * 2 ** PLACE_PAGE_ZOOM)).join(",");
if (!this.loading && sig !== this.signature) {
this.signature = sig;
this.loading = true;
this.missing = true;
void placesInView(this.reader, world, zoom)
.then((p) => {
if (!this.dead) {
this.values = p;
this.missing = false;
}
})
.catch((e) => {
if (!this.dead) this.error(String(e));
})
.finally(() => {
this.loading = false;
if (!this.dead) this.invalidate();
});
}
return this.values;
}
retry() {
this.signature = "";
}
destroy() {
this.dead = true;
}
}
