Replace offline basemap pipeline with Planetiler, unify online/offline styling
Offline tiles were built from Protomaps' own basemap schema, needing a completely separate hand-tuned theme (protomaps-themes-base + OFFLINE_THEME) from the online Liberty style's ONLINE_STYLE_PATCH — the two could never be made to match exactly no matter how much either side got tuned (today's session found and fixed several mismatches: building opacity, park boundaries, road casings, road widths). Offline.pmtiles is now built with Planetiler's default OpenMapTiles profile against Geofabrik per-country OSM extracts (osmium-merged when more than one country is selected), the same vector schema Liberty already expects. offlineStyle() now clones the same Liberty JSON (vendored as a local snapshot for reliability with no internet) and applies the identical ONLINE_STYLE_PATCH, just repointed at local PMTiles/glyphs/sprite — one palette instead of two, correct on both sides by construction. Also vendors Liberty's icon sprite for the first time (offline had none before), adds Java/osmium-tool/planetiler.jar to the Docker image, and removes the now-unused protomaps-themes-base dependency and the theme-comparison debug page that found today's bugs.
This commit is contained in:
1 parent
0874e8f8ed
commit
d08ed41185
21 files changed
+4286
-512
No files matched your search
+90
-112
@@ -28,29 +28,46 @@ const CENTER = [17.1077, 48.1486];
|
||||
const ZOOM = 13;
|
||||
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, 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.
|
||||
// Recolors OpenFreeMap Liberty to a warm, NPS-Park-Tiles-inspired palette —
|
||||
// cream ground, muted roads/boundaries — mapped onto Liberty's OpenMapTiles
|
||||
// layer ids. Applied identically regardless of which PMTiles data actually
|
||||
// backs the "openmaptiles" source (see offlineStyle() below): offline tiles
|
||||
// are built by Planetiler's own OpenMapTiles profile (docs/development.adoc),
|
||||
// the same schema Liberty already expects, so one patch serves both —
|
||||
// no separate offline theme/palette to keep in sync. 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.
|
||||
//
|
||||
// wood/grass are deliberately left unpatched — Liberty's own green reads
|
||||
// better than this app's earlier brown/khaki recolor, and OFFLINE_THEME's
|
||||
// wood_a/scrub_a below were changed to match Liberty's green instead.
|
||||
// wood/grass are deliberately left unpatched — Liberty's own green (a
|
||||
// translucent landcover_wood/landcover_grass wash) already reads well over
|
||||
// the patched cream background.
|
||||
const ONLINE_STYLE_PATCH = {
|
||||
background: { "background-color": "#f9f6ef" },
|
||||
// Liberty washes every built-up area in a translucent gray-tan below
|
||||
// z12 (landuse_residential, maxzoom 12 — gone by this app's default
|
||||
// z13) that the offline schema has no equivalent of; protomaps just
|
||||
// leaves that ground as plain background/earth. Matched to background
|
||||
// here rather than left alone, so a zoomed-out view doesn't read as
|
||||
// visibly dingier online than offline.
|
||||
landuse_residential: { "fill-color": "#f9f6ef" },
|
||||
landcover_sand: { "fill-color": "#f0e4c4" },
|
||||
water: { "fill-color": "#a3cedf" },
|
||||
park: { "fill-color": "#b3e378" },
|
||||
park_outline: { "line-color": "#546619" },
|
||||
building: { "fill-color": "#ded7cc" },
|
||||
// fill-outline-color isn't touched by a plain fill-color patch — Liberty
|
||||
// draws it separately, defaulting to a bright green that reads as a
|
||||
// distinct border around named conservation areas (e.g. the Malé
|
||||
// Karpaty CHKO). Matched to the fill itself, and park_outline's own
|
||||
// dashed line hidden below, so a reserve blends in as ordinary forest —
|
||||
// offline has no equivalent stroke at all (see park_a/park_b below),
|
||||
// and the point was never to call reserves out as visibly different,
|
||||
// on either side.
|
||||
park: { "fill-color": "#b3e378", "fill-outline-color": "#b3e378" },
|
||||
park_outline: { "line-opacity": 0 },
|
||||
// fill-opacity: 0.5 matches the offline theme's own "buildings" layer —
|
||||
// without it, Liberty's dense individual building footprints (each with
|
||||
// its own outline) paint fully opaque and read much heavier/grayer than
|
||||
// offline's softer, half-transparent fill over the same cream ground.
|
||||
building: { "fill-color": "#ded7cc", "fill-opacity": 0.5 },
|
||||
boundary_2: { "line-color": "#546619" },
|
||||
boundary_3: { "line-color": "#546619" },
|
||||
|
||||
@@ -58,19 +75,30 @@ const ONLINE_STYLE_PATCH = {
|
||||
// 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" },
|
||||
//
|
||||
// line-width below is a flat number, not Liberty's own zoom-interpolated
|
||||
// expression — picked to match the offline theme's actual rendered width
|
||||
// at this app's default zoom (13; verified live via queryRenderedFeatures
|
||||
// on both sides), since without it Liberty's own curve draws surface
|
||||
// roads roughly twice as thick as offline at street level, reading as a
|
||||
// much bolder road network online.
|
||||
// ponytail: tuned for z13 only, not a matching curve across all zooms —
|
||||
// will drift back apart at very different zoom levels. Upgrade path:
|
||||
// sample offline's width at a few more zooms and replace with a matching
|
||||
// ["interpolate", ["linear"], ["zoom"], ...] expression.
|
||||
road_motorway: { "line-color": "#fdefbe", "line-width": 2.26 },
|
||||
road_trunk_primary: { "line-color": "#fdefbe", "line-width": 2.26 },
|
||||
road_secondary_tertiary: { "line-color": "#fef7dd", "line-width": 1.87 },
|
||||
road_link: { "line-color": "#fef7dd", "line-width": 1 },
|
||||
road_motorway_link: { "line-color": "#fef7dd", "line-width": 1 },
|
||||
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_motorway_casing: { "line-color": "#c9a66b", "line-width": 1.48 },
|
||||
road_trunk_primary_casing: { "line-color": "#c9a66b", "line-width": 1.48 },
|
||||
road_secondary_tertiary_casing: { "line-color": "#c9a66b", "line-width": 1 },
|
||||
road_link_casing: { "line-color": "#c9a66b", "line-width": 1.5 },
|
||||
road_motorway_link_casing: { "line-color": "#c9a66b", "line-width": 1.5 },
|
||||
road_minor_casing: { "line-color": "#c9a66b" },
|
||||
road_service_track_casing: { "line-color": "#c9a66b" },
|
||||
|
||||
@@ -161,95 +189,45 @@ async function loadOnlineStyle() {
|
||||
|
||||
const ONLINE_STYLE = await loadOnlineStyle();
|
||||
|
||||
// A vendored copy of the same Liberty style ONLINE_STYLE fetches live
|
||||
// (public/vendor/liberty-style/liberty.json — a point-in-time snapshot, not
|
||||
// auto-updated) that offlineStyle() below patches+repoints the exact same
|
||||
// way. Offline mode can't depend on ONLINE_STYLE_URL actually being
|
||||
// reachable — that fetch failing/timing out is the normal case in the
|
||||
// field, not an edge case — so this is a local, same-origin fetch instead
|
||||
// (fast and always available, since it's served by this app itself).
|
||||
const OFFLINE_STYLE_TEMPLATE = await fetch("/vendor/liberty-style/liberty.json").then((response) => response.json());
|
||||
|
||||
const protocol = new pmtiles.Protocol();
|
||||
addProtocol("pmtiles", protocol.tile);
|
||||
|
||||
// Nudged from protomaps-themes-base's stock "light" theme towards the
|
||||
// National Park Service's own "Park Tiles" cartography — a warm,
|
||||
// Harpers-Ferry-Center-derived look. Not their actual map: a handful of
|
||||
// values below (marked) are the genuine paint colors pulled from a public
|
||||
// Park Tiles style JSON
|
||||
// (nps.gov/lib/npmap.js/style/ck72fwp2642dv07o7tbqinvz4.json, the "Imagery"
|
||||
// variant — the only one of their four styles served from a plain,
|
||||
// unauthenticated URL; "Standard"/"Slate"/"Light" resolve through Mapbox
|
||||
// Studio's private API with an access token, which isn't something to
|
||||
// piggyback on even though it's technically visible in their own JS bundle).
|
||||
// The rest are reasoned to match that same family (warm cream ground, olive
|
||||
// greens, muted blue) rather than sourced. Not a full recolor: labels,
|
||||
// casings, and roles with no clean real-color anchor are left at theme
|
||||
// defaults.
|
||||
// Builds the offline style from OFFLINE_STYLE_TEMPLATE — the identical
|
||||
// Liberty JSON + ONLINE_STYLE_PATCH recolor as the online map, just
|
||||
// repointed at a local PMTiles source instead of tiles.openfreemap.org.
|
||||
// This works because data/offline.pmtiles is now built by Planetiler's own
|
||||
// OpenMapTiles profile (see docs/development.adoc), the same vector schema
|
||||
// Liberty expects — before that, offline tiles used a different schema
|
||||
// (protomaps' own basemaps) needing a completely separate hand-tuned
|
||||
// theme (protomaps-themes-base + OFFLINE_THEME) that could never be made
|
||||
// to match ONLINE_STYLE_PATCH's look exactly, no matter how much either
|
||||
// side got tuned.
|
||||
//
|
||||
// Recreating the color choices is legitimate — that's not the kind of thing
|
||||
// a data license restricts (see docs/architecture.adoc's "Offline maps"
|
||||
// section) — what stays off-limits is their actual tile *service*: its
|
||||
// OSM/terrain layers are licensed through Mapbox Atlas, a commercial,
|
||||
// non-sublicensable data service scoped to NPS.
|
||||
const OFFLINE_THEME = {
|
||||
...protomaps_themes_base.namedTheme("light"),
|
||||
background: "#f9f6ef", // real: Park Tiles' own background, hsl(40, 37%, 97%)
|
||||
earth: "#f9f6ef",
|
||||
water: "#a3cedf",
|
||||
// Matches OpenFreeMap Liberty's own wood/grass green (its landcover_wood
|
||||
// hsla(98,61%,72%,0.7) and landcover_grass rgba(176,213,154,1) at 0.3
|
||||
// opacity, blended over the cream background) — the online map's actual
|
||||
// look whenever ONLINE_STYLE_PATCH's brown/khaki recolor fails to apply
|
||||
// (see loadOnlineStyle's fetch fallback) turned out to be the look this
|
||||
// app's users actually want, so offline was changed to match it instead
|
||||
// of chasing the patch's own reliability.
|
||||
wood_a: "#c3e8ab",
|
||||
wood_b: "#a8d888",
|
||||
scrub_a: "#dcecc9",
|
||||
scrub_b: "#cfe3b8",
|
||||
// Close to wood_b, not the more saturated Park Tiles green: protomaps'
|
||||
// theme routes "forest"/"nature_reserve"/"protected_area" (e.g. the Malé
|
||||
// Karpaty CHKO boundary) through this same park_a/park_b pair as actual
|
||||
// named parks — Liberty's online style doesn't make that reserve-boundary
|
||||
// distinction at all, so a visibly different green here reads as an
|
||||
// offline-only blotch rather than a deliberate distinction.
|
||||
park_a: "#c3e8ab",
|
||||
park_b: "#a8d888",
|
||||
sand: "#f0e4c4",
|
||||
beach: "#f0e4c4",
|
||||
buildings: "#ded7cc",
|
||||
pedestrian: "#e9e2d8",
|
||||
highway: "#fdefbe", // real: Park Tiles' primary/highway road color
|
||||
major: "#fef7dd", // real: Park Tiles' secondary road color
|
||||
// "minor" (singular) is never actually read by protomaps' own paint
|
||||
// expressions — only the zoom-interpolated minor_a/minor_b pair is, so a
|
||||
// plain "minor" key here was dead: ordinary streets were silently
|
||||
// falling back to the base theme's default pale yellow instead of this
|
||||
// app's cream.
|
||||
minor_a: "#fffdf8",
|
||||
minor_b: "#fffdf8",
|
||||
link: "#fef7dd",
|
||||
// Distinct brown, not cream: protomaps' "other"/"path" kind is what a
|
||||
// camper actually needs called out — unpaved tracks and footpaths,
|
||||
// separate from paved minor_road/service (minor_service, left at the
|
||||
// base theme's default since it's real driveways/parking aisles, not
|
||||
// unpaved). Already rendered dashed by the theme itself; this just picks
|
||||
// a real "dirt" tone instead of unstyled white.
|
||||
other: "#c9a66b",
|
||||
boundaries: "#546619", // real: Park Tiles' park-boundary line color
|
||||
};
|
||||
|
||||
// glyphs/sprite point at vendored local copies (public/vendor/fonts,
|
||||
// public/vendor/liberty-sprite) rather than tiles.openfreemap.org's —
|
||||
// glyphs are Latin-only (see docs/architecture.adoc: labels in other
|
||||
// scripts won't render offline), sprite is a one-time snapshot like
|
||||
// OFFLINE_STYLE_TEMPLATE itself. ne2_shaded (Liberty's low-zoom shaded-
|
||||
// relief backdrop, z0-6 only) is left pointing at its remote URL
|
||||
// unchanged — offline usage is regional, not whole-earth zoomed out, so
|
||||
// this only ever silently fails to load at a zoom level offline users
|
||||
// aren't in, the same graceful-degradation story as this app's other
|
||||
// online-only extras (Wikipedia, live radar).
|
||||
function offlineStyle() {
|
||||
const pmtilesUrl = `${window.location.origin}/tiles/offline.pmtiles`;
|
||||
|
||||
return {
|
||||
version: 8,
|
||||
glyphs: `${window.location.origin}/vendor/fonts/{fontstack}/{range}.pbf`,
|
||||
sources: {
|
||||
offline: {
|
||||
type: "vector",
|
||||
url: `pmtiles://${pmtilesUrl}`,
|
||||
attribution: "© OpenStreetMap contributors",
|
||||
},
|
||||
},
|
||||
// lang: "en" pulls in labels_layers (see docs/architecture.adoc) — only
|
||||
// the Latin-script glyph ranges are vendored, so labels using other
|
||||
// scripts won't render offline.
|
||||
layers: protomaps_themes_base.layers("offline", OFFLINE_THEME, { lang: "en" }),
|
||||
};
|
||||
const style = patchOnlineStyle(structuredClone(OFFLINE_STYLE_TEMPLATE));
|
||||
style.sources.openmaptiles.url = `pmtiles://${window.location.origin}/tiles/offline.pmtiles`;
|
||||
style.glyphs = `${window.location.origin}/vendor/fonts/{fontstack}/{range}.pbf`;
|
||||
style.sprite = `${window.location.origin}/vendor/liberty-sprite/ofm`;
|
||||
return style;
|
||||
}
|
||||
|
||||
// A sidebar icon that opens a full-height side panel (like the GPX one)
|
||||
|
||||
@@ -37,13 +37,13 @@ function flagIconUrl(code) {
|
||||
return `/vendor/flag-icons/1x1/${code.toLowerCase()}.svg`;
|
||||
}
|
||||
|
||||
// meta.source is a Protomaps build filename like "20260910.pmtiles" — the
|
||||
// leading 8 digits are its build date.
|
||||
// meta.sources is one entry per country the current build covers (see
|
||||
// lib/maptool/pmtiles_builder.rb) — listing the codes is more useful here
|
||||
// than a count, since they're exactly what RegionPicker's checklist above
|
||||
// already shows.
|
||||
function formatOfflineMeta(meta) {
|
||||
if (!meta) return "";
|
||||
const match = meta.source.match(/^(\d{4})(\d{2})(\d{2})/);
|
||||
const date = match ? `${match[1]}-${match[2]}-${match[3]}` : meta.source;
|
||||
return ` · Protomaps ${date}`;
|
||||
return ` · ${meta.sources.map((s) => s.code).join(", ")}`;
|
||||
}
|
||||
|
||||
function formatTerrainMeta(meta) {
|
||||
@@ -172,9 +172,9 @@ class DataFileRow {
|
||||
}
|
||||
|
||||
// Separate from _refreshStatus's own fetch (and only called once, right
|
||||
// after — not polled): this one reaches an external service (Protomaps'
|
||||
// build index or Mapterhorn's manifest, see
|
||||
// lib/maptool/pmtiles_builder.rb), so it's kept off the hot status path
|
||||
// after — not polled): this one reaches an external service (Geofabrik
|
||||
// or Mapterhorn's manifest, see lib/maptool/pmtiles_builder.rb), so it's
|
||||
// kept off the hot status path
|
||||
// and just silently skipped if it fails, same as everywhere else this
|
||||
// app degrades gracefully when an optional network check doesn't answer.
|
||||
async _checkUpdate() {
|
||||
|
||||
@@ -1,111 +0,0 @@
|
||||
<!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>
|
||||
+1850
File diff suppressed because it is too large.
Load diff
Vendored
BIN
Binary file not shown.
|
After Width: | Height: | Size: 48 KiB |
+1850
File diff suppressed because it is too large.
Load diff
BIN
Binary file not shown.
|
After Width: | Height: | Size: 116 KiB |
+1
@@ -0,0 +1 @@
|
||||
{"version":8,"sources":{"ne2_shaded":{"maxzoom":6,"tileSize":256,"tiles":["https://tiles.openfreemap.org/natural_earth/ne2sr/{z}/{x}/{y}.png"],"type":"raster"},"openmaptiles":{"type":"vector","url":"https://tiles.openfreemap.org/planet"}},"sprite":"https://tiles.openfreemap.org/sprites/ofm_f384/ofm","glyphs":"https://tiles.openfreemap.org/fonts/{fontstack}/{range}.pbf","layers":[{"id":"background","type":"background","paint":{"background-color":"#f8f4f0"}},{"id":"natural_earth","type":"raster","source":"ne2_shaded","maxzoom":7,"paint":{"raster-opacity":["interpolate",["exponential",1.5],["zoom"],0,0.6,6,0.1]}},{"id":"park","type":"fill","source":"openmaptiles","source-layer":"park","paint":{"fill-color":"#d8e8c8","fill-opacity":0.7,"fill-outline-color":"rgba(95, 208, 100, 1)"}},{"id":"park_outline","type":"line","source":"openmaptiles","source-layer":"park","paint":{"line-color":"rgba(228, 241, 215, 1)","line-dasharray":[1,1.5]}},{"id":"landuse_residential","type":"fill","source":"openmaptiles","source-layer":"landuse","maxzoom":12,"filter":["==",["get","class"],"residential"],"paint":{"fill-color":["interpolate",["linear"],["zoom"],9,"hsla(0,3%,85%,0.84)",12,"hsla(35,57%,88%,0.49)"]}},{"id":"landcover_wood","type":"fill","source":"openmaptiles","source-layer":"landcover","filter":["==",["get","class"],"wood"],"paint":{"fill-antialias":false,"fill-color":"hsla(98,61%,72%,0.7)","fill-opacity":0.4}},{"id":"landcover_grass","type":"fill","source":"openmaptiles","source-layer":"landcover","filter":["==",["get","class"],"grass"],"paint":{"fill-antialias":false,"fill-color":"rgba(176, 213, 154, 1)","fill-opacity":0.3}},{"id":"landcover_ice","type":"fill","source":"openmaptiles","source-layer":"landcover","filter":["==",["get","class"],"ice"],"paint":{"fill-antialias":false,"fill-color":"rgba(224, 236, 236, 1)","fill-opacity":0.8}},{"id":"landcover_wetland","type":"fill","source":"openmaptiles","source-layer":"landcover","minzoom":12,"filter":["==",["get","class"],"wetland"],"paint":{"fill-antialias":true,"fill-opacity":0.8,"fill-pattern":"wetland_bg_11","fill-translate-anchor":"map"}},{"id":"landuse_pitch","type":"fill","source":"openmaptiles","source-layer":"landuse","filter":["==",["get","class"],"pitch"],"paint":{"fill-color":"#DEE3CD"}},{"id":"landuse_track","type":"fill","source":"openmaptiles","source-layer":"landuse","filter":["==",["get","class"],"track"],"paint":{"fill-color":"#DEE3CD"}},{"id":"landuse_cemetery","type":"fill","source":"openmaptiles","source-layer":"landuse","filter":["==",["get","class"],"cemetery"],"paint":{"fill-color":"hsl(75,37%,81%)"}},{"id":"landuse_hospital","type":"fill","source":"openmaptiles","source-layer":"landuse","filter":["==",["get","class"],"hospital"],"paint":{"fill-color":"#fde"}},{"id":"landuse_school","type":"fill","source":"openmaptiles","source-layer":"landuse","filter":["==",["get","class"],"school"],"paint":{"fill-color":"rgb(236,238,204)"}},{"id":"waterway_tunnel","type":"line","source":"openmaptiles","source-layer":"waterway","filter":["==",["get","brunnel"],"tunnel"],"paint":{"line-color":"#a0c8f0","line-dasharray":[3,3],"line-gap-width":["interpolate",["linear"],["zoom"],12,0,20,6],"line-opacity":1,"line-width":["interpolate",["exponential",1.4],["zoom"],8,1,20,2]}},{"id":"waterway_river","type":"line","source":"openmaptiles","source-layer":"waterway","filter":["all",["==",["get","class"],"river"],["!=",["get","brunnel"],"tunnel"]],"layout":{"line-cap":"round"},"paint":{"line-color":"#a0c8f0","line-width":["interpolate",["exponential",1.2],["zoom"],11,0.5,20,6]}},{"id":"waterway_other","type":"line","source":"openmaptiles","source-layer":"waterway","filter":["all",["!=",["get","class"],"river"],["!=",["get","brunnel"],"tunnel"]],"layout":{"line-cap":"round"},"paint":{"line-color":"#a0c8f0","line-width":["interpolate",["exponential",1.3],["zoom"],13,0.5,20,6]}},{"id":"water","type":"fill","source":"openmaptiles","source-layer":"water","filter":["!=",["get","brunnel"],"tunnel"],"paint":{"fill-color":"rgb(158,189,255)"}},{"id":"landcover_sand","type":"fill","source":"openmaptiles","source-layer":"landcover","filter":["==",["get","class"],"sand"],"paint":{"fill-color":"rgba(247, 239, 195, 1)"}},{"id":"aeroway_fill","type":"fill","source":"openmaptiles","source-layer":"aeroway","minzoom":11,"filter":["match",["geometry-type"],["MultiPolygon","Polygon"],true,false],"paint":{"fill-color":"rgba(229, 228, 224, 1)","fill-opacity":0.7}},{"id":"aeroway_runway","type":"line","source":"openmaptiles","source-layer":"aeroway","minzoom":11,"filter":["all",["match",["geometry-type"],["LineString","MultiLineString"],true,false],["==",["get","class"],"runway"]],"paint":{"line-color":"#f0ede9","line-width":["interpolate",["exponential",1.2],["zoom"],11,3,20,16]}},{"id":"aeroway_taxiway","type":"line","source":"openmaptiles","source-layer":"aeroway","minzoom":11,"filter":["all",["match",["geometry-type"],["LineString","MultiLineString"],true,false],["==",["get","class"],"taxiway"Line truncated
|
||||
@@ -1,16 +0,0 @@
|
||||
"use strict";var protomaps_themes_base=(()=>{var f=Object.defineProperty;var D=Object.getOwnPropertyDescriptor;var T=Object.getOwnPropertyNames,x=Object.getOwnPropertySymbols;var j=Object.prototype.hasOwnProperty,$=Object.prototype.propertyIsEnumerable;var z=(a,e,i)=>e in a?f(a,e,{enumerable:!0,configurable:!0,writable:!0,value:i}):a[e]=i,b=(a,e)=>{for(var i in e||(e={}))j.call(e,i)&&z(a,i,e[i]);if(x)for(var i of x(e))$.call(e,i)&&z(a,i,e[i]);return a};var l=(a,e)=>f(a,"name",{value:e,configurable:!0});var C=(a,e)=>{for(var i in e)f(a,i,{get:e[i],enumerable:!0})},P=(a,e,i,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of T(e))!j.call(a,r)&&r!==i&&f(a,r,{get:()=>e[r],enumerable:!(n=D(e,r))||n.enumerable});return a};var A=a=>P(f({},"__esModule",{value:!0}),a);var W={};C(W,{default:()=>S,labels:()=>I,labelsWithCustomTheme:()=>K,language_script_pairs:()=>m,layers:()=>B,layersWithCustomTheme:()=>V,layersWithPartialCustomTheme:()=>F,namedTheme:()=>H,noLabels:()=>E,noLabelsWithCustomTheme:()=>G});function t(a,e){let i="script";return a==="name"?i="script":a==="name2"?i="script2":a==="name3"&&(i="script3"),[["coalesce",["get",`pgf:${a}`],["get",a]],{"text-font":["case",["==",["get",i],"Devanagari"],["literal",["Noto Sans Devanagari Regular v1"]],["literal",[e||"Noto Sans Regular"]]]}]}l(t,"get_name_block");function c(a,e,i){let n="name";return i==="name"?n="":i==="name2"?n="2":i==="name3"&&(n="3"),e==="Latin"?["has",`script${n}`]:a==="ja"?["all",["!=",["get",`script${n}`],"Han"],["!=",["get",`script${n}`],"Hiragana"],["!=",["get",`script${n}`],"Katakana"],["!=",["get",`script${n}`],"Mixed-Japanese"]]:["!=",["get",`script${n}`],e]}l(c,"is_not_in_target_script");function o(a){return a==="Devanagari"?{"text-font":["literal",["Noto Sans Devanagari Regular v1"]]}:{}}l(o,"get_font_formatting");function v(a){let e=m.find(i=>i.lang===a);return e===void 0?"Latin":e.script}l(v,"get_default_script");function L(a,e){let i=e||v(a),n;return i==="Devanagari"?n="pgf:":n="",["format",["coalesce",["get",`${n}name:${a}`],["get","name:en"]],o(i)]}l(L,"get_country_name");function s(a,e,i){let n=e||v(a),r;return n==="Devanagari"?r="pgf:":r="",["case",["all",["any",["has","name"],["has","pgf:name"]],["!",["any",["has","name2"],["has","pgf:name2"]]],["!",["any",["has","name3"],["has","pgf:name3"]]]],["case",c(a,n,"name"),["case",["any",["is-supported-script",["get","name"]],["has","pgf:name"]],["format",["coalesce",["get",`${r}name:${a}`],["get","name:en"]],o(n),`
|
||||
`,{},["case",["all",["!",["has",`${r}name:${a}`]],["has","name:en"],["!",["has","script"]]],"",["coalesce",["get","pgf:name"],["get","name"]]],{"text-font":["case",["==",["get","script"],"Devanagari"],["literal",["Noto Sans Devanagari Regular v1"]],["literal",[i||"Noto Sans Regular"]]]}],["get","name:en"]],["format",["coalesce",["get",`${r}name:${a}`],["get","pgf:name"],["get","name"]],o(n)]],["all",["any",["has","name"],["has","pgf:name"]],["any",["has","name2"],["has","pgf:name2"]],["!",["any",["has","name3"],["has","pgf:name3"]]]],["case",["all",c(a,n,"name"),c(a,n,"name2")],["format",["get",`${r}name:${a}`],o(n),`
|
||||
`,{},...t("name"),`
|
||||
`,{},...t("name2")],["case",c(a,n,"name2"),["format",["coalesce",["get",`${r}name:${a}`],["get","pgf:name"],["get","name"]],o(n),`
|
||||
`,{},...t("name2")],["format",["coalesce",["get",`${r}name:${a}`],["get","pgf:name2"],["get","name2"]],o(n),`
|
||||
`,{},...t("name")]]],["case",["all",c(a,n,"name"),c(a,n,"name2"),c(a,n,"name3")],["format",["get",`${r}name:${a}`],o(n),`
|
||||
`,{},...t("name"),`
|
||||
`,{},...t("name2"),`
|
||||
`,{},...t("name3")],["case",["!",c(a,n,"name")],["format",["coalesce",["get",`${r}name:${a}`],["get","pgf:name"],["get","name"]],o(n),`
|
||||
`,{},...t("name2"),`
|
||||
`,{},...t("name3")],["!",c(a,n,"name2")],["format",["coalesce",["get",`${r}name:${a}`],["get","pgf:name2"],["get","name2"]],o(n),`
|
||||
`,{},...t("name"),`
|
||||
`,{},...t("name3")],["format",["coalesce",["get",`${r}name:${a}`],["get","pgf:name3"],["get","name3"]],o(n),`
|
||||
`,{},...t("name"),`
|
||||
`,{},...t("name2")]]]]}l(s,"get_multiline_name");var m=[{lang:"ar",full_name:"Arabic",script:"Arabic"},{lang:"cs",full_name:"Czech",script:"Latin"},{lang:"bg",full_name:"Bulgarian",script:"Cyrillic"},{lang:"da",full_name:"Danish",script:"Latin"},{lang:"de",full_name:"German",script:"Latin"},{lang:"el",full_name:"Greek",script:"Greek"},{lang:"en",full_name:"English",script:"Latin"},{lang:"es",full_name:"Spanish",script:"Latin"},{lang:"et",full_name:"Estonian",script:"Latin"},{lang:"fa",full_name:"Persian",script:"Arabic"},{lang:"fi",full_name:"Finnish",script:"Latin"},{lang:"fr",full_name:"French",script:"Latin"},{lang:"ga",full_name:"Irish",script:"Latin"},{lang:"he",full_name:"Hebrew",script:"Hebrew"},{lang:"hi",full_name:"Hindi",script:"Devanagari"},{lang:"hr",full_name:"Croatian",script:"Latin"},{lang:"hu",full_name:"Hungarian",script:"Latin"},{lang:"id",full_name:"Indonesian",script:"Latin"},{lang:"it",full_name:"Italian",script:"Latin"},{lang:"ja",full_name:"Japanese",script:""},{lang:"ko",full_name:"Korean",script:"Hangul"},{lang:"lt",full_name:"Lithuanian",script:"Latin"},{lang:"lv",full_name:"Latvian",script:"Latin"},{lang:"ne",full_name:"Nepali",script:"Devanagari"},{lang:"nl",full_name:"Dutch",script:"Latin"},{lang:"no",full_name:"Norwegian",script:"Latin"},{lang:"mr",full_name:"Marathi",script:"Devanagari"},{lang:"mt",full_name:"Maltese",script:"Latin"},{lang:"pl",full_name:"Polish",script:"Latin"},{lang:"pt",full_name:"Portuguese",script:"Latin"},{lang:"ro",full_name:"Romanian",script:"Latin"},{lang:"ru",full_name:"Russian",script:"Cyrillic"},{lang:"sk",full_name:"Slovak",script:"Latin"},{lang:"sl",full_name:"Slovenian",script:"Latin"},{lang:"sv",full_name:"Swedish",script:"Latin"},{lang:"tr",full_name:"Turkish",script:"Latin"},{lang:"uk",full_name:"Ukrainian",script:"Cyrillic"},{lang:"ur",full_name:"Urdu",script:"Arabic"},{lang:"vi",full_name:"Vietnamese",script:"Latin"},{lang:"zh-Hans",full_name:"Chinese (Simplified)",script:"Han"},{lang:"zh-Hant",full_name:"Chinese (Traditional)",script:"Han"}];function _(a,e){return[{id:"background",type:"background",paint:{"background-color":e.background}},{id:"earth",type:"fill",filter:["==","$type","Polygon"],source:a,"source-layer":"earth",paint:{"fill-color":e.earth}},...e.landcover?[{id:"landcover",type:"fill",source:a,"source-layer":"landcover",paint:{"fill-color":["match",["get","kind"],"grassland",e.landcover.grassland,"barren",e.landcover.barren,"urban_area",e.landcover.urban_area,"farmland",e.landcover.farmland,"glacier",e.landcover.glacier,"scrub",e.landcover.scrub,e.landcover.forest],"fill-opacity":["interpolate",["linear"],["zoom"],5,1,7,0]}}]:[],{id:"landuse_park",type:"fill",source:a,"source-layer":"landuse",filter:["in","kind","national_park","park","cemetery","protected_area","nature_reserve","forest","golf_course","wood","nature_reserve","forest","scrub","grassland","grass","military","naval_base","airfield"],paint:{"fill-opacity":["interpolate",["linear"],["zoom"],6,0,11,1],"fill-color":["case",["in",["get","kind"],["literal",["national_park","park","cemetery","protected_area","nature_reserve","forest","golf_course"]]],e.park_b,["in",["get","kind"],["literal",["wood","nature_reserve","forest"]]],e.wood_b,["in",["get","kind"],["literal",["scrub","grassland","grass"]]],e.scrub_b,["in",["get","kind"],["literal",["glacier"]]],e.glacier,["in",["get","kind"],["literal",["sand"]]],e.sand,["in",["get","kind"],["literal",["military","naval_base","airfield"]]],e.zoo,e.earth]}},{id:"landuse_urban_green",type:"fill",source:a,"source-layer":"landuse",filter:["in","kind","allotments","village_green","playground"],paint:{"fill-color":e.park_b,"fill-opacity":.7}},{id:"landuse_hospital",type:"fill",source:a,"source-layer":"landuse",filter:["==","kind","hospital"],paint:{"fill-color":e.hospital}},{id:"landuse_industrial",type:"fill",source:a,"source-layer":"landuse",filter:["==","kind","industrial"],paint:{"fill-color":e.industrial}},{id:"landuse_school",type:"fill",source:a,"source-layer":"landuse",filter:["in","kind","school","university","college"],paint:{"fill-color":e.school}},{id:"landuse_beach",type:"fill",source:a,"source-layer":"landuse",filter:["in","kind","beach"],paint:{"fill-color":e.beach}},{id:"landuse_zoo",type:"fill",source:a,"source-layer":"landuse",filter:["in","kind","zoo"],paint:{"fill-color":e.zoo}},{id:"landuse_aerodrome",type:"fill",source:a,"source-layer":"landuse",filter:["in","kind","aerodrome"],paint:{"fill-color":e.aerodrome}},{id:"roads_runway",type:"line",source:a,"source-layer":"roads",filter:["==","kind_detail","runway"],paint:{"line-color":e.runway,"line-width":["interpolate",["exponential",1.6],["zoom"],10,0,12,4,18,30]}},{id:"roads_taxiway",type:"line",source:a,"source-layer":"roads",minzoom:13,filter:["==","kind_detail","taxiway"],paint:{"line-color":e.runway,"line-width":["interpolate",["exponential",1.6],["zoom"],13,0,13.5,1,15,6]}},{id:"landuse_runway",type:"fill",source:a,"source-layer":"landuse",filter:["any",["in
Line truncated
|
||||
//# sourceMappingURL=protomaps-themes-base.js.map
|
||||
Reference in new issue
Block a user