package com.github.pfichtner.jrunalyser.base.data.stat;
import static com.github.pfichtner.jrunalyser.base.data.stat.Orderings.elevationOrdering;
import static com.github.pfichtner.jrunalyser.base.data.stat.Orderings.speedOrdering;
import static com.github.pfichtner.jrunalyser.base.data.stat.Predicates.hasValidSpeed;
import static com.github.pfichtner.jrunalyser.base.data.stat.Predicates.LinkedWayPoints.hasLink;
import static com.github.pfichtner.jrunalyser.base.data.stat.Predicates.LinkedWayPoints.hasNegativeElevationdiff;
import static com.github.pfichtner.jrunalyser.base.data.stat.Predicates.LinkedWayPoints.hasPositiveElevationdiff;
import static com.google.common.collect.Iterables.filter;
import static com.google.common.collect.Iterables.transform;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import com.github.pfichtner.jrunalyser.base.data.DefaultDistance;
import com.github.pfichtner.jrunalyser.base.data.DefaultDuration;
import com.github.pfichtner.jrunalyser.base.data.DefaultSpeed;
import com.github.pfichtner.jrunalyser.base.data.Distance;
import com.github.pfichtner.jrunalyser.base.data.DistanceUnit;
import com.github.pfichtner.jrunalyser.base.data.Duration;
import com.github.pfichtner.jrunalyser.base.data.LinkedTrackPoint;
import com.github.pfichtner.jrunalyser.base.data.Speed;
import com.github.pfichtner.jrunalyser.base.util.MovingAverageIterator;
import com.github.pfichtner.jrunalyser.base.util.Primitives;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.primitives.Doubles;
public class StatCalculators {
public interface StatCalculator<T> {
T calculate(Iterable<? extends LinkedTrackPoint> wayPoints);
}
// -----------------------------------------------------------------------------------
public static StatCalculator<? extends LinkedTrackPoint> minEle() {
return new StatCalculator<LinkedTrackPoint>() {
@Override
public LinkedTrackPoint calculate(
Iterable<? extends LinkedTrackPoint> wayPoints) {
Iterator<? extends LinkedTrackPoint> it = waypointsWithEle(
wayPoints).iterator();
return it.hasNext() ? elevationOrdering.min(it) : null;
}
};
}
public static StatCalculator<? extends LinkedTrackPoint> maxEle() {
return new StatCalculator<LinkedTrackPoint>() {
@Override
public LinkedTrackPoint calculate(
Iterable<? extends LinkedTrackPoint> wayPoints) {
Iterator<? extends LinkedTrackPoint> it = waypointsWithEle(
wayPoints).iterator();
return it.hasNext() ? elevationOrdering.max(it) : null;
}
};
}
private static Iterable<? extends LinkedTrackPoint> waypointsWithEle(
Iterable<? extends LinkedTrackPoint> wayPoints) {
return filter(wayPoints, Predicates.WayPoints.hasElevation());
}
// -----------------------------------------------------------------------------------
public static StatCalculator<Integer> ascent() {
return new StatCalculator<Integer>() {
@Override
public Integer calculate(
Iterable<? extends LinkedTrackPoint> wayPoints) {
return Integer.valueOf(sumElevations(wayPoints,
hasPositiveElevationdiff()));
}
};
}
public static StatCalculator<Integer> descent() {
return new StatCalculator<Integer>() {
@Override
public Integer calculate(
Iterable<? extends LinkedTrackPoint> wayPoints) {
return Integer.valueOf(0 - sumElevations(wayPoints,
hasNegativeElevationdiff()));
}
};
}
private static int sumElevations(
Iterable<? extends LinkedTrackPoint> wayPoints,
Predicate<Number> predicate) {
List<Double> values = Lists.newArrayList(new MovingAverageIterator(
transform(wayPoints, Functions.WayPoints.elevation())
.iterator(), 3));
return (int) Primitives.sum(Doubles.toArray(values), predicate);
}
// -----------------------------------------------------------------------------------
public static StatCalculator<? extends LinkedTrackPoint> maxSpeed() {
return new StatCalculator<LinkedTrackPoint>() {
@Override
public LinkedTrackPoint calculate(
Iterable<? extends LinkedTrackPoint> wayPoints) {
Iterable<? extends LinkedTrackPoint> validSpeedValues = filter(
wayPoints, hasValidSpeed);
return Iterables.isEmpty(validSpeedValues) ? null
: speedOrdering.max(validSpeedValues);
}
};
}
public static StatCalculator<? extends LinkedTrackPoint> minSpeed() {
return new StatCalculator<LinkedTrackPoint>() {
@Override
public LinkedTrackPoint calculate(
Iterable<? extends LinkedTrackPoint> wayPoints) {
Iterable<? extends LinkedTrackPoint> validSpeedValues = filter(
wayPoints, hasValidSpeed);
return Iterables.isEmpty(validSpeedValues) ? null
: speedOrdering.min(validSpeedValues);
}
};
}
public static StatCalculator<Speed> avgSpeed(
final DistanceUnit distanceUnit, final TimeUnit timeUnit) {
return new StatCalculator<Speed>() {
@Override
public Speed calculate(Iterable<? extends LinkedTrackPoint> wayPoints) {
return new DefaultSpeed(distance(distanceUnit).calculate(
wayPoints), duration(timeUnit).calculate(wayPoints))
.convert(distanceUnit, timeUnit);
}
};
}
// -----------------------------------------------------------------------------------
public static StatCalculator<Distance> distance(
final DistanceUnit distanceUnit) {
return new StatCalculator<Distance>() {
@Override
public Distance calculate(
Iterable<? extends LinkedTrackPoint> wayPoints) {
double[] distances = Doubles.toArray(Lists
.newArrayList(transform(filter(wayPoints, hasLink()),
createDistanceFunc(distanceUnit))));
return DefaultDistance.of(Primitives.sum(distances),
distanceUnit);
}
private Function<LinkedTrackPoint, Double> createDistanceFunc(
final DistanceUnit distanceUnit) {
return new Function<LinkedTrackPoint, Double>() {
public Double apply(LinkedTrackPoint wp) {
return Double.valueOf(wp.getLink().getDistance()
.getValue(distanceUnit));
}
};
}
};
}
// -----------------------------------------------------------------------------------
public static StatCalculator<Duration> duration(final TimeUnit timeUnit) {
return new StatCalculator<Duration>() {
@Override
public Duration calculate(
Iterable<? extends LinkedTrackPoint> wayPoints) {
double[] durations = Doubles.toArray(Lists
.newArrayList(transform(filter(wayPoints, hasLink()),
createTimeFunc(timeUnit))));
return DefaultDuration.of(Primitives.sum(durations), timeUnit);
}
private Function<LinkedTrackPoint, Double> createTimeFunc(
final TimeUnit timeUnit) {
return new Function<LinkedTrackPoint, Double>() {
public Double apply(LinkedTrackPoint wp) {
return Double.valueOf(wp.getLink().getDuration()
.getValue(timeUnit));
}
};
}
};
}
private static final Map<StatType, StatCalculator<?>> calculators = createMap();
public static StatCalculator<?> getCalculator(StatType statType) {
return calculators.get(statType);
}
private static Map<StatType, StatCalculator<?>> createMap() {
return ImmutableMap.<StatType, StatCalculator<?>> builder()
.put(StatType.MAX_ELE, maxEle())
.put(StatType.MIN_ELE, minEle()).put(StatType.ASCENT, ascent())
.put(StatType.DESCENT, descent())
.put(StatType.MAX_SPEED, maxSpeed())
.put(StatType.MIN_SPEED, minSpeed()).build();
}
}