diff --git a/brouter-core/src/main/java/btools/router/StdPath.java b/brouter-core/src/main/java/btools/router/StdPath.java index 925b06af..d7164a44 100644 --- a/brouter-core/src/main/java/btools/router/StdPath.java +++ b/brouter-core/src/main/java/btools/router/StdPath.java @@ -49,6 +49,8 @@ protected double processWaySection(RoutingContext rc, double distance, double de float turncostbase = rc.expctxWay.getTurncost(); float uphillcutoff = rc.expctxWay.getUphillcutoff() * 10000; float downhillcutoff = rc.expctxWay.getDownhillcutoff() * 10000; + float uphillmaxslope = rc.expctxWay.getUphillmaxslope() * 10000; + float downhillmaxslope = rc.expctxWay.getDownhillmaxslope() * 10000; float cfup = rc.expctxWay.getUphillCostfactor(); float cfdown = rc.expctxWay.getDownhillCostfactor(); float cf = rc.expctxWay.getCostfactor(); @@ -60,11 +62,27 @@ protected double processWaySection(RoutingContext rc, double distance, double de downhillcostdiv = 1000000 / downhillcostdiv; } + int downhillmaxslopecostdiv = (int) rc.expctxWay.getDownhillmaxslopecost(); + if (downhillmaxslopecostdiv > 0) { + downhillmaxslopecostdiv = 1000000 / downhillmaxslopecostdiv; + } else { + // if not given, use legacy behavior + downhillmaxslopecostdiv = downhillcostdiv; + } + uphillcostdiv = (int) rc.expctxWay.getUphillcost(); if (uphillcostdiv > 0) { uphillcostdiv = 1000000 / uphillcostdiv; } + int uphillmaxslopecostdiv = (int) rc.expctxWay.getUphillmaxslopecost(); + if (uphillmaxslopecostdiv > 0) { + uphillmaxslopecostdiv = 1000000 / uphillmaxslopecostdiv; + } else { + // if not given, use legacy behavior + uphillmaxslopecostdiv = uphillcostdiv; + } + int dist = (int) distance; // legacy arithmetics needs int // penalty for turning angle @@ -99,8 +117,14 @@ protected double processWaySection(RoutingContext rc, double distance, double de reduce = excess; } ehbd -= reduce; + float elevationCost = 0.f; if (downhillcostdiv > 0) { - int elevationCost = reduce / downhillcostdiv; + elevationCost += Math.min(reduce, dist * downhillmaxslope) / downhillcostdiv; + } + if (downhillmaxslopecostdiv > 0) { + elevationCost += Math.max(0, reduce - dist * downhillmaxslope) / downhillmaxslopecostdiv; + } + if (elevationCost > 0) { sectionCost += elevationCost; if (message != null) { message.linkelevationcost += elevationCost; @@ -125,8 +149,14 @@ protected double processWaySection(RoutingContext rc, double distance, double de reduce = excess; } ehbu -= reduce; + float elevationCost = 0.f; if (uphillcostdiv > 0) { - int elevationCost = reduce / uphillcostdiv; + elevationCost += Math.min(reduce, dist * uphillmaxslope) / uphillcostdiv; + } + if (uphillmaxslopecostdiv > 0) { + elevationCost += Math.max(0, reduce - dist * uphillmaxslope) / uphillmaxslopecostdiv; + } + if (elevationCost > 0) { sectionCost += elevationCost; if (message != null) { message.linkelevationcost += elevationCost; diff --git a/brouter-expressions/src/main/java/btools/expressions/BExpression.java b/brouter-expressions/src/main/java/btools/expressions/BExpression.java index ded5b340..b083f58c 100644 --- a/brouter-expressions/src/main/java/btools/expressions/BExpression.java +++ b/brouter-expressions/src/main/java/btools/expressions/BExpression.java @@ -9,14 +9,15 @@ final class BExpression { private static final int ADD_EXP = 20; private static final int MULTIPLY_EXP = 21; - private static final int MAX_EXP = 22; - private static final int EQUAL_EXP = 23; - private static final int GREATER_EXP = 24; - private static final int MIN_EXP = 25; + private static final int DIVIDE_EXP = 22; + private static final int MAX_EXP = 23; + private static final int EQUAL_EXP = 24; + private static final int GREATER_EXP = 25; + private static final int MIN_EXP = 26; - private static final int SUB_EXP = 26; - private static final int LESSER_EXP = 27; - private static final int XOR_EXP = 28; + private static final int SUB_EXP = 27; + private static final int LESSER_EXP = 28; + private static final int XOR_EXP = 29; private static final int SWITCH_EXP = 30; private static final int ASSIGN_EXP = 31; @@ -144,6 +145,8 @@ private static BExpression parseRaw(BExpressionContext ctx, int level, String op exp.typ = AND_EXP; } else if ("multiply".equals(operator)) { exp.typ = MULTIPLY_EXP; + } else if ("divide".equals(operator)) { + exp.typ = DIVIDE_EXP; } else if ("add".equals(operator)) { exp.typ = ADD_EXP; } else if ("max".equals(operator)) { @@ -277,6 +280,8 @@ public float evaluate(BExpressionContext ctx) { return op1.evaluate(ctx) - op2.evaluate(ctx); case MULTIPLY_EXP: return op1.evaluate(ctx) * op2.evaluate(ctx); + case DIVIDE_EXP: + return divide(op1.evaluate(ctx), op2.evaluate(ctx)); case MAX_EXP: return max(op1.evaluate(ctx), op2.evaluate(ctx)); case MIN_EXP: @@ -360,6 +365,11 @@ private float min(float v1, float v2) { return v1 < v2 ? v1 : v2; } + private float divide(float v1, float v2) { + if (v2 == 0f) throw new IllegalArgumentException("div by zero"); + return v1 / v2; + } + @Override public String toString() { if (typ == NUMBER_EXP) { diff --git a/brouter-expressions/src/main/java/btools/expressions/BExpressionContextWay.java b/brouter-expressions/src/main/java/btools/expressions/BExpressionContextWay.java index 5b37b7ce..0a8338c6 100644 --- a/brouter-expressions/src/main/java/btools/expressions/BExpressionContextWay.java +++ b/brouter-expressions/src/main/java/btools/expressions/BExpressionContextWay.java @@ -12,7 +12,7 @@ public final class BExpressionContextWay extends BExpressionContext implements T private boolean decodeForbidden = true; private static String[] buildInVariables = - {"costfactor", "turncost", "uphillcostfactor", "downhillcostfactor", "initialcost", "nodeaccessgranted", "initialclassifier", "trafficsourcedensity", "istrafficbackbone", "priorityclassifier", "classifiermask", "maxspeed", "uphillcost", "downhillcost", "uphillcutoff", "downhillcutoff"}; + {"costfactor", "turncost", "uphillcostfactor", "downhillcostfactor", "initialcost", "nodeaccessgranted", "initialclassifier", "trafficsourcedensity", "istrafficbackbone", "priorityclassifier", "classifiermask", "maxspeed", "uphillcost", "downhillcost", "uphillcutoff", "downhillcutoff", "uphillmaxslope", "downhillmaxslope", "uphillmaxslopecost", "downhillmaxslopecost"}; protected String[] getBuildInVariableNames() { return buildInVariables; @@ -82,6 +82,22 @@ public float getDownhillcutoff() { return getBuildInVariable(15); } + public float getUphillmaxslope() { + return getBuildInVariable(16); + } + + public float getDownhillmaxslope() { + return getBuildInVariable(17); + } + + public float getUphillmaxslopecost() { + return getBuildInVariable(18); + } + + public float getDownhillmaxslopecost() { + return getBuildInVariable(19); + } + public BExpressionContextWay(BExpressionMetaData meta) { super("way", meta); } diff --git a/docs/developers/profile_developers_guide.md b/docs/developers/profile_developers_guide.md index 200732cb..f5b69bf3 100644 --- a/docs/developers/profile_developers_guide.md +++ b/docs/developers/profile_developers_guide.md @@ -72,12 +72,16 @@ Some variable names are pre-defined and accessed by the routing engine: - for the global section these are: - - 7 elevation configuration parameters: + - 11 elevation configuration parameters: - `downhillcost` - `downhillcutoff` + - `downhillmaxslope` + - `downhillmaxslopecost` - `uphillcost` - `uphillcutoff` + - `uphillmaxslope` + - `uphillmaxslopecost` - `elevationpenaltybuffer` - `elevationmaxbuffer` - `elevationbufferreduce` @@ -172,6 +176,7 @@ All expressions have one of the following basic forms: - `and ` - `xor ` - `multiply ` + - `div ` - `add ` - `sub ` - `max ` @@ -276,33 +281,37 @@ it climbed only 10 m on those 500 m, all 10 m would be *swallowed* by cutoff, together with up to 5 m from the buffer, if there were any. When elevation does not fit the buffer of size `elevationmaxbuffer`, it is -converted by up/downhillcost ratio to Elevationcost portion of Equivalentlength. -Up/downhillcostfactors are used, if defined, otherwise costfactor is used. +converted by `up/downhill[maxslope]cost` ratio to Elevationcost portion of Equivalentlength. +`up/downhillcostfactors` are used, if defined, otherwise `costfactor` is used. - `elevationpenaltybuffer` - default 5(m). The variable value is used for 2 purposes - with `buffer content > elevationpenaltybuffer`, it starts partially convert - the buffered elevation to ElevationCost by Up/downhillcost + the buffered elevation to ElevationCost by `up/downhillcost` - with `elevation taken = MIN (buffer content - elevationpenaltybuffer, WayLength[km] * elevationbufferreduce*10` - Up/downhillcost factor takes place instead of costfactor at the percentage + The `up/downhillcostfactor` takes place instead of `costfactor` at the percentage of how much is `WayLength[km] * elevationbufferreduce*10` is saturated by the buffer content above elevationpenaltybuffer. - `elevationmaxbuffer` - default 10(m) is the size of the buffer, above which all elevation is converted to - Elevationcost by Up/Downhillcost ratio, and - if defined - - Up/downhillcostfactor fully replaces costfactor in way cost calculation. + Elevationcost by `up/downhill[maxslope]cost` ratio, and - if defined - + `up/downhillcostfactor` fully replaces `costfactor` in way cost calculation. - `elevationbufferreduce` - default 0(slope%) is rate of conversion of the buffer content above elevationpenaltybuffer to ElevationCost. For a way of length L, the amount of converted elevation is L[km] * elevationbufferreduce[%] * 10. The elevation to Elevationcost - conversion ratio is given by Up/downhillcost. + conversion ratio is given by `up/downhill[maxslope]cost`. + +Whether `up/downhillmaxslope` or `up/downhillmaxslopecost` is used as conversion +ratio depends on whether the elevation was accumulated below or above the slope +threshold values defined in `up/downhillmaxslope`. Example: Let's examine steady slopes with `elevationmaxbuffer=10`, `elevationpenaltybuffer=5`, `elevationbufferreduce=0.5`, `cutoffs=1.5`, @@ -313,7 +322,7 @@ All slopes within 0 .. 1.5% are swallowed by the cutoff. - For slope 1.75%, there will remain 0.25%. That saturates the elevationbufferreduce 0.5% by 50%. That gives Way cost to - be calculated 50% from costfactor and 50% from Up/downhillcostfactor. + be calculated 50% from `costfactor` and 50% from `up/downhillcostfactor`. Additionally, 0.25% gives 2.5m per 1km, converted to 2.5*60 = 150m of Elevationcost.