Autobahn Traffic Overlay

Displays German Autobahn traffic information in Waze Map Editor

// ==UserScript==
// @name         Autobahn Traffic Overlay
// @namespace    https://greasyfork.org/de/users/863740-horst-wittlich
// @version      2025.08.18
// @description  Displays German Autobahn traffic information in Waze Map Editor
// @author       Hiwi234
// @match        https://www.waze.com/editor*
// @match        https://www.waze.com/*/editor*
// @match        https://beta.waze.com/editor*
// @match        https://beta.waze.com/*/editor*
// @grant        GM_xmlhttpRequest
// @connect      verkehr.autobahn.de
// @require      https://greasyfork.org/scripts/24851-wazewrap/code/WazeWrap.js
// @license MIT
// Calculate distance between two points in kilometers
    function calculateDistance(lat1, lon1, lat2, lon2) {
        const R = 6371; // Earth's radius in kilometers
        const dLat = (lat2 - lat1) * Math.PI / 180;
        const dLon = (lon2 - lon1) * Math.PI / 180;
        const a = Math.sin(dLat/2) * Math.sin(dLat/2) +
                  Math.cos(lat1 * Math.PI / 180) * Math.cos(lat2 * Math.PI / 180) *
                  Math.sin(dLon/2) * Math.sin(dLon/2);
        const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
        return R * c;
    }
// ==/UserScript==

(function() {
    'use strict';

    const SCRIPT_ID = 'vz-deutschland-overlay';
    const SCRIPT_NAME = 'VZ Deutschland Traffic';
    const API_BASE = 'https://verkehr.autobahn.de/o/autobahn';
    
    // List of all German Autobahns
    const AUTOBAHNS = [
        'A1', 'A2', 'A3', 'A4', 'A5', 'A6', 'A7', 'A8', 'A9', 'A10',
        'A11', 'A12', 'A13', 'A14', 'A15', 'A17', 'A19', 'A20', 'A21', 'A23',
        'A24', 'A25', 'A26', 'A27', 'A28', 'A29', 'A30', 'A31', 'A33', 'A36',
        'A37', 'A38', 'A39', 'A40', 'A42', 'A43', 'A44', 'A45', 'A46', 'A48',
        'A49', 'A52', 'A57', 'A59', 'A60', 'A61', 'A62', 'A63', 'A64', 'A65',
        'A66', 'A67', 'A70', 'A71', 'A72', 'A73', 'A81', 'A92', 'A93', 'A94',
        'A95', 'A96', 'A98', 'A99', 'A100', 'A103', 'A111', 'A113', 'A114', 'A115'
    ];
    
    let isOverlayVisible = false;
    let overlayLayer = null;
    let vectorLayer = null; // For line segments
    let tabPane = null;
    let trafficMarkers = [];
    let trafficFeatures = []; // For line features
    let updateInterval = null;
    let currentRequests = [];
    let lastUpdateTime = null;
    let currentEventsData = []; // Store current events data for re-sorting
    let mapMoveTimeout = null; // For debouncing map movement updates
    let lastMapCenter = null; // Track last map center
    let lastMapZoom = null; // Track last map zoom
    let currentTooltip = null; // Track current hover tooltip

    // Initialize the userscript when WME is ready
    function initializeScript() {
        console.log(`${SCRIPT_NAME}: Initializing...`);
        
        try {
            // Register sidebar tab
            const { tabLabel, tabPane: pane } = W.userscripts.registerSidebarTab(SCRIPT_ID);
            tabPane = pane;
            
            // Set up tab label
            tabLabel.innerHTML = '<i class="fa fa-road" style="font-size: 16px;"></i>';
            tabLabel.title = SCRIPT_NAME;
            tabLabel.style.cursor = 'pointer';
            
            // Wait for tab pane to be connected to DOM
            W.userscripts.waitForElementConnected(tabPane).then(() => {
                setupTabContent();
                createOverlayLayer();
                
                // Auto-enable overlay after setup
                setTimeout(() => {
                    const overlayToggle = document.getElementById('vzOverlayToggle');
                    if (overlayToggle && overlayToggle.checked) {
                        showOverlay();
                    }
                }, 500);
            });
            
            console.log(`${SCRIPT_NAME}: Successfully initialized`);
            
        } catch (error) {
            console.error(`${SCRIPT_NAME}: Failed to initialize:`, error);
        }
    }

    // Create OpenLayers overlay layer
    function createOverlayLayer() {
        if (!W.map) return;

        // Create layer for point markers
        overlayLayer = new OpenLayers.Layer.Markers("German Autobahn Traffic", {
            displayInLayerSwitcher: false,
            uniqueName: "__autobahnTraffic"
        });

        // Create vector layer for line segments (affected road sections) with WME compatible styling
        vectorLayer = new OpenLayers.Layer.Vector("German Autobahn Sections", {
            displayInLayerSwitcher: false,
            uniqueName: "__autobahnSections",
            renderers: ["SVG", "VML", "Canvas"], // Ensure compatibility
            styleMap: new OpenLayers.StyleMap({
                "default": new OpenLayers.Style({
                    strokeColor: "#ff4444",
                    strokeWidth: 4,
                    strokeOpacity: 0.8,
                    strokeDasharray: "10,5",
                    fillOpacity: 0
                })
            })
        });

        overlayLayer.setZIndex(2000);
        vectorLayer.setZIndex(1999); // Behind markers but above map
    }

    // Setup the content of the sidebar tab
    function setupTabContent() {
        tabPane.innerHTML = `
            <div style="padding: 10px;">
                <h3 style="margin-top: 0;">German Autobahn Traffic</h3>
                <div style="margin-bottom: 10px;">
                    <label style="display: block; margin-bottom: 5px;">
                        <input type="checkbox" id="vzOverlayToggle" checked style="margin-right: 5px;">
                        Show Traffic Overlay
                    </label>
                    <label style="display: block; margin-bottom: 5px; margin-left: 20px;">
                        <input type="checkbox" id="showAffectedSections" checked style="margin-right: 5px;">
                        Show affected road sections as lines
                    </label>
                </div>
                
                <div style="margin-bottom: 10px;">
                    <h4>Data Types:</h4>
                    <label style="display: block; margin-bottom: 3px;">
                        <input type="checkbox" class="vz-source" data-source="roadworks" checked style="margin-right: 5px;">
                        🚧 Construction Sites (Baustellen)
                    </label>
                    <label style="display: block; margin-bottom: 3px;">
                        <input type="checkbox" class="vz-source" data-source="warning" checked style="margin-right: 5px;">
                        ⚠️ Traffic Warnings (Verkehrsmeldungen)
                    </label>
                    <label style="display: block; margin-bottom: 3px;">
                        <input type="checkbox" class="vz-source" data-source="closure" checked style="margin-right: 5px;">
                        🚫 Road Closures (Sperrungen)
                    </label>
                </div>
                
                <div style="margin-bottom: 10px;">
                    <label style="display: block; margin-bottom: 5px;">
                        <input type="checkbox" id="autoUpdateEnabled" checked style="margin-right: 5px;">
                        Enable automatic updates
                    </label>
                    <div id="updateIntervalContainer" style="margin-left: 20px;">
                        <label style="display: block; margin-bottom: 5px;">
                            Update Interval: <span id="intervalValue">120</span>s
                        </label>
                        <input type="range" id="intervalSlider" min="30" max="600" step="30" value="120" style="width: 100%;">
                        <div style="font-size: 11px; color: #666; margin-top: 3px;">
                            Range: 30s - 10min (Map movement updates: Smart filtering)
                        </div>
                    </div>
                </div>
                
                <div style="margin-bottom: 10px;">
                    <button id="refreshOverlay" style="padding: 5px 10px; margin-right: 5px;">
                        🔄 Refresh Data
                    </button>
                    <button id="clearCache" style="padding: 5px 10px;">
                        🗑️ Clear Cache
                    </button>
                </div>
                
                <div id="statusDisplay" style="margin-top: 10px; padding: 8px; border: 1px solid #ccc; border-radius: 3px; background: #f9f9f9; font-size: 12px;">
                    Status: Ready to load traffic data
                </div>
                
                <div id="statisticsDisplay" style="margin-top: 10px; padding: 8px; border: 1px solid #ddd; border-radius: 3px; background: #f0f8ff; font-size: 11px;">
                    <strong>Statistics:</strong><br>
                    Last Update: Never<br>
                    Total Events: 0<br>
                    Active Requests: 0
                </div>
                
                <!-- Traffic Events List -->
                <div id="eventsListContainer" style="margin-top: 15px; border-top: 2px solid #ddd; padding-top: 10px;">
                    <h4 style="margin: 0 0 10px 0; display: flex; justify-content: space-between; align-items: center;">
                        Traffic Events
                        <span id="eventsCount" style="font-size: 12px; background: #007cba; color: white; padding: 2px 6px; border-radius: 3px;">
                            0
                        </span>
                    </h4>
                    
                    <!-- Sorting Controls -->
                    <div style="margin-bottom: 10px; padding: 5px; background: #f0f8ff; border-radius: 3px; border: 1px solid #ddd;">
                        <label style="font-size: 11px; margin-right: 10px;">
                            <strong>Sortierung:</strong>
                        </label>
                        <label style="font-size: 11px; margin-right: 15px;">
                            <input type="radio" name="sortBy" value="distance" checked style="margin-right: 3px;">
                            Entfernung
                        </label>
                        <label style="font-size: 11px; margin-right: 15px;">
                            <input type="radio" name="sortBy" value="date" style="margin-right: 3px;">
                            Datum (neu→alt)
                        </label>
                        <label style="font-size: 11px; margin-right: 15px;">
                            <input type="radio" name="sortBy" value="dateOld" style="margin-right: 3px;">
                            Datum (alt→neu)
                        </label>
                        <label style="font-size: 11px;">
                            <input type="radio" name="sortBy" value="future" style="margin-right: 3px;">
                            Zukünftige Ereignisse
                        </label>
                    </div>
                    
                    <div id="eventsList" style="max-height: 300px; overflow-y: auto; border: 1px solid #ccc; border-radius: 3px; background: #fafafa;">
                        <div style="padding: 10px; text-align: center; color: #666; font-size: 12px;">
                            No events loaded yet
                        </div>
                    </div>
                </div>
                
                <div style="margin-top: 10px; font-size: 11px; color: #666;">
                    <p><strong>Real Data Source:</strong> verkehr.autobahn.de</p>
                    <p>Shows live traffic information from German Federal Highways. Data updates automatically based on your current map view.</p>
                    <p><strong>Tooltip:</strong> Hover over markers to see details. Click on markers to show persistent popup.</p>
                </div>
            </div>
        `;
        
        setupEventListeners();
    }

    // Setup event listeners for the controls
    function setupEventListeners() {
        const overlayToggle = document.getElementById('vzOverlayToggle');
        const intervalSlider = document.getElementById('intervalSlider');
        const intervalValue = document.getElementById('intervalValue');
        const refreshButton = document.getElementById('refreshOverlay');
        const clearCacheButton = document.getElementById('clearCache');
        const sourceCheckboxes = document.querySelectorAll('.vz-source');
        const autoUpdateCheckbox = document.getElementById('autoUpdateEnabled');
        const intervalContainer = document.getElementById('updateIntervalContainer');

        overlayToggle.addEventListener('change', function() {
            if (this.checked) {
                showOverlay();
            } else {
                hideOverlay();
            }
        });

        intervalSlider.addEventListener('input', function() {
            intervalValue.textContent = this.value;
            if (updateInterval && autoUpdateCheckbox.checked) {
                clearInterval(updateInterval);
                startAutoUpdate(parseInt(this.value) * 1000);
            }
        });

        // Auto-update toggle
        autoUpdateCheckbox.addEventListener('change', function() {
            if (this.checked) {
                // Enable auto-update
                intervalContainer.style.opacity = '1';
                intervalContainer.style.pointerEvents = 'auto';
                if (isOverlayVisible) {
                    const interval = parseInt(intervalSlider.value) * 1000;
                    startAutoUpdate(interval);
                }
                updateStatus('Automatic updates enabled');
            } else {
                // Disable auto-update
                intervalContainer.style.opacity = '0.5';
                intervalContainer.style.pointerEvents = 'none';
                if (updateInterval) {
                    clearInterval(updateInterval);
                    updateInterval = null;
                }
                updateStatus('Automatic updates disabled - manual refresh only');
            }
        });

        refreshButton.addEventListener('click', function() {
            if (isOverlayVisible) {
                updateTrafficData();
            }
        });

        clearCacheButton.addEventListener('click', function() {
            clearMarkers();
            clearFeatures();
            updateEventsList([]);
            updateStatus('Cache cleared');
        });

        sourceCheckboxes.forEach(checkbox => {
            checkbox.addEventListener('change', function() {
                if (isOverlayVisible) {
                    updateTrafficData();
                }
            });
        });

        // Add event listeners for sorting radio buttons and section display
        document.addEventListener('change', function(e) {
            if (e.target.name === 'sortBy') {
                // Re-sort and update the events list
                const currentEvents = getCurrentEventsData();
                if (currentEvents && currentEvents.length > 0) {
                    updateEventsList(currentEvents);
                }
            } else if (e.target.id === 'showAffectedSections') {
                // Toggle road sections display
                if (isOverlayVisible) {
                    const currentEvents = getCurrentEventsData();
                    if (currentEvents && currentEvents.length > 0) {
                        clearFeatures(); // Clear existing features
                        const bounds = W.map.getExtent();
                        const displayData = currentEvents.filter(item => isItemInBounds(item, bounds));
                        displayData.forEach(item => {
                            addTrafficMarker(item);
                            if (e.target.checked) {
                                addRoadSection(item);
                            }
                        });
                    }
                }
            }
        });

        // Listen for map movement events with debouncing
        if (W.map && W.map.events) {
            W.map.events.register('moveend', null, function() {
                if (isOverlayVisible) {
                    handleMapMovement();
                }
            });

            W.map.events.register('zoomend', null, function() {
                if (isOverlayVisible) {
                    handleMapMovement();
                }
            });
        }
    }

    // Show the traffic overlay
    function showOverlay() {
        if (!overlayLayer || !vectorLayer || !W.map) return;

        W.map.addLayer(vectorLayer); // Add vector layer first (behind markers)
        W.map.addLayer(overlayLayer);

        // Add vector layer interaction controls
        setupVectorLayerEvents();
        
        isOverlayVisible = true;
        updateTrafficData();
        
        // Only start auto-update if enabled
        const autoUpdateEnabled = document.getElementById('autoUpdateEnabled')?.checked;
        if (autoUpdateEnabled) {
            const interval = parseInt(document.getElementById('intervalSlider').value) * 1000;
            startAutoUpdate(interval);
        }
        
        updateStatus('Overlay enabled - Loading traffic data...');
    }

    // Setup vector layer events for road sections
    function setupVectorLayerEvents() {
        if (!vectorLayer) return;

        console.log('Setting up vector layer events...');

        // Use WME-compatible event handling approach
        const selectControl = new OpenLayers.Control.SelectFeature(vectorLayer, {
            onSelect: function(feature) {
                console.log('Feature selected:', feature);
                if (feature.eventData) {
                    const bounds = feature.geometry.getBounds();
                    const centerPoint = bounds.getCenterLonLat();
                    
                    // Create a mouse event for tooltip positioning
                    const mockEvent = {
                        clientX: window.innerWidth / 2,
                        clientY: window.innerHeight / 2
                    };
                    
                    const tooltipContent = createTooltipContent(feature.eventData);
                    showTooltip(mockEvent, tooltipContent);
                }
            },
            onUnselect: function(feature) {
                // Optional: handle unselect
            }
        });

        // Add and activate the control
        W.map.addControl(selectControl);
        selectControl.activate();

        // Store reference for cleanup
        vectorLayer.selectControl = selectControl;
        
        console.log('Vector layer events set up successfully');
    }

    // Hide the traffic overlay
    function hideOverlay() {
        if (overlayLayer && W.map) {
            W.map.removeLayer(overlayLayer);
        }
        if (vectorLayer && W.map) {
            // Remove select control if exists
            if (vectorLayer.selectControl) {
                W.map.removeControl(vectorLayer.selectControl);
                vectorLayer.selectControl = null;
            }
            W.map.removeLayer(vectorLayer);
        }
        isOverlayVisible = false;
        
        if (updateInterval) {
            clearInterval(updateInterval);
            updateInterval = null;
        }
        
        // Cancel pending requests and map movement timeouts
        currentRequests.forEach(request => {
            if (request.abort) request.abort();
        });
        currentRequests = [];
        
        if (mapMoveTimeout) {
            clearTimeout(mapMoveTimeout);
            mapMoveTimeout = null;
        }
        
        // Hide any tooltip
        hideTooltip();
        
        updateStatus('Overlay disabled');
        updateStatistics();
    }

    // Start automatic updates (only if enabled)
    function startAutoUpdate(interval) {
        if (updateInterval) {
            clearInterval(updateInterval);
        }
        
        const autoUpdateEnabled = document.getElementById('autoUpdateEnabled')?.checked;
        if (!autoUpdateEnabled) {
            updateStatus('Automatic updates disabled');
            return;
        }
        
        updateInterval = setInterval(() => {
            if (isOverlayVisible && autoUpdateEnabled) {
                updateTrafficData();
            }
        }, interval);
        
        updateStatus(`Automatic updates enabled - every ${interval/1000} seconds`);
    }

    // Main function to update traffic data
    function updateTrafficData() {
        if (!overlayLayer || !W.map) return;

        const zoom = W.map.getZoom();
        if (zoom < 8) {
            updateStatus('Zoom in to see traffic data (minimum zoom level 8)');
            return;
        }

        updateStatus('Fetching traffic data from autobahn.de...');
        
        // Cancel any pending requests
        currentRequests.forEach(request => {
            if (request.abort) request.abort();
        });
        currentRequests = [];

        // Clear existing markers and features
        clearMarkers();
        clearFeatures();

        const selectedSources = getSelectedSources();
        if (selectedSources.length === 0) {
            updateStatus('No data types selected');
            return;
        }

        // Get relevant autobahns for current view
        const relevantAutobahns = getRelevantAutobahns();
        
        updateStatus(`Loading data for ${relevantAutobahns.length} autobahns...`);
        
        let completedRequests = 0;
        let totalRequests = relevantAutobahns.length * selectedSources.length;
        let allData = [];

        // Fetch data for each autobahn and data type
        relevantAutobahns.forEach(autobahn => {
            selectedSources.forEach(dataType => {
                fetchAutobahnData(autobahn, dataType)
                    .then(data => {
                        if (data && data.length > 0) {
                            allData = allData.concat(data.map(item => ({...item, autobahn, dataType})));
                        }
                        completedRequests++;
                        
                        if (completedRequests === totalRequests) {
                            // All requests completed
                            processTrafficData(allData);
                            lastUpdateTime = new Date();
                            updateStatus(`Data loaded successfully - ${allData.length} events found`);
                            updateStatistics();
                        } else {
                            updateStatus(`Loading... ${completedRequests}/${totalRequests} requests completed`);
                        }
                    })
                    .catch(error => {
                        console.error(`Error fetching ${dataType} for ${autobahn}:`, error);
                        completedRequests++;
                        
                        if (completedRequests === totalRequests) {
                            processTrafficData(allData);
                            lastUpdateTime = new Date();
                            updateStatus(`Data loaded with errors - ${allData.length} events found`);
                            updateStatistics();
                        }
                    });
            });
        });

        updateStatistics();
    }

    // Fetch data from autobahn API
    function fetchAutobahnData(autobahn, dataType) {
        return new Promise((resolve, reject) => {
            const url = `${API_BASE}/${autobahn}/services/${dataType}`;
            
            const request = GM_xmlhttpRequest({
                method: 'GET',
                url: url,
                headers: {
                    'Accept': 'application/json',
                    'User-Agent': 'WME VZ Deutschland Overlay'
                },
                timeout: 15000,
                onload: function(response) {
                    try {
                        if (response.status === 200) {
                            const data = JSON.parse(response.responseText);
                            resolve(data[dataType] || []);
                        } else {
                            console.warn(`HTTP ${response.status} for ${autobahn}/${dataType}`);
                            resolve([]);
                        }
                    } catch (error) {
                        console.error(`Parse error for ${autobahn}/${dataType}:`, error);
                        resolve([]);
                    }
                    
                    // Remove from active requests
                    const index = currentRequests.indexOf(request);
                    if (index > -1) currentRequests.splice(index, 1);
                },
                onerror: function(error) {
                    console.error(`Request error for ${autobahn}/${dataType}:`, error);
                    reject(error);
                    
                    // Remove from active requests
                    const index = currentRequests.indexOf(request);
                    if (index > -1) currentRequests.splice(index, 1);
                },
                ontimeout: function() {
                    console.warn(`Timeout for ${autobahn}/${dataType}`);
                    resolve([]);
                    
                    // Remove from active requests
                    const index = currentRequests.indexOf(request);
                    if (index > -1) currentRequests.splice(index, 1);
                }
            });
            
            currentRequests.push(request);
        });
    }

    // Get autobahns relevant for current map view
    function getRelevantAutobahns() {
        // Get current map bounds in WGS84
        const bounds = W.map.getExtent();
        const center = bounds.getCenterLonLat().transform(
            new OpenLayers.Projection("EPSG:3857"), 
            new OpenLayers.Projection("EPSG:4326")
        );
        
        console.log('Current center coordinates:', center.lat, center.lon);
        
        // More comprehensive autobahn selection based on geographic regions
        let relevantAutobahns = [];
        
        // Check if we're in Germany at all (rough bounds)
        if (center.lat < 47.3 || center.lat > 55.1 || center.lon < 5.9 || center.lon > 15.0) {
            // Outside Germany - use major autobahns
            relevantAutobahns = ['A1', 'A2', 'A3', 'A4', 'A5', 'A6', 'A7', 'A8', 'A9'];
        } else {
            // Within Germany - regional selection
            if (center.lat > 53.5) {
                // Schleswig-Holstein, Hamburg
                relevantAutobahns = ['A1', 'A7', 'A20', 'A21', 'A23', 'A24', 'A25'];
            } else if (center.lat > 52.5) {
                // Lower Saxony, Bremen, Mecklenburg-Vorpommern
                relevantAutobahns = ['A1', 'A2', 'A7', 'A14', 'A19', 'A20', 'A24', 'A27', 'A28', 'A29', 'A30', 'A31'];
            } else if (center.lat > 51.5) {
                // North Rhine-Westphalia, Saxony-Anhalt, Brandenburg
                relevantAutobahns = ['A1', 'A2', 'A3', 'A4', 'A30', 'A31', 'A33', 'A37', 'A38', 'A39', 'A40', 'A42', 'A43', 'A44', 'A45', 'A46'];
            } else if (center.lat > 50.0) {
                // Hesse, Thuringia, Saxony, parts of Rhineland-Palatinate
                relevantAutobahns = ['A3', 'A4', 'A5', 'A6', 'A7', 'A9', 'A38', 'A40', 'A44', 'A45', 'A48', 'A60', 'A61', 'A66', 'A67', 'A71', 'A72', 'A73'];
            } else if (center.lat > 48.5) {
                // Baden-Württemberg, Bavaria (north)
                relevantAutobahns = ['A3', 'A5', 'A6', 'A7', 'A8', 'A9', 'A60', 'A61', 'A63', 'A65', 'A67', 'A70', 'A73', 'A81'];
            } else {
                // Bavaria (south), parts of Baden-Württemberg
                relevantAutobahns = ['A3', 'A5', 'A6', 'A8', 'A9', 'A70', 'A92', 'A93', 'A94', 'A95', 'A96', 'A99'];
            }
        }
        
        console.log('Selected autobahns for region:', relevantAutobahns);
        return relevantAutobahns;
    }

    // Process and display traffic data
    function processTrafficData(allData) {
        if (!allData || allData.length === 0) {
            updateStatus('No traffic events found for current area');
            updateEventsList([]);
            return;
        }

        console.log('Processing', allData.length, 'total events');
        
        // Log some sample data to understand structure
        if (allData.length > 0) {
            console.log('Sample event:', allData[0]);
        }

        // Get current map bounds
        const bounds = W.map.getExtent();
        console.log('Current map bounds (Web Mercator):', bounds);
        
        // Transform bounds to WGS84 for logging
        const boundsWGS84 = bounds.clone().transform(new OpenLayers.Projection("EPSG:3857"), new OpenLayers.Projection("EPSG:4326"));
        console.log('Current map bounds (WGS84):', boundsWGS84);

        // Calculate center for distance calculations
        const center = bounds.getCenterLonLat().transform(new OpenLayers.Projection("EPSG:3857"), new OpenLayers.Projection("EPSG:4326"));

        // Add distance and process timestamps for all events
        const allDataWithDistance = allData
            .filter(item => item.coordinate && item.coordinate.lat && item.coordinate.long)
            .map(item => {
                const distance = calculateDistance(center.lat, center.lon, item.coordinate.lat, item.coordinate.long);
                
                // Process timestamp for sorting
                let timestamp = null;
                let formattedDate = 'Unbekannt';
                
                if (item.startTimestamp) {
                    try {
                        timestamp = new Date(item.startTimestamp);
                        formattedDate = timestamp.toLocaleDateString('de-DE') + ' ' + timestamp.toLocaleTimeString('de-DE', {hour: '2-digit', minute: '2-digit'});
                    } catch (e) {
                        console.warn('Invalid timestamp for event:', item.identifier, item.startTimestamp);
                    }
                }
                
                return { 
                    ...item, 
                    distance,
                    timestamp,
                    formattedDate
                };
            });

        // Store current data for re-sorting
        currentEventsData = allDataWithDistance;

        // Filter data based on current map bounds
        const filteredData = allDataWithDistance.filter(item => {
            return isItemInBounds(item, bounds);
        });

        console.log('Filtered to', filteredData.length, 'events in current view');

        // If no events in current bounds, show nearby events
        let displayData = filteredData;
        if (filteredData.length === 0 && allDataWithDistance.length > 0) {
            displayData = allDataWithDistance.slice(0, 10); // Show 10 closest events
            console.log('Showing 10 nearest events instead');
            updateStatus(`No events in current view. Showing ${displayData.length} nearest events (closest: ${displayData[0]?.distance?.toFixed(1)}km away)`);
        } else {
            updateStatus(`Displaying ${filteredData.length} of ${allData.length} traffic events in current view`);
        }

        // Add markers and road sections for display data
        const showSections = document.getElementById('showAffectedSections')?.checked || false;
        displayData.forEach(item => {
            addTrafficMarker(item);
            if (showSections) {
                addRoadSection(item);
            }
        });

        // Update events list with all data (sorted according to current selection)
        updateEventsList(allDataWithDistance);
    }

    // Get current events data for re-sorting
    function getCurrentEventsData() {
        return currentEventsData;
    }

    // Sort events based on selected criteria
    function sortEvents(events, sortBy) {
        const sorted = [...events];
        
        switch (sortBy) {
            case 'distance':
                return sorted.sort((a, b) => a.distance - b.distance);
            
            case 'date':
                // Newest first (newest dates have higher timestamp values)
                return sorted.sort((a, b) => {
                    if (!a.timestamp && !b.timestamp) return 0;
                    if (!a.timestamp) return 1; // Events without date go to the end
                    if (!b.timestamp) return -1;
                    return b.timestamp - a.timestamp; // Newest first
                });
            
            case 'dateOld':
                // Oldest first (oldest dates have lower timestamp values)
                return sorted.sort((a, b) => {
                    if (!a.timestamp && !b.timestamp) return 0;
                    if (!a.timestamp) return 1; // Events without date go to the end
                    if (!b.timestamp) return -1;
                    return a.timestamp - b.timestamp; // Oldest first
                });
            
            case 'future':
                // Future events first, then by distance
                return sorted.sort((a, b) => {
                    // First sort by future status
                    if (a.future && !b.future) return -1;
                    if (!a.future && b.future) return 1;
                    
                    // If both have same future status, sort by distance
                    return a.distance - b.distance;
                });
            
            default:
                return sorted;
        }
    }

    // Update the events list in the sidebar
    function updateEventsList(events) {
        const eventsList = document.getElementById('eventsList');
        const eventsCount = document.getElementById('eventsCount');
        
        if (!eventsList) return;

        if (!events || events.length === 0) {
            eventsList.innerHTML = `
                <div style="padding: 10px; text-align: center; color: #666; font-size: 12px;">
                    No events loaded yet
                </div>
            `;
            if (eventsCount) eventsCount.textContent = '0';
            return;
        }

        // Get selected sorting method
        const sortBy = document.querySelector('input[name="sortBy"]:checked')?.value || 'distance';
        const sortedEvents = sortEvents(events, sortBy);

        if (eventsCount) eventsCount.textContent = events.length;

        const typeNames = {
            roadworks: '🚧 Baustelle',
            warning: '⚠️ Verkehrsmeldung',
            closure: '🚫 Sperrung'
        };

        const typeColors = {
            roadworks: '#ff9800',
            warning: '#f44336',
            closure: '#9c27b0'
        };

        let listHTML = '';
        sortedEvents.slice(0, 1000).forEach((event, index) => { // Increased limit to 1000 events
            const typeName = typeNames[event.dataType] || event.dataType;
            const typeColor = typeColors[event.dataType] || '#2196f3';
            const title = event.title || 'Keine Beschreibung';
            const autobahn = event.autobahn || 'Unbekannt';
            const distance = event.distance ? `${event.distance.toFixed(1)} km` : 'Unbekannt';
            const dateInfo = event.formattedDate || 'Unbekannt';
            
            // Add visual indicator for future events
            const futureIndicator = event.future ? ' style="background: #d1ecf1; border-left-color: #17a2b8 !important;"' : '';
            const futureIcon = event.future ? ' 🔮' : '';
            
            // Create secondary info based on sort order
            let secondaryInfo = '';
            if (sortBy === 'distance') {
                secondaryInfo = `<span>Entfernung: ${distance}</span><span>Datum: ${dateInfo}</span>`;
            } else if (sortBy === 'future') {
                const futureStatus = event.future ? '🔮 Zukünftig' : '📅 Aktuell';
                secondaryInfo = `<span>${futureStatus}</span><span>Entfernung: ${distance}</span>`;
            } else {
                secondaryInfo = `<span>Datum: ${dateInfo}</span><span>Entfernung: ${distance}</span>`;
            }
            
            listHTML += `
                <div class="event-item" data-event-id="${event.identifier}" style="
                    padding: 8px; 
                    border-bottom: 1px solid #ddd; 
                    cursor: pointer; 
                    background: white;
                    border-left: 4px solid ${typeColor};
                    ${futureIndicator.slice(7, -1)} // Remove 'style="' and '"'
                " onmouseover="this.style.background='#f0f8ff'" onmouseout="this.style.background='${event.future ? '#d1ecf1' : 'white'}'">
                    <div style="font-weight: bold; font-size: 12px; color: #333; margin-bottom: 3px;">
                        ${typeName}${futureIcon} - ${autobahn}
                    </div>
                    <div style="font-size: 11px; color: #666; margin-bottom: 3px;">
                        ${title}
                    </div>
                    <div style="font-size: 10px; color: #999; display: flex; justify-content: space-between;">
                        ${secondaryInfo}
                    </div>
                    <div style="font-size: 9px; color: #007cba; text-align: right; margin-top: 2px;">
                        Zur Position springen →
                    </div>
                </div>
            `;
        });

        if (events.length > 1000) {
            listHTML += `
                <div style="padding: 8px; text-align: center; color: #666; font-size: 11px; background: #f5f5f5;">
                    ... und ${events.length - 1000} weitere Events (nur die ersten 1000 werden angezeigt)
                </div>
            `;
        }

        eventsList.innerHTML = listHTML;

        // Add click event listeners to event items
        eventsList.querySelectorAll('.event-item').forEach(item => {
            item.addEventListener('click', function() {
                const eventId = this.dataset.eventId;
                jumpToEvent(eventId, sortedEvents);
            });
        });
    }

    // Jump to specific event on map (simplified - no popup)
    function jumpToEvent(eventId, events) {
        const event = events.find(e => e.identifier === eventId);
        if (!event || !event.coordinate) {
            console.error('Event not found or has no coordinates:', eventId);
            return;
        }

        try {
            // Transform coordinates to map projection
            const lonLat = new OpenLayers.LonLat(event.coordinate.long, event.coordinate.lat)
                .transform(new OpenLayers.Projection("EPSG:4326"), new OpenLayers.Projection("EPSG:3857"));

            // Center map on event
            W.map.setCenter(lonLat, Math.max(W.map.getZoom(), 12));

            updateStatus(`Jumped to ${event.dataType}: ${event.title || event.identifier}`);
        } catch (error) {
            console.error('Error jumping to event:', error);
            updateStatus('Error jumping to event');
        }
    }

    // Handle map movement with intelligent updates
    function handleMapMovement() {
        // Clear any pending update
        if (mapMoveTimeout) {
            clearTimeout(mapMoveTimeout);
        }

        try {
            // Get current map state
            const currentCenter = W.map.getCenter();
            const currentZoom = W.map.getZoom();

            // Check if we need to update based on movement distance and zoom
            const shouldUpdate = checkIfUpdateNeeded(currentCenter, currentZoom);
            
            if (shouldUpdate) {
                // Only auto-update if enabled
                const autoUpdateEnabled = document.getElementById('autoUpdateEnabled')?.checked;
                if (autoUpdateEnabled) {
                    // Debounce the update - wait 3 seconds after movement stops
                    mapMoveTimeout = setTimeout(() => {
                        updateStatus('Map moved significantly - updating traffic data...');
                        lastMapCenter = new OpenLayers.LonLat(currentCenter.lon, currentCenter.lat);
                        lastMapZoom = currentZoom;
                        updateTrafficData();
                    }, 3000);
                } else {
                    // Just update tracking without data fetch
                    lastMapCenter = new OpenLayers.LonLat(currentCenter.lon, currentCenter.lat);
                    lastMapZoom = currentZoom;
                    mapMoveTimeout = setTimeout(() => {
                        updateStatus('Map moved - automatic updates disabled (use refresh button)');
                        refilterExistingData();
                    }, 1000);
                }
            } else {
                // Just re-filter existing data for new viewport
                mapMoveTimeout = setTimeout(() => {
                    updateStatus('Re-filtering existing traffic data for new view...');
                    refilterExistingData();
                }, 1000);
            }
        } catch (error) {
            console.error('Error in handleMapMovement:', error);
            updateStatus('Error handling map movement');
        }
    }

    // Check if update is needed based on movement distance
    function checkIfUpdateNeeded(currentCenter, currentZoom) {
        try {
            // Always update on first load
            if (!lastMapCenter || !lastMapZoom) {
                return true;
            }

            // Always update if zoom changed significantly
            if (Math.abs(currentZoom - lastMapZoom) >= 2) {
                return true;
            }

            // Calculate movement distance in map units
            const distance = Math.sqrt(
                Math.pow(currentCenter.lon - lastMapCenter.lon, 2) + 
                Math.pow(currentCenter.lat - lastMapCenter.lat, 2)
            );

            // Update threshold based on zoom level (more movement needed at higher zooms)
            const updateThreshold = 100000 / Math.pow(2, currentZoom - 10); // Adjust threshold by zoom
            
            return distance > updateThreshold;
        } catch (error) {
            console.error('Error in checkIfUpdateNeeded:', error);
            return true; // Safe fallback: allow update
        }
    }

    // Re-filter existing data for new viewport without fetching new data
    function refilterExistingData() {
        if (!currentEventsData || currentEventsData.length === 0) {
            return;
        }

        try {
            // Clear current markers and features
            clearMarkers();
            clearFeatures();

            // Get current map bounds
            const bounds = W.map.getExtent();
            
            // Filter existing data for current bounds
            const filteredData = currentEventsData.filter(item => {
                return isItemInBounds(item, bounds);
            });

            // If no events in current bounds, show nearby events
            let displayData = filteredData;
            if (filteredData.length === 0 && currentEventsData.length > 0) {
                // Recalculate distances for current center
                const center = bounds.getCenterLonLat().transform(
                    new OpenLayers.Projection("EPSG:3857"), 
                    new OpenLayers.Projection("EPSG:4326")
                );
                
                const eventsWithNewDistances = currentEventsData.map(item => ({
                    ...item,
                    distance: calculateDistance(center.lat, center.lon, item.coordinate.lat, item.coordinate.long)
                })).sort((a, b) => a.distance - b.distance);
                
                displayData = eventsWithNewDistances.slice(0, 10);
                updateStatus(`No events in current view. Showing ${displayData.length} nearest events (using cached data)`);
            } else {
                updateStatus(`Re-filtered to ${filteredData.length} events in current view (using cached data)`);
            }

            // Add markers and road sections for display data
            const showSections = document.getElementById('showAffectedSections')?.checked || false;
            displayData.forEach(item => {
                addTrafficMarker(item);
                if (showSections) {
                    addRoadSection(item);
                }
            });

            updateStatistics();
        } catch (error) {
            console.error('Error in refilterExistingData:', error);
            updateStatus('Error re-filtering data');
        }
    }

    // Check if item is within current map bounds
    function isItemInBounds(item, bounds) {
        if (!item.coordinate || (!item.coordinate.lat && item.coordinate.lat !== 0) || (!item.coordinate.long && item.coordinate.long !== 0)) {
            console.log('Invalid coordinates for item:', item.identifier, item.coordinate);
            return false;
        }

        try {
            // Transform bounds to WGS84 for comparison
            const boundsWGS84 = bounds.clone().transform(new OpenLayers.Projection("EPSG:3857"), new OpenLayers.Projection("EPSG:4326"));
            
            // Check if item coordinates are within bounds
            const lat = parseFloat(item.coordinate.lat);
            const lng = parseFloat(item.coordinate.long);
            
            if (isNaN(lat) || isNaN(lng)) {
                console.log('Invalid coordinate values:', lat, lng, 'for item:', item.identifier);
                return false;
            }
            
            const isInBounds = lat >= boundsWGS84.bottom && lat <= boundsWGS84.top && 
                              lng >= boundsWGS84.left && lng <= boundsWGS84.right;
            
            if (!isInBounds) {
                console.log('Item outside bounds:', item.identifier, 'coords:', lat, lng, 'bounds:', boundsWGS84);
            }
            
            return isInBounds;
        } catch (error) {
            console.error('Error checking bounds for item:', item.identifier, error);
            return false;
        }
    }

    // Add a traffic marker to the map with FIXED event handling
    function addTrafficMarker(item) {
        if (!item.coordinate || (!item.coordinate.lat && item.coordinate.lat !== 0) || (!item.coordinate.long && item.coordinate.long !== 0)) {
            console.log('Skipping item with invalid coordinates:', item.identifier);
            return;
        }

        try {
            const lat = parseFloat(item.coordinate.lat);
            const lng = parseFloat(item.coordinate.long);
            
            if (isNaN(lat) || isNaN(lng)) {
                console.log('Skipping item with NaN coordinates:', item.identifier, lat, lng);
                return;
            }

            const lonLat = new OpenLayers.LonLat(lng, lat)
                .transform(new OpenLayers.Projection("EPSG:4326"), new OpenLayers.Projection("EPSG:3857"));

            // Create marker icon based on data type
            const size = new OpenLayers.Size(24, 24);
            const offset = new OpenLayers.Pixel(-(size.w/2), -size.h);
            const iconUrl = createMarkerIcon(item.dataType);
            const icon = new OpenLayers.Icon(iconUrl, size, offset);

            const marker = new OpenLayers.Marker(lonLat, icon);
            
            // FIXED: Simplified event handling - hover shows tooltip, click shows persistent popup
            marker.events.register('mouseover', marker, function(e) {
                const tooltipContent = createTooltipContent(item);
                const mouseEvent = e.originalEvent || e;
                showHoverTooltip(mouseEvent, tooltipContent);
            });
            
            marker.events.register('mouseout', marker, function(e) {
                hideHoverTooltip();
            });
            
            marker.events.register('click', marker, function(e) {
                const tooltipContent = createTooltipContent(item);
                const mouseEvent = e.originalEvent || e;
                showPersistentPopup(mouseEvent, tooltipContent);
                // Don't stop propagation - let click pass through to WME
            });

            // Store marker data
            marker.trafficData = item;

            overlayLayer.addMarker(marker);
            trafficMarkers.push({marker, data: item});
            
            console.log('Added marker for:', item.identifier, 'at', lat, lng);
        } catch (error) {
            console.error('Error adding marker for item:', item.identifier, error);
        }
    }

    // Parse event description for structured information with enhanced closure details
    function parseEventDescription(description, eventType) {
        const info = {
            begin: '',
            end: '',
            measure: '',
            restrictions: '',
            situation: '',
            location: '',
            additional: '',
            closureType: '',
            affectedVehicles: '',
            landmark: '',
            closedSection: '',
            alternativeRoute: '',
            closureReason: '',
            detour: '',
            sectionLength: 0
        };

        if (!Array.isArray(description)) {
            return info;
        }

        let additionalLines = [];
        let inRestrictions = false;

        description.forEach((line, index) => {
            const trimmedLine = line.trim();
            
            if (!trimmedLine) return; // Skip empty lines

            if (trimmedLine.startsWith('Beginn:')) {
                info.begin = trimmedLine;
            } else if (trimmedLine.startsWith('Ende:')) {
                info.end = trimmedLine;
            } else if (trimmedLine.startsWith('Art der Maßnahme:')) {
                info.measure = trimmedLine.replace('Art der Maßnahme:', '').trim();
            } else if (trimmedLine.startsWith('Einschränkungen:')) {
                info.restrictions = trimmedLine.replace('Einschränkungen:', '').trim().replace(/\\n/g, '<br>');
                inRestrictions = true;
            } else if (inRestrictions && !trimmedLine.includes(':') && !trimmedLine.includes('gesperrt')) {
                // Continue restrictions on next lines
                info.restrictions += '<br>' + trimmedLine.replace(/\\n/g, '<br>');
            } else if (trimmedLine.includes('zwischen ') || trimmedLine.includes('bei ') || trimmedLine.includes('von ') || trimmedLine.includes('bis ')) {
                // Location information - enhanced for closures
                info.location = trimmedLine;
                
                // Extract specific closure section details
                if (eventType === 'closure' || eventType === 'roadworks') {
                    // Try to extract closed section from location
                    const sectionMatch = trimmedLine.match(/(zwischen|von)\s+([^-]+)\s*-?\s*(bis|und)?\s*([^,]*)/i);
                    if (sectionMatch) {
                        const start = sectionMatch[2]?.trim();
                        const end = sectionMatch[4]?.trim();
                        if (start && end) {
                            info.closedSection = `${start} → ${end}`;
                        } else if (start) {
                            info.closedSection = start;
                        }
                    }
                }
                inRestrictions = false;
            } else if ((eventType === 'closure' || eventType === 'roadworks') && (
                trimmedLine.includes('gesperrt') || 
                trimmedLine.includes('Sperrung') ||
                trimmedLine.includes('Vollsperrung') ||
                trimmedLine.includes('blockiert') ||
                trimmedLine.includes('Baustelle')
            )) {
                // Enhanced closure type analysis
                info.closureType = trimmedLine;
                
                // Extract closure reason
                if (trimmedLine.includes('Unfall')) {
                    info.closureReason = 'Verkehrsunfall';
                } else if (trimmedLine.includes('Baustelle')) {
                    info.closureReason = 'Bauarbeiten';
                } else if (trimmedLine.includes('Bergung')) {
                    info.closureReason = 'Bergungsarbeiten';
                } else if (trimmedLine.includes('Defekt') || trimmedLine.includes('Panne')) {
                    info.closureReason = 'Fahrzeugpanne';
                } else if (trimmedLine.includes('Wetter') || trimmedLine.includes('Glätte') || trimmedLine.includes('Schnee')) {
                    info.closureReason = 'Witterungsbedingungen';
                } else if (trimmedLine.includes('Kontrolle') || trimmedLine.includes('Polizei')) {
                    info.closureReason = 'Polizeikontrolle';
                }
                
                // Extract affected vehicles with more detail
                if (trimmedLine.includes('LKW')) {
                    info.affectedVehicles = 'LKW';
                    if (trimmedLine.includes('über 3,5 t')) {
                        info.affectedVehicles += ' über 3,5t';
                    }
                } else if (trimmedLine.includes('PKW')) {
                    info.affectedVehicles = 'PKW';
                } else if (trimmedLine.includes('allen Fahrzeugen') || trimmedLine.includes('beide Richtungen')) {
                    info.affectedVehicles = 'Alle Fahrzeuge';
                } else if (trimmedLine.includes('Fahrbahn')) {
                    if (trimmedLine.includes('eine Fahrbahn')) {
                        info.affectedVehicles = 'Eine Fahrbahn';
                    } else if (trimmedLine.includes('beide Fahrbahnen')) {
                        info.affectedVehicles = 'Beide Fahrbahnen';
                    }
                }
                
                // Extract direction if specified
                if (trimmedLine.includes('Richtung ')) {
                    const directionMatch = trimmedLine.match(/Richtung\s+([^\s,]+)/i);
                    if (directionMatch) {
                        info.affectedVehicles += ` (Richtung ${directionMatch[1]})`;
                    }
                }
                
                inRestrictions = false;
            } else if (trimmedLine.includes('Umleitung') || trimmedLine.includes('Ausweichroute') || trimmedLine.includes('Alternative')) {
                // Extract detour information
                info.detour = trimmedLine;
                inRestrictions = false;
            } else if (eventType === 'warning' && (
                trimmedLine.includes('Stau') || 
                trimmedLine.includes('Verengung') || 
                trimmedLine.includes('Verkehrsführung') ||
                trimmedLine.includes('gesperrt') ||
                trimmedLine.includes('Gefahr')
            )) {
                // Traffic situation for warnings
                info.situation = trimmedLine.replace(/\\n/g, '<br>');
                inRestrictions = false;
            } else if (trimmedLine.includes('Maximale Durchfahrsbreite:') || trimmedLine.includes('Höhenbegrenzung:')) {
                // Add to restrictions
                if (info.restrictions) {
                    info.restrictions += '<br>' + trimmedLine;
                } else {
                    info.restrictions = trimmedLine;
                }
                inRestrictions = false;
            } else if ((eventType === 'closure' || eventType === 'roadworks') && (
                trimmedLine.includes('Brücke') || 
                trimmedLine.includes('Tunnel') ||
                trimmedLine.includes('Kreuz') ||
                trimmedLine.includes('Dreieck') ||
                trimmedLine.includes('Anschlussstelle') ||
                trimmedLine.includes('Auffahrt') ||
                trimmedLine.includes('Abfahrt')
            )) {
                // Enhanced landmark information for closures
                info.landmark = trimmedLine;
                inRestrictions = false;
            } else if (trimmedLine.includes('AS ') || trimmedLine.includes('AK ') || trimmedLine.includes('AD ')) {
                // Autobahn junction/interchange codes
                if (!info.landmark) {
                    info.landmark = trimmedLine;
                } else {
                    info.landmark += ' • ' + trimmedLine;
                }
                inRestrictions = false;
            } else if (index > 2 && !trimmedLine.includes(':')) {
                // Additional information (skip first few lines which are usually structured)
                additionalLines.push(trimmedLine.replace(/\\n/g, '<br>'));
                inRestrictions = false;
            }
        });

        if (additionalLines.length > 0) {
            info.additional = additionalLines.join('<br>');
        }

        return info;
    }

    // Calculate section length from extent coordinates
    function calculateSectionLength(item) {
        if (!item.extent) {
            return 0;
        }

        try {
            // Parse extent: "lon1,lat1,lon2,lat2"
            const extentParts = item.extent.split(',').map(parseFloat);
            if (extentParts.length !== 4) {
                return 0;
            }

            const [lon1, lat1, lon2, lat2] = extentParts;
            
            // Calculate distance between start and end points
            const distance = calculateDistance(lat1, lon1, lat2, lon2);
            return distance;
        } catch (error) {
            console.error('Error calculating section length:', error);
            return 0;
        }
    }

    // Format length for display
    function formatSectionLength(lengthKm) {
        if (lengthKm === 0) return '';
        
        if (lengthKm < 1) {
            return `${Math.round(lengthKm * 1000)} m`;
        } else if (lengthKm < 10) {
            return `${lengthKm.toFixed(1)} km`;
        } else {
            return `${Math.round(lengthKm)} km`;
        }
    }

    // Create visual length indicator
    function createLengthIndicator(lengthKm, eventType) {
        if (lengthKm === 0) return '';

        // Determine color based on event type and length
        let color = '#007cba';
        let severity = 'normal';
        
        if (eventType === 'closure') {
            color = '#dc3545';
            severity = lengthKm > 5 ? 'severe' : lengthKm > 2 ? 'moderate' : 'normal';
        } else if (eventType === 'roadworks') {
            color = '#ff9800';
            severity = lengthKm > 10 ? 'severe' : lengthKm > 5 ? 'moderate' : 'normal';
        } else if (eventType === 'warning') {
            color = '#ffc107';
            severity = lengthKm > 3 ? 'moderate' : 'normal';
        }

        // Create visual bar indicator
        const maxWidth = 120; // Max width in pixels
        const width = Math.min(maxWidth, Math.max(20, lengthKm * 8)); // Scale factor
        
        const severityText = {
            'severe': 'Lange Strecke',
            'moderate': 'Mittlere Strecke', 
            'normal': 'Kurze Strecke'
        };

        return `
            <div style="margin: 4px 0; padding: 4px; background: rgba(0,0,0,0.05); border-radius: 3px;">
                <div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 2px;">
                    <span style="font-size: 10px; font-weight: bold;">Streckenlänge:</span>
                    <span style="font-size: 11px; color: ${color}; font-weight: bold;">${formatSectionLength(lengthKm)}</span>
                </div>
                <div style="background: #e0e0e0; height: 8px; border-radius: 4px; overflow: hidden;">
                    <div style="
                        background: ${color}; 
                        height: 100%; 
                        width: ${width}px; 
                        border-radius: 4px;
                        transition: width 0.3s ease;
                        background-image: repeating-linear-gradient(
                            45deg,
                            transparent,
                            transparent 5px,
                            rgba(255,255,255,0.2) 5px,
                            rgba(255,255,255,0.2) 10px
                        );
                    "></div>
                </div>
                <div style="font-size: 9px; color: #666; margin-top: 2px;">${severityText[severity]}</div>
            </div>
        `;
    }

    // Create tooltip content with full details
    function createTooltipContent(item) {
        const typeNames = {
            roadworks: '🚧 Baustelle',
            warning: '⚠️ Verkehrsmeldung',
            closure: '🚫 Sperrung'
        };

        const displayTypeInfo = getDisplayTypeInfo(item.display_type);
        const typeName = displayTypeInfo.name || typeNames[item.dataType] || item.dataType;
        const typeIcon = displayTypeInfo.icon || (typeNames[item.dataType] ? typeNames[item.dataType].split(' ')[0] : '📍');

        const title = item.title || 'Keine Beschreibung';
        const subtitle = item.subtitle || '';
        const autobahn = item.autobahn || 'Unbekannt';
        const distance = item.distance ? `${item.distance.toFixed(1)} km` : '';

        // Extract detailed info from description with enhanced closure parsing
        let beginInfo = '';
        let endInfo = '';
        let measureInfo = '';
        let restrictionInfo = '';
        let additionalInfo = '';
        let closedSection = '';
        let closureReason = '';
        let affectedVehicles = '';
        let detour = '';

        if (item.description && Array.isArray(item.description)) {
            const parsedInfo = parseEventDescription(item.description, item.dataType);
            beginInfo = parsedInfo.begin;
            endInfo = parsedInfo.end;
            measureInfo = parsedInfo.measure;
            restrictionInfo = parsedInfo.restrictions;
            additionalInfo = parsedInfo.additional;
            closedSection = parsedInfo.closedSection;
            closureReason = parsedInfo.closureReason;
            affectedVehicles = parsedInfo.affectedVehicles;
            detour = parsedInfo.detour;
        }

        // Calculate section length
        const sectionLength = calculateSectionLength(item);
        const lengthIndicator = createLengthIndicator(sectionLength, item.dataType);

        const startTime = item.startTimestamp ? new Date(item.startTimestamp).toLocaleString('de-DE') : '';

        return {
            icon: typeIcon,
            type: typeName,
            autobahn: autobahn,
            title: title,
            subtitle: subtitle,
            distance: distance,
            begin: beginInfo || (startTime ? `Beginn: ${startTime}` : ''),
            end: endInfo,
            measure: measureInfo,
            restrictions: restrictionInfo,
            additional: additionalInfo,
            isBlocked: item.isBlocked && item.isBlocked !== 'false',
            isFuture: item.future,
            identifier: item.identifier || '',
            closedSection: closedSection,
            closureReason: closureReason,
            affectedVehicles: affectedVehicles,
            detour: detour,
            sectionLength: sectionLength,
            lengthIndicator: lengthIndicator
        };
    }

    // Show hover tooltip (simple, follows mouse)
    function showHoverTooltip(event, content) {
        hideHoverTooltip(); // Clear any existing hover tooltip
        
        const tooltip = document.createElement('div');
        tooltip.id = 'traffic-hover-tooltip';
        tooltip.style.cssText = `
            position: fixed;
            background: rgba(0, 0, 0, 0.8);
            color: white;
            padding: 8px 12px;
            border-radius: 4px;
            font-size: 12px;
            font-family: Arial, sans-serif;
            z-index: 10002;
            pointer-events: none;
            max-width: 300px;
            line-height: 1.3;
        `;

        tooltip.innerHTML = `
            <div style="font-weight: bold; margin-bottom: 4px;">
                ${content.icon} ${content.type} - ${content.autobahn}
            </div>
            <div style="font-size: 11px;">
                ${content.title}
            </div>
            ${content.distance ? `<div style="font-size: 10px; margin-top: 4px; opacity: 0.8;">Entfernung: ${content.distance}</div>` : ''}
            ${content.sectionLength > 0 ? `<div style="font-size: 10px; margin-top: 2px; opacity: 0.9; color: #ffc107;">Länge: ${formatSectionLength(content.sectionLength)}</div>` : ''}
        `;

        document.body.appendChild(tooltip);
        currentTooltip = tooltip;

        // Position tooltip
        const x = event.clientX + 15;
        const y = event.clientY - 10;
        
        tooltip.style.left = x + 'px';
        tooltip.style.top = y + 'px';

        // Adjust if tooltip goes off screen
        setTimeout(() => {
            const rect = tooltip.getBoundingClientRect();
            if (rect.right > window.innerWidth) {
                tooltip.style.left = (event.clientX - rect.width - 15) + 'px';
            }
            if (rect.bottom > window.innerHeight) {
                tooltip.style.top = (event.clientY - rect.height - 15) + 'px';
            }
        }, 10);
    }

    // Hide hover tooltip
    function hideHoverTooltip() {
        if (currentTooltip) {
            currentTooltip.remove();
            currentTooltip = null;
        }
    }

    // Show persistent popup with full details (on click)
    function showPersistentPopup(event, content) {
        // Don't create multiple popups for the same event
        const existingPopup = document.getElementById(`traffic-popup-${content.identifier}`);
        if (existingPopup) {
            // Just bring existing popup to front
            existingPopup.style.zIndex = '10001';
            return;
        }
        
        const popup = document.createElement('div');
        popup.id = `traffic-popup-${content.identifier}`;
        popup.className = 'traffic-persistent-popup';
        popup.style.cssText = `
            position: fixed;
            background: white;
            border: 2px solid #007cba;
            border-radius: 8px;
            padding: 12px;
            font-size: 12px;
            font-family: Arial, sans-serif;
            box-shadow: 0 6px 20px rgba(0,0,0,0.3);
            z-index: 10000;
            max-width: 400px;
            min-width: 280px;
            opacity: 0;
            transition: opacity 0.2s ease;
            line-height: 1.4;
            user-select: text;
            cursor: default;
        `;

        // Create close button
        const closeButton = document.createElement('div');
        closeButton.innerHTML = '✕';
        closeButton.style.cssText = `
            position: absolute;
            top: 8px;
            right: 8px;
            width: 20px;
            height: 20px;
            background: #ff4444;
            color: white;
            border-radius: 50%;
            display: flex;
            align-items: center;
            justify-content: center;
            cursor: pointer;
            font-weight: bold;
            font-size: 12px;
            z-index: 1;
        `;
        
        closeButton.addEventListener('click', function(e) {
            e.stopPropagation();
            popup.remove();
        });

        // Create drag handle
        const dragHandle = document.createElement('div');
        dragHandle.innerHTML = '⋮⋮';
        dragHandle.style.cssText = `
            position: absolute;
            top: 8px;
            right: 32px;
            width: 20px;
            height: 20px;
            background: #007cba;
            color: white;
            border-radius: 3px;
            display: flex;
            align-items: center;
            justify-content: center;
            cursor: move;
            font-weight: bold;
            font-size: 10px;
            z-index: 1;
        `;

        let popupHTML = `
            <div style="margin-bottom: 8px; font-weight: bold; color: #007cba; border-bottom: 1px solid #eee; padding-bottom: 6px; padding-right: 60px; font-size: 13px;">
                ${content.icon} ${content.type} - ${content.autobahn}
            </div>
            <div style="margin-bottom: 6px;">
                <strong>📍 Strecke:</strong><br>
                <span style="font-size: 11px; user-select: text;">${content.title}</span>
            </div>
        `;

        if (content.subtitle) {
            popupHTML += `
            <div style="margin-bottom: 6px;">
                <strong>🚗 Richtung:</strong> <span style="font-size: 11px; user-select: text;">${content.subtitle}</span>
            </div>`;
        }

        if (content.measure) {
            popupHTML += `
            <div style="margin-bottom: 6px;">
                <strong>🔧 Maßnahme:</strong> <span style="font-size: 11px; user-select: text;">${content.measure}</span>
            </div>`;
        }

        // Enhanced closure information display
        if (content.closedSection) {
            popupHTML += `
            <div style="margin-bottom: 6px; padding: 6px; background: #fff3cd; border-left: 3px solid #dc3545; border-radius: 3px;">
                <strong>🚧 Gesperrter Streckenabschnitt:</strong><br>
                <span style="font-size: 12px; font-weight: bold; color: #721c24; user-select: text;">${content.closedSection}</span>
            </div>`;
        }

        // Add visual length indicator
        if (content.lengthIndicator) {
            popupHTML += content.lengthIndicator;
        }

        if (content.closureReason) {
            popupHTML += `
            <div style="margin-bottom: 6px;">
                <strong>❓ Sperrungsgrund:</strong> <span style="font-size: 11px; user-select: text;">${content.closureReason}</span>
            </div>`;
        }

        if (content.affectedVehicles) {
            popupHTML += `
            <div style="margin-bottom: 6px;">
                <strong>🚛 Betroffene Fahrzeuge:</strong> <span style="font-size: 11px; user-select: text;">${content.affectedVehicles}</span>
            </div>`;
        }

        if (content.detour) {
            popupHTML += `
            <div style="margin-bottom: 6px; padding: 6px; background: #d4edda; border-left: 3px solid #28a745; border-radius: 3px;">
                <strong>🛣️ Umleitung:</strong><br>
                <span style="font-size: 11px; user-select: text;">${content.detour}</span>
            </div>`;
        }

        if (content.begin || content.end) {
            popupHTML += `
            <div style="margin-bottom: 6px;">
                <strong>📅 Zeitraum:</strong><br>
                <span style="font-size: 11px; user-select: text;">
                    ${content.begin}<br>
                    ${content.end || 'Ende: Unbekannt'}
                </span>
            </div>`;
        }

        if (content.restrictions) {
            popupHTML += `
            <div style="margin-bottom: 6px; padding: 6px; background: #fff3cd; border-left: 3px solid #ffc107; border-radius: 3px;">
                <strong>⚠️ Einschränkungen:</strong><br>
                <span style="font-size: 11px; user-select: text;">${content.restrictions}</span>
            </div>`;
        }

        if (content.additional) {
            popupHTML += `
            <div style="margin-bottom: 6px; padding: 6px; background: #e8f4fd; border-left: 3px solid #007cba; border-radius: 3px;">
                <strong>ℹ️ Information:</strong><br>
                <span style="font-size: 11px; user-select: text;">${content.additional}</span>
            </div>`;
        }

        if (content.isFuture) {
            popupHTML += `
            <div style="margin-bottom: 6px; padding: 4px; background: #d1ecf1; border-radius: 3px; font-size: 11px; color: #0c5460;">
                <strong>🔮 Zukünftiges Ereignis</strong>
            </div>`;
        }

        if (content.isBlocked) {
            popupHTML += `
            <div style="margin-bottom: 6px; padding: 4px; background: #f8d7da; border-radius: 3px; font-size: 11px; color: #721c24;">
                <strong>🚫 Blockiert</strong>
            </div>`;
        }

        if (content.distance) {
            popupHTML += `
            <div style="margin-bottom: 6px;">
                <strong>📏 Entfernung:</strong> <span style="user-select: text;">${content.distance}</span>
            </div>`;
        }

        popupHTML += `
            <div style="font-size: 10px; color: #666; border-top: 1px solid #eee; padding-top: 6px; margin-top: 8px;">
                <strong>ID:</strong> <span style="user-select: text;">${content.identifier}</span>
            </div>
            <div style="margin-top: 8px; text-align: center;">
                <button class="copy-id-btn" style="padding: 4px 8px; font-size: 10px; background: #28a745; color: white; border: none; border-radius: 3px; cursor: pointer; margin-right: 5px;">
                    📋 Copy ID
                </button>
                <button class="copy-all-btn" style="padding: 4px 8px; font-size: 10px; background: #007cba; color: white; border: none; border-radius: 3px; cursor: pointer;">
                    📄 Copy All Info
                </button>
            </div>
        `;

        popup.innerHTML = popupHTML;
        popup.appendChild(closeButton);
        popup.appendChild(dragHandle);
        
        // Add event listeners for copy buttons
        const copyIdBtn = popup.querySelector('.copy-id-btn');
        const copyAllBtn = popup.querySelector('.copy-all-btn');
        
        copyIdBtn.addEventListener('click', function() {
            copyToClipboard(content.identifier, popup);
        });
        
        copyAllBtn.addEventListener('click', function() {
            copyAllInfo(content, popup);
        });
        
        document.body.appendChild(popup);
        
        // Position popup
        let x = event.clientX + 15;
        let y = event.clientY - 10;
        
        popup.style.left = x + 'px';
        popup.style.top = y + 'px';
        
        // Show with fade-in
        setTimeout(() => {
            popup.style.opacity = '1';
        }, 10);
        
        // Adjust position if popup goes off screen
        setTimeout(() => {
            const rect = popup.getBoundingClientRect();
            if (rect.right > window.innerWidth) {
                x = event.clientX - rect.width - 15;
                popup.style.left = x + 'px';
            }
            if (rect.bottom > window.innerHeight) {
                y = event.clientY - rect.height - 10;
                popup.style.top = y + 'px';
            }
            if (rect.top < 0) {
                popup.style.top = '10px';
            }
            if (x < 0) {
                popup.style.left = '10px';
            }
        }, 20);

        // Make popup draggable
        makeDraggable(popup, dragHandle);

        // Store content for copy functions
        popup.contentData = content;
    }

    // Hide tooltip (now hides all popups and hover tooltips)
    function hideTooltip() {
        hideHoverTooltip();
        const popups = document.querySelectorAll('.traffic-persistent-popup');
        popups.forEach(popup => popup.remove());
    }

    // Make popup draggable
    function makeDraggable(popup, handle) {
        let isDragging = false;
        let startX, startY;
        let popupStartX, popupStartY;

        handle.addEventListener('mousedown', dragStart);
        document.addEventListener('mousemove', drag);
        document.addEventListener('mouseup', dragEnd);

        function dragStart(e) {
            e.preventDefault();
            isDragging = true;
            
            // Get the current popup position
            const rect = popup.getBoundingClientRect();
            popupStartX = rect.left;
            popupStartY = rect.top;
            
            // Get mouse position
            startX = e.clientX;
            startY = e.clientY;
            
            popup.style.zIndex = '10001'; // Bring to front when dragging
            popup.style.transition = 'none'; // Disable transition during drag
            
            // Add dragging class for visual feedback
            popup.style.opacity = '0.9';
            handle.style.background = '#005a9b';
        }

        function drag(e) {
            if (!isDragging) return;
            
            e.preventDefault();
            
            // Calculate the new position
            const deltaX = e.clientX - startX;
            const deltaY = e.clientY - startY;
            
            const newX = popupStartX + deltaX;
            const newY = popupStartY + deltaY;
            
            // Apply constraints to keep popup on screen
            const maxX = window.innerWidth - popup.offsetWidth;
            const maxY = window.innerHeight - popup.offsetHeight;
            
            const constrainedX = Math.max(0, Math.min(newX, maxX));
            const constrainedY = Math.max(0, Math.min(newY, maxY));
            
            popup.style.left = constrainedX + 'px';
            popup.style.top = constrainedY + 'px';
        }

        function dragEnd(e) {
            if (!isDragging) return;
            
            isDragging = false;
            
            // Restore visual feedback
            popup.style.opacity = '1';
            popup.style.transition = 'opacity 0.2s ease';
            handle.style.background = '#007cba';
        }
    }

    // Copy functions
    function copyToClipboard(identifier, popup) {
        navigator.clipboard.writeText(identifier).then(() => {
            // Show brief confirmation
            if (popup) {
                const originalBorder = popup.style.border;
                popup.style.border = '2px solid #28a745';
                setTimeout(() => {
                    popup.style.border = originalBorder;
                }, 300);
            }
        }).catch(err => {
            console.error('Failed to copy ID:', err);
            // Fallback for older browsers
            const textArea = document.createElement('textarea');
            textArea.value = identifier;
            document.body.appendChild(textArea);
            textArea.select();
            try {
                document.execCommand('copy');
                if (popup) {
                    const originalBorder = popup.style.border;
                    popup.style.border = '2px solid #28a745';
                    setTimeout(() => {
                        popup.style.border = originalBorder;
                    }, 300);
                }
            } catch (fallbackErr) {
                console.error('Fallback copy failed:', fallbackErr);
            }
            document.body.removeChild(textArea);
        });
    }

    function copyAllInfo(content, popup) {
        let copyText = `${content.type} - ${content.autobahn}\n`;
        copyText += `Strecke: ${content.title}\n`;
        if (content.subtitle) copyText += `Richtung: ${content.subtitle}\n`;
        if (content.measure) copyText += `Maßnahme: ${content.measure}\n`;
        if (content.begin) copyText += `${content.begin}\n`;
        if (content.end) copyText += `${content.end}\n`;
        if (content.restrictions) copyText += `Einschränkungen: ${content.restrictions}\n`;
        if (content.additional) copyText += `Information: ${content.additional}\n`;
        if (content.distance) copyText += `Entfernung: ${content.distance}\n`;
        copyText += `ID: ${content.identifier}`;

        navigator.clipboard.writeText(copyText).then(() => {
            // Show brief confirmation
            if (popup) {
                const originalBorder = popup.style.border;
                popup.style.border = '2px solid #28a745';
                setTimeout(() => {
                    popup.style.border = originalBorder;
                }, 300);
            }
        }).catch(err => {
            console.error('Failed to copy info:', err);
            // Fallback for older browsers
            const textArea = document.createElement('textarea');
            textArea.value = copyText;
            document.body.appendChild(textArea);
            textArea.select();
            try {
                document.execCommand('copy');
                if (popup) {
                    const originalBorder = popup.style.border;
                    popup.style.border = '2px solid #28a745';
                    setTimeout(() => {
                        popup.style.border = originalBorder;
                    }, 300);
                }
            } catch (fallbackErr) {
                console.error('Fallback copy failed:', fallbackErr);
            }
            document.body.removeChild(textArea);
        });
    }

    // Create marker icon based on data type
    function createMarkerIcon(dataType) {
        const iconConfig = {
            roadworks: { emoji: '🚧', color: '#ff9800' },
            warning: { emoji: '⚠️', color: '#f44336' },
            closure: { emoji: '🚫', color: '#9c27b0' }
        };

        const config = iconConfig[dataType] || { emoji: '📍', color: '#2196f3' };

        const svg = `
            <svg width="24" height="24" xmlns="http://www.w3.org/2000/svg">
                <circle cx="12" cy="12" r="10" fill="${config.color}" stroke="white" stroke-width="2" opacity="0.9"/>
                <text x="12" y="16" text-anchor="middle" font-size="12" fill="white">${config.emoji}</text>
            </svg>
        `;
        
        return 'data:image/svg+xml;charset=utf-8,' + encodeURIComponent(svg);
    }

    // Get display type specific information
    function getDisplayTypeInfo(displayType) {
        const displayTypes = {
            'ROADWORKS': {
                name: 'Baustelle',
                icon: '🚧',
                description: 'Bauarbeiten im Gange',
                color: '#fff3cd'
            },
            'WEBCAM': {
                name: 'Webcam',
                icon: '📹',
                description: 'Verkehrskamera verfügbar',
                color: '#e8f4fd'
            },
            'PARKING': {
                name: 'Rastplatz',
                icon: '🅿️',
                description: 'Parkplatz/Rastanlage',
                color: '#d4edda'
            },
            'WARNING': {
                name: 'Verkehrsmeldung',
                icon: '⚠️',
                description: 'Aktuelle Verkehrswarnung',
                color: '#fff3cd'
            },
            'WEIGHT_LIMIT_35': {
                name: 'Gewichtsbeschränkung',
                icon: '⚖️',
                description: 'Sperrung für Fahrzeuge über 3,5t',
                color: '#f8d7da'
            },
            'CLOSURE': {
                name: 'Sperrung',
                icon: '🚫',
                description: 'Vollsperrung der Fahrbahn',
                color: '#f8d7da'
            },
            'CLOSURE_ENTRY_EXIT': {
                name: 'Anschlussstellen-Sperrung',
                icon: '🚫',
                description: 'Ein-/Ausfahrt gesperrt',
                color: '#f8d7da'
            },
            'STRONG_ELECTRIC_CHARGING_STATION': {
                name: 'Schnellladestation',
                icon: '⚡',
                description: 'Elektrische Schnellladestation',
                color: '#d1ecf1'
            }
        };

        return displayTypes[displayType] || {};
    }

    // Add a road section line based on extent data with length labels
    function addRoadSection(item) {
        if (!item.extent || !vectorLayer) {
            return;
        }

        try {
            // Parse extent: "lon1,lat1,lon2,lat2"
            const extentParts = item.extent.split(',').map(parseFloat);
            if (extentParts.length !== 4) {
                console.log('Invalid extent format for item:', item.identifier);
                return;
            }

            const [lon1, lat1, lon2, lat2] = extentParts;

            // Create line geometry from extent corners
            const points = [
                new OpenLayers.Geometry.Point(lon1, lat1).transform(
                    new OpenLayers.Projection("EPSG:4326"), 
                    new OpenLayers.Projection("EPSG:3857")
                ),
                new OpenLayers.Geometry.Point(lon2, lat2).transform(
                    new OpenLayers.Projection("EPSG:4326"), 
                    new OpenLayers.Projection("EPSG:3857")
                )
            ];

            const lineGeometry = new OpenLayers.Geometry.LineString(points);

            // Calculate section length for display
            const sectionLength = calculateSectionLength(item);
            const lengthText = formatSectionLength(sectionLength);

            // Create style based on event type
            const style = getRoadSectionStyle(item.dataType);
            
            const lineFeature = new OpenLayers.Feature.Vector(lineGeometry, {
                eventId: item.identifier,
                eventType: item.dataType,
                title: item.title,
                autobahn: item.autobahn,
                sectionLength: lengthText
            }, style);

            // Store item data for events
            lineFeature.eventData = item;

            vectorLayer.addFeatures([lineFeature]);
            trafficFeatures.push({feature: lineFeature, data: item});

            // Add length label as separate feature if length is available
            if (sectionLength > 0) {
                addSectionLengthLabel(lineGeometry, lengthText, item.dataType, item.identifier);
            }

            console.log('Added road section for:', item.identifier, 'extent:', item.extent, 'length:', lengthText);
        } catch (error) {
            console.error('Error adding road section for item:', item.identifier, error);
        }
    }

    // Add length label on the road section using OpenLayers 2 compatible method
    function addSectionLengthLabel(lineGeometry, lengthText, eventType, identifier) {
        try {
            // Calculate midpoint of the line
            const bounds = lineGeometry.getBounds();
            const centerPoint = bounds.getCenterLonLat();

            // Create point geometry for label
            const labelPoint = new OpenLayers.Geometry.Point(centerPoint.lon, centerPoint.lat);

            // Style for the length label - simplified for OpenLayers 2
            const labelStyle = new OpenLayers.Style({
                label: lengthText,
                fontColor: "#ffffff",
                fontSize: "11px",
                fontFamily: "Arial, sans-serif",
                fontWeight: "bold",
                labelAlign: "cm",
                labelXOffset: 0,
                labelYOffset: 0,
                labelOutlineColor: "#000000",
                labelOutlineWidth: 2,
                pointRadius: 8,
                fillColor: getEventTypeColor(eventType),
                fillOpacity: 0.9,
                strokeColor: "#ffffff",
                strokeWidth: 1,
                strokeOpacity: 1
            });

            const labelFeature = new OpenLayers.Feature.Vector(labelPoint, {
                eventId: identifier + '_label',
                isLabel: true,
                lengthText: lengthText
            }, labelStyle);

            vectorLayer.addFeatures([labelFeature]);
            trafficFeatures.push({feature: labelFeature, data: {identifier: identifier + '_label', isLabel: true}});

            console.log('Added length label:', lengthText, 'for event:', identifier);

        } catch (error) {
            console.error('Error adding section label:', error);
        }
    }

    // Get event type color for labels
    function getEventTypeColor(eventType) {
        const colors = {
            roadworks: "#ff9800",
            warning: "#f44336", 
            closure: "#9c27b0"
        };
        return colors[eventType] || "#2196f3";
    }

    // Get style for road section based on event type
    function getRoadSectionStyle(dataType) {
        const styleConfig = {
            roadworks: {
                strokeColor: "#ff9800",
                strokeWidth: 6,
                strokeOpacity: 0.8,
                strokeDasharray: "15,10"
            },
            warning: {
                strokeColor: "#f44336", 
                strokeWidth: 5,
                strokeOpacity: 0.7,
                strokeDasharray: "10,5"
            },
            closure: {
                strokeColor: "#9c27b0",
                strokeWidth: 7,
                strokeOpacity: 0.9,
                strokeDasharray: "20,5"
            }
        };

        const config = styleConfig[dataType] || {
            strokeColor: "#2196f3",
            strokeWidth: 4,
            strokeOpacity: 0.6,
            strokeDasharray: "8,8"
        };

        return config; // Return plain style object
    }

    // Clear all vector features (road sections)
    function clearFeatures() {
        if (vectorLayer) {
            vectorLayer.removeAllFeatures();
        }
        trafficFeatures = [];
    }

    // Clear all markers
    function clearMarkers() {
        if (overlayLayer) {
            overlayLayer.clearMarkers();
        }
        trafficMarkers = [];
    }

    // Get selected data sources
    function getSelectedSources() {
        const sources = [];
        document.querySelectorAll('.vz-source:checked').forEach(checkbox => {
            sources.push(checkbox.dataset.source);
        });
        return sources;
    }

    // Update status display
    function updateStatus(message) {
        const statusDisplay = document.getElementById('statusDisplay');
        if (statusDisplay) {
            const timestamp = new Date().toLocaleTimeString('de-DE');
            statusDisplay.innerHTML = `[${timestamp}] ${message}`;
        }
        console.log(`${SCRIPT_NAME}: ${message}`);
    }

    // Update statistics display
    function updateStatistics() {
        const statisticsDisplay = document.getElementById('statisticsDisplay');
        if (statisticsDisplay) {
            const lastUpdate = lastUpdateTime ? lastUpdateTime.toLocaleString('de-DE') : 'Never';
            const totalEvents = trafficMarkers.length;
            const activeRequests = currentRequests.length;
            
            statisticsDisplay.innerHTML = `
                <strong>Statistics:</strong><br>
                Last Update: ${lastUpdate}<br>
                Total Events: ${totalEvents}<br>
                Active Requests: ${activeRequests}
            `;
        }
    }

    // Main initialization logic
    function bootstrap() {
        if (W?.userscripts?.state.isReady) {
            initializeScript();
        } else if (W?.userscripts?.state.isInitialized) {
            document.addEventListener("wme-ready", initializeScript, { once: true });
        } else {
            document.addEventListener("wme-initialized", function() {
                if (W?.userscripts?.state.isReady) {
                    initializeScript();
                } else {
                    document.addEventListener("wme-ready", initializeScript, { once: true });
                }
            }, { once: true });
        }
    }

    // Start the script
    bootstrap();

    // Cleanup function
    window.addEventListener('beforeunload', function() {
        try {
            if (updateInterval) {
                clearInterval(updateInterval);
            }
            currentRequests.forEach(request => {
                if (request.abort) request.abort();
            });
            if (overlayLayer && W.map) {
                W.map.removeLayer(overlayLayer);
            }
            if (vectorLayer && W.map) {
                W.map.removeLayer(vectorLayer);
            }
            hideTooltip(); // Clean up any tooltips
            W.userscripts.removeSidebarTab(SCRIPT_ID);
        } catch (error) {
            console.error('Cleanup error:', error);
        }
    });

})();