Restructuring of profile dir, more work on porting bicycle, still a bug for relation handling
This commit is contained in:
parent
287dde6cee
commit
e9e41e170c
40 changed files with 233 additions and 1303 deletions
|
@ -232,7 +232,7 @@ namespace AspectedRouting.IO.itinero1
|
|||
case ValueTuple<string, string> unpack:
|
||||
return unpack.Item1 + "[" + unpack.Item2 + "]";
|
||||
case IEnumerable<object> ls:
|
||||
var t = (c.Types.First() as ListType).InnerType;
|
||||
var t = ((ListType) c.Types.First()).InnerType;
|
||||
return "{" + string.Join(", ", ls.Select(obj =>
|
||||
{
|
||||
var objInConstant = new Constant(t, obj);
|
||||
|
|
|
@ -49,9 +49,8 @@ namespace AspectedRouting.IO.itinero1
|
|||
|
||||
foreach (var (calledInFunction, expr) in memberships)
|
||||
{
|
||||
|
||||
func.Add($"\n\n -- {calledInFunction} ---");
|
||||
|
||||
|
||||
var usedParameters = expr.UsedParameters().Select(param => param.ParamName.TrimStart('#')).ToHashSet();
|
||||
|
||||
// First, we calculate the value for the default parameters
|
||||
|
@ -65,7 +64,8 @@ namespace AspectedRouting.IO.itinero1
|
|||
var tagKey = "_relation:" + calledInFunction.FunctionName();
|
||||
_neededKeys.Add(tagKey);
|
||||
func.Add(
|
||||
" -- " + tagKey +" is the default value, which will be overwritten in 'remove_relation_prefix' for behaviours having a different parameter settign");
|
||||
" -- " + tagKey +
|
||||
" is the default value, which will be overwritten in 'remove_relation_prefix' for behaviours having a different parameter settign");
|
||||
func.Add($" result.attributes_to_keep[\"{tagKey}\"] = \"yes\"");
|
||||
func.Add(" end");
|
||||
|
||||
|
@ -76,7 +76,7 @@ namespace AspectedRouting.IO.itinero1
|
|||
func.Add(" -- No parameter dependence for aspect " + calledInFunction);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
foreach (var (behaviourName, parameters) in profile.Behaviours)
|
||||
{
|
||||
if (usedParameters.Except(parameters.Keys.ToHashSet()).Any())
|
||||
|
@ -99,8 +99,6 @@ namespace AspectedRouting.IO.itinero1
|
|||
func.Add($" result.attributes_to_keep[\"{tagKey}\"] = \"yes\"");
|
||||
func.Add(" end");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
func.Add("end");
|
||||
|
@ -173,7 +171,7 @@ namespace AspectedRouting.IO.itinero1
|
|||
foreach (var (parameterName, expression) in profile.Priority)
|
||||
{
|
||||
var paramInLua = ToLua(new Parameter(parameterName));
|
||||
|
||||
|
||||
|
||||
var exprInLua = ToLua(expression);
|
||||
var subs = new Curry(Typs.Tags, new Var(("a"))).UnificationTable(expression.Types.First());
|
||||
|
@ -184,21 +182,22 @@ namespace AspectedRouting.IO.itinero1
|
|||
exprInLua = "parse(" + exprInLua + ")";
|
||||
}
|
||||
|
||||
impl += "\n "+string.Join("\n ",
|
||||
$"if({paramInLua} ~= 0) then",
|
||||
$" priority = priority + {paramInLua} * {exprInLua}",
|
||||
"end"
|
||||
);
|
||||
|
||||
|
||||
impl += "\n " + string.Join("\n ",
|
||||
$"if({paramInLua} ~= 0) then",
|
||||
$" priority = priority + {paramInLua} * {exprInLua}",
|
||||
"end"
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
impl += string.Join("\n",
|
||||
"",
|
||||
"",
|
||||
" if (priority <= 0) then",
|
||||
" result.access = 0",
|
||||
" return",
|
||||
" end",
|
||||
"",
|
||||
" -- put all the values into the result-table, as needed for itinero",
|
||||
" result.access = 1",
|
||||
" result.speed = speed",
|
||||
" result.factor = 1 / priority",
|
||||
|
@ -248,8 +247,8 @@ namespace AspectedRouting.IO.itinero1
|
|||
subParams.TryGetValue("description", out var description);
|
||||
profiles.Add(
|
||||
string.Join(",\n ",
|
||||
$" name = \"{name}\"",
|
||||
" function_name = \"profile_" + functionName + "\"",
|
||||
$" name = \"{name.FunctionName()}\"",
|
||||
" function_name = \"behaviour_" + functionName.FunctionName() + "\"",
|
||||
" metric = \"custom\""
|
||||
)
|
||||
);
|
||||
|
@ -260,9 +259,10 @@ namespace AspectedRouting.IO.itinero1
|
|||
"--[[",
|
||||
description,
|
||||
"]]",
|
||||
"function profile_" + functionName + "(tags, result)",
|
||||
"function behaviour_" + functionName.FunctionName() + "(tags, result)",
|
||||
$" tags = remove_relation_prefix(tags, \"{name.FunctionName()}\")",
|
||||
" local parameters = default_parameters()",
|
||||
" parameters.name = \"" + functionName + "\"",
|
||||
""
|
||||
);
|
||||
|
||||
|
|
|
@ -46,7 +46,7 @@ namespace AspectedRouting.IO.itinero1
|
|||
}
|
||||
// function unit_test_profile(profile_function, profile_name, index, expected, tags)
|
||||
|
||||
return $"unit_test_profile(profile_bicycle_{testSuite.BehaviourName.FunctionName()}, " +
|
||||
return $"unit_test_profile(behaviour_{testSuite.Profile.Name.FunctionName()}_{testSuite.BehaviourName.FunctionName()}, " +
|
||||
$"\"{testSuite.BehaviourName}\", " +
|
||||
$"{index}, " +
|
||||
$"{{access = \"{D(expected.Access)}\", speed = {expected.Speed}, oneway = \"{D(expected.Oneway)}\", weight = {expected.Weight} }}, " +
|
||||
|
@ -65,7 +65,7 @@ namespace AspectedRouting.IO.itinero1
|
|||
}
|
||||
|
||||
|
||||
public void AddTestSuite(FunctionTestSuite testSuite)
|
||||
public void AddTestSuite(AspectTestSuite testSuite)
|
||||
{
|
||||
var fName = testSuite.FunctionToApply.Name;
|
||||
var tests = string.Join("\n",
|
||||
|
|
|
@ -51,14 +51,15 @@ namespace AspectedRouting.IO.jsonParser
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
private static ProfileMetaData ParseProfile(this JsonElement e, Context context, FileInfo filepath)
|
||||
{
|
||||
if (!e.TryGetProperty("priority",out _))
|
||||
if (!e.TryGetProperty("speed", out _))
|
||||
{
|
||||
// not a profile
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
var name = e.Get("name");
|
||||
var author = e.TryGet("author");
|
||||
if (filepath != null && !(name + ".json").ToLower().Equals(filepath.Name.ToLower()))
|
||||
|
@ -67,9 +68,20 @@ namespace AspectedRouting.IO.jsonParser
|
|||
$"filename is {filepath.Name}, declared name is {name}");
|
||||
}
|
||||
|
||||
var vehicleTypes = e.GetProperty("vehicletypes").EnumerateArray().Select(
|
||||
JsonElement GetTopProperty(string name)
|
||||
{
|
||||
if (e.TryGetProperty(name, out var p))
|
||||
{
|
||||
return p;
|
||||
}
|
||||
|
||||
throw new ArgumentException(
|
||||
filepath + " is not a valid profile; it does not contain the obligated parameter " + name);
|
||||
}
|
||||
|
||||
var vehicleTypes = GetTopProperty("vehicletypes").EnumerateArray().Select(
|
||||
el => el.GetString()).ToList();
|
||||
var metadata = e.GetProperty("metadata").EnumerateArray().Select(
|
||||
var metadata = GetTopProperty("metadata").EnumerateArray().Select(
|
||||
el => el.GetString()).ToList();
|
||||
|
||||
|
||||
|
@ -111,7 +123,7 @@ namespace AspectedRouting.IO.jsonParser
|
|||
|
||||
var profiles = new Dictionary<string, Dictionary<string, IExpression>>();
|
||||
|
||||
foreach (var profile in e.GetProperty("behaviours").EnumerateObject())
|
||||
foreach (var profile in GetTopProperty("behaviours").EnumerateObject())
|
||||
{
|
||||
profiles[profile.Name] = ParseParameters(profile.Value);
|
||||
}
|
||||
|
@ -169,7 +181,18 @@ namespace AspectedRouting.IO.jsonParser
|
|||
try
|
||||
{
|
||||
var simpleMapping = new Mapping(keys, exprs);
|
||||
return new Apply(_mappingWrapper, simpleMapping);
|
||||
|
||||
var wrapped = (IExpression) new Apply(_mappingWrapper, simpleMapping);
|
||||
if (keys.Count == 1)
|
||||
{
|
||||
// We can interpret this directly without going through a list
|
||||
|
||||
wrapped = Funcs.Either(Funcs.Id,
|
||||
new Apply(Funcs.Dot, Funcs.Head),
|
||||
wrapped);
|
||||
}
|
||||
|
||||
return wrapped;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
@ -446,7 +469,7 @@ namespace AspectedRouting.IO.jsonParser
|
|||
throw new ArgumentException($"Filename does not match the defined name: " +
|
||||
$"filename is {filepath}, declared name is {name}");
|
||||
}
|
||||
|
||||
|
||||
var keys = (IEnumerable<string>) expr.PossibleTags()?.Keys ?? new List<string>();
|
||||
foreach (var key in keys)
|
||||
{
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using AspectedRouting.Language;
|
||||
using AspectedRouting.Language.Expression;
|
||||
using AspectedRouting.Language.Functions;
|
||||
using AspectedRouting.Language.Typ;
|
||||
|
||||
|
@ -14,9 +12,14 @@ namespace AspectedRouting.IO.jsonParser
|
|||
{
|
||||
private static IExpression ParseProfileProperty(JsonElement e, Context c, string property)
|
||||
{
|
||||
|
||||
if (!e.TryGetProperty(property, out var prop))
|
||||
{
|
||||
throw new ArgumentException("Not a valid profile: the declaration expression for '" + property +
|
||||
"' is missing");
|
||||
}
|
||||
try
|
||||
{
|
||||
var prop = e.GetProperty(property);
|
||||
return ParseExpression(prop, c)
|
||||
.Specialize(new Curry(Typs.Tags, new Var("a")))
|
||||
.Optimize();
|
||||
|
@ -57,7 +60,7 @@ namespace AspectedRouting.IO.jsonParser
|
|||
ps[nm] = new Constant(Typs.Bool, "yes");
|
||||
break;
|
||||
case JsonValueKind.Array:
|
||||
var list = obj.Value.EnumerateArray().Select(e => e.ToString()).ToList();
|
||||
var list = obj.Value.EnumerateArray().Select(x => x.ToString()).ToList();
|
||||
ps[nm] = new Constant(new ListType(Typs.String),list);
|
||||
break;
|
||||
default:
|
||||
|
|
|
@ -11,4 +11,20 @@ function debug_table(table, prefix)
|
|||
end
|
||||
end
|
||||
print("")
|
||||
end
|
||||
|
||||
function debug_table_str(table, prefix)
|
||||
if (prefix == nil) then
|
||||
prefix = ""
|
||||
end
|
||||
local str = "";
|
||||
for k, v in pairs(table) do
|
||||
|
||||
if (type(v) == "table") then
|
||||
str = str .. "," .. debug_table_str(v, " ")
|
||||
else
|
||||
str = str .. "," .. (prefix .. tostring(k) .. " = " .. tostring(v))
|
||||
end
|
||||
end
|
||||
return str
|
||||
end
|
11
AspectedRouting/IO/lua/head.lua
Normal file
11
AspectedRouting/IO/lua/head.lua
Normal file
|
@ -0,0 +1,11 @@
|
|||
function head(ls)
|
||||
if(ls == nil) then
|
||||
return nil
|
||||
end
|
||||
for _, v in ipairs(ls) do
|
||||
if(v ~= nil) then
|
||||
return v
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
|
@ -129,7 +129,7 @@ namespace AspectedRouting.Language
|
|||
public static Dictionary<string, (List<Type> Types, string inFunction)> UsedParameters(
|
||||
this ProfileMetaData profile, Context context)
|
||||
{
|
||||
var parameters = new Dictionary<string, (List<Type> Types, string inFunction)>();
|
||||
var parameters = new Dictionary<string, (List<Type> Types, string usageLocation)>();
|
||||
|
||||
|
||||
void AddParams(IExpression e, string inFunction)
|
||||
|
@ -145,8 +145,8 @@ namespace AspectedRouting.Language
|
|||
{
|
||||
throw new ArgumentException("Inconsistent parameter usage: the paremeter " +
|
||||
param.ParamName + " is used\n" +
|
||||
$" in function {oldUsage} as {string.Join(",", types)}\n" +
|
||||
$" in function {inFunction} as {string.Join(",", param.Types)}\n" +
|
||||
$" in {oldUsage} as {string.Join(",", types)}\n" +
|
||||
$" in {inFunction} as {string.Join(",", param.Types)}\n" +
|
||||
$"which can not be unified");
|
||||
}
|
||||
}
|
||||
|
@ -158,9 +158,9 @@ namespace AspectedRouting.Language
|
|||
}
|
||||
|
||||
|
||||
AddParams(profile.Access, profile.Name + ".access");
|
||||
AddParams(profile.Oneway, profile.Name + ".oneway");
|
||||
AddParams(profile.Speed, profile.Name + ".speed");
|
||||
AddParams(profile.Access, "profile definition for " + profile.Name + ".access");
|
||||
AddParams(profile.Oneway, "profile definition for " + profile.Name + ".oneway");
|
||||
AddParams(profile.Speed, "profile definition for " + profile.Name + ".speed");
|
||||
|
||||
foreach (var (key, expr) in profile.Priority)
|
||||
{
|
||||
|
@ -173,7 +173,12 @@ namespace AspectedRouting.Language
|
|||
foreach (var calledFunction in calledFunctions)
|
||||
{
|
||||
var func = context.GetFunction(calledFunction);
|
||||
AddParams(func, calledFunction);
|
||||
if (func is AspectMetadata meta && meta.ProfileInternal)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
AddParams(func, "function " + calledFunction);
|
||||
}
|
||||
|
||||
|
||||
|
@ -257,7 +262,7 @@ namespace AspectedRouting.Language
|
|||
|
||||
public static void SanityCheckProfile(this ProfileMetaData pmd, Context context)
|
||||
{
|
||||
var defaultParameters = pmd.DefaultParameters.Keys;
|
||||
var defaultParameters = pmd.DefaultParameters.Keys.Select(k => k.TrimStart('#'));
|
||||
|
||||
|
||||
var usedMetadata = pmd.UsedParameters(context);
|
||||
|
@ -275,7 +280,7 @@ namespace AspectedRouting.Language
|
|||
return metaInfo;
|
||||
}
|
||||
|
||||
var usedParameters = usedMetadata.Keys.Select(key => key.TrimStart('#'));
|
||||
var usedParameters = usedMetadata.Keys.Select(key => key.TrimStart('#')).ToList();
|
||||
|
||||
var diff = usedParameters.ToHashSet().Except(defaultParameters).ToList();
|
||||
if (diff.Any())
|
||||
|
@ -296,9 +301,9 @@ namespace AspectedRouting.Language
|
|||
{
|
||||
var sum = 0.0;
|
||||
var explanation = "";
|
||||
paramsUsedInBehaviour.UnionWith(behaviourParams.Keys.Select(k => k.Trim('#')));
|
||||
foreach (var (paramName, _) in pmd.Priority)
|
||||
{
|
||||
paramsUsedInBehaviour.Add(paramName);
|
||||
if (!pmd.DefaultParameters.ContainsKey(paramName))
|
||||
{
|
||||
throw new ArgumentException(
|
||||
|
|
|
@ -32,12 +32,7 @@ namespace AspectedRouting.Language.Expression
|
|||
|
||||
public object Evaluate(Context c, params IExpression[] arguments)
|
||||
{
|
||||
if (ProfileInternal && arguments.Length > 0)
|
||||
{
|
||||
var tags = (Dictionary<string, string>) arguments[0].Evaluate(c);
|
||||
|
||||
}
|
||||
|
||||
|
||||
return ExpressionImplementation.Evaluate(c, arguments);
|
||||
}
|
||||
|
||||
|
|
|
@ -49,6 +49,7 @@ namespace AspectedRouting.Language
|
|||
public static readonly Function EitherFunc = new EitherFunc();
|
||||
|
||||
public static readonly Function StringStringToTags = new StringStringToTagsFunction();
|
||||
public static readonly Function Head = new HeadFunction();
|
||||
|
||||
public static void AddBuiltin(Function f, string name = null)
|
||||
{
|
||||
|
|
|
@ -40,7 +40,9 @@ namespace AspectedRouting.Language.Functions
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new Exception($"While creating a list with members {string.Join(", ", exprs.Select(e => e.Optimize()))} {e.Message}", e);
|
||||
throw new Exception($"While creating a list with members "+
|
||||
string.Join(", ", exprs.Select(x => x.Optimize()))+
|
||||
$" {e.Message}", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
62
AspectedRouting/Language/Functions/HeadFunction.cs
Normal file
62
AspectedRouting/Language/Functions/HeadFunction.cs
Normal file
|
@ -0,0 +1,62 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using AspectedRouting.Language.Expression;
|
||||
using AspectedRouting.Language.Typ;
|
||||
|
||||
namespace AspectedRouting.Language.Functions
|
||||
{
|
||||
public class HeadFunction : Function
|
||||
{
|
||||
public override string Description { get; } =
|
||||
"Select the first non-null value of a list; returns 'null' on empty list or on null";
|
||||
|
||||
public override List<string> ArgNames { get; } = new List<string> {"ls"};
|
||||
|
||||
|
||||
public HeadFunction() : base("head", true,
|
||||
new[]
|
||||
{
|
||||
new Curry(new ListType(new Var("a")),
|
||||
new Var("a"))
|
||||
}
|
||||
)
|
||||
{
|
||||
}
|
||||
|
||||
private HeadFunction(IEnumerable<Type> unified) : base("head", unified)
|
||||
{
|
||||
}
|
||||
|
||||
public override object Evaluate(Context c, params IExpression[] arguments)
|
||||
{
|
||||
var o = arguments[0].Evaluate(c);
|
||||
while (o is IExpression e)
|
||||
{
|
||||
o = e.Evaluate(c);
|
||||
}
|
||||
|
||||
if (!(o is IEnumerable<object> ls)) return null;
|
||||
|
||||
foreach (var a in ls)
|
||||
{
|
||||
if (a != null)
|
||||
{
|
||||
return a;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public override IExpression Specialize(IEnumerable<Type> allowedTypes)
|
||||
{
|
||||
var unified = Types.SpecializeTo(allowedTypes);
|
||||
if (unified == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return new HeadFunction(unified);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -11,7 +11,6 @@ namespace AspectedRouting.Language.Functions
|
|||
{
|
||||
public readonly Dictionary<string, IExpression> StringToResultFunctions;
|
||||
|
||||
|
||||
public Mapping(IReadOnlyList<string> keys, IEnumerable<IExpression> expressions) :
|
||||
base(
|
||||
$"mapping ({MappingToString(keys, expressions.SpecializeToCommonTypes(out var specializedTypes, out var specializedExpressions))})",
|
||||
|
@ -69,11 +68,13 @@ namespace AspectedRouting.Language.Functions
|
|||
|
||||
public override object Evaluate(Context c, params IExpression[] arguments)
|
||||
{
|
||||
|
||||
var s = arguments[0].Evaluate(c);
|
||||
while (s is Constant constant)
|
||||
{
|
||||
s = constant.Evaluate(c);
|
||||
}
|
||||
|
||||
|
||||
var key = (string) s;
|
||||
var otherARgs = arguments.ToList().GetRange(1, arguments.Length - 1);
|
||||
|
|
|
@ -4,23 +4,20 @@ using AspectedRouting.Language.Typ;
|
|||
|
||||
namespace AspectedRouting.Language.Functions
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts a function 'string -> string -> a' onto a function 'tags -> [a]'
|
||||
/// </summary>
|
||||
public class StringStringToTagsFunction : Function
|
||||
{
|
||||
public override string Description { get; } =
|
||||
"stringToTags converts a function `string -> string -> a` into a function `tags -> [a]`";
|
||||
public override List<string> ArgNames { get; } = new List<string>{"f","tags"};
|
||||
|
||||
private static Type baseFunction =
|
||||
private static readonly Type _baseFunction =
|
||||
Curry.ConstructFrom(new Var("a"), Typs.String, Typs.String);
|
||||
|
||||
|
||||
public StringStringToTagsFunction() : base("stringToTags", true,
|
||||
new[]
|
||||
{
|
||||
new Curry(baseFunction,
|
||||
new Curry(_baseFunction,
|
||||
new Curry(Typs.Tags, new ListType(new Var("a"))))
|
||||
}
|
||||
)
|
||||
|
|
|
@ -20,6 +20,9 @@ namespace AspectedRouting.Language.Functions
|
|||
new Curry(new ListType(Typs.Bool), Typs.Int),
|
||||
})
|
||||
{
|
||||
Funcs.AddBuiltin(this, "plus");
|
||||
Funcs.AddBuiltin(this, "add");
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,23 +0,0 @@
|
|||
|
||||
# Functions
|
||||
|
||||
- `$funcName` indicates a builtin function
|
||||
- `#parameter` indicates a configurable parameter
|
||||
|
||||
A profile is a function which maps a set of tags onto a value. It is basically Haskell, except that functions can have _multiple_ types. During typechecking, some types will turn out not to be possible and they will dissappear, potentially fixing the implementation of the function itself.
|
||||
|
||||
|
||||
# Profile
|
||||
|
||||
- Metdata: these tags will be copied to the routerdb, but can not be used for routeplanning. A prime example is `name` (the streetname), as it is useful for routeplanning but very useful for navigation afterwards
|
||||
- vehciletypes: used for turn restrictions, legacy for use with itinero 1.0
|
||||
- defaults: a dictionary of `{"#paramName": "value"}`, used in determining the weight of an edge. note: the `#` has to be included
|
||||
- `access`, `oneway`, `speed`: three expressions indicating respectively if access is allowed (if not equals to no), in what direction one can drive (one of `with`, `against` or `both`) and how fast one will go there. (Hint: this should be capped on legal_max_speed)
|
||||
- `weights`: a table of `{'#paramName', expression}` determining the weight (aka COST) of a way, per meter. The formula used is `paramName * expression + paramName0 * expression0 + ...` (`$speed`, `$access` and `$oneway` can be used here to indicate the earlier defined respective aspects). Use a weight == 1 to get the shortest route or `$inv: $speed` to get the fastest route
|
||||
|
||||
|
||||
# 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
|
|
@ -1,539 +0,0 @@
|
|||
-- The different profiles
|
||||
profiles = {
|
||||
|
||||
|
||||
|
||||
{
|
||||
name = "b2w",
|
||||
description = "[Custom] Route for bike2work. Same as 'commute' ATM. Migth diverge in the future. In use.",
|
||||
function_name = "determine_weights_commute",
|
||||
metric = "custom"
|
||||
},
|
||||
{
|
||||
name = "networks",
|
||||
description = "A recreative route following existing cycling networks. Might make longer detours",
|
||||
function_name = "determine_weights_networks",
|
||||
metric = "custom"
|
||||
},
|
||||
{
|
||||
name = "node_network",
|
||||
description = "A recreative route following existing cycle node networks. Might make longer detours",
|
||||
function_name = "determine_weights_networks_node_network",
|
||||
metric = "custom"
|
||||
},
|
||||
|
||||
{
|
||||
name = "cycle_highway",
|
||||
description = "A functional route, preferring cycle_highways or the Brussels Mobility network. If none are availale, will favour speed",
|
||||
function_name = "determine_weights_cycle_highways",
|
||||
metric = "custom"
|
||||
},
|
||||
{
|
||||
name = "commute",
|
||||
description = "A functional route which is aimed to commuters. It is a mix of safety, comfort, a pinch of speed and cycle_highway",
|
||||
function_name = "determine_weights_commute",
|
||||
metric = "custom"
|
||||
},
|
||||
{
|
||||
name = "anyways.network", -- prefers the anyways network, thus where `operator=Anyways` -
|
||||
description = "A route following the cycle network with the operator 'Anyways'. This is for use in ShortCut/Impact as there are no such networks existing in OSM.",
|
||||
function_name = "determine_weights_anyways_network",
|
||||
metric = "custom"
|
||||
},
|
||||
|
||||
{
|
||||
name = "commute.race",
|
||||
description = "[Deprecated] Same as commute, please use that one instead. Might be unused",
|
||||
function_name = "determine_weights_cycle_highways", -- TODO tweak this profile
|
||||
metric = "custom"
|
||||
},
|
||||
{
|
||||
name = "opa",
|
||||
description = "[Deprecated][Custom] Same as fastest, please use that one instead. Might be unused. Note: all profiles take anyways:* tags into account",
|
||||
function_name = "determine_weights",
|
||||
metric = "custom"
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
-- Returns 1 if no access restrictions, 0 if it is permissive
|
||||
function determine_permissive_score(attributes, result)
|
||||
if (attributes.access == "permissive") then
|
||||
return 0
|
||||
end
|
||||
return 1
|
||||
end
|
||||
|
||||
|
||||
--[[ Gives 1 if this is on a cycling network.
|
||||
If attributes.settings.cycle_network_operator is defined, then only follow these networks
|
||||
If attributes.settings.cycle_network_highway is defined, then only follow the 'Fietssnelwegen'
|
||||
|
||||
The colour _will only be copied_ if the score is one
|
||||
]]
|
||||
cycle_network_attributes_to_match = { "cycle_network_highway", "cycle_network_node_network" }
|
||||
function determine_network_score(attributes, result)
|
||||
|
||||
if (attributes.cycle_network == nil) then
|
||||
return 0
|
||||
end
|
||||
|
||||
for i = 1, #cycle_network_attributes_to_match do
|
||||
|
||||
local key = cycle_network_attributes_to_match[i]
|
||||
local expected = attributes.settings[key]
|
||||
|
||||
|
||||
if (expected ~= nil) then
|
||||
-- we have to check that this tag matches
|
||||
local value = attributes[key]
|
||||
if (value == nil) then
|
||||
-- the waysegment doesn't have this attribute - abort
|
||||
return 0
|
||||
end
|
||||
|
||||
if (value ~= expected) then
|
||||
-- the way segment doesn't have the expected value - abort
|
||||
return 0
|
||||
end
|
||||
|
||||
-- hooray, we have a match!
|
||||
result.attributes_to_keep[key] = value
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
if (attributes.settings.cycle_network_operator ~= nil) then
|
||||
local expected = attributes.settings.cycle_network_operator;
|
||||
expected = expected:gsub(" ", "");
|
||||
if (attributes[expected] ~= "yes") then
|
||||
return 0
|
||||
end
|
||||
end
|
||||
|
||||
result.attributes_to_keep.cycle_network = "yes";
|
||||
|
||||
return 1
|
||||
end
|
||||
|
||||
function determine_weights_comfort_safety(attributes, result)
|
||||
|
||||
-- we add a 'settings' element to attributes, they can be used by other profiles
|
||||
if (attributes.settings == nil) then
|
||||
attributes.settings = {}
|
||||
attributes.settings.default_speed = 15
|
||||
attributes.settings.min_speed = 3
|
||||
attributes.settings.max_speed = 30
|
||||
|
||||
attributes.settings.safety_weight = 1
|
||||
attributes.settings.time_weight = 0
|
||||
attributes.settings.comfort_weight = 1
|
||||
attributes.settings.network_weight = 0
|
||||
attributes.settings.clear_restrictions_preference = 1;
|
||||
end
|
||||
|
||||
determine_weights(attributes, result)
|
||||
end
|
||||
|
||||
|
||||
function determine_weights_commute(attributes, result)
|
||||
-- lots of safety and comfort, but also slightly prefers 'fietssnelwegen' and 'Brussels Mobility' 'route cyclable', as they are functional
|
||||
|
||||
if (attributes.settings == nil) then
|
||||
attributes.settings = {}
|
||||
attributes.settings.default_speed = 15
|
||||
attributes.settings.min_speed = 3
|
||||
attributes.settings.max_speed = 30
|
||||
|
||||
attributes.settings.clear_restrictions_preference = 1
|
||||
attributes.settings.safety_weight = 3
|
||||
attributes.settings.time_weight = 1
|
||||
attributes.settings.comfort_weight = 2
|
||||
|
||||
attributes.settings.network_weight = 3
|
||||
|
||||
|
||||
|
||||
end
|
||||
|
||||
determine_weights(attributes, result)
|
||||
|
||||
-- commute is a big exception to the other profiles, as in that we overwrite the result.factor here
|
||||
-- this is in order to support _two_ cycling networks
|
||||
|
||||
|
||||
local safety = determine_safety(attributes, result);
|
||||
local comfort = determine_comfort(attributes, result);
|
||||
|
||||
attributes.cycle_network = "yes"
|
||||
attributes.settings.cycle_network_highway = "yes"
|
||||
local cycle_highway_score = determine_network_score(attributes, result);
|
||||
attributes.settings.cycle_network_highway = nil
|
||||
|
||||
|
||||
attributes.cycle_network = "yes"
|
||||
attributes.settings.cycle_network_operator = "Brussels Mobility"
|
||||
local brussels_mobility_score = determine_network_score(attributes, result);
|
||||
attributes.settings.cycle_network_operator = nil
|
||||
|
||||
local network = math.min(1, cycle_highway_score + brussels_mobility_score);
|
||||
|
||||
|
||||
local clear_restrictions = determine_permissive_score(attributes, result);
|
||||
|
||||
|
||||
result.factor = 1 /
|
||||
(safety * attributes.settings.safety_weight +
|
||||
result.speed * attributes.settings.time_weight +
|
||||
comfort * attributes.settings.comfort_weight +
|
||||
network * attributes.settings.network_weight +
|
||||
clear_restrictions * attributes.settings.clear_restrictions_preference);
|
||||
|
||||
end
|
||||
|
||||
function determine_weights_cycle_highways(attributes, result)
|
||||
-- heavily prefers 'fietssnelwegen' and 'Brussels Mobility' 'route cyclable', as they are functional
|
||||
|
||||
if (attributes.settings == nil) then
|
||||
attributes.settings = {}
|
||||
attributes.settings.default_speed = 15
|
||||
attributes.settings.min_speed = 3
|
||||
attributes.settings.max_speed = 30
|
||||
|
||||
attributes.settings.clear_restrictions_preference = 1
|
||||
attributes.settings.safety_weight = 1
|
||||
attributes.settings.time_weight = 0
|
||||
attributes.settings.comfort_weight = 0
|
||||
|
||||
attributes.settings.network_weight = 20
|
||||
|
||||
|
||||
|
||||
end
|
||||
|
||||
determine_weights(attributes, result)
|
||||
|
||||
-- commute is a big exception to the other profiles, as in that we overwrite the result.factor here
|
||||
-- this is in order to support _two_ cycling networks
|
||||
|
||||
|
||||
local safety = determine_safety(attributes, result);
|
||||
local comfort = determine_comfort(attributes, result);
|
||||
|
||||
attributes.cycle_network = "yes"
|
||||
attributes.settings.cycle_network_highway = "yes"
|
||||
local cycle_highway_score = determine_network_score(attributes, result);
|
||||
attributes.settings.cycle_network_highway = nil
|
||||
|
||||
|
||||
attributes.cycle_network = "yes"
|
||||
attributes.settings.cycle_network_operator = "Brussels Mobility"
|
||||
local brussels_mobility_score = determine_network_score(attributes, result);
|
||||
attributes.settings.cycle_network_operator = nil
|
||||
|
||||
local network = math.min(1, cycle_highway_score + brussels_mobility_score);
|
||||
|
||||
|
||||
local clear_restrictions = determine_permissive_score(attributes, result);
|
||||
|
||||
|
||||
result.factor = 1 /
|
||||
(safety * attributes.settings.safety_weight +
|
||||
result.speed * attributes.settings.time_weight +
|
||||
comfort * attributes.settings.comfort_weight +
|
||||
network * attributes.settings.network_weight +
|
||||
clear_restrictions * attributes.settings.clear_restrictions_preference);
|
||||
|
||||
end
|
||||
|
||||
|
||||
function determine_weights_networks(attributes, result)
|
||||
|
||||
-- we add a 'settings' element to attributes, they can be used by other profiles
|
||||
if (attributes.settings == nil) then
|
||||
attributes.settings = {}
|
||||
attributes.settings.default_speed = 15
|
||||
attributes.settings.min_speed = 3
|
||||
attributes.settings.max_speed = 30
|
||||
|
||||
attributes.settings.safety_weight = 1
|
||||
attributes.settings.time_weight = 0
|
||||
attributes.settings.comfort_weight = 0
|
||||
attributes.settings.network_weight = 3
|
||||
attributes.settings.clear_restrictions_preference = 1;
|
||||
end
|
||||
|
||||
determine_weights(attributes, result)
|
||||
end
|
||||
|
||||
function determine_weights_networks_node_network(attributes, result)
|
||||
|
||||
-- we add a 'settings' element to attributes, they can be used by other profiles
|
||||
if (attributes.settings == nil) then
|
||||
attributes.settings = {}
|
||||
attributes.settings.default_speed = 15
|
||||
attributes.settings.min_speed = 3
|
||||
attributes.settings.max_speed = 30
|
||||
|
||||
attributes.settings.safety_weight = 1
|
||||
attributes.settings.time_weight = 0
|
||||
attributes.settings.comfort_weight = 0
|
||||
attributes.settings.network_weight = 10
|
||||
attributes.settings.clear_restrictions_preference = 1
|
||||
|
||||
attributes.cycle_network = "yes"
|
||||
attributes.settings.cycle_network_node_network = "yes"
|
||||
end
|
||||
|
||||
determine_weights(attributes, result)
|
||||
end
|
||||
|
||||
|
||||
|
||||
function determine_weights_anyways_network(attributes, result)
|
||||
|
||||
|
||||
-- we add a 'settings' element to attributes, they can be used by other profiles
|
||||
if (attributes.settings == nil) then
|
||||
attributes.settings = {}
|
||||
attributes.settings.default_speed = 15
|
||||
attributes.settings.min_speed = 3
|
||||
attributes.settings.max_speed = 30
|
||||
|
||||
attributes.settings.safety_weight = 0
|
||||
attributes.settings.time_weight = 0
|
||||
attributes.settings.comfort_weight = 0
|
||||
attributes.settings.network_weight = 10
|
||||
attributes.settings.clear_restrictions_preference = 1;
|
||||
|
||||
attributes.settings.cycle_network_operator = "Anyways"
|
||||
end
|
||||
|
||||
|
||||
attributes.access = nil -- little hack: remove ALL access restrictions in order to go onto private parts
|
||||
|
||||
determine_weights(attributes, result)
|
||||
end
|
||||
|
||||
|
||||
function determine_weights(attributes, result)
|
||||
|
||||
-- we add a 'settings' element to attributes, they can be used by other profiles
|
||||
if (attributes.settings == nil) then
|
||||
attributes.settings = {}
|
||||
attributes.settings.default_speed = 15
|
||||
attributes.settings.min_speed = 3
|
||||
attributes.settings.max_speed = 30
|
||||
|
||||
attributes.settings.safety_weight = 0
|
||||
attributes.settings.time_weight = 0
|
||||
attributes.settings.comfort_weight = 0
|
||||
attributes.settings.network_weight = 0
|
||||
attributes.settings.clear_restrictions_preference = 1; -- if 1: discourage 'permissive' access tags
|
||||
|
||||
attributes.settings.cycle_network_operator = nil -- e.g. "Stad Genk" of "Brussels Mobility"
|
||||
attributes.settings.cycle_network_highway = nil -- if "yes", will only follow the 'fietsostrades'
|
||||
attributes.settings.cycle_network_node_network = nil -- if "yes", will only follow the 'node_networks'
|
||||
end
|
||||
|
||||
|
||||
-- Init default values
|
||||
result.access = 0
|
||||
result.speed = 0
|
||||
result.factor = 1
|
||||
result.direction = 0
|
||||
result.canstop = true
|
||||
result.attributes_to_keep = {}
|
||||
|
||||
-- Do misc preprocessing, such as handling the ferry case
|
||||
preprocess(attributes, result);
|
||||
|
||||
|
||||
-- 1) Can we enter this segment legally?
|
||||
if (not can_access_legally(attributes)) then
|
||||
return
|
||||
end
|
||||
result.access = 1;
|
||||
result.attributes_to_keep.highway = attributes.highway
|
||||
result.attributes_to_keep.access = attributes.access
|
||||
|
||||
|
||||
-- 2) Is this a oneway?
|
||||
determine_oneway(attributes, result)
|
||||
|
||||
-- 3) How fast would one drive on average on this segment?
|
||||
determine_speed(attributes, result);
|
||||
-- Cap using settings and legal max speed
|
||||
result.speed = math.max(attributes.settings.min_speed, result.speed)
|
||||
result.speed = math.min(attributes.settings.max_speed, result.speed)
|
||||
local legal_max_speed = highway_types[attributes.highway].speed
|
||||
result.speed = math.min(legal_max_speed, result.speed)
|
||||
|
||||
|
||||
-- 4) What is the factor of this segment?
|
||||
--[[
|
||||
This is determined by multiple factors and the weight that is given to them by the settings
|
||||
Factors are:
|
||||
- safety
|
||||
- comfort
|
||||
- ...
|
||||
]]
|
||||
|
||||
local safety = determine_safety(attributes, result);
|
||||
local comfort = determine_comfort(attributes, result);
|
||||
local network = determine_network_score(attributes, result);
|
||||
local clear_restrictions = determine_permissive_score(attributes, result);
|
||||
|
||||
result.factor = 1 /
|
||||
(safety * attributes.settings.safety_weight +
|
||||
result.speed * attributes.settings.time_weight +
|
||||
comfort * attributes.settings.comfort_weight +
|
||||
network * attributes.settings.network_weight +
|
||||
clear_restrictions * attributes.settings.clear_restrictions_preference);
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
-- Unit test: are all tags in profile_whitelist
|
||||
cycling_network_operators_to_tag = { "Stad Genk", "Anyways", "Brussels Mobility" }
|
||||
cycling_network_tags = { "cycle_network", "cycle_network_highway", "cycle_network_operator", "cycle_network_colour", "cycle_network_node_network", "operator", "StadGenk", "Anyways", "BrusselsMobility" }
|
||||
|
||||
|
||||
--[[ Copies all relevant relation tags onto the way
|
||||
All these tags start with 'cycle_network', e.g. cycle_network_colour
|
||||
|
||||
Some of them are exclusively for the metadata (cyclecolour as prime example)
|
||||
|
||||
Note that all the tags used are listed in 'cycling_network_tags' as well, for unit testing purposes
|
||||
|
||||
]]
|
||||
function cycling_network_tag_processor(attributes, result)
|
||||
result.attributes_to_keep.cycle_network = "yes"
|
||||
|
||||
if (attributes.cycle_network == "cycle_highway"
|
||||
and attributes.state ~= "proposed") then
|
||||
result.attributes_to_keep.cycle_network_highway = "yes"
|
||||
end
|
||||
|
||||
if (attributes.cycle_network == "node_network") then
|
||||
result.cycle_network_node_network = "yes"
|
||||
end
|
||||
|
||||
|
||||
if (attributes.operator ~= nil) then
|
||||
for k, v in pairs(cycling_network_operators_to_tag) do
|
||||
v = v:gsub(" ", "") -- remove spaces from the operator as lua can't handle them
|
||||
result.attributes_to_keep[v] = "yes"
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
if (attributes.colour ~= nil) then
|
||||
result.attributes_to_keep.cycle_network_colour = attributes.colour
|
||||
end
|
||||
|
||||
if (attributes.color ~= nil) then
|
||||
-- for the americans!
|
||||
result.attributes_to_keep.cycle_network_colour = attributes.color
|
||||
end
|
||||
end
|
||||
|
||||
-- Processes the relation. All tags which are added to result.attributes_to_keep will be copied to 'attributes' of each individual way
|
||||
function relation_tag_processor(attributes, result)
|
||||
result.attributes_to_keep = {}
|
||||
|
||||
|
||||
if (attributes.route == "bicycle") then
|
||||
-- This is a cycling network!
|
||||
cycling_network_tag_processor(attributes, result)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
-----------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
function unit_tests()
|
||||
|
||||
|
||||
|
||||
unit_test_relation_tag_processor({ route = "bicycle", operator = "Stad Genk", color = "red", type = "route" },
|
||||
{ StadGenk = "yes", cycle_network_colour = "red", cycle_network = "yes" });
|
||||
unit_test_relation_tag_processor({ route = "bicycle", cycle_network = "cycle_highway", color = "red", type = "route" },
|
||||
{ cycle_network_colour = "red", cycle_network = "yes", cycle_network_highway = "yes" });
|
||||
|
||||
|
||||
unit_test_cycle_networks({ highway = "residential", settings = {} }, 0)
|
||||
unit_test_cycle_networks({ highway = "residential", cycle_network = "yes", settings = {} }, 1)
|
||||
unit_test_cycle_networks({
|
||||
highway = "residential",
|
||||
cycle_network = "yes",
|
||||
settings = { cycle_network_operator = "Stad Genk" }
|
||||
}, 0 --[[Not the right network, not Genk]])
|
||||
|
||||
unit_test_weights(determine_weights_speed_first, { highway = "residential" }, 0.0625);
|
||||
unit_test_weights(determine_weights_speed_first, { highway = "cycleway" }, 0.0625); -- unit_test_weights({ highway = "residential", access = "destination", surface = "sett" });
|
||||
unit_test_weights(determine_weights_speed_first, { highway = "primary", bicycle="yes", surface = "sett" }, 0.068965517241379);
|
||||
|
||||
unit_test_weights(determine_weights_speed_first, { highway = "primary" , bicycle="yes"}, 0.0625);
|
||||
unit_test_weights(determine_weights_safety_first, { highway = "primary" , bicycle="yes"}, 0.76923076923077);
|
||||
|
||||
-- regression test
|
||||
unit_test_weights(determine_weights_safety_first, { ["cycleway:left"] = "track", ["cycleway:left:oneway"] = "no", highway="unclassified", oneway="yes", ["oneway:bicycle"]=no}, 0.45454545454545);
|
||||
unit_test_weights(determine_weights_speed_first, { ["cycleway:left"] = "track", ["cycleway:left:oneway"] = "no", highway="unclassified", oneway="yes", ["oneway:bicycle"]=no}, 0.0625);
|
||||
|
||||
|
||||
unit_test_weights(determine_weights_genk, { highway = "residential" }, 0.52631578947368);
|
||||
unit_test_weights(determine_weights_genk,
|
||||
{ highway = "residential", StadGenk = "yes", cycle_network = "yes" },
|
||||
0.20408163265306); -- factor = 1 / preference, should be lower
|
||||
unit_test_weights(determine_weights_genk,
|
||||
{ highway = "residential", cycle_network_operator = nil, cycle_network = "yes" },
|
||||
0.52631578947368);
|
||||
unit_test_weights(determine_weights_genk, {
|
||||
highway = "residential",
|
||||
cycle_network = "yes",
|
||||
cycle_network_operator = "Niet Stad Genk"
|
||||
}, 0.52631578947368);
|
||||
|
||||
unit_test_weights(determine_weights_networks_node_network, {
|
||||
highway = "residential",
|
||||
cycle_network = "yes",
|
||||
cycle_network_operator = "Niet Stad Genk"
|
||||
}, 0.52631578947368);
|
||||
|
||||
unit_test_weights(determine_weights_networks_node_network, {
|
||||
highway = "residential",
|
||||
cycle_network = "yes",
|
||||
cycle_network_node_network = "yes",
|
||||
}, 0.084033613445378);
|
||||
|
||||
unit_test_weights(determine_weights_anyways_network, { highway = "residential", cycle_network = "yes", Anyways = "yes" }, 0.090909090909091);
|
||||
unit_test_weights(determine_weights_anyways_network, { highway = "residential" }, 1.0);
|
||||
|
||||
|
||||
unit_test_weights(determine_weights_networks_node_network, { highway = "residential" }, 0.52631578947368);
|
||||
unit_test_weights(determine_weights_networks_node_network, { highway = "residential", cycle_network="yes", cycle_network_node_network="yes" }, 0.084033613445378);
|
||||
|
||||
end
|
||||
|
||||
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
access,oneway,speed,priority,highway,_relation:bicycle.network_by_operator
|
||||
no,both,0,0,,
|
||||
yes,both,15,1.9,residential,
|
||||
yes,both,15,6.9,residential,yes
|
||||
dismount,both,2.25,0.0195,footway,
|
|
|
@ -1,61 +0,0 @@
|
|||
{
|
||||
"name": "bicycle.comfort",
|
||||
"description": "Gives a comfort factor for a road, purely based on physical aspects of the road, which is a bit subjective; this takes a bit of scnery into account with a preference for `railway=abandoned` and `towpath=yes`",
|
||||
"unit": "[0, 2]",
|
||||
"$default": 1,
|
||||
"value": {
|
||||
"$multiply": {
|
||||
"highway": {
|
||||
"cycleway": 1.2,
|
||||
"primary": 0.3,
|
||||
"secondary": 0.4,
|
||||
"tertiary": 0.5,
|
||||
"unclassified": 0.8,
|
||||
"track": 0.95,
|
||||
"residential": 1.0,
|
||||
"living_street": 1.1,
|
||||
"footway": 0.95,
|
||||
"path": 0.5
|
||||
},
|
||||
"railway": {
|
||||
"abandoned": 2
|
||||
},
|
||||
"towpath": {
|
||||
"yes": 2
|
||||
},
|
||||
"cycleway": {
|
||||
"track": 1.2
|
||||
},
|
||||
"cyclestreet": {
|
||||
"yes": 1.1
|
||||
},
|
||||
"access": {
|
||||
"designated": 1.2,
|
||||
"dismount": 0.01
|
||||
},
|
||||
"surface": {
|
||||
"#": "The surface mapping heavily resembles the one in speed_factor, but it is not entirely the same",
|
||||
"paved": 0.99,
|
||||
"concrete:lanes": 0.8,
|
||||
"concrete:plates": 1.0,
|
||||
"sett": 0.9,
|
||||
"unhewn_cobblestone": 0.75,
|
||||
"cobblestone": 0.8,
|
||||
"unpaved": 0.75,
|
||||
"compacted": 1.1,
|
||||
"fine_gravel": 0.99,
|
||||
"gravel": 0.9,
|
||||
"dirt": 0.6,
|
||||
"earth": 0.6,
|
||||
"grass": 0.6,
|
||||
"grass_paver": 0.9,
|
||||
"ground": 0.7,
|
||||
"sand": 0.5,
|
||||
"woodchips": 0.5,
|
||||
"snow": 0.5,
|
||||
"pebblestone": 0.5,
|
||||
"mud": 0.4
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
expected,highway,foot,bicycle,cyclestreet,cycleway,cycleway:right,surface,railway,towpath
|
||||
1,,,,,,,,,
|
||||
1,residential,,,,,,,,
|
||||
1.1,residential,,,yes,,,,,
|
||||
1.2,cycleway,,,,,,,,
|
||||
1.2,cycleway,designated,,,,,,,
|
||||
0.5,path,designated,designated,,,,,,
|
||||
0.5,path,,designated,,,,,,
|
||||
0.95,footway,designated,,,,,,,
|
||||
0.3,primary,,,,no,,,,
|
||||
0.3,primary,,,,yes,,,,
|
||||
0.36,primary,,,,track,,,,
|
||||
0.4,secondary,,,,lane,,,,
|
||||
0.4,secondary,,,,,lane,asphalt,,
|
||||
1.1,residential,,,yes,,,asphalt,,
|
||||
2,,,,,,,,abandoned,
|
||||
2,,,,,,,,,yes
|
||||
4,,,,,,,,abandoned,yes
|
|
|
@ -1,6 +0,0 @@
|
|||
access,oneway,speed,priority,highway,_relation:bicycle.network_by_operator,_relation:bicycle.network_is_cyclehighway
|
||||
no,,,,,,
|
||||
yes,both,15,1.9,residential,,
|
||||
yes,both,15,6.9,residential,yes,
|
||||
yes,both,15,6.9,residential,,yes
|
||||
yes,both,15,11.9,residential,yes,yes
|
|
|
@ -1,4 +0,0 @@
|
|||
access,oneway,speed,priority,highway,_relation:bicycle.network_score
|
||||
designated,both,15,22,cycleway,yes
|
||||
no,,,,,
|
||||
designated,both,15,2,cycleway,
|
|
|
@ -1,7 +0,0 @@
|
|||
access,oneway,speed,priority,highway,bicycle
|
||||
no,both,0,0,,
|
||||
designated,both,15,15,cycleway,
|
||||
no,both,0,0,primary,
|
||||
dismount,both,2.25,2.25,pedestrian,
|
||||
yes,both,15,15,pedestrian,yes
|
||||
yes,both,15,15,residential,
|
|
|
@ -1,107 +0,0 @@
|
|||
{
|
||||
"name": "bicycle",
|
||||
"description": "A simple profile which routes a normal bicycle",
|
||||
"vehicletypes": [
|
||||
"vehicle",
|
||||
"bicycle"
|
||||
],
|
||||
"metadata": [
|
||||
"name",
|
||||
"bridge",
|
||||
"tunnel",
|
||||
"colour",
|
||||
"cycle_network_colour",
|
||||
"ref",
|
||||
"status",
|
||||
"network"
|
||||
],
|
||||
"defaults": {
|
||||
"#defaultSpeed": 15,
|
||||
"#maxspeed": 30,
|
||||
"#timeNeeded": 0,
|
||||
"#distance": 0,
|
||||
"#comfort": 0,
|
||||
"#safety": 0,
|
||||
"#operatorNetworkScore": 0,
|
||||
"#networkOperator": [],
|
||||
"#cycleHighwayNetworkScore": 0
|
||||
},
|
||||
"behaviours": {
|
||||
|
||||
"fastest": {
|
||||
"description": "The fastest route to your destination",
|
||||
"#timeNeeded": 1
|
||||
},
|
||||
"shortest": {
|
||||
"description": "The shortest route, independent of of speed",
|
||||
"#distance": 1
|
||||
},
|
||||
|
||||
|
||||
"safety": {
|
||||
"description": "A defensive route shying away from big roads with lots of cars",
|
||||
"#safety": 1
|
||||
},
|
||||
"comfort": {
|
||||
"description": "A comfortable route preferring well-paved roads, smaller roads and a bit of scenery at the cost of speed",
|
||||
"#comfort": 1
|
||||
},
|
||||
"comfort_safety": {
|
||||
"description": "A route which aims to be both safe and comfortable at the cost of speed",
|
||||
"#comfort": 1,
|
||||
"#safety": 1
|
||||
},
|
||||
|
||||
|
||||
"brussels": {
|
||||
"description": "A route preferring the cycling network by operator 'Brussels Mobility'",
|
||||
"#operatorNetworkScore": 5,
|
||||
"#networkOperator": ["Brussels Mobility"],
|
||||
"#comfort": 1,
|
||||
"#safety": 1
|
||||
},
|
||||
"genk": {
|
||||
"description": "A route preferring the cycling network by operator 'Stad Genk'",
|
||||
"#operatorNetworkScore": 3,
|
||||
"#networkOperator": ["Stad Genk"],
|
||||
"#comfort": 0,
|
||||
"#safety": 1
|
||||
},
|
||||
"cycle_highway": {
|
||||
"description": "A route preferring the 'cycle-highways' ",
|
||||
"#cycleHighwayNetworkScore": 5,
|
||||
"#comfort": 1,
|
||||
"#safety": 1
|
||||
},
|
||||
"commute": {
|
||||
"description": "A route preferring the 'cycle-highways' or the cycling network by operator 'Brussels Mobility'",
|
||||
"#operatorNetworkScore": 5,
|
||||
"#networkOperator": ["Brussels Mobility"],
|
||||
"#cycleHighwayNetworkScore": 5,
|
||||
"#comfort": 1,
|
||||
"#safety": 1
|
||||
}
|
||||
},
|
||||
"access": "$bicycle.legal_access",
|
||||
"oneway": "$bicycle.oneway",
|
||||
"speed": {
|
||||
"$min": [
|
||||
"$legal_maxspeed_be",
|
||||
"#maxspeed",
|
||||
{
|
||||
"$multiply": [
|
||||
"#defaultSpeed",
|
||||
"$bicycle.speed_factor"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"priority": {
|
||||
"#comfort": "$bicycle.comfort",
|
||||
"#safety": "$bicycle.safety",
|
||||
"#operatorNetworkScore": "$bicycle.network_by_operator",
|
||||
"#cycleHighwayNetworkScore": "$bicycle.network_is_cyclehighway",
|
||||
"#timeNeeded": "$speed",
|
||||
"#distance": "$distance"
|
||||
}
|
||||
}
|
|
@ -1,80 +0,0 @@
|
|||
{
|
||||
"name": "bicycle.legal_access",
|
||||
"description": "Gives, for each type of highway, whether or not a normal bicycle can enter legally.\nNote that legal access is a bit 'grey' in the case of roads marked private and permissive, in which case these values are returned ",
|
||||
"unit": "'designated': Access is allowed and even specifically for bicycles\n'yes': bicycles are allowed here\n'permissive': bicycles are allowed here, but this might be a private road or service where usage is allowed, but uncommon\n'dismount': cycling here is not allowed, but walking with the bicycle is\n'destination': cycling is allowed here, but only if truly necessary to reach the destination\n'private': this is a private road, only go here if the destination is here\n'no': do not cycle here",
|
||||
"$default": "no",
|
||||
"value": {
|
||||
"$firstMatchOf": [
|
||||
"anyways:bicycle",
|
||||
"anyways:access",
|
||||
"anyways:construction",
|
||||
"bicycle",
|
||||
"access",
|
||||
"cycleway:right",
|
||||
"cycleway:left",
|
||||
"cycleway",
|
||||
"highway"
|
||||
],
|
||||
"value": {
|
||||
"access": {
|
||||
"no": "no",
|
||||
"customers": "no",
|
||||
"private": "no",
|
||||
"#": "Note that we leave out 'yes', as it is meaningless - the default assumption is that everything on OSM is accessible! This forces to fall through to the road type, in order to force `highway=motorway; access=yes` as not to trigger access for bicycles",
|
||||
"permissive": "permissive",
|
||||
"destination": "destination",
|
||||
"delivery": "destination",
|
||||
"service": "destination"
|
||||
},
|
||||
"highway": {
|
||||
"cycleway": "designated",
|
||||
"residential": "yes",
|
||||
"living_street": "yes",
|
||||
"service": "permissive",
|
||||
"services": "permissive",
|
||||
"track": "yes",
|
||||
"crossing": "dismount",
|
||||
"footway": "dismount",
|
||||
"pedestrian": "dismount",
|
||||
"corridor": "dismount",
|
||||
"path": "permissive",
|
||||
"primary": "no",
|
||||
"primary_link": "no",
|
||||
"secondary": "yes",
|
||||
"secondary_link": "yes",
|
||||
"tertiary": "yes",
|
||||
"tertiary_link": "yes",
|
||||
"unclassified": "yes",
|
||||
"road": "yes"
|
||||
},
|
||||
"bicycle": {
|
||||
"yes": "yes",
|
||||
"no": "no",
|
||||
"use_sidepath": "no",
|
||||
"designated": "designated",
|
||||
"permissive": "permissive",
|
||||
"private": "private",
|
||||
"official": "designated",
|
||||
"dismount": "dismount"
|
||||
},
|
||||
"cycleway:right": {
|
||||
"$not": "no"
|
||||
},
|
||||
"cycleway:left": {
|
||||
"$not": "no"
|
||||
},
|
||||
"cycleway": {
|
||||
"$not": "no"
|
||||
},
|
||||
"anyways:bicycle": "$id",
|
||||
"anyways:access": {
|
||||
"no": "no",
|
||||
"destination": "destination",
|
||||
"yes": "yes"
|
||||
},
|
||||
"anyways:construction": {
|
||||
"yes": "no"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
expected,highway,bicycle,access,anyways:access,cycleway:right,cycleway:right
|
||||
no,,,,,,
|
||||
no,,,no,,,
|
||||
yes,,yes,no,,,
|
||||
permissive,path,,,,,
|
||||
yes,pedestrian,yes,,,,
|
||||
dismount,pedestrian,,,,,
|
||||
designated,cycleway,,,,,
|
||||
destination,residential,,destination,,,
|
||||
no,residential,,private,,,
|
||||
designated,residential,designated,,,,
|
||||
designated,motorway,designated,,,,
|
||||
no,residential,use_sidepath,,,,
|
||||
yes,residential,,no,yes,,
|
||||
no,primary,,,,,
|
||||
yes,primary,,,,yes,
|
||||
yes,primary,,,,,yes
|
||||
yes,secondary,,,,track,
|
||||
destination,service,,destination,,,
|
||||
no,residential,use_sidepath,,,,,
|
Can't render this file because it has a wrong number of fields in line 20.
|
|
@ -1,13 +0,0 @@
|
|||
{
|
||||
"name": "bicycle.network_by_operator",
|
||||
"description": "The 'bicycle.network_score' returns true if the way is part of a cycling network of a certain (group of) operators",
|
||||
"$memberOf": {
|
||||
"$mustMatch": {
|
||||
"type": "route",
|
||||
"route": "bicycle",
|
||||
"state": {"$not": "proposed"},
|
||||
"operator": {"$containedIn": "#networkOperator"}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
{
|
||||
"name": "bicycle.network_is_cyclehighway",
|
||||
"description": "Returns true if the highway is part of a [cycle highway](https://wiki.openstreetmap.org/wiki/Tag:cycle_network%3Dcycle_highway)",
|
||||
"$memberOf": {
|
||||
"$mustMatch": {
|
||||
"type": "route",
|
||||
"route": "bicycle",
|
||||
"state": {"$not": "proposed"},
|
||||
"cycle_network": "cycle_highway"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
{
|
||||
"name": "bicycle.network_score",
|
||||
"description": "The 'bicycle.network_score' returns true if the way is part of a cycling network",
|
||||
"$memberOf": {
|
||||
"$mustMatch": {
|
||||
"type": "route",
|
||||
"route": "bicycle",
|
||||
"state": {"$notEq": "proposed"}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
{
|
||||
"name": "bicycle.oneway",
|
||||
"description": "Determines wether or not a bicycle can go in both ways in this street, and if it is oneway, in what direction",
|
||||
"unit": "both: direction is allowed in both direction\nwith: this is a oneway street with direction allowed with the grain of the way\nagainst: oneway street with direction against the way",
|
||||
"$default": "both",
|
||||
"value": {
|
||||
"$firstMatchOf": [
|
||||
"oneway:bicycle",
|
||||
"junction",
|
||||
"cycleway",
|
||||
"cycleway:left",
|
||||
"oneway"
|
||||
],
|
||||
"value": {
|
||||
"oneway": {
|
||||
"yes": "with",
|
||||
"no": "both",
|
||||
"1": "with",
|
||||
"-1": "against"
|
||||
},
|
||||
"oneway:bicycle": {
|
||||
"yes": "with",
|
||||
"no": "both",
|
||||
"1": "with",
|
||||
"-1": "against"
|
||||
},
|
||||
"junction": {
|
||||
"roundabout": "with"
|
||||
},
|
||||
"cycleway": {
|
||||
"right": "against",
|
||||
"#": "We ignore 'no' as it has no meaning and is the default assumption",
|
||||
"opposite_lane": "both",
|
||||
"track": "both",
|
||||
"lane": "both",
|
||||
"opposite": "both",
|
||||
"opposite_share_busway": "both",
|
||||
"opposite_track": "both"
|
||||
},
|
||||
"cycleway:left": {
|
||||
"no": "with",
|
||||
"yes": "both",
|
||||
"lane": "both",
|
||||
"track": "both",
|
||||
"shared_lane": "both",
|
||||
"share_busway": "both"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
expected,highway,oneway,oneway:bicycle,junction,cycleway,cycleway:right,cycleway:left
|
||||
both,,,,,,,
|
||||
both,,no,,,,,
|
||||
with,,no,yes,,,,
|
||||
with,,,,roundabout,,,
|
||||
both,,yes,,,opposite,,
|
||||
against,,yes,-1,,,,
|
||||
with,residential,yes,,,,,
|
||||
both,residential,no,,,,,
|
||||
both,residential,yes,no,,,,
|
||||
with,residential,,,roundabout,,,
|
||||
both,residential,,no,roundabout,,,
|
||||
against,residential,,-1,,,,
|
||||
both,residential,invalidKey,no,,,,
|
||||
with,secondary,yes,,,,track,
|
||||
both,secondary,yes,,,,,track
|
||||
both,secondary,yes,,,track,,
|
||||
with,,yes,,,,,no
|
||||
both,residential,yes,,,,,lane
|
|
|
@ -1,72 +0,0 @@
|
|||
{
|
||||
"name": "bicycle.safety",
|
||||
"description": "Determines how safe a cyclist feels on a certain road, mostly based on car pressure. This is a relatively ",
|
||||
"unit": "safety",
|
||||
"$default": 1,
|
||||
"value": {
|
||||
"$multiply": {
|
||||
"access": {
|
||||
"#": "access=no implies no access for cars too!",
|
||||
"no": 1.1,
|
||||
"dismount": 0.01
|
||||
},
|
||||
"motor_vehicle": {
|
||||
"no": 1.5
|
||||
},
|
||||
"foot": {
|
||||
"designated": 0.95
|
||||
},
|
||||
"bicycle": {
|
||||
"designated": 1.5
|
||||
},
|
||||
"cyclestreet": {
|
||||
"yes": 1.5
|
||||
},
|
||||
"towpath": {
|
||||
"yes": 1.1
|
||||
},
|
||||
"designation": {
|
||||
"towpath": 1.5
|
||||
},
|
||||
"highway": {
|
||||
"cycleway": 1.0,
|
||||
"primary": 0.3,
|
||||
"secondary": 0.4,
|
||||
"tertiary": 0.5,
|
||||
"unclassified": 0.8,
|
||||
"track": 0.95,
|
||||
"residential": 0.9,
|
||||
"living_street": 1,
|
||||
"footway": 1,
|
||||
"path": 1
|
||||
},
|
||||
"cycleway": {
|
||||
"yes": 0.95,
|
||||
"no": 0.5,
|
||||
"lane": 1,
|
||||
"shared": 0.8,
|
||||
"shared_lane": 0.8,
|
||||
"share_busway": 0.9,
|
||||
"track": 1.5
|
||||
},
|
||||
"cycleway:left": {
|
||||
"yes": 0.95,
|
||||
"no": 0.5,
|
||||
"lane": 1,
|
||||
"shared": 0.8,
|
||||
"shared_lane": 0.8,
|
||||
"share_busway": 0.9,
|
||||
"track": 1.5
|
||||
},
|
||||
"cycleway:right": {
|
||||
"yes": 0.95,
|
||||
"no": 0.5,
|
||||
"lane": 1,
|
||||
"shared": 0.8,
|
||||
"shared_lane": 0.8,
|
||||
"share_busway": 0.9,
|
||||
"track": 1.5
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
expected,highway,cycleway,cyclestreet,foot,bicycle,cycleway:right
|
||||
0.15,primary,no,,,,
|
||||
0.285,primary,yes,,,,
|
||||
0.45,primary,track,,,,
|
||||
0.4,secondary,lane,,,,
|
||||
0.9,residential,,,,,
|
||||
1,cycleway,,,,,
|
||||
1.35,residential,,yes,,,
|
||||
0.95,cycleway,,,designated,,
|
||||
0.95,footway,,,designated,,
|
||||
1.425,path,,,designated,designated,
|
||||
1.5,path,,,,designated,
|
||||
0.4,secondary,,,,,lane
|
|
|
@ -1,62 +0,0 @@
|
|||
{
|
||||
"name": "bicycle.speed_factor",
|
||||
"description": "Calculates a speed factor for bicycles based on physical features, e.g. a sand surface will slow a cyclist down; going over pedestrian areas even more, ...",
|
||||
"$multiply": {
|
||||
"access": {
|
||||
"#": "We have to go by foot. Default speed of 20km/h * 0.15 = 3km/h",
|
||||
"dismount": 0.15
|
||||
},
|
||||
"highway": {
|
||||
"#": "A small forest path is typically slow",
|
||||
"path": 0.5,
|
||||
"#": "an unmaintained track (in Belgium: tractor path) is slower as well",
|
||||
"track": 0.7
|
||||
},
|
||||
"surface": {
|
||||
"paved": 0.99,
|
||||
"asphalt": 1,
|
||||
"concrete": 1,
|
||||
"metal": 1,
|
||||
"wood": 1,
|
||||
"concrete:lanes": 0.95,
|
||||
"concrete:plates": 1,
|
||||
"paving_stones": 1,
|
||||
"sett": 0.9,
|
||||
"unhewn_cobblestone": 0.75,
|
||||
"cobblestone": 0.8,
|
||||
"unpaved": 0.75,
|
||||
"compacted": 0.99,
|
||||
"fine_gravel": 0.99,
|
||||
"gravel": 0.9,
|
||||
"dirt": 0.6,
|
||||
"earth": 0.6,
|
||||
"grass": 0.6,
|
||||
"grass_paver": 0.9,
|
||||
"ground": 0.7,
|
||||
"sand": 0.5,
|
||||
"woodchips": 0.5,
|
||||
"snow": 0.5,
|
||||
"pebblestone": 0.5,
|
||||
"mud": 0.4
|
||||
},
|
||||
"tracktype": {
|
||||
"grade1": 0.99,
|
||||
"grade2": 0.8,
|
||||
"grade3": 0.6,
|
||||
"grade4": 0.3,
|
||||
"grade5": 0.1
|
||||
},
|
||||
"incline": {
|
||||
"up": 0.75,
|
||||
"down": 1.25,
|
||||
"0": 1,
|
||||
"0%": 1,
|
||||
"10%": 0.9,
|
||||
"-10%": 1.1,
|
||||
"20%": 0.8,
|
||||
"-20%": 1.2,
|
||||
"30%": 0.7,
|
||||
"-30%": 1.3
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
expected,incline,surface,highway,access
|
||||
1,,,,
|
||||
1,,,residential,
|
||||
0.75,up,,residential,
|
||||
1.25,down,,residential,
|
||||
0.3,up,mud,residential,
|
||||
1.125,down,sett,residential,
|
||||
0.675,up,sett,residential,
|
||||
0.9,,sett,residential,
|
||||
1,,asphalt,residential,
|
||||
0.15,,,residential,dismount
|
||||
0.0315,up,mud,track,dismount
|
|
|
@ -1,37 +0,0 @@
|
|||
{
|
||||
"name": "small",
|
||||
"description": "A minimal example, to start designing a profile or to debug",
|
||||
"vehicletypes": [
|
||||
"vehicle",
|
||||
"bicycle"
|
||||
],
|
||||
"metadata": [
|
||||
"name"
|
||||
],
|
||||
"defaults": {
|
||||
"#defaultSpeed": 15,
|
||||
"#distance": 0,
|
||||
"#cycleHighwayNetworkScore": 0
|
||||
},
|
||||
"behaviours": {
|
||||
"shortest": {
|
||||
"description": "The shortest route, independent of of speed",
|
||||
"#distance": 1
|
||||
},
|
||||
"fastest": {
|
||||
"description": "The shortest route, independent of of speed",
|
||||
"#distance": 1
|
||||
},
|
||||
"commute": {
|
||||
"description": "The shortest route, independent of of speed",
|
||||
"#cycleHighwayNetworkScore": 3
|
||||
}
|
||||
},
|
||||
"access": "$bicycle.legal_access",
|
||||
"oneway": "$bicycle.oneway",
|
||||
"speed": "#defaultSpeed",
|
||||
"priority": {
|
||||
"#distance": "$distance",
|
||||
"#cycleHighwayNetworkScore": "$bicycle.network_is_cyclehighway"
|
||||
}
|
||||
}
|
|
@ -1,44 +0,0 @@
|
|||
{
|
||||
"name": "legal_maxspeed_be",
|
||||
"description": "Gives, for each type of highway, which the default legal maxspeed is in Belgium. This file is intended to be reused for in all vehicles, from pedestrian to car. In some cases, a legal maxspeed is not really defined (e.g. on footways). In that case, a socially acceptable speed should be taken (e.g.: a bicycle on a pedestrian path will go say around 12km/h)",
|
||||
"unit": "km/h",
|
||||
"$default": 30,
|
||||
"value": {
|
||||
"$firstMatchOf": [
|
||||
"maxspeed",
|
||||
"designation",
|
||||
"highway",
|
||||
"ferry"
|
||||
],
|
||||
"value": {
|
||||
"maxspeed": "$parse",
|
||||
"highway": {
|
||||
"cycleway": 30,
|
||||
"footway": 20,
|
||||
"crossing": 20,
|
||||
"pedestrian": 15,
|
||||
"path": 15,
|
||||
"corridor": 5,
|
||||
"residential": 30,
|
||||
"living_street": 20,
|
||||
"service": 30,
|
||||
"services": 30,
|
||||
"track": 50,
|
||||
"unclassified": 50,
|
||||
"road": 50,
|
||||
"motorway": 120,
|
||||
"motorway_link": 120,
|
||||
"primary": 90,
|
||||
"primary_link": 90,
|
||||
"secondary": 50,
|
||||
"secondary_link": 50,
|
||||
"tertiary": 50,
|
||||
"tertiary_link": 50
|
||||
},
|
||||
"designation": {
|
||||
"towpath": 30
|
||||
},
|
||||
"ferry": 5
|
||||
}
|
||||
}
|
||||
}
|
|
@ -13,21 +13,22 @@ namespace AspectedRouting
|
|||
{
|
||||
static class Program
|
||||
{
|
||||
public static List<(AspectMetadata aspect, FunctionTestSuite tests)> ParseAspects(
|
||||
this List<string> jsonFileNames, Context context)
|
||||
public static IEnumerable<(AspectMetadata aspect, AspectTestSuite tests)> ParseAspects(
|
||||
this IEnumerable<string> jsonFileNames, Context context)
|
||||
{
|
||||
var aspects = new List<(AspectMetadata aspect, FunctionTestSuite tests)>();
|
||||
var aspects = new List<(AspectMetadata aspect, AspectTestSuite tests)>();
|
||||
foreach (var file in jsonFileNames)
|
||||
{
|
||||
var fi = new FileInfo(file);
|
||||
Console.WriteLine("Parsing " + file);
|
||||
var aspect = JsonParser.AspectFromJson(context, File.ReadAllText(file), fi.Name);
|
||||
if (aspect != null)
|
||||
{
|
||||
var testPath = fi.DirectoryName + "/" + aspect.Name + ".test.csv";
|
||||
FunctionTestSuite tests = null;
|
||||
AspectTestSuite tests = null;
|
||||
if (File.Exists(testPath))
|
||||
{
|
||||
tests = FunctionTestSuite.FromString(aspect, File.ReadAllText(testPath));
|
||||
tests = AspectTestSuite.FromString(aspect, File.ReadAllText(testPath));
|
||||
}
|
||||
|
||||
aspects.Add((aspect, tests));
|
||||
|
@ -38,7 +39,7 @@ namespace AspectedRouting
|
|||
}
|
||||
|
||||
private static LuaPrinter GenerateLua(Context context,
|
||||
List<(AspectMetadata aspect, FunctionTestSuite tests)> aspects,
|
||||
IEnumerable<(AspectMetadata aspect, AspectTestSuite tests)> aspects,
|
||||
ProfileMetaData profile, List<ProfileTestSuite> profileTests)
|
||||
{
|
||||
var luaPrinter = new LuaPrinter(context);
|
||||
|
@ -69,7 +70,7 @@ namespace AspectedRouting
|
|||
return luaPrinter;
|
||||
}
|
||||
|
||||
private static List<(ProfileMetaData profile, List<ProfileTestSuite> profileTests)> ParseProfiles(
|
||||
private static IEnumerable<(ProfileMetaData profile, List<ProfileTestSuite> profileTests)> ParseProfiles(
|
||||
IEnumerable<string> jsonFiles, Context context)
|
||||
{
|
||||
var result = new List<(ProfileMetaData profile, List<ProfileTestSuite> profileTests)>();
|
||||
|
@ -90,7 +91,7 @@ namespace AspectedRouting
|
|||
var profileTests = new List<ProfileTestSuite>();
|
||||
foreach (var behaviourName in profile.Behaviours.Keys)
|
||||
{
|
||||
var path = profileFi.DirectoryName + "/" + profile.Name + "." + behaviourName + ".csv";
|
||||
var path = profileFi.DirectoryName + "/" + profile.Name + "." + behaviourName + ".behaviour_test.csv";
|
||||
if (File.Exists(path))
|
||||
{
|
||||
var test = ProfileTestSuite.FromString(context, profile, behaviourName,
|
||||
|
@ -107,7 +108,8 @@ namespace AspectedRouting
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
PrintError(jsonFile, e);
|
||||
// PrintError(jsonFile, e);
|
||||
throw new Exception("In the file " + jsonFile, e);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -116,18 +118,35 @@ namespace AspectedRouting
|
|||
|
||||
private static void PrintError(string file, Exception exception)
|
||||
{
|
||||
Console.WriteLine($"Error in the file {file}:\n {exception.Message}");
|
||||
var msg = exception.Message;
|
||||
while (exception.InnerException != null)
|
||||
{
|
||||
exception = exception.InnerException;
|
||||
msg += "\n " + exception.Message;
|
||||
}
|
||||
|
||||
Console.WriteLine($"Error in the file {file}:\n {msg}");
|
||||
}
|
||||
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
MdPrinter.GenerateHelpText("IO/md/helpText.md");
|
||||
|
||||
if (args.Length < 2)
|
||||
{
|
||||
Console.WriteLine("Usage: <directory where all aspects and profiles can be found> <outputdirectory>");
|
||||
return;
|
||||
}
|
||||
|
||||
var inputDir = args[0];
|
||||
var outputDir = args[1];
|
||||
|
||||
|
||||
MdPrinter.GenerateHelpText(outputDir + "helpText.md");
|
||||
|
||||
|
||||
var files = Directory.EnumerateFiles("Profiles", "*.json", SearchOption.AllDirectories)
|
||||
var files = Directory.EnumerateFiles(inputDir, "*.json", SearchOption.AllDirectories)
|
||||
.ToList();
|
||||
|
||||
|
||||
|
||||
var context = new Context();
|
||||
|
||||
var aspects = ParseAspects(files, context);
|
||||
|
@ -161,7 +180,7 @@ namespace AspectedRouting
|
|||
}
|
||||
|
||||
var luaPrinter = GenerateLua(context, aspects, profile, profileTests);
|
||||
File.WriteAllText(profile.Name + ".lua", luaPrinter.ToLua());
|
||||
File.WriteAllText(outputDir + "/" + profile.Name + ".lua", luaPrinter.ToLua());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,12 +7,12 @@ using AspectedRouting.Language.Functions;
|
|||
|
||||
namespace AspectedRouting.Tests
|
||||
{
|
||||
public class FunctionTestSuite
|
||||
public class AspectTestSuite
|
||||
{
|
||||
public readonly AspectMetadata FunctionToApply;
|
||||
public readonly IEnumerable<(string expected, Dictionary<string, string> tags)> Tests;
|
||||
|
||||
public static FunctionTestSuite FromString(AspectMetadata function, string csvContents)
|
||||
public static AspectTestSuite FromString(AspectMetadata function, string csvContents)
|
||||
{
|
||||
var all = csvContents.Split("\n").ToList();
|
||||
var keys = all[0].Split(",").ToList();
|
||||
|
@ -42,10 +42,10 @@ namespace AspectedRouting.Tests
|
|||
tests.Add((expected, tags));
|
||||
}
|
||||
|
||||
return new FunctionTestSuite(function, tests);
|
||||
return new AspectTestSuite(function, tests);
|
||||
}
|
||||
|
||||
public FunctionTestSuite(
|
||||
public AspectTestSuite(
|
||||
AspectMetadata functionToApply,
|
||||
IEnumerable<(string expected, Dictionary<string, string> tags)> tests)
|
||||
{
|
||||
|
@ -84,7 +84,7 @@ namespace AspectedRouting.Tests
|
|||
{
|
||||
failed = true;
|
||||
Console.WriteLine(
|
||||
$"[{FunctionToApply.Name}] Testcase {testCase} failed:\n Expected: {test.expected}\n actual: {actual}\n tags: {test.tags.Pretty()}");
|
||||
$"[{FunctionToApply.Name}] Line {testCase+1} failed:\n Expected: {test.expected}\n actual: {actual}\n tags: {test.tags.Pretty()}\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -148,11 +148,11 @@ namespace AspectedRouting.Tests
|
|||
{
|
||||
c = new Context(c);
|
||||
tags = new Dictionary<string, string>(tags);
|
||||
|
||||
|
||||
void Err(string message, object exp, object act)
|
||||
{
|
||||
Console.WriteLine(
|
||||
$"[{Profile.Name}.{BehaviourName}]: Test {i} failed: {message}; expected {exp} but got {act}\n {{{tags.Pretty()}}}");
|
||||
$"[{Profile.Name}.{BehaviourName}]: Test on line {i + 1} failed: {message}; expected {exp} but got {act}\n {{{tags.Pretty()}}}");
|
||||
}
|
||||
|
||||
var success = true;
|
||||
|
@ -183,12 +183,13 @@ namespace AspectedRouting.Tests
|
|||
var speed = (double) Profile.Speed.Run(c, tags);
|
||||
tags["speed"] = "" + speed;
|
||||
|
||||
c.AddFunction("speed", new AspectMetadata(new Constant(Typs.Double, speed),
|
||||
"speed", "Actual speed of this function", "NA","NA","NA", true));
|
||||
c.AddFunction("oneway", new AspectMetadata(new Constant(Typs.String, oneway),
|
||||
"oneway", "Actual direction of this function", "NA","NA","NA", true));
|
||||
c.AddFunction("access", new AspectMetadata(new Constant(Typs.String, canAccess),
|
||||
"access", "Actual access of this function", "NA","NA","NA", true));
|
||||
|
||||
c.AddFunction("speed", new AspectMetadata(new Constant(Typs.Double, speed),
|
||||
"speed", "Actual speed of this function", "NA", "NA", "NA", true));
|
||||
c.AddFunction("oneway", new AspectMetadata(new Constant(Typs.String, oneway),
|
||||
"oneway", "Actual direction of this function", "NA", "NA", "NA", true));
|
||||
c.AddFunction("access", new AspectMetadata(new Constant(Typs.String, canAccess),
|
||||
"access", "Actual access of this function", "NA", "NA", "NA", true));
|
||||
|
||||
if (Math.Abs(speed - expected.Speed) > 0.0001)
|
||||
{
|
||||
|
@ -198,6 +199,7 @@ namespace AspectedRouting.Tests
|
|||
|
||||
|
||||
var priority = 0.0;
|
||||
var weightExplanation = new List<string>();
|
||||
foreach (var (paramName, expression) in Profile.Priority)
|
||||
{
|
||||
var aspectInfluence = (double) c.Parameters[paramName].Evaluate(c);
|
||||
|
@ -210,7 +212,7 @@ namespace AspectedRouting.Tests
|
|||
|
||||
var aspectWeightObj = new Apply(
|
||||
Funcs.EitherFunc.Apply(Funcs.Id, Funcs.Const, expression)
|
||||
, new Constant((tags))).Evaluate(c);
|
||||
, new Constant(tags)).Evaluate(c);
|
||||
|
||||
double aspectWeight;
|
||||
switch (aspectWeightObj)
|
||||
|
@ -241,15 +243,22 @@ namespace AspectedRouting.Tests
|
|||
throw new Exception($"Invalid value as result for {paramName}: got object {aspectWeightObj}");
|
||||
}
|
||||
|
||||
weightExplanation.Add($"({paramName} = {aspectInfluence}) * {aspectWeight}");
|
||||
priority += aspectInfluence * aspectWeight;
|
||||
}
|
||||
|
||||
if (Math.Abs(priority - expected.Weight) > 0.0001)
|
||||
{
|
||||
Err("weight incorrect", expected.Weight, priority);
|
||||
Err($"weight incorrect. Calculation is {string.Join(" + ", weightExplanation)}", expected.Weight,
|
||||
priority);
|
||||
success = false;
|
||||
}
|
||||
|
||||
if (!success)
|
||||
{
|
||||
Console.WriteLine();
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
|
@ -292,7 +301,7 @@ namespace AspectedRouting.Tests
|
|||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine($"[{BehaviourName}] {Tests.Count()} tests successful");
|
||||
Console.WriteLine($"[{Profile.Name}] {Tests.Count()} tests successful for behaviour {BehaviourName}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue