V0.1
This commit is contained in:
parent
3fd19dce5c
commit
287dde6cee
20 changed files with 402 additions and 1349 deletions
|
@ -58,15 +58,19 @@ namespace AspectedRouting.IO.itinero1
|
|||
|
||||
code.Add("\n\n----------------------------- PROFILE ---------------------------");
|
||||
var keys = _neededKeys.Select(key => "\"" + key + "\"");
|
||||
code.Add("\n\nprofile_whitelist = {" + string.Join(", ", keys) + "}");
|
||||
code.Add("\n\nprofile_whitelist = {\n " + string.Join("\n , ", keys) + "}");
|
||||
|
||||
code.AddRange(_code);
|
||||
|
||||
code.Add("\n\n ------------------------------- TESTS -------------------------");
|
||||
|
||||
code.AddRange(_tests);
|
||||
code.Add("function test_all()");
|
||||
code.Add(string.Join("\n",_tests).Indent());
|
||||
code.Add("end");
|
||||
|
||||
var compatibility = string.Join("\n",
|
||||
|
||||
|
||||
"",
|
||||
"if (itinero == nil) then",
|
||||
" itinero = {}",
|
||||
|
@ -80,6 +84,7 @@ namespace AspectedRouting.IO.itinero1
|
|||
" print = itinero.log",
|
||||
"end",
|
||||
"",
|
||||
"test_all()",
|
||||
"if (not failed_tests and not failed_profile_tests) then",
|
||||
" print(\"Tests OK\")",
|
||||
"end"
|
||||
|
|
|
@ -20,7 +20,8 @@ namespace AspectedRouting.IO.itinero1
|
|||
_alreadyAddedFunctions.Add(meta.Name);
|
||||
|
||||
var possibleTags = meta.PossibleTags() ?? new Dictionary<string, List<string>>();
|
||||
var numberOfCombinations = possibleTags.Values.Select(lst => 1 + lst.Count).Multiply();
|
||||
int numberOfCombinations;
|
||||
numberOfCombinations = possibleTags.Values.Select(lst => 1 + lst.Count).Multiply();
|
||||
|
||||
var usedParams = meta.UsedParameters();
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ namespace AspectedRouting.IO.itinero1
|
|||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
|
||||
private void CreateMembershipPreprocessor(ProfileMetaData profile)
|
||||
{
|
||||
var memberships = Analysis.MembershipMappingsFor(profile, _context);
|
||||
|
@ -47,34 +47,69 @@ namespace AspectedRouting.IO.itinero1
|
|||
" result.attributes_to_keep = {}"
|
||||
};
|
||||
|
||||
foreach (var (calledInFunction, _) in memberships)
|
||||
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
|
||||
var preProcName = "relation_preprocessing_for_" + calledInFunction.FunctionName();
|
||||
func.Add("");
|
||||
func.Add("");
|
||||
func.Add(" subresult.attributes_to_keep = {}");
|
||||
func.Add(" parameters = default_parameters()");
|
||||
func.Add($" matched = {preProcName}(parameters, relation_tags, subresult)");
|
||||
func.Add(" if (matched) then");
|
||||
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");
|
||||
func.Add($" result.attributes_to_keep[\"{tagKey}\"] = \"yes\"");
|
||||
func.Add(" end");
|
||||
|
||||
|
||||
if (usedParameters.Count() == 0)
|
||||
{
|
||||
// Every behaviour uses the default parameters for this one
|
||||
func.Add(" -- No parameter dependence for aspect " + calledInFunction);
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (var (behaviourName, parameters) in profile.Behaviours)
|
||||
{
|
||||
var preProcName = "relation_preprocessing_for_" + calledInFunction.FunctionName();
|
||||
if (usedParameters.Except(parameters.Keys.ToHashSet()).Any())
|
||||
{
|
||||
// The parameters where the membership depends on, are not used here
|
||||
// This is thus the same as the default. We don't have to calculate it
|
||||
continue;
|
||||
}
|
||||
|
||||
func.Add("");
|
||||
func.Add("");
|
||||
func.Add(" subresult.attributes_to_keep = {}");
|
||||
func.Add(" parameters = default_parameters()");
|
||||
func.Add(ParametersToLua(parameters));
|
||||
func.Add(ParametersToLua(parameters.Where(kv => usedParameters.Contains(kv.Key))
|
||||
.ToDictionary(kv => kv.Key, kv => kv.Value)));
|
||||
func.Add($" matched = {preProcName}(parameters, relation_tags, subresult)");
|
||||
func.Add(" if (matched) then");
|
||||
var tagKey = "_relation:" + behaviourName.FunctionName() + ":" + calledInFunction.FunctionName();
|
||||
_neededKeys.Add("_relation:"+calledInFunction.FunctionName()); // Slightly different then tagkey!
|
||||
tagKey = "_relation:" + behaviourName.FunctionName() + ":" + calledInFunction.FunctionName();
|
||||
_neededKeys.Add("_relation:" + calledInFunction.FunctionName()); // Slightly different then tagkey!
|
||||
func.Add($" result.attributes_to_keep[\"{tagKey}\"] = \"yes\"");
|
||||
func.Add(" end");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
func.Add("end");
|
||||
|
||||
|
||||
|
||||
_code.Add(string.Join("\n", func));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Adds the necessary called functions and the profile main entry point
|
||||
/// </summary>
|
||||
|
@ -96,7 +131,8 @@ namespace AspectedRouting.IO.itinero1
|
|||
$"name = \"{profile.Name}\"",
|
||||
"normalize = false",
|
||||
"vehicle_type = {" + string.Join(", ", profile.VehicleTyps.Select(s => "\"" + s + "\"")) + "}",
|
||||
"meta_whitelist = {" + string.Join(", ", profile.Metadata.Select(s => "\"" + s + "\"")) + "}",
|
||||
"meta_whitelist = {\n " + string.Join("\n , ", profile.Metadata.Select(s => "\"" + s + "\"")) +
|
||||
"}",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
|
@ -132,28 +168,31 @@ namespace AspectedRouting.IO.itinero1
|
|||
"");
|
||||
|
||||
impl +=
|
||||
"\n local priority = \n ";
|
||||
"\n local priority = 0\n ";
|
||||
|
||||
var weightParts = new List<string>();
|
||||
foreach (var (parameterName, expression) in profile.Priority)
|
||||
{
|
||||
var priorityPart = ToLua(new Parameter(parameterName)) + " * ";
|
||||
var paramInLua = ToLua(new Parameter(parameterName));
|
||||
|
||||
|
||||
var exprInLua = ToLua(expression);
|
||||
var subs = new Curry(Typs.Tags, new Var(("a"))).UnificationTable(expression.Types.First());
|
||||
if (subs != null && subs.TryGetValue("$a", out var resultType) &&
|
||||
(resultType.Equals(Typs.Bool) || resultType.Equals(Typs.String)))
|
||||
{
|
||||
priorityPart += "parse(" + ToLua(expression) + ")";
|
||||
}
|
||||
else
|
||||
{
|
||||
priorityPart += ToLua(expression);
|
||||
AddDep("parse");
|
||||
exprInLua = "parse(" + exprInLua + ")";
|
||||
}
|
||||
|
||||
weightParts.Add(priorityPart);
|
||||
impl += "\n "+string.Join("\n ",
|
||||
$"if({paramInLua} ~= 0) then",
|
||||
$" priority = priority + {paramInLua} * {exprInLua}",
|
||||
"end"
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
|
||||
impl += string.Join(" + \n ", weightParts);
|
||||
|
||||
impl += string.Join("\n",
|
||||
"",
|
||||
|
@ -162,14 +201,16 @@ namespace AspectedRouting.IO.itinero1
|
|||
" -- put all the values into the result-table, as needed for itinero",
|
||||
" result.access = 1",
|
||||
" result.speed = speed",
|
||||
" result.factor = priority",
|
||||
" result.factor = 1 / priority",
|
||||
"",
|
||||
" if (oneway == \"both\") then",
|
||||
" result.oneway = 0",
|
||||
" result.direction = 0",
|
||||
" elseif (oneway == \"with\") then",
|
||||
" result.oneway = 1",
|
||||
" result.direction = 1",
|
||||
" elseif (oneway == \"against\") then",
|
||||
" result.direction = 2",
|
||||
" else",
|
||||
" result.oneway = 2",
|
||||
" error(\"Unexpected value for oneway: \"..oneway)",
|
||||
" end",
|
||||
"",
|
||||
"end",
|
||||
|
@ -239,7 +280,6 @@ namespace AspectedRouting.IO.itinero1
|
|||
/// <returns></returns>
|
||||
private string ParametersToLua(Dictionary<string, IExpression> subParams)
|
||||
{
|
||||
|
||||
var impl = "";
|
||||
foreach (var (paramName, value) in subParams)
|
||||
{
|
||||
|
@ -247,6 +287,7 @@ namespace AspectedRouting.IO.itinero1
|
|||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
impl += $" parameters.{paramName.TrimStart('#').FunctionName()} = {ToLua(value)}\n";
|
||||
}
|
||||
|
||||
|
|
|
@ -92,6 +92,7 @@ namespace AspectedRouting.IO.itinero1
|
|||
}
|
||||
|
||||
AddDep("unitTest");
|
||||
AddDep("debug_table");
|
||||
var funcName = functionToApplyName.Replace(" ", "_").Replace(".", "_");
|
||||
return
|
||||
$"unit_test({funcName}, \"{functionToApplyName}\", {index}, \"{expected}\", {parameters.ToLuaTable()}, {tags.ToLuaTable()})";
|
||||
|
|
|
@ -53,6 +53,12 @@ namespace AspectedRouting.IO.jsonParser
|
|||
|
||||
private static ProfileMetaData ParseProfile(this JsonElement e, Context context, FileInfo filepath)
|
||||
{
|
||||
if (!e.TryGetProperty("priority",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()))
|
||||
|
|
|
@ -1,6 +1,24 @@
|
|||
--[[
|
||||
must_match checks that a collection of tags matches a specification.
|
||||
|
||||
The function is not trivial and contains a few subtilities.
|
||||
|
||||
Consider the following source:
|
||||
|
||||
{"$mustMatch":{ "a":"X", "b":{"not":"Y"}}}
|
||||
|
||||
This is desugared into
|
||||
|
||||
{"$mustMatch":{ "a":{"$eq":"X"}, "b":{"not":"Y"}}}
|
||||
|
||||
When applied on the tags {"a" : "X"}, this yields the table {"a":"yes", "b":"yes} (as `notEq` "Y" "nil") yields "yes"..
|
||||
MustMatch checks that every key in this last table yields yes - even if it is not in the original tags!
|
||||
|
||||
|
||||
]]
|
||||
function must_match(tags, result, needed_keys, table)
|
||||
for _, key in ipairs(needed_keys) do
|
||||
local v = tags[key]
|
||||
local v = table[key] -- use the table here, as a tag that must _not_ match might be 'nil' in the tags
|
||||
if (v == nil) then
|
||||
return false
|
||||
end
|
||||
|
@ -16,22 +34,29 @@ function must_match(tags, result, needed_keys, table)
|
|||
end
|
||||
elseif (type(mapping) == "string") then
|
||||
local bool = mapping
|
||||
if (bool == "yes" or bool == "1") then
|
||||
return true
|
||||
elseif (bool == "no" or bool == "0") then
|
||||
if (bool == "no" or bool == "0") then
|
||||
return false
|
||||
end
|
||||
|
||||
if (bool ~= "yes" and bool ~= "1") then
|
||||
error("MustMatch got a string value it can't handle: " .. bool)
|
||||
end
|
||||
elseif (type(mapping) == "boolean") then
|
||||
if(not mapping) then
|
||||
return false
|
||||
end
|
||||
error("MustMatch got a string value it can't handle: " .. bool)
|
||||
else
|
||||
error("The mapping is not a table. This is not supported. We got " .. mapping)
|
||||
error("The mapping is not a table. This is not supported. We got " .. tostring(mapping) .. " (" .. type(mapping)..")")
|
||||
end
|
||||
end
|
||||
|
||||
-- Now that we know for sure that every key matches, we add them all
|
||||
for _, key in ipairs(needed_keys) do
|
||||
local v = tags[key]
|
||||
-- Now that we know for sure that every key matches, we add them all
|
||||
for _, key in ipairs(needed_keys) do
|
||||
local v = tags[key] -- this is the only place where we use the original tags
|
||||
if (v ~= nil) then
|
||||
result.attributes_to_keep[key] = v
|
||||
end
|
||||
end
|
||||
|
||||
return true;
|
||||
return true
|
||||
end
|
|
@ -30,24 +30,24 @@ function unit_test_profile(profile_function, profile_name, index, expected, tags
|
|||
end
|
||||
|
||||
|
||||
local actualOneway = result.oneway;
|
||||
if (result.oneway == 0) then
|
||||
local actualOneway = result.direction;
|
||||
if (result.direction == 0) then
|
||||
actualOneway = "both"
|
||||
elseif (result.oneway == 1) then
|
||||
elseif (result.direction == 1) then
|
||||
actualOneway = "with"
|
||||
elseif (result.oneway == 2) then
|
||||
elseif (result.direction == 2) then
|
||||
actualOneway = "against"
|
||||
end
|
||||
|
||||
if (expected.oneway ~= actualOneway) then
|
||||
print("Test " .. tostring(index) .. " failed for " .. profile_name .. ".oneway: expected " .. expected.oneway .. " but got " .. actualOneway)
|
||||
print("Test " .. tostring(index) .. " failed for " .. profile_name .. ".oneway: expected " .. expected.direction .. " but got " .. actualOneway)
|
||||
failed_profile_tests = true
|
||||
profile_failed = true
|
||||
end
|
||||
|
||||
|
||||
if (not double_compare(result.factor, expected.weight)) then
|
||||
print("Test " .. tostring(index) .. " failed for " .. profile_name .. ".factor: expected " .. expected.weight .. " but got " .. result.factor)
|
||||
if (not double_compare(result.factor, 1/expected.weight)) then
|
||||
print("Test " .. tostring(index) .. " failed for " .. profile_name .. ".factor: expected " .. expected.weight .. " but got " .. 1/result.factor)
|
||||
failed_profile_tests = true
|
||||
profile_failed = true
|
||||
end
|
||||
|
|
|
@ -508,9 +508,27 @@ Note that this is a privileged builtin function, as the parser will automaticall
|
|||
Lua implementation:
|
||||
|
||||
````lua
|
||||
--[[
|
||||
must_match checks that a collection of tags matches a specification.
|
||||
|
||||
The function is not trivial and contains a few subtilities.
|
||||
|
||||
Consider the following source:
|
||||
|
||||
{"$mustMatch":{ "a":"X", "b":{"not":"Y"}}}
|
||||
|
||||
This is desugared into
|
||||
|
||||
{"$mustMatch":{ "a":{"$eq":"X"}, "b":{"not":"Y"}}}
|
||||
|
||||
When applied on the tags {"a" : "X"}, this yields the table {"a":"yes", "b":"yes} (as `notEq` "Y" "nil") yields "yes"..
|
||||
MustMatch checks that every key in this last table yields yes - even if it is not in the original tags!
|
||||
|
||||
|
||||
]]
|
||||
function must_match(tags, result, needed_keys, table)
|
||||
for _, key in ipairs(needed_keys) do
|
||||
local v = tags[key]
|
||||
local v = table[key] -- use the table here, as a tag that must _not_ match might be 'nil' in the tags
|
||||
if (v == nil) then
|
||||
return false
|
||||
end
|
||||
|
@ -526,24 +544,31 @@ function must_match(tags, result, needed_keys, table)
|
|||
end
|
||||
elseif (type(mapping) == "string") then
|
||||
local bool = mapping
|
||||
if (bool == "yes" or bool == "1") then
|
||||
return true
|
||||
elseif (bool == "no" or bool == "0") then
|
||||
if (bool == "no" or bool == "0") then
|
||||
return false
|
||||
end
|
||||
|
||||
if (bool ~= "yes" and bool ~= "1") then
|
||||
error("MustMatch got a string value it can't handle: " .. bool)
|
||||
end
|
||||
elseif (type(mapping) == "boolean") then
|
||||
if(not mapping) then
|
||||
return false
|
||||
end
|
||||
error("MustMatch got a string value it can't handle: " .. bool)
|
||||
else
|
||||
error("The mapping is not a table. This is not supported. We got " .. mapping)
|
||||
error("The mapping is not a table. This is not supported. We got " .. tostring(mapping) .. " (" .. type(mapping)..")")
|
||||
end
|
||||
end
|
||||
|
||||
-- Now that we know for sure that every key matches, we add them all
|
||||
for _, key in ipairs(needed_keys) do
|
||||
local v = tags[key]
|
||||
-- Now that we know for sure that every key matches, we add them all
|
||||
for _, key in ipairs(needed_keys) do
|
||||
local v = tags[key] -- this is the only place where we use the original tags
|
||||
if (v ~= nil) then
|
||||
result.attributes_to_keep[key] = v
|
||||
end
|
||||
end
|
||||
|
||||
return true;
|
||||
return true
|
||||
end
|
||||
````
|
||||
|
||||
|
|
|
@ -131,6 +131,7 @@ namespace AspectedRouting.Language
|
|||
{
|
||||
var parameters = new Dictionary<string, (List<Type> Types, string inFunction)>();
|
||||
|
||||
|
||||
void AddParams(IExpression e, string inFunction)
|
||||
{
|
||||
var parms = e.UsedParameters();
|
||||
|
@ -167,11 +168,15 @@ namespace AspectedRouting.Language
|
|||
AddParams(expr, profile.Name + ".priority");
|
||||
}
|
||||
|
||||
foreach (var (name, expr) in context.DefinedFunctions)
|
||||
var calledFunctions = profile.CalledFunctionsRecursive(context).Values
|
||||
.SelectMany(ls => ls).ToHashSet();
|
||||
foreach (var calledFunction in calledFunctions)
|
||||
{
|
||||
AddParams(expr, name);
|
||||
var func = context.GetFunction(calledFunction);
|
||||
AddParams(func, calledFunction);
|
||||
}
|
||||
|
||||
|
||||
return parameters;
|
||||
}
|
||||
|
||||
|
@ -191,6 +196,54 @@ namespace AspectedRouting.Language
|
|||
return result;
|
||||
}
|
||||
|
||||
|
||||
public static Dictionary<string, List<string>> CalledFunctionsRecursive(this ProfileMetaData profile,
|
||||
Context c)
|
||||
{
|
||||
// Read as: this function calls the value-function
|
||||
var result = new Dictionary<string, List<string>>();
|
||||
var calledFunctions = new Queue<string>();
|
||||
|
||||
void ScanExpression(IExpression e, string inFunction)
|
||||
{
|
||||
result.Add(inFunction, new List<string>());
|
||||
|
||||
e.Visit(x =>
|
||||
{
|
||||
if (x is FunctionCall fc)
|
||||
{
|
||||
result[inFunction].Add(fc.CalledFunctionName);
|
||||
if (!result.ContainsKey(fc.CalledFunctionName))
|
||||
{
|
||||
calledFunctions.Enqueue(fc.CalledFunctionName);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
ScanExpression(profile.Access, profile.Name + ".access");
|
||||
ScanExpression(profile.Oneway, profile.Name + ".oneway");
|
||||
ScanExpression(profile.Speed, profile.Name + ".speed");
|
||||
|
||||
foreach (var (key, expr) in profile.Priority)
|
||||
{
|
||||
ScanExpression(new Parameter(key), $"{profile.Name}.priority.{key}.lefthand");
|
||||
ScanExpression(expr, $"{profile.Name}.priority.{key}");
|
||||
}
|
||||
|
||||
while (calledFunctions.TryDequeue(out var calledFunction))
|
||||
{
|
||||
var func = c.GetFunction(calledFunction);
|
||||
ScanExpression(func, calledFunction);
|
||||
}
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static string TypeBreakdown(this IExpression e)
|
||||
{
|
||||
var text = "";
|
||||
|
@ -206,12 +259,28 @@ namespace AspectedRouting.Language
|
|||
{
|
||||
var defaultParameters = pmd.DefaultParameters.Keys;
|
||||
|
||||
var usedParameters = pmd.UsedParameters(context).Keys.Select(key => key.TrimStart('#'));
|
||||
|
||||
var usedMetadata = pmd.UsedParameters(context);
|
||||
|
||||
string MetaList(IEnumerable<string> paramNames)
|
||||
{
|
||||
var metaInfo = "";
|
||||
foreach (var paramName in paramNames)
|
||||
{
|
||||
var _ = usedMetadata.TryGetValue(paramName, out var inFunction) ||
|
||||
usedMetadata.TryGetValue('#' + paramName, out inFunction);
|
||||
metaInfo += $"\n - {paramName} (used in {inFunction.inFunction})";
|
||||
}
|
||||
|
||||
return metaInfo;
|
||||
}
|
||||
|
||||
var usedParameters = usedMetadata.Keys.Select(key => key.TrimStart('#'));
|
||||
|
||||
var diff = usedParameters.ToHashSet().Except(defaultParameters).ToList();
|
||||
if (diff.Any())
|
||||
{
|
||||
throw new ArgumentException("No default value set for parameter " + string.Join(", ", diff));
|
||||
throw new ArgumentException("No default value set for parameter: " + MetaList(diff));
|
||||
}
|
||||
|
||||
var unused = defaultParameters.Except(usedParameters);
|
||||
|
@ -221,19 +290,21 @@ namespace AspectedRouting.Language
|
|||
string.Join(", ", unused));
|
||||
}
|
||||
|
||||
var paramsUsedInBehaviour = new HashSet<string>();
|
||||
|
||||
foreach (var (behaviourName, behaviourParams) in pmd.Behaviours)
|
||||
{
|
||||
var sum = 0.0;
|
||||
var explanation = "";
|
||||
foreach (var (paramName, _) in pmd.Priority)
|
||||
{
|
||||
|
||||
paramsUsedInBehaviour.Add(paramName);
|
||||
if (!pmd.DefaultParameters.ContainsKey(paramName))
|
||||
{
|
||||
throw new ArgumentException(
|
||||
$"The behaviour {behaviourName} uses a parameter for which no default is set: {paramName}");
|
||||
}
|
||||
|
||||
|
||||
if (!behaviourParams.TryGetValue(paramName, out var weight))
|
||||
{
|
||||
explanation += $"\n - {paramName} = default (not set)";
|
||||
|
@ -244,7 +315,8 @@ namespace AspectedRouting.Language
|
|||
|
||||
if (!(weightObj is double d))
|
||||
{
|
||||
throw new ArgumentException($"The parameter {paramName} is not a numeric value");
|
||||
throw new ArgumentException(
|
||||
$"The parameter {paramName} is not a numeric value in profile {behaviourName}");
|
||||
}
|
||||
|
||||
sum += Math.Abs(d);
|
||||
|
@ -258,6 +330,14 @@ namespace AspectedRouting.Language
|
|||
explanation);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var defaultOnly = defaultParameters.Except(paramsUsedInBehaviour).ToList();
|
||||
if (defaultOnly.Any())
|
||||
{
|
||||
Console.WriteLine(
|
||||
$"[{pmd.Name}] WARNING: Some parameters only have a default value: {string.Join(", ", defaultOnly)}");
|
||||
}
|
||||
}
|
||||
|
||||
public static void SanityCheck(this IExpression e)
|
||||
|
|
|
@ -21,18 +21,7 @@ profiles = {
|
|||
function_name = "determine_weights_networks_node_network",
|
||||
metric = "custom"
|
||||
},
|
||||
{
|
||||
name = "genk",
|
||||
description = "[Custom] A route following the Genk cycle network",
|
||||
function_name = "determine_weights_genk",
|
||||
metric = "custom"
|
||||
},
|
||||
{
|
||||
name = "brussels",
|
||||
description = "[Custom] A route following the Brussels cycle network",
|
||||
function_name = "determine_weights_brussels",
|
||||
metric = "custom"
|
||||
},
|
||||
|
||||
{
|
||||
name = "cycle_highway",
|
||||
description = "A functional route, preferring cycle_highways or the Brussels Mobility network. If none are availale, will favour speed",
|
||||
|
@ -303,49 +292,6 @@ function determine_weights_networks_node_network(attributes, result)
|
|||
determine_weights(attributes, result)
|
||||
end
|
||||
|
||||
function determine_weights_genk(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
|
||||
|
||||
attributes.cycle_network = "yes"
|
||||
attributes.settings.cycle_network_operator = "Stad Genk"
|
||||
end
|
||||
|
||||
determine_weights(attributes, result)
|
||||
end
|
||||
|
||||
function determine_weights_brussels(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 = 5
|
||||
attributes.settings.clear_restrictions_preference = 1;
|
||||
|
||||
attributes.settings.cycle_network_operator = "Brussels Mobility"
|
||||
end
|
||||
|
||||
determine_weights(attributes, result)
|
||||
end
|
||||
|
||||
|
||||
function determine_weights_anyways_network(attributes, result)
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
access,oneway,speed,priority,highway,_relation:bicycle.network_by_operator
|
||||
no,both,0,0,,
|
||||
yes,both,15,1.9,residential,
|
||||
yes,both,15,21.9,residential,yes
|
||||
yes,both,15,6.9,residential,yes
|
||||
dismount,both,2.25,0.0195,footway,
|
||||
|
|
|
|
@ -31,7 +31,7 @@
|
|||
},
|
||||
"access": {
|
||||
"designated": 1.2,
|
||||
"dismount": 0.5
|
||||
"dismount": 0.01
|
||||
},
|
||||
"surface": {
|
||||
"#": "The surface mapping heavily resembles the one in speed_factor, but it is not entirely the same",
|
||||
|
|
6
AspectedRouting/Profiles/bicycle/bicycle.commute.csv
Normal file
6
AspectedRouting/Profiles/bicycle/bicycle.commute.csv
Normal file
|
@ -0,0 +1,6 @@
|
|||
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
|
|
|
@ -22,8 +22,9 @@
|
|||
"#distance": 0,
|
||||
"#comfort": 0,
|
||||
"#safety": 0,
|
||||
"#network": 0,
|
||||
"#networkOperator": []
|
||||
"#operatorNetworkScore": 0,
|
||||
"#networkOperator": [],
|
||||
"#cycleHighwayNetworkScore": 0
|
||||
},
|
||||
"behaviours": {
|
||||
|
||||
|
@ -54,10 +55,31 @@
|
|||
|
||||
"brussels": {
|
||||
"description": "A route preferring the cycling network by operator 'Brussels Mobility'",
|
||||
"#network": 20,
|
||||
"#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",
|
||||
|
@ -77,7 +99,8 @@
|
|||
"priority": {
|
||||
"#comfort": "$bicycle.comfort",
|
||||
"#safety": "$bicycle.safety",
|
||||
"#network": "$bicycle.network_by_operator",
|
||||
"#operatorNetworkScore": "$bicycle.network_by_operator",
|
||||
"#cycleHighwayNetworkScore": "$bicycle.network_is_cyclehighway",
|
||||
"#timeNeeded": "$speed",
|
||||
"#distance": "$distance"
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
"$mustMatch": {
|
||||
"type": "route",
|
||||
"route": "bicycle",
|
||||
"state": {"$notEq": "proposed"},
|
||||
"state": {"$not": "proposed"},
|
||||
"operator": {"$containedIn": "#networkOperator"}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -6,9 +6,11 @@
|
|||
"value": {
|
||||
"$multiply": {
|
||||
"access": {
|
||||
"no": 1.1
|
||||
"#": "access=no implies no access for cars too!",
|
||||
"no": 1.1,
|
||||
"dismount": 0.01
|
||||
},
|
||||
"motor": {
|
||||
"motor_vehicle": {
|
||||
"no": 1.5
|
||||
},
|
||||
"foot": {
|
||||
|
|
37
AspectedRouting/Profiles/bicycle/small.json
Normal file
37
AspectedRouting/Profiles/bicycle/small.json
Normal file
|
@ -0,0 +1,37 @@
|
|||
{
|
||||
"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"
|
||||
}
|
||||
}
|
|
@ -42,8 +42,16 @@ namespace AspectedRouting
|
|||
ProfileMetaData profile, List<ProfileTestSuite> profileTests)
|
||||
{
|
||||
var luaPrinter = new LuaPrinter(context);
|
||||
|
||||
var usedFunctions = profile.CalledFunctionsRecursive(context).Values.SelectMany(v => v).ToHashSet();
|
||||
|
||||
foreach (var (aspect, tests) in aspects)
|
||||
{
|
||||
if (!usedFunctions.Contains(aspect.Name))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
luaPrinter.AddFunction(aspect);
|
||||
if (tests != null)
|
||||
{
|
||||
|
@ -52,8 +60,6 @@ namespace AspectedRouting
|
|||
}
|
||||
|
||||
luaPrinter.AddProfile(profile);
|
||||
|
||||
|
||||
foreach (var testSuite in profileTests)
|
||||
{
|
||||
luaPrinter.AddTestSuite(testSuite);
|
||||
|
@ -63,29 +69,54 @@ namespace AspectedRouting
|
|||
return luaPrinter;
|
||||
}
|
||||
|
||||
private static (ProfileMetaData profile, List<ProfileTestSuite> profileTests) ParseProfile(string profilePath,
|
||||
Context context)
|
||||
private static List<(ProfileMetaData profile, List<ProfileTestSuite> profileTests)> ParseProfiles(
|
||||
IEnumerable<string> jsonFiles, Context context)
|
||||
{
|
||||
var profile = JsonParser.ProfileFromJson(context, File.ReadAllText(profilePath), new FileInfo(profilePath));
|
||||
profile.SanityCheckProfile(context);
|
||||
|
||||
var profileFi = new FileInfo(profilePath);
|
||||
var profileTests = new List<ProfileTestSuite>();
|
||||
foreach (var behaviourName in profile.Behaviours.Keys)
|
||||
var result = new List<(ProfileMetaData profile, List<ProfileTestSuite> profileTests)>();
|
||||
foreach (var jsonFile in jsonFiles)
|
||||
{
|
||||
var testPath = profileFi.DirectoryName + "/" + profile.Name + "." + behaviourName + ".csv";
|
||||
if (File.Exists(testPath))
|
||||
try
|
||||
{
|
||||
var test = ProfileTestSuite.FromString(context, profile, behaviourName, File.ReadAllText(testPath));
|
||||
profileTests.Add(test);
|
||||
var profile =
|
||||
JsonParser.ProfileFromJson(context, File.ReadAllText(jsonFile), new FileInfo(jsonFile));
|
||||
if (profile == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
profile.SanityCheckProfile(context);
|
||||
|
||||
var profileFi = new FileInfo(jsonFile);
|
||||
var profileTests = new List<ProfileTestSuite>();
|
||||
foreach (var behaviourName in profile.Behaviours.Keys)
|
||||
{
|
||||
var path = profileFi.DirectoryName + "/" + profile.Name + "." + behaviourName + ".csv";
|
||||
if (File.Exists(path))
|
||||
{
|
||||
var test = ProfileTestSuite.FromString(context, profile, behaviourName,
|
||||
File.ReadAllText(path));
|
||||
profileTests.Add(test);
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine($"[{profile.Name}] WARNING: no test found for behaviour {behaviourName}");
|
||||
}
|
||||
}
|
||||
|
||||
result.Add((profile, profileTests));
|
||||
}
|
||||
else
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine($"[{behaviourName}] WARNING: no test found for behaviour");
|
||||
PrintError(jsonFile, e);
|
||||
}
|
||||
}
|
||||
|
||||
return (profile, profileTests);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void PrintError(string file, Exception exception)
|
||||
{
|
||||
Console.WriteLine($"Error in the file {file}:\n {exception.Message}");
|
||||
}
|
||||
|
||||
public static void Main(string[] args)
|
||||
|
@ -101,6 +132,15 @@ namespace AspectedRouting
|
|||
|
||||
var aspects = ParseAspects(files, context);
|
||||
|
||||
foreach (var (aspect, _) in aspects)
|
||||
{
|
||||
context.AddFunction(aspect.Name, aspect);
|
||||
}
|
||||
|
||||
var profiles = ParseProfiles(files, context);
|
||||
|
||||
|
||||
// With everything parsed and typechecked, time for tests
|
||||
foreach (var (aspect, t) in aspects)
|
||||
{
|
||||
if (t == null)
|
||||
|
@ -111,23 +151,18 @@ namespace AspectedRouting
|
|||
{
|
||||
t.Run();
|
||||
}
|
||||
|
||||
context.AddFunction(aspect.Name, aspect);
|
||||
}
|
||||
|
||||
|
||||
var profilePath = "Profiles/bicycle/bicycle.json";
|
||||
var (profile, profileTests) = ParseProfile(profilePath, context);
|
||||
|
||||
foreach (var test in profileTests)
|
||||
foreach (var (profile, profileTests) in profiles)
|
||||
{
|
||||
test.Run(context);
|
||||
foreach (var test in profileTests)
|
||||
{
|
||||
test.Run(context);
|
||||
}
|
||||
|
||||
var luaPrinter = GenerateLua(context, aspects, profile, profileTests);
|
||||
File.WriteAllText(profile.Name + ".lua", luaPrinter.ToLua());
|
||||
}
|
||||
|
||||
|
||||
var luaPrinter = GenerateLua(context, aspects, profile, profileTests);
|
||||
|
||||
File.WriteAllText("output.lua", luaPrinter.ToLua());
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue