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:
parent
d2e183c625
commit
2f1422352e
4 changed files with 84 additions and 19 deletions
|
@ -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;
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue