// ==UserScript== // @author DanielOnDiordna // @name Quick Draw Links // @category Layer // @version 0.0.9.20210724.002500 // @updateURL https://raw.githubusercontent.com/IITC-CE/Community-plugins/master/dist/DanielOnDiordna/quick-draw-links.meta.js // @downloadURL https://raw.githubusercontent.com/IITC-CE/Community-plugins/master/dist/DanielOnDiordna/quick-draw-links.user.js // @description [danielondiordna-0.0.9.20210724.002500] Quickly draw and move links from portal to portal on the map. Show crosslinks, for links on the map, as well as for drawn links. Store/Restore your projects. Added great circle support. Added fields layer. Export list of used portals with link count. Integrated Spectrum Colorpicker 1.8.1 // @id quick-draw-links@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.quickdrawlinks = function() {}; var self = window.plugin.quickdrawlinks; self.id = 'quickdrawlinks'; self.title = 'Quick Draw Links'; self.version = '0.0.9.20210724.002500'; self.author = 'DanielOnDiordna'; self.changelog = ` Changelog: version 0.0.1.20181101.224000 - added a new button to add bookmarks to portals connected to crosslinks version 0.0.2.20190530.121700 - closedialog fix for smartphone - fixed in/out direction for drawn links in portal overview version 0.0.3.20191120.220400 - open side pane fix for smartphone version 0.0.4.20200613.180700 - linkexists now checks both directions version 0.0.5.20201230.204600 - replaced L.geodesicPolyline by L.geoJson (added sourcecode https://github.com/springmeyer/arc.js) version 0.0.6.20210117.001400 - fixed color storage for setStyle compatibility issues with L.geoJson version 0.0.7.20210118.220700 - rewritten some code for placing the buttons; buttons now also visible on desktop above the status bar - updated plugin wrapper and userscript header formatting to match IITC-CE coding version 0.0.7.20210119.185800 - fixed button width for smartphone version 0.0.8.20210119.220700 - integrated Spectrum Colorpicker 1.8.1 plugin code, no need anymore for the separate plugin - removed drawtools integration version 0.0.9.20210127.225400 - removed icon button titles for mobile users - enabled toggle function for icon buttons - new active icon button indicators version 0.0.9.20210421.190200 - minor fix for IITC CE where runHooks iitcLoaded is executed before addHook is defined in this plugin version 0.0.9.20210724.002500 - prevent double plugin setup on hook iitcLoaded `; self.namespace = 'window.plugin.' + self.id + '.'; self.pluginname = 'plugin-' + self.id; self.panename = 'plugin-' + self.id; self.localstoragesettings = self.pluginname + '-settings'; self.localstoragelayer = self.pluginname + '-layer'; self.localstoragetitlecache = self.pluginname + '-titlecache'; self.localstoragedata = self.pluginname + '-data'; self.markerLayer = undefined; self.iconstyle = 'link'; self.startguid = undefined; self.startpos = undefined; self.multistartpos = undefined; self.guidpos = {}; self.titlecache = {}; self.isSmartphone = null; self.settings = {}; self.settings.hidebuttons = false; self.settings.drawcolor = '#E27000'; self.settings.greatcirclecolor = '#FF0000'; self.settings.fieldcolor = '#E27000'; self.settings.crosslinkbookmarkcolor = '#FF0000'; self.settings.showcrosslinks = 0; // 0 = Links, 1 = Drawn, 2 = Both self.settings.showlinkdirection = false; self.settings.fieldexistinglinks = false; self.requestid = undefined; self.selectedlink = undefined; self.movelinksposition = undefined; self.copylinksposition = undefined; self.linkstyle = [ '10,5,5,5,5,5,5,5,100%', ]; self.crosslinklayerdisabled = false; self.highlightlinkoptions = { color: "#C33", opacity: 1, weight: 5, fill: false, dashArray: "1,6", radius: 18, }; // 120 x 60 - 4 buttons, 2 rows: link, move, star, copy self.menuicons = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAHgAAAA8CAIAAAAiz+n/AAAAK3RFWHRDcmVhdGlvbiBUaW1lAGRvIDI4IGphbiAyMDIxIDE2OjQwOjQ0ICswMTAwig8y5gAAAAd0SU1FB+UBHA8wMrE/wUgAAAAJcEhZcwAAHsEAAB7BAcNpVFMAAAAEZ0FNQQAAsY8L/GEFAAAR+ElEQVR42u1cCXQUZba+1V29r1kIScgGhCAgmzhGUYZEUIGHkAAijgwDZOYNMGDAw7wBRked58w4B9+ocyKiwgQFFFQm+BRQ1BDEBYdozEZCyAYhCVm7O91Jr1X/+6srNEWnu6qSbjzv+XJPHam+/ddXX92+//2X+iLAsA3bj8mIH+xOStBLQMqe00A5oIcAqRK0EiDdYHeBHQB5CUnloJYCSYHHCTbW+SMwUnzTwjy5TqeLiIzs6bHe9ujVQd5GoQJDKsxmP9bCaQrcGoiKhQkaiG6D6l5Je/qY+y911VwynU+E2/QQ1wE1nVBvhx4KXEN+vA9j1mi1WkOEwWKzZlx4KYyBG7d9+6hRo1JHj26urz+xaVOogf7XPn10pF6nVWjUSpVKjT0URZWbrgyBWSLMWEPsZ8/3oBUm4vJSeD4F7sAZ7QBrLfr8vy/9LtIzNhcKRxG3YqcL+qrQybdgnRXaBnWjosQNUVqDSoEJq1RKFcMZ6K7WjtCDm/7KK4kTJxrj4nTR0bqICAnumi5XyZEjYq4NXDqKD6aMSY6OMKgAOYB2AnIBotmvuru7o+Z2DpYizuiJMH8dUcB+PA8f4VRtgfLZ8Bs9xPYjw+UOqL2Mvp1FrFOCDpiqgV5Cc2rhCxrcgrf4atLW+KgYvVwNbgo5PUBdrzkmizm15tkhx3fp3r23ZmZqRo/uBqaW4RrnYn48Jnamjo7jMTHiIhDIbn+0sfUkgNboDbSLCbS3VuJ0HkKUB1oKpL9GLHGARYtGZMJj16jID6ONZrgiR6rZxEZgnoSIgfGNcM4lItAzK5+vmfwHpJEiN/5dvIFGFMMZQShRxnYkJ0dWW2sEsHij7PZGmck7mj4pLsrYJMG+iLu/sa2lFrmtHpfF2Wfus3VZLe1k+uBKczBrh4sSJE1Hv2hC3/ucLagSj34ecNfBlz5nAkwhQSYSNq38j90t7XSf09PrdFr7WOfIqidDJ3woNfWCxdKFUA9CvQg5EHIidFIqFY/AV6NjF9i8fSX8dgV9fxlKcE3A9cTnbIcaF/QC030cQ0Yed/mvvvPOif8ZRs6njEbfOaFQSORyMiICJzXtcOBKDUhgdjSIWUcYjWY6XwBm6H/5ZI4gpHo9/pc0GBTx8arUVE9PT191tautDXk8dG8vz6USQfCUlJT8/HyTyYS8VlJSsnr16rDQjoqM0qjVvo9yhTwpMSksyCxn9jxsnCUS0mgcsWjR1JMnb//uu2mnTk3Yv3/y0aMzzp2bVlgYs2wZGRnJdzU/+LRp01iWxmsdB3vyvRYibYPesGPHDrlCcZ0KIZl+2/RVP18VIrKPM9cTOmcc5cTc3LQ9e/R33CGLjDQdP964ZUvX8eP4K/W4cakvvhizfDnJKS+DC3RBQQEOsdlsfuaZZzK9dvToUezHj7F58+ZQeN+Rnq5SqxB9vVZQNDNJuPOuO5Wc6A/BfJzZj2HhLFEqlUlJox57DJdm/NF+8WLj1q22999vWreuYubMirvvLp05s/PYMcpux4k/6EBjZrgPslyffvrpIq9lZ2ezvHNzcwM85E5dMDS85m6GMvbokbSo1cxSosN6xedsd9W3tjKzGo/M7nOaoIm9PDtS1OSBy5n1fF50mp9zXsIyMYGOW7WKGf28Zjp92m4ydTY0mJubH5kz5y+bN29bu9bT3s4MiTTtu2r67t2ifkbc13CBwwni58dPwtZr3CV9zgvvGpo/nWwrWRIQigCJFGQyULOHVmn4dc763E2buU68qDFqo97Y96ZBG6GU6nzOu6S/2P2ThsIdXYPljGcdviMg56Ixm0rn/qV500FBWBziia+/noEQe8Tm5ODQs18lJycPRM4wmx9qa3vWYrmOwIPOpkZpaamfv7GxkT3xFW7bmVEafTzIIqtrLweEwj80xRz9645X8vY88MADeXl52ENxFiNmm/PBRQunTZ+ak5NTXFyMPTvI70bGj0hOTKio+FxMoFnOs35f4Te3K1bndE1ili2fwtJoYCbvDTOeNSbFSqLUZfV1grC02+25cOHGJ2JMqtG09PZu2rZt6sSJBNG/zM6kaS1BRAPUcULHVzpOnz6N/7t48WI/f1ZWFnvy/fcM46sfyjRaHRAYiqirbxATjitXriQkJKxYsWIgckRExJQpU2pra/HHlwk6WTY9WpGAu8O54nNikDFnNqA+iz7vX3PYHDdEGNkNiOrqakFY5HbbcKCvTZYj7r2XLdayqKgR2dlHKit/++c/lzc0ECrVpCNHCG/I8VFdWSkq0Lgig3fI5o4hOIufeuopNsrsgCNTqIGQ46PP7l64WVQHF4mMH5CQgkQOdo9tf8tWMcgb37m+WsMhZqOc2vJfTQcXRlU+wW1JKGWEXNrrcvyqTrh0ILzSrKqy19f3U83MVMTFMbO95cvTXn01raBg0pdf4qFSqlTq09NxiDEJ2m7/+tFHr9/Od/bJrli92qNTudUKSko4Ehd7wDuCs/mLQ4OTxWAw+KZ6eLRh4+U4m6JQjwCp/mJDd9rCkoEsHx9VEGm9RWOPl3k0EpBuQIRI5F1SWmEg9GPhqqxs01dTByJ/mLpeS5NqSqJEpMRJxUZF+b7CYeVBbp75V9LcLk9OOI9sd38UYJjNeO89SEuDxETQ6UAqLSIIHNaE++4b8dRTmokT8crFXlfnsVg0t97KpDZC5qKiqrVrXVevzu7rkxEE5oHq6w+PHesDvF6jk1ImpI0xgscElLXH1IxLAnauWbMGU8y4ZmxLnG5btmxhGV8+KlcocR9U4uNCTeACfcv0MbHOW0zV4OgAytnf+wSRcd2QyghSg4dIKCv7V0DkhLHJKREjkc2FbE66/frIU/7yPRgxGPLXcb9RRxqJpBiJQVX26fsBkaeMHauePLmR2VMEl7dieMzm9q++sixZYpg3TzljhjotTaJW91VWOi5csBUXX8nPp/v67mxokBC4d4MGoKqi4oYOxP1wuTAjMVYLlBmoHqu5XZ/Rv4WEMwJXajYpcL9+6aWXfONh7RH12HGTQWrodSq0t30QrOvlZ1xSm5Jsl8FlZfbU2KTmR84j3CojqU0CxRhbVkHQWWPVgudjNZGox0E39VcttlzwIH+RuHHSnbdJDMpeOYrf9Wgw5A2NjZ7kZByCXu92XZF3rCNkssQJE5x2O+V0uhwOuUbTa7O5nE7Kih8MzaZpBUHgW0bb7bs4i17/QO/5vXLJoowIHcKBBqq3q7MlWsSmaEfR5Oi4tJq67vELTgVrs1z5t6wZOXSrvq8dKDuzue2LNY+9GmuLn6ZplZb9+7GpwdrkjcheNOs+HZKjPhc4Pd0t7WI2Ravn7YyflFrZXHfXoaCl/5bnnpu7bp3FYOhmFgFAIVQkEd6xmO9wJCoUVG3t3nHjuP4brvzlnxzvHztlsXpwwcVH1Ijkpg8MgtDfljaARF9V08zT5h3H4x8VvwsRNmUUSFWAhzhcGQSRG80VuhQou/I1T5uNHQUfnym0Ei5CryS0isik2O9HCw+bZ8tLcOOShiq+H2PbtsK33lJZrQbmhSdICSKDFuZsqa+PxdMq79xUwPL/oHBWLEPnF6KKjKYTonZ5ivbNENNsjfyNd2e6D4xBeyPQy6TwXj62p39aIKYZzuuOVW+YV+SbsveUTX1CzCVvz/utmGaTXntts8u1CqHFCN3rEvX2cvknn4hpxtjh50Y4K1eg6uzvDsaKvUacbYw+XDDXdXgq+psqDC/xuLZ3zMrOXx6wrD1YND43dDSu3XngwH84nb/GgW5tHTJI4KLz8LaOd/5Z6KINXZZB4glZXufDJ8r368a7zCpRSxvxllN/4N1Tx9w6WTeyhxf57MqVp//xjySnk24YOuegS/CfP9muM5a2tYaZNLbX2nIiGlStlCnsyOvr3taURHf1hbmvYPtm/fpRGo2pMwzvS4dt2IZt2MQbgYRe3w5bWEx4qTNsYbFByA18G9vgfbU8qNvga/3UpHZkkRAkV02KvKoz7PRTk4bS50LhHF5kgUBz4bhqUuwfFO+BalI5oTZAHFdNOn/c2ktdNSrQ+6lJB3svLmeumnSwOPzIXDWpGGSSH/H/kJrUxznsalIfckhq0oE/BcYVVJMONjVkhDIUNSmFBDYZMGdBNenQ0hkjC6pJh1g68GUYnUdNGnqxG6yaVBCQ5cyjJh0yZxaZR00qEpnkQb96XBsTbaAoB+V2UrQHR1k/xx2WIaVfTQpeNem1usdVk86GjawzAaZ8J05NynK+mPQ7g0ZHuzyU26PQMkKckVVPhsiZRc40m2V6vYcNsTfvTodFTcqi+6lJwzVw3yQ16TXO/RZGNakfMldNKlUqWTUpf3BIfvR+3Gv38J2EGPGbpyYdyNl3Hoa8lkh41KT8cw/hefTo0aPz8/OzsrK4799CnyoBqyZ1qOHa/iCjJo1Jqm0SVlmI5Aw7r7ABCgtnQioljcbohQvjN25UjR0r1WgYsRJCHqvVXlvb/Pe/d37wAc8tBAI9ffr0kpISI0ckySozZ8+eHSJvg96wbcfWf/7pW1+gWTXpzIw7iveHtM/p49y189lwccYXkpGRiY89lrh9O6sv6CoosJ45o5s71zBrFqsmlahU7YcOBUMg+dEbGhpYZSbOCPZdfW5uLs7u1atXl5aWDuQt/kl41KQV75z0+4s38X2fy5n1ZGZmhs45oJoUD7Xm995rMhhwSfF4PE6bjbLbceIjihqIHDTQuBFXmcmqv8Crd2G1L5j9iy++6BeLgp26YLxZNSl73iNpMXDUpKyz3VXv9KlJXf1OrppUMCJ+nD+FpeBVk/JzzktYJogcUE1qxb8lQaxfsmTq1Kld3d1PPvGET03KIk/fvfs6MgpicKMyk+vnKjN9Tq6adCAaj5rU10aMmhTxGohTk/oac9Wk/MgB1aTsV35qUhaZqya99nTBLaCalDvRYWs3/iioJqURxc0jrprU5/eA84dRk7L8w6ImJbVaXJq5alJmxh0WNSkG4qpJmXXN/1c1KbNiHIKalLjRcOZyNZ8+v6CaNODmydCQ/dSkgsgwVDWpMOehqkn9B0OM5acmZXkfPXoUZ9kLL7yA89pPmbllyxa2jGh0ESBR4Hg0t3YPzAXcwE9NKh6ZkBBSOZBqaGqrD4jMryY9VXSPH/KymM+uq0nra+TJCQ3IFhB5oJrU3tnZtn07qybFUZ587BirJmVaS6W9ZWVX33wTzzrwQgYnL56QmG4UgfCpSfHT4o7Mo/nEtxiamlQM8o9MTdofaDaD+tWkyKM3xvQU9U8D2TnTQGUm+5WLIr3prOp1SB58PEDdYJsxatLIJI8NaA+xi0JikBFQpIpURgKttb3e9qtgyKyalPkLDU7FgEzgQaZIAlQyQquwSan1dW8HQ97Q2KhPTu7DE3yc4LjcEURfc7NdJtMVFZlOnOgMpCaVjxxJeqOssNu/WryYi3zDrOPkZ2e9alLcktIZYzo/9fgism/fPi4PLqGOor7ouITmSwHqhs9OnD2SNSNHEaGn3IyadBeNxCC/qrCp4zStrnoe5M/OfcmoSdVySWIUqyb1Xc6DXD1pZ3xS6qVmvvlG4aFDc9etw5WH8qpJM2i6SCKhXS7uLoofPv5qvsNhVCiczf6ST5Lb1FttTmX/2z0GnR4X9KgRsqYP3IKT+W9LGx4YdXtVTXmwBiyyslh//60PKymto4v5/8+8TNGCyI3mivEp6R9//TU/suKM9IFZ9+r0SrC7GTWpe6sg8tnykqXpk3jUpCyyRK+/Z+VKg455C+HwqkkFkS319T+ZMOGbAWpSciA6wBc/W/6gXCoDggTS4N3vDmrsJUWGsqzNNTwM+n/FctmC238mkZJOEzisFHhAEFlxviCvdJ0gct4Z6uH5WTIpATKpRK8EEIH8zagN3+wVRCZI8r7Vq0mZDEfB6vGIQU745JOPH3nED5kM2FStPp314Bw5KevoFhaq9pdUoe2C/ilKjXLOtGxZh8xSYwZPOJFVX2gW3ztPppZ3O6zh5axTq3/60EMqudzaJfynUMGQg77K2u8uXL50gUg1qciNpP7xs3xv1uyV5qYGECGgHBwyQT20MFukmnRQyMhqXbRmzQVxatKAyHyvsm6GmpRFvhlq0v4ecxPUpCxyiGpSvu4TlhcTw8j9l98MTsM20Ia1dz+Q/Q+qIizhHnrpFQAAAABJRU5ErkJggg=="; // 350 x 50 - 7 buttons: trash, move, swap, zoom, list, confirm, cancel self.linkmenubuttonsicon = "data:image/png;base64,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"; self.restoresettings = function() { if (typeof localStorage[self.localstoragesettings] === 'string') { var settings = JSON.parse(localStorage[self.localstoragesettings]); if (typeof settings === 'object' && settings instanceof Object) { if (typeof settings.hidebuttons === 'boolean') self.settings.hidebuttons = settings.hidebuttons; if (typeof settings.drawcolor === 'string') self.settings.drawcolor = settings.drawcolor; if (typeof settings.greatcirclecolor === 'string') self.settings.greatcirclecolor = settings.greatcirclecolor; if (typeof settings.fieldcolor === 'string') self.settings.fieldcolor = settings.fieldcolor; if (typeof settings.crosslinkbookmarkcolor === 'string') self.settings.crosslinkbookmarkcolor = settings.crosslinkbookmarkcolor; if (typeof settings.showcrosslinks === 'number') self.settings.showcrosslinks = settings.showcrosslinks; if (typeof settings.showlinkdirection === 'boolean') self.settings.showlinkdirection = settings.showlinkdirection; if (typeof settings.fieldexistinglinks === 'boolean') self.settings.fieldexistinglinks = settings.fieldexistinglinks; } } }; self.storesettings = function() { localStorage[self.localstoragesettings] = JSON.stringify(self.settings); }; self.linkicon = function(zoom,iconstyle) { if (iconstyle instanceof Object) iconstyle = iconstyle.icon; if (!iconstyle) iconstyle = self.iconstyle; var icon; switch(iconstyle) { case 'link': icon = 'data:image/png;base64,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'; break; case 'move': icon = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGAAAABgCAYAAADimHc4AAAAB3RJTUUH4QIJCSgUXlI/NQAAAAlwSFlzAAAewQAAHsEBw2lUUwAAAARnQU1BAACxjwv8YQUAABSsSURBVHja7V0JlFTVmf7uq7WrqzegWYQAAiogiwuuk2QkIHBQEyQj0Zy4RycaNSeJ45xjRjPOHE4SHeMxCNHoQEYzKqKggyCrG4KIqBBkEUWanYam16rq2u98f9Wr6uruarqrqKahrb/P7Xqv3n3v3fsv3///9913C8hTnvKUpzx9U0l1dQM6m+YobeOHg8Vq9lezGCxRFu/dWoW6sn3WrmZQLonMtiDO3EIWYbydZZhC5Ls21dDfBk+RTXlcDtQWa1jWHole9CiP5wWQDZHZ0nZhuBtxRotmi6aPsKu6qwyE+hgIu0JwF4W1yxbUJQiilLU0bMobDmn3x+Y5XUqnhQBMGBFmFyGu2aLlwuwxVjROMlSw3IKgk5ruDOpiKcbx0DWqrQf58Tm6WPuFTjkBmJotGi0wIkxW5uf51OpJLOUWFeTxqCOsCx1hOA3ogg5f34JQKALnUm5WEf+DXd3fLhVACoyUmG2RbSfLWMLHZGG2oUIOarY9RGZHYTNYQEjJ6n68po7Avpiby1i6nPnASRQAmW2Y9xPNFiYr8/M8MnhiDLNViOocFmYXRGEns1k9A+1ujwwE63jNN7lZT+0PnKy+H486RQBmNGI3izBe9oWTo1gmWuDvTxgpIOMLqM0uaqVBWEBEOzuto4rIH4ZrJTcPk/n+TrtRhtRZFmBQo6+wqMDlCrrMQKCUml1M51gSRoGts5mdnnQN/61gaTzJNz4udRoEEasPFuLQKI8e0EfHgpeuI2p/lHG/ON4Dpwr0JMjopOtKlrmzQQ+czQSotivDbTKfdzfE6S5kOWWgJ0GdIgBqWQTxKGM9E6A/2uDbyng92jUdDNbzYxFLzamE/Qnq1LEgM8yUxKkn4s73ZuK/4+R1Lsq813iVm/PI/NqTdd/M2hinMpZvpTlezbKfRQLvgSxnmduSRX6J1rF0X5aRLOVmnd0sPjOTlfDzakY+txKPW8WWPtRajuJLxz58VmBHYbQfRvr7YoTfBmcz/GrAUWsVdtlbd8RAf4xm/YJogvkheI4t0g8/twWLD1Xh65bYv4+lpsV3VrPtI8z2SvsPmXzoFByNCcCJolsVLE+UoG+ykX402ELwL/bi2Fwnin9ngXWkA+5oFBHF7w0rHB/W4eDdrPo1i6UQPe80YHmQ2yVkQjgAb8QK2/ZG1N8bhHdzihCmkDnT2J8BEWjLViwr+kTP73MIWwsbUWfluUr6yvurb+F83wXqusOjMLXGhR4Ca9iG5UV/1TcOL8UAmpY9yZQgg5sfqId3jMU1lfyygXnE6qf1D4wKfPTvLpSFE/XCCBlstzWExqu5uzahOKxzkwHr1VGEh7BvDivsFraH0Zylhrz52IeaOTy2hnUjORcAmXfXMHxn5s3q+bLEgfX4H//r+oF91KyC0bim+Hvql8XlGBo7dhS78J5+qn47Vm6vx+E7eP4dpeh/7ST1r2cMwWUG96la+7BOz/V/igVH2Ykbj6HivZSh4fIwAtNW6//6+Ro8O2gwLjHGqO+T4eepMhpiBKHYPfZgg/5Qz406Ueq7Qf35yz44JyAWMEtfOWqC+rVzLKYlIfQzvBrapBcuvEst/jeTSVW/1Wc/+R31zzO+jTsLE/U2Y1HkNf3rXR5UXWpawJAilM93omTEJepG61B820HLoynYeXA/y169Vb/l+RxLPREE59Xh0O8hhpgjSoahFt7QmRIu0vTJBv/QS3FLw3XqyeLUY8Lg/uqZ4v/W15+9A6uWlmOY6zb1YlkvDEkypB/OxbXqUSc70+9t/cR/8quJ5thLiIIIztRjlB3unreplywDcaGyojmqyD0G42J1rppqWar/w7VEPzTwBjV7cxF6VRWjn2+7XjHuYvWTQmWiKOva3secwbxHRUzRadhUmDFULFei7Zp/2/RyrwW2V03m9y1Gn/89D9OHTVG/KaQStWrDAIxVo9TVRRPwtXuBvu/WCML9PDj6M+RoKKPNKIgCcVBzImR+iTNNHE+owHT1WOk43NCDzO+Ryvymi1twIWZY3Sgf0QfDxye+J5OGUbh3zVB/KqTFJJnvRz2+wNuoxYHkNXhdzFCzLAE02mfraVtptA/QBzz2Fd4P0PqS9croohwo6muHq3dCB2SbQki2S+pXYEOA2i8hKZ1Z2a20uOHT1O9LE8wXIR3FV6DfiFmhkAhZrnOteqyfA4VTCVWX54L5xxXACFwpN7SFyRaaHz3R1ljjUqk3zlbXq9kuweNd+CBWwi0Ug/6DOnStm52YYn5lp8k/NBJTyobgcluiHp0k/qJ/6H1e3+x5Wk/zfIHVkaZrFGGiur+IjP8hhec+gi/XBeGr2o0Pm9Vhe9y9MPRi2S9EjwsoFBt9R7ItUp/Yf5AQt527/QmvdxLK3NJGIfo30IqCc/TVDS/rn9fO0pMaduLd5JA1AwNMUL9yUHD3I0dJbJsCKOHtfHT+z+jp9S/pn9XO1lc1rMe8cOuaCsRU77P6Os88/ZOal/Xd1WROsxq9MNggfp5j7p5FpzZhvLqvZwI+6NywSD9QR8a+TNw9twFHbp+v791NS0hK/ExayiBc7KY13cndGirDqs/0a95UpRiqLi+k458g27TQiYPURa7EPaSe1OemjIb6KLAryzCghA4+yUgKSK/Uj1ZSSNPpw4bVo3LmAv2LWkJO8h7n4HultNixiEdLnScAafBK/VhVPQ7NY2OGN6L2V0v0I/V0ps3qMcqI/B1vVHhRNbkaey6iT9ibqr1CPXGmndgpYWwhte38vhiu6FCTx8Vh78dmD89/hLt76fgWECbe34C/VSfqCEyNUlMdhLUhss8I5VVaXDgVhuhLnFSIcYg9uFHjhuHbSecr9Rji+ujE55sCGk/LtCS0X+hTvaCW0dv71dh7hLv9Gb2tZ38ju7Fep/QFtHgXLSwnMNSmAAJ09OxggDg4h7uV7PArZGK14HQq7cVGvwFjHY+t4+4uhnCv79TvtBrwotnHRkbpAMtKcIbFSLHgKuzWtJo6xOPtmPw1ou99pdfoVA3vjbMIWXqwCJJWs6klDIkfIEP78dpj2Y6+fTHSkjgm9WiFu1h2xDtu7dEDA5Oen1EZ/zx2MnYyFWSlWV4hXLookGQj6FcIaoNoVqo3ckBt4lgtDsKLaskLjiWYgjTJyB69MeiHZ0tin3nDkRocaPNRnw2OSwapcc1CHjNJ+jp2etN1toj2kSnJ6IzwYyF8ORFXHEYxagVh5SZqcrFATcIP7MemGwkvliITJQTiPtIveCjM1xCPkBg9GYMYoSWzcgoMEhREES1s2WYLbM32+6mRlq/12jGdKgBqijilxJgO2q4XcwthdJDYwVZZLDNfRQYmHj/GhMxooxdhx2qkGCmxOELGiaXEBExY/CujoevpnMnqYbE64gc+0s/PGIDzkLAygU3Cj58Qt9i8lEELc5LpyQhJLE2ir1AHxuu0jjhYv1+nCqCziFHSht16/QSyOqlpNHVygj42PsOhId4w53Bqs2FvqiZQFSLjJEaNcUmimRAC+xipMEkcFhOs+AHxF+eo8UnJ8XiQyrQTSDowgcgjldgxmNm2LS6ACBbrh3y7sC7ohLsdpRP5692npQDoQz47hO1hP/mcgJZiioAxfDFNfSg7t4lf2amdk8eoa5pZywG9OcDj+1K+8pFxr3+qXznzMnWLXTRe/MDZuMLRH2Ni2i3ws0kv8nLneTRZapj7FYz3z0d8sDBmLSPU5IYdevWKWuy/D01DDjK+lQ7vxRKTFpstddbzgDaJDNlGKPEexvZkw0UADAeLClDyIp3cnYw05tIqzh+Na5LqL/DAfCTKyGRV6vXop948iM/DiehMhMoMWTFbju3L92S0l+HtktTzaIlvbtFL/KkhM9vQ241e/0hfc784cvqRB5moLWfEtpDtiRXZJjyuZFtvygU/umJWxGEKYdUyPfOffqoWlAlciAOdqn5bqLQxZCNefmIgLpBhDFdqiPgxXvTTeirIsHdaXG8H/VAzGBqLa2NZuJDAD61ks9w39SSGue/S4o4cxJaiwbgkZgVUBMU2DXhTP3wXfca9PTHIOlk9WCDZemLgWBLSF/Rt+xg4PIMcjJCedAuQRrPzM/fikwNr8RfB5tiXornT1B8c/6I+dN2qXnTJEESCjpCNa/SfG5kgPQ60iIObYMgXNREmwfwE/NDxz0/DLCpCZNZi/XCtL2VUuj9GG7er+T3uV2uL71HLXSMxWYkiSPskIVuqH/HQOl5k2LorF8zoCgEI7WG08ctV+vGaFfhDKMEAsQQZk0kdG9qM16PP6R8dYYK0hGVRuou1hKEEyX41KuqOYffKdOcx436OeP/u8/qWWiaCyaEWub+0w4b4xAGBqQpswFz94/pK7FxDq5mZK0YkIYghneUA/p48UIkv4jxpTsr8HqnntbxoR67FOH8VOziZ2eej27Hi0hFqkqscQ62MfIj3+8EkLMiMOshEkI43/AKxX7JkH9JTDIY+w8KSczElGbRvxbIQtXU9WsBPCgUPYdvtVICnZ+lJ3z8P040h6jKXtEHyArE83j+8Rf9f4DB2yLOQT5iX3IUcDkfHmMK0fDqd36O8aepzW0WT/5KNuw7xBKnQhbIFrCtPxZLmTFM0qCEPsO7CDK6VSvah+Icp1O7rfagey+iojBASIj5/wWt/QO2W5Gkb2sFbwsQ9vOcveF6yHkPWML+7h/H/2+3wwcV2T3HAfTvPGU3osvPTwnP9Ftj3sR0bqTDSv0/RGgJPXACIW0K6KWjRFgyTqCQdbDWiKcTr6LXS8zEe9omGZfoYsK37ejK4TuIavRB/cCSTeH3IINHMU57ylKc85SlPecpTnvKUpzzlKU/Ho26/VMGpRub8WBnyKL5bq8quGo7+RlLKDPErFfRvtFs5T7kXtbsbkemJt0Vl5rk8BL9MITJDw5DtH+UF0ElkMl5GVOWB/hVSrGgsj8LqlhfOzWp78gLIMaUwvg/LeGr7ZH721bAY4daj5R/mBZADMrFdGC9TsUtZLrXAfxU1vZcwPt058uqs8uhAXgAnSGS+qLXAzHcBPcmCYB8yvTACZzsRZnSP/M8L4ATIfAu03ED4EQ01lIy3RND+S6CsH6UvWBLfztOJkHD7Ump+jPkdPclAQB6TfhLfztOJkMwcm0ys7zDzhaJwyCyN2MyKvACyJNPxltDZZjRL2kBI4EcWDamN7+cpWxL8H6dhLc7kJANB0XyBn1B8P0/ZUpmCnkBtzoiHUdjlzfvaxHKZeQFkQSb8lBFO+mZyHkPUCP2FrNySnNxlNS8oE5FEGCrlMxHHttxPnRMvUhQsC3X1AqgnmST6uTAKS0YLIRkqUB/Rdpldl5zoZdVuNXqe7+i9EVl9EMqutbLEQyoljOa2EgEkijKNRgQgM92qzTXYVlOIsgrtN2UGmSxAemVbWW5bFNFOebmkLlVZxQLOCuieo3R2aNRbFt2gXEQgYlrdXgBN0U+odwT2Dp9nQSDMJG054lMlk2Qoj15YoCqPncC7BvJe1zUs5WZm2N1J4OeiqCwQkwFZVKNAtbx+1ez9s5ja+3S/x53qWF12QpCVeoJnc2Mii9vUkO5MAj8TMoMfjbAurEAcfpq9xJ64yEd+3euPTlXd3uzltCRrfFrReDM3ZQ2ewu5qCanwk8l5VvhDjH7eQprZ4QkBiFms9euejztUTVbLOspylFb47uDmVYgLoTtagsDPJVnAj0y134o0PjImgJRF9tYEdNkTDlWd1dKOYbhsTM1FCOITuqMl9MgSfuQd5fqW8COUvJAZQsoL0G8HdI8nbcqTVUTDcNZOIdzOzanoRpZgPukqJfxktEqKTXmDjH5k7CctvDeTZIolrAvrgg0K2a00KSsjMuv7KeI+wdVNLEEEcDHhJ6OVw23wVvJD1idKq9CtTMm0hBqambybtS/b8JQxspNCuA3dxyfI48bxmcCPKHBQFwvzPeRrWka2dTERwiYmZ7MNhPdlbwn2Ap4vcNQdLIHQGuiV0QmqtpHBiSRfbQY2aQVgSkv8wccMn2YrCiFbS4jCKkLoFpZgqFBGD17saJCFReSF7jb9aZvmJEJgEcltYJz/FKV/8ESEoBARIZzOlhCiX6zuaGX5sQg/esoSCd624Cder32SkHQjHetTTCgOZysEYqeLUCbJ2hUATkcrOMY+vEXGdmjUt0Ad8Ya0ezXi/GuT2hVAiiV8RDz7EzPerMeNNJSbjnka4nNnTjeS6HAxrfkN0e7jVRSfSfwX6NnT3ghxJkOg4hMoBNcsZrxZLkmv4FDHzsRpaAHmorPyIGWxgeCx49UlXEd9+oyN6MCPRXRYACmOeU0YhYQjX1brJdCMY88ZTibzckWmECqphG/Sp6UNDUX7i4yKmoAu/QAd+JmsjB4CmEIQTHuHQqAleDMevGNoGsn0vqcYSf9Xk9GH0h1U0kPUSex/IN3QQ0vKmBG8aBRNQniKmV5Gg3daHrKdxmT+BEolw/OFhJoWa8tpYn99tF4PWY92nG+CstJEUwgCR6tDcSF0ePBO69NbACYJ45dH4PgboTgJRXTOKDO2HW7UveVnEju0zH3WUGCal2j/Sgphjk15OriauNElP2WSSzJ9gfjAFTbVUKFivNawKh8cqJOnXlWmkrbPjRNsiNxZLGE5Y97ZFEK7Tkd3AwGYfZe+VjXqPi8x5m9keA2Xqgwdi46RH4Xo8NL2J+wMzThX1tRZRiE8KyHY8epHYZHj3QGGhEQIGxX0Gpc6jHLj40MNeuDnHXG+CcpJNGIKQeDoPaYg24/HX1pAbNXTLmZcTsi0glqvPmMRsX+3Wx2Qp14Z/VhQzsJBUwhVzHbfOH4jlIyndKfpKyKEXdXR0c/uDP9YljXLCGJzHY9LY94jk+cyTm6VqMnMYH7I+EiHB7VOdUpEhB49YK1XD9iRCfwI5fxFbfOHPOWp0VQK4TbzdcwYOVS1L6B73MPNXd+wqYxtUs4z0pTwdCmZ/xxDtKQl0FlJdODNM7+JOuunDBNOeRkz3+cIPfUx9xuHoG4RhuaKOm1MJlUITNufKFJ7djJWlldzupMDPmHq9MU6Un5P0m1+5TGfL+QpT3nKU566lv4fl+1LXBoUp3EAAAAASUVORK5CYII='; break; case 'copy': icon = 'data:image/png;base64,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'; break; case 'star': icon = 'data:image/png;base64,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'; break; default: iconstyle = 'link'; icon = 'data:image/png;base64,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'; // icon = '//storage.googleapis.com/ingress.com/img/map_icons/marker_images/lp-origin-ornament.png'; } self.iconstyle = iconstyle; var size = Math.max(30,zoom * 4); return L.icon({ iconUrl: icon, iconSize: [size, size], // size of the icon iconAnchor: [size/2, size/2], // point of the icon which will correspond to marker's location zIndexOffset: -1000 }); }; self.zoomadjust = function(zoom) { if (self.markerLayer === undefined) return; self.markerLayer.setIcon(self.linkicon(zoom)); }; self.clearall = function() { self.moveselectedlinkstartposition = undefined; self.moveselectedlinkendposition = undefined; self.movelinksposition = undefined; self.multistartpos = undefined; self.copylinksposition = undefined; self.closedialog(); self.deactivatebuttons(); }; self.addMarker = function(guid,iconstyle) { if (self.removeMarker() == iconstyle.icon) { self.clearall(); return; } if (self.drawnItems._map === null) return; self.activatebutton(iconstyle.icon); var portal = window.portals[guid]; var startpos = portal.getLatLng(); self.markerLayer = L.marker([startpos.lat, startpos.lng], {icon: self.linkicon(map.getZoom(),iconstyle), iconstyle: iconstyle.icon}).addTo(map); self.markerLayer.on('click', function(e) { self.clearall(); self.removeMarker(); }); self.startguid = guid; self.startpos = startpos; map.on('zoomend', function() { self.zoomadjust(map.getZoom()); }); }; self.multistartlinks = function() { if (window.selectedPortal === null) return; self.addMarker(window.selectedPortal,{icon:'star'}); self.multistartpos = self.startpos; }; self.removeMarker = function() { if (self.markerLayer === undefined) return false; // remove marker let iconstyle = self.markerLayer.options.iconstyle; map.removeLayer(self.markerLayer); self.markerLayer = undefined; return iconstyle; }; self.removelink = function(link) { if (self.markerLayer !== undefined) return; // disable remove when drawing a new line self.stophighlightlink(); var drawlayer = self.getDrawlayer(); drawlayer.removeLayer(link); self.updategreatcircleslayer(); self.updatefieldslayer(); self.storelinks(); // all other event types - assume anything could have been modified and re-check all links self.checkAllLinksForCrosslinks(); return true; }; self.linkexists = function(latLngs) { var drawlayer = self.getDrawlayer(); var linkexists = false; drawlayer.eachLayer( function(layer) { //if (layer instanceof L.GeodesicPolyline && layer.getLatLngs().length === 2) { if (layer instanceof L.GeoJSON && layer.getLatLngs().length === 2) { var existinglatLngs = layer.getLatLngs(); if ((existinglatLngs[0].lat === latLngs[0].lat && existinglatLngs[0].lng === latLngs[0].lng && existinglatLngs[1].lat === latLngs[1].lat && existinglatLngs[1].lng === latLngs[1].lng) || (existinglatLngs[0].lat === latLngs[1].lat && existinglatLngs[0].lng === latLngs[1].lng && existinglatLngs[1].lat === latLngs[0].lat && existinglatLngs[1].lng === latLngs[0].lng)){ linkexists = true; } } }); //console.log('link exists: ' + linkexists); return linkexists; }; 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.Coord = function(c, e) { // source: Arc 1.7.0 let D2R = Math.PI / 180; let R2D = 180 / Math.PI; this.lon = c; this.lat = e; this.x = D2R * c; this.y = D2R * e; }; self.Coord.prototype.view = function() { return String(this.lon).slice(0, 4) + "," + String(this.lat).slice(0, 4); }; self.Coord.prototype.antipode = function() { return new Coord(0 > this.lon ? 180 + this.lon : -1 * (180 - this.lon),-1 * this.lat); }; */ self.drawline = function(latLngs,color) { if (latLngs[0].lat === latLngs[1].lat && latLngs[0].lng === latLngs[1].lng) return; // no 0 length lines if (self.linkexists(latLngs)) return; // no double lines function randomguid() { function s4() { return Math.floor((1 + Math.random()) * 0x10000) .toString(16) .substring(1); } return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4(); } var drawlayer = self.getDrawlayer(); // Draw method: /* self.lineOptions = { stroke: true, color: self.settings.drawcolor, weight: 4, opacity: 0.8, fill: false, clickable: true }; var lineoptions = self.lineOptions; var link = L.geodesicPolyline(latLngs, L.extend({}, lineoptions, {color:color, dashArray: (self.settings.showlinkdirection?self.linkstyle:null)})); */ // Arc method: var startCoord = new self.arc.Coord(latLngs[0].lng,latLngs[0].lat); var stopCoord = new self.arc.Coord(latLngs[1].lng,latLngs[1].lat); var lineoptions = self.lineOptions; var myStyle = L.extend({}, lineoptions, {color:color, dashArray: (self.settings.showlinkdirection?self.linkstyle:null)}); var distance = self.distanceBetween(latLngs[0],latLngs[1]); var gc = new self.arc.GreatCircle(startCoord,stopCoord); //var gc = new window.plugin.arcs.arc.GreatCircle(startCoord,stopCoord); //console.log('==================== gc new arcs:',gcnew,gc); var geojson_feature = gc.Arc(Math.round(distance)).json(); var link = L.geoJson(geojson_feature,{ style: myStyle }); // additions to Arc, to make it compatible for Quick Draw: link.getLatLngs = function() { return [L.latLng(latLngs[0]),L.latLng(latLngs[1])]; }; link.options = L.extend({}, myStyle); link.on('click',function(e) { self.linkmenu(link); }); drawlayer.addLayer(link); self.updategreatcircleslayer(); self.updatefieldslayer(); link.options.guid = randomguid(); // create a fake guid for cross link support // we can just test the new layer in this case self.testAllLinksAgainstLayer(link); return link; }; self.addline = function() { if (!self.startpos || window.selectedPortal === null) return; var startpos = self.startpos; var endpos = window.portals[window.selectedPortal].getLatLng(); var latLngs = [new L.LatLng(startpos.lat,startpos.lng),new L.LatLng(endpos.lat,endpos.lng)]; self.drawline(latLngs,self.settings.drawcolor); self.storelinks(); }; self.clonelatlngs = function(latlngs) { var newlatlngs = []; for (var cnt = 0; cnt < latlngs.length; cnt++) { newlatlngs.push(new L.LatLng(latlngs[cnt].lat,latlngs[cnt].lng)); } return newlatlngs; }; self.moveline = function(index) { // 0 = start, 1 = end if (window.selectedPortal === null) return; var guid = window.selectedPortal; var portal = window.portals[guid]; var drawlayer = self.getDrawlayer(); var latLngs = self.clonelatlngs(self.selectedlink.getLatLngs()); var newll = portal.getLatLng(); latLngs[index] = new L.LatLng(newll.lat,newll.lng); if ((latLngs[0].lat === latLngs[1].lat && latLngs[0].lng === latLngs[1].lng) || self.linkexists(latLngs)) { // remove line if start and end match or if line already exists drawlayer.removeLayer(self.selectedlink); self.selectedlink = undefined; } else { //self.selectedlink.setLatLngs(latLngs); var color = self.selectedlink.options.color; drawlayer.removeLayer(self.selectedlink); self.selectedlink = self.drawline(latLngs,color); } self.updategreatcircleslayer(); self.updatefieldslayer(); self.storelinks(); // all other event types - assume anything could have been modified and re-check all links self.checkAllLinksForCrosslinks(); }; self.movelines = function() { if (window.selectedPortal === null) return; if (!self.movelinksposition) return; var guid = window.selectedPortal; var portal = window.portals[guid]; var newll = portal.getLatLng(); var drawlayer = self.getDrawlayer(); var movefromposition = self.movelinksposition; var movecnt = 0; drawlayer.eachLayer( function(layer) { //if (layer instanceof L.GeodesicPolyline && layer.getLatLngs().length === 2) { if (layer instanceof L.GeoJSON && layer.getLatLngs().length === 2) { var moved = false; var latLngs = self.clonelatlngs(layer.getLatLngs()); if (latLngs[0].lat === movefromposition.lat && latLngs[0].lng === movefromposition.lng) { latLngs[0] = new L.LatLng(newll.lat,newll.lng); moved = true; } else if (latLngs[1].lat === movefromposition.lat && latLngs[1].lng === movefromposition.lng) { latLngs[1] = new L.LatLng(newll.lat,newll.lng); moved = true; } if (moved) { movecnt++; if ((latLngs[0].lat === latLngs[1].lat && latLngs[0].lng === latLngs[1].lng) || self.linkexists(latLngs)) { // remove line if start and end match or if line already exists drawlayer.removeLayer(layer); } else { //layer.setLatLngs(latLngs); var color = layer.options.color; drawlayer.removeLayer(layer); self.drawline(latLngs,color); } } } }); if (movecnt > 0) { self.updategreatcircleslayer(); self.updatefieldslayer(); self.storelinks(); } // all other event types - assume anything could have been modified and re-check all links self.checkAllLinksForCrosslinks(); }; self.copylines = function() { if (window.selectedPortal === null) return; if (!self.copylinksposition) return; var guid = window.selectedPortal; var portal = window.portals[guid]; var newll = portal.getLatLng(); var drawlayer = self.getDrawlayer(); var copyfromposition = self.copylinksposition; var copycnt = 0; drawlayer.eachLayer( function(layer) { //if (layer instanceof L.GeodesicPolyline && layer.getLatLngs().length === 2) { if (layer instanceof L.GeoJSON && layer.getLatLngs().length === 2) { var latLngs = self.clonelatlngs(layer.getLatLngs()); var startpos; var endpos; if (latLngs[0].lat === copyfromposition.lat && latLngs[0].lng === copyfromposition.lng) { startpos = newll; endpos = new L.LatLng(latLngs[1].lat,latLngs[1].lng); } else if (latLngs[1].lat === copyfromposition.lat && latLngs[1].lng === copyfromposition.lng) { startpos = new L.LatLng(latLngs[0].lat,latLngs[0].lng); endpos = newll; } if (startpos && endpos) { latLngs = [new L.LatLng(startpos.lat,startpos.lng),new L.LatLng(endpos.lat,endpos.lng)]; self.drawline(latLngs,layer.options.color); copycnt++; } } }); if (copycnt > 0) { self.updategreatcircleslayer(); self.updatefieldslayer(); self.storelinks(); } // all other event types - assume anything could have been modified and re-check all links self.checkAllLinksForCrosslinks(); }; self.storetitles = function() { // only save titles for stored positions var savetitles = {}; var drawlayer = self.getDrawlayer(); drawlayer.eachLayer( function(layer) { //if (layer instanceof L.GeodesicPolyline && layer.getLatLngs().length === 2) { if (layer instanceof L.GeoJSON && layer.getLatLngs().length === 2) { var latLngs = layer.getLatLngs(); for (var cnt = 0; cnt < latLngs.length; cnt++) { var guid = self.getguid(latLngs[cnt]); var title = self.gettitle(latLngs[cnt]); if (title && guid) { savetitles[latLngs[cnt].lat + ',' + latLngs[cnt].lng] = {title:title,guid:guid}; } } } }); localStorage[self.localstoragetitlecache] = JSON.stringify(savetitles); }; self.restoretitles = function() { // restore stored titles try { var dataStr = localStorage[self.localstoragetitlecache]; if (dataStr === undefined) return; var data = JSON.parse(dataStr); $.each(data, function(pos,item) { if (item instanceof Object) { self.guidpos[pos] = item.guid; self.titlecache[item.guid] = item.title; } }); } catch(e) { console.warn(self.id + ': failed to load data from localStorage: '+e); } }; self.gettitle = function(position) { var guid = self.getguid(position); if (!guid) return undefined; return self.titlecache[guid]; }; self.getguid = function(position) { if (!(position instanceof Object) || !position.lat || !position.lng) return undefined; return self.guidpos[position.lat + ',' + position.lng]; }; self.deactivatebuttons = function() { console.log('deactivatebuttons'); $('.screenlinkicon, .titlelinkicon').css('background-position-y','top'); $('.screenmoveicon, .titlemoveicon').css('background-position-y','top'); $('.screenstaricon, .titlestaricon').css('background-position-y','top'); $('.screencopyicon, .titlecopyicon').css('background-position-y','top'); }; self.activatebutton = function(icon) { console.log('activatebutton',icon); $('.screen' + icon + 'icon, .title' + icon + 'icon').css('background-position-y','bottom'); }; self.onPortalSelected = function() { // remove the sidebar button self.stophighlightlink(); $('.quickdrawbutton').remove(); if (window.selectedPortal === null) { $('#quickdrawmenuselectedportal').hide(); return; } if (self.drawnItems._map === null) return; $('#quickdrawmenuselectedportal').show(); var guid = window.selectedPortal; var portal = window.portals[guid]; var latlng = portal.getLatLng(); if (portal.options.data.title) { self.guidpos[latlng.lat + ',' + latlng.lng] = guid; self.titlecache[guid] = portal.options.data.title; self.storetitles(); } if (self.markerLayer !== undefined) { // there is a start marker if (self.startguid !== guid) { // another portal then the start portal is selected if (self.selectedlink && self.moveselectedlinkstartposition) { self.moveline(0); self.moveselectedlinkstartposition = undefined; // remove marker self.removeMarker(); } else if (self.selectedlink && self.moveselectedlinkendposition) { self.moveline(1); self.moveselectedlinkendposition = undefined; // remove marker self.removeMarker(); } else if (self.movelinksposition) { // move all links connected to movelinksposition to new portal self.movelines(); self.movelinksposition = undefined; // remove marker self.removeMarker(); } else if (self.copylinksposition) { // copy all links connected to copylinksposition to new portal self.copylines(); self.copylinksposition = undefined; // remove marker self.removeMarker(); } else { self.addline(); // remove marker let iconstyle = self.removeMarker(); // set marker for new portal self.addMarker(guid,{icon:iconstyle}); // repeat link or star if (self.multistartpos) { // star self.startpos = self.multistartpos; } } } } setTimeout(function() { // the sidebar is constructed after firing the hook // add button to portal details window: $('.quickdrawbutton').remove(); if (window.selectedPortal === null) return; var guid = window.selectedPortal; var portalposition = window.portals[guid].getLatLng(); // find links connected to selectedportal var linkcount = self.linkcount(portalposition); if (linkcount > 0) { // copybutton let titledescription = ''; if (!self.isSmartphone) titledescription = ' title="Click to copy all links from this portal to another portal"'; let onclickaction = self.namespace + 'clearall(); ' + self.namespace + 'copylinks(); return false;' if (!self.settings.hidebuttons) { $('#updatestatus').prepend(''); } $('#portaldetails > .title').prepend(''); } if (true) { // starbutton let titledescription = ''; if (!self.isSmartphone) titledescription = ' title="Click to start making multiple links to this portal"'; let onclickaction = self.namespace + 'clearall(); ' + self.namespace + 'multistartlinks(); return false;' let styleactivebutton = (self.markerLayer != undefined && self.markerLayer.options.iconstyle == 'star'?' style="background-position-y: bottom;"':'' ); if (!self.settings.hidebuttons) { $('#updatestatus').prepend(''); } $('#portaldetails > .title').prepend(''); } if (linkcount > 0) { // movebutton let titledescription = ''; if (!self.isSmartphone) titledescription = ' title="Click to move all links from this portal to another portal"'; let onclickaction = self.namespace + 'clearall(); ' + self.namespace + 'movelinks(); return false;' if (!self.settings.hidebuttons) { $('#updatestatus').prepend(''); } $('#portaldetails > .title').prepend(''); } if (true) { // linkbutton let titledescription = ''; if (!self.isSmartphone) titledescription = ' title="Click to start a link from this portal, click another portal to create a link"'; let onclickaction = self.namespace + 'clearall(); ' + self.namespace + 'addMarker(\'' + guid + '\',{icon:\'link\'}); return false;' let styleactivebutton = (self.markerLayer != undefined && self.markerLayer.options.iconstyle == 'link'?' style="background-position-y: bottom;"':'' ); if (!self.settings.hidebuttons) { $('#updatestatus').prepend(''); } $('#portaldetails > .title').prepend(''); } }, 0); }; self.setalloutgoing = function(position) { if (!position) return; var drawlayer = self.getDrawlayer(); var swapcount = 0; var outcount = 0; drawlayer.eachLayer( function(layer) { //if (layer instanceof L.GeodesicPolyline && layer.getLatLngs().length === 2) { if (layer instanceof L.GeoJSON && layer.getLatLngs().length === 2) { var latLngs = layer.getLatLngs(); if (position.lat === latLngs[1].lat && position.lng === latLngs[1].lng) { // set incoming link to outgoing link self.selectedlink = layer; self.swapstartend(); swapcount++; } else if (position.lat === latLngs[0].lat && position.lng === latLngs[0].lng) { outcount++; } } }); alert('Total Drawn Links connected to portal: ' + (swapcount + outcount) + "\n" + 'links changed to outgoing: ' + swapcount); }; self.setallincoming = function(position) { if (!position) return; var drawlayer = self.getDrawlayer(); var swapcount = 0; var incount = 0; drawlayer.eachLayer( function(layer) { //if (layer instanceof L.GeodesicPolyline && layer.getLatLngs().length === 2) { if (layer instanceof L.GeoJSON && layer.getLatLngs().length === 2) { var latLngs = layer.getLatLngs(); if (position.lat === latLngs[0].lat && position.lng === latLngs[0].lng) { // set outgoing link to incoming link self.selectedlink = layer; self.swapstartend(); swapcount++; } else if (position.lat === latLngs[1].lat && position.lng === latLngs[1].lng) { incount++; } } }); alert('Total Drawn Links connected to portal: ' + (swapcount + incount) + "\n" + 'links changed to incoming: ' + swapcount); }; self.linkcount = function(position) { var drawlayer = self.getDrawlayer(); var linkcount = 0; drawlayer.eachLayer( function(layer) { //if (layer instanceof L.GeodesicPolyline && layer.getLatLngs().length === 2) { if (layer instanceof L.GeoJSON && layer.getLatLngs().length === 2) { var latLngs = layer.getLatLngs(); if (!position || position.lat === latLngs[0].lat && position.lng === latLngs[0].lng || position.lat === latLngs[1].lat && position.lng === latLngs[1].lng) { linkcount++; } } }); return linkcount; }; self.storelinks = function() { var data = []; self.drawnItems.eachLayer( function(layer) { if (layer instanceof L.GeoJSON && layer.getLatLngs().length === 2) { var item = {}; item.type = 'polyline'; item.latLngs = layer.getLatLngs(); item.color = layer.options.color; data.push(item); } }); localStorage[self.localstoragelayer] = JSON.stringify(data); self.storetitles(); }; self.load = function() { try { var dataStr = localStorage[self.localstoragelayer]; if (dataStr === undefined) return; var data = JSON.parse(dataStr); self.import(data); } catch(e) { console.warn(self.id + ': failed to load data from localStorage: '+e); } }; self.import = function(data) { if (!data.layerdata && !data.titledata) data.layerdata = data; // support some backwards compatiblity if (data.layerdata) { $.each(data.layerdata, function(index,item) { switch(item.type) { case 'polyline': self.drawline(item.latLngs,item.color); break; default: console.warn(self.id + ': unknown import layer type "'+item.type); break; } }); } if (data.titledata) { localStorage[self.localstoragetitlecache] = JSON.stringify(data.titledata); self.restoretitles(); } }; // copy start // copied from Cross Links plugin ////////// self.greatCircleArcIntersect = function(a0,a1,b0,b1) { // based on the formula at http://williams.best.vwh.net/avform.htm#Int // method: // check to ensure no line segment is zero length - if so, cannot cross // check to see if either of the lines start/end at the same point. if so, then they cannot cross // check to see if the line segments overlap in longitude. if not, no crossing // if overlap, clip each line to the overlapping longitudes, then see if latitudes cross // anti-meridian handling. this code will not sensibly handle a case where one point is // close to -180 degrees and the other +180 degrees. unwrap coordinates in this case, so one point // is beyond +-180 degrees. this is already true in IITC // FIXME? if the two lines have been 'unwrapped' differently - one positive, one negative - it will fail // zero length line tests if (a0.equals(a1)) return false; if (b0.equals(b1)) return false; // lines have a common point if (a0.equals(b0) || a0.equals(b1)) return false; if (a1.equals(b0) || a1.equals(b1)) return false; // check for 'horizontal' overlap in lngitude if (Math.min(a0.lng,a1.lng) > Math.max(b0.lng,b1.lng)) return false; if (Math.max(a0.lng,a1.lng) < Math.min(b0.lng,b1.lng)) return false; // ok, our two lines have some horizontal overlap in longitude // 1. calculate the overlapping min/max longitude // 2. calculate each line latitude at each point // 3. if latitudes change place between overlapping range, the lines cross // class to hold the pre-calculated maths for a geodesic line // TODO: move this outside this function, so it can be pre-calculated once for each line we test var GeodesicLine = function(start,end) { var d2r = Math.PI/180.0; var r2d = 180.0/Math.PI; // maths based on http://williams.best.vwh.net/avform.htm#Int if (start.lng == end.lng) { throw 'Error: cannot calculate latitude for meridians'; } // only the variables needed to calculate a latitude for a given longitude are stored in 'this' this.lat1 = start.lat * d2r; this.lat2 = end.lat * d2r; this.lng1 = start.lng * d2r; this.lng2 = end.lng * d2r; var dLng = this.lng1-this.lng2; var sinLat1 = Math.sin(this.lat1); var sinLat2 = Math.sin(this.lat2); var cosLat1 = Math.cos(this.lat1); var cosLat2 = Math.cos(this.lat2); this.sinLat1CosLat2 = sinLat1*cosLat2; this.sinLat2CosLat1 = sinLat2*cosLat1; this.cosLat1CosLat2SinDLng = cosLat1*cosLat2*Math.sin(dLng); }; GeodesicLine.prototype.isMeridian = function() { return this.lng1 == this.lng2; }; GeodesicLine.prototype.latAtLng = function(lng) { lng = lng * Math.PI / 180; //to radians var lat; // if we're testing the start/end point, return that directly rather than calculating // 1. this may be fractionally faster, no complex maths // 2. there's odd rounding issues that occur on some browsers (noticed on IITC MObile) for very short links - this may help if (lng == this.lng1) { lat = this.lat1; } else if (lng == this.lng2) { lat = this.lat2; } else { lat = Math.atan ( (this.sinLat1CosLat2*Math.sin(lng-this.lng2) - this.sinLat2CosLat1*Math.sin(lng-this.lng1)) / this.cosLat1CosLat2SinDLng); } return lat * 180 / Math.PI; // return value in degrees }; // calculate the longitude of the overlapping region var leftLng = Math.max( Math.min(a0.lng,a1.lng), Math.min(b0.lng,b1.lng) ); var rightLng = Math.min( Math.max(a0.lng,a1.lng), Math.max(b0.lng,b1.lng) ); // calculate the latitudes for each line at left + right longitudes // NOTE: need a special case for meridians - as GeodesicLine.latAtLng method is invalid in that case var aLeftLat, aRightLat; if (a0.lng == a1.lng) { // 'left' and 'right' now become 'top' and 'bottom' (in some order) - which is fine for the below intersection code aLeftLat = a0.lat; aRightLat = a1.lat; } else { var aGeo = new GeodesicLine(a0,a1); aLeftLat = aGeo.latAtLng(leftLng); aRightLat = aGeo.latAtLng(rightLng); } var bLeftLat, bRightLat; if (b0.lng == b1.lng) { // 'left' and 'right' now become 'top' and 'bottom' (in some order) - which is fine for the below intersection code bLeftLat = b0.lat; bRightLat = b1.lat; } else { var bGeo = new GeodesicLine(b0,b1); bLeftLat = bGeo.latAtLng(leftLng); bRightLat = bGeo.latAtLng(rightLng); } // if both a are less or greater than both b, then lines do not cross if (aLeftLat < bLeftLat && aRightLat < bRightLat) return false; if (aLeftLat > bLeftLat && aRightLat > bRightLat) return false; // latitudes cross between left and right - so geodesic lines cross return true; }; self.testPolyLine = function (polyline, link,closed) { var a = link.getLatLngs(); var b = polyline.getLatLngs(); for (var i=0;i' + displaylength + ''; rows.push({length:length,text:direction + ' ' + title + ' - ' + displaylength}); } rows.sort( function(a, b) { if (a.length === b.length) { return 0; } else { return (a.length > b.length) ? 1 : -1; } }); let rowstext = []; for (let cnt = 0; cnt < rows.length; cnt++) { rowstext.push(rows[cnt].text); } return rowstext.join('
\n') + '
\n'; }; self.overviewConnected = function() { var drawlayer = self.getDrawlayer(); if (!drawlayer) return; if (!window.selectedPortal) return; var portal = window.portals[window.selectedPortal]; var portalLinks = { in: [], out: [] }; $.each(window.links, function(linkguid,link) { if (link.options.data.oGuid === window.selectedPortal) portalLinks.out.push(link); if (link.options.data.dGuid === window.selectedPortal) portalLinks.in.push(link); }); var position = portal.getLatLng(); var drawnLinks = { in: [], out: [] }; drawlayer.eachLayer( function(layer) { //if (layer instanceof L.GeodesicPolyline && layer.getLatLngs().length === 2) { if (layer instanceof L.GeoJSON && layer.getLatLngs().length === 2) { var latLngs = layer.getLatLngs(); if (position.lat === latLngs[0].lat && position.lng === latLngs[0].lng) { // portal position matches first draw links position (base) drawnLinks.out.push(layer); } else if (position.lat === latLngs[1].lat && position.lng === latLngs[1].lng) { // portal position matches second draw links position (target) drawnLinks.in.push(layer); } } }); var html = '
' + '< Main menu' + '
' + 'Selected portal (refresh):
\n' + '' + portal.options.data.title + '
\n' + '
\n' + 'Drawn links: ' + (drawnLinks.in.length + drawnLinks.out.length) + ' (' + drawnLinks.out.length + ' out, ' + drawnLinks.in.length + ' in)
\n' + self.linkedportalshtml(drawnLinks.out,position,true) + self.linkedportalshtml(drawnLinks.in,position,true) + '
\n' + 'Existing links: ' + (portalLinks.in.length + portalLinks.out.length) + ' (' + portalLinks.out.length + ' out, ' + portalLinks.in.length + ' in)
\n' + self.linkedportalshtml(portalLinks.in,position,false) + self.linkedportalshtml(portalLinks.out,position,false) + '
'; if (window.useAndroidPanes()) window.show("map"); window.dialog({ html: html, id: self.pluginname + '-dialog', dialogClass: 'ui-dialog-quickdrawlinks', title: self.title + ' Overview', width: 400 }); }; self.showAll = function() { var drawlayer = self.getDrawlayer(); if (Object.keys(drawlayer._layers).length === 0) { alert('No drawn links to display'); return; } map.fitBounds(drawlayer.getBounds()); }; self.showLink = function(link) { var drawlayer = self.getDrawlayer(); if (!link) return; map.fitBounds(link.getBounds()); }; self.setDrawColor = function(color) { self.settings.drawcolor = color; self.storesettings(); }; self.closedialog = function() { $(".ui-dialog-content").dialog("close"); }; self.removeselectedlink = function() { if (!self.selectedlink) return; self.removelink(self.selectedlink); self.selectedlink = undefined; self.closedialog(); }; self.setSelectedLinkColor = function(color) { if (!self.selectedlink) return; self.selectedlink.setStyle({color:color}); self.selectedlink.options.color = color; // added for L.geoJson compatibility self.storelinks(); }; self.updateAllLinkStyle = function() { var drawlayer = self.getDrawlayer(); drawlayer.eachLayer( function(layer) { //if (layer instanceof L.GeodesicPolyline && layer.getLatLngs().length === 2) { if (layer instanceof L.GeoJSON && layer.getLatLngs().length === 2) { layer.setStyle({dashArray: (self.settings.showlinkdirection?self.linkstyle:null)}); } }); }; self.findPortalGuid = function(position) { for (var guid in window.portals) { var portalposition = window.portals[guid].getLatLng(); if (portalposition.lat === position.lat && portalposition.lng == position.lng) { return guid; } } return undefined; }; self.selectportalposition = function(position) { if (Object.keys(window.portals).length === 0) return; // cancel while no portals loaded yet; prevents an error inside the IITC core var guid = self.getguid(position); if (!guid) guid = self.findPortalGuid(position); if (!guid) return false; if (guid === window.selectedPortal) { var visibleBounds = map.getBounds(); if (!visibleBounds.contains(position)) window.map.setView(position, map.getZoom()); return true; } if (window.portals[guid]) { window.renderPortalDetails(guid); } else { self.requestid = guid; window.portalDetail.request(guid); return false; } return true; }; self.selectlinkstart = function() { if (!self.selectedlink) return; self.selectportalposition(self.selectedlink.getLatLngs()[0]); }; self.selectlinkend = function() { if (!self.selectedlink) return; self.selectportalposition(self.selectedlink.getLatLngs()[1]); }; self.movelinkstart = function() { if (!self.selectedlink) return; var position = self.selectedlink.getLatLngs()[0]; if (self.selectportalposition(position)) { self.moveselectedlinkstartposition = position; self.moveselectedlinkendposition = undefined; self.addMarker(window.selectedPortal,{icon:'move'}); self.closedialog(); } else { alert('Portal not found'); } }; self.movelinkend = function() { if (!self.selectedlink) return; var position = self.selectedlink.getLatLngs()[1]; if (self.selectportalposition(position)) { self.moveselectedlinkstartposition = undefined; self.moveselectedlinkendposition = position; self.addMarker(window.selectedPortal,{icon:'move'}); self.closedialog(); } else { alert('Portal not found'); } }; self.swapstartend = function() { if (!self.selectedlink) return; var latLngs = self.clonelatlngs(self.selectedlink.getLatLngs()); var latLng0 = new L.LatLng(latLngs[0].lat,latLngs[0].lng); var latLng1 = new L.LatLng(latLngs[1].lat,latLngs[1].lng); latLngs[0] = latLng1; latLngs[1] = latLng0; //self.selectedlink.setLatLngs(latLngs); var drawlayer = self.getDrawlayer(); var color = self.selectedlink.options.color; drawlayer.removeLayer(self.selectedlink); self.selectedlink = self.drawline(latLngs,color); self.storelinks(); }; self.linkstarttitle = function() { if (!self.selectedlink) return; var title = self.gettitle(self.selectedlink.getLatLngs()[0]); return (title?title:'unknown'); }; self.linkendtitle = function() { if (!self.selectedlink) return; var title = self.gettitle(self.selectedlink.getLatLngs()[1]); return (title?title:'unknown'); }; self.linklength = function(link) { if (!link) link = self.selectedlink; if (!link) return ''; var latlngs = link.getLatLngs(); //var length_old = L.latLng(latlngs[0].lat,latlngs[0].lng).distanceTo([latlngs[1].lat,latlngs[1].lng]); var length = self.distanceBetween(latlngs[0],latlngs[1]) * 1000; return length < 100000 ? Math.round(length) + 'm' : Math.round(length/1000) + 'km'; }; self.drawgreatcircle = function(linklayer) { var drawPolylineLines = window.map.hasLayer(self.greatcircleslayer); if (!drawPolylineLines) return; //var vincenty_ellipsoid = { a: 6378137, b: 6356752.3142, f: 1/298.257223563 }; // WGS-84 var vincenty_ellipsoid = { a: 6367000, b: 6367000, f: 0 }; // Sphere // From Leaflet.Geodesic (https://github.com/henrythasler/Leaflet.Geodesic/) var vincenty_inverse = function (p1, p2) { 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; var sinλ, cosλ, σ, sinσ, cosσ, cosSqα, cos2σM; do { sinλ = Math.sin(λ); cosλ = Math.cos(λ); var sinSqσ = (cosU2*sinλ) * (cosU2*sinλ) + (cosU1*sinU2-sinU1*cosU2*cosλ) * (cosU1*sinU2-sinU1*cosU2*cosλ); sinσ = Math.sqrt(sinSqσ); if (sinσ==0) return 0; // co-incident points cosσ = sinU1*sinU2 + cosU1*cosU2*cosλ; σ = Math.atan2(sinσ, cosσ); var sinα = cosU1 * cosU2 * sinλ / sinσ; cosSqα = 1 - sinα*sinα; 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() }; }; //From Leaflet.Geodesic (https://github.com/henrythasler/Leaflet.Geodesic/) var vincenty_direct = function (p1, initialBearing, distance, wrap) { 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; var cos2σM; var sinσ; var cosσ; do { cos2σM = Math.cos(2*σ1 + σ); sinσ = Math.sin(σ); 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() }; }; var drawLink = function(a, b, style, layerGroup) { var poly = L.geodesicPolyline([a, b], style); poly.addTo(layerGroup); }; var extendEdge = function(a,b,layerGroup) { if(!a || !b) return; var inverse = vincenty_inverse(a,b); var maxLinkDistance = 6881280; var maxLinkToAnchor = maxLinkDistance - inverse.distance; if(maxLinkToAnchor < 0) return; var direct = vincenty_direct(b, inverse.finalBearing, maxLinkToAnchor, true); var c = new L.LatLng(direct.lat, direct.lng); drawLink(b, c, { color: self.settings.greatcirclecolor, opacity: 0.9, weight: 1, clickable: false, smoothFactor: 1, dashArray: null //[6, 4], }, layerGroup); }; var vertices = linklayer.getLatLngs(); $.each(vertices, function(idx, vertex) { var previousVertex = idx === 0 ? null : vertices[idx - 1]; var nextVertex = idx === (vertices.length - 1) ? null : vertices[idx + 1]; extendEdge(previousVertex, vertex, self.greatcircleslayer); extendEdge(nextVertex, vertex, self.greatcircleslayer); }); }; self.updategreatcircleslayer = function() { if (!self.layeractive('Quick Draw Great Circles')) return; self.greatcircleslayer.clearLayers(); var drawlayer = self.getDrawlayer(); if (!drawlayer) return; drawlayer.eachLayer(function(layer) { //if (layer instanceof L.GeodesicPolyline && layer.getLatLngs().length === 2) { if (layer instanceof L.GeoJSON && layer.getLatLngs().length === 2) { self.drawgreatcircle(layer); } }); }; self.removegreatcircles = function() { var drawlayer = self.getDrawlayer(); self.greatcircleslayer.clearLayers(); // erase all }; self.layeractive = function(layername) { var layers = window.layerChooser.getLayers().overlayLayers; for (var id in layers) { if (layers[id].name === layername) { if (layers[id].active) { return true; } } } return false; }; self.updatefieldslayer = function() { if (!self.layeractive('Quick Draw Fields')) return; // draw triangles self.fieldslayer.clearLayers(); var drawlayer = self.getDrawlayer(); if (!drawlayer) return; var layers = []; drawlayer.eachLayer(function(layer) { //if (layer instanceof L.GeodesicPolyline && layer.getLatLngs().length === 2) { if (layer instanceof L.GeoJSON && layer.getLatLngs().length === 2) { layers.push(layer); } }); function findcornermatch(corner,layers,startcnt) { var cnt; for (cnt = startcnt; cnt < layers.length; cnt++) { var latlngs = layers[cnt].getLatLngs(); if (corner[0].lat === latlngs[0].lat && corner[0].lng === latlngs[0].lng) { // matching corner found return [0,cnt,0]; } else if (corner[0].lat === latlngs[1].lat && corner[0].lng === latlngs[1].lng) { // matching corner found return [0,cnt,1]; } else if (corner[1].lat === latlngs[0].lat && corner[1].lng === latlngs[0].lng) { // matching corner found return [1,cnt,0]; } else if (corner[1].lat === latlngs[1].lat && corner[1].lng === latlngs[1].lng) { // matching corner found return [1,cnt,1]; } } return false; } function findlinkmatch(corner1,corner2,layers,startcnt) { var cnt; for (cnt = startcnt; cnt < layers.length; cnt++) { var latlngs = layers[cnt].getLatLngs(); if (corner1.lat === latlngs[0].lat && corner1.lng === latlngs[0].lng && corner2.lat === latlngs[1].lat && corner2.lng === latlngs[1].lng) { return cnt; } else if (corner1.lat === latlngs[1].lat && corner1.lng === latlngs[1].lng && corner2.lat === latlngs[0].lat && corner2.lng === latlngs[0].lng) { return cnt; } } return false; } var polygonOptions = { stroke: false, color: null, weight: 4, opacity: 0.5, clickable: false, fill: true, fillColor: self.settings.fieldcolor, fillOpacity: 0.2 }; var cnt; if (self.settings.fieldexistinglinks) { var drawncorners = {}; for (cnt = 0; cnt < layers.length; cnt++) { var layerlatlngs = layers[cnt].getLatLngs(); drawncorners[layerlatlngs[0].lat] = 1; drawncorners[layerlatlngs[0].lng] = 1; drawncorners[layerlatlngs[1].lat] = 1; drawncorners[layerlatlngs[1].lng] = 1; } // include all drawn links for current player faction that match a corner of drawn links var playerteam = (PLAYER.team === 'RESISTANCE'?'R':'E'); for (var guid in window.links) { var link = window.links[guid]; if (link.options.data.team === playerteam) { var linklatlngs = link.getLatLngs(); if (drawncorners[linklatlngs[0].lat] || drawncorners[linklatlngs[0].lng] || drawncorners[linklatlngs[1].lat] || drawncorners[linklatlngs[1].lng]) { layers.push(link); } } } } // to do: find 3 links with 3 matching corners // step 1: test every link, start and end point, for a match with any other (next) start or end point for (cnt = 0; cnt < layers.length; cnt++) { var link1latlngs = layers[cnt].getLatLngs(); var cornermatch; cornermatch = findcornermatch(link1latlngs,layers,cnt + 1); // sourcecorner,matchcnt,matchcorner do { if (cornermatch instanceof Array) { // matching corner found: layers[cnt].getLatLngs()[cornermatch[0]] === layers[cornermatch[1]].getLatLngs()[cornermatch[2]] // step 2: test every (next) link for a match with the 2 opposite corners of the 2 matching links var link2latlngs = layers[cornermatch[1]].getLatLngs(); var linkmatch = findlinkmatch(link1latlngs[1 - cornermatch[0]],link2latlngs[1 - cornermatch[2]],layers,cornermatch[1] + 1); if (linkmatch) { // draw a triangle polygon //console.log(link1latlngs[0],link1latlngs[1],link2latlngs[1 - cornermatch[2]]); /* var trianglejson = { "type": "Polygon", "coordinates": [ [ [link1latlngs[0].lat, link1latlngs[0].lng], [link1latlngs[1].lat, link1latlngs[1].lng], [link2latlngs[1 - cornermatch[2]].lat, link2latlngs[1 - cornermatch[2]].lng], [link1latlngs[0].lat, link1latlngs[0].lng] ] ] }; */ var latLngs = [link1latlngs[0],link1latlngs[1],link2latlngs[1 - cornermatch[2]]]; var layer = L.geodesicPolygon(latLngs,polygonOptions); self.fieldslayer.addLayer(layer).bringToBack(); // link1latlngs[0],link1latlngs[1],link2latlngs[1 - cornermatch[2]] } // check for another match cornermatch = findcornermatch(link1latlngs,layers,cornermatch[1] + 1); // sourcecorner,matchcnt,matchcorner } } while (cornermatch instanceof Array); } }; self.linkindex = function(link) { var drawlayer = self.getDrawlayer(); if (!link) link = self.selectedlink; if (!link) return -1; var links = []; var selectedindex = -1; var selectedlatlngs = link.getLatLngs(); drawlayer.eachLayer( function(layer) { //if (layer instanceof L.GeodesicPolyline && layer.getLatLngs().length === 2) { if (layer instanceof L.GeoJSON && layer.getLatLngs().length === 2) { links.push(layer); var latLngs = layer.getLatLngs(); if (selectedlatlngs && selectedlatlngs[0].lat === latLngs[0].lat && selectedlatlngs[0].lng === latLngs[0].lng && selectedlatlngs[1].lat === latLngs[1].lat && selectedlatlngs[1].lng === latLngs[1].lng) { selectedindex = links.length - 1; } } }); return selectedindex; }; self.highlightlinkbylatlng = function(lat0,lng0,lat1,lng1) { self.stophighlightlink(); var latlng0 = L.latLng(lat0, lng0); var latlng1 = L.latLng(lat1, lng1); self.highlightlink = L.layerGroup().addTo(map); L.circleMarker(latlng0, self.highlightlinkoptions) .addTo(self.highlightlink); L.circleMarker(latlng1, self.highlightlinkoptions) .addTo(self.highlightlink); L.geodesicPolyline([latlng0, latlng1], self.highlightlinkoptions) .addTo(self.highlightlink); }; self.stophighlightlink = function() { if (self.highlightlink) map.removeLayer(self.highlightlink); self.highlightlink = null; }; self.selectlinkbylatlng = function(lat0,lng0,lat1,lng1) { var drawlayer = self.getDrawlayer(); var linkfound = false; drawlayer.eachLayer( function(layer) { //if (layer instanceof L.GeodesicPolyline && layer.getLatLngs().length === 2) { if (layer instanceof L.GeoJSON && layer.getLatLngs().length === 2) { var latLngs = layer.getLatLngs(); if (lat0 === latLngs[0].lat && lng0 === latLngs[0].lng && lat1 === latLngs[1].lat && lng1 === latLngs[1].lng) { self.selectedlink = layer; linkfound = true; } } }); return linkfound; }; self.selectlink = function(offset = 0) { self.stophighlightlink(); // place links in a reference array, find the one that matches the current link var drawlayer = self.getDrawlayer(); var links = []; var selectedindex = -1; var selectedlatlngs = (self.selectedlink?self.selectedlink.getLatLngs():null); drawlayer.eachLayer( function(layer) { //if (layer instanceof L.GeodesicPolyline && layer.getLatLngs().length === 2) { if (layer instanceof L.GeoJSON && layer.getLatLngs().length === 2) { links.push(layer); var latLngs = layer.getLatLngs(); if (selectedlatlngs && selectedlatlngs[0].lat === latLngs[0].lat && selectedlatlngs[0].lng === latLngs[0].lng && selectedlatlngs[1].lat === latLngs[1].lat && selectedlatlngs[1].lng === latLngs[1].lng) { selectedindex = links.length - 1; } } }); if (links.length === 0) return; if (selectedindex === -1) { selectedindex = 0; // select first if none selected } else { selectedindex += offset; } // fix offset within range while (selectedindex < 0) { selectedindex += links.length; } while (selectedindex >= links.length) { selectedindex -= links.length; } self.selectedlink = links[selectedindex]; }; self.selectpreviouslinkmenu = function() { self.selectlink(-1); self.linkmenu(self.selectedlink); }; self.selectnextlinkmenu = function() { self.selectlink(1); self.linkmenu(self.selectedlink); }; self.linkmenu = function(line) { if (self.markerLayer !== undefined) return; // disable menu when drawing a new line if (!line) { self.selectlink(); } else { self.stophighlightlink(); self.selectedlink = line; } var html = '
' + '< Main menu' + '
' + '
' + '
' + '
' + '' + '
' + '
' + ' ' + '' + '
' + '
' + '
' + '
' + '
' + '
Start:
' + '
' + self.linkstarttitle() + '
' + '
' + '
' + '' + ' ' + ' ' + '
' + '
' + '
End:
' + '
' + self.linkendtitle() + '
' + '
' + '
' + ' ' + ' ' + '
' + '
' + (self.linkindex(line) + 1) + '/' + self.linkcount() + '
'; if (window.useAndroidPanes()) window.show('map'); window.dialog({ html: html, id: self.pluginname + '-dialog', dialogClass: 'ui-dialog-quickdrawlinks', width: 350, title: 'Edit Link (' + self.linklength() + ')' }); // need to initialise the 'spectrum' color picker $('#quickdrawlinks_linkcolor').spectrum({ flat: false, showInput: true, showButtons: true, showPalette: true, showSelectionPalette: true, allowEmpty: false, palette: [ ['#004000','#008000','#00C000'], ['#00FF00','#80FF80','#C0FFC0'], ['#000040','#000080','#0000C0'], ['#4040FF','#8080FF','#C0C0FF'], ['#6A3400','#964A00','#C05F00'], ['#E27000','#FF8309','#FFC287'], ['#a24ac3','#514ac3','#4aa8c3','#51c34a'], ['#c1c34a','#c38a4a','#c34a4a','#c34a6f'], ['#000000','#666666','#bbbbbb','#ffffff'] ], change: function(color) { self.setSelectedLinkColor(color.toHexString()); }, color: self.selectedlink.options.color, }); }; self.createURL = function() { var drawlayer = self.getDrawlayer(); if (self.linkcount() === 0) { alert('No drawn links'); return; } var data = []; drawlayer.eachLayer( function(layer) { //if (layer instanceof L.GeodesicPolyline && layer.getLatLngs().length === 2) { if (layer instanceof L.GeoJSON && layer.getLatLngs().length === 2) { var latLngs = layer.getLatLngs(); data.push([latLngs[0].lat,latLngs[0].lng,latLngs[1].lat,latLngs[1].lng].join(',')); } }); var mappos = map.getCenter(); var baseurl = window.location.protocol + "//" + window.location.host; var URL = baseurl + "/intel?" + "ll=" + mappos.lat + "," + mappos.lng + "&z=" + map.getZoom() + "&pls=" + data.join('_'); var html = '

Select all and press CTRL+C to copy it.

' + ''; window.dialog({ html: html, id: 'ui-dialog-quickdrawlinks-export-url', width: 600, dialogClass: 'ui-dialog-quickdrawlinks-export', title: self.title + ' URL' }); }; self.copydata = function() { if (self.linkcount() === 0) { alert('No drawn links'); return; } if (typeof android !== 'undefined' && android && android.shareString) { android.shareString(localStorage[self.localstoragelayer]); return; } var data = {}; data.layerdata = JSON.parse(localStorage[self.localstoragelayer]); data.titledata = JSON.parse(localStorage[self.localstoragetitlecache]); var html = '

Select all and press CTRL+C to copy it.

' + ''; window.dialog({ html: html, id: 'ui-dialog-quickdrawlinks-export-data', width: 600, dialogClass: 'ui-dialog-quickdrawlinks-export', title: self.title + ' Copy' }); }; self.pastedata = function(appenddata) { if (!appenddata && self.linkcount() !== 0 && !confirm('Are you sure you want to replace all data?')) return; var promptAction = prompt('Press CTRL+V to paste (' + (appenddata?'append':'replace') + ' ' + self.title + ' data or URL).', ''); if (promptAction === null || promptAction === '') return; try { // first see if it looks like a URL-format stock intel link, and if so, try and parse out any stock drawn items // from the pls parameter if (promptAction.match(new RegExp("^(https?://)?(www\\.)ingress\\.com/intel.*[?&]pls="))) { //looks like a ingress URL that has drawn items... var items = promptAction.split(/[?&]/); var foundAt = -1; var i; for (i=0; i b.toLowerCase()?1:0)); }); if (layers.length === 0) return 'nothing stored'; var list = []; if (!selectedlayer) selectedlayer = $('#quickdrawlinks_selectlayer option:selected').val(); if (!selectedlayer) selectedlayer = layers[0]; for (var index in layers) { var layername = layers[index]; list.push(''); } return ''; }; self.updatelayerlist = function(selectedlayer) { var newlist = self.projectselectlist(selectedlayer); if (newlist !== $('#quickdrawlinks_selectlayer').html()) $('#quickdrawlinks_selectlayer').replaceWith(newlist); }; self.deletestored = function() { var selectedlayer = $('#quickdrawlinks_selectlayer option:selected').val(); if (!selectedlayer) { alert('Nothing stored to delete'); return; } if (!confirm('Are you sure you want to delete stored data \'' + selectedlayer + '\'?')) return; var storeddata = self.getstoreddata(); delete(storeddata[selectedlayer]); localStorage[self.localstoragedata] = JSON.stringify(storeddata); self.updatelayerlist(); }; self.restore = function(replacelinks) { var selectedlayer = $('#quickdrawlinks_selectlayer option:selected').val(); if (!selectedlayer) { alert('Nothing to restore'); return; } var linkcount = self.linkcount(); if (linkcount !== 0) { if (replacelinks) { if (!confirm('Are you sure you want to replace all drawn links with stored data \'' + selectedlayer + '\'?')) return; } else { if (!confirm('Are you sure you want to add stored links from \'' + selectedlayer + '\' to current drawn links?')) return; } } var storeddata = self.getstoreddata(); if (replacelinks && linkcount) self.removeAll(true); self.import(storeddata[selectedlayer]); self.storelinks(); }; self.renamestored = function() { var selectedlayer = $('#quickdrawlinks_selectlayer option:selected').val(); if (!selectedlayer) { alert('Nothing to rename'); return; } var newname = prompt('Enter a new unique name for this layer:',selectedlayer); if (newname === null || newname === selectedlayer) return; if (newname === '') { alert('Layer name cannot be empty!'); return; } if (newname.match(/['"]/) !== null) { alert('Quote characters are not allowed in the layer name'); return; } var storeddata = self.getstoreddata(); if (storeddata[newname]) { alert('Layer name must be unique! \'' + newname + '\' already in use'); return; } storeddata[newname] = storeddata[selectedlayer]; delete(storeddata[selectedlayer]); localStorage[self.localstoragedata] = JSON.stringify(storeddata); self.updatelayerlist(newname); }; self.listportals = function() { if (self.linkcount() === 0) { alert('No drawn links'); return; } var drawlayer = self.getDrawlayer(); var listportalsindex = {}; var listportals = []; var linklengths = {}; drawlayer.eachLayer( function(layer) { //if (layer instanceof L.GeodesicPolyline && layer.getLatLngs().length === 2) { if (layer instanceof L.GeoJSON && layer.getLatLngs().length === 2) { var latLngs = layer.getLatLngs(); for (var cnt = 0; cnt < latLngs.length; cnt++) { var guid = self.getguid(latLngs[cnt]); var title = self.gettitle(latLngs[cnt]); if (title && guid) { var id = latLngs[cnt].lat + ',' + latLngs[cnt].lng; if (listportalsindex[id] === undefined) { listportalsindex[id] = listportals.length; listportals.push({id:id,title:title,guid:guid,countin:0,countout:0,outindex:[]}); linklengths[listportalsindex[id]] = {}; } if (cnt === 0) { listportals[listportalsindex[id]].countout++; var id1 = latLngs[1].lat + ',' + latLngs[1].lng; listportals[listportalsindex[id]].outindex.push(id1); linklengths[listportalsindex[id]][id1] = self.linklength(layer); } else { listportals[listportalsindex[id]].countin++; } } } } }); var linkout = []; // listportals sort must be AFTER linkout is created, because listportals are used by indexes for (var cnt = 0; cnt < listportals.length; cnt++) { var portalinfo = listportals[cnt]; for (var cnt2 = 0; cnt2 < listportals[cnt].outindex.length; cnt2++) { linkout.push(portalinfo.title + "\t" + listportals[listportalsindex[portalinfo.outindex[cnt2]]].title + "\t" + linklengths[cnt][portalinfo.outindex[cnt2]]); } } linkout.sort(); listportals.sort( function(a, b) { // reverse sort by countin (highest first) if (a.countin === b.countin) { return 0; } else { return (a.countin > b.countin) ? -1 : 1; } }); var baseurl = window.location.protocol + "//" + window.location.host; var linkcount = []; var locations = []; for (var cnt = 0; cnt < listportals.length; cnt++) { var portalinfo = listportals[cnt]; linkcount.push(portalinfo.title + "\t" + portalinfo.countin + "\t" + portalinfo.countout); locations.push(portalinfo.title + "\t" + baseurl + '/intel?ll=' + portalinfo.id + '&pll=' + portalinfo.id + "\t" + portalinfo.guid); } locations.sort(); var html = '

Select all and press CTRL+C to copy it.

' + ''; window.dialog({ html: html, id: 'ui-dialog-quickdrawlinks-export-data', width: 600, dialogClass: 'ui-dialog-quickdrawlinks-export', title: self.title + ' Copy' }); }; self.getDrawlayer = function() { // feature: implement multiple draw layers return self.drawnItems; }; self.getportalguidbylatlng = function(latlng) { for (var guid in window.portals) { var portallatlng = window.portals[guid].getLatLng(); if (latlng.lat === portallatlng.lat && latlng.lng === portallatlng.lng) { return guid; } } return null; }; self.addPortalBookmarkSetup = function() { if (!window.plugin.bookmarks) return; if (self.addPortalBookmark) return; var addPortalBookmark_override = window.plugin.bookmarks.addPortalBookmark.toString(); addPortalBookmark_override = addPortalBookmark_override.replace('window.runHooks','//window.runHooks'); // disable runHooks, just for this plugin eval(self.namespace + 'addPortalBookmark = ' + addPortalBookmark_override + ';'); } self.addcrosslinkbookmarks = function() { if (self.crosslinklayerdisabled) { alert('Quick Draw Cross Links layer is disabled, unable to draw bookmarks'); return; } if (self.linkcount() === 0) { alert('No drawn links, so no cross links, no bookmarks drawn'); return; } var drawlayer = self.getDrawlayer(); // getMapZoomTileParameters(getDataZoomForMapZoom(map.getZoom())).minLinkLength if (Object.keys(self.crosslinkLayerGuids).length === 0) { alert('No crosslinks found: no bookmarks drawn.\n\nBe aware: Crosslinks must be in visible range (and zoom level set at: all portals or all links)!'); return; } var countmissingportals = 0; var countnewbookmarks = 0; var countexistingbookmarks = 0; for (var crosslinkguid in self.crosslinkLayerGuids) { var latlngs = self.crosslinkLayerGuids[crosslinkguid].getLatLngs(); for (var cnt = 0; cnt < latlngs.length; cnt++) { var portalguid = self.getportalguidbylatlng(latlngs[cnt]); if (portalguid === null) { countmissingportals++; } else { var bookmarktitle = window.portals[portalguid].options.data.title || 'crosslink portal ' + portalguid; // draw new bookmark for portalguid var bkmrkData = window.plugin.bookmarks.findByGuid(portalguid); if (!bkmrkData) { var colorbackup; if (window.plugin.bookmarksAddon) { colorbackup = window.plugin.bookmarksAddon.settings.color; window.plugin.bookmarksAddon.settings.color = self.settings.crosslinkbookmarkcolor; } self.addPortalBookmark(portalguid, latlngs[cnt].lat + ',' + latlngs[cnt].lng, bookmarktitle); countnewbookmarks++; window.plugin.bookmarks.addStar(portalguid, latlngs[cnt], bookmarktitle); if (window.plugin.bookmarksAddon) { window.plugin.bookmarksAddon.settings.color = colorbackup; } } else { countexistingbookmarks++; } } } } alert('Visible crosslinks count: ' + Object.keys(self.crosslinkLayerGuids).length + '\nNew bookmarks count: ' + countnewbookmarks + '\nExisting bookmarks count: ' + countexistingbookmarks + '\nSkipped portal count: ' + countmissingportals + '\n\nBe aware: Crosslinks must be in visible range (and zoom level set at: all portals or all links)!'); }; self.storemenu = function() { var html = ''; if (window.useAndroidPanes()) window.show('map'); window.dialog({ html: $('
').append(html), id: self.pluginname + '-dialog', dialogClass: 'ui-dialog-quickdrawlinks', title: 'Quick Draw Store/Restore Projects' }); }; self.backupmenu = function() { var html = '

' + '< Main menu' + 'Export URL' + 'Export data (copy)' + 'Import data (paste)' + 'Append data (paste)' + 'List portals with drawn links' + (window.requestFile !== undefined ? 'Import file' : '') + ((typeof android !== 'undefined' && android && android.saveFile) ? 'Export file' : ''); if (window.useAndroidPanes()) window.show('map'); window.dialog({ html: html, id: self.pluginname + '-dialog', dialogClass: 'ui-dialog-quickdrawlinks', title: self.title + ' Import/Export' }); }; self.menu = function() { var html = '

' + ' Links Great circles
Fields
' + '
\n' + 'Zoom to view all drawn links' + 'Show link menu...' + 'Import/Export data...' + 'Store/Restore Projects...' + 'Delete fully visible links' + 'Remove all links' + (window.plugin.bookmarks?'Add bookmarks for crosslink portals
\n':'') + '
' + 'Show cross links on:
' + ' ' + ' ' + '
' + 'For selected portal:
\n' + 'Overview connected links' + 'Delete connected Drawn Links...' + 'Set all Drawn Links to outgoing' + 'Set all Drawn Links to incoming' + '
' + 'version ' + self.version + ' by ' + self.author + '' + '
'; if (window.useAndroidPanes()) { self.closedialog(); $('
').append(html).appendTo(document.body); } else { window.dialog({ html: $('
').append(html), id: self.pluginname + '-dialog', dialogClass: 'ui-dialog-quickdrawlinks', title: self.title }); } // need to initialise the 'spectrum' color picker var spectrumoptions = { flat: false, showInput: true, showButtons: true, showPalette: true, showSelectionPalette: true, allowEmpty: false, palette: [ ['#004000','#008000','#00C000'], ['#00FF00','#80FF80','#C0FFC0'], ['#000040','#000080','#0000C0'], ['#4040FF','#8080FF','#C0C0FF'], ['#6A3400','#964A00','#C05F00'], ['#E27000','#FF8309','#FFC287'], ['#a24ac3','#514ac3','#4aa8c3','#51c34a'], ['#c1c34a','#c38a4a','#c34a4a','#c34a6f'], ['#000000','#666666','#bbbbbb','#ffffff'] ]}; $('#quickdrawlinks_color').spectrum($.extend(true, spectrumoptions, { change: function(color) { self.setDrawColor(color.toHexString()); }, color: self.settings.drawcolor, })); $('#greatcircle_color').spectrum($.extend(true, spectrumoptions, { change: function(color) { self.settings.greatcirclecolor = color.toHexString(); self.storesettings(); self.updategreatcircleslayer(); }, color: self.settings.greatcirclecolor, })); $('#field_color').spectrum($.extend(true, spectrumoptions, { change: function(color) { self.settings.fieldcolor = color.toHexString(); self.storesettings(); self.updatefieldslayer(); }, color: self.settings.fieldcolor, })); }; self.onPaneChanged = function(pane) { if (pane === self.panename) self.menu(); else $("#quickdrawlinksdialog").remove(); }; self.setup_arc = function() { // source start: https://github.com/springmeyer/arc.js (Latest commit e30b63b on 6 Nov 2015) var D2R = Math.PI / 180; var R2D = 180 / Math.PI; var Coord = function(lon,lat) { this.lon = lon; this.lat = lat; this.x = D2R * lon; this.y = D2R * lat; }; Coord.prototype.view = function() { return String(this.lon).slice(0, 4) + ',' + String(this.lat).slice(0, 4); }; Coord.prototype.antipode = function() { var anti_lat = -1 * this.lat; var anti_lon = (this.lon < 0) ? 180 + this.lon : (180 - this.lon) * -1; return new Coord(anti_lon, anti_lat); }; var LineString = function() { this.coords = []; this.length = 0; }; LineString.prototype.move_to = function(coord) { this.length++; this.coords.push(coord); }; var Arc = function(properties) { this.properties = properties || {}; this.geometries = []; }; Arc.prototype.json = function() { if (this.geometries.length <= 0) { return {'geometry': { 'type': 'LineString', 'coordinates': null }, 'type': 'Feature', 'properties': this.properties }; } else if (this.geometries.length == 1) { return {'geometry': { 'type': 'LineString', 'coordinates': this.geometries[0].coords }, 'type': 'Feature', 'properties': this.properties }; } else { var multiline = []; for (var i = 0; i < this.geometries.length; i++) { multiline.push(this.geometries[i].coords); } return {'geometry': { 'type': 'MultiLineString', 'coordinates': multiline }, 'type': 'Feature', 'properties': this.properties }; } }; // TODO - output proper multilinestring Arc.prototype.wkt = function() { var wkt_string = ''; var wkt = 'LINESTRING('; var collect = function(c) { wkt += c[0] + ' ' + c[1] + ','; }; for (var i = 0; i < this.geometries.length; i++) { if (this.geometries[i].coords.length === 0) { return 'LINESTRING(empty)'; } else { var coords = this.geometries[i].coords; coords.forEach(collect); wkt_string += wkt.substring(0, wkt.length - 1) + ')'; } } return wkt_string; }; /* * http://en.wikipedia.org/wiki/Great-circle_distance * */ var GreatCircle = function(start,end,properties) { if (!start || start.x === undefined || start.y === undefined) { throw new Error("GreatCircle constructor expects two args: start and end objects with x and y properties"); } if (!end || end.x === undefined || end.y === undefined) { throw new Error("GreatCircle constructor expects two args: start and end objects with x and y properties"); } this.start = start; //new Coord(start.x,start.y); // FIXED source to match plugin this.end = end; //new Coord(end.x,end.y); // FIXED source to match plugin this.properties = properties || {}; var w = this.start.x - this.end.x; var h = this.start.y - this.end.y; var z = Math.pow(Math.sin(h / 2.0), 2) + Math.cos(this.start.y) * Math.cos(this.end.y) * Math.pow(Math.sin(w / 2.0), 2); this.g = 2.0 * Math.asin(Math.sqrt(z)); if (this.g == Math.PI) { throw new Error('it appears ' + start.view() + ' and ' + end.view() + " are 'antipodal', e.g diametrically opposite, thus there is no single route but rather infinite"); } else if (isNaN(this.g)) { throw new Error('could not calculate great circle between ' + start + ' and ' + end); } }; /* * http://williams.best.vwh.net/avform.htm#Intermediate */ GreatCircle.prototype.interpolate = function(f) { var A = Math.sin((1 - f) * this.g) / Math.sin(this.g); var B = Math.sin(f * this.g) / Math.sin(this.g); var x = A * Math.cos(this.start.y) * Math.cos(this.start.x) + B * Math.cos(this.end.y) * Math.cos(this.end.x); var y = A * Math.cos(this.start.y) * Math.sin(this.start.x) + B * Math.cos(this.end.y) * Math.sin(this.end.x); var z = A * Math.sin(this.start.y) + B * Math.sin(this.end.y); var lat = R2D * Math.atan2(z, Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2))); var lon = R2D * Math.atan2(y, x); return [lon, lat]; }; /* * Generate points along the great circle */ GreatCircle.prototype.Arc = function(npoints,options) { var first_pass = []; if (!npoints || npoints <= 2) { first_pass.push([this.start.lon, this.start.lat]); first_pass.push([this.end.lon, this.end.lat]); } else { var delta = 1.0 / (npoints - 1); for (var i = 0; i < npoints; ++i) { var step = delta * i; var pair = this.interpolate(step); first_pass.push(pair); } } /* partial port of dateline handling from: gdal/ogr/ogrgeometryfactory.cpp TODO - does not handle all wrapping scenarios yet */ var bHasBigDiff = false; var dfMaxSmallDiffLong = 0; // from http://www.gdal.org/ogr2ogr.html // -datelineoffset: // (starting with GDAL 1.10) offset from dateline in degrees (default long. = +/- 10deg, geometries within 170deg to -170deg will be splited) var dfDateLineOffset = options && options.offset ? options.offset : 10; var dfLeftBorderX = 180 - dfDateLineOffset; var dfRightBorderX = -180 + dfDateLineOffset; var dfDiffSpace = 360 - dfDateLineOffset; // https://github.com/OSGeo/gdal/blob/7bfb9c452a59aac958bff0c8386b891edf8154ca/gdal/ogr/ogrgeometryfactory.cpp#L2342 for (var j = 1; j < first_pass.length; ++j) { var dfPrevX = first_pass[j-1][0]; var dfX = first_pass[j][0]; var dfDiffLong = Math.abs(dfX - dfPrevX); if (dfDiffLong > dfDiffSpace && ((dfX > dfLeftBorderX && dfPrevX < dfRightBorderX) || (dfPrevX > dfLeftBorderX && dfX < dfRightBorderX))) { bHasBigDiff = true; } else if (dfDiffLong > dfMaxSmallDiffLong) { dfMaxSmallDiffLong = dfDiffLong; } } var poMulti = []; if (bHasBigDiff && dfMaxSmallDiffLong < dfDateLineOffset) { var poNewLS = []; poMulti.push(poNewLS); for (var k = 0; k < first_pass.length; ++k) { var dfX0 = parseFloat(first_pass[k][0]); if (k > 0 && Math.abs(dfX0 - first_pass[k-1][0]) > dfDiffSpace) { var dfX1 = parseFloat(first_pass[k-1][0]); var dfY1 = parseFloat(first_pass[k-1][1]); var dfX2 = parseFloat(first_pass[k][0]); var dfY2 = parseFloat(first_pass[k][1]); if (dfX1 > -180 && dfX1 < dfRightBorderX && dfX2 == 180 && k+1 < first_pass.length && first_pass[k-1][0] > -180 && first_pass[k-1][0] < dfRightBorderX) { poNewLS.push([-180, first_pass[k][1]]); k++; poNewLS.push([first_pass[k][0], first_pass[k][1]]); continue; } else if (dfX1 > dfLeftBorderX && dfX1 < 180 && dfX2 == -180 && k+1 < first_pass.length && first_pass[k-1][0] > dfLeftBorderX && first_pass[k-1][0] < 180) { poNewLS.push([180, first_pass[k][1]]); k++; poNewLS.push([first_pass[k][0], first_pass[k][1]]); continue; } if (dfX1 < dfRightBorderX && dfX2 > dfLeftBorderX) { // swap dfX1, dfX2 var tmpX = dfX1; dfX1 = dfX2; dfX2 = tmpX; // swap dfY1, dfY2 var tmpY = dfY1; dfY1 = dfY2; dfY2 = tmpY; } if (dfX1 > dfLeftBorderX && dfX2 < dfRightBorderX) { dfX2 += 360; } if (dfX1 <= 180 && dfX2 >= 180 && dfX1 < dfX2) { var dfRatio = (180 - dfX1) / (dfX2 - dfX1); var dfY = dfRatio * dfY2 + (1 - dfRatio) * dfY1; poNewLS.push([first_pass[k-1][0] > dfLeftBorderX ? 180 : -180, dfY]); poNewLS = []; poNewLS.push([first_pass[k-1][0] > dfLeftBorderX ? -180 : 180, dfY]); poMulti.push(poNewLS); } else { poNewLS = []; poMulti.push(poNewLS); } poNewLS.push([dfX0, first_pass[k][1]]); } else { poNewLS.push([first_pass[k][0], first_pass[k][1]]); } } } else { // add normally var poNewLS0 = []; poMulti.push(poNewLS0); for (var l = 0; l < first_pass.length; ++l) { poNewLS0.push([first_pass[l][0],first_pass[l][1]]); } } var arc = new Arc(this.properties); for (var m = 0; m < poMulti.length; ++m) { var line = new LineString(); arc.geometries.push(line); var points = poMulti[m]; for (var j0 = 0; j0 < points.length; ++j0) { line.move_to(points[j0]); } } return arc; }; // source arc.js end self.arc = {}; self.arc.Coord = Coord; self.arc.Arc = Arc; self.arc.GreatCircle = GreatCircle; }; // end setup_arc self.setupColorpickerSpectrum = function() { // source: https://github.com/bgrins/spectrum // minified with https://www.minifier.org/ // Spectrum Colorpicker v1.8.1 // https://github.com/bgrins/spectrum // Author: Brian Grinstead // License: MIT (function(factory){"use strict";if(typeof define==='function'&&define.amd){define(['jquery'],factory)}else if(typeof exports=="object"&&typeof module=="object"){module.exports=factory(require('jquery'))}else{factory(jQuery)}})(function($,undefined){"use strict";var defaultOpts={beforeShow:noop,move:noop,change:noop,show:noop,hide:noop,color:!1,flat:!1,showInput:!1,allowEmpty:!1,showButtons:!0,clickoutFiresChange:!0,showInitial:!1,showPalette:!1,showPaletteOnly:!1,hideAfterPaletteSelect:!1,togglePaletteOnly:!1,showSelectionPalette:!0,localStorageKey:!1,appendTo:"body",maxSelectionSize:7,cancelText:"cancel",chooseText:"choose",togglePaletteMoreText:"more",togglePaletteLessText:"less",clearText:"Clear Color Selection",noColorSelectedText:"No Color Selected",preferredFormat:!1,className:"",containerClassName:"",replacerClassName:"",showAlpha:!1,theme:"sp-light",palette:[["#ffffff","#000000","#ff0000","#ff8000","#ffff00","#008000","#0000ff","#4b0082","#9400d3"]],selectionPalette:[],disabled:!1,offset:null},spectrums=[],IE=!!/msie/i.exec(window.navigator.userAgent),rgbaSupport=(function(){function contains(str,substr){return!!~(''+str).indexOf(substr)} var elem=document.createElement('div');var style=elem.style;style.cssText='background-color:rgba(0,0,0,.5)';return contains(style.backgroundColor,'rgba')||contains(style.backgroundColor,'hsla')})(),replaceInput=["
","
","
","
"].join(''),markup=(function(){var gradientFix="";if(IE){for(var i=1;i<=6;i++){gradientFix+="
"}} return["
","
","
","
","","
","
","
","
","
","
","
","
","
","
","
","
","
","
","
","
","
",gradientFix,"
","
","
","
","
","","
","
","
","","","
","
","
"].join("")})();function paletteTemplate(p,color,className,opts){var html=[];for(var i=0;i')}else{var cls='sp-clear-display';html.push($('
').append($('').attr('title',opts.noColorSelectedText)).html())}} return"
"+html.join('')+"
"} function hideAll(){for(var i=0;iMath.abs(dragY-oldDragY);shiftMovementDirection=furtherFromX?"x":"y"} var setSaturation=!shiftMovementDirection||shiftMovementDirection==="x";var setValue=!shiftMovementDirection||shiftMovementDirection==="y";if(setSaturation){currentSaturation=parseFloat(dragX/dragWidth)} if(setValue){currentValue=parseFloat((dragHeight-dragY)/dragHeight)} isEmpty=!1;if(!opts.showAlpha){currentAlpha=1} move()},dragStart,dragStop);if(!!initialColor){set(initialColor);updateUI();currentPreferredFormat=opts.preferredFormat||tinycolor(initialColor).format;addColorToSelectionPalette(initialColor)}else{updateUI()} if(flat){show()} function paletteElementClick(e){if(e.data&&e.data.ignore){set($(e.target).closest(".sp-thumb-el").data("color"));move()}else{set($(e.target).closest(".sp-thumb-el").data("color"));move();if(opts.hideAfterPaletteSelect){updateOriginalInput(!0);hide()}else{updateOriginalInput()}} return!1} var paletteEvent=IE?"mousedown.spectrum":"click.spectrum touchstart.spectrum";paletteContainer.on(paletteEvent,".sp-thumb-el",paletteElementClick);initialColorContainer.on(paletteEvent,".sp-thumb-el:nth-child(1)",{ignore:!0},paletteElementClick)} function updateSelectionPaletteFromStorage(){if(localStorageKey&&window.localStorage){try{var oldPalette=window.localStorage[localStorageKey].split(",#");if(oldPalette.length>1){delete window.localStorage[localStorageKey];$.each(oldPalette,function(i,c){addColorToSelectionPalette(c)})}}catch(e){} try{selectionPalette=window.localStorage[localStorageKey].split(";")}catch(e){}}} function addColorToSelectionPalette(color){if(showSelectionPalette){var rgb=tinycolor(color).toRgbString();if(!paletteLookup[rgb]&&$.inArray(rgb,selectionPalette)===-1){selectionPalette.push(rgb);while(selectionPalette.length>maxSelectionSize){selectionPalette.shift()}} if(localStorageKey&&window.localStorage){try{window.localStorage[localStorageKey]=selectionPalette.join(";")}catch(e){}}}} function getUniqueSelectionPalette(){var unique=[];if(opts.showPalette){for(var i=0;iviewWidth&&viewWidth>dpWidth)?Math.abs(offsetLeft+dpWidth-viewWidth):0);offsetTop-=Math.min(offsetTop,((offsetTop+dpHeight>viewHeight&&viewHeight>dpHeight)?Math.abs(dpHeight+inputHeight-extraY):extraY));return{top:offsetTop,bottom:offset.bottom,left:offsetLeft,right:offset.right,width:offset.width,height:offset.height}} function noop(){} function stopPropagation(e){e.stopPropagation()} function bind(func,obj){var slice=Array.prototype.slice;var args=slice.call(arguments,2);return function(){return func.apply(obj,args.concat(slice.call(arguments)))}} function draggable(element,onmove,onstart,onstop){onmove=onmove||function(){};onstart=onstart||function(){};onstop=onstop||function(){};var doc=document;var dragging=!1;var offset={};var maxHeight=0;var maxWidth=0;var hasTouch=('ontouchstart' in window);var duringDragEvents={};duringDragEvents.selectstart=prevent;duringDragEvents.dragstart=prevent;duringDragEvents["touchmove mousemove"]=move;duringDragEvents["touchend mouseup"]=stop;function prevent(e){if(e.stopPropagation){e.stopPropagation()} if(e.preventDefault){e.preventDefault()} e.returnValue=!1} function move(e){if(dragging){if(IE&&doc.documentMode<9&&!e.button){return stop()} var t0=e.originalEvent&&e.originalEvent.touches&&e.originalEvent.touches[0];var pageX=t0&&t0.pageX||e.pageX;var pageY=t0&&t0.pageY||e.pageY;var dragX=Math.max(0,Math.min(pageX-offset.left,maxWidth));var dragY=Math.max(0,Math.min(pageY-offset.top,maxHeight));if(hasTouch){prevent(e)} onmove.apply(element,[dragX,dragY,e])}} function start(e){var rightclick=(e.which)?(e.which==3):(e.button==2);if(!rightclick&&!dragging){if(onstart.apply(element,arguments)!==!1){dragging=!0;maxHeight=$(element).height();maxWidth=$(element).width();offset=$(element).offset();$(doc).on(duringDragEvents);$(doc.body).addClass("sp-dragging");move(e);prevent(e)}}} function stop(){if(dragging){$(doc).off(duringDragEvents);$(doc.body).removeClass("sp-dragging");setTimeout(function(){onstop.apply(element,arguments)},0)} dragging=!1} $(element).on("touchstart mousedown",start)} function throttle(func,wait,debounce){var timeout;return function(){var context=this,args=arguments;var throttler=function(){timeout=null;func.apply(context,args)};if(debounce)clearTimeout(timeout);if(debounce||!timeout)timeout=setTimeout(throttler,wait)}} function inputTypeColorSupport(){return $.fn.spectrum.inputTypeColorSupport()} var dataID="spectrum.id";$.fn.spectrum=function(opts,extra){if(typeof opts=="string"){var returnValue=this;var args=Array.prototype.slice.call(arguments,1);this.each(function(){var spect=spectrums[$(this).data(dataID)];if(spect){var method=spect[opts];if(!method){throw new Error("Spectrum: no such method: '"+opts+"'")} if(opts=="get"){returnValue=spect.get()}else if(opts=="container"){returnValue=spect.container}else if(opts=="option"){returnValue=spect.option.apply(spect,args)}else if(opts=="destroy"){spect.destroy();$(this).removeData(dataID)}else{method.apply(spect,args)}}});return returnValue} return this.spectrum("destroy").each(function(){var options=$.extend({},$(this).data(),opts);var spect=spectrum(this,options);$(this).data(dataID,spect.id)})};$.fn.spectrum.load=!0;$.fn.spectrum.loadOpts={};$.fn.spectrum.draggable=draggable;$.fn.spectrum.defaults=defaultOpts;$.fn.spectrum.inputTypeColorSupport=function inputTypeColorSupport(){if(typeof inputTypeColorSupport._cachedResult==="undefined"){var colorInput=$("")[0];inputTypeColorSupport._cachedResult=colorInput.type==="color"&&colorInput.value!==""} return inputTypeColorSupport._cachedResult};$.spectrum={};$.spectrum.localization={};$.spectrum.palettes={};$.fn.spectrum.processNativeColorInputs=function(){var colorInputs=$("input[type=color]");if(colorInputs.length&&!inputTypeColorSupport()){colorInputs.spectrum({preferredFormat:"hex6"})}};(function(){var trimLeft=/^[\s,#]+/,trimRight=/\s+$/,tinyCounter=0,math=Math,mathRound=math.round,mathMin=math.min,mathMax=math.max,mathRandom=math.random;var tinycolor=function(color,opts){color=(color)?color:'';opts=opts||{};if(color instanceof tinycolor){return color} if(!(this instanceof tinycolor)){return new tinycolor(color,opts)} var rgb=inputToRGB(color);this._originalInput=color;this._r=rgb.r;this._g=rgb.g;this._b=rgb.b;this._a=rgb.a;this._roundA=mathRound(1000*this._a)/1000;this._format=opts.format||rgb.format;this._gradientType=opts.gradientType;if(this._r<1){this._r=mathRound(this._r)} if(this._g<1){this._g=mathRound(this._g)} if(this._b<1){this._b=mathRound(this._b)} this._ok=rgb.ok;this._tc_id=tinyCounter++};tinycolor.prototype={isDark:function(){return this.getBrightness()<128},isLight:function(){return!this.isDark()},isValid:function(){return this._ok},getOriginalInput:function(){return this._originalInput},getFormat:function(){return this._format},getAlpha:function(){return this._a},getBrightness:function(){var rgb=this.toRgb();return(rgb.r*299+rgb.g*587+rgb.b*114)/1000},setAlpha:function(value){this._a=boundAlpha(value);this._roundA=mathRound(1000*this._a)/1000;return this},toHsv:function(){var hsv=rgbToHsv(this._r,this._g,this._b);return{h:hsv.h*360,s:hsv.s,v:hsv.v,a:this._a}},toHsvString:function(){var hsv=rgbToHsv(this._r,this._g,this._b);var h=mathRound(hsv.h*360),s=mathRound(hsv.s*100),v=mathRound(hsv.v*100);return(this._a==1)?"hsv("+h+", "+s+"%, "+v+"%)":"hsva("+h+", "+s+"%, "+v+"%, "+this._roundA+")"},toHsl:function(){var hsl=rgbToHsl(this._r,this._g,this._b);return{h:hsl.h*360,s:hsl.s,l:hsl.l,a:this._a}},toHslString:function(){var hsl=rgbToHsl(this._r,this._g,this._b);var h=mathRound(hsl.h*360),s=mathRound(hsl.s*100),l=mathRound(hsl.l*100);return(this._a==1)?"hsl("+h+", "+s+"%, "+l+"%)":"hsla("+h+", "+s+"%, "+l+"%, "+this._roundA+")"},toHex:function(allow3Char){return rgbToHex(this._r,this._g,this._b,allow3Char)},toHexString:function(allow3Char){return'#'+this.toHex(allow3Char)},toHex8:function(){return rgbaToHex(this._r,this._g,this._b,this._a)},toHex8String:function(){return'#'+this.toHex8()},toRgb:function(){return{r:mathRound(this._r),g:mathRound(this._g),b:mathRound(this._b),a:this._a}},toRgbString:function(){return(this._a==1)?"rgb("+mathRound(this._r)+", "+mathRound(this._g)+", "+mathRound(this._b)+")":"rgba("+mathRound(this._r)+", "+mathRound(this._g)+", "+mathRound(this._b)+", "+this._roundA+")"},toPercentageRgb:function(){return{r:mathRound(bound01(this._r,255)*100)+"%",g:mathRound(bound01(this._g,255)*100)+"%",b:mathRound(bound01(this._b,255)*100)+"%",a:this._a}},toPercentageRgbString:function(){return(this._a==1)?"rgb("+mathRound(bound01(this._r,255)*100)+"%, "+mathRound(bound01(this._g,255)*100)+"%, "+mathRound(bound01(this._b,255)*100)+"%)":"rgba("+mathRound(bound01(this._r,255)*100)+"%, "+mathRound(bound01(this._g,255)*100)+"%, "+mathRound(bound01(this._b,255)*100)+"%, "+this._roundA+")"},toName:function(){if(this._a===0){return"transparent"} if(this._a<1){return!1} return hexNames[rgbToHex(this._r,this._g,this._b,!0)]||!1},toFilter:function(secondColor){var hex8String='#'+rgbaToHex(this._r,this._g,this._b,this._a);var secondHex8String=hex8String;var gradientType=this._gradientType?"GradientType = 1, ":"";if(secondColor){var s=tinycolor(secondColor);secondHex8String=s.toHex8String()} return"progid:DXImageTransform.Microsoft.gradient("+gradientType+"startColorstr="+hex8String+",endColorstr="+secondHex8String+")"},toString:function(format){var formatSet=!!format;format=format||this._format;var formattedString=!1;var hasAlpha=this._a<1&&this._a>=0;var needsAlphaFormat=!formatSet&&hasAlpha&&(format==="hex"||format==="hex6"||format==="hex3"||format==="name");if(needsAlphaFormat){if(format==="name"&&this._a===0){return this.toName()} return this.toRgbString()} if(format==="rgb"){formattedString=this.toRgbString()} if(format==="prgb"){formattedString=this.toPercentageRgbString()} if(format==="hex"||format==="hex6"){formattedString=this.toHexString()} if(format==="hex3"){formattedString=this.toHexString(!0)} if(format==="hex8"){formattedString=this.toHex8String()} if(format==="name"){formattedString=this.toName()} if(format==="hsl"){formattedString=this.toHslString()} if(format==="hsv"){formattedString=this.toHsvString()} return formattedString||this.toHexString()},_applyModification:function(fn,args){var color=fn.apply(null,[this].concat([].slice.call(args)));this._r=color._r;this._g=color._g;this._b=color._b;this.setAlpha(color._a);return this},lighten:function(){return this._applyModification(lighten,arguments)},brighten:function(){return this._applyModification(brighten,arguments)},darken:function(){return this._applyModification(darken,arguments)},desaturate:function(){return this._applyModification(desaturate,arguments)},saturate:function(){return this._applyModification(saturate,arguments)},greyscale:function(){return this._applyModification(greyscale,arguments)},spin:function(){return this._applyModification(spin,arguments)},_applyCombination:function(fn,args){return fn.apply(null,[this].concat([].slice.call(args)))},analogous:function(){return this._applyCombination(analogous,arguments)},complement:function(){return this._applyCombination(complement,arguments)},monochromatic:function(){return this._applyCombination(monochromatic,arguments)},splitcomplement:function(){return this._applyCombination(splitcomplement,arguments)},triad:function(){return this._applyCombination(triad,arguments)},tetrad:function(){return this._applyCombination(tetrad,arguments)}};tinycolor.fromRatio=function(color,opts){if(typeof color=="object"){var newColor={};for(var i in color){if(color.hasOwnProperty(i)){if(i==="a"){newColor[i]=color[i]}else{newColor[i]=convertToPercentage(color[i])}}} color=newColor} return tinycolor(color,opts)};function inputToRGB(color){var rgb={r:0,g:0,b:0};var a=1;var ok=!1;var format=!1;if(typeof color=="string"){color=stringInputToObject(color)} if(typeof color=="object"){if(color.hasOwnProperty("r")&&color.hasOwnProperty("g")&&color.hasOwnProperty("b")){rgb=rgbToRgb(color.r,color.g,color.b);ok=!0;format=String(color.r).substr(-1)==="%"?"prgb":"rgb"}else if(color.hasOwnProperty("h")&&color.hasOwnProperty("s")&&color.hasOwnProperty("v")){color.s=convertToPercentage(color.s);color.v=convertToPercentage(color.v);rgb=hsvToRgb(color.h,color.s,color.v);ok=!0;format="hsv"}else if(color.hasOwnProperty("h")&&color.hasOwnProperty("s")&&color.hasOwnProperty("l")){color.s=convertToPercentage(color.s);color.l=convertToPercentage(color.l);rgb=hslToRgb(color.h,color.s,color.l);ok=!0;format="hsl"} if(color.hasOwnProperty("a")){a=color.a}} a=boundAlpha(a);return{ok:ok,format:color.format||format,r:mathMin(255,mathMax(rgb.r,0)),g:mathMin(255,mathMax(rgb.g,0)),b:mathMin(255,mathMax(rgb.b,0)),a:a}} function rgbToRgb(r,g,b){return{r:bound01(r,255)*255,g:bound01(g,255)*255,b:bound01(b,255)*255}} function rgbToHsl(r,g,b){r=bound01(r,255);g=bound01(g,255);b=bound01(b,255);var max=mathMax(r,g,b),min=mathMin(r,g,b);var h,s,l=(max+min)/2;if(max==min){h=s=0}else{var d=max-min;s=l>0.5?d/(2-max-min):d/(max+min);switch(max){case r:h=(g-b)/d+(g1)t-=1;if(t<1/6)return p+(q-p)*6*t;if(t<1/2)return q;if(t<2/3)return p+(q-p)*(2/3-t)*6;return p} if(s===0){r=g=b=l}else{var q=l<0.5?l*(1+s):l+s-l*s;var p=2*l-q;r=hue2rgb(p,q,h+1/3);g=hue2rgb(p,q,h);b=hue2rgb(p,q,h-1/3)} return{r:r*255,g:g*255,b:b*255}} function rgbToHsv(r,g,b){r=bound01(r,255);g=bound01(g,255);b=bound01(b,255);var max=mathMax(r,g,b),min=mathMin(r,g,b);var h,s,v=max;var d=max-min;s=max===0?0:d/max;if(max==min){h=0}else{switch(max){case r:h=(g-b)/d+(g>1))+720)%360;--results;){hsl.h=(hsl.h+part)%360;ret.push(tinycolor(hsl))} return ret} function monochromatic(color,results){results=results||6;var hsv=tinycolor(color).toHsv();var h=hsv.h,s=hsv.s,v=hsv.v;var ret=[];var modification=1/results;while(results--){ret.push(tinycolor({h:h,s:s,v:v}));v=(v+modification)%1} return ret} tinycolor.mix=function(color1,color2,amount){amount=(amount===0)?0:(amount||50);var rgb1=tinycolor(color1).toRgb();var rgb2=tinycolor(color2).toRgb();var p=amount/100;var w=p*2-1;var a=rgb2.a-rgb1.a;var w1;if(w*a==-1){w1=w}else{w1=(w+a)/(1+w*a)} w1=(w1+1)/2;var w2=1-w1;var rgba={r:rgb2.r*w1+rgb1.r*w2,g:rgb2.g*w1+rgb1.g*w2,b:rgb2.b*w1+rgb1.b*w2,a:rgb2.a*p+rgb1.a*(1-p)};return tinycolor(rgba)};tinycolor.readability=function(color1,color2){var c1=tinycolor(color1);var c2=tinycolor(color2);var rgb1=c1.toRgb();var rgb2=c2.toRgb();var brightnessA=c1.getBrightness();var brightnessB=c2.getBrightness();var colorDiff=(Math.max(rgb1.r,rgb2.r)-Math.min(rgb1.r,rgb2.r)+Math.max(rgb1.g,rgb2.g)-Math.min(rgb1.g,rgb2.g)+Math.max(rgb1.b,rgb2.b)-Math.min(rgb1.b,rgb2.b));return{brightness:Math.abs(brightnessA-brightnessB),color:colorDiff}};tinycolor.isReadable=function(color1,color2){var readability=tinycolor.readability(color1,color2);return readability.brightness>125&&readability.color>500};tinycolor.mostReadable=function(baseColor,colorList){var bestColor=null;var bestScore=0;var bestIsReadable=!1;for(var i=0;i125&&readability.color>500;var score=3*(readability.brightness/125)+(readability.color/500);if((readable&&!bestIsReadable)||(readable&&bestIsReadable&&score>bestScore)||((!readable)&&(!bestIsReadable)&&score>bestScore)){bestIsReadable=readable;bestScore=score;bestColor=tinycolor(colorList[i])}} return bestColor};var names=tinycolor.names={aliceblue:"f0f8ff",antiquewhite:"faebd7",aqua:"0ff",aquamarine:"7fffd4",azure:"f0ffff",beige:"f5f5dc",bisque:"ffe4c4",black:"000",blanchedalmond:"ffebcd",blue:"00f",blueviolet:"8a2be2",brown:"a52a2a",burlywood:"deb887",burntsienna:"ea7e5d",cadetblue:"5f9ea0",chartreuse:"7fff00",chocolate:"d2691e",coral:"ff7f50",cornflowerblue:"6495ed",cornsilk:"fff8dc",crimson:"dc143c",cyan:"0ff",darkblue:"00008b",darkcyan:"008b8b",darkgoldenrod:"b8860b",darkgray:"a9a9a9",darkgreen:"006400",darkgrey:"a9a9a9",darkkhaki:"bdb76b",darkmagenta:"8b008b",darkolivegreen:"556b2f",darkorange:"ff8c00",darkorchid:"9932cc",darkred:"8b0000",darksalmon:"e9967a",darkseagreen:"8fbc8f",darkslateblue:"483d8b",darkslategray:"2f4f4f",darkslategrey:"2f4f4f",darkturquoise:"00ced1",darkviolet:"9400d3",deeppink:"ff1493",deepskyblue:"00bfff",dimgray:"696969",dimgrey:"696969",dodgerblue:"1e90ff",firebrick:"b22222",floralwhite:"fffaf0",forestgreen:"228b22",fuchsia:"f0f",gainsboro:"dcdcdc",ghostwhite:"f8f8ff",gold:"ffd700",goldenrod:"daa520",gray:"808080",green:"008000",greenyellow:"adff2f",grey:"808080",honeydew:"f0fff0",hotpink:"ff69b4",indianred:"cd5c5c",indigo:"4b0082",ivory:"fffff0",khaki:"f0e68c",lavender:"e6e6fa",lavenderblush:"fff0f5",lawngreen:"7cfc00",lemonchiffon:"fffacd",lightblue:"add8e6",lightcoral:"f08080",lightcyan:"e0ffff",lightgoldenrodyellow:"fafad2",lightgray:"d3d3d3",lightgreen:"90ee90",lightgrey:"d3d3d3",lightpink:"ffb6c1",lightsalmon:"ffa07a",lightseagreen:"20b2aa",lightskyblue:"87cefa",lightslategray:"789",lightslategrey:"789",lightsteelblue:"b0c4de",lightyellow:"ffffe0",lime:"0f0",limegreen:"32cd32",linen:"faf0e6",magenta:"f0f",maroon:"800000",mediumaquamarine:"66cdaa",mediumblue:"0000cd",mediumorchid:"ba55d3",mediumpurple:"9370db",mediumseagreen:"3cb371",mediumslateblue:"7b68ee",mediumspringgreen:"00fa9a",mediumturquoise:"48d1cc",mediumvioletred:"c71585",midnightblue:"191970",mintcream:"f5fffa",mistyrose:"ffe4e1",moccasin:"ffe4b5",navajowhite:"ffdead",navy:"000080",oldlace:"fdf5e6",olive:"808000",olivedrab:"6b8e23",orange:"ffa500",orangered:"ff4500",orchid:"da70d6",palegoldenrod:"eee8aa",palegreen:"98fb98",paleturquoise:"afeeee",palevioletred:"db7093",papayawhip:"ffefd5",peachpuff:"ffdab9",peru:"cd853f",pink:"ffc0cb",plum:"dda0dd",powderblue:"b0e0e6",purple:"800080",rebeccapurple:"663399",red:"f00",rosybrown:"bc8f8f",royalblue:"4169e1",saddlebrown:"8b4513",salmon:"fa8072",sandybrown:"f4a460",seagreen:"2e8b57",seashell:"fff5ee",sienna:"a0522d",silver:"c0c0c0",skyblue:"87ceeb",slateblue:"6a5acd",slategray:"708090",slategrey:"708090",snow:"fffafa",springgreen:"00ff7f",steelblue:"4682b4",tan:"d2b48c",teal:"008080",thistle:"d8bfd8",tomato:"ff6347",turquoise:"40e0d0",violet:"ee82ee",wheat:"f5deb3",white:"fff",whitesmoke:"f5f5f5",yellow:"ff0",yellowgreen:"9acd32"};var hexNames=tinycolor.hexNames=flip(names);function flip(o){var flipped={};for(var i in o){if(o.hasOwnProperty(i)){flipped[o[i]]=i}} return flipped} function boundAlpha(a){a=parseFloat(a);if(isNaN(a)||a<0||a>1){a=1} return a} function bound01(n,max){if(isOnePointZero(n)){n="100%"} var processPercent=isPercentage(n);n=mathMin(max,mathMax(0,parseFloat(n)));if(processPercent){n=parseInt(n*max,10)/100} if((math.abs(n-max)<0.000001)){return 1} return(n%max)/parseFloat(max)} function clamp01(val){return mathMin(1,mathMax(0,val))} function parseIntFromHex(val){return parseInt(val,16)} function isOnePointZero(n){return typeof n=="string"&&n.indexOf('.')!=-1&&parseFloat(n)===1} function isPercentage(n){return typeof n==="string"&&n.indexOf('%')!=-1} function pad2(c){return c.length==1?'0'+c:''+c} function convertToPercentage(n){if(n<=1){n=(n*100)+"%"} return n} function convertDecimalToHex(d){return Math.round(parseFloat(d)*255).toString(16)} function convertHexToDecimal(h){return(parseIntFromHex(h)/255)} var matchers=(function(){var CSS_INTEGER="[-\\+]?\\d+%?";var CSS_NUMBER="[-\\+]?\\d*\\.\\d+%?";var CSS_UNIT="(?:"+CSS_NUMBER+")|(?:"+CSS_INTEGER+")";var PERMISSIVE_MATCH3="[\\s|\\(]+("+CSS_UNIT+")[,|\\s]+("+CSS_UNIT+")[,|\\s]+("+CSS_UNIT+")\\s*\\)?";var PERMISSIVE_MATCH4="[\\s|\\(]+("+CSS_UNIT+")[,|\\s]+("+CSS_UNIT+")[,|\\s]+("+CSS_UNIT+")[,|\\s]+("+CSS_UNIT+")\\s*\\)?";return{rgb:new RegExp("rgb"+PERMISSIVE_MATCH3),rgba:new RegExp("rgba"+PERMISSIVE_MATCH4),hsl:new RegExp("hsl"+PERMISSIVE_MATCH3),hsla:new RegExp("hsla"+PERMISSIVE_MATCH4),hsv:new RegExp("hsv"+PERMISSIVE_MATCH3),hsva:new RegExp("hsva"+PERMISSIVE_MATCH4),hex3:/^([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/,hex6:/^([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/,hex8:/^([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/}})();function stringInputToObject(color){color=color.replace(trimLeft,'').replace(trimRight,'').toLowerCase();var named=!1;if(names[color]){color=names[color];named=!0}else if(color=='transparent'){return{r:0,g:0,b:0,a:0,format:"name"}} var match;if((match=matchers.rgb.exec(color))){return{r:match[1],g:match[2],b:match[3]}} if((match=matchers.rgba.exec(color))){return{r:match[1],g:match[2],b:match[3],a:match[4]}} if((match=matchers.hsl.exec(color))){return{h:match[1],s:match[2],l:match[3]}} if((match=matchers.hsla.exec(color))){return{h:match[1],s:match[2],l:match[3],a:match[4]}} if((match=matchers.hsv.exec(color))){return{h:match[1],s:match[2],v:match[3]}} if((match=matchers.hsva.exec(color))){return{h:match[1],s:match[2],v:match[3],a:match[4]}} if((match=matchers.hex8.exec(color))){return{a:convertHexToDecimal(match[1]),r:parseIntFromHex(match[2]),g:parseIntFromHex(match[3]),b:parseIntFromHex(match[4]),format:named?"name":"hex8"}} if((match=matchers.hex6.exec(color))){return{r:parseIntFromHex(match[1]),g:parseIntFromHex(match[2]),b:parseIntFromHex(match[3]),format:named?"name":"hex"}} if((match=matchers.hex3.exec(color))){return{r:parseIntFromHex(match[1]+''+match[1]),g:parseIntFromHex(match[2]+''+match[2]),b:parseIntFromHex(match[3]+''+match[3]),format:named?"name":"hex"}} return!1} window.tinycolor=tinycolor})();$(function(){if($.fn.spectrum.load){$.fn.spectrum.processNativeColorInputs()}})}); $('head').append(''); }; // end setupColorpickerSpectrum self.setup = function() { if ('pluginloaded' in self) { console.log('IITC plugin already loaded: ' + self.title + ' version ' + self.version); return; } else { self.pluginloaded = true; } self.setup_arc(); self.setupColorpickerSpectrum(); self.isSmartphone = window.isSmartphone(); self.restoresettings(); self.lineOptions = { stroke: true, color: self.settings.drawcolor, weight: 4, opacity: 0.8, fill: false, clickable: true }; // START - Great Circles functionality /** 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; }; } // END - Great Circles functionality //create a leaflet FeatureGroup to hold drawn items self.drawnItems = new L.FeatureGroup(); self.drawnItems._map = null; window.addLayerGroup(self.title, self.drawnItems,true); self.greatcircleslayer = new L.FeatureGroup(); window.addLayerGroup('Quick Draw Great Circles',self.greatcircleslayer,true); self.fieldslayer = new L.FeatureGroup(); window.addLayerGroup('Quick Draw Fields',self.fieldslayer,true); map.on('layeradd', function(obj) { // show button if(obj.layer === self.drawnItems) { self.onPortalSelected(); } if(obj.layer === self.greatcircleslayer) { self.updategreatcircleslayer(); } if(obj.layer === self.fieldslayer) { self.updatefieldslayer(); } }); map.on('layerremove', function(obj) { // hide button if(obj.layer === self.drawnItems) { self.removeMarker(); self.onPortalSelected(); } }); //window.map.on('zoomend', function(obj) { //self.updategreatcircleslayer(); //}); //load any previously saved items self.load(); self.restoretitles(); self.storelinks(); // overwrite and update data window.addHook('portalSelected', self.onPortalSelected); self.createCrossLinksLayer(); self.addPortalBookmarkSetup(); window.addHook('linkAdded', self.onLinkAdded); window.addHook('linkAdded', function() { if (!self.linktimerid) self.linktimerid = window.setTimeout(function() { self.linktimerid = undefined; self.updatefieldslayer(); },1000); }); window.addHook('mapDataRefreshEnd', self.onMapDataRefreshEnd); window.addHook('portalDetailLoaded', function(data) { if (self.requestid === data.guid) { self.requestid = undefined; window.renderPortalDetails(data.guid); } } ); //add options menu if(window.useAndroidPanes()) { android.addPane(self.panename, self.title, "ic_action_share"); addHook("paneChanged", self.onPaneChanged); } else { $('#toolbox').append('' + self.title + ''); } let titlebuttonwidth = 23; let titlebuttonheight = 23; let screenbuttonwidth = 25; let screenbuttonheight = 25; if (self.isSmartphone) { screenbuttonwidth = 30; screenbuttonheight = 30; } // Place buttons above the status-bar and add buttons to the portal details screen (in front of the portal title) let topoffset = -33; if (self.isSmartphone && window.plugin.miniMap) { // place it above the miniMap, if enabled topoffset = -196; } let leftoffset = 3; $('head').append( ''); 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);