WME RTC Plus

RTC Management mit Kreisen, Perimeter-Sperrungen, GPX Import/Export

이 스크립트를 설치하려면 Tampermonkey, Greasemonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램을 설치해야 합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Userscripts와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 유저 스크립트 관리자 확장 프로그램이 필요합니다.

(이미 유저 스크립트 관리자가 설치되어 있습니다. 설치를 진행합니다!)

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

(이미 유저 스타일 관리자가 설치되어 있습니다. 설치를 진행합니다!)

// ==UserScript==
// @name         WME RTC Plus
// @namespace    http://tampermonkey.net/
// @version      2026.01.11
// @description  RTC Management mit Kreisen, Perimeter-Sperrungen, GPX Import/Export
// @namespace    https://greasyfork.org/de/users/863740-horst-wittlich
// @author       Hiwi234
// @match        https://*.waze.com/editor*
// @match        https://*.waze.com/*/editor*
// @match        https://beta.waze.com/editor*
// @match        https://beta.waze.com/*/editor*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=waze.com
// @grant        none
// @license      MIT
// @run-at       document-idle
// ==/UserScript==

(function() {
    'use strict';

    let uOpenLayers;
    let uWaze;
    let radiusLayer, infoLayer;
    let polygonControl, freehandControl, dragControl;
    let eventListeners = [];
    let modificationHistory = [];
    let presetCircleMode = false;
    let presetCircleCenter = null;
    const DEBUG = true;
    const EARTH_RADIUS = 6371000;

    // Translations
    const translations = {
        en: {
            tabTitle: 'RTC +',
            title: 'WME RTC Plus',
            enableCircle: 'Enable Circle Drawing',
            enableFreehand: 'Enable Free Hand Drawing',
            selectStreets: 'Select / Start',
            undoLastCircle: 'Undo',
            clearCircles: 'Clear Circles',
            displayOptions: 'Display Options:',
            showRadius: 'Show Radius',
            showDiameter: 'Show Diameter',
            showArea: 'Show Area',
            imperialUnits: 'Imperial Units (ft/mi)',
            filterOptions: 'Filter Options:',
            onlyDrivable: 'Only drivable segments',
            perimeterBlocking: 'Perimeter Blocking:',
            blockInbound: 'Block perimeter inbound only',
            blockInboundHint: '(hollow circle - exit allowed)',
            rtcSettings: 'RTC Settings:',
            rtcReason: 'Closure reason:',
            rtcDuration: 'Duration:',
            rtcHours: 'hrs',
            rtcMinutes: 'min',
            rtcDefaultReason: 'Perimeter closure',
            rtcModeDuration: 'Duration',
            rtcModeEndDate: 'End date',
            rtcEndDate: 'End',
            deleteRtcsInArea: 'Delete RTCs in area',
            deletingRtcs: 'Deleting RTCs...',
            rtcsDeleted: '✓ {0} RTCs deleted',
            noRtcsFound: 'No RTCs found in area',
            currentRadius: 'Current Radius',
            circlesCleared: 'Circles cleared',
            noCirclesToUndo: 'No circles to undo',
            removedCircle: 'Removed last circle',
            remaining: 'remaining',
            errorRemoving: 'Error removing circle',
            selectedSegments: 'Selected {0} segments',
            noSegmentsFound: 'No segments found in circles',
            appliedRestrictions: '✓ Applied RTC to {0} segments (inbound blocked)',
            noModification: '⚠ No segments needed modification ({0} processed)',
            presetCircle: 'Preset Circle:',
            presetRadius: 'Radius:',
            presetHint: 'Create Circle',
            clickForCenter: 'Click for center',
            circleCreated: 'Circle created ({0}m)',
            moveCircles: 'Move circles',
            moveHint: 'Drag circles to move them',
            createMapComment: 'Create Map Comment',
            mapCommentOptions: 'Map Comment Options:',
            commentTitle: 'Title:',
            commentDesc: 'Description:',
            expiresIn: 'Expires in:',
            days: 'days',
            createFromCircle: 'Create from last circle',
            noCircleToConvert: 'No circle to convert',
            mapCommentCreated: 'Map Comment created!',
            selectCircleFirst: 'Draw a circle first',
            brushMode: 'Segment Brush',
            brushModeHint: 'Paint to select segments',
            brushErase: 'Erase mode',
            brushModeActive: 'Segment brush active',
            exportRtcs: 'Export RTCs',
            importRtcs: 'Import RTCs',
            exportedRtcs: '✓ {0} RTCs exported',
            importedRtcs: '✓ {0} RTCs imported',
            noRtcsToExport: 'No RTCs to export',
            importExport: 'Import/Export:',
            exportTypeRtcs: 'RTCs',
            exportTypeCircles: 'Circles',
            exportedCircles: '✓ {0} circles exported',
            importedCircles: '✓ {0} circles imported',
            noCirclesToExport: 'No circles to export',
            exportTypeGpx: 'GPX',
            importedGpx: '✓ GPX imported: {0} segments matched',
            exportedGpx: '✓ GPX exported',
            gpxNoSegments: 'No segments matched from GPX'
        },
        de: {
            tabTitle: 'RTC +',
            title: 'WME RTC Plus',
            enableCircle: 'Kreis zeichnen aktivieren',
            enableFreehand: 'Freihand zeichnen aktivieren',
            selectStreets: 'Auswahl / Start',
            undoLastCircle: 'Zurück',
            clearCircles: 'Kreise löschen',
            displayOptions: 'Anzeigeoptionen:',
            showRadius: 'Radius anzeigen',
            showDiameter: 'Durchmesser anzeigen',
            showArea: 'Fläche anzeigen',
            imperialUnits: 'Imperiale Einheiten (ft/mi)',
            filterOptions: 'Filteroptionen:',
            onlyDrivable: 'Nur befahrbare Segmente',
            perimeterBlocking: 'Perimeter-Sperrung:',
            blockInbound: 'Nur eingehenden Verkehr sperren',
            blockInboundHint: '(hohler Kreis - Ausfahrt erlaubt)',
            rtcSettings: 'RTC Einstellungen:',
            rtcReason: 'Sperrgrund:',
            rtcDuration: 'Sperrdauer:',
            rtcHours: 'Std',
            rtcMinutes: 'Min',
            rtcDefaultReason: 'Perimeter-Sperrung',
            rtcModeDuration: 'Dauer',
            rtcModeEndDate: 'Enddatum',
            rtcEndDate: 'Ende',
            deleteRtcsInArea: 'RTCs im Bereich löschen',
            deletingRtcs: 'Lösche RTCs...',
            rtcsDeleted: '✓ {0} RTCs gelöscht',
            noRtcsFound: 'Keine RTCs im Bereich gefunden',
            currentRadius: 'Aktueller Radius',
            circlesCleared: 'Kreise gelöscht',
            noCirclesToUndo: 'Keine Kreise zum Rückgängigmachen',
            removedCircle: 'Letzten Kreis entfernt',
            remaining: 'übrig',
            errorRemoving: 'Fehler beim Entfernen',
            selectedSegments: '{0} Segmente ausgewählt',
            noSegmentsFound: 'Keine Segmente in Kreisen gefunden',
            appliedRestrictions: '✓ RTC auf {0} Segmente angewendet (Einfahrt gesperrt)',
            noModification: '⚠ Keine Segmente mussten geändert werden ({0} verarbeitet)',
            presetCircle: 'Vorbereiteter Kreis:',
            presetRadius: 'Radius:',
            presetHint: 'Kreis Erstellen',
            clickForCenter: 'Klicke für Mittelpunkt',
            circleCreated: 'Kreis erstellt ({0}m)',
            moveCircles: 'Kreise verschieben',
            moveHint: 'Kreise ziehen zum Verschieben',
            createMapComment: 'Map Comment erstellen',
            mapCommentOptions: 'Map Comment Optionen:',
            commentTitle: 'Titel:',
            commentDesc: 'Beschreibung:',
            expiresIn: 'Läuft ab in:',
            days: 'Tagen',
            createFromCircle: 'Aus letztem Kreis erstellen',
            noCircleToConvert: 'Kein Kreis vorhanden',
            mapCommentCreated: 'Map Comment erstellt!',
            selectCircleFirst: 'Erst einen Kreis zeichnen',
            brushMode: 'Segment-Pinsel',
            brushModeHint: 'Malen um Segmente auszuwählen',
            brushErase: 'Löschen-Modus',
            brushModeActive: 'Segment-Pinsel aktiv',
            exportRtcs: 'RTCs exportieren',
            importRtcs: 'RTCs importieren',
            exportedRtcs: '✓ {0} RTCs exportiert',
            importedRtcs: '✓ {0} RTCs importiert',
            noRtcsToExport: 'Keine RTCs zum Exportieren',
            importExport: 'Import/Export:',
            exportTypeRtcs: 'RTCs',
            exportTypeCircles: 'Kreise',
            exportedCircles: '✓ {0} Kreise exportiert',
            importedCircles: '✓ {0} Kreise importiert',
            noCirclesToExport: 'Keine Kreise zum Exportieren',
            exportTypeGpx: 'GPX',
            importedGpx: '✓ GPX importiert: {0} Segmente gematcht',
            exportedGpx: '✓ GPX exportiert',
            gpxNoSegments: 'Keine Segmente aus GPX gematcht'
        },
        fr: {
            tabTitle: 'RTC +',
            title: 'WME RTC Plus',
            enableCircle: 'Activer le dessin de cercle',
            enableFreehand: 'Activer le dessin à main levée',
            selectStreets: 'Sélection / Démarrer',
            undoLastCircle: 'Annuler',
            clearCircles: 'Effacer les cercles',
            displayOptions: "Options d'affichage:",
            showRadius: 'Afficher le rayon',
            showDiameter: 'Afficher le diamètre',
            showArea: 'Afficher la surface',
            imperialUnits: 'Unités impériales (ft/mi)',
            filterOptions: 'Options de filtre:',
            onlyDrivable: 'Segments praticables uniquement',
            perimeterBlocking: 'Blocage du périmètre:',
            blockInbound: 'Bloquer uniquement le trafic entrant',
            blockInboundHint: '(cercle creux - sortie autorisée)',
            currentRadius: 'Rayon actuel',
            circlesCleared: 'Cercles effacés',
            noCirclesToUndo: 'Aucun cercle à annuler',
            removedCircle: 'Dernier cercle supprimé',
            remaining: 'restant(s)',
            errorRemoving: 'Erreur lors de la suppression',
            selectedSegments: '{0} segments sélectionnés',
            noSegmentsFound: 'Aucun segment trouvé dans les cercles',
            appliedRestrictions: '✓ Restrictions de direction appliquées à {0} segments (sortie préservée)',
            noModification: '⚠ Aucun segment à modifier ({0} traités)',
            presetCircle: 'Cercle prédéfini:',
            presetRadius: 'Rayon:',
            presetHint: 'Créer Cercle',
            clickForCenter: 'Cliquez pour centre',
            circleCreated: 'Cercle créé ({0}m)',
            moveCircles: 'Déplacer les cercles',
            moveHint: 'Glisser pour déplacer',
            createMapComment: 'Créer Map Comment',
            mapCommentOptions: 'Options Map Comment:',
            commentTitle: 'Titre:',
            commentDesc: 'Description:',
            expiresIn: 'Expire dans:',
            days: 'jours',
            createFromCircle: 'Créer depuis dernier cercle',
            noCircleToConvert: 'Aucun cercle disponible',
            mapCommentCreated: 'Map Comment créé!',
            selectCircleFirst: 'Dessinez un cercle d\'abord',
            brushMode: 'Pinceau segments',
            brushModeHint: 'Peindre pour sélectionner',
            brushErase: 'Mode effacer',
            brushModeActive: 'Pinceau segments actif',
            exportRtcs: 'Exporter RTCs',
            importRtcs: 'Importer RTCs',
            exportedRtcs: '✓ {0} RTCs exportés',
            importedRtcs: '✓ {0} RTCs importés',
            noRtcsToExport: 'Aucun RTC à exporter',
            importExport: 'Import/Export:',
            exportTypeRtcs: 'RTCs',
            exportTypeCircles: 'Cercles',
            exportedCircles: '✓ {0} cercles exportés',
            importedCircles: '✓ {0} cercles importés',
            noCirclesToExport: 'Aucun cercle à exporter',
            exportTypeGpx: 'GPX',
            importedGpx: '✓ GPX importé: {0} segments matchés',
            exportedGpx: '✓ GPX exporté',
            gpxNoSegments: 'Aucun segment matché depuis GPX'
        },
        it: {
            tabTitle: 'RTC +',
            title: 'WME RTC Plus',
            enableCircle: 'Attiva disegno cerchio',
            enableFreehand: 'Attiva disegno a mano libera',
            selectStreets: 'Selezione / Avvia',
            undoLastCircle: 'Annulla',
            clearCircles: 'Cancella cerchi',
            displayOptions: 'Opzioni di visualizzazione:',
            showRadius: 'Mostra raggio',
            showDiameter: 'Mostra diametro',
            showArea: 'Mostra area',
            imperialUnits: 'Unità imperiali (ft/mi)',
            filterOptions: 'Opzioni filtro:',
            onlyDrivable: 'Solo segmenti percorribili',
            perimeterBlocking: 'Blocco perimetrale:',
            blockInbound: 'Blocca solo traffico in entrata',
            blockInboundHint: '(cerchio vuoto - uscita consentita)',
            currentRadius: 'Raggio attuale',
            circlesCleared: 'Cerchi cancellati',
            noCirclesToUndo: 'Nessun cerchio da annullare',
            removedCircle: 'Ultimo cerchio rimosso',
            remaining: 'rimanenti',
            errorRemoving: 'Errore nella rimozione',
            selectedSegments: '{0} segmenti selezionati',
            noSegmentsFound: 'Nessun segmento trovato nei cerchi',
            appliedRestrictions: '✓ Restrizioni di direzione applicate a {0} segmenti (uscita preservata)',
            noModification: '⚠ Nessun segmento da modificare ({0} elaborati)',
            presetCircle: 'Cerchio predefinito:',
            presetRadius: 'Raggio:',
            presetHint: 'Crea Cerchio',
            clickForCenter: 'Clicca per centro',
            circleCreated: 'Cerchio creato ({0}m)',
            moveCircles: 'Sposta i cerchi',
            moveHint: 'Trascina per spostare',
            createMapComment: 'Crea Map Comment',
            mapCommentOptions: 'Opzioni Map Comment:',
            commentTitle: 'Titolo:',
            commentDesc: 'Descrizione:',
            expiresIn: 'Scade tra:',
            days: 'giorni',
            createFromCircle: 'Crea dall\'ultimo cerchio',
            noCircleToConvert: 'Nessun cerchio disponibile',
            mapCommentCreated: 'Map Comment creato!',
            selectCircleFirst: 'Prima disegna un cerchio',
            brushMode: 'Pennello segmenti',
            brushModeHint: 'Dipingi per selezionare',
            brushErase: 'Modalità cancella',
            brushModeActive: 'Pennello segmenti attivo',
            exportRtcs: 'Esporta RTCs',
            importRtcs: 'Importa RTCs',
            exportedRtcs: '✓ {0} RTCs esportati',
            importedRtcs: '✓ {0} RTCs importati',
            noRtcsToExport: 'Nessun RTC da esportare',
            importExport: 'Import/Export:',
            exportTypeRtcs: 'RTCs',
            exportTypeCircles: 'Cerchi',
            exportedCircles: '✓ {0} cerchi esportati',
            importedCircles: '✓ {0} cerchi importati',
            noCirclesToExport: 'Nessun cerchio da esportare',
            exportTypeGpx: 'GPX',
            importedGpx: '✓ GPX importato: {0} segmenti matchati',
            exportedGpx: '✓ GPX esportato',
            gpxNoSegments: 'Nessun segmento matchato da GPX'
        },
        es: {
            tabTitle: 'RTC +',
            title: 'WME RTC Plus',
            enableCircle: 'Activar dibujo de círculo',
            enableFreehand: 'Activar dibujo a mano alzada',
            selectStreets: 'Selección / Iniciar',
            undoLastCircle: 'Deshacer',
            clearCircles: 'Borrar círculos',
            displayOptions: 'Opciones de visualización:',
            showRadius: 'Mostrar radio',
            showDiameter: 'Mostrar diámetro',
            showArea: 'Mostrar área',
            imperialUnits: 'Unidades imperiales (ft/mi)',
            filterOptions: 'Opciones de filtro:',
            onlyDrivable: 'Solo segmentos transitables',
            perimeterBlocking: 'Bloqueo de perímetro:',
            blockInbound: 'Bloquear solo tráfico entrante',
            blockInboundHint: '(círculo hueco - salida permitida)',
            currentRadius: 'Radio actual',
            circlesCleared: 'Círculos borrados',
            noCirclesToUndo: 'No hay círculos para deshacer',
            removedCircle: 'Último círculo eliminado',
            remaining: 'restantes',
            errorRemoving: 'Error al eliminar',
            selectedSegments: '{0} segmentos seleccionados',
            noSegmentsFound: 'No se encontraron segmentos en los círculos',
            appliedRestrictions: '✓ Restricciones de dirección aplicadas a {0} segmentos (salida preservada)',
            noModification: '⚠ Ningún segmento necesitó modificación ({0} procesados)',
            presetCircle: 'Círculo predefinido:',
            presetRadius: 'Radio:',
            presetHint: 'Crear Círculo',
            clickForCenter: 'Clic para centro',
            circleCreated: 'Círculo creado ({0}m)',
            moveCircles: 'Mover los círculos',
            moveHint: 'Arrastra para mover',
            createMapComment: 'Crear Map Comment',
            mapCommentOptions: 'Opciones Map Comment:',
            commentTitle: 'Título:',
            commentDesc: 'Descripción:',
            expiresIn: 'Expira en:',
            days: 'días',
            createFromCircle: 'Crear desde último círculo',
            noCircleToConvert: 'No hay círculo disponible',
            mapCommentCreated: '¡Map Comment creado!',
            selectCircleFirst: 'Primero dibuja un círculo',
            brushMode: 'Pincel segmentos',
            brushModeHint: 'Pintar para seleccionar',
            brushErase: 'Modo borrar',
            brushModeActive: 'Pincel segmentos activo',
            exportRtcs: 'Exportar RTCs',
            importRtcs: 'Importar RTCs',
            exportedRtcs: '✓ {0} RTCs exportados',
            importedRtcs: '✓ {0} RTCs importados',
            noRtcsToExport: 'No hay RTCs para exportar',
            importExport: 'Import/Export:',
            exportTypeRtcs: 'RTCs',
            exportTypeCircles: 'Círculos',
            exportedCircles: '✓ {0} círculos exportados',
            importedCircles: '✓ {0} círculos importados',
            noCirclesToExport: 'No hay círculos para exportar',
            exportTypeGpx: 'GPX',
            importedGpx: '✓ GPX importado: {0} segmentos matcheados',
            exportedGpx: '✓ GPX exportado',
            gpxNoSegments: 'Ningún segmento matcheado desde GPX'
        },
        nl: {
            tabTitle: 'RTC +',
            title: 'WME RTC Plus',
            enableCircle: 'Cirkel tekenen inschakelen',
            enableFreehand: 'Vrije hand tekenen inschakelen',
            selectStreets: 'Selectie / Start',
            undoLastCircle: 'Ongedaan',
            clearCircles: 'Cirkels wissen',
            displayOptions: 'Weergaveopties:',
            showRadius: 'Straal tonen',
            showDiameter: 'Diameter tonen',
            showArea: 'Oppervlakte tonen',
            imperialUnits: 'Imperiale eenheden (ft/mi)',
            filterOptions: 'Filteropties:',
            onlyDrivable: 'Alleen berijdbare segmenten',
            perimeterBlocking: 'Perimeterblokkering:',
            blockInbound: 'Alleen inkomend verkeer blokkeren',
            blockInboundHint: '(holle cirkel - uitgang toegestaan)',
            currentRadius: 'Huidige straal',
            circlesCleared: 'Cirkels gewist',
            noCirclesToUndo: 'Geen cirkels om ongedaan te maken',
            removedCircle: 'Laatste cirkel verwijderd',
            remaining: 'resterend',
            errorRemoving: 'Fout bij verwijderen',
            selectedSegments: '{0} segmenten geselecteerd',
            noSegmentsFound: 'Geen segmenten gevonden in cirkels',
            appliedRestrictions: '✓ Richtingsbeperkingen toegepast op {0} segmenten (uitgang behouden)',
            noModification: '⚠ Geen segmenten hoefden te worden gewijzigd ({0} verwerkt)',
            presetCircle: 'Vooraf ingestelde cirkel:',
            presetRadius: 'Straal:',
            presetHint: 'Cirkel Maken',
            clickForCenter: 'Klik voor middelpunt',
            circleCreated: 'Cirkel gemaakt ({0}m)',
            moveCircles: 'Cirkels verplaatsen',
            moveHint: 'Sleep om te verplaatsen',
            createMapComment: 'Map Comment maken',
            mapCommentOptions: 'Map Comment Opties:',
            commentTitle: 'Titel:',
            commentDesc: 'Beschrijving:',
            expiresIn: 'Verloopt over:',
            days: 'dagen',
            createFromCircle: 'Maken van laatste cirkel',
            noCircleToConvert: 'Geen cirkel beschikbaar',
            mapCommentCreated: 'Map Comment gemaakt!',
            selectCircleFirst: 'Teken eerst een cirkel',
            brushMode: 'Segment-penseel',
            brushModeHint: 'Schilderen om te selecteren',
            brushErase: 'Wis-modus',
            brushModeActive: 'Segment-penseel actief',
            exportRtcs: 'RTCs exporteren',
            importRtcs: 'RTCs importeren',
            exportedRtcs: '✓ {0} RTCs geëxporteerd',
            importedRtcs: '✓ {0} RTCs geïmporteerd',
            noRtcsToExport: 'Geen RTCs om te exporteren',
            importExport: 'Import/Export:',
            exportTypeRtcs: 'RTCs',
            exportTypeCircles: 'Cirkels',
            exportedCircles: '✓ {0} cirkels geëxporteerd',
            importedCircles: '✓ {0} cirkels geïmporteerd',
            noCirclesToExport: 'Geen cirkels om te exporteren',
            exportTypeGpx: 'GPX',
            importedGpx: '✓ GPX geïmporteerd: {0} segmenten gematcht',
            exportedGpx: '✓ GPX geëxporteerd',
            gpxNoSegments: 'Geen segmenten gematcht uit GPX'
        }
    };

    let currentLang = 'en';

    function detectLanguage() {
        try {
            // 1. WME I18n Sprache (Benutzereinstellung im Editor)
            if (window.I18n?.currentLocale) {
                const locale = window.I18n.currentLocale();
                const lang = locale.split('-')[0].toLowerCase();
                if (translations[lang]) return lang;
            }

            // 2. Fallback: Browser-Sprache
            const browserLang = navigator.language?.substring(0, 2) || 'en';
            if (translations[browserLang]) return browserLang;

            return 'en';
        } catch (e) {
            return 'en';
        }
    }

    function t(key, ...args) {
        let text = translations[currentLang]?.[key] || translations['en'][key] || key;
        args.forEach((arg, i) => {
            text = text.replace(`{${i}}`, arg);
        });
        return text;
    }

    function log(message, data = null) {
        if (DEBUG) {
            console.log(`[WME RTC Plus] ${message}`, data || '');
        }
    }

    function warn(message, data = null) {
        console.warn(`[WME RTC Plus] ${message}`, data || '');
    }

    // Befahrbare Segment-Typen (roadType Werte)
    // 1=Street, 2=Primary, 3=Freeway, 4=Ramp, 6=Major Highway, 7=Minor Highway, 21=Narrow Street
    const DRIVABLE_ROAD_TYPES = new Set([1, 2, 3, 4, 6, 7, 21]);

    function isDrivableSegment(segment) {
        const type = segment?.attributes?.roadType;
        return type && DRIVABLE_ROAD_TYPES.has(type);
    }

    // Settings Storage
    const STORAGE_KEY = 'wme-rtc-plus-settings';

    function loadSettings() {
        try {
            const saved = localStorage.getItem(STORAGE_KEY);
            return saved ? JSON.parse(saved) : {};
        } catch (e) { return {}; }
    }

    function saveSettings(settings) {
        try {
            const current = loadSettings();
            localStorage.setItem(STORAGE_KEY, JSON.stringify({ ...current, ...settings }));
        } catch (e) { warn('Settings save error:', e); }
    }

    function addEventListenerWithCleanup(element, event, handler) {
        if (!element) return;
        element.addEventListener(event, handler);
        eventListeners.push({ element, event, handler });
    }

    function cleanupEventListeners() {
        eventListeners.forEach(({ element, event, handler }) => {
            try {
                element.removeEventListener(event, handler);
            } catch (e) {
                warn('Error removing event listener:', e);
            }
        });
        eventListeners = [];
    }

    function initializeScript() {
        uWaze = window.W;
        uOpenLayers = window.OpenLayers;

        if (!uWaze || !uOpenLayers || !uWaze.map) {
            log('WME objects not ready, retrying...');
            setTimeout(initializeScript, 500);
            return;
        }

        currentLang = detectLanguage();
        log(`Initializing with language: ${currentLang}`);
        radiusInit();
    }

    function addSidePanel() {
        try {
            const { tabLabel, tabPane } = W.userscripts.registerSidebarTab("wme-rtc-plus");

            tabLabel.innerText = t('tabTitle');
            tabLabel.title = 'WME RTC Plus';

            setupSidebarContent(tabPane);
        } catch (error) {
            log('New API failed, using fallback:', error);
            addSidePanelFallback();
        }
    }

    function addSidePanelFallback() {
        let userTabs = document.getElementById('user-info');
        if (!userTabs) {
            setTimeout(addSidePanelFallback, 1000);
            return;
        }

        let navTabs = userTabs.getElementsByClassName('nav-tabs')[0];
        let tabContent = userTabs.getElementsByClassName('tab-content')[0];

        if (!navTabs || !tabContent) {
            setTimeout(addSidePanelFallback, 1000);
            return;
        }

        let tab = document.createElement('li');
        tab.innerHTML = `<a href="#sidepanel-wme-rtc-plus" data-toggle="tab">${t('tabTitle')}</a>`;
        navTabs.appendChild(tab);

        let tabPane = document.createElement('section');
        tabPane.id = "sidepanel-wme-rtc-plus";
        tabPane.className = "tab-pane";
        tabContent.appendChild(tabPane);

        setupSidebarContent(tabPane);
    }

    function setupSidebarContent(tabPane) {
        tabPane.innerHTML = `
            <div style="padding:6px;font-size:12px;">
                <div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:4px;">
                    <h4 style="margin:0;">${t('title')}</h4>
                    <span style="font-size:9px;color:#999;">v2026.01.03.24</span>
                </div>
                <div id="circles-current-radius" style="font-size:10px;color:#666;margin-bottom:4px;"></div>

                <div style="margin-bottom:6px;">
                    <label style="display:flex;align-items:center;cursor:pointer;margin-bottom:2px;font-size:11px;">
                        <input type="checkbox" id="wme-circles-edit-mode" style="margin-right:4px;">${t('enableCircle')}
                    </label>
                    <label style="display:flex;align-items:center;cursor:pointer;margin-bottom:2px;font-size:11px;">
                        <input type="checkbox" id="wme-freehand-edit-mode" style="margin-right:4px;">${t('enableFreehand')}
                    </label>
                    <label style="display:flex;align-items:center;cursor:pointer;margin-bottom:2px;font-size:11px;">
                        <input type="checkbox" id="wme-circles-move-mode" style="margin-right:4px;">${t('moveCircles')}
                    </label>
                    <label style="display:flex;align-items:center;cursor:pointer;font-size:11px;">
                        <input type="checkbox" id="wme-brush-mode" style="margin-right:4px;">🖌️ ${t('brushMode')}
                    </label>
                    <label style="display:none;align-items:center;cursor:pointer;font-size:11px;margin-left:18px;" id="brush-erase-label">
                        <input type="checkbox" id="wme-brush-erase" style="margin-right:4px;">🧹 ${t('brushErase')}
                    </label>
                    <div id="brush-mode-hint" style="display:none;font-size:9px;color:#666;margin-left:18px;">${t('brushModeHint')}</div>
                </div>

                <div style="padding:4px;background:#e3f2fd;border-radius:3px;margin-bottom:6px;">
                    <div style="display:flex;align-items:center;gap:4px;">
                        <span style="font-size:10px;">📐</span>
                        <span style="font-size:10px;">${t('presetRadius')}</span>
                        <input type="number" id="wme-preset-radius" min="5" max="5000" value="500" style="width:60px;padding:2px 4px;border:1px solid #ccc;border-radius:3px;font-size:11px;">
                        <span style="font-size:11px;">m</span>
                        <button id="wme-preset-circle-btn" style="flex:1;padding:4px;background:#2196F3;color:white;border:none;border-radius:3px;cursor:pointer;font-size:11px;">📍</button>
                    </div>
                    <button id="wme-preset-cancel-btn" style="width:100%;padding:4px;background:#f44336;color:white;border:none;border-radius:3px;cursor:pointer;margin-top:4px;display:none;font-size:11px;">❌</button>
                </div>

                <div style="display:grid;grid-template-columns:1fr 1fr;gap:3px;margin-bottom:6px;">
                    <button id="wme-circles-select" style="padding:4px;background:#4CAF50;color:white;border:none;border-radius:3px;cursor:pointer;font-size:10px;">${t('selectStreets')}</button>
                    <button id="wme-circles-undo" style="padding:4px;background:#2196F3;color:white;border:none;border-radius:3px;cursor:pointer;font-size:10px;">↩️ ${t('undoLastCircle')}</button>
                    <button id="wme-circles-clear" style="padding:4px;background:#f44336;color:white;border:none;border-radius:3px;cursor:pointer;font-size:10px;">🗑️ ${t('clearCircles')}</button>
                    <button id="wme-circles-delete-rtcs" style="padding:4px;background:#ff5722;color:white;border:none;border-radius:3px;cursor:pointer;font-size:10px;">🗑️ RTCs</button>
                </div>

                <details style="margin-bottom:6px;">
                    <summary style="cursor:pointer;padding:3px;background:#f5f5f5;border-radius:3px;font-size:10px;"><strong>${t('displayOptions')}</strong></summary>
                    <div style="padding:4px;background:#f5f5f5;border-radius:0 0 3px 3px;">
                        <div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:4px;">
                            <span style="font-size:10px;">${t('showRadius')}</span>
                            <label class="ts"><input type="checkbox" id="show-radius"><span class="sl"></span></label>
                        </div>
                        <div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:4px;">
                            <span style="font-size:10px;">${t('showDiameter')}</span>
                            <label class="ts"><input type="checkbox" id="show-diameter"><span class="sl"></span></label>
                        </div>
                        <div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:4px;">
                            <span style="font-size:10px;">${t('showArea')}</span>
                            <label class="ts"><input type="checkbox" id="show-area"><span class="sl"></span></label>
                        </div>
                        <div style="display:flex;justify-content:space-between;align-items:center;">
                            <span style="font-size:10px;">${t('imperialUnits')}</span>
                            <label class="ts"><input type="checkbox" id="use-imperial"><span class="sl"></span></label>
                        </div>
                    </div>
                </details>

                <div style="display:flex;justify-content:space-between;align-items:center;padding:4px;background:#f5f5f5;border-radius:3px;margin-bottom:6px;">
                    <span style="font-size:10px;">${t('onlyDrivable')}</span>
                    <label class="ts"><input type="checkbox" id="filter-drivable-only"><span class="sl"></span></label>
                </div>

                <details style="margin-bottom:6px;">
                    <summary style="cursor:pointer;padding:3px;background:#e8f5e8;border-radius:3px;font-size:10px;"><strong>${t('perimeterBlocking')}</strong></summary>
                    <div style="padding:4px;background:#e8f5e8;border-radius:0 0 3px 3px;">
                        <div style="display:flex;justify-content:space-between;align-items:center;">
                            <div>
                                <div style="font-size:10px;">${t('blockInbound')}</div>
                                <div style="font-size:8px;color:#666;">${t('blockInboundHint')}</div>
                            </div>
                            <label class="ts"><input type="checkbox" id="block-inbound-only"><span class="sl"></span></label>
                        </div>
                        <div id="rtc-duration-settings" style="margin-top:6px;padding-top:6px;border-top:1px solid #c8e6c9;display:none;">
                            <div style="margin-bottom:4px;">
                                <label style="font-size:9px;margin-right:6px;"><input type="radio" name="rtc-mode" id="rtc-mode-duration" checked> ${t('rtcModeDuration')}</label>
                                <label style="font-size:9px;"><input type="radio" name="rtc-mode" id="rtc-mode-enddate"> ${t('rtcModeEndDate')}</label>
                            </div>
                            <div id="rtc-duration-inputs" style="display:flex;align-items:center;gap:3px;flex-wrap:wrap;">
                                <input type="number" id="rtc-duration-days" min="0" max="365" value="0" style="width:35px;padding:2px;border:1px solid #ccc;border-radius:3px;font-size:9px;">
                                <span style="font-size:9px;">${t('days')}</span>
                                <input type="number" id="rtc-duration-hours" min="0" max="23" value="24" style="width:35px;padding:2px;border:1px solid #ccc;border-radius:3px;font-size:9px;">
                                <span style="font-size:9px;">${t('rtcHours')}</span>
                                <input type="number" id="rtc-duration-minutes" min="0" max="59" value="0" style="width:35px;padding:2px;border:1px solid #ccc;border-radius:3px;font-size:9px;">
                                <span style="font-size:9px;">${t('rtcMinutes')}</span>
                            </div>
                            <div id="rtc-enddate-inputs" style="display:none;">
                                <div style="display:flex;align-items:center;gap:3px;">
                                    <input type="date" id="rtc-end-date" style="padding:2px;border:1px solid #ccc;border-radius:3px;font-size:9px;flex:1;">
                                    <input type="time" id="rtc-end-time" value="23:59" style="width:60px;padding:2px;border:1px solid #ccc;border-radius:3px;font-size:9px;">
                                </div>
                            </div>
                        </div>
                    </div>
                </details>

                <details style="margin-bottom:6px;">
                    <summary style="cursor:pointer;padding:3px;background:#fff3e0;border-radius:3px;font-size:10px;"><strong>💬 ${t('createMapComment')}</strong></summary>
                    <div style="padding:4px;background:#fff3e0;border-radius:0 0 3px 3px;">
                        <input type="text" id="wme-mc-title" maxlength="30" placeholder="${t('commentTitle')}" style="width:100%;padding:3px;border:1px solid #ccc;border-radius:3px;box-sizing:border-box;font-size:10px;margin-bottom:3px;">
                        <textarea id="wme-mc-desc" rows="1" placeholder="${t('commentDesc')}" style="width:100%;padding:3px;border:1px solid #ccc;border-radius:3px;box-sizing:border-box;resize:vertical;font-size:10px;margin-bottom:3px;"></textarea>
                        <div style="display:flex;align-items:center;gap:3px;margin-bottom:4px;">
                            <span style="font-size:9px;">${t('expiresIn')}</span>
                            <input type="number" id="wme-mc-days" min="1" max="365" value="30" style="width:40px;padding:2px;border:1px solid #ccc;border-radius:3px;font-size:9px;">
                            <span style="font-size:9px;">${t('days')}</span>
                        </div>
                        <button id="wme-create-mapcomment-btn" style="width:100%;padding:4px;background:#ff9800;color:white;border:none;border-radius:3px;cursor:pointer;font-size:10px;">💬 ${t('createFromCircle')}</button>
                    </div>
                </details>

                <div style="padding:4px;background:#e8eaf6;border-radius:3px;">
                    <div style="display:flex;align-items:center;gap:6px;margin-bottom:4px;flex-wrap:wrap;">
                        <label style="font-size:9px;"><input type="radio" name="export-type" id="export-type-rtcs" checked> ${t('exportTypeRtcs')}</label>
                        <label style="font-size:9px;"><input type="radio" name="export-type" id="export-type-circles"> ${t('exportTypeCircles')}</label>
                        <label style="font-size:9px;"><input type="radio" name="export-type" id="export-type-gpx"> ${t('exportTypeGpx')}</label>
                    </div>
                    <div style="display:flex;gap:3px;">
                        <button id="wme-export-rtcs-btn" style="flex:1;padding:3px;background:#3f51b5;color:white;border:none;border-radius:3px;cursor:pointer;font-size:10px;">Export</button>
                        <button id="wme-import-rtcs-btn" style="flex:1;padding:3px;background:#3f51b5;color:white;border:none;border-radius:3px;cursor:pointer;font-size:10px;">Import</button>
                    </div>
                    <input type="file" id="wme-import-rtcs-file" accept=".json,.gpx" style="display:none;">
                </div>

                <style>
                    .ts{position:relative;display:inline-block;width:32px;height:16px;}
                    .ts input{opacity:0;width:0;height:0;}
                    .sl{position:absolute;cursor:pointer;top:0;left:0;right:0;bottom:0;background:#ccc;transition:.2s;border-radius:16px;}
                    .sl:before{position:absolute;content:"";height:12px;width:12px;left:2px;bottom:2px;background:white;transition:.2s;border-radius:50%;}
                    input:checked+.sl{background:#4CAF50;}
                    input:checked+.sl:before{transform:translateX(16px);}
                    details summary{list-style:none;}
                    details summary::-webkit-details-marker{display:none;}
                    details summary::before{content:'▶';font-size:8px;margin-right:4px;display:inline-block;transition:transform .2s;}
                    details[open] summary::before{transform:rotate(90deg);}
                </style>
            </div>
        `;

        const checkbox = tabPane.querySelector('#wme-circles-edit-mode');
        const freehandCheckbox = tabPane.querySelector('#wme-freehand-edit-mode');
        const moveCheckbox = tabPane.querySelector('#wme-circles-move-mode');
        const clearButton = tabPane.querySelector('#wme-circles-clear');
        const selectButton = tabPane.querySelector('#wme-circles-select');
        const undoButton = tabPane.querySelector('#wme-circles-undo');
        const showRadiusCheckbox = tabPane.querySelector('#show-radius');
        const showDiameterCheckbox = tabPane.querySelector('#show-diameter');
        const showAreaCheckbox = tabPane.querySelector('#show-area');
        const useImperialCheckbox = tabPane.querySelector('#use-imperial');
        const filterDrivableCheckbox = tabPane.querySelector('#filter-drivable-only');
        const blockInboundCheckbox = tabPane.querySelector('#block-inbound-only');
        const presetRadiusInput = tabPane.querySelector('#wme-preset-radius');
        const presetCircleBtn = tabPane.querySelector('#wme-preset-circle-btn');
        const presetCancelBtn = tabPane.querySelector('#wme-preset-cancel-btn');
        const mapCommentBtn = tabPane.querySelector('#wme-create-mapcomment-btn');
        const mcTitleInput = tabPane.querySelector('#wme-mc-title');
        const mcDescInput = tabPane.querySelector('#wme-mc-desc');
        const mcDaysInput = tabPane.querySelector('#wme-mc-days');

        // Details elements für Klappmenüs
        const detailsDisplay = tabPane.querySelector('details:nth-of-type(1)');
        const detailsPerimeter = tabPane.querySelector('details:nth-of-type(2)');
        const detailsMapComment = tabPane.querySelector('details:nth-of-type(3)');

        // Einstellungen laden und anwenden
        const settings = loadSettings();
        if (settings.showRadius) showRadiusCheckbox.checked = true;
        if (settings.showDiameter) showDiameterCheckbox.checked = true;
        if (settings.showArea) showAreaCheckbox.checked = true;
        if (settings.useImperial) useImperialCheckbox.checked = true;
        if (settings.filterDrivable) filterDrivableCheckbox.checked = true;
        if (settings.blockInbound) blockInboundCheckbox.checked = true;
        if (settings.presetRadius) presetRadiusInput.value = settings.presetRadius;
        if (settings.detailsDisplayOpen) detailsDisplay?.setAttribute('open', '');
        if (settings.detailsPerimeterOpen) detailsPerimeter?.setAttribute('open', '');
        if (settings.detailsMapCommentOpen) detailsMapComment?.setAttribute('open', '');
        if (settings.rtcDays) tabPane.querySelector('#rtc-duration-days').value = settings.rtcDays;
        if (settings.rtcHours) tabPane.querySelector('#rtc-duration-hours').value = settings.rtcHours;
        if (settings.rtcMinutes) tabPane.querySelector('#rtc-duration-minutes').value = settings.rtcMinutes;

        // Einstellungen bei Änderung speichern
        const saveAllSettings = () => {
            saveSettings({
                showRadius: showRadiusCheckbox.checked,
                showDiameter: showDiameterCheckbox.checked,
                showArea: showAreaCheckbox.checked,
                useImperial: useImperialCheckbox.checked,
                filterDrivable: filterDrivableCheckbox.checked,
                blockInbound: blockInboundCheckbox.checked,
                presetRadius: presetRadiusInput.value,
                detailsDisplayOpen: detailsDisplay?.hasAttribute('open'),
                detailsPerimeterOpen: detailsPerimeter?.hasAttribute('open'),
                detailsMapCommentOpen: detailsMapComment?.hasAttribute('open'),
                rtcDays: tabPane.querySelector('#rtc-duration-days')?.value,
                rtcHours: tabPane.querySelector('#rtc-duration-hours')?.value,
                rtcMinutes: tabPane.querySelector('#rtc-duration-minutes')?.value
            });
        };

        // Event Listener für Speicherung
        [showRadiusCheckbox, showDiameterCheckbox, showAreaCheckbox, useImperialCheckbox,
         filterDrivableCheckbox, blockInboundCheckbox].forEach(el => {
            el?.addEventListener('change', saveAllSettings);
        });
        presetRadiusInput?.addEventListener('change', saveAllSettings);
        detailsDisplay?.addEventListener('toggle', saveAllSettings);
        detailsPerimeter?.addEventListener('toggle', saveAllSettings);
        detailsMapComment?.addEventListener('toggle', saveAllSettings);
        tabPane.querySelector('#rtc-duration-days')?.addEventListener('change', saveAllSettings);
        tabPane.querySelector('#rtc-duration-hours')?.addEventListener('change', saveAllSettings);
        tabPane.querySelector('#rtc-duration-minutes')?.addEventListener('change', saveAllSettings);

        function formatLength(meters, useImperial = false) {
            if (useImperial) {
                let feet = meters * 3.28084;
                if (feet >= 5280) {
                    let miles = feet / 5280;
                    return `${Math.round(miles * 100) / 100} mi`;
                } else {
                    return `${Math.round(feet)} ft`;
                }
            } else {
                if (meters > 1000) {
                    return `${Math.round((meters / 1000) * 100) / 100} km`;
                } else {
                    return `${Math.round(meters)} m`;
                }
            }
        }

        function formatArea(squareMeters, useImperial = false) {
            if (useImperial) {
                let squareFeet = squareMeters * 10.7639;
                if (squareFeet >= 27878400) {
                    let squareMiles = squareFeet / 27878400;
                    return `${Math.round(squareMiles * 100) / 100} mi²`;
                } else if (squareFeet >= 43560) {
                    let acres = squareFeet / 43560;
                    return `${Math.round(acres * 100) / 100} ac`;
                } else {
                    return `${Math.round(squareFeet)} ft²`;
                }
            } else {
                if (squareMeters > 1000000) {
                    return `${Math.round((squareMeters / 1000000) * 100) / 100} km²`;
                } else {
                    return `${Math.round(squareMeters)} m²`;
                }
            }
        }

        let updateTimeout;
        function updateAllAnnotations() {
            clearTimeout(updateTimeout);
            updateTimeout = setTimeout(() => {
                if (!infoLayer || !radiusLayer) return;

                try {
                    infoLayer.destroyFeatures();

                    if (!radiusLayer.features || radiusLayer.features.length === 0) {
                        return;
                    }

                    radiusLayer.features.forEach(feature => {
                        if (!feature || !feature.geometry) return;

                        if (feature.geometry.CLASS_NAME === "OpenLayers.Geometry.Polygon") {
                            const components = feature.geometry.components?.[0]?.components;
                            if (components && components.length > 90) {
                                addCircleAnnotations(feature);
                            } else if (components) {
                                addFreehandAnnotations(feature);
                            }
                        }
                    });
                } catch (error) {
                    warn('Error updating annotations:', error);
                }
            }, 100);
        }

        function addCircleAnnotations(f) {
            try {
                let minX = f.geometry.bounds.left;
                let minY = f.geometry.bounds.bottom;
                let maxX = f.geometry.bounds.right;
                let maxY = f.geometry.bounds.top;

                let startX = (minX + maxX) / 2;
                let startY = (minY + maxY) / 2;

                let startPoint = new uOpenLayers.Geometry.Point(startX, startY);
                let endPoint = new uOpenLayers.Geometry.Point(maxX, startY);
                let radius = new uOpenLayers.Geometry.LineString([startPoint, endPoint]);
                let len = radius.getGeodesicLength(new uOpenLayers.Projection("EPSG:900913"));

                let area = Math.PI * len * len;

                let showRadius = showRadiusCheckbox?.checked;
                let showDiameter = showDiameterCheckbox?.checked;
                let showArea = showAreaCheckbox?.checked;
                let useImperial = useImperialCheckbox?.checked;

                let centerStyle = {
                    strokeColor: "#c40606",
                    strokeWidth: 2,
                    pointRadius: 5,
                    fillOpacity: 0.2
                };

                let labelText = [];

                if (showRadius) {
                    labelText.push('R: ' + formatLength(len, useImperial));
                }

                if (showDiameter) {
                    labelText.push('Ø: ' + formatLength(len * 2, useImperial));
                }

                if (showArea) {
                    labelText.push('A: ' + formatArea(area, useImperial));
                }

                let lineStyle = {
                    strokeColor: "#c40606",
                    strokeWidth: 3,
                    label: labelText.join(' | '),
                    labelAlign: "left",
                    labelXOffset: "20",
                    labelYOffset: "10",
                    labelOutlineColor: "white",
                    labelOutlineWidth: 3
                };

                let center = new uOpenLayers.Feature.Vector(startPoint, {}, centerStyle);
                if (labelText.length > 0) {
                    let radiusLine = new uOpenLayers.Feature.Vector(radius, { 'length': len }, lineStyle);
                    infoLayer.addFeatures([center, radiusLine]);
                } else {
                    infoLayer.addFeatures([center]);
                }
            } catch (error) {
                warn('Error adding circle annotations:', error);
            }
        }

        function addFreehandAnnotations(f) {
            try {
                let bounds = f.geometry.getBounds();
                let minX = bounds.left;
                let minY = bounds.bottom;
                let maxX = bounds.right;
                let maxY = bounds.top;

                let centerX = (minX + maxX) / 2;
                let centerY = (minY + maxY) / 2;
                let width = maxX - minX;
                let height = maxY - minY;
                let diameter = Math.max(width, height);

                let centerPoint = new uOpenLayers.Geometry.Point(centerX, centerY);
                let endPoint = new uOpenLayers.Geometry.Point(centerX + diameter/2, centerY);
                let diameterLine = new uOpenLayers.Geometry.LineString([centerPoint, endPoint]);
                let len = diameterLine.getGeodesicLength(new uOpenLayers.Projection("EPSG:900913"));

                let area = f.geometry.getArea();

                let showRadius = showRadiusCheckbox?.checked;
                let showDiameter = showDiameterCheckbox?.checked;
                let showArea = showAreaCheckbox?.checked;
                let useImperial = useImperialCheckbox?.checked;

                let centerStyle = {
                    strokeColor: "#c40606",
                    strokeWidth: 2,
                    pointRadius: 5,
                    fillOpacity: 0.2
                };

                let labelText = [];

                if (showRadius) {
                    labelText.push('R: ' + formatLength(len / 2, useImperial));
                }

                if (showDiameter) {
                    labelText.push('Ø: ' + formatLength(len, useImperial));
                }

                if (showArea) {
                    labelText.push('A: ' + formatArea(area, useImperial));
                }

                let lineStyle = {
                    strokeColor: "#c40606",
                    strokeWidth: 3,
                    label: labelText.join(' | '),
                    labelAlign: "left",
                    labelXOffset: "20",
                    labelYOffset: "10",
                    labelOutlineColor: "white",
                    labelOutlineWidth: 3
                };

                let center = new uOpenLayers.Feature.Vector(centerPoint, {}, centerStyle);
                if (labelText.length > 0) {
                    let diameterLineFeature = new uOpenLayers.Feature.Vector(diameterLine, { 'diameter': len }, lineStyle);
                    infoLayer.addFeatures([center, diameterLineFeature]);
                } else {
                    infoLayer.addFeatures([center]);
                }
            } catch (error) {
                warn('Error adding freehand annotations:', error);
            }
        }

        // Register global event listener for annotation updates (used by drag control)
        document.addEventListener('updateAnnotations', updateAllAnnotations);

        addEventListenerWithCleanup(showRadiusCheckbox, 'change', updateAllAnnotations);
        addEventListenerWithCleanup(showDiameterCheckbox, 'change', updateAllAnnotations);
        addEventListenerWithCleanup(showAreaCheckbox, 'change', updateAllAnnotations);
        addEventListenerWithCleanup(useImperialCheckbox, 'change', updateAllAnnotations);

        // Toggle RTC duration settings visibility
        const rtcDurationSettings = tabPane.querySelector('#rtc-duration-settings');
        const rtcDurationInputs = tabPane.querySelector('#rtc-duration-inputs');
        const rtcEnddateInputs = tabPane.querySelector('#rtc-enddate-inputs');
        const rtcModeDuration = tabPane.querySelector('#rtc-mode-duration');
        const rtcModeEnddate = tabPane.querySelector('#rtc-mode-enddate');
        const rtcEndDateInput = tabPane.querySelector('#rtc-end-date');

        // Initial state based on saved settings
        if (blockInboundCheckbox.checked && rtcDurationSettings) {
            rtcDurationSettings.style.display = 'block';
        }

        addEventListenerWithCleanup(blockInboundCheckbox, 'change', () => {
            if (rtcDurationSettings) {
                rtcDurationSettings.style.display = blockInboundCheckbox.checked ? 'block' : 'none';
                // Set default end date to tomorrow and min to today
                if (blockInboundCheckbox.checked && rtcEndDateInput) {
                    const today = new Date();
                    const tomorrow = new Date();
                    tomorrow.setDate(tomorrow.getDate() + 1);
                    rtcEndDateInput.min = today.toISOString().split('T')[0];
                    rtcEndDateInput.value = tomorrow.toISOString().split('T')[0];
                }
            }
        });

        // Toggle between duration and end date mode
        const updateRtcMode = () => {
            if (rtcModeDuration?.checked) {
                rtcDurationInputs.style.display = 'flex';
                rtcEnddateInputs.style.display = 'none';
            } else {
                rtcDurationInputs.style.display = 'none';
                rtcEnddateInputs.style.display = 'block';
            }
        };
        addEventListenerWithCleanup(rtcModeDuration, 'change', updateRtcMode);
        addEventListenerWithCleanup(rtcModeEnddate, 'change', updateRtcMode);

        addEventListenerWithCleanup(checkbox, 'click', (e) => {
            if (e.target.checked) {
                freehandCheckbox.checked = false;
                moveCheckbox.checked = false;
                freehandControl.deactivate();
                dragControl.deactivate();
                polygonControl.activate();
            } else {
                polygonControl.deactivate();
            }
        });

        addEventListenerWithCleanup(freehandCheckbox, 'click', (e) => {
            if (e.target.checked) {
                checkbox.checked = false;
                moveCheckbox.checked = false;
                polygonControl.deactivate();
                dragControl.deactivate();
                freehandControl.activate();
            } else {
                freehandControl.deactivate();
            }
        });

        // DragFeature Control - recreated on demand
        function createDragControl() {
            log('Creating new DragFeature control, layer has ' + (radiusLayer.features?.length || 0) + ' features');
            const ctrl = new uOpenLayers.Control.DragFeature(radiusLayer, {
                onStart: function(feature, pixel) {
                    log('Drag started');
                },
                onDrag: function(feature, pixel) {
                    if (!this._lastUpdate || Date.now() - this._lastUpdate > 100) {
                        this._lastUpdate = Date.now();
                        document.dispatchEvent(new CustomEvent('updateAnnotations'));
                    }
                },
                onComplete: function(feature, pixel) {
                    log('Drag completed');
                    document.dispatchEvent(new CustomEvent('updateAnnotations'));
                }
            });
            return ctrl;
        }

        dragControl = createDragControl();
        uWaze.map.addControl(dragControl);

        function activateDragMode() {
            log('activateDragMode called');

            // Remove old control completely
            if (dragControl) {
                try {
                    dragControl.deactivate();
                    uWaze.map.removeControl(dragControl);
                } catch(e) {}
            }

            // Create fresh control and add it
            dragControl = createDragControl();
            uWaze.map.addControl(dragControl);

            // Activate it
            dragControl.activate();

            log('Drag mode activated, control.active=' + dragControl.active);
        }

        addEventListenerWithCleanup(moveCheckbox, 'click', (e) => {
            if (e.target.checked) {
                checkbox.checked = false;
                freehandCheckbox.checked = false;
                polygonControl.deactivate();
                freehandControl.deactivate();
                activateDragMode();
                updateStatusMessage(t('moveHint'), '#2196F3');
            } else {
                if (dragControl) {
                    dragControl.deactivate();
                    log('Drag mode deactivated');
                }
                updateStatusMessage('', '#4CAF50');
            }
        });

        // Track pending reactivation
        let reactivateTimer = null;

        function scheduleReactivation() {
            if (!moveCheckbox || !moveCheckbox.checked) return;

            // Clear any pending timer
            if (reactivateTimer) clearTimeout(reactivateTimer);

            // Wait for WME and other scripts to finish, then reactivate
            reactivateTimer = setTimeout(() => {
                if (moveCheckbox && moveCheckbox.checked) {
                    activateDragMode();
                }
            }, 2000); // Wait 2 seconds for everything to settle
        }

        // On zoom: schedule reactivation after everything finishes
        uWaze.map.events.register('zoomend', null, function() {
            log('zoomend event fired');

            if (radiusLayer && radiusLayer.redraw) radiusLayer.redraw();
            if (infoLayer && infoLayer.redraw) infoLayer.redraw();

            scheduleReactivation();

            document.dispatchEvent(new CustomEvent('updateAnnotations'));
        });

        // Also reactivate after map move completes
        uWaze.map.events.register('moveend', null, function() {
            if (moveCheckbox && moveCheckbox.checked) {
                scheduleReactivation();
            }
        });

        addEventListenerWithCleanup(clearButton, 'click', (e) => {
            clearAllCircles();
            updateStatusMessage(t('circlesCleared'), '#4CAF50');
        });

        addEventListenerWithCleanup(undoButton, 'click', (e) => {
            undoLastCircle();
        });

        addEventListenerWithCleanup(selectButton, 'click', (e) => {
            selectSegmentsInCircles();
        });

        // Delete RTCs Button
        const deleteRtcsBtn = tabPane.querySelector('#wme-circles-delete-rtcs');
        addEventListenerWithCleanup(deleteRtcsBtn, 'click', (e) => {
            deleteRtcsInCircles();
        });

        // Brush Mode - basiert auf Freihand-Zeichnung mit breiter Linie
        const brushCheckbox = tabPane.querySelector('#wme-brush-mode');
        const brushHint = tabPane.querySelector('#brush-mode-hint');
        let brushModeActive = false;
        let brushControl = null;
        let brushLayer = null;
        let brushSelectedSegments = new Set(); // Speichert alle ausgewählten Segment-IDs
        let brushHistory = []; // History für Undo - speichert Snapshots der Auswahl

        // Erstelle separaten Layer für Brush mit breiter Linie
        brushLayer = new uOpenLayers.Layer.Vector("WME Brush Layer", {
            displayInLayerSwitcher: false,
            visibility: true,
            style: {
                strokeColor: "#2196F3",
                strokeOpacity: 0.6,
                strokeWidth: 20, // 20 Pixel breit
                strokeLinecap: "round"
            }
        });
        uWaze.map.addLayer(brushLayer);

        // Brush Control - wie Freihand aber für Selektion
        brushControl = new uOpenLayers.Control.DrawFeature(brushLayer, uOpenLayers.Handler.Path, {
            handlerOptions: {
                freehand: true,
                freehandToggle: null
            }
        });
        uWaze.map.addControl(brushControl);

        // Wenn Brush-Zeichnung fertig ist, finde geschnittene Segmente
        brushControl.events.on({
            'featureadded': function(e) {
                if (!brushModeActive) return;

                const brushGeom = e.feature.geometry;
                if (!brushGeom) return;

                // Prüfe ob Löschen-Modus aktiv
                const brushEraseCheckbox = tabPane.querySelector('#wme-brush-erase');
                const eraseMode = brushEraseCheckbox?.checked || false;

                log(`Brush Event: eraseMode=${eraseMode}, brushSelectedSegments.size=${brushSelectedSegments.size}`);

                // Speichere aktuellen Zustand für Undo BEVOR Änderungen
                brushHistory.push(new Set(brushSelectedSegments));
                if (brushHistory.length > 50) brushHistory.shift(); // Max 50 Schritte

                // Erstelle Buffer um die Linie
                const resolution = uWaze.map.getResolution();
                const bufferSize = eraseMode ? 10 * resolution : 20 * resolution;

                // Finde alle Segmente die die Brush-Linie schneiden
                const segments = uWaze.model.segments.getObjectArray();
                let changeCount = 0;
                let checkedCount = 0;

                for (const segment of segments) {
                    if (!segment.attributes?.roadType) continue;

                    // Filter: nur befahrbare
                    if (filterDrivableCheckbox?.checked) {
                        if (!isDrivableSegment(segment)) continue;
                    }

                    const segGeom = segment.getOLGeometry?.() || segment.geometry;
                    if (!segGeom) continue;

                    checkedCount++;

                    // Prüfe ob Segment die Brush-Linie schneidet oder nah dran ist
                    const distance = brushGeom.distanceTo(segGeom);

                    let threshold;
                    if (eraseMode) {
                        threshold = Math.min(5 * resolution, 3);
                    } else {
                        threshold = bufferSize;
                    }

                    if (distance < threshold) {
                        const segId = segment.getID();

                        if (eraseMode) {
                            // LÖSCHEN-MODUS: Entferne NUR dieses eine Segment
                            if (brushSelectedSegments.has(segId)) {
                                brushSelectedSegments.delete(segId);
                                if (window._gpxSegmentIds) {
                                    window._gpxSegmentIds.delete(segId);
                                }
                                changeCount++;
                                log(`Brush Erase: Segment ${segId} entfernt`);
                            }
                        } else {
                            // NORMAL-MODUS: Füge Segment zur Auswahl hinzu
                            if (!brushSelectedSegments.has(segId)) {
                                brushSelectedSegments.add(segId);
                                changeCount++;
                            }
                        }
                    }
                }

                log(`Brush: ${checkedCount} Segmente geprüft, ${changeCount} geändert, ${brushSelectedSegments.size} IDs gesamt`);

                // Lösche die Brush-Linie
                brushLayer.removeAllFeatures();

                // Aktualisiere WME Selektion
                const allSegments = Array.from(brushSelectedSegments)
                    .map(id => uWaze.model.segments.getObjectById(id))
                    .filter(Boolean);

                log(`Brush: ${allSegments.length} Segmente geladen von ${brushSelectedSegments.size} IDs`);

                if (allSegments.length > 0) {
                    uWaze.selectionManager.setSelectedModels(allSegments);
                    if (eraseMode) {
                        const notLoaded = brushSelectedSegments.size - allSegments.length;
                        const notLoadedText = notLoaded > 0 ? ` (+${notLoaded} nicht geladen)` : '';
                        updateStatusMessage(`🧹 ${allSegments.length} Segmente (-${changeCount} entfernt)${notLoadedText}`, '#ff9800');
                    } else {
                        updateStatusMessage(`🖌️ ${allSegments.length} Segmente (+${changeCount} neu)`, '#2196F3');
                    }
                } else if (brushSelectedSegments.size > 0) {
                    // IDs vorhanden aber nicht geladen - NICHT deselektieren!
                    if (eraseMode) {
                        updateStatusMessage(`🧹 ${changeCount} entfernt (${brushSelectedSegments.size} IDs gespeichert)`, '#ff9800');
                    } else {
                        updateStatusMessage(`🖌️ ${brushSelectedSegments.size} Segmente (nicht im Viewport)`, '#2196F3');
                    }
                } else {
                    uWaze.selectionManager.unselectAll();
                    window._gpxSegmentIds = null;
                    updateStatusMessage(`🧹 Alle Segmente entfernt`, '#ff9800');
                }
            }
        });

        // Brush Undo Funktion
        function undoBrushSelection() {
            if (brushHistory.length === 0) {
                return false; // Keine History
            }

            // Letzten Zustand wiederherstellen
            brushSelectedSegments = brushHistory.pop();

            // WME Selektion aktualisieren
            const allSegments = Array.from(brushSelectedSegments)
                .map(id => uWaze.model.segments.getObjectById(id))
                .filter(Boolean);

            if (allSegments.length > 0) {
                uWaze.selectionManager.setSelectedModels(allSegments);
            } else {
                uWaze.selectionManager.unselectAll();
            }

            updateStatusMessage(`↩️ ${allSegments.length} Segmente`, '#2196F3');
            return true;
        }
        addEventListenerWithCleanup(brushCheckbox, 'change', (e) => {
            brushModeActive = e.target.checked;
            brushHint.style.display = brushModeActive ? 'block' : 'none';

            // Zeige/verstecke Löschen-Checkbox
            const brushEraseLabel = tabPane.querySelector('#brush-erase-label');
            if (brushEraseLabel) {
                brushEraseLabel.style.display = brushModeActive ? 'flex' : 'none';
            }

            if (brushModeActive) {
                // Deaktiviere andere Modi
                checkbox.checked = false;
                freehandCheckbox.checked = false;
                moveCheckbox.checked = false;
                polygonControl.deactivate();
                freehandControl.deactivate();
                if (dragControl) dragControl.deactivate();

                // WICHTIG: Übernehme Segment-IDs
                brushSelectedSegments.clear();

                // 1. Zuerst: Global gespeicherte GPX-IDs übernehmen (falls vorhanden)
                if (window._gpxSegmentIds && window._gpxSegmentIds.size > 0) {
                    for (const id of window._gpxSegmentIds) {
                        brushSelectedSegments.add(id);
                    }
                    log(`Brush: ${brushSelectedSegments.size} GPX-Segment-IDs übernommen`);
                }

                // 2. Dann: Aktuelle WME-Selektion hinzufügen (falls zusätzliche Segmente)
                try {
                    const currentSelection = uWaze.selectionManager.getSelectedFeatures() || [];
                    let addedFromSelection = 0;
                    for (const feature of currentSelection) {
                        if (feature?.model?.type === 'segment') {
                            const segId = feature.model.attributes?.id || feature.model.getID?.();
                            if (segId && !brushSelectedSegments.has(segId)) {
                                brushSelectedSegments.add(segId);
                                addedFromSelection++;
                            }
                        }
                    }
                    if (addedFromSelection > 0) {
                        log(`Brush: +${addedFromSelection} Segmente aus aktueller Auswahl`);
                    }
                } catch (e) {
                    // Fallback: Keine Übernahme
                }

                // Aktiviere Brush
                brushControl.activate();
                updateStatusMessage(t('brushModeActive') + (brushSelectedSegments.size > 0 ? ` (${brushSelectedSegments.size})` : ''), '#2196F3');
                log('Brush-Modus aktiviert');
            } else {
                brushControl.deactivate();
                brushLayer.removeAllFeatures();
                // Löschen-Checkbox zurücksetzen
                const brushEraseCheckbox = tabPane.querySelector('#wme-brush-erase');
                if (brushEraseCheckbox) brushEraseCheckbox.checked = false;
                // Auswahl bleibt erhalten beim Deaktivieren
                updateStatusMessage('', '#4CAF50');
                log('Brush-Modus deaktiviert');
            }
        });

        // Import/Export Buttons
        const exportBtn = tabPane.querySelector('#wme-export-rtcs-btn');
        const importBtn = tabPane.querySelector('#wme-import-rtcs-btn');
        const importFile = tabPane.querySelector('#wme-import-rtcs-file');
        const exportTypeRtcs = tabPane.querySelector('#export-type-rtcs');
        const exportTypeCircles = tabPane.querySelector('#export-type-circles');
        const exportTypeGpx = tabPane.querySelector('#export-type-gpx');

        addEventListenerWithCleanup(exportBtn, 'click', (e) => {
            if (exportTypeGpx?.checked) {
                exportSelectedAsGpx();
            } else if (exportTypeCircles?.checked) {
                exportCirclesInArea();
            } else {
                exportRtcsInArea();
            }
        });

        addEventListenerWithCleanup(importBtn, 'click', (e) => {
            importFile.click();
        });

        addEventListenerWithCleanup(importFile, 'change', (e) => {
            if (e.target.files.length > 0) {
                const file = e.target.files[0];
                const fileName = file.name.toLowerCase();

                // GPX-Datei erkennen (unabhängig von Radio-Button)
                if (fileName.endsWith('.gpx')) {
                    importGpxFile(file);
                } else if (exportTypeCircles?.checked) {
                    importCirclesFromFile(file);
                } else {
                    importRtcsFromFile(file);
                }
                e.target.value = ''; // Reset für erneuten Import
            }
        });

        // Preset Circle Event Handlers
        addEventListenerWithCleanup(presetCircleBtn, 'click', (e) => {
            startPresetCircleMode();
        });

        addEventListenerWithCleanup(presetCancelBtn, 'click', (e) => {
            cancelPresetCircleMode();
        });

        // Map Comment Event Handler
        addEventListenerWithCleanup(mapCommentBtn, 'click', (e) => {
            createMapCommentFromCircle();
        });

        function createMapCommentFromCircle() {
            try {
                if (!radiusLayer || !radiusLayer.features || radiusLayer.features.length === 0) {
                    updateStatusMessage(t('selectCircleFirst'), '#ff9800');
                    return;
                }

                const lastFeature = radiusLayer.features[radiusLayer.features.length - 1];
                if (!lastFeature || !lastFeature.geometry) {
                    updateStatusMessage(t('noCircleToConvert'), '#ff9800');
                    return;
                }

                // Get SDK
                let sdk = null;
                try {
                    if (typeof getWmeSdk === 'function') {
                        sdk = getWmeSdk({ scriptId: 'wme-circles', scriptName: 'WME Circles' });
                    }
                } catch (e) {
                    warn('SDK not available:', e);
                }

                if (!sdk || !sdk.DataModel || !sdk.DataModel.MapComments) {
                    updateStatusMessage('SDK not available', '#f44336');
                    warn('WME SDK not available for Map Comments');
                    return;
                }

                // Convert OpenLayers geometry to GeoJSON
                const geometry = lastFeature.geometry;
                const coords = [];

                // Mercator to WGS84 conversion
                function mercatorToWgs84(x, y) {
                    const lon = (x / 20037508.34) * 180;
                    let lat = (y / 20037508.34) * 180;
                    lat = (180 / Math.PI) * (2 * Math.atan(Math.exp(lat * Math.PI / 180)) - Math.PI / 2);
                    return [lon, lat];
                }

                // Get polygon points
                if (geometry.CLASS_NAME === "OpenLayers.Geometry.Polygon") {
                    const ring = geometry.components[0];
                    if (ring && ring.components) {
                        for (const point of ring.components) {
                            const [lon, lat] = mercatorToWgs84(point.x, point.y);
                            coords.push([lon, lat]);
                        }
                    }
                }

                if (coords.length < 4) {
                    updateStatusMessage('Invalid geometry', '#f44336');
                    return;
                }

                // Build GeoJSON
                const geoJson = {
                    type: 'Polygon',
                    coordinates: [coords]
                };

                // Get form values
                const title = mcTitleInput?.value || '';
                const desc = mcDescInput?.value || '';
                const days = parseInt(mcDaysInput?.value) || 30;

                // Calculate end date
                const endDate = Date.now() + (days * 24 * 60 * 60 * 1000);

                log('Creating Map Comment with geometry:', geoJson);

                // Create Map Comment via SDK
                const result = sdk.DataModel.MapComments.addComment({
                    geometry: geoJson,
                    subject: String(title).substring(0, 30),
                    body: String(desc).substring(0, 2000),
                    endDate: endDate
                });

                if (result) {
                    updateStatusMessage('✅ ' + t('mapCommentCreated') + ' (ID: ' + result.id + ')', '#4CAF50');
                    log('Map Comment created:', result);

                    // Clear form
                    if (mcTitleInput) mcTitleInput.value = '';
                    if (mcDescInput) mcDescInput.value = '';
                } else {
                    updateStatusMessage('✅ ' + t('mapCommentCreated'), '#4CAF50');
                }

            } catch (error) {
                warn('Error creating Map Comment:', error);
                updateStatusMessage('Error: ' + error.message, '#f44336');
            }
        }

        function startPresetCircleMode() {
            presetCircleMode = true;
            presetCircleCenter = null;

            // Deactivate other drawing modes
            checkbox.checked = false;
            freehandCheckbox.checked = false;
            polygonControl.deactivate();
            freehandControl.deactivate();

            // Update UI
            presetCircleBtn.style.background = '#ff9800';
            presetCircleBtn.textContent = '⏳ ' + t('clickForCenter');
            presetCancelBtn.style.display = 'block';

            updateStatusMessage(t('clickForCenter'), '#2196F3');

            // Add map click handler
            uWaze.map.events.register('click', null, onPresetMapClick);
        }

        function cancelPresetCircleMode() {
            presetCircleMode = false;
            presetCircleCenter = null;

            // Reset UI
            presetCircleBtn.style.background = '#2196F3';
            presetCircleBtn.textContent = '📍 ' + t('presetHint');
            presetCancelBtn.style.display = 'none';

            updateStatusMessage('', '#4CAF50');

            // Remove map click handler
            uWaze.map.events.unregister('click', null, onPresetMapClick);
        }

        function onPresetMapClick(e) {
            if (!presetCircleMode) return;

            // Get click position - this returns WGS84 coordinates directly
            const lonlat = uWaze.map.getLonLatFromPixel(e.xy);
            if (!lonlat) {
                warn('Could not get coordinates from click');
                return;
            }

            const radius = parseInt(presetRadiusInput?.value) || 100;

            log(`Preset click at: ${lonlat.lon}, ${lonlat.lat}, radius: ${radius}m`);

            // lonlat.lon and lonlat.lat are already in WGS84
            createPresetCircle(lonlat.lon, lonlat.lat, radius);

            // Reset mode
            cancelPresetCircleMode();

            updateStatusMessage(t('circleCreated', radius), '#4CAF50');
        }

        function createPresetCircle(centerLon, centerLat, radiusMeters) {
            try {
                // centerLon, centerLat are already in WGS84
                log(`Creating circle at WGS84: ${centerLon}, ${centerLat}`);

                // WGS84 to Mercator conversion
                function wgs84ToMercator(lon, lat) {
                    const x = lon * 20037508.34 / 180;
                    let y = Math.log(Math.tan((90 + lat) * Math.PI / 360)) / (Math.PI / 180);
                    y = y * 20037508.34 / 180;
                    return [x, y];
                }

                // Create circle points using geodesic calculation
                const points = [];
                const steps = 100;

                for (let i = 0; i <= steps; i++) {
                    const bearing = (i / steps) * 360;
                    const destPoint = destinationPoint(
                        [centerLon, centerLat],
                        radiusMeters,
                        bearing
                    );

                    // Convert to Mercator for OpenLayers
                    const [mx, my] = wgs84ToMercator(destPoint[0], destPoint[1]);
                    points.push(new uOpenLayers.Geometry.Point(mx, my));
                }

                // Create polygon
                const ring = new uOpenLayers.Geometry.LinearRing(points);
                const polygon = new uOpenLayers.Geometry.Polygon([ring]);
                const feature = new uOpenLayers.Feature.Vector(polygon);

                // Add to layer
                radiusLayer.addFeatures([feature]);

                // Force redraw
                if (radiusLayer.redraw) radiusLayer.redraw();

                // Update annotations
                setTimeout(() => {
                    const event = new CustomEvent('updateAnnotations');
                    document.dispatchEvent(event);
                }, 100);

                log(`Created preset circle: ${radiusMeters}m`);

            } catch (error) {
                warn('Error creating preset circle:', error);
                console.error(error);
            }
        }

        // Geodesic destination point calculation
        function destinationPoint(start, distanceMeters, bearingDegrees) {
            const toRadians = (deg) => deg * Math.PI / 180;
            const toDegrees = (rad) => rad * 180 / Math.PI;

            const startLon = Number(start[0]);
            const startLat = Number(start[1]);
            const dist = Number(distanceMeters);
            const bearing = Number(bearingDegrees);

            const lon1 = toRadians(startLon);
            const lat1 = toRadians(startLat);
            const bearingRad = toRadians(bearing);
            const angularDist = dist / EARTH_RADIUS;

            const lat2 = Math.asin(
                Math.sin(lat1) * Math.cos(angularDist) +
                Math.cos(lat1) * Math.sin(angularDist) * Math.cos(bearingRad)
            );
            const lon2 = lon1 + Math.atan2(
                Math.sin(bearingRad) * Math.sin(angularDist) * Math.cos(lat1),
                Math.cos(angularDist) - Math.sin(lat1) * Math.sin(lat2)
            );
            return [toDegrees(lon2), toDegrees(lat2)];
        }

        function updateStatusMessage(message, color = '#4CAF50') {
            const statusElement = document.getElementById('circles-current-radius');
            if (statusElement) {
                statusElement.innerHTML = message;
                statusElement.style.color = color;
                setTimeout(() => {
                    statusElement.innerHTML = '';
                    statusElement.style.color = '';
                }, 4000);
            }
        }

        function selectSegmentsInCircles() {
            const toSelect = [];
            const segments = uWaze.model.segments.getObjectArray();
            const blockInboundOnly = blockInboundCheckbox?.checked;

            if (!segments || segments.length === 0) {
                warn('No segments found');
                return;
            }

            if (blockInboundOnly) {
                log('Finding perimeter segments for hollow circle blocking...');

                for (const drawnFeature of radiusLayer.features) {
                    const circleGeometry = drawnFeature.geometry;
                    const bounds = circleGeometry.getBounds();
                    const circleCenter = {
                        x: (bounds.left + bounds.right) / 2,
                        y: (bounds.bottom + bounds.top) / 2
                    };

                    const radius = Math.abs(bounds.right - bounds.left) / 2;
                    // Toleranz für Segmente die den Kreis knapp berühren (5% des Radius)
                    const tolerance = radius * 0.05;

                    for (const segment of segments) {
                        if (!segment.attributes?.roadType) continue;

                        if (filterDrivableCheckbox.checked) {
                            if (!isDrivableSegment(segment)) continue;
                        }

                        const segGeom = segment.getOLGeometry ? segment.getOLGeometry() : segment.geometry;
                        if (!segGeom) continue;

                        const coords = segGeom.components || segGeom.getVertices?.();
                        if (!coords || coords.length < 2) continue;

                        // Prüfe Start- und Endpunkt
                        const startPoint = coords[0];
                        const endPoint = coords[coords.length - 1];

                        if (!startPoint?.x || !endPoint?.x) continue;

                        const startDist = Math.hypot(startPoint.x - circleCenter.x, startPoint.y - circleCenter.y);
                        const endDist = Math.hypot(endPoint.x - circleCenter.x, endPoint.y - circleCenter.y);

                        // Prüfe ob Punkte innen oder außen sind (mit Toleranz)
                        const startInside = startDist < (radius + tolerance);
                        const endInside = endDist < (radius + tolerance);
                        const startStrictInside = startDist < (radius - tolerance);
                        const endStrictInside = endDist < (radius - tolerance);

                        // Ein Segment ist ein Perimeter-Segment wenn:
                        // 1. Ein Punkt klar innen und einer klar außen
                        // 2. Mindestens ein Punkt berührt den Perimeter (innerhalb Toleranz)
                        // 3. Das Segment schneidet den Kreis geometrisch

                        let isPerimeterSegment = false;

                        // Fall 1: Ein Punkt innen, einer außen
                        if (startStrictInside !== endStrictInside) {
                            isPerimeterSegment = true;
                        }
                        // Fall 2: Mindestens ein Punkt liegt nah am Perimeter
                        else if (Math.abs(startDist - radius) <= tolerance || Math.abs(endDist - radius) <= tolerance) {
                            isPerimeterSegment = true;
                        }
                        // Fall 3: Segment kreuzt den Kreis (beide außen aber intersects)
                        else if (!startInside && !endInside && circleGeometry.intersects(segGeom)) {
                            isPerimeterSegment = true;
                        }
                        // Fall 4: Prüfe ob irgendein Zwischenpunkt den Kreis kreuzt
                        else if (coords.length > 2) {
                            for (let i = 1; i < coords.length - 1; i++) {
                                const midPoint = coords[i];
                                if (midPoint?.x) {
                                    const midDist = Math.hypot(midPoint.x - circleCenter.x, midPoint.y - circleCenter.y);
                                    if (Math.abs(midDist - radius) <= tolerance) {
                                        isPerimeterSegment = true;
                                        break;
                                    }
                                }
                            }
                        }

                        if (isPerimeterSegment) {
                            if (shouldBlockSegmentInbound(segment, segGeom, circleCenter, radius)) {
                                // Duplikate vermeiden
                                if (!toSelect.find(s => s.getID() === segment.getID())) {
                                    toSelect.push(segment);
                                    log(`✓ Perimeter-Segment gefunden: ${segment.getID()} (${segment.attributes.primaryStreetID ? 'Straße' : 'unbenannt'})`);
                                }
                            }
                        }
                    }
                }

                log(`=== Gefundene Perimeter-Segmente: ${toSelect.length} ===`);
                toSelect.forEach(s => log(`  - Segment ${s.getID()}`));
            } else {
                for (const segment of segments) {
                    if (!segment.attributes?.roadType) continue;

                    if (filterDrivableCheckbox.checked) {
                        if (!isDrivableSegment(segment)) continue;
                    }

                    const segGeom = segment.getOLGeometry ? segment.getOLGeometry() : segment.geometry;
                    if (!segGeom) continue;

                    let isInCircle = false;

                    for (const drawnFeature of radiusLayer.features) {
                        if (drawnFeature.geometry.intersects(segGeom)) {
                            isInCircle = true;
                            break;
                        }
                    }

                    if (isInCircle) {
                        toSelect.push(segment);
                    }
                }
            }

            log(`Selected ${toSelect.length} segments${blockInboundOnly ? ' (perimeter only)' : ''}`);

            if (toSelect.length > 0) {
                if (blockInboundOnly) {
                    applyDirectionRestrictions(toSelect);
                } else {
                    uWaze.selectionManager.setSelectedModels(toSelect);
                    updateStatusMessage(t('selectedSegments', toSelect.length), '#4CAF50');
                }
            } else {
                updateStatusMessage(t('noSegmentsFound'), '#ff9800');
            }

            checkbox.checked = false;
            freehandCheckbox.checked = false;
            polygonControl.deactivate();
            freehandControl.deactivate();
        }

        function exportRtcsInArea() {
            log('=== Export Button geklickt ===');

            if (!radiusLayer || !radiusLayer.features || radiusLayer.features.length === 0) {
                updateStatusMessage('Erst einen Kreis zeichnen', '#ff9800');
                log('Export abgebrochen: Kein Kreis vorhanden');
                return;
            }

            log(`radiusLayer hat ${radiusLayer.features.length} Features`);

            const segments = uWaze.model.segments.getObjectArray();
            log(`${segments.length} Segmente im Model`);

            const segmentsInCircle = [];

            // Finde alle Segmente im Kreis
            for (const segment of segments) {
                if (!segment.attributes?.roadType) continue;

                const segGeom = segment.getOLGeometry ? segment.getOLGeometry() : segment.geometry;
                if (!segGeom) continue;

                for (const drawnFeature of radiusLayer.features) {
                    if (drawnFeature.geometry.intersects(segGeom)) {
                        segmentsInCircle.push(segment);
                        break;
                    }
                }
            }

            log(`${segmentsInCircle.length} Segmente im Kreis gefunden`);

            if (segmentsInCircle.length === 0) {
                updateStatusMessage('Keine Segmente im Kreis gefunden', '#ff9800');
                return;
            }

            // Finde alle RTCs auf diesen Segmenten
            const rtcsToExport = [];
            const segmentIds = new Set(segmentsInCircle.map(s => s.getID()));

            log(`Prüfe roadClosures...`);
            log(`W.model.roadClosures existiert: ${!!W.model.roadClosures}`);

            if (W.model.roadClosures) {
                const allClosures = W.model.roadClosures.getObjectArray();
                log(`${allClosures.length} Closures im Model insgesamt`);

                for (const closure of allClosures) {
                    const attrs = closure.attributes;
                    if (!attrs) continue;

                    log(`Closure prüfen: segID=${attrs.segID}, in Set: ${segmentIds.has(attrs.segID)}`);

                    if (segmentIds.has(attrs.segID)) {
                        rtcsToExport.push({
                            segmentId: attrs.segID,
                            reason: attrs.reason || '',
                            direction: attrs.direction,
                            startDate: attrs.startDate,
                            endDate: attrs.endDate,
                            permanent: attrs.permanent || false
                        });
                        log(`  → RTC hinzugefügt für Segment ${attrs.segID}`);
                    }
                }
            } else {
                warn('W.model.roadClosures nicht verfügbar!');

                // Alternative: Prüfe ob Closures auf den Segmenten selbst gespeichert sind
                log('Versuche Closures direkt von Segmenten zu lesen...');
                for (const segment of segmentsInCircle) {
                    const closures = segment.attributes?.roadClosures || segment.attributes?.closures;
                    if (closures && closures.length > 0) {
                        log(`Segment ${segment.getID()} hat ${closures.length} Closures`);
                        for (const c of closures) {
                            rtcsToExport.push({
                                segmentId: segment.getID(),
                                reason: c.reason || '',
                                direction: c.direction,
                                startDate: c.startDate,
                                endDate: c.endDate,
                                permanent: c.permanent || false
                            });
                        }
                    }
                }
            }

            log(`${rtcsToExport.length} RTCs zum Export gefunden`);

            if (rtcsToExport.length === 0) {
                updateStatusMessage(t('noRtcsToExport') + ' (0 RTCs auf ' + segmentsInCircle.length + ' Segmenten)', '#ff9800');
                return;
            }

            // Export als JSON
            const exportData = {
                version: 1,
                exportDate: new Date().toISOString(),
                segmentCount: segmentsInCircle.length,
                rtcs: rtcsToExport
            };

            log('Erstelle Download...');

            try {
                const jsonStr = JSON.stringify(exportData, null, 2);
                log(`JSON erstellt: ${jsonStr.length} Zeichen`);

                const blob = new Blob([jsonStr], { type: 'application/json' });
                log(`Blob erstellt: ${blob.size} Bytes`);

                const url = URL.createObjectURL(blob);
                log(`URL erstellt: ${url}`);

                const a = document.createElement('a');
                a.href = url;
                a.download = `wme-rtcs-export-${new Date().toISOString().slice(0,10)}.json`;
                a.style.display = 'none';
                document.body.appendChild(a);

                log('Starte Download...');
                a.click();

                // Cleanup nach kurzer Verzögerung
                setTimeout(() => {
                    document.body.removeChild(a);
                    URL.revokeObjectURL(url);
                    log('Cleanup abgeschlossen');
                }, 100);

                updateStatusMessage(t('exportedRtcs', rtcsToExport.length), '#4CAF50');
                log(`✓ ${rtcsToExport.length} RTCs exportiert`);
            } catch (err) {
                warn('Export Fehler:', err);
                console.error('Export Fehler Details:', err);
                updateStatusMessage('Export Fehler: ' + err.message, '#f44336');
            }
        }

        function importRtcsFromFile(file) {
            const reader = new FileReader();

            reader.onload = function(e) {
                try {
                    const data = JSON.parse(e.target.result);

                    if (!data.rtcs || !Array.isArray(data.rtcs)) {
                        updateStatusMessage('Ungültiges Dateiformat', '#f44336');
                        return;
                    }

                    log(`Importiere ${data.rtcs.length} RTCs...`);
                    importRtcs(data.rtcs);

                } catch (err) {
                    warn('Import Fehler:', err);
                    updateStatusMessage('Fehler beim Lesen der Datei', '#f44336');
                }
            };

            reader.readAsText(file);
        }

        function importRtcs(rtcList) {
            if (rtcList.length === 0) {
                updateStatusMessage('Keine RTCs zum Importieren', '#ff9800');
                return;
            }

            const cab = require("Waze/Modules/Closures/Models/ClosureActionBuilder");
            const sc = require("Waze/Modules/Closures/Models/SharedClosure");

            let importedCount = 0;
            let errorCount = 0;
            let currentIndex = 0;

            const importNext = () => {
                if (currentIndex >= rtcList.length) {
                    updateStatusMessage(t('importedRtcs', importedCount) + (errorCount > 0 ? ` (${errorCount} Fehler)` : ''), '#4CAF50');
                    return;
                }

                const rtc = rtcList[currentIndex];
                currentIndex++;

                const segment = W.model.segments.getObjectById(rtc.segmentId);
                if (!segment) {
                    log(`Segment ${rtc.segmentId} nicht gefunden, überspringe...`);
                    errorCount++;
                    importNext();
                    return;
                }

                W.selectionManager.setSelectedModels([segment]);

                const closureDetails = {
                    closures: [],
                    attributions: [],
                    reason: (rtc.reason || 'Import') + String.fromCharCode(160),
                    direction: rtc.direction || 3,
                    startDate: rtc.startDate,
                    endDate: rtc.endDate,
                    location: "",
                    permanent: rtc.permanent || false,
                    segments: [segment],
                    closuresType: 'roadClosure',
                    reverseSegments: {}
                };

                const closure = new sc(closureDetails, {
                    dataModel: W.model,
                    segmentSelection: W.selectionManager.getSegmentSelection(),
                    isNewClosure: true,
                    closedNodesMap: {}
                });

                if (closure.closureNodes?.models) {
                    for (const n of closure.closureNodes.models) {
                        n.attributes.isClosed = false;
                    }
                }

                const action = cab.add(closure, W.loginManager.user, W.model);

                W.controller.save({ actions: [action] }).then(() => {
                    importedCount++;
                    updateStatusMessage(`Importiere... ${currentIndex}/${rtcList.length}`, '#2196F3');
                    importNext();
                }).catch((err) => {
                    errorCount++;
                    log(`Import Fehler für Segment ${rtc.segmentId}: ${err.errors?.[0]?.attributes?.details || err.message}`);
                    importNext();
                });
            };

            updateStatusMessage(`Importiere... 0/${rtcList.length}`, '#2196F3');
            importNext();
        }

        // Kreise Export/Import Funktionen
        function exportCirclesInArea() {
            log('=== Kreise Export ===');

            if (!radiusLayer || !radiusLayer.features || radiusLayer.features.length === 0) {
                updateStatusMessage(t('noCirclesToExport'), '#ff9800');
                return;
            }

            // Mercator zu WGS84 Konvertierung
            function mercatorToWgs84(x, y) {
                const lon = (x / 20037508.34) * 180;
                let lat = (y / 20037508.34) * 180;
                lat = (180 / Math.PI) * (2 * Math.atan(Math.exp(lat * Math.PI / 180)) - Math.PI / 2);
                return [lon, lat];
            }

            const circles = [];

            for (const feature of radiusLayer.features) {
                if (!feature.geometry) continue;

                const geom = feature.geometry;
                const coords = [];

                if (geom.CLASS_NAME === "OpenLayers.Geometry.Polygon") {
                    const ring = geom.components?.[0];
                    if (ring && ring.components) {
                        for (const point of ring.components) {
                            const [lon, lat] = mercatorToWgs84(point.x, point.y);
                            coords.push([lon, lat]);
                        }
                    }
                }

                if (coords.length > 0) {
                    // Berechne Mittelpunkt und Radius
                    const bounds = geom.getBounds();
                    const centerX = (bounds.left + bounds.right) / 2;
                    const centerY = (bounds.bottom + bounds.top) / 2;
                    const [centerLon, centerLat] = mercatorToWgs84(centerX, centerY);

                    // Radius in Metern berechnen
                    const startPoint = new uOpenLayers.Geometry.Point(centerX, centerY);
                    const endPoint = new uOpenLayers.Geometry.Point(bounds.right, centerY);
                    const radiusLine = new uOpenLayers.Geometry.LineString([startPoint, endPoint]);
                    const radiusMeters = radiusLine.getGeodesicLength(new uOpenLayers.Projection("EPSG:900913"));

                    circles.push({
                        type: 'circle',
                        center: [centerLon, centerLat],
                        radiusMeters: Math.round(radiusMeters),
                        coordinates: coords
                    });
                }
            }

            if (circles.length === 0) {
                updateStatusMessage(t('noCirclesToExport'), '#ff9800');
                return;
            }

            const exportData = {
                version: 1,
                type: 'circles',
                exportDate: new Date().toISOString(),
                circles: circles
            };

            try {
                const jsonStr = JSON.stringify(exportData, null, 2);
                const blob = new Blob([jsonStr], { type: 'application/json' });
                const url = URL.createObjectURL(blob);

                const a = document.createElement('a');
                a.href = url;
                a.download = `wme-circles-export-${new Date().toISOString().slice(0,10)}.json`;
                a.style.display = 'none';
                document.body.appendChild(a);
                a.click();

                setTimeout(() => {
                    document.body.removeChild(a);
                    URL.revokeObjectURL(url);
                }, 100);

                updateStatusMessage(t('exportedCircles', circles.length), '#4CAF50');
                log(`✓ ${circles.length} Kreise exportiert`);
            } catch (err) {
                warn('Export Fehler:', err);
                updateStatusMessage('Export Fehler: ' + err.message, '#f44336');
            }
        }

        function importCirclesFromFile(file) {
            const reader = new FileReader();

            reader.onload = function(e) {
                try {
                    const data = JSON.parse(e.target.result);

                    if (data.type === 'circles' && data.circles && Array.isArray(data.circles)) {
                        importCircles(data.circles);
                    } else {
                        updateStatusMessage('Ungültiges Dateiformat (keine Kreise)', '#f44336');
                    }
                } catch (err) {
                    warn('Import Fehler:', err);
                    updateStatusMessage('Fehler beim Lesen der Datei', '#f44336');
                }
            };

            reader.readAsText(file);
        }

        function importCircles(circleList) {
            if (circleList.length === 0) {
                updateStatusMessage('Keine Kreise zum Importieren', '#ff9800');
                return;
            }

            // WGS84 zu Mercator Konvertierung
            function wgs84ToMercator(lon, lat) {
                const x = lon * 20037508.34 / 180;
                let y = Math.log(Math.tan((90 + lat) * Math.PI / 360)) / (Math.PI / 180);
                y = y * 20037508.34 / 180;
                return [x, y];
            }

            let importedCount = 0;

            for (const circle of circleList) {
                try {
                    let points = [];

                    if (circle.coordinates && circle.coordinates.length > 0) {
                        // Verwende gespeicherte Koordinaten
                        for (const coord of circle.coordinates) {
                            const [mx, my] = wgs84ToMercator(coord[0], coord[1]);
                            points.push(new uOpenLayers.Geometry.Point(mx, my));
                        }
                    } else if (circle.center && circle.radiusMeters) {
                        // Erstelle Kreis aus Mittelpunkt und Radius
                        const steps = 100;
                        for (let i = 0; i <= steps; i++) {
                            const bearing = (i / steps) * 360;
                            const destPoint = destinationPoint(circle.center, circle.radiusMeters, bearing);
                            const [mx, my] = wgs84ToMercator(destPoint[0], destPoint[1]);
                            points.push(new uOpenLayers.Geometry.Point(mx, my));
                        }
                    }

                    if (points.length > 2) {
                        const ring = new uOpenLayers.Geometry.LinearRing(points);
                        const polygon = new uOpenLayers.Geometry.Polygon([ring]);
                        const feature = new uOpenLayers.Feature.Vector(polygon);
                        radiusLayer.addFeatures([feature]);
                        importedCount++;
                    }
                } catch (err) {
                    warn('Fehler beim Importieren eines Kreises:', err);
                }
            }

            if (radiusLayer.redraw) radiusLayer.redraw();

            setTimeout(() => {
                document.dispatchEvent(new CustomEvent('updateAnnotations'));
            }, 100);

            updateStatusMessage(t('importedCircles', importedCount), '#4CAF50');
            log(`✓ ${importedCount} Kreise importiert`);
        }

        // GPX Import/Export Funktionen
        function exportSelectedAsGpx() {
            log('=== GPX Export ===');

            // Exportiere ausgewählte Segmente oder Segmente im Kreis
            const selectedSegments = uWaze.selectionManager?.getSelectedFeatures?.() || [];
            let segmentsToExport = [];

            if (selectedSegments.length > 0) {
                // Ausgewählte Segmente exportieren
                segmentsToExport = selectedSegments.filter(f => f?.model?.type === 'segment').map(f => f.model);
            } else if (radiusLayer?.features?.length > 0) {
                // Segmente im Kreis exportieren
                const allSegments = uWaze.model.segments.getObjectArray();
                for (const segment of allSegments) {
                    if (!segment?.attributes?.roadType) continue;
                    const segGeom = segment.getOLGeometry?.() || segment.geometry;
                    if (!segGeom) continue;

                    for (const drawnFeature of radiusLayer.features) {
                        if (drawnFeature.geometry.intersects(segGeom)) {
                            segmentsToExport.push(segment);
                            break;
                        }
                    }
                }
            }

            if (segmentsToExport.length === 0) {
                updateStatusMessage('Keine Segmente zum Exportieren', '#ff9800');
                return;
            }

            // GPX erstellen
            const gpxHeader = `<?xml version="1.0" encoding="UTF-8"?>
<gpx version="1.1" creator="WME Circles" xmlns="http://www.topografix.com/GPX/1/1">
  <metadata>
    <name>WME Segments Export</name>
    <time>${new Date().toISOString()}</time>
  </metadata>`;

            let gpxTracks = '';

            // Mercator zu WGS84
            const toWGS84 = (x, y) => {
                const lon = x * 180 / 20037508.34;
                let lat = y * 180 / 20037508.34;
                lat = 180 / Math.PI * (2 * Math.atan(Math.exp(lat * Math.PI / 180)) - Math.PI / 2);
                return { lon, lat };
            };

            for (const segment of segmentsToExport) {
                const streetName = getSegmentStreetName(segment) || 'Unbekannt';
                const segId = segment.attributes?.id || segment.getID?.();

                let coords = [];
                const geom = segment.getOLGeometry?.() || segment.geometry || segment.attributes?.geometry;

                if (geom?.components) {
                    coords = geom.components.map(c => toWGS84(c.x, c.y));
                } else if (geom?.coordinates) {
                    coords = geom.coordinates.map(c => ({ lon: c[0], lat: c[1] }));
                } else if (geom?.getVertices) {
                    coords = geom.getVertices().map(v => toWGS84(v.x, v.y));
                }

                if (coords.length > 0) {
                    gpxTracks += `
  <trk>
    <name>${streetName} (${segId})</name>
    <trkseg>`;
                    for (const c of coords) {
                        gpxTracks += `
      <trkpt lat="${c.lat.toFixed(7)}" lon="${c.lon.toFixed(7)}"/>`;
                    }
                    gpxTracks += `
    </trkseg>
  </trk>`;
                }
            }

            const gpxContent = gpxHeader + gpxTracks + '\n</gpx>';

            // Download
            const blob = new Blob([gpxContent], { type: 'application/gpx+xml' });
            const url = URL.createObjectURL(blob);
            const a = document.createElement('a');
            a.href = url;
            a.download = `wme-segments-${new Date().toISOString().slice(0,10)}.gpx`;
            document.body.appendChild(a);
            a.click();
            setTimeout(() => {
                document.body.removeChild(a);
                URL.revokeObjectURL(url);
            }, 100);

            updateStatusMessage(t('exportedGpx') + ` (${segmentsToExport.length})`, '#4CAF50');
        }

        function getSegmentStreetName(segment) {
            if (!segment?.attributes) return null;
            const primaryStreet = segment.attributes.primaryStreetID;
            if (primaryStreet && uWaze?.model?.streets) {
                const street = uWaze.model.streets.getObjectById(primaryStreet);
                if (street?.attributes?.name) return street.attributes.name;
            }
            return segment.attributes.streetName || null;
        }

        function importGpxFile(file) {
            const reader = new FileReader();

            reader.onload = function(e) {
                try {
                    const parser = new DOMParser();
                    const gpxDoc = parser.parseFromString(e.target.result, 'text/xml');

                    // Prüfe auf Parse-Fehler
                    const parseError = gpxDoc.querySelector('parsererror');
                    if (parseError) {
                        log('GPX Parse-Fehler: ' + parseError.textContent);
                        updateStatusMessage('GPX Parse-Fehler', '#f44336');
                        return;
                    }

                    // Extrahiere alle Track-Punkte und Route-Punkte
                    const points = [];

                    // Track Points (trkpt) - mit und ohne Namespace
                    let trkpts = gpxDoc.querySelectorAll('trkpt');
                    if (trkpts.length === 0) {
                        trkpts = gpxDoc.getElementsByTagNameNS('*', 'trkpt');
                    }
                    log(`GPX: ${trkpts.length} trkpt Elemente gefunden`);

                    for (const pt of trkpts) {
                        const lat = parseFloat(pt.getAttribute('lat'));
                        const lon = parseFloat(pt.getAttribute('lon'));
                        if (!isNaN(lat) && !isNaN(lon) && Math.abs(lat) <= 90 && Math.abs(lon) <= 180) {
                            points.push({ lat, lon });
                        }
                    }

                    // Route Points (rtept)
                    let rtepts = gpxDoc.querySelectorAll('rtept');
                    if (rtepts.length === 0) {
                        rtepts = gpxDoc.getElementsByTagNameNS('*', 'rtept');
                    }
                    log(`GPX: ${rtepts.length} rtept Elemente gefunden`);

                    for (const pt of rtepts) {
                        const lat = parseFloat(pt.getAttribute('lat'));
                        const lon = parseFloat(pt.getAttribute('lon'));
                        if (!isNaN(lat) && !isNaN(lon) && Math.abs(lat) <= 90 && Math.abs(lon) <= 180) {
                            points.push({ lat, lon });
                        }
                    }

                    // Waypoints (wpt)
                    let wpts = gpxDoc.querySelectorAll('wpt');
                    if (wpts.length === 0) {
                        wpts = gpxDoc.getElementsByTagNameNS('*', 'wpt');
                    }
                    log(`GPX: ${wpts.length} wpt Elemente gefunden`);

                    for (const pt of wpts) {
                        const lat = parseFloat(pt.getAttribute('lat'));
                        const lon = parseFloat(pt.getAttribute('lon'));
                        if (!isNaN(lat) && !isNaN(lon) && Math.abs(lat) <= 90 && Math.abs(lon) <= 180) {
                            points.push({ lat, lon });
                        }
                    }

                    if (points.length === 0) {
                        // Debug: Zeige XML-Struktur
                        log('GPX XML Root: ' + gpxDoc.documentElement?.tagName);
                        log('GPX XML Children: ' + Array.from(gpxDoc.documentElement?.children || []).map(c => c.tagName).join(', '));
                        updateStatusMessage('Keine Punkte in GPX gefunden', '#ff9800');
                        return;
                    }

                    // Debug: Erste 3 Punkte zeigen
                    log(`GPX: ${points.length} Punkte gefunden`);
                    if (points.length > 0) {
                        log(`GPX Erster Punkt: ${points[0].lat.toFixed(5)}, ${points[0].lon.toFixed(5)}`);
                    }

                    // Zur GPX-Position zoomen (Mittelpunkt der Punkte)
                    const avgLat = points.reduce((s, p) => s + p.lat, 0) / points.length;
                    const avgLon = points.reduce((s, p) => s + p.lon, 0) / points.length;

                    log(`GPX Zentrum: ${avgLat.toFixed(5)}, ${avgLon.toFixed(5)}`);

                    // Starte Auto-Scroll mit Kontrolle
                    startGpxAutoScroll(points);

                } catch (err) {
                    warn('GPX Import Fehler:', err);
                    updateStatusMessage('GPX Fehler: ' + err.message, '#f44336');
                }
            };

            reader.readAsText(file);
        }

        // GPX-Linie auf der Karte zeichnen
        function drawGpxLine(gpxPoints) {
            removeGpxLine(); // Alte Linie entfernen

            try {
                const OL = window.OpenLayers || window.OL;
                if (!OL || !uWaze?.map?.olMap) return;

                // Layer erstellen
                const layer = new OL.Layer.Vector('GPX Route', {
                    displayInLayerSwitcher: false,
                    uniqueName: 'rtcplus_gpx_route'
                });

                // Punkte in Mercator konvertieren
                const points = gpxPoints.map(p => {
                    const mercX = p.lon * 20037508.34 / 180;
                    const mercY = Math.log(Math.tan((90 + p.lat) * Math.PI / 360)) / (Math.PI / 180) * 20037508.34 / 180;
                    return new OL.Geometry.Point(mercX, mercY);
                });

                // Linie erstellen
                const line = new OL.Geometry.LineString(points);
                const feature = new OL.Feature.Vector(line, {}, {
                    strokeColor: '#FF00FF',
                    strokeWidth: 4,
                    strokeOpacity: 0.7,
                    strokeDashstyle: 'dash'
                });

                layer.addFeatures([feature]);
                uWaze.map.olMap.addLayer(layer);

                gpxAutoScroll.gpxLayer = layer;
                log('GPX-Linie gezeichnet');
            } catch (e) {
                warn('GPX-Linie zeichnen fehlgeschlagen:', e);
            }
        }

        function removeGpxLine() {
            if (gpxAutoScroll.gpxLayer) {
                try {
                    uWaze.map.olMap.removeLayer(gpxAutoScroll.gpxLayer);
                    gpxAutoScroll.gpxLayer.destroy();
                } catch (e) {}
                gpxAutoScroll.gpxLayer = null;
            }
        }

        // GPX Auto-Scroll mit Benutzer-Kontrolle
        let gpxAutoScroll = {
            active: false,
            paused: false,
            gpxPoints: [],
            scrollPositions: [],
            currentIndex: 0,
            collectedSegmentIds: new Set(),
            timeoutId: null,
            gpxLayer: null
        };

        function startGpxAutoScroll(gpxPoints) {
            // GPX-Linie auf der Karte zeichnen
            drawGpxLine(gpxPoints);

            // Scroll-Positionen berechnen
            const scrollPositions = [];
            let lastLat = null, lastLon = null;
            const MIN_DISTANCE = 300; // 300m Schritte

            for (const p of gpxPoints) {
                if (lastLat === null) {
                    scrollPositions.push({ lat: p.lat, lon: p.lon });
                    lastLat = p.lat;
                    lastLon = p.lon;
                } else {
                    const R = 6371000;
                    const dLat = (p.lat - lastLat) * Math.PI / 180;
                    const dLon = (p.lon - lastLon) * Math.PI / 180;
                    const a = Math.sin(dLat/2) * Math.sin(dLat/2) +
                              Math.cos(lastLat * Math.PI / 180) * Math.cos(p.lat * Math.PI / 180) *
                              Math.sin(dLon/2) * Math.sin(dLon/2);
                    const dist = R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));

                    if (dist >= MIN_DISTANCE) {
                        scrollPositions.push({ lat: p.lat, lon: p.lon });
                        lastLat = p.lat;
                        lastLon = p.lon;
                    }
                }
            }

            // Letzten Punkt
            const lastPoint = gpxPoints[gpxPoints.length - 1];
            if (scrollPositions.length === 0 ||
                scrollPositions[scrollPositions.length-1].lat !== lastPoint.lat) {
                scrollPositions.push({ lat: lastPoint.lat, lon: lastPoint.lon });
            }

            gpxAutoScroll = {
                active: true,
                paused: false,
                gpxPoints: gpxPoints,
                scrollPositions: scrollPositions,
                currentIndex: 0,
                collectedSegmentIds: new Set(),
                timeoutId: null
            };

            log(`GPX Auto-Scroll: ${scrollPositions.length} Positionen`);

            // UI anzeigen
            showGpxAutoScrollUI();

            // Zoom setzen
            try {
                if (uWaze?.map?.olMap?.zoomTo) {
                    uWaze.map.olMap.zoomTo(17);
                }
            } catch (e) {}

            // Starten
            gpxScrollNext();
        }

        function gpxScrollNext() {
            if (!gpxAutoScroll.active) return;
            if (gpxAutoScroll.paused) return;

            const { scrollPositions, currentIndex, gpxPoints } = gpxAutoScroll;

            if (currentIndex >= scrollPositions.length) {
                // Fertig!
                finishGpxAutoScroll();
                return;
            }

            const pos = scrollPositions[currentIndex];

            // Zur Position scrollen
            try {
                const mercX = pos.lon * 20037508.34 / 180;
                const mercY = Math.log(Math.tan((90 + pos.lat) * Math.PI / 360)) / (Math.PI / 180) * 20037508.34 / 180;

                if (uWaze?.map?.setCenter) {
                    uWaze.map.setCenter({ lon: mercX, lat: mercY });
                }
            } catch (e) {}

            // UI aktualisieren
            updateGpxAutoScrollUI();

            // Segmente sammeln nach kurzer Wartezeit
            gpxAutoScroll.timeoutId = setTimeout(() => {
                // Segmente matchen
                const matches = matchGpxToSegmentsLocal(gpxPoints);
                const newSegments = [];

                for (const seg of matches) {
                    const segId = seg.attributes?.id || seg.getID?.();
                    if (segId && !gpxAutoScroll.collectedSegmentIds.has(segId)) {
                        gpxAutoScroll.collectedSegmentIds.add(segId);
                        newSegments.push(seg);
                    }
                }

                // INKREMENTELL zur Auswahl hinzufügen (nicht ersetzen!)
                if (newSegments.length > 0) {
                    try {
                        const currentSelection = uWaze.selectionManager.getSelectedFeatures() || [];
                        const currentSegments = currentSelection
                            .filter(f => f?.model?.type === 'segment')
                            .map(f => f.model);

                        // Neue Segmente zur bestehenden Auswahl hinzufügen
                        const combinedSelection = [...currentSegments, ...newSegments];
                        uWaze.selectionManager.setSelectedModels(combinedSelection);
                    } catch (e) {
                        // Fallback: Nur neue Segmente auswählen
                        try {
                            const current = uWaze.selectionManager.getSelectedDataModelObjects?.() || [];
                            uWaze.selectionManager.setSelectedModels([...current, ...newSegments]);
                        } catch (e2) {}
                    }
                }

                // UI aktualisieren
                updateGpxAutoScrollUI();

                // Nächste Position
                gpxAutoScroll.currentIndex++;
                gpxScrollNext();

            }, 800); // 800ms pro Position für zuverlässiges Laden
        }

        function showGpxAutoScrollUI() {
            let ui = document.getElementById('gpx-autoscroll-ui');
            if (ui) ui.remove();

            ui = document.createElement('div');
            ui.id = 'gpx-autoscroll-ui';
            ui.style.cssText = `
                position: fixed;
                top: 10px;
                left: 50%;
                transform: translateX(-50%);
                background: rgba(0,0,0,0.92);
                color: white;
                padding: 12px 16px;
                border-radius: 10px;
                z-index: 99999;
                font-family: Arial, sans-serif;
                box-shadow: 0 4px 20px rgba(0,0,0,0.5);
                min-width: 380px;
            `;

            document.body.appendChild(ui);
            updateGpxAutoScrollUI();
        }

        function updateGpxAutoScrollUI() {
            const ui = document.getElementById('gpx-autoscroll-ui');
            if (!ui) return;

            const { scrollPositions, currentIndex, collectedSegmentIds, paused } = gpxAutoScroll;
            const total = scrollPositions.length;
            const percent = total > 0 ? Math.round((currentIndex / total) * 100) : 0;

            // Zähle aktuell ausgewählte Segmente
            let selectedCount = 0;
            try {
                const selection = uWaze.selectionManager.getSelectedFeatures() || [];
                selectedCount = selection.filter(f => f?.model?.type === 'segment').length;
            } catch (e) {
                selectedCount = collectedSegmentIds.size;
            }

            ui.innerHTML = `
                <div style="display: flex; align-items: center; gap: 12px;">
                    <div style="flex: 1;">
                        <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 6px;">
                            <span style="font-size: 13px; font-weight: bold;">🗺️ GPX Import</span>
                            <span style="font-size: 12px; color: #aaa;">${currentIndex}/${total}</span>
                        </div>
                        <div style="background: #333; border-radius: 4px; height: 8px; overflow: hidden;">
                            <div style="background: #4CAF50; height: 100%; width: ${percent}%; transition: width 0.2s;"></div>
                        </div>
                    </div>
                    <div style="text-align: center; min-width: 70px;">
                        <div style="font-size: 18px; font-weight: bold; color: #4CAF50;">${selectedCount}</div>
                        <div style="font-size: 9px; color: #888;">ausgewählt</div>
                    </div>
                    <button id="gpx-pause-btn" style="background: ${paused ? '#4CAF50' : '#ff9800'}; color: white; border: none; padding: 8px 12px; border-radius: 5px; cursor: pointer; font-size: 16px;" title="${paused ? 'Fortsetzen' : 'Pause'}">
                        ${paused ? '▶' : '⏸'}
                    </button>
                    <button id="gpx-stop-btn" style="background: #f44336; color: white; border: none; padding: 8px 12px; border-radius: 5px; cursor: pointer; font-size: 14px;" title="Beenden & Auswählen">
                        ✓
                    </button>
                    <button id="gpx-cancel-btn" style="background: #666; color: white; border: none; padding: 8px 10px; border-radius: 5px; cursor: pointer; font-size: 12px;" title="Abbrechen">
                        ✕
                    </button>
                </div>
            `;

            // Event Listener
            document.getElementById('gpx-pause-btn')?.addEventListener('click', toggleGpxPause);
            document.getElementById('gpx-stop-btn')?.addEventListener('click', finishGpxAutoScroll);
            document.getElementById('gpx-cancel-btn')?.addEventListener('click', cancelGpxAutoScroll);
        }

        function toggleGpxPause() {
            gpxAutoScroll.paused = !gpxAutoScroll.paused;

            if (!gpxAutoScroll.paused) {
                // Fortsetzen
                gpxScrollNext();
            }

            updateGpxAutoScrollUI();
        }

        function finishGpxAutoScroll() {
            if (gpxAutoScroll.timeoutId) {
                clearTimeout(gpxAutoScroll.timeoutId);
            }

            const collectedIds = gpxAutoScroll.collectedSegmentIds;
            const gpxPoints = gpxAutoScroll.gpxPoints;

            log(`GPX Auto-Scroll Sammlung beendet: ${collectedIds.size} Segment-IDs`);

            if (collectedIds.size === 0) {
                const ui = document.getElementById('gpx-autoscroll-ui');
                if (ui) ui.remove();
                gpxAutoScroll.active = false;
                updateStatusMessage(t('gpxNoSegments'), '#ff9800');
                return;
            }

            // PRUNE: Finde den optimalen Pfad durch die gesammelten Segmente
            updateGpxAutoScrollUIText('Berechne optimale Route...');

            setTimeout(() => {
                const prunedIds = pruneSegmentIds(collectedIds, gpxPoints);
                log(`GPX Prune: ${prunedIds.length} Segmente auf optimalem Pfad`);

                // WICHTIG: Speichere IDs SOFORT global für Brush-Modus
                window._gpxSegmentIds = new Set(prunedIds);
                log(`GPX: ${window._gpxSegmentIds.size} Segment-IDs global gespeichert`);

                // Speichere für Auswahl-Durchgang
                gpxAutoScroll.prunedIds = prunedIds;
                gpxAutoScroll.currentIndex = 0;

                // Starte Auswahl-Durchgang
                updateGpxAutoScrollUIText('Wähle Segmente aus...');
                selectPrunedSegments();
            }, 100);
        }

        function updateGpxAutoScrollUIText(text) {
            const ui = document.getElementById('gpx-autoscroll-ui');
            if (ui) {
                const titleSpan = ui.querySelector('span');
                if (titleSpan) titleSpan.textContent = '🗺️ ' + text;
            }
        }

        // PRUNE: Bilde eine Kette entlang der GPX-Route, entferne tote Arme
        function pruneSegmentIds(collectedIds, gpxPoints) {
            const segments = uWaze.model.segments.getObjectArray();

            // Sammle Segment-Daten mit GPX-Position
            const segmentMap = new Map();
            for (const seg of segments) {
                const id = seg.attributes?.id || seg.getID?.();
                if (!id || !collectedIds.has(id)) continue;

                const coords = getSegmentCoordsWGS84(seg);
                if (!coords || coords.length < 2) continue;

                // Berechne GPX-Position (wo auf der GPX-Linie liegt dieses Segment)
                const midPt = { lat: coords[Math.floor(coords.length/2)][1], lon: coords[Math.floor(coords.length/2)][0] };
                const gpxPos = findGpxPosition(midPt, gpxPoints);

                segmentMap.set(id, {
                    id: id,
                    coords: coords,
                    fromNode: seg.attributes?.fromNodeID,
                    toNode: seg.attributes?.toNodeID,
                    gpxPos: gpxPos
                });
            }

            if (segmentMap.size === 0) return [...collectedIds];

            // Baue Node-zu-Segment Index
            const nodeToSegs = new Map();
            for (const [id, sd] of segmentMap) {
                if (sd.fromNode) {
                    if (!nodeToSegs.has(sd.fromNode)) nodeToSegs.set(sd.fromNode, []);
                    nodeToSegs.get(sd.fromNode).push(sd);
                }
                if (sd.toNode) {
                    if (!nodeToSegs.has(sd.toNode)) nodeToSegs.set(sd.toNode, []);
                    nodeToSegs.get(sd.toNode).push(sd);
                }
            }

            // Sortiere alle Segmente nach GPX-Position
            const sortedSegs = [...segmentMap.values()].sort((a, b) => a.gpxPos - b.gpxPos);

            // Finde Start-Segment (niedrigste GPX-Position)
            const startSeg = sortedSegs[0];
            if (!startSeg) return [...collectedIds];

            // Bestimme Start-Node (der Node der NICHT in GPX-Richtung zeigt)
            const startCoord = startSeg.coords[0];
            const endCoord = startSeg.coords[startSeg.coords.length - 1];
            const startGpxPos = findGpxPosition({ lat: startCoord[1], lon: startCoord[0] }, gpxPoints);
            const endGpxPos = findGpxPosition({ lat: endCoord[1], lon: endCoord[0] }, gpxPoints);

            // currentNode ist der Exit-Node (höhere GPX-Position)
            let currentNode = startGpxPos < endGpxPos ? startSeg.toNode : startSeg.fromNode;

            // Baue Kette
            const chain = [startSeg.id];
            const usedIds = new Set([startSeg.id]);
            let lastGpxPos = startSeg.gpxPos;

            for (let step = 0; step < 2000; step++) {
                // Finde nächstes Segment über currentNode
                const candidates = nodeToSegs.get(currentNode) || [];
                let bestNext = null;
                let bestScore = -Infinity;

                for (const cand of candidates) {
                    if (usedIds.has(cand.id)) continue;

                    // Score basiert auf:
                    // 1. GPX-Position muss vorwärts sein (höher als lastGpxPos)
                    // 2. Aber nicht zu weit springen
                    const posDiff = cand.gpxPos - lastGpxPos;

                    // Muss vorwärts sein (oder minimal rückwärts für Kurven)
                    if (posDiff < -50) continue; // Nicht mehr als 50m zurück

                    // Score: Vorwärts ist gut, aber nicht zu weit springen
                    let score = 100;
                    if (posDiff > 0 && posDiff < 500) {
                        score = 200 - posDiff * 0.1; // Nähere Segmente bevorzugen
                    } else if (posDiff >= 500) {
                        score = 50; // Weite Sprünge sind schlecht
                    }

                    if (score > bestScore) {
                        bestScore = score;
                        bestNext = cand;
                    }
                }

                if (!bestNext) {
                    // Keine direkte Verbindung - versuche Lücke zu überbrücken
                    // Suche das nächste Segment auf der GPX-Linie das noch nicht verwendet wurde
                    let nextOnGpx = null;
                    let minPosDiff = Infinity;

                    for (const sd of sortedSegs) {
                        if (usedIds.has(sd.id)) continue;
                        const posDiff = sd.gpxPos - lastGpxPos;
                        if (posDiff > 0 && posDiff < minPosDiff) {
                            minPosDiff = posDiff;
                            nextOnGpx = sd;
                        }
                    }

                    if (nextOnGpx && minPosDiff < 200) {
                        // Springe zum nächsten Segment (Lücke akzeptieren)
                        bestNext = nextOnGpx;
                        log(`GPX Prune: Lücke übersprungen (${Math.round(minPosDiff)}m)`);
                    } else {
                        break; // Keine Fortsetzung möglich
                    }
                }

                // Füge Segment hinzu
                chain.push(bestNext.id);
                usedIds.add(bestNext.id);
                lastGpxPos = bestNext.gpxPos;

                // Update currentNode
                if (bestNext.fromNode === currentNode) {
                    currentNode = bestNext.toNode;
                } else {
                    currentNode = bestNext.fromNode;
                }

                // Sind wir am Ende der GPX-Linie?
                const gpxTotalLen = gpxPoints.length > 1 ?
                    findGpxPosition(gpxPoints[gpxPoints.length-1], gpxPoints) : 0;
                if (lastGpxPos >= gpxTotalLen * 0.95) {
                    break;
                }
            }

            log(`GPX Prune: Kette mit ${chain.length} Segmenten (von ${segmentMap.size} geflooded)`);
            return chain;
        }

        // Finde Position auf GPX-Linie (kumulative Distanz in Metern)
        function findGpxPosition(pt, gpxPoints) {
            let cumDist = 0;
            let bestDist = Infinity;
            let bestPos = 0;

            for (let i = 0; i < gpxPoints.length - 1; i++) {
                const segLen = haversineDistance(
                    gpxPoints[i].lat, gpxPoints[i].lon,
                    gpxPoints[i+1].lat, gpxPoints[i+1].lon
                );

                // Projektion auf dieses Segment
                const proj = projectPointToSegment(
                    pt.lat, pt.lon,
                    gpxPoints[i].lat, gpxPoints[i].lon,
                    gpxPoints[i+1].lat, gpxPoints[i+1].lon
                );

                if (proj.dist < bestDist) {
                    bestDist = proj.dist;
                    bestPos = cumDist + proj.t * segLen;
                }

                cumDist += segLen;
            }

            return bestPos;
        }

        // Projiziere Punkt auf Liniensegment
        function projectPointToSegment(pLat, pLon, aLat, aLon, bLat, bLon) {
            const dLat = bLat - aLat;
            const dLon = bLon - aLon;
            const lenSq = dLat * dLat + dLon * dLon;

            let t = 0;
            if (lenSq > 0) {
                t = Math.max(0, Math.min(1, ((pLat - aLat) * dLat + (pLon - aLon) * dLon) / lenSq));
            }

            const projLat = aLat + t * dLat;
            const projLon = aLon + t * dLon;

            return {
                t: t,
                dist: haversineDistance(pLat, pLon, projLat, projLon)
            };
        }

        // Distanz von Punkt zu Segment (für Flood-Matching)
        function distToSegment(pt, coords) {
            let minD = Infinity;
            for (let i = 0; i < coords.length - 1; i++) {
                const d = pointToLineDist(pt.lat, pt.lon, coords[i][1], coords[i][0], coords[i+1][1], coords[i+1][0]);
                if (d < minD) minD = d;
            }
            return minD;
        }

        function pointToLineDist(pLat, pLon, aLat, aLon, bLat, bLon) {
            const dLat = bLat - aLat, dLon = bLon - aLon;
            const lenSq = dLat * dLat + dLon * dLon;
            const t = lenSq > 0 ? Math.max(0, Math.min(1, ((pLat - aLat) * dLat + (pLon - aLon) * dLon) / lenSq)) : 0;
            return haversineDistance(pLat, pLon, aLat + t * dLat, aLon + t * dLon);
        }

        function pointToSegmentDistanceSimple(point, coords) {
            let minDist = Infinity;
            for (let i = 0; i < coords.length - 1; i++) {
                const dist = pointToLineDistSimple(
                    point.lat, point.lon,
                    coords[i][1], coords[i][0],
                    coords[i+1][1], coords[i+1][0]
                );
                if (dist < minDist) minDist = dist;
            }
            return minDist;
        }

        function pointToLineDistSimple(pLat, pLon, aLat, aLon, bLat, bLon) {
            const dLat = bLat - aLat;
            const dLon = bLon - aLon;
            const lenSq = dLat * dLat + dLon * dLon;
            let t = lenSq > 0 ? Math.max(0, Math.min(1, ((pLat - aLat) * dLat + (pLon - aLon) * dLon) / lenSq)) : 0;
            const projLat = aLat + t * dLat;
            const projLon = aLon + t * dLon;
            return haversineDistance(pLat, pLon, projLat, projLon);
        }

        // Auswahl-Durchgang: Scrolle nochmal und wähle die geprunten Segmente aus
        function selectPrunedSegments() {
            const { scrollPositions, currentIndex, prunedIds } = gpxAutoScroll;

            if (currentIndex >= scrollPositions.length) {
                // Fertig!
                finishGpxSelection();
                return;
            }

            const pos = scrollPositions[currentIndex];

            // Scrollen
            try {
                const mercX = pos.lon * 20037508.34 / 180;
                const mercY = Math.log(Math.tan((90 + pos.lat) * Math.PI / 360)) / (Math.PI / 180) * 20037508.34 / 180;
                uWaze.map.setCenter({ lon: mercX, lat: mercY });
            } catch (e) {}

            // UI Update
            const ui = document.getElementById('gpx-autoscroll-ui');
            if (ui) {
                const total = scrollPositions.length;
                const percent = Math.round((currentIndex / total) * 100);
                const progressDiv = ui.querySelector('div > div > div:nth-child(2)');
                if (progressDiv) {
                    progressDiv.innerHTML = `<div style="background: #2196F3; height: 100%; width: ${percent}%;"></div>`;
                }
            }

            // Warte und wähle Segmente
            gpxAutoScroll.timeoutId = setTimeout(() => {
                // Finde und wähle Segmente
                const prunedSet = new Set(prunedIds);
                const segments = uWaze.model.segments.getObjectArray();
                const toAdd = [];

                for (const seg of segments) {
                    const id = seg.attributes?.id || seg.getID?.();
                    if (prunedSet.has(id)) {
                        toAdd.push(seg);
                    }
                }

                if (toAdd.length > 0) {
                    try {
                        const current = uWaze.selectionManager.getSelectedFeatures() || [];
                        const currentSegs = current.filter(f => f?.model?.type === 'segment').map(f => f.model);
                        const currentIds = new Set(currentSegs.map(s => s.attributes?.id));
                        const newSegs = toAdd.filter(s => !currentIds.has(s.attributes?.id));
                        if (newSegs.length > 0) {
                            uWaze.selectionManager.setSelectedModels([...currentSegs, ...newSegs]);
                        }
                    } catch (e) {}
                }

                gpxAutoScroll.currentIndex++;
                selectPrunedSegments();
            }, 500);
        }

        function finishGpxSelection() {
            const ui = document.getElementById('gpx-autoscroll-ui');
            if (ui) ui.remove();

            gpxAutoScroll.active = false;

            // Zähle ausgewählte Segmente
            let count = 0;
            try {
                const sel = uWaze.selectionManager.getSelectedFeatures() || [];
                count = sel.filter(f => f?.model?.type === 'segment').length;
            } catch (e) {}

            if (count > 0) {
                updateStatusMessage(t('importedGpx', count), '#4CAF50');
            } else {
                updateStatusMessage('Segmente konnten nicht ausgewählt werden', '#ff9800');
            }

            // GPX-Linie nach kurzer Zeit entfernen
            setTimeout(() => removeGpxLine(), 5000);
        }

        function showGpxFinishedUI(totalSegments) {
            let ui = document.getElementById('gpx-finished-ui');
            if (ui) ui.remove();

            ui = document.createElement('div');
            ui.id = 'gpx-finished-ui';
            ui.style.cssText = `
                position: fixed;
                top: 10px;
                left: 50%;
                transform: translateX(-50%);
                background: rgba(0,0,0,0.9);
                color: white;
                padding: 10px 14px;
                border-radius: 8px;
                z-index: 99999;
                font-family: Arial, sans-serif;
                box-shadow: 0 4px 15px rgba(0,0,0,0.4);
            `;

            ui.innerHTML = `
                <div style="display: flex; align-items: center; gap: 10px;">
                    <span style="font-size: 12px;">🗺️ GPX: ${totalSegments} Segmente</span>
                    <button id="gpx-reselect-btn" style="background: #4CAF50; color: white; border: none; padding: 5px 10px; border-radius: 4px; cursor: pointer; font-size: 11px;" title="Segmente im aktuellen Bereich auswählen">
                        Auswählen
                    </button>
                    <button id="gpx-clear-btn" style="background: #666; color: white; border: none; padding: 5px 8px; border-radius: 4px; cursor: pointer; font-size: 11px;" title="GPX-Linie entfernen">
                        ✕
                    </button>
                </div>
            `;

            document.body.appendChild(ui);

            document.getElementById('gpx-reselect-btn')?.addEventListener('click', () => {
                reselectGpxSegments();
            });

            document.getElementById('gpx-clear-btn')?.addEventListener('click', () => {
                removeGpxLine();
                ui.remove();
                window._gpxSegmentIds = null;
            });
        }

        function reselectGpxSegments() {
            const segmentIds = window._gpxSegmentIds;
            if (!segmentIds || segmentIds.size === 0) {
                updateStatusMessage('Keine GPX-Segmente gespeichert', '#ff9800');
                return;
            }

            const segments = uWaze.model.segments.getObjectArray();
            const toSelect = segments.filter(seg => {
                const segId = seg.attributes?.id || seg.getID?.();
                return segId && segmentIds.has(segId);
            });

            if (toSelect.length > 0) {
                // Zur bestehenden Auswahl hinzufügen
                try {
                    const currentSelection = uWaze.selectionManager.getSelectedFeatures() || [];
                    const currentSegments = currentSelection
                        .filter(f => f?.model?.type === 'segment')
                        .map(f => f.model);

                    const existingIds = new Set(currentSegments.map(s => s.attributes?.id || s.getID?.()));
                    const newSegments = toSelect.filter(s => {
                        const id = s.attributes?.id || s.getID?.();
                        return !existingIds.has(id);
                    });

                    uWaze.selectionManager.setSelectedModels([...currentSegments, ...newSegments]);
                    updateStatusMessage(`+${newSegments.length} Segmente ausgewählt`, '#4CAF50');
                } catch (e) {
                    uWaze.selectionManager.setSelectedModels(toSelect);
                    updateStatusMessage(`${toSelect.length} Segmente ausgewählt`, '#4CAF50');
                }
            } else {
                updateStatusMessage('Keine passenden Segmente im Bereich', '#ff9800');
            }
        }

        function cancelGpxAutoScroll() {
            if (gpxAutoScroll.timeoutId) {
                clearTimeout(gpxAutoScroll.timeoutId);
            }

            gpxAutoScroll.active = false;

            // GPX-Linie entfernen
            removeGpxLine();

            const ui = document.getElementById('gpx-autoscroll-ui');
            if (ui) ui.remove();

            updateStatusMessage('GPX Import abgebrochen', '#ff9800');
        }

        // Lokales Matching ohne UI-Update (für Auto-Scroll)
        // LÄNGENBASIERTER ANSATZ: Segment muss signifikante Strecke entlang GPX abdecken
        function matchGpxToSegmentsLocal(gpxPoints) {
            if (!uWaze?.model?.segments) return [];
            if (gpxPoints.length < 2) return [];

            const segments = uWaze.model.segments.getObjectArray();
            const MAX_DISTANCE = 15; // 15 Meter maximale Abweichung

            // Filtere gültige Segmente
            const validSegments = segments.filter(seg => {
                if (!seg?.attributes) return false;
                if (seg.isDeleted?.()) return false;
                const roadType = seg.attributes.roadType;
                return roadType && [1, 2, 3, 4, 6, 7, 17, 20, 21].includes(roadType);
            });

            if (validSegments.length === 0) return [];

            // Berechne GPX-Gesamtlänge und erstelle Lookup für schnelle Distanzprüfung
            let gpxTotalLength = 0;
            const gpxCumDist = [0];
            for (let i = 1; i < gpxPoints.length; i++) {
                const d = haversineDistance(
                    gpxPoints[i-1].lat, gpxPoints[i-1].lon,
                    gpxPoints[i].lat, gpxPoints[i].lon
                );
                gpxTotalLength += d;
                gpxCumDist.push(gpxTotalLength);
            }

            const matchedSegments = [];

            for (const seg of validSegments) {
                const segCoordsWGS = getSegmentCoordsWGS84(seg);
                if (!segCoordsWGS || segCoordsWGS.length < 2) continue;

                // Finde Start- und End-Position des Segments auf der GPX-Linie
                let minGpxPos = Infinity;
                let maxGpxPos = -Infinity;
                let allPointsClose = true;

                for (const segPt of segCoordsWGS) {
                    // Finde nächsten Punkt auf GPX-Linie und dessen Position
                    let bestDist = Infinity;
                    let bestGpxPos = -1;

                    for (let i = 0; i < gpxPoints.length - 1; i++) {
                        const result = pointToLineWithPosition(
                            segPt[1], segPt[0],
                            gpxPoints[i].lat, gpxPoints[i].lon,
                            gpxPoints[i+1].lat, gpxPoints[i+1].lon,
                            gpxCumDist[i], gpxCumDist[i+1]
                        );

                        if (result.dist < bestDist) {
                            bestDist = result.dist;
                            bestGpxPos = result.position;
                        }
                    }

                    if (bestDist > MAX_DISTANCE) {
                        allPointsClose = false;
                        break;
                    }

                    if (bestGpxPos < minGpxPos) minGpxPos = bestGpxPos;
                    if (bestGpxPos > maxGpxPos) maxGpxPos = bestGpxPos;
                }

                if (!allPointsClose) continue;

                // Berechne wie viel GPX-Strecke das Segment abdeckt
                const gpxCoverage = maxGpxPos - minGpxPos;

                // Berechne Segment-Länge
                let segLength = 0;
                for (let i = 1; i < segCoordsWGS.length; i++) {
                    segLength += haversineDistance(
                        segCoordsWGS[i-1][1], segCoordsWGS[i-1][0],
                        segCoordsWGS[i][1], segCoordsWGS[i][0]
                    );
                }

                // KRITISCH: Die GPX-Abdeckung muss mindestens 50% der Segment-Länge sein
                // Das filtert Segmente die quer zur Route liegen
                const coverageRatio = gpxCoverage / Math.max(segLength, 1);

                if (coverageRatio < 0.5) continue;

                // Zusätzlich: Segment muss mindestens 10m GPX-Strecke abdecken
                // (filtert sehr kurze Kreuzungssegmente)
                if (gpxCoverage < 10 && segLength > 20) continue;

                matchedSegments.push({
                    segment: seg,
                    gpxCoverage: gpxCoverage,
                    segLength: segLength,
                    coverageRatio: coverageRatio,
                    startPos: minGpxPos,
                    endPos: maxGpxPos
                });
            }

            // Sortiere nach GPX-Position (von Start nach Ende)
            matchedSegments.sort((a, b) => a.startPos - b.startPos);

            console.log(`[WME RTC Plus] GPX Length-Based: ${gpxPoints.length} Punkte → ${matchedSegments.length} Segmente`);

            return matchedSegments.map(m => m.segment);
        }

        // Punkt-zu-Linie mit Position auf der Linie
        function pointToLineWithPosition(pLat, pLon, aLat, aLon, bLat, bLon, aPos, bPos) {
            const dLat = bLat - aLat;
            const dLon = bLon - aLon;
            const lengthSq = dLat * dLat + dLon * dLon;

            let t = 0;
            if (lengthSq > 0) {
                t = Math.max(0, Math.min(1, ((pLat - aLat) * dLat + (pLon - aLon) * dLon) / lengthSq));
            }

            const projLat = aLat + t * dLat;
            const projLon = aLon + t * dLon;

            return {
                dist: haversineDistance(pLat, pLon, projLat, projLon),
                position: aPos + t * (bPos - aPos)
            };
        }

        // Finde ein Verbindungssegment zwischen zwei nicht direkt verbundenen Segmenten
        function findBridgeSegment(segA, segB, segmentsByNode, excludeIds) {
            const aFromNode = segA.attributes?.fromNodeID;
            const aToNode = segA.attributes?.toNodeID;
            const bFromNode = segB.attributes?.fromNodeID;
            const bToNode = segB.attributes?.toNodeID;

            // Suche Segmente die beide verbinden können
            const aNodes = [aFromNode, aToNode].filter(n => n);
            const bNodes = [bFromNode, bToNode].filter(n => n);

            for (const aNode of aNodes) {
                const connectedToA = segmentsByNode.get(aNode) || [];
                for (const bridge of connectedToA) {
                    const bridgeId = bridge.attributes?.id || bridge.getID?.();
                    if (excludeIds.has(bridgeId)) continue;

                    const bridgeFrom = bridge.attributes?.fromNodeID;
                    const bridgeTo = bridge.attributes?.toNodeID;

                    // Prüfe ob dieses Segment auch mit B verbunden ist
                    if (bNodes.includes(bridgeFrom) || bNodes.includes(bridgeTo)) {
                        return bridge;
                    }
                }
            }

            return null;
        }

        // Bestimme welcher Node der Exit-Node ist basierend auf GPX-Richtung
        function determineExitNode(segment, currentPoint, gpxPoints, currentIndex) {
            const fromNode = segment.attributes?.fromNodeID;
            const toNode = segment.attributes?.toNodeID;

            if (!fromNode || !toNode) return null;

            // Hole Segment-Koordinaten
            const segCoords = getSegmentCoordsWGS84(segment);
            if (!segCoords || segCoords.length < 2) return toNode;

            // Segment Start und Ende
            const segStart = segCoords[0];
            const segEnd = segCoords[segCoords.length - 1];

            // Finde nächsten GPX-Punkt (Richtung)
            let nextPoint = null;
            for (let i = currentIndex + 1; i < Math.min(currentIndex + 10, gpxPoints.length); i++) {
                const dist = haversineDistance(currentPoint.lat, currentPoint.lon, gpxPoints[i].lat, gpxPoints[i].lon);
                if (dist > 20) { // Mindestens 20m entfernt
                    nextPoint = gpxPoints[i];
                    break;
                }
            }

            if (!nextPoint) return toNode;

            // Welches Ende ist näher am nächsten GPX-Punkt?
            const distToStart = haversineDistance(nextPoint.lat, nextPoint.lon, segStart[1], segStart[0]);
            const distToEnd = haversineDistance(nextPoint.lat, nextPoint.lon, segEnd[1], segEnd[0]);

            // Der Exit-Node ist der, der näher am nächsten GPX-Punkt ist
            // Normalerweise: fromNode = Start, toNode = Ende
            return (distToEnd < distToStart) ? toNode : fromNode;
        }

        // Präzise Distanzberechnung wie im Hausnummern-Script mit OpenLayers
        function getDistanceToSegmentPrecise(point, segment) {
            try {
                // Versuche OpenLayers distanceTo für präzise Berechnung
                if (uOpenLayers?.Geometry?.Point && typeof segment.getOLGeometry === 'function') {
                    // GPX-Punkt zu Web Mercator konvertieren
                    const mercX = point.lon * 20037508.34 / 180;
                    const mercY = Math.log(Math.tan((90 + point.lat) * Math.PI / 360)) / (Math.PI / 180) * 20037508.34 / 180;

                    const olPoint = new uOpenLayers.Geometry.Point(mercX, mercY);
                    const segOLGeom = segment.getOLGeometry();

                    if (segOLGeom) {
                        const distResult = olPoint.distanceTo(segOLGeom, { details: true });
                        if (distResult && distResult.x1 !== undefined && distResult.y1 !== undefined) {
                            // Konvertiere zurück zu WGS84 für Haversine-Distanz
                            const closestLon = distResult.x1 * 180 / 20037508.34;
                            let closestLat = distResult.y1 * 180 / 20037508.34;
                            closestLat = 180 / Math.PI * (2 * Math.atan(Math.exp(closestLat * Math.PI / 180)) - Math.PI / 2);

                            return haversineDistance(point.lat, point.lon, closestLat, closestLon);
                        }
                    }
                }
            } catch (e) {
                // Fallback auf manuelle Berechnung
            }

            // Fallback: Manuelle WGS84 Distanzberechnung
            return getDistanceToSegmentFast(point, segment);
        }

        // Berechne Bearing zwischen zwei Punkten
        function calculateBearing(lat1, lon1, lat2, lon2) {
            const dLon = (lon2 - lon1) * Math.PI / 180;
            const lat1Rad = lat1 * Math.PI / 180;
            const lat2Rad = lat2 * Math.PI / 180;

            const y = Math.sin(dLon) * Math.cos(lat2Rad);
            const x = Math.cos(lat1Rad) * Math.sin(lat2Rad) - Math.sin(lat1Rad) * Math.cos(lat2Rad) * Math.cos(dLon);

            let bearing = Math.atan2(y, x) * 180 / Math.PI;
            return (bearing + 360) % 360; // Normalisiere zu 0-360°
        }

        // Berechne Bearing eines Segments
        function getSegmentBearing(segment) {
            const segCoords = getSegmentCoordsWGS84(segment);
            if (!segCoords || segCoords.length < 2) return null;

            const start = segCoords[0];
            const end = segCoords[segCoords.length - 1];

            return calculateBearing(start[1], start[0], end[1], end[0]);
        }

        // Schnelle Distanzberechnung (Fallback)
        function getDistanceToSegmentFast(point, seg) {
            const segCoords = getSegmentCoordsWGS84(seg);
            if (!segCoords || segCoords.length < 2) return Infinity;

            let minDist = Infinity;
            for (let j = 0; j < segCoords.length - 1; j++) {
                const d = pointToSegmentHaversine(
                    point.lon, point.lat,
                    segCoords[j][0], segCoords[j][1],
                    segCoords[j+1][0], segCoords[j+1][1]
                );
                if (d < minDist) minDist = d;
            }
            return minDist;
        }

        // Haversine Distanz in Metern
        function haversineDistance(lat1, lon1, lat2, lon2) {
            const R = 6371000; // Erdradius in Metern
            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);
            return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
        }

        // Punkt-zu-Segment Distanz mit Haversine (wie im Hausnummern-Script)
        function pointToSegmentHaversine(px, py, ax, ay, bx, by) {
            const dx = bx - ax;
            const dy = by - ay;
            const lengthSq = dx * dx + dy * dy;

            let t = 0;
            if (lengthSq > 0) {
                t = Math.max(0, Math.min(1, ((px - ax) * dx + (py - ay) * dy) / lengthSq));
            }

            const projX = ax + t * dx;
            const projY = ay + t * dy;

            return haversineDistance(py, px, projY, projX);
        }

        // Einfache Distanzberechnung: Punkt zu Liniensegment in Metern
        function distPointToSegment(px, py, x1, y1, x2, y2) {
            const dx = x2 - x1;
            const dy = y2 - y1;
            const lenSq = dx * dx + dy * dy;

            let t = 0;
            if (lenSq > 0) {
                t = ((px - x1) * dx + (py - y1) * dy) / lenSq;
                t = Math.max(0, Math.min(1, t));
            }

            const nearestX = x1 + t * dx;
            const nearestY = y1 + t * dy;

            // Haversine-ähnliche Distanz in Metern
            const dLon = (px - nearestX) * 111320 * Math.cos(py * Math.PI / 180);
            const dLat = (py - nearestY) * 110540;

            return Math.sqrt(dLon * dLon + dLat * dLat);
        }

        function matchGpxToSegments(gpxPoints) {
            if (!uWaze?.model?.segments) {
                updateStatusMessage('Keine Segmente verfügbar', '#f44336');
                return;
            }

            const segments = uWaze.model.segments.getObjectArray();
            log(`GPX Matching: ${gpxPoints.length} Punkte, ${segments.length} Segmente verfügbar`);

            if (segments.length === 0) {
                updateStatusMessage('Keine Segmente geladen - bitte näher zoomen', '#f44336');
                return;
            }

            // Verwende die verbesserte lokale Matching-Funktion
            const matchedSegments = matchGpxToSegmentsLocal(gpxPoints);

            log(`GPX: ${matchedSegments.length} Segmente gematcht`);

            if (matchedSegments.length === 0) {
                updateStatusMessage(t('gpxNoSegments') + ` (${gpxPoints.length} Punkte)`, '#ff9800');
                return;
            }

            const segmentArray = matchedSegments;
            uWaze.selectionManager.setSelectedModels(segmentArray);

            updateStatusMessage(t('importedGpx', matchedSegments.size), '#4CAF50');
        }

        // Winkel auf -180 bis 180 normalisieren
        function normalizeAngle(angle) {
            while (angle > 180) angle -= 360;
            while (angle < -180) angle += 360;
            return angle;
        }

        // Segment-Koordinaten in WGS84 extrahieren
        function getSegmentCoordsWGS84(segment) {
            const geom = segment.getOLGeometry?.() || segment.geometry || segment.attributes?.geometry;
            if (!geom) return null;

            const toWGS84 = (x, y) => {
                if (Math.abs(x) <= 180 && Math.abs(y) <= 90) return [x, y];
                const lon = x * 180 / 20037508.34;
                let lat = y * 180 / 20037508.34;
                lat = 180 / Math.PI * (2 * Math.atan(Math.exp(lat * Math.PI / 180)) - Math.PI / 2);
                return [lon, lat];
            };

            let coords = [];

            if (geom.components && geom.components.length > 0) {
                coords = geom.components.map(c => toWGS84(c.x, c.y));
            } else if (geom.getVertices) {
                coords = geom.getVertices().map(v => toWGS84(v.x, v.y));
            } else if (geom.coordinates && Array.isArray(geom.coordinates)) {
                if (Array.isArray(geom.coordinates[0])) {
                    coords = geom.coordinates.map(c => [c[0], c[1]]);
                }
            }

            return coords.length >= 2 ? coords : null;
        }

        // Robuste Distanz-Berechnung zu einem Segment
        function getDistanceToSegment(pointLon, pointLat, segment) {
            // Methode 1: OpenLayers distanceTo (präziseste Methode)
            try {
                // GPX-Punkt zu Web Mercator konvertieren
                const mercX = pointLon * 20037508.34 / 180;
                const mercY = Math.log(Math.tan((90 + pointLat) * Math.PI / 360)) / (Math.PI / 180) * 20037508.34 / 180;

                if (uOpenLayers?.Geometry?.Point) {
                    const olPoint = new uOpenLayers.Geometry.Point(mercX, mercY);
                    const segGeom = segment.getOLGeometry?.();

                    if (segGeom && olPoint.distanceTo) {
                        const distResult = olPoint.distanceTo(segGeom, { details: true });

                        if (distResult && distResult.x1 !== undefined) {
                            // Nächster Punkt auf Segment (Web Mercator)
                            const closestLon = distResult.x1 * 180 / 20037508.34;
                            let closestLat = distResult.y1 * 180 / 20037508.34;
                            closestLat = 180 / Math.PI * (2 * Math.atan(Math.exp(closestLat * Math.PI / 180)) - Math.PI / 2);

                            // Haversine Distanz
                            return haversineDistance(pointLat, pointLon, closestLat, closestLon);
                        }
                    }
                }
            } catch (e) {
                // Fallback verwenden
            }

            // Methode 2: Manuelle Berechnung
            return manualDistanceToSegment(pointLon, pointLat, segment);
        }

        // Manuelle Distanz-Berechnung als Fallback
        function manualDistanceToSegment(pointLon, pointLat, segment) {
            // Segment-Koordinaten extrahieren
            let coords = [];

            // Versuche verschiedene Wege die Geometrie zu bekommen
            const geom = segment.getOLGeometry?.() || segment.geometry || segment.attributes?.geometry;

            if (!geom) return Infinity;

            // Web Mercator zu WGS84
            const toWGS84 = (x, y) => {
                // Prüfe ob bereits WGS84 (kleine Zahlen)
                if (Math.abs(x) <= 180 && Math.abs(y) <= 90) {
                    return [x, y];
                }
                // Web Mercator konvertieren
                const lon = x * 180 / 20037508.34;
                let lat = y * 180 / 20037508.34;
                lat = 180 / Math.PI * (2 * Math.atan(Math.exp(lat * Math.PI / 180)) - Math.PI / 2);
                return [lon, lat];
            };

            // Koordinaten aus verschiedenen Formaten extrahieren
            if (geom.components && geom.components.length > 0) {
                // OpenLayers LineString
                coords = geom.components.map(c => toWGS84(c.x, c.y));
            } else if (geom.getVertices) {
                // OpenLayers mit getVertices
                const verts = geom.getVertices();
                coords = verts.map(v => toWGS84(v.x, v.y));
            } else if (geom.coordinates && Array.isArray(geom.coordinates)) {
                // GeoJSON Format
                if (Array.isArray(geom.coordinates[0])) {
                    // [[lon, lat], [lon, lat], ...]
                    coords = geom.coordinates.map(c => [c[0], c[1]]);
                }
            }

            if (coords.length < 2) return Infinity;

            // Minimale Distanz zu allen Liniensegmenten
            let minDist = Infinity;

            for (let i = 0; i < coords.length - 1; i++) {
                const dist = pointToLineDistance(
                    pointLon, pointLat,
                    coords[i][0], coords[i][1],
                    coords[i+1][0], coords[i+1][1]
                );
                if (dist < minDist) minDist = dist;
            }

            return minDist;
        }

        // Punkt-zu-Linie Distanz (WGS84 Koordinaten, Ergebnis in Metern)
        function pointToLineDistance(px, py, ax, ay, bx, by) {
            // Projektion des Punktes auf die Linie
            const dx = bx - ax;
            const dy = by - ay;
            const lengthSq = dx * dx + dy * dy;

            let t = 0;
            if (lengthSq > 0) {
                t = Math.max(0, Math.min(1, ((px - ax) * dx + (py - ay) * dy) / lengthSq));
            }

            const projX = ax + t * dx;
            const projY = ay + t * dy;

            return haversineDistance(py, px, projY, projX);
        }

        // Haversine Distanz in Metern
        function haversineDistance(lat1, lon1, lat2, lon2) {
            const R = 6371000; // Erdradius in Metern
            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);
            return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
        }

        function deleteRtcsInCircles() {
            if (!radiusLayer || !radiusLayer.features || radiusLayer.features.length === 0) {
                updateStatusMessage('Erst einen Kreis zeichnen', '#ff9800');
                return;
            }

            updateStatusMessage(t('deletingRtcs'), '#2196F3');
            log('Suche RTCs im markierten Bereich...');

            const segments = uWaze.model.segments.getObjectArray();
            const segmentsInCircle = [];

            // Finde alle Segmente im Kreis
            for (const segment of segments) {
                if (!segment.attributes?.roadType) continue;

                const segGeom = segment.getOLGeometry ? segment.getOLGeometry() : segment.geometry;
                if (!segGeom) continue;

                for (const drawnFeature of radiusLayer.features) {
                    if (drawnFeature.geometry.intersects(segGeom)) {
                        segmentsInCircle.push(segment);
                        break;
                    }
                }
            }

            if (segmentsInCircle.length === 0) {
                updateStatusMessage(t('noRtcsFound'), '#ff9800');
                return;
            }

            log(`${segmentsInCircle.length} Segmente im Bereich gefunden`);

            // Finde alle RTCs auf diesen Segmenten
            const closuresToDelete = [];
            const segmentIds = new Set(segmentsInCircle.map(s => s.getID()));
            const now = Date.now();

            // Hole alle Closures aus dem Model
            if (W.model.roadClosures) {
                const allClosures = W.model.roadClosures.getObjectArray();
                log(`Prüfe ${allClosures.length} Closures insgesamt...`);

                for (const closure of allClosures) {
                    if (closure.attributes && segmentIds.has(closure.attributes.segID)) {
                        // Debug: Zeige Closure-Daten
                        const attrs = closure.attributes;
                        log(`Closure auf Segment ${attrs.segID}: Start=${attrs.startDate}, Ende=${attrs.endDate}`);

                        // Nur aktive/zukünftige RTCs (nicht abgelaufene)
                        // endDate kann ein String oder Timestamp sein
                        let endTime;
                        if (attrs.endDate) {
                            endTime = typeof attrs.endDate === 'number' ? attrs.endDate : new Date(attrs.endDate).getTime();
                        }

                        if (!endTime || endTime > now) {
                            closuresToDelete.push(closure);
                            log(`  → Wird gelöscht (aktiv/zukünftig)`);
                        } else {
                            log(`  → Übersprungen (abgelaufen, Ende war ${new Date(endTime).toLocaleString()})`);
                        }
                    }
                }
            }

            if (closuresToDelete.length === 0) {
                updateStatusMessage(t('noRtcsFound'), '#ff9800');
                return;
            }

            log(`${closuresToDelete.length} RTCs zum Löschen gefunden`);

            // Prüfe ob WMEAC verfügbar ist
            if (typeof WMEAC !== 'undefined' && WMEAC.removeClosure) {
                log('Using WMEAC.removeClosure...');
                WMEAC.removeClosure(closuresToDelete,
                    () => {
                        log(`✓ ${closuresToDelete.length} RTCs gelöscht`);
                        updateStatusMessage(t('rtcsDeleted', closuresToDelete.length), '#4CAF50');
                    },
                    (err) => {
                        warn('WMEAC Lösch-Fehler:', err);
                        updateStatusMessage('Fehler beim Löschen', '#f44336');
                    }
                );
                return;
            }

            // Fallback: RTCs einzeln löschen um Fehler bei abgelaufenen zu umgehen
            try {
                const cab = require("Waze/Modules/Closures/Models/ClosureActionBuilder");
                const sc = require("Waze/Modules/Closures/Models/SharedClosure");

                let deletedCount = 0;
                let errorCount = 0;
                let currentIndex = 0;

                const deleteNext = () => {
                    if (currentIndex >= closuresToDelete.length) {
                        // Fertig
                        if (deletedCount > 0) {
                            updateStatusMessage(t('rtcsDeleted', deletedCount) + (errorCount > 0 ? ` (${errorCount} Fehler)` : ''), deletedCount > 0 ? '#4CAF50' : '#ff9800');
                        } else {
                            updateStatusMessage('Keine RTCs gelöscht (alle abgelaufen?)', '#ff9800');
                        }
                        return;
                    }

                    const closure = closuresToDelete[currentIndex];
                    currentIndex++;

                    const seg = W.model.segments.getObjectById(closure.attributes.segID);
                    if (!seg) {
                        errorCount++;
                        deleteNext();
                        return;
                    }

                    W.selectionManager.setSelectedModels([seg]);

                    const sclo = new sc(
                        {
                            segments: [seg],
                            closures: [closure],
                            reverseSegments: {}
                        },
                        {
                            dataModel: W.model,
                            segmentSelection: W.selectionManager.getSegmentSelection(),
                            isNew: true
                        }
                    );

                    const action = cab.delete(W.model, sclo);

                    W.controller.save({ actions: [action] }).then(() => {
                        deletedCount++;
                        log(`✓ RTC ${currentIndex}/${closuresToDelete.length} gelöscht`);
                        updateStatusMessage(`Lösche RTCs... ${currentIndex}/${closuresToDelete.length}`, '#2196F3');
                        deleteNext();
                    }).catch((err) => {
                        // Bei Fehler (z.B. abgelaufen) weitermachen
                        errorCount++;
                        log(`✗ RTC ${currentIndex} Fehler: ${err.errors?.[0]?.attributes?.details || err.message}`);
                        deleteNext();
                    });
                };

                // Starte das Löschen
                updateStatusMessage(`Lösche RTCs... 0/${closuresToDelete.length}`, '#2196F3');
                deleteNext();

            } catch (e) {
                warn('Fehler beim RTC-Löschen:', e);
                updateStatusMessage('Fehler: ' + e.message, '#f44336');
            }
        }

        function shouldBlockSegmentInbound(segment, segGeom, circleCenter, radius) {
            try {
                const coords = segGeom.components || segGeom.getVertices?.();

                if (!coords?.length || coords.length < 2) {
                    warn(`Invalid coordinates for segment ${segment.getID()}`);
                    return false;
                }

                // Punkt A (Start) und Punkt B (Ende) des Segments
                const startPoint = coords[0];
                const endPoint = coords[coords.length - 1];

                if (!startPoint?.x || !endPoint?.x || !circleCenter?.x) {
                    warn(`Invalid point data for segment ${segment.getID()}`);
                    return false;
                }

                // Distanz von A und B zum Kreismittelpunkt berechnen
                const startDist = Math.hypot(
                    startPoint.x - circleCenter.x,
                    startPoint.y - circleCenter.y
                );
                const endDist = Math.hypot(
                    endPoint.x - circleCenter.x,
                    endPoint.y - circleCenter.y
                );

                // Toleranz für Randberührung (5% des Radius)
                const tolerance = radius * 0.05;

                // Prüfe ob Punkte innen oder außen sind
                const startInside = startDist < (radius - tolerance);
                const endInside = endDist < (radius - tolerance);
                const startOnEdge = Math.abs(startDist - radius) <= tolerance;
                const endOnEdge = Math.abs(endDist - radius) <= tolerance;

                let blockDirection;

                if (startInside && !endInside) {
                    // A ist innen, B ist außen → Einfahrt ist B→A, also B→A sperren
                    blockDirection = 'B→A';
                } else if (!startInside && endInside) {
                    // A ist außen, B ist innen → Einfahrt ist A→B, also A→B sperren
                    blockDirection = 'A→B';
                } else if (startOnEdge || endOnEdge) {
                    // Mindestens ein Punkt liegt auf dem Rand
                    // Der Punkt der weiter vom Zentrum entfernt ist, ist die "Außenseite"
                    if (startDist > endDist) {
                        // A ist weiter außen → Einfahrt von A-Seite → A→B sperren
                        blockDirection = 'A→B';
                    } else {
                        // B ist weiter außen → Einfahrt von B-Seite → B→A sperren
                        blockDirection = 'B→A';
                    }
                } else if (!startInside && !endInside) {
                    // Beide außen aber Segment kreuzt den Kreis
                    // Der Punkt der näher am Zentrum ist, ist die "Einfahrtsseite"
                    if (startDist < endDist) {
                        // A ist näher am Zentrum → Einfahrt von A-Seite → B→A sperren
                        blockDirection = 'B→A';
                    } else {
                        // B ist näher am Zentrum → Einfahrt von B-Seite → A→B sperren
                        blockDirection = 'A→B';
                    }
                } else {
                    // Beide klar innen - sollte nicht als Perimeter-Segment erkannt werden
                    // Aber wir behandeln es trotzdem: der weiter außen liegende Punkt bestimmt die Richtung
                    if (startDist > endDist) {
                        blockDirection = 'A→B';
                    } else {
                        blockDirection = 'B→A';
                    }
                    log(`Segment ${segment.getID()}: Beide Punkte nah am Rand, verwende Distanz-Logik → ${blockDirection}`);
                }

                // Richtungsinfo speichern für spätere Verwendung
                segment._circleDirectionInfo = {
                    startPoint, endPoint, startDist, endDist, circleCenter,
                    blockDirection: blockDirection
                };

                log(`Segment ${segment.getID()}: A-Dist=${Math.round(startDist)}m, B-Dist=${Math.round(endDist)}m → Sperre ${blockDirection}`);

                return true;
            } catch (error) {
                warn(`Error in shouldBlockSegmentInbound for segment ${segment.getID()}:`, error);
                return false;
            }
        }

        function applyDirectionRestrictions(segments) {
            log(`=== Perimeter-Sperrung: ${segments.length} Segmente ===`);

            // Segmente selektieren
            uWaze.selectionManager.setSelectedModels(segments);

            // Modus prüfen: Dauer oder Enddatum
            const useDuration = document.querySelector('#rtc-mode-duration')?.checked !== false;

            const now = new Date();
            let end;

            if (useDuration) {
                // Dauer aus UI lesen
                const durationDays = parseInt(document.querySelector('#rtc-duration-days')?.value) || 0;
                const durationHours = parseInt(document.querySelector('#rtc-duration-hours')?.value) || 24;
                const durationMinutes = parseInt(document.querySelector('#rtc-duration-minutes')?.value) || 0;
                const totalMinutes = (durationDays * 24 * 60) + (durationHours * 60) + durationMinutes;

                if (totalMinutes <= 0) {
                    updateStatusMessage('Bitte Dauer eingeben', '#ff9800');
                    return;
                }

                end = new Date(now.getTime() + totalMinutes * 60 * 1000);
                log(`RTC Dauer: ${durationDays}d ${durationHours}h ${durationMinutes}m`);
            } else {
                // Enddatum aus UI lesen
                const endDateStr = document.querySelector('#rtc-end-date')?.value;
                const endTimeStr = document.querySelector('#rtc-end-time')?.value || '23:59';

                if (!endDateStr) {
                    updateStatusMessage('Bitte Enddatum eingeben', '#ff9800');
                    return;
                }

                end = new Date(`${endDateStr}T${endTimeStr}`);

                if (end <= now) {
                    updateStatusMessage('Enddatum muss in der Zukunft liegen', '#ff9800');
                    return;
                }

                log(`RTC Enddatum: ${endDateStr} ${endTimeStr}`);
            }

            const formatDate = (d) => {
                const pad = (n) => ('0' + n).slice(-2);
                return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`;
            };
            const startStr = formatDate(now);
            const endStr = formatDate(end);

            log(`Start: ${startStr}, Ende: ${endStr}`);

            // Segmente nach Richtung gruppieren
            const segmentsAtoB = [];
            const segmentsBtoA = [];

            for (const segment of segments) {
                try {
                    const dirInfo = segment._circleDirectionInfo;
                    if (!dirInfo) {
                        warn(`Segment ${segment.getID()}: Keine Richtungsinfo`);
                        continue;
                    }

                    if (dirInfo.blockDirection === 'A→B') {
                        // A→B sperren (direction = A_TO_B = 1)
                        segmentsAtoB.push(segment);
                        log(`✓ Segment ${segment.getID()}: RTC sperrt A→B`);
                    } else {
                        // B→A sperren (direction = B_TO_A = 2)
                        segmentsBtoA.push(segment);
                        log(`✓ Segment ${segment.getID()}: RTC sperrt B→A`);
                    }

                    delete segment._circleDirectionInfo;

                } catch (error) {
                    warn('Error:', error);
                }
            }

            // Closures über WME Action System erstellen
            try {
                // Prüfe ob WMEAC (Advanced Closures) verfügbar ist
                if (typeof WMEAC !== 'undefined' && WMEAC.addClosure) {
                    log('Using WMEAC.addClosure...');

                    const allSegs = [...segmentsAtoB, ...segmentsBtoA];
                    W.selectionManager.setSelectedModels(allSegs);

                    let successCount = 0;
                    let errorCount = 0;
                    const totalExpected = (segmentsAtoB.length > 0 ? 1 : 0) + (segmentsBtoA.length > 0 ? 1 : 0);

                    const checkDone = () => {
                        if (successCount + errorCount >= totalExpected) {
                            if (successCount > 0) {
                                updateStatusMessage(`✓ RTCs erstellt`, '#4CAF50');
                            } else {
                                updateStatusMessage(`Fehler beim Erstellen`, '#f44336');
                            }
                        }
                    };

                    if (segmentsAtoB.length > 0) {
                        W.selectionManager.setSelectedModels(segmentsAtoB);
                        WMEAC.addClosure({
                            segments: segmentsAtoB,
                            reason: "Perimeter-Sperrung",
                            direction: 1,
                            startDate: startStr,
                            endDate: endStr,
                            location: "",
                            permanent: false
                        }, () => { successCount++; checkDone(); }, (err) => { warn('A→B Error:', err); errorCount++; checkDone(); });
                    }

                    if (segmentsBtoA.length > 0) {
                        setTimeout(() => {
                            W.selectionManager.setSelectedModels(segmentsBtoA);
                            WMEAC.addClosure({
                                segments: segmentsBtoA,
                                reason: "Perimeter-Sperrung",
                                direction: 2,
                                startDate: startStr,
                                endDate: endStr,
                                location: "",
                                permanent: false
                            }, () => { successCount++; checkDone(); }, (err) => { warn('B→A Error:', err); errorCount++; checkDone(); });
                        }, 1000);
                    }

                    return;
                }

                // Fallback: Eigene Implementation
                log('WMEAC not available, using internal implementation...');

                const cab = require("Waze/Modules/Closures/Models/ClosureActionBuilder");
                const sc = require("Waze/Modules/Closures/Models/SharedClosure");

                if (segmentsAtoB.length === 0 && segmentsBtoA.length === 0) {
                    updateStatusMessage('Keine RTCs erstellt', '#ff9800');
                    return;
                }

                const createClosureForSegments = (segs, direction) => {
                    if (segs.length === 0) return null;

                    log(`Creating closure for ${segs.length} segments, direction=${direction}`);
                    log(`Segment IDs: ${segs.map(s => s.getID()).join(', ')}`);

                    // Segmente für diese Richtung selektieren
                    W.selectionManager.setSelectedModels(segs);

                    const closureDetails = {
                        closures: [],
                        attributions: [],
                        reason: "Perimeter-Sperrung" + String.fromCharCode(160),
                        direction: direction,
                        startDate: startStr,
                        endDate: endStr,
                        location: "",
                        permanent: false,
                        segments: segs,
                        closuresType: 'roadClosure',
                        reverseSegments: {}
                    };

                    log(`Closure details:`, JSON.stringify({
                        reason: closureDetails.reason,
                        direction: closureDetails.direction,
                        startDate: closureDetails.startDate,
                        endDate: closureDetails.endDate,
                        segmentCount: closureDetails.segments.length
                    }));

                    const closure = new sc(closureDetails, {
                        dataModel: W.model,
                        segmentSelection: W.selectionManager.getSegmentSelection(),
                        isNewClosure: true,
                        closedNodesMap: {}
                    });

                    log(`Closure created, closureNodes: ${closure.closureNodes?.models?.length || 0}`);

                    // ClosureNodes setzen (wie in Advanced Closures)
                    if (closure.closureNodes?.models) {
                        for (const n of closure.closureNodes.models) {
                            n.attributes.isClosed = false;
                        }
                    }

                    return cab.add(closure, W.loginManager.user, W.model);
                };

                // Sequentiell speichern um Konflikte zu vermeiden
                const saveWithCallback = (segs, direction, dirName, onSuccess, onError) => {
                    if (segs.length === 0) {
                        onSuccess(0);
                        return;
                    }

                    const action = createClosureForSegments(segs, direction);
                    if (!action) {
                        onSuccess(0);
                        return;
                    }

                    const t = { actions: [action] };

                    W.controller.save(t).then(function() {
                        log(`✓ ${segs.length} ${dirName} RTCs gespeichert`);
                        onSuccess(segs.length);
                    }).catch(function(err) {
                        warn(`${dirName} Save Error:`, err);
                        let errorDetails = '';
                        if (err.errors) {
                            err.errors.forEach(e => {
                                warn(`Error details:`, JSON.stringify(e.attributes || e));
                                if (e.attributes?.details) {
                                    errorDetails = e.attributes.details;
                                }
                            });
                        }

                        // Spezifische Fehlermeldung für überlappende RTCs
                        if (errorDetails.includes('overlapped')) {
                            err.userMessage = 'RTC existiert bereits für diesen Zeitraum! Bitte erst bestehende RTCs löschen.';
                        }

                        onError(err);
                    });
                };

                // Erst A→B, dann B→A
                saveWithCallback(segmentsAtoB, 1, 'A→B',
                    function(countA) {
                        // A→B erfolgreich, jetzt B→A
                        saveWithCallback(segmentsBtoA, 2, 'B→A',
                            function(countB) {
                                const total = countA + countB;
                                if (total > 0) {
                                    updateStatusMessage(`✓ ${total} RTCs erstellt`, '#4CAF50');
                                } else {
                                    updateStatusMessage('Keine RTCs erstellt', '#ff9800');
                                }
                            },
                            function(err) {
                                // B→A fehlgeschlagen, aber A→B war erfolgreich
                                if (countA > 0) {
                                    updateStatusMessage(`⚠ ${countA} RTCs erstellt, B→A fehlgeschlagen`, '#ff9800');
                                } else {
                                    let details = '';
                                    if (err.errors) {
                                        err.errors.forEach(e => {
                                            if (e.attributes?.details) details += e.attributes.details + ' ';
                                        });
                                    }
                                    updateStatusMessage(`Fehler: ${details || err.message || 'Unbekannt'}`, '#f44336');
                                }
                            }
                        );
                    },
                    function(err) {
                        // A→B fehlgeschlagen
                        let details = err.userMessage || '';
                        if (!details && err.errors) {
                            err.errors.forEach(e => {
                                if (e.attributes?.details) details += e.attributes.details + ' ';
                            });
                        }
                        updateStatusMessage(`Fehler: ${details || err.message || 'Unbekannt'}`, '#f44336');
                    }
                );

            } catch (e) {
                warn('Action System Fehler:', e);
                updateStatusMessage('Action System nicht verfügbar', '#f44336');
            }
        }

        function undoLastCircle() {
            try {
                // 1. Prüfe ob Brush-Modus aktiv und History vorhanden
                if (brushModeActive && brushHistory && brushHistory.length > 0) {
                    if (undoBrushSelection()) {
                        return;
                    }
                }

                // 2. Prüfe ob es Kreise zum Entfernen gibt
                if (radiusLayer && radiusLayer.features && radiusLayer.features.length > 0) {
                    const lastFeature = radiusLayer.features[radiusLayer.features.length - 1];

                    if (lastFeature) {
                        radiusLayer.removeFeatures([lastFeature]);

                        if (radiusLayer.redraw) radiusLayer.redraw();

                        updateAllAnnotations();

                        const remaining = radiusLayer.features.length;
                        updateStatusMessage(`✓ ${t('removedCircle')} (${remaining} ${t('remaining')})`, '#2196F3');
                        return;
                    }
                }

                // 3. Keine Kreise - versuche WME Undo
                if (W.controller && W.controller.undo) {
                    W.controller.undo();
                    updateStatusMessage('↩️ WME', '#2196F3');
                } else if (W.model && W.model.actionManager) {
                    W.model.actionManager.undo();
                    updateStatusMessage('↩️ WME', '#2196F3');
                } else {
                    updateStatusMessage(t('noCirclesToUndo'), '#ff9800');
                }
            } catch (error) {
                warn('Error undoing:', error);
                updateStatusMessage(t('errorRemoving'), '#f44336');
            }
        }

        function clearAllCircles() {
            try {
                // Deactivate controls first
                if (polygonControl) polygonControl.deactivate();
                if (freehandControl) freehandControl.deactivate();
                if (dragControl) dragControl.deactivate();

                // Clear checkboxes
                if (checkbox) checkbox.checked = false;
                if (freehandCheckbox) freehandCheckbox.checked = false;
                if (moveCheckbox) moveCheckbox.checked = false;

                // Remove features one by one (more reliable than destroyFeatures)
                if (infoLayer && infoLayer.features) {
                    const infoFeatures = [...infoLayer.features];
                    infoFeatures.forEach(f => {
                        try {
                            infoLayer.removeFeatures([f]);
                        } catch (e) {
                            log('Error removing info feature');
                        }
                    });
                }

                if (radiusLayer && radiusLayer.features) {
                    const radiusFeatures = [...radiusLayer.features];
                    radiusFeatures.forEach(f => {
                        try {
                            radiusLayer.removeFeatures([f]);
                        } catch (e) {
                            log('Error removing radius feature');
                        }
                    });
                }

                // Force redraw
                if (infoLayer && infoLayer.redraw) infoLayer.redraw();
                if (radiusLayer && radiusLayer.redraw) radiusLayer.redraw();

            } catch (error) {
                warn('Error clearing circles:', error);
            }
        }
    }

    function radiusInit() {
        radiusLayer = new uOpenLayers.Layer.Vector("WME Circle Control Layer", {
            displayInLayerSwitcher: true,
            uniqueName: "__CircleControlLayer",
            visibility: true,
            style: {
                "fillColor": "#c40606",
                "fillOpacity": 0.2,
                "strokeColor": "#c40606",
                "strokeOpacity": 1,
                "strokeWidth": 1,
                "strokeLinecap": "round",
                "strokeDashstyle": "solid",
                "pointRadius": 6,
                "pointerEvents": "visiblePainted",
                "labelAlign": "cm",
                "labelOutlineColor": "white",
                "labelOutlineWidth": 3
            }
        });

        infoLayer = new uOpenLayers.Layer.Vector("WME Circle Visual Layer", {
            displayInLayerSwitcher: true,
            uniqueName: "__DrawCircleDisplayLayer",
            visibility: true
        });

        let polygonHandler = uOpenLayers.Handler.RegularPolygon;
        polygonControl = new uOpenLayers.Control.DrawFeature(radiusLayer, polygonHandler, {
            handlerOptions: {
                sides: 100
            }
        });

        let polygonHandlerFreehand = uOpenLayers.Handler.Polygon;
        freehandControl = new uOpenLayers.Control.DrawFeature(radiusLayer, polygonHandlerFreehand, {
            handlerOptions: {
                freehand: true,
                freehandToggle: null
            }
        });

        // Drag control is created in setupSidebarContent via setupDragControl()

        uWaze.map.addLayer(radiusLayer);
        uWaze.map.addLayer(infoLayer);
        uWaze.map.addControl(polygonControl);
        uWaze.map.addControl(freehandControl);
        // dragControl is added via setupDragControl() in setupSidebarContent

        addSidePanel();

        polygonControl.handler.callbacks.move = function (e) {
            let linearRing = new uOpenLayers.Geometry.LinearRing(e.components[0].components);
            let geometry = new uOpenLayers.Geometry.Polygon([linearRing]);
            let polygonFeature = new uOpenLayers.Feature.Vector(geometry, null);
            let polybounds = polygonFeature.geometry.getBounds();
            let minX = polybounds.left;
            let minY = polybounds.bottom;
            let maxX = polybounds.right;
            let maxY = polybounds.top;
            let startX = (minX + maxX) / 2;
            let startY = (minY + maxY) / 2;
            let startPoint = new uOpenLayers.Geometry.Point(startX, startY);
            let endPoint = new uOpenLayers.Geometry.Point(maxX, startY);
            let radius = new uOpenLayers.Geometry.LineString([startPoint, endPoint]);
            let len = radius.getGeodesicLength(new uOpenLayers.Projection("EPSG:900913"));

            let unit = 'm';
            if (len > 1000) {
                len = Math.round((len / 1000) * 100) / 100;
                unit = "km";
            } else {
                len = Math.round(len);
            }

            let rad = document.getElementById('circles-current-radius');
            if (rad) {
                rad.innerHTML = 'Current Radius: ' + len + ' ' + unit;
            }

            infoLayer.destroyFeatures();

            let centerStyle = {
                strokeColor: "#ff9800",
                strokeWidth: 2,
                pointRadius: 3,
                fillOpacity: 0.3
            };

            let lineStyle = {
                strokeColor: "#ff9800",
                strokeWidth: 2,
                label: len + ' ' + unit,
                labelAlign: "left",
                labelXOffset: "15",
                labelYOffset: "5",
                labelOutlineColor: "white",
                labelOutlineWidth: 2,
                strokeDashstyle: "dash"
            };

            let center = new uOpenLayers.Feature.Vector(startPoint, {}, centerStyle);
            let radiusLine = new uOpenLayers.Feature.Vector(radius, {}, lineStyle);

            infoLayer.addFeatures([center, radiusLine]);
        }

        polygonControl.events.on({
            'featureadded': function (e) {
                let rad = document.getElementById('circles-current-radius');
                if (rad) {
                    rad.innerHTML = '';
                }
                setTimeout(() => {
                    const event = new CustomEvent('updateAnnotations');
                    document.dispatchEvent(event);
                }, 100);
            }
        });

        freehandControl.events.on({
            'featureadded': function (e) {
                let rad = document.getElementById('circles-current-radius');
                if (rad) {
                    rad.innerHTML = '';
                }
                setTimeout(() => {
                    const event = new CustomEvent('updateAnnotations');
                    document.dispatchEvent(event);
                }, 100);
            }
        });
    }

    if (W?.userscripts?.state?.isReady) {
        log('WME already ready, initializing...');
        initializeScript();
    } else {
        log('Waiting for WME ready event...');
        document.addEventListener("wme-ready", initializeScript, { once: true });
    }

})();