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
250 lines (248 loc) • 8.36 kB
JavaScript
import TreeSet from '../../../../../java/util/TreeSet'
import LineString from '../../geom/LineString'
import Geometry from '../../geom/Geometry'
import hasInterface from '../../../../../hasInterface'
import GeometryFactory from '../../geom/GeometryFactory'
import Collection from '../../../../../java/util/Collection'
import Coordinate from '../../geom/Coordinate'
import Integer from '../../../../../java/lang/Integer'
import LineMergeGraph from './LineMergeGraph'
import LinkedList from '../../../../../java/util/LinkedList'
import GeometryComponentFilter from '../../geom/GeometryComponentFilter'
import ArrayList from '../../../../../java/util/ArrayList'
import ConnectedSubgraphFinder from '../../planargraph/algorithm/ConnectedSubgraphFinder'
import Assert from '../../util/Assert'
import MultiLineString from '../../geom/MultiLineString'
import GraphComponent from '../../planargraph/GraphComponent'
export default class LineSequencer {
constructor () {
this._graph = new LineMergeGraph()
this._factory = new GeometryFactory()
this._lineCount = 0
this._isRun = false
this._sequencedGeometry = null
this._isSequenceable = false
}
addLine (lineString) {
if (this._factory === null) {
this._factory = lineString.getFactory()
}
this._graph.addEdge(lineString)
this._lineCount++
}
hasSequence (graph) {
let oddDegreeCount = 0
for (const i = graph.nodeIterator(); i.hasNext();) {
const node = i.next()
if (node.getDegree() % 2 === 1) oddDegreeCount++
}
return oddDegreeCount <= 2
}
computeSequence () {
if (this._isRun) {
return null
}
this._isRun = true
const sequences = this.findSequences()
if (sequences === null) return null
this._sequencedGeometry = this.buildSequencedGeometry(sequences)
this._isSequenceable = true
const finalLineCount = this._sequencedGeometry.getNumGeometries()
Assert.isTrue(this._lineCount === finalLineCount, 'Lines were missing from result')
Assert.isTrue(this._sequencedGeometry instanceof LineString || this._sequencedGeometry instanceof MultiLineString, 'Result is not lineal')
}
findSequences () {
const sequences = new ArrayList()
const csFinder = new ConnectedSubgraphFinder(this._graph)
const subgraphs = csFinder.getConnectedSubgraphs()
for (const i = subgraphs.iterator(); i.hasNext();) {
const subgraph = i.next()
if (this.hasSequence(subgraph)) {
const seq = this.findSequence(subgraph)
sequences.add(seq)
} else {
return null
}
}
return sequences
}
addReverseSubpath (de, lit, expectedClosed) {
const endNode = de.getToNode()
let fromNode = null
while (true) {
lit.add(de.getSym())
de.getEdge().setVisited(true)
fromNode = de.getFromNode()
const unvisitedOutDE = LineSequencer.findUnvisitedBestOrientedDE(fromNode)
if (unvisitedOutDE === null) break
de = unvisitedOutDE.getSym()
}
if (expectedClosed) {
Assert.isTrue(fromNode === endNode, 'path not contiguous')
}
}
findSequence (graph) {
GraphComponent.setVisited(graph.edgeIterator(), false)
const startNode = LineSequencer.findLowestDegreeNode(graph)
const startDE = startNode.getOutEdges().iterator().next()
const startDESym = startDE.getSym()
const seq = new LinkedList()
const lit = seq.listIterator()
this.addReverseSubpath(startDESym, lit, false)
while (lit.hasPrevious()) {
const prev = lit.previous()
const unvisitedOutDE = LineSequencer.findUnvisitedBestOrientedDE(prev.getFromNode())
if (unvisitedOutDE !== null) this.addReverseSubpath(unvisitedOutDE.getSym(), lit, true)
}
const orientedSeq = this.orient(seq)
return orientedSeq
}
reverse (seq) {
const newSeq = new LinkedList()
for (const i = seq.iterator(); i.hasNext();) {
const de = i.next()
newSeq.addFirst(de.getSym())
}
return newSeq
}
orient (seq) {
const startEdge = seq.get(0)
const endEdge = seq.get(seq.size() - 1)
const startNode = startEdge.getFromNode()
const endNode = endEdge.getToNode()
let flipSeq = false
const hasDegree1Node = startNode.getDegree() === 1 || endNode.getDegree() === 1
if (hasDegree1Node) {
let hasObviousStartNode = false
if (endEdge.getToNode().getDegree() === 1 && endEdge.getEdgeDirection() === false) {
hasObviousStartNode = true
flipSeq = true
}
if (startEdge.getFromNode().getDegree() === 1 && startEdge.getEdgeDirection() === true) {
hasObviousStartNode = true
flipSeq = false
}
if (!hasObviousStartNode) {
if (startEdge.getFromNode().getDegree() === 1) flipSeq = true
}
}
if (flipSeq) return this.reverse(seq)
return seq
}
buildSequencedGeometry (sequences) {
const lines = new ArrayList()
for (const i1 = sequences.iterator(); i1.hasNext();) {
const seq = i1.next()
for (const i2 = seq.iterator(); i2.hasNext();) {
const de = i2.next()
const e = de.getEdge()
const line = e.getLine()
let lineToAdd = line
if (!de.getEdgeDirection() && !line.isClosed()) lineToAdd = LineSequencer.reverse(line)
lines.add(lineToAdd)
}
}
if (lines.size() === 0) return this._factory.createMultiLineString(new Array(0).fill(null))
return this._factory.buildGeometry(lines)
}
getSequencedLineStrings () {
this.computeSequence()
return this._sequencedGeometry
}
isSequenceable () {
this.computeSequence()
return this._isSequenceable
}
add () {
if (hasInterface(arguments[0], Collection)) {
let geometries = arguments[0]
for (const i = geometries.iterator(); i.hasNext();) {
const geometry = i.next()
this.add(geometry)
}
} else if (arguments[0] instanceof Geometry) {
let geometry = arguments[0]
geometry.apply({
interfaces_: function () {
return [GeometryComponentFilter]
},
filter: function (component) {
if (component instanceof LineString) {
this.addLine(component)
}
}
})
}
}
interfaces_ () {
return []
}
getClass () {
return LineSequencer
}
static findUnvisitedBestOrientedDE (node) {
let wellOrientedDE = null
let unvisitedDE = null
for (const i = node.getOutEdges().iterator(); i.hasNext();) {
const de = i.next()
if (!de.getEdge().isVisited()) {
unvisitedDE = de
if (de.getEdgeDirection()) wellOrientedDE = de
}
}
if (wellOrientedDE !== null) return wellOrientedDE
return unvisitedDE
}
static findLowestDegreeNode (graph) {
let minDegree = Integer.MAX_VALUE
let minDegreeNode = null
for (const i = graph.nodeIterator(); i.hasNext();) {
const node = i.next()
if (minDegreeNode === null || node.getDegree() < minDegree) {
minDegree = node.getDegree()
minDegreeNode = node
}
}
return minDegreeNode
}
static isSequenced (geom) {
if (!(geom instanceof MultiLineString)) {
return true
}
const mls = geom
const prevSubgraphNodes = new TreeSet()
let lastNode = null
const currNodes = new ArrayList()
for (let i = 0; i < mls.getNumGeometries(); i++) {
const line = mls.getGeometryN(i)
const startNode = line.getCoordinateN(0)
const endNode = line.getCoordinateN(line.getNumPoints() - 1)
if (prevSubgraphNodes.contains(startNode)) return false
if (prevSubgraphNodes.contains(endNode)) return false
if (lastNode !== null) {
if (!startNode.equals(lastNode)) {
prevSubgraphNodes.addAll(currNodes)
currNodes.clear()
}
}
currNodes.add(startNode)
currNodes.add(endNode)
lastNode = endNode
}
return true
}
static reverse (line) {
const pts = line.getCoordinates()
const revPts = new Array(pts.length).fill(null)
const len = pts.length
for (let i = 0; i < len; i++) {
revPts[len - 1 - i] = new Coordinate(pts[i])
}
return line.getFactory().createLineString(revPts)
}
static sequence (geom) {
const sequencer = new LineSequencer()
sequencer.add(geom)
return sequencer.getSequencedLineStrings()
}
}