Greasy Fork 还支持 简体中文。

WME Map Overlay (Google + OSM + Traffic Layers with Slider & Icons + Click to Maps)

Sobreposição de mapas no WME com camadas Google Maps, Trânsito e OSM, sliders com ícones personalizados, UI moderna e botão de esconder/mostrar. Agora com clique direto para Google Maps, OSM e ArcGIS na mesma posição!

目前為 2025-04-21 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         WME Map Overlay (Google + OSM + Traffic Layers with Slider & Icons + Click to Maps)
// @namespace    https://waze.com
// @version      2.8
// @description  Sobreposição de mapas no WME com camadas Google Maps, Trânsito e OSM, sliders com ícones personalizados, UI moderna e botão de esconder/mostrar. Agora com clique direto para Google Maps, OSM e ArcGIS na mesma posição!
// @author       ChatGPT
// @match        https://www.waze.com/*editor*
// @grant        none
// @require      https://greasyfork.org/scripts/24851-wazewrap/code/WazeWrap.js
// @license      MIT
// ==/UserScript==

(function () {
    'use strict';

    let gmap, trafficDiv, sliderContainer, isSliderVisible = true;

    function initOverlay() {
        if (typeof W === 'undefined' || typeof W.map === 'undefined') {
            setTimeout(initOverlay, 1000);
            return;
        }

        const map = W.map;

        const googleBaseLayer = new OpenLayers.Layer.XYZ(
            "Google Maps (Base)",
            "https://mt.google.com/vt/lyrs=m&x=${x}&y=${y}&z=${z}",
            { isBaseLayer: false, opacity: 0.01, visibility: true }
        );

        const osmLayer = new OpenLayers.Layer.XYZ(
            "OpenStreetMap",
            "https://tile.openstreetmap.org/${z}/${x}/${y}.png",
            { isBaseLayer: false, opacity: 0.01, visibility: true }
        );

        map.addLayer(googleBaseLayer);
        map.addLayer(osmLayer);

        sliderContainer = document.createElement("div");
        sliderContainer.style.position = "absolute";
        sliderContainer.style.top = "80px";
        sliderContainer.style.left = "50%";
        sliderContainer.style.transform = "translateX(-50%)";
        sliderContainer.style.zIndex = "1000";
        sliderContainer.style.padding = "8px";
        sliderContainer.style.background = "rgba(10, 25, 50, 0.95)";
        sliderContainer.style.borderRadius = "10px";
        sliderContainer.style.border = "1px solid white";
        sliderContainer.style.boxShadow = "0 2px 6px rgba(0,0,0,0.3)";
        sliderContainer.style.display = "flex";
        sliderContainer.style.gap = "10px";
        sliderContainer.style.fontFamily = "sans-serif";
        sliderContainer.style.transition = "all 0.3s ease";

        const layers = [
            {
                name: "Google Maps",
                icon: "https://static.vecteezy.com/system/resources/previews/016/716/478/non_2x/google-maps-icon-free-png.png",
                initial: 0.01,
                onChange: value => googleBaseLayer.setOpacity(value)
            },
            {
                name: "Traffic",
                icon: "https://cdn-icons-png.flaticon.com/256/2228/2228204.png",
                initial: 0.00,
                onChange: value => {
                    if (trafficDiv) trafficDiv.style.opacity = value;
                }
            },
            {
                name: "OSM",
                icon: "https://upload.wikimedia.org/wikipedia/commons/thumb/b/b0/Openstreetmap_logo.svg/2048px-Openstreetmap_logo.svg.png",
                initial: 0.01,
                onChange: value => osmLayer.setOpacity(value)
            },
            {
                name: "ArcGIS",
                icon: "https://th.bing.com/th/id/OIP.e5Qa0gpEBMxdY1zIgJu5PQHaHa?w=164&h=180&c=7&r=0&o=5&pid=1.7",
                initial: 0.00,
                onChange: () => {},
                isButton: true
            }
        ];

        layers.forEach(layer => {
            const wrapper = document.createElement("div");
            wrapper.style.width = "60px";
            wrapper.style.textAlign = "center";

            const img = document.createElement("img");
            img.src = layer.icon;
            img.alt = layer.name;
            img.title = layer.name;
            img.style.width = "60px";
            img.style.height = "60px";
            img.style.borderRadius = "12px";
            img.style.border = "2px solid white";
            img.style.transition = "transform 0.3s ease, border 0.3s ease";
            img.style.display = "block";
            img.style.marginBottom = "4px";
            img.style.cursor = "pointer";

            img.addEventListener("mouseenter", () => {
                img.style.transform = "scale(1.1)";
                img.style.border = "2px solid gold";
            });

            img.addEventListener("mouseleave", () => {
                img.style.transform = "scale(1)";
                img.style.border = "2px solid white";
            });

            img.addEventListener("click", () => {
                const center = W.map.getCenter();
                const zoom = W.map.getZoom();

                const lonlat = new OpenLayers.LonLat(center.lon, center.lat);
                lonlat.transform(
                    new OpenLayers.Projection("EPSG:900913"),
                    new OpenLayers.Projection("EPSG:4326")
                );

                const lat = lonlat.lat;
                const lon = lonlat.lon;

                if (layer.name === "Google Maps") {
                    const gmapsZoom = Math.min(Math.max(zoom + 0, 0), 21);
                    const gmapsUrl = `https://www.google.com/maps/@${lat},${lon},${gmapsZoom}z`;
                    window.open(gmapsUrl, '_blank');
                }

                if (layer.name === "OSM") {
                    const osmUrl = `https://www.openstreetmap.org/#map=${zoom}/${lat}/${lon}`;
                    window.open(osmUrl, '_blank');
                }

                if (layer.name === "ArcGIS") {
                    // Ajuste especial para o ArcGIS
                    // O zoom do ArcGIS é diferente do WME, precisamos converter
                    const arcgisZoom = Math.min(Math.max(zoom + 0, 0), 20); // Ajuste de nível de zoom
                    const arcgisUrl = `https://www.arcgis.com/apps/Viewer/index.html?appid=3801d24f76d246adb134d43a7a222b2c&center=${lon},${lat}&level=${arcgisZoom}`;
                    window.open(arcgisUrl, '_blank');
                }
            });

            wrapper.appendChild(img);

            if (!layer.isButton) {
                const slider = document.createElement("input");
                slider.type = "range";
                slider.min = "0";
                slider.max = "1";
                slider.step = "0.01";
                slider.value = layer.initial;
                slider.style.width = "100%";
                slider.addEventListener("input", () => {
                    layer.onChange(parseFloat(slider.value));
                });
                wrapper.appendChild(slider);
            }

            sliderContainer.appendChild(wrapper);
        });

        const toggleButton = document.createElement("button");
        toggleButton.textContent = isSliderVisible ? "Hide Sliders" : "Show Sliders";
        toggleButton.style.position = "absolute";
        toggleButton.style.top = "30px";
        toggleButton.style.left = "50%";
        toggleButton.style.transform = "translateX(-50%)";
        toggleButton.style.zIndex = "2000";
        toggleButton.style.padding = "5px 10px";
        toggleButton.style.backgroundColor = "rgba(10, 25, 50, 0.8)";
        toggleButton.style.color = "white";
        toggleButton.style.border = "1px solid white";
        toggleButton.style.borderRadius = "5px";
        toggleButton.style.cursor = "pointer";
        toggleButton.style.transition = "all 0.3s ease";
        toggleButton.addEventListener("click", () => {
            isSliderVisible = !isSliderVisible;
            sliderContainer.style.display = isSliderVisible ? "flex" : "none";
            toggleButton.textContent = isSliderVisible ? "Hide Sliders" : "Show Sliders";
        });

        document.body.appendChild(toggleButton);
        document.body.appendChild(sliderContainer);

        initTrafficLayer();
    }

    function initTrafficLayer() {
        const trafficLayer = new google.maps.TrafficLayer();

        trafficDiv = document.createElement('div');
        trafficDiv.id = "trafficDiv";
        trafficDiv.style.position = 'absolute';
        trafficDiv.style.top = '0';
        trafficDiv.style.left = '0';
        trafficDiv.style.right = '0';
        trafficDiv.style.bottom = '0';
        trafficDiv.style.zIndex = '500';
        trafficDiv.style.opacity = '0';
        trafficDiv.style.pointerEvents = 'none';

        W.map.olMap.getViewport().appendChild(trafficDiv);

        const lonlat = new OpenLayers.LonLat(W.map.getCenter().lon, W.map.getCenter().lat);
        lonlat.transform(new OpenLayers.Projection('EPSG:900913'), new OpenLayers.Projection('EPSG:4326'));

        gmap = new google.maps.Map(trafficDiv, {
            zoom: W.map.getZoom(),
            center: { lat: lonlat.lat, lng: lonlat.lon },
            disableDefaultUI: true,
            zoomControl: false,
            styles: [
                { elementType: 'labels', stylers: [{ visibility: 'off' }] },
                { featureType: 'road', elementType: 'geometry', stylers: [{ lightness: 100 }] },
                { featureType: 'road', elementType: 'labels', stylers: [{ visibility: 'off' }] },
                { featureType: 'transit', stylers: [{ visibility: 'off' }] },
                { featureType: 'poi', stylers: [{ visibility: 'off' }] },
                { featureType: 'administrative', stylers: [{ visibility: 'off' }] },
                { featureType: 'landscape', stylers: [{ visibility: 'off' }] },
                { featureType: 'water', stylers: [{ visibility: 'off' }] }
            ]
        });

        trafficLayer.setMap(gmap);
        trafficDiv.firstElementChild.style.backgroundColor = 'rgba(0, 0, 0, 0)';

        WazeWrap.Events.register('moveend', null, () => {
            const lonlat = new OpenLayers.LonLat(W.map.getCenter().lon, W.map.getCenter().lat);
            lonlat.transform(new OpenLayers.Projection('EPSG:900913'), new OpenLayers.Projection('EPSG:4326'));
            gmap.panTo({ lat: lonlat.lat, lng: lonlat.lon });
        });

        WazeWrap.Events.register('zoomend', null, () => {
            gmap.setZoom(W.map.getZoom());
        });
    }

    function waitReady() {
        if (W && WazeWrap && WazeWrap.Ready) {
            initOverlay();
        } else {
            setTimeout(waitReady, 500);
        }
    }

    waitReady();
})();