Geoguessr Map-Making Auto-Tag

Tag your street views by date and address

目前為 2023-09-27 提交的版本,檢視 最新版本

// ==UserScript==
// @name         Geoguessr Map-Making Auto-Tag
// @namespace    http://tampermonkey.net/
// @version      2.5
// @description  Tag your street views by date and address
// @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 = [];

        async function getMetaData(url) {
            try {
                const response = await fetch(url);
                const data = await response.json();
                if (data.status == "OK") {
                    let year = 'nodate';
                    const match = data.date.match(/\d{4}/);
                    if (match) {
                        year = match[0];
                    }
                    let panoType = 'unofficial';
                    if (data.copyright.includes('Google')) {
                        panoType = 'official';
                    }
                    return [year, panoType];
                } else {
                    return ["nodata","nodata"];
                }
            } catch (error) {
                console.error(`Error fetching metadata: ${error}`);
            }
        }

        function get_Meta(id) {
            const url = `https://maps.googleapis.com/maps/api/streetview/metadata?pano=${id}&key=${api_key}`;
            return getMetaData(url);
        }

        function search_Meta(lat, lng) {
            const url = `https://maps.googleapis.com/maps/api/streetview/metadata?location=${lat},${lng}&key=${api_key}`;
            return getMetaData(url);
        }

        let last_token = null;
        let last_token_expiry = 0;

        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 getAddress(url) {
            let country = 'nodata';
            let subdivision = 'nodata';
            let locality = 'nodata';
            try {
                let response = await fetch(url);
                if (response.status == 200) {
                    let data = await response.json();
                    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;
                        }
                    }
                }
            } catch (error) {
                console.log(error);
            }
            return [country, subdivision, locality];
        }

        async function get_add(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 getAddress(url);
        }

        async function search_add(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 getAddress(url);
        }


        var CHUNK_SIZE = 1000;
        var promises = [];

        async function processCoord(coord, tags, svData) {
            if (!coord.extra) {
                coord.extra = {};
            }
            if (!coord.extra.tags) {
                coord.extra.tags = [];
            }
            var meta;
            var address;
            if (coord.panoId && (tags.includes('year') || tags.includes('type'))) {
                meta = await get_Meta(coord.panoId);
            } else if (tags.includes('year') || tags.includes('type')) {
                meta = await search_Meta(coord.lat, coord.lng);
            }

            if (coord.panoId && (tags.includes('country') || tags.includes('subdivision') || tags.includes('locality'))) {
                address= await get_add(coord.panoId);
            } else if (tags.includes('country') || tags.includes('subdivision') || tags.includes('locality')) {
                address= await search_add(coord.lat, coord.lng);
            }

            if (meta && meta.length >= 2) {
                var year_tag = meta[0];
                var type_tag = meta[1];

                if (tags.includes('year')) coord.extra.tags.push(year_tag);
                if (tags.includes('type')) coord.extra.tags.push(type_tag);
            }

            if (address && address.length >= 3) {
                var country_tag=address[0]
                var subdivision_tag=address[1]
                var locality_tag=address[2]

                if (tags.includes('country')) coord.extra.tags.push(country_tag);
                if (tags.includes('subdivision')) coord.extra.tags.push(subdivision_tag);
                if (tags.includes('locality')) coord.extra.tags.push(locality_tag);
            }

            if (svData) {

                let trekkerTag = (svData.hasOwnProperty('dn') && svData.dn) ? 'trekker' : null;

                if (tags.includes('type') && trekkerTag) {
                    coord.extra.tags.push(trekkerTag);
                }
            }

            newData.push(coord);
        }

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

                if ((panoId || latLng) && tags.includes('type')) {
                    svData = await getSVData(service, panoId ? {pano: panoId} : {location: latLng, radius: 50});
                }

                await processCoord(coord, tags, svData)
            });

            await Promise.all(promises);
        }

        function getSVData(service, options) {
            return new Promise(resolve => service.getPanorama(options, (data, status) => {
                resolve(data);
            }));
        }

        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';
    document.body.appendChild(buttonContainer);

    function createButton(text, tags) {
        var button = document.createElement('button');
        button.textContent = text;
        button.style.display = 'none';
        button.addEventListener('click', async function() { await runScript(tags); });
        buttonContainer.appendChild(button);
        return button;
    }

    var mainButton = document.createElement('button');
    mainButton.textContent = 'Auto-Tag';
    mainButton.addEventListener('click', function() {

        for (var i = 0; i < buttonContainer.children.length; i++) {
            var button = buttonContainer.children[i];
            if (button.style.display === 'none') {
                button.style.display = 'block';
            } else {
                button.style.display = 'none';
            }
        }
    });
    mainButtonContainer.appendChild(mainButton);



    createButton('Tag by Year', ['year']);
    createButton('Tag by Type',[ 'type']);
    createButton('Tag by Country', ['country']);
    createButton('Tag by Subdivision', ['subdivision']);
    createButton('Tag by Locality', ['locality']);
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址