Back to Atlas map viewer
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.

packages/labels/src/place-pages.ts
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;
  }
}