// ==UserScript== // @name Guess Peek (Geoguessr) // @namespace alienperfect // @version 1.0.8 // @description See where your guess was after each round! // @author Alien Perfect // @match https://www.geoguessr.com/* // @icon https://www.google.com/s2/favicons?sz=32&domain=geoguessr.com // @run-at document-start // @grant GM_addStyle // @grant GM_info // @grant unsafeWindow // @downloadURL none // ==/UserScript== "use strict"; const searchRadius = 250000; const scriptName = GM_info.script.name; const GAMES_API = "https://www.geoguessr.com/api/v3/games/"; let streetViewService; function main() { console.log(`${scriptName} is running!`); GM_addStyle(` .peek-tooltip { display: none; position: absolute; width: 120px; background: #323232; border-radius: 4px; text-align: center; padding: 0.5rem; font-size: 0.9rem; right: 50%; bottom: 220%; margin-right: -60px; opacity: 90%; z-index: 4; } .peek-tooltip:after { content: ""; position: absolute; top: 100%; left: 50%; margin-left: -5px; border-width: 5px; border-style: solid; border-color: #323232 transparent transparent transparent; } [data-pano]:hover .peek-tooltip, [data-no-pano]:hover .peek-tooltip { display: block; } [data-pano] > :first-child { cursor: pointer; --border-color: #E91E63 !important; --border-size-factor: 2 !important; } [data-no-pano] > :first-child { cursor: initial; --border-color: #323232 !important; --border-size-factor: 1.5 !important; } `); const _fetch = unsafeWindow.fetch; unsafeWindow.fetch = async (resource, options) => { const response = await _fetch(resource, options); const url = typeof resource === "string" ? resource : resource.url; if (url.includes(GAMES_API) && options.method === "POST") { try { if (!streetViewService) initStreetViewService(); const resp = await response.clone().json(); const guess = resp.player.guesses.pop(); const guessCoords = { lat: guess.lat, lng: guess.lng }; let pano; let oldRadius; let radius = searchRadius; let proceed = true; while (proceed) { try { pano = await streetViewService.getPanorama({ location: guessCoords, radius: radius, source: "outdoor", preference: "nearest", }); radius = unsafeWindow.google.maps.geometry.spherical.computeDistanceBetween( guessCoords, pano.data.location.latLng, ); // Avoid infinite loop! if (oldRadius && radius >= oldRadius) break; oldRadius = radius; } catch (e) { proceed = false; } } if (pano) { const url = getPanoUrl(pano.data.location.pano); updateGuessMarker(url, radius); } else { updateGuessMarker(); } } catch (e) { console.error(`${scriptName} error: ${e}`); } } return response; }; } function updateGuessMarker(url, radius) { new MutationObserver(function () { const flag = document.querySelector("[data-qa='correct-location-marker']"); const marker = document.querySelector("[data-qa='guess-marker']"); if (flag && marker) { this.disconnect(); const [distance, units] = humanizeDistance(searchRadius); const tooltip = document.createElement("div"); tooltip.className = "peek-tooltip"; tooltip.textContent = `No location was found within ${distance} ${units}!`; if (url) { const [distance, units] = humanizeDistance(radius); tooltip.textContent = `Click to see the nearest location! [${distance} ${units}]`; marker.setAttribute("data-pano", "true"); marker.addEventListener("click", () => { window.open(url, "_blank"); }); } else { marker.setAttribute("data-no-pano", "true"); } marker.append(tooltip); } }).observe(document.body, { childList: true, subtree: true, }); } function initStreetViewService() { streetViewService = new unsafeWindow.google.maps.StreetViewService(); } function getPanoUrl(pano) { return `https://www.google.com/maps/@?api=1&map_action=pano&pano=${pano}`; } function humanizeDistance(distance) { if (distance >= 1000) return [(distance / 1000).toFixed(1), "km"]; return [distance.toFixed(1), "m"]; } main();