Link POI popups to offline Wikipedia via kiwix-serve
lib/maptool/kiwix.rb: a small reachability check for an external kiwix-serve instance. GET /api/kiwix reports it (same optional- integration pattern as offline-tiles/terrain-tiles); the frontend adds a "Nearby on Wikipedia" link to a point's popup pointing straight at kiwix-serve's own geo search (GET /search?latitude=&longitude= &distance=, confirmed from libkiwix's actual source rather than guessed) — linked to directly, not proxied/embedded, since it already has a full search UI of its own. docker-compose.yaml: new `kiwix` service (ghcr.io/kiwix/kiwix-serve) on the default network, KIWIX_URL wired to reach it by service name. The ZIM itself isn't part of the repo (large, self-contained archive) — data/kiwix/ gitignored same as the *.pmtiles files, setup steps in docs/development.adoc. No kiwix-serve/ZIM available here to test end-to-end — verified the app side against a stand-in local HTTP server (GET /api/kiwix flips available correctly) and confirmed the generated popup link matches the real geo-search query shape. docker-compose.yaml validated with `docker-compose config`. Full test suite: 118 runs, 0 failures.
This commit is contained in:
1 parent
7cd3171998
commit
24ac5931f8
13 files changed
+150
-9
No files matched your search
+21
-4
@@ -702,7 +702,7 @@ export class GpxPanelControl {
|
||||
const editable = feature.properties.category === "Note" && !!feature.properties.id && isLoggedIn();
|
||||
const popup = new Popup({ closeButton: true, maxWidth: "260px" })
|
||||
.setLngLat(event.lngLat)
|
||||
.setHTML(popupHtml(feature.properties, { editable }))
|
||||
.setHTML(popupHtml(feature.properties, { editable, lngLat: event.lngLat }))
|
||||
.addTo(this._map);
|
||||
if (editable) this._wireNoteActions(popup, path, feature);
|
||||
});
|
||||
@@ -754,7 +754,7 @@ export class GpxPanelControl {
|
||||
const bodyInput = content.querySelector(".gpx-popup__edit-body");
|
||||
titleInput.focus();
|
||||
content.querySelector(".gpx-popup__edit-cancel").addEventListener("click", () => {
|
||||
content.innerHTML = popupHtml(feature.properties, { editable: true });
|
||||
content.innerHTML = popupHtml(feature.properties, { editable: true, lngLat: popup.getLngLat() });
|
||||
this._wireNoteActions(popup, path, feature);
|
||||
});
|
||||
content.querySelector(".gpx-popup__edit-save").addEventListener("click", async () => {
|
||||
@@ -1077,13 +1077,26 @@ function gpxFileUrl(path) {
|
||||
return `/api/gpx/file/${path.split("/").map(encodeURIComponent).join("/")}`;
|
||||
}
|
||||
|
||||
// Set once from map.js after its one-time GET /api/kiwix check (same
|
||||
// fetch-once-at-load story as the offline-tiles/terrain-tiles checks) —
|
||||
// module-level rather than threaded through every popupHtml call, since
|
||||
// it's the same answer for the whole page's lifetime.
|
||||
let kiwixConfig = { available: false, url: "" };
|
||||
|
||||
export function setKiwixConfig(config) {
|
||||
kiwixConfig = config;
|
||||
}
|
||||
|
||||
// properties come straight from an uploaded GPX file — untrusted input —
|
||||
// so every value is escaped before landing in the popup's HTML.
|
||||
// editable (only ever true for a "Note" feature the viewer is logged in
|
||||
// for — see _registerPointInteractivity) adds Edit/Delete buttons; the
|
||||
// click handling for them lives in _wireNoteActions, not here, since this
|
||||
// only builds markup, it has no map/store access of its own.
|
||||
function popupHtml({ name, description, time, category, photos, icon }, { editable = false } = {}) {
|
||||
// only builds markup, it has no map/store access of its own. lngLat (the
|
||||
// popup's own anchor point) drives the "Nearby on Wikipedia" link — kiwix-
|
||||
// serve's own geo search, linked to directly rather than proxied/embedded
|
||||
// (it already has a full search UI of its own, see docs/development.adoc).
|
||||
function popupHtml({ name, description, time, category, photos, icon }, { editable = false, lngLat = null } = {}) {
|
||||
const parts = [];
|
||||
if (name) {
|
||||
const glyph = icon && POI_ICONS[icon] ? `<span class="gpx-popup__header-icon">${POI_ICONS[icon]}</span>` : "";
|
||||
@@ -1104,6 +1117,10 @@ function popupHtml({ name, description, time, category, photos, icon }, { editab
|
||||
parts.push(`<p class="gpx-popup__time">${escapeHtml(parsed.toLocaleString())}</p>`);
|
||||
}
|
||||
}
|
||||
if (kiwixConfig.available && lngLat) {
|
||||
const wikiUrl = `${kiwixConfig.url}/search?pattern=&latitude=${lngLat.lat}&longitude=${lngLat.lng}&distance=2000`;
|
||||
parts.push(`<p class="gpx-popup__wiki"><a href="${escapeHtml(wikiUrl)}" target="_blank" rel="noopener noreferrer">Nearby on Wikipedia →</a></p>`);
|
||||
}
|
||||
if (editable) {
|
||||
parts.push(
|
||||
`<div class="gpx-popup__note-actions"><button type="button" class="gpx-popup__edit">Edit</button><button type="button" class="gpx-popup__delete">Delete</button></div>`,
|
||||
|
||||
Reference in new issue
Block a user