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
195 lines (190 loc) • 6.24 kB
JavaScript
import Coordinate from '../geom/Coordinate'
import Polygon from '../geom/Polygon'
import Double from '../../../../java/lang/Double'
import GeometryCollection from '../geom/GeometryCollection'
import ArrayList from '../../../../java/util/ArrayList'
import Comparator from '../../../../java/util/Comparator'
import Assert from '../util/Assert'
export default class InteriorPointArea {
constructor() {
InteriorPointArea.constructor_.apply(this, arguments)
}
static constructor_() {
this._interiorPoint = null
this._maxWidth = -1
const g = arguments[0]
this.process(g)
}
static getInteriorPoint(geom) {
const intPt = new InteriorPointArea(geom)
return intPt.getInteriorPoint()
}
static avg(a, b) {
return (a + b) / 2.0
}
getInteriorPoint() {
return this._interiorPoint
}
process(geom) {
if (geom.isEmpty()) return null
if (geom instanceof Polygon) {
this.processPolygon(geom)
} else if (geom instanceof GeometryCollection) {
const gc = geom
for (let i = 0; i < gc.getNumGeometries(); i++)
this.process(gc.getGeometryN(i))
}
}
processPolygon(polygon) {
const intPtPoly = new InteriorPointPolygon(polygon)
intPtPoly.process()
const width = intPtPoly.getWidth()
if (width > this._maxWidth) {
this._maxWidth = width
this._interiorPoint = intPtPoly.getInteriorPoint()
}
}
}
class InteriorPointPolygon {
constructor() {
InteriorPointPolygon.constructor_.apply(this, arguments)
}
static constructor_() {
this._polygon = null
this._interiorPointY = null
this._interiorSectionWidth = 0.0
this._interiorPoint = null
const polygon = arguments[0]
this._polygon = polygon
this._interiorPointY = ScanLineYOrdinateFinder.getScanLineY(polygon)
}
static isEdgeCrossingCounted(p0, p1, scanY) {
const y0 = p0.getY()
const y1 = p1.getY()
if (y0 === y1) return false
if (y0 === scanY && y1 < scanY) return false
if (y1 === scanY && y0 < scanY) return false
return true
}
static intersectsHorizontalLine() {
if (arguments.length === 2) {
const env = arguments[0], y = arguments[1]
if (y < env.getMinY()) return false
if (y > env.getMaxY()) return false
return true
} else if (arguments.length === 3) {
const p0 = arguments[0], p1 = arguments[1], y = arguments[2]
if (p0.getY() > y && p1.getY() > y) return false
if (p0.getY() < y && p1.getY() < y) return false
return true
}
}
static intersection(p0, p1, Y) {
const x0 = p0.getX()
const x1 = p1.getX()
if (x0 === x1) return x0
const segDX = x1 - x0
const segDY = p1.getY() - p0.getY()
const m = segDY / segDX
const x = x0 + (Y - p0.getY()) / m
return x
}
findBestMidpoint(crossings) {
if (crossings.size() === 0) return null
Assert.isTrue(0 === crossings.size() % 2, 'Interior Point robustness failure: odd number of scanline crossings')
crossings.sort(new DoubleComparator())
for (let i = 0; i < crossings.size(); i += 2) {
const x1 = crossings.get(i)
const x2 = crossings.get(i + 1)
const width = x2 - x1
if (width > this._interiorSectionWidth) {
this._interiorSectionWidth = width
const interiorPointX = InteriorPointArea.avg(x1, x2)
this._interiorPoint = new Coordinate(interiorPointX, this._interiorPointY)
}
}
}
process() {
if (this._polygon.isEmpty()) return null
this._interiorPoint = new Coordinate(this._polygon.getCoordinate())
const crossings = new ArrayList()
this.scanRing(this._polygon.getExteriorRing(), crossings)
for (let i = 0; i < this._polygon.getNumInteriorRing(); i++)
this.scanRing(this._polygon.getInteriorRingN(i), crossings)
this.findBestMidpoint(crossings)
}
scanRing(ring, crossings) {
if (!InteriorPointPolygon.intersectsHorizontalLine(ring.getEnvelopeInternal(), this._interiorPointY)) return null
const seq = ring.getCoordinateSequence()
for (let i = 1; i < seq.size(); i++) {
const ptPrev = seq.getCoordinate(i - 1)
const pt = seq.getCoordinate(i)
this.addEdgeCrossing(ptPrev, pt, this._interiorPointY, crossings)
}
}
getWidth() {
return this._interiorSectionWidth
}
getInteriorPoint() {
return this._interiorPoint
}
addEdgeCrossing(p0, p1, scanY, crossings) {
if (!InteriorPointPolygon.intersectsHorizontalLine(p0, p1, scanY)) return null
if (!InteriorPointPolygon.isEdgeCrossingCounted(p0, p1, scanY)) return null
const xInt = InteriorPointPolygon.intersection(p0, p1, scanY)
crossings.add(xInt)
}
}
class DoubleComparator {
compare(v1, v2) {
return v1 < v2 ? -1 : v1 > v2 ? +1 : 0
}
get interfaces_() {
return [Comparator]
}
}
InteriorPointPolygon.DoubleComparator = DoubleComparator
class ScanLineYOrdinateFinder {
constructor() {
ScanLineYOrdinateFinder.constructor_.apply(this, arguments)
}
static constructor_() {
this._poly = null
this._centreY = null
this._hiY = Double.MAX_VALUE
this._loY = -Double.MAX_VALUE
const poly = arguments[0]
this._poly = poly
this._hiY = poly.getEnvelopeInternal().getMaxY()
this._loY = poly.getEnvelopeInternal().getMinY()
this._centreY = InteriorPointArea.avg(this._loY, this._hiY)
}
static getScanLineY(poly) {
const finder = new ScanLineYOrdinateFinder(poly)
return finder.getScanLineY()
}
updateInterval(y) {
if (y <= this._centreY) {
if (y > this._loY) this._loY = y
} else if (y > this._centreY) {
if (y < this._hiY)
this._hiY = y
}
}
getScanLineY() {
this.process(this._poly.getExteriorRing())
for (let i = 0; i < this._poly.getNumInteriorRing(); i++)
this.process(this._poly.getInteriorRingN(i))
const scanLineY = InteriorPointArea.avg(this._hiY, this._loY)
return scanLineY
}
process(line) {
const seq = line.getCoordinateSequence()
for (let i = 0; i < seq.size(); i++) {
const y = seq.getY(i)
this.updateInterval(y)
}
}
}
InteriorPointArea.InteriorPointPolygon = InteriorPointPolygon
InteriorPointArea.ScanLineYOrdinateFinder = ScanLineYOrdinateFinder