* The WKTWriter
outputs coordinates rounded to the precision
* model. Only the maximum number of decimal places necessary to represent the
* ordinates to the required precision will be output.
*
* The SFS WKT spec does not define a special tag for {@link LinearRing}s.
* Under the spec, rings are output as
* LINESTRING
s.
*/
class WKTWriter {
/**
* @param {GeometryFactory} geometryFactory
*/
constructor(geometryFactory) {
this.parser = new WKTParser(geometryFactory);
}
/**
* Converts a Geometry
to its Well-known Text representation.
*
* @param {Geometry} geometry a Geometry
to process.
* @return {string} a GeoJSONReader
is parameterized by a GeometryFactory
,
* to allow it to create Geometry
objects of the appropriate
* implementation. In particular, the GeometryFactory
determines
* the PrecisionModel
and SRID
that is used.
*
* @param {GeometryFactory} geometryFactory
*/
constructor(geometryFactory) {
this.parser = new GeoJSONParser(geometryFactory || new GeometryFactory());
}
/**
* Reads a GeoJSON representation of a {@link Geometry}
*
* Will also parse GeoJSON Features/FeatureCollections as custom objects.
*
* @param {Object|String} geoJson a GeoJSON Object or String.
* @return {Geometry|Object} a Geometry or Feature/FeatureCollection representation.
* @memberof module:org/locationtech/jts/io/GeoJSONReader#
*/
read(geoJson) {
const geometry = this.parser.read(geoJson);
return geometry;
}
}
/**
* @module org/locationtech/jts/io/GeoJSONWriter
*/
/**
* Writes the GeoJSON representation of a {@link Geometry}. The
* The GeoJSON format is defined here.
*/
class GeoJSONWriter {
/**
* The GeoJSONWriter
outputs coordinates rounded to the precision
* model. Only the maximum number of decimal places necessary to represent the
* ordinates to the required precision will be output.
*
* @param {GeometryFactory} geometryFactory
* @constructor
*/
constructor() {
this.parser = new GeoJSONParser(this.geometryFactory);
}
/**
* Converts a Geometry
to its GeoJSON representation.
*
* @param {Geometry}
* geometry a Geometry
to process.
* @return {Object} The GeoJSON representation of the Geometry.
* @memberof module:org/locationtech/jts/io/GeoJSONWriter#
*/
write(geometry) {
return this.parser.write(geometry);
}
}
/**
* @module org/locationtech/jts/io/WKTReader
*/
/**
* Converts a geometry in Well-Known Text format to a {@link Geometry}.
* WKTReader
supports extracting Geometry
objects
* from either {@link Reader}s or {@link String}s. This allows it to function
* as a parser to read Geometry
objects from text blocks embedded
* in other data formats (e.g. XML).
*/
class WKTReader {
/**
* A WKTReader
is parameterized by a GeometryFactory
,
* to allow it to create Geometry
objects of the appropriate
* implementation. In particular, the GeometryFactory
determines
* the PrecisionModel
and SRID
that is used.
* @param {GeometryFactory} geometryFactory
*/
constructor(geometryFactory) {
this.parser = new WKTParser(geometryFactory || new GeometryFactory());
}
/**
* Reads a Well-Known Text representation of a {@link Geometry}
*
* @param {string}
* wkt a Geometry
read from
* string.
* @memberof module:org/locationtech/jts/io/WKTReader#
*/
read(wkt) {
return this.parser.read(wkt);
}
}
/* eslint-disable no-undef */
function p2c(p) {
return [p.x, p.y];
}
class OL3Parser {
/**
* OpenLayers Geometry parser and writer
* @param {GeometryFactory} geometryFactory
* @param {ol} olReference
*/
constructor(geometryFactory, olReference) {
this.geometryFactory = geometryFactory || new GeometryFactory();
this.ol = olReference || typeof ol !== 'undefined' && ol;
}
/**
* Inject OpenLayers geom classes
*/
inject(Point, LineString, LinearRing, Polygon, MultiPoint, MultiLineString, MultiPolygon, GeometryCollection) {
this.ol = {
geom: {
Point,
LineString,
LinearRing,
Polygon,
MultiPoint,
MultiLineString,
MultiPolygon,
GeometryCollection
}
};
}
/**
* @param geometry {ol.geom.Geometry}
* @return {Geometry}
* @memberof module:org/locationtech/jts/io/OL3Parser#
*/
read(geometry) {
const ol = this.ol;
if (geometry instanceof ol.geom.Point) return this.convertFromPoint(geometry);else if (geometry instanceof ol.geom.LineString) return this.convertFromLineString(geometry);else if (geometry instanceof ol.geom.LinearRing) return this.convertFromLinearRing(geometry);else if (geometry instanceof ol.geom.Polygon) return this.convertFromPolygon(geometry);else if (geometry instanceof ol.geom.MultiPoint) return this.convertFromMultiPoint(geometry);else if (geometry instanceof ol.geom.MultiLineString) return this.convertFromMultiLineString(geometry);else if (geometry instanceof ol.geom.MultiPolygon) return this.convertFromMultiPolygon(geometry);else if (geometry instanceof ol.geom.GeometryCollection) return this.convertFromCollection(geometry);
}
convertFromPoint(point) {
const coordinates = point.getCoordinates();
return this.geometryFactory.createPoint(new Coordinate(coordinates[0], coordinates[1]));
}
convertFromLineString(lineString) {
return this.geometryFactory.createLineString(lineString.getCoordinates().map(function (coordinates) {
return new Coordinate(coordinates[0], coordinates[1]);
}));
}
convertFromLinearRing(linearRing) {
return this.geometryFactory.createLinearRing(linearRing.getCoordinates().map(function (coordinates) {
return new Coordinate(coordinates[0], coordinates[1]);
}));
}
convertFromPolygon(polygon) {
const linearRings = polygon.getLinearRings();
let shell = null;
const holes = [];
for (let i = 0; i < linearRings.length; i++) {
const linearRing = this.convertFromLinearRing(linearRings[i]);
if (i === 0) shell = linearRing;else holes.push(linearRing);
}
return this.geometryFactory.createPolygon(shell, holes);
}
convertFromMultiPoint(multiPoint) {
const points = multiPoint.getPoints().map(function (point) {
return this.convertFromPoint(point);
}, this);
return this.geometryFactory.createMultiPoint(points);
}
convertFromMultiLineString(multiLineString) {
const lineStrings = multiLineString.getLineStrings().map(function (lineString) {
return this.convertFromLineString(lineString);
}, this);
return this.geometryFactory.createMultiLineString(lineStrings);
}
convertFromMultiPolygon(multiPolygon) {
const polygons = multiPolygon.getPolygons().map(function (polygon) {
return this.convertFromPolygon(polygon);
}, this);
return this.geometryFactory.createMultiPolygon(polygons);
}
convertFromCollection(collection) {
const geometries = collection.getGeometries().map(function (geometry) {
return this.read(geometry);
}, this);
return this.geometryFactory.createGeometryCollection(geometries);
}
/**
* @param geometry
* {Geometry}
* @return {ol.geom.Geometry}
* @memberof module:org/locationtech/jts/io/OL3Parser#
*/
write(geometry) {
if (geometry.getGeometryType() === 'Point') return this.convertToPoint(geometry.getCoordinate());else if (geometry.getGeometryType() === 'LineString') return this.convertToLineString(geometry);else if (geometry.getGeometryType() === 'LinearRing') return this.convertToLinearRing(geometry);else if (geometry.getGeometryType() === 'Polygon') return this.convertToPolygon(geometry);else if (geometry.getGeometryType() === 'MultiPoint') return this.convertToMultiPoint(geometry);else if (geometry.getGeometryType() === 'MultiLineString') return this.convertToMultiLineString(geometry);else if (geometry.getGeometryType() === 'MultiPolygon') return this.convertToMultiPolygon(geometry);else if (geometry.getGeometryType() === 'GeometryCollection') return this.convertToCollection(geometry);
}
convertToPoint(coordinate) {
return new this.ol.geom.Point([coordinate.x, coordinate.y]);
}
convertToLineString(lineString) {
const points = lineString._points._coordinates.map(p2c);
return new this.ol.geom.LineString(points);
}
convertToLinearRing(linearRing) {
const points = linearRing._points._coordinates.map(p2c);
return new this.ol.geom.LinearRing(points);
}
convertToPolygon(polygon) {
const rings = [polygon._shell._points._coordinates.map(p2c)];
for (let i = 0; i < polygon._holes.length; i++) rings.push(polygon._holes[i]._points._coordinates.map(p2c));
return new this.ol.geom.Polygon(rings);
}
convertToMultiPoint(multiPoint) {
return new this.ol.geom.MultiPoint(multiPoint.getCoordinates().map(p2c));
}
convertToMultiLineString(multiLineString) {
const lineStrings = [];
for (let i = 0; i < multiLineString._geometries.length; i++) lineStrings.push(this.convertToLineString(multiLineString._geometries[i]).getCoordinates());
return new this.ol.geom.MultiLineString(lineStrings);
}
convertToMultiPolygon(multiPolygon) {
const polygons = [];
for (let i = 0; i < multiPolygon._geometries.length; i++) polygons.push(this.convertToPolygon(multiPolygon._geometries[i]).getCoordinates());
return new this.ol.geom.MultiPolygon(polygons);
}
convertToCollection(geometryCollection) {
const geometries = [];
for (let i = 0; i < geometryCollection._geometries.length; i++) {
const geometry = geometryCollection._geometries[i];
geometries.push(this.write(geometry));
}
return new this.ol.geom.GeometryCollection(geometries);
}
}
var io = /*#__PURE__*/Object.freeze({
__proto__: null,
GeoJSONReader: GeoJSONReader,
GeoJSONWriter: GeoJSONWriter,
OL3Parser: OL3Parser,
WKTReader: WKTReader,
WKTWriter: WKTWriter
});
class SegmentPointComparator {
static relativeSign(x0, x1) {
if (x0 < x1) return -1;
if (x0 > x1) return 1;
return 0;
}
static compare(octant, p0, p1) {
if (p0.equals2D(p1)) return 0;
const xSign = SegmentPointComparator.relativeSign(p0.x, p1.x);
const ySign = SegmentPointComparator.relativeSign(p0.y, p1.y);
switch (octant) {
case 0:
return SegmentPointComparator.compareValue(xSign, ySign);
case 1:
return SegmentPointComparator.compareValue(ySign, xSign);
case 2:
return SegmentPointComparator.compareValue(ySign, -xSign);
case 3:
return SegmentPointComparator.compareValue(-xSign, ySign);
case 4:
return SegmentPointComparator.compareValue(-xSign, -ySign);
case 5:
return SegmentPointComparator.compareValue(-ySign, -xSign);
case 6:
return SegmentPointComparator.compareValue(-ySign, xSign);
case 7:
return SegmentPointComparator.compareValue(xSign, -ySign);
}
Assert.shouldNeverReachHere('invalid octant value');
return 0;
}
static compareValue(compareSign0, compareSign1) {
if (compareSign0 < 0) return -1;
if (compareSign0 > 0) return 1;
if (compareSign1 < 0) return -1;
if (compareSign1 > 0) return 1;
return 0;
}
}
class SegmentNode {
constructor() {
SegmentNode.constructor_.apply(this, arguments);
}
static constructor_() {
this._segString = null;
this.coord = null;
this.segmentIndex = null;
this._segmentOctant = null;
this._isInterior = null;
const segString = arguments[0],
coord = arguments[1],
segmentIndex = arguments[2],
segmentOctant = arguments[3];
this._segString = segString;
this.coord = new Coordinate(coord);
this.segmentIndex = segmentIndex;
this._segmentOctant = segmentOctant;
this._isInterior = !coord.equals2D(segString.getCoordinate(segmentIndex));
}
getCoordinate() {
return this.coord;
}
print(out) {
out.print(this.coord);
out.print(' seg # = ' + this.segmentIndex);
}
compareTo(obj) {
const other = obj;
if (this.segmentIndex < other.segmentIndex) return -1;
if (this.segmentIndex > other.segmentIndex) return 1;
if (this.coord.equals2D(other.coord)) return 0;
if (!this._isInterior) return -1;
if (!other._isInterior) return 1;
return SegmentPointComparator.compare(this._segmentOctant, this.coord, other.coord);
}
isEndPoint(maxSegmentIndex) {
if (this.segmentIndex === 0 && !this._isInterior) return true;
if (this.segmentIndex === maxSegmentIndex) return true;
return false;
}
toString() {
return this.segmentIndex + ':' + this.coord.toString();
}
isInterior() {
return this._isInterior;
}
get interfaces_() {
return [Comparable];
}
}
class SegmentNodeList {
constructor() {
SegmentNodeList.constructor_.apply(this, arguments);
}
static constructor_() {
this._nodeMap = new TreeMap();
this._edge = null;
const edge = arguments[0];
this._edge = edge;
}
getSplitCoordinates() {
const coordList = new CoordinateList();
this.addEndpoints();
const it = this.iterator();
let eiPrev = it.next();
while (it.hasNext()) {
const ei = it.next();
this.addEdgeCoordinates(eiPrev, ei, coordList);
eiPrev = ei;
}
return coordList.toCoordinateArray();
}
addCollapsedNodes() {
const collapsedVertexIndexes = new ArrayList();
this.findCollapsesFromInsertedNodes(collapsedVertexIndexes);
this.findCollapsesFromExistingVertices(collapsedVertexIndexes);
for (let it = collapsedVertexIndexes.iterator(); it.hasNext();) {
const vertexIndex = it.next().intValue();
this.add(this._edge.getCoordinate(vertexIndex), vertexIndex);
}
}
createSplitEdgePts(ei0, ei1) {
let npts = ei1.segmentIndex - ei0.segmentIndex + 2;
if (npts === 2) return [new Coordinate(ei0.coord), new Coordinate(ei1.coord)];
const lastSegStartPt = this._edge.getCoordinate(ei1.segmentIndex);
const useIntPt1 = ei1.isInterior() || !ei1.coord.equals2D(lastSegStartPt);
if (!useIntPt1) npts--;
const pts = new Array(npts).fill(null);
let ipt = 0;
pts[ipt++] = new Coordinate(ei0.coord);
for (let i = ei0.segmentIndex + 1; i <= ei1.segmentIndex; i++) pts[ipt++] = this._edge.getCoordinate(i);
if (useIntPt1) pts[ipt] = new Coordinate(ei1.coord);
return pts;
}
print(out) {
out.println('Intersections:');
for (let it = this.iterator(); it.hasNext();) {
const ei = it.next();
ei.print(out);
}
}
findCollapsesFromExistingVertices(collapsedVertexIndexes) {
for (let i = 0; i < this._edge.size() - 2; i++) {
const p0 = this._edge.getCoordinate(i);
this._edge.getCoordinate(i + 1);
const p2 = this._edge.getCoordinate(i + 2);
if (p0.equals2D(p2)) collapsedVertexIndexes.add(Integer.valueOf(i + 1));
}
}
addEdgeCoordinates(ei0, ei1, coordList) {
const pts = this.createSplitEdgePts(ei0, ei1);
coordList.add(pts, false);
}
iterator() {
return this._nodeMap.values().iterator();
}
addSplitEdges(edgeList) {
this.addEndpoints();
this.addCollapsedNodes();
const it = this.iterator();
let eiPrev = it.next();
while (it.hasNext()) {
const ei = it.next();
const newEdge = this.createSplitEdge(eiPrev, ei);
edgeList.add(newEdge);
eiPrev = ei;
}
}
findCollapseIndex(ei0, ei1, collapsedVertexIndex) {
if (!ei0.coord.equals2D(ei1.coord)) return false;
let numVerticesBetween = ei1.segmentIndex - ei0.segmentIndex;
if (!ei1.isInterior()) numVerticesBetween--;
if (numVerticesBetween === 1) {
collapsedVertexIndex[0] = ei0.segmentIndex + 1;
return true;
}
return false;
}
findCollapsesFromInsertedNodes(collapsedVertexIndexes) {
const collapsedVertexIndex = new Array(1).fill(null);
const it = this.iterator();
let eiPrev = it.next();
while (it.hasNext()) {
const ei = it.next();
const isCollapsed = this.findCollapseIndex(eiPrev, ei, collapsedVertexIndex);
if (isCollapsed) collapsedVertexIndexes.add(Integer.valueOf(collapsedVertexIndex[0]));
eiPrev = ei;
}
}
getEdge() {
return this._edge;
}
addEndpoints() {
const maxSegIndex = this._edge.size() - 1;
this.add(this._edge.getCoordinate(0), 0);
this.add(this._edge.getCoordinate(maxSegIndex), maxSegIndex);
}
createSplitEdge(ei0, ei1) {
const pts = this.createSplitEdgePts(ei0, ei1);
return new NodedSegmentString(pts, this._edge.getData());
}
add(intPt, segmentIndex) {
const eiNew = new SegmentNode(this._edge, intPt, segmentIndex, this._edge.getSegmentOctant(segmentIndex));
const ei = this._nodeMap.get(eiNew);
if (ei !== null) {
Assert.isTrue(ei.coord.equals2D(intPt), 'Found equal nodes with different coordinates');
return ei;
}
this._nodeMap.put(eiNew, eiNew);
return eiNew;
}
checkSplitEdgesCorrectness(splitEdges) {
const edgePts = this._edge.getCoordinates();
const split0 = splitEdges.get(0);
const pt0 = split0.getCoordinate(0);
if (!pt0.equals2D(edgePts[0])) throw new RuntimeException('bad split edge start point at ' + pt0);
const splitn = splitEdges.get(splitEdges.size() - 1);
const splitnPts = splitn.getCoordinates();
const ptn = splitnPts[splitnPts.length - 1];
if (!ptn.equals2D(edgePts[edgePts.length - 1])) throw new RuntimeException('bad split edge end point at ' + ptn);
}
}
class Octant {
static octant() {
if (typeof arguments[0] === 'number' && typeof arguments[1] === 'number') {
const dx = arguments[0],
dy = arguments[1];
if (dx === 0.0 && dy === 0.0) throw new IllegalArgumentException('Cannot compute the octant for point ( ' + dx + ', ' + dy + ' )');
const adx = Math.abs(dx);
const ady = Math.abs(dy);
if (dx >= 0) {
if (dy >= 0) {
if (adx >= ady) return 0;else return 1;
} else 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;
} else if (arguments[0] instanceof Coordinate && arguments[1] instanceof Coordinate) {
const p0 = arguments[0],
p1 = arguments[1];
const dx = p1.x - p0.x;
const dy = p1.y - p0.y;
if (dx === 0.0 && dy === 0.0) throw new IllegalArgumentException('Cannot compute the octant for two identical points ' + p0);
return Octant.octant(dx, dy);
}
}
}
class SegmentString {
getCoordinates() {}
size() {}
getCoordinate(i) {}
isClosed() {}
setData(data) {}
getData() {}
}
class NodableSegmentString {
addIntersection(intPt, segmentIndex) {}
get interfaces_() {
return [SegmentString];
}
}
class NodedSegmentString {
constructor() {
NodedSegmentString.constructor_.apply(this, arguments);
}
static constructor_() {
this._nodeList = new SegmentNodeList(this);
this._pts = null;
this._data = null;
const pts = arguments[0],
data = arguments[1];
this._pts = pts;
this._data = data;
}
static getNodedSubstrings() {
if (arguments.length === 1) {
const segStrings = arguments[0];
const resultEdgelist = new ArrayList();
NodedSegmentString.getNodedSubstrings(segStrings, resultEdgelist);
return resultEdgelist;
} else if (arguments.length === 2) {
const segStrings = arguments[0],
resultEdgelist = arguments[1];
for (let i = segStrings.iterator(); i.hasNext();) {
const ss = i.next();
ss.getNodeList().addSplitEdges(resultEdgelist);
}
}
}
getCoordinates() {
return this._pts;
}
size() {
return this._pts.length;
}
getCoordinate(i) {
return this._pts[i];
}
isClosed() {
return this._pts[0].equals(this._pts[this._pts.length - 1]);
}
getSegmentOctant(index) {
if (index === this._pts.length - 1) return -1;
return this.safeOctant(this.getCoordinate(index), this.getCoordinate(index + 1));
}
setData(data) {
this._data = data;
}
safeOctant(p0, p1) {
if (p0.equals2D(p1)) return 0;
return Octant.octant(p0, p1);
}
getData() {
return this._data;
}
addIntersection() {
if (arguments.length === 2) {
const intPt = arguments[0],
segmentIndex = arguments[1];
this.addIntersectionNode(intPt, segmentIndex);
} else if (arguments.length === 4) {
const li = arguments[0],
segmentIndex = arguments[1],
intIndex = arguments[3];
const intPt = new Coordinate(li.getIntersection(intIndex));
this.addIntersection(intPt, segmentIndex);
}
}
toString() {
return WKTWriter.toLineString(new CoordinateArraySequence(this._pts));
}
getNodeList() {
return this._nodeList;
}
addIntersectionNode(intPt, segmentIndex) {
let normalizedSegmentIndex = segmentIndex;
const nextSegIndex = normalizedSegmentIndex + 1;
if (nextSegIndex < this._pts.length) {
const nextPt = this._pts[nextSegIndex];
if (intPt.equals2D(nextPt)) normalizedSegmentIndex = nextSegIndex;
}
const ei = this._nodeList.add(intPt, normalizedSegmentIndex);
return ei;
}
addIntersections(li, segmentIndex, geomIndex) {
for (let i = 0; i < li.getIntersectionNum(); i++) this.addIntersection(li, segmentIndex, geomIndex, i);
}
get interfaces_() {
return [NodableSegmentString];
}
}
class MonotoneChainOverlapAction {
constructor() {
MonotoneChainOverlapAction.constructor_.apply(this, arguments);
}
static constructor_() {
this._overlapSeg1 = new LineSegment();
this._overlapSeg2 = new LineSegment();
}
overlap() {
if (arguments.length === 2) ; else if (arguments.length === 4) {
const mc1 = arguments[0],
start1 = arguments[1],
mc2 = arguments[2],
start2 = arguments[3];
mc1.getLineSegment(start1, this._overlapSeg1);
mc2.getLineSegment(start2, this._overlapSeg2);
this.overlap(this._overlapSeg1, this._overlapSeg2);
}
}
}
class MonotoneChain {
constructor() {
MonotoneChain.constructor_.apply(this, arguments);
}
static constructor_() {
this._pts = null;
this._start = null;
this._end = null;
this._env = null;
this._context = null;
this._id = null;
const pts = arguments[0],
start = arguments[1],
end = arguments[2],
context = arguments[3];
this._pts = pts;
this._start = start;
this._end = end;
this._context = context;
}
getLineSegment(index, ls) {
ls.p0 = this._pts[index];
ls.p1 = this._pts[index + 1];
}
computeSelect(searchEnv, start0, end0, mcs) {
const p0 = this._pts[start0];
const p1 = this._pts[end0];
if (end0 - start0 === 1) {
mcs.select(this, start0);
return null;
}
if (!searchEnv.intersects(p0, p1)) return null;
const mid = Math.trunc((start0 + end0) / 2);
if (start0 < mid) this.computeSelect(searchEnv, start0, mid, mcs);
if (mid < end0) this.computeSelect(searchEnv, mid, end0, mcs);
}
getCoordinates() {
const coord = new Array(this._end - this._start + 1).fill(null);
let index = 0;
for (let i = this._start; i <= this._end; i++) coord[index++] = this._pts[i];
return coord;
}
computeOverlaps() {
if (arguments.length === 2) {
const mc = arguments[0],
mco = arguments[1];
this.computeOverlaps(this._start, this._end, mc, mc._start, mc._end, mco);
} else if (arguments.length === 6) {
const start0 = arguments[0],
end0 = arguments[1],
mc = arguments[2],
start1 = arguments[3],
end1 = arguments[4],
mco = arguments[5];
if (end0 - start0 === 1 && end1 - start1 === 1) {
mco.overlap(this, start0, mc, start1);
return null;
}
if (!this.overlaps(start0, end0, mc, start1, end1)) return null;
const mid0 = Math.trunc((start0 + end0) / 2);
const mid1 = Math.trunc((start1 + end1) / 2);
if (start0 < mid0) {
if (start1 < mid1) this.computeOverlaps(start0, mid0, mc, start1, mid1, mco);
if (mid1 < end1) this.computeOverlaps(start0, mid0, mc, mid1, end1, mco);
}
if (mid0 < end0) {
if (start1 < mid1) this.computeOverlaps(mid0, end0, mc, start1, mid1, mco);
if (mid1 < end1) this.computeOverlaps(mid0, end0, mc, mid1, end1, mco);
}
}
}
setId(id) {
this._id = id;
}
select(searchEnv, mcs) {
this.computeSelect(searchEnv, this._start, this._end, mcs);
}
getEnvelope() {
if (this._env === null) {
const p0 = this._pts[this._start];
const p1 = this._pts[this._end];
this._env = new Envelope(p0, p1);
}
return this._env;
}
overlaps(start0, end0, mc, start1, end1) {
return Envelope.intersects(this._pts[start0], this._pts[end0], mc._pts[start1], mc._pts[end1]);
}
getEndIndex() {
return this._end;
}
getStartIndex() {
return this._start;
}
getContext() {
return this._context;
}
getId() {
return this._id;
}
}
class MonotoneChainBuilder {
static findChainEnd(pts, start) {
let safeStart = start;
while (safeStart < pts.length - 1 && pts[safeStart].equals2D(pts[safeStart + 1])) safeStart++;
if (safeStart >= pts.length - 1) return pts.length - 1;
const chainQuad = Quadrant.quadrant(pts[safeStart], pts[safeStart + 1]);
let last = start + 1;
while (last < pts.length) {
if (!pts[last - 1].equals2D(pts[last])) {
const quad = Quadrant.quadrant(pts[last - 1], pts[last]);
if (quad !== chainQuad) break;
}
last++;
}
return last - 1;
}
static getChains() {
if (arguments.length === 1) {
const pts = arguments[0];
return MonotoneChainBuilder.getChains(pts, null);
} else if (arguments.length === 2) {
const pts = arguments[0],
context = arguments[1];
const mcList = new ArrayList();
let chainStart = 0;
do {
const chainEnd = MonotoneChainBuilder.findChainEnd(pts, chainStart);
const mc = new MonotoneChain(pts, chainStart, chainEnd, context);
mcList.add(mc);
chainStart = chainEnd;
} while (chainStart < pts.length - 1);
return mcList;
}
}
}
class Noder {
computeNodes(segStrings) {}
getNodedSubstrings() {}
}
class SinglePassNoder {
constructor() {
SinglePassNoder.constructor_.apply(this, arguments);
}
static constructor_() {
this._segInt = null;
if (arguments.length === 0) ; else if (arguments.length === 1) {
const segInt = arguments[0];
this.setSegmentIntersector(segInt);
}
}
setSegmentIntersector(segInt) {
this._segInt = segInt;
}
get interfaces_() {
return [Noder];
}
}
class MCIndexNoder extends SinglePassNoder {
constructor() {
super();
MCIndexNoder.constructor_.apply(this, arguments);
}
static constructor_() {
this._monoChains = new ArrayList();
this._index = new STRtree();
this._idCounter = 0;
this._nodedSegStrings = null;
this._nOverlaps = 0;
if (arguments.length === 0) ; else if (arguments.length === 1) {
const si = arguments[0];
SinglePassNoder.constructor_.call(this, si);
}
}
getMonotoneChains() {
return this._monoChains;
}
getNodedSubstrings() {
return NodedSegmentString.getNodedSubstrings(this._nodedSegStrings);
}
getIndex() {
return this._index;
}
add(segStr) {
const segChains = MonotoneChainBuilder.getChains(segStr.getCoordinates(), segStr);
for (let i = segChains.iterator(); i.hasNext();) {
const mc = i.next();
mc.setId(this._idCounter++);
this._index.insert(mc.getEnvelope(), mc);
this._monoChains.add(mc);
}
}
computeNodes(inputSegStrings) {
this._nodedSegStrings = inputSegStrings;
for (let i = inputSegStrings.iterator(); i.hasNext();) this.add(i.next());
this.intersectChains();
}
intersectChains() {
const overlapAction = new SegmentOverlapAction(this._segInt);
for (let i = this._monoChains.iterator(); i.hasNext();) {
const queryChain = i.next();
const overlapChains = this._index.query(queryChain.getEnvelope());
for (let j = overlapChains.iterator(); j.hasNext();) {
const testChain = j.next();
if (testChain.getId() > queryChain.getId()) {
queryChain.computeOverlaps(testChain, overlapAction);
this._nOverlaps++;
}
if (this._segInt.isDone()) return null;
}
}
}
}
class SegmentOverlapAction extends MonotoneChainOverlapAction {
constructor() {
super();
SegmentOverlapAction.constructor_.apply(this, arguments);
}
static constructor_() {
this._si = null;
const si = arguments[0];
this._si = si;
}
overlap() {
if (arguments.length === 4) {
const mc1 = arguments[0],
start1 = arguments[1],
mc2 = arguments[2],
start2 = arguments[3];
const ss1 = mc1.getContext();
const ss2 = mc2.getContext();
this._si.processIntersections(ss1, start1, ss2, start2);
} else {
return super.overlap.apply(this, arguments);
}
}
}
MCIndexNoder.SegmentOverlapAction = SegmentOverlapAction;
class ScaledNoder {
constructor() {
ScaledNoder.constructor_.apply(this, arguments);
}
static constructor_() {
this._noder = null;
this._scaleFactor = null;
this._offsetX = null;
this._offsetY = null;
this._isScaled = false;
if (arguments.length === 2) {
const noder = arguments[0],
scaleFactor = arguments[1];
ScaledNoder.constructor_.call(this, noder, scaleFactor, 0, 0);
} else if (arguments.length === 4) {
const noder = arguments[0],
scaleFactor = arguments[1];
this._noder = noder;
this._scaleFactor = scaleFactor;
this._isScaled = !this.isIntegerPrecision();
}
}
rescale() {
if (hasInterface(arguments[0], Collection)) {
const segStrings = arguments[0];
for (let i = segStrings.iterator(); i.hasNext();) {
const ss = i.next();
this.rescale(ss.getCoordinates());
}
} else if (arguments[0] instanceof Array) {
const pts = arguments[0];
for (let i = 0; i < pts.length; i++) {
pts[i].x = pts[i].x / this._scaleFactor + this._offsetX;
pts[i].y = pts[i].y / this._scaleFactor + this._offsetY;
}
if (pts.length === 2 && pts[0].equals2D(pts[1])) System.out.println(pts);
}
}
scale() {
if (hasInterface(arguments[0], Collection)) {
const segStrings = arguments[0];
const nodedSegmentStrings = new ArrayList(segStrings.size());
for (let i = segStrings.iterator(); i.hasNext();) {
const ss = i.next();
nodedSegmentStrings.add(new NodedSegmentString(this.scale(ss.getCoordinates()), ss.getData()));
}
return nodedSegmentStrings;
} else if (arguments[0] instanceof Array) {
const pts = arguments[0];
const roundPts = new Array(pts.length).fill(null);
for (let i = 0; i < pts.length; i++) roundPts[i] = new Coordinate(Math.round((pts[i].x - this._offsetX) * this._scaleFactor), Math.round((pts[i].y - this._offsetY) * this._scaleFactor), pts[i].getZ());
const roundPtsNoDup = CoordinateArrays.removeRepeatedPoints(roundPts);
return roundPtsNoDup;
}
}
isIntegerPrecision() {
return this._scaleFactor === 1.0;
}
getNodedSubstrings() {
const splitSS = this._noder.getNodedSubstrings();
if (this._isScaled) this.rescale(splitSS);
return splitSS;
}
computeNodes(inputSegStrings) {
let intSegStrings = inputSegStrings;
if (this._isScaled) intSegStrings = this.scale(inputSegStrings);
this._noder.computeNodes(intSegStrings);
}
get interfaces_() {
return [Noder];
}
}
var noding = /*#__PURE__*/Object.freeze({
__proto__: null,
MCIndexNoder: MCIndexNoder,
ScaledNoder: ScaledNoder,
SegmentString: SegmentString
});
class BoundaryOp {
constructor() {
BoundaryOp.constructor_.apply(this, arguments);
}
static constructor_() {
this._geom = null;
this._geomFact = null;
this._bnRule = null;
this._endpointMap = null;
if (arguments.length === 1) {
const geom = arguments[0];
BoundaryOp.constructor_.call(this, geom, BoundaryNodeRule.MOD2_BOUNDARY_RULE);
} else if (arguments.length === 2) {
const geom = arguments[0],
bnRule = arguments[1];
this._geom = geom;
this._geomFact = geom.getFactory();
this._bnRule = bnRule;
}
}
static getBoundary() {
if (arguments.length === 1) {
const g = arguments[0];
const bop = new BoundaryOp(g);
return bop.getBoundary();
} else if (arguments.length === 2) {
const g = arguments[0],
bnRule = arguments[1];
const bop = new BoundaryOp(g, bnRule);
return bop.getBoundary();
}
}
boundaryMultiLineString(mLine) {
if (this._geom.isEmpty()) return this.getEmptyMultiPoint();
const bdyPts = this.computeBoundaryCoordinates(mLine);
if (bdyPts.length === 1) return this._geomFact.createPoint(bdyPts[0]);
return this._geomFact.createMultiPointFromCoords(bdyPts);
}
getBoundary() {
if (this._geom instanceof LineString) return this.boundaryLineString(this._geom);
if (this._geom instanceof MultiLineString) return this.boundaryMultiLineString(this._geom);
return this._geom.getBoundary();
}
boundaryLineString(line) {
if (this._geom.isEmpty()) return this.getEmptyMultiPoint();
if (line.isClosed()) {
const closedEndpointOnBoundary = this._bnRule.isInBoundary(2);
if (closedEndpointOnBoundary) return line.getStartPoint();else return this._geomFact.createMultiPoint();
}
return this._geomFact.createMultiPoint([line.getStartPoint(), line.getEndPoint()]);
}
getEmptyMultiPoint() {
return this._geomFact.createMultiPoint();
}
computeBoundaryCoordinates(mLine) {
const bdyPts = new ArrayList();
this._endpointMap = new TreeMap();
for (let i = 0; i < mLine.getNumGeometries(); i++) {
const line = mLine.getGeometryN(i);
if (line.getNumPoints() === 0) continue;
this.addEndpoint(line.getCoordinateN(0));
this.addEndpoint(line.getCoordinateN(line.getNumPoints() - 1));
}
for (let it = this._endpointMap.entrySet().iterator(); it.hasNext();) {
const entry = it.next();
const counter = entry.getValue();
const valence = counter.count;
if (this._bnRule.isInBoundary(valence)) bdyPts.add(entry.getKey());
}
return CoordinateArrays.toCoordinateArray(bdyPts);
}
addEndpoint(pt) {
let counter = this._endpointMap.get(pt);
if (counter === null) {
counter = new Counter$1();
this._endpointMap.put(pt, counter);
}
counter.count++;
}
}
class Counter$1 {
constructor() {
Counter$1.constructor_.apply(this, arguments);
}
static constructor_() {
this.count = null;
}
}
class IsSimpleOp {
constructor() {
IsSimpleOp.constructor_.apply(this, arguments);
}
static constructor_() {
this._inputGeom = null;
this._isClosedEndpointsInInterior = true;
this._nonSimpleLocation = null;
if (arguments.length === 1) {
const geom = arguments[0];
this._inputGeom = geom;
} else if (arguments.length === 2) {
const geom = arguments[0],
boundaryNodeRule = arguments[1];
this._inputGeom = geom;
this._isClosedEndpointsInInterior = !boundaryNodeRule.isInBoundary(2);
}
}
static isSimple() {
if (arguments.length === 1) {
const geom = arguments[0];
const op = new IsSimpleOp(geom);
return op.isSimple();
} else if (arguments.length === 2) {
const geom = arguments[0],
boundaryNodeRule = arguments[1];
const op = new IsSimpleOp(geom, boundaryNodeRule);
return op.isSimple();
}
}
isSimpleMultiPoint(mp) {
if (mp.isEmpty()) return true;
const points = new TreeSet();
for (let i = 0; i < mp.getNumGeometries(); i++) {
const pt = mp.getGeometryN(i);
const p = pt.getCoordinate();
if (points.contains(p)) {
this._nonSimpleLocation = p;
return false;
}
points.add(p);
}
return true;
}
isSimplePolygonal(geom) {
const rings = LinearComponentExtracter.getLines(geom);
for (let i = rings.iterator(); i.hasNext();) {
const ring = i.next();
if (!this.isSimpleLinearGeometry(ring)) return false;
}
return true;
}
hasClosedEndpointIntersection(graph) {
const endPoints = new TreeMap();
for (let i = graph.getEdgeIterator(); i.hasNext();) {
const e = i.next();
const isClosed = e.isClosed();
const p0 = e.getCoordinate(0);
this.addEndpoint(endPoints, p0, isClosed);
const p1 = e.getCoordinate(e.getNumPoints() - 1);
this.addEndpoint(endPoints, p1, isClosed);
}
for (let i = endPoints.values().iterator(); i.hasNext();) {
const eiInfo = i.next();
if (eiInfo.isClosed && eiInfo.degree !== 2) {
this._nonSimpleLocation = eiInfo.getCoordinate();
return true;
}
}
return false;
}
getNonSimpleLocation() {
return this._nonSimpleLocation;
}
isSimpleLinearGeometry(geom) {
if (geom.isEmpty()) return true;
const graph = new GeometryGraph(0, geom);
const li = new RobustLineIntersector();
const si = graph.computeSelfNodes(li, true);
if (!si.hasIntersection()) return true;
if (si.hasProperIntersection()) {
this._nonSimpleLocation = si.getProperIntersectionPoint();
return false;
}
if (this.hasNonEndpointIntersection(graph)) return false;
if (this._isClosedEndpointsInInterior) if (this.hasClosedEndpointIntersection(graph)) return false;
return true;
}
hasNonEndpointIntersection(graph) {
for (let i = graph.getEdgeIterator(); i.hasNext();) {
const e = i.next();
const maxSegmentIndex = e.getMaximumSegmentIndex();
for (let eiIt = e.getEdgeIntersectionList().iterator(); eiIt.hasNext();) {
const ei = eiIt.next();
if (!ei.isEndPoint(maxSegmentIndex)) {
this._nonSimpleLocation = ei.getCoordinate();
return true;
}
}
}
return false;
}
addEndpoint(endPoints, p, isClosed) {
let eiInfo = endPoints.get(p);
if (eiInfo === null) {
eiInfo = new EndpointInfo(p);
endPoints.put(p, eiInfo);
}
eiInfo.addEndpoint(isClosed);
}
computeSimple(geom) {
this._nonSimpleLocation = null;
if (geom.isEmpty()) 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._nonSimpleLocation = null;
return this.computeSimple(this._inputGeom);
}
isSimpleGeometryCollection(geom) {
for (let i = 0; i < geom.getNumGeometries(); i++) {
const comp = geom.getGeometryN(i);
if (!this.computeSimple(comp)) return false;
}
return true;
}
}
class EndpointInfo {
constructor() {
EndpointInfo.constructor_.apply(this, arguments);
}
static constructor_() {
this.pt = null;
this.isClosed = null;
this.degree = null;
const pt = arguments[0];
this.pt = pt;
this.isClosed = false;
this.degree = 0;
}
addEndpoint(isClosed) {
this.degree++;
this.isClosed |= isClosed;
}
getCoordinate() {
return this.pt;
}
}
IsSimpleOp.EndpointInfo = EndpointInfo;
class BufferParameters {
constructor() {
BufferParameters.constructor_.apply(this, arguments);
}
static constructor_() {
this._quadrantSegments = BufferParameters.DEFAULT_QUADRANT_SEGMENTS;
this._endCapStyle = BufferParameters.CAP_ROUND;
this._joinStyle = BufferParameters.JOIN_ROUND;
this._mitreLimit = BufferParameters.DEFAULT_MITRE_LIMIT;
this._isSingleSided = false;
this._simplifyFactor = BufferParameters.DEFAULT_SIMPLIFY_FACTOR;
if (arguments.length === 0) ; else if (arguments.length === 1) {
const quadrantSegments = arguments[0];
this.setQuadrantSegments(quadrantSegments);
} else if (arguments.length === 2) {
const quadrantSegments = arguments[0],
endCapStyle = arguments[1];
this.setQuadrantSegments(quadrantSegments);
this.setEndCapStyle(endCapStyle);
} else if (arguments.length === 4) {
const quadrantSegments = arguments[0],
endCapStyle = arguments[1],
joinStyle = arguments[2],
mitreLimit = arguments[3];
this.setQuadrantSegments(quadrantSegments);
this.setEndCapStyle(endCapStyle);
this.setJoinStyle(joinStyle);
this.setMitreLimit(mitreLimit);
}
}
static bufferDistanceError(quadSegs) {
const alpha = Math.PI / 2.0 / quadSegs;
return 1 - Math.cos(alpha / 2.0);
}
getEndCapStyle() {
return this._endCapStyle;
}
isSingleSided() {
return this._isSingleSided;
}
setQuadrantSegments(quadSegs) {
this._quadrantSegments = quadSegs;
if (this._quadrantSegments === 0) this._joinStyle = BufferParameters.JOIN_BEVEL;
if (this._quadrantSegments < 0) {
this._joinStyle = BufferParameters.JOIN_MITRE;
this._mitreLimit = Math.abs(this._quadrantSegments);
}
if (quadSegs <= 0) this._quadrantSegments = 1;
if (this._joinStyle !== BufferParameters.JOIN_ROUND) this._quadrantSegments = BufferParameters.DEFAULT_QUADRANT_SEGMENTS;
}
getJoinStyle() {
return this._joinStyle;
}
setJoinStyle(joinStyle) {
this._joinStyle = joinStyle;
}
setSimplifyFactor(simplifyFactor) {
this._simplifyFactor = simplifyFactor < 0 ? 0 : simplifyFactor;
}
getSimplifyFactor() {
return this._simplifyFactor;
}
getQuadrantSegments() {
return this._quadrantSegments;
}
setEndCapStyle(endCapStyle) {
this._endCapStyle = endCapStyle;
}
getMitreLimit() {
return this._mitreLimit;
}
setMitreLimit(mitreLimit) {
this._mitreLimit = mitreLimit;
}
setSingleSided(isSingleSided) {
this._isSingleSided = isSingleSided;
}
}
BufferParameters.CAP_ROUND = 1;
BufferParameters.CAP_FLAT = 2;
BufferParameters.CAP_SQUARE = 3;
BufferParameters.JOIN_ROUND = 1;
BufferParameters.JOIN_MITRE = 2;
BufferParameters.JOIN_BEVEL = 3;
BufferParameters.DEFAULT_QUADRANT_SEGMENTS = 8;
BufferParameters.DEFAULT_MITRE_LIMIT = 5.0;
BufferParameters.DEFAULT_SIMPLIFY_FACTOR = 0.01;
class RightmostEdgeFinder {
constructor() {
RightmostEdgeFinder.constructor_.apply(this, arguments);
}
static constructor_() {
this._minIndex = -1;
this._minCoord = null;
this._minDe = null;
this._orientedDe = null;
}
getCoordinate() {
return this._minCoord;
}
getRightmostSide(de, index) {
let side = this.getRightmostSideOfSegment(de, index);
if (side < 0) side = this.getRightmostSideOfSegment(de, index - 1);
if (side < 0) {
this._minCoord = null;
this.checkForRightmostCoordinate(de);
}
return side;
}
findRightmostEdgeAtVertex() {
const pts = this._minDe.getEdge().getCoordinates();
Assert.isTrue(this._minIndex > 0 && this._minIndex < pts.length, 'rightmost point expected to be interior vertex of edge');
const pPrev = pts[this._minIndex - 1];
const pNext = pts[this._minIndex + 1];
const orientation = Orientation.index(this._minCoord, pNext, pPrev);
let usePrev = false;
if (pPrev.y < this._minCoord.y && pNext.y < this._minCoord.y && orientation === Orientation.COUNTERCLOCKWISE) usePrev = true;else if (pPrev.y > this._minCoord.y && pNext.y > this._minCoord.y && orientation === Orientation.CLOCKWISE) usePrev = true;
if (usePrev) this._minIndex = this._minIndex - 1;
}
getRightmostSideOfSegment(de, i) {
const e = de.getEdge();
const coord = e.getCoordinates();
if (i < 0 || i + 1 >= coord.length) return -1;
if (coord[i].y === coord[i + 1].y) return -1;
let pos = Position.LEFT;
if (coord[i].y < coord[i + 1].y) pos = Position.RIGHT;
return pos;
}
getEdge() {
return this._orientedDe;
}
checkForRightmostCoordinate(de) {
const coord = de.getEdge().getCoordinates();
for (let i = 0; i < coord.length - 1; i++) if (this._minCoord === null || coord[i].x > this._minCoord.x) {
this._minDe = de;
this._minIndex = i;
this._minCoord = coord[i];
}
}
findRightmostEdgeAtNode() {
const node = this._minDe.getNode();
const star = node.getEdges();
this._minDe = star.getRightmostEdge();
if (!this._minDe.isForward()) {
this._minDe = this._minDe.getSym();
this._minIndex = this._minDe.getEdge().getCoordinates().length - 1;
}
}
findEdge(dirEdgeList) {
for (let i = dirEdgeList.iterator(); i.hasNext();) {
const de = i.next();
if (!de.isForward()) continue;
this.checkForRightmostCoordinate(de);
}
Assert.isTrue(this._minIndex !== 0 || this._minCoord.equals(this._minDe.getCoordinate()), 'inconsistency in rightmost processing');
if (this._minIndex === 0) this.findRightmostEdgeAtNode();else this.findRightmostEdgeAtVertex();
this._orientedDe = this._minDe;
const rightmostSide = this.getRightmostSide(this._minDe, this._minIndex);
if (rightmostSide === Position.LEFT) this._orientedDe = this._minDe.getSym();
}
}
class LinkedList {
constructor() {
this.array = [];
}
addLast(e) {
this.array.push(e);
}
removeFirst() {
return this.array.shift();
}
isEmpty() {
return this.array.length === 0;
}
}
class BufferSubgraph {
constructor() {
BufferSubgraph.constructor_.apply(this, arguments);
}
static constructor_() {
this._finder = null;
this._dirEdgeList = new ArrayList();
this._nodes = new ArrayList();
this._rightMostCoord = null;
this._env = null;
this._finder = new RightmostEdgeFinder();
}
clearVisitedEdges() {
for (let it = this._dirEdgeList.iterator(); it.hasNext();) {
const de = it.next();
de.setVisited(false);
}
}
getRightmostCoordinate() {
return this._rightMostCoord;
}
computeNodeDepth(n) {
let startEdge = null;
for (let i = n.getEdges().iterator(); i.hasNext();) {
const de = i.next();
if (de.isVisited() || de.getSym().isVisited()) {
startEdge = de;
break;
}
}
if (startEdge === null) throw new TopologyException('unable to find edge to compute depths at ' + n.getCoordinate());
n.getEdges().computeDepths(startEdge);
for (let i = n.getEdges().iterator(); i.hasNext();) {
const de = i.next();
de.setVisited(true);
this.copySymDepths(de);
}
}
computeDepth(outsideDepth) {
this.clearVisitedEdges();
const de = this._finder.getEdge();
de.getNode();
de.getLabel();
de.setEdgeDepths(Position.RIGHT, outsideDepth);
this.copySymDepths(de);
this.computeDepths(de);
}
create(node) {
this.addReachable(node);
this._finder.findEdge(this._dirEdgeList);
this._rightMostCoord = this._finder.getCoordinate();
}
findResultEdges() {
for (let it = this._dirEdgeList.iterator(); it.hasNext();) {
const de = it.next();
if (de.getDepth(Position.RIGHT) >= 1 && de.getDepth(Position.LEFT) <= 0 && !de.isInteriorAreaEdge()) de.setInResult(true);
}
}
computeDepths(startEdge) {
const nodesVisited = new HashSet();
const nodeQueue = new LinkedList();
const startNode = startEdge.getNode();
nodeQueue.addLast(startNode);
nodesVisited.add(startNode);
startEdge.setVisited(true);
while (!nodeQueue.isEmpty()) {
const n = nodeQueue.removeFirst();
nodesVisited.add(n);
this.computeNodeDepth(n);
for (let i = n.getEdges().iterator(); i.hasNext();) {
const de = i.next();
const sym = de.getSym();
if (sym.isVisited()) continue;
const adjNode = sym.getNode();
if (!nodesVisited.contains(adjNode)) {
nodeQueue.addLast(adjNode);
nodesVisited.add(adjNode);
}
}
}
}
compareTo(o) {
const graph = o;
if (this._rightMostCoord.x < graph._rightMostCoord.x) return -1;
if (this._rightMostCoord.x > graph._rightMostCoord.x) return 1;
return 0;
}
getEnvelope() {
if (this._env === null) {
const edgeEnv = new Envelope();
for (let it = this._dirEdgeList.iterator(); it.hasNext();) {
const dirEdge = it.next();
const pts = dirEdge.getEdge().getCoordinates();
for (let i = 0; i < pts.length - 1; i++) edgeEnv.expandToInclude(pts[i]);
}
this._env = edgeEnv;
}
return this._env;
}
addReachable(startNode) {
const nodeStack = new Stack();
nodeStack.add(startNode);
while (!nodeStack.empty()) {
const node = nodeStack.pop();
this.add(node, nodeStack);
}
}
copySymDepths(de) {
const sym = de.getSym();
sym.setDepth(Position.LEFT, de.getDepth(Position.RIGHT));
sym.setDepth(Position.RIGHT, de.getDepth(Position.LEFT));
}
add(node, nodeStack) {
node.setVisited(true);
this._nodes.add(node);
for (let i = node.getEdges().iterator(); i.hasNext();) {
const de = i.next();
this._dirEdgeList.add(de);
const sym = de.getSym();
const symNode = sym.getNode();
if (!symNode.isVisited()) nodeStack.push(symNode);
}
}
getNodes() {
return this._nodes;
}
getDirectedEdges() {
return this._dirEdgeList;
}
get interfaces_() {
return [Comparable];
}
}
class EdgeRing$1 {
constructor() {
EdgeRing$1.constructor_.apply(this, arguments);
}
static constructor_() {
this._startDe = null;
this._maxNodeDegree = -1;
this._edges = new ArrayList();
this._pts = new ArrayList();
this._label = new Label(Location.NONE);
this._ring = null;
this._isHole = null;
this._shell = null;
this._holes = new ArrayList();
this._geometryFactory = null;
if (arguments.length === 0) ; else if (arguments.length === 2) {
const start = arguments[0],
geometryFactory = arguments[1];
this._geometryFactory = geometryFactory;
this.computePoints(start);
this.computeRing();
}
}
computeRing() {
if (this._ring !== null) return null;
const coord = new Array(this._pts.size()).fill(null);
for (let i = 0; i < this._pts.size(); i++) coord[i] = this._pts.get(i);
this._ring = this._geometryFactory.createLinearRing(coord);
this._isHole = Orientation.isCCW(this._ring.getCoordinates());
}
isIsolated() {
return this._label.getGeometryCount() === 1;
}
computePoints(start) {
this._startDe = start;
let de = start;
let isFirstEdge = true;
do {
if (de === null) throw new TopologyException('Found null DirectedEdge');
if (de.getEdgeRing() === this) throw new TopologyException('Directed Edge visited twice during ring-building at ' + de.getCoordinate());
this._edges.add(de);
const label = de.getLabel();
Assert.isTrue(label.isArea());
this.mergeLabel(label);
this.addPoints(de.getEdge(), de.isForward(), isFirstEdge);
isFirstEdge = false;
this.setEdgeRing(de, this);
de = this.getNext(de);
} while (de !== this._startDe);
}
getLinearRing() {
return this._ring;
}
getCoordinate(i) {
return this._pts.get(i);
}
computeMaxNodeDegree() {
this._maxNodeDegree = 0;
let de = this._startDe;
do {
const node = de.getNode();
const degree = node.getEdges().getOutgoingDegree(this);
if (degree > this._maxNodeDegree) this._maxNodeDegree = degree;
de = this.getNext(de);
} while (de !== this._startDe);
this._maxNodeDegree *= 2;
}
addPoints(edge, isForward, isFirstEdge) {
const edgePts = edge.getCoordinates();
if (isForward) {
let startIndex = 1;
if (isFirstEdge) startIndex = 0;
for (let i = startIndex; i < edgePts.length; i++) this._pts.add(edgePts[i]);
} else {
let startIndex = edgePts.length - 2;
if (isFirstEdge) startIndex = edgePts.length - 1;
for (let i = startIndex; i >= 0; i--) this._pts.add(edgePts[i]);
}
}
isHole() {
return this._isHole;
}
setInResult() {
let de = this._startDe;
do {
de.getEdge().setInResult(true);
de = de.getNext();
} while (de !== this._startDe);
}
containsPoint(p) {
const shell = this.getLinearRing();
const env = shell.getEnvelopeInternal();
if (!env.contains(p)) return false;
if (!PointLocation.isInRing(p, shell.getCoordinates())) return false;
for (let i = this._holes.iterator(); i.hasNext();) {
const hole = i.next();
if (hole.containsPoint(p)) return false;
}
return true;
}
addHole(ring) {
this._holes.add(ring);
}
isShell() {
return this._shell === null;
}
getLabel() {
return this._label;
}
getEdges() {
return this._edges;
}
getMaxNodeDegree() {
if (this._maxNodeDegree < 0) this.computeMaxNodeDegree();
return this._maxNodeDegree;
}
getShell() {
return this._shell;
}
mergeLabel() {
if (arguments.length === 1) {
const deLabel = arguments[0];
this.mergeLabel(deLabel, 0);
this.mergeLabel(deLabel, 1);
} else if (arguments.length === 2) {
const deLabel = arguments[0],
geomIndex = arguments[1];
const loc = deLabel.getLocation(geomIndex, Position.RIGHT);
if (loc === Location.NONE) return null;
if (this._label.getLocation(geomIndex) === Location.NONE) {
this._label.setLocation(geomIndex, loc);
return null;
}
}
}
setShell(shell) {
this._shell = shell;
if (shell !== null) shell.addHole(this);
}
toPolygon(geometryFactory) {
const holeLR = new Array(this._holes.size()).fill(null);
for (let i = 0; i < this._holes.size(); i++) holeLR[i] = this._holes.get(i).getLinearRing();
const poly = geometryFactory.createPolygon(this.getLinearRing(), holeLR);
return poly;
}
}
class MinimalEdgeRing extends EdgeRing$1 {
constructor() {
super();
MinimalEdgeRing.constructor_.apply(this, arguments);
}
static constructor_() {
const start = arguments[0],
geometryFactory = arguments[1];
EdgeRing$1.constructor_.call(this, start, geometryFactory);
}
setEdgeRing(de, er) {
de.setMinEdgeRing(er);
}
getNext(de) {
return de.getNextMin();
}
}
class MaximalEdgeRing extends EdgeRing$1 {
constructor() {
super();
MaximalEdgeRing.constructor_.apply(this, arguments);
}
static constructor_() {
const start = arguments[0],
geometryFactory = arguments[1];
EdgeRing$1.constructor_.call(this, start, geometryFactory);
}
buildMinimalRings() {
const minEdgeRings = new ArrayList();
let de = this._startDe;
do {
if (de.getMinEdgeRing() === null) {
const minEr = new MinimalEdgeRing(de, this._geometryFactory);
minEdgeRings.add(minEr);
}
de = de.getNext();
} while (de !== this._startDe);
return minEdgeRings;
}
setEdgeRing(de, er) {
de.setEdgeRing(er);
}
linkDirectedEdgesForMinimalEdgeRings() {
let de = this._startDe;
do {
const node = de.getNode();
node.getEdges().linkMinimalDirectedEdges(this);
de = de.getNext();
} while (de !== this._startDe);
}
getNext(de) {
return de.getNext();
}
}
class PolygonBuilder {
constructor() {
PolygonBuilder.constructor_.apply(this, arguments);
}
static constructor_() {
this._geometryFactory = null;
this._shellList = new ArrayList();
const geometryFactory = arguments[0];
this._geometryFactory = geometryFactory;
}
static findEdgeRingContaining(testEr, shellList) {
const testRing = testEr.getLinearRing();
const testEnv = testRing.getEnvelopeInternal();
let testPt = testRing.getCoordinateN(0);
let minShell = null;
let minShellEnv = null;
for (let it = shellList.iterator(); it.hasNext();) {
const tryShell = it.next();
const tryShellRing = tryShell.getLinearRing();
const tryShellEnv = tryShellRing.getEnvelopeInternal();
if (tryShellEnv.equals(testEnv)) continue;
if (!tryShellEnv.contains(testEnv)) continue;
testPt = CoordinateArrays.ptNotInList(testRing.getCoordinates(), tryShellRing.getCoordinates());
let isContained = false;
if (PointLocation.isInRing(testPt, tryShellRing.getCoordinates())) isContained = true;
if (isContained) if (minShell === null || minShellEnv.contains(tryShellEnv)) {
minShell = tryShell;
minShellEnv = minShell.getLinearRing().getEnvelopeInternal();
}
}
return minShell;
}
sortShellsAndHoles(edgeRings, shellList, freeHoleList) {
for (let it = edgeRings.iterator(); it.hasNext();) {
const er = it.next();
if (er.isHole()) freeHoleList.add(er);else shellList.add(er);
}
}
computePolygons(shellList) {
const resultPolyList = new ArrayList();
for (let it = shellList.iterator(); it.hasNext();) {
const er = it.next();
const poly = er.toPolygon(this._geometryFactory);
resultPolyList.add(poly);
}
return resultPolyList;
}
placeFreeHoles(shellList, freeHoleList) {
for (let it = freeHoleList.iterator(); it.hasNext();) {
const hole = it.next();
if (hole.getShell() === null) {
const shell = PolygonBuilder.findEdgeRingContaining(hole, shellList);
if (shell === null) throw new TopologyException('unable to assign hole to a shell', hole.getCoordinate(0));
hole.setShell(shell);
}
}
}
buildMinimalEdgeRings(maxEdgeRings, shellList, freeHoleList) {
const edgeRings = new ArrayList();
for (let it = maxEdgeRings.iterator(); it.hasNext();) {
const er = it.next();
if (er.getMaxNodeDegree() > 2) {
er.linkDirectedEdgesForMinimalEdgeRings();
const minEdgeRings = er.buildMinimalRings();
const shell = this.findShell(minEdgeRings);
if (shell !== null) {
this.placePolygonHoles(shell, minEdgeRings);
shellList.add(shell);
} else {
freeHoleList.addAll(minEdgeRings);
}
} else {
edgeRings.add(er);
}
}
return edgeRings;
}
buildMaximalEdgeRings(dirEdges) {
const maxEdgeRings = new ArrayList();
for (let it = dirEdges.iterator(); it.hasNext();) {
const de = it.next();
if (de.isInResult() && de.getLabel().isArea()) if (de.getEdgeRing() === null) {
const er = new MaximalEdgeRing(de, this._geometryFactory);
maxEdgeRings.add(er);
er.setInResult();
}
}
return maxEdgeRings;
}
placePolygonHoles(shell, minEdgeRings) {
for (let it = minEdgeRings.iterator(); it.hasNext();) {
const er = it.next();
if (er.isHole()) er.setShell(shell);
}
}
getPolygons() {
const resultPolyList = this.computePolygons(this._shellList);
return resultPolyList;
}
findShell(minEdgeRings) {
let shellCount = 0;
let shell = null;
for (let it = minEdgeRings.iterator(); it.hasNext();) {
const er = it.next();
if (!er.isHole()) {
shell = er;
shellCount++;
}
}
Assert.isTrue(shellCount <= 1, 'found two shells in MinimalEdgeRing list');
return shell;
}
add() {
if (arguments.length === 1) {
const graph = arguments[0];
this.add(graph.getEdgeEnds(), graph.getNodes());
} else if (arguments.length === 2) {
const dirEdges = arguments[0],
nodes = arguments[1];
PlanarGraph$1.linkResultDirectedEdges(nodes);
const maxEdgeRings = this.buildMaximalEdgeRings(dirEdges);
const freeHoleList = new ArrayList();
const edgeRings = this.buildMinimalEdgeRings(maxEdgeRings, this._shellList, freeHoleList);
this.sortShellsAndHoles(edgeRings, this._shellList, freeHoleList);
this.placeFreeHoles(this._shellList, freeHoleList);
}
}
}
class BufferInputLineSimplifier {
constructor() {
BufferInputLineSimplifier.constructor_.apply(this, arguments);
}
static constructor_() {
this._inputLine = null;
this._distanceTol = null;
this._isDeleted = null;
this._angleOrientation = Orientation.COUNTERCLOCKWISE;
const inputLine = arguments[0];
this._inputLine = inputLine;
}
static simplify(inputLine, distanceTol) {
const simp = new BufferInputLineSimplifier(inputLine);
return simp.simplify(distanceTol);
}
isDeletable(i0, i1, i2, distanceTol) {
const p0 = this._inputLine[i0];
const p1 = this._inputLine[i1];
const p2 = this._inputLine[i2];
if (!this.isConcave(p0, p1, p2)) return false;
if (!this.isShallow(p0, p1, p2, distanceTol)) return false;
return this.isShallowSampled(p0, p1, i0, i2, distanceTol);
}
deleteShallowConcavities() {
let index = 1;
let midIndex = this.findNextNonDeletedIndex(index);
let lastIndex = this.findNextNonDeletedIndex(midIndex);
let isChanged = false;
while (lastIndex < this._inputLine.length) {
let isMiddleVertexDeleted = false;
if (this.isDeletable(index, midIndex, lastIndex, this._distanceTol)) {
this._isDeleted[midIndex] = BufferInputLineSimplifier.DELETE;
isMiddleVertexDeleted = true;
isChanged = true;
}
if (isMiddleVertexDeleted) index = lastIndex;else index = midIndex;
midIndex = this.findNextNonDeletedIndex(index);
lastIndex = this.findNextNonDeletedIndex(midIndex);
}
return isChanged;
}
isShallowConcavity(p0, p1, p2, distanceTol) {
const orientation = Orientation.index(p0, p1, p2);
const isAngleToSimplify = orientation === this._angleOrientation;
if (!isAngleToSimplify) return false;
const dist = Distance.pointToSegment(p1, p0, p2);
return dist < distanceTol;
}
isShallowSampled(p0, p2, i0, i2, distanceTol) {
let inc = Math.trunc((i2 - i0) / BufferInputLineSimplifier.NUM_PTS_TO_CHECK);
if (inc <= 0) inc = 1;
for (let i = i0; i < i2; i += inc) if (!this.isShallow(p0, p2, this._inputLine[i], distanceTol)) return false;
return true;
}
isConcave(p0, p1, p2) {
const orientation = Orientation.index(p0, p1, p2);
const isConcave = orientation === this._angleOrientation;
return isConcave;
}
simplify(distanceTol) {
this._distanceTol = Math.abs(distanceTol);
if (distanceTol < 0) this._angleOrientation = Orientation.CLOCKWISE;
this._isDeleted = new Array(this._inputLine.length).fill(null);
let isChanged = false;
do isChanged = this.deleteShallowConcavities(); while (isChanged);
return this.collapseLine();
}
findNextNonDeletedIndex(index) {
let next = index + 1;
while (next < this._inputLine.length && this._isDeleted[next] === BufferInputLineSimplifier.DELETE) next++;
return next;
}
isShallow(p0, p1, p2, distanceTol) {
const dist = Distance.pointToSegment(p1, p0, p2);
return dist < distanceTol;
}
collapseLine() {
const coordList = new CoordinateList();
for (let i = 0; i < this._inputLine.length; i++) if (this._isDeleted[i] !== BufferInputLineSimplifier.DELETE) coordList.add(this._inputLine[i]);
return coordList.toCoordinateArray();
}
}
BufferInputLineSimplifier.INIT = 0;
BufferInputLineSimplifier.DELETE = 1;
BufferInputLineSimplifier.KEEP = 1;
BufferInputLineSimplifier.NUM_PTS_TO_CHECK = 10;
class OffsetSegmentString {
constructor() {
OffsetSegmentString.constructor_.apply(this, arguments);
}
static constructor_() {
this._ptList = null;
this._precisionModel = null;
this._minimimVertexDistance = 0.0;
this._ptList = new ArrayList();
}
getCoordinates() {
const coord = this._ptList.toArray(OffsetSegmentString.COORDINATE_ARRAY_TYPE);
return coord;
}
setPrecisionModel(precisionModel) {
this._precisionModel = precisionModel;
}
addPt(pt) {
const bufPt = new Coordinate(pt);
this._precisionModel.makePrecise(bufPt);
if (this.isRedundant(bufPt)) return null;
this._ptList.add(bufPt);
}
reverse() {}
addPts(pt, isForward) {
if (isForward) for (let i = 0; i < pt.length; i++) this.addPt(pt[i]);else for (let i = pt.length - 1; i >= 0; i--) this.addPt(pt[i]);
}
isRedundant(pt) {
if (this._ptList.size() < 1) return false;
const lastPt = this._ptList.get(this._ptList.size() - 1);
const ptDist = pt.distance(lastPt);
if (ptDist < this._minimimVertexDistance) return true;
return false;
}
toString() {
const fact = new GeometryFactory();
const line = fact.createLineString(this.getCoordinates());
return line.toString();
}
closeRing() {
if (this._ptList.size() < 1) return null;
const startPt = new Coordinate(this._ptList.get(0));
const lastPt = this._ptList.get(this._ptList.size() - 1);
if (startPt.equals(lastPt)) return null;
this._ptList.add(startPt);
}
setMinimumVertexDistance(minimimVertexDistance) {
this._minimimVertexDistance = minimimVertexDistance;
}
}
OffsetSegmentString.COORDINATE_ARRAY_TYPE = new Array(0).fill(null);
class OffsetSegmentGenerator {
constructor() {
OffsetSegmentGenerator.constructor_.apply(this, arguments);
}
static constructor_() {
this._maxCurveSegmentError = 0.0;
this._filletAngleQuantum = null;
this._closingSegLengthFactor = 1;
this._segList = null;
this._distance = 0.0;
this._precisionModel = null;
this._bufParams = null;
this._li = null;
this._s0 = null;
this._s1 = null;
this._s2 = null;
this._seg0 = new LineSegment();
this._seg1 = new LineSegment();
this._offset0 = new LineSegment();
this._offset1 = new LineSegment();
this._side = 0;
this._hasNarrowConcaveAngle = false;
const precisionModel = arguments[0],
bufParams = arguments[1],
distance = arguments[2];
this._precisionModel = precisionModel;
this._bufParams = bufParams;
this._li = new RobustLineIntersector();
this._filletAngleQuantum = Math.PI / 2.0 / bufParams.getQuadrantSegments();
if (bufParams.getQuadrantSegments() >= 8 && bufParams.getJoinStyle() === BufferParameters.JOIN_ROUND) this._closingSegLengthFactor = OffsetSegmentGenerator.MAX_CLOSING_SEG_LEN_FACTOR;
this.init(distance);
}
addNextSegment(p, addStartPoint) {
this._s0 = this._s1;
this._s1 = this._s2;
this._s2 = p;
this._seg0.setCoordinates(this._s0, this._s1);
this.computeOffsetSegment(this._seg0, this._side, this._distance, this._offset0);
this._seg1.setCoordinates(this._s1, this._s2);
this.computeOffsetSegment(this._seg1, this._side, this._distance, this._offset1);
if (this._s1.equals(this._s2)) return null;
const orientation = Orientation.index(this._s0, this._s1, this._s2);
const outsideTurn = orientation === Orientation.CLOCKWISE && this._side === Position.LEFT || orientation === Orientation.COUNTERCLOCKWISE && this._side === Position.RIGHT;
if (orientation === 0) this.addCollinear(addStartPoint);else if (outsideTurn) this.addOutsideTurn(orientation, addStartPoint);else this.addInsideTurn(orientation, addStartPoint);
}
addLineEndCap(p0, p1) {
const seg = new LineSegment(p0, p1);
const offsetL = new LineSegment();
this.computeOffsetSegment(seg, Position.LEFT, this._distance, offsetL);
const offsetR = new LineSegment();
this.computeOffsetSegment(seg, Position.RIGHT, this._distance, offsetR);
const dx = p1.x - p0.x;
const dy = p1.y - p0.y;
const angle = Math.atan2(dy, dx);
switch (this._bufParams.getEndCapStyle()) {
case BufferParameters.CAP_ROUND:
this._segList.addPt(offsetL.p1);
this.addDirectedFillet(p1, angle + Math.PI / 2, angle - Math.PI / 2, Orientation.CLOCKWISE, this._distance);
this._segList.addPt(offsetR.p1);
break;
case BufferParameters.CAP_FLAT:
this._segList.addPt(offsetL.p1);
this._segList.addPt(offsetR.p1);
break;
case BufferParameters.CAP_SQUARE:
const squareCapSideOffset = new Coordinate();
squareCapSideOffset.x = Math.abs(this._distance) * Math.cos(angle);
squareCapSideOffset.y = Math.abs(this._distance) * Math.sin(angle);
const squareCapLOffset = new Coordinate(offsetL.p1.x + squareCapSideOffset.x, offsetL.p1.y + squareCapSideOffset.y);
const squareCapROffset = new Coordinate(offsetR.p1.x + squareCapSideOffset.x, offsetR.p1.y + squareCapSideOffset.y);
this._segList.addPt(squareCapLOffset);
this._segList.addPt(squareCapROffset);
break;
}
}
getCoordinates() {
const pts = this._segList.getCoordinates();
return pts;
}
addMitreJoin(p, offset0, offset1, distance) {
const intPt = Intersection.intersection(offset0.p0, offset0.p1, offset1.p0, offset1.p1);
if (intPt !== null) {
const mitreRatio = distance <= 0.0 ? 1.0 : intPt.distance(p) / Math.abs(distance);
if (mitreRatio <= this._bufParams.getMitreLimit()) {
this._segList.addPt(intPt);
return null;
}
}
this.addLimitedMitreJoin(offset0, offset1, distance, this._bufParams.getMitreLimit());
}
addOutsideTurn(orientation, addStartPoint) {
if (this._offset0.p1.distance(this._offset1.p0) < this._distance * OffsetSegmentGenerator.OFFSET_SEGMENT_SEPARATION_FACTOR) {
this._segList.addPt(this._offset0.p1);
return null;
}
if (this._bufParams.getJoinStyle() === BufferParameters.JOIN_MITRE) {
this.addMitreJoin(this._s1, this._offset0, this._offset1, this._distance);
} else if (this._bufParams.getJoinStyle() === BufferParameters.JOIN_BEVEL) {
this.addBevelJoin(this._offset0, this._offset1);
} else {
if (addStartPoint) this._segList.addPt(this._offset0.p1);
this.addCornerFillet(this._s1, this._offset0.p1, this._offset1.p0, orientation, this._distance);
this._segList.addPt(this._offset1.p0);
}
}
createSquare(p) {
this._segList.addPt(new Coordinate(p.x + this._distance, p.y + this._distance));
this._segList.addPt(new Coordinate(p.x + this._distance, p.y - this._distance));
this._segList.addPt(new Coordinate(p.x - this._distance, p.y - this._distance));
this._segList.addPt(new Coordinate(p.x - this._distance, p.y + this._distance));
this._segList.closeRing();
}
addSegments(pt, isForward) {
this._segList.addPts(pt, isForward);
}
addFirstSegment() {
this._segList.addPt(this._offset1.p0);
}
addCornerFillet(p, p0, p1, direction, radius) {
const dx0 = p0.x - p.x;
const dy0 = p0.y - p.y;
let startAngle = Math.atan2(dy0, dx0);
const dx1 = p1.x - p.x;
const dy1 = p1.y - p.y;
const endAngle = Math.atan2(dy1, dx1);
if (direction === Orientation.CLOCKWISE) {
if (startAngle <= endAngle) startAngle += 2.0 * Math.PI;
} else {
if (startAngle >= endAngle) startAngle -= 2.0 * Math.PI;
}
this._segList.addPt(p0);
this.addDirectedFillet(p, startAngle, endAngle, direction, radius);
this._segList.addPt(p1);
}
addLastSegment() {
this._segList.addPt(this._offset1.p1);
}
initSideSegments(s1, s2, side) {
this._s1 = s1;
this._s2 = s2;
this._side = side;
this._seg1.setCoordinates(s1, s2);
this.computeOffsetSegment(this._seg1, side, this._distance, this._offset1);
}
addLimitedMitreJoin(offset0, offset1, distance, mitreLimit) {
const basePt = this._seg0.p1;
const ang0 = Angle.angle(basePt, this._seg0.p0);
const angDiff = Angle.angleBetweenOriented(this._seg0.p0, basePt, this._seg1.p1);
const angDiffHalf = angDiff / 2;
const midAng = Angle.normalize(ang0 + angDiffHalf);
const mitreMidAng = Angle.normalize(midAng + Math.PI);
const mitreDist = mitreLimit * distance;
const bevelDelta = mitreDist * Math.abs(Math.sin(angDiffHalf));
const bevelHalfLen = distance - bevelDelta;
const bevelMidX = basePt.x + mitreDist * Math.cos(mitreMidAng);
const bevelMidY = basePt.y + mitreDist * Math.sin(mitreMidAng);
const bevelMidPt = new Coordinate(bevelMidX, bevelMidY);
const mitreMidLine = new LineSegment(basePt, bevelMidPt);
const bevelEndLeft = mitreMidLine.pointAlongOffset(1.0, bevelHalfLen);
const bevelEndRight = mitreMidLine.pointAlongOffset(1.0, -bevelHalfLen);
if (this._side === Position.LEFT) {
this._segList.addPt(bevelEndLeft);
this._segList.addPt(bevelEndRight);
} else {
this._segList.addPt(bevelEndRight);
this._segList.addPt(bevelEndLeft);
}
}
addDirectedFillet(p, startAngle, endAngle, direction, radius) {
const directionFactor = direction === Orientation.CLOCKWISE ? -1 : 1;
const totalAngle = Math.abs(startAngle - endAngle);
const nSegs = Math.trunc(totalAngle / this._filletAngleQuantum + 0.5);
if (nSegs < 1) return null;
const angleInc = totalAngle / nSegs;
const pt = new Coordinate();
for (let i = 0; i < nSegs; i++) {
const angle = startAngle + directionFactor * i * angleInc;
pt.x = p.x + radius * Math.cos(angle);
pt.y = p.y + radius * Math.sin(angle);
this._segList.addPt(pt);
}
}
computeOffsetSegment(seg, side, distance, offset) {
const sideSign = side === Position.LEFT ? 1 : -1;
const dx = seg.p1.x - seg.p0.x;
const dy = seg.p1.y - seg.p0.y;
const len = Math.sqrt(dx * dx + dy * dy);
const ux = sideSign * distance * dx / len;
const uy = sideSign * distance * dy / len;
offset.p0.x = seg.p0.x - uy;
offset.p0.y = seg.p0.y + ux;
offset.p1.x = seg.p1.x - uy;
offset.p1.y = seg.p1.y + ux;
}
addInsideTurn(orientation, addStartPoint) {
this._li.computeIntersection(this._offset0.p0, this._offset0.p1, this._offset1.p0, this._offset1.p1);
if (this._li.hasIntersection()) {
this._segList.addPt(this._li.getIntersection(0));
} else {
this._hasNarrowConcaveAngle = true;
if (this._offset0.p1.distance(this._offset1.p0) < this._distance * OffsetSegmentGenerator.INSIDE_TURN_VERTEX_SNAP_DISTANCE_FACTOR) {
this._segList.addPt(this._offset0.p1);
} else {
this._segList.addPt(this._offset0.p1);
if (this._closingSegLengthFactor > 0) {
const mid0 = new Coordinate((this._closingSegLengthFactor * this._offset0.p1.x + this._s1.x) / (this._closingSegLengthFactor + 1), (this._closingSegLengthFactor * this._offset0.p1.y + this._s1.y) / (this._closingSegLengthFactor + 1));
this._segList.addPt(mid0);
const mid1 = new Coordinate((this._closingSegLengthFactor * this._offset1.p0.x + this._s1.x) / (this._closingSegLengthFactor + 1), (this._closingSegLengthFactor * this._offset1.p0.y + this._s1.y) / (this._closingSegLengthFactor + 1));
this._segList.addPt(mid1);
} else {
this._segList.addPt(this._s1);
}
this._segList.addPt(this._offset1.p0);
}
}
}
createCircle(p) {
const pt = new Coordinate(p.x + this._distance, p.y);
this._segList.addPt(pt);
this.addDirectedFillet(p, 0.0, 2.0 * Math.PI, -1, this._distance);
this._segList.closeRing();
}
addBevelJoin(offset0, offset1) {
this._segList.addPt(offset0.p1);
this._segList.addPt(offset1.p0);
}
init(distance) {
this._distance = distance;
this._maxCurveSegmentError = distance * (1 - Math.cos(this._filletAngleQuantum / 2.0));
this._segList = new OffsetSegmentString();
this._segList.setPrecisionModel(this._precisionModel);
this._segList.setMinimumVertexDistance(distance * OffsetSegmentGenerator.CURVE_VERTEX_SNAP_DISTANCE_FACTOR);
}
addCollinear(addStartPoint) {
this._li.computeIntersection(this._s0, this._s1, this._s1, this._s2);
const numInt = this._li.getIntersectionNum();
if (numInt >= 2) if (this._bufParams.getJoinStyle() === BufferParameters.JOIN_BEVEL || this._bufParams.getJoinStyle() === BufferParameters.JOIN_MITRE) {
if (addStartPoint) this._segList.addPt(this._offset0.p1);
this._segList.addPt(this._offset1.p0);
} else {
this.addCornerFillet(this._s1, this._offset0.p1, this._offset1.p0, Orientation.CLOCKWISE, this._distance);
}
}
closeRing() {
this._segList.closeRing();
}
hasNarrowConcaveAngle() {
return this._hasNarrowConcaveAngle;
}
}
OffsetSegmentGenerator.OFFSET_SEGMENT_SEPARATION_FACTOR = 1.0E-3;
OffsetSegmentGenerator.INSIDE_TURN_VERTEX_SNAP_DISTANCE_FACTOR = 1.0E-3;
OffsetSegmentGenerator.CURVE_VERTEX_SNAP_DISTANCE_FACTOR = 1.0E-6;
OffsetSegmentGenerator.MAX_CLOSING_SEG_LEN_FACTOR = 80;
class OffsetCurveBuilder {
constructor() {
OffsetCurveBuilder.constructor_.apply(this, arguments);
}
static constructor_() {
this._distance = 0.0;
this._precisionModel = null;
this._bufParams = null;
const precisionModel = arguments[0],
bufParams = arguments[1];
this._precisionModel = precisionModel;
this._bufParams = bufParams;
}
static copyCoordinates(pts) {
const copy = new Array(pts.length).fill(null);
for (let i = 0; i < copy.length; i++) copy[i] = new Coordinate(pts[i]);
return copy;
}
getOffsetCurve(inputPts, distance) {
this._distance = distance;
if (distance === 0.0) return null;
const isRightSide = distance < 0.0;
const posDistance = Math.abs(distance);
const segGen = this.getSegGen(posDistance);
if (inputPts.length <= 1) this.computePointCurve(inputPts[0], segGen);else this.computeOffsetCurve(inputPts, isRightSide, segGen);
const curvePts = segGen.getCoordinates();
if (isRightSide) CoordinateArrays.reverse(curvePts);
return curvePts;
}
computeSingleSidedBufferCurve(inputPts, isRightSide, segGen) {
const distTol = this.simplifyTolerance(this._distance);
if (isRightSide) {
segGen.addSegments(inputPts, true);
const simp2 = BufferInputLineSimplifier.simplify(inputPts, -distTol);
const n2 = simp2.length - 1;
segGen.initSideSegments(simp2[n2], simp2[n2 - 1], Position.LEFT);
segGen.addFirstSegment();
for (let i = n2 - 2; i >= 0; i--) segGen.addNextSegment(simp2[i], true);
} else {
segGen.addSegments(inputPts, false);
const simp1 = BufferInputLineSimplifier.simplify(inputPts, distTol);
const n1 = simp1.length - 1;
segGen.initSideSegments(simp1[0], simp1[1], Position.LEFT);
segGen.addFirstSegment();
for (let i = 2; i <= n1; i++) segGen.addNextSegment(simp1[i], true);
}
segGen.addLastSegment();
segGen.closeRing();
}
computeRingBufferCurve(inputPts, side, segGen) {
let distTol = this.simplifyTolerance(this._distance);
if (side === Position.RIGHT) distTol = -distTol;
const simp = BufferInputLineSimplifier.simplify(inputPts, distTol);
const n = simp.length - 1;
segGen.initSideSegments(simp[n - 1], simp[0], side);
for (let i = 1; i <= n; i++) {
const addStartPoint = i !== 1;
segGen.addNextSegment(simp[i], addStartPoint);
}
segGen.closeRing();
}
computeLineBufferCurve(inputPts, segGen) {
const distTol = this.simplifyTolerance(this._distance);
const simp1 = BufferInputLineSimplifier.simplify(inputPts, distTol);
const n1 = simp1.length - 1;
segGen.initSideSegments(simp1[0], simp1[1], Position.LEFT);
for (let i = 2; i <= n1; i++) segGen.addNextSegment(simp1[i], true);
segGen.addLastSegment();
segGen.addLineEndCap(simp1[n1 - 1], simp1[n1]);
const simp2 = BufferInputLineSimplifier.simplify(inputPts, -distTol);
const n2 = simp2.length - 1;
segGen.initSideSegments(simp2[n2], simp2[n2 - 1], Position.LEFT);
for (let i = n2 - 2; i >= 0; i--) segGen.addNextSegment(simp2[i], true);
segGen.addLastSegment();
segGen.addLineEndCap(simp2[1], simp2[0]);
segGen.closeRing();
}
computePointCurve(pt, segGen) {
switch (this._bufParams.getEndCapStyle()) {
case BufferParameters.CAP_ROUND:
segGen.createCircle(pt);
break;
case BufferParameters.CAP_SQUARE:
segGen.createSquare(pt);
break;
}
}
getLineCurve(inputPts, distance) {
this._distance = distance;
if (this.isLineOffsetEmpty(distance)) return null;
const posDistance = Math.abs(distance);
const segGen = this.getSegGen(posDistance);
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);
}
const lineCoord = segGen.getCoordinates();
return lineCoord;
}
getBufferParameters() {
return this._bufParams;
}
simplifyTolerance(bufDistance) {
return bufDistance * this._bufParams.getSimplifyFactor();
}
getRingCurve(inputPts, side, distance) {
this._distance = distance;
if (inputPts.length <= 2) return this.getLineCurve(inputPts, distance);
if (distance === 0.0) return OffsetCurveBuilder.copyCoordinates(inputPts);
const segGen = this.getSegGen(distance);
this.computeRingBufferCurve(inputPts, side, segGen);
return segGen.getCoordinates();
}
computeOffsetCurve(inputPts, isRightSide, segGen) {
const distTol = this.simplifyTolerance(this._distance);
if (isRightSide) {
const simp2 = BufferInputLineSimplifier.simplify(inputPts, -distTol);
const n2 = simp2.length - 1;
segGen.initSideSegments(simp2[n2], simp2[n2 - 1], Position.LEFT);
segGen.addFirstSegment();
for (let i = n2 - 2; i >= 0; i--) segGen.addNextSegment(simp2[i], true);
} else {
const simp1 = BufferInputLineSimplifier.simplify(inputPts, distTol);
const n1 = simp1.length - 1;
segGen.initSideSegments(simp1[0], simp1[1], Position.LEFT);
segGen.addFirstSegment();
for (let i = 2; i <= n1; i++) segGen.addNextSegment(simp1[i], true);
}
segGen.addLastSegment();
}
isLineOffsetEmpty(distance) {
if (distance === 0.0) return true;
if (distance < 0.0 && !this._bufParams.isSingleSided()) return true;
return false;
}
getSegGen(distance) {
return new OffsetSegmentGenerator(this._precisionModel, this._bufParams, distance);
}
}
class SubgraphDepthLocater {
constructor() {
SubgraphDepthLocater.constructor_.apply(this, arguments);
}
static constructor_() {
this._subgraphs = null;
this._seg = new LineSegment();
const subgraphs = arguments[0];
this._subgraphs = subgraphs;
}
findStabbedSegments() {
if (arguments.length === 1) {
const stabbingRayLeftPt = arguments[0];
const stabbedSegments = new ArrayList();
for (let i = this._subgraphs.iterator(); i.hasNext();) {
const bsg = i.next();
const env = bsg.getEnvelope();
if (stabbingRayLeftPt.y < env.getMinY() || stabbingRayLeftPt.y > env.getMaxY()) continue;
this.findStabbedSegments(stabbingRayLeftPt, bsg.getDirectedEdges(), stabbedSegments);
}
return stabbedSegments;
} else if (arguments.length === 3) {
if (hasInterface(arguments[2], List) && arguments[0] instanceof Coordinate && arguments[1] instanceof DirectedEdge$1) {
const stabbingRayLeftPt = arguments[0],
dirEdge = arguments[1],
stabbedSegments = arguments[2];
const pts = dirEdge.getEdge().getCoordinates();
for (let i = 0; i < pts.length - 1; i++) {
this._seg.p0 = pts[i];
this._seg.p1 = pts[i + 1];
if (this._seg.p0.y > this._seg.p1.y) this._seg.reverse();
const maxx = Math.max(this._seg.p0.x, this._seg.p1.x);
if (maxx < stabbingRayLeftPt.x) continue;
if (this._seg.isHorizontal()) continue;
if (stabbingRayLeftPt.y < this._seg.p0.y || stabbingRayLeftPt.y > this._seg.p1.y) continue;
if (Orientation.index(this._seg.p0, this._seg.p1, stabbingRayLeftPt) === Orientation.RIGHT) continue;
let depth = dirEdge.getDepth(Position.LEFT);
if (!this._seg.p0.equals(pts[i])) depth = dirEdge.getDepth(Position.RIGHT);
const ds = new DepthSegment(this._seg, depth);
stabbedSegments.add(ds);
}
} else if (hasInterface(arguments[2], List) && arguments[0] instanceof Coordinate && hasInterface(arguments[1], List)) {
const stabbingRayLeftPt = arguments[0],
dirEdges = arguments[1],
stabbedSegments = arguments[2];
for (let i = dirEdges.iterator(); i.hasNext();) {
const de = i.next();
if (!de.isForward()) continue;
this.findStabbedSegments(stabbingRayLeftPt, de, stabbedSegments);
}
}
}
}
getDepth(p) {
const stabbedSegments = this.findStabbedSegments(p);
if (stabbedSegments.size() === 0) return 0;
const ds = Collections.min(stabbedSegments);
return ds._leftDepth;
}
}
class DepthSegment {
constructor() {
DepthSegment.constructor_.apply(this, arguments);
}
static constructor_() {
this._upwardSeg = null;
this._leftDepth = null;
const seg = arguments[0],
depth = arguments[1];
this._upwardSeg = new LineSegment(seg);
this._leftDepth = depth;
}
compareTo(obj) {
const other = obj;
if (this._upwardSeg.minX() >= other._upwardSeg.maxX()) return 1;
if (this._upwardSeg.maxX() <= other._upwardSeg.minX()) return -1;
let orientIndex = this._upwardSeg.orientationIndex(other._upwardSeg);
if (orientIndex !== 0) return orientIndex;
orientIndex = -1 * other._upwardSeg.orientationIndex(this._upwardSeg);
if (orientIndex !== 0) return orientIndex;
return this._upwardSeg.compareTo(other._upwardSeg);
}
compareX(seg0, seg1) {
const compare0 = seg0.p0.compareTo(seg1.p0);
if (compare0 !== 0) return compare0;
return seg0.p1.compareTo(seg1.p1);
}
toString() {
return this._upwardSeg.toString();
}
get interfaces_() {
return [Comparable];
}
}
SubgraphDepthLocater.DepthSegment = DepthSegment;
class OffsetCurveSetBuilder {
constructor() {
OffsetCurveSetBuilder.constructor_.apply(this, arguments);
}
static constructor_() {
this._inputGeom = null;
this._distance = null;
this._curveBuilder = null;
this._curveList = new ArrayList();
const inputGeom = arguments[0],
distance = arguments[1],
curveBuilder = arguments[2];
this._inputGeom = inputGeom;
this._distance = distance;
this._curveBuilder = curveBuilder;
}
addRingSide(coord, offsetDistance, side, cwLeftLoc, cwRightLoc) {
if (offsetDistance === 0.0 && coord.length < LinearRing.MINIMUM_VALID_SIZE) return null;
let leftLoc = cwLeftLoc;
let rightLoc = cwRightLoc;
if (coord.length >= LinearRing.MINIMUM_VALID_SIZE && Orientation.isCCW(coord)) {
leftLoc = cwRightLoc;
rightLoc = cwLeftLoc;
side = Position.opposite(side);
}
const curve = this._curveBuilder.getRingCurve(coord, side, offsetDistance);
this.addCurve(curve, leftLoc, rightLoc);
}
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();
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 = CoordinateArrays.removeRepeatedPoints(shell.getCoordinates());
if (this._distance < 0.0 && this.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 = CoordinateArrays.removeRepeatedPoints(hole.getCoordinates());
if (this._distance > 0.0 && this.isErodedCompletely(hole, -this._distance)) continue;
this.addRingSide(holeCoord, offsetDistance, Position.opposite(offsetSide), Location.INTERIOR, Location.EXTERIOR);
}
}
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);
}
addLineString(line) {
if (this._curveBuilder.isLineOffsetEmpty(this._distance)) return null;
const coord = CoordinateArrays.removeRepeatedPoints(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.getGeometryType());
}
isErodedCompletely(ring, bufferDistance) {
const ringCoord = ring.getCoordinates();
if (ringCoord.length < 4) return bufferDistance < 0;
if (ringCoord.length === 4) return this.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;
}
addCollection(gc) {
for (let i = 0; i < gc.getNumGeometries(); i++) {
const g = gc.getGeometryN(i);
this.add(g);
}
}
}
class EdgeEndStar {
constructor() {
EdgeEndStar.constructor_.apply(this, arguments);
}
static constructor_() {
this._edgeMap = new TreeMap();
this._edgeList = null;
this._ptInAreaLocation = [Location.NONE, Location.NONE];
}
getNextCW(ee) {
this.getEdges();
const i = this._edgeList.indexOf(ee);
let iNextCW = i - 1;
if (i === 0) iNextCW = this._edgeList.size() - 1;
return this._edgeList.get(iNextCW);
}
propagateSideLabels(geomIndex) {
let startLoc = Location.NONE;
for (let it = this.iterator(); it.hasNext();) {
const e = it.next();
const label = e.getLabel();
if (label.isArea(geomIndex) && label.getLocation(geomIndex, Position.LEFT) !== Location.NONE) startLoc = label.getLocation(geomIndex, Position.LEFT);
}
if (startLoc === Location.NONE) return null;
let currLoc = startLoc;
for (let it = this.iterator(); it.hasNext();) {
const e = it.next();
const label = e.getLabel();
if (label.getLocation(geomIndex, Position.ON) === Location.NONE) label.setLocation(geomIndex, Position.ON, currLoc);
if (label.isArea(geomIndex)) {
const leftLoc = label.getLocation(geomIndex, Position.LEFT);
const rightLoc = label.getLocation(geomIndex, Position.RIGHT);
if (rightLoc !== Location.NONE) {
if (rightLoc !== currLoc) throw new TopologyException('side location conflict', e.getCoordinate());
if (leftLoc === Location.NONE) Assert.shouldNeverReachHere('found single null side (at ' + e.getCoordinate() + ')');
currLoc = leftLoc;
} else {
Assert.isTrue(label.getLocation(geomIndex, Position.LEFT) === Location.NONE, 'found single null side');
label.setLocation(geomIndex, Position.RIGHT, currLoc);
label.setLocation(geomIndex, Position.LEFT, currLoc);
}
}
}
}
getCoordinate() {
const it = this.iterator();
if (!it.hasNext()) return null;
const e = it.next();
return e.getCoordinate();
}
print(out) {
System.out.println('EdgeEndStar: ' + this.getCoordinate());
for (let it = this.iterator(); it.hasNext();) {
const e = it.next();
e.print(out);
}
}
isAreaLabelsConsistent(geomGraph) {
this.computeEdgeEndLabels(geomGraph.getBoundaryNodeRule());
return this.checkAreaLabelsConsistent(0);
}
checkAreaLabelsConsistent(geomIndex) {
const edges = this.getEdges();
if (edges.size() <= 0) return true;
const lastEdgeIndex = edges.size() - 1;
const startLabel = edges.get(lastEdgeIndex).getLabel();
const startLoc = startLabel.getLocation(geomIndex, Position.LEFT);
Assert.isTrue(startLoc !== Location.NONE, 'Found unlabelled area edge');
let currLoc = startLoc;
for (let it = this.iterator(); it.hasNext();) {
const e = it.next();
const label = e.getLabel();
Assert.isTrue(label.isArea(geomIndex), 'Found non-area edge');
const leftLoc = label.getLocation(geomIndex, Position.LEFT);
const rightLoc = label.getLocation(geomIndex, Position.RIGHT);
if (leftLoc === rightLoc) return false;
if (rightLoc !== currLoc) return false;
currLoc = leftLoc;
}
return true;
}
findIndex(eSearch) {
this.iterator();
for (let i = 0; i < this._edgeList.size(); i++) {
const e = this._edgeList.get(i);
if (e === eSearch) return i;
}
return -1;
}
iterator() {
return this.getEdges().iterator();
}
getEdges() {
if (this._edgeList === null) this._edgeList = new ArrayList(this._edgeMap.values());
return this._edgeList;
}
getLocation(geomIndex, p, geom) {
if (this._ptInAreaLocation[geomIndex] === Location.NONE) this._ptInAreaLocation[geomIndex] = SimplePointInAreaLocator.locate(p, geom[geomIndex].getGeometry());
return this._ptInAreaLocation[geomIndex];
}
toString() {
const buf = new StringBuffer();
buf.append('EdgeEndStar: ' + this.getCoordinate());
buf.append('\n');
for (let it = this.iterator(); it.hasNext();) {
const e = it.next();
buf.append(e);
buf.append('\n');
}
return buf.toString();
}
computeEdgeEndLabels(boundaryNodeRule) {
for (let it = this.iterator(); it.hasNext();) {
const ee = it.next();
ee.computeLabel(boundaryNodeRule);
}
}
computeLabelling(geomGraph) {
this.computeEdgeEndLabels(geomGraph[0].getBoundaryNodeRule());
this.propagateSideLabels(0);
this.propagateSideLabels(1);
const hasDimensionalCollapseEdge = [false, false];
for (let it = this.iterator(); it.hasNext();) {
const e = it.next();
const label = e.getLabel();
for (let geomi = 0; geomi < 2; geomi++) if (label.isLine(geomi) && label.getLocation(geomi) === Location.BOUNDARY) hasDimensionalCollapseEdge[geomi] = true;
}
for (let it = this.iterator(); it.hasNext();) {
const e = it.next();
const label = e.getLabel();
for (let geomi = 0; geomi < 2; geomi++) if (label.isAnyNull(geomi)) {
let loc = Location.NONE;
if (hasDimensionalCollapseEdge[geomi]) {
loc = Location.EXTERIOR;
} else {
const p = e.getCoordinate();
loc = this.getLocation(geomi, p, geomGraph);
}
label.setAllLocationsIfNull(geomi, loc);
}
}
}
getDegree() {
return this._edgeMap.size();
}
insertEdgeEnd(e, obj) {
this._edgeMap.put(e, obj);
this._edgeList = null;
}
}
class DirectedEdgeStar$1 extends EdgeEndStar {
constructor() {
super();
DirectedEdgeStar$1.constructor_.apply(this, arguments);
}
static constructor_() {
this._resultAreaEdgeList = null;
this._label = null;
this._SCANNING_FOR_INCOMING = 1;
this._LINKING_TO_OUTGOING = 2;
}
linkResultDirectedEdges() {
this.getResultAreaEdges();
let firstOut = null;
let incoming = null;
let state = this._SCANNING_FOR_INCOMING;
for (let i = 0; i < this._resultAreaEdgeList.size(); i++) {
const nextOut = this._resultAreaEdgeList.get(i);
const nextIn = nextOut.getSym();
if (!nextOut.getLabel().isArea()) continue;
if (firstOut === null && nextOut.isInResult()) firstOut = nextOut;
switch (state) {
case this._SCANNING_FOR_INCOMING:
if (!nextIn.isInResult()) continue;
incoming = nextIn;
state = this._LINKING_TO_OUTGOING;
break;
case this._LINKING_TO_OUTGOING:
if (!nextOut.isInResult()) continue;
incoming.setNext(nextOut);
state = this._SCANNING_FOR_INCOMING;
break;
}
}
if (state === this._LINKING_TO_OUTGOING) {
if (firstOut === null) throw new TopologyException('no outgoing dirEdge found', this.getCoordinate());
Assert.isTrue(firstOut.isInResult(), 'unable to link last incoming dirEdge');
incoming.setNext(firstOut);
}
}
insert(ee) {
const de = ee;
this.insertEdgeEnd(de, de);
}
getRightmostEdge() {
const edges = this.getEdges();
const size = edges.size();
if (size < 1) return null;
const de0 = edges.get(0);
if (size === 1) return de0;
const deLast = edges.get(size - 1);
const quad0 = de0.getQuadrant();
const quad1 = deLast.getQuadrant();
if (Quadrant.isNorthern(quad0) && Quadrant.isNorthern(quad1)) {
return de0;
} else if (!Quadrant.isNorthern(quad0) && !Quadrant.isNorthern(quad1)) {
return deLast;
} else {
if (de0.getDy() !== 0) return de0;else if (deLast.getDy() !== 0) return deLast;
}
Assert.shouldNeverReachHere('found two horizontal edges incident on node');
return null;
}
print(out) {
System.out.println('DirectedEdgeStar: ' + this.getCoordinate());
for (let it = this.iterator(); it.hasNext();) {
const de = it.next();
out.print('out ');
de.print(out);
out.println();
out.print('in ');
de.getSym().print(out);
out.println();
}
}
getResultAreaEdges() {
if (this._resultAreaEdgeList !== null) return this._resultAreaEdgeList;
this._resultAreaEdgeList = new ArrayList();
for (let it = this.iterator(); it.hasNext();) {
const de = it.next();
if (de.isInResult() || de.getSym().isInResult()) this._resultAreaEdgeList.add(de);
}
return this._resultAreaEdgeList;
}
updateLabelling(nodeLabel) {
for (let it = this.iterator(); it.hasNext();) {
const de = it.next();
const label = de.getLabel();
label.setAllLocationsIfNull(0, nodeLabel.getLocation(0));
label.setAllLocationsIfNull(1, nodeLabel.getLocation(1));
}
}
linkAllDirectedEdges() {
this.getEdges();
let prevOut = null;
let firstIn = null;
for (let i = this._edgeList.size() - 1; i >= 0; i--) {
const nextOut = this._edgeList.get(i);
const nextIn = nextOut.getSym();
if (firstIn === null) firstIn = nextIn;
if (prevOut !== null) nextIn.setNext(prevOut);
prevOut = nextOut;
}
firstIn.setNext(prevOut);
}
computeDepths() {
if (arguments.length === 1) {
const de = arguments[0];
const edgeIndex = this.findIndex(de);
const startDepth = de.getDepth(Position.LEFT);
const targetLastDepth = de.getDepth(Position.RIGHT);
const nextDepth = this.computeDepths(edgeIndex + 1, this._edgeList.size(), startDepth);
const lastDepth = this.computeDepths(0, edgeIndex, nextDepth);
if (lastDepth !== targetLastDepth) throw new TopologyException('depth mismatch at ' + de.getCoordinate());
} else if (arguments.length === 3) {
const startIndex = arguments[0],
endIndex = arguments[1],
startDepth = arguments[2];
let currDepth = startDepth;
for (let i = startIndex; i < endIndex; i++) {
const nextDe = this._edgeList.get(i);
nextDe.setEdgeDepths(Position.RIGHT, currDepth);
currDepth = nextDe.getDepth(Position.LEFT);
}
return currDepth;
}
}
mergeSymLabels() {
for (let it = this.iterator(); it.hasNext();) {
const de = it.next();
const label = de.getLabel();
label.merge(de.getSym().getLabel());
}
}
linkMinimalDirectedEdges(er) {
let firstOut = null;
let incoming = null;
let state = this._SCANNING_FOR_INCOMING;
for (let i = this._resultAreaEdgeList.size() - 1; i >= 0; i--) {
const nextOut = this._resultAreaEdgeList.get(i);
const nextIn = nextOut.getSym();
if (firstOut === null && nextOut.getEdgeRing() === er) firstOut = nextOut;
switch (state) {
case this._SCANNING_FOR_INCOMING:
if (nextIn.getEdgeRing() !== er) continue;
incoming = nextIn;
state = this._LINKING_TO_OUTGOING;
break;
case this._LINKING_TO_OUTGOING:
if (nextOut.getEdgeRing() !== er) continue;
incoming.setNextMin(nextOut);
state = this._SCANNING_FOR_INCOMING;
break;
}
}
if (state === this._LINKING_TO_OUTGOING) {
Assert.isTrue(firstOut !== null, 'found null for first outgoing dirEdge');
Assert.isTrue(firstOut.getEdgeRing() === er, 'unable to link last incoming dirEdge');
incoming.setNextMin(firstOut);
}
}
getOutgoingDegree() {
if (arguments.length === 0) {
let degree = 0;
for (let it = this.iterator(); it.hasNext();) {
const de = it.next();
if (de.isInResult()) degree++;
}
return degree;
} else if (arguments.length === 1) {
const er = arguments[0];
let degree = 0;
for (let it = this.iterator(); it.hasNext();) {
const de = it.next();
if (de.getEdgeRing() === er) degree++;
}
return degree;
}
}
getLabel() {
return this._label;
}
findCoveredLineEdges() {
let startLoc = Location.NONE;
for (let it = this.iterator(); it.hasNext();) {
const nextOut = it.next();
const nextIn = nextOut.getSym();
if (!nextOut.isLineEdge()) {
if (nextOut.isInResult()) {
startLoc = Location.INTERIOR;
break;
}
if (nextIn.isInResult()) {
startLoc = Location.EXTERIOR;
break;
}
}
}
if (startLoc === Location.NONE) return null;
let currLoc = startLoc;
for (let it = this.iterator(); it.hasNext();) {
const nextOut = it.next();
const nextIn = nextOut.getSym();
if (nextOut.isLineEdge()) {
nextOut.getEdge().setCovered(currLoc === Location.INTERIOR);
} else {
if (nextOut.isInResult()) currLoc = Location.EXTERIOR;
if (nextIn.isInResult()) currLoc = Location.INTERIOR;
}
}
}
computeLabelling(geom) {
super.computeLabelling.call(this, geom);
this._label = new Label(Location.NONE);
for (let it = this.iterator(); it.hasNext();) {
const ee = it.next();
const e = ee.getEdge();
const eLabel = e.getLabel();
for (let i = 0; i < 2; i++) {
const eLoc = eLabel.getLocation(i);
if (eLoc === Location.INTERIOR || eLoc === Location.BOUNDARY) this._label.setLocation(i, Location.INTERIOR);
}
}
}
}
class OverlayNodeFactory extends NodeFactory {
constructor() {
super();
}
createNode(coord) {
return new Node$2(coord, new DirectedEdgeStar$1());
}
}
class OrientedCoordinateArray {
constructor() {
OrientedCoordinateArray.constructor_.apply(this, arguments);
}
static constructor_() {
this._pts = null;
this._orientation = null;
const pts = arguments[0];
this._pts = pts;
this._orientation = OrientedCoordinateArray.orientation(pts);
}
static orientation(pts) {
return CoordinateArrays.increasingDirection(pts) === 1;
}
static compareOriented(pts1, orientation1, pts2, orientation2) {
const dir1 = orientation1 ? 1 : -1;
const dir2 = orientation2 ? 1 : -1;
const limit1 = orientation1 ? pts1.length : -1;
const limit2 = orientation2 ? pts2.length : -1;
let i1 = orientation1 ? 0 : pts1.length - 1;
let i2 = orientation2 ? 0 : pts2.length - 1;
while (true) {
const compPt = pts1[i1].compareTo(pts2[i2]);
if (compPt !== 0) return compPt;
i1 += dir1;
i2 += dir2;
const done1 = i1 === limit1;
const done2 = i2 === limit2;
if (done1 && !done2) return -1;
if (!done1 && done2) return 1;
if (done1 && done2) return 0;
}
}
compareTo(o1) {
const oca = o1;
const comp = OrientedCoordinateArray.compareOriented(this._pts, this._orientation, oca._pts, oca._orientation);
return comp;
}
get interfaces_() {
return [Comparable];
}
}
class EdgeList {
constructor() {
EdgeList.constructor_.apply(this, arguments);
}
static constructor_() {
this._edges = new ArrayList();
this._ocaMap = new TreeMap();
}
print(out) {
out.print('MULTILINESTRING ( ');
for (let j = 0; j < this._edges.size(); j++) {
const e = this._edges.get(j);
if (j > 0) out.print(',');
out.print('(');
const pts = e.getCoordinates();
for (let i = 0; i < pts.length; i++) {
if (i > 0) out.print(',');
out.print(pts[i].x + ' ' + pts[i].y);
}
out.println(')');
}
out.print(') ');
}
addAll(edgeColl) {
for (let i = edgeColl.iterator(); i.hasNext();) this.add(i.next());
}
findEdgeIndex(e) {
for (let i = 0; i < this._edges.size(); i++) if (this._edges.get(i).equals(e)) return i;
return -1;
}
iterator() {
return this._edges.iterator();
}
getEdges() {
return this._edges;
}
get(i) {
return this._edges.get(i);
}
findEqualEdge(e) {
const oca = new OrientedCoordinateArray(e.getCoordinates());
const matchEdge = this._ocaMap.get(oca);
return matchEdge;
}
add(e) {
this._edges.add(e);
const oca = new OrientedCoordinateArray(e.getCoordinates());
this._ocaMap.put(oca, e);
}
}
class SegmentIntersector {
processIntersections(e0, segIndex0, e1, segIndex1) {}
isDone() {}
}
class IntersectionAdder {
constructor() {
IntersectionAdder.constructor_.apply(this, arguments);
}
static constructor_() {
this._hasIntersection = false;
this._hasProper = false;
this._hasProperInterior = false;
this._hasInterior = false;
this._properIntersectionPoint = null;
this._li = null;
this._isSelfIntersection = null;
this.numIntersections = 0;
this.numInteriorIntersections = 0;
this.numProperIntersections = 0;
this.numTests = 0;
const li = arguments[0];
this._li = li;
}
static isAdjacentSegments(i1, i2) {
return Math.abs(i1 - i2) === 1;
}
isTrivialIntersection(e0, segIndex0, e1, segIndex1) {
if (e0 === e1) if (this._li.getIntersectionNum() === 1) {
if (IntersectionAdder.isAdjacentSegments(segIndex0, segIndex1)) return true;
if (e0.isClosed()) {
const maxSegIndex = e0.size() - 1;
if (segIndex0 === 0 && segIndex1 === maxSegIndex || segIndex1 === 0 && segIndex0 === maxSegIndex) return true;
}
}
return false;
}
getProperIntersectionPoint() {
return this._properIntersectionPoint;
}
hasProperInteriorIntersection() {
return this._hasProperInterior;
}
getLineIntersector() {
return this._li;
}
hasProperIntersection() {
return this._hasProper;
}
processIntersections(e0, segIndex0, e1, segIndex1) {
if (e0 === e1 && segIndex0 === segIndex1) return null;
this.numTests++;
const p00 = e0.getCoordinates()[segIndex0];
const p01 = e0.getCoordinates()[segIndex0 + 1];
const p10 = e1.getCoordinates()[segIndex1];
const p11 = e1.getCoordinates()[segIndex1 + 1];
this._li.computeIntersection(p00, p01, p10, p11);
if (this._li.hasIntersection()) {
this.numIntersections++;
if (this._li.isInteriorIntersection()) {
this.numInteriorIntersections++;
this._hasInterior = true;
}
if (!this.isTrivialIntersection(e0, segIndex0, e1, segIndex1)) {
this._hasIntersection = true;
e0.addIntersections(this._li, segIndex0, 0);
e1.addIntersections(this._li, segIndex1, 1);
if (this._li.isProper()) {
this.numProperIntersections++;
this._hasProper = true;
this._hasProperInterior = true;
}
}
}
}
hasIntersection() {
return this._hasIntersection;
}
isDone() {
return false;
}
hasInteriorIntersection() {
return this._hasInterior;
}
get interfaces_() {
return [SegmentIntersector];
}
}
class BufferBuilder {
constructor() {
BufferBuilder.constructor_.apply(this, arguments);
}
static constructor_() {
this._bufParams = null;
this._workingPrecisionModel = null;
this._workingNoder = null;
this._geomFact = null;
this._graph = null;
this._edgeList = new EdgeList();
const bufParams = arguments[0];
this._bufParams = bufParams;
}
static depthDelta(label) {
const lLoc = label.getLocation(0, Position.LEFT);
const rLoc = label.getLocation(0, Position.RIGHT);
if (lLoc === Location.INTERIOR && rLoc === Location.EXTERIOR) return 1;else if (lLoc === Location.EXTERIOR && rLoc === Location.INTERIOR) return -1;
return 0;
}
static convertSegStrings(it) {
const fact = new GeometryFactory();
const lines = new ArrayList();
while (it.hasNext()) {
const ss = it.next();
const line = fact.createLineString(ss.getCoordinates());
lines.add(line);
}
return fact.buildGeometry(lines);
}
setWorkingPrecisionModel(pm) {
this._workingPrecisionModel = pm;
}
insertUniqueEdge(e) {
const existingEdge = this._edgeList.findEqualEdge(e);
if (existingEdge !== null) {
const existingLabel = existingEdge.getLabel();
let labelToMerge = e.getLabel();
if (!existingEdge.isPointwiseEqual(e)) {
labelToMerge = new Label(e.getLabel());
labelToMerge.flip();
}
existingLabel.merge(labelToMerge);
const mergeDelta = BufferBuilder.depthDelta(labelToMerge);
const existingDelta = existingEdge.getDepthDelta();
const newDelta = existingDelta + mergeDelta;
existingEdge.setDepthDelta(newDelta);
} else {
this._edgeList.add(e);
e.setDepthDelta(BufferBuilder.depthDelta(e.getLabel()));
}
}
buildSubgraphs(subgraphList, polyBuilder) {
const processedGraphs = new ArrayList();
for (let i = subgraphList.iterator(); i.hasNext();) {
const subgraph = i.next();
const p = subgraph.getRightmostCoordinate();
const locater = new SubgraphDepthLocater(processedGraphs);
const outsideDepth = locater.getDepth(p);
subgraph.computeDepth(outsideDepth);
subgraph.findResultEdges();
processedGraphs.add(subgraph);
polyBuilder.add(subgraph.getDirectedEdges(), subgraph.getNodes());
}
}
createSubgraphs(graph) {
const subgraphList = new ArrayList();
for (let i = graph.getNodes().iterator(); i.hasNext();) {
const node = i.next();
if (!node.isVisited()) {
const subgraph = new BufferSubgraph();
subgraph.create(node);
subgraphList.add(subgraph);
}
}
Collections.sort(subgraphList, Collections.reverseOrder());
return subgraphList;
}
createEmptyResultGeometry() {
const emptyGeom = this._geomFact.createPolygon();
return emptyGeom;
}
getNoder(precisionModel) {
if (this._workingNoder !== null) return this._workingNoder;
const noder = new MCIndexNoder();
const li = new RobustLineIntersector();
li.setPrecisionModel(precisionModel);
noder.setSegmentIntersector(new IntersectionAdder(li));
return noder;
}
buffer(g, distance) {
let precisionModel = this._workingPrecisionModel;
if (precisionModel === null) precisionModel = g.getPrecisionModel();
this._geomFact = g.getFactory();
const curveBuilder = new OffsetCurveBuilder(precisionModel, this._bufParams);
const curveSetBuilder = new OffsetCurveSetBuilder(g, distance, curveBuilder);
const bufferSegStrList = curveSetBuilder.getCurves();
if (bufferSegStrList.size() <= 0) return this.createEmptyResultGeometry();
this.computeNodedEdges(bufferSegStrList, precisionModel);
this._graph = new PlanarGraph$1(new OverlayNodeFactory());
this._graph.addEdges(this._edgeList.getEdges());
const subgraphList = this.createSubgraphs(this._graph);
const polyBuilder = new PolygonBuilder(this._geomFact);
this.buildSubgraphs(subgraphList, polyBuilder);
const resultPolyList = polyBuilder.getPolygons();
if (resultPolyList.size() <= 0) return this.createEmptyResultGeometry();
const resultGeom = this._geomFact.buildGeometry(resultPolyList);
return resultGeom;
}
computeNodedEdges(bufferSegStrList, precisionModel) {
const noder = this.getNoder(precisionModel);
noder.computeNodes(bufferSegStrList);
const nodedSegStrings = noder.getNodedSubstrings();
for (let i = nodedSegStrings.iterator(); i.hasNext();) {
const segStr = i.next();
const pts = segStr.getCoordinates();
if (pts.length === 2 && pts[0].equals2D(pts[1])) continue;
const oldLabel = segStr.getData();
const edge = new Edge$1(segStr.getCoordinates(), new Label(oldLabel));
this.insertUniqueEdge(edge);
}
}
setNoder(noder) {
this._workingNoder = noder;
}
}
class NodingValidator {
constructor() {
NodingValidator.constructor_.apply(this, arguments);
}
static constructor_() {
this._li = new RobustLineIntersector();
this._segStrings = null;
const segStrings = arguments[0];
this._segStrings = segStrings;
}
checkEndPtVertexIntersections() {
if (arguments.length === 0) {
for (let i = this._segStrings.iterator(); i.hasNext();) {
const ss = i.next();
const pts = ss.getCoordinates();
this.checkEndPtVertexIntersections(pts[0], this._segStrings);
this.checkEndPtVertexIntersections(pts[pts.length - 1], this._segStrings);
}
} else if (arguments.length === 2) {
const testPt = arguments[0],
segStrings = arguments[1];
for (let i = segStrings.iterator(); i.hasNext();) {
const ss = i.next();
const pts = ss.getCoordinates();
for (let j = 1; j < pts.length - 1; j++) if (pts[j].equals(testPt)) throw new RuntimeException('found endpt/interior pt intersection at index ' + j + ' :pt ' + testPt);
}
}
}
checkInteriorIntersections() {
if (arguments.length === 0) {
for (let i = this._segStrings.iterator(); i.hasNext();) {
const ss0 = i.next();
for (let j = this._segStrings.iterator(); j.hasNext();) {
const ss1 = j.next();
this.checkInteriorIntersections(ss0, ss1);
}
}
} else if (arguments.length === 2) {
const ss0 = arguments[0],
ss1 = arguments[1];
const pts0 = ss0.getCoordinates();
const pts1 = ss1.getCoordinates();
for (let i0 = 0; i0 < pts0.length - 1; i0++) for (let i1 = 0; i1 < pts1.length - 1; i1++) this.checkInteriorIntersections(ss0, i0, ss1, i1);
} else if (arguments.length === 4) {
const e0 = arguments[0],
segIndex0 = arguments[1],
e1 = arguments[2],
segIndex1 = arguments[3];
if (e0 === e1 && segIndex0 === segIndex1) return null;
const p00 = e0.getCoordinates()[segIndex0];
const p01 = e0.getCoordinates()[segIndex0 + 1];
const p10 = e1.getCoordinates()[segIndex1];
const p11 = e1.getCoordinates()[segIndex1 + 1];
this._li.computeIntersection(p00, p01, p10, p11);
if (this._li.hasIntersection()) if (this._li.isProper() || this.hasInteriorIntersection(this._li, p00, p01) || this.hasInteriorIntersection(this._li, p10, p11)) throw new RuntimeException('found non-noded intersection at ' + p00 + '-' + p01 + ' and ' + p10 + '-' + p11);
}
}
checkValid() {
this.checkEndPtVertexIntersections();
this.checkInteriorIntersections();
this.checkCollapses();
}
checkCollapses() {
if (arguments.length === 0) {
for (let i = this._segStrings.iterator(); i.hasNext();) {
const ss = i.next();
this.checkCollapses(ss);
}
} else if (arguments.length === 1) {
const ss = arguments[0];
const pts = ss.getCoordinates();
for (let i = 0; i < pts.length - 2; i++) this.checkCollapse(pts[i], pts[i + 1], pts[i + 2]);
}
}
hasInteriorIntersection(li, p0, p1) {
for (let i = 0; i < li.getIntersectionNum(); i++) {
const intPt = li.getIntersection(i);
if (!(intPt.equals(p0) || intPt.equals(p1))) return true;
}
return false;
}
checkCollapse(p0, p1, p2) {
if (p0.equals(p2)) throw new RuntimeException('found non-noded collapse at ' + NodingValidator.fact.createLineString([p0, p1, p2]));
}
}
NodingValidator.fact = new GeometryFactory();
class HotPixel {
constructor() {
HotPixel.constructor_.apply(this, arguments);
}
static constructor_() {
this._li = null;
this._pt = null;
this._originalPt = null;
this._ptScaled = null;
this._p0Scaled = null;
this._p1Scaled = null;
this._scaleFactor = null;
this._minx = null;
this._maxx = null;
this._miny = null;
this._maxy = null;
this._corner = new Array(4).fill(null);
this._safeEnv = null;
const pt = arguments[0],
scaleFactor = arguments[1],
li = arguments[2];
this._originalPt = pt;
this._pt = pt;
this._scaleFactor = scaleFactor;
this._li = li;
if (scaleFactor <= 0) throw new IllegalArgumentException('Scale factor must be non-zero');
if (scaleFactor !== 1.0) {
this._pt = new Coordinate(this.scale(pt.x), this.scale(pt.y));
this._p0Scaled = new Coordinate();
this._p1Scaled = new Coordinate();
}
this.initCorners(this._pt);
}
intersectsScaled(p0, p1) {
const segMinx = Math.min(p0.x, p1.x);
const segMaxx = Math.max(p0.x, p1.x);
const segMiny = Math.min(p0.y, p1.y);
const segMaxy = Math.max(p0.y, p1.y);
const isOutsidePixelEnv = this._maxx < segMinx || this._minx > segMaxx || this._maxy < segMiny || this._miny > segMaxy;
if (isOutsidePixelEnv) return false;
const intersects = this.intersectsToleranceSquare(p0, p1);
Assert.isTrue(!(isOutsidePixelEnv && intersects), 'Found bad envelope test');
return intersects;
}
initCorners(pt) {
const tolerance = 0.5;
this._minx = pt.x - tolerance;
this._maxx = pt.x + tolerance;
this._miny = pt.y - tolerance;
this._maxy = pt.y + tolerance;
this._corner[0] = new Coordinate(this._maxx, this._maxy);
this._corner[1] = new Coordinate(this._minx, this._maxy);
this._corner[2] = new Coordinate(this._minx, this._miny);
this._corner[3] = new Coordinate(this._maxx, this._miny);
}
intersects(p0, p1) {
if (this._scaleFactor === 1.0) return this.intersectsScaled(p0, p1);
this.copyScaled(p0, this._p0Scaled);
this.copyScaled(p1, this._p1Scaled);
return this.intersectsScaled(this._p0Scaled, this._p1Scaled);
}
scale(val) {
return Math.round(val * this._scaleFactor);
}
getCoordinate() {
return this._originalPt;
}
copyScaled(p, pScaled) {
pScaled.x = this.scale(p.x);
pScaled.y = this.scale(p.y);
}
getSafeEnvelope() {
if (this._safeEnv === null) {
const safeTolerance = HotPixel.SAFE_ENV_EXPANSION_FACTOR / this._scaleFactor;
this._safeEnv = new Envelope(this._originalPt.x - safeTolerance, this._originalPt.x + safeTolerance, this._originalPt.y - safeTolerance, this._originalPt.y + safeTolerance);
}
return this._safeEnv;
}
intersectsPixelClosure(p0, p1) {
this._li.computeIntersection(p0, p1, this._corner[0], this._corner[1]);
if (this._li.hasIntersection()) return true;
this._li.computeIntersection(p0, p1, this._corner[1], this._corner[2]);
if (this._li.hasIntersection()) return true;
this._li.computeIntersection(p0, p1, this._corner[2], this._corner[3]);
if (this._li.hasIntersection()) return true;
this._li.computeIntersection(p0, p1, this._corner[3], this._corner[0]);
if (this._li.hasIntersection()) return true;
return false;
}
intersectsToleranceSquare(p0, p1) {
let intersectsLeft = false;
let intersectsBottom = false;
this._li.computeIntersection(p0, p1, this._corner[0], this._corner[1]);
if (this._li.isProper()) return true;
this._li.computeIntersection(p0, p1, this._corner[1], this._corner[2]);
if (this._li.isProper()) return true;
if (this._li.hasIntersection()) intersectsLeft = true;
this._li.computeIntersection(p0, p1, this._corner[2], this._corner[3]);
if (this._li.isProper()) return true;
if (this._li.hasIntersection()) intersectsBottom = true;
this._li.computeIntersection(p0, p1, this._corner[3], this._corner[0]);
if (this._li.isProper()) return true;
if (intersectsLeft && intersectsBottom) return true;
if (p0.equals(this._pt)) return true;
if (p1.equals(this._pt)) return true;
return false;
}
addSnappedNode(segStr, segIndex) {
const p0 = segStr.getCoordinate(segIndex);
const p1 = segStr.getCoordinate(segIndex + 1);
if (this.intersects(p0, p1)) {
segStr.addIntersection(this.getCoordinate(), segIndex);
return true;
}
return false;
}
}
HotPixel.SAFE_ENV_EXPANSION_FACTOR = 0.75;
class MonotoneChainSelectAction {
constructor() {
MonotoneChainSelectAction.constructor_.apply(this, arguments);
}
static constructor_() {
this.selectedSegment = new LineSegment();
}
select() {
if (arguments.length === 1) ; else if (arguments.length === 2) {
const mc = arguments[0],
startIndex = arguments[1];
mc.getLineSegment(startIndex, this.selectedSegment);
this.select(this.selectedSegment);
}
}
}
class MCIndexPointSnapper {
constructor() {
MCIndexPointSnapper.constructor_.apply(this, arguments);
}
static constructor_() {
this._index = null;
const index = arguments[0];
this._index = index;
}
snap() {
if (arguments.length === 1) {
const hotPixel = arguments[0];
return this.snap(hotPixel, null, -1);
} else if (arguments.length === 3) {
const hotPixel = arguments[0],
parentEdge = arguments[1],
hotPixelVertexIndex = arguments[2];
const pixelEnv = hotPixel.getSafeEnvelope();
const hotPixelSnapAction = new HotPixelSnapAction(hotPixel, parentEdge, hotPixelVertexIndex);
this._index.query(pixelEnv, new class {
get interfaces_() {
return [ItemVisitor];
}
visitItem(item) {
const testChain = item;
testChain.select(pixelEnv, hotPixelSnapAction);
}
}());
return hotPixelSnapAction.isNodeAdded();
}
}
}
class HotPixelSnapAction extends MonotoneChainSelectAction {
constructor() {
super();
HotPixelSnapAction.constructor_.apply(this, arguments);
}
static constructor_() {
this._hotPixel = null;
this._parentEdge = null;
this._hotPixelVertexIndex = null;
this._isNodeAdded = false;
const hotPixel = arguments[0],
parentEdge = arguments[1],
hotPixelVertexIndex = arguments[2];
this._hotPixel = hotPixel;
this._parentEdge = parentEdge;
this._hotPixelVertexIndex = hotPixelVertexIndex;
}
isNodeAdded() {
return this._isNodeAdded;
}
select() {
if (arguments.length === 2 && Number.isInteger(arguments[1]) && arguments[0] instanceof MonotoneChain) {
const mc = arguments[0],
startIndex = arguments[1];
const ss = mc.getContext();
if (this._parentEdge === ss) if (startIndex === this._hotPixelVertexIndex || startIndex + 1 === this._hotPixelVertexIndex) return null;
this._isNodeAdded |= this._hotPixel.addSnappedNode(ss, startIndex);
} else {
return super.select.apply(this, arguments);
}
}
}
MCIndexPointSnapper.HotPixelSnapAction = HotPixelSnapAction;
class InteriorIntersectionFinderAdder {
constructor() {
InteriorIntersectionFinderAdder.constructor_.apply(this, arguments);
}
static constructor_() {
this._li = null;
this._interiorIntersections = null;
const li = arguments[0];
this._li = li;
this._interiorIntersections = new ArrayList();
}
processIntersections(e0, segIndex0, e1, segIndex1) {
if (e0 === e1 && segIndex0 === segIndex1) return null;
const p00 = e0.getCoordinates()[segIndex0];
const p01 = e0.getCoordinates()[segIndex0 + 1];
const p10 = e1.getCoordinates()[segIndex1];
const p11 = e1.getCoordinates()[segIndex1 + 1];
this._li.computeIntersection(p00, p01, p10, p11);
if (this._li.hasIntersection()) if (this._li.isInteriorIntersection()) {
for (let intIndex = 0; intIndex < this._li.getIntersectionNum(); intIndex++) this._interiorIntersections.add(this._li.getIntersection(intIndex));
e0.addIntersections(this._li, segIndex0, 0);
e1.addIntersections(this._li, segIndex1, 1);
}
}
isDone() {
return false;
}
getInteriorIntersections() {
return this._interiorIntersections;
}
get interfaces_() {
return [SegmentIntersector];
}
}
class MCIndexSnapRounder {
constructor() {
MCIndexSnapRounder.constructor_.apply(this, arguments);
}
static constructor_() {
this._pm = null;
this._li = null;
this._scaleFactor = null;
this._noder = null;
this._pointSnapper = null;
this._nodedSegStrings = null;
const pm = arguments[0];
this._pm = pm;
this._li = new RobustLineIntersector();
this._li.setPrecisionModel(pm);
this._scaleFactor = pm.getScale();
}
checkCorrectness(inputSegmentStrings) {
const resultSegStrings = NodedSegmentString.getNodedSubstrings(inputSegmentStrings);
const nv = new NodingValidator(resultSegStrings);
try {
nv.checkValid();
} catch (ex) {
if (ex instanceof Exception) ex.printStackTrace();else throw ex;
} finally {}
}
getNodedSubstrings() {
return NodedSegmentString.getNodedSubstrings(this._nodedSegStrings);
}
snapRound(segStrings, li) {
const intersections = this.findInteriorIntersections(segStrings, li);
this.computeIntersectionSnaps(intersections);
this.computeVertexSnaps(segStrings);
}
findInteriorIntersections(segStrings, li) {
const intFinderAdder = new InteriorIntersectionFinderAdder(li);
this._noder.setSegmentIntersector(intFinderAdder);
this._noder.computeNodes(segStrings);
return intFinderAdder.getInteriorIntersections();
}
computeVertexSnaps() {
if (hasInterface(arguments[0], Collection)) {
const edges = arguments[0];
for (let i0 = edges.iterator(); i0.hasNext();) {
const edge0 = i0.next();
this.computeVertexSnaps(edge0);
}
} else if (arguments[0] instanceof NodedSegmentString) {
const e = arguments[0];
const pts0 = e.getCoordinates();
for (let i = 0; i < pts0.length; i++) {
const hotPixel = new HotPixel(pts0[i], this._scaleFactor, this._li);
const isNodeAdded = this._pointSnapper.snap(hotPixel, e, i);
if (isNodeAdded) e.addIntersection(pts0[i], i);
}
}
}
computeNodes(inputSegmentStrings) {
this._nodedSegStrings = inputSegmentStrings;
this._noder = new MCIndexNoder();
this._pointSnapper = new MCIndexPointSnapper(this._noder.getIndex());
this.snapRound(inputSegmentStrings, this._li);
}
computeIntersectionSnaps(snapPts) {
for (let it = snapPts.iterator(); it.hasNext();) {
const snapPt = it.next();
const hotPixel = new HotPixel(snapPt, this._scaleFactor, this._li);
this._pointSnapper.snap(hotPixel);
}
}
get interfaces_() {
return [Noder];
}
}
class BufferOp {
constructor() {
BufferOp.constructor_.apply(this, arguments);
}
static constructor_() {
this._argGeom = null;
this._distance = null;
this._bufParams = new BufferParameters();
this._resultGeometry = null;
this._saveException = null;
if (arguments.length === 1) {
const g = arguments[0];
this._argGeom = g;
} else if (arguments.length === 2) {
const g = arguments[0],
bufParams = arguments[1];
this._argGeom = g;
this._bufParams = bufParams;
}
}
static bufferOp() {
if (arguments.length === 2) {
const g = arguments[0],
distance = arguments[1];
const gBuf = new BufferOp(g);
const geomBuf = gBuf.getResultGeometry(distance);
return geomBuf;
} else if (arguments.length === 3) {
if (Number.isInteger(arguments[2]) && arguments[0] instanceof Geometry && typeof arguments[1] === 'number') {
const g = arguments[0],
distance = arguments[1],
quadrantSegments = arguments[2];
const bufOp = new BufferOp(g);
bufOp.setQuadrantSegments(quadrantSegments);
const geomBuf = bufOp.getResultGeometry(distance);
return geomBuf;
} else if (arguments[2] instanceof BufferParameters && arguments[0] instanceof Geometry && typeof arguments[1] === 'number') {
const g = arguments[0],
distance = arguments[1],
params = arguments[2];
const bufOp = new BufferOp(g, params);
const geomBuf = bufOp.getResultGeometry(distance);
return geomBuf;
}
} else if (arguments.length === 4) {
const g = arguments[0],
distance = arguments[1],
quadrantSegments = arguments[2],
endCapStyle = arguments[3];
const bufOp = new BufferOp(g);
bufOp.setQuadrantSegments(quadrantSegments);
bufOp.setEndCapStyle(endCapStyle);
const geomBuf = bufOp.getResultGeometry(distance);
return geomBuf;
}
}
static precisionScaleFactor(g, distance, maxPrecisionDigits) {
const env = g.getEnvelopeInternal();
const envMax = MathUtil.max(Math.abs(env.getMaxX()), Math.abs(env.getMaxY()), Math.abs(env.getMinX()), Math.abs(env.getMinY()));
const expandByDistance = distance > 0.0 ? distance : 0.0;
const bufEnvMax = envMax + 2 * expandByDistance;
const bufEnvPrecisionDigits = Math.trunc(Math.log(bufEnvMax) / Math.log(10) + 1.0);
const minUnitLog10 = maxPrecisionDigits - bufEnvPrecisionDigits;
const scaleFactor = Math.pow(10.0, minUnitLog10);
return scaleFactor;
}
bufferFixedPrecision(fixedPM) {
const noder = new ScaledNoder(new MCIndexSnapRounder(new PrecisionModel(1.0)), fixedPM.getScale());
const bufBuilder = new BufferBuilder(this._bufParams);
bufBuilder.setWorkingPrecisionModel(fixedPM);
bufBuilder.setNoder(noder);
this._resultGeometry = bufBuilder.buffer(this._argGeom, this._distance);
}
bufferReducedPrecision() {
if (arguments.length === 0) {
for (let precDigits = BufferOp.MAX_PRECISION_DIGITS; precDigits >= 0; precDigits--) {
try {
this.bufferReducedPrecision(precDigits);
} catch (ex) {
if (ex instanceof TopologyException) this._saveException = ex;else throw ex;
} finally {}
if (this._resultGeometry !== null) return null;
}
throw this._saveException;
} else if (arguments.length === 1) {
const precisionDigits = arguments[0];
const sizeBasedScaleFactor = BufferOp.precisionScaleFactor(this._argGeom, this._distance, precisionDigits);
const fixedPM = new PrecisionModel(sizeBasedScaleFactor);
this.bufferFixedPrecision(fixedPM);
}
}
computeGeometry() {
this.bufferOriginalPrecision();
if (this._resultGeometry !== null) return null;
const argPM = this._argGeom.getFactory().getPrecisionModel();
if (argPM.getType() === PrecisionModel.FIXED) this.bufferFixedPrecision(argPM);else this.bufferReducedPrecision();
}
setQuadrantSegments(quadrantSegments) {
this._bufParams.setQuadrantSegments(quadrantSegments);
}
bufferOriginalPrecision() {
try {
const bufBuilder = new BufferBuilder(this._bufParams);
this._resultGeometry = bufBuilder.buffer(this._argGeom, this._distance);
} catch (ex) {
if (ex instanceof RuntimeException) this._saveException = ex;else throw ex;
} finally {}
}
getResultGeometry(distance) {
this._distance = distance;
this.computeGeometry();
return this._resultGeometry;
}
setEndCapStyle(endCapStyle) {
this._bufParams.setEndCapStyle(endCapStyle);
}
}
BufferOp.CAP_ROUND = BufferParameters.CAP_ROUND;
BufferOp.CAP_BUTT = BufferParameters.CAP_FLAT;
BufferOp.CAP_FLAT = BufferParameters.CAP_FLAT;
BufferOp.CAP_SQUARE = BufferParameters.CAP_SQUARE;
BufferOp.MAX_PRECISION_DIGITS = 12;
var buffer = /*#__PURE__*/Object.freeze({
__proto__: null,
BufferOp: BufferOp,
BufferParameters: BufferParameters
});
class GeometryLocation {
constructor() {
GeometryLocation.constructor_.apply(this, arguments);
}
static constructor_() {
this._component = null;
this._segIndex = null;
this._pt = null;
if (arguments.length === 2) {
const component = arguments[0],
pt = arguments[1];
GeometryLocation.constructor_.call(this, component, GeometryLocation.INSIDE_AREA, pt);
} else if (arguments.length === 3) {
const component = arguments[0],
segIndex = arguments[1],
pt = arguments[2];
this._component = component;
this._segIndex = segIndex;
this._pt = pt;
}
}
getSegmentIndex() {
return this._segIndex;
}
getCoordinate() {
return this._pt;
}
isInsideArea() {
return this._segIndex === GeometryLocation.INSIDE_AREA;
}
toString() {
return this._component.getGeometryType() + '[' + this._segIndex + ']' + '-' + WKTWriter.toPoint(this._pt);
}
getGeometryComponent() {
return this._component;
}
}
GeometryLocation.INSIDE_AREA = -1;
class ConnectedElementLocationFilter {
constructor() {
ConnectedElementLocationFilter.constructor_.apply(this, arguments);
}
static constructor_() {
this._locations = null;
const locations = arguments[0];
this._locations = locations;
}
static getLocations(geom) {
const locations = new ArrayList();
geom.apply(new ConnectedElementLocationFilter(locations));
return locations;
}
filter(geom) {
if (geom.isEmpty()) return null;
if (geom instanceof Point || geom instanceof LineString || geom instanceof Polygon) this._locations.add(new GeometryLocation(geom, 0, geom.getCoordinate()));
}
get interfaces_() {
return [GeometryFilter];
}
}
class DistanceOp {
constructor() {
DistanceOp.constructor_.apply(this, arguments);
}
static constructor_() {
this._geom = null;
this._terminateDistance = 0.0;
this._ptLocator = new PointLocator();
this._minDistanceLocation = null;
this._minDistance = Double.MAX_VALUE;
if (arguments.length === 2) {
const g0 = arguments[0],
g1 = arguments[1];
DistanceOp.constructor_.call(this, g0, g1, 0.0);
} else if (arguments.length === 3) {
const g0 = arguments[0],
g1 = arguments[1],
terminateDistance = arguments[2];
this._geom = new Array(2).fill(null);
this._geom[0] = g0;
this._geom[1] = g1;
this._terminateDistance = terminateDistance;
}
}
static distance(g0, g1) {
const distOp = new DistanceOp(g0, g1);
return distOp.distance();
}
static isWithinDistance(g0, g1, distance) {
const envDist = g0.getEnvelopeInternal().distance(g1.getEnvelopeInternal());
if (envDist > distance) return false;
const distOp = new DistanceOp(g0, g1, distance);
return distOp.distance() <= distance;
}
static nearestPoints(g0, g1) {
const distOp = new DistanceOp(g0, g1);
return distOp.nearestPoints();
}
computeContainmentDistance() {
if (arguments.length === 0) {
const locPtPoly = new Array(2).fill(null);
this.computeContainmentDistance(0, locPtPoly);
if (this._minDistance <= this._terminateDistance) return null;
this.computeContainmentDistance(1, locPtPoly);
} else if (arguments.length === 2) {
const polyGeomIndex = arguments[0],
locPtPoly = arguments[1];
const polyGeom = this._geom[polyGeomIndex];
if (polyGeom.getDimension() < 2) return null;
const locationsIndex = 1 - polyGeomIndex;
const polys = PolygonExtracter.getPolygons(polyGeom);
if (polys.size() > 0) {
const insideLocs = ConnectedElementLocationFilter.getLocations(this._geom[locationsIndex]);
this.computeContainmentDistance(insideLocs, polys, locPtPoly);
if (this._minDistance <= this._terminateDistance) {
this._minDistanceLocation[locationsIndex] = locPtPoly[0];
this._minDistanceLocation[polyGeomIndex] = locPtPoly[1];
return null;
}
}
} else if (arguments.length === 3) {
if (arguments[2] instanceof Array && hasInterface(arguments[0], List) && hasInterface(arguments[1], List)) {
const locs = arguments[0],
polys = arguments[1],
locPtPoly = arguments[2];
for (let i = 0; i < locs.size(); i++) {
const loc = locs.get(i);
for (let j = 0; j < polys.size(); j++) {
this.computeContainmentDistance(loc, polys.get(j), locPtPoly);
if (this._minDistance <= this._terminateDistance) return null;
}
}
} else if (arguments[2] instanceof Array && arguments[0] instanceof GeometryLocation && arguments[1] instanceof Polygon) {
const ptLoc = arguments[0],
poly = arguments[1],
locPtPoly = arguments[2];
const pt = ptLoc.getCoordinate();
if (Location.EXTERIOR !== this._ptLocator.locate(pt, poly)) {
this._minDistance = 0.0;
locPtPoly[0] = ptLoc;
locPtPoly[1] = new GeometryLocation(poly, pt);
return null;
}
}
}
}
computeMinDistanceLinesPoints(lines, points, locGeom) {
for (let i = 0; i < lines.size(); i++) {
const line = lines.get(i);
for (let j = 0; j < points.size(); j++) {
const pt = points.get(j);
this.computeMinDistance(line, pt, locGeom);
if (this._minDistance <= this._terminateDistance) return null;
}
}
}
computeFacetDistance() {
const locGeom = new Array(2).fill(null);
const lines0 = LinearComponentExtracter.getLines(this._geom[0]);
const lines1 = LinearComponentExtracter.getLines(this._geom[1]);
const pts0 = PointExtracter.getPoints(this._geom[0]);
const pts1 = PointExtracter.getPoints(this._geom[1]);
this.computeMinDistanceLines(lines0, lines1, locGeom);
this.updateMinDistance(locGeom, false);
if (this._minDistance <= this._terminateDistance) return null;
locGeom[0] = null;
locGeom[1] = null;
this.computeMinDistanceLinesPoints(lines0, pts1, locGeom);
this.updateMinDistance(locGeom, false);
if (this._minDistance <= this._terminateDistance) return null;
locGeom[0] = null;
locGeom[1] = null;
this.computeMinDistanceLinesPoints(lines1, pts0, locGeom);
this.updateMinDistance(locGeom, true);
if (this._minDistance <= this._terminateDistance) return null;
locGeom[0] = null;
locGeom[1] = null;
this.computeMinDistancePoints(pts0, pts1, locGeom);
this.updateMinDistance(locGeom, false);
}
nearestLocations() {
this.computeMinDistance();
return this._minDistanceLocation;
}
updateMinDistance(locGeom, flip) {
if (locGeom[0] === null) return null;
if (flip) {
this._minDistanceLocation[0] = locGeom[1];
this._minDistanceLocation[1] = locGeom[0];
} else {
this._minDistanceLocation[0] = locGeom[0];
this._minDistanceLocation[1] = locGeom[1];
}
}
nearestPoints() {
this.computeMinDistance();
const nearestPts = [this._minDistanceLocation[0].getCoordinate(), this._minDistanceLocation[1].getCoordinate()];
return nearestPts;
}
computeMinDistance() {
if (arguments.length === 0) {
if (this._minDistanceLocation !== null) return null;
this._minDistanceLocation = new Array(2).fill(null);
this.computeContainmentDistance();
if (this._minDistance <= this._terminateDistance) return null;
this.computeFacetDistance();
} else if (arguments.length === 3) {
if (arguments[2] instanceof Array && arguments[0] instanceof LineString && arguments[1] instanceof Point) {
const line = arguments[0],
pt = arguments[1],
locGeom = arguments[2];
if (line.getEnvelopeInternal().distance(pt.getEnvelopeInternal()) > this._minDistance) return null;
const coord0 = line.getCoordinates();
const coord = pt.getCoordinate();
for (let i = 0; i < coord0.length - 1; i++) {
const dist = Distance.pointToSegment(coord, coord0[i], coord0[i + 1]);
if (dist < this._minDistance) {
this._minDistance = dist;
const seg = new LineSegment(coord0[i], coord0[i + 1]);
const segClosestPoint = seg.closestPoint(coord);
locGeom[0] = new GeometryLocation(line, i, segClosestPoint);
locGeom[1] = new GeometryLocation(pt, 0, coord);
}
if (this._minDistance <= this._terminateDistance) return null;
}
} else if (arguments[2] instanceof Array && arguments[0] instanceof LineString && arguments[1] instanceof LineString) {
const line0 = arguments[0],
line1 = arguments[1],
locGeom = arguments[2];
if (line0.getEnvelopeInternal().distance(line1.getEnvelopeInternal()) > this._minDistance) return null;
const coord0 = line0.getCoordinates();
const coord1 = line1.getCoordinates();
for (let i = 0; i < coord0.length - 1; i++) {
const segEnv0 = new Envelope(coord0[i], coord0[i + 1]);
if (segEnv0.distance(line1.getEnvelopeInternal()) > this._minDistance) continue;
for (let j = 0; j < coord1.length - 1; j++) {
const segEnv1 = new Envelope(coord1[j], coord1[j + 1]);
if (segEnv0.distance(segEnv1) > this._minDistance) continue;
const dist = Distance.segmentToSegment(coord0[i], coord0[i + 1], coord1[j], coord1[j + 1]);
if (dist < this._minDistance) {
this._minDistance = dist;
const seg0 = new LineSegment(coord0[i], coord0[i + 1]);
const seg1 = new LineSegment(coord1[j], coord1[j + 1]);
const closestPt = seg0.closestPoints(seg1);
locGeom[0] = new GeometryLocation(line0, i, closestPt[0]);
locGeom[1] = new GeometryLocation(line1, j, closestPt[1]);
}
if (this._minDistance <= this._terminateDistance) return null;
}
}
}
}
}
computeMinDistancePoints(points0, points1, locGeom) {
for (let i = 0; i < points0.size(); i++) {
const pt0 = points0.get(i);
for (let j = 0; j < points1.size(); j++) {
const pt1 = points1.get(j);
const dist = pt0.getCoordinate().distance(pt1.getCoordinate());
if (dist < this._minDistance) {
this._minDistance = dist;
locGeom[0] = new GeometryLocation(pt0, 0, pt0.getCoordinate());
locGeom[1] = new GeometryLocation(pt1, 0, pt1.getCoordinate());
}
if (this._minDistance <= this._terminateDistance) return null;
}
}
}
distance() {
if (this._geom[0] === null || this._geom[1] === null) throw new IllegalArgumentException('null geometries are not supported');
if (this._geom[0].isEmpty() || this._geom[1].isEmpty()) return 0.0;
this.computeMinDistance();
return this._minDistance;
}
computeMinDistanceLines(lines0, lines1, locGeom) {
for (let i = 0; i < lines0.size(); i++) {
const line0 = lines0.get(i);
for (let j = 0; j < lines1.size(); j++) {
const line1 = lines1.get(j);
this.computeMinDistance(line0, line1, locGeom);
if (this._minDistance <= this._terminateDistance) return null;
}
}
}
}
var distance = /*#__PURE__*/Object.freeze({
__proto__: null,
DistanceOp: DistanceOp
});
class EdgeString {
constructor() {
EdgeString.constructor_.apply(this, arguments);
}
static constructor_() {
this._factory = null;
this._directedEdges = new ArrayList();
this._coordinates = null;
const factory = arguments[0];
this._factory = factory;
}
getCoordinates() {
if (this._coordinates === null) {
let forwardDirectedEdges = 0;
let reverseDirectedEdges = 0;
const coordinateList = new CoordinateList();
for (let i = this._directedEdges.iterator(); i.hasNext();) {
const directedEdge = i.next();
if (directedEdge.getEdgeDirection()) forwardDirectedEdges++;else reverseDirectedEdges++;
coordinateList.add(directedEdge.getEdge().getLine().getCoordinates(), false, directedEdge.getEdgeDirection());
}
this._coordinates = coordinateList.toCoordinateArray();
if (reverseDirectedEdges > forwardDirectedEdges) CoordinateArrays.reverse(this._coordinates);
}
return this._coordinates;
}
toLineString() {
return this._factory.createLineString(this.getCoordinates());
}
add(directedEdge) {
this._directedEdges.add(directedEdge);
}
}
class GraphComponent {
constructor() {
GraphComponent.constructor_.apply(this, arguments);
}
static constructor_() {
this._isMarked = false;
this._isVisited = false;
this._data = null;
}
static getComponentWithVisitedState(i, visitedState) {
while (i.hasNext()) {
const comp = i.next();
if (comp.isVisited() === visitedState) return comp;
}
return null;
}
static setVisited(i, visited) {
while (i.hasNext()) {
const comp = i.next();
comp.setVisited(visited);
}
}
static setMarked(i, marked) {
while (i.hasNext()) {
const comp = i.next();
comp.setMarked(marked);
}
}
setVisited(isVisited) {
this._isVisited = isVisited;
}
isMarked() {
return this._isMarked;
}
setData(data) {
this._data = data;
}
getData() {
return this._data;
}
setMarked(isMarked) {
this._isMarked = isMarked;
}
getContext() {
return this._data;
}
isVisited() {
return this._isVisited;
}
setContext(data) {
this._data = data;
}
}
class DirectedEdge extends GraphComponent {
constructor() {
super();
DirectedEdge.constructor_.apply(this, arguments);
}
static constructor_() {
this._parentEdge = null;
this._from = null;
this._to = null;
this._p0 = null;
this._p1 = null;
this._sym = null;
this._edgeDirection = null;
this._quadrant = null;
this._angle = null;
if (arguments.length === 0) ; else if (arguments.length === 4) {
const from = arguments[0],
to = arguments[1],
directionPt = arguments[2],
edgeDirection = arguments[3];
this._from = from;
this._to = to;
this._edgeDirection = edgeDirection;
this._p0 = from.getCoordinate();
this._p1 = directionPt;
const dx = this._p1.x - this._p0.x;
const dy = this._p1.y - this._p0.y;
this._quadrant = Quadrant.quadrant(dx, dy);
this._angle = Math.atan2(dy, dx);
}
}
static toEdges(dirEdges) {
const edges = new ArrayList();
for (let i = dirEdges.iterator(); i.hasNext();) edges.add(i.next()._parentEdge);
return edges;
}
isRemoved() {
return this._parentEdge === null;
}
compareDirection(e) {
if (this._quadrant > e._quadrant) return 1;
if (this._quadrant < e._quadrant) return -1;
return Orientation.index(e._p0, e._p1, this._p1);
}
getCoordinate() {
return this._from.getCoordinate();
}
print(out) {
const className = this.getClass().getName();
const lastDotPos = className.lastIndexOf('.');
const name = className.substring(lastDotPos + 1);
out.print(' ' + name + ': ' + this._p0 + ' - ' + this._p1 + ' ' + this._quadrant + ':' + this._angle);
}
getDirectionPt() {
return this._p1;
}
getAngle() {
return this._angle;
}
compareTo(obj) {
const de = obj;
return this.compareDirection(de);
}
getFromNode() {
return this._from;
}
getSym() {
return this._sym;
}
setEdge(parentEdge) {
this._parentEdge = parentEdge;
}
remove() {
this._sym = null;
this._parentEdge = null;
}
getEdge() {
return this._parentEdge;
}
getQuadrant() {
return this._quadrant;
}
setSym(sym) {
this._sym = sym;
}
getToNode() {
return this._to;
}
getEdgeDirection() {
return this._edgeDirection;
}
get interfaces_() {
return [Comparable];
}
}
class LineMergeDirectedEdge extends DirectedEdge {
constructor() {
super();
LineMergeDirectedEdge.constructor_.apply(this, arguments);
}
static constructor_() {
const from = arguments[0],
to = arguments[1],
directionPt = arguments[2],
edgeDirection = arguments[3];
DirectedEdge.constructor_.call(this, from, to, directionPt, edgeDirection);
}
getNext() {
if (this.getToNode().getDegree() !== 2) return null;
if (this.getToNode().getOutEdges().getEdges().get(0) === this.getSym()) return this.getToNode().getOutEdges().getEdges().get(1);
Assert.isTrue(this.getToNode().getOutEdges().getEdges().get(1) === this.getSym());
return this.getToNode().getOutEdges().getEdges().get(0);
}
}
class Edge extends GraphComponent {
constructor() {
super();
Edge.constructor_.apply(this, arguments);
}
static constructor_() {
this._dirEdge = null;
if (arguments.length === 0) ; else if (arguments.length === 2) {
const de0 = arguments[0],
de1 = arguments[1];
this.setDirectedEdges(de0, de1);
}
}
isRemoved() {
return this._dirEdge === null;
}
setDirectedEdges(de0, de1) {
this._dirEdge = [de0, de1];
de0.setEdge(this);
de1.setEdge(this);
de0.setSym(de1);
de1.setSym(de0);
de0.getFromNode().addOutEdge(de0);
de1.getFromNode().addOutEdge(de1);
}
getDirEdge() {
if (Number.isInteger(arguments[0])) {
const i = arguments[0];
return this._dirEdge[i];
} else if (arguments[0] instanceof Node) {
const fromNode = arguments[0];
if (this._dirEdge[0].getFromNode() === fromNode) return this._dirEdge[0];
if (this._dirEdge[1].getFromNode() === fromNode) return this._dirEdge[1];
return null;
}
}
remove() {
this._dirEdge = null;
}
getOppositeNode(node) {
if (this._dirEdge[0].getFromNode() === node) return this._dirEdge[0].getToNode();
if (this._dirEdge[1].getFromNode() === node) return this._dirEdge[1].getToNode();
return null;
}
}
class DirectedEdgeStar {
constructor() {
DirectedEdgeStar.constructor_.apply(this, arguments);
}
static constructor_() {
this._outEdges = new ArrayList();
this._sorted = false;
}
getNextEdge(dirEdge) {
const i = this.getIndex(dirEdge);
return this._outEdges.get(this.getIndex(i + 1));
}
getCoordinate() {
const it = this.iterator();
if (!it.hasNext()) return null;
const e = it.next();
return e.getCoordinate();
}
iterator() {
this.sortEdges();
return this._outEdges.iterator();
}
sortEdges() {
if (!this._sorted) {
Collections.sort(this._outEdges);
this._sorted = true;
}
}
remove(de) {
this._outEdges.remove(de);
}
getEdges() {
this.sortEdges();
return this._outEdges;
}
getNextCWEdge(dirEdge) {
const i = this.getIndex(dirEdge);
return this._outEdges.get(this.getIndex(i - 1));
}
getIndex() {
if (arguments[0] instanceof Edge) {
const edge = arguments[0];
this.sortEdges();
for (let i = 0; i < this._outEdges.size(); i++) {
const de = this._outEdges.get(i);
if (de.getEdge() === edge) return i;
}
return -1;
} else if (arguments[0] instanceof DirectedEdge) {
const dirEdge = arguments[0];
this.sortEdges();
for (let i = 0; i < this._outEdges.size(); i++) {
const de = this._outEdges.get(i);
if (de === dirEdge) return i;
}
return -1;
} else if (Number.isInteger(arguments[0])) {
const i = arguments[0];
let modi = i % this._outEdges.size();
if (modi < 0) modi += this._outEdges.size();
return modi;
}
}
add(de) {
this._outEdges.add(de);
this._sorted = false;
}
getDegree() {
return this._outEdges.size();
}
}
class Node extends GraphComponent {
constructor() {
super();
Node.constructor_.apply(this, arguments);
}
static constructor_() {
this._pt = null;
this._deStar = null;
if (arguments.length === 1) {
const pt = arguments[0];
Node.constructor_.call(this, pt, new DirectedEdgeStar());
} else if (arguments.length === 2) {
const pt = arguments[0],
deStar = arguments[1];
this._pt = pt;
this._deStar = deStar;
}
}
static getEdgesBetween(node0, node1) {
const edges0 = DirectedEdge.toEdges(node0.getOutEdges().getEdges());
const commonEdges = new HashSet(edges0);
const edges1 = DirectedEdge.toEdges(node1.getOutEdges().getEdges());
commonEdges.retainAll(edges1);
return commonEdges;
}
isRemoved() {
return this._pt === null;
}
addOutEdge(de) {
this._deStar.add(de);
}
getCoordinate() {
return this._pt;
}
getOutEdges() {
return this._deStar;
}
remove() {
if (arguments.length === 0) {
this._pt = null;
} else if (arguments.length === 1) {
const de = arguments[0];
this._deStar.remove(de);
}
}
getIndex(edge) {
return this._deStar.getIndex(edge);
}
getDegree() {
return this._deStar.getDegree();
}
}
class LineMergeEdge extends Edge {
constructor() {
super();
LineMergeEdge.constructor_.apply(this, arguments);
}
static constructor_() {
this._line = null;
const line = arguments[0];
this._line = line;
}
getLine() {
return this._line;
}
}
class NodeMap {
constructor() {
NodeMap.constructor_.apply(this, arguments);
}
static constructor_() {
this._nodeMap = new TreeMap();
}
find(coord) {
return this._nodeMap.get(coord);
}
iterator() {
return this._nodeMap.values().iterator();
}
remove(pt) {
return this._nodeMap.remove(pt);
}
values() {
return this._nodeMap.values();
}
add(n) {
this._nodeMap.put(n.getCoordinate(), n);
return n;
}
}
class PlanarGraph {
constructor() {
PlanarGraph.constructor_.apply(this, arguments);
}
static constructor_() {
this._edges = new HashSet();
this._dirEdges = new HashSet();
this._nodeMap = new NodeMap();
}
findNodesOfDegree(degree) {
const nodesFound = new ArrayList();
for (let i = this.nodeIterator(); i.hasNext();) {
const node = i.next();
if (node.getDegree() === degree) nodesFound.add(node);
}
return nodesFound;
}
dirEdgeIterator() {
return this._dirEdges.iterator();
}
edgeIterator() {
return this._edges.iterator();
}
remove() {
if (arguments[0] instanceof Edge) {
const edge = arguments[0];
this.remove(edge.getDirEdge(0));
this.remove(edge.getDirEdge(1));
this._edges.remove(edge);
edge.remove();
} else if (arguments[0] instanceof DirectedEdge) {
const de = arguments[0];
const sym = de.getSym();
if (sym !== null) sym.setSym(null);
de.getFromNode().remove(de);
de.remove();
this._dirEdges.remove(de);
} else if (arguments[0] instanceof Node) {
const node = arguments[0];
const outEdges = node.getOutEdges().getEdges();
for (let i = outEdges.iterator(); i.hasNext();) {
const de = i.next();
const sym = de.getSym();
if (sym !== null) this.remove(sym);
this._dirEdges.remove(de);
const edge = de.getEdge();
if (edge !== null) this._edges.remove(edge);
}
this._nodeMap.remove(node.getCoordinate());
node.remove();
}
}
findNode(pt) {
return this._nodeMap.find(pt);
}
getEdges() {
return this._edges;
}
nodeIterator() {
return this._nodeMap.iterator();
}
contains() {
if (arguments[0] instanceof Edge) {
const e = arguments[0];
return this._edges.contains(e);
} else if (arguments[0] instanceof DirectedEdge) {
const de = arguments[0];
return this._dirEdges.contains(de);
}
}
add() {
if (arguments[0] instanceof Node) {
const node = arguments[0];
this._nodeMap.add(node);
} else if (arguments[0] instanceof Edge) {
const edge = arguments[0];
this._edges.add(edge);
this.add(edge.getDirEdge(0));
this.add(edge.getDirEdge(1));
} else if (arguments[0] instanceof DirectedEdge) {
const dirEdge = arguments[0];
this._dirEdges.add(dirEdge);
}
}
getNodes() {
return this._nodeMap.values();
}
}
class LineMergeGraph extends PlanarGraph {
constructor() {
super();
}
addEdge(lineString) {
if (lineString.isEmpty()) return null;
const coordinates = CoordinateArrays.removeRepeatedPoints(lineString.getCoordinates());
if (coordinates.length <= 1) return null;
const startCoordinate = coordinates[0];
const endCoordinate = coordinates[coordinates.length - 1];
const startNode = this.getNode(startCoordinate);
const endNode = this.getNode(endCoordinate);
const directedEdge0 = new LineMergeDirectedEdge(startNode, endNode, coordinates[1], true);
const directedEdge1 = new LineMergeDirectedEdge(endNode, startNode, coordinates[coordinates.length - 2], false);
const edge = new LineMergeEdge(lineString);
edge.setDirectedEdges(directedEdge0, directedEdge1);
this.add(edge);
}
getNode(coordinate) {
let node = this.findNode(coordinate);
if (node === null) {
node = new Node(coordinate);
this.add(node);
}
return node;
}
}
class LineMerger {
constructor() {
LineMerger.constructor_.apply(this, arguments);
}
static constructor_() {
this._graph = new LineMergeGraph();
this._mergedLineStrings = null;
this._factory = null;
this._edgeStrings = null;
}
buildEdgeStringsForUnprocessedNodes() {
for (let i = this._graph.getNodes().iterator(); i.hasNext();) {
const node = i.next();
if (!node.isMarked()) {
Assert.isTrue(node.getDegree() === 2);
this.buildEdgeStringsStartingAt(node);
node.setMarked(true);
}
}
}
buildEdgeStringsForNonDegree2Nodes() {
for (let i = this._graph.getNodes().iterator(); i.hasNext();) {
const node = i.next();
if (node.getDegree() !== 2) {
this.buildEdgeStringsStartingAt(node);
node.setMarked(true);
}
}
}
buildEdgeStringsForObviousStartNodes() {
this.buildEdgeStringsForNonDegree2Nodes();
}
getMergedLineStrings() {
this.merge();
return this._mergedLineStrings;
}
buildEdgeStringsStartingAt(node) {
for (let i = node.getOutEdges().iterator(); i.hasNext();) {
const directedEdge = i.next();
if (directedEdge.getEdge().isMarked()) continue;
this._edgeStrings.add(this.buildEdgeStringStartingWith(directedEdge));
}
}
merge() {
if (this._mergedLineStrings !== null) return null;
GraphComponent.setMarked(this._graph.nodeIterator(), false);
GraphComponent.setMarked(this._graph.edgeIterator(), false);
this._edgeStrings = new ArrayList();
this.buildEdgeStringsForObviousStartNodes();
this.buildEdgeStringsForIsolatedLoops();
this._mergedLineStrings = new ArrayList();
for (let i = this._edgeStrings.iterator(); i.hasNext();) {
const edgeString = i.next();
this._mergedLineStrings.add(edgeString.toLineString());
}
}
addLineString(lineString) {
if (this._factory === null) this._factory = lineString.getFactory();
this._graph.addEdge(lineString);
}
buildEdgeStringStartingWith(start) {
const edgeString = new EdgeString(this._factory);
let current = start;
do {
edgeString.add(current);
current.getEdge().setMarked(true);
current = current.getNext();
} while (current !== null && current !== start);
return edgeString;
}
add() {
if (arguments[0] instanceof Geometry) {
const geometry = arguments[0];
for (let i = 0; i < geometry.getNumGeometries(); i++) {
const component = geometry.getGeometryN(i);
if (component instanceof LineString) this.addLineString(component);
}
} else if (hasInterface(arguments[0], Collection)) {
const geometries = arguments[0];
this._mergedLineStrings = null;
for (let i = geometries.iterator(); i.hasNext();) {
const geometry = i.next();
this.add(geometry);
}
}
}
buildEdgeStringsForIsolatedLoops() {
this.buildEdgeStringsForUnprocessedNodes();
}
}
class Subgraph {
constructor() {
Subgraph.constructor_.apply(this, arguments);
}
static constructor_() {
this._parentGraph = null;
this._edges = new HashSet();
this._dirEdges = new ArrayList();
this._nodeMap = new NodeMap();
const parentGraph = arguments[0];
this._parentGraph = parentGraph;
}
dirEdgeIterator() {
return this._dirEdges.iterator();
}
edgeIterator() {
return this._edges.iterator();
}
getParent() {
return this._parentGraph;
}
nodeIterator() {
return this._nodeMap.iterator();
}
contains(e) {
return this._edges.contains(e);
}
add(e) {
if (this._edges.contains(e)) return null;
this._edges.add(e);
this._dirEdges.add(e.getDirEdge(0));
this._dirEdges.add(e.getDirEdge(1));
this._nodeMap.add(e.getDirEdge(0).getFromNode());
this._nodeMap.add(e.getDirEdge(1).getFromNode());
}
}
class ConnectedSubgraphFinder {
constructor() {
ConnectedSubgraphFinder.constructor_.apply(this, arguments);
}
static constructor_() {
this._graph = null;
const graph = arguments[0];
this._graph = graph;
}
addReachable(startNode, subgraph) {
const nodeStack = new Stack();
nodeStack.add(startNode);
while (!nodeStack.empty()) {
const node = nodeStack.pop();
this.addEdges(node, nodeStack, subgraph);
}
}
findSubgraph(node) {
const subgraph = new Subgraph(this._graph);
this.addReachable(node, subgraph);
return subgraph;
}
getConnectedSubgraphs() {
const subgraphs = new ArrayList();
GraphComponent.setVisited(this._graph.nodeIterator(), false);
for (let i = this._graph.edgeIterator(); i.hasNext();) {
const e = i.next();
const node = e.getDirEdge(0).getFromNode();
if (!node.isVisited()) subgraphs.add(this.findSubgraph(node));
}
return subgraphs;
}
addEdges(node, nodeStack, subgraph) {
node.setVisited(true);
for (let i = node.getOutEdges().iterator(); i.hasNext();) {
const de = i.next();
subgraph.add(de.getEdge());
const toNode = de.getToNode();
if (!toNode.isVisited()) nodeStack.push(toNode);
}
}
}
class LineSequencer {
constructor() {
LineSequencer.constructor_.apply(this, arguments);
}
static constructor_() {
this._graph = new LineMergeGraph();
this._factory = new GeometryFactory();
this._lineCount = 0;
this._isRun = false;
this._sequencedGeometry = null;
this._isSequenceable = false;
}
static findUnvisitedBestOrientedDE(node) {
let wellOrientedDE = null;
let unvisitedDE = null;
for (let i = node.getOutEdges().iterator(); i.hasNext();) {
const de = i.next();
if (!de.getEdge().isVisited()) {
unvisitedDE = de;
if (de.getEdgeDirection()) wellOrientedDE = de;
}
}
if (wellOrientedDE !== null) return wellOrientedDE;
return unvisitedDE;
}
static findLowestDegreeNode(graph) {
let minDegree = Integer.MAX_VALUE;
let minDegreeNode = null;
for (let i = graph.nodeIterator(); i.hasNext();) {
const node = i.next();
if (minDegreeNode === null || node.getDegree() < minDegree) {
minDegree = node.getDegree();
minDegreeNode = node;
}
}
return minDegreeNode;
}
static isSequenced(geom) {
if (!(geom instanceof MultiLineString)) return true;
const mls = geom;
const prevSubgraphNodes = new TreeSet();
let lastNode = null;
const currNodes = new ArrayList();
for (let i = 0; i < mls.getNumGeometries(); i++) {
const line = mls.getGeometryN(i);
const startNode = line.getCoordinateN(0);
const endNode = line.getCoordinateN(line.getNumPoints() - 1);
if (prevSubgraphNodes.contains(startNode)) return false;
if (prevSubgraphNodes.contains(endNode)) return false;
if (lastNode !== null) if (!startNode.equals(lastNode)) {
prevSubgraphNodes.addAll(currNodes);
currNodes.clear();
}
currNodes.add(startNode);
currNodes.add(endNode);
lastNode = endNode;
}
return true;
}
static reverse(line) {
const pts = line.getCoordinates();
const revPts = new Array(pts.length).fill(null);
const len = pts.length;
for (let i = 0; i < len; i++) revPts[len - 1 - i] = new Coordinate(pts[i]);
return line.getFactory().createLineString(revPts);
}
static sequence(geom) {
const sequencer = new LineSequencer();
sequencer.add(geom);
return sequencer.getSequencedLineStrings();
}
addLine(lineString) {
if (this._factory === null) this._factory = lineString.getFactory();
this._graph.addEdge(lineString);
this._lineCount++;
}
hasSequence(graph) {
let oddDegreeCount = 0;
for (let i = graph.nodeIterator(); i.hasNext();) {
const node = i.next();
if (node.getDegree() % 2 === 1) oddDegreeCount++;
}
return oddDegreeCount <= 2;
}
computeSequence() {
if (this._isRun) return null;
this._isRun = true;
const sequences = this.findSequences();
if (sequences === null) return null;
this._sequencedGeometry = this.buildSequencedGeometry(sequences);
this._isSequenceable = true;
const finalLineCount = this._sequencedGeometry.getNumGeometries();
Assert.isTrue(this._lineCount === finalLineCount, 'Lines were missing from result');
Assert.isTrue(this._sequencedGeometry instanceof LineString || this._sequencedGeometry instanceof MultiLineString, 'Result is not lineal');
}
findSequences() {
const sequences = new ArrayList();
const csFinder = new ConnectedSubgraphFinder(this._graph);
const subgraphs = csFinder.getConnectedSubgraphs();
for (let i = subgraphs.iterator(); i.hasNext();) {
const subgraph = i.next();
if (this.hasSequence(subgraph)) {
const seq = this.findSequence(subgraph);
sequences.add(seq);
} else {
return null;
}
}
return sequences;
}
addReverseSubpath(de, lit, expectedClosed) {
const endNode = de.getToNode();
let fromNode = null;
while (true) {
lit.add(de.getSym());
de.getEdge().setVisited(true);
fromNode = de.getFromNode();
const unvisitedOutDE = LineSequencer.findUnvisitedBestOrientedDE(fromNode);
if (unvisitedOutDE === null) break;
de = unvisitedOutDE.getSym();
}
if (expectedClosed) Assert.isTrue(fromNode === endNode, 'path not contiguous');
}
findSequence(graph) {
GraphComponent.setVisited(graph.edgeIterator(), false);
const startNode = LineSequencer.findLowestDegreeNode(graph);
const startDE = startNode.getOutEdges().iterator().next();
const startDESym = startDE.getSym();
const seq = new LinkedList();
const lit = seq.listIterator();
this.addReverseSubpath(startDESym, lit, false);
while (lit.hasPrevious()) {
const prev = lit.previous();
const unvisitedOutDE = LineSequencer.findUnvisitedBestOrientedDE(prev.getFromNode());
if (unvisitedOutDE !== null) this.addReverseSubpath(unvisitedOutDE.getSym(), lit, true);
}
const orientedSeq = this.orient(seq);
return orientedSeq;
}
reverse(seq) {
const newSeq = new LinkedList();
for (let i = seq.iterator(); i.hasNext();) {
const de = i.next();
newSeq.addFirst(de.getSym());
}
return newSeq;
}
orient(seq) {
const startEdge = seq.get(0);
const endEdge = seq.get(seq.size() - 1);
const startNode = startEdge.getFromNode();
const endNode = endEdge.getToNode();
let flipSeq = false;
const hasDegree1Node = startNode.getDegree() === 1 || endNode.getDegree() === 1;
if (hasDegree1Node) {
let hasObviousStartNode = false;
if (endEdge.getToNode().getDegree() === 1 && endEdge.getEdgeDirection() === false) {
hasObviousStartNode = true;
flipSeq = true;
}
if (startEdge.getFromNode().getDegree() === 1 && startEdge.getEdgeDirection() === true) {
hasObviousStartNode = true;
flipSeq = false;
}
if (!hasObviousStartNode) if (startEdge.getFromNode().getDegree() === 1) flipSeq = true;
}
if (flipSeq) return this.reverse(seq);
return seq;
}
buildSequencedGeometry(sequences) {
const lines = new ArrayList();
for (let i1 = sequences.iterator(); i1.hasNext();) {
const seq = i1.next();
for (let i2 = seq.iterator(); i2.hasNext();) {
const de = i2.next();
const e = de.getEdge();
const line = e.getLine();
let lineToAdd = line;
if (!de.getEdgeDirection() && !line.isClosed()) lineToAdd = LineSequencer.reverse(line);
lines.add(lineToAdd);
}
}
if (lines.size() === 0) return this._factory.createMultiLineString(new Array(0).fill(null));
return this._factory.buildGeometry(lines);
}
getSequencedLineStrings() {
this.computeSequence();
return this._sequencedGeometry;
}
isSequenceable() {
this.computeSequence();
return this._isSequenceable;
}
add() {
if (hasInterface(arguments[0], Collection)) {
const geometries = arguments[0];
for (let i = geometries.iterator(); i.hasNext();) {
const geometry = i.next();
this.add(geometry);
}
} else if (arguments[0] instanceof Geometry) {
const geometry = arguments[0];
geometry.apply(new class {
get interfaces_() {
return [GeometryComponentFilter];
}
filter(component) {
if (component instanceof LineString) this.addLine(component);
}
}());
}
}
}
var linemerge = /*#__PURE__*/Object.freeze({
__proto__: null,
LineMerger: LineMerger,
LineSequencer: LineSequencer
});
class LineStringSnapper {
constructor() {
LineStringSnapper.constructor_.apply(this, arguments);
}
static constructor_() {
this._snapTolerance = 0.0;
this._srcPts = null;
this._seg = new LineSegment();
this._allowSnappingToSourceVertices = false;
this._isClosed = false;
if (arguments[0] instanceof LineString && typeof arguments[1] === 'number') {
const srcLine = arguments[0],
snapTolerance = arguments[1];
LineStringSnapper.constructor_.call(this, srcLine.getCoordinates(), snapTolerance);
} else if (arguments[0] instanceof Array && typeof arguments[1] === 'number') {
const srcPts = arguments[0],
snapTolerance = arguments[1];
this._srcPts = srcPts;
this._isClosed = LineStringSnapper.isClosed(srcPts);
this._snapTolerance = snapTolerance;
}
}
static isClosed(pts) {
if (pts.length <= 1) return false;
return pts[0].equals2D(pts[pts.length - 1]);
}
snapVertices(srcCoords, snapPts) {
const end = this._isClosed ? srcCoords.size() - 1 : srcCoords.size();
for (let i = 0; i < end; i++) {
const srcPt = srcCoords.get(i);
const snapVert = this.findSnapForVertex(srcPt, snapPts);
if (snapVert !== null) {
srcCoords.set(i, new Coordinate(snapVert));
if (i === 0 && this._isClosed) srcCoords.set(srcCoords.size() - 1, new Coordinate(snapVert));
}
}
}
findSnapForVertex(pt, snapPts) {
for (let i = 0; i < snapPts.length; i++) {
if (pt.equals2D(snapPts[i])) return null;
if (pt.distance(snapPts[i]) < this._snapTolerance) return snapPts[i];
}
return null;
}
snapTo(snapPts) {
const coordList = new CoordinateList(this._srcPts);
this.snapVertices(coordList, snapPts);
this.snapSegments(coordList, snapPts);
const newPts = coordList.toCoordinateArray();
return newPts;
}
snapSegments(srcCoords, snapPts) {
if (snapPts.length === 0) return null;
let distinctPtCount = snapPts.length;
if (snapPts[0].equals2D(snapPts[snapPts.length - 1])) distinctPtCount = snapPts.length - 1;
for (let i = 0; i < distinctPtCount; i++) {
const snapPt = snapPts[i];
const index = this.findSegmentIndexToSnap(snapPt, srcCoords);
if (index >= 0) srcCoords.add(index + 1, new Coordinate(snapPt), false);
}
}
findSegmentIndexToSnap(snapPt, srcCoords) {
let minDist = Double.MAX_VALUE;
let snapIndex = -1;
for (let i = 0; i < srcCoords.size() - 1; i++) {
this._seg.p0 = srcCoords.get(i);
this._seg.p1 = srcCoords.get(i + 1);
if (this._seg.p0.equals2D(snapPt) || this._seg.p1.equals2D(snapPt)) if (this._allowSnappingToSourceVertices) continue;else return -1;
const dist = this._seg.distance(snapPt);
if (dist < this._snapTolerance && dist < minDist) {
minDist = dist;
snapIndex = i;
}
}
return snapIndex;
}
setAllowSnappingToSourceVertices(allowSnappingToSourceVertices) {
this._allowSnappingToSourceVertices = allowSnappingToSourceVertices;
}
}
class GeometrySnapper {
constructor() {
GeometrySnapper.constructor_.apply(this, arguments);
}
static constructor_() {
this._srcGeom = null;
const srcGeom = arguments[0];
this._srcGeom = srcGeom;
}
static snap(g0, g1, snapTolerance) {
const snapGeom = new Array(2).fill(null);
const snapper0 = new GeometrySnapper(g0);
snapGeom[0] = snapper0.snapTo(g1, snapTolerance);
const snapper1 = new GeometrySnapper(g1);
snapGeom[1] = snapper1.snapTo(snapGeom[0], snapTolerance);
return snapGeom;
}
static computeOverlaySnapTolerance() {
if (arguments.length === 1) {
const g = arguments[0];
let snapTolerance = GeometrySnapper.computeSizeBasedSnapTolerance(g);
const pm = g.getPrecisionModel();
if (pm.getType() === PrecisionModel.FIXED) {
const fixedSnapTol = 1 / pm.getScale() * 2 / 1.415;
if (fixedSnapTol > snapTolerance) snapTolerance = fixedSnapTol;
}
return snapTolerance;
} else if (arguments.length === 2) {
const g0 = arguments[0],
g1 = arguments[1];
return Math.min(GeometrySnapper.computeOverlaySnapTolerance(g0), GeometrySnapper.computeOverlaySnapTolerance(g1));
}
}
static computeSizeBasedSnapTolerance(g) {
const env = g.getEnvelopeInternal();
const minDimension = Math.min(env.getHeight(), env.getWidth());
const snapTol = minDimension * GeometrySnapper.SNAP_PRECISION_FACTOR;
return snapTol;
}
static snapToSelf(geom, snapTolerance, cleanResult) {
const snapper0 = new GeometrySnapper(geom);
return snapper0.snapToSelf(snapTolerance, cleanResult);
}
snapTo(snapGeom, snapTolerance) {
const snapPts = this.extractTargetCoordinates(snapGeom);
const snapTrans = new SnapTransformer(snapTolerance, snapPts);
return snapTrans.transform(this._srcGeom);
}
snapToSelf(snapTolerance, cleanResult) {
const snapPts = this.extractTargetCoordinates(this._srcGeom);
const snapTrans = new SnapTransformer(snapTolerance, snapPts, true);
const snappedGeom = snapTrans.transform(this._srcGeom);
let result = snappedGeom;
if (cleanResult && hasInterface(result, Polygonal)) result = snappedGeom.buffer(0);
return result;
}
computeSnapTolerance(ringPts) {
const minSegLen = this.computeMinimumSegmentLength(ringPts);
const snapTol = minSegLen / 10;
return snapTol;
}
extractTargetCoordinates(g) {
const ptSet = new TreeSet();
const pts = g.getCoordinates();
for (let i = 0; i < pts.length; i++) ptSet.add(pts[i]);
return ptSet.toArray(new Array(0).fill(null));
}
computeMinimumSegmentLength(pts) {
let minSegLen = Double.MAX_VALUE;
for (let i = 0; i < pts.length - 1; i++) {
const segLen = pts[i].distance(pts[i + 1]);
if (segLen < minSegLen) minSegLen = segLen;
}
return minSegLen;
}
}
GeometrySnapper.SNAP_PRECISION_FACTOR = 1e-9;
class SnapTransformer extends GeometryTransformer {
constructor() {
super();
SnapTransformer.constructor_.apply(this, arguments);
}
static constructor_() {
this._snapTolerance = null;
this._snapPts = null;
this._isSelfSnap = false;
if (arguments.length === 2) {
const snapTolerance = arguments[0],
snapPts = arguments[1];
this._snapTolerance = snapTolerance;
this._snapPts = snapPts;
} else if (arguments.length === 3) {
const snapTolerance = arguments[0],
snapPts = arguments[1],
isSelfSnap = arguments[2];
this._snapTolerance = snapTolerance;
this._snapPts = snapPts;
this._isSelfSnap = isSelfSnap;
}
}
snapLine(srcPts, snapPts) {
const snapper = new LineStringSnapper(srcPts, this._snapTolerance);
snapper.setAllowSnappingToSourceVertices(this._isSelfSnap);
return snapper.snapTo(snapPts);
}
transformCoordinates(coords, parent) {
const srcPts = coords.toCoordinateArray();
const newPts = this.snapLine(srcPts, this._snapPts);
return this._factory.getCoordinateSequenceFactory().create(newPts);
}
}
var snap = /*#__PURE__*/Object.freeze({
__proto__: null,
GeometrySnapper: GeometrySnapper,
LineStringSnapper: LineStringSnapper
});
class BasicSegmentString {
constructor() {
BasicSegmentString.constructor_.apply(this, arguments);
}
static constructor_() {
this._pts = null;
this._data = null;
const pts = arguments[0],
data = arguments[1];
this._pts = pts;
this._data = data;
}
getCoordinates() {
return this._pts;
}
size() {
return this._pts.length;
}
getCoordinate(i) {
return this._pts[i];
}
isClosed() {
return this._pts[0].equals(this._pts[this._pts.length - 1]);
}
getSegmentOctant(index) {
if (index === this._pts.length - 1) return -1;
return Octant.octant(this.getCoordinate(index), this.getCoordinate(index + 1));
}
setData(data) {
this._data = data;
}
getData() {
return this._data;
}
toString() {
return WKTWriter.toLineString(new CoordinateArraySequence(this._pts));
}
get interfaces_() {
return [SegmentString];
}
}
class NodingIntersectionFinder {
constructor() {
NodingIntersectionFinder.constructor_.apply(this, arguments);
}
static constructor_() {
this._findAllIntersections = false;
this._isCheckEndSegmentsOnly = false;
this._keepIntersections = true;
this._isInteriorIntersectionsOnly = false;
this._li = null;
this._interiorIntersection = null;
this._intSegments = null;
this._intersections = new ArrayList();
this._intersectionCount = 0;
const li = arguments[0];
this._li = li;
this._interiorIntersection = null;
}
static createAllIntersectionsFinder(li) {
const finder = new NodingIntersectionFinder(li);
finder.setFindAllIntersections(true);
return finder;
}
static isInteriorVertexIntersection() {
if (arguments.length === 4) {
const p0 = arguments[0],
p1 = arguments[1],
isEnd0 = arguments[2],
isEnd1 = arguments[3];
if (isEnd0 && isEnd1) return false;
if (p0.equals2D(p1)) return true;
return false;
} else if (arguments.length === 8) {
const p00 = arguments[0],
p01 = arguments[1],
p10 = arguments[2],
p11 = arguments[3],
isEnd00 = arguments[4],
isEnd01 = arguments[5],
isEnd10 = arguments[6],
isEnd11 = arguments[7];
if (NodingIntersectionFinder.isInteriorVertexIntersection(p00, p10, isEnd00, isEnd10)) return true;
if (NodingIntersectionFinder.isInteriorVertexIntersection(p00, p11, isEnd00, isEnd11)) return true;
if (NodingIntersectionFinder.isInteriorVertexIntersection(p01, p10, isEnd01, isEnd10)) return true;
if (NodingIntersectionFinder.isInteriorVertexIntersection(p01, p11, isEnd01, isEnd11)) return true;
return false;
}
}
static createInteriorIntersectionCounter(li) {
const finder = new NodingIntersectionFinder(li);
finder.setInteriorIntersectionsOnly(true);
finder.setFindAllIntersections(true);
finder.setKeepIntersections(false);
return finder;
}
static createIntersectionCounter(li) {
const finder = new NodingIntersectionFinder(li);
finder.setFindAllIntersections(true);
finder.setKeepIntersections(false);
return finder;
}
static isEndSegment(segStr, index) {
if (index === 0) return true;
if (index >= segStr.size() - 2) return true;
return false;
}
static createAnyIntersectionFinder(li) {
return new NodingIntersectionFinder(li);
}
static createInteriorIntersectionsFinder(li) {
const finder = new NodingIntersectionFinder(li);
finder.setFindAllIntersections(true);
finder.setInteriorIntersectionsOnly(true);
return finder;
}
setCheckEndSegmentsOnly(isCheckEndSegmentsOnly) {
this._isCheckEndSegmentsOnly = isCheckEndSegmentsOnly;
}
getIntersectionSegments() {
return this._intSegments;
}
count() {
return this._intersectionCount;
}
getIntersections() {
return this._intersections;
}
setFindAllIntersections(findAllIntersections) {
this._findAllIntersections = findAllIntersections;
}
setKeepIntersections(keepIntersections) {
this._keepIntersections = keepIntersections;
}
getIntersection() {
return this._interiorIntersection;
}
processIntersections(e0, segIndex0, e1, segIndex1) {
if (!this._findAllIntersections && this.hasIntersection()) return null;
const isSameSegString = e0 === e1;
const isSameSegment = isSameSegString && segIndex0 === segIndex1;
if (isSameSegment) return null;
if (this._isCheckEndSegmentsOnly) {
const isEndSegPresent = NodingIntersectionFinder.isEndSegment(e0, segIndex0) || NodingIntersectionFinder.isEndSegment(e1, segIndex1);
if (!isEndSegPresent) return null;
}
const p00 = e0.getCoordinate(segIndex0);
const p01 = e0.getCoordinate(segIndex0 + 1);
const p10 = e1.getCoordinate(segIndex1);
const p11 = e1.getCoordinate(segIndex1 + 1);
const isEnd00 = segIndex0 === 0;
const isEnd01 = segIndex0 + 2 === e0.size();
const isEnd10 = segIndex1 === 0;
const isEnd11 = segIndex1 + 2 === e1.size();
this._li.computeIntersection(p00, p01, p10, p11);
const isInteriorInt = this._li.hasIntersection() && this._li.isInteriorIntersection();
let isInteriorVertexInt = false;
if (!this._isInteriorIntersectionsOnly) {
const isAdjacentSegment = isSameSegString && Math.abs(segIndex1 - segIndex0) <= 1;
isInteriorVertexInt = !isAdjacentSegment && NodingIntersectionFinder.isInteriorVertexIntersection(p00, p01, p10, p11, isEnd00, isEnd01, isEnd10, isEnd11);
}
if (isInteriorInt || isInteriorVertexInt) {
this._intSegments = new Array(4).fill(null);
this._intSegments[0] = p00;
this._intSegments[1] = p01;
this._intSegments[2] = p10;
this._intSegments[3] = p11;
this._interiorIntersection = this._li.getIntersection(0);
if (this._keepIntersections) this._intersections.add(this._interiorIntersection);
this._intersectionCount++;
}
}
hasIntersection() {
return this._interiorIntersection !== null;
}
isDone() {
if (this._findAllIntersections) return false;
return this._interiorIntersection !== null;
}
setInteriorIntersectionsOnly(isInteriorIntersectionsOnly) {
this._isInteriorIntersectionsOnly = isInteriorIntersectionsOnly;
}
get interfaces_() {
return [SegmentIntersector];
}
}
class FastNodingValidator {
constructor() {
FastNodingValidator.constructor_.apply(this, arguments);
}
static constructor_() {
this._li = new RobustLineIntersector();
this._segStrings = null;
this._findAllIntersections = false;
this._segInt = null;
this._isValid = true;
const segStrings = arguments[0];
this._segStrings = segStrings;
}
static computeIntersections(segStrings) {
const nv = new FastNodingValidator(segStrings);
nv.setFindAllIntersections(true);
nv.isValid();
return nv.getIntersections();
}
execute() {
if (this._segInt !== null) return null;
this.checkInteriorIntersections();
}
getIntersections() {
return this._segInt.getIntersections();
}
isValid() {
this.execute();
return this._isValid;
}
setFindAllIntersections(findAllIntersections) {
this._findAllIntersections = findAllIntersections;
}
checkInteriorIntersections() {
this._isValid = true;
this._segInt = new NodingIntersectionFinder(this._li);
this._segInt.setFindAllIntersections(this._findAllIntersections);
const noder = new MCIndexNoder();
noder.setSegmentIntersector(this._segInt);
noder.computeNodes(this._segStrings);
if (this._segInt.hasIntersection()) {
this._isValid = false;
return null;
}
}
checkValid() {
this.execute();
if (!this._isValid) throw new TopologyException(this.getErrorMessage(), this._segInt.getIntersection());
}
getErrorMessage() {
if (this._isValid) return 'no intersections found';
const intSegs = this._segInt.getIntersectionSegments();
return 'found non-noded intersection between ' + WKTWriter.toLineString(intSegs[0], intSegs[1]) + ' and ' + WKTWriter.toLineString(intSegs[2], intSegs[3]);
}
}
class EdgeNodingValidator {
constructor() {
EdgeNodingValidator.constructor_.apply(this, arguments);
}
static constructor_() {
this._nv = null;
const edges = arguments[0];
this._nv = new FastNodingValidator(EdgeNodingValidator.toSegmentStrings(edges));
}
static toSegmentStrings(edges) {
const segStrings = new ArrayList();
for (let i = edges.iterator(); i.hasNext();) {
const e = i.next();
segStrings.add(new BasicSegmentString(e.getCoordinates(), e));
}
return segStrings;
}
static checkValid(edges) {
const validator = new EdgeNodingValidator(edges);
validator.checkValid();
}
checkValid() {
this._nv.checkValid();
}
}
class LineBuilder {
constructor() {
LineBuilder.constructor_.apply(this, arguments);
}
static constructor_() {
this._op = null;
this._geometryFactory = null;
this._ptLocator = null;
this._lineEdgesList = new ArrayList();
this._resultLineList = new ArrayList();
const op = arguments[0],
geometryFactory = arguments[1],
ptLocator = arguments[2];
this._op = op;
this._geometryFactory = geometryFactory;
this._ptLocator = ptLocator;
}
collectLines(opCode) {
for (let it = this._op.getGraph().getEdgeEnds().iterator(); it.hasNext();) {
const de = it.next();
this.collectLineEdge(de, opCode, this._lineEdgesList);
this.collectBoundaryTouchEdge(de, opCode, this._lineEdgesList);
}
}
labelIsolatedLine(e, targetIndex) {
const loc = this._ptLocator.locate(e.getCoordinate(), this._op.getArgGeometry(targetIndex));
e.getLabel().setLocation(targetIndex, loc);
}
build(opCode) {
this.findCoveredLineEdges();
this.collectLines(opCode);
this.buildLines(opCode);
return this._resultLineList;
}
collectLineEdge(de, opCode, edges) {
const label = de.getLabel();
const e = de.getEdge();
if (de.isLineEdge()) if (!de.isVisited() && OverlayOp.isResultOfOp(label, opCode) && !e.isCovered()) {
edges.add(e);
de.setVisitedEdge(true);
}
}
findCoveredLineEdges() {
for (let nodeit = this._op.getGraph().getNodes().iterator(); nodeit.hasNext();) {
const node = nodeit.next();
node.getEdges().findCoveredLineEdges();
}
for (let it = this._op.getGraph().getEdgeEnds().iterator(); it.hasNext();) {
const de = it.next();
const e = de.getEdge();
if (de.isLineEdge() && !e.isCoveredSet()) {
const isCovered = this._op.isCoveredByA(de.getCoordinate());
e.setCovered(isCovered);
}
}
}
labelIsolatedLines(edgesList) {
for (let it = edgesList.iterator(); it.hasNext();) {
const e = it.next();
const label = e.getLabel();
if (e.isIsolated()) if (label.isNull(0)) this.labelIsolatedLine(e, 0);else this.labelIsolatedLine(e, 1);
}
}
buildLines(opCode) {
for (let it = this._lineEdgesList.iterator(); it.hasNext();) {
const e = it.next();
const line = this._geometryFactory.createLineString(e.getCoordinates());
this._resultLineList.add(line);
e.setInResult(true);
}
}
collectBoundaryTouchEdge(de, opCode, edges) {
const label = de.getLabel();
if (de.isLineEdge()) return null;
if (de.isVisited()) return null;
if (de.isInteriorAreaEdge()) return null;
if (de.getEdge().isInResult()) return null;
Assert.isTrue(!(de.isInResult() || de.getSym().isInResult()) || !de.getEdge().isInResult());
if (OverlayOp.isResultOfOp(label, opCode) && opCode === OverlayOp.INTERSECTION) {
edges.add(de.getEdge());
de.setVisitedEdge(true);
}
}
}
class PointBuilder {
constructor() {
PointBuilder.constructor_.apply(this, arguments);
}
static constructor_() {
this._op = null;
this._geometryFactory = null;
this._resultPointList = new ArrayList();
const op = arguments[0],
geometryFactory = arguments[1];
this._op = op;
this._geometryFactory = geometryFactory;
}
filterCoveredNodeToPoint(n) {
const coord = n.getCoordinate();
if (!this._op.isCoveredByLA(coord)) {
const pt = this._geometryFactory.createPoint(coord);
this._resultPointList.add(pt);
}
}
extractNonCoveredResultNodes(opCode) {
for (let nodeit = this._op.getGraph().getNodes().iterator(); nodeit.hasNext();) {
const n = nodeit.next();
if (n.isInResult()) continue;
if (n.isIncidentEdgeInResult()) continue;
if (n.getEdges().getDegree() === 0 || opCode === OverlayOp.INTERSECTION) {
const label = n.getLabel();
if (OverlayOp.isResultOfOp(label, opCode)) this.filterCoveredNodeToPoint(n);
}
}
}
build(opCode) {
this.extractNonCoveredResultNodes(opCode);
return this._resultPointList;
}
}
class CommonBits {
constructor() {
this._isFirst = true;
this._commonMantissaBitsCount = 53;
this._commonBits = new Long();
this._commonSignExp = null;
}
getCommon() {
return Double.longBitsToDouble(this._commonBits);
}
add(num) {
const numBits = Double.doubleToLongBits(num);
if (this._isFirst) {
this._commonBits = numBits;
this._commonSignExp = CommonBits.signExpBits(this._commonBits);
this._isFirst = false;
return null;
}
const numSignExp = CommonBits.signExpBits(numBits);
if (numSignExp !== this._commonSignExp) {
this._commonBits.high = 0 | 0;
this._commonBits.low = 0 | 0;
return null;
}
this._commonMantissaBitsCount = CommonBits.numCommonMostSigMantissaBits(this._commonBits, numBits);
this._commonBits = CommonBits.zeroLowerBits(this._commonBits, 64 - (12 + this._commonMantissaBitsCount));
}
toString() {
if (arguments.length === 1) {
const bits = arguments[0];
const x = Double.longBitsToDouble(bits);
const numStr = Long.toBinaryString(bits);
const padStr = '0000000000000000000000000000000000000000000000000000000000000000' + numStr;
const bitStr = padStr.substring(padStr.length - 64);
const str = bitStr.substring(0, 1) + ' ' + bitStr.substring(1, 12) + '(exp) ' + bitStr.substring(12) + ' [ ' + x + ' ]';
return str;
}
}
getClass() {
return CommonBits;
}
get interfaces_() {
return [];
}
static getBit(bits, i) {
const mask = 1 << i % 32;
if (i < 32) return (bits.low & mask) !== 0 ? 1 : 0;
return (bits.high & mask) !== 0 ? 1 : 0;
}
static signExpBits(num) {
return num.high >>> 20;
}
static zeroLowerBits(bits, nBits) {
let prop = 'low';
if (nBits > 32) {
bits.low = 0 | 0;
nBits %= 32;
prop = 'high';
}
if (nBits > 0) {
const mask = nBits < 32 ? ~((1 << nBits) - 1) : 0;
bits[prop] &= mask;
}
return bits;
}
static numCommonMostSigMantissaBits(num1, num2) {
let count = 0;
for (let i = 52; i >= 0; i--) {
if (CommonBits.getBit(num1, i) !== CommonBits.getBit(num2, i)) return count;
count++;
}
return 52;
}
}
class CommonBitsRemover {
constructor() {
CommonBitsRemover.constructor_.apply(this, arguments);
}
static constructor_() {
this._commonCoord = null;
this._ccFilter = new CommonCoordinateFilter();
}
addCommonBits(geom) {
const trans = new Translater(this._commonCoord);
geom.apply(trans);
geom.geometryChanged();
}
removeCommonBits(geom) {
if (this._commonCoord.x === 0.0 && this._commonCoord.y === 0.0) return geom;
const invCoord = new Coordinate(this._commonCoord);
invCoord.x = -invCoord.x;
invCoord.y = -invCoord.y;
const trans = new Translater(invCoord);
geom.apply(trans);
geom.geometryChanged();
return geom;
}
getCommonCoordinate() {
return this._commonCoord;
}
add(geom) {
geom.apply(this._ccFilter);
this._commonCoord = this._ccFilter.getCommonCoordinate();
}
}
class CommonCoordinateFilter {
constructor() {
CommonCoordinateFilter.constructor_.apply(this, arguments);
}
static constructor_() {
this._commonBitsX = new CommonBits();
this._commonBitsY = new CommonBits();
}
filter(coord) {
this._commonBitsX.add(coord.x);
this._commonBitsY.add(coord.y);
}
getCommonCoordinate() {
return new Coordinate(this._commonBitsX.getCommon(), this._commonBitsY.getCommon());
}
get interfaces_() {
return [CoordinateFilter];
}
}
class Translater {
constructor() {
Translater.constructor_.apply(this, arguments);
}
static constructor_() {
this.trans = null;
const trans = arguments[0];
this.trans = trans;
}
filter(seq, i) {
const xp = seq.getOrdinate(i, 0) + this.trans.x;
const yp = seq.getOrdinate(i, 1) + this.trans.y;
seq.setOrdinate(i, 0, xp);
seq.setOrdinate(i, 1, yp);
}
isDone() {
return false;
}
isGeometryChanged() {
return true;
}
get interfaces_() {
return [CoordinateSequenceFilter];
}
}
CommonBitsRemover.CommonCoordinateFilter = CommonCoordinateFilter;
CommonBitsRemover.Translater = Translater;
class SnapOverlayOp {
constructor() {
SnapOverlayOp.constructor_.apply(this, arguments);
}
static constructor_() {
this._geom = new Array(2).fill(null);
this._snapTolerance = null;
this._cbr = null;
const g1 = arguments[0],
g2 = arguments[1];
this._geom[0] = g1;
this._geom[1] = g2;
this.computeSnapTolerance();
}
static overlayOp(g0, g1, opCode) {
const op = new SnapOverlayOp(g0, g1);
return op.getResultGeometry(opCode);
}
static union(g0, g1) {
return SnapOverlayOp.overlayOp(g0, g1, OverlayOp.UNION);
}
static intersection(g0, g1) {
return SnapOverlayOp.overlayOp(g0, g1, OverlayOp.INTERSECTION);
}
static symDifference(g0, g1) {
return SnapOverlayOp.overlayOp(g0, g1, OverlayOp.SYMDIFFERENCE);
}
static difference(g0, g1) {
return SnapOverlayOp.overlayOp(g0, g1, OverlayOp.DIFFERENCE);
}
selfSnap(geom) {
const snapper0 = new GeometrySnapper(geom);
const snapGeom = snapper0.snapTo(geom, this._snapTolerance);
return snapGeom;
}
removeCommonBits(geom) {
this._cbr = new CommonBitsRemover();
this._cbr.add(geom[0]);
this._cbr.add(geom[1]);
const remGeom = new Array(2).fill(null);
remGeom[0] = this._cbr.removeCommonBits(geom[0].copy());
remGeom[1] = this._cbr.removeCommonBits(geom[1].copy());
return remGeom;
}
prepareResult(geom) {
this._cbr.addCommonBits(geom);
return geom;
}
getResultGeometry(opCode) {
const prepGeom = this.snap(this._geom);
const result = OverlayOp.overlayOp(prepGeom[0], prepGeom[1], opCode);
return this.prepareResult(result);
}
checkValid(g) {
if (!g.isValid()) System.out.println('Snapped geometry is invalid');
}
computeSnapTolerance() {
this._snapTolerance = GeometrySnapper.computeOverlaySnapTolerance(this._geom[0], this._geom[1]);
}
snap(geom) {
const remGeom = this.removeCommonBits(geom);
const snapGeom = GeometrySnapper.snap(remGeom[0], remGeom[1], this._snapTolerance);
return snapGeom;
}
}
class SnapIfNeededOverlayOp {
constructor() {
SnapIfNeededOverlayOp.constructor_.apply(this, arguments);
}
static constructor_() {
this._geom = new Array(2).fill(null);
const g1 = arguments[0],
g2 = arguments[1];
this._geom[0] = g1;
this._geom[1] = g2;
}
static overlayOp(g0, g1, opCode) {
const op = new SnapIfNeededOverlayOp(g0, g1);
return op.getResultGeometry(opCode);
}
static union(g0, g1) {
return SnapIfNeededOverlayOp.overlayOp(g0, g1, OverlayOp.UNION);
}
static intersection(g0, g1) {
return SnapIfNeededOverlayOp.overlayOp(g0, g1, OverlayOp.INTERSECTION);
}
static symDifference(g0, g1) {
return SnapIfNeededOverlayOp.overlayOp(g0, g1, OverlayOp.SYMDIFFERENCE);
}
static difference(g0, g1) {
return SnapIfNeededOverlayOp.overlayOp(g0, g1, OverlayOp.DIFFERENCE);
}
getResultGeometry(opCode) {
let result = null;
let isSuccess = false;
let savedException = null;
try {
result = OverlayOp.overlayOp(this._geom[0], this._geom[1], opCode);
const isValid = true;
if (isValid) isSuccess = true;
} catch (ex) {
if (ex instanceof RuntimeException) savedException = ex;else throw ex;
} finally {}
if (!isSuccess) try {
result = SnapOverlayOp.overlayOp(this._geom[0], this._geom[1], opCode);
} catch (ex) {
if (ex instanceof RuntimeException) throw savedException;else throw ex;
} finally {}
return result;
}
}
class GeometryGraphOperation {
constructor() {
GeometryGraphOperation.constructor_.apply(this, arguments);
}
static constructor_() {
this._li = new RobustLineIntersector();
this._resultPrecisionModel = null;
this._arg = null;
if (arguments.length === 1) {
const g0 = arguments[0];
this.setComputationPrecision(g0.getPrecisionModel());
this._arg = new Array(1).fill(null);
this._arg[0] = new GeometryGraph(0, g0);
} else if (arguments.length === 2) {
const g0 = arguments[0],
g1 = arguments[1];
GeometryGraphOperation.constructor_.call(this, g0, g1, BoundaryNodeRule.OGC_SFS_BOUNDARY_RULE);
} else if (arguments.length === 3) {
const g0 = arguments[0],
g1 = arguments[1],
boundaryNodeRule = arguments[2];
if (g0.getPrecisionModel().compareTo(g1.getPrecisionModel()) >= 0) this.setComputationPrecision(g0.getPrecisionModel());else this.setComputationPrecision(g1.getPrecisionModel());
this._arg = new Array(2).fill(null);
this._arg[0] = new GeometryGraph(0, g0, boundaryNodeRule);
this._arg[1] = new GeometryGraph(1, g1, boundaryNodeRule);
}
}
getArgGeometry(i) {
return this._arg[i].getGeometry();
}
setComputationPrecision(pm) {
this._resultPrecisionModel = pm;
this._li.setPrecisionModel(this._resultPrecisionModel);
}
}
class OverlayOp extends GeometryGraphOperation {
constructor() {
super();
OverlayOp.constructor_.apply(this, arguments);
}
static constructor_() {
this._ptLocator = new PointLocator();
this._geomFact = null;
this._resultGeom = null;
this._graph = null;
this._edgeList = new EdgeList();
this._resultPolyList = new ArrayList();
this._resultLineList = new ArrayList();
this._resultPointList = new ArrayList();
const g0 = arguments[0],
g1 = arguments[1];
GeometryGraphOperation.constructor_.call(this, g0, g1);
this._graph = new PlanarGraph$1(new OverlayNodeFactory());
this._geomFact = g0.getFactory();
}
static overlayOp(geom0, geom1, opCode) {
const gov = new OverlayOp(geom0, geom1);
const geomOv = gov.getResultGeometry(opCode);
return geomOv;
}
static union(geom, other) {
if (geom.isEmpty() || other.isEmpty()) {
if (geom.isEmpty() && other.isEmpty()) return OverlayOp.createEmptyResult(OverlayOp.UNION, geom, other, geom.getFactory());
if (geom.isEmpty()) return other.copy();
if (other.isEmpty()) return geom.copy();
}
if (geom.isGeometryCollection() || other.isGeometryCollection()) throw new IllegalArgumentException('This method does not support GeometryCollection arguments');
return SnapIfNeededOverlayOp.overlayOp(geom, other, OverlayOp.UNION);
}
static intersection(geom, other) {
if (geom.isEmpty() || other.isEmpty()) return OverlayOp.createEmptyResult(OverlayOp.INTERSECTION, geom, other, geom.getFactory());
if (geom.isGeometryCollection()) {
const g2 = other;
return GeometryCollectionMapper.map(geom, new class {
get interfaces_() {
return [MapOp];
}
map(g) {
return OverlayOp.intersection(g, g2);
}
}());
}
return SnapIfNeededOverlayOp.overlayOp(geom, other, OverlayOp.INTERSECTION);
}
static symDifference(geom, other) {
if (geom.isEmpty() || other.isEmpty()) {
if (geom.isEmpty() && other.isEmpty()) return OverlayOp.createEmptyResult(OverlayOp.SYMDIFFERENCE, geom, other, geom.getFactory());
if (geom.isEmpty()) return other.copy();
if (other.isEmpty()) return geom.copy();
}
if (geom.isGeometryCollection() || other.isGeometryCollection()) throw new IllegalArgumentException('This method does not support GeometryCollection arguments');
return SnapIfNeededOverlayOp.overlayOp(geom, other, OverlayOp.SYMDIFFERENCE);
}
static resultDimension(opCode, g0, g1) {
const dim0 = g0.getDimension();
const dim1 = g1.getDimension();
let resultDimension = -1;
switch (opCode) {
case OverlayOp.INTERSECTION:
resultDimension = Math.min(dim0, dim1);
break;
case OverlayOp.UNION:
resultDimension = Math.max(dim0, dim1);
break;
case OverlayOp.DIFFERENCE:
resultDimension = dim0;
break;
case OverlayOp.SYMDIFFERENCE:
resultDimension = Math.max(dim0, dim1);
break;
}
return resultDimension;
}
static createEmptyResult(overlayOpCode, a, b, geomFact) {
const resultDim = OverlayOp.resultDimension(overlayOpCode, a, b);
return geomFact.createEmpty(resultDim);
}
static difference(geom, other) {
if (geom.isEmpty()) return OverlayOp.createEmptyResult(OverlayOp.DIFFERENCE, geom, other, geom.getFactory());
if (other.isEmpty()) return geom.copy();
if (geom.isGeometryCollection() || other.isGeometryCollection()) throw new IllegalArgumentException('This method does not support GeometryCollection arguments');
return SnapIfNeededOverlayOp.overlayOp(geom, other, OverlayOp.DIFFERENCE);
}
static isResultOfOp() {
if (arguments.length === 2) {
const label = arguments[0],
opCode = arguments[1];
const loc0 = label.getLocation(0);
const loc1 = label.getLocation(1);
return OverlayOp.isResultOfOp(loc0, loc1, opCode);
} else if (arguments.length === 3) {
let loc0 = arguments[0],
loc1 = arguments[1],
overlayOpCode = arguments[2];
if (loc0 === Location.BOUNDARY) loc0 = Location.INTERIOR;
if (loc1 === Location.BOUNDARY) loc1 = Location.INTERIOR;
switch (overlayOpCode) {
case OverlayOp.INTERSECTION:
return loc0 === Location.INTERIOR && loc1 === Location.INTERIOR;
case OverlayOp.UNION:
return loc0 === Location.INTERIOR || loc1 === Location.INTERIOR;
case OverlayOp.DIFFERENCE:
return loc0 === Location.INTERIOR && loc1 !== Location.INTERIOR;
case OverlayOp.SYMDIFFERENCE:
return loc0 === Location.INTERIOR && loc1 !== Location.INTERIOR || loc0 !== Location.INTERIOR && loc1 === Location.INTERIOR;
}
return false;
}
}
insertUniqueEdge(e) {
const existingEdge = this._edgeList.findEqualEdge(e);
if (existingEdge !== null) {
const existingLabel = existingEdge.getLabel();
let labelToMerge = e.getLabel();
if (!existingEdge.isPointwiseEqual(e)) {
labelToMerge = new Label(e.getLabel());
labelToMerge.flip();
}
const depth = existingEdge.getDepth();
if (depth.isNull()) depth.add(existingLabel);
depth.add(labelToMerge);
existingLabel.merge(labelToMerge);
} else {
this._edgeList.add(e);
}
}
getGraph() {
return this._graph;
}
cancelDuplicateResultEdges() {
for (let it = this._graph.getEdgeEnds().iterator(); it.hasNext();) {
const de = it.next();
const sym = de.getSym();
if (de.isInResult() && sym.isInResult()) {
de.setInResult(false);
sym.setInResult(false);
}
}
}
isCoveredByLA(coord) {
if (this.isCovered(coord, this._resultLineList)) return true;
if (this.isCovered(coord, this._resultPolyList)) return true;
return false;
}
computeGeometry(resultPointList, resultLineList, resultPolyList, opcode) {
const geomList = new ArrayList();
geomList.addAll(resultPointList);
geomList.addAll(resultLineList);
geomList.addAll(resultPolyList);
if (geomList.isEmpty()) return OverlayOp.createEmptyResult(opcode, this._arg[0].getGeometry(), this._arg[1].getGeometry(), this._geomFact);
return this._geomFact.buildGeometry(geomList);
}
mergeSymLabels() {
for (let nodeit = this._graph.getNodes().iterator(); nodeit.hasNext();) {
const node = nodeit.next();
node.getEdges().mergeSymLabels();
}
}
isCovered(coord, geomList) {
for (let it = geomList.iterator(); it.hasNext();) {
const geom = it.next();
const loc = this._ptLocator.locate(coord, geom);
if (loc !== Location.EXTERIOR) return true;
}
return false;
}
replaceCollapsedEdges() {
const newEdges = new ArrayList();
for (let it = this._edgeList.iterator(); it.hasNext();) {
const e = it.next();
if (e.isCollapsed()) {
it.remove();
newEdges.add(e.getCollapsedEdge());
}
}
this._edgeList.addAll(newEdges);
}
updateNodeLabelling() {
for (let nodeit = this._graph.getNodes().iterator(); nodeit.hasNext();) {
const node = nodeit.next();
const lbl = node.getEdges().getLabel();
node.getLabel().merge(lbl);
}
}
getResultGeometry(overlayOpCode) {
this.computeOverlay(overlayOpCode);
return this._resultGeom;
}
insertUniqueEdges(edges) {
for (let i = edges.iterator(); i.hasNext();) {
const e = i.next();
this.insertUniqueEdge(e);
}
}
computeOverlay(opCode) {
this.copyPoints(0);
this.copyPoints(1);
this._arg[0].computeSelfNodes(this._li, false);
this._arg[1].computeSelfNodes(this._li, false);
this._arg[0].computeEdgeIntersections(this._arg[1], this._li, true);
const baseSplitEdges = new ArrayList();
this._arg[0].computeSplitEdges(baseSplitEdges);
this._arg[1].computeSplitEdges(baseSplitEdges);
this.insertUniqueEdges(baseSplitEdges);
this.computeLabelsFromDepths();
this.replaceCollapsedEdges();
EdgeNodingValidator.checkValid(this._edgeList.getEdges());
this._graph.addEdges(this._edgeList.getEdges());
this.computeLabelling();
this.labelIncompleteNodes();
this.findResultAreaEdges(opCode);
this.cancelDuplicateResultEdges();
const polyBuilder = new PolygonBuilder(this._geomFact);
polyBuilder.add(this._graph);
this._resultPolyList = polyBuilder.getPolygons();
const lineBuilder = new LineBuilder(this, this._geomFact, this._ptLocator);
this._resultLineList = lineBuilder.build(opCode);
const pointBuilder = new PointBuilder(this, this._geomFact, this._ptLocator);
this._resultPointList = pointBuilder.build(opCode);
this._resultGeom = this.computeGeometry(this._resultPointList, this._resultLineList, this._resultPolyList, opCode);
}
labelIncompleteNode(n, targetIndex) {
const loc = this._ptLocator.locate(n.getCoordinate(), this._arg[targetIndex].getGeometry());
n.getLabel().setLocation(targetIndex, loc);
}
copyPoints(argIndex) {
for (let i = this._arg[argIndex].getNodeIterator(); i.hasNext();) {
const graphNode = i.next();
const newNode = this._graph.addNode(graphNode.getCoordinate());
newNode.setLabel(argIndex, graphNode.getLabel().getLocation(argIndex));
}
}
findResultAreaEdges(opCode) {
for (let it = this._graph.getEdgeEnds().iterator(); it.hasNext();) {
const de = it.next();
const label = de.getLabel();
if (label.isArea() && !de.isInteriorAreaEdge() && OverlayOp.isResultOfOp(label.getLocation(0, Position.RIGHT), label.getLocation(1, Position.RIGHT), opCode)) de.setInResult(true);
}
}
computeLabelsFromDepths() {
for (let it = this._edgeList.iterator(); it.hasNext();) {
const e = it.next();
const lbl = e.getLabel();
const depth = e.getDepth();
if (!depth.isNull()) {
depth.normalize();
for (let i = 0; i < 2; i++) if (!lbl.isNull(i) && lbl.isArea() && !depth.isNull(i)) if (depth.getDelta(i) === 0) {
lbl.toLine(i);
} else {
Assert.isTrue(!depth.isNull(i, Position.LEFT), 'depth of LEFT side has not been initialized');
lbl.setLocation(i, Position.LEFT, depth.getLocation(i, Position.LEFT));
Assert.isTrue(!depth.isNull(i, Position.RIGHT), 'depth of RIGHT side has not been initialized');
lbl.setLocation(i, Position.RIGHT, depth.getLocation(i, Position.RIGHT));
}
}
}
}
computeLabelling() {
for (let nodeit = this._graph.getNodes().iterator(); nodeit.hasNext();) {
const node = nodeit.next();
node.getEdges().computeLabelling(this._arg);
}
this.mergeSymLabels();
this.updateNodeLabelling();
}
labelIncompleteNodes() {
for (let ni = this._graph.getNodes().iterator(); ni.hasNext();) {
const n = ni.next();
const label = n.getLabel();
if (n.isIsolated()) if (label.isNull(0)) this.labelIncompleteNode(n, 0);else this.labelIncompleteNode(n, 1);
n.getEdges().updateLabelling(label);
}
}
isCoveredByA(coord) {
if (this.isCovered(coord, this._resultPolyList)) return true;
return false;
}
}
OverlayOp.INTERSECTION = 1;
OverlayOp.UNION = 2;
OverlayOp.DIFFERENCE = 3;
OverlayOp.SYMDIFFERENCE = 4;
var overlay = /*#__PURE__*/Object.freeze({
__proto__: null,
snap: snap,
OverlayOp: OverlayOp
});
class PolygonizeDirectedEdge extends DirectedEdge {
constructor() {
super();
PolygonizeDirectedEdge.constructor_.apply(this, arguments);
}
static constructor_() {
this._edgeRing = null;
this._next = null;
this._label = -1;
const from = arguments[0],
to = arguments[1],
directionPt = arguments[2],
edgeDirection = arguments[3];
DirectedEdge.constructor_.call(this, from, to, directionPt, edgeDirection);
}
getNext() {
return this._next;
}
isInRing() {
return this._edgeRing !== null;
}
setRing(edgeRing) {
this._edgeRing = edgeRing;
}
setLabel(label) {
this._label = label;
}
getLabel() {
return this._label;
}
setNext(next) {
this._next = next;
}
getRing() {
return this._edgeRing;
}
}
class PolygonizeEdge extends Edge {
constructor() {
super();
PolygonizeEdge.constructor_.apply(this, arguments);
}
static constructor_() {
this._line = null;
const line = arguments[0];
this._line = line;
}
getLine() {
return this._line;
}
}
class ConnectedInteriorTester {
constructor() {
ConnectedInteriorTester.constructor_.apply(this, arguments);
}
static constructor_() {
this._geometryFactory = new GeometryFactory();
this._geomGraph = null;
this._disconnectedRingcoord = null;
const geomGraph = arguments[0];
this._geomGraph = geomGraph;
}
static findDifferentPoint(coord, pt) {
for (let i = 0; i < coord.length; i++) if (!coord[i].equals(pt)) return coord[i];
return null;
}
visitInteriorRing(ring, graph) {
if (ring.isEmpty()) return null;
const pts = ring.getCoordinates();
const pt0 = pts[0];
const pt1 = ConnectedInteriorTester.findDifferentPoint(pts, pt0);
const e = graph.findEdgeInSameDirection(pt0, pt1);
const de = graph.findEdgeEnd(e);
let intDe = null;
if (de.getLabel().getLocation(0, Position.RIGHT) === Location.INTERIOR) intDe = de;else if (de.getSym().getLabel().getLocation(0, Position.RIGHT) === Location.INTERIOR) intDe = de.getSym();
Assert.isTrue(intDe !== null, 'unable to find dirEdge with Interior on RHS');
this.visitLinkedDirectedEdges(intDe);
}
visitShellInteriors(g, graph) {
if (g instanceof Polygon) {
const p = g;
this.visitInteriorRing(p.getExteriorRing(), graph);
}
if (g instanceof MultiPolygon) {
const mp = g;
for (let i = 0; i < mp.getNumGeometries(); i++) {
const p = mp.getGeometryN(i);
this.visitInteriorRing(p.getExteriorRing(), graph);
}
}
}
getCoordinate() {
return this._disconnectedRingcoord;
}
setInteriorEdgesInResult(graph) {
for (let it = graph.getEdgeEnds().iterator(); it.hasNext();) {
const de = it.next();
if (de.getLabel().getLocation(0, Position.RIGHT) === Location.INTERIOR) de.setInResult(true);
}
}
visitLinkedDirectedEdges(start) {
const startDe = start;
let de = start;
do {
Assert.isTrue(de !== null, 'found null Directed Edge');
de.setVisited(true);
de = de.getNext();
} while (de !== startDe);
}
buildEdgeRings(dirEdges) {
const edgeRings = new ArrayList();
for (let it = dirEdges.iterator(); it.hasNext();) {
const de = it.next();
if (de.isInResult() && de.getEdgeRing() === null) {
const er = new MaximalEdgeRing(de, this._geometryFactory);
er.linkDirectedEdgesForMinimalEdgeRings();
const minEdgeRings = er.buildMinimalRings();
edgeRings.addAll(minEdgeRings);
}
}
return edgeRings;
}
hasUnvisitedShellEdge(edgeRings) {
for (let i = 0; i < edgeRings.size(); i++) {
const er = edgeRings.get(i);
if (er.isHole()) continue;
const edges = er.getEdges();
let de = edges.get(0);
if (de.getLabel().getLocation(0, Position.RIGHT) !== Location.INTERIOR) continue;
for (let j = 0; j < edges.size(); j++) {
de = edges.get(j);
if (!de.isVisited()) {
this._disconnectedRingcoord = de.getCoordinate();
return true;
}
}
}
return false;
}
isInteriorsConnected() {
const splitEdges = new ArrayList();
this._geomGraph.computeSplitEdges(splitEdges);
const graph = new PlanarGraph$1(new OverlayNodeFactory());
graph.addEdges(splitEdges);
this.setInteriorEdgesInResult(graph);
graph.linkResultDirectedEdges();
const edgeRings = this.buildEdgeRings(graph.getEdgeEnds());
this.visitShellInteriors(this._geomGraph.getGeometry(), graph);
return !this.hasUnvisitedShellEdge(edgeRings);
}
}
class EdgeEndBuilder {
createEdgeEndForNext(edge, l, eiCurr, eiNext) {
const iNext = eiCurr.segmentIndex + 1;
if (iNext >= edge.getNumPoints() && eiNext === null) return null;
let pNext = edge.getCoordinate(iNext);
if (eiNext !== null && eiNext.segmentIndex === eiCurr.segmentIndex) pNext = eiNext.coord;
const e = new EdgeEnd(edge, eiCurr.coord, pNext, new Label(edge.getLabel()));
l.add(e);
}
createEdgeEndForPrev(edge, l, eiCurr, eiPrev) {
let iPrev = eiCurr.segmentIndex;
if (eiCurr.dist === 0.0) {
if (iPrev === 0) return null;
iPrev--;
}
let pPrev = edge.getCoordinate(iPrev);
if (eiPrev !== null && eiPrev.segmentIndex >= iPrev) pPrev = eiPrev.coord;
const label = new Label(edge.getLabel());
label.flip();
const e = new EdgeEnd(edge, eiCurr.coord, pPrev, label);
l.add(e);
}
computeEdgeEnds() {
if (arguments.length === 1) {
const edges = arguments[0];
const l = new ArrayList();
for (let i = edges; i.hasNext();) {
const e = i.next();
this.computeEdgeEnds(e, l);
}
return l;
} else if (arguments.length === 2) {
const edge = arguments[0],
l = arguments[1];
const eiList = edge.getEdgeIntersectionList();
eiList.addEndpoints();
const it = eiList.iterator();
let eiPrev = null;
let eiCurr = null;
if (!it.hasNext()) return null;
let eiNext = it.next();
do {
eiPrev = eiCurr;
eiCurr = eiNext;
eiNext = null;
if (it.hasNext()) eiNext = it.next();
if (eiCurr !== null) {
this.createEdgeEndForPrev(edge, l, eiCurr, eiPrev);
this.createEdgeEndForNext(edge, l, eiCurr, eiNext);
}
} while (eiCurr !== null);
}
}
}
class EdgeEndBundle extends EdgeEnd {
constructor() {
super();
EdgeEndBundle.constructor_.apply(this, arguments);
}
static constructor_() {
this._edgeEnds = new ArrayList();
if (arguments.length === 1) {
const e = arguments[0];
EdgeEndBundle.constructor_.call(this, null, e);
} else if (arguments.length === 2) {
const e = arguments[1];
EdgeEnd.constructor_.call(this, e.getEdge(), e.getCoordinate(), e.getDirectedCoordinate(), new Label(e.getLabel()));
this.insert(e);
}
}
insert(e) {
this._edgeEnds.add(e);
}
print(out) {
out.println('EdgeEndBundle--> Label: ' + this._label);
for (let it = this.iterator(); it.hasNext();) {
const ee = it.next();
ee.print(out);
out.println();
}
}
iterator() {
return this._edgeEnds.iterator();
}
getEdgeEnds() {
return this._edgeEnds;
}
computeLabelOn(geomIndex, boundaryNodeRule) {
let boundaryCount = 0;
let foundInterior = false;
for (let it = this.iterator(); it.hasNext();) {
const e = it.next();
const loc = e.getLabel().getLocation(geomIndex);
if (loc === Location.BOUNDARY) boundaryCount++;
if (loc === Location.INTERIOR) foundInterior = true;
}
let loc = Location.NONE;
if (foundInterior) loc = Location.INTERIOR;
if (boundaryCount > 0) loc = GeometryGraph.determineBoundary(boundaryNodeRule, boundaryCount);
this._label.setLocation(geomIndex, loc);
}
computeLabelSide(geomIndex, side) {
for (let it = this.iterator(); it.hasNext();) {
const e = it.next();
if (e.getLabel().isArea()) {
const loc = e.getLabel().getLocation(geomIndex, side);
if (loc === Location.INTERIOR) {
this._label.setLocation(geomIndex, side, Location.INTERIOR);
return null;
} else if (loc === Location.EXTERIOR) {
this._label.setLocation(geomIndex, side, Location.EXTERIOR);
}
}
}
}
getLabel() {
return this._label;
}
computeLabelSides(geomIndex) {
this.computeLabelSide(geomIndex, Position.LEFT);
this.computeLabelSide(geomIndex, Position.RIGHT);
}
updateIM(im) {
Edge$1.updateIM(this._label, im);
}
computeLabel(boundaryNodeRule) {
let isArea = false;
for (let it = this.iterator(); it.hasNext();) {
const e = it.next();
if (e.getLabel().isArea()) isArea = true;
}
if (isArea) this._label = new Label(Location.NONE, Location.NONE, Location.NONE);else this._label = new Label(Location.NONE);
for (let i = 0; i < 2; i++) {
this.computeLabelOn(i, boundaryNodeRule);
if (isArea) this.computeLabelSides(i);
}
}
}
class EdgeEndBundleStar extends EdgeEndStar {
constructor() {
super();
}
updateIM(im) {
for (let it = this.iterator(); it.hasNext();) {
const esb = it.next();
esb.updateIM(im);
}
}
insert(e) {
let eb = this._edgeMap.get(e);
if (eb === null) {
eb = new EdgeEndBundle(e);
this.insertEdgeEnd(e, eb);
} else {
eb.insert(e);
}
}
}
class RelateNode extends Node$2 {
constructor() {
super();
RelateNode.constructor_.apply(this, arguments);
}
static constructor_() {
const coord = arguments[0],
edges = arguments[1];
Node$2.constructor_.call(this, coord, edges);
}
updateIMFromEdges(im) {
this._edges.updateIM(im);
}
computeIM(im) {
im.setAtLeastIfValid(this._label.getLocation(0), this._label.getLocation(1), 0);
}
}
class RelateNodeFactory extends NodeFactory {
constructor() {
super();
}
createNode(coord) {
return new RelateNode(coord, new EdgeEndBundleStar());
}
}
class RelateNodeGraph {
constructor() {
RelateNodeGraph.constructor_.apply(this, arguments);
}
static constructor_() {
this._nodes = new NodeMap$1(new RelateNodeFactory());
}
insertEdgeEnds(ee) {
for (let i = ee.iterator(); i.hasNext();) {
const e = i.next();
this._nodes.add(e);
}
}
getNodeIterator() {
return this._nodes.iterator();
}
copyNodesAndLabels(geomGraph, argIndex) {
for (let nodeIt = geomGraph.getNodeIterator(); nodeIt.hasNext();) {
const graphNode = nodeIt.next();
const newNode = this._nodes.addNode(graphNode.getCoordinate());
newNode.setLabel(argIndex, graphNode.getLabel().getLocation(argIndex));
}
}
build(geomGraph) {
this.computeIntersectionNodes(geomGraph, 0);
this.copyNodesAndLabels(geomGraph, 0);
const eeBuilder = new EdgeEndBuilder();
const eeList = eeBuilder.computeEdgeEnds(geomGraph.getEdgeIterator());
this.insertEdgeEnds(eeList);
}
computeIntersectionNodes(geomGraph, argIndex) {
for (let edgeIt = geomGraph.getEdgeIterator(); edgeIt.hasNext();) {
const e = edgeIt.next();
const eLoc = e.getLabel().getLocation(argIndex);
for (let eiIt = e.getEdgeIntersectionList().iterator(); eiIt.hasNext();) {
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);
}
}
}
}
class ConsistentAreaTester {
constructor() {
ConsistentAreaTester.constructor_.apply(this, arguments);
}
static constructor_() {
this._li = new RobustLineIntersector();
this._geomGraph = null;
this._nodeGraph = new RelateNodeGraph();
this._invalidPoint = null;
const geomGraph = arguments[0];
this._geomGraph = geomGraph;
}
isNodeEdgeAreaLabelsConsistent() {
for (let nodeIt = this._nodeGraph.getNodeIterator(); nodeIt.hasNext();) {
const node = nodeIt.next();
if (!node.getEdges().isAreaLabelsConsistent(this._geomGraph)) {
this._invalidPoint = node.getCoordinate().copy();
return false;
}
}
return true;
}
getInvalidPoint() {
return this._invalidPoint;
}
hasDuplicateRings() {
for (let nodeIt = this._nodeGraph.getNodeIterator(); nodeIt.hasNext();) {
const node = nodeIt.next();
for (let i = node.getEdges().iterator(); i.hasNext();) {
const eeb = i.next();
if (eeb.getEdgeEnds().size() > 1) {
this._invalidPoint = eeb.getEdge().getCoordinate(0);
return true;
}
}
}
return false;
}
isNodeConsistentArea() {
const intersector = this._geomGraph.computeSelfNodes(this._li, true, true);
if (intersector.hasProperIntersection()) {
this._invalidPoint = intersector.getProperIntersectionPoint();
return false;
}
this._nodeGraph.build(this._geomGraph);
return this.isNodeEdgeAreaLabelsConsistent();
}
}
class IndexedNestedRingTester {
constructor() {
IndexedNestedRingTester.constructor_.apply(this, arguments);
}
static constructor_() {
this._graph = null;
this._rings = new ArrayList();
this._totalEnv = new Envelope();
this._index = null;
this._nestedPt = null;
const graph = arguments[0];
this._graph = graph;
}
buildIndex() {
this._index = new STRtree();
for (let i = 0; i < this._rings.size(); i++) {
const ring = this._rings.get(i);
const env = ring.getEnvelopeInternal();
this._index.insert(env, ring);
}
}
getNestedPoint() {
return this._nestedPt;
}
isNonNested() {
this.buildIndex();
for (let i = 0; i < this._rings.size(); i++) {
const innerRing = this._rings.get(i);
const innerRingPts = innerRing.getCoordinates();
const results = this._index.query(innerRing.getEnvelopeInternal());
for (let j = 0; j < results.size(); j++) {
const searchRing = results.get(j);
const searchRingPts = searchRing.getCoordinates();
if (innerRing === searchRing) continue;
if (!innerRing.getEnvelopeInternal().intersects(searchRing.getEnvelopeInternal())) continue;
const innerRingPt = IsValidOp.findPtNotNode(innerRingPts, searchRing, this._graph);
if (innerRingPt === null) continue;
const isInside = PointLocation.isInRing(innerRingPt, searchRingPts);
if (isInside) {
this._nestedPt = innerRingPt;
return false;
}
}
}
return true;
}
add(ring) {
this._rings.add(ring);
this._totalEnv.expandToInclude(ring.getEnvelopeInternal());
}
}
class TopologyValidationError {
constructor() {
TopologyValidationError.constructor_.apply(this, arguments);
}
static constructor_() {
this._errorType = null;
this._pt = null;
if (arguments.length === 1) {
const errorType = arguments[0];
TopologyValidationError.constructor_.call(this, errorType, null);
} else if (arguments.length === 2) {
const errorType = arguments[0],
pt = arguments[1];
this._errorType = errorType;
if (pt !== null) this._pt = pt.copy();
}
}
getErrorType() {
return this._errorType;
}
getMessage() {
return TopologyValidationError.errMsg[this._errorType];
}
getCoordinate() {
return this._pt;
}
toString() {
let locStr = '';
if (this._pt !== null) locStr = ' at or near point ' + this._pt;
return this.getMessage() + locStr;
}
}
TopologyValidationError.ERROR = 0;
TopologyValidationError.REPEATED_POINT = 1;
TopologyValidationError.HOLE_OUTSIDE_SHELL = 2;
TopologyValidationError.NESTED_HOLES = 3;
TopologyValidationError.DISCONNECTED_INTERIOR = 4;
TopologyValidationError.SELF_INTERSECTION = 5;
TopologyValidationError.RING_SELF_INTERSECTION = 6;
TopologyValidationError.NESTED_SHELLS = 7;
TopologyValidationError.DUPLICATE_RINGS = 8;
TopologyValidationError.TOO_FEW_POINTS = 9;
TopologyValidationError.INVALID_COORDINATE = 10;
TopologyValidationError.RING_NOT_CLOSED = 11;
TopologyValidationError.errMsg = ['Topology Validation Error', 'Repeated Point', 'Hole lies outside shell', 'Holes are nested', 'Interior is disconnected', 'Self-intersection', 'Ring Self-intersection', 'Nested shells', 'Duplicate Rings', 'Too few distinct points in geometry component', 'Invalid Coordinate', 'Ring is not closed'];
class IsValidOp {
constructor() {
IsValidOp.constructor_.apply(this, arguments);
}
static constructor_() {
this._parentGeometry = null;
this._isSelfTouchingRingFormingHoleValid = false;
this._validErr = null;
const parentGeometry = arguments[0];
this._parentGeometry = parentGeometry;
}
static findPtNotNode(testCoords, searchRing, graph) {
const searchEdge = graph.findEdge(searchRing);
const eiList = searchEdge.getEdgeIntersectionList();
for (let i = 0; i < testCoords.length; i++) {
const pt = testCoords[i];
if (!eiList.isIntersection(pt)) return pt;
}
return null;
}
static isValid() {
if (arguments[0] instanceof Geometry) {
const geom = arguments[0];
const isValidOp = new IsValidOp(geom);
return isValidOp.isValid();
} else if (arguments[0] instanceof Coordinate) {
const coord = arguments[0];
if (Double.isNaN(coord.x)) return false;
if (Double.isInfinite(coord.x)) return false;
if (Double.isNaN(coord.y)) return false;
if (Double.isInfinite(coord.y)) return false;
return true;
}
}
checkInvalidCoordinates() {
if (arguments[0] instanceof Array) {
const coords = arguments[0];
for (let i = 0; i < coords.length; i++) if (!IsValidOp.isValid(coords[i])) {
this._validErr = new TopologyValidationError(TopologyValidationError.INVALID_COORDINATE, coords[i]);
return null;
}
} else if (arguments[0] instanceof Polygon) {
const poly = arguments[0];
this.checkInvalidCoordinates(poly.getExteriorRing().getCoordinates());
if (this._validErr !== null) return null;
for (let i = 0; i < poly.getNumInteriorRing(); i++) {
this.checkInvalidCoordinates(poly.getInteriorRingN(i).getCoordinates());
if (this._validErr !== null) return null;
}
}
}
checkHolesNotNested(p, graph) {
if (p.getNumInteriorRing() <= 0) return null;
const nestedTester = new IndexedNestedRingTester(graph);
for (let i = 0; i < p.getNumInteriorRing(); i++) {
const innerHole = p.getInteriorRingN(i);
if (innerHole.isEmpty()) continue;
nestedTester.add(innerHole);
}
const isNonNested = nestedTester.isNonNested();
if (!isNonNested) this._validErr = new TopologyValidationError(TopologyValidationError.NESTED_HOLES, nestedTester.getNestedPoint());
}
checkConsistentArea(graph) {
const cat = new ConsistentAreaTester(graph);
const isValidArea = cat.isNodeConsistentArea();
if (!isValidArea) {
this._validErr = new TopologyValidationError(TopologyValidationError.SELF_INTERSECTION, cat.getInvalidPoint());
return null;
}
if (cat.hasDuplicateRings()) this._validErr = new TopologyValidationError(TopologyValidationError.DUPLICATE_RINGS, cat.getInvalidPoint());
}
isValid() {
this.checkValid(this._parentGeometry);
return this._validErr === null;
}
checkShellInsideHole(shell, hole, graph) {
const shellPts = shell.getCoordinates();
const holePts = hole.getCoordinates();
const shellPt = IsValidOp.findPtNotNode(shellPts, hole, graph);
if (shellPt !== null) {
const insideHole = PointLocation.isInRing(shellPt, holePts);
if (!insideHole) return shellPt;
}
const holePt = IsValidOp.findPtNotNode(holePts, shell, graph);
if (holePt !== null) {
const insideShell = PointLocation.isInRing(holePt, shellPts);
if (insideShell) return holePt;
return null;
}
Assert.shouldNeverReachHere('points in shell and hole appear to be equal');
return null;
}
checkNoSelfIntersectingRings(graph) {
for (let i = graph.getEdgeIterator(); i.hasNext();) {
const e = i.next();
this.checkNoSelfIntersectingRing(e.getEdgeIntersectionList());
if (this._validErr !== null) return null;
}
}
checkConnectedInteriors(graph) {
const cit = new ConnectedInteriorTester(graph);
if (!cit.isInteriorsConnected()) this._validErr = new TopologyValidationError(TopologyValidationError.DISCONNECTED_INTERIOR, cit.getCoordinate());
}
checkNoSelfIntersectingRing(eiList) {
const nodeSet = new TreeSet();
let isFirst = true;
for (let i = eiList.iterator(); i.hasNext();) {
const ei = i.next();
if (isFirst) {
isFirst = false;
continue;
}
if (nodeSet.contains(ei.coord)) {
this._validErr = new TopologyValidationError(TopologyValidationError.RING_SELF_INTERSECTION, ei.coord);
return null;
} else {
nodeSet.add(ei.coord);
}
}
}
checkHolesInShell(p, graph) {
if (p.getNumInteriorRing() <= 0) return null;
const shell = p.getExteriorRing();
const isShellEmpty = shell.isEmpty();
const pir = new IndexedPointInAreaLocator(shell);
for (let i = 0; i < p.getNumInteriorRing(); i++) {
const hole = p.getInteriorRingN(i);
let holePt = null;
if (hole.isEmpty()) continue;
holePt = IsValidOp.findPtNotNode(hole.getCoordinates(), shell, graph);
if (holePt === null) return null;
const outside = isShellEmpty || Location.EXTERIOR === pir.locate(holePt);
if (outside) {
this._validErr = new TopologyValidationError(TopologyValidationError.HOLE_OUTSIDE_SHELL, holePt);
return null;
}
}
}
checkTooFewPoints(graph) {
if (graph.hasTooFewPoints()) {
this._validErr = new TopologyValidationError(TopologyValidationError.TOO_FEW_POINTS, graph.getInvalidPoint());
return null;
}
}
getValidationError() {
this.checkValid(this._parentGeometry);
return this._validErr;
}
checkValid() {
if (arguments[0] instanceof Point) {
const g = arguments[0];
this.checkInvalidCoordinates(g.getCoordinates());
} else if (arguments[0] instanceof MultiPoint) {
const g = arguments[0];
this.checkInvalidCoordinates(g.getCoordinates());
} else if (arguments[0] instanceof LinearRing) {
const g = arguments[0];
this.checkInvalidCoordinates(g.getCoordinates());
if (this._validErr !== null) return null;
this.checkClosedRing(g);
if (this._validErr !== null) return null;
const graph = new GeometryGraph(0, g);
this.checkTooFewPoints(graph);
if (this._validErr !== null) return null;
const li = new RobustLineIntersector();
graph.computeSelfNodes(li, true, true);
this.checkNoSelfIntersectingRings(graph);
} else if (arguments[0] instanceof LineString) {
const g = arguments[0];
this.checkInvalidCoordinates(g.getCoordinates());
if (this._validErr !== null) return null;
const graph = new GeometryGraph(0, g);
this.checkTooFewPoints(graph);
} else if (arguments[0] instanceof Polygon) {
const g = arguments[0];
this.checkInvalidCoordinates(g);
if (this._validErr !== null) return null;
this.checkClosedRings(g);
if (this._validErr !== null) return null;
const graph = new GeometryGraph(0, g);
this.checkTooFewPoints(graph);
if (this._validErr !== null) return null;
this.checkConsistentArea(graph);
if (this._validErr !== null) return null;
if (!this._isSelfTouchingRingFormingHoleValid) {
this.checkNoSelfIntersectingRings(graph);
if (this._validErr !== null) return null;
}
this.checkHolesInShell(g, graph);
if (this._validErr !== null) return null;
this.checkHolesNotNested(g, graph);
if (this._validErr !== null) return null;
this.checkConnectedInteriors(graph);
} else if (arguments[0] instanceof MultiPolygon) {
const g = arguments[0];
for (let i = 0; i < g.getNumGeometries(); i++) {
const p = g.getGeometryN(i);
this.checkInvalidCoordinates(p);
if (this._validErr !== null) return null;
this.checkClosedRings(p);
if (this._validErr !== null) return null;
}
const graph = new GeometryGraph(0, g);
this.checkTooFewPoints(graph);
if (this._validErr !== null) return null;
this.checkConsistentArea(graph);
if (this._validErr !== null) return null;
if (!this._isSelfTouchingRingFormingHoleValid) {
this.checkNoSelfIntersectingRings(graph);
if (this._validErr !== null) return null;
}
for (let i = 0; i < g.getNumGeometries(); i++) {
const p = g.getGeometryN(i);
this.checkHolesInShell(p, graph);
if (this._validErr !== null) return null;
}
for (let i = 0; i < g.getNumGeometries(); i++) {
const p = g.getGeometryN(i);
this.checkHolesNotNested(p, graph);
if (this._validErr !== null) return null;
}
this.checkShellsNotNested(g, graph);
if (this._validErr !== null) return null;
this.checkConnectedInteriors(graph);
} else if (arguments[0] instanceof GeometryCollection) {
const gc = arguments[0];
for (let i = 0; i < gc.getNumGeometries(); i++) {
const g = gc.getGeometryN(i);
this.checkValid(g);
if (this._validErr !== null) return null;
}
} else if (arguments[0] instanceof Geometry) {
const g = arguments[0];
this._validErr = null;
if (g.isEmpty()) return null;
if (g instanceof Point) this.checkValid(g);else if (g instanceof MultiPoint) this.checkValid(g);else if (g instanceof LinearRing) this.checkValid(g);else if (g instanceof LineString) this.checkValid(g);else if (g instanceof Polygon) this.checkValid(g);else if (g instanceof MultiPolygon) this.checkValid(g);else if (g instanceof GeometryCollection) this.checkValid(g);else throw new UnsupportedOperationException(g.getGeometryType());
}
}
setSelfTouchingRingFormingHoleValid(isValid) {
this._isSelfTouchingRingFormingHoleValid = isValid;
}
checkShellNotNested(shell, p, graph) {
const shellPts = shell.getCoordinates();
const polyShell = p.getExteriorRing();
if (polyShell.isEmpty()) return null;
const polyPts = polyShell.getCoordinates();
const shellPt = IsValidOp.findPtNotNode(shellPts, polyShell, graph);
if (shellPt === null) return null;
const insidePolyShell = PointLocation.isInRing(shellPt, polyPts);
if (!insidePolyShell) return null;
if (p.getNumInteriorRing() <= 0) {
this._validErr = new TopologyValidationError(TopologyValidationError.NESTED_SHELLS, shellPt);
return null;
}
let badNestedPt = null;
for (let i = 0; i < p.getNumInteriorRing(); i++) {
const hole = p.getInteriorRingN(i);
badNestedPt = this.checkShellInsideHole(shell, hole, graph);
if (badNestedPt === null) return null;
}
this._validErr = new TopologyValidationError(TopologyValidationError.NESTED_SHELLS, badNestedPt);
}
checkClosedRings(poly) {
this.checkClosedRing(poly.getExteriorRing());
if (this._validErr !== null) return null;
for (let i = 0; i < poly.getNumInteriorRing(); i++) {
this.checkClosedRing(poly.getInteriorRingN(i));
if (this._validErr !== null) return null;
}
}
checkClosedRing(ring) {
if (ring.isEmpty()) return null;
if (!ring.isClosed()) {
let pt = null;
if (ring.getNumPoints() >= 1) pt = ring.getCoordinateN(0);
this._validErr = new TopologyValidationError(TopologyValidationError.RING_NOT_CLOSED, pt);
}
}
checkShellsNotNested(mp, graph) {
for (let i = 0; i < mp.getNumGeometries(); i++) {
const p = mp.getGeometryN(i);
const shell = p.getExteriorRing();
for (let j = 0; j < mp.getNumGeometries(); j++) {
if (i === j) continue;
const p2 = mp.getGeometryN(j);
this.checkShellNotNested(shell, p2, graph);
if (this._validErr !== null) return null;
}
}
}
}
class EdgeRing {
constructor() {
EdgeRing.constructor_.apply(this, arguments);
}
static constructor_() {
this._factory = null;
this._deList = new ArrayList();
this._lowestEdge = null;
this._ring = null;
this._locator = null;
this._ringPts = null;
this._holes = null;
this._shell = null;
this._isHole = null;
this._isProcessed = false;
this._isIncludedSet = false;
this._isIncluded = false;
const factory = arguments[0];
this._factory = factory;
}
static findDirEdgesInRing(startDE) {
let de = startDE;
const edges = new ArrayList();
do {
edges.add(de);
de = de.getNext();
Assert.isTrue(de !== null, 'found null DE in ring');
Assert.isTrue(de === startDE || !de.isInRing(), 'found DE already in ring');
} while (de !== startDE);
return edges;
}
static addEdge(coords, isForward, coordList) {
if (isForward) for (let i = 0; i < coords.length; i++) coordList.add(coords[i], false);else for (let i = coords.length - 1; i >= 0; i--) coordList.add(coords[i], false);
}
static findEdgeRingContaining(testEr, erList) {
const testRing = testEr.getRing();
const testEnv = testRing.getEnvelopeInternal();
let testPt = testRing.getCoordinateN(0);
let minRing = null;
let minRingEnv = null;
for (let it = erList.iterator(); it.hasNext();) {
const tryEdgeRing = it.next();
const tryRing = tryEdgeRing.getRing();
const tryShellEnv = tryRing.getEnvelopeInternal();
if (tryShellEnv.equals(testEnv)) continue;
if (!tryShellEnv.contains(testEnv)) continue;
testPt = CoordinateArrays.ptNotInList(testRing.getCoordinates(), tryEdgeRing.getCoordinates());
const isContained = tryEdgeRing.isInRing(testPt);
if (isContained) if (minRing === null || minRingEnv.contains(tryShellEnv)) {
minRing = tryEdgeRing;
minRingEnv = minRing.getRing().getEnvelopeInternal();
}
}
return minRing;
}
isIncluded() {
return this._isIncluded;
}
getCoordinates() {
if (this._ringPts === null) {
const coordList = new CoordinateList();
for (let i = this._deList.iterator(); i.hasNext();) {
const de = i.next();
const edge = de.getEdge();
EdgeRing.addEdge(edge.getLine().getCoordinates(), de.getEdgeDirection(), coordList);
}
this._ringPts = coordList.toCoordinateArray();
}
return this._ringPts;
}
isIncludedSet() {
return this._isIncludedSet;
}
isValid() {
this.getCoordinates();
if (this._ringPts.length <= 3) return false;
this.getRing();
return IsValidOp.isValid(this._ring);
}
build(startDE) {
let de = startDE;
do {
this.add(de);
de.setRing(this);
de = de.getNext();
Assert.isTrue(de !== null, 'found null DE in ring');
Assert.isTrue(de === startDE || !de.isInRing(), 'found DE already in ring');
} while (de !== startDE);
}
isInRing(pt) {
return Location.EXTERIOR !== this.getLocator().locate(pt);
}
isOuterHole() {
if (!this._isHole) return false;
return !this.hasShell();
}
getPolygon() {
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);
}
const poly = this._factory.createPolygon(this._ring, holeLR);
return poly;
}
isHole() {
return this._isHole;
}
isProcessed() {
return this._isProcessed;
}
addHole() {
if (arguments[0] instanceof LinearRing) {
const hole = arguments[0];
if (this._holes === null) this._holes = new ArrayList();
this._holes.add(hole);
} else if (arguments[0] instanceof EdgeRing) {
const holeER = arguments[0];
holeER.setShell(this);
const hole = holeER.getRing();
if (this._holes === null) this._holes = new ArrayList();
this._holes.add(hole);
}
}
setIncluded(isIncluded) {
this._isIncluded = isIncluded;
this._isIncludedSet = true;
}
getOuterHole() {
if (this.isHole()) return null;
for (let i = 0; i < this._deList.size(); i++) {
const de = this._deList.get(i);
const adjRing = de.getSym().getRing();
if (adjRing.isOuterHole()) return adjRing;
}
return null;
}
computeHole() {
const ring = this.getRing();
this._isHole = Orientation.isCCW(ring.getCoordinates());
}
hasShell() {
return this._shell !== null;
}
isOuterShell() {
return this.getOuterHole() !== null;
}
getLineString() {
this.getCoordinates();
return this._factory.createLineString(this._ringPts);
}
toString() {
return WKTWriter.toLineString(new CoordinateArraySequence(this.getCoordinates()));
}
getLocator() {
if (this._locator === null) this._locator = new IndexedPointInAreaLocator(this.getRing());
return this._locator;
}
getShell() {
if (this.isHole()) return this._shell;
return this;
}
add(de) {
this._deList.add(de);
}
getRing() {
if (this._ring !== null) return this._ring;
this.getCoordinates();
if (this._ringPts.length < 3) System.out.println(this._ringPts);
try {
this._ring = this._factory.createLinearRing(this._ringPts);
} catch (ex) {
if (ex instanceof Exception) System.out.println(this._ringPts);else throw ex;
} finally {}
return this._ring;
}
updateIncluded() {
if (this.isHole()) return null;
for (let i = 0; i < this._deList.size(); i++) {
const de = this._deList.get(i);
const adjShell = de.getSym().getRing().getShell();
if (adjShell !== null && adjShell.isIncludedSet()) {
this.setIncluded(!adjShell.isIncluded());
return null;
}
}
}
setShell(shell) {
this._shell = shell;
}
setProcessed(isProcessed) {
this._isProcessed = isProcessed;
}
}
class EnvelopeComparator {
compare(obj0, obj1) {
const r0 = obj0;
const r1 = obj1;
return r0.getRing().getEnvelope().compareTo(r1.getRing().getEnvelope());
}
get interfaces_() {
return [Comparator];
}
}
EdgeRing.EnvelopeComparator = EnvelopeComparator;
class PolygonizeGraph extends PlanarGraph {
constructor() {
super();
PolygonizeGraph.constructor_.apply(this, arguments);
}
static constructor_() {
this._factory = null;
const factory = arguments[0];
this._factory = factory;
}
static findLabeledEdgeRings(dirEdges) {
const edgeRingStarts = new ArrayList();
let currLabel = 1;
for (let i = dirEdges.iterator(); i.hasNext();) {
const de = i.next();
if (de.isMarked()) continue;
if (de.getLabel() >= 0) continue;
edgeRingStarts.add(de);
const edges = EdgeRing.findDirEdgesInRing(de);
PolygonizeGraph.label(edges, currLabel);
currLabel++;
}
return edgeRingStarts;
}
static getDegreeNonDeleted(node) {
const edges = node.getOutEdges().getEdges();
let degree = 0;
for (let i = edges.iterator(); i.hasNext();) {
const de = i.next();
if (!de.isMarked()) degree++;
}
return degree;
}
static deleteAllEdges(node) {
const edges = node.getOutEdges().getEdges();
for (let i = edges.iterator(); i.hasNext();) {
const de = i.next();
de.setMarked(true);
const sym = de.getSym();
if (sym !== null) sym.setMarked(true);
}
}
static label(dirEdges, label) {
for (let i = dirEdges.iterator(); i.hasNext();) {
const de = i.next();
de.setLabel(label);
}
}
static computeNextCWEdges(node) {
const deStar = node.getOutEdges();
let startDE = null;
let prevDE = null;
for (let i = deStar.getEdges().iterator(); i.hasNext();) {
const outDE = i.next();
if (outDE.isMarked()) continue;
if (startDE === null) startDE = outDE;
if (prevDE !== null) {
const sym = prevDE.getSym();
sym.setNext(outDE);
}
prevDE = outDE;
}
if (prevDE !== null) {
const sym = prevDE.getSym();
sym.setNext(startDE);
}
}
static computeNextCCWEdges(node, label) {
const deStar = node.getOutEdges();
let firstOutDE = null;
let prevInDE = null;
const edges = deStar.getEdges();
for (let i = edges.size() - 1; i >= 0; i--) {
const de = edges.get(i);
const sym = de.getSym();
let outDE = null;
if (de.getLabel() === label) outDE = de;
let inDE = null;
if (sym.getLabel() === label) inDE = sym;
if (outDE === null && inDE === null) continue;
if (inDE !== null) prevInDE = inDE;
if (outDE !== null) {
if (prevInDE !== null) {
prevInDE.setNext(outDE);
prevInDE = null;
}
if (firstOutDE === null) firstOutDE = outDE;
}
}
if (prevInDE !== null) {
Assert.isTrue(firstOutDE !== null);
prevInDE.setNext(firstOutDE);
}
}
static getDegree(node, label) {
const edges = node.getOutEdges().getEdges();
let degree = 0;
for (let i = edges.iterator(); i.hasNext();) {
const de = i.next();
if (de.getLabel() === label) degree++;
}
return degree;
}
static findIntersectionNodes(startDE, label) {
let de = startDE;
let intNodes = null;
do {
const node = de.getFromNode();
if (PolygonizeGraph.getDegree(node, label) > 1) {
if (intNodes === null) intNodes = new ArrayList();
intNodes.add(node);
}
de = de.getNext();
Assert.isTrue(de !== null, 'found null DE in ring');
Assert.isTrue(de === startDE || !de.isInRing(), 'found DE already in ring');
} while (de !== startDE);
return intNodes;
}
findEdgeRing(startDE) {
const er = new EdgeRing(this._factory);
er.build(startDE);
return er;
}
computeDepthParity() {
if (arguments.length === 0) {
while (true) {
return null;
}
}
}
computeNextCWEdges() {
for (let iNode = this.nodeIterator(); iNode.hasNext();) {
const node = iNode.next();
PolygonizeGraph.computeNextCWEdges(node);
}
}
addEdge(line) {
if (line.isEmpty()) return null;
const linePts = CoordinateArrays.removeRepeatedPoints(line.getCoordinates());
if (linePts.length < 2) return null;
const startPt = linePts[0];
const endPt = linePts[linePts.length - 1];
const nStart = this.getNode(startPt);
const nEnd = this.getNode(endPt);
const de0 = new PolygonizeDirectedEdge(nStart, nEnd, linePts[1], true);
const de1 = new PolygonizeDirectedEdge(nEnd, nStart, linePts[linePts.length - 2], false);
const edge = new PolygonizeEdge(line);
edge.setDirectedEdges(de0, de1);
this.add(edge);
}
deleteCutEdges() {
this.computeNextCWEdges();
PolygonizeGraph.findLabeledEdgeRings(this._dirEdges);
const cutLines = new ArrayList();
for (let i = this._dirEdges.iterator(); i.hasNext();) {
const de = i.next();
if (de.isMarked()) continue;
const sym = de.getSym();
if (de.getLabel() === sym.getLabel()) {
de.setMarked(true);
sym.setMarked(true);
const e = de.getEdge();
cutLines.add(e.getLine());
}
}
return cutLines;
}
getEdgeRings() {
this.computeNextCWEdges();
PolygonizeGraph.label(this._dirEdges, -1);
const maximalRings = PolygonizeGraph.findLabeledEdgeRings(this._dirEdges);
this.convertMaximalToMinimalEdgeRings(maximalRings);
const edgeRingList = new ArrayList();
for (let i = this._dirEdges.iterator(); i.hasNext();) {
const de = i.next();
if (de.isMarked()) continue;
if (de.isInRing()) continue;
const er = this.findEdgeRing(de);
edgeRingList.add(er);
}
return edgeRingList;
}
getNode(pt) {
let node = this.findNode(pt);
if (node === null) {
node = new Node(pt);
this.add(node);
}
return node;
}
convertMaximalToMinimalEdgeRings(ringEdges) {
for (let i = ringEdges.iterator(); i.hasNext();) {
const de = i.next();
const label = de.getLabel();
const intNodes = PolygonizeGraph.findIntersectionNodes(de, label);
if (intNodes === null) continue;
for (let iNode = intNodes.iterator(); iNode.hasNext();) {
const node = iNode.next();
PolygonizeGraph.computeNextCCWEdges(node, label);
}
}
}
deleteDangles() {
const nodesToRemove = this.findNodesOfDegree(1);
const dangleLines = new HashSet();
const nodeStack = new Stack();
for (let i = nodesToRemove.iterator(); i.hasNext();) nodeStack.push(i.next());
while (!nodeStack.isEmpty()) {
const node = nodeStack.pop();
PolygonizeGraph.deleteAllEdges(node);
const nodeOutEdges = node.getOutEdges().getEdges();
for (let i = nodeOutEdges.iterator(); i.hasNext();) {
const de = i.next();
de.setMarked(true);
const sym = de.getSym();
if (sym !== null) sym.setMarked(true);
const e = de.getEdge();
dangleLines.add(e.getLine());
const toNode = de.getToNode();
if (PolygonizeGraph.getDegreeNonDeleted(toNode) === 1) nodeStack.push(toNode);
}
}
return dangleLines;
}
}
class HoleAssigner {
constructor() {
HoleAssigner.constructor_.apply(this, arguments);
}
static constructor_() {
this._shells = null;
this._shellIndex = null;
const shells = arguments[0];
this._shells = shells;
this.buildIndex();
}
static assignHolesToShells(holes, shells) {
const assigner = new HoleAssigner(shells);
assigner.assignHolesToShells(holes);
}
assignHolesToShells(holeList) {
for (let i = holeList.iterator(); i.hasNext();) {
const holeER = i.next();
this.assignHoleToShell(holeER);
}
}
buildIndex() {
this._shellIndex = new STRtree();
for (const shell of this._shells) this._shellIndex.insert(shell.getRing().getEnvelopeInternal(), shell);
}
queryOverlappingShells(ringEnv) {
return this._shellIndex.query(ringEnv);
}
findShellContaining(testEr) {
const testEnv = testEr.getRing().getEnvelopeInternal();
const candidateShells = this.queryOverlappingShells(testEnv);
return EdgeRing.findEdgeRingContaining(testEr, candidateShells);
}
assignHoleToShell(holeER) {
const shell = this.findShellContaining(holeER);
if (shell !== null) shell.addHole(holeER);
}
}
class Polygonizer {
constructor() {
Polygonizer.constructor_.apply(this, arguments);
}
static constructor_() {
this._lineStringAdder = new LineStringAdder(this);
this._graph = null;
this._dangles = new ArrayList();
this._cutEdges = new ArrayList();
this._invalidRingLines = new ArrayList();
this._holeList = null;
this._shellList = null;
this._polyList = null;
this._isCheckingRingsValid = true;
this._extractOnlyPolygonal = null;
this._geomFactory = null;
if (arguments.length === 0) {
Polygonizer.constructor_.call(this, false);
} else if (arguments.length === 1) {
const extractOnlyPolygonal = arguments[0];
this._extractOnlyPolygonal = extractOnlyPolygonal;
}
}
static extractPolygons(shellList, includeAll) {
const polyList = new ArrayList();
for (let i = shellList.iterator(); i.hasNext();) {
const er = i.next();
if (includeAll || er.isIncluded()) polyList.add(er.getPolygon());
}
return polyList;
}
static findOuterShells(shellList) {
for (let i = shellList.iterator(); i.hasNext();) {
const er = i.next();
const outerHoleER = er.getOuterHole();
if (outerHoleER !== null && !outerHoleER.isProcessed()) {
er.setIncluded(true);
outerHoleER.setProcessed(true);
}
}
}
static findDisjointShells(shellList) {
Polygonizer.findOuterShells(shellList);
let isMoreToScan = null;
do {
isMoreToScan = false;
for (let i = shellList.iterator(); i.hasNext();) {
const er = i.next();
if (er.isIncludedSet()) continue;
er.updateIncluded();
if (!er.isIncludedSet()) isMoreToScan = true;
}
} while (isMoreToScan);
}
getGeometry() {
if (this._geomFactory === null) this._geomFactory = new GeometryFactory();
this.polygonize();
if (this._extractOnlyPolygonal) return this._geomFactory.buildGeometry(this._polyList);
return this._geomFactory.createGeometryCollection(GeometryFactory.toGeometryArray(this._polyList));
}
getInvalidRingLines() {
this.polygonize();
return this._invalidRingLines;
}
findValidRings(edgeRingList, validEdgeRingList, invalidRingList) {
for (let i = edgeRingList.iterator(); i.hasNext();) {
const er = i.next();
if (er.isValid()) validEdgeRingList.add(er);else invalidRingList.add(er.getLineString());
}
}
polygonize() {
if (this._polyList !== null) return null;
this._polyList = new ArrayList();
if (this._graph === null) return null;
this._dangles = this._graph.deleteDangles();
this._cutEdges = this._graph.deleteCutEdges();
const edgeRingList = this._graph.getEdgeRings();
let validEdgeRingList = new ArrayList();
this._invalidRingLines = new ArrayList();
if (this._isCheckingRingsValid) this.findValidRings(edgeRingList, validEdgeRingList, this._invalidRingLines);else validEdgeRingList = edgeRingList;
this.findShellsAndHoles(validEdgeRingList);
HoleAssigner.assignHolesToShells(this._holeList, this._shellList);
Collections.sort(this._shellList, new EdgeRing.EnvelopeComparator());
let includeAll = true;
if (this._extractOnlyPolygonal) {
Polygonizer.findDisjointShells(this._shellList);
includeAll = false;
}
this._polyList = Polygonizer.extractPolygons(this._shellList, includeAll);
}
getDangles() {
this.polygonize();
return this._dangles;
}
getCutEdges() {
this.polygonize();
return this._cutEdges;
}
getPolygons() {
this.polygonize();
return this._polyList;
}
add() {
if (hasInterface(arguments[0], Collection)) {
const geomList = arguments[0];
for (let i = geomList.iterator(); i.hasNext();) {
const geometry = i.next();
this.add(geometry);
}
} else if (arguments[0] instanceof LineString) {
const line = arguments[0];
this._geomFactory = line.getFactory();
if (this._graph === null) this._graph = new PolygonizeGraph(this._geomFactory);
this._graph.addEdge(line);
} else if (arguments[0] instanceof Geometry) {
const g = arguments[0];
g.apply(this._lineStringAdder);
}
}
setCheckRingsValid(isCheckingRingsValid) {
this._isCheckingRingsValid = isCheckingRingsValid;
}
findShellsAndHoles(edgeRingList) {
this._holeList = new ArrayList();
this._shellList = new ArrayList();
for (let i = edgeRingList.iterator(); i.hasNext();) {
const er = i.next();
er.computeHole();
if (er.isHole()) this._holeList.add(er);else this._shellList.add(er);
}
}
}
class LineStringAdder {
constructor() {
LineStringAdder.constructor_.apply(this, arguments);
}
static constructor_() {
this.p = null;
const p = arguments[0];
this.p = p;
}
filter(g) {
if (g instanceof LineString) this.p.add(g);
}
get interfaces_() {
return [GeometryComponentFilter];
}
}
Polygonizer.LineStringAdder = LineStringAdder;
var polygonize = /*#__PURE__*/Object.freeze({
__proto__: null,
Polygonizer: Polygonizer
});
class RelateComputer {
constructor() {
RelateComputer.constructor_.apply(this, arguments);
}
static constructor_() {
this._li = new RobustLineIntersector();
this._ptLocator = new PointLocator();
this._arg = null;
this._nodes = new NodeMap$1(new RelateNodeFactory());
this._im = null;
this._isolatedEdges = new ArrayList();
this._invalidPoint = null;
const arg = arguments[0];
this._arg = arg;
}
insertEdgeEnds(ee) {
for (let i = ee.iterator(); i.hasNext();) {
const e = i.next();
this._nodes.add(e);
}
}
computeProperIntersectionIM(intersector, im) {
const dimA = this._arg[0].getGeometry().getDimension();
const dimB = this._arg[1].getGeometry().getDimension();
const hasProper = intersector.hasProperIntersection();
const hasProperInterior = intersector.hasProperInteriorIntersection();
if (dimA === 2 && dimB === 2) {
if (hasProper) im.setAtLeast('212101212');
} else if (dimA === 2 && dimB === 1) {
if (hasProper) im.setAtLeast('FFF0FFFF2');
if (hasProperInterior) im.setAtLeast('1FFFFF1FF');
} else if (dimA === 1 && dimB === 2) {
if (hasProper) im.setAtLeast('F0FFFFFF2');
if (hasProperInterior) im.setAtLeast('1F1FFFFFF');
} else if (dimA === 1 && dimB === 1) {
if (hasProperInterior) im.setAtLeast('0FFFFFFFF');
}
}
labelIsolatedEdges(thisIndex, targetIndex) {
for (let ei = this._arg[thisIndex].getEdgeIterator(); ei.hasNext();) {
const e = ei.next();
if (e.isIsolated()) {
this.labelIsolatedEdge(e, targetIndex, this._arg[targetIndex].getGeometry());
this._isolatedEdges.add(e);
}
}
}
labelIsolatedEdge(e, targetIndex, target) {
if (target.getDimension() > 0) {
const loc = this._ptLocator.locate(e.getCoordinate(), target);
e.getLabel().setAllLocations(targetIndex, loc);
} else {
e.getLabel().setAllLocations(targetIndex, Location.EXTERIOR);
}
}
computeIM() {
const im = new IntersectionMatrix();
im.set(Location.EXTERIOR, Location.EXTERIOR, 2);
if (!this._arg[0].getGeometry().getEnvelopeInternal().intersects(this._arg[1].getGeometry().getEnvelopeInternal())) {
this.computeDisjointIM(im);
return im;
}
this._arg[0].computeSelfNodes(this._li, false);
this._arg[1].computeSelfNodes(this._li, false);
const intersector = this._arg[0].computeEdgeIntersections(this._arg[1], this._li, false);
this.computeIntersectionNodes(0);
this.computeIntersectionNodes(1);
this.copyNodesAndLabels(0);
this.copyNodesAndLabels(1);
this.labelIsolatedNodes();
this.computeProperIntersectionIM(intersector, im);
const eeBuilder = new EdgeEndBuilder();
const ee0 = eeBuilder.computeEdgeEnds(this._arg[0].getEdgeIterator());
this.insertEdgeEnds(ee0);
const ee1 = eeBuilder.computeEdgeEnds(this._arg[1].getEdgeIterator());
this.insertEdgeEnds(ee1);
this.labelNodeEdges();
this.labelIsolatedEdges(0, 1);
this.labelIsolatedEdges(1, 0);
this.updateIM(im);
return im;
}
labelNodeEdges() {
for (let ni = this._nodes.iterator(); ni.hasNext();) {
const node = ni.next();
node.getEdges().computeLabelling(this._arg);
}
}
copyNodesAndLabels(argIndex) {
for (let i = this._arg[argIndex].getNodeIterator(); i.hasNext();) {
const graphNode = i.next();
const newNode = this._nodes.addNode(graphNode.getCoordinate());
newNode.setLabel(argIndex, graphNode.getLabel().getLocation(argIndex));
}
}
labelIntersectionNodes(argIndex) {
for (let i = this._arg[argIndex].getEdgeIterator(); i.hasNext();) {
const e = i.next();
const eLoc = e.getLabel().getLocation(argIndex);
for (let eiIt = e.getEdgeIntersectionList().iterator(); eiIt.hasNext();) {
const ei = eiIt.next();
const n = this._nodes.find(ei.coord);
if (n.getLabel().isNull(argIndex)) if (eLoc === Location.BOUNDARY) n.setLabelBoundary(argIndex);else n.setLabel(argIndex, Location.INTERIOR);
}
}
}
labelIsolatedNode(n, targetIndex) {
const loc = this._ptLocator.locate(n.getCoordinate(), this._arg[targetIndex].getGeometry());
n.getLabel().setAllLocations(targetIndex, loc);
}
computeIntersectionNodes(argIndex) {
for (let i = this._arg[argIndex].getEdgeIterator(); i.hasNext();) {
const e = i.next();
const eLoc = e.getLabel().getLocation(argIndex);
for (let eiIt = e.getEdgeIntersectionList().iterator(); eiIt.hasNext();) {
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);
}
}
}
labelIsolatedNodes() {
for (let ni = this._nodes.iterator(); ni.hasNext();) {
const n = ni.next();
const label = n.getLabel();
Assert.isTrue(label.getGeometryCount() > 0, 'node with empty label found');
if (n.isIsolated()) if (label.isNull(0)) this.labelIsolatedNode(n, 0);else this.labelIsolatedNode(n, 1);
}
}
updateIM(im) {
for (let ei = this._isolatedEdges.iterator(); ei.hasNext();) {
const e = ei.next();
e.updateIM(im);
}
for (let ni = this._nodes.iterator(); ni.hasNext();) {
const node = ni.next();
node.updateIM(im);
node.updateIMFromEdges(im);
}
}
computeDisjointIM(im) {
const ga = this._arg[0].getGeometry();
if (!ga.isEmpty()) {
im.set(Location.INTERIOR, Location.EXTERIOR, ga.getDimension());
im.set(Location.BOUNDARY, Location.EXTERIOR, ga.getBoundaryDimension());
}
const gb = this._arg[1].getGeometry();
if (!gb.isEmpty()) {
im.set(Location.EXTERIOR, Location.INTERIOR, gb.getDimension());
im.set(Location.EXTERIOR, Location.BOUNDARY, gb.getBoundaryDimension());
}
}
}
class RectangleContains {
constructor() {
RectangleContains.constructor_.apply(this, arguments);
}
static constructor_() {
this._rectEnv = null;
const rectangle = arguments[0];
this._rectEnv = rectangle.getEnvelopeInternal();
}
static contains(rectangle, b) {
const rc = new RectangleContains(rectangle);
return rc.contains(b);
}
isContainedInBoundary(geom) {
if (geom instanceof Polygon) return false;
if (geom instanceof Point) return this.isPointContainedInBoundary(geom);
if (geom instanceof LineString) return this.isLineStringContainedInBoundary(geom);
for (let i = 0; i < geom.getNumGeometries(); i++) {
const comp = geom.getGeometryN(i);
if (!this.isContainedInBoundary(comp)) return false;
}
return true;
}
isLineSegmentContainedInBoundary(p0, p1) {
if (p0.equals(p1)) return this.isPointContainedInBoundary(p0);
if (p0.x === p1.x) {
if (p0.x === this._rectEnv.getMinX() || p0.x === this._rectEnv.getMaxX()) return true;
} else if (p0.y === p1.y) {
if (p0.y === this._rectEnv.getMinY() || p0.y === this._rectEnv.getMaxY()) return true;
}
return false;
}
isLineStringContainedInBoundary(line) {
const seq = line.getCoordinateSequence();
const p0 = new Coordinate();
const p1 = new Coordinate();
for (let i = 0; i < seq.size() - 1; i++) {
seq.getCoordinate(i, p0);
seq.getCoordinate(i + 1, p1);
if (!this.isLineSegmentContainedInBoundary(p0, p1)) return false;
}
return true;
}
isPointContainedInBoundary() {
if (arguments[0] instanceof Point) {
const point = arguments[0];
return this.isPointContainedInBoundary(point.getCoordinate());
} else if (arguments[0] instanceof Coordinate) {
const pt = arguments[0];
return pt.x === this._rectEnv.getMinX() || pt.x === this._rectEnv.getMaxX() || pt.y === this._rectEnv.getMinY() || pt.y === this._rectEnv.getMaxY();
}
}
contains(geom) {
if (!this._rectEnv.contains(geom.getEnvelopeInternal())) return false;
if (this.isContainedInBoundary(geom)) return false;
return true;
}
}
class RectangleLineIntersector {
constructor() {
RectangleLineIntersector.constructor_.apply(this, arguments);
}
static constructor_() {
this._li = new RobustLineIntersector();
this._rectEnv = null;
this._diagUp0 = null;
this._diagUp1 = null;
this._diagDown0 = null;
this._diagDown1 = null;
const rectEnv = arguments[0];
this._rectEnv = rectEnv;
this._diagUp0 = new Coordinate(rectEnv.getMinX(), rectEnv.getMinY());
this._diagUp1 = new Coordinate(rectEnv.getMaxX(), rectEnv.getMaxY());
this._diagDown0 = new Coordinate(rectEnv.getMinX(), rectEnv.getMaxY());
this._diagDown1 = new Coordinate(rectEnv.getMaxX(), rectEnv.getMinY());
}
intersects(p0, p1) {
const segEnv = new Envelope(p0, p1);
if (!this._rectEnv.intersects(segEnv)) return false;
if (this._rectEnv.intersects(p0)) return true;
if (this._rectEnv.intersects(p1)) return true;
if (p0.compareTo(p1) > 0) {
const tmp = p0;
p0 = p1;
p1 = tmp;
}
let isSegUpwards = false;
if (p1.y > p0.y) isSegUpwards = true;
if (isSegUpwards) this._li.computeIntersection(p0, p1, this._diagDown0, this._diagDown1);else this._li.computeIntersection(p0, p1, this._diagUp0, this._diagUp1);
if (this._li.hasIntersection()) return true;
return false;
}
}
class RectangleIntersects {
constructor() {
RectangleIntersects.constructor_.apply(this, arguments);
}
static constructor_() {
this._rectangle = null;
this._rectEnv = null;
const rectangle = arguments[0];
this._rectangle = rectangle;
this._rectEnv = rectangle.getEnvelopeInternal();
}
static intersects(rectangle, b) {
const rp = new RectangleIntersects(rectangle);
return rp.intersects(b);
}
intersects(geom) {
if (!this._rectEnv.intersects(geom.getEnvelopeInternal())) return false;
const visitor = new EnvelopeIntersectsVisitor(this._rectEnv);
visitor.applyTo(geom);
if (visitor.intersects()) return true;
const ecpVisitor = new GeometryContainsPointVisitor(this._rectangle);
ecpVisitor.applyTo(geom);
if (ecpVisitor.containsPoint()) return true;
const riVisitor = new RectangleIntersectsSegmentVisitor(this._rectangle);
riVisitor.applyTo(geom);
if (riVisitor.intersects()) return true;
return false;
}
}
class EnvelopeIntersectsVisitor extends ShortCircuitedGeometryVisitor {
constructor() {
super();
EnvelopeIntersectsVisitor.constructor_.apply(this, arguments);
}
static constructor_() {
this._rectEnv = null;
this._intersects = false;
const rectEnv = arguments[0];
this._rectEnv = rectEnv;
}
isDone() {
return this._intersects === true;
}
visit(element) {
const elementEnv = element.getEnvelopeInternal();
if (!this._rectEnv.intersects(elementEnv)) return null;
if (this._rectEnv.contains(elementEnv)) {
this._intersects = true;
return null;
}
if (elementEnv.getMinX() >= this._rectEnv.getMinX() && elementEnv.getMaxX() <= this._rectEnv.getMaxX()) {
this._intersects = true;
return null;
}
if (elementEnv.getMinY() >= this._rectEnv.getMinY() && elementEnv.getMaxY() <= this._rectEnv.getMaxY()) {
this._intersects = true;
return null;
}
}
intersects() {
return this._intersects;
}
}
class GeometryContainsPointVisitor extends ShortCircuitedGeometryVisitor {
constructor() {
super();
GeometryContainsPointVisitor.constructor_.apply(this, arguments);
}
static constructor_() {
this._rectSeq = null;
this._rectEnv = null;
this._containsPoint = false;
const rectangle = arguments[0];
this._rectSeq = rectangle.getExteriorRing().getCoordinateSequence();
this._rectEnv = rectangle.getEnvelopeInternal();
}
isDone() {
return this._containsPoint === true;
}
visit(geom) {
if (!(geom instanceof Polygon)) return null;
const elementEnv = geom.getEnvelopeInternal();
if (!this._rectEnv.intersects(elementEnv)) return null;
const rectPt = new Coordinate();
for (let i = 0; i < 4; i++) {
this._rectSeq.getCoordinate(i, rectPt);
if (!elementEnv.contains(rectPt)) continue;
if (SimplePointInAreaLocator.containsPointInPolygon(rectPt, geom)) {
this._containsPoint = true;
return null;
}
}
}
containsPoint() {
return this._containsPoint;
}
}
class RectangleIntersectsSegmentVisitor extends ShortCircuitedGeometryVisitor {
constructor() {
super();
RectangleIntersectsSegmentVisitor.constructor_.apply(this, arguments);
}
static constructor_() {
this._rectEnv = null;
this._rectIntersector = null;
this._hasIntersection = false;
this._p0 = new Coordinate();
this._p1 = new Coordinate();
const rectangle = arguments[0];
this._rectEnv = rectangle.getEnvelopeInternal();
this._rectIntersector = new RectangleLineIntersector(this._rectEnv);
}
intersects() {
return this._hasIntersection;
}
isDone() {
return this._hasIntersection === true;
}
visit(geom) {
const elementEnv = geom.getEnvelopeInternal();
if (!this._rectEnv.intersects(elementEnv)) return null;
const lines = LinearComponentExtracter.getLines(geom);
this.checkIntersectionWithLineStrings(lines);
}
checkIntersectionWithLineStrings(lines) {
for (let i = lines.iterator(); i.hasNext();) {
const testLine = i.next();
this.checkIntersectionWithSegments(testLine);
if (this._hasIntersection) return null;
}
}
checkIntersectionWithSegments(testLine) {
const seq1 = testLine.getCoordinateSequence();
for (let j = 1; j < seq1.size(); j++) {
seq1.getCoordinate(j - 1, this._p0);
seq1.getCoordinate(j, this._p1);
if (this._rectIntersector.intersects(this._p0, this._p1)) {
this._hasIntersection = true;
return null;
}
}
}
}
class RelateOp extends GeometryGraphOperation {
constructor() {
super();
RelateOp.constructor_.apply(this, arguments);
}
static constructor_() {
this._relate = null;
if (arguments.length === 2) {
const g0 = arguments[0],
g1 = arguments[1];
GeometryGraphOperation.constructor_.call(this, g0, g1);
this._relate = new RelateComputer(this._arg);
} else if (arguments.length === 3) {
const g0 = arguments[0],
g1 = arguments[1],
boundaryNodeRule = arguments[2];
GeometryGraphOperation.constructor_.call(this, g0, g1, boundaryNodeRule);
this._relate = new RelateComputer(this._arg);
}
}
static covers(g1, g2) {
if (g2.getDimension() === 2 && g1.getDimension() < 2) return false;
if (g2.getDimension() === 1 && g1.getDimension() < 1 && g2.getLength() > 0.0) return false;
if (!g1.getEnvelopeInternal().covers(g2.getEnvelopeInternal())) return false;
if (g1.isRectangle()) return true;
return new RelateOp(g1, g2).getIntersectionMatrix().isCovers();
}
static intersects(g1, g2) {
if (!g1.getEnvelopeInternal().intersects(g2.getEnvelopeInternal())) return false;
if (g1.isRectangle()) return RectangleIntersects.intersects(g1, g2);
if (g2.isRectangle()) return RectangleIntersects.intersects(g2, g1);
if (g1.isGeometryCollection() || g2.isGeometryCollection()) {
for (let i = 0; i < g1.getNumGeometries(); i++) for (let j = 0; j < g2.getNumGeometries(); j++) if (g1.getGeometryN(i).intersects(g2.getGeometryN(j))) return true;
return false;
}
return new RelateOp(g1, g2).getIntersectionMatrix().isIntersects();
}
static touches(g1, g2) {
if (!g1.getEnvelopeInternal().intersects(g2.getEnvelopeInternal())) return false;
return new RelateOp(g1, g2).getIntersectionMatrix().isTouches(g1.getDimension(), g2.getDimension());
}
static equalsTopo(g1, g2) {
if (!g1.getEnvelopeInternal().equals(g2.getEnvelopeInternal())) return false;
return RelateOp.relate(g1, g2).isEquals(g1.getDimension(), g2.getDimension());
}
static relate() {
if (arguments.length === 2) {
const a = arguments[0],
b = arguments[1];
const relOp = new RelateOp(a, b);
const im = relOp.getIntersectionMatrix();
return im;
} else if (arguments.length === 3) {
const a = arguments[0],
b = arguments[1],
boundaryNodeRule = arguments[2];
const relOp = new RelateOp(a, b, boundaryNodeRule);
const im = relOp.getIntersectionMatrix();
return im;
}
}
static overlaps(g1, g2) {
if (!g1.getEnvelopeInternal().intersects(g2.getEnvelopeInternal())) return false;
return new RelateOp(g1, g2).getIntersectionMatrix().isOverlaps(g1.getDimension(), g2.getDimension());
}
static crosses(g1, g2) {
if (!g1.getEnvelopeInternal().intersects(g2.getEnvelopeInternal())) return false;
return new RelateOp(g1, g2).getIntersectionMatrix().isCrosses(g1.getDimension(), g2.getDimension());
}
static contains(g1, g2) {
if (g2.getDimension() === 2 && g1.getDimension() < 2) return false;
if (g2.getDimension() === 1 && g1.getDimension() < 1 && g2.getLength() > 0.0) return false;
if (!g1.getEnvelopeInternal().contains(g2.getEnvelopeInternal())) return false;
if (g1.isRectangle()) return RectangleContains.contains(g1, g2);
return new RelateOp(g1, g2).getIntersectionMatrix().isContains();
}
getIntersectionMatrix() {
return this._relate.computeIM();
}
}
var relate = /*#__PURE__*/Object.freeze({
__proto__: null,
RelateOp: RelateOp
});
class PointGeometryUnion {
constructor() {
PointGeometryUnion.constructor_.apply(this, arguments);
}
static constructor_() {
this._pointGeom = null;
this._otherGeom = null;
this._geomFact = null;
const pointGeom = arguments[0],
otherGeom = arguments[1];
this._pointGeom = pointGeom;
this._otherGeom = otherGeom;
this._geomFact = otherGeom.getFactory();
}
static union(pointGeom, otherGeom) {
const unioner = new PointGeometryUnion(pointGeom, otherGeom);
return unioner.union();
}
union() {
const locater = new PointLocator();
const exteriorCoords = new TreeSet();
for (let i = 0; i < this._pointGeom.getNumGeometries(); i++) {
const point = this._pointGeom.getGeometryN(i);
const coord = point.getCoordinate();
const loc = locater.locate(coord, this._otherGeom);
if (loc === Location.EXTERIOR) exteriorCoords.add(coord);
}
if (exteriorCoords.size() === 0) return this._otherGeom;
let ptComp = null;
const coords = CoordinateArrays.toCoordinateArray(exteriorCoords);
if (coords.length === 1) ptComp = this._geomFact.createPoint(coords[0]);else ptComp = this._geomFact.createMultiPointFromCoords(coords);
return GeometryCombiner.combine(ptComp, this._otherGeom);
}
}
class InputExtracter {
constructor() {
InputExtracter.constructor_.apply(this, arguments);
}
static constructor_() {
this._geomFactory = null;
this._polygons = new ArrayList();
this._lines = new ArrayList();
this._points = new ArrayList();
this._dimension = Dimension.FALSE;
}
static extract() {
if (hasInterface(arguments[0], Collection)) {
const geoms = arguments[0];
const extracter = new InputExtracter();
extracter.add(geoms);
return extracter;
} else if (arguments[0] instanceof Geometry) {
const geom = arguments[0];
const extracter = new InputExtracter();
extracter.add(geom);
return extracter;
}
}
getFactory() {
return this._geomFactory;
}
recordDimension(dim) {
if (dim > this._dimension) this._dimension = dim;
}
getDimension() {
return this._dimension;
}
filter(geom) {
this.recordDimension(geom.getDimension());
if (geom instanceof GeometryCollection) return null;
if (geom.isEmpty()) return null;
if (geom instanceof Polygon) {
this._polygons.add(geom);
return null;
} else if (geom instanceof LineString) {
this._lines.add(geom);
return null;
} else if (geom instanceof Point) {
this._points.add(geom);
return null;
}
Assert.shouldNeverReachHere('Unhandled geometry type: ' + geom.getGeometryType());
}
getExtract(dim) {
switch (dim) {
case 0:
return this._points;
case 1:
return this._lines;
case 2:
return this._polygons;
}
Assert.shouldNeverReachHere('Invalid dimension: ' + dim);
return null;
}
isEmpty() {
return this._polygons.isEmpty() && this._lines.isEmpty() && this._points.isEmpty();
}
add() {
if (hasInterface(arguments[0], Collection)) {
const geoms = arguments[0];
for (const geom of geoms) this.add(geom);
} else if (arguments[0] instanceof Geometry) {
const geom = arguments[0];
if (this._geomFactory === null) this._geomFactory = geom.getFactory();
geom.apply(this);
}
}
get interfaces_() {
return [GeometryFilter];
}
}
class OverlapUnion {
constructor() {
OverlapUnion.constructor_.apply(this, arguments);
}
static constructor_() {
this._geomFactory = null;
this._g0 = null;
this._g1 = null;
this._isUnionSafe = null;
const g0 = arguments[0],
g1 = arguments[1];
this._g0 = g0;
this._g1 = g1;
this._geomFactory = g0.getFactory();
}
static containsProperly() {
if (arguments.length === 2) {
const env = arguments[0],
p = arguments[1];
if (env.isNull()) return false;
return p.getX() > env.getMinX() && p.getX() < env.getMaxX() && p.getY() > env.getMinY() && p.getY() < env.getMaxY();
} else if (arguments.length === 3) {
const env = arguments[0],
p0 = arguments[1],
p1 = arguments[2];
return OverlapUnion.containsProperly(env, p0) && OverlapUnion.containsProperly(env, p1);
}
}
static union(g0, g1) {
const union = new OverlapUnion(g0, g1);
return union.union();
}
static intersects(env, p0, p1) {
return env.intersects(p0) || env.intersects(p1);
}
static overlapEnvelope(g0, g1) {
const g0Env = g0.getEnvelopeInternal();
const g1Env = g1.getEnvelopeInternal();
const overlapEnv = g0Env.intersection(g1Env);
return overlapEnv;
}
static extractBorderSegments(geom, env, segs) {
geom.apply(new class {
get interfaces_() {
return [CoordinateSequenceFilter];
}
filter(seq, i) {
if (i <= 0) return null;
const p0 = seq.getCoordinate(i - 1);
const p1 = seq.getCoordinate(i);
const isBorder = OverlapUnion.intersects(env, p0, p1) && !OverlapUnion.containsProperly(env, p0, p1);
if (isBorder) {
const seg = new LineSegment(p0, p1);
segs.add(seg);
}
}
isDone() {
return false;
}
isGeometryChanged() {
return false;
}
}());
}
static unionBuffer(g0, g1) {
const factory = g0.getFactory();
const gColl = factory.createGeometryCollection([g0, g1]);
const union = gColl.buffer(0.0);
return union;
}
isBorderSegmentsSame(result, env) {
const segsBefore = this.extractBorderSegments(this._g0, this._g1, env);
const segsAfter = new ArrayList();
OverlapUnion.extractBorderSegments(result, env, segsAfter);
return this.isEqual(segsBefore, segsAfter);
}
extractByEnvelope(env, geom, disjointGeoms) {
const intersectingGeoms = new ArrayList();
for (let i = 0; i < geom.getNumGeometries(); i++) {
const elem = geom.getGeometryN(i);
if (elem.getEnvelopeInternal().intersects(env)) {
intersectingGeoms.add(elem);
} else {
const copy = elem.copy();
disjointGeoms.add(copy);
}
}
return this._geomFactory.buildGeometry(intersectingGeoms);
}
isEqual(segs0, segs1) {
if (segs0.size() !== segs1.size()) return false;
const segIndex = new HashSet(segs0);
for (const seg of segs1) if (!segIndex.contains(seg)) return false;
return true;
}
union() {
const overlapEnv = OverlapUnion.overlapEnvelope(this._g0, this._g1);
if (overlapEnv.isNull()) {
const g0Copy = this._g0.copy();
const g1Copy = this._g1.copy();
return GeometryCombiner.combine(g0Copy, g1Copy);
}
const disjointPolys = new ArrayList();
const g0Overlap = this.extractByEnvelope(overlapEnv, this._g0, disjointPolys);
const g1Overlap = this.extractByEnvelope(overlapEnv, this._g1, disjointPolys);
const unionGeom = this.unionFull(g0Overlap, g1Overlap);
let result = null;
this._isUnionSafe = this.isBorderSegmentsSame(unionGeom, overlapEnv);
if (!this._isUnionSafe) result = this.unionFull(this._g0, this._g1);else result = this.combine(unionGeom, disjointPolys);
return result;
}
combine(unionGeom, disjointPolys) {
if (disjointPolys.size() <= 0) return unionGeom;
disjointPolys.add(unionGeom);
const result = GeometryCombiner.combine(disjointPolys);
return result;
}
unionFull(geom0, geom1) {
try {
return geom0.union(geom1);
} catch (ex) {
if (ex instanceof TopologyException) return OverlapUnion.unionBuffer(geom0, geom1);else throw ex;
} finally {}
}
extractBorderSegments(geom0, geom1, env) {
const segs = new ArrayList();
OverlapUnion.extractBorderSegments(geom0, env, segs);
if (geom1 !== null) OverlapUnion.extractBorderSegments(geom1, env, segs);
return segs;
}
isUnionOptimized() {
return this._isUnionSafe;
}
}
class CascadedPolygonUnion {
constructor() {
CascadedPolygonUnion.constructor_.apply(this, arguments);
}
static constructor_() {
this._inputPolys = null;
this._geomFactory = null;
const polys = arguments[0];
this._inputPolys = polys;
if (this._inputPolys === null) this._inputPolys = new ArrayList();
}
static restrictToPolygons(g) {
if (hasInterface(g, Polygonal)) return g;
const polygons = PolygonExtracter.getPolygons(g);
if (polygons.size() === 1) return polygons.get(0);
return g.getFactory().createMultiPolygon(GeometryFactory.toPolygonArray(polygons));
}
static getGeometry(list, index) {
if (index >= list.size()) return null;
return list.get(index);
}
static union(polys) {
const op = new CascadedPolygonUnion(polys);
return op.union();
}
reduceToGeometries(geomTree) {
const geoms = new ArrayList();
for (let i = geomTree.iterator(); i.hasNext();) {
const o = i.next();
let geom = null;
if (hasInterface(o, List)) geom = this.unionTree(o);else if (o instanceof Geometry) geom = o;
geoms.add(geom);
}
return geoms;
}
union() {
if (this._inputPolys === null) throw new IllegalStateException('union() method cannot be called twice');
if (this._inputPolys.isEmpty()) return null;
this._geomFactory = this._inputPolys.iterator().next().getFactory();
const index = new STRtree(CascadedPolygonUnion.STRTREE_NODE_CAPACITY);
for (let i = this._inputPolys.iterator(); i.hasNext();) {
const item = i.next();
index.insert(item.getEnvelopeInternal(), item);
}
this._inputPolys = null;
const itemTree = index.itemsTree();
const unionAll = this.unionTree(itemTree);
return unionAll;
}
binaryUnion() {
if (arguments.length === 1) {
const geoms = arguments[0];
return this.binaryUnion(geoms, 0, geoms.size());
} else if (arguments.length === 3) {
const geoms = arguments[0],
start = arguments[1],
end = arguments[2];
if (end - start <= 1) {
const g0 = CascadedPolygonUnion.getGeometry(geoms, start);
return this.unionSafe(g0, null);
} else if (end - start === 2) {
return this.unionSafe(CascadedPolygonUnion.getGeometry(geoms, start), CascadedPolygonUnion.getGeometry(geoms, start + 1));
} else {
const mid = Math.trunc((end + start) / 2);
const g0 = this.binaryUnion(geoms, start, mid);
const g1 = this.binaryUnion(geoms, mid, end);
return this.unionSafe(g0, g1);
}
}
}
repeatedUnion(geoms) {
let union = null;
for (let i = geoms.iterator(); i.hasNext();) {
const g = i.next();
if (union === null) union = g.copy();else union = union.union(g);
}
return union;
}
unionSafe(g0, g1) {
if (g0 === null && g1 === null) return null;
if (g0 === null) return g1.copy();
if (g1 === null) return g0.copy();
return this.unionActual(g0, g1);
}
unionActual(g0, g1) {
const union = OverlapUnion.union(g0, g1);
return CascadedPolygonUnion.restrictToPolygons(union);
}
unionTree(geomTree) {
const geoms = this.reduceToGeometries(geomTree);
const union = this.binaryUnion(geoms);
return union;
}
bufferUnion() {
if (arguments.length === 1) {
const geoms = arguments[0];
const factory = geoms.get(0).getFactory();
const gColl = factory.buildGeometry(geoms);
const unionAll = gColl.buffer(0.0);
return unionAll;
} else if (arguments.length === 2) {
const g0 = arguments[0],
g1 = arguments[1];
const factory = g0.getFactory();
const gColl = factory.createGeometryCollection([g0, g1]);
const unionAll = gColl.buffer(0.0);
return unionAll;
}
}
}
CascadedPolygonUnion.STRTREE_NODE_CAPACITY = 4;
class UnaryUnionOp {
constructor() {
UnaryUnionOp.constructor_.apply(this, arguments);
}
static constructor_() {
this._geomFact = null;
this._extracter = null;
if (arguments.length === 1) {
if (hasInterface(arguments[0], Collection)) {
const geoms = arguments[0];
this.extract(geoms);
} else if (arguments[0] instanceof Geometry) {
const geom = arguments[0];
this.extract(geom);
}
} else if (arguments.length === 2) {
const geoms = arguments[0],
geomFact = arguments[1];
this._geomFact = geomFact;
this.extract(geoms);
}
}
static union() {
if (arguments.length === 1) {
if (hasInterface(arguments[0], Collection)) {
const geoms = arguments[0];
const op = new UnaryUnionOp(geoms);
return op.union();
} else if (arguments[0] instanceof Geometry) {
const geom = arguments[0];
const op = new UnaryUnionOp(geom);
return op.union();
}
} else if (arguments.length === 2) {
const geoms = arguments[0],
geomFact = arguments[1];
const op = new UnaryUnionOp(geoms, geomFact);
return op.union();
}
}
unionNoOpt(g0) {
const empty = this._geomFact.createPoint();
return SnapIfNeededOverlayOp.overlayOp(g0, empty, OverlayOp.UNION);
}
unionWithNull(g0, g1) {
if (g0 === null && g1 === null) return null;
if (g1 === null) return g0;
if (g0 === null) return g1;
return g0.union(g1);
}
extract() {
if (hasInterface(arguments[0], Collection)) {
const geoms = arguments[0];
this._extracter = InputExtracter.extract(geoms);
} else if (arguments[0] instanceof Geometry) {
const geom = arguments[0];
this._extracter = InputExtracter.extract(geom);
}
}
union() {
if (this._geomFact === null) this._geomFact = this._extracter.getFactory();
if (this._geomFact === null) return null;
if (this._extracter.isEmpty()) return this._geomFact.createEmpty(this._extracter.getDimension());
const points = this._extracter.getExtract(0);
const lines = this._extracter.getExtract(1);
const polygons = this._extracter.getExtract(2);
let unionPoints = null;
if (points.size() > 0) {
const ptGeom = this._geomFact.buildGeometry(points);
unionPoints = this.unionNoOpt(ptGeom);
}
let unionLines = null;
if (lines.size() > 0) {
const lineGeom = this._geomFact.buildGeometry(lines);
unionLines = this.unionNoOpt(lineGeom);
}
let unionPolygons = null;
if (polygons.size() > 0) unionPolygons = CascadedPolygonUnion.union(polygons);
const unionLA = this.unionWithNull(unionLines, unionPolygons);
let union = null;
if (unionPoints === null) union = unionLA;else if (unionLA === null) union = unionPoints;else union = PointGeometryUnion.union(unionPoints, unionLA);
if (union === null) return this._geomFact.createGeometryCollection();
return union;
}
}
var union = /*#__PURE__*/Object.freeze({
__proto__: null,
UnaryUnionOp: UnaryUnionOp
});
var valid = /*#__PURE__*/Object.freeze({
__proto__: null,
IsValidOp: IsValidOp,
ConsistentAreaTester: ConsistentAreaTester
});
var operation = /*#__PURE__*/Object.freeze({
__proto__: null,
BoundaryOp: BoundaryOp,
IsSimpleOp: IsSimpleOp,
buffer: buffer,
distance: distance,
linemerge: linemerge,
overlay: overlay,
polygonize: polygonize,
relate: relate,
union: union,
valid: valid
});
class CommonBitsOp {
constructor() {
CommonBitsOp.constructor_.apply(this, arguments);
}
static constructor_() {
this._returnToOriginalPrecision = true;
this._cbr = null;
if (arguments.length === 0) {
CommonBitsOp.constructor_.call(this, true);
} else if (arguments.length === 1) {
const returnToOriginalPrecision = arguments[0];
this._returnToOriginalPrecision = returnToOriginalPrecision;
}
}
computeResultPrecision(result) {
if (this._returnToOriginalPrecision) this._cbr.addCommonBits(result);
return result;
}
union(geom0, geom1) {
const geom = this.removeCommonBits(geom0, geom1);
return this.computeResultPrecision(geom[0].union(geom[1]));
}
intersection(geom0, geom1) {
const geom = this.removeCommonBits(geom0, geom1);
return this.computeResultPrecision(geom[0].intersection(geom[1]));
}
removeCommonBits() {
if (arguments.length === 1) {
const geom0 = arguments[0];
this._cbr = new CommonBitsRemover();
this._cbr.add(geom0);
const geom = this._cbr.removeCommonBits(geom0.copy());
return geom;
} else if (arguments.length === 2) {
const geom0 = arguments[0],
geom1 = arguments[1];
this._cbr = new CommonBitsRemover();
this._cbr.add(geom0);
this._cbr.add(geom1);
const geom = new Array(2).fill(null);
geom[0] = this._cbr.removeCommonBits(geom0.copy());
geom[1] = this._cbr.removeCommonBits(geom1.copy());
return geom;
}
}
buffer(geom0, distance) {
const geom = this.removeCommonBits(geom0);
return this.computeResultPrecision(geom.buffer(distance));
}
symDifference(geom0, geom1) {
const geom = this.removeCommonBits(geom0, geom1);
return this.computeResultPrecision(geom[0].symDifference(geom[1]));
}
difference(geom0, geom1) {
const geom = this.removeCommonBits(geom0, geom1);
return this.computeResultPrecision(geom[0].difference(geom[1]));
}
}
class EnhancedPrecisionOp {
static union(geom0, geom1) {
let originalEx = null;
try {
const result = geom0.union(geom1);
return result;
} catch (ex) {
if (ex instanceof RuntimeException) originalEx = ex;else throw ex;
} finally {}
try {
const cbo = new CommonBitsOp(true);
const resultEP = cbo.union(geom0, geom1);
if (!resultEP.isValid()) throw originalEx;
return resultEP;
} catch (ex2) {
if (ex2 instanceof RuntimeException) throw originalEx;else throw ex2;
} finally {}
}
static intersection(geom0, geom1) {
let originalEx = null;
try {
const result = geom0.intersection(geom1);
return result;
} catch (ex) {
if (ex instanceof RuntimeException) originalEx = ex;else throw ex;
} finally {}
try {
const cbo = new CommonBitsOp(true);
const resultEP = cbo.intersection(geom0, geom1);
if (!resultEP.isValid()) throw originalEx;
return resultEP;
} catch (ex2) {
if (ex2 instanceof RuntimeException) throw originalEx;else throw ex2;
} finally {}
}
static buffer(geom, distance) {
let originalEx = null;
try {
const result = geom.buffer(distance);
return result;
} catch (ex) {
if (ex instanceof RuntimeException) originalEx = ex;else throw ex;
} finally {}
try {
const cbo = new CommonBitsOp(true);
const resultEP = cbo.buffer(geom, distance);
if (!resultEP.isValid()) throw originalEx;
return resultEP;
} catch (ex2) {
if (ex2 instanceof RuntimeException) throw originalEx;else throw ex2;
} finally {}
}
static symDifference(geom0, geom1) {
let originalEx = null;
try {
const result = geom0.symDifference(geom1);
return result;
} catch (ex) {
if (ex instanceof RuntimeException) originalEx = ex;else throw ex;
} finally {}
try {
const cbo = new CommonBitsOp(true);
const resultEP = cbo.symDifference(geom0, geom1);
if (!resultEP.isValid()) throw originalEx;
return resultEP;
} catch (ex2) {
if (ex2 instanceof RuntimeException) throw originalEx;else throw ex2;
} finally {}
}
static difference(geom0, geom1) {
let originalEx = null;
try {
const result = geom0.difference(geom1);
return result;
} catch (ex) {
if (ex instanceof RuntimeException) originalEx = ex;else throw ex;
} finally {}
try {
const cbo = new CommonBitsOp(true);
const resultEP = cbo.difference(geom0, geom1);
if (!resultEP.isValid()) throw originalEx;
return resultEP;
} catch (ex2) {
if (ex2 instanceof RuntimeException) throw originalEx;else throw ex2;
} finally {}
}
}
class PrecisionReducerCoordinateOperation extends GeometryEditor.CoordinateOperation {
constructor() {
super();
PrecisionReducerCoordinateOperation.constructor_.apply(this, arguments);
}
static constructor_() {
this._targetPM = null;
this._removeCollapsed = true;
const targetPM = arguments[0],
removeCollapsed = arguments[1];
this._targetPM = targetPM;
this._removeCollapsed = removeCollapsed;
}
edit() {
if (arguments.length === 2 && arguments[1] instanceof Geometry && arguments[0] instanceof Array) {
const coordinates = arguments[0],
geom = arguments[1];
if (coordinates.length === 0) return null;
const reducedCoords = new Array(coordinates.length).fill(null);
for (let i = 0; i < coordinates.length; i++) {
const coord = new Coordinate(coordinates[i]);
this._targetPM.makePrecise(coord);
reducedCoords[i] = coord;
}
const noRepeatedCoordList = new CoordinateList(reducedCoords, false);
const noRepeatedCoords = noRepeatedCoordList.toCoordinateArray();
let minLength = 0;
if (geom instanceof LineString) minLength = 2;
if (geom instanceof LinearRing) minLength = 4;
let collapsedCoords = reducedCoords;
if (this._removeCollapsed) collapsedCoords = null;
if (noRepeatedCoords.length < minLength) return collapsedCoords;
return noRepeatedCoords;
} else {
return super.edit.apply(this, arguments);
}
}
}
class GeometryPrecisionReducer {
constructor() {
GeometryPrecisionReducer.constructor_.apply(this, arguments);
}
static constructor_() {
this._targetPM = null;
this._removeCollapsed = true;
this._changePrecisionModel = false;
this._isPointwise = false;
const pm = arguments[0];
this._targetPM = pm;
}
static reduce(g, precModel) {
const reducer = new GeometryPrecisionReducer(precModel);
return reducer.reduce(g);
}
static reducePointwise(g, precModel) {
const reducer = new GeometryPrecisionReducer(precModel);
reducer.setPointwise(true);
return reducer.reduce(g);
}
fixPolygonalTopology(geom) {
let geomToBuffer = geom;
if (!this._changePrecisionModel) geomToBuffer = this.changePM(geom, this._targetPM);
const bufGeom = BufferOp.bufferOp(geomToBuffer, 0);
return bufGeom;
}
reducePointwise(geom) {
let geomEdit = null;
if (this._changePrecisionModel) {
const newFactory = this.createFactory(geom.getFactory(), this._targetPM);
geomEdit = new GeometryEditor(newFactory);
} else {
geomEdit = new GeometryEditor();
}
let finalRemoveCollapsed = this._removeCollapsed;
if (geom.getDimension() >= 2) finalRemoveCollapsed = true;
const reduceGeom = geomEdit.edit(geom, new PrecisionReducerCoordinateOperation(this._targetPM, finalRemoveCollapsed));
return reduceGeom;
}
changePM(geom, newPM) {
const geomEditor = this.createEditor(geom.getFactory(), newPM);
return geomEditor.edit(geom, new GeometryEditor.NoOpGeometryOperation());
}
setRemoveCollapsedComponents(removeCollapsed) {
this._removeCollapsed = removeCollapsed;
}
createFactory(inputFactory, pm) {
const newFactory = new GeometryFactory(pm, inputFactory.getSRID(), inputFactory.getCoordinateSequenceFactory());
return newFactory;
}
setChangePrecisionModel(changePrecisionModel) {
this._changePrecisionModel = changePrecisionModel;
}
reduce(geom) {
const reducePW = this.reducePointwise(geom);
if (this._isPointwise) return reducePW;
if (!hasInterface(reducePW, Polygonal)) return reducePW;
if (IsValidOp.isValid(reducePW)) return reducePW;
return this.fixPolygonalTopology(reducePW);
}
setPointwise(isPointwise) {
this._isPointwise = isPointwise;
}
createEditor(geomFactory, newPM) {
if (geomFactory.getPrecisionModel() === newPM) return new GeometryEditor();
const newFactory = this.createFactory(geomFactory, newPM);
const geomEdit = new GeometryEditor(newFactory);
return geomEdit;
}
}
class FacetSequence {
constructor() {
FacetSequence.constructor_.apply(this, arguments);
}
static constructor_() {
this._geom = null;
this._pts = null;
this._start = null;
this._end = null;
if (arguments.length === 2) {
const pts = arguments[0],
start = arguments[1];
this._pts = pts;
this._start = start;
this._end = start + 1;
} else if (arguments.length === 3) {
const pts = arguments[0],
start = arguments[1],
end = arguments[2];
this._pts = pts;
this._start = start;
this._end = end;
} else if (arguments.length === 4) {
const geom = arguments[0],
pts = arguments[1],
start = arguments[2],
end = arguments[3];
this._geom = geom;
this._pts = pts;
this._start = start;
this._end = end;
}
}
computeDistanceLineLine(facetSeq, locs) {
let minDistance = Double.MAX_VALUE;
for (let i = this._start; i < this._end - 1; i++) {
const p0 = this._pts.getCoordinate(i);
const p1 = this._pts.getCoordinate(i + 1);
for (let j = facetSeq._start; j < facetSeq._end - 1; j++) {
const q0 = facetSeq._pts.getCoordinate(j);
const q1 = facetSeq._pts.getCoordinate(j + 1);
const dist = Distance.segmentToSegment(p0, p1, q0, q1);
if (dist < minDistance) {
minDistance = dist;
if (locs !== null) this.updateNearestLocationsLineLine(i, p0, p1, facetSeq, j, q0, q1, locs);
if (minDistance <= 0.0) return minDistance;
}
}
}
return minDistance;
}
updateNearestLocationsPointLine(pt, facetSeq, i, q0, q1, locs) {
locs[0] = new GeometryLocation(this._geom, this._start, new Coordinate(pt));
const seg = new LineSegment(q0, q1);
const segClosestPoint = seg.closestPoint(pt);
locs[1] = new GeometryLocation(facetSeq._geom, i, new Coordinate(segClosestPoint));
}
size() {
return this._end - this._start;
}
getCoordinate(index) {
return this._pts.getCoordinate(this._start + index);
}
nearestLocations(facetSeq) {
const isPoint = this.isPoint();
const isPointOther = facetSeq.isPoint();
const locs = new Array(2).fill(null);
if (isPoint && isPointOther) {
const pt = this._pts.getCoordinate(this._start);
const seqPt = facetSeq._pts.getCoordinate(facetSeq._start);
locs[0] = new GeometryLocation(this._geom, this._start, new Coordinate(pt));
locs[1] = new GeometryLocation(facetSeq._geom, facetSeq._start, new Coordinate(seqPt));
} else if (isPoint) {
const pt = this._pts.getCoordinate(this._start);
this.computeDistancePointLine(pt, facetSeq, locs);
} else if (isPointOther) {
const seqPt = facetSeq._pts.getCoordinate(facetSeq._start);
this.computeDistancePointLine(seqPt, this, locs);
const tmp = locs[0];
locs[0] = locs[1];
locs[1] = tmp;
} else {
this.computeDistanceLineLine(facetSeq, locs);
}
return locs;
}
getEnvelope() {
const env = new Envelope();
for (let i = this._start; i < this._end; i++) env.expandToInclude(this._pts.getX(i), this._pts.getY(i));
return env;
}
updateNearestLocationsLineLine(i, p0, p1, facetSeq, j, q0, q1, locs) {
const seg0 = new LineSegment(p0, p1);
const seg1 = new LineSegment(q0, q1);
const closestPt = seg0.closestPoints(seg1);
locs[0] = new GeometryLocation(this._geom, i, new Coordinate(closestPt[0]));
locs[1] = new GeometryLocation(facetSeq._geom, j, new Coordinate(closestPt[1]));
}
toString() {
const buf = new StringBuffer();
buf.append('LINESTRING ( ');
const p = new Coordinate();
for (let i = this._start; i < this._end; i++) {
if (i > this._start) buf.append(', ');
this._pts.getCoordinate(i, p);
buf.append(p.x + ' ' + p.y);
}
buf.append(' )');
return buf.toString();
}
computeDistancePointLine(pt, facetSeq, locs) {
let minDistance = Double.MAX_VALUE;
for (let i = facetSeq._start; i < facetSeq._end - 1; i++) {
const q0 = facetSeq._pts.getCoordinate(i);
const q1 = facetSeq._pts.getCoordinate(i + 1);
const dist = Distance.pointToSegment(pt, q0, q1);
if (dist < minDistance) {
minDistance = dist;
if (locs !== null) this.updateNearestLocationsPointLine(pt, facetSeq, i, q0, q1, locs);
if (minDistance <= 0.0) return minDistance;
}
}
return minDistance;
}
isPoint() {
return this._end - this._start === 1;
}
distance(facetSeq) {
const isPoint = this.isPoint();
const isPointOther = facetSeq.isPoint();
let distance = null;
if (isPoint && isPointOther) {
const pt = this._pts.getCoordinate(this._start);
const seqPt = facetSeq._pts.getCoordinate(facetSeq._start);
distance = pt.distance(seqPt);
} else if (isPoint) {
const pt = this._pts.getCoordinate(this._start);
distance = this.computeDistancePointLine(pt, facetSeq, null);
} else if (isPointOther) {
const seqPt = facetSeq._pts.getCoordinate(facetSeq._start);
distance = this.computeDistancePointLine(seqPt, this, null);
} else {
distance = this.computeDistanceLineLine(facetSeq, null);
}
return distance;
}
}
class FacetSequenceTreeBuilder {
static addFacetSequences(geom, pts, sections) {
let i = 0;
const size = pts.size();
while (i <= size - 1) {
let end = i + FacetSequenceTreeBuilder.FACET_SEQUENCE_SIZE + 1;
if (end >= size - 1) end = size;
const sect = new FacetSequence(geom, pts, i, end);
sections.add(sect);
i = i + FacetSequenceTreeBuilder.FACET_SEQUENCE_SIZE;
}
}
static computeFacetSequences(g) {
const sections = new ArrayList();
g.apply(new class {
get interfaces_() {
return [GeometryComponentFilter];
}
filter(geom) {
let seq = null;
if (geom instanceof LineString) {
seq = geom.getCoordinateSequence();
FacetSequenceTreeBuilder.addFacetSequences(geom, seq, sections);
} else if (geom instanceof Point) {
seq = geom.getCoordinateSequence();
FacetSequenceTreeBuilder.addFacetSequences(geom, seq, sections);
}
}
}());
return sections;
}
static build(g) {
const tree = new STRtree(FacetSequenceTreeBuilder.STR_TREE_NODE_CAPACITY);
const sections = FacetSequenceTreeBuilder.computeFacetSequences(g);
for (let i = sections.iterator(); i.hasNext();) {
const section = i.next();
tree.insert(section.getEnvelope(), section);
}
tree.build();
return tree;
}
}
FacetSequenceTreeBuilder.FACET_SEQUENCE_SIZE = 6;
FacetSequenceTreeBuilder.STR_TREE_NODE_CAPACITY = 4;
class MinimumClearance {
constructor() {
MinimumClearance.constructor_.apply(this, arguments);
}
static constructor_() {
this._inputGeom = null;
this._minClearance = null;
this._minClearancePts = null;
const geom = arguments[0];
this._inputGeom = geom;
}
static getLine(g) {
const rp = new MinimumClearance(g);
return rp.getLine();
}
static getDistance(g) {
const rp = new MinimumClearance(g);
return rp.getDistance();
}
getLine() {
this.compute();
if (this._minClearancePts === null || this._minClearancePts[0] === null) return this._inputGeom.getFactory().createLineString();
return this._inputGeom.getFactory().createLineString(this._minClearancePts);
}
compute() {
if (this._minClearancePts !== null) return null;
this._minClearancePts = new Array(2).fill(null);
this._minClearance = Double.MAX_VALUE;
if (this._inputGeom.isEmpty()) return null;
const geomTree = FacetSequenceTreeBuilder.build(this._inputGeom);
const nearest = geomTree.nearestNeighbour(new MinClearanceDistance());
const mcd = new MinClearanceDistance();
this._minClearance = mcd.distance(nearest[0], nearest[1]);
this._minClearancePts = mcd.getCoordinates();
}
getDistance() {
this.compute();
return this._minClearance;
}
}
class MinClearanceDistance {
constructor() {
MinClearanceDistance.constructor_.apply(this, arguments);
}
static constructor_() {
this._minDist = Double.MAX_VALUE;
this._minPts = new Array(2).fill(null);
}
vertexDistance(fs1, fs2) {
for (let i1 = 0; i1 < fs1.size(); i1++) for (let i2 = 0; i2 < fs2.size(); i2++) {
const p1 = fs1.getCoordinate(i1);
const p2 = fs2.getCoordinate(i2);
if (!p1.equals2D(p2)) {
const d = p1.distance(p2);
if (d < this._minDist) {
this._minDist = d;
this._minPts[0] = p1;
this._minPts[1] = p2;
if (d === 0.0) return d;
}
}
}
return this._minDist;
}
getCoordinates() {
return this._minPts;
}
segmentDistance(fs1, fs2) {
for (let i1 = 0; i1 < fs1.size(); i1++) for (let i2 = 1; i2 < fs2.size(); i2++) {
const p = fs1.getCoordinate(i1);
const seg0 = fs2.getCoordinate(i2 - 1);
const seg1 = fs2.getCoordinate(i2);
if (!(p.equals2D(seg0) || p.equals2D(seg1))) {
const d = Distance.pointToSegment(p, seg0, seg1);
if (d < this._minDist) {
this._minDist = d;
this.updatePts(p, seg0, seg1);
if (d === 0.0) return d;
}
}
}
return this._minDist;
}
distance() {
if (arguments[0] instanceof ItemBoundable && arguments[1] instanceof ItemBoundable) {
const b1 = arguments[0],
b2 = arguments[1];
const fs1 = b1.getItem();
const fs2 = b2.getItem();
this._minDist = Double.MAX_VALUE;
return this.distance(fs1, fs2);
} else if (arguments[0] instanceof FacetSequence && arguments[1] instanceof FacetSequence) {
const fs1 = arguments[0],
fs2 = arguments[1];
this.vertexDistance(fs1, fs2);
if (fs1.size() === 1 && fs2.size() === 1) return this._minDist;
if (this._minDist <= 0.0) return this._minDist;
this.segmentDistance(fs1, fs2);
if (this._minDist <= 0.0) return this._minDist;
this.segmentDistance(fs2, fs1);
return this._minDist;
}
}
updatePts(p, seg0, seg1) {
this._minPts[0] = p;
const seg = new LineSegment(seg0, seg1);
this._minPts[1] = new Coordinate(seg.closestPoint(p));
}
get interfaces_() {
return [ItemDistance];
}
}
MinimumClearance.MinClearanceDistance = MinClearanceDistance;
class SimpleMinimumClearance {
constructor() {
SimpleMinimumClearance.constructor_.apply(this, arguments);
}
static constructor_() {
this._inputGeom = null;
this._minClearance = null;
this._minClearancePts = null;
const geom = arguments[0];
this._inputGeom = geom;
}
static getLine(g) {
const rp = new SimpleMinimumClearance(g);
return rp.getLine();
}
static getDistance(g) {
const rp = new SimpleMinimumClearance(g);
return rp.getDistance();
}
getLine() {
this.compute();
return this._inputGeom.getFactory().createLineString(this._minClearancePts);
}
updateClearance() {
if (arguments.length === 3) {
const candidateValue = arguments[0],
p0 = arguments[1],
p1 = arguments[2];
if (candidateValue < this._minClearance) {
this._minClearance = candidateValue;
this._minClearancePts[0] = new Coordinate(p0);
this._minClearancePts[1] = new Coordinate(p1);
}
} else if (arguments.length === 4) {
const candidateValue = arguments[0],
p = arguments[1],
seg0 = arguments[2],
seg1 = arguments[3];
if (candidateValue < this._minClearance) {
this._minClearance = candidateValue;
this._minClearancePts[0] = new Coordinate(p);
const seg = new LineSegment(seg0, seg1);
this._minClearancePts[1] = new Coordinate(seg.closestPoint(p));
}
}
}
compute() {
if (this._minClearancePts !== null) return null;
this._minClearancePts = new Array(2).fill(null);
this._minClearance = Double.MAX_VALUE;
this._inputGeom.apply(new VertexCoordinateFilter(this));
}
getDistance() {
this.compute();
return this._minClearance;
}
}
class VertexCoordinateFilter {
constructor() {
VertexCoordinateFilter.constructor_.apply(this, arguments);
}
static constructor_() {
this.smc = null;
const smc = arguments[0];
this.smc = smc;
}
filter(coord) {
this.smc._inputGeom.apply(new ComputeMCCoordinateSequenceFilter(this.smc, coord));
}
get interfaces_() {
return [CoordinateFilter];
}
}
class ComputeMCCoordinateSequenceFilter {
constructor() {
ComputeMCCoordinateSequenceFilter.constructor_.apply(this, arguments);
}
static constructor_() {
this.smc = null;
this._queryPt = null;
const smc = arguments[0],
queryPt = arguments[1];
this.smc = smc;
this._queryPt = queryPt;
}
isGeometryChanged() {
return false;
}
checkVertexDistance(vertex) {
const vertexDist = vertex.distance(this._queryPt);
if (vertexDist > 0) this.smc.updateClearance(vertexDist, this._queryPt, vertex);
}
filter(seq, i) {
this.checkVertexDistance(seq.getCoordinate(i));
if (i > 0) this.checkSegmentDistance(seq.getCoordinate(i - 1), seq.getCoordinate(i));
}
checkSegmentDistance(seg0, seg1) {
if (this._queryPt.equals2D(seg0) || this._queryPt.equals2D(seg1)) return null;
const segDist = Distance.pointToSegment(this._queryPt, seg1, seg0);
if (segDist > 0) this.smc.updateClearance(segDist, this._queryPt, seg1, seg0);
}
isDone() {
return false;
}
get interfaces_() {
return [CoordinateSequenceFilter];
}
}
SimpleMinimumClearance.VertexCoordinateFilter = VertexCoordinateFilter;
SimpleMinimumClearance.ComputeMCCoordinateSequenceFilter = ComputeMCCoordinateSequenceFilter;
var precision = /*#__PURE__*/Object.freeze({
__proto__: null,
CommonBits: CommonBits,
CommonBitsOp: CommonBitsOp,
CommonBitsRemover: CommonBitsRemover,
EnhancedPrecisionOp: EnhancedPrecisionOp,
GeometryPrecisionReducer: GeometryPrecisionReducer,
MinimumClearance: MinimumClearance,
SimpleMinimumClearance: SimpleMinimumClearance
});
class DouglasPeuckerLineSimplifier {
constructor() {
DouglasPeuckerLineSimplifier.constructor_.apply(this, arguments);
}
static constructor_() {
this._pts = null;
this._usePt = null;
this._distanceTolerance = null;
this._seg = new LineSegment();
const pts = arguments[0];
this._pts = pts;
}
static simplify(pts, distanceTolerance) {
const simp = new DouglasPeuckerLineSimplifier(pts);
simp.setDistanceTolerance(distanceTolerance);
return simp.simplify();
}
simplifySection(i, j) {
if (i + 1 === j) return null;
this._seg.p0 = this._pts[i];
this._seg.p1 = this._pts[j];
let maxDistance = -1.0;
let maxIndex = i;
for (let k = i + 1; k < j; k++) {
const distance = this._seg.distance(this._pts[k]);
if (distance > maxDistance) {
maxDistance = distance;
maxIndex = k;
}
}
if (maxDistance <= this._distanceTolerance) {
for (let k = i + 1; k < j; k++) this._usePt[k] = false;
} else {
this.simplifySection(i, maxIndex);
this.simplifySection(maxIndex, j);
}
}
setDistanceTolerance(distanceTolerance) {
this._distanceTolerance = distanceTolerance;
}
simplify() {
this._usePt = new Array(this._pts.length).fill(null);
for (let i = 0; i < this._pts.length; i++) this._usePt[i] = true;
this.simplifySection(0, this._pts.length - 1);
const coordList = new CoordinateList();
for (let i = 0; i < this._pts.length; i++) if (this._usePt[i]) coordList.add(new Coordinate(this._pts[i]));
return coordList.toCoordinateArray();
}
}
class DouglasPeuckerSimplifier {
constructor() {
DouglasPeuckerSimplifier.constructor_.apply(this, arguments);
}
static constructor_() {
this._inputGeom = null;
this._distanceTolerance = null;
this._isEnsureValidTopology = true;
const inputGeom = arguments[0];
this._inputGeom = inputGeom;
}
static simplify(geom, distanceTolerance) {
const tss = new DouglasPeuckerSimplifier(geom);
tss.setDistanceTolerance(distanceTolerance);
return tss.getResultGeometry();
}
setEnsureValid(isEnsureValidTopology) {
this._isEnsureValidTopology = isEnsureValidTopology;
}
getResultGeometry() {
if (this._inputGeom.isEmpty()) return this._inputGeom.copy();
return new DPTransformer(this._isEnsureValidTopology, this._distanceTolerance).transform(this._inputGeom);
}
setDistanceTolerance(distanceTolerance) {
if (distanceTolerance < 0.0) throw new IllegalArgumentException('Tolerance must be non-negative');
this._distanceTolerance = distanceTolerance;
}
}
class DPTransformer extends GeometryTransformer {
constructor() {
super();
DPTransformer.constructor_.apply(this, arguments);
}
static constructor_() {
this._isEnsureValidTopology = true;
this._distanceTolerance = null;
const isEnsureValidTopology = arguments[0],
distanceTolerance = arguments[1];
this._isEnsureValidTopology = isEnsureValidTopology;
this._distanceTolerance = distanceTolerance;
}
transformPolygon(geom, parent) {
if (geom.isEmpty()) return null;
const rawGeom = super.transformPolygon.call(this, geom, parent);
if (parent instanceof MultiPolygon) return rawGeom;
return this.createValidArea(rawGeom);
}
createValidArea(rawAreaGeom) {
if (this._isEnsureValidTopology) return rawAreaGeom.buffer(0.0);
return rawAreaGeom;
}
transformCoordinates(coords, parent) {
const inputPts = coords.toCoordinateArray();
let newPts = null;
if (inputPts.length === 0) newPts = new Array(0).fill(null);else newPts = DouglasPeuckerLineSimplifier.simplify(inputPts, this._distanceTolerance);
return this._factory.getCoordinateSequenceFactory().create(newPts);
}
transformMultiPolygon(geom, parent) {
const rawGeom = super.transformMultiPolygon.call(this, geom, parent);
return this.createValidArea(rawGeom);
}
transformLinearRing(geom, parent) {
const removeDegenerateRings = parent instanceof Polygon;
const simpResult = super.transformLinearRing.call(this, geom, parent);
if (removeDegenerateRings && !(simpResult instanceof LinearRing)) return null;
return simpResult;
}
}
DouglasPeuckerSimplifier.DPTransformer = DPTransformer;
class TaggedLineSegment extends LineSegment {
constructor() {
super();
TaggedLineSegment.constructor_.apply(this, arguments);
}
static constructor_() {
this._parent = null;
this._index = null;
if (arguments.length === 2) {
const p0 = arguments[0],
p1 = arguments[1];
TaggedLineSegment.constructor_.call(this, p0, p1, null, -1);
} else if (arguments.length === 4) {
const p0 = arguments[0],
p1 = arguments[1],
parent = arguments[2],
index = arguments[3];
LineSegment.constructor_.call(this, p0, p1);
this._parent = parent;
this._index = index;
}
}
getIndex() {
return this._index;
}
getParent() {
return this._parent;
}
}
class TaggedLineString {
constructor() {
TaggedLineString.constructor_.apply(this, arguments);
}
static constructor_() {
this._parentLine = null;
this._segs = null;
this._resultSegs = new ArrayList();
this._minimumSize = null;
if (arguments.length === 1) {
const parentLine = arguments[0];
TaggedLineString.constructor_.call(this, parentLine, 2);
} else if (arguments.length === 2) {
const parentLine = arguments[0],
minimumSize = arguments[1];
this._parentLine = parentLine;
this._minimumSize = minimumSize;
this.init();
}
}
static extractCoordinates(segs) {
const pts = new Array(segs.size() + 1).fill(null);
let seg = null;
for (let i = 0; i < segs.size(); i++) {
seg = segs.get(i);
pts[i] = seg.p0;
}
pts[pts.length - 1] = seg.p1;
return pts;
}
addToResult(seg) {
this._resultSegs.add(seg);
}
asLineString() {
return this._parentLine.getFactory().createLineString(TaggedLineString.extractCoordinates(this._resultSegs));
}
getResultSize() {
const resultSegsSize = this._resultSegs.size();
return resultSegsSize === 0 ? 0 : resultSegsSize + 1;
}
getParent() {
return this._parentLine;
}
getSegment(i) {
return this._segs[i];
}
getParentCoordinates() {
return this._parentLine.getCoordinates();
}
getMinimumSize() {
return this._minimumSize;
}
asLinearRing() {
return this._parentLine.getFactory().createLinearRing(TaggedLineString.extractCoordinates(this._resultSegs));
}
getSegments() {
return this._segs;
}
init() {
const pts = this._parentLine.getCoordinates();
this._segs = new Array(pts.length - 1).fill(null);
for (let i = 0; i < pts.length - 1; i++) {
const seg = new TaggedLineSegment(pts[i], pts[i + 1], this._parentLine, i);
this._segs[i] = seg;
}
}
getResultCoordinates() {
return TaggedLineString.extractCoordinates(this._resultSegs);
}
}
class LineSegmentIndex {
constructor() {
LineSegmentIndex.constructor_.apply(this, arguments);
}
static constructor_() {
this._index = new Quadtree();
}
remove(seg) {
this._index.remove(new Envelope(seg.p0, seg.p1), seg);
}
add() {
if (arguments[0] instanceof TaggedLineString) {
const line = arguments[0];
const segs = line.getSegments();
for (let i = 0; i < segs.length; i++) {
const seg = segs[i];
this.add(seg);
}
} else if (arguments[0] instanceof LineSegment) {
const seg = arguments[0];
this._index.insert(new Envelope(seg.p0, seg.p1), seg);
}
}
query(querySeg) {
const env = new Envelope(querySeg.p0, querySeg.p1);
const visitor = new LineSegmentVisitor(querySeg);
this._index.query(env, visitor);
const itemsFound = visitor.getItems();
return itemsFound;
}
}
class LineSegmentVisitor {
constructor() {
LineSegmentVisitor.constructor_.apply(this, arguments);
}
static constructor_() {
this._querySeg = null;
this._items = new ArrayList();
const querySeg = arguments[0];
this._querySeg = querySeg;
}
visitItem(item) {
const seg = item;
if (Envelope.intersects(seg.p0, seg.p1, this._querySeg.p0, this._querySeg.p1)) this._items.add(item);
}
getItems() {
return this._items;
}
get interfaces_() {
return [ItemVisitor];
}
}
class TaggedLineStringSimplifier {
constructor() {
TaggedLineStringSimplifier.constructor_.apply(this, arguments);
}
static constructor_() {
this._li = new RobustLineIntersector();
this._inputIndex = new LineSegmentIndex();
this._outputIndex = new LineSegmentIndex();
this._line = null;
this._linePts = null;
this._distanceTolerance = 0.0;
const inputIndex = arguments[0],
outputIndex = arguments[1];
this._inputIndex = inputIndex;
this._outputIndex = outputIndex;
}
static isInLineSection(line, sectionIndex, seg) {
if (seg.getParent() !== line.getParent()) return false;
const segIndex = seg.getIndex();
if (segIndex >= sectionIndex[0] && segIndex < sectionIndex[1]) return true;
return false;
}
flatten(start, end) {
const p0 = this._linePts[start];
const p1 = this._linePts[end];
const newSeg = new LineSegment(p0, p1);
this.remove(this._line, start, end);
this._outputIndex.add(newSeg);
return newSeg;
}
hasBadIntersection(parentLine, sectionIndex, candidateSeg) {
if (this.hasBadOutputIntersection(candidateSeg)) return true;
if (this.hasBadInputIntersection(parentLine, sectionIndex, candidateSeg)) return true;
return false;
}
setDistanceTolerance(distanceTolerance) {
this._distanceTolerance = distanceTolerance;
}
simplifySection(i, j, depth) {
depth += 1;
const sectionIndex = new Array(2).fill(null);
if (i + 1 === j) {
const newSeg = this._line.getSegment(i);
this._line.addToResult(newSeg);
return null;
}
let isValidToSimplify = true;
if (this._line.getResultSize() < this._line.getMinimumSize()) {
const worstCaseSize = depth + 1;
if (worstCaseSize < this._line.getMinimumSize()) isValidToSimplify = false;
}
const distance = new Array(1).fill(null);
const furthestPtIndex = this.findFurthestPoint(this._linePts, i, j, distance);
if (distance[0] > this._distanceTolerance) isValidToSimplify = false;
const candidateSeg = new LineSegment();
candidateSeg.p0 = this._linePts[i];
candidateSeg.p1 = this._linePts[j];
sectionIndex[0] = i;
sectionIndex[1] = j;
if (this.hasBadIntersection(this._line, sectionIndex, candidateSeg)) isValidToSimplify = false;
if (isValidToSimplify) {
const newSeg = this.flatten(i, j);
this._line.addToResult(newSeg);
return null;
}
this.simplifySection(i, furthestPtIndex, depth);
this.simplifySection(furthestPtIndex, j, depth);
}
hasBadOutputIntersection(candidateSeg) {
const querySegs = this._outputIndex.query(candidateSeg);
for (let i = querySegs.iterator(); i.hasNext();) {
const querySeg = i.next();
if (this.hasInteriorIntersection(querySeg, candidateSeg)) return true;
}
return false;
}
findFurthestPoint(pts, i, j, maxDistance) {
const seg = new LineSegment();
seg.p0 = pts[i];
seg.p1 = pts[j];
let maxDist = -1.0;
let maxIndex = i;
for (let k = i + 1; k < j; k++) {
const midPt = pts[k];
const distance = seg.distance(midPt);
if (distance > maxDist) {
maxDist = distance;
maxIndex = k;
}
}
maxDistance[0] = maxDist;
return maxIndex;
}
simplify(line) {
this._line = line;
this._linePts = line.getParentCoordinates();
this.simplifySection(0, this._linePts.length - 1, 0);
}
remove(line, start, end) {
for (let i = start; i < end; i++) {
const seg = line.getSegment(i);
this._inputIndex.remove(seg);
}
}
hasInteriorIntersection(seg0, seg1) {
this._li.computeIntersection(seg0.p0, seg0.p1, seg1.p0, seg1.p1);
return this._li.isInteriorIntersection();
}
hasBadInputIntersection(parentLine, sectionIndex, candidateSeg) {
const querySegs = this._inputIndex.query(candidateSeg);
for (let i = querySegs.iterator(); i.hasNext();) {
const querySeg = i.next();
if (this.hasInteriorIntersection(querySeg, candidateSeg)) {
if (TaggedLineStringSimplifier.isInLineSection(parentLine, sectionIndex, querySeg)) continue;
return true;
}
}
return false;
}
}
class TaggedLinesSimplifier {
constructor() {
TaggedLinesSimplifier.constructor_.apply(this, arguments);
}
static constructor_() {
this._inputIndex = new LineSegmentIndex();
this._outputIndex = new LineSegmentIndex();
this._distanceTolerance = 0.0;
}
setDistanceTolerance(distanceTolerance) {
this._distanceTolerance = distanceTolerance;
}
simplify(taggedLines) {
for (let i = taggedLines.iterator(); i.hasNext();) this._inputIndex.add(i.next());
for (let i = taggedLines.iterator(); i.hasNext();) {
const tlss = new TaggedLineStringSimplifier(this._inputIndex, this._outputIndex);
tlss.setDistanceTolerance(this._distanceTolerance);
tlss.simplify(i.next());
}
}
}
class TopologyPreservingSimplifier {
constructor() {
TopologyPreservingSimplifier.constructor_.apply(this, arguments);
}
static constructor_() {
this._inputGeom = null;
this._lineSimplifier = new TaggedLinesSimplifier();
this._linestringMap = null;
const inputGeom = arguments[0];
this._inputGeom = inputGeom;
}
static simplify(geom, distanceTolerance) {
const tss = new TopologyPreservingSimplifier(geom);
tss.setDistanceTolerance(distanceTolerance);
return tss.getResultGeometry();
}
getResultGeometry() {
if (this._inputGeom.isEmpty()) return this._inputGeom.copy();
this._linestringMap = new HashMap();
this._inputGeom.apply(new LineStringMapBuilderFilter(this));
this._lineSimplifier.simplify(this._linestringMap.values());
const result = new LineStringTransformer(this._linestringMap).transform(this._inputGeom);
return result;
}
setDistanceTolerance(distanceTolerance) {
if (distanceTolerance < 0.0) throw new IllegalArgumentException('Tolerance must be non-negative');
this._lineSimplifier.setDistanceTolerance(distanceTolerance);
}
}
class LineStringTransformer extends GeometryTransformer {
constructor() {
super();
LineStringTransformer.constructor_.apply(this, arguments);
}
static constructor_() {
this._linestringMap = null;
const linestringMap = arguments[0];
this._linestringMap = linestringMap;
}
transformCoordinates(coords, parent) {
if (coords.size() === 0) return null;
if (parent instanceof LineString) {
const taggedLine = this._linestringMap.get(parent);
return this.createCoordinateSequence(taggedLine.getResultCoordinates());
}
return super.transformCoordinates.call(this, coords, parent);
}
}
class LineStringMapBuilderFilter {
constructor() {
LineStringMapBuilderFilter.constructor_.apply(this, arguments);
}
static constructor_() {
this.tps = null;
const tps = arguments[0];
this.tps = tps;
}
filter(geom) {
if (geom instanceof LineString) {
const line = geom;
if (line.isEmpty()) return null;
const minSize = line.isClosed() ? 4 : 2;
const taggedLine = new TaggedLineString(line, minSize);
this.tps._linestringMap.put(line, taggedLine);
}
}
get interfaces_() {
return [GeometryComponentFilter];
}
}
TopologyPreservingSimplifier.LineStringTransformer = LineStringTransformer;
TopologyPreservingSimplifier.LineStringMapBuilderFilter = LineStringMapBuilderFilter;
class VWLineSimplifier {
constructor() {
VWLineSimplifier.constructor_.apply(this, arguments);
}
static constructor_() {
this._pts = null;
this._tolerance = null;
const pts = arguments[0],
distanceTolerance = arguments[1];
this._pts = pts;
this._tolerance = distanceTolerance * distanceTolerance;
}
static simplify(pts, distanceTolerance) {
const simp = new VWLineSimplifier(pts, distanceTolerance);
return simp.simplify();
}
simplifyVertex(vwLine) {
let curr = vwLine;
let minArea = curr.getArea();
let minVertex = null;
while (curr !== null) {
const area = curr.getArea();
if (area < minArea) {
minArea = area;
minVertex = curr;
}
curr = curr._next;
}
if (minVertex !== null && minArea < this._tolerance) minVertex.remove();
if (!vwLine.isLive()) return -1;
return minArea;
}
simplify() {
const vwLine = VWVertex.buildLine(this._pts);
let minArea = this._tolerance;
do minArea = this.simplifyVertex(vwLine); while (minArea < this._tolerance);
const simp = vwLine.getCoordinates();
if (simp.length < 2) return [simp[0], new Coordinate(simp[0])];
return simp;
}
}
class VWVertex {
constructor() {
VWVertex.constructor_.apply(this, arguments);
}
static constructor_() {
this._pt = null;
this._prev = null;
this._next = null;
this._area = VWVertex.MAX_AREA;
this._isLive = true;
const pt = arguments[0];
this._pt = pt;
}
static buildLine(pts) {
let first = null;
let prev = null;
for (let i = 0; i < pts.length; i++) {
const v = new VWVertex(pts[i]);
if (first === null) first = v;
v.setPrev(prev);
if (prev !== null) {
prev.setNext(v);
prev.updateArea();
}
prev = v;
}
return first;
}
getCoordinates() {
const coords = new CoordinateList();
let curr = this;
do {
coords.add(curr._pt, false);
curr = curr._next;
} while (curr !== null);
return coords.toCoordinateArray();
}
getArea() {
return this._area;
}
updateArea() {
if (this._prev === null || this._next === null) {
this._area = VWVertex.MAX_AREA;
return null;
}
this._area = Math.abs(Triangle.area(this._prev._pt, this._pt, this._next._pt));
}
remove() {
const tmpPrev = this._prev;
const tmpNext = this._next;
let result = null;
if (this._prev !== null) {
this._prev.setNext(tmpNext);
this._prev.updateArea();
result = this._prev;
}
if (this._next !== null) {
this._next.setPrev(tmpPrev);
this._next.updateArea();
if (result === null) result = this._next;
}
this._isLive = false;
return result;
}
isLive() {
return this._isLive;
}
setPrev(prev) {
this._prev = prev;
}
setNext(next) {
this._next = next;
}
}
VWVertex.MAX_AREA = Double.MAX_VALUE;
VWLineSimplifier.VWVertex = VWVertex;
class VWSimplifier {
constructor() {
VWSimplifier.constructor_.apply(this, arguments);
}
static constructor_() {
this._inputGeom = null;
this._distanceTolerance = null;
this._isEnsureValidTopology = true;
const inputGeom = arguments[0];
this._inputGeom = inputGeom;
}
static simplify(geom, distanceTolerance) {
const simp = new VWSimplifier(geom);
simp.setDistanceTolerance(distanceTolerance);
return simp.getResultGeometry();
}
setEnsureValid(isEnsureValidTopology) {
this._isEnsureValidTopology = isEnsureValidTopology;
}
getResultGeometry() {
if (this._inputGeom.isEmpty()) return this._inputGeom.copy();
return new VWTransformer(this._isEnsureValidTopology, this._distanceTolerance).transform(this._inputGeom);
}
setDistanceTolerance(distanceTolerance) {
if (distanceTolerance < 0.0) throw new IllegalArgumentException('Tolerance must be non-negative');
this._distanceTolerance = distanceTolerance;
}
}
class VWTransformer extends GeometryTransformer {
constructor() {
super();
VWTransformer.constructor_.apply(this, arguments);
}
static constructor_() {
this._isEnsureValidTopology = true;
this._distanceTolerance = null;
const isEnsureValidTopology = arguments[0],
distanceTolerance = arguments[1];
this._isEnsureValidTopology = isEnsureValidTopology;
this._distanceTolerance = distanceTolerance;
}
transformPolygon(geom, parent) {
if (geom.isEmpty()) return null;
const rawGeom = super.transformPolygon.call(this, geom, parent);
if (parent instanceof MultiPolygon) return rawGeom;
return this.createValidArea(rawGeom);
}
createValidArea(rawAreaGeom) {
if (this._isEnsureValidTopology) return rawAreaGeom.buffer(0.0);
return rawAreaGeom;
}
transformCoordinates(coords, parent) {
const inputPts = coords.toCoordinateArray();
let newPts = null;
if (inputPts.length === 0) newPts = new Array(0).fill(null);else newPts = VWLineSimplifier.simplify(inputPts, this._distanceTolerance);
return this._factory.getCoordinateSequenceFactory().create(newPts);
}
transformMultiPolygon(geom, parent) {
const rawGeom = super.transformMultiPolygon.call(this, geom, parent);
return this.createValidArea(rawGeom);
}
transformLinearRing(geom, parent) {
const removeDegenerateRings = parent instanceof Polygon;
const simpResult = super.transformLinearRing.call(this, geom, parent);
if (removeDegenerateRings && !(simpResult instanceof LinearRing)) return null;
return simpResult;
}
}
VWSimplifier.VWTransformer = VWTransformer;
var simplify = /*#__PURE__*/Object.freeze({
__proto__: null,
DouglasPeuckerSimplifier: DouglasPeuckerSimplifier,
TopologyPreservingSimplifier: TopologyPreservingSimplifier,
VWSimplifier: VWSimplifier
});
class SplitSegment {
constructor() {
SplitSegment.constructor_.apply(this, arguments);
}
static constructor_() {
this._seg = null;
this._segLen = null;
this._splitPt = null;
this._minimumLen = 0.0;
const seg = arguments[0];
this._seg = seg;
this._segLen = seg.getLength();
}
static pointAlongReverse(seg, segmentLengthFraction) {
const coord = new Coordinate();
coord.x = seg.p1.x - segmentLengthFraction * (seg.p1.x - seg.p0.x);
coord.y = seg.p1.y - segmentLengthFraction * (seg.p1.y - seg.p0.y);
return coord;
}
splitAt() {
if (arguments.length === 1) {
const pt = arguments[0];
const minFrac = this._minimumLen / this._segLen;
if (pt.distance(this._seg.p0) < this._minimumLen) {
this._splitPt = this._seg.pointAlong(minFrac);
return null;
}
if (pt.distance(this._seg.p1) < this._minimumLen) {
this._splitPt = SplitSegment.pointAlongReverse(this._seg, minFrac);
return null;
}
this._splitPt = pt;
} else if (arguments.length === 2) {
const length = arguments[0],
endPt = arguments[1];
const actualLen = this.getConstrainedLength(length);
const frac = actualLen / this._segLen;
if (endPt.equals2D(this._seg.p0)) this._splitPt = this._seg.pointAlong(frac);else this._splitPt = SplitSegment.pointAlongReverse(this._seg, frac);
}
}
setMinimumLength(minLen) {
this._minimumLen = minLen;
}
getConstrainedLength(len) {
if (len < this._minimumLen) return this._minimumLen;
return len;
}
getSplitPoint() {
return this._splitPt;
}
}
class ConstraintSplitPointFinder {
findSplitPoint(seg, encroachPt) {}
}
class NonEncroachingSplitPointFinder {
static projectedSplitPoint(seg, encroachPt) {
const lineSeg = seg.getLineSegment();
const projPt = lineSeg.project(encroachPt);
return projPt;
}
findSplitPoint(seg, encroachPt) {
const lineSeg = seg.getLineSegment();
const segLen = lineSeg.getLength();
const midPtLen = segLen / 2;
const splitSeg = new SplitSegment(lineSeg);
const projPt = NonEncroachingSplitPointFinder.projectedSplitPoint(seg, encroachPt);
const nonEncroachDiam = projPt.distance(encroachPt) * 2 * 0.8;
let maxSplitLen = nonEncroachDiam;
if (maxSplitLen > midPtLen) maxSplitLen = midPtLen;
splitSeg.setMinimumLength(maxSplitLen);
splitSeg.splitAt(projPt);
return splitSeg.getSplitPoint();
}
get interfaces_() {
return [ConstraintSplitPointFinder];
}
}
class TrianglePredicate {
static triArea(a, b, c) {
return (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x);
}
static isInCircleDDNormalized(a, b, c, p) {
const adx = DD.valueOf(a.x).selfSubtract(p.x);
const ady = DD.valueOf(a.y).selfSubtract(p.y);
const bdx = DD.valueOf(b.x).selfSubtract(p.x);
const bdy = DD.valueOf(b.y).selfSubtract(p.y);
const cdx = DD.valueOf(c.x).selfSubtract(p.x);
const cdy = DD.valueOf(c.y).selfSubtract(p.y);
const abdet = adx.multiply(bdy).selfSubtract(bdx.multiply(ady));
const bcdet = bdx.multiply(cdy).selfSubtract(cdx.multiply(bdy));
const cadet = cdx.multiply(ady).selfSubtract(adx.multiply(cdy));
const alift = adx.multiply(adx).selfAdd(ady.multiply(ady));
const blift = bdx.multiply(bdx).selfAdd(bdy.multiply(bdy));
const clift = cdx.multiply(cdx).selfAdd(cdy.multiply(cdy));
const sum = alift.selfMultiply(bcdet).selfAdd(blift.selfMultiply(cadet)).selfAdd(clift.selfMultiply(abdet));
const isInCircle = sum.doubleValue() > 0;
return isInCircle;
}
static checkRobustInCircle(a, b, c, p) {
const nonRobustInCircle = TrianglePredicate.isInCircleNonRobust(a, b, c, p);
const isInCircleDD = TrianglePredicate.isInCircleDDSlow(a, b, c, p);
const isInCircleCC = TrianglePredicate.isInCircleCC(a, b, c, p);
const circumCentre = Triangle.circumcentre(a, b, c);
System.out.println('p radius diff a = ' + Math.abs(p.distance(circumCentre) - a.distance(circumCentre)) / a.distance(circumCentre));
if (nonRobustInCircle !== isInCircleDD || nonRobustInCircle !== isInCircleCC) {
System.out.println('inCircle robustness failure (double result = ' + nonRobustInCircle + ', DD result = ' + isInCircleDD + ', CC result = ' + isInCircleCC + ')');
System.out.println(WKTWriter.toLineString(new CoordinateArraySequence([a, b, c, p])));
System.out.println('Circumcentre = ' + WKTWriter.toPoint(circumCentre) + ' radius = ' + a.distance(circumCentre));
System.out.println('p radius diff a = ' + Math.abs(p.distance(circumCentre) / a.distance(circumCentre) - 1));
System.out.println('p radius diff b = ' + Math.abs(p.distance(circumCentre) / b.distance(circumCentre) - 1));
System.out.println('p radius diff c = ' + Math.abs(p.distance(circumCentre) / c.distance(circumCentre) - 1));
System.out.println();
}
}
static isInCircleDDFast(a, b, c, p) {
const aTerm = DD.sqr(a.x).selfAdd(DD.sqr(a.y)).selfMultiply(TrianglePredicate.triAreaDDFast(b, c, p));
const bTerm = DD.sqr(b.x).selfAdd(DD.sqr(b.y)).selfMultiply(TrianglePredicate.triAreaDDFast(a, c, p));
const cTerm = DD.sqr(c.x).selfAdd(DD.sqr(c.y)).selfMultiply(TrianglePredicate.triAreaDDFast(a, b, p));
const pTerm = DD.sqr(p.x).selfAdd(DD.sqr(p.y)).selfMultiply(TrianglePredicate.triAreaDDFast(a, b, c));
const sum = aTerm.selfSubtract(bTerm).selfAdd(cTerm).selfSubtract(pTerm);
const isInCircle = sum.doubleValue() > 0;
return isInCircle;
}
static isInCircleCC(a, b, c, p) {
const cc = Triangle.circumcentre(a, b, c);
const ccRadius = a.distance(cc);
const pRadiusDiff = p.distance(cc) - ccRadius;
return pRadiusDiff <= 0;
}
static isInCircleNormalized(a, b, c, p) {
const adx = a.x - p.x;
const ady = a.y - p.y;
const bdx = b.x - p.x;
const bdy = b.y - p.y;
const cdx = c.x - p.x;
const cdy = c.y - p.y;
const abdet = adx * bdy - bdx * ady;
const bcdet = bdx * cdy - cdx * bdy;
const cadet = cdx * ady - adx * cdy;
const alift = adx * adx + ady * ady;
const blift = bdx * bdx + bdy * bdy;
const clift = cdx * cdx + cdy * cdy;
const disc = alift * bcdet + blift * cadet + clift * abdet;
return disc > 0;
}
static isInCircleDDSlow(a, b, c, p) {
const px = DD.valueOf(p.x);
const py = DD.valueOf(p.y);
const ax = DD.valueOf(a.x);
const ay = DD.valueOf(a.y);
const bx = DD.valueOf(b.x);
const by = DD.valueOf(b.y);
const cx = DD.valueOf(c.x);
const cy = DD.valueOf(c.y);
const aTerm = ax.multiply(ax).add(ay.multiply(ay)).multiply(TrianglePredicate.triAreaDDSlow(bx, by, cx, cy, px, py));
const bTerm = bx.multiply(bx).add(by.multiply(by)).multiply(TrianglePredicate.triAreaDDSlow(ax, ay, cx, cy, px, py));
const cTerm = cx.multiply(cx).add(cy.multiply(cy)).multiply(TrianglePredicate.triAreaDDSlow(ax, ay, bx, by, px, py));
const pTerm = px.multiply(px).add(py.multiply(py)).multiply(TrianglePredicate.triAreaDDSlow(ax, ay, bx, by, cx, cy));
const sum = aTerm.subtract(bTerm).add(cTerm).subtract(pTerm);
const isInCircle = sum.doubleValue() > 0;
return isInCircle;
}
static isInCircleNonRobust(a, b, c, p) {
const isInCircle = (a.x * a.x + a.y * a.y) * TrianglePredicate.triArea(b, c, p) - (b.x * b.x + b.y * b.y) * TrianglePredicate.triArea(a, c, p) + (c.x * c.x + c.y * c.y) * TrianglePredicate.triArea(a, b, p) - (p.x * p.x + p.y * p.y) * TrianglePredicate.triArea(a, b, c) > 0;
return isInCircle;
}
static isInCircleRobust(a, b, c, p) {
return TrianglePredicate.isInCircleNormalized(a, b, c, p);
}
static triAreaDDSlow(ax, ay, bx, by, cx, cy) {
return bx.subtract(ax).multiply(cy.subtract(ay)).subtract(by.subtract(ay).multiply(cx.subtract(ax)));
}
static triAreaDDFast(a, b, c) {
const t1 = DD.valueOf(b.x).selfSubtract(a.x).selfMultiply(DD.valueOf(c.y).selfSubtract(a.y));
const t2 = DD.valueOf(b.y).selfSubtract(a.y).selfMultiply(DD.valueOf(c.x).selfSubtract(a.x));
return t1.selfSubtract(t2);
}
}
class Vertex {
constructor() {
Vertex.constructor_.apply(this, arguments);
}
static constructor_() {
this._p = null;
if (arguments.length === 1) {
const _p = arguments[0];
this._p = new Coordinate(_p);
} else if (arguments.length === 2) {
const _x = arguments[0],
_y = arguments[1];
this._p = new Coordinate(_x, _y);
} else if (arguments.length === 3) {
const _x = arguments[0],
_y = arguments[1],
_z = arguments[2];
this._p = new Coordinate(_x, _y, _z);
}
}
static interpolateZ() {
if (arguments.length === 3) {
const p = arguments[0],
p0 = arguments[1],
p1 = arguments[2];
const segLen = p0.distance(p1);
const ptLen = p.distance(p0);
const dz = p1.getZ() - p0.getZ();
const pz = p0.getZ() + dz * (ptLen / segLen);
return pz;
} else if (arguments.length === 4) {
const p = arguments[0],
v0 = arguments[1],
v1 = arguments[2],
v2 = arguments[3];
const x0 = v0.x;
const y0 = v0.y;
const a = v1.x - x0;
const b = v2.x - x0;
const c = v1.y - y0;
const d = v2.y - y0;
const det = a * d - b * c;
const dx = p.x - x0;
const dy = p.y - y0;
const t = (d * dx - b * dy) / det;
const u = (-c * dx + a * dy) / det;
const z = v0.getZ() + t * (v1.getZ() - v0.getZ()) + u * (v2.getZ() - v0.getZ());
return z;
}
}
circleCenter(b, c) {
const a = new Vertex(this.getX(), this.getY());
const cab = this.bisector(a, b);
const cbc = this.bisector(b, c);
const hcc = new HCoordinate(cab, cbc);
let cc = null;
try {
cc = new Vertex(hcc.getX(), hcc.getY());
} catch (nre) {
if (nre instanceof NotRepresentableException) {
System.err.println('a: ' + a + ' b: ' + b + ' c: ' + c);
System.err.println(nre);
} else {
throw nre;
}
} finally {}
return cc;
}
dot(v) {
return this._p.x * v.getX() + this._p.y * v.getY();
}
magn() {
return Math.sqrt(this._p.x * this._p.x + this._p.y * this._p.y);
}
getZ() {
return this._p.getZ();
}
bisector(a, b) {
const dx = b.getX() - a.getX();
const dy = b.getY() - a.getY();
const l1 = new HCoordinate(a.getX() + dx / 2.0, a.getY() + dy / 2.0, 1.0);
const l2 = new HCoordinate(a.getX() - dy + dx / 2.0, a.getY() + dx + dy / 2.0, 1.0);
return new HCoordinate(l1, l2);
}
equals() {
if (arguments.length === 1) {
const _x = arguments[0];
if (this._p.x === _x.getX() && this._p.y === _x.getY()) return true;else return false;
} else if (arguments.length === 2) {
const _x = arguments[0],
tolerance = arguments[1];
if (this._p.distance(_x.getCoordinate()) < tolerance) return true;else return false;
}
}
getCoordinate() {
return this._p;
}
isInCircle(a, b, c) {
return TrianglePredicate.isInCircleRobust(a._p, b._p, c._p, this._p);
}
interpolateZValue(v0, v1, v2) {
const x0 = v0.getX();
const y0 = v0.getY();
const a = v1.getX() - x0;
const b = v2.getX() - x0;
const c = v1.getY() - y0;
const d = v2.getY() - y0;
const det = a * d - b * c;
const dx = this.getX() - x0;
const dy = this.getY() - y0;
const t = (d * dx - b * dy) / det;
const u = (-c * dx + a * dy) / det;
const z = v0.getZ() + t * (v1.getZ() - v0.getZ()) + u * (v2.getZ() - v0.getZ());
return z;
}
midPoint(a) {
const xm = (this._p.x + a.getX()) / 2.0;
const ym = (this._p.y + a.getY()) / 2.0;
const zm = (this._p.getZ() + a.getZ()) / 2.0;
return new Vertex(xm, ym, zm);
}
rightOf(e) {
return this.isCCW(e.dest(), e.orig());
}
isCCW(b, c) {
return (b._p.x - this._p.x) * (c._p.y - this._p.y) - (b._p.y - this._p.y) * (c._p.x - this._p.x) > 0;
}
getX() {
return this._p.x;
}
crossProduct(v) {
return this._p.x * v.getY() - this._p.y * v.getX();
}
setZ(_z) {
this._p.setZ(_z);
}
times(c) {
return new Vertex(c * this._p.x, c * this._p.y);
}
cross() {
return new Vertex(this._p.y, -this._p.x);
}
leftOf(e) {
return this.isCCW(e.orig(), e.dest());
}
toString() {
return 'POINT (' + this._p.x + ' ' + this._p.y + ')';
}
sub(v) {
return new Vertex(this._p.x - v.getX(), this._p.y - v.getY());
}
getY() {
return this._p.y;
}
classify(p0, p1) {
const p2 = this;
const a = p1.sub(p0);
const b = p2.sub(p0);
const sa = a.crossProduct(b);
if (sa > 0.0) return Vertex.LEFT;
if (sa < 0.0) return Vertex.RIGHT;
if (a.getX() * b.getX() < 0.0 || a.getY() * b.getY() < 0.0) return Vertex.BEHIND;
if (a.magn() < b.magn()) return Vertex.BEYOND;
if (p0.equals(p2)) return Vertex.ORIGIN;
if (p1.equals(p2)) return Vertex.DESTINATION;
return Vertex.BETWEEN;
}
sum(v) {
return new Vertex(this._p.x + v.getX(), this._p.y + v.getY());
}
distance(v1, v2) {
return Math.sqrt(Math.pow(v2.getX() - v1.getX(), 2.0) + Math.pow(v2.getY() - v1.getY(), 2.0));
}
circumRadiusRatio(b, c) {
const x = this.circleCenter(b, c);
const radius = this.distance(x, b);
let edgeLength = this.distance(this, b);
let el = this.distance(b, c);
if (el < edgeLength) edgeLength = el;
el = this.distance(c, this);
if (el < edgeLength) edgeLength = el;
return radius / edgeLength;
}
}
Vertex.LEFT = 0;
Vertex.RIGHT = 1;
Vertex.BEYOND = 2;
Vertex.BEHIND = 3;
Vertex.BETWEEN = 4;
Vertex.ORIGIN = 5;
Vertex.DESTINATION = 6;
class ConstraintVertex extends Vertex {
constructor() {
super();
ConstraintVertex.constructor_.apply(this, arguments);
}
static constructor_() {
this._isOnConstraint = null;
this._constraint = null;
const p = arguments[0];
Vertex.constructor_.call(this, p);
}
getConstraint() {
return this._constraint;
}
setOnConstraint(isOnConstraint) {
this._isOnConstraint = isOnConstraint;
}
merge(other) {
if (other._isOnConstraint) {
this._isOnConstraint = true;
this._constraint = other._constraint;
}
}
isOnConstraint() {
return this._isOnConstraint;
}
setConstraint(constraint) {
this._isOnConstraint = true;
this._constraint = constraint;
}
}
class QuadEdge {
constructor() {
QuadEdge.constructor_.apply(this, arguments);
}
static constructor_() {
this._rot = null;
this._vertex = null;
this._next = null;
this._data = null;
}
static makeEdge(o, d) {
const q0 = new QuadEdge();
const q1 = new QuadEdge();
const q2 = new QuadEdge();
const q3 = new QuadEdge();
q0._rot = q1;
q1._rot = q2;
q2._rot = q3;
q3._rot = q0;
q0.setNext(q0);
q1.setNext(q3);
q2.setNext(q2);
q3.setNext(q1);
const base = q0;
base.setOrig(o);
base.setDest(d);
return base;
}
static swap(e) {
const a = e.oPrev();
const b = e.sym().oPrev();
QuadEdge.splice(e, a);
QuadEdge.splice(e.sym(), b);
QuadEdge.splice(e, a.lNext());
QuadEdge.splice(e.sym(), b.lNext());
e.setOrig(a.dest());
e.setDest(b.dest());
}
static splice(a, b) {
const alpha = a.oNext().rot();
const beta = b.oNext().rot();
const t1 = b.oNext();
const t2 = a.oNext();
const t3 = beta.oNext();
const t4 = alpha.oNext();
a.setNext(t1);
b.setNext(t2);
alpha.setNext(t3);
beta.setNext(t4);
}
static connect(a, b) {
const e = QuadEdge.makeEdge(a.dest(), b.orig());
QuadEdge.splice(e, a.lNext());
QuadEdge.splice(e.sym(), b);
return e;
}
equalsNonOriented(qe) {
if (this.equalsOriented(qe)) return true;
if (this.equalsOriented(qe.sym())) return true;
return false;
}
toLineSegment() {
return new LineSegment(this._vertex.getCoordinate(), this.dest().getCoordinate());
}
dest() {
return this.sym().orig();
}
oNext() {
return this._next;
}
equalsOriented(qe) {
if (this.orig().getCoordinate().equals2D(qe.orig().getCoordinate()) && this.dest().getCoordinate().equals2D(qe.dest().getCoordinate())) return true;
return false;
}
dNext() {
return this.sym().oNext().sym();
}
lPrev() {
return this._next.sym();
}
rPrev() {
return this.sym().oNext();
}
rot() {
return this._rot;
}
oPrev() {
return this._rot._next._rot;
}
sym() {
return this._rot._rot;
}
setOrig(o) {
this._vertex = o;
}
lNext() {
return this.invRot().oNext().rot();
}
getLength() {
return this.orig().getCoordinate().distance(this.dest().getCoordinate());
}
invRot() {
return this._rot.sym();
}
setDest(d) {
this.sym().setOrig(d);
}
setData(data) {
this._data = data;
}
getData() {
return this._data;
}
delete() {
this._rot = null;
}
orig() {
return this._vertex;
}
rNext() {
return this._rot._next.invRot();
}
toString() {
const p0 = this._vertex.getCoordinate();
const p1 = this.dest().getCoordinate();
return WKTWriter.toLineString(p0, p1);
}
isLive() {
return this._rot !== null;
}
getPrimary() {
if (this.orig().getCoordinate().compareTo(this.dest().getCoordinate()) <= 0) return this;else return this.sym();
}
dPrev() {
return this.invRot().oNext().invRot();
}
setNext(next) {
this._next = next;
}
}
class IncrementalDelaunayTriangulator {
constructor() {
IncrementalDelaunayTriangulator.constructor_.apply(this, arguments);
}
static constructor_() {
this._subdiv = null;
this._isUsingTolerance = false;
const subdiv = arguments[0];
this._subdiv = subdiv;
this._isUsingTolerance = subdiv.getTolerance() > 0.0;
}
insertSite(v) {
let e = this._subdiv.locate(v);
if (this._subdiv.isVertexOfEdge(e, v)) {
return e;
} else if (this._subdiv.isOnEdge(e, v.getCoordinate())) {
e = e.oPrev();
this._subdiv.delete(e.oNext());
}
let base = this._subdiv.makeEdge(e.orig(), v);
QuadEdge.splice(base, e);
const startEdge = base;
do {
base = this._subdiv.connect(e, base.sym());
e = base.oPrev();
} while (e.lNext() !== startEdge);
do {
const t = e.oPrev();
if (t.dest().rightOf(e) && v.isInCircle(e.orig(), t.dest(), e.dest())) {
QuadEdge.swap(e);
e = e.oPrev();
} else if (e.oNext() === startEdge) {
return base;
} else {
e = e.oNext().lPrev();
}
} while (true);
}
insertSites(vertices) {
for (let i = vertices.iterator(); i.hasNext();) {
const v = i.next();
this.insertSite(v);
}
}
}
class QuadEdgeLocator {
locate(v) {}
}
class LastFoundQuadEdgeLocator {
constructor() {
LastFoundQuadEdgeLocator.constructor_.apply(this, arguments);
}
static constructor_() {
this._subdiv = null;
this._lastEdge = null;
const subdiv = arguments[0];
this._subdiv = subdiv;
this.init();
}
init() {
this._lastEdge = this.findEdge();
}
locate(v) {
if (!this._lastEdge.isLive()) this.init();
const e = this._subdiv.locateFromEdge(v, this._lastEdge);
this._lastEdge = e;
return e;
}
findEdge() {
const edges = this._subdiv.getEdges();
return edges.iterator().next();
}
get interfaces_() {
return [QuadEdgeLocator];
}
}
class LocateFailureException extends RuntimeException {
constructor() {
super();
LocateFailureException.constructor_.apply(this, arguments);
}
static constructor_() {
this._seg = null;
if (arguments.length === 1) {
if (typeof arguments[0] === 'string') {
const msg = arguments[0];
RuntimeException.constructor_.call(this, msg);
} else if (arguments[0] instanceof LineSegment) {
const seg = arguments[0];
RuntimeException.constructor_.call(this, 'Locate failed to converge (at edge: ' + seg + '). Possible causes include invalid Subdivision topology or very close sites');
this._seg = new LineSegment(seg);
}
} else if (arguments.length === 2) {
const msg = arguments[0],
seg = arguments[1];
RuntimeException.constructor_.call(this, LocateFailureException.msgWithSpatial(msg, seg));
this._seg = new LineSegment(seg);
}
}
static msgWithSpatial(msg, seg) {
if (seg !== null) return msg + ' [ ' + seg + ' ]';
return msg;
}
getSegment() {
return this._seg;
}
}
class TriangleVisitor {
visit(triEdges) {}
}
class QuadEdgeSubdivision {
constructor() {
QuadEdgeSubdivision.constructor_.apply(this, arguments);
}
static constructor_() {
this._visitedKey = 0;
this._quadEdges = new ArrayList();
this._startingEdge = null;
this._tolerance = null;
this._edgeCoincidenceTolerance = null;
this._frameVertex = new Array(3).fill(null);
this._frameEnv = null;
this._locator = null;
this._seg = new LineSegment();
this._triEdges = new Array(3).fill(null);
const env = arguments[0],
tolerance = arguments[1];
this._tolerance = tolerance;
this._edgeCoincidenceTolerance = tolerance / QuadEdgeSubdivision.EDGE_COINCIDENCE_TOL_FACTOR;
this.createFrame(env);
this._startingEdge = this.initSubdiv();
this._locator = new LastFoundQuadEdgeLocator(this);
}
static getTriangleEdges(startQE, triEdge) {
triEdge[0] = startQE;
triEdge[1] = triEdge[0].lNext();
triEdge[2] = triEdge[1].lNext();
if (triEdge[2].lNext() !== triEdge[0]) throw new IllegalArgumentException('Edges do not form a triangle');
}
getTriangleVertices(includeFrame) {
const visitor = new TriangleVertexListVisitor();
this.visitTriangles(visitor, includeFrame);
return visitor.getTriangleVertices();
}
isFrameVertex(v) {
if (v.equals(this._frameVertex[0])) return true;
if (v.equals(this._frameVertex[1])) return true;
if (v.equals(this._frameVertex[2])) return true;
return false;
}
isVertexOfEdge(e, v) {
if (v.equals(e.orig(), this._tolerance) || v.equals(e.dest(), this._tolerance)) return true;
return false;
}
connect(a, b) {
const q = QuadEdge.connect(a, b);
this._quadEdges.add(q);
return q;
}
getVoronoiCellPolygon(qe, geomFact) {
const cellPts = new ArrayList();
const startQE = qe;
do {
const cc = qe.rot().orig().getCoordinate();
cellPts.add(cc);
qe = qe.oPrev();
} while (qe !== startQE);
const coordList = new CoordinateList();
coordList.addAll(cellPts, false);
coordList.closeRing();
if (coordList.size() < 4) {
System.out.println(coordList);
coordList.add(coordList.get(coordList.size() - 1), true);
}
const pts = coordList.toCoordinateArray();
const cellPoly = geomFact.createPolygon(geomFact.createLinearRing(pts));
const v = startQE.orig();
cellPoly.setUserData(v.getCoordinate());
return cellPoly;
}
setLocator(locator) {
this._locator = locator;
}
initSubdiv() {
const ea = this.makeEdge(this._frameVertex[0], this._frameVertex[1]);
const eb = this.makeEdge(this._frameVertex[1], this._frameVertex[2]);
QuadEdge.splice(ea.sym(), eb);
const ec = this.makeEdge(this._frameVertex[2], this._frameVertex[0]);
QuadEdge.splice(eb.sym(), ec);
QuadEdge.splice(ec.sym(), ea);
return ea;
}
isFrameBorderEdge(e) {
const leftTri = new Array(3).fill(null);
QuadEdgeSubdivision.getTriangleEdges(e, leftTri);
const rightTri = new Array(3).fill(null);
QuadEdgeSubdivision.getTriangleEdges(e.sym(), rightTri);
const vLeftTriOther = e.lNext().dest();
if (this.isFrameVertex(vLeftTriOther)) return true;
const vRightTriOther = e.sym().lNext().dest();
if (this.isFrameVertex(vRightTriOther)) return true;
return false;
}
makeEdge(o, d) {
const q = QuadEdge.makeEdge(o, d);
this._quadEdges.add(q);
return q;
}
visitTriangles(triVisitor, includeFrame) {
this._visitedKey++;
const edgeStack = new Stack();
edgeStack.push(this._startingEdge);
const visitedEdges = new HashSet();
while (!edgeStack.empty()) {
const edge = edgeStack.pop();
if (!visitedEdges.contains(edge)) {
const triEdges = this.fetchTriangleToVisit(edge, edgeStack, includeFrame, visitedEdges);
if (triEdges !== null) triVisitor.visit(triEdges);
}
}
}
isFrameEdge(e) {
if (this.isFrameVertex(e.orig()) || this.isFrameVertex(e.dest())) return true;
return false;
}
isOnEdge(e, p) {
this._seg.setCoordinates(e.orig().getCoordinate(), e.dest().getCoordinate());
const dist = this._seg.distance(p);
return dist < this._edgeCoincidenceTolerance;
}
getEnvelope() {
return new Envelope(this._frameEnv);
}
createFrame(env) {
const deltaX = env.getWidth();
const deltaY = env.getHeight();
let offset = 0.0;
if (deltaX > deltaY) offset = deltaX * 10.0;else offset = deltaY * 10.0;
this._frameVertex[0] = new Vertex((env.getMaxX() + env.getMinX()) / 2.0, env.getMaxY() + offset);
this._frameVertex[1] = new Vertex(env.getMinX() - offset, env.getMinY() - offset);
this._frameVertex[2] = new Vertex(env.getMaxX() + offset, env.getMinY() - offset);
this._frameEnv = new Envelope(this._frameVertex[0].getCoordinate(), this._frameVertex[1].getCoordinate());
this._frameEnv.expandToInclude(this._frameVertex[2].getCoordinate());
}
getTriangleCoordinates(includeFrame) {
const visitor = new TriangleCoordinatesVisitor();
this.visitTriangles(visitor, includeFrame);
return visitor.getTriangles();
}
getVertices(includeFrame) {
const vertices = new HashSet();
for (let i = this._quadEdges.iterator(); i.hasNext();) {
const qe = i.next();
const v = qe.orig();
if (includeFrame || !this.isFrameVertex(v)) vertices.add(v);
const vd = qe.dest();
if (includeFrame || !this.isFrameVertex(vd)) vertices.add(vd);
}
return vertices;
}
fetchTriangleToVisit(edge, edgeStack, includeFrame, visitedEdges) {
let curr = edge;
let edgeCount = 0;
let isFrame = false;
do {
this._triEdges[edgeCount] = curr;
if (this.isFrameEdge(curr)) isFrame = true;
const sym = curr.sym();
if (!visitedEdges.contains(sym)) edgeStack.push(sym);
visitedEdges.add(curr);
edgeCount++;
curr = curr.lNext();
} while (curr !== edge);
if (isFrame && !includeFrame) return null;
return this._triEdges;
}
getEdges() {
if (arguments.length === 0) {
return this._quadEdges;
} else if (arguments.length === 1) {
const geomFact = arguments[0];
const quadEdges = this.getPrimaryEdges(false);
const edges = new Array(quadEdges.size()).fill(null);
let i = 0;
for (let it = quadEdges.iterator(); it.hasNext();) {
const qe = it.next();
edges[i++] = geomFact.createLineString([qe.orig().getCoordinate(), qe.dest().getCoordinate()]);
}
return geomFact.createMultiLineString(edges);
}
}
getVertexUniqueEdges(includeFrame) {
const edges = new ArrayList();
const visitedVertices = new HashSet();
for (let i = this._quadEdges.iterator(); i.hasNext();) {
const qe = i.next();
const v = qe.orig();
if (!visitedVertices.contains(v)) {
visitedVertices.add(v);
if (includeFrame || !this.isFrameVertex(v)) edges.add(qe);
}
const qd = qe.sym();
const vd = qd.orig();
if (!visitedVertices.contains(vd)) {
visitedVertices.add(vd);
if (includeFrame || !this.isFrameVertex(vd)) edges.add(qd);
}
}
return edges;
}
getTriangleEdges(includeFrame) {
const visitor = new TriangleEdgesListVisitor();
this.visitTriangles(visitor, includeFrame);
return visitor.getTriangleEdges();
}
getPrimaryEdges(includeFrame) {
this._visitedKey++;
const edges = new ArrayList();
const edgeStack = new Stack();
edgeStack.push(this._startingEdge);
const visitedEdges = new HashSet();
while (!edgeStack.empty()) {
const edge = edgeStack.pop();
if (!visitedEdges.contains(edge)) {
const priQE = edge.getPrimary();
if (includeFrame || !this.isFrameEdge(priQE)) edges.add(priQE);
edgeStack.push(edge.oNext());
edgeStack.push(edge.sym().oNext());
visitedEdges.add(edge);
visitedEdges.add(edge.sym());
}
}
return edges;
}
delete(e) {
QuadEdge.splice(e, e.oPrev());
QuadEdge.splice(e.sym(), e.sym().oPrev());
const eSym = e.sym();
const eRot = e.rot();
const eRotSym = e.rot().sym();
this._quadEdges.remove(e);
this._quadEdges.remove(eSym);
this._quadEdges.remove(eRot);
this._quadEdges.remove(eRotSym);
e.delete();
eSym.delete();
eRot.delete();
eRotSym.delete();
}
locateFromEdge(v, startEdge) {
let iter = 0;
const maxIter = this._quadEdges.size();
let e = startEdge;
while (true) {
iter++;
if (iter > maxIter) throw new LocateFailureException(e.toLineSegment());
if (v.equals(e.orig()) || v.equals(e.dest())) break;else if (v.rightOf(e)) e = e.sym();else if (!v.rightOf(e.oNext())) e = e.oNext();else if (!v.rightOf(e.dPrev())) e = e.dPrev();else break;
}
return e;
}
getTolerance() {
return this._tolerance;
}
getVoronoiCellPolygons(geomFact) {
this.visitTriangles(new TriangleCircumcentreVisitor(), true);
const cells = new ArrayList();
const edges = this.getVertexUniqueEdges(false);
for (let i = edges.iterator(); i.hasNext();) {
const qe = i.next();
cells.add(this.getVoronoiCellPolygon(qe, geomFact));
}
return cells;
}
getVoronoiDiagram(geomFact) {
const vorCells = this.getVoronoiCellPolygons(geomFact);
return geomFact.createGeometryCollection(GeometryFactory.toGeometryArray(vorCells));
}
getTriangles(geomFact) {
const triPtsList = this.getTriangleCoordinates(false);
const tris = new Array(triPtsList.size()).fill(null);
let i = 0;
for (let it = triPtsList.iterator(); it.hasNext();) {
const triPt = it.next();
tris[i++] = geomFact.createPolygon(geomFact.createLinearRing(triPt));
}
return geomFact.createGeometryCollection(tris);
}
insertSite(v) {
let e = this.locate(v);
if (v.equals(e.orig(), this._tolerance) || v.equals(e.dest(), this._tolerance)) return e;
let base = this.makeEdge(e.orig(), v);
QuadEdge.splice(base, e);
const startEdge = base;
do {
base = this.connect(e, base.sym());
e = base.oPrev();
} while (e.lNext() !== startEdge);
return startEdge;
}
locate() {
if (arguments.length === 1) {
if (arguments[0] instanceof Vertex) {
const v = arguments[0];
return this._locator.locate(v);
} else if (arguments[0] instanceof Coordinate) {
const p = arguments[0];
return this._locator.locate(new Vertex(p));
}
} else if (arguments.length === 2) {
const p0 = arguments[0],
p1 = arguments[1];
const e = this._locator.locate(new Vertex(p0));
if (e === null) return null;
let base = e;
if (e.dest().getCoordinate().equals2D(p0)) base = e.sym();
let locEdge = base;
do {
if (locEdge.dest().getCoordinate().equals2D(p1)) return locEdge;
locEdge = locEdge.oNext();
} while (locEdge !== base);
return null;
}
}
}
class TriangleCircumcentreVisitor {
visit(triEdges) {
const a = triEdges[0].orig().getCoordinate();
const b = triEdges[1].orig().getCoordinate();
const c = triEdges[2].orig().getCoordinate();
const cc = Triangle.circumcentreDD(a, b, c);
const ccVertex = new Vertex(cc);
for (let i = 0; i < 3; i++) triEdges[i].rot().setOrig(ccVertex);
}
get interfaces_() {
return [TriangleVisitor];
}
}
class TriangleEdgesListVisitor {
constructor() {
TriangleEdgesListVisitor.constructor_.apply(this, arguments);
}
static constructor_() {
this._triList = new ArrayList();
}
getTriangleEdges() {
return this._triList;
}
visit(triEdges) {
this._triList.add(triEdges);
}
get interfaces_() {
return [TriangleVisitor];
}
}
class TriangleVertexListVisitor {
constructor() {
TriangleVertexListVisitor.constructor_.apply(this, arguments);
}
static constructor_() {
this._triList = new ArrayList();
}
visit(triEdges) {
this._triList.add([triEdges[0].orig(), triEdges[1].orig(), triEdges[2].orig()]);
}
getTriangleVertices() {
return this._triList;
}
get interfaces_() {
return [TriangleVisitor];
}
}
class TriangleCoordinatesVisitor {
constructor() {
TriangleCoordinatesVisitor.constructor_.apply(this, arguments);
}
static constructor_() {
this._coordList = new CoordinateList();
this._triCoords = new ArrayList();
}
checkTriangleSize(pts) {
if (pts.length >= 2) WKTWriter.toLineString(pts[0], pts[1]);else if (pts.length >= 1) WKTWriter.toPoint(pts[0]);
}
visit(triEdges) {
this._coordList.clear();
for (let i = 0; i < 3; i++) {
const v = triEdges[i].orig();
this._coordList.add(v.getCoordinate());
}
if (this._coordList.size() > 0) {
this._coordList.closeRing();
const pts = this._coordList.toCoordinateArray();
if (pts.length !== 4) return null;
this._triCoords.add(pts);
}
}
getTriangles() {
return this._triCoords;
}
get interfaces_() {
return [TriangleVisitor];
}
}
QuadEdgeSubdivision.TriangleCircumcentreVisitor = TriangleCircumcentreVisitor;
QuadEdgeSubdivision.TriangleEdgesListVisitor = TriangleEdgesListVisitor;
QuadEdgeSubdivision.TriangleVertexListVisitor = TriangleVertexListVisitor;
QuadEdgeSubdivision.TriangleCoordinatesVisitor = TriangleCoordinatesVisitor;
QuadEdgeSubdivision.EDGE_COINCIDENCE_TOL_FACTOR = 1000;
class Segment {
constructor() {
Segment.constructor_.apply(this, arguments);
}
static constructor_() {
this._ls = null;
this._data = null;
if (arguments.length === 2) {
const p0 = arguments[0],
p1 = arguments[1];
this._ls = new LineSegment(p0, p1);
} else if (arguments.length === 3) {
const p0 = arguments[0],
p1 = arguments[1],
data = arguments[2];
this._ls = new LineSegment(p0, p1);
this._data = data;
} else if (arguments.length === 6) {
const x1 = arguments[0],
y1 = arguments[1],
z1 = arguments[2],
x2 = arguments[3],
y2 = arguments[4],
z2 = arguments[5];
Segment.constructor_.call(this, new Coordinate(x1, y1, z1), new Coordinate(x2, y2, z2));
} else if (arguments.length === 7) {
const x1 = arguments[0],
y1 = arguments[1],
z1 = arguments[2],
x2 = arguments[3],
y2 = arguments[4],
z2 = arguments[5],
data = arguments[6];
Segment.constructor_.call(this, new Coordinate(x1, y1, z1), new Coordinate(x2, y2, z2), data);
}
}
getLineSegment() {
return this._ls;
}
getEndZ() {
const p = this._ls.getCoordinate(1);
return p.getZ();
}
getStartZ() {
const p = this._ls.getCoordinate(0);
return p.getZ();
}
intersection(s) {
return this._ls.intersection(s.getLineSegment());
}
getStart() {
return this._ls.getCoordinate(0);
}
getEnd() {
return this._ls.getCoordinate(1);
}
getEndY() {
const p = this._ls.getCoordinate(1);
return p.y;
}
getStartX() {
const p = this._ls.getCoordinate(0);
return p.x;
}
equalsTopo(s) {
return this._ls.equalsTopo(s.getLineSegment());
}
getStartY() {
const p = this._ls.getCoordinate(0);
return p.y;
}
setData(data) {
this._data = data;
}
getData() {
return this._data;
}
getEndX() {
const p = this._ls.getCoordinate(1);
return p.x;
}
toString() {
return this._ls.toString();
}
}
class ConstraintEnforcementException extends RuntimeException {
constructor() {
super();
ConstraintEnforcementException.constructor_.apply(this, arguments);
}
static constructor_() {
this._pt = null;
if (arguments.length === 1) {
const msg = arguments[0];
RuntimeException.constructor_.call(this, msg);
} else if (arguments.length === 2) {
const msg = arguments[0],
pt = arguments[1];
RuntimeException.constructor_.call(this, ConstraintEnforcementException.msgWithCoord(msg, pt));
this._pt = new Coordinate(pt);
}
}
static msgWithCoord(msg, pt) {
if (pt !== null) return msg + ' [ ' + WKTWriter.toPoint(pt) + ' ]';
return msg;
}
getCoordinate() {
return this._pt;
}
}
class ConformingDelaunayTriangulator {
constructor() {
ConformingDelaunayTriangulator.constructor_.apply(this, arguments);
}
static constructor_() {
this._initialVertices = null;
this._segVertices = null;
this._segments = new ArrayList();
this._subdiv = null;
this._incDel = null;
this._convexHull = null;
this._splitFinder = new NonEncroachingSplitPointFinder();
this._kdt = null;
this._vertexFactory = null;
this._computeAreaEnv = null;
this._splitPt = null;
this._tolerance = null;
const initialVertices = arguments[0],
tolerance = arguments[1];
this._initialVertices = new ArrayList(initialVertices);
this._tolerance = tolerance;
this._kdt = new KdTree(tolerance);
}
static computeVertexEnvelope(vertices) {
const env = new Envelope();
for (let i = vertices.iterator(); i.hasNext();) {
const v = i.next();
env.expandToInclude(v.getCoordinate());
}
return env;
}
getInitialVertices() {
return this._initialVertices;
}
getKDT() {
return this._kdt;
}
enforceConstraints() {
this.addConstraintVertices();
let count = 0;
let splits = 0;
do {
splits = this.enforceGabriel(this._segments);
count++;
} while (splits > 0 && count < ConformingDelaunayTriangulator.MAX_SPLIT_ITER);
if (count === ConformingDelaunayTriangulator.MAX_SPLIT_ITER) throw new ConstraintEnforcementException('Too many splitting iterations while enforcing constraints. Last split point was at: ', this._splitPt);
}
insertSites(vertices) {
for (let i = vertices.iterator(); i.hasNext();) {
const v = i.next();
this.insertSite(v);
}
}
getVertexFactory() {
return this._vertexFactory;
}
getPointArray() {
const pts = new Array(this._initialVertices.size() + this._segVertices.size()).fill(null);
let index = 0;
for (let i = this._initialVertices.iterator(); i.hasNext();) {
const v = i.next();
pts[index++] = v.getCoordinate();
}
for (let i2 = this._segVertices.iterator(); i2.hasNext();) {
const v = i2.next();
pts[index++] = v.getCoordinate();
}
return pts;
}
setConstraints(segments, segVertices) {
this._segments = segments;
this._segVertices = segVertices;
}
computeConvexHull() {
const fact = new GeometryFactory();
const coords = this.getPointArray();
const hull = new ConvexHull(coords, fact);
this._convexHull = hull.getConvexHull();
}
addConstraintVertices() {
this.computeConvexHull();
this.insertSites(this._segVertices);
}
findNonGabrielPoint(seg) {
const p = seg.getStart();
const q = seg.getEnd();
const midPt = new Coordinate((p.x + q.x) / 2.0, (p.y + q.y) / 2.0);
const segRadius = p.distance(midPt);
const env = new Envelope(midPt);
env.expandBy(segRadius);
const result = this._kdt.query(env);
let closestNonGabriel = null;
let minDist = Double.MAX_VALUE;
for (let i = result.iterator(); i.hasNext();) {
const nextNode = i.next();
const testPt = nextNode.getCoordinate();
if (testPt.equals2D(p) || testPt.equals2D(q)) continue;
const testRadius = midPt.distance(testPt);
if (testRadius < segRadius) {
const testDist = testRadius;
if (closestNonGabriel === null || testDist < minDist) {
closestNonGabriel = testPt;
minDist = testDist;
}
}
}
return closestNonGabriel;
}
getConstraintSegments() {
return this._segments;
}
setSplitPointFinder(splitFinder) {
this._splitFinder = splitFinder;
}
getConvexHull() {
return this._convexHull;
}
getTolerance() {
return this._tolerance;
}
enforceGabriel(segsToInsert) {
const newSegments = new ArrayList();
let splits = 0;
const segsToRemove = new ArrayList();
for (let i = segsToInsert.iterator(); i.hasNext();) {
const seg = i.next();
const encroachPt = this.findNonGabrielPoint(seg);
if (encroachPt === null) continue;
this._splitPt = this._splitFinder.findSplitPoint(seg, encroachPt);
const splitVertex = this.createVertex(this._splitPt, seg);
const insertedVertex = this.insertSite(splitVertex);
if (!insertedVertex.getCoordinate().equals2D(this._splitPt)) ;
const s1 = new Segment(seg.getStartX(), seg.getStartY(), seg.getStartZ(), splitVertex.getX(), splitVertex.getY(), splitVertex.getZ(), seg.getData());
const s2 = new Segment(splitVertex.getX(), splitVertex.getY(), splitVertex.getZ(), seg.getEndX(), seg.getEndY(), seg.getEndZ(), seg.getData());
newSegments.add(s1);
newSegments.add(s2);
segsToRemove.add(seg);
splits = splits + 1;
}
segsToInsert.removeAll(segsToRemove);
segsToInsert.addAll(newSegments);
return splits;
}
createVertex() {
if (arguments.length === 1) {
const p = arguments[0];
let v = null;
if (this._vertexFactory !== null) v = this._vertexFactory.createVertex(p, null);else v = new ConstraintVertex(p);
return v;
} else if (arguments.length === 2) {
const p = arguments[0],
seg = arguments[1];
let v = null;
if (this._vertexFactory !== null) v = this._vertexFactory.createVertex(p, seg);else v = new ConstraintVertex(p);
v.setOnConstraint(true);
return v;
}
}
getSubdivision() {
return this._subdiv;
}
computeBoundingBox() {
const vertexEnv = ConformingDelaunayTriangulator.computeVertexEnvelope(this._initialVertices);
const segEnv = ConformingDelaunayTriangulator.computeVertexEnvelope(this._segVertices);
const allPointsEnv = new Envelope(vertexEnv);
allPointsEnv.expandToInclude(segEnv);
const deltaX = allPointsEnv.getWidth() * 0.2;
const deltaY = allPointsEnv.getHeight() * 0.2;
const delta = Math.max(deltaX, deltaY);
this._computeAreaEnv = new Envelope(allPointsEnv);
this._computeAreaEnv.expandBy(delta);
}
setVertexFactory(vertexFactory) {
this._vertexFactory = vertexFactory;
}
formInitialDelaunay() {
this.computeBoundingBox();
this._subdiv = new QuadEdgeSubdivision(this._computeAreaEnv, this._tolerance);
this._subdiv.setLocator(new LastFoundQuadEdgeLocator(this._subdiv));
this._incDel = new IncrementalDelaunayTriangulator(this._subdiv);
this.insertSites(this._initialVertices);
}
insertSite() {
if (arguments[0] instanceof ConstraintVertex) {
const v = arguments[0];
const kdnode = this._kdt.insert(v.getCoordinate(), v);
if (!kdnode.isRepeated()) {
this._incDel.insertSite(v);
} else {
const snappedV = kdnode.getData();
snappedV.merge(v);
return snappedV;
}
return v;
} else if (arguments[0] instanceof Coordinate) {
const p = arguments[0];
this.insertSite(this.createVertex(p));
}
}
}
ConformingDelaunayTriangulator.MAX_SPLIT_ITER = 99;
class DelaunayTriangulationBuilder {
constructor() {
DelaunayTriangulationBuilder.constructor_.apply(this, arguments);
}
static constructor_() {
this._siteCoords = null;
this._tolerance = 0.0;
this._subdiv = null;
}
static extractUniqueCoordinates(geom) {
if (geom === null) return new CoordinateList();
const coords = geom.getCoordinates();
return DelaunayTriangulationBuilder.unique(coords);
}
static envelope(coords) {
const env = new Envelope();
for (let i = coords.iterator(); i.hasNext();) {
const coord = i.next();
env.expandToInclude(coord);
}
return env;
}
static unique(coords) {
const coordsCopy = CoordinateArrays.copyDeep(coords);
Arrays.sort(coordsCopy);
const coordList = new CoordinateList(coordsCopy, false);
return coordList;
}
static toVertices(coords) {
const verts = new ArrayList();
for (let i = coords.iterator(); i.hasNext();) {
const coord = i.next();
verts.add(new Vertex(coord));
}
return verts;
}
create() {
if (this._subdiv !== null) return null;
const siteEnv = DelaunayTriangulationBuilder.envelope(this._siteCoords);
const vertices = DelaunayTriangulationBuilder.toVertices(this._siteCoords);
this._subdiv = new QuadEdgeSubdivision(siteEnv, this._tolerance);
const triangulator = new IncrementalDelaunayTriangulator(this._subdiv);
triangulator.insertSites(vertices);
}
setTolerance(tolerance) {
this._tolerance = tolerance;
}
setSites() {
if (arguments[0] instanceof Geometry) {
const geom = arguments[0];
this._siteCoords = DelaunayTriangulationBuilder.extractUniqueCoordinates(geom);
} else if (hasInterface(arguments[0], Collection)) {
const coords = arguments[0];
this._siteCoords = DelaunayTriangulationBuilder.unique(CoordinateArrays.toCoordinateArray(coords));
}
}
getEdges(geomFact) {
this.create();
return this._subdiv.getEdges(geomFact);
}
getSubdivision() {
this.create();
return this._subdiv;
}
getTriangles(geomFact) {
this.create();
return this._subdiv.getTriangles(geomFact);
}
}
class ConformingDelaunayTriangulationBuilder {
constructor() {
ConformingDelaunayTriangulationBuilder.constructor_.apply(this, arguments);
}
static constructor_() {
this._siteCoords = null;
this._constraintLines = null;
this._tolerance = 0.0;
this._subdiv = null;
this._constraintVertexMap = new TreeMap();
}
static createConstraintSegments() {
if (arguments.length === 1) {
const geom = arguments[0];
const lines = LinearComponentExtracter.getLines(geom);
const constraintSegs = new ArrayList();
for (let i = lines.iterator(); i.hasNext();) {
const line = i.next();
ConformingDelaunayTriangulationBuilder.createConstraintSegments(line, constraintSegs);
}
return constraintSegs;
} else if (arguments.length === 2) {
const line = arguments[0],
constraintSegs = arguments[1];
const coords = line.getCoordinates();
for (let i = 1; i < coords.length; i++) constraintSegs.add(new Segment(coords[i - 1], coords[i]));
}
}
createSiteVertices(coords) {
const verts = new ArrayList();
for (let i = coords.iterator(); i.hasNext();) {
const coord = i.next();
if (this._constraintVertexMap.containsKey(coord)) continue;
verts.add(new ConstraintVertex(coord));
}
return verts;
}
create() {
if (this._subdiv !== null) return null;
const siteEnv = DelaunayTriangulationBuilder.envelope(this._siteCoords);
let segments = new ArrayList();
if (this._constraintLines !== null) {
siteEnv.expandToInclude(this._constraintLines.getEnvelopeInternal());
this.createVertices(this._constraintLines);
segments = ConformingDelaunayTriangulationBuilder.createConstraintSegments(this._constraintLines);
}
const sites = this.createSiteVertices(this._siteCoords);
const cdt = new ConformingDelaunayTriangulator(sites, this._tolerance);
cdt.setConstraints(segments, new ArrayList(this._constraintVertexMap.values()));
cdt.formInitialDelaunay();
cdt.enforceConstraints();
this._subdiv = cdt.getSubdivision();
}
setTolerance(tolerance) {
this._tolerance = tolerance;
}
setConstraints(constraintLines) {
this._constraintLines = constraintLines;
}
setSites(geom) {
this._siteCoords = DelaunayTriangulationBuilder.extractUniqueCoordinates(geom);
}
getEdges(geomFact) {
this.create();
return this._subdiv.getEdges(geomFact);
}
getSubdivision() {
this.create();
return this._subdiv;
}
getTriangles(geomFact) {
this.create();
return this._subdiv.getTriangles(geomFact);
}
createVertices(geom) {
const coords = geom.getCoordinates();
for (let i = 0; i < coords.length; i++) {
const v = new ConstraintVertex(coords[i]);
this._constraintVertexMap.put(coords[i], v);
}
}
}
class VoronoiDiagramBuilder {
constructor() {
VoronoiDiagramBuilder.constructor_.apply(this, arguments);
}
static constructor_() {
this._siteCoords = null;
this._tolerance = 0.0;
this._subdiv = null;
this._clipEnv = null;
this._diagramEnv = null;
}
static clipGeometryCollection(geom, clipEnv) {
const clipPoly = geom.getFactory().toGeometry(clipEnv);
const clipped = new ArrayList();
for (let i = 0; i < geom.getNumGeometries(); i++) {
const g = geom.getGeometryN(i);
let result = null;
if (clipEnv.contains(g.getEnvelopeInternal())) {
result = g;
} else if (clipEnv.intersects(g.getEnvelopeInternal())) {
result = OverlayOp.intersection(clipPoly, g);
result.setUserData(g.getUserData());
}
if (result !== null && !result.isEmpty()) clipped.add(result);
}
return geom.getFactory().createGeometryCollection(GeometryFactory.toGeometryArray(clipped));
}
create() {
if (this._subdiv !== null) return null;
const siteEnv = DelaunayTriangulationBuilder.envelope(this._siteCoords);
this._diagramEnv = this._clipEnv;
if (this._diagramEnv === null) {
this._diagramEnv = siteEnv;
const expandBy = this._diagramEnv.getDiameter();
this._diagramEnv.expandBy(expandBy);
}
const vertices = DelaunayTriangulationBuilder.toVertices(this._siteCoords);
this._subdiv = new QuadEdgeSubdivision(siteEnv, this._tolerance);
const triangulator = new IncrementalDelaunayTriangulator(this._subdiv);
triangulator.insertSites(vertices);
}
getDiagram(geomFact) {
this.create();
const polys = this._subdiv.getVoronoiDiagram(geomFact);
return VoronoiDiagramBuilder.clipGeometryCollection(polys, this._diagramEnv);
}
setTolerance(tolerance) {
this._tolerance = tolerance;
}
setSites() {
if (arguments[0] instanceof Geometry) {
const geom = arguments[0];
this._siteCoords = DelaunayTriangulationBuilder.extractUniqueCoordinates(geom);
} else if (hasInterface(arguments[0], Collection)) {
const coords = arguments[0];
this._siteCoords = DelaunayTriangulationBuilder.unique(CoordinateArrays.toCoordinateArray(coords));
}
}
setClipEnvelope(clipEnv) {
this._clipEnv = clipEnv;
}
getSubdivision() {
this.create();
return this._subdiv;
}
}
var quadedge = /*#__PURE__*/Object.freeze({
__proto__: null,
Vertex: Vertex
});
var triangulate = /*#__PURE__*/Object.freeze({
__proto__: null,
ConformingDelaunayTriangulationBuilder: ConformingDelaunayTriangulationBuilder,
DelaunayTriangulationBuilder: DelaunayTriangulationBuilder,
VoronoiDiagramBuilder: VoronoiDiagramBuilder,
quadedge: quadedge
});
class LinearIterator {
constructor() {
LinearIterator.constructor_.apply(this, arguments);
}
static constructor_() {
this._linearGeom = null;
this._numLines = null;
this._currentLine = null;
this._componentIndex = 0;
this._vertexIndex = 0;
if (arguments.length === 1) {
const linear = arguments[0];
LinearIterator.constructor_.call(this, linear, 0, 0);
} else if (arguments.length === 2) {
const linear = arguments[0],
start = arguments[1];
LinearIterator.constructor_.call(this, linear, start.getComponentIndex(), LinearIterator.segmentEndVertexIndex(start));
} else if (arguments.length === 3) {
const linearGeom = arguments[0],
componentIndex = arguments[1],
vertexIndex = arguments[2];
if (!hasInterface(linearGeom, Lineal)) throw new IllegalArgumentException('Lineal geometry is required');
this._linearGeom = linearGeom;
this._numLines = linearGeom.getNumGeometries();
this._componentIndex = componentIndex;
this._vertexIndex = vertexIndex;
this.loadCurrentLine();
}
}
static segmentEndVertexIndex(loc) {
if (loc.getSegmentFraction() > 0.0) return loc.getSegmentIndex() + 1;
return loc.getSegmentIndex();
}
getComponentIndex() {
return this._componentIndex;
}
getLine() {
return this._currentLine;
}
getVertexIndex() {
return this._vertexIndex;
}
getSegmentEnd() {
if (this._vertexIndex < this.getLine().getNumPoints() - 1) return this._currentLine.getCoordinateN(this._vertexIndex + 1);
return null;
}
next() {
if (!this.hasNext()) return null;
this._vertexIndex++;
if (this._vertexIndex >= this._currentLine.getNumPoints()) {
this._componentIndex++;
this.loadCurrentLine();
this._vertexIndex = 0;
}
}
loadCurrentLine() {
if (this._componentIndex >= this._numLines) {
this._currentLine = null;
return null;
}
this._currentLine = this._linearGeom.getGeometryN(this._componentIndex);
}
getSegmentStart() {
return this._currentLine.getCoordinateN(this._vertexIndex);
}
isEndOfLine() {
if (this._componentIndex >= this._numLines) return false;
if (this._vertexIndex < this._currentLine.getNumPoints() - 1) return false;
return true;
}
hasNext() {
if (this._componentIndex >= this._numLines) return false;
if (this._componentIndex === this._numLines - 1 && this._vertexIndex >= this._currentLine.getNumPoints()) return false;
return true;
}
}
class LengthIndexOfPoint {
constructor() {
LengthIndexOfPoint.constructor_.apply(this, arguments);
}
static constructor_() {
this._linearGeom = null;
const linearGeom = arguments[0];
this._linearGeom = linearGeom;
}
static indexOf(linearGeom, inputPt) {
const locater = new LengthIndexOfPoint(linearGeom);
return locater.indexOf(inputPt);
}
static indexOfAfter(linearGeom, inputPt, minIndex) {
const locater = new LengthIndexOfPoint(linearGeom);
return locater.indexOfAfter(inputPt, minIndex);
}
indexOf(inputPt) {
return this.indexOfFromStart(inputPt, -1.0);
}
indexOfFromStart(inputPt, minIndex) {
let minDistance = Double.MAX_VALUE;
let ptMeasure = minIndex;
let segmentStartMeasure = 0.0;
const seg = new LineSegment();
const it = new LinearIterator(this._linearGeom);
while (it.hasNext()) {
if (!it.isEndOfLine()) {
seg.p0 = it.getSegmentStart();
seg.p1 = it.getSegmentEnd();
const segDistance = seg.distance(inputPt);
const segMeasureToPt = this.segmentNearestMeasure(seg, inputPt, segmentStartMeasure);
if (segDistance < minDistance && segMeasureToPt > minIndex) {
ptMeasure = segMeasureToPt;
minDistance = segDistance;
}
segmentStartMeasure += seg.getLength();
}
it.next();
}
return ptMeasure;
}
indexOfAfter(inputPt, minIndex) {
if (minIndex < 0.0) return this.indexOf(inputPt);
const endIndex = this._linearGeom.getLength();
if (endIndex < minIndex) return endIndex;
const closestAfter = this.indexOfFromStart(inputPt, minIndex);
Assert.isTrue(closestAfter >= minIndex, 'computed index is before specified minimum index');
return closestAfter;
}
segmentNearestMeasure(seg, inputPt, segmentStartMeasure) {
const projFactor = seg.projectionFactor(inputPt);
if (projFactor <= 0.0) return segmentStartMeasure;
if (projFactor <= 1.0) return segmentStartMeasure + projFactor * seg.getLength();
return segmentStartMeasure + seg.getLength();
}
}
class LinearLocation {
constructor() {
LinearLocation.constructor_.apply(this, arguments);
}
static constructor_() {
this._componentIndex = 0;
this._segmentIndex = 0;
this._segmentFraction = 0.0;
if (arguments.length === 0) ; else if (arguments.length === 1) {
const loc = arguments[0];
this._componentIndex = loc._componentIndex;
this._segmentIndex = loc._segmentIndex;
this._segmentFraction = loc._segmentFraction;
} else if (arguments.length === 2) {
const segmentIndex = arguments[0],
segmentFraction = arguments[1];
LinearLocation.constructor_.call(this, 0, segmentIndex, segmentFraction);
} else if (arguments.length === 3) {
const componentIndex = arguments[0],
segmentIndex = arguments[1],
segmentFraction = arguments[2];
this._componentIndex = componentIndex;
this._segmentIndex = segmentIndex;
this._segmentFraction = segmentFraction;
this.normalize();
} else if (arguments.length === 4) {
const componentIndex = arguments[0],
segmentIndex = arguments[1],
segmentFraction = arguments[2],
doNormalize = arguments[3];
this._componentIndex = componentIndex;
this._segmentIndex = segmentIndex;
this._segmentFraction = segmentFraction;
if (doNormalize) this.normalize();
}
}
static getEndLocation(linear) {
const loc = new LinearLocation();
loc.setToEnd(linear);
return loc;
}
static pointAlongSegmentByFraction(p0, p1, frac) {
if (frac <= 0.0) return p0;
if (frac >= 1.0) return p1;
const x = (p1.x - p0.x) * frac + p0.x;
const y = (p1.y - p0.y) * frac + p0.y;
const z = (p1.getZ() - p0.getZ()) * frac + p0.getZ();
return new Coordinate(x, y, z);
}
static compareLocationValues(componentIndex0, segmentIndex0, segmentFraction0, componentIndex1, segmentIndex1, segmentFraction1) {
if (componentIndex0 < componentIndex1) return -1;
if (componentIndex0 > componentIndex1) return 1;
if (segmentIndex0 < segmentIndex1) return -1;
if (segmentIndex0 > segmentIndex1) return 1;
if (segmentFraction0 < segmentFraction1) return -1;
if (segmentFraction0 > segmentFraction1) return 1;
return 0;
}
static numSegments(line) {
const npts = line.getNumPoints();
if (npts <= 1) return 0;
return npts - 1;
}
getSegmentIndex() {
return this._segmentIndex;
}
getComponentIndex() {
return this._componentIndex;
}
isEndpoint(linearGeom) {
const lineComp = linearGeom.getGeometryN(this._componentIndex);
const nseg = LinearLocation.numSegments(lineComp);
return this._segmentIndex >= nseg || this._segmentIndex === nseg - 1 && this._segmentFraction >= 1.0;
}
isValid(linearGeom) {
if (this._componentIndex < 0 || this._componentIndex >= linearGeom.getNumGeometries()) return false;
const lineComp = linearGeom.getGeometryN(this._componentIndex);
if (this._segmentIndex < 0 || this._segmentIndex > lineComp.getNumPoints()) return false;
if (this._segmentIndex === lineComp.getNumPoints() && this._segmentFraction !== 0.0) return false;
if (this._segmentFraction < 0.0 || this._segmentFraction > 1.0) return false;
return true;
}
normalize() {
if (this._segmentFraction < 0.0) this._segmentFraction = 0.0;
if (this._segmentFraction > 1.0) this._segmentFraction = 1.0;
if (this._componentIndex < 0) {
this._componentIndex = 0;
this._segmentIndex = 0;
this._segmentFraction = 0.0;
}
if (this._segmentIndex < 0) {
this._segmentIndex = 0;
this._segmentFraction = 0.0;
}
if (this._segmentFraction === 1.0) {
this._segmentFraction = 0.0;
this._segmentIndex += 1;
}
}
toLowest(linearGeom) {
const lineComp = linearGeom.getGeometryN(this._componentIndex);
const nseg = LinearLocation.numSegments(lineComp);
if (this._segmentIndex < nseg) return this;
return new LinearLocation(this._componentIndex, nseg - 1, 1.0, false);
}
getCoordinate(linearGeom) {
const lineComp = linearGeom.getGeometryN(this._componentIndex);
const p0 = lineComp.getCoordinateN(this._segmentIndex);
if (this._segmentIndex >= LinearLocation.numSegments(lineComp)) return p0;
const p1 = lineComp.getCoordinateN(this._segmentIndex + 1);
return LinearLocation.pointAlongSegmentByFraction(p0, p1, this._segmentFraction);
}
getSegmentFraction() {
return this._segmentFraction;
}
getSegment(linearGeom) {
const lineComp = linearGeom.getGeometryN(this._componentIndex);
const p0 = lineComp.getCoordinateN(this._segmentIndex);
if (this._segmentIndex >= LinearLocation.numSegments(lineComp)) {
const prev = lineComp.getCoordinateN(lineComp.getNumPoints() - 2);
return new LineSegment(prev, p0);
}
const p1 = lineComp.getCoordinateN(this._segmentIndex + 1);
return new LineSegment(p0, p1);
}
clamp(linear) {
if (this._componentIndex >= linear.getNumGeometries()) {
this.setToEnd(linear);
return null;
}
if (this._segmentIndex >= linear.getNumPoints()) {
const line = linear.getGeometryN(this._componentIndex);
this._segmentIndex = LinearLocation.numSegments(line);
this._segmentFraction = 1.0;
}
}
setToEnd(linear) {
this._componentIndex = linear.getNumGeometries() - 1;
const lastLine = linear.getGeometryN(this._componentIndex);
this._segmentIndex = LinearLocation.numSegments(lastLine);
this._segmentFraction = 0.0;
}
compareTo(o) {
const other = o;
if (this._componentIndex < other._componentIndex) return -1;
if (this._componentIndex > other._componentIndex) return 1;
if (this._segmentIndex < other._segmentIndex) return -1;
if (this._segmentIndex > other._segmentIndex) return 1;
if (this._segmentFraction < other._segmentFraction) return -1;
if (this._segmentFraction > other._segmentFraction) return 1;
return 0;
}
copy() {
return new LinearLocation(this._componentIndex, this._segmentIndex, this._segmentFraction);
}
toString() {
return 'LinearLoc[' + this._componentIndex + ', ' + this._segmentIndex + ', ' + this._segmentFraction + ']';
}
isOnSameSegment(loc) {
if (this._componentIndex !== loc._componentIndex) return false;
if (this._segmentIndex === loc._segmentIndex) return true;
if (loc._segmentIndex - this._segmentIndex === 1 && loc._segmentFraction === 0.0) return true;
if (this._segmentIndex - loc._segmentIndex === 1 && this._segmentFraction === 0.0) return true;
return false;
}
snapToVertex(linearGeom, minDistance) {
if (this._segmentFraction <= 0.0 || this._segmentFraction >= 1.0) return null;
const segLen = this.getSegmentLength(linearGeom);
const lenToStart = this._segmentFraction * segLen;
const lenToEnd = segLen - lenToStart;
if (lenToStart <= lenToEnd && lenToStart < minDistance) this._segmentFraction = 0.0;else if (lenToEnd <= lenToStart && lenToEnd < minDistance) this._segmentFraction = 1.0;
}
compareLocationValues(componentIndex1, segmentIndex1, segmentFraction1) {
if (this._componentIndex < componentIndex1) return -1;
if (this._componentIndex > componentIndex1) return 1;
if (this._segmentIndex < segmentIndex1) return -1;
if (this._segmentIndex > segmentIndex1) return 1;
if (this._segmentFraction < segmentFraction1) return -1;
if (this._segmentFraction > segmentFraction1) return 1;
return 0;
}
getSegmentLength(linearGeom) {
const lineComp = linearGeom.getGeometryN(this._componentIndex);
let segIndex = this._segmentIndex;
if (this._segmentIndex >= LinearLocation.numSegments(lineComp)) segIndex = lineComp.getNumPoints() - 2;
const p0 = lineComp.getCoordinateN(segIndex);
const p1 = lineComp.getCoordinateN(segIndex + 1);
return p0.distance(p1);
}
isVertex() {
return this._segmentFraction <= 0.0 || this._segmentFraction >= 1.0;
}
get interfaces_() {
return [Comparable];
}
}
class LocationIndexOfPoint {
constructor() {
LocationIndexOfPoint.constructor_.apply(this, arguments);
}
static constructor_() {
this._linearGeom = null;
const linearGeom = arguments[0];
this._linearGeom = linearGeom;
}
static indexOf(linearGeom, inputPt) {
const locater = new LocationIndexOfPoint(linearGeom);
return locater.indexOf(inputPt);
}
static indexOfAfter(linearGeom, inputPt, minIndex) {
const locater = new LocationIndexOfPoint(linearGeom);
return locater.indexOfAfter(inputPt, minIndex);
}
indexOf(inputPt) {
return this.indexOfFromStart(inputPt, null);
}
indexOfFromStart(inputPt, minIndex) {
let minDistance = Double.MAX_VALUE;
let minComponentIndex = 0;
let minSegmentIndex = 0;
let minFrac = -1.0;
const seg = new LineSegment();
for (let it = new LinearIterator(this._linearGeom); it.hasNext(); it.next()) if (!it.isEndOfLine()) {
seg.p0 = it.getSegmentStart();
seg.p1 = it.getSegmentEnd();
const segDistance = seg.distance(inputPt);
const segFrac = seg.segmentFraction(inputPt);
const candidateComponentIndex = it.getComponentIndex();
const candidateSegmentIndex = it.getVertexIndex();
if (segDistance < minDistance) if (minIndex === null || minIndex.compareLocationValues(candidateComponentIndex, candidateSegmentIndex, segFrac) < 0) {
minComponentIndex = candidateComponentIndex;
minSegmentIndex = candidateSegmentIndex;
minFrac = segFrac;
minDistance = segDistance;
}
}
if (minDistance === Double.MAX_VALUE) return new LinearLocation(minIndex);
const loc = new LinearLocation(minComponentIndex, minSegmentIndex, minFrac);
return loc;
}
indexOfAfter(inputPt, minIndex) {
if (minIndex === null) return this.indexOf(inputPt);
const endLoc = LinearLocation.getEndLocation(this._linearGeom);
if (endLoc.compareTo(minIndex) <= 0) return endLoc;
const closestAfter = this.indexOfFromStart(inputPt, minIndex);
Assert.isTrue(closestAfter.compareTo(minIndex) >= 0, 'computed location is before specified minimum location');
return closestAfter;
}
}
class LocationIndexOfLine {
constructor() {
LocationIndexOfLine.constructor_.apply(this, arguments);
}
static constructor_() {
this._linearGeom = null;
const linearGeom = arguments[0];
this._linearGeom = linearGeom;
}
static indicesOf(linearGeom, subLine) {
const locater = new LocationIndexOfLine(linearGeom);
return locater.indicesOf(subLine);
}
indicesOf(subLine) {
const startPt = subLine.getGeometryN(0).getCoordinateN(0);
const lastLine = subLine.getGeometryN(subLine.getNumGeometries() - 1);
const endPt = lastLine.getCoordinateN(lastLine.getNumPoints() - 1);
const locPt = new LocationIndexOfPoint(this._linearGeom);
const subLineLoc = new Array(2).fill(null);
subLineLoc[0] = locPt.indexOf(startPt);
if (subLine.getLength() === 0.0) subLineLoc[1] = subLineLoc[0].copy();else subLineLoc[1] = locPt.indexOfAfter(endPt, subLineLoc[0]);
return subLineLoc;
}
}
class LengthLocationMap {
constructor() {
LengthLocationMap.constructor_.apply(this, arguments);
}
static constructor_() {
this._linearGeom = null;
const linearGeom = arguments[0];
this._linearGeom = linearGeom;
}
static getLength(linearGeom, loc) {
const locater = new LengthLocationMap(linearGeom);
return locater.getLength(loc);
}
static getLocation() {
if (arguments.length === 2) {
const linearGeom = arguments[0],
length = arguments[1];
const locater = new LengthLocationMap(linearGeom);
return locater.getLocation(length);
} else if (arguments.length === 3) {
const linearGeom = arguments[0],
length = arguments[1],
resolveLower = arguments[2];
const locater = new LengthLocationMap(linearGeom);
return locater.getLocation(length, resolveLower);
}
}
getLength(loc) {
let totalLength = 0.0;
const it = new LinearIterator(this._linearGeom);
while (it.hasNext()) {
if (!it.isEndOfLine()) {
const p0 = it.getSegmentStart();
const p1 = it.getSegmentEnd();
const segLen = p1.distance(p0);
if (loc.getComponentIndex() === it.getComponentIndex() && loc.getSegmentIndex() === it.getVertexIndex()) return totalLength + segLen * loc.getSegmentFraction();
totalLength += segLen;
}
it.next();
}
return totalLength;
}
resolveHigher(loc) {
if (!loc.isEndpoint(this._linearGeom)) return loc;
let compIndex = loc.getComponentIndex();
if (compIndex >= this._linearGeom.getNumGeometries() - 1) return loc;
do compIndex++; while (compIndex < this._linearGeom.getNumGeometries() - 1 && this._linearGeom.getGeometryN(compIndex).getLength() === 0);
return new LinearLocation(compIndex, 0, 0.0);
}
getLocation() {
if (arguments.length === 1) {
const length = arguments[0];
return this.getLocation(length, true);
} else if (arguments.length === 2) {
const length = arguments[0],
resolveLower = arguments[1];
let forwardLength = length;
if (length < 0.0) {
const lineLen = this._linearGeom.getLength();
forwardLength = lineLen + length;
}
const loc = this.getLocationForward(forwardLength);
if (resolveLower) return loc;
return this.resolveHigher(loc);
}
}
getLocationForward(length) {
if (length <= 0.0) return new LinearLocation();
let totalLength = 0.0;
const it = new LinearIterator(this._linearGeom);
while (it.hasNext()) {
if (it.isEndOfLine()) {
if (totalLength === length) {
const compIndex = it.getComponentIndex();
const segIndex = it.getVertexIndex();
return new LinearLocation(compIndex, segIndex, 0.0);
}
} else {
const p0 = it.getSegmentStart();
const p1 = it.getSegmentEnd();
const segLen = p1.distance(p0);
if (totalLength + segLen > length) {
const frac = (length - totalLength) / segLen;
const compIndex = it.getComponentIndex();
const segIndex = it.getVertexIndex();
return new LinearLocation(compIndex, segIndex, frac);
}
totalLength += segLen;
}
it.next();
}
return LinearLocation.getEndLocation(this._linearGeom);
}
}
class LinearGeometryBuilder {
constructor() {
LinearGeometryBuilder.constructor_.apply(this, arguments);
}
static constructor_() {
this._geomFact = null;
this._lines = new ArrayList();
this._coordList = null;
this._ignoreInvalidLines = false;
this._fixInvalidLines = false;
this._lastPt = null;
const geomFact = arguments[0];
this._geomFact = geomFact;
}
getGeometry() {
this.endLine();
return this._geomFact.buildGeometry(this._lines);
}
getLastCoordinate() {
return this._lastPt;
}
endLine() {
if (this._coordList === null) return null;
if (this._ignoreInvalidLines && this._coordList.size() < 2) {
this._coordList = null;
return null;
}
const rawPts = this._coordList.toCoordinateArray();
let pts = rawPts;
if (this._fixInvalidLines) pts = this.validCoordinateSequence(rawPts);
this._coordList = null;
let line = null;
try {
line = this._geomFact.createLineString(pts);
} catch (ex) {
if (ex instanceof IllegalArgumentException) {
if (!this._ignoreInvalidLines) throw ex;
} else {
throw ex;
}
} finally {}
if (line !== null) this._lines.add(line);
}
setFixInvalidLines(fixInvalidLines) {
this._fixInvalidLines = fixInvalidLines;
}
add() {
if (arguments.length === 1) {
const pt = arguments[0];
this.add(pt, true);
} else if (arguments.length === 2) {
const pt = arguments[0],
allowRepeatedPoints = arguments[1];
if (this._coordList === null) this._coordList = new CoordinateList();
this._coordList.add(pt, allowRepeatedPoints);
this._lastPt = pt;
}
}
setIgnoreInvalidLines(ignoreInvalidLines) {
this._ignoreInvalidLines = ignoreInvalidLines;
}
validCoordinateSequence(pts) {
if (pts.length >= 2) return pts;
const validPts = [pts[0], pts[0]];
return validPts;
}
}
class ExtractLineByLocation {
constructor() {
ExtractLineByLocation.constructor_.apply(this, arguments);
}
static constructor_() {
this._line = null;
const line = arguments[0];
this._line = line;
}
static extract(line, start, end) {
const ls = new ExtractLineByLocation(line);
return ls.extract(start, end);
}
computeLinear(start, end) {
const builder = new LinearGeometryBuilder(this._line.getFactory());
builder.setFixInvalidLines(true);
if (!start.isVertex()) builder.add(start.getCoordinate(this._line));
for (let it = new LinearIterator(this._line, start); it.hasNext(); it.next()) {
if (end.compareLocationValues(it.getComponentIndex(), it.getVertexIndex(), 0.0) < 0) break;
const pt = it.getSegmentStart();
builder.add(pt);
if (it.isEndOfLine()) builder.endLine();
}
if (!end.isVertex()) builder.add(end.getCoordinate(this._line));
return builder.getGeometry();
}
computeLine(start, end) {
const coordinates = this._line.getCoordinates();
const newCoordinates = new CoordinateList();
let startSegmentIndex = start.getSegmentIndex();
if (start.getSegmentFraction() > 0.0) startSegmentIndex += 1;
let lastSegmentIndex = end.getSegmentIndex();
if (end.getSegmentFraction() === 1.0) lastSegmentIndex += 1;
if (lastSegmentIndex >= coordinates.length) lastSegmentIndex = coordinates.length - 1;
if (!start.isVertex()) newCoordinates.add(start.getCoordinate(this._line));
for (let i = startSegmentIndex; i <= lastSegmentIndex; i++) newCoordinates.add(coordinates[i]);
if (!end.isVertex()) newCoordinates.add(end.getCoordinate(this._line));
if (newCoordinates.size() <= 0) newCoordinates.add(start.getCoordinate(this._line));
let newCoordinateArray = newCoordinates.toCoordinateArray();
if (newCoordinateArray.length <= 1) newCoordinateArray = [newCoordinateArray[0], newCoordinateArray[0]];
return this._line.getFactory().createLineString(newCoordinateArray);
}
extract(start, end) {
if (end.compareTo(start) < 0) return this.reverse(this.computeLinear(end, start));
return this.computeLinear(start, end);
}
reverse(linear) {
if (hasInterface(linear, Lineal)) return linear.reverse();
Assert.shouldNeverReachHere('non-linear geometry encountered');
return null;
}
}
class LengthIndexedLine {
constructor() {
LengthIndexedLine.constructor_.apply(this, arguments);
}
static constructor_() {
this._linearGeom = null;
const linearGeom = arguments[0];
this._linearGeom = linearGeom;
}
clampIndex(index) {
const posIndex = this.positiveIndex(index);
const startIndex = this.getStartIndex();
if (posIndex < startIndex) return startIndex;
const endIndex = this.getEndIndex();
if (posIndex > endIndex) return endIndex;
return posIndex;
}
locationOf() {
if (arguments.length === 1) {
const index = arguments[0];
return LengthLocationMap.getLocation(this._linearGeom, index);
} else if (arguments.length === 2) {
const index = arguments[0],
resolveLower = arguments[1];
return LengthLocationMap.getLocation(this._linearGeom, index, resolveLower);
}
}
project(pt) {
return LengthIndexOfPoint.indexOf(this._linearGeom, pt);
}
positiveIndex(index) {
if (index >= 0.0) return index;
return this._linearGeom.getLength() + index;
}
extractPoint() {
if (arguments.length === 1) {
const index = arguments[0];
const loc = LengthLocationMap.getLocation(this._linearGeom, index);
return loc.getCoordinate(this._linearGeom);
} else if (arguments.length === 2) {
const index = arguments[0],
offsetDistance = arguments[1];
const loc = LengthLocationMap.getLocation(this._linearGeom, index);
const locLow = loc.toLowest(this._linearGeom);
return locLow.getSegment(this._linearGeom).pointAlongOffset(locLow.getSegmentFraction(), offsetDistance);
}
}
isValidIndex(index) {
return index >= this.getStartIndex() && index <= this.getEndIndex();
}
getEndIndex() {
return this._linearGeom.getLength();
}
getStartIndex() {
return 0.0;
}
indexOfAfter(pt, minIndex) {
return LengthIndexOfPoint.indexOfAfter(this._linearGeom, pt, minIndex);
}
extractLine(startIndex, endIndex) {
const startIndex2 = this.clampIndex(startIndex);
const endIndex2 = this.clampIndex(endIndex);
const resolveStartLower = startIndex2 === endIndex2;
const startLoc = this.locationOf(startIndex2, resolveStartLower);
const endLoc = this.locationOf(endIndex2);
return ExtractLineByLocation.extract(this._linearGeom, startLoc, endLoc);
}
indexOf(pt) {
return LengthIndexOfPoint.indexOf(this._linearGeom, pt);
}
indicesOf(subLine) {
const locIndex = LocationIndexOfLine.indicesOf(this._linearGeom, subLine);
const index = [LengthLocationMap.getLength(this._linearGeom, locIndex[0]), LengthLocationMap.getLength(this._linearGeom, locIndex[1])];
return index;
}
}
class LocationIndexedLine {
constructor() {
LocationIndexedLine.constructor_.apply(this, arguments);
}
static constructor_() {
this._linearGeom = null;
const linearGeom = arguments[0];
this._linearGeom = linearGeom;
this.checkGeometryType();
}
clampIndex(index) {
const loc = index.copy();
loc.clamp(this._linearGeom);
return loc;
}
project(pt) {
return LocationIndexOfPoint.indexOf(this._linearGeom, pt);
}
checkGeometryType() {
if (!(this._linearGeom instanceof LineString || this._linearGeom instanceof MultiLineString)) throw new IllegalArgumentException('Input geometry must be linear');
}
extractPoint() {
if (arguments.length === 1) {
const index = arguments[0];
return index.getCoordinate(this._linearGeom);
} else if (arguments.length === 2) {
const index = arguments[0],
offsetDistance = arguments[1];
const indexLow = index.toLowest(this._linearGeom);
return indexLow.getSegment(this._linearGeom).pointAlongOffset(indexLow.getSegmentFraction(), offsetDistance);
}
}
isValidIndex(index) {
return index.isValid(this._linearGeom);
}
getEndIndex() {
return LinearLocation.getEndLocation(this._linearGeom);
}
getStartIndex() {
return new LinearLocation();
}
indexOfAfter(pt, minIndex) {
return LocationIndexOfPoint.indexOfAfter(this._linearGeom, pt, minIndex);
}
extractLine(startIndex, endIndex) {
return ExtractLineByLocation.extract(this._linearGeom, startIndex, endIndex);
}
indexOf(pt) {
return LocationIndexOfPoint.indexOf(this._linearGeom, pt);
}
indicesOf(subLine) {
return LocationIndexOfLine.indicesOf(this._linearGeom, subLine);
}
}
var linearref = /*#__PURE__*/Object.freeze({
__proto__: null,
LengthIndexedLine: LengthIndexedLine,
LengthLocationMap: LengthLocationMap,
LinearGeometryBuilder: LinearGeometryBuilder,
LinearIterator: LinearIterator,
LinearLocation: LinearLocation,
LocationIndexedLine: LocationIndexedLine
});
class CollectionUtil {
static transform(coll, func) {
const result = new ArrayList();
for (let i = coll.iterator(); i.hasNext();) result.add(func.execute(i.next()));
return result;
}
static select(collection, func) {
const result = new ArrayList();
for (let i = collection.iterator(); i.hasNext();) {
const item = i.next();
if (Boolean.TRUE.equals(func.execute(item))) result.add(item);
}
return result;
}
static apply(coll, func) {
for (let i = coll.iterator(); i.hasNext();) func.execute(i.next());
}
}
function Function() {}
CollectionUtil.Function = Function;
class CoordinateArrayFilter {
constructor() {
CoordinateArrayFilter.constructor_.apply(this, arguments);
}
static constructor_() {
this.pts = null;
this.n = 0;
const size = arguments[0];
this.pts = new Array(size).fill(null);
}
filter(coord) {
this.pts[this.n++] = coord;
}
getCoordinates() {
return this.pts;
}
get interfaces_() {
return [CoordinateFilter];
}
}
class CoordinateCountFilter {
constructor() {
CoordinateCountFilter.constructor_.apply(this, arguments);
}
static constructor_() {
this._n = 0;
}
filter(coord) {
this._n++;
}
getCount() {
return this._n;
}
get interfaces_() {
return [CoordinateFilter];
}
}
class ObjectCounter {
constructor() {
ObjectCounter.constructor_.apply(this, arguments);
}
static constructor_() {
this._counts = new HashMap();
}
count(o) {
const counter = this._counts.get(o);
if (counter === null) return 0;else return counter.count();
}
add(o) {
const counter = this._counts.get(o);
if (counter === null) this._counts.put(o, new Counter(1));else counter.increment();
}
}
class Counter {
constructor() {
Counter.constructor_.apply(this, arguments);
}
static constructor_() {
this.count = 0;
if (arguments.length === 0) ; else if (arguments.length === 1) {
const count = arguments[0];
this.count = count;
}
}
count() {
return this.count;
}
increment() {
this.count++;
}
}
ObjectCounter.Counter = Counter;
function PrintStream() {}
function StringReader() {}
function ByteArrayOutputStream() {}
class IOException extends Exception {}
function LineNumberReader() {}
class StringUtil {
static chars(c, n) {
const ch = new Array(n).fill(null);
for (let i = 0; i < n; i++) ch[i] = c;
return new String(ch);
}
static getStackTrace() {
if (arguments.length === 1) {
const t = arguments[0];
const os = new ByteArrayOutputStream();
const ps = new PrintStream(os);
t.printStackTrace(ps);
return os.toString();
} else if (arguments.length === 2) {
const t = arguments[0],
depth = arguments[1];
let stackTrace = '';
const stringReader = new StringReader(StringUtil.getStackTrace(t));
const lineNumberReader = new LineNumberReader(stringReader);
for (let i = 0; i < depth; i++) try {
stackTrace += lineNumberReader.readLine() + StringUtil.NEWLINE;
} catch (e) {
if (e instanceof IOException) Assert.shouldNeverReachHere();else throw e;
} finally {}
return stackTrace;
}
}
static spaces(n) {
return StringUtil.chars(' ', n);
}
static split(s, separator) {
const separatorlen = separator.length;
const tokenList = new ArrayList();
let tmpString = '' + s;
let pos = tmpString.indexOf(separator);
while (pos >= 0) {
const token = tmpString.substring(0, pos);
tokenList.add(token);
tmpString = tmpString.substring(pos + separatorlen);
pos = tmpString.indexOf(separator);
}
if (tmpString.length > 0) tokenList.add(tmpString);
const res = new Array(tokenList.size()).fill(null);
for (let i = 0; i < res.length; i++) res[i] = tokenList.get(i);
return res;
}
}
StringUtil.NEWLINE = System.getProperty('line.separator');
var util = /*#__PURE__*/Object.freeze({
__proto__: null,
CollectionUtil: CollectionUtil,
CoordinateArrayFilter: CoordinateArrayFilter,
CoordinateCountFilter: CoordinateCountFilter,
GeometricShapeFactory: GeometricShapeFactory,
NumberUtil: NumberUtil,
ObjectCounter: ObjectCounter,
PriorityQueue: PriorityQueue,
StringUtil: StringUtil,
UniqueCoordinateArrayFilter: UniqueCoordinateArrayFilter
});
const version = '2.7.1 (16652a2)';
export { algorithm, densify, dissolve, geom, geomgraph, index, io, linearref, noding, operation, precision, simplify, triangulate, util, version };
//# sourceMappingURL=jsts.es6.js.map