WME GIS Buttons

Displays the locality of the current map location and provides links to open GIS if available

当前为 2023-12-02 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name WME GIS Buttons
  3. // @author @Philistine11
  4. // @namespace https://greasyfork.org/en/users/53803
  5. // @description Displays the locality of the current map location and provides links to open GIS if available
  6. // @match *://*.waze.com/*editor*
  7. // @exclude *://*.waze.com/user/editor*
  8. // @grant GM.xmlHttpRequest
  9. // @connect script.google.com
  10. // @connect script.googleusercontent.com
  11. // @connect maps.googleapis.com
  12. // @version 1.7.0
  13. // ==/UserScript==
  14. /* global $, W, OpenLayers */
  15.  
  16. function gm_get(url) {
  17. return new Promise((resolve, reject) => {
  18. GM.xmlHttpRequest({
  19. method: 'GET',
  20. url: url,
  21. responseType: 'json',
  22. onload: e => resolve(e.response),
  23. onerror: reject,
  24. ontimeout: reject,
  25. });
  26. });
  27. }
  28.  
  29. async function gis_init() {
  30. let gisButtonsOn = JSON.parse(localStorage.getItem('gisButtonsOn'));
  31. let gisButtonsApiKey = localStorage.getItem('gisButtonsApiKey');
  32. let gisButtonsDefaultGoogle = JSON.parse(localStorage.getItem('gisButtonsDefaultGoogle'));
  33. const gisButtons = $('<div style="display:flex; gap:2px; padding-left:2rem;"><div class="btn btn-default" style="padding:5px;"><span id="gisStatus" class="fa fa-spinner fa-pulse" style="font-size:x-large;"></span></div><a id="gisSubLocality" class="btn btn-default disabled hide" target="_blank" href="#">Sublocality</a><a id="gisLocality" class="btn btn-default disabled hide" target="_blank" href="#">Locality</a><a id="gisCounty" class="btn btn-default disabled hide" target="_blank" href="#">County</a><a id="gisState" class="btn btn-default disabled hide" target="_blank" href="#">State</a></div>');
  34.  
  35. gisButtons.find('#gisStatus').click(() => {
  36. gisButtonsOn = !gisButtonsOn;
  37. localStorage.setItem('gisButtonsOn', gisButtonsOn);
  38. start();
  39. }).contextmenu(() => {
  40. do {
  41. gisButtonsApiKey = prompt("Enter your Google Maps API key:", gisButtonsApiKey || "").trim();
  42. } while (gisButtonsApiKey && gisButtonsApiKey.length != 39);
  43. localStorage.setItem('gisButtonsApiKey', gisButtonsApiKey);
  44. if (gisButtonsApiKey) {
  45. gisButtonsDefaultGoogle = confirm("Use Google Maps API by default ('OK'), or only when Census data is not available ('Cancel')?");
  46. localStorage.setItem('gisButtonsDefaultGoogle', gisButtonsDefaultGoogle);
  47. }
  48. start();
  49. return false;
  50. });
  51.  
  52. let trigger;
  53. const setTrigger = () => {trigger = setTimeout(update, 1000);}
  54. const clearTrigger = () => clearTimeout(trigger);
  55.  
  56. const states = {};
  57. const rows = await gm_get('https://script.google.com/macros/s/AKfycbx2bytvT5Un0TWcaU7BpVkauqeE8zqt8Mek7Zq-OF-bznGYDyZw/exec?link=10dR8z16eKPHeI-ywLcHh2UNS3enQ7gt36Hhzm9nOJbA');
  58. for (let row in rows) states[rows[row][0]] = rows[row][1];
  59.  
  60. const update = async () => {
  61. $('#gisStatus').removeClass().addClass('fa fa-spinner fa-pulse').css('color','').attr('title','');
  62. $('#gisSubLocality, #gisLocality, #gisCounty, #gisState').addClass('hide');
  63. let sublocality = '', locality = '', county = '', state = '';
  64.  
  65. const center = W.map.getOLMap().getCenter().transform(new OpenLayers.Projection('EPSG:900913'), new OpenLayers.Projection('EPSG:4326'));
  66. const censusData = gisButtonsDefaultGoogle ? {} : await $.getJSON(`https://geocoding.geo.census.gov/geocoder/geographies/coordinates?benchmark=Public_AR_Current&vintage=Current_Current&format=jsonp&callback=?&x=${center.lon}&y=${center.lat}`);
  67. if (censusData.result && Object.keys(censusData.result.geographies).length) {
  68. if (censusData.result.geographies.hasOwnProperty('States')) {
  69. state = censusData.result.geographies['States'][0].NAME;
  70. $('#gisState').removeClass('hide').text(state);
  71. }
  72. if (censusData.result.geographies.hasOwnProperty('Counties')) {
  73. county = censusData.result.geographies['Counties'][0].NAME;
  74. $('#gisCounty').removeClass('hide').text(county);
  75. }
  76. if (censusData.result.geographies.hasOwnProperty('Incorporated Places')) {
  77. locality = censusData.result.geographies['Incorporated Places'][0].BASENAME;
  78. $('#gisLocality').removeClass('hide').text(locality);
  79. }
  80. } else {
  81. if (!gisButtonsApiKey)
  82. return $('#gisStatus').removeClass().addClass('fa fa-key').css('color','red').attr('title',"Right-click to set Google Maps API Key");
  83.  
  84. const googleData = await gm_get(`https://maps.googleapis.com/maps/api/geocode/json?latlng=${center.lat},${center.lon}&key=${gisButtonsApiKey}`);
  85. if (googleData.status === 'OK') {
  86. const locs = googleData.results.find((result) => !result.types.includes("point_of_interest")).address_components || [];
  87. for (let loc = 0; loc < locs.length; loc++)
  88. if (locs[loc].types.indexOf('administrative_area_level_1') !== -1) {
  89. state = locs[loc].long_name;
  90. $('#gisState').removeClass('hide').text(state);
  91. } else if (locs[loc].types.indexOf('administrative_area_level_2') !== -1) {
  92. county = locs[loc].long_name;
  93. $('#gisCounty').removeClass('hide').text(county);
  94. } else if (locs[loc].types.indexOf('locality') !== -1) {
  95. locality = locs[loc].long_name;
  96. $('#gisLocality').removeClass('hide').text(locality);
  97. } else if (locs[loc].types.indexOf('sublocality') !== -1 || locs[loc].types.indexOf('neighborhood') !== -1) {
  98. sublocality = locs[loc].long_name;
  99. $('#gisSubLocality').removeClass('hide').text(sublocality);
  100. }
  101. } else {
  102. $('#gisStatus').removeClass().addClass('fa fa-exclamation-circle').css('color','red').attr('title', "Check console for errors");
  103. return console.error("GIS Buttons", censusData, googleData);
  104. }
  105. }
  106.  
  107. $('#gisLocality, #gisCounty, #gisState').prop('href', '#').addClass('disabled');
  108. if (states.hasOwnProperty(state)) {
  109. if (typeof (states[state]) === 'string')
  110. states[state] = await gm_get(`https://script.google.com/macros/s/AKfycbx2bytvT5Un0TWcaU7BpVkauqeE8zqt8Mek7Zq-OF-bznGYDyZw/exec?link=${states[state]}`);
  111. for (let row in states[state])
  112. if (states[state][row][2] !== '')
  113. if (states[state][row][1] === 'State') {
  114. $('#gisState').prop('href', states[state][row][2].replace('<lat>',center.lat).replace('<lon>',center.lon).replace('<zoom>',W.map.getZoom())).removeClass('disabled');
  115. } else if (states[state][row][1] === 'County') {
  116. if (county.indexOf(states[state][row][0]) != -1)
  117. $('#gisCounty').prop('href', states[state][row][2].replace('<lat>',center.lat).replace('<lon>',center.lon).replace('<zoom>',W.map.getZoom())).removeClass('disabled');
  118. } else if (states[state][row][0] === locality)
  119. $('#gisLocality').prop('href', states[state][row][2].replace('<lat>',center.lat).replace('<lon>',center.lon).replace('<zoom>',W.map.getZoom())).removeClass('disabled');
  120. }
  121. $('#gisStatus').removeClass().addClass('fa fa-power-off').css('color','green').attr('title',"Click to turn off GIS Buttons");
  122. };
  123.  
  124. function start() {
  125. const location = $('div.topbar:not(.topbar-mte) > div.location-info-region');
  126. if (location.length === 0) return setTimeout(start, 500);
  127. location.after(gisButtons);
  128.  
  129. const mapEvents = W.map.events;
  130. if (gisButtonsOn) {
  131. mapEvents.register('movestart', null, clearTrigger);
  132. mapEvents.register('moveend', null, setTrigger);
  133. update();
  134. } else {
  135. clearTrigger();
  136. mapEvents.unregister('movestart', null, clearTrigger);
  137. mapEvents.unregister('moveend', null, setTrigger);
  138. $('#gisSubLocality, #gisLocality, #gisCounty, #gisState').addClass('hide');
  139. $('#gisStatus').removeClass().addClass('fa fa-power-off').css('color','red').attr('title',"Click to turn on GIS Buttons");
  140. }
  141. }
  142.  
  143. start();
  144. }
  145.  
  146. gis_init();