2018-10-26 18:07:14 +02:00
|
|
|
package net.osmand.router;
|
|
|
|
|
2018-10-31 17:00:07 +01:00
|
|
|
import java.util.*;
|
2018-10-26 18:07:14 +02:00
|
|
|
|
|
|
|
public class RouteStatistics {
|
|
|
|
|
|
|
|
private static final String UNDEFINED = "undefined";
|
|
|
|
|
2018-11-02 17:33:33 +01:00
|
|
|
private final List<RouteSegmentResult> route;
|
|
|
|
|
|
|
|
private RouteStatistics(List<RouteSegmentResult> route) {
|
|
|
|
this.route = route;
|
2018-10-31 17:00:07 +01:00
|
|
|
}
|
2018-10-26 18:07:14 +02:00
|
|
|
|
2018-10-31 17:00:07 +01:00
|
|
|
public static RouteStatistics newRouteStatistic(List<RouteSegmentResult> route) {
|
2018-11-02 17:33:33 +01:00
|
|
|
return new RouteStatistics(route);
|
2018-10-26 18:07:14 +02:00
|
|
|
}
|
|
|
|
|
2018-11-02 17:33:33 +01:00
|
|
|
public RouteStatistic getRouteSurfaceStatistic() {
|
|
|
|
RouteStatisticComputer statisticComputer = new RouteSurfaceStatisticComputer(route);
|
|
|
|
return statisticComputer.computeStatistic();
|
2018-10-26 18:07:14 +02:00
|
|
|
}
|
|
|
|
|
2018-11-02 17:33:33 +01:00
|
|
|
public RouteStatistic getRouteSmoothnessStatistic() {
|
|
|
|
RouteStatisticComputer statisticComputer = new RouteSmoothnessStatisticComputer(route);
|
|
|
|
return statisticComputer.computeStatistic();
|
2018-10-26 18:07:14 +02:00
|
|
|
}
|
|
|
|
|
2018-11-02 17:33:33 +01:00
|
|
|
public RouteStatistic getRouteClassStatistic() {
|
|
|
|
RouteStatisticComputer statisticComputer = new RouteClassStatisticComputer(route);
|
|
|
|
return statisticComputer.computeStatistic();
|
2018-10-31 17:00:07 +01:00
|
|
|
}
|
|
|
|
|
2018-11-02 17:33:33 +01:00
|
|
|
public RouteStatistic getRouteSteepnessStatistic() {
|
|
|
|
RouteStatisticComputer statisticComputer = new RouteSteepnessStatisticComputer(route);
|
|
|
|
return statisticComputer.computeStatistic();
|
2018-10-26 18:07:14 +02:00
|
|
|
}
|
|
|
|
|
2018-10-31 17:00:07 +01:00
|
|
|
|
2018-10-26 18:07:14 +02:00
|
|
|
private abstract static class RouteStatisticComputer {
|
|
|
|
|
2018-10-31 17:00:07 +01:00
|
|
|
private final List<RouteSegmentResult> route;
|
|
|
|
|
|
|
|
public RouteStatisticComputer(List<RouteSegmentResult> route) {
|
|
|
|
this.route = new ArrayList<>(route);
|
|
|
|
}
|
|
|
|
|
2018-11-02 17:33:33 +01:00
|
|
|
private Map<String, RouteSegmentAttribute> makePartition(List<RouteSegmentAttribute> routeAttributes) {
|
|
|
|
Map<String, RouteSegmentAttribute> partition = new HashMap<>();
|
|
|
|
for (RouteSegmentAttribute attribute : routeAttributes) {
|
|
|
|
String key = attribute.getAttribute();
|
|
|
|
RouteSegmentAttribute pattr = partition.get(key);
|
|
|
|
if (pattr == null) {
|
|
|
|
pattr = new RouteSegmentAttribute(attribute.getIndex(), attribute.getAttribute());
|
|
|
|
partition.put(key, pattr);
|
|
|
|
}
|
|
|
|
pattr.incrementDistanceBy(attribute.getDistance());
|
|
|
|
}
|
|
|
|
return partition;
|
|
|
|
}
|
|
|
|
|
|
|
|
private float computeTotalDistance(List<RouteSegmentAttribute> attributes) {
|
|
|
|
float distance = 0f;
|
|
|
|
for (RouteSegmentAttribute attribute : attributes) {
|
|
|
|
distance += attribute.getDistance();
|
|
|
|
}
|
|
|
|
return distance;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected List<RouteSegmentResult> getRoute() {
|
2018-10-31 17:00:07 +01:00
|
|
|
return route;
|
|
|
|
}
|
|
|
|
|
2018-11-02 17:33:33 +01:00
|
|
|
protected List<RouteSegmentAttribute> processRoute() {
|
2018-10-26 18:07:14 +02:00
|
|
|
int index = 0;
|
2018-11-02 17:33:33 +01:00
|
|
|
List<RouteSegmentAttribute> routes = new ArrayList<>();
|
2018-10-26 18:07:14 +02:00
|
|
|
String prev = null;
|
2018-10-31 17:00:07 +01:00
|
|
|
for (RouteSegmentResult segment : getRoute()) {
|
2018-10-26 18:07:14 +02:00
|
|
|
String current = getAttribute(segment);
|
|
|
|
if (current == null) {
|
|
|
|
current = UNDEFINED;
|
|
|
|
}
|
|
|
|
if (prev != null && !prev.equals(current)) {
|
|
|
|
index++;
|
|
|
|
}
|
2018-11-02 17:33:33 +01:00
|
|
|
if (index >= routes.size()) {
|
|
|
|
routes.add(new RouteSegmentAttribute(index, current));
|
2018-10-26 18:07:14 +02:00
|
|
|
}
|
2018-11-02 17:33:33 +01:00
|
|
|
RouteSegmentAttribute surface = routes.get(index);
|
2018-10-26 18:07:14 +02:00
|
|
|
surface.incrementDistanceBy(segment.getDistance());
|
|
|
|
prev = current;
|
|
|
|
}
|
2018-11-02 17:33:33 +01:00
|
|
|
return routes;
|
|
|
|
}
|
|
|
|
|
|
|
|
public RouteStatistic computeStatistic() {
|
|
|
|
List<RouteSegmentAttribute> routeAttributes = processRoute();
|
|
|
|
Map<String, RouteSegmentAttribute> partition = makePartition(routeAttributes);
|
|
|
|
float totalDistance = computeTotalDistance(routeAttributes);
|
|
|
|
return new RouteStatisticImpl(routeAttributes, partition, totalDistance);
|
2018-10-26 18:07:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public abstract String getAttribute(RouteSegmentResult segment);
|
|
|
|
|
2018-11-02 17:33:33 +01:00
|
|
|
private static class RouteStatisticImpl implements RouteStatistic {
|
|
|
|
|
|
|
|
private final List<RouteStatistics.RouteSegmentAttribute> elements;
|
|
|
|
private final Map<String, RouteStatistics.RouteSegmentAttribute> partition;
|
|
|
|
private final float totalDistance;
|
|
|
|
|
|
|
|
public RouteStatisticImpl(List<RouteSegmentAttribute> elements,
|
|
|
|
Map<String, RouteSegmentAttribute> partition,
|
|
|
|
float totalDistance) {
|
|
|
|
this.elements = elements;
|
|
|
|
this.partition = partition;
|
|
|
|
this.totalDistance = totalDistance;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public float getTotalDistance() {
|
|
|
|
return totalDistance;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public List<RouteSegmentAttribute> getElements() {
|
|
|
|
return new ArrayList<>(elements);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Map<String, RouteSegmentAttribute> getPartition() {
|
|
|
|
return new HashMap<>(partition);
|
|
|
|
}
|
|
|
|
}
|
2018-10-26 18:07:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-11-02 17:33:33 +01:00
|
|
|
|
|
|
|
private static class RouteSurfaceStatisticComputer extends RouteStatisticComputer {
|
|
|
|
|
|
|
|
public RouteSurfaceStatisticComputer(List<RouteSegmentResult> route) {
|
2018-10-31 17:00:07 +01:00
|
|
|
super(route);
|
|
|
|
}
|
|
|
|
|
2018-10-26 18:07:14 +02:00
|
|
|
@Override
|
|
|
|
public String getAttribute(RouteSegmentResult segment) {
|
2018-10-31 17:00:07 +01:00
|
|
|
String segmentSurface = segment.getSurface();
|
|
|
|
if (segmentSurface == null) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
for (RoadSurface roadSurface : RoadSurface.values()) {
|
|
|
|
if (roadSurface.contains(segmentSurface)) {
|
|
|
|
return roadSurface.name().toLowerCase();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return null;
|
2018-10-26 18:07:14 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-02 17:33:33 +01:00
|
|
|
private static class RouteSmoothnessStatisticComputer extends RouteStatisticComputer {
|
2018-10-26 18:07:14 +02:00
|
|
|
|
2018-11-02 17:33:33 +01:00
|
|
|
public RouteSmoothnessStatisticComputer(List<RouteSegmentResult> route) {
|
2018-10-31 17:00:07 +01:00
|
|
|
super(route);
|
|
|
|
}
|
|
|
|
|
2018-10-26 18:07:14 +02:00
|
|
|
@Override
|
|
|
|
public String getAttribute(RouteSegmentResult segment) {
|
|
|
|
return segment.getSmoothness();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-11-02 17:33:33 +01:00
|
|
|
private static class RouteClassStatisticComputer extends RouteStatisticComputer {
|
|
|
|
|
|
|
|
public RouteClassStatisticComputer(List<RouteSegmentResult> route) {
|
2018-10-31 17:00:07 +01:00
|
|
|
super(route);
|
|
|
|
}
|
|
|
|
|
2018-10-26 18:07:14 +02:00
|
|
|
@Override
|
|
|
|
public String getAttribute(RouteSegmentResult segment) {
|
2018-10-31 17:00:07 +01:00
|
|
|
String segmentClass = segment.getHighway();
|
|
|
|
if (segmentClass == null) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
for (RoadClass roadClass : RoadClass.values()) {
|
|
|
|
if (roadClass.contains(segmentClass)) {
|
|
|
|
return roadClass.name().toLowerCase();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-11-02 17:33:33 +01:00
|
|
|
private static class RouteSteepnessStatisticComputer extends RouteStatisticComputer {
|
|
|
|
|
|
|
|
public RouteSteepnessStatisticComputer(List<RouteSegmentResult> route) {
|
2018-10-31 17:00:07 +01:00
|
|
|
super(route);
|
|
|
|
}
|
|
|
|
|
2018-11-02 17:33:33 +01:00
|
|
|
private float computeIncline(float prevHeight, float currHeight, float distance) {
|
|
|
|
float incline = (currHeight - prevHeight) / distance;
|
|
|
|
if (incline > 30f || incline < -30f) {
|
|
|
|
throw new IllegalArgumentException("Invalid incline " + incline);
|
|
|
|
}
|
|
|
|
if (Float.isInfinite(incline) || Float.isNaN(incline)) {
|
|
|
|
incline = 0f;
|
|
|
|
}
|
|
|
|
return incline * 100;
|
2018-10-31 17:00:07 +01:00
|
|
|
}
|
|
|
|
|
2018-11-02 17:33:33 +01:00
|
|
|
private List<Incline> computeSegmentInclines() {
|
|
|
|
List<Incline> inclines = new ArrayList<>();
|
2018-10-31 17:00:07 +01:00
|
|
|
for (RouteSegmentResult segment : getRoute()) {
|
|
|
|
float[] heights = segment.getHeightValues();
|
|
|
|
if (heights.length == 0) {
|
2018-11-02 17:33:33 +01:00
|
|
|
Incline incline = new Incline(0, segment.getDistance());
|
|
|
|
inclines.add(incline);
|
2018-10-31 17:00:07 +01:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
for (int index = 1; index < heights.length / 2; index++) {
|
|
|
|
int prevHeightIndex = 2 * (index - 1) + 1;
|
|
|
|
int currHeightIndex = 2 * index + 1;
|
|
|
|
int distanceBetweenHeightsIndex = 2 * index;
|
|
|
|
float prevHeight = heights[prevHeightIndex];
|
|
|
|
float currHeight = heights[currHeightIndex];
|
|
|
|
float distanceBetweenHeights = heights[distanceBetweenHeightsIndex];
|
2018-11-02 17:33:33 +01:00
|
|
|
float computedIncline = computeIncline(prevHeight, currHeight, distanceBetweenHeights);
|
|
|
|
Incline incline = new Incline(computedIncline, distanceBetweenHeights);
|
|
|
|
inclines.add(incline);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return inclines;
|
|
|
|
}
|
2018-10-31 17:00:07 +01:00
|
|
|
|
2018-11-02 17:33:33 +01:00
|
|
|
@Override
|
|
|
|
public List<RouteSegmentAttribute> processRoute() {
|
|
|
|
List<RouteSegmentAttribute> routeInclines = new ArrayList<>();
|
|
|
|
int index = 0;
|
|
|
|
String prev = null;
|
|
|
|
for (Incline incline : computeSegmentInclines()) {
|
|
|
|
String current = incline.getBoundariesAsString();
|
|
|
|
if (prev != null && !prev.equals(current)) {
|
|
|
|
index++;
|
|
|
|
}
|
|
|
|
if (index >= routeInclines.size()) {
|
|
|
|
routeInclines.add(new RouteSegmentAttribute(index, current));
|
2018-10-31 17:00:07 +01:00
|
|
|
}
|
2018-11-02 17:33:33 +01:00
|
|
|
RouteSegmentAttribute routeIncline = routeInclines.get(index);
|
|
|
|
routeIncline.incrementDistanceBy(incline.getDistance());
|
|
|
|
prev = current;
|
2018-10-31 17:00:07 +01:00
|
|
|
}
|
|
|
|
return routeInclines;
|
|
|
|
}
|
|
|
|
|
2018-11-02 17:33:33 +01:00
|
|
|
@Override
|
|
|
|
public String getAttribute(RouteSegmentResult segment) {
|
|
|
|
return null;
|
2018-10-26 18:07:14 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-11-02 17:33:33 +01:00
|
|
|
|
2018-10-26 18:07:14 +02:00
|
|
|
public static class RouteSegmentAttribute {
|
|
|
|
|
|
|
|
private final int index;
|
|
|
|
|
|
|
|
private final String attribute;
|
|
|
|
|
|
|
|
private float distance;
|
|
|
|
|
|
|
|
public RouteSegmentAttribute(int index, String attribute) {
|
|
|
|
this.index = index;
|
|
|
|
this.attribute = attribute;
|
|
|
|
}
|
|
|
|
|
|
|
|
public int getIndex() {
|
|
|
|
return index;
|
|
|
|
}
|
|
|
|
|
|
|
|
public String getAttribute() {
|
|
|
|
return attribute;
|
|
|
|
}
|
|
|
|
|
|
|
|
public float getDistance() {
|
|
|
|
return distance;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void incrementDistanceBy(float distance) {
|
|
|
|
this.distance += distance;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public String toString() {
|
|
|
|
return "{" +
|
|
|
|
"index=" + index +
|
|
|
|
", attribute='" + attribute + '\'' +
|
|
|
|
", distance=" + distance +
|
|
|
|
'}';
|
|
|
|
}
|
|
|
|
}
|
2018-10-31 17:00:07 +01:00
|
|
|
|
2018-11-02 17:33:33 +01:00
|
|
|
|
|
|
|
|
|
|
|
private static class Incline {
|
2018-10-31 17:00:07 +01:00
|
|
|
|
|
|
|
private static final float MAX_INCLINE = 30;
|
|
|
|
private static final float MIN_INCLINE = -30;
|
|
|
|
private static final float STEP = 3;
|
|
|
|
private static final int NUM;
|
|
|
|
private static final float[] INTERVALS;
|
|
|
|
|
|
|
|
static {
|
|
|
|
NUM = (int) ((MAX_INCLINE - MIN_INCLINE) / STEP) + 1;
|
|
|
|
INTERVALS = new float[NUM];
|
|
|
|
for (int k = 0; k < INTERVALS.length; k++) {
|
|
|
|
INTERVALS[k] = STEP * k + MIN_INCLINE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void determineBoundaries(float incline) {
|
|
|
|
for (int pos = 1; pos < INTERVALS.length; pos++) {
|
|
|
|
float lower = INTERVALS[pos - 1];
|
|
|
|
float upper = INTERVALS[pos];
|
|
|
|
if (incline >= lower && incline < upper) {
|
|
|
|
this.lowerBoundary = lower;
|
|
|
|
this.upperBoundary = upper;
|
|
|
|
this.middlePoint = (upperBoundary + lowerBoundary) / 2f;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-02 17:33:33 +01:00
|
|
|
private float upperBoundary;
|
|
|
|
private float lowerBoundary;
|
|
|
|
private float middlePoint;
|
|
|
|
|
|
|
|
private final float inclineValue;
|
|
|
|
private final float distance;
|
|
|
|
|
|
|
|
public Incline(float inclineValue, float distance) {
|
|
|
|
this.inclineValue = inclineValue;
|
|
|
|
this.distance = distance;
|
|
|
|
determineBoundaries(inclineValue);
|
|
|
|
if (upperBoundary == lowerBoundary) {
|
|
|
|
throw new IllegalArgumentException("Invalid boundaries");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-31 17:00:07 +01:00
|
|
|
public float getUpperBoundary() {
|
|
|
|
return upperBoundary;
|
|
|
|
}
|
|
|
|
|
|
|
|
public float getLowerBoundary() {
|
|
|
|
return lowerBoundary;
|
|
|
|
}
|
|
|
|
|
2018-11-02 17:33:33 +01:00
|
|
|
public float getMiddlePoint() {
|
|
|
|
return middlePoint;
|
2018-10-31 17:00:07 +01:00
|
|
|
}
|
|
|
|
|
2018-11-02 17:33:33 +01:00
|
|
|
public float getValue() {
|
|
|
|
return inclineValue;
|
2018-10-31 17:00:07 +01:00
|
|
|
}
|
|
|
|
|
2018-11-02 17:33:33 +01:00
|
|
|
public float getDistance() {
|
|
|
|
return distance;
|
|
|
|
}
|
|
|
|
|
|
|
|
public String getBoundariesAsString() {
|
|
|
|
return String.format("%.2f|%.2f", getLowerBoundary(), getUpperBoundary());
|
2018-10-31 17:00:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public String toString() {
|
2018-11-02 17:33:33 +01:00
|
|
|
return "Incline{" +
|
|
|
|
"upperBoundary=" + upperBoundary +
|
2018-10-31 17:00:07 +01:00
|
|
|
", lowerBoundary=" + lowerBoundary +
|
|
|
|
", middlePoint=" + middlePoint +
|
2018-11-02 17:33:33 +01:00
|
|
|
", incline=" + inclineValue +
|
|
|
|
", distance=" + distance +
|
2018-10-31 17:00:07 +01:00
|
|
|
'}';
|
|
|
|
}
|
|
|
|
}
|
2018-10-26 18:07:14 +02:00
|
|
|
}
|