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
253 lines (250 loc) • 7.5 kB
JavaScript
import TreeSet from '../../../../java/util/TreeSet'
import CGAlgorithms from './CGAlgorithms'
import CoordinateList from '../geom/CoordinateList'
import Arrays from '../../../../java/util/Arrays'
import Stack from '../../../../java/util/Stack'
import CoordinateArrays from '../geom/CoordinateArrays'
import ArrayList from '../../../../java/util/ArrayList'
import Comparator from '../../../../java/util/Comparator'
import UniqueCoordinateArrayFilter from '../util/UniqueCoordinateArrayFilter'
import Assert from '../util/Assert'
export default class ConvexHull {
constructor () {
this._geomFactory = null
this._inputPts = null
if (arguments.length === 1) {
const geometry = arguments[0]
const pts = ConvexHull.extractCoordinates(geometry)
const geomFactory = geometry.getFactory()
this._inputPts = UniqueCoordinateArrayFilter.filterCoordinates(pts)
this._geomFactory = geomFactory
} else if (arguments.length === 2) {
const pts = arguments[0]
const geomFactory = arguments[1]
this._inputPts = UniqueCoordinateArrayFilter.filterCoordinates(pts)
this._geomFactory = geomFactory
}
}
preSort (pts) {
let t = null
for (let i = 1; i < pts.length; i++) {
if ((pts[i].y < pts[0].y || pts[i].y === pts[0].y) && pts[i].x < pts[0].x) {
t = pts[0]
pts[0] = pts[i]
pts[i] = t
}
}
Arrays.sort(pts, 1, pts.length, new RadialComparator(pts[0]))
return pts
}
computeOctRing (inputPts) {
const octPts = this.computeOctPts(inputPts)
const coordList = new CoordinateList()
coordList.add(octPts, false)
if (coordList.size() < 3) {
return null
}
coordList.closeRing()
return coordList.toCoordinateArray()
}
lineOrPolygon (coordinates) {
coordinates = this.cleanRing(coordinates)
if (coordinates.length === 3) {
return this._geomFactory.createLineString([coordinates[0], coordinates[1]])
}
const linearRing = this._geomFactory.createLinearRing(coordinates)
return this._geomFactory.createPolygon(linearRing, null)
}
cleanRing (original) {
Assert.equals(original[0], original[original.length - 1])
const cleanedRing = new ArrayList()
let previousDistinctCoordinate = null
for (let i = 0; i <= original.length - 2; i++) {
const currentCoordinate = original[i]
const nextCoordinate = original[i + 1]
if (currentCoordinate.equals(nextCoordinate)) {
continue
}
if (previousDistinctCoordinate !== null && this.isBetween(previousDistinctCoordinate, currentCoordinate, nextCoordinate)) {
continue
}
cleanedRing.add(currentCoordinate)
previousDistinctCoordinate = currentCoordinate
}
cleanedRing.add(original[original.length - 1])
const cleanedRingCoordinates = new Array(cleanedRing.size()).fill(null)
return cleanedRing.toArray(cleanedRingCoordinates)
}
isBetween (c1, c2, c3) {
if (CGAlgorithms.computeOrientation(c1, c2, c3) !== 0) {
return false
}
if (c1.x !== c3.x) {
if (c1.x <= c2.x && c2.x <= c3.x) {
return true
}
if (c3.x <= c2.x && c2.x <= c1.x) {
return true
}
}
if (c1.y !== c3.y) {
if (c1.y <= c2.y && c2.y <= c3.y) {
return true
}
if (c3.y <= c2.y && c2.y <= c1.y) {
return true
}
}
return false
}
reduce (inputPts) {
const polyPts = this.computeOctRing(inputPts)
if (polyPts === null) return inputPts
const reducedSet = new TreeSet()
for (let i = 0; i < polyPts.length; i++) {
reducedSet.add(polyPts[i])
}
for (let i = 0; i < inputPts.length; i++) {
if (!CGAlgorithms.isPointInRing(inputPts[i], polyPts)) {
reducedSet.add(inputPts[i])
}
}
const reducedPts = CoordinateArrays.toCoordinateArray(reducedSet)
if (reducedPts.length < 3) return this.padArray3(reducedPts)
return reducedPts
}
getConvexHull () {
if (this._inputPts.length === 0) {
return this._geomFactory.createGeometryCollection(null)
}
if (this._inputPts.length === 1) {
return this._geomFactory.createPoint(this._inputPts[0])
}
if (this._inputPts.length === 2) {
return this._geomFactory.createLineString(this._inputPts)
}
let reducedPts = this._inputPts
if (this._inputPts.length > 50) {
reducedPts = this.reduce(this._inputPts)
}
const sortedPts = this.preSort(reducedPts)
const cHS = this.grahamScan(sortedPts)
const cH = this.toCoordinateArray(cHS)
return this.lineOrPolygon(cH)
}
padArray3 (pts) {
const pad = new Array(3).fill(null)
for (let i = 0; i < pad.length; i++) {
if (i < pts.length) {
pad[i] = pts[i]
} else pad[i] = pts[0]
}
return pad
}
computeOctPts (inputPts) {
const pts = new Array(8).fill(null)
for (let j = 0; j < pts.length; j++) {
pts[j] = inputPts[0]
}
for (let i = 1; i < inputPts.length; i++) {
if (inputPts[i].x < pts[0].x) {
pts[0] = inputPts[i]
}
if (inputPts[i].x - inputPts[i].y < pts[1].x - pts[1].y) {
pts[1] = inputPts[i]
}
if (inputPts[i].y > pts[2].y) {
pts[2] = inputPts[i]
}
if (inputPts[i].x + inputPts[i].y > pts[3].x + pts[3].y) {
pts[3] = inputPts[i]
}
if (inputPts[i].x > pts[4].x) {
pts[4] = inputPts[i]
}
if (inputPts[i].x - inputPts[i].y > pts[5].x - pts[5].y) {
pts[5] = inputPts[i]
}
if (inputPts[i].y < pts[6].y) {
pts[6] = inputPts[i]
}
if (inputPts[i].x + inputPts[i].y < pts[7].x + pts[7].y) {
pts[7] = inputPts[i]
}
}
return pts
}
toCoordinateArray (stack) {
const coordinates = new Array(stack.size()).fill(null)
for (let i = 0; i < stack.size(); i++) {
const coordinate = stack.get(i)
coordinates[i] = coordinate
}
return coordinates
}
grahamScan (c) {
let p = null
const ps = new Stack()
p = ps.push(c[0])
p = ps.push(c[1])
p = ps.push(c[2])
for (let i = 3; i < c.length; i++) {
p = ps.pop()
while (!ps.empty() && CGAlgorithms.computeOrientation(ps.peek(), p, c[i]) > 0) {
p = ps.pop()
}
p = ps.push(p)
p = ps.push(c[i])
}
p = ps.push(c[0])
return ps
}
interfaces_ () {
return []
}
getClass () {
return ConvexHull
}
static extractCoordinates (geom) {
const filter = new UniqueCoordinateArrayFilter()
geom.apply(filter)
return filter.getCoordinates()
}
static get RadialComparator () { return RadialComparator }
}
class RadialComparator {
constructor () {
this._origin = null
const origin = arguments[0]
this._origin = origin
}
compare (o1, o2) {
const p1 = o1
const p2 = o2
return RadialComparator.polarCompare(this._origin, p1, p2)
}
interfaces_ () {
return [Comparator]
}
getClass () {
return RadialComparator
}
static polarCompare (o, p, q) {
const dxp = p.x - o.x
const dyp = p.y - o.y
const dxq = q.x - o.x
const dyq = q.y - o.y
const orient = CGAlgorithms.computeOrientation(o, p, q)
if (orient === CGAlgorithms.COUNTERCLOCKWISE) return 1
if (orient === CGAlgorithms.CLOCKWISE) return -1
const op = dxp * dxp + dyp * dyp
const oq = dxq * dxq + dyq * dyq
if (op < oq) {
return -1
}
if (op > oq) {
return 1
}
return 0
}
}