package pl.edu.agh.logic; import static com.google.common.collect.Lists.newArrayList; import static pl.edu.agh.spatial.HaversineDistanceCalculator.EARTH_DISTANCE_CALCULATOR; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.ListIterator; import com.google.common.base.Function; import com.google.common.collect.Lists; import com.vividsolutions.jts.geom.Coordinate; import com.vividsolutions.jts.geom.LineSegment; public class UTurnPathCorrector implements PathCorrector { private RoadNetwork roadNetwork; public UTurnPathCorrector(RoadNetwork roadNetwork) { this.roadNetwork = roadNetwork; } @Override public Path correct(Path path) { if (PathUtils.pathContainsOneUndirectedRoad(path)) { if ((getDistanceBetweenPathPoints(path.getMatchings()) > path.getMatchings().get(0).getRoad().getLength()) && !PathUtils.pathContainsOneDirectedRoad(path)) { return path; } else { return correctPathComposedOfOneUndirectedRoad(path); } } return correctPathWithManyUndirectedRoads(path); } private double getDistanceBetweenPathPoints(List<PointMatching> matchings) { List<Coordinate> pathPoints = Lists.transform(matchings, new Function<PointMatching, Coordinate>() { @Override public Coordinate apply(PointMatching pointMatching) { return pointMatching.getPoint(); } }); return PathUtils.getDistanceBetweenPoints(pathPoints); } private Path correctPathComposedOfOneUndirectedRoad(Path path) { Collection<Road> directedRoadSegments = roadNetwork.getRoadsById(path.getMatchings().get(0).getRoad().getId()); Iterator<Road> segmentInterator = directedRoadSegments.iterator(); Road firstDirectionRoad = segmentInterator.next(); if (!segmentInterator.hasNext()) { return path; } Road secondDirectionRoad = segmentInterator.next(); Coordinate roadBegin = new LineSegment(firstDirectionRoad.getStartPoint(), secondDirectionRoad.getEndPoint()) .midPoint(); Road correctlyDirectedRoad = (EARTH_DISTANCE_CALCULATOR.distance(roadBegin, path.getStartPoint()) < EARTH_DISTANCE_CALCULATOR .distance(roadBegin, path.getEndPoint())) ? firstDirectionRoad : secondDirectionRoad; return createPathComposedOfOneRoad(path, correctlyDirectedRoad); } private Path createPathComposedOfOneRoad(Path path, final Road road) { List<PointMatching> newPathMatchings = Lists.transform(path.getMatchings(), new Function<PointMatching, PointMatching>() { @Override public PointMatching apply(PointMatching matching) { return new PointMatching(matching.getPoint(), road, matching.getTime()); } }); return Path.createPath(newPathMatchings); } private Path correctPathWithManyUndirectedRoads(Path path) { List<PointMatching> matchings = newArrayList(path.getMatchings()); matchings = correctUTurnAtBeginning(matchings); matchings = correctUTurnAtEnd(matchings); return Path.createPath(matchings); } private List<PointMatching> correctUTurnAtBeginning(List<PointMatching> matchings) { ListIterator<PointMatching> firstMatching = matchings.listIterator(); ListIterator<PointMatching> firstNonUTurnMatching = PathUtils.nextNonUTurnRoad(firstMatching, matchings); if (firstMatching.nextIndex() == firstNonUTurnMatching.nextIndex()) { return matchings; } Road correctRoad = matchings.get(firstNonUTurnMatching.previousIndex()).getRoad(); if (getDistanceBetweenPathPoints(matchings.subList(0, firstNonUTurnMatching.nextIndex())) > correctRoad .getLength()) { return matchings; } replaceRoadForMatchings(matchings.subList(0, firstNonUTurnMatching.nextIndex()), correctRoad); return matchings; } private List<PointMatching> correctUTurnAtEnd(List<PointMatching> matchings) { ListIterator<PointMatching> lastMatching = matchings.listIterator(matchings.size()); ListIterator<PointMatching> lastNonUTurnMatching = PathUtils.previousNonUTurnRoad(lastMatching, matchings); if (lastMatching.previousIndex() == lastNonUTurnMatching.previousIndex()) { return matchings; } Road correctRoad = matchings.get(lastNonUTurnMatching.nextIndex()).getRoad(); if (getDistanceBetweenPathPoints(matchings.subList(lastNonUTurnMatching.nextIndex(), matchings.size())) > correctRoad .getLength()) { return matchings; } replaceRoadForMatchings(matchings.subList(lastNonUTurnMatching.nextIndex(), matchings.size()), correctRoad); return matchings; } private List<PointMatching> replaceRoadForMatchings(List<PointMatching> matchings, Road road) { for (int i = 0; i < matchings.size(); i++) { PointMatching oldMatching = matchings.get(i); matchings.set(i, new PointMatching(oldMatching.getPoint(), road, oldMatching.getTime())); } return matchings; } }