OsmAnd/OsmAnd-java/src/main/java/net/osmand/router/RouteStatisticsHelper.java

442 lines
14 KiB
Java
Raw Normal View History

package net.osmand.router;
2019-07-07 19:31:38 +02:00
import net.osmand.binary.BinaryMapRouteReaderAdapter;
import net.osmand.binary.RouteDataObject;
2019-03-07 18:02:41 +01:00
import net.osmand.render.RenderingRuleSearchRequest;
import net.osmand.render.RenderingRulesStorage;
2019-03-01 12:35:59 +01:00
import java.util.ArrayList;
import java.util.List;
2019-07-07 18:30:01 +02:00
import java.util.Locale;
2019-03-01 12:35:59 +01:00
import java.util.Map;
import java.util.TreeMap;
2019-07-07 18:30:01 +02:00
public class RouteStatisticsHelper {
public static final String UNDEFINED_ATTR = "undefined";
2019-03-07 18:02:41 +01:00
2019-07-07 18:30:01 +02:00
public static List<RouteStatistics> calculateRouteStatistic(List<RouteSegmentResult> route, RenderingRulesStorage currentRenderer,
RenderingRulesStorage defaultRenderer, RenderingRuleSearchRequest currentSearchRequest,
RenderingRuleSearchRequest defaultSearchRequest) {
2019-03-07 18:02:41 +01:00
2019-07-07 18:30:01 +02:00
List<RouteStatistics> result = new ArrayList<>();
2019-03-07 18:02:41 +01:00
2019-07-07 18:30:01 +02:00
String[] colorAttrNames = { "surfaceColor", "roadClassColor", "smoothnessColor" };
2019-07-07 19:31:38 +02:00
for (int i = 0; i < colorAttrNames.length; i++) {
2019-07-07 18:30:01 +02:00
String colorAttrName = colorAttrNames[i];
2019-07-07 19:43:13 +02:00
RouteStatisticComputer statisticComputer =
new RouteStatisticComputer(route, colorAttrName, currentRenderer, defaultRenderer, currentSearchRequest, defaultSearchRequest);
2019-07-07 18:30:01 +02:00
result.add(statisticComputer.computeStatistic());
}
2019-03-07 18:02:41 +01:00
2019-07-07 18:30:01 +02:00
return result;
2019-03-07 18:02:41 +01:00
}
2019-07-07 19:31:38 +02:00
private static class RouteSegmentClass {
String className;
int color;
}
2019-07-07 19:43:13 +02:00
private static class RouteStatisticComputer {
2019-03-07 18:02:41 +01:00
2019-07-07 18:30:01 +02:00
final List<RouteSegmentResult> route;
final String colorAttrName;
final RenderingRulesStorage currentRenderer;
final RenderingRulesStorage defaultRenderer;
final RenderingRuleSearchRequest currentRenderingRuleSearchRequest;
final RenderingRuleSearchRequest defaultRenderingRuleSearchRequest;
2019-03-07 18:02:41 +01:00
2019-07-07 19:31:38 +02:00
RouteStatisticComputer(List<RouteSegmentResult> route, String colorAttrName,
2019-07-07 18:30:01 +02:00
RenderingRulesStorage currentRenderer, RenderingRulesStorage defaultRenderer,
RenderingRuleSearchRequest currentRenderingRuleSearchRequest, RenderingRuleSearchRequest defaultRenderingRuleSearchRequest) {
2019-03-07 18:02:41 +01:00
this.route = route;
2019-07-07 18:30:01 +02:00
this.colorAttrName = colorAttrName;
this.currentRenderer = currentRenderer;
this.defaultRenderer = defaultRenderer;
this.currentRenderingRuleSearchRequest = currentRenderingRuleSearchRequest;
this.defaultRenderingRuleSearchRequest = defaultRenderingRuleSearchRequest;
2019-03-07 18:02:41 +01:00
}
2019-07-07 19:43:13 +02:00
Map<String, RouteSegmentAttribute> makePartition(List<RouteSegmentAttribute> routeAttributes) {
Map<String, RouteSegmentAttribute> partition = new TreeMap<>();
for (RouteSegmentAttribute attribute : routeAttributes) {
RouteSegmentAttribute attr = partition.get(attribute.propertyName);
if (attr == null) {
attr = new RouteSegmentAttribute(attribute);
partition.put(attribute.propertyName, attr);
2019-03-07 18:02:41 +01:00
}
2019-07-07 19:43:13 +02:00
attr.incrementDistanceBy(attribute.getDistance());
2019-03-07 18:02:41 +01:00
}
return partition;
}
2019-07-07 19:43:13 +02:00
private float computeTotalDistance(List<RouteSegmentAttribute> attributes) {
2019-03-07 18:02:41 +01:00
float distance = 0f;
for (RouteSegmentAttribute attribute : attributes) {
distance += attribute.getDistance();
}
return distance;
}
2019-07-07 19:31:38 +02:00
protected List<RouteSegmentAttribute> processRoute() {
2019-03-07 18:02:41 +01:00
int index = 0;
2019-07-07 19:31:38 +02:00
List<RouteSegmentAttribute> routes = new ArrayList<>();
RouteSegmentClass prev = null;
2019-07-07 18:30:01 +02:00
for (RouteSegmentResult segment : route) {
2019-07-07 19:31:38 +02:00
RouteSegmentClass current = getAttribute(segment);
if (prev != null && !prev.className.equals(current.className)) {
2019-03-07 18:02:41 +01:00
index++;
}
if (index >= routes.size()) {
2019-07-07 19:31:38 +02:00
routes.add(new RouteSegmentAttribute(index, current.className, current.color));
2019-03-07 18:02:41 +01:00
}
RouteSegmentAttribute surface = routes.get(index);
surface.incrementDistanceBy(segment.getDistance());
prev = current;
}
return routes;
}
2019-07-07 19:43:13 +02:00
RouteStatistics computeStatistic() {
List<RouteSegmentAttribute> routeAttributes = processRoute();
Map<String, RouteSegmentAttribute> partition = makePartition(routeAttributes);
2019-03-07 18:02:41 +01:00
float totalDistance = computeTotalDistance(routeAttributes);
2019-07-07 19:43:13 +02:00
return new RouteStatistics(routeAttributes, partition, totalDistance);
2019-03-07 18:02:41 +01:00
}
RenderingRuleSearchRequest getSearchRequest(boolean useCurrentRenderer) {
return new RenderingRuleSearchRequest(useCurrentRenderer ? currentRenderingRuleSearchRequest : defaultRenderingRuleSearchRequest);
}
2019-07-07 19:31:38 +02:00
public RouteSegmentClass getAttribute(RouteSegmentResult segment) {
RenderingRuleSearchRequest currentRequest = getSearchRequest(true);
2019-07-07 19:43:13 +02:00
RouteSegmentClass res = new RouteSegmentClass();
res.className = UNDEFINED_ATTR;
2019-07-07 19:31:38 +02:00
if (searchRenderingAttribute(currentRenderer, currentRequest, segment)) {
2019-07-07 19:43:13 +02:00
2019-07-07 19:31:38 +02:00
res.className = currentRequest.getStringPropertyValue(currentRenderer.PROPS.R_ATTR_STRING_VALUE);
res.color = currentRequest.getIntPropertyValue(currentRenderer.PROPS.R_ATTR_COLOR_VALUE);
} else {
RenderingRuleSearchRequest defaultRequest = getSearchRequest(false);
2019-07-07 19:31:38 +02:00
if (searchRenderingAttribute(defaultRenderer, defaultRequest, segment)) {
res = new RouteSegmentClass();
res.className = defaultRequest.getStringPropertyValue(defaultRenderer.PROPS.R_ATTR_STRING_VALUE);
res.color = defaultRequest.getIntPropertyValue(defaultRenderer.PROPS.R_ATTR_COLOR_VALUE);
}
}
2019-07-07 19:31:38 +02:00
return res;
}
2019-03-07 18:02:41 +01:00
2019-07-07 19:31:38 +02:00
protected boolean searchRenderingAttribute(RenderingRulesStorage rrs, RenderingRuleSearchRequest req, RouteSegmentResult segment) {
//String additional = attrName + "=" + attribute;
RouteDataObject obj = segment.getObject();
int[] tps = obj.getTypes();
String additional = "";
for(int k = 0; k < tps.length; k++) {
BinaryMapRouteReaderAdapter.RouteTypeRule tp = obj.region.quickGetEncodingRule(tps[k]);
if(tp.getTag().equals("highway") || tp.getTag().equals("route") ||
tp.getTag().equals("railway") || tp.getTag().equals("aeroway") || tp.getTag().equals("aerialway")) {
req.setStringFilter(rrs.PROPS.R_TAG, tp.getTag());
req.setStringFilter(rrs.PROPS.R_TAG, tp.getValue());
} else {
if(additional.length() > 0) {
additional += ";";
}
additional = tp.getTag() +"=" +tp.getValue();
}
}
req.setStringFilter(rrs.PROPS.R_ADDITIONAL, additional);
return req.searchRenderingAttribute(colorAttrName);
2019-07-07 19:31:38 +02:00
}
2019-03-07 18:02:41 +01:00
}
2019-07-07 18:30:01 +02:00
private static class RouteBoundariesStatisticComputer extends RouteStatisticComputer<Boundaries> {
2019-03-07 18:02:41 +01:00
private final List<Incline> inclines;
2019-07-07 18:30:01 +02:00
public RouteBoundariesStatisticComputer(List<Incline> inclines, String attrName, String colorAttrName,
RenderingRulesStorage currentRenderer, RenderingRulesStorage defaultRenderer,
RenderingRuleSearchRequest currentSearchRequest, RenderingRuleSearchRequest defaultSearchRequest) {
super(null, attrName, colorAttrName, currentRenderer, defaultRenderer, currentSearchRequest, defaultSearchRequest);
2019-03-07 18:02:41 +01:00
this.inclines = inclines;
}
@Override
public List<RouteSegmentAttribute<Boundaries>> processRoute() {
List<RouteSegmentAttribute<Boundaries>> routeInclines = new ArrayList<>();
int index = 0;
Boundaries prev = null;
Incline prevIncline = null;
for (Incline incline : inclines) {
Boundaries current = incline.getBoundaries();
if (prev != null && !prev.equals(current)) {
index++;
}
if (index >= routeInclines.size()) {
String propertyName = getPropertyName(current);
int color = getColor(current);
RouteSegmentAttribute<Boundaries> attribute = new RouteSegmentAttribute<>(index, current, propertyName, color);
if (prevIncline != null) {
attribute.setInitDistance(prevIncline.getDistance());
}
routeInclines.add(attribute);
}
RouteSegmentAttribute routeIncline = routeInclines.get(index);
routeIncline.relativeSum(incline.getDistance());
prev = current;
prevIncline = incline;
}
return routeInclines;
}
@Override
public Boundaries getAttribute(RouteSegmentResult segment) {
return null;
}
@Override
public String getPropertyName(Boundaries attribute) {
int lowerBoundary = Math.round(attribute.getLowerBoundary());
int upperBoundary = Math.round(attribute.getUpperBoundary());
if (lowerBoundary >= Boundaries.MIN_DIVIDED_INCLINE) {
lowerBoundary++;
2019-03-07 18:02:41 +01:00
}
2019-07-07 18:30:01 +02:00
return String.format(Locale.US, "%d%% ... %d%%", lowerBoundary, upperBoundary);
2019-03-07 18:02:41 +01:00
}
@Override
public boolean searchRenderingAttribute(RenderingRulesStorage rrs, RenderingRuleSearchRequest req, Boundaries attribute) {
int lowerBoundary = Math.round(attribute.getLowerBoundary());
int upperBoundary = Math.round(attribute.getUpperBoundary());
StringBuilder range = new StringBuilder();
if (lowerBoundary >= Boundaries.MIN_DIVIDED_INCLINE) {
lowerBoundary++;
2019-03-15 23:18:36 +01:00
} else {
lowerBoundary = Boundaries.MIN_INCLINE;
}
if (upperBoundary > Boundaries.MAX_DIVIDED_INCLINE) {
upperBoundary = Boundaries.MAX_INCLINE;
}
range.append(lowerBoundary);
range.append(upperBoundary < 0 ? "_" : "-");
range.append(upperBoundary);
2019-07-07 18:30:01 +02:00
String additional = attrName + "=" + range;
req.setStringFilter(rrs.PROPS.R_ADDITIONAL, additional);
2019-07-07 18:30:01 +02:00
return req.searchRenderingAttribute(colorAttrName);
2019-03-07 18:02:41 +01:00
}
}
2019-07-07 19:31:38 +02:00
public static class RouteSegmentAttribute {
2019-03-07 18:02:41 +01:00
private final int index;
private final int color;
private final String propertyName;
private float distance;
private float initDistance;
2019-07-07 19:31:38 +02:00
RouteSegmentAttribute(int index, String propertyName, int color) {
2019-03-07 18:02:41 +01:00
this.index = index;
this.propertyName = propertyName;
this.color = color;
}
2019-07-07 19:31:38 +02:00
RouteSegmentAttribute(RouteSegmentAttribute segmentAttribute) {
2019-03-07 18:02:41 +01:00
this.index = segmentAttribute.getIndex();
this.propertyName = segmentAttribute.getPropertyName();
this.color = segmentAttribute.getColor();
}
public int getIndex() {
return index;
}
public float getDistance() {
return distance;
}
public void setInitDistance(float initDistance) {
this.initDistance = initDistance;
}
public void incrementDistanceBy(float distance) {
this.distance += distance;
}
public void relativeSum(float distance) {
this.distance = this.distance + ((distance - this.initDistance) - this.distance);
}
public String getPropertyName() {
return propertyName;
}
public int getColor() {
return color;
}
@Override
public String toString() {
return "RouteSegmentAttribute{" +
"index=" + index +
2019-07-07 19:31:38 +02:00
", propertyName='" + propertyName + '\'' +
2019-03-07 18:02:41 +01:00
", color='" + color + '\'' +
", distance=" + distance +
'}';
2019-03-01 12:35:59 +01:00
}
}
2019-03-07 18:02:41 +01:00
public static class Incline {
private final float inclineValue;
2019-03-07 18:02:41 +01:00
private final float distance;
private Boundaries boundaries;
2019-03-07 18:02:41 +01:00
public Incline(float inclineValue, float distance) {
this.inclineValue = inclineValue;
this.distance = distance;
}
public void computeBoundaries(float minIncline, float maxIncline) {
this.boundaries = Boundaries.newBoundariesFor(inclineValue, minIncline, maxIncline);
2019-03-07 18:02:41 +01:00
}
public float getValue() {
return inclineValue;
}
public float getDistance() {
return distance;
}
public Boundaries getBoundaries() {
return this.boundaries;
}
@Override
public String toString() {
return "Incline{" +
", incline=" + inclineValue +
", distance=" + distance +
'}';
2019-03-01 12:35:59 +01:00
}
}
2019-03-07 18:02:41 +01:00
public static class Boundaries implements Comparable<Boundaries> {
private static final int MIN_INCLINE = -100;
private static final int MIN_DIVIDED_INCLINE = -20;
private static final int MAX_INCLINE = 100;
private static final int MAX_DIVIDED_INCLINE = 20;
private static final int STEP = 4;
private static final int NUM;
private static final int[] BOUNDARIES_ARRAY;
static {
NUM = ((MAX_DIVIDED_INCLINE - MIN_DIVIDED_INCLINE) / STEP) + 3;
BOUNDARIES_ARRAY = new int[NUM];
BOUNDARIES_ARRAY[0] = MIN_INCLINE;
for (int i = 1; i < NUM - 1; i++) {
BOUNDARIES_ARRAY[i] = MIN_DIVIDED_INCLINE + (i - 1) * STEP;
}
BOUNDARIES_ARRAY[NUM - 1] = MAX_INCLINE;
}
private final float upperBoundary;
private final float lowerBoundary;
private Boundaries(float upperBoundary, float lowerBoundary) {
this.upperBoundary = upperBoundary;
this.lowerBoundary = lowerBoundary;
}
public static Boundaries newBoundariesFor(float incline, float minIncline, float maxIncline) {
int maxRoundedIncline = Math.round(maxIncline);
int minRoundedIncline = Math.round(minIncline);
2019-03-07 18:02:41 +01:00
if (incline > MAX_INCLINE) {
return new Boundaries(MAX_INCLINE, MAX_DIVIDED_INCLINE);
2019-03-07 18:02:41 +01:00
}
if (incline < MIN_INCLINE) {
return new Boundaries(MIN_DIVIDED_INCLINE, MIN_INCLINE);
2019-03-07 18:02:41 +01:00
}
for (int i = 1; i < NUM; i++) {
if (incline >= BOUNDARIES_ARRAY[i - 1] && incline < BOUNDARIES_ARRAY[i]) {
if (i == 1) {
return new Boundaries(BOUNDARIES_ARRAY[i], minRoundedIncline);
} else if (i == NUM - 1) {
return new Boundaries(maxRoundedIncline, BOUNDARIES_ARRAY[i - 1]);
} else {
return new Boundaries(BOUNDARIES_ARRAY[i], BOUNDARIES_ARRAY[i - 1]);
}
2019-03-07 18:02:41 +01:00
}
}
return null;
}
2019-03-01 12:35:59 +01:00
2019-03-07 18:02:41 +01:00
public float getUpperBoundary() {
return upperBoundary;
}
public float getLowerBoundary() {
return lowerBoundary;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Boundaries that = (Boundaries) o;
if (Float.compare(that.upperBoundary, upperBoundary) != 0) return false;
return Float.compare(that.lowerBoundary, lowerBoundary) == 0;
}
@Override
public int hashCode() {
int result = (upperBoundary != +0.0f ? Float.floatToIntBits(upperBoundary) : 0);
result = 31 * result + (lowerBoundary != +0.0f ? Float.floatToIntBits(lowerBoundary) : 0);
return result;
}
@Override
public int compareTo(Boundaries boundaries) {
return (int) (getLowerBoundary() - boundaries.getLowerBoundary());
}
@Override
public String toString() {
2019-07-07 18:30:01 +02:00
return String.format(Locale.US, "%d%% ... %d%%", Math.round(getLowerBoundary()), Math.round(getUpperBoundary()));
2019-03-07 18:02:41 +01:00
}
}
2019-07-07 19:43:13 +02:00
public static class RouteStatistics {
2019-03-07 18:02:41 +01:00
2019-07-07 19:43:13 +02:00
private final List<RouteSegmentAttribute> elements;
private final Map<String, RouteSegmentAttribute> partition;
2019-03-07 18:02:41 +01:00
private final float totalDistance;
2019-07-07 19:43:13 +02:00
private RouteStatistics(List<RouteSegmentAttribute> elements,
Map<String, RouteSegmentAttribute> partition,
2019-07-07 18:30:01 +02:00
float totalDistance) {
2019-03-07 18:02:41 +01:00
this.elements = elements;
this.partition = partition;
this.totalDistance = totalDistance;
}
2019-03-01 12:35:59 +01:00
2019-03-07 18:02:41 +01:00
public float getTotalDistance() {
return totalDistance;
2019-03-01 12:35:59 +01:00
}
2019-07-07 19:43:13 +02:00
public List<RouteSegmentAttribute> getElements() {
2019-03-07 18:02:41 +01:00
return elements;
2019-03-01 12:35:59 +01:00
}
2019-07-07 19:43:13 +02:00
public Map<E, RouteSegmentAttribute> getPartition() {
2019-03-07 18:02:41 +01:00
return partition;
2019-03-01 12:35:59 +01:00
}
}
2019-03-07 18:02:41 +01:00
}