node-red-contrib-tak-registration
Version:
A Node-RED node to register to TAK and to help wrap files as datapackages to send to TAK
309 lines (268 loc) • 9.12 kB
JavaScript
var cleanCoords = require('@turf/clean-coords');
var clone = require('@turf/clone');
var meta = require('@turf/meta');
var helpers = require('@turf/helpers');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var cleanCoords__default = /*#__PURE__*/_interopDefaultLegacy(cleanCoords);
var clone__default = /*#__PURE__*/_interopDefaultLegacy(clone);
/*
(c) 2013, Vladimir Agafonkin
Simplify.js, a high-performance JS polyline simplification library
mourner.github.io/simplify-js
*/
// to suit your point format, run search/replace for '.x' and '.y';
// for 3D version, see 3d branch (configurability would draw significant performance overhead)
// square distance between 2 points
function getSqDist(p1, p2) {
var dx = p1.x - p2.x,
dy = p1.y - p2.y;
return dx * dx + dy * dy;
}
// square distance from a point to a segment
function getSqSegDist(p, p1, p2) {
var x = p1.x,
y = p1.y,
dx = p2.x - x,
dy = p2.y - y;
if (dx !== 0 || dy !== 0) {
var t = ((p.x - x) * dx + (p.y - y) * dy) / (dx * dx + dy * dy);
if (t > 1) {
x = p2.x;
y = p2.y;
} else if (t > 0) {
x += dx * t;
y += dy * t;
}
}
dx = p.x - x;
dy = p.y - y;
return dx * dx + dy * dy;
}
// rest of the code doesn't care about point format
// basic distance-based simplification
function simplifyRadialDist(points, sqTolerance) {
var prevPoint = points[0],
newPoints = [prevPoint],
point;
for (var i = 1, len = points.length; i < len; i++) {
point = points[i];
if (getSqDist(point, prevPoint) > sqTolerance) {
newPoints.push(point);
prevPoint = point;
}
}
if (prevPoint !== point) newPoints.push(point);
return newPoints;
}
function simplifyDPStep(points, first, last, sqTolerance, simplified) {
var maxSqDist = sqTolerance,
index;
for (var i = first + 1; i < last; i++) {
var sqDist = getSqSegDist(points[i], points[first], points[last]);
if (sqDist > maxSqDist) {
index = i;
maxSqDist = sqDist;
}
}
if (maxSqDist > sqTolerance) {
if (index - first > 1)
simplifyDPStep(points, first, index, sqTolerance, simplified);
simplified.push(points[index]);
if (last - index > 1)
simplifyDPStep(points, index, last, sqTolerance, simplified);
}
}
// simplification using Ramer-Douglas-Peucker algorithm
function simplifyDouglasPeucker(points, sqTolerance) {
var last = points.length - 1;
var simplified = [points[0]];
simplifyDPStep(points, 0, last, sqTolerance, simplified);
simplified.push(points[last]);
return simplified;
}
// both algorithms combined for awesome performance
function simplify(points, tolerance, highestQuality) {
if (points.length <= 2) return points;
var sqTolerance = tolerance !== undefined ? tolerance * tolerance : 1;
points = highestQuality ? points : simplifyRadialDist(points, sqTolerance);
points = simplifyDouglasPeucker(points, sqTolerance);
return points;
}
/**
* Takes a {@link GeoJSON} object and returns a simplified version. Internally uses
* [simplify-js](http://mourner.github.io/simplify-js/) to perform simplification using the Ramer-Douglas-Peucker algorithm.
*
* @name simplify
* @param {GeoJSON} geojson object to be simplified
* @param {Object} [options={}] Optional parameters
* @param {number} [options.tolerance=1] simplification tolerance
* @param {boolean} [options.highQuality=false] whether or not to spend more time to create a higher-quality simplification with a different algorithm
* @param {boolean} [options.mutate=false] allows GeoJSON input to be mutated (significant performance increase if true)
* @returns {GeoJSON} a simplified GeoJSON
* @example
* var geojson = turf.polygon([[
* [-70.603637, -33.399918],
* [-70.614624, -33.395332],
* [-70.639343, -33.392466],
* [-70.659942, -33.394759],
* [-70.683975, -33.404504],
* [-70.697021, -33.419406],
* [-70.701141, -33.434306],
* [-70.700454, -33.446339],
* [-70.694274, -33.458369],
* [-70.682601, -33.465816],
* [-70.668869, -33.472117],
* [-70.646209, -33.473835],
* [-70.624923, -33.472117],
* [-70.609817, -33.468107],
* [-70.595397, -33.458369],
* [-70.587158, -33.442901],
* [-70.587158, -33.426283],
* [-70.590591, -33.414248],
* [-70.594711, -33.406224],
* [-70.603637, -33.399918]
* ]]);
* var options = {tolerance: 0.01, highQuality: false};
* var simplified = turf.simplify(geojson, options);
*
* //addToMap
* var addToMap = [geojson, simplified]
*/
function simplify$1(geojson, options) {
// Optional parameters
options = options || {};
if (!helpers.isObject(options)) throw new Error("options is invalid");
var tolerance = options.tolerance !== undefined ? options.tolerance : 1;
var highQuality = options.highQuality || false;
var mutate = options.mutate || false;
if (!geojson) throw new Error("geojson is required");
if (tolerance && tolerance < 0) throw new Error("invalid tolerance");
// Clone geojson to avoid side effects
if (mutate !== true) geojson = clone__default['default'](geojson);
meta.geomEach(geojson, function (geom) {
simplifyGeom(geom, tolerance, highQuality);
});
return geojson;
}
/**
* Simplifies a feature's coordinates
*
* @private
* @param {Geometry} geometry to be simplified
* @param {number} [tolerance=1] simplification tolerance
* @param {boolean} [highQuality=false] whether or not to spend more time to create a higher-quality simplification with a different algorithm
* @returns {Geometry} output
*/
function simplifyGeom(geometry, tolerance, highQuality) {
var type = geometry.type;
// "unsimplyfiable" geometry types
if (type === "Point" || type === "MultiPoint") return geometry;
// Remove any extra coordinates
cleanCoords__default['default'](geometry, true);
var coordinates = geometry.coordinates;
switch (type) {
case "LineString":
geometry["coordinates"] = simplifyLine(
coordinates,
tolerance,
highQuality
);
break;
case "MultiLineString":
geometry["coordinates"] = coordinates.map(function (lines) {
return simplifyLine(lines, tolerance, highQuality);
});
break;
case "Polygon":
geometry["coordinates"] = simplifyPolygon(
coordinates,
tolerance,
highQuality
);
break;
case "MultiPolygon":
geometry["coordinates"] = coordinates.map(function (rings) {
return simplifyPolygon(rings, tolerance, highQuality);
});
}
return geometry;
}
/**
* Simplifies the coordinates of a LineString with simplify-js
*
* @private
* @param {Array<number>} coordinates to be processed
* @param {number} tolerance simplification tolerance
* @param {boolean} highQuality whether or not to spend more time to create a higher-quality
* @returns {Array<Array<number>>} simplified coords
*/
function simplifyLine(coordinates, tolerance, highQuality) {
return simplify(
coordinates.map(function (coord) {
return { x: coord[0], y: coord[1], z: coord[2] };
}),
tolerance,
highQuality
).map(function (coords) {
return coords.z ? [coords.x, coords.y, coords.z] : [coords.x, coords.y];
});
}
/**
* Simplifies the coordinates of a Polygon with simplify-js
*
* @private
* @param {Array<number>} coordinates to be processed
* @param {number} tolerance simplification tolerance
* @param {boolean} highQuality whether or not to spend more time to create a higher-quality
* @returns {Array<Array<Array<number>>>} simplified coords
*/
function simplifyPolygon(coordinates, tolerance, highQuality) {
return coordinates.map(function (ring) {
var pts = ring.map(function (coord) {
return { x: coord[0], y: coord[1] };
});
if (pts.length < 4) {
throw new Error("invalid polygon");
}
var simpleRing = simplify(pts, tolerance, highQuality).map(function (
coords
) {
return [coords.x, coords.y];
});
//remove 1 percent of tolerance until enough points to make a triangle
while (!checkValidity(simpleRing)) {
tolerance -= tolerance * 0.01;
simpleRing = simplify(pts, tolerance, highQuality).map(function (
coords
) {
return [coords.x, coords.y];
});
}
if (
simpleRing[simpleRing.length - 1][0] !== simpleRing[0][0] ||
simpleRing[simpleRing.length - 1][1] !== simpleRing[0][1]
) {
simpleRing.push(simpleRing[0]);
}
return simpleRing;
});
}
/**
* Returns true if ring has at least 3 coordinates and its first coordinate is the same as its last
*
* @private
* @param {Array<number>} ring coordinates to be checked
* @returns {boolean} true if valid
*/
function checkValidity(ring) {
if (ring.length < 3) return false;
//if the last point is the same as the first, it's not a triangle
return !(
ring.length === 3 &&
ring[2][0] === ring[0][0] &&
ring[2][1] === ring[0][1]
);
}
module.exports = simplify$1;
module.exports.default = simplify$1;
;