Apply the NPS-inspired palette to the online map too
ONLINE_STYLE_PATCH mirrors OFFLINE_THEME's color choices onto OpenFreeMap Liberty's OpenMapTiles layer ids (a separate hand-built table -- the two schemas share no layer names). loadOnlineStyle fetches Liberty's style.json once, patches it in place, and the result replaces the plain URL everywhere ONLINE_STYLE is used -- MapLibre accepts a style object anywhere it accepts a style URL, so applyStyle needed no change. Runs before the map (and the rest of the page) is constructed, so there's no flash of Liberty's own colors first -- but with a 3s timeout and a catch-all fallback to the plain URL, since this app is meant to keep working offline in the field and blocking startup on a remote style server would undermine that. Verified in a real browser: online map now visually matches the offline theme (cream ground, brown/khaki land cover, green parks, muted roads), no console errors, Online/Offline toggle still works.
This commit is contained in:
1 parent
24b6180c03
commit
ceaaa0bb92
3 files changed
+134
-3
No files matched your search
@@ -121,6 +121,31 @@ No PMTiles file ships with the repository — it is expected to be
|
|||||||
supplied locally (see `docs/development.adoc`) and stays gitignored
|
supplied locally (see `docs/development.adoc`) and stays gitignored
|
||||||
because such extracts are typically large binary files.
|
because such extracts are typically large binary files.
|
||||||
|
|
||||||
|
The Online map (OpenFreeMap Liberty) gets the same treatment via
|
||||||
|
`ONLINE_STYLE_PATCH`, a separate hand-built table — Liberty is an
|
||||||
|
OpenMapTiles-schema style, with its own layer ids (`landcover_wood`,
|
||||||
|
`road_secondary_tertiary`, `boundary_2`, ...) that don't correspond to
|
||||||
|
protomaps' theme roles, so the two palettes can't share one object,
|
||||||
|
only the same underlying color choices. `loadOnlineStyle`
|
||||||
|
(`public/js/map.js`) fetches Liberty's `style.json` once, patches it
|
||||||
|
in place, and uses the resulting object everywhere `ONLINE_STYLE` used
|
||||||
|
to be a plain URL string — MapLibre accepts a style object anywhere it
|
||||||
|
accepts a style URL, so `applyStyle`'s `map.setStyle(... : ONLINE_STYLE)`
|
||||||
|
needed no change. That fetch runs before the map (and the rest of the
|
||||||
|
page) is constructed — a top-level `await` — so there's no flash of
|
||||||
|
Liberty's own colors before the patch lands; a 3s timeout and a
|
||||||
|
catch-all fall back to the plain URL (letting MapLibre fetch and
|
||||||
|
render it unpatched) rather than block the whole page's startup on a
|
||||||
|
remote style server, since this app is meant to keep working offline
|
||||||
|
in the field, independent of whether the online map itself loads.
|
||||||
|
Only the layers that dominate the map's overall color read are
|
||||||
|
patched (land cover, water, parks, roads, boundaries, buildings) —
|
||||||
|
same deliberately-partial scope as `OFFLINE_THEME`, not a full
|
||||||
|
per-layer recolor. Road casings are the one addition with no
|
||||||
|
protomaps-role equivalent to reuse, since offline roads don't get a
|
||||||
|
separate outline color there; `#c9a66b` is reasoned to fit the family
|
||||||
|
rather than sourced from anywhere real.
|
||||||
|
|
||||||
== Map permalink
|
== Map permalink
|
||||||
|
|
||||||
The map's `hash: "map"` option (`public/js/map.js`) turns on MapLibre
|
The map's `hash: "map"` option (`public/js/map.js`) turns on MapLibre
|
||||||
|
|||||||
@@ -24,8 +24,8 @@ checking here before searching from zero next time.
|
|||||||
| MIT (per `package.json`); the whole `npmap5-plugins` repo is separately
|
| MIT (per `package.json`); the whole `npmap5-plugins` repo is separately
|
||||||
public domain as a US government work
|
public domain as a US government work
|
||||||
|
|
||||||
| Park Tiles color palette (offline basemap theme)
|
| Park Tiles color palette (offline + online basemap themes)
|
||||||
| `public/js/map.js`'s `OFFLINE_THEME`
|
| `public/js/map.js`'s `OFFLINE_THEME` and `ONLINE_STYLE_PATCH`
|
||||||
| Colors only — see <<park-tiles>> for why the actual tile service isn't used
|
| Colors only — see <<park-tiles>> for why the actual tile service isn't used
|
||||||
|
|
||||||
| `overview` (top-right minimap/inset control)
|
| `overview` (top-right minimap/inset control)
|
||||||
|
|||||||
+107
-1
@@ -19,7 +19,113 @@ import * as maplibregl from "/vendor/maplibre-gl/maplibre-gl.mjs";
|
|||||||
|
|
||||||
const CENTER = [17.1077, 48.1486];
|
const CENTER = [17.1077, 48.1486];
|
||||||
const ZOOM = 13;
|
const ZOOM = 13;
|
||||||
const ONLINE_STYLE = "https://tiles.openfreemap.org/styles/liberty";
|
const ONLINE_STYLE_URL = "https://tiles.openfreemap.org/styles/liberty";
|
||||||
|
|
||||||
|
// Recolors OpenFreeMap Liberty to the same NPS-inspired palette as
|
||||||
|
// OFFLINE_THEME below — warm cream ground, brown/khaki land cover, green
|
||||||
|
// reserved for parks, muted roads/boundaries — mapped onto Liberty's
|
||||||
|
// OpenMapTiles layer ids instead of protomaps' theme roles (the two
|
||||||
|
// schemas don't share layer names, so this is a separate hand-built
|
||||||
|
// table, not a shared object). Only the layers that dominate the map's
|
||||||
|
// overall color read are touched; labels, halos, rail, and minor
|
||||||
|
// overlays (cemetery/hospital/school/pitch/track shading, building
|
||||||
|
// outlines) are left at Liberty's own defaults — same "not a full
|
||||||
|
// recolor" scope as OFFLINE_THEME. Road casings have no protomaps-role
|
||||||
|
// equivalent to reuse (offline roads don't get a separate outline), so
|
||||||
|
// #c9a66b there is reasoned to fit the family rather than sourced from
|
||||||
|
// anywhere real.
|
||||||
|
const ONLINE_STYLE_PATCH = {
|
||||||
|
background: { "background-color": "#f9f6ef" },
|
||||||
|
landcover_wood: { "fill-color": "#c9bd9c" },
|
||||||
|
landcover_grass: { "fill-color": "#d9cfb0" },
|
||||||
|
landcover_sand: { "fill-color": "#f0e4c4" },
|
||||||
|
water: { "fill-color": "#a3cedf" },
|
||||||
|
park: { "fill-color": "#b3e378" },
|
||||||
|
park_outline: { "line-color": "#546619" },
|
||||||
|
building: { "fill-color": "#ded7cc" },
|
||||||
|
boundary_2: { "line-color": "#546619" },
|
||||||
|
boundary_3: { "line-color": "#546619" },
|
||||||
|
|
||||||
|
// Highway (motorway/trunk/primary), major (secondary/tertiary/link),
|
||||||
|
// minor (residential/service/track), pedestrian — each repeated for its
|
||||||
|
// tunnel_/bridge_ counterpart so a road doesn't change color when it
|
||||||
|
// dips into a tunnel or crosses a bridge. Rail is left alone.
|
||||||
|
road_motorway: { "line-color": "#fdefbe" },
|
||||||
|
road_trunk_primary: { "line-color": "#fdefbe" },
|
||||||
|
road_secondary_tertiary: { "line-color": "#fef7dd" },
|
||||||
|
road_link: { "line-color": "#fef7dd" },
|
||||||
|
road_motorway_link: { "line-color": "#fef7dd" },
|
||||||
|
road_minor: { "line-color": "#fffdf8" },
|
||||||
|
road_service_track: { "line-color": "#fffdf8" },
|
||||||
|
road_path_pedestrian: { "line-color": "#e9e2d8" },
|
||||||
|
road_motorway_casing: { "line-color": "#c9a66b" },
|
||||||
|
road_trunk_primary_casing: { "line-color": "#c9a66b" },
|
||||||
|
road_secondary_tertiary_casing: { "line-color": "#c9a66b" },
|
||||||
|
road_link_casing: { "line-color": "#c9a66b" },
|
||||||
|
road_motorway_link_casing: { "line-color": "#c9a66b" },
|
||||||
|
road_minor_casing: { "line-color": "#c9a66b" },
|
||||||
|
road_service_track_casing: { "line-color": "#c9a66b" },
|
||||||
|
|
||||||
|
tunnel_motorway: { "line-color": "#fdefbe" },
|
||||||
|
tunnel_trunk_primary: { "line-color": "#fdefbe" },
|
||||||
|
tunnel_secondary_tertiary: { "line-color": "#fef7dd" },
|
||||||
|
tunnel_link: { "line-color": "#fef7dd" },
|
||||||
|
tunnel_motorway_link: { "line-color": "#fef7dd" },
|
||||||
|
tunnel_minor: { "line-color": "#fffdf8" },
|
||||||
|
tunnel_service_track: { "line-color": "#fffdf8" },
|
||||||
|
tunnel_path_pedestrian: { "line-color": "#e9e2d8" },
|
||||||
|
tunnel_motorway_casing: { "line-color": "#c9a66b" },
|
||||||
|
tunnel_trunk_primary_casing: { "line-color": "#c9a66b" },
|
||||||
|
tunnel_secondary_tertiary_casing: { "line-color": "#c9a66b" },
|
||||||
|
tunnel_link_casing: { "line-color": "#c9a66b" },
|
||||||
|
tunnel_motorway_link_casing: { "line-color": "#c9a66b" },
|
||||||
|
tunnel_street_casing: { "line-color": "#c9a66b" },
|
||||||
|
|
||||||
|
bridge_motorway: { "line-color": "#fdefbe" },
|
||||||
|
bridge_trunk_primary: { "line-color": "#fdefbe" },
|
||||||
|
bridge_secondary_tertiary: { "line-color": "#fef7dd" },
|
||||||
|
bridge_link: { "line-color": "#fef7dd" },
|
||||||
|
bridge_motorway_link: { "line-color": "#fef7dd" },
|
||||||
|
bridge_street: { "line-color": "#fffdf8" },
|
||||||
|
bridge_service_track: { "line-color": "#fffdf8" },
|
||||||
|
bridge_path_pedestrian: { "line-color": "#e9e2d8" },
|
||||||
|
bridge_motorway_casing: { "line-color": "#c9a66b" },
|
||||||
|
bridge_trunk_primary_casing: { "line-color": "#c9a66b" },
|
||||||
|
bridge_secondary_tertiary_casing: { "line-color": "#c9a66b" },
|
||||||
|
bridge_link_casing: { "line-color": "#c9a66b" },
|
||||||
|
bridge_motorway_link_casing: { "line-color": "#c9a66b" },
|
||||||
|
bridge_street_casing: { "line-color": "#c9a66b" },
|
||||||
|
bridge_path_pedestrian_casing: { "line-color": "#c9a66b" },
|
||||||
|
};
|
||||||
|
|
||||||
|
function patchOnlineStyle(style) {
|
||||||
|
for (const layer of style.layers) {
|
||||||
|
const patch = ONLINE_STYLE_PATCH[layer.id];
|
||||||
|
if (patch) Object.assign((layer.paint ??= {}), patch);
|
||||||
|
}
|
||||||
|
return style;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetched (not just referenced by URL) so its colors can be patched before
|
||||||
|
// the map ever sees it — MapLibre accepts a style object anywhere it'd
|
||||||
|
// accept a style URL, so every other use of ONLINE_STYLE (applyStyle's
|
||||||
|
// map.setStyle) needs no change. This runs before the map — and the rest
|
||||||
|
// of the page's setup — is constructed (see the top-level await below), so
|
||||||
|
// it falls back to the plain URL, letting MapLibre fetch and render it
|
||||||
|
// unpatched, both when the fetch fails outright and when it's just slow
|
||||||
|
// (a 3s timeout): this app is meant to work offline in the field, and
|
||||||
|
// blocking the entire page on a remote style server would undermine that
|
||||||
|
// even though this particular fetch is only for the online map.
|
||||||
|
async function loadOnlineStyle() {
|
||||||
|
try {
|
||||||
|
const response = await fetch(ONLINE_STYLE_URL, { signal: AbortSignal.timeout(3000) });
|
||||||
|
return patchOnlineStyle(await response.json());
|
||||||
|
} catch (err) {
|
||||||
|
return ONLINE_STYLE_URL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const ONLINE_STYLE = await loadOnlineStyle();
|
||||||
|
|
||||||
const protocol = new pmtiles.Protocol();
|
const protocol = new pmtiles.Protocol();
|
||||||
addProtocol("pmtiles", protocol.tile);
|
addProtocol("pmtiles", protocol.tile);
|
||||||
|
|||||||
Reference in new issue
Block a user