From 279f8aabfb67bda62b51cc32542ea18a812e4823 Mon Sep 17 00:00:00 2001 From: Patrick Niklaus Date: Thu, 12 May 2016 18:50:10 +0200 Subject: [PATCH] Allow specifing a weight for routing that is independent of duration --- CHANGELOG.md | 7 + features/car/traffic_speeds.feature | 127 --------- features/options/contract/invalid.feature | 2 +- features/options/profiles/version0.feature | 9 +- features/options/profiles/version1.feature | 66 +++++ features/support/route.js | 22 +- features/support/shared_steps.js | 11 +- features/testbot/matching.feature | 7 +- features/testbot/side_bias.feature | 22 +- features/testbot/traffic_speeds.feature | 132 +++++++++ .../testbot/traffic_turn_penalties.feature | 32 ++- features/testbot/weight.feature | 262 ++++++++++++++++++ include/contractor/contractor.hpp | 4 +- include/contractor/contractor_config.hpp | 8 +- include/contractor/contractor_graph.hpp | 4 +- include/contractor/contractor_heap.hpp | 7 +- include/contractor/graph_contractor.hpp | 6 +- .../contractor/graph_contractor_adaptors.hpp | 4 +- include/contractor/query_edge.hpp | 2 +- include/engine/api/json_factory.hpp | 3 +- include/engine/api/route_api.hpp | 9 +- .../contiguous_internalmem_datafacade.hpp | 110 ++++++++ include/engine/datafacade/datafacade_base.hpp | 14 +- include/engine/geospatial_query.hpp | 22 +- include/engine/guidance/assemble_geometry.hpp | 11 +- include/engine/guidance/assemble_leg.hpp | 27 +- include/engine/guidance/assemble_steps.hpp | 29 +- include/engine/guidance/leg_geometry.hpp | 5 +- include/engine/guidance/route_step.hpp | 8 +- include/engine/hint.hpp | 4 +- include/engine/internal_route_result.hpp | 2 + include/engine/phantom_node.hpp | 57 ++-- .../routing_algorithms/alternative_path.hpp | 16 +- .../routing_algorithms/many_to_many.hpp | 6 +- .../routing_algorithms/routing_base.hpp | 31 ++- include/engine/search_engine_data.hpp | 4 +- .../extractor/compressed_edge_container.hpp | 17 +- include/extractor/edge_based_edge.hpp | 8 +- .../extractor/edge_based_graph_factory.hpp | 26 +- include/extractor/extraction_segment.hpp | 32 +++ include/extractor/extraction_turn.hpp | 34 +++ include/extractor/extraction_way.hpp | 10 + include/extractor/extractor_callbacks.hpp | 5 +- include/extractor/extractor_config.hpp | 8 +- include/extractor/internal_extractor_edge.hpp | 80 +++--- include/extractor/node_based_edge.hpp | 38 ++- include/extractor/profile_properties.hpp | 39 ++- include/extractor/scripting_environment.hpp | 10 +- .../extractor/scripting_environment_lua.hpp | 10 +- include/extractor/travel_mode.hpp | 4 +- include/storage/shared_datatype.hpp | 13 +- include/storage/storage_config.hpp | 2 + include/util/fingerprint.hpp | 4 +- include/util/node_based_graph.hpp | 20 +- include/util/typedefs.hpp | 2 + profiles/bicycle.lua | 60 ++-- profiles/car.lua | 56 ++-- profiles/examples/postgis.lua | 1 - profiles/foot.lua | 54 ++-- profiles/rasterbot.lua | 25 +- profiles/rasterbotinterp.lua | 23 +- profiles/testbot.lua | 18 +- profiles/turnbot.lua | 7 +- src/contractor/contractor.cpp | 252 +++++++++++------ src/contractor/contractor_dijkstra.cpp | 6 +- src/engine/api/json_factory.cpp | 5 +- .../routing_algorithms/alternative_path.cpp | 10 +- .../routing_algorithms/many_to_many.cpp | 8 +- .../routing_algorithms/routing_base.cpp | 54 ++-- src/extractor/compressed_edge_container.cpp | 30 +- src/extractor/edge_based_graph_factory.cpp | 128 ++++++--- src/extractor/extraction_containers.cpp | 86 +++--- src/extractor/extractor.cpp | 11 +- src/extractor/extractor_callbacks.cpp | 257 ++++++++--------- src/extractor/graph_compressor.cpp | 44 ++- src/extractor/guidance/turn_discovery.cpp | 4 +- src/extractor/scripting_environment_lua.cpp | 211 ++++++++++++-- src/storage/storage.cpp | 52 ++++ src/storage/storage_config.cpp | 6 +- unit_tests/engine/guidance_assembly.cpp | 4 +- .../extractor/compressed_edge_container.cpp | 12 +- unit_tests/extractor/graph_compressor.cpp | 1 + unit_tests/library/route.cpp | 3 + unit_tests/mocks/mock_datafacade.hpp | 25 +- unit_tests/server/parameters_parser.cpp | 46 +-- 85 files changed, 2100 insertions(+), 853 deletions(-) delete mode 100644 features/car/traffic_speeds.feature create mode 100644 features/options/profiles/version1.feature create mode 100644 features/testbot/traffic_speeds.feature create mode 100644 features/testbot/weight.feature create mode 100644 include/extractor/extraction_segment.hpp create mode 100644 include/extractor/extraction_turn.hpp diff --git a/CHANGELOG.md b/CHANGELOG.md index 75413af8c..c0f1d042a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# 6.0.0 + - Profiles: + - `turn_function` now does not return an integer but takes in a `turn` object and modifies it + - `uturn_penalty` is deprecated set it over the `turn_function` + - traffic light penalties now need to be set over the node function, `traffic_light_penalty` is deprecated: + result.weight_penalty and result.duration_penalty + # 5.6.0 - Changes from 5.5 - Bugfixes diff --git a/features/car/traffic_speeds.feature b/features/car/traffic_speeds.feature deleted file mode 100644 index 24e2e7f59..000000000 --- a/features/car/traffic_speeds.feature +++ /dev/null @@ -1,127 +0,0 @@ -@routing @speed @traffic -Feature: Traffic - speeds - - Background: Use specific speeds - - Scenario: Weighting based on speed file - Given the node locations - | node | lat | lon | - | a | 0.1 | 0.1 | - | b | 0.05 | 0.1 | - | c | 0.0 | 0.1 | - | d | 0.05 | 0.03 | - | e | 0.05 | 0.066 | - | f | 0.075 | 0.066 | - | g | 0.075 | 0.1 | - And the ways - | nodes | highway | - | ab | primary | - | ad | primary | - | bc | primary | - | dc | primary | - | de | primary | - | eb | primary | - | df | primary | - | fb | primary | - Given the profile "testbot" - Given the extract extra arguments "--generate-edge-lookup" - Given the contract extra arguments "--segment-speed-file {speeds_file}" - Given the speed file - """ - 1,2,0 - 2,1,0 - 2,3,27 - 3,2,27 - 1,4,27 - 4,1,27 - """ - And I route I should get - | from | to | route | speed | - | a | b | ad,de,eb,eb | 30 km/h | - | a | c | ad,dc,dc | 31 km/h | - | b | c | bc,bc | 27 km/h | - | a | d | ad,ad | 27 km/h | - | d | c | dc,dc | 36 km/h | - | g | b | fb,fb | 36 km/h | - | a | g | ad,df,fb,fb | 30 km/h | - - - Scenario: Speeds that isolate a single node (a) - Given the node locations - | node | lat | lon | - | a | 0.1 | 0.1 | - | b | 0.05 | 0.1 | - | c | 0.0 | 0.1 | - | d | 0.05 | 0.03 | - | e | 0.05 | 0.066 | - | f | 0.075 | 0.066 | - | g | 0.075 | 0.1 | - | h | 2.075 | 19.1 | - And the ways - | nodes | highway | - | ab | primary | - | ad | primary | - | bc | primary | - | dc | primary | - | de | primary | - | eb | primary | - | df | primary | - | fb | primary | - Given the profile "testbot" - Given the extract extra arguments "--generate-edge-lookup" - Given the contract extra arguments "--segment-speed-file {speeds_file}" - Given the speed file - """ - 1,2,0 - 2,1,0 - 2,3,27 - 3,2,27 - 1,4,0 - 4,1,0 - """ - And I route I should get - | from | to | route | speed | - | a | b | fb,fb | 36 km/h | - | a | c | fb,bc,bc | 30 km/h | - | b | c | bc,bc | 27 km/h | - | a | d | fb,df,df | 36 km/h | - | d | c | dc,dc | 36 km/h | - | g | b | fb,fb | 36 km/h | - | a | g | fb,fb | 36 km/h | - - Scenario: Verify that negative values cause an error, they're not valid at all - Given the node locations - | node | lat | lon | - | a | 0.1 | 0.1 | - | b | 0.05 | 0.1 | - | c | 0.0 | 0.1 | - | d | 0.05 | 0.03 | - | e | 0.05 | 0.066 | - | f | 0.075 | 0.066 | - | g | 0.075 | 0.1 | - | h | 1.075 | 10.1 | - And the ways - | nodes | highway | - | ab | primary | - | ad | primary | - | bc | primary | - | dc | primary | - | de | primary | - | eb | primary | - | df | primary | - | fb | primary | - Given the profile "testbot" - Given the extract extra arguments "--generate-edge-lookup" - Given the speed file - """ - 1,2,-10 - 2,1,-20 - 2,3,27 - 3,2,27 - 1,4,-3 - 4,1,-5 - """ - And the data has been extracted - When I try to run "osrm-contract --segment-speed-file {speeds_file} {processed_file}" - And stderr should contain "malformed" - And it should exit with an error diff --git a/features/options/contract/invalid.feature b/features/options/contract/invalid.feature index b4413f111..15da09596 100644 --- a/features/options/contract/invalid.feature +++ b/features/options/contract/invalid.feature @@ -24,5 +24,5 @@ Feature: osrm-contract command line options: invalid options Scenario: osrm-contract - Someone forgot --generate-edge-lookup on osrm-extract When I try to run "osrm-contract --segment-speed-file /dev/null {processed_file}" Then stderr should contain "Error while trying to mmap" - Then stderr should contain ".osrm.edge_penalties" + Then stderr should contain ".osrm.turn_penalties_index" And it should exit with an error diff --git a/features/options/profiles/version0.feature b/features/options/profiles/version0.feature index 5e173a338..fe47d8b08 100644 --- a/features/options/profiles/version0.feature +++ b/features/options/profiles/version0.feature @@ -27,7 +27,7 @@ end Scenario: Out-bound API version Given the profile file """ -api_version = 1 +api_version = 2 """ And the node map """ @@ -55,6 +55,7 @@ properties.max_speed_for_map_matching = 180/3.6 properties.use_turn_restrictions = true properties.continue_straight_at_waypoint = true properties.left_hand_driving = false +properties.weight_name = 'duration' function node_function (node, result) print ('node_function ' .. node:id()) @@ -71,7 +72,7 @@ end function turn_function (angle) print('turn_function ' .. angle) - return angle == 0 and 0 or 42 + return angle == 0 and 0 or 17 end function segment_function (source, target, distance, weight) @@ -101,6 +102,6 @@ end When I route I should get | from | to | route | time | - | a | b | ac,cb,cb | 19.2s | - | a | d | ac,cd,cd | 19.2s | + | a | b | ac,cb,cb | 16.7s | + | a | d | ac,cd,cd | 16.7s | | a | e | ac,ce,ce | 20s | diff --git a/features/options/profiles/version1.feature b/features/options/profiles/version1.feature new file mode 100644 index 000000000..592e8d4fe --- /dev/null +++ b/features/options/profiles/version1.feature @@ -0,0 +1,66 @@ +Feature: Profile API version 1 + + Background: + Given a grid size of 100 meters + + Scenario: Basic profile function calls and property values + Given the profile file + """ +api_version = 1 + +-- set profile properties +properties.max_speed_for_map_matching = 180/3.6 +properties.use_turn_restrictions = true +properties.continue_straight_at_waypoint = true +properties.weight_name = 'test_version1' + +function node_function (node, result) + print ('node_function ' .. node:id()) +end + +function way_function(way, result) + result.name = way:get_value_by_key('name') + result.weight = 10 + result.forward_mode = mode.driving + result.backward_mode = mode.driving + result.forward_speed = 36 + result.backward_speed = 36 + print ('way_function ' .. way:id() .. ' ' .. result.name) +end + +function turn_function (turn) + print('turn_function', turn.angle, turn.turn_type, turn.direction_modifier, turn.has_traffic_light) + turn.weight = turn.angle == 0 and 0 or 4.2 + turn.duration = turn.weight +end + +function segment_function (segment) + print ('segment_function ' .. segment.source.lon .. ' ' .. segment.source.lat) +end + """ + And the node map + """ + a + bcd + e + """ + And the ways + | nodes | + | ac | + | cb | + | cd | + | ce | + And the data has been saved to disk + + When I run "osrm-extract --profile {profile_file} {osm_file}" + Then it should exit successfully + And stdout should contain "node_function" + And stdout should contain "way_function" + And stdout should contain "turn_function" + And stdout should contain "segment_function" + + When I route I should get + | from | to | route | time | + | a | b | ac,cb,cb | 19.2s | + | a | d | ac,cd,cd | 19.2s | + | a | e | ac,ce,ce | 20s | diff --git a/features/support/route.js b/features/support/route.js index a76b1618f..34f0ff3de 100644 --- a/features/support/route.js +++ b/features/support/route.js @@ -164,15 +164,21 @@ module.exports = function () { }; this.annotationList = (instructions) => { - function zip(list_1, list_2, list_3) + if (!('annotation' in instructions.legs[0])) + return ''; + + function zip(list_1, list_2, list_3, list_4) { let tuples = []; - for (let i = 0; i < list_1.length; ++i) { - tuples.push([list_1[i], list_2[i], list_3[i]]); + for (let i = 0; i < list_1.length; ++i) { + tuples.push([list_1[i], list_2[i], list_3[i], list_4[i]]); } return tuples; } - return instructions.legs.map(l => {return zip(l.annotation.duration, l.annotation.distance, l.annotation.datasources).map(p => { return p.join(':'); }).join(','); }).join(','); + return instructions.legs.map(l => { + const values = zip( l.annotation.weight, l.annotation.duration, l.annotation.distance, l.annotation.datasources); + return values.map(p => { return p.join(':'); }).join(','); + }).join(','); }; this.OSMIDList = (instructions) => { @@ -250,4 +256,12 @@ module.exports = function () { this.distanceList = (instructions) => { return this.extractInstructionList(instructions, s => s.distance + 'm'); }; + + this.weightName = (instructions) => { + return instructions ? instructions.weight_name : ''; + }; + + this.weightList = (instructions) => { + return this.extractInstructionList(instructions, s => s.weight); + }; }; diff --git a/features/support/shared_steps.js b/features/support/shared_steps.js index 6d04ce728..3f6683695 100644 --- a/features/support/shared_steps.js +++ b/features/support/shared_steps.js @@ -35,7 +35,7 @@ module.exports = function () { if (err) return cb(err); if (body && body.length) { let destinations, pronunciations, instructions, refs, bearings, turns, modes, times, - distances, summary, intersections, lanes, locations; + distances, summary, intersections, lanes, locations, annotation, weight_name, weights; let json = JSON.parse(body); @@ -55,6 +55,9 @@ module.exports = function () { lanes = this.lanesList(json.routes[0]); summary = this.summary(json.routes[0]); locations = this.locations(json.routes[0]); + annotation = this.annotationList(json.routes[0]); + weight_name = this.weightName(json.routes[0]); + weights = this.weightList(json.routes[0]); } if (headers.has('status')) { @@ -130,6 +133,10 @@ module.exports = function () { got.locations = (locations || '').trim(); } + if (headers.has('annotation')){ + got.annotation = (annotation || '').trim(); + } + var putValue = (key, value) => { if (headers.has(key)) got[key] = instructions ? value : ''; }; @@ -142,6 +149,8 @@ module.exports = function () { putValue('distances', distances); putValue('pronunciations', pronunciations); putValue('destinations', destinations); + putValue('weight_name', weight_name); + putValue('weights', weights); } for (var key in row) { diff --git a/features/testbot/matching.feature b/features/testbot/matching.feature index 56a6fe2fc..c7985032f 100644 --- a/features/testbot/matching.feature +++ b/features/testbot/matching.feature @@ -141,9 +141,9 @@ Feature: Basic Map Matching And the contract extra arguments "--segment-speed-file {speeds_file}" When I match I should get - | trace | matchings | annotation | - | abeh | abeh | 1:10.008842:1,0:0:0,1:10.008842:0,1:10.008842:0,1:10.008842:0,0:0:0,2:19.906475:0,1:10.008842:0 | - | abci | abci | 1:10.008842:1,0:0:0,1:10.008842:0,0:0:0,1:10.010367:0 | + | trace | matchings | annotation | + | abeh | abeh | 1:1:10.008842:1,0:0:0:0,1:1:10.008842:0,1:1:10.008842:0,1:1:10.008842:0,0:0:0:0,2:2:19.906475:0,1:1:10.008842:0 | + | abci | abci | 1:1:10.008842:1,0:0:0:0,1:1:10.008842:0,0:0:0:0,1:1:10.010367:0 | # The following is the same as the above, but separated for readability (line length) When I match I should get @@ -337,4 +337,3 @@ Feature: Basic Map Matching | trace | OSM IDs | | 12 | 1,2,3,4,5,6 | | 21 | 6,5,4,3,2,1 | - diff --git a/features/testbot/side_bias.feature b/features/testbot/side_bias.feature index d892eb057..8bfe541e5 100644 --- a/features/testbot/side_bias.feature +++ b/features/testbot/side_bias.feature @@ -9,13 +9,10 @@ Feature: Testbot - side bias """ Scenario: Left hand bias - Given the profile file "testbot" extended with + Given the profile file "car" extended with """ properties.left_hand_driving = true - function turn_function (angle) - local k = 10 * angle * angle * 50 / (90.0 * 90.0) - return (angle >= 0) and k * 1.2 or k / 1.2 - end + turn_bias = properties.left_hand_driving and 1/1.075 or 1.075 """ Given the node map """ @@ -31,17 +28,14 @@ Feature: Testbot - side bias When I route I should get | from | to | route | time | - | d | a | bd,ab,ab | 82s +-1 | - | d | c | bd,bc,bc | 100s +-1 | + | d | a | bd,ab,ab | 29s +-1 | + | d | c | bd,bc,bc | 33s +-1 | Scenario: Right hand bias - Given the profile file "testbot" extended with + Given the profile file "car" extended with """ properties.left_hand_driving = false - function turn_function (angle) - local k = 10 * angle * angle * 50 / (90.0 * 90.0) - return (angle >= 0) and k / 1.2 or k * 1.2 - end + turn_bias = properties.left_hand_driving and 1/1.075 or 1.075 """ And the node map """ @@ -57,8 +51,8 @@ Feature: Testbot - side bias When I route I should get | from | to | route | time | - | d | a | bd,ab,ab | 100s +-1 | - | d | c | bd,bc,bc | 82s +-1 | + | d | a | bd,ab,ab | 33s +-1 | + | d | c | bd,bc,bc | 29s +-1 | Scenario: Roundabout exit counting for left sided driving And a grid size of 10 meters diff --git a/features/testbot/traffic_speeds.feature b/features/testbot/traffic_speeds.feature new file mode 100644 index 000000000..8754fba2f --- /dev/null +++ b/features/testbot/traffic_speeds.feature @@ -0,0 +1,132 @@ +@routing @speed @traffic +Feature: Traffic - speeds + + Background: Use specific speeds + Given the node locations + | node | lat | lon | + | a | 0.1 | 0.1 | + | b | 0.05 | 0.1 | + | c | 0.0 | 0.1 | + | d | 0.05 | 0.03 | + | e | 0.05 | 0.066 | + | f | 0.075 | 0.066 | + | g | 0.075 | 0.1 | + And the ways + | nodes | highway | + | ab | primary | + | ad | primary | + | bc | primary | + | dc | primary | + | de | primary | + | eb | primary | + | df | primary | + | fb | primary | + And the profile "testbot" + And the extract extra arguments "--generate-edge-lookup" + + + Scenario: Weighting based on speed file + Given the contract extra arguments "--segment-speed-file {speeds_file}" + Given the speed file + """ + 1,2,0 + 2,1,0 + 2,3,27 + 3,2,27 + 1,4,27 + 4,1,27 + """ + And I route I should get + | from | to | route | speed | weights | + | a | b | ad,de,eb,eb | 30 km/h | 1275.7,400.4,378.2,0 | + | a | c | ad,dc,dc | 31 km/h | 1275.7,956.8,0 | + | b | c | bc,bc | 27 km/h | 741.5,0 | + | a | d | ad,ad | 27 km/h | 1275.7,0 | + | d | c | dc,dc | 36 km/h | 956.8,0 | + | g | b | fb,fb | 36 km/h | 164.7,0 | + | a | g | ad,df,fb,fb | 30 km/h | 1275.7,487.5,304.7,0 | + + + Scenario: Weighting based on speed file weights, ETA based on file durations + Given the contract extra arguments "--segment-speed-file {speeds_file}" + Given the speed file + """ + 1,2,1,200207 + 2,1,1,200207 + 2,3,27,7415 + 3,2,27,7415 + 1,4,27,12757 + 4,1,27,12757 + """ + And I route I should get + | from | to | route | speed | weights | + | a | b | ad,de,eb,eb | 30 km/h | 1275.7,400.4,378.2,0 | + | a | c | ad,dc,dc | 31 km/h | 1275.7,956.8,0 | + | b | c | bc,bc | 27 km/h | 741.5,0 | + | a | d | ad,ad | 27 km/h | 1275.7,0 | + | d | c | dc,dc | 36 km/h | 956.8,0 | + | g | b | ab,ab | 1 km/h | 10010.4,0 | + | a | g | ab,ab | 1 km/h | 10010.3,0 | + + + Scenario: Weighting based on speed file weights, ETA based on file durations + Given the contract extra arguments "--segment-speed-file {speeds_file}" + Given the speed file + """ + 1,2,1,200207 + 2,1,1,200207 + 2,3,27,7415 + 3,2,27,7415 + 1,4,1,344450 + 4,1,1,344450 + """ + And I route I should get + | from | to | route | speed | weights | + | a | b | ab,ab | 1 km/h | 20020.7,0 | + | a | c | ab,bc,bc | 2 km/h | 20020.7,741.5,0 | + | b | c | bc,bc | 27 km/h | 741.5,0 | + | a | d | ab,eb,de,de | 2 km/h | 20020.7,378.2,400.4,0 | + | d | c | dc,dc | 36 km/h | 956.8,0 | + | g | b | ab,ab | 1 km/h | 10010.4,0 | + | a | g | ab,ab | 1 km/h | 10010.3,0 | + + + Scenario: Speeds that isolate a single node (a) + Given the contract extra arguments "--segment-speed-file {speeds_file}" + And the node locations + | node | lat | lon | + | h | 2.075 | 19.1 | + Given the speed file + """ + 1,2,0 + 2,1,0 + 2,3,27 + 3,2,27 + 1,4,0 + 4,1,0 + """ + And I route I should get + | from | to | route | speed | weights | + | a | b | fb,fb | 36 km/h | 329.4,0 | + | a | c | fb,bc,bc | 30 km/h | 329.4,741.5,0 | + | b | c | bc,bc | 27 km/h | 741.5,0 | + | a | d | fb,df,df | 36 km/h | 140,487.5,0 | + | d | c | dc,dc | 36 km/h | 956.8,0 | + | g | b | fb,fb | 36 km/h | 164.7,0 | + | a | g | fb,fb | 36 km/h | 164.7,0 | + + + Scenario: Verify that negative values cause an error, they're not valid at all + Given the speed file + """ + 1,2,-10 + 2,1,-20 + 2,3,27 + 3,2,27 + 1,4,-3 + 4,1,-5 + """ + And the data has been extracted + When I try to run "osrm-contract --segment-speed-file {speeds_file} {processed_file}" + And stderr should contain "malformed" + And it should exit with an error diff --git a/features/testbot/traffic_turn_penalties.feature b/features/testbot/traffic_turn_penalties.feature index 81b65ea39..a86d5323e 100644 --- a/features/testbot/traffic_turn_penalties.feature +++ b/features/testbot/traffic_turn_penalties.feature @@ -37,14 +37,30 @@ Feature: Traffic - turn penalties applied to turn onto which a phantom node snap Given the turn penalty file """ 1,2,5,0,comment - 3,4,7,-20 + 3,4,7,-30 """ And the contract extra arguments "--turn-penalty-file {penalties_file}" When I route I should get - | from | to | route | speed | time | - | a | e | ab,be,be | 36 km/h | 40s +-1 | - | 1 | e | ab,be,be | 36 km/h | 30s +-1 | - | b | f | bc,cf,cf | 36 km/h | 40s +-1 | - | 2 | f | bc,cf,cf | 36 km/h | 30s +-1 | - | c | g | cd,dg,dg | 71 km/h | 20s +-1 | - | 3 | g | cd,dg,dg | 54 km/h | 20s +-1 | + | from | to | route | speed | time | + | a | e | ab,be,be | 36 km/h | 40s +-1 | + | 1 | e | ab,be,be | 36 km/h | 30s +-1 | + | b | f | bc,cf,cf | 36 km/h | 40s +-1 | + | 2 | f | bc,cf,cf | 36 km/h | 30s +-1 | + | c | g | cd,dg,dg | 144 km/h | 10s +-1 | + | 3 | g | cd,dg,dg | 54 km/h | 20s +-1 | + + Scenario: Weighting based on turn penalty file with weights + Given the turn penalty file + """ + 1,2,5,0,-3.33,comment + 3,4,7,-30,100.75 + """ + And the contract extra arguments "--turn-penalty-file {penalties_file}" + When I route I should get + | from | to | route | speed | time | weights | + | a | e | ab,be,be | 36 km/h | 40s +-1 | 16.7,20,0 | + | 1 | e | ab,be,be | 36 km/h | 30s +-1 | 6.7,20,0 | + | b | f | bc,cf,cf | 36 km/h | 40s +-1 | 20,20,0 | + | 2 | f | bc,cf,cf | 36 km/h | 30s +-1 | 10,20,0 | + | c | g | cd,dg,dg | 144 km/h | 10s +-1 | 120.8,20,0 | + | 3 | g | cd,dg,dg | 54 km/h | 20s +-1 | 110.8,20,0 | diff --git a/features/testbot/weight.feature b/features/testbot/weight.feature new file mode 100644 index 000000000..39edffaeb --- /dev/null +++ b/features/testbot/weight.feature @@ -0,0 +1,262 @@ +@testbot +Feature: Weight tests + + Background: + Given the profile "testbot" + Given a grid size of 10 meters + Given the extract extra arguments "--generate-edge-lookup" + Given the query options + | geometries | geojson | + + Scenario: Weight details + Given the query options + | annotations | true | + + Given the node map + """ + s + · + a---b---c + | + d + |··t + e + """ + + And the ways + | nodes | + | abc | + | cde | + + When I route I should get + | waypoints | route | annotation | + | s,t | abc,cde,cde | 1.1:1.1:10.008843:0,2:2:20.017686:0,2:2:20.020734:0,1:1:10.010367:0 | + + When I route I should get + | waypoints | route | times | weight_name | weights | + | s,t | abc,cde,cde | 3.1s,3s,0s | duration | 3.1,3,0 | + + # FIXME include/engine/guidance/assemble_geometry.hpp:95 + @todo + Scenario: Start and target on the same edge + Given the query options + | annotations | true | + + Given the node map + """ + a-------b + · · + s t + """ + + And the ways + | nodes | + | ab | + + When I route I should get + | waypoints | route | distances | weights | times | annotation | + | s,t | abc,abc | 20m,0m | 20,0 | 2s,0s | 29:2.9:20.017685:0 | + | t,s | abc,abc | 20m,0m | 20,0 | 2s,0s | 29:2.9:20.017685:0 | + + # FIXME include/engine/guidance/assemble_geometry.hpp:95 + @todo + Scenario: Start and target on adjacent edges + Given the query options + | annotations | true | + + Given the node map + """ + a-------b-------c + · · + s t + """ + + And the ways + | nodes | + | ab | + + When I route I should get + | waypoints | route | distances | weights | times | annotation | + | s,t | abc,abc | 30m,0m | 31,0 | 3.1s,0s | 31:3.1:30.026527:0 | + | t,s | abc,abc | 30m,0m | 31,0 | 3.1s,0s | 31:3.1:30.026527:0 | + + + Scenario: Step weights -- way_function: fail if no weight or weight_per_meter property + Given the profile file "testbot" extended with + """ + api_version = 1 + properties.traffic_signal_penalty = 0 + properties.u_turn_penalty = 0 + properties.weight_name = 'steps' + function way_function(way, result) + result.forward_mode = mode.driving + result.backward_mode = mode.driving + result.forward_speed = 42 + result.backward_speed = 42 + end + """ + And the node map + """ + a---b + """ + And the ways + | nodes | + | ab | + And the data has been saved to disk + + When I try to run "osrm-extract {osm_file} --profile {profile_file}" + Then stderr should contain "There are no edges" + And it should exit with an error + + Scenario: Step weights -- way_function: second way wins + Given the profile file "testbot" extended with + """ + api_version = 1 + properties.traffic_signal_penalty = 0 + properties.u_turn_penalty = 0 + properties.weight_name = 'steps' + function way_function(way, result) + result.forward_mode = mode.driving + result.backward_mode = mode.driving + result.duration = 42 + result.weight = 35 + end + """ + + Given the node map + """ + a---b---c---d---e---f---g---h + """ + + And the ways + | nodes | + | abcdef | + | abcdefgh | + + When I route I should get + | waypoints | route | distance | weights | times | + | a,f | , | 100m | 25,0 | 30s,0s | + | f,a | , | 100m | 25,0 | 30s,0s | + | a,h | , | 140m +-1 | 35,0 | 42s,0s | + | h,a | , | 140m +-1 | 35,0 | 42s,0s | + + Scenario: Step weights -- way_function: higher weight_per_meter is preferred + Given the profile file "testbot" extended with + """ + api_version = 1 + properties.traffic_signal_penalty = 0 + properties.u_turn_penalty = 0 + properties.weight_name = 'steps' + function way_function(way, result) + result.forward_mode = mode.driving + result.backward_mode = mode.driving + result.duration = 42 + result.forward_rate = 1 + result.backward_rate = 0.5 + end + """ + + Given the node map + """ + a---b---c---d---e---f---g---h + """ + + And the ways + | nodes | + | abcdefgh | + | abcdef | + | fgh | + + When I route I should get + | waypoints | route | distance | weights | times | + | a,f | , | 100m | 99.9,0 | 30s,0s | + | f,a | , | 100m | 199.8,0 | 30s,0s | + | a,h | , | 140m | 139.9,0 | 42s,0s | + | h,a | , | 140m | 279.8,0 | 42s,0s | + | f,h | , | 40m | 40,0 | 12s,0s | + | h,f | , | 40m | 80,0 | 12s,0s | + + Scenario: Step weights -- segment_function + Given the profile file "testbot" extended with + """ + api_version = 1 + properties.traffic_signal_penalty = 0 + properties.u_turn_penalty = 0 + properties.weight_name = 'steps' + function way_function(way, result) + result.forward_mode = mode.driving + result.backward_mode = mode.driving + result.weight = 42 + result.duration = 3 + end + function segment_function (segment) + segment.weight = 1 + segment.duration = 11 + end + """ + + Given the node map + """ + a---b---c---d---e---f---g---h + """ + + And the ways + | nodes | + | abcdefgh | + | abcdef | + | fgh | + + When I route I should get + | waypoints | route | distance | weights | times | + | a,f | , | 100m | 5,0 | 55s,0s | + | f,a | , | 100m | 5,0 | 55s,0s | + | a,h | , | 140m +-1 | 7,0 | 77s,0s | + | h,a | , | 140m +-1 | 7,0 | 77s,0s | + | f,h | , | 40m +-1 | 2,0 | 22s,0s | + | h,f | , | 40m +-1 | 2,0 | 22s,0s | + + + Scenario: Step weights -- segment_function and turn_function with weight precision + Given the profile file "testbot" extended with + """ + api_version = 1 + properties.traffic_signal_penalty = 0 + properties.u_turn_penalty = 0 + properties.weight_name = 'steps' + properties.weight_precision = 3 + function way_function(way, result) + result.forward_mode = mode.driving + result.backward_mode = mode.driving + result.weight = 42 + result.duration = 3 + end + function segment_function (segment) + segment.weight = 1.11 + segment.duration = 100 + end + function turn_function (turn) + print (turn.angle) + turn.weight = turn.angle / 100 + turn.duration = turn.angle + end + """ + + Given the node map + """ + a---b---c---d + ⋮ + e + """ + + And the ways + | nodes | + | abcd | + | ce | + + When I route I should get + | waypoints | route | distance | weights | times | + | a,c | , | 40m +-.1 | 3.119,0 | 289.9s,0s | + | a,e | ,, | 60m +-.1 | 3.119,1.11,0 | 289.9s,100s,0s | + | e,a | ,, | 60m +-.1 | 0.211,2.22,0 | 10.1s,200s,0s | + | e,d | ,, | 40m +-.1 | 2.009,1.11,0 | 189.9s,100s,0s | + | d,e | ,, | 40m +-.1 | 0.211,1.11,0 | 10.1s,100s,0s | diff --git a/include/contractor/contractor.hpp b/include/contractor/contractor.hpp index 8eff611dd..f0d1688f6 100644 --- a/include/contractor/contractor.hpp +++ b/include/contractor/contractor.hpp @@ -82,7 +82,9 @@ class Contractor std::vector &edge_based_edge_list, std::vector &node_weights, const std::string &edge_segment_lookup_path, - const std::string &edge_penalty_path, + const std::string &turn_weight_penalties_path, + const std::string &turn_duration_penalties_path, + const std::string &turn_penalties_index_path, const std::vector &segment_speed_path, const std::vector &turn_penalty_path, const std::string &nodes_filename, diff --git a/include/contractor/contractor_config.hpp b/include/contractor/contractor_config.hpp index cdb1b080d..887f511dd 100644 --- a/include/contractor/contractor_config.hpp +++ b/include/contractor/contractor_config.hpp @@ -49,7 +49,9 @@ struct ContractorConfig graph_output_path = osrm_input_path.string() + ".hsgr"; edge_based_graph_path = osrm_input_path.string() + ".ebg"; edge_segment_lookup_path = osrm_input_path.string() + ".edge_segment_lookup"; - edge_penalty_path = osrm_input_path.string() + ".edge_penalties"; + turn_weight_penalties_path = osrm_input_path.string() + ".turn_weight_penalties"; + turn_duration_penalties_path = osrm_input_path.string() + ".turn_duration_penalties"; + turn_penalties_index_path = osrm_input_path.string() + ".turn_penalties_index"; node_based_graph_path = osrm_input_path.string() + ".nodes"; geometry_path = osrm_input_path.string() + ".geometry"; rtree_leaf_path = osrm_input_path.string() + ".fileIndex"; @@ -66,7 +68,9 @@ struct ContractorConfig std::string edge_based_graph_path; std::string edge_segment_lookup_path; - std::string edge_penalty_path; + std::string turn_weight_penalties_path; + std::string turn_duration_penalties_path; + std::string turn_penalties_index_path; std::string node_based_graph_path; std::string geometry_path; std::string rtree_leaf_path; diff --git a/include/contractor/contractor_graph.hpp b/include/contractor/contractor_graph.hpp index 017d452b3..8d2d1728d 100644 --- a/include/contractor/contractor_graph.hpp +++ b/include/contractor/contractor_graph.hpp @@ -16,7 +16,7 @@ struct ContractorEdgeData is_original_via_node_ID(false) { } - ContractorEdgeData(unsigned weight, + ContractorEdgeData(EdgeWeight weight, unsigned original_edges, unsigned id, bool shortcut, @@ -26,7 +26,7 @@ struct ContractorEdgeData shortcut(shortcut), forward(forward), backward(backward), is_original_via_node_ID(false) { } - unsigned weight; + EdgeWeight weight; unsigned id; unsigned originalEdges : 28; bool shortcut : 1; diff --git a/include/contractor/contractor_heap.hpp b/include/contractor/contractor_heap.hpp index fb1f20ecf..4aca16609 100644 --- a/include/contractor/contractor_heap.hpp +++ b/include/contractor/contractor_heap.hpp @@ -18,8 +18,11 @@ struct ContractorHeapData bool target = false; }; -using ContractorHeap = util:: - BinaryHeap>; +using ContractorHeap = util::BinaryHeap>; } // namespace contractor } // namespace osrm diff --git a/include/contractor/graph_contractor.hpp b/include/contractor/graph_contractor.hpp index 575d9678b..a7d7a4295 100644 --- a/include/contractor/graph_contractor.hpp +++ b/include/contractor/graph_contractor.hpp @@ -200,7 +200,7 @@ class GraphContractor dijkstra.Clear(); dijkstra.Insert(source, 0, ContractorHeapData{}); - int max_weight = 0; + EdgeWeight max_weight = 0; unsigned number_of_targets = 0; for (auto out_edge : contractor_graph->GetAdjacentEdgeRange(node)) @@ -294,8 +294,8 @@ class GraphContractor const NodeID target = contractor_graph->GetTarget(out_edge); if (target == node) continue; - const int path_weight = in_data.weight + out_data.weight; - const int weight = dijkstra.GetKey(target); + const EdgeWeight path_weight = in_data.weight + out_data.weight; + const EdgeWeight weight = dijkstra.GetKey(target); if (path_weight < weight) { if (RUNSIMULATION) diff --git a/include/contractor/graph_contractor_adaptors.hpp b/include/contractor/graph_contractor_adaptors.hpp index 335e50635..963f2e80e 100644 --- a/include/contractor/graph_contractor_adaptors.hpp +++ b/include/contractor/graph_contractor_adaptors.hpp @@ -32,7 +32,7 @@ std::vector adaptToContractorInput(InputEdgeContainer input_edge #endif edges.emplace_back(input_edge.source, input_edge.target, - static_cast(std::max(input_edge.weight, 1)), + std::max(input_edge.weight, 1), 1, input_edge.edge_id, false, @@ -41,7 +41,7 @@ std::vector adaptToContractorInput(InputEdgeContainer input_edge edges.emplace_back(input_edge.target, input_edge.source, - static_cast(std::max(input_edge.weight, 1)), + std::max(input_edge.weight, 1), 1, input_edge.edge_id, false, diff --git a/include/contractor/query_edge.hpp b/include/contractor/query_edge.hpp index ebc1ee990..bf66693f8 100644 --- a/include/contractor/query_edge.hpp +++ b/include/contractor/query_edge.hpp @@ -31,7 +31,7 @@ struct QueryEdge // node. Otherwise we see the edge based node to access node data. NodeID id : 31; bool shortcut : 1; - int weight : 30; + EdgeWeight weight : 30; bool forward : 1; bool backward : 1; } data; diff --git a/include/engine/api/json_factory.hpp b/include/engine/api/json_factory.hpp index d9e03a968..75ae2e51c 100644 --- a/include/engine/api/json_factory.hpp +++ b/include/engine/api/json_factory.hpp @@ -87,7 +87,8 @@ util::json::Object makeRouteStep(guidance::RouteStep step, util::json::Value geo util::json::Object makeRoute(const guidance::Route &route, util::json::Array legs, - boost::optional geometry); + boost::optional geometry, + const char *weight_name); // Creates a Waypoint without Hint, see the Hint overload below util::json::Object makeWaypoint(const util::Coordinate location, std::string name); diff --git a/include/engine/api/route_api.hpp b/include/engine/api/route_api.hpp index 1c62cb4b7..a984f1090 100644 --- a/include/engine/api/route_api.hpp +++ b/include/engine/api/route_api.hpp @@ -221,21 +221,24 @@ class RouteAPI : public BaseAPI { util::json::Array durations; util::json::Array distances; + util::json::Array weights; util::json::Array nodes; util::json::Array datasources; auto &leg_geometry = leg_geometries[idx]; durations.values.reserve(leg_geometry.annotations.size()); distances.values.reserve(leg_geometry.annotations.size()); + weights.values.reserve(leg_geometry.annotations.size()); nodes.values.reserve(leg_geometry.osm_node_ids.size()); datasources.values.reserve(leg_geometry.annotations.size()); std::for_each(leg_geometry.annotations.begin(), leg_geometry.annotations.end(), - [this, &durations, &distances, &datasources]( + [this, &durations, &distances, &weights, &datasources]( const guidance::LegGeometry::Annotation &step) { durations.values.push_back(step.duration); distances.values.push_back(step.distance); + weights.values.push_back(step.weight); datasources.values.push_back(step.datasource); }); std::for_each(leg_geometry.osm_node_ids.begin(), @@ -246,6 +249,7 @@ class RouteAPI : public BaseAPI util::json::Object annotation; annotation.values["distance"] = std::move(distances); annotation.values["duration"] = std::move(durations); + annotation.values["weight"] = std::move(weights); annotation.values["nodes"] = std::move(nodes); annotation.values["datasources"] = std::move(datasources); annotations.push_back(std::move(annotation)); @@ -256,7 +260,8 @@ class RouteAPI : public BaseAPI json::makeRouteLegs(std::move(legs), std::move(step_geometries), std::move(annotations)), - std::move(json_overview)); + std::move(json_overview), + facade.GetWeightName()); return result; } diff --git a/include/engine/datafacade/contiguous_internalmem_datafacade.hpp b/include/engine/datafacade/contiguous_internalmem_datafacade.hpp index 5a08ab9b9..17ee2a5ce 100644 --- a/include/engine/datafacade/contiguous_internalmem_datafacade.hpp +++ b/include/engine/datafacade/contiguous_internalmem_datafacade.hpp @@ -88,10 +88,14 @@ class ContiguousInternalMemoryDataFacade : public BaseDataFacade util::ShM::vector m_geometry_node_list; util::ShM::vector m_geometry_fwd_weight_list; util::ShM::vector m_geometry_rev_weight_list; + util::ShM::vector m_geometry_fwd_duration_list; + util::ShM::vector m_geometry_rev_duration_list; util::ShM::vector m_is_core_node; util::ShM::vector m_datasource_list; util::ShM::vector m_lane_description_offsets; util::ShM::vector m_lane_description_masks; + util::ShM::vector m_turn_weight_penalties; + util::ShM::vector m_turn_duration_penalties; util::ShM::vector m_datasource_name_data; util::ShM::vector m_datasource_name_offsets; @@ -311,6 +315,29 @@ class ContiguousInternalMemoryDataFacade : public BaseDataFacade m_is_core_node = std::move(is_core_node); } + void InitializeTurnPenalties(storage::DataLayout &data_layout, char *memory_block) + { + auto turn_weight_penalties_ptr = data_layout.GetBlockPtr( + memory_block, storage::DataLayout::TURN_WEIGHT_PENALTIES); + m_turn_weight_penalties = util::ShM::vector( + turn_weight_penalties_ptr, + data_layout.num_entries[storage::DataLayout::TURN_WEIGHT_PENALTIES]); + if (data_layout.num_entries[storage::DataLayout::TURN_DURATION_PENALTIES] == 0) + { // Fallback to turn weight penalties that are turn duration penalties in deciseconds + m_turn_duration_penalties = util::ShM::vector( + turn_weight_penalties_ptr, + data_layout.num_entries[storage::DataLayout::TURN_WEIGHT_PENALTIES]); + } + else + { + auto turn_duration_penalties_ptr = data_layout.GetBlockPtr( + memory_block, storage::DataLayout::TURN_DURATION_PENALTIES); + m_turn_duration_penalties = util::ShM::vector( + turn_duration_penalties_ptr, + data_layout.num_entries[storage::DataLayout::TURN_DURATION_PENALTIES]); + } + } + void InitializeGeometryPointers(storage::DataLayout &data_layout, char *memory_block) { auto geometries_index_ptr = @@ -346,6 +373,20 @@ class ContiguousInternalMemoryDataFacade : public BaseDataFacade datasources_list_ptr, data_layout.num_entries[storage::DataLayout::DATASOURCES_LIST]); m_datasource_list = std::move(datasources_list); + auto geometries_fwd_duration_list_ptr = data_layout.GetBlockPtr( + memory_block, storage::DataLayout::GEOMETRIES_FWD_DURATION_LIST); + util::ShM::vector geometry_fwd_duration_list( + geometries_fwd_duration_list_ptr, + data_layout.num_entries[storage::DataLayout::GEOMETRIES_FWD_DURATION_LIST]); + m_geometry_fwd_duration_list = std::move(geometry_fwd_duration_list); + + auto geometries_rev_duration_list_ptr = data_layout.GetBlockPtr( + memory_block, storage::DataLayout::GEOMETRIES_REV_DURATION_LIST); + util::ShM::vector geometry_rev_duration_list( + geometries_rev_duration_list_ptr, + data_layout.num_entries[storage::DataLayout::GEOMETRIES_REV_DURATION_LIST]); + m_geometry_rev_duration_list = std::move(geometry_rev_duration_list); + auto datasource_name_data_ptr = data_layout.GetBlockPtr(memory_block, storage::DataLayout::DATASOURCE_NAME_DATA); util::ShM::vector datasource_name_data( @@ -406,6 +447,7 @@ class ContiguousInternalMemoryDataFacade : public BaseDataFacade InitializeGraphPointer(data_layout, memory_block); InitializeChecksumPointer(data_layout, memory_block); InitializeNodeAndEdgeInformationPointers(data_layout, memory_block); + InitializeTurnPenalties(data_layout, memory_block); InitializeGeometryPointers(data_layout, memory_block); InitializeTimestampPointer(data_layout, memory_block); InitializeViaNodeListPointer(data_layout, memory_block); @@ -535,6 +577,55 @@ class ContiguousInternalMemoryDataFacade : public BaseDataFacade return result_nodes; } + virtual std::vector + GetUncompressedForwardDurations(const EdgeID id) const override final + { + /* + * EdgeWeights's for geometries are stored in one place for + * both forward and reverse segments along the same bi- + * directional edge. The m_geometry_indices stores + * refences to where to find the beginning of the bi- + * directional edge in the m_geometry_fwd_weight_list vector. + * */ + const unsigned begin = m_geometry_indices.at(id) + 1; + const unsigned end = m_geometry_indices.at(id + 1); + + std::vector result_durations; + result_durations.resize(end - begin); + + std::copy(m_geometry_fwd_duration_list.begin() + begin, + m_geometry_fwd_duration_list.begin() + end, + result_durations.begin()); + + return result_durations; + } + + virtual std::vector + GetUncompressedReverseDurations(const EdgeID id) const override final + { + /* + * EdgeWeights for geometries are stored in one place for + * both forward and reverse segments along the same bi- + * directional edge. The m_geometry_indices stores + * refences to where to find the beginning of the bi- + * directional edge in the m_geometry_rev_weight_list vector. For + * reverse durations of bi-directional edges, edges 1 to + * n-1 of that edge need to be read in reverse. + */ + const unsigned begin = m_geometry_indices.at(id); + const unsigned end = m_geometry_indices.at(id + 1) - 1; + + std::vector result_durations; + result_durations.resize(end - begin); + + std::copy( + m_geometry_rev_duration_list.rbegin() + (m_geometry_rev_duration_list.size() - end), + m_geometry_rev_duration_list.rbegin() + (m_geometry_rev_duration_list.size() - begin), + result_durations.begin()); + + return result_durations; + } + virtual std::vector GetUncompressedForwardWeights(const EdgeID id) const override final { @@ -588,6 +679,18 @@ class ContiguousInternalMemoryDataFacade : public BaseDataFacade return m_via_geometry_list.at(id); } + virtual TurnPenalty GetWeightPenaltyForEdgeID(const unsigned id) const override final + { + BOOST_ASSERT(m_turn_weight_penalties.size() > id); + return m_turn_weight_penalties[id]; + } + + virtual TurnPenalty GetDurationPenaltyForEdgeID(const unsigned id) const override final + { + BOOST_ASSERT(m_turn_duration_penalties.size() > id); + return m_turn_duration_penalties[id]; + } + extractor::guidance::TurnInstruction GetTurnInstructionForEdgeID(const EdgeID id) const override final { @@ -886,6 +989,13 @@ class ContiguousInternalMemoryDataFacade : public BaseDataFacade return m_profile_properties->max_speed_for_map_matching; } + const char *GetWeightName() const override final { return m_profile_properties->weight_name; } + + unsigned GetWeightPrecision() const override final + { + return m_profile_properties->weight_precision; + } + BearingClassID GetBearingClassID(const NodeID id) const override final { return m_bearing_class_id_table.at(id); diff --git a/include/engine/datafacade/datafacade_base.hpp b/include/engine/datafacade/datafacade_base.hpp index b738f12f9..db33c5b04 100644 --- a/include/engine/datafacade/datafacade_base.hpp +++ b/include/engine/datafacade/datafacade_base.hpp @@ -85,12 +85,20 @@ class BaseDataFacade virtual std::vector GetUncompressedReverseGeometry(const EdgeID id) const = 0; + virtual TurnPenalty GetWeightPenaltyForEdgeID(const unsigned id) const = 0; + + virtual TurnPenalty GetDurationPenaltyForEdgeID(const unsigned id) const = 0; + // Gets the weight values for each segment in an uncompressed geometry. // Should always be 1 shorter than GetUncompressedGeometry virtual std::vector GetUncompressedForwardWeights(const EdgeID id) const = 0; - virtual std::vector GetUncompressedReverseWeights(const EdgeID id) const = 0; + // Gets the duration values for each segment in an uncompressed geometry. + // Should always be 1 shorter than GetUncompressedGeometry + virtual std::vector GetUncompressedForwardDurations(const EdgeID id) const = 0; + virtual std::vector GetUncompressedReverseDurations(const EdgeID id) const = 0; + // Returns the data source ids that were used to supply the edge // weights. Will return an empty array when only the base profile is used. virtual std::vector GetUncompressedForwardDatasources(const EdgeID id) const = 0; @@ -177,6 +185,10 @@ class BaseDataFacade virtual double GetMapMatchingMaxSpeed() const = 0; + virtual const char *GetWeightName() const = 0; + + virtual unsigned GetWeightPrecision() const = 0; + virtual BearingClassID GetBearingClassID(const NodeID id) const = 0; virtual util::guidance::TurnBearing PreTurnBearing(const EdgeID eid) const = 0; diff --git a/include/engine/geospatial_query.hpp b/include/engine/geospatial_query.hpp index cf27b2604..bcd9a6879 100644 --- a/include/engine/geospatial_query.hpp +++ b/include/engine/geospatial_query.hpp @@ -369,19 +369,24 @@ template class GeospatialQuery // Find the node-based-edge that this belongs to, and directly // calculate the forward_weight, forward_offset, reverse_weight, reverse_offset - int forward_offset = 0, forward_weight = 0; - int reverse_offset = 0, reverse_weight = 0; + EdgeWeight forward_offset = 0, forward_weight = 0, forward_duration = 0; + EdgeWeight reverse_offset = 0, reverse_weight = 0, reverse_duration = 0; const std::vector forward_weight_vector = datafacade.GetUncompressedForwardWeights(data.packed_geometry_id); const std::vector reverse_weight_vector = datafacade.GetUncompressedReverseWeights(data.packed_geometry_id); + const std::vector forward_duration_vector = + datafacade.GetUncompressedForwardDurations(data.packed_geometry_id); + const std::vector reverse_duration_vector = + datafacade.GetUncompressedReverseDurations(data.packed_geometry_id); for (std::size_t i = 0; i < data.fwd_segment_position; i++) { forward_offset += forward_weight_vector[i]; } forward_weight = forward_weight_vector[data.fwd_segment_position]; + forward_duration = forward_duration_vector[data.fwd_segment_position]; BOOST_ASSERT(data.fwd_segment_position < reverse_weight_vector.size()); @@ -392,22 +397,27 @@ template class GeospatialQuery } reverse_weight = reverse_weight_vector[reverse_weight_vector.size() - data.fwd_segment_position - 1]; + reverse_duration = + reverse_duration_vector[reverse_duration_vector.size() - data.fwd_segment_position - 1]; ratio = std::min(1.0, std::max(0.0, ratio)); if (data.forward_segment_id.id != SPECIAL_SEGMENTID) { - forward_weight *= ratio; + forward_weight = static_cast(forward_weight * ratio); + forward_duration = static_cast(forward_duration * ratio); } if (data.reverse_segment_id.id != SPECIAL_SEGMENTID) { - const EdgeWeight difference = reverse_weight * ratio; - reverse_weight -= difference; + reverse_weight -= static_cast(reverse_weight * ratio); + reverse_duration -= static_cast(reverse_duration * ratio); } auto transformed = PhantomNodeWithDistance{PhantomNode{data, forward_weight, - forward_offset, reverse_weight, + forward_duration, + reverse_duration, + forward_offset, reverse_offset, point_on_segment, input_coordinate}, diff --git a/include/engine/guidance/assemble_geometry.hpp b/include/engine/guidance/assemble_geometry.hpp index 7e15a7860..337d1e81b 100644 --- a/include/engine/guidance/assemble_geometry.hpp +++ b/include/engine/guidance/assemble_geometry.hpp @@ -75,8 +75,11 @@ inline LegGeometry assembleGeometry(const datafacade::BaseDataFacade &facade, } prev_coordinate = coordinate; - geometry.annotations.emplace_back(LegGeometry::Annotation{ - current_distance, path_point.duration_until_turn / 10., path_point.datasource_id}); + geometry.annotations.emplace_back( + LegGeometry::Annotation{current_distance, + path_point.duration_until_turn / 10., + path_point.weight_until_turn / 10., + path_point.datasource_id}); geometry.locations.push_back(std::move(coordinate)); geometry.osm_node_ids.push_back(facade.GetOSMNodeIDOfNode(path_point.turn_via_node)); } @@ -89,10 +92,14 @@ inline LegGeometry assembleGeometry(const datafacade::BaseDataFacade &facade, const std::vector forward_datasources = facade.GetUncompressedForwardDatasources(target_node.packed_geometry_id); + // FIXME this is wrong. We need to check for traversal direction here + // and for the case of a local path (target and source on the same edge) geometry.annotations.emplace_back( LegGeometry::Annotation{current_distance, + target_node.forward_duration / 10., target_node.forward_weight / 10., forward_datasources[target_node.fwd_segment_position]}); + geometry.segment_offsets.push_back(geometry.locations.size()); geometry.locations.push_back(target_node.location); diff --git a/include/engine/guidance/assemble_leg.hpp b/include/engine/guidance/assemble_leg.hpp index edffcf55c..c523c6346 100644 --- a/include/engine/guidance/assemble_leg.hpp +++ b/include/engine/guidance/assemble_leg.hpp @@ -79,7 +79,7 @@ std::array summarizeRoute(const std::vector 1) segments.push_back({target_duration, index++, target_node.name_id}); // this makes sure that the segment with the lowest position comes first @@ -130,18 +130,14 @@ inline RouteLeg assembleLeg(const datafacade::BaseDataFacade &facade, const bool needs_summary) { const auto target_duration = - (target_traversed_in_reverse ? target_node.reverse_weight : target_node.forward_weight) / - 10.; + (target_traversed_in_reverse ? target_node.reverse_duration : target_node.forward_duration); auto distance = std::accumulate( leg_geometry.segment_distances.begin(), leg_geometry.segment_distances.end(), 0.); - auto duration = std::accumulate(route_data.begin(), - route_data.end(), - 0., - [](const double sum, const PathData &data) { - return sum + data.duration_until_turn; - }) / - 10.; + auto duration = std::accumulate( + route_data.begin(), route_data.end(), 0, [](const double sum, const PathData &data) { + return sum + data.duration_until_turn; + }); // s // | @@ -155,10 +151,10 @@ inline RouteLeg assembleLeg(const datafacade::BaseDataFacade &facade, // The duration of the turn (a,c) -> (c,e) will be the duration of (a,c) (e.g. the duration // of (a,b,c)). // The phantom node of s will contain: - // `forward_weight`: duration of (a,s) + // `forward_duration`: duration of (a,s) // `forward_offset`: 0 (its the first segment) // The phantom node of t will contain: - // `forward_weight`: duration of (d,t) + // `forward_duration`: duration of (d,t) // `forward_offset`: duration of (c, d) // path_data will have entries for (s,b), (b, c), (c, d) but (d, t) is only // caputed by the phantom node. So we need to add the target duration here. @@ -167,9 +163,8 @@ inline RouteLeg assembleLeg(const datafacade::BaseDataFacade &facade, duration = duration + target_duration; if (route_data.empty()) { - duration -= (target_traversed_in_reverse ? source_node.reverse_weight - : source_node.forward_weight) / - 10.0; + duration -= (target_traversed_in_reverse ? source_node.reverse_duration + : source_node.forward_duration); } std::string summary; @@ -201,7 +196,7 @@ inline RouteLeg assembleLeg(const datafacade::BaseDataFacade &facade, summary = boost::algorithm::join(summary_names, ", "); } - return RouteLeg{duration, distance, summary, {}}; + return RouteLeg{duration / 10., distance, summary, {}}; } } // namespace guidance diff --git a/include/engine/guidance/assemble_steps.hpp b/include/engine/guidance/assemble_steps.hpp index d62ae5dd0..9a2fa0d3e 100644 --- a/include/engine/guidance/assemble_steps.hpp +++ b/include/engine/guidance/assemble_steps.hpp @@ -41,14 +41,20 @@ inline std::vector assembleSteps(const datafacade::BaseDataFacade &fa const bool source_traversed_in_reverse, const bool target_traversed_in_reverse) { - const double constexpr ZERO_DURATION = 0., ZERO_DISTANCE = 0.; + const double weight_multiplier = std::pow(10., facade.GetWeightPrecision()); + + const double constexpr ZERO_DURATION = 0., ZERO_DISTANCE = 0., ZERO_WEIGHT = 0; const constexpr char *NO_ROTARY_NAME = ""; - const EdgeWeight source_duration = + const EdgeWeight source_weight = source_traversed_in_reverse ? source_node.reverse_weight : source_node.forward_weight; + const EdgeWeight source_duration = + source_traversed_in_reverse ? source_node.reverse_duration : source_node.forward_duration; const auto source_mode = source_traversed_in_reverse ? source_node.backward_travel_mode : source_node.forward_travel_mode; const EdgeWeight target_duration = + target_traversed_in_reverse ? target_node.reverse_duration : target_node.forward_duration; + const EdgeWeight target_weight = target_traversed_in_reverse ? target_node.reverse_weight : target_node.forward_weight; const auto target_mode = target_traversed_in_reverse ? target_node.backward_travel_mode : target_node.forward_travel_mode; @@ -84,7 +90,8 @@ inline std::vector assembleSteps(const datafacade::BaseDataFacade &fa // but a RouteStep is with regard to the segment after the turn. // We need to skip the first segment because it is already covered by the // initial start of a route - int segment_duration = 0; + EdgeWeight segment_duration = 0; + EdgeWeight segment_weight = 0; // some name changes are not announced in our processing. For these, we have to keep the // first name on the segment @@ -93,11 +100,12 @@ inline std::vector assembleSteps(const datafacade::BaseDataFacade &fa { const auto &path_point = leg_data[leg_data_index]; segment_duration += path_point.duration_until_turn; + segment_weight += path_point.weight_until_turn; // all changes to this check have to be matched with assemble_geometry if (path_point.turn_instruction.type != extractor::guidance::TurnType::NoTurn) { - BOOST_ASSERT(segment_duration >= 0); + BOOST_ASSERT(segment_weight >= 0); const auto name = facade.GetNameForID(step_name_id); const auto ref = facade.GetRefForID(step_name_id); const auto pronunciation = facade.GetPronunciationForID(step_name_id); @@ -111,8 +119,9 @@ inline std::vector assembleSteps(const datafacade::BaseDataFacade &fa destinations.to_string(), NO_ROTARY_NAME, NO_ROTARY_NAME, - segment_duration / 10.0, + segment_duration / 10., distance, + segment_weight / weight_multiplier, path_point.travel_mode, maneuver, leg_geometry.FrontIndex(segment_index), @@ -173,10 +182,12 @@ inline std::vector assembleSteps(const datafacade::BaseDataFacade &fa 0}; segment_index++; segment_duration = 0; + segment_weight = 0; } } const auto distance = leg_geometry.segment_distances[segment_index]; - const int duration = segment_duration + target_duration; + const EdgeWeight duration = segment_duration + target_duration; + const EdgeWeight weight = segment_weight + target_weight; BOOST_ASSERT(duration >= 0); steps.push_back(RouteStep{step_name_id, facade.GetNameForID(step_name_id).to_string(), @@ -187,6 +198,7 @@ inline std::vector assembleSteps(const datafacade::BaseDataFacade &fa NO_ROTARY_NAME, duration / 10., distance, + weight / weight_multiplier, target_mode, maneuver, leg_geometry.FrontIndex(segment_index), @@ -202,7 +214,8 @@ inline std::vector assembleSteps(const datafacade::BaseDataFacade &fa // |---| source_duration // |---------| target_duration - int duration = target_duration - source_duration; + const EdgeWeight duration = target_duration - source_duration; + const EdgeWeight weight = target_weight - source_weight; BOOST_ASSERT(duration >= 0); steps.push_back(RouteStep{source_node.name_id, @@ -214,6 +227,7 @@ inline std::vector assembleSteps(const datafacade::BaseDataFacade &fa NO_ROTARY_NAME, duration / 10., leg_geometry.segment_distances[segment_index], + weight / weight_multiplier, source_mode, std::move(maneuver), leg_geometry.FrontIndex(segment_index), @@ -251,6 +265,7 @@ inline std::vector assembleSteps(const datafacade::BaseDataFacade &fa NO_ROTARY_NAME, ZERO_DURATION, ZERO_DISTANCE, + ZERO_WEIGHT, target_mode, std::move(maneuver), leg_geometry.locations.size() - 1, diff --git a/include/engine/guidance/leg_geometry.hpp b/include/engine/guidance/leg_geometry.hpp index 3c6fb8189..1c5fa54b2 100644 --- a/include/engine/guidance/leg_geometry.hpp +++ b/include/engine/guidance/leg_geometry.hpp @@ -37,8 +37,9 @@ struct LegGeometry // Per-coordinate metadata struct Annotation { - double distance; - double duration; + double distance; // distance in meters + double duration; // duration in seconds + double weight; // weight value DatasourceID datasource; }; std::vector annotations; diff --git a/include/engine/guidance/route_step.hpp b/include/engine/guidance/route_step.hpp index e7801f475..a48c204aa 100644 --- a/include/engine/guidance/route_step.hpp +++ b/include/engine/guidance/route_step.hpp @@ -64,8 +64,9 @@ struct RouteStep std::string destinations; std::string rotary_name; std::string rotary_pronunciation; - double duration; - double distance; + double duration; // duration in seconds + double distance; // distance in meters + double weight; // weight value extractor::TravelMode mode; StepManeuver maneuver; // indices into the locations array stored the LegGeometry @@ -117,6 +118,7 @@ inline void RouteStep::Invalidate() rotary_pronunciation.clear(); duration = 0; distance = 0; + weight = 0; mode = TRAVEL_MODE_INACCESSIBLE; maneuver = getInvalidStepManeuver(); geometry_begin = 0; @@ -132,6 +134,7 @@ inline RouteStep &RouteStep::AddInFront(const RouteStep &preceeding_step) BOOST_ASSERT(mode == preceeding_step.mode); duration += preceeding_step.duration; distance += preceeding_step.distance; + weight += preceeding_step.weight; geometry_begin = preceeding_step.geometry_begin; intersections.insert(intersections.begin(), @@ -148,6 +151,7 @@ inline RouteStep &RouteStep::ElongateBy(const RouteStep &following_step) BOOST_ASSERT(mode == following_step.mode); duration += following_step.duration; distance += following_step.distance; + weight += following_step.weight; geometry_end = following_step.geometry_end; intersections.insert(intersections.end(), diff --git a/include/engine/hint.hpp b/include/engine/hint.hpp index 0819cb7ef..334a45fd9 100644 --- a/include/engine/hint.hpp +++ b/include/engine/hint.hpp @@ -63,8 +63,8 @@ struct Hint friend std::ostream &operator<<(std::ostream &, const Hint &); }; -static_assert(sizeof(Hint) == 56 + 4, "Hint is bigger than expected"); -constexpr std::size_t ENCODED_HINT_SIZE = 80; +static_assert(sizeof(Hint) == 64 + 4, "Hint is bigger than expected"); +constexpr std::size_t ENCODED_HINT_SIZE = 92; static_assert(ENCODED_HINT_SIZE / 4 * 3 >= sizeof(Hint), "ENCODED_HINT_SIZE does not match size of Hint"); } diff --git a/include/engine/internal_route_result.hpp b/include/engine/internal_route_result.hpp index 939de14e3..248ee25ac 100644 --- a/include/engine/internal_route_result.hpp +++ b/include/engine/internal_route_result.hpp @@ -24,6 +24,8 @@ struct PathData NodeID turn_via_node; // name of the street that leads to the turn unsigned name_id; + // weight that is traveled on the segment until the turn is reached + EdgeWeight weight_until_turn; // duration that is traveled on the segment until the turn is reached EdgeWeight duration_until_turn; // instruction to execute at the turn diff --git a/include/engine/phantom_node.hpp b/include/engine/phantom_node.hpp index 3dd0cf63c..94fa02cee 100644 --- a/include/engine/phantom_node.hpp +++ b/include/engine/phantom_node.hpp @@ -49,10 +49,12 @@ struct PhantomNode PhantomNode(SegmentID forward_segment_id, SegmentID reverse_segment_id, unsigned name_id, - int forward_weight, - int reverse_weight, - int forward_offset, - int reverse_offset, + EdgeWeight forward_weight, + EdgeWeight reverse_weight, + EdgeWeight forward_duration, + EdgeWeight reverse_duration, + EdgeWeight forward_offset, + EdgeWeight reverse_offset, unsigned packed_geometry_id_, bool is_tiny_component, unsigned component_id, @@ -63,6 +65,7 @@ struct PhantomNode extractor::TravelMode backward_travel_mode) : forward_segment_id(forward_segment_id), reverse_segment_id(reverse_segment_id), name_id(name_id), forward_weight(forward_weight), reverse_weight(reverse_weight), + forward_duration(forward_duration), reverse_duration(reverse_duration), forward_offset(forward_offset), reverse_offset(reverse_offset), packed_geometry_id(packed_geometry_id_), component{component_id, is_tiny_component}, location(std::move(location)), input_location(std::move(input_location)), @@ -75,20 +78,21 @@ struct PhantomNode : forward_segment_id{SPECIAL_SEGMENTID, false}, reverse_segment_id{SPECIAL_SEGMENTID, false}, name_id(std::numeric_limits::max()), forward_weight(INVALID_EDGE_WEIGHT), - reverse_weight(INVALID_EDGE_WEIGHT), forward_offset(0), reverse_offset(0), + reverse_weight(INVALID_EDGE_WEIGHT), forward_duration(INVALID_EDGE_WEIGHT), + reverse_duration(INVALID_EDGE_WEIGHT), forward_offset(0), reverse_offset(0), packed_geometry_id(SPECIAL_GEOMETRYID), component{INVALID_COMPONENTID, false}, fwd_segment_position(0), forward_travel_mode(TRAVEL_MODE_INACCESSIBLE), backward_travel_mode(TRAVEL_MODE_INACCESSIBLE) { } - int GetForwardWeightPlusOffset() const + EdgeWeight GetForwardWeightPlusOffset() const { BOOST_ASSERT(forward_segment_id.enabled); return forward_offset + forward_weight; } - int GetReverseWeightPlusOffset() const + EdgeWeight GetReverseWeightPlusOffset() const { BOOST_ASSERT(reverse_segment_id.enabled); return reverse_offset + reverse_weight; @@ -102,6 +106,8 @@ struct PhantomNode (reverse_segment_id.id < number_of_nodes)) && ((forward_weight != INVALID_EDGE_WEIGHT) || (reverse_weight != INVALID_EDGE_WEIGHT)) && + ((forward_duration != INVALID_EDGE_WEIGHT) || + (reverse_duration != INVALID_EDGE_WEIGHT)) && (component.id != INVALID_COMPONENTID) && (name_id != INVALID_NAMEID); } @@ -116,19 +122,22 @@ struct PhantomNode template explicit PhantomNode(const OtherT &other, - int forward_weight_, - int forward_offset_, - int reverse_weight_, - int reverse_offset_, - const util::Coordinate location_, - const util::Coordinate input_location_) + EdgeWeight forward_weight, + EdgeWeight reverse_weight, + EdgeWeight forward_duration, + EdgeWeight reverse_duration, + EdgeWeight forward_offset, + EdgeWeight reverse_offset, + const util::Coordinate location, + const util::Coordinate input_location) : forward_segment_id{other.forward_segment_id}, reverse_segment_id{other.reverse_segment_id}, name_id{other.name_id}, - forward_weight{forward_weight_}, reverse_weight{reverse_weight_}, - forward_offset{forward_offset_}, reverse_offset{reverse_offset_}, + forward_weight{forward_weight}, reverse_weight{reverse_weight}, + forward_duration{forward_duration}, reverse_duration{reverse_duration}, + forward_offset{forward_offset}, reverse_offset{reverse_offset}, packed_geometry_id{other.packed_geometry_id}, - component{other.component.id, other.component.is_tiny}, location{location_}, - input_location{input_location_}, fwd_segment_position{other.fwd_segment_position}, + component{other.component.id, other.component.is_tiny}, location{location}, + input_location{input_location}, fwd_segment_position{other.fwd_segment_position}, forward_travel_mode{other.forward_travel_mode}, backward_travel_mode{other.backward_travel_mode} { @@ -137,10 +146,12 @@ struct PhantomNode SegmentID forward_segment_id; SegmentID reverse_segment_id; unsigned name_id; - int forward_weight; - int reverse_weight; - int forward_offset; - int reverse_offset; + EdgeWeight forward_weight; + EdgeWeight reverse_weight; + EdgeWeight forward_duration; + EdgeWeight reverse_duration; + EdgeWeight forward_offset; + EdgeWeight reverse_offset; unsigned packed_geometry_id; struct ComponentType { @@ -158,7 +169,7 @@ struct PhantomNode extractor::TravelMode backward_travel_mode; }; -static_assert(sizeof(PhantomNode) == 56, "PhantomNode has more padding then expected"); +static_assert(sizeof(PhantomNode) == 64, "PhantomNode has more padding then expected"); using PhantomNodePair = std::pair; @@ -188,6 +199,8 @@ inline std::ostream &operator<<(std::ostream &out, const PhantomNode &pn) << "name: " << pn.name_id << ", " << "fwd-w: " << pn.forward_weight << ", " << "rev-w: " << pn.reverse_weight << ", " + << "fwd-d: " << pn.forward_duration << ", " + << "rev-d: " << pn.reverse_duration << ", " << "fwd-o: " << pn.forward_offset << ", " << "rev-o: " << pn.reverse_offset << ", " << "geom: " << pn.packed_geometry_id << ", " diff --git a/include/engine/routing_algorithms/alternative_path.hpp b/include/engine/routing_algorithms/alternative_path.hpp index 13195407b..4dc9659af 100644 --- a/include/engine/routing_algorithms/alternative_path.hpp +++ b/include/engine/routing_algorithms/alternative_path.hpp @@ -91,7 +91,7 @@ class AlternativeRouting final : private BasicRoutingInterface QueryHeap &heap1, QueryHeap &heap2, NodeID *middle_node, - int *upper_bound_to_shortest_path_weight, + EdgeWeight *upper_bound_to_shortest_path_weight, std::vector &search_space_intersection, std::vector &search_space, const EdgeWeight min_edge_offset) const @@ -100,14 +100,14 @@ class AlternativeRouting final : private BasicRoutingInterface QueryHeap &reverse_heap = (is_forward_directed ? heap2 : heap1); const NodeID node = forward_heap.DeleteMin(); - const int weight = forward_heap.GetKey(node); + const EdgeWeight weight = forward_heap.GetKey(node); // const NodeID parentnode = forward_heap.GetData(node).parent; // util::Log() << (is_forward_directed ? "[fwd] " : "[rev] ") << "settled // edge (" // << parentnode << "," << node << "), dist: " << weight; - const int scaled_weight = - static_cast((weight + min_edge_offset) / (1. + VIAPATH_EPSILON)); + const auto scaled_weight = + static_cast((weight + min_edge_offset) / (1. + VIAPATH_EPSILON)); if ((INVALID_EDGE_WEIGHT != *upper_bound_to_shortest_path_weight) && (scaled_weight > *upper_bound_to_shortest_path_weight)) { @@ -120,7 +120,7 @@ class AlternativeRouting final : private BasicRoutingInterface if (reverse_heap.WasInserted(node)) { search_space_intersection.emplace_back(node); - const int new_weight = reverse_heap.GetKey(node) + weight; + const EdgeWeight new_weight = reverse_heap.GetKey(node) + weight; if (new_weight < *upper_bound_to_shortest_path_weight) { if (new_weight >= 0) @@ -139,7 +139,7 @@ class AlternativeRouting final : private BasicRoutingInterface { // check whether there is a loop present at the node const auto loop_weight = super::GetLoopWeight(facade, node); - const int new_weight_with_loop = new_weight + loop_weight; + const EdgeWeight new_weight_with_loop = new_weight + loop_weight; if (loop_weight != INVALID_EDGE_WEIGHT && new_weight_with_loop <= *upper_bound_to_shortest_path_weight) { @@ -158,10 +158,10 @@ class AlternativeRouting final : private BasicRoutingInterface if (edge_is_forward_directed) { const NodeID to = facade->GetTarget(edge); - const int edge_weight = data.weight; + const EdgeWeight edge_weight = data.weight; BOOST_ASSERT(edge_weight > 0); - const int to_weight = weight + edge_weight; + const EdgeWeight to_weight = weight + edge_weight; // New Node discovered -> Add to Heap + Node Info Storage if (!forward_heap.WasInserted(to)) diff --git a/include/engine/routing_algorithms/many_to_many.hpp b/include/engine/routing_algorithms/many_to_many.hpp index 157ba44a0..616034928 100644 --- a/include/engine/routing_algorithms/many_to_many.hpp +++ b/include/engine/routing_algorithms/many_to_many.hpp @@ -76,10 +76,10 @@ class ManyToManyRouting final : public BasicRoutingInterface if (direction_flag) { const NodeID to = facade->GetTarget(edge); - const int edge_weight = data.weight; + const EdgeWeight edge_weight = data.weight; BOOST_ASSERT_MSG(edge_weight > 0, "edge_weight invalid"); - const int to_weight = weight + edge_weight; + const EdgeWeight to_weight = weight + edge_weight; // New Node discovered -> Add to Heap + Node Info Storage if (!query_heap.WasInserted(to)) @@ -111,7 +111,7 @@ class ManyToManyRouting final : public BasicRoutingInterface if (reverse_flag) { const NodeID to = facade->GetTarget(edge); - const int edge_weight = data.weight; + const EdgeWeight edge_weight = data.weight; BOOST_ASSERT_MSG(edge_weight > 0, "edge_weight invalid"); if (query_heap.WasInserted(to)) { diff --git a/include/engine/routing_algorithms/routing_base.hpp b/include/engine/routing_algorithms/routing_base.hpp index c3e6e37d7..f3e6d6b9c 100644 --- a/include/engine/routing_algorithms/routing_base.hpp +++ b/include/engine/routing_algorithms/routing_base.hpp @@ -122,12 +122,15 @@ class BasicRoutingInterface const auto geometry_index = facade->GetGeometryIndexForEdgeID(edge_data.id); std::vector id_vector; + std::vector weight_vector; + std::vector duration_vector; std::vector datasource_vector; if (geometry_index.forward) { id_vector = facade->GetUncompressedForwardGeometry(geometry_index.id); weight_vector = facade->GetUncompressedForwardWeights(geometry_index.id); + duration_vector = facade->GetUncompressedForwardDurations(geometry_index.id); datasource_vector = facade->GetUncompressedForwardDatasources(geometry_index.id); } @@ -135,17 +138,14 @@ class BasicRoutingInterface { id_vector = facade->GetUncompressedReverseGeometry(geometry_index.id); weight_vector = facade->GetUncompressedReverseWeights(geometry_index.id); + duration_vector = facade->GetUncompressedReverseDurations(geometry_index.id); datasource_vector = facade->GetUncompressedReverseDatasources(geometry_index.id); } BOOST_ASSERT(id_vector.size() > 0); - BOOST_ASSERT(weight_vector.size() > 0); BOOST_ASSERT(datasource_vector.size() > 0); - - const auto total_weight = - std::accumulate(weight_vector.begin(), weight_vector.end(), 0); - BOOST_ASSERT(weight_vector.size() == id_vector.size() - 1); + BOOST_ASSERT(duration_vector.size() == id_vector.size() - 1); const bool is_first_segment = unpacked_path.empty(); const std::size_t start_index = @@ -165,6 +165,7 @@ class BasicRoutingInterface PathData{id_vector[segment_idx + 1], name_index, weight_vector[segment_idx], + duration_vector[segment_idx], extractor::guidance::TurnInstruction::NO_TURN(), {{0, INVALID_LANEID}, INVALID_LANE_DESCRIPTIONID}, travel_mode, @@ -179,7 +180,10 @@ class BasicRoutingInterface unpacked_path.back().entry_classid = facade->GetEntryClassID(edge_data.id); unpacked_path.back().turn_instruction = turn_instruction; - unpacked_path.back().duration_until_turn += (edge_data.weight - total_weight); + unpacked_path.back().duration_until_turn += + facade->GetDurationPenaltyForEdgeID(edge_data.id); + unpacked_path.back().weight_until_turn += + facade->GetWeightPenaltyForEdgeID(edge_data.id); unpacked_path.back().pre_turn_bearing = facade->PreTurnBearing(edge_data.id); unpacked_path.back().post_turn_bearing = facade->PostTurnBearing(edge_data.id); }); @@ -187,6 +191,7 @@ class BasicRoutingInterface std::size_t start_index = 0, end_index = 0; std::vector id_vector; std::vector weight_vector; + std::vector duration_vector; std::vector datasource_vector; const bool is_local_path = (phantom_node_pair.source_phantom.packed_geometry_id == phantom_node_pair.target_phantom.packed_geometry_id) && @@ -200,6 +205,9 @@ class BasicRoutingInterface weight_vector = facade->GetUncompressedReverseWeights( phantom_node_pair.target_phantom.packed_geometry_id); + duration_vector = facade->GetUncompressedReverseDurations( + phantom_node_pair.target_phantom.packed_geometry_id); + datasource_vector = facade->GetUncompressedReverseDatasources( phantom_node_pair.target_phantom.packed_geometry_id); @@ -225,6 +233,9 @@ class BasicRoutingInterface weight_vector = facade->GetUncompressedForwardWeights( phantom_node_pair.target_phantom.packed_geometry_id); + duration_vector = facade->GetUncompressedForwardDurations( + phantom_node_pair.target_phantom.packed_geometry_id); + datasource_vector = facade->GetUncompressedForwardDatasources( phantom_node_pair.target_phantom.packed_geometry_id); } @@ -245,6 +256,7 @@ class BasicRoutingInterface id_vector[start_index < end_index ? segment_idx + 1 : segment_idx - 1], phantom_node_pair.target_phantom.name_id, weight_vector[segment_idx], + duration_vector[segment_idx], extractor::guidance::TurnInstruction::NO_TURN(), {{0, INVALID_LANEID}, INVALID_LANE_DESCRIPTIONID}, target_traversed_in_reverse ? phantom_node_pair.target_phantom.backward_travel_mode @@ -260,6 +272,9 @@ class BasicRoutingInterface const auto source_weight = start_traversed_in_reverse ? phantom_node_pair.source_phantom.reverse_weight : phantom_node_pair.source_phantom.forward_weight; + const auto source_duration = start_traversed_in_reverse + ? phantom_node_pair.source_phantom.reverse_duration + : phantom_node_pair.source_phantom.forward_duration; // The above code will create segments for (v, w), (w,x), (x, y) and (y, Z). // However the first segment duration needs to be adjusted to the fact that the source // phantom is in the middle of the segment. We do this by subtracting v--s from the @@ -273,8 +288,10 @@ class BasicRoutingInterface // TODO this creates a scenario where it's possible the duration from a phantom // node to the first turn would be the same as from end to end of a segment, // which is obviously incorrect and not ideal... + unpacked_path.front().weight_until_turn = + std::max(unpacked_path.front().weight_until_turn - source_weight, 0); unpacked_path.front().duration_until_turn = - std::max(unpacked_path.front().duration_until_turn - source_weight, 0); + std::max(unpacked_path.front().duration_until_turn - source_duration, 0); } // there is no equivalent to a node-based node in an edge-expanded graph. diff --git a/include/engine/search_engine_data.hpp b/include/engine/search_engine_data.hpp index e97066449..0ec0992f0 100644 --- a/include/engine/search_engine_data.hpp +++ b/include/engine/search_engine_data.hpp @@ -19,8 +19,8 @@ struct HeapData struct SearchEngineData { - using QueryHeap = - util::BinaryHeap>; + using QueryHeap = util:: + BinaryHeap>; using SearchEngineHeapPtr = boost::thread_specific_ptr; static SearchEngineHeapPtr forward_heap_1; diff --git a/include/extractor/compressed_edge_container.hpp b/include/extractor/compressed_edge_container.hpp index 9aeb0d78f..72c93f9dc 100644 --- a/include/extractor/compressed_edge_container.hpp +++ b/include/extractor/compressed_edge_container.hpp @@ -19,8 +19,9 @@ class CompressedEdgeContainer struct OnewayCompressedEdge { public: - NodeID node_id; // refers to an internal node-based-node - EdgeWeight weight; // the weight of the edge leading to this node + NodeID node_id; // refers to an internal node-based-node + EdgeWeight weight; // the weight of the edge leading to this node + EdgeWeight duration; // the duration of the edge leading to this node }; using OnewayEdgeBucket = std::vector; @@ -31,10 +32,14 @@ class CompressedEdgeContainer const NodeID via_node_id, const NodeID target_node, const EdgeWeight weight1, - const EdgeWeight weight2); + const EdgeWeight weight2, + const EdgeWeight duration1, + const EdgeWeight duration2); - void - AddUncompressedEdge(const EdgeID edge_id, const NodeID target_node, const EdgeWeight weight); + void AddUncompressedEdge(const EdgeID edge_id, + const NodeID target_node, + const EdgeWeight weight, + const EdgeWeight duration); void InitializeBothwayVector(); unsigned ZipEdges(const unsigned f_edge_pos, const unsigned r_edge_pos); @@ -62,6 +67,8 @@ class CompressedEdgeContainer std::vector m_compressed_geometry_nodes; std::vector m_compressed_geometry_fwd_weights; std::vector m_compressed_geometry_rev_weights; + std::vector m_compressed_geometry_fwd_durations; + std::vector m_compressed_geometry_rev_durations; std::vector m_free_list; std::unordered_map m_edge_id_to_list_index_map; std::unordered_map m_forward_edge_id_to_zipped_index_map; diff --git a/include/extractor/edge_based_edge.hpp b/include/extractor/edge_based_edge.hpp index 3dd3fe4c5..699b45e79 100644 --- a/include/extractor/edge_based_edge.hpp +++ b/include/extractor/edge_based_edge.hpp @@ -30,9 +30,13 @@ struct EdgeBasedEdge NodeID target; NodeID edge_id; EdgeWeight weight : 30; - bool forward : 1; - bool backward : 1; + std::uint32_t forward : 1; + std::uint32_t backward : 1; }; +static_assert(sizeof(extractor::EdgeBasedEdge) == 16, + "Size of extractor::EdgeBasedEdge type is " + "bigger than expected. This will influence " + "memory consumption."); // Impl. diff --git a/include/extractor/edge_based_graph_factory.hpp b/include/extractor/edge_based_graph_factory.hpp index 69d7fd4fa..81a174392 100644 --- a/include/extractor/edge_based_graph_factory.hpp +++ b/include/extractor/edge_based_graph_factory.hpp @@ -6,6 +6,7 @@ #include "extractor/compressed_edge_container.hpp" #include "extractor/edge_based_edge.hpp" #include "extractor/edge_based_node.hpp" +#include "extractor/extraction_turn.hpp" #include "extractor/original_edge_data.hpp" #include "extractor/profile_properties.hpp" #include "extractor/query_node.hpp" @@ -65,15 +66,25 @@ struct SegmentBlock static_assert(sizeof(SegmentBlock) == 20, "SegmentBlock is not packed correctly"); #pragma pack(push, 1) -struct PenaltyBlock +struct TurnPenaltiesHeader +{ + //! the number of penalties in each block + std::uint64_t number_of_penalties; +}; +#pragma pack(pop) +static_assert(std::is_trivial::value, "TurnPenaltiesHeader is not trivial"); +static_assert(sizeof(TurnPenaltiesHeader) == 8, "TurnPenaltiesHeader is not packed correctly"); + +#pragma pack(push, 1) +struct TurnIndexBlock { - std::uint32_t fixed_penalty; OSMNodeID from_id; OSMNodeID via_id; OSMNodeID to_id; }; #pragma pack(pop) -static_assert(sizeof(PenaltyBlock) == 28, "PenaltyBlock is not packed correctly"); +static_assert(std::is_trivial::value, "TurnIndexBlock is not trivial"); +static_assert(sizeof(TurnIndexBlock) == 24, "TurnIndexBlock is not packed correctly"); } class EdgeBasedGraphFactory @@ -98,7 +109,9 @@ class EdgeBasedGraphFactory const std::string &original_edge_data_filename, const std::string &turn_lane_data_filename, const std::string &edge_segment_lookup_filename, - const std::string &edge_penalty_filename, + const std::string &turn_weight_penalties_filename, + const std::string &turn_duration_penalties_filename, + const std::string &turn_penalties_index_filename, const bool generate_edge_lookup); // The following get access functions destroy the content in the factory @@ -157,14 +170,15 @@ class EdgeBasedGraphFactory std::vector &turn_lane_masks; guidance::LaneDescriptionMap &lane_description_map; - void CompressGeometry(); unsigned RenumberEdges(); void GenerateEdgeExpandedNodes(); void GenerateEdgeExpandedEdges(ScriptingEnvironment &scripting_environment, const std::string &original_edge_data_filename, const std::string &turn_lane_data_filename, const std::string &edge_segment_lookup_filename, - const std::string &edge_fixed_penalties_filename, + const std::string &turn_weight_penalties_filename, + const std::string &turn_duration_penalties_filename, + const std::string &turn_penalties_index_filename, const bool generate_edge_lookup); void InsertEdgeBasedNode(const NodeID u, const NodeID v); diff --git a/include/extractor/extraction_segment.hpp b/include/extractor/extraction_segment.hpp new file mode 100644 index 000000000..6e3f3b5d5 --- /dev/null +++ b/include/extractor/extraction_segment.hpp @@ -0,0 +1,32 @@ +#ifndef OSRM_EXTRACTION_SEGMENT_HPP +#define OSRM_EXTRACTION_SEGMENT_HPP + +#include + +namespace osrm +{ +namespace extractor +{ + +struct ExtractionSegment +{ + ExtractionSegment(const osrm::util::Coordinate source_, + const osrm::util::Coordinate target_, + double distance_, + double weight_, + double duration_) + : source(source_), target(target_), distance(distance_), weight(weight_), + duration(duration_) + { + } + + const osrm::util::Coordinate source; + const osrm::util::Coordinate target; + const double distance; + double weight; + double duration; +}; +} +} + +#endif diff --git a/include/extractor/extraction_turn.hpp b/include/extractor/extraction_turn.hpp new file mode 100644 index 000000000..1fd942c39 --- /dev/null +++ b/include/extractor/extraction_turn.hpp @@ -0,0 +1,34 @@ +#ifndef OSRM_EXTRACTION_TURN_HPP +#define OSRM_EXTRACTION_TURN_HPP + +#include + +#include + +#include + +namespace osrm +{ +namespace extractor +{ + +struct ExtractionTurn +{ + ExtractionTurn(const guidance::ConnectedRoad &turn, bool has_traffic_light) + : angle(180. - turn.angle), turn_type(turn.instruction.type), + direction_modifier(turn.instruction.direction_modifier), + has_traffic_light(has_traffic_light), weight(0.), duration(0.) + { + } + + const double angle; + const guidance::TurnType::Enum turn_type; + const guidance::DirectionModifier::Enum direction_modifier; + const bool has_traffic_light; + double weight; + double duration; +}; +} +} + +#endif diff --git a/include/extractor/extraction_way.hpp b/include/extractor/extraction_way.hpp index 67a982146..171e18bd9 100644 --- a/include/extractor/extraction_way.hpp +++ b/include/extractor/extraction_way.hpp @@ -42,7 +42,10 @@ struct ExtractionWay { forward_speed = -1; backward_speed = -1; + forward_rate = -1; + backward_rate = -1; duration = -1; + weight = -1; roundabout = false; circular = false; is_startpoint = true; @@ -84,9 +87,16 @@ struct ExtractionWay } const char *GetTurnLanesBackward() const { return turn_lanes_backward.c_str(); } + // speed in km/h double forward_speed; double backward_speed; + // weight per meter + double forward_rate; + double backward_rate; + // duration of the whole way in both directions double duration; + // weight of the whole way in both directions + double weight; std::string name; std::string ref; std::string pronunciation; diff --git a/include/extractor/extractor_callbacks.hpp b/include/extractor/extractor_callbacks.hpp index e5cb86c32..7577ca8e3 100644 --- a/include/extractor/extractor_callbacks.hpp +++ b/include/extractor/extractor_callbacks.hpp @@ -43,6 +43,7 @@ class ExtractionContainers; struct InputRestrictionContainer; struct ExtractionNode; struct ExtractionWay; +struct ProfileProperties; /** * This class is used by the extractor with the results of the @@ -61,9 +62,11 @@ class ExtractorCallbacks std::unordered_map string_map; guidance::LaneDescriptionMap lane_description_map; ExtractionContainers &external_memory; + bool fallback_to_duration; public: - explicit ExtractorCallbacks(ExtractionContainers &extraction_containers); + explicit ExtractorCallbacks(ExtractionContainers &extraction_containers, + const ProfileProperties &properties); ExtractorCallbacks(const ExtractorCallbacks &) = delete; ExtractorCallbacks &operator=(const ExtractorCallbacks &) = delete; diff --git a/include/extractor/extractor_config.hpp b/include/extractor/extractor_config.hpp index 667f09603..0bb3d48fc 100644 --- a/include/extractor/extractor_config.hpp +++ b/include/extractor/extractor_config.hpp @@ -71,7 +71,9 @@ struct ExtractorConfig rtree_nodes_output_path = basepath + ".osrm.ramIndex"; rtree_leafs_output_path = basepath + ".osrm.fileIndex"; edge_segment_lookup_path = basepath + ".osrm.edge_segment_lookup"; - edge_penalty_path = basepath + ".osrm.edge_penalties"; + turn_duration_penalties_path = basepath + ".osrm.turn_duration_penalties"; + turn_weight_penalties_path = basepath + ".osrm.turn_weight_penalties"; + turn_penalties_index_path = basepath + ".osrm.turn_penalties_index"; edge_based_node_weights_output_path = basepath + ".osrm.enw"; profile_properties_output_path = basepath + ".osrm.properties"; intersection_class_data_output_path = basepath + ".osrm.icd"; @@ -95,12 +97,14 @@ struct ExtractorConfig std::string rtree_leafs_output_path; std::string profile_properties_output_path; std::string intersection_class_data_output_path; + std::string turn_weight_penalties_path; + std::string turn_duration_penalties_path; unsigned requested_num_threads; unsigned small_component_size; bool generate_edge_lookup; - std::string edge_penalty_path; + std::string turn_penalties_index_path; std::string edge_segment_lookup_path; bool use_metadata; diff --git a/include/extractor/internal_extractor_edge.hpp b/include/extractor/internal_extractor_edge.hpp index 42fea6e42..87d14b333 100644 --- a/include/extractor/internal_extractor_edge.hpp +++ b/include/extractor/internal_extractor_edge.hpp @@ -1,15 +1,16 @@ #ifndef INTERNAL_EXTRACTOR_EDGE_HPP #define INTERNAL_EXTRACTOR_EDGE_HPP +#include "extractor/guidance/road_classification.hpp" +#include "extractor/guidance/turn_lane_types.hpp" #include "extractor/node_based_edge.hpp" #include "extractor/travel_mode.hpp" +#include "osrm/coordinate.hpp" +#include "util/strong_typedef.hpp" #include "util/typedefs.hpp" #include - -#include "extractor/guidance/road_classification.hpp" -#include "extractor/guidance/turn_lane_types.hpp" -#include "osrm/coordinate.hpp" +#include #include namespace osrm @@ -17,31 +18,37 @@ namespace osrm namespace extractor { +namespace detail +{ +// these are used for duration based mode of transportations like ferries +OSRM_STRONG_TYPEDEF(double, ValueByEdge) +OSRM_STRONG_TYPEDEF(double, ValueByMeter) +using ByEdgeOrByMeterValue = mapbox::util::variant; + +struct ToValueByEdge +{ + ToValueByEdge(double distance_) : distance(distance_) {} + + ValueByEdge operator()(const ValueByMeter by_meter) const + { + return ValueByEdge{distance / static_cast(by_meter)}; + } + + ValueByEdge operator()(const ValueByEdge by_edge) const { return by_edge; } + + double distance; +}; +} + struct InternalExtractorEdge { - // specify the type of the weight data - enum class WeightType : char - { - INVALID, - SPEED, - EDGE_DURATION, - WAY_DURATION, - }; - - struct WeightData - { - WeightData() : duration(0.0), type(WeightType::INVALID) {} - - union { - double duration; - double speed; - }; - WeightType type; - }; + using WeightData = detail::ByEdgeOrByMeterValue; + using DurationData = detail::ByEdgeOrByMeterValue; explicit InternalExtractorEdge() : result(MIN_OSM_NODEID, MIN_OSM_NODEID, + SPECIAL_NODEID, 0, 0, false, // forward @@ -52,7 +59,8 @@ struct InternalExtractorEdge TRAVEL_MODE_INACCESSIBLE, false, guidance::TurnLaneType::empty, - guidance::RoadClassification()) + guidance::RoadClassification()), + weight_data(detail::ValueByMeter{0.0}), duration_data(detail::ValueByMeter{0.0}) { } @@ -60,6 +68,7 @@ struct InternalExtractorEdge OSMNodeID target, NodeID name_id, WeightData weight_data, + DurationData duration_data, bool forward, bool backward, bool roundabout, @@ -68,11 +77,13 @@ struct InternalExtractorEdge TravelMode travel_mode, bool is_split, LaneDescriptionID lane_description, - guidance::RoadClassification road_classification) + guidance::RoadClassification road_classification, + util::Coordinate source_coordinate) : result(source, target, name_id, 0, + 0, forward, backward, roundabout, @@ -82,7 +93,8 @@ struct InternalExtractorEdge is_split, lane_description, std::move(road_classification)), - weight_data(std::move(weight_data)) + weight_data(std::move(weight_data)), duration_data(std::move(duration_data)), + source_coordinate(std::move(source_coordinate)) { } @@ -90,6 +102,8 @@ struct InternalExtractorEdge NodeBasedEdgeWithOSM result; // intermediate edge weight WeightData weight_data; + // intermediate edge duration + DurationData duration_data; // coordinate of the source node util::Coordinate source_coordinate; @@ -98,8 +112,9 @@ struct InternalExtractorEdge { return InternalExtractorEdge(MIN_OSM_NODEID, MIN_OSM_NODEID, - 0, - WeightData(), + SPECIAL_NODEID, + detail::ValueByMeter{0.0}, + detail::ValueByMeter{0.0}, false, // forward false, // backward false, // roundabout @@ -108,14 +123,16 @@ struct InternalExtractorEdge TRAVEL_MODE_INACCESSIBLE, false, INVALID_LANE_DESCRIPTIONID, - guidance::RoadClassification()); + guidance::RoadClassification(), + util::Coordinate()); } static InternalExtractorEdge max_osm_value() { return InternalExtractorEdge(MAX_OSM_NODEID, MAX_OSM_NODEID, SPECIAL_NODEID, - WeightData(), + detail::ValueByMeter{0.0}, + detail::ValueByMeter{0.0}, false, // forward false, // backward false, // roundabout @@ -124,7 +141,8 @@ struct InternalExtractorEdge TRAVEL_MODE_INACCESSIBLE, false, INVALID_LANE_DESCRIPTIONID, - guidance::RoadClassification()); + guidance::RoadClassification(), + util::Coordinate()); } static InternalExtractorEdge min_internal_value() diff --git a/include/extractor/node_based_edge.hpp b/include/extractor/node_based_edge.hpp index ddd50b6b9..a6d401541 100644 --- a/include/extractor/node_based_edge.hpp +++ b/include/extractor/node_based_edge.hpp @@ -19,6 +19,7 @@ struct NodeBasedEdge NodeID target, NodeID name_id, EdgeWeight weight, + EdgeWeight duration, bool forward, bool backward, bool roundabout, @@ -35,12 +36,13 @@ struct NodeBasedEdge NodeID target; NodeID name_id; EdgeWeight weight; - bool forward : 1; - bool backward : 1; - bool roundabout : 1; - bool circular : 1; - bool startpoint : 1; - bool is_split : 1; + EdgeWeight duration; + std::uint8_t forward : 1; + std::uint8_t backward : 1; + std::uint8_t roundabout : 1; + std::uint8_t circular : 1; + std::uint8_t startpoint : 1; + std::uint8_t is_split : 1; TravelMode travel_mode : 4; LaneDescriptionID lane_description_id; guidance::RoadClassification road_classification; @@ -52,6 +54,7 @@ struct NodeBasedEdgeWithOSM : NodeBasedEdge OSMNodeID target, NodeID name_id, EdgeWeight weight, + EdgeWeight duration, bool forward, bool backward, bool roundabout, @@ -69,9 +72,10 @@ struct NodeBasedEdgeWithOSM : NodeBasedEdge // Impl. inline NodeBasedEdge::NodeBasedEdge() - : source(SPECIAL_NODEID), target(SPECIAL_NODEID), name_id(0), weight(0), forward(false), - backward(false), roundabout(false), circular(false), startpoint(true), is_split(false), - travel_mode(false), lane_description_id(INVALID_LANE_DESCRIPTIONID) + : source(SPECIAL_NODEID), target(SPECIAL_NODEID), name_id(0), weight(0), duration(0), + forward(false), backward(false), roundabout(false), circular(false), startpoint(true), + is_split(false), travel_mode(TRAVEL_MODE_INACCESSIBLE), + lane_description_id(INVALID_LANE_DESCRIPTIONID) { } @@ -79,6 +83,7 @@ inline NodeBasedEdge::NodeBasedEdge(NodeID source, NodeID target, NodeID name_id, EdgeWeight weight, + EdgeWeight duration, bool forward, bool backward, bool roundabout, @@ -88,10 +93,10 @@ inline NodeBasedEdge::NodeBasedEdge(NodeID source, bool is_split, const LaneDescriptionID lane_description_id, guidance::RoadClassification road_classification) - : source(source), target(target), name_id(name_id), weight(weight), forward(forward), - backward(backward), roundabout(roundabout), circular(circular), startpoint(startpoint), - is_split(is_split), travel_mode(travel_mode), lane_description_id(lane_description_id), - road_classification(std::move(road_classification)) + : source(source), target(target), name_id(name_id), weight(weight), duration(duration), + forward(forward), backward(backward), roundabout(roundabout), circular(circular), + startpoint(startpoint), is_split(is_split), travel_mode(travel_mode), + lane_description_id(lane_description_id), road_classification(std::move(road_classification)) { } @@ -116,6 +121,7 @@ inline NodeBasedEdgeWithOSM::NodeBasedEdgeWithOSM(OSMNodeID source, OSMNodeID target, NodeID name_id, EdgeWeight weight, + EdgeWeight duration, bool forward, bool backward, bool roundabout, @@ -129,6 +135,7 @@ inline NodeBasedEdgeWithOSM::NodeBasedEdgeWithOSM(OSMNodeID source, SPECIAL_NODEID, name_id, weight, + duration, forward, backward, roundabout, @@ -142,6 +149,11 @@ inline NodeBasedEdgeWithOSM::NodeBasedEdgeWithOSM(OSMNodeID source, { } +static_assert(sizeof(extractor::NodeBasedEdge) == 28, + "Size of extractor::NodeBasedEdge type is " + "bigger than expected. This will influence " + "memory consumption."); + } // ns extractor } // ns osrm diff --git a/include/extractor/profile_properties.hpp b/include/extractor/profile_properties.hpp index d53bb04f3..d286fbffd 100644 --- a/include/extractor/profile_properties.hpp +++ b/include/extractor/profile_properties.hpp @@ -1,6 +1,8 @@ #ifndef PROFILE_PROPERTIES_HPP #define PROFILE_PROPERTIES_HPP +#include +#include #include namespace osrm @@ -12,11 +14,18 @@ const constexpr auto DEFAULT_MAX_SPEED = 180 / 3.6; // 180kmph -> m/s struct ProfileProperties { + enum + { + MAX_WEIGHT_NAME_LENGTH = 255 + }; + ProfileProperties() : traffic_signal_penalty(0), u_turn_penalty(0), max_speed_for_map_matching(DEFAULT_MAX_SPEED), continue_straight_at_waypoint(true), - use_turn_restrictions(false), left_hand_driving(false) + use_turn_restrictions(false), left_hand_driving(false), fallback_to_duration(true), + weight_name{"duration"} { + BOOST_ASSERT(weight_name[MAX_WEIGHT_NAME_LENGTH] == '\0'); } double GetUturnPenalty() const { return u_turn_penalty / 10.; } @@ -40,14 +49,38 @@ struct ProfileProperties max_speed_for_map_matching = max_speed_for_map_matching_; } + void SetWeightName(const std::string &name) + { + auto count = std::min(name.length(), MAX_WEIGHT_NAME_LENGTH) + 1; + std::copy_n(name.c_str(), count, weight_name); + // Make sure this is always zero terminated + BOOST_ASSERT(weight_name[count - 1] == '\0'); + BOOST_ASSERT(weight_name[MAX_WEIGHT_NAME_LENGTH] == '\0'); + // Set lazy fallback flag + fallback_to_duration = name == "duration"; + } + + std::string GetWeightName() const + { + // Make sure this is always zero terminated + BOOST_ASSERT(weight_name[MAX_WEIGHT_NAME_LENGTH] == '\0'); + return std::string(weight_name); + } + //! penalty to cross a traffic light in deci-seconds - int traffic_signal_penalty; + std::int32_t traffic_signal_penalty; //! penalty to do a uturn in deci-seconds - int u_turn_penalty; + std::int32_t u_turn_penalty; double max_speed_for_map_matching; + //! depending on the profile, force the routing to always continue in the same direction bool continue_straight_at_waypoint; + //! flag used for restriction parser (e.g. used for the walk profile) bool use_turn_restrictions; bool left_hand_driving; + bool fallback_to_duration; + //! stores the name of the weight (e.g. 'duration', 'distance', 'safety') + char weight_name[MAX_WEIGHT_NAME_LENGTH + 1]; + unsigned weight_precision = 1; }; } } diff --git a/include/extractor/scripting_environment.hpp b/include/extractor/scripting_environment.hpp index d7f3bd4f9..865ee6b2e 100644 --- a/include/extractor/scripting_environment.hpp +++ b/include/extractor/scripting_environment.hpp @@ -35,6 +35,8 @@ namespace extractor class RestrictionParser; struct ExtractionNode; struct ExtractionWay; +struct ExtractionTurn; +struct ExtractionSegment; /** * Abstract class that handles processing osmium ways, nodes and relation objects by applying @@ -53,11 +55,9 @@ class ScriptingEnvironment virtual std::vector GetNameSuffixList() = 0; virtual std::vector GetRestrictions() = 0; virtual void SetupSources() = 0; - virtual int32_t GetTurnPenalty(double angle) = 0; - virtual void ProcessSegment(const osrm::util::Coordinate &source, - const osrm::util::Coordinate &target, - double distance, - InternalExtractorEdge::WeightData &weight) = 0; + virtual void ProcessTurn(ExtractionTurn &turn) = 0; + virtual void ProcessSegment(ExtractionSegment &segment) = 0; + virtual void ProcessElements(const std::vector &osm_elements, const RestrictionParser &restriction_parser, diff --git a/include/extractor/scripting_environment_lua.hpp b/include/extractor/scripting_environment_lua.hpp index 9b91031ce..604916f9d 100644 --- a/include/extractor/scripting_environment_lua.hpp +++ b/include/extractor/scripting_environment_lua.hpp @@ -45,7 +45,7 @@ class Sol2ScriptingEnvironment final : public ScriptingEnvironment { public: static const constexpr int SUPPORTED_MIN_API_VERSION = 0; - static const constexpr int SUPPORTED_MAX_API_VERSION = 0; + static const constexpr int SUPPORTED_MAX_API_VERSION = 1; explicit Sol2ScriptingEnvironment(const std::string &file_name); ~Sol2ScriptingEnvironment() override = default; @@ -57,11 +57,9 @@ class Sol2ScriptingEnvironment final : public ScriptingEnvironment std::vector GetNameSuffixList() override; std::vector GetRestrictions() override; void SetupSources() override; - int32_t GetTurnPenalty(double angle) override; - void ProcessSegment(const osrm::util::Coordinate &source, - const osrm::util::Coordinate &target, - double distance, - InternalExtractorEdge::WeightData &weight) override; + void ProcessTurn(ExtractionTurn &turn) override; + void ProcessSegment(ExtractionSegment &segment) override; + void ProcessElements(const std::vector &osm_elements, const RestrictionParser &restriction_parser, diff --git a/include/extractor/travel_mode.hpp b/include/extractor/travel_mode.hpp index fa75c373b..ff36a6724 100644 --- a/include/extractor/travel_mode.hpp +++ b/include/extractor/travel_mode.hpp @@ -28,6 +28,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef TRAVEL_MODE_HPP #define TRAVEL_MODE_HPP +#include + namespace osrm { namespace extractor @@ -35,7 +37,7 @@ namespace extractor // This is a char instead of a typed enum, so that we can // pack it into e.g. a "TravelMode mode : 4" packed bitfield -using TravelMode = unsigned char; +using TravelMode = std::uint8_t; } } diff --git a/include/storage/shared_datatype.hpp b/include/storage/shared_datatype.hpp index e48ad3041..250e69871 100644 --- a/include/storage/shared_datatype.hpp +++ b/include/storage/shared_datatype.hpp @@ -5,6 +5,8 @@ #include "util/exception_utils.hpp" #include "util/log.hpp" +#include + #include #include @@ -33,6 +35,8 @@ const constexpr char *block_id_to_name[] = {"NAME_OFFSETS", "GEOMETRIES_NODE_LIST", "GEOMETRIES_FWD_WEIGHT_LIST", "GEOMETRIES_REV_WEIGHT_LIST", + "GEOMETRIES_FWD_DURATION_LIST", + "GEOMETRIES_REV_DURATION_LIST", "HSGR_CHECKSUM", "TIMESTAMP", "FILE_INDEX_PATH", @@ -52,7 +56,9 @@ const constexpr char *block_id_to_name[] = {"NAME_OFFSETS", "POST_TURN_BEARING", "TURN_LANE_DATA", "LANE_DESCRIPTION_OFFSETS", - "LANE_DESCRIPTION_MASKS"}; + "LANE_DESCRIPTION_MASKS", + "TURN_WEIGHT_PENALTIES", + "TURN_DURATION_PENALTIES"}; struct DataLayout { @@ -75,6 +81,8 @@ struct DataLayout GEOMETRIES_NODE_LIST, GEOMETRIES_FWD_WEIGHT_LIST, GEOMETRIES_REV_WEIGHT_LIST, + GEOMETRIES_FWD_DURATION_LIST, + GEOMETRIES_REV_DURATION_LIST, HSGR_CHECKSUM, TIMESTAMP, FILE_INDEX_PATH, @@ -95,6 +103,8 @@ struct DataLayout TURN_LANE_DATA, LANE_DESCRIPTION_OFFSETS, LANE_DESCRIPTION_MASKS, + TURN_WEIGHT_PENALTIES, + TURN_DURATION_PENALTIES, NUM_BLOCKS }; @@ -127,6 +137,7 @@ struct DataLayout uint64_t result = 0; for (auto i = 0; i < NUM_BLOCKS; i++) { + BOOST_ASSERT(entry_align[i] > 0); result += 2 * sizeof(CANARY) + GetBlockSize((BlockID)i) + entry_align[i]; } return result; diff --git a/include/storage/storage_config.hpp b/include/storage/storage_config.hpp index e68129a11..e27c7ae38 100644 --- a/include/storage/storage_config.hpp +++ b/include/storage/storage_config.hpp @@ -60,6 +60,8 @@ struct StorageConfig final boost::filesystem::path core_data_path; boost::filesystem::path geometries_path; boost::filesystem::path timestamp_path; + boost::filesystem::path turn_weight_penalties_path; + boost::filesystem::path turn_duration_penalties_path; boost::filesystem::path datasource_names_path; boost::filesystem::path datasource_indexes_path; boost::filesystem::path names_data_path; diff --git a/include/util/fingerprint.hpp b/include/util/fingerprint.hpp index aaf89131d..1c99a3a6b 100644 --- a/include/util/fingerprint.hpp +++ b/include/util/fingerprint.hpp @@ -12,9 +12,8 @@ namespace util { // implements a singleton, i.e. there is one and only one conviguration object -class FingerPrint +struct FingerPrint { - public: static FingerPrint GetValid(); bool IsValid() const; @@ -36,6 +35,7 @@ class FingerPrint static_assert(sizeof(FingerPrint) == 8, "FingerPrint has unexpected size"); static_assert(std::is_trivial::value, "FingerPrint needs to be trivial."); +static_assert(std::is_pod::value, "FingerPrint needs to be a POD."); } } diff --git a/include/util/node_based_graph.hpp b/include/util/node_based_graph.hpp index 87321a02b..caf6b3517 100644 --- a/include/util/node_based_graph.hpp +++ b/include/util/node_based_graph.hpp @@ -18,14 +18,15 @@ namespace util struct NodeBasedEdgeData { NodeBasedEdgeData() - : distance(INVALID_EDGE_WEIGHT), edge_id(SPECIAL_NODEID), + : weight(INVALID_EDGE_WEIGHT), duration(INVALID_EDGE_WEIGHT), edge_id(SPECIAL_NODEID), name_id(std::numeric_limits::max()), reversed(false), roundabout(false), circular(false), travel_mode(TRAVEL_MODE_INACCESSIBLE), lane_description_id(INVALID_LANE_DESCRIPTIONID) { } - NodeBasedEdgeData(int distance, + NodeBasedEdgeData(EdgeWeight weight, + EdgeWeight duration, unsigned edge_id, unsigned name_id, bool reversed, @@ -34,13 +35,14 @@ struct NodeBasedEdgeData bool startpoint, extractor::TravelMode travel_mode, const LaneDescriptionID lane_description_id) - : distance(distance), edge_id(edge_id), name_id(name_id), reversed(reversed), - roundabout(roundabout), circular(circular), startpoint(startpoint), + : weight(weight), duration(duration), edge_id(edge_id), name_id(name_id), + reversed(reversed), roundabout(roundabout), circular(circular), startpoint(startpoint), travel_mode(travel_mode), lane_description_id(lane_description_id) { } - int distance; + EdgeWeight weight; + EdgeWeight duration; unsigned edge_id; unsigned name_id; bool reversed : 1; @@ -78,9 +80,8 @@ NodeBasedDynamicGraphFromEdges(NodeID number_of_nodes, input_edge_list, [](NodeBasedDynamicGraph::InputEdge &output_edge, const extractor::NodeBasedEdge &input_edge) { - output_edge.data.distance = static_cast(input_edge.weight); - BOOST_ASSERT(output_edge.data.distance > 0); - + output_edge.data.weight = input_edge.weight; + output_edge.data.duration = input_edge.duration; output_edge.data.roundabout = input_edge.roundabout; output_edge.data.circular = input_edge.circular; output_edge.data.name_id = input_edge.name_id; @@ -88,6 +89,9 @@ NodeBasedDynamicGraphFromEdges(NodeID number_of_nodes, output_edge.data.startpoint = input_edge.startpoint; output_edge.data.road_classification = input_edge.road_classification; output_edge.data.lane_description_id = input_edge.lane_description_id; + + BOOST_ASSERT(output_edge.data.weight > 0); + BOOST_ASSERT(output_edge.data.duration > 0); }); tbb::parallel_sort(edges_list.begin(), edges_list.end()); diff --git a/include/util/typedefs.hpp b/include/util/typedefs.hpp index ce5dbd3c5..caf0fbfba 100644 --- a/include/util/typedefs.hpp +++ b/include/util/typedefs.hpp @@ -58,6 +58,7 @@ using NodeID = std::uint32_t; using EdgeID = std::uint32_t; using NameID = std::uint32_t; using EdgeWeight = std::int32_t; +using TurnPenalty = std::int16_t; // turn penalty in 100ms units using LaneID = std::uint8_t; static const LaneID INVALID_LANEID = std::numeric_limits::max(); @@ -83,6 +84,7 @@ static const NameID INVALID_NAMEID = std::numeric_limits::max(); static const NameID EMPTY_NAMEID = 0; static const unsigned INVALID_COMPONENTID = 0; static const EdgeWeight INVALID_EDGE_WEIGHT = std::numeric_limits::max(); +static const TurnPenalty INVALID_TURN_PENALTY = std::numeric_limits::max(); using DatasourceID = std::uint8_t; diff --git a/profiles/bicycle.lua b/profiles/bicycle.lua index 3f55d755d..2c16f33ef 100644 --- a/profiles/bicycle.lua +++ b/profiles/bicycle.lua @@ -1,4 +1,4 @@ -api_version = 0 +api_version = 1 -- Bicycle profile @@ -92,14 +92,16 @@ surface_speeds = { } -- these need to be global because they are accesed externaly -properties.traffic_signal_penalty = 2 -properties.u_turn_penalty = 20 properties.max_speed_for_map_matching = 110/3.6 -- kmph -> m/s properties.use_turn_restrictions = false properties.continue_straight_at_waypoint = false +properties.weight_name = 'duration' +--properties.weight_name = 'cyclability' local obey_oneway = true local ignore_areas = true +local traffic_light_penalty = 2 +local u_turn_penalty = 20 local turn_penalty = 6 local turn_bias = 1.4 -- reduce the driving speed by 30% for unsafe roads @@ -272,10 +274,6 @@ function way_function (way, result) -- regular ways result.forward_speed = bicycle_speeds[highway] result.backward_speed = bicycle_speeds[highway] - if safety_penalty < 1 and unsafe_highway_list[highway] then - result.forward_speed = result.forward_speed * safety_penalty - result.backward_speed = result.backward_speed * safety_penalty - end elseif access and access_tag_whitelist[access] then -- unknown way, but valid access tag result.forward_speed = default_speed @@ -399,15 +397,45 @@ function way_function (way, result) -- maxspeed limit( result, maxspeed, maxspeed_forward, maxspeed_backward ) -end -function turn_function (angle) - -- compute turn penalty as angle^2, with a left/right bias - -- multiplying by 10 converts to deci-seconds see issue #1318 - k = 10*turn_penalty/(90.0*90.0) - if angle>=0 then - return angle*angle*k/turn_bias - else - return angle*angle*k*turn_bias + -- convert duration into cyclability + local is_unsafe = safety_penalty < 1 and unsafe_highway_list[highway] + if result.forward_speed > 0 then + -- convert from km/h to m/s + result.forward_rate = result.forward_speed / 3.6; + if is_unsafe then + result.forward_rate = result.forward_rate * safety_penalty + end + end + if result.backward_speed > 0 then + -- convert from km/h to m/s + result.backward_rate = result.backward_speed / 3.6; + if is_unsafe then + result.backward_rate = result.backward_rate * safety_penalty + end + end + if result.duration > 0 then + result.weight = result.duration; + if is_unsafe then + result.weight = result.weight * (1+safety_penalty) + end + end +end + +function turn_function(turn) + -- compute turn penalty as angle^2, with a left/right bias + local normalized_angle = turn.angle / 90.0 + if normalized_angle >= 0.0 then + turn.duration = normalized_angle * normalized_angle * turn_penalty / turn_bias + else + turn.duration = normalized_angle * normalized_angle * turn_penalty * turn_bias + end + + if turn.direction_modifier == direction_modifier.uturn then + turn.duration = turn.duration + u_turn_penalty + end + + if turn.has_traffic_light then + turn.duration = turn.duration + traffic_light_penalty end end diff --git a/profiles/car.lua b/profiles/car.lua index 8f93272cd..062a4465c 100644 --- a/profiles/car.lua +++ b/profiles/car.lua @@ -1,4 +1,4 @@ -api_version = 0 +api_version = 1 -- Car profile local find_access_tag = require("lib/access").find_access_tag @@ -190,16 +190,19 @@ maxspeed_table = { } -- set profile properties -properties.u_turn_penalty = 20 -properties.traffic_signal_penalty = 2 properties.max_speed_for_map_matching = 180/3.6 -- 180kmph -> m/s properties.use_turn_restrictions = true properties.continue_straight_at_waypoint = true properties.left_hand_driving = false +-- this will use the duration and {forward/backward}_speed values as weight +properties.weight_name = 'duration' +--properties.weight_name = 'distance' local side_road_speed_multiplier = 0.8 - local turn_penalty = 7.5 +local traffic_light_penalty = 2 +local u_turn_penalty = 20 + -- Note: this biases right-side driving. Should be -- inverted for left-driving countries. local turn_bias = properties.left_hand_driving and 1/1.075 or 1.075 @@ -335,7 +338,7 @@ function is_way_blocked(way,result) if ignore_areas and "yes" == area then return false end - + -- respect user-preference for toll=yes ways local toll = way:get_value_by_key("toll") if ignore_toll_ways and "yes" == toll then @@ -445,7 +448,7 @@ function handle_speed(way,result,data) if -1 == result.forward_speed and -1 == result.backward_speed then return false end - + if handle_side_roads(way,result) == false then return false end if handle_surface(way,result) == false then return false end if handle_maxspeed(way,data,result) == false then return false end @@ -454,10 +457,9 @@ function handle_speed(way,result,data) end -- reduce speed on special side roads -function handle_side_roads(way,result) +function handle_side_roads(way,result) local sideway = way:get_value_by_key("side_road") - if "yes" == sideway or - "rotary" == sideway then + if "yes" == sideway or "rotary" == sideway then result.forward_speed = result.forward_speed * side_road_speed_multiplier result.backward_speed = result.backward_speed * side_road_speed_multiplier end @@ -562,7 +564,7 @@ function handle_speed_scaling(way,result) end end - local is_bidirectional = result.forward_mode ~= mode.inaccessible and + local is_bidirectional = result.forward_mode ~= mode.inaccessible and result.backward_mode ~= mode.inaccessible local service = way:get_value_by_key("service") @@ -605,7 +607,7 @@ function handle_oneway(way,result,data) else local junction = way:get_value_by_key("junction") if data.highway == "motorway" or - junction == "roundabout" or + junction == "roundabout" or junction == "circular" then if oneway ~= "no" then -- implied oneway @@ -659,7 +661,7 @@ end -- determine if this way can be used as a start/end point for routing function handle_startpoint(way,result) -- only allow this road as start point if it not a ferry - result.is_startpoint = result.forward_mode == mode.driving or + result.is_startpoint = result.forward_mode == mode.driving or result.backward_mode == mode.driving end @@ -678,7 +680,7 @@ function way_function(way, result) -- unnecessary work. this implies we should check things that -- commonly forbids access early, and handle complicated edge -- cases later. - + -- perform an quick initial check and abort if way is obviously -- not routable, e.g. because it does not have any of the key -- tags indicating routability @@ -728,14 +730,30 @@ function way_function(way, result) if handle_names(way,result) == false then return end end -function turn_function (angle) +function turn_function (turn) -- Use a sigmoid function to return a penalty that maxes out at turn_penalty -- over the space of 0-180 degrees. Values here were chosen by fitting -- the function to some turn penalty samples from real driving. - -- multiplying by 10 converts to deci-seconds see issue #1318 - if angle>=0 then - return 10 * turn_penalty / (1 + 2.718 ^ - ((13 / turn_bias) * angle/180 - 6.5*turn_bias)) - else - return 10 * turn_penalty / (1 + 2.718 ^ - ((13 * turn_bias) * - angle/180 - 6.5/turn_bias)) + if turn.turn_type ~= turn_type.no_turn then + if turn.angle >= 0 then + turn.duration = turn_penalty / (1 + math.exp( -((13 / turn_bias) * turn.angle/180 - 6.5*turn_bias))) + else + turn.duration = turn_penalty / (1 + math.exp( -((13 * turn_bias) * -turn.angle/180 - 6.5/turn_bias))) + end + + if turn.direction_modifier == direction_modifier.u_turn then + turn.duration = turn.duration + u_turn_penalty + end + + if turn.has_traffic_light then + turn.duration = turn.duration + traffic_light_penalty + end + + -- for distance based routing we don't want to have penalties based on turn angle + if properties.weight_name == 'distance' then + turn.weight = 0 + else + turn.weight = turn.duration + end end end diff --git a/profiles/examples/postgis.lua b/profiles/examples/postgis.lua index d30f35b1a..cfdbb2831 100644 --- a/profiles/examples/postgis.lua +++ b/profiles/examples/postgis.lua @@ -36,7 +36,6 @@ obey_oneway = true use_restrictions = true ignore_areas = true -- future feature traffic_signal_penalty = 7 -- seconds -u_turn_penalty = 20 -- nodes processing, called from OSRM function node_function(node) diff --git a/profiles/foot.lua b/profiles/foot.lua index 92c0fb7ee..f57b0a21e 100644 --- a/profiles/foot.lua +++ b/profiles/foot.lua @@ -1,4 +1,4 @@ -api_version = 0 +api_version = 1 -- Foot profile local find_access_tag = require("lib/access").find_access_tag @@ -21,16 +21,16 @@ barrier_whitelist = Set { 'block' } access_tag_whitelist = Set { - 'yes', - 'foot', - 'permissive', - 'designated' + 'yes', + 'foot', + 'permissive', + 'designated' } access_tag_blacklist = Set { - 'no', - 'private', - 'agricultural', - 'forestry', + 'no', + 'private', + 'agricultural', + 'forestry', 'delivery' } @@ -103,11 +103,13 @@ leisure_speeds = { track = walking_speed } -properties.traffic_signal_penalty = 2 -properties.u_turn_penalty = 2 properties.max_speed_for_map_matching = 40/3.6 -- kmph -> m/s properties.use_turn_restrictions = false properties.continue_straight_at_waypoint = false +properties.weight_name = 'duration' + +local traffic_light_penalty = 2 +local u_turn_penalty = 2 @@ -193,7 +195,7 @@ function initial_routability_check(way,result,data) data.platform = way:get_value_by_key("platform") data.amenity = way:get_value_by_key("amenity") data.public_transport = way:get_value_by_key("public_transport") - + return data.highway ~= nil or data.leisure ~= nil or data.route ~= nil or @@ -293,14 +295,14 @@ function handle_speed(way,result,data) amenity_speeds[data.amenity] or man_made_speeds[data.man_made] or leisure_speeds[data.leisure] - + if speed then -- set speed by way type result.forward_speed = highway_speed result.backward_speed = highway_speed result.forward_speed = speed result.backward_speed = speed - else + else -- Set the avg speed on ways that are marked accessible if access_tag_whitelist[data.forward_access] then result.forward_speed = speed_profile["default"] @@ -315,7 +317,7 @@ function handle_speed(way,result,data) if -1 == result.forward_speed and -1 == result.backward_speed then return false end - + if handle_surface(way,result) == false then return false end end @@ -398,9 +400,9 @@ function handle_oneway(way,result,data) elseif oneway_handling == 'specific' then oneway = Directional.get_value_by_prefixed_sequence(way,restrictions,'oneway') end - + data.oneway = oneway - + if oneway then if oneway == "-1" then data.is_reverse_oneway = true @@ -413,7 +415,7 @@ function handle_oneway(way,result,data) else local junction = way:get_value_by_key("junction") if data.highway == "motorway" or - junction == "roundabout" or + junction == "roundabout" or junction == "circular" then if oneway ~= "no" then -- implied oneway @@ -436,7 +438,7 @@ end -- determine if this way can be used as a start/end point for routing function handle_startpoint(way,result) -- only allow this road as start point if it not a ferry - result.is_startpoint = result.forward_mode == mode.walking or + result.is_startpoint = result.forward_mode == mode.walking or result.backward_mode == mode.walking end @@ -455,7 +457,7 @@ function way_function(way, result) -- unnecessary work. this implies we should check things that -- commonly forbids access early, and handle complicated edge -- cases later. - + -- perform an quick initial check and abort if way is obviously -- not routable, e.g. because it does not have any of the key -- tags indicating routability @@ -497,3 +499,15 @@ function way_function(way, result) -- set name, ref and pronunciation if handle_names(way,result) == false then return end end + +function turn_function (turn) + turn.duration = 0. + + if turn.direction_modifier == direction_modifier.u_turn then + turn.duration = turn.duration + u_turn_penalty + end + + if turn.has_traffic_light then + turn.duration = traffic_light_penalty + end +end diff --git a/profiles/rasterbot.lua b/profiles/rasterbot.lua index 69e709616..d3c024612 100644 --- a/profiles/rasterbot.lua +++ b/profiles/rasterbot.lua @@ -1,4 +1,4 @@ -api_version = 0 +api_version = 1 -- Rasterbot profile -- Minimalist node_ and way_functions in order to test source_ and segment_functions @@ -37,18 +37,25 @@ function source_function () ) end -function segment_function (source, target, distance, weight) - local sourceData = sources:query(raster_source, source.lon, source.lat) - local targetData = sources:query(raster_source, target.lon, target.lat) +function segment_function (segment) + local sourceData = sources:query(raster_source, segment.source.lon, segment.source.lat) + local targetData = sources:query(raster_source, segment.target.lon, segment.target.lat) io.write("evaluating segment: " .. sourceData.datum .. " " .. targetData.datum .. "\n") local invalid = sourceData.invalid_data() + local scaled_weight = segment.weight + local scaled_duration = segment.duration if sourceData.datum ~= invalid and targetData.datum ~= invalid then - local slope = math.abs(sourceData.datum - targetData.datum) / distance + local slope = math.abs(sourceData.datum - targetData.datum) / segment.distance + scaled_weight = scaled_weight / (1.0 - (slope * 5.0)) + scaled_duration = scaled_duration / (1.0 - (slope * 5.0)) io.write(" slope: " .. slope .. "\n") - io.write(" was speed: " .. weight.speed .. "\n") - - weight.speed = weight.speed * (1 - (slope * 5)) - io.write(" new speed: " .. weight.speed .. "\n") + io.write(" was weight: " .. segment.weight .. "\n") + io.write(" new weight: " .. scaled_weight .. "\n") + io.write(" was duration: " .. segment.duration .. "\n") + io.write(" new duration: " .. scaled_duration .. "\n") end + + segment.weight = scaled_weight + segment.duration = scaled_duration end diff --git a/profiles/rasterbotinterp.lua b/profiles/rasterbotinterp.lua index e75c808d8..e3dcbb74d 100644 --- a/profiles/rasterbotinterp.lua +++ b/profiles/rasterbotinterp.lua @@ -1,4 +1,4 @@ -api_version = 0 +api_version = 1 -- Rasterbot profile -- Minimalist node_ and way_functions in order to test source_ and segment_functions @@ -37,18 +37,25 @@ function source_function () ) end -function segment_function (source, target, distance, weight) - local sourceData = sources:interpolate(raster_source, source.lon, source.lat) - local targetData = sources:interpolate(raster_source, target.lon, target.lat) +function segment_function (segment) + local sourceData = sources:interpolate(raster_source, segment.source.lon, segment.source.lat) + local targetData = sources:interpolate(raster_source, segment.target.lon, segment.target.lat) io.write("evaluating segment: " .. sourceData.datum .. " " .. targetData.datum .. "\n") local invalid = sourceData.invalid_data() + local scaled_weight = segment.weight + local scaled_duration = segment.duration if sourceData.datum ~= invalid and targetData.datum ~= invalid then - local slope = math.abs(sourceData.datum - targetData.datum) / distance + local slope = math.abs(sourceData.datum - targetData.datum) / segment.distance io.write(" slope: " .. slope .. "\n") - io.write(" was speed: " .. weight.speed .. "\n") + io.write(" was weight: " .. segment.weight .. "\n") + io.write(" was speed: " .. segment.duration .. "\n") - weight.speed = weight.speed * (1 - (slope * 5)) - io.write(" new speed: " .. weight.speed .. "\n") + scaled_weight = scaled_weight / (1 - (slope * 5)) + io.write(" new weight: " .. scaled_weight .. "\n") + scaled_duration = scaled_duration / (1 - (slope * 5)) + io.write(" new speed: " .. scaled_duration .. "\n") end + segment.weight = scaled_weight + segment.duration = scaled_duration end diff --git a/profiles/testbot.lua b/profiles/testbot.lua index 004a345a8..9321ffc0e 100644 --- a/profiles/testbot.lua +++ b/profiles/testbot.lua @@ -1,4 +1,4 @@ -api_version = 0 +api_version = 1 -- Testbot profile -- Moves at fixed, well-known speeds, practical for testing speed and travel times: @@ -19,9 +19,11 @@ speed_profile = { properties.continue_straight_at_waypoint = true properties.use_turn_restrictions = true -properties.traffic_signal_penalty = 7 -- seconds -properties.u_turn_penalty = 20 properties.max_speed_for_map_matching = 30/3.6 --km -> m/s +properties.weight_name = 'duration' + +local uturn_penalty = 20 +local traffic_light_penalty = 7 -- seconds function limit_speed(speed, limits) -- don't use ipairs(), since it stops at the first nil value @@ -114,3 +116,13 @@ function way_function (way, result) result.roundabout = true end end + +function turn_function (turn) + if turn.direction_modifier == direction_modifier.uturn then + turn.duration = uturn_penalty + turn.weight = uturn_penalty + end + if turn.has_traffic_light then + turn.duration = turn.duration + traffic_light_penalty + end +end diff --git a/profiles/turnbot.lua b/profiles/turnbot.lua index dc6a09fb4..cf8a81ecf 100644 --- a/profiles/turnbot.lua +++ b/profiles/turnbot.lua @@ -1,11 +1,10 @@ -api_version = 0 +api_version = 1 -- Testbot, with turn penalty -- Used for testing turn penalties require 'testbot' -function turn_function (angle) - -- multiplying by 10 converts to deci-seconds see issue #1318 - return 10*20*math.abs(angle)/180 +function turn_function (turn) + turn.duration = 20 * math.abs(turn.angle) / 180 end diff --git a/src/contractor/contractor.cpp b/src/contractor/contractor.cpp index 398634dd4..b1f9bbd99 100644 --- a/src/contractor/contractor.cpp +++ b/src/contractor/contractor.cpp @@ -74,7 +74,9 @@ struct Segment final struct SpeedSource final { + SpeedSource() : speed(0), weight(INVALID_EDGE_WEIGHT) {} unsigned speed; + EdgeWeight weight; std::uint8_t source; }; @@ -86,7 +88,7 @@ struct Turn final : from(from), via(via), to(to) { } - Turn(const osrm::extractor::lookup::PenaltyBlock &turn) + Turn(const osrm::extractor::lookup::TurnIndexBlock &turn) : from(static_cast(turn.from_id)), via(static_cast(turn.via_id)), to(static_cast(turn.to_id)) { @@ -103,7 +105,9 @@ struct Turn final struct PenaltySource final { - double penalty; + PenaltySource() : duration(0.), weight(std::numeric_limits::quiet_NaN()) {} + double duration; + double weight; std::uint8_t source; }; @@ -115,11 +119,15 @@ template inline bool is_aligned(const void *pointer) } // anon ns BOOST_FUSION_ADAPT_STRUCT(Segment, (decltype(Segment::from), from)(decltype(Segment::to), to)) -BOOST_FUSION_ADAPT_STRUCT(SpeedSource, (decltype(SpeedSource::speed), speed)) +BOOST_FUSION_ADAPT_STRUCT(SpeedSource, + (decltype(SpeedSource::speed), speed)(decltype(SpeedSource::weight), + weight)) BOOST_FUSION_ADAPT_STRUCT(Turn, (decltype(Turn::from), from)(decltype(Turn::via), via)(decltype(Turn::to), to)) -BOOST_FUSION_ADAPT_STRUCT(PenaltySource, (decltype(PenaltySource::penalty), penalty)) +BOOST_FUSION_ADAPT_STRUCT(PenaltySource, + (decltype(PenaltySource::duration), + duration)(decltype(PenaltySource::weight), weight)) namespace osrm { @@ -240,29 +248,37 @@ inline EdgeWeight ConvertToDuration(double distance_in_meters, double speed_in_k BOOST_ASSERT(speed_in_kmh > 0); const double speed_in_ms = speed_in_kmh / 3.6; const double duration = distance_in_meters / speed_in_ms; - return std::max(1, static_cast(std::round(duration * 10))); + return std::max(1, static_cast(std::round(duration * 10.))); } // Returns updated edge weight -EdgeWeight GetNewWeight(const SpeedSource &value, - const double &segment_length, - const std::vector &segment_speed_filenames, - const EdgeWeight old_weight, - const double log_edge_updates_factor, - const OSMNodeID &from, - const OSMNodeID &to) +void GetNewWeight(const SpeedSource &value, + const double &segment_length, + const std::vector &segment_speed_filenames, + const EdgeWeight current_duration, + const double log_edge_updates_factor, + const OSMNodeID from, + const OSMNodeID to, + EdgeWeight &new_segment_weight, + EdgeWeight &new_segment_duration) { - const auto new_segment_weight = + // Update the edge duration as distance/speed + new_segment_duration = (value.speed > 0) ? ConvertToDuration(segment_length, value.speed) : INVALID_EDGE_WEIGHT; - // the check here is enabled by the `--edge-weight-updates-over-factor` flag it logs - // a warning if the new weight exceeds a heuristic of what a reasonable weight update is - if (log_edge_updates_factor > 0 && old_weight != 0) + + // Update the edge weight or fallback to the new edge duration + new_segment_weight = + (value.weight == INVALID_EDGE_WEIGHT) ? new_segment_duration : value.weight; + + // The check here is enabled by the `--edge-weight-updates-over-factor` flag it logs a warning + // if the new duration exceeds a heuristic of what a reasonable duration update is + if (log_edge_updates_factor > 0 && current_duration != 0) { - auto new_secs = new_segment_weight / 10.0; - auto old_secs = old_weight / 10.0; - auto approx_original_speed = (segment_length / old_secs) * 3.6; - if (old_weight >= (new_segment_weight * log_edge_updates_factor)) + if (current_duration >= (new_segment_duration * log_edge_updates_factor)) { + auto new_secs = new_segment_duration / 10.; + auto old_secs = current_duration / 10.; + auto approx_original_speed = (segment_length / old_secs) * 3.6; auto speed_file = segment_speed_filenames.at(value.source - 1); util::Log(logWARNING) << "[weight updates] Edge weight update from " << old_secs << "s to " << new_secs << "s New speed: " << value.speed @@ -273,21 +289,10 @@ EdgeWeight GetNewWeight(const SpeedSource &value, << speed_file; } } - - return new_segment_weight; } int Contractor::Run() { -#ifdef WIN32 -#pragma message("Memory consumption on Windows can be higher due to different bit packing") -#else - static_assert(sizeof(extractor::NodeBasedEdge) == 24, - "changing extractor::NodeBasedEdge type has influence on memory consumption!"); - static_assert(sizeof(extractor::EdgeBasedEdge) == 16, - "changing EdgeBasedEdge type has influence on memory consumption!"); -#endif - if (config.core_factor > 1.0 || config.core_factor < 0) { throw util::exception("Core factor must be between 0.0 to 1.0 (inclusive)" + SOURCE_REF); @@ -314,7 +319,9 @@ int Contractor::Run() edge_based_edge_list, node_weights, config.edge_segment_lookup_path, - config.edge_penalty_path, + config.turn_weight_penalties_path, + config.turn_duration_penalties_path, + config.turn_penalties_index_path, config.segment_speed_lookup_paths, config.turn_penalty_lookup_paths, config.node_based_graph_path, @@ -377,7 +384,9 @@ Contractor::LoadEdgeExpandedGraph(std::string const &edge_based_graph_filename, std::vector &edge_based_edge_list, std::vector &node_weights, const std::string &edge_segment_lookup_filename, - const std::string &edge_penalty_filename, + const std::string &turn_weight_penalties_filename, + const std::string &turn_duration_penalties_filename, + const std::string &turn_penalties_index_filename, const std::vector &segment_speed_filenames, const std::vector &turn_penalty_filenames, const std::string &nodes_filename, @@ -393,15 +402,14 @@ Contractor::LoadEdgeExpandedGraph(std::string const &edge_based_graph_filename, util::Log() << "Opening " << edge_based_graph_filename; - auto mmap_file = [](const std::string &filename) { + auto mmap_file = [](const std::string &filename, boost::interprocess::mode_t mode) { using boost::interprocess::file_mapping; using boost::interprocess::mapped_region; - using boost::interprocess::read_only; try { - const file_mapping mapping{filename.c_str(), read_only}; - mapped_region region{mapping, read_only}; + const file_mapping mapping{filename.c_str(), mode}; + mapped_region region{mapping, mode}; region.advise(mapped_region::advice_sequential); return region; } @@ -412,15 +420,16 @@ Contractor::LoadEdgeExpandedGraph(std::string const &edge_based_graph_filename, } }; - const auto edge_based_graph_region = mmap_file(edge_based_graph_filename); + const auto edge_based_graph_region = + mmap_file(edge_based_graph_filename, boost::interprocess::read_only); const bool update_edge_weights = !segment_speed_filenames.empty(); const bool update_turn_penalties = !turn_penalty_filenames.empty(); - const auto edge_penalty_region = [&] { + const auto turn_penalties_index_region = [&] { if (update_edge_weights || update_turn_penalties) { - return mmap_file(edge_penalty_filename); + return mmap_file(turn_penalties_index_filename, boost::interprocess::read_only); } return boost::interprocess::mapped_region(); }(); @@ -428,7 +437,7 @@ Contractor::LoadEdgeExpandedGraph(std::string const &edge_based_graph_filename, const auto edge_segment_region = [&] { if (update_edge_weights || update_turn_penalties) { - return mmap_file(edge_segment_lookup_filename); + return mmap_file(edge_segment_lookup_filename, boost::interprocess::read_only); } return boost::interprocess::mapped_region(); }(); @@ -474,12 +483,13 @@ Contractor::LoadEdgeExpandedGraph(std::string const &edge_based_graph_filename, util::Log() << "Reading " << graph_header.number_of_edges << " edges from the edge based graph"; auto segment_speed_lookup = CSVFilesParser( - 1, qi::ulong_long >> ',' >> qi::ulong_long, qi::uint_)(segment_speed_filenames); + 1, qi::ulong_long >> ',' >> qi::ulong_long, qi::uint_ >> -(',' >> qi::uint_))( + segment_speed_filenames); auto turn_penalty_lookup = CSVFilesParser( 1 + segment_speed_filenames.size(), qi::ulong_long >> ',' >> qi::ulong_long >> ',' >> qi::ulong_long, - qi::double_)(turn_penalty_filenames); + qi::double_ >> -(',' >> qi::double_))(turn_penalty_filenames); // If we update the edge weights, this file will hold the datasource information for each // segment; the other files will also be conditionally filled concurrently if we make an update @@ -490,9 +500,11 @@ Contractor::LoadEdgeExpandedGraph(std::string const &edge_based_graph_filename, std::vector geometry_node_list; std::vector geometry_fwd_weight_list; std::vector geometry_rev_weight_list; + std::vector geometry_fwd_duration_list; + std::vector geometry_rev_duration_list; const auto maybe_load_internal_to_external_node_map = [&] { - if (!(update_edge_weights || update_turn_penalties)) + if (!update_edge_weights) return; storage::io::FileReader nodes_file(nodes_filename, @@ -503,7 +515,7 @@ Contractor::LoadEdgeExpandedGraph(std::string const &edge_based_graph_filename, }; const auto maybe_load_geometries = [&] { - if (!(update_edge_weights || update_turn_penalties)) + if (!update_edge_weights) return; storage::io::FileReader geometry_file(geometry_filename, @@ -518,6 +530,8 @@ Contractor::LoadEdgeExpandedGraph(std::string const &edge_based_graph_filename, geometry_node_list.resize(number_of_compressed_geometries); geometry_fwd_weight_list.resize(number_of_compressed_geometries); geometry_rev_weight_list.resize(number_of_compressed_geometries); + geometry_fwd_duration_list.resize(number_of_compressed_geometries); + geometry_rev_duration_list.resize(number_of_compressed_geometries); if (number_of_compressed_geometries > 0) { @@ -526,13 +540,17 @@ Contractor::LoadEdgeExpandedGraph(std::string const &edge_based_graph_filename, number_of_compressed_geometries); geometry_file.ReadInto(geometry_rev_weight_list.data(), number_of_compressed_geometries); + geometry_file.ReadInto(geometry_fwd_duration_list.data(), + number_of_compressed_geometries); + geometry_file.ReadInto(geometry_rev_duration_list.data(), + number_of_compressed_geometries); } }; // Folds all our actions into independently concurrently executing lambdas tbb::parallel_invoke(maybe_load_internal_to_external_node_map, maybe_load_geometries); - if (update_edge_weights || update_turn_penalties) + if (update_edge_weights) { // Here, we have to update the compressed geometry weights // First, we need the external-to-internal node lookup table @@ -552,7 +570,7 @@ Contractor::LoadEdgeExpandedGraph(std::string const &edge_based_graph_filename, using boost::interprocess::mapped_region; - auto region = mmap_file(rtree_leaf_filename.c_str()); + auto region = mmap_file(rtree_leaf_filename.c_str(), boost::interprocess::read_only); region.advise(mapped_region::advice_willneed); BOOST_ASSERT(is_aligned(region.get_address())); @@ -589,32 +607,38 @@ Contractor::LoadEdgeExpandedGraph(std::string const &edge_based_graph_filename, auto fwd_source = LUA_SOURCE, rev_source = LUA_SOURCE; if (auto value = segment_speed_lookup({u.node_id, v.node_id})) { - const auto current_fwd_weight = geometry_fwd_weight_list[u_index]; - const auto new_segment_weight = GetNewWeight(*value, - segment_length, - segment_speed_filenames, - current_fwd_weight, - log_edge_updates_factor, - u.node_id, - v.node_id); + EdgeWeight new_segment_weight, new_segment_duration; + GetNewWeight(*value, + segment_length, + segment_speed_filenames, + geometry_fwd_duration_list[u_index], + log_edge_updates_factor, + u.node_id, + v.node_id, + new_segment_weight, + new_segment_duration); geometry_fwd_weight_list[v_index] = new_segment_weight; + geometry_fwd_duration_list[v_index] = new_segment_duration; geometry_datasource[v_index] = value->source; fwd_source = value->source; } if (auto value = segment_speed_lookup({v.node_id, u.node_id})) { - const auto current_rev_weight = geometry_rev_weight_list[u_index]; - const auto new_segment_weight = GetNewWeight(*value, - segment_length, - segment_speed_filenames, - current_rev_weight, - log_edge_updates_factor, - v.node_id, - u.node_id); + EdgeWeight new_segment_weight, new_segment_duration; + GetNewWeight(*value, + segment_length, + segment_speed_filenames, + geometry_rev_duration_list[u_index], + log_edge_updates_factor, + v.node_id, + u.node_id, + new_segment_weight, + new_segment_duration); geometry_rev_weight_list[u_index] = new_segment_weight; + geometry_rev_duration_list[u_index] = new_segment_duration; geometry_datasource[u_index] = value->source; rev_source = value->source; } @@ -652,7 +676,7 @@ Contractor::LoadEdgeExpandedGraph(std::string const &edge_based_graph_filename, } const auto maybe_save_geometries = [&] { - if (!(update_edge_weights || update_turn_penalties)) + if (!update_edge_weights) return; // Now save out the updated compressed geometries @@ -675,6 +699,10 @@ Contractor::LoadEdgeExpandedGraph(std::string const &edge_based_graph_filename, number_of_compressed_geometries * sizeof(EdgeWeight)); geometry_stream.write(reinterpret_cast(&(geometry_rev_weight_list[0])), number_of_compressed_geometries * sizeof(EdgeWeight)); + geometry_stream.write(reinterpret_cast(&(geometry_fwd_duration_list[0])), + number_of_compressed_geometries * sizeof(EdgeWeight)); + geometry_stream.write(reinterpret_cast(&(geometry_rev_duration_list[0])), + number_of_compressed_geometries * sizeof(EdgeWeight)); }; const auto save_datasource_indexes = [&] { @@ -716,17 +744,47 @@ Contractor::LoadEdgeExpandedGraph(std::string const &edge_based_graph_filename, tbb::parallel_invoke(maybe_save_geometries, save_datasource_indexes, save_datastore_names); - auto turn_penalty_blocks_ptr = reinterpret_cast( - edge_penalty_region.get_address()); - BOOST_ASSERT(is_aligned(turn_penalty_blocks_ptr)); + std::vector turn_weight_penalties; + std::vector turn_duration_penalties; - auto edge_based_edge_ptr = reinterpret_cast( + const auto maybe_load_turn_weight_penalties = [&] { + if (!update_edge_weights && !update_turn_penalties) + return; + using storage::io::FileReader; + FileReader file(turn_weight_penalties_filename, FileReader::HasNoFingerprint); + file.DeserializeVector(turn_weight_penalties); + }; + + const auto maybe_load_turn_duration_penalties = [&] { + if (!update_turn_penalties) + return; + using storage::io::FileReader; + FileReader file(turn_duration_penalties_filename, FileReader::HasNoFingerprint); + file.DeserializeVector(turn_duration_penalties); + }; + + tbb::parallel_invoke(maybe_load_turn_weight_penalties, maybe_load_turn_duration_penalties); + + if (update_turn_penalties && turn_duration_penalties.empty()) + { // Copy-on-write for duration penalties as turn weight penalties + turn_duration_penalties = turn_weight_penalties; + } + + // Mapped file pointer for turn indices + const extractor::lookup::TurnIndexBlock *turn_index_blocks = + reinterpret_cast( + turn_penalties_index_region.get_address()); + BOOST_ASSERT(is_aligned(turn_index_blocks)); + + // Mapped file pointers for edge-based graph edges + auto edge_based_edge_ptr = reinterpret_cast( reinterpret_cast(edge_based_graph_region.get_address()) + sizeof(EdgeBasedGraphHeader)); BOOST_ASSERT(is_aligned(edge_based_edge_ptr)); auto edge_segment_byte_ptr = reinterpret_cast(edge_segment_region.get_address()); + bool fallback_to_duration = true; for (std::uint64_t edge_index = 0; edge_index < graph_header.number_of_edges; ++edge_index) { // Make a copy of the data from the memory map @@ -759,7 +817,10 @@ Contractor::LoadEdgeExpandedGraph(std::string const &edge_based_graph_filename, if (value->speed == 0) return true; - segment_weight = ConvertToDuration(segment.segment_length, value->speed); + segment_weight = + value->weight == INVALID_EDGE_WEIGHT + ? ConvertToDuration(segment.segment_length, value->speed) + : value->weight; } // Update the edge weight and the next OSM node ID @@ -779,30 +840,61 @@ Contractor::LoadEdgeExpandedGraph(std::string const &edge_based_graph_filename, continue; // Get the turn penalty and update to the new value if required - const auto &turn_block = turn_penalty_blocks_ptr[edge_index]; - EdgeWeight new_turn_penalty = turn_block.fixed_penalty; - if (auto value = turn_penalty_lookup(turn_block)) + const auto &turn_index = turn_index_blocks[edge_index]; + auto turn_weight_penalty = turn_weight_penalties[edge_index]; + if (auto value = turn_penalty_lookup(turn_index)) { - new_turn_penalty = static_cast(value->penalty * 10); + auto turn_duration_penalty = + boost::numeric_cast(std::round(value->duration * 10.)); + turn_weight_penalty = + std::isfinite(value->weight) + ? boost::numeric_cast(std::round(value->weight * 10)) + : turn_duration_penalty; - if (new_weight + new_turn_penalty < static_cast(header->num_osm_nodes)) + const auto weight_min_value = static_cast(header->num_osm_nodes); + if (turn_weight_penalty + new_weight < weight_min_value) { - util::Log(logWARNING) - << "turn penalty " << value->penalty << " for turn " << turn_block.from_id - << "," << turn_block.via_id << "," << turn_block.to_id - << " is too negative: clamping turn weight to " << header->num_osm_nodes; + util::Log(logWARNING) << "turn penalty " << turn_weight_penalty << " for turn " + << turn_index.from_id << ", " << turn_index.via_id << ", " + << turn_index.to_id + << " is too negative: clamping turn weight to " + << weight_min_value; - new_turn_penalty = header->num_osm_nodes - new_weight; + turn_weight_penalty = weight_min_value - new_weight; } + + turn_duration_penalties[edge_index] = turn_duration_penalty; + turn_weight_penalties[edge_index] = turn_weight_penalty; + + // Is fallback of duration to weight values allowed + fallback_to_duration &= (turn_duration_penalty == turn_weight_penalty); } // Update edge weight - inbuffer.weight = new_weight + new_turn_penalty; + inbuffer.weight = new_weight + turn_weight_penalty; } edge_based_edge_list.emplace_back(std::move(inbuffer)); } + if (update_turn_penalties) + { + if (fallback_to_duration) + { // Turn duration penalties are identical to turn weight penalties + // Save empty data vector, so turn weight penalties will be used by data facade. + turn_duration_penalties.clear(); + } + + const auto save_penalties = [](const auto &filename, const auto &data) -> void { + storage::io::FileWriter file(filename, storage::io::FileWriter::HasNoFingerprint); + file.SerializeVector(data); + }; + + tbb::parallel_invoke( + [&] { save_penalties(turn_weight_penalties_filename, turn_weight_penalties); }, + [&] { save_penalties(turn_duration_penalties_filename, turn_duration_penalties); }); + } + util::Log() << "Done reading edges"; return graph_header.max_edge_id; } diff --git a/src/contractor/contractor_dijkstra.cpp b/src/contractor/contractor_dijkstra.cpp index 95c47354f..c638e7ef2 100644 --- a/src/contractor/contractor_dijkstra.cpp +++ b/src/contractor/contractor_dijkstra.cpp @@ -9,7 +9,7 @@ ContractorDijkstra::ContractorDijkstra(const std::size_t heap_size) : heap(heap_ void ContractorDijkstra::Run(const unsigned number_of_targets, const int node_limit, - const int weight_limit, + const EdgeWeight weight_limit, const NodeID forbidden_node, const ContractorGraph &graph) { @@ -43,7 +43,7 @@ void ContractorDijkstra::Run(const unsigned number_of_targets, } void ContractorDijkstra::RelaxNode(const NodeID node, - const int node_weight, + const EdgeWeight node_weight, const NodeID forbidden_node, const ContractorGraph &graph) { @@ -60,7 +60,7 @@ void ContractorDijkstra::RelaxNode(const NodeID node, { continue; } - const int to_weight = node_weight + data.weight; + const EdgeWeight to_weight = node_weight + data.weight; // New Node discovered -> Add to Heap + Node Info Storage if (!heap.WasInserted(to)) diff --git a/src/engine/api/json_factory.cpp b/src/engine/api/json_factory.cpp index 1b86cc0f3..79a2a693e 100644 --- a/src/engine/api/json_factory.cpp +++ b/src/engine/api/json_factory.cpp @@ -242,6 +242,7 @@ util::json::Object makeRouteStep(guidance::RouteStep step, util::json::Value geo util::json::Object route_step; route_step.values["distance"] = std::round(step.distance * 10) / 10.; route_step.values["duration"] = std::round(step.duration * 10) / 10.; + route_step.values["weight"] = step.weight; // We should round to weight_precision here route_step.values["name"] = std::move(step.name); if (!step.ref.empty()) route_step.values["ref"] = std::move(step.ref); @@ -275,9 +276,11 @@ util::json::Object makeRouteStep(guidance::RouteStep step, util::json::Value geo util::json::Object makeRoute(const guidance::Route &route, util::json::Array legs, - boost::optional geometry) + boost::optional geometry, + const char *weight_name) { util::json::Object json_route; + json_route.values["weight_name"] = weight_name; json_route.values["distance"] = std::round(route.distance * 10) / 10.; json_route.values["duration"] = std::round(route.duration * 10) / 10.; json_route.values["legs"] = std::move(legs); diff --git a/src/engine/routing_algorithms/alternative_path.cpp b/src/engine/routing_algorithms/alternative_path.cpp index 18b2df962..64174932d 100644 --- a/src/engine/routing_algorithms/alternative_path.cpp +++ b/src/engine/routing_algorithms/alternative_path.cpp @@ -26,7 +26,7 @@ void AlternativeRouting::operator()(const std::shared_ptr(VIAPATH_EPSILON * length_of_shortest_path); - int unpacked_until_weight = 0; + EdgeWeight unpacked_until_weight = 0; std::stack unpack_stack; // Traverse path s-->v @@ -607,7 +607,7 @@ bool AlternativeRouting::ViaNodeCandidatePassesTTest( { const EdgeID current_edge_id = facade->FindEdgeInEitherDirection(packed_s_v_path[i - 1], packed_s_v_path[i]); - const int length_of_current_edge = facade->GetEdgeData(current_edge_id).weight; + const EdgeWeight length_of_current_edge = facade->GetEdgeData(current_edge_id).weight; if ((length_of_current_edge + unpacked_until_weight) >= T_threshold) { unpack_stack.emplace(packed_s_v_path[i - 1], packed_s_v_path[i]); @@ -660,7 +660,7 @@ bool AlternativeRouting::ViaNodeCandidatePassesTTest( } } - int t_test_path_length = unpacked_until_weight; + EdgeWeight t_test_path_length = unpacked_until_weight; unpacked_until_weight = 0; // Traverse path s-->v BOOST_ASSERT(!packed_v_t_path.empty()); @@ -727,7 +727,7 @@ bool AlternativeRouting::ViaNodeCandidatePassesTTest( QueryHeap &forward_heap3 = *engine_working_data.forward_heap_3; QueryHeap &reverse_heap3 = *engine_working_data.reverse_heap_3; - int upper_bound = INVALID_EDGE_WEIGHT; + EdgeWeight upper_bound = INVALID_EDGE_WEIGHT; NodeID middle = SPECIAL_NODEID; forward_heap3.Insert(s_P, 0, s_P); diff --git a/src/engine/routing_algorithms/many_to_many.cpp b/src/engine/routing_algorithms/many_to_many.cpp index e5d357d4b..8ad9dc22f 100644 --- a/src/engine/routing_algorithms/many_to_many.cpp +++ b/src/engine/routing_algorithms/many_to_many.cpp @@ -128,7 +128,7 @@ void ManyToManyRouting::ForwardRoutingStep( std::vector &result_table) const { const NodeID node = query_heap.DeleteMin(); - const int source_weight = query_heap.GetKey(node); + const EdgeWeight source_weight = query_heap.GetKey(node); // check if each encountered node has an entry const auto bucket_iterator = search_space_with_buckets.find(node); @@ -140,14 +140,14 @@ void ManyToManyRouting::ForwardRoutingStep( { // get target id from bucket entry const unsigned column_idx = current_bucket.target_id; - const int target_weight = current_bucket.weight; + const EdgeWeight target_weight = current_bucket.weight; auto ¤t_weight = result_table[row_idx * number_of_targets + column_idx]; // check if new weight is better const EdgeWeight new_weight = source_weight + target_weight; if (new_weight < 0) { const EdgeWeight loop_weight = super::GetLoopWeight(facade, node); - const int new_weight_with_loop = new_weight + loop_weight; + const EdgeWeight new_weight_with_loop = new_weight + loop_weight; if (loop_weight != INVALID_EDGE_WEIGHT && new_weight_with_loop >= 0) { current_weight = std::min(current_weight, new_weight_with_loop); @@ -173,7 +173,7 @@ void ManyToManyRouting::BackwardRoutingStep( SearchSpaceWithBuckets &search_space_with_buckets) const { const NodeID node = query_heap.DeleteMin(); - const int target_weight = query_heap.GetKey(node); + const EdgeWeight target_weight = query_heap.GetKey(node); // store settled nodes in search space bucket search_space_with_buckets[node].emplace_back(column_idx, target_weight); diff --git a/src/engine/routing_algorithms/routing_base.cpp b/src/engine/routing_algorithms/routing_base.cpp index 592f33638..980d50ea6 100644 --- a/src/engine/routing_algorithms/routing_base.cpp +++ b/src/engine/routing_algorithms/routing_base.cpp @@ -12,19 +12,19 @@ void BasicRoutingInterface::RoutingStep( SearchEngineData::QueryHeap &forward_heap, SearchEngineData::QueryHeap &reverse_heap, NodeID &middle_node_id, - std::int32_t &upper_bound, - std::int32_t min_edge_offset, + EdgeWeight &upper_bound, + EdgeWeight min_edge_offset, const bool forward_direction, const bool stalling, const bool force_loop_forward, const bool force_loop_reverse) const { const NodeID node = forward_heap.DeleteMin(); - const std::int32_t weight = forward_heap.GetKey(node); + const EdgeWeight weight = forward_heap.GetKey(node); if (reverse_heap.WasInserted(node)) { - const std::int32_t new_weight = reverse_heap.GetKey(node) + weight; + const EdgeWeight new_weight = reverse_heap.GetKey(node) + weight; if (new_weight < upper_bound) { // if loops are forced, they are so at the source @@ -45,7 +45,7 @@ void BasicRoutingInterface::RoutingStep( if (to == node) { const EdgeWeight edge_weight = data.weight; - const std::int32_t loop_weight = new_weight + edge_weight; + const EdgeWeight loop_weight = new_weight + edge_weight; if (loop_weight >= 0 && loop_weight < upper_bound) { middle_node_id = node; @@ -109,7 +109,7 @@ void BasicRoutingInterface::RoutingStep( const EdgeWeight edge_weight = data.weight; BOOST_ASSERT_MSG(edge_weight > 0, "edge_weight invalid"); - const int to_weight = weight + edge_weight; + const EdgeWeight to_weight = weight + edge_weight; // New Node discovered -> Add to Heap + Node Info Storage if (!forward_heap.WasInserted(to)) @@ -216,14 +216,14 @@ void BasicRoutingInterface::RetrievePackedPathFromSingleHeap( void BasicRoutingInterface::Search(const std::shared_ptr facade, SearchEngineData::QueryHeap &forward_heap, SearchEngineData::QueryHeap &reverse_heap, - std::int32_t &weight, + EdgeWeight &weight, std::vector &packed_leg, const bool force_loop_forward, const bool force_loop_reverse, - const int duration_upper_bound) const + const EdgeWeight weight_upper_bound) const { NodeID middle = SPECIAL_NODEID; - weight = duration_upper_bound; + weight = weight_upper_bound; // get offset to account for offsets on phantom nodes on compressed edges const auto min_edge_offset = std::min(0, forward_heap.MinKey()); @@ -264,7 +264,7 @@ void BasicRoutingInterface::Search(const std::shared_ptr &packed_leg, const bool force_loop_forward, const bool force_loop_reverse, - int duration_upper_bound) const + EdgeWeight weight_upper_bound) const { NodeID middle = SPECIAL_NODEID; - weight = duration_upper_bound; + weight = weight_upper_bound; using CoreEntryPoint = std::tuple; std::vector forward_entry_points; @@ -328,7 +328,7 @@ void BasicRoutingInterface::SearchWithCore( if (facade->IsCoreNode(forward_heap.Min())) { const NodeID node = forward_heap.DeleteMin(); - const int key = forward_heap.GetKey(node); + const EdgeWeight key = forward_heap.GetKey(node); forward_entry_points.emplace_back(node, key, forward_heap.GetData(node).parent); } else @@ -350,7 +350,7 @@ void BasicRoutingInterface::SearchWithCore( if (facade->IsCoreNode(reverse_heap.Min())) { const NodeID node = reverse_heap.DeleteMin(); - const int key = reverse_heap.GetKey(node); + const EdgeWeight key = reverse_heap.GetKey(node); reverse_entry_points.emplace_back(node, key, reverse_heap.GetData(node).parent); } else @@ -392,7 +392,7 @@ void BasicRoutingInterface::SearchWithCore( } // get offset to account for offsets on phantom nodes on compressed edges - int min_core_edge_offset = 0; + EdgeWeight min_core_edge_offset = 0; if (forward_core_heap.Size() > 0) { min_core_edge_offset = std::min(min_core_edge_offset, forward_core_heap.MinKey()); @@ -432,7 +432,7 @@ void BasicRoutingInterface::SearchWithCore( } // No path found for both target nodes? - if (duration_upper_bound <= weight || SPECIAL_NODEID == middle) + if (weight_upper_bound <= weight || SPECIAL_NODEID == middle) { weight = INVALID_EDGE_WEIGHT; return; @@ -567,7 +567,7 @@ double BasicRoutingInterface::GetNetworkDistanceWithCore( SearchEngineData::QueryHeap &reverse_core_heap, const PhantomNode &source_phantom, const PhantomNode &target_phantom, - int duration_upper_bound) const + EdgeWeight weight_upper_bound) const { BOOST_ASSERT(forward_heap.Empty()); BOOST_ASSERT(reverse_heap.Empty()); @@ -601,21 +601,21 @@ double BasicRoutingInterface::GetNetworkDistanceWithCore( const bool constexpr DO_NOT_FORCE_LOOPS = false; // prevents forcing of loops, since offsets are set correctly - int duration = INVALID_EDGE_WEIGHT; + EdgeWeight weight = INVALID_EDGE_WEIGHT; std::vector packed_path; SearchWithCore(facade, forward_heap, reverse_heap, forward_core_heap, reverse_core_heap, - duration, + weight, packed_path, DO_NOT_FORCE_LOOPS, DO_NOT_FORCE_LOOPS, - duration_upper_bound); + weight_upper_bound); double distance = std::numeric_limits::max(); - if (duration != INVALID_EDGE_WEIGHT) + if (weight != INVALID_EDGE_WEIGHT) { return GetPathDistance(facade, packed_path, source_phantom, target_phantom); } @@ -631,7 +631,7 @@ double BasicRoutingInterface::GetNetworkDistance( SearchEngineData::QueryHeap &reverse_heap, const PhantomNode &source_phantom, const PhantomNode &target_phantom, - int duration_upper_bound) const + EdgeWeight weight_upper_bound) const { BOOST_ASSERT(forward_heap.Empty()); BOOST_ASSERT(reverse_heap.Empty()); @@ -665,18 +665,18 @@ double BasicRoutingInterface::GetNetworkDistance( const bool constexpr DO_NOT_FORCE_LOOPS = false; // prevents forcing of loops, since offsets are set correctly - int duration = INVALID_EDGE_WEIGHT; + EdgeWeight weight = INVALID_EDGE_WEIGHT; std::vector packed_path; Search(facade, forward_heap, reverse_heap, - duration, + weight, packed_path, DO_NOT_FORCE_LOOPS, DO_NOT_FORCE_LOOPS, - duration_upper_bound); + weight_upper_bound); - if (duration == INVALID_EDGE_WEIGHT) + if (weight == INVALID_EDGE_WEIGHT) { return std::numeric_limits::max(); } diff --git a/src/extractor/compressed_edge_container.cpp b/src/extractor/compressed_edge_container.cpp index 8332e2712..b34ab90e7 100644 --- a/src/extractor/compressed_edge_container.cpp +++ b/src/extractor/compressed_edge_container.cpp @@ -101,6 +101,14 @@ void CompressedEdgeContainer::SerializeInternalVector(const std::string &path) c // write compressed geometry reverse weights geometry_out_stream.write((char *)(m_compressed_geometry_rev_weights.data()), sizeof(EdgeWeight) * m_compressed_geometry_rev_weights.size()); + + // write compressed geometry forward durations + geometry_out_stream.write((char *)(m_compressed_geometry_fwd_durations.data()), + sizeof(EdgeWeight) * m_compressed_geometry_fwd_durations.size()); + + // write compressed geometry reverse durations + geometry_out_stream.write((char *)(m_compressed_geometry_rev_durations.data()), + sizeof(EdgeWeight) * m_compressed_geometry_rev_durations.size()); } // Adds info for a compressed edge to the container. edge_id_2 @@ -111,12 +119,15 @@ void CompressedEdgeContainer::SerializeInternalVector(const std::string &path) c // edge_id_1 edge_id_2 // ----------> via_node_id -----------> target_node_id // weight_1 weight_2 +// duration_1 duration_2 void CompressedEdgeContainer::CompressEdge(const EdgeID edge_id_1, const EdgeID edge_id_2, const NodeID via_node_id, const NodeID target_node_id, const EdgeWeight weight1, - const EdgeWeight weight2) + const EdgeWeight weight2, + const EdgeWeight duration1, + const EdgeWeight duration2) { // remove super-trivial geometries BOOST_ASSERT(SPECIAL_EDGEID != edge_id_1); @@ -161,7 +172,7 @@ void CompressedEdgeContainer::CompressEdge(const EdgeID edge_id_1, // weight1 is the distance to the (currently) last coordinate in the bucket if (edge_bucket_list1.empty()) { - edge_bucket_list1.emplace_back(OnewayCompressedEdge{via_node_id, weight1}); + edge_bucket_list1.emplace_back(OnewayCompressedEdge{via_node_id, weight1, duration1}); } BOOST_ASSERT(0 < edge_bucket_list1.size()); @@ -192,13 +203,14 @@ void CompressedEdgeContainer::CompressEdge(const EdgeID edge_id_1, else { // we are certain that the second edge is atomic. - edge_bucket_list1.emplace_back(OnewayCompressedEdge{target_node_id, weight2}); + edge_bucket_list1.emplace_back(OnewayCompressedEdge{target_node_id, weight2, duration2}); } } void CompressedEdgeContainer::AddUncompressedEdge(const EdgeID edge_id, const NodeID target_node_id, - const EdgeWeight weight) + const EdgeWeight weight, + const EdgeWeight duration) { // remove super-trivial geometries BOOST_ASSERT(SPECIAL_EDGEID != edge_id); @@ -234,7 +246,7 @@ void CompressedEdgeContainer::AddUncompressedEdge(const EdgeID edge_id, // Don't re-add this if it's already in there. if (edge_bucket_list.empty()) { - edge_bucket_list.emplace_back(OnewayCompressedEdge{target_node_id, weight}); + edge_bucket_list.emplace_back(OnewayCompressedEdge{target_node_id, weight, duration}); } } @@ -244,6 +256,8 @@ void CompressedEdgeContainer::InitializeBothwayVector() m_compressed_geometry_nodes.reserve(m_compressed_oneway_geometries.size()); m_compressed_geometry_fwd_weights.reserve(m_compressed_oneway_geometries.size()); m_compressed_geometry_rev_weights.reserve(m_compressed_oneway_geometries.size()); + m_compressed_geometry_fwd_durations.reserve(m_compressed_oneway_geometries.size()); + m_compressed_geometry_rev_durations.reserve(m_compressed_oneway_geometries.size()); } unsigned CompressedEdgeContainer::ZipEdges(const EdgeID f_edge_id, const EdgeID r_edge_id) @@ -264,6 +278,8 @@ unsigned CompressedEdgeContainer::ZipEdges(const EdgeID f_edge_id, const EdgeID m_compressed_geometry_nodes.emplace_back(first_node.node_id); m_compressed_geometry_fwd_weights.emplace_back(INVALID_EDGE_WEIGHT); m_compressed_geometry_rev_weights.emplace_back(first_node.weight); + m_compressed_geometry_fwd_durations.emplace_back(INVALID_EDGE_WEIGHT); + m_compressed_geometry_rev_durations.emplace_back(first_node.duration); for (std::size_t i = 0; i < forward_bucket.size() - 1; ++i) { @@ -275,6 +291,8 @@ unsigned CompressedEdgeContainer::ZipEdges(const EdgeID f_edge_id, const EdgeID m_compressed_geometry_nodes.emplace_back(fwd_node.node_id); m_compressed_geometry_fwd_weights.emplace_back(fwd_node.weight); m_compressed_geometry_rev_weights.emplace_back(rev_node.weight); + m_compressed_geometry_fwd_durations.emplace_back(fwd_node.duration); + m_compressed_geometry_rev_durations.emplace_back(rev_node.duration); } const auto &last_node = forward_bucket.back(); @@ -282,6 +300,8 @@ unsigned CompressedEdgeContainer::ZipEdges(const EdgeID f_edge_id, const EdgeID m_compressed_geometry_nodes.emplace_back(last_node.node_id); m_compressed_geometry_fwd_weights.emplace_back(last_node.weight); m_compressed_geometry_rev_weights.emplace_back(INVALID_EDGE_WEIGHT); + m_compressed_geometry_fwd_durations.emplace_back(last_node.duration); + m_compressed_geometry_rev_durations.emplace_back(INVALID_EDGE_WEIGHT); return zipped_geometry_id; } diff --git a/src/extractor/edge_based_graph_factory.cpp b/src/extractor/edge_based_graph_factory.cpp index e08cf9ec4..d7ec78676 100644 --- a/src/extractor/edge_based_graph_factory.cpp +++ b/src/extractor/edge_based_graph_factory.cpp @@ -190,7 +190,9 @@ void EdgeBasedGraphFactory::Run(ScriptingEnvironment &scripting_environment, const std::string &original_edge_data_filename, const std::string &turn_lane_data_filename, const std::string &edge_segment_lookup_filename, - const std::string &edge_penalty_filename, + const std::string &turn_weight_penalties_filename, + const std::string &turn_duration_penalties_filename, + const std::string &turn_penalties_index_filename, const bool generate_edge_lookup) { TIMER_START(renumber); @@ -207,7 +209,9 @@ void EdgeBasedGraphFactory::Run(ScriptingEnvironment &scripting_environment, original_edge_data_filename, turn_lane_data_filename, edge_segment_lookup_filename, - edge_penalty_filename, + turn_weight_penalties_filename, + turn_duration_penalties_filename, + turn_penalties_index_filename, generate_edge_lookup); TIMER_STOP(generate_edges); @@ -237,11 +241,7 @@ unsigned EdgeBasedGraphFactory::RenumberEdges() continue; } - // oneway streets always require this self-loop. Other streets only if a u-turn plus - // traversal - // of the street takes longer than the loop - m_edge_based_node_weights.push_back(edge_data.distance + - profile_properties.u_turn_penalty); + m_edge_based_node_weights.push_back(edge_data.weight); BOOST_ASSERT(numbered_edges_count < m_node_based_graph->GetNumberOfEdges()); edge_data.edge_id = numbered_edges_count; @@ -311,7 +311,9 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges( const std::string &original_edge_data_filename, const std::string &turn_lane_data_filename, const std::string &edge_segment_lookup_filename, - const std::string &edge_fixed_penalties_filename, + const std::string &turn_weight_penalties_filename, + const std::string &turn_duration_penalties_filename, + const std::string &turn_penalties_index_filename, const bool generate_edge_lookup) { util::Log() << "Generating edge-expanded edges "; @@ -324,12 +326,12 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges( std::ofstream edge_data_file(original_edge_data_filename.c_str(), std::ios::binary); std::ofstream edge_segment_file; - std::ofstream edge_penalty_file; + std::ofstream turn_penalties_index_file; if (generate_edge_lookup) { edge_segment_file.open(edge_segment_lookup_filename.c_str(), std::ios::binary); - edge_penalty_file.open(edge_fixed_penalties_filename.c_str(), std::ios::binary); + turn_penalties_index_file.open(turn_penalties_index_filename.c_str(), std::ios::binary); } // Writes a dummy value at the front that is updated later with the total length @@ -364,6 +366,13 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges( bearing_class_by_node_based_node.resize(m_node_based_graph->GetNumberOfNodes(), std::numeric_limits::max()); + // FIXME these need to be tuned in pre-allocated size + std::vector turn_weight_penalties; + std::vector turn_duration_penalties; + + const auto weight_multiplier = + std::pow(10, scripting_environment.GetProfileProperties().weight_precision); + { util::UnbufferedLog log; @@ -474,28 +483,6 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges( BOOST_ASSERT(!edge_data2.reversed); // the following is the core of the loop. - unsigned distance = edge_data1.distance; - if (m_traffic_lights.find(node_at_center_of_intersection) != - m_traffic_lights.end()) - { - distance += profile_properties.traffic_signal_penalty; - } - - const int32_t turn_penalty = - scripting_environment.GetTurnPenalty(180. - turn.angle); - - const auto turn_instruction = turn.instruction; - if (turn_instruction.direction_modifier == guidance::DirectionModifier::UTurn) - { - distance += profile_properties.u_turn_penalty; - } - - // don't add turn penalty if it is not an actual turn. This heuristic is - // necessary - // since OSRM cannot handle looping roads/parallel roads - if (turn_instruction.type != guidance::TurnType::NoTurn) - distance += turn_penalty; - const bool is_encoded_forwards = m_compressed_edge_container.HasZippedEntryForForwardID(incoming_edge); const bool is_encoded_backwards = @@ -509,7 +496,7 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges( true}, edge_data1.name_id, turn.lane_data_id, - turn_instruction, + turn.instruction, entry_class_id, edge_data1.travel_mode, util::guidance::TurnBearing(intersection[0].bearing), @@ -523,7 +510,7 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges( false}, edge_data1.name_id, turn.lane_data_id, - turn_instruction, + turn.instruction, entry_class_id, edge_data1.travel_mode, util::guidance::TurnBearing(intersection[0].bearing), @@ -531,26 +518,46 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges( } ++original_edges_counter; - if (original_edge_data_vector.size() > 1024 * 1024 * 10) { FlushVectorToStream(edge_data_file, original_edge_data_vector); } + // compute weight and duration penalties + auto is_traffic_light = m_traffic_lights.count(node_at_center_of_intersection); + ExtractionTurn extracted_turn(turn, is_traffic_light); + scripting_environment.ProcessTurn(extracted_turn); + + // turn penalties are limited to [-2^15, 2^15) which roughly + // translates to 54 minutes and fits signed 16bit deci-seconds + auto weight_penalty = + boost::numeric_cast(extracted_turn.weight * weight_multiplier); + auto duration_penalty = + boost::numeric_cast(extracted_turn.duration * 10.); + BOOST_ASSERT(SPECIAL_NODEID != edge_data1.edge_id); BOOST_ASSERT(SPECIAL_NODEID != edge_data2.edge_id); // NOTE: potential overflow here if we hit 2^32 routable edges BOOST_ASSERT(m_edge_based_edge_list.size() <= std::numeric_limits::max()); - m_edge_based_edge_list.emplace_back(edge_data1.edge_id, - edge_data2.edge_id, - m_edge_based_edge_list.size(), - distance, - true, - false); + auto turn_id = m_edge_based_edge_list.size(); + auto weight = + boost::numeric_cast(edge_data1.weight + weight_penalty); + m_edge_based_edge_list.emplace_back( + edge_data1.edge_id, edge_data2.edge_id, turn_id, weight, true, false); BOOST_ASSERT(original_edges_counter == m_edge_based_edge_list.size()); + BOOST_ASSERT(turn_weight_penalties.size() == turn_id); + turn_weight_penalties.push_back(weight_penalty); + + // the weight and the duration are not the same thing + if (!profile_properties.fallback_to_duration) + { + BOOST_ASSERT(turn_duration_penalties.size() == turn_id); + turn_duration_penalties.push_back(duration_penalty); + } + // Here is where we write out the mapping between the edge-expanded edges, and // the node-based edges that are originally used to calculate the `distance` // for the edge-expanded edges. About 40 lines back, there is: @@ -618,17 +625,46 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges( m_node_info_list[m_compressed_edge_container.GetFirstEdgeTargetID( turn.eid)]; - const unsigned fixed_penalty = distance - edge_data1.distance; - lookup::PenaltyBlock penaltyblock = { - fixed_penalty, from_node.node_id, via_node.node_id, to_node.node_id}; - edge_penalty_file.write(reinterpret_cast(&penaltyblock), - sizeof(penaltyblock)); + lookup::TurnIndexBlock turn_index_block = { + from_node.node_id, via_node.node_id, to_node.node_id}; + BOOST_ASSERT(turn_penalties_index_file.tellp() / + (sizeof(turn_index_block)) == + turn_id); + turn_penalties_index_file.write( + reinterpret_cast(&turn_index_block), + sizeof(turn_index_block)); } } } } } + // write weight penalties per turn + std::ofstream turn_weight_penalties_file(turn_weight_penalties_filename.c_str(), + std::ios::binary); + lookup::TurnPenaltiesHeader turn_weight_penalties_header{turn_weight_penalties.size()}; + turn_weight_penalties_file.write(reinterpret_cast(&turn_weight_penalties_header), + sizeof(turn_weight_penalties_header)); + turn_weight_penalties_file.write(reinterpret_cast(turn_weight_penalties.data()), + sizeof(decltype(turn_weight_penalties)::value_type) * + turn_weight_penalties.size()); + + // write duration penalties per turn if we need them + BOOST_ASSERT(!profile_properties.fallback_to_duration || turn_duration_penalties.size() == 0); + std::ofstream turn_duration_penalties_file(turn_duration_penalties_filename.c_str(), + std::ios::binary); + lookup::TurnPenaltiesHeader turn_duration_penalties_header{turn_duration_penalties.size()}; + turn_duration_penalties_file.write( + reinterpret_cast(&turn_duration_penalties_header), + sizeof(turn_duration_penalties_header)); + if (!profile_properties.fallback_to_duration) + { + BOOST_ASSERT(turn_weight_penalties.size() == turn_duration_penalties.size()); + turn_duration_penalties_file.write( + reinterpret_cast(turn_duration_penalties.data()), + sizeof(decltype(turn_duration_penalties)::value_type) * turn_duration_penalties.size()); + } + util::Log() << "Created " << entry_class_hash.size() << " entry classes and " << bearing_class_hash.size() << " Bearing Classes"; diff --git a/src/extractor/extraction_containers.cpp b/src/extractor/extraction_containers.cpp index bb4a2a6c0..a129b9b50 100644 --- a/src/extractor/extraction_containers.cpp +++ b/src/extractor/extraction_containers.cpp @@ -1,4 +1,5 @@ #include "extractor/extraction_containers.hpp" +#include "extractor/extraction_segment.hpp" #include "extractor/extraction_way.hpp" #include "util/coordinate_calculation.hpp" @@ -390,6 +391,9 @@ void ExtractionContainers::PrepareEdges(ScriptingEnvironment &scripting_environm const auto all_edges_list_end_ = all_edges_list.end(); const auto all_nodes_list_end_ = all_nodes_list.end(); + const auto weight_multiplier = + std::pow(10, scripting_environment.GetProfileProperties().weight_precision); + while (edge_iterator != all_edges_list_end_ && node_iterator != all_nodes_list_end_) { // skip all invalid edges @@ -414,44 +418,28 @@ void ExtractionContainers::PrepareEdges(ScriptingEnvironment &scripting_environm } BOOST_ASSERT(edge_iterator->result.osm_target_id == node_iterator->node_id); - BOOST_ASSERT(edge_iterator->weight_data.speed >= 0); BOOST_ASSERT(edge_iterator->source_coordinate.lat != util::FixedLatitude{std::numeric_limits::min()}); BOOST_ASSERT(edge_iterator->source_coordinate.lon != util::FixedLongitude{std::numeric_limits::min()}); + const util::Coordinate target_coord{node_iterator->lon, node_iterator->lat}; const double distance = util::coordinate_calculation::greatCircleDistance( - edge_iterator->source_coordinate, - util::Coordinate(node_iterator->lon, node_iterator->lat)); + edge_iterator->source_coordinate, target_coord); - scripting_environment.ProcessSegment(edge_iterator->source_coordinate, - *node_iterator, - distance, - edge_iterator->weight_data); + double weight = static_cast(mapbox::util::apply_visitor( + detail::ToValueByEdge(distance), edge_iterator->weight_data)); + double duration = static_cast(mapbox::util::apply_visitor( + detail::ToValueByEdge(distance), edge_iterator->duration_data)); - const double weight = [distance, edge_iterator, node_iterator]( - const InternalExtractorEdge::WeightData &data) { - switch (data.type) - { - case InternalExtractorEdge::WeightType::EDGE_DURATION: - case InternalExtractorEdge::WeightType::WAY_DURATION: - return data.duration * 10.; - break; - case InternalExtractorEdge::WeightType::SPEED: - return (distance * 10.) / (data.speed / 3.6); - break; - case InternalExtractorEdge::WeightType::INVALID: - std::stringstream coordstring; - coordstring << edge_iterator->source_coordinate << " to " << node_iterator->lon - << "," << node_iterator->lat; - util::exception("Encountered invalid weight at segment " + coordstring.str() + - SOURCE_REF); - } - return -1.0; - }(edge_iterator->weight_data); + ExtractionSegment extracted_segment( + edge_iterator->source_coordinate, target_coord, distance, weight, duration); + scripting_environment.ProcessSegment(extracted_segment); auto &edge = edge_iterator->result; - edge.weight = std::max(1, static_cast(std::floor(weight + .5))); + edge.weight = + std::max(1, std::round(extracted_segment.weight * weight_multiplier)); + edge.duration = std::max(1, std::round(extracted_segment.duration * 10.)); // assign new node id auto id_iter = external_to_internal_node_id_map.find(node_iterator->node_id); @@ -499,7 +487,7 @@ void ExtractionContainers::PrepareEdges(ScriptingEnvironment &scripting_environm } BOOST_ASSERT(all_edges_list.size() > 0); - for (unsigned i = 0; i < all_edges_list.size();) + for (std::size_t i = 0; i < all_edges_list.size();) { // only invalid edges left if (all_edges_list[i].result.source == SPECIAL_NODEID) @@ -513,42 +501,44 @@ void ExtractionContainers::PrepareEdges(ScriptingEnvironment &scripting_environm continue; } - unsigned start_idx = i; + std::size_t start_idx = i; NodeID source = all_edges_list[i].result.source; NodeID target = all_edges_list[i].result.target; - int min_forward_weight = std::numeric_limits::max(); - int min_backward_weight = std::numeric_limits::max(); - unsigned min_forward_idx = std::numeric_limits::max(); - unsigned min_backward_idx = std::numeric_limits::max(); + auto min_forward = std::make_pair(std::numeric_limits::max(), + std::numeric_limits::max()); + auto min_backward = std::make_pair(std::numeric_limits::max(), + std::numeric_limits::max()); + std::size_t min_forward_idx = std::numeric_limits::max(); + std::size_t min_backward_idx = std::numeric_limits::max(); // find minimal edge in both directions - while (all_edges_list[i].result.source == source && + while (i < all_edges_list.size() && all_edges_list[i].result.source == source && all_edges_list[i].result.target == target) { - if (all_edges_list[i].result.forward && - all_edges_list[i].result.weight < min_forward_weight) + const auto &result = all_edges_list[i].result; + const auto value = std::make_pair(result.weight, result.duration); + if (result.forward && value < min_forward) { min_forward_idx = i; - min_forward_weight = all_edges_list[i].result.weight; + min_forward = value; } - if (all_edges_list[i].result.backward && - all_edges_list[i].result.weight < min_backward_weight) + if (result.backward && value < min_backward) { min_backward_idx = i; - min_backward_weight = all_edges_list[i].result.weight; + min_backward = value; } // this also increments the outer loop counter! i++; } - BOOST_ASSERT(min_forward_idx == std::numeric_limits::max() || + BOOST_ASSERT(min_forward_idx == std::numeric_limits::max() || min_forward_idx < i); - BOOST_ASSERT(min_backward_idx == std::numeric_limits::max() || + BOOST_ASSERT(min_backward_idx == std::numeric_limits::max() || min_backward_idx < i); - BOOST_ASSERT(min_backward_idx != std::numeric_limits::max() || - min_forward_idx != std::numeric_limits::max()); + BOOST_ASSERT(min_backward_idx != std::numeric_limits::max() || + min_forward_idx != std::numeric_limits::max()); if (min_backward_idx == min_forward_idx) { @@ -558,8 +548,8 @@ void ExtractionContainers::PrepareEdges(ScriptingEnvironment &scripting_environm } else { - bool has_forward = min_forward_idx != std::numeric_limits::max(); - bool has_backward = min_backward_idx != std::numeric_limits::max(); + bool has_forward = min_forward_idx != std::numeric_limits::max(); + bool has_backward = min_backward_idx != std::numeric_limits::max(); if (has_forward) { all_edges_list[min_forward_idx].result.forward = true; @@ -577,7 +567,7 @@ void ExtractionContainers::PrepareEdges(ScriptingEnvironment &scripting_environm } // invalidate all unused edges - for (unsigned j = start_idx; j < i; j++) + for (std::size_t j = start_idx; j < i; j++) { if (j == min_forward_idx || j == min_backward_idx) { diff --git a/src/extractor/extractor.cpp b/src/extractor/extractor.cpp index 994f9d3c4..965a8cab7 100644 --- a/src/extractor/extractor.cpp +++ b/src/extractor/extractor.cpp @@ -126,9 +126,6 @@ int Extractor::run(ScriptingEnvironment &scripting_environment) } util::Log() << "Threads: " << number_of_threads; - ExtractionContainers extraction_containers; - auto extractor_callbacks = std::make_unique(extraction_containers); - const osmium::io::File input_file(config.input_path.string()); osmium::io::Reader reader( @@ -144,6 +141,10 @@ int Extractor::run(ScriptingEnvironment &scripting_environment) util::Log() << "Parsing in progress.."; TIMER_START(parsing); + ExtractionContainers extraction_containers; + auto extractor_callbacks = std::make_unique( + extraction_containers, scripting_environment.GetProfileProperties()); + // setup raster sources scripting_environment.SetupSources(); @@ -496,7 +497,9 @@ Extractor::BuildEdgeExpandedGraph(ScriptingEnvironment &scripting_environment, config.edge_output_path, config.turn_lane_data_file_name, config.edge_segment_lookup_path, - config.edge_penalty_path, + config.turn_weight_penalties_path, + config.turn_duration_penalties_path, + config.turn_penalties_index_path, config.generate_edge_lookup); WriteTurnLaneData(config.turn_lane_descriptions_file_name); diff --git a/src/extractor/extractor_callbacks.cpp b/src/extractor/extractor_callbacks.cpp index fcf04db47..03ac0e5de 100644 --- a/src/extractor/extractor_callbacks.cpp +++ b/src/extractor/extractor_callbacks.cpp @@ -4,6 +4,7 @@ #include "extractor/extraction_node.hpp" #include "extractor/extraction_way.hpp" #include "extractor/guidance/road_classification.hpp" +#include "extractor/profile_properties.hpp" #include "extractor/restriction.hpp" #include "util/for_each_pair.hpp" @@ -18,6 +19,7 @@ #include "osrm/coordinate.hpp" +#include #include #include #include @@ -31,8 +33,9 @@ namespace extractor using TurnLaneDescription = guidance::TurnLaneDescription; namespace TurnLaneType = guidance::TurnLaneType; -ExtractorCallbacks::ExtractorCallbacks(ExtractionContainers &extraction_containers) - : external_memory(extraction_containers) +ExtractorCallbacks::ExtractorCallbacks(ExtractionContainers &extraction_containers_, + const ProfileProperties &properties) + : external_memory(extraction_containers_), fallback_to_duration(properties.fallback_to_duration) { // we reserved 0, 1, 2, 3 for the empty case string_map[MapKey("", "", "", "")] = 0; @@ -80,16 +83,18 @@ void ExtractorCallbacks::ProcessRestriction( */ void ExtractorCallbacks::ProcessWay(const osmium::Way &input_way, const ExtractionWay &parsed_way) { - if (((0 >= parsed_way.forward_speed) || - (TRAVEL_MODE_INACCESSIBLE == parsed_way.forward_travel_mode)) && - ((0 >= parsed_way.backward_speed) || - (TRAVEL_MODE_INACCESSIBLE == parsed_way.backward_travel_mode)) && - (0 >= parsed_way.duration)) + if ((parsed_way.forward_travel_mode == TRAVEL_MODE_INACCESSIBLE && + parsed_way.backward_travel_mode == TRAVEL_MODE_INACCESSIBLE) || + (parsed_way.forward_speed <= 0 && parsed_way.backward_speed <= 0 && + parsed_way.duration <= 0) || + (!fallback_to_duration && parsed_way.forward_rate <= 0 && parsed_way.backward_rate <= 0 && + parsed_way.weight <= 0)) { // Only true if the way is specified by the speed profile return; } - if (input_way.nodes().size() <= 1) + const auto &nodes = input_way.nodes(); + if (nodes.size() <= 1) { // safe-guard against broken data return; } @@ -97,46 +102,63 @@ void ExtractorCallbacks::ProcessWay(const osmium::Way &input_way, const Extracti if (std::numeric_limits::max() == input_way.id()) { util::Log(logDEBUG) << "found bogus way with id: " << input_way.id() << " of size " - << input_way.nodes().size(); + << nodes.size(); return; } + InternalExtractorEdge::DurationData forward_duration_data; + InternalExtractorEdge::DurationData backward_duration_data; InternalExtractorEdge::WeightData forward_weight_data; InternalExtractorEdge::WeightData backward_weight_data; - if (0 < parsed_way.duration) - { - const unsigned num_edges = (input_way.nodes().size() - 1); - // FIXME We devide by the numer of nodes here, but should rather consider - // the length of each segment. We would eigther have to compute the length - // of the whole way here (we can't: no node coordinates) or push that back - // to the container and keep a reference to the way. - forward_weight_data.duration = parsed_way.duration / num_edges; - forward_weight_data.type = InternalExtractorEdge::WeightType::WAY_DURATION; - backward_weight_data.duration = parsed_way.duration / num_edges; - backward_weight_data.type = InternalExtractorEdge::WeightType::WAY_DURATION; - } - else - { - if (parsed_way.forward_speed > 0 && - parsed_way.forward_travel_mode != TRAVEL_MODE_INACCESSIBLE) + const auto toValueByEdgeOrByMeter = + [&nodes](const double by_way, const double by_meter) -> detail::ByEdgeOrByMeterValue { + if (by_way > 0) { - forward_weight_data.speed = parsed_way.forward_speed; - forward_weight_data.type = InternalExtractorEdge::WeightType::SPEED; + // FIXME We divide by the number of edges here, but should rather consider + // the length of each segment. We would either have to compute the length + // of the whole way here (we can't: no node coordinates) or push that back + // to the container and keep a reference to the way. + const unsigned num_edges = (nodes.size() - 1); + return detail::ValueByEdge{by_way / num_edges}; } - if (parsed_way.backward_speed > 0 && - parsed_way.backward_travel_mode != TRAVEL_MODE_INACCESSIBLE) + else { - backward_weight_data.speed = parsed_way.backward_speed; - backward_weight_data.type = InternalExtractorEdge::WeightType::SPEED; + return detail::ValueByMeter{by_meter}; } - } + }; - if (forward_weight_data.type == InternalExtractorEdge::WeightType::INVALID && - backward_weight_data.type == InternalExtractorEdge::WeightType::INVALID) + if (parsed_way.forward_travel_mode != TRAVEL_MODE_INACCESSIBLE) { - util::Log(logDEBUG) << "found way with bogus speed, id: " << input_way.id(); - return; + BOOST_ASSERT(parsed_way.duration > 0 || parsed_way.forward_speed > 0); + forward_duration_data = + toValueByEdgeOrByMeter(parsed_way.duration, parsed_way.forward_speed / 3.6); + // fallback to duration as weight + if (parsed_way.weight > 0 || parsed_way.forward_rate > 0) + { + forward_weight_data = + toValueByEdgeOrByMeter(parsed_way.weight, parsed_way.forward_rate); + } + else if (fallback_to_duration) + { + forward_weight_data = forward_duration_data; + } + } + if (parsed_way.backward_travel_mode != TRAVEL_MODE_INACCESSIBLE) + { + BOOST_ASSERT(parsed_way.duration > 0 || parsed_way.backward_speed > 0); + backward_duration_data = + toValueByEdgeOrByMeter(parsed_way.duration, parsed_way.backward_speed / 3.6); + // fallback to duration as weight + if (parsed_way.weight > 0 || parsed_way.backward_rate > 0) + { + backward_weight_data = + toValueByEdgeOrByMeter(parsed_way.weight, parsed_way.backward_rate); + } + else if (fallback_to_duration) + { + backward_weight_data = backward_duration_data; + } } const auto laneStringToDescription = [](const std::string &lane_string) -> TurnLaneDescription { @@ -237,27 +259,23 @@ void ExtractorCallbacks::ProcessWay(const osmium::Way &input_way, const Extracti const auto road_classification = parsed_way.road_classification; - const constexpr auto MAX_STRING_LENGTH = 255u; + const constexpr std::size_t MAX_STRING_LENGTH = 255u; // Get the unique identifier for the street name, destination, and ref const auto name_iterator = string_map.find( MapKey(parsed_way.name, parsed_way.destinations, parsed_way.ref, parsed_way.pronunciation)); - unsigned name_id = EMPTY_NAMEID; + auto name_id = EMPTY_NAMEID; if (string_map.end() == name_iterator) { - const auto name_length = std::min(MAX_STRING_LENGTH, parsed_way.name.size()); + const auto name_length = std::min(MAX_STRING_LENGTH, parsed_way.name.size()); const auto destinations_length = - std::min(MAX_STRING_LENGTH, parsed_way.destinations.size()); + std::min(MAX_STRING_LENGTH, parsed_way.destinations.size()); const auto pronunciation_length = - std::min(MAX_STRING_LENGTH, parsed_way.pronunciation.size()); - const auto ref_length = std::min(MAX_STRING_LENGTH, parsed_way.ref.size()); + std::min(MAX_STRING_LENGTH, parsed_way.pronunciation.size()); + const auto ref_length = std::min(MAX_STRING_LENGTH, parsed_way.ref.size()); // name_offsets already has an offset of a new name, take the offset index as the name id name_id = external_memory.name_offsets.size() - 1; - external_memory.name_char_data.reserve(external_memory.name_char_data.size() + name_length + - destinations_length + pronunciation_length + - ref_length); - std::copy(parsed_way.name.c_str(), parsed_way.name.c_str() + name_length, std::back_inserter(external_memory.name_char_data)); @@ -288,114 +306,85 @@ void ExtractorCallbacks::ProcessWay(const osmium::Way &input_way, const Extracti name_id = name_iterator->second; } - const bool split_edge = (parsed_way.forward_speed > 0) && - (TRAVEL_MODE_INACCESSIBLE != parsed_way.forward_travel_mode) && - (parsed_way.backward_speed > 0) && - (TRAVEL_MODE_INACCESSIBLE != parsed_way.backward_travel_mode) && - ((parsed_way.forward_speed != parsed_way.backward_speed) || + const bool in_forward_direction = + (parsed_way.forward_speed > 0 || parsed_way.forward_rate > 0 || parsed_way.duration > 0 || + parsed_way.weight > 0) && + (parsed_way.forward_travel_mode != TRAVEL_MODE_INACCESSIBLE); + + const bool in_backward_direction = + (parsed_way.backward_speed > 0 || parsed_way.backward_rate > 0 || parsed_way.duration > 0 || + parsed_way.weight > 0) && + (parsed_way.backward_travel_mode != TRAVEL_MODE_INACCESSIBLE); + + const bool split_edge = in_forward_direction && in_backward_direction && + ((parsed_way.forward_rate != parsed_way.backward_rate) || + (parsed_way.forward_speed != parsed_way.backward_speed) || (parsed_way.forward_travel_mode != parsed_way.backward_travel_mode) || (turn_lane_id_forward != turn_lane_id_backward)); - external_memory.used_node_id_list.reserve(external_memory.used_node_id_list.size() + - input_way.nodes().size()); - - std::transform(input_way.nodes().begin(), - input_way.nodes().end(), - std::back_inserter(external_memory.used_node_id_list), - [](const osmium::NodeRef &ref) { - return OSMNodeID{static_cast(ref.ref())}; - }); - - const bool is_opposite_way = TRAVEL_MODE_INACCESSIBLE == parsed_way.forward_travel_mode; - - // traverse way in reverse in this case - if (is_opposite_way) + if (in_forward_direction) { - BOOST_ASSERT(split_edge == false); - BOOST_ASSERT(parsed_way.backward_travel_mode != TRAVEL_MODE_INACCESSIBLE); util::for_each_pair( - input_way.nodes().crbegin(), - input_way.nodes().crend(), - [&](const osmium::NodeRef &first_node, const osmium::NodeRef &last_node) { - external_memory.all_edges_list.push_back( - InternalExtractorEdge(OSMNodeID{static_cast(first_node.ref())}, - OSMNodeID{static_cast(last_node.ref())}, - name_id, - backward_weight_data, - true, - false, - parsed_way.roundabout, - parsed_way.circular, - parsed_way.is_startpoint, - parsed_way.backward_travel_mode, - false, - turn_lane_id_backward, - road_classification)); - }); - - external_memory.way_start_end_id_list.push_back( - {OSMWayID{static_cast(input_way.id())}, - OSMNodeID{static_cast(input_way.nodes().back().ref())}, - OSMNodeID{ - static_cast(input_way.nodes()[input_way.nodes().size() - 2].ref())}, - OSMNodeID{static_cast(input_way.nodes()[1].ref())}, - OSMNodeID{static_cast(input_way.nodes()[0].ref())}}); - } - else - { - const bool forward_only = - split_edge || TRAVEL_MODE_INACCESSIBLE == parsed_way.backward_travel_mode; - util::for_each_pair( - input_way.nodes().cbegin(), - input_way.nodes().cend(), + nodes.cbegin(), + nodes.cend(), [&](const osmium::NodeRef &first_node, const osmium::NodeRef &last_node) { external_memory.all_edges_list.push_back( InternalExtractorEdge(OSMNodeID{static_cast(first_node.ref())}, OSMNodeID{static_cast(last_node.ref())}, name_id, forward_weight_data, + forward_duration_data, true, - !forward_only, + in_backward_direction && !split_edge, parsed_way.roundabout, parsed_way.circular, parsed_way.is_startpoint, parsed_way.forward_travel_mode, split_edge, turn_lane_id_forward, - road_classification)); + road_classification, + {})); }); - if (split_edge) - { - BOOST_ASSERT(parsed_way.backward_travel_mode != TRAVEL_MODE_INACCESSIBLE); - util::for_each_pair( - input_way.nodes().cbegin(), - input_way.nodes().cend(), - [&](const osmium::NodeRef &first_node, const osmium::NodeRef &last_node) { - external_memory.all_edges_list.push_back(InternalExtractorEdge( - OSMNodeID{static_cast(first_node.ref())}, - OSMNodeID{static_cast(last_node.ref())}, - name_id, - backward_weight_data, - false, - true, - parsed_way.roundabout, - parsed_way.circular, - parsed_way.is_startpoint, - parsed_way.backward_travel_mode, - true, - turn_lane_id_backward, - road_classification)); - }); - } - - external_memory.way_start_end_id_list.push_back( - {OSMWayID{static_cast(input_way.id())}, - OSMNodeID{static_cast(input_way.nodes().back().ref())}, - OSMNodeID{ - static_cast(input_way.nodes()[input_way.nodes().size() - 2].ref())}, - OSMNodeID{static_cast(input_way.nodes()[1].ref())}, - OSMNodeID{static_cast(input_way.nodes()[0].ref())}}); } + + if (in_backward_direction || split_edge) + { + util::for_each_pair( + nodes.cbegin(), + nodes.cend(), + [&](const osmium::NodeRef &first_node, const osmium::NodeRef &last_node) { + external_memory.all_edges_list.push_back( + InternalExtractorEdge(OSMNodeID{static_cast(first_node.ref())}, + OSMNodeID{static_cast(last_node.ref())}, + name_id, + backward_weight_data, + backward_duration_data, + false, + true, + parsed_way.roundabout, + parsed_way.circular, + parsed_way.is_startpoint, + parsed_way.backward_travel_mode, + split_edge, + turn_lane_id_backward, + road_classification, + {})); + }); + } + + std::transform(nodes.begin(), + nodes.end(), + std::back_inserter(external_memory.used_node_id_list), + [](const osmium::NodeRef &ref) { + return OSMNodeID{static_cast(ref.ref())}; + }); + + external_memory.way_start_end_id_list.push_back( + {OSMWayID{static_cast(input_way.id())}, + OSMNodeID{static_cast(nodes[0].ref())}, + OSMNodeID{static_cast(nodes[1].ref())}, + OSMNodeID{static_cast(nodes[nodes.size() - 2].ref())}, + OSMNodeID{static_cast(nodes.back().ref())}}); } guidance::LaneDescriptionMap &&ExtractorCallbacks::moveOutLaneDescriptionMap() diff --git a/src/extractor/graph_compressor.cpp b/src/extractor/graph_compressor.cpp index 4339cda18..12a07d319 100644 --- a/src/extractor/graph_compressor.cpp +++ b/src/extractor/graph_compressor.cpp @@ -119,22 +119,30 @@ void GraphCompressor::Compress(const std::unordered_set &barrier_nodes, if (has_node_penalty) continue; - // Get distances before graph is modified - const int forward_weight1 = graph.GetEdgeData(forward_e1).distance; - const int forward_weight2 = graph.GetEdgeData(forward_e2).distance; + // Get weights before graph is modified + const EdgeWeight forward_weight1 = fwd_edge_data1.weight; + const EdgeWeight forward_weight2 = fwd_edge_data2.weight; + const EdgeWeight forward_duration1 = fwd_edge_data1.duration; + const EdgeWeight forward_duration2 = fwd_edge_data2.duration; BOOST_ASSERT(0 != forward_weight1); BOOST_ASSERT(0 != forward_weight2); - const int reverse_weight1 = graph.GetEdgeData(reverse_e1).distance; - const int reverse_weight2 = graph.GetEdgeData(reverse_e2).distance; + const EdgeWeight reverse_weight1 = rev_edge_data1.weight; + const EdgeWeight reverse_weight2 = rev_edge_data2.weight; + const EdgeWeight reverse_duration1 = rev_edge_data1.duration; + const EdgeWeight reverse_duration2 = rev_edge_data2.duration; BOOST_ASSERT(0 != reverse_weight1); BOOST_ASSERT(0 != reverse_weight2); // add weight of e2's to e1 - graph.GetEdgeData(forward_e1).distance += fwd_edge_data2.distance; - graph.GetEdgeData(reverse_e1).distance += rev_edge_data2.distance; + graph.GetEdgeData(forward_e1).weight += forward_weight2; + graph.GetEdgeData(reverse_e1).weight += reverse_weight2; + + // add duration of e2's to e1 + graph.GetEdgeData(forward_e1).duration += forward_duration2; + graph.GetEdgeData(reverse_e1).duration += reverse_duration2; // extend e1's to targets of e2's graph.SetTarget(forward_e1, node_w); @@ -196,10 +204,22 @@ void GraphCompressor::Compress(const std::unordered_set &barrier_nodes, restriction_map.FixupArrivingTurnRestriction(node_w, node_v, node_u, graph); // store compressed geometry in container - geometry_compressor.CompressEdge( - forward_e1, forward_e2, node_v, node_w, forward_weight1, forward_weight2); - geometry_compressor.CompressEdge( - reverse_e1, reverse_e2, node_v, node_u, reverse_weight1, reverse_weight2); + geometry_compressor.CompressEdge(forward_e1, + forward_e2, + node_v, + node_w, + forward_weight1, + forward_weight2, + forward_duration1, + forward_duration2); + geometry_compressor.CompressEdge(reverse_e1, + reverse_e2, + node_v, + node_u, + reverse_weight1, + reverse_weight2, + reverse_duration1, + reverse_duration2); } } } @@ -215,7 +235,7 @@ void GraphCompressor::Compress(const std::unordered_set &barrier_nodes, { const EdgeData &data = graph.GetEdgeData(edge_id); const NodeID target = graph.GetTarget(edge_id); - geometry_compressor.AddUncompressedEdge(edge_id, target, data.distance); + geometry_compressor.AddUncompressedEdge(edge_id, target, data.weight, data.duration); } } } diff --git a/src/extractor/guidance/turn_discovery.cpp b/src/extractor/guidance/turn_discovery.cpp index fc8f868d9..4b603db1d 100644 --- a/src/extractor/guidance/turn_discovery.cpp +++ b/src/extractor/guidance/turn_discovery.cpp @@ -41,7 +41,7 @@ bool findPreviousIntersection(const NodeID node_v, * PREVIOUS_ID. To verify that find, we check the intersection using our PREVIOUS_ID candidate * to check the intersection at NODE for via_edge */ - const constexpr double COMBINE_DISTANCE_CUTOFF = 30; + const constexpr double COMBINE_WEIGHT_CUTOFF = 30; const auto coordinate_extractor = intersection_generator.GetCoordinateExtractor(); const auto coordinates_along_via_edge = @@ -53,7 +53,7 @@ bool findPreviousIntersection(const NodeID node_v, // we check if via-edge is too short. In this case the previous turn cannot influence the turn // at via_edge and the intersection at NODE_W - if (via_edge_length > COMBINE_DISTANCE_CUTOFF) + if (via_edge_length > COMBINE_WEIGHT_CUTOFF) return false; // Node -> Via_Edge -> Intersection[0 == UTURN] -> reverse_of(via_edge) -> Intersection at diff --git a/src/extractor/scripting_environment_lua.cpp b/src/extractor/scripting_environment_lua.cpp index bd7d3cf17..5067837b9 100644 --- a/src/extractor/scripting_environment_lua.cpp +++ b/src/extractor/scripting_environment_lua.cpp @@ -3,6 +3,8 @@ #include "extractor/external_memory_node.hpp" #include "extractor/extraction_helper_functions.hpp" #include "extractor/extraction_node.hpp" +#include "extractor/extraction_segment.hpp" +#include "extractor/extraction_turn.hpp" #include "extractor/extraction_way.hpp" #include "extractor/internal_extractor_edge.hpp" #include "extractor/profile_properties.hpp" @@ -140,6 +142,80 @@ void Sol2ScriptingEnvironment::InitContext(LuaScriptingContext &context) "connectivity", extractor::guidance::RoadPriorityClass::CONNECTIVITY); + context.state.new_enum("turn_type", + "invalid", + extractor::guidance::TurnType::Invalid, + "new_name", + extractor::guidance::TurnType::NewName, + "continue", + extractor::guidance::TurnType::Continue, + "turn", + extractor::guidance::TurnType::Turn, + "merge", + extractor::guidance::TurnType::Merge, + "on_ramp", + extractor::guidance::TurnType::OnRamp, + "off_ramp", + extractor::guidance::TurnType::OffRamp, + "fork", + extractor::guidance::TurnType::Fork, + "end_of_road", + extractor::guidance::TurnType::EndOfRoad, + "notification", + extractor::guidance::TurnType::Notification, + "enter_roundabout", + extractor::guidance::TurnType::EnterRoundabout, + "enter_and_exit_roundabout", + extractor::guidance::TurnType::EnterAndExitRoundabout, + "enter_rotary", + extractor::guidance::TurnType::EnterRotary, + "enter_and_exit_rotary", + extractor::guidance::TurnType::EnterAndExitRotary, + "enter_roundabout_intersection", + extractor::guidance::TurnType::EnterRoundaboutIntersection, + "enter_and_exit_roundabout_intersection", + extractor::guidance::TurnType::EnterAndExitRoundaboutIntersection, + "use_lane", + extractor::guidance::TurnType::UseLane, + "no_turn", + extractor::guidance::TurnType::NoTurn, + "suppressed", + extractor::guidance::TurnType::Suppressed, + "enter_roundabout_at_exit", + extractor::guidance::TurnType::EnterRoundaboutAtExit, + "exit_roundabout", + extractor::guidance::TurnType::ExitRoundabout, + "enter_rotary_at_exit", + extractor::guidance::TurnType::EnterRotaryAtExit, + "exit_rotary", + extractor::guidance::TurnType::ExitRotary, + "enter_roundabout_intersection_at_exit", + extractor::guidance::TurnType::EnterRoundaboutIntersectionAtExit, + "exit_roundabout_intersection", + extractor::guidance::TurnType::ExitRoundaboutIntersection, + "stay_on_roundabout", + extractor::guidance::TurnType::StayOnRoundabout, + "sliproad", + extractor::guidance::TurnType::Sliproad); + + context.state.new_enum("direction_modifier", + "u_turn", + extractor::guidance::DirectionModifier::UTurn, + "sharp_right", + extractor::guidance::DirectionModifier::SharpRight, + "right", + extractor::guidance::DirectionModifier::Right, + "slight_right", + extractor::guidance::DirectionModifier::SlightRight, + "straight", + extractor::guidance::DirectionModifier::Straight, + "slight_left", + extractor::guidance::DirectionModifier::SlightLeft, + "left", + extractor::guidance::DirectionModifier::Left, + "sharp_left", + extractor::guidance::DirectionModifier::SharpLeft); + context.state.new_usertype("sources", "load", &SourceContainer::LoadRasterSource, @@ -156,8 +232,7 @@ void Sol2ScriptingEnvironment::InitContext(LuaScriptingContext &context) sol::property(&ProfileProperties::GetTrafficSignalPenalty, &ProfileProperties::SetTrafficSignalPenalty), "u_turn_penalty", - sol::property(&ProfileProperties::GetUturnPenalty, // - &ProfileProperties::SetUturnPenalty), + sol::property(&ProfileProperties::GetUturnPenalty, &ProfileProperties::SetUturnPenalty), "max_speed_for_map_matching", sol::property(&ProfileProperties::GetMaxSpeedForMapMatching, &ProfileProperties::SetMaxSpeedForMapMatching), @@ -166,7 +241,11 @@ void Sol2ScriptingEnvironment::InitContext(LuaScriptingContext &context) "use_turn_restrictions", &ProfileProperties::use_turn_restrictions, "left_hand_driving", - &ProfileProperties::left_hand_driving); + &ProfileProperties::left_hand_driving, + "weight_precision", + &ProfileProperties::weight_precision, + "weight_name", + sol::property(&ProfileProperties::SetWeightName, &ProfileProperties::GetWeightName)); context.state.new_usertype>( "vector", @@ -233,6 +312,10 @@ void Sol2ScriptingEnvironment::InitContext(LuaScriptingContext &context) &ExtractionWay::forward_speed, "backward_speed", &ExtractionWay::backward_speed, + "forward_rate", + &ExtractionWay::forward_rate, + "backward_rate", + &ExtractionWay::backward_rate, "name", sol::property(&ExtractionWay::GetName, &ExtractionWay::SetName), "ref", @@ -253,6 +336,8 @@ void Sol2ScriptingEnvironment::InitContext(LuaScriptingContext &context) &ExtractionWay::is_startpoint, "duration", &ExtractionWay::duration, + "weight", + &ExtractionWay::weight, "road_classification", &ExtractionWay::road_classification, "forward_mode", @@ -260,17 +345,45 @@ void Sol2ScriptingEnvironment::InitContext(LuaScriptingContext &context) "backward_mode", sol::property(&ExtractionWay::get_backward_mode, &ExtractionWay::set_backward_mode)); + context.state.new_usertype("ExtractionSegment", + "source", + &ExtractionSegment::source, + "target", + &ExtractionSegment::target, + "distance", + &ExtractionSegment::distance, + "weight", + &ExtractionSegment::weight, + "duration", + &ExtractionSegment::duration); + + context.state.new_usertype("ExtractionTurn", + "angle", + &ExtractionTurn::angle, + "turn_type", + &ExtractionTurn::turn_type, + "direction_modifier", + &ExtractionTurn::direction_modifier, + "has_traffic_light", + &ExtractionTurn::has_traffic_light, + "weight", + &ExtractionTurn::weight, + "duration", + &ExtractionTurn::duration); + // Keep in mind .location is undefined since we're not using libosmium's location cache context.state.new_usertype("NodeRef", "id", &osmium::NodeRef::ref); context.state.new_usertype("EdgeSource", "source_coordinate", &InternalExtractorEdge::source_coordinate, - "weight_data", - &InternalExtractorEdge::weight_data); + "weight", + &InternalExtractorEdge::weight_data, + "duration", + &InternalExtractorEdge::duration_data); - context.state.new_usertype( - "WeightData", "speed", &InternalExtractorEdge::WeightData::speed); + // context.state.new_usertype( + // "WeightData", "weight", &InternalExtractorEdge::WeightData::weight_data); context.state.new_usertype("EdgeTarget", "lon", @@ -307,6 +420,8 @@ void Sol2ScriptingEnvironment::InitContext(LuaScriptingContext &context) context.has_node_function = node_function.valid(); context.has_way_function = way_function.valid(); context.has_segment_function = segment_function.valid(); + + // Check profile API version auto maybe_version = context.state.get>("api_version"); if (maybe_version) { @@ -321,6 +436,18 @@ void Sol2ScriptingEnvironment::InitContext(LuaScriptingContext &context) " to " + std::to_string(SUPPORTED_MAX_API_VERSION) + " are supported." + SOURCE_REF); } + + // Assert that version-dependent properties were not changed by profile + switch (context.api_version) + { + case 1: + BOOST_ASSERT(context.properties.GetUturnPenalty() == 0); + BOOST_ASSERT(context.properties.GetTrafficSignalPenalty() == 0); + break; + case 0: + BOOST_ASSERT(context.properties.GetWeightName() == "duration"); + break; + } } const ProfileProperties &Sol2ScriptingEnvironment::GetProfileProperties() @@ -437,37 +564,73 @@ void Sol2ScriptingEnvironment::SetupSources() } } -int32_t Sol2ScriptingEnvironment::GetTurnPenalty(const double angle) +void Sol2ScriptingEnvironment::ProcessTurn(ExtractionTurn &turn) { auto &context = GetSol2Context(); sol::function turn_function = context.state["turn_function"]; - - if (turn_function.valid()) + switch (context.api_version) { - const double penalty = turn_function(angle); + case 1: + if (context.has_turn_penalty_function) + { + turn_function(turn); - BOOST_ASSERT(penalty < std::numeric_limits::max()); - BOOST_ASSERT(penalty > std::numeric_limits::min()); + // Turn weight falls back to the duration value in deciseconds + // or uses the extracted unit-less weight value + if (context.properties.fallback_to_duration) + turn.weight = turn.duration; + } - return penalty; + break; + case 0: + if (context.has_turn_penalty_function) + { + if (turn.turn_type != guidance::TurnType::NoTurn) + { + // Get turn duration and convert deci-seconds to seconds + turn.duration = static_cast(turn_function(turn.angle)) / 10.; + BOOST_ASSERT(turn.weight == 0); + + // add U-turn penalty + if (turn.direction_modifier == guidance::DirectionModifier::UTurn) + turn.duration += context.properties.GetUturnPenalty(); + } + else + { + // Use zero turn penalty if it is not an actual turn. This heuristic is necessary + // since OSRM cannot handle looping roads/parallel roads + turn.duration = 0.; + } + } + + // Add traffic light penalty, back-compatibility of api_version=0 + if (turn.has_traffic_light) + turn.duration += context.properties.GetTrafficSignalPenalty(); + + // Turn weight falls back to the duration value in deciseconds + turn.weight = turn.duration; + break; } - - return 0; } -void Sol2ScriptingEnvironment::ProcessSegment(const osrm::util::Coordinate &source, - const osrm::util::Coordinate &target, - double distance, - InternalExtractorEdge::WeightData &weight) +void Sol2ScriptingEnvironment::ProcessSegment(ExtractionSegment &segment) { auto &context = GetSol2Context(); - sol::function segment_function = context.state["segment_function"]; - - if (segment_function.valid()) + if (context.has_segment_function) { - segment_function(source, target, distance, weight); + sol::function segment_function = context.state["segment_function"]; + switch (context.api_version) + { + case 1: + segment_function(segment); + break; + case 0: + segment_function(segment.source, segment.target, segment.distance, segment.duration); + segment.weight = segment.duration; // back-compatibility fallback to duration + break; + } } } diff --git a/src/storage/storage.cpp b/src/storage/storage.cpp index 617a58494..980b56851 100644 --- a/src/storage/storage.cpp +++ b/src/storage/storage.cpp @@ -279,6 +279,22 @@ void Storage::PopulateLayout(DataLayout &layout) layout.SetBlockSize(DataLayout::CORE_MARKER, number_of_core_markers); } + // load turn weight penalties + { + io::FileReader turn_weight_penalties_file(config.turn_weight_penalties_path, + io::FileReader::HasNoFingerprint); + const auto number_of_penalties = turn_weight_penalties_file.ReadElementCount64(); + layout.SetBlockSize(DataLayout::TURN_WEIGHT_PENALTIES, number_of_penalties); + } + + // load turn duration penalties + { + io::FileReader turn_duration_penalties_file(config.turn_duration_penalties_path, + io::FileReader::HasNoFingerprint); + const auto number_of_penalties = turn_duration_penalties_file.ReadElementCount64(); + layout.SetBlockSize(DataLayout::TURN_DURATION_PENALTIES, number_of_penalties); + } + // load coordinate size { io::FileReader node_file(config.nodes_data_path, io::FileReader::HasNoFingerprint); @@ -307,6 +323,10 @@ void Storage::PopulateLayout(DataLayout &layout) number_of_compressed_geometries); layout.SetBlockSize(DataLayout::GEOMETRIES_REV_WEIGHT_LIST, number_of_compressed_geometries); + layout.SetBlockSize(DataLayout::GEOMETRIES_FWD_DURATION_LIST, + number_of_compressed_geometries); + layout.SetBlockSize(DataLayout::GEOMETRIES_REV_DURATION_LIST, + number_of_compressed_geometries); } // load datasource sizes. This file is optional, and it's non-fatal if it doesn't @@ -582,6 +602,18 @@ void Storage::PopulateData(const DataLayout &layout, char *memory_ptr) BOOST_ASSERT(geometry_node_lists_count == layout.num_entries[DataLayout::GEOMETRIES_REV_WEIGHT_LIST]); geometry_input_file.ReadInto(geometries_rev_weight_list_ptr, geometry_node_lists_count); + + const auto geometries_fwd_duration_list_ptr = layout.GetBlockPtr( + memory_ptr, DataLayout::GEOMETRIES_FWD_DURATION_LIST); + BOOST_ASSERT(geometry_node_lists_count == + layout.num_entries[DataLayout::GEOMETRIES_FWD_DURATION_LIST]); + geometry_input_file.ReadInto(geometries_fwd_duration_list_ptr, geometry_node_lists_count); + + const auto geometries_rev_duration_list_ptr = layout.GetBlockPtr( + memory_ptr, DataLayout::GEOMETRIES_REV_DURATION_LIST); + BOOST_ASSERT(geometry_node_lists_count == + layout.num_entries[DataLayout::GEOMETRIES_REV_DURATION_LIST]); + geometry_input_file.ReadInto(geometries_rev_duration_list_ptr, geometry_node_lists_count); } { @@ -666,6 +698,26 @@ void Storage::PopulateData(const DataLayout &layout, char *memory_ptr) layout.num_entries[DataLayout::COORDINATE_LIST]); } + // load turn weight penalties + { + io::FileReader turn_weight_penalties_file(config.turn_weight_penalties_path, + io::FileReader::HasNoFingerprint); + const auto number_of_penalties = turn_weight_penalties_file.ReadElementCount64(); + const auto turn_weight_penalties_ptr = + layout.GetBlockPtr(memory_ptr, DataLayout::TURN_WEIGHT_PENALTIES); + turn_weight_penalties_file.ReadInto(turn_weight_penalties_ptr, number_of_penalties); + } + + // load turn duration penalties + { + io::FileReader turn_duration_penalties_file(config.turn_duration_penalties_path, + io::FileReader::HasNoFingerprint); + const auto number_of_penalties = turn_duration_penalties_file.ReadElementCount64(); + const auto turn_duration_penalties_ptr = + layout.GetBlockPtr(memory_ptr, DataLayout::TURN_DURATION_PENALTIES); + turn_duration_penalties_file.ReadInto(turn_duration_penalties_ptr, number_of_penalties); + } + // store timestamp { io::FileReader timestamp_file(config.timestamp_path, io::FileReader::HasNoFingerprint); diff --git a/src/storage/storage_config.cpp b/src/storage/storage_config.cpp index 221c27c9e..dfb1fe25a 100644 --- a/src/storage/storage_config.cpp +++ b/src/storage/storage_config.cpp @@ -13,6 +13,8 @@ StorageConfig::StorageConfig(const boost::filesystem::path &base) hsgr_data_path{base.string() + ".hsgr"}, nodes_data_path{base.string() + ".nodes"}, edges_data_path{base.string() + ".edges"}, core_data_path{base.string() + ".core"}, geometries_path{base.string() + ".geometry"}, timestamp_path{base.string() + ".timestamp"}, + turn_weight_penalties_path{base.string() + ".turn_weight_penalties"}, + turn_duration_penalties_path{base.string() + ".turn_duration_penalties"}, datasource_names_path{base.string() + ".datasource_names"}, datasource_indexes_path{base.string() + ".datasource_indexes"}, names_data_path{base.string() + ".names"}, properties_path{base.string() + ".properties"}, @@ -23,7 +25,7 @@ StorageConfig::StorageConfig(const boost::filesystem::path &base) bool StorageConfig::IsValid() const { - const constexpr auto num_files = 13; + const constexpr auto num_files = 15; const boost::filesystem::path paths[num_files] = {ram_index_path, file_index_path, hsgr_data_path, @@ -32,6 +34,8 @@ bool StorageConfig::IsValid() const core_data_path, geometries_path, timestamp_path, + turn_weight_penalties_path, + turn_duration_penalties_path, datasource_indexes_path, datasource_indexes_path, names_data_path, diff --git a/unit_tests/engine/guidance_assembly.cpp b/unit_tests/engine/guidance_assembly.cpp index d8daa4b49..25a36800a 100644 --- a/unit_tests/engine/guidance_assembly.cpp +++ b/unit_tests/engine/guidance_assembly.cpp @@ -42,6 +42,7 @@ BOOST_AUTO_TEST_CASE(trim_short_segments) "", 0.2, 1.9076601161280742, + 0.2, TRAVEL_MODE_DRIVING, {{FloatLongitude{-73.981492}, FloatLatitude{40.768258}}, 329, @@ -61,6 +62,7 @@ BOOST_AUTO_TEST_CASE(trim_short_segments) "", 0, 0, + 0, TRAVEL_MODE_DRIVING, {{FloatLongitude{-73.981495}, FloatLatitude{40.768275}}, 0, @@ -79,7 +81,7 @@ BOOST_AUTO_TEST_CASE(trim_short_segments) geometry.segment_offsets = {0, 2}; geometry.segment_distances = {1.9076601161280742}; geometry.osm_node_ids = {OSMNodeID{0}, OSMNodeID{1}, OSMNodeID{2}}; - geometry.annotations = {{1.9076601161280742, 0.2, 0}, {0, 0, 0}}; + geometry.annotations = {{1.9076601161280742, 0.2, 0.2, 0}, {0, 0, 0, 0}}; trimShortSegments(steps, geometry); diff --git a/unit_tests/extractor/compressed_edge_container.cpp b/unit_tests/extractor/compressed_edge_container.cpp index 87d549660..e4716ee53 100644 --- a/unit_tests/extractor/compressed_edge_container.cpp +++ b/unit_tests/extractor/compressed_edge_container.cpp @@ -16,7 +16,7 @@ BOOST_AUTO_TEST_CASE(long_road_test) CompressedEdgeContainer container; // compress 0---1---2 to 0---2 - container.CompressEdge(0, 1, 1, 2, 1, 1); + container.CompressEdge(0, 1, 1, 2, 1, 1, 11, 11); BOOST_CHECK(container.HasEntryForID(0)); BOOST_CHECK(!container.HasEntryForID(1)); BOOST_CHECK(!container.HasEntryForID(2)); @@ -25,7 +25,7 @@ BOOST_AUTO_TEST_CASE(long_road_test) BOOST_CHECK_EQUAL(container.GetLastEdgeSourceID(0), 1); // compress 2---3---4 to 2---4 - container.CompressEdge(2, 3, 3, 4, 1, 1); + container.CompressEdge(2, 3, 3, 4, 1, 1, 11, 11); BOOST_CHECK(container.HasEntryForID(0)); BOOST_CHECK(!container.HasEntryForID(1)); BOOST_CHECK(container.HasEntryForID(2)); @@ -34,7 +34,7 @@ BOOST_AUTO_TEST_CASE(long_road_test) BOOST_CHECK_EQUAL(container.GetLastEdgeSourceID(2), 3); // compress 0---2---4 to 0---4 - container.CompressEdge(0, 2, 2, 4, 2, 2); + container.CompressEdge(0, 2, 2, 4, 2, 2, 22, 22); BOOST_CHECK(container.HasEntryForID(0)); BOOST_CHECK(!container.HasEntryForID(1)); BOOST_CHECK(!container.HasEntryForID(2)); @@ -54,7 +54,7 @@ BOOST_AUTO_TEST_CASE(t_crossing) CompressedEdgeContainer container; // compress 0---1---2 to 0---2 - container.CompressEdge(0, 1, 1, 2, 1, 1); + container.CompressEdge(0, 1, 1, 2, 1, 1, 11, 11); BOOST_CHECK(container.HasEntryForID(0)); BOOST_CHECK(!container.HasEntryForID(1)); BOOST_CHECK(!container.HasEntryForID(2)); @@ -65,7 +65,7 @@ BOOST_AUTO_TEST_CASE(t_crossing) BOOST_CHECK_EQUAL(container.GetLastEdgeSourceID(0), 1); // compress 2---5---6 to 2---6 - container.CompressEdge(4, 5, 5, 6, 1, 1); + container.CompressEdge(4, 5, 5, 6, 1, 1, 11, 11); BOOST_CHECK(container.HasEntryForID(0)); BOOST_CHECK(!container.HasEntryForID(1)); BOOST_CHECK(!container.HasEntryForID(2)); @@ -76,7 +76,7 @@ BOOST_AUTO_TEST_CASE(t_crossing) BOOST_CHECK_EQUAL(container.GetLastEdgeSourceID(4), 5); // compress 2---3---4 to 2---4 - container.CompressEdge(2, 3, 3, 4, 1, 1); + container.CompressEdge(2, 3, 3, 4, 1, 1, 11, 11); BOOST_CHECK(container.HasEntryForID(0)); BOOST_CHECK(!container.HasEntryForID(1)); BOOST_CHECK(container.HasEntryForID(2)); diff --git a/unit_tests/extractor/graph_compressor.cpp b/unit_tests/extractor/graph_compressor.cpp index 3294231c3..234ba8bec 100644 --- a/unit_tests/extractor/graph_compressor.cpp +++ b/unit_tests/extractor/graph_compressor.cpp @@ -28,6 +28,7 @@ inline InputEdge MakeUnitEdge(const NodeID from, const NodeID to) 1, SPECIAL_EDGEID, 0, + 0, false, false, false, diff --git a/unit_tests/library/route.cpp b/unit_tests/library/route.cpp index d4c2db644..e5400b524 100644 --- a/unit_tests/library/route.cpp +++ b/unit_tests/library/route.cpp @@ -50,6 +50,7 @@ BOOST_AUTO_TEST_CASE(test_route_same_coordinates_fixture) json::Array{{json::Object{ {{"distance", 0.}, {"duration", 0.}, + {"weight_name", "duration"}, {"geometry", "yw_jGupkl@??"}, {"legs", json::Array{{json::Object{ @@ -59,6 +60,7 @@ BOOST_AUTO_TEST_CASE(test_route_same_coordinates_fixture) {"steps", json::Array{{{json::Object{{{"duration", 0.}, {"distance", 0.}, + {"weight", 0.}, {"geometry", "yw_jGupkl@??"}, {"name", "Boulevard du Larvotto"}, {"mode", "driving"}, @@ -78,6 +80,7 @@ BOOST_AUTO_TEST_CASE(test_route_same_coordinates_fixture) json::Object{{{"duration", 0.}, {"distance", 0.}, + {"weight", 0.}, {"geometry", "yw_jGupkl@"}, {"name", "Boulevard du Larvotto"}, {"mode", "driving"}, diff --git a/unit_tests/mocks/mock_datafacade.hpp b/unit_tests/mocks/mock_datafacade.hpp index eac95222f..0b0f4cfce 100644 --- a/unit_tests/mocks/mock_datafacade.hpp +++ b/unit_tests/mocks/mock_datafacade.hpp @@ -68,6 +68,14 @@ class MockDataFacade final : public engine::datafacade::BaseDataFacade { return GeometryID{SPECIAL_GEOMETRYID, false}; } + TurnPenalty GetWeightPenaltyForEdgeID(const unsigned /* id */) const override final + { + return 0; + } + TurnPenalty GetDurationPenaltyForEdgeID(const unsigned /* id */) const override final + { + return 0; + } std::vector GetUncompressedForwardGeometry(const EdgeID /* id */) const override { return {}; @@ -83,12 +91,17 @@ class MockDataFacade final : public engine::datafacade::BaseDataFacade result_weights[0] = 1; return result_weights; } - std::vector GetUncompressedReverseWeights(const EdgeID /* id */) const override + std::vector GetUncompressedReverseWeights(const EdgeID id) const override { - std::vector result_weights; - result_weights.resize(1); - result_weights[0] = 1; - return result_weights; + return GetUncompressedForwardWeights(id); + } + std::vector GetUncompressedForwardDurations(const EdgeID id) const override + { + return GetUncompressedForwardWeights(id); + } + std::vector GetUncompressedReverseDurations(const EdgeID id) const override + { + return GetUncompressedForwardWeights(id); } std::vector GetUncompressedForwardDatasources(const EdgeID /*id*/) const override { @@ -211,6 +224,8 @@ class MockDataFacade final : public engine::datafacade::BaseDataFacade std::string GetTimestamp() const override { return ""; } bool GetContinueStraightDefault() const override { return true; } double GetMapMatchingMaxSpeed() const override { return 180 / 3.6; } + const char *GetWeightName() const override final { return "duration"; } + unsigned GetWeightPrecision() const override final { return 1; } BearingClassID GetBearingClassID(const NodeID /*id*/) const override { return 0; } EntryClassID GetEntryClassID(const EdgeID /*id*/) const override { return 0; } diff --git a/unit_tests/server/parameters_parser.cpp b/unit_tests/server/parameters_parser.cpp index bbd0e5298..89ae71470 100644 --- a/unit_tests/server/parameters_parser.cpp +++ b/unit_tests/server/parameters_parser.cpp @@ -81,8 +81,9 @@ BOOST_AUTO_TEST_CASE(invalid_table_urls) BOOST_AUTO_TEST_CASE(valid_route_hint) { - auto hint = engine::Hint::FromBase64( - "XAYAgP___3-QAAAABAAAACEAAAA_AAAAHgAAAHsFAAAUAAAAaWhxALeCmwI7aHEAy4KbAgUAAQE0h8Z2"); + auto hint = engine::Hint::FromBase64("XAYAgP___3-QAAAABAAAACEAAAA_" + "AAAAHgAAAHsFAAAUAAAAaWhxALeCmwI7aHEAy4KbAjtocQDLgpsCAAAAA" + "KCQXlw="); BOOST_CHECK_EQUAL( hint.phantom.input_location, util::Coordinate(util::FloatLongitude{7.432251}, util::FloatLatitude{43.745995})); @@ -151,12 +152,12 @@ BOOST_AUTO_TEST_CASE(valid_route_urls) CHECK_EQUAL_RANGE(reference_3.hints, result_3->hints); std::vector> hints_4 = { - engine::Hint::FromBase64( - "XAYAgP___3-QAAAABAAAACEAAAA_AAAAHgAAAHsFAAAUAAAAaWhxALeCmwI7aHEAy4KbAgUAAQE0h8Z2"), - engine::Hint::FromBase64( - "lgQAgP___3-QAAAADwAAABMAAAAoAAAALAAAADQAAAAUAAAAmWFxAL1zmwLcYXEAu3ObAgQAAQE0h8Z2"), - engine::Hint::FromBase64( - "OAUAgMUFAIAAAAAADwAAAAIAAAAAAAAAnQAAALwEAAAUAAAAgz5xAE9WmwKIPnEAUFabAgAAAQE0h8Z2")}; + engine::Hint::FromBase64("0QkAgFcLAIBFAAAADAAAACcAAAA0AAAANAAAAB4AAAAmAAAAGA0AAL8QAAARAAAAJ" + "ipxAP1ImwIfKnEABUmbAgIAAQFy"), + engine::Hint::FromBase64("YwsAgP___3-fAAAAEQAAACcAAAAnAAAAJwAAABYAAAAxAAAA3wEAAOQBAAARAAAA-" + "DlxAGBcmwLLOXEAfVybAgEAAQFy"), + engine::Hint::FromBase64("awQAgDIJAID7AQAAAAAAAB0AAAAdAAAAHQAAAP4DAAABAQAADwsAAMULAAARAAAAd" + "T5yADqVmwIAtMQEAAAAABUAAQFy")}; RouteParameters reference_4{false, false, false, @@ -169,9 +170,12 @@ BOOST_AUTO_TEST_CASE(valid_route_urls) std::vector>{}}; auto result_4 = parseParameters( "1,2;3,4?steps=false&hints=" - "XAYAgP___3-QAAAABAAAACEAAAA_AAAAHgAAAHsFAAAUAAAAaWhxALeCmwI7aHEAy4KbAgUAAQE0h8Z2;" - "lgQAgP___3-QAAAADwAAABMAAAAoAAAALAAAADQAAAAUAAAAmWFxAL1zmwLcYXEAu3ObAgQAAQE0h8Z2;" - "OAUAgMUFAIAAAAAADwAAAAIAAAAAAAAAnQAAALwEAAAUAAAAgz5xAE9WmwKIPnEAUFabAgAAAQE0h8Z2"); + "0QkAgFcLAIBFAAAADAAAACcAAAA0AAAANAAAAB4AAAAmAAAAGA0AAL8QAAARAAAAJipxAP1ImwIfKnEABUmbAgIAAQ" + "Fy;" + "YwsAgP___3-fAAAAEQAAACcAAAAnAAAAJwAAABYAAAAxAAAA3wEAAOQBAAARAAAA-" + "DlxAGBcmwLLOXEAfVybAgEAAQFy;" + "awQAgDIJAID7AQAAAAAAAB0AAAAdAAAAHQAAAP4DAAABAQAADwsAAMULAAARAAAAdT5yADqVmwIAtMQEAAAAABUAAQ" + "Fy"); BOOST_CHECK(result_4); BOOST_CHECK_EQUAL(reference_4.steps, result_4->steps); BOOST_CHECK_EQUAL(reference_4.alternatives, result_4->alternatives); @@ -266,11 +270,11 @@ BOOST_AUTO_TEST_CASE(valid_route_urls) {util::FloatLongitude{5}, util::FloatLatitude{6}}, {util::FloatLongitude{7}, util::FloatLatitude{8}}}; std::vector> hints_10 = { - engine::Hint::FromBase64( - "XAYAgP___3-QAAAABAAAACEAAAA_AAAAHgAAAHsFAAAUAAAAaWhxALeCmwI7aHEAy4KbAgUAAQE0h8Z2"), + engine::Hint::FromBase64("0QkAgFcLAIBFAAAADAAAACcAAAA0AAAANAAAAB4AAAAmAAAAGA0AAL8QAAARAAAAJ" + "ipxAP1ImwIfKnEABUmbAgIAAQFy"), boost::none, - engine::Hint::FromBase64( - "lgQAgP___3-QAAAADwAAABMAAAAoAAAALAAAADQAAAAUAAAAmWFxAL1zmwLcYXEAu3ObAgQAAQE0h8Z2"), + engine::Hint::FromBase64("awQAgDIJAID7AQAAAAAAAB0AAAAdAAAAHQAAAP4DAAABAQAADwsAAMULAAARAAAAd" + "T5yADqVmwIAtMQEAAAAABUAAQFy"), boost::none}; RouteParameters reference_10{false, false, @@ -282,11 +286,13 @@ BOOST_AUTO_TEST_CASE(valid_route_urls) hints_10, std::vector>{}, std::vector>{}}; - auto result_10 = parseParameters( - "1,2;3,4;5,6;7,8?steps=false&hints=" - "XAYAgP___3-QAAAABAAAACEAAAA_AAAAHgAAAHsFAAAUAAAAaWhxALeCmwI7aHEAy4KbAgUAAQE0h8Z2;;" - "lgQAgP___3-QAAAADwAAABMAAAAoAAAALAAAADQAAAAUAAAAmWFxAL1zmwLcYXEAu3ObAgQAAQE0h8Z2" - ";"); + auto result_10 = + parseParameters("1,2;3,4;5,6;7,8?steps=false&hints=" + "0QkAgFcLAIBFAAAADAAAACcAAAA0AAAANAAAAB4AAAAmAAAAGA0AAL8QA" + "AARAAAAJipxAP1ImwIfKnEABUmbAgIAAQFy;;" + "awQAgDIJAID7AQAAAAAAAB0AAAAdAAAAHQAAAP4DAAABAQAADwsAAMULA" + "AARAAAAdT5yADqVmwIAtMQEAAAAABUAAQFy" + ";"); BOOST_CHECK(result_10); BOOST_CHECK_EQUAL(reference_10.steps, result_10->steps); BOOST_CHECK_EQUAL(reference_10.alternatives, result_10->alternatives);