WME Multi Map Overlay

Erweiterte Version mit TopPlus, Dark Map, Basemap DE und allen DACH Geoportal Overlays

Nainštalovať tento skript?
Autor skriptu navrhuje

Tiež sa vám môže páčiť WME GeoPortal Overlay DACH Beta.

Nainštalovať tento skript
// ==UserScript==
// @name WME Multi Map Overlay
// @namespace https://greasyfork.org/de/users/863740-horst-wittlich
// @version 2025.06.12
// @description Erweiterte Version mit TopPlus, Dark Map, Basemap DE und allen DACH Geoportal Overlays
// @author Hiwi234
// @match https://www.waze.com/editor*
// @match https://www.waze.com/*/editor*
// @match https://beta.waze.com/editor*
// @match https://beta.waze.com/*/editor*
// @grant unsafeWindow
// @grant GM_xmlhttpRequest
// @grant GM_info
// @connect sgx.geodatenzentrum.de
// @connect owsproxy.lgl-bw.de
// @connect www.wmts.nrw.de
// @connect geoservices.bayern.de
// @connect mapsneu.wien.gv.at
// @connect www.basemap.at
// @connect wmts.geo.admin.ch
// @connect cdnjs.cloudflare.com
// @connect a.tile.openstreetmap.org
// @connect b.tile.openstreetmap.org
// @connect c.tile.openstreetmap.org
// @connect a.basemaps.cartocdn.com
// @connect b.basemaps.cartocdn.com
// @connect c.basemaps.cartocdn.com
// @license MIT
// ==/UserScript==
(function() {
'use strict';

const SCRIPT_NAME = 'WME Multi Overlay';
const SCRIPT_ID = 'wme-multi-overlay';
const DEFAULT_OPACITY = 0.64;
const DEFAULT_ZINDEX = 2010;
const STORAGE_KEY = 'wme-overlay-settings';
const COLLAPSED_STATE_KEY = 'wme-overlay-collapsed-groups';

// Layer storage
const layers = {};

// Collapsed state storage
let collapsedGroups = {};

// Settings Management
function saveSettings() {
    const settings = {
        version: GM_info.script.version,
        layers: {}
    };

    Object.keys(layers).forEach(layerId => {
        const layer = layers[layerId];
        if (layer) {
            settings.layers[layerId] = {
                visible: layer.getVisibility(),
                opacity: layer.opacity || DEFAULT_OPACITY,
                zIndex: layer.getZIndex() || DEFAULT_ZINDEX
            };
        }
    });

    localStorage.setItem(STORAGE_KEY, JSON.stringify(settings));
    console.log('Settings gespeichert:', settings);
}

function saveCollapsedState() {
    localStorage.setItem(COLLAPSED_STATE_KEY, JSON.stringify(collapsedGroups));
}

function loadCollapsedState() {
    try {
        const saved = localStorage.getItem(COLLAPSED_STATE_KEY);
        if (saved) {
            collapsedGroups = JSON.parse(saved);
        }
    } catch (error) {
        console.error('Fehler beim Laden der Collapsed State:', error);
        collapsedGroups = {};
    }
}

function saveGlobalFilters() {
    localStorage.setItem('wme-overlay-global-filters', JSON.stringify(globalFilters));
}

function loadGlobalFilters() {
    try {
        const saved = localStorage.getItem('wme-overlay-global-filters');
        if (saved) {
            const filters = JSON.parse(saved);
            globalFilters = { ...globalFilters, ...filters };
        }
    } catch (error) {
        console.error('Fehler beim Laden der globalen Filter:', error);
    }
}

function loadSettings() {
    try {
        const saved = localStorage.getItem(STORAGE_KEY);
        if (saved) {
            const settings = JSON.parse(saved);
            console.log('Settings geladen:', settings);
            return settings;
        }
    } catch (error) {
        console.error('Fehler beim Laden der Settings:', error);
    }
    return { layers: {} };
}

function applySettingsToLayer(layerId, layer) {
    const settings = loadSettings();
    const layerSettings = settings.layers[layerId];

    if (layerSettings) {
        // Sichtbarkeit anwenden
        layer.setVisibility(layerSettings.visible || false);
        // Transparenz anwenden
        layer.setOpacity(layerSettings.opacity || DEFAULT_OPACITY);
        // Z-Index anwenden
        layer.setZIndex(layerSettings.zIndex || DEFAULT_ZINDEX);

        console.log(`Settings für ${layerId} angewendet:`, layerSettings);
    }
}

// Filter-Updates für alle Layer
function updateAllLayerFilters() {
    const brightness = globalFilters.brightness;
    const contrast = globalFilters.contrast;
    const saturation = globalFilters.saturation;
    const sharpness = globalFilters.sharpness;

    // CSS Filter mit funktionierender Schärfe erstellen
    // Schärfe wird über blur() (umgekehrt) implementiert: 100% = kein blur, über 100% = unsharp mask via contrast
    let filterString = `brightness(${brightness}%) contrast(${contrast}%) saturate(${saturation}%)`;

    // Schärfe-Implementierung
    if (sharpness < 100) {
        // Unter 100%: Blur hinzufügen (weicher)
        const blurAmount = (100 - sharpness) / 100 * 2; // Max 2px blur bei 0%
        filterString += ` blur(${blurAmount}px)`;
    } else if (sharpness > 100) {
        // Über 100%: Zusätzlichen Kontrast für Schärfe-Effekt
        const extraContrast = 100 + (sharpness - 100) * 0.5; // Moderate Verstärkung
        filterString += ` contrast(${extraContrast}%)`;
    }

    console.log('Applying global filters:', filterString);

    // Filter auf alle OpenLayers Divs anwenden
    setTimeout(() => {
        const layerElements = document.querySelectorAll('.olLayerDiv');
        layerElements.forEach(element => {
            if (element && element.style) {
                element.style.filter = filterString;
                element.style.webkitFilter = filterString;
            }
        });

        // Zusätzlich auf alle Layer-Container anwenden
        Object.keys(layers).forEach(layerId => {
            const layer = layers[layerId];
            if (layer && layer.div && layer.getVisibility()) {
                layer.div.style.filter = filterString;
                layer.div.style.webkitFilter = filterString;
            }
        });

        // Für Canvas-Elemente (falls vorhanden)
        const canvasElements = document.querySelectorAll('#map canvas');
        canvasElements.forEach(canvas => {
            if (canvas && canvas.style) {
                canvas.style.filter = filterString;
                canvas.style.webkitFilter = filterString;
            }
        });
    }, 100);
}

// Neu organisierte Layer-Konfiguration
const layerGroups = {
    basic: {
        name: "🔧 Basis Layer",
        layers: [
            {
                id: 'topplus',
                name: 'TopPlus WMS',
                type: 'wms',
                url: 'https://sgx.geodatenzentrum.de/wms_topplus_web_open',
                params: {
                    layers: 'web',
                    format: 'image/png',
                    transparent: true
                },
                attribution: '© BKG'
            },
            {
                id: 'topplus-grau',
                name: 'TopPlus Grau',
                type: 'wms',
                url: 'https://sgx.geodatenzentrum.de/wms_topplus_web_open',
                params: {
                    layers: 'web_grau',
                    format: 'image/png',
                    transparent: true
                },
                attribution: '© BKG'
            }
        ]
    },
    germany: {
        name: "🇩🇪 GeoOverlays DE",
        layers: [
            {
                id: 'basemap-de',
                name: 'Basemap DE',
                type: 'wms',
                url: 'https://sgx.geodatenzentrum.de/wms_basemapde',
                params: {
                    layers: 'de_basemapde_web_raster_farbe',
                    format: 'image/png',
                    transparent: true
                },
                attribution: '© <a href="https://www.basemap.de">basemap.de</a>'
            },
            {
                id: 'basemap-de-grau',
                name: 'Basemap.de Grau',
                type: 'wms',
                url: 'https://sgx.geodatenzentrum.de/wms_basemapde',
                params: {
                    layers: 'de_basemapde_web_raster_grau',
                    format: 'image/png',
                    transparent: true
                },
                attribution: '© <a href="https://www.basemap.de">basemap.de</a>'
            },
            {
                id: "geoportal-nrw",
                name: "GeoPortal NRW",
                type: "wmts",
                source: "https://www.wmts.nrw.de/geobasis/wmts_nw_dtk/1.0.0/WMTSCapabilities.xml",
                layerName: "nw_dtk_col",
                matrixSet: "EPSG_3857_16",
            },
            {
                id: "geoportal-nrw-overlay",
                name: "GeoPortal NRW Overlay",
                type: "wmts",
                source: "https://www.wmts.nrw.de/geobasis/wmts_nw_dop_overlay/1.0.0/WMTSCapabilities.xml",
                layerName: "nw_dop_overlay",
                matrixSet: "EPSG_3857_16",
            },
            {
                id: "geoportal-by",
                name: "GeoPortal BY",
                type: "wmts",
                source: "https://geoservices.bayern.de/od/wmts/geobasis/v1/1.0.0/WMTSCapabilities.xml",
                layerName: "by_webkarte",
                matrixSet: "smerc",
            }
        ]
    },
    austria: {
        name: "🇦🇹 GeoOverlays AT",
        layers: [
            {
                id: "basemap-at",
                name: "Basemap AT",
                type: "wmts",
                source: "https://mapsneu.wien.gv.at/basemapneu/1.0.0/WMTSCapabilities.xml",
                layerName: "geolandbasemap",
                matrixSet: "google3857",
            },
            {
                id: "overlay-at",
                name: "Overlay AT",
                type: "wmts",
                source: "https://www.basemap.at/wmts/1.0.0/WMTSCapabilities.xml",
                layerName: "bmapoverlay",
                matrixSet: "google3857",
            }
        ]
    },
    switzerland: {
        name: "🇨🇭 GeoOverlays CH",
        layers: [
            {
                id: "swiss-strassen",
                name: "Strassenkarte",
                type: "wmts",
                source: "https://wmts.geo.admin.ch/EPSG/3857/1.0.0/WMTSCapabilities.xml",
                layerName: "ch.swisstopo.swisstne-base",
                matrixSet: "3857_18",
            },
            {
                id: "swiss-basis",
                name: "Basisnetz",
                type: "wmts",
                source: "https://wmts.geo.admin.ch/EPSG/3857/1.0.0/WMTSCapabilities.xml",
                layerName: "ch.swisstopo.swisstlm3d-strassen",
                matrixSet: "3857_18",
            },
            {
                id: "swiss-luft",
                name: "Luftbild",
                type: "wmts",
                source: "https://wmts.geo.admin.ch/EPSG/3857/1.0.0/WMTSCapabilities.xml",
                layerName: "ch.swisstopo.swissimage-product",
                matrixSet: "3857_20",
            }
        ]
    },
    unofficial: {
        name: "🌍 Nicht Amtliche Karten",
        layers: [
            {
                id: 'osm-standard',
                name: 'OpenStreetMap Standard',
                type: 'xyz',
                urls: [
                    'https://a.tile.openstreetmap.org/${z}/${x}/${y}.png',
                    'https://b.tile.openstreetmap.org/${z}/${x}/${y}.png',
                    'https://c.tile.openstreetmap.org/${z}/${x}/${y}.png'
                ],
                attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
            },
            {
                id: 'darkmap',
                name: 'OSM Dark Matter',
                type: 'xyz',
                urls: [
                    'https://a.basemaps.cartocdn.com/dark_all/${z}/${x}/${y}@2x.png',
                    'https://b.basemaps.cartocdn.com/dark_all/${z}/${x}/${y}@2x.png',
                    'https://c.basemaps.cartocdn.com/dark_all/${z}/${x}/${y}@2x.png'
                ],
                attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors © <a href="https://carto.com/attributions">CARTO</a>'
            }
        ]
    }
};

// Hilfsfunktionen für OpenLayers Patches
function loadOLScript(filename) {
    const version = OpenLayers.VERSION_NUMBER.replace(/Release /, "");
    console.info("Loading openlayers/" + version + "/" + filename + ".js");
    const script = document.createElement("script");
    script.src = `https://cdnjs.cloudflare.com/ajax/libs/openlayers/${version}/${filename}.js`;
    script.type = "text/javascript";
    script.async = false;
    document.head.appendChild(script);
}

function patchOpenLayers() {
    console.log("Patching OpenLayers for WMTS support...");
    if (!OpenLayers.VERSION_NUMBER.match(/^Release [0-9.]*$/)) {
        console.error("OpenLayers version mismatch - cannot apply patch");
        return;
    }

    // Lade notwendige OpenLayers Komponenten
    loadOLScript("lib/OpenLayers/Format/XML");
    loadOLScript("lib/OpenLayers/Format/XML/VersionedOGC");
    loadOLScript("lib/OpenLayers/Layer/WMTS");
    loadOLScript("lib/OpenLayers/Format/OWSCommon");
    loadOLScript("lib/OpenLayers/Format/OWSCommon/v1");
    loadOLScript("lib/OpenLayers/Format/OWSCommon/v1_1_0");
    loadOLScript("lib/OpenLayers/Format/WMSCapabilities");
    loadOLScript("lib/OpenLayers/Format/WMSCapabilities/v1");
    loadOLScript("lib/OpenLayers/Format/WMSCapabilities/v1_3");
    loadOLScript("lib/OpenLayers/Format/WMSCapabilities/v1_3_0");
    loadOLScript("lib/OpenLayers/Format/WMTSCapabilities");
    loadOLScript("lib/OpenLayers/Format/WMTSCapabilities/v1_0_0");
}

// Basis Layer erstellen
function createBasicLayer(config) {
    const olMap = W.map.getOLMap();
    let layer;

    switch (config.type) {
        case 'wms':
            layer = new OpenLayers.Layer.WMS(
                config.name,
                config.url,
                config.params,
                {
                    transitionEffect: 'resize',
                    attribution: config.attribution,
                    isBaseLayer: false,
                    visibility: false,
                    opacity: DEFAULT_OPACITY,
                    projection: new OpenLayers.Projection("EPSG:3857"),
                    displayInLayerSwitcher: false,
                    alwaysInRange: true,
                    zIndex: DEFAULT_ZINDEX
                }
            );
            break;
        case 'xyz':
            layer = new OpenLayers.Layer.XYZ(
                config.name,
                config.urls,
                {
                    attribution: config.attribution,
                    transitionEffect: 'resize',
                    isBaseLayer: false,
                    visibility: false,
                    opacity: DEFAULT_OPACITY,
                    displayInLayerSwitcher: false,
                    alwaysInRange: true,
                    zIndex: DEFAULT_ZINDEX
                }
            );
            break;
    }

    if (layer) {
        layer.setOpacity(DEFAULT_OPACITY);
        olMap.addLayer(layer);
        layers[config.id] = layer;

        // Gespeicherte Einstellungen anwenden
        applySettingsToLayer(config.id, layer);
    }
}

// WMTS Layer erstellen
function createWMTSLayer(layerConfig) {
    return new Promise((resolve, reject) => {
        GM_xmlhttpRequest({
            method: "GET",
            url: layerConfig.source,
            onload: (response) => {
                try {
                    let responseXML = response.responseXML;
                    if (!responseXML) {
                        responseXML = new DOMParser().parseFromString(response.responseText, "text/xml");
                    }

                    if (!responseXML || !(responseXML instanceof XMLDocument)) {
                        throw new Error("Invalid XML response");
                    }

                    const format = new OpenLayers.Format.WMTSCapabilities();
                    const capabilities = format.read(responseXML);
                    const layer = format.createLayer(capabilities, {
                        layer: layerConfig.layerName,
                        matrixSet: layerConfig.matrixSet,
                        opacity: DEFAULT_OPACITY,
                        isBaseLayer: false,
                        requestEncoding: layerConfig.requestEncoding || "REST",
                        visibility: false,
                        zIndex: DEFAULT_ZINDEX
                    });

                    if (layer && layer.url && layer.url.length) {
                        const olMap = W.map.getOLMap();
                        olMap.addLayer(layer);
                        olMap.setLayerIndex(layer, 9);
                        layers[layerConfig.id] = layer;

                        // Gespeicherte Einstellungen anwenden
                        applySettingsToLayer(layerConfig.id, layer);

                        console.log(`✓ ${layerConfig.name} loaded successfully`);
                        resolve(layer);
                    } else {
                        throw new Error("No valid URLs found");
                    }
                } catch (error) {
                    console.error(`✗ Failed to load ${layerConfig.name}:`, error);
                    reject(error);
                }
            },
            onerror: () => reject(new Error("Network error")),
            ontimeout: () => reject(new Error("Request timeout"))
        });
    });
}

// Update Notification System
function showUpdateNotification() {
    // Prüfe ob bereits angezeigt wurde
    const lastShown = localStorage.getItem('wme-overlay-update-shown');
    const currentVersion = GM_info.script.version;

    if (lastShown === currentVersion) {
        return; // Bereits für diese Version angezeigt
    }

    // Erstelle Overlay
    const overlay = document.createElement('div');
    overlay.className = 'update-notification-overlay';
    overlay.innerHTML = `
        <div class="update-notification">
            <div class="update-header">
                <h2>WME Multi Map 🗺️ Overlay</h2>
                <div class="header-right">
                    <span class="version-badge">v${currentVersion}</span>
                    <button class="close-btn" id="header-close-btn">×</button>
                </div>
            </div>
            <div class="update-content">
                <h3>🎨 Neue Overlay-Einstellungen!</h3>
                <div class="new-features">
                    <div class="feature-group">
                        <h4>🎛️ Neue Filter-Optionen:</h4>
                        <ul>
                            <li>✅ Helligkeit (0-300%)</li>
                            <li>✅ Kontrast (0-300%)</li>
                            <li>✅ Sättigung (0-300%)</li>
                            <li>✅ Schärfe (0-300%)</li>
                        </ul>
                    </div>
                    <div class="feature-group">
                        <h4>📁 Zusammenklappbare Kategorien:</h4>
                        <ul>
                            <li>✅ Alle Kategorien können ein-/ausgeklappt werden</li>
                            <li>✅ Zustand wird automatisch gespeichert</li>
                            <li>✅ Übersichtlichere Navigation</li>
                        </ul>
                    </div>
                    <div class="feature-group">
                        <h4>🔧 Basis Layer:</h4>
                        <ul>
                            <li>✅ TopPlus WMS</li>
                            <li>✅ TopPlus Grau</li>
                        </ul>
                    </div>
                    <div class="feature-group">
                        <h4>🇩🇪 GeoOverlays DE:</h4>
                        <ul>
                            <li>✅ Basemap DE</li>
                            <li>✅ Basemap.de Grau</li>
                            <li>✅ GeoPortal NRW</li>
                            <li>✅ GeoPortal NRW Overlay</li>
                            <li>✅ GeoPortal BY</li>
                        </ul>
                    </div>
                </div>
                <div class="feature-info">
                    <p><strong>🎨 Individuelle Anpassung!</strong></p>
                    <p>Jede Karte kann jetzt individuell in Helligkeit, Kontrast, Sättigung und Schärfe angepasst werden!</p>
                </div>
            </div>
            <div class="update-actions">
                <button class="btn-primary" id="explore-maps-btn">
                    🚀 Karten erkunden!
                </button>
                <button class="btn-secondary" id="close-popup-btn">
                    Später
                </button>
            </div>
        </div>
    `;

    // Styles für das Update-Popup
    const updateStyle = document.createElement('style');
    updateStyle.textContent = `
        .update-notification-overlay {
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background: rgba(0, 0, 0, 0.8);
            z-index: 999999;
            display: flex;
            align-items: center;
            justify-content: center;
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
        }

        .update-notification {
            background: white;
            border-radius: 12px;
            max-width: 600px;
            width: 90%;
            max-height: 80vh;
            overflow-y: auto;
            box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
            animation: slideIn 0.3s ease-out;
        }

        @keyframes slideIn {
            from {
                opacity: 0;
                transform: scale(0.9) translateY(-20px);
            }
            to {
                opacity: 1;
                transform: scale(1) translateY(0);
            }
        }

        .update-header {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
            padding: 20px;
            border-radius: 12px 12px 0 0;
            display: flex;
            align-items: center;
            justify-content: space-between;
        }

        .header-right {
            display: flex;
            align-items: center;
            gap: 12px;
        }

        .close-btn {
            background: rgba(255, 255, 255, 0.2);
            border: none;
            color: white;
            width: 32px;
            height: 32px;
            border-radius: 50%;
            font-size: 18px;
            font-weight: bold;
            cursor: pointer;
            display: flex;
            align-items: center;
            justify-content: center;
            transition: background 0.2s;
        }

        .close-btn:hover {
            background: rgba(255, 255, 255, 0.3);
        }

        .update-header h2 {
            margin: 0;
            font-size: 24px;
            font-weight: 600;
        }

        .version-badge {
            background: rgba(255, 255, 255, 0.2);
            padding: 4px 12px;
            border-radius: 20px;
            font-size: 14px;
            font-weight: 500;
        }

        .update-content {
            padding: 24px;
        }

        .update-content h3 {
            margin: 0 0 20px 0;
            color: #333;
            font-size: 20px;
        }

        .new-features {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
            gap: 20px;
            margin-bottom: 24px;
        }

        .feature-group {
            background: #f8f9fa;
            padding: 16px;
            border-radius: 8px;
            border-left: 4px solid #667eea;
        }

        .feature-group h4 {
            margin: 0 0 12px 0;
            color: #333;
            font-size: 16px;
            font-weight: 600;
        }

        .feature-group ul {
            margin: 0;
            padding-left: 0;
            list-style: none;
        }

        .feature-group li {
            margin: 6px 0;
            color: #555;
            font-size: 14px;
        }

        .feature-info {
            background: #e3f2fd;
            padding: 16px;
            border-radius: 8px;
            border-left: 4px solid #2196f3;
        }

        .feature-info p {
            margin: 0;
            color: #1565c0;
            font-size: 14px;
        }

        .feature-info strong {
            font-weight: 600;
        }

        .update-actions {
            padding: 20px 24px;
            border-top: 1px solid #eee;
            display: flex;
            gap: 12px;
            justify-content: flex-end;
        }

        .btn-primary, .btn-secondary {
            padding: 12px 24px;
            border: none;
            border-radius: 6px;
            font-size: 14px;
            font-weight: 500;
            cursor: pointer;
            transition: all 0.2s;
        }

        .btn-primary {
            background: #667eea;
            color: white;
        }

        .btn-primary:hover {
            background: #5a6fd8;
            transform: translateY(-1px);
        }

        .btn-secondary {
            background: #e0e0e0;
            color: #333;
        }

        .btn-secondary:hover {
            background: #d0d0d0;
        }
    `;

    document.head.appendChild(updateStyle);
    document.body.appendChild(overlay);

    // Event Listener für Buttons hinzufügen
    overlay.querySelector('#explore-maps-btn').addEventListener('click', function() {
        overlay.remove();
        localStorage.setItem('wme-overlay-update-shown', currentVersion);
    });

    overlay.querySelector('#close-popup-btn').addEventListener('click', function() {
        overlay.remove();
        localStorage.setItem('wme-overlay-update-shown', currentVersion);
    });

    overlay.querySelector('#header-close-btn').addEventListener('click', function() {
        overlay.remove();
        localStorage.setItem('wme-overlay-update-shown', currentVersion);
    });

    // Overlay bei Klick außerhalb schließen
    overlay.addEventListener('click', function(e) {
        if (e.target === overlay) {
            overlay.remove();
            localStorage.setItem('wme-overlay-update-shown', currentVersion);
        }
    });

    // ESC-Taste zum Schließen
    document.addEventListener('keydown', function(e) {
        if (e.key === 'Escape' && document.contains(overlay)) {
            overlay.remove();
            localStorage.setItem('wme-overlay-update-shown', currentVersion);
        }
    });
}

function createLayerControl(layerId, name, isGroup = false, groupKey = null) {
    const container = document.createElement('div');
    container.className = isGroup ? 'layer-group' : 'layer-control';

    if (isGroup) {
        const header = document.createElement('div');
        header.className = 'group-header';
        header.style.cursor = 'pointer';

        // Erstelle Toggle-Button mit Pfeil
        const toggleButton = document.createElement('span');
        toggleButton.className = 'toggle-button';
        toggleButton.innerHTML = collapsedGroups[groupKey] ? '▶' : '▼';

        const titleSpan = document.createElement('span');
        titleSpan.className = 'group-title';
        titleSpan.textContent = name;

        header.appendChild(toggleButton);
        header.appendChild(titleSpan);

        // Erstelle Content-Container
        const content = document.createElement('div');
        content.className = 'group-content';
        content.style.display = collapsedGroups[groupKey] ? 'none' : 'block';

        // Click-Handler für Toggle
        header.addEventListener('click', () => {
            const isCollapsed = content.style.display === 'none';
            content.style.display = isCollapsed ? 'block' : 'none';
            toggleButton.innerHTML = isCollapsed ? '▼' : '▶';

            // Speichere Zustand
            collapsedGroups[groupKey] = !isCollapsed;
            saveCollapsedState();
        });

        container.appendChild(header);
        container.appendChild(content);
        container.content = content; // Referenz für späteren Zugriff
        return container;
    }

    const layer = layers[layerId];
    if (!layer) return container;

    // Sichtbarkeits-Checkbox
    const checkbox = document.createElement('input');
    checkbox.type = 'checkbox';
    checkbox.checked = layer.getVisibility();
    checkbox.addEventListener('change', () => {
        layer.setVisibility(checkbox.checked);
        saveSettings(); // Speichern nach Änderung
    });

    const label = document.createElement('label');
    label.appendChild(checkbox);
    label.appendChild(document.createTextNode(name));

    // Transparenz-Slider
    const opacityContainer = document.createElement('div');
    opacityContainer.className = 'slider-container';

    const opacityLabel = document.createElement('span');
    opacityLabel.textContent = 'Transparenz: ';

    const opacityValue = document.createElement('span');
    opacityValue.className = 'slider-value';
    opacityValue.textContent = Math.round((layer.opacity || DEFAULT_OPACITY) * 100) + '%';

    const opacitySlider = document.createElement('input');
    opacitySlider.type = 'range';
    opacitySlider.min = '0';
    opacitySlider.max = '100';
    opacitySlider.step = '1';
    opacitySlider.value = Math.round((layer.opacity || DEFAULT_OPACITY) * 100);
    opacitySlider.addEventListener('input', () => {
        const value = parseInt(opacitySlider.value) / 100;
        layer.setOpacity(value);
        opacityValue.textContent = opacitySlider.value + '%';
        saveSettings(); // Speichern nach Änderung
    });

    opacityContainer.appendChild(opacityLabel);
    opacityContainer.appendChild(opacitySlider);
    opacityContainer.appendChild(opacityValue);

    // Z-Index-Slider
    const zIndexContainer = document.createElement('div');
    zIndexContainer.className = 'slider-container';

    const zIndexLabel = document.createElement('span');
    zIndexLabel.textContent = 'Ebene: ';

    const zIndexValue = document.createElement('span');
    zIndexValue.className = 'slider-value';
    zIndexValue.textContent = layer.getZIndex() || DEFAULT_ZINDEX;

    const zIndexSlider = document.createElement('input');
    zIndexSlider.type = 'range';
    zIndexSlider.min = '1900';
    zIndexSlider.max = '2600';
    zIndexSlider.step = '5';
    zIndexSlider.value = layer.getZIndex() || DEFAULT_ZINDEX;
    zIndexSlider.addEventListener('input', () => {
        const zIndex = parseInt(zIndexSlider.value);
        layer.setZIndex(zIndex);
        zIndexValue.textContent = zIndex;
        saveSettings(); // Speichern nach Änderung
    });

    zIndexContainer.appendChild(zIndexLabel);
    zIndexContainer.appendChild(zIndexSlider);
    zIndexContainer.appendChild(zIndexValue);

    container.appendChild(label);
    container.appendChild(opacityContainer);
    container.appendChild(zIndexContainer);

    return container;
}

// Globale Filter-Einstellungen
let globalFilters = {
    brightness: 100,
    contrast: 100,
    saturation: 100,
    sharpness: 100
};

function createGlobalFilterControls() {
    const container = createLayerControl(null, '🎨 Karten Einstellungen', true, 'global-filters');
    const content = container.content;

    // Helligkeit-Slider
    const brightnessContainer = document.createElement('div');
    brightnessContainer.className = 'slider-container global-filter';

    const brightnessLabel = document.createElement('span');
    brightnessLabel.textContent = 'Helligkeit: ';

    const brightnessValue = document.createElement('span');
    brightnessValue.className = 'slider-value';
    brightnessValue.textContent = globalFilters.brightness + '%';

    const brightnessSlider = document.createElement('input');
    brightnessSlider.type = 'range';
    brightnessSlider.min = '0';
    brightnessSlider.max = '300';
    brightnessSlider.step = '1';
    brightnessSlider.value = globalFilters.brightness;
    brightnessSlider.addEventListener('input', () => {
        const value = parseInt(brightnessSlider.value);
        globalFilters.brightness = value;
        brightnessValue.textContent = value + '%';
        updateAllLayerFilters();
        saveGlobalFilters();
    });

    brightnessContainer.appendChild(brightnessLabel);
    brightnessContainer.appendChild(brightnessSlider);
    brightnessContainer.appendChild(brightnessValue);

    // Kontrast-Slider
    const contrastContainer = document.createElement('div');
    contrastContainer.className = 'slider-container global-filter';

    const contrastLabel = document.createElement('span');
    contrastLabel.textContent = 'Kontrast: ';

    const contrastValue = document.createElement('span');
    contrastValue.className = 'slider-value';
    contrastValue.textContent = globalFilters.contrast + '%';

    const contrastSlider = document.createElement('input');
    contrastSlider.type = 'range';
    contrastSlider.min = '0';
    contrastSlider.max = '300';
    contrastSlider.step = '1';
    contrastSlider.value = globalFilters.contrast;
    contrastSlider.addEventListener('input', () => {
        const value = parseInt(contrastSlider.value);
        globalFilters.contrast = value;
        contrastValue.textContent = value + '%';
        updateAllLayerFilters();
        saveGlobalFilters();
    });

    contrastContainer.appendChild(contrastLabel);
    contrastContainer.appendChild(contrastSlider);
    contrastContainer.appendChild(contrastValue);

    // Sättigung-Slider
    const saturationContainer = document.createElement('div');
    saturationContainer.className = 'slider-container global-filter';

    const saturationLabel = document.createElement('span');
    saturationLabel.textContent = 'Sättigung: ';

    const saturationValue = document.createElement('span');
    saturationValue.className = 'slider-value';
    saturationValue.textContent = globalFilters.saturation + '%';

    const saturationSlider = document.createElement('input');
    saturationSlider.type = 'range';
    saturationSlider.min = '0';
    saturationSlider.max = '300';
    saturationSlider.step = '1';
    saturationSlider.value = globalFilters.saturation;
    saturationSlider.addEventListener('input', () => {
        const value = parseInt(saturationSlider.value);
        globalFilters.saturation = value;
        saturationValue.textContent = value + '%';
        updateAllLayerFilters();
        saveGlobalFilters();
    });

    saturationContainer.appendChild(saturationLabel);
    saturationContainer.appendChild(saturationSlider);
    saturationContainer.appendChild(saturationValue);

    // Schärfe-Slider (mit funktionierender Implementierung)
    const sharpnessContainer = document.createElement('div');
    sharpnessContainer.className = 'slider-container global-filter';

    const sharpnessLabel = document.createElement('span');
    sharpnessLabel.textContent = 'Schärfe: ';

    const sharpnessValue = document.createElement('span');
    sharpnessValue.className = 'slider-value';
    sharpnessValue.textContent = globalFilters.sharpness + '%';

    const sharpnessSlider = document.createElement('input');
    sharpnessSlider.type = 'range';
    sharpnessSlider.min = '0';
    sharpnessSlider.max = '300';
    sharpnessSlider.step = '1';
    sharpnessSlider.value = globalFilters.sharpness;
    sharpnessSlider.addEventListener('input', () => {
        const value = parseInt(sharpnessSlider.value);
        globalFilters.sharpness = value;
        sharpnessValue.textContent = value + '%';
        updateAllLayerFilters();
        saveGlobalFilters();
    });

    sharpnessContainer.appendChild(sharpnessLabel);
    sharpnessContainer.appendChild(sharpnessSlider);
    sharpnessContainer.appendChild(sharpnessValue);

    content.appendChild(brightnessContainer);
    content.appendChild(contrastContainer);
    content.appendChild(saturationContainer);
    content.appendChild(sharpnessContainer);

    return container;
}

async function initializeScript() {
    try {
        // Registriere Script-Info
        W.userscripts[SCRIPT_ID] = {
            name: SCRIPT_NAME,
            author: 'Hiwi234',
            version: GM_info.script.version
        };

        // Lade gespeicherte Zustände
        loadCollapsedState();

        // Patche OpenLayers für WMTS Support
        patchOpenLayers();

        // Warte kurz für OpenLayers Patches
        await new Promise(resolve => setTimeout(resolve, 1000));

        // Erstelle alle Layer
        console.log("Creating layers...");
        const wmtsPromises = [];

        Object.keys(layerGroups).forEach(groupKey => {
            const group = layerGroups[groupKey];
            group.layers.forEach(layerConfig => {
                if (layerConfig.type === 'wmts') {
                    wmtsPromises.push(createWMTSLayer(layerConfig));
                } else {
                    createBasicLayer(layerConfig);
                }
            });
        });

        // Warte auf alle WMTS Layer
        await Promise.allSettled(wmtsPromises);

        // Erstelle Sidebar Tab
        const { tabLabel, tabPane } = W.userscripts.registerSidebarTab(SCRIPT_ID);
        tabLabel.textContent = 'Multi Map 🗺️';
        tabLabel.title = 'Erweiterte Karten-Overlays';

        await W.userscripts.waitForElementConnected(tabPane);

        // Erstelle Content Container
        const content = document.createElement('div');
        content.className = 'overlay-tab';

        // Lade globale Filter-Einstellungen
        loadGlobalFilters();

        // Füge alle Gruppen in der gewünschten Reihenfolge hinzu
        const groupOrder = ['basic', 'germany', 'austria', 'switzerland', 'unofficial'];

        groupOrder.forEach(groupKey => {
            if (layerGroups[groupKey]) {
                const group = layerGroups[groupKey];
                const groupContainer = createLayerControl(null, group.name, true, groupKey);

                group.layers.forEach(layerConfig => {
                    if (layers[layerConfig.id]) {
                        groupContainer.content.appendChild(createLayerControl(layerConfig.id, layerConfig.name));
                    }
                });

                content.appendChild(groupContainer);
            }
        });

        // Füge globale Filter-Kontrollen am Ende hinzu
        content.appendChild(createGlobalFilterControls());

        tabPane.appendChild(content);

        // Füge Reset-Button hinzu
        const resetContainer = document.createElement('div');
        resetContainer.className = 'reset-container';

        const resetButton = document.createElement('button');
        resetButton.textContent = '🔄 Einstellungen zurücksetzen';
        resetButton.className = 'reset-button';
        resetButton.addEventListener('click', () => {
            if (confirm('Alle Einstellungen zurücksetzen?')) {
                localStorage.removeItem(STORAGE_KEY);
                localStorage.removeItem('wme-overlay-global-filters');
                localStorage.removeItem(COLLAPSED_STATE_KEY);
                location.reload();
            }
        });

        resetContainer.appendChild(resetButton);
        tabPane.appendChild(resetContainer);

        // Füge erweiterte Styles hinzu
        const style = document.createElement('style');
        style.textContent = `
            .overlay-tab {
                padding: 8px;
                max-height: 70vh;
                overflow-y: auto;
            }

            .layer-group {
                margin-bottom: 15px;
                border: 1px solid #ddd;
                border-radius: 6px;
                overflow: hidden;
            }

            .group-header {
                background: #f5f5f5;
                padding: 8px 12px;
                font-weight: bold;
                border-bottom: 1px solid #ddd;
                display: flex;
                align-items: center;
                gap: 8px;
                transition: background-color 0.2s ease;
            }

            .group-header:hover {
                background: #e8e8e8;
            }

            .toggle-button {
                font-size: 12px;
                color: #666;
                transition: transform 0.2s ease;
                display: inline-block;
                width: 16px;
                text-align: center;
            }

            .group-title {
                font-size: 14px;
                flex: 1;
            }

            .group-content {
                transition: all 0.3s ease;
                overflow: hidden;
            }

            .layer-control {
                margin: 0;
                padding: 12px;
                border-bottom: 1px solid #eee;
            }

            .layer-control:last-child {
                border-bottom: none;
            }

            .layer-control label {
                display: block;
                margin-bottom: 8px;
                font-weight: 500;
                cursor: pointer;
            }

            .layer-control input[type="checkbox"] {
                margin-right: 8px;
            }

            .slider-container {
                margin: 6px 0;
                display: flex;
                align-items: center;
            }

            .global-filter {
                padding: 8px 12px;
                border-bottom: 1px solid #eee;
            }

            .global-filter:last-child {
                border-bottom: none;
            }

            .slider-container span {
                display: inline-block;
                width: 90px;
                font-size: 12px;
                color: #666;
            }

            .slider-container input[type="range"] {
                flex: 1;
                margin: 0 8px;
            }

            .slider-value {
                width: 40px !important;
                text-align: right;
                font-weight: 500;
                color: #333 !important;
            }

            .reset-container {
                margin-top: 15px;
                padding: 15px;
                border-top: 1px solid #ddd;
                text-align: center;
            }

            .reset-button {
                background: #f44336;
                color: white;
                border: none;
                padding: 8px 16px;
                border-radius: 4px;
                cursor: pointer;
                font-size: 12px;
                transition: background 0.2s;
            }

            .reset-button:hover {
                background: #d32f2f;
            }
        `;

        document.head.appendChild(style);

        console.log(SCRIPT_NAME + ': Erfolgreich initialisiert mit', Object.keys(layers).length, 'Layern');

        // Zeige Update-Benachrichtigung
        showUpdateNotification();

        // Initiale Anwendung der globalen Filter
        setTimeout(() => {
            updateAllLayerFilters();
        }, 2000);

    } catch (error) {
        console.error(SCRIPT_NAME + ': Fehler bei der Initialisierung:', error);
    }
}

// Starte Initialisierung wenn WME bereit ist
if (W?.userscripts?.state.isReady) {
    initializeScript();
} else {
    document.addEventListener('wme-ready', initializeScript, { once: true });
}

})();