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
229 lines (184 loc) • 6.25 kB
JavaScript
/* quadTree node constructor */
function TreeNode(data, x, y, dx, dy) {
var dx_tmp = dx,
dy_tmp = dy,
msb_x = 0,
msb_y = 0;
/* left-bottom corner of current quadrant */
this.x = x;
this.y = y;
/* minimum value in subtree under this node */
this.lowerBound = null;
/* maximum value in subtree under this node */
this.upperBound = null;
/*
* child nodes are layed out in the following way:
*
* (x, y + 1) ---- (x + 1, y + 1)
* | | |
* | D | C |
* | | |
* |----------------------------|
* | | |
* | A | B |
* | | |
* (x, y) ------------ (x + 1, y)
*/
this.childA = null;
this.childB = null;
this.childC = null;
this.childD = null;
if ((dx === 1) && (dy === 1)) {
/* do not further subdivision */
this.lowerBound = Math.min(
data[y][x],
data[y][x + 1],
data[y + 1][x + 1],
data[y + 1][x]
);
this.upperBound = Math.max(
data[y][x],
data[y][x + 1],
data[y + 1][x + 1],
data[y + 1][x]
);
} else {
/* get most significant bit from dx */
if (dx > 1) {
while (dx_tmp !== 0) {
dx_tmp = dx_tmp >> 1;
msb_x++;
}
if (dx === (1 << (msb_x - 1)))
msb_x--;
dx_tmp = 1 << (msb_x - 1);
}
/* get most significant bit from dx */
if (dy > 1) {
while (dy_tmp !== 0) {
dy_tmp = dy_tmp >> 1;
msb_y++;
}
if (dy === (1 << (msb_y - 1)))
msb_y--;
dy_tmp = 1 << (msb_y - 1);
}
this.childA = new TreeNode(data, x, y, dx_tmp, dy_tmp);
this.lowerBound = this.childA.lowerBound;
this.upperBound = this.childA.upperBound;
if (dx - dx_tmp > 0) {
this.childB = new TreeNode(data, x + dx_tmp, y, dx - dx_tmp, dy_tmp);
this.lowerBound = Math.min(this.lowerBound, this.childB.lowerBound);
this.upperBound = Math.max(this.upperBound, this.childB.upperBound);
if (dy - dy_tmp > 0) {
this.childC = new TreeNode(data, x + dx_tmp, y + dy_tmp, dx - dx_tmp, dy - dy_tmp);
this.lowerBound = Math.min(this.lowerBound, this.childC.lowerBound);
this.upperBound = Math.max(this.upperBound, this.childC.upperBound);
}
}
if (dy - dy_tmp > 0) {
this.childD = new TreeNode(data, x, y + dy_tmp, dx_tmp, dy - dy_tmp);
this.lowerBound = Math.min(this.lowerBound, this.childD.lowerBound);
this.upperBound = Math.max(this.upperBound, this.childD.upperBound);
}
}
}
/**
* Retrieve a list of cells within a particular range of values by
* recursivly traversing the quad tree to it's leaves.
*
* @param subsumed If 'true' include all cells that are completely
* subsumed within the specified range. Otherwise,
* return only cells where at least one corner is
* outside the specified range.
*
* @return An array of objects 'o' where each object has exactly two
* properties: 'o.x' and 'o.y' denoting the left-bottom corner
* of the corresponding cell.
*/
TreeNode.prototype.cellsInBand = function(lowerBound, upperBound, subsumed) {
var cells = [];
subsumed = (typeof subsumed === 'undefined') ? true : subsumed;
if ((this.lowerBound > upperBound) || (this.upperBound < lowerBound))
return cells;
if (!(this.childA || this.childB || this.childC || this.childD)) {
if ((subsumed) ||
(this.lowerBound <= lowerBound) ||
(this.upperBound >= upperBound)) {
cells.push({
x: this.x,
y: this.y
});
}
} else {
if (this.childA)
cells = cells.concat(this.childA.cellsInBand(lowerBound, upperBound, subsumed));
if (this.childB)
cells = cells.concat(this.childB.cellsInBand(lowerBound, upperBound, subsumed));
if (this.childD)
cells = cells.concat(this.childD.cellsInBand(lowerBound, upperBound, subsumed));
if (this.childC)
cells = cells.concat(this.childC.cellsInBand(lowerBound, upperBound, subsumed));
}
return cells;
};
TreeNode.prototype.cellsBelowThreshold = function(threshold, subsumed) {
var cells = [];
subsumed = (typeof subsumed === 'undefined') ? true : subsumed;
if (this.lowerBound > threshold)
return cells;
if (!(this.childA || this.childB || this.childC || this.childD)) {
if ((subsumed) ||
(this.upperBound >= threshold)) {
cells.push({
x: this.x,
y: this.y
});
}
} else {
if (this.childA)
cells = cells.concat(this.childA.cellsBelowThreshold(threshold, subsumed));
if (this.childB)
cells = cells.concat(this.childB.cellsBelowThreshold(threshold, subsumed));
if (this.childD)
cells = cells.concat(this.childD.cellsBelowThreshold(threshold, subsumed));
if (this.childC)
cells = cells.concat(this.childC.cellsBelowThreshold(threshold, subsumed));
}
return cells;
};
/*
* Given a scalar field `data` construct a QuadTree
* to efficiently lookup those parts of the scalar
* field where values are within a particular
* range of [lowerbound, upperbound] limits.
*/
function QuadTree(data) {
var i, cols;
/* do some input checking */
if (!data)
throw new Error('data is required');
if (!Array.isArray(data) ||
!Array.isArray(data[0]))
throw new Error('data must be scalar field, i.e. array of arrays');
if (data.length < 2)
throw new Error('data must contain at least two rows');
/* check if we've got a regular grid */
cols = data[0].length;
if (cols < 2)
throw new Error('data must contain at least two columns');
for (i = 1; i < data.length; i++) {
if (!Array.isArray(data[i]))
throw new Error('Row ' + i + ' is not an array');
if (data[i].length != cols)
throw new Error('unequal row lengths detected, please provide a regular grid');
}
/* create pre-processing object */
this.data = data;
/* root node, i.e. entry to the data */
this.root = new TreeNode(data, 0, 0, data[0].length - 1, data.length - 1);
}
export {
QuadTree,
QuadTree as quadTree
};