mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-13 14:52:19 +00:00
Add the Data Library: live regional datasets, tiles and packs
DATA LAYERS keeps its curated core layers; regional and niche sources live in the Data Library, opened from Browse data library at the foot of the panel. Each entry is a JSON manifest in src/library/entries/ pointing at a live upstream (query API, provider-hosted file or tile endpoint), so nothing is bundled and a self-hosted instance hosts no data. Sources without CORS go through one allowlisted /api/library route. Kinds: dataset (GeoJSON, CSV, JSON records, ArcGIS, partitioned GeoParquet), tiles (XYZ, WMS/ArcGIS bbox export, terrarium relief) and pack. Selections persist and travel in share links as lib=, separate from the core layer tokens. Library rows get Zoom and Remove icon chips that work while the layer is off, rendered through RailChips. Strings are translated in every locale.
This commit is contained in:
1 parent
10a788db78
commit
db80d82b8d
182 files changed
+15805
-44
No files matched your search
@@ -384,6 +384,206 @@ provider registration rejects compositions that cannot fit rather than truncatin
|
||||
identities. A malformed or duplicate entry invalidates the complete custom-switch
|
||||
field. Existing whole-link size limits still apply.
|
||||
|
||||
## Data Library — October 21, 2026
|
||||
|
||||
DATA LAYERS keeps its curated core layers; everything else lives in the Data
|
||||
Library, opened from **Browse data library** at the foot of the panel. An entry
|
||||
is a manifest in `src/library/entries/` that points at a live upstream its
|
||||
provider hosts — a query API, a provider-hosted static file or a tile endpoint.
|
||||
Nothing an entry describes is bundled, so a self-hosted instance serves every
|
||||
entry without hosting data. Manifests are validated strictly when
|
||||
`src/library/catalog.js` loads (unknown fields are errors), so an invalid entry
|
||||
fails the unit suite. Authoring guide: [DATA-LIBRARY.md](DATA-LIBRARY.md).
|
||||
|
||||
Three kinds ship. A `dataset` (GeoJSON, GeoJSON Lines, CSV with lat/lon
|
||||
columns, a JSON record list read from `source.records` with named or guessed
|
||||
lat/lon fields, or a paged ArcGIS FeatureServer query) renders through the same
|
||||
GeoJSON layer as Datacenters and Dams, with the manifest's label, up to two
|
||||
card fields and an optional `rank` field that orders a capped set and feeds the
|
||||
layer's level-of-detail priority (0–600, below the named/unnamed gap). A capped
|
||||
dataset reads PARTIAL. A dataset with `source.view` is view-bounded,
|
||||
for layers too large to load whole (FEMA USA Structures: ~135 million
|
||||
footprints): below `view.maxAltitudeM` it queries ArcGIS for what intersects a
|
||||
window around the point the camera looks at (half the camera height each way,
|
||||
330 m – 5.5 km, clipped to the visible rectangle), one page per view after the
|
||||
camera settles, replacing the previous set only when the new one is ready and
|
||||
aborting a superseded request; above it the row reads "zoom in to load this
|
||||
layer" and nothing is fetched. Footprints are ground-clamped polygons (draped on
|
||||
Google 3D tiles too), without outlines. Zoom to layer lands straight above the
|
||||
manifest's `view.sample` area at 60 % of the altitude limit. View-bounded
|
||||
entries are ArcGIS or GeoParquet, and browser-direct, in this build.
|
||||
|
||||
`geoparquet` datasets are read in the browser with `hyparquet` (with `fzstd`
|
||||
for ZSTD), imported lazily on first use; only the columns an entry uses are
|
||||
decoded, and WKB geometry arrives as GeoJSON. A partitioned set
|
||||
(`source.partitions`: an S3 ListObjectsV2 `listUrl` and a `keyBase`, both on the
|
||||
source host) must be view-bounded. Its index — each file's GeoParquet footer
|
||||
bbox, read with one range request per file — comes from `/api/library/index/<id>`
|
||||
(built once per `cacheSeconds` and shared), or is built in the browser and kept
|
||||
in `gev:library:index:<id>` on a static build. Footer bboxes can be stretched
|
||||
by stray geocodes, so candidates are ranked by how much of each file's bbox
|
||||
the view covers, read in parallel batches of four (up to eight per view)
|
||||
until a batch has features in view, and each file read once teaches its real
|
||||
extent (the box holding 98 % of its points, `gev:library:extent:<id>`), so
|
||||
later views skip files that do not cover them. The last four decoded files
|
||||
stay in memory. `display.colorBy` colours each feature by a field's value (a
|
||||
dotted path such as `categories.0.title` is flattened into the feature's
|
||||
`__gevColorKey`): view-bounded layers draw 6 px dots or filled polygons, other
|
||||
datasets keep the GeoJSON layer's stems, cards and level of detail with a
|
||||
per-feature colour (`featureColor` in `localGeojsonCore.js`). Values sharing a
|
||||
`colorBy.labels` label form one legend group (coal, gas and oil are "Fossil");
|
||||
values the manifest does not colour draw in `display.color` and count as
|
||||
Other. The row legend lists each group with its count (in view, or in the
|
||||
loaded set), one item per line as Fire Perimeters' does. Status-like fields
|
||||
(GDACS and PortWatch alert levels, NWS severity, bridge condition) use the
|
||||
fixed status palette (good #0ca30c, warning #fab219, serious #ec835a,
|
||||
critical #d03b3b); categorical ones (fuel, EONET category, building
|
||||
occupancy) use at most four hues that pass all-pairs colour-vision checks on
|
||||
the dark globe, plus a neutral Other. Tile layers carry a static
|
||||
`display.legend` copied from the provider's own scale (GBIF classic.poly,
|
||||
JRC occurrence, GIBS GPW, Macrostrat's ICS period colours); EMODnet publishes
|
||||
no machine-readable scale and has none. Aerial photo layers (PDOK, IGN, GSI
|
||||
photos, swisstopo SWISSIMAGE) set `display.outline`: their covered countries'
|
||||
borders draw as a thin white ground-clamped line, so the imagery's edge is
|
||||
plain, with a "Coverage outline" legend item. Borders come from Natural
|
||||
Earth's 1:50m admin-0 countries (public domain, fetched live once per page and
|
||||
credited); `countries` draws every part (Japan's islands, Liechtenstein beside
|
||||
Switzerland), `coverage` only the parts inside `coverage.bbox` (the European
|
||||
Netherlands, metropolitan France). A failed border fetch costs only the
|
||||
outline. The same layers set `display.aerial`: they drape above every other
|
||||
imagery layer and raise themselves again whenever another drape is added, so a
|
||||
basemap or weather overlay switched on later goes underneath. Each is
|
||||
`source.bounded` (tiles requested inside `coverage.bbox` only) and names its
|
||||
provider's no-imagery fill in `display.noDataColor`, drawn transparent through
|
||||
Cesium's colour-to-alpha (max channel distance 0.02): measured fills are PDOK
|
||||
and swisstopo white, IGN's flat ocean blue (#0a3b59) and GSI's ocean grey
|
||||
(#343434). Without both, PDOK's white fill covered the whole globe. A 4xx tile
|
||||
answer counts as "no imagery here", never as a tile error. IGN's overseas
|
||||
departments are outside its single bbox and are not shown. The aerial set now holds 68
|
||||
entries: 64 imported from the Open Imagery Register (national programmes in
|
||||
Austria, Belgium, Germany, Estonia, Spain, Luxembourg, Poland, Portugal,
|
||||
Geneva, Australia's NSW, and 51 US services: NAIP plus state, county and
|
||||
city programmes) plus PDOK, IGN, GSI and SWISSIMAGE, each with an `imagery`
|
||||
block (type, published resolution, true ortho, years flown, refresh) that
|
||||
Browse shows as a spec list. Tiles take `xyz` (with `{reverseY}` for TMS) or
|
||||
`bbox` templates (`{bbox-epsg-3857}`: WMS GetMap and ArcGIS export, filled
|
||||
by Cesium in the browser and by the proxy from z/x/y). Sub-national entries
|
||||
draw a `bbox` rectangle outline without fetching the country borders. 35
|
||||
entries whose licence the register gives only as "see publisher terms" are
|
||||
operator opt-in. Norway comes from Kartverket's open
|
||||
services (CC BY 4.0, "© Kartverket"; its zoom 12–20 Geovekst data may be shown
|
||||
in live services but not copied): the topographic, greyscale, raster-topo and
|
||||
nautical-chart caches (`xyz`), and WMS hillshade, DTM colour relief, sea depths
|
||||
(from zoom 11, Kartverket's 16-class depth legend), county and municipality
|
||||
boundaries, and the cadastre (from zoom 14). Norge i bilder needs
|
||||
authentication and is not included. `mapterhorn-terrain` is the first
|
||||
`source.encoding: terrarium` entry: Mapterhorn's global 512 px terrain-RGB
|
||||
tiles are decoded per tile in the browser (`src/library/relief.js`) into
|
||||
shaded relief with a six-class elevation legend in metres. A `tiles` entry (`{z}/{x}/{y}` template) drapes on the
|
||||
active imagery host — the globe, or the Google 3D tileset — and follows map
|
||||
stack switches; with no host it shows the existing "choose a globe map"
|
||||
guidance, and its row reads DRAPED rather than a count. A `pack` describes an
|
||||
existing host-layer source pack (today the opt-in CCTV packs): it is
|
||||
informational in Browse, names the host's own switch (`CCTV_NYC_ENABLED=1` and
|
||||
so on) and offers no Add. The `provider` kind from the RFC is refused by the
|
||||
validator until it ships.
|
||||
|
||||
Entries register their layer only when a viewer adds them, after the core
|
||||
catalog is sealed: `LayerLifecycle.registerLibraryLayer` accepts only ids in
|
||||
the `lib-<entry id>` namespace and only after `finalizeRegistrations`, and
|
||||
`unregisterLibraryLayer` destroys them. Library layers never enter
|
||||
`LAYER_STATE_REGISTRY` or the core token ledger. The panel groups them under a
|
||||
**Library** heading after every core group and rebuilds its rows on a
|
||||
`registrations` activity. Add switches the layer on; Remove destroys it.
|
||||
A library layer's row carries two icon chips, on or off (the panel shows
|
||||
row controls for an off layer only for Data Library rows, whose chips are
|
||||
marked `whileOff`): **Zoom to layer**
|
||||
(`zoom_in_map`) flies to the extent of the loaded features, or a tiles entry's
|
||||
declared coverage, taking the shorter way round the antimeridian and showing the
|
||||
whole globe for world-scale sets; **Remove from Data Layers** (`delete`)
|
||||
removes the entry. Zoom on a layer that is off switches it on first (a
|
||||
dataset only knows its extent once loaded). Zoom runs as an immediate
|
||||
navigation (`layer`), so Cockpit
|
||||
refuses it with its own message and the current follow owner is released first;
|
||||
reduced motion flies instantly. Icon chips (`chipGroup.js` `icon`/`action`) use
|
||||
their label as accessible name and tooltip and carry no `aria-pressed`.
|
||||
|
||||
This browser's selection persists in `gev:library:v1` (`{ added, on }`), written
|
||||
only for explicit (user, voice, tool) changes, like core layer state, so a
|
||||
Context session's temporary changes are not remembered. Share links carry the
|
||||
switched-on entries as `lib=<t>.<t>` with the permanent two-character tokens in
|
||||
`src/library/ledger.json` (append-only; packs take none). A link's entries join
|
||||
the viewer's library and, as for core layers, the link alone decides what is
|
||||
on. Unknown tokens are skipped. The live hash follows the existing
|
||||
initial-restore suppression.
|
||||
|
||||
Browser-direct entries are fetched without cookies or a referrer, redirects
|
||||
refused, bodies capped while streaming. Proxied entries go through
|
||||
`/api/library` (`server/providers/library.js`): `/catalog` reports each entry's
|
||||
availability and reason (`operator-opt-in`, `operator-disabled`, `key-missing`)
|
||||
without secrets; `/data/<id>` and `/tile/<id>/<z>/<x>/<y>` reach only the
|
||||
manifest's fixed upstream (the browser names an id, never a URL), refuse
|
||||
redirects and off-manifest hosts, cap dataset bodies at the manifest's
|
||||
`maxBytes` (≤ 32 MiB) and tiles at 2 MiB of `image/png|jpeg|webp`, cache per the
|
||||
manifest's `cacheSeconds` (60 s – 24 h; at most 512 tiles), serve the last good
|
||||
dataset as stale on failure, rate-limit per client, and never return upstream
|
||||
text. A keyed entry must be proxied; its key comes from the server environment
|
||||
through the key registry and its routes use the strict cross-site gate, while
|
||||
keyless routes use the keyless gate so they work behind a reverse proxy.
|
||||
Operators opt entries in with `GEV_LIBRARY_ENABLE` and remove them with
|
||||
`GEV_LIBRARY_DISABLE`. A static build with no server still serves every
|
||||
browser-direct, non-opt-in entry; proxied ones say they need the app's server.
|
||||
|
||||
The Browse dialog is a `dialog[data-panel-surface]`: modal, labelled, search
|
||||
focused on open, Escape and backdrop dismiss and return focus to Browse, and it
|
||||
closes when clean view, recording or Cockpit hides the panels. Its surface is
|
||||
frosted glass (a lighter `--glass-bg` than the rails, a 22 px backdrop blur
|
||||
clipped to the theme's frame, a barely dimmed backdrop), so the globe shows
|
||||
through without the HUD text behind it competing. An added entry that is on
|
||||
offers ZOOM, which closes the dialog and flies to it. Above the intro, four
|
||||
stat tiles summarise the library: Datasets (every entry, broken down by kind),
|
||||
Themes (how many distinct themes the entries cover), Added (and how many are on
|
||||
the map), and Map features (features loaded by dataset layers that are on;
|
||||
tiles have no count).
|
||||
They refresh with the dialog and once a second while it is open, so counts
|
||||
land as datasets finish loading. The line beside **REMOVE ALL** answers the
|
||||
filter ("Showing 2 of 8"). **REMOVE ALL** removes every added entry; its first
|
||||
press arms a confirm for four seconds. It lists
|
||||
addable entries first, then entries this instance would need to switch on, then
|
||||
packs, with kind, status, opt-in or key badges, provider, coverage and a licence
|
||||
link, and filters by text, theme and "only what this instance serves".
|
||||
Archived entries are hidden unless added. An entry's attribution and licence are
|
||||
registered in the Data attribution popover while it is in use.
|
||||
|
||||
Seeds: NASA EONET open events (GeoJSON, browser-direct), EIA US power plants
|
||||
≥ 100 MW (ArcGIS, browser-direct, ranked by capacity), Smithsonian GVP Holocene
|
||||
volcanoes (WFS GeoJSON, proxied — the upstream sends no CORS headers), NASA
|
||||
Black Marble 2016 night lights and OpenSeaMap seamarks (tiles), and the NYC,
|
||||
Lithuania and FL511 CCTV packs. Added from the Ask dataset registry (its live,
|
||||
mappable sources that are not core layers): the WRI Global Power Plant Database
|
||||
(provider-hosted CSV, the 10,000 largest of ~35,000 by capacity, so it reads
|
||||
PARTIAL), NWS active alerts that carry their own polygon (zone-wide alerts have
|
||||
no geometry and are dropped), NOAA CO-OPS tide stations (JSON records), and IMF
|
||||
PortWatch chokepoints and port disruptions (ArcGIS, operator opt-in until the
|
||||
reuse terms are confirmed, as Ask holds them in hosted mode). FEMA/ORNL USA
|
||||
Structures (CC BY 4.0) is the first view-bounded entry; its service refuses a
|
||||
query page smaller than its own 2,000 records, so the probe asks for a full
|
||||
page. Addresscloud's EPC ratings for England and Wales (Source Cooperative,
|
||||
read from `s3.us-west-2.amazonaws.com/us-west-2.opendata.source.coop`, 344
|
||||
local-authority GeoParquet files, OGL v3.0) are the first partitioned
|
||||
GeoParquet entry, coloured by band A–G with the official EPC colours. From an
|
||||
open/freemium registry review (commercial sources excluded): GDACS disaster
|
||||
alerts; the National Bridge Inventory (view-bounded, coloured Good/Fair/Poor);
|
||||
EPA Superfund sites and Toxics Release Inventory facilities (view-bounded); and
|
||||
tile overlays for Macrostrat geology, GBIF biodiversity density, USGS
|
||||
topographic and hydrography maps, PDOK (Netherlands) and IGN (France) aerial
|
||||
photos, BKG TopPlusOpen, GSI Japan map and photos, EMODnet bathymetry, SEDAC
|
||||
GPW 2020 population density (legend from the GIBS colour map) and JRC surface
|
||||
water occurrence. `npm run library:check` validates manifests,
|
||||
the ledger (append-only with `--base-ref`, run in CI on pull requests) and the
|
||||
generated DATA_SOURCES.md section; `npm run library:probe` hits every upstream
|
||||
once; `npm run qa:data-library` is the browser gate (fixture-backed).
|
||||
|
||||
## Utilities panel — October 29, 2026
|
||||
|
||||
UTILITIES is its own left-side panel, between Data Layers and Scenes, with
|
||||
|
||||
Reference in new issue
Block a user