package com.revolsys.geometry.graph.visitor;
import java.util.LinkedHashSet;
import java.util.function.Consumer;
import com.revolsys.geometry.event.CoordinateEventListenerList;
import com.revolsys.geometry.graph.Edge;
import com.revolsys.geometry.graph.Graph;
import com.revolsys.geometry.graph.event.EdgeEvent;
import com.revolsys.geometry.graph.event.EdgeEventListenerList;
import com.revolsys.geometry.model.GeometryFactory;
import com.revolsys.geometry.model.LineString;
import com.revolsys.geometry.model.coordinates.list.CoordinatesListUtil;
import com.revolsys.geometry.model.impl.PointDoubleXY;
import com.revolsys.math.Angle;
import com.revolsys.util.MathUtil;
public class EdgeCleanCloseVerticesVisitor<T> implements Consumer<Edge<T>> {
private final CoordinateEventListenerList coordinateListeners = new CoordinateEventListenerList();
private final EdgeEventListenerList<T> edgeListeners = new EdgeEventListenerList<>();
private final Graph<T> graph;
private final double minDistance;
public EdgeCleanCloseVerticesVisitor(final Graph<T> graph, final double minDistance) {
this.graph = graph;
this.minDistance = minDistance;
}
public EdgeCleanCloseVerticesVisitor(final Graph<T> graph, final double minDistance,
final Consumer<Edge<T>> visitor) {
this.graph = graph;
this.minDistance = minDistance;
}
// TODO look at the angles with the previous and next segments to decide
// which coordinate to remove. If there is a right angle in a building then
// it should probably not be removed. This would be confirmed by the angles
// of the next and previous segments.
/**
* Visit the edge performing any required cleanup.
*
* @param edge The edge to visit.
* @return true If further edges should be processed.
*/
@Override
public void accept(final Edge<T> edge) {
final String typePath = edge.getTypeName();
final LineString line = edge.getLine();
final int vertexCount = line.getVertexCount();
if (vertexCount > 2) {
final GeometryFactory geometryFactory = line.getGeometryFactory();
final LinkedHashSet<Integer> removeIndicies = new LinkedHashSet<>();
double x1 = line.getX(0);
double y1 = line.getY(0);
for (int i = 1; i < vertexCount; i++) {
final double x2 = line.getX(i);
final double y2 = line.getY(i);
final double distance = MathUtil.distance(x1, y1, x2, y2);
if (distance < this.minDistance) {
final double previousAngle = getAngle(edge, line, i - 1);
final double angle = getAngle(edge, line, i);
final double nextAngle = getAngle(edge, line, i + 1);
boolean fixed = false;
if (angle > previousAngle) {
if (angle > nextAngle) {
if (angle > Math.toRadians(160)) {
fixed = true;
}
}
} else if (previousAngle > nextAngle) {
}
if (fixed) {
this.coordinateListeners.coordinateEvent(new PointDoubleXY(x2, y2), typePath,
"Short Segment", "Fixed", distance + " " + Math.toDegrees(previousAngle) + " "
+ Math.toDegrees(angle) + " " + Math.toDegrees(nextAngle));
} else {
this.coordinateListeners.coordinateEvent(new PointDoubleXY(x2, y2), typePath,
"Short Segment", "Review", distance + " " + Math.toDegrees(previousAngle) + " "
+ Math.toDegrees(angle) + " " + Math.toDegrees(nextAngle));
}
}
x1 = x2;
y1 = y2;
}
if (!removeIndicies.isEmpty()) {
final int axisCount = line.getAxisCount();
final double[] newCoordinates = new double[(vertexCount - removeIndicies.size())
* axisCount];
int k = 0;
for (int j = 0; j < vertexCount; j++) {
if (!removeIndicies.contains(j)) {
CoordinatesListUtil.setCoordinates(newCoordinates, axisCount, k, line, j);
k++;
}
}
final LineString newLine = geometryFactory.lineString(axisCount, newCoordinates);
final Edge<T> newEdge = this.graph.replaceEdge(edge, newLine);
this.edgeListeners.edgeEvent(newEdge, "Edge close indicies", EdgeEvent.EDGE_CHANGED, null);
}
}
}
private double getAngle(final Edge<T> edge, final LineString line, final int index) {
if (index + index - 1 < 0 || index + index + 1 >= line.getVertexCount()) {
return Double.NaN;
} else {
final double x1 = line.getCoordinate(index - 1, 0);
final double y1 = line.getCoordinate(index - 1, 1);
final double x2 = line.getCoordinate(index, 0);
final double y2 = line.getCoordinate(index, 1);
final double x3 = line.getCoordinate(index + 1, 0);
final double y3 = line.getCoordinate(index + 1, 1);
return Angle.angleBetween(x1, y1, x2, y2, x3, y3);
}
}
public CoordinateEventListenerList getCoordinateListeners() {
return this.coordinateListeners;
}
public EdgeEventListenerList<T> getEdgeListeners() {
return this.edgeListeners;
}
}