// ==UserScript== // @author Zaso // @name Link Prolongation // @category Layers // @version 0.1.2 // @description Create link prolongations. // @id link-prolongation@Zaso // @namespace https://github.com/IITC-CE/ingress-intel-total-conversion // @updateURL https://raw.githubusercontent.com/IITC-CE/Community-plugins/master/dist/Zaso/link-prolongation.meta.js // @downloadURL https://raw.githubusercontent.com/IITC-CE/Community-plugins/master/dist/Zaso/link-prolongation.user.js // @recommends draw-tools@breunigs|draw-tools-plus@zaso|bookmarks@ZasoGD|font-awesome@zaso // @match https://intel.ingress.com/* // @match https://intel-x.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() {}; //PLUGIN AUTHORS: writing a plugin outside of the IITC build environment? if so, delete these lines!! //(leaving them in place might break the 'About IITC' page or break update checks) plugin_info.buildName = 'ZasoItems'; plugin_info.dateTimeVersion = '2022-12-18-193146'; plugin_info.pluginId = 'link-prolongation'; //END PLUGIN AUTHORS NOTE // PLUGIN START //////////////////////////////////////////////////////// // history // 0.1.2 setup changed, code-style // 0.1.1 headers changed. Ready for IITC-CE // 0.1.0 Original script /* exported setup --eslint */ /* global L, dialog, map */ // use own namespace for plugin window.plugin.linkProlongation = function(){}; window.plugin.linkProlongation.storage = {}; window.plugin.linkProlongation.data = {}; window.plugin.linkProlongation.obj = {}; window.plugin.linkProlongation.dialog = {}; window.plugin.linkProlongation.ui = {}; window.plugin.linkProlongation.action = {}; window.plugin.linkProlongation.layer = {}; window.plugin.linkProlongation.bookmarks = {}; window.plugin.linkProlongation.obj.status = { dist:1, drawType:5, drawColor:{enabled:0, value:'#f66'}}; window.plugin.linkProlongation.obj.points = {}; window.plugin.linkProlongation.obj.supp = {}; // ----------------------------------- // STORAGE // ----------------------------------- window.plugin.linkProlongation.storage.NAME = 'plugin-linkProlongation'; window.plugin.linkProlongation.storage.save = function(){ window.localStorage[window.plugin.linkProlongation.storage.NAME] = JSON.stringify(window.plugin.linkProlongation.obj.status); }; window.plugin.linkProlongation.storage.load = function(){ window.plugin.linkProlongation.obj.status = JSON.parse(window.localStorage[window.plugin.linkProlongation.storage.NAME]); }; window.plugin.linkProlongation.storage.check = function(){ if (!window.localStorage[window.plugin.linkProlongation.storage.NAME]){ window.plugin.linkProlongation.storage.save(); } window.plugin.linkProlongation.storage.load(); }; // ----------------------------------- // DATA // ----------------------------------- window.plugin.linkProlongation.data.setDist = function(number){ var v = parseInt(number); if (Number.isInteger(v) === false){ window.plugin.linkProlongation.dialog.openBoxMessage(' Error: Insert a number!'); } else if (v < 1){ window.plugin.linkProlongation.dialog.openBoxMessage(' Not Saved. Use a number equal or greater than 1!'); } else if (v > 7000){ window.plugin.linkProlongation.dialog.openBoxMessage(' Not Saved. Use a smallest number!'); } else { var newDist = Math.round(number); var abs = Math.abs(number - newDist); if (abs !== 0 && abs !== 1){ window.plugin.linkProlongation.dialog.openBoxMessage(' The distance saved has been rounded to '+newDist); } window.plugin.linkProlongation.obj.status.dist = newDist; window.plugin.linkProlongation.storage.save(); $('.linkProlongationDialog input.dist').val(newDist); return window.plugin.linkProlongation.data.getDist(); } return false; }; window.plugin.linkProlongation.data.getDist = function(){ return window.plugin.linkProlongation.obj.status.dist; }; window.plugin.linkProlongation.data.setDrawType = function(number){ number = isNaN( parseInt(number))? 5 : parseInt(number); window.plugin.linkProlongation.obj.status.drawType = number; }; window.plugin.linkProlongation.data.getDrawType = function(){ return window.plugin.linkProlongation.obj.status.drawType; }; window.plugin.linkProlongation.data.setDrawColorStatus = function(status){ window.plugin.linkProlongation.obj.status.drawColor.enabled = status; }; window.plugin.linkProlongation.data.setDrawColorValue = function(color){ window.plugin.linkProlongation.obj.status.drawColor.value = color; }; window.plugin.linkProlongation.data.getDrawColor = function(){ return window.plugin.linkProlongation.obj.status.drawColor; }; window.plugin.linkProlongation.data.setPoint = function(number, LatLng){ // is coord array console.log(LatLng); try { if (Array.isArray(LatLng) === true && LatLng.length === 2){ var lat = parseFloat(LatLng[0].replace(',', '.')); var lng = parseFloat(LatLng[1].replace(',', '.')); var ll = new L.LatLng(lat, lng); window.plugin.linkProlongation.obj.points['p'+number] = ll; } // is coord obj else if (typeof LatLng === 'object' && LatLng.lat !== undefined && LatLng.lng !== undefined){ window.plugin.linkProlongation.obj.points['p'+number] = LatLng; } } catch (e) { window.plugin.linkProlongation.dialog.openBoxMessage(' Point not saved!'); return false; } window.plugin.linkProlongation.storage.save(); return window.plugin.linkProlongation.obj.points['p'+number]; }; window.plugin.linkProlongation.data.setPoint_1 = function(LatLng){ window.plugin.linkProlongation.data.setPoint(1, LatLng); }; window.plugin.linkProlongation.data.setPoint_2 = function(LatLng){ window.plugin.linkProlongation.data.setPoint(2, LatLng); }; window.plugin.linkProlongation.data.setPoints = function(LatLng1, LatLng2){ window.plugin.linkProlongation.data.setPoint_1(LatLng1); window.plugin.linkProlongation.data.setPoint_2(LatLng2); }; window.plugin.linkProlongation.data.getPoints = function(){ return window.plugin.linkProlongation.obj.points; }; window.plugin.linkProlongation.data.getCurrPortal = function(){ var guid = window.selectedPortal; if (guid === null){ window.plugin.linkProlongation.dialog.openBoxMessage(' Select a portal.'); } else { var p = window.portals[guid]; var ll = p.getLatLng(); return ll; } return false; }; window.plugin.linkProlongation.data.setP1fromPortal = function(){ var latLng = window.plugin.linkProlongation.data.getCurrPortal(); if (latLng !== false){ window.plugin.linkProlongation.data.setPoint_1(latLng); window.plugin.linkProlongation.ui.updateInput(); } }; window.plugin.linkProlongation.data.setP2fromPortal = function(){ var latLng = window.plugin.linkProlongation.data.getCurrPortal(); if (latLng !== false){ window.plugin.linkProlongation.data.setPoint_2(latLng); window.plugin.linkProlongation.ui.updateInput(); } }; window.plugin.linkProlongation.data.invertPoint = function(){ var pp = window.plugin.linkProlongation.data.getPoints(); var p1 = pp.p1; var p2 = pp.p2; window.plugin.linkProlongation.data.setPoints(p2, p1); window.plugin.linkProlongation.ui.updateInput(); }; window.plugin.linkProlongation.data.calcP3 = function(p1, p2, dist){ if (p1 === undefined || p2 === undefined){ window.plugin.linkProlongation.dialog.openBoxMessage(' Error. Select two portals.'); return false; } if (p1.equals(p2) === true){ window.plugin.linkProlongation.dialog.openBoxMessage(' Error. Insert two different portals LatLng.'); return false; } var vinInv = window.plugin.linkProlongation.thirdParty.vincenty_inverse(p1, p2); var newDist = vinInv.distance+dist; var initBear = vinInv.initialBearing; var finalBear = vinInv.finalBearing; var vinDir1 = window.plugin.linkProlongation.thirdParty.vincenty_direct(p1, initBear, newDist, true); var p3 = new L.LatLng(vinDir1.lat, vinDir1.lng); return p3; }; window.plugin.linkProlongation.data.getP1_P2_P3_P0 = function(){ var point = window.plugin.linkProlongation.data.getPoints(); var dist = window.plugin.linkProlongation.data.getDist()*1000; // delete window.plugin.linkProlongation.obj.points['p3']; try { var p1 = point.p1; var p2 = point.p2; if (p1.equals(p2) === true){ window.plugin.linkProlongation.dialog.openBoxMessage(' Error. Insert two different portals LatLng.'); return false; } var p0 = window.plugin.linkProlongation.data.calcP3(p2, p1, dist); var p3 = window.plugin.linkProlongation.data.calcP3(p1, p2, dist); // window.plugin.linkProlongation.obj.points['p3'] = p3; return {p1:p1, p2:p2, p3:p3, p0:p0}; } catch (error) { window.plugin.linkProlongation.dialog.openBoxMessage(' Error.'); return false; } }; // ----------------------------------- // ACTION // ----------------------------------- window.plugin.linkProlongation.action.setDrawType = function(number){ window.plugin.linkProlongation.data.setDrawType(number); window.plugin.linkProlongation.storage.save(); }; window.plugin.linkProlongation.action.setDrawColorStatus = function(status){ window.plugin.linkProlongation.data.setDrawColorStatus(status); window.plugin.linkProlongation.storage.save(); }; window.plugin.linkProlongation.action.setDrawColorValue = function(color){ window.plugin.linkProlongation.data.setDrawColorValue(color); window.plugin.linkProlongation.storage.save(); }; window.plugin.linkProlongation.action.drawFavoriteType = function(){ if (window.plugin.drawToolsPlus === undefined){ window.plugin.linkProlongation.dialog.openBoxMessage( ' Draw Tools and Draw Tools Plus plugins needed to draw.' ); return false; } window.plugin.linkProlongation.layer.drawFavoriteType(); window.plugin.linkProlongation.layer.clearPreview(); }; window.plugin.linkProlongation.action.drawPreview = function(){ window.plugin.linkProlongation.layer.clearPreview(); window.plugin.linkProlongation.layer.drawPreview(); }; // ----------------------------------- // LAYER // ----------------------------------- window.plugin.linkProlongation.layer.drawFavoriteType = function(){ if (window.plugin.drawToolsPlus === undefined){ window.plugin.linkProlongation.dialog.openBoxMessage( ' Draw Tools and Draw Tools Plus plugins needed to draw.' ); return false; } var pp = window.plugin.linkProlongation.data.getP1_P2_P3_P0(); if (pp === false) { return false; } var drawType = window.plugin.linkProlongation.data.getDrawType(); var drawColor = window.plugin.linkProlongation.data.getDrawColor(); var color = undefined; if (drawColor.enabled){ color = drawColor.value; } switch (drawType){ case 1: window.plugin.drawToolsPlus.drawPolyline([pp.p0, pp.p1], color); break; case 2: window.plugin.drawToolsPlus.drawPolyline([pp.p1, pp.p2], color); break; case 3: window.plugin.drawToolsPlus.drawPolyline([pp.p2, pp.p3], color); break; case 4: window.plugin.drawToolsPlus.drawPolyline([pp.p0, pp.p2], color); break; case 5: window.plugin.drawToolsPlus.drawPolyline([pp.p1, pp.p3], color); break; // ------------------------------- case 6: window.plugin.drawToolsPlus.drawPolyline([pp.p0, pp.p1, pp.p2]); break; case 7: window.plugin.drawToolsPlus.drawPolyline([pp.p1, pp.p2, pp.p3]); break; case 8: window.plugin.drawToolsPlus.drawPolyline([pp.p0, pp.p1, pp.p2, pp.p3]); break; // ------------------------------- case 9: window.plugin.drawToolsPlus.drawPolyline([pp.p0, pp.p1], color); window.plugin.drawToolsPlus.drawPolyline([pp.p2, pp.p3], color); break; case 10: window.plugin.drawToolsPlus.drawPolyline([pp.p0, pp.p1], color); window.plugin.drawToolsPlus.drawPolyline([pp.p2, pp.p3]); break; case 11: window.plugin.drawToolsPlus.drawPolyline([pp.p1, pp.p2]); window.plugin.drawToolsPlus.drawPolyline([pp.p2, pp.p3], color); break; case 12: window.plugin.drawToolsPlus.drawPolyline([pp.p0, pp.p1], color); window.plugin.drawToolsPlus.drawPolyline([pp.p1, pp.p2]); window.plugin.drawToolsPlus.drawPolyline([pp.p2, pp.p3], color); break; default: break; } }; window.plugin.linkProlongation.layer.clearPreview = function(){ window.plugin.linkProlongation.layer.layerGroup.clearLayers(); }; window.plugin.linkProlongation.layer.drawPreview = function(){ var pp = window.plugin.linkProlongation.data.getP1_P2_P3_P0(); if (pp === false){ return false; } var drawColor = window.plugin.linkProlongation.data.getDrawColor(); var opt = { color: drawColor.value, weight: 3 }; window.plugin.linkProlongation.layer.layerGroup.addLayer(new L.geodesicPolyline([pp.p1,pp.p2], opt)); opt.dashArray = [7,10,2,10]; window.plugin.linkProlongation.layer.layerGroup.addLayer(new L.geodesicPolyline([pp.p0,pp.p1], opt)); window.plugin.linkProlongation.layer.layerGroup.addLayer(new L.geodesicPolyline([pp.p2,pp.p3], opt)); }; // ----------------------------------- // BOOKMARKS // ----------------------------------- window.plugin.linkProlongation.dialog.openBookmarkChooser = function(point_number){ if (!window.plugin.linkProlongation.dialog.noBookmarksPlugin()){ return false; } if (parseInt(point_number) !== 1 && parseInt(point_number) !== 2){ return false; } var html = window.plugin.linkProlongation.bookmarks.getHTMLPortalsList(); dialog ({ html: '
'+html+'
', dialogClass: 'ui-dialog-linkProlongation bookmarks noFirst', title: 'linkProlongation - Bookmarks', buttons: { 'SELECT & CLOSE': function(){ var ll = window.plugin.linkProlongation.obj.supp; window.plugin.linkProlongation.data.setPoint(point_number, ll); window.plugin.linkProlongation.obj.supp = {}; window.plugin.linkProlongation.ui.updateInput(); $(this).dialog('close'); }, 'SELECT': function(){ var ll = window.plugin.linkProlongation.obj.supp; window.plugin.linkProlongation.data.setPoint(point_number, ll); window.plugin.linkProlongation.obj.supp = {}; window.plugin.linkProlongation.ui.updateInput(); }, } }); }; window.plugin.linkProlongation.bookmarks.clickOnListItem = function(elem){ var bk = $(elem); var list = bk.parent().parent().parent().find('.bkmrk.selected'); list.each(function(){ $(this).removeClass('selected'); }); bk.toggleClass('selected'); var idBkmrk = bk.prop('id'); var idFolder = bk.parent().parent().prop('id'); var bkmrk = window.plugin.bookmarks.bkmrksObj.portals[idFolder].bkmrk[idBkmrk]; var latlng = bkmrk.latlng; var ll = latlng.split(','); ll = new L.latLng(ll[0], ll[1]); window.plugin.linkProlongation.obj.supp = ll; return ll; }; window.plugin.linkProlongation.bookmarks.getHTMLPortalsList = function(){ if (!window.plugin.linkProlongation.dialog.noBookmarksPlugin()){ return false; } // var portalsList = JSON.parse(window.plugin.bookmarks.KEY_STORAGE); var portalsList = window.plugin.bookmarks.bkmrksObj; var element = ''; var elementTemp = ''; var elemGenericFolder = ''; // For each folder var list = portalsList.portals; for (var idFolders in list) { var folders = list[idFolders]; // Create a label and a anchor for the sortable var folderLabel = ''+folders['label']+''; // Create a folder elementTemp = '
'+folderLabel+'
'; // For each bookmark var fold = folders['bkmrk']; for (var idBkmrk in fold) { var bkmrk = fold[idBkmrk]; var label = bkmrk['label']; var latlng = bkmrk['latlng']; // Create the bookmark elementTemp += ''+label+''; } elementTemp += '
'; if (idFolders !== window.plugin.bookmarks.KEY_OTHER_BKMRK) { element += elementTemp; } else { elemGenericFolder += elementTemp; } } element += elemGenericFolder; // Append all folders and bookmarks var r = '' + '
' + '

You must select 1 portal!

' // + '
' + element // + '
' + '
'; return r; }; window.plugin.linkProlongation.dialog.noBookmarksPlugin = function(){ if (window.plugin.bookmarks === undefined){ window.plugin.linkProlongation.dialog.openBoxMessage( ' Bookmarks for Maps and Portals plugin needed for this features.' ); return false; } return true; }; // ----------------------------------- // DIALOG // ----------------------------------- window.plugin.linkProlongation.dialog.getHtmlMainBox = function(){ var html = ''; var dist = window.plugin.linkProlongation.data.getDist(); var points = window.plugin.linkProlongation.data.getPoints(); html += ''; // html += '
'; html += '
'; // html += '
'; html += ''; // html += ''; html += ''; html += '
'; html += '
'; // html += '
'; html += ''; // html += ''; html += ''; html += '
'; // html += '
'; // html += '
'; return html; }; window.plugin.linkProlongation.dialog.openMainBox = function(){ var html = window.plugin.linkProlongation.dialog.getHtmlMainBox(); if ($('.ui-dialog-linkProlongation.linkProlongation.main').length > 0){ return false; } dialog ({ title: 'Link Prolongation', html: '
'+html+'
', width: 300, dialogClass: 'ui-dialog-linkProlongation linkProlongation main noFirst', buttons:{ 'Settings': function(){ window.plugin.linkProlongation.dialog.openSettingsBox(); }, 'PREVIEW': function(){ window.plugin.linkProlongation.action.drawPreview(); }, 'DRAW': function(){ window.plugin.linkProlongation.action.drawFavoriteType(); }, } }); }; window.plugin.linkProlongation.dialog.getHtmlSettingsBox = function(){ var html = ''; // var currDrawType = window.plugin.linkProlongation.data.getDrawType(); var currDrawColor = window.plugin.linkProlongation.data.getDrawColor(); var dist = window.plugin.linkProlongation.data.getDist(); var e1 = 'a'; var p1 = 'A'; var p2 = 'B'; var e2 = 'b'; function getHtmlLabel(arr, val){ var txt = ''; var check = (currDrawType === val)? 'checked' : ''; txt += ''; return txt; } // Legends if (1 === 1){ html += '
'; html += ''; html += ''; html += ''; html += ''; html += ''; html += ''; html += ''; html += ''; html += ''; html += '
'+e1+''+p1+' (P1)'+p2+' (P2)'+e2+'
distdist
'; html += '
'; } // Set Dist if (1 === 1){ html += '
'; html += ''; html += ''; html += ''; html += '
'; html += '
'; } // Draw types if (1 === 1){ html += '
'; html += '

Draw types

'; html += '
'; // html += 'Single polyline:'; html += getHtmlLabel([e1+p1], 1); html += getHtmlLabel([p1+p2], 2); html += getHtmlLabel([p2+e2], 3); html += getHtmlLabel([e1+p2], 4); html += getHtmlLabel([p1+e2], 5); html += '
'; html += '
'; // html += 'Polyline chain:'; html += getHtmlLabel([e1+p1+p2], 6); html += getHtmlLabel([p1+p2+e2], 7); html += getHtmlLabel([e1+p1+p2+e2], 8); html += '
'; html += '
'; // html += 'Polylines:'; html += getHtmlLabel([e1+p1, p2+e2], 9); html += getHtmlLabel([e1+p1, p1+p2], 10); html += getHtmlLabel([p1+p2, p2+e2], 11); html += getHtmlLabel([e1+p1, p1+p2, p2+e2], 12); html += '
'; html += '
'; html += '
'; } // html += '
'; // Custom color if (1 === 1){ html += '
'; html += '

Custom Draw Color

'; html += ''; html += ' '; html += 'NB: for the latest 4 draw types the custom draw color will be applicated only for '; html += 'aA and Bb.'; html += '
'; } return html; }; window.plugin.linkProlongation.dialog.openSettingsBox = function(){ var html = window.plugin.linkProlongation.dialog.getHtmlSettingsBox(); if ($('.ui-dialog-linkProlongation.linkProlongation.settings').length > 0){ return false; } dialog ({ title: 'Link Prolongation - Settings', html: '
'+html+'
', width: 310, dialogClass: 'ui-dialog-linkProlongation linkProlongation settings' }); }; window.plugin.linkProlongation.dialog.openBoxMessage = function(html){ dialog ({ title: 'Link Prolongation - Message', html: '
'+html+'
', dialogClass: 'ui-dialog-linkProlongation linkProlongation message' }); }; // ----------------------------------- // THIRD PARTY CODE // ----------------------------------- window.plugin.linkProlongation.thirdParty = function(){ // From Leaflet.Geodesic (https://github.com/henrythasler/Leaflet.Geodesic/) /* *Extend Number object with method to convert numeric degrees to radians */ if (typeof Number.prototype.toRadians === 'undefined'){ Number.prototype.toRadians = function(){ return this * Math.PI / 180; }; } /** Extend Number object with method to convert radians to numeric (signed) degrees */ if (typeof Number.prototype.toDegrees === 'undefined'){ Number.prototype.toDegrees = function(){ return this * 180 / Math.PI; }; } // Vincenty inverse calculation and Vincenty direct calculation are based on the work of Chris Veness (https://github.com/chrisveness/geodesy) window.plugin.linkProlongation.thirdParty.vincenty_direct = function(p1, initialBearing, distance, wrap){ var vincenty_ellipsoid = { a: 6367000, b: 6367000, f: 0 }; // Sphere var φ1 = p1.lat.toRadians(), λ1 = p1.lng.toRadians(); var α1 = initialBearing.toRadians(); var s = distance; var a = vincenty_ellipsoid.a, b = vincenty_ellipsoid.b, f = vincenty_ellipsoid.f; var sinα1 = Math.sin(α1); var cosα1 = Math.cos(α1); var tanU1 = (1-f) * Math.tan(φ1), cosU1 = 1 / Math.sqrt((1 + tanU1*tanU1)), sinU1 = tanU1 * cosU1; var σ1 = Math.atan2(tanU1, cosα1); var sinα = cosU1 * sinα1; var cosSqα = 1 - sinα*sinα; var uSq = cosSqα * (a*a - b*b) / (b*b); var A = 1 + uSq/16384*(4096+uSq*(-768+uSq*(320-175*uSq))); var B = uSq/1024 * (256+uSq*(-128+uSq*(74-47*uSq))); var σ = s / (b*A), σʹ, iterations = 0; do { var cos2σM = Math.cos(2*σ1 + σ); var sinσ = Math.sin(σ); var cosσ = Math.cos(σ); var Δσ = B*sinσ*(cos2σM+B/4*(cosσ*(-1+2*cos2σM*cos2σM)- B/6*cos2σM*(-3+4*sinσ*sinσ)*(-3+4*cos2σM*cos2σM))); σʹ = σ; σ = s / (b*A) + Δσ; } while (Math.abs(σ-σʹ) > 1e-12 && ++iterations); var x = sinU1*sinσ - cosU1*cosσ*cosα1; var φ2 = Math.atan2(sinU1*cosσ + cosU1*sinσ*cosα1, (1-f)*Math.sqrt(sinα*sinα + x*x)); var λ = Math.atan2(sinσ*sinα1, cosU1*cosσ - sinU1*sinσ*cosα1); var C = f/16*cosSqα*(4+f*(4-3*cosSqα)); var L = λ - (1-C) * f * sinα * (σ + C*sinσ*(cos2σM+C*cosσ*(-1+2*cos2σM*cos2σM))); var λ2; if (wrap) { λ2 = (λ1+L+3*Math.PI)%(2*Math.PI) - Math.PI; // normalise to -180...+180 } else { λ2 = (λ1+L); // do not normalize } var revAz = Math.atan2(sinα, -x); return {lat: φ2.toDegrees(), lng: λ2.toDegrees(), finalBearing: revAz.toDegrees() }; }; window.plugin.linkProlongation.thirdParty.vincenty_inverse = function(p1, p2){ var vincenty_ellipsoid = { a: 6367000, b: 6367000, f: 0 }; // Sphere var φ1 = p1.lat.toRadians(), λ1 = p1.lng.toRadians(); var φ2 = p2.lat.toRadians(), λ2 = p2.lng.toRadians(); var a = vincenty_ellipsoid.a, b = vincenty_ellipsoid.b, f = vincenty_ellipsoid.f; var L = λ2 - λ1; var tanU1 = (1-f) * Math.tan(φ1), cosU1 = 1 / Math.sqrt((1 + tanU1*tanU1)), sinU1 = tanU1 * cosU1; var tanU2 = (1-f) * Math.tan(φ2), cosU2 = 1 / Math.sqrt((1 + tanU2*tanU2)), sinU2 = tanU2 * cosU2; var λ = L, λʹ, iterations = 0; do { var sinλ = Math.sin(λ), cosλ = Math.cos(λ); var sinSqσ = (cosU2*sinλ) * (cosU2*sinλ) + (cosU1*sinU2-sinU1*cosU2*cosλ) * (cosU1*sinU2-sinU1*cosU2*cosλ); var sinσ = Math.sqrt(sinSqσ); if (sinσ===0) return 0; // co-incident points var cosσ = sinU1*sinU2 + cosU1*cosU2*cosλ; var σ = Math.atan2(sinσ, cosσ); var sinα = cosU1 * cosU2 * sinλ / sinσ; var cosSqα = 1 - sinα*sinα; var cos2σM = cosσ - 2*sinU1*sinU2/cosSqα; if (isNaN(cos2σM)) cos2σM = 0; // equatorial line: cosSqα=0 (§6) var C = f/16*cosSqα*(4+f*(4-3*cosSqα)); λʹ = λ; λ = L + (1-C) * f * sinα * (σ + C*sinσ*(cos2σM+C*cosσ*(-1+2*cos2σM*cos2σM))); } while (Math.abs(λ-λʹ) > 1e-12 && ++iterations<100); if (iterations>=100) { console.log('Formula failed to converge. Altering target position.'); return this._vincenty_inverse(p1, {lat: p2.lat, lng:p2.lng-0.01}); // throw new Error('Formula failed to converge'); } var uSq = cosSqα * (a*a - b*b) / (b*b); var A = 1 + uSq/16384*(4096+uSq*(-768+uSq*(320-175*uSq))); var B = uSq/1024 * (256+uSq*(-128+uSq*(74-47*uSq))); var Δσ = B*sinσ*(cos2σM+B/4*(cosσ*(-1+2*cos2σM*cos2σM)- B/6*cos2σM*(-3+4*sinσ*sinσ)*(-3+4*cos2σM*cos2σM))); var s = b*A*(σ-Δσ); var fwdAz = Math.atan2(cosU2*sinλ, cosU1*sinU2-sinU1*cosU2*cosλ); var revAz = Math.atan2(cosU1*sinλ, -sinU1*cosU2+cosU1*sinU2*cosλ); s = Number(s.toFixed(3)); // round to 1mm precision return { distance: s, initialBearing: fwdAz.toDegrees(), finalBearing: revAz.toDegrees() }; }; // Thanks to TheSned // (https://github.com/TheSned/ingress-intel-total-conversion/blob/e23ea7bafb4610911b9a8772700e40b2e0836873/plugins/extend-poly-lines.user.js#L38) // vincenty_ellipsoid = { a: 6378137, b: 6356752.3142, f: 1/298.257223563 }; // WGS-84 // vincenty_ellipsoid = { a: 6367000, b: 6367000, f: 0 }; // Sphere }; // ----------------------------------- // UI // ----------------------------------- window.plugin.linkProlongation.ui.appendToToolbox = function(){ var fa = (window.plugin.faIcon === undefined)? '' : ''; var text = fa+'Link Prolongation'; $('#toolbox').append(''+text+''); }; window.plugin.linkProlongation.ui.updateInput = function(){ var dist = window.plugin.linkProlongation.data.getDist(); var pp = window.plugin.linkProlongation.data.getPoints(); $('.linkProlongationDialog input.p1.lat').val(pp.p1.lat); $('.linkProlongationDialog input.p1.lng').val(pp.p1.lng); $('.linkProlongationDialog input.p2.lat').val(pp.p2.lat); $('.linkProlongationDialog input.p2.lng').val(pp.p2.lng); $('.linkProlongationDialog input.dist.number').val(dist); }; window.plugin.linkProlongation.ui.setupCSS = function(){ $('