Geoguessr Map-Making Auto-Tag

Tag your street views by date and address

当前为 2023-09-26 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Geoguessr Map-Making Auto-Tag
  3. // @namespace http://tampermonkey.net/
  4. // @version 2.4
  5. // @description Tag your street views by date and address
  6. // @author KaKa
  7. // @match https://map-making.app/maps/*
  8. // @grant GM_setValue
  9. // @grant GM_getValue
  10. // @grant GM_setClipboard
  11. // @license MIT
  12. // ==/UserScript==
  13.  
  14. (function() {
  15. 'use strict';
  16.  
  17. async function runScript(tags) {
  18. let api_key = GM_getValue("api_key");
  19. if (!api_key) {
  20. api_key = prompt("Please enter your Google API key");
  21. GM_setValue("api_key", api_key);
  22. }
  23.  
  24. const option = confirm('Do you want to input data from the clipboard? If you click "Cancel", you will need to upload a JSON file.');
  25.  
  26. let data;
  27. if (option) {
  28.  
  29. const text = await navigator.clipboard.readText();
  30. try {
  31. data = JSON.parse(text);
  32. } catch (error) {
  33. alert('The input JSON data is invalid or incorrectly formatted.');
  34. return;
  35. }
  36. } else {
  37.  
  38. const input = document.createElement('input');
  39. input.type = 'file';
  40. document.body.appendChild(input);
  41.  
  42. data = await new Promise((resolve) => {
  43. input.addEventListener('change', async () => {
  44. const file = input.files[0];
  45. const reader = new FileReader();
  46.  
  47. reader.onload = (event) => {
  48. try {
  49. const result = JSON.parse(event.target.result);
  50. resolve(result);
  51.  
  52. document.body.removeChild(input);
  53. } catch (error) {
  54. alert('The input JSON data is invalid or incorrectly formatted.');
  55. }
  56. };
  57.  
  58. reader.readAsText(file);
  59. });
  60.  
  61.  
  62. input.click();
  63. });
  64. }
  65. const newData = [];
  66.  
  67. async function getMetaData(url) {
  68. try {
  69. const response = await fetch(url);
  70. const data = await response.json();
  71. console.log(data);
  72. if (data.status == "OK") {
  73. let year = 'nodate';
  74. const match = data.date.match(/\d{4}/);
  75. if (match) {
  76. year = match[0];
  77. }
  78. let panoType = 'unofficial';
  79. if (data.copyright.includes('Google')) {
  80. panoType = 'official';
  81. }
  82. return [year, panoType];
  83. } else {
  84. return ["nodata","nodata"];
  85. }
  86. } catch (error) {
  87. console.error(`Error fetching metadata: ${error}`);
  88. }
  89. }
  90.  
  91. function get_Meta(id) {
  92. const url = `https://maps.googleapis.com/maps/api/streetview/metadata?pano=${id}&key=${api_key}`;
  93. return getMetaData(url);
  94. }
  95.  
  96. function search_Meta(lat, lng) {
  97. const url = `https://maps.googleapis.com/maps/api/streetview/metadata?location=${lat},${lng}&key=${api_key}`;
  98. return getMetaData(url);
  99. }
  100.  
  101. let last_token = null;
  102. let last_token_expiry = 0;
  103.  
  104. async function get_Token(api_key) {
  105. let current_time = Date.now() / 1000;
  106. if (last_token && last_token_expiry > current_time) {
  107. return last_token;
  108. }
  109. let url = `https://tile.googleapis.com/v1/createSession?key=${api_key}`;
  110. let headers = {'Content-Type': 'application/json'};
  111. let data = { "mapType": "streetview",
  112. "language": "en-US",
  113. "region": "US"};
  114. let response = await fetch(url, {method: 'POST', headers: headers, body: JSON.stringify(data)});
  115. if (response.status == 200) {
  116. let token = (await response.json()).session;
  117. last_token_expiry = current_time + 5 * 60;
  118. last_token = token;
  119. return token;
  120. } else {
  121. console.log(`Error: ${response.status}, ${await response.text()}`);
  122. }
  123. }
  124.  
  125. async function getAddress(url) {
  126. let country = 'nodata';
  127. let subdivision = 'nodata';
  128. let locality = 'nodata';
  129. try {
  130. let response = await fetch(url);
  131. if (response.status == 200) {
  132. let data = await response.json();
  133. for (let add of data.addressComponents) {
  134. if (add.types.includes('country')) {
  135. country = add.longName;
  136. }
  137. if (add.types.includes('administrative_area_level_1')) {
  138. subdivision = add.longName;
  139. }
  140. if (add.types.includes('locality')) {
  141. locality = add.longName;
  142. }
  143. }
  144. }
  145. } catch (error) {
  146. console.log(error);
  147. }
  148. return [country, subdivision, locality];
  149. }
  150.  
  151. async function get_add(id) {
  152. let tk = await get_Token(api_key);
  153. let url = `https://tile.googleapis.com/v1/streetview/metadata?session=${tk}&key=${api_key}&panoId=${id}`;
  154. return getAddress(url);
  155. }
  156.  
  157. async function search_add(lat,lng) {
  158. let tk = await get_Token(api_key);
  159. let url = `https://tile.googleapis.com/v1/streetview/metadata?session=${tk}&key=${api_key}&lat=${lat}&lng=${lng}&radius=50`;
  160. return getAddress(url);
  161. }
  162.  
  163.  
  164. var CHUNK_SIZE = 1000;
  165. var promises = [];
  166.  
  167. async function processCoord(coord, tags) {
  168. if (!coord.extra) {
  169. coord.extra = {};
  170. }
  171. if (!coord.extra.tags) {
  172. coord.extra.tags = [];
  173. }
  174. var meta;
  175. var address;
  176. if (coord.panoId) {
  177. meta = await get_Meta(coord.panoId);
  178. address= await get_add(coord.panoId);
  179. } else {
  180. meta = await search_Meta(coord.lat, coord.lng);
  181. address= await search_add(coord.lat, coord.lng);
  182. }
  183. if (meta && meta.length >= 2) {
  184. var year_tag = meta[0];
  185. var type_tag = meta[1];
  186. var country_tag=address[0]
  187. var subdivision_tag=address[1]
  188. var locality_tag=address[2]
  189. if (tags.includes('year')) coord.extra.tags.push(year_tag);
  190. if (tags.includes('type')) coord.extra.tags.push(type_tag);
  191. if (tags.includes('country')) coord.extra.tags.push(country_tag);
  192. if (tags.includes('subdivision')) coord.extra.tags.push(subdivision_tag);
  193. if (tags.includes('locality')) coord.extra.tags.push(locality_tag);
  194. newData.push(coord);
  195. }
  196. }
  197.  
  198. async function processChunk(chunk) {
  199. var promises = chunk.map(async coord => await processCoord(coord, tags));
  200. await Promise.all(promises);
  201. }
  202.  
  203. async function processData(tags) {
  204. for (let i = 0; i < data.customCoordinates.length; i += CHUNK_SIZE) {
  205. let chunk = data.customCoordinates.slice(i, i + CHUNK_SIZE);
  206. await processChunk(chunk);
  207. }
  208.  
  209. GM_setClipboard(JSON.stringify(newData));
  210. alert("New JSON data has been copied to the clipboard!");
  211. }
  212. processData(tags);
  213. }
  214.  
  215. var buttonContainer = document.createElement('div');
  216. buttonContainer.style.position = 'absolute';
  217. buttonContainer.style.left = '20px';
  218. buttonContainer.style.bottom = '48px';
  219. document.body.appendChild(buttonContainer);
  220.  
  221. function createButton(text, tags) {
  222. var button = document.createElement('button');
  223. button.textContent = text;
  224. button.style.display = 'none';
  225. button.addEventListener('click', async function() { await runScript(tags); });
  226. buttonContainer.appendChild(button);
  227. return button;
  228. }
  229.  
  230.  
  231. var mainButton = document.createElement('button');
  232. mainButton.textContent = 'Auto-Tag';
  233. mainButton.addEventListener('click', function() {
  234.  
  235. for (var i = 0; i < buttonContainer.children.length; i++) {
  236. var button = buttonContainer.children[i];
  237. if (button.style.display === 'none') {
  238. button.style.display = 'block';
  239. } else {
  240. button.style.display = 'none';
  241. }
  242. }
  243. });
  244. document.body.appendChild(mainButton);
  245.  
  246. createButton('Tag by Year', ['year']);
  247. createButton('Tag by Type',[ 'type']);
  248. createButton('Tag by Country', ['country']);
  249. createButton('Tag by Subdivision', ['subdivision']);
  250. createButton('Tag by Locality', ['locality']);
  251. })();