// ==UserScript== // @name 图寻复盘工具 // @namespace https://greasyfork.org/users/1179204 // @version 1.0.2 // @description 多功能转换图寻复盘链接成街景链接,显示地点位置信息和拍摄时间以及百度街景的panoId // @match https://tuxun.fun/replay-pano?gameId=*&round=* // @icon data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgNDggNDgiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgZmlsbD0iIzAwMDAwMCI+PGcgaWQ9IlNWR1JlcG9fYmdDYXJyaWVyIiBzdHJva2Utd2lkdGg9IjAiPjwvZz48ZyBpZD0iU1ZHUmVwb190cmFjZXJDYXJyaWVyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjwvZz48ZyBpZD0iU1ZHUmVwb19pY29uQ2FycmllciI+PHRpdGxlPjcwIEJhc2ljIGljb25zIGJ5IFhpY29ucy5jbzwvdGl0bGU+PHBhdGggZD0iTTI0LDEuMzJjLTkuOTIsMC0xOCw3LjgtMTgsMTcuMzhBMTYuODMsMTYuODMsMCwwLDAsOS41NywyOS4wOWwxMi44NCwxNi44YTIsMiwwLDAsMCwzLjE4LDBsMTIuODQtMTYuOEExNi44NCwxNi44NCwwLDAsMCw0MiwxOC43QzQyLDkuMTIsMzMuOTIsMS4zMiwyNCwxLjMyWiIgZmlsbD0iI2ZmOTQyNyI+PC9wYXRoPjxwYXRoIGQ9Ik0yNS4zNywxMi4xM2E3LDcsMCwxLDAsNS41LDUuNUE3LDcsMCwwLDAsMjUuMzcsMTIuMTNaIiBmaWxsPSIjZmZmZmZmIj48L3BhdGg+PC9nPjwvc3ZnPg== // @author KaKa // @grant GM_setClipboard // @grant GM_addStyle // @copyright KaKa // @license MIT // @downloadURL none // ==/UserScript== (function() { 'use strict'; GM_addStyle(` #coordinates-container { position: fixed; top: 100px; left: 10px; padding: 10px; border-radius: 20px !important; z-index: 1000; display: flex; flex-direction: column; width: 180px; } #coordinates-container button { cursor: pointer; width: 100% !important; font-weight: bold !important; border: 8px solid #000000 !important; text-align: left !important; padding-left: 8px !important; padding-right: 8px !important; backdrop-filter: blur(10px); margin-bottom: 5px; border-radius: 4px; background-color: #000000 !important; color: #A0A0A0 !important; }; #conversion-mode { font-family: Arial, sans-serif !important; color: #000000 !important; text-shadow: -1px -1px 0 #A0A0A0, 1px -1px 0 #A0A0A0, -1px 1px 0 #A0A0A0, 1px 1px 0 #A0A0A0 !important; margin-bottom: 5px !important; } .link-button { background: none!important; border: none; padding: 0!important; color: #FFCC00 !important; text-decoration: underline; cursor: pointer; } .link-button:hover { color: #FFCC00 !important; } `); const container = document.createElement('div'); container.id = 'coordinates-container'; document.body.appendChild(container); const conversionModeLabel = document.createElement('div'); conversionModeLabel.id = 'conversion-mode'; conversionModeLabel.style.display='none' container.appendChild(conversionModeLabel); const openButton = document.createElement('button'); openButton.textContent = 'Open in Map'; container.appendChild(openButton); const copyButton = document.createElement('button'); copyButton.textContent = 'Copy to Clipboard'; container.appendChild(copyButton); let currentLink = ''; let globalPanoId=null openButton.onclick = () => window.open(currentLink, '_blank'); copyButton.onclick = () => { GM_setClipboard(currentLink, 'text'); alert('Link copied to clipboard'); }; const areaButton = document.createElement('button'); areaButton.textContent = 'Area'; container.appendChild(areaButton); const streetButton = document.createElement('button'); streetButton.textContent = 'Street'; container.appendChild(streetButton); const timeButton = document.createElement('button'); timeButton.textContent = 'Time'; container.appendChild(timeButton); let globalTimeInfo = null; let globalAreaInfo = null; let globalStreetInfo = null; function updateButtonContent() { areaButton.textContent = globalAreaInfo ? `${globalAreaInfo}` : 'Area'; streetButton.textContent = globalStreetInfo ? `${globalStreetInfo}` : 'Street'; timeButton.textContent = globalTimeInfo ? `${globalTimeInfo}` : 'Time'; copyButton.textContent=globalPanoId ? `${globalPanoId.substring(6,10)}, ${globalPanoId.substring(25,27)}` : 'Copy to clipboard' copyButton.style.fontSize='15px' } setInterval(updateButtonContent, 1000); var realSend = XMLHttpRequest.prototype.send; XMLHttpRequest.prototype.send = function(value) { this.addEventListener('load', function() { if (this._url && this._url.includes('https://tuxun.fun/api/v0/tuxun/mapProxy/getGooglePanoInfoPost')) { const responseText = this.responseText; const coordinatePattern = /\[\[null,null,(-?\d+\.\d+),(-?\d+\.\d+)\],\[\d+\.\d+\],\[\d+\.\d+,\d+\.\d+,\d+\.\d+\]\]|\[\s*null,\s*null,\s*(-?\d+\.\d+),\s*(-?\d+\.\d+)\s*\]/; const coordinateMatches = coordinatePattern.exec(responseText); if (coordinateMatches) { const latitude = coordinateMatches[1] || coordinateMatches[3]; const longitude = coordinateMatches[2] || coordinateMatches[4]; if (latitude && longitude) { currentLink = `https://www.google.com/maps/@?api=1&map_action=pano&viewpoint=${latitude},${longitude}`; } } const countryPattern = /,\s*"([A-Z]{2})"\s*\],null,\[/; const countryMatches = countryPattern.exec(responseText); let countryCode = countryMatches ? countryMatches[1] : '未知国家'; const areaPattern = /\[\[\s*"([^"]+)",\s*"[a-z]{2}"\s*\],\s*\["([^"]+)",\s*"zh"\s*\]\]/; const areaMatches = areaPattern.exec(responseText); if (areaMatches && areaMatches.length >= 3) { globalAreaInfo = `${countryCode}, ${areaMatches[2]}`; } const fullAddressPattern = /\[\s*null,\s*null,\s*\[\s*\["([^"]+)",\s*"[a-z]{2}"\s*\]\]/; const addressMatches = fullAddressPattern.exec(responseText); if (addressMatches && addressMatches.length > 1) { globalStreetInfo = addressMatches[1]; } else { globalStreetInfo = '未知地址'; } const timePattern = /\[\d+,\d+,\d+,null,null,\[null,null,"launch",\[\d+\]\],null,\[(\d{4}),(\d{1,2})\]\]/; const timeMatches = timePattern.exec(responseText); if (timeMatches) { globalTimeInfo = `${timeMatches[1]}年${timeMatches[2]}月`; } else { globalTimeInfo = '未知时间'; } } if (this._url && this._url.includes('mapProxy/getPanoInfo')) { var responseData const responseText = this.responseText; if (responseText) responseData=JSON.parse(responseText) if(responseData){ const latitude = responseData.data.lat const longitude =responseData.data.lng globalPanoId=responseData.data.pano if (globalPanoId){ const year=parseInt(globalPanoId.substring(9,12)) const month=parseInt(globalPanoId.substring(12,14)) const day=parseInt(globalPanoId.substring(14,16)) globalTimeInfo = `20${year}年${month}月${day}日`; } else{ globalTimeInfo = '未知时间'; } const heading=responseData.data.centerHeading if (latitude && longitude) { currentLink = `https://map.baidu.com/@12707848.16,2573405.14,21z,87t,-139.74h#panoid=${globalPanoId}&panotype=street&heading=${heading}&pitch=0&l=21&tn=B_NORMAL_MAP&sc=0&newmap=1&shareurl=1&pid=${globalPanoId}`; } getAddressFromApi(latitude,longitude) .then(address => { var province,city,suburb,street globalAreaInfo='' if (address) { if (address.province) { province = address.province; globalAreaInfo += `中国, ${province}` } if (address.city) { city = address.city; globalAreaInfo +=`, ${city}` } if (address.suburb) { suburb = address.suburb; globalAreaInfo +=`, ${suburb}` } if (address.street) { globalStreetInfo = address.street; } } }) .catch(error => { console.error('获取地址时发生错误:', error); }); } } }, false); realSend.call(this, value); function getAddressFromApi(latitude, longitude) { return new Promise((resolve, reject) => { const apiUrl = `https://nominatim.openstreetmap.org/reverse?format=json&lat=${latitude}&lon=${longitude}&addressdetails=1&accept-language=cn`; fetch(apiUrl) .then(response => response.json()) .then(data => { const addressInfo = { province: data?.address?.state || '', city: data?.address?.city || '', suburb: data?.address?.suburb || '', street: data?.address?.road || '', }; resolve(addressInfo); }) .catch(error => { console.error('Error fetching address:', error); reject(error); }); }); } }; XMLHttpRequest.prototype.realOpen = XMLHttpRequest.prototype.open; XMLHttpRequest.prototype.open = function(method, url, async, user, pass) { this._url = url; this.realOpen(method, url, async, user, pass); }; window.addEventListener('popstate', function(event) { const container = document.getElementById('coordinates-container'); if (container) { container.remove(); } }); })();