// ==UserScript==
// @name Geoguessr Activities Analysis Tool
// @version 1.1
// @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.google.com/s2/favicons?domain=geoguessr.com
// @grant GM_xmlhttpRequest
// @namespace http://tampermonkey.net/
// ==/UserScript==
(function() {
'use strict';
var u = 'https://www.geoguessr.com/api/v4/feed/friends?count=26';
var map,heatmapLayer,streetViewContainer,streetViewMap
let storedCoords
let myNick
let activities =JSON.parse(localStorage.getItem('activities'));
let isUpdated=JSON.parse(localStorage.getItem('isUpdated'));
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);
}
});
});
}
async function getFlagSvg(countryCode) {
const url = `https://flagicons.lipis.dev/flags/4x3/${countryCode.toLowerCase()}.svg`;
return new Promise((resolve, reject) => {
GM_xmlhttpRequest({
method: 'GET',
url,
responseType: 'text',
onload: (response) => {
if (response.status === 200) {
resolve(response.responseText);
} else {
reject(`Error fetching flag for ${countryCode}: ${response.statusText}`);
}
},
onerror: (error) => {
reject(`Error fetching flag for ${countryCode}: ${error}`);
},
});
});
}
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) {
var data = JSON.stringify(coords, null, 2);
var blob = new Blob([data], { type: 'application/json' });
var url = URL.createObjectURL(blob);
var a = document.createElement('a');
a.href = url;
a.download = 'coordinates.json';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
}
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 weightKey = 'score';
let heatmapData=[]
let filteredData
let marker
let markers = [];
function filterHeatmapData(minScore, maxScore) {
filteredData = heatmapData.filter(point => point.weight >= minScore && point.weight <= maxScore);
heatmapLayer.setData(filteredData);
}
function createMap() {
var css = `
#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);
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);
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');
});
});
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
});
}
}
if (event.target.checked) {
for (const coord of heatmapData) {
const marker = new google.maps.Marker({
position: new google.maps.LatLng(coord.location),
map: map,
title: 'Score: ' + coord.weight,
pin: {
fillOpacity: 1,
strokeWeight: 0,
color:'gray',
scale: (coord.weight/5000)
}
});
markers.push(marker);
marker.addListener('click', () => {
createStreetViewContainer(coord.location.lat(),coord.location.lng())
});
}
} else {
markers.forEach(marker => marker.setMap(null));
markers = [];
}
})
}
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
});
}
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 {
const 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.all(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.all(chunkPromises));
}
}
await Promise.all([...duelsPromises, ...gamesPromises]);
swal.close();
try {
const friends = await getFriends();
const matchedData = matchPlayerNick(coordinates, friends);
coordinates=matchedData
createHeatmap(matchedData);
Swal.fire('Success', 'Heatmap is prepared!', 'success');
} catch (error) {
console.error("Error:", error);
Swal.fire('Error', 'Failed to prepare heatmap', 'error');
}
await downloadJSON(coordinates);
} catch (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 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 });
}
});
});
});
}
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 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 });
});
}}
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,mapContainer) {
if (streetViewContainer) {
streetViewContainer.remove();
}
if (streetViewMap){streetViewMap.setStreetView(null)
}
streetViewContainer = document.createElement('div');
streetViewContainer.id = 'street-view-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 = '9999';
streetViewMap = new google.maps.Map(streetViewContainer, {
center: { lat: latitude, lng: longitude },
zoom: 14,
streetViewControl: true
});
var streetViewService = new google.maps.StreetViewService();
streetViewService.getPanorama({ location: { lat: latitude, lng: longitude }, radius: 50 }, function(data, status) {
if (status === 'OK') {
var streetView = new google.maps.StreetViewPanorama(streetViewContainer, {
position: data.location.latLng,
pov: { heading: 235, pitch: 10 }
});
streetViewMap.setStreetView(streetView);
} 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: 100,
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 = '360px';
mapButton.style.zIndex = '9999';
document.body.appendChild(mapButton);
var refreshButton = document.createElement('button');
refreshButton.textContent = 'Update Your Activities';
refreshButton.addEventListener('click', function(){getStatics(u,activities,10)});
refreshButton.style.position = 'fixed';
refreshButton.style.top = '10px';
refreshButton.style.right = '200px';
refreshButton.style.zIndex = '9999';
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 = '80px';
clearButton.style.zIndex = '9999';
document.body.appendChild(clearButton);
}
loadGoogleMapsAPI('AIzaSyAiRLvmrxcqZRhsiPMzK5Ps2b5Ov6XhJrY', ['visualization','streetView'])
.then(createButton)
.catch(error => {
console.error(error);
});
})();