package pl.edu.agh.logic;
import static com.google.common.collect.Sets.newHashSet;
import static pl.edu.agh.spatial.HaversineDistanceCalculator.EARTH_DISTANCE_CALCULATOR;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.ListIterator;
import java.util.Set;
import pl.edu.agh.utils.Collections;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.collect.Lists;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.LineSegment;
public class PathUtils {
public static ListIterator<PointMatching> iteratorForNextRoadMatching(ListIterator<PointMatching> current,
List<PointMatching> matchings) {
final Road currentRoad = matchings.get(current.nextIndex()).getRoad();
ListIterator<PointMatching> nextRoadMatching = matchings.listIterator(current.nextIndex());
nextRoadMatching = Collections.moveIteratorToNextElementMatchingPredicate(nextRoadMatching,
new Predicate<PointMatching>() {
@Override
public boolean apply(PointMatching matching) {
return !currentRoad.equals(matching.getRoad());
}
});
return nextRoadMatching;
}
public static ListIterator<PointMatching> iteratorForPreviousRoadMatching(ListIterator<PointMatching> current,
List<PointMatching> matchings) {
final Road currentRoad = matchings.get(current.previousIndex()).getRoad();
ListIterator<PointMatching> previousRoadMatching = matchings.listIterator(current.previousIndex());
previousRoadMatching = Collections.moveIteratorToPreviousElementMatchingPredicate(previousRoadMatching,
new Predicate<PointMatching>() {
@Override
public boolean apply(PointMatching matching) {
return !currentRoad.equals(matching.getRoad());
}
});
return previousRoadMatching;
}
public static ListIterator<PointMatching> nextNonUTurnRoad(ListIterator<PointMatching> current,
List<PointMatching> matchings) {
return nextNonUTurnRoad(current, matchings, false);
}
private static ListIterator<PointMatching> nextNonUTurnRoad(ListIterator<PointMatching> current,
List<PointMatching> matchings, boolean isCurrentUTurn) {
if (!current.hasNext()) {
return current;
}
ListIterator<PointMatching> nextRoadMatching = PathUtils.iteratorForNextRoadMatching(current, matchings);
if (nextRoadMatching.hasNext()
&& isUTurn(matchings.get(current.nextIndex()).getRoad(), matchings.get(nextRoadMatching.nextIndex())
.getRoad())) {
return nextNonUTurnRoad(nextRoadMatching, matchings, true);
}
return isCurrentUTurn ? nextRoadMatching : current;
}
public static ListIterator<PointMatching> previousNonUTurnRoad(ListIterator<PointMatching> current,
List<PointMatching> matchings) {
return previousNonUTurnRoad(current, matchings, false);
}
private static ListIterator<PointMatching> previousNonUTurnRoad(ListIterator<PointMatching> current,
List<PointMatching> matchings, boolean isCurrentUTurn) {
if (!current.hasPrevious()) {
return current;
}
ListIterator<PointMatching> previousRoadMatching = PathUtils.iteratorForPreviousRoadMatching(current, matchings);
if (previousRoadMatching.hasPrevious()
&& isUTurn(matchings.get(current.previousIndex()).getRoad(), matchings.get(previousRoadMatching.previousIndex())
.getRoad())) {
return previousNonUTurnRoad(previousRoadMatching, matchings, true);
}
return isCurrentUTurn ? previousRoadMatching : current;
}
private static boolean isUTurn(Road road1, Road road2) {
return (road1.getId() == road2.getId()) && (road1.isReversed() != road2.isReversed());
}
public static ListIterator<PointMatching> nextUTurnRoad(ListIterator<PointMatching> current, List<PointMatching> matchings) {
if (!current.hasNext()) {
return current;
}
ListIterator<PointMatching> nextRoadMatching = PathUtils.iteratorForNextRoadMatching(current, matchings);
if (!nextRoadMatching.hasNext()) {
return nextRoadMatching;
}
return (isUTurn(matchings.get(current.nextIndex()).getRoad(), matchings.get(nextRoadMatching.nextIndex())
.getRoad())) ? current : nextUTurnRoad(nextRoadMatching, matchings);
}
public static ListIterator<PointMatching> previousUTurnRoad(ListIterator<PointMatching> current, List<PointMatching> matchings) {
if (!current.hasPrevious()) {
return current;
}
ListIterator<PointMatching> previousRoadMatching = PathUtils.iteratorForPreviousRoadMatching(current, matchings);
if (!previousRoadMatching.hasPrevious()) {
return previousRoadMatching;
}
return (isUTurn(matchings.get(current.previousIndex()).getRoad(), matchings.get(previousRoadMatching.previousIndex())
.getRoad())) ? current : previousUTurnRoad(previousRoadMatching, matchings);
}
public static final double getDistanceBetweenPointMatchings(PointMatching matching1, PointMatching matching2) {
if (!matching1.getRoad().equals(matching2.getRoad())) {
throw new IllegalArgumentException("Cannot calculate distance bewteen matchings to different roads!");
}
Road road = matching1.getRoad();
Coordinate point1 = matching1.getPoint();
Coordinate point2 = matching2.getPoint();
LineSegment matching1NearestLine = road.nearestLine(point1);
LineSegment matching2NearestLine = road.nearestLine(point2);
if (matching1NearestLine.equals(matching2NearestLine)) {
return EARTH_DISTANCE_CALCULATOR.distance(road.nearestPoint(point1), road.nearestPoint(point2));
}
double distance = 0.0;
ListIterator<LineSegment> segmentsIterator = road.listIterator();
HashSet<LineSegment> edgeSegments = newHashSet(matching1NearestLine, matching2NearestLine);
LineSegment startSegment = skipLinesOutsideRoute(segmentsIterator, edgeSegments);
distance += (startSegment.equals(matching1NearestLine)) ? EARTH_DISTANCE_CALCULATOR.distance(
road.nearestPoint(point1), startSegment.p1) : EARTH_DISTANCE_CALCULATOR.distance(
road.nearestPoint(point2), startSegment.p1);
LineSegment endSegment;
while (true) {
LineSegment nextSegment = segmentsIterator.next();
if (!edgeSegments.contains(nextSegment)) {
distance += EARTH_DISTANCE_CALCULATOR.distance(nextSegment.p0, nextSegment.p1);
} else {
endSegment = nextSegment;
break;
}
}
distance += (endSegment.equals(matching1NearestLine)) ? EARTH_DISTANCE_CALCULATOR.distance(
road.nearestPoint(point1), endSegment.p0) : EARTH_DISTANCE_CALCULATOR.distance(
road.nearestPoint(point2), endSegment.p0);
return distance;
}
private static LineSegment skipLinesOutsideRoute(ListIterator<LineSegment> linesIterator, Set<LineSegment> edgeLines) {
LineSegment segment = linesIterator.next();
while (!edgeLines.contains(segment)) {
segment = linesIterator.next();
}
linesIterator.previous();
return linesIterator.next();
}
public static double getDistanceBetweenPoints(List<Coordinate> points) {
double distance = 0.0;
if (points.isEmpty()) {
return distance;
}
for (int i = 1; i < points.size(); i++) {
distance += EARTH_DISTANCE_CALCULATOR.distance(points.get(i - 1), points.get(i));
}
return distance;
}
public static boolean pathContainsOneDirectedRoad(Path path) {
int numberOfDirectedSegments = Collections.getNumberOfElementsWithoutAdjacentDuplicates(Lists.transform(
path.getMatchings(), new Function<PointMatching, Road>() {
@Override
public Road apply(PointMatching matching) {
return matching.getRoad();
}
}));
return numberOfDirectedSegments == 1;
}
public static boolean pathContainsOneUndirectedRoad(Path path) {
List<Road> segments = Lists.transform(path.getMatchings(), new Function<PointMatching, Road>() {
@Override
public Road apply(PointMatching pointMatching) {
return pointMatching.getRoad();
}
});
int numberOfUndirectedSegments = Collections.getNumberOfElementsWithoutAdjacentDuplicates(segments,
new Comparator<Road>() {
@Override
public int compare(Road road1, Road road2) {
return road1.getId() - road2.getId();
}
});
return numberOfUndirectedSegments == 1;
}
}