diff --git a/static/js/map.js b/static/js/map.js index 5fcbc98..4d716aa 100644 --- a/static/js/map.js +++ b/static/js/map.js @@ -3,7 +3,9 @@ let map; let markerLayer; let markerClusterGroup; +let heatmapLayer; let capturesData = []; +let heatmapEnabled = false; // Frequency color mapping const FREQUENCY_COLORS = { @@ -64,14 +66,33 @@ function setupMapControls() { // Frequency filter document.getElementById('frequency-filter').addEventListener('change', () => { - renderMarkers(); + if (heatmapEnabled) { + renderHeatmap(); + } else { + renderMarkers(); + } }); - // Heatmap toggle (placeholder) + // Heatmap toggle document.getElementById('heatmap-toggle').addEventListener('change', (e) => { - if (e.target.checked) { - alert('Heatmap view coming soon!'); - e.target.checked = false; + heatmapEnabled = e.target.checked; + if (heatmapEnabled) { + // Hide markers, show heatmap + map.removeLayer(markerClusterGroup); + map.removeLayer(markerLayer); + renderHeatmap(); + } else { + // Hide heatmap, show markers + if (heatmapLayer) { + map.removeLayer(heatmapLayer); + } + // Re-add appropriate marker layer based on cluster toggle + const clusterEnabled = document.getElementById('cluster-toggle').checked; + if (clusterEnabled) { + map.addLayer(markerClusterGroup); + } else { + map.addLayer(markerLayer); + } } }); } @@ -199,5 +220,57 @@ function updateStats() { document.getElementById('unique-devices').textContent = uniqueDevices; } +function renderHeatmap() { + // Remove existing heatmap layer if it exists + if (heatmapLayer) { + map.removeLayer(heatmapLayer); + } + + // Get frequency filter + const frequencyFilter = document.getElementById('frequency-filter').value; + + // Filter captures + let filtered = capturesData; + + if (frequencyFilter) { + const targetFreq = parseInt(frequencyFilter) * 1e6; + filtered = capturesData.filter(c => { + const freq = c.frequency / 1e6; + return Math.abs(freq - parseInt(frequencyFilter)) < 50; + }); + } + + // Convert captures to heatmap data format + // Format: [[lat, lng, intensity], ...] + const heatmapData = filtered.map(capture => { + // Use match confidence as intensity (default to 0.5 if no match) + const intensity = capture.match_confidence || 0.5; + return [capture.latitude, capture.longitude, intensity]; + }); + + console.log(`Rendering heatmap with ${heatmapData.length} points`); + + // Create heatmap layer + heatmapLayer = L.heatLayer(heatmapData, { + radius: 25, // Radius of each point + blur: 15, // Amount of blur + maxZoom: 17, // Zoom level where max intensity is reached + max: 1.0, // Maximum point intensity + gradient: { // Custom color gradient + 0.0: 'blue', + 0.3: 'cyan', + 0.5: 'lime', + 0.7: 'yellow', + 1.0: 'red' + } + }); + + // Add to map + map.addLayer(heatmapLayer); + + // Update count + document.getElementById('total-captures').textContent = filtered.length; +} + // Export for external use window.reloadMapData = loadCaptures; diff --git a/templates/index.html b/templates/index.html index 8075e4e..675de81 100644 --- a/templates/index.html +++ b/templates/index.html @@ -280,6 +280,7 @@ +