WME Mapraid Overlays

Mapraid overlays

  1. // ==UserScript==
  2. // @name WME Mapraid Overlays
  3. // @namespace https://greasyfork.org/en/users/166843-wazedev
  4. // @version 2024.05.29.01
  5. // @description Mapraid overlays
  6. // @author JustinS83
  7. // @include https://www.waze.com/editor*
  8. // @include https://www.waze.com/*/editor*
  9. // @include https://beta.waze.com/editor*
  10. // @include https://beta.waze.com/*/editor*
  11. // @exclude https://www.waze.com/*user/editor*
  12. // @grant GM_xmlhttpRequest
  13. // @connect api.github.com
  14. // @connect raw.githubusercontent.com
  15. // @require https://greasyfork.org/scripts/24851-wazewrap/code/WazeWrap.js
  16. // @contributionURL https://github.com/WazeDev/Thank-The-Authors
  17. // ==/UserScript==
  18.  
  19. /* global W */
  20. /* global OL */
  21. /* ecmaVersion 2017 */
  22. /* global $ */
  23. /* global I18n */
  24. /* global _ */
  25. /* global WazeWrap */
  26. /* global require */
  27. /* eslint curly: ["warn", "multi-or-nest"] */
  28.  
  29. (function() {
  30. 'use strict';
  31.  
  32. var _settings;
  33. var _settingsStoreName = '_wme_mapraid_overlays';
  34. var _kml;
  35. var _layerName = 'Mapraid Overlay';
  36. var _layer = null;
  37. var countryAbbr;
  38. var _origOpacity;
  39. var _mapraidNameMap = {};
  40.  
  41. function bootstrap(tries = 1) {
  42. if (W &&
  43. W.map &&
  44. W.model &&
  45. W.loginManager.user &&
  46. W.model.getTopCountry() &&
  47. $ && WazeWrap.Ready)
  48. init();
  49. else if (tries < 1000)
  50. setTimeout(function () {bootstrap(++tries);}, 200);
  51. }
  52.  
  53. bootstrap();
  54.  
  55. function isChecked(checkboxId) {
  56. return $('#' + checkboxId).is(':checked');
  57. }
  58.  
  59. function setChecked(checkboxId, checked) {
  60. $('#' + checkboxId).prop('checked', checked);
  61. }
  62.  
  63. function loadSettings() {
  64. _settings = $.parseJSON(localStorage.getItem(_settingsStoreName));
  65. let _defaultsettings = {
  66. layerVisible: true,
  67. EnabledOverlays: {},
  68. HideCurrentArea: false
  69. };
  70. _settings = $.extend({}, _defaultsettings, _settings);
  71. }
  72.  
  73. function saveSettings() {
  74. if (localStorage) {
  75. var settings = {
  76. layerVisible: _layer.visibility,
  77. EnabledOverlays: _settings.EnabledOverlays,
  78. HideCurrentArea: _settings.HideCurrentArea
  79. };
  80. localStorage.setItem(_settingsStoreName, JSON.stringify(settings));
  81. }
  82. }
  83.  
  84. async function getKML(url){
  85. return await wrapGMXMLHTTP(url); //$.get(url);
  86. }
  87.  
  88. async function wrapGMXMLHTTP(url){
  89. return new Promise((resolve, reject) => {
  90. GM_xmlhttpRequest({
  91. url,
  92. method: 'GET',
  93. onload(res) {
  94. if (res.status < 400) {
  95. resolve(res.responseText);
  96. } else {
  97. console.log("error");
  98. console.log(res.status);
  99. // handle errors here
  100. }
  101. },
  102. onerror(res) {
  103. // handle errors here
  104. console.log("Error:");
  105. console.log(res.text);
  106. }
  107. });
  108. });
  109. }
  110.  
  111. function GetFeaturesFromKMLString(strKML) {
  112. var format = new OpenLayers.Format.KML({
  113. 'internalProjection': W.map.getProjectionObject(),
  114. 'externalProjection': new OpenLayers.Projection("EPSG:4326"),
  115. 'extractStyles': true
  116. });
  117. return format.read(strKML);
  118. }
  119.  
  120. async function init(){
  121. loadSettings();
  122.  
  123. var layerid = 'wme_mapraid_overlays';
  124.  
  125. _layer = new OpenLayers.Layer.Vector("Mapraid Overlays", {
  126. rendererOptions: { zIndexing: true },
  127. uniqueName: layerid,
  128. layerGroup: 'mapraid_overlays',
  129. zIndex: -9999,
  130. visibility: _settings.layerVisible
  131. });
  132. I18n.translations[I18n.locale].layers.name[layerid] = "Mapraid Overlays";
  133. W.map.addLayer(_layer);
  134.  
  135. var $section = $("<div>", {style:"padding:8px 16px", id:"WMEMapraidOverlays"});
  136. $section.html([
  137. `<h4 style="margin-bottom:0px;"><b>WME Mapraid Overlays</b></h4>`,
  138. `<h6 style="margin-top:0px;">${GM_info.script.version}</h6>`,
  139. `<div><input type="checkbox" id="_cbMROHideCurrentArea" class="wmemroSettingsCheckbox" /><label for="_cbMROHideCurrentArea">Hide fill for current area</label></div>`,
  140. `<div id="divWMEMROAvailableOverlays"><label>Available overlays</label> <select id="mroOverlaySelect" style="min-width:125px;"></select><i class="fa fa-plus fa-lg" id="mroAddOverlay" aria-hidden="true" style="color:green; cursor:pointer;"></i></div>`,
  141. '<div id="currOverlays"></div>',
  142. '<div style="position:absolute; bottom:0;">Generate new mapraid overlays at <a href="http://wazedev.com/mapraidgenerator.html" target="_blank">http://wazedev.com/mapraidgenerator.html</a></div>',
  143. '</div>'
  144. ].join(' '));
  145.  
  146. WazeWrap.Interface.Tab('MRO', $section.html(), init2, 'MRO');
  147. }
  148.  
  149. async function getAvailableOverlays(){
  150. $('#mroOverlaySelect').innerHTML = "";
  151. countryAbbr = W.model.getTopCountry().attributes.abbr;
  152. let KMLinfoArr = $.parseJSON(await wrapGMXMLHTTP(`https://api.github.com/repos/WazeDev/WME-Mapraid-Overlays/contents/KMLs/${countryAbbr}`));
  153. let overlaysSelect = $('<div>');
  154.  
  155. overlaysSelect.html([
  156. '<option selected disabled hidden style="display: none" value=""></option>',
  157. `${KMLinfoArr.map(function(obj){
  158. let fileName = obj.name.replace(".kml", "");
  159. if(!_settings.EnabledOverlays[fileName])
  160. return `<option value="${fileName}">${fileName}</option>`;
  161. })}`,
  162. '</select>'
  163. ].join(''));
  164. $('#mroOverlaySelect')[0].innerHTML = overlaysSelect.html();
  165. }
  166.  
  167. function updatePolygons(newKML, mapraidName){
  168. var _features = GetFeaturesFromKMLString(newKML);
  169.  
  170. for(let i=0; i< _features.length; i++){
  171. _features[i].attributes.name = _features[i].attributes.name.replace('<at><openparen>', '').replace('<closeparen>','');
  172. _features[i].style.label = _features[i].attributes.name;
  173. _features[i].style.labelOutlineColor= '#000000';
  174. _features[i].style.labelOutlineWidth= 4;
  175. _features[i].style.labelAlign= 'cm';
  176. _features[i].style.fontSize= "16px";
  177. _features[i].style.fontColor= _features[i].style.fillColor;//"#ffffff";
  178. _features[i].attributes.mapraidName = mapraidName;
  179.  
  180. if(!_settings.EnabledOverlays[mapraidName].fillAreas){
  181. if(!_origOpacity)
  182. _origOpacity = _features[i].style.fillOpacity;
  183. _features[i].style.fillOpacity = 0;
  184. }
  185. }
  186.  
  187. _layer.addFeatures(_features);
  188. }
  189.  
  190. function hex_is_light(color) {
  191. const hex = color.replace('#', '');
  192. const c_r = parseInt(hex.substr(0, 2), 16);
  193. const c_g = parseInt(hex.substr(2, 2), 16);
  194. const c_b = parseInt(hex.substr(4, 2), 16);
  195. const brightness = ((c_r * 299) + (c_g * 587) + (c_b * 114)) / 1000;
  196. return brightness > 70;
  197. }
  198.  
  199. async function BuildEnabledOverlays(mapraidName){
  200. let kml;
  201. try{
  202. kml = await getKML(encodeURI(`https://raw.githubusercontent.com/WazeDev/WME-Mapraid-Overlays/master/KMLs/${countryAbbr}/${mapraidName}.kml`));
  203. }
  204. catch(err){
  205. return;
  206. console.error(err);
  207. }
  208. let kmlObj = $($.parseXML(kml));
  209. let RaidAreas = $(kmlObj).find('Placemark');
  210.  
  211. let $newRaidSection = $('<div>');
  212. $newRaidSection.html([
  213. `<fieldset style="border:1px solid silver; padding:8px; border-radius:4px; position:relative;"><legend style="margin-bottom:0px; borer-bottom-style:none; width:auto;"><h4>${mapraidName}</h4></legend>`,
  214. `<i class="fa fa-minus fa-lg" id="mroRemoveOverlay${mapraidName.replace(/\s/g, "_")}" aria-hidden="true" style="color:red; position:absolute; cursor:pointer; top:10px; right:5px;"></i>`,
  215. `<div><input type="checkbox" id="_cbMROFillRaidArea${mapraidName.replace(/\s/g, "_")}" ${_settings.EnabledOverlays[mapraidName].fillAreas ? 'checked' : ''} /><label for="_cbMROFillRaidArea${mapraidName.replace(/\s/g, "_")}">Fill raid area</label></div>`,
  216. `Jump to <select id="${mapraidName.replace(/\s/g, "_")}_Areas">${
  217. function(){
  218. let names = $(RaidAreas).find('name');
  219. let options = "";
  220. for(let i=0; i<names.length; i++)
  221. options += `<option>${$(names[i]).text()}</option>`;
  222. return options;
  223. }()
  224. }</select>`,
  225. `<i class="fa fa-share" aria-hidden="true" style="color:green; cursor:pointer;" id="JumpTo${mapraidName.replace(/\s/g, "_")}"></i>`,
  226. '</fieldset>'
  227. ].join(''));
  228.  
  229. $(`#mroOverlaySelect option[value="${mapraidName}"]`).remove(); //remove this option from the list
  230. $('#currOverlays').append($newRaidSection.html()); //add the mapraid section
  231.  
  232. $('[id^="_cbMROFillRaidArea"]').change(function(){
  233. let mapraid = this.id.replace("_cbMROFillRaidArea", "");
  234. _settings.EnabledOverlays[_mapraidNameMap[mapraid]].fillAreas = isChecked(this.id);
  235. saveSettings();
  236. });
  237.  
  238. $('[id^="mroRemoveOverlay"]').click(function(){
  239. let mapraid = this.id.replace("mroRemoveOverlay", "");
  240. $(this).parent().remove();
  241.  
  242. delete _settings.EnabledOverlays[_mapraidNameMap[mapraid]];
  243. saveSettings();
  244.  
  245. let deleteFeatures = [];
  246. for(let i=0; i < _layer.features.length; i++){ //delete the features from the layer
  247. if(_layer.features[i].attributes.mapraidName === _mapraidNameMap[mapraid])
  248. deleteFeatures.push(_layer.features[i]);
  249. }
  250. _layer.removeFeatures(deleteFeatures);
  251. getAvailableOverlays();
  252. });
  253.  
  254. $('[id^=_cbMROFillRaidArea]').change(function(){
  255. let mapraid = this.id.replace("_cbMROFillRaidArea", "");
  256. for(let i=0; i<_layer.features.length; i++){
  257. if(_layer.features[i].attributes.mapraidName.replace(/\s/g, "_") === mapraid){
  258. if(!_origOpacity)
  259. _origOpacity = _layer.features[i].style.fillOpacity;
  260. _layer.features[i].style.fillOpacity = isChecked(this.id) ? _origOpacity : 0;
  261. _layer.redraw();
  262. }
  263. }
  264. });
  265.  
  266. $('[id^="JumpTo"]').click(function(){
  267. //jump to the appropriate area - look up the area in the layer features and jump to the centroid.
  268. let raidArea = this.id.replace("JumpTo", "");
  269. for(let i=0; i<_layer.features.length; i++){
  270. if(_layer.features[i].attributes.mapraidName.replace(/\s/g, "_") === raidArea){
  271. let selectedArea = $(`#${raidArea.replace(/\s/g, "_")}_Areas`).val();
  272. if(_layer.features[i].attributes.name === selectedArea){
  273. let centroid = _layer.features[i].geometry.getCentroid();
  274. W.map.setCenter(new OL.LonLat(centroid.x, centroid.y), W.map.zoom)
  275. break;
  276. }
  277. }
  278. }
  279.  
  280. });
  281.  
  282. updatePolygons(kml, mapraidName);
  283. }
  284.  
  285. function HandleMoveZoom(){
  286. //display the current MR area in the title bar
  287. //hide the current MR area fill (if setting is enabled)
  288.  
  289. if($('#mrodivCurrMapraidArea').length === 0){
  290. var $section = $("<div>");
  291. $section.html([
  292. '<div id="mrodivCurrMapraidArea" style="font-size: 16px; font-weight:bold; margin-left:10px; float:left;">',
  293. '<span id="mroCurrAreaTopbar"></span>',
  294. '</div>'
  295. ].join(' '));
  296.  
  297. $('.topbar').append($section.html());
  298. }
  299.  
  300. let center = new OpenLayers.Geometry.Point(W.map.getCenter().lon,W.map.getCenter().lat);
  301. $('#mroCurrAreaTopbar').text("");
  302. for (var i=0;i<_layer.features.length;i++){
  303. var feature = _layer.features[i];
  304. if(_origOpacity && _settings.EnabledOverlays[feature.attributes.mapraidName].fillAreas)
  305. feature.style.fillOpacity = _origOpacity;
  306. if(feature.geometry.intersects(center)){
  307. $('#mroCurrAreaTopbar').text(feature.attributes.name);
  308. $('#mroCurrAreaTopbar').css('color', feature.style.fillColor);
  309.  
  310. if(!hex_is_light(feature.style.fillColor))
  311. $('#mroCurrAreaTopbar').css('text-shadow', '-1px 0 #efefef, 0 1px #efefef, 1px 0 #efefef, 0 -1px #efefef');
  312. else
  313. $('#mroCurrAreaTopbar').css('text-shadow', '-1px 0 black, 0 1px black, 1px 0 black, 0 -1px black');
  314.  
  315.  
  316. if(_settings.HideCurrentArea){
  317. if(!_origOpacity)
  318. _origOpacity = feature.style.fillOpacity;
  319. if(feature.style.fillOpacity > 0)
  320. feature.style.fillOpacity = 0;
  321. }
  322. }
  323. }
  324. _layer.redraw();
  325. }
  326.  
  327. function init2(){
  328. getAvailableOverlays();
  329.  
  330. $.each(_settings.EnabledOverlays, function(k, v){
  331. if(!_mapraidNameMap[k.replace(/\s/g, "_")])
  332. _mapraidNameMap[k.replace(/\s/g, "_")] = k;
  333. BuildEnabledOverlays(k);
  334.  
  335. });
  336.  
  337. $('#mroAddOverlay').click(async function(){
  338. if($('#mroOverlaySelect').val() !== null){
  339. let raid = $('#mroOverlaySelect').val();
  340. _settings.EnabledOverlays[raid] = {fillAreas: true};
  341.  
  342. BuildEnabledOverlays(raid);
  343. if(!_mapraidNameMap[raid.replace(/\s/g, "_")])
  344. _mapraidNameMap[raid.replace(/\s/g, "_")] = raid;
  345.  
  346. saveSettings();
  347. }
  348. });
  349.  
  350. $('.wmemroSettingsCheckbox').change(function(){
  351. var settingName = $(this)[0].id.substr(6);
  352. _settings[settingName] = this.checked;
  353. saveSettings();
  354. });
  355.  
  356. $('#_cbMROHideCurrentArea').change(function(){
  357. HandleMoveZoom();
  358. });
  359.  
  360. WazeWrap.Events.register("zoomend", null, HandleMoveZoom);
  361. WazeWrap.Events.register("moveend", null, HandleMoveZoom);
  362.  
  363. setChecked('_cbMROHideCurrentArea', _settings.HideCurrentArea);
  364. HandleMoveZoom();
  365. }
  366.  
  367. })();