Geoguessr Activities Analysis Tool

map visualization of your geoguessr activities

当前为 2024-05-25 提交的版本,查看 最新版本

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         Geoguessr Activities Analysis Tool
// @version      1.50
// @description  map visualization of your geoguessr activities
// @author       KaKa
// @match        https://map-making.app/
// @require      https://cdn.jsdelivr.net/npm/sweetalert2@11
// @license      MIT
// @icon         https://www.svgrepo.com/show/191885/map.svg
// @grant        GM_xmlhttpRequest
// @grant        GM_setClipboard
// @grant        GM_addStyle
// @namespace http://tampermonkey.net/
// ==/UserScript==
(function() {
    'use strict';
    let pinSvg=`<svg viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" fill="#000000"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"><title>70 Basic icons by Xicons.co</title><path d="M24,1.32c-9.92,0-18,7.8-18,17.38A16.83,16.83,0,0,0,9.57,29.09l12.84,16.8a2,2,0,0,0,3.18,0l12.84-16.8A16.84,16.84,0,0,0,42,18.7C42,9.12,33.92,1.32,24,1.32Z" fill="#a9a3a2"></path><path d="M25.37,12.13a7,7,0,1,0,5.5,5.5A7,7,0,0,0,25.37,12.13Z" fill="#f3edec"></path></g></svg>`
    let shareSvg=`<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" width="22px" height="22px" stroke="#ffffff"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"> <path d="M21 9.00001L21 3.00001M21 3.00001H15M21 3.00001L12 12M10 3H7.8C6.11984 3 5.27976 3 4.63803 3.32698C4.07354 3.6146 3.6146 4.07354 3.32698 4.63803C3 5.27976 3 6.11984 3 7.8V16.2C3 17.8802 3 18.7202 3.32698 19.362C3.6146 19.9265 4.07354 20.3854 4.63803 20.673C5.27976 21 6.11984 21 7.8 21H16.2C17.8802 21 18.7202 21 19.362 20.673C19.9265 20.3854 20.3854 19.9265 20.673 19.362C21 18.7202 21 17.8802 21 16.2V14" stroke="#d3cfcf" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path> </g></svg>`
    let linkSvg=`<svg fill="#d3cfcf" version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="29px" height="29px" viewBox="0 0 100 100" enable-background="new 0 0 100 100" xml:space="preserve" stroke="#d3cfcf"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"> <g> <path d="M78.746,33.028L61.62,17.213c-0.368-0.336-0.85-0.713-1.349-0.713H42.634c-5.407,0-9.134,2.81-9.134,6.877V28.5h-3.358 c-4.946,0-8.642,3.124-8.642,7.369V75.87c0,4.492,4.077,7.63,8.642,7.63h29.231c4.677,0,8.127-3.288,8.127-7.63V71.5h4.365 c4.41,0,7.635-3.513,7.635-8.122V35.055c0-0.029,0.242-0.057,0.241-0.085c0.001-0.03,0.134-0.059,0.134-0.089 C79.875,34.041,79.48,33.324,78.746,33.028z M61.5,23.165L72.649,32.5H61.5V23.165z M63.5,75.87c0,2.363-1.938,3.63-4.127,3.63 H30.142c-2.323,0-4.642-1.381-4.642-3.63V35.869c0-2.6,3.095-3.369,4.642-3.369H45.5v15.381c0,1.104,1.396,1.619,2.5,1.619h15.5 V75.87z M61.216,45.5H49.5v-9.878L61.216,45.5z M71.865,67.5H67.5V47.547c0-0.562,0.014-1.097-0.4-1.476l-17.43-16.059 c-0.324-0.667-0.94-1.132-1.732-1.132c-0.036,0-0.039-0.182-0.075-0.18c-0.038-0.002-0.044-0.201-0.083-0.201H37.5v-5.123 c0-2.063,3.02-2.877,5.134-2.877H57.5v14.381c0,1.104,1.396,1.619,2.5,1.619h15.5v26.878C75.5,65.776,74.068,67.5,71.865,67.5z"></path> </g> </g></svg>`
    let loadingSvg=`<svg fill="#d3cfcf" version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="22px" height="22px" viewBox="0 0 330 330" xml:space="preserve"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"> <g id="XMLID_19_"> <path id="XMLID_20_" d="M307.5,150h-60c-8.284,0-15,6.716-15,15s6.716,15,15,15h60c8.284,0,15-6.716,15-15S315.784,150,307.5,150z"></path> <path id="XMLID_21_" d="M52.5,150h-30c-8.284,0-15,6.716-15,15s6.716,15,15,15h30c8.284,0,15-6.716,15-15S60.784,150,52.5,150z"></path> <path id="XMLID_22_" d="M157.5,240c-8.284,0-15,6.716-15,15v60c0,8.284,6.716,15,15,15c8.284,0,15-6.716,15-15v-60 C172.5,246.716,165.784,240,157.5,240z"></path> <path id="XMLID_23_" d="M157.5,0c-8.284,0-15,6.716-15,15v60c0,8.284,6.716,15,15,15c8.284,0,15-6.716,15-15V15 C172.5,6.716,165.784,0,157.5,0z"></path> <path id="XMLID_24_" d="M231.746,218.033c-5.857-5.858-15.355-5.858-21.213,0c-5.858,5.858-5.858,15.355,0,21.213l42.427,42.427 c2.929,2.929,6.767,4.393,10.606,4.393s7.678-1.464,10.607-4.393c5.858-5.858,5.858-15.355,0-21.213L231.746,218.033z"></path> <path id="XMLID_25_" d="M62.04,48.328c-5.857-5.857-15.355-5.858-21.213,0c-5.858,5.858-5.858,15.356,0,21.213l42.427,42.426 c2.929,2.929,6.768,4.393,10.606,4.393c3.839,0,7.678-1.464,10.607-4.394c5.857-5.858,5.857-15.355,0-21.213L62.04,48.328z"></path> <path id="XMLID_26_" d="M221.14,116.36c3.838,0,7.678-1.465,10.607-4.393l42.427-42.426c5.858-5.858,5.858-15.355,0-21.213 c-5.859-5.858-15.355-5.857-21.213,0l-42.427,42.426c-5.858,5.858-5.858,15.355,0,21.213 C213.462,114.896,217.3,116.36,221.14,116.36z"></path> </g> </g></svg>`
    let successSvg=`<svg viewBox="-25.6 -25.6 563.20 563.20" xmlns="http://www.w3.org/2000/svg" width="22px" height="22px" fill="#000000" stroke="#000000"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"> <defs> <style>.cls-1{fill:none;stroke:#d3cfcf;stroke-linecap:round;stroke-linejoin:round;stroke-width:35.839999999999996;}</style> </defs> <g data-name="Layer 2" id="Layer_2"> <g data-name="E408, Success, Media, media player, multimedia" id="E408_Success_Media_media_player_multimedia"> <circle class="cls-1" cx="256" cy="256" r="246"></circle> <polyline class="cls-1" points="115.54 268.77 200.67 353.9 396.46 158.1"></polyline> </g> </g> </g></svg>`
    const svgBlob = new Blob([pinSvg], {type: 'image/svg+xml'});
    const svgUrl = URL.createObjectURL(svgBlob);
    var u = 'https://www.geoguessr.com/api/v4/feed/friends?count=26';
    var map,heatmapLayer,streetViewContainer,streetViewMap
    let myNick
    let activities =JSON.parse(localStorage.getItem('activities'));
    let isUpdated=JSON.parse(localStorage.getItem('isUpdated'));
    const fontAwesomeCDN = document.createElement('link');
    fontAwesomeCDN.rel = 'stylesheet';
    fontAwesomeCDN.href = 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css';
    document.head.appendChild(fontAwesomeCDN);

    getMyNick().then(nick => {
        myNick = nick;
    })
    .catch(error => {
        console.error("Failed to fetch user nick:", error);
    });
    if (!activities){
        activities={'duels':{},'games':{}};
    }

    function getMyNick() {
        return new Promise((resolve, reject) => {
            GM_xmlhttpRequest({
                method: "GET",
                url: "https://geoguessr.com/api/v3/profiles",
                onload: function(response) {
                    if (response.status === 200) {
                        const data = JSON.parse(response.responseText);
                        const myId=data.user.id
                        const nickMap={}
                        nickMap[myId]=data.user.nick
                        resolve(nickMap);
                    } else {
                        console.error("Error fetching user data: " + response.statusText);
                        reject(null);
                    }
                },
                onerror: function(error) {
                    console.error("Error fetching user data:", error);
                    reject(null);
                }
            });
        });
    }

    function matchPlayerNick(gameData, friendMap) {
        gameData.forEach(game => {
            const playerId = game.playerId;
            Object.assign(friendMap,myNick)
            game.playerNick = friendMap[playerId] || "Opponents";
        });
        return gameData;
    }

    function getFriends() {
        return new Promise((resolve, reject) => {
            GM_xmlhttpRequest({
                method: "GET",
                url: "https://www.geoguessr.com/api/v3/social/friends/summary?page=0&fast=true",
                onload: function(response) {
                    if (response.status === 200) {
                        try {
                            const data = JSON.parse(response.responseText);
                            const friendMap = {};
                            data.friends.forEach(friend => {
                                friendMap[friend.userId] = friend.nick;
                            });
                            resolve(friendMap);
                        } catch (error) {
                            console.error("Error parsing JSON: ", error);
                            reject(null);
                        }
                    } else {
                        console.error("Error fetching user data: " + response.statusText);
                        reject(null);
                    }
                },
                onerror: function(error) {
                    console.error("Error fetching user data:", error);
                    reject(null);
                }
            });
        });
    }

    function getDiffdays(dataList) {
        const currentDate = new Date();
        return dataList.map(item => {
            if ('gameDate' in item) {
                const gameDate = new Date(item.gameDate);
                const timeDiff = Math.abs(currentDate - gameDate);
                const daysDiff = Math.floor(timeDiff / (1000 * 60 * 60 * 24));
                item.gameDate = daysDiff;
            }
            return item;
        });
    }

    function saveLocalStorage(item) {
        localStorage.setItem('activities', JSON.stringify(item));
        activities =JSON.parse(localStorage.getItem('activities'));
        localStorage.setItem('isUpdated',JSON.stringify('updated'));

    }

    function downloadJSON(coords,format) {
        function processData(coords) {
            return new Promise((resolve, reject) => {
                var convertedData = {
                    "name": "coordinates",
                    "customCoordinates": coords.map(function(item) {
                        var scoreCategory;
                        if (item.score >= 0 && item.score <= 1499) {
                            scoreCategory = "0-1499";
                        } else if (item.score >= 1500 && item.score <= 2999) {
                            scoreCategory = "1500-2999";
                        } else if (item.score >= 3000 && item.score <= 4499) {
                            scoreCategory = "3000-4499";
                        } else if (item.score >= 4500 && item.score <= 4999) {
                            scoreCategory = "4500-4999";
                        } else {
                            scoreCategory = "5000";
                        }
                        var tags = [];
                        if (item.gameMode !== null) {
                            tags.push(item.gameMode);
                        }
                        if (item.playerNick !== null) {
                            tags.push(item.playerNick);
                        }
                        if (item.forbidOptions !== null) {
                            tags.push(item.forbidOptions);
                        }
                        if (item.usingMap !== null) {
                            tags.push(item.usingMap);
                        }
                        tags.push(scoreCategory);
                        return {
                            "lat": item.latLng.lat,
                            "lng": item.latLng.lng,
                            "heading": item.heading,
                            "pitch": item.pitch,
                            "zoom": item.zoom,
                            "panoId": null,
                            "countryCode": null,
                            "stateCode": null,
                            "extra": {
                                "tags": tags
                            }
                        };
                    })
                };
                resolve(convertedData);
            });
}
        processData(coords)
            .then((convertedData) => {

            if (format) {
                GM_setClipboard(JSON.stringify(convertedData));
            }
            else{
        var originalJson = JSON.stringify(coords, null, 2);
        var originalBlob = new Blob([originalJson], { type: 'application/json' });
        var originalUrl = URL.createObjectURL(originalBlob);

        var originalLink = document.createElement('a');
        originalLink.href = originalUrl;
        originalLink.download = 'original_data.json';
        document.body.appendChild(originalLink);
        originalLink.click();
        document.body.removeChild(originalLink);


        var convertedJson = JSON.stringify(convertedData, null, 2);
        var convertedBlob = new Blob([convertedJson], { type: 'application/json' });
        var convertedUrl = URL.createObjectURL(convertedBlob);

        var convertedLink = document.createElement('a');
        convertedLink.href = convertedUrl;
        convertedLink.download = 'map-making_data.json';
        document.body.appendChild(convertedLink);
        convertedLink.click();
        document.body.removeChild(convertedLink);}
        })
            .catch((error) => {
            console.error("处理数据时发生错误:", error);
        });

    }

    function clearLocalStorage() {
        localStorage.removeItem('activities');
        localStorage.removeItem('isUpdated');
    }

    function loadScript(url) {
        return new Promise((resolve, reject) => {
            var script = document.createElement('script');
            script.type = 'text/javascript';
            script.src = url;
            script.async = true;
            script.onload = resolve;
            script.onerror = reject;
            document.body.appendChild(script);
        });
    }

    function loadGoogleMapsAPI(apiKey, libraries) {
        var librariesParam = libraries ? '&libraries=' + libraries.join(',') : '';
        var url = 'https://maps.googleapis.com/maps/api/js?key=' + apiKey + librariesParam;

        return new Promise((resolve, reject) => {
            if (typeof google === 'undefined' || typeof google.maps === 'undefined') {
                loadScript(url)
                .then(resolve)
                .catch(() => {
                    reject(new Error('Failed to load Google Maps JavaScript API'));
                });
            } else {
                resolve();
            }
        });
    }

    function createHeatmap(coordinates) {
        let heatmapData=[]
        let filteredData
        let marker
        let markers = [];

        function createMap() {
                    var css = `
                        #downloadButton {
    position:absolute;
    border-radius:1px;
    width:40px;
    height:40px;
    padding: 10px;
    border: none;
    background-color: #f0f0f0;
    background-image: url('https://www.svgrepo.com/show/177647/clipboard-list.svg');
    background-repeat: no-repeat;
    background-position: 5px;
    background-size: 30px auto;
    position: relative;
    transition: background-color 0.3s ease;
    cursor: pointer;
    opacity:0.8;
}

#downloadButton:hover {
    background-color: #f0f0f0;
    opacity:1
}
#downloadButton::after {
    position: absolute;
    bottom: calc(100% + 5px);
    left: 50%;
    transform: translateX(-50%);
    background-color: rgba(0, 0, 0, 0.8);
    color: #fff;
    padding: 5px;
    border-radius: 5px;
    font-size: 11px;
    line-height: 1;
    height: auto;
    white-space: nowrap;
    opacity: 0;
    transition: opacity 0.3s ease;
}

#downloadButton:hover::after {
    content: "Paste JSON data to your clipboard";
    opacity: 0.8;
}

        #map-container {
            position: fixed;
            bottom: 0px;
            left: 40%;
            transform: translateX(-50%);
            width: 800px;
            height: 600px;
            z-index: 1;
        }

.control-panel {
    width: 120px;
    height: 30px;
    background-color: #fff;
    cursor: pointer;
    text-align: center;
    line-height: 30px;
}

.control-panel .select-container {
    display: none;
}

.control-panel:hover .select-container {
    display: block;
}

    .control-panel select {
        width: 100%;
        padding: 5px;
        border: 1px solid #ccc;
        border-radius: 5px;
        background-color: #fff;
        cursor: pointer;
        outline: none;
    }

    `;

            var style = document.createElement('style');
            style.textContent = css;
            document.head.appendChild(style);

            const mapContainer = document.createElement('div');
            mapContainer.id = 'map-container';
            document.body.appendChild(mapContainer);

            var downloadButton = document.createElement('button');
            downloadButton.id = 'downloadButton'



            map = new google.maps.Map(mapContainer, {
                zoom: 2,
                center: { lat: 35.77, lng: 139.76 },
                mapTypeId: 'roadmap',
                gestureHandling: 'greedy',
                disableDefaultUI: true,
                streetViewControl: true
            });
            const controlDiv = document.createElement('div');
            controlDiv.className = 'control-panel';
            map.controls[google.maps.ControlPosition.TOP_LEFT].push(controlDiv);
            map.controls[google.maps.ControlPosition.BOTTOM_LEFT].push(downloadButton);

            controlDiv.classList.add('hoverable');
            controlDiv.addEventListener('mouseenter', () => {
                selectContainer.style.display = 'block';
                controlDiv.style.height = '380px';
            });


            controlDiv.addEventListener('mouseleave', () => {
                selectContainer.style.display = 'none';
                controlDiv.style.height = '30px';
            });
            const layerCheckbox = document.createElement('input');
            layerCheckbox.type = 'checkbox';
            layerCheckbox.id = 'scatter'
            const label = document.createElement('label');
            label.htmlFor = 'scatter';
            label.textContent = 'Draw Scatter';
            label.style.color='#000'
            label.style.fontSize='16px'
            controlDiv.appendChild(layerCheckbox)
            controlDiv.appendChild(label);


            const keysToFilter = ['playerNick', 'gameMode', 'forbidOptions', 'usingMap', 'country', 'score', 'distance', 'guessSeconds', 'gameDate'];
            const optionValues = {};
            let filters = [];
            let players=[];

            const selectContainer = document.createElement('div');
            selectContainer.className = 'select-container';
            controlDiv.appendChild(selectContainer);

            keysToFilter.forEach(key => {

                const select = document.createElement('select');
                select.setAttribute('name', key);
                select.style.marginBottom = '15px';

                const defaultOption = document.createElement('option');
                defaultOption.setAttribute('value', '');
                defaultOption.textContent = `${key}`;
                select.appendChild(defaultOption);

                if (key === 'score' || key === 'distance' || key === 'guessSeconds') {
                    const ranges = key === 'score' ? [
                        { min: 0, max: 1499 },
                        { min: 1500, max: 2999 },
                        { min: 3000, max: 3999 },
                        { min: 4000, max: 4499 },
                        { min: 4500, max: 4999 },
                        { min: 5000 }
                    ] : key === 'distance' ? [
                        { max: 0.5 },
                        { min: 0.5, max: 5 },
                        { min: 5, max: 100 },
                        { min: 100, max: 1000 },
                        { min: 1000, max: 5000 },
                        { min: 5000 }
                    ] : [
                        { max: 5 },
                        { min: 5, max: 15 },
                        { min: 15, max: 30 },
                        { min: 30, max: 60 },
                        { min: 60, max: 300 },
                        { min: 300 }
                    ];

                    for (let range of ranges) {
                        const option = document.createElement('option');
                        let label = range.max === undefined ? (key === 'score' ? '5000' : key === 'distance' ? '>5000km' : '>300s') : (key === 'score' ? `${range.min}-${range.max}` : key === 'distance' ? `${range.min}-${range.max}km` : `${range.min}-${range.max}s`);
                        if (range.min==undefined&&key==='distance'){
                            label='<0.5km';
                        }
                        if (range.min==undefined&&key==='guessSeconds'){
                            label='<5s';
                        }
                        option.setAttribute('value', label);
                        option.textContent = label;
                        select.appendChild(option);
                    }
                }
                else if (key === 'gameDate') {
                    const dateRanges = [
                        { label: 'More than 1 month', min:30, max: 1000 },
                        { label: 'More than 15 days', min: 15, max: 30 },
                        { label: 'More than 1 week', min: 7, max: 15 },
                        { label: 'More than 1 day', min: 1, max: 7 },
                        { label: 'Within 24 hours', min: 0, max: 1 }
                    ];

                    dateRanges.forEach(dateRange => {
                        const option = document.createElement('option');
                        option.setAttribute('value', `${dateRange.min}-${dateRange.max}` );
                        option.textContent = dateRange.label;
                        select.appendChild(option);

                    });
                }
                else {
                    const optionCounts = {};
                    coordinates.forEach(item => {
                        const value = item[key];
                        optionCounts[value] = (optionCounts[value] || 0) + 1;
                    });
                    const sortedOptions = Object.keys(optionCounts).sort((a, b) => optionCounts[b] - optionCounts[a]);
                    sortedOptions.forEach(value => {
                        if (!optionValues[value]) {
                            optionValues[value] = true;
                            const option = document.createElement('option');
                            option.setAttribute('value', value);
                            if (key === 'playerNick') {
                                const myKey = Object.keys(myNick);
                                option.textContent = value;
                                if (value == myNick[myKey]) {
                                    option.textContent = `${value}(me)`;
                                    option.style.color = 'red';
                                }
                            } else {
                                option.textContent = value;
                            }
                            select.appendChild(option);
                        }
                    });
                }

                selectContainer.appendChild(select);


                select.addEventListener('change', () => {
                    const selectedValue = select.value;
                    filters[key] = selectedValue;

                    let filteredData = coordinates

                    Object.keys(filters).forEach(filterKey => {
                        const filterValue = filters[filterKey];
                        if (filterValue) {
                            if (filterValue.includes('-')) {

                                filteredData = filteredData.filter(item => {
                                    if (filterKey=='gameDate'){

                                        const [minDays, maxDays] = filterValue.split('-')
                                        const itemValue = parseFloat(item[filterKey])
                                        return itemValue >=minDays && itemValue <maxDays;
                                    }

                                    else{
                                        const [minValue, maxValue] = filterValue.split('-').map(val => parseFloat(val));
                                        const itemValue = parseFloat(item[filterKey]);
                                        return itemValue >= minValue && itemValue <= maxValue;}
                                });
                            } else if (filterValue.includes('>') || filterValue.includes('<')) {
                                const operator = filterValue.includes('>') ? '>' : '<';
                                const value = parseFloat(filterValue.substring(1));
                                filteredData = filteredData.filter(item => {
                                    const itemValue = parseFloat(item[filterKey]);
                                    return operator === '>' ? itemValue > value : itemValue < value;
                                });
                            } else if (filterValue.includes('5000')) {
                                filteredData = filteredData.filter(item => {
                                    const itemValue = parseFloat(item[filterKey]);
                                    return itemValue === 5000;
                                });}
                            else {
                                filteredData = filteredData.filter(item => item[filterKey] === filterValue);
                            }
                        }
                    });
                    refreshHeatmap(filteredData, 'score');
                    downloadButton.addEventListener('click', function() {
                        if (filteredData){downloadJSON(filteredData,'mm')}
                        else{downloadJSON(coordinates,'mm')}
                        setTimeout(function() {
                            downloadButton.style.backgroundImage = "url('https://www.svgrepo.com/show/177648/clipboard-list.svg')";
                        }, 300);

                        setTimeout(function() {
                            downloadButton.style.backgroundImage = "url('https://www.svgrepo.com/show/177640/clipboard-list.svg')";
                        }, 700);

                        setTimeout(function() {
                            downloadButton.style.backgroundImage = "url('https://www.svgrepo.com/show/177647/clipboard-list.svg')";
                        }, 1000);
                    });

                });
            });
            let checkedMarker=null
            function handleMarkerClick(marker, coord) {
                return function() {
                    createStreetViewContainer(coord.location.lat(), coord.location.lng(), coord.heading, coord.pitch, coord.zoom);

                    if (checkedMarker && checkedMarker !== marker) {
                        checkedMarker.setIcon({
                            url: svgUrl,
                            fillOpacity: 0.8,
                            scaledSize: new google.maps.Size(25, 25)
                        });
                    }


                    marker.setIcon({
                        url: "https://www.svgrepo.com/show/313155/pin.svg",
                        scaledSize: new google.maps.Size(25, 25)
                    });


                    checkedMarker = marker;


                    var closeListen = document.querySelector('#close');
                    closeListen.addEventListener('click', function() {
                        marker.setIcon({
                            url: svgUrl,
                            fillOpacity: 0.8,
                            scaledSize: new google.maps.Size(25, 25)
                        });
                    });
                };
            }

            layerCheckbox.addEventListener('change', (event) => {
                if (heatmapData.length==0){
                    for (const coord of coordinates) {
                        heatmapData.push({
                            location: new google.maps.LatLng(coord.latLng.lat, coord.latLng.lng),
                            weight: coord.score,
                            player:coord.playerNick,
                            forbidOptions:coord.forbidOptions,
                            heading:coord.heading,
                            pitch:coord.pitch,
                            zoom:coord.zoom
                        });
                    }
                }
                if (event.target.checked) {
                    let existingMarkers = []
                    for (const coord of heatmapData) {
                        let markerExists = existingMarkers.some(existingPosition => {
                            return existingPosition.equals(coord.location);
                        });
                        if (!markerExists) {
                            const marker = new google.maps.Marker({
                                position: new google.maps.LatLng(coord.location),
                                map: map,
                                title: coord.player+' : ' + coord.weight+'('+coord.forbidOptions+')',
                                icon: {url:svgUrl,
                                       fillOpacity: 0.8,
                                       scaledSize:new google.maps.Size(24,24)
                                      }
                            });
                            existingMarkers.push(coord.location)
                            marker.addListener('click', handleMarkerClick(marker, coord));
                            markers.push(marker);

                    }
                    }


                } else {
                    markers.forEach(m => m.setMap(null));
                    markers = [];

                }
            })
            downloadButton.addEventListener('click', function() {
                downloadJSON(coordinates,'mm')
                setTimeout(function() {
                    downloadButton.style.backgroundImage = "url('https://www.svgrepo.com/show/177648/clipboard-list.svg')";
                }, 300);

                setTimeout(function() {
                    downloadButton.style.backgroundImage = "url('https://www.svgrepo.com/show/177640/clipboard-list.svg')";
                }, 700);

                setTimeout(function() {
                    downloadButton.style.backgroundImage = "url('https://www.svgrepo.com/show/177647/clipboard-list.svg')";
                }, 1000);
            });
        }

        function refreshHeatmap(fd,k) {
            if (fd.length === 0) {
                console.error('No valid coordinates data found');
            }
            heatmapData=[]
            for (const coord of fd) {
                heatmapData.push({
                    location: new google.maps.LatLng(coord.latLng.lat, coord.latLng.lng),
                    weight: coord[k]
                });
            }
            heatmapLayer.setData(heatmapData);
            if (markers.length!=0){
                markers.forEach(marker => marker.setMap(null));
                markers = [];
            }
        }

        if (!map) {
            createMap();
            getDefault(coordinates)
        }

    }

    function getDefault(coordinates){
        var h=[];
        if (heatmapLayer) {
            heatmapLayer.setMap(null);
        }
        for (const coord of coordinates) {
            h.push({
                location: new google.maps.LatLng(coord.latLng.lat, coord.latLng.lng),
                weight: coord.score,
                heading:coord.heading,
                pitch:coord.pitch,
                zoom:coord.zoom
            });
        }

        heatmapLayer = new google.maps.visualization.HeatmapLayer({
            data: h,
            dissipating: true,
            map: map,
        });}

    function getStatics(url, result, maxPages,currentPage = 1, pageToken = null) {
        if (currentPage==1){
            const swal = Swal.fire({
                title: 'Fetching Activities',
                text: 'Please wait...',
                allowOutsideClick: false,
                allowEscapeKey: false,
                showConfirmButton: false,
                didOpen: () => {
                    Swal.showLoading();
                }
            });
        }
        if (currentPage > maxPages) {
            console.log(`Reached maximum number of pages (${maxPages}). Stopping requests.`);
            swal.close()
            Swal.fire('Success', 'All activities retrieved successfully!', 'success');
            saveLocalStorage(result)
            getCoords(result);
            return;
        }

        let nextPageUrl = url;
        if (pageToken) {
            nextPageUrl += `&paginationToken=${encodeURIComponent(pageToken)}`;
        }

        GM_xmlhttpRequest({
            method: "GET",
            url: nextPageUrl,
            onload: function(response) {
                if (response.status === 200) {
                    const data = JSON.parse(response.responseText);
                    processActivities(data, result);
                    const nextPageToken = data.paginationToken;
                    if (nextPageToken) {
                        getStatics(url, result, maxPages, currentPage + 1, nextPageToken);
                    } else {
                        Swal.fire('Success', 'All activities retrieved successfully!', 'success');
                        saveLocalStorage(result)
                        getCoords(result);
                    }
                } else {
                    console.error('Request failed: ' + response.statusText);
                    Swal.fire('Error', 'Failed to fetch activities. Please try again later.', 'error');
                }
            },
            onerror: function(response) {
                console.error('Request failed: ' + response.statusText);
                Swal.fire('Error', 'Failed to fetch activities. Please try again later.', 'error');
            }
        });
    }

    function processActivities(data, result) {
        const entries = data.entries;
        if (entries && entries.length > 0) {
            entries.forEach(entry => {
                if (entry.payload) {
                    const payloadList = JSON.parse(entry.payload);
                    const userId = entry.user.id;
                    if (!Array.isArray(payloadList)) {
                        processPayload(payloadList, userId, result);
                    } else {
                        payloadList.forEach(payload => {
                            processPayload(payload, userId, result);
                        });
                    }
                }
            });
        } else {
            console.error('Data not found!');
        }
    }

    function processPayload(payload, userId, result) {
        if (payload.gameToken) {
            result.games[userId] = result.games[userId] || [];
            if (!result.games[userId].includes(payload.gameToken)) {
                result.games[userId].push(payload.gameToken);
            }
        } else if (payload.gameId) {
            result.duels[userId] = result.duels[userId] || [];
            if (!result.duels[userId].includes(payload.gameId)) {
                result.duels[userId].push(payload.gameId);
            }
        }
    }

    async function getCoords(data) {
        try {
            var coordinates = [];
            const duelsPromises = [];
            const gamesPromises = [];
            const chunkSize = 20;
            const swal = Swal.fire({
                title: 'Fetching Coordinates',
                text: 'Please wait...',
                allowOutsideClick: false,
                allowEscapeKey: false,
                showConfirmButton: false,
                didOpen: () => {
                    Swal.showLoading();
                }
            });
            for (const gameIds of Object.values(data.duels)) {
                for (let i = 0; i < gameIds.length; i += chunkSize) {
                    const chunk = gameIds.slice(i, i + chunkSize);
                    const chunkPromises = chunk.map(gameId => {
                        const requestUrl = `https://game-server.geoguessr.com/api/duels/${gameId}`;
                        return getGameSummary(requestUrl, 'duels', coordinates);
                    });
                    duelsPromises.push(Promise.allSettled(chunkPromises));
                }
            }

            for (const gameIds of Object.values(data.games)) {
                for (let i = 0; i < gameIds.length; i += chunkSize) {
                    const chunk = gameIds.slice(i, i + chunkSize);
                    const chunkPromises = chunk.map(gameId => {
                        const requestUrl = `https://www.geoguessr.com/api/v3/games/${gameId}?client=web`;
                        return getGameSummary(requestUrl, 'games', coordinates);
                    });
                    gamesPromises.push(Promise.allSettled(chunkPromises));
                }
            }

            await Promise.all([...duelsPromises, ...gamesPromises]);
            swal.close();
            try {
                const friends = await getFriends();
                var matchedData = matchPlayerNick(coordinates, friends);
                await downloadJSON(matchedData);
                matchedData=getDiffdays(matchedData)
                createHeatmap(matchedData);
                Swal.fire('Success', 'Heatmap is prepared!', 'success');
            } catch (error) {
                console.error("Error:", error);
                Swal.fire('Error', 'Failed to prepare heatmap', 'error');
            }
        } catch (error) {
            Swal.fire('Error', 'Error parsing JSON from localStorage', 'error');
            console.error('Error parsing JSON from localStorage:', error);
        }
    }

    async function getGameSummary(url, mode, coordinates) {
        return new Promise((resolve, reject) => {
            GM_xmlhttpRequest({
                method: "GET",
                url: url,
                onload: function(response) {
                    try {let forbidOptions
                        const data = JSON.parse(response.responseText);
                        if(data.teams||data.player){
                            if (mode === 'duels') {
                                const movementOptions=data.movementOptions
                                if(movementOptions.forbidMoving&&movementOptions.forbidZooming&&movementOptions.forbidRotating){forbidOptions='NMPZ'}
                                else if (!movementOptions.forbidMoving&&!movementOptions.forbidZooming&&!movementOptions.forbidRotating){forbidOptions='Moving'}
                                else if(movementOptions.forbidMoving&&!movementOptions.forbidZooming&&!movementOptions.forbidRotating){forbidOptions='NoMoving'}
                                else{forbidOptions='Entertainment'}
                            const usingMap=data.options.map.name
                            data.teams.forEach(team => {
                                team.players.forEach(player => {
                                    player.guesses.forEach(guess => {
                                        const roundNumber = guess.roundNumber;
                                        const roundData = data.rounds.find(round => round.roundNumber === roundNumber);
                                        if (roundData) {
                                            const gameDate = guess.created.substring(0, 10);
                                            const gameMode = 'duels';
                                            const latLng = { 'lat': roundData.panorama.lat, 'lng': roundData.panorama.lng };
                                            const country = roundData.panorama.countryCode;
                                            const heading=roundData.panorama.heading
                                            const pitch=roundData.panorama.pitch
                                            const zoom=roundData.panorama.zoom
                                            const playerId = player.playerId;
                                            const score = team.roundResults.find(result => result.roundNumber === roundNumber).score;
                                            const distance = (guess.distance / 1000).toFixed(2);
                                            const guessSeconds = (Math.abs(new Date(guess.created) - new Date(roundData.startTime)) / 1000).toFixed(2);
                                            coordinates.push({ gameMode, playerId, latLng, country, score, distance, guessSeconds, forbidOptions, usingMap, gameDate,heading,pitch,zoom});
                                        }
                                    });
                                });
                            });
                        }
                        else {
                            if(data.forbidMoving&&data.forbidZooming&&data.forbidRotating){forbidOptions='NMPZ'}
                            else if (!data.forbidMoving&&!data.forbidZooming&&!data.forbidRotating){forbidOptions='Moving'}
                            else if(data.forbidMoving&&!data.forbidZooming&&!data.forbidRotating){forbidOptions='NoMoving'}
                            else{forbidOptions='Entertainment'}
                            const gameMode = 'classic';
                            const player = data.player;
                            const playerId = player.id;
                            const usingMap=data.mapName
                            player.guesses.forEach((guess, index) => {
                                const roundData = data.rounds[index];
                                const gameDate = roundData.startTime.substring(0, 10);
                                const heading=roundData.heading
                                const pitch=roundData.pitch
                                const zoom=roundData.zoom
                                const latLng = { 'lat': roundData.lat, 'lng': roundData.lng };
                                const country = roundData.streakLocationCode;
                                const score = guess.roundScoreInPoints;
                                const distance = parseFloat(guess.distance.meters.amount);
                                const guessSeconds = guess.time;
                                coordinates.push({ gameMode, playerId, latLng, country, score, distance, guessSeconds, forbidOptions, usingMap, gameDate,heading,pitch,zoom});
                            });
                        }}
                        resolve();
                             }catch (error) {
                        console.error(`Error parsing JSON from URL: ${url}`, error);
                    }
                },
                onerror: function(error) {
                    reject(error);
                }
            });
        });
    }

    async function getPlayerName(id) {
        return new Promise((resolve, reject) => {
            const url = `https://www.geoguessr.com/user/${id}`;
            GM_xmlhttpRequest({
                method: 'GET',
                url: url,
                onload: function(response) {
                    if (response.status === 200) {
                        const playerName = extractPlayerName(response.responseText);
                        resolve(playerName);
                    } else {
                        reject('Error:', response.status);
                    }
                },
                onerror: function(error) {
                    reject('Error:', error);
                }
            });
        });
    }

    function extractPlayerName(responseText) {
        const regex = /"user"\s*:\s*{\s*"nick"\s*:\s*"(.+?)"/;
        const match = responseText.match(regex);
        if (match && match.length > 1) {
            return match[1];
        }
        return null;
    }

    function createStreetViewContainer(latitude, longitude,h,p,z) {
        var css = `
.custom-container div[style*='position: absolute; left: 0px; bottom: 0px;'] {
    display: none !important;
}

.transfer-button::after,
.link-button::after {
    position: absolute;
    bottom: 42px;
    left: 100%;
    transform: translateX(-50%);
    background-color: rgba(0, 0, 0, 0.8);
    color: #fff;
    padding: 5px;
    border-radius: 5px;
    font-size: 11px;
    line-height: 1;
    height: auto;
    white-space: nowrap;
    opacity: 0;
    transition: opacity 0.3s ease;
}

.transfer-button:hover::after,
.link-button:hover::after {
    content: attr(data-text);
    opacity: 0.9;
}
 `;
        GM_addStyle(css);
        if (streetViewContainer) {
            streetViewContainer.remove();
        }
        if (streetViewMap){streetViewMap.setStreetView(null)
                          }
        streetViewContainer = document.createElement('div');
        streetViewContainer.id = 'street-view-container';
        streetViewContainer.classList.add('custom-container');
        streetViewContainer.style.position = 'fixed';
        streetViewContainer.style.bottom = '0px';
        streetViewContainer.style.left = '0px';
        streetViewContainer.style.width = '800px';
        streetViewContainer.style.height = '600px';
        streetViewContainer.style.overflow = 'hidden';
        streetViewContainer.style.zIndex = '1';

        streetViewMap = new google.maps.Map(streetViewContainer, {
            center: { lat: latitude, lng: longitude },
            zoom: 14,
            streetViewControl: true
        });

        var closeButton = document.createElement('div');
        closeButton.className = 'custom-close-button';
        closeButton.id='close'
        closeButton.innerHTML = '×';
        closeButton.style.position = 'absolute';
        closeButton.style.top = '1px';
        closeButton.style.right = '80px';
        closeButton.style.margin = '10px';
        closeButton.style.padding = '0px';
        closeButton.style.background = 'none';
        closeButton.style.border = '0px';
        closeButton.style.textTransform = 'none';
        closeButton.style.appearance = 'none';
        closeButton.style.cursor = 'pointer';
        closeButton.style.userSelect = 'none';
        closeButton.style.borderRadius = '2px';
        closeButton.style.height = '40px';
        closeButton.style.width = '40px';
        closeButton.style.boxShadow = 'rgba(0, 0, 0, 0.3) 0px 1px 4px -1px';
        closeButton.style.overflow = 'hidden';
        closeButton.style.color = '#d3cfcf';
        closeButton.style.fontSize = '30px';
        closeButton.style.lineHeight = '40px';
        closeButton.style.textAlign = 'center';
        closeButton.style.backgroundColor = 'rgb(34, 34, 34)';
        closeButton.onclick = function() {
            streetViewContainer.remove();
        };
         closeButton.style.zIndex = '2';
        closeButton.onclick = function() {
            streetViewContainer.remove();
        };
        streetViewContainer.appendChild(closeButton);

        var transferButton = document.createElement("button");
        const shareUrl = `data:image/svg+xml;base64,${btoa(shareSvg)}`;
        transferButton.style.backgroundImage = `url('${shareUrl}')`;
        transferButton.style.backgroundPosition = 'center'
        transferButton.classList.add('transfer-button');
        transferButton.setAttribute('data-text', 'Open in map');
        transferButton.style.zIndex = '2';
        transferButton.style.position = 'absolute';
        transferButton.style.bottom = '2px';
        transferButton.style.left = '2px';
        transferButton.style.margin = '10px';
        transferButton.style.padding = '0px';
        transferButton.style.border = 'none';
        transferButton.style.cursor = 'pointer';
        transferButton.style.borderRadius = '2px';
        transferButton.style.height = '40px';
        transferButton.style.width = '40px';
        transferButton.style.boxShadow = 'rgba(0, 0, 0, 0.3) 0px 1px 4px -1px';
        transferButton.style.overflow = 'visible';
        transferButton.style.fontSize = '24px';
        transferButton.style.lineHeight = '40px';
        transferButton.style.textAlign = 'center';
        transferButton.style.backgroundColor = 'rgb(34, 34, 34)';
        streetViewContainer.appendChild(transferButton);

        var linkButton = document.createElement("button");
        const linkUrl = `data:image/svg+xml;base64,${btoa(linkSvg)}`;
        linkButton.style.backgroundImage = `url('${linkUrl}')`;
        linkButton.style.backgroundPosition = 'center'
        linkButton.classList.add('link-button');
        linkButton.setAttribute('data-text', 'Copy as link');
        linkButton.style.zIndex = '2';
        linkButton.style.position = 'absolute';
        linkButton.style.bottom = '2px';
        linkButton.style.left = '40px';
        linkButton.style.margin = '10px';
        linkButton.style.padding = '0px';
        linkButton.style.border = 'none';
        linkButton.style.cursor = 'pointer';
        linkButton.style.borderRadius = '2px';
        linkButton.style.height = '40px';
        linkButton.style.width = '40px';
        linkButton.style.boxShadow = 'rgba(0, 0, 0, 0.3) 0px 1px 4px -1px';
        linkButton.style.overflow = 'visible';
        linkButton.style.fontSize = '24px';
        linkButton.style.lineHeight = '40px';
        linkButton.style.textAlign = 'center';
        linkButton.style.backgroundColor = 'rgb(34, 34, 34)';
        streetViewContainer.appendChild(linkButton);

        var streetViewService = new google.maps.StreetViewService();
        streetViewService.getPanorama({ location: { lat: latitude, lng: longitude }, radius: 50 }, function(data, status) {
            if (status === 'OK') {
                var panoId = data.location.pano;
                var streetView = new google.maps.StreetViewPanorama(streetViewContainer, {
                    position: data.location.latLng,
                    pov: { heading: h, pitch: p },
                    zoom:z,
                });
                streetViewMap.setStreetView(streetView);

                var focus = { id: panoId, heading: h, pitch: p,zoom:z};
                streetView.addListener("pov_changed", function() {
                    focus.heading = streetView.getPov().heading;
                    focus.pitch = streetView.getPov().pitch;
                    focus.zoom = streetView.getZoom();
                });

                streetView.addListener("pano_changed", function() {
                    focus.id = streetView.getPano();
                });

                transferButton.addEventListener("click", function() {
                    window.open(`https://www.google.com/maps/@?api=1&map_action=pano&heading=${focus.heading}&pitch=${focus.pitch}&fov=${Math.floor(90/focus.zoom)}&pano=${focus.id}`, "_blank");
                });

                linkButton.addEventListener("click", function() {
                    setTimeout(function() {
                        linkButton.style.backgroundImage = `url(data:image/svg+xml;base64,${btoa(loadingSvg)})`;
                    }, 500);

                    setTimeout(function() {
                        linkButton.style.backgroundImage = `url(data:image/svg+xml;base64,${btoa(successSvg)})`;
                    }, 1000);

                    setTimeout(function() {
                        linkButton.style.backgroundImage = `url(data:image/svg+xml;base64,${btoa(linkSvg)})`;
                    }, 1300);

                    GM_setClipboard(`https://www.google.com/maps/@?api=1&map_action=pano&heading=${focus.heading}&pitch=${focus.pitch}&fov=${Math.floor(90/focus.zoom)}&pano=${focus.id}`);
                });
            } else {
                console.error('Street View data not found for this location');
            }
        });

        document.body.appendChild(streetViewContainer);

    }

    async function swalOption(){
        const { value: inputOption,dismiss: inputDismiss } =await Swal.fire({
            title: 'Get Activities',
            text: 'Do you want to fetch activities from your Geoguessr account? If you click "Cancel", you will need to upload a JSON file',
            icon: 'question',
            showCancelButton: true,
            showCloseButton:true,
            allowOutsideClick: false,
            input: 'number',
            inputLabel: 'Set A Limit Of Activities Pages',
            inputPlaceholder: '10',
            inputAttributes: {
                min:1,
                max:80,
                step:10,
            },
            confirmButtonColor: '#3085d6',
            cancelButtonColor: '#d33',
            confirmButtonText: 'Yes',
            cancelButtonText: 'Cancel',
            inputValidator: (value) => {
                if (!value||parseInt(value)<1) {
                    return 'Please set a valid limit number';
                }
                if (parseInt(value) > 100) {
                    return 'It is recommended that the maximum number of pages should not exceed 100!';
                }
            }
        });

        if (inputOption) {
            if (!isUpdated){
                const pageValue=parseInt(inputOption)
                getStatics(u,activities,pageValue)}
            else{
                getCoords(activities)}
        }
        else if(inputDismiss==='cancel'){
            const input = document.createElement('input');
            input.type = 'file';
            input.style.display = 'none'
            document.body.appendChild(input);

            const coordsPromise = 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) {
                            Swal.fire('Error Parsing JSON Data!', 'The input JSON data is invalid or incorrectly formatted.','error');
                        }
                    };

                    reader.readAsText(file);
                });


                input.click();
            });
            coordsPromise.then(async (data) => {
                try {
                    const friends = await getFriends();
                    var matchedData = matchPlayerNick(data, friends);
                    matchedData=getDiffdays(matchedData)
                    createHeatmap(matchedData);
                    Swal.fire('Success', 'Heatmap is prepared!', 'success');
                } catch (error) {
                    console.error("Error:", error);
                    Swal.fire('Error', 'Failed to prepare heatmap', 'error');
                }
});
        }
    }

    function createButton() {

        var mapButton = document.createElement('button');
        mapButton.textContent = 'Create Heatmap';
        mapButton.addEventListener('click',swalOption);
        mapButton.style.position = 'fixed';
        mapButton.style.top = '10px';
        mapButton.style.right = '340px';
        mapButton.style.zIndex = '9999';
        mapButton.style.borderRadius = "18px";
        mapButton.style.padding = "10px 20px";
        mapButton.style.border = "none";
        mapButton.style.backgroundColor = "#4CAF50";
        mapButton.style.color = "white";
        mapButton.style.cursor = "pointer";
        document.body.appendChild(mapButton);

        var refreshButton = document.createElement('button');
        refreshButton.textContent = 'Update Activities';
        refreshButton.addEventListener('click', function(){getStatics(u,activities,10)});
        refreshButton.style.position = 'fixed';
        refreshButton.style.top = '10px';
        refreshButton.style.right = '180px';
        refreshButton.style.zIndex = '9999';
        refreshButton.style.borderRadius = "18px";
        refreshButton.style.padding = "10px 20px";
        refreshButton.style.border = "none";
        refreshButton.style.backgroundColor = "#4CAF50";
        refreshButton.style.color = "white";
        refreshButton.style.cursor = "pointer";
        document.body.appendChild(refreshButton);

        var clearButton = document.createElement('button');
        clearButton.textContent = 'Clear Activities';
        clearButton.addEventListener('click', clearLocalStorage);
        clearButton.style.position = 'fixed';
        clearButton.style.top = '10px';
        clearButton.style.right = '35px';
        clearButton.style.zIndex = '9999';
        clearButton.style.borderRadius = "18px";
        clearButton.style.padding = "10px 20px";
        clearButton.style.border = "none";
        clearButton.style.backgroundColor = "#4CAF50";
        clearButton.style.color = "white";
        clearButton.style.cursor = "pointer";
        document.body.appendChild(clearButton);
    }

    loadGoogleMapsAPI('AIzaSyAiRLvmrxcqZRhsiPMzK5Ps2b5Ov6XhJrY', ['visualization','streetView'])
    .then(createButton)
    .catch(error => {
        console.error(error);
    });
})();