diff --git a/templates/index.html b/templates/index.html
index d3877d0..0b72ba7 100644
--- a/templates/index.html
+++ b/templates/index.html
@@ -66,6 +66,7 @@
+
@@ -9986,6 +9987,7 @@
// APRS Functions
// ============================================
let aprsMap = null;
+ let aprsMapOverlays = null;
let aprsMarkers = {};
let aprsEventSource = null;
let isAprsRunning = false;
@@ -10137,47 +10139,6 @@
});
}
- function createAprsFallbackGridLayer() {
- const layer = L.gridLayer({
- tileSize: 256,
- updateWhenIdle: true,
- attribution: 'Local fallback grid'
- });
- layer.createTile = function(coords) {
- const tile = document.createElement('canvas');
- tile.width = 256;
- tile.height = 256;
- const ctx = tile.getContext('2d');
-
- ctx.fillStyle = '#08121c';
- ctx.fillRect(0, 0, 256, 256);
-
- ctx.strokeStyle = 'rgba(0, 212, 255, 0.12)';
- ctx.lineWidth = 1;
- ctx.beginPath();
- ctx.moveTo(0, 0);
- ctx.lineTo(256, 0);
- ctx.moveTo(0, 0);
- ctx.lineTo(0, 256);
- ctx.stroke();
-
- ctx.strokeStyle = 'rgba(255, 255, 255, 0.06)';
- ctx.beginPath();
- ctx.moveTo(128, 0);
- ctx.lineTo(128, 256);
- ctx.moveTo(0, 128);
- ctx.lineTo(256, 128);
- ctx.stroke();
-
- ctx.fillStyle = 'rgba(160, 220, 255, 0.28)';
- ctx.font = '11px "JetBrains Mono", monospace';
- ctx.fillText(`Z${coords.z} X${coords.x} Y${coords.y}`, 12, 22);
-
- return tile;
- };
- return layer;
- }
-
async function initAprsMap() {
if (aprsMap) return;
@@ -10206,26 +10167,22 @@
const initialLon = hasUserLocation ? aprsUserLocation.lon : (hasGpsLocation ? gpsLon : -98.5795);
const initialZoom = (hasUserLocation || hasGpsLocation) ? 8 : 4;
- aprsMap = L.map('aprsMap').setView([initialLat, initialLon], initialZoom);
+ aprsMap = MapUtils.init('aprsMap', {
+ center: [initialLat, initialLon],
+ zoom: initialZoom,
+ minZoom: 2,
+ maxZoom: 18,
+ });
+ if (!aprsMap) return;
window.aprsMap = aprsMap;
- // Zero-network fallback so mode switches never block on external tiles.
- const fallbackTiles = createAprsFallbackGridLayer().addTo(aprsMap);
-
- // Upgrade tiles in background via Settings (with timeout fallback)
- if (typeof Settings !== 'undefined') {
- try {
- await Promise.race([
- Settings.init(),
- new Promise((_, reject) => setTimeout(() => reject(new Error('Settings timeout')), 5000))
- ]);
- aprsMap.removeLayer(fallbackTiles);
- Settings.createTileLayer().addTo(aprsMap);
- Settings.registerMap(aprsMap);
- } catch (e) {
- console.warn('APRS: Settings init failed/timed out, using fallback tiles:', e);
- }
- }
+ aprsMapOverlays = MapUtils.addTacticalOverlays(aprsMap, {
+ hudPanels: {
+ modeName: 'APRS',
+ getContactCount: () => Object.keys(aprsStations).length,
+ },
+ scaleBar: true,
+ });
// Add user marker if GPS position is already available
if (gpsConnected && hasGpsLocation) {
@@ -11327,6 +11284,7 @@
// Ground Track Map
let groundTrackMap = null;
+ let gpsMapOverlays = null;
let groundTrackLine = null;
let satMarker = null;
let observerMarker = null;
@@ -11336,47 +11294,28 @@
const mapContainer = document.getElementById('groundTrackMap');
if (!mapContainer || groundTrackMap) return;
- groundTrackMap = L.map('groundTrackMap', {
+ groundTrackMap = MapUtils.init('groundTrackMap', {
center: [20, 0],
zoom: 1,
zoomControl: true,
- attributionControl: false
+ attributionControl: false,
});
+ if (!groundTrackMap) return;
window.groundTrackMap = groundTrackMap;
- // Add fallback tiles immediately so the map is visible instantly
- const fallbackTiles = L.tileLayer('https://cartodb-basemaps-{s}.global.ssl.fastly.net/dark_all/{z}/{x}/{y}.png', {
- attribution: '© OSM © CARTO',
- maxZoom: 19,
- subdomains: 'abcd',
- className: 'tile-layer-cyan'
- }).addTo(groundTrackMap);
+ gpsMapOverlays = MapUtils.addTacticalOverlays(groundTrackMap, {
+ hudPanels: {
+ modeName: 'GPS',
+ getContactCount: () => 0,
+ },
+ scaleBar: true,
+ });
- // Upgrade tiles in background via Settings (with timeout fallback)
- if (typeof Settings !== 'undefined') {
- try {
- await Promise.race([
- Settings.init(),
- new Promise((_, reject) => setTimeout(() => reject(new Error('Settings timeout')), 5000))
- ]);
- groundTrackMap.removeLayer(fallbackTiles);
- Settings.createTileLayer().addTo(groundTrackMap);
- Settings.registerMap(groundTrackMap);
- } catch (e) {
- console.warn('Ground track: Settings init failed/timed out, using fallback tiles:', e);
- }
- }
-
- // Add observer marker
- const lat = parseFloat(document.getElementById('obsLat').value) || 51.5;
- const lon = parseFloat(document.getElementById('obsLon').value) || -0.1;
- observerMarker = L.circleMarker([lat, lon], {
- radius: 8,
- fillColor: '#ff6600',
- color: '#fff',
- weight: 2,
- fillOpacity: 1
- }).addTo(groundTrackMap).bindPopup('Observer Location');
+ // Observer crosshair via MapUtils
+ const obsLat = parseFloat(document.getElementById('obsLat')?.value) || 51.5;
+ const obsLon = parseFloat(document.getElementById('obsLon')?.value) || -0.1;
+ observerMarker = MapUtils._buildReticle([obsLat, obsLon]);
+ observerMarker.addTo(groundTrackMap);
}
function updateGroundTrack(pass) {
@@ -16474,6 +16413,7 @@
+
diff --git a/tests/test_map_utils.py b/tests/test_map_utils.py
index f9899df..8a0660f 100644
--- a/tests/test_map_utils.py
+++ b/tests/test_map_utils.py
@@ -51,3 +51,14 @@ def test_satellite_dashboard_includes_map_utils(client):
html = resp.data.decode()
assert "map-utils.js" in html
assert "MapUtils.init" in html
+
+
+def test_index_includes_map_utils(client):
+ """Main SPA index.html loads map-utils.js and uses it for APRS and GPS maps."""
+ with client.session_transaction() as sess:
+ sess["logged_in"] = True
+ resp = client.get("/")
+ assert resp.status_code == 200
+ html = resp.data.decode()
+ assert "map-utils.js" in html
+ assert "MapUtils.init" in html