diff --git a/AspectedRouting/Language/Functions/HeadFunction.cs b/AspectedRouting/Language/Functions/HeadFunction.cs index 3a344dd..1d97d1c 100644 --- a/AspectedRouting/Language/Functions/HeadFunction.cs +++ b/AspectedRouting/Language/Functions/HeadFunction.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using System.Linq; using AspectedRouting.Language.Expression; using AspectedRouting.Language.Typ; @@ -7,41 +6,42 @@ namespace AspectedRouting.Language.Functions { public class HeadFunction : Function { + public HeadFunction() : base("head", true, + new[] { + new Curry(new ListType(new Var("a")), + new Var("a")), + + new Curry( + new Curry(new Var("b"), new ListType(new Var("a"))), + new Curry(new Var("b"), new Var("a"))) + } + ) { } + + private HeadFunction(IEnumerable unified) : base("head", unified) { } + 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 ArgNames { get; } = new List {"ls"}; - - public HeadFunction() : base("head", true, - new[] - { - new Curry(new ListType(new Var("a")), - new Var("a")) - } - ) - { - } - - private HeadFunction(IEnumerable unified) : base("head", unified) - { - } - public override object Evaluate(Context c, params IExpression[] arguments) { - var o = arguments[0].Evaluate(c); - while (o is IExpression e) - { + object o = arguments[0]; + if (o is Apply app) { + o = app.Evaluate(c, arguments.SubArray(1)); + } + + while (o is IExpression e) { o = e.Evaluate(c); } - if (!(o is IEnumerable ls)) return null; - - foreach (var a in ls) - { - if (a != null) - { - return a; + if (!(o is IEnumerable ls)) { + return null; + } + + foreach (var v in ls) { + if (v != null) { + return v; } } @@ -51,8 +51,7 @@ namespace AspectedRouting.Language.Functions public override IExpression Specialize(IEnumerable allowedTypes) { var unified = Types.SpecializeTo(allowedTypes); - if (unified == null) - { + if (unified == null) { return null; } diff --git a/AspectedRouting/Language/Functions/StringStringToTagsFunction.cs b/AspectedRouting/Language/Functions/StringStringToTagsFunction.cs index 820abba..5c9e0bf 100644 --- a/AspectedRouting/Language/Functions/StringStringToTagsFunction.cs +++ b/AspectedRouting/Language/Functions/StringStringToTagsFunction.cs @@ -7,7 +7,8 @@ namespace AspectedRouting.Language.Functions public class StringStringToTagsFunction : Function { public override string Description { get; } = - "stringToTags converts a function `string -> string -> a` into a function `tags -> [a]`"; + "*stringToTags* converts a function `string -> string -> a` into a function `tags -> [a]`. " + + "It is used internally to convert a hash of functions. `stringToTags` shouldn't be needed when implementing profiles."; public override List ArgNames { get; } = new List {"f", "tags"}; diff --git a/AspectedRouting/Tests/FunctionTestSuite.cs b/AspectedRouting/Tests/FunctionTestSuite.cs index b32b669..4834280 100644 --- a/AspectedRouting/Tests/FunctionTestSuite.cs +++ b/AspectedRouting/Tests/FunctionTestSuite.cs @@ -12,6 +12,18 @@ namespace AspectedRouting.Tests public readonly AspectMetadata FunctionToApply; public readonly IEnumerable<(string expected, Dictionary tags)> Tests; + public AspectTestSuite( + AspectMetadata functionToApply, + IEnumerable<(string expected, Dictionary tags)> tests) + { + if (functionToApply == null) { + throw new NullReferenceException("functionToApply is null"); + } + + FunctionToApply = functionToApply; + Tests = tests; + } + public static AspectTestSuite FromString(AspectMetadata function, string csvContents) { var all = csvContents.Split("\n").ToList(); @@ -20,10 +32,8 @@ namespace AspectedRouting.Tests var tests = new List<(string, Dictionary)>(); - foreach (var test in all.GetRange(1, all.Count - 1)) - { - if (string.IsNullOrEmpty(test.Trim())) - { + foreach (var test in all.GetRange(1, all.Count - 1)) { + if (string.IsNullOrEmpty(test.Trim())) { continue; } @@ -31,10 +41,8 @@ namespace AspectedRouting.Tests var expected = testData[0]; var vals = testData.GetRange(1, testData.Count - 1); var tags = new Dictionary(); - for (int i = 0; i < keys.Count; i++) - { - if (i < vals.Count && !string.IsNullOrEmpty(vals[i])) - { + for (var i = 0; i < keys.Count; i++) { + if (i < vals.Count && !string.IsNullOrEmpty(vals[i])) { tags[keys[i]] = vals[i]; } } @@ -45,57 +53,54 @@ namespace AspectedRouting.Tests return new AspectTestSuite(function, tests); } - public AspectTestSuite( - AspectMetadata functionToApply, - IEnumerable<(string expected, Dictionary tags)> tests) - { - if (functionToApply == null) - { - throw new NullReferenceException("functionToApply is null"); - } - - FunctionToApply = functionToApply; - Tests = tests; - } - public bool Run() { var failed = false; var testCase = 0; - foreach (var test in Tests) - { + foreach (var test in Tests) { testCase++; var context = new Context(); - foreach (var (key, value) in test.tags) - { - if (key.StartsWith("#")) - { + foreach (var (key, value) in test.tags) { + if (key.StartsWith("#")) { context.AddParameter(key, value); } } - try - { + try { var actual = FunctionToApply.Evaluate(context, new Constant(test.tags)); - if (test.expected == "null" && actual == null) - { - // Test ok + + if (string.IsNullOrWhiteSpace(test.expected)) { + failed = true; + Console.WriteLine( + $"[{FunctionToApply.Name}] Line {testCase + 1} failed:\n The expected value is not defined or only whitespace. Do you want null? Write null in your test as expected value\n"); + continue; } - else if (!actual.ToString().Equals(test.expected) && - !(actual is double actualD && Math.Abs(double.Parse(test.expected) - actualD) < 0.0001) - ) - { + + if (test.expected == "null" && actual == null) { + // Test ok + continue; + } + + if (actual == null) { + Console.WriteLine( + $"[{FunctionToApply.Name}] Line {testCase + 1} failed:\n Expected: {test.expected}\n actual value is not defined (null)\n tags: {test.tags.Pretty()}\n"); + failed = true; + continue; + } + + if (!actual.ToString().Equals(test.expected) && + !(actual is double actualD && Math.Abs(double.Parse(test.expected) - actualD) < 0.0001) + ) { failed = true; Console.WriteLine( $"[{FunctionToApply.Name}] Line {testCase + 1} failed:\n Expected: {test.expected}\n actual: {actual}\n tags: {test.tags.Pretty()}\n"); } } - catch (Exception e) - { + catch (Exception e) { Console.WriteLine( $"[{FunctionToApply.Name}] Line {testCase + 1} ERROR:\n Expected: {test.expected}\n error message: {e.Message}\n tags: {test.tags.Pretty()}\n"); - + Console.WriteLine(e); failed = true; } } diff --git a/AspectedRouting/Utils.cs b/AspectedRouting/Utils.cs index 5e7bc54..2fad1e8 100644 --- a/AspectedRouting/Utils.cs +++ b/AspectedRouting/Utils.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Linq; using AspectedRouting.Language; @@ -33,6 +34,18 @@ namespace AspectedRouting return factor; } + public static T[] SubArray(this T[] data, int index, int length) + { + T[] result = new T[length]; + Array.Copy(data, index, result, 0, length); + return result; + } + + public static T[] SubArray(this T[] data, int index) + { + return data.SubArray(index, data.Length - index); + } + /// /// Generates a JSON file where all the profiles are listed, together with descriptions and other metadata. /// Useful for other apps, e.g. the routing api to have diff --git a/Examples/output/ProfileMetadata.json b/Examples/output/ProfileMetadata.json deleted file mode 100644 index d3772f3..0000000 --- a/Examples/output/ProfileMetadata.json +++ /dev/null @@ -1,6 +0,0 @@ -[{"name": "shortest","type": "pedestrian","author": "","description": "The shortest route, independent of of speed (Profile for someone who is walking)"} -,{"name": "fastest","type": "bicycle","author": "","description": "The fastest route to your destination (Profile for a normal bicycle)"} -,{"name": "shortest","type": "bicycle","author": "","description": "The shortest route, independent of of speed (Profile for a normal bicycle)"} -,{"name": "comfort","type": "bicycle","author": "","description": "A comfortable route preferring well-paved roads, smaller roads and a bit of scenery at the cost of speed (Profile for a normal bicycle)"} -,{"name": "electric","type": "bicycle","author": "","description": "An electrical bicycle (Profile for a normal bicycle)"} -] \ No newline at end of file diff --git a/Examples/output/bicycle.lua b/Examples/output/bicycle.lua deleted file mode 100644 index 6493dbd..0000000 --- a/Examples/output/bicycle.lua +++ /dev/null @@ -1,1032 +0,0 @@ --- Itinero 1.0-profile, generated by AspectedRouting. Last source file change is 2021-01-27T15:51:22 -name = "bicycle" -normalize = false -vehicle_type = {"vehicle", "bicycle"} -meta_whitelist = { -"name" - , "bridge" - , "tunnel" - , "colour" - , "cycle_network_colour" - , "cycle_network_ref" - , "ref" - , "status" - , "network" } -profile_whitelist = { - "access" - , "highway" - , "service" - , "bicycle" - , "anyways:bicycle" - , "anyways:access" - , "anyways:construction" - , "oneway" - , "oneway:bicycle" - , "junction" - , "cycleway" - , "cycleway:left" - , "maxspeed" - , "designation" - , "railway" - , "towpath" - , "cyclestreet" - , "bicycle:class" - , "surface" - , "route" - } - - - -profiles = { - { - name = "fastest", - function_name = "behaviour_bicycle_fastest", - metric = "custom" - }, - { - name = "shortest", - function_name = "behaviour_bicycle_shortest", - metric = "custom" - }, - { - name = "comfort", - function_name = "behaviour_bicycle_comfort", - metric = "custom" - }, - { - name = "electric", - function_name = "behaviour_bicycle_electric", - metric = "custom" - } -} - -function default_parameters() - local parameters = {} - parameters.defaultSpeed = 15 - parameters.timeNeeded = 0 - parameters.distance = 0 - parameters.comfort = 0 - - return parameters -end - - - ---[[ -"The fastest route to your destination" -]] -function behaviour_bicycle_fastest(tags, result) - tags = remove_relation_prefix(tags, "fastest") - local parameters = default_parameters() - parameters.name = "bicycle_fastest" - parameters.timeNeeded = 1 - bicycle(parameters, tags, result) -end - - ---[[ -"The shortest route, independent of of speed" -]] -function behaviour_bicycle_shortest(tags, result) - tags = remove_relation_prefix(tags, "shortest") - local parameters = default_parameters() - parameters.name = "bicycle_shortest" - parameters.distance = 1 - bicycle(parameters, tags, result) -end - - ---[[ -"A comfortable route preferring well-paved roads, smaller roads and a bit of scenery at the cost of speed" -]] -function behaviour_bicycle_comfort(tags, result) - tags = remove_relation_prefix(tags, "comfort") - local parameters = default_parameters() - parameters.name = "bicycle_comfort" - parameters.comfort = 1 - bicycle(parameters, tags, result) -end - - ---[[ -"An electrical bicycle" -]] -function behaviour_bicycle_electric(tags, result) - tags = remove_relation_prefix(tags, "electric") - local parameters = default_parameters() - parameters.name = "bicycle_electric" - parameters.defaultSpeed = 25 - parameters.comfort = 1 - parameters.timeNeeded = 5 - bicycle(parameters, tags, result) -end - - - - - ---[[ -bicycle -This is the main function called to calculate the access, oneway and speed. -Comfort is calculated as well, based on the parameters which are padded in - -Created by -Originally defined in /home/pietervdvn/git/AspectedRouting/Examples/bicycle -]] -function bicycle(parameters, tags, result) - - -- initialize the result table on the default values - result.access = 0 - result.speed = 0 - result.factor = 1 - result.direction = 0 - result.canstop = true - result.attributes_to_keep = {} - - local access = bicycle_legal_access(parameters, tags, result) - if (access == nil or access == "no") then - return - end - tags.access = access - local oneway = bicycle_oneway(parameters, tags, result) - tags.oneway = oneway - local speed = - min({ - legal_maxspeed_be(parameters, tags, result), - parameters["defaultSpeed"] - }) - tags.speed = speed - local distance = 1 -- the weight per meter for distance travelled is, well, 1m/m - - local priority = 0 - - if(parameters["comfort"] ~= 0) then - priority = priority + parameters["comfort"] * bicycle_comfort(parameters, tags, result) - end - if(parameters["timeNeeded"] ~= 0) then - priority = priority + parameters["timeNeeded"] * speed - end - if(parameters["distance"] ~= 0) then - priority = priority + parameters["distance"] * distance - end - - if (priority <= 0) then - result.access = 0 - return - end - - result.access = 1 - result.speed = speed - result.factor = 1 / priority - - result.direction = 0 - if (oneway == "with" or oneway == "yes") then - result.direction = 1 - elseif (oneway == "against" or oneway == "-1") then - result.direction = 2 - 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(relation_tags, result) - local parameters = {} - local subresult = {} - local matched = false - result.attributes_to_keep = {} - - -- Legacy to add colours to the bike networks - legacy_relation_preprocessor(relation_tags, result) -end - - ----------------------- ASPECTS ---------------------- - - ---[[ -Gives, for each type of highway, whether or not a normal bicycle can enter legally. -Note 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 -'yes': bicycles are allowed here -'permissive': bicycles are allowed here, but this might be a private road or service where usage is allowed, but could be retracted one day by the owner -'dismount': cycling here is not allowed, but walking with the bicycle is -'destination': cycling is allowed here, but only if truly necessary to reach the destination -'private': this is a private road, only go here if the destination is here -'no': do not cycle here -Created by -Originally defined in bicycle.legal_access.json -Uses tags: access, highway, service, bicycle, anyways:bicycle, anyways:access, anyways:construction -Used parameters: -Number of combintations: 54 -Returns values: -]] -function bicycle_legal_access(parameters, tags, result) - return default("no", first_match_of(tags, result, - {"anyways:bicycle", "anyways:access", "anyways:construction", "bicycle", "access", "service", "highway"}, - { - access = { - no = "no", - customers = "private", - private = "private", - permissive = "permissive", - destination = "destination", - delivery = "destination", - service = "destination", - permit = "destination" - }, - highway = { - cycleway = "designated", - residential = "yes", - living_street = "yes", - service = "yes", - services = "yes", - track = "yes", - crossing = "dismount", - footway = "dismount", - pedestrian = "dismount", - corridor = "dismount", - construction = "dismount", - steps = "dismount", - path = "yes", - primary = "yes", - primary_link = "yes", - secondary = "yes", - secondary_link = "yes", - tertiary = "yes", - tertiary_link = "yes", - unclassified = "yes", - road = "yes" - }, - service = { - parking_aisle = "permissive", - driveway = "private", - alley = "yes", - bus = "no" - }, - bicycle = { - yes = "yes", - no = "no", - use_sidepath = "no", - designated = "designated", - permissive = "permissive", - private = "private", - official = "designated", - dismount = "dismount", - permit = "destination" - }, - ["anyways:bicycle"] = tags["anyways:bicycle"], - ["anyways:access"] = { - no = "no", - destination = "destination", - yes = "yes" - }, - ["anyways:construction"] = { - yes = "no" - } - })) -end ---[[ -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 -with: this is a oneway street with direction allowed with the grain of the way -against: oneway street with direction against the way -Created by -Originally defined in bicycle.oneway.json -Uses tags: oneway, oneway:bicycle, junction, cycleway, cycleway:left -Used parameters: -Number of combintations: 32 -Returns values: -]] -function bicycle_oneway(parameters, tags, result) - return default("both", first_match_of(tags, result, - {"oneway:bicycle", "junction", "cycleway", "cycleway:left", "oneway"}, - { - 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", - opposite_lane = "both", - track = "both", - lane = "both", - opposite = "both", - opposite_share_busway = "both", - opposite_track = "both" - }, - ["cycleway:left"] = { - no = "with", - none = "with", - yes = "both", - lane = "both", - track = "both", - shared_lane = "both", - share_busway = "both", - opposite_lane = "both", - opposite_track = "both", - opposite = "both" - } - })) -end ---[[ -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 -Created by -Originally defined in legal_maxspeed_be.json -Uses tags: maxspeed, highway, designation -Used parameters: -Number of combintations: 26 -Returns values: -]] -function legal_maxspeed_be(parameters, tags, result) - return default(30, first_match_of(tags, result, - {"maxspeed", "designation", "highway"}, - { - maxspeed = parse(tags["maxspeed"]), - 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 - } - })) -end ---[[ -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] -Created by -Originally defined in bicycle.comfort.json -Uses tags: highway, railway, towpath, cycleway, cyclestreet, access, bicycle:class, surface, route -Used parameters: -Number of combintations: 55 -Returns values: -]] -function bicycle_comfort(parameters, tags, result) - return default(1, multiply(table_to_list(tags, result, - { - highway = { - cycleway = 1.2, - primary = 0.3, - secondary = 0.4, - tertiary = 0.5, - unclassified = 0.8, - track = 0.95, - residential = 1, - living_street = 1.1, - footway = 0.95, - path = 0.5, - construction = 0.5 - }, - railway = { - abandoned = 2 - }, - towpath = { - yes = 2 - }, - cycleway = { - track = 1.2 - }, - cyclestreet = { - yes = 1.1 - }, - access = { - designated = 1.2, - dismount = 0.01 - }, - ["bicycle:class"] = { - ["-3"] = 0.5, - ["-2"] = 0.7, - ["-1"] = 0.9, - ["0"] = 1, - ["1"] = 1.1, - ["2"] = 1.3, - ["3"] = 1.5 - }, - surface = { - paved = 0.99, - ["concrete:lanes"] = 0.8, - ["concrete:plates"] = 1, - sett = 0.9, - unhewn_cobblestone = 0.75, - cobblestone = 0.8, - unpaved = 0.75, - compacted = 0.95, - fine_gravel = 0.7, - 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 - }, - route = { - ferry = 0.01 - } - }))) -end ---[[ -Actual speed of this function - -Unit: NA -Created by NA -Originally defined in NA -Uses tags: -Used parameters: -Number of combintations: 1 -Returns values: -]] -function speed(parameters, tags, result) - return 15 -end ---[[ -The distance travelled of this profile - -Unit: -Created by -Originally defined in bicycle.json -Uses tags: -Used parameters: -Number of combintations: 1 -Returns values: -]] -function distance(parameters, tags, result) - return 1 -end - - ----------------------- UTILS ------------------------ - - --- instruction generators -instruction_generators = { - { - applies_to = "", -- applies to all profiles when empty - generators = { - { - name = "start", - function_name = "get_start" - }, - { - name = "stop", - function_name = "get_stop" - }, - { - name = "roundabout", - function_name = "get_roundabout" - }, - { - name = "turn", - function_name = "get_turn" - } - } - } -} - --- gets the first instruction -function get_start(route_position, language_reference, instruction) - if route_position.is_first() then - local direction = route_position.direction() - instruction.text = itinero.format(language_reference.get("Start {0}."), language_reference.get(direction)); - instruction.shape = route_position.shape - return 1 - end - return 0 -end - --- gets the last instruction -function get_stop(route_position, language_reference, instruction) - if route_position.is_last() then - instruction.text = language_reference.get("Arrived at destination."); - instruction.shape = route_position.shape - return 1 - end - return 0 -end - - --- gets a roundabout instruction -function get_roundabout(route_position, language_reference, instruction) - if route_position.attributes.junction == "roundabout" and - (not route_position.is_last()) then - local attributes = route_position.next().attributes - if attributes.junction then - else - local exit = 1 - local count = 1 - local previous = route_position.previous() - while previous and previous.attributes.junction == "roundabout" do - local branches = previous.branches - if branches then - branches = branches.get_traversable() - if branches.count > 0 then - exit = exit + 1 - end - end - count = count + 1 - previous = previous.previous() - end - - instruction.text = itinero.format(language_reference.get("Take the {0}th exit at the next roundabout."), "" .. exit) - if exit == 1 then - instruction.text = itinero.format(language_reference.get("Take the first exit at the next roundabout.")) - elseif exit == 2 then - instruction.text = itinero.format(language_reference.get("Take the second exit at the next roundabout.")) - elseif exit == 3 then - instruction.text = itinero.format(language_reference.get("Take the third exit at the next roundabout.")) - end - instruction.type = "roundabout" - instruction.shape = route_position.shape - return count - end - end - return 0 -end - --- gets a turn -function get_turn(route_position, language_reference, instruction) - local relative_direction = route_position.relative_direction().direction - - local turn_relevant = false - local branches = route_position.branches - if branches then - branches = branches.get_traversable() - if relative_direction == "straighton" and - branches.count >= 2 then - turn_relevant = true -- straight on at cross road - end - if relative_direction ~= "straighton" and - branches.count > 0 then - turn_relevant = true -- an actual normal turn - end - end - - if relative_direction == "unknown" then - turn_relevant = false -- turn could not be calculated. - end - - if turn_relevant then - local next = route_position.next() - local name = nil - if next then - name = next.attributes.name - end - if name then - instruction.text = itinero.format(language_reference.get("Go {0} on {1}."), - language_reference.get(relative_direction), name) - instruction.shape = route_position.shape - else - instruction.text = itinero.format(language_reference.get("Go {0}."), - language_reference.get(relative_direction)) - instruction.shape = route_position.shape - end - - return 1 - end - return 0 -end - ---[[ - Legacy function to add cycle_colour -]] - -function legacy_relation_preprocessor(attributes, result) - if (attributes.route == "bicycle") then - -- This is a cycling network, the colour is copied - 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 - - if (attributes.ref ~= nil and attributes.operator == "Stad Genk") then - -- This is pure legacy: we need the ref number only of stad Genk - result.attributes_to_keep.cycle_network_ref = attributes.ref - end - end -end - -function string.start(strt, s) - return string.sub(s, 1, string.len(strt)) == strt -end - - --- every key starting with "_relation::XXX" is rewritten to "_relation:XXX" -function remove_relation_prefix(tags, name) - - local new_tags = {} - for k, v in pairs(tags) do - local prefix = "_relation:" .. name .. ":"; - if (string.start(prefix, k)) then - local new_key = "_relation:" .. string.sub(k, string.len(prefix) + 1) -- plus 1: sub uses one-based indexing to select the start - new_tags[new_key] = v - else - new_tags[k] = v - end - end - return new_tags -end -function default(defaultValue, realValue) - if(realValue ~= nil) then - return realValue - end - return defaultValue -end -function first_match_of(tags, result, order_of_keys, table) - for _, key in pairs(order_of_keys) do - local v = tags[key] - if (v ~= nil) then - - local mapping = table[key] - if (type(mapping) == "table") then - local resultValue = mapping[v] - if (resultValue ~= nil) then - result.attributes_to_keep[key] = v - return resultValue - end - else - result.attributes_to_keep[key] = v - return mapping - end - end - end - return nil; -end -function min(list) - local min - for _, value in pairs(list) do - if(value ~= nil) then - if (min == nil) then - min = value - elseif (value < min) then - min = value - end - end - end - - return min; -end -function parse(string) - if (string == nil) then - return 0 - end - if (type(string) == "number") then - return string - end - - if (string == "yes" or string == "true") then - return 1 - end - - if (string == "no" or string == "false") then - return 0 - end - - if (type(string) == "boolean") then - if (string) then - return 1 - else - return 0 - end - end - - if(string:match("%d+:%d+")) then - -- duration in minute - local duration = 0 - for part in string:gmatch "%d+" do - duration = duration * 60 + tonumber(part) - end - return duration - end - - - return tonumber(string) -end -function multiply(list) - local factor = 1 - for _, value in pairs(list) do - if (value ~= nil) then - factor = factor * value - end - end - return factor; -end -function table_to_list(tags, result, factor_table) - local list = {} - for key, mapping in pairs(factor_table) do - local v = tags[key] - if (v ~= nil) then - if (type(mapping) == "table") then - local f = mapping[v] - if (f ~= nil) then - table.insert(list, f); - result.attributes_to_keep[key] = v - end - else - table.insert(list, mapping); - result.attributes_to_keep[key] = v - end - end - end - - return list; -end -failed_tests = false -function unit_test(f, fname, index, expected, parameters, tags) - if (f == nil) then - print("Trying to unit test " .. fname .. " but this function is not defined") - failed_tests = true - return - end - local result = {attributes_to_keep = {}} - local actual = f(parameters, tags, result) - if(expected == "null" and actual == nil) then - -- OK! - elseif(tonumber(actual) and tonumber(expected) and math.abs(tonumber(actual) - tonumber(expected)) < 0.1) then - -- OK! - elseif (tostring(actual) ~= expected) then - print("[" .. fname .. "] " .. index .. " failed: expected " .. expected .. " but got " .. tostring(actual)) - failed_tests = true - end -end -failed_profile_tests = false ---[[ -expected should be a table containing 'access', 'speed' and 'priority' -]] -function unit_test_profile(profile_function, profile_name, index, expected, tags) - local result = { attributes_to_keep = {} } - local profile_failed = false - profile_function(tags, result) - - local accessCorrect = (result.access == 0 and (expected.access == "no" or expected.priority <= 0)) or result.access == 1 - if (not accessCorrect) then - print("Test " .. tostring(index) .. " failed for " .. profile_name .. ".access: expected " .. expected.access .. " but got " .. result.access) - profile_failed = true - failed_profile_tests = true - end - - if (expected.access == "no" or expected.priority <= 0) then - -- we cannot access this road, the other results are irrelevant - if (profile_failed) then - print("The used tags for test " .. tostring(index) .. " are:") - debug_table(tags) - end - return - end - - if (not double_compare(result.speed, expected.speed)) then - print("Test " .. tostring(index) .. " failed for " .. profile_name .. ".speed: expected " .. expected.speed .. " but got " .. result.speed) - failed_profile_tests = true - profile_failed = true - end - - - local actualOneway = result.direction - - if(actualOneway == nil) then - print("Fail: result.direction is nil") - profile_failed = true; - end - - if (result.direction == 0) then - actualOneway = "both" - elseif (result.direction == 1) then - actualOneway = "with" - 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) - failed_profile_tests = true - profile_failed = true - end - - - if (not double_compare(result.factor, 1/expected.priority)) then - print("Test " .. tostring(index) .. " failed for " .. profile_name .. ".factor: expected " .. expected.priority .. " but got " .. 1/result.factor) - failed_profile_tests = true - profile_failed = true - end - - if (profile_failed == true) then - print("The used tags for test " .. tostring(index) .. " are:") - debug_table(tags) - end -end -function inv(n) - return 1/n -end -function double_compare(a, b) - if (b == nil) then - return false - end - - if (type(a) ~= "number") then - a = parse(a) - end - - if(type(b) ~= "number") then - b = parse(b) - end - if (a == b) then - return true - end - - return math.abs(a - b) < 0.0001 -end -function debug_table(table, prefix) - if (prefix == nil) then - prefix = "" - end - for k, v in pairs(table) do - - if (type(v) == "table") then - debug_table(v, " ") - else - print(prefix .. tostring(k) .. " = " .. tostring(v)) - 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 - - ------------------------ TESTS ------------------------ - - -function test_all() - - - - - - - unit_test(legal_maxspeed_be, "legal_maxspeed_be", 0, "30", {}, {}) - unit_test(legal_maxspeed_be, "legal_maxspeed_be", 1, "30", {}, {highway = "residential"}) - unit_test(legal_maxspeed_be, "legal_maxspeed_be", 2, "50", {}, {highway = "secondary"}) - unit_test(legal_maxspeed_be, "legal_maxspeed_be", 3, "70", {}, {highway = "secondary", maxspeed = "70"}) - - - - unit_test(bicycle_comfort, "bicycle.comfort", 0, "1", {}, {}) - unit_test(bicycle_comfort, "bicycle.comfort", 1, "1", {}, {highway = "residential"}) - unit_test(bicycle_comfort, "bicycle.comfort", 2, "1.1", {}, {highway = "residential", cyclestreet = "yes"}) - unit_test(bicycle_comfort, "bicycle.comfort", 3, "1.2", {}, {highway = "cycleway"}) - unit_test(bicycle_comfort, "bicycle.comfort", 4, "1.2", {}, {highway = "cycleway", foot = "designated"}) - unit_test(bicycle_comfort, "bicycle.comfort", 5, "0.5", {}, {highway = "path", foot = "designated", bicycle = "designated"}) - unit_test(bicycle_comfort, "bicycle.comfort", 6, "0.5", {}, {highway = "path", bicycle = "designated"}) - unit_test(bicycle_comfort, "bicycle.comfort", 7, "0.95", {}, {highway = "footway", foot = "designated"}) - unit_test(bicycle_comfort, "bicycle.comfort", 8, "0.3", {}, {highway = "primary", cycleway = "no"}) - unit_test(bicycle_comfort, "bicycle.comfort", 9, "0.3", {}, {highway = "primary", cycleway = "yes"}) - unit_test(bicycle_comfort, "bicycle.comfort", 10, "0.36", {}, {highway = "primary", cycleway = "track"}) - unit_test(bicycle_comfort, "bicycle.comfort", 11, "0.4", {}, {highway = "secondary", cycleway = "lane"}) - unit_test(bicycle_comfort, "bicycle.comfort", 12, "0.4", {}, {highway = "secondary", ["cycleway:right"] = "lane", surface = "asphalt"}) - unit_test(bicycle_comfort, "bicycle.comfort", 13, "1.1", {}, {highway = "residential", cyclestreet = "yes", surface = "asphalt"}) - unit_test(bicycle_comfort, "bicycle.comfort", 14, "2", {}, {railway = "abandoned"}) - unit_test(bicycle_comfort, "bicycle.comfort", 15, "2", {}, {towpath = "yes"}) - unit_test(bicycle_comfort, "bicycle.comfort", 16, "4", {}, {railway = "abandoned", towpath = "yes"}) - - - - unit_test(bicycle_legal_access, "bicycle.legal_access", 0, "no", {}, {}) - unit_test(bicycle_legal_access, "bicycle.legal_access", 1, "no", {}, {access = "no"}) - unit_test(bicycle_legal_access, "bicycle.legal_access", 2, "yes", {}, {bicycle = "yes", access = "no"}) - unit_test(bicycle_legal_access, "bicycle.legal_access", 3, "yes", {}, {highway = "path"}) - unit_test(bicycle_legal_access, "bicycle.legal_access", 4, "yes", {}, {highway = "pedestrian", bicycle = "yes"}) - unit_test(bicycle_legal_access, "bicycle.legal_access", 5, "dismount", {}, {highway = "pedestrian"}) - unit_test(bicycle_legal_access, "bicycle.legal_access", 6, "designated", {}, {highway = "cycleway"}) - unit_test(bicycle_legal_access, "bicycle.legal_access", 7, "destination", {}, {highway = "residential", access = "destination"}) - unit_test(bicycle_legal_access, "bicycle.legal_access", 8, "private", {}, {highway = "residential", access = "private"}) - unit_test(bicycle_legal_access, "bicycle.legal_access", 9, "designated", {}, {highway = "residential", bicycle = "designated"}) - unit_test(bicycle_legal_access, "bicycle.legal_access", 10, "designated", {}, {highway = "motorway", bicycle = "designated"}) - unit_test(bicycle_legal_access, "bicycle.legal_access", 11, "no", {}, {highway = "residential", bicycle = "use_sidepath"}) - unit_test(bicycle_legal_access, "bicycle.legal_access", 12, "yes", {}, {highway = "residential", access = "no", ["anyways:access"] = "yes"}) - unit_test(bicycle_legal_access, "bicycle.legal_access", 13, "yes", {}, {highway = "primary"}) - unit_test(bicycle_legal_access, "bicycle.legal_access", 14, "yes", {}, {highway = "primary", ["cycleway:right"] = "yes"}) - unit_test(bicycle_legal_access, "bicycle.legal_access", 15, "yes", {}, {highway = "primary", cycleway = "yes"}) - unit_test(bicycle_legal_access, "bicycle.legal_access", 16, "yes", {}, {highway = "secondary", ["cycleway:right"] = "track"}) - unit_test(bicycle_legal_access, "bicycle.legal_access", 17, "destination", {}, {highway = "service", access = "destination"}) - unit_test(bicycle_legal_access, "bicycle.legal_access", 18, "no", {}, {highway = "residential", bicycle = "use_sidepath"}) - unit_test(bicycle_legal_access, "bicycle.legal_access", 19, "yes", {}, {highway = "tertiary", cycleway = "no"}) - unit_test(bicycle_legal_access, "bicycle.legal_access", 20, "dismount", {}, {highway = "construction"}) - - unit_test(bicycle_oneway, "bicycle.oneway", 0, "both", {}, {}) - unit_test(bicycle_oneway, "bicycle.oneway", 1, "both", {}, {oneway = "no"}) - unit_test(bicycle_oneway, "bicycle.oneway", 2, "with", {}, {oneway = "no", ["oneway:bicycle"] = "yes"}) - unit_test(bicycle_oneway, "bicycle.oneway", 3, "with", {}, {junction = "roundabout"}) - unit_test(bicycle_oneway, "bicycle.oneway", 4, "both", {}, {oneway = "yes", cycleway = "opposite"}) - unit_test(bicycle_oneway, "bicycle.oneway", 5, "against", {}, {oneway = "yes", ["oneway:bicycle"] = "-1"}) - unit_test(bicycle_oneway, "bicycle.oneway", 6, "with", {}, {highway = "residential", oneway = "yes"}) - unit_test(bicycle_oneway, "bicycle.oneway", 7, "both", {}, {highway = "residential", oneway = "no"}) - unit_test(bicycle_oneway, "bicycle.oneway", 8, "both", {}, {highway = "residential", oneway = "yes", ["oneway:bicycle"] = "no"}) - unit_test(bicycle_oneway, "bicycle.oneway", 9, "with", {}, {highway = "residential", junction = "roundabout"}) - unit_test(bicycle_oneway, "bicycle.oneway", 10, "both", {}, {highway = "residential", ["oneway:bicycle"] = "no", junction = "roundabout"}) - unit_test(bicycle_oneway, "bicycle.oneway", 11, "against", {}, {highway = "residential", ["oneway:bicycle"] = "-1"}) - unit_test(bicycle_oneway, "bicycle.oneway", 12, "both", {}, {highway = "residential", oneway = "invalidKey", ["oneway:bicycle"] = "no"}) - unit_test(bicycle_oneway, "bicycle.oneway", 13, "with", {}, {highway = "secondary", oneway = "yes", ["cycleway:right"] = "track"}) - unit_test(bicycle_oneway, "bicycle.oneway", 14, "both", {}, {highway = "secondary", oneway = "yes", ["cycleway:left"] = "track"}) - unit_test(bicycle_oneway, "bicycle.oneway", 15, "both", {}, {highway = "secondary", oneway = "yes", cycleway = "track"}) - unit_test(bicycle_oneway, "bicycle.oneway", 16, "with", {}, {oneway = "yes", ["cycleway:left"] = "no"}) - unit_test(bicycle_oneway, "bicycle.oneway", 17, "both", {}, {highway = "residential", oneway = "yes", ["cycleway:left"] = "lane"}) - - - - - -- Behaviour tests -- - - - unit_test_profile(behaviour_bicycle_fastest, "fastest", 0, {access = "no", speed = 0, oneway = "both", priority = 0 }, {}) - unit_test_profile(behaviour_bicycle_fastest, "fastest", 1, {access = "designated", speed = 15, oneway = "both", priority = 15 }, {highway = "cycleway"}) - unit_test_profile(behaviour_bicycle_fastest, "fastest", 2, {access = "yes", speed = 15, oneway = "both", priority = 15 }, {highway = "residential"}) - unit_test_profile(behaviour_bicycle_fastest, "fastest", 3, {access = "yes", speed = 15, oneway = "both", priority = 15 }, {highway = "pedestrian", bicycle = "yes"}) - unit_test_profile(behaviour_bicycle_fastest, "fastest", 4, {access = "yes", speed = 15, oneway = "both", priority = 15 }, {highway = "unclassified", ["cycleway:left"] = "track", oneway = "yes", ["oneway:bicycle"] = "no"}) - unit_test_profile(behaviour_bicycle_fastest, "fastest", 5, {access = "yes", speed = 15, oneway = "both", priority = 15 }, {highway = "service"}) - unit_test_profile(behaviour_bicycle_fastest, "fastest", 6, {access = "yes", speed = 15, oneway = "both", priority = 15 }, {highway = "tertiary", access = "yes", maxspeed = "50"}) - unit_test_profile(behaviour_bicycle_fastest, "fastest", 7, {access = "yes", speed = 15, oneway = "with", priority = 15 }, {highway = "residential", junction = "roundabout"}) - - unit_test_profile(behaviour_bicycle_shortest, "shortest", 0, {access = "no", speed = 0, oneway = "both", priority = 0 }, {}) - unit_test_profile(behaviour_bicycle_shortest, "shortest", 1, {access = "designated", speed = 15, oneway = "both", priority = 1 }, {highway = "cycleway"}) - unit_test_profile(behaviour_bicycle_shortest, "shortest", 2, {access = "yes", speed = 15, oneway = "both", priority = 1 }, {highway = "path", surface = "ground"}) - - -end - - - -if (itinero == nil) then - itinero = {} - itinero.log = print - - -- Itinero is not defined -> we are running from a lua interpreter -> the tests are intended - runTests = true - - -else - print = itinero.log -end - -test_all() -if (not failed_tests and not failed_profile_tests) then - print("Tests OK") -end \ No newline at end of file diff --git a/Examples/output/helpText.md b/Examples/output/helpText.md deleted file mode 100644 index 1988169..0000000 --- a/Examples/output/helpText.md +++ /dev/null @@ -1,1058 +0,0 @@ -## Types - -- double -- pdouble -- nat -- int -- string -- tags -- bool -## Builtin functions - -- eq -- notEq -- not -- inv -- default -- parse -- to_string -- concat -- containedIn -- min -- and -- max -- or -- sum -- plus -- add -- multiply -- atleast -- firstMatchOf -- mustMatch -- memberOf -- if_then_else -- if -- if_then_else_dotted -- ifdotted -- ifDotted -- id -- const -- constRight -- dot -- listDot -- eitherFunc -- stringToTags -- head - - -### Function overview - -#### eq - -a | b | returns | ---- | --- | --- | -$a | $a | bool | -$a | $a | string | - -Returns 'yes' if both values _are_ the same - - - -Lua implementation: - -````lua -function eq(a, b) - if (a == b) then - return "yes" - else - return "no" - end -end - -```` - - -#### notEq - -a | b | returns | ---- | --- | --- | -$a | $a | bool | -$a | $a | string | -bool | bool | - -OVerloaded function, either boolean not or returns 'yes' if the two passed in values are _not_ the same; - - - -Lua implementation: - -````lua -function notEq(a, b) - if (b == nil) then - b = "yes" - end - - if (a ~= b) then - return "yes" - else - return "no" - end -end -```` - - -#### not - -a | b | returns | ---- | --- | --- | -$a | $a | bool | -$a | $a | string | -bool | bool | - -OVerloaded function, either boolean not or returns 'yes' if the two passed in values are _not_ the same; - - - -Lua implementation: - -````lua -function notEq(a, b) - if (b == nil) then - b = "yes" - end - - if (a ~= b) then - return "yes" - else - return "no" - end -end -```` - - -#### inv - -d | returns | ---- | --- | -pdouble | pdouble | -double | double | - -Calculates `1/d` - - - -Lua implementation: - -````lua -function inv(n) - return 1/n -end -```` - - -#### default - -defaultValue | f | returns | ---- | --- | --- | -$a | $b -> $a | $b | $a | - -Calculates function `f` for the given argument. If the result is `null`, the default value is returned instead - - - -Lua implementation: - -````lua -function default(defaultValue, realValue) - if(realValue ~= nil) then - return realValue - end - return defaultValue -end -```` - - -#### parse - -s | returns | ---- | --- | -string | double | -string | pdouble | - -Parses a string into a numerical value. Returns 'null' if parsing fails or no input is given. If a duration is given (e.g. `01:15`), then the number of minutes (75) is returned - - - -Lua implementation: - -````lua -function parse(string) - if (string == nil) then - return 0 - end - if (type(string) == "number") then - return string - end - - if (string == "yes" or string == "true") then - return 1 - end - - if (string == "no" or string == "false") then - return 0 - end - - if (type(string) == "boolean") then - if (string) then - return 1 - else - return 0 - end - end - - if(string:match("%d+:%d+")) then - -- duration in minute - local duration = 0 - for part in string:gmatch "%d+" do - duration = duration * 60 + tonumber(part) - end - return duration - end - - - return tonumber(string) -end -```` - - -#### to_string - -obj | returns | ---- | --- | -$a | string | - -Converts a value into a human readable string - - - -Lua implementation: - -````lua -function to_string(o) - return o; -end -```` - - -#### concat - -a | b | returns | ---- | --- | --- | -string | string | string | - -Concatenates two strings - - - -Lua implementation: - -````lua -function concat(a, b) - return a .. b -end -```` - - -#### containedIn - -list | a | returns | ---- | --- | --- | -list ($a) | $a | bool | - -Given a list of values, checks if the argument is contained in the list. - - - -Lua implementation: - -````lua -function containedIn(list, a) - for _, value in pairs(list) do - if (value == a) then - return true - end - end - - return false; -end -```` - - -#### min - -list | returns | ---- | --- | -list (nat) | nat | -list (int) | int | -list (pdouble) | pdouble | -list (double) | double | -list (bool) | bool | - -Out of a list of values, gets the smallest value. In case of a list of bools, this acts as `and`. Note that 'null'-values are ignored. - - - -Lua implementation: - -````lua -function min(list) - local min - for _, value in pairs(list) do - if(value ~= nil) then - if (min == nil) then - min = value - elseif (value < min) then - min = value - end - end - end - - return min; -end -```` - - -#### and - -list | returns | ---- | --- | -list (nat) | nat | -list (int) | int | -list (pdouble) | pdouble | -list (double) | double | -list (bool) | bool | - -Out of a list of values, gets the smallest value. In case of a list of bools, this acts as `and`. Note that 'null'-values are ignored. - - - -Lua implementation: - -````lua -function min(list) - local min - for _, value in pairs(list) do - if(value ~= nil) then - if (min == nil) then - min = value - elseif (value < min) then - min = value - end - end - end - - return min; -end -```` - - -#### max - -list | returns | ---- | --- | -list (nat) | nat | -list (int) | int | -list (pdouble) | pdouble | -list (double) | double | -list (bool) | bool | - -Returns the biggest value in the list. For a list of booleans, this acts as 'or' - - - -Lua implementation: - -````lua -function max(list) - local max - for _, value in pairs(list) do - if (value ~= nil) then - if (max == nil) then - max = value - elseif (max < value) then - max = value - end - end - end - - return max; -end -```` - - -#### or - -list | returns | ---- | --- | -list (nat) | nat | -list (int) | int | -list (pdouble) | pdouble | -list (double) | double | -list (bool) | bool | - -Returns the biggest value in the list. For a list of booleans, this acts as 'or' - - - -Lua implementation: - -````lua -function max(list) - local max - for _, value in pairs(list) do - if (value ~= nil) then - if (max == nil) then - max = value - elseif (max < value) then - max = value - end - end - end - - return max; -end -```` - - -#### sum - -list | returns | ---- | --- | -list (nat) | nat | -list (int) | int | -list (pdouble) | pdouble | -list (double) | double | -list (bool) | int | - -Sums all the numbers in the given list. If the list is a list of booleans, `yes` or `true` will be considered to equal `1`. Null values are ignored (and thus handled as being `0`) - - - -Lua implementation: - -````lua -function sum(list) - local sum = 0 - for _, value in pairs(list) do - if(value ~= nil) then - if(value == 'yes' or value == 'true') then - value = 1 - end - sum = sum + value - end - end - return sum; -end -```` - - -#### plus - -list | returns | ---- | --- | -list (nat) | nat | -list (int) | int | -list (pdouble) | pdouble | -list (double) | double | -list (bool) | int | - -Sums all the numbers in the given list. If the list is a list of booleans, `yes` or `true` will be considered to equal `1`. Null values are ignored (and thus handled as being `0`) - - - -Lua implementation: - -````lua -function sum(list) - local sum = 0 - for _, value in pairs(list) do - if(value ~= nil) then - if(value == 'yes' or value == 'true') then - value = 1 - end - sum = sum + value - end - end - return sum; -end -```` - - -#### add - -list | returns | ---- | --- | -list (nat) | nat | -list (int) | int | -list (pdouble) | pdouble | -list (double) | double | -list (bool) | int | - -Sums all the numbers in the given list. If the list is a list of booleans, `yes` or `true` will be considered to equal `1`. Null values are ignored (and thus handled as being `0`) - - - -Lua implementation: - -````lua -function sum(list) - local sum = 0 - for _, value in pairs(list) do - if(value ~= nil) then - if(value == 'yes' or value == 'true') then - value = 1 - end - sum = sum + value - end - end - return sum; -end -```` - - -#### multiply - -list | returns | ---- | --- | -list (nat) | nat | -list (int) | int | -list (pdouble) | pdouble | -list (double) | double | -list (bool) | bool | - -Multiplies all the values in a given list. On a list of booleans, this acts as 'and' or 'all', as `false` and `no` are interpreted as zero. Null values are ignored and thus considered to be `one` - - - -Lua implementation: - -````lua -function multiply(list) - local factor = 1 - for _, value in pairs(list) do - if (value ~= nil) then - factor = factor * value - end - end - return factor; -end -```` - - -#### atleast - -minimum | f | then | else | returns | ---- | --- | --- | --- | --- | -double | $b -> double | $a | $a | $b | $a | - -Returns 'yes' if the second argument is bigger then the first argument. (Works great in combination with $dot) - - - -Lua implementation: - -````lua -function atleast(minimumExpected, actual, thn, els) - if (minimumExpected <= actual) then - return thn; - end - return els -end -```` - - -#### firstMatchOf - -s | returns | ---- | --- | -list (string) | tags -> list ($a) | tags | $a | - -This higherorder function takes a list of keys, a mapping (function over tags) and a collection of tags. It will try the function for the first key (and it's respective value). If the function fails (it gives null), it'll try the next key. - -E.g. `$firstMatchOf ['maxspeed','highway'] {'maxspeed' --> $parse, 'highway' --> {residential --> 30, tertiary --> 50}}` applied on `{maxspeed=70, highway=tertiary}` will yield `70` as that is the first key in the list; `{highway=residential}` will yield `30`. - - - -Lua implementation: - -````lua -function first_match_of(tags, result, order_of_keys, table) - for _, key in pairs(order_of_keys) do - local v = tags[key] - if (v ~= nil) then - - local mapping = table[key] - if (type(mapping) == "table") then - local resultValue = mapping[v] - if (resultValue ~= nil) then - result.attributes_to_keep[key] = v - return resultValue - end - else - result.attributes_to_keep[key] = v - return mapping - end - end - end - return nil; -end -```` - - -#### mustMatch - -neededKeys (filled in by parser) | f | returns | ---- | --- | --- | -list (string) | tags -> list (string) | tags | bool | - -Checks that every specified key is present and gives a non-false value -. -If, on top, a value is present with a mapping, every key/value will be executed and must return a value that is not 'no' or 'false' -Note that this is a privileged builtin function, as the parser will automatically inject the keys used in the called function. - - - -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 = 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 - - local mapping = table[key] - if (type(mapping) == "table") then - local resultValue = mapping[v] - if (resultValue == nil or - resultValue == false or - resultValue == "no" or - resultValue == "false") then - return false - end - elseif (type(mapping) == "string") then - local bool = mapping - 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 - else - 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] -- 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 -end -```` - - -#### memberOf - -f | tags | returns | ---- | --- | --- | -tags -> bool | tags | bool | - -This function returns true, if the way is member of a relation matching the specified function. - -In order to use this for itinero 1.0, the membership _must_ be the top level expression. - -Conceptually, when the aspect is executed for a way, every relation will be used as argument in the subfunction `f` -If this subfunction returns 'true', the entire aspect will return true. - -In the lua implementation for itinero 1.0, this is implemented slightly different: a flag `_relation:="yes"` will be set if the aspect matches on every way for where this aspect matches. -However, this plays poorly with parameters (e.g.: what if we want to cycle over a highway which is part of a certain cycling network with a certain `#network_name`?) Luckily, parameters can only be simple values. To work around this problem, an extra tag is introduced for _every single profile_:`_relation::=yes'. The subfunction is thus executed `countOr(relations) * countOf(profiles)` time, yielding `countOf(profiles)` tags. The profile function then picks the tags for himself and strips the `:` away from the key. - - - -In the test.csv, one can simply use `_relation:=yes` to mimic relations in your tests - - - -Lua implementation: - -````lua -function member_of(calledIn, parameters, tags, result) - local k = "_relation:" .. calledIn - -- This tag is conventiently setup by all the preprocessors, which take the parameters into account - local doesMatch = tags[k] - if (doesMatch == "yes") then - result.attributes_to_keep[k] = "yes" - return true - end - return false -end -```` - - -#### if_then_else - -condition | then | else | returns | ---- | --- | --- | --- | -bool | $a | $a | $a | -bool | $a | $a | -string | $a | $a | $a | -string | $a | $a | - -Selects either one of the branches, depending on the condition. The 'then' branch is returned if the condition returns the string `yes` or `true`. Otherwise, the `else` branch is taken (including if the condition returns `null`)If the `else` branch is not set, `null` is returned in the condition is false. - - - -Lua implementation: - -````lua -function if_then_else(condition, thn, els) - if (condition ~= nil and (condition == "yes" or condition == true or condition == "true") then - return thn - else - return els -- if no third parameter is given, 'els' will be nil - end -end -```` - - -#### if - -condition | then | else | returns | ---- | --- | --- | --- | -bool | $a | $a | $a | -bool | $a | $a | -string | $a | $a | $a | -string | $a | $a | - -Selects either one of the branches, depending on the condition. The 'then' branch is returned if the condition returns the string `yes` or `true`. Otherwise, the `else` branch is taken (including if the condition returns `null`)If the `else` branch is not set, `null` is returned in the condition is false. - - - -Lua implementation: - -````lua -function if_then_else(condition, thn, els) - if (condition ~= nil and (condition == "yes" or condition == true or condition == "true") then - return thn - else - return els -- if no third parameter is given, 'els' will be nil - end -end -```` - - -#### if_then_else_dotted - -condition | then | else | returns | ---- | --- | --- | --- | -$b -> bool | $b -> $a | $b | $a | -$b -> string | $b -> $a | $b | $a | -$b -> bool | $b -> $a | $b -> $a | $b | $a | -$b -> string | $b -> $a | $b -> $a | $b | $a | - -An if_then_else, but one which takes an extra argument and applies it on the condition, then and else. -Consider `fc`, `fthen` and `felse` are all functions taking an `a`, then: -`(ifDotted fc fthen felse) a` === `(if (fc a) (fthen a) (felse a)Selects either one of the branches, depending on the condition. The 'then' branch is returned if the condition returns the string `yes` or `true` or the boolean `true`If the `else` branch is not set, `null` is returned in the condition is false.In case the condition returns 'null', then the 'else'-branch is taken. - - - -Lua implementation: - -````lua -function applyIfNeeded(f, arg) - if(f == nil) then - return nil - end - if(type(f) == "function") then - return f(arg) - else - return f - end -end - -function if_then_else_dotted(conditionf, thnf, elsef, arg) - local condition = applyIfNeeded(conditionf, arg); - if (condition) then - return applyIfNeeded(thnf, arg) - else - return applyIfNeeded(elsef, arg) -- if no third parameter is given, 'els' will be nil - end -end -```` - - -#### ifdotted - -condition | then | else | returns | ---- | --- | --- | --- | -$b -> bool | $b -> $a | $b | $a | -$b -> string | $b -> $a | $b | $a | -$b -> bool | $b -> $a | $b -> $a | $b | $a | -$b -> string | $b -> $a | $b -> $a | $b | $a | - -An if_then_else, but one which takes an extra argument and applies it on the condition, then and else. -Consider `fc`, `fthen` and `felse` are all functions taking an `a`, then: -`(ifDotted fc fthen felse) a` === `(if (fc a) (fthen a) (felse a)Selects either one of the branches, depending on the condition. The 'then' branch is returned if the condition returns the string `yes` or `true` or the boolean `true`If the `else` branch is not set, `null` is returned in the condition is false.In case the condition returns 'null', then the 'else'-branch is taken. - - - -Lua implementation: - -````lua -function applyIfNeeded(f, arg) - if(f == nil) then - return nil - end - if(type(f) == "function") then - return f(arg) - else - return f - end -end - -function if_then_else_dotted(conditionf, thnf, elsef, arg) - local condition = applyIfNeeded(conditionf, arg); - if (condition) then - return applyIfNeeded(thnf, arg) - else - return applyIfNeeded(elsef, arg) -- if no third parameter is given, 'els' will be nil - end -end -```` - - -#### ifDotted - -condition | then | else | returns | ---- | --- | --- | --- | -$b -> bool | $b -> $a | $b | $a | -$b -> string | $b -> $a | $b | $a | -$b -> bool | $b -> $a | $b -> $a | $b | $a | -$b -> string | $b -> $a | $b -> $a | $b | $a | - -An if_then_else, but one which takes an extra argument and applies it on the condition, then and else. -Consider `fc`, `fthen` and `felse` are all functions taking an `a`, then: -`(ifDotted fc fthen felse) a` === `(if (fc a) (fthen a) (felse a)Selects either one of the branches, depending on the condition. The 'then' branch is returned if the condition returns the string `yes` or `true` or the boolean `true`If the `else` branch is not set, `null` is returned in the condition is false.In case the condition returns 'null', then the 'else'-branch is taken. - - - -Lua implementation: - -````lua -function applyIfNeeded(f, arg) - if(f == nil) then - return nil - end - if(type(f) == "function") then - return f(arg) - else - return f - end -end - -function if_then_else_dotted(conditionf, thnf, elsef, arg) - local condition = applyIfNeeded(conditionf, arg); - if (condition) then - return applyIfNeeded(thnf, arg) - else - return applyIfNeeded(elsef, arg) -- if no third parameter is given, 'els' will be nil - end -end -```` - - -#### id - -a | returns | ---- | --- | -$a | $a | - -Returns the argument unchanged - the identity function. Seems useless at first sight, but useful in parsing - - - -Lua implementation: - -````lua -function id(v) - return v -end -```` - - -#### const - -a | b | returns | ---- | --- | --- | -$a | $b | $a | - -Small utility function, which takes two arguments `a` and `b` and returns `a`. Used extensively to insert freedom - - - -Lua implementation: - -````lua -function const(a, b) - return a -end -```` - - -#### constRight - -a | b | returns | ---- | --- | --- | -$a | $b | $b | - -Small utility function, which takes two arguments `a` and `b` and returns `b`. Used extensively to insert freedom - - - -Lua implementation: - -````lua - -```` - - -#### dot - -f | g | a | returns | ---- | --- | --- | --- | -$b -> $c | $a -> $b | $a | $c | - -Higher order function: converts `f (g a)` into `(dot f g) a`. In other words, this fuses `f` and `g` in a new function, which allows the argument to be lifted out of the expression - - - -Lua implementation: - -````lua - -```` - - -#### listDot - -list | a | returns | ---- | --- | --- | -list ($a -> $b) | $a | list ($b) | - -Listdot takes a list of functions `[f, g, h]` and and an argument `a`. It applies the argument on every single function.It conveniently lifts the argument out of the list. - - - -Lua implementation: - -````lua --- TODO --- listDot -```` - - -#### eitherFunc - -f | g | a | returns | ---- | --- | --- | --- | -$a -> $b | $c -> $d | $a | $b | -$a -> $b | $c -> $d | $c | $d | - -EitherFunc is a small utility function, mostly used in the parser. It allows the compiler to choose a function, based on the types. - -Consider the mapping `{'someKey':'someValue'}`. Under normal circumstances, this acts as a pointwise-function, converting the string `someKey` into `someValue`, just like an ordinary dictionary would do. However, in the context of `mustMatch`, we would prefer this to act as a _check_, that the highway _has_ a key `someKey` which is `someValue`, thus acting as `{'someKey': {'$eq':'someValue'}}. Both behaviours are automatically supported in parsing, by parsing the string as `(eitherFunc id eq) 'someValue'`. The type system is then able to figure out which implementation is needed. - -Disclaimer: _you should never ever need this in your profiles_ - - - -Lua implementation: - -````lua - -```` - - -#### stringToTags - -f | tags | returns | ---- | --- | --- | -string -> string -> $a | tags | list ($a) | - -stringToTags converts a function `string -> string -> a` into a function `tags -> [a]` - - - -Lua implementation: - -````lua -print("ERROR: stringToTag is needed. This should not happen") -```` - - -#### head - -ls | returns | ---- | --- | -list ($a) | $a | - -Select the first non-null value of a list; returns 'null' on empty list or on null - - - -Lua implementation: - -````lua -function head(ls) - if(ls == nil) then - return nil - end - for _, v in pairs(ls) do - if(v ~= nil) then - return v - end - end - return nil -end -```` - - diff --git a/Examples/output/itinero2/bicycle.comfort.lua b/Examples/output/itinero2/bicycle.comfort.lua deleted file mode 100644 index cb68862..0000000 --- a/Examples/output/itinero2/bicycle.comfort.lua +++ /dev/null @@ -1,604 +0,0 @@ -name = "bicycle.comfort" -generationDate = "2021-01-27T15:51:22" -description = "A comfortable route preferring well-paved roads, smaller roads and a bit of scenery at the cost of speed (Profile for a normal bicycle)" - ---[[ -Calculate the actual factor.forward and factor.backward for a segment with the given properties -]] -function factor(tags, result) - - -- initialize the result table on the default values - result.forward_speed = 0 - result.backward_speed = 0 - result.forward = 0 - result.backward = 0 - result.canstop = true - result.attributes_to_keep = {} -- not actually used anymore, but the code generation still uses this - - - local parameters = default_parameters() - parameters.comfort = 1 - - - local oneway = bicycle_oneway(parameters, tags, result) - tags.oneway = oneway - -- An aspect describing oneway should give either 'both', 'against' or 'width' - - - -- forward calculation. We set the meta tag '_direction' to 'width' to indicate that we are going forward. The other functions will pick this up - tags["_direction"] = "with" - local access_forward = bicycle_legal_access(parameters, tags, result) - if(oneway == "against") then - -- no 'oneway=both' or 'oneway=with', so we can only go back over this segment - -- we overwrite the 'access_forward'-value with no; whatever it was... - access_forward = "no" - end - if(access_forward ~= nil and access_forward ~= "no") then - tags.access = access_forward -- might be relevant, e.g. for 'access=dismount' for bicycles - result.forward_speed = - min({ - legal_maxspeed_be(parameters, tags, result), - parameters["defaultSpeed"] - }) - tags.speed = result.forward_speed - local priority = calculate_priority(parameters, tags, result, access_forward, oneway, result.forward_speed) - if (priority <= 0) then - result.forward_speed = 0 - else - result.forward = 1 / priority - end - end - - -- backward calculation - tags["_direction"] = "against" -- indicate the backward direction to priority calculation - local access_backward = bicycle_legal_access(parameters, tags, result) - if(oneway == "with") then - -- no 'oneway=both' or 'oneway=against', so we can only go forward over this segment - -- we overwrite the 'access_forward'-value with no; whatever it was... - access_backward = "no" - end - if(access_backward ~= nil and access_backward ~= "no") then - tags.access = access_backward - result.backward_speed = - min({ - legal_maxspeed_be(parameters, tags, result), - parameters["defaultSpeed"] - }) - tags.speed = result.backward_speed - local priority = calculate_priority(parameters, tags, result, access_backward, oneway, result.backward_speed) - if (priority <= 0) then - result.backward_speed = 0 - else - result.backward = 1 / priority - end - end -end - ---[[ -Generates the factor according to the priorities and the parameters for this behaviour -Note: 'result' is not actually used -]] -function calculate_priority(parameters, tags, result, access, oneway, speed) - local distance = 1 - local priority = - 1 * bicycle_comfort(parameters, tags, result) - return priority -end - -function default_parameters() - local parameters = {} - parameters.defaultSpeed = 15 - parameters.timeNeeded = 0 - parameters.distance = 0 - parameters.comfort = 0 - - return parameters -end - - ---[[ -Gives, for each type of highway, whether or not a normal bicycle can enter legally. -Note 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 -'yes': bicycles are allowed here -'permissive': bicycles are allowed here, but this might be a private road or service where usage is allowed, but could be retracted one day by the owner -'dismount': cycling here is not allowed, but walking with the bicycle is -'destination': cycling is allowed here, but only if truly necessary to reach the destination -'private': this is a private road, only go here if the destination is here -'no': do not cycle here -Created by -Originally defined in bicycle.legal_access.json -Uses tags: access, highway, service, bicycle, anyways:bicycle, anyways:access, anyways:construction -Used parameters: -Number of combintations: 54 -Returns values: -]] -function bicycle_legal_access(parameters, tags, result) - return default("no", first_match_of(tags, result, - {"anyways:bicycle", "anyways:access", "anyways:construction", "bicycle", "access", "service", "highway"}, - { - access = { - no = "no", - customers = "private", - private = "private", - permissive = "permissive", - destination = "destination", - delivery = "destination", - service = "destination", - permit = "destination" - }, - highway = { - cycleway = "designated", - residential = "yes", - living_street = "yes", - service = "yes", - services = "yes", - track = "yes", - crossing = "dismount", - footway = "dismount", - pedestrian = "dismount", - corridor = "dismount", - construction = "dismount", - steps = "dismount", - path = "yes", - primary = "yes", - primary_link = "yes", - secondary = "yes", - secondary_link = "yes", - tertiary = "yes", - tertiary_link = "yes", - unclassified = "yes", - road = "yes" - }, - service = { - parking_aisle = "permissive", - driveway = "private", - alley = "yes", - bus = "no" - }, - bicycle = { - yes = "yes", - no = "no", - use_sidepath = "no", - designated = "designated", - permissive = "permissive", - private = "private", - official = "designated", - dismount = "dismount", - permit = "destination" - }, - ["anyways:bicycle"] = tags["anyways:bicycle"], - ["anyways:access"] = { - no = "no", - destination = "destination", - yes = "yes" - }, - ["anyways:construction"] = { - yes = "no" - } - })) -end - ---[[ -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 -with: this is a oneway street with direction allowed with the grain of the way -against: oneway street with direction against the way -Created by -Originally defined in bicycle.oneway.json -Uses tags: oneway, oneway:bicycle, junction, cycleway, cycleway:left -Used parameters: -Number of combintations: 32 -Returns values: -]] -function bicycle_oneway(parameters, tags, result) - return default("both", first_match_of(tags, result, - {"oneway:bicycle", "junction", "cycleway", "cycleway:left", "oneway"}, - { - 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", - opposite_lane = "both", - track = "both", - lane = "both", - opposite = "both", - opposite_share_busway = "both", - opposite_track = "both" - }, - ["cycleway:left"] = { - no = "with", - none = "with", - yes = "both", - lane = "both", - track = "both", - shared_lane = "both", - share_busway = "both", - opposite_lane = "both", - opposite_track = "both", - opposite = "both" - } - })) -end - ---[[ -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 -Created by -Originally defined in legal_maxspeed_be.json -Uses tags: maxspeed, highway, designation -Used parameters: -Number of combintations: 26 -Returns values: -]] -function legal_maxspeed_be(parameters, tags, result) - return default(30, first_match_of(tags, result, - {"maxspeed", "designation", "highway"}, - { - maxspeed = parse(tags["maxspeed"]), - 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 - } - })) -end - ---[[ -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] -Created by -Originally defined in bicycle.comfort.json -Uses tags: highway, railway, towpath, cycleway, cyclestreet, access, bicycle:class, surface, route -Used parameters: -Number of combintations: 55 -Returns values: -]] -function bicycle_comfort(parameters, tags, result) - return default(1, multiply(table_to_list(tags, result, - { - highway = { - cycleway = 1.2, - primary = 0.3, - secondary = 0.4, - tertiary = 0.5, - unclassified = 0.8, - track = 0.95, - residential = 1, - living_street = 1.1, - footway = 0.95, - path = 0.5, - construction = 0.5 - }, - railway = { - abandoned = 2 - }, - towpath = { - yes = 2 - }, - cycleway = { - track = 1.2 - }, - cyclestreet = { - yes = 1.1 - }, - access = { - designated = 1.2, - dismount = 0.01 - }, - ["bicycle:class"] = { - ["-3"] = 0.5, - ["-2"] = 0.7, - ["-1"] = 0.9, - ["0"] = 1, - ["1"] = 1.1, - ["2"] = 1.3, - ["3"] = 1.5 - }, - surface = { - paved = 0.99, - ["concrete:lanes"] = 0.8, - ["concrete:plates"] = 1, - sett = 0.9, - unhewn_cobblestone = 0.75, - cobblestone = 0.8, - unpaved = 0.75, - compacted = 0.95, - fine_gravel = 0.7, - 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 - }, - route = { - ferry = 0.01 - } - }))) -end - -failed_profile_tests = false ---[[ -Unit test of a behaviour function for an itinero 2.0 profile -]] - -function unit_test_profile(profile_function, profile_name, index, expected, tags) - -- Note: we don't actually use 'profile_function' - - local result = {} - local profile_failed = false - factor(tags, result) - - - local forward_access = result.forward_speed > 0 and result.forward > 0; - local backward_access = result.backward_speed > 0 and result.backward > 0; - - if (not forward_access and not backward_access) then - - if (expected.access == "no" or expected.speed <= 0 or expected.priority <= 0) then - -- All is fine, we can't access this thing anyway - return - end - - profile_failed = true - print("Test " .. tostring(index) .. " failed for " .. profile_name .. ".access: expected " .. expected.access .. " but forward and backward are 0 (for either speed or factor)") - end - - if (expected.oneway == "with") then - if (backward_access) then - -- we can go against the direction, not good - print("Test " .. tostring(index) .. " failed for " .. profile_name .. ".oneway: expected " .. expected.oneway .. " but going against the direction is possible") - profile_failed = true; - end - if (not forward_access) then - print("Test " .. tostring(index) .. " warning for " .. profile_name .. ".oneway: expected " .. expected.oneway .. " but going with the direction is not possible") - end - end - - if (expected.oneway == "against") then - if (forward_access) then - -- we can go against the direction, not good - print("Test " .. tostring(index) .. " failed for " .. profile_name .. ".oneway: expected " .. expected.oneway .. " but going with the direction is possible") - end - if (not backward_access) then - print("Test " .. tostring(index) .. " warning for " .. profile_name .. ".oneway: expected " .. expected.oneway .. " but going against the direction is not possible") - end - end - - if (result.forward_speed ~= expected.speed and result.backward_speed ~= expected.speed) then - print("Test " .. tostring(index) .. " failed for " .. profile_name .. ".speed: expected " .. expected.speed .. " but got " .. result.forward_speed .. " forward and " .. result.backward_speed .. " backward") - profile_failed = true; - end - - - if (result.forward ~= expected.priority and result.backward ~= expected.priority) then - print("Test " .. tostring(index) .. " failed for " .. profile_name .. ".priority: expected " .. expected.priority .. " but got " .. result.forward .. " forward and " .. result.backward .. " backward") - profile_failed = true; - end - - if(profile_failed) then - failed_profile_tests = true; - debug_table(tags, "tags: ") - debug_table(expected, "expected: ") - debug_table(result, "result: ") - end - -end - -function inv(n) - return 1/n -end - -function double_compare(a, b) - if (b == nil) then - return false - end - - if (type(a) ~= "number") then - a = parse(a) - end - - if(type(b) ~= "number") then - b = parse(b) - end - if (a == b) then - return true - end - - return math.abs(a - b) < 0.0001 -end - -function default(defaultValue, realValue) - if(realValue ~= nil) then - return realValue - end - return defaultValue -end - -function first_match_of(tags, result, order_of_keys, table) - for _, key in pairs(order_of_keys) do - local v = tags[key] - if (v ~= nil) then - - local mapping = table[key] - if (type(mapping) == "table") then - local resultValue = mapping[v] - if (resultValue ~= nil) then - result.attributes_to_keep[key] = v - return resultValue - end - else - result.attributes_to_keep[key] = v - return mapping - end - end - end - return nil; -end - -function parse(string) - if (string == nil) then - return 0 - end - if (type(string) == "number") then - return string - end - - if (string == "yes" or string == "true") then - return 1 - end - - if (string == "no" or string == "false") then - return 0 - end - - if (type(string) == "boolean") then - if (string) then - return 1 - else - return 0 - end - end - - if(string:match("%d+:%d+")) then - -- duration in minute - local duration = 0 - for part in string:gmatch "%d+" do - duration = duration * 60 + tonumber(part) - end - return duration - end - - - return tonumber(string) -end - -function eq(a, b) - if (a == b) then - return "yes" - else - return "no" - end -end - - -function min(list) - local min - for _, value in pairs(list) do - if(value ~= nil) then - if (min == nil) then - min = value - elseif (value < min) then - min = value - end - end - end - - return min; -end - -function multiply(list) - local factor = 1 - for _, value in pairs(list) do - if (value ~= nil) then - factor = factor * value - end - end - return factor; -end - -function table_to_list(tags, result, factor_table) - local list = {} - for key, mapping in pairs(factor_table) do - local v = tags[key] - if (v ~= nil) then - if (type(mapping) == "table") then - local f = mapping[v] - if (f ~= nil) then - table.insert(list, f); - result.attributes_to_keep[key] = v - end - else - table.insert(list, mapping); - result.attributes_to_keep[key] = v - end - end - end - - return list; -end - -function test_all() - - - - - - -- Behaviour tests -- - - - - - -end - -if (itinero == nil) then - itinero = {} - itinero.log = print - - -- Itinero is not defined -> we are running from a lua interpreter -> the tests are intended - runTests = true - - -else - print = itinero.log -end - -test_all() -if (not failed_tests and not failed_profile_tests and print ~= nil) then - print("Tests OK") -end \ No newline at end of file diff --git a/Examples/output/itinero2/bicycle.electric.lua b/Examples/output/itinero2/bicycle.electric.lua deleted file mode 100644 index 00534f3..0000000 --- a/Examples/output/itinero2/bicycle.electric.lua +++ /dev/null @@ -1,607 +0,0 @@ -name = "bicycle.electric" -generationDate = "2021-01-27T15:51:22" -description = "An electrical bicycle (Profile for a normal bicycle)" - ---[[ -Calculate the actual factor.forward and factor.backward for a segment with the given properties -]] -function factor(tags, result) - - -- initialize the result table on the default values - result.forward_speed = 0 - result.backward_speed = 0 - result.forward = 0 - result.backward = 0 - result.canstop = true - result.attributes_to_keep = {} -- not actually used anymore, but the code generation still uses this - - - local parameters = default_parameters() - parameters.defaultSpeed = 25 - parameters.comfort = 1 - parameters.timeNeeded = 5 - - - local oneway = bicycle_oneway(parameters, tags, result) - tags.oneway = oneway - -- An aspect describing oneway should give either 'both', 'against' or 'width' - - - -- forward calculation. We set the meta tag '_direction' to 'width' to indicate that we are going forward. The other functions will pick this up - tags["_direction"] = "with" - local access_forward = bicycle_legal_access(parameters, tags, result) - if(oneway == "against") then - -- no 'oneway=both' or 'oneway=with', so we can only go back over this segment - -- we overwrite the 'access_forward'-value with no; whatever it was... - access_forward = "no" - end - if(access_forward ~= nil and access_forward ~= "no") then - tags.access = access_forward -- might be relevant, e.g. for 'access=dismount' for bicycles - result.forward_speed = - min({ - legal_maxspeed_be(parameters, tags, result), - parameters["defaultSpeed"] - }) - tags.speed = result.forward_speed - local priority = calculate_priority(parameters, tags, result, access_forward, oneway, result.forward_speed) - if (priority <= 0) then - result.forward_speed = 0 - else - result.forward = 1 / priority - end - end - - -- backward calculation - tags["_direction"] = "against" -- indicate the backward direction to priority calculation - local access_backward = bicycle_legal_access(parameters, tags, result) - if(oneway == "with") then - -- no 'oneway=both' or 'oneway=against', so we can only go forward over this segment - -- we overwrite the 'access_forward'-value with no; whatever it was... - access_backward = "no" - end - if(access_backward ~= nil and access_backward ~= "no") then - tags.access = access_backward - result.backward_speed = - min({ - legal_maxspeed_be(parameters, tags, result), - parameters["defaultSpeed"] - }) - tags.speed = result.backward_speed - local priority = calculate_priority(parameters, tags, result, access_backward, oneway, result.backward_speed) - if (priority <= 0) then - result.backward_speed = 0 - else - result.backward = 1 / priority - end - end -end - ---[[ -Generates the factor according to the priorities and the parameters for this behaviour -Note: 'result' is not actually used -]] -function calculate_priority(parameters, tags, result, access, oneway, speed) - local distance = 1 - local priority = - 1 * bicycle_comfort(parameters, tags, result) + - 5 * speed - return priority -end - -function default_parameters() - local parameters = {} - parameters.defaultSpeed = 15 - parameters.timeNeeded = 0 - parameters.distance = 0 - parameters.comfort = 0 - - return parameters -end - - ---[[ -Gives, for each type of highway, whether or not a normal bicycle can enter legally. -Note 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 -'yes': bicycles are allowed here -'permissive': bicycles are allowed here, but this might be a private road or service where usage is allowed, but could be retracted one day by the owner -'dismount': cycling here is not allowed, but walking with the bicycle is -'destination': cycling is allowed here, but only if truly necessary to reach the destination -'private': this is a private road, only go here if the destination is here -'no': do not cycle here -Created by -Originally defined in bicycle.legal_access.json -Uses tags: access, highway, service, bicycle, anyways:bicycle, anyways:access, anyways:construction -Used parameters: -Number of combintations: 54 -Returns values: -]] -function bicycle_legal_access(parameters, tags, result) - return default("no", first_match_of(tags, result, - {"anyways:bicycle", "anyways:access", "anyways:construction", "bicycle", "access", "service", "highway"}, - { - access = { - no = "no", - customers = "private", - private = "private", - permissive = "permissive", - destination = "destination", - delivery = "destination", - service = "destination", - permit = "destination" - }, - highway = { - cycleway = "designated", - residential = "yes", - living_street = "yes", - service = "yes", - services = "yes", - track = "yes", - crossing = "dismount", - footway = "dismount", - pedestrian = "dismount", - corridor = "dismount", - construction = "dismount", - steps = "dismount", - path = "yes", - primary = "yes", - primary_link = "yes", - secondary = "yes", - secondary_link = "yes", - tertiary = "yes", - tertiary_link = "yes", - unclassified = "yes", - road = "yes" - }, - service = { - parking_aisle = "permissive", - driveway = "private", - alley = "yes", - bus = "no" - }, - bicycle = { - yes = "yes", - no = "no", - use_sidepath = "no", - designated = "designated", - permissive = "permissive", - private = "private", - official = "designated", - dismount = "dismount", - permit = "destination" - }, - ["anyways:bicycle"] = tags["anyways:bicycle"], - ["anyways:access"] = { - no = "no", - destination = "destination", - yes = "yes" - }, - ["anyways:construction"] = { - yes = "no" - } - })) -end - ---[[ -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 -with: this is a oneway street with direction allowed with the grain of the way -against: oneway street with direction against the way -Created by -Originally defined in bicycle.oneway.json -Uses tags: oneway, oneway:bicycle, junction, cycleway, cycleway:left -Used parameters: -Number of combintations: 32 -Returns values: -]] -function bicycle_oneway(parameters, tags, result) - return default("both", first_match_of(tags, result, - {"oneway:bicycle", "junction", "cycleway", "cycleway:left", "oneway"}, - { - 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", - opposite_lane = "both", - track = "both", - lane = "both", - opposite = "both", - opposite_share_busway = "both", - opposite_track = "both" - }, - ["cycleway:left"] = { - no = "with", - none = "with", - yes = "both", - lane = "both", - track = "both", - shared_lane = "both", - share_busway = "both", - opposite_lane = "both", - opposite_track = "both", - opposite = "both" - } - })) -end - ---[[ -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 -Created by -Originally defined in legal_maxspeed_be.json -Uses tags: maxspeed, highway, designation -Used parameters: -Number of combintations: 26 -Returns values: -]] -function legal_maxspeed_be(parameters, tags, result) - return default(30, first_match_of(tags, result, - {"maxspeed", "designation", "highway"}, - { - maxspeed = parse(tags["maxspeed"]), - 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 - } - })) -end - ---[[ -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] -Created by -Originally defined in bicycle.comfort.json -Uses tags: highway, railway, towpath, cycleway, cyclestreet, access, bicycle:class, surface, route -Used parameters: -Number of combintations: 55 -Returns values: -]] -function bicycle_comfort(parameters, tags, result) - return default(1, multiply(table_to_list(tags, result, - { - highway = { - cycleway = 1.2, - primary = 0.3, - secondary = 0.4, - tertiary = 0.5, - unclassified = 0.8, - track = 0.95, - residential = 1, - living_street = 1.1, - footway = 0.95, - path = 0.5, - construction = 0.5 - }, - railway = { - abandoned = 2 - }, - towpath = { - yes = 2 - }, - cycleway = { - track = 1.2 - }, - cyclestreet = { - yes = 1.1 - }, - access = { - designated = 1.2, - dismount = 0.01 - }, - ["bicycle:class"] = { - ["-3"] = 0.5, - ["-2"] = 0.7, - ["-1"] = 0.9, - ["0"] = 1, - ["1"] = 1.1, - ["2"] = 1.3, - ["3"] = 1.5 - }, - surface = { - paved = 0.99, - ["concrete:lanes"] = 0.8, - ["concrete:plates"] = 1, - sett = 0.9, - unhewn_cobblestone = 0.75, - cobblestone = 0.8, - unpaved = 0.75, - compacted = 0.95, - fine_gravel = 0.7, - 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 - }, - route = { - ferry = 0.01 - } - }))) -end - -failed_profile_tests = false ---[[ -Unit test of a behaviour function for an itinero 2.0 profile -]] - -function unit_test_profile(profile_function, profile_name, index, expected, tags) - -- Note: we don't actually use 'profile_function' - - local result = {} - local profile_failed = false - factor(tags, result) - - - local forward_access = result.forward_speed > 0 and result.forward > 0; - local backward_access = result.backward_speed > 0 and result.backward > 0; - - if (not forward_access and not backward_access) then - - if (expected.access == "no" or expected.speed <= 0 or expected.priority <= 0) then - -- All is fine, we can't access this thing anyway - return - end - - profile_failed = true - print("Test " .. tostring(index) .. " failed for " .. profile_name .. ".access: expected " .. expected.access .. " but forward and backward are 0 (for either speed or factor)") - end - - if (expected.oneway == "with") then - if (backward_access) then - -- we can go against the direction, not good - print("Test " .. tostring(index) .. " failed for " .. profile_name .. ".oneway: expected " .. expected.oneway .. " but going against the direction is possible") - profile_failed = true; - end - if (not forward_access) then - print("Test " .. tostring(index) .. " warning for " .. profile_name .. ".oneway: expected " .. expected.oneway .. " but going with the direction is not possible") - end - end - - if (expected.oneway == "against") then - if (forward_access) then - -- we can go against the direction, not good - print("Test " .. tostring(index) .. " failed for " .. profile_name .. ".oneway: expected " .. expected.oneway .. " but going with the direction is possible") - end - if (not backward_access) then - print("Test " .. tostring(index) .. " warning for " .. profile_name .. ".oneway: expected " .. expected.oneway .. " but going against the direction is not possible") - end - end - - if (result.forward_speed ~= expected.speed and result.backward_speed ~= expected.speed) then - print("Test " .. tostring(index) .. " failed for " .. profile_name .. ".speed: expected " .. expected.speed .. " but got " .. result.forward_speed .. " forward and " .. result.backward_speed .. " backward") - profile_failed = true; - end - - - if (result.forward ~= expected.priority and result.backward ~= expected.priority) then - print("Test " .. tostring(index) .. " failed for " .. profile_name .. ".priority: expected " .. expected.priority .. " but got " .. result.forward .. " forward and " .. result.backward .. " backward") - profile_failed = true; - end - - if(profile_failed) then - failed_profile_tests = true; - debug_table(tags, "tags: ") - debug_table(expected, "expected: ") - debug_table(result, "result: ") - end - -end - -function inv(n) - return 1/n -end - -function double_compare(a, b) - if (b == nil) then - return false - end - - if (type(a) ~= "number") then - a = parse(a) - end - - if(type(b) ~= "number") then - b = parse(b) - end - if (a == b) then - return true - end - - return math.abs(a - b) < 0.0001 -end - -function default(defaultValue, realValue) - if(realValue ~= nil) then - return realValue - end - return defaultValue -end - -function first_match_of(tags, result, order_of_keys, table) - for _, key in pairs(order_of_keys) do - local v = tags[key] - if (v ~= nil) then - - local mapping = table[key] - if (type(mapping) == "table") then - local resultValue = mapping[v] - if (resultValue ~= nil) then - result.attributes_to_keep[key] = v - return resultValue - end - else - result.attributes_to_keep[key] = v - return mapping - end - end - end - return nil; -end - -function parse(string) - if (string == nil) then - return 0 - end - if (type(string) == "number") then - return string - end - - if (string == "yes" or string == "true") then - return 1 - end - - if (string == "no" or string == "false") then - return 0 - end - - if (type(string) == "boolean") then - if (string) then - return 1 - else - return 0 - end - end - - if(string:match("%d+:%d+")) then - -- duration in minute - local duration = 0 - for part in string:gmatch "%d+" do - duration = duration * 60 + tonumber(part) - end - return duration - end - - - return tonumber(string) -end - -function eq(a, b) - if (a == b) then - return "yes" - else - return "no" - end -end - - -function min(list) - local min - for _, value in pairs(list) do - if(value ~= nil) then - if (min == nil) then - min = value - elseif (value < min) then - min = value - end - end - end - - return min; -end - -function multiply(list) - local factor = 1 - for _, value in pairs(list) do - if (value ~= nil) then - factor = factor * value - end - end - return factor; -end - -function table_to_list(tags, result, factor_table) - local list = {} - for key, mapping in pairs(factor_table) do - local v = tags[key] - if (v ~= nil) then - if (type(mapping) == "table") then - local f = mapping[v] - if (f ~= nil) then - table.insert(list, f); - result.attributes_to_keep[key] = v - end - else - table.insert(list, mapping); - result.attributes_to_keep[key] = v - end - end - end - - return list; -end - -function test_all() - - - - - - -- Behaviour tests -- - - - - - -end - -if (itinero == nil) then - itinero = {} - itinero.log = print - - -- Itinero is not defined -> we are running from a lua interpreter -> the tests are intended - runTests = true - - -else - print = itinero.log -end - -test_all() -if (not failed_tests and not failed_profile_tests and print ~= nil) then - print("Tests OK") -end \ No newline at end of file diff --git a/Examples/output/itinero2/bicycle.fastest.lua b/Examples/output/itinero2/bicycle.fastest.lua deleted file mode 100644 index eeca368..0000000 --- a/Examples/output/itinero2/bicycle.fastest.lua +++ /dev/null @@ -1,531 +0,0 @@ -name = "bicycle.fastest" -generationDate = "2021-01-27T15:51:22" -description = "The fastest route to your destination (Profile for a normal bicycle)" - ---[[ -Calculate the actual factor.forward and factor.backward for a segment with the given properties -]] -function factor(tags, result) - - -- initialize the result table on the default values - result.forward_speed = 0 - result.backward_speed = 0 - result.forward = 0 - result.backward = 0 - result.canstop = true - result.attributes_to_keep = {} -- not actually used anymore, but the code generation still uses this - - - local parameters = default_parameters() - parameters.timeNeeded = 1 - - - local oneway = bicycle_oneway(parameters, tags, result) - tags.oneway = oneway - -- An aspect describing oneway should give either 'both', 'against' or 'width' - - - -- forward calculation. We set the meta tag '_direction' to 'width' to indicate that we are going forward. The other functions will pick this up - tags["_direction"] = "with" - local access_forward = bicycle_legal_access(parameters, tags, result) - if(oneway == "against") then - -- no 'oneway=both' or 'oneway=with', so we can only go back over this segment - -- we overwrite the 'access_forward'-value with no; whatever it was... - access_forward = "no" - end - if(access_forward ~= nil and access_forward ~= "no") then - tags.access = access_forward -- might be relevant, e.g. for 'access=dismount' for bicycles - result.forward_speed = - min({ - legal_maxspeed_be(parameters, tags, result), - parameters["defaultSpeed"] - }) - tags.speed = result.forward_speed - local priority = calculate_priority(parameters, tags, result, access_forward, oneway, result.forward_speed) - if (priority <= 0) then - result.forward_speed = 0 - else - result.forward = 1 / priority - end - end - - -- backward calculation - tags["_direction"] = "against" -- indicate the backward direction to priority calculation - local access_backward = bicycle_legal_access(parameters, tags, result) - if(oneway == "with") then - -- no 'oneway=both' or 'oneway=against', so we can only go forward over this segment - -- we overwrite the 'access_forward'-value with no; whatever it was... - access_backward = "no" - end - if(access_backward ~= nil and access_backward ~= "no") then - tags.access = access_backward - result.backward_speed = - min({ - legal_maxspeed_be(parameters, tags, result), - parameters["defaultSpeed"] - }) - tags.speed = result.backward_speed - local priority = calculate_priority(parameters, tags, result, access_backward, oneway, result.backward_speed) - if (priority <= 0) then - result.backward_speed = 0 - else - result.backward = 1 / priority - end - end -end - ---[[ -Generates the factor according to the priorities and the parameters for this behaviour -Note: 'result' is not actually used -]] -function calculate_priority(parameters, tags, result, access, oneway, speed) - local distance = 1 - local priority = - 1 * speed - return priority -end - -function default_parameters() - local parameters = {} - parameters.defaultSpeed = 15 - parameters.timeNeeded = 0 - parameters.distance = 0 - parameters.comfort = 0 - - return parameters -end - - ---[[ -Gives, for each type of highway, whether or not a normal bicycle can enter legally. -Note 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 -'yes': bicycles are allowed here -'permissive': bicycles are allowed here, but this might be a private road or service where usage is allowed, but could be retracted one day by the owner -'dismount': cycling here is not allowed, but walking with the bicycle is -'destination': cycling is allowed here, but only if truly necessary to reach the destination -'private': this is a private road, only go here if the destination is here -'no': do not cycle here -Created by -Originally defined in bicycle.legal_access.json -Uses tags: access, highway, service, bicycle, anyways:bicycle, anyways:access, anyways:construction -Used parameters: -Number of combintations: 54 -Returns values: -]] -function bicycle_legal_access(parameters, tags, result) - return default("no", first_match_of(tags, result, - {"anyways:bicycle", "anyways:access", "anyways:construction", "bicycle", "access", "service", "highway"}, - { - access = { - no = "no", - customers = "private", - private = "private", - permissive = "permissive", - destination = "destination", - delivery = "destination", - service = "destination", - permit = "destination" - }, - highway = { - cycleway = "designated", - residential = "yes", - living_street = "yes", - service = "yes", - services = "yes", - track = "yes", - crossing = "dismount", - footway = "dismount", - pedestrian = "dismount", - corridor = "dismount", - construction = "dismount", - steps = "dismount", - path = "yes", - primary = "yes", - primary_link = "yes", - secondary = "yes", - secondary_link = "yes", - tertiary = "yes", - tertiary_link = "yes", - unclassified = "yes", - road = "yes" - }, - service = { - parking_aisle = "permissive", - driveway = "private", - alley = "yes", - bus = "no" - }, - bicycle = { - yes = "yes", - no = "no", - use_sidepath = "no", - designated = "designated", - permissive = "permissive", - private = "private", - official = "designated", - dismount = "dismount", - permit = "destination" - }, - ["anyways:bicycle"] = tags["anyways:bicycle"], - ["anyways:access"] = { - no = "no", - destination = "destination", - yes = "yes" - }, - ["anyways:construction"] = { - yes = "no" - } - })) -end - ---[[ -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 -with: this is a oneway street with direction allowed with the grain of the way -against: oneway street with direction against the way -Created by -Originally defined in bicycle.oneway.json -Uses tags: oneway, oneway:bicycle, junction, cycleway, cycleway:left -Used parameters: -Number of combintations: 32 -Returns values: -]] -function bicycle_oneway(parameters, tags, result) - return default("both", first_match_of(tags, result, - {"oneway:bicycle", "junction", "cycleway", "cycleway:left", "oneway"}, - { - 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", - opposite_lane = "both", - track = "both", - lane = "both", - opposite = "both", - opposite_share_busway = "both", - opposite_track = "both" - }, - ["cycleway:left"] = { - no = "with", - none = "with", - yes = "both", - lane = "both", - track = "both", - shared_lane = "both", - share_busway = "both", - opposite_lane = "both", - opposite_track = "both", - opposite = "both" - } - })) -end - ---[[ -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 -Created by -Originally defined in legal_maxspeed_be.json -Uses tags: maxspeed, highway, designation -Used parameters: -Number of combintations: 26 -Returns values: -]] -function legal_maxspeed_be(parameters, tags, result) - return default(30, first_match_of(tags, result, - {"maxspeed", "designation", "highway"}, - { - maxspeed = parse(tags["maxspeed"]), - 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 - } - })) -end - -failed_profile_tests = false ---[[ -Unit test of a behaviour function for an itinero 2.0 profile -]] - -function unit_test_profile(profile_function, profile_name, index, expected, tags) - -- Note: we don't actually use 'profile_function' - - local result = {} - local profile_failed = false - factor(tags, result) - - - local forward_access = result.forward_speed > 0 and result.forward > 0; - local backward_access = result.backward_speed > 0 and result.backward > 0; - - if (not forward_access and not backward_access) then - - if (expected.access == "no" or expected.speed <= 0 or expected.priority <= 0) then - -- All is fine, we can't access this thing anyway - return - end - - profile_failed = true - print("Test " .. tostring(index) .. " failed for " .. profile_name .. ".access: expected " .. expected.access .. " but forward and backward are 0 (for either speed or factor)") - end - - if (expected.oneway == "with") then - if (backward_access) then - -- we can go against the direction, not good - print("Test " .. tostring(index) .. " failed for " .. profile_name .. ".oneway: expected " .. expected.oneway .. " but going against the direction is possible") - profile_failed = true; - end - if (not forward_access) then - print("Test " .. tostring(index) .. " warning for " .. profile_name .. ".oneway: expected " .. expected.oneway .. " but going with the direction is not possible") - end - end - - if (expected.oneway == "against") then - if (forward_access) then - -- we can go against the direction, not good - print("Test " .. tostring(index) .. " failed for " .. profile_name .. ".oneway: expected " .. expected.oneway .. " but going with the direction is possible") - end - if (not backward_access) then - print("Test " .. tostring(index) .. " warning for " .. profile_name .. ".oneway: expected " .. expected.oneway .. " but going against the direction is not possible") - end - end - - if (result.forward_speed ~= expected.speed and result.backward_speed ~= expected.speed) then - print("Test " .. tostring(index) .. " failed for " .. profile_name .. ".speed: expected " .. expected.speed .. " but got " .. result.forward_speed .. " forward and " .. result.backward_speed .. " backward") - profile_failed = true; - end - - - if (result.forward ~= expected.priority and result.backward ~= expected.priority) then - print("Test " .. tostring(index) .. " failed for " .. profile_name .. ".priority: expected " .. expected.priority .. " but got " .. result.forward .. " forward and " .. result.backward .. " backward") - profile_failed = true; - end - - if(profile_failed) then - failed_profile_tests = true; - debug_table(tags, "tags: ") - debug_table(expected, "expected: ") - debug_table(result, "result: ") - end - -end - -function inv(n) - return 1/n -end - -function double_compare(a, b) - if (b == nil) then - return false - end - - if (type(a) ~= "number") then - a = parse(a) - end - - if(type(b) ~= "number") then - b = parse(b) - end - if (a == b) then - return true - end - - return math.abs(a - b) < 0.0001 -end - -function debug_table(table, prefix) - if (prefix == nil) then - prefix = "" - end - for k, v in pairs(table) do - - if (type(v) == "table") then - debug_table(v, " ") - else - print(prefix .. tostring(k) .. " = " .. tostring(v)) - 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 - -function default(defaultValue, realValue) - if(realValue ~= nil) then - return realValue - end - return defaultValue -end - -function first_match_of(tags, result, order_of_keys, table) - for _, key in pairs(order_of_keys) do - local v = tags[key] - if (v ~= nil) then - - local mapping = table[key] - if (type(mapping) == "table") then - local resultValue = mapping[v] - if (resultValue ~= nil) then - result.attributes_to_keep[key] = v - return resultValue - end - else - result.attributes_to_keep[key] = v - return mapping - end - end - end - return nil; -end - -function parse(string) - if (string == nil) then - return 0 - end - if (type(string) == "number") then - return string - end - - if (string == "yes" or string == "true") then - return 1 - end - - if (string == "no" or string == "false") then - return 0 - end - - if (type(string) == "boolean") then - if (string) then - return 1 - else - return 0 - end - end - - if(string:match("%d+:%d+")) then - -- duration in minute - local duration = 0 - for part in string:gmatch "%d+" do - duration = duration * 60 + tonumber(part) - end - return duration - end - - - return tonumber(string) -end - -function eq(a, b) - if (a == b) then - return "yes" - else - return "no" - end -end - - -function min(list) - local min - for _, value in pairs(list) do - if(value ~= nil) then - if (min == nil) then - min = value - elseif (value < min) then - min = value - end - end - end - - return min; -end - -function test_all() - - - - - - -- Behaviour tests -- - - - unit_test_profile(behaviour_bicycle_fastest, "fastest", 0, {access = "no", speed = 0, oneway = "both", priority = inv(0) }, {}) - unit_test_profile(behaviour_bicycle_fastest, "fastest", 1, {access = "designated", speed = 15, oneway = "both", priority = inv(15) }, {highway = "cycleway"}) - unit_test_profile(behaviour_bicycle_fastest, "fastest", 2, {access = "yes", speed = 15, oneway = "both", priority = inv(15) }, {highway = "residential"}) - unit_test_profile(behaviour_bicycle_fastest, "fastest", 3, {access = "yes", speed = 15, oneway = "both", priority = inv(15) }, {highway = "pedestrian", bicycle = "yes"}) - unit_test_profile(behaviour_bicycle_fastest, "fastest", 4, {access = "yes", speed = 15, oneway = "both", priority = inv(15) }, {highway = "unclassified", ["cycleway:left"] = "track", oneway = "yes", ["oneway:bicycle"] = "no"}) - unit_test_profile(behaviour_bicycle_fastest, "fastest", 5, {access = "yes", speed = 15, oneway = "both", priority = inv(15) }, {highway = "service"}) - unit_test_profile(behaviour_bicycle_fastest, "fastest", 6, {access = "yes", speed = 15, oneway = "both", priority = inv(15) }, {highway = "tertiary", access = "yes", maxspeed = "50"}) - unit_test_profile(behaviour_bicycle_fastest, "fastest", 7, {access = "yes", speed = 15, oneway = "with", priority = inv(15) }, {highway = "residential", junction = "roundabout"}) - - -end - -if (itinero == nil) then - itinero = {} - itinero.log = print - - -- Itinero is not defined -> we are running from a lua interpreter -> the tests are intended - runTests = true - - -else - print = itinero.log -end - -test_all() -if (not failed_tests and not failed_profile_tests and print ~= nil) then - print("Tests OK") -end \ No newline at end of file diff --git a/Examples/output/itinero2/bicycle.shortest.lua b/Examples/output/itinero2/bicycle.shortest.lua deleted file mode 100644 index 5b07f95..0000000 --- a/Examples/output/itinero2/bicycle.shortest.lua +++ /dev/null @@ -1,526 +0,0 @@ -name = "bicycle.shortest" -generationDate = "2021-01-27T15:51:22" -description = "The shortest route, independent of of speed (Profile for a normal bicycle)" - ---[[ -Calculate the actual factor.forward and factor.backward for a segment with the given properties -]] -function factor(tags, result) - - -- initialize the result table on the default values - result.forward_speed = 0 - result.backward_speed = 0 - result.forward = 0 - result.backward = 0 - result.canstop = true - result.attributes_to_keep = {} -- not actually used anymore, but the code generation still uses this - - - local parameters = default_parameters() - parameters.distance = 1 - - - local oneway = bicycle_oneway(parameters, tags, result) - tags.oneway = oneway - -- An aspect describing oneway should give either 'both', 'against' or 'width' - - - -- forward calculation. We set the meta tag '_direction' to 'width' to indicate that we are going forward. The other functions will pick this up - tags["_direction"] = "with" - local access_forward = bicycle_legal_access(parameters, tags, result) - if(oneway == "against") then - -- no 'oneway=both' or 'oneway=with', so we can only go back over this segment - -- we overwrite the 'access_forward'-value with no; whatever it was... - access_forward = "no" - end - if(access_forward ~= nil and access_forward ~= "no") then - tags.access = access_forward -- might be relevant, e.g. for 'access=dismount' for bicycles - result.forward_speed = - min({ - legal_maxspeed_be(parameters, tags, result), - parameters["defaultSpeed"] - }) - tags.speed = result.forward_speed - local priority = calculate_priority(parameters, tags, result, access_forward, oneway, result.forward_speed) - if (priority <= 0) then - result.forward_speed = 0 - else - result.forward = 1 / priority - end - end - - -- backward calculation - tags["_direction"] = "against" -- indicate the backward direction to priority calculation - local access_backward = bicycle_legal_access(parameters, tags, result) - if(oneway == "with") then - -- no 'oneway=both' or 'oneway=against', so we can only go forward over this segment - -- we overwrite the 'access_forward'-value with no; whatever it was... - access_backward = "no" - end - if(access_backward ~= nil and access_backward ~= "no") then - tags.access = access_backward - result.backward_speed = - min({ - legal_maxspeed_be(parameters, tags, result), - parameters["defaultSpeed"] - }) - tags.speed = result.backward_speed - local priority = calculate_priority(parameters, tags, result, access_backward, oneway, result.backward_speed) - if (priority <= 0) then - result.backward_speed = 0 - else - result.backward = 1 / priority - end - end -end - ---[[ -Generates the factor according to the priorities and the parameters for this behaviour -Note: 'result' is not actually used -]] -function calculate_priority(parameters, tags, result, access, oneway, speed) - local distance = 1 - local priority = - 1 * distance - return priority -end - -function default_parameters() - local parameters = {} - parameters.defaultSpeed = 15 - parameters.timeNeeded = 0 - parameters.distance = 0 - parameters.comfort = 0 - - return parameters -end - - ---[[ -Gives, for each type of highway, whether or not a normal bicycle can enter legally. -Note 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 -'yes': bicycles are allowed here -'permissive': bicycles are allowed here, but this might be a private road or service where usage is allowed, but could be retracted one day by the owner -'dismount': cycling here is not allowed, but walking with the bicycle is -'destination': cycling is allowed here, but only if truly necessary to reach the destination -'private': this is a private road, only go here if the destination is here -'no': do not cycle here -Created by -Originally defined in bicycle.legal_access.json -Uses tags: access, highway, service, bicycle, anyways:bicycle, anyways:access, anyways:construction -Used parameters: -Number of combintations: 54 -Returns values: -]] -function bicycle_legal_access(parameters, tags, result) - return default("no", first_match_of(tags, result, - {"anyways:bicycle", "anyways:access", "anyways:construction", "bicycle", "access", "service", "highway"}, - { - access = { - no = "no", - customers = "private", - private = "private", - permissive = "permissive", - destination = "destination", - delivery = "destination", - service = "destination", - permit = "destination" - }, - highway = { - cycleway = "designated", - residential = "yes", - living_street = "yes", - service = "yes", - services = "yes", - track = "yes", - crossing = "dismount", - footway = "dismount", - pedestrian = "dismount", - corridor = "dismount", - construction = "dismount", - steps = "dismount", - path = "yes", - primary = "yes", - primary_link = "yes", - secondary = "yes", - secondary_link = "yes", - tertiary = "yes", - tertiary_link = "yes", - unclassified = "yes", - road = "yes" - }, - service = { - parking_aisle = "permissive", - driveway = "private", - alley = "yes", - bus = "no" - }, - bicycle = { - yes = "yes", - no = "no", - use_sidepath = "no", - designated = "designated", - permissive = "permissive", - private = "private", - official = "designated", - dismount = "dismount", - permit = "destination" - }, - ["anyways:bicycle"] = tags["anyways:bicycle"], - ["anyways:access"] = { - no = "no", - destination = "destination", - yes = "yes" - }, - ["anyways:construction"] = { - yes = "no" - } - })) -end - ---[[ -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 -with: this is a oneway street with direction allowed with the grain of the way -against: oneway street with direction against the way -Created by -Originally defined in bicycle.oneway.json -Uses tags: oneway, oneway:bicycle, junction, cycleway, cycleway:left -Used parameters: -Number of combintations: 32 -Returns values: -]] -function bicycle_oneway(parameters, tags, result) - return default("both", first_match_of(tags, result, - {"oneway:bicycle", "junction", "cycleway", "cycleway:left", "oneway"}, - { - 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", - opposite_lane = "both", - track = "both", - lane = "both", - opposite = "both", - opposite_share_busway = "both", - opposite_track = "both" - }, - ["cycleway:left"] = { - no = "with", - none = "with", - yes = "both", - lane = "both", - track = "both", - shared_lane = "both", - share_busway = "both", - opposite_lane = "both", - opposite_track = "both", - opposite = "both" - } - })) -end - ---[[ -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 -Created by -Originally defined in legal_maxspeed_be.json -Uses tags: maxspeed, highway, designation -Used parameters: -Number of combintations: 26 -Returns values: -]] -function legal_maxspeed_be(parameters, tags, result) - return default(30, first_match_of(tags, result, - {"maxspeed", "designation", "highway"}, - { - maxspeed = parse(tags["maxspeed"]), - 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 - } - })) -end - -failed_profile_tests = false ---[[ -Unit test of a behaviour function for an itinero 2.0 profile -]] - -function unit_test_profile(profile_function, profile_name, index, expected, tags) - -- Note: we don't actually use 'profile_function' - - local result = {} - local profile_failed = false - factor(tags, result) - - - local forward_access = result.forward_speed > 0 and result.forward > 0; - local backward_access = result.backward_speed > 0 and result.backward > 0; - - if (not forward_access and not backward_access) then - - if (expected.access == "no" or expected.speed <= 0 or expected.priority <= 0) then - -- All is fine, we can't access this thing anyway - return - end - - profile_failed = true - print("Test " .. tostring(index) .. " failed for " .. profile_name .. ".access: expected " .. expected.access .. " but forward and backward are 0 (for either speed or factor)") - end - - if (expected.oneway == "with") then - if (backward_access) then - -- we can go against the direction, not good - print("Test " .. tostring(index) .. " failed for " .. profile_name .. ".oneway: expected " .. expected.oneway .. " but going against the direction is possible") - profile_failed = true; - end - if (not forward_access) then - print("Test " .. tostring(index) .. " warning for " .. profile_name .. ".oneway: expected " .. expected.oneway .. " but going with the direction is not possible") - end - end - - if (expected.oneway == "against") then - if (forward_access) then - -- we can go against the direction, not good - print("Test " .. tostring(index) .. " failed for " .. profile_name .. ".oneway: expected " .. expected.oneway .. " but going with the direction is possible") - end - if (not backward_access) then - print("Test " .. tostring(index) .. " warning for " .. profile_name .. ".oneway: expected " .. expected.oneway .. " but going against the direction is not possible") - end - end - - if (result.forward_speed ~= expected.speed and result.backward_speed ~= expected.speed) then - print("Test " .. tostring(index) .. " failed for " .. profile_name .. ".speed: expected " .. expected.speed .. " but got " .. result.forward_speed .. " forward and " .. result.backward_speed .. " backward") - profile_failed = true; - end - - - if (result.forward ~= expected.priority and result.backward ~= expected.priority) then - print("Test " .. tostring(index) .. " failed for " .. profile_name .. ".priority: expected " .. expected.priority .. " but got " .. result.forward .. " forward and " .. result.backward .. " backward") - profile_failed = true; - end - - if(profile_failed) then - failed_profile_tests = true; - debug_table(tags, "tags: ") - debug_table(expected, "expected: ") - debug_table(result, "result: ") - end - -end - -function inv(n) - return 1/n -end - -function double_compare(a, b) - if (b == nil) then - return false - end - - if (type(a) ~= "number") then - a = parse(a) - end - - if(type(b) ~= "number") then - b = parse(b) - end - if (a == b) then - return true - end - - return math.abs(a - b) < 0.0001 -end - -function debug_table(table, prefix) - if (prefix == nil) then - prefix = "" - end - for k, v in pairs(table) do - - if (type(v) == "table") then - debug_table(v, " ") - else - print(prefix .. tostring(k) .. " = " .. tostring(v)) - 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 - -function default(defaultValue, realValue) - if(realValue ~= nil) then - return realValue - end - return defaultValue -end - -function first_match_of(tags, result, order_of_keys, table) - for _, key in pairs(order_of_keys) do - local v = tags[key] - if (v ~= nil) then - - local mapping = table[key] - if (type(mapping) == "table") then - local resultValue = mapping[v] - if (resultValue ~= nil) then - result.attributes_to_keep[key] = v - return resultValue - end - else - result.attributes_to_keep[key] = v - return mapping - end - end - end - return nil; -end - -function parse(string) - if (string == nil) then - return 0 - end - if (type(string) == "number") then - return string - end - - if (string == "yes" or string == "true") then - return 1 - end - - if (string == "no" or string == "false") then - return 0 - end - - if (type(string) == "boolean") then - if (string) then - return 1 - else - return 0 - end - end - - if(string:match("%d+:%d+")) then - -- duration in minute - local duration = 0 - for part in string:gmatch "%d+" do - duration = duration * 60 + tonumber(part) - end - return duration - end - - - return tonumber(string) -end - -function eq(a, b) - if (a == b) then - return "yes" - else - return "no" - end -end - - -function min(list) - local min - for _, value in pairs(list) do - if(value ~= nil) then - if (min == nil) then - min = value - elseif (value < min) then - min = value - end - end - end - - return min; -end - -function test_all() - - - - - - -- Behaviour tests -- - - - unit_test_profile(behaviour_bicycle_shortest, "shortest", 0, {access = "no", speed = 0, oneway = "both", priority = inv(0) }, {}) - unit_test_profile(behaviour_bicycle_shortest, "shortest", 1, {access = "designated", speed = 15, oneway = "both", priority = inv(1) }, {highway = "cycleway"}) - unit_test_profile(behaviour_bicycle_shortest, "shortest", 2, {access = "yes", speed = 15, oneway = "both", priority = inv(1) }, {highway = "path", surface = "ground"}) - - -end - -if (itinero == nil) then - itinero = {} - itinero.log = print - - -- Itinero is not defined -> we are running from a lua interpreter -> the tests are intended - runTests = true - - -else - print = itinero.log -end - -test_all() -if (not failed_tests and not failed_profile_tests and print ~= nil) then - print("Tests OK") -end \ No newline at end of file diff --git a/Examples/output/itinero2/pedestrian.shortest.lua b/Examples/output/itinero2/pedestrian.shortest.lua deleted file mode 100644 index f4304bf..0000000 --- a/Examples/output/itinero2/pedestrian.shortest.lua +++ /dev/null @@ -1,393 +0,0 @@ -name = "pedestrian.shortest" -generationDate = "2021-01-27T15:51:22" -description = "The shortest route, independent of of speed (Profile for someone who is walking)" - ---[[ -Calculate the actual factor.forward and factor.backward for a segment with the given properties -]] -function factor(tags, result) - - -- initialize the result table on the default values - result.forward_speed = 0 - result.backward_speed = 0 - result.forward = 0 - result.backward = 0 - result.canstop = true - result.attributes_to_keep = {} -- not actually used anymore, but the code generation still uses this - - - local parameters = default_parameters() - parameters.distance = 1 - parameters.leastSafetyPenalty = 2 - - - local oneway = const("both") - tags.oneway = oneway - -- An aspect describing oneway should give either 'both', 'against' or 'width' - - - -- forward calculation. We set the meta tag '_direction' to 'width' to indicate that we are going forward. The other functions will pick this up - tags["_direction"] = "with" - local access_forward = pedestrian_legal_access(parameters, tags, result) - if(oneway == "against") then - -- no 'oneway=both' or 'oneway=with', so we can only go back over this segment - -- we overwrite the 'access_forward'-value with no; whatever it was... - access_forward = "no" - end - if(access_forward ~= nil and access_forward ~= "no") then - tags.access = access_forward -- might be relevant, e.g. for 'access=dismount' for bicycles - result.forward_speed = const(parameters["defaultSpeed"]) - tags.speed = result.forward_speed - local priority = calculate_priority(parameters, tags, result, access_forward, oneway, result.forward_speed) - if (priority <= 0) then - result.forward_speed = 0 - else - result.forward = 1 / priority - end - end - - -- backward calculation - tags["_direction"] = "against" -- indicate the backward direction to priority calculation - local access_backward = pedestrian_legal_access(parameters, tags, result) - if(oneway == "with") then - -- no 'oneway=both' or 'oneway=against', so we can only go forward over this segment - -- we overwrite the 'access_forward'-value with no; whatever it was... - access_backward = "no" - end - if(access_backward ~= nil and access_backward ~= "no") then - tags.access = access_backward - result.backward_speed = const(parameters["defaultSpeed"]) - tags.speed = result.backward_speed - local priority = calculate_priority(parameters, tags, result, access_backward, oneway, result.backward_speed) - if (priority <= 0) then - result.backward_speed = 0 - else - result.backward = 1 / priority - end - end -end - ---[[ -Generates the factor according to the priorities and the parameters for this behaviour -Note: 'result' is not actually used -]] -function calculate_priority(parameters, tags, result, access, oneway, speed) - local distance = 1 - local priority = - 1 * distance + - 1 * clean_permission_score(parameters, tags, result) - return priority -end - -function default_parameters() - local parameters = {} - parameters.defaultSpeed = 4 - parameters.maxspeed = 6 - parameters.timeNeeded = 0 - parameters.distance = 0 - parameters.slow_road_preference = 0 - parameters.trespassingPenalty = 1 - - return parameters -end - - ---[[ -Gives, for each type of highway, whether or not someone can enter legally. -Note 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 pedestrian -'yes': pedestrians are allowed here -'permissive': pedestrians are allowed here, but this might be a private road or service where usage is allowed, but could be retracted one day by the owner -'destination': walking is allowed here, but only if truly necessary to reach the destination (e.g. a service road) -'private': this is a private road, only go here if the destination is here -'no': do not walk here -Created by -Originally defined in pedestrian.legal_access.json -Uses tags: access, highway, service, foot, anyways:foot, anyways:access, anyways:construction -Used parameters: -Number of combintations: 53 -Returns values: -]] -function pedestrian_legal_access(parameters, tags, result) - return default("no", first_match_of(tags, result, - {"anyways:foot", "anyways:access", "anyways:construction", "foot", "access", "service", "highway"}, - { - access = { - no = "no", - customers = "private", - private = "private", - permissive = "permissive", - destination = "destination", - delivery = "destination", - service = "destination", - permit = "destination" - }, - highway = { - pedestrian = "designated", - footway = "designated", - living_street = "designated", - steps = "yes", - corridor = "designated", - residential = "yes", - service = "yes", - services = "yes", - track = "yes", - crossing = "yes", - construction = "permissive", - path = "yes", - primary = "yes", - primary_link = "yes", - secondary = "yes", - secondary_link = "yes", - tertiary = "yes", - tertiary_link = "yes", - unclassified = "yes", - road = "yes" - }, - service = { - parking_aisle = "permissive", - driveway = "private", - alley = "yes", - bus = "no" - }, - foot = { - yes = "yes", - no = "no", - use_sidepath = "no", - designated = "designated", - permissive = "permissive", - private = "private", - official = "designated", - dismount = "dismount", - permit = "destination" - }, - ["anyways:foot"] = tags["anyways:foot"], - ["anyways:access"] = { - no = "no", - destination = "destination", - yes = "yes" - }, - ["anyways:construction"] = { - yes = "no" - } - })) -end - ---[[ -Gives 0 on private roads, 0.1 on destination-only roads, and 0.9 on permissive roads; gives 1 by default. This helps to select roads with no access retrictions on them - -Unit: -Created by -Originally defined in clean_permission_score.json -Uses tags: access -Used parameters: -Number of combintations: 5 -Returns values: -]] -function clean_permission_score(parameters, tags, result) - return default(0, head(table_to_list(tags, result, - { - access = { - private = -50, - destination = -3, - permissive = -1 - } - }))) -end - -failed_profile_tests = false ---[[ -Unit test of a behaviour function for an itinero 2.0 profile -]] - -function unit_test_profile(profile_function, profile_name, index, expected, tags) - -- Note: we don't actually use 'profile_function' - - local result = {} - local profile_failed = false - factor(tags, result) - - - local forward_access = result.forward_speed > 0 and result.forward > 0; - local backward_access = result.backward_speed > 0 and result.backward > 0; - - if (not forward_access and not backward_access) then - - if (expected.access == "no" or expected.speed <= 0 or expected.priority <= 0) then - -- All is fine, we can't access this thing anyway - return - end - - profile_failed = true - print("Test " .. tostring(index) .. " failed for " .. profile_name .. ".access: expected " .. expected.access .. " but forward and backward are 0 (for either speed or factor)") - end - - if (expected.oneway == "with") then - if (backward_access) then - -- we can go against the direction, not good - print("Test " .. tostring(index) .. " failed for " .. profile_name .. ".oneway: expected " .. expected.oneway .. " but going against the direction is possible") - profile_failed = true; - end - if (not forward_access) then - print("Test " .. tostring(index) .. " warning for " .. profile_name .. ".oneway: expected " .. expected.oneway .. " but going with the direction is not possible") - end - end - - if (expected.oneway == "against") then - if (forward_access) then - -- we can go against the direction, not good - print("Test " .. tostring(index) .. " failed for " .. profile_name .. ".oneway: expected " .. expected.oneway .. " but going with the direction is possible") - end - if (not backward_access) then - print("Test " .. tostring(index) .. " warning for " .. profile_name .. ".oneway: expected " .. expected.oneway .. " but going against the direction is not possible") - end - end - - if (result.forward_speed ~= expected.speed and result.backward_speed ~= expected.speed) then - print("Test " .. tostring(index) .. " failed for " .. profile_name .. ".speed: expected " .. expected.speed .. " but got " .. result.forward_speed .. " forward and " .. result.backward_speed .. " backward") - profile_failed = true; - end - - - if (result.forward ~= expected.priority and result.backward ~= expected.priority) then - print("Test " .. tostring(index) .. " failed for " .. profile_name .. ".priority: expected " .. expected.priority .. " but got " .. result.forward .. " forward and " .. result.backward .. " backward") - profile_failed = true; - end - - if(profile_failed) then - failed_profile_tests = true; - debug_table(tags, "tags: ") - debug_table(expected, "expected: ") - debug_table(result, "result: ") - end - -end - -function inv(n) - return 1/n -end - -function double_compare(a, b) - if (b == nil) then - return false - end - - if (type(a) ~= "number") then - a = parse(a) - end - - if(type(b) ~= "number") then - b = parse(b) - end - if (a == b) then - return true - end - - return math.abs(a - b) < 0.0001 -end - -function default(defaultValue, realValue) - if(realValue ~= nil) then - return realValue - end - return defaultValue -end - -function first_match_of(tags, result, order_of_keys, table) - for _, key in pairs(order_of_keys) do - local v = tags[key] - if (v ~= nil) then - - local mapping = table[key] - if (type(mapping) == "table") then - local resultValue = mapping[v] - if (resultValue ~= nil) then - result.attributes_to_keep[key] = v - return resultValue - end - else - result.attributes_to_keep[key] = v - return mapping - end - end - end - return nil; -end - -function eq(a, b) - if (a == b) then - return "yes" - else - return "no" - end -end - - -function const(a, b) - return a -end - -function head(ls) - if(ls == nil) then - return nil - end - for _, v in pairs(ls) do - if(v ~= nil) then - return v - end - end - return nil -end - -function table_to_list(tags, result, factor_table) - local list = {} - for key, mapping in pairs(factor_table) do - local v = tags[key] - if (v ~= nil) then - if (type(mapping) == "table") then - local f = mapping[v] - if (f ~= nil) then - table.insert(list, f); - result.attributes_to_keep[key] = v - end - else - table.insert(list, mapping); - result.attributes_to_keep[key] = v - end - end - end - - return list; -end - -function test_all() - - - - - - -- Behaviour tests -- - - - - - -end - -if (itinero == nil) then - itinero = {} - itinero.log = print - - -- Itinero is not defined -> we are running from a lua interpreter -> the tests are intended - runTests = true - - -else - print = itinero.log -end - -test_all() -if (not failed_tests and not failed_profile_tests and print ~= nil) then - print("Tests OK") -end \ No newline at end of file diff --git a/Examples/output/pedestrian.lua b/Examples/output/pedestrian.lua deleted file mode 100644 index 4e6dcdc..0000000 --- a/Examples/output/pedestrian.lua +++ /dev/null @@ -1,697 +0,0 @@ --- Itinero 1.0-profile, generated by AspectedRouting. Last source file change is 2021-01-27T15:51:22 -name = "pedestrian" -normalize = false -vehicle_type = {"vehicle", "foot"} -meta_whitelist = { -"name" - , "bridge" - , "tunnel" - , "colour" - , "ref" - , "status" - , "network" } -profile_whitelist = { - "access" - , "highway" - , "service" - , "foot" - , "anyways:foot" - , "anyways:access" - , "anyways:construction" - } - - - -profiles = { - { - name = "shortest", - function_name = "behaviour_pedestrian_shortest", - metric = "custom" - } -} - -function default_parameters() - local parameters = {} - parameters.defaultSpeed = 4 - parameters.maxspeed = 6 - parameters.timeNeeded = 0 - parameters.distance = 0 - parameters.slow_road_preference = 0 - parameters.trespassingPenalty = 1 - - return parameters -end - - - ---[[ -"The shortest route, independent of of speed" -]] -function behaviour_pedestrian_shortest(tags, result) - tags = remove_relation_prefix(tags, "shortest") - local parameters = default_parameters() - parameters.name = "pedestrian_shortest" - parameters.distance = 1 - parameters.leastSafetyPenalty = 2 - pedestrian(parameters, tags, result) -end - - - - - ---[[ -pedestrian -This is the main function called to calculate the access, oneway and speed. -Comfort is calculated as well, based on the parameters which are padded in - -Created by -Originally defined in /home/pietervdvn/git/AspectedRouting/Examples/pedestrian -]] -function pedestrian(parameters, tags, result) - - -- initialize the result table on the default values - result.access = 0 - result.speed = 0 - result.factor = 1 - result.direction = 0 - result.canstop = true - result.attributes_to_keep = {} - - local access = pedestrian_legal_access(parameters, tags, result) - if (access == nil or access == "no") then - return - end - tags.access = access - local oneway = const("both") - tags.oneway = oneway - local speed = const(parameters["defaultSpeed"]) - tags.speed = speed - local distance = 1 -- the weight per meter for distance travelled is, well, 1m/m - - local priority = 0 - - if(parameters["timeNeeded"] ~= 0) then - priority = priority + parameters["timeNeeded"] * speed - end - if(parameters["distance"] ~= 0) then - priority = priority + parameters["distance"] * distance - end - if(parameters["trespassingPenalty"] ~= 0) then - priority = priority + parameters["trespassingPenalty"] * clean_permission_score(parameters, tags, result) - end - - if (priority <= 0) then - result.access = 0 - return - end - - result.access = 1 - result.speed = speed - result.factor = 1 / priority - - result.direction = 0 - if (oneway == "with" or oneway == "yes") then - result.direction = 1 - elseif (oneway == "against" or oneway == "-1") then - result.direction = 2 - 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(relation_tags, result) - local parameters = {} - local subresult = {} - local matched = false - result.attributes_to_keep = {} - - -- Legacy to add colours to the bike networks - legacy_relation_preprocessor(relation_tags, result) -end - - ----------------------- ASPECTS ---------------------- - - ---[[ -Gives, for each type of highway, whether or not someone can enter legally. -Note 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 pedestrian -'yes': pedestrians are allowed here -'permissive': pedestrians are allowed here, but this might be a private road or service where usage is allowed, but could be retracted one day by the owner -'destination': walking is allowed here, but only if truly necessary to reach the destination (e.g. a service road) -'private': this is a private road, only go here if the destination is here -'no': do not walk here -Created by -Originally defined in pedestrian.legal_access.json -Uses tags: access, highway, service, foot, anyways:foot, anyways:access, anyways:construction -Used parameters: -Number of combintations: 53 -Returns values: -]] -function pedestrian_legal_access(parameters, tags, result) - return default("no", first_match_of(tags, result, - {"anyways:foot", "anyways:access", "anyways:construction", "foot", "access", "service", "highway"}, - { - access = { - no = "no", - customers = "private", - private = "private", - permissive = "permissive", - destination = "destination", - delivery = "destination", - service = "destination", - permit = "destination" - }, - highway = { - pedestrian = "designated", - footway = "designated", - living_street = "designated", - steps = "yes", - corridor = "designated", - residential = "yes", - service = "yes", - services = "yes", - track = "yes", - crossing = "yes", - construction = "permissive", - path = "yes", - primary = "yes", - primary_link = "yes", - secondary = "yes", - secondary_link = "yes", - tertiary = "yes", - tertiary_link = "yes", - unclassified = "yes", - road = "yes" - }, - service = { - parking_aisle = "permissive", - driveway = "private", - alley = "yes", - bus = "no" - }, - foot = { - yes = "yes", - no = "no", - use_sidepath = "no", - designated = "designated", - permissive = "permissive", - private = "private", - official = "designated", - dismount = "dismount", - permit = "destination" - }, - ["anyways:foot"] = tags["anyways:foot"], - ["anyways:access"] = { - no = "no", - destination = "destination", - yes = "yes" - }, - ["anyways:construction"] = { - yes = "no" - } - })) -end ---[[ -Gives 0 on private roads, 0.1 on destination-only roads, and 0.9 on permissive roads; gives 1 by default. This helps to select roads with no access retrictions on them - -Unit: -Created by -Originally defined in clean_permission_score.json -Uses tags: access -Used parameters: -Number of combintations: 5 -Returns values: -]] -function clean_permission_score(parameters, tags, result) - return default(0, head(table_to_list(tags, result, - { - access = { - private = -50, - destination = -3, - permissive = -1 - } - }))) -end ---[[ -Actual speed of this function - -Unit: NA -Created by NA -Originally defined in NA -Uses tags: -Used parameters: -Number of combintations: 1 -Returns values: -]] -function speed(parameters, tags, result) - return 15 -end ---[[ -The distance travelled of this profile - -Unit: -Created by -Originally defined in bicycle.json -Uses tags: -Used parameters: -Number of combintations: 1 -Returns values: -]] -function distance(parameters, tags, result) - return 1 -end - - ----------------------- UTILS ------------------------ - - --- instruction generators -instruction_generators = { - { - applies_to = "", -- applies to all profiles when empty - generators = { - { - name = "start", - function_name = "get_start" - }, - { - name = "stop", - function_name = "get_stop" - }, - { - name = "roundabout", - function_name = "get_roundabout" - }, - { - name = "turn", - function_name = "get_turn" - } - } - } -} - --- gets the first instruction -function get_start(route_position, language_reference, instruction) - if route_position.is_first() then - local direction = route_position.direction() - instruction.text = itinero.format(language_reference.get("Start {0}."), language_reference.get(direction)); - instruction.shape = route_position.shape - return 1 - end - return 0 -end - --- gets the last instruction -function get_stop(route_position, language_reference, instruction) - if route_position.is_last() then - instruction.text = language_reference.get("Arrived at destination."); - instruction.shape = route_position.shape - return 1 - end - return 0 -end - - --- gets a roundabout instruction -function get_roundabout(route_position, language_reference, instruction) - if route_position.attributes.junction == "roundabout" and - (not route_position.is_last()) then - local attributes = route_position.next().attributes - if attributes.junction then - else - local exit = 1 - local count = 1 - local previous = route_position.previous() - while previous and previous.attributes.junction == "roundabout" do - local branches = previous.branches - if branches then - branches = branches.get_traversable() - if branches.count > 0 then - exit = exit + 1 - end - end - count = count + 1 - previous = previous.previous() - end - - instruction.text = itinero.format(language_reference.get("Take the {0}th exit at the next roundabout."), "" .. exit) - if exit == 1 then - instruction.text = itinero.format(language_reference.get("Take the first exit at the next roundabout.")) - elseif exit == 2 then - instruction.text = itinero.format(language_reference.get("Take the second exit at the next roundabout.")) - elseif exit == 3 then - instruction.text = itinero.format(language_reference.get("Take the third exit at the next roundabout.")) - end - instruction.type = "roundabout" - instruction.shape = route_position.shape - return count - end - end - return 0 -end - --- gets a turn -function get_turn(route_position, language_reference, instruction) - local relative_direction = route_position.relative_direction().direction - - local turn_relevant = false - local branches = route_position.branches - if branches then - branches = branches.get_traversable() - if relative_direction == "straighton" and - branches.count >= 2 then - turn_relevant = true -- straight on at cross road - end - if relative_direction ~= "straighton" and - branches.count > 0 then - turn_relevant = true -- an actual normal turn - end - end - - if relative_direction == "unknown" then - turn_relevant = false -- turn could not be calculated. - end - - if turn_relevant then - local next = route_position.next() - local name = nil - if next then - name = next.attributes.name - end - if name then - instruction.text = itinero.format(language_reference.get("Go {0} on {1}."), - language_reference.get(relative_direction), name) - instruction.shape = route_position.shape - else - instruction.text = itinero.format(language_reference.get("Go {0}."), - language_reference.get(relative_direction)) - instruction.shape = route_position.shape - end - - return 1 - end - return 0 -end - ---[[ - Legacy function to add cycle_colour -]] - -function legacy_relation_preprocessor(attributes, result) - if (attributes.route == "bicycle") then - -- This is a cycling network, the colour is copied - 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 - - if (attributes.ref ~= nil and attributes.operator == "Stad Genk") then - -- This is pure legacy: we need the ref number only of stad Genk - result.attributes_to_keep.cycle_network_ref = attributes.ref - end - end -end - -function string.start(strt, s) - return string.sub(s, 1, string.len(strt)) == strt -end - - --- every key starting with "_relation::XXX" is rewritten to "_relation:XXX" -function remove_relation_prefix(tags, name) - - local new_tags = {} - for k, v in pairs(tags) do - local prefix = "_relation:" .. name .. ":"; - if (string.start(prefix, k)) then - local new_key = "_relation:" .. string.sub(k, string.len(prefix) + 1) -- plus 1: sub uses one-based indexing to select the start - new_tags[new_key] = v - else - new_tags[k] = v - end - end - return new_tags -end -function default(defaultValue, realValue) - if(realValue ~= nil) then - return realValue - end - return defaultValue -end -function first_match_of(tags, result, order_of_keys, table) - for _, key in pairs(order_of_keys) do - local v = tags[key] - if (v ~= nil) then - - local mapping = table[key] - if (type(mapping) == "table") then - local resultValue = mapping[v] - if (resultValue ~= nil) then - result.attributes_to_keep[key] = v - return resultValue - end - else - result.attributes_to_keep[key] = v - return mapping - end - end - end - return nil; -end -function const(a, b) - return a -end -function head(ls) - if(ls == nil) then - return nil - end - for _, v in pairs(ls) do - if(v ~= nil) then - return v - end - end - return nil -end -function table_to_list(tags, result, factor_table) - local list = {} - for key, mapping in pairs(factor_table) do - local v = tags[key] - if (v ~= nil) then - if (type(mapping) == "table") then - local f = mapping[v] - if (f ~= nil) then - table.insert(list, f); - result.attributes_to_keep[key] = v - end - else - table.insert(list, mapping); - result.attributes_to_keep[key] = v - end - end - end - - return list; -end -failed_tests = false -function unit_test(f, fname, index, expected, parameters, tags) - if (f == nil) then - print("Trying to unit test " .. fname .. " but this function is not defined") - failed_tests = true - return - end - local result = {attributes_to_keep = {}} - local actual = f(parameters, tags, result) - if(expected == "null" and actual == nil) then - -- OK! - elseif(tonumber(actual) and tonumber(expected) and math.abs(tonumber(actual) - tonumber(expected)) < 0.1) then - -- OK! - elseif (tostring(actual) ~= expected) then - print("[" .. fname .. "] " .. index .. " failed: expected " .. expected .. " but got " .. tostring(actual)) - failed_tests = true - end -end -failed_profile_tests = false ---[[ -expected should be a table containing 'access', 'speed' and 'priority' -]] -function unit_test_profile(profile_function, profile_name, index, expected, tags) - local result = { attributes_to_keep = {} } - local profile_failed = false - profile_function(tags, result) - - local accessCorrect = (result.access == 0 and (expected.access == "no" or expected.priority <= 0)) or result.access == 1 - if (not accessCorrect) then - print("Test " .. tostring(index) .. " failed for " .. profile_name .. ".access: expected " .. expected.access .. " but got " .. result.access) - profile_failed = true - failed_profile_tests = true - end - - if (expected.access == "no" or expected.priority <= 0) then - -- we cannot access this road, the other results are irrelevant - if (profile_failed) then - print("The used tags for test " .. tostring(index) .. " are:") - debug_table(tags) - end - return - end - - if (not double_compare(result.speed, expected.speed)) then - print("Test " .. tostring(index) .. " failed for " .. profile_name .. ".speed: expected " .. expected.speed .. " but got " .. result.speed) - failed_profile_tests = true - profile_failed = true - end - - - local actualOneway = result.direction - - if(actualOneway == nil) then - print("Fail: result.direction is nil") - profile_failed = true; - end - - if (result.direction == 0) then - actualOneway = "both" - elseif (result.direction == 1) then - actualOneway = "with" - 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) - failed_profile_tests = true - profile_failed = true - end - - - if (not double_compare(result.factor, 1/expected.priority)) then - print("Test " .. tostring(index) .. " failed for " .. profile_name .. ".factor: expected " .. expected.priority .. " but got " .. 1/result.factor) - failed_profile_tests = true - profile_failed = true - end - - if (profile_failed == true) then - print("The used tags for test " .. tostring(index) .. " are:") - debug_table(tags) - end -end -function inv(n) - return 1/n -end -function double_compare(a, b) - if (b == nil) then - return false - end - - if (type(a) ~= "number") then - a = parse(a) - end - - if(type(b) ~= "number") then - b = parse(b) - end - if (a == b) then - return true - end - - return math.abs(a - b) < 0.0001 -end -function debug_table(table, prefix) - if (prefix == nil) then - prefix = "" - end - for k, v in pairs(table) do - - if (type(v) == "table") then - debug_table(v, " ") - else - print(prefix .. tostring(k) .. " = " .. tostring(v)) - 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 - - ------------------------ TESTS ------------------------ - - -function test_all() - - - - - unit_test(clean_permission_score, "clean_permission_score", 0, "0", {}, {access = "yes"}) - unit_test(clean_permission_score, "clean_permission_score", 1, "-1", {}, {access = "permissive"}) - unit_test(clean_permission_score, "clean_permission_score", 2, "-50", {}, {access = "private"}) - unit_test(clean_permission_score, "clean_permission_score", 3, "0", {}, {access = "qmsldkfjqsmldkfj"}) - - - - unit_test(pedestrian_legal_access, "pedestrian.legal_access", 0, "no", {}, {}) - unit_test(pedestrian_legal_access, "pedestrian.legal_access", 1, "yes", {}, {highway = "residential"}) - unit_test(pedestrian_legal_access, "pedestrian.legal_access", 2, "designated", {}, {highway = "pedestrian"}) - - - - - - - - - - - - - -- Behaviour tests -- - - - - - -end - - - -if (itinero == nil) then - itinero = {} - itinero.log = print - - -- Itinero is not defined -> we are running from a lua interpreter -> the tests are intended - runTests = true - - -else - print = itinero.log -end - -test_all() -if (not failed_tests and not failed_profile_tests) then - print("Tests OK") -end \ No newline at end of file