diff --git a/docs/architecture.adoc b/docs/architecture.adoc index de1fcde..4fd8f07 100644 --- a/docs/architecture.adoc +++ b/docs/architecture.adoc @@ -154,6 +154,24 @@ manages in the page's own sidebar — both call the same `LayerToggleControl#toggle()`, so there's one source of truth for the panel's open/closed state either way. +=== Debug inspector (Alt+click) + +Alt+click anywhere on the map opens a popup listing every rendered +feature at that point — across all layers, not just this app's own GPX/ +FIT/radar layers — with its raw properties (`public/js/map.js`, a plain +`map.on("click", ...)` handler gated on `event.originalEvent.altKey`, +calling `map.queryRenderedFeatures(event.point)`). Inspired by NPMap4's +`infoboxControl`, a similar generic "show me everything here" debug +view kept separate from the curated popups those same features already +have. Deliberately no dedicated UI for it — a keyboard-modified click +needs no button, toggle state, or cursor change of its own, and it +coexists fine with layer-specific click handlers (e.g. a GPX waypoint's +normal popup) firing on the same click, since this is a power-user/ +debugging affordance, not a replacement for those. Feature properties +can come straight from an uploaded GPX/FIT file (untrusted input) via +this app's own layers, not just the base map, so every key/value is +HTML-escaped the same way `gpx.js`'s popups already are. + == Weather radar overlay `public/js/radar.js` adds a "Radar" map control that overlays recent diff --git a/docs/nps-resources.adoc b/docs/nps-resources.adoc index 51fa4e9..6609195 100644 --- a/docs/nps-resources.adoc +++ b/docs/nps-resources.adoc @@ -266,8 +266,16 @@ plugins (several still at `0.0.1-x`, pre-1.0), a few of which (`overview`, `carto`, `wms`) aren't even merged to `main` yet — see above. v5 is the direction NPS is migrating to, not (yet) a feature-for-feature replacement; expect this gap to close over time, not to stay this way. -Two things worth noting from v4: +Three things worth noting from v4: +* *`infoboxControl`* — example: + https://www.nps.gov/lib/npmap.js/4.2.1/examples/basic/infobox.html. A + generic "click anything, show every raw property in a table" debug + view, kept separate from a dataset's own curated popup. *Adopted* — not + the code (Leaflet, not portable) but the concept: this app now has the + same thing as an Alt+click handler (see `docs/architecture.adoc`'s + "Debug inspector" section), showing `queryRenderedFeatures` results — + base map OSM tags included, not just this app's own layers. * *Geocoder control* — example: https://www.nps.gov/lib/npmap.js/4.2.1/examples/controls/geocoder-control.html. Config is `geocoderControl: { searchPlaces: true }`: a search box that diff --git a/public/css/style.css b/public/css/style.css index 3d3aa01..6451c32 100644 --- a/public/css/style.css +++ b/public/css/style.css @@ -438,6 +438,40 @@ html, body { margin: 4px 0 0; } +.debug-popup .maplibregl-popup-content { + max-height: 320px; + overflow-y: auto; +} + +.debug-popup__feature { + font-size: 11px; + font-family: monospace; +} + +.debug-popup__feature + .debug-popup__feature { + margin-top: 8px; + padding-top: 8px; + border-top: 1px solid #eee; +} + +.debug-popup__feature table { + border-collapse: collapse; + width: 100%; +} + +.debug-popup__feature th { + text-align: left; + color: #888; + font-weight: normal; + padding: 1px 6px 1px 0; + vertical-align: top; + white-space: nowrap; +} + +.debug-popup__feature td { + word-break: break-word; +} + .gpx-popup__time { color: #888; font-size: 12px; diff --git a/public/js/map.js b/public/js/map.js index cf9591b..8b2ceca 100644 --- a/public/js/map.js +++ b/public/js/map.js @@ -2,6 +2,7 @@ import { Map as MaplibreMap, NavigationControl, GeolocateControl, + Popup, addProtocol, } from "/vendor/maplibre-gl/maplibre-gl.mjs"; import { RadarControl } from "/js/radar.js"; @@ -263,3 +264,40 @@ fetch("/api/offline-tiles") // is rather than guessing — this is the same-origin request itself // failing, not evidence offline tiles are actually unavailable. }); + +// Alt+click anywhere shows every rendered feature at that point — base map +// (OSM tags via OpenFreeMap/protomaps), GPX/FIT layers, radar, all of it — +// with its raw properties, inspired by NPMap4's infoboxControl (a generic +// "show me everything here" debug view, kept separate from the curated +// popups those same features already have via their own layer-specific +// click handlers, e.g. gpx.js's waypoint popups — both can fire on the same +// click, which is fine, this is a power-user/debugging affordance, not +// something replacing the normal popups). No dedicated UI for this +// deliberately — a keyboard-modified click needs no button, state, or +// cursor change of its own. +map.on("click", (event) => { + if (!event.originalEvent.altKey) return; + + const features = map.queryRenderedFeatures(event.point); + if (features.length === 0) return; + + const html = features + .map((feature) => { + const rows = Object.entries(feature.properties) + .map(([key, value]) => `${escapeHtml(key)}${escapeHtml(String(value))}`) + .join(""); + return `
${escapeHtml(feature.layer.id)}${rows || ""}
(no properties)
`; + }) + .join(""); + + new Popup({ maxWidth: "320px", className: "debug-popup" }).setLngLat(event.lngLat).setHTML(html).addTo(map); +}); + +// Feature properties can come straight from an uploaded GPX/FIT file (an +// untrusted source) via this app's own layers, not just the base map, so +// every value is escaped the same way gpx.js's popups already do. +function escapeHtml(text) { + const div = document.createElement("div"); + div.textContent = text; + return div.innerHTML; +}