/*! geolib.isPointInsideRobust 2.0.21 * !!EXPERIMENTAL!! * * Robust version of isPointInside for Geolib.js * * Based on https://github.com/mikolalysenko/robust-point-in-polygon * by Mikola Lysenko, licensed under MIT * * @author Manuel Bieh * @url http://www.manuelbieh.com/ * @version 2.0.21 * @license MIT * */ ;(function(global, geolib, undefined) { var addOn = function(geolib) { var SPLITTER = +(Math.pow(2, 27) + 1.0); var NUM_EXPAND = 5; var EPSILON = 1.1102230246251565e-16; var ERRBOUND3 = (3.0 + 16.0 * EPSILON) * EPSILON; var ERRBOUND4 = (7.0 + 56.0 * EPSILON) * EPSILON; var twoProduct = function(a, b, result) { var x = a * b; var c = SPLITTER * a; var abig = c - a; var ahi = c - abig; var alo = a - ahi; var d = SPLITTER * b; var bbig = d - b; var bhi = d - bbig; var blo = b - bhi; var err1 = x - (ahi * bhi); var err2 = err1 - (alo * bhi); var err3 = err2 - (ahi * blo); var y = alo * blo - err3; if(result) { result[0] = y; result[1] = x; return result; } return [ y, x ]; }; var fastTwoSum = function(a, b, result) { var x = a + b; var bv = x - a; var av = x - bv; var br = b - bv; var ar = a - av; if(result) { result[0] = ar + br; result[1] = x; return result; } return [ar+br, x]; }; var twoSum = fastTwoSum; var linearExpansionSum = function(e, f) { var ne = e.length|0; var nf = f.length|0; if(ne === 1 && nf === 1) { return scalarScalar(e[0], f[0]); } var n = ne + nf; var g = new Array(n); var count = 0; var eptr = 0; var fptr = 0; var abs = Math.abs; var ei = e[eptr]; var ea = abs(ei); var fi = f[fptr]; var fa = abs(fi); var a, b; if(ea < fa) { b = ei; eptr += 1; if(eptr < ne) { ei = e[eptr]; ea = abs(ei); } } else { b = fi; fptr += 1; if(fptr < nf) { fi = f[fptr]; fa = abs(fi); } } if((eptr < ne && ea < fa) || (fptr >= nf)) { a = ei; eptr += 1; if(eptr < ne) { ei = e[eptr]; ea = abs(ei); } } else { a = fi; fptr += 1; if(fptr < nf) { fi = f[fptr]; fa = abs(fi); } } var x = a + b; var bv = x - a; var y = b - bv; var q0 = y; var q1 = x; var _x, _bv, _av, _br, _ar; while(eptr < ne && fptr < nf) { if(ea < fa) { a = ei; eptr += 1; if(eptr < ne) { ei = e[eptr]; ea = abs(ei); } } else { a = fi; fptr += 1; if(fptr < nf) { fi = f[fptr]; fa = abs(fi); } } b = q0; x = a + b; bv = x - a; y = b - bv; if(y) { g[count++] = y; } _x = q1 + x; _bv = _x - q1; _av = _x - _bv; _br = x - _bv; _ar = q1 - _av; q0 = _ar + _br; q1 = _x; } while(eptr < ne) { a = ei; b = q0; x = a + b; bv = x - a; y = b - bv; if(y) { g[count++] = y; } _x = q1 + x; _bv = _x - q1; _av = _x - _bv; _br = x - _bv; _ar = q1 - _av; q0 = _ar + _br; q1 = _x; eptr += 1; if(eptr < ne) { ei = e[eptr]; } } while(fptr < nf) { a = fi; b = q0; x = a + b; bv = x - a; y = b - bv; if(y) { g[count++] = y; } _x = q1 + x; _bv = _x - q1; _av = _x - _bv; _br = x - _bv; _ar = q1 - _av; q0 = _ar + _br; q1 = _x; fptr += 1; if(fptr < nf) { fi = f[fptr]; } } if(q0) { g[count++] = q0; } if(q1) { g[count++] = q1; } if(!count) { g[count++] = 0.0; } g.length = count; return g; }; var robustSum = linearExpansionSum; var scaleLinearExpansion = function(e, scale) { var n = e.length; if(n === 1) { var ts = twoProduct(e[0], scale); if(ts[0]) { return ts; } return [ ts[1] ]; } var g = new Array(2 * n); var q = [0.1, 0.1]; var t = [0.1, 0.1]; var count = 0; twoProduct(e[0], scale, q); if(q[0]) { g[count++] = q[0]; } for(var i=1; i<n; ++i) { twoProduct(e[i], scale, t); var pq = q[1]; twoSum(pq, t[0], q); if(q[0]) { g[count++] = q[0]; } var a = t[1]; var b = q[1]; var x = a + b; var bv = x - a; var y = b - bv; q[1] = x; if(y) { g[count++] = y; } } if(q[1]) { g[count++] = q[1]; } if(count === 0) { g[count++] = 0.0; } g.length = count; return g; }; var robustScale = scaleLinearExpansion; var scalarScalar = function(a, b) { var x = a + b; var bv = x - a; var av = x - bv; var br = b - bv; var ar = a - av; var y = ar + br; if(y) { return [y, x]; } return [x]; }; var robustSubtract = function(e, f) { var ne = e.length|0; var nf = f.length|0; if(ne === 1 && nf === 1) { return scalarScalar(e[0], -f[0]); } var n = ne + nf; var g = new Array(n); var count = 0; var eptr = 0; var fptr = 0; var abs = Math.abs; var ei = e[eptr]; var ea = abs(ei); var fi = -f[fptr]; var fa = abs(fi); var a, b; if(ea < fa) { b = ei; eptr += 1; if(eptr < ne) { ei = e[eptr]; ea = abs(ei); } } else { b = fi; fptr += 1; if(fptr < nf) { fi = -f[fptr]; fa = abs(fi); } } if((eptr < ne && ea < fa) || (fptr >= nf)) { a = ei; eptr += 1; if(eptr < ne) { ei = e[eptr]; ea = abs(ei); } } else { a = fi; fptr += 1; if(fptr < nf) { fi = -f[fptr]; fa = abs(fi); } } var x = a + b; var bv = x - a; var y = b - bv; var q0 = y; var q1 = x; var _x, _bv, _av, _br, _ar; while(eptr < ne && fptr < nf) { if(ea < fa) { a = ei; eptr += 1; if(eptr < ne) { ei = e[eptr]; ea = abs(ei); } } else { a = fi; fptr += 1; if(fptr < nf) { fi = -f[fptr]; fa = abs(fi); } } b = q0; x = a + b; bv = x - a; y = b - bv; if(y) { g[count++] = y; } _x = q1 + x; _bv = _x - q1; _av = _x - _bv; _br = x - _bv; _ar = q1 - _av; q0 = _ar + _br; q1 = _x; } while(eptr < ne) { a = ei; b = q0; x = a + b; bv = x - a; y = b - bv; if(y) { g[count++] = y; } _x = q1 + x; _bv = _x - q1; _av = _x - _bv; _br = x - _bv; _ar = q1 - _av; q0 = _ar + _br; q1 = _x; eptr += 1; if(eptr < ne) { ei = e[eptr]; } } while(fptr < nf) { a = fi; b = q0; x = a + b; bv = x - a; y = b - bv; if(y) { g[count++] = y; } _x = q1 + x; _bv = _x - q1; _av = _x - _bv; _br = x - _bv; _ar = q1 - _av; q0 = _ar + _br; q1 = _x; fptr += 1; if(fptr < nf) { fi = -f[fptr]; } } if(q0) { g[count++] = q0; } if(q1) { g[count++] = q1; } if(!count) { g[count++] = 0.0; } g.length = count; return g; }; var cofactor = function(m, c) { var result = new Array(m.length-1); for(var i=1; i<m.length; ++i) { var r = result[i-1] = new Array(m.length-1); for(var j=0,k=0; j<m.length; ++j) { if(j === c) { continue; } r[k++] = m[i][j]; } } return result; }; var matrix = function(n) { var result = new Array(n); for(var i=0; i<n; ++i) { result[i] = new Array(n); for(var j=0; j<n; ++j) { result[i][j] = ["m", j, "[", (n-i-1), "]"].join(""); } } return result; }; var sign = function(n) { if(n & 1) { return "-"; } return ""; }; var generateSum = function(expr) { if(expr.length === 1) { return expr[0]; } else if(expr.length === 2) { return ["sum(", expr[0], ",", expr[1], ")"].join(""); } else { var m = expr.length>>1; return ["sum(", generateSum(expr.slice(0, m)), ",", generateSum(expr.slice(m)), ")"].join(""); } }; var determinant = function(m) { if(m.length === 2) { return [["sum(prod(", m[0][0], ",", m[1][1], "),prod(-", m[0][1], ",", m[1][0], "))"].join("")]; } else { var expr = []; for(var i=0; i<m.length; ++i) { expr.push(["scale(", generateSum(determinant(cofactor(m, i))), ",", sign(i), m[0][i], ")"].join("")); } return expr; } }; var orientation = function(n) { var pos = []; var neg = []; var m = matrix(n); var args = []; for(var i=0; i<n; ++i) { if((i&1)===0) { pos.push.apply(pos, determinant(cofactor(m, i))); } else { neg.push.apply(neg, determinant(cofactor(m, i))); } args.push("m" + i); } var posExpr = generateSum(pos); var negExpr = generateSum(neg); var funcName = "orientation" + n + "Exact"; var code = [ "function ", funcName, "(", args.join(), "){var p=", posExpr, ",n=", negExpr, ",d=sub(p,n);return d[d.length-1];};return ", funcName ].join(""); var proc = new Function("sum", "prod", "scale", "sub", code); return proc(robustSum, twoProduct, robustScale, robustSubtract); }; var orient; var orientation3Exact = orientation(3); var orientation4Exact = orientation(4); var CACHED = [ function orientation0() { return 0; }, function orientation1() { return 0; }, function orientation2(a, b) { return b[0] - a[0]; }, function orientation3(a, b, c) { var l = (a[1] - c[1]) * (b[0] - c[0]); var r = (a[0] - c[0]) * (b[1] - c[1]); var det = l - r; var s; if(l > 0) { if(r <= 0) { return det; } else { s = l + r; } } else if(l < 0) { if(r >= 0) { return det; } else { s = -(l + r); } } else { return det; } var tol = ERRBOUND3 * s; if(det >= tol || det <= -tol) { return det; } return orientation3Exact(a, b, c); }, function orientation4(a,b,c,d) { var adx = a[0] - d[0]; var bdx = b[0] - d[0]; var cdx = c[0] - d[0]; var ady = a[1] - d[1]; var bdy = b[1] - d[1]; var cdy = c[1] - d[1]; var adz = a[2] - d[2]; var bdz = b[2] - d[2]; var cdz = c[2] - d[2]; var bdxcdy = bdx * cdy; var cdxbdy = cdx * bdy; var cdxady = cdx * ady; var adxcdy = adx * cdy; var adxbdy = adx * bdy; var bdxady = bdx * ady; var det = adz * (bdxcdy - cdxbdy) + bdz * (cdxady - adxcdy) + cdz * (adxbdy - bdxady); var permanent = (Math.abs(bdxcdy) + Math.abs(cdxbdy)) * Math.abs(adz) + (Math.abs(cdxady) + Math.abs(adxcdy)) * Math.abs(bdz) + (Math.abs(adxbdy) + Math.abs(bdxady)) * Math.abs(cdz); var tol = ERRBOUND4 * permanent; if ((det > tol) || (-det > tol)) { return det; } return orientation4Exact(a,b,c,d); } ]; var slowOrient = function(args) { var proc = CACHED[args.length]; if(!proc) { proc = CACHED[args.length] = orientation(args.length); } return proc.apply(undefined, args); }; var generateOrientationProc = function() { while(CACHED.length <= NUM_EXPAND) { CACHED.push(orientation(CACHED.length)); } var args = []; var procArgs = ["slow"]; for(var i=0; i<=NUM_EXPAND; ++i) { args.push("a" + i); procArgs.push("o" + i); } var code = [ "function getOrientation(", args.join(), "){switch(arguments.length){case 0:case 1:return 0;" ]; for(i=2; i<=NUM_EXPAND; ++i) { code.push("case ", i, ":return o", i, "(", args.slice(0, i).join(), ");"); } code.push("}var s=new Array(arguments.length);for(var i=0;i<arguments.length;++i){s[i]=arguments[i]};return slow(s);}return getOrientation"); procArgs.push(code.join("")); var proc = Function.apply(undefined, procArgs); orient = proc.apply(undefined, [slowOrient].concat(CACHED)); for(i=0; i<=NUM_EXPAND; ++i) { orient[i] = CACHED[i]; } }; generateOrientationProc(); var robustPointInPolygon = function(vs, point) { // transform from geolib format to array syntax var x = geolib.longitude(point); var y = geolib.latitude(point); var coords = vs.map(function(coords) { return [geolib.longitude(coords), geolib.latitude(coords)]; }); vs = coords; point = [x,y]; var n = vs.length; var inside = 1; var lim = n; var s, c, yk, px, p; for(var i = 0, j = n-1; i<lim; j=i++) { var a = vs[i]; var b = vs[j]; var yi = a[1]; var yj = b[1]; if(yj < yi) { if(yj < y && y < yi) { s = orient(a, b, point); if(s === 0) { return 0; } else { inside ^= (0 < s)|0; } } else if(y === yi) { c = vs[(i+1)%n]; yk = c[1]; if(yi < yk) { s = orient(a, b, point); if(s === 0) { return 0; } else { inside ^= (0 < s)|0; } } } } else if(yi < yj) { if(yi < y && y < yj) { s = orient(a, b, point); if(s === 0) { return 0; } else { inside ^= (s < 0)|0; } } else if(y === yi) { c = vs[(i+1)%n]; yk = c[1]; if(yk < yi) { s = orient(a, b, point); if(s === 0) { return 0; } else { inside ^= (s < 0)|0; } } } } else if(y === yi) { var x0 = Math.min(a[0], b[0]); var x1 = Math.max(a[0], b[0]); if(i === 0) { while(j>0) { var k = (j+n-1)%n; p = vs[k]; if(p[1] !== y) { break; } px = p[0]; x0 = Math.min(x0, px); x1 = Math.max(x1, px); j = k; } if(j === 0) { if(x0 <= x && x <= x1) { return 0; } return 1; } lim = j+1; } var y0 = vs[(j+n-1)%n][1]; while(i+1<lim) { p = vs[i+1]; if(p[1] !== y) { break; } px = p[0]; x0 = Math.min(x0, px); x1 = Math.max(x1, px); i += 1; } if(x0 <= x && x <= x1) { return 0; } var y1 = vs[(i+1)%n][1]; if(x < x0 && (y0 < y !== y1 < y)) { inside ^= 1; } } } return 2 * inside - 1; }; return { /** * @param object coordinate to check e.g. {latitude: 51.5023, longitude: 7.3815} * @param array array with coords e.g. [{latitude: 51.5143, longitude: 7.4138}, {latitude: 123, longitude: 123}, ...] * @return integer -1 if point is inside, 0 if point is on boundaries, 1 if point is outside */ isPointInsideRobust: function(latlng, coords) { return robustPointInPolygon(coords, latlng); }, isInside: function() { return this.isPointInsideRobust.apply(this, arguments); } }; }; // Node module if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') { module.exports = function(geolib) { geolib.extend(addOn(geolib), true); return geolib; }; // AMD module } else if (typeof define === "function" && define.amd) { define(["geolib"], function (geolib) { geolib.extend(addOn(geolib), true); return geolib; }); // we're in a browser } else { geolib.extend(addOn(geolib), true); } }(this, this.geolib));