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
324 lines (318 loc) • 12.9 kB
JavaScript
import Location from '../../geom/Location'
import TreeSet from '../../../../../java/util/TreeSet'
import LineString from '../../geom/LineString'
import Geometry from '../../geom/Geometry'
import ConnectedInteriorTester from './ConnectedInteriorTester'
import Coordinate from '../../geom/Coordinate'
import Point from '../../geom/Point'
import Polygon from '../../geom/Polygon'
import MultiPoint from '../../geom/MultiPoint'
import PointLocation from '../../algorithm/PointLocation'
import LinearRing from '../../geom/LinearRing'
import Double from '../../../../../java/lang/Double'
import GeometryGraph from '../../geomgraph/GeometryGraph'
import MultiPolygon from '../../geom/MultiPolygon'
import ConsistentAreaTester from './ConsistentAreaTester'
import GeometryCollection from '../../geom/GeometryCollection'
import UnsupportedOperationException from '../../../../../java/lang/UnsupportedOperationException'
import IndexedNestedRingTester from './IndexedNestedRingTester'
import RobustLineIntersector from '../../algorithm/RobustLineIntersector'
import TopologyValidationError from './TopologyValidationError'
import IndexedPointInAreaLocator from '../../algorithm/locate/IndexedPointInAreaLocator'
import Assert from '../../util/Assert'
export default class IsValidOp {
constructor() {
IsValidOp.constructor_.apply(this, arguments)
}
static constructor_() {
this._parentGeometry = null
this._isSelfTouchingRingFormingHoleValid = false
this._validErr = null
const parentGeometry = arguments[0]
this._parentGeometry = parentGeometry
}
static findPtNotNode(testCoords, searchRing, graph) {
const searchEdge = graph.findEdge(searchRing)
const eiList = searchEdge.getEdgeIntersectionList()
for (let i = 0; i < testCoords.length; i++) {
const pt = testCoords[i]
if (!eiList.isIntersection(pt)) return pt
}
return null
}
static isValid() {
if (arguments[0] instanceof Geometry) {
const geom = arguments[0]
const isValidOp = new IsValidOp(geom)
return isValidOp.isValid()
} else if (arguments[0] instanceof Coordinate) {
const coord = arguments[0]
if (Double.isNaN(coord.x)) return false
if (Double.isInfinite(coord.x)) return false
if (Double.isNaN(coord.y)) return false
if (Double.isInfinite(coord.y)) return false
return true
}
}
checkInvalidCoordinates() {
if (arguments[0] instanceof Array) {
const coords = arguments[0]
for (let i = 0; i < coords.length; i++)
if (!IsValidOp.isValid(coords[i])) {
this._validErr = new TopologyValidationError(TopologyValidationError.INVALID_COORDINATE, coords[i])
return null
}
} else if (arguments[0] instanceof Polygon) {
const poly = arguments[0]
this.checkInvalidCoordinates(poly.getExteriorRing().getCoordinates())
if (this._validErr !== null) return null
for (let i = 0; i < poly.getNumInteriorRing(); i++) {
this.checkInvalidCoordinates(poly.getInteriorRingN(i).getCoordinates())
if (this._validErr !== null) return null
}
}
}
checkHolesNotNested(p, graph) {
if (p.getNumInteriorRing() <= 0) return null
const nestedTester = new IndexedNestedRingTester(graph)
for (let i = 0; i < p.getNumInteriorRing(); i++) {
const innerHole = p.getInteriorRingN(i)
if (innerHole.isEmpty()) continue
nestedTester.add(innerHole)
}
const isNonNested = nestedTester.isNonNested()
if (!isNonNested)
this._validErr = new TopologyValidationError(TopologyValidationError.NESTED_HOLES, nestedTester.getNestedPoint())
}
checkConsistentArea(graph) {
const cat = new ConsistentAreaTester(graph)
const isValidArea = cat.isNodeConsistentArea()
if (!isValidArea) {
this._validErr = new TopologyValidationError(TopologyValidationError.SELF_INTERSECTION, cat.getInvalidPoint())
return null
}
if (cat.hasDuplicateRings())
this._validErr = new TopologyValidationError(TopologyValidationError.DUPLICATE_RINGS, cat.getInvalidPoint())
}
isValid() {
this.checkValid(this._parentGeometry)
return this._validErr === null
}
checkShellInsideHole(shell, hole, graph) {
const shellPts = shell.getCoordinates()
const holePts = hole.getCoordinates()
const shellPt = IsValidOp.findPtNotNode(shellPts, hole, graph)
if (shellPt !== null) {
const insideHole = PointLocation.isInRing(shellPt, holePts)
if (!insideHole)
return shellPt
}
const holePt = IsValidOp.findPtNotNode(holePts, shell, graph)
if (holePt !== null) {
const insideShell = PointLocation.isInRing(holePt, shellPts)
if (insideShell)
return holePt
return null
}
Assert.shouldNeverReachHere('points in shell and hole appear to be equal')
return null
}
checkNoSelfIntersectingRings(graph) {
for (let i = graph.getEdgeIterator(); i.hasNext(); ) {
const e = i.next()
this.checkNoSelfIntersectingRing(e.getEdgeIntersectionList())
if (this._validErr !== null) return null
}
}
checkConnectedInteriors(graph) {
const cit = new ConnectedInteriorTester(graph)
if (!cit.isInteriorsConnected()) this._validErr = new TopologyValidationError(TopologyValidationError.DISCONNECTED_INTERIOR, cit.getCoordinate())
}
checkNoSelfIntersectingRing(eiList) {
const nodeSet = new TreeSet()
let isFirst = true
for (let i = eiList.iterator(); i.hasNext(); ) {
const ei = i.next()
if (isFirst) {
isFirst = false
continue
}
if (nodeSet.contains(ei.coord)) {
this._validErr = new TopologyValidationError(TopologyValidationError.RING_SELF_INTERSECTION, ei.coord)
return null
} else {
nodeSet.add(ei.coord)
}
}
}
checkHolesInShell(p, graph) {
if (p.getNumInteriorRing() <= 0) return null
const shell = p.getExteriorRing()
const isShellEmpty = shell.isEmpty()
const pir = new IndexedPointInAreaLocator(shell)
for (let i = 0; i < p.getNumInteriorRing(); i++) {
const hole = p.getInteriorRingN(i)
let holePt = null
if (hole.isEmpty()) continue
holePt = IsValidOp.findPtNotNode(hole.getCoordinates(), shell, graph)
if (holePt === null) return null
const outside = isShellEmpty || Location.EXTERIOR === pir.locate(holePt)
if (outside) {
this._validErr = new TopologyValidationError(TopologyValidationError.HOLE_OUTSIDE_SHELL, holePt)
return null
}
}
}
checkTooFewPoints(graph) {
if (graph.hasTooFewPoints()) {
this._validErr = new TopologyValidationError(TopologyValidationError.TOO_FEW_POINTS, graph.getInvalidPoint())
return null
}
}
getValidationError() {
this.checkValid(this._parentGeometry)
return this._validErr
}
checkValid() {
if (arguments[0] instanceof Point) {
const g = arguments[0]
this.checkInvalidCoordinates(g.getCoordinates())
} else if (arguments[0] instanceof MultiPoint) {
const g = arguments[0]
this.checkInvalidCoordinates(g.getCoordinates())
} else if (arguments[0] instanceof LinearRing) {
const g = arguments[0]
this.checkInvalidCoordinates(g.getCoordinates())
if (this._validErr !== null) return null
this.checkClosedRing(g)
if (this._validErr !== null) return null
const graph = new GeometryGraph(0, g)
this.checkTooFewPoints(graph)
if (this._validErr !== null) return null
const li = new RobustLineIntersector()
graph.computeSelfNodes(li, true, true)
this.checkNoSelfIntersectingRings(graph)
} else if (arguments[0] instanceof LineString) {
const g = arguments[0]
this.checkInvalidCoordinates(g.getCoordinates())
if (this._validErr !== null) return null
const graph = new GeometryGraph(0, g)
this.checkTooFewPoints(graph)
} else if (arguments[0] instanceof Polygon) {
const g = arguments[0]
this.checkInvalidCoordinates(g)
if (this._validErr !== null) return null
this.checkClosedRings(g)
if (this._validErr !== null) return null
const graph = new GeometryGraph(0, g)
this.checkTooFewPoints(graph)
if (this._validErr !== null) return null
this.checkConsistentArea(graph)
if (this._validErr !== null) return null
if (!this._isSelfTouchingRingFormingHoleValid) {
this.checkNoSelfIntersectingRings(graph)
if (this._validErr !== null) return null
}
this.checkHolesInShell(g, graph)
if (this._validErr !== null) return null
this.checkHolesNotNested(g, graph)
if (this._validErr !== null) return null
this.checkConnectedInteriors(graph)
} else if (arguments[0] instanceof MultiPolygon) {
const g = arguments[0]
for (let i = 0; i < g.getNumGeometries(); i++) {
const p = g.getGeometryN(i)
this.checkInvalidCoordinates(p)
if (this._validErr !== null) return null
this.checkClosedRings(p)
if (this._validErr !== null) return null
}
const graph = new GeometryGraph(0, g)
this.checkTooFewPoints(graph)
if (this._validErr !== null) return null
this.checkConsistentArea(graph)
if (this._validErr !== null) return null
if (!this._isSelfTouchingRingFormingHoleValid) {
this.checkNoSelfIntersectingRings(graph)
if (this._validErr !== null) return null
}
for (let i = 0; i < g.getNumGeometries(); i++) {
const p = g.getGeometryN(i)
this.checkHolesInShell(p, graph)
if (this._validErr !== null) return null
}
for (let i = 0; i < g.getNumGeometries(); i++) {
const p = g.getGeometryN(i)
this.checkHolesNotNested(p, graph)
if (this._validErr !== null) return null
}
this.checkShellsNotNested(g, graph)
if (this._validErr !== null) return null
this.checkConnectedInteriors(graph)
} else if (arguments[0] instanceof GeometryCollection) {
const gc = arguments[0]
for (let i = 0; i < gc.getNumGeometries(); i++) {
const g = gc.getGeometryN(i)
this.checkValid(g)
if (this._validErr !== null) return null
}
} else if (arguments[0] instanceof Geometry) {
const g = arguments[0]
this._validErr = null
if (g.isEmpty()) return null
if (g instanceof Point) this.checkValid(g); else if (g instanceof MultiPoint) this.checkValid(g); else if (g instanceof LinearRing) this.checkValid(g); else if (g instanceof LineString) this.checkValid(g); else if (g instanceof Polygon) this.checkValid(g); else if (g instanceof MultiPolygon) this.checkValid(g); else if (g instanceof GeometryCollection) this.checkValid(g); else throw new UnsupportedOperationException(g.getGeometryType())
}
}
setSelfTouchingRingFormingHoleValid(isValid) {
this._isSelfTouchingRingFormingHoleValid = isValid
}
checkShellNotNested(shell, p, graph) {
const shellPts = shell.getCoordinates()
const polyShell = p.getExteriorRing()
if (polyShell.isEmpty()) return null
const polyPts = polyShell.getCoordinates()
const shellPt = IsValidOp.findPtNotNode(shellPts, polyShell, graph)
if (shellPt === null) return null
const insidePolyShell = PointLocation.isInRing(shellPt, polyPts)
if (!insidePolyShell) return null
if (p.getNumInteriorRing() <= 0) {
this._validErr = new TopologyValidationError(TopologyValidationError.NESTED_SHELLS, shellPt)
return null
}
let badNestedPt = null
for (let i = 0; i < p.getNumInteriorRing(); i++) {
const hole = p.getInteriorRingN(i)
badNestedPt = this.checkShellInsideHole(shell, hole, graph)
if (badNestedPt === null) return null
}
this._validErr = new TopologyValidationError(TopologyValidationError.NESTED_SHELLS, badNestedPt)
}
checkClosedRings(poly) {
this.checkClosedRing(poly.getExteriorRing())
if (this._validErr !== null) return null
for (let i = 0; i < poly.getNumInteriorRing(); i++) {
this.checkClosedRing(poly.getInteriorRingN(i))
if (this._validErr !== null) return null
}
}
checkClosedRing(ring) {
if (ring.isEmpty()) return null
if (!ring.isClosed()) {
let pt = null
if (ring.getNumPoints() >= 1) pt = ring.getCoordinateN(0)
this._validErr = new TopologyValidationError(TopologyValidationError.RING_NOT_CLOSED, pt)
}
}
checkShellsNotNested(mp, graph) {
for (let i = 0; i < mp.getNumGeometries(); i++) {
const p = mp.getGeometryN(i)
const shell = p.getExteriorRing()
for (let j = 0; j < mp.getNumGeometries(); j++) {
if (i === j) continue
const p2 = mp.getGeometryN(j)
this.checkShellNotNested(shell, p2, graph)
if (this._validErr !== null) return null
}
}
}
}