clone

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

feat/atlas-next-horizon
atlas / render / data / payload.ts
payload.ts 6.1K
1// Presentation-facing types shared by the native model and renderers.2//3// The wire contract itself lives in semantic.ts and vnext.ts. The older4// document-shaped interfaces below remain only for extracted test fixtures;5// production has no endpoint or decoder for them.67import {8 KEY_LABEL_POLICY,9 KEY_RENDER_AS,10 KEY_STROKE_WIDTH_PX,11} from "@atlas/analysis/semconv/keys";1213/** `atlas.*` keys to their values, flat, as every entity carries them. */14export type Attrs = Readonly<Record<string, string>>;1516/** A rectangle in y-down world pixels, the space a lens declares itself in. */17export interface PixelRect {18 readonly x: number;19 readonly y: number;20 readonly width: number;21 readonly height: number;22}2324/** Sparse-level tile presence: format.md §6.3.1. */25export interface CoverageLevel {26 readonly x: number;27 readonly y: number;28 readonly w: number;29 readonly h: number;30 readonly bits: string;31}3233/** One raster pyramid picturing a world. */34export interface Lens {35 readonly id?: string;36 readonly name: string;37 /** Stable presentation key, derived from the semantic raster identity. */38 readonly tiles: string;39 /** Native blob path template; URLs substitute z/x/y/format directly. */40 readonly template?: string;41 readonly minZoom: number;42 readonly maxZoom: number;43 readonly fullZoom: number;44 readonly sourceZoom: number;45 readonly formats: readonly string[];46 readonly bounds?: PixelRect;47 readonly surface?: PixelRect;48 readonly interpolate: boolean;49 readonly background?: string;50 readonly shard?: number;51 readonly coverage?: Readonly<Record<string, CoverageLevel>>;52}5354/** A point in the volume's own world space, as payloads spell it. */55export interface LatLng {56 readonly lat: number;57 readonly lng: number;58}5960/** GeoJSON-shaped geometry in the volume's own world space. */61export interface Geometry {62 readonly type: string;63 readonly coordinates: unknown;64}6566/** An inline path or area feature. Point collections carry none. */67export interface ShapeFeature {68 readonly id: number;69 readonly title: string;70 readonly subtitle?: string;71 readonly hasText?: boolean;72 readonly parent?: number;73 readonly center?: LatLng;74 readonly shard?: number;75 readonly geometry: readonly Geometry[];76 readonly attrs?: Attrs;77}7879/** The kinds a collection declares. Every feature is exactly one. */80export type Kind = "point" | "path" | "area";8182/** One ordered group of features. The array's order is load-bearing. */83export interface Collection {84 /** The presentation layer identity; never a position or compatibility hash. */85 readonly id: string | number;86 readonly featureSet?: string;87 readonly style?: string;88 readonly title: string;89 readonly group?: string;90 readonly kind: Kind;91 readonly icon?: string;92 readonly iconAsset?: string;93 readonly iconPicture?: boolean;94 readonly color?: string;95 readonly iconColor?: string;96 readonly visible: boolean;97 readonly labelPolicy?: string;98 readonly renderAs?: string;99 readonly attrs?: Attrs;100 readonly features?: readonly ShapeFeature[];101}102103/** `worlds/<slug>.json`. */104export interface WorldPayload {105 readonly grid?: { readonly sourceZoom: number; readonly firstTile: number };106 readonly lenses: readonly Lens[];107 readonly collections: readonly Collection[];108 readonly attrs?: Attrs;109 readonly merged?: readonly unknown[];110}111112/** One entry of `worlds/<slug>.text`, fetched when a card opens. */113export interface TextEntry {114 readonly d?: string;115 readonly l?: readonly unknown[];116 readonly a?: Attrs;117}118119/** `worlds/<slug>.text`: feature id as a string, to its prose. */120export type TextPayload = Readonly<Record<string, TextEntry>>;121122/** The volume's world square and the window its worlds were cut from. */123export interface TileGrid {124 readonly sourceZoom: number;125 /** Native windows may begin at different source tile columns and rows. */126 readonly originX?: number;127 readonly originY?: number;128 /** Legacy square-window origin; native coordinate spaces do not use it. */129 readonly firstTile: number;130 readonly tileSize: number;131 readonly size: number;132 /** The authoritative coordinate-space extent, converted to y-up display coordinates. */133 readonly extent?: readonly [number, number, number, number];134}135136/** One world as the manifest lists it. */137export interface WorldEntry {138 readonly slug: string;139 readonly title: string;140 readonly parent?: string;141 readonly iconOutset?: string;142 readonly center: LatLng;143 readonly points: number;144 readonly paths: number;145 readonly areas: number;146 readonly updatedAt: string;147}148149/** One volume as `/data/catalog.json` lists it. */150export interface CatalogVolume {151 readonly slug: string;152 readonly title: string;153 readonly stamp: string;154 readonly base: string;155 readonly tileGrid: TileGrid;156 readonly worlds: readonly WorldEntry[];157}158159/** `/data/catalog.json`. Composed when asked for, never cached. */160export interface Catalog {161 readonly volumes: readonly CatalogVolume[];162 readonly bundlesDir: string;163}164165/** The extension of every tile at zoom `z`: `formats[z − minZoom]`. */166export function tileFormat(lens: Lens, z: number): string | null {167 return lens.formats[z - lens.minZoom] ?? null;168}169170/** The native template; the fallback exists only for hand-built test lenses. */171export function tileTemplate(lens: Lens): string {172 return lens.template ?? `tiles/${lens.tiles}/{z}/{x}/{y}.{format}`;173}174175/** The label policy an area collection curates, `always` when it says nothing. */176export function labelPolicy(collection: Collection): "always" | "quiet" {177 return (collection.labelPolicy || collection.attrs?.[KEY_LABEL_POLICY]) === "quiet" ? "quiet" : "always";178}179180/** How a point collection draws: markers, or floating text. Absent means pin. */181export function renderAs(collection: Collection): "pin" | "text" {182 return (collection.renderAs || collection.attrs?.[KEY_RENDER_AS]) === "text" ? "text" : "pin";183}184185/** The ground width of a path collection's features, in world pixels. */186export function strokeWidth(collection: Collection): number {187 const declared = Number(collection.attrs?.[KEY_STROKE_WIDTH_PX]);188 return Number.isFinite(declared) && declared > 0 ? declared : 0;189}