Add a debug page for comparing offline base-map themes side by side
Renders the bare protomaps-themes-base themes next to the app's actual offline/online styles (each real app instance, embedded), with pan/zoom kept in sync via the existing URL-hash mechanism, to make it easy to see where the hand-tuned offline palette diverges from the online one.
This commit is contained in:
1 parent
9223410b40
commit
0874e8f8ed
2 files changed
+122
No files matched your search
@@ -23,6 +23,17 @@ explicit cache headers, so an edit to `public/` (or `views/`) can
|
||||
appear not to have taken effect on the next reload. Force a real
|
||||
reload (e.g. Ctrl/Cmd+Shift+R) if that happens.
|
||||
|
||||
`/theme_preview.html` is a standalone debug page (not linked from the
|
||||
app itself) for comparing offline base-map themes side by side: a bare
|
||||
`protomaps-themes-base` theme, an iframe of the real app's offline
|
||||
style, and one of its online style, with pan/zoom kept in sync across
|
||||
all three via the same URL-hash mechanism `map.js` already uses for
|
||||
shareable links. It's a plain static file, so nothing gates it to
|
||||
development — it's reachable on a production instance too, same as any
|
||||
other file under `public/`. Harmless if it stays that way (it shows
|
||||
nothing `/` doesn't already), but worth knowing if that's ever not the
|
||||
case.
|
||||
|
||||
== Offline map data
|
||||
|
||||
To enable the offline style, place a PMTiles file at
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Offline theme preview</title>
|
||||
<link rel="stylesheet" href="/vendor/maplibre-gl/maplibre-gl.css">
|
||||
<style>
|
||||
body { margin: 0; font-family: sans-serif; background: #222; }
|
||||
.grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 4px; }
|
||||
.cell { position: relative; height: 90vh; }
|
||||
.cell .map, .cell iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
|
||||
.cell .label {
|
||||
position: absolute; top: 4px; left: 4px; z-index: 1;
|
||||
background: rgba(0,0,0,.6); color: #fff; padding: 2px 8px;
|
||||
font-size: 13px; border-radius: 3px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="grid">
|
||||
<div class="cell">
|
||||
<div class="label">light (bare base theme)</div>
|
||||
<div class="map" id="light-map"></div>
|
||||
</div>
|
||||
<div class="cell">
|
||||
<div class="label">offline (current app)</div>
|
||||
<iframe id="offline-frame" src="/"></iframe>
|
||||
</div>
|
||||
<div class="cell">
|
||||
<div class="label">online (current app)</div>
|
||||
<iframe id="online-frame" src="/"></iframe>
|
||||
</div>
|
||||
</div>
|
||||
<script src="/vendor/pmtiles/pmtiles.js"></script>
|
||||
<script src="/vendor/protomaps-themes-base/protomaps-themes-base.js"></script>
|
||||
<script type="module">
|
||||
import { Map as MaplibreMap, addProtocol } from "/vendor/maplibre-gl/maplibre-gl.mjs";
|
||||
|
||||
const protocol = new pmtiles.Protocol();
|
||||
addProtocol("pmtiles", protocol.tile);
|
||||
|
||||
const CENTER = [17.1077, 48.1486];
|
||||
const ZOOM = 13;
|
||||
const pmtilesUrl = `${window.location.origin}/tiles/offline.pmtiles`;
|
||||
|
||||
const lightMap = new MaplibreMap({
|
||||
container: "light-map",
|
||||
center: CENTER,
|
||||
zoom: ZOOM,
|
||||
style: {
|
||||
version: 8,
|
||||
glyphs: `${window.location.origin}/vendor/fonts/{fontstack}/{range}.pbf`,
|
||||
sources: {
|
||||
offline: { type: "vector", url: `pmtiles://${pmtilesUrl}` },
|
||||
},
|
||||
layers: protomaps_themes_base.layers("offline", protomaps_themes_base.namedTheme("light"), { lang: "en" }),
|
||||
},
|
||||
});
|
||||
|
||||
// "offline" frame needs nothing: the app already defaults to its offline
|
||||
// style whenever /api/offline-tiles reports data present (see map.js).
|
||||
// "online" frame: click its own real Online button, same button a user
|
||||
// would click — reuses the app's actual ONLINE_STYLE (patched Liberty)
|
||||
// instead of re-fetching/re-patching a second copy here.
|
||||
const offlineFrame = document.getElementById("offline-frame");
|
||||
const onlineFrame = document.getElementById("online-frame");
|
||||
const clickOnline = () => {
|
||||
const button = onlineFrame.contentDocument?.querySelector('.basemap-panel__option[data-style="online"]');
|
||||
if (button) button.click();
|
||||
};
|
||||
onlineFrame.addEventListener("load", () => {
|
||||
clickOnline();
|
||||
setTimeout(clickOnline, 1000); // retry once in case map.js was still mid-init
|
||||
});
|
||||
|
||||
// Keep all three views looking at the same spot. The two iframes are the
|
||||
// real app (separate documents, own module scope — nothing to reach into
|
||||
// directly), but map.js already syncs its map to the URL hash (`hash:
|
||||
// "map"`, format #map=zoom/lat/lon) for shareable links. Reusing that: poll
|
||||
// each source's position, and whichever moved gets pushed to the other
|
||||
// two — for a frame, by writing its location.hash, which fires its own
|
||||
// hashchange and lets its Hash control jumpTo for us.
|
||||
const HASH_RE = /map=([\d.]+)\/(-?[\d.]+)\/(-?[\d.]+)/;
|
||||
function frameHash(frame) {
|
||||
const match = HASH_RE.exec(frame.contentWindow?.location.hash ?? "");
|
||||
return match ? { zoom: +match[1], lat: +match[2], lon: +match[3] } : null;
|
||||
}
|
||||
function lightPos() {
|
||||
const c = lightMap.getCenter();
|
||||
return { zoom: lightMap.getZoom(), lat: c.lat, lon: c.lng };
|
||||
}
|
||||
function samePos(a, b) {
|
||||
return Math.abs(a.zoom - b.zoom) < 0.005 && Math.abs(a.lat - b.lat) < 1e-4 && Math.abs(a.lon - b.lon) < 1e-4;
|
||||
}
|
||||
let current = { zoom: ZOOM, lat: CENTER[1], lon: CENTER[0] };
|
||||
setInterval(() => {
|
||||
const sources = { light: lightPos(), offline: frameHash(offlineFrame), online: frameHash(onlineFrame) };
|
||||
const leader = Object.keys(sources).find((name) => sources[name] && !samePos(sources[name], current));
|
||||
if (!leader) return;
|
||||
current = sources[leader];
|
||||
if (leader !== "light") lightMap.jumpTo({ center: [current.lon, current.lat], zoom: current.zoom });
|
||||
if (leader !== "offline" && offlineFrame.contentWindow) {
|
||||
offlineFrame.contentWindow.location.hash = `map=${current.zoom}/${current.lat}/${current.lon}`;
|
||||
}
|
||||
if (leader !== "online" && onlineFrame.contentWindow) {
|
||||
onlineFrame.contentWindow.location.hash = `map=${current.zoom}/${current.lat}/${current.lon}`;
|
||||
}
|
||||
}, 150);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in new issue
Block a user