diff --git a/OsmAnd-java/src/main/java/net/osmand/router/RoutePlannerFrontEnd.java b/OsmAnd-java/src/main/java/net/osmand/router/RoutePlannerFrontEnd.java index c0df50a35e..cad29d75e3 100644 --- a/OsmAnd-java/src/main/java/net/osmand/router/RoutePlannerFrontEnd.java +++ b/OsmAnd-java/src/main/java/net/osmand/router/RoutePlannerFrontEnd.java @@ -204,15 +204,6 @@ public class RoutePlannerFrontEnd { makeStartEndPointsPrecise(res, start, end, intermediates); if (res != null) { new RouteResultPreparation().printResults(ctx, start, end, res); - RouteStatistics routeStatistics = RouteStatistics.calculate(res); - System.out.println("Smoothness"); - System.out.println(routeStatistics.getRouteSmoothnessStatistic()); - - System.out.println("Surface"); - System.out.println(routeStatistics.getRouteSurfaceStatistic()); - - System.out.println("Highway"); - System.out.println(routeStatistics.getRouteClassStatistic()); } return res; } diff --git a/OsmAnd-java/src/main/java/net/osmand/router/RouteSegmentResult.java b/OsmAnd-java/src/main/java/net/osmand/router/RouteSegmentResult.java index fae75bf21a..7403e7ee81 100644 --- a/OsmAnd-java/src/main/java/net/osmand/router/RouteSegmentResult.java +++ b/OsmAnd-java/src/main/java/net/osmand/router/RouteSegmentResult.java @@ -49,7 +49,7 @@ public class RouteSegmentResult { res[2 * k] = 0; } else { if(ind < pf.length) { - res[2 * k] = pf[k]; + res[2 * k] = pf[ind]; } } if(ind < pf.length) { diff --git a/OsmAnd-java/src/main/java/net/osmand/router/RouteStatistics.java b/OsmAnd-java/src/main/java/net/osmand/router/RouteStatistics.java index da57075c4e..a3f1ef83f4 100644 --- a/OsmAnd-java/src/main/java/net/osmand/router/RouteStatistics.java +++ b/OsmAnd-java/src/main/java/net/osmand/router/RouteStatistics.java @@ -1,53 +1,72 @@ package net.osmand.router; -import java.util.ArrayList; -import java.util.List; +import java.util.*; public class RouteStatistics { private static final String UNDEFINED = "undefined"; - private final List routeSurfaceStatistic; - private final List routeSmoothnessStatistic; - private final List routeClassStatistic; + private final RouteStatisticComputer routeSurfaceStatisticComputer; + private final RouteStatisticComputer routeSmoothnessStatisticComputer; + private final RouteStatisticComputer routeClassStatisticComputer; + private final RouteStatisticComputer routeSteepnessStatisticComputer; - public RouteStatistics(List routeSurfaceStatistic, - List routeSmoothnessStatistic, - List routeClassStatistic) { - this.routeSurfaceStatistic = routeSurfaceStatistic; - this.routeSmoothnessStatistic = routeSmoothnessStatistic; - this.routeClassStatistic = routeClassStatistic; + + private RouteStatistics(RouteStatisticComputer routeSurfaceStatisticComputer, + RouteStatisticComputer routeSmoothnessStatisticComputer, + RouteStatisticComputer routeClassStatisticComputer, + RouteStatisticComputer routeSteepnessStatisticComputer) { + this.routeSurfaceStatisticComputer = routeSurfaceStatisticComputer; + this.routeSmoothnessStatisticComputer = routeSmoothnessStatisticComputer; + this.routeClassStatisticComputer = routeClassStatisticComputer; + this.routeSteepnessStatisticComputer = routeSteepnessStatisticComputer; } - public static RouteStatistics calculate(List route) { - RouteStatisticComputer routeSurfaceStatisticComputer = new RouteSegmentSurfaceStatisticComputer(); - RouteStatisticComputer routeSmoothnessStatisticComputer = new RouteSegmentSmoothnessStatisticComputer(); - RouteStatisticComputer routeClassStatisticComputer = new RouteSegmentClassStatisticComputer(); - - return new RouteStatistics(routeSurfaceStatisticComputer.computeStatistic(route), - routeSmoothnessStatisticComputer.computeStatistic(route), - routeClassStatisticComputer.computeStatistic(route)); + public static RouteStatistics newRouteStatistic(List route) { + RouteStatisticComputer routeSurfaceStatisticComputer = new RouteSegmentSurfaceStatisticComputer(route); + RouteStatisticComputer routeSmoothnessStatisticComputer = new RouteSegmentSmoothnessStatisticComputer(route); + RouteStatisticComputer routeClassStatisticComputer = new RouteSegmentClassStatisticComputer(route); + RouteStatisticComputer routeSteepnessStatisticComputer = new RouteSegmentSteepnessStatisticComputer(route); + return new RouteStatistics(routeSurfaceStatisticComputer, + routeSmoothnessStatisticComputer, + routeClassStatisticComputer, + routeSteepnessStatisticComputer); } public List getRouteSurfaceStatistic() { - return routeSurfaceStatistic; + return routeSurfaceStatisticComputer.computeStatistic(); } public List getRouteSmoothnessStatistic() { - return routeSmoothnessStatistic; + return routeSmoothnessStatisticComputer.computeStatistic(); } public List getRouteClassStatistic() { - return routeClassStatistic; + return routeClassStatisticComputer.computeStatistic(); } + public List getRouteSteepnessStatistic() { + return routeSteepnessStatisticComputer.computeStatistic(); + } + + private abstract static class RouteStatisticComputer { - protected List computeStatistic(List segments) { + private final List route; + + public RouteStatisticComputer(List route) { + this.route = new ArrayList<>(route); + } + + public List getRoute() { + return route; + } + + protected List computeStatistic() { int index = 0; List routeSurfaces = new ArrayList<>(); String prev = null; - for (RouteSegmentResult segment : segments) { + for (RouteSegmentResult segment : getRoute()) { String current = getAttribute(segment); if (current == null) { current = UNDEFINED; @@ -71,14 +90,31 @@ public class RouteStatistics { private static class RouteSegmentSurfaceStatisticComputer extends RouteStatisticComputer { + public RouteSegmentSurfaceStatisticComputer(List route) { + super(route); + } + @Override public String getAttribute(RouteSegmentResult segment) { - return segment.getSurface(); + String segmentSurface = segment.getSurface(); + if (segmentSurface == null) { + return null; + } + for (RoadSurface roadSurface : RoadSurface.values()) { + if (roadSurface.contains(segmentSurface)) { + return roadSurface.name().toLowerCase(); + } + } + return null; } } private static class RouteSegmentSmoothnessStatisticComputer extends RouteStatisticComputer { + public RouteSegmentSmoothnessStatisticComputer(List route) { + super(route); + } + @Override public String getAttribute(RouteSegmentResult segment) { return segment.getSmoothness(); @@ -87,9 +123,81 @@ public class RouteStatistics { private static class RouteSegmentClassStatisticComputer extends RouteStatisticComputer { + public RouteSegmentClassStatisticComputer(List route) { + super(route); + } + @Override public String getAttribute(RouteSegmentResult segment) { - return segment.getHighway(); + String segmentClass = segment.getHighway(); + if (segmentClass == null) { + return null; + } + for (RoadClass roadClass : RoadClass.values()) { + if (roadClass.contains(segmentClass)) { + return roadClass.name().toLowerCase(); + } + } + return null; + } + } + + private static class RouteSegmentSteepnessStatisticComputer extends RouteStatisticComputer { + + public RouteSegmentSteepnessStatisticComputer(List route) { + super(route); + } + + @Override + public String getAttribute(RouteSegmentResult segment) { + return null; + } + + @Override + public List computeStatistic() { + List routeInclines = new ArrayList<>(); + int inclineIndex = 0; + for (RouteSegmentResult segment : getRoute()) { + float[] heights = segment.getHeightValues(); + if (heights.length == 0) { + RouteSegmentIncline routeIncline = new RouteSegmentIncline(inclineIndex++); + routeIncline.mayJoin(0); + routeIncline.incrementDistanceBy(segment.getDistance()); + routeInclines.add(routeIncline); + 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]; + float incline = computeIncline(prevHeight, currHeight, distanceBetweenHeights); + + if (inclineIndex >= routeInclines.size()) { + routeInclines.add(new RouteSegmentIncline(inclineIndex)); + } + + RouteSegmentIncline routeIncline = (RouteSegmentIncline) routeInclines.get(inclineIndex); + + if (routeIncline.mayJoin(incline)) { + routeIncline.addIncline(incline); + routeIncline.incrementDistanceBy(distanceBetweenHeights); + } else { + inclineIndex++; + } + } + } + return routeInclines; + } + + private float computeIncline(float prevHeight, float currHeight, float distance) { + float incline = (currHeight - prevHeight) / distance; + if (Float.isInfinite(incline) || Float.isNaN(incline)) { + incline = 0f; + } + return incline * 100; } } @@ -132,4 +240,135 @@ public class RouteStatistics { '}'; } } + + public static class RouteSegmentIncline extends RouteSegmentAttribute { + + 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 final List inclines = new ArrayList<>(); + private float upperBoundary; + private float lowerBoundary; + private float middlePoint; + + public RouteSegmentIncline(int index) { + super(index,"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; + } + } + } + + public float getUpperBoundary() { + return upperBoundary; + } + + public float getLowerBoundary() { + return lowerBoundary; + } + + public boolean mayJoin(float incline) { + if (lowerBoundary == upperBoundary) { + determineBoundaries(incline); + } + return incline >= lowerBoundary && incline < upperBoundary; + } + + public void addIncline(float incline) { + inclines.add(incline); + } + + public float getMiddlePoint() { + return this.middlePoint; + } + + @Override + public String toString() { + return "RouteSegmentIncline{" + + "index=" + getIndex() + + ", distance=" + getDistance() + + ", inclines=" + inclines + + ", upperBoundary=" + upperBoundary + + ", lowerBoundary=" + lowerBoundary + + ", middlePoint=" + middlePoint + + '}'; + } + } + + public enum RoadSurface { + PAVED("paved"), + UNPAVED("unpaved"), + ASPHALT("asphalt"), + CONCRETE("concrete"), + COMPACTED("compacted"), + GRAVEL("gravel"), + FINE_GRAVEL("fine_gravel"), + PAVING_STONES("paving_stones"), + SETT("sett"), + COBBLESTONE("cobblestone"), + PEBBLESTONE("pebblestone"), + STONE("stone"), + METAL("metal"), + GROUND("ground", "mud"), + WOOD("wood"), + GRASS_PAVER("grass_paver"), + GRASS("grass"), + SAND("sand"), + SALT("salt"), + SNOW("snow"), + ICE("ice"), + CLAY("clay"); + + final Set surfaces = new TreeSet<>(); + + RoadSurface(String... surfaces) { + this.surfaces.addAll(Arrays.asList(surfaces)); + } + + boolean contains(String surface) { + return surfaces.contains(surface); + } + } + + public enum RoadClass { + MOTORWAY("motorway", "motorway_link"), + STATE_ROAD("trunk", "trunk_link", "primary", "primary_link"), + ROAD("secondary", "secondary_link", "tertiary", "tertiary_link", "unclassified"), + STREET("residential", "living_street"), + SERVICE("service"), + TRACK("track", "road"), + FOOTWAY("footway"), + PATH("path"), + CYCLE_WAY("cycleway"); + + final Set roadClasses = new TreeSet<>(); + + RoadClass(String... classes) { + roadClasses.addAll(Arrays.asList(classes)); + } + + boolean contains(String roadClass) { + return roadClasses.contains(roadClass); + } + } }