| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323 |
- 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
- }
- }
- }
- }
|