Add "DIVIDE" command and new "maxslope" and "maxslopecost" parameters (#642)

* Added 'DIV' expression for profiles

* Added 'uphillmaxbuffercost' and 'downhillmaxbuffercost' parameter. This makes it possible to penalize very steep path sections

* Added 'div by zero' check in BExpression.java DIV command

* Simplify maxbuffercostdiv logic

* Added documentation about new features

* Fix typo

* Rename new DIV command

* Redesign the new commands
- Allow to set both the maxslope and the maxslopecost in the way context separately for uphill and downhill
- New names for the new commands that better reflect what they actually do

* Adapt the profile developers guide to the latest changes

* Improve wording

---------

Co-authored-by: quaelnix <122357328+quaelnix@users.noreply.github.com>
This commit is contained in:
simdens 2024-01-17 16:34:52 +01:00 committed by GitHub
parent d2e183c625
commit 2f1422352e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 84 additions and 19 deletions

View file

@ -49,6 +49,8 @@ final class StdPath extends OsmPath {
float turncostbase = rc.expctxWay.getTurncost(); float turncostbase = rc.expctxWay.getTurncost();
float uphillcutoff = rc.expctxWay.getUphillcutoff() * 10000; float uphillcutoff = rc.expctxWay.getUphillcutoff() * 10000;
float downhillcutoff = rc.expctxWay.getDownhillcutoff() * 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 cfup = rc.expctxWay.getUphillCostfactor();
float cfdown = rc.expctxWay.getDownhillCostfactor(); float cfdown = rc.expctxWay.getDownhillCostfactor();
float cf = rc.expctxWay.getCostfactor(); float cf = rc.expctxWay.getCostfactor();
@ -60,11 +62,27 @@ final class StdPath extends OsmPath {
downhillcostdiv = 1000000 / downhillcostdiv; 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(); uphillcostdiv = (int) rc.expctxWay.getUphillcost();
if (uphillcostdiv > 0) { if (uphillcostdiv > 0) {
uphillcostdiv = 1000000 / uphillcostdiv; 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 int dist = (int) distance; // legacy arithmetics needs int
// penalty for turning angle // penalty for turning angle
@ -99,8 +117,14 @@ final class StdPath extends OsmPath {
reduce = excess; reduce = excess;
} }
ehbd -= reduce; ehbd -= reduce;
float elevationCost = 0.f;
if (downhillcostdiv > 0) { 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; sectionCost += elevationCost;
if (message != null) { if (message != null) {
message.linkelevationcost += elevationCost; message.linkelevationcost += elevationCost;
@ -125,8 +149,14 @@ final class StdPath extends OsmPath {
reduce = excess; reduce = excess;
} }
ehbu -= reduce; ehbu -= reduce;
float elevationCost = 0.f;
if (uphillcostdiv > 0) { 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; sectionCost += elevationCost;
if (message != null) { if (message != null) {
message.linkelevationcost += elevationCost; message.linkelevationcost += elevationCost;

View file

@ -9,14 +9,15 @@ final class BExpression {
private static final int ADD_EXP = 20; private static final int ADD_EXP = 20;
private static final int MULTIPLY_EXP = 21; private static final int MULTIPLY_EXP = 21;
private static final int MAX_EXP = 22; private static final int DIVIDE_EXP = 22;
private static final int EQUAL_EXP = 23; private static final int MAX_EXP = 23;
private static final int GREATER_EXP = 24; private static final int EQUAL_EXP = 24;
private static final int MIN_EXP = 25; 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 SUB_EXP = 27;
private static final int LESSER_EXP = 27; private static final int LESSER_EXP = 28;
private static final int XOR_EXP = 28; private static final int XOR_EXP = 29;
private static final int SWITCH_EXP = 30; private static final int SWITCH_EXP = 30;
private static final int ASSIGN_EXP = 31; private static final int ASSIGN_EXP = 31;
@ -144,6 +145,8 @@ final class BExpression {
exp.typ = AND_EXP; exp.typ = AND_EXP;
} else if ("multiply".equals(operator)) { } else if ("multiply".equals(operator)) {
exp.typ = MULTIPLY_EXP; exp.typ = MULTIPLY_EXP;
} else if ("divide".equals(operator)) {
exp.typ = DIVIDE_EXP;
} else if ("add".equals(operator)) { } else if ("add".equals(operator)) {
exp.typ = ADD_EXP; exp.typ = ADD_EXP;
} else if ("max".equals(operator)) { } else if ("max".equals(operator)) {
@ -277,6 +280,8 @@ final class BExpression {
return op1.evaluate(ctx) - op2.evaluate(ctx); return op1.evaluate(ctx) - op2.evaluate(ctx);
case MULTIPLY_EXP: case MULTIPLY_EXP:
return op1.evaluate(ctx) * op2.evaluate(ctx); return op1.evaluate(ctx) * op2.evaluate(ctx);
case DIVIDE_EXP:
return divide(op1.evaluate(ctx), op2.evaluate(ctx));
case MAX_EXP: case MAX_EXP:
return max(op1.evaluate(ctx), op2.evaluate(ctx)); return max(op1.evaluate(ctx), op2.evaluate(ctx));
case MIN_EXP: case MIN_EXP:
@ -360,6 +365,11 @@ final class BExpression {
return v1 < v2 ? v1 : 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 @Override
public String toString() { public String toString() {
if (typ == NUMBER_EXP) { if (typ == NUMBER_EXP) {

View file

@ -12,7 +12,7 @@ public final class BExpressionContextWay extends BExpressionContext implements T
private boolean decodeForbidden = true; private boolean decodeForbidden = true;
private static String[] buildInVariables = 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() { protected String[] getBuildInVariableNames() {
return buildInVariables; return buildInVariables;
@ -82,6 +82,22 @@ public final class BExpressionContextWay extends BExpressionContext implements T
return getBuildInVariable(15); 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) { public BExpressionContextWay(BExpressionMetaData meta) {
super("way", meta); super("way", meta);
} }

View file

@ -72,12 +72,16 @@ Some variable names are pre-defined and accessed by the routing engine:
- for the global section these are: - for the global section these are:
- 7 elevation configuration parameters: - 11 elevation configuration parameters:
- `downhillcost` - `downhillcost`
- `downhillcutoff` - `downhillcutoff`
- `downhillmaxslope`
- `downhillmaxslopecost`
- `uphillcost` - `uphillcost`
- `uphillcutoff` - `uphillcutoff`
- `uphillmaxslope`
- `uphillmaxslopecost`
- `elevationpenaltybuffer` - `elevationpenaltybuffer`
- `elevationmaxbuffer` - `elevationmaxbuffer`
- `elevationbufferreduce` - `elevationbufferreduce`
@ -172,6 +176,7 @@ All expressions have one of the following basic forms:
- `and <boolean expression 1> <boolean expression 2>` - `and <boolean expression 1> <boolean expression 2>`
- `xor <boolean expression 1> <boolean expression 2>` - `xor <boolean expression 1> <boolean expression 2>`
- `multiply <numeric expression 1> <numeric expression 2>` - `multiply <numeric expression 1> <numeric expression 2>`
- `div <numeric expression 1> <numeric expression 2>`
- `add <numeric expression 1> <numeric expression 2>` - `add <numeric expression 1> <numeric expression 2>`
- `sub <numeric expression 1> <numeric expression 2>` - `sub <numeric expression 1> <numeric expression 2>`
- `max <numeric expression 1> <numeric expression 2>` - `max <numeric expression 1> <numeric expression 2>`
@ -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. 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 When elevation does not fit the buffer of size `elevationmaxbuffer`, it is
converted by up/downhillcost ratio to Elevationcost portion of Equivalentlength. converted by `up/downhill[maxslope]cost` ratio to Elevationcost portion of Equivalentlength.
Up/downhillcostfactors are used, if defined, otherwise costfactor is used. `up/downhillcostfactors` are used, if defined, otherwise `costfactor` is used.
- `elevationpenaltybuffer` - default 5(m). - `elevationpenaltybuffer` - default 5(m).
The variable value is used for 2 purposes The variable value is used for 2 purposes
- with `buffer content > elevationpenaltybuffer`, it starts partially convert - 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` - 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 of how much is `WayLength[km] * elevationbufferreduce*10` is saturated by
the buffer content above elevationpenaltybuffer. the buffer content above elevationpenaltybuffer.
- `elevationmaxbuffer` - default 10(m) - `elevationmaxbuffer` - default 10(m)
is the size of the buffer, above which all elevation is converted to is the size of the buffer, above which all elevation is converted to
Elevationcost by Up/Downhillcost ratio, and - if defined - Elevationcost by `up/downhill[maxslope]cost` ratio, and - if defined -
Up/downhillcostfactor fully replaces costfactor in way cost calculation. `up/downhillcostfactor` fully replaces `costfactor` in way cost calculation.
- `elevationbufferreduce` - default 0(slope%) - `elevationbufferreduce` - default 0(slope%)
is rate of conversion of the buffer content above elevationpenaltybuffer to is rate of conversion of the buffer content above elevationpenaltybuffer to
ElevationCost. For a way of length L, the amount of converted elevation is ElevationCost. For a way of length L, the amount of converted elevation is
L[km] * elevationbufferreduce[%] * 10. The elevation to Elevationcost 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`, Example: Let's examine steady slopes with `elevationmaxbuffer=10`,
`elevationpenaltybuffer=5`, `elevationbufferreduce=0.5`, `cutoffs=1.5`, `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%. - For slope 1.75%, there will remain 0.25%.
That saturates the elevationbufferreduce 0.5% by 50%. That gives Way cost to 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 Additionally, 0.25% gives 2.5m per 1km, converted to 2.5*60 = 150m of
Elevationcost. Elevationcost.