266 lines
9.0 KiB
JavaScript
266 lines
9.0 KiB
JavaScript
// ==UserScript==
|
|
// @id iitc-plugin-cross-links@mcben
|
|
// @name IITC plugin: cross links
|
|
// @category Layer
|
|
// @version 1.1.0.@@DATETIMEVERSION@@
|
|
// @namespace https://github.com/jonatkins/ingress-intel-total-conversion
|
|
// @updateURL @@UPDATEURL@@
|
|
// @downloadURL @@DOWNLOADURL@@
|
|
// @description [@@BUILDNAME@@-@@BUILDDATE@@] EXPERIMENTAL: Checks for existing links that cross planned links. Requires draw-tools plugin.
|
|
// @include https://www.ingress.com/intel*
|
|
// @include http://www.ingress.com/intel*
|
|
// @match https://www.ingress.com/intel*
|
|
// @match http://www.ingress.com/intel*
|
|
// @grant none
|
|
// ==/UserScript==
|
|
|
|
@@PLUGINSTART@@
|
|
|
|
// PLUGIN START ////////////////////////////////////////////////////////
|
|
|
|
|
|
window.plugin.crossLinks = function() {};
|
|
|
|
|
|
window.plugin.crossLinks.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 R = 6378137; // earth radius in meters (doesn't have to be exact)
|
|
var d2r = Math.PI/180.0;
|
|
var r2d = 180.0/Math.PI;
|
|
|
|
// maths based on http://williams.best.vwh.net/avform.htm#Int
|
|
|
|
// only the variables needed to calculate a latitude for a given longitude are stored in 'this'
|
|
var lat1 = start.lat * d2r;
|
|
var lat2 = end.lat * d2r;
|
|
this.lng1 = start.lng * d2r;
|
|
this.lng2 = end.lng * d2r;
|
|
|
|
var dLng = this.lng2-this.lng1;
|
|
|
|
var sinLat1 = Math.sin(lat1);
|
|
var sinLat2 = Math.sin(lat2);
|
|
var cosLat1 = Math.cos(lat1);
|
|
var cosLat2 = Math.cos(lat2);
|
|
|
|
this.sinLat1CosLat2 = sinLat1*cosLat2;
|
|
this.sinLat2CosLat1 = sinLat2*cosLat1;
|
|
|
|
this.cosLat1CosLat2SinDLng = cosLat1*cosLat2*Math.sin(dLng);
|
|
}
|
|
|
|
GeodesicLine.prototype.latAtLng = function(lng) {
|
|
lng = lng * Math.PI / 180; //to radians
|
|
|
|
var lat = Math.atan ( (this.sinLat1CosLat2*Math.sin(this.lng2-lng) + 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) );
|
|
|
|
// prepare geodesic line maths
|
|
var aGeo = new GeodesicLine(a0,a1);
|
|
var bGeo = new GeodesicLine(b0,b1);
|
|
|
|
// calculate the latitudes for each line at left + right points
|
|
var aLeftLat = aGeo.latAtLng(leftLng);
|
|
var aRightLat = aGeo.latAtLng(rightLng);
|
|
|
|
var bLeftLat = bGeo.latAtLng(leftLng);
|
|
var 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;
|
|
}
|
|
|
|
|
|
|
|
window.plugin.crossLinks.testPolyLine = function (polyline, link,closed) {
|
|
|
|
var a = link.getLatLngs();
|
|
var b = polyline.getLatLngs();
|
|
|
|
for (var i=0;i<b.length-1;++i) {
|
|
if (window.plugin.crossLinks.greatCircleArcIntersect(a[0],a[1],b[i],b[i+1])) return true;
|
|
}
|
|
|
|
if (closed) {
|
|
if (window.plugin.crossLinks.greatCircleArcIntersect(a[0],a[1],b[b.length-1],b[0])) return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
window.plugin.crossLinks.onLinkAdded = function (data) {
|
|
if (window.plugin.crossLinks.disabled) return;
|
|
|
|
plugin.crossLinks.testLink(data.link);
|
|
}
|
|
|
|
window.plugin.crossLinks.checkAllLinks = function() {
|
|
if (window.plugin.crossLinks.disabled) return;
|
|
|
|
console.debug("Cross-Links: checking all links");
|
|
plugin.crossLinks.linkLayer.clearLayers();
|
|
plugin.crossLinks.linkLayerGuids = {};
|
|
|
|
$.each(window.links, function(guid, link) {
|
|
plugin.crossLinks.testLink(link);
|
|
});
|
|
}
|
|
|
|
window.plugin.crossLinks.testLink = function (link) {
|
|
if (plugin.crossLinks.linkLayerGuids[link.options.guid]) return;
|
|
|
|
for (var i in plugin.drawTools.drawnItems._layers) { // leaflet don't support breaking out of the loop
|
|
var layer = plugin.drawTools.drawnItems._layers[i];
|
|
if (layer instanceof L.GeodesicPolygon) {
|
|
if (plugin.crossLinks.testPolyLine(layer, link,true)) {
|
|
plugin.crossLinks.showLink(link);
|
|
break;
|
|
}
|
|
} else if (layer instanceof L.GeodesicPolyline) {
|
|
if (plugin.crossLinks.testPolyLine(layer, link)) {
|
|
plugin.crossLinks.showLink(link);
|
|
break;
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
|
|
window.plugin.crossLinks.showLink = function(link) {
|
|
|
|
var poly = L.geodesicPolyline(link.getLatLngs(), {
|
|
color: '#f11',
|
|
opacity: 0.5,
|
|
weight: 5,
|
|
clickable: false,
|
|
dashArray: [8,8],
|
|
|
|
guid: link.options.guid
|
|
});
|
|
|
|
poly.addTo(plugin.crossLinks.linkLayer);
|
|
plugin.crossLinks.linkLayerGuids[link.options.guid]=poly;
|
|
}
|
|
|
|
window.plugin.crossLinks.onMapDataRefreshEnd = function () {
|
|
window.plugin.crossLinks.linkLayer.bringToFront();
|
|
|
|
window.plugin.crossLinks.testForDeletedLinks();
|
|
}
|
|
|
|
window.plugin.crossLinks.testAllLinksAgainstLayer = function (layer) {
|
|
if (window.plugin.crossLinks.disabled) return;
|
|
|
|
$.each(window.links, function(guid, link) {
|
|
if (!plugin.crossLinks.linkLayerGuids[link.options.guid])
|
|
{
|
|
if (layer instanceof L.GeodesicPolygon) {
|
|
if (plugin.crossLinks.testPolyLine(layer, link,true)) {
|
|
plugin.crossLinks.showLink(link);
|
|
}
|
|
} else if (layer instanceof L.GeodesicPolyline) {
|
|
if (plugin.crossLinks.testPolyLine(layer, link)) {
|
|
plugin.crossLinks.showLink(link);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
window.plugin.crossLinks.testForDeletedLinks = function () {
|
|
window.plugin.crossLinks.linkLayer.eachLayer( function(layer) {
|
|
var guid = layer.options.guid;
|
|
if (!window.mapDataRequest.render.seenLinksGuid[guid]) {
|
|
console.log("link removed");
|
|
plugin.crossLinks.linkLayer.removeLayer(layer);
|
|
delete plugin.crossLinks.linkLayerGuids[guid];
|
|
}
|
|
});
|
|
}
|
|
|
|
window.plugin.crossLinks.createLayer = function() {
|
|
window.plugin.crossLinks.linkLayer = new L.FeatureGroup();
|
|
window.plugin.crossLinks.linkLayerGuids={};
|
|
window.addLayerGroup('Cross Links', window.plugin.crossLinks.linkLayer, true);
|
|
|
|
map.on('layeradd', function(obj) {
|
|
if(obj.layer === window.plugin.crossLinks.linkLayer) {
|
|
delete window.plugin.crossLinks.disabled;
|
|
window.plugin.crossLinks.checkAllLinks();
|
|
}
|
|
});
|
|
map.on('layerremove', function(obj) {
|
|
if(obj.layer === window.plugin.crossLinks.linkLayer) {
|
|
window.plugin.crossLinks.disabled = true;
|
|
}
|
|
});
|
|
}
|
|
|
|
var setup = function() {
|
|
if (window.plugin.drawTools === undefined) {
|
|
alert("'Cross-Links' requires 'draw-tools'");
|
|
return;
|
|
}
|
|
|
|
window.plugin.crossLinks.createLayer();
|
|
|
|
// events
|
|
map.on('draw:created', function(e) { window.plugin.crossLinks.testAllLinksAgainstLayer(e.layer);});
|
|
map.on('draw:deleted', function(e) {window.plugin.crossLinks.checkAllLinks(); });
|
|
map.on('draw:edited', function(e) {window.plugin.crossLinks.checkAllLinks();});
|
|
|
|
window.addHook('linkAdded', window.plugin.crossLinks.onLinkAdded);
|
|
window.addHook('mapDataRefreshEnd', window.plugin.crossLinks.onMapDataRefreshEnd);
|
|
}
|
|
|
|
// PLUGIN END //////////////////////////////////////////////////////////
|
|
|
|
@@PLUGINEND@@
|