/*
This file is part of RouteConverter.
RouteConverter is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
RouteConverter is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with RouteConverter; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
Copyright (C) 2007 Christian Pesch. All Rights Reserved.
*/
package slash.navigation.base;
import slash.common.type.CompactCalendar;
import slash.navigation.common.NavigationPosition;
import java.util.List;
import static java.lang.Math.abs;
import static java.lang.System.arraycopy;
import static slash.common.io.Transfer.isEmpty;
import static slash.common.type.CompactCalendar.fromMillis;
/**
* Provides {@link NavigationPosition} calculation functionality.
*
* @author Christian Pesch, Malte Neumann
*/
public class RouteCalculations {
private static int[] douglasPeuckerSimplify(List<? extends NavigationPosition> positions, int from, int to, double threshold) {
// find the point with the maximum distance
NavigationPosition pointA = positions.get(from);
NavigationPosition pointB = positions.get(to);
int maximumDistanceIndex = -1;
double maximumDistance = 0.0;
for (int i = from + 1; i < to; i++) {
NavigationPosition position = positions.get(i);
if (position.hasCoordinates()) {
double distance = abs(position.calculateOrthogonalDistance(pointA, pointB));
if (distance > maximumDistance) {
maximumDistance = distance;
maximumDistanceIndex = i;
}
}
}
// if maximum distance is greater than threshold, recursively simplify
if ((maximumDistanceIndex != -1) && (maximumDistance > threshold)) {
int[] res1 = douglasPeuckerSimplify(positions, from, maximumDistanceIndex, threshold);
int[] res2 = douglasPeuckerSimplify(positions, maximumDistanceIndex, to, threshold);
int[] result = new int[res1.length - 1 + res2.length];
arraycopy(res1, 0, result, 0, res1.length - 1);
arraycopy(res2, 0, result, res1.length - 1, res2.length);
return result;
} else
return new int[]{from, to};
}
/**
* Search the significant positions with the Douglas-Peucker-Algorithm.
* <p/>
* http://de.wikipedia.org/wiki/Douglas-Peucker-Algorithmus
*
* @param positions the original list of positions
* @param threshold determines the threshold for significance in meter
* @return an array of indices to the original list of positions with the significant positions
*/
public static int[] getSignificantPositions(List<? extends NavigationPosition> positions, double threshold) {
if (positions.size() == 0)
return new int[0];
else if (positions.size() == 1)
return new int[]{0};
else
return douglasPeuckerSimplify(positions, 0, positions.size() - 1, threshold);
}
public static CompactCalendar extrapolateTime(NavigationPosition position, NavigationPosition predecessor, NavigationPosition beforePredecessor) {
if (!predecessor.hasTime() || !beforePredecessor.hasTime())
return null;
long timeDelta = abs(beforePredecessor.calculateTime(predecessor));
Double distanceDelta = beforePredecessor.calculateDistance(predecessor);
if (isEmpty(distanceDelta))
return null;
Double distance = predecessor.calculateDistance(position);
if (isEmpty(distance))
return null;
long time = (long) (predecessor.getTime().getTimeInMillis() + (double) timeDelta * (distance / distanceDelta));
return fromMillis(time);
}
public static CompactCalendar intrapolateTime(NavigationPosition position, NavigationPosition predecessor, NavigationPosition successor) {
if (!predecessor.hasTime() || !successor.hasTime())
return null;
long timeDelta = abs(predecessor.calculateTime(successor));
Double distanceToPredecessor = predecessor.calculateDistance(position);
if (isEmpty(distanceToPredecessor))
return null;
Double distanceToSuccessor = position.calculateDistance(successor);
if (isEmpty(distanceToSuccessor))
return null;
Double distanceRatio = distanceToPredecessor / (distanceToPredecessor + distanceToSuccessor);
long time = (long) (predecessor.getTime().getTimeInMillis() + (double) timeDelta * distanceRatio);
return fromMillis(time);
}
public static Wgs84Position asWgs84Position(double longitude, double latitude) {
return asWgs84Position(longitude, latitude, null);
}
public static Wgs84Position asWgs84Position(Double longitude, Double latitude, String description) {
return new Wgs84Position(longitude, latitude, null, null, null, description);
}
}