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
333 lines (331 loc) • 11.8 kB
JavaScript
import PointLocator from '../../algorithm/PointLocator'
import Location from '../../geom/Location'
import EdgeNodingValidator from '../../geomgraph/EdgeNodingValidator'
import GeometryCollectionMapper from '../../geom/util/GeometryCollectionMapper'
import PolygonBuilder from './PolygonBuilder'
import Position from '../../geomgraph/Position'
import LineBuilder from './LineBuilder'
import PointBuilder from './PointBuilder'
import SnapIfNeededOverlayOp from './snap/SnapIfNeededOverlayOp'
import Label from '../../geomgraph/Label'
import OverlayNodeFactory from './OverlayNodeFactory'
import { GeometryGraphOp } from '../geometrygraph'
import EdgeList from '../../geomgraph/EdgeList'
import ArrayList from '../../../../../java/util/ArrayList'
import Assert from '../../util/Assert'
import PlanarGraph from '../../geomgraph/PlanarGraph'
import GeometryMapper from '../../geom/util/GeometryMapper'
export default class OverlayOp extends GeometryGraphOp {
constructor () {
const g0 = arguments[0]
const g1 = arguments[1]
super(g0, g1)
this._ptLocator = new PointLocator()
this._geomFact = null
this._resultGeom = null
this._graph = null
this._edgeList = new EdgeList()
this._resultPolyList = new ArrayList()
this._resultLineList = new ArrayList()
this._resultPointList = new ArrayList()
this._graph = new PlanarGraph(new OverlayNodeFactory())
this._geomFact = g0.getFactory()
}
insertUniqueEdge (e) {
const existingEdge = this._edgeList.findEqualEdge(e)
if (existingEdge !== null) {
const existingLabel = existingEdge.getLabel()
let labelToMerge = e.getLabel()
if (!existingEdge.isPointwiseEqual(e)) {
labelToMerge = new Label(e.getLabel())
labelToMerge.flip()
}
const depth = existingEdge.getDepth()
if (depth.isNull()) {
depth.add(existingLabel)
}
depth.add(labelToMerge)
existingLabel.merge(labelToMerge)
} else {
this._edgeList.add(e)
}
}
getGraph () {
return this._graph
}
cancelDuplicateResultEdges () {
for (const it = this._graph.getEdgeEnds().iterator(); it.hasNext();) {
const de = it.next()
const sym = de.getSym()
if (de.isInResult() && sym.isInResult()) {
de.setInResult(false)
sym.setInResult(false)
}
}
}
isCoveredByLA (coord) {
if (this.isCovered(coord, this._resultLineList)) return true
if (this.isCovered(coord, this._resultPolyList)) return true
return false
}
computeGeometry (resultPointList, resultLineList, resultPolyList, opcode) {
const geomList = new ArrayList()
geomList.addAll(resultPointList)
geomList.addAll(resultLineList)
geomList.addAll(resultPolyList)
if (geomList.isEmpty()) return OverlayOp.createEmptyResult(opcode, this._arg[0].getGeometry(), this._arg[1].getGeometry(), this._geomFact)
return this._geomFact.buildGeometry(geomList)
}
mergeSymLabels () {
for (const nodeit = this._graph.getNodes().iterator(); nodeit.hasNext();) {
const node = nodeit.next()
node.getEdges().mergeSymLabels()
}
}
isCovered (coord, geomList) {
for (const it = geomList.iterator(); it.hasNext();) {
const geom = it.next()
const loc = this._ptLocator.locate(coord, geom)
if (loc !== Location.EXTERIOR) return true
}
return false
}
replaceCollapsedEdges () {
const newEdges = new ArrayList()
for (const it = this._edgeList.iterator(); it.hasNext();) {
const e = it.next()
if (e.isCollapsed()) {
it.remove()
newEdges.add(e.getCollapsedEdge())
}
}
this._edgeList.addAll(newEdges)
}
updateNodeLabelling () {
for (const nodeit = this._graph.getNodes().iterator(); nodeit.hasNext();) {
const node = nodeit.next()
const lbl = node.getEdges().getLabel()
node.getLabel().merge(lbl)
}
}
getResultGeometry (overlayOpCode) {
this.computeOverlay(overlayOpCode)
return this._resultGeom
}
insertUniqueEdges (edges) {
for (let i = edges.iterator(); i.hasNext();) {
const e = i.next()
this.insertUniqueEdge(e)
}
}
computeOverlay (opCode) {
this.copyPoints(0)
this.copyPoints(1)
this._arg[0].computeSelfNodes(this._li, false)
this._arg[1].computeSelfNodes(this._li, false)
this._arg[0].computeEdgeIntersections(this._arg[1], this._li, true)
const baseSplitEdges = new ArrayList()
this._arg[0].computeSplitEdges(baseSplitEdges)
this._arg[1].computeSplitEdges(baseSplitEdges)
// const splitEdges = baseSplitEdges
this.insertUniqueEdges(baseSplitEdges)
this.computeLabelsFromDepths()
this.replaceCollapsedEdges()
EdgeNodingValidator.checkValid(this._edgeList.getEdges())
this._graph.addEdges(this._edgeList.getEdges())
this.computeLabelling()
this.labelIncompleteNodes()
this.findResultAreaEdges(opCode)
this.cancelDuplicateResultEdges()
const polyBuilder = new PolygonBuilder(this._geomFact)
polyBuilder.add(this._graph)
this._resultPolyList = polyBuilder.getPolygons()
const lineBuilder = new LineBuilder(this, this._geomFact, this._ptLocator)
this._resultLineList = lineBuilder.build(opCode)
const pointBuilder = new PointBuilder(this, this._geomFact, this._ptLocator)
this._resultPointList = pointBuilder.build(opCode)
this._resultGeom = this.computeGeometry(this._resultPointList, this._resultLineList, this._resultPolyList, opCode)
}
labelIncompleteNode (n, targetIndex) {
const loc = this._ptLocator.locate(n.getCoordinate(), this._arg[targetIndex].getGeometry())
n.getLabel().setLocation(targetIndex, loc)
}
copyPoints (argIndex) {
for (let i = this._arg[argIndex].getNodeIterator(); i.hasNext();) {
const graphNode = i.next()
const newNode = this._graph.addNode(graphNode.getCoordinate())
newNode.setLabel(argIndex, graphNode.getLabel().getLocation(argIndex))
}
}
findResultAreaEdges (opCode) {
for (const it = this._graph.getEdgeEnds().iterator(); it.hasNext();) {
const de = it.next()
const label = de.getLabel()
if (label.isArea() && !de.isInteriorAreaEdge() && OverlayOp.isResultOfOp(label.getLocation(0, Position.RIGHT), label.getLocation(1, Position.RIGHT), opCode)) {
de.setInResult(true)
}
}
}
computeLabelsFromDepths () {
for (const it = this._edgeList.iterator(); it.hasNext();) {
const e = it.next()
const lbl = e.getLabel()
const depth = e.getDepth()
if (!depth.isNull()) {
depth.normalize()
for (let i = 0; i < 2; i++) {
if (!lbl.isNull(i) && lbl.isArea() && !depth.isNull(i)) {
if (depth.getDelta(i) === 0) {
lbl.toLine(i)
} else {
Assert.isTrue(!depth.isNull(i, Position.LEFT), 'depth of LEFT side has not been initialized')
lbl.setLocation(i, Position.LEFT, depth.getLocation(i, Position.LEFT))
Assert.isTrue(!depth.isNull(i, Position.RIGHT), 'depth of RIGHT side has not been initialized')
lbl.setLocation(i, Position.RIGHT, depth.getLocation(i, Position.RIGHT))
}
}
}
}
}
}
computeLabelling () {
for (const nodeit = this._graph.getNodes().iterator(); nodeit.hasNext();) {
const node = nodeit.next()
node.getEdges().computeLabelling(this._arg)
}
this.mergeSymLabels()
this.updateNodeLabelling()
}
labelIncompleteNodes () {
// let nodeCount = 0
for (const ni = this._graph.getNodes().iterator(); ni.hasNext();) {
const n = ni.next()
const label = n.getLabel()
if (n.isIsolated()) {
// nodeCount++
if (label.isNull(0)) this.labelIncompleteNode(n, 0); else this.labelIncompleteNode(n, 1)
}
n.getEdges().updateLabelling(label)
}
}
isCoveredByA (coord) {
if (this.isCovered(coord, this._resultPolyList)) return true
return false
}
interfaces_ () {
return []
}
getClass () {
return OverlayOp
}
}
OverlayOp.overlayOp = function (geom0, geom1, opCode) {
const gov = new OverlayOp(geom0, geom1)
const geomOv = gov.getResultGeometry(opCode)
return geomOv
}
OverlayOp.intersection = function (g, other) {
if (g.isEmpty() || other.isEmpty()) return OverlayOp.createEmptyResult(OverlayOp.INTERSECTION, g, other, g.getFactory())
if (g.isGeometryCollection()) {
const g2 = other
return GeometryCollectionMapper.map(g, {
interfaces_: function () {
return [GeometryMapper.MapOp]
},
map: function (g) {
return g.intersection(g2)
}
})
}
g.checkNotGeometryCollection(g)
g.checkNotGeometryCollection(other)
return SnapIfNeededOverlayOp.overlayOp(g, other, OverlayOp.INTERSECTION)
}
OverlayOp.symDifference = function (g, other) {
if (g.isEmpty() || other.isEmpty()) {
if (g.isEmpty() && other.isEmpty()) return OverlayOp.createEmptyResult(OverlayOp.SYMDIFFERENCE, g, other, g.getFactory())
if (g.isEmpty()) return other.copy()
if (other.isEmpty()) return g.copy()
}
g.checkNotGeometryCollection(g)
g.checkNotGeometryCollection(other)
return SnapIfNeededOverlayOp.overlayOp(g, other, OverlayOp.SYMDIFFERENCE)
}
OverlayOp.resultDimension = function (opCode, g0, g1) {
const dim0 = g0.getDimension()
const dim1 = g1.getDimension()
let resultDimension = -1
switch (opCode) {
case OverlayOp.INTERSECTION:
resultDimension = Math.min(dim0, dim1)
break
case OverlayOp.UNION:
resultDimension = Math.max(dim0, dim1)
break
case OverlayOp.DIFFERENCE:
resultDimension = dim0
break
case OverlayOp.SYMDIFFERENCE:
resultDimension = Math.max(dim0, dim1)
break
default:
}
return resultDimension
}
OverlayOp.createEmptyResult = function (overlayOpCode, a, b, geomFact) {
let result = null
switch (OverlayOp.resultDimension(overlayOpCode, a, b)) {
case -1:
result = geomFact.createGeometryCollection(new Array(0).fill(null))
break
case 0:
result = geomFact.createPoint()
break
case 1:
result = geomFact.createLineString()
break
case 2:
result = geomFact.createPolygon()
break
default:
}
return result
}
OverlayOp.difference = function (g, other) {
if (g.isEmpty()) return OverlayOp.createEmptyResult(OverlayOp.DIFFERENCE, g, other, g.getFactory())
if (other.isEmpty()) return g.copy()
g.checkNotGeometryCollection(g)
g.checkNotGeometryCollection(other)
return SnapIfNeededOverlayOp.overlayOp(g, other, OverlayOp.DIFFERENCE)
}
OverlayOp.isResultOfOp = function () {
if (arguments.length === 2) {
const label = arguments[0]
const opCode = arguments[1]
const loc0 = label.getLocation(0)
const loc1 = label.getLocation(1)
return OverlayOp.isResultOfOp(loc0, loc1, opCode)
} else if (arguments.length === 3) {
let loc0 = arguments[0]
let loc1 = arguments[1]
const overlayOpCode = arguments[2]
if (loc0 === Location.BOUNDARY) loc0 = Location.INTERIOR
if (loc1 === Location.BOUNDARY) loc1 = Location.INTERIOR
switch (overlayOpCode) {
case OverlayOp.INTERSECTION:
return loc0 === Location.INTERIOR && loc1 === Location.INTERIOR
case OverlayOp.UNION:
return loc0 === Location.INTERIOR || loc1 === Location.INTERIOR
case OverlayOp.DIFFERENCE:
return loc0 === Location.INTERIOR && loc1 !== Location.INTERIOR
case OverlayOp.SYMDIFFERENCE:
return (loc0 === Location.INTERIOR && loc1 !== Location.INTERIOR) || (loc0 !== Location.INTERIOR && loc1 === Location.INTERIOR)
default:
}
return false
}
}
OverlayOp.INTERSECTION = 1
OverlayOp.UNION = 2
OverlayOp.DIFFERENCE = 3
OverlayOp.SYMDIFFERENCE = 4