RelateSegmentString.java
/*
* Copyright (c) 2024 Martin Davis.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* and Eclipse Distribution License v. 1.0 which accompanies this distribution.
* The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at
*
* http://www.eclipse.org/org/documents/edl-v10.php.
*/
package org.locationtech.jts.operation.relateng;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.CoordinateArrays;
import org.locationtech.jts.geom.Dimension;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.noding.BasicSegmentString;
/**
* Models a linear edge of a {@link RelateGeometry}.
*
* @author mdavis
*
*/
class RelateSegmentString extends BasicSegmentString {
public static RelateSegmentString createLine(Coordinate[] pts, boolean isA, int elementId, RelateGeometry parent) {
return createSegmentString(pts, isA, Dimension.L, elementId, -1, null, parent);
}
public static RelateSegmentString createRing(Coordinate[] pts, boolean isA, int elementId, int ringId,
Geometry poly, RelateGeometry parent) {
return createSegmentString(pts, isA, Dimension.A, elementId, ringId, poly, parent);
}
private static RelateSegmentString createSegmentString(Coordinate[] pts, boolean isA, int dim, int elementId, int ringId,
Geometry poly, RelateGeometry parent) {
pts = removeRepeatedPoints(pts);
return new RelateSegmentString(pts, isA, dim, elementId, ringId, poly, parent);
}
private static Coordinate[] removeRepeatedPoints(Coordinate[] pts) {
if (CoordinateArrays.hasRepeatedPoints(pts)) {
pts = CoordinateArrays.removeRepeatedPoints(pts);
}
return pts;
}
private boolean isA;
private int dimension;
private int id;
private int ringId;
private RelateGeometry inputGeom;
private Geometry parentPolygonal = null;
private RelateSegmentString(Coordinate[] pts, boolean isA, int dimension, int id, int ringId, Geometry poly, RelateGeometry inputGeom) {
super(pts, null);
this.isA = isA;
this.dimension = dimension;
this.id = id;
this.ringId = ringId;
this.parentPolygonal = poly;
this.inputGeom = inputGeom;
}
public boolean isA() {
return isA;
}
public RelateGeometry getGeometry() {
return inputGeom;
}
public Geometry getPolygonal() {
return parentPolygonal;
}
public NodeSection createNodeSection(int segIndex, Coordinate intPt) {
boolean isNodeAtVertex =
intPt.equals2D(getCoordinate(segIndex))
|| intPt.equals2D(getCoordinate(segIndex + 1));
Coordinate prev = prevVertex(segIndex, intPt);
Coordinate next = nextVertex(segIndex, intPt);
NodeSection a = new NodeSection(isA, dimension, id, ringId, parentPolygonal, isNodeAtVertex, prev, intPt, next);
return a;
}
/**
*
* @param ss
* @param segIndex
* @param pt
* @return the previous vertex, or null if none exists
*/
private Coordinate prevVertex(int segIndex, Coordinate pt) {
Coordinate segStart = getCoordinate(segIndex);
if (! segStart.equals2D(pt))
return segStart;
//-- pt is at segment start, so get previous vertex
if (segIndex > 0)
return getCoordinate(segIndex - 1);
if (isClosed())
return prevInRing(segIndex);
return null;
}
/**
*
* @param ss
* @param segIndex
* @param pt
* @return the next vertex, or null if none exists
*/
private Coordinate nextVertex(int segIndex, Coordinate pt) {
Coordinate segEnd = getCoordinate(segIndex + 1);
if (! segEnd.equals2D(pt))
return segEnd;
//-- pt is at seg end, so get next vertex
if (segIndex < size() - 2)
return getCoordinate(segIndex + 2);
if (isClosed())
return nextInRing(segIndex + 1);
//-- segstring is not closed, so there is no next segment
return null;
}
/**
* Tests if a segment intersection point has that segment as its
* canonical containing segment.
* Segments are half-closed, and contain their start point but not the endpoint,
* except for the final segment in a non-closed segment string, which contains
* its endpoint as well.
* This test ensures that vertices are assigned to a unique segment in a segment string.
* In particular, this avoids double-counting intersections which lie exactly
* at segment endpoints.
*
* @param segIndex the segment the point may lie on
* @param pt the point
* @return true if the segment contains the point
*/
public boolean isContainingSegment(int segIndex, Coordinate pt) {
//-- intersection is at segment start vertex - process it
if (pt.equals2D(getCoordinate(segIndex)))
return true;
if (pt.equals2D(getCoordinate(segIndex+1))) {
boolean isFinalSegment = segIndex == size() - 2;
if (isClosed() || ! isFinalSegment)
return false;
//-- for final segment, process intersections with final endpoint
return true;
}
//-- intersection is interior - process it
return true;
}
}