您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Greasy Fork is available in English.
Displays the country name, coordinates, and panoId for a given Google Street View panorama
当前为
// ==UserScript== // @name Google Street View Panorama Info // @namespace http://greasyfork.icu/users/1340965 // @version 1.13 // @description Displays the country name, coordinates, and panoId for a given Google Street View panorama // @author ZecaGeo // @run-at document-end // @match https://www.google.com/maps/* // @match https://www.google.at/maps/* // @grant GM_log // @grant GM_setClipboard // @grant GM_xmlhttpRequest // @icon https://www.google.com/s2/favicons?sz=64&domain=geohints.com // @connect nominatim.openstreetmap.org // @license MIT // ==/UserScript== /* jshint esversion: 11 */ (function () { 'use strict'; const DEBUG_MODE = false; function init() { log( `Starting userscript '${GM_info.script.name}' v${GM_info.script.version}` ); waitForElement('.pB8Nmf > div:last-child', updateTitleCard, '#titlecard'); } async function updateTitleCard(referenceElement) { let h2Element = document.createElement('h2'); h2Element.setAttribute('class', 'lsdM5 fontBodySmall'); h2Element.setAttribute('jsan', '7.lsdM5,7.fontBodySmall'); let divElement = document.createElement('div'); divElement.appendChild(h2Element); let [latitude, longitude, panoId] = [ ...parsePanoramaInfoFromUrl(window.location.href), ]; let countryNode = referenceElement.parentNode.insertBefore( cloneNode(divElement, 'Retrieving country...'), referenceElement ); let url = `https://nominatim.openstreetmap.org/reverse?lat=${latitude}&lon=${longitude}&format=json&accept-language=en-US`; countryNode.querySelector('h2').innerText = await getCountry(url); referenceElement.parentNode.insertBefore( cloneNode(divElement, latitude, true), referenceElement ); referenceElement.parentNode.insertBefore( cloneNode(divElement, longitude, true), referenceElement ); referenceElement.parentNode.insertBefore( cloneNode(divElement, panoId, true), referenceElement ); } function parseCoordinates(url) { const regex_coordinates = new RegExp(/@(-?\d+\.\d+),(-?\d+\.\d+)/); return regex_coordinates.exec(url).slice(1); } function parsePanoId(url) { const regex_panoId = new RegExp(/!1s(.+)!2e/); return regex_panoId.exec(url).slice(1); } function parsePanoramaInfoFromUrl(url) { parseCoordinates(url).forEach((x) => debug(x)); debug(...parsePanoId(url)); return [...parseCoordinates(url), ...parsePanoId(url)]; } function cloneNode(originalNode, value, isClickable = false) { let node = originalNode.cloneNode(true); node.querySelector('h2').innerText = value; if (isClickable) { node.style.cursor = 'pointer'; node.onclick = () => GM_setClipboard(value); } return node; } async function getCountry(url) { const response = await promiseRequest('GET', url); const data = JSON.parse(response.responseText); return data?.address?.country ?? 'Country not found'; } function promiseRequest(method, url) { log( [ '---PROMISEREQUEST---', '\tmethod: ' + method, '\turl: ' + url, '---PROMISEREQUEST---', ].join('\n') ); return new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: method, url: url, onload: (result) => { responseInfo(result); if (result.status >= 200 && result.status < 300) { // ok resolve(result); } else { // error reject(result.responseText); } }, ontimeout: () => { let l = new URL(url); reject( ' timeout detected: "no answer from ' + l.host + ' for ' + l.timeout / 1000 + 's"' ); }, onerror: (result) => { // network error responseInfo(result); reject( ' error: ' + result.status + ', message: ' + result.statusText ); }, }); }); } // GM_xmlhttpRequest response info function responseInfo(r) { debug( [ '', 'finalUrl: \t\t' + (r.finalUrl || '-'), 'status: \t\t' + (r.status || '-'), 'statusText: \t' + (r.statusText || '-'), 'readyState: \t' + (r.readyState || '-'), 'responseHeaders: ' + (r.responseHeaders.replaceAll('\r\n', ';') || '-'), 'responseText: \t' + (r.responseText || '-'), ].join('\n') ); } const waitForElement = (selector, callback, targetNode) => { new MutationObserver((mutationsList, observer) => { for (const mutation of mutationsList) { if (mutation.type === 'childList') { const element = document.querySelector(selector); if (element) { observer.disconnect(); callback(element); return; } } } }).observe( targetNode ? document.querySelector(targetNode) : document.body, { childList: true, subtree: true, } ); }; // debug output functions function toLog(typ, msg) { if (DEBUG_MODE) { if (console && console[typ] && console.group && console.groupEnd) { console[typ](msg); } else { GM_log(typ + ': ' + msg.toString()); } } } function log(msg) { toLog('log', msg); } function debug(msg) { toLog('debug', msg); } function error(msg) { toLog('error', msg); } init(); })();