Skip to content

feat(cog): explicit value/range classes for single-band styling - #1

Merged
pergerch merged 8 commits into
mainfrom
vnext
Aug 21, 2026
Merged

feat(cog): explicit value/range classes for single-band styling#1
pergerch merged 8 commits into
mainfrom
vnext

Conversation

@pergerch

Copy link
Copy Markdown
Member

color.classes styles a COG by exact pixel values and/or [from, to)
ranges with hand-picked hex colors — the ramp grid never fit categorical
or binary rasters. The highest "to" is inclusive so the data maximum
stays styled; unmatched and noData pixels render transparent. Rendering
goes through the protocol's per-URL color function (refcounted, cleared
on unmount) and the legend derives one labelled swatch per class.

Legend swatches now composite over white, so a semi-transparent class
color reads like it does over a light basemap instead of mixing with
the dark panel.

The COG demo splits into three focused maps, each with its config
underneath: imagery (a URL is enough), terrain (one DEM as hillshade +
hypsometric ramp), and classification (a binary sealed-surfaces raster
via classes). The tarball gate now exercises the terrain config, which
needs no remote data.

color.classes styles a COG by exact pixel values and/or [from, to)
ranges with hand-picked hex colors — the ramp grid never fit categorical
or binary rasters. The highest "to" is inclusive so the data maximum
stays styled; unmatched and noData pixels render transparent. Rendering
goes through the protocol's per-URL color function (refcounted, cleared
on unmount) and the legend derives one labelled swatch per class.
Legend swatches now composite over white, so a semi-transparent class
color reads like it does over a light basemap instead of mixing with
the dark panel.
The COG demo splits into three focused maps, each with its config
underneath: imagery (a URL is enough), terrain (one DEM as hillshade +
hypsometric ramp), and classification (a binary sealed-surfaces raster
via classes). The tarball gate now exercises the terrain config, which
needs no remote data.
The folder is map0.net — landing page, demo gallery and the configs they
run on — not a collection of code samples, and everything around it already
said "site": `pnpm build:site`, `dist-site/`, `deploy-site.yml`.
Pure rename plus the paths that pointed at it: package.json scripts,
.gitignore, the release-it hook, the tarball gate, the schema sync test,
stub-aliases, README and docs.
`theme.mode` in the config suits a map that owns the light/dark decision,
but a host page with its own toggle knows better than the config can.
`theme="dark"` / `theme="light"` on the element wins over `theme.mode`, and
flipping the attribute restyles a running map — the media-query listener of
the "auto" mode is dropped and re-attached rather than leaking per flip.
Unset, nothing changes: the config decides as before.
The site already followed the OS colour scheme; this makes the choice
overridable. An inline head script applies a stored preference before first
paint, so a dark-mode visitor never sees a white flash. With nothing stored
the media queries keep deciding, live.
CSS carries the dark tokens twice — `@media (prefers-color-scheme: dark)`
for the OS default, `:root.dark` for an explicit choice — because a single
block cannot serve both selectors. Embedded maps follow through the
viewer's new `theme` attribute.
The English page stays the only source: elements carry `data-i18n` keys and
keep their English text inline, and one JSON catalogue per page under
site/i18n/de/ holds the German. A Vite plugin emits every page a second time
under /de/ — `lang="de"`, rewritten internal links, hreflang pairs — and
serves the same on the fly in dev.
Visitors arrive in their browser's language through a one-time redirect on
/, and the topbar switcher persists the choice in localStorage: the
behaviour spatial-focus.net gets from @nuxtjs/i18n, without a framework and
without runtime cost. The build warns about missing and stale keys, so the
German pages cannot silently drift behind an edit to the English copy.
Strings that scripts render (gallery, pager, copy buttons, validator status)
read the page language instead. The demo count on the landing page now comes
from the demo registry — it had been stale at 19 — and the privacy page
names the two preferences the site now keeps in localStorage.
@github-actions

Copy link
Copy Markdown

Azure Static Web Apps: Your stage site is ready! Visit it here: https://brave-mud-0c9fbf503-1.westeurope.7.azurestaticapps.net

@github-actions

Copy link
Copy Markdown

Azure Static Web Apps: Your stage site is ready! Visit it here: https://brave-mud-0c9fbf503-1.westeurope.7.azurestaticapps.net

The sealed-surfaces layer now carries its own attribution and a metadata
link back to data.gv.at, so the demo credits the dataset the way the licence
asks for.
The imprint names both Austrian datasets the COG demos redistribute under
CC BY 4.0 — the ÖROK-Monitoring sealed-surfaces raster and the BEV terrain
model — with what was changed for the demos (reprojected, converted to COG,
the terrain excerpt cut and resampled) and that neither publisher endorses
map0. The privacy page adds the ICGC and Geomatico hosts the demo contacts,
and discloses that the sealed-surfaces raster is served from our own
Scaleway storage in France rather than from its publisher, because it has to
be converted before a browser can read it.
… down
A "What's next" section between the positioning table and the demos names the
eight things being built next — accessibility audit, WFS and OGC API
Features, GeoJSON/KML/GPX by drop or URL, React and Angular wrappers,
scale-true print, the configurator, the plugin API, catalog search — as
dashed cards with one-line headings, so nobody mistakes a plan for a shipped
feature. A note underneath covers the rest and links the roadmap. The topbar
gets the section as a fourth anchor.
Every number the site claims is now filled at build time instead of typed.
`check-size.mjs` grew a `measure()` export that the i18n plugin calls, so the
page-load tier, the map tier and the deferred range come from the bundle that
was just built — the pages had been claiming 23 KB while the budget check read
30.6 KB, and the same numbers on the lazy-loading demo were older still. The
config-key count comes from the published schema the same way. Markers wrap
only the number, which lets a translated sentence carry one too; German gets
its decimal comma. Without a built dist (a bare `pnpm dev`) the copy keeps
its written fallback.
The stats strip trades "1 JSON document" and "2 UI languages" for the two
figures that show scope: 114 config keys and 7 source types. The three — now
four — zeros in the hero count down from a two-digit number to the zero that
is in the markup all along: the digits live in an injected, aria-hidden
overlay, so crawlers and screen readers only ever see 0, and a timer
guarantees the overlay is gone even in a tab that never gets a frame.
The four "Why map0" pillars drop their second paragraph, folding what
mattered into the first, and the German headings are short enough to stay on
one line. Roadmap and READMEs pick up the measured sizes, KML alongside
GeoJSON/GPX, and Angular alongside the React wrapper.
@github-actions

Copy link
Copy Markdown

Azure Static Web Apps: Your stage site is ready! Visit it here: https://brave-mud-0c9fbf503-1.westeurope.7.azurestaticapps.net

@pergerch
pergerch merged commit e16b5a6 into mainAug 21, 2026
3 checks passed
@pergerch
pergerch deleted the vnext branch August 21, 2026 13:20
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@pergerch@sgreifeneder