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
247 lines (225 loc) • 6.6 kB
JavaScript
var meta = require('@turf/meta');
var invariant = require('@turf/invariant');
var helpers = require('@turf/helpers');
/**
* https://github.com/rook2pawn/node-intersection
*
* Author @rook2pawn
*/
/**
* AB
*
* @private
* @param {Array<Array<number>>} segment - 2 vertex line segment
* @returns {Array<number>} coordinates [x, y]
*/
function ab(segment) {
var start = segment[0];
var end = segment[1];
return [end[0] - start[0], end[1] - start[1]];
}
/**
* Cross Product
*
* @private
* @param {Array<number>} v1 coordinates [x, y]
* @param {Array<number>} v2 coordinates [x, y]
* @returns {Array<number>} Cross Product
*/
function crossProduct(v1, v2) {
return v1[0] * v2[1] - v2[0] * v1[1];
}
/**
* Add
*
* @private
* @param {Array<number>} v1 coordinates [x, y]
* @param {Array<number>} v2 coordinates [x, y]
* @returns {Array<number>} Add
*/
function add(v1, v2) {
return [v1[0] + v2[0], v1[1] + v2[1]];
}
/**
* Sub
*
* @private
* @param {Array<number>} v1 coordinates [x, y]
* @param {Array<number>} v2 coordinates [x, y]
* @returns {Array<number>} Sub
*/
function sub(v1, v2) {
return [v1[0] - v2[0], v1[1] - v2[1]];
}
/**
* scalarMult
*
* @private
* @param {number} s scalar
* @param {Array<number>} v coordinates [x, y]
* @returns {Array<number>} scalarMult
*/
function scalarMult(s, v) {
return [s * v[0], s * v[1]];
}
/**
* Intersect Segments
*
* @private
* @param {Array<number>} a coordinates [x, y]
* @param {Array<number>} b coordinates [x, y]
* @returns {Array<number>} intersection
*/
function intersectSegments(a, b) {
var p = a[0];
var r = ab(a);
var q = b[0];
var s = ab(b);
var cross = crossProduct(r, s);
var qmp = sub(q, p);
var numerator = crossProduct(qmp, s);
var t = numerator / cross;
var intersection = add(p, scalarMult(t, r));
return intersection;
}
/**
* Is Parallel
*
* @private
* @param {Array<number>} a coordinates [x, y]
* @param {Array<number>} b coordinates [x, y]
* @returns {boolean} true if a and b are parallel (or co-linear)
*/
function isParallel(a, b) {
var r = ab(a);
var s = ab(b);
return crossProduct(r, s) === 0;
}
/**
* Intersection
*
* @private
* @param {Array<number>} a coordinates [x, y]
* @param {Array<number>} b coordinates [x, y]
* @returns {Array<number>|boolean} true if a and b are parallel (or co-linear)
*/
function intersection(a, b) {
if (isParallel(a, b)) return false;
return intersectSegments(a, b);
}
/**
* Takes a {@link LineString|line} and returns a {@link LineString|line} at offset by the specified distance.
*
* @name lineOffset
* @param {Geometry|Feature<LineString|MultiLineString>} geojson input GeoJSON
* @param {number} distance distance to offset the line (can be of negative value)
* @param {Object} [options={}] Optional parameters
* @param {string} [options.units='kilometers'] can be degrees, radians, miles, kilometers, inches, yards, meters
* @returns {Feature<LineString|MultiLineString>} Line offset from the input line
* @example
* var line = turf.lineString([[-83, 30], [-84, 36], [-78, 41]], { "stroke": "#F00" });
*
* var offsetLine = turf.lineOffset(line, 2, {units: 'miles'});
*
* //addToMap
* var addToMap = [offsetLine, line]
* offsetLine.properties.stroke = "#00F"
*/
function lineOffset(geojson, distance, options) {
// Optional parameters
options = options || {};
if (!helpers.isObject(options)) throw new Error("options is invalid");
var units = options.units;
// Valdiation
if (!geojson) throw new Error("geojson is required");
if (distance === undefined || distance === null || isNaN(distance))
throw new Error("distance is required");
var type = invariant.getType(geojson);
var properties = geojson.properties;
switch (type) {
case "LineString":
return lineOffsetFeature(geojson, distance, units);
case "MultiLineString":
var coords = [];
meta.flattenEach(geojson, function (feature) {
coords.push(
lineOffsetFeature(feature, distance, units).geometry.coordinates
);
});
return helpers.multiLineString(coords, properties);
default:
throw new Error("geometry " + type + " is not supported");
}
}
/**
* Line Offset
*
* @private
* @param {Geometry|Feature<LineString>} line input line
* @param {number} distance distance to offset the line (can be of negative value)
* @param {string} [units=kilometers] units
* @returns {Feature<LineString>} Line offset from the input line
*/
function lineOffsetFeature(line, distance, units) {
var segments = [];
var offsetDegrees = helpers.lengthToDegrees(distance, units);
var coords = invariant.getCoords(line);
var finalCoords = [];
coords.forEach(function (currentCoords, index) {
if (index !== coords.length - 1) {
var segment = processSegment(
currentCoords,
coords[index + 1],
offsetDegrees
);
segments.push(segment);
if (index > 0) {
var seg2Coords = segments[index - 1];
var intersects = intersection(segment, seg2Coords);
// Handling for line segments that aren't straight
if (intersects !== false) {
seg2Coords[1] = intersects;
segment[0] = intersects;
}
finalCoords.push(seg2Coords[0]);
if (index === coords.length - 2) {
finalCoords.push(segment[0]);
finalCoords.push(segment[1]);
}
}
// Handling for lines that only have 1 segment
if (coords.length === 2) {
finalCoords.push(segment[0]);
finalCoords.push(segment[1]);
}
}
});
return helpers.lineString(finalCoords, line.properties);
}
/**
* Process Segment
* Inspiration taken from http://stackoverflow.com/questions/2825412/draw-a-parallel-line
*
* @private
* @param {Array<number>} point1 Point coordinates
* @param {Array<number>} point2 Point coordinates
* @param {number} offset Offset
* @returns {Array<Array<number>>} offset points
*/
function processSegment(point1, point2, offset) {
var L = Math.sqrt(
(point1[0] - point2[0]) * (point1[0] - point2[0]) +
(point1[1] - point2[1]) * (point1[1] - point2[1])
);
var out1x = point1[0] + (offset * (point2[1] - point1[1])) / L;
var out2x = point2[0] + (offset * (point2[1] - point1[1])) / L;
var out1y = point1[1] + (offset * (point1[0] - point2[0])) / L;
var out2y = point2[1] + (offset * (point1[0] - point2[0])) / L;
return [
[out1x, out1y],
[out2x, out2y],
];
}
module.exports = lineOffset;
module.exports.default = lineOffset;
;