Merge branch 'McBen-master'
This commit is contained in:
commit
1484b4947e
285
plugins/cross_link.user.js
Normal file
285
plugins/cross_link.user.js
Normal file
@ -0,0 +1,285 @@
|
|||||||
|
// ==UserScript==
|
||||||
|
// @id iitc-plugin-cross-links@mcben
|
||||||
|
// @name IITC plugin: cross links
|
||||||
|
// @category Layer
|
||||||
|
// @version 1.0.@@DATETIMEVERSION@@
|
||||||
|
// @namespace https://github.com/jonatkins/ingress-intel-total-conversion
|
||||||
|
// @updateURL @@UPDATEURL@@
|
||||||
|
// @downloadURL @@DOWNLOADURL@@
|
||||||
|
// @description [McBen] requires drawtool! coloring of link collision
|
||||||
|
// @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() {};
|
||||||
|
|
||||||
|
/* Great Circle Arc Intersection
|
||||||
|
Conecpt in short:
|
||||||
|
- build a plane of each arc (p1,p2,center)
|
||||||
|
- find intersection line and intersection points on sphere
|
||||||
|
- check if a point are on both arcs
|
||||||
|
see: http://geospatialmethods.org/spheres/GCAIntersect.html
|
||||||
|
*/
|
||||||
|
var PI = Math.PI;
|
||||||
|
var radians = PI / 180;
|
||||||
|
var near_0 = 1e-6;
|
||||||
|
|
||||||
|
function greatCircleArcIntersect(a0,a1,b0,b1) {
|
||||||
|
|
||||||
|
// Order points
|
||||||
|
if (a1.lat < a0.lat) { var t=a1;a1=a0;a0=t;}
|
||||||
|
if (b1.lat < b0.lat) { var t=b1;b1=b0;b0=t;}
|
||||||
|
|
||||||
|
var λ0 = a0.lat,
|
||||||
|
λ1 = a1.lat,
|
||||||
|
λ2 = b0.lat,
|
||||||
|
λ3 = b1.lat,
|
||||||
|
δλ0 = λ1 - λ0,
|
||||||
|
δλ1 = λ3 - λ2,
|
||||||
|
sλ0 = δλ0 > 180,
|
||||||
|
sλ1 = δλ1 > 180,
|
||||||
|
φ0 = a0.lng * radians,
|
||||||
|
φ1 = a1.lng * radians,
|
||||||
|
φ2 = b0.lng * radians,
|
||||||
|
φ3 = b1.lng * radians,
|
||||||
|
t;
|
||||||
|
|
||||||
|
// Check if longitude ranges overlap.
|
||||||
|
// TODO handle antimeridian crossings.
|
||||||
|
if (!sλ0 && !sλ1 && (λ0 > λ3 || λ2 > λ1)) return;
|
||||||
|
|
||||||
|
// Check for polar endpoints.
|
||||||
|
if (Math.abs(Math.abs(φ0) - PI / 2) < near_0) λ0 = λ1, δλ0 = 0, sλ0 = false;
|
||||||
|
if (Math.abs(Math.abs(φ1) - PI / 2) < near_0) λ1 = λ0, δλ0 = 0, sλ0 = false;
|
||||||
|
if (Math.abs(Math.abs(φ2) - PI / 2) < near_0) λ2 = λ3, δλ1 = 0, sλ1 = false;
|
||||||
|
if (Math.abs(Math.abs(φ3) - PI / 2) < near_0) λ3 = λ2, δλ1 = 0, sλ1 = false;
|
||||||
|
|
||||||
|
// Check for arcs along meridians.
|
||||||
|
var m0 = δλ0 < near_0 || Math.abs(δλ0 - 180) < near_0,
|
||||||
|
m1 = δλ1 < near_0 || Math.abs(δλ1 - 180) < near_0;
|
||||||
|
|
||||||
|
λ0 *= radians, λ1 *= radians, λ2 *= radians, λ3 *= radians;
|
||||||
|
|
||||||
|
// Intersect two great circles and check the two intersection points against
|
||||||
|
// the longitude ranges. The intersection points are simply the cross
|
||||||
|
// product of the great-circle normals ±n1⨯n2.
|
||||||
|
|
||||||
|
// First plane.
|
||||||
|
var cosφ,
|
||||||
|
x0 = (cosφ = Math.cos(φ0)) * Math.cos(λ0),
|
||||||
|
y0 = cosφ * Math.sin(λ0),
|
||||||
|
z0 = Math.sin(φ0),
|
||||||
|
x1 = (cosφ = Math.cos(φ1)) * Math.cos(λ1),
|
||||||
|
y1 = cosφ * Math.sin(λ1),
|
||||||
|
z1 = Math.sin(φ1),
|
||||||
|
n0x = y0 * z1 - z0 * y1,
|
||||||
|
n0y = z0 * x1 - x0 * z1,
|
||||||
|
n0z = x0 * y1 - y0 * x1,
|
||||||
|
m = length(n0x, n0y, n0z);
|
||||||
|
|
||||||
|
n0x /= m, n0y /= m, n0z /= m;
|
||||||
|
|
||||||
|
// Second plane.
|
||||||
|
var x2 = (cosφ = Math.cos(φ2)) * Math.cos(λ2),
|
||||||
|
y2 = cosφ * Math.sin(λ2),
|
||||||
|
z2 = Math.sin(φ2),
|
||||||
|
x3 = (cosφ = Math.cos(φ3)) * Math.cos(λ3),
|
||||||
|
y3 = cosφ * Math.sin(λ3),
|
||||||
|
z3 = Math.sin(φ3),
|
||||||
|
n1x = y2 * z3 - z2 * y3,
|
||||||
|
n1y = z2 * x3 - x2 * z3,
|
||||||
|
n1z = x2 * y3 - y2 * x3,
|
||||||
|
m = length(n1x, n1y, n1z);
|
||||||
|
|
||||||
|
n1x /= m, n1y /= m, n1z /= m;
|
||||||
|
|
||||||
|
var Nx = n0y * n1z - n0z * n1y,
|
||||||
|
Ny = n0z * n1x - n0x * n1z,
|
||||||
|
Nz = n0x * n1y - n0y * n1x;
|
||||||
|
|
||||||
|
if (length(Nx, Ny, Nz) < near_0) return;
|
||||||
|
|
||||||
|
var λ = Math.atan2(Ny, Nx);
|
||||||
|
if ( (sλ0 ^ (λ0 <= λ && λ <= λ1) || m0 && Math.abs(λ - λ0) < near_0) && (sλ1 ^ (λ2 <= λ && λ <= λ3) || m1 && Math.abs(λ - λ2) < near_0) || (Nz = -Nz,
|
||||||
|
(sλ0 ^ (λ0 <= (λ = (λ + 2 * PI) % (2 * PI) - PI) && λ <= λ1) || m0 && Math.abs(λ - λ0) < near_0) && (sλ1 ^ (λ2 <= λ && λ <= λ3) || m1 && Math.abs(λ - λ2) < near_0))) {
|
||||||
|
|
||||||
|
var φ = Math.asin(Nz / length(Nx, Ny, Nz));
|
||||||
|
|
||||||
|
if (m0 || m1) {
|
||||||
|
if (m1) φ0 = φ2, φ1 = φ3, λ0 = λ2, λ1 = λ3, δλ0 = δλ1;
|
||||||
|
|
||||||
|
if (δλ0 > near_0)
|
||||||
|
return (φ0 + φ1 > 0 ^ φ < (Math.abs(λ - λ0) < near_0 ? φ0 : φ1)) ? [λ / radians, φ / radians] : null;
|
||||||
|
|
||||||
|
// Ensure φ0 ≤ φ1.
|
||||||
|
if (φ1 < φ0) t = φ0, φ0 = φ1, φ1 = t;
|
||||||
|
return (Math.abs(λ - (m0 ? λ0 : λ2)) < near_0 && φ0 <= φ && φ <= φ1) ? [λ / radians, φ / radians] : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return [λ / radians, φ / radians];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function length(x, y, z) {
|
||||||
|
return Math.sqrt(x * x + y * y + z * z);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
smallCircleIntersect
|
||||||
|
idea:
|
||||||
|
- build the plane of the small circle: normalvector (center) and p=center+radian // E:n1*x+n2*y+n3*z=n1*p1+n2*p2+n3*p3
|
||||||
|
- calc distance to both points // d=(n1*x+n2*y+n3*z- (n1*p1+n2*p2+n3*p3)) / length(n)
|
||||||
|
- both >0 = inside ; one >0 = collision
|
||||||
|
*/
|
||||||
|
|
||||||
|
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 (greatCircleArcIntersect(a[0],a[1],b[i],b[i+1])) return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (closed) {
|
||||||
|
if (greatCircleArcIntersect(a[0],a[1],b[b.length],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.7,
|
||||||
|
weight: 4,
|
||||||
|
clickable: false,
|
||||||
|
|
||||||
|
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@@
|
Loading…
x
Reference in New Issue
Block a user