document.addEventListener('DOMContentLoaded', function() { // --- Global Variables --- let map; let currentMarker; let crimeTypeChartInstance = null; let crimeTrendChartInstance = null; // --- DOM Elements --- const liveMapTab = document.getElementById('liveMapTab'); const modelAnalyticsTab = document.getElementById('modelAnalyticsTab'); const liveMapContent = document.getElementById('liveMapContent'); const modelAnalyticsContent = document.getElementById('modelAnalyticsContent'); const analysisCardContainer = document.getElementById('analysis-card-container'); const mapElement = document.getElementById('map'); // --- Tab Switching Logic --- function showContent(element) { element.style.display = 'flex'; void element.offsetWidth; // Trigger reflow for transition element.classList.remove('hidden'); element.classList.add('active'); } function hideContent(element) { element.classList.add('hidden'); element.classList.remove('active'); setTimeout(() => { if (!element.classList.contains('active')) { element.style.display = 'none'; } }, 500); // Match CSS transition duration } liveMapTab.addEventListener('click', () => { if (!liveMapTab.classList.contains('active')) { liveMapTab.classList.add('active'); modelAnalyticsTab.classList.remove('active'); hideContent(modelAnalyticsContent); showContent(liveMapContent); if (map) { map.resize(); } } }); modelAnalyticsTab.addEventListener('click', () => { if (!modelAnalyticsTab.classList.contains('active')) { modelAnalyticsTab.classList.add('active'); liveMapTab.classList.remove('active'); hideContent(liveMapContent); showContent(modelAnalyticsContent); // Notify charts.js that its tab is active document.dispatchEvent(new Event('analyticsTabActivated')); } }); // --- Map Initialization --- function initializeMap() { if (!mapElement) return; mapboxgl.accessToken = 'pk.eyJ1IjoibmF1bWFua2hhbmtoYW4iLCJhIjoiY21qZ3RuejdyMDRuMzNmc2xvemZsY2ZueiJ9.om5voCTqgCN2xFZ6_CVDsg'; // Replace with your token if needed map = new mapboxgl.Map({ container: 'map', style: 'mapbox://styles/mapbox/dark-v11', center: [-87.6298, 41.8781], // Chicago zoom: 12, pitch: 55, bearing: -20, }); map.addControl(new mapboxgl.NavigationControl(), 'top-right'); map.on('load', () => { const layers = map.getStyle().layers; const labelLayerId = layers.find(layer => layer.type === 'symbol' && layer.layout['text-field'])?.id; map.addLayer({ 'id': '3d-buildings', 'source': 'composite', 'source-layer': 'building', 'filter': ['==', 'extrude', 'true'], 'type': 'fill-extrusion', 'minzoom': 15, 'paint': { 'fill-extrusion-color': '#aaa', 'fill-extrusion-height': ['interpolate', ['linear'], ['zoom'], 15, 0, 15.05, ['get', 'height']], 'fill-extrusion-base': ['interpolate', ['linear'], ['zoom'], 15, 0, 15.05, ['get', 'min_height']], 'fill-extrusion-opacity': 0.6 } }, labelLayerId); }); map.on('click', handleMapClick); } // --- Map and Prediction Logic --- function handleMapClick(e) { if (currentMarker) { currentMarker.remove(); } currentMarker = new mapboxgl.Marker({ color: '#FF0000' }) .setLngLat(e.lngLat) .addTo(map); map.flyTo({ center: e.lngLat, zoom: 15 }); fetchAndDisplayAnalysis(e.lngLat.lat, e.lngLat.lng); } async function fetchAndDisplayAnalysis(latitude, longitude) { if (!analysisCardContainer) return; analysisCardContainer.innerHTML = `
Latitude: ${latitude.toFixed(4)}, Longitude: ${longitude.toFixed(4)}
Could not fetch crime prediction data.
${error.message}
Location: ${latitude.toFixed(4)}, ${longitude.toFixed(4)}
Predicted Crime Intensity: ${prediction.toFixed(2)}
Risk Level: ${risk_level}
Confidence: ${confidence}
Explanation: ${explanation}
Total Crimes: ${historical_insights.total_crimes}