WME Google Place Layer

Adds a Google Maps traffic layer overlay to the Waze Map Editor. Syncs with WME’s map center and zoom level. - Shift+P: Toggle layer visibility. - Shift+L: Toggle interactivity (enable/disable pointer events).

目前為 2025-02-27 提交的版本,檢視 最新版本

// ==UserScript==
// @name         WME Google Place Layer
// @name:vi      WME Google Place Layer
// @namespace    https://waze.com/minhtanz1
// @version      1.2
// @description  Adds a Google Maps traffic layer overlay to the Waze Map Editor. Syncs with WME’s map center and zoom level. - Shift+P: Toggle layer visibility. - Shift+L: Toggle interactivity (enable/disable pointer events).
// @description:vi Thêm lớp phủ giao thông của Google Maps vào Waze Map Editor. Đồng bộ với trung tâm bản đồ và mức thu phóng của WME. - Shift+P: Chuyển đổi chế độ hiển thị lớp. - Shift+L: Chuyển đổi tính tương tác (bật/tắt sự kiện con trỏ).
// @author       Minh Tan
// @match        https://*.waze.com/*/editor*
// @match        https://*.waze.com/editor*
// @exclude      https://*.waze.com/user/editor*
// @grant        none
// @require      https://greasyfork.org/scripts/24851-wazewrap/code/WazeWrap.js
// @license      MIT
// ==/UserScript==

/* global W, OpenLayers, google, WazeWrap */

(function() {
    'use strict';
    let gmap;
    let trafficDiv;
    let layerEnabled = (localStorage.getItem("WMEGoogleTrafficLayer-enabled") ?? false) === 'true';
    let interactivityEnabled = false; // Default: interactivity disabled

    // Toggle the display of the Google Traffic Layer
    function toggleLayer() {
        layerEnabled = !layerEnabled;
        const checkbox = document.querySelector("#layer-switcher-item_google_traffic_layer");
        if (checkbox) {
            checkbox.checked = layerEnabled;
        }
        trafficDiv.style.display = layerEnabled ? "block" : "none";
        localStorage.setItem("WMEGoogleTrafficLayer-enabled", layerEnabled);
    }

    // Toggle pointer events (i.e. interactivity) on the overlay
    function toggleInteractivity() {
        interactivityEnabled = !interactivityEnabled;
        trafficDiv.style.pointerEvents = interactivityEnabled ? 'auto' : 'none';
        console.log("Google Map interactivity " + (interactivityEnabled ? "enabled" : "disabled"));
    }

    // Initialize the Google Maps overlay
    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.opacity = '0.7'; // transparent layer
        // Start with interactivity disabled so WME controls work
        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,
            mapTypeId: 'roadmap',
            styles: [
                //https://developers.google.com/maps/documentation/javascript/style-reference#:~:text=The%20following%20features%20are%20available%3A
                // testing style site
                // https://mapstyle.withgoogle.com/
                { elementType: 'labels', stylers: [{ visibility: 'off' }] },
                { featureType: 'administrative', stylers: [{ visibility: 'off' }] },
                { featureType: 'administrative', elementType: 'geometry', stylers: [{ visibility: 'off' }] },
                { featureType: 'administrative.land_parcel', stylers: [{ visibility: 'off' }] },
                { featureType: 'administrative.neighborhood', stylers: [{ visibility: 'off' }] },
                { featureType: 'landscape', stylers: [{ visibility: 'off' }] },
                { featureType: "poi", elementType: "geometry", stylers: [{ visibility: "on" }] },
                { featureType: "poi", elementType: "labels", stylers: [{ visibility: "on" }] },
                //                 { featureType: 'road', elementType: 'labels', stylers: [{ visibility: 'on' }] },
                //                 { featureType: 'road', elementType: 'labels.icon', stylers: [{ visibility: 'on' }] },
                //                 { featureType: 'road', elementType: 'geometry', stylers: [{visibility: 'simplified'}] },
                //                 // Show local (narrow) roads with a red tint
                //                 { featureType: 'transit', stylers: [{ visibility: 'off' }] },
                //                 { featureType: 'water', stylers: [{ visibility: 'off' }] }
                {
                    "featureType": "poi.business",
                    "elementType": "labels.text",
                    "stylers": [
                        {
                            "visibility": "off"
                        }
                    ]
                },
                {
                    "featureType": "poi.park",
                    "elementType": "labels.text",
                    "stylers": [
                        {
                            "visibility": "off"
                        }
                    ]
                },
                // Show local (narrow) roads with a red tint
                {
                    "featureType": "road.local",
                    "elementType": "geometry.fill",
                    "stylers": [
                        {
                            "color": "#ff5252"
                        },
                        {
                            "visibility": "on"
                        },
                        {
                            "weight": 1.5
                        }
                    ]
                },
                {
                    "featureType": "road.local",
                    "elementType": "labels",
                    "stylers": [
                        {
                            "visibility": "on"
                        }
                    ]
                }
            ]
            
        });

        //show traffic layer
        //         trafficLayer.setMap(gmap);
        if (trafficDiv.firstElementChild) {
            trafficDiv.firstElementChild.style.backgroundColor = 'rgb(0 0 0 / 0%)'; //remove white background
        }
        if (!layerEnabled) {
            trafficDiv.style.display = "none";
        }

        // Sync Google Maps center with WME map movements
        WazeWrap.Events.register('moveend', null, function() {
            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 });
        });

        // Sync Google Maps zoom with WME zoom events
        WazeWrap.Events.register('zoomend', null, function() {
            gmap.setZoom(W.map.getZoom());
        });

        window.gmap = gmap; // for testing

        WazeWrap.Interface.AddLayerCheckbox(
            "display",
            "Google Traffic Layer",
            layerEnabled,
            toggleLayer,
            W.map.getLayerByName("Google Traffic Layer")
        );

        new WazeWrap.Interface.Shortcut(
            'WMEGoogleTrafficLayer',
            'Toggle Traffic Layer',
            'layers',
            'layersToggleWMEGoogleTrafficLayer',
            "Shift+P",
            toggleLayer,
            null
        ).add();

        new WazeWrap.Interface.Shortcut(
            'WMEGoogleInteractivityToggle',
            'Toggle Google Map Interactivity',
            'layers',
            'layersToggleGoogleInteractivity',
            'Shift+L',
            toggleInteractivity,
            null
        ).add();
    }

    if (W && W.userscripts && W.userscripts.state && W.userscripts.state.isReady) {
        initTrafficLayer();
    } else {
        document.addEventListener('wme-ready', initTrafficLayer, { once: true });
    }
})();