Geoguessr Map-Making Auto-Tag

Tag your street views by date&address&generations(API key is need)

当前为 2023-09-30 提交的版本,查看 最新版本

// ==UserScript==
// @name         Geoguessr Map-Making Auto-Tag
// @namespace    http://tampermonkey.net/
// @version      2.9
// @description  Tag your street views by date&address&generations(API key is need)
// @author       KaKa
// @match        https://map-making.app/maps/*
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_setClipboard
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    async function runScript(tags) {
        let api_key = GM_getValue("api_key");
        if (!api_key) {
            api_key = prompt("Please enter your Google API key");
            GM_setValue("api_key", api_key);
        }

        const option = confirm('Do you want to input data from the clipboard? If you click "Cancel", you will need to upload a JSON file.');

        let data;
        if (option) {

            const text = await navigator.clipboard.readText();
            try {
                data = JSON.parse(text);
            } catch (error) {
                alert('The input JSON data is invalid or incorrectly formatted.');
                return;
            }
        } else {

            const input = document.createElement('input');
            input.type = 'file';
            document.body.appendChild(input);

            data = await new Promise((resolve) => {
                input.addEventListener('change', async () => {
                    const file = input.files[0];
                    const reader = new FileReader();

                    reader.onload = (event) => {
                        try {
                            const result = JSON.parse(event.target.result);
                            resolve(result);

                            document.body.removeChild(input);
                        } catch (error) {
                            alert('The input JSON data is invalid or incorrectly formatted.');
                        }
                    };

                    reader.readAsText(file);
                });


                input.click();
            });
        }
        const newData = [];

        let last_token = null;
        let last_token_expiry = 0;

        function getGeneration(data,country){
            if (data&&data.imageHeight) {
                if (data.imageHeight === 1664) {
                    return 'Gen1'
                } else if (data.imageHeight === 6656) {

                    let lat=data.lat

                    let date;
                    if (data.date) {
                        date = new Date(data.date);
                    } else {
                        date = 'nodata';
                    }
                    if (date!=='nodata'&&((country == 'Bangladesh' && (date >= new Date('2021-04'))) ||
                                          (country == 'Ecuador' && (date >= new Date('2022-03'))) ||
                                          (country == 'Finland' && (date >= new Date('2020-09'))) ||
                                          (country == 'India' && (date >= new Date('2021-10'))) ||
                                          (country == 'Sri Lanka' && (date >= new Date('2021-02'))) ||
                                          (country == 'Cambodia' && (date >= new Date('2022-10'))) ||
                                          (country == 'Nigeria' && (date >= new Date('2021-06'))) ||
                                          (country == 'United States' && lat > 52 && (date >= new Date('2019-01'))))) {
                        return 'Shitcam'
                    }

                    else if ((country === 'Australia' || 'Brazil'||'Canada' || 'Chile'|| 'Japan' || 'United Kingdom' || 'Ireland' || 'New Zealand'
                              || 'Mexico'|| 'Russia' || 'United States' || 'Italy'||'Denmark' || 'Greece' || 'Romania' || 'Poland'
                              || 'Czechia' )|| 'Switzerland'|| 'Sweden' || 'Finland'|| 'Belgium' || 'Luxembourg' || 'Netherlands' || 'South Africa'
                             || 'Singapore' || 'Taiwan' || 'Hong Kong' || 'Monaco'||'Macao' || 'San Marino' || 'Andorra' || 'Isle of Man'
                             ||'Jersey'||'France' ||'Germany' || 'Spain'|| 'Portugal' ) {

                        return 'Gen2or3'
                    }
                    else {return 'Gen3'}
                }

                else if(data.imageHeight === 8192){
                    return 'Gen4'
                }
            }
        }

        async function get_Token(api_key) {
            let current_time = Date.now() / 1000;
            if (last_token && last_token_expiry > current_time) {
                return last_token;
            }
            let url = `https://tile.googleapis.com/v1/createSession?key=${api_key}`;
            let headers = {'Content-Type': 'application/json'};
            let data = { "mapType": "streetview",
                        "language": "en-US",
                        "region": "US"};
            let response = await fetch(url, {method: 'POST', headers: headers, body: JSON.stringify(data)});
            if (response.status == 200) {
                let token = (await response.json()).session;
                last_token_expiry = current_time + 5 * 60;
                last_token = token;
                return token;
            } else {
               alert(`Error: ${response.status}, ${await response.text()}`);
            }
        }

        async function getMeta(url) {
            let country = 'nocountry',subdivision = 'nosub',locality = 'nolocality',route='noroute'
            let year='noyear',month='nomonth'
            let panoType='Unofficial',generation

            try {
                let response = await fetch(url);
                if (response.status == 200) {
                    let data = await response.json();

                    if (data.date) {
                        const matchYear = data.date.match(/\d{4}/);
                        if (matchYear) {
                            year = matchYear[0];
                        }

                        const matchMonth = data.date.match(/-(\d{2})/);
                        if (matchMonth) {
                            month = matchMonth[1];
                        }
                    }

                    if (data.copyright.includes('Google')) {
                        panoType = 'Official';
                    }

                    for (let add of data.addressComponents) {
                        if (add.types.includes('country')) {
                            country = add.longName;
                        }
                        if (add.types.includes('administrative_area_level_1')) {
                            subdivision = add.longName;
                        }
                        if (add.types.includes('locality')) {
                            locality = add.longName;
                        }
                        if (add.types.includes('route')) {
                            route = add.longName;
                        }
                    }
                 generation=getGeneration(data,country)
                }
            return [country, subdivision, locality,route,year,month,panoType,generation]
            }
            catch (error) {
                console.log(error);
            }

        }

        async function get_meta(id) {
            let tk = await get_Token(api_key);
            let url = `https://tile.googleapis.com/v1/streetview/metadata?session=${tk}&key=${api_key}&panoId=${id}`;
            return getMeta(url);
        }

        async function search_meta(lat,lng) {
            let tk = await get_Token(api_key);
            let url = `https://tile.googleapis.com/v1/streetview/metadata?session=${tk}&key=${api_key}&lat=${lat}&lng=${lng}&radius=50`;
            return getMeta(url);
        }




        var CHUNK_SIZE = 1200;
        var promises = [];

        async function processCoord(coord, tags) {
            if (!coord.extra) {
                coord.extra = {};
            }
            if (!coord.extra.tags) {
                coord.extra.tags = [];
            }

            var meta;

            if (coord.panoId ) {
                meta = await get_meta(coord.panoId)
            }

            else {
                meta =await search_meta(coord.lat,coord.lng)
            }

            if (meta){

                let countryTag=meta[0]
                let subdivisionTag=meta[1]
                let localityTag=meta[2]
                let routeTag=meta[3]
                let yearTag=meta[4]
                let monthTag=meta[5]
                let typeTag=meta[6]
                let generationTag=meta[7]


                if (tags.includes('country')) coord.extra.tags.push(countryTag);
                if (tags.includes('subdivision')) coord.extra.tags.push(subdivisionTag);
                if (tags.includes('locality')) coord.extra.tags.push(localityTag);
                if (tags.includes('route')) coord.extra.tags.push(routeTag);
                if (tags.includes('year')) coord.extra.tags.push(yearTag);
                if (tags.includes('month')) coord.extra.tags.push(monthTag);
                if (tags.includes('type')) coord.extra.tags.push(typeTag);
                if (tags.includes('generation')&&typeTag=='Official') coord.extra.tags.push(generationTag);

            }
            else{
                coord.extra.tags.push('nopano')
            }

            newData.push(coord);
        }

        async function processChunk(chunk, tags) {
            var promises = chunk.map(async coord => {
                let panoId = coord.panoId;
                let latLng = {lat: coord.lat, lng: coord.lng};

                await processCoord(coord, tags)
            });

            await Promise.all(promises);
        }



        async function processData(tags) {
            try {

                for (let i = 0; i < data.customCoordinates.length; i += CHUNK_SIZE) {
                    let chunk = data.customCoordinates.slice(i, i + CHUNK_SIZE);
                    await processChunk(chunk, tags);
                }

                GM_setClipboard(JSON.stringify(newData));
                alert("New JSON data has been copied to the clipboard!");
            } catch (error) {
                alert("Invalid JSON data");
                console.error('Error processing JSON data:', error);
            }
        }
        processData(tags);
    }

    var mainButtonContainer = document.createElement('div');
    mainButtonContainer.style.position = 'fixed';
    mainButtonContainer.style.right = '20px';
    mainButtonContainer.style.bottom = '20px';
    document.body.appendChild(mainButtonContainer);

    var buttonContainer = document.createElement('div');
    buttonContainer.style.position = 'fixed';
    buttonContainer.style.right = '20px';
    buttonContainer.style.bottom = '60px';
    buttonContainer.style.display = 'none';
    document.body.appendChild(buttonContainer);

    function createCheckbox(text, tags) {
        var label = document.createElement('label');
        var checkbox = document.createElement('input');
        checkbox.type = 'checkbox';
        checkbox.value = text;
        checkbox.name = 'tags';
        checkbox.id = tags;
        label.appendChild(checkbox);
        label.appendChild(document.createTextNode(text));
        buttonContainer.appendChild(label);
        return checkbox;
    }

    var triggerButton = document.createElement('button');
    triggerButton.textContent = 'Star Tagging';
    triggerButton.addEventListener('click', function() {
        var checkboxes = document.getElementsByName('tags');
        var checkedTags = [];
        for (var i=0; i<checkboxes.length; i++) {
            if (checkboxes[i].checked) {
                checkedTags.push(checkboxes[i].id);
            }
        }
        runScript(checkedTags);
    });
    buttonContainer.appendChild(triggerButton);

    var mainButton = document.createElement('button');
    mainButton.textContent = 'Auto-Tag';
    mainButton.addEventListener('click', function() {
        if (buttonContainer.style.display === 'none') {
            buttonContainer.style.display = 'block';
        } else {
            buttonContainer.style.display = 'none';
        }
    });
    mainButtonContainer.appendChild(mainButton);

    createCheckbox('Year', 'year');
    createCheckbox('Month', 'month');
    createCheckbox('Type', 'type');
    createCheckbox('Country', 'country');
    createCheckbox('Subdivision', 'subdivision');
    createCheckbox('Locality', 'locality');
    createCheckbox('Route', 'route');
    createCheckbox('Generations', 'generation');
})();