Add obstacleaccess & obstaclecost into the parameters

This commit is contained in:
Pieter Vander Vennet 2022-10-11 13:53:19 +02:00
parent 3b717b0dbb
commit f1b7900b19
7 changed files with 104 additions and 43 deletions

View file

@ -17,6 +17,10 @@ namespace AspectedRouting.IO.LuaSkeleton
internal string ToLuaWithTags(IExpression bare)
{
if (bare == null)
{
throw new NullReferenceException("bare is null");
}
var opt = bare.Apply(new LuaLiteral(Typs.Tags, "tags")).SpecializeToSmallestType().Optimize(out _);
return this.ToLua(opt);
}

View file

@ -2,7 +2,9 @@ using System;
using System.Collections.Generic;
using System.Linq;
using AspectedRouting.IO.LuaSkeleton;
using AspectedRouting.IO.LuaSnippets;
using AspectedRouting.Language;
using AspectedRouting.Language.Functions;
using AspectedRouting.Language.Typ;
namespace AspectedRouting.IO.itinero2
@ -12,15 +14,9 @@ namespace AspectedRouting.IO.itinero2
private string GenerateFactorFunction()
{
var parameters = new Dictionary<string, IExpression>();
foreach (var (name, value) in _profile.DefaultParameters)
{
parameters[name] = value;
}
foreach (var (name, value) in _profile.DefaultParameters) parameters[name] = value;
foreach (var (name, value) in _profile.Behaviours[_behaviourName])
{
parameters[name] = value;
}
foreach (var (name, value) in _profile.Behaviours[_behaviourName]) parameters[name] = value;
var aspects = new List<string>();
@ -28,15 +24,9 @@ namespace AspectedRouting.IO.itinero2
foreach (var (paramName, expr) in _profile.Priority)
{
var weightExpr = parameters[paramName].Evaluate(_context);
if (!(weightExpr is double weight))
{
continue;
}
if (!(weightExpr is double weight)) continue;
if (weight == 0)
{
continue;
}
if (weight == 0) continue;
// The expression might still have multiple typings,
// which take inputs different from 'Tags', so we specialize the expr first
@ -45,7 +35,8 @@ namespace AspectedRouting.IO.itinero2
.PruneTypes(tp => !(tp is Curry));
var exprSpecialized = appliedExpr.Optimize(out _);
if (exprSpecialized.Types.First().Equals(Typs.Bool) || exprSpecialized.Types.First().Equals(Typs.String))
if (exprSpecialized.Types.First().Equals(Typs.Bool) ||
exprSpecialized.Types.First().Equals(Typs.String))
{
_skeleton.AddDep("parse");
exprSpecialized = Funcs.Parse.Apply(exprSpecialized);
@ -53,14 +44,12 @@ namespace AspectedRouting.IO.itinero2
var exprInLua = _skeleton.ToLua(exprSpecialized);
if (exprInLua.Contains("constRight") || exprInLua.Contains("firstArg"))
{
throw new Exception("Not optimized properly:" + exprSpecialized.Repr());
}
aspects.Add(weight + " * " + exprInLua);
}
Console.WriteLine(aspects.Lined());
var code = new List<string>()
var code = new List<string>
{
"--[[",
"Generates the factor according to the priorities and the parameters for this behaviour",
@ -81,8 +70,8 @@ namespace AspectedRouting.IO.itinero2
_skeleton.AddDep("containedIn");
_skeleton.AddDep("str_split");
_skeleton.AddDep("calculate_turn_cost_factor");
/**
* Calculates the turn cost factor for relation attributes or obstacles.
Keep in mind that there are no true relations in the routerDB anymore, instead the attributes are copied onto a turn cost object.
@ -97,17 +86,32 @@ If result.factor is positive, that is the cost.
There is no forward or backward, so this should always be the same for the same attributes
*/
var code = new List<string> {
"--[[ Function called by itinero2 on every turn restriction relation"," ]]",
var tags = new LuaLiteral(Typs.Tags, "attributes");
var hasAccess = _profile.ObstacleAccess.Apply(tags).SpecializeToSmallestType().Optimize(out _);
var code = new List<string>
{
"--[[ Function called by itinero2 on every turn restriction relation", " ]]",
"function turn_cost_factor(attributes, result)",
" result.factor = calculate_turn_cost_factor(attributes, vehicle_types)" ,
"local has_access",
Snippets.Convert(_skeleton, "has_access", hasAccess),
"if ( has_access == \"no\" or has_access == \"false\") then",
" result.factor = -1",
"else",
Snippets.Convert(_skeleton, "result.factor", _profile.ObstacleCost.Apply(tags).SpecializeToSmallestType().Optimize(out _)),
"end",
"",
" -- not known by the profile or invalid value - use the default implementation",
" if (result.factor == nil) then",
" result.factor = calculate_turn_cost_factor(attributes, vehicle_types)",
" end",
"end",
""
};
return code.Lined();
}
private string GenerateMainFunction()
{
var parameters = _profile.Behaviours[_behaviourName];

View file

@ -113,7 +113,8 @@ namespace AspectedRouting.IO.jsonParser
var access = ParseProfileProperty(e, contextWithParameters, "access").Finalize();
var oneway = ParseProfileProperty(e, contextWithParameters, "oneway").Finalize();
var speed = ParseProfileProperty(e, contextWithParameters, "speed").Finalize();
var obstacle_access = ParseProfileProperty(e, contextWithParameters, "obstacleaccess", Funcs.Const.Apply(new Constant(new Var("any"), null))).Finalize();
var obstacle_cost = ParseProfileProperty(e, contextWithParameters, "obstaclecost", Funcs.Const.Apply(new Constant(Typs.Double,0))).Finalize();
IExpression TagsApplied(IExpression x)
{
@ -171,6 +172,7 @@ namespace AspectedRouting.IO.jsonParser
access,
oneway,
speed,
obstacle_access, obstacle_cost,
weights,
metadata,
lastChange

View file

@ -12,11 +12,17 @@ namespace AspectedRouting.IO.jsonParser
{
public static partial class JsonParser
{
internal static IExpression ParseProfileProperty(JsonElement e, Context c, string property)
internal static IExpression ParseProfileProperty(JsonElement e, Context c, string property, IExpression defaultExpression = null)
{
if (!e.TryGetProperty(property, out var prop)) {
throw new ArgumentException("Not a valid profile: the declaration expression for '" + property +
"' is missing");
if (defaultExpression == null)
{
throw new ArgumentException("Not a valid profile: the declaration expression for '" + property +
"' is missing");
}
Console.Error.WriteLine("WARNING: no expression defined for "+property+", using the default instead");
return defaultExpression;
}
try {
@ -46,6 +52,11 @@ namespace AspectedRouting.IO.jsonParser
if (c.ResultType is Curry) {
return false;
}
if (c.ResultType is ListType)
{
return false;
}
return true;
});
if (pruned.SpecializeToSmallestType().Types.Count() != 1) {

View file

@ -28,8 +28,13 @@ namespace AspectedRouting.Language.Expression
public IExpression Access { get; }
public IExpression Oneway { get; }
public IExpression Speed { get; }
public IExpression ObstacleAccess { get; }
public IExpression ObstacleCost { get; }
public Dictionary<string, IExpression> Priority { get; }
/**
* Moment of last change of any upstream file
*/
@ -39,6 +44,7 @@ namespace AspectedRouting.Language.Expression
List<string> vehicleTyps, Dictionary<string, IExpression> defaultParameters,
Dictionary<string, Dictionary<string, IExpression>> behaviours,
IExpression access, IExpression oneway, IExpression speed,
IExpression obstacleAccess, IExpression obstacleCost,
Dictionary<string, IExpression> priority, List<string> metadata, DateTime lastChange)
{
Name = name;
@ -46,9 +52,11 @@ namespace AspectedRouting.Language.Expression
Author = author;
Filename = filename;
VehicleTyps = vehicleTyps;
Access = access.Optimize(out var _);
Oneway = oneway.Optimize(out var _);
Speed = speed.Optimize(out var _);
Access = access.Optimize(out _);
Oneway = oneway.Optimize(out _);
Speed = speed.Optimize(out _);
ObstacleAccess = obstacleAccess.Optimize(out _);
ObstacleCost = obstacleCost.Optimize(out _);
Priority = priority;
Metadata = metadata;
LastChange = lastChange;
@ -58,10 +66,16 @@ namespace AspectedRouting.Language.Expression
CheckTypes(Access, "access");
CheckTypes(Oneway, "oneway");
CheckTypes(Speed, "speed");
CheckTypes(ObstacleAccess, "obstacleaccess");
CheckTypes(ObstacleCost, "obstaclecost");
}
private static void CheckTypes(IExpression e, string name)
{
if (e == null)
{
throw new Exception("No expression given for " +name);
}
if (e.Types.Count() == 1)
{
return;
@ -73,11 +87,11 @@ namespace AspectedRouting.Language.Expression
public List<IExpression> AllExpressions(Context ctx)
{
var l = new List<IExpression> { Access, Oneway, Speed };
var l = new List<IExpression> { Access, Oneway, Speed, ObstacleAccess, ObstacleCost };
l.AddRange(DefaultParameters.Values);
l.AddRange(Behaviours.Values.SelectMany(b => b.Values));
l.AddRange(Priority.Values);
var allExpr = new List<IExpression>();
allExpr.AddRange(l);
@ -100,10 +114,14 @@ namespace AspectedRouting.Language.Expression
public List<IExpression> AllExpressionsFor(string behaviourName, Context context)
{
var allExpressions = new List<IExpression>();
allExpressions.Add(Access);
allExpressions.Add(Oneway);
allExpressions.Add(Speed);
var allExpressions = new List<IExpression>
{
Access,
Oneway,
Speed,
ObstacleAccess,
ObstacleCost
};
var behaviourContext = new Context(context);
var behaviourParameters = ParametersFor(behaviourName);

View file

@ -111,7 +111,7 @@ namespace AspectedRouting.Language.Functions
{
if (i >= 0)
{
Types = new[] { Typs.Double, Typs.Nat, Typs.Nat, Typs.PDouble };
Types = new[] { Typs.Double, Typs.Int, Typs.Nat, Typs.PDouble };
}
else
{

View file

@ -24,9 +24,11 @@ Aspects can use the following (extra) tags:
- `oneway` is a field in the vehicle file. It should be an expression returning `both`, `with` or `against`.
When calculated, the tag `oneway` is added to the tags for the other aspects to be calculated.
- `speed`: an expression indicating how fast the vehicle can go there. It should take into account legal, practical and social aspects. An example expression could be `{"$min", ["$legal_maxspeed", "#defaultspeed"]}`
- `obstacleaccess` and `obstaclecost` are two (optional) expressions that calculate whether an obstacle can be passed and if so, if there is a penalty for this. See detailed explanations below
- `priorities`: a table of `{'#paramName', expression}` determining the priority (1/cost) of a way, per meter. The formula used is `paramName * expression + paramName0 * expression0 + ...` (`speed`, `access` and `oneway` can be used here as tags indicate the earlier defined respective aspects). Use a weight == 1 to get the shortest route or `$speed` to get the fastest route
# Calculating oneway and forward/backward speeds
There are two possibilities in order to calculate the possible direction of a traveller can go over an edge:
@ -36,9 +38,29 @@ There are two possibilities in order to calculate the possible direction of a tr
Note that `_direction=with` and `_direction=against` are _not_ supported in Itinero1.0 profiles. For maximal compatibility and programming comfort, a mixture of both techniques should be used. For example, one aspect interpreting the legal onewayness in tandem with one aspect determining comfort by direction is optimal.
# Obstacle costs
(Note: this only works with itinero2.0)
Obstacles are objects which are encountered on nodes, e.g. bollards, traffic lights but also turn restrictions.
The first property for this is `obstacleaccess` which calculates wether or not a vehicle can pass the obstacle.
The possible values are:
- "no" of "false": the current vehicle _cannot_ pass this obstacle and should take a different route
- "yes" or "true": the current vehicle _can_ pass this obstacle. The turn cost will be calculated
- `null`: same as 'yes'
If `obstacleaccess` is not `no` or `false`, then `obstaclecost` will be triggered. This possible return values are:
- a positive number, indicating the cost for passing this obstacle
- 0: there is no cost to cross this obstacle
- null: this profile has no knowledge of a cost, will be interpreted as `0`
If the resulting cost is null, the default implementation will be used.
# Pitfalls
"$all" should not be used together with a mapping: it checks if all _present_ keys return true or yes (or some other value); it does _not_ check that all the specified keys in the mapping are present.
For this, an additional 'mustHaveKeys' should be added added
For this, an additional 'mustHaveKeys' should be added added