import {
Map as MaplibreMap,
NavigationControl,
GeolocateControl,
addProtocol,
} from "/vendor/maplibre-gl/maplibre-gl.mjs";
import { RadarControl } from "/js/radar.js";
import { GpxPanelControl } from "/js/gpx.js";
const CENTER = [17.1077, 48.1486];
const ZOOM = 13;
const ONLINE_STYLE = "https://tiles.openfreemap.org/styles/liberty";
const protocol = new pmtiles.Protocol();
addProtocol("pmtiles", protocol.tile);
// No label layers here (see docs/architecture.adoc): rendering text needs a
// glyphs server, which the offline style deliberately does not depend on.
function offlineStyle() {
const theme = protomaps_themes_base.namedTheme("light");
const pmtilesUrl = `${window.location.origin}/tiles/offline.pmtiles`;
return {
version: 8,
sources: {
offline: {
type: "vector",
url: `pmtiles://${pmtilesUrl}`,
attribution: "© OpenStreetMap contributors",
},
},
layers: protomaps_themes_base.layers("offline", theme),
};
}
class LayerToggleControl {
constructor(onSelect) {
this._onSelect = onSelect;
}
onAdd() {
this._container = document.createElement("div");
this._container.className = "maplibregl-ctrl maplibregl-ctrl-group layer-toggle";
this._container.innerHTML = `
`;
this._container.addEventListener("click", (event) => {
const button = event.target.closest("button[data-style]");
if (button) this._onSelect(button.dataset.style);
});
return this._container;
}
onRemove() {
this._container.remove();
}
setActive(name) {
this._container.querySelectorAll("button").forEach((button) => {
button.classList.toggle("active", button.dataset.style === name);
});
}
}
const map = new MaplibreMap({
container: "map",
style: ONLINE_STYLE,
center: CENTER,
zoom: ZOOM,
});
map.addControl(new NavigationControl(), "top-right");
map.addControl(
new GeolocateControl({
positionOptions: { enableHighAccuracy: true },
trackUserLocation: true,
showAccuracyCircle: true,
}),
"top-right",
);
map.addControl(new RadarControl(map), "top-right");
const layerToggle = new LayerToggleControl(applyStyle);
map.addControl(layerToggle, "top-left");
layerToggle.setActive("online");
map.addControl(new GpxPanelControl(map), "top-left");
function applyStyle(name) {
layerToggle.setActive(name);
map.setStyle(name === "offline" ? offlineStyle() : ONLINE_STYLE);
}
// If a local PMTiles file is present, prefer it as the default style — its
// presence signals the app is meant to be used offline here.
fetch("/api/offline-tiles")
.then((response) => response.json())
.then(({ available }) => {
if (available) applyStyle("offline");
})
.catch(() => {
// Backend check failed; keep the online style.
});