GeoGuessr Custom Maps

playing with modified maps in geoguessr games

当前为 2024-11-21 提交的版本,查看 最新版本

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

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

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         GeoGuessr Custom Maps
// @description  playing with modified maps in geoguessr games
// @version      1.1.2
// @match        *://www.geoguessr.com/*
// @author       KaKa
// @license      BSD
// @require      https://update.greasyfork.org/scripts/502813/1423193/Geoguessr%20Tag.js
// @icon         https://www.svgrepo.com/show/392367/interaction-interface-layer-layers-location-map.svg
// @namespace https://greasyfork.org/users/1179204
// ==/UserScript==

(function() {

    /*=========================================================================Modifiy your guess map here==================================================================================*/

    let customOptions={

        Language:'en',                                  //en,zh,ja,fr,de,es

        Google_StreetView_Layer_Lines_Style:'Default',   // More styles see below

        Google_StreetView_Layer_Shortcut:'V',

        Google_Labels_Layer_Shortcut:'G',

        Google_Terrain_Layer_Shortcut:'T',

        Google_Satellite_Layer_Shortcut:'B',

        Apple_StreetView_Layer_Shortcut:'P',

        Yandex_StreetView_Layer_Shortcut:'Y',

        OpenWeather_Shortcut:'Q',

        OpenWeather_Style:'radar',                      //'radar': Global Precipitation;  'CL':Cloud;  'APM':Pressure;  'TA2'Temperature;  'WS10':Wind Speed;

        OpenWeather_Date:'now',                        // foramt:yyyy-mm-dd, less than one week ago

        Bing_Maps_Style:'r',                           // 'a':satellite(without labels);  'h':hybrid;  'r':roadmap,'sre':terrain

        Map_Tiler_Style:'basic',                       //basic,satellite,bright,landscape,ocean,outdoor,topo,streets,dataviz

        Carto_Style:'light_all',                       //light_all,dark_all

        Thunderforest_Style:'spinal-map'}              //spinal-map,landscape,outdoors,atlas,transport,


    let tileServices=["Google_Maps","OpenStreetMap","Bing_Maps","Map_Tiler","Thunderforest","Carto","Yandex_Maps","Petal_Maps"]

    let colorOptions={

        Default:['1098ad','99e9f2'],

        Crimson:['f03e3e','ffc9c9'],

        Deep_Pink:['d6336c','fcc2d7'],

        Blue_Violet:['ae3ec9','eebefa'],

        Slate_Blue:['7048e8','d0bfff'],

        Royal_Blue:['4263eb','bac8ff'],

        Dodger_Blue: ['1c7ed6','a5d8ff'],

        Sea_Green:['0ca678','96f2d7'],

        Lime_Green:['37b24d','b2f2bb'],

        OliveDrab:['74b816','d8f5a2'],

        Orange:['f59f00','ffec99'],

        Dark_Orange:['f76707','ffd8a8'],

        Brown:['bd5f1b','f7ca9e'],
    }

    /*======================================================================================================================================================================================*/

    let map,google,customMapType,initLayer=tag
    let currentLayers=["Google_Maps","Google_Labels"]

    const openWeatherBaseURL = "https://g.sat.owm.io/vane/2.0/weather";
    const radarURL = `https://b.sat.owm.io/maps/2.0/radar/{z}/{x}/{y}?appid=9de243494c0b295cca9337e1e96b00e2&day=${getNow(customOptions.OpenWeather_Date)}`;

    const openWeatherURL = (customOptions.OpenWeather_Style === 'radar')
    ? radarURL
    : `${openWeatherBaseURL}/${customOptions.OpenWeather_Style}/{z}/{x}/{y}?appid=9de243494c0b295cca9337e1e96b00e2&&date=${getTimestamp(customOptions.OpenWeather_Date)}&fill_bound=true`;

    let tileUrls = {
        Petal_Maps: `https://maprastertile-dra.dbankcdn.com/display-service/v1/online-render/getTile/24.08.06.20/{z}/{x}/{y}/?language=${customOptions.Language}&p=46&scale=1&mapType=ROADMAP&presetStyleId=standard&key=DAEDANitav6P7Q0lWzCzKkLErbrJG4kS1u%2FCpEe5ZyxW5u0nSkb40bJ%2BYAugRN03fhf0BszLS1rCrzAogRHDZkxaMrloaHPQGO6LNg==`,
        OpenStreetMap: "https://tile.openstreetmap.org/{z}/{x}/{y}.png",
        Map_Tiler:`https://api.maptiler.com/maps/${customOptions.Map_Tiler_Style}-v2/256/{z}/{x}/{y}.png?key=0epLOAjD7fw17tghcyee`,
        Thunderforest:`https://b.tile.thunderforest.com/${customOptions.Thunderforest_Style}/{z}/{x}/{y}@2x.png?apikey=6a53e8b25d114a5e9216df5bf9b5e9c8`,
        Carto:`https://cartodb-basemaps-3.global.ssl.fastly.net/${customOptions.Carto_Style}/{z}/{x}/{y}.png`,
        Google_Maps:`https://www.google.com/maps/vt?pb=!1m5!1m4!1i{z}!2i{x}!3i{y}!4i256!2m1!2sm!3m17!2sen!3sUS!5e18!12m4!1e68!2m2!1sset!2sRoadmap!12m3!1e37!2m1!1ssmartmaps!12m4!1e26!2m2!1sstyles!2ss.e:l|p.v:off,s.t:0.8|s.e:g.s|p.v:on!5m1!5f1.5`,
        Google_Terrain:`https://maps.googleapis.com/maps/vt?pb=!1m5!1m4!1i{z}!2i{x}!3i{y}!4i256!2m1!2sm!2m2!1e5!2sshading!2m2!1e6!2scontours!3m17!2s${customOptions.Language}!3sUS!5e18!12m4!1e68!2m2!1sset!2sTerrain!12m3!1e37!2m1!1ssmartmaps!12m4!1e26!2m2!1sstyles!2ss.e:l|p.v:off,s.t:0.8|s.e:g.s|p.v:on!5m1!5f1.5`,
        Google_Satellite:`https://mt3.google.com/vt/lyrs=s&hl=en&x={x}&y={y}&z={z}`,
        Google_StreetView:`https://maps.googleapis.com/maps/vt?pb=%211m5%211m4%211i{z}%212i{x}%213i{y}%214i256%212m8%211e2%212ssvv%214m2%211scc%212s*211m3*211e2*212b1*213e2*212b1*214b1%214m2%211ssvl%212s*212b1%213m17%212sen%213sUS%215e18%2112m4%211e68%212m2%211sset%212sRoadmap%2112m3%211e37%212m1%211ssmartmaps%2112m4%211e26%212m2%211sstyles%212ss.e%3Ag.f%7Cp.c%3A%23${colorOptions[customOptions.Google_StreetView_Layer_Lines_Style][0]}%7Cp.w%3A1%2Cs.e%3Ag.s%7Cp.c%3A%23${colorOptions[customOptions.Google_StreetView_Layer_Lines_Style][1]}%7Cp.w%3A3%215m1%215f1.35`,
        Google_Labels:`https://maps.googleapis.com/maps/vt?pb=!1m5!1m4!1i{z}!2i{x}!3i{y}!4i256!2m1!2sm!3m17!2s${customOptions.Language}!3sUS!5e18!12m4!1e68!2m2!1sset!2sRoadmap!12m3!1e37!2m1!1ssmartmaps!12m4!1e26!2m2!1sstyles!2ss.e:g|p.v:off,s.t:1|s.e:g.s|p.v:off,s.e:l|p.v:on!5m1!5f2`,
        Apple_StreetView:`https://lookmap.eu.pythonanywhere.com/bluelines_raster_2x/{z}/{x}/{y}.png`,
        Yandex_StreetView:`https://core-stv-renderer.maps.yandex.net/2.x/tiles?l=stv&x={x}&y={y}&z={z}&scale=1&v=2024.09.06.19.22-1_24.09.08-0-21241`,
        Yandex_Maps:`https://core-renderer-tiles.maps.yandex.net/tiles?l=map&v=24.09.06-3-b240906182700&x={x}&y={y}&z={z}&scale=1&lang=${customOptions.Language}_US`,
        OpenWeather: openWeatherURL
    }

    function getMap(){
        let element = document.getElementsByClassName("guess-map_canvas__cvpqv")[0]
        //if (!element) element=document.getElementsByClassName("coordinate-result-map_map__Yh2Il")[0]
        const keys = Object.keys(element)
        const key = keys.find(key => key.startsWith("__reactFiber$"))
        const props = element[key]
        map=props.return.return.memoizedProps.map
        google=unsafeWindow.google
        customMapType=setMapType()

    }

    function OC(controlDiv, layer) {
        controlDiv.style.margin='20px'
        controlDiv.style.backgroundColor = '#fff';
        controlDiv.style.height = '30px';
        controlDiv.style.boxShadow = 'rgba(0, 0, 0, 0.3) 0px 1px 4px -1px';
        controlDiv.style.borderRadius = '5px';

        var opacitySlider = document.createElement('input');

        opacitySlider.setAttribute('type', 'range');
        opacitySlider.setAttribute('min', '0');
        opacitySlider.setAttribute('max', '100');
        opacitySlider.setAttribute('value', '100');
        opacitySlider.setAttribute('step', '1');
        opacitySlider.style.width = '90px';
        opacitySlider.style.height='20px'
        opacitySlider.style.marginTop='5px'
        opacitySlider.addEventListener('input', function() {
            var opacity = opacitySlider.value / 100
            layer.set('opacity',opacity)
        });

        controlDiv.appendChild(opacitySlider);
    }

    function addOpacityControl(m, layer) {
        var opacityControlDiv = document.createElement('div');
        new OC(opacityControlDiv, layer);
        opacityControlDiv.id=layer.name
        opacityControlDiv.index = 1;
        m.controls[google.maps.ControlPosition.TOP_RIGHT].push(opacityControlDiv);
    }

    function removeOpacityControl(id) {
        var controls = map.controls[google.maps.ControlPosition.TOP_RIGHT];
        if(controls&&controls.getLength()>0){
            if(!id) controls.removeAt(0)
            else{
                for (var i = 0; i < controls.getLength(); i++) {
                    var control = controls.getAt(i);
                    if (control.id === id) {
                        controls.removeAt(i);
                        break;
                    }
                }
            }
        }
    }

    function MR(e, t) {
        return new Promise(n => {
            google.maps.event.addListenerOnce(e, t, n);
        });
    }

    function getNow(date) {
        if(date!='now'){
            return date
        }
        const now = new Date();
        now.setHours(now.getHours() - 1);
        return now.toISOString().slice(0, 14)+'00';
    }

    function getTimestamp(date){
        var parsedDate
        if (date=== 'now') {
            parsedDate= new Date()
            return Math.floor(parsedDate.getTime() / 1000)
        }
        parsedDate = new Date(date);

        if (isNaN(parsedDate.getTime())) {
            throw new Error('Invalid date format');
        }
        return Math.floor(parsedDate.getTime() / 1000);

    }

    function extractTileCoordinates(url) {
        const regex = /!1i(\d+)!2i(\d+)!3i(\d+)!4i(\d+)/;
        const matches = url.match(regex);

        if (matches && matches.length === 5) {
            const z = matches[1];
            const x = matches[2];
            const y = matches[3];
            return { z, x, y };
        } else {
            return null;
        }
    }

    function BaiduProjection() {
        var R = 6378206;
        var R_MINOR = 6356584.314245179;
        var bounds = new google.maps.LatLngBounds(
            new google.maps.LatLng(-19994619.55417086, -20037725.11268234),
            new google.maps.LatLng(19994619.55417086, 20037725.11268234)
        );

        this.fromLatLngToPoint = function(latLng) {

            var lat = latLng.lat() * Math.PI / 180;
            var lng = latLng.lng() * Math.PI / 180;

            var x = lng * R;
            var y = Math.log(Math.tan(Math.PI / 4 + lat / 2)) * R;

            var scale = 1 / Math.pow(2, 18);
            var origin = new google.maps.Point(bounds.getSouthWest().lng(), bounds.getNorthEast().lat());
            return new google.maps.Point(
                (x - origin.x) * scale,
                (origin.y - y) * scale
            );
        };

        this.fromPointToLatLng = function(point) {
            var scale = 1 / Math.pow(2, 18);
            var origin = new google.maps.Point(bounds.getSouthWest().lng(), bounds.getNorthEast().lat());

            var x = point.x / scale + origin.x;
            var y = origin.y - point.y / scale;

            var lng = x / R * 180 / Math.PI;
            var lat = (2 * Math.atan(Math.exp(y / R)) - Math.PI / 2) * 180 / Math.PI;

            return new google.maps.LatLng(lat, lng);
        };

        return this;
    }

    function getBingTiles(tileX, tileY, zoom,type) {
        var quadKey = tileXYToQuadKey(tileX, tileY, zoom);
        var baseUrl = 'https://ecn.t0.tiles.virtualearth.net/tiles/';

        return baseUrl + type + quadKey + '.jpeg?g=14519';
    }

    function tileXYToQuadKey(tileX, tileY, zoom) {
        var quadKey = '';
        for (var i = zoom; i > 0; i--) {
            var digit = 0;
            var mask = 1 << (i - 1);
            if ((tileX & mask) !== 0) {
                digit += 1;
            }
            if ((tileY & mask) !== 0) {
                digit += 2;
            }
            quadKey += digit.toString();
        }
        return quadKey;
    }

    function setMapType(){
        class customMapType extends google.maps.ImageMapType {
            constructor(layers, options = null) {
                const defaultOptions = {
                    getTileUrl: function(coord, zoom) {
                        return null;
                    },
                    tileSize: new google.maps.Size(256, 256),
                    maxZoom: 20,
                    name: 'CustomMapType',
                };


                super({...defaultOptions, ...options});
                this.layers = layers;
            }

            getTile(t, n, r) {
                const o = this.layers.map(i => {
                    if (typeof i.getTile !== 'function') {
                        console.error('getTile method is missing in layer:', i);
                    }
                    return i.getTile(t, n, r);
                });
                const s = document.createElement("div");
                s.append(...o);

                Promise.all(o.map(i => MR(i, "load"))).then(() => {
                    google.maps.event.trigger(s, "load");
                });

                return s;
            }


            releaseTile(tile) {
                let index = 0;
                for (const child of tile.children) {
                    if (child instanceof HTMLElement) {
                        this.layers[index]?.releaseTile(child);
                        index += 1;
                    }
                }
            }
        }
        return customMapType
    }

    function setTileLayer(layerName){

        var tileLayer
        const tileUrl = tileUrls[layerName];
        if (layerName==='Bing_Maps'){
            tileLayer = new google.maps.ImageMapType({
                getTileUrl: function(coord, zoom) {
                    return getBingTiles(coord.x,coord.y,zoom,customOptions.Bing_Maps_Style)
                        .replace('{z}', zoom)
                        .replace('{x}', coord.x)
                        .replace('{y}', coord.y);
                },
                tileSize: new google.maps.Size(256, 256),
                name: layerName,
                maxZoom:20
            });}
        else{
            tileLayer = new google.maps.ImageMapType({
                getTileUrl: function(coord, zoom) {
                    return tileUrl
                        .replace('{z}', zoom)
                        .replace('{x}', coord.x)
                        .replace('{y}', coord.y);
                },
                tileSize: new google.maps.Size(256, 256),
                name:layerName,
                maxZoom:20,
            })}
        if (layerName.includes('StreetView') || layerName.includes('Weather')) {
            if (!document.getElementById(layerName)) addOpacityControl(map, tileLayer);
        }
        return tileLayer
    }


    let onKeyDown = (e) => {
        if ( e.target.tagName === 'TEXTAREA' || e.target.isContentEditable) return
        if (e.key >= '1' && e.key <= '7') {
            e.stopImmediatePropagation();
            if(!map) getMap()
            const tileIndex=parseInt(e.key)
            const layerName=tileServices[tileIndex]
            if(!currentLayers.includes(layerName)){
                initLayer(`layer:${layerName}`)
                currentLayers[0]=layerName
                currentLayers = currentLayers.filter(layer => layer !== 'Google_Labels')
                const layers =new customMapType(currentLayers.map(layerName => setTileLayer(layerName)));
                map.mapTypes.set("overlap",layers)
            }

            else {
                currentLayers[0]='Google_Maps'
                if(!currentLayers.includes('Google_Labels'))currentLayers.push('Google_Labels')
                const layers =new customMapType(currentLayers.map(layerName => setTileLayer(layerName)));
                map.mapTypes.set("overlap",layers)
            }
        }

        else if (e.key === '0') {
            e.stopImmediatePropagation();
            if(!map) getMap()
            currentLayers[0]='Google_Maps'
            initLayer('layer:Classic Style')
            if(!currentLayers.includes('Google_Labels'))currentLayers.push('Google_Labels')
            const layers =new customMapType(currentLayers.map(layerName => setTileLayer(layerName)));
            map.mapTypes.set("overlap",layers)
        }

        else if (e.key === customOptions.Google_StreetView_Layer_Shortcut.toLowerCase()|| e.key === customOptions.Google_StreetView_Layer_Shortcut) {
            e.stopImmediatePropagation();
            if(!map) getMap()
            initLayer(`layer:Google_StreetView`)
            if(!currentLayers.includes('Google_StreetView')){
                currentLayers.splice(1, 0, 'Google_StreetView');
                const layers =new customMapType(currentLayers.map(layerName => setTileLayer(layerName)));
                map.mapTypes.set("overlap",layers)
            }
            else{currentLayers = currentLayers.filter(layer => layer !== 'Google_StreetView')
                 removeOpacityControl('Google_StreetView')
                 const layers =new customMapType(currentLayers.map(layerName => setTileLayer(layerName)));
                 map.mapTypes.set("overlap",layers)}
        }

        else if (e.key === customOptions.Apple_StreetView_Layer_Shortcut.toLowerCase()|| e.key === customOptions.Apple_StreetView_Layer_Shortcut) {
            e.stopImmediatePropagation();
            if(!map) getMap()
            if(!currentLayers.includes('Apple_StreetView')){
                initLayer(`layer:Apple_StreetView`)
                currentLayers.splice(1, 0, 'Apple_StreetView');
                const layers =new customMapType(currentLayers.map(layerName => setTileLayer(layerName)));
                map.mapTypes.set("overlap",layers)
            }
            else{currentLayers = currentLayers.filter(layer => layer !== 'Apple_StreetView')
                 removeOpacityControl('Apple_StreetView')
                 const layers =new customMapType(currentLayers.map(layerName => setTileLayer(layerName)));
                 map.mapTypes.set("overlap",layers)}
        }

        else if (e.key === customOptions.Yandex_StreetView_Layer_Shortcut.toLowerCase()|| e.key === customOptions.Yandex_StreetView_Layer_Shortcut) {
            e.stopImmediatePropagation();
            if(!map) getMap()
            initLayer(`layer:Yandex_StreetView`)
            if(!currentLayers.includes('Yandex_StreetView')){
                currentLayers.splice(1, 0, 'Yandex_StreetView');
                const layers =new customMapType(currentLayers.map(layerName => setTileLayer(layerName)));
                map.mapTypes.set("overlap",layers)
            }
            else{currentLayers = currentLayers.filter(layer => layer !== 'Yandex_StreetView')
                 removeOpacityControl('Yandex_StreetView')
                 const layers =new customMapType(currentLayers.map(layerName => setTileLayer(layerName)));
                 map.mapTypes.set("overlap",layers)}
        }

        else if (e.key === customOptions.Google_Labels_Layer_Shortcut.toLowerCase()|| e.key === customOptions.Google_Labels_Layer_Shortcut) {
            e.stopImmediatePropagation();
            if(!map) getMap()
            initLayer(`layer:Google_Labels`)
            if(!currentLayers.includes('Google_Labels')){
                currentLayers.push('Google_Labels');
                const layers =new customMapType(currentLayers.map(layerName => setTileLayer(layerName)))
                map.mapTypes.set("overlap",layers)}

            else{currentLayers = currentLayers.filter(layer => layer !== 'Google_Labels')
                 const layers =new customMapType(currentLayers.map(layerName => setTileLayer(layerName)))
                 map.mapTypes.set("overlap",layers)}
        }

        else if (e.key === customOptions.Google_Terrain_Layer_Shortcut.toLowerCase()|| e.key === customOptions.Google_Terrain_Layer_Shortcut) {
            e.stopImmediatePropagation();
            if(!map) getMap()
            initLayer(`layer:Google_Terrain`)
            if(!currentLayers.includes('Google_Terrain')){
                currentLayers[0]='Google_Terrain'
                if(!currentLayers.includes('Google_Labels'))currentLayers.push('Google_Labels')
                const layers =new customMapType(currentLayers.map(layerName => setTileLayer(layerName)));
                map.mapTypes.set("overlap",layers)}
            else {
                currentLayers[0]='Google_Maps'
                if(!currentLayers.includes('Google_Labels'))currentLayers.push('Google_Labels')
                const layers =new customMapType(currentLayers.map(layerName => setTileLayer(layerName)));
                map.mapTypes.set("overlap",layers)
            }
        }

        else if (e.key === customOptions.Google_Satellite_Layer_Shortcut.toLowerCase()|| e.key === customOptions.Google_Satellite_Layer_Shortcut) {
            e.stopImmediatePropagation();
            if(!map) getMap()
            initLayer(`layer:Google_Satellite`)
            if(!currentLayers.includes('Google_Satellite')){
                currentLayers[0]='Google_Satellite'
                if(!currentLayers.includes('Google_Labels'))currentLayers.push('Google_Labels')
                const layers =new customMapType(currentLayers.map(layerName => setTileLayer(layerName)));
                map.mapTypes.set("overlap",layers)}
            else {
                currentLayers[0]='Google_Maps'
                if(!currentLayers.includes('Google_Labels'))currentLayers.push('Google_Labels')
                const layers =new customMapType(currentLayers.map(layerName => setTileLayer(layerName)));
                map.mapTypes.set("overlap",layers)
            }
        }

        else if (e.key === customOptions.OpenWeather_Shortcut.toLowerCase()|| e.key === customOptions.OpenWeather_Shortcut) {
            e.stopImmediatePropagation();
            if(!map) getMap()
            if(!currentLayers.includes('OpenWeather')){
                initLayer(`layer:OpenWeather`)
                currentLayers.splice(1, 0, 'OpenWeather');
                const layers =new customMapType(currentLayers.map(layerName => setTileLayer(layerName)));
                map.mapTypes.set("overlap",layers)
            }
            else{currentLayers = currentLayers.filter(layer => layer !== 'OpenWeather')
                 removeOpacityControl('OpenWeather')
                 const layers =new customMapType(currentLayers.map(layerName => setTileLayer(layerName)));
                 map.mapTypes.set("overlap",layers)}
        }

        map.setMapTypeId("overlap")
    }

    document.addEventListener("keydown", onKeyDown);

})();