// ==UserScript== // @author DanielOnDiordna // @name IITC plugin: UMM distance // @category Misc // @version 1.1.0.20230315.212300 // @updateURL https://softspot.nl/ingress/plugins/iitc-plugin-ummdistance.meta.js // @downloadURL https://softspot.nl/ingress/plugins/iitc-plugin-ummdistance.user.js // @description [danielondiordna-1.1.0.20230315.212300] Display and auto update the total missions distance (in km or miles) for the Ultimate Mission Maker drawn missions in IITC // @id iitc-plugin-ummdistance@danielondiordna // @namespace https://softspot.nl/ingress/ // @match https://intel.ingress.com/* // @grant none // ==/UserScript== function wrapper(plugin_info) { // ensure plugin framework is there, even if iitc is not yet loaded if(typeof window.plugin !== 'function') window.plugin = function() {}; // use own namespace for plugin window.plugin.ummdistance = function() {}; var self = window.plugin.ummdistance; self.id = 'ummdistance'; self.title = 'UMM distance'; self.version = '1.1.0.20230315.212300'; self.author = 'DanielOnDiordna'; self.changelog = ` Changelog: version 1.1.0.20230315.212300 - auto position the distance button directly under the UMM buttons - auto position the layer chooser directly under the UMM layers - added a link to the Ultimate Mission Maker website in the about menu - update distance after clear all missions data is used in UMM version 1.0.0.20230315.003700 - first release - included an onscreen control button with the distance - distance keeps up to 3 decimals for lower distances - included an option to display the distance in kilometers or miles - included an option to include the distance between missions - included a layer chooser to show or hide the distance icon `; self.namespace = 'window.plugin.' + self.id + '.'; self.pluginname = 'plugin-' + self.id; self.localstoragesettings = self.pluginname + '-settings'; self.settings = { // default settings: measurement: 'kilometers', between: true }; self.injectUMM = function() { if (typeof window.plugin?.umm?.saveUmmState == 'function') { let original_saveUmmState = window.plugin.umm.saveUmmState; window.plugin.umm.saveUmmState = function(ummState) { original_saveUmmState(ummState); self.updatedistance(); self.updatemenu(); }; } if (typeof window.plugin?.umm?.clearMissionData == 'function') { let original_clearMissionData = window.plugin.umm.clearMissionData; window.plugin.umm.clearMissionData = function(ummState) { original_clearMissionData(ummState); self.updatedistance(); self.updatemenu(); }; } }; self.distanceBetween = function(startLatLng,endLatLng) { // source: Arc 1.7.0 // How far between portals. let R = 6367; // km let lat1 = startLatLng.lat; let lon1 = startLatLng.lng; let lat2 = endLatLng.lat; let lon2 = endLatLng.lng; let dLat = (lat2 - lat1) * Math.PI / 180; let dLon = (lon2 - lon1) * Math.PI / 180; lat1 = lat1 * Math.PI / 180; lat2 = lat2 * Math.PI / 180; let a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.sin(dLon / 2) * Math.sin(dLon / 2) * Math.cos(lat1) * Math.cos(lat2); let c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); let d = R * c; d = Math.round(d * 1000) / 1000; return d; }; self.distancebetweenpoints = function(point1,point2) { let length = self.distanceBetween(point1,point2) * 1000; let distance = window.L.latLng(point1.lat, point1.lng).distanceTo([point2.lat, point2.lng]); return distance; }; self.convertKmToMiles = function(km) { return km * 0.621371; }; self.getdistance = function() { if (typeof window.plugin?.umm?.getUmmState != 'function') return 'UMM'; let distance = 0; let ummState = window.plugin.umm.getUmmState(); let previousposition = undefined; for (let missioncnt in ummState.missions) { let missiondistance = 0; if (!self.settings.between) { previousposition = undefined; } for (let portalcnt in ummState.missions[missioncnt].portals) { let location = ummState.missions[missioncnt].portals[portalcnt].location; let position = window.L.latLng(location.latitude,location.longitude); if (previousposition) { missiondistance += self.distancebetweenpoints(previousposition,position); } previousposition = position; } distance += missiondistance; } distance = distance / 1000; // km if (self.settings.measurement == 'miles') { distance = self.convertKmToMiles(distance); } if (distance == 0) { return 0; } else if (distance < 10) { return distance.toFixed(3).replace(/0+$/,'').replace(/\.$/,''); } else if (distance < 100) { return distance.toFixed(2).replace(/0+$/,'').replace(/\.$/,''); } else if (distance < 1000) { return distance.toFixed(1).replace(/0+$/,'').replace(/\.$/,''); } return Math.round(distance); }; self.updatedistance = function() { $(`#${self.id}-controls-distance`).text(self.getdistance()); }; self.addControls = function() { let controls = window.L.Control.extend({ options: { position: 'topleft' }, onAdd: function (map) { let container = window.L.DomUtil.create('div', 'leaflet-ummdistance leaflet-bar'); $(container).append(`${self.getdistance()}`).on('click dblclick', `#${self.id}-controls-distance`, function (event) { self.menu(); }); return container; } }); window.map.addControl(new controls()); }; self.moveControls = function() { let ummcontrols = document.querySelector('.leaflet-umm'); let mycontrols = document.querySelector('.leaflet-ummdistance'); if (ummcontrols && mycontrols) { ummcontrols.after(mycontrols); } }; self.moveLayerControls = function() { let ummmissionnumbers = [...document.querySelectorAll('.leaflet-control-layers-selector')].filter(el=>{return el.parentElement.innerText.match(/^ UMM: /)})?.reverse()[0]?.parentElement.parentElement; // find the last UMM: layer let mylayercontrols = [...document.querySelectorAll('.leaflet-control-layers-selector')].filter(el=>{return el.parentElement.innerText == ' ' + self.title})?.[0].parentElement.parentElement; if (ummmissionnumbers && mylayercontrols) { ummmissionnumbers.after(mylayercontrols); } }; self.restoresettings = function() { try { let settings = JSON.parse(localStorage[self.localstoragesettings]); for (let key in self.settings) { if (key in settings && typeof settings[key] == typeof self.settings[key]) { self.settings[key] = settings[key]; } } } catch(e) { self.storesettings(); } }; self.storesettings = function() { try { localStorage[self.localstoragesettings] = JSON.stringify(self.settings); } catch(e) { console.log(self.title + ' - storesettings - FAILED: out of localStorage memory'); } }; self.about = function() { let container = document.createElement('div'); container.innerHTML = `

Thank you for using the ${self.title} plugin.
With this plugin you can instantly display the total distance of your Ultimate Mission Maker missions.
The Ultimate Mission Maker plugin is required.

This plugins hooks into the UMM plugin and will auto update the distance on every change of your missions.

You can show or hide the distance icon from the layer chooser.

Share this plugin with this link: Softspot IITC plugins to get the latest version.

${self.title} version ${self.version} by ${self.author}
`; window.dialog({ html: container, id: self.pluginname + '-dialog', title: self.title + ' - About', width: 'auto' }).dialog('option', 'buttons', { '< Main menu': function() { self.menu(); }, 'Changelog': function() { alert(self.changelog); }, 'Close': function() { $(this).dialog('close'); } }); }; self.updatemenu = function(container) { if (typeof window.plugin?.umm?.getUmmState != 'function') return; if (!container) container = window.DIALOGS[`dialog-${self.pluginname}-dialog`]; if (!container) return; let ummState = window.plugin.umm.getUmmState(); let missioncount = ummState.missions.length; let portalcount = 0; for (let missioncnt in ummState.missions) { portalcount += ummState.missions[missioncnt].portals.length; } container.querySelector(`.${self.id}-missions-count`).innerText = missioncount; container.querySelector(`.${self.id}-portals-count`).innerText = portalcount; container.querySelector(`.${self.id}-distance-count`).innerText = self.getdistance() + ' ' + self.settings.measurement; }; self.menu = function() { if (typeof window.plugin?.umm?.getUmmState != 'function') { self.about(); return; } let container = document.createElement('div'); container.innerHTML = ` Total missions:
Total portals:
Total distance:



`; self.updatemenu(container); let betweencheckbox = container.querySelector(`input[name="${self.id}-between-checkbox"]`); betweencheckbox.checked = self.settings.between; betweencheckbox.addEventListener('click', function(e) { self.settings.between = this.checked; self.updatedistance(); self.updatemenu(); self.storesettings(); }, false); container.querySelectorAll(`input[name="${self.id}-measurement-radio"]`).forEach((radio) => { if (radio.value == self.settings.measurement) radio.checked = true; radio.addEventListener('change', function(e) { self.settings.measurement = e.target.value; self.updatedistance(); self.updatemenu(); self.storesettings(); }, false); }); window.dialog({ html: container, id: self.pluginname + '-dialog', title: self.title, width: 'auto' }).dialog('option', 'buttons', { 'About': function() { self.about(); }, 'Close': function() { $(this).dialog('close'); } }); }; self.setup = function() { self.restoresettings(); self.addControls(); setTimeout(self.moveControls); // place in a time event, so it will run after UMM buttons are activated as well self.injectUMM(); let togglecontrolslayer = new window.L.FeatureGroup(); window.addLayerGroup(self.title, togglecontrolslayer,true); setTimeout(self.moveLayerControls); if (!window.map.hasLayer(togglecontrolslayer)) { $('.leaflet-ummdistance').hide(); } window.map.on('layeradd', function(obj) { // show controls if (obj.layer === togglecontrolslayer) { $('.leaflet-ummdistance').show(); } }); window.map.on('layerremove', function(obj) { // hide controls if (obj.layer === togglecontrolslayer) { $('.leaflet-ummdistance').hide(); } }); console.log('IITC plugin loaded: ' + self.title + ' version ' + self.version); }; var setup = function() { (window.iitcLoaded?self.setup():window.addHook('iitcLoaded',self.setup)); }; setup.info = plugin_info; //add the script info data to the function as a property if(!window.bootPlugins) window.bootPlugins = []; window.bootPlugins.push(setup); // if IITC has already booted, immediately run the 'setup' function if(window.iitcLoaded && typeof setup === 'function') setup(); } // wrapper end // inject code into site context var script = document.createElement('script'); var info = {}; if (typeof GM_info !== 'undefined' && GM_info && GM_info.script) info.script = { version: GM_info.script.version, name: GM_info.script.name, description: GM_info.script.description }; script.appendChild(document.createTextNode('('+ wrapper +')('+JSON.stringify(info)+');')); (document.body || document.head || document.documentElement).appendChild(script);