From 5f803e4c0d74b03b0de5e75e3546fdea8519f9e9 Mon Sep 17 00:00:00 2001 From: Graham McIntire Date: Sat, 11 Apr 2026 18:08:06 -0500 Subject: [PATCH] Add 6-char subsquare grid overlay and fix map edge rendering - Show subsquares (5' x 2.5') at zoom >= 11 with lighter styling - Pad map bounds by 10% so edge tiles always have score data - Re-send bounds after 500ms to catch late container layout changes --- assets/js/maidenhead_grid.ts | 45 +++++++++++++++++++++++++------ assets/js/propagation_map_hook.ts | 5 +++- 2 files changed, 41 insertions(+), 9 deletions(-) diff --git a/assets/js/maidenhead_grid.ts b/assets/js/maidenhead_grid.ts index cb290e00..49ee23cc 100644 --- a/assets/js/maidenhead_grid.ts +++ b/assets/js/maidenhead_grid.ts @@ -73,25 +73,29 @@ function drawGridSquare( c.south > bounds.getNorth() || c.north < bounds.getSouth()) return const field = precision <= 2 + const sub = precision >= 6 + const weight = field ? 2.5 : sub ? 1 : 2 + const opacity = sub ? 0.5 : 0.8 layerGroup.addLayer(L.rectangle( [[c.south, c.west], [c.north, c.east]], - { color: "#E63946", weight: field ? 2.5 : 2, opacity: 0.8, fillOpacity: 0, interactive: false } + { color: "#E63946", weight, opacity, fillOpacity: 0, interactive: false } )) const sw = map.latLngToContainerPoint([c.south, c.west]) const ne = map.latLngToContainerPoint([c.north, c.east]) const px = Math.abs(ne.x - sw.x) - const minW = field ? 30 : 40 + const minW = sub ? 25 : field ? 30 : 40 if (px > minW) { - const len = field ? 2 : 4 - const text = grid.substring(0, len) - const fs = Math.max(10, Math.min(14, Math.round(px / (len * 1.2)))) + // Subsquares show just the last 2 chars (e.g. "kp"), others show full label + const text = sub ? grid.substring(4, 6) : grid.substring(0, field ? 2 : 4) + const len = text.length + const fs = Math.max(9, Math.min(sub ? 12 : 14, Math.round(px / (len * 1.2)))) layerGroup.addLayer(L.marker([c.center.lat, c.center.lon], { interactive: false, icon: L.divIcon({ className: "grid-label", - html: `
${text}
`, + html: `
${text}
`, iconSize: [Math.round(len * fs * 0.8), Math.round(fs * 1.5)], iconAnchor: [Math.round(len * fs * 0.4), Math.round(fs * 0.75)] }) @@ -105,6 +109,7 @@ export function updateGridOverlay(map: L.Map, gridLayer: L.LayerGroup): void { const bounds = map.getBounds() const zoom = map.getZoom() const showSquares = zoom >= 7 + const showSubsquares = zoom >= 11 const south = Math.max(bounds.getSouth(), -90) const north = Math.min(bounds.getNorth(), 90) @@ -133,8 +138,32 @@ export function updateGridOverlay(map: L.Map, gridLayer: L.LayerGroup): void { for (let lon = wE; lon < eE; lon += 2) { for (let lat = sE; lat < nE; lat += 1) { const nLon = ((lon % 360) + 540) % 360 - 180 - const g = new GridSquare(lat + 0.5, nLon + 1).encode(4) - drawGridSquare(g.substring(0, 4), 4, bounds, gridLayer, drawn, map) + + if (showSubsquares) { + // Draw the 4-char square boundary first + const sq = new GridSquare(lat + 0.5, nLon + 1).encode(4) + drawGridSquare(sq.substring(0, 4), 4, bounds, gridLayer, drawn, map) + + // Then draw 6-char subsquares (24 x 24 = 576 per square, 5' x 2.5') + const subW = 5 / 60 + const subH = 2.5 / 60 + const startI = Math.max(0, Math.floor((bounds.getWest() - lon) / subW)) + const endI = Math.min(24, Math.ceil((bounds.getEast() - lon) / subW)) + const startJ = Math.max(0, Math.floor((bounds.getSouth() - lat) / subH)) + const endJ = Math.min(24, Math.ceil((bounds.getNorth() - lat) / subH)) + + for (let i = startI; i < endI; i++) { + for (let j = startJ; j < endJ; j++) { + const cLon = nLon + i * subW + subW / 2 + const cLat = lat + j * subH + subH / 2 + const sub = new GridSquare(cLat, cLon).encode(6) + drawGridSquare(sub, 6, bounds, gridLayer, drawn, map) + } + } + } else { + const g = new GridSquare(lat + 0.5, nLon + 1).encode(4) + drawGridSquare(g.substring(0, 4), 4, bounds, gridLayer, drawn, map) + } } } } diff --git a/assets/js/propagation_map_hook.ts b/assets/js/propagation_map_hook.ts index d4cf7a2f..5093cae0 100644 --- a/assets/js/propagation_map_hook.ts +++ b/assets/js/propagation_map_hook.ts @@ -852,6 +852,8 @@ export const PropagationMap: Record & { this.el.addEventListener("show-loading", () => topbar.show(300)) requestAnimationFrame(() => this.sendBounds()) + // Re-check bounds after layout settles (sidebar, fonts, etc.) + setTimeout(() => { this.map.invalidateSize(); this.sendBounds() }, 500) this.map.on("moveend", () => { this.sendBounds() if (this.gridVisible) { @@ -1124,7 +1126,8 @@ export const PropagationMap: Record & { sendBounds(this: PropagationMapHook) { topbar.show() - const b = this.map.getBounds() + // Pad bounds by ~0.5° so Leaflet edge tiles always have data to render + const b = this.map.getBounds().pad(0.1) this.pushEvent("map_bounds", { south: b.getSouth(), north: b.getNorth(),