Remove unintended partial newer not working files from jts transpilation

This commit is contained in:
Björn Harrtell 2023-08-23 20:21:46 +02:00
parent ad5af47313
commit 0ae8b18d05
52 changed files with 78 additions and 5152 deletions

View File

@ -1,47 +0,0 @@
import Orientation from './Orientation.js'
import Quadrant from '../geom/Quadrant.js'
export default class PolygonNodeTopology {
static isBetween(origin, p, e0, e1) {
const isGreater0 = PolygonNodeTopology.isAngleGreater(origin, p, e0)
if (!isGreater0) return false
const isGreater1 = PolygonNodeTopology.isAngleGreater(origin, p, e1)
return !isGreater1
}
static isInteriorSegment(nodePt, a0, a1, b) {
let aLo = a0
let aHi = a1
let isInteriorBetween = true
if (PolygonNodeTopology.isAngleGreater(nodePt, aLo, aHi)) {
aLo = a1
aHi = a0
isInteriorBetween = false
}
const isBetween = PolygonNodeTopology.isBetween(nodePt, b, aLo, aHi)
const isInterior = isBetween && isInteriorBetween || !isBetween && !isInteriorBetween
return isInterior
}
static isCrossing(nodePt, a0, a1, b0, b1) {
let aLo = a0
let aHi = a1
if (PolygonNodeTopology.isAngleGreater(nodePt, aLo, aHi)) {
aLo = a1
aHi = a0
}
const isBetween0 = PolygonNodeTopology.isBetween(nodePt, b0, aLo, aHi)
const isBetween1 = PolygonNodeTopology.isBetween(nodePt, b1, aLo, aHi)
return isBetween0 !== isBetween1
}
static isAngleGreater(origin, p, q) {
const quadrantP = PolygonNodeTopology.quadrant(origin, p)
const quadrantQ = PolygonNodeTopology.quadrant(origin, q)
if (quadrantP > quadrantQ) return true
if (quadrantP < quadrantQ) return false
const orient = Orientation.index(origin, q, p)
return orient === Orientation.COUNTERCLOCKWISE
}
static quadrant(origin, p) {
const dx = p.getX() - origin.getX()
const dy = p.getY() - origin.getY()
return Quadrant.quadrant(dx, dy)
}
}

View File

@ -22,10 +22,10 @@ export default class RobustDeterminant {
return sign
else
if (x2 > 0)
return sign
else
return -sign
if (x2 > 0)
return sign
else
return -sign
@ -37,10 +37,10 @@ export default class RobustDeterminant {
return -sign
else
if (x1 > 0)
return -sign
else
return sign
if (x1 > 0)
return -sign
else
return sign
@ -58,48 +58,48 @@ export default class RobustDeterminant {
y2 = swap
}
} else
if (y1 <= -y2) {
sign = -sign
x2 = -x2
y2 = -y2
} else {
swap = x1
x1 = -x2
x2 = swap
swap = y1
y1 = -y2
y2 = swap
}
if (y1 <= -y2) {
sign = -sign
x2 = -x2
y2 = -y2
} else {
swap = x1
x1 = -x2
x2 = swap
swap = y1
y1 = -y2
y2 = swap
}
} else
if (0.0 < y2) {
if (-y1 <= y2) {
sign = -sign
x1 = -x1
y1 = -y1
} else {
swap = -x1
x1 = x2
x2 = swap
swap = -y1
y1 = y2
y2 = swap
}
} else
if (y1 >= y2) {
x1 = -x1
y1 = -y1
x2 = -x2
y2 = -y2
if (0.0 < y2) {
if (-y1 <= y2) {
sign = -sign
x1 = -x1
y1 = -y1
} else {
swap = -x1
x1 = x2
x2 = swap
swap = -y1
y1 = y2
y2 = swap
}
} else
if (y1 >= y2) {
x1 = -x1
y1 = -y1
x2 = -x2
y2 = -y2
} else {
sign = -sign
swap = -x1
x1 = -x2
x2 = swap
swap = -y1
y1 = -y2
y2 = swap
}
} else {
sign = -sign
swap = -x1
x1 = -x2
x2 = swap
swap = -y1
y1 = -y2
y2 = swap
}
if (0.0 < x1) {
@ -112,17 +112,17 @@ export default class RobustDeterminant {
else
return sign
} else
if (0.0 < x2) {
return -sign
} else
if (x1 >= x2) {
sign = -sign
x1 = -x1
x2 = -x2
if (0.0 < x2) {
return -sign
} else
if (x1 >= x2) {
sign = -sign
x1 = -x1
x2 = -x2
} else {
return -sign
}
} else {
return -sign
}
while (true) {

View File

@ -32,7 +32,7 @@ export default class Quadrant {
if (dx >= 0.0)
if (dy >= 0.0) return Quadrant.NE; else return Quadrant.SE
else
if (dy >= 0.0) return Quadrant.NW; else return Quadrant.SW
if (dy >= 0.0) return Quadrant.NW; else return Quadrant.SW
} else if (arguments[0] instanceof Coordinate && arguments[1] instanceof Coordinate) {
const p0 = arguments[0], p1 = arguments[1]
@ -40,7 +40,7 @@ export default class Quadrant {
if (p1.x >= p0.x)
if (p1.y >= p0.y) return Quadrant.NE; else return Quadrant.SE
else
if (p1.y >= p0.y) return Quadrant.NW; else return Quadrant.SW
if (p1.y >= p0.y) return Quadrant.NW; else return Quadrant.SW
}
}

View File

@ -11,13 +11,13 @@ export default class Octant {
if (dy >= 0)
if (adx >= ady) return 0; else return 1
else
if (adx >= ady) return 7; else return 6
if (adx >= ady) return 7; else return 6
else
if (dy >= 0)
if (adx >= ady) return 3; else return 2
else
if (adx >= ady) return 4; else return 5
if (dy >= 0)
if (adx >= ady) return 3; else return 2
else
if (adx >= ady) return 4; else return 5
} else if (arguments[0] instanceof Coordinate && arguments[1] instanceof Coordinate) {

View File

@ -37,10 +37,10 @@ export default class SegmentStringDissolver {
if (existing === null) {
this.add(oca, segString)
} else
if (this._merger !== null) {
const isSameOrientation = CoordinateArrays.equals(existing.getCoordinates(), segString.getCoordinates())
this._merger.merge(existing, segString, isSameOrientation)
}
if (this._merger !== null) {
const isSameOrientation = CoordinateArrays.equals(existing.getCoordinates(), segString.getCoordinates())
this._merger.merge(existing, segString, isSameOrientation)
}
}
}

View File

@ -1,159 +0,0 @@
import Location from '../../geom/Location.js'
import LineString from '../../geom/LineString.js'
import Position from '../../geom/Position.js'
import Point from '../../geom/Point.js'
import NodedSegmentString from '../../noding/NodedSegmentString.js'
import Polygon from '../../geom/Polygon.js'
import MultiPoint from '../../geom/MultiPoint.js'
import OffsetCurveBuilder from './OffsetCurveBuilder.js'
import LinearRing from '../../geom/LinearRing.js'
import Orientation from '../../algorithm/Orientation.js'
import MultiPolygon from '../../geom/MultiPolygon.js'
import Label from '../../geomgraph/Label.js'
import GeometryCollection from '../../geom/GeometryCollection.js'
import UnsupportedOperationException from '../../../../../java/lang/UnsupportedOperationException.js'
import CoordinateArrays from '../../geom/CoordinateArrays.js'
import ArrayList from '../../../../../java/util/ArrayList.js'
import Distance from '../../algorithm/Distance.js'
import MultiLineString from '../../geom/MultiLineString.js'
import Triangle from '../../geom/Triangle.js'
export default class BufferCurveSetBuilder {
constructor() {
BufferCurveSetBuilder.constructor_.apply(this, arguments)
}
static constructor_() {
this._inputGeom = null
this._distance = null
this._curveBuilder = null
this._curveList = new ArrayList()
this._isInvertOrientation = false
const inputGeom = arguments[0], distance = arguments[1], precisionModel = arguments[2], bufParams = arguments[3]
this._inputGeom = inputGeom
this._distance = distance
this._curveBuilder = new OffsetCurveBuilder(precisionModel, bufParams)
}
static clean(coords) {
return CoordinateArrays.removeRepeatedOrInvalidPoints(coords)
}
static isTriangleErodedCompletely(triangleCoord, bufferDistance) {
const tri = new Triangle(triangleCoord[0], triangleCoord[1], triangleCoord[2])
const inCentre = tri.inCentre()
const distToCentre = Distance.pointToSegment(inCentre, tri.p0, tri.p1)
return distToCentre < Math.abs(bufferDistance)
}
static isRingCurveInverted(inputPts, distance, curvePts) {
if (distance === 0.0) return false
if (inputPts.length <= 3) return false
if (inputPts.length >= BufferCurveSetBuilder.MAX_INVERTED_RING_SIZE) return false
if (curvePts.length > BufferCurveSetBuilder.INVERTED_CURVE_VERTEX_FACTOR * inputPts.length) return false
const distTol = BufferCurveSetBuilder.NEARNESS_FACTOR * Math.abs(distance)
const maxDist = BufferCurveSetBuilder.maxDistance(curvePts, inputPts)
const isCurveTooClose = maxDist < distTol
return isCurveTooClose
}
static maxDistance(pts, line) {
let maxDistance = 0
for (const p of pts) {
const dist = Distance.pointToSegmentString(p, line)
if (dist > maxDistance)
maxDistance = dist
}
return maxDistance
}
static isErodedCompletely(ring, bufferDistance) {
const ringCoord = ring.getCoordinates()
if (ringCoord.length < 4) return bufferDistance < 0
if (ringCoord.length === 4) return BufferCurveSetBuilder.isTriangleErodedCompletely(ringCoord, bufferDistance)
const env = ring.getEnvelopeInternal()
const envMinDimension = Math.min(env.getHeight(), env.getWidth())
if (bufferDistance < 0.0 && 2 * Math.abs(bufferDistance) > envMinDimension) return true
return false
}
addRingSide(coord, offsetDistance, side, cwLeftLoc, cwRightLoc) {
if (offsetDistance === 0.0 && coord.length < LinearRing.MINIMUM_VALID_SIZE) return null
let leftLoc = cwLeftLoc
let rightLoc = cwRightLoc
const isCCW = this.isRingCCW(coord)
if (coord.length >= LinearRing.MINIMUM_VALID_SIZE && isCCW) {
leftLoc = cwRightLoc
rightLoc = cwLeftLoc
side = Position.opposite(side)
}
const curve = this._curveBuilder.getRingCurve(coord, side, offsetDistance)
if (BufferCurveSetBuilder.isRingCurveInverted(coord, offsetDistance, curve))
return null
this.addCurve(curve, leftLoc, rightLoc)
}
isRingCCW(coord) {
const isCCW = Orientation.isCCWArea(coord)
if (this._isInvertOrientation) return !isCCW
return isCCW
}
addRingBothSides(coord, distance) {
this.addRingSide(coord, distance, Position.LEFT, Location.EXTERIOR, Location.INTERIOR)
this.addRingSide(coord, distance, Position.RIGHT, Location.INTERIOR, Location.EXTERIOR)
}
addPoint(p) {
if (this._distance <= 0.0) return null
const coord = p.getCoordinates()
if (coord.length >= 1 && !coord[0].isValid()) return null
const curve = this._curveBuilder.getLineCurve(coord, this._distance)
this.addCurve(curve, Location.EXTERIOR, Location.INTERIOR)
}
addPolygon(p) {
let offsetDistance = this._distance
let offsetSide = Position.LEFT
if (this._distance < 0.0) {
offsetDistance = -this._distance
offsetSide = Position.RIGHT
}
const shell = p.getExteriorRing()
const shellCoord = BufferCurveSetBuilder.clean(shell.getCoordinates())
if (this._distance < 0.0 && BufferCurveSetBuilder.isErodedCompletely(shell, this._distance)) return null
if (this._distance <= 0.0 && shellCoord.length < 3) return null
this.addRingSide(shellCoord, offsetDistance, offsetSide, Location.EXTERIOR, Location.INTERIOR)
for (let i = 0; i < p.getNumInteriorRing(); i++) {
const hole = p.getInteriorRingN(i)
const holeCoord = BufferCurveSetBuilder.clean(hole.getCoordinates())
if (this._distance > 0.0 && BufferCurveSetBuilder.isErodedCompletely(hole, -this._distance)) continue
this.addRingSide(holeCoord, offsetDistance, Position.opposite(offsetSide), Location.INTERIOR, Location.EXTERIOR)
}
}
setInvertOrientation(isInvertOrientation) {
this._isInvertOrientation = isInvertOrientation
}
addLineString(line) {
if (this._curveBuilder.isLineOffsetEmpty(this._distance)) return null
const coord = BufferCurveSetBuilder.clean(line.getCoordinates())
if (CoordinateArrays.isRing(coord) && !this._curveBuilder.getBufferParameters().isSingleSided()) {
this.addRingBothSides(coord, this._distance)
} else {
const curve = this._curveBuilder.getLineCurve(coord, this._distance)
this.addCurve(curve, Location.EXTERIOR, Location.INTERIOR)
}
}
addCurve(coord, leftLoc, rightLoc) {
if (coord === null || coord.length < 2) return null
const e = new NodedSegmentString(coord, new Label(0, Location.BOUNDARY, leftLoc, rightLoc))
this._curveList.add(e)
}
getCurves() {
this.add(this._inputGeom)
return this._curveList
}
add(g) {
if (g.isEmpty()) return null
if (g instanceof Polygon) this.addPolygon(g); else if (g instanceof LineString) this.addLineString(g); else if (g instanceof Point) this.addPoint(g); else if (g instanceof MultiPoint) this.addCollection(g); else if (g instanceof MultiLineString) this.addCollection(g); else if (g instanceof MultiPolygon) this.addCollection(g); else if (g instanceof GeometryCollection) this.addCollection(g); else throw new UnsupportedOperationException(g.getClass().getName())
}
addCollection(gc) {
for (let i = 0; i < gc.getNumGeometries(); i++) {
const g = gc.getGeometryN(i)
this.add(g)
}
}
}
BufferCurveSetBuilder.MAX_INVERTED_RING_SIZE = 9
BufferCurveSetBuilder.INVERTED_CURVE_VERTEX_FACTOR = 4
BufferCurveSetBuilder.NEARNESS_FACTOR = 0.99

View File

@ -1,238 +0,0 @@
import BufferParameters from './BufferParameters.js'
import MonotoneChainSelectAction from '../../index/chain/MonotoneChainSelectAction.js'
import LineString from '../../geom/LineString.js'
import CoordinateList from '../../geom/CoordinateList.js'
import MonotoneChain from '../../index/chain/MonotoneChain.js'
import Point from '../../geom/Point.js'
import Polygon from '../../geom/Polygon.js'
import GeometryMapper from '../../geom/util/GeometryMapper.js'
import SegmentMCIndex from './SegmentMCIndex.js'
import OffsetCurveBuilder from './OffsetCurveBuilder.js'
import LinearRing from '../../geom/LinearRing.js'
import BufferOp from './BufferOp.js'
import LineSegment from '../../geom/LineSegment.js'
import Envelope from '../../geom/Envelope.js'
import Distance from '../../algorithm/Distance.js'
export default class OffsetCurve {
constructor() {
OffsetCurve.constructor_.apply(this, arguments)
}
static constructor_() {
this._inputGeom = null
this._distance = null
this._bufferParams = null
this._matchDistance = null
this._geomFactory = null
if (arguments.length === 2) {
const geom = arguments[0], distance = arguments[1]
OffsetCurve.constructor_.call(this, geom, distance, null)
} else if (arguments.length === 3) {
const geom = arguments[0], distance = arguments[1], bufParams = arguments[2]
this._inputGeom = geom
this._distance = distance
this._matchDistance = Math.abs(distance) / OffsetCurve.NEARNESS_FACTOR
this._geomFactory = this._inputGeom.getFactory()
this._bufferParams = new BufferParameters()
if (bufParams !== null) {
this._bufferParams.setQuadrantSegments(bufParams.getQuadrantSegments())
this._bufferParams.setJoinStyle(bufParams.getJoinStyle())
this._bufferParams.setMitreLimit(bufParams.getMitreLimit())
}
}
}
static subsegmentMatchFrac(p0, p1, seg0, seg1, matchDistance) {
if (matchDistance < Distance.pointToSegment(p0, seg0, seg1)) return -1
if (matchDistance < Distance.pointToSegment(p1, seg0, seg1)) return -1
const seg = new LineSegment(seg0, seg1)
return seg.segmentFraction(p0)
}
static extractMaxAreaPolygon(geom) {
if (geom.getNumGeometries() === 1) return geom
let maxArea = 0
let maxPoly = null
for (let i = 0; i < geom.getNumGeometries(); i++) {
const poly = geom.getGeometryN(i)
const area = poly.getArea()
if (maxPoly === null || area > maxArea) {
maxPoly = poly
maxArea = area
}
}
return maxPoly
}
static extractSection(ring, startIndex, isExtracted) {
if (startIndex < 0) return new Array(0).fill(null)
const coordList = new CoordinateList()
let i = startIndex
do {
coordList.add(ring[i], false)
if (!isExtracted[i])
break
i = OffsetCurve.next(i, ring.length - 1)
} while (i !== startIndex)
if (isExtracted[i])
coordList.add(ring[i], false)
if (coordList.size() === 1) return new Array(0).fill(null)
return coordList.toCoordinateArray()
}
static next(i, size) {
i += 1
return i < size ? i : 0
}
static getBufferOriented(geom, distance, bufParams) {
const buffer = BufferOp.bufferOp(geom, Math.abs(distance), bufParams)
let bufferPoly = OffsetCurve.extractMaxAreaPolygon(buffer)
if (distance < 0)
bufferPoly = bufferPoly.reverse()
return bufferPoly
}
static extractLongestHole(poly) {
let largestHole = null
let maxLen = -1
for (let i = 0; i < poly.getNumInteriorRing(); i++) {
const hole = poly.getInteriorRingN(i)
const len = hole.getLength()
if (len > maxLen) {
largestHole = hole
maxLen = len
}
}
return largestHole
}
static rawOffset() {
if (arguments.length === 2) {
const geom = arguments[0], distance = arguments[1]
return OffsetCurve.rawOffset(geom, distance, new BufferParameters())
} else if (arguments.length === 3) {
const geom = arguments[0], distance = arguments[1], bufParams = arguments[2]
const ocb = new OffsetCurveBuilder(geom.getFactory().getPrecisionModel(), bufParams)
const pts = ocb.getOffsetCurve(geom.getCoordinates(), distance)
return pts
}
}
static getCurve() {
if (arguments.length === 2) {
const geom = arguments[0], distance = arguments[1]
const oc = new OffsetCurve(geom, distance)
return oc.getCurve()
} else if (arguments.length === 5) {
const geom = arguments[0], distance = arguments[1], quadSegs = arguments[2], joinStyle = arguments[3], mitreLimit = arguments[4]
const bufferParams = new BufferParameters()
if (quadSegs >= 0) bufferParams.setQuadrantSegments(quadSegs)
if (joinStyle >= 0) bufferParams.setJoinStyle(joinStyle)
if (mitreLimit >= 0) bufferParams.setMitreLimit(mitreLimit)
const oc = new OffsetCurve(geom, distance, bufferParams)
return oc.getCurve()
}
}
getCurve() {
return GeometryMapper.flatMap(this._inputGeom, 1, new (class {
get interfaces_() {
return [MapOp]
}
map(geom) {
if (geom instanceof Point) return null
if (geom instanceof Polygon)
return this.toLineString(geom.buffer(this._distance).getBoundary())
return this.computeCurve(geom, this._distance)
}
toLineString(geom) {
if (geom instanceof LinearRing) {
const ring = geom
return geom.getFactory().createLineString(ring.getCoordinateSequence())
}
return geom
}
})())
}
computeCurve() {
if (arguments[0] instanceof LineString && typeof arguments[1] === 'number') {
const lineGeom = arguments[0], distance = arguments[1]
if (lineGeom.getNumPoints() < 2 || lineGeom.getLength() === 0.0)
return this._geomFactory.createLineString()
if (lineGeom.getNumPoints() === 2)
return this.offsetSegment(lineGeom.getCoordinates(), distance)
const rawOffset = OffsetCurve.rawOffset(lineGeom, distance, this._bufferParams)
if (rawOffset.length === 0)
return this._geomFactory.createLineString()
const bufferPoly = OffsetCurve.getBufferOriented(lineGeom, distance, this._bufferParams)
const shell = bufferPoly.getExteriorRing().getCoordinates()
let offsetCurve = this.computeCurve(shell, rawOffset)
if (!offsetCurve.isEmpty() || bufferPoly.getNumInteriorRing() === 0) return offsetCurve
const holePts = OffsetCurve.extractLongestHole(bufferPoly).getCoordinates()
offsetCurve = this.computeCurve(holePts, rawOffset)
return offsetCurve
} else if (arguments[0] instanceof Array && arguments[1] instanceof Array) {
const bufferPts = arguments[0], rawOffset = arguments[1]
const isInCurve = new Array(bufferPts.length - 1).fill(null)
const segIndex = new SegmentMCIndex(bufferPts)
let curveStart = -1
for (let i = 0; i < rawOffset.length - 1; i++) {
const index = this.markMatchingSegments(rawOffset[i], rawOffset[i + 1], segIndex, bufferPts, isInCurve)
if (curveStart < 0)
curveStart = index
}
const curvePts = OffsetCurve.extractSection(bufferPts, curveStart, isInCurve)
return this._geomFactory.createLineString(curvePts)
}
}
offsetSegment(pts, distance) {
const offsetSeg = new LineSegment(pts[0], pts[1]).offset(distance)
return this._geomFactory.createLineString([offsetSeg.p0, offsetSeg.p1])
}
markMatchingSegments(p0, p1, segIndex, bufferPts, isInCurve) {
const matchEnv = new Envelope(p0, p1)
matchEnv.expandBy(this._matchDistance)
const action = new MatchCurveSegmentAction(p0, p1, bufferPts, this._matchDistance, isInCurve)
segIndex.query(matchEnv, action)
return action.getMinCurveIndex()
}
}
class MatchCurveSegmentAction extends MonotoneChainSelectAction {
constructor() {
super()
MatchCurveSegmentAction.constructor_.apply(this, arguments)
}
static constructor_() {
this._p0 = null
this._p1 = null
this._bufferPts = null
this._matchDistance = null
this._isInCurve = null
this._minFrac = -1
this._minCurveIndex = -1
const p0 = arguments[0], p1 = arguments[1], bufferPts = arguments[2], matchDistance = arguments[3], isInCurve = arguments[4]
this._p0 = p0
this._p1 = p1
this._bufferPts = bufferPts
this._matchDistance = matchDistance
this._isInCurve = isInCurve
}
select() {
if (arguments.length === 2 && (Number.isInteger(arguments[1]) && arguments[0] instanceof MonotoneChain)) {
const mc = arguments[0], segIndex = arguments[1]
const frac = OffsetCurve.subsegmentMatchFrac(this._bufferPts[segIndex], this._bufferPts[segIndex + 1], this._p0, this._p1, this._matchDistance)
if (frac < 0) return null
this._isInCurve[segIndex] = true
if (this._minFrac < 0 || frac < this._minFrac) {
this._minFrac = frac
this._minCurveIndex = segIndex
}
} else {
return super.select.apply(this, arguments)
}
}
getMinCurveIndex() {
return this._minCurveIndex
}
}
OffsetCurve.MatchCurveSegmentAction = MatchCurveSegmentAction
OffsetCurve.NEARNESS_FACTOR = 10000

View File

@ -112,12 +112,12 @@ export default class OffsetCurveBuilder {
if (inputPts.length <= 1) {
this.computePointCurve(inputPts[0], segGen)
} else
if (this._bufParams.isSingleSided()) {
const isRightSide = distance < 0.0
this.computeSingleSidedBufferCurve(inputPts, isRightSide, segGen)
} else {
this.computeLineBufferCurve(inputPts, segGen)
}
if (this._bufParams.isSingleSided()) {
const isRightSide = distance < 0.0
this.computeSingleSidedBufferCurve(inputPts, isRightSide, segGen)
} else {
this.computeLineBufferCurve(inputPts, segGen)
}
const lineCoord = segGen.getCoordinates()
return lineCoord

View File

@ -1,32 +0,0 @@
import STRtree from '../../index/strtree/STRtree.js'
import ItemVisitor from '../../index/ItemVisitor.js'
import MonotoneChainBuilder from '../../index/chain/MonotoneChainBuilder.js'
export default class SegmentMCIndex {
constructor() {
SegmentMCIndex.constructor_.apply(this, arguments)
}
static constructor_() {
this._index = null
const segs = arguments[0]
this._index = this.buildIndex(segs)
}
buildIndex(segs) {
const index = new STRtree()
const segChains = MonotoneChainBuilder.getChains(segs, segs)
for (const mc of segChains)
index.insert(mc.getEnvelope(), mc)
return index
}
query(env, action) {
this._index.query(env, new (class {
get interfaces_() {
return [ItemVisitor]
}
visitItem(item) {
const testChain = item
testChain.select(env, action)
}
})())
}
}

View File

@ -1,12 +0,0 @@
import OverlayNG from './OverlayNG.js'
import SegmentExtractingNoder from '../../noding/SegmentExtractingNoder.js'
import BoundaryChainNoder from '../../noding/BoundaryChainNoder.js'
export default class CoverageUnion {
static union(coverage) {
let noder = new BoundaryChainNoder()
if (coverage.getDimension() < 2)
noder = new SegmentExtractingNoder()
return OverlayNG.union(coverage, null, noder)
}
}

View File

@ -1,184 +0,0 @@
import Location from '../../geom/Location.js'
import OverlayLabel from './OverlayLabel.js'
import WKTWriter from '../../io/WKTWriter.js'
import Integer from '../../../../../java/lang/Integer.js'
import Dimension from '../../geom/Dimension.js'
import IllegalStateException from '../../../../../java/lang/IllegalStateException.js'
export default class Edge {
constructor() {
Edge.constructor_.apply(this, arguments)
}
static constructor_() {
this._pts = null
this._aDim = OverlayLabel.DIM_UNKNOWN
this._aDepthDelta = 0
this._aIsHole = false
this._bDim = OverlayLabel.DIM_UNKNOWN
this._bDepthDelta = 0
this._bIsHole = false
const pts = arguments[0], info = arguments[1]
this._pts = pts
this.copyInfo(info)
}
static isCollapsed(pts) {
if (pts.length < 2) return true
if (pts[0].equals2D(pts[1])) return true
if (pts.length > 2)
if (pts[pts.length - 1].equals2D(pts[pts.length - 2])) return true
return false
}
static hasAreaParent(dim) {
return dim === OverlayLabel.DIM_BOUNDARY || dim === OverlayLabel.DIM_COLLAPSE
}
static labelDim(dim, depthDelta) {
if (dim === Dimension.FALSE) return OverlayLabel.DIM_NOT_PART
if (dim === Dimension.L) return OverlayLabel.DIM_LINE
const isCollapse = depthDelta === 0
if (isCollapse) return OverlayLabel.DIM_COLLAPSE
return OverlayLabel.DIM_BOUNDARY
}
static infoString(index, dim, isHole, depthDelta) {
return (index === 0 ? 'A:' : 'B:') + OverlayLabel.dimensionSymbol(dim) + Edge.ringRoleSymbol(dim, isHole) + Integer.toString(depthDelta)
}
static initLabel(lbl, geomIndex, dim, depthDelta, isHole) {
const dimLabel = Edge.labelDim(dim, depthDelta)
switch (dimLabel) {
case OverlayLabel.DIM_NOT_PART:
lbl.initNotPart(geomIndex)
break
case OverlayLabel.DIM_BOUNDARY:
lbl.initBoundary(geomIndex, Edge.locationLeft(depthDelta), Edge.locationRight(depthDelta), isHole)
break
case OverlayLabel.DIM_COLLAPSE:
lbl.initCollapse(geomIndex, isHole)
break
case OverlayLabel.DIM_LINE:
lbl.initLine(geomIndex)
break
}
}
static locationRight(depthDelta) {
const delSign = Edge.delSign(depthDelta)
switch (delSign) {
case 0:
return OverlayLabel.LOC_UNKNOWN
case 1:
return Location.INTERIOR
case -1:
return Location.EXTERIOR
}
return OverlayLabel.LOC_UNKNOWN
}
static toStringPts(pts) {
const orig = pts[0]
const dest = pts[pts.length - 1]
const dirPtStr = pts.length > 2 ? ', ' + WKTWriter.format(pts[1]) : ''
const ptsStr = WKTWriter.format(orig) + dirPtStr + ' .. ' + WKTWriter.format(dest)
return ptsStr
}
static delSign(depthDel) {
if (depthDel > 0) return 1
if (depthDel < 0) return -1
return 0
}
static locationLeft(depthDelta) {
const delSign = Edge.delSign(depthDelta)
switch (delSign) {
case 0:
return OverlayLabel.LOC_UNKNOWN
case 1:
return Location.EXTERIOR
case -1:
return Location.INTERIOR
}
return OverlayLabel.LOC_UNKNOWN
}
static ringRoleSymbol(dim, isHole) {
if (Edge.hasAreaParent(dim)) return '' + OverlayLabel.ringRoleSymbol(isHole)
return ''
}
static isHoleMerged(geomIndex, edge1, edge2) {
const isShell1 = edge1.isShell(geomIndex)
const isShell2 = edge2.isShell(geomIndex)
const isShellMerged = isShell1 || isShell2
return !isShellMerged
}
getCoordinates() {
return this._pts
}
size() {
return this._pts.length
}
getCoordinate(index) {
return this._pts[index]
}
direction() {
const pts = this.getCoordinates()
if (pts.length < 2)
throw new IllegalStateException('Edge must have >= 2 points')
const p0 = pts[0]
const p1 = pts[1]
const pn0 = pts[pts.length - 1]
const pn1 = pts[pts.length - 2]
let cmp = 0
const cmp0 = p0.compareTo(pn0)
if (cmp0 !== 0) cmp = cmp0
if (cmp === 0) {
const cmp1 = p1.compareTo(pn1)
if (cmp1 !== 0) cmp = cmp1
}
if (cmp === 0)
throw new IllegalStateException('Edge direction cannot be determined because endpoints are equal')
return cmp === -1
}
createLabel() {
const lbl = new OverlayLabel()
Edge.initLabel(lbl, 0, this._aDim, this._aDepthDelta, this._aIsHole)
Edge.initLabel(lbl, 1, this._bDim, this._bDepthDelta, this._bIsHole)
return lbl
}
relativeDirection(edge2) {
if (!this.getCoordinate(0).equals2D(edge2.getCoordinate(0))) return false
if (!this.getCoordinate(1).equals2D(edge2.getCoordinate(1))) return false
return true
}
copyInfo(info) {
if (info.getIndex() === 0) {
this._aDim = info.getDimension()
this._aIsHole = info.isHole()
this._aDepthDelta = info.getDepthDelta()
} else {
this._bDim = info.getDimension()
this._bIsHole = info.isHole()
this._bDepthDelta = info.getDepthDelta()
}
}
isShell(geomIndex) {
if (geomIndex === 0)
return this._aDim === OverlayLabel.DIM_BOUNDARY && !this._aIsHole
return this._bDim === OverlayLabel.DIM_BOUNDARY && !this._bIsHole
}
merge(edge) {
this._aIsHole = Edge.isHoleMerged(0, this, edge)
this._bIsHole = Edge.isHoleMerged(1, this, edge)
if (edge._aDim > this._aDim) this._aDim = edge._aDim
if (edge._bDim > this._bDim) this._bDim = edge._bDim
const relDir = this.relativeDirection(edge)
const flipFactor = relDir ? 1 : -1
this._aDepthDelta += flipFactor * edge._aDepthDelta
this._bDepthDelta += flipFactor * edge._bDepthDelta
}
toLineString() {
return WKTWriter.toLineString(this._pts)
}
toString() {
const ptsStr = Edge.toStringPts(this._pts)
const aInfo = Edge.infoString(0, this._aDim, this._aIsHole, this._aDepthDelta)
const bInfo = Edge.infoString(1, this._bDim, this._bIsHole, this._bDepthDelta)
return 'Edge( ' + ptsStr + ' ) ' + aInfo + '/' + bInfo
}
}

View File

@ -1,76 +0,0 @@
import OrdinateFormat from '../../io/OrdinateFormat.js'
import Double from '../../../../../java/lang/Double.js'
import Comparable from '../../../../../java/lang/Comparable.js'
export default class EdgeKey {
constructor() {
EdgeKey.constructor_.apply(this, arguments)
}
static constructor_() {
this._p0x = null
this._p0y = null
this._p1x = null
this._p1y = null
const edge = arguments[0]
this.initPoints(edge)
}
static create(edge) {
return new EdgeKey(edge)
}
static hashCode() {
if (arguments.length === 1 && typeof arguments[0] === 'number') {
const x = arguments[0]
const f = Double.doubleToLongBits(x)
return Math.trunc(f ^ f >>> 32)
}
}
format(x, y) {
return OrdinateFormat.DEFAULT.format(x) + ' ' + OrdinateFormat.DEFAULT.format(y)
}
equals(o) {
if (!(o instanceof EdgeKey))
return false
const ek = o
return this._p0x === ek._p0x && this._p0y === ek._p0y && this._p1x === ek._p1x && this._p1y === ek._p1y
}
initPoints(edge) {
const direction = edge.direction()
if (direction) {
this.init(edge.getCoordinate(0), edge.getCoordinate(1))
} else {
const len = edge.size()
this.init(edge.getCoordinate(len - 1), edge.getCoordinate(len - 2))
}
}
compareTo(ek) {
if (this._p0x < ek._p0x) return -1
if (this._p0x > ek._p0x) return 1
if (this._p0y < ek._p0y) return -1
if (this._p0y > ek._p0y) return 1
if (this._p1x < ek._p1x) return -1
if (this._p1x > ek._p1x) return 1
if (this._p1y < ek._p1y) return -1
if (this._p1y > ek._p1y) return 1
return 0
}
toString() {
return 'EdgeKey(' + this.format(this._p0x, this._p0y) + ', ' + this.format(this._p1x, this._p1y) + ')'
}
init(p0, p1) {
this._p0x = p0.getX()
this._p0y = p0.getY()
this._p1x = p1.getX()
this._p1y = p1.getY()
}
hashCode() {
let result = 17
result = 37 * result + EdgeKey.hashCode(this._p0x)
result = 37 * result + EdgeKey.hashCode(this._p0y)
result = 37 * result + EdgeKey.hashCode(this._p1x)
result = 37 * result + EdgeKey.hashCode(this._p1y)
return result
}
get interfaces_() {
return [Comparable]
}
}

View File

@ -1,22 +0,0 @@
import HashMap from '../../../../../java/util/HashMap.js'
import ArrayList from '../../../../../java/util/ArrayList.js'
import Assert from '../../util/Assert.js'
import EdgeKey from './EdgeKey.js'
export default class EdgeMerger {
static merge(edges) {
const mergedEdges = new ArrayList()
const edgeMap = new HashMap()
for (const edge of edges) {
const edgeKey = EdgeKey.create(edge)
const baseEdge = edgeMap.get(edgeKey)
if (baseEdge === null) {
edgeMap.put(edgeKey, edge)
mergedEdges.add(edge)
} else {
Assert.isTrue(baseEdge.size() === edge.size(), 'Merge of edges of different sizes - probable noding error.')
baseEdge.merge(edge)
}
}
return mergedEdges
}
}

View File

@ -1,198 +0,0 @@
import LineString from '../../geom/LineString.js'
import ValidatingNoder from '../../noding/ValidatingNoder.js'
import IllegalArgumentException from '../../../../../java/lang/IllegalArgumentException.js'
import MCIndexNoder from '../../noding/MCIndexNoder.js'
import NodedSegmentString from '../../noding/NodedSegmentString.js'
import Polygon from '../../geom/Polygon.js'
import RingClipper from './RingClipper.js'
import SnapRoundingNoder from '../../noding/snapround/SnapRoundingNoder.js'
import EdgeMerger from './EdgeMerger.js'
import Orientation from '../../algorithm/Orientation.js'
import MultiPolygon from '../../geom/MultiPolygon.js'
import OverlayUtil from './OverlayUtil.js'
import GeometryCollection from '../../geom/GeometryCollection.js'
import LineLimiter from './LineLimiter.js'
import CoordinateArrays from '../../geom/CoordinateArrays.js'
import ArrayList from '../../../../../java/util/ArrayList.js'
import RobustLineIntersector from '../../algorithm/RobustLineIntersector.js'
import IntersectionAdder from '../../noding/IntersectionAdder.js'
import Edge from './Edge.js'
import MultiLineString from '../../geom/MultiLineString.js'
import EdgeSourceInfo from './EdgeSourceInfo.js'
export default class EdgeNodingBuilder {
constructor() {
EdgeNodingBuilder.constructor_.apply(this, arguments)
}
static constructor_() {
this._pm = null
this.inputEdges = new ArrayList()
this._customNoder = null
this._clipEnv = null
this._clipper = null
this._limiter = null
this._hasEdges = new Array(2).fill(null)
const pm = arguments[0], noder = arguments[1]
this._pm = pm
this._customNoder = noder
}
static createFixedPrecisionNoder(pm) {
const noder = new SnapRoundingNoder(pm)
return noder
}
static createFloatingPrecisionNoder(doValidation) {
const mcNoder = new MCIndexNoder()
const li = new RobustLineIntersector()
mcNoder.setSegmentIntersector(new IntersectionAdder(li))
let noder = mcNoder
if (doValidation)
noder = new ValidatingNoder(mcNoder)
return noder
}
static removeRepeatedPoints(line) {
const pts = line.getCoordinates()
return CoordinateArrays.removeRepeatedPoints(pts)
}
static computeDepthDelta(ring, isHole) {
const isCCW = Orientation.isCCW(ring.getCoordinateSequence())
let isOriented = true
if (!isHole) isOriented = !isCCW; else
isOriented = isCCW
const depthDelta = isOriented ? 1 : -1
return depthDelta
}
addLine() {
if (arguments[0] instanceof LineString && Number.isInteger(arguments[1])) {
const line = arguments[0], geomIndex = arguments[1]
if (line.isEmpty()) return null
if (this.isClippedCompletely(line.getEnvelopeInternal())) return null
if (this.isToBeLimited(line)) {
const sections = this.limit(line)
for (const pts of sections)
this.addLine(pts, geomIndex)
} else {
const ptsNoRepeat = EdgeNodingBuilder.removeRepeatedPoints(line)
this.addLine(ptsNoRepeat, geomIndex)
}
} else if (arguments[0] instanceof Array && Number.isInteger(arguments[1])) {
const pts = arguments[0], geomIndex = arguments[1]
if (pts.length < 2)
return null
const info = new EdgeSourceInfo(geomIndex)
this.addEdge(pts, info)
}
}
getNoder() {
if (this._customNoder !== null) return this._customNoder
if (OverlayUtil.isFloating(this._pm)) return EdgeNodingBuilder.createFloatingPrecisionNoder(EdgeNodingBuilder.IS_NODING_VALIDATED)
return EdgeNodingBuilder.createFixedPrecisionNoder(this._pm)
}
hasEdgesFor(geomIndex) {
return this._hasEdges[geomIndex]
}
addPolygon(poly, geomIndex) {
const shell = poly.getExteriorRing()
this.addPolygonRing(shell, false, geomIndex)
for (let i = 0; i < poly.getNumInteriorRing(); i++) {
const hole = poly.getInteriorRingN(i)
this.addPolygonRing(hole, true, geomIndex)
}
}
build(geom0, geom1) {
this.add(geom0, 0)
this.add(geom1, 1)
const nodedEdges = this.node(this.inputEdges)
const mergedEdges = EdgeMerger.merge(nodedEdges)
return mergedEdges
}
isToBeLimited(line) {
const pts = line.getCoordinates()
if (this._limiter === null || pts.length <= EdgeNodingBuilder.MIN_LIMIT_PTS)
return false
const env = line.getEnvelopeInternal()
if (this._clipEnv.covers(env))
return false
return true
}
addEdge(pts, info) {
const ss = new NodedSegmentString(pts, info)
this.inputEdges.add(ss)
}
createEdges(segStrings) {
const edges = new ArrayList()
for (const ss of segStrings) {
const pts = ss.getCoordinates()
if (Edge.isCollapsed(pts)) continue
const info = ss.getData()
this._hasEdges[info.getIndex()] = true
edges.add(new Edge(ss.getCoordinates(), info))
}
return edges
}
setClipEnvelope(clipEnv) {
this._clipEnv = clipEnv
this._clipper = new RingClipper(clipEnv)
this._limiter = new LineLimiter(clipEnv)
}
node(segStrings) {
const noder = this.getNoder()
noder.computeNodes(segStrings)
const nodedSS = noder.getNodedSubstrings()
const edges = this.createEdges(nodedSS)
return edges
}
addPolygonRing(ring, isHole, index) {
if (ring.isEmpty()) return null
if (this.isClippedCompletely(ring.getEnvelopeInternal())) return null
const pts = this.clip(ring)
if (pts.length < 2)
return null
const depthDelta = EdgeNodingBuilder.computeDepthDelta(ring, isHole)
const info = new EdgeSourceInfo(index, depthDelta, isHole)
this.addEdge(pts, info)
}
clip(ring) {
const pts = ring.getCoordinates()
const env = ring.getEnvelopeInternal()
if (this._clipper === null || this._clipEnv.covers(env))
return EdgeNodingBuilder.removeRepeatedPoints(ring)
return this._clipper.clip(pts)
}
limit(line) {
const pts = line.getCoordinates()
return this._limiter.limit(pts)
}
add(g, geomIndex) {
if (g === null || g.isEmpty()) return null
if (this.isClippedCompletely(g.getEnvelopeInternal())) return null
if (g instanceof Polygon) this.addPolygon(g, geomIndex); else if (g instanceof LineString) this.addLine(g, geomIndex); else if (g instanceof MultiLineString) this.addCollection(g, geomIndex); else if (g instanceof MultiPolygon) this.addCollection(g, geomIndex); else if (g instanceof GeometryCollection) this.addGeometryCollection(g, geomIndex, g.getDimension())
}
addCollection(gc, geomIndex) {
for (let i = 0; i < gc.getNumGeometries(); i++) {
const g = gc.getGeometryN(i)
this.add(g, geomIndex)
}
}
isClippedCompletely(env) {
if (this._clipEnv === null) return false
return this._clipEnv.disjoint(env)
}
addGeometryCollection(gc, geomIndex, expectedDim) {
for (let i = 0; i < gc.getNumGeometries(); i++) {
const g = gc.getGeometryN(i)
if (g.getDimension() !== expectedDim)
throw new IllegalArgumentException('Overlay input is mixed-dimension')
this.add(g, geomIndex)
}
}
}
EdgeNodingBuilder.MIN_LIMIT_PTS = 20
EdgeNodingBuilder.IS_NODING_VALIDATED = true

View File

@ -1,39 +0,0 @@
import Dimension from '../../geom/Dimension.js'
import Edge from './Edge.js'
export default class EdgeSourceInfo {
constructor() {
EdgeSourceInfo.constructor_.apply(this, arguments)
}
static constructor_() {
this._index = null
this._dim = -999
this._isHole = false
this._depthDelta = 0
if (arguments.length === 1) {
const index = arguments[0]
this._index = index
this._dim = Dimension.L
} else if (arguments.length === 3) {
const index = arguments[0], depthDelta = arguments[1], isHole = arguments[2]
this._index = index
this._dim = Dimension.A
this._depthDelta = depthDelta
this._isHole = isHole
}
}
getDimension() {
return this._dim
}
isHole() {
return this._isHole
}
getDepthDelta() {
return this._depthDelta
}
toString() {
return Edge.infoString(this._index, this._dim, this._isHole, this._depthDelta)
}
getIndex() {
return this._index
}
}

View File

@ -1,165 +0,0 @@
import Coordinate from '../../geom/Coordinate.js'
import Double from '../../../../../java/lang/Double.js'
import MathUtil from '../../math/MathUtil.js'
import CoordinateSequenceFilter from '../../geom/CoordinateSequenceFilter.js'
export default class ElevationModel {
constructor() {
ElevationModel.constructor_.apply(this, arguments)
}
static constructor_() {
this._extent = null
this._numCellX = null
this._numCellY = null
this._cellSizeX = null
this._cellSizeY = null
this._cells = null
this._isInitialized = false
this._hasZValue = false
this._averageZ = Double.NaN
const extent = arguments[0], numCellX = arguments[1], numCellY = arguments[2]
this._extent = extent
this._numCellX = numCellX
this._numCellY = numCellY
this._cellSizeX = extent.getWidth() / numCellX
this._cellSizeY = extent.getHeight() / numCellY
if (this._cellSizeX <= 0.0)
this._numCellX = 1
if (this._cellSizeY <= 0.0)
this._numCellY = 1
this._cells = Array(numCellX).fill().map(() => Array(numCellY))
}
static create(geom1, geom2) {
const extent = geom1.getEnvelopeInternal().copy()
if (geom2 !== null)
extent.expandToInclude(geom2.getEnvelopeInternal())
const model = new ElevationModel(extent, ElevationModel.DEFAULT_CELL_NUM, ElevationModel.DEFAULT_CELL_NUM)
if (geom1 !== null) model.add(geom1)
if (geom2 !== null) model.add(geom2)
return model
}
getZ(x, y) {
if (!this._isInitialized) this.init()
const cell = this.getCell(x, y, false)
if (cell === null) return this._averageZ
return cell.getZ()
}
populateZ(geom) {
if (!this._hasZValue) return null
if (!this._isInitialized) this.init()
geom.apply(new (class {
get interfaces_() {
return [CoordinateSequenceFilter]
}
filter(seq, i) {
if (!seq.hasZ()) {
this._isDone = true
return null
}
if (Double.isNaN(seq.getZ(i))) {
const z = this.getZ(seq.getOrdinate(i, Coordinate.X), seq.getOrdinate(i, Coordinate.Y))
seq.setOrdinate(i, Coordinate.Z, z)
}
}
isDone() {
return this._isDone
}
isGeometryChanged() {
return false
}
})())
}
getCell(x, y, isCreateIfMissing) {
let ix = 0
if (this._numCellX > 1) {
ix = Math.trunc((x - this._extent.getMinX()) / this._cellSizeX)
ix = MathUtil.clamp(ix, 0, this._numCellX - 1)
}
let iy = 0
if (this._numCellY > 1) {
iy = Math.trunc((y - this._extent.getMinY()) / this._cellSizeY)
iy = MathUtil.clamp(iy, 0, this._numCellY - 1)
}
let cell = this._cells[ix][iy]
if (isCreateIfMissing && cell === null) {
cell = new ElevationCell()
this._cells[ix][iy] = cell
}
return cell
}
add() {
if (arguments.length === 1) {
const geom = arguments[0]
geom.apply(new (class {
get interfaces_() {
return [CoordinateSequenceFilter]
}
filter(seq, i) {
if (!seq.hasZ()) {
this._hasZ = false
return null
}
const z = seq.getOrdinate(i, Coordinate.Z)
this.add(seq.getOrdinate(i, Coordinate.X), seq.getOrdinate(i, Coordinate.Y), z)
}
isDone() {
return !this._hasZ
}
isGeometryChanged() {
return false
}
})())
} else if (arguments.length === 3) {
const x = arguments[0], y = arguments[1], z = arguments[2]
if (Double.isNaN(z)) return null
this._hasZValue = true
const cell = this.getCell(x, y, true)
cell.add(z)
}
}
init() {
this._isInitialized = true
let numCells = 0
let sumZ = 0.0
for (let i = 0; i < this._cells.length; i++)
for (let j = 0; j < this._cells[0].length; j++) {
const cell = this._cells[i][j]
if (cell !== null) {
cell.compute()
numCells++
sumZ += cell.getZ()
}
}
this._averageZ = Double.NaN
if (numCells > 0)
this._averageZ = sumZ / numCells
}
}
class ElevationCell {
constructor() {
ElevationCell.constructor_.apply(this, arguments)
}
static constructor_() {
this._numZ = 0
this._sumZ = 0.0
this._avgZ = null
}
add(z) {
this._numZ++
this._sumZ += z
}
compute() {
this._avgZ = Double.NaN
if (this._numZ > 0) this._avgZ = this._sumZ / this._numZ
}
getZ() {
return this._avgZ
}
}
ElevationModel.ElevationCell = ElevationCell
ElevationModel.DEFAULT_CELL_NUM = 3

View File

@ -1,45 +0,0 @@
import OverlayNG from './OverlayNG.js'
import OverlayUtil from './OverlayUtil.js'
export default class FastOverlayFilter {
constructor() {
FastOverlayFilter.constructor_.apply(this, arguments)
}
static constructor_() {
this._targetGeom = null
this._isTargetRectangle = null
const geom = arguments[0]
this._targetGeom = geom
this._isTargetRectangle = this._targetGeom.isRectangle()
}
createEmpty(geom) {
return OverlayUtil.createEmptyResult(geom.getDimension(), geom.getFactory())
}
isEnvelopeCovers(a, b) {
return a.getEnvelopeInternal().covers(b.getEnvelopeInternal())
}
intersection(geom) {
const resultForRect = this.intersectionRectangle(geom)
if (resultForRect !== null) return resultForRect
if (!this.isEnvelopeIntersects(this._targetGeom, geom))
return this.createEmpty(geom)
return null
}
overlay(geom, overlayOpCode) {
if (overlayOpCode !== OverlayNG.INTERSECTION) return null
return this.intersection(geom)
}
isEnvelopeIntersects(a, b) {
return a.getEnvelopeInternal().intersects(b.getEnvelopeInternal())
}
intersectionRectangle(geom) {
if (!this._isTargetRectangle) return null
if (this.isEnvelopeCovers(this._targetGeom, geom))
return geom.copy()
if (!this.isEnvelopeIntersects(this._targetGeom, geom))
return this.createEmpty(geom)
return null
}
}

View File

@ -1,19 +0,0 @@
import PointLocator from '../../algorithm/PointLocator.js'
import PointOnGeometryLocator from '../../algorithm/locate/PointOnGeometryLocator.js'
export default class IndexedPointOnLineLocator {
constructor() {
IndexedPointOnLineLocator.constructor_.apply(this, arguments)
}
static constructor_() {
this._inputGeom = null
const geomLinear = arguments[0]
this._inputGeom = geomLinear
}
locate(p) {
const locator = new PointLocator()
return locator.locate(p, this._inputGeom)
}
get interfaces_() {
return [PointOnGeometryLocator]
}
}

View File

@ -1,69 +0,0 @@
import Location from '../../geom/Location.js'
import IndexedPointInAreaLocator from '../../algorithm/locate/IndexedPointInAreaLocator.js'
export default class InputGeometry {
constructor() {
InputGeometry.constructor_.apply(this, arguments)
}
static constructor_() {
this._geom = new Array(2).fill(null)
this._ptLocatorA = null
this._ptLocatorB = null
this._isCollapsed = new Array(2).fill(null)
const geomA = arguments[0], geomB = arguments[1]
this._geom = [geomA, geomB]
}
hasPoints() {
return this.getDimension(0) === 0 || this.getDimension(1) === 0
}
isAllPoints() {
return this.getDimension(0) === 0 && this._geom[1] !== null && this.getDimension(1) === 0
}
getGeometry(geomIndex) {
return this._geom[geomIndex]
}
isSingle() {
return this._geom[1] === null
}
isLine(geomIndex) {
return this.getDimension(geomIndex) === 1
}
getDimension(index) {
if (this._geom[index] === null) return -1
return this._geom[index].getDimension()
}
getEnvelope(geomIndex) {
return this._geom[geomIndex].getEnvelopeInternal()
}
setCollapsed(geomIndex, isGeomCollapsed) {
this._isCollapsed[geomIndex] = isGeomCollapsed
}
getAreaIndex() {
if (this.getDimension(0) === 2) return 0
if (this.getDimension(1) === 2) return 1
return -1
}
getLocator(geomIndex) {
if (geomIndex === 0) {
if (this._ptLocatorA === null) this._ptLocatorA = new IndexedPointInAreaLocator(this.getGeometry(geomIndex))
return this._ptLocatorA
} else {
if (this._ptLocatorB === null) this._ptLocatorB = new IndexedPointInAreaLocator(this.getGeometry(geomIndex))
return this._ptLocatorB
}
}
hasEdges(geomIndex) {
return this._geom[geomIndex] !== null && this._geom[geomIndex].getDimension() > 0
}
locatePointInArea(geomIndex, pt) {
if (this._isCollapsed[geomIndex]) return Location.EXTERIOR
if (this.getGeometry(geomIndex).isEmpty() || this._isCollapsed[geomIndex]) return Location.EXTERIOR
const ptLocator = this.getLocator(geomIndex)
return ptLocator.locate(pt)
}
isArea(geomIndex) {
return this._geom[geomIndex] !== null && this._geom[geomIndex].getDimension() === 2
}
isEmpty(geomIndex) {
return this._geom[geomIndex].isEmpty()
}
}

View File

@ -1,49 +0,0 @@
import OverlayNG from './OverlayNG.js'
import ArrayList from '../../../../../java/util/ArrayList.js'
export default class IntersectionPointBuilder {
constructor() {
IntersectionPointBuilder.constructor_.apply(this, arguments)
}
static constructor_() {
this._geometryFactory = null
this._graph = null
this._points = new ArrayList()
this._isAllowCollapseLines = !OverlayNG.STRICT_MODE_DEFAULT
const graph = arguments[0], geomFact = arguments[1]
this._graph = graph
this._geometryFactory = geomFact
}
getPoints() {
this.addResultPoints()
return this._points
}
addResultPoints() {
for (const nodeEdge of this._graph.getNodeEdges())
if (this.isResultPoint(nodeEdge)) {
const pt = this._geometryFactory.createPoint(nodeEdge.getCoordinate().copy())
this._points.add(pt)
}
}
isEdgeOf(label, i) {
if (!this._isAllowCollapseLines && label.isBoundaryCollapse()) return false
return label.isBoundary(i) || label.isLine(i)
}
isResultPoint(nodeEdge) {
let isEdgeOfA = false
let isEdgeOfB = false
let edge = nodeEdge
do {
if (edge.isInResult()) return false
const label = edge.getLabel()
isEdgeOfA |= this.isEdgeOf(label, 0)
isEdgeOfB |= this.isEdgeOf(label, 1)
edge = edge.oNext()
} while (edge !== nodeEdge)
const isNodeInBoth = isEdgeOfA && isEdgeOfB
return isNodeInBoth
}
setStrictMode(isStrictMode) {
this._isAllowCollapseLines = !isStrictMode
}
}

View File

@ -1,143 +0,0 @@
import Location from '../../geom/Location.js'
import CoordinateList from '../../geom/CoordinateList.js'
import OverlayNG from './OverlayNG.js'
import ArrayList from '../../../../../java/util/ArrayList.js'
export default class LineBuilder {
constructor() {
LineBuilder.constructor_.apply(this, arguments)
}
static constructor_() {
this._geometryFactory = null
this._graph = null
this._opCode = null
this._inputAreaIndex = null
this._hasResultArea = null
this._isAllowMixedResult = !OverlayNG.STRICT_MODE_DEFAULT
this._isAllowCollapseLines = !OverlayNG.STRICT_MODE_DEFAULT
this._lines = new ArrayList()
const inputGeom = arguments[0], graph = arguments[1], hasResultArea = arguments[2], opCode = arguments[3], geomFact = arguments[4]
this._graph = graph
this._opCode = opCode
this._geometryFactory = geomFact
this._hasResultArea = hasResultArea
this._inputAreaIndex = inputGeom.getAreaIndex()
}
static effectiveLocation(lbl, geomIndex) {
if (lbl.isCollapse(geomIndex)) return Location.INTERIOR
if (lbl.isLine(geomIndex)) return Location.INTERIOR
return lbl.getLineLocation(geomIndex)
}
static nextLineEdgeUnvisited(node) {
let e = node
do {
e = e.oNextOE()
if (e.isVisited()) continue
if (e.isInResultLine())
return e
} while (e !== node)
return null
}
static degreeOfLines(node) {
let degree = 0
let e = node
do {
if (e.isInResultLine())
degree++
e = e.oNextOE()
} while (e !== node)
return degree
}
isResultLine(lbl) {
if (lbl.isBoundarySingleton()) return false
if (!this._isAllowCollapseLines && lbl.isBoundaryCollapse()) return false
if (lbl.isInteriorCollapse()) return false
if (this._opCode !== OverlayNG.INTERSECTION) {
if (lbl.isCollapseAndNotPartInterior()) return false
if (this._hasResultArea && lbl.isLineInArea(this._inputAreaIndex)) return false
}
if (this._isAllowMixedResult && this._opCode === OverlayNG.INTERSECTION && lbl.isBoundaryTouch())
return true
const aLoc = LineBuilder.effectiveLocation(lbl, 0)
const bLoc = LineBuilder.effectiveLocation(lbl, 1)
const isInResult = OverlayNG.isResultOfOp(this._opCode, aLoc, bLoc)
return isInResult
}
getLines() {
this.markResultLines()
this.addResultLines()
return this._lines
}
buildLine(node) {
const pts = new CoordinateList()
pts.add(node.orig(), false)
const isForward = node.isForward()
let e = node
do {
e.markVisitedBoth()
e.addCoordinates(pts)
if (LineBuilder.degreeOfLines(e.symOE()) !== 2)
break
e = LineBuilder.nextLineEdgeUnvisited(e.symOE())
} while (e !== null)
const ptsOut = pts.toCoordinateArray(isForward)
const line = this._geometryFactory.createLineString(ptsOut)
return line
}
addResultLinesRings() {
const edges = this._graph.getEdges()
for (const edge of edges) {
if (!edge.isInResultLine()) continue
if (edge.isVisited()) continue
this._lines.add(this.buildLine(edge))
}
}
addResultLinesForNodes() {
const edges = this._graph.getEdges()
for (const edge of edges) {
if (!edge.isInResultLine()) continue
if (edge.isVisited()) continue
if (LineBuilder.degreeOfLines(edge) !== 2)
this._lines.add(this.buildLine(edge))
}
}
markResultLines() {
const edges = this._graph.getEdges()
for (const edge of edges) {
if (edge.isInResultEither()) continue
if (this.isResultLine(edge.getLabel()))
edge.markInResultLine()
}
}
addResultLines() {
const edges = this._graph.getEdges()
for (const edge of edges) {
if (!edge.isInResultLine()) continue
if (edge.isVisited()) continue
this._lines.add(this.toLine(edge))
edge.markVisitedBoth()
}
}
setStrictMode(isStrictResultMode) {
this._isAllowCollapseLines = !isStrictResultMode
this._isAllowMixedResult = !isStrictResultMode
}
addResultLinesMerged() {
this.addResultLinesForNodes()
this.addResultLinesRings()
}
toLine(edge) {
const isForward = edge.isForward()
const pts = new CoordinateList()
pts.add(edge.orig(), false)
edge.addCoordinates(pts)
const ptsOut = pts.toCoordinateArray(isForward)
const line = this._geometryFactory.createLineString(ptsOut)
return line
}
}

View File

@ -1,72 +0,0 @@
import CoordinateList from '../../geom/CoordinateList.js'
import ArrayList from '../../../../../java/util/ArrayList.js'
export default class LineLimiter {
constructor() {
LineLimiter.constructor_.apply(this, arguments)
}
static constructor_() {
this._limitEnv = null
this._ptList = null
this._lastOutside = null
this._sections = null
const env = arguments[0]
this._limitEnv = env
}
isSectionOpen() {
return this._ptList !== null
}
addPoint(p) {
if (p === null) return null
this.startSection()
this._ptList.add(p, false)
}
addOutside(p) {
const segIntersects = this.isLastSegmentIntersecting(p)
if (!segIntersects) {
this.finishSection()
} else {
this.addPoint(this._lastOutside)
this.addPoint(p)
}
this._lastOutside = p
}
finishSection() {
if (this._ptList === null) return null
if (this._lastOutside !== null) {
this._ptList.add(this._lastOutside, false)
this._lastOutside = null
}
const section = this._ptList.toCoordinateArray()
this._sections.add(section)
this._ptList = null
}
startSection() {
if (this._ptList === null)
this._ptList = new CoordinateList()
if (this._lastOutside !== null)
this._ptList.add(this._lastOutside, false)
this._lastOutside = null
}
limit(pts) {
this._lastOutside = null
this._ptList = null
this._sections = new ArrayList()
for (let i = 0; i < pts.length; i++) {
const p = pts[i]
if (this._limitEnv.intersects(p)) this.addPoint(p); else
this.addOutside(p)
}
this.finishSection()
return this._sections
}
isLastSegmentIntersecting(p) {
if (this._lastOutside === null) {
if (this.isSectionOpen()) return true
return false
}
return this._limitEnv.intersects(this._lastOutside, p)
}
}

View File

@ -1,126 +0,0 @@
import CoordinateList from '../../geom/CoordinateList.js'
import WKTWriter from '../../io/WKTWriter.js'
import TopologyException from '../../geom/TopologyException.js'
import OverlayEdgeRing from './OverlayEdgeRing.js'
import ArrayList from '../../../../../java/util/ArrayList.js'
import Assert from '../../util/Assert.js'
export default class MaximalEdgeRing {
constructor() {
MaximalEdgeRing.constructor_.apply(this, arguments)
}
static constructor_() {
this._startEdge = null
const e = arguments[0]
this._startEdge = e
this.attachEdges(e)
}
static isAlreadyLinked(edge, maxRing) {
const isLinked = edge.getEdgeRingMax() === maxRing && edge.isResultLinked()
return isLinked
}
static linkMaxInEdge(currOut, currMaxRingOut, maxEdgeRing) {
const currIn = currOut.symOE()
if (currIn.getEdgeRingMax() !== maxEdgeRing) return currMaxRingOut
currIn.setNextResult(currMaxRingOut)
return null
}
static linkResultAreaMaxRingAtNode(nodeEdge) {
Assert.isTrue(nodeEdge.isInResultArea(), 'Attempt to link non-result edge')
const endOut = nodeEdge.oNextOE()
let currOut = endOut
let state = MaximalEdgeRing.STATE_FIND_INCOMING
let currResultIn = null
do {
if (currResultIn !== null && currResultIn.isResultMaxLinked()) return null
switch (state) {
case MaximalEdgeRing.STATE_FIND_INCOMING:
const currIn = currOut.symOE()
if (!currIn.isInResultArea()) break
currResultIn = currIn
state = MaximalEdgeRing.STATE_LINK_OUTGOING
break
case MaximalEdgeRing.STATE_LINK_OUTGOING:
if (!currOut.isInResultArea()) break
currResultIn.setNextResultMax(currOut)
state = MaximalEdgeRing.STATE_FIND_INCOMING
break
}
currOut = currOut.oNextOE()
} while (currOut !== endOut)
if (state === MaximalEdgeRing.STATE_LINK_OUTGOING)
throw new TopologyException('no outgoing edge found', nodeEdge.getCoordinate())
}
static linkMinRingEdgesAtNode(nodeEdge, maxRing) {
const endOut = nodeEdge
let currMaxRingOut = endOut
let currOut = endOut.oNextOE()
do {
if (MaximalEdgeRing.isAlreadyLinked(currOut.symOE(), maxRing)) return null
if (currMaxRingOut === null)
currMaxRingOut = MaximalEdgeRing.selectMaxOutEdge(currOut, maxRing)
else
currMaxRingOut = MaximalEdgeRing.linkMaxInEdge(currOut, currMaxRingOut, maxRing)
currOut = currOut.oNextOE()
} while (currOut !== endOut)
if (currMaxRingOut !== null)
throw new TopologyException('Unmatched edge found during min-ring linking', nodeEdge.getCoordinate())
}
static selectMaxOutEdge(currOut, maxEdgeRing) {
if (currOut.getEdgeRingMax() === maxEdgeRing) return currOut
return null
}
getCoordinates() {
const coords = new CoordinateList()
let edge = this._startEdge
do {
coords.add(edge.orig())
if (edge.nextResultMax() === null)
break
edge = edge.nextResultMax()
} while (edge !== this._startEdge)
coords.add(edge.dest())
return coords.toCoordinateArray()
}
linkMinimalRings() {
let e = this._startEdge
do {
MaximalEdgeRing.linkMinRingEdgesAtNode(e, this)
e = e.nextResultMax()
} while (e !== this._startEdge)
}
buildMinimalRings(geometryFactory) {
this.linkMinimalRings()
const minEdgeRings = new ArrayList()
let e = this._startEdge
do {
if (e.getEdgeRing() === null) {
const minEr = new OverlayEdgeRing(e, geometryFactory)
minEdgeRings.add(minEr)
}
e = e.nextResultMax()
} while (e !== this._startEdge)
return minEdgeRings
}
attachEdges(startEdge) {
let edge = startEdge
do {
if (edge === null) throw new TopologyException('Ring edge is null')
if (edge.getEdgeRingMax() === this) throw new TopologyException('Ring edge visited twice at ' + edge.getCoordinate(), edge.getCoordinate())
if (edge.nextResultMax() === null)
throw new TopologyException('Ring edge missing at', edge.dest())
edge.setEdgeRingMax(this)
edge = edge.nextResultMax()
} while (edge !== startEdge)
}
toString() {
const pts = this.getCoordinates()
return WKTWriter.toLineString(pts)
}
}
MaximalEdgeRing.STATE_FIND_INCOMING = 1
MaximalEdgeRing.STATE_LINK_OUTGOING = 2

View File

@ -1,186 +0,0 @@
import WKTWriter from '../../io/WKTWriter.js';
import HalfEdge from '../../edgegraph/HalfEdge.js';
import CoordinateArrays from '../../geom/CoordinateArrays.js';
export default class OverlayEdge extends HalfEdge {
constructor() {
super();
OverlayEdge.constructor_.apply(this, arguments);
}
static constructor_() {
this._pts = null;
this._direction = null;
this._dirPt = null;
this._label = null;
this._isInResultArea = false;
this._isInResultLine = false;
this._isVisited = false;
this._nextResultEdge = null;
this._edgeRing = null;
this._maxEdgeRing = null;
this._nextResultMaxEdge = null;
let orig = arguments[0], dirPt = arguments[1], direction = arguments[2], label = arguments[3], pts = arguments[4];
HalfEdge.constructor_.call(this, orig);
this._dirPt = dirPt;
this._direction = direction;
this._pts = pts;
this._label = label;
}
static createEdge(pts, lbl, direction) {
let origin = null;
let dirPt = null;
if (direction) {
origin = pts[0];
dirPt = pts[1];
} else {
let ilast = pts.length - 1;
origin = pts[ilast];
dirPt = pts[ilast - 1];
}
return new OverlayEdge(origin, dirPt, direction, lbl, pts);
}
static createEdgePair(pts, lbl) {
let e0 = OverlayEdge.createEdge(pts, lbl, true);
let e1 = OverlayEdge.createEdge(pts, lbl, false);
e0.link(e1);
return e0;
}
static nodeComparator() {
return new (class {
get interfaces_() {
return [Comparator<OverlayEdge>];
}
compare(e1, e2) {
return e1.orig().compareTo(e2.orig());
}
})();
}
symOE() {
return this.sym();
}
markInResultArea() {
this._isInResultArea = true;
}
getCoordinates() {
return this._pts;
}
nextResultMax() {
return this._nextResultMaxEdge;
}
resultSymbol() {
if (this._isInResultArea) return " resA";
if (this._isInResultLine) return " resL";
return "";
}
isInResultLine() {
return this._isInResultLine;
}
getCoordinate() {
return this.orig();
}
isInResultAreaBoth() {
return this._isInResultArea && this.symOE()._isInResultArea;
}
directionPt() {
return this._dirPt;
}
addCoordinates(coords) {
let isFirstEdge = coords.size() > 0;
if (this._direction) {
let startIndex = 1;
if (isFirstEdge) startIndex = 0;
for (let i = startIndex; i < this._pts.length; i++) {
coords.add(this._pts[i], false);
}
} else {
let startIndex = this._pts.length - 2;
if (isFirstEdge) startIndex = this._pts.length - 1;
for (let i = startIndex; i >= 0; i--) {
coords.add(this._pts[i], false);
}
}
}
nextResult() {
return this._nextResultEdge;
}
getCoordinatesOriented() {
if (this._direction) {
return this._pts;
}
let copy = this._pts.clone();
CoordinateArrays.reverse(copy);
return copy;
}
setNextResultMax(e) {
this._nextResultMaxEdge = e;
}
setEdgeRing(edgeRing) {
this._edgeRing = edgeRing;
}
isInResultEither() {
return this.isInResult() || this.symOE().isInResult();
}
getEdgeRingMax() {
return this._maxEdgeRing;
}
isResultLinked() {
return this._nextResultEdge !== null;
}
isForward() {
return this._direction;
}
setEdgeRingMax(maximalEdgeRing) {
this._maxEdgeRing = maximalEdgeRing;
}
unmarkFromResultAreaBoth() {
this._isInResultArea = false;
this.symOE()._isInResultArea = false;
}
markInResultLine() {
this._isInResultLine = true;
this.symOE()._isInResultLine = true;
}
getLabel() {
return this._label;
}
markVisitedBoth() {
this.markVisited();
this.symOE().markVisited();
}
isResultMaxLinked() {
return this._nextResultMaxEdge !== null;
}
getLocation(index, position) {
return this._label.getLocation(index, position, this._direction);
}
markVisited() {
this._isVisited = true;
}
oNextOE() {
return this.oNext();
}
setNextResult(e) {
this._nextResultEdge = e;
}
toString() {
let orig = this.orig();
let dest = this.dest();
let dirPtStr = this._pts.length > 2 ? ", " + WKTWriter.format(this.directionPt()) : "";
return "OE( " + WKTWriter.format(orig) + dirPtStr + " .. " + WKTWriter.format(dest) + " ) " + this._label.toString(this._direction) + this.resultSymbol() + " / Sym: " + this.symOE().getLabel().toString(this.symOE()._direction) + this.symOE().resultSymbol();
}
getEdgeRing() {
return this._edgeRing;
}
isInResultArea() {
return this._isInResultArea;
}
isInResult() {
return this._isInResultArea || this._isInResultLine;
}
isVisited() {
return this._isVisited;
}
markInResultAreaBoth() {
this._isInResultArea = true;
this.symOE()._isInResultArea = true;
}
}

View File

@ -1,123 +0,0 @@
import Location from '../../geom/Location.js'
import CoordinateList from '../../geom/CoordinateList.js'
import TopologyException from '../../geom/TopologyException.js'
import Orientation from '../../algorithm/Orientation.js'
import ArrayList from '../../../../../java/util/ArrayList.js'
import IndexedPointInAreaLocator from '../../algorithm/locate/IndexedPointInAreaLocator.js'
export default class OverlayEdgeRing {
constructor() {
OverlayEdgeRing.constructor_.apply(this, arguments)
}
static constructor_() {
this._startEdge = null
this._ring = null
this._isHole = null
this._ringPts = null
this._locator = null
this._shell = null
this._holes = new ArrayList()
const start = arguments[0], geometryFactory = arguments[1]
this._startEdge = start
this._ringPts = this.computeRingPts(start)
this.computeRing(this._ringPts, geometryFactory)
}
computeRing(ringPts, geometryFactory) {
if (this._ring !== null) return null
this._ring = geometryFactory.createLinearRing(ringPts)
this._isHole = Orientation.isCCW(this._ring.getCoordinates())
}
getCoordinates() {
return this._ringPts
}
isPointInOrOut(ring) {
for (const pt of ring.getCoordinates()) {
const loc = this.locate(pt)
if (loc === Location.INTERIOR)
return true
if (loc === Location.EXTERIOR)
return false
}
return false
}
getCoordinate() {
return this._ringPts[0]
}
isHole() {
return this._isHole
}
addHole(ring) {
this._holes.add(ring)
}
getEnvelope() {
return this._ring.getEnvelopeInternal()
}
getEdge() {
return this._startEdge
}
computeRingPts(start) {
let edge = start
const pts = new CoordinateList()
do {
if (edge.getEdgeRing() === this) throw new TopologyException('Edge visited twice during ring-building at ' + edge.getCoordinate(), edge.getCoordinate())
edge.addCoordinates(pts)
edge.setEdgeRing(this)
if (edge.nextResult() === null) throw new TopologyException('Found null edge in ring', edge.dest())
edge = edge.nextResult()
} while (edge !== start)
pts.closeRing()
return pts.toCoordinateArray()
}
hasShell() {
return this._shell !== null
}
findEdgeRingContaining(erList) {
let minContainingRing = null
for (const edgeRing of erList)
if (edgeRing.contains(this))
if (minContainingRing === null || minContainingRing.getEnvelope().contains(edgeRing.getEnvelope()))
minContainingRing = edgeRing
return minContainingRing
}
getLocator() {
if (this._locator === null)
this._locator = new IndexedPointInAreaLocator(this.getRing())
return this._locator
}
getShell() {
if (this.isHole()) return this._shell
return this
}
contains(ring) {
const env = this.getEnvelope()
const testEnv = ring.getEnvelope()
if (!env.containsProperly(testEnv)) return false
return this.isPointInOrOut(ring)
}
getRing() {
return this._ring
}
locate(pt) {
return this.getLocator().locate(pt)
}
setShell(shell) {
this._shell = shell
if (shell !== null) shell.addHole(this)
}
toPolygon(factory) {
let holeLR = null
if (this._holes !== null) {
holeLR = new Array(this._holes.size()).fill(null)
for (let i = 0; i < this._holes.size(); i++)
holeLR[i] = this._holes.get(i).getRing()
}
const poly = factory.createPolygon(this._ring, holeLR)
return poly
}
}

View File

@ -1,45 +0,0 @@
import HashMap from '../../../../../java/util/HashMap.js'
import OverlayEdge from './OverlayEdge.js'
import ArrayList from '../../../../../java/util/ArrayList.js'
export default class OverlayGraph {
constructor() {
OverlayGraph.constructor_.apply(this, arguments)
}
static constructor_() {
this._edges = new ArrayList()
this._nodeMap = new HashMap()
}
getNodeEdge(nodePt) {
return this._nodeMap.get(nodePt)
}
insert(e) {
this._edges.add(e)
const nodeEdge = this._nodeMap.get(e.orig())
if (nodeEdge !== null)
nodeEdge.insert(e)
else
this._nodeMap.put(e.orig(), e)
}
getResultAreaEdges() {
const resultEdges = new ArrayList()
for (const edge of this.getEdges())
if (edge.isInResultArea())
resultEdges.add(edge)
return resultEdges
}
addEdge(pts, label) {
const e = OverlayEdge.createEdgePair(pts, label)
this.insert(e)
this.insert(e.symOE())
return e
}
getEdges() {
return this._edges
}
getNodeEdges() {
return this._nodeMap.values()
}
}

View File

@ -1,306 +0,0 @@
import Location from '../../geom/Location.js'
import Position from '../../geom/Position.js'
import StringBuilder from '../../../../../java/lang/StringBuilder.js'
export default class OverlayLabel {
constructor() {
OverlayLabel.constructor_.apply(this, arguments)
}
static constructor_() {
this._aDim = OverlayLabel.DIM_NOT_PART
this._aIsHole = false
this._aLocLeft = OverlayLabel.LOC_UNKNOWN
this._aLocRight = OverlayLabel.LOC_UNKNOWN
this._aLocLine = OverlayLabel.LOC_UNKNOWN
this._bDim = OverlayLabel.DIM_NOT_PART
this._bIsHole = false
this._bLocLeft = OverlayLabel.LOC_UNKNOWN
this._bLocRight = OverlayLabel.LOC_UNKNOWN
this._bLocLine = OverlayLabel.LOC_UNKNOWN
if (arguments.length === 0) {} else if (arguments.length === 1) {
if (Number.isInteger(arguments[0])) {
const index = arguments[0]
this.initLine(index)
} else if (arguments[0] instanceof OverlayLabel) {
const lbl = arguments[0]
this._aLocLeft = lbl._aLocLeft
this._aLocRight = lbl._aLocRight
this._aLocLine = lbl._aLocLine
this._aDim = lbl._aDim
this._aIsHole = lbl._aIsHole
this._bLocLeft = lbl._bLocLeft
this._bLocRight = lbl._bLocRight
this._bLocLine = lbl._bLocLine
this._bDim = lbl._bDim
this._bIsHole = lbl._bIsHole
}
} else if (arguments.length === 4) {
const index = arguments[0], locLeft = arguments[1], locRight = arguments[2], isHole = arguments[3]
this.initBoundary(index, locLeft, locRight, isHole)
}
}
static ringRoleSymbol(isHole) {
return isHole ? 'h' : 's'
}
static dimensionSymbol(dim) {
switch (dim) {
case OverlayLabel.DIM_LINE:
return OverlayLabel.SYM_LINE
case OverlayLabel.DIM_COLLAPSE:
return OverlayLabel.SYM_COLLAPSE
case OverlayLabel.DIM_BOUNDARY:
return OverlayLabel.SYM_BOUNDARY
}
return OverlayLabel.SYM_UNKNOWN
}
isKnown(index) {
if (index === 0)
return this._aDim !== OverlayLabel.DIM_UNKNOWN
return this._bDim !== OverlayLabel.DIM_UNKNOWN
}
isLineInArea(index) {
if (index === 0)
return this._aLocLine === Location.INTERIOR
return this._bLocLine === Location.INTERIOR
}
isNotPart(index) {
if (index === 0)
return this._aDim === OverlayLabel.DIM_NOT_PART
return this._bDim === OverlayLabel.DIM_NOT_PART
}
isBoundaryEither() {
return this._aDim === OverlayLabel.DIM_BOUNDARY || this._bDim === OverlayLabel.DIM_BOUNDARY
}
initCollapse(index, isHole) {
if (index === 0) {
this._aDim = OverlayLabel.DIM_COLLAPSE
this._aIsHole = isHole
} else {
this._bDim = OverlayLabel.DIM_COLLAPSE
this._bIsHole = isHole
}
}
getLocationBoundaryOrLine(index, position, isForward) {
if (this.isBoundary(index))
return this.getLocation(index, position, isForward)
return this.getLineLocation(index)
}
isBoundary(index) {
if (index === 0)
return this._aDim === OverlayLabel.DIM_BOUNDARY
return this._bDim === OverlayLabel.DIM_BOUNDARY
}
isInteriorCollapse() {
if (this._aDim === OverlayLabel.DIM_COLLAPSE && this._aLocLine === Location.INTERIOR) return true
if (this._bDim === OverlayLabel.DIM_COLLAPSE && this._bLocLine === Location.INTERIOR) return true
return false
}
isLineLocationUnknown(index) {
if (index === 0)
return this._aLocLine === OverlayLabel.LOC_UNKNOWN
else
return this._bLocLine === OverlayLabel.LOC_UNKNOWN
}
isLine() {
if (arguments.length === 0) {
return this._aDim === OverlayLabel.DIM_LINE || this._bDim === OverlayLabel.DIM_LINE
} else if (arguments.length === 1) {
const index = arguments[0]
if (index === 0)
return this._aDim === OverlayLabel.DIM_LINE
return this._bDim === OverlayLabel.DIM_LINE
}
}
isHole(index) {
if (index === 0)
return this._aIsHole
else
return this._bIsHole
}
hasSides(index) {
if (index === 0)
return this._aLocLeft !== OverlayLabel.LOC_UNKNOWN || this._aLocRight !== OverlayLabel.LOC_UNKNOWN
return this._bLocLeft !== OverlayLabel.LOC_UNKNOWN || this._bLocRight !== OverlayLabel.LOC_UNKNOWN
}
isLineInterior(index) {
if (index === 0)
return this._aLocLine === Location.INTERIOR
return this._bLocLine === Location.INTERIOR
}
setLocationAll(index, loc) {
if (index === 0) {
this._aLocLine = loc
this._aLocLeft = loc
this._aLocRight = loc
} else {
this._bLocLine = loc
this._bLocLeft = loc
this._bLocRight = loc
}
}
isCollapse(index) {
return this.dimension(index) === OverlayLabel.DIM_COLLAPSE
}
setLocationCollapse(index) {
const loc = this.isHole(index) ? Location.INTERIOR : Location.EXTERIOR
if (index === 0)
this._aLocLine = loc
else
this._bLocLine = loc
}
setLocationLine(index, loc) {
if (index === 0)
this._aLocLine = loc
else
this._bLocLine = loc
}
isBoundaryTouch() {
return this.isBoundaryBoth() && this.getLocation(0, Position.RIGHT, true) !== this.getLocation(1, Position.RIGHT, true)
}
isBoundaryBoth() {
return this._aDim === OverlayLabel.DIM_BOUNDARY && this._bDim === OverlayLabel.DIM_BOUNDARY
}
getLocation() {
if (arguments.length === 1) {
const index = arguments[0]
if (index === 0)
return this._aLocLine
return this._bLocLine
} else if (arguments.length === 3) {
const index = arguments[0], position = arguments[1], isForward = arguments[2]
if (index === 0)
switch (position) {
case Position.LEFT:
return isForward ? this._aLocLeft : this._aLocRight
case Position.RIGHT:
return isForward ? this._aLocRight : this._aLocLeft
case Position.ON:
return this._aLocLine
}
switch (position) {
case Position.LEFT:
return isForward ? this._bLocLeft : this._bLocRight
case Position.RIGHT:
return isForward ? this._bLocRight : this._bLocLeft
case Position.ON:
return this._bLocLine
}
return OverlayLabel.LOC_UNKNOWN
}
}
copy() {
return new OverlayLabel(this)
}
toString() {
if (arguments.length === 0) {
return this.toString(true)
} else if (arguments.length === 1) {
const isForward = arguments[0]
const buf = new StringBuilder()
buf.append('A:')
buf.append(this.locationString(0, isForward))
buf.append('/B:')
buf.append(this.locationString(1, isForward))
return buf.toString()
}
}
initLine(index) {
if (index === 0) {
this._aDim = OverlayLabel.DIM_LINE
this._aLocLine = OverlayLabel.LOC_UNKNOWN
} else {
this._bDim = OverlayLabel.DIM_LINE
this._bLocLine = OverlayLabel.LOC_UNKNOWN
}
}
getLineLocation(index) {
if (index === 0)
return this._aLocLine
else
return this._bLocLine
}
isCollapseAndNotPartInterior() {
if (this._aDim === OverlayLabel.DIM_COLLAPSE && this._bDim === OverlayLabel.DIM_NOT_PART && this._bLocLine === Location.INTERIOR) return true
if (this._bDim === OverlayLabel.DIM_COLLAPSE && this._aDim === OverlayLabel.DIM_NOT_PART && this._aLocLine === Location.INTERIOR) return true
return false
}
initBoundary(index, locLeft, locRight, isHole) {
if (index === 0) {
this._aDim = OverlayLabel.DIM_BOUNDARY
this._aIsHole = isHole
this._aLocLeft = locLeft
this._aLocRight = locRight
this._aLocLine = Location.INTERIOR
} else {
this._bDim = OverlayLabel.DIM_BOUNDARY
this._bIsHole = isHole
this._bLocLeft = locLeft
this._bLocRight = locRight
this._bLocLine = Location.INTERIOR
}
}
dimension(index) {
if (index === 0) return this._aDim
return this._bDim
}
isBoundarySingleton() {
if (this._aDim === OverlayLabel.DIM_BOUNDARY && this._bDim === OverlayLabel.DIM_NOT_PART) return true
if (this._bDim === OverlayLabel.DIM_BOUNDARY && this._aDim === OverlayLabel.DIM_NOT_PART) return true
return false
}
isBoundaryCollapse() {
if (this.isLine()) return false
return !this.isBoundaryBoth()
}
isLinear(index) {
if (index === 0)
return this._aDim === OverlayLabel.DIM_LINE || this._aDim === OverlayLabel.DIM_COLLAPSE
return this._bDim === OverlayLabel.DIM_LINE || this._bDim === OverlayLabel.DIM_COLLAPSE
}
initNotPart(index) {
if (index === 0)
this._aDim = OverlayLabel.DIM_NOT_PART
else
this._bDim = OverlayLabel.DIM_NOT_PART
}
locationString(index, isForward) {
const buf = new StringBuilder()
if (this.isBoundary(index)) {
buf.append(Location.toLocationSymbol(this.getLocation(index, Position.LEFT, isForward)))
buf.append(Location.toLocationSymbol(this.getLocation(index, Position.RIGHT, isForward)))
} else {
buf.append(Location.toLocationSymbol(index === 0 ? this._aLocLine : this._bLocLine))
}
if (this.isKnown(index)) buf.append(OverlayLabel.dimensionSymbol(index === 0 ? this._aDim : this._bDim))
if (this.isCollapse(index))
buf.append(OverlayLabel.ringRoleSymbol(index === 0 ? this._aIsHole : this._bIsHole))
return buf.toString()
}
}
OverlayLabel.SYM_UNKNOWN = '#'
OverlayLabel.SYM_BOUNDARY = 'B'
OverlayLabel.SYM_COLLAPSE = 'C'
OverlayLabel.SYM_LINE = 'L'
OverlayLabel.DIM_UNKNOWN = -1
OverlayLabel.DIM_NOT_PART = OverlayLabel.DIM_UNKNOWN
OverlayLabel.DIM_LINE = 1
OverlayLabel.DIM_BOUNDARY = 2
OverlayLabel.DIM_COLLAPSE = 3
OverlayLabel.LOC_UNKNOWN = Location.NONE

View File

@ -1,201 +0,0 @@
import Location from '../../geom/Location.js'
import ArrayDeque from '../../../../../java/util/ArrayDeque.js'
import WKTWriter from '../../io/WKTWriter.js'
import Position from '../../geom/Position.js'
import OverlayNG from './OverlayNG.js'
import TopologyException from '../../geom/TopologyException.js'
import OverlayEdge from './OverlayEdge.js'
import ArrayList from '../../../../../java/util/ArrayList.js'
import Assert from '../../util/Assert.js'
import StringBuilder from '../../../../../java/lang/StringBuilder.js'
export default class OverlayLabeller {
constructor() {
OverlayLabeller.constructor_.apply(this, arguments)
}
static constructor_() {
this._graph = null
this._inputGeometry = null
this._edges = null
const graph = arguments[0], inputGeometry = arguments[1]
this._graph = graph
this._inputGeometry = inputGeometry
this._edges = graph.getEdges()
}
static findPropagationStartEdge(nodeEdge, geomIndex) {
let eStart = nodeEdge
do {
const label = eStart.getLabel()
if (label.isBoundary(geomIndex)) {
Assert.isTrue(label.hasSides(geomIndex))
return eStart
}
eStart = eStart.oNext()
} while (eStart !== nodeEdge)
return null
}
static propagateLinearLocationAtNode(eNode, geomIndex, isInputLine, edgeStack) {
const lineLoc = eNode.getLabel().getLineLocation(geomIndex)
if (isInputLine && lineLoc !== Location.EXTERIOR) return null
let e = eNode.oNextOE()
do {
const label = e.getLabel()
if (label.isLineLocationUnknown(geomIndex)) {
label.setLocationLine(geomIndex, lineLoc)
edgeStack.addFirst(e.symOE())
}
e = e.oNextOE()
} while (e !== eNode)
}
static findLinearEdgesWithLocation(edges, geomIndex) {
const linearEdges = new ArrayList()
for (const edge of edges) {
const lbl = edge.getLabel()
if (lbl.isLinear(geomIndex) && !lbl.isLineLocationUnknown(geomIndex))
linearEdges.add(edge)
}
return linearEdges
}
static toString() {
if (arguments.length === 1 && arguments[0] instanceof OverlayEdge) {
const nodeEdge = arguments[0]
const orig = nodeEdge.orig()
const sb = new StringBuilder()
sb.append('Node( ' + WKTWriter.format(orig) + ' )' + '\n')
let e = nodeEdge
do {
sb.append(' -> ' + e)
if (e.isResultLinked()) {
sb.append(' Link: ')
sb.append(e.nextResult())
}
sb.append('\n')
e = e.oNextOE()
} while (e !== nodeEdge)
return sb.toString()
}
}
markInResultArea(e, overlayOpCode) {
const label = e.getLabel()
if (label.isBoundaryEither() && OverlayNG.isResultOfOp(overlayOpCode, label.getLocationBoundaryOrLine(0, Position.RIGHT, e.isForward()), label.getLocationBoundaryOrLine(1, Position.RIGHT, e.isForward())))
e.markInResultArea()
}
locateEdgeBothEnds(geomIndex, edge) {
const locOrig = this._inputGeometry.locatePointInArea(geomIndex, edge.orig())
const locDest = this._inputGeometry.locatePointInArea(geomIndex, edge.dest())
const isInt = locOrig !== Location.EXTERIOR && locDest !== Location.EXTERIOR
const edgeLoc = isInt ? Location.INTERIOR : Location.EXTERIOR
return edgeLoc
}
labelAreaNodeEdges(nodes) {
for (const nodeEdge of nodes) {
this.propagateAreaLocations(nodeEdge, 0)
if (this._inputGeometry.hasEdges(1))
this.propagateAreaLocations(nodeEdge, 1)
}
}
labelCollapsedEdges() {
for (const edge of this._edges) {
if (edge.getLabel().isLineLocationUnknown(0))
this.labelCollapsedEdge(edge, 0)
if (edge.getLabel().isLineLocationUnknown(1))
this.labelCollapsedEdge(edge, 1)
}
}
labelCollapsedEdge(edge, geomIndex) {
const label = edge.getLabel()
if (!label.isCollapse(geomIndex)) return null
label.setLocationCollapse(geomIndex)
}
propagateLinearLocations(geomIndex) {
const linearEdges = OverlayLabeller.findLinearEdgesWithLocation(this._edges, geomIndex)
if (linearEdges.size() <= 0) return null
const edgeStack = new ArrayDeque(linearEdges)
const isInputLine = this._inputGeometry.isLine(geomIndex)
while (!edgeStack.isEmpty()) {
const lineEdge = edgeStack.removeFirst()
OverlayLabeller.propagateLinearLocationAtNode(lineEdge, geomIndex, isInputLine, edgeStack)
}
}
propagateAreaLocations(nodeEdge, geomIndex) {
if (!this._inputGeometry.isArea(geomIndex)) return null
if (nodeEdge.degree() === 1) return null
const eStart = OverlayLabeller.findPropagationStartEdge(nodeEdge, geomIndex)
if (eStart === null) return null
let currLoc = eStart.getLocation(geomIndex, Position.LEFT)
let e = eStart.oNextOE()
do {
const label = e.getLabel()
if (!label.isBoundary(geomIndex)) {
label.setLocationLine(geomIndex, currLoc)
} else {
Assert.isTrue(label.hasSides(geomIndex))
const locRight = e.getLocation(geomIndex, Position.RIGHT)
if (locRight !== currLoc)
throw new TopologyException('side location conflict: arg ' + geomIndex, e.getCoordinate())
const locLeft = e.getLocation(geomIndex, Position.LEFT)
if (locLeft === Location.NONE)
Assert.shouldNeverReachHere('found single null side at ' + e)
currLoc = locLeft
}
e = e.oNextOE()
} while (e !== eStart)
}
labelDisconnectedEdges() {
for (const edge of this._edges) {
if (edge.getLabel().isLineLocationUnknown(0))
this.labelDisconnectedEdge(edge, 0)
if (edge.getLabel().isLineLocationUnknown(1))
this.labelDisconnectedEdge(edge, 1)
}
}
unmarkDuplicateEdgesFromResultArea() {
for (const edge of this._edges)
if (edge.isInResultAreaBoth())
edge.unmarkFromResultAreaBoth()
}
locateEdge(geomIndex, edge) {
const loc = this._inputGeometry.locatePointInArea(geomIndex, edge.orig())
const edgeLoc = loc !== Location.EXTERIOR ? Location.INTERIOR : Location.EXTERIOR
return edgeLoc
}
labelConnectedLinearEdges() {
this.propagateLinearLocations(0)
if (this._inputGeometry.hasEdges(1))
this.propagateLinearLocations(1)
}
labelDisconnectedEdge(edge, geomIndex) {
const label = edge.getLabel()
if (!this._inputGeometry.isArea(geomIndex)) {
label.setLocationAll(geomIndex, Location.EXTERIOR)
return null
}
const edgeLoc = this.locateEdgeBothEnds(geomIndex, edge)
label.setLocationAll(geomIndex, edgeLoc)
}
markResultAreaEdges(overlayOpCode) {
for (const edge of this._edges)
this.markInResultArea(edge, overlayOpCode)
}
computeLabelling() {
const nodes = this._graph.getNodeEdges()
this.labelAreaNodeEdges(nodes)
this.labelConnectedLinearEdges()
this.labelCollapsedEdges()
this.labelConnectedLinearEdges()
this.labelDisconnectedEdges()
}
}

View File

@ -1,168 +0,0 @@
import IndexedPointOnLineLocator from './IndexedPointOnLineLocator.js'
import Location from '../../geom/Location.js'
import CoordinateList from '../../geom/CoordinateList.js'
import HashSet from '../../../../../java/util/HashSet.js'
import CoordinateFilter from '../../geom/CoordinateFilter.js'
import GeometryFactory from '../../geom/GeometryFactory.js'
import OverlayNG from './OverlayNG.js'
import OverlayUtil from './OverlayUtil.js'
import ArrayList from '../../../../../java/util/ArrayList.js'
import IndexedPointInAreaLocator from '../../algorithm/locate/IndexedPointInAreaLocator.js'
import Assert from '../../util/Assert.js'
export default class OverlayMixedPoints {
constructor() {
OverlayMixedPoints.constructor_.apply(this, arguments)
}
static constructor_() {
this._opCode = null
this._pm = null
this._geomPoint = null
this._geomNonPointInput = null
this._geometryFactory = null
this._isPointRHS = null
this._geomNonPoint = null
this._geomNonPointDim = null
this._locator = null
this._resultDim = null
const opCode = arguments[0], geom0 = arguments[1], geom1 = arguments[2], pm = arguments[3]
this._opCode = opCode
this._pm = pm
this._geometryFactory = geom0.getFactory()
this._resultDim = OverlayUtil.resultDimension(opCode, geom0.getDimension(), geom1.getDimension())
if (geom0.getDimension() === 0) {
this._geomPoint = geom0
this._geomNonPointInput = geom1
this._isPointRHS = false
} else {
this._geomPoint = geom1
this._geomNonPointInput = geom0
this._isPointRHS = true
}
}
static overlay(opCode, geom0, geom1, pm) {
const overlay = new OverlayMixedPoints(opCode, geom0, geom1, pm)
return overlay.getResult()
}
static extractCoordinates(points, pm) {
const coords = new CoordinateList()
points.apply(new (class {
get interfaces_() {
return [CoordinateFilter]
}
filter(coord) {
const p = OverlayUtil.round(coord, pm)
coords.add(p, false)
}
})())
return coords.toCoordinateArray()
}
static extractPolygons(geom) {
const list = new ArrayList()
for (let i = 0; i < geom.getNumGeometries(); i++) {
const poly = geom.getGeometryN(i)
if (!poly.isEmpty())
list.add(poly)
}
return list
}
static extractLines(geom) {
const list = new ArrayList()
for (let i = 0; i < geom.getNumGeometries(); i++) {
const line = geom.getGeometryN(i)
if (!line.isEmpty())
list.add(line)
}
return list
}
createLocator(geomNonPoint) {
if (this._geomNonPointDim === 2)
return new IndexedPointInAreaLocator(geomNonPoint)
else
return new IndexedPointOnLineLocator(geomNonPoint)
}
hasLocation(isCovered, coord) {
const isExterior = Location.EXTERIOR === this._locator.locate(coord)
if (isCovered)
return !isExterior
return isExterior
}
computeIntersection(coords) {
return this.createPointResult(this.findPoints(true, coords))
}
computeDifference(coords) {
if (this._isPointRHS)
return this.copyNonPoint()
return this.createPointResult(this.findPoints(false, coords))
}
computeUnion(coords) {
const resultPointList = this.findPoints(false, coords)
let resultLineList = null
if (this._geomNonPointDim === 1)
resultLineList = OverlayMixedPoints.extractLines(this._geomNonPoint)
let resultPolyList = null
if (this._geomNonPointDim === 2)
resultPolyList = OverlayMixedPoints.extractPolygons(this._geomNonPoint)
return OverlayUtil.createResultGeometry(resultPolyList, resultLineList, resultPointList, this._geometryFactory)
}
getResult() {
this._geomNonPoint = this.prepareNonPoint(this._geomNonPointInput)
this._geomNonPointDim = this._geomNonPoint.getDimension()
this._locator = this.createLocator(this._geomNonPoint)
const coords = OverlayMixedPoints.extractCoordinates(this._geomPoint, this._pm)
switch (this._opCode) {
case OverlayNG.INTERSECTION:
return this.computeIntersection(coords)
case OverlayNG.UNION:
case OverlayNG.SYMDIFFERENCE:
return this.computeUnion(coords)
case OverlayNG.DIFFERENCE:
return this.computeDifference(coords)
}
Assert.shouldNeverReachHere('Unknown overlay op code')
return null
}
copyNonPoint() {
if (this._geomNonPointInput !== this._geomNonPoint) return this._geomNonPoint
return this._geomNonPoint.copy()
}
prepareNonPoint(geomInput) {
if (this._resultDim === 0)
return geomInput
const geomPrep = OverlayNG.union(this._geomNonPointInput, this._pm)
return geomPrep
}
createPointResult(points) {
if (points.size() === 0)
return this._geometryFactory.createEmpty(0)
else if (points.size() === 1)
return points.get(0)
const pointsArray = GeometryFactory.toPointArray(points)
return this._geometryFactory.createMultiPoint(pointsArray)
}
findPoints(isCovered, coords) {
const resultCoords = new HashSet()
for (const coord of coords)
if (this.hasLocation(isCovered, coord))
resultCoords.add(coord.copy())
return this.createPoints(resultCoords)
}
createPoints(coords) {
const points = new ArrayList()
for (const coord of coords) {
const point = this._geometryFactory.createPoint(coord)
points.add(point)
}
return points
}
}

View File

@ -1,227 +0,0 @@
import IntersectionPointBuilder from './IntersectionPointBuilder.js'
import OverlayMixedPoints from './OverlayMixedPoints.js'
import Location from '../../geom/Location.js'
import OverlayPoints from './OverlayPoints.js'
import Geometry from '../../geom/Geometry.js'
import PolygonBuilder from './PolygonBuilder.js'
import hasInterface from '../../../../../hasInterface.js'
import Noder from '../../noding/Noder.js'
import OverlayLabeller from './OverlayLabeller.js'
import LineBuilder from './LineBuilder.js'
import TopologyException from '../../geom/TopologyException.js'
import ElevationModel from './ElevationModel.js'
import EdgeNodingBuilder from './EdgeNodingBuilder.js'
import OverlayUtil from './OverlayUtil.js'
import PrecisionModel from '../../geom/PrecisionModel.js'
import OverlayGraph from './OverlayGraph.js'
import OverlayOp from '../overlay/OverlayOp.js'
import InputGeometry from './InputGeometry.js'
export default class OverlayNG {
constructor() {
OverlayNG.constructor_.apply(this, arguments)
}
static constructor_() {
this._opCode = null
this._inputGeom = null
this._geomFact = null
this._pm = null
this._noder = null
this._isStrictMode = OverlayNG.STRICT_MODE_DEFAULT
this._isOptimized = true
this._isAreaResultOnly = false
this._isOutputEdges = false
this._isOutputResultEdges = false
this._isOutputNodedEdges = false
if (arguments.length === 2) {
const geom = arguments[0], pm = arguments[1]
OverlayNG.constructor_.call(this, geom, null, pm, OverlayNG.UNION)
} else if (arguments.length === 3) {
const geom0 = arguments[0], geom1 = arguments[1], opCode = arguments[2]
OverlayNG.constructor_.call(this, geom0, geom1, geom0.getFactory().getPrecisionModel(), opCode)
} else if (arguments.length === 4) {
const geom0 = arguments[0], geom1 = arguments[1], pm = arguments[2], opCode = arguments[3]
this._pm = pm
this._opCode = opCode
this._geomFact = geom0.getFactory()
this._inputGeom = new InputGeometry(geom0, geom1)
}
}
static union() {
if (arguments.length === 2) {
const geom = arguments[0], pm = arguments[1]
const ov = new OverlayNG(geom, pm)
const geomOv = ov.getResult()
return geomOv
} else if (arguments.length === 3) {
const geom = arguments[0], pm = arguments[1], noder = arguments[2]
const ov = new OverlayNG(geom, pm)
ov.setNoder(noder)
ov.setStrictMode(true)
const geomOv = ov.getResult()
return geomOv
}
}
static overlay() {
if (arguments.length === 3) {
const geom0 = arguments[0], geom1 = arguments[1], opCode = arguments[2]
const ov = new OverlayNG(geom0, geom1, opCode)
return ov.getResult()
} else if (arguments.length === 4) {
if (hasInterface(arguments[3], Noder) && (Number.isInteger(arguments[2]) && (arguments[0] instanceof Geometry && arguments[1] instanceof Geometry))) {
const geom0 = arguments[0], geom1 = arguments[1], opCode = arguments[2], noder = arguments[3]
const ov = new OverlayNG(geom0, geom1, null, opCode)
ov.setNoder(noder)
const geomOv = ov.getResult()
return geomOv
} else if (arguments[3] instanceof PrecisionModel && (Number.isInteger(arguments[2]) && (arguments[0] instanceof Geometry && arguments[1] instanceof Geometry))) {
const geom0 = arguments[0], geom1 = arguments[1], opCode = arguments[2], pm = arguments[3]
const ov = new OverlayNG(geom0, geom1, pm, opCode)
const geomOv = ov.getResult()
return geomOv
}
} else if (arguments.length === 5) {
const geom0 = arguments[0], geom1 = arguments[1], opCode = arguments[2], pm = arguments[3], noder = arguments[4]
const ov = new OverlayNG(geom0, geom1, pm, opCode)
ov.setNoder(noder)
const geomOv = ov.getResult()
return geomOv
}
}
static isResultOfOpPoint(label, opCode) {
const loc0 = label.getLocation(0)
const loc1 = label.getLocation(1)
return OverlayNG.isResultOfOp(opCode, loc0, loc1)
}
static isEmpty(list) {
return list === null || list.size() === 0
}
static isResultOfOp(overlayOpCode, loc0, loc1) {
if (loc0 === Location.BOUNDARY) loc0 = Location.INTERIOR
if (loc1 === Location.BOUNDARY) loc1 = Location.INTERIOR
switch (overlayOpCode) {
case OverlayNG.INTERSECTION:
return loc0 === Location.INTERIOR && loc1 === Location.INTERIOR
case OverlayNG.UNION:
return loc0 === Location.INTERIOR || loc1 === Location.INTERIOR
case OverlayNG.DIFFERENCE:
return loc0 === Location.INTERIOR && loc1 !== Location.INTERIOR
case OverlayNG.SYMDIFFERENCE:
return loc0 === Location.INTERIOR && loc1 !== Location.INTERIOR || loc0 !== Location.INTERIOR && loc1 === Location.INTERIOR
}
return false
}
setOutputResultEdges(isOutputResultEdges) {
this._isOutputResultEdges = isOutputResultEdges
}
getResult() {
if (OverlayUtil.isEmptyResult(this._opCode, this._inputGeom.getGeometry(0), this._inputGeom.getGeometry(1), this._pm))
return this.createEmptyResult()
const elevModel = ElevationModel.create(this._inputGeom.getGeometry(0), this._inputGeom.getGeometry(1))
let result = null
if (this._inputGeom.isAllPoints())
result = OverlayPoints.overlay(this._opCode, this._inputGeom.getGeometry(0), this._inputGeom.getGeometry(1), this._pm)
else if (!this._inputGeom.isSingle() && this._inputGeom.hasPoints())
result = OverlayMixedPoints.overlay(this._opCode, this._inputGeom.getGeometry(0), this._inputGeom.getGeometry(1), this._pm)
else
result = this.computeEdgeOverlay()
elevModel.populateZ(result)
return result
}
nodeEdges() {
const nodingBuilder = new EdgeNodingBuilder(this._pm, this._noder)
if (this._isOptimized) {
const clipEnv = OverlayUtil.clippingEnvelope(this._opCode, this._inputGeom, this._pm)
if (clipEnv !== null) nodingBuilder.setClipEnvelope(clipEnv)
}
const mergedEdges = nodingBuilder.build(this._inputGeom.getGeometry(0), this._inputGeom.getGeometry(1))
this._inputGeom.setCollapsed(0, !nodingBuilder.hasEdgesFor(0))
this._inputGeom.setCollapsed(1, !nodingBuilder.hasEdgesFor(1))
return mergedEdges
}
setOutputNodedEdges(isOutputNodedEdges) {
this._isOutputEdges = true
this._isOutputNodedEdges = isOutputNodedEdges
}
buildGraph(edges) {
const graph = new OverlayGraph()
for (const e of edges)
graph.addEdge(e.getCoordinates(), e.createLabel())
return graph
}
setAreaResultOnly(isAreaResultOnly) {
this._isAreaResultOnly = isAreaResultOnly
}
setOptimized(isOptimized) {
this._isOptimized = isOptimized
}
extractResult(opCode, graph) {
const isAllowMixedIntResult = !this._isStrictMode
const resultAreaEdges = graph.getResultAreaEdges()
const polyBuilder = new PolygonBuilder(resultAreaEdges, this._geomFact)
const resultPolyList = polyBuilder.getPolygons()
const hasResultAreaComponents = resultPolyList.size() > 0
let resultLineList = null
let resultPointList = null
if (!this._isAreaResultOnly) {
const allowResultLines = !hasResultAreaComponents || isAllowMixedIntResult || opCode === OverlayNG.SYMDIFFERENCE || opCode === OverlayNG.UNION
if (allowResultLines) {
const lineBuilder = new LineBuilder(this._inputGeom, graph, hasResultAreaComponents, opCode, this._geomFact)
lineBuilder.setStrictMode(this._isStrictMode)
resultLineList = lineBuilder.getLines()
}
const hasResultComponents = hasResultAreaComponents || resultLineList.size() > 0
const allowResultPoints = !hasResultComponents || isAllowMixedIntResult
if (opCode === OverlayNG.INTERSECTION && allowResultPoints) {
const pointBuilder = new IntersectionPointBuilder(graph, this._geomFact)
pointBuilder.setStrictMode(this._isStrictMode)
resultPointList = pointBuilder.getPoints()
}
}
if (OverlayNG.isEmpty(resultPolyList) && OverlayNG.isEmpty(resultLineList) && OverlayNG.isEmpty(resultPointList)) return this.createEmptyResult()
const resultGeom = OverlayUtil.createResultGeometry(resultPolyList, resultLineList, resultPointList, this._geomFact)
return resultGeom
}
computeEdgeOverlay() {
const edges = this.nodeEdges()
const graph = this.buildGraph(edges)
if (this._isOutputNodedEdges)
return OverlayUtil.toLines(graph, this._isOutputEdges, this._geomFact)
this.labelGraph(graph)
if (this._isOutputEdges || this._isOutputResultEdges)
return OverlayUtil.toLines(graph, this._isOutputEdges, this._geomFact)
const result = this.extractResult(this._opCode, graph)
if (OverlayUtil.isFloating(this._pm)) {
const isAreaConsistent = OverlayUtil.isResultAreaConsistent(this._inputGeom.getGeometry(0), this._inputGeom.getGeometry(1), this._opCode, result)
if (!isAreaConsistent) throw new TopologyException('Result area inconsistent with overlay operation')
}
return result
}
setOutputEdges(isOutputEdges) {
this._isOutputEdges = isOutputEdges
}
createEmptyResult() {
return OverlayUtil.createEmptyResult(OverlayUtil.resultDimension(this._opCode, this._inputGeom.getDimension(0), this._inputGeom.getDimension(1)), this._geomFact)
}
setNoder(noder) {
this._noder = noder
}
setStrictMode(isStrictMode) {
this._isStrictMode = isStrictMode
}
labelGraph(graph) {
const labeller = new OverlayLabeller(graph, this._inputGeom)
labeller.computeLabelling()
labeller.markResultAreaEdges(this._opCode)
labeller.unmarkDuplicateEdgesFromResultArea()
}
}
OverlayNG.INTERSECTION = OverlayOp.INTERSECTION
OverlayNG.UNION = OverlayOp.UNION
OverlayNG.DIFFERENCE = OverlayOp.DIFFERENCE
OverlayNG.SYMDIFFERENCE = OverlayOp.SYMDIFFERENCE
OverlayNG.STRICT_MODE_DEFAULT = false

View File

@ -1,142 +0,0 @@
import Geometry from '../../geom/Geometry.js'
import hasInterface from '../../../../../hasInterface.js'
import UnionStrategy from '../union/UnionStrategy.js'
import Collection from '../../../../../java/util/Collection.js'
import OverlayNG from './OverlayNG.js'
import UnaryUnionOp from '../union/UnaryUnionOp.js'
import TopologyException from '../../geom/TopologyException.js'
import SnappingNoder from '../../noding/snap/SnappingNoder.js'
import PrecisionUtil from './PrecisionUtil.js'
import PrecisionModel from '../../geom/PrecisionModel.js'
import RuntimeException from '../../../../../java/lang/RuntimeException.js'
export default class OverlayNGRobust {
static union() {
if (arguments.length === 1) {
if (arguments[0] instanceof Geometry) {
const geom = arguments[0]
const op = new UnaryUnionOp(geom)
op.setUnionFunction(OverlayNGRobust.OVERLAY_UNION)
return op.union()
} else if (hasInterface(arguments[0], Collection)) {
const geoms = arguments[0]
const op = new UnaryUnionOp(geoms)
op.setUnionFunction(OverlayNGRobust.OVERLAY_UNION)
return op.union()
}
} else if (arguments.length === 2) {
const geoms = arguments[0], geomFact = arguments[1]
const op = new UnaryUnionOp(geoms, geomFact)
op.setUnionFunction(OverlayNGRobust.OVERLAY_UNION)
return op.union()
}
}
static overlay(geom0, geom1, opCode) {
let result = null
let exOriginal = null
try {
result = OverlayNG.overlay(geom0, geom1, opCode)
return result
} catch (ex) {
if (ex instanceof RuntimeException)
exOriginal = ex
else throw ex
} finally {}
result = OverlayNGRobust.overlaySnapTries(geom0, geom1, opCode)
if (result !== null) return result
result = OverlayNGRobust.overlaySR(geom0, geom1, opCode)
if (result !== null) return result
throw exOriginal
}
static overlaySnapTol(geom0, geom1, opCode, snapTol) {
const snapNoder = new SnappingNoder(snapTol)
return OverlayNG.overlay(geom0, geom1, opCode, snapNoder)
}
static overlaySnapTries(geom0, geom1, opCode) {
let result = null
let snapTol = OverlayNGRobust.snapTolerance(geom0, geom1)
for (let i = 0; i < OverlayNGRobust.NUM_SNAP_TRIES; i++) {
result = OverlayNGRobust.overlaySnapping(geom0, geom1, opCode, snapTol)
if (result !== null) return result
result = OverlayNGRobust.overlaySnapBoth(geom0, geom1, opCode, snapTol)
if (result !== null) return result
snapTol = snapTol * 10
}
return null
}
static overlaySR(geom0, geom1, opCode) {
let result = null
try {
const scaleSafe = PrecisionUtil.safeScale(geom0, geom1)
const pmSafe = new PrecisionModel(scaleSafe)
result = OverlayNG.overlay(geom0, geom1, opCode, pmSafe)
return result
} catch (ex) {
if (ex instanceof TopologyException) {} else {
throw ex
}
} finally {}
return null
}
static snapSelf(geom, snapTol) {
const ov = new OverlayNG(geom, null)
const snapNoder = new SnappingNoder(snapTol)
ov.setNoder(snapNoder)
ov.setStrictMode(true)
return ov.getResult()
}
static overlaySnapBoth(geom0, geom1, opCode, snapTol) {
try {
const snap0 = OverlayNGRobust.snapSelf(geom0, snapTol)
const snap1 = OverlayNGRobust.snapSelf(geom1, snapTol)
return OverlayNGRobust.overlaySnapTol(snap0, snap1, opCode, snapTol)
} catch (ex) {
if (ex instanceof TopologyException) {} else {
throw ex
}
} finally {}
return null
}
static overlaySnapping(geom0, geom1, opCode, snapTol) {
try {
return OverlayNGRobust.overlaySnapTol(geom0, geom1, opCode, snapTol)
} catch (ex) {
if (ex instanceof TopologyException) {} else {
throw ex
}
} finally {}
return null
}
static ordinateMagnitude(geom) {
if (geom === null || geom.isEmpty()) return 0
const env = geom.getEnvelopeInternal()
const magMax = Math.max(Math.abs(env.getMaxX()), Math.abs(env.getMaxY()))
const magMin = Math.max(Math.abs(env.getMinX()), Math.abs(env.getMinY()))
return Math.max(magMax, magMin)
}
static snapTolerance() {
if (arguments.length === 1) {
const geom = arguments[0]
const magnitude = OverlayNGRobust.ordinateMagnitude(geom)
return magnitude / OverlayNGRobust.SNAP_TOL_FACTOR
} else if (arguments.length === 2) {
const geom0 = arguments[0], geom1 = arguments[1]
const tol0 = OverlayNGRobust.snapTolerance(geom0)
const tol1 = OverlayNGRobust.snapTolerance(geom1)
const snapTol = Math.max(tol0, tol1)
return snapTol
}
}
}
OverlayNGRobust.OVERLAY_UNION = new (class {
get interfaces_() {
return [UnionStrategy]
}
union(g0, g1) {
return OverlayNGRobust.overlay(g0, g1, OverlayNG.UNION)
}
isFloatingPrecision() {
return true
}
})()
OverlayNGRobust.NUM_SNAP_TRIES = 5
OverlayNGRobust.SNAP_TOL_FACTOR = 1e12

View File

@ -1,107 +0,0 @@
import HashMap from '../../../../../java/util/HashMap.js'
import Point from '../../geom/Point.js'
import OverlayNG from './OverlayNG.js'
import OverlayUtil from './OverlayUtil.js'
import GeometryComponentFilter from '../../geom/GeometryComponentFilter.js'
import CoordinateSequence from '../../geom/CoordinateSequence.js'
import ArrayList from '../../../../../java/util/ArrayList.js'
export default class OverlayPoints {
constructor() {
OverlayPoints.constructor_.apply(this, arguments)
}
static constructor_() {
this._opCode = null
this._geom0 = null
this._geom1 = null
this._pm = null
this._geometryFactory = null
this._resultList = null
const opCode = arguments[0], geom0 = arguments[1], geom1 = arguments[2], pm = arguments[3]
this._opCode = opCode
this._geom0 = geom0
this._geom1 = geom1
this._pm = pm
this._geometryFactory = geom0.getFactory()
}
static overlay(opCode, geom0, geom1, pm) {
const overlay = new OverlayPoints(opCode, geom0, geom1, pm)
return overlay.getResult()
}
static roundCoord(pt, pm) {
const p = pt.getCoordinate()
if (OverlayUtil.isFloating(pm)) return p
const p2 = p.copy()
pm.makePrecise(p2)
return p2
}
buildPointMap(geoms) {
const map = new HashMap()
geoms.apply(new (class {
get interfaces_() {
return [GeometryComponentFilter]
}
filter(geom) {
if (!(geom instanceof Point)) return null
if (geom.isEmpty()) return null
const pt = geom
const p = OverlayPoints.roundCoord(pt, this._pm)
if (!map.containsKey(p)) map.put(p, pt)
}
})())
return map
}
computeIntersection(map0, map1, resultList) {
for (const entry of map0.entrySet())
if (map1.containsKey(entry.getKey()))
resultList.add(this.copyPoint(entry.getValue()))
}
computeDifference(map0, map1, resultList) {
for (const entry of map0.entrySet())
if (!map1.containsKey(entry.getKey()))
resultList.add(this.copyPoint(entry.getValue()))
}
computeUnion(map0, map1, resultList) {
for (const p of map0.values())
resultList.add(this.copyPoint(p))
for (const entry of map1.entrySet())
if (!map0.containsKey(entry.getKey()))
resultList.add(this.copyPoint(entry.getValue()))
}
getResult() {
const map0 = this.buildPointMap(this._geom0)
const map1 = this.buildPointMap(this._geom1)
this._resultList = new ArrayList()
switch (this._opCode) {
case OverlayNG.INTERSECTION:
this.computeIntersection(map0, map1, this._resultList)
break
case OverlayNG.UNION:
this.computeUnion(map0, map1, this._resultList)
break
case OverlayNG.DIFFERENCE:
this.computeDifference(map0, map1, this._resultList)
break
case OverlayNG.SYMDIFFERENCE:
this.computeDifference(map0, map1, this._resultList)
this.computeDifference(map1, map0, this._resultList)
break
}
if (this._resultList.isEmpty()) return OverlayUtil.createEmptyResult(0, this._geometryFactory)
return this._geometryFactory.buildGeometry(this._resultList)
}
copyPoint(pt) {
if (OverlayUtil.isFloating(this._pm)) return pt.copy()
const seq = pt.getCoordinateSequence()
const seq2 = seq.copy()
seq2.setOrdinate(0, CoordinateSequence.X, this._pm.makePrecise(seq.getX(0)))
seq2.setOrdinate(0, CoordinateSequence.Y, this._pm.makePrecise(seq.getY(0)))
return this._geometryFactory.createPoint(seq2)
}
}

View File

@ -1,192 +0,0 @@
import Coordinate from '../../geom/Coordinate.js'
import Point from '../../geom/Point.js'
import OverlayNG from './OverlayNG.js'
import RobustClipEnvelopeComputer from './RobustClipEnvelopeComputer.js'
import PrecisionModel from '../../geom/PrecisionModel.js'
import ArrayList from '../../../../../java/util/ArrayList.js'
import Assert from '../../util/Assert.js'
export default class OverlayUtil {
static labelForResult(edge) {
return edge.getLabel().toString(edge.isForward()) + (edge.isInResultArea() ? ' Res' : '')
}
static isGreater(v1, v2, tol) {
return v1 >= v2 * (1 - tol)
}
static isEmptyResult(opCode, a, b, pm) {
switch (opCode) {
case OverlayNG.INTERSECTION:
if (OverlayUtil.isEnvDisjoint(a, b, pm)) return true
break
case OverlayNG.DIFFERENCE:
if (OverlayUtil.isEmpty(a)) return true
break
case OverlayNG.UNION:
case OverlayNG.SYMDIFFERENCE:
if (OverlayUtil.isEmpty(a) && OverlayUtil.isEmpty(b)) return true
break
}
return false
}
static isEnvDisjoint(a, b, pm) {
if (OverlayUtil.isEmpty(a) || OverlayUtil.isEmpty(b)) return true
if (OverlayUtil.isFloating(pm))
return a.getEnvelopeInternal().disjoint(b.getEnvelopeInternal())
return OverlayUtil.isDisjoint(a.getEnvelopeInternal(), b.getEnvelopeInternal(), pm)
}
static safeExpandDistance(env, pm) {
let envExpandDist = null
if (OverlayUtil.isFloating(pm)) {
let minSize = Math.min(env.getHeight(), env.getWidth())
if (minSize <= 0.0)
minSize = Math.max(env.getHeight(), env.getWidth())
envExpandDist = OverlayUtil.SAFE_ENV_BUFFER_FACTOR * minSize
} else {
const gridSize = 1.0 / pm.getScale()
envExpandDist = OverlayUtil.SAFE_ENV_GRID_FACTOR * gridSize
}
return envExpandDist
}
static createResultGeometry(resultPolyList, resultLineList, resultPointList, geometryFactory) {
const geomList = new ArrayList()
if (resultPolyList !== null) geomList.addAll(resultPolyList)
if (resultLineList !== null) geomList.addAll(resultLineList)
if (resultPointList !== null) geomList.addAll(resultPointList)
return geometryFactory.buildGeometry(geomList)
}
static safeEnv(env, pm) {
const envExpandDist = OverlayUtil.safeExpandDistance(env, pm)
const safeEnv = env.copy()
safeEnv.expandBy(envExpandDist)
return safeEnv
}
static isFloating(pm) {
if (pm === null) return true
return pm.isFloating()
}
static isResultAreaConsistent(geom0, geom1, opCode, result) {
if (geom0 === null || geom1 === null) return true
const areaResult = result.getArea()
const areaA = geom0.getArea()
const areaB = geom1.getArea()
let isConsistent = true
switch (opCode) {
case OverlayNG.INTERSECTION:
isConsistent = OverlayUtil.isLess(areaResult, areaA, OverlayUtil.AREA_HEURISTIC_TOLERANCE) && OverlayUtil.isLess(areaResult, areaB, OverlayUtil.AREA_HEURISTIC_TOLERANCE)
break
case OverlayNG.DIFFERENCE:
isConsistent = OverlayUtil.isLess(areaResult, areaA, OverlayUtil.AREA_HEURISTIC_TOLERANCE) && OverlayUtil.isGreater(areaResult, areaA - areaB, OverlayUtil.AREA_HEURISTIC_TOLERANCE)
break
case OverlayNG.SYMDIFFERENCE:
isConsistent = OverlayUtil.isLess(areaResult, areaA + areaB, OverlayUtil.AREA_HEURISTIC_TOLERANCE)
break
case OverlayNG.UNION:
isConsistent = OverlayUtil.isLess(areaA, areaResult, OverlayUtil.AREA_HEURISTIC_TOLERANCE) && OverlayUtil.isLess(areaB, areaResult, OverlayUtil.AREA_HEURISTIC_TOLERANCE) && OverlayUtil.isGreater(areaResult, areaA - areaB, OverlayUtil.AREA_HEURISTIC_TOLERANCE)
break
}
return isConsistent
}
static clippingEnvelope(opCode, inputGeom, pm) {
const resultEnv = OverlayUtil.resultEnvelope(opCode, inputGeom, pm)
if (resultEnv === null) return null
const clipEnv = RobustClipEnvelopeComputer.getEnvelope(inputGeom.getGeometry(0), inputGeom.getGeometry(1), resultEnv)
const safeEnv = OverlayUtil.safeEnv(clipEnv, pm)
return safeEnv
}
static round() {
if (arguments[0] instanceof Point && arguments[1] instanceof PrecisionModel) {
const pt = arguments[0], pm = arguments[1]
if (pt.isEmpty()) return null
return OverlayUtil.round(pt.getCoordinate(), pm)
} else if (arguments[0] instanceof Coordinate && arguments[1] instanceof PrecisionModel) {
const p = arguments[0], pm = arguments[1]
if (!OverlayUtil.isFloating(pm)) {
const pRound = p.copy()
pm.makePrecise(pRound)
return pRound
}
return p
}
}
static resultDimension(opCode, dim0, dim1) {
let resultDimension = -1
switch (opCode) {
case OverlayNG.INTERSECTION:
resultDimension = Math.min(dim0, dim1)
break
case OverlayNG.UNION:
resultDimension = Math.max(dim0, dim1)
break
case OverlayNG.DIFFERENCE:
resultDimension = dim0
break
case OverlayNG.SYMDIFFERENCE:
resultDimension = Math.max(dim0, dim1)
break
}
return resultDimension
}
static toLines(graph, isOutputEdges, geomFact) {
const lines = new ArrayList()
for (const edge of graph.getEdges()) {
const includeEdge = isOutputEdges || edge.isInResultArea()
if (!includeEdge) continue
const pts = edge.getCoordinatesOriented()
const line = geomFact.createLineString(pts)
line.setUserData(OverlayUtil.labelForResult(edge))
lines.add(line)
}
return geomFact.buildGeometry(lines)
}
static createEmptyResult(dim, geomFact) {
let result = null
switch (dim) {
case 0:
result = geomFact.createPoint()
break
case 1:
result = geomFact.createLineString()
break
case 2:
result = geomFact.createPolygon()
break
case -1:
result = geomFact.createGeometryCollection()
break
default:
Assert.shouldNeverReachHere('Unable to determine overlay result geometry dimension')
}
return result
}
static resultEnvelope(opCode, inputGeom, pm) {
let overlapEnv = null
switch (opCode) {
case OverlayNG.INTERSECTION:
const envA = OverlayUtil.safeEnv(inputGeom.getEnvelope(0), pm)
const envB = OverlayUtil.safeEnv(inputGeom.getEnvelope(1), pm)
overlapEnv = envA.intersection(envB)
break
case OverlayNG.DIFFERENCE:
overlapEnv = OverlayUtil.safeEnv(inputGeom.getEnvelope(0), pm)
break
}
return overlapEnv
}
static isEmpty(geom) {
return geom === null || geom.isEmpty()
}
static isLess(v1, v2, tol) {
return v1 <= v2 * (1 + tol)
}
static isDisjoint(envA, envB, pm) {
if (pm.makePrecise(envB.getMinX()) > pm.makePrecise(envA.getMaxX())) return true
if (pm.makePrecise(envB.getMaxX()) < pm.makePrecise(envA.getMinX())) return true
if (pm.makePrecise(envB.getMinY()) > pm.makePrecise(envA.getMaxY())) return true
if (pm.makePrecise(envB.getMaxY()) < pm.makePrecise(envA.getMinY())) return true
return false
}
}
OverlayUtil.SAFE_ENV_BUFFER_FACTOR = 0.1
OverlayUtil.SAFE_ENV_GRID_FACTOR = 3
OverlayUtil.AREA_HEURISTIC_TOLERANCE = 0.1

View File

@ -1,106 +0,0 @@
import TopologyException from '../../geom/TopologyException.js'
import MaximalEdgeRing from './MaximalEdgeRing.js'
import ArrayList from '../../../../../java/util/ArrayList.js'
import Assert from '../../util/Assert.js'
export default class PolygonBuilder {
constructor() {
PolygonBuilder.constructor_.apply(this, arguments)
}
static constructor_() {
this._geometryFactory = null
this._shellList = new ArrayList()
this._freeHoleList = new ArrayList()
this._isEnforcePolygonal = true
if (arguments.length === 2) {
const resultAreaEdges = arguments[0], geomFact = arguments[1]
PolygonBuilder.constructor_.call(this, resultAreaEdges, geomFact, true)
} else if (arguments.length === 3) {
const resultAreaEdges = arguments[0], geomFact = arguments[1], isEnforcePolygonal = arguments[2]
this._geometryFactory = geomFact
this._isEnforcePolygonal = isEnforcePolygonal
this.buildRings(resultAreaEdges)
}
}
static buildMaximalRings(edges) {
const edgeRings = new ArrayList()
for (const e of edges)
if (e.isInResultArea() && e.getLabel().isBoundaryEither())
if (e.getEdgeRingMax() === null) {
const er = new MaximalEdgeRing(e)
edgeRings.add(er)
}
return edgeRings
}
static assignHoles(shell, edgeRings) {
for (const er of edgeRings)
if (er.isHole())
er.setShell(shell)
}
buildRings(resultAreaEdges) {
this.linkResultAreaEdgesMax(resultAreaEdges)
const maxRings = PolygonBuilder.buildMaximalRings(resultAreaEdges)
this.buildMinimalRings(maxRings)
this.placeFreeHoles(this._shellList, this._freeHoleList)
}
assignShellsAndHoles(minRings) {
const shell = this.findSingleShell(minRings)
if (shell !== null) {
PolygonBuilder.assignHoles(shell, minRings)
this._shellList.add(shell)
} else {
this._freeHoleList.addAll(minRings)
}
}
buildMinimalRings(maxRings) {
for (const erMax of maxRings) {
const minRings = erMax.buildMinimalRings(this._geometryFactory)
this.assignShellsAndHoles(minRings)
}
}
computePolygons(shellList) {
const resultPolyList = new ArrayList()
for (const er of shellList) {
const poly = er.toPolygon(this._geometryFactory)
resultPolyList.add(poly)
}
return resultPolyList
}
findSingleShell(edgeRings) {
let shellCount = 0
let shell = null
for (const er of edgeRings)
if (!er.isHole()) {
shell = er
shellCount++
}
Assert.isTrue(shellCount <= 1, 'found two shells in EdgeRing list')
return shell
}
placeFreeHoles(shellList, freeHoleList) {
for (const hole of freeHoleList)
if (hole.getShell() === null) {
const shell = hole.findEdgeRingContaining(shellList)
if (this._isEnforcePolygonal && shell === null)
throw new TopologyException('unable to assign free hole to a shell', hole.getCoordinate())
hole.setShell(shell)
}
}
getShellRings() {
return this._shellList
}
getPolygons() {
return this.computePolygons(this._shellList)
}
linkResultAreaEdgesMax(resultEdges) {
for (const edge of resultEdges)
MaximalEdgeRing.linkResultAreaMaxRingAtNode(edge)
}
}

View File

@ -1,19 +0,0 @@
import IllegalArgumentException from '../../../../../java/lang/IllegalArgumentException.js'
import OverlayNG from './OverlayNG.js'
import TopologyException from '../../geom/TopologyException.js'
export default class PrecisionReducer {
static reducePrecision(geom, pm) {
const ov = new OverlayNG(geom, pm)
if (geom.getDimension() === 2)
ov.setAreaResultOnly(true)
try {
const reduced = ov.getResult()
return reduced
} catch (ex) {
if (ex instanceof TopologyException)
throw new IllegalArgumentException('Reduction failed, possible invalid input')
else throw ex
} finally {}
}
}

View File

@ -1,125 +0,0 @@
import Geometry from '../../geom/Geometry.js'
import CoordinateFilter from '../../geom/CoordinateFilter.js'
import OrdinateFormat from '../../io/OrdinateFormat.js'
import MathUtil from '../../math/MathUtil.js'
import PrecisionModel from '../../geom/PrecisionModel.js'
export default class PrecisionUtil {
static robustScale() {
if (arguments.length === 1) {
const a = arguments[0]
const inherentScale = PrecisionUtil.inherentScale(a)
const safeScale = PrecisionUtil.safeScale(a)
return PrecisionUtil.robustScale(inherentScale, safeScale)
} else if (arguments.length === 2) {
if (arguments[0] instanceof Geometry && arguments[1] instanceof Geometry) {
const a = arguments[0], b = arguments[1]
const inherentScale = PrecisionUtil.inherentScale(a, b)
const safeScale = PrecisionUtil.safeScale(a, b)
return PrecisionUtil.robustScale(inherentScale, safeScale)
} else if (typeof arguments[0] === 'number' && typeof arguments[1] === 'number') {
const inherentScale = arguments[0], safeScale = arguments[1]
if (inherentScale <= safeScale)
return inherentScale
return safeScale
}
}
}
static precisionScale(value, precisionDigits) {
const magnitude = Math.trunc(Math.log(value) / Math.log(10) + 1.0)
const precDigits = precisionDigits - magnitude
const scaleFactor = Math.pow(10.0, precDigits)
return scaleFactor
}
static safeScale() {
if (arguments.length === 1) {
if (typeof arguments[0] === 'number') {
const value = arguments[0]
return PrecisionUtil.precisionScale(value, PrecisionUtil.MAX_ROBUST_DP_DIGITS)
} else if (arguments[0] instanceof Geometry) {
const geom = arguments[0]
return PrecisionUtil.safeScale(PrecisionUtil.maxBoundMagnitude(geom.getEnvelopeInternal()))
}
} else if (arguments.length === 2) {
const a = arguments[0], b = arguments[1]
let maxBnd = PrecisionUtil.maxBoundMagnitude(a.getEnvelopeInternal())
if (b !== null) {
const maxBndB = PrecisionUtil.maxBoundMagnitude(b.getEnvelopeInternal())
maxBnd = Math.max(maxBnd, maxBndB)
}
const scale = PrecisionUtil.safeScale(maxBnd)
return scale
}
}
static inherentScale() {
if (arguments.length === 1) {
if (typeof arguments[0] === 'number') {
const value = arguments[0]
const numDec = PrecisionUtil.numberOfDecimals(value)
const scaleFactor = Math.pow(10.0, numDec)
return scaleFactor
} else if (arguments[0] instanceof Geometry) {
const geom = arguments[0]
const scaleFilter = new InherentScaleFilter()
geom.apply(scaleFilter)
return scaleFilter.getScale()
}
} else if (arguments.length === 2) {
const a = arguments[0], b = arguments[1]
let scale = PrecisionUtil.inherentScale(a)
if (b !== null) {
const scaleB = PrecisionUtil.inherentScale(b)
scale = Math.max(scale, scaleB)
}
return scale
}
}
static numberOfDecimals(value) {
const s = OrdinateFormat.DEFAULT.format(value)
if (s.endsWith('.0')) return 0
const len = s.length
const decIndex = s.indexOf('.')
if (decIndex <= 0) return 0
return len - decIndex - 1
}
static maxBoundMagnitude(env) {
return MathUtil.max(Math.abs(env.getMaxX()), Math.abs(env.getMaxY()), Math.abs(env.getMinX()), Math.abs(env.getMinY()))
}
static robustPM() {
if (arguments.length === 1) {
const a = arguments[0]
const scale = PrecisionUtil.robustScale(a)
return new PrecisionModel(scale)
} else if (arguments.length === 2) {
const a = arguments[0], b = arguments[1]
const scale = PrecisionUtil.robustScale(a, b)
return new PrecisionModel(scale)
}
}
}
class InherentScaleFilter {
constructor() {
InherentScaleFilter.constructor_.apply(this, arguments)
}
static constructor_() {
this._scale = 0
}
getScale() {
return this._scale
}
filter(coord) {
this.updateScaleMax(coord.getX())
this.updateScaleMax(coord.getY())
}
updateScaleMax(value) {
const scaleVal = PrecisionUtil.inherentScale(value)
if (scaleVal > this._scale)
this._scale = scaleVal
}
get interfaces_() {
return [CoordinateFilter]
}
}
PrecisionUtil.InherentScaleFilter = InherentScaleFilter
PrecisionUtil.MAX_ROBUST_DP_DIGITS = 14

View File

@ -1,103 +0,0 @@
import CoordinateList from '../../geom/CoordinateList.js'
import Coordinate from '../../geom/Coordinate.js'
export default class RingClipper {
constructor() {
RingClipper.constructor_.apply(this, arguments)
}
static constructor_() {
this._clipEnv = null
this._clipEnvMinY = null
this._clipEnvMaxY = null
this._clipEnvMinX = null
this._clipEnvMaxX = null
const clipEnv = arguments[0]
this._clipEnv = clipEnv
this._clipEnvMinY = clipEnv.getMinY()
this._clipEnvMaxY = clipEnv.getMaxY()
this._clipEnvMinX = clipEnv.getMinX()
this._clipEnvMaxX = clipEnv.getMaxX()
}
intersectionLineX(a, b, x) {
const m = (b.y - a.y) / (b.x - a.x)
const intercept = (x - a.x) * m
return a.y + intercept
}
clipToBoxEdge(pts, edgeIndex, closeRing) {
const ptsClip = new CoordinateList()
let p0 = pts[pts.length - 1]
for (let i = 0; i < pts.length; i++) {
const p1 = pts[i]
if (this.isInsideEdge(p1, edgeIndex)) {
if (!this.isInsideEdge(p0, edgeIndex)) {
const intPt = this.intersection(p0, p1, edgeIndex)
ptsClip.add(intPt, false)
}
ptsClip.add(p1.copy(), false)
} else if (this.isInsideEdge(p0, edgeIndex)) {
const intPt = this.intersection(p0, p1, edgeIndex)
ptsClip.add(intPt, false)
}
p0 = p1
}
if (closeRing && ptsClip.size() > 0) {
const start = ptsClip.get(0)
if (!start.equals2D(ptsClip.get(ptsClip.size() - 1)))
ptsClip.add(start.copy())
}
return ptsClip.toCoordinateArray()
}
intersection(a, b, edgeIndex) {
let intPt = null
switch (edgeIndex) {
case RingClipper.BOX_BOTTOM:
intPt = new Coordinate(this.intersectionLineY(a, b, this._clipEnvMinY), this._clipEnvMinY)
break
case RingClipper.BOX_RIGHT:
intPt = new Coordinate(this._clipEnvMaxX, this.intersectionLineX(a, b, this._clipEnvMaxX))
break
case RingClipper.BOX_TOP:
intPt = new Coordinate(this.intersectionLineY(a, b, this._clipEnvMaxY), this._clipEnvMaxY)
break
case RingClipper.BOX_LEFT:
default:
intPt = new Coordinate(this._clipEnvMinX, this.intersectionLineX(a, b, this._clipEnvMinX))
}
return intPt
}
intersectionLineY(a, b, y) {
const m = (b.x - a.x) / (b.y - a.y)
const intercept = (y - a.y) * m
return a.x + intercept
}
isInsideEdge(p, edgeIndex) {
let isInside = false
switch (edgeIndex) {
case RingClipper.BOX_BOTTOM:
isInside = p.y > this._clipEnvMinY
break
case RingClipper.BOX_RIGHT:
isInside = p.x < this._clipEnvMaxX
break
case RingClipper.BOX_TOP:
isInside = p.y < this._clipEnvMaxY
break
case RingClipper.BOX_LEFT:
default:
isInside = p.x > this._clipEnvMinX
}
return isInside
}
clip(pts) {
for (let edgeIndex = 0; edgeIndex < 4; edgeIndex++) {
const closeRing = edgeIndex === 3
pts = this.clipToBoxEdge(pts, edgeIndex, closeRing)
if (pts.length === 0) return pts
}
return pts
}
}
RingClipper.BOX_LEFT = 3
RingClipper.BOX_TOP = 2
RingClipper.BOX_RIGHT = 1
RingClipper.BOX_BOTTOM = 0

View File

@ -1,57 +0,0 @@
import Polygon from '../../geom/Polygon.js'
import GeometryCollection from '../../geom/GeometryCollection.js'
export default class RobustClipEnvelopeComputer {
constructor() {
RobustClipEnvelopeComputer.constructor_.apply(this, arguments)
}
static constructor_() {
this._targetEnv = null
this._clipEnv = null
const targetEnv = arguments[0]
this._targetEnv = targetEnv
this._clipEnv = targetEnv.copy()
}
static getEnvelope(a, b, targetEnv) {
const cec = new RobustClipEnvelopeComputer(targetEnv)
cec.add(a)
cec.add(b)
return cec.getEnvelope()
}
static intersectsSegment(env, p1, p2) {
return env.intersects(p1, p2)
}
addPolygon(poly) {
const shell = poly.getExteriorRing()
this.addPolygonRing(shell)
for (let i = 0; i < poly.getNumInteriorRing(); i++) {
const hole = poly.getInteriorRingN(i)
this.addPolygonRing(hole)
}
}
addSegment(p1, p2) {
if (RobustClipEnvelopeComputer.intersectsSegment(this._targetEnv, p1, p2)) {
this._clipEnv.expandToInclude(p1)
this._clipEnv.expandToInclude(p2)
}
}
getEnvelope() {
return this._clipEnv
}
addPolygonRing(ring) {
if (ring.isEmpty()) return null
const seq = ring.getCoordinateSequence()
for (let i = 1; i < seq.size(); i++)
this.addSegment(seq.getCoordinate(i - 1), seq.getCoordinate(i))
}
add(g) {
if (g === null || g.isEmpty()) return null
if (g instanceof Polygon) this.addPolygon(g); else if (g instanceof GeometryCollection) this.addCollection(g)
}
addCollection(gc) {
for (let i = 0; i < gc.getNumGeometries(); i++) {
const g = gc.getGeometryN(i)
this.add(g)
}
}
}

View File

@ -1,45 +0,0 @@
import Geometry from '../../geom/Geometry.js'
import hasInterface from '../../../../../hasInterface.js'
import UnionStrategy from '../union/UnionStrategy.js'
import Collection from '../../../../../java/util/Collection.js'
import OverlayNG from './OverlayNG.js'
import UnaryUnionOp from '../union/UnaryUnionOp.js'
import OverlayUtil from './OverlayUtil.js'
import PrecisionModel from '../../geom/PrecisionModel.js'
import UNION from './OverlayNG/UNION.js'
export default class UnaryUnionNG {
static union() {
if (arguments.length === 2) {
if (arguments[0] instanceof Geometry && arguments[1] instanceof PrecisionModel) {
const geom = arguments[0], pm = arguments[1]
const op = new UnaryUnionOp(geom)
op.setUnionFunction(UnaryUnionNG.createUnionStrategy(pm))
return op.union()
} else if (hasInterface(arguments[0], Collection) && arguments[1] instanceof PrecisionModel) {
const geoms = arguments[0], pm = arguments[1]
const op = new UnaryUnionOp(geoms)
op.setUnionFunction(UnaryUnionNG.createUnionStrategy(pm))
return op.union()
}
} else if (arguments.length === 3) {
const geoms = arguments[0], geomFact = arguments[1], pm = arguments[2]
const op = new UnaryUnionOp(geoms, geomFact)
op.setUnionFunction(UnaryUnionNG.createUnionStrategy(pm))
return op.union()
}
}
static createUnionStrategy(pm) {
const unionSRFun = new (class {
get interfaces_() {
return [UnionStrategy]
}
union(g0, g1) {
return OverlayNG.overlay(g0, g1, OverlayNG.UNION, pm)
}
isFloatingPrecision() {
return OverlayUtil.isFloating(pm)
}
})()
return unionSRFun
}
}

View File

@ -127,7 +127,7 @@ export default class RelateComputer {
const ei = eiIt.next()
const n = this._nodes.addNode(ei.coord)
if (eLoc === Location.BOUNDARY) n.setLabelBoundary(argIndex); else
if (n.getLabel().isNull(argIndex)) n.setLabel(argIndex, Location.INTERIOR)
if (n.getLabel().isNull(argIndex)) n.setLabel(argIndex, Location.INTERIOR)
}
}

View File

@ -40,7 +40,7 @@ export default class RelateNodeGraph {
const ei = eiIt.next()
const n = this._nodes.addNode(ei.coord)
if (eLoc === Location.BOUNDARY) n.setLabelBoundary(argIndex); else
if (n.getLabel().isNull(argIndex)) n.setLabel(argIndex, Location.INTERIOR)
if (n.getLabel().isNull(argIndex)) n.setLabel(argIndex, Location.INTERIOR)
}
}

View File

@ -1,4 +0,0 @@
export default class UnionStrategy {
union(g0, g1) {}
isFloatingPrecision() {}
}

View File

@ -1,41 +0,0 @@
import STRtree from '../../index/strtree/STRtree.js'
import PolygonTopologyAnalyzer from './PolygonTopologyAnalyzer.js'
export default class IndexedNestedHoleTester {
constructor() {
IndexedNestedHoleTester.constructor_.apply(this, arguments)
}
static constructor_() {
this._polygon = null
this._index = null
this._nestedPt = null
const poly = arguments[0]
this._polygon = poly
this.loadIndex()
}
loadIndex() {
this._index = new STRtree()
for (let i = 0; i < this._polygon.getNumInteriorRing(); i++) {
const hole = this._polygon.getInteriorRingN(i)
const env = hole.getEnvelopeInternal()
this._index.insert(env, hole)
}
}
getNestedPoint() {
return this._nestedPt
}
isNested() {
for (let i = 0; i < this._polygon.getNumInteriorRing(); i++) {
const hole = this._polygon.getInteriorRingN(i)
const results = this._index.query(hole.getEnvelopeInternal())
for (const testHole of results) {
if (hole === testHole) continue
if (!testHole.getEnvelopeInternal().covers(hole.getEnvelopeInternal())) continue
if (PolygonTopologyAnalyzer.isRingNested(hole, testHole)) {
this._nestedPt = hole.getCoordinateN(0)
return true
}
}
}
return false
}
}

View File

@ -1,82 +0,0 @@
import Location from '../../geom/Location.js'
import STRtree from '../../index/strtree/STRtree.js'
import PolygonTopologyAnalyzer from './PolygonTopologyAnalyzer.js'
import IndexedPointInAreaLocator from '../../algorithm/locate/IndexedPointInAreaLocator.js'
export default class IndexedNestedPolygonTester {
constructor() {
IndexedNestedPolygonTester.constructor_.apply(this, arguments)
}
static constructor_() {
this._multiPoly = null
this._index = null
this._locators = null
this._nestedPt = null
const multiPoly = arguments[0]
this._multiPoly = multiPoly
this.loadIndex()
}
static findIncidentSegmentNestedPoint(shell, poly) {
const polyShell = poly.getExteriorRing()
if (polyShell.isEmpty()) return null
if (!PolygonTopologyAnalyzer.isRingNested(shell, polyShell)) return null
for (let i = 0; i < poly.getNumInteriorRing(); i++) {
const hole = poly.getInteriorRingN(i)
if (hole.getEnvelopeInternal().covers(shell.getEnvelopeInternal()) && PolygonTopologyAnalyzer.isRingNested(shell, hole))
return null
}
return shell.getCoordinateN(0)
}
getNestedPoint() {
return this._nestedPt
}
loadIndex() {
this._index = new STRtree()
for (let i = 0; i < this._multiPoly.getNumGeometries(); i++) {
const poly = this._multiPoly.getGeometryN(i)
const env = poly.getEnvelopeInternal()
this._index.insert(env, i)
}
}
isNested() {
for (let i = 0; i < this._multiPoly.getNumGeometries(); i++) {
const poly = this._multiPoly.getGeometryN(i)
const shell = poly.getExteriorRing()
const results = this._index.query(poly.getEnvelopeInternal())
for (const polyIndex of results) {
const possibleOuterPoly = this._multiPoly.getGeometryN(polyIndex)
if (poly === possibleOuterPoly) continue
if (!possibleOuterPoly.getEnvelopeInternal().covers(poly.getEnvelopeInternal())) continue
this._nestedPt = this.findNestedPoint(shell, possibleOuterPoly, this.getLocator(polyIndex))
if (this._nestedPt !== null) return true
}
}
return false
}
getLocator(polyIndex) {
if (this._locators === null)
this._locators = new Array(this._multiPoly.getNumGeometries()).fill(null)
let locator = this._locators[polyIndex]
if (locator === null) {
locator = new IndexedPointInAreaLocator(this._multiPoly.getGeometryN(polyIndex))
this._locators[polyIndex] = locator
}
return locator
}
findNestedPoint(shell, possibleOuterPoly, locator) {
const shellPt0 = shell.getCoordinateN(0)
const loc0 = locator.locate(shellPt0)
if (loc0 === Location.EXTERIOR) return null
if (loc0 === Location.INTERIOR)
return shellPt0
const shellPt1 = shell.getCoordinateN(1)
const loc1 = locator.locate(shellPt1)
if (loc1 === Location.EXTERIOR) return null
if (loc1 === Location.INTERIOR)
return shellPt1
return IndexedNestedPolygonTester.findIncidentSegmentNestedPoint(shell, possibleOuterPoly)
}
}

View File

@ -1,232 +0,0 @@
import BasicSegmentString from '../../noding/BasicSegmentString.js'
import LineString from '../../geom/LineString.js'
import HashSet from '../../../../../java/util/HashSet.js'
import hasInterface from '../../../../../hasInterface.js'
import MCIndexNoder from '../../noding/MCIndexNoder.js'
import Point from '../../geom/Point.js'
import MultiPoint from '../../geom/MultiPoint.js'
import BoundaryNodeRule from '../../algorithm/BoundaryNodeRule.js'
import SegmentIntersector from '../../noding/SegmentIntersector.js'
import GeometryCollection from '../../geom/GeometryCollection.js'
import CoordinateArrays from '../../geom/CoordinateArrays.js'
import Polygonal from '../../geom/Polygonal.js'
import ArrayList from '../../../../../java/util/ArrayList.js'
import RobustLineIntersector from '../../algorithm/RobustLineIntersector.js'
import LinearComponentExtracter from '../../geom/util/LinearComponentExtracter.js'
import MultiLineString from '../../geom/MultiLineString.js'
export default class IsSimpleOp {
constructor() {
IsSimpleOp.constructor_.apply(this, arguments)
}
static constructor_() {
this._inputGeom = null
this._isClosedEndpointsInInterior = null
this._isFindAllLocations = null
this._isSimple = false
this._nonSimplePts = null
if (arguments.length === 1) {
const geom = arguments[0]
IsSimpleOp.constructor_.call(this, geom, BoundaryNodeRule.MOD2_BOUNDARY_RULE)
} else if (arguments.length === 2) {
const geom = arguments[0], boundaryNodeRule = arguments[1]
this._inputGeom = geom
this._isClosedEndpointsInInterior = !boundaryNodeRule.isInBoundary(2)
}
}
static isSimple(geom) {
const op = new IsSimpleOp(geom)
return op.isSimple()
}
static getNonSimpleLocation(geom) {
const op = new IsSimpleOp(geom)
return op.getNonSimpleLocation()
}
static extractSegmentStrings(geom) {
const segStrings = new ArrayList()
for (let i = 0; i < geom.getNumGeometries(); i++) {
const line = geom.getGeometryN(i)
const trimPts = IsSimpleOp.trimRepeatedPoints(line.getCoordinates())
if (trimPts !== null) {
const ss = new BasicSegmentString(trimPts, null)
segStrings.add(ss)
}
}
return segStrings
}
static trimRepeatedPoints(pts) {
if (pts.length <= 2) return pts
const len = pts.length
const hasRepeatedStart = pts[0].equals2D(pts[1])
const hasRepeatedEnd = pts[len - 1].equals2D(pts[len - 2])
if (!hasRepeatedStart && !hasRepeatedEnd) return pts
let startIndex = 0
const startPt = pts[0]
while (startIndex < len - 1 && startPt.equals2D(pts[startIndex + 1]))
startIndex++
let endIndex = len - 1
const endPt = pts[endIndex]
while (endIndex > 0 && endPt.equals2D(pts[endIndex - 1]))
endIndex--
if (endIndex - startIndex < 1)
return null
const trimPts = CoordinateArrays.extract(pts, startIndex, endIndex)
return trimPts
}
isSimpleMultiPoint(mp) {
if (mp.isEmpty()) return true
let isSimple = true
const points = new HashSet()
for (let i = 0; i < mp.getNumGeometries(); i++) {
const pt = mp.getGeometryN(i)
const p = pt.getCoordinate()
if (points.contains(p)) {
this._nonSimplePts.add(p)
isSimple = false
if (!this._isFindAllLocations) break
} else {
points.add(p)
}
}
return isSimple
}
isSimplePolygonal(geom) {
let isSimple = true
const rings = LinearComponentExtracter.getLines(geom)
for (const ring of rings)
if (!this.isSimpleLinearGeometry(ring)) {
isSimple = false
if (!this._isFindAllLocations) break
}
return isSimple
}
compute() {
if (this._nonSimplePts !== null) return null
this._nonSimplePts = new ArrayList()
this._isSimple = this.computeSimple(this._inputGeom)
}
getNonSimpleLocation() {
this.compute()
if (this._nonSimplePts.size() === 0) return null
return this._nonSimplePts.get(0)
}
getNonSimpleLocations() {
this.compute()
return this._nonSimplePts
}
isSimpleLinearGeometry(geom) {
if (geom.isEmpty()) return true
const segStrings = IsSimpleOp.extractSegmentStrings(geom)
const segInt = new NonSimpleIntersectionFinder(this._isClosedEndpointsInInterior, this._isFindAllLocations, this._nonSimplePts)
const noder = new MCIndexNoder()
noder.setSegmentIntersector(segInt)
noder.computeNodes(segStrings)
if (segInt.hasIntersection())
return false
return true
}
setFindAllLocations(isFindAll) {
this._isFindAllLocations = isFindAll
}
computeSimple(geom) {
if (geom.isEmpty()) return true
if (geom instanceof Point) return true
if (geom instanceof LineString) return this.isSimpleLinearGeometry(geom)
if (geom instanceof MultiLineString) return this.isSimpleLinearGeometry(geom)
if (geom instanceof MultiPoint) return this.isSimpleMultiPoint(geom)
if (hasInterface(geom, Polygonal)) return this.isSimplePolygonal(geom)
if (geom instanceof GeometryCollection) return this.isSimpleGeometryCollection(geom)
return true
}
isSimple() {
this.compute()
return this._isSimple
}
isSimpleGeometryCollection(geom) {
let isSimple = true
for (let i = 0; i < geom.getNumGeometries(); i++) {
const comp = geom.getGeometryN(i)
if (!this.computeSimple(comp)) {
isSimple = false
if (!this._isFindAllLocations) break
}
}
return isSimple
}
}
class NonSimpleIntersectionFinder {
constructor() {
NonSimpleIntersectionFinder.constructor_.apply(this, arguments)
}
static constructor_() {
this._isClosedEndpointsInInterior = null
this._isFindAll = null
this.li = new RobustLineIntersector()
this._intersectionPts = null
const isClosedEndpointsInInterior = arguments[0], isFindAll = arguments[1], intersectionPts = arguments[2]
this._isClosedEndpointsInInterior = isClosedEndpointsInInterior
this._isFindAll = isFindAll
this._intersectionPts = intersectionPts
}
static isIntersectionEndpoint(ss, ssIndex, li, liSegmentIndex) {
const vertexIndex = NonSimpleIntersectionFinder.intersectionVertexIndex(li, liSegmentIndex)
if (vertexIndex === 0)
return ssIndex === 0
else
return ssIndex + 2 === ss.size()
}
static intersectionVertexIndex(li, segmentIndex) {
const intPt = li.getIntersection(0)
const endPt0 = li.getEndpoint(segmentIndex, 0)
return intPt.equals2D(endPt0) ? 0 : 1
}
hasIntersection() {
return this._intersectionPts.size() > 0
}
processIntersections(ss0, segIndex0, ss1, segIndex1) {
const isSameSegString = ss0 === ss1
const isSameSegment = isSameSegString && segIndex0 === segIndex1
if (isSameSegment) return null
const hasInt = this.findIntersection(ss0, segIndex0, ss1, segIndex1)
if (hasInt)
this._intersectionPts.add(this.li.getIntersection(0))
}
findIntersection(ss0, segIndex0, ss1, segIndex1) {
const p00 = ss0.getCoordinate(segIndex0)
const p01 = ss0.getCoordinate(segIndex0 + 1)
const p10 = ss1.getCoordinate(segIndex1)
const p11 = ss1.getCoordinate(segIndex1 + 1)
this.li.computeIntersection(p00, p01, p10, p11)
if (!this.li.hasIntersection()) return false
const hasInteriorInt = this.li.isInteriorIntersection()
if (hasInteriorInt) return true
const hasEqualSegments = this.li.getIntersectionNum() >= 2
if (hasEqualSegments) return true
const isSameSegString = ss0 === ss1
const isAdjacentSegment = isSameSegString && Math.abs(segIndex1 - segIndex0) <= 1
if (isAdjacentSegment) return false
const isIntersectionEndpt0 = NonSimpleIntersectionFinder.isIntersectionEndpoint(ss0, segIndex0, this.li, 0)
const isIntersectionEndpt1 = NonSimpleIntersectionFinder.isIntersectionEndpoint(ss1, segIndex1, this.li, 1)
const hasInteriorVertexInt = !(isIntersectionEndpt0 && isIntersectionEndpt1)
if (hasInteriorVertexInt) return true
if (this._isClosedEndpointsInInterior && !isSameSegString) {
const hasInteriorEndpointInt = ss0.isClosed() || ss1.isClosed()
if (hasInteriorEndpointInt) return true
}
return false
}
isDone() {
if (this._isFindAll) return false
return this._intersectionPts.size() > 0
}
get interfaces_() {
return [SegmentIntersector]
}
}
IsSimpleOp.NonSimpleIntersectionFinder = NonSimpleIntersectionFinder

View File

@ -1,122 +0,0 @@
import PolygonNodeTopology from '../../algorithm/PolygonNodeTopology.js'
import SegmentIntersector from '../../noding/SegmentIntersector.js'
import PolygonRing from './PolygonRing.js'
import RobustLineIntersector from '../../algorithm/RobustLineIntersector.js'
import TopologyValidationError from './TopologyValidationError.js'
import IllegalStateException from '../../../../../java/lang/IllegalStateException.js'
export default class PolygonIntersectionAnalyzer {
constructor() {
PolygonIntersectionAnalyzer.constructor_.apply(this, arguments)
}
static constructor_() {
this._isInvertedRingValid = null
this._li = new RobustLineIntersector()
this._invalidCode = PolygonIntersectionAnalyzer.NO_INVALID_INTERSECTION
this._invalidLocation = null
this._hasDoubleTouch = false
this._doubleTouchLocation = null
const isInvertedRingValid = arguments[0]
this._isInvertedRingValid = isInvertedRingValid
}
static prevCoordinateInRing(ringSS, segIndex) {
let prevIndex = segIndex - 1
if (prevIndex < 0)
prevIndex = ringSS.size() - 2
return ringSS.getCoordinate(prevIndex)
}
static isAdjacentInRing(ringSS, segIndex0, segIndex1) {
const delta = Math.abs(segIndex1 - segIndex0)
if (delta <= 1) return true
if (delta >= ringSS.size() - 2) return true
return false
}
getDoubleTouchLocation() {
return this._doubleTouchLocation
}
hasDoubleTouch() {
return this._hasDoubleTouch
}
addSelfTouch(ss, intPt, e00, e01, e10, e11) {
const polyRing = ss.getData()
if (polyRing === null)
throw new IllegalStateException('SegmentString missing PolygonRing data when checking self-touches')
polyRing.addSelfTouch(intPt, e00, e01, e10, e11)
}
findInvalidIntersection(ss0, segIndex0, ss1, segIndex1) {
const p00 = ss0.getCoordinate(segIndex0)
const p01 = ss0.getCoordinate(segIndex0 + 1)
const p10 = ss1.getCoordinate(segIndex1)
const p11 = ss1.getCoordinate(segIndex1 + 1)
this._li.computeIntersection(p00, p01, p10, p11)
if (!this._li.hasIntersection())
return PolygonIntersectionAnalyzer.NO_INVALID_INTERSECTION
const isSameSegString = ss0 === ss1
if (this._li.isProper() || this._li.getIntersectionNum() >= 2)
return TopologyValidationError.SELF_INTERSECTION
const intPt = this._li.getIntersection(0)
const isAdjacentSegments = isSameSegString && PolygonIntersectionAnalyzer.isAdjacentInRing(ss0, segIndex0, segIndex1)
if (isAdjacentSegments) return PolygonIntersectionAnalyzer.NO_INVALID_INTERSECTION
if (isSameSegString && !this._isInvertedRingValid)
return TopologyValidationError.RING_SELF_INTERSECTION
if (intPt.equals2D(p01) || intPt.equals2D(p11)) return PolygonIntersectionAnalyzer.NO_INVALID_INTERSECTION
let e00 = p00
let e01 = p01
if (intPt.equals2D(p00)) {
e00 = PolygonIntersectionAnalyzer.prevCoordinateInRing(ss0, segIndex0)
e01 = p01
}
let e10 = p10
let e11 = p11
if (intPt.equals2D(p10)) {
e10 = PolygonIntersectionAnalyzer.prevCoordinateInRing(ss1, segIndex1)
e11 = p11
}
const hasCrossing = PolygonNodeTopology.isCrossing(intPt, e00, e01, e10, e11)
if (hasCrossing)
return TopologyValidationError.SELF_INTERSECTION
if (isSameSegString && this._isInvertedRingValid)
this.addSelfTouch(ss0, intPt, e00, e01, e10, e11)
const isDoubleTouch = this.addDoubleTouch(ss0, ss1, intPt)
if (isDoubleTouch && !isSameSegString) {
this._hasDoubleTouch = true
this._doubleTouchLocation = intPt
}
return PolygonIntersectionAnalyzer.NO_INVALID_INTERSECTION
}
processIntersections(ss0, segIndex0, ss1, segIndex1) {
const isSameSegString = ss0 === ss1
const isSameSegment = isSameSegString && segIndex0 === segIndex1
if (isSameSegment) return null
const code = this.findInvalidIntersection(ss0, segIndex0, ss1, segIndex1)
if (code !== PolygonIntersectionAnalyzer.NO_INVALID_INTERSECTION) {
this._invalidCode = code
this._invalidLocation = this._li.getIntersection(0)
}
}
isDone() {
return this.isInvalid() || this._hasDoubleTouch
}
addDoubleTouch(ss0, ss1, intPt) {
return PolygonRing.addTouch(ss0.getData(), ss1.getData(), intPt)
}
isInvalid() {
return this._invalidCode >= 0
}
getInvalidLocation() {
return this._invalidLocation
}
getInvalidCode() {
return this._invalidCode
}
get interfaces_() {
return [SegmentIntersector]
}
}
PolygonIntersectionAnalyzer.NO_INVALID_INTERSECTION = -1

View File

@ -1,196 +0,0 @@
import ArrayDeque from '../../../../../java/util/ArrayDeque.js'
import HashMap from '../../../../../java/util/HashMap.js'
import PolygonNodeTopology from '../../algorithm/PolygonNodeTopology.js'
import Orientation from '../../algorithm/Orientation.js'
import ArrayList from '../../../../../java/util/ArrayList.js'
export default class PolygonRing {
constructor() {
PolygonRing.constructor_.apply(this, arguments)
}
static constructor_() {
this._id = null
this._shell = null
this._ring = null
this._touchSetRoot = null
this._touches = null
this._selfNodes = null
if (arguments.length === 1) {
const ring = arguments[0]
this._ring = ring
this._id = -1
this._shell = this
} else if (arguments.length === 3) {
const ring = arguments[0], index = arguments[1], shell = arguments[2]
this._ring = ring
this._id = index
this._shell = shell
}
}
static findInteriorSelfNode(polyRings) {
for (const polyRing of polyRings) {
const interiorSelfNode = polyRing.findInteriorSelfNode()
if (interiorSelfNode !== null)
return interiorSelfNode
}
return null
}
static addTouch(ring0, ring1, pt) {
if (ring0 === null || ring1 === null) return false
if (!ring0.isSamePolygon(ring1)) return false
if (!ring0.isOnlyTouch(ring1, pt)) return true
if (!ring1.isOnlyTouch(ring0, pt)) return true
ring0.addTouch(ring1, pt)
ring1.addTouch(ring0, pt)
return false
}
static isShell(polyRing) {
if (polyRing === null) return true
return polyRing.isShell()
}
static findHoleCycleLocation(polyRings) {
for (const polyRing of polyRings)
if (!polyRing.isInTouchSet()) {
const holeCycleLoc = polyRing.findHoleCycleLocation()
if (holeCycleLoc !== null) return holeCycleLoc
}
return null
}
static init(root, touchStack) {
for (const touch of root.getTouches()) {
touch.getRing().setTouchSetRoot(root)
touchStack.push(touch)
}
}
scanForHoleCycle(currentTouch, root, touchStack) {
const ring = currentTouch.getRing()
const currentPt = currentTouch.getCoordinate()
for (const touch of ring.getTouches()) {
if (currentPt.equals2D(touch.getCoordinate())) continue
const touchRing = touch.getRing()
if (touchRing.getTouchSetRoot() === root) return touch.getCoordinate()
touchRing.setTouchSetRoot(root)
touchStack.push(touch)
}
return null
}
findInteriorSelfNode() {
if (this._selfNodes === null) return null
const isCCW = Orientation.isCCW(this._ring.getCoordinates())
const isInteriorOnRight = this.isShell() ^ isCCW
for (const selfNode of this._selfNodes)
if (!selfNode.isExterior(isInteriorOnRight))
return selfNode.getCoordinate()
return null
}
addTouch(ring, pt) {
if (this._touches === null)
this._touches = new HashMap()
const touch = this._touches.get(ring._id)
if (touch === null)
this._touches.put(ring._id, new PolygonRingTouch(ring, pt))
}
addSelfTouch(origin, e00, e01, e10, e11) {
if (this._selfNodes === null)
this._selfNodes = new ArrayList()
this._selfNodes.add(new PolygonRingSelfNode(origin, e00, e01, e10, e11))
}
isOnlyTouch(ring, pt) {
if (this._touches === null) return true
const touch = this._touches.get(ring._id)
if (touch === null) return true
return touch.isAtLocation(pt)
}
getTouches() {
return this._touches.values()
}
isShell() {
return this._shell === this
}
hasTouches() {
return this._touches !== null && !this._touches.isEmpty()
}
setTouchSetRoot(ring) {
this._touchSetRoot = ring
}
findHoleCycleLocation() {
if (this.isInTouchSet()) return null
const root = this
root.setTouchSetRoot(root)
if (!this.hasTouches()) return null
const touchStack = new ArrayDeque()
PolygonRing.init(root, touchStack)
while (!touchStack.isEmpty()) {
const touch = touchStack.pop()
const holeCyclePt = this.scanForHoleCycle(touch, root, touchStack)
if (holeCyclePt !== null)
return holeCyclePt
}
return null
}
toString() {
return this._ring.toString()
}
isInTouchSet() {
return this._touchSetRoot !== null
}
isSamePolygon(ring) {
return this._shell === ring._shell
}
getTouchSetRoot() {
return this._touchSetRoot
}
}
class PolygonRingTouch {
constructor() {
PolygonRingTouch.constructor_.apply(this, arguments)
}
static constructor_() {
this._ring = null
this._touchPt = null
const ring = arguments[0], pt = arguments[1]
this._ring = ring
this._touchPt = pt
}
getCoordinate() {
return this._touchPt
}
getRing() {
return this._ring
}
isAtLocation(pt) {
return this._touchPt.equals2D(pt)
}
}
class PolygonRingSelfNode {
constructor() {
PolygonRingSelfNode.constructor_.apply(this, arguments)
}
static constructor_() {
this._nodePt = null
this._e00 = null
this._e01 = null
this._e10 = null
const nodePt = arguments[0], e00 = arguments[1], e01 = arguments[2], e10 = arguments[3], e11 = arguments[4]
this._nodePt = nodePt
this._e00 = e00
this._e01 = e01
this._e10 = e10
}
getCoordinate() {
return this._nodePt
}
isExterior(isInteriorOnRight) {
const isInteriorSeg = PolygonNodeTopology.isInteriorSegment(this._nodePt, this._e00, this._e01, this._e10)
const isExterior = isInteriorOnRight ? !isInteriorSeg : isInteriorSeg
return isExterior
}
}

View File

@ -1,205 +0,0 @@
import BasicSegmentString from '../../noding/BasicSegmentString.js'
import Location from '../../geom/Location.js'
import PolygonIntersectionAnalyzer from './PolygonIntersectionAnalyzer.js'
import PolygonNodeTopology from '../../algorithm/PolygonNodeTopology.js'
import IllegalArgumentException from '../../../../../java/lang/IllegalArgumentException.js'
import MCIndexNoder from '../../noding/MCIndexNoder.js'
import PointLocation from '../../algorithm/PointLocation.js'
import LinearRing from '../../geom/LinearRing.js'
import Orientation from '../../algorithm/Orientation.js'
import CoordinateArrays from '../../geom/CoordinateArrays.js'
import ArrayList from '../../../../../java/util/ArrayList.js'
import PolygonRing from './PolygonRing.js'
import RobustLineIntersector from '../../algorithm/RobustLineIntersector.js'
export default class PolygonTopologyAnalyzer {
constructor() {
PolygonTopologyAnalyzer.constructor_.apply(this, arguments)
}
static constructor_() {
this._isInvertedRingValid = null
this._intFinder = null
this._polyRings = null
this._disconnectionPt = null
const geom = arguments[0], isInvertedRingValid = arguments[1]
this._isInvertedRingValid = isInvertedRingValid
this.analyze(geom)
}
static ringIndexPrev(ringPts, index) {
if (index === 0) return ringPts.length - 2
return index - 1
}
static findNonEqualVertex(ring, p) {
let i = 1
let next = ring.getCoordinateN(i)
while (next.equals2D(p) && i < ring.getNumPoints() - 1) {
i += 1
next = ring.getCoordinateN(i)
}
return next
}
static ringIndexNext(ringPts, index) {
if (index >= ringPts.length - 2) return 0
return index + 1
}
static createSegmentStrings(geom, isInvertedRingValid) {
const segStrings = new ArrayList()
if (geom instanceof LinearRing) {
const ring = geom
segStrings.add(PolygonTopologyAnalyzer.createSegString(ring, null))
return segStrings
}
for (let i = 0; i < geom.getNumGeometries(); i++) {
const poly = geom.getGeometryN(i)
if (poly.isEmpty()) continue
const hasHoles = poly.getNumInteriorRing() > 0
let shellRing = null
if (hasHoles || isInvertedRingValid)
shellRing = new PolygonRing(poly.getExteriorRing())
segStrings.add(PolygonTopologyAnalyzer.createSegString(poly.getExteriorRing(), shellRing))
for (let j = 0; j < poly.getNumInteriorRing(); j++) {
const hole = poly.getInteriorRingN(j)
if (hole.isEmpty()) continue
const holeRing = new PolygonRing(hole, j, shellRing)
segStrings.add(PolygonTopologyAnalyzer.createSegString(hole, holeRing))
}
}
return segStrings
}
static isIncidentSegmentInRing(p0, p1, ringPts) {
const index = PolygonTopologyAnalyzer.intersectingSegIndex(ringPts, p0)
if (index < 0)
throw new IllegalArgumentException('Segment vertex does not intersect ring')
let rPrev = PolygonTopologyAnalyzer.findRingVertexPrev(ringPts, index, p0)
let rNext = PolygonTopologyAnalyzer.findRingVertexNext(ringPts, index, p0)
const isInteriorOnRight = !Orientation.isCCW(ringPts)
if (!isInteriorOnRight) {
const temp = rPrev
rPrev = rNext
rNext = temp
}
return PolygonNodeTopology.isInteriorSegment(p0, rPrev, rNext, p1)
}
static intersectingSegIndex(ringPts, pt) {
const li = new RobustLineIntersector()
for (let i = 0; i < ringPts.length - 1; i++) {
li.computeIntersection(pt, ringPts[i], ringPts[i + 1])
if (li.hasIntersection()) {
if (pt.equals2D(ringPts[i + 1]))
return i + 1
return i
}
}
return -1
}
static findRingVertexNext(ringPts, index, node) {
let iNext = index + 1
let next = ringPts[iNext]
while (node.equals2D(next)) {
iNext = PolygonTopologyAnalyzer.ringIndexNext(ringPts, iNext)
next = ringPts[iNext]
}
return next
}
static getPolygonRings(segStrings) {
let polyRings = null
for (const ss of segStrings) {
const polyRing = ss.getData()
if (polyRing !== null) {
if (polyRings === null)
polyRings = new ArrayList()
polyRings.add(polyRing)
}
}
return polyRings
}
static createSegString(ring, polyRing) {
let pts = ring.getCoordinates()
if (CoordinateArrays.hasRepeatedPoints(pts))
pts = CoordinateArrays.removeRepeatedPoints(pts)
const ss = new BasicSegmentString(pts, polyRing)
return ss
}
static findSelfIntersection(ring) {
const ata = new PolygonTopologyAnalyzer(ring, false)
if (ata.hasInvalidIntersection()) return ata.getInvalidLocation()
return null
}
static isRingNested(test, target) {
const p0 = test.getCoordinateN(0)
const targetPts = target.getCoordinates()
const loc = PointLocation.locateInRing(p0, targetPts)
if (loc === Location.EXTERIOR) return false
if (loc === Location.INTERIOR) return true
const p1 = PolygonTopologyAnalyzer.findNonEqualVertex(test, p0)
return PolygonTopologyAnalyzer.isIncidentSegmentInRing(p0, p1, targetPts)
}
static findRingVertexPrev(ringPts, index, node) {
let iPrev = index
let prev = ringPts[iPrev]
while (node.equals2D(prev)) {
iPrev = PolygonTopologyAnalyzer.ringIndexPrev(ringPts, iPrev)
prev = ringPts[iPrev]
}
return prev
}
analyze(geom) {
if (geom.isEmpty()) return null
const segStrings = PolygonTopologyAnalyzer.createSegmentStrings(geom, this._isInvertedRingValid)
this._polyRings = PolygonTopologyAnalyzer.getPolygonRings(segStrings)
this._intFinder = this.analyzeIntersections(segStrings)
if (this._intFinder.hasDoubleTouch()) {
this._disconnectionPt = this._intFinder.getDoubleTouchLocation()
return null
}
}
checkInteriorDisconnectedByHoleCycle() {
if (this._polyRings !== null)
this._disconnectionPt = PolygonRing.findHoleCycleLocation(this._polyRings)
}
getDisconnectionLocation() {
return this._disconnectionPt
}
hasInvalidIntersection() {
return this._intFinder.isInvalid()
}
analyzeIntersections(segStrings) {
const segInt = new PolygonIntersectionAnalyzer(this._isInvertedRingValid)
const noder = new MCIndexNoder()
noder.setSegmentIntersector(segInt)
noder.computeNodes(segStrings)
return segInt
}
isInteriorDisconnected() {
if (this._disconnectionPt !== null)
return true
if (this._isInvertedRingValid) {
this.checkInteriorDisconnectedBySelfTouch()
if (this._disconnectionPt !== null)
return true
}
this.checkInteriorDisconnectedByHoleCycle()
if (this._disconnectionPt !== null)
return true
return false
}
checkInteriorDisconnectedBySelfTouch() {
if (this._polyRings !== null)
this._disconnectionPt = PolygonRing.findInteriorSelfNode(this._polyRings)
}
getInvalidLocation() {
return this._intFinder.getInvalidLocation()
}
getInvalidCode() {
return this._intFinder.getInvalidCode()
}
}

View File

@ -1,30 +0,0 @@
import GeometryTransformer from '../geom/util/GeometryTransformer.js'
export default class PointwisePrecisionReducerTransformer extends GeometryTransformer {
constructor() {
super()
PointwisePrecisionReducerTransformer.constructor_.apply(this, arguments)
}
static constructor_() {
this._targetPM = null
const targetPM = arguments[0]
this._targetPM = targetPM
}
static reduce(geom, targetPM) {
const trans = new PointwisePrecisionReducerTransformer(targetPM)
return trans.transform(geom)
}
transformCoordinates(coordinates, parent) {
if (coordinates.size() === 0) return null
const coordsReduce = this.reducePointwise(coordinates)
return this._factory.getCoordinateSequenceFactory().create(coordsReduce)
}
reducePointwise(coordinates) {
const coordReduce = new Array(coordinates.size()).fill(null)
for (let i = 0; i < coordinates.size(); i++) {
const coord = coordinates.getCoordinate(i).copy()
this._targetPM.makePrecise(coord)
coordReduce[i] = coord
}
return coordReduce
}
}

View File

@ -1,65 +0,0 @@
import LineString from '../geom/LineString.js'
import CoordinateList from '../geom/CoordinateList.js'
import GeometryTransformer from '../geom/util/GeometryTransformer.js'
import LinearRing from '../geom/LinearRing.js'
import PrecisionReducer from '../operation/overlayng/PrecisionReducer.js'
export default class PrecisionReducerTransformer extends GeometryTransformer {
constructor() {
super()
PrecisionReducerTransformer.constructor_.apply(this, arguments)
}
static constructor_() {
this._targetPM = null
this._isRemoveCollapsed = false
const targetPM = arguments[0], isRemoveCollapsed = arguments[1]
this._targetPM = targetPM
this._isRemoveCollapsed = isRemoveCollapsed
}
static reduce(geom, targetPM, isRemoveCollapsed) {
const trans = new PrecisionReducerTransformer(targetPM, isRemoveCollapsed)
return trans.transform(geom)
}
transformPolygon(geom, parent) {
return this.reduceArea(geom)
}
reduceCompress(coordinates) {
const noRepeatCoordList = new CoordinateList()
for (let i = 0; i < coordinates.size(); i++) {
const coord = coordinates.getCoordinate(i).copy()
this._targetPM.makePrecise(coord)
noRepeatCoordList.add(coord, false)
}
const noRepeatCoords = noRepeatCoordList.toCoordinateArray()
return noRepeatCoords
}
transformCoordinates(coordinates, parent) {
if (coordinates.size() === 0) return null
let coordsReduce = this.reduceCompress(coordinates)
let minSize = 0
if (parent instanceof LineString) minSize = 2
if (parent instanceof LinearRing) minSize = LinearRing.MINIMUM_VALID_SIZE
if (coordsReduce.length < minSize) {
if (this._isRemoveCollapsed)
return null
coordsReduce = this.extend(coordsReduce, minSize)
}
return this._factory.getCoordinateSequenceFactory().create(coordsReduce)
}
extend(coords, minLength) {
if (coords.length >= minLength) return coords
const exCoords = new Array(minLength).fill(null)
for (let i = 0; i < exCoords.length; i++) {
const iSrc = i < coords.length ? i : coords.length - 1
exCoords[i] = coords[iSrc].copy()
}
return exCoords
}
transformMultiPolygon(geom, parent) {
return this.reduceArea(geom)
}
reduceArea(geom) {
const reduced = PrecisionReducer.reducePrecision(geom, this._targetPM)
return reduced
}
}

View File

@ -1,250 +0,0 @@
import LineString from '../geom/LineString.js'
import CoordinateList from '../geom/CoordinateList.js'
import Geometry from '../geom/Geometry.js'
import Coordinate from '../geom/Coordinate.js'
import IllegalArgumentException from '../../../../java/lang/IllegalArgumentException.js'
import Polygon from '../geom/Polygon.js'
import GeometryMapper from '../geom/util/GeometryMapper.js'
import Angle from '../algorithm/Angle.js'
export default class CubicBezierCurve {
constructor() {
CubicBezierCurve.constructor_.apply(this, arguments)
}
static constructor_() {
this._minSegmentLength = 0.0
this._numVerticesPerSegment = 16
this._inputGeom = null
this._alpha = -1
this._skew = 0
this._controlPoints = null
this._geomFactory = null
this._bezierCurvePts = null
this._interpolationParam = null
this._controlPointIndex = 0
if (arguments.length === 2) {
if (arguments[0] instanceof Geometry && arguments[1] instanceof Geometry) {
const geom = arguments[0], controlPoints = arguments[1]
this._inputGeom = geom
this._geomFactory = geom.getFactory()
this._controlPoints = controlPoints
} else if (arguments[0] instanceof Geometry && typeof arguments[1] === 'number') {
let geom = arguments[0], alpha = arguments[1]
this._inputGeom = geom
this._geomFactory = geom.getFactory()
if (alpha < 0.0) alpha = 0
this._alpha = alpha
}
} else if (arguments.length === 3) {
let geom = arguments[0], alpha = arguments[1], skew = arguments[2]
this._inputGeom = geom
this._geomFactory = geom.getFactory()
if (alpha < 0.0) alpha = 0
this._alpha = alpha
this._skew = skew
}
}
static computeIterpolationParameters(n) {
const param = Array(n).fill().map(() => Array(4))
for (let i = 0; i < n; i++) {
const t = i / (n - 1)
const tc = 1.0 - t
param[i][0] = tc * tc * tc
param[i][1] = 3.0 * tc * tc * t
param[i][2] = 3.0 * tc * t * t
param[i][3] = t * t * t
}
return param
}
static mirrorControlPoint(c, p0, p1) {
const vlinex = p1.x - p0.x
const vliney = p1.y - p0.y
const vrotx = -vliney
const vroty = vlinex
const midx = (p0.x + p1.x) / 2
const midy = (p0.y + p1.y) / 2
return CubicBezierCurve.reflectPointInLine(c, new Coordinate(midx, midy), new Coordinate(midx + vrotx, midy + vroty))
}
static bezierCurve() {
if (arguments.length === 2) {
if (arguments[0] instanceof Geometry && arguments[1] instanceof Geometry) {
const geom = arguments[0], controlPoints = arguments[1]
const curve = new CubicBezierCurve(geom, controlPoints)
return curve.getResult()
} else if (arguments[0] instanceof Geometry && typeof arguments[1] === 'number') {
const geom = arguments[0], alpha = arguments[1]
const curve = new CubicBezierCurve(geom, alpha)
return curve.getResult()
}
} else if (arguments.length === 3) {
const geom = arguments[0], alpha = arguments[1], skew = arguments[2]
const curve = new CubicBezierCurve(geom, alpha, skew)
return curve.getResult()
}
}
static reflectPointInLine(p, p0, p1) {
const vx = p1.x - p0.x
const vy = p1.y - p0.y
const x = p0.x - p.x
const y = p0.y - p.y
const r = 1 / (vx * vx + vy * vy)
const rx = p.x + 2 * (x - x * vx * vx * r - y * vx * vy * r)
const ry = p.y + 2 * (y - y * vy * vy * r - x * vx * vy * r)
return new Coordinate(rx, ry)
}
static aimedControlPoint(c, p1, p0) {
const len = p1.distance(c)
const ang = Angle.angle(p0, p1)
return Angle.project(p0, ang, len)
}
getResult() {
this._bezierCurvePts = new Array(this._numVerticesPerSegment).fill(null)
this._interpolationParam = CubicBezierCurve.computeIterpolationParameters(this._numVerticesPerSegment)
return GeometryMapper.flatMap(this._inputGeom, 1, new (class {
get interfaces_() {
return [MapOp]
}
map(geom) {
if (geom instanceof LineString)
return this.bezierLine(geom)
if (geom instanceof Polygon)
return this.bezierPolygon(geom)
return geom.copy()
}
})())
}
bezierPolygon(poly) {
const shell = this.bezierRing(poly.getExteriorRing())
let holes = null
if (poly.getNumInteriorRing() > 0) {
holes = new Array(poly.getNumInteriorRing()).fill(null)
for (let i = 0; i < poly.getNumInteriorRing(); i++)
holes[i] = this.bezierRing(poly.getInteriorRingN(i))
}
return this._geomFactory.createPolygon(shell, holes)
}
controlPoints() {
if (arguments.length === 2) {
const coords = arguments[0], isRing = arguments[1]
if (this._controlPoints !== null) {
if (this._controlPointIndex >= this._controlPoints.getNumGeometries())
throw new IllegalArgumentException('Too few control point elements')
const ctrlPtsGeom = this._controlPoints.getGeometryN(this._controlPointIndex++)
const ctrlPts = ctrlPtsGeom.getCoordinates()
const expectedNum1 = 2 * coords.length - 2
const expectedNum2 = isRing ? coords.length - 1 : coords.length
if (expectedNum1 !== ctrlPts.length && expectedNum2 !== ctrlPts.length)
throw new IllegalArgumentException(String.format('Wrong number of control points for element %d - expected %d or %d, found %d', this._controlPointIndex - 1, expectedNum1, expectedNum2, ctrlPts.length))
return ctrlPts
}
return this.controlPoints(coords, isRing, this._alpha, this._skew)
} else if (arguments.length === 4) {
const coords = arguments[0], isRing = arguments[1], alpha = arguments[2], skew = arguments[3]
let N = coords.length
let start = 1
let end = N - 1
if (isRing) {
N = coords.length - 1
start = 0
end = N
}
const nControl = 2 * coords.length - 2
const ctrl = new Array(nControl).fill(null)
for (let i = start; i < end; i++) {
const iprev = i === 0 ? N - 1 : i - 1
const v0 = coords[iprev]
const v1 = coords[i]
const v2 = coords[i + 1]
const interiorAng = Angle.angleBetweenOriented(v0, v1, v2)
const orient = Math.signum(interiorAng)
const angBisect = Angle.bisector(v0, v1, v2)
const ang0 = angBisect - orient * Angle.PI_OVER_2
const ang1 = angBisect + orient * Angle.PI_OVER_2
const dist0 = v1.distance(v0)
const dist1 = v1.distance(v2)
const lenBase = Math.min(dist0, dist1)
const intAngAbs = Math.abs(interiorAng)
const sharpnessFactor = intAngAbs >= Angle.PI_OVER_2 ? 1 : intAngAbs / Angle.PI_OVER_2
const len = alpha * CubicBezierCurve.CIRCLE_LEN_FACTOR * sharpnessFactor * lenBase
let stretch0 = 1
let stretch1 = 1
if (skew !== 0) {
const stretch = Math.abs(dist0 - dist1) / Math.max(dist0, dist1)
let skewIndex = dist0 > dist1 ? 0 : 1
if (skew < 0) skewIndex = 1 - skewIndex
if (skewIndex === 0)
stretch0 += Math.abs(skew) * stretch
else
stretch1 += Math.abs(skew) * stretch
}
const ctl0 = Angle.project(v1, ang0, stretch0 * len)
const ctl1 = Angle.project(v1, ang1, stretch1 * len)
const index = 2 * i - 1
const i0 = index < 0 ? nControl - 1 : index
ctrl[i0] = ctl0
ctrl[index + 1] = ctl1
}
if (!isRing)
this.setLineEndControlPoints(coords, ctrl)
return ctrl
}
}
bezierCurve(coords, isRing) {
const control = this.controlPoints(coords, isRing)
const curvePts = new CoordinateList()
for (let i = 0; i < coords.length - 1; i++) {
const ctrlIndex = 2 * i
this.addCurve(coords[i], coords[i + 1], control[ctrlIndex], control[ctrlIndex + 1], curvePts)
}
return curvePts
}
setLineEndControlPoints(coords, ctrl) {
const N = ctrl.length
ctrl[0] = CubicBezierCurve.mirrorControlPoint(ctrl[1], coords[1], coords[0])
ctrl[N - 1] = CubicBezierCurve.mirrorControlPoint(ctrl[N - 2], coords[coords.length - 1], coords[coords.length - 2])
}
bezierRing(ring) {
const coords = ring.getCoordinates()
const curvePts = this.bezierCurve(coords, true)
curvePts.closeRing()
return this._geomFactory.createLinearRing(curvePts.toCoordinateArray())
}
cubicBezier(p0, p1, ctrl1, ctrl2, param, curve) {
const n = curve.length
curve[0] = new Coordinate(p0)
curve[n - 1] = new Coordinate(p1)
for (let i = 1; i < n - 1; i++) {
const c = new Coordinate()
const sum = param[i][0] + param[i][1] + param[i][2] + param[i][3]
c.x = param[i][0] * p0.x + param[i][1] * ctrl1.x + param[i][2] * ctrl2.x + param[i][3] * p1.x
c.x /= sum
c.y = param[i][0] * p0.y + param[i][1] * ctrl1.y + param[i][2] * ctrl2.y + param[i][3] * p1.y
c.y /= sum
curve[i] = c
}
}
addCurve(p0, p1, ctrl0, crtl1, curvePts) {
const len = p0.distance(p1)
if (len < this._minSegmentLength) {
curvePts.add(new Coordinate(p0))
} else {
this.cubicBezier(p0, p1, ctrl0, crtl1, this._interpolationParam, this._bezierCurvePts)
for (let i = 0; i < this._bezierCurvePts.length - 1; i++)
curvePts.add(this._bezierCurvePts[i], false)
}
}
bezierLine(ls) {
const coords = ls.getCoordinates()
const curvePts = this.bezierCurve(coords, false)
curvePts.add(coords[coords.length - 1].copy(), false)
return this._geomFactory.createLineString(curvePts.toCoordinateArray())
}
}
CubicBezierCurve.CIRCLE_LEN_FACTOR = 3.0 / 8.0

View File

@ -411,7 +411,7 @@ class TriangleCoordinatesVisitor {
checkTriangleSize(pts) {
let loc = ''
if (pts.length >= 2) loc = WKTWriter.toLineString(pts[0], pts[1]); else
if (pts.length >= 1) loc = WKTWriter.toPoint(pts[0])
if (pts.length >= 1) loc = WKTWriter.toPoint(pts[0])
}
visit(triEdges) {