package com.revolsys.geometry.util;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import com.revolsys.geometry.algorithm.RobustLineIntersector;
import com.revolsys.geometry.algorithm.linematch.LineMatchGraph;
import com.revolsys.geometry.model.End;
import com.revolsys.geometry.model.GeometryFactory;
import com.revolsys.geometry.model.LineString;
import com.revolsys.geometry.model.Point;
import com.revolsys.geometry.model.coordinates.CoordinatesUtil;
import com.revolsys.geometry.model.coordinates.LineSegmentUtil;
public final class LineStringUtil {
public static final String COORDINATE_DISTANCE = "coordinateDistance";
public static final String COORDINATE_INDEX = "coordinateIndex";
public static final String SEGMENT_DISTANCE = "segmentDistance";
public static final String SEGMENT_INDEX = "segmentIndex";
public static LineString addElevation(final LineString original, final LineString update) {
final int axisCount = update.getAxisCount();
if (axisCount > 2) {
final double[] coordinates = update.getCoordinates();
final Point c0 = update.getPoint(0);
if (Double.isNaN(update.getZ(0))) {
final double z = CoordinatesUtil.getElevation(original, c0);
coordinates[2] = z;
}
final Point cN = update.getPoint(update.getVertexCount() - 1);
if (Double.isNaN(cN.getZ())) {
final double z = CoordinatesUtil.getElevation(original, c0);
coordinates[update.getVertexCount() * axisCount + 2] = z;
}
return update.getGeometryFactory().lineString(axisCount, coordinates);
} else {
return update;
}
}
public static void addLineString(final GeometryFactory geometryFactory, final LineString points,
final Point startPoint, final int startIndex, final int endIndex, final Point endPoint,
final List<LineString> lines) {
final int length = endIndex - startIndex + 1;
final LineString newPoints = points.subLine(startPoint, startIndex, length, endPoint);
if (newPoints.getVertexCount() > 1) {
final LineString newLine = geometryFactory.lineString(newPoints);
if (newLine.getLength() > 0) {
lines.add(newLine);
}
}
}
/**
* Compare the coordinates of the two lines up to the given dimension to see
* if they have the same ordinate values in either the forward or reverse
* direction.
*
* @param line1 The first line.
* @param line2 The second line.
* @param dimension The dimension.
* @return True if the coordinates match.
*/
public static boolean equalsIgnoreDirection(final LineString line1, final LineString line2,
final int dimension) {
if (line1 == line2) {
return true;
} else {
if (line1.equals(dimension, line2)) {
return true;
} else {
return line1.reverse().equals(dimension, line2);
}
}
}
/**
* Compare the 2D coordinates of the two lines to see if they have the same
* ordinate values in either the forward or reverse direction.
*
* @param line1 The first line.
* @param line2 The second line.
* @return True if the coordinates match.
*/
public static boolean equalsIgnoreDirection2d(final LineString line1, final LineString line2) {
return equalsIgnoreDirection(line1, line2, 2);
}
public static Point getClosestEndsCoordinates(final LineString line, final Point coordinates) {
final Point fromCoordinates = line.getPoint(0);
final Point toCoordinates = line.getToPoint();
if (fromCoordinates.distancePoint(coordinates) <= toCoordinates.distancePoint(coordinates)) {
return fromCoordinates;
} else {
return toCoordinates;
}
}
/**
* Get the coordinate where two lines cross, or null if they don't cross.
*
* @param line1 The first line.
* @param line2 The second line
* @return The coordinate or null if they don't cross
*/
public static Point getCrossingIntersection(final LineString line1, final LineString line2) {
final RobustLineIntersector intersector = new RobustLineIntersector();
final LineString coordinates1 = line1;
final LineString coordinates2 = line2;
final int numCoordinates1 = coordinates1.getVertexCount();
final int numCoordinates2 = coordinates2.getVertexCount();
final Point firstCoord1 = coordinates1.getPoint(0);
final Point firstCoord2 = coordinates2.getPoint(0);
final Point lastCoord1 = coordinates1.getPoint(numCoordinates1 - 1);
final Point lastCoord2 = coordinates2.getPoint(numCoordinates2 - 2);
Point previousCoord1 = firstCoord1;
for (int i1 = 1; i1 < numCoordinates1; i1++) {
final Point currentCoord1 = coordinates1.getPoint(i1);
Point previousCoord2 = firstCoord2;
for (int i2 = 1; i2 < numCoordinates2; i2++) {
final Point currentCoord2 = coordinates2.getPoint(i2);
intersector.computeIntersectionPoints(previousCoord1, currentCoord1, previousCoord2,
currentCoord2);
final int numIntersections = intersector.getIntersectionCount();
if (intersector.hasIntersection()) {
if (intersector.isProper()) {
final Point intersection = intersector.getIntersection(0);
return intersection;
} else if (numIntersections == 1) {
final Point intersection = intersector.getIntersection(0);
if (i1 == 1 || i2 == 1 || i1 == numCoordinates1 - 1 || i2 == numCoordinates2 - 1) {
if (!((intersection.equals(2, firstCoord1) || intersection.equals(2, lastCoord1))
&& (intersection.equals(2, firstCoord2) || intersection.equals(2, lastCoord2)))) {
return intersection;
}
} else {
return intersection;
}
} else if (intersector.isInteriorIntersection()) {
for (int i = 0; i < numIntersections; i++) {
final Point intersection = intersector.getIntersection(i);
if (!Arrays.asList(currentCoord1, previousCoord1, currentCoord2, previousCoord2)
.contains(intersection)) {
return intersection;
}
}
}
}
previousCoord2 = currentCoord2;
}
previousCoord1 = currentCoord1;
}
return null;
}
public static double getElevation(final Point point, final Point point1, final Point point2) {
final double z1 = point1.getZ();
final double z2 = point2.getZ();
final double fraction;
if (point1.equals(2, point2)) {
fraction = 0.5;
} else {
fraction = point.distancePoint(point1) / point1.distancePoint(point2);
}
final double z = z1 + (z2 - z1) * fraction;
return z;
}
public static Point getEndPoint(final LineString line, final boolean fromPoint) {
if (fromPoint) {
return line.getPoint(0);
} else {
return line.getToPoint();
}
}
public static boolean hasEqualExact2d(final List<LineString> lines, final LineString newLine) {
for (final LineString line : lines) {
if (line.equals(2, newLine)) {
return true;
}
}
return false;
}
public static boolean hasLoop(final Collection<LineString> lines) {
for (final LineString line : lines) {
if (line.isClosed()) {
return true;
}
}
return false;
}
public static boolean intersects(final LineString line1, final LineString line2) {
if (line1.getBoundingBox().intersects(line2.getBoundingBox())) {
final LineMatchGraph<LineString> graph = new LineMatchGraph<>(line2);
for (final LineString line : line1.segments()) {
if (graph.add(line)) {
return true;
}
}
}
return false;
}
public static boolean isEndsWithinDistance(final LineString line1, final LineString line2,
final double maxDistance) {
final Point fromPoint = line1.getPoint(0);
if (isEndsWithinDistance(line2, fromPoint, maxDistance)) {
return true;
} else {
final Point toPoint = line1.getToPoint();
if (isEndsWithinDistance(line2, toPoint, maxDistance)) {
return true;
} else {
return false;
}
}
}
public static boolean isEndsWithinDistance(final LineString line, final Point point,
final double maxDistance) {
final Point fromPoint = line.getPoint(0);
if (fromPoint.distancePoint(point) < maxDistance) {
return true;
} else {
final Point toPoint = line.getToPoint();
if (toPoint.distancePoint(point) < maxDistance) {
return true;
} else {
return false;
}
}
}
public static boolean isEndsWithinDistanceOfEnds(final LineString line1, final LineString line2,
final double maxDistance) {
final Point fromPoint = line1.getPoint(0);
if (isWithinDistanceOfEnds(fromPoint, line2, maxDistance)) {
final Point toPoint = line1.getToPoint();
return isWithinDistanceOfEnds(toPoint, line2, maxDistance);
} else {
return false;
}
}
public static boolean isPointOnLine(final LineString line, final double x, final double y,
final double maxDistance) {
final int vertexCount = line.getVertexCount();
if (vertexCount > 0) {
double x1 = line.getX(0);
double y1 = line.getY(0);
if (x == x1 && y == y1) {
return true;
}
for (int vertexIndex = 1; vertexIndex < vertexCount; vertexIndex++) {
final double x2 = line.getX(vertexIndex);
final double y2 = line.getY(vertexIndex);
if (x == x2 && y == y2) {
return true;
} else {
if (LineSegmentUtil.isPointOnLine(x1, y1, x2, y2, x, y, maxDistance)) {
return true;
}
}
x1 = x2;
y1 = y2;
}
}
return false;
}
/**
* Check to see if the point is on any of the segments of the line.
*
* @param line The line.
* @param point The point.
* @return True if the point is on the line, false otherwise.
* @see LineSegmentUtil#isPointOnLine(GeometryFactory, Coordinates,
* Coordinates, Point)
*/
public static boolean isPointOnLine(final LineString line, final Point point) {
final GeometryFactory factory = line.getGeometryFactory();
final double x = point.getX();
final double y = point.getY();
final int vertexCount = line.getVertexCount();
if (vertexCount > 0) {
double x1 = line.getX(0);
double y1 = line.getY(0);
if (x == x1 && y == y1) {
return true;
}
for (int vertexIndex = 1; vertexIndex < vertexCount; vertexIndex++) {
final double x2 = line.getX(vertexIndex);
final double y2 = line.getY(vertexIndex);
if (x == x2 && y == y2) {
return true;
} else {
if (LineSegmentUtil.isPointOnLine(factory, x1, y1, x2, y2, x, y)) {
return true;
}
}
x1 = x2;
y1 = y2;
}
}
return false;
}
/**
* Check to see if the point is on any of the segments of the line.
*
* @param line The line.
* @param point The point.
* @param maxDistance The maximum distance the point can be from the line.
* @return True if the point is on the line, false otherwise.
* @see LineSegmentUtil#isPointOnLine(Coordinates, Coordinates, Coordinates,
* double)
*/
public static boolean isPointOnLine(final LineString line, final Point point,
final double maxDistance) {
final double x = point.getX();
final double y = point.getY();
return isPointOnLine(line, x, y, maxDistance);
}
public static boolean isWithinDistance(final Point point, final LineString line, final int index,
final double maxDistance) {
final Point point2 = line.getVertex(index);
return point.distancePoint(point2) < maxDistance;
}
public static boolean isWithinDistanceOfEnds(final Point point, final LineString line,
final double maxDistance) {
if (isWithinDistance(point, line, 0, maxDistance)) {
return true;
} else {
return isWithinDistance(point, line, line.getVertexCount() - 1, maxDistance);
}
}
public static Point pointOffset(final LineString line, final End lineEnd, final double xOffset,
double yOffset) {
if (line.getLength() == 0) {
return line.getFromPoint();
} else {
Point point1;
Point point2;
if (End.isFrom(lineEnd)) {
point1 = line.getPoint(0);
int i = 1;
do {
point2 = line.getPoint(i);
i++;
} while (point1.equals(point2));
} else {
point1 = line.getToPoint();
int i = -2;
do {
point2 = line.getPoint(i);
i--;
} while (point1.equals(point2));
yOffset = -yOffset;
}
return Points.pointOffset(point1, point2, xOffset, yOffset);
}
}
}