[fix](trx-frontend): replace locator wavelength filters with bands

Co-authored-by: OpenAI Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
This commit is contained in:
2026-03-03 22:12:27 +01:00
parent b9b420fa54
commit 4bdb4937c9
3 changed files with 80 additions and 48 deletions
@@ -3171,12 +3171,37 @@ const stationMarkers = new Map();
const locatorMarkers = new Map();
const mapMarkers = new Set();
const mapFilter = { ais: true, vdes: true, aprs: true, bookmark: true, ft8: true, wspr: true };
const mapLocatorFilter = { types: new Set(), wavelengths: new Set() };
const mapLocatorFilter = { types: new Set(), bands: new Set() };
const APRS_TRACK_MAX_POINTS = 64;
const AIS_TRACK_MAX_POINTS = 64;
const aisMarkers = new Map();
const vdesMarkers = new Map();
let selectedAisTrackMmsi = null;
const HAM_BANDS = [
{ label: "2200m", lo: 135_700, hi: 137_800 },
{ label: "630m", lo: 472_000, hi: 479_000 },
{ label: "160m", lo: 1_800_000, hi: 2_000_000 },
{ label: "80m", lo: 3_500_000, hi: 4_000_000 },
{ label: "60m", lo: 5_351_500, hi: 5_366_500 },
{ label: "40m", lo: 7_000_000, hi: 7_300_000 },
{ label: "30m", lo: 10_100_000, hi: 10_150_000 },
{ label: "20m", lo: 14_000_000, hi: 14_350_000 },
{ label: "17m", lo: 18_068_000, hi: 18_168_000 },
{ label: "15m", lo: 21_000_000, hi: 21_450_000 },
{ label: "12m", lo: 24_890_000, hi: 24_990_000 },
{ label: "10m", lo: 28_000_000, hi: 29_700_000 },
{ label: "6m", lo: 50_000_000, hi: 54_000_000 },
{ label: "4m", lo: 70_000_000, hi: 71_000_000 },
{ label: "3m", lo: 88_000_000, hi: 108_000_000 },
{ label: "2m", lo: 144_000_000, hi: 148_000_000 },
{ label: "1m", lo: 222_000_000, hi: 225_000_000 },
{ label: "70cm", lo: 420_000_000, hi: 450_000_000 },
{ label: "23cm", lo: 1_240_000_000, hi: 1_300_000_000 },
{ label: "13cm", lo: 2_300_000_000, hi: 2_450_000_000 },
{ label: "9cm", lo: 3_300_000_000, hi: 3_500_000_000 },
{ label: "6cm", lo: 5_650_000_000, hi: 5_925_000_000 },
{ label: "3cm", lo: 10_000_000_000, hi: 10_500_000_000 },
];
function locatorSourceLabel(type) {
if (type === "bookmark") return "Bookmarks";
@@ -3190,24 +3215,31 @@ function locatorFilterColor(type) {
return "#ff9b1a";
}
function collectWavelengthMeta(freqs) {
function bandForHz(hz) {
if (!Number.isFinite(hz) || hz <= 0) return null;
for (const band of HAM_BANDS) {
if (hz >= band.lo && hz <= band.hi) return band;
}
return null;
}
function collectBandMeta(freqs) {
const out = new Map();
if (!Array.isArray(freqs)) return out;
for (const hz of freqs) {
if (!Number.isFinite(hz) || hz <= 0) continue;
const label = formatWavelength(hz);
if (label !== "--" && !out.has(label)) out.set(label, hz);
const band = bandForHz(hz);
if (band && !out.has(band.label)) out.set(band.label, band.lo);
}
return out;
}
function assignLocatorMarkerMeta(marker, sourceType, wavelengthMeta) {
function assignLocatorMarkerMeta(marker, sourceType, bandMeta) {
if (!marker) return;
const safeMeta = wavelengthMeta instanceof Map ? wavelengthMeta : new Map();
const safeMeta = bandMeta instanceof Map ? bandMeta : new Map();
marker._locatorFilterMeta = {
sourceType,
wavelengths: new Set(safeMeta.keys()),
wavelengthMeta: new Map(safeMeta),
bands: new Set(safeMeta.keys()),
bandMeta: new Map(safeMeta),
};
}
@@ -3215,7 +3247,7 @@ function renderMapLocatorChipRow(container, items, selectedSet, kind) {
if (!container) return;
container.innerHTML = "";
if (!Array.isArray(items) || items.length === 0) {
container.innerHTML = `<span class="map-locator-empty">All ${kind === "type" ? "locator sources" : "wavelengths"} visible</span>`;
container.innerHTML = `<span class="map-locator-empty">All ${kind === "type" ? "locator sources" : "bands"} visible</span>`;
return;
}
for (const item of items) {
@@ -3226,8 +3258,8 @@ function renderMapLocatorChipRow(container, items, selectedSet, kind) {
btn.dataset.filterKind = kind;
btn.dataset.filterKey = item.key;
btn.style.setProperty("--chip-color", item.color);
btn.innerHTML = item.kind === "wavelength"
? `<span class="map-locator-chip-wavelength">${escapeMapHtml(item.label)}</span>`
btn.innerHTML = item.kind === "band"
? `<span class="map-locator-chip-band">${escapeMapHtml(item.label)}</span>`
: `<span class="map-locator-chip-text">${escapeMapHtml(item.label)}</span>`;
container.appendChild(btn);
}
@@ -3235,11 +3267,11 @@ function renderMapLocatorChipRow(container, items, selectedSet, kind) {
function rebuildMapLocatorFilters() {
const typeEl = document.getElementById("map-locator-mode-filter");
const wavelengthEl = document.getElementById("map-locator-wavelength-filter");
if (!typeEl || !wavelengthEl) return;
const bandEl = document.getElementById("map-locator-band-filter");
if (!typeEl || !bandEl) return;
const typeMap = new Map();
const wavelengthMap = new Map();
const bandMap = new Map();
for (const entry of locatorMarkers.values()) {
const sourceType = entry?.sourceType;
if (!sourceType) continue;
@@ -3251,15 +3283,15 @@ function rebuildMapLocatorFilters() {
kind: "type",
});
}
const meta = entry?.wavelengthMeta instanceof Map ? entry.wavelengthMeta : new Map();
const meta = entry?.bandMeta instanceof Map ? entry.bandMeta : new Map();
for (const [label, hz] of meta.entries()) {
const key = `${sourceType}:${label}`;
if (wavelengthMap.has(key)) continue;
wavelengthMap.set(key, {
if (bandMap.has(key)) continue;
bandMap.set(key, {
key,
label,
color: locatorFilterColor(sourceType),
kind: "wavelength",
kind: "band",
sortHz: Number.isFinite(hz) ? hz : 0,
});
}
@@ -3268,18 +3300,18 @@ function rebuildMapLocatorFilters() {
for (const key of Array.from(mapLocatorFilter.types)) {
if (!typeMap.has(key)) mapLocatorFilter.types.delete(key);
}
for (const key of Array.from(mapLocatorFilter.wavelengths)) {
if (!wavelengthMap.has(key)) mapLocatorFilter.wavelengths.delete(key);
for (const key of Array.from(mapLocatorFilter.bands)) {
if (!bandMap.has(key)) mapLocatorFilter.bands.delete(key);
}
const typeItems = ["bookmark", "ft8", "wspr"]
.filter((key) => typeMap.has(key))
.map((key) => typeMap.get(key));
const wavelengthItems = Array.from(wavelengthMap.values())
const bandItems = Array.from(bandMap.values())
.sort((a, b) => (b.sortHz - a.sortHz) || a.label.localeCompare(b.label));
renderMapLocatorChipRow(typeEl, typeItems, mapLocatorFilter.types, "type");
renderMapLocatorChipRow(wavelengthEl, wavelengthItems, mapLocatorFilter.wavelengths, "wavelength");
renderMapLocatorChipRow(bandEl, bandItems, mapLocatorFilter.bands, "band");
}
function markerPassesLocatorFilters(marker) {
@@ -3288,14 +3320,14 @@ function markerPassesLocatorFilters(marker) {
if (mapLocatorFilter.types.size > 0 && !mapLocatorFilter.types.has(meta.sourceType)) {
return false;
}
if (mapLocatorFilter.wavelengths.size > 0) {
const wanted = Array.from(mapLocatorFilter.wavelengths);
if (mapLocatorFilter.bands.size > 0) {
const wanted = Array.from(mapLocatorFilter.bands);
const matches = wanted.some((key) => {
const sep = key.indexOf(":");
if (sep < 0) return false;
const sourceType = key.slice(0, sep);
const label = key.slice(sep + 1);
return sourceType === meta.sourceType && meta.wavelengths instanceof Set && meta.wavelengths.has(label);
return sourceType === meta.sourceType && meta.bands instanceof Set && meta.bands.has(label);
});
if (!matches) return false;
}
@@ -3530,12 +3562,12 @@ function initAprsMap() {
const bounds = maidenheadToBounds(entry.grid);
if (!bounds) continue;
entry.sourceType = "bookmark";
entry.wavelengthMeta = collectWavelengthMeta((entry.bookmarks || []).map((bm) => Number(bm?.freq_hz)));
entry.bandMeta = collectBandMeta((entry.bookmarks || []).map((bm) => Number(bm?.freq_hz)));
entry.marker = L.rectangle(bounds, locatorStyleForCount(entry.bookmarks?.length || 1, "bookmark"))
.addTo(aprsMap)
.bindPopup(buildBookmarkLocatorPopupHtml(entry.grid, entry.bookmarks || []));
entry.marker.__trxType = "bookmark";
assignLocatorMarkerMeta(entry.marker, entry.sourceType, entry.wavelengthMeta);
assignLocatorMarkerMeta(entry.marker, entry.sourceType, entry.bandMeta);
mapMarkers.add(entry.marker);
}
rebuildMapLocatorFilters();
@@ -3548,7 +3580,7 @@ function initAprsMap() {
const ft8Filter = document.getElementById("map-filter-ft8");
const wsprFilter = document.getElementById("map-filter-wspr");
const locatorModeFilterEl = document.getElementById("map-locator-mode-filter");
const locatorWavelengthFilterEl = document.getElementById("map-locator-wavelength-filter");
const locatorBandFilterEl = document.getElementById("map-locator-band-filter");
if (aisFilter) {
aisFilter.addEventListener("change", () => {
mapFilter.ais = aisFilter.checked;
@@ -3606,16 +3638,16 @@ function initAprsMap() {
applyMapFilter();
});
}
if (locatorWavelengthFilterEl) {
locatorWavelengthFilterEl.addEventListener("click", (e) => {
const chip = e.target.closest(".map-locator-chip[data-filter-kind='wavelength']");
if (locatorBandFilterEl) {
locatorBandFilterEl.addEventListener("click", (e) => {
const chip = e.target.closest(".map-locator-chip[data-filter-kind='band']");
if (!chip) return;
const key = String(chip.dataset.filterKey || "");
if (!key) return;
if (mapLocatorFilter.wavelengths.has(key)) {
mapLocatorFilter.wavelengths.delete(key);
if (mapLocatorFilter.bands.has(key)) {
mapLocatorFilter.bands.delete(key);
} else {
mapLocatorFilter.wavelengths.add(key);
mapLocatorFilter.bands.add(key);
}
rebuildMapLocatorFilters();
applyMapFilter();
@@ -4287,18 +4319,18 @@ window.syncBookmarkMapLocators = function(bookmarks) {
for (const [key, next] of grouped.entries()) {
const existing = locatorMarkers.get(key);
const popupHtml = buildBookmarkLocatorPopupHtml(next.grid, next.bookmarks);
const wavelengthMeta = collectWavelengthMeta(next.bookmarks.map((bm) => Number(bm?.freq_hz)));
const bandMeta = collectBandMeta(next.bookmarks.map((bm) => Number(bm?.freq_hz)));
if (existing) {
existing.grid = next.grid;
existing.bounds = next.bounds;
existing.bookmarks = next.bookmarks;
existing.sourceType = "bookmark";
existing.wavelengthMeta = wavelengthMeta;
existing.bandMeta = bandMeta;
if (existing.marker) {
existing.marker.setBounds(next.bounds);
existing.marker.setStyle(locatorStyleForCount(next.bookmarks.length, "bookmark"));
existing.marker.setPopupContent(popupHtml);
assignLocatorMarkerMeta(existing.marker, existing.sourceType, existing.wavelengthMeta);
assignLocatorMarkerMeta(existing.marker, existing.sourceType, existing.bandMeta);
}
continue;
}
@@ -4309,7 +4341,7 @@ window.syncBookmarkMapLocators = function(bookmarks) {
bounds: next.bounds,
bookmarks: next.bookmarks,
sourceType: "bookmark",
wavelengthMeta,
bandMeta,
};
locatorMarkers.set(key, entry);
if (aprsMap) {
@@ -4317,7 +4349,7 @@ window.syncBookmarkMapLocators = function(bookmarks) {
.addTo(aprsMap)
.bindPopup(popupHtml);
entry.marker.__trxType = "bookmark";
assignLocatorMarkerMeta(entry.marker, entry.sourceType, entry.wavelengthMeta);
assignLocatorMarkerMeta(entry.marker, entry.sourceType, entry.bandMeta);
mapMarkers.add(entry.marker);
}
}
@@ -4352,7 +4384,7 @@ window.ft8MapAddLocator = function(message, grids, type = "ft8", station = null,
if (!(existing.stationDetails instanceof Map)) existing.stationDetails = new Map();
existing.stationDetails.set(detailKey, { ...detailEntry });
existing.sourceType = markerType;
existing.wavelengthMeta = collectWavelengthMeta(
existing.bandMeta = collectBandMeta(
Array.from(existing.stationDetails.values()).map((detail) => Number(detail?.freq_hz))
);
const count = Math.max(existing.stationDetails.size, existing.stations.size || 0, 1);
@@ -4360,7 +4392,7 @@ window.ft8MapAddLocator = function(message, grids, type = "ft8", station = null,
existing.marker.setStyle(locatorStyleForCount(count, markerType));
existing.marker.setPopupContent(tooltipHtml);
existing.marker.setTooltipContent(tooltipHtml);
assignLocatorMarkerMeta(existing.marker, existing.sourceType, existing.wavelengthMeta);
assignLocatorMarkerMeta(existing.marker, existing.sourceType, existing.bandMeta);
rebuildMapLocatorFilters();
applyMapFilter();
continue;
@@ -4382,11 +4414,11 @@ window.ft8MapAddLocator = function(message, grids, type = "ft8", station = null,
opacity: 1,
});
marker.__trxType = markerType;
const wavelengthMeta = collectWavelengthMeta(
const bandMeta = collectBandMeta(
Array.from(stationDetails.values()).map((detail) => Number(detail?.freq_hz))
);
assignLocatorMarkerMeta(marker, markerType, wavelengthMeta);
locatorMarkers.set(key, { marker, stations, stationDetails, sourceType: markerType, wavelengthMeta });
assignLocatorMarkerMeta(marker, markerType, bandMeta);
locatorMarkers.set(key, { marker, stations, stationDetails, sourceType: markerType, bandMeta });
mapMarkers.add(marker);
}
rebuildMapLocatorFilters();
@@ -583,8 +583,8 @@
<div id="map-locator-mode-filter" class="map-locator-chip-row"></div>
</div>
<div class="map-locator-filter-group">
<span class="map-locator-filter-label">Wavelength</span>
<div id="map-locator-wavelength-filter" class="map-locator-chip-row"></div>
<span class="map-locator-filter-label">Bands</span>
<div id="map-locator-band-filter" class="map-locator-chip-row"></div>
</div>
</div>
<div id="aprs-map"></div>
@@ -1626,7 +1626,7 @@ small { color: var(--text-muted); }
font-size: 0.8rem;
font-weight: 600;
}
.map-locator-chip-wavelength {
.map-locator-chip-band {
font-size: 0.78rem;
font-weight: 700;
color: var(--chip-color);