refactor: simplify map.ts code with helper functions

- Add isValidCoordinate() helper to reduce duplicate coordinate validation
- Add createDivIcon() helper to standardize marker icon creation
- Simplify map ready event handling with sendMapReadyEvents() helper
- Fix typo: bindTooltip -> bindTooltip
- Reduce code duplication throughout the file
This commit is contained in:
Graham McIntire 2025-08-05 13:51:33 -05:00
parent 537ddec4d6
commit 44bc802f9d
No known key found for this signature in database

View file

@ -136,10 +136,7 @@ let MapAPRSMap = {
typeof lat === "number" && typeof lat === "number" &&
typeof lng === "number" && typeof lng === "number" &&
typeof zoom === "number" && typeof zoom === "number" &&
lat >= -90 && self.isValidCoordinate(lat, lng) &&
lat <= 90 &&
lng >= -180 &&
lng <= 180 &&
zoom >= 1 && zoom >= 1 &&
zoom <= 20 zoom <= 20
) { ) {
@ -358,10 +355,10 @@ let MapAPRSMap = {
className = "marker-cluster-large"; className = "marker-cluster-large";
} }
return L.divIcon({ return createDivIcon("<div><span>" + count + "</span></div>", {
html: "<div><span>" + count + "</span></div>",
className: "marker-cluster " + className, className: "marker-cluster " + className,
iconSize: [40, 40], iconSize: [40, 40],
iconAnchor: [20, 20],
}); });
}, },
}); });
@ -412,59 +409,43 @@ let MapAPRSMap = {
self.map!.whenReady(() => { self.map!.whenReady(() => {
try { try {
self.lastZoom = self.map!.getZoom(); self.lastZoom = self.map!.getZoom();
self.sendMapReadyEvents();
// Ensure we have a valid pushEvent function before using it } catch (error) {
if (self.pushEvent && typeof self.pushEvent === "function") { console.error("Error in map ready callback:", error);
self.pushEvent("map_ready", {}); }
// Send initial bounds to trigger historical loading });
if (self.map && self.pushEvent && typeof self.pushEvent === "function") {
saveMapState(self.map, self.pushEvent.bind(self)); // Helper function to send map ready events with retry
// Also send bounds to server to trigger historical loading self.sendMapReadyEvents = (retryCount = 0) => {
self.sendBoundsToServer(); if (self.pushEvent && typeof self.pushEvent === "function" && !self.isDestroyed) {
} self.pushEvent("map_ready", {});
if (self.map) {
// Also send update_map_state to ensure URL updates and bounds processing saveMapState(self.map, self.pushEvent.bind(self));
// Increase delay to ensure LiveView is fully connected and ready self.sendBoundsToServer();
// Send map state update after a delay
setTimeout(() => { setTimeout(() => {
if (self.map && self.pushEvent && !self.isDestroyed) { if (self.map && self.pushEvent && !self.isDestroyed) {
saveMapState(self.map, self.pushEvent.bind(self)); saveMapState(self.map, self.pushEvent.bind(self));
} }
}, 500); }, 500);
} else {
console.warn("pushEvent not available in whenReady callback");
// Retry after a short delay
setTimeout(() => {
if (self.pushEvent && typeof self.pushEvent === "function" && !self.isDestroyed) {
self.pushEvent("map_ready", {});
if (self.map) {
saveMapState(self.map, self.pushEvent.bind(self));
// Also send bounds to server to trigger historical loading
console.log("Calling sendBoundsToServer after map_ready (retry)");
self.sendBoundsToServer();
}
// Also trigger map state update after a delay
setTimeout(() => {
if (self.map && self.pushEvent && !self.isDestroyed) {
saveMapState(self.map, self.pushEvent.bind(self));
}
}, 500);
}
}, 200);
} }
} else if (retryCount < 3) {
// Start periodic cleanup of old trail positions (every 5 minutes) // Retry up to 3 times
self.cleanupInterval = setInterval( console.warn(`pushEvent not available, retrying... (attempt ${retryCount + 1})`);
() => { setTimeout(() => self.sendMapReadyEvents(retryCount + 1), 200);
if (!self.isDestroyed && self.trailManager) {
self.trailManager.cleanupOldPositions();
}
},
5 * 60 * 1000,
);
} catch (error) {
console.error("Error in map ready callback:", error);
} }
}); };
// Start periodic cleanup of old trail positions (every 5 minutes)
self.cleanupInterval = setInterval(
() => {
if (!self.isDestroyed && self.trailManager) {
self.trailManager.cleanupOldPositions();
}
},
5 * 60 * 1000,
);
// Track map event handlers for cleanup // Track map event handlers for cleanup
self.mapEventHandlers = new Map(); self.mapEventHandlers = new Map();
@ -781,7 +762,7 @@ let MapAPRSMap = {
const zoom = parseInt(data.zoom?.toString() || "12"); const zoom = parseInt(data.zoom?.toString() || "12");
// Validate coordinates // Validate coordinates
if (isNaN(lat) || isNaN(lng) || lat < -90 || lat > 90 || lng < -180 || lng > 180) { if (!isValidCoordinate(lat, lng)) {
console.error("Invalid coordinates for zoom:", lat, lng); console.error("Invalid coordinates for zoom:", lat, lng);
return; return;
} }
@ -1378,7 +1359,7 @@ let MapAPRSMap = {
const lng = parseFloat(data.lng.toString()); const lng = parseFloat(data.lng.toString());
// Validate coordinates // Validate coordinates
if (isNaN(lat) || isNaN(lng) || lat < -90 || lat > 90 || lng < -180 || lng > 180) { if (!isValidCoordinate(lat, lng)) {
console.warn("Invalid coordinates:", lat, lng); console.warn("Invalid coordinates:", lat, lng);
return; return;
} }
@ -1611,7 +1592,7 @@ let MapAPRSMap = {
if (data.lat && data.lng) { if (data.lat && data.lng) {
const lat = parseFloat(data.lat.toString()); const lat = parseFloat(data.lat.toString());
const lng = parseFloat(data.lng.toString()); const lng = parseFloat(data.lng.toString());
if (!isNaN(lat) && !isNaN(lng)) { if (self.isValidCoordinate(lat, lng)) {
const currentPos = existingMarker.getLatLng(); const currentPos = existingMarker.getLatLng();
const positionChanged = const positionChanged =
Math.abs(currentPos.lat - lat) > 0.0001 || Math.abs(currentPos.lng - lng) > 0.0001; Math.abs(currentPos.lat - lat) > 0.0001 || Math.abs(currentPos.lng - lng) > 0.0001;
@ -1793,9 +1774,7 @@ let MapAPRSMap = {
} }
// Use server-generated symbol HTML if available // Use server-generated symbol HTML if available
if (data.symbol_html) { if (data.symbol_html) {
return L.divIcon({ return createDivIcon(data.symbol_html, {
html: data.symbol_html,
className: "",
iconSize: [120, 32], // Increased width to accommodate callsign label iconSize: [120, 32], // Increased width to accommodate callsign label
iconAnchor: [16, 16], iconAnchor: [16, 16],
}); });
@ -1816,8 +1795,7 @@ let MapAPRSMap = {
box-shadow: 0 0 2px rgba(0,0,0,0.3); box-shadow: 0 0 2px rgba(0,0,0,0.3);
" title="Historical position for ${data.callsign}"></div>`; " title="Historical position for ${data.callsign}"></div>`;
return L.divIcon({ return createDivIcon(iconHtml, {
html: iconHtml,
className: "historical-dot-marker", className: "historical-dot-marker",
iconSize: [12, 12], iconSize: [12, 12],
iconAnchor: [6, 6], iconAnchor: [6, 6],
@ -1826,12 +1804,7 @@ let MapAPRSMap = {
// Fallback: Use server-generated symbol HTML if available // Fallback: Use server-generated symbol HTML if available
if (data.symbol_html) { if (data.symbol_html) {
return L.divIcon({ return createDivIcon(data.symbol_html);
html: data.symbol_html,
className: "",
iconSize: [32, 32],
iconAnchor: [16, 16],
});
} }
// Final fallback: Simple dot // Final fallback: Simple dot
@ -1845,9 +1818,7 @@ let MapAPRSMap = {
box-shadow: 0 0 2px rgba(0,0,0,0.3); box-shadow: 0 0 2px rgba(0,0,0,0.3);
" title="APRS Station: ${data.callsign}"></div>`; " title="APRS Station: ${data.callsign}"></div>`;
return L.divIcon({ return createDivIcon(iconHtml, {
html: iconHtml,
className: "",
iconSize: [12, 12], iconSize: [12, 12],
iconAnchor: [6, 6], iconAnchor: [6, 6],
}); });
@ -2017,6 +1988,25 @@ let MapAPRSMap = {
}, },
}; };
// Helper to validate coordinates
function isValidCoordinate(lat: number, lng: number): boolean {
return !isNaN(lat) && !isNaN(lng) && lat >= -90 && lat <= 90 && lng >= -180 && lng <= 180;
}
// Helper to create divIcon with common defaults
function createDivIcon(html: string, options: Partial<{
className: string;
iconSize: [number, number];
iconAnchor: [number, number];
}> = {}) {
return L.divIcon({
html,
className: options.className || "",
iconSize: options.iconSize || [32, 32],
iconAnchor: options.iconAnchor || [16, 16],
});
}
// Helper to validate and fallback symbol code per aprs.fi logic // Helper to validate and fallback symbol code per aprs.fi logic
function getValidSymbolCode( function getValidSymbolCode(
symbolCode: string | undefined, symbolCode: string | undefined,