package com.revolsys.geometry.graph.linestring;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.revolsys.geometry.graph.Edge;
import com.revolsys.geometry.graph.Graph;
import com.revolsys.geometry.graph.Node;
import com.revolsys.geometry.model.GeometryFactory;
import com.revolsys.geometry.model.LineString;
import com.revolsys.geometry.model.Lineal;
import com.revolsys.geometry.model.Point;
import com.revolsys.geometry.model.segment.LineSegment;
public class LineStringRelate {
private final Point fromPoint1;
private final Point fromPoint2;
private final LineStringGraph graph1;
private final LineStringGraph graph2;
private final LineString line1;
private final LineString line2;
private final Point toPoint1;
private final Point toPoint2;
public LineStringRelate(final LineString line1, final LineString line2) {
this(line1, line2, 1);
}
public LineStringRelate(final LineString line1, final LineString line2, final double tolerance) {
this.line1 = line1;
this.line2 = line2;
final GeometryFactory geometryFactory = line1.getGeometryFactory();
this.graph1 = new LineStringGraph(geometryFactory, line1);
this.graph2 = new LineStringGraph(geometryFactory, line2);
final Map<Point, Point> movedNodes = new HashMap<>();
this.graph1
.forEachNode((node) -> this.graph2.movePointsWithinTolerance(movedNodes, tolerance, node));
this.graph2
.forEachNode((node) -> this.graph2.movePointsWithinTolerance(movedNodes, tolerance, node));
final int i = 0;
this.fromPoint1 = getMovedCoordinate(movedNodes, line1, i);
this.fromPoint2 = getMovedCoordinate(movedNodes, line2, i);
this.toPoint1 = getMovedCoordinate(movedNodes, line1, line1.getVertexCount() - 1);
this.toPoint2 = getMovedCoordinate(movedNodes, line2, line2.getVertexCount() - 1);
}
public Graph<LineSegment> getGraph1() {
return this.graph1;
}
public Graph<LineSegment> getGraph2() {
return this.graph2;
}
public LineString getLine1() {
return this.line1;
}
public LineString getLine2() {
return this.line2;
}
public Point getMovedCoordinate(final Map<Point, Point> movedNodes, final LineString line,
final int i) {
final Point coordinates = line.getVertex(i);
if (movedNodes.containsKey(coordinates)) {
return movedNodes.get(coordinates);
} else {
return coordinates;
}
}
public Lineal getOverlap() {
final List<List<Point>> intersections = new ArrayList<>();
final LineString points1 = this.line1;
final List<Point> currentCoordinates = new ArrayList<>();
Node<LineSegment> previousNode = this.graph1.getNode(this.fromPoint1);
do {
final List<Edge<LineSegment>> outEdges = previousNode.getOutEdges();
if (outEdges.isEmpty()) {
previousNode = null;
} else if (outEdges.size() > 1) {
System.err.println("Cannot handle overlaps\n" + getLine1() + "\n " + getLine2());
final GeometryFactory factory = this.line1.getGeometryFactory();
return factory.lineString();
} else {
final Edge<LineSegment> edge = outEdges.get(0);
final LineSegment line = edge.getObject();
final Node<LineSegment> nextNode = edge.getToNode();
if (this.graph2.hasEdgeBetween(previousNode, nextNode)) {
if (currentCoordinates.size() == 0) {
currentCoordinates.add(line.getPoint(0));
}
currentCoordinates.add(line.getPoint(1));
} else {
if (currentCoordinates.size() > 0) {
final List<Point> points = new ArrayList<>();
intersections.add(points);
currentCoordinates.clear();
}
}
previousNode = nextNode;
}
} while (previousNode != null && !previousNode.equals(2, this.fromPoint1));
if (currentCoordinates.size() > 0) {
final List<Point> points = new ArrayList<>();
intersections.add(points);
}
final GeometryFactory factory = this.line1.getGeometryFactory();
return factory.lineal(intersections);
}
public LineString getRelateLine1() {
return this.graph1.getLine();
}
public LineString getRelateLine2() {
return this.graph2.getLine();
}
public boolean isContained() {
return isContains(this.graph2, this.graph1);
}
public boolean isContains() {
return isContains(this.graph1, this.graph2);
}
private boolean isContains(final Graph<LineSegment> graph1, final Graph<LineSegment> graph2) {
for (final Edge<LineSegment> edge : graph2.getEdges()) {
final Node<LineSegment> fromNode = edge.getFromNode();
final Node<LineSegment> toNode = edge.getToNode();
if (!graph1.hasEdgeBetween(fromNode, toNode)) {
return false;
}
}
return true;
}
public boolean isEndOverlaps(final double maxDistance) {
if (isOverlaps()) {
boolean overlaps = false;
final boolean from1Within = isWithin2(this.fromPoint1, maxDistance);
final boolean to1Within = isWithin2(this.toPoint1, 1);
if (from1Within != to1Within) {
final boolean from2Within = isWithin1(this.fromPoint2, 1);
final boolean to2Within = isWithin1(this.toPoint2, 1);
if (from2Within != to2Within) {
overlaps = true;
}
}
if (overlaps) {
final Lineal intersection = getOverlap();
if (intersection.getGeometryCount() == 1) {
return true;
}
}
}
return false;
}
public boolean isEqual() {
if (this.graph1.getEdgeCount() == this.graph2.getEdgeCount()) {
for (final Edge<LineSegment> edge : this.graph1.getEdges()) {
if (!this.graph2.hasEdge(edge)) {
return false;
}
}
return true;
} else {
return false;
}
}
public boolean isOverlaps() {
final LineStringGraph g1 = this.graph1;
final LineStringGraph g2 = this.graph2;
if (g1.getEdgeCount() <= g2.getEdgeCount()) {
return isOverlaps(g1, g2);
} else {
return isOverlaps(g2, g1);
}
}
private boolean isOverlaps(final LineStringGraph graph1, final LineStringGraph graph2) {
for (final Edge<LineSegment> edge : graph1.getEdges()) {
final Node<LineSegment> fromNode = edge.getFromNode();
final Node<LineSegment> toNode = edge.getToNode();
if (graph2.hasEdgeBetween(fromNode, toNode)) {
return true;
}
}
return false;
}
private boolean isWithin(final LineStringGraph graph, final Point fromPoint, final Point toPoint,
final Point point, final double maxDistance) {
if (point.distancePoint(fromPoint) < maxDistance) {
return false;
} else if (point.distancePoint(toPoint) < maxDistance) {
return false;
} else {
if (!graph.getNodes(point, maxDistance).isEmpty()) {
return true;
}
final List<Edge<LineSegment>> edges = graph.getEdges(point, maxDistance);
for (final Edge<LineSegment> edge : edges) {
final LineSegment line = edge.getObject();
if (line.intersects(point, maxDistance)) {
return true;
}
}
}
return false;
}
public boolean isWithin1(final Point point, final double maxDistance) {
return isWithin(this.graph1, this.fromPoint1, this.toPoint1, point, maxDistance);
}
public boolean isWithin2(final Point point, final double maxDistance) {
return isWithin(this.graph2, this.fromPoint2, this.toPoint2, point, maxDistance);
}
public void splitEdgesCloseToNodes(final double maxDistance) {
final Map<Edge<LineSegment>, List<Node<LineSegment>>> pointsOnEdge1 = this.graph1
.getPointsOnEdges(this.graph2, maxDistance);
final Map<Edge<LineSegment>, List<Node<LineSegment>>> pointsOnEdge2 = this.graph2
.getPointsOnEdges(this.graph1, maxDistance);
this.graph1.splitEdges(pointsOnEdge1);
this.graph2.splitEdges(pointsOnEdge2);
}
}