[feat](trx-frontend-http): improve locator map plotting and themed filters

Refine map plotting and filter UX in HTTP frontend plugins.\n\n- support plotting multiple locator squares from FT8/WSPR messages\n- show locator lists in popup content as newline-separated entries\n- add WSPR map layer filter toggle and marker typing\n- style filter controls for strong dark/light mode contrast\n- keep themed behavior aligned with map and control updates\n\nCo-authored-by: OpenAI Codex <codex@openai.com>

Signed-off-by: Stanislaw Grams <stanislawgrams@gmail.com>
This commit is contained in:
2026-02-13 02:11:31 +01:00
parent f637cf23d3
commit 4f35be539f
5 changed files with 95 additions and 22 deletions
@@ -990,7 +990,7 @@ let aprsMapBaseLayer = null;
let aprsMapReceiverMarker = null;
const stationMarkers = new Map();
const mapMarkers = new Set();
const mapFilter = { aprs: true, ft8: true };
const mapFilter = { aprs: true, ft8: true, wspr: true };
function mapTileSpecForTheme(theme) {
if (theme === "dark") {
@@ -1044,6 +1044,7 @@ function initAprsMap() {
const aprsFilter = document.getElementById("map-filter-aprs");
const ft8Filter = document.getElementById("map-filter-ft8");
const wsprFilter = document.getElementById("map-filter-wspr");
if (aprsFilter) {
aprsFilter.addEventListener("change", () => {
mapFilter.aprs = aprsFilter.checked;
@@ -1056,6 +1057,12 @@ function initAprsMap() {
applyMapFilter();
});
}
if (wsprFilter) {
wsprFilter.addEventListener("change", () => {
mapFilter.wspr = wsprFilter.checked;
applyMapFilter();
});
}
}
function sizeAprsMapToViewport() {
@@ -1141,27 +1148,43 @@ function applyMapFilter() {
if (!aprsMap) return;
mapMarkers.forEach((marker) => {
const type = marker.__trxType;
const visible = (type === "aprs" && mapFilter.aprs) || (type === "ft8" && mapFilter.ft8);
const visible =
(type === "aprs" && mapFilter.aprs) ||
(type === "ft8" && mapFilter.ft8) ||
(type === "wspr" && mapFilter.wspr);
const onMap = aprsMap.hasLayer(marker);
if (visible && !onMap) marker.addTo(aprsMap);
if (!visible && onMap) marker.removeFrom(aprsMap);
});
}
window.ft8MapAddLocator = function(message, grid) {
function escapeMapHtml(input) {
return String(input)
.replaceAll("&", "&amp;")
.replaceAll("<", "&lt;")
.replaceAll(">", "&gt;")
.replaceAll("\"", "&quot;");
}
window.ft8MapAddLocator = function(message, grids, type = "ft8") {
if (!aprsMap) initAprsMap();
if (!aprsMap) return;
const bounds = maidenheadToBounds(grid);
if (!bounds) return;
const popupContent = `<b>${grid}</b><br>${message}`;
const marker = L.rectangle(bounds, {
color: "#ffb020",
weight: 1,
fillColor: "#ffb020",
fillOpacity: 0.25,
}).addTo(aprsMap).bindPopup(popupContent);
marker.__trxType = "ft8";
mapMarkers.add(marker);
if (!Array.isArray(grids) || grids.length === 0) return;
const unique = [...new Set(grids.map((g) => String(g).toUpperCase()))];
const locatorsLines = unique.map((g) => escapeMapHtml(g)).join("<br>");
for (const grid of unique) {
const bounds = maidenheadToBounds(grid);
if (!bounds) continue;
const popupContent = `<b>${escapeMapHtml(grid)}</b><br>${locatorsLines}`;
const marker = L.rectangle(bounds, {
color: "#ffb020",
weight: 1,
fillColor: "#ffb020",
fillOpacity: 0.25,
}).addTo(aprsMap).bindPopup(popupContent);
marker.__trxType = type === "wspr" ? "wspr" : "ft8";
mapMarkers.add(marker);
}
applyMapFilter();
};