diff --git a/src/org/locationtech/jts/algorithm/PolygonNodeTopology.js b/src/org/locationtech/jts/algorithm/PolygonNodeTopology.js deleted file mode 100644 index a6497706..00000000 --- a/src/org/locationtech/jts/algorithm/PolygonNodeTopology.js +++ /dev/null @@ -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) - } -} diff --git a/src/org/locationtech/jts/algorithm/RobustDeterminant.js b/src/org/locationtech/jts/algorithm/RobustDeterminant.js index 952ca68d..885cc73a 100644 --- a/src/org/locationtech/jts/algorithm/RobustDeterminant.js +++ b/src/org/locationtech/jts/algorithm/RobustDeterminant.js @@ -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) { diff --git a/src/org/locationtech/jts/geomgraph/Quadrant.js b/src/org/locationtech/jts/geomgraph/Quadrant.js index 11a72002..e7883a0d 100644 --- a/src/org/locationtech/jts/geomgraph/Quadrant.js +++ b/src/org/locationtech/jts/geomgraph/Quadrant.js @@ -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 } } diff --git a/src/org/locationtech/jts/noding/Octant.js b/src/org/locationtech/jts/noding/Octant.js index 52d6f9ec..07797c38 100644 --- a/src/org/locationtech/jts/noding/Octant.js +++ b/src/org/locationtech/jts/noding/Octant.js @@ -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) { diff --git a/src/org/locationtech/jts/noding/SegmentStringDissolver.js b/src/org/locationtech/jts/noding/SegmentStringDissolver.js index 5ff3df57..85f3767c 100644 --- a/src/org/locationtech/jts/noding/SegmentStringDissolver.js +++ b/src/org/locationtech/jts/noding/SegmentStringDissolver.js @@ -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) + } } } diff --git a/src/org/locationtech/jts/operation/buffer/BufferCurveSetBuilder.js b/src/org/locationtech/jts/operation/buffer/BufferCurveSetBuilder.js deleted file mode 100644 index 62c32e3e..00000000 --- a/src/org/locationtech/jts/operation/buffer/BufferCurveSetBuilder.js +++ /dev/null @@ -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 diff --git a/src/org/locationtech/jts/operation/buffer/OffsetCurve.js b/src/org/locationtech/jts/operation/buffer/OffsetCurve.js deleted file mode 100644 index 33097548..00000000 --- a/src/org/locationtech/jts/operation/buffer/OffsetCurve.js +++ /dev/null @@ -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 diff --git a/src/org/locationtech/jts/operation/buffer/OffsetCurveBuilder.js b/src/org/locationtech/jts/operation/buffer/OffsetCurveBuilder.js index b908c8a2..7f23e1c2 100644 --- a/src/org/locationtech/jts/operation/buffer/OffsetCurveBuilder.js +++ b/src/org/locationtech/jts/operation/buffer/OffsetCurveBuilder.js @@ -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 diff --git a/src/org/locationtech/jts/operation/buffer/SegmentMCIndex.js b/src/org/locationtech/jts/operation/buffer/SegmentMCIndex.js deleted file mode 100644 index 3beb2aac..00000000 --- a/src/org/locationtech/jts/operation/buffer/SegmentMCIndex.js +++ /dev/null @@ -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) - } - })()) - } -} diff --git a/src/org/locationtech/jts/operation/overlayng/CoverageUnion.js b/src/org/locationtech/jts/operation/overlayng/CoverageUnion.js deleted file mode 100644 index c2bf6c6e..00000000 --- a/src/org/locationtech/jts/operation/overlayng/CoverageUnion.js +++ /dev/null @@ -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) - } -} diff --git a/src/org/locationtech/jts/operation/overlayng/Edge.js b/src/org/locationtech/jts/operation/overlayng/Edge.js deleted file mode 100644 index 823d91e2..00000000 --- a/src/org/locationtech/jts/operation/overlayng/Edge.js +++ /dev/null @@ -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 - } -} diff --git a/src/org/locationtech/jts/operation/overlayng/EdgeKey.js b/src/org/locationtech/jts/operation/overlayng/EdgeKey.js deleted file mode 100644 index 0b3c0324..00000000 --- a/src/org/locationtech/jts/operation/overlayng/EdgeKey.js +++ /dev/null @@ -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] - } -} diff --git a/src/org/locationtech/jts/operation/overlayng/EdgeMerger.js b/src/org/locationtech/jts/operation/overlayng/EdgeMerger.js deleted file mode 100644 index 194896aa..00000000 --- a/src/org/locationtech/jts/operation/overlayng/EdgeMerger.js +++ /dev/null @@ -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 - } -} diff --git a/src/org/locationtech/jts/operation/overlayng/EdgeNodingBuilder.js b/src/org/locationtech/jts/operation/overlayng/EdgeNodingBuilder.js deleted file mode 100644 index 8b47be0e..00000000 --- a/src/org/locationtech/jts/operation/overlayng/EdgeNodingBuilder.js +++ /dev/null @@ -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 diff --git a/src/org/locationtech/jts/operation/overlayng/EdgeSourceInfo.js b/src/org/locationtech/jts/operation/overlayng/EdgeSourceInfo.js deleted file mode 100644 index 3345df9e..00000000 --- a/src/org/locationtech/jts/operation/overlayng/EdgeSourceInfo.js +++ /dev/null @@ -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 - } -} diff --git a/src/org/locationtech/jts/operation/overlayng/ElevationModel.js b/src/org/locationtech/jts/operation/overlayng/ElevationModel.js deleted file mode 100644 index 1a95943c..00000000 --- a/src/org/locationtech/jts/operation/overlayng/ElevationModel.js +++ /dev/null @@ -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 diff --git a/src/org/locationtech/jts/operation/overlayng/FastOverlayFilter.js b/src/org/locationtech/jts/operation/overlayng/FastOverlayFilter.js deleted file mode 100644 index c1e68666..00000000 --- a/src/org/locationtech/jts/operation/overlayng/FastOverlayFilter.js +++ /dev/null @@ -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 - } -} diff --git a/src/org/locationtech/jts/operation/overlayng/IndexedPointOnLineLocator.js b/src/org/locationtech/jts/operation/overlayng/IndexedPointOnLineLocator.js deleted file mode 100644 index 2f3d37f1..00000000 --- a/src/org/locationtech/jts/operation/overlayng/IndexedPointOnLineLocator.js +++ /dev/null @@ -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] - } -} diff --git a/src/org/locationtech/jts/operation/overlayng/InputGeometry.js b/src/org/locationtech/jts/operation/overlayng/InputGeometry.js deleted file mode 100644 index 8057f180..00000000 --- a/src/org/locationtech/jts/operation/overlayng/InputGeometry.js +++ /dev/null @@ -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() - } -} diff --git a/src/org/locationtech/jts/operation/overlayng/IntersectionPointBuilder.js b/src/org/locationtech/jts/operation/overlayng/IntersectionPointBuilder.js deleted file mode 100644 index be6a08d0..00000000 --- a/src/org/locationtech/jts/operation/overlayng/IntersectionPointBuilder.js +++ /dev/null @@ -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 - } -} diff --git a/src/org/locationtech/jts/operation/overlayng/LineBuilder.js b/src/org/locationtech/jts/operation/overlayng/LineBuilder.js deleted file mode 100644 index 60bd3515..00000000 --- a/src/org/locationtech/jts/operation/overlayng/LineBuilder.js +++ /dev/null @@ -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 - } -} diff --git a/src/org/locationtech/jts/operation/overlayng/LineLimiter.js b/src/org/locationtech/jts/operation/overlayng/LineLimiter.js deleted file mode 100644 index d29b8588..00000000 --- a/src/org/locationtech/jts/operation/overlayng/LineLimiter.js +++ /dev/null @@ -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) - } -} diff --git a/src/org/locationtech/jts/operation/overlayng/MaximalEdgeRing.js b/src/org/locationtech/jts/operation/overlayng/MaximalEdgeRing.js deleted file mode 100644 index 79105666..00000000 --- a/src/org/locationtech/jts/operation/overlayng/MaximalEdgeRing.js +++ /dev/null @@ -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 diff --git a/src/org/locationtech/jts/operation/overlayng/OverlayEdge.js b/src/org/locationtech/jts/operation/overlayng/OverlayEdge.js deleted file mode 100644 index 356b5ff5..00000000 --- a/src/org/locationtech/jts/operation/overlayng/OverlayEdge.js +++ /dev/null @@ -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]; - } - 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; - } -} diff --git a/src/org/locationtech/jts/operation/overlayng/OverlayEdgeRing.js b/src/org/locationtech/jts/operation/overlayng/OverlayEdgeRing.js deleted file mode 100644 index bee184fc..00000000 --- a/src/org/locationtech/jts/operation/overlayng/OverlayEdgeRing.js +++ /dev/null @@ -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 - } -} diff --git a/src/org/locationtech/jts/operation/overlayng/OverlayGraph.js b/src/org/locationtech/jts/operation/overlayng/OverlayGraph.js deleted file mode 100644 index f7e5ecbb..00000000 --- a/src/org/locationtech/jts/operation/overlayng/OverlayGraph.js +++ /dev/null @@ -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() - } -} diff --git a/src/org/locationtech/jts/operation/overlayng/OverlayLabel.js b/src/org/locationtech/jts/operation/overlayng/OverlayLabel.js deleted file mode 100644 index 9acec96e..00000000 --- a/src/org/locationtech/jts/operation/overlayng/OverlayLabel.js +++ /dev/null @@ -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 diff --git a/src/org/locationtech/jts/operation/overlayng/OverlayLabeller.js b/src/org/locationtech/jts/operation/overlayng/OverlayLabeller.js deleted file mode 100644 index dc6753fa..00000000 --- a/src/org/locationtech/jts/operation/overlayng/OverlayLabeller.js +++ /dev/null @@ -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() - } -} diff --git a/src/org/locationtech/jts/operation/overlayng/OverlayMixedPoints.js b/src/org/locationtech/jts/operation/overlayng/OverlayMixedPoints.js deleted file mode 100644 index 47eefac1..00000000 --- a/src/org/locationtech/jts/operation/overlayng/OverlayMixedPoints.js +++ /dev/null @@ -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 - } -} diff --git a/src/org/locationtech/jts/operation/overlayng/OverlayNG.js b/src/org/locationtech/jts/operation/overlayng/OverlayNG.js deleted file mode 100644 index 65cf74df..00000000 --- a/src/org/locationtech/jts/operation/overlayng/OverlayNG.js +++ /dev/null @@ -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 diff --git a/src/org/locationtech/jts/operation/overlayng/OverlayNGRobust.js b/src/org/locationtech/jts/operation/overlayng/OverlayNGRobust.js deleted file mode 100644 index 421e97f6..00000000 --- a/src/org/locationtech/jts/operation/overlayng/OverlayNGRobust.js +++ /dev/null @@ -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 diff --git a/src/org/locationtech/jts/operation/overlayng/OverlayPoints.js b/src/org/locationtech/jts/operation/overlayng/OverlayPoints.js deleted file mode 100644 index eea2c2cc..00000000 --- a/src/org/locationtech/jts/operation/overlayng/OverlayPoints.js +++ /dev/null @@ -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) - } -} diff --git a/src/org/locationtech/jts/operation/overlayng/OverlayUtil.js b/src/org/locationtech/jts/operation/overlayng/OverlayUtil.js deleted file mode 100644 index 4a6b7303..00000000 --- a/src/org/locationtech/jts/operation/overlayng/OverlayUtil.js +++ /dev/null @@ -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 diff --git a/src/org/locationtech/jts/operation/overlayng/PolygonBuilder.js b/src/org/locationtech/jts/operation/overlayng/PolygonBuilder.js deleted file mode 100644 index 2ed055c8..00000000 --- a/src/org/locationtech/jts/operation/overlayng/PolygonBuilder.js +++ /dev/null @@ -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) - - } -} diff --git a/src/org/locationtech/jts/operation/overlayng/PrecisionReducer.js b/src/org/locationtech/jts/operation/overlayng/PrecisionReducer.js deleted file mode 100644 index 684891be..00000000 --- a/src/org/locationtech/jts/operation/overlayng/PrecisionReducer.js +++ /dev/null @@ -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 {} - } -} diff --git a/src/org/locationtech/jts/operation/overlayng/PrecisionUtil.js b/src/org/locationtech/jts/operation/overlayng/PrecisionUtil.js deleted file mode 100644 index 741ecd33..00000000 --- a/src/org/locationtech/jts/operation/overlayng/PrecisionUtil.js +++ /dev/null @@ -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 diff --git a/src/org/locationtech/jts/operation/overlayng/RingClipper.js b/src/org/locationtech/jts/operation/overlayng/RingClipper.js deleted file mode 100644 index 3c760e2b..00000000 --- a/src/org/locationtech/jts/operation/overlayng/RingClipper.js +++ /dev/null @@ -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 diff --git a/src/org/locationtech/jts/operation/overlayng/RobustClipEnvelopeComputer.js b/src/org/locationtech/jts/operation/overlayng/RobustClipEnvelopeComputer.js deleted file mode 100644 index 466a065a..00000000 --- a/src/org/locationtech/jts/operation/overlayng/RobustClipEnvelopeComputer.js +++ /dev/null @@ -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) - } - } -} diff --git a/src/org/locationtech/jts/operation/overlayng/UnaryUnionNG.js b/src/org/locationtech/jts/operation/overlayng/UnaryUnionNG.js deleted file mode 100644 index 6d88b90a..00000000 --- a/src/org/locationtech/jts/operation/overlayng/UnaryUnionNG.js +++ /dev/null @@ -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 - } -} diff --git a/src/org/locationtech/jts/operation/relate/RelateComputer.js b/src/org/locationtech/jts/operation/relate/RelateComputer.js index e8a18f6e..238675b3 100644 --- a/src/org/locationtech/jts/operation/relate/RelateComputer.js +++ b/src/org/locationtech/jts/operation/relate/RelateComputer.js @@ -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) } } diff --git a/src/org/locationtech/jts/operation/relate/RelateNodeGraph.js b/src/org/locationtech/jts/operation/relate/RelateNodeGraph.js index 0ebb3809..b9879956 100644 --- a/src/org/locationtech/jts/operation/relate/RelateNodeGraph.js +++ b/src/org/locationtech/jts/operation/relate/RelateNodeGraph.js @@ -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) } } diff --git a/src/org/locationtech/jts/operation/union/UnionStrategy.js b/src/org/locationtech/jts/operation/union/UnionStrategy.js deleted file mode 100644 index ed8991b2..00000000 --- a/src/org/locationtech/jts/operation/union/UnionStrategy.js +++ /dev/null @@ -1,4 +0,0 @@ -export default class UnionStrategy { - union(g0, g1) {} - isFloatingPrecision() {} -} diff --git a/src/org/locationtech/jts/operation/valid/IndexedNestedHoleTester.js b/src/org/locationtech/jts/operation/valid/IndexedNestedHoleTester.js deleted file mode 100644 index 194f6417..00000000 --- a/src/org/locationtech/jts/operation/valid/IndexedNestedHoleTester.js +++ /dev/null @@ -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 - } -} diff --git a/src/org/locationtech/jts/operation/valid/IndexedNestedPolygonTester.js b/src/org/locationtech/jts/operation/valid/IndexedNestedPolygonTester.js deleted file mode 100644 index 698a77b2..00000000 --- a/src/org/locationtech/jts/operation/valid/IndexedNestedPolygonTester.js +++ /dev/null @@ -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) - } -} diff --git a/src/org/locationtech/jts/operation/valid/IsSimpleOp.js b/src/org/locationtech/jts/operation/valid/IsSimpleOp.js deleted file mode 100644 index 68580172..00000000 --- a/src/org/locationtech/jts/operation/valid/IsSimpleOp.js +++ /dev/null @@ -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 diff --git a/src/org/locationtech/jts/operation/valid/PolygonIntersectionAnalyzer.js b/src/org/locationtech/jts/operation/valid/PolygonIntersectionAnalyzer.js deleted file mode 100644 index 9ecb070a..00000000 --- a/src/org/locationtech/jts/operation/valid/PolygonIntersectionAnalyzer.js +++ /dev/null @@ -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 diff --git a/src/org/locationtech/jts/operation/valid/PolygonRing.js b/src/org/locationtech/jts/operation/valid/PolygonRing.js deleted file mode 100644 index 56d156b1..00000000 --- a/src/org/locationtech/jts/operation/valid/PolygonRing.js +++ /dev/null @@ -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 - } -} diff --git a/src/org/locationtech/jts/operation/valid/PolygonTopologyAnalyzer.js b/src/org/locationtech/jts/operation/valid/PolygonTopologyAnalyzer.js deleted file mode 100644 index d369b5ae..00000000 --- a/src/org/locationtech/jts/operation/valid/PolygonTopologyAnalyzer.js +++ /dev/null @@ -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() - } -} diff --git a/src/org/locationtech/jts/precision/PointwisePrecisionReducerTransformer.js b/src/org/locationtech/jts/precision/PointwisePrecisionReducerTransformer.js deleted file mode 100644 index d14a7227..00000000 --- a/src/org/locationtech/jts/precision/PointwisePrecisionReducerTransformer.js +++ /dev/null @@ -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 - } -} diff --git a/src/org/locationtech/jts/precision/PrecisionReducerTransformer.js b/src/org/locationtech/jts/precision/PrecisionReducerTransformer.js deleted file mode 100644 index b94bd3e9..00000000 --- a/src/org/locationtech/jts/precision/PrecisionReducerTransformer.js +++ /dev/null @@ -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 - } -} diff --git a/src/org/locationtech/jts/shape/CubicBezierCurve.js b/src/org/locationtech/jts/shape/CubicBezierCurve.js deleted file mode 100644 index bbd48c9e..00000000 --- a/src/org/locationtech/jts/shape/CubicBezierCurve.js +++ /dev/null @@ -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 diff --git a/src/org/locationtech/jts/triangulate/quadedge/QuadEdgeSubdivision.js b/src/org/locationtech/jts/triangulate/quadedge/QuadEdgeSubdivision.js index eb3631c8..de18d9c0 100644 --- a/src/org/locationtech/jts/triangulate/quadedge/QuadEdgeSubdivision.js +++ b/src/org/locationtech/jts/triangulate/quadedge/QuadEdgeSubdivision.js @@ -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) {