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/objects/src/index.ts
import type { Geometry } from "geojson";
import booleanIntersects from "@turf/boolean-intersects";
import type { AtlasReader } from "../../format/src/reader";
import {
  boxValid,
  tagTerm,
  PACK_BYTES,
  CHUNK_BYTES,
  objectId,
  overlap,
  splitBounds,
  validKey,
  type Box,
  type ObjectCatalog,
  type ObjectRecord,
  type Page,
} from "./model";
export * from "./model";
export const hasObjects = (r: AtlasReader) =>
  r.index.manifest.capabilities.includes("source-objects");
function checkedPage(p: Page) {
  if (
    !p ||
    !validKey(p.key) ||
    !/^objects\/(records|spatial|lookup|ids|tag-values|tag-index|tag-pages)\//.test(
      p.key,
    ) ||
    typeof p.unlocated !== "boolean" ||
    !Array.isArray(p.bounds) ||
    !p.bounds.every(boxValid) ||
    !Number.isSafeInteger(p.count) ||
    p.count < 0 ||
    !Array.isArray(p.keys) ||
    !p.keys.every((x) => typeof x === "string") ||
    !Array.isArray(p.facets) ||
    !p.facets.every((x) => typeof x === "string")
  )
    throw new Error("Invalid object index page");
  return p;
}
export async function objectCatalog(r: AtlasReader): Promise<ObjectCatalog> {
  if (!hasObjects(r))
    throw new Error(
      "This archive has no general source-object data; its display layers cannot recover omitted objects.",
    );
  const c = JSON.parse(
    new TextDecoder().decode(await r.block("objects/index")),
  );
  if (
    !["source-objects-v1", "source-objects-v2"].includes(c?.schema) ||
    !Number.isSafeInteger(c.count) ||
    c.count < 0 ||
    c.report?.schema !== "retention-v1"
  )
    throw new Error("Invalid object catalog");
  checkedPage(c.spatial);
  checkedPage(c.lookup);
  if (c.schema === "source-objects-v2") checkedPage(c.tags);
  if (c.spatial.count !== c.count || c.lookup.count !== c.count)
    throw new Error("Object catalog count mismatch");
  return c;
}
async function page(r: AtlasReader, p: Page, signal?: AbortSignal) {
  signal?.throwIfAborted();
  checkedPage(p);
  const b = await r.block(p.key);
  signal?.throwIfAborted();
  if (b.length > 4 * 1024 ** 2)
    throw new Error("Object index page exceeds 4 MiB");
  const v = JSON.parse(new TextDecoder().decode(b));
  if (
    !Array.isArray(v.rows) ||
    v.schema !== `objects-${p.key.split("/")[1]}-v1` ||
    v.rows.length > 256
  )
    throw new Error("Invalid object page");
  return v;
}
function validateRecord(v: ObjectRecord): ObjectRecord {
  const blob = (b: any, kind: string) =>
    !b ||
    (Array.isArray(b.blocks) &&
      (b.offset === undefined
        ? b.blocks.length === Math.ceil(b.bytes / CHUNK_BYTES)
        : b.blocks.length === 1 &&
          Number.isSafeInteger(b.offset) &&
          b.offset >= 0 &&
          b.offset + b.bytes <= PACK_BYTES) &&
      b.blocks.every(
        (k: any) => validKey(k) && k.startsWith(`objects/${kind}/`),
      ) &&
      Number.isSafeInteger(b.bytes) &&
      b.bytes > 0 &&
      b.bytes <= 768 * 1024 ** 2);
  if (
    !v ||
    !objectId(v.id) ||
    typeof v.name !== "string" ||
    !v.tags ||
    Array.isArray(v.tags) ||
    Object.values(v.tags).some((x) => typeof x !== "string") ||
    !Array.isArray(v.bounds) ||
    !v.bounds.every(boxValid) ||
    !Array.isArray(v.facets) ||
    !v.facets.every((x) => typeof x === "string") ||
    ![
      "complete",
      "missing-nodes",
      "incomplete-area",
      "relation-members",
    ].includes(v.geometryStatus) ||
    typeof v.dependencyOnly !== "boolean" ||
    !blob(v.geometry, "geometry") ||
    !blob(v.topology, "topology")
  )
    throw new Error("Invalid object metadata");
  return v;
}
export interface ObjectQuery {
  bounds?: Box;
  facet?: string;
  tag?: { key: string; value?: string };
  text?: string;
  includeDependencies?: boolean;
  includeUnlocated?: boolean;
}
export async function* objectRecords(
  r: AtlasReader,
  q: ObjectQuery = {},
  signal?: AbortSignal,
  progress: (s: string) => void = () => {},
  onPage: (key: string) => Promise<void> = async () => {},
) {
  await onPage("objects/index");
  const root = await objectCatalog(r),
    windows = q.bounds ? splitBounds(q.bounds) : undefined;
  let pages = 0;
  const seen = new Set<string>();
  const visit = async function* (p: Page): AsyncGenerator<ObjectRecord> {
    signal?.throwIfAborted();
    checkedPage(p);
    if (seen.has(p.key)) throw new Error("Cyclic object index");
    seen.add(p.key);
    if (
      windows &&
      !(q.includeUnlocated && p.unlocated) &&
      !p.bounds.some((b) => windows.some((w) => overlap(w, b)))
    )
      return;
    if (q.tag && !p.keys.includes(q.tag.key)) return;
    if (q.facet && !p.facets.includes(q.facet)) return;
    await onPage(p.key);
    const v = await page(r, p, signal);
    progress(`Reading source objects · ${++pages} index/attribute pages`);
    if (["objects-spatial-v1", "objects-tag-pages-v1"].includes(v.schema)) {
      for (const child of v.rows) yield* visit(child);
      return;
    }
    if (v.schema !== "objects-records-v1")
      throw new Error("Unexpected object spatial page");
    for (const input of v.rows) {
      const row = validateRecord(input);
      if (!q.includeDependencies && row.dependencyOnly) continue;
      if (
        windows &&
        !(q.includeUnlocated && !row.bounds.length) &&
        !row.bounds.some((b) => windows.some((w) => overlap(w, b)))
      )
        continue;
      if (q.facet && !row.facets.includes(q.facet)) continue;
      if (
        q.tag &&
        (!Object.hasOwn(row.tags, q.tag.key) ||
          (q.tag.value !== undefined && row.tags[q.tag.key] !== q.tag.value))
      )
        continue;
      if (q.text) {
        const text = Object.entries(row.tags)
          .filter(
            ([k]) =>
              k.includes("name") ||
              ["ref", "wikidata", "ISO3166-1", "ISO3166-1:alpha2"].includes(k),
          )
          .map(([, v]) => v)
          .join(" ")
          .normalize("NFKD")
          .toLowerCase();
        if (
          !q.text
            .normalize("NFKD")
            .toLowerCase()
            .split(/\s+/)
            .every((t) => text.includes(t))
        )
          continue;
      }
      yield row;
    }
  };
  let selected: Page | undefined = root.spatial;
  const compact: Page[] = [];
  if (q.tag?.value !== undefined && root.schema === "source-objects-v2") {
    const term = tagTerm(q.tag.key, q.tag.value),
      visited = new Set<string>();
    const lookup = async (p: Page): Promise<Page | undefined> => {
      signal?.throwIfAborted();
      checkedPage(p);
      if (p.min !== undefined && (term < p.min || term > p.max!)) return;
      if (visited.has(p.key)) throw new Error("Cyclic tag-value index");
      visited.add(p.key);
      await onPage(p.key);
      const v = await page(r, p, signal);
      progress(`Finding tag value · ${++pages} index pages`);
      if (v.schema === "objects-tag-index-v1") {
        for (const child of v.rows) {
          const hit = await lookup(child);
          if (hit) return hit;
        }
        return;
      }
      if (v.schema !== "objects-tag-values-v1")
        throw new Error("Unexpected tag-value index page");
      const found = v.rows.find((row: any) => row.term === term);
      if (!found) return;
      if (found.root) return checkedPage(found.root);
      if (
        !Array.isArray(found.pages) ||
        found.pages.length > 8 ||
        !found.pages.every(
          (key: unknown) =>
            validKey(key) && String(key).startsWith("objects/records/"),
        )
      )
        throw new Error("Invalid tag-value postings");
      // Short lists avoid repeating attribute-page summaries for every unique name/address.
      for (const key of found.pages) {
        // Records still apply exact attribute and geographic filters; the synthetic
        // envelope only lets this compact posting reach the normal record visitor.
        compact.push({
          key,
          bounds: [[-180, -90, 180, 90]],
          unlocated: true,
          keys: [q.tag!.key],
          facets: q.facet ? [q.facet] : [],
          count: 0,
        });
      }
      return;
    };
    selected = await lookup(root.tags!);
  }
  if (selected) yield* visit(selected);
  for (const p of compact) yield* visit(p);
}
export async function findObject(
  r: AtlasReader,
  id: string,
  signal?: AbortSignal,
): Promise<ObjectRecord | undefined> {
  if (!objectId(id)) throw new Error("Use an exact OSM object identity");
  const c = await objectCatalog(r);
  const seen = new Set<string>();
  const visit = async (p: Page): Promise<ObjectRecord | undefined> => {
    if ((p.min && id < p.min) || (p.max && id > p.max)) return;
    if (seen.has(p.key)) throw new Error("Cyclic object ID index");
    seen.add(p.key);
    const v = await page(r, p, signal);
    if (v.schema === "objects-lookup-v1") {
      for (const child of v.rows) {
        const found = await visit(child);
        if (found) return found;
      }
      return;
    }
    if (v.schema !== "objects-ids-v1")
      throw new Error("Unexpected object ID index");
    const hit = v.rows.find((x: any) => x.id === id);
    if (!hit) return;
    if (!validKey(hit.page) || !hit.page.startsWith("objects/records/"))
      throw new Error("Invalid object record pointer");
    const records = JSON.parse(
      new TextDecoder().decode(await r.block(hit.page)),
    );
    if (records.schema !== "objects-records-v1" || !Array.isArray(records.rows))
      throw new Error("Invalid object record page");
    const found = records.rows.find((x: any) => x.id === id);
    if (!found) throw new Error("Object index identity mismatch");
    return validateRecord(found);
  };
  return visit(c.lookup);
}
async function blob(
  r: AtlasReader,
  ref: NonNullable<ObjectRecord["geometry"]>,
  signal?: AbortSignal,
) {
  if (ref.offset !== undefined) {
    signal?.throwIfAborted();
    const packed = await r.block(ref.blocks[0]!);
    signal?.throwIfAborted();
    if (packed.length > PACK_BYTES || ref.offset + ref.bytes > packed.length)
      throw new Error("Invalid packed object slice");
    return JSON.parse(
      new TextDecoder().decode(
        packed.subarray(ref.offset, ref.offset + ref.bytes),
      ),
    );
  }
  const output = new Uint8Array(ref.bytes);
  let at = 0;
  for (const key of ref.blocks) {
    signal?.throwIfAborted();
    const b = await r.block(key);
    if (b.length > 1024 ** 2 || at + b.length > output.length)
      throw new Error("Invalid object payload size");
    output.set(b, at);
    at += b.length;
  }
  if (at !== output.length) throw new Error("Incomplete object payload");
  return JSON.parse(new TextDecoder().decode(output));
}
export async function objectGeometry(
  r: AtlasReader,
  row: ObjectRecord,
  signal?: AbortSignal,
): Promise<Geometry | undefined> {
  validateRecord(row);
  if (!row.geometry) return;
  const g: Geometry = await blob(r, row.geometry, signal);
  const { geometryBounds } = await import("./model");
  if (JSON.stringify(geometryBounds(g)) !== JSON.stringify(row.bounds))
    throw new Error("Object geometry extent mismatch");
  return g;
}
export async function objectTopology(
  r: AtlasReader,
  row: ObjectRecord,
  signal?: AbortSignal,
) {
  validateRecord(row);
  return row.topology ? blob(r, row.topology, signal) : {};
}
/** Dateline-aware local planar intersection. Storage supports poles; polar cap polygon interpretation is not inferred. */
export function intersectsGlobal(g: Geometry, bounds: Box) {
  const unwrap = (v: Geometry): Geometry => {
    const line = (ps: number[][], anchor?: number) => {
      let prev = anchor ?? ps[0]![0]!;
      return ps.map((p) => {
        let x = p[0]!;
        while (x - prev > 180) x -= 360;
        while (x - prev < -180) x += 360;
        prev = x;
        return [x, p[1]!];
      });
    };
    switch (v.type) {
      case "Point":
      case "MultiPoint":
        return v;
      case "LineString":
        return { ...v, coordinates: line(v.coordinates) };
      case "MultiLineString":
        return { ...v, coordinates: v.coordinates.map((p) => line(p)) };
      case "Polygon":
        return {
          ...v,
          coordinates: v.coordinates.map((p) =>
            line(p, v.coordinates[0]![0]![0]),
          ),
        };
      case "MultiPolygon":
        return {
          ...v,
          coordinates: v.coordinates.map((poly) =>
            poly.map((p) => line(p, poly[0]![0]![0])),
          ),
        };
      case "GeometryCollection":
        return { ...v, geometries: v.geometries.map(unwrap) };
    }
  };
  const u = unwrap(g);
  return splitBounds(bounds).some((b) =>
    [-360, 0, 360].some((d) =>
      booleanIntersects(u, {
        type: "Polygon",
        coordinates: [
          [
            [b[0] + d, b[1]],
            [b[2] + d, b[1]],
            [b[2] + d, b[3]],
            [b[0] + d, b[3]],
            [b[0] + d, b[1]],
          ],
        ],
      }),
    ),
  );
}
export async function* readObjects(
  r: AtlasReader,
  bounds: Box,
  geometry = true,
  signal?: AbortSignal,
  predicate: (f: any) => boolean = () => true,
  progress?: (s: string) => void,
  q: ObjectQuery = {},
) {
  for await (const row of objectRecords(
    r,
    { ...q, bounds },
    signal,
    progress,
  )) {
    const f = {
      type: "Feature" as const,
      id: row.id,
      bbox: row.bounds[0],
      properties: {
        id: row.id,
        name: row.name,
        kind: "objects" as const,
        tags: row.tags,
        facets: row.facets,
        geometryStatus: row.geometryStatus,
      },
    };
    if (!predicate(f) || !row.geometry) continue;
    const contained = row.bounds.every((b) =>
      splitBounds(bounds).some(
        (w) => w[0] <= b[0] && w[1] <= b[1] && w[2] >= b[2] && w[3] >= b[3],
      ),
    );
    const g =
      geometry || !contained ? await objectGeometry(r, row, signal) : undefined;
    if (g && !intersectsGlobal(g, bounds)) continue;
    yield { ...f, ...(geometry ? { geometry: g! } : {}) };
  }
}