[osrm] 01/07: Imported Upstream version 5.2.1+ds

Bas Couwenberg sebastic at debian.org
Mon Jun 13 20:16:07 UTC 2016


This is an automated email from the git hooks/post-receive script.

sebastic pushed a commit to branch master
in repository osrm.

commit 8c9b14d653cf96cb9b911d340c48808cca18eace
Author: Bas Couwenberg <sebastic at xs4all.nl>
Date:   Mon Jun 13 19:31:04 2016 +0200

    Imported Upstream version 5.2.1+ds
---
 .clang-format                                      |   82 +-
 .travis.yml                                        |   39 +-
 CHANGELOG.md                                       |   98 ++
 CMakeLists.txt                                     |  108 +-
 README.md                                          |    2 +-
 cmake/FindTBB.cmake                                |    1 +
 cmake/pkgconfig.in                                 |    2 +-
 codecov.yml                                        |    7 +
 docker/Dockerfile                                  |   10 +-
 docker/run-clang.sh                                |    2 +-
 docker/run-gcc.sh                                  |    2 +-
 docker/test.sh                                     |   11 +-
 docs/http.md                                       |   98 +-
 docs/testing.md                                    |  239 +++++
 example/CMakeLists.txt                             |   13 +-
 example/cmake/FindLibOSRM.cmake                    |   50 +-
 example/example.cpp                                |   10 +-
 features/bicycle/oneway.feature                    |   28 +-
 features/car/names.feature                         |   17 +
 features/car/oneway.feature                        |   24 +-
 features/car/restrictions.feature                  |    1 +
 features/car/traffic_turn_penalties.feature        |   54 +-
 features/guidance/collapse.feature                 |  287 +++++-
 features/guidance/continue.feature                 |   45 +-
 features/guidance/dedicated-turn-roads.feature     |  125 +++
 features/guidance/destination-signs.feature        |   20 +-
 features/guidance/fork.feature                     |   15 +
 features/guidance/intersections.feature            |  153 +++
 features/guidance/motorway.feature                 |   88 +-
 features/guidance/new-name.feature                 |    6 +-
 features/guidance/roundabout-turn.feature          |   22 +
 features/guidance/roundabout.feature               |   24 +
 features/guidance/turn.feature                     |   27 +
 features/options/contract/datasources.feature      |   30 +
 features/step_definitions/data.js                  |   11 +-
 features/step_definitions/matching.js              |   23 +-
 features/step_definitions/options.js               |    6 +
 features/step_definitions/routability.js           |    3 +-
 features/step_definitions/trip.js                  |    4 +-
 features/support/config.js                         |   35 +-
 features/support/data.js                           |   75 +-
 features/support/data_classes.js                   |   79 +-
 features/support/env.js                            |   11 +-
 features/support/hash.js                           |   12 +-
 features/support/hooks.js                          |    2 +
 features/support/launch_classes.js                 |   39 +-
 features/support/route.js                          |   42 +-
 features/support/run.js                            |    2 +-
 features/support/shared_steps.js                   |   17 +-
 features/testbot/alternative.feature               |    5 +-
 features/testbot/basic.feature                     |    2 +-
 features/testbot/bearing.feature                   |  146 +--
 features/testbot/bearing_param.feature             |   84 +-
 features/testbot/compression.feature               |    4 +-
 features/testbot/continue_straight.feature         |    1 +
 features/testbot/matching.feature                  |   24 +
 features/testbot/mode.feature                      |    7 +-
 features/testbot/projection.feature                |   12 +-
 features/testbot/summary.feature                   |    6 +-
 include/contractor/contractor.hpp                  |    4 +-
 include/contractor/graph_contractor.hpp            |  161 +--
 include/engine/api/base_api.hpp                    |   12 +-
 include/engine/api/base_parameters.hpp             |   10 +-
 include/engine/api/json_factory.hpp                |   19 +-
 include/engine/api/match_api.hpp                   |    2 +-
 include/engine/api/match_parameters.hpp            |    1 +
 include/engine/api/nearest_api.hpp                 |    6 +-
 include/engine/api/route_api.hpp                   |   85 +-
 include/engine/api/route_parameters.hpp            |    5 +-
 include/engine/api/table_api.hpp                   |   28 +-
 include/engine/api/table_parameters.hpp            |    5 +-
 include/engine/api/trip_api.hpp                    |    3 +-
 include/engine/base64.hpp                          |   10 +-
 include/engine/datafacade/datafacade_base.hpp      |   39 +-
 include/engine/datafacade/internal_datafacade.hpp  |  207 +++-
 include/engine/datafacade/shared_datafacade.hpp    |  197 +++-
 include/engine/douglas_peucker.hpp                 |    2 +-
 include/engine/engine.hpp                          |    4 +-
 include/engine/geospatial_query.hpp                |  121 +--
 include/engine/guidance/assemble_geometry.hpp      |   47 +-
 include/engine/guidance/assemble_leg.hpp           |   77 +-
 include/engine/guidance/assemble_route.hpp         |    2 +-
 include/engine/guidance/assemble_steps.hpp         |  167 ++-
 include/engine/guidance/leg_geometry.hpp           |   13 +-
 include/engine/guidance/post_processing.hpp        |    9 +-
 include/engine/guidance/route_step.hpp             |   42 +-
 include/engine/guidance/step_maneuver.hpp          |   16 +-
 include/engine/guidance/toolkit.hpp                |    2 +-
 include/engine/hint.hpp                            |    5 +-
 include/engine/internal_route_result.hpp           |    6 +-
 include/engine/map_matching/bayes_classifier.hpp   |    2 +-
 include/engine/phantom_node.hpp                    |    4 +-
 include/engine/plugins/match.hpp                   |    2 +-
 include/engine/plugins/nearest.hpp                 |    2 +-
 include/engine/plugins/plugin_base.hpp             |  110 +-
 include/engine/plugins/tile.hpp                    |    2 +-
 include/engine/plugins/trip.hpp                    |    6 +-
 include/engine/plugins/viaroute.hpp                |    6 +-
 .../engine/routing_algorithms/alternative_path.hpp |  161 ++-
 .../routing_algorithms/direct_shortest_path.hpp    |   20 +-
 include/engine/routing_algorithms/many_to_many.hpp |   13 +-
 include/engine/routing_algorithms/map_matching.hpp |   47 +-
 include/engine/routing_algorithms/routing_base.hpp |  127 ++-
 .../engine/routing_algorithms/shortest_path.hpp    |  120 ++-
 include/engine/search_engine_data.hpp              |    2 +-
 include/engine/trip/trip_brute_force.hpp           |    8 +-
 include/engine/trip/trip_farthest_insertion.hpp    |    7 +-
 include/engine/trip/trip_nearest_neighbour.hpp     |    8 +-
 include/extractor/edge_based_graph_factory.hpp     |   28 +-
 include/extractor/edge_based_node.hpp              |   12 +-
 include/extractor/external_memory_node.hpp         |    8 +-
 include/extractor/extraction_containers.hpp        |    8 +-
 include/extractor/extraction_helper_functions.hpp  |  134 +--
 include/extractor/extraction_way.hpp               |    4 +
 include/extractor/extractor.hpp                    |   21 +-
 include/extractor/extractor_callbacks.hpp          |    7 +-
 include/extractor/extractor_config.hpp             |    4 +-
 include/extractor/guidance/classification_data.hpp |   11 +
 include/extractor/guidance/constants.hpp           |    5 +-
 .../extractor/guidance/intersection_handler.hpp    |    2 +-
 include/extractor/guidance/motorway_handler.hpp    |    8 +-
 include/extractor/guidance/roundabout_type.hpp     |    8 +-
 include/extractor/guidance/toolkit.hpp             |   87 +-
 include/extractor/guidance/turn_analysis.hpp       |    6 +
 include/extractor/guidance/turn_classification.hpp |  104 +-
 include/extractor/guidance/turn_instruction.hpp    |  132 +--
 include/extractor/internal_extractor_edge.hpp      |   35 +-
 include/extractor/original_edge_data.hpp           |   11 +-
 include/extractor/profile_properties.hpp           |   13 +-
 include/extractor/query_node.hpp                   |    6 +-
 include/extractor/raster_source.hpp                |   10 +-
 include/extractor/restriction.hpp                  |    3 +-
 include/extractor/restriction_map.hpp              |    2 +-
 include/extractor/restriction_parser.hpp           |    2 +-
 include/extractor/scripting_environment.hpp        |    2 +-
 include/extractor/tarjan_scc.hpp                   |   14 +-
 include/osrm/bearing.hpp                           |    2 +-
 include/server/api/base_parameters_grammar.hpp     |  109 +-
 include/server/api/match_parameter_grammar.hpp     |   18 +-
 include/server/api/nearest_parameter_grammar.hpp   |   17 +-
 include/server/api/route_parameters_grammar.hpp    |   50 +-
 include/server/api/table_parameter_grammar.hpp     |   25 +-
 include/server/api/tile_parameter_grammar.hpp      |   12 +-
 include/server/api/trip_parameter_grammar.hpp      |    8 +-
 include/server/http/header.hpp                     |    2 +-
 include/server/server.hpp                          |   11 +-
 include/server/service/base_service.hpp            |    5 +-
 include/server/service/match_service.hpp           |    5 +-
 include/server/service/nearest_service.hpp         |    5 +-
 include/server/service/route_service.hpp           |    5 +-
 include/server/service/table_service.hpp           |    5 +-
 include/server/service/tile_service.hpp            |    5 +-
 include/server/service/trip_service.hpp            |    5 +-
 include/storage/shared_barriers.hpp                |    2 +-
 include/storage/shared_datatype.hpp                |    7 +
 include/storage/shared_memory.hpp                  |   15 +-
 include/storage/storage_config.hpp                 |    1 +
 include/util/bearing.hpp                           |    7 +
 include/util/cast.hpp                              |    4 +-
 include/util/coordinate.hpp                        |    8 +-
 include/util/coordinate_calculation.hpp            |    5 +-
 include/util/dist_table_wrapper.hpp                |    6 +-
 include/util/for_each_pair.hpp                     |    2 +-
 include/util/graph_loader.hpp                      |   19 +-
 include/util/guidance/bearing_class.hpp            |   79 ++
 include/util/guidance/entry_class.hpp              |   80 ++
 include/util/guidance/toolkit.hpp                  |   32 +-
 include/util/io.hpp                                |   23 +-
 include/util/iso_8601_duration_parser.hpp          |   82 --
 include/util/json_container.hpp                    |    4 +-
 include/util/json_deep_compare.hpp                 |   51 +-
 include/util/json_renderer.hpp                     |    4 +-
 include/util/lua_util.hpp                          |    2 +-
 include/util/matrix_graph_wrapper.hpp              |    6 +-
 include/util/node_based_graph.hpp                  |   10 +-
 include/util/packed_vector.hpp                     |  195 ++++
 include/util/percent.hpp                           |    2 +-
 include/util/rectangle.hpp                         |   28 +-
 include/util/shared_memory_vector_wrapper.hpp      |    4 +-
 include/util/static_graph.hpp                      |    2 +-
 include/util/static_rtree.hpp                      |  363 ++++---
 include/util/string_util.hpp                       |    3 +-
 include/util/strong_typedef.hpp                    |    2 +-
 include/util/timing_util.hpp                       |   41 -
 include/util/trigonometry_table.hpp                |    3 +-
 include/util/typedefs.hpp                          |   25 +-
 include/util/vector_tile.hpp                       |    1 -
 include/util/web_mercator.hpp                      |   49 +-
 include/util/xor_fast_hash.hpp                     |    2 +-
 profiles/bicycle.lua                               |    4 +-
 profiles/car.lua                                   |   11 +-
 src/benchmarks/CMakeLists.txt                      |   18 +-
 src/benchmarks/match.cpp                           |  236 +++++
 src/benchmarks/static_rtree.cpp                    |   24 +-
 src/contractor/contractor.cpp                      |  739 +++++++++-----
 src/engine/api/json_factory.cpp                    |  139 ++-
 src/engine/douglas_peucker.cpp                     |   13 +-
 src/engine/engine.cpp                              |   11 +-
 src/engine/guidance/assemble_overview.cpp          |   38 +-
 src/engine/guidance/assemble_route.cpp             |   18 +-
 src/engine/guidance/assemble_steps.cpp             |   76 +-
 src/engine/guidance/post_processing.cpp            |  629 +++++++++---
 src/engine/hint.cpp                                |    2 +-
 src/engine/plugins/match.cpp                       |   80 +-
 src/engine/plugins/nearest.cpp                     |    2 +-
 src/engine/plugins/table.cpp                       |    8 +-
 src/engine/plugins/tile.cpp                        |   74 +-
 src/engine/plugins/trip.cpp                        |   16 +-
 src/engine/plugins/viaroute.cpp                    |   22 +-
 src/engine/polyline_compressor.cpp                 |   31 +-
 src/extractor/compressed_edge_container.cpp        |    4 +-
 src/extractor/edge_based_graph_factory.cpp         |  133 ++-
 src/extractor/extraction_containers.cpp            |   70 +-
 src/extractor/extractor.cpp                        |  142 ++-
 src/extractor/extractor_callbacks.cpp              |  156 ++-
 src/extractor/graph_compressor.cpp                 |    8 +-
 src/extractor/guidance/intersection_generator.cpp  |   22 +-
 src/extractor/guidance/intersection_handler.cpp    |   21 +-
 .../guidance/intersection_scenario_three_way.cpp   |    2 +-
 src/extractor/guidance/motorway_handler.cpp        |   78 +-
 src/extractor/guidance/roundabout_handler.cpp      |   75 +-
 src/extractor/guidance/turn_analysis.cpp           |  140 ++-
 src/extractor/guidance/turn_classification.cpp     |  116 +++
 src/extractor/guidance/turn_handler.cpp            |  139 ++-
 src/extractor/restriction_map.cpp                  |    4 +-
 src/extractor/restriction_parser.cpp               |   24 +-
 src/extractor/scripting_environment.cpp            |   80 +-
 src/extractor/suffix_table.cpp                     |    5 +-
 src/osrm/osrm.cpp                                  |    6 +-
 src/server/api/parameters_parser.cpp               |   53 +-
 src/server/api/url_parser.cpp                      |   19 +-
 src/server/connection.cpp                          |   29 +-
 src/server/request_handler.cpp                     |   77 +-
 src/server/service/match_service.cpp               |   22 +-
 src/server/service/nearest_service.cpp             |   18 +-
 src/server/service/route_service.cpp               |   18 +-
 src/server/service/table_service.cpp               |   18 +-
 src/server/service/tile_service.cpp                |    2 +-
 src/server/service/trip_service.cpp                |   15 +-
 src/server/service_handler.cpp                     |    6 +-
 src/storage/storage.cpp                            |  206 +++-
 src/storage/storage_config.cpp                     |   43 +-
 src/tools/components.cpp                           |   23 +-
 src/tools/contract.cpp                             |   31 +-
 src/tools/extract.cpp                              |   12 +-
 src/tools/io-benchmark.cpp                         |    4 +-
 src/tools/routed.cpp                               |  153 +--
 src/tools/springclean.cpp                          |    5 +-
 src/tools/store.cpp                                |   14 +-
 src/tools/unlock_all_mutexes.cpp                   |    2 +-
 src/util/coordinate.cpp                            |   12 +-
 src/util/coordinate_calculation.cpp                |   12 +-
 src/util/guidance/bearing_class.cpp                |   81 ++
 src/util/guidance/entry_class.cpp                  |   38 +
 src/util/name_table.cpp                            |    5 +-
 taginfo.json                                       |    1 +
 third_party/fast-cpp-csv-parser/LICENSE            |   28 -
 third_party/fast-cpp-csv-parser/README.md          |  252 -----
 third_party/fast-cpp-csv-parser/csv.h              | 1068 --------------------
 unit_tests/CMakeLists.txt                          |    4 +-
 unit_tests/engine/base64.cpp                       |    6 +-
 unit_tests/engine/douglas_peucker.cpp              |   12 +-
 unit_tests/engine/geometry_string.cpp              |    6 +-
 unit_tests/engine/guidance_assembly.cpp            |    8 +-
 unit_tests/extractor/compressed_edge_container.cpp |    2 +-
 unit_tests/extractor/graph_compressor.cpp          |    2 +-
 unit_tests/extractor/raster_source.cpp             |    4 +-
 unit_tests/library/args.hpp                        |    2 +-
 unit_tests/library/limits.cpp                      |    8 +-
 unit_tests/library/match.cpp                       |   23 +-
 unit_tests/library/nearest.cpp                     |    6 +-
 unit_tests/library/route.cpp                       |  112 +-
 unit_tests/library/table.cpp                       |   10 +-
 unit_tests/library/tile.cpp                        |   91 +-
 unit_tests/library/trip.cpp                        |    3 +-
 unit_tests/mocks/mock_datafacade.hpp               |   30 +-
 unit_tests/server/parameters_parser.cpp            |   52 +-
 unit_tests/util/bearing.cpp                        |    2 +-
 unit_tests/util/binary_heap.cpp                    |    7 +-
 unit_tests/util/coordinate_calculation.cpp         |   32 +-
 unit_tests/util/duration_parsing.cpp               |   29 +-
 unit_tests/util/dynamic_graph.cpp                  |   11 +-
 unit_tests/util/io.cpp                             |    6 +-
 unit_tests/util/packed_vector.cpp                  |   47 +
 unit_tests/util/range_table.cpp                    |    2 +-
 unit_tests/util/rectangle.cpp                      |  114 ++-
 unit_tests/util/static_graph.cpp                   |   11 +-
 unit_tests/util/static_rtree.cpp                   |   35 +-
 unit_tests/util/string_util.cpp                    |    2 +-
 unit_tests/util/viewport.cpp                       |    2 +-
 unit_tests/util/web_mercator.cpp                   |   30 +-
 291 files changed, 8023 insertions(+), 4862 deletions(-)

diff --git a/.clang-format b/.clang-format
index f5577f5..285ed38 100644
--- a/.clang-format
+++ b/.clang-format
@@ -2,53 +2,89 @@
 Language:        Cpp
 # BasedOnStyle:  LLVM
 AccessModifierOffset: -2
-ConstructorInitializerIndentWidth: 4
+AlignAfterOpenBracket: Align
+AlignConsecutiveAssignments: false
+AlignConsecutiveDeclarations: false
 AlignEscapedNewlinesLeft: false
+AlignOperands:   true
 AlignTrailingComments: true
 AllowAllParametersOfDeclarationOnNextLine: true
+AllowShortBlocksOnASingleLine: true
+AllowShortCaseLabelsOnASingleLine: false
+AllowShortFunctionsOnASingleLine: All
 AllowShortIfStatementsOnASingleLine: false
 AllowShortLoopsOnASingleLine: false
-AllowShortFunctionsOnASingleLine: true
-AlwaysBreakTemplateDeclarations: false
+AlwaysBreakAfterDefinitionReturnType: None
+AlwaysBreakAfterReturnType: None
 AlwaysBreakBeforeMultilineStrings: false
+AlwaysBreakTemplateDeclarations: false
+BinPackArguments: false
+BinPackParameters: false
+BraceWrapping:   
+  AfterClass:      true
+  AfterControlStatement: true
+  AfterEnum:       true
+  AfterFunction:   true
+  AfterNamespace:  true
+  AfterObjCDeclaration: true
+  AfterStruct:     true
+  AfterUnion:      true
+  BeforeCatch:     true
+  BeforeElse:      true
+  IndentBraces:    true
 BreakBeforeBinaryOperators: false
+BreakBeforeBraces: Allman
 BreakBeforeTernaryOperators: true
 BreakConstructorInitializersBeforeComma: false
-BinPackParameters: false
 ColumnLimit:     100
+CommentPragmas:  '^ IWYU pragma:'
 ConstructorInitializerAllOnOneLineOrOnePerLine: false
-DerivePointerBinding: false
+ConstructorInitializerIndentWidth: 4
+ContinuationIndentWidth: 4
+Cpp11BracedListStyle: true
+DerivePointerAlignment: false
+DisableFormat:   false
 ExperimentalAutoDetectBinPacking: false
+ForEachMacros:   [ foreach, Q_FOREACH, BOOST_FOREACH ]
+IncludeCategories: 
+  - Regex:           '^<'
+    Priority:        3
+  - Regex:           '^"(osrm|util|engine|extract|contract)/'
+    Priority:        2
+  - Regex:           '.*'
+    Priority:        1
 IndentCaseLabels: false
-MaxEmptyLinesToKeep: 1
+IndentWidth:     4
+IndentWrappedFunctionNames: false
 KeepEmptyLinesAtTheStartOfBlocks: true
+MacroBlockBegin: ''
+MacroBlockEnd:   ''
+MaxEmptyLinesToKeep: 1
 NamespaceIndentation: None
+ObjCBlockIndentWidth: 2
 ObjCSpaceAfterProperty: false
 ObjCSpaceBeforeProtocolList: true
 PenaltyBreakBeforeFirstCallParameter: 19
 PenaltyBreakComment: 300
-PenaltyBreakString: 1000
 PenaltyBreakFirstLessLess: 120
-PenaltyExcessCharacter: 1000
+PenaltyBreakString: 1000
+PenaltyExcessCharacter: 1000000
 PenaltyReturnTypeOnItsOwnLine: 60
-PointerBindsToType: false
+PointerAlignment: Right
+ReflowComments:  true
+SortIncludes:    true
+SpaceAfterCStyleCast: false
+SpaceBeforeAssignmentOperators: true
+SpaceBeforeParens: ControlStatements
+SpaceInEmptyParentheses: false
 SpacesBeforeTrailingComments: 1
-Cpp11BracedListStyle: true
+SpacesInAngles:  false
+SpacesInContainerLiterals: true
+SpacesInCStyleCastParentheses: false
+SpacesInParentheses: false
+SpacesInSquareBrackets: false
 Standard:        Cpp11
-IndentWidth:     4
 TabWidth:        8
 UseTab:          Never
-BreakBeforeBraces: Allman
-IndentFunctionDeclarationAfterType: false
-SpacesInParentheses: false
-SpacesInAngles:  false
-SpaceInEmptyParentheses: false
-SpacesInCStyleCastParentheses: false
-SpacesInContainerLiterals: true
-SpaceBeforeAssignmentOperators: true
-ContinuationIndentWidth: 4
-CommentPragmas:  '^ IWYU pragma:'
-ForEachMacros:   [ foreach, Q_FOREACH, BOOST_FOREACH ]
-SpaceBeforeParens: ControlStatements
 ...
 
diff --git a/.travis.yml b/.travis.yml
index 83e6614..a81b29c 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -13,10 +13,13 @@ notifications:
 branches:
   only:
     - master
+    - "5.2"
 
 cache:
   ccache: true
   apt: true
+  directories:
+    - test/cache
 
 env:
   global:
@@ -37,7 +40,7 @@ matrix:
         apt:
           sources: ['ubuntu-toolchain-r-test']
           packages: ['g++-5', 'libbz2-dev', 'libstxxl-dev', 'libstxxl1', 'libxml2-dev', 'libzip-dev', 'lua5.1', 'liblua5.1-0-dev', 'libtbb-dev', 'libgdal-dev', 'libluabind-dev', 'libboost-all-dev', 'ccache']
-      env: CCOMPILER='gcc-5' CXXCOMPILER='g++-5' BUILD_TYPE='Debug'
+      env: CCOMPILER='gcc-5' CXXCOMPILER='g++-5' BUILD_TYPE='Debug' COVERAGE=ON
 
     - os: linux
       compiler: "gcc-4.8-debug"
@@ -51,9 +54,9 @@ matrix:
       compiler: "clang-3.8-debug"
       addons: &clang38
         apt:
-          sources: ['llvm-toolchain-precise-3.8', 'ubuntu-toolchain-r-test']
-          packages: ['clang-3.8', 'libbz2-dev', 'libstxxl-dev', 'libstxxl1', 'libxml2-dev', 'libzip-dev', 'lua5.1', 'liblua5.1-0-dev', 'libtbb-dev', 'libgdal-dev', 'libluabind-dev', 'libboost-all-dev', 'ccache']
-      env: CCOMPILER='clang-3.8' CXXCOMPILER='clang++-3.8' BUILD_TYPE='Debug' RUN_CLANG_FORMAT=ON
+          sources: ['ubuntu-toolchain-r-test']
+          packages: ['libstdc++-5-dev', 'libbz2-dev', 'libstxxl-dev', 'libstxxl1', 'libxml2-dev', 'libzip-dev', 'lua5.1', 'liblua5.1-0-dev', 'libtbb-dev', 'libgdal-dev', 'libluabind-dev', 'libboost-all-dev', 'ccache']
+      env: CLANG_VERSION='3.8.0' BUILD_TYPE='Debug' RUN_CLANG_FORMAT=ON
 
     - os: osx
       osx_image: xcode7.3
@@ -112,22 +115,27 @@ matrix:
       #- env: CCOMPILER='clang-3.8' CXXCOMPILER='clang++-3.8' BUILD_TYPE='Release' BUILD_SHARED_LIBS=ON
 
 before_install:
+  - if [[ $(uname -s) == 'Darwin' ]]; then sudo mdutil -i off /; fi;
   - source ./scripts/install_node.sh 4
   - npm install
   - DEPS_DIR="${TRAVIS_BUILD_DIR}/deps"
-  - mkdir -p ${DEPS_DIR} && cd ${DEPS_DIR}
+  - export PATH=${DEPS_DIR}/bin:${PATH} && mkdir -p ${DEPS_DIR}
+  - CMAKE_URL="https://mason-binaries.s3.amazonaws.com/${TRAVIS_OS_NAME}-x86_64/cmake/3.5.2.tar.gz"
+  - travis_retry wget --quiet -O - ${CMAKE_URL} | tar --strip-components=1 -xz -C ${DEPS_DIR}
   - |
-    if [[ "${TRAVIS_OS_NAME}" == "linux" ]]; then
-      CMAKE_URL="http://www.cmake.org/files/v3.5/cmake-3.5.1-Linux-x86_64.tar.gz"
-      mkdir cmake && travis_retry wget --quiet -O - ${CMAKE_URL} | tar --strip-components=1 -xz -C cmake
-      export PATH=${DEPS_DIR}/cmake/bin:${PATH}
-    elif [[ "${TRAVIS_OS_NAME}" == "osx" ]]; then
+    if [[ ${CLANG_VERSION:-false} != false ]]; then
+      export CCOMPILER='clang'
+      export CXXCOMPILER='clang++'
+      CLANG_URL="https://mason-binaries.s3.amazonaws.com/${TRAVIS_OS_NAME}-x86_64/clang/${CLANG_VERSION}.tar.gz"
+      travis_retry wget --quiet -O - ${CLANG_URL} | tar --strip-components=1 -xz -C ${DEPS_DIR}
+    fi
+  - |
+    if [[ "${TRAVIS_OS_NAME}" == "osx" ]]; then
       # implicit deps, but seem to be installed by default with recent images: libxml2 GDAL boost
-      brew install cmake libzip libstxxl lua51 luabind tbb md5sha1sum ccache
+      brew install libzip libstxxl lua51 luabind tbb md5sha1sum ccache
     fi
 
 install:
-  - cd ${TRAVIS_BUILD_DIR}
   - |
     if [[ "${TRAVIS_OS_NAME}" == "linux" ]]; then
       ./scripts/check_taginfo.py taginfo.json profiles/car.lua
@@ -136,6 +144,7 @@ install:
   - export CC=${CCOMPILER} CXX=${CXXCOMPILER}
   - cmake .. -DCMAKE_BUILD_TYPE=${BUILD_TYPE} -DBUILD_SHARED_LIBS=${BUILD_SHARED_LIBS:-OFF} -DCOVERAGE=${COVERAGE:-OFF} -DBUILD_TOOLS=1 -DENABLE_CCACHE=ON
   - echo "travis_fold:start:MAKE"
+  - make osrm-extract --jobs=3
   - make --jobs=${JOBS}
   - make tests --jobs=${JOBS}
   - make benchmarks --jobs=${JOBS}
@@ -148,7 +157,7 @@ install:
     fi
   - popd
   - mkdir example/build && pushd example/build
-  - cmake ..
+  - cmake .. -DCMAKE_BUILD_TYPE=${BUILD_TYPE}
   - make
   - popd
 
@@ -170,9 +179,9 @@ script:
 after_success:
   - |
     if [ -n "${RUN_CLANG_FORMAT}" ]; then
-      ./scripts/format.sh || true # we don't want to fail just yet
+      ./scripts/format.sh # we don't want to fail just yet
     fi
   - |
     if [ -n "${COVERAGE}" ]; then
-      coveralls --build-root build --exclude unit_tests --exclude third_party --exclude node_modules --gcov-options '\-lp'
+      bash <(curl -s https://codecov.io/bash)
     fi
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 54ad915..513de6c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,101 @@
+# 5.2.1
+  Changes from 5.2.0
+  Bugfixes:
+    - Removed debug statement that was spamming the console
+# 5.2.0
+  Changes form 5.2.0 RC2
+   - Bugfixes:
+     - Fixed crash when loading shared memory caused by invalid OSM IDs segment size.
+     - Various small instructions handling fixes
+
+   Changes from 5.1.0
+   - API:
+     - new parameter `annotations` for `route`, `trip` and `match` requests.  Returns additional data about each
+       coordinate along the selected/matched route line per `RouteLeg`:
+         - duration of each segment
+         - distance of each segment
+         - OSM node ids of all segment endpoints
+     - Introducing Intersections for Route Steps. This changes the API format in multiple ways.
+         - `bearing_before`/`bearing_after` of `StepManeuver` are now deprecated and will be removed in the next major release
+         - `location` of `StepManeuvers` is now deprecated and will be removed in the next major release
+         - every `RouteStep` now has property `intersections` containing a list of `Intersection` objects.
+     - Support for destination signs. New member `destinations` in `RouteStep`, based on `destination` and `destination:ref`
+     - Support for name pronunciations. New member `pronunciation` in `RouteStep`, based on `name:pronunciation`
+
+   - Profile changes:
+     - duration parser now accepts P[n]DT[n]H[n]M[n]S, P[n]W, PTHHMMSS and PTHH:MM:SS ISO8601 formats.
+     - `result.destinations` allows you to set a way's destinations
+     - `result.pronunciation` allows you to set way name pronunciations
+     - `highway=motorway_link` no longer implies `oneway` as per the OSM Wiki
+
+   - Infrastructure:
+     - BREAKING: Changed the on-disk encoding of the StaticRTree to reduce ramIndex file size. This breaks the **data format**
+     - BREAKING: Intersection Classification adds a new file to the mix (osrm.icd). This breaks the fileformat for older versions.
+     - Better support for osrm-routed binary upgrade on the fly [UNIX specific]:
+       - Open sockets with SO_REUSEPORT to allow multiple osrm-routed processes serving requests from the same port.
+       - Add SIGNAL_PARENT_WHEN_READY environment variable to enable osrm-routed signal its parent with USR1 when it's running and waiting for requests.
+     - Disable http access logging via DISABLE_ACCESS_LOGGING environment variable.
+
+   - Guidance:
+     - BREAKING: modifies the file format with new internal identifiers
+     - improved detection of turning streets, not reporting new-name in wrong situations
+     - improved handling of sliproads (emit turns instead of 'take the ramp')
+     - improved collapsing of instructions. Some 'new name' instructions will be suppressed if they are without alternative and the segment is short
+
+   - Bugfixes
+     - fixed broken summaries for very short routes
+
+# 5.2.0 RC2
+   Changes from 5.2.0 RC1
+
+   - Guidance:
+     - improved handling of sliproads (emit turns instead of 'take the ramp')
+     - improved collapsing of instructions. Some 'new name' instructions will be suppressed if they are without alternative and the segment is short
+     - BREAKING: modifies the file format with new internal identifiers
+
+   - API:
+     - paramater `annotate` was renamed to `annotations`.
+     - `annotation` as accidentally placed in `Route` instead of `RouteLeg`
+     - Support for destination signs. New member `destinations` in `RouteStep`, based on `destination` and `destination:ref`
+     - Support for name pronunciations. New member `pronunciation` in `RouteStep`, based on `name:pronunciation`
+     - Add `nodes` property to `annotation` in `RouteLeg` containing the ids of nodes covered by the route
+
+   - Profile changes:
+     - `result.destinations` allows you to set a way's destinations
+     - `result.pronunciation` allows you to set way name pronunciations
+     - `highway=motorway_link` no longer implies `oneway` as per the OSM Wiki
+
+   - Infrastructure
+     - BREAKING: Changed the on-disk encoding of the StaticRTree to reduce ramIndex file size. This breaks the **data format**
+
+   - Bugfixes
+     - fixed broken summaries for very short routes
+
+# 5.2.0 RC1
+   Changes from 5.1.0
+
+   - API:
+     - new parameter `annotate` for `route` and `match` requests.  Returns additional data about each
+       coordinate along the selected/matched route line.
+     - Introducing Intersections for Route Steps. This changes the API format in multiple ways.
+         - `bearing_before`/`bearing_after` of `StepManeuver` are now deprecated and will be removed in the next major release
+         - `location` of `StepManeuvers` is now deprecated and will be removed in the next major release
+         - every `RouteStep` now has property `intersections` containing a list of `Intersection` objects.
+
+   - Profile changes:
+     - duration parser now accepts P[n]DT[n]H[n]M[n]S, P[n]W, PTHHMMSS and PTHH:MM:SS ISO8601 formats.
+
+   - Infrastructure:
+     - Better support for osrm-routed binary upgrade on the fly [UNIX specific]:
+       - Open sockets with SO_REUSEPORT to allow multiple osrm-routed processes serving requests from the same port.
+       - Add SIGNAL_PARENT_WHEN_READY environment variable to enable osrm-routed signal its parent with USR1 when it's running and waiting for requests.
+     - BREAKING: Intersection Classification adds a new file to the mix (osrm.icd). This breaks the fileformat for older versions.
+     - Disable http access logging via DISABLE_ACCESS_LOGGING environment
+       variable.
+
+   - Guidance:
+     - improved detection of turning streets, not reporting new-name in wrong situations
+
 # 5.1.0
    Changes with regard to 5.0.0
 
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 8656752..6d02a17 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,4 +1,5 @@
-cmake_minimum_required(VERSION 2.8.8)
+cmake_minimum_required(VERSION 2.8.11)
+# we depend on 2.8.11 introducing target_include_directories
 
 if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_CURRENT_BINARY_DIR AND NOT MSVC_IDE)
   message(FATAL_ERROR "In-source builds are not allowed.
@@ -8,9 +9,25 @@ endif()
 
 project(OSRM C CXX)
 set(OSRM_VERSION_MAJOR 5)
-set(OSRM_VERSION_MINOR 1)
+set(OSRM_VERSION_MINOR 2)
 set(OSRM_VERSION_PATCH 0)
 
+# these two functions build up custom variables:
+#   OSRM_INCLUDE_PATHS and OSRM_DEFINES
+# These variables we want to pass to
+# include_directories and add_definitions for both
+# this build and for sharing externally via pkg-config
+
+function(add_dependency_includes includes)
+  list(APPEND OSRM_INCLUDE_PATHS "${includes}")
+  set(OSRM_INCLUDE_PATHS "${OSRM_INCLUDE_PATHS}" PARENT_SCOPE)
+endfunction(add_dependency_includes)
+
+function(add_dependency_defines defines)
+  list(APPEND OSRM_DEFINES "${defines}")
+  set(OSRM_DEFINES "${OSRM_DEFINES}" PARENT_SCOPE)
+endfunction(add_dependency_defines)
+
 set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
 include(CheckCXXCompilerFlag)
 include(FindPackageHandleStandardArgs)
@@ -32,7 +49,7 @@ endif()
 option(ENABLE_CCACHE "Speed up incremental rebuilds via ccache" ON)
 option(ENABLE_JSON_LOGGING "Adds additional JSON debug logging to the response" OFF)
 option(BUILD_TOOLS "Build OSRM tools" OFF)
-option(BUILD_COMPONENTS "Build osrm-components" ON)
+option(BUILD_COMPONENTS "Build osrm-components" OFF)
 option(ENABLE_ASSERTIONS OFF)
 option(COVERAGE OFF)
 option(SANITIZER OFF)
@@ -54,7 +71,7 @@ configure_file(
   ${CMAKE_CURRENT_SOURCE_DIR}/include/util/version.hpp.in
   ${CMAKE_CURRENT_BINARY_DIR}/include/util/version.hpp
 )
-file(GLOB UtilGlob src/util/*.cpp)
+file(GLOB UtilGlob src/util/*.cpp src/util/*/*.cpp)
 file(GLOB ExtractorGlob src/extractor/*.cpp src/extractor/*/*.cpp)
 file(GLOB ContractorGlob src/contractor/*.cpp)
 file(GLOB StorageGlob src/storage/*.cpp)
@@ -163,7 +180,7 @@ elseif(${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU")
   # using GCC
   set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pedantic -Wuninitialized -Wunreachable-code -Wstrict-overflow=1 -D_FORTIFY_SOURCE=2 ${COLOR_FLAG} -fPIC")
   if(WIN32) # using mingw
-    add_definitions(-DWIN32)
+    add_dependency_defines(-DWIN32)
     set(OPTIONAL_SOCKET_LIBS ws2_32 wsock32)
   endif()
 elseif(${CMAKE_CXX_COMPILER_ID} STREQUAL "Intel")
@@ -172,12 +189,12 @@ elseif(${CMAKE_CXX_COMPILER_ID} STREQUAL "Intel")
 elseif(${CMAKE_CXX_COMPILER_ID} STREQUAL "MSVC")
   # using Visual Studio C++
   set(BOOST_COMPONENTS ${BOOST_COMPONENTS} date_time chrono zlib)
-  add_definitions(-DBOOST_LIB_DIAGNOSTIC)
-  add_definitions(-D_CRT_SECURE_NO_WARNINGS)
-  add_definitions(-DNOMINMAX) # avoid min and max macros that can break compilation
-  add_definitions(-D_USE_MATH_DEFINES) #needed for M_PI with cmath.h
-  add_definitions(-D_WIN32_WINNT=0x0501)
-  add_definitions(-DXML_STATIC)
+  add_dependency_defines(-DBOOST_LIB_DIAGNOSTIC)
+  add_dependency_defines(-D_CRT_SECURE_NO_WARNINGS)
+  add_dependency_defines(-DNOMINMAX) # avoid min and max macros that can break compilation
+  add_dependency_defines(-D_USE_MATH_DEFINES) #needed for M_PI with cmath.h
+  add_dependency_defines(-D_WIN32_WINNT=0x0501)
+  add_dependency_defines(-DXML_STATIC)
   find_library(ws2_32_LIBRARY_PATH ws2_32)
   target_link_libraries(osrm-extract wsock32 ws2_32)
 endif()
@@ -204,7 +221,7 @@ set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} ${LINKER_FLAGS}")
 
 # Activate C++11
 if(NOT ${CMAKE_CXX_COMPILER_ID} STREQUAL "MSVC")
-  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 ")
+  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
 endif()
 
 # Configuring other platform dependencies
@@ -229,41 +246,54 @@ endif()
 list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/third_party/libosmium/cmake")
 set(OSMIUM_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/third_party/libosmium/include")
 find_package(Osmium REQUIRED COMPONENTS io)
-include_directories(SYSTEM ${OSMIUM_INCLUDE_DIRS})
+add_dependency_includes(${OSMIUM_INCLUDE_DIR})
 
 
 find_package(Boost 1.49.0 REQUIRED COMPONENTS ${BOOST_COMPONENTS})
-if(NOT WIN32)
-  add_definitions(-DBOOST_TEST_DYN_LINK)
+
+# collect a subset of the boost libraries needed
+# by libosrm
+foreach(lib ${Boost_LIBRARIES})
+  if(NOT WIN32)
+    if(lib MATCHES filesystem OR lib MATCHES thread OR lib MATCHES iostreams OR lib MATCHES system)
+       list(APPEND BOOST_ENGINE_LIBRARIES "${lib}")
+    endif()
+  else()
+    list(APPEND BOOST_ENGINE_LIBRARIES "${lib}")
+  endif()
+endforeach(lib)
+
+if(NOT WIN32 AND NOT Boost_USE_STATIC_LIBS)
+  add_dependency_defines(-DBOOST_TEST_DYN_LINK)
 endif()
-add_definitions(-DBOOST_SPIRIT_USE_PHOENIX_V3)
-add_definitions(-DBOOST_RESULT_OF_USE_DECLTYPE)
-add_definitions(-DBOOST_FILESYSTEM_NO_DEPRECATED)
-include_directories(SYSTEM ${Boost_INCLUDE_DIRS})
+add_dependency_defines(-DBOOST_SPIRIT_USE_PHOENIX_V3)
+add_dependency_defines(-DBOOST_RESULT_OF_USE_DECLTYPE)
+add_dependency_defines(-DBOOST_FILESYSTEM_NO_DEPRECATED)
+add_dependency_includes(${Boost_INCLUDE_DIRS})
 
 find_package(Threads REQUIRED)
 
 find_package(TBB REQUIRED)
-include_directories(SYSTEM ${TBB_INCLUDE_DIR})
+add_dependency_includes(${TBB_INCLUDE_DIR})
 if(WIN32 AND CMAKE_BUILD_TYPE MATCHES Debug)
   set(TBB_LIBRARIES ${TBB_DEBUG_LIBRARIES})
 endif()
 
 find_package(Luabind REQUIRED)
 include(check_luabind)
-include_directories(SYSTEM ${LUABIND_INCLUDE_DIR})
+add_dependency_includes(${LUABIND_INCLUDE_DIR})
 
 set(USED_LUA_LIBRARIES ${LUA_LIBRARY})
 if(LUAJIT_FOUND)
   set(USED_LUA_LIBRARIES, LUAJIT_LIBRARIES)
 endif()
-include_directories(SYSTEM ${LUA_INCLUDE_DIR})
+add_dependency_includes(${LUA_INCLUDE_DIR})
 
 find_package(EXPAT REQUIRED)
-include_directories(SYSTEM ${EXPAT_INCLUDE_DIRS})
+add_dependency_includes(${EXPAT_INCLUDE_DIRS})
 
 find_package(STXXL REQUIRED)
-include_directories(SYSTEM ${STXXL_INCLUDE_DIR})
+add_dependency_includes(${STXXL_INCLUDE_DIR})
 
 set(OpenMP_FIND_QUIETLY ON)
 find_package(OpenMP)
@@ -273,16 +303,19 @@ if(OPENMP_FOUND)
 endif()
 
 find_package(BZip2 REQUIRED)
-include_directories(SYSTEM ${BZIP_INCLUDE_DIRS})
+add_dependency_includes(${BZIP2_INCLUDE_DIR})
 
 find_package(ZLIB REQUIRED)
-include_directories(SYSTEM ${ZLIB_INCLUDE_DIRS})
+add_dependency_includes(${ZLIB_INCLUDE_DIRS})
 
 if (ENABLE_JSON_LOGGING)
   message(STATUS "Enabling json logging")
-  add_definitions(-DENABLE_JSON_LOGGING)
+  add_dependency_defines(-DENABLE_JSON_LOGGING)
 endif()
 
+add_definitions(${OSRM_DEFINES})
+include_directories(SYSTEM ${OSRM_INCLUDE_PATHS})
+
 # Binaries
 target_link_libraries(osrm-datastore osrm_store ${Boost_LIBRARIES})
 target_link_libraries(osrm-extract osrm_extract ${Boost_LIBRARIES})
@@ -311,12 +344,12 @@ set(CONTRACTOR_LIBRARIES
     ${MAYBE_RT_LIBRARY}
     ${MAYBE_COVERAGE_LIBRARIES})
 set(ENGINE_LIBRARIES
-    ${Boost_LIBRARIES}
+    ${BOOST_ENGINE_LIBRARIES}
     ${CMAKE_THREAD_LIBS_INIT}
-    ${STXXL_LIBRARY}
     ${TBB_LIBRARIES}
     ${MAYBE_RT_LIBRARY}
-    ${MAYBE_COVERAGE_LIBRARIES})
+    ${MAYBE_COVERAGE_LIBRARIES}
+    ${ZLIB_LIBRARY})
 set(STORAGE_LIBRARIES
     ${Boost_LIBRARIES}
     ${CMAKE_THREAD_LIBS_INIT}
@@ -412,14 +445,23 @@ foreach(lib ${ENGINE_LIBRARIES})
   set(ENGINE_LIBRARY_LISTING "${ENGINE_LIBRARY_LISTING} -L${ENGINE_LIBRARY_PATH} -l${ENGINE_LIBRARY_NAME}")
 endforeach()
 
-configure_file(${CMAKE_CURRENT_SOURCE_DIR}/cmake/pkgconfig.in libosrm.pc @ONLY)
-install(FILES ${PROJECT_BINARY_DIR}/libosrm.pc DESTINATION lib/pkgconfig)
-
 if(BUILD_DEBIAN_PACKAGE)
   include(CPackDebianConfig)
   include(CPack)
 endif()
 
+function(JOIN VALUES GLUE OUTPUT)
+  string (REPLACE ";" "${GLUE}" _TMP_STR "${VALUES}")
+  set (${OUTPUT} "${_TMP_STR}" PARENT_SCOPE)
+endfunction()
+
+# Set up variables, then write to pkgconfig file
+JOIN("${OSRM_DEFINES}" " " OSRM_DEFINES_STRING)
+JOIN("-I${OSRM_INCLUDE_PATHS}" " -I" OSRM_INCLUDE_PATHS_STRING)
+
+configure_file(${CMAKE_CURRENT_SOURCE_DIR}/cmake/pkgconfig.in libosrm.pc @ONLY)
+install(FILES ${PROJECT_BINARY_DIR}/libosrm.pc DESTINATION lib/pkgconfig)
+
 # add a target to generate API documentation with Doxygen
 find_package(Doxygen)
 if(DOXYGEN_FOUND)
diff --git a/README.md b/README.md
index f3ace6f..ef6d870 100644
--- a/README.md
+++ b/README.md
@@ -8,7 +8,7 @@ The Open Source Routing Machine is a high performance routing engine written in
 |:-------------|:-------|
 | Linux        | [![Build Status](https://travis-ci.org/Project-OSRM/osrm-backend.png?branch=master)](https://travis-ci.org/Project-OSRM/osrm-backend) |
 | Windows      | [![Build status](https://ci.appveyor.com/api/projects/status/4iuo3s9gxprmcjjh)](https://ci.appveyor.com/project/DennisOSRM/osrm-backend) |
-| Coverage     | [![Coverage Status](https://coveralls.io/repos/github/Project-OSRM/osrm-backend/badge.svg?branch=master)](https://coveralls.io/github/Project-OSRM/osrm-backend?branch=master) |
+| Coverage     | [![codecov](https://codecov.io/gh/Project-OSRM/osrm-backend/branch/master/graph/badge.svg)](https://codecov.io/gh/Project-OSRM/osrm-backend) |
 
 ## Building
 
diff --git a/cmake/FindTBB.cmake b/cmake/FindTBB.cmake
index f9e3e0f..577c34e 100644
--- a/cmake/FindTBB.cmake
+++ b/cmake/FindTBB.cmake
@@ -280,4 +280,5 @@ if (TBB_FOUND)
 	FILE(READ "${TBB_INCLUDE_DIRS}/tbb/tbb_stddef.h" _TBB_VERSION_CONTENTS)
 	STRING(REGEX REPLACE ".*#define TBB_INTERFACE_VERSION ([0-9]+).*" "\\1" TBB_INTERFACE_VERSION "${_TBB_VERSION_CONTENTS}")
 	set(TBB_INTERFACE_VERSION "${TBB_INTERFACE_VERSION}")
+        message(STATUS "TBB interface version: ${TBB_INTERFACE_VERSION}")
 endif (TBB_FOUND)
diff --git a/cmake/pkgconfig.in b/cmake/pkgconfig.in
index c85a076..06ba3af 100644
--- a/cmake/pkgconfig.in
+++ b/cmake/pkgconfig.in
@@ -8,4 +8,4 @@ Version: v at OSRM_VERSION_MAJOR@. at OSRM_VERSION_MINOR@. at OSRM_VERSION_PATCH@
 Requires:
 Libs: -L${libdir} -losrm
 Libs.private: @ENGINE_LIBRARY_LISTING@
-Cflags: -I${includedir} -I${includedir}/osrm
+Cflags: -I${includedir} -I${includedir}/osrm @OSRM_INCLUDE_PATHS_STRING@ @OSRM_DEFINES_STRING@ @CMAKE_CXX_FLAGS@
diff --git a/codecov.yml b/codecov.yml
new file mode 100644
index 0000000..1e5c1b9
--- /dev/null
+++ b/codecov.yml
@@ -0,0 +1,7 @@
+coverage:
+
+  ignore:
+    - unit_tests/.*
+    - third_party/.*
+
+comment: off
diff --git a/docker/Dockerfile b/docker/Dockerfile
index aa37682..72ec608 100644
--- a/docker/Dockerfile
+++ b/docker/Dockerfile
@@ -5,15 +5,23 @@ RUN apt-get install -y build-essential git-core python-pip python-software-prope
 
 RUN apt-get -y install gcc-4.8 g++-4.8 libboost1.55-all-dev llvm-3.4
 RUN apt-get -y install libbz2-dev libstxxl-dev libstxxl1 libxml2-dev
-RUN apt-get -y install libzip-dev lua5.1 liblua5.1-0-dev libtbb-dev libgdal-dev ruby1.9
+RUN apt-get -y install libzip-dev lua5.1 liblua5.1-0-dev libtbb-dev libgdal-dev
 RUN apt-get -y install curl cmake cmake-curses-gui
 
 RUN pip install awscli
 
+
 # luabind
 RUN curl https://gist.githubusercontent.com/DennisOSRM/f2eb7b948e6fe1ae319e/raw/install-luabind.sh | sudo bash
 
+WORKDIR /opt
+RUN git clone --depth 1 --branch v0.31.0 https://github.com/creationix/nvm.git
+RUN /bin/bash -c "source /opt/nvm/nvm.sh && nvm install v4"
+
 RUN useradd -ms /bin/bash mapbox
 USER mapbox
 ENV HOME /home/mapbox
 WORKDIR /home/mapbox
+
+RUN echo "source /opt/nvm/nvm.sh" > .bashrc
+RUN echo "source /home/mapbox/.bashrc" > .profile
diff --git a/docker/run-clang.sh b/docker/run-clang.sh
index 716beb6..bfe3472 100755
--- a/docker/run-clang.sh
+++ b/docker/run-clang.sh
@@ -8,4 +8,4 @@ docker run \
     -e "CXX=clang++" \
     -v `pwd`:/home/mapbox/osrm-backend \
     -t mapbox/osrm:linux \
-    osrm-backend/docker/test.sh
+    /bin/bash -lc "osrm-backend/docker/test.sh"
diff --git a/docker/run-gcc.sh b/docker/run-gcc.sh
index 313b0f7..2b2527b 100755
--- a/docker/run-gcc.sh
+++ b/docker/run-gcc.sh
@@ -8,4 +8,4 @@ docker run \
     -e "CXX=g++" \
     -v `pwd`:/home/mapbox/osrm-backend \
     -t mapbox/osrm:linux \
-    osrm-backend/docker/test.sh
+    /bin/bash -lc "osrm-backend/docker/test.sh"
diff --git a/docker/test.sh b/docker/test.sh
index 0cefe5d..1c75df4 100755
--- a/docker/test.sh
+++ b/docker/test.sh
@@ -4,11 +4,8 @@ set -e
 set -o pipefail
 
 export CMAKEOPTIONS="-DCMAKE_BUILD_TYPE=Release"
-export PATH=$PATH:/home/mapbox/.gem/ruby/1.9.1/bin:/home/mapbox/osrm-backend/vendor/bundle/ruby/1.9.1/bin
 
 cd /home/mapbox/osrm-backend
-gem install --user-install bundler
-bundle install --path vendor/bundle
 [ -d build ] && rm -rf build
 mkdir -p build
 cd build
@@ -16,7 +13,9 @@ cmake .. $CMAKEOPTIONS -DBUILD_TOOLS=1
 
 make -j`nproc`
 make tests -j`nproc`
-./datastructure-tests
-./algorithm-tests
+#./unit_tests/server-tests
+#./unit_tests/library-tests
+#./unit_tests/extractor-tests
+#./unit_tests/util-tests
 cd ..
-bundle exec cucumber -p verify
+npm test
diff --git a/docs/http.md b/docs/http.md
index cbf0fe8..1b02153 100644
--- a/docs/http.md
+++ b/docs/http.md
@@ -1,3 +1,18 @@
+## Environent Variables
+
+### SIGNAL_PARENT_WHEN_READY
+
+If the SIGNAL_PARENT_WHEN_READY environment variable is set osrm-routed will
+send the USR1 signal to its parent when it will be running and waiting for
+requests. This could be used to upgrade osrm-routed to a new binary on the fly
+without any service downtime - no incoming requests will be lost.
+
+### DISABLE_ACCESS_LOGGING
+
+If the DISABLE_ACCESS_LOGGING environment variable is set osrm-routed will
+**not** log any http requests to standard output. This can be useful in high
+traffic setup.
+
 ## HTTP API
 
 `osrm-routed` supports only `GET` requests of the form. If you your response size
@@ -136,7 +151,7 @@ http://router.project-osrm.org/nearest/v1/driving/13.388860,52.517037?number=3&b
 ### Request
 
 ```
-http://{server}/route/v1/{profile}/{coordinates}?alternatives={true|false}&steps={true|false}&geometries={polyline|geojson}&overview={full|simplified|false}
+http://{server}/route/v1/{profile}/{coordinates}?alternatives={true|false}&steps={true|false}&geometries={polyline|geojson}&overview={full|simplified|false}&annotations={true|false}
 ```
 
 In addition to the [general options](#general-options) the following options are supported for this service:
@@ -145,6 +160,7 @@ In addition to the [general options](#general-options) the following options are
 |------------|------------------------------------------|-------------------------------------------------------------------------------|
 |alternatives|`true`, `false` (default)                 |Search for alternative routes and return as well.\*                            |
 |steps       |`true`, `false` (default)                 |Return route steps for each route leg                                          |
+|annotations |`true`, `false` (default)                 |Returns additional metadata for each coordinate along the route geometry.      |
 |geometries  |`polyline` (default), `geojson`           |Returned route geometry format (influences overview and per step)             |
 |overview    |`simplified` (default), `full`, `false`   |Add overview geometry either full, simplified according to highest zoom level it could be display on, or not at all.|
 |continue_straight |`default` (default), `true`, `false`|Forces the route to keep going straight at waypoints and don't do a uturn even if it would be faster. Default value depends on the profile. |
@@ -237,7 +253,7 @@ The algorithm might not be able to match all points. Outliers are removed if the
 ### Request
 
 ```
-http://{server}/match/v1/{profile}/{coordinates}?steps={true|false}&geometries={polyline|geojson}&overview={simplified|full|false}
+http://{server}/match/v1/{profile}/{coordinates}?steps={true|false}&geometries={polyline|geojson}&overview={simplified|full|false}&annotations={true|false}
 ```
 
 In addition to the [general options](#general-options) the following options are supported for this service:
@@ -247,6 +263,7 @@ In addition to the [general options](#general-options) the following options are
 |------------|------------------------------------------------|------------------------------------------------------------------------------------------|
 |steps       |`true`, `false` (default)                       |Return route steps for each route                                                         |
 |geometries  |`polyline` (default), `geojson`                 |Returned route geometry format (influences overview and per step)                        |
+|annotations |`true`, `false` (default)                       |Returns additional metadata for each coordinate along the route geometry.                |
 |overview    |`simplified` (default), `full`, `false`         |Add overview geometry either full, simplified according to highest zoom level it could be display on, or not at all.|
 |timestamps  |`{timestamp};{timestamp}[;{timestamp} ...]`     |Timestamp of the input location.                                                          |
 |radiuses    |`{radius};{radius}[;{radius} ...]`              |Standard deviation of GPS precision used for map matching. If applicable use GPS accuracy.|
@@ -284,7 +301,7 @@ multiple trips for each connected component are returned.
 ### Request
 
 ```
-http://{server}/trip/v1/{profile}/{coordinates}?steps={true|false}&geometries={polyline|geojson}&overview={simplified|full|false}
+http://{server}/trip/v1/{profile}/{coordinates}?steps={true|false}&geometries={polyline|geojson}&overview={simplified|full|false}&annotations={true|false}
 ```
 
 In addition to the [general options](#general-options) the following options are supported for this service:
@@ -292,6 +309,7 @@ In addition to the [general options](#general-options) the following options are
 |Option      |Values                                          |Description                                                                |
 |------------|------------------------------------------------|---------------------------------------------------------------------------|
 |steps       |`true`, `false` (default)                       |Return route instructions for each trip                                    |
+|annotations |`true`, `false` (default)                       |Returns additional metadata for each coordinate along the route geometry.      |
 |geometries  |`polyline` (default), `geojson`                 |Returned route geometry format (influences overview and per step)         |
 |overview    |`simplified` (default), `full`, `false`         |Add overview geometry either full, simplified according to highest zoom level it could be display on, or not at all.|
 
@@ -377,15 +395,27 @@ Represents a route between two waypoints.
    | true         | array of `RouteStep` objects describing the turn-by-turn instructions |
    | false        | empty array                                                           |
 
+- `annotation`: Additional details about each coordinate along the route geometry:
+
+   | annotations  |                                                                       |
+   |--------------|-----------------------------------------------------------------------|
+   | true         | returns distance and durations of each coordinate along the route     |
+   | false        | will not exist                                                        |
+
 #### Example
 
-With `steps=false`:
+With `steps=false` and `annotations=true`:
 
 ```json
 {
   "distance": 30.0,
   "duration": 100.0,
   "steps": []
+  "annotation": {
+    "distance": [5,5,10,5,5],
+    "duration": [15,15,40,15,15],
+    "nodes": [49772551,49772552,49786799,49786800,49786801,49786802]
+  }
 }
 ```
 
@@ -407,11 +437,39 @@ step.
   | geojson    | [GeoJSON `LineString`](http://geojson.org/geojson-spec.html#linestring) or [GeoJSON `Point`](http://geojson.org/geojson-spec.html#point) if it is only one coordinate (not wrapped by a GeoJSON feature)|
   
 - `name`: The name of the way along which travel proceeds.
+- `pronunciation`: The pronunciation hint of the way name. Will be `undefined` if there is no pronunciation hit.
+- `destinations`: The destinations of the way. Will be `undefined` if there are no destinations.
 - `mode`: A string signifying the mode of transportation.
 - `maneuver`: A `StepManeuver` object representing the maneuver.
+- `intersections`: A list of `Intersections` that are passed along the segment, the very first belonging to the StepManeuver
 
 #### Example
 
+```
+{
+ "distance":152.3,
+ "duration":15.6,
+ "name":"Lortzingstraße",
+ "maneuver":{
+     "type":"depart",
+     "modifier":"left"
+ },
+ "geometry":"{lu_IypwpAVrAvAdI",
+ "mode":"driving",
+ "intersections":[
+    {"location":[13.39677,52.54366],
+    "out":1,
+    "bearings":[66,246],
+    "entry":["true","true"]},
+    {"location":[13.394718,52.543096],
+    "in":0,
+    "out":2,
+    "bearings":[60,150,240,330],
+    "entry":["false","true","true","true"]
+    }
+]}
+```
+
 ### StepManeuver
 
 #### Properties
@@ -427,7 +485,7 @@ step.
   | `type`            | Description                                                  |
   |-------------------|--------------------------------------------------------------|
   | turn              | a basic turn into direction of the `modifier`                |
-  | new name          | no turn is taken, but the road name changes. The Road can take a turn itself, following `modifier`                  |
+  | new name          | no turn is taken/possible, but the road name changes. The road can take a turn itself, following `modifier`.                  |
   | depart            | indicates the departure of the leg                           |
   | arrive            | indicates the destination of the leg                         |
   | merge             | merge onto a street (e.g. getting on the highway from a ramp, the `modifier specifies the direction of the merge`) |
@@ -474,11 +532,39 @@ step.
   | `type`                 | Description                                                                                                               |
   |------------------------|---------------------------------------------------------------------------------------------------------------------------|
   | `roundabout`           | Number of the roundabout exit to take. If exit is `undefined` the destination is on the roundabout.                       |
-  | `turn` or `end of road`| Indicates the number of intersections passed until the turn. Example instruction: `at the fourth intersection, turn left` |
+  | else                   | Indicates the number of intersections passed until the turn. Example instruction: `at the fourth intersection, turn left` |
   
 
 New properties (potentially depending on `type`) may be introduced in the future without an API version change.
 
+### Intersections
+
+An intersection gives a full representation of any cross-way the path passes bay. For every step, the very first intersection (`intersections[0]`) corresponds to the
+location of the StepManeuver. Further intersections are listed for every cross-way until the next turn instruction.
+
+#### Properties
+
+- `location`: A `[longitude, latitude]` pair describing the location of the turn.
+- `bearings`: A list of bearing values (e.g. [0,90,180,270]) that are available at the intersection. The bearings describe all available roads at the intersection.
+- `entry`: A list of entry flags, corresponding in a 1:1 relationship to the bearings. A value of `true` indicates that the respective road could be entered on a valid route.
+  `false` indicates that the turn onto the respective road would violate a restriction.
+- `in`: index into bearings/entry array. Used to calculate the bearing just before the turn. Namely, the clockwise angle from true north to the
+  direction of travel immediately before the maneuver/passing the intersection. Bearings are given relative to the intersection. To get the bearing
+  in the direction of driving, the bearing has to be rotated by a value of 180. The value is not supplied for `depart` maneuvers.
+- `out`: index into the bearings/entry array. Used to extract the bearing just after the turn. Namely, The clockwise angle from true north to the
+  direction of travel immediately after the maneuver/passing the intersection. The value is not supplied for `arrive` maneuvers.
+
+#### Example
+```
+{
+    "location":[13.394718,52.543096],
+    "in":0,
+    "out":2,
+    "bearings":[60,150,240,330],
+    "entry":["false","true","true","true"]
+}
+```
+
 ### Waypoint
 
 Object used to describe waypoint on a route.
diff --git a/docs/testing.md b/docs/testing.md
new file mode 100644
index 0000000..7331d65
--- /dev/null
+++ b/docs/testing.md
@@ -0,0 +1,239 @@
+# Testsuite
+
+OSRM comes with a testsuite containing both unit-tests using the Boost library and cucucmber.js for scenario driven testing.
+
+## Unit Tests
+
+For a general introduction on Boost.Test have a look at [its docs](http://www.boost.org/doc/libs/1_60_0/libs/test/doc/html/index.html).
+
+### Separate Test Binaries
+
+Unit tests should be registered according to the sub-project they're in.
+If you want to write tests for utility functions, add them to the utility test binary.
+See `CMakeLists.txt` in the unit test directory for how to register new unit tests.
+
+### Using Boost.Test Primitives
+
+There is a difference between only reporting a failed condition and aborting the test right at a failed condition.
+Have a look at [`BOOST_CHECK` vs `BOOST_REQUIRE`](http://www.boost.org/doc/libs/1_60_0/libs/test/doc/html/boost_test/utf_reference/testing_tool_ref/assertion_boost_level.html).
+Instead of manually checking e.g. for equality, less than, if a function throws etc. use their [corresponding Boost.Test primitives](http://www.boost.org/doc/libs/1_60_0/libs/test/doc/html/boost_test/utf_reference/testing_tool_ref.html).
+
+If you use `BOOST_CHECK_EQUAL` you have to implement `operator<<` for your type so that Boost.Test can print mismatches.
+If you do not want to do this, define `BOOST_TEST_DONT_PRINT_LOG_VALUE` (and undef it after the check call) or sidestep it with `BOOST_CHECK(fst == snd);`.
+
+### Test Fixture
+
+If you need to test features on a real dataset (think about this twice: prefer cucumber and dataset-independent tests for their reproducibility and minimality), there is a fixed dataset in `test/data`.
+This dataset is a small extract and may not even contain all tags or edge cases.
+Furthermore this dataset is not in sync with what you see in up-to-date OSM maps or on the demo server.
+See the library tests for how to add new dataset dependent tests.
+
+
+## Cucumber
+
+For a general introduction on cucumber in our testsuite, have a look at [the wiki](https://github.com/Project-OSRM/osrm-backend/wiki/Cucumber-Test-Suite).
+
+This documentation aims to supply a guideline on how to write cucumber tests that test new features introduced into osrm.
+
+### Test the feature
+
+It is often tempting to reduce the test to a path and accompanying instructions. Instructions can and will change over the course of improving guidance.
+
+Instructions should only be used when writing a feature located in `features/guidance`. All other features should avoid using instructions at all.
+
+### Write Tests to Scale
+
+OSRM is a navigation engine. Tests should always consider this background. 
+
+An important implication is the grid size. If tests use a very small grid size, you run into the chance of instructions being omitted.
+For example:
+
+```
+Background:
+    Given the profile "car"
+    Given a grid size of 10 meters
+
+Scenario: Testbot - Straight Road
+    Given the node map
+        | a | b | c | d |
+
+    And the ways
+        | nodes | highway |
+        | ab    | primary |
+        | bc    | primary |
+        | cd    | primary |
+
+    When I route I should get
+        | from | to | route       |
+        | a    | d  | ab,bc,cd,cd |
+
+```
+
+In a navigation engine, the instructions
+
+ - depart east on ab
+ - in 10 meters the road name changes to bc
+ - in 10 meters the road name changes to cd
+ - you arrived at cd
+ 
+would be impossible to announce and not helpful at all.
+Since no actual choices exist, the route you get could result in `ab,cd` and simply say `depart` and `arrive`.
+
+To prevent such surprises, always consider the availability of other roads and use grid sizes/road lengths that correspond to actually reasonable scenarios in a road network.
+
+### Use names
+
+If you specify many nodes in close succession to present a specific road geometry, consider using `name` to indicate to OSRM that the segment is a single road.
+
+```
+Background:
+    Given the profile "car"
+    Given a grid size of 10 meters
+
+Scenario: Testbot - Straight Road
+    Given the node map
+        | a | b | c | d |
+
+    And the ways
+        | nodes | highway | name |
+        | ab    | primary | road |
+        | bc    | primary | road |
+        | cd    | primary | road |
+
+    When I route I should get
+        | from | to | route     | turns         |
+        | a    | d  | road,road | depart,arrive |
+
+```
+
+Guidance guarantees only essential maneuvers. You will always see `depart` and `arrive` as well as all turns that are not obvious.
+
+So the following scenario does not change the instructions
+
+```
+Background:
+    Given the profile "car"
+    Given a grid size of 10 meters
+
+Scenario: Testbot - Straight Road
+    Given the node map
+        | a | b |
+        | d | c |
+
+    And the ways
+        | nodes | highway | name |
+        | ab    | primary | road |
+        | bc    | primary | road |
+        | cd    | primary | road |
+
+    When I route I should get
+        | from | to | route     | turns         |
+        | a    | d  | road,road | depart,arrive |
+```
+
+but if we modify it to
+
+```
+Background:
+    Given the profile "car"
+    Given a grid size of 10 meters
+
+Scenario: Testbot - Straight Road
+    Given the node map
+        | a | b | e |
+        | d | c |   |
+
+    And the ways
+        | nodes | highway | name |
+        | ab    | primary | road |
+        | bc    | primary | road |
+        | cd    | primary | road |
+        | be    | primary | turn |
+
+    When I route I should get
+        | from | to | route          | turns                        |
+        | a    | d  | road,road,road | depart,continue right,arrive |
+```
+
+### Test all directions
+
+Modelling a road as roundabout has an implied oneway tag associated with it. In the following case, we can route from `a` to `d` but not from `d` to `a`.
+To discover those errors, make sure to check for all allowed directions.
+
+```
+Scenario: Enter and Exit mini roundabout with sharp angle   # features/guidance/mini-roundabout.feature:37
+    Given the profile "car"                                   # features/step_definitions/data.js:8
+    Given a grid size of 10 meters                            # features/step_definitions/data.js:20
+    Given the node map                                        # features/step_definitions/data.js:45
+        | a | b |   |
+        |   | c | d |
+    And the ways                                              # features/step_definitions/data.js:128
+        | nodes | highway         | name |
+        | ab    | tertiary        | MySt |
+        | bc    | roundabout      |      |
+        | cd    | tertiary        | MySt |
+    When I route I should get                                 # features/step_definitions/routing.js:4
+        | from | to | route     | turns         | #                                               |
+        | a    | d  | MySt,MySt | depart,arrive | # suppress multiple enter/exit mini roundabouts |
+        | d    | a  | MySt,MySt | depart,arrive | # suppress multiple enter/exit mini roundabouts |
+    Tables were not identical:
+        |  from |     to |     route     |     turns         |     #
+        |     a |      d |     MySt,MySt |     depart,arrive |     # suppress multiple enter/exit mini roundabouts |
+        | (-) d |  (-) a | (-) MySt,MySt | (-) depart,arrive | (-) # suppress multiple enter/exit mini roundabouts |
+        | (+) d |  (+) a | (+)           | (+)               | (+) # suppress multiple enter/exit mini roundabouts |
+```
+
+### Prevent Randomness
+
+Some features in OSRM can result in strange experiences during testcases. To prevent some of these issues, follow the guidelines below.
+
+#### Use Waypoints
+
+Using grid nodes as waypoints offers the chance of unwanted side effects.
+OSRM converts the grid into a so called edge-based graph.
+
+```
+Scenario: Testbot - Intersection
+    Given the node map
+        |   | e |   |
+        | b | a | d |
+        |   | c |   |
+
+    And the ways
+        | nodes | highway | oneway |
+        | ab    | primary | yes    |
+        | ac    | primary | yes    |
+        | ad    | primary | yes    |
+        | ae    | primary | yes    |
+```
+Selecting `a` as a `waypoint` results in four possible starting locations. Which one of the routes `a,b`, `a,c`, `a,d`, or `a,e` is found is pure chance and depends on the order in the static `r-tree`.
+
+To guarantee discovery, use:
+
+```
+Scenario: Testbot - Intersection
+    Given the node map
+        |   |   | e |   |   |
+        |   |   | 4 |   |   |
+        | b | 1 | a | 3 | d |
+        |   |   | 2 |   |   |
+        |   |   | c |   |   |
+
+    And the ways
+        | nodes | highway | oneway |
+        | ab    | primary | yes    |
+        | ac    | primary | yes    |
+        | ad    | primary | yes    |
+        | ae    | primary | yes    |
+```
+And use `1`,`2`,`3`, and `4` as starting waypoints. The routes `1,b`, `2,c`, `3,d`, and `4,e` can all be discovered.
+
+#### Allow For Small Offsets
+
+Whenever you are independent of the start location (see use waypoints), the waypoint chosen as start/end location can still influence distances/durations.
+
+If you are testing for a duration metric, allow for a tiny offset to ensure a passing test in the presence of rounding/snapping issues.
+
+#### Don't Rely on Alternatives
+
+Alternative route discovery is a random feature in itself. The discovery of routes depends on the contraction order of roads and cannot be assumed successful, ever.
diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt
index 9ad67cb..89e4419 100644
--- a/example/CMakeLists.txt
+++ b/example/CMakeLists.txt
@@ -6,10 +6,13 @@ Please create a directory and run cmake from there, passing the path to this sou
 This process created the file `CMakeCache.txt' and the directory `CMakeFiles'. Please delete them.")
 endif()
 
+if(NOT CMAKE_BUILD_TYPE MATCHES Debug)
+  set(CMAKE_BUILD_TYPE Release)
+endif()
+
 project(osrm-example C CXX)
 
 list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
-set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 ")
 
 set(bitness 32)
 if(CMAKE_SIZEOF_VOID_P EQUAL 8)
@@ -23,11 +26,11 @@ if(WIN32 AND MSVC_VERSION LESS 1900)
   message(FATAL_ERROR "Building with Microsoft compiler needs Latest Visual Studio 2015 (Community or better)")
 endif()
 
+link_directories(${LibOSRM_LIBRARY_DIRS})
 add_executable(osrm-example example.cpp)
 
 find_package(LibOSRM REQUIRED)
-find_package(Boost 1.49.0 COMPONENTS filesystem system thread REQUIRED)
-
-target_link_libraries(osrm-example ${LibOSRM_LIBRARIES} ${Boost_LIBRARIES})
-include_directories(SYSTEM ${LibOSRM_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS})
 
+target_link_libraries(osrm-example ${LibOSRM_LIBRARIES} ${LibOSRM_DEPENDENT_LIBRARIES})
+include_directories(SYSTEM ${LibOSRM_INCLUDE_DIRS})
+set(CMAKE_CXX_FLAGS ${LibOSRM_CXXFLAGS})
diff --git a/example/cmake/FindLibOSRM.cmake b/example/cmake/FindLibOSRM.cmake
index 8475c74..50a5641 100644
--- a/example/cmake/FindLibOSRM.cmake
+++ b/example/cmake/FindLibOSRM.cmake
@@ -1,13 +1,24 @@
 # - Try to find LibOSRM
 # Once done this will define
 #  LibOSRM_FOUND - System has LibOSRM
-#  LibOSRM_INCLUDE_DIRS - The LibOSRM include directories
-#  LibOSRM_LIBRARIES - The libraries needed to use LibOSRM
-#  LibOSRM_DEFINITIONS - Compiler switches required for using LibOSRM
+#  LibOSRM_LIBRARIES - The libraries and ldflags needed to use LibOSRM
+#  LibOSRM_DEPENDENT_LIBRARIES - The libraries and ldflags need to link LibOSRM dependencies
+#  LibOSRM_LIBRARY_DIRS - The libraries paths needed to find LibOSRM
+#  LibOSRM_CXXFLAGS - Compiler switches required for using LibOSRM
 
 find_package(PkgConfig)
-pkg_check_modules(PC_LibOSRM QUIET libosrm)
-set(LibOSRM_DEFINITIONS ${PC_LibOSRM_CFLAGS_OTHER})
+pkg_search_module(PC_LibOSRM QUIET libosrm)
+
+function(JOIN VALUES GLUE OUTPUT)
+  string (REPLACE ";" "${GLUE}" _TMP_STR "${VALUES}")
+  set (${OUTPUT} "${_TMP_STR}" PARENT_SCOPE)
+endfunction()
+
+list(REMOVE_ITEM PC_LibOSRM_CFLAGS " ")
+JOIN("${PC_LibOSRM_CFLAGS}" " " output)
+
+set(LibOSRM_CXXFLAGS ${output})
+set(LibOSRM_LIBRARY_DIRS ${PC_LibOSRM_LIBRARY_DIRS})
 
 find_path(LibOSRM_INCLUDE_DIR osrm/osrm.hpp
   PATH_SUFFIXES osrm include/osrm include
@@ -19,8 +30,6 @@ find_path(LibOSRM_INCLUDE_DIR osrm/osrm.hpp
   /opt/local
   /opt)
 
-set(LibOSRM_INCLUDE_DIRS ${LibOSRM_INCLUDE_DIR} ${LibOSRM_INCLUDE_DIR}/osrm)
-
 find_library(TEST_LibOSRM_STATIC_LIBRARY Names osrm.lib libosrm.a
   PATH_SUFFIXES osrm lib/osrm lib
   HINTS ${PC_LibOSRM_LIBDIR} ${PC_LibOSRM_LIBRARY_DIRS}
@@ -30,7 +39,7 @@ find_library(TEST_LibOSRM_STATIC_LIBRARY Names osrm.lib libosrm.a
   /usr
   /opt/local
   /opt)
-find_library(TEST_LibOSRM_DYNAMIC_LIBRARY Names osrm.dynlib libosrm.so
+find_library(TEST_LibOSRM_DYNAMIC_LIBRARY Names libosrm.dylib libosrm.so
   PATH_SUFFIXES osrm lib/osrm lib
   HINTS ${PC_LibOSRM_LIBDIR} ${PC_LibOSRM_LIBRARY_DIRS}
   ~/Library/Frameworks
@@ -40,26 +49,15 @@ find_library(TEST_LibOSRM_DYNAMIC_LIBRARY Names osrm.dynlib libosrm.so
   /opt/local
   /opt)
 
-if (NOT ("${TEST_LibOSRM_STATIC_LIBRARY}" STREQUAL "TEST_LibOSRM_STATIC_LIBRARY-NOTFOUND"))
-  if ("${PC_LibOSRM_STATIC_LIBRARIES}" STREQUAL "")
-    set(LibOSRM_STATIC_LIBRARIES ${TEST_LibOSRM_STATIC_LIBRARY})
-  else()
-    set(LibOSRM_STATIC_LIBRARIES ${PC_LibOSRM_STATIC_LIBRARIES})
-  endif()
-  set(LibOSRM_LIBRARIES ${LibOSRM_STATIC_LIBRARIES})
-endif()
-
-if (NOT ("${TEST_LibOSRM_DYNAMIC_LIBRARY}" STREQUAL "TEST_LibOSRM_DYNAMIC_LIBRARY-NOTFOUND"))
-  if ("${PC_LibOSRM_LIBRARIES}" STREQUAL "")
-    set(LibOSRM_DYNAMIC_LIBRARIES ${TEST_LibOSRM_DYNAMIC_LIBRARY})
-  else()
-    set(LibOSRM_DYNAMIC_LIBRARIES ${PC_LibOSRM_LIBRARIES})
-  endif()
-  set(LibOSRM_LIBRARIES ${LibOSRM_DYNAMIC_LIBRARIES})
-endif()
+set(LibOSRM_DEPENDENT_LIBRARIES ${PC_LibOSRM_STATIC_LDFLAGS})
+set(LibOSRM_LIBRARIES ${PC_LibOSRM_LDFLAGS})
 
 include(FindPackageHandleStandardArgs)
 # handle the QUIETLY and REQUIRED arguments and set LIBOSRM_FOUND to TRUE
 # if all listed variables are TRUE
 find_package_handle_standard_args(LibOSRM DEFAULT_MSG
-                                LibOSRM_LIBRARIES LibOSRM_INCLUDE_DIR)
+                                LibOSRM_LIBRARY_DIRS
+                                LibOSRM_CXXFLAGS
+                                LibOSRM_LIBRARIES
+                                LibOSRM_DEPENDENT_LIBRARIES
+                                LibOSRM_INCLUDE_DIR)
diff --git a/example/example.cpp b/example/example.cpp
index fe1de36..5061343 100644
--- a/example/example.cpp
+++ b/example/example.cpp
@@ -1,20 +1,20 @@
+#include "osrm/match_parameters.hpp"
+#include "osrm/nearest_parameters.hpp"
 #include "osrm/route_parameters.hpp"
 #include "osrm/table_parameters.hpp"
-#include "osrm/nearest_parameters.hpp"
 #include "osrm/trip_parameters.hpp"
-#include "osrm/match_parameters.hpp"
 
 #include "osrm/coordinate.hpp"
 #include "osrm/engine_config.hpp"
 #include "osrm/json_container.hpp"
 
-#include "osrm/status.hpp"
 #include "osrm/osrm.hpp"
+#include "osrm/status.hpp"
 
+#include <exception>
+#include <iostream>
 #include <string>
 #include <utility>
-#include <iostream>
-#include <exception>
 
 #include <cstdlib>
 
diff --git a/features/bicycle/oneway.feature b/features/bicycle/oneway.feature
index 0cb774c..1d363eb 100644
--- a/features/bicycle/oneway.feature
+++ b/features/bicycle/oneway.feature
@@ -48,22 +48,22 @@ Feature: Bike - Oneway streets
 
     Scenario: Bike - Implied oneways
         Then routability should be
-            | highway       | foot | bicycle | junction   | forw | backw |
-            |               | no   |         |            | x    | x     |
-            |               | no   |         | roundabout | x    |       |
-            | motorway      | no   | yes     |            | x    |       |
-            | motorway_link | no   | yes     |            | x    |       |
-            | motorway      | no   | yes     | roundabout | x    |       |
-            | motorway_link | no   | yes     | roundabout | x    |       |
+            | highway         | foot | bicycle | junction   | forw | backw | #                     |
+            |                 | no   |         |            | x    | x     |                       |
+            |                 | no   |         | roundabout | x    |       |                       |
+            | motorway        | no   | yes     |            | x    |       |                       |
+            | motorway_link   | no   | yes     |            | x    | x     | does not imply oneway |
+            | motorway        | no   | yes     | roundabout | x    |       |                       |
+            | motorway_link   | no   | yes     | roundabout | x    |       |                       |
 
     Scenario: Bike - Overriding implied oneways
         Then routability should be
-            | highway       | foot | junction   | oneway | forw | backw |
-            | primary       | no   | roundabout | no     | x    | x     |
-            | primary       | no   | roundabout | yes    | x    |       |
-            | motorway_link | no   |            | -1     |      |       |
-            | trunk_link    | no   |            | -1     |      |       |
-            | primary       | no   | roundabout | -1     |      | x     |
+            | highway         | foot | junction   | oneway | forw | backw |
+            | primary         | no   | roundabout | no     | x    | x     |
+            | primary         | no   | roundabout | yes    | x    |       |
+            | motorway_link   | no   |            | -1     |      |       |
+            | trunk_link      | no   |            | -1     |      |       |
+            | primary         | no   | roundabout | -1     |      | x     |
 
     Scenario: Bike - Oneway:bicycle should override normal oneways tags
         Then routability should be
@@ -115,7 +115,7 @@ Feature: Bike - Oneway streets
 
     Scenario: Bike - Two consecutive oneways
         Given the node map
-            | a | b | c |
+            | a | b |   | c |
 
         And the ways
             | nodes | oneway |
diff --git a/features/car/names.feature b/features/car/names.feature
index 7a47253..1dfe944 100644
--- a/features/car/names.feature
+++ b/features/car/names.feature
@@ -18,6 +18,23 @@ Feature: Car - Street names in instructions
             | from | to | route                              |
             | a    | c  | My Way,Your Way (A1),Your Way (A1) |
 
+    Scenario: Car - A named street with pronunciation
+        Given the node map
+            | a | b | d |
+            |   | 1 |   |
+            |   | c |   |
+
+        And the ways
+            | nodes | name     |name:pronunciation | ref |
+            | ab    | My Way   |                    |     |
+            | bd    | My Way   | meyeway            | A1  |
+            | cd    | Your Way | yourewaye          |     |
+
+        When I route I should get
+            | from | to | route              | pronunciations      |
+            | a    | d  | My Way,My Way (A1) | ,meyeway             |
+            | 1    | c  | Your Way,Your Way  | yourewaye,yourewaye  |
+
     @todo
     Scenario: Car - Use way type to describe unnamed ways
         Given the node map
diff --git a/features/car/oneway.feature b/features/car/oneway.feature
index b1b08bf..d048afb 100644
--- a/features/car/oneway.feature
+++ b/features/car/oneway.feature
@@ -17,19 +17,21 @@ Feature: Car - Oneway streets
 
     Scenario: Car - Implied oneways
         Then routability should be
-            | highway       | junction   | forw | backw |
-            | motorway      |            | x    |       |
-            | motorway_link |            | x    |       |
-            | primary       |            | x    | x     |
-            | motorway      | roundabout | x    |       |
-            | motorway_link | roundabout | x    |       |
-            | primary       | roundabout | x    |       |
+            | highway         | junction   | forw | backw | #                     |
+            | motorway        |            | x    |       |                       |
+            | motorway_link   |            | x    | x     | does not imply oneway |
+            | primary         |            | x    | x     |                       |
+            | motorway        | roundabout | x    |       |                       |
+            | motorway_link   | roundabout | x    |       |                       |
+            | primary         | roundabout | x    |       |                       |
 
     Scenario: Car - Overrule implied oneway
         Then routability should be
-            | highway       | oneway | forw | backw |
-            | motorway      | no     | x    | x     |
-            | motorway_link | no     | x    | x     |
+            | highway       | oneway | forw | backw | #                    |
+            | motorway      | no     | x    | x     |                      |
+            | motorway_link | no     | x    | x     |                      |
+            | motorway_link | yes    | x    |       |                      |
+            | motorway_link |        | x    | x     | does not imply onway |
 
     Scenario: Car - Around the Block
         Given the node map
@@ -66,7 +68,7 @@ Feature: Car - Oneway streets
 
     Scenario: Car - Two consecutive oneways
         Given the node map
-            | a | b | c |
+            | a | b |   | c |
 
         And the ways
             | nodes | oneway |
diff --git a/features/car/restrictions.feature b/features/car/restrictions.feature
index d311fb0..719ab5f 100644
--- a/features/car/restrictions.feature
+++ b/features/car/restrictions.feature
@@ -5,6 +5,7 @@ Feature: Car - Turn restrictions
 
     Background: Use car routing
         Given the profile "car"
+        Given a grid size of 200 meters
 
     @no_turning
     Scenario: Car - No left turn
diff --git a/features/car/traffic_turn_penalties.feature b/features/car/traffic_turn_penalties.feature
index e0c3727..658f780 100644
--- a/features/car/traffic_turn_penalties.feature
+++ b/features/car/traffic_turn_penalties.feature
@@ -12,19 +12,17 @@ Feature: Traffic - turn penalties
             | nodes | highway |
             | ad    | primary |
             | cd    | primary |
-            | de    | primary |
+            | def   | primary |
             | dhk   | primary |
 
             | bf    | primary |
-            | ef    | primary |
             | fg    | primary |
             | fim   | primary |
 
             | jk    | primary |
-            | kl    | primary |
+            | klm   | primary |
             | ko    | primary |
 
-            | lm    | primary |
             | mn    | primary |
             | mp    | primary |
         And the profile "car"
@@ -32,22 +30,22 @@ Feature: Traffic - turn penalties
 
     Scenario: Weighting not based on turn penalty file
         When I route I should get
-            | from | to | route          | speed   | time      |
-            | a    | h  | ad,dhk,dhk     | 63 km/h | 11.5s +-1 |
+            | from | to | route           | speed   | time      |
+            | a    | h  | ad,dhk,dhk      | 63 km/h | 11.5s +-1 |
                                                                   # straight
-            | i    | g  | fim,fg,fg      | 59 km/h | 12s  +-1  |
+            | i    | g  | fim,fg,fg       | 59 km/h | 12s  +-1  |
                                                                   # right
-            | a    | e  | ad,de,de       | 57 km/h | 12.5s +-1 |
+            | a    | e  | ad,def,def      | 57 km/h | 12.5s +-1 |
                                                                   # left
-            | c    | g  | cd,de,ef,fg,fg | 63 km/h | 23s +-1   |
+            | c    | g  | cd,def,fg,fg    | 63 km/h | 23s +-1   |
                                                                   # double straight
-            | p    | g  | mp,fim,fg,fg   | 61 km/h | 23.5s +-1 |
+            | p    | g  | mp,fim,fg,fg    | 61 km/h | 23.5s +-1 |
                                                                   # straight-right
-            | a    | l  | ad,dhk,kl,kl   | 60 km/h | 24s +-1   |
+            | a    | l  | ad,dhk,klm,klm  | 60 km/h | 24s +-1   |
                                                                   # straight-left
-            | l    | e  | kl,dhk,de,de   | 59 km/h | 24.5s +-1 |
+            | l    | e  | klm,dhk,def,def | 59 km/h | 24.5s +-1 |
                                                                   # double right
-            | g    | n  | fg,fim,mn,mn   | 57 km/h | 25s +-1   |
+            | g    | n  | fg,fim,mn,mn    | 57 km/h | 25s +-1   |
                                                                   # double left
 
     Scenario: Weighting based on turn penalty file
@@ -62,24 +60,24 @@ Feature: Traffic - turn penalties
             """
         And the contract extra arguments "--turn-penalty-file penalties.csv"
         When I route I should get
-            | from | to | route                    | speed   | time      |
-            | a    | h  | ad,dhk,dhk               | 63 km/h | 11.5s +-1 |
+            | from | to | route                 | speed   | time      |
+            | a    | h  | ad,dhk,dhk            | 63 km/h | 11.5s +-1 |
                                                                               # straight
-            | i    | g  | fim,fg,fg                | 55 km/h | 13s +-1   |
+            | i    | g  | fim,fg,fg             | 55 km/h | 13s +-1   |
                                                                               # right - ifg penalty
-            | a    | e  | ad,de,de                 | 64 km/h | 11s +-1   |
+            | a    | e  | ad,def,def            | 64 km/h | 11s +-1   |
                                                                               # left - faster because of negative ade penalty
-            | c    | g  | cd,de,ef,fg,fg           | 63 km/h | 23s +-1   |
+            | c    | g  | cd,def,fg,fg          | 63 km/h | 23s +-1   |
                                                                               # double straight
-            | p    | g  | mp,fim,fg,fg             | 59 km/h | 24.5s +-1 |
+            | p    | g  | mp,fim,fg,fg          | 59 km/h | 24.5s +-1 |
                                                                               # straight-right - ifg penalty
-            | a    | l  | ad,de,ef,fim,lm,lm       | 61 km/h | 35.5s +-1 |
+            | a    | l  | ad,def,fim,klm,klm    | 61 km/h | 35.5s +-1 |
                                                                               # was straight-left - forced around by hkl penalty
-            | l    | e  | lm,fim,ef,ef             | 57 km/h | 25s +-1   |
+            | l    | e  | klm,fim,def,def       | 57 km/h | 25s +-1   |
                                                                               # double right - forced left by lkh penalty
-            | g    | n  | fg,fim,mn,mn             | 30 km/h | 47.5s +-1   |
+            | g    | n  | fg,fim,mn,mn          | 30 km/h | 47.5s +-1 |
                                                                               # double left - imn penalty
-            | j    | c  | jk,kl,lm,fim,ef,de,cd,cd | 60 km/h | 48s +-1   |
+            | j    | c  | jk,klm,fim,def,cd,cd  | 60 km/h | 48s +-1   |
                                                                               # double left - hdc penalty ever so slightly higher than imn; forces all the way around
 
     Scenario: Too-negative penalty clamps, but does not fail
@@ -90,8 +88,8 @@ Feature: Traffic - turn penalties
             1,4,5,-10
             """
         When I route I should get
-            | from | to | route    | time    |
-            | a    | d  | ad,ad    | 10s +-1 |
-            | a    | e  | ad,de,de | 10s +-1 |
-            | b    | f  | bf,bf    | 10s +-1 |
-            | b    | g  | bf,fg,fg | 20s +-1 |
+            | from | to | route      | time    |
+            | a    | d  | ad,ad      | 10s +-1 |
+            | a    | e  | ad,def,def | 10s +-1 |
+            | b    | f  | bf,bf      | 10s +-1 |
+            | b    | g  | bf,fg,fg   | 20s +-1 |
diff --git a/features/guidance/collapse.feature b/features/guidance/collapse.feature
index 9cde9eb..b5669ec 100644
--- a/features/guidance/collapse.feature
+++ b/features/guidance/collapse.feature
@@ -344,4 +344,289 @@ Feature: Collapse
             | waypoints | route                   | turns                                      |
             | a,d       | first,first,first,first | depart,continue left,continue right,arrive |
             | a,e       | first,second,second     | depart,turn left,arrive                    |
-            | a,f       | first,third,third       | depart,new name straight,arrive            |
+            | a,f       | first,third,third       | depart,turn straight,arrive                |
+
+     Scenario: Bridge on unnamed road
+        Given the node map
+            | a | b |   |   |   | c | d |
+
+        And the ways
+            | nodes | highway | name   |
+            | ab    | primary |        |
+            | bc    | primary | Bridge |
+            | cd    | primary |        |
+
+        When I route I should get
+            | waypoints | route | turns         |
+            | a,d       | ,     | depart,arrive |
+
+     Scenario: Crossing Bridge into Segregated Turn
+        Given the node map
+            |   |   |   |   |   | f |
+            | i | h |   |   | g | e |
+            | a | b |   |   | c | d |
+
+        And the ways
+            | nodes | highway | oneway | name        |
+            | ab    | primary | yes    | to_bridge   |
+            | bc    | primary | yes    | bridge      |
+            | cd    | primary | yes    | off_bridge  |
+            | de    | primary | yes    |             |
+            | ef    | primary | no     | target_road |
+            | eg    | primary | yes    | off_bridge  |
+            | gh    | primary | yes    | bridge      |
+            | hi    | primary | yes    | to_bridge   |
+
+        When I route I should get
+            | waypoints | route                             | turns                   |
+            | a,f       | to_bridge,target_road,target_road | depart,turn left,arrive |
+
+    Scenario: Pankenbruecke
+        Given the node map
+            | h |   |   |   |   |   | i |   |   |   |   |   |   |
+            |   |   | b | c | d | e | f |   |   |   |   |   | g |
+            | a |   |   |   |   |   |   |   |   |   |   |   |   |
+
+        And the ways
+            | nodes | highway | name    | oneway |
+            | abh   | primary | inroad  | yes    |
+            | bc    | primary | inroad  | no     |
+            | cd    | primary | bridge  | no     |
+            | defg  | primary | outroad | no     |
+            | fi    | primary | cross   | no     |
+
+       When I route I should get
+            | waypoints | route                  | turns                           |
+            | a,g       | inroad,outroad,outroad | depart,new name straight,arrive |
+            | a,i       | inroad,cross,cross     | depart,turn left,arrive         |
+
+     Scenario: Close Turns - Don't Collapse
+        Given the node map
+            |   | g | d |   |
+            |   |   |   |   |
+            | e | b | c | f |
+            |   |   |   |   |
+            |   | a | h |   |
+
+        And the ways
+            | nodes | highway | name     |
+            | ab    | primary | in       |
+            | ebcf  | primary | cross    |
+            | cd    | primary | out      |
+            | bg    | primary | straight |
+            | ch    | primary | reverse  |
+
+        When I route I should get
+            | waypoints | route                    | turns                               |
+            | a,d       | in,cross,out,out         | depart,turn right,turn left,arrive  |
+            | a,h       | in,cross,reverse,reverse | depart,turn right,turn right,arrive |
+            | g,d       | straight,cross,out,out   | depart,turn left,turn left,arrive   |
+
+     Scenario: No Name During Turns
+        Given the node map
+            | a | b |   |
+            |   | c | d |
+
+        And the ways
+            | nodes | highway  | name |
+            | ab    | tertiary | road |
+            | bc    | tertiary |      |
+            | cd    | tertiary | road |
+
+        When I route I should get
+            | waypoints | route     | turns         |
+            | a,d       | road,road | depart,arrive |
+
+    Scenario: No Name During Turns, Random Oneway
+        Given the node map
+            | a | b |   |
+            |   | c | d |
+
+        And the ways
+            | nodes | highway  | name | oneway |
+            | ab    | tertiary | road | no     |
+            | bc    | tertiary |      | yes    |
+            | cd    | tertiary | road | no     |
+
+        When I route I should get
+            | waypoints | route     | turns         |
+            | a,d       | road,road | depart,arrive |
+
+    Scenario: Pulled Back Turn
+        Given the node map
+            |   |   | d |
+            | a | b | c |
+            |   | e |   |
+
+        And the ways
+            | nodes | highway  | name  |
+            | abc   | tertiary | road  |
+            | cd    | tertiary | left  |
+            | be    | tertiary | right |
+
+        When I route I should get
+            | waypoints | route            | turns                    |
+            | a,d       | road,left,left   | depart,turn left,arrive  |
+            | a,e       | road,right,right | depart,turn right,arrive |
+
+    Scenario: No Name During Turns, keep important turns
+        Given the node map
+            | a | b | e |
+            |   | c | d |
+
+        And the ways
+            | nodes | highway  | name  |
+            | ab    | tertiary | road  |
+            | bc    | tertiary |       |
+            | cd    | tertiary | road  |
+            | be    | tertiary | other |
+
+        When I route I should get
+            | waypoints | route          | turns                        |
+            | a,d       | road,road,road | depart,continue right,arrive |
+
+    Scenario: Segregated Intersection into Slight Turn
+        Given the node map
+            | h |   |   |   |   |   |   |
+            | a |   |   |   |   |   |   |
+            |   |   |   |   |   |   |   |
+            |   |   | g |   |   |   |   |
+            |   |   | b | f |   |   |   |
+            |   |   |   | c |   |   |   |
+            |   |   |   |   |   |   |   |
+            |   |   |   |   |   |   |   |
+            |   |   |   |   |   |   | e |
+            |   |   |   |   |   |   | d |
+            |   |   | j | i |   |   |   |
+
+        And the ways
+            | nodes | highway   | name | oneway |
+            | abcd  | primary   | road | yes    |
+            | efgh  | primary   | road | yes    |
+            | icf   | secondary | in   | yes    |
+            | gbj   | secondary | out  | yes    |
+
+        When I route I should get
+            | waypoints | route        | turns                           |
+            | i,h       | in,road,road | depart,turn slight left,arrive  |
+            | a,d       | road,road    | depart,arrive                   |
+            | a,j       | road,out,out | depart,turn slight right,arrive |
+
+    Scenario: Don't collapse everything to u-turn / too wide
+        Given the node map
+            | a |   | b |   | e |
+            |   |   |   |   |   |
+            | d |   | c |   | f |
+
+        And the ways
+            | nodes | highway   | name   |
+            | abcd  | primary   | road   |
+            | be    | secondary | top    |
+            | cf    | secondary | bottom |
+
+        When I route I should get
+            | waypoints | turns                                          | route               |
+            | a,d       | depart,continue right,end of road right,arrive | road,road,road,road |
+            | d,a       | depart,continue left,end of road left,arrive   | road,road,road,road |
+
+    Scenario: Forking before a turn
+        Given the node map
+            |   |   |   | g |   |
+            |   |   |   |   |   |
+            |   |   |   | c |   |
+            | a |   | b | d | e |
+            |   |   |   |   |   |
+            |   |   |   | f |   |
+
+        And the ways
+            | nodes | name  | oneway | highway   |
+            | ab    | road  | yes    | primary   |
+            | bd    | road  | yes    | primary   |
+            | bc    | road  | yes    | primary   |
+            | de    | road  | yes    | primary   |
+            | fdcg  | cross | no     | secondary |
+
+        And the relations
+            | type        | way:from | way:to | node:via | restriction   |
+            | restriction | bd       | fdcg   | d        | no_left_turn  |
+            | restriction | bc       | fdcg   | c        | no_right_turn |
+
+        When I route I should get
+          | waypoints | route            | turns                   |
+          | a,g       | road,cross,cross | depart,turn left,arrive |
+          | a,e       | road,road        | depart,arrive           |
+
+    Scenario: Forking before a turn (narrow)
+        Given the node map
+            |   |   |   | g |   |
+            |   |   |   |   |   |
+            |   |   |   | c |   |
+            | a | b |   | d | e |
+            |   |   |   |   |   |
+            |   |   |   | f |   |
+
+        And the ways
+            | nodes | name  | oneway | highway   |
+            | ab    | road  | yes    | primary   |
+            | bd    | road  | yes    | primary   |
+            | bc    | road  | yes    | primary   |
+            | de    | road  | yes    | primary   |
+            | fdcg  | cross | no     | secondary |
+
+        And the relations
+            | type        | way:from | way:to | node:via | restriction   |
+            | restriction | bd       | fdcg   | d        | no_left_turn  |
+            | restriction | bc       | fdcg   | c        | no_right_turn |
+
+        When I route I should get
+          | waypoints | route            | turns                   |
+          | a,g       | road,cross,cross | depart,turn left,arrive |
+          | a,e       | road,road        | depart,arrive           |
+
+    Scenario: Forking before a turn (forky)
+        Given the node map
+            |   |   |   | g |   |   |
+            |   |   |   |   |   |   |
+            |   |   |   | c |   |   |
+            | a | b |   |   |   |   |
+            |   |   |   |   | d |   |
+            |   |   |   |   | f | e |
+
+        And the ways
+            | nodes | name  | oneway | highway   |
+            | ab    | road  | yes    | primary   |
+            | bd    | road  | yes    | primary   |
+            | bc    | road  | yes    | primary   |
+            | de    | road  | yes    | primary   |
+            | fdcg  | cross | no     | secondary |
+
+        And the relations
+            | type        | way:from | way:to | node:via | restriction   |
+            | restriction | bd       | fdcg   | d        | no_left_turn  |
+            | restriction | bc       | fdcg   | c        | no_right_turn |
+
+        When I route I should get
+          | waypoints | route                 | turns                                          |
+          | a,g       | road,cross,cross      | depart,turn left,arrive                        |
+          | a,e       | road,road,road        | depart,continue slight right,arrive            |
+          # We should discuss whether the next item should be collapsed to depart,turn right,arrive.
+          | a,f       | road,road,cross,cross | depart,continue slight right,turn right,arrive |
+
+     Scenario: On-Off on Highway
+        Given the node map
+            | f |   |   |   |
+            | a | b | c | d |
+            |   |   |   | e |
+
+        And the ways
+            | nodes | name | highway       | oneway |
+            | abcd  | Hwy  | motorway      | yes    |
+            | fb    | on   | motorway_link | yes    |
+            | ce    | off  | motorway_link | yes    |
+
+        When I route I should get
+            | waypoints | route          | turns                                           |
+            | a,d       | Hwy,Hwy        | depart,arrive                                   |
+            | f,d       | on,Hwy,Hwy     | depart,merge slight right,arrive                |
+            | f,e       | on,Hwy,off,off | depart,merge slight right,off ramp right,arrive |
+            | a,e       | Hwy,off,off    | depart,off ramp right,arrive                    |
diff --git a/features/guidance/continue.feature b/features/guidance/continue.feature
index b3abe48..aedb878 100644
--- a/features/guidance/continue.feature
+++ b/features/guidance/continue.feature
@@ -16,9 +16,42 @@ Feature: Continue Instructions
             | bd     | primary |
 
        When I route I should get
-            | waypoints | route       | turns                           |
-            | a,c       | abc,abc,abc | depart,continue left,arrive     |
-            | a,d       | abc,bd,bd   | depart,new name straight,arrive |
+            | waypoints | route       | turns                       |
+            | a,c       | abc,abc,abc | depart,continue left,arrive |
+            | a,d       | abc,bd,bd   | depart,turn straight,arrive |
+
+    Scenario: Road turning left and straight
+        Given the node map
+            |   |   | c |   |
+            | a |   | b | d |
+
+        And the ways
+            | nodes  | highway | name |
+            | abc    | primary | road |
+            | bd     | primary | road |
+
+       When I route I should get
+            | waypoints | route          | turns                       |
+            | a,c       | road,road,road | depart,continue left,arrive |
+            | a,d       | road,road      | depart,arrive               |
+
+    Scenario: Road turning left and straight
+        Given the node map
+            |   |   | c |   |
+            | a |   | b | d |
+            |   |   | e |   |
+
+        And the ways
+            | nodes  | highway | name |
+            | abc    | primary | road |
+            | bd     | primary | road |
+            | be     | primary | road |
+
+       When I route I should get
+            | waypoints | route          | turns                        |
+            | a,c       | road,road,road | depart,continue left,arrive  |
+            | a,d       | road,road      | depart,arrive                |
+            | a,e       | road,road,road | depart,continue right,arrive |
 
     Scenario: Road turning right
         Given the node map
@@ -31,9 +64,9 @@ Feature: Continue Instructions
             | bd     | primary |
 
        When I route I should get
-            | waypoints | route       | turns                           |
-            | a,c       | abc,abc,abc | depart,continue right,arrive    |
-            | a,d       | abc,bd,bd   | depart,new name straight,arrive |
+            | waypoints | route       | turns                        |
+            | a,c       | abc,abc,abc | depart,continue right,arrive |
+            | a,d       | abc,bd,bd   | depart,turn straight,arrive  |
 
     Scenario: Road turning slight left
         Given the node map
diff --git a/features/guidance/dedicated-turn-roads.feature b/features/guidance/dedicated-turn-roads.feature
new file mode 100644
index 0000000..3513ad3
--- /dev/null
+++ b/features/guidance/dedicated-turn-roads.feature
@@ -0,0 +1,125 @@
+ at routing  @guidance
+Feature: Slipways and Dedicated Turn Lanes
+
+    Background:
+        Given the profile "car"
+        Given a grid size of 5 meters
+
+    Scenario: Turn Instead of Ramp
+        Given the node map
+            |   |   |   |   | e |   |
+            | a | b |   |   | c | d |
+            |   |   |   | h |   |   |
+            |   |   |   |   |   |   |
+            |   |   |   | 1 |   |   |
+            |   |   |   |   |   |   |
+            |   |   |   |   | f |   |
+            |   |   |   |   |   |   |
+            |   |   |   |   | g |   |
+
+        And the ways
+            | nodes | highway    | name   |
+            | abcd  | trunk      | first  |
+            | bhf   | trunk_link |        |
+            | ecfg  | primary    | second |
+
+        And the relations
+            | type        | way:from | way:to | node:via | restriction   |
+            | restriction | abcd     | ecfg   | c        | no_right_turn |
+
+       When I route I should get
+            | waypoints | route               | turns                           |
+            | a,g       | first,second,second | depart,turn right,arrive        |
+            | a,1       | first,,             | depart,turn slight right,arrive |
+
+    Scenario: Turn Instead of Ramp
+        Given the node map
+            |   |   |   |   | e |   |
+            | a | b |   |   | c | d |
+            |   |   |   | h |   |   |
+            |   |   |   |   |   |   |
+            |   |   |   |   |   |   |
+            |   |   |   |   |   |   |
+            |   |   |   |   | f |   |
+            |   |   |   |   |   |   |
+            |   |   |   |   |   |   |
+            |   |   |   |   | g |   |
+
+        And the ways
+            | nodes | highway       | name   |
+            | abcd  | motorway      | first  |
+            | bhf   | motorway_link |        |
+            | efg   | primary       | second |
+
+       When I route I should get
+            | waypoints | route                | turns                                                 |
+            | a,g       | first,,second,second | depart,off ramp slight right,merge slight left,arrive |
+
+    Scenario: Inner city expressway with on road
+        Given the node map
+            | a | b |   |   |   | c |
+            |   |   |   |   | f |   |
+            |   |   |   |   |   |   |
+            |   |   |   |   |   |   |
+            |   |   |   |   |   |   |
+            |   |   |   |   |   | d |
+            |   |   |   |   |   |   |
+            |   |   |   |   |   |   |
+            |   |   |   |   |   |   |
+            |   |   |   |   |   | e |
+
+        And the ways
+            | nodes | highway      | name  |
+            | abc   | primary      | road  |
+            | bfd   | trunk_link   |       |
+            | cde   | trunk        | trunk |
+
+        And the relations
+            | type        | way:from | way:to | node:via | restriction   |
+            | restriction | abc      | cde    | c        | no_right_turn |
+
+       When I route I should get
+            | waypoints | route                | turns                    |
+            | a,e       | road,trunk,trunk     | depart,turn right,arrive |
+
+
+    Scenario: Slipway Round U-Turn
+        Given the node map
+            | a |   | f |
+            |   |   |   |
+            | b |   | e |
+            |   |   |   |
+            |   |   |   |
+            |   | g |   |
+            |   |   |   |
+            | c |   | d |
+
+        And the ways
+            | nodes | highway      | name | oneway |
+            | abc   | primary      | road | yes    |
+            | bge   | primary_link |      | yes    |
+            | def   | primary      | road | yes    |
+
+       When I route I should get
+            | waypoints | route          | turns                        |
+            | a,f       | road,road,road | depart,continue uturn,arrive |
+
+    Scenario: Slipway Steep U-Turn
+        Given the node map
+            | a |   | f |
+            |   |   |   |
+            | b |   | e |
+            |   | g |   |
+            |   |   |   |
+            |   |   |   |
+            | c |   | d |
+
+        And the ways
+            | nodes | highway      | name | oneway |
+            | abc   | primary      | road | yes    |
+            | bge   | primary_link |      | yes    |
+            | def   | primary      | road | yes    |
+
+       When I route I should get
+            | waypoints | route          | turns                        |
+            | a,f       | road,road,road | depart,continue uturn,arrive |
diff --git a/features/guidance/destination-signs.feature b/features/guidance/destination-signs.feature
index e2bbaea..3c5aa4e 100644
--- a/features/guidance/destination-signs.feature
+++ b/features/guidance/destination-signs.feature
@@ -29,13 +29,13 @@ Feature: Destination Signs
           | qr    | QR   |     |                | A1;A2           | yes    |                                      |
 
         When I route I should get
-          | from | to | route                                                     | #                         |
-          | a    | b  | AB (E1),AB (E1)                                           |                           |
-          | c    | d  | CD (Berlin),CD (Berlin)                                   |                           |
-          | e    | f  | EF (A1: Berlin),EF (A1: Berlin)                           |                           |
-          | g    | h  | ,                                                         |                           |
-          | i    | j  | ,                                                         |                           |
-          | k    | l  | KL (E1),KL (E1)                                           |                           |
-          | m    | n  | MN (A1, A2: Berlin, Hamburg),MN (A1, A2: Berlin, Hamburg) |                           |
-          | o    | p  | OP,OP                                                     | guard against mis-tagging |
-          | q    | r  | QR (A1, A2),QR (A1, A2)                                   |                           |
+          | from | to | route                                                     | destinations                                    | #                         |
+          | a    | b  | AB (E1),AB (E1)                                           | ,                                               |                           |
+          | c    | d  | CD (Berlin),CD (Berlin)                                   | Berlin,Berlin                                   |                           |
+          | e    | f  | EF (A1: Berlin),EF (A1: Berlin)                           | A1: Berlin,A1: Berlin                           |                           |
+          | g    | h  | ,                                                         | A1: Berlin,A1: Berlin                           |                           |
+          | i    | j  | ,                                                         | Berlin,Berlin                                   |                           |
+          | k    | l  | KL (E1),KL (E1)                                           | A1: Berlin,A1: Berlin                           |                           |
+          | m    | n  | MN (A1, A2: Berlin, Hamburg),MN (A1, A2: Berlin, Hamburg) | A1, A2: Berlin, Hamburg,A1, A2: Berlin, Hamburg |                           |
+          | o    | p  | OP,OP                                                     | ,                                               | guard against mis-tagging |
+          | q    | r  | QR (A1, A2),QR (A1, A2)                                   | A1, A2,A1, A2                                   |                           |
diff --git a/features/guidance/fork.feature b/features/guidance/fork.feature
index f5c8087..835b772 100644
--- a/features/guidance/fork.feature
+++ b/features/guidance/fork.feature
@@ -281,3 +281,18 @@ Feature: Fork Instructions
             | a,c       | abd,bc,bc | depart,turn slight left,arrive  |
             | a,d       | abd,abd   | depart,arrive                   |
             | a,e       | abd,be,be | depart,turn slight right,arrive |
+
+    Scenario: Don't Fork when leaving Road
+        Given the node map
+            | a |   | b |   | c |
+            |   |   |   |   | d |
+
+        And the ways
+            | nodes  | highway   |
+            | abc    | secondary |
+            | bd     | secondary |
+
+       When I route I should get
+            | waypoints | route     | turns                           |
+            | a,c       | abc,abc   | depart,arrive                   |
+            | a,d       | abc,bd,bd | depart,turn slight right,arrive |
diff --git a/features/guidance/intersections.feature b/features/guidance/intersections.feature
new file mode 100644
index 0000000..b25667a
--- /dev/null
+++ b/features/guidance/intersections.feature
@@ -0,0 +1,153 @@
+ at routing  @guidance @intersections
+Feature: Intersections Data
+
+    Background:
+        Given the profile "car"
+        Given a grid size of 10 meters
+
+    Scenario: Passing Three Way South
+        Given the node map
+            | a |   | b |   | c |
+            |   |   | d |   |   |
+
+        And the ways
+            | nodes  | name    |
+            | ab     | through |
+            | bc     | through |
+            | bd     | corner  |
+
+       When I route I should get
+            | waypoints | route           | turns         | intersections                               |
+            | a,c       | through,through | depart,arrive | true:90,true:90 true:180 false:270;true:270 |
+
+    Scenario: Passing Three Way North
+        Given the node map
+            |   |   | d |   |   |
+            | a |   | b |   | c |
+
+        And the ways
+            | nodes  | name    |
+            | ab     | through |
+            | bc     | through |
+            | bd     | corner  |
+
+       When I route I should get
+            | waypoints | route           | turns         | intersections                             |
+            | a,c       | through,through | depart,arrive | true:90,true:0 true:90 false:270;true:270 |
+
+    Scenario: Passing Oneway Street In
+        Given the node map
+            |   |   | d |   |   |
+            | a |   | b |   | c |
+
+        And the ways
+            | nodes  | name    | oneway |
+            | ab     | through | no     |
+            | bc     | through | no     |
+            | db     | corner  | yes    |
+
+       When I route I should get
+            | waypoints | route           | turns         | intersections                              |
+            | a,c       | through,through | depart,arrive | true:90,false:0 true:90 false:270;true:270 |
+
+    Scenario: Passing Oneway Street Out
+        Given the node map
+            |   |   | d |   |   |
+            | a |   | b |   | c |
+
+        And the ways
+            | nodes  | name    | oneway |
+            | ab     | through | no     |
+            | bc     | through | no     |
+            | bd     | corner  | yes    |
+
+       When I route I should get
+            | waypoints | route           | turns         | intersections                             |
+            | a,c       | through,through | depart,arrive | true:90,true:0 true:90 false:270;true:270 |
+
+    Scenario: Passing Two Intersections
+        Given the node map
+            |   |   | e |   |   |   |   |
+            | a |   | b |   | c |   | d |
+            |   |   |   |   | f |   |   |
+
+        And the ways
+            | nodes  | name    |
+            | ab     | through |
+            | bc     | through |
+            | cd     | through |
+            | be     | corner  |
+            | cf     | corner  |
+
+       When I route I should get
+            | waypoints | route           | turns         | intersections                                                        |
+            | a,d       | through,through | depart,arrive | true:90,true:0 true:90 false:270,true:90 true:180 false:270;true:270 |
+
+    Scenario: Regression test #2424
+        Given the node map
+            |   |   | e |   |   |   |   |   | i |   |   |   |   |
+            | a |   | b |   | c |   | d |   | h |   | k |   | m |
+            |   |   |   |   | f |   |   |   |   |   | l |   |   |
+
+        And the ways
+            | nodes  | name    |
+            | abcd   | Fritz-Elsas-Straße |
+            | hkm    | Fritz-Elsas-Straße |
+            | dhi    | Martin-Luther-Straße |
+            | be     | corner  |
+            | kl     | corner  |
+            | cf     | corner  |
+
+       When I route I should get
+            | waypoints | route | turns         |
+            | a,m       | Fritz-Elsas-Straße,Fritz-Elsas-Straße| depart,arrive |
+
+    Scenario: Passing Two Intersections, Collapsing
+        Given the node map
+            |   |   | e |   |   |   |   |
+            | a |   | b |   | c |   | d |
+            |   |   |   |   | f |   |   |
+
+        And the ways
+            | nodes  | name          |
+            | ab     | through       |
+            | bc     | throughbridge |
+            | cd     | through       |
+            | be     | corner        |
+            | cf     | corner        |
+
+       When I route I should get
+            | waypoints | route                  | turns                          | intersections                                                        |
+            | a,d       | through,through        | depart,arrive                  | true:90,true:0 true:90 false:270,true:90 true:180 false:270;true:270 |
+            | f,a       | corner,through,through | depart,end of road left,arrive | true:0;true:90 false:180 true:270,true:0 false:90 true:270;true:90   |
+
+    Scenario: Roundabouts
+        Given the node map
+            |   |   |   |   | e |   |   |   |   |
+            |   |   |   |   |   |   |   |   |   |
+            |   |   |   |   | a |   |   |   |   |
+            |   |   |   | 1 |   | 4 |   |   |   |
+            |   |   |   |   |   |   |   |   |   |
+            | f |   | b |   |   |   | d |   | h |
+            |   |   |   |   |   |   |   |   |   |
+            |   |   |   | 2 |   | 3 |   |   |   |
+            |   |   |   |   | c |   |   |   |   |
+            |   |   |   |   |   |   |   |   |   |
+            |   |   |   |   | g |   |   |   |   |
+
+        And the ways
+            | nodes | junction   |
+            | abcda | roundabout |
+            | ea    |            |
+            | fb    |            |
+            | gc    |            |
+            | hd    |            |
+
+        When I route I should get
+            | waypoints | route          | turns                              | intersections                                                         |
+            | e,f       | ea,fb,fb       | depart,abcda-exit-1,arrive         | true:180;false:0 false:150 true:210,false:30 true:150 true:270;true:90 |
+            | e,g       | ea,gc,gc       | depart,abcda-exit-2,arrive         | true:180;false:0 false:150 true:210,false:30 true:150 true:270,true:30 true:180 false:330;true:0|
+            | e,h       | ea,hd,hd       | depart,abcda-exit-3,arrive         | true:180;false:0 false:150 true:210,false:30 true:150 true:270,true:30 true:180 false:330,true:90 false:210 true:330;true:270 |
+            | e,2       | ea,abcda,abcda | depart,abcda-exit-undefined,arrive | true:180;false:0 false:150 true:210,false:30 true:150 true:270;true:327 +-1|
+            | 1,g       | abcda,gc,gc    | depart,abcda-exit-2,arrive         | true:214;true:214,false:30 true:150 true:270,true:30 true:180 false:330;true:0|
+            | 1,3       | abcda,abcda    | depart,arrive                      | true:214,false:30 true:150 true:270,true:30 true:180 false:330;true:214|
diff --git a/features/guidance/motorway.feature b/features/guidance/motorway.feature
index 4e79233..f39c077 100644
--- a/features/guidance/motorway.feature
+++ b/features/guidance/motorway.feature
@@ -11,9 +11,9 @@ Feature: Motorway Guidance
             |   |   |   | f | g |
 
         And the ways
-            | nodes  | highway       |
-            | abcde  | motorway      |
-            | bfg    | motorway_link |
+            | nodes | highway       | oneway |
+            | abcde | motorway      |        |
+            | bfg   | motorway_link | yes    |
 
        When I route I should get
             | waypoints | route         | turns                                |
@@ -27,9 +27,9 @@ Feature: Motorway Guidance
             |   |   |   | g | e |
 
         And the ways
-            | nodes  | highway       |
-            | abcde  | motorway      |
-            | bfg    | motorway_link |
+            | nodes | highway       | oneway |
+            | abcde | motorway      |        |
+            | bfg   | motorway_link | yes    |
 
        When I route I should get
             | waypoints | route         | turns                         |
@@ -44,9 +44,9 @@ Feature: Motorway Guidance
 
 
         And the ways
-            | nodes  | highway       |
-            | abcde  | motorway      |
-            | cfg    | motorway_link |
+            | nodes | highway       | oneway |
+            | abcde | motorway      |        |
+            | cfg   | motorway_link | yes    |
 
        When I route I should get
             | waypoints | route         | turns                                |
@@ -60,9 +60,9 @@ Feature: Motorway Guidance
             | a | b | c | d | e |
 
         And the ways
-            | nodes  | highway       |
-            | abcde  | motorway      |
-            | bfg    | motorway_link |
+            | nodes | highway       | oneway |
+            | abcde | motorway      |        |
+            | bfg   | motorway_link | yes    |
 
        When I route I should get
             | waypoints | route         | turns                               |
@@ -76,9 +76,9 @@ Feature: Motorway Guidance
             | a | b | c |   |   |
 
         And the ways
-            | nodes  | highway       |
-            | abcde  | motorway      |
-            | bfg    | motorway_link |
+            | nodes | highway       | oneway |
+            | abcde | motorway      |        |
+            | bfg   | motorway_link | yes    |
 
        When I route I should get
             | waypoints | route         | turns                        |
@@ -92,9 +92,9 @@ Feature: Motorway Guidance
             |   |   |   |   | e |
 
         And the ways
-            | nodes  | highway       |
-            | abcde  | motorway      |
-            | cfg    | motorway_link |
+            | nodes | highway       | oneway |
+            | abcde | motorway      |        |
+            | cfg   | motorway_link | yes    |
 
        When I route I should get
             | waypoints | route         | turns                               |
@@ -107,9 +107,9 @@ Feature: Motorway Guidance
             | f | g |   |   |   |
 
         And the ways
-            | nodes  | highway       |
-            | abcde  | motorway      |
-            | fgd    | motorway_link |
+            | nodes | highway       | oneway |
+            | abcde | motorway      |        |
+            | fgd   | motorway_link | yes    |
 
        When I route I should get
             | waypoints | route           | turns                           |
@@ -122,9 +122,9 @@ Feature: Motorway Guidance
             | a | b | c | d | e |
 
         And the ways
-            | nodes  | highway       |
-            | abcde  | motorway      |
-            | fgd    | motorway_link |
+            | nodes | highway       | oneway |
+            | abcde | motorway      |        |
+            | fgd   | motorway_link | yes    |
 
        When I route I should get
             | waypoints | route           | turns                            |
@@ -154,10 +154,10 @@ Feature: Motorway Guidance
             |   |   |   |   | f | g |
 
         And the ways
-            | nodes  | highway       |
-            | abc    | motorway_link |
-            | cde    | motorway      |
-            | cfg    | motorway      |
+            | nodes | highway       | oneway |
+            | abc   | motorway_link | yes    |
+            | cde   | motorway      |        |
+            | cfg   | motorway      |        |
 
        When I route I should get
             | waypoints | route       | turns                           |
@@ -170,17 +170,17 @@ Feature: Motorway Guidance
             | f | g |   |   |   | h | i |
 
         And the ways
-            | nodes  | highway       |
-            | abcde  | motorway      |
-            | fgc    | motorway_link |
-            | chi    | motorway_link |
+            | nodes | highway       | oneway |
+            | abcde | motorway      |        |
+            | fgc   | motorway_link | yes    |
+            | chi   | motorway_link | yes    |
 
        When I route I should get
-            | waypoints | route           | turns                                |
-            | a,e       | abcde,abcde     | depart,arrive                        |
-            | f,e       | fgc,abcde,abcde | depart,merge slight left,arrive      |
+            | waypoints | route           | turns                              |
+            | a,e       | abcde,abcde     | depart,arrive                      |
+            | f,e       | fgc,abcde,abcde | depart,merge slight left,arrive    |
             | a,i       | abcde,chi,chi   | depart,off ramp slight right,arrive |
-            | f,i       | fgc,chi,chi     | depart,off ramp right,arrive        |
+            | f,i       | fgc,chi,chi     | depart,off ramp right,arrive       |
 
     Scenario: On And Off Ramp Left
        Given the node map
@@ -188,17 +188,17 @@ Feature: Motorway Guidance
             | a | b |   | c |   | d | e |
 
         And the ways
-            | nodes  | highway       |
-            | abcde  | motorway      |
-            | fgc    | motorway_link |
-            | chi    | motorway_link |
+            | nodes | highway       | oneway |
+            | abcde | motorway      |        |
+            | fgc   | motorway_link | yes    |
+            | chi   | motorway_link | yes    |
 
        When I route I should get
-            | waypoints | route           | turns                               |
-            | a,e       | abcde,abcde     | depart,arrive                       |
-            | f,e       | fgc,abcde,abcde | depart,merge slight right,arrive    |
+            | waypoints | route           | turns                             |
+            | a,e       | abcde,abcde     | depart,arrive                     |
+            | f,e       | fgc,abcde,abcde | depart,merge slight right,arrive  |
             | a,i       | abcde,chi,chi   | depart,off ramp slight left,arrive |
-            | f,i       | fgc,chi,chi     | depart,off ramp left,arrive        |
+            | f,i       | fgc,chi,chi     | depart,off ramp left,arrive       |
 
     Scenario: Merging Motorways
         Given the node map
diff --git a/features/guidance/new-name.feature b/features/guidance/new-name.feature
index 984ae7b..8c823dd 100644
--- a/features/guidance/new-name.feature
+++ b/features/guidance/new-name.feature
@@ -3,7 +3,7 @@ Feature: New-Name Instructions
 
     Background:
         Given the profile "car"
-        Given a grid size of 10 meters
+        Given a grid size of 100 meters
 
     Scenario: Undisturbed name Change
         Given the node map
@@ -136,7 +136,7 @@ Feature: New-Name Instructions
 
     Scenario: Empty road names - Announce Change From, suppress Change To
         Given the node map
-            | a |  | b |  | c |  | d |
+            | a |  | b | 1 | c |  | d |
 
         And the ways
             | nodes | name |
@@ -147,7 +147,7 @@ Feature: New-Name Instructions
         When I route I should get
             | waypoints | route    | turns                           |
             | a,d       | ab,cd,cd | depart,new name straight,arrive |
-            | a,c       | ab,      | depart,arrive                   |
+            | a,1       | ab,      | depart,arrive                   |
 
     Scenario: Empty road names - Loose name shortly
         Given the node map
diff --git a/features/guidance/roundabout-turn.feature b/features/guidance/roundabout-turn.feature
index 5a31045..66609f8 100644
--- a/features/guidance/roundabout-turn.feature
+++ b/features/guidance/roundabout-turn.feature
@@ -367,3 +367,25 @@ Feature: Basic Roundabout
            | h,d       | gh,cd,cd | depart,roundabout-exit-2,arrive |
            | h,f       | gh,ef,ef | depart,roundabout-exit-1,arrive |
 
+    Scenario: Enter and Exit -- Bearing
+        Given the node map
+           |   |   | a |   |   |
+           |   |   | b |   |   |
+           | h | g |   | c | d |
+           |   |   | e |   |   |
+           |   |   | f |   |   |
+
+       And the ways
+            | nodes | junction   |
+            | ab    |            |
+            | cd    |            |
+            | ef    |            |
+            | gh    |            |
+            | bgecb | roundabout |
+
+       When I route I should get
+           | waypoints | route    | turns                                         | bearing                |
+           | a,d       | ab,cd,cd | depart,roundabout turn left exit-3,arrive     | 0->180,180->224,90->0  |
+           | a,f       | ab,ef,ef | depart,roundabout turn straight exit-2,arrive | 0->180,180->224,180->0 |
+           | a,h       | ab,gh,gh | depart,roundabout turn right exit-1,arrive    | 0->180,180->224,270->0 |
+
diff --git a/features/guidance/roundabout.feature b/features/guidance/roundabout.feature
index c9eecc3..37b9b08 100644
--- a/features/guidance/roundabout.feature
+++ b/features/guidance/roundabout.feature
@@ -337,3 +337,27 @@ Feature: Basic Roundabout
             | a,e       | ac,de,de | depart,roundabout-exit-1,arrive |
             | a,f       | ac,bf,bf | depart,roundabout-exit-2,arrive |
 
+    Scenario: Enter and Exit - Bearings
+        Given the node map
+            |   |   |   | a |   |   |   |
+            |   |   |   |   |   |   |   |
+            |   |   | i | b | l |   |   |
+            | h |   | g |   | c |   | d |
+            |   |   | j | e | k |   |   |
+            |   |   |   |   |   |   |   |
+            |   |   |   | f |   |   |   |
+
+       And the ways
+            | nodes      | junction   |
+            | ab         |            |
+            | cd         |            |
+            | ef         |            |
+            | gh         |            |
+            | bigjekclb  | roundabout |
+
+       When I route I should get
+           | waypoints | route    | turns                           | bearing                |
+           | a,d       | ab,cd,cd | depart,roundabout-exit-3,arrive | 0->180,180->270,90->0  |
+           | a,f       | ab,ef,ef | depart,roundabout-exit-2,arrive | 0->180,180->270,180->0 |
+           | a,h       | ab,gh,gh | depart,roundabout-exit-1,arrive | 0->180,180->270,270->0 |
+
diff --git a/features/guidance/turn.feature b/features/guidance/turn.feature
index 47cd633..d82b849 100644
--- a/features/guidance/turn.feature
+++ b/features/guidance/turn.feature
@@ -806,3 +806,30 @@ Feature: Simple Turns
             | a,e       | abc,be,be | depart,turn right,arrive        |
             | a,f       | abc,bf,bf | depart,turn slight right,arrive |
 
+    Scenario: Turn Lane on Splitting up Road
+        Given the node map
+            |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |
+            | g |   |   |   | f |   |   |   |   |   |   |   |   |   |   |
+            |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |
+            |   |   |   |   |   | h |   |   | e |   |   | c |   |   | d |
+            | a |   |   | b |   |   |   |   |   |   |   |   |   |   |   |
+            |   |   |   | i |   |   |   |   |   |   |   |   |   |   |   |
+
+        And the ways
+            | nodes | highway        | oneway | name  |
+            | ab    | secondary      | yes    | road  |
+            | be    | secondary      | yes    | road  |
+            | ecd   | secondary      | no     | road  |
+            | efg   | secondary      | yes    | road  |
+            | ehb   | secondary_link | yes    | road  |
+            | bi    | tertiary       | no     | cross |
+
+        And the relations
+            | type        | way:from | way:to | node:via | restriction  |
+            | restriction | ehb      | be     | b        | no_left_turn |
+
+        When I route I should get
+            | waypoints | route            | turns                   |
+            | a,d       | road,road        | depart,arrive           |
+            | d,i       | road,cross,cross | depart,turn left,arrive |
+            | d,g       | road,road        | depart,arrive           |
diff --git a/features/options/contract/datasources.feature b/features/options/contract/datasources.feature
new file mode 100644
index 0000000..bf8eed5
--- /dev/null
+++ b/features/options/contract/datasources.feature
@@ -0,0 +1,30 @@
+ at prepare @options @files
+Feature: osrm-contract command line options: datasources
+# expansions:
+# {extracted_base} => path to current extracted input file
+# {profile} => path to current profile script
+
+    Background:
+        Given the profile "testbot"
+        Given the extract extra arguments "--generate-edge-lookup"
+        And the node map
+            | a | b |
+        And the ways
+            | nodes |
+            | ab    |
+        And the speed file
+        """
+        1,2,27
+        2,1,27
+        2,3,27
+        3,2,27
+        1,4,27
+        4,1,27
+        """
+        And the data has been extracted
+
+    Scenario: osrm-contract - Passing base file
+        When I run "osrm-contract --segment-speed-file speeds.csv {extracted_base}.osrm"
+        Then stderr should be empty
+        And datasource names should contain "lua profile,speeds"
+        And it should exit with code 0
diff --git a/features/step_definitions/data.js b/features/step_definitions/data.js
index 6026c0f..bb58b85 100644
--- a/features/step_definitions/data.js
+++ b/features/step_definitions/data.js
@@ -228,14 +228,17 @@ module.exports = function () {
     });
 
     this.Given(/^the raster source$/, (data, callback) => {
+        this.updateFingerprintExtract(data);
         fs.writeFile(path.resolve(this.TEST_FOLDER, 'rastersource.asc'), data, callback);
     });
 
     this.Given(/^the speed file$/, (data, callback) => {
+        this.updateFingerprintContract(data);
         fs.writeFile(path.resolve(this.TEST_FOLDER, 'speeds.csv'), data, callback);
     });
 
     this.Given(/^the turn penalty file$/, (data, callback) => {
+        this.updateFingerprintContract(data);
         fs.writeFile(path.resolve(this.TEST_FOLDER, 'penalties.csv'), data, callback);
     });
 
@@ -249,9 +252,11 @@ module.exports = function () {
     });
 
     this.Given(/^the data has been extracted$/, (callback) => {
-        this.writeAndExtract((err) => {
-            if (err) this.processError = err;
-            callback();
+        this.osmData.populate(() => {
+            this.writeAndExtract((err) => {
+                if (err) this.processError = err;
+                callback();
+            });
         });
     });
 
diff --git a/features/step_definitions/matching.js b/features/step_definitions/matching.js
index 1b770d7..4ca378f 100644
--- a/features/step_definitions/matching.js
+++ b/features/step_definitions/matching.js
@@ -33,7 +33,10 @@ module.exports = function () {
                     var subMatchings = [],
                         turns = '',
                         route = '',
-                        duration = '';
+                        duration = '',
+                        annotation = '',
+                        OSMIDs = '';
+
 
                     if (res.statusCode === 200) {
                         if (headers.has('matchings')) {
@@ -54,6 +57,16 @@ module.exports = function () {
                             if (json.matchings.length != 1) throw new Error('*** Checking duration only supported for matchings with one subtrace');
                             duration = json.matchings[0].duration;
                         }
+
+                        if (headers.has('annotation')) {
+                            if (json.matchings.length != 1) throw new Error('*** Checking annotation only supported for matchings with one subtrace');
+                            annotation = this.annotationList(json.matchings[0]);
+                        }
+
+                        if (headers.has('OSM IDs')) {
+                            if (json.matchings.length != 1) throw new Error('*** CHecking annotation only supported for matchings with one subtrace');
+                            OSMIDs = this.OSMIDList(json.matchings[0]);
+                        }
                     }
 
                     if (headers.has('turns')) {
@@ -68,6 +81,14 @@ module.exports = function () {
                         got.duration = duration.toString();
                     }
 
+                    if (headers.has('annotation')) {
+                        got.annotation = annotation.toString();
+                    }
+
+                    if (headers.has('OSM IDs')) {
+                        got['OSM IDs'] = OSMIDs;
+                    }
+
                     var ok = true;
                     var encodedResult = '',
                         extendedTarget = '';
diff --git a/features/step_definitions/options.js b/features/step_definitions/options.js
index 166c3b3..2f67f52 100644
--- a/features/step_definitions/options.js
+++ b/features/step_definitions/options.js
@@ -1,4 +1,5 @@
 var assert = require('assert');
+var fs = require('fs');
 
 module.exports = function () {
     this.When(/^I run "osrm\-routed\s?(.*?)"$/, { timeout: this.TIMEOUT }, (options, callback) => {
@@ -59,6 +60,11 @@ module.exports = function () {
         assert.equal(this.stdout.split('\n').length - 1, parseInt(lines));
     });
 
+    this.Then(/^datasource names should contain "(.+)"$/, (expectedData) => {
+        var actualData = fs.readFileSync(this.osmData.extractedFile + '.osrm.datasource_names', {encoding:'UTF-8'}).trim().split('\n').join(',');
+        assert.equal(actualData, expectedData);
+    });
+
     this.Given(/^the query options$/, (table, callback) => {
         table.raw().forEach(tuple => {
             this.queryParams[tuple[0]] = tuple[1];
diff --git a/features/step_definitions/routability.js b/features/step_definitions/routability.js
index 245eb99..c4190c5 100644
--- a/features/step_definitions/routability.js
+++ b/features/step_definitions/routability.js
@@ -12,7 +12,8 @@ module.exports = function () {
                 throw new Error('*** routability table must contain either "forw", "backw" or "bothw" column');
             }
 
-            this.reprocessAndLoadData(() => {
+            this.reprocessAndLoadData((e) => {
+                if (e) callback(e);
                 var testRow = (row, i, cb) => {
                     var outputRow = row;
 
diff --git a/features/step_definitions/trip.js b/features/step_definitions/trip.js
index 9662ac6..35eb9ac 100644
--- a/features/step_definitions/trip.js
+++ b/features/step_definitions/trip.js
@@ -46,8 +46,8 @@ module.exports = function () {
                         if (headers.has('trips')) {
                             subTrips = json.trips.filter(t => !!t).map(t => t.legs).map(tl => Array.prototype.concat.apply([], tl.map((sl, i) => {
                                 var toAdd = [];
-                                if (i === 0) toAdd.push(sl.steps[0].maneuver.location);
-                                toAdd.push(sl.steps[sl.steps.length-1].maneuver.location);
+                                if (i === 0) toAdd.push(sl.steps[0].intersections[0].location);
+                                toAdd.push(sl.steps[sl.steps.length-1].intersections[0].location);
                                 return toAdd;
                             })));
                         }
diff --git a/features/support/config.js b/features/support/config.js
index 1d62030..769755f 100644
--- a/features/support/config.js
+++ b/features/support/config.js
@@ -50,22 +50,27 @@ module.exports = function () {
         };
 
         var hashExtract = (cb) => {
-            this.hashOfFiles(util.format('%s/osrm-extract%s', this.BIN_PATH, this.EXE), (hash) => {
+            var files = [ util.format('%s/osrm-extract%s', this.BIN_PATH, this.EXE),
+                          util.format('%s/libosrm_extract%s', this.BIN_PATH, this.LIB) ];
+            this.hashOfFiles(files, (hash) => {
                 this.binExtractHash = hash;
                 cb();
             });
         };
 
         var hashContract = (cb) => {
-            this.hashOfFiles(util.format('%s/osrm-contract%s', this.BIN_PATH, this.EXE), (hash) => {
+            var files = [ util.format('%s/osrm-contract%s', this.BIN_PATH, this.EXE),
+                          util.format('%s/libosrm_contract%s', this.BIN_PATH, this.LIB) ];
+            this.hashOfFiles(files, (hash) => {
                 this.binContractHash = hash;
-                this.fingerprintContract = this.hashString(this.binContractHash);
                 cb();
             });
         };
 
         var hashRouted = (cb) => {
-            this.hashOfFiles(util.format('%s/osrm-routed%s', this.BIN_PATH, this.EXE), (hash) => {
+            var files = [ util.format('%s/osrm-routed%s', this.BIN_PATH, this.EXE),
+                          util.format('%s/libosrm%s', this.BIN_PATH, this.LIB) ];
+            this.hashOfFiles(files, (hash) => {
                 this.binRoutedHash = hash;
                 this.fingerprintRoute = this.hashString(this.binRoutedHash);
                 cb();
@@ -79,16 +84,18 @@ module.exports = function () {
             .defer(hashContract)
             .defer(hashRouted)
             .awaitAll(() => {
-                this.fingerprintExtract = this.hashString([this.profileHash, this.luaLibHash, this.binExtractHash].join('-'));
                 this.AfterConfiguration(() => {
                     callback();
                 });
             });
     };
 
-    this.setProfileBasedHashes = () => {
-        this.fingerprintExtract = this.hashString([this.profileHash, this.luaLibHash, this.binExtractHash].join('-'));
-        this.fingerprintContract = this.hashString(this.binContractHash);
+    this.updateFingerprintExtract = (str) => {
+        this.fingerprintExtract = this.hashString([this.fingerprintExtract, str].join('-'));
+    };
+
+    this.updateFingerprintContract = (str) => {
+        this.fingerprintContract = this.hashString([this.fingerprintContract, str].join('-'));
     };
 
     this.setProfile = (profile, cb) => {
@@ -97,22 +104,24 @@ module.exports = function () {
             this.profile = profile;
             this.hashProfile((hash) => {
                 this.profileHash = hash;
-                this.setProfileBasedHashes();
+                this.updateFingerprintExtract(this.profileHash);
                 cb();
             });
-        } else cb();
+        } else {
+            this.updateFingerprintExtract(this.profileHash);
+            cb();
+        }
     };
 
     this.setExtractArgs = (args, callback) => {
         this.extractArgs = args;
-        this.forceExtract = true;
-        this.forceContract = true;
+        this.updateFingerprintExtract(args);
         callback();
     };
 
     this.setContractArgs = (args, callback) => {
         this.contractArgs = args;
-        this.forceContract = true;
+        this.updateFingerprintContract(args);
         callback();
     };
 };
diff --git a/features/support/data.js b/features/support/data.js
index d37e555..375229d 100644
--- a/features/support/data.js
+++ b/features/support/data.js
@@ -194,8 +194,8 @@ module.exports = function () {
     this.extractData = (callback) => {
         this.logPreprocessInfo();
         this.log(util.format('== Extracting %s.osm...', this.osmData.osmFile), 'preprocess');
-        var cmd = util.format('%s%s/osrm-extract %s.osm %s --profile %s/%s.lua >>%s 2>&1',
-            this.LOAD_LIBRARIES, this.BIN_PATH, this.osmData.osmFile, this.extractArgs || '', this.PROFILES_PATH, this.profile, this.PREPROCESS_LOG_FILE);
+        var cmd = util.format('%s/osrm-extract %s.osm %s --profile %s/%s.lua >>%s 2>&1',
+            this.BIN_PATH, this.osmData.osmFile, this.extractArgs || '', this.PROFILES_PATH, this.profile, this.PREPROCESS_LOG_FILE);
         this.log(cmd);
         process.chdir(this.TEST_FOLDER);
         exec(cmd, (err) => {
@@ -222,11 +222,12 @@ module.exports = function () {
                 });
             };
 
-            ['osrm','osrm.names','osrm.restrictions','osrm.ebg','osrm.enw','osrm.edges','osrm.fileIndex','osrm.geometry','osrm.nodes','osrm.ramIndex','osrm.properties'].forEach(file => {
-                q.defer(rename, file);
-            });
+            ['osrm', 'osrm.ebg', 'osrm.edges', 'osrm.enw', 'osrm.fileIndex', 'osrm.geometry', 'osrm.icd',
+             'osrm.names', 'osrm.nodes', 'osrm.properties', 'osrm.ramIndex', 'osrm.restrictions'].forEach(file => {
+                 q.defer(rename, file);
+             });
 
-            ['osrm.edge_segment_lookup','osrm.edge_penalties'].forEach(file => {
+            ['osrm.edge_penalties', 'osrm.edge_segment_lookup'].forEach(file => {
                 q.defer(renameIfExists, file);
             });
 
@@ -241,8 +242,8 @@ module.exports = function () {
     this.contractData = (callback) => {
         this.logPreprocessInfo();
         this.log(util.format('== Contracting %s.osm...', this.osmData.extractedFile), 'preprocess');
-        var cmd = util.format('%s%s/osrm-contract %s %s.osrm >>%s 2>&1',
-            this.LOAD_LIBRARIES, this.BIN_PATH, this.contractArgs || '', this.osmData.extractedFile, this.PREPROCESS_LOG_FILE);
+        var cmd = util.format('%s/osrm-contract %s %s.osrm >>%s 2>&1',
+            this.BIN_PATH, this.contractArgs || '', this.osmData.extractedFile, this.PREPROCESS_LOG_FILE);
         this.log(cmd);
         process.chdir(this.TEST_FOLDER);
         exec(cmd, (err) => {
@@ -260,6 +261,13 @@ module.exports = function () {
                 });
             };
 
+            var renameIfExists = (file, cb) => {
+                fs.stat([this.osmData.extractedFile, file].join('.'), (doesNotExistErr, exists) => {
+                    if (exists) rename(file, cb);
+                    else cb();
+                });
+            };
+
             var copy = (file, cb) => {
                 this.log(util.format('Copying %s.%s to %s.%s', this.osmData.extractedFile, file, this.osmData.contractedFile, file), 'preprocess');
                 fs.createReadStream([this.osmData.extractedFile, file].join('.'))
@@ -273,11 +281,17 @@ module.exports = function () {
 
             var q = d3.queue();
 
-            ['osrm.hsgr','osrm.fileIndex','osrm.geometry','osrm.nodes','osrm.ramIndex','osrm.core','osrm.edges','osrm.datasource_indexes','osrm.datasource_names','osrm.level'].forEach((file) => {
-                q.defer(rename, file);
+            ['osrm', 'osrm.core', 'osrm.datasource_indexes', 'osrm.datasource_names', 'osrm.ebg','osrm.edges',
+             'osrm.enw', 'osrm.fileIndex', 'osrm.geometry', 'osrm.hsgr', 'osrm.icd','osrm.level', 'osrm.names',
+             'osrm.nodes', 'osrm.properties', 'osrm.ramIndex', 'osrm.restrictions'].forEach((file) => {
+                 q.defer(rename, file);
+             });
+
+            ['osrm.edge_penalties', 'osrm.edge_segment_lookup'].forEach(file => {
+                q.defer(renameIfExists, file);
             });
 
-            ['osrm.names','osrm.restrictions','osrm.properties','osrm'].forEach((file) => {
+            [].forEach((file) => {
                 q.defer(copy, file);
             });
 
@@ -292,32 +306,33 @@ module.exports = function () {
     var noop = (cb) => cb();
 
     this.reprocess = (callback) => {
-        this.writeAndExtract((e) => {
-            if (e) return callback(e);
+        this.osmData.populate(() => {
             this.isContracted((isContracted) => {
-                var contractFn = (isContracted && !this.forceContract) ? noop : this.contractData;
-                if (isContracted) this.log('Already contracted ' + this.osmData.contractedFile, 'preprocess');
-                contractFn((e) => {
-                    this.forceContract = false;
-                    if (e) return callback(e);
-                    this.logPreprocessDone();
+                if (!isContracted) {
+                    this.writeAndExtract((e) => {
+                        if (e) return callback(e);
+                        this.contractData((e) => {
+                            if (e) return callback(e);
+                            this.logPreprocessDone();
+                            callback();
+                        });
+                    });
+                } else {
+                    this.log('Already contracted ' + this.osmData.contractedFile, 'preprocess');
                     callback();
-                });
+                }
             });
         });
     };
 
     this.writeAndExtract = (callback) => {
-        this.osmData.populate(() => {
-            this.writeInputData((e) => {
-                if (e) return callback(e);
-                this.isExtracted((isExtracted) => {
-                    var extractFn = (isExtracted && !this.forceExtract) ? noop : this.extractData;
-                    if (isExtracted) this.log('Already extracted ' + this.osmData.extractedFile, 'preprocess');
-                    extractFn((e) => {
-                        this.forceExtract = false;
-                        callback(e);
-                    });
+        this.writeInputData((e) => {
+            if (e) return callback(e);
+            this.isExtracted((isExtracted) => {
+                var extractFn = isExtracted ? noop : this.extractData;
+                if (isExtracted) this.log('Already extracted ' + this.osmData.extractedFile, 'preprocess');
+                extractFn((e) => {
+                    callback(e);
                 });
             });
         });
diff --git a/features/support/data_classes.js b/features/support/data_classes.js
index 8eeaab3..391bb1c 100644
--- a/features/support/data_classes.js
+++ b/features/support/data_classes.js
@@ -52,24 +52,91 @@ module.exports = {
         match (got, want) {
             var matchPercent = want.match(/(.*)\s+~(.+)%$/),
                 matchAbs = want.match(/(.*)\s+\+\-(.+)$/),
-                matchRe = want.match(/^\/(.*)\/$/);
+                matchRe = want.match(/^\/(.*)\/$/),
+                // we use this for matching before/after bearing
+                matchBearingListAbs = want.match(/^((\d+)->(\d+))(,(\d+)->(\d+))*\s+\+\-(.+)$/),
+                matchIntersectionListAbs = want.match(/^(((((true|false):\d+)\s{0,1})+,{0,1})+;{0,1})+\s+\+\-(.+)$/);
+
+            function inRange(margin, got, want) {
+                var fromR = parseFloat(want) - margin,
+                    toR = parseFloat(want) + margin;
+                return parseFloat(got) >= fromR && parseFloat(got) <= toR;
+            }
+            function parseIntersectionString(str) {
+                return str.split(';')
+                          .map((turn_intersections) => turn_intersections
+                                                       .split(',')
+                                                       .map((intersection) => intersection
+                                                                               .split(' ')
+                                                                               .map((entry_bearing_pair) => entry_bearing_pair
+                                                                                                            .split(':'))));
+            }
 
             if (got === want) {
                 return true;
+            } else if (matchBearingListAbs) {
+                let want_and_margin = want.split('+-'),
+                    margin = parseFloat(want_and_margin[1].trim()),
+                    want_pairs = want_and_margin[0].trim().split(',').map((pair) => pair.split('->')),
+                    got_pairs = got.split(',').map((pair) => pair.split('->'));
+                if (want_pairs.length != got_pairs.length)
+                {
+                    return false;
+                }
+                for (var i = 0; i < want_pairs.length; ++i)
+                {
+                    if (!inRange(margin, got_pairs[i][0], want_pairs[i][0]) ||
+                        !inRange(margin, got_pairs[i][1], want_pairs[i][1]))
+                    {
+                        return false;
+                    }
+                }
+                return true;
+            } else if (matchIntersectionListAbs) {
+                let margin = parseFloat(want.split('+-')[1]),
+                    want_intersections = parseIntersectionString(want.split('+-')[0].trim()),
+                    got_intersections = parseIntersectionString(got);
+                if (want_intersections.length != got_intersections.length)
+                {
+                    return false;
+                }
+                for (let step_idx = 0; step_idx < want_intersections.length; ++step_idx)
+                {
+                    if (want_intersections[step_idx].length != got_intersections[step_idx].length)
+                    {
+                        return false;
+                    }
+                    for (let intersection_idx = 0; intersection_idx < want_intersections[step_idx].length; ++intersection_idx)
+                    {
+                        if (want_intersections[step_idx][intersection_idx].length != got_intersections[step_idx][intersection_idx].length)
+                        {
+                            return false;
+                        }
+                        for (let pair_idx = 0; pair_idx < want_intersections[step_idx][intersection_idx].length; ++pair_idx)
+                        {
+                            let want_pair = want_intersections[step_idx][intersection_idx][pair_idx],
+                                got_pair = got_intersections[step_idx][intersection_idx][pair_idx];
+                            if (got_pair[0] != want_pair[0] ||
+                                !inRange(margin, got_pair[1], want_pair[1]))
+                            {
+                                return false;
+                            }
+                        }
+                    }
+                }
+                return true;
             } else if (matchPercent) {         // percentage range: 100 ~ 5%
                 var target = parseFloat(matchPercent[1]),
                     percentage = parseFloat(matchPercent[2]);
                 if (target === 0) {
                     return true;
                 } else {
-                    var ratio = Math.abs(1 - parseFloat(got) / target);
+                    let ratio = Math.abs(1 - parseFloat(got) / target);
                     return 100 * ratio < percentage;
                 }
             } else if (matchAbs) {             // absolute range: 100 +-5
-                var margin = parseFloat(matchAbs[2]),
-                    fromR = parseFloat(matchAbs[1]) - margin,
-                    toR = parseFloat(matchAbs[1]) + margin;
-                return parseFloat(got) >= fromR && parseFloat(got) <= toR;
+                let margin = parseFloat(matchAbs[2]);
+                return inRange(margin, got, matchAbs[1]);
             } else if (matchRe) {               // regex: /a,b,.*/
                 return got.match(matchRe[1]);
             } else {
diff --git a/features/support/env.js b/features/support/env.js
index 621143b..32ff82d 100644
--- a/features/support/env.js
+++ b/features/support/env.js
@@ -7,7 +7,7 @@ var d3 = require('d3-queue');
 module.exports = function () {
     this.initializeEnv = (callback) => {
         this.OSRM_PORT = process.env.OSRM_PORT && parseInt(process.env.OSRM_PORT) || 5000;
-        this.TIMEOUT = process.env.CUCUMBER_TIMEOUT && parseInt(process.env.CUCUMBER_TIMEOUT) || 3000;
+        this.TIMEOUT = process.env.CUCUMBER_TIMEOUT && parseInt(process.env.CUCUMBER_TIMEOUT) || 5000;
         this.setDefaultTimeout(this.TIMEOUT);
         this.ROOT_FOLDER = process.cwd();
         this.OSM_USER = 'osrm';
@@ -28,19 +28,16 @@ module.exports = function () {
         this.OSRM_ROUTED_LOG_FILE = path.resolve(this.TEST_FOLDER, 'osrm-routed.log');
         this.ERROR_LOG_FILE = path.resolve(this.TEST_FOLDER, 'error.log');
 
-        // OS X shim to ensure shared libraries from custom locations can be loaded
-        // This is needed in OS X >= 10.11 because DYLD_LIBRARY_PATH is blocked
-        // https://forums.developer.apple.com/thread/9233
-        this.LOAD_LIBRARIES = process.env.OSRM_SHARED_LIBRARY_PATH ? util.format('DYLD_LIBRARY_PATH=%s ', process.env.OSRM_SHARED_LIBRARY_PATH) : '';
-
         // TODO make sure this works on win
         if (process.platform.match(/indows.*/)) {
             this.TERMSIGNAL = 9;
             this.EXE = '.exe';
+            this.LIB = '.dll';
             this.QQ = '"';
         } else {
             this.TERMSIGNAL = 'SIGTERM';
             this.EXE = '';
+            this.LIB = '.so';
             this.QQ = '';
         }
 
@@ -65,7 +62,7 @@ module.exports = function () {
             var binPath = path.resolve(util.format('%s/%s%s', this.BIN_PATH, bin, this.EXE));
             fs.exists(binPath, (exists) => {
                 if (!exists) throw new Error(util.format('%s is missing. Build failed?', binPath));
-                var helpPath = util.format('%s%s --help > /dev/null 2>&1', this.LOAD_LIBRARIES, binPath);
+                var helpPath = util.format('%s --help > /dev/null 2>&1', binPath);
                 exec(helpPath, (err) => {
                     if (err) {
                         this.log(util.format('*** Exited with code %d', err.code), 'preprocess');
diff --git a/features/support/hash.js b/features/support/hash.js
index 6ab44ad..399dd51 100644
--- a/features/support/hash.js
+++ b/features/support/hash.js
@@ -6,14 +6,19 @@ var d3 = require('d3-queue');
 module.exports = function () {
     this.hashOfFiles = (paths, cb) => {
         paths = Array.isArray(paths) ? paths : [paths];
-        var shasum = crypto.createHash('sha1');
+        var shasum = crypto.createHash('sha1'), hashedFiles = false;
 
         var q = d3.queue(1);
 
         var addFile = (path, cb) => {
             fs.readFile(path, (err, data) => {
-                shasum.update(data);
-                cb(err);
+                if (err && err.code === 'ENOENT') cb(); // ignore non-existing files
+                else if (err) cb(err);
+                else {
+                    shasum.update(data);
+                    hashedFiles = true;
+                    cb();
+                }
             });
         };
 
@@ -21,6 +26,7 @@ module.exports = function () {
 
         q.awaitAll(err => {
             if (err) throw new Error('*** Error reading files:', err);
+            if (!hashedFiles) throw new Error('*** No files found: [' + paths.join(', ') + ']');
             cb(shasum.digest('hex'));
         });
     };
diff --git a/features/support/hooks.js b/features/support/hooks.js
index 5090264..753bdf7 100644
--- a/features/support/hooks.js
+++ b/features/support/hooks.js
@@ -20,6 +20,8 @@ module.exports = function () {
         this.hasLoggedScenarioInfo = false;
         this.setGridSize(this.DEFAULT_GRID_SIZE);
         this.setOrigin(this.DEFAULT_ORIGIN);
+        this.fingerprintExtract = this.hashString([this.luaLibHash, this.binExtractHash].join('-'));
+        this.fingerprintContract = this.hashString(this.binContractHash);
         callback();
     });
 
diff --git a/features/support/launch_classes.js b/features/support/launch_classes.js
index 976f89e..2aace88 100644
--- a/features/support/launch_classes.js
+++ b/features/support/launch_classes.js
@@ -1,9 +1,9 @@
 'use strict';
 
 var fs = require('fs');
-var net = require('net');
 var spawn = require('child_process').spawn;
 var util = require('util');
+var net = require('net');
 var Timeout = require('node-timeout');
 
 var OSRMBaseLoader = class {
@@ -15,9 +15,7 @@ var OSRMBaseLoader = class {
         var limit = Timeout(this.scope.TIMEOUT, { err: this.scope.RoutedError('Launching osrm-routed timed out.') });
 
         var runLaunch = (cb) => {
-            this.osrmUp(() => {
-                this.waitForConnection(cb);
-            });
+            this.osrmUp(() => { this.waitForConnection(cb); });
         };
 
         runLaunch(limit((e) => { if (e) callback(e); else callback(); }));
@@ -46,18 +44,21 @@ var OSRMBaseLoader = class {
     }
 
     waitForConnection (callback) {
-        net.connect({
-            port: this.scope.OSRM_PORT,
-            host: '127.0.0.1'
-        })
-            .on('connect', () => {
-                callback();
-            })
-            .on('error', (e) => {
-                setTimeout(() => {
-                    callback(e);
-                }, 100);
-            });
+        var retryCount = 0;
+        var connectWithRetry = () => {
+            net.connect({ port: this.scope.OSRM_PORT, host: '127.0.0.1' })
+               .on('connect', () => { callback(); })
+               .on('error', () => {
+                   if (retryCount < 2) {
+                       retryCount++;
+                       setTimeout(connectWithRetry, 100);
+                   } else {
+                       callback(new Error('Could not connect to osrm-routed after three retires'));
+                   }
+               });
+        };
+
+        connectWithRetry();
     }
 
     waitForShutdown (callback) {
@@ -86,7 +87,7 @@ var OSRMDirectLoader = class extends OSRMBaseLoader {
             fs.appendFile(this.scope.OSRM_ROUTED_LOG_FILE, data, (err) => { if (err) throw err; });
         };
 
-        var child = spawn(util.format('%s%s/osrm-routed', this.scope.LOAD_LIBRARIES, this.scope.BIN_PATH), [this.inputFile, util.format('-p%d', this.scope.OSRM_PORT)]);
+        var child = spawn(util.format('%s/osrm-routed', this.scope.BIN_PATH), [this.inputFile, util.format('-p%d', this.scope.OSRM_PORT)]);
         this.scope.pid = child.pid;
         child.stdout.on('data', writeToLog);
         child.stderr.on('data', writeToLog);
@@ -122,7 +123,7 @@ var OSRMDatastoreLoader = class extends OSRMBaseLoader {
             fs.appendFile(this.scope.OSRM_ROUTED_LOG_FILE, data, (err) => { if (err) throw err; });
         };
 
-        var child = spawn(util.format('%s%s/osrm-routed', this.scope.LOAD_LIBRARIES, this.scope.BIN_PATH), ['--shared-memory=1', util.format('-p%d', this.scope.OSRM_PORT)]);
+        var child = spawn(util.format('%s/osrm-routed', this.scope.BIN_PATH), ['--shared-memory=1', util.format('-p%d', this.scope.OSRM_PORT)]);
         this.child = child;
         this.scope.pid = child.pid;
         child.stdout.on('data', writeToLog);
@@ -148,7 +149,7 @@ module.exports = {
                 this.loader = new OSRMDirectLoader(this.scope);
                 this.loader.load(inputFile, callback);
             } else {
-                throw new Error('*** Unknown load method ' + method);
+                callback(new Error('*** Unknown load method ' + method));
             }
         }
 
diff --git a/features/support/route.js b/features/support/route.js
index 99a6835..ac214fc 100644
--- a/features/support/route.js
+++ b/features/support/route.js
@@ -1,3 +1,5 @@
+'use strict';
+
 var Timeout = require('node-timeout');
 var request = require('request');
 
@@ -137,8 +139,33 @@ module.exports = function () {
         return this.extractInstructionList(instructions, s => s.name);
     };
 
+    this.pronunciationList = (instructions) => {
+        return this.extractInstructionList(instructions, s => s.pronunciation || '');
+    };
+
+    this.destinationsList = (instructions) => {
+        return this.extractInstructionList(instructions, s => s.destinations || '');
+    };
+
     this.bearingList = (instructions) => {
-        return this.extractInstructionList(instructions, s => s.maneuver.bearing_after);
+        return this.extractInstructionList(instructions, s => s.maneuver.bearing_before + '->' + s.maneuver.bearing_after);
+    };
+
+    this.annotationList = (instructions) => {
+        function zip(list_1, list_2)
+        {
+            let pairs = [];
+            for (let i = 0; i <  list_1.length; ++i) {
+                pairs.push([list_1[i], list_2[i]]);
+            }
+            return pairs;
+        }
+        return instructions.legs.map(l => {return zip(l.annotation.duration, l.annotation.distance).map(p => { return p.join(':'); }).join(','); }).join(',');
+    };
+
+    this.OSMIDList = (instructions) => {
+        // OSM node IDs also come from the annotation list
+        return instructions.legs.map(l => l.annotation.nodes.map(n => n.toString()).join(',')).join(',');
     };
 
     this.turnList = (instructions) => {
@@ -168,6 +195,19 @@ module.exports = function () {
             .join(',');
     };
 
+    this.intersectionList = (instructions) => {
+        return instructions.legs.reduce((m, v) => m.concat(v.steps), [])
+            .map( v => {
+                return v.intersections
+                    .map( intersection => {
+                        var string = intersection.entry[0]+':'+intersection.bearings[0], i;
+                        for( i = 1; i < intersection.bearings.length; ++i )
+                            string = string + ' ' + intersection.entry[i]+':'+intersection.bearings[i];
+                        return string;
+                    }).join(',');
+            }).join(';');
+    };
+
     this.modeList = (instructions) => {
         return this.extractInstructionList(instructions, s => s.mode);
     };
diff --git a/features/support/run.js b/features/support/run.js
index cede245..35561d8 100644
--- a/features/support/run.js
+++ b/features/support/run.js
@@ -25,7 +25,7 @@ module.exports = function () {
             opts = opts.replace('{profile}', [this.PROFILES_PATH, this.profile + '.lua'].join('/'));
         }
 
-        var cmd = util.format('%s%s%s/%s%s%s %s 2>%s', this.QQ, this.LOAD_LIBRARIES, this.BIN_PATH, bin, this.EXE, this.QQ, opts, this.ERROR_LOG_FILE);
+        var cmd = util.format('%s%s/%s%s%s %s 2>%s', this.QQ, this.BIN_PATH, bin, this.EXE, this.QQ, opts, this.ERROR_LOG_FILE);
         process.chdir(this.TEST_FOLDER);
         exec(cmd, (err, stdout, stderr) => {
             this.stdout = stdout.toString();
diff --git a/features/support/shared_steps.js b/features/support/shared_steps.js
index aac42bd..a6783bf 100644
--- a/features/support/shared_steps.js
+++ b/features/support/shared_steps.js
@@ -1,3 +1,5 @@
+'use strict';
+
 var util = require('util');
 var assert = require('assert');
 
@@ -31,16 +33,19 @@ module.exports = function () {
                 var afterRequest = (err, res, body) => {
                     if (err) return cb(err);
                     if (body && body.length) {
-                        var instructions, bearings, turns, modes, times, distances, summary;
+                        let destinations, pronunciations, instructions, bearings, turns, modes, times, distances, summary, intersections;
 
-                        var json = JSON.parse(body);
+                        let json = JSON.parse(body);
 
-                        var hasRoute = json.code === 'Ok';
+                        let hasRoute = json.code === 'Ok';
 
                         if (hasRoute) {
                             instructions = this.wayList(json.routes[0]);
+                            pronunciations = this.pronunciationList(json.routes[0]);
+                            destinations = this.destinationsList(json.routes[0]);
                             bearings = this.bearingList(json.routes[0]);
                             turns = this.turnList(json.routes[0]);
+                            intersections = this.intersectionList(json.routes[0]);
                             modes = this.modeList(json.routes[0]);
                             times = this.timeList(json.routes[0]);
                             distances = this.distanceList(json.routes[0]);
@@ -108,6 +113,10 @@ module.exports = function () {
                                 }
                             }
 
+                            if (headers.has('intersections')) {
+                                got.intersections = (intersections || '').trim();
+                            }
+
                             var putValue = (key, value) => {
                                 if (headers.has(key)) got[key] = instructions ? value : '';
                             };
@@ -117,6 +126,8 @@ module.exports = function () {
                             putValue('modes', modes);
                             putValue('times', times);
                             putValue('distances', distances);
+                            putValue('pronunciations', pronunciations);
+                            putValue('destinations', destinations);
                         }
 
                         var ok = true;
diff --git a/features/testbot/alternative.feature b/features/testbot/alternative.feature
index 4031631..8f2edcc 100644
--- a/features/testbot/alternative.feature
+++ b/features/testbot/alternative.feature
@@ -3,6 +3,7 @@ Feature: Alternative route
 
     Background:
         Given the profile "testbot"
+        And a grid size of 200 meters
 
         And the node map
             |   | b | c | d |   |   |
@@ -17,11 +18,11 @@ Feature: Alternative route
             | dz    |
             | ag    |
             | gh    |
+            | ck    |
+            | kh    |
             | hi    |
             | ij    |
             | jz    |
-            | ck    |
-            | kh    |
 
     Scenario: Enabled alternative
         Given the query options
diff --git a/features/testbot/basic.feature b/features/testbot/basic.feature
index c2f969d..03b4892 100644
--- a/features/testbot/basic.feature
+++ b/features/testbot/basic.feature
@@ -56,7 +56,7 @@ Feature: Basic Routing
 
     Scenario: Two ways connected in a straight line
         Given the node map
-            | a | b | c |
+            | a |   | b |   | c |
 
         And the ways
             | nodes |
diff --git a/features/testbot/bearing.feature b/features/testbot/bearing.feature
index 76a2819..b8aa493 100644
--- a/features/testbot/bearing.feature
+++ b/features/testbot/bearing.feature
@@ -14,8 +14,8 @@ Feature: Compass bearing
             | ab    |
 
         When I route I should get
-            | from | to | route    | bearing   |
-            | a    | b  | ab,ab    | 315,0     |
+            | from | to | route    | bearing      |
+            | a    | b  | ab,ab    | 0->315,315->0|
 
     Scenario: Bearing when going west
         Given the node map
@@ -26,8 +26,8 @@ Feature: Compass bearing
             | ab    |
 
         When I route I should get
-            | from | to | route    | bearing |
-            | a    | b  | ab,ab    | 270,0   |
+            | from | to | route    | bearing      |
+            | a    | b  | ab,ab    | 0->270,270->0|
 
     Scenario: Bearing af 45 degree intervals
         Given the node map
@@ -48,14 +48,14 @@ Feature: Compass bearing
 
         When I route I should get
             | from | to | route    | bearing |
-            | x    | a  | xa,xa    | 0,0     |
-            | x    | b  | xb,xb    | 315,0   |
-            | x    | c  | xc,xc    | 270,0   |
-            | x    | d  | xd,xd    | 225,0   |
-            | x    | e  | xe,xe    | 180,0   |
-            | x    | f  | xf,xf    | 135,0   |
-            | x    | g  | xg,xg    | 90,0    |
-            | x    | h  | xh,xh    | 45,0    |
+            | x    | a  | xa,xa    | 0->0,0->0|
+            | x    | b  | xb,xb    | 0->315,315->0|
+            | x    | c  | xc,xc    | 0->270,270->0|
+            | x    | d  | xd,xd    | 0->225,225->0|
+            | x    | e  | xe,xe    | 0->180,180->0|
+            | x    | f  | xf,xf    | 0->135,135->0|
+            | x    | g  | xg,xg    | 0->90,90->0|
+            | x    | h  | xh,xh    | 0->45,45->0|
 
     Scenario: Bearing in a roundabout
         Given the node map
@@ -76,9 +76,9 @@ Feature: Compass bearing
             | ha    | yes    |
 
         When I route I should get
-            | from | to | route                   | bearing                   |
-            | c    | b  | cd,de,ef,fg,gh,ha,ab,ab | 270,225,180,135,90,45,0,0 |
-            | g    | f  | gh,ha,ab,bc,cd,de,ef,ef | 90,45,0,315,270,225,180,0 |
+            | from | to | route                   | bearing                                                     |
+            | c    | b  | cd,de,ef,fg,gh,ha,ab,ab | 0->270,270->225,225->180,180->135,135->90,90->45,45->0,0->0 |
+            | g    | f  | gh,ha,ab,bc,cd,de,ef,ef | 0->90,90->45,45->0,0->315,315->270,270->225,225->180,180->0 |
 
     Scenario: Bearing should stay constant when zig-zagging
         Given the node map
@@ -97,7 +97,7 @@ Feature: Compass bearing
 
         When I route I should get
             | from | to | route                   | bearing               |
-            | a    | h  | ab,bc,cd,de,ef,fg,gh,gh | 0,135,0,135,0,135,0,0 |
+            | a    | h  | ab,bc,cd,de,ef,fg,gh,gh | 0->0,0->135,135->0,0->135,135->0,0->135,135->0,0->0 |
 
     Scenario: Bearings on an east-west way.
         Given the node map
@@ -108,37 +108,37 @@ Feature: Compass bearing
             | abcdef |
 
         When I route I should get
-            | from | to | route         | bearing |
-            | a    | b  | abcdef,abcdef | 90,0    |
-            | a    | c  | abcdef,abcdef | 90,0    |
-            | a    | d  | abcdef,abcdef | 90,0    |
-            | a    | e  | abcdef,abcdef | 90,0    |
-            | a    | f  | abcdef,abcdef | 90,0    |
-            | b    | a  | abcdef,abcdef | 270,0   |
-            | b    | c  | abcdef,abcdef | 90,0    |
-            | b    | d  | abcdef,abcdef | 90,0    |
-            | b    | e  | abcdef,abcdef | 90,0    |
-            | b    | f  | abcdef,abcdef | 90,0    |
-            | c    | a  | abcdef,abcdef | 270,0   |
-            | c    | b  | abcdef,abcdef | 270,0   |
-            | c    | d  | abcdef,abcdef | 90,0    |
-            | c    | e  | abcdef,abcdef | 90,0    |
-            | c    | f  | abcdef,abcdef | 90,0    |
-            | d    | a  | abcdef,abcdef | 270,0   |
-            | d    | b  | abcdef,abcdef | 270,0   |
-            | d    | c  | abcdef,abcdef | 270,0   |
-            | d    | e  | abcdef,abcdef | 90,0    |
-            | d    | f  | abcdef,abcdef | 90,0    |
-            | e    | a  | abcdef,abcdef | 270,0   |
-            | e    | b  | abcdef,abcdef | 270,0   |
-            | e    | c  | abcdef,abcdef | 270,0   |
-            | e    | d  | abcdef,abcdef | 270,0   |
-            | e    | f  | abcdef,abcdef | 90,0    |
-            | f    | a  | abcdef,abcdef | 270,0   |
-            | f    | b  | abcdef,abcdef | 270,0   |
-            | f    | c  | abcdef,abcdef | 270,0   |
-            | f    | d  | abcdef,abcdef | 270,0   |
-            | f    | e  | abcdef,abcdef | 270,0   |
+            | from | to | route         | bearing       |
+            | a    | b  | abcdef,abcdef | 0->90,90->0   |
+            | a    | c  | abcdef,abcdef | 0->90,90->0   |
+            | a    | d  | abcdef,abcdef | 0->90,90->0   |
+            | a    | e  | abcdef,abcdef | 0->90,90->0   |
+            | a    | f  | abcdef,abcdef | 0->90,90->0   |
+            | b    | a  | abcdef,abcdef | 0->270,270->0 |
+            | b    | c  | abcdef,abcdef | 0->90,90->0   |
+            | b    | d  | abcdef,abcdef | 0->90,90->0   |
+            | b    | e  | abcdef,abcdef | 0->90,90->0   |
+            | b    | f  | abcdef,abcdef | 0->90,90->0   |
+            | c    | a  | abcdef,abcdef | 0->270,270->0 |
+            | c    | b  | abcdef,abcdef | 0->270,270->0 |
+            | c    | d  | abcdef,abcdef | 0->90,90->0   |
+            | c    | e  | abcdef,abcdef | 0->90,90->0   |
+            | c    | f  | abcdef,abcdef | 0->90,90->0   |
+            | d    | a  | abcdef,abcdef | 0->270,270->0 |
+            | d    | b  | abcdef,abcdef | 0->270,270->0 |
+            | d    | c  | abcdef,abcdef | 0->270,270->0 |
+            | d    | e  | abcdef,abcdef | 0->90,90->0   |
+            | d    | f  | abcdef,abcdef | 0->90,90->0   |
+            | e    | a  | abcdef,abcdef | 0->270,270->0 |
+            | e    | b  | abcdef,abcdef | 0->270,270->0 |
+            | e    | c  | abcdef,abcdef | 0->270,270->0 |
+            | e    | d  | abcdef,abcdef | 0->270,270->0 |
+            | e    | f  | abcdef,abcdef | 0->90,90->0   |
+            | f    | a  | abcdef,abcdef | 0->270,270->0 |
+            | f    | b  | abcdef,abcdef | 0->270,270->0 |
+            | f    | c  | abcdef,abcdef | 0->270,270->0 |
+            | f    | d  | abcdef,abcdef | 0->270,270->0 |
+            | f    | e  | abcdef,abcdef | 0->270,270->0 |
 
     Scenario: Bearings at high latitudes
     # The coordinas below was calculated using http://www.movable-type.co.uk/scripts/latlong.html,
@@ -161,19 +161,19 @@ Feature: Compass bearing
             | bd    |
 
         When I route I should get
-            | from | to | route | bearing |
-            | a    | b  | ab,ab | 0,0     |
-            | b    | c  | bc,bc | 90,0    |
-            | c    | d  | cd,cd | 180,0   |
-            | d    | a  | da,da | 270,0   |
-            | b    | a  | ab,ab | 180,0   |
-            | c    | b  | bc,bc | 270,0   |
-            | d    | c  | cd,cd | 0,0     |
-            | a    | d  | da,da | 90,0    |
-            | a    | c  | ac,ac | 45,0    |
-            | c    | a  | ac,ac | 225,0   |
-            | b    | d  | bd,bd | 135,0   |
-            | d    | b  | bd,bd | 315,0   |
+            | from | to | route | bearing         |
+            | a    | b  | ab,ab | 0->0,0->0       |
+            | b    | c  | bc,bc | 0->90,90->0     |
+            | c    | d  | cd,cd | 0->180,180->0   |
+            | d    | a  | da,da | 0->270,270->0   |
+            | b    | a  | ab,ab | 0->180,180->0   |
+            | c    | b  | bc,bc | 0->270,270->0   |
+            | d    | c  | cd,cd | 0->0,0->0       |
+            | a    | d  | da,da | 0->90,90->0     |
+            | a    | c  | ac,ac | 0->45,45->0     |
+            | c    | a  | ac,ac | 0->225,225->0   |
+            | b    | d  | bd,bd | 0->135,135->0   |
+            | d    | b  | bd,bd | 0->315,315->0   |
 
     Scenario: Bearings at high negative latitudes
     # The coordinas below was calculated using http://www.movable-type.co.uk/scripts/latlong.html,
@@ -196,16 +196,16 @@ Feature: Compass bearing
             | bd    |
 
         When I route I should get
-            | from | to | route  | bearing |
-            | a    | b  | ab,ab  | 180,0   |
-            | b    | c  | bc,bc  | 90,0    |
-            | c    | d  | cd,cd  | 0,0     |
-            | d    | a  | da,da  | 270,0   |
-            | b    | a  | ab,ab  | 0,0     |
-            | c    | b  | bc,bc  | 270,0   |
-            | d    | c  | cd,cd  | 180,0   |
-            | a    | d  | da,da  | 90,0    |
-            | a    | c  | ac,ac  | 135,0   |
-            | c    | a  | ac,ac  | 315,0   |
-            | b    | d  | bd,bd  | 45,0    |
-            | d    | b  | bd,bd  | 225,0   |
+            | from | to | route  | bearing         |
+            | a    | b  | ab,ab  | 0->180,180->0   |
+            | b    | c  | bc,bc  | 0->90,90->0     |
+            | c    | d  | cd,cd  | 0->0,0->0       |
+            | d    | a  | da,da  | 0->270,270->0   |
+            | b    | a  | ab,ab  | 0->0,0->0       |
+            | c    | b  | bc,bc  | 0->270,270->0   |
+            | d    | c  | cd,cd  | 0->180,180->0   |
+            | a    | d  | da,da  | 0->90,90->0     |
+            | a    | c  | ac,ac  | 0->135,135->0   |
+            | c    | a  | ac,ac  | 0->315,315->0   |
+            | b    | d  | bd,bd  | 0->45,45->0     |
+            | d    | b  | bd,bd  | 0->225,225->0   |
diff --git a/features/testbot/bearing_param.feature b/features/testbot/bearing_param.feature
index ea60856..c0c4072 100644
--- a/features/testbot/bearing_param.feature
+++ b/features/testbot/bearing_param.feature
@@ -15,11 +15,11 @@ Feature: Bearing parameter
 
         When I route I should get
             | from | to | bearings  | route | bearing |
-            | b    | c  | 90 90     | ad,ad | 90,0    |
+            | b    | c  | 90 90     | ad,ad | 0->90,90->0|
             | b    | c  | 180 90    |       |         |
-            | b    | c  | 80 100    | ad,ad | 90,0    |
+            | b    | c  | 80 100    | ad,ad | 0->90,90->0|
             | b    | c  | 79 100    |       |         |
-            | b    | c  | 79,11 100 | ad,ad | 90,0    |
+            | b    | c  | 79,11 100 | ad,ad | 0->90,90->0|
 
     Scenario: Testbot - Intial bearing in simple case
         Given the node map
@@ -35,16 +35,16 @@ Feature: Bearing parameter
         When I route I should get
             | from | to | bearings | route | bearing |
             | 0    | c  | 0 0      |       |         |
-            | 0    | c  | 45 45    | bc,bc | 45 ~3%  |
+            | 0    | c  | 45 45    | bc,bc | 0->44,44->0 +- 1|
             | 0    | c  | 85 85    |       |         |
             | 0    | c  | 95 95    |       |         |
-            | 0    | c  | 135 135  | ac,ac | 135 ~1% |
+            | 0    | c  | 135 135  | ac,ac | 0->135,135->0 +- 1|
             | 0    | c  | 180 180  |       |         |
 
     Scenario: Testbot - Initial bearing on split way
         Given the node map
-        | d |  |  |  |  | 1 |  |  |  |  | c |
-        | a |  |  |  |  | 0 |  |  |  |  | b |
+           | g | d |  |  |  |  | 1 |  |  |  |  | c | f |
+           | h | a |  |  |  |  | 0 |  |  |  |  | b | e |
 
         And the ways
             | nodes | oneway |
@@ -52,22 +52,26 @@ Feature: Bearing parameter
             | bc    | yes    |
             | cd    | yes    |
             | da    | yes    |
+            | be    | yes    |
+            | fc    | yes    |
+            | dg    | yes    |
+            | ha    | yes    |
 
         When I route I should get
             | from | to | bearings | route       | bearing       |
-            | 0    | b  | 10 10    | bc,bc       | 0,0            |
-            | 0    | b  | 90 90    | ab,ab       | 90,0           |
+            | 0    | b  | 10 10    | bc,bc       | 0->0,0->0     |
+            | 0    | b  | 90 90    | ab,ab       | 0->90,90->0   |
             # The returned bearing is wrong here, it's based on the snapped
             # coordinates, not the acutal edge bearing.  This should be
             # fixed one day, but it's only a problem when we snap two vias
             # to the same point - DP
             #| 0    | b  | 170 170  | da          | 180           |
             #| 0    | b  | 189 189  | da          | 180           |
-            | 0    | 1  | 90 270   | ab,bc,cd,cd    | 90,0,270,0      |
-            | 1    | d  | 10 10    | bc,bc          | 0,0             |
-            | 1    | d  | 90 90    | ab,bc,cd,da,da | 90,0,270,180,0  |
-            | 1    | 0  | 189 189  | da,da       | 180,0           |
-            | 1    | d  | 270 270  | cd,cd       | 270,0           |
+            | 0    | 1  | 90 270   | ab,bc,cd,cd    | 0->90,90->0,0->270,270->0      |
+            | 1    | d  | 10 10    | bc,bc          | 0->0,0->0  |
+            | 1    | d  | 90 90    | ab,bc,cd,da,da | 0->90,90->0,0->270,270->180,180->0  |
+            | 1    | 0  | 189 189  | da,da       | 0->180,180->0 |
+            | 1    | d  | 270 270  | cd,cd       | 0->270,270->0 |
             | 1    | d  | 349 349  |             |               |
 
     Scenario: Testbot - Initial bearing in all direction
@@ -81,31 +85,31 @@ Feature: Bearing parameter
             | f |  |   | e |   |  | d |
 
         And the ways
-            | nodes | oneway |
-            | ia    | yes    |
-            | jb    | yes    |
-            | kc    | yes    |
-            | ld    | yes    |
-            | me    | yes    |
-            | nf    | yes    |
-            | og    | yes    |
-            | ph    | yes    |
-            | ab    | yes    |
-            | bc    | yes    |
-            | cd    | yes    |
-            | de    | yes    |
-            | ef    | yes    |
-            | fg    | yes    |
-            | gh    | yes    |
-            | ha    | yes    |
+            | nodes | oneway | name |
+            | ia    | yes    | ia   |
+            | jb    | yes    | jb   |
+            | kc    | yes    | kc   |
+            | ld    | yes    | ld   |
+            | me    | yes    | me   |
+            | nf    | yes    | nf   |
+            | og    | yes    | og   |
+            | ph    | yes    | ph   |
+            | ab    | yes    | ring |
+            | bc    | yes    | ring |
+            | cd    | yes    | ring |
+            | de    | yes    | ring |
+            | ef    | yes    | ring |
+            | fg    | yes    | ring |
+            | gh    | yes    | ring |
+            | ha    | yes    | ring |
 
         When I route I should get
-            | from | to | bearings | route                         | bearing                       |
-            | 0    | q  | 0 90     | ia,ab,bc,cd,de,ef,fg,gh,ha,ha | 0,90,180,180,270,270,0,0,90,0 |
-            | 0    | a  | 45 90    | jb,bc,cd,de,ef,fg,gh,ha,ha    | 45,180,180,270,270,0,0,90,0   |
-            | 0    | q  | 90 90    | kc,cd,de,ef,fg,gh,ha,ha       | 90,180,270,270,0,0,90,0       |
-            | 0    | a  | 135 90   | ld,de,ef,fg,gh,ha,ha          | 135,270,270,0,0,90,0          |
-            | 0    | a  | 180 90   | me,ef,fg,gh,ha,ha             | 180,270,0,0,90,0              |
-            | 0    | a  | 225 90   | nf,fg,gh,ha,ha                | 225,0,0,90,0                  |
-            | 0    | a  | 270 90   | og,gh,ha,ha                   | 270,0,90,0                    |
-            | 0    | a  | 315 90   | ph,ha,ha                      | 315,90,0                      |
+            | from | to | bearings | route                       | bearing                                   |
+            | 0    | q  | 0 90     | ia,ring,ring,ring,ring,ring | 0->0,0->90,180->270,270->0,0->90,90->0    |
+            | 0    | a  | 45 90    | jb,ring,ring,ring,ring,ring | 0->45,45->180,180->270,270->0,0->90,90->0 |
+            | 0    | q  | 90 90    | kc,ring,ring,ring,ring      | 0->90,90->180,270->0,0->90,90->0          |
+            | 0    | a  | 135 90   | ld,ring,ring,ring,ring      | 0->135,135->270,270->0,0->90,90->0        |
+            | 0    | a  | 180 90   | me,ring,ring,ring           | 0->180,180->270,0->90,90->0               |
+            | 0    | a  | 225 90   | nf,ring,ring,ring           | 0->225,225->0,0->90,90->0                 |
+            | 0    | a  | 270 90   | og,ring,ring                | 0->270,270->0,90->0                       |
+            | 0    | a  | 315 90   | ph,ring,ring                | 0->315,315->90,90->0                      |
diff --git a/features/testbot/compression.feature b/features/testbot/compression.feature
index 79823ce..4569b4c 100644
--- a/features/testbot/compression.feature
+++ b/features/testbot/compression.feature
@@ -18,5 +18,5 @@ Feature: Geometry Compression
 
         When I route I should get
             | from | to | route         | distance | speed   |
-            | b    | e  | abcdef,abcdef | 589m     | 36 km/h |
-            | e    | b  | abcdef,abcdef | 589m     | 36 km/h |
+            | b    | e  | abcdef,abcdef | 588.8m   | 36 km/h |
+            | e    | b  | abcdef,abcdef | 588.8m   | 36 km/h |
diff --git a/features/testbot/continue_straight.feature b/features/testbot/continue_straight.feature
index 40fcbb4..d761ead 100644
--- a/features/testbot/continue_straight.feature
+++ b/features/testbot/continue_straight.feature
@@ -3,6 +3,7 @@ Feature: U-turns at via points
 
     Background:
         Given the profile "testbot"
+        Given a grid size of 100 meters
 
     Scenario: Continue straight at waypoints enabled by default
         Given the node map
diff --git a/features/testbot/matching.feature b/features/testbot/matching.feature
index 71ac077..f105a72 100644
--- a/features/testbot/matching.feature
+++ b/features/testbot/matching.feature
@@ -104,3 +104,27 @@ Feature: Basic Map Matching
             | trace | matchings |
             | dcba  | hg,gf,fe  |
             | efgh  | ab,bc,cd  |
+
+    Scenario: Testbot - Duration details
+        Given the query options
+            | annotations | true |
+
+        Given the node map
+            | a | b | c | d | e |   | g | h |
+            |   |   | i |   |   |   |   |   |
+
+        And the ways
+            | nodes    | oneway |
+            | abcdegh  | no     |
+            | ci       | no     |
+
+        When I match I should get
+            | trace | matchings | annotation                                                                     |
+            | abeh  | abcedgh   | 1:9.897633,0:0,1:10.008842,1:10.008842,1:10.008842,0:0,2:20.017685,1:10.008842 |
+            | abci  | abc,ci    | 1:9.897633,0:0,1:10.008842,0:0.111209,1:10.010367                              |
+
+        # The following is the same as the above, but separated for readability (line length)
+        When I match I should get
+            | trace | matchings | OSM IDs               |
+            | abeh  | abcedgh   | 1,2,3,2,3,4,5,4,5,6,7 |
+            | abci  | abc,ci    | 1,2,3,2,3,8,3,8       |
diff --git a/features/testbot/mode.feature b/features/testbot/mode.feature
index b98c8bf..f0675b5 100644
--- a/features/testbot/mode.feature
+++ b/features/testbot/mode.feature
@@ -11,6 +11,7 @@ Feature: Testbot - Travel mode
 
     Background:
        Given the profile "testbot"
+       Given a grid size of 200 meters
 
     Scenario: Testbot - Always announce mode change
         Given the node map
@@ -72,9 +73,9 @@ Feature: Testbot - Travel mode
             | ab    | steps   |
 
         When I route I should get
-            | from | to | route | modes                 | time    |
-            | 0    | 1  | ab,ab | steps down,steps down | 60s +-1 |
-            | 1    | 0  | ab,ab | steps up,steps up     | 60s +-1 |
+            | from | to | route | modes                 | time     |
+            | 0    | 1  | ab,ab | steps down,steps down | 120s +-1 |
+            | 1    | 0  | ab,ab | steps up,steps up     | 120s +-1 |
 
     @oneway
     Scenario: Testbot - Modes for oneway, different forward/backward speeds
diff --git a/features/testbot/projection.feature b/features/testbot/projection.feature
index ad4a32b..cabd38e 100644
--- a/features/testbot/projection.feature
+++ b/features/testbot/projection.feature
@@ -24,12 +24,12 @@ Feature: Projection to nearest point on road
     Scenario: Projection onto way at high latitudes, 1km distance
         When I route I should get
             | from | to | route   | bearing   | distance   |
-            | b    | a  | abc,abc | 225,0 +-1 | 1000m +- 7 |
-            | b    | c  | abc,abc | 45,0  +-1 | 1000m +- 7 |
-            | a    | d  | abc,abc | 45,0  +-1 | 1000m +- 7 |
-            | d    | a  | abc,abc | 225,0 +-1 | 1000m +- 7 |
-            | c    | d  | abc,abc | 225,0 +-1 | 1000m +- 8 |
-            | d    | c  | abc,abc | 45 +-1    | 1000m +- 8 |
+            | b    | a  | abc,abc | 0->225,225->0 | 1000m +- 7 |
+            | b    | c  | abc,abc | 0->45,45->0 | 1000m +- 7 |
+            | a    | d  | abc,abc | 0->45,45->0 | 1000m +- 7 |
+            | d    | a  | abc,abc | 0->225,225->0 | 1000m +- 7 |
+            | c    | d  | abc,abc | 0->225,224->0 | 1000m +- 8 |
+            | d    | c  | abc,abc | 0->44,45->0  | 1000m +- 8 |
 
     Scenario: Projection onto way at high latitudes, no distance
         When I route I should get
diff --git a/features/testbot/summary.feature b/features/testbot/summary.feature
index 3290e3c..028c863 100644
--- a/features/testbot/summary.feature
+++ b/features/testbot/summary.feature
@@ -3,11 +3,12 @@ Feature: Basic Routing
 
     Background:
         Given the profile "testbot"
+        Given a grid size of 200 meters
 
     @smallest
-    Scenario: Checking 
+    Scenario: Checking
         Given the node map
-            | a | b |  | c | d | e |
+            | a | b | 1 | c | d | e |
 
         And the ways
             | nodes |
@@ -22,6 +23,7 @@ Feature: Basic Routing
             | e    | a  | de,cd,bc,ab,ab | de, bc   |
             | a    | b  | ab,ab          | ab       |
             | b    | d  | bc,cd,cd       | bc, cd   |
+            | 1    | c  | bc,bc          | bc       |
 
     @smallest
     Scenario: Check handling empty values
diff --git a/include/contractor/contractor.hpp b/include/contractor/contractor.hpp
index a188a86..66f9980 100644
--- a/include/contractor/contractor.hpp
+++ b/include/contractor/contractor.hpp
@@ -32,11 +32,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include "contractor/query_edge.hpp"
 #include "extractor/edge_based_edge.hpp"
 #include "extractor/edge_based_node.hpp"
-#include "util/typedefs.hpp"
 #include "util/deallocating_vector.hpp"
+#include "util/typedefs.hpp"
 
-#include <vector>
 #include <string>
+#include <vector>
 
 #include <cstddef>
 
diff --git a/include/contractor/graph_contractor.hpp b/include/contractor/graph_contractor.hpp
index 1e7831d..3eaa5a9 100644
--- a/include/contractor/graph_contractor.hpp
+++ b/include/contractor/graph_contractor.hpp
@@ -1,17 +1,17 @@
 #ifndef GRAPH_CONTRACTOR_HPP
 #define GRAPH_CONTRACTOR_HPP
 
+#include "contractor/query_edge.hpp"
 #include "util/binary_heap.hpp"
 #include "util/deallocating_vector.hpp"
 #include "util/dynamic_graph.hpp"
-#include "util/percent.hpp"
-#include "contractor/query_edge.hpp"
-#include "util/xor_fast_hash.hpp"
-#include "util/xor_fast_hash_storage.hpp"
 #include "util/integer_range.hpp"
+#include "util/percent.hpp"
 #include "util/simple_logger.hpp"
 #include "util/timing_util.hpp"
 #include "util/typedefs.hpp"
+#include "util/xor_fast_hash.hpp"
+#include "util/xor_fast_hash_storage.hpp"
 
 #include <boost/assert.hpp>
 
@@ -165,14 +165,22 @@ class GraphContractor
                     << static_cast<unsigned int>(diter->target);
             }
 #endif
-            edges.emplace_back(diter->source, diter->target,
-                               static_cast<unsigned int>(std::max(diter->weight, 1)), 1,
-                               diter->edge_id, false, diter->forward ? true : false,
+            edges.emplace_back(diter->source,
+                               diter->target,
+                               static_cast<unsigned int>(std::max(diter->weight, 1)),
+                               1,
+                               diter->edge_id,
+                               false,
+                               diter->forward ? true : false,
                                diter->backward ? true : false);
 
-            edges.emplace_back(diter->target, diter->source,
-                               static_cast<unsigned int>(std::max(diter->weight, 1)), 1,
-                               diter->edge_id, false, diter->backward ? true : false,
+            edges.emplace_back(diter->target,
+                               diter->source,
+                               static_cast<unsigned int>(std::max(diter->weight, 1)),
+                               1,
+                               diter->edge_id,
+                               false,
+                               diter->backward ? true : false,
                                diter->forward ? true : false);
         }
         // clear input vector
@@ -276,8 +284,7 @@ class GraphContractor
         std::vector<RemainingNodeData> remaining_nodes(number_of_nodes);
         // initialize priorities in parallel
         tbb::parallel_for(tbb::blocked_range<int>(0, number_of_nodes, InitGrainSize),
-                          [this, &remaining_nodes](const tbb::blocked_range<int> &range)
-                          {
+                          [this, &remaining_nodes](const tbb::blocked_range<int> &range) {
                               for (int x = range.begin(), end = range.end(); x != end; ++x)
                               {
                                   remaining_nodes[x].id = x;
@@ -299,9 +306,8 @@ class GraphContractor
 
             std::cout << "initializing elimination PQ ..." << std::flush;
             tbb::parallel_for(tbb::blocked_range<int>(0, number_of_nodes, PQGrainSize),
-                              [this, &node_priorities, &node_depth,
-                               &thread_data_list](const tbb::blocked_range<int> &range)
-                              {
+                              [this, &node_priorities, &node_depth, &thread_data_list](
+                                  const tbb::blocked_range<int> &range) {
                                   ContractorThreadData *data = thread_data_list.GetThreadData();
                                   for (int x = range.begin(), end = range.end(); x != end; ++x)
                                   {
@@ -342,7 +348,8 @@ class GraphContractor
                 // remaining graph
                 std::vector<NodeID> new_node_id_from_orig_id_map(number_of_nodes, SPECIAL_NODEID);
 
-                for (const auto new_node_id : util::irange<std::size_t>(0UL, remaining_nodes.size()))
+                for (const auto new_node_id :
+                     util::irange<std::size_t>(0UL, remaining_nodes.size()))
                 {
                     auto &node = remaining_nodes[new_node_id];
                     BOOST_ASSERT(node_priorities.size() > node.id);
@@ -352,7 +359,8 @@ class GraphContractor
                 }
 
                 // build forward and backward renumbering map and remap ids in remaining_nodes
-                for (const auto new_node_id : util::irange<std::size_t>(0UL, remaining_nodes.size()))
+                for (const auto new_node_id :
+                     util::irange<std::size_t>(0UL, remaining_nodes.size()))
                 {
                     auto &node = remaining_nodes[new_node_id];
                     // create renumbering maps in both directions
@@ -378,7 +386,8 @@ class GraphContractor
                             // node is not yet contracted.
                             // add (renumbered) outgoing edges to new util::DynamicGraph.
                             ContractorEdge new_edge = {new_node_id_from_orig_id_map[source],
-                                                       new_node_id_from_orig_id_map[target], data};
+                                                       new_node_id_from_orig_id_map[target],
+                                                       data};
 
                             new_edge.data.is_original_via_node_ID = true;
                             BOOST_ASSERT_MSG(SPECIAL_NODEID != new_node_id_from_orig_id_map[source],
@@ -421,9 +430,8 @@ class GraphContractor
 
             tbb::parallel_for(
                 tbb::blocked_range<std::size_t>(0, remaining_nodes.size(), IndependentGrainSize),
-                [this, &node_priorities, &remaining_nodes,
-                 &thread_data_list](const tbb::blocked_range<std::size_t> &range)
-                {
+                [this, &node_priorities, &remaining_nodes, &thread_data_list](
+                    const tbb::blocked_range<std::size_t> &range) {
                     ContractorThreadData *data = thread_data_list.GetThreadData();
                     // determine independent node set
                     for (auto i = range.begin(), end = range.end(); i != end; ++i)
@@ -436,8 +444,7 @@ class GraphContractor
 
             // sort all remaining nodes to the beginning of the sequence
             const auto begin_independent_nodes = stable_partition(
-                remaining_nodes.begin(), remaining_nodes.end(), [](RemainingNodeData node_data)
-                {
+                remaining_nodes.begin(), remaining_nodes.end(), [](RemainingNodeData node_data) {
                     return !node_data.is_independent;
                 });
             auto begin_independent_nodes_idx =
@@ -448,11 +455,10 @@ class GraphContractor
             {
                 // write out contraction level
                 tbb::parallel_for(
-                    tbb::blocked_range<std::size_t>(begin_independent_nodes_idx,
-                                                    end_independent_nodes_idx, ContractGrainSize),
-                    [this, remaining_nodes, flushed_contractor,
-                     current_level](const tbb::blocked_range<std::size_t> &range)
-                    {
+                    tbb::blocked_range<std::size_t>(
+                        begin_independent_nodes_idx, end_independent_nodes_idx, ContractGrainSize),
+                    [this, remaining_nodes, flushed_contractor, current_level](
+                        const tbb::blocked_range<std::size_t> &range) {
                         if (flushed_contractor)
                         {
                             for (int position = range.begin(), end = range.end(); position != end;
@@ -475,26 +481,24 @@ class GraphContractor
             }
 
             // contract independent nodes
-            tbb::parallel_for(tbb::blocked_range<std::size_t>(begin_independent_nodes_idx,
-                                                              end_independent_nodes_idx,
-                                                              ContractGrainSize),
-                              [this, &remaining_nodes,
-                               &thread_data_list](const tbb::blocked_range<std::size_t> &range)
-                              {
-                                  ContractorThreadData *data = thread_data_list.GetThreadData();
-                                  for (int position = range.begin(), end = range.end();
-                                       position != end; ++position)
-                                  {
-                                      const NodeID x = remaining_nodes[position].id;
-                                      this->ContractNode<false>(data, x);
-                                  }
-                              });
+            tbb::parallel_for(
+                tbb::blocked_range<std::size_t>(
+                    begin_independent_nodes_idx, end_independent_nodes_idx, ContractGrainSize),
+                [this, &remaining_nodes, &thread_data_list](
+                    const tbb::blocked_range<std::size_t> &range) {
+                    ContractorThreadData *data = thread_data_list.GetThreadData();
+                    for (int position = range.begin(), end = range.end(); position != end;
+                         ++position)
+                    {
+                        const NodeID x = remaining_nodes[position].id;
+                        this->ContractNode<false>(data, x);
+                    }
+                });
 
             tbb::parallel_for(
-                tbb::blocked_range<int>(begin_independent_nodes_idx, end_independent_nodes_idx,
-                                        DeleteGrainSize),
-                [this, &remaining_nodes, &thread_data_list](const tbb::blocked_range<int> &range)
-                {
+                tbb::blocked_range<int>(
+                    begin_independent_nodes_idx, end_independent_nodes_idx, DeleteGrainSize),
+                [this, &remaining_nodes, &thread_data_list](const tbb::blocked_range<int> &range) {
                     ContractorThreadData *data = thread_data_list.GetThreadData();
                     for (int position = range.begin(), end = range.end(); position != end;
                          ++position)
@@ -507,8 +511,7 @@ class GraphContractor
             // make sure we really sort each block
             tbb::parallel_for(
                 thread_data_list.data.range(),
-                [&](const ThreadDataContainer::EnumerableThreadData::range_type &range)
-                {
+                [&](const ThreadDataContainer::EnumerableThreadData::range_type &range) {
                     for (auto &data : range)
                         tbb::parallel_sort(data->inserted_edges.begin(),
                                            data->inserted_edges.end());
@@ -542,11 +545,11 @@ class GraphContractor
             if (!use_cached_node_priorities)
             {
                 tbb::parallel_for(
-                    tbb::blocked_range<int>(begin_independent_nodes_idx, end_independent_nodes_idx,
+                    tbb::blocked_range<int>(begin_independent_nodes_idx,
+                                            end_independent_nodes_idx,
                                             NeighboursGrainSize),
-                    [this, &node_priorities, &remaining_nodes, &node_depth,
-                     &thread_data_list](const tbb::blocked_range<int> &range)
-                    {
+                    [this, &node_priorities, &remaining_nodes, &node_depth, &thread_data_list](
+                        const tbb::blocked_range<int> &range) {
                         ContractorThreadData *data = thread_data_list.GetThreadData();
                         for (int position = range.begin(), end = range.end(); position != end;
                              ++position)
@@ -570,8 +573,7 @@ class GraphContractor
             if (orig_node_id_from_new_node_id_map.size() > 0)
             {
                 tbb::parallel_for(tbb::blocked_range<int>(0, remaining_nodes.size(), InitGrainSize),
-                                  [this, &remaining_nodes](const tbb::blocked_range<int> &range)
-                                  {
+                                  [this, &remaining_nodes](const tbb::blocked_range<int> &range) {
                                       for (int x = range.begin(), end = range.end(); x != end; ++x)
                                       {
                                           const auto orig_id = remaining_nodes[x].id;
@@ -583,8 +585,7 @@ class GraphContractor
             else
             {
                 tbb::parallel_for(tbb::blocked_range<int>(0, remaining_nodes.size(), InitGrainSize),
-                                  [this, &remaining_nodes](const tbb::blocked_range<int> &range)
-                                  {
+                                  [this, &remaining_nodes](const tbb::blocked_range<int> &range) {
                                       for (int x = range.begin(), end = range.end(); x != end; ++x)
                                       {
                                           const auto orig_id = remaining_nodes[x].id;
@@ -843,15 +844,25 @@ class GraphContractor
                             // guarantees that source is not connected to another node that is
                             // contracted
                             node_weights[source] = path_distance; // make sure to prune better
-                            inserted_edges.emplace_back(
-                                source, target, path_distance,
-                                out_data.originalEdges + in_data.originalEdges, node, SHORTCUT_ARC,
-                                FORWARD_DIRECTION_ENABLED, REVERSE_DIRECTION_DISABLED);
-
-                            inserted_edges.emplace_back(
-                                target, source, path_distance,
-                                out_data.originalEdges + in_data.originalEdges, node, SHORTCUT_ARC,
-                                FORWARD_DIRECTION_DISABLED, REVERSE_DIRECTION_ENABLED);
+                            inserted_edges.emplace_back(source,
+                                                        target,
+                                                        path_distance,
+                                                        out_data.originalEdges +
+                                                            in_data.originalEdges,
+                                                        node,
+                                                        SHORTCUT_ARC,
+                                                        FORWARD_DIRECTION_ENABLED,
+                                                        REVERSE_DIRECTION_DISABLED);
+
+                            inserted_edges.emplace_back(target,
+                                                        source,
+                                                        path_distance,
+                                                        out_data.originalEdges +
+                                                            in_data.originalEdges,
+                                                        node,
+                                                        SHORTCUT_ARC,
+                                                        FORWARD_DIRECTION_DISABLED,
+                                                        REVERSE_DIRECTION_ENABLED);
                         }
                     }
                     continue;
@@ -867,8 +878,8 @@ class GraphContractor
             if (RUNSIMULATION)
             {
                 const int constexpr SIMULATION_SEARCH_SPACE_SIZE = 1000;
-                Dijkstra(max_distance, number_of_targets, SIMULATION_SEARCH_SPACE_SIZE, *data,
-                         node);
+                Dijkstra(
+                    max_distance, number_of_targets, SIMULATION_SEARCH_SPACE_SIZE, *data, node);
             }
             else
             {
@@ -898,14 +909,22 @@ class GraphContractor
                     }
                     else
                     {
-                        inserted_edges.emplace_back(source, target, path_distance,
+                        inserted_edges.emplace_back(source,
+                                                    target,
+                                                    path_distance,
                                                     out_data.originalEdges + in_data.originalEdges,
-                                                    node, SHORTCUT_ARC, FORWARD_DIRECTION_ENABLED,
+                                                    node,
+                                                    SHORTCUT_ARC,
+                                                    FORWARD_DIRECTION_ENABLED,
                                                     REVERSE_DIRECTION_DISABLED);
 
-                        inserted_edges.emplace_back(target, source, path_distance,
+                        inserted_edges.emplace_back(target,
+                                                    source,
+                                                    path_distance,
                                                     out_data.originalEdges + in_data.originalEdges,
-                                                    node, SHORTCUT_ARC, FORWARD_DIRECTION_DISABLED,
+                                                    node,
+                                                    SHORTCUT_ARC,
+                                                    FORWARD_DIRECTION_DISABLED,
                                                     REVERSE_DIRECTION_ENABLED);
                     }
                 }
diff --git a/include/engine/api/base_api.hpp b/include/engine/api/base_api.hpp
index 8cac72b..1141694 100644
--- a/include/engine/api/base_api.hpp
+++ b/include/engine/api/base_api.hpp
@@ -37,11 +37,10 @@ class BaseAPI
         waypoints.values[0] = MakeWaypoint(segment_end_coordinates.front().source_phantom);
 
         auto out_iter = std::next(waypoints.values.begin());
-        boost::range::transform(segment_end_coordinates, out_iter,
-                                [this](const PhantomNodes &phantom_pair)
-                                {
-                                    return MakeWaypoint(phantom_pair.target_phantom);
-                                });
+        boost::range::transform(
+            segment_end_coordinates, out_iter, [this](const PhantomNodes &phantom_pair) {
+                return MakeWaypoint(phantom_pair.target_phantom);
+            });
         return waypoints;
     }
 
@@ -49,7 +48,8 @@ class BaseAPI
     //  protected:
     util::json::Object MakeWaypoint(const PhantomNode &phantom) const
     {
-        return json::makeWaypoint(phantom.location, facade.GetNameForID(phantom.name_id),
+        return json::makeWaypoint(phantom.location,
+                                  facade.GetNameForID(phantom.name_id),
                                   Hint{phantom, facade.GetCheckSum()});
     }
 
diff --git a/include/engine/api/base_parameters.hpp b/include/engine/api/base_parameters.hpp
index 63239b7..303f472 100644
--- a/include/engine/api/base_parameters.hpp
+++ b/include/engine/api/base_parameters.hpp
@@ -28,14 +28,14 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #ifndef ENGINE_API_BASE_PARAMETERS_HPP
 #define ENGINE_API_BASE_PARAMETERS_HPP
 
-#include "engine/hint.hpp"
 #include "engine/bearing.hpp"
+#include "engine/hint.hpp"
 #include "util/coordinate.hpp"
 
 #include <boost/optional.hpp>
 
-#include <vector>
 #include <algorithm>
+#include <vector>
 
 namespace osrm
 {
@@ -72,9 +72,9 @@ struct BaseParameters
         return (hints.empty() || hints.size() == coordinates.size()) &&
                (bearings.empty() || bearings.size() == coordinates.size()) &&
                (radiuses.empty() || radiuses.size() == coordinates.size()) &&
-               std::all_of(bearings.begin(), bearings.end(),
-                           [](const boost::optional<Bearing> bearing_and_range)
-                           {
+               std::all_of(bearings.begin(),
+                           bearings.end(),
+                           [](const boost::optional<Bearing> bearing_and_range) {
                                if (bearing_and_range)
                                {
                                    return bearing_and_range->IsValid();
diff --git a/include/engine/api/json_factory.hpp b/include/engine/api/json_factory.hpp
index 398fb68..58a2686 100644
--- a/include/engine/api/json_factory.hpp
+++ b/include/engine/api/json_factory.hpp
@@ -3,12 +3,12 @@
 
 #include "extractor/guidance/turn_instruction.hpp"
 #include "extractor/travel_mode.hpp"
-#include "engine/polyline_compressor.hpp"
+#include "engine/guidance/leg_geometry.hpp"
+#include "engine/guidance/route.hpp"
+#include "engine/guidance/route_leg.hpp"
 #include "engine/guidance/route_step.hpp"
 #include "engine/guidance/step_maneuver.hpp"
-#include "engine/guidance/route_leg.hpp"
-#include "engine/guidance/route.hpp"
-#include "engine/guidance/leg_geometry.hpp"
+#include "engine/polyline_compressor.hpp"
 #include "util/coordinate.hpp"
 #include "util/json_container.hpp"
 
@@ -33,8 +33,8 @@ namespace json
 namespace detail
 {
 
-std::string instructionTypeToString(extractor::guidance::TurnType type);
-std::string instructionModifierToString(extractor::guidance::DirectionModifier modifier);
+std::string instructionTypeToString(extractor::guidance::TurnType::Enum type);
+std::string instructionModifierToString(extractor::guidance::DirectionModifier::Enum modifier);
 
 util::json::Array coordinateToLonLat(const util::Coordinate coordinate);
 
@@ -57,7 +57,9 @@ util::json::Object makeGeoJSONGeometry(ForwardIter begin, ForwardIter end)
     {
         geojson.values["type"] = "LineString";
         util::json::Array coordinates;
-        std::transform(begin, end, std::back_inserter(coordinates.values), &detail::coordinateToLonLat);
+        coordinates.values.reserve(num_coordinates);
+        std::transform(
+            begin, end, std::back_inserter(coordinates.values), &detail::coordinateToLonLat);
         geojson.values["coordinates"] = std::move(coordinates);
     }
     else if (num_coordinates > 0)
@@ -85,7 +87,8 @@ makeWaypoint(const util::Coordinate location, std::string name, const Hint &hint
 util::json::Object makeRouteLeg(guidance::RouteLeg leg, util::json::Array steps);
 
 util::json::Array makeRouteLegs(std::vector<guidance::RouteLeg> legs,
-                                std::vector<util::json::Value> step_geometries);
+                                std::vector<util::json::Value> step_geometries,
+                                std::vector<util::json::Object> annotations);
 }
 }
 } // namespace engine
diff --git a/include/engine/api/match_api.hpp b/include/engine/api/match_api.hpp
index 46b26ae..c931509 100644
--- a/include/engine/api/match_api.hpp
+++ b/include/engine/api/match_api.hpp
@@ -1,8 +1,8 @@
 #ifndef ENGINE_API_MATCH_HPP
 #define ENGINE_API_MATCH_HPP
 
-#include "engine/api/route_api.hpp"
 #include "engine/api/match_parameters.hpp"
+#include "engine/api/route_api.hpp"
 
 #include "engine/datafacade/datafacade_base.hpp"
 
diff --git a/include/engine/api/match_parameters.hpp b/include/engine/api/match_parameters.hpp
index 0cb4741..87e67eb 100644
--- a/include/engine/api/match_parameters.hpp
+++ b/include/engine/api/match_parameters.hpp
@@ -53,6 +53,7 @@ struct MatchParameters : public RouteParameters
     MatchParameters()
         : RouteParameters(false,
                           false,
+                          false,
                           RouteParameters::GeometriesType::Polyline,
                           RouteParameters::OverviewType::Simplified,
                           {})
diff --git a/include/engine/api/nearest_api.hpp b/include/engine/api/nearest_api.hpp
index ed09f62..c0c2c80 100644
--- a/include/engine/api/nearest_api.hpp
+++ b/include/engine/api/nearest_api.hpp
@@ -34,10 +34,10 @@ class NearestAPI final : public BaseAPI
 
         util::json::Array waypoints;
         waypoints.values.resize(phantom_nodes.front().size());
-        std::transform(phantom_nodes.front().begin(), phantom_nodes.front().end(),
+        std::transform(phantom_nodes.front().begin(),
+                       phantom_nodes.front().end(),
                        waypoints.values.begin(),
-                       [this](const PhantomNodeWithDistance &phantom_with_distance)
-                       {
+                       [this](const PhantomNodeWithDistance &phantom_with_distance) {
                            auto waypoint = MakeWaypoint(phantom_with_distance.phantom_node);
                            waypoint.values["distance"] = phantom_with_distance.distance;
                            return waypoint;
diff --git a/include/engine/api/route_api.hpp b/include/engine/api/route_api.hpp
index b8cbee2..76ff9a7 100644
--- a/include/engine/api/route_api.hpp
+++ b/include/engine/api/route_api.hpp
@@ -42,14 +42,16 @@ class RouteAPI : public BaseAPI
         auto number_of_routes = raw_route.has_alternative() ? 2UL : 1UL;
         util::json::Array routes;
         routes.values.resize(number_of_routes);
-        routes.values[0] =
-            MakeRoute(raw_route.segment_end_coordinates, raw_route.unpacked_path_segments,
-                      raw_route.source_traversed_in_reverse, raw_route.target_traversed_in_reverse);
+        routes.values[0] = MakeRoute(raw_route.segment_end_coordinates,
+                                     raw_route.unpacked_path_segments,
+                                     raw_route.source_traversed_in_reverse,
+                                     raw_route.target_traversed_in_reverse);
         if (raw_route.has_alternative())
         {
             std::vector<std::vector<PathData>> wrapped_leg(1);
             wrapped_leg.front() = std::move(raw_route.unpacked_alternative);
-            routes.values[1] = MakeRoute(raw_route.segment_end_coordinates, wrapped_leg,
+            routes.values[1] = MakeRoute(raw_route.segment_end_coordinates,
+                                         wrapped_leg,
                                          raw_route.alt_source_traversed_in_reverse,
                                          raw_route.alt_target_traversed_in_reverse);
         }
@@ -93,14 +95,23 @@ class RouteAPI : public BaseAPI
 
             auto leg_geometry = guidance::assembleGeometry(
                 BaseAPI::facade, path_data, phantoms.source_phantom, phantoms.target_phantom);
-            auto leg = guidance::assembleLeg(facade, path_data, leg_geometry, phantoms.source_phantom,
-                                             phantoms.target_phantom, reversed_target, parameters.steps);
+            auto leg = guidance::assembleLeg(facade,
+                                             path_data,
+                                             leg_geometry,
+                                             phantoms.source_phantom,
+                                             phantoms.target_phantom,
+                                             reversed_target,
+                                             parameters.steps);
 
             if (parameters.steps)
             {
-                auto steps = guidance::assembleSteps(
-                    BaseAPI::facade, path_data, leg_geometry, phantoms.source_phantom,
-                    phantoms.target_phantom, reversed_source, reversed_target);
+                auto steps = guidance::assembleSteps(BaseAPI::facade,
+                                                     path_data,
+                                                     leg_geometry,
+                                                     phantoms.source_phantom,
+                                                     phantoms.target_phantom,
+                                                     reversed_source,
+                                                     reversed_target);
 
                 /* Perform step-based post-processing.
                  *
@@ -133,7 +144,9 @@ class RouteAPI : public BaseAPI
                 guidance::trimShortSegments(steps, leg_geometry);
                 leg.steps = guidance::postProcess(std::move(steps));
                 leg.steps = guidance::collapseTurns(std::move(leg.steps));
-                leg.steps = guidance::assignRelativeLocations(std::move(leg.steps), leg_geometry,
+                leg.steps = guidance::buildIntersections(std::move(leg.steps));
+                leg.steps = guidance::assignRelativeLocations(std::move(leg.steps),
+                                                              leg_geometry,
                                                               phantoms.source_phantom,
                                                               phantoms.target_phantom);
                 leg_geometry = guidance::resyncGeometry(std::move(leg_geometry), leg.steps);
@@ -160,8 +173,13 @@ class RouteAPI : public BaseAPI
         for (const auto idx : util::irange<std::size_t>(0UL, legs.size()))
         {
             auto &leg_geometry = leg_geometries[idx];
+
+            step_geometries.reserve(step_geometries.size() + legs[idx].steps.size());
+
             std::transform(
-                legs[idx].steps.begin(), legs[idx].steps.end(), std::back_inserter(step_geometries),
+                legs[idx].steps.begin(),
+                legs[idx].steps.end(),
+                std::back_inserter(step_geometries),
                 [this, &leg_geometry](const guidance::RouteStep &step) {
                     if (parameters.geometries == RouteParameters::GeometriesType::Polyline)
                     {
@@ -176,9 +194,48 @@ class RouteAPI : public BaseAPI
                 });
         }
 
-        return json::makeRoute(route,
-                               json::makeRouteLegs(std::move(legs), std::move(step_geometries)),
-                               std::move(json_overview));
+        std::vector<util::json::Object> annotations;
+
+        if (parameters.annotations)
+        {
+            for (const auto idx : util::irange<std::size_t>(0UL, leg_geometries.size()))
+            {
+                util::json::Array durations;
+                util::json::Array distances;
+                util::json::Array nodes;
+                auto &leg_geometry = leg_geometries[idx];
+
+                durations.values.reserve(leg_geometry.annotations.size());
+                distances.values.reserve(leg_geometry.annotations.size());
+                nodes.values.reserve(leg_geometry.osm_node_ids.size());
+
+                std::for_each(
+                    leg_geometry.annotations.begin(),
+                    leg_geometry.annotations.end(),
+                    [this, &durations, &distances](const guidance::LegGeometry::Annotation &step) {
+                        durations.values.push_back(step.duration);
+                        distances.values.push_back(step.distance);
+                    });
+                std::for_each(leg_geometry.osm_node_ids.begin(),
+                              leg_geometry.osm_node_ids.end(),
+                              [this, &nodes](const OSMNodeID &node_id) {
+                                  nodes.values.push_back(static_cast<std::uint64_t>(node_id));
+                              });
+                util::json::Object annotation;
+                annotation.values["distance"] = std::move(distances);
+                annotation.values["duration"] = std::move(durations);
+                annotation.values["nodes"] = std::move(nodes);
+                annotations.push_back(std::move(annotation));
+            }
+        }
+
+        auto result = json::makeRoute(route,
+                                      json::makeRouteLegs(std::move(legs),
+                                                          std::move(step_geometries),
+                                                          std::move(annotations)),
+                                      std::move(json_overview));
+
+        return result;
     }
 
     const RouteParameters ¶meters;
diff --git a/include/engine/api/route_parameters.hpp b/include/engine/api/route_parameters.hpp
index 5904020..bca6e4d 100644
--- a/include/engine/api/route_parameters.hpp
+++ b/include/engine/api/route_parameters.hpp
@@ -72,17 +72,20 @@ struct RouteParameters : public BaseParameters
     template <typename... Args>
     RouteParameters(const bool steps_,
                     const bool alternatives_,
+                    const bool annotations_,
                     const GeometriesType geometries_,
                     const OverviewType overview_,
                     const boost::optional<bool> continue_straight_,
                     Args... args_)
         : BaseParameters{std::forward<Args>(args_)...}, steps{steps_}, alternatives{alternatives_},
-          geometries{geometries_}, overview{overview_}, continue_straight{continue_straight_}
+          annotations{annotations_}, geometries{geometries_}, overview{overview_},
+          continue_straight{continue_straight_}
     {
     }
 
     bool steps = false;
     bool alternatives = false;
+    bool annotations = false;
     GeometriesType geometries = GeometriesType::Polyline;
     OverviewType overview = OverviewType::Simplified;
     boost::optional<bool> continue_straight;
diff --git a/include/engine/api/table_api.hpp b/include/engine/api/table_api.hpp
index 1655ce3..727d335 100644
--- a/include/engine/api/table_api.hpp
+++ b/include/engine/api/table_api.hpp
@@ -2,15 +2,15 @@
 #define ENGINE_API_TABLE_HPP
 
 #include "engine/api/base_api.hpp"
-#include "engine/api/table_parameters.hpp"
 #include "engine/api/json_factory.hpp"
+#include "engine/api/table_parameters.hpp"
 
 #include "engine/datafacade/datafacade_base.hpp"
 
-#include "engine/guidance/assemble_leg.hpp"
-#include "engine/guidance/assemble_route.hpp"
 #include "engine/guidance/assemble_geometry.hpp"
+#include "engine/guidance/assemble_leg.hpp"
 #include "engine/guidance/assemble_overview.hpp"
+#include "engine/guidance/assemble_route.hpp"
 #include "engine/guidance/assemble_steps.hpp"
 
 #include "engine/internal_route_result.hpp"
@@ -78,11 +78,10 @@ class TableAPI final : public BaseAPI
         json_waypoints.values.reserve(phantoms.size());
         BOOST_ASSERT(phantoms.size() == parameters.coordinates.size());
 
-        boost::range::transform(phantoms, std::back_inserter(json_waypoints.values),
-                                [this](const PhantomNode &phantom)
-                                {
-                                    return BaseAPI::MakeWaypoint(phantom);
-                                });
+        boost::range::transform(
+            phantoms,
+            std::back_inserter(json_waypoints.values),
+            [this](const PhantomNode &phantom) { return BaseAPI::MakeWaypoint(phantom); });
         return json_waypoints;
     }
 
@@ -91,9 +90,9 @@ class TableAPI final : public BaseAPI
     {
         util::json::Array json_waypoints;
         json_waypoints.values.reserve(indices.size());
-        boost::range::transform(indices, std::back_inserter(json_waypoints.values),
-                                [this, phantoms](const std::size_t idx)
-                                {
+        boost::range::transform(indices,
+                                std::back_inserter(json_waypoints.values),
+                                [this, phantoms](const std::size_t idx) {
                                     BOOST_ASSERT(idx < phantoms.size());
                                     return BaseAPI::MakeWaypoint(phantoms[idx]);
                                 });
@@ -111,9 +110,10 @@ class TableAPI final : public BaseAPI
             auto row_begin_iterator = values.begin() + (row * number_of_columns);
             auto row_end_iterator = values.begin() + ((row + 1) * number_of_columns);
             json_row.values.resize(number_of_columns);
-            std::transform(row_begin_iterator, row_end_iterator, json_row.values.begin(),
-                           [](const EdgeWeight duration)
-                           {
+            std::transform(row_begin_iterator,
+                           row_end_iterator,
+                           json_row.values.begin(),
+                           [](const EdgeWeight duration) {
                                if (duration == INVALID_EDGE_WEIGHT)
                                {
                                    return util::json::Value(util::json::Null());
diff --git a/include/engine/api/table_parameters.hpp b/include/engine/api/table_parameters.hpp
index d4d3fd3..78f17b1 100644
--- a/include/engine/api/table_parameters.hpp
+++ b/include/engine/api/table_parameters.hpp
@@ -89,10 +89,7 @@ struct TableParameters : public BaseParameters
             return false;
 
         // 3/ 0 <= index < len(locations)
-        const auto not_in_range = [this](const std::size_t x)
-        {
-            return x >= coordinates.size();
-        };
+        const auto not_in_range = [this](const std::size_t x) { return x >= coordinates.size(); };
 
         if (std::any_of(begin(sources), end(sources), not_in_range))
             return false;
diff --git a/include/engine/api/trip_api.hpp b/include/engine/api/trip_api.hpp
index 9ed72ef..8c13d25 100644
--- a/include/engine/api/trip_api.hpp
+++ b/include/engine/api/trip_api.hpp
@@ -79,8 +79,7 @@ class TripAPI final : public RouteAPI
         std::vector<TripIndex> input_idx_to_trip_idx(parameters.coordinates.size());
         for (auto sub_trip_index : util::irange<unsigned>(0u, sub_trips.size()))
         {
-            for (auto point_index :
-                 util::irange<unsigned>(0u, sub_trips[sub_trip_index].size()))
+            for (auto point_index : util::irange<unsigned>(0u, sub_trips[sub_trip_index].size()))
             {
                 input_idx_to_trip_idx[sub_trips[sub_trip_index][point_index]] =
                     TripIndex{sub_trip_index, point_index};
diff --git a/include/engine/base64.hpp b/include/engine/base64.hpp
index 6b234b4..2cb7d3b 100644
--- a/include/engine/base64.hpp
+++ b/include/engine/base64.hpp
@@ -1,18 +1,18 @@
 #ifndef OSRM_BASE64_HPP
 #define OSRM_BASE64_HPP
 
-#include <string>
-#include <vector>
 #include <iterator>
+#include <string>
 #include <type_traits>
+#include <vector>
 
-#include <cstddef>
 #include <climits>
+#include <cstddef>
 
-#include <boost/archive/iterators/binary_from_base64.hpp>
+#include <boost/algorithm/string/trim.hpp>
 #include <boost/archive/iterators/base64_from_binary.hpp>
+#include <boost/archive/iterators/binary_from_base64.hpp>
 #include <boost/archive/iterators/transform_width.hpp>
-#include <boost/algorithm/string/trim.hpp>
 #include <boost/range/algorithm/copy.hpp>
 
 namespace osrm
diff --git a/include/engine/datafacade/datafacade_base.hpp b/include/engine/datafacade/datafacade_base.hpp
index 039b3a9..22b8ad1 100644
--- a/include/engine/datafacade/datafacade_base.hpp
+++ b/include/engine/datafacade/datafacade_base.hpp
@@ -3,13 +3,15 @@
 
 // Exposes all data access interfaces to the algorithms via base class ptr
 
+#include "contractor/query_edge.hpp"
 #include "extractor/edge_based_node.hpp"
 #include "extractor/external_memory_node.hpp"
-#include "contractor/query_edge.hpp"
-#include "engine/phantom_node.hpp"
 #include "extractor/guidance/turn_instruction.hpp"
-#include "util/integer_range.hpp"
+#include "engine/phantom_node.hpp"
 #include "util/exception.hpp"
+#include "util/guidance/bearing_class.hpp"
+#include "util/guidance/entry_class.hpp"
+#include "util/integer_range.hpp"
 #include "util/string_util.hpp"
 #include "util/typedefs.hpp"
 
@@ -17,9 +19,9 @@
 
 #include <cstddef>
 
-#include <vector>
-#include <utility>
 #include <string>
+#include <utility>
+#include <vector>
 
 namespace osrm
 {
@@ -65,6 +67,7 @@ class BaseDataFacade
 
     // node and edge information access
     virtual util::Coordinate GetCoordinateOfNode(const unsigned id) const = 0;
+    virtual OSMNodeID GetOSMNodeIDOfNode(const unsigned id) const = 0;
 
     virtual unsigned GetGeometryIndexForEdgeID(const unsigned id) const = 0;
 
@@ -113,14 +116,15 @@ class BaseDataFacade
                         const int bearing,
                         const int bearing_range) const = 0;
     virtual std::vector<PhantomNodeWithDistance>
-    NearestPhantomNodes(const util::Coordinate input_coordinate, const unsigned max_results) const = 0;
+    NearestPhantomNodes(const util::Coordinate input_coordinate,
+                        const unsigned max_results) const = 0;
     virtual std::vector<PhantomNodeWithDistance>
     NearestPhantomNodes(const util::Coordinate input_coordinate,
                         const unsigned max_results,
                         const double max_distance) const = 0;
 
-    virtual std::pair<PhantomNode, PhantomNode>
-    NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate) const = 0;
+    virtual std::pair<PhantomNode, PhantomNode> NearestPhantomNodeWithAlternativeFromBigComponent(
+        const util::Coordinate input_coordinate) const = 0;
     virtual std::pair<PhantomNode, PhantomNode>
     NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate,
                                                       const double max_distance) const = 0;
@@ -129,8 +133,10 @@ class BaseDataFacade
                                                       const double max_distance,
                                                       const int bearing,
                                                       const int bearing_range) const = 0;
-    virtual std::pair<PhantomNode, PhantomNode> NearestPhantomNodeWithAlternativeFromBigComponent(
-        const util::Coordinate input_coordinate, const int bearing, const int bearing_range) const = 0;
+    virtual std::pair<PhantomNode, PhantomNode>
+    NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate,
+                                                      const int bearing,
+                                                      const int bearing_range) const = 0;
 
     virtual unsigned GetCheckSum() const = 0;
 
@@ -140,11 +146,24 @@ class BaseDataFacade
 
     virtual std::string GetNameForID(const unsigned name_id) const = 0;
 
+    virtual std::string GetPronunciationForID(const unsigned name_id) const = 0;
+
+    virtual std::string GetDestinationsForID(const unsigned name_id) const = 0;
+
     virtual std::size_t GetCoreSize() const = 0;
 
     virtual std::string GetTimestamp() const = 0;
 
     virtual bool GetContinueStraightDefault() const = 0;
+
+    virtual BearingClassID GetBearingClassID(const NodeID id) const = 0;
+
+    virtual util::guidance::BearingClass
+    GetBearingClass(const BearingClassID bearing_class_id) const = 0;
+
+    virtual EntryClassID GetEntryClassID(const EdgeID eid) const = 0;
+
+    virtual util::guidance::EntryClass GetEntryClass(const EntryClassID entry_class_id) const = 0;
 };
 }
 }
diff --git a/include/engine/datafacade/internal_datafacade.hpp b/include/engine/datafacade/internal_datafacade.hpp
index ff9d4ad..476ab17 100644
--- a/include/engine/datafacade/internal_datafacade.hpp
+++ b/include/engine/datafacade/internal_datafacade.hpp
@@ -6,21 +6,25 @@
 #include "engine/datafacade/datafacade_base.hpp"
 
 #include "extractor/guidance/turn_instruction.hpp"
+#include "util/guidance/bearing_class.hpp"
+#include "util/guidance/entry_class.hpp"
 
-#include "storage/storage_config.hpp"
-#include "engine/geospatial_query.hpp"
+#include "extractor/compressed_edge_container.hpp"
 #include "extractor/original_edge_data.hpp"
 #include "extractor/profile_properties.hpp"
 #include "extractor/query_node.hpp"
-#include "contractor/query_edge.hpp"
+#include "storage/storage_config.hpp"
+#include "engine/geospatial_query.hpp"
+#include "util/graph_loader.hpp"
+#include "util/io.hpp"
+#include "util/packed_vector.hpp"
+#include "util/range_table.hpp"
+#include "util/rectangle.hpp"
 #include "util/shared_memory_vector_wrapper.hpp"
+#include "util/simple_logger.hpp"
 #include "util/static_graph.hpp"
 #include "util/static_rtree.hpp"
-#include "util/range_table.hpp"
-#include "util/graph_loader.hpp"
-#include "util/simple_logger.hpp"
-#include "util/rectangle.hpp"
-#include "extractor/compressed_edge_container.hpp"
+#include "util/typedefs.hpp"
 
 #include "osrm/coordinate.hpp"
 
@@ -70,6 +74,7 @@ class InternalDataFacade final : public BaseDataFacade
     std::string m_timestamp;
 
     util::ShM<util::Coordinate, false>::vector m_coordinate_list;
+    util::PackedVector<OSMNodeID, false> m_osmnodeid_list;
     util::ShM<NodeID, false>::vector m_via_node_list;
     util::ShM<unsigned, false>::vector m_name_ID_list;
     util::ShM<extractor::guidance::TurnInstruction, false>::vector m_turn_instruction_list;
@@ -89,6 +94,18 @@ class InternalDataFacade final : public BaseDataFacade
     boost::filesystem::path file_index_path;
     util::RangeTable<16, false> m_name_table;
 
+    // bearing classes by node based node
+    util::ShM<BearingClassID, false>::vector m_bearing_class_id_table;
+    // entry class IDs by edge based egde
+    util::ShM<EntryClassID, false>::vector m_entry_class_id_list;
+    // the look-up table for entry classes. An entry class lists the possibility of entry for all
+    // available turns. For every turn, there is an associated entry class.
+    util::ShM<util::guidance::EntryClass, false>::vector m_entry_class_table;
+    // the look-up table for distinct bearing classes. A bearing class lists the available bearings
+    // at an intersection
+    util::RangeTable<16, false> m_bearing_ranges_table;
+    util::ShM<DiscreteBearing, false>::vector m_bearing_values_table;
+
     void LoadProfileProperties(const boost::filesystem::path &properties_path)
     {
         boost::filesystem::ifstream in_stream(properties_path);
@@ -97,7 +114,8 @@ class InternalDataFacade final : public BaseDataFacade
             throw util::exception("Could not open " + properties_path.string() + " for reading.");
         }
 
-        in_stream.read(reinterpret_cast<char*>(&m_profile_properties), sizeof(m_profile_properties));
+        in_stream.read(reinterpret_cast<char *>(&m_profile_properties),
+                       sizeof(m_profile_properties));
     }
 
     void LoadTimestamp(const boost::filesystem::path &timestamp_path)
@@ -140,10 +158,12 @@ class InternalDataFacade final : public BaseDataFacade
         unsigned number_of_coordinates = 0;
         nodes_input_stream.read((char *)&number_of_coordinates, sizeof(unsigned));
         m_coordinate_list.resize(number_of_coordinates);
+        m_osmnodeid_list.reserve(number_of_coordinates);
         for (unsigned i = 0; i < number_of_coordinates; ++i)
         {
             nodes_input_stream.read((char *)&current_node, sizeof(extractor::QueryNode));
             m_coordinate_list[i] = util::Coordinate(current_node.lon, current_node.lat);
+            m_osmnodeid_list.push_back(current_node.node_id);
             BOOST_ASSERT(m_coordinate_list[i].IsValid());
         }
 
@@ -154,6 +174,7 @@ class InternalDataFacade final : public BaseDataFacade
         m_name_ID_list.resize(number_of_edges);
         m_turn_instruction_list.resize(number_of_edges);
         m_travel_mode_list.resize(number_of_edges);
+        m_entry_class_id_list.resize(number_of_edges);
 
         extractor::OriginalEdgeData current_edge_data;
         for (unsigned i = 0; i < number_of_edges; ++i)
@@ -164,6 +185,7 @@ class InternalDataFacade final : public BaseDataFacade
             m_name_ID_list[i] = current_edge_data.name_id;
             m_turn_instruction_list[i] = current_edge_data.turn_instruction;
             m_travel_mode_list[i] = current_edge_data.travel_mode;
+            m_entry_class_id_list[i] = current_edge_data.entry_classid;
         }
     }
 
@@ -224,7 +246,8 @@ class InternalDataFacade final : public BaseDataFacade
         boost::filesystem::ifstream datasources_stream(datasource_indexes_file, std::ios::binary);
         if (!datasources_stream)
         {
-            throw util::exception("Could not open " + datasource_indexes_file.string() + " for reading!");
+            throw util::exception("Could not open " + datasource_indexes_file.string() +
+                                  " for reading!");
         }
         BOOST_ASSERT(datasources_stream);
 
@@ -241,7 +264,8 @@ class InternalDataFacade final : public BaseDataFacade
         boost::filesystem::ifstream datasourcenames_stream(datasource_names_file, std::ios::binary);
         if (!datasourcenames_stream)
         {
-            throw util::exception("Could not open " + datasource_names_file.string() + " for reading!");
+            throw util::exception("Could not open " + datasource_names_file.string() +
+                                  " for reading!");
         }
         BOOST_ASSERT(datasourcenames_stream);
         std::string name;
@@ -277,6 +301,55 @@ class InternalDataFacade final : public BaseDataFacade
         }
     }
 
+    void LoadIntersectionClasses(const boost::filesystem::path &intersection_class_file)
+    {
+        std::ifstream intersection_stream(intersection_class_file.string(), std::ios::binary);
+        if (!intersection_stream)
+            throw util::exception("Could not open " + intersection_class_file.string() +
+                                  " for reading.");
+
+        if (!util::readAndCheckFingerprint(intersection_stream))
+            throw util::exception("Fingeprint does not match in " +
+                                  intersection_class_file.string());
+
+        {
+            util::SimpleLogger().Write(logINFO) << "Loading Bearing Class IDs";
+            std::vector<BearingClassID> bearing_class_id;
+            if (!util::deserializeVector(intersection_stream, bearing_class_id))
+                throw util::exception("Reading from " + intersection_class_file.string() +
+                                      " failed.");
+
+            m_bearing_class_id_table.resize(bearing_class_id.size());
+            std::copy(
+                bearing_class_id.begin(), bearing_class_id.end(), &m_bearing_class_id_table[0]);
+        }
+        {
+            util::SimpleLogger().Write(logINFO) << "Loading Bearing Classes";
+            // read the range table
+            intersection_stream >> m_bearing_ranges_table;
+            std::vector<util::guidance::BearingClass> bearing_classes;
+            // and the actual bearing values
+            std::uint64_t num_bearings;
+            intersection_stream >> num_bearings;
+            m_bearing_values_table.resize(num_bearings);
+            intersection_stream.read(reinterpret_cast<char *>(&m_bearing_values_table[0]),
+                                     sizeof(m_bearing_values_table[0]) * num_bearings);
+            if (!static_cast<bool>(intersection_stream))
+                throw util::exception("Reading from " + intersection_class_file.string() +
+                                      " failed.");
+        }
+        {
+            util::SimpleLogger().Write(logINFO) << "Loading Entry Classes";
+            std::vector<util::guidance::EntryClass> entry_classes;
+            if (!util::deserializeVector(intersection_stream, entry_classes))
+                throw util::exception("Reading from " + intersection_class_file.string() +
+                                      " failed.");
+
+            m_entry_class_table.resize(entry_classes.size());
+            std::copy(entry_classes.begin(), entry_classes.end(), &m_entry_class_table[0]);
+        }
+    }
+
   public:
     virtual ~InternalDataFacade()
     {
@@ -284,7 +357,7 @@ class InternalDataFacade final : public BaseDataFacade
         m_geospatial_query.reset();
     }
 
-    explicit InternalDataFacade(const storage::StorageConfig& config)
+    explicit InternalDataFacade(const storage::StorageConfig &config)
     {
         ram_index_path = config.ram_index_path;
         file_index_path = config.file_index_path;
@@ -302,8 +375,7 @@ class InternalDataFacade final : public BaseDataFacade
         LoadGeometries(config.geometries_path);
 
         util::SimpleLogger().Write() << "loading datasource info";
-        LoadDatasourceInfo(config.datasource_names_path,
-                           config.datasource_indexes_path);
+        LoadDatasourceInfo(config.datasource_names_path, config.datasource_indexes_path);
 
         util::SimpleLogger().Write() << "loading timestamp";
         LoadTimestamp(config.timestamp_path);
@@ -316,6 +388,9 @@ class InternalDataFacade final : public BaseDataFacade
 
         util::SimpleLogger().Write() << "loading rtree";
         LoadRTree();
+
+        util::SimpleLogger().Write() << "loading intersection class data";
+        LoadIntersectionClasses(config.intersection_class_path);
     }
 
     // search graph access
@@ -367,6 +442,11 @@ class InternalDataFacade final : public BaseDataFacade
         return m_coordinate_list[id];
     }
 
+    OSMNodeID GetOSMNodeIDOfNode(const unsigned id) const override final
+    {
+        return m_osmnodeid_list.at(id);
+    }
+
     extractor::guidance::TurnInstruction
     GetTurnInstructionForEdgeID(const unsigned id) const override final
     {
@@ -382,8 +462,8 @@ class InternalDataFacade final : public BaseDataFacade
                                          const util::Coordinate north_east) const override final
     {
         BOOST_ASSERT(m_geospatial_query.get());
-        const util::RectangleInt2D bbox{south_west.lon, north_east.lon, south_west.lat,
-                                        north_east.lat};
+        const util::RectangleInt2D bbox{
+            south_west.lon, north_east.lon, south_west.lat, north_east.lat};
         return m_geospatial_query->Search(bbox);
     }
 
@@ -404,8 +484,8 @@ class InternalDataFacade final : public BaseDataFacade
     {
         BOOST_ASSERT(m_geospatial_query.get());
 
-        return m_geospatial_query->NearestPhantomNodesInRange(input_coordinate, max_distance,
-                                                              bearing, bearing_range);
+        return m_geospatial_query->NearestPhantomNodesInRange(
+            input_coordinate, max_distance, bearing, bearing_range);
     }
 
     std::vector<PhantomNodeWithDistance>
@@ -435,8 +515,8 @@ class InternalDataFacade final : public BaseDataFacade
     {
         BOOST_ASSERT(m_geospatial_query.get());
 
-        return m_geospatial_query->NearestPhantomNodes(input_coordinate, max_results, bearing,
-                                                       bearing_range);
+        return m_geospatial_query->NearestPhantomNodes(
+            input_coordinate, max_results, bearing, bearing_range);
     }
 
     std::vector<PhantomNodeWithDistance>
@@ -448,13 +528,12 @@ class InternalDataFacade final : public BaseDataFacade
     {
         BOOST_ASSERT(m_geospatial_query.get());
 
-        return m_geospatial_query->NearestPhantomNodes(input_coordinate, max_results, max_distance,
-                                                       bearing, bearing_range);
+        return m_geospatial_query->NearestPhantomNodes(
+            input_coordinate, max_results, max_distance, bearing, bearing_range);
     }
 
-    std::pair<PhantomNode, PhantomNode>
-    NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate,
-                                                      const double max_distance) const override final
+    std::pair<PhantomNode, PhantomNode> NearestPhantomNodeWithAlternativeFromBigComponent(
+        const util::Coordinate input_coordinate, const double max_distance) const override final
     {
         BOOST_ASSERT(m_geospatial_query.get());
 
@@ -515,11 +594,30 @@ class InternalDataFacade final : public BaseDataFacade
         {
             result.resize(range.back() - range.front() + 1);
             std::copy(m_names_char_list.begin() + range.front(),
-                      m_names_char_list.begin() + range.back() + 1, result.begin());
+                      m_names_char_list.begin() + range.back() + 1,
+                      result.begin());
         }
         return result;
     }
 
+    std::string GetPronunciationForID(const unsigned name_id) const override final
+    {
+        // We store the pronunciation after the name and destination of a street.
+        // We do this to get around the street length limit of 255 which would hit
+        // if we concatenate these. Order (see extractor_callbacks):
+        // name (0), destination (1), pronunciation (2)
+        return GetNameForID(name_id + 2);
+    }
+
+    std::string GetDestinationsForID(const unsigned name_id) const override final
+    {
+        // We store the destination after the name of a street.
+        // We do this to get around the street length limit of 255 which would hit
+        // if we concatenate these. Order (see extractor_callbacks):
+        // name (0), destination (1), pronunciation (2)
+        return GetNameForID(name_id + 1);
+    }
+
     virtual unsigned GetGeometryIndexForEdgeID(const unsigned id) const override final
     {
         return m_via_node_list.at(id);
@@ -547,9 +645,9 @@ class InternalDataFacade final : public BaseDataFacade
 
         result_nodes.clear();
         result_nodes.reserve(end - begin);
-        std::for_each(m_geometry_list.begin() + begin, m_geometry_list.begin() + end,
-                      [&](const osrm::extractor::CompressedEdgeContainer::CompressedEdge &edge)
-                      {
+        std::for_each(m_geometry_list.begin() + begin,
+                      m_geometry_list.begin() + end,
+                      [&](const osrm::extractor::CompressedEdgeContainer::CompressedEdge &edge) {
                           result_nodes.emplace_back(edge.node_id);
                       });
     }
@@ -563,9 +661,9 @@ class InternalDataFacade final : public BaseDataFacade
 
         result_weights.clear();
         result_weights.reserve(end - begin);
-        std::for_each(m_geometry_list.begin() + begin, m_geometry_list.begin() + end,
-                      [&](const osrm::extractor::CompressedEdgeContainer::CompressedEdge &edge)
-                      {
+        std::for_each(m_geometry_list.begin() + begin,
+                      m_geometry_list.begin() + end,
+                      [&](const osrm::extractor::CompressedEdgeContainer::CompressedEdge &edge) {
                           result_weights.emplace_back(edge.weight);
                       });
     }
@@ -592,11 +690,10 @@ class InternalDataFacade final : public BaseDataFacade
         }
         else
         {
-            std::for_each(m_datasource_list.begin() + begin, m_datasource_list.begin() + end,
-                          [&](const uint8_t &datasource_id)
-                          {
-                              result_datasources.push_back(datasource_id);
-                          });
+            std::for_each(
+                m_datasource_list.begin() + begin,
+                m_datasource_list.begin() + end,
+                [&](const uint8_t &datasource_id) { result_datasources.push_back(datasource_id); });
         }
     }
 
@@ -609,7 +706,41 @@ class InternalDataFacade final : public BaseDataFacade
 
     std::string GetTimestamp() const override final { return m_timestamp; }
 
-    bool GetContinueStraightDefault() const override final { return m_profile_properties.continue_straight_at_waypoint; }
+    bool GetContinueStraightDefault() const override final
+    {
+        return m_profile_properties.continue_straight_at_waypoint;
+    }
+
+    BearingClassID GetBearingClassID(const NodeID nid) const override final
+    {
+        return m_bearing_class_id_table.at(nid);
+    }
+
+    util::guidance::BearingClass
+    GetBearingClass(const BearingClassID bearing_class_id) const override final
+    {
+        BOOST_ASSERT(bearing_class_id != INVALID_BEARING_CLASSID);
+        auto range = m_bearing_ranges_table.GetRange(bearing_class_id);
+
+        util::guidance::BearingClass result;
+
+        for (auto itr = m_bearing_values_table.begin() + range.front();
+             itr != m_bearing_values_table.begin() + range.back() + 1;
+             ++itr)
+            result.add(*itr);
+
+        return result;
+    }
+
+    EntryClassID GetEntryClassID(const EdgeID eid) const override final
+    {
+        return m_entry_class_id_list.at(eid);
+    }
+
+    util::guidance::EntryClass GetEntryClass(const EntryClassID entry_class_id) const override final
+    {
+        return m_entry_class_table.at(entry_class_id);
+    }
 };
 }
 }
diff --git a/include/engine/datafacade/shared_datafacade.hpp b/include/engine/datafacade/shared_datafacade.hpp
index 97e1ea2..12bce18 100644
--- a/include/engine/datafacade/shared_datafacade.hpp
+++ b/include/engine/datafacade/shared_datafacade.hpp
@@ -3,21 +3,24 @@
 
 // implements all data storage when shared memory _IS_ used
 
-#include "engine/datafacade/datafacade_base.hpp"
 #include "storage/shared_datatype.hpp"
 #include "storage/shared_memory.hpp"
+#include "engine/datafacade/datafacade_base.hpp"
 
+#include "extractor/compressed_edge_container.hpp"
 #include "extractor/guidance/turn_instruction.hpp"
 #include "extractor/profile_properties.hpp"
-#include "extractor/compressed_edge_container.hpp"
+#include "util/guidance/bearing_class.hpp"
+#include "util/guidance/entry_class.hpp"
 
 #include "engine/geospatial_query.hpp"
+#include "util/make_unique.hpp"
 #include "util/range_table.hpp"
+#include "util/rectangle.hpp"
+#include "util/simple_logger.hpp"
 #include "util/static_graph.hpp"
 #include "util/static_rtree.hpp"
-#include "util/make_unique.hpp"
-#include "util/simple_logger.hpp"
-#include "util/rectangle.hpp"
+#include "util/typedefs.hpp"
 
 #include <cstddef>
 
@@ -30,9 +33,9 @@
 #include <vector>
 
 #include <boost/assert.hpp>
-#include <boost/thread/tss.hpp>
-#include <boost/thread/shared_mutex.hpp>
 #include <boost/thread/lock_guard.hpp>
+#include <boost/thread/shared_mutex.hpp>
+#include <boost/thread/tss.hpp>
 
 namespace osrm
 {
@@ -49,7 +52,7 @@ class SharedDataFacade final : public BaseDataFacade
     using QueryGraph = util::StaticGraph<EdgeData, true>;
     using GraphNode = QueryGraph::NodeArrayEntry;
     using GraphEdge = QueryGraph::EdgeArrayEntry;
-    using NameIndexBlock = util::RangeTable<16, true>::BlockT;
+    using IndexBlock = util::RangeTable<16, true>::BlockT;
     using InputEdge = QueryGraph::InputEdge;
     using RTreeLeaf = super::RTreeLeaf;
     using SharedRTree =
@@ -73,6 +76,7 @@ class SharedDataFacade final : public BaseDataFacade
     extractor::ProfileProperties *m_profile_properties;
 
     util::ShM<util::Coordinate, true>::vector m_coordinate_list;
+    util::PackedVector<OSMNodeID, true> m_osmnodeid_list;
     util::ShM<NodeID, true>::vector m_via_node_list;
     util::ShM<unsigned, true>::vector m_name_ID_list;
     util::ShM<extractor::guidance::TurnInstruction, true>::vector m_turn_instruction_list;
@@ -94,6 +98,18 @@ class SharedDataFacade final : public BaseDataFacade
 
     std::shared_ptr<util::RangeTable<16, true>> m_name_table;
 
+    // bearing classes by node based node
+    util::ShM<BearingClassID, true>::vector m_bearing_class_id_table;
+    // entry class IDs
+    util::ShM<EntryClassID, true>::vector m_entry_class_id_list;
+    // the look-up table for entry classes. An entry class lists the possibility of entry for all
+    // available turns. Such a class id is stored with every edge.
+    util::ShM<util::guidance::EntryClass, true>::vector m_entry_class_table;
+    // the look-up table for distinct bearing classes. A bearing class lists the available bearings
+    // at an intersection
+    std::shared_ptr<util::RangeTable<16, true>> m_bearing_ranges_table;
+    util::ShM<DiscreteBearing, true>::vector m_bearing_values_table;
+
     void LoadChecksum()
     {
         m_check_sum = *data_layout->GetBlockPtr<unsigned>(shared_memory,
@@ -123,9 +139,11 @@ class SharedDataFacade final : public BaseDataFacade
 
         auto tree_ptr = data_layout->GetBlockPtr<RTreeNode>(
             shared_memory, storage::SharedDataLayout::R_SEARCH_TREE);
-        m_static_rtree.reset(new SharedRTree(
-            tree_ptr, data_layout->num_entries[storage::SharedDataLayout::R_SEARCH_TREE],
-            file_index_path, m_coordinate_list));
+        m_static_rtree.reset(
+            new SharedRTree(tree_ptr,
+                            data_layout->num_entries[storage::SharedDataLayout::R_SEARCH_TREE],
+                            file_index_path,
+                            m_coordinate_list));
         m_geospatial_query.reset(
             new SharedGeospatialQuery(*m_static_rtree, m_coordinate_list, *this));
     }
@@ -149,9 +167,18 @@ class SharedDataFacade final : public BaseDataFacade
     {
         auto coordinate_list_ptr = data_layout->GetBlockPtr<util::Coordinate>(
             shared_memory, storage::SharedDataLayout::COORDINATE_LIST);
-        m_coordinate_list.reset(coordinate_list_ptr,
+        m_coordinate_list.reset(
+            coordinate_list_ptr,
             data_layout->num_entries[storage::SharedDataLayout::COORDINATE_LIST]);
 
+        auto osmnodeid_list_ptr = data_layout->GetBlockPtr<std::uint64_t>(
+            shared_memory, storage::SharedDataLayout::OSM_NODE_ID_LIST);
+        m_osmnodeid_list.reset(
+            osmnodeid_list_ptr,
+            data_layout->num_entries[storage::SharedDataLayout::OSM_NODE_ID_LIST]);
+        // We (ab)use the number of coordinates here because we know we have the same amount of ids
+        m_osmnodeid_list.set_number_of_entries(data_layout->num_entries[storage::SharedDataLayout::COORDINATE_LIST]);
+
         auto travel_mode_list_ptr = data_layout->GetBlockPtr<extractor::TravelMode>(
             shared_memory, storage::SharedDataLayout::TRAVEL_MODE);
         util::ShM<extractor::TravelMode, true>::vector travel_mode_list(
@@ -171,6 +198,13 @@ class SharedDataFacade final : public BaseDataFacade
         util::ShM<unsigned, true>::vector name_id_list(
             name_id_list_ptr, data_layout->num_entries[storage::SharedDataLayout::NAME_ID_LIST]);
         m_name_ID_list = std::move(name_id_list);
+
+        auto entry_class_id_list_ptr = data_layout->GetBlockPtr<EntryClassID>(
+            shared_memory, storage::SharedDataLayout::ENTRY_CLASSID);
+        typename util::ShM<EntryClassID, true>::vector entry_class_id_list(
+            entry_class_id_list_ptr,
+            data_layout->num_entries[storage::SharedDataLayout::ENTRY_CLASSID]);
+        m_entry_class_id_list = std::move(entry_class_id_list);
     }
 
     void LoadViaNodeList()
@@ -186,11 +220,11 @@ class SharedDataFacade final : public BaseDataFacade
     {
         auto offsets_ptr = data_layout->GetBlockPtr<unsigned>(
             shared_memory, storage::SharedDataLayout::NAME_OFFSETS);
-        auto blocks_ptr = data_layout->GetBlockPtr<NameIndexBlock>(
+        auto blocks_ptr = data_layout->GetBlockPtr<IndexBlock>(
             shared_memory, storage::SharedDataLayout::NAME_BLOCKS);
         util::ShM<unsigned, true>::vector name_offsets(
             offsets_ptr, data_layout->num_entries[storage::SharedDataLayout::NAME_OFFSETS]);
-        util::ShM<NameIndexBlock, true>::vector name_blocks(
+        util::ShM<IndexBlock, true>::vector name_blocks(
             blocks_ptr, data_layout->num_entries[storage::SharedDataLayout::NAME_BLOCKS]);
 
         auto names_list_ptr = data_layout->GetBlockPtr<char>(
@@ -263,6 +297,40 @@ class SharedDataFacade final : public BaseDataFacade
         m_datasource_name_lengths = std::move(datasource_name_lengths);
     }
 
+    void LoadIntersectionClasses()
+    {
+        auto bearing_class_id_ptr = data_layout->GetBlockPtr<BearingClassID>(
+            shared_memory, storage::SharedDataLayout::BEARING_CLASSID);
+        typename util::ShM<BearingClassID, true>::vector bearing_class_id_table(
+            bearing_class_id_ptr,
+            data_layout->num_entries[storage::SharedDataLayout::BEARING_CLASSID]);
+        m_bearing_class_id_table = std::move(bearing_class_id_table);
+
+        auto bearing_class_ptr = data_layout->GetBlockPtr<DiscreteBearing>(
+            shared_memory, storage::SharedDataLayout::BEARING_VALUES);
+        typename util::ShM<DiscreteBearing, true>::vector bearing_class_table(
+            bearing_class_ptr, data_layout->num_entries[storage::SharedDataLayout::BEARING_VALUES]);
+        m_bearing_values_table = std::move(bearing_class_table);
+
+        auto offsets_ptr = data_layout->GetBlockPtr<unsigned>(
+            shared_memory, storage::SharedDataLayout::BEARING_OFFSETS);
+        auto blocks_ptr = data_layout->GetBlockPtr<IndexBlock>(
+            shared_memory, storage::SharedDataLayout::BEARING_BLOCKS);
+        util::ShM<unsigned, true>::vector bearing_offsets(
+            offsets_ptr, data_layout->num_entries[storage::SharedDataLayout::BEARING_OFFSETS]);
+        util::ShM<IndexBlock, true>::vector bearing_blocks(
+            blocks_ptr, data_layout->num_entries[storage::SharedDataLayout::BEARING_BLOCKS]);
+
+        m_bearing_ranges_table = util::make_unique<util::RangeTable<16, true>>(
+            bearing_offsets, bearing_blocks, static_cast<unsigned>(m_bearing_values_table.size()));
+
+        auto entry_class_ptr = data_layout->GetBlockPtr<util::guidance::EntryClass>(
+            shared_memory, storage::SharedDataLayout::ENTRY_CLASS);
+        typename util::ShM<util::guidance::EntryClass, true>::vector entry_class_table(
+            entry_class_ptr, data_layout->num_entries[storage::SharedDataLayout::ENTRY_CLASS]);
+        m_entry_class_table = std::move(entry_class_table);
+    }
+
   public:
     virtual ~SharedDataFacade() {}
 
@@ -276,8 +344,8 @@ class SharedDataFacade final : public BaseDataFacade
                 "No shared memory blocks found, have you forgotten to run osrm-datastore?");
         }
         data_timestamp_ptr = static_cast<storage::SharedDataTimestamp *>(
-            storage::makeSharedMemory(storage::CURRENT_REGIONS,
-                                      sizeof(storage::SharedDataTimestamp), false, false)
+            storage::makeSharedMemory(
+                storage::CURRENT_REGIONS, sizeof(storage::SharedDataTimestamp), false, false)
                 ->Ptr());
         CURRENT_LAYOUT = storage::LAYOUT_NONE;
         CURRENT_DATA = storage::DATA_NONE;
@@ -350,6 +418,7 @@ class SharedDataFacade final : public BaseDataFacade
                 LoadCoreInformation();
                 LoadProfileProperties();
                 LoadRTree();
+                LoadIntersectionClasses();
 
                 util::SimpleLogger().Write() << "number of geometries: "
                                              << m_coordinate_list.size();
@@ -411,6 +480,11 @@ class SharedDataFacade final : public BaseDataFacade
         return m_coordinate_list[id];
     }
 
+    OSMNodeID GetOSMNodeIDOfNode(const unsigned id) const override final
+    {
+        return m_osmnodeid_list.at(id);
+    }
+
     virtual void GetUncompressedGeometry(const EdgeID id,
                                          std::vector<NodeID> &result_nodes) const override final
     {
@@ -419,9 +493,9 @@ class SharedDataFacade final : public BaseDataFacade
 
         result_nodes.clear();
         result_nodes.reserve(end - begin);
-        std::for_each(m_geometry_list.begin() + begin, m_geometry_list.begin() + end,
-                      [&](const osrm::extractor::CompressedEdgeContainer::CompressedEdge &edge)
-                      {
+        std::for_each(m_geometry_list.begin() + begin,
+                      m_geometry_list.begin() + end,
+                      [&](const osrm::extractor::CompressedEdgeContainer::CompressedEdge &edge) {
                           result_nodes.emplace_back(edge.node_id);
                       });
     }
@@ -435,9 +509,9 @@ class SharedDataFacade final : public BaseDataFacade
 
         result_weights.clear();
         result_weights.reserve(end - begin);
-        std::for_each(m_geometry_list.begin() + begin, m_geometry_list.begin() + end,
-                      [&](const osrm::extractor::CompressedEdgeContainer::CompressedEdge &edge)
-                      {
+        std::for_each(m_geometry_list.begin() + begin,
+                      m_geometry_list.begin() + end,
+                      [&](const osrm::extractor::CompressedEdgeContainer::CompressedEdge &edge) {
                           result_weights.emplace_back(edge.weight);
                       });
     }
@@ -462,8 +536,8 @@ class SharedDataFacade final : public BaseDataFacade
                                          const util::Coordinate north_east) const override final
     {
         BOOST_ASSERT(m_geospatial_query.get());
-        const util::RectangleInt2D bbox{south_west.lon, north_east.lon, south_west.lat,
-                                        north_east.lat};
+        const util::RectangleInt2D bbox{
+            south_west.lon, north_east.lon, south_west.lat, north_east.lat};
         return m_geospatial_query->Search(bbox);
     }
 
@@ -484,8 +558,8 @@ class SharedDataFacade final : public BaseDataFacade
     {
         BOOST_ASSERT(m_geospatial_query.get());
 
-        return m_geospatial_query->NearestPhantomNodesInRange(input_coordinate, max_distance,
-                                                              bearing, bearing_range);
+        return m_geospatial_query->NearestPhantomNodesInRange(
+            input_coordinate, max_distance, bearing, bearing_range);
     }
 
     std::vector<PhantomNodeWithDistance>
@@ -515,8 +589,8 @@ class SharedDataFacade final : public BaseDataFacade
     {
         BOOST_ASSERT(m_geospatial_query.get());
 
-        return m_geospatial_query->NearestPhantomNodes(input_coordinate, max_results, bearing,
-                                                       bearing_range);
+        return m_geospatial_query->NearestPhantomNodes(
+            input_coordinate, max_results, bearing, bearing_range);
     }
 
     std::vector<PhantomNodeWithDistance>
@@ -528,8 +602,8 @@ class SharedDataFacade final : public BaseDataFacade
     {
         BOOST_ASSERT(m_geospatial_query.get());
 
-        return m_geospatial_query->NearestPhantomNodes(input_coordinate, max_results, max_distance,
-                                                       bearing, bearing_range);
+        return m_geospatial_query->NearestPhantomNodes(
+            input_coordinate, max_results, max_distance, bearing, bearing_range);
     }
 
     std::pair<PhantomNode, PhantomNode> NearestPhantomNodeWithAlternativeFromBigComponent(
@@ -541,9 +615,8 @@ class SharedDataFacade final : public BaseDataFacade
             input_coordinate);
     }
 
-    std::pair<PhantomNode, PhantomNode>
-    NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate,
-                                                      const double max_distance) const override final
+    std::pair<PhantomNode, PhantomNode> NearestPhantomNodeWithAlternativeFromBigComponent(
+        const util::Coordinate input_coordinate, const double max_distance) const override final
     {
         BOOST_ASSERT(m_geospatial_query.get());
 
@@ -595,11 +668,30 @@ class SharedDataFacade final : public BaseDataFacade
         {
             result.resize(range.back() - range.front() + 1);
             std::copy(m_names_char_list.begin() + range.front(),
-                      m_names_char_list.begin() + range.back() + 1, result.begin());
+                      m_names_char_list.begin() + range.back() + 1,
+                      result.begin());
         }
         return result;
     }
 
+    std::string GetPronunciationForID(const unsigned name_id) const override final
+    {
+        // We store the pronunciation after the name and destination of a street.
+        // We do this to get around the street length limit of 255 which would hit
+        // if we concatenate these. Order (see extractor_callbacks):
+        // name (0), destination (1), pronunciation (2)
+        return GetNameForID(name_id + 2);
+    }
+
+    std::string GetDestinationsForID(const unsigned name_id) const override final
+    {
+        // We store the destination after the name of a street.
+        // We do this to get around the street length limit of 255 which would hit
+        // if we concatenate these. Order (see extractor_callbacks):
+        // name (0), destination (1), pronunciation (2)
+        return GetNameForID(name_id + 1);
+    }
+
     bool IsCoreNode(const NodeID id) const override final
     {
         if (m_is_core_node.size() > 0)
@@ -634,11 +726,10 @@ class SharedDataFacade final : public BaseDataFacade
         }
         else
         {
-            std::for_each(m_datasource_list.begin() + begin, m_datasource_list.begin() + end,
-                          [&](const uint8_t &datasource_id)
-                          {
-                              result_datasources.push_back(datasource_id);
-                          });
+            std::for_each(
+                m_datasource_list.begin() + begin,
+                m_datasource_list.begin() + end,
+                [&](const uint8_t &datasource_id) { result_datasources.push_back(datasource_id); });
         }
     }
 
@@ -663,6 +754,34 @@ class SharedDataFacade final : public BaseDataFacade
     {
         return m_profile_properties->continue_straight_at_waypoint;
     }
+
+    BearingClassID GetBearingClassID(const NodeID id) const override final
+    {
+        return m_bearing_class_id_table.at(id);
+    }
+
+    util::guidance::BearingClass
+    GetBearingClass(const BearingClassID bearing_class_id) const override final
+    {
+        BOOST_ASSERT(bearing_class_id != INVALID_BEARING_CLASSID);
+        auto range = m_bearing_ranges_table->GetRange(bearing_class_id);
+        util::guidance::BearingClass result;
+        for (auto itr = m_bearing_values_table.begin() + range.front();
+             itr != m_bearing_values_table.begin() + range.back() + 1;
+             ++itr)
+            result.add(*itr);
+        return result;
+    }
+
+    EntryClassID GetEntryClassID(const EdgeID eid) const override final
+    {
+        return m_entry_class_id_list.at(eid);
+    }
+
+    util::guidance::EntryClass GetEntryClass(const EntryClassID entry_class_id) const override final
+    {
+        return m_entry_class_table.at(entry_class_id);
+    }
 };
 }
 }
diff --git a/include/engine/douglas_peucker.hpp b/include/engine/douglas_peucker.hpp
index e95d0cc..0f48378 100644
--- a/include/engine/douglas_peucker.hpp
+++ b/include/engine/douglas_peucker.hpp
@@ -3,8 +3,8 @@
 
 #include "util/coordinate.hpp"
 
-#include <vector>
 #include <iterator>
+#include <vector>
 
 namespace osrm
 {
diff --git a/include/engine/engine.hpp b/include/engine/engine.hpp
index 027320b..634ece6 100644
--- a/include/engine/engine.hpp
+++ b/include/engine/engine.hpp
@@ -1,13 +1,13 @@
 #ifndef ENGINE_HPP
 #define ENGINE_HPP
 
-#include "engine/status.hpp"
 #include "storage/shared_barriers.hpp"
+#include "engine/status.hpp"
 #include "util/json_container.hpp"
 
 #include <memory>
-#include <unordered_map>
 #include <string>
+#include <unordered_map>
 
 namespace osrm
 {
diff --git a/include/engine/geospatial_query.hpp b/include/engine/geospatial_query.hpp
index 81678c0..f61dfeb 100644
--- a/include/engine/geospatial_query.hpp
+++ b/include/engine/geospatial_query.hpp
@@ -1,11 +1,11 @@
 #ifndef GEOSPATIAL_QUERY_HPP
 #define GEOSPATIAL_QUERY_HPP
 
-#include "util/coordinate_calculation.hpp"
-#include "util/typedefs.hpp"
 #include "engine/phantom_node.hpp"
 #include "util/bearing.hpp"
+#include "util/coordinate_calculation.hpp"
 #include "util/rectangle.hpp"
+#include "util/typedefs.hpp"
 #include "util/web_mercator.hpp"
 
 #include "osrm/coordinate.hpp"
@@ -30,9 +30,7 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
     using CandidateSegment = typename RTreeT::CandidateSegment;
 
   public:
-    GeospatialQuery(RTreeT &rtree_,
-                    const CoordinateList &coordinates_,
-                    DataFacadeT &datafacade_)
+    GeospatialQuery(RTreeT &rtree_, const CoordinateList &coordinates_, DataFacadeT &datafacade_)
         : rtree(rtree_), coordinates(coordinates_), datafacade(datafacade_)
     {
     }
@@ -45,17 +43,14 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
     // Returns nearest PhantomNodes in the given bearing range within max_distance.
     // Does not filter by small/big component!
     std::vector<PhantomNodeWithDistance>
-    NearestPhantomNodesInRange(const util::Coordinate input_coordinate, const double max_distance) const
+    NearestPhantomNodesInRange(const util::Coordinate input_coordinate,
+                               const double max_distance) const
     {
         auto results =
             rtree.Nearest(input_coordinate,
-                          [](const CandidateSegment &)
-                          {
-                              return std::make_pair(true, true);
-                          },
+                          [](const CandidateSegment &) { return std::make_pair(true, true); },
                           [this, max_distance, input_coordinate](const std::size_t,
-                                                                 const CandidateSegment &segment)
-                          {
+                                                                 const CandidateSegment &segment) {
                               return CheckSegmentDistance(input_coordinate, segment, max_distance);
                           });
 
@@ -72,13 +67,11 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
     {
         auto results = rtree.Nearest(
             input_coordinate,
-            [this, bearing, bearing_range, max_distance](const CandidateSegment &segment)
-            {
+            [this, bearing, bearing_range, max_distance](const CandidateSegment &segment) {
                 return CheckSegmentBearing(segment, bearing, bearing_range);
             },
             [this, max_distance, input_coordinate](const std::size_t,
-                                                   const CandidateSegment &segment)
-            {
+                                                   const CandidateSegment &segment) {
                 return CheckSegmentDistance(input_coordinate, segment, max_distance);
             });
 
@@ -95,12 +88,10 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
     {
         auto results =
             rtree.Nearest(input_coordinate,
-                          [this, bearing, bearing_range](const CandidateSegment &segment)
-                          {
+                          [this, bearing, bearing_range](const CandidateSegment &segment) {
                               return CheckSegmentBearing(segment, bearing, bearing_range);
                           },
-                          [max_results](const std::size_t num_results, const CandidateSegment &)
-                          {
+                          [max_results](const std::size_t num_results, const CandidateSegment &) {
                               return num_results >= max_results;
                           });
 
@@ -119,13 +110,11 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
     {
         auto results =
             rtree.Nearest(input_coordinate,
-                          [this, bearing, bearing_range](const CandidateSegment &segment)
-                          {
+                          [this, bearing, bearing_range](const CandidateSegment &segment) {
                               return CheckSegmentBearing(segment, bearing, bearing_range);
                           },
                           [this, max_distance, max_results, input_coordinate](
-                              const std::size_t num_results, const CandidateSegment &segment)
-                          {
+                              const std::size_t num_results, const CandidateSegment &segment) {
                               return num_results >= max_results ||
                                      CheckSegmentDistance(input_coordinate, segment, max_distance);
                           });
@@ -140,12 +129,8 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
     {
         auto results =
             rtree.Nearest(input_coordinate,
-                          [](const CandidateSegment &)
-                          {
-                              return std::make_pair(true, true);
-                          },
-                          [max_results](const std::size_t num_results, const CandidateSegment &)
-                          {
+                          [](const CandidateSegment &) { return std::make_pair(true, true); },
+                          [max_results](const std::size_t num_results, const CandidateSegment &) {
                               return num_results >= max_results;
                           });
 
@@ -161,13 +146,9 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
     {
         auto results =
             rtree.Nearest(input_coordinate,
-                          [](const CandidateSegment &)
-                          {
-                              return std::make_pair(true, true);
-                          },
+                          [](const CandidateSegment &) { return std::make_pair(true, true); },
                           [this, max_distance, max_results, input_coordinate](
-                              const std::size_t num_results, const CandidateSegment &segment)
-                          {
+                              const std::size_t num_results, const CandidateSegment &segment) {
                               return num_results >= max_results ||
                                      CheckSegmentDistance(input_coordinate, segment, max_distance);
                           });
@@ -185,8 +166,7 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
         bool has_big_component = false;
         auto results = rtree.Nearest(
             input_coordinate,
-            [&has_big_component, &has_small_component](const CandidateSegment &segment)
-            {
+            [&has_big_component, &has_small_component](const CandidateSegment &segment) {
                 auto use_segment = (!has_small_component ||
                                     (!has_big_component && !segment.data.component.is_tiny));
                 auto use_directions = std::make_pair(use_segment, use_segment);
@@ -196,9 +176,8 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
 
                 return use_directions;
             },
-            [this, &has_big_component, max_distance,
-             input_coordinate](const std::size_t num_results, const CandidateSegment &segment)
-            {
+            [this, &has_big_component, max_distance, input_coordinate](
+                const std::size_t num_results, const CandidateSegment &segment) {
                 return (num_results > 0 && has_big_component) ||
                        CheckSegmentDistance(input_coordinate, segment, max_distance);
             });
@@ -222,8 +201,7 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
         bool has_big_component = false;
         auto results = rtree.Nearest(
             input_coordinate,
-            [&has_big_component, &has_small_component](const CandidateSegment &segment)
-            {
+            [&has_big_component, &has_small_component](const CandidateSegment &segment) {
                 auto use_segment = (!has_small_component ||
                                     (!has_big_component && !segment.data.component.is_tiny));
                 auto use_directions = std::make_pair(use_segment, use_segment);
@@ -233,8 +211,7 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
 
                 return use_directions;
             },
-            [&has_big_component](const std::size_t num_results, const CandidateSegment &)
-            {
+            [&has_big_component](const std::size_t num_results, const CandidateSegment &) {
                 return num_results > 0 && has_big_component;
             });
 
@@ -257,9 +234,8 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
         bool has_big_component = false;
         auto results = rtree.Nearest(
             input_coordinate,
-            [this, bearing, bearing_range, &has_big_component,
-             &has_small_component](const CandidateSegment &segment)
-            {
+            [this, bearing, bearing_range, &has_big_component, &has_small_component](
+                const CandidateSegment &segment) {
                 auto use_segment = (!has_small_component ||
                                     (!has_big_component && !segment.data.component.is_tiny));
                 auto use_directions = std::make_pair(use_segment, use_segment);
@@ -276,8 +252,7 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
 
                 return use_directions;
             },
-            [&has_big_component](const std::size_t num_results, const CandidateSegment &)
-            {
+            [&has_big_component](const std::size_t num_results, const CandidateSegment &) {
                 return num_results > 0 && has_big_component;
             });
 
@@ -303,9 +278,8 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
         bool has_big_component = false;
         auto results = rtree.Nearest(
             input_coordinate,
-            [this, bearing, bearing_range, &has_big_component,
-             &has_small_component](const CandidateSegment &segment)
-            {
+            [this, bearing, bearing_range, &has_big_component, &has_small_component](
+                const CandidateSegment &segment) {
                 auto use_segment = (!has_small_component ||
                                     (!has_big_component && !segment.data.component.is_tiny));
                 auto use_directions = std::make_pair(use_segment, use_segment);
@@ -322,9 +296,8 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
 
                 return use_directions;
             },
-            [this, &has_big_component, max_distance,
-             input_coordinate](const std::size_t num_results, const CandidateSegment &segment)
-            {
+            [this, &has_big_component, max_distance, input_coordinate](
+                const std::size_t num_results, const CandidateSegment &segment) {
                 return (num_results > 0 && has_big_component) ||
                        CheckSegmentDistance(input_coordinate, segment, max_distance);
             });
@@ -345,9 +318,10 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
                      const std::vector<EdgeData> &results) const
     {
         std::vector<PhantomNodeWithDistance> distance_and_phantoms(results.size());
-        std::transform(results.begin(), results.end(), distance_and_phantoms.begin(),
-                       [this, &input_coordinate](const EdgeData &data)
-                       {
+        std::transform(results.begin(),
+                       results.end(),
+                       distance_and_phantoms.begin(),
+                       [this, &input_coordinate](const EdgeData &data) {
                            return MakePhantomNode(input_coordinate, data);
                        });
         return distance_and_phantoms;
@@ -359,9 +333,11 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
         util::Coordinate point_on_segment;
         double ratio;
         const auto current_perpendicular_distance =
-            util::coordinate_calculation::perpendicularDistance(
-                coordinates[data.u], coordinates[data.v], input_coordinate,
-                point_on_segment, ratio);
+            util::coordinate_calculation::perpendicularDistance(coordinates[data.u],
+                                                                coordinates[data.v],
+                                                                input_coordinate,
+                                                                point_on_segment,
+                                                                ratio);
 
         // Find the node-based-edge that this belongs to, and directly
         // calculate the forward_weight, forward_offset, reverse_weight, reverse_offset
@@ -390,7 +366,8 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
             BOOST_ASSERT(data.fwd_segment_position < reverse_weight_vector.size());
 
             for (std::size_t i = 0;
-                 i < reverse_weight_vector.size() - data.fwd_segment_position - 1; i++)
+                 i < reverse_weight_vector.size() - data.fwd_segment_position - 1;
+                 i++)
             {
                 reverse_offset += reverse_weight_vector[i];
             }
@@ -408,9 +385,13 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
             reverse_weight *= 1.0 - ratio;
         }
 
-        auto transformed = PhantomNodeWithDistance{PhantomNode{data, forward_weight, forward_offset,
-                                                               reverse_weight, reverse_offset,
-                                                               point_on_segment, input_coordinate},
+        auto transformed = PhantomNodeWithDistance{PhantomNode{data,
+                                                               forward_weight,
+                                                               forward_offset,
+                                                               reverse_weight,
+                                                               reverse_offset,
+                                                               point_on_segment,
+                                                               input_coordinate},
                                                    current_perpendicular_distance};
 
         return transformed;
@@ -449,12 +430,12 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
                                                  : (forward_edge_bearing + 180);
 
         const bool forward_bearing_valid =
-            util::bearing::CheckInBounds(std::round(forward_edge_bearing), filter_bearing,
-                                         filter_bearing_range) &&
+            util::bearing::CheckInBounds(
+                std::round(forward_edge_bearing), filter_bearing, filter_bearing_range) &&
             segment.data.forward_segment_id.enabled;
         const bool backward_bearing_valid =
-            util::bearing::CheckInBounds(std::round(backward_edge_bearing), filter_bearing,
-                                         filter_bearing_range) &&
+            util::bearing::CheckInBounds(
+                std::round(backward_edge_bearing), filter_bearing, filter_bearing_range) &&
             segment.data.reverse_segment_id.enabled;
         return std::make_pair(forward_bearing_valid, backward_bearing_valid);
     }
diff --git a/include/engine/guidance/assemble_geometry.hpp b/include/engine/guidance/assemble_geometry.hpp
index 4fd84cb..1c2b588 100644
--- a/include/engine/guidance/assemble_geometry.hpp
+++ b/include/engine/guidance/assemble_geometry.hpp
@@ -1,18 +1,18 @@
 #ifndef ENGINE_GUIDANCE_ASSEMBLE_GEOMETRY_HPP
 #define ENGINE_GUIDANCE_ASSEMBLE_GEOMETRY_HPP
 
-#include "engine/internal_route_result.hpp"
-#include "engine/phantom_node.hpp"
-#include "engine/guidance/route_step.hpp"
+#include "extractor/guidance/turn_instruction.hpp"
+#include "extractor/travel_mode.hpp"
 #include "engine/guidance/leg_geometry.hpp"
+#include "engine/guidance/route_step.hpp"
 #include "engine/guidance/toolkit.hpp"
-#include "util/coordinate_calculation.hpp"
+#include "engine/internal_route_result.hpp"
+#include "engine/phantom_node.hpp"
 #include "util/coordinate.hpp"
-#include "extractor/guidance/turn_instruction.hpp"
-#include "extractor/travel_mode.hpp"
+#include "util/coordinate_calculation.hpp"
 
-#include <vector>
 #include <utility>
+#include <vector>
 
 namespace osrm
 {
@@ -43,34 +43,57 @@ LegGeometry assembleGeometry(const DataFacadeT &facade,
     geometry.segment_offsets.push_back(0);
     geometry.locations.push_back(source_node.location);
 
+    // Need to get the node ID preceding the source phantom node
+    // TODO: check if this was traversed in reverse?
+    std::vector<NodeID> reverse_geometry;
+    facade.GetUncompressedGeometry(source_node.reverse_packed_geometry_id, reverse_geometry);
+    geometry.osm_node_ids.push_back(facade.GetOSMNodeIDOfNode(
+        reverse_geometry[reverse_geometry.size() - source_node.fwd_segment_position - 1]));
+
+    auto cumulative_distance = 0.;
     auto current_distance = 0.;
     auto prev_coordinate = geometry.locations.front();
     for (const auto &path_point : leg_data)
     {
         auto coordinate = facade.GetCoordinateOfNode(path_point.turn_via_node);
-        current_distance +=
+        current_distance =
             util::coordinate_calculation::haversineDistance(prev_coordinate, coordinate);
+        cumulative_distance += current_distance;
 
         // all changes to this check have to be matched with assemble_steps
         if (path_point.turn_instruction.type != extractor::guidance::TurnType::NoTurn)
         {
-            geometry.segment_distances.push_back(current_distance);
+            geometry.segment_distances.push_back(cumulative_distance);
             geometry.segment_offsets.push_back(geometry.locations.size());
-            current_distance = 0.;
+            cumulative_distance = 0.;
         }
 
         prev_coordinate = coordinate;
+        geometry.annotations.emplace_back(
+            LegGeometry::Annotation{current_distance, path_point.duration_until_turn / 10.});
         geometry.locations.push_back(std::move(coordinate));
+        geometry.osm_node_ids.push_back(facade.GetOSMNodeIDOfNode(path_point.turn_via_node));
     }
-    current_distance +=
+    current_distance =
         util::coordinate_calculation::haversineDistance(prev_coordinate, target_node.location);
+    cumulative_distance += current_distance;
     // segment leading to the target node
-    geometry.segment_distances.push_back(current_distance);
+    geometry.segment_distances.push_back(cumulative_distance);
+    geometry.annotations.emplace_back(
+        LegGeometry::Annotation{current_distance, target_node.forward_weight / 10.});
     geometry.segment_offsets.push_back(geometry.locations.size());
     geometry.locations.push_back(target_node.location);
 
+    // Need to get the node ID following the destination phantom node
+    // TODO: check if this was traversed in reverse??
+    std::vector<NodeID> forward_geometry;
+    facade.GetUncompressedGeometry(target_node.forward_packed_geometry_id, forward_geometry);
+    geometry.osm_node_ids.push_back(
+        facade.GetOSMNodeIDOfNode(forward_geometry[target_node.fwd_segment_position]));
+
     BOOST_ASSERT(geometry.segment_distances.size() == geometry.segment_offsets.size() - 1);
     BOOST_ASSERT(geometry.locations.size() > geometry.segment_distances.size());
+    BOOST_ASSERT(geometry.annotations.size() == geometry.locations.size() - 1);
 
     return geometry;
 }
diff --git a/include/engine/guidance/assemble_leg.hpp b/include/engine/guidance/assemble_leg.hpp
index 864fc26..fe8832d 100644
--- a/include/engine/guidance/assemble_leg.hpp
+++ b/include/engine/guidance/assemble_leg.hpp
@@ -35,11 +35,12 @@ struct NamedSegment
 
 template <std::size_t SegmentNumber>
 
-std::array<std::uint32_t, SegmentNumber> summarizeRoute(const std::vector<PathData> &route_data)
+std::array<std::uint32_t, SegmentNumber> summarizeRoute(const std::vector<PathData> &route_data,
+                                                        const PhantomNode &target_node,
+                                                        const bool target_traversed_in_reverse)
 {
     // merges segments with same name id
-    const auto collapse_segments = [](std::vector<NamedSegment> &segments)
-    {
+    const auto collapse_segments = [](std::vector<NamedSegment> &segments) {
         auto out = segments.begin();
         auto end = segments.end();
 
@@ -69,47 +70,48 @@ std::array<std::uint32_t, SegmentNumber> summarizeRoute(const std::vector<PathDa
     std::vector<NamedSegment> segments(route_data.size());
     std::uint32_t index = 0;
     std::transform(
-        route_data.begin(), route_data.end(), segments.begin(), [&index](const PathData &point)
-        {
+        route_data.begin(), route_data.end(), segments.begin(), [&index](const PathData &point) {
             return NamedSegment{point.duration_until_turn, index++, point.name_id};
         });
+    const auto target_duration =
+        target_traversed_in_reverse ? target_node.reverse_weight : target_node.forward_weight;
+    if (target_duration > 1)
+        segments.push_back({target_duration, index++, target_node.name_id});
     // this makes sure that the segment with the lowest position comes first
-    std::sort(segments.begin(), segments.end(), [](const NamedSegment &lhs, const NamedSegment &rhs)
-              {
-                  return lhs.name_id < rhs.name_id ||
-                         (lhs.name_id == rhs.name_id && lhs.position < rhs.position);
-              });
+    std::sort(
+        segments.begin(), segments.end(), [](const NamedSegment &lhs, const NamedSegment &rhs) {
+            return lhs.name_id < rhs.name_id ||
+                   (lhs.name_id == rhs.name_id && lhs.position < rhs.position);
+        });
     auto new_end = collapse_segments(segments);
     segments.resize(new_end - segments.begin());
 
     // Filter out segments with an empty name (name_id == 0)
-    new_end = std::remove_if(segments.begin(), segments.end(), [](const NamedSegment &segment)
-              {
-                  return segment.name_id == 0;
-              });
+    new_end = std::remove_if(segments.begin(), segments.end(), [](const NamedSegment &segment) {
+        return segment.name_id == 0;
+    });
     segments.resize(new_end - segments.begin());
 
     // sort descending
-    std::sort(segments.begin(), segments.end(), [](const NamedSegment &lhs, const NamedSegment &rhs)
-              {
-                  return lhs.duration > rhs.duration ||
-                         (lhs.duration == rhs.duration && lhs.position < rhs.position);
-              });
+    std::sort(
+        segments.begin(), segments.end(), [](const NamedSegment &lhs, const NamedSegment &rhs) {
+            return lhs.duration > rhs.duration ||
+                   (lhs.duration == rhs.duration && lhs.position < rhs.position);
+        });
 
     // make sure the segments are sorted by position
     segments.resize(std::min(segments.size(), SegmentNumber));
-    std::sort(segments.begin(), segments.end(), [](const NamedSegment &lhs, const NamedSegment &rhs)
-              {
-                  return lhs.position < rhs.position;
-              });
+    std::sort(
+        segments.begin(), segments.end(), [](const NamedSegment &lhs, const NamedSegment &rhs) {
+            return lhs.position < rhs.position;
+        });
 
     std::array<std::uint32_t, SegmentNumber> summary;
     std::fill(summary.begin(), summary.end(), 0);
-    std::transform(segments.begin(), segments.end(), summary.begin(),
-                   [](const NamedSegment &segment)
-                   {
-                       return segment.name_id;
-                   });
+    std::transform(segments.begin(),
+                   segments.end(),
+                   summary.begin(),
+                   [](const NamedSegment &segment) { return segment.name_id; });
     return summary;
 }
 }
@@ -126,9 +128,11 @@ inline RouteLeg assembleLeg(const datafacade::BaseDataFacade &facade,
         (target_traversed_in_reverse ? target_node.reverse_weight : target_node.forward_weight) /
         10.;
 
-    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.,
+    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;
                                     }) /
@@ -166,14 +170,17 @@ inline RouteLeg assembleLeg(const datafacade::BaseDataFacade &facade,
     std::string summary;
     if (needs_summary)
     {
-        auto summary_array = detail::summarizeRoute<detail::MAX_USED_SEGMENTS>(route_data);
+        auto summary_array = detail::summarizeRoute<detail::MAX_USED_SEGMENTS>(
+            route_data, target_node, target_traversed_in_reverse);
+        if (route_data.empty())
+            summary_array[0] = source_node.name_id;
 
         BOOST_ASSERT(detail::MAX_USED_SEGMENTS > 0);
         BOOST_ASSERT(summary_array.begin() != summary_array.end());
-        summary = std::accumulate(std::next(summary_array.begin()), summary_array.end(),
+        summary = std::accumulate(std::next(summary_array.begin()),
+                                  summary_array.end(),
                                   facade.GetNameForID(summary_array.front()),
-                                  [&facade](std::string previous, const std::uint32_t name_id)
-                                  {
+                                  [&facade](std::string previous, const std::uint32_t name_id) {
                                       if (name_id != 0)
                                       {
                                           previous += ", " + facade.GetNameForID(name_id);
diff --git a/include/engine/guidance/assemble_route.hpp b/include/engine/guidance/assemble_route.hpp
index ff1c905..82f4aa4 100644
--- a/include/engine/guidance/assemble_route.hpp
+++ b/include/engine/guidance/assemble_route.hpp
@@ -1,8 +1,8 @@
 #ifndef ENGINE_GUIDANCE_ASSEMBLE_ROUTE_HPP
 #define ENGINE_GUIDANCE_ASSEMBLE_ROUTE_HPP
 
-#include "engine/guidance/route_leg.hpp"
 #include "engine/guidance/route.hpp"
+#include "engine/guidance/route_leg.hpp"
 
 #include <vector>
 
diff --git a/include/engine/guidance/assemble_steps.hpp b/include/engine/guidance/assemble_steps.hpp
index d798f04..815ad03 100644
--- a/include/engine/guidance/assemble_steps.hpp
+++ b/include/engine/guidance/assemble_steps.hpp
@@ -1,17 +1,20 @@
 #ifndef ENGINE_GUIDANCE_ASSEMBLE_STEPS_HPP_
 #define ENGINE_GUIDANCE_ASSEMBLE_STEPS_HPP_
 
+#include "extractor/guidance/turn_instruction.hpp"
+#include "extractor/travel_mode.hpp"
 #include "engine/guidance/leg_geometry.hpp"
 #include "engine/guidance/route_step.hpp"
 #include "engine/guidance/step_maneuver.hpp"
 #include "engine/guidance/toolkit.hpp"
 #include "engine/internal_route_result.hpp"
 #include "engine/phantom_node.hpp"
-#include "extractor/guidance/turn_instruction.hpp"
-#include "extractor/travel_mode.hpp"
 #include "util/bearing.hpp"
 #include "util/coordinate.hpp"
 #include "util/coordinate_calculation.hpp"
+#include "util/guidance/entry_class.hpp"
+#include "util/guidance/toolkit.hpp"
+#include "util/typedefs.hpp"
 
 #include <boost/optional.hpp>
 #include <cstddef>
@@ -25,14 +28,10 @@ namespace guidance
 {
 namespace detail
 {
-StepManeuver stepManeuverFromGeometry(extractor::guidance::TurnInstruction instruction,
-                                      const LegGeometry &leg_geometry,
-                                      const std::size_t segment_index);
-
-StepManeuver stepManeuverFromGeometry(extractor::guidance::TurnInstruction instruction,
-                                      const WaypointType waypoint_type,
-                                      const LegGeometry &leg_geometry);
-
+std::pair<short, short> getDepartBearings(const LegGeometry &leg_geometry);
+std::pair<short, short> getArriveBearings(const LegGeometry &leg_geometry);
+std::pair<short, short> getIntermediateBearings(const LegGeometry &leg_geometry,
+                                                const std::size_t segment_index);
 } // ns detail
 
 template <typename DataFacadeT>
@@ -64,13 +63,22 @@ std::vector<RouteStep> assembleSteps(const DataFacadeT &facade,
     std::size_t segment_index = 0;
     BOOST_ASSERT(leg_geometry.locations.size() >= 2);
 
-    if (leg_data.size() > 0)
-    {
+    auto bearings = detail::getDepartBearings(leg_geometry);
 
-        StepManeuver maneuver = detail::stepManeuverFromGeometry(
-            extractor::guidance::TurnInstruction::NO_TURN(), WaypointType::Depart, leg_geometry);
-        maneuver.location = source_node.location;
+    StepManeuver maneuver{source_node.location,
+                          bearings.first,
+                          bearings.second,
+                          extractor::guidance::TurnInstruction::NO_TURN(),
+                          WaypointType::Depart,
+                          0};
+    Intersection intersection{source_node.location,
+                              std::vector<short>({bearings.second}),
+                              std::vector<bool>({true}),
+                              Intersection::NO_INDEX,
+                              0};
 
+    if (leg_data.size() > 0)
+    {
         // PathData saves the information we need of the segment _before_ the turn,
         // 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
@@ -90,18 +98,56 @@ std::vector<RouteStep> assembleSteps(const DataFacadeT &facade,
             {
                 BOOST_ASSERT(segment_duration >= 0);
                 const auto name = facade.GetNameForID(step_name_id);
+                const auto pronunciation = facade.GetPronunciationForID(step_name_id);
+                const auto destinations = facade.GetDestinationsForID(step_name_id);
                 const auto distance = leg_geometry.segment_distances[segment_index];
-                steps.push_back(RouteStep{step_name_id, name, NO_ROTARY_NAME,
-                                          segment_duration / 10.0, distance, path_point.travel_mode,
-                                          maneuver, leg_geometry.FrontIndex(segment_index),
-                                          leg_geometry.BackIndex(segment_index) + 1});
-                if (leg_data_index + 1 < leg_data.size()){
+
+                steps.push_back(RouteStep{step_name_id,
+                                          std::move(name),
+                                          std::move(pronunciation),
+                                          std::move(destinations),
+                                          NO_ROTARY_NAME,
+                                          segment_duration / 10.0,
+                                          distance,
+                                          path_point.travel_mode,
+                                          maneuver,
+                                          leg_geometry.FrontIndex(segment_index),
+                                          leg_geometry.BackIndex(segment_index) + 1,
+                                          {intersection}});
+
+                if (leg_data_index + 1 < leg_data.size())
+                {
                     step_name_id = leg_data[leg_data_index + 1].name_id;
-                } else {
+                }
+                else
+                {
                     step_name_id = target_node.name_id;
                 }
-                maneuver = detail::stepManeuverFromGeometry(path_point.turn_instruction,
-                                                            leg_geometry, segment_index);
+
+                bearings = detail::getIntermediateBearings(leg_geometry, segment_index);
+                const auto entry_class = facade.GetEntryClass(path_point.entry_classid);
+                const auto bearing_class =
+                    facade.GetBearingClass(facade.GetBearingClassID(path_point.turn_via_node));
+                intersection.in = bearing_class.findMatchingBearing(
+                    util::bearing::reverseBearing(bearings.first));
+                intersection.out = bearing_class.findMatchingBearing(bearings.second);
+                intersection.location = facade.GetCoordinateOfNode(path_point.turn_via_node);
+                intersection.bearings.clear();
+                intersection.bearings.reserve(bearing_class.getAvailableBearings().size());
+                std::copy(bearing_class.getAvailableBearings().begin(),
+                          bearing_class.getAvailableBearings().end(),
+                          std::back_inserter(intersection.bearings));
+                intersection.entry.clear();
+                for (auto idx : util::irange<std::size_t>(0, intersection.bearings.size()))
+                {
+                    intersection.entry.push_back(entry_class.allowsEntry(idx));
+                }
+                maneuver = {intersection.location,
+                            bearings.first,
+                            bearings.second,
+                            path_point.turn_instruction,
+                            WaypointType::None,
+                            0};
                 segment_index++;
                 segment_duration = 0;
             }
@@ -109,10 +155,18 @@ std::vector<RouteStep> assembleSteps(const DataFacadeT &facade,
         const auto distance = leg_geometry.segment_distances[segment_index];
         const int duration = segment_duration + target_duration;
         BOOST_ASSERT(duration >= 0);
-        steps.push_back(RouteStep{step_name_id, facade.GetNameForID(step_name_id),
-                                  NO_ROTARY_NAME, duration / 10., distance, target_mode, maneuver,
+        steps.push_back(RouteStep{step_name_id,
+                                  facade.GetNameForID(step_name_id),
+                                  facade.GetPronunciationForID(step_name_id),
+                                  facade.GetDestinationsForID(step_name_id),
+                                  NO_ROTARY_NAME,
+                                  duration / 10.,
+                                  distance,
+                                  target_mode,
+                                  maneuver,
                                   leg_geometry.FrontIndex(segment_index),
-                                  leg_geometry.BackIndex(segment_index) + 1});
+                                  leg_geometry.BackIndex(segment_index) + 1,
+                                  {intersection}});
     }
     // In this case the source + target are on the same edge segment
     else
@@ -123,29 +177,62 @@ std::vector<RouteStep> assembleSteps(const DataFacadeT &facade,
         // |---| source_duration
         // |---------| target_duration
 
-        StepManeuver maneuver = detail::stepManeuverFromGeometry(
-            extractor::guidance::TurnInstruction::NO_TURN(), WaypointType::Depart, leg_geometry);
         int duration = target_duration - source_duration;
         BOOST_ASSERT(duration >= 0);
 
-        steps.push_back(RouteStep{source_node.name_id, facade.GetNameForID(source_node.name_id),
-                                  NO_ROTARY_NAME, duration / 10.,
-                                  leg_geometry.segment_distances[segment_index], source_mode,
-                                  std::move(maneuver), leg_geometry.FrontIndex(segment_index),
-                                  leg_geometry.BackIndex(segment_index) + 1});
+        steps.push_back(RouteStep{source_node.name_id,
+                                  facade.GetNameForID(source_node.name_id),
+                                  facade.GetPronunciationForID(source_node.name_id),
+                                  facade.GetDestinationsForID(source_node.name_id),
+                                  NO_ROTARY_NAME,
+                                  duration / 10.,
+                                  leg_geometry.segment_distances[segment_index],
+                                  source_mode,
+                                  std::move(maneuver),
+                                  leg_geometry.FrontIndex(segment_index),
+                                  leg_geometry.BackIndex(segment_index) + 1,
+                                  {intersection}});
     }
 
     BOOST_ASSERT(segment_index == number_of_segments - 1);
+    bearings = detail::getArriveBearings(leg_geometry);
     // This step has length zero, the only reason we need it is the target location
-    auto final_maneuver = detail::stepManeuverFromGeometry(
-        extractor::guidance::TurnInstruction::NO_TURN(), WaypointType::Arrive, leg_geometry);
+    maneuver = {intersection.location,
+                bearings.first,
+                bearings.second,
+                extractor::guidance::TurnInstruction::NO_TURN(),
+                WaypointType::Arrive,
+                0};
+    intersection = {
+        target_node.location,
+        std::vector<short>({static_cast<short>(util::bearing::reverseBearing(bearings.first))}),
+        std::vector<bool>({true}),
+        0,
+        Intersection::NO_INDEX};
 
     BOOST_ASSERT(!leg_geometry.locations.empty());
-    steps.push_back(RouteStep{target_node.name_id, facade.GetNameForID(target_node.name_id),
-                              NO_ROTARY_NAME, ZERO_DURATION, ZERO_DISTANCE, target_mode,
-                              final_maneuver, leg_geometry.locations.size() - 1,
-                              leg_geometry.locations.size()});
-
+    steps.push_back(RouteStep{target_node.name_id,
+                              facade.GetNameForID(target_node.name_id),
+                              facade.GetPronunciationForID(target_node.name_id),
+                              facade.GetDestinationsForID(target_node.name_id),
+                              NO_ROTARY_NAME,
+                              ZERO_DURATION,
+                              ZERO_DISTANCE,
+                              target_mode,
+                              std::move(maneuver),
+                              leg_geometry.locations.size() - 1,
+                              leg_geometry.locations.size(),
+                              {intersection}});
+
+    BOOST_ASSERT(steps.front().intersections.size() == 1);
+    BOOST_ASSERT(steps.front().intersections.front().bearings.size() == 1);
+    BOOST_ASSERT(steps.front().intersections.front().entry.size() == 1);
+    BOOST_ASSERT(steps.front().maneuver.waypoint_type == WaypointType::Depart);
+
+    BOOST_ASSERT(steps.back().intersections.size() == 1);
+    BOOST_ASSERT(steps.back().intersections.front().bearings.size() == 1);
+    BOOST_ASSERT(steps.back().intersections.front().entry.size() == 1);
+    BOOST_ASSERT(steps.back().maneuver.waypoint_type == WaypointType::Arrive);
     return steps;
 }
 
diff --git a/include/engine/guidance/leg_geometry.hpp b/include/engine/guidance/leg_geometry.hpp
index 622e40f..72b0e65 100644
--- a/include/engine/guidance/leg_geometry.hpp
+++ b/include/engine/guidance/leg_geometry.hpp
@@ -3,13 +3,14 @@
 
 #include "util/coordinate.hpp"
 #include "util/integer_range.hpp"
+#include "util/typedefs.hpp"
 
 #include <boost/assert.hpp>
 
 #include <cstddef>
 
-#include <vector>
 #include <cstdlib>
+#include <vector>
 
 namespace osrm
 {
@@ -30,6 +31,16 @@ struct LegGeometry
     std::vector<std::size_t> segment_offsets;
     // length of the segment in meters
     std::vector<double> segment_distances;
+    // original OSM node IDs for each coordinate
+    std::vector<OSMNodeID> osm_node_ids;
+
+    // Per-coordinate metadata
+    struct Annotation
+    {
+        double distance;
+        double duration;
+    };
+    std::vector<Annotation> annotations;
 
     std::size_t FrontIndex(std::size_t segment_index) const
     {
diff --git a/include/engine/guidance/post_processing.hpp b/include/engine/guidance/post_processing.hpp
index 790d5af..16d383d 100644
--- a/include/engine/guidance/post_processing.hpp
+++ b/include/engine/guidance/post_processing.hpp
@@ -1,9 +1,9 @@
 #ifndef ENGINE_GUIDANCE_POST_PROCESSING_HPP
 #define ENGINE_GUIDANCE_POST_PROCESSING_HPP
 
-#include "engine/phantom_node.hpp"
-#include "engine/guidance/route_step.hpp"
 #include "engine/guidance/leg_geometry.hpp"
+#include "engine/guidance/route_step.hpp"
+#include "engine/phantom_node.hpp"
 
 #include <vector>
 
@@ -37,7 +37,10 @@ std::vector<RouteStep> assignRelativeLocations(std::vector<RouteStep> steps,
                                                const PhantomNode &source_node,
                                                const PhantomNode &target_node);
 
-//remove steps invalidated by post-processing
+// collapse suppressed instructions remaining into intersections array
+std::vector<RouteStep> buildIntersections(std::vector<RouteStep> steps);
+
+// remove steps invalidated by post-processing
 std::vector<RouteStep> removeNoTurnInstructions(std::vector<RouteStep> steps);
 
 // postProcess will break the connection between the leg geometry
diff --git a/include/engine/guidance/route_step.hpp b/include/engine/guidance/route_step.hpp
index a527f85..1687698 100644
--- a/include/engine/guidance/route_step.hpp
+++ b/include/engine/guidance/route_step.hpp
@@ -1,8 +1,11 @@
 #ifndef ROUTE_STEP_HPP
 #define ROUTE_STEP_HPP
 
-#include "engine/guidance/step_maneuver.hpp"
 #include "extractor/travel_mode.hpp"
+#include "engine/guidance/step_maneuver.hpp"
+#include "util/coordinate.hpp"
+#include "util/guidance/bearing_class.hpp"
+#include "util/guidance/entry_class.hpp"
 
 #include <cstddef>
 
@@ -21,10 +24,33 @@ namespace guidance
 // Notable exceptions are Departure and Arrival steps.
 // Departue: s --> a --> b. Represents the segment s,a with location being s.
 // Arrive: a --> b --> t. The segment (b,t) is already covered by the previous segment.
+
+// A represenetation of intermediate intersections
+struct Intersection
+{
+    static const constexpr std::size_t NO_INDEX = std::numeric_limits<std::size_t>::max();
+    util::Coordinate location;
+    std::vector<short> bearings;
+    std::vector<bool> entry;
+    std::size_t in;
+    std::size_t out;
+};
+
+inline Intersection getInvalidIntersection()
+{
+    return {util::Coordinate{util::FloatLongitude{0.0}, util::FloatLatitude{0.0}},
+            {},
+            {},
+            Intersection::NO_INDEX,
+            Intersection::NO_INDEX};
+}
+
 struct RouteStep
 {
     unsigned name_id;
     std::string name;
+    std::string pronunciation;
+    std::string destinations;
     std::string rotary_name;
     double duration;
     double distance;
@@ -33,11 +59,23 @@ struct RouteStep
     // indices into the locations array stored the LegGeometry
     std::size_t geometry_begin;
     std::size_t geometry_end;
+    std::vector<Intersection> intersections;
 };
 
 inline RouteStep getInvalidRouteStep()
 {
-    return {0, "", "", 0, 0, TRAVEL_MODE_INACCESSIBLE, getInvalidStepManeuver(), 0, 0};
+    return {0,
+            "",
+            "",
+            "",
+            "",
+            0,
+            0,
+            TRAVEL_MODE_INACCESSIBLE,
+            getInvalidStepManeuver(),
+            0,
+            0,
+            {getInvalidIntersection()}};
 }
 }
 }
diff --git a/include/engine/guidance/step_maneuver.hpp b/include/engine/guidance/step_maneuver.hpp
index 084a999..1c2b55a 100644
--- a/include/engine/guidance/step_maneuver.hpp
+++ b/include/engine/guidance/step_maneuver.hpp
@@ -21,23 +21,14 @@ enum class WaypointType : std::uint8_t
     Depart,
 };
 
-// A represenetation of intermediate intersections
-struct IntermediateIntersection
-{
-    double duration;
-    double distance;
-    util::Coordinate location;
-};
-
 struct StepManeuver
 {
     util::Coordinate location;
-    double bearing_before;
-    double bearing_after;
+    short bearing_before;
+    short bearing_after;
     extractor::guidance::TurnInstruction instruction;
     WaypointType waypoint_type;
     unsigned exit;
-    std::vector<IntermediateIntersection> intersections;
 };
 
 inline StepManeuver getInvalidStepManeuver()
@@ -47,8 +38,7 @@ inline StepManeuver getInvalidStepManeuver()
             0,
             extractor::guidance::TurnInstruction::NO_TURN(),
             WaypointType::None,
-            0,
-            {}};
+            0};
 }
 
 } // namespace guidance
diff --git a/include/engine/guidance/toolkit.hpp b/include/engine/guidance/toolkit.hpp
index f3d534f..0562013 100644
--- a/include/engine/guidance/toolkit.hpp
+++ b/include/engine/guidance/toolkit.hpp
@@ -49,7 +49,7 @@ inline bool staysOnRoundabout(const extractor::guidance::TurnInstruction instruc
     return instruction.type == extractor::guidance::TurnType::StayOnRoundabout;
 }
 
-inline extractor::guidance::DirectionModifier angleToDirectionModifier(const double bearing)
+inline extractor::guidance::DirectionModifier::Enum angleToDirectionModifier(const double bearing)
 {
     if (bearing < 135)
     {
diff --git a/include/engine/hint.hpp b/include/engine/hint.hpp
index c39b03c..a5fd860 100644
--- a/include/engine/hint.hpp
+++ b/include/engine/hint.hpp
@@ -32,9 +32,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #include "util/coordinate.hpp"
 
-#include <string>
 #include <cstdint>
 #include <iosfwd>
+#include <string>
 
 namespace osrm
 {
@@ -65,7 +65,8 @@ struct Hint
 
 static_assert(sizeof(Hint) == 60 + 4, "Hint is bigger than expected");
 constexpr std::size_t ENCODED_HINT_SIZE = 88;
-static_assert(ENCODED_HINT_SIZE / 4 * 3 >= sizeof(Hint), "ENCODED_HINT_SIZE does not match size of Hint");
+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 d1666e1..af58705 100644
--- a/include/engine/internal_route_result.hpp
+++ b/include/engine/internal_route_result.hpp
@@ -1,9 +1,9 @@
 #ifndef RAW_ROUTE_DATA_H
 #define RAW_ROUTE_DATA_H
 
-#include "engine/phantom_node.hpp"
-#include "extractor/travel_mode.hpp"
 #include "extractor/guidance/turn_instruction.hpp"
+#include "extractor/travel_mode.hpp"
+#include "engine/phantom_node.hpp"
 #include "util/typedefs.hpp"
 
 #include "osrm/coordinate.hpp"
@@ -29,6 +29,8 @@ struct PathData
     extractor::guidance::TurnInstruction turn_instruction;
     // travel mode of the street that leads to the turn
     extractor::TravelMode travel_mode : 4;
+    // entry class of the turn, indicating possibility of turns
+    EntryClassID entry_classid;
 };
 
 struct InternalRouteResult
diff --git a/include/engine/map_matching/bayes_classifier.hpp b/include/engine/map_matching/bayes_classifier.hpp
index 733d5e4..70e2cfd 100644
--- a/include/engine/map_matching/bayes_classifier.hpp
+++ b/include/engine/map_matching/bayes_classifier.hpp
@@ -3,8 +3,8 @@
 
 #include <cmath>
 
-#include <vector>
 #include <utility>
+#include <vector>
 
 #include <boost/math/constants/constants.hpp>
 
diff --git a/include/engine/phantom_node.hpp b/include/engine/phantom_node.hpp
index 7f8c29d..df8454a 100644
--- a/include/engine/phantom_node.hpp
+++ b/include/engine/phantom_node.hpp
@@ -149,8 +149,8 @@ struct PhantomNode
     unsigned reverse_packed_geometry_id;
     struct ComponentType
     {
-        std::uint32_t id         : 31;
-        std::uint32_t is_tiny    : 1;
+        std::uint32_t id : 31;
+        std::uint32_t is_tiny : 1;
     } component;
     static_assert(sizeof(ComponentType) == 4, "ComponentType needs to be 4 bytes big");
 
diff --git a/include/engine/plugins/match.hpp b/include/engine/plugins/match.hpp
index 57f556a..3334395 100644
--- a/include/engine/plugins/match.hpp
+++ b/include/engine/plugins/match.hpp
@@ -1,8 +1,8 @@
 #ifndef MATCH_HPP
 #define MATCH_HPP
 
-#include "engine/plugins/plugin_base.hpp"
 #include "engine/api/match_parameters.hpp"
+#include "engine/plugins/plugin_base.hpp"
 
 #include "engine/map_matching/bayes_classifier.hpp"
 #include "engine/routing_algorithms/map_matching.hpp"
diff --git a/include/engine/plugins/nearest.hpp b/include/engine/plugins/nearest.hpp
index 103b789..657aec9 100644
--- a/include/engine/plugins/nearest.hpp
+++ b/include/engine/plugins/nearest.hpp
@@ -1,8 +1,8 @@
 #ifndef NEAREST_HPP
 #define NEAREST_HPP
 
-#include "engine/plugins/plugin_base.hpp"
 #include "engine/api/nearest_parameters.hpp"
+#include "engine/plugins/plugin_base.hpp"
 #include "osrm/json_container.hpp"
 
 namespace osrm
diff --git a/include/engine/plugins/plugin_base.hpp b/include/engine/plugins/plugin_base.hpp
index e030ff8..8b5a870 100644
--- a/include/engine/plugins/plugin_base.hpp
+++ b/include/engine/plugins/plugin_base.hpp
@@ -1,15 +1,15 @@
 #ifndef BASE_PLUGIN_HPP
 #define BASE_PLUGIN_HPP
 
-#include "engine/datafacade/datafacade_base.hpp"
 #include "engine/api/base_parameters.hpp"
+#include "engine/datafacade/datafacade_base.hpp"
 #include "engine/phantom_node.hpp"
 #include "engine/status.hpp"
 
 #include "util/coordinate.hpp"
 #include "util/coordinate_calculation.hpp"
-#include "util/json_container.hpp"
 #include "util/integer_range.hpp"
+#include "util/json_container.hpp"
 
 #include <algorithm>
 #include <iterator>
@@ -31,11 +31,10 @@ class BasePlugin
 
     bool CheckAllCoordinates(const std::vector<util::Coordinate> &coordinates)
     {
-        return !std::any_of(std::begin(coordinates), std::end(coordinates),
-                            [](const util::Coordinate coordinate)
-                            {
-                                return !coordinate.IsValid();
-                            });
+        return !std::any_of(
+            std::begin(coordinates), std::end(coordinates), [](const util::Coordinate coordinate) {
+                return !coordinate.IsValid();
+            });
     }
 
     Status Error(const std::string &code,
@@ -53,43 +52,38 @@ class BasePlugin
     SnapPhantomNodes(const std::vector<PhantomNodePair> &phantom_node_pair_list) const
     {
         const auto check_component_id_is_tiny =
-            [](const std::pair<PhantomNode, PhantomNode> &phantom_pair)
-        {
-            return phantom_pair.first.component.is_tiny;
-        };
+            [](const std::pair<PhantomNode, PhantomNode> &phantom_pair) {
+                return phantom_pair.first.component.is_tiny;
+            };
 
         // are all phantoms from a tiny cc?
         const auto check_all_in_same_component =
-            [](const std::vector<std::pair<PhantomNode, PhantomNode>> &nodes)
-        {
-            const auto component_id = nodes.front().first.component.id;
+            [](const std::vector<std::pair<PhantomNode, PhantomNode>> &nodes) {
+                const auto component_id = nodes.front().first.component.id;
 
-            return std::all_of(std::begin(nodes), std::end(nodes),
-                               [component_id](const PhantomNodePair &phantom_pair)
-                               {
-                                   return component_id == phantom_pair.first.component.id;
-                               });
-        };
+                return std::all_of(std::begin(nodes),
+                                   std::end(nodes),
+                                   [component_id](const PhantomNodePair &phantom_pair) {
+                                       return component_id == phantom_pair.first.component.id;
+                                   });
+            };
 
         const auto fallback_to_big_component =
-            [](const std::pair<PhantomNode, PhantomNode> &phantom_pair)
-        {
-            if (phantom_pair.first.component.is_tiny && phantom_pair.second.IsValid() &&
-                !phantom_pair.second.component.is_tiny)
-            {
-                return phantom_pair.second;
-            }
-            return phantom_pair.first;
-        };
+            [](const std::pair<PhantomNode, PhantomNode> &phantom_pair) {
+                if (phantom_pair.first.component.is_tiny && phantom_pair.second.IsValid() &&
+                    !phantom_pair.second.component.is_tiny)
+                {
+                    return phantom_pair.second;
+                }
+                return phantom_pair.first;
+            };
 
-        const auto use_closed_phantom = [](const std::pair<PhantomNode, PhantomNode> &phantom_pair)
-        {
-            return phantom_pair.first;
-        };
+        const auto use_closed_phantom = [](
+            const std::pair<PhantomNode, PhantomNode> &phantom_pair) { return phantom_pair.first; };
 
-        const bool every_phantom_is_in_tiny_cc =
-            std::all_of(std::begin(phantom_node_pair_list), std::end(phantom_node_pair_list),
-                        check_component_id_is_tiny);
+        const bool every_phantom_is_in_tiny_cc = std::all_of(std::begin(phantom_node_pair_list),
+                                                             std::end(phantom_node_pair_list),
+                                                             check_component_id_is_tiny);
         auto all_in_same_component = check_all_in_same_component(phantom_node_pair_list);
 
         std::vector<PhantomNode> snapped_phantoms;
@@ -99,13 +93,17 @@ class BasePlugin
         // component
         if (every_phantom_is_in_tiny_cc && all_in_same_component)
         {
-            std::transform(phantom_node_pair_list.begin(), phantom_node_pair_list.end(),
-                           std::back_inserter(snapped_phantoms), use_closed_phantom);
+            std::transform(phantom_node_pair_list.begin(),
+                           phantom_node_pair_list.end(),
+                           std::back_inserter(snapped_phantoms),
+                           use_closed_phantom);
         }
         else
         {
-            std::transform(phantom_node_pair_list.begin(), phantom_node_pair_list.end(),
-                           std::back_inserter(snapped_phantoms), fallback_to_big_component);
+            std::transform(phantom_node_pair_list.begin(),
+                           phantom_node_pair_list.end(),
+                           std::back_inserter(snapped_phantoms),
+                           fallback_to_big_component);
         }
 
         return snapped_phantoms;
@@ -137,9 +135,11 @@ class BasePlugin
             }
             if (use_bearings && parameters.bearings[i])
             {
-                phantom_nodes[i] = facade.NearestPhantomNodesInRange(
-                    parameters.coordinates[i], radiuses[i], parameters.bearings[i]->bearing,
-                    parameters.bearings[i]->range);
+                phantom_nodes[i] =
+                    facade.NearestPhantomNodesInRange(parameters.coordinates[i],
+                                                      radiuses[i],
+                                                      parameters.bearings[i]->bearing,
+                                                      parameters.bearings[i]->range);
             }
             else
             {
@@ -179,15 +179,18 @@ class BasePlugin
             {
                 if (use_radiuses && parameters.radiuses[i])
                 {
-                    phantom_nodes[i] = facade.NearestPhantomNodes(
-                        parameters.coordinates[i], number_of_results, *parameters.radiuses[i],
-                        parameters.bearings[i]->bearing, parameters.bearings[i]->range);
+                    phantom_nodes[i] = facade.NearestPhantomNodes(parameters.coordinates[i],
+                                                                  number_of_results,
+                                                                  *parameters.radiuses[i],
+                                                                  parameters.bearings[i]->bearing,
+                                                                  parameters.bearings[i]->range);
                 }
                 else
                 {
-                    phantom_nodes[i] = facade.NearestPhantomNodes(
-                        parameters.coordinates[i], number_of_results,
-                        parameters.bearings[i]->bearing, parameters.bearings[i]->range);
+                    phantom_nodes[i] = facade.NearestPhantomNodes(parameters.coordinates[i],
+                                                                  number_of_results,
+                                                                  parameters.bearings[i]->bearing,
+                                                                  parameters.bearings[i]->range);
                 }
             }
             else
@@ -238,14 +241,17 @@ class BasePlugin
                 {
                     phantom_node_pairs[i] =
                         facade.NearestPhantomNodeWithAlternativeFromBigComponent(
-                            parameters.coordinates[i], *parameters.radiuses[i],
-                            parameters.bearings[i]->bearing, parameters.bearings[i]->range);
+                            parameters.coordinates[i],
+                            *parameters.radiuses[i],
+                            parameters.bearings[i]->bearing,
+                            parameters.bearings[i]->range);
                 }
                 else
                 {
                     phantom_node_pairs[i] =
                         facade.NearestPhantomNodeWithAlternativeFromBigComponent(
-                            parameters.coordinates[i], parameters.bearings[i]->bearing,
+                            parameters.coordinates[i],
+                            parameters.bearings[i]->bearing,
                             parameters.bearings[i]->range);
                 }
             }
diff --git a/include/engine/plugins/tile.hpp b/include/engine/plugins/tile.hpp
index 77ba357..d154eb5 100644
--- a/include/engine/plugins/tile.hpp
+++ b/include/engine/plugins/tile.hpp
@@ -1,8 +1,8 @@
 #ifndef TILEPLUGIN_HPP
 #define TILEPLUGIN_HPP
 
-#include "engine/plugins/plugin_base.hpp"
 #include "engine/api/tile_parameters.hpp"
+#include "engine/plugins/plugin_base.hpp"
 
 #include <string>
 
diff --git a/include/engine/plugins/trip.hpp b/include/engine/plugins/trip.hpp
index 0e97ce7..6135284 100644
--- a/include/engine/plugins/trip.hpp
+++ b/include/engine/plugins/trip.hpp
@@ -4,20 +4,20 @@
 #include "engine/plugins/plugin_base.hpp"
 
 #include "engine/api/trip_parameters.hpp"
-#include "engine/routing_algorithms/shortest_path.hpp"
 #include "engine/routing_algorithms/many_to_many.hpp"
+#include "engine/routing_algorithms/shortest_path.hpp"
 
 #include "osrm/json_container.hpp"
 
 #include <boost/assert.hpp>
 
-#include <cstdlib>
 #include <algorithm>
+#include <cstdlib>
+#include <iterator>
 #include <memory>
 #include <string>
 #include <utility>
 #include <vector>
-#include <iterator>
 
 namespace osrm
 {
diff --git a/include/engine/plugins/viaroute.hpp b/include/engine/plugins/viaroute.hpp
index f73dcc6..cd45b49 100644
--- a/include/engine/plugins/viaroute.hpp
+++ b/include/engine/plugins/viaroute.hpp
@@ -1,14 +1,14 @@
 #ifndef VIA_ROUTE_HPP
 #define VIA_ROUTE_HPP
 
+#include "engine/api/route_api.hpp"
 #include "engine/datafacade/datafacade_base.hpp"
 #include "engine/plugins/plugin_base.hpp"
-#include "engine/api/route_api.hpp"
 
-#include "engine/search_engine_data.hpp"
-#include "engine/routing_algorithms/shortest_path.hpp"
 #include "engine/routing_algorithms/alternative_path.hpp"
 #include "engine/routing_algorithms/direct_shortest_path.hpp"
+#include "engine/routing_algorithms/shortest_path.hpp"
+#include "engine/search_engine_data.hpp"
 #include "util/json_container.hpp"
 
 #include <cstdlib>
diff --git a/include/engine/routing_algorithms/alternative_path.hpp b/include/engine/routing_algorithms/alternative_path.hpp
index 493d62f..df5cfd8 100644
--- a/include/engine/routing_algorithms/alternative_path.hpp
+++ b/include/engine/routing_algorithms/alternative_path.hpp
@@ -130,16 +130,22 @@ class AlternativeRouting final
         {
             if (0 < forward_heap1.Size())
             {
-                AlternativeRoutingStep<true>(forward_heap1, reverse_heap1, &middle_node,
+                AlternativeRoutingStep<true>(forward_heap1,
+                                             reverse_heap1,
+                                             &middle_node,
                                              &upper_bound_to_shortest_path_distance,
-                                             via_node_candidate_list, forward_search_space,
+                                             via_node_candidate_list,
+                                             forward_search_space,
                                              min_edge_offset);
             }
             if (0 < reverse_heap1.Size())
             {
-                AlternativeRoutingStep<false>(forward_heap1, reverse_heap1, &middle_node,
+                AlternativeRoutingStep<false>(forward_heap1,
+                                              reverse_heap1,
+                                              &middle_node,
                                               &upper_bound_to_shortest_path_distance,
-                                              via_node_candidate_list, reverse_search_space,
+                                              via_node_candidate_list,
+                                              reverse_search_space,
                                               min_edge_offset);
             }
         }
@@ -162,18 +168,16 @@ class AlternativeRouting final
         if (path_is_a_loop)
         {
             // Self Loop
-            BOOST_ASSERT(forward_heap1.GetData(middle_node).parent == middle_node &&
-                         reverse_heap1.GetData(middle_node).parent == middle_node);
             packed_forward_path.push_back(middle_node);
             packed_forward_path.push_back(middle_node);
         }
         else
         {
 
-            super::RetrievePackedPathFromSingleHeap(forward_heap1, middle_node,
-                                                    packed_forward_path);
-            super::RetrievePackedPathFromSingleHeap(reverse_heap1, middle_node,
-                                                    packed_reverse_path);
+            super::RetrievePackedPathFromSingleHeap(
+                forward_heap1, middle_node, packed_forward_path);
+            super::RetrievePackedPathFromSingleHeap(
+                reverse_heap1, middle_node, packed_reverse_path);
         }
 
         // this set is is used as an indicator if a node is on the shortest path
@@ -273,8 +277,8 @@ class AlternativeRouting final
         {
             std::reverse(packed_shortest_path.begin(), packed_shortest_path.end());
             packed_shortest_path.emplace_back(middle_node);
-            packed_shortest_path.insert(packed_shortest_path.end(), packed_reverse_path.begin(),
-                                        packed_reverse_path.end());
+            packed_shortest_path.insert(
+                packed_shortest_path.end(), packed_reverse_path.begin(), packed_reverse_path.end());
         }
         std::vector<RankedCandidateNode> ranked_candidates_list;
 
@@ -282,8 +286,11 @@ class AlternativeRouting final
         for (const NodeID node : preselected_node_list)
         {
             int length_of_via_path = 0, sharing_of_via_path = 0;
-            ComputeLengthAndSharingOfViaPath(node, &length_of_via_path, &sharing_of_via_path,
-                                             packed_shortest_path, min_edge_offset);
+            ComputeLengthAndSharingOfViaPath(node,
+                                             &length_of_via_path,
+                                             &sharing_of_via_path,
+                                             packed_shortest_path,
+                                             min_edge_offset);
             const int maximum_allowed_sharing =
                 static_cast<int>(upper_bound_to_shortest_path_distance * VIAPATH_GAMMA);
             if (sharing_of_via_path <= maximum_allowed_sharing &&
@@ -299,10 +306,16 @@ class AlternativeRouting final
         NodeID s_v_middle = SPECIAL_NODEID, v_t_middle = SPECIAL_NODEID;
         for (const RankedCandidateNode &candidate : ranked_candidates_list)
         {
-            if (ViaNodeCandidatePassesTTest(
-                    forward_heap1, reverse_heap1, forward_heap2, reverse_heap2, candidate,
-                    upper_bound_to_shortest_path_distance, &length_of_via_path, &s_v_middle,
-                    &v_t_middle, min_edge_offset))
+            if (ViaNodeCandidatePassesTTest(forward_heap1,
+                                            reverse_heap1,
+                                            forward_heap2,
+                                            reverse_heap2,
+                                            candidate,
+                                            upper_bound_to_shortest_path_distance,
+                                            &length_of_via_path,
+                                            &s_v_middle,
+                                            &v_t_middle,
+                                            min_edge_offset))
             {
                 // select first admissable
                 selected_via_node = candidate.node;
@@ -324,7 +337,8 @@ class AlternativeRouting final
 
             super::UnpackPath(
                 // -- packed input
-                packed_shortest_path.begin(), packed_shortest_path.end(),
+                packed_shortest_path.begin(),
+                packed_shortest_path.end(),
                 // -- start of route
                 phantom_node_pair,
                 // -- unpacked output
@@ -336,8 +350,13 @@ class AlternativeRouting final
         {
             std::vector<NodeID> packed_alternate_path;
             // retrieve alternate path
-            RetrievePackedAlternatePath(forward_heap1, reverse_heap1, forward_heap2, reverse_heap2,
-                                        s_v_middle, v_t_middle, packed_alternate_path);
+            RetrievePackedAlternatePath(forward_heap1,
+                                        reverse_heap1,
+                                        forward_heap2,
+                                        reverse_heap2,
+                                        s_v_middle,
+                                        v_t_middle,
+                                        packed_alternate_path);
 
             raw_route_data.alt_source_traversed_in_reverse.push_back(
                 (packed_alternate_path.front() !=
@@ -347,8 +366,10 @@ class AlternativeRouting final
                  phantom_node_pair.target_phantom.forward_segment_id.id));
 
             // unpack the alternate path
-            super::UnpackPath(packed_alternate_path.begin(), packed_alternate_path.end(),
-                              phantom_node_pair, raw_route_data.unpacked_alternative);
+            super::UnpackPath(packed_alternate_path.begin(),
+                              packed_alternate_path.end(),
+                              phantom_node_pair,
+                              raw_route_data.unpacked_alternative);
 
             raw_route_data.alternative_path_length = length_of_via_path;
         }
@@ -374,8 +395,8 @@ class AlternativeRouting final
         packed_path.pop_back(); // remove middle node. It's in both half-paths
 
         // fetch patched path [v,t]
-        super::RetrievePackedPathFromHeap(forward_heap2, reverse_heap1, v_t_middle,
-                                          packed_v_t_path);
+        super::RetrievePackedPathFromHeap(
+            forward_heap2, reverse_heap1, v_t_middle, packed_v_t_path);
 
         packed_path.insert(packed_path.end(), packed_v_t_path.begin(), packed_v_t_path.end());
     }
@@ -412,9 +433,15 @@ class AlternativeRouting final
         const bool constexpr DO_NOT_FORCE_LOOPS = false;
         while (!new_reverse_heap.Empty())
         {
-            super::RoutingStep(new_reverse_heap, existing_forward_heap, s_v_middle,
-                               upper_bound_s_v_path_length, min_edge_offset, false,
-                               STALLING_ENABLED, DO_NOT_FORCE_LOOPS, DO_NOT_FORCE_LOOPS);
+            super::RoutingStep(new_reverse_heap,
+                               existing_forward_heap,
+                               s_v_middle,
+                               upper_bound_s_v_path_length,
+                               min_edge_offset,
+                               false,
+                               STALLING_ENABLED,
+                               DO_NOT_FORCE_LOOPS,
+                               DO_NOT_FORCE_LOOPS);
         }
         // compute path <v,..,t> by reusing backward search from node t
         NodeID v_t_middle = SPECIAL_NODEID;
@@ -422,9 +449,15 @@ class AlternativeRouting final
         new_forward_heap.Insert(via_node, 0, via_node);
         while (!new_forward_heap.Empty())
         {
-            super::RoutingStep(new_forward_heap, existing_reverse_heap, v_t_middle,
-                               upper_bound_of_v_t_path_length, min_edge_offset, true,
-                               STALLING_ENABLED, DO_NOT_FORCE_LOOPS, DO_NOT_FORCE_LOOPS);
+            super::RoutingStep(new_forward_heap,
+                               existing_reverse_heap,
+                               v_t_middle,
+                               upper_bound_of_v_t_path_length,
+                               min_edge_offset,
+                               true,
+                               STALLING_ENABLED,
+                               DO_NOT_FORCE_LOOPS,
+                               DO_NOT_FORCE_LOOPS);
         }
         *real_length_of_via_path = upper_bound_s_v_path_length + upper_bound_of_v_t_path_length;
 
@@ -434,10 +467,10 @@ class AlternativeRouting final
         }
 
         // retrieve packed paths
-        super::RetrievePackedPathFromHeap(existing_forward_heap, new_reverse_heap, s_v_middle,
-                                          packed_s_v_path);
-        super::RetrievePackedPathFromHeap(new_forward_heap, existing_reverse_heap, v_t_middle,
-                                          packed_v_t_path);
+        super::RetrievePackedPathFromHeap(
+            existing_forward_heap, new_reverse_heap, s_v_middle, packed_s_v_path);
+        super::RetrievePackedPathFromHeap(
+            new_forward_heap, existing_reverse_heap, v_t_middle, packed_v_t_path);
 
         // partial unpacking, compute sharing
         // First partially unpack s-->v until paths deviate, note length of common path.
@@ -503,7 +536,8 @@ class AlternativeRouting final
                 if (packed_v_t_path[via_path_index] == packed_shortest_path[shortest_path_index])
                 {
                     super::UnpackEdge(packed_v_t_path[via_path_index - 1],
-                                      packed_v_t_path[via_path_index], partially_unpacked_via_path);
+                                      packed_v_t_path[via_path_index],
+                                      partially_unpacked_via_path);
                     super::UnpackEdge(packed_shortest_path[shortest_path_index - 1],
                                       packed_shortest_path[shortest_path_index],
                                       partially_unpacked_shortest_path);
@@ -701,9 +735,15 @@ class AlternativeRouting final
         const bool constexpr DO_NOT_FORCE_LOOPS = false;
         while (new_reverse_heap.Size() > 0)
         {
-            super::RoutingStep(new_reverse_heap, existing_forward_heap, *s_v_middle,
-                               upper_bound_s_v_path_length, min_edge_offset, false,
-                               STALLING_ENABLED, DO_NOT_FORCE_LOOPS, DO_NOT_FORCE_LOOPS);
+            super::RoutingStep(new_reverse_heap,
+                               existing_forward_heap,
+                               *s_v_middle,
+                               upper_bound_s_v_path_length,
+                               min_edge_offset,
+                               false,
+                               STALLING_ENABLED,
+                               DO_NOT_FORCE_LOOPS,
+                               DO_NOT_FORCE_LOOPS);
         }
 
         if (INVALID_EDGE_WEIGHT == upper_bound_s_v_path_length)
@@ -717,9 +757,15 @@ class AlternativeRouting final
         new_forward_heap.Insert(candidate.node, 0, candidate.node);
         while (new_forward_heap.Size() > 0)
         {
-            super::RoutingStep(new_forward_heap, existing_reverse_heap, *v_t_middle,
-                               upper_bound_of_v_t_path_length, min_edge_offset, true,
-                               STALLING_ENABLED, DO_NOT_FORCE_LOOPS, DO_NOT_FORCE_LOOPS);
+            super::RoutingStep(new_forward_heap,
+                               existing_reverse_heap,
+                               *v_t_middle,
+                               upper_bound_of_v_t_path_length,
+                               min_edge_offset,
+                               true,
+                               STALLING_ENABLED,
+                               DO_NOT_FORCE_LOOPS,
+                               DO_NOT_FORCE_LOOPS);
         }
 
         if (INVALID_EDGE_WEIGHT == upper_bound_of_v_t_path_length)
@@ -730,11 +776,11 @@ class AlternativeRouting final
         *length_of_via_path = upper_bound_s_v_path_length + upper_bound_of_v_t_path_length;
 
         // retrieve packed paths
-        super::RetrievePackedPathFromHeap(existing_forward_heap, new_reverse_heap, *s_v_middle,
-                                          packed_s_v_path);
+        super::RetrievePackedPathFromHeap(
+            existing_forward_heap, new_reverse_heap, *s_v_middle, packed_s_v_path);
 
-        super::RetrievePackedPathFromHeap(new_forward_heap, existing_reverse_heap, *v_t_middle,
-                                          packed_v_t_path);
+        super::RetrievePackedPathFromHeap(
+            new_forward_heap, existing_reverse_heap, *v_t_middle, packed_v_t_path);
 
         NodeID s_P = *s_v_middle, t_P = *v_t_middle;
         if (SPECIAL_NODEID == s_P)
@@ -814,7 +860,8 @@ class AlternativeRouting final
         // Traverse path s-->v
         BOOST_ASSERT(!packed_v_t_path.empty());
         for (unsigned i = 0, packed_path_length = static_cast<unsigned>(packed_v_t_path.size() - 1);
-             (i < packed_path_length) && unpack_stack.empty(); ++i)
+             (i < packed_path_length) && unpack_stack.empty();
+             ++i)
         {
             const EdgeID edgeID =
                 facade->FindEdgeInEitherDirection(packed_v_t_path[i], packed_v_t_path[i + 1]);
@@ -886,14 +933,26 @@ class AlternativeRouting final
         {
             if (!forward_heap3.Empty())
             {
-                super::RoutingStep(forward_heap3, reverse_heap3, middle, upper_bound,
-                                   min_edge_offset, true, STALLING_ENABLED, DO_NOT_FORCE_LOOPS,
+                super::RoutingStep(forward_heap3,
+                                   reverse_heap3,
+                                   middle,
+                                   upper_bound,
+                                   min_edge_offset,
+                                   true,
+                                   STALLING_ENABLED,
+                                   DO_NOT_FORCE_LOOPS,
                                    DO_NOT_FORCE_LOOPS);
             }
             if (!reverse_heap3.Empty())
             {
-                super::RoutingStep(reverse_heap3, forward_heap3, middle, upper_bound,
-                                   min_edge_offset, false, STALLING_ENABLED, DO_NOT_FORCE_LOOPS,
+                super::RoutingStep(reverse_heap3,
+                                   forward_heap3,
+                                   middle,
+                                   upper_bound,
+                                   min_edge_offset,
+                                   false,
+                                   STALLING_ENABLED,
+                                   DO_NOT_FORCE_LOOPS,
                                    DO_NOT_FORCE_LOOPS);
             }
         }
diff --git a/include/engine/routing_algorithms/direct_shortest_path.hpp b/include/engine/routing_algorithms/direct_shortest_path.hpp
index 970949e..5b18eca 100644
--- a/include/engine/routing_algorithms/direct_shortest_path.hpp
+++ b/include/engine/routing_algorithms/direct_shortest_path.hpp
@@ -102,12 +102,22 @@ class DirectShortestPathRouting final
             forward_core_heap.Clear();
             reverse_core_heap.Clear();
 
-            super::SearchWithCore(forward_heap, reverse_heap, forward_core_heap, reverse_core_heap,
-                                  distance, packed_leg, DO_NOT_FORCE_LOOPS, DO_NOT_FORCE_LOOPS);
+            super::SearchWithCore(forward_heap,
+                                  reverse_heap,
+                                  forward_core_heap,
+                                  reverse_core_heap,
+                                  distance,
+                                  packed_leg,
+                                  DO_NOT_FORCE_LOOPS,
+                                  DO_NOT_FORCE_LOOPS);
         }
         else
         {
-            super::Search(forward_heap, reverse_heap, distance, packed_leg, DO_NOT_FORCE_LOOPS,
+            super::Search(forward_heap,
+                          reverse_heap,
+                          distance,
+                          packed_leg,
+                          DO_NOT_FORCE_LOOPS,
                           DO_NOT_FORCE_LOOPS);
         }
 
@@ -128,7 +138,9 @@ class DirectShortestPathRouting final
         raw_route_data.target_traversed_in_reverse.push_back(
             (packed_leg.back() != phantom_node_pair.target_phantom.forward_segment_id.id));
 
-        super::UnpackPath(packed_leg.begin(), packed_leg.end(), phantom_node_pair,
+        super::UnpackPath(packed_leg.begin(),
+                          packed_leg.end(),
+                          phantom_node_pair,
                           raw_route_data.unpacked_path_segments.front());
     }
 };
diff --git a/include/engine/routing_algorithms/many_to_many.hpp b/include/engine/routing_algorithms/many_to_many.hpp
index e09a91d..6897197 100644
--- a/include/engine/routing_algorithms/many_to_many.hpp
+++ b/include/engine/routing_algorithms/many_to_many.hpp
@@ -66,8 +66,7 @@ class ManyToManyRouting final
         SearchSpaceWithBuckets search_space_with_buckets;
 
         unsigned column_idx = 0;
-        const auto search_target_phantom = [&](const PhantomNode &phantom)
-        {
+        const auto search_target_phantom = [&](const PhantomNode &phantom) {
             query_heap.Clear();
             // insert target(s) at distance 0
 
@@ -94,8 +93,7 @@ class ManyToManyRouting final
 
         // for each source do forward search
         unsigned row_idx = 0;
-        const auto search_source_phantom = [&](const PhantomNode &phantom)
-        {
+        const auto search_source_phantom = [&](const PhantomNode &phantom) {
             query_heap.Clear();
             // insert target(s) at distance 0
 
@@ -115,8 +113,11 @@ class ManyToManyRouting final
             // explore search space
             while (!query_heap.Empty())
             {
-                ForwardRoutingStep(row_idx, number_of_targets, query_heap,
-                                   search_space_with_buckets, result_table);
+                ForwardRoutingStep(row_idx,
+                                   number_of_targets,
+                                   query_heap,
+                                   search_space_with_buckets,
+                                   result_table);
             }
             ++row_idx;
         };
diff --git a/include/engine/routing_algorithms/map_matching.hpp b/include/engine/routing_algorithms/map_matching.hpp
index e64ea4f..343e26a 100644
--- a/include/engine/routing_algorithms/map_matching.hpp
+++ b/include/engine/routing_algorithms/map_matching.hpp
@@ -4,12 +4,12 @@
 #include "engine/routing_algorithms/routing_base.hpp"
 
 #include "engine/map_matching/hidden_markov_model.hpp"
-#include "engine/map_matching/sub_matching.hpp"
 #include "engine/map_matching/matching_confidence.hpp"
+#include "engine/map_matching/sub_matching.hpp"
 
 #include "util/coordinate_calculation.hpp"
-#include "util/json_logger.hpp"
 #include "util/for_each_pair.hpp"
+#include "util/json_logger.hpp"
 
 #include <cstddef>
 
@@ -86,8 +86,7 @@ class MapMatching final : public BasicRoutingInterface<DataFacadeT, MapMatching<
 
         const bool use_timestamps = trace_timestamps.size() > 1;
 
-        const auto median_sample_time = [&]
-        {
+        const auto median_sample_time = [&] {
             if (use_timestamps)
             {
                 return std::max(1u, GetMedianSampleTime(trace_timestamps));
@@ -98,8 +97,7 @@ class MapMatching final : public BasicRoutingInterface<DataFacadeT, MapMatching<
             }
         }();
         const auto max_broken_time = median_sample_time * MAX_BROKEN_STATES;
-        const auto max_distance_delta = [&]
-        {
+        const auto max_distance_delta = [&] {
             if (use_timestamps)
             {
                 return median_sample_time * MAX_SPEED;
@@ -116,10 +114,10 @@ class MapMatching final : public BasicRoutingInterface<DataFacadeT, MapMatching<
             for (auto t = 0UL; t < candidates_list.size(); ++t)
             {
                 emission_log_probabilities[t].resize(candidates_list[t].size());
-                std::transform(candidates_list[t].begin(), candidates_list[t].end(),
+                std::transform(candidates_list[t].begin(),
+                               candidates_list[t].end(),
                                emission_log_probabilities[t].begin(),
-                               [this](const PhantomNodeWithDistance &candidate)
-                               {
+                               [this](const PhantomNodeWithDistance &candidate) {
                                    return default_emission_log_probability(candidate.distance);
                                });
             }
@@ -134,19 +132,19 @@ class MapMatching final : public BasicRoutingInterface<DataFacadeT, MapMatching<
                     map_matching::EmissionLogProbability emission_log_probability(
                         *trace_gps_precision[t]);
                     std::transform(
-                        candidates_list[t].begin(), candidates_list[t].end(),
+                        candidates_list[t].begin(),
+                        candidates_list[t].end(),
                         emission_log_probabilities[t].begin(),
-                        [&emission_log_probability](const PhantomNodeWithDistance &candidate)
-                        {
+                        [&emission_log_probability](const PhantomNodeWithDistance &candidate) {
                             return emission_log_probability(candidate.distance);
                         });
                 }
                 else
                 {
-                    std::transform(candidates_list[t].begin(), candidates_list[t].end(),
+                    std::transform(candidates_list[t].begin(),
+                                   candidates_list[t].end(),
                                    emission_log_probabilities[t].begin(),
-                                   [this](const PhantomNodeWithDistance &candidate)
-                                   {
+                                   [this](const PhantomNodeWithDistance &candidate) {
                                        return default_emission_log_probability(candidate.distance);
                                    });
                 }
@@ -269,14 +267,19 @@ class MapMatching final : public BasicRoutingInterface<DataFacadeT, MapMatching<
                         forward_core_heap.Clear();
                         reverse_core_heap.Clear();
                         network_distance = super::GetNetworkDistanceWithCore(
-                            forward_heap, reverse_heap, forward_core_heap, reverse_core_heap,
+                            forward_heap,
+                            reverse_heap,
+                            forward_core_heap,
+                            reverse_core_heap,
                             prev_unbroken_timestamps_list[s].phantom_node,
-                            current_timestamps_list[s_prime].phantom_node, duration_uppder_bound);
+                            current_timestamps_list[s_prime].phantom_node,
+                            duration_uppder_bound);
                     }
                     else
                     {
                         network_distance = super::GetNetworkDistance(
-                            forward_heap, reverse_heap,
+                            forward_heap,
+                            reverse_heap,
                             prev_unbroken_timestamps_list[s].phantom_node,
                             current_timestamps_list[s_prime].phantom_node);
                     }
@@ -398,10 +401,10 @@ class MapMatching final : public BasicRoutingInterface<DataFacadeT, MapMatching<
                 matching_distance += model.path_distances[timestamp_index][location_index];
             }
             util::for_each_pair(
-                reconstructed_indices, [&trace_distance, &trace_coordinates](
-                                           const std::pair<std::size_t, std::size_t> &prev,
-                                           const std::pair<std::size_t, std::size_t> &curr)
-                {
+                reconstructed_indices,
+                [&trace_distance,
+                 &trace_coordinates](const std::pair<std::size_t, std::size_t> &prev,
+                                     const std::pair<std::size_t, std::size_t> &curr) {
                     trace_distance += util::coordinate_calculation::haversineDistance(
                         trace_coordinates[prev.first], trace_coordinates[curr.first]);
                 });
diff --git a/include/engine/routing_algorithms/routing_base.hpp b/include/engine/routing_algorithms/routing_base.hpp
index deeec64..8181dff 100644
--- a/include/engine/routing_algorithms/routing_base.hpp
+++ b/include/engine/routing_algorithms/routing_base.hpp
@@ -1,10 +1,10 @@
 #ifndef ROUTING_BASE_HPP
 #define ROUTING_BASE_HPP
 
-#include "util/coordinate_calculation.hpp"
+#include "extractor/guidance/turn_instruction.hpp"
 #include "engine/internal_route_result.hpp"
 #include "engine/search_engine_data.hpp"
-#include "extractor/guidance/turn_instruction.hpp"
+#include "util/coordinate_calculation.hpp"
 #include "util/typedefs.hpp"
 
 #include <boost/assert.hpp>
@@ -14,10 +14,10 @@
 
 #include <algorithm>
 #include <iterator>
+#include <numeric>
+#include <stack>
 #include <utility>
 #include <vector>
-#include <stack>
-#include <numeric>
 
 namespace osrm
 {
@@ -226,6 +226,12 @@ template <class DataFacadeT, class Derived> class BasicRoutingInterface
             recursion_stack.emplace(*std::prev(current), *current);
         }
 
+        BOOST_ASSERT(*packed_path_begin == phantom_node_pair.source_phantom.forward_segment_id.id ||
+                     *packed_path_begin == phantom_node_pair.source_phantom.reverse_segment_id.id);
+        BOOST_ASSERT(
+            *std::prev(packed_path_end) == phantom_node_pair.target_phantom.forward_segment_id.id ||
+            *std::prev(packed_path_end) == phantom_node_pair.target_phantom.reverse_segment_id.id);
+
         std::pair<NodeID, NodeID> edge;
         while (!recursion_stack.empty())
         {
@@ -321,9 +327,11 @@ template <class DataFacadeT, class Derived> class BasicRoutingInterface
                                  name_index,
                                  weight_vector[i],
                                  extractor::guidance::TurnInstruction::NO_TURN(),
-                                 travel_mode});
+                                 travel_mode,
+                                 INVALID_ENTRY_CLASSID});
                 }
                 BOOST_ASSERT(unpacked_path.size() > 0);
+                unpacked_path.back().entry_classid = facade->GetEntryClassID(ed.id);
                 unpacked_path.back().turn_instruction = turn_instruction;
                 unpacked_path.back().duration_until_turn += (ed.distance - total_weight);
             }
@@ -376,14 +384,14 @@ template <class DataFacadeT, class Derived> class BasicRoutingInterface
         {
             BOOST_ASSERT(i < id_vector.size());
             BOOST_ASSERT(phantom_node_pair.target_phantom.forward_travel_mode > 0);
-            unpacked_path.push_back(
-                PathData{id_vector[i],
-                         phantom_node_pair.target_phantom.name_id,
-                         weight_vector[i],
-                         extractor::guidance::TurnInstruction::NO_TURN(),
-                         target_traversed_in_reverse
-                             ? phantom_node_pair.target_phantom.backward_travel_mode
-                             : phantom_node_pair.target_phantom.forward_travel_mode});
+            unpacked_path.push_back(PathData{
+                id_vector[i],
+                phantom_node_pair.target_phantom.name_id,
+                weight_vector[i],
+                extractor::guidance::TurnInstruction::NO_TURN(),
+                target_traversed_in_reverse ? phantom_node_pair.target_phantom.backward_travel_mode
+                                            : phantom_node_pair.target_phantom.forward_travel_mode,
+                INVALID_ENTRY_CLASSID});
         }
 
         if (unpacked_path.size() > 0)
@@ -542,13 +550,27 @@ template <class DataFacadeT, class Derived> class BasicRoutingInterface
         {
             if (!forward_heap.Empty())
             {
-                RoutingStep(forward_heap, reverse_heap, middle, distance, min_edge_offset, true,
-                            STALLING_ENABLED, force_loop_forward, force_loop_reverse);
+                RoutingStep(forward_heap,
+                            reverse_heap,
+                            middle,
+                            distance,
+                            min_edge_offset,
+                            true,
+                            STALLING_ENABLED,
+                            force_loop_forward,
+                            force_loop_reverse);
             }
             if (!reverse_heap.Empty())
             {
-                RoutingStep(reverse_heap, forward_heap, middle, distance, min_edge_offset, false,
-                            STALLING_ENABLED, force_loop_reverse, force_loop_forward);
+                RoutingStep(reverse_heap,
+                            forward_heap,
+                            middle,
+                            distance,
+                            min_edge_offset,
+                            false,
+                            STALLING_ENABLED,
+                            force_loop_reverse,
+                            force_loop_forward);
             }
         }
 
@@ -620,8 +642,15 @@ template <class DataFacadeT, class Derived> class BasicRoutingInterface
                 }
                 else
                 {
-                    RoutingStep(forward_heap, reverse_heap, middle, distance, min_edge_offset, true,
-                                STALLING_ENABLED, force_loop_forward, force_loop_reverse);
+                    RoutingStep(forward_heap,
+                                reverse_heap,
+                                middle,
+                                distance,
+                                min_edge_offset,
+                                true,
+                                STALLING_ENABLED,
+                                force_loop_forward,
+                                force_loop_reverse);
                 }
             }
             if (!reverse_heap.Empty())
@@ -634,16 +663,22 @@ template <class DataFacadeT, class Derived> class BasicRoutingInterface
                 }
                 else
                 {
-                    RoutingStep(reverse_heap, forward_heap, middle, distance, min_edge_offset,
-                                false, STALLING_ENABLED, force_loop_reverse, force_loop_forward);
+                    RoutingStep(reverse_heap,
+                                forward_heap,
+                                middle,
+                                distance,
+                                min_edge_offset,
+                                false,
+                                STALLING_ENABLED,
+                                force_loop_reverse,
+                                force_loop_forward);
                 }
             }
         }
         // TODO check if unordered_set might be faster
         // sort by id and increasing by distance
-        auto entry_point_comparator =
-            [](const std::pair<NodeID, EdgeWeight> &lhs, const std::pair<NodeID, EdgeWeight> &rhs)
-        {
+        auto entry_point_comparator = [](const std::pair<NodeID, EdgeWeight> &lhs,
+                                         const std::pair<NodeID, EdgeWeight> &rhs) {
             return lhs.first < rhs.first || (lhs.first == rhs.first && lhs.second < rhs.second);
         };
         std::sort(forward_entry_points.begin(), forward_entry_points.end(), entry_point_comparator);
@@ -689,12 +724,24 @@ template <class DataFacadeT, class Derived> class BasicRoutingInterface
         while (0 < forward_core_heap.Size() && 0 < reverse_core_heap.Size() &&
                distance > (forward_core_heap.MinKey() + reverse_core_heap.MinKey()))
         {
-            RoutingStep(forward_core_heap, reverse_core_heap, middle, distance,
-                        min_core_edge_offset, true, STALLING_DISABLED, force_loop_forward,
+            RoutingStep(forward_core_heap,
+                        reverse_core_heap,
+                        middle,
+                        distance,
+                        min_core_edge_offset,
+                        true,
+                        STALLING_DISABLED,
+                        force_loop_forward,
                         force_loop_reverse);
 
-            RoutingStep(reverse_core_heap, forward_core_heap, middle, distance,
-                        min_core_edge_offset, false, STALLING_DISABLED, force_loop_reverse,
+            RoutingStep(reverse_core_heap,
+                        forward_core_heap,
+                        middle,
+                        distance,
+                        min_core_edge_offset,
+                        false,
+                        STALLING_DISABLED,
+                        force_loop_reverse,
                         force_loop_forward);
         }
 
@@ -723,8 +770,8 @@ template <class DataFacadeT, class Derived> class BasicRoutingInterface
             else
             {
                 std::vector<NodeID> packed_core_leg;
-                RetrievePackedPathFromHeap(forward_core_heap, reverse_core_heap, middle,
-                                           packed_core_leg);
+                RetrievePackedPathFromHeap(
+                    forward_core_heap, reverse_core_heap, middle, packed_core_leg);
                 BOOST_ASSERT(packed_core_leg.size() > 0);
                 RetrievePackedPathFromSingleHeap(forward_heap, packed_core_leg.front(), packed_leg);
                 std::reverse(packed_leg.begin(), packed_leg.end());
@@ -871,8 +918,15 @@ template <class DataFacadeT, class Derived> class BasicRoutingInterface
 
         int duration = INVALID_EDGE_WEIGHT;
         std::vector<NodeID> packed_path;
-        SearchWithCore(forward_heap, reverse_heap, forward_core_heap, reverse_core_heap, duration,
-                       packed_path, DO_NOT_FORCE_LOOPS, DO_NOT_FORCE_LOOPS, duration_upper_bound);
+        SearchWithCore(forward_heap,
+                       reverse_heap,
+                       forward_core_heap,
+                       reverse_core_heap,
+                       duration,
+                       packed_path,
+                       DO_NOT_FORCE_LOOPS,
+                       DO_NOT_FORCE_LOOPS,
+                       duration_upper_bound);
 
         double distance = std::numeric_limits<double>::max();
         if (duration != INVALID_EDGE_WEIGHT)
@@ -925,8 +979,13 @@ template <class DataFacadeT, class Derived> class BasicRoutingInterface
 
         int duration = INVALID_EDGE_WEIGHT;
         std::vector<NodeID> packed_path;
-        Search(forward_heap, reverse_heap, duration, packed_path, DO_NOT_FORCE_LOOPS,
-               DO_NOT_FORCE_LOOPS, duration_upper_bound);
+        Search(forward_heap,
+               reverse_heap,
+               duration,
+               packed_path,
+               DO_NOT_FORCE_LOOPS,
+               DO_NOT_FORCE_LOOPS,
+               duration_upper_bound);
 
         if (duration == INVALID_EDGE_WEIGHT)
         {
diff --git a/include/engine/routing_algorithms/shortest_path.hpp b/include/engine/routing_algorithms/shortest_path.hpp
index cc612a1..5d52dc9 100644
--- a/include/engine/routing_algorithms/shortest_path.hpp
+++ b/include/engine/routing_algorithms/shortest_path.hpp
@@ -57,13 +57,13 @@ class ShortestPathRouting final
         if (search_from_forward_node)
         {
             forward_heap.Insert(source_phantom.forward_segment_id.id,
-                                    -source_phantom.GetForwardWeightPlusOffset(),
+                                -source_phantom.GetForwardWeightPlusOffset(),
                                 source_phantom.forward_segment_id.id);
         }
         if (search_from_reverse_node)
         {
             forward_heap.Insert(source_phantom.reverse_segment_id.id,
-                                    -source_phantom.GetReverseWeightPlusOffset(),
+                                -source_phantom.GetReverseWeightPlusOffset(),
                                 source_phantom.reverse_segment_id.id);
         }
         if (search_to_forward_node)
@@ -96,16 +96,25 @@ class ShortestPathRouting final
             reverse_core_heap.Clear();
             BOOST_ASSERT(forward_core_heap.Size() == 0);
             BOOST_ASSERT(reverse_core_heap.Size() == 0);
-            super::SearchWithCore(forward_heap, reverse_heap, forward_core_heap, reverse_core_heap,
-                                  new_total_distance, leg_packed_path, needs_loop_forwad,
+            super::SearchWithCore(forward_heap,
+                                  reverse_heap,
+                                  forward_core_heap,
+                                  reverse_core_heap,
+                                  new_total_distance,
+                                  leg_packed_path,
+                                  needs_loop_forwad,
                                   needs_loop_backwards);
         }
         else
         {
-            super::Search(forward_heap, reverse_heap, new_total_distance, leg_packed_path,
-                          needs_loop_forwad, needs_loop_backwards);
+            super::Search(forward_heap,
+                          reverse_heap,
+                          new_total_distance,
+                          leg_packed_path,
+                          needs_loop_forwad,
+                          needs_loop_backwards);
         }
-        new_total_distance += std::min(total_distance_to_forward,total_distance_to_reverse);
+        new_total_distance += std::min(total_distance_to_forward, total_distance_to_reverse);
     }
 
     // searches shortest path between:
@@ -159,14 +168,20 @@ class ShortestPathRouting final
                 reverse_core_heap.Clear();
                 BOOST_ASSERT(forward_core_heap.Size() == 0);
                 BOOST_ASSERT(reverse_core_heap.Size() == 0);
-                super::SearchWithCore(
-                    forward_heap, reverse_heap, forward_core_heap, reverse_core_heap,
-                    new_total_distance_to_forward, leg_packed_path_forward,
-                    super::NeedsLoopForward(source_phantom, target_phantom), DO_NOT_FORCE_LOOP);
+                super::SearchWithCore(forward_heap,
+                                      reverse_heap,
+                                      forward_core_heap,
+                                      reverse_core_heap,
+                                      new_total_distance_to_forward,
+                                      leg_packed_path_forward,
+                                      super::NeedsLoopForward(source_phantom, target_phantom),
+                                      DO_NOT_FORCE_LOOP);
             }
             else
             {
-                super::Search(forward_heap, reverse_heap, new_total_distance_to_forward,
+                super::Search(forward_heap,
+                              reverse_heap,
+                              new_total_distance_to_forward,
                               leg_packed_path_forward,
                               super::NeedsLoopForward(source_phantom, target_phantom),
                               DO_NOT_FORCE_LOOP);
@@ -202,15 +217,22 @@ class ShortestPathRouting final
                 reverse_core_heap.Clear();
                 BOOST_ASSERT(forward_core_heap.Size() == 0);
                 BOOST_ASSERT(reverse_core_heap.Size() == 0);
-                super::SearchWithCore(forward_heap, reverse_heap, forward_core_heap,
-                                      reverse_core_heap, new_total_distance_to_reverse,
-                                      leg_packed_path_reverse, DO_NOT_FORCE_LOOP,
+                super::SearchWithCore(forward_heap,
+                                      reverse_heap,
+                                      forward_core_heap,
+                                      reverse_core_heap,
+                                      new_total_distance_to_reverse,
+                                      leg_packed_path_reverse,
+                                      DO_NOT_FORCE_LOOP,
                                       super::NeedsLoopBackwards(source_phantom, target_phantom));
             }
             else
             {
-                super::Search(forward_heap, reverse_heap, new_total_distance_to_reverse,
-                              leg_packed_path_reverse, DO_NOT_FORCE_LOOP,
+                super::Search(forward_heap,
+                              reverse_heap,
+                              new_total_distance_to_reverse,
+                              leg_packed_path_reverse,
+                              DO_NOT_FORCE_LOOP,
                               super::NeedsLoopBackwards(source_phantom, target_phantom));
             }
         }
@@ -231,7 +253,9 @@ class ShortestPathRouting final
             auto leg_begin = total_packed_path.begin() + packed_leg_begin[current_leg];
             auto leg_end = total_packed_path.begin() + packed_leg_begin[current_leg + 1];
             const auto &unpack_phantom_node_pair = phantom_nodes_vector[current_leg];
-            super::UnpackPath(leg_begin, leg_end, unpack_phantom_node_pair,
+            super::UnpackPath(leg_begin,
+                              leg_end,
+                              unpack_phantom_node_pair,
                               raw_route_data.unpacked_path_segments[current_leg]);
 
             raw_route_data.source_traversed_in_reverse.push_back(
@@ -247,7 +271,9 @@ class ShortestPathRouting final
                     const boost::optional<bool> continue_straight_at_waypoint,
                     InternalRouteResult &raw_route_data) const
     {
-        const bool allow_uturn_at_waypoint = !(continue_straight_at_waypoint ? *continue_straight_at_waypoint : super::facade->GetContinueStraightDefault());
+        const bool allow_uturn_at_waypoint =
+            !(continue_straight_at_waypoint ? *continue_straight_at_waypoint
+                                            : super::facade->GetContinueStraightDefault());
 
         engine_working_data.InitializeOrClearFirstThreadLocalStorage(
             super::facade->GetNumberOfNodes());
@@ -300,12 +326,20 @@ class ShortestPathRouting final
             {
                 if (allow_uturn_at_waypoint)
                 {
-                    SearchWithUTurn(forward_heap, reverse_heap, forward_core_heap,
-                                    reverse_core_heap, search_from_forward_node,
-                                    search_from_reverse_node, search_to_forward_node,
-                                    search_to_reverse_node, source_phantom, target_phantom,
-                                    total_distance_to_forward, total_distance_to_reverse,
-                                    new_total_distance_to_forward, packed_leg_to_forward);
+                    SearchWithUTurn(forward_heap,
+                                    reverse_heap,
+                                    forward_core_heap,
+                                    reverse_core_heap,
+                                    search_from_forward_node,
+                                    search_from_reverse_node,
+                                    search_to_forward_node,
+                                    search_to_reverse_node,
+                                    source_phantom,
+                                    target_phantom,
+                                    total_distance_to_forward,
+                                    total_distance_to_reverse,
+                                    new_total_distance_to_forward,
+                                    packed_leg_to_forward);
                     // if only the reverse node is valid (e.g. when using the match plugin) we
                     // actually need to move
                     if (!target_phantom.forward_segment_id.enabled)
@@ -323,12 +357,22 @@ class ShortestPathRouting final
                 }
                 else
                 {
-                    Search(forward_heap, reverse_heap, forward_core_heap, reverse_core_heap,
-                           search_from_forward_node, search_from_reverse_node,
-                           search_to_forward_node, search_to_reverse_node, source_phantom,
-                           target_phantom, total_distance_to_forward, total_distance_to_reverse,
-                           new_total_distance_to_forward, new_total_distance_to_reverse,
-                           packed_leg_to_forward, packed_leg_to_reverse);
+                    Search(forward_heap,
+                           reverse_heap,
+                           forward_core_heap,
+                           reverse_core_heap,
+                           search_from_forward_node,
+                           search_from_reverse_node,
+                           search_to_forward_node,
+                           search_to_reverse_node,
+                           source_phantom,
+                           target_phantom,
+                           total_distance_to_forward,
+                           total_distance_to_reverse,
+                           new_total_distance_to_forward,
+                           new_total_distance_to_reverse,
+                           packed_leg_to_forward,
+                           packed_leg_to_reverse);
                 }
             }
 
@@ -441,8 +485,11 @@ class ShortestPathRouting final
             packed_leg_to_reverse_begin.push_back(total_packed_path_to_reverse.size());
             BOOST_ASSERT(packed_leg_to_reverse_begin.size() == phantom_nodes_vector.size() + 1);
 
-            UnpackLegs(phantom_nodes_vector, total_packed_path_to_reverse,
-                       packed_leg_to_reverse_begin, total_distance_to_reverse, raw_route_data);
+            UnpackLegs(phantom_nodes_vector,
+                       total_packed_path_to_reverse,
+                       packed_leg_to_reverse_begin,
+                       total_distance_to_reverse,
+                       raw_route_data);
         }
         else
         {
@@ -450,8 +497,11 @@ class ShortestPathRouting final
             packed_leg_to_forward_begin.push_back(total_packed_path_to_forward.size());
             BOOST_ASSERT(packed_leg_to_forward_begin.size() == phantom_nodes_vector.size() + 1);
 
-            UnpackLegs(phantom_nodes_vector, total_packed_path_to_forward,
-                       packed_leg_to_forward_begin, total_distance_to_forward, raw_route_data);
+            UnpackLegs(phantom_nodes_vector,
+                       total_packed_path_to_forward,
+                       packed_leg_to_forward_begin,
+                       total_distance_to_forward,
+                       raw_route_data);
         }
     }
 };
diff --git a/include/engine/search_engine_data.hpp b/include/engine/search_engine_data.hpp
index 3112a78..e970664 100644
--- a/include/engine/search_engine_data.hpp
+++ b/include/engine/search_engine_data.hpp
@@ -3,8 +3,8 @@
 
 #include <boost/thread/tss.hpp>
 
-#include "util/typedefs.hpp"
 #include "util/binary_heap.hpp"
+#include "util/typedefs.hpp"
 
 namespace osrm
 {
diff --git a/include/engine/trip/trip_brute_force.hpp b/include/engine/trip/trip_brute_force.hpp
index b8fe8ec..497c0bf 100644
--- a/include/engine/trip/trip_brute_force.hpp
+++ b/include/engine/trip/trip_brute_force.hpp
@@ -1,18 +1,18 @@
 #ifndef TRIP_BRUTE_FORCE_HPP
 #define TRIP_BRUTE_FORCE_HPP
 
-#include "util/typedefs.hpp"
 #include "util/dist_table_wrapper.hpp"
 #include "util/simple_logger.hpp"
+#include "util/typedefs.hpp"
 
 #include "osrm/json_container.hpp"
 
-#include <cstdlib>
 #include <algorithm>
-#include <string>
+#include <cstdlib>
 #include <iterator>
-#include <vector>
 #include <limits>
+#include <string>
+#include <vector>
 
 namespace osrm
 {
diff --git a/include/engine/trip/trip_farthest_insertion.hpp b/include/engine/trip/trip_farthest_insertion.hpp
index c062e72..038386a 100644
--- a/include/engine/trip/trip_farthest_insertion.hpp
+++ b/include/engine/trip/trip_farthest_insertion.hpp
@@ -1,18 +1,18 @@
 #ifndef TRIP_FARTHEST_INSERTION_HPP
 #define TRIP_FARTHEST_INSERTION_HPP
 
-#include "util/typedefs.hpp"
 #include "util/dist_table_wrapper.hpp"
 #include "util/typedefs.hpp"
+#include "util/typedefs.hpp"
 
 #include "osrm/json_container.hpp"
 #include <boost/assert.hpp>
 
-#include <cstdlib>
 #include <algorithm>
+#include <cstdlib>
+#include <limits>
 #include <string>
 #include <vector>
-#include <limits>
 
 namespace osrm
 {
@@ -153,7 +153,6 @@ std::vector<NodeID> FarthestInsertionTrip(const NodeIDIterator &start,
     // 5. DONE!
     //////////////////////////////////////////////////////////////////////////////////////////////////
 
-
     // Guard against division-by-zero in the code path below.
     BOOST_ASSERT(number_of_locations > 0);
 
diff --git a/include/engine/trip/trip_nearest_neighbour.hpp b/include/engine/trip/trip_nearest_neighbour.hpp
index df70bc4..b0ec093 100644
--- a/include/engine/trip/trip_nearest_neighbour.hpp
+++ b/include/engine/trip/trip_nearest_neighbour.hpp
@@ -1,17 +1,17 @@
 #ifndef TRIP_NEAREST_NEIGHBOUR_HPP
 #define TRIP_NEAREST_NEIGHBOUR_HPP
 
-#include "util/typedefs.hpp"
-#include "util/simple_logger.hpp"
 #include "util/dist_table_wrapper.hpp"
+#include "util/simple_logger.hpp"
+#include "util/typedefs.hpp"
 
 #include "osrm/json_container.hpp"
 
-#include <cstdlib>
 #include <algorithm>
+#include <cstdlib>
+#include <limits>
 #include <string>
 #include <vector>
-#include <limits>
 
 namespace osrm
 {
diff --git a/include/extractor/edge_based_graph_factory.hpp b/include/extractor/edge_based_graph_factory.hpp
index 1aff390..0280501 100644
--- a/include/extractor/edge_based_graph_factory.hpp
+++ b/include/extractor/edge_based_graph_factory.hpp
@@ -3,33 +3,35 @@
 #ifndef EDGE_BASED_GRAPH_FACTORY_HPP_
 #define EDGE_BASED_GRAPH_FACTORY_HPP_
 
-#include "extractor/edge_based_edge.hpp"
-#include "extractor/profile_properties.hpp"
-#include "extractor/restriction_map.hpp"
 #include "extractor/compressed_edge_container.hpp"
+#include "extractor/edge_based_edge.hpp"
 #include "extractor/edge_based_node.hpp"
 #include "extractor/original_edge_data.hpp"
+#include "extractor/profile_properties.hpp"
 #include "extractor/query_node.hpp"
-#include "extractor/guidance/turn_analysis.hpp"
+#include "extractor/restriction_map.hpp"
 
+#include "extractor/guidance/turn_analysis.hpp"
 #include "extractor/guidance/turn_instruction.hpp"
+#include "util/guidance/bearing_class.hpp"
+#include "util/guidance/entry_class.hpp"
 
-#include "util/node_based_graph.hpp"
-#include "util/typedefs.hpp"
 #include "util/deallocating_vector.hpp"
 #include "util/name_table.hpp"
+#include "util/node_based_graph.hpp"
+#include "util/typedefs.hpp"
 
 #include <algorithm>
-#include <cstdint>
 #include <cstddef>
+#include <cstdint>
 #include <iosfwd>
 #include <memory>
 #include <queue>
 #include <string>
+#include <string>
 #include <unordered_map>
 #include <unordered_set>
 #include <vector>
-#include <string>
 
 #include <boost/filesystem/fstream.hpp>
 
@@ -67,6 +69,12 @@ class EdgeBasedGraphFactory
     void GetStartPointMarkers(std::vector<bool> &node_is_startpoint);
     void GetEdgeBasedNodeWeights(std::vector<EdgeWeight> &output_node_weights);
 
+    // These access functions don't destroy the content
+    const std::vector<BearingClassID> &GetBearingClassIds() const;
+    std::vector<BearingClassID> &GetBearingClassIds();
+    std::vector<util::guidance::BearingClass> GetBearingClasses() const;
+    std::vector<util::guidance::EntryClass> GetEntryClasses() const;
+
     unsigned GetHighestEdgeID();
 
     // Basic analysis of a turn (u --(e1)-- v --(e2)-- w)
@@ -127,6 +135,10 @@ class EdgeBasedGraphFactory
     std::size_t restricted_turns_counter;
     std::size_t skipped_uturns_counter;
     std::size_t skipped_barrier_turns_counter;
+
+    std::unordered_map<util::guidance::BearingClass, BearingClassID> bearing_class_hash;
+    std::vector<BearingClassID> bearing_class_by_node_based_node;
+    std::unordered_map<util::guidance::EntryClass, EntryClassID> entry_class_hash;
 };
 } // namespace extractor
 } // namespace osrm
diff --git a/include/extractor/edge_based_node.hpp b/include/extractor/edge_based_node.hpp
index 699e9e7..6ea0349 100644
--- a/include/extractor/edge_based_node.hpp
+++ b/include/extractor/edge_based_node.hpp
@@ -21,8 +21,8 @@ struct EdgeBasedNode
 {
     EdgeBasedNode()
         : forward_segment_id{SPECIAL_SEGMENTID, false},
-          reverse_segment_id{SPECIAL_SEGMENTID, false}, u(SPECIAL_NODEID),
-          v(SPECIAL_NODEID), name_id(0), forward_packed_geometry_id(SPECIAL_EDGEID),
+          reverse_segment_id{SPECIAL_SEGMENTID, false}, u(SPECIAL_NODEID), v(SPECIAL_NODEID),
+          name_id(0), forward_packed_geometry_id(SPECIAL_EDGEID),
           reverse_packed_geometry_id(SPECIAL_EDGEID), component{INVALID_COMPONENTID, false},
           fwd_segment_position(std::numeric_limits<unsigned short>::max()),
           forward_travel_mode(TRAVEL_MODE_INACCESSIBLE),
@@ -42,15 +42,13 @@ struct EdgeBasedNode
                            unsigned short fwd_segment_position,
                            TravelMode forward_travel_mode,
                            TravelMode backward_travel_mode)
-        : forward_segment_id(forward_segment_id_),
-          reverse_segment_id(reverse_segment_id_), u(u), v(v), name_id(name_id),
-          forward_packed_geometry_id(forward_geometry_id_),
+        : forward_segment_id(forward_segment_id_), reverse_segment_id(reverse_segment_id_), u(u),
+          v(v), name_id(name_id), forward_packed_geometry_id(forward_geometry_id_),
           reverse_packed_geometry_id(reverse_geometry_id_),
           component{component_id, is_tiny_component}, fwd_segment_position(fwd_segment_position),
           forward_travel_mode(forward_travel_mode), backward_travel_mode(backward_travel_mode)
     {
-        BOOST_ASSERT(forward_segment_id.enabled ||
-                     reverse_segment_id.enabled);
+        BOOST_ASSERT(forward_segment_id.enabled || reverse_segment_id.enabled);
     }
 
     SegmentID forward_segment_id; // needed for edge-expanded graph
diff --git a/include/extractor/external_memory_node.hpp b/include/extractor/external_memory_node.hpp
index d26f7d3..e531580 100644
--- a/include/extractor/external_memory_node.hpp
+++ b/include/extractor/external_memory_node.hpp
@@ -25,15 +25,17 @@ struct ExternalMemoryNode : QueryNode
 
     static ExternalMemoryNode min_value()
     {
-        return ExternalMemoryNode(util::FixedLongitude(0), util::FixedLatitude(0), MIN_OSM_NODEID,
-                                  false, false);
+        return ExternalMemoryNode(
+            util::FixedLongitude(0), util::FixedLatitude(0), MIN_OSM_NODEID, false, false);
     }
 
     static ExternalMemoryNode max_value()
     {
         return ExternalMemoryNode(util::FixedLongitude(std::numeric_limits<int>::max()),
                                   util::FixedLatitude(std::numeric_limits<int>::max()),
-                                  MAX_OSM_NODEID, false, false);
+                                  MAX_OSM_NODEID,
+                                  false,
+                                  false);
     }
 
     bool barrier;
diff --git a/include/extractor/extraction_containers.hpp b/include/extractor/extraction_containers.hpp
index 848e457..ca27e8d 100644
--- a/include/extractor/extraction_containers.hpp
+++ b/include/extractor/extraction_containers.hpp
@@ -1,11 +1,11 @@
 #ifndef EXTRACTION_CONTAINERS_HPP
 #define EXTRACTION_CONTAINERS_HPP
 
-#include "extractor/internal_extractor_edge.hpp"
-#include "extractor/first_and_last_segment_of_way.hpp"
-#include "extractor/scripting_environment.hpp"
 #include "extractor/external_memory_node.hpp"
+#include "extractor/first_and_last_segment_of_way.hpp"
+#include "extractor/internal_extractor_edge.hpp"
 #include "extractor/restriction.hpp"
+#include "extractor/scripting_environment.hpp"
 
 #include <stxxl/vector>
 #include <unordered_map>
@@ -58,8 +58,6 @@ class ExtractionContainers
 
     ExtractionContainers();
 
-    ~ExtractionContainers();
-
     void PrepareData(const std::string &output_file_name,
                      const std::string &restrictions_file_name,
                      const std::string &names_file_name,
diff --git a/include/extractor/extraction_helper_functions.hpp b/include/extractor/extraction_helper_functions.hpp
index cbd5a64..aefac66 100644
--- a/include/extractor/extraction_helper_functions.hpp
+++ b/include/extractor/extraction_helper_functions.hpp
@@ -1,13 +1,8 @@
 #ifndef EXTRACTION_HELPER_FUNCTIONS_HPP
 #define EXTRACTION_HELPER_FUNCTIONS_HPP
 
-#include "util/cast.hpp"
-#include "util/iso_8601_duration_parser.hpp"
-
-#include <boost/algorithm/string.hpp>
-#include <boost/algorithm/string_regex.hpp>
+#include <boost/spirit/include/phoenix.hpp>
 #include <boost/spirit/include/qi.hpp>
-#include <boost/regex.hpp>
 
 #include <limits>
 #include <string>
@@ -17,82 +12,91 @@ namespace osrm
 namespace extractor
 {
 
-inline bool simple_duration_is_valid(const std::string &s)
+namespace detail
 {
-    boost::regex simple_format(
-        "((\\d|\\d\\d):(\\d|\\d\\d):(\\d|\\d\\d))|((\\d|\\d\\d):(\\d|\\d\\d))|(\\d|\\d\\d)",
-        boost::regex_constants::icase | boost::regex_constants::perl);
-
-    const bool simple_matched = regex_match(s, simple_format);
 
-    if (simple_matched)
-    {
-        return true;
-    }
-    return false;
-}
+namespace qi = boost::spirit::qi;
 
-inline bool iso_8601_duration_is_valid(const std::string &s)
+template <typename Iterator> struct iso_8601_grammar : qi::grammar<Iterator, unsigned()>
 {
-    util::iso_8601_grammar<std::string::const_iterator> iso_parser;
-    const bool result = boost::spirit::qi::parse(s.begin(), s.end(), iso_parser);
+    iso_8601_grammar() : iso_8601_grammar::base_type(root)
 
-    // check if the was an error with the request
-    if (result && (0 != iso_parser.get_duration()))
     {
-        return true;
+        using qi::_1;
+        using qi::_a;
+        using qi::_b;
+        using qi::_c;
+        using qi::_pass;
+        using qi::_val;
+        using qi::eoi;
+        using qi::eps;
+        using qi::uint_;
+        using qi::char_;
+
+        hh = uint2_p[_pass = bind([](unsigned x) { return x < 24; }, _1), _val = _1];
+        mm = uint2_p[_pass = bind([](unsigned x) { return x < 60; }, _1), _val = _1];
+        ss = uint2_p[_pass = bind([](unsigned x) { return x < 60; }, _1), _val = _1];
+
+        osm_time = (uint_p[_a = _1] >> eoi)[_val = _a * 60] |
+                   (uint_p[_a = _1] >> ':' >> uint_p[_b = _1] >> eoi)[_val = _a * 3600 + _b * 60] |
+                   (uint_p[_a = _1] >> ':' >> uint_p[_b = _1] >> ':' >> uint_p[_c = _1] >>
+                    eoi)[_val = _a * 3600 + _b * 60 + _c];
+
+        alternative_time =
+            ('T' >> hh[_a = _1] >> mm[_b = _1] >> ss[_c = _1])[_val = _a * 3600 + _b * 60 + _c];
+
+        extended_time = ('T' >> hh[_a = _1] >> ':' >> mm[_b = _1] >> ':' >>
+                         ss[_c = _1])[_val = _a * 3600 + _b * 60 + _c];
+
+        standard_time =
+            ('T' >> -(uint_ >> char_("Hh"))[_a = _1] >> -(uint_ >> char_("Mm"))[_b = _1] >>
+             -(uint_ >> char_("Ss"))[_c = _1])[_val = _a * 3600 + _b * 60 + _c];
+
+        standard_date = (uint_ >> char_("Dd"))[_val = _1 * 86400];
+
+        standard_week = (uint_ >> char_("Ww"))[_val = _1 * 604800];
+
+        iso_period =
+            osm_time[_val = _1] | ('P' >> standard_week >> eoi)[_val = _1] |
+            ('P' >> (alternative_time[_a = 0, _b = _1] | extended_time[_a = 0, _b = _1] |
+                     (eps[_a = 0, _b = 0] >> -standard_date[_a = _1] >> -standard_time[_b = _1])) >>
+             eoi)[_val = _a + _b];
+
+        root = iso_period;
     }
-    return false;
+
+    qi::rule<Iterator, unsigned()> root;
+    qi::rule<Iterator, unsigned(), qi::locals<unsigned, unsigned>> iso_period;
+    qi::rule<Iterator, unsigned(), qi::locals<unsigned, unsigned, unsigned>> osm_time,
+        standard_time, alternative_time, extended_time;
+    qi::rule<Iterator, unsigned()> standard_date, standard_week;
+    qi::rule<Iterator, unsigned()> hh, mm, ss;
+
+    qi::uint_parser<unsigned, 10, 1, 2> uint_p;
+    qi::uint_parser<unsigned, 10, 2, 2> uint2_p;
+};
 }
 
 inline bool durationIsValid(const std::string &s)
 {
-    return simple_duration_is_valid(s) || iso_8601_duration_is_valid(s);
+    static detail::iso_8601_grammar<std::string::const_iterator> const iso_8601_grammar;
+
+    std::string::const_iterator iter = s.begin();
+    unsigned duration = 0;
+    boost::spirit::qi::parse(iter, s.end(), iso_8601_grammar, duration);
+
+    return !s.empty() && iter == s.end();
 }
 
 inline unsigned parseDuration(const std::string &s)
 {
-    if (simple_duration_is_valid(s))
-    {
-        unsigned hours = 0;
-        unsigned minutes = 0;
-        unsigned seconds = 0;
-        boost::regex e(
-            "((\\d|\\d\\d):(\\d|\\d\\d):(\\d|\\d\\d))|((\\d|\\d\\d):(\\d|\\d\\d))|(\\d|\\d\\d)",
-            boost::regex_constants::icase | boost::regex_constants::perl);
-
-        std::vector<std::string> result;
-        boost::algorithm::split_regex(result, s, boost::regex(":"));
-        const bool matched = regex_match(s, e);
-        if (matched)
-        {
-            if (1 == result.size())
-            {
-                minutes = std::stoul(result[0]);
-            }
-            if (2 == result.size())
-            {
-                minutes = std::stoul(result[1]);
-                hours = std::stoul(result[0]);
-            }
-            if (3 == result.size())
-            {
-                seconds = std::stoul(result[2]);
-                minutes = std::stoul(result[1]);
-                hours = std::stoul(result[0]);
-            }
-            return (3600 * hours + 60 * minutes + seconds);
-        }
-    }
-    else if (iso_8601_duration_is_valid(s))
-    {
-        util::iso_8601_grammar<std::string::const_iterator> iso_parser;
-        boost::spirit::qi::parse(s.begin(), s.end(), iso_parser);
+    static detail::iso_8601_grammar<std::string::const_iterator> const iso_8601_grammar;
 
-        return iso_parser.get_duration();
-    }
+    std::string::const_iterator iter = s.begin();
+    unsigned duration = 0;
+    boost::spirit::qi::parse(iter, s.end(), iso_8601_grammar, duration);
 
-    return std::numeric_limits<unsigned>::max();
+    return !s.empty() && iter == s.end() ? duration : std::numeric_limits<unsigned>::max();
 }
 }
 }
diff --git a/include/extractor/extraction_way.hpp b/include/extractor/extraction_way.hpp
index f82787d..437d17c 100644
--- a/include/extractor/extraction_way.hpp
+++ b/include/extractor/extraction_way.hpp
@@ -31,6 +31,8 @@ struct ExtractionWay
         is_startpoint = true;
         is_access_restricted = false;
         name.clear();
+        pronunciation.clear();
+        destinations.clear();
         forward_travel_mode = TRAVEL_MODE_INACCESSIBLE;
         backward_travel_mode = TRAVEL_MODE_INACCESSIBLE;
     }
@@ -46,6 +48,8 @@ struct ExtractionWay
     double backward_speed;
     double duration;
     std::string name;
+    std::string pronunciation;
+    std::string destinations;
     bool roundabout;
     bool is_access_restricted;
     bool is_startpoint;
diff --git a/include/extractor/extractor.hpp b/include/extractor/extractor.hpp
index 1b61513..b929523 100644
--- a/include/extractor/extractor.hpp
+++ b/include/extractor/extractor.hpp
@@ -29,10 +29,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #define EXTRACTOR_HPP
 
 #include "extractor/edge_based_edge.hpp"
-#include "extractor/extractor_config.hpp"
 #include "extractor/edge_based_graph_factory.hpp"
+#include "extractor/extractor_config.hpp"
 #include "extractor/graph_compressor.hpp"
 
+#include "util/guidance/bearing_class.hpp"
+#include "util/guidance/entry_class.hpp"
+
 #include "util/typedefs.hpp"
 
 namespace osrm
@@ -52,14 +55,16 @@ class Extractor
     ExtractorConfig config;
 
     std::pair<std::size_t, std::size_t>
-    BuildEdgeExpandedGraph(lua_State* lua_state,
-                           const ProfileProperties& profile_properties,
+    BuildEdgeExpandedGraph(lua_State *lua_state,
+                           const ProfileProperties &profile_properties,
                            std::vector<QueryNode> &internal_to_external_node_map,
                            std::vector<EdgeBasedNode> &node_based_edge_list,
                            std::vector<bool> &node_is_startpoint,
                            std::vector<EdgeWeight> &edge_based_node_weights,
-                           util::DeallocatingVector<EdgeBasedEdge> &edge_based_edge_list);
-    void WriteProfileProperties(const std::string& output_path, const ProfileProperties& properties) const;
+                           util::DeallocatingVector<EdgeBasedEdge> &edge_based_edge_list,
+                           const std::string &intersection_class_output_file);
+    void WriteProfileProperties(const std::string &output_path,
+                                const ProfileProperties &properties) const;
     void WriteNodeMapping(const std::vector<QueryNode> &internal_to_external_node_map);
     void FindComponents(unsigned max_edge_id,
                         const util::DeallocatingVector<EdgeBasedEdge> &edges,
@@ -76,6 +81,12 @@ class Extractor
     void WriteEdgeBasedGraph(const std::string &output_file_filename,
                              const size_t max_edge_id,
                              util::DeallocatingVector<EdgeBasedEdge> const &edge_based_edge_list);
+
+    void WriteIntersectionClassificationData(
+        const std::string &output_file_name,
+        const std::vector<std::uint32_t> &node_based_intersection_classes,
+        const std::vector<util::guidance::BearingClass> &bearing_classes,
+        const std::vector<util::guidance::EntryClass> &entry_classes) const;
 };
 }
 }
diff --git a/include/extractor/extractor_callbacks.hpp b/include/extractor/extractor_callbacks.hpp
index db4e870..120884d 100644
--- a/include/extractor/extractor_callbacks.hpp
+++ b/include/extractor/extractor_callbacks.hpp
@@ -2,6 +2,7 @@
 #define EXTRACTOR_CALLBACKS_HPP
 
 #include "util/typedefs.hpp"
+#include <boost/functional/hash.hpp>
 #include <boost/optional/optional_fwd.hpp>
 
 #include <string>
@@ -33,8 +34,10 @@ struct ExtractionWay;
 class ExtractorCallbacks
 {
   private:
-    // used to deduplicate street names: actually maps to name ids
-    std::unordered_map<std::string, NodeID> string_map;
+    // used to deduplicate street names and street destinations: actually maps to name ids
+    using MapKey = std::pair<std::string, std::string>;
+    using MapVal = unsigned;
+    std::unordered_map<MapKey, MapVal, boost::hash<MapKey>> string_map;
     ExtractionContainers &external_memory;
 
   public:
diff --git a/include/extractor/extractor_config.hpp b/include/extractor/extractor_config.hpp
index 80196db..ec4d086 100644
--- a/include/extractor/extractor_config.hpp
+++ b/include/extractor/extractor_config.hpp
@@ -30,8 +30,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #include <boost/filesystem/path.hpp>
 
-#include <string>
 #include <array>
+#include <string>
 
 namespace osrm
 {
@@ -72,6 +72,7 @@ struct ExtractorConfig
         edge_penalty_path = basepath + ".osrm.edge_penalties";
         edge_based_node_weights_output_path = basepath + ".osrm.enw";
         profile_properties_output_path = basepath + ".osrm.properties";
+        intersection_class_data_output_path = basepath + ".osrm.icd";
     }
 
     boost::filesystem::path config_file_path;
@@ -90,6 +91,7 @@ struct ExtractorConfig
     std::string rtree_nodes_output_path;
     std::string rtree_leafs_output_path;
     std::string profile_properties_output_path;
+    std::string intersection_class_data_output_path;
 
     unsigned requested_num_threads;
     unsigned small_component_size;
diff --git a/include/extractor/guidance/classification_data.hpp b/include/extractor/guidance/classification_data.hpp
index 53f54cf..fd63e4f 100644
--- a/include/extractor/guidance/classification_data.hpp
+++ b/include/extractor/guidance/classification_data.hpp
@@ -47,6 +47,17 @@ inline bool isRampClass(const FunctionalRoadClass road_class)
            road_class == FunctionalRoadClass::TRUNK_LINK;
 }
 
+// Links are usually smaller than ramps, but are sometimes tagged
+// as MOTORWAY_LINK if they exit/enter a motorway/trunk road.
+inline bool isLinkClass(const FunctionalRoadClass road_class)
+{
+    return road_class == FunctionalRoadClass::MOTORWAY_LINK ||
+           road_class == FunctionalRoadClass::TRUNK_LINK ||
+           road_class == FunctionalRoadClass::PRIMARY_LINK ||
+           road_class == FunctionalRoadClass::SECONDARY_LINK ||
+           road_class == FunctionalRoadClass::TERTIARY_LINK;
+}
+
 // TODO augment this with all data required for guidance generation
 struct RoadClassificationData
 {
diff --git a/include/extractor/guidance/constants.hpp b/include/extractor/guidance/constants.hpp
index 3e4ae6d..54729ba 100644
--- a/include/extractor/guidance/constants.hpp
+++ b/include/extractor/guidance/constants.hpp
@@ -20,12 +20,13 @@ const double constexpr GROUP_ANGLE = 60;
 // angle difference that can be classified as straight, if its the only narrow turn
 const double constexpr FUZZY_ANGLE_DIFFERENCE = 20.;
 const double constexpr DISTINCTION_RATIO = 2;
-const unsigned constexpr INVALID_NAME_ID = 0;
 
-const double constexpr MAX_ROUNDABOUT_INTERSECTION_RADIUS =  5;
+const double constexpr MAX_ROUNDABOUT_INTERSECTION_RADIUS = 5;
 const double constexpr MAX_ROUNDABOUT_RADIUS = 15; // 30 m diameter as final distinction
 const double constexpr INCREASES_BY_FOURTY_PERCENT = 1.4;
 
+const int constexpr MAX_SLIPROAD_THRESHOLD = 250;
+
 } // namespace guidance
 } // namespace extractor
 } // namespace osrm
diff --git a/include/extractor/guidance/intersection_handler.hpp b/include/extractor/guidance/intersection_handler.hpp
index f1d4981..e657181 100644
--- a/include/extractor/guidance/intersection_handler.hpp
+++ b/include/extractor/guidance/intersection_handler.hpp
@@ -49,7 +49,7 @@ class IntersectionHandler
     std::size_t countValid(const Intersection &intersection) const;
 
     // Decide on a basic turn types
-    TurnType findBasicTurnType(const EdgeID via_edge, const ConnectedRoad &candidate) const;
+    TurnType::Enum findBasicTurnType(const EdgeID via_edge, const ConnectedRoad &candidate) const;
 
     // Get the Instruction for an obvious turn
     TurnInstruction getInstructionForObvious(const std::size_t number_of_candidates,
diff --git a/include/extractor/guidance/motorway_handler.hpp b/include/extractor/guidance/motorway_handler.hpp
index 42805c5..75abe88 100644
--- a/include/extractor/guidance/motorway_handler.hpp
+++ b/include/extractor/guidance/motorway_handler.hpp
@@ -2,6 +2,7 @@
 #define OSRM_EXTRACTOR_GUIDANCE_MOTORWAY_HANDLER_HPP_
 
 #include "extractor/guidance/intersection.hpp"
+#include "extractor/guidance/intersection_generator.hpp"
 #include "extractor/guidance/intersection_handler.hpp"
 #include "extractor/query_node.hpp"
 
@@ -25,7 +26,8 @@ class MotorwayHandler : public IntersectionHandler
     MotorwayHandler(const util::NodeBasedDynamicGraph &node_based_graph,
                     const std::vector<QueryNode> &node_info_list,
                     const util::NameTable &name_table,
-                    const SuffixTable &street_name_suffix_table);
+                    const SuffixTable &street_name_suffix_table,
+                    const IntersectionGenerator &intersection_generator);
     ~MotorwayHandler() override final;
 
     // check whether the handler can actually handle the intersection
@@ -39,10 +41,14 @@ class MotorwayHandler : public IntersectionHandler
                             Intersection intersection) const override final;
 
   private:
+    Intersection handleSliproads(const NodeID intersection_node_id,
+                                 Intersection intersection) const;
     Intersection fromMotorway(const EdgeID via_edge, Intersection intersection) const;
     Intersection fromRamp(const EdgeID via_edge, Intersection intersection) const;
 
     Intersection fallback(Intersection intersection) const;
+
+    const IntersectionGenerator &intersection_generator;
 };
 
 } // namespace guidance
diff --git a/include/extractor/guidance/roundabout_type.hpp b/include/extractor/guidance/roundabout_type.hpp
index 1cbb22d..2c77aeb 100644
--- a/include/extractor/guidance/roundabout_type.hpp
+++ b/include/extractor/guidance/roundabout_type.hpp
@@ -9,10 +9,10 @@ namespace guidance
 {
 enum class RoundaboutType
 {
-    None,                   // not a roundabout
-    Roundabout,             // standard roundabout
-    Rotary,                 // traffic circle (large roundabout) with dedicated name
-    RoundaboutIntersection  // small roundabout with distinct turns, handled as intersection
+    None,                  // not a roundabout
+    Roundabout,            // standard roundabout
+    Rotary,                // traffic circle (large roundabout) with dedicated name
+    RoundaboutIntersection // small roundabout with distinct turns, handled as intersection
 };
 } /* namespace guidance */
 } /* namespace extractor */
diff --git a/include/extractor/guidance/toolkit.hpp b/include/extractor/guidance/toolkit.hpp
index f7d0f61..0c7d267 100644
--- a/include/extractor/guidance/toolkit.hpp
+++ b/include/extractor/guidance/toolkit.hpp
@@ -41,7 +41,8 @@ const constexpr bool shiftable_ccw[] = {false, true, true, false, false, true, t
 const constexpr bool shiftable_cw[] = {false, false, true, true, false, false, true, true};
 const constexpr std::uint8_t modifier_bounds[detail::num_direction_modifiers] = {
     0, 36, 93, 121, 136, 163, 220, 255};
-const constexpr double discrete_angle_step_size = 360. / 256.;
+
+const constexpr double discrete_angle_step_size = 360. / 24;
 
 template <typename IteratorType>
 util::Coordinate
@@ -69,7 +70,8 @@ getCoordinateFromCompressedRange(util::Coordinate current_coordinate,
     };
 
     for (auto compressed_geometry_itr = compressed_geometry_begin;
-         compressed_geometry_itr != compressed_geometry_end; ++compressed_geometry_itr)
+         compressed_geometry_itr != compressed_geometry_end;
+         ++compressed_geometry_itr)
     {
         const auto next_coordinate =
             extractCoordinateFromNode(query_nodes[compressed_geometry_itr->node_id]);
@@ -81,7 +83,8 @@ getCoordinateFromCompressedRange(util::Coordinate current_coordinate,
         if (distance_to_next_coordinate >= detail::DESIRED_SEGMENT_LENGTH)
             return util::coordinate_calculation::interpolateLinear(
                 getFactor(distance_to_current_coordinate, distance_to_next_coordinate),
-                current_coordinate, next_coordinate);
+                current_coordinate,
+                next_coordinate);
 
         // prepare for next iteration
         current_coordinate = next_coordinate;
@@ -97,7 +100,8 @@ getCoordinateFromCompressedRange(util::Coordinate current_coordinate,
         distance_to_next_coordinate >= detail::DESIRED_SEGMENT_LENGTH)
         return util::coordinate_calculation::interpolateLinear(
             getFactor(distance_to_current_coordinate, distance_to_next_coordinate),
-            current_coordinate, final_coordinate);
+            current_coordinate,
+            final_coordinate);
     else
         return final_coordinate;
 }
@@ -144,13 +148,13 @@ getRepresentativeCoordinate(const NodeID from_node,
 }
 
 // shift an instruction around the degree circle in CCW order
-inline DirectionModifier forcedShiftCCW(const DirectionModifier modifier)
+inline DirectionModifier::Enum forcedShiftCCW(const DirectionModifier::Enum modifier)
 {
-    return static_cast<DirectionModifier>((static_cast<std::uint32_t>(modifier) + 1) %
-                                          detail::num_direction_modifiers);
+    return static_cast<DirectionModifier::Enum>((static_cast<std::uint32_t>(modifier) + 1) %
+                                                detail::num_direction_modifiers);
 }
 
-inline DirectionModifier shiftCCW(const DirectionModifier modifier)
+inline DirectionModifier::Enum shiftCCW(const DirectionModifier::Enum modifier)
 {
     if (detail::shiftable_ccw[static_cast<int>(modifier)])
         return forcedShiftCCW(modifier);
@@ -159,14 +163,14 @@ inline DirectionModifier shiftCCW(const DirectionModifier modifier)
 }
 
 // shift an instruction around the degree circle in CW order
-inline DirectionModifier forcedShiftCW(const DirectionModifier modifier)
+inline DirectionModifier::Enum forcedShiftCW(const DirectionModifier::Enum modifier)
 {
-    return static_cast<DirectionModifier>(
+    return static_cast<DirectionModifier::Enum>(
         (static_cast<std::uint32_t>(modifier) + detail::num_direction_modifiers - 1) %
         detail::num_direction_modifiers);
 }
 
-inline DirectionModifier shiftCW(const DirectionModifier modifier)
+inline DirectionModifier::Enum shiftCW(const DirectionModifier::Enum modifier)
 {
     if (detail::shiftable_cw[static_cast<int>(modifier)])
         return forcedShiftCW(modifier);
@@ -174,7 +178,7 @@ inline DirectionModifier shiftCW(const DirectionModifier modifier)
         return modifier;
 }
 
-inline bool isBasic(const TurnType type)
+inline bool isBasic(const TurnType::Enum type)
 {
     return type == TurnType::Turn || type == TurnType::EndOfRoad;
 }
@@ -218,7 +222,7 @@ inline bool isSlightTurn(const TurnInstruction turn)
             turn.direction_modifier == DirectionModifier::SlightLeft);
 }
 
-inline bool isSlightModifier(const DirectionModifier direction_modifier)
+inline bool isSlightModifier(const DirectionModifier::Enum direction_modifier)
 {
     return (direction_modifier == DirectionModifier::Straight ||
             direction_modifier == DirectionModifier::SlightRight ||
@@ -246,15 +250,17 @@ inline bool isConflict(const TurnInstruction first, const TurnInstruction second
 inline DiscreteAngle discretizeAngle(const double angle)
 {
     BOOST_ASSERT(angle >= 0. && angle <= 360.);
-    return DiscreteAngle(static_cast<std::uint8_t>(angle / detail::discrete_angle_step_size));
+    return DiscreteAngle(static_cast<std::uint8_t>(
+        (angle + 0.5 * detail::discrete_angle_step_size) / detail::discrete_angle_step_size));
 }
 
 inline double angleFromDiscreteAngle(const DiscreteAngle angle)
 {
-    return static_cast<double>(angle) * detail::discrete_angle_step_size;
+    return static_cast<double>(angle) * detail::discrete_angle_step_size +
+           0.5 * detail::discrete_angle_step_size;
 }
 
-inline double getAngularPenalty(const double angle, DirectionModifier modifier)
+inline double getAngularPenalty(const double angle, DirectionModifier::Enum modifier)
 {
     // these are not aligned with getTurnDirection but represent an ideal center
     const double center[] = {0, 45, 90, 135, 180, 225, 270, 315};
@@ -274,17 +280,7 @@ inline double getTurnConfidence(const double angle, TurnInstruction instruction)
     return 1.0 - (difference / max_deviation) * (difference / max_deviation);
 }
 
-// swaps left <-> right modifier types
-inline DirectionModifier mirrorDirectionModifier(const DirectionModifier modifier)
-{
-    const constexpr DirectionModifier results[] = {
-        DirectionModifier::UTurn,      DirectionModifier::SharpLeft, DirectionModifier::Left,
-        DirectionModifier::SlightLeft, DirectionModifier::Straight,  DirectionModifier::SlightRight,
-        DirectionModifier::Right,      DirectionModifier::SharpRight};
-    return results[modifier];
-}
-
-inline bool canBeSuppressed(const TurnType type)
+inline bool canBeSuppressed(const TurnType::Enum type)
 {
     if (type == TurnType::Turn)
         return true;
@@ -297,7 +293,7 @@ inline bool isLowPriorityRoadClass(const FunctionalRoadClass road_class)
            road_class == FunctionalRoadClass::SERVICE;
 }
 
-inline bool isDistinct(const DirectionModifier first, const DirectionModifier second)
+inline bool isDistinct(const DirectionModifier::Enum first, const DirectionModifier::Enum second)
 {
     if ((first + 1) % detail::num_direction_modifiers == second)
         return false;
@@ -325,8 +321,8 @@ inline bool requiresNameAnnounced(const std::string &from,
                                   const std::string &to,
                                   const SuffixTable &suffix_table)
 {
-    //first is empty and the second is not
-    if(from.empty() && !to.empty())
+    // first is empty and the second is not
+    if (from.empty() && !to.empty())
         return true;
 
     // FIXME, handle in profile to begin with?
@@ -377,9 +373,11 @@ inline bool requiresNameAnnounced(const std::string &from,
                     return false;
                 if (!checkTable(first_prefix_and_suffixes.first))
                     return false;
-                return !first.compare(first_prefix_and_suffixes.first.length(), std::string::npos,
-                                     second, second_prefix_and_suffixes.first.length(),
-                                     std::string::npos);
+                return !first.compare(first_prefix_and_suffixes.first.length(),
+                                      std::string::npos,
+                                      second,
+                                      second_prefix_and_suffixes.first.length(),
+                                      std::string::npos);
             }();
 
             const bool is_suffix_change = [&]() -> bool {
@@ -387,8 +385,11 @@ inline bool requiresNameAnnounced(const std::string &from,
                     return false;
                 if (!checkTable(first_prefix_and_suffixes.second))
                     return false;
-                return !first.compare(0, first.length() - first_prefix_and_suffixes.second.length(),
-                                     second, 0, second.length() - second_prefix_and_suffixes.second.length());
+                return !first.compare(0,
+                                      first.length() - first_prefix_and_suffixes.second.length(),
+                                      second,
+                                      0,
+                                      second.length() - second_prefix_and_suffixes.second.length());
             }();
 
             return is_prefix_change || is_suffix_change;
@@ -417,8 +418,8 @@ inline int getPriority(const FunctionalRoadClass road_class)
     // The road priorities indicate which roads can bee seen as more or less equal.
     // They are used in Fork-Discovery. Possibly should be moved to profiles post v5?
     // A fork can happen between road types that are at most 1 priority apart from each other
-    const constexpr int road_priority[] = {10, 0, 10, 2,  10, 4,  10, 6,
-                                           10, 8, 10, 11, 10, 12, 10, 14};
+    const constexpr int road_priority[] = {
+        10, 0, 10, 2, 10, 4, 10, 6, 10, 8, 10, 11, 10, 12, 10, 14};
     return road_priority[static_cast<int>(road_class)];
 }
 
@@ -437,10 +438,14 @@ inline bool canBeSeenAsFork(const FunctionalRoadClass first, const FunctionalRoa
 // turn and vice versa.
 inline ConnectedRoad mirror(ConnectedRoad road)
 {
-    const constexpr DirectionModifier mirrored_modifiers[] = {
-        DirectionModifier::UTurn,      DirectionModifier::SharpLeft, DirectionModifier::Left,
-        DirectionModifier::SlightLeft, DirectionModifier::Straight,  DirectionModifier::SlightRight,
-        DirectionModifier::Right,      DirectionModifier::SharpRight};
+    const constexpr DirectionModifier::Enum mirrored_modifiers[] = {DirectionModifier::UTurn,
+                                                                    DirectionModifier::SharpLeft,
+                                                                    DirectionModifier::Left,
+                                                                    DirectionModifier::SlightLeft,
+                                                                    DirectionModifier::Straight,
+                                                                    DirectionModifier::SlightRight,
+                                                                    DirectionModifier::Right,
+                                                                    DirectionModifier::SharpRight};
 
     if (angularDeviation(road.turn.angle, 0) > std::numeric_limits<double>::epsilon())
     {
diff --git a/include/extractor/guidance/turn_analysis.hpp b/include/extractor/guidance/turn_analysis.hpp
index 3f202f3..cade181 100644
--- a/include/extractor/guidance/turn_analysis.hpp
+++ b/include/extractor/guidance/turn_analysis.hpp
@@ -46,6 +46,9 @@ class TurnAnalysis
     // the entry into the turn analysis
     std::vector<TurnOperation> getTurns(const NodeID from_node, const EdgeID via_eid) const;
 
+    // access to the intersection representation for classification purposes
+    Intersection getIntersection(const NodeID from_node, const EdgeID via_eid) const;
+
   private:
     const util::NodeBasedDynamicGraph &node_based_graph;
     const IntersectionGenerator intersection_generator;
@@ -56,6 +59,9 @@ class TurnAnalysis
     // Utility function, setting basic turn types. Prepares for normal turn handling.
     Intersection
     setTurnTypes(const NodeID from, const EdgeID via_edge, Intersection intersection) const;
+
+    Intersection handleSliproads(const NodeID intersection_node_id,
+                                 Intersection intersection) const;
 }; // class TurnAnalysis
 
 } // namespace guidance
diff --git a/include/extractor/guidance/turn_classification.hpp b/include/extractor/guidance/turn_classification.hpp
index 6cf7a08..6c8b467 100644
--- a/include/extractor/guidance/turn_classification.hpp
+++ b/include/extractor/guidance/turn_classification.hpp
@@ -1,17 +1,20 @@
 #ifndef OSRM_GUIDANCE_TURN_CLASSIFICATION_HPP_
 #define OSRM_GUIDANCE_TURN_CLASSIFICATION_HPP_
 
+#include "extractor/compressed_edge_container.hpp"
+#include "extractor/guidance/intersection.hpp"
 #include "extractor/guidance/toolkit.hpp"
+#include "extractor/query_node.hpp"
 
 #include "util/coordinate.hpp"
+#include "util/guidance/bearing_class.hpp"
+#include "util/guidance/entry_class.hpp"
 #include "util/node_based_graph.hpp"
 #include "util/typedefs.hpp"
 
-#include "extractor/compressed_edge_container.hpp"
-#include "extractor/query_node.hpp"
-
 #include <algorithm>
 #include <cstddef>
+#include <utility>
 #include <vector>
 
 namespace osrm
@@ -21,99 +24,12 @@ namespace extractor
 namespace guidance
 {
 
-struct TurnPossibility
-{
-    TurnPossibility(DiscreteAngle angle, EdgeID edge_id)
-        : angle(std::move(angle)), edge_id(std::move(edge_id))
-    {
-    }
-
-    TurnPossibility() : angle(0), edge_id(SPECIAL_EDGEID) {}
-
-    DiscreteAngle angle;
-    EdgeID edge_id;
-};
-
-struct CompareTurnPossibilities
-{
-    bool operator()(const std::vector<TurnPossibility> &left,
-                    const std::vector<TurnPossibility> &right) const
-    {
-        if (left.size() < right.size())
-            return true;
-        if (left.size() > right.size())
-            return false;
-        for (std::size_t i = 0; i < left.size(); ++i)
-        {
-            if ((((int)left[i].angle + 16) % 256) / 32 < (((int)right[i].angle + 16) % 256) / 32)
-                return true;
-            if ((((int)left[i].angle + 16) % 256) / 32 > (((int)right[i].angle + 16) % 256) / 32)
-                return false;
-        }
-        return false;
-    }
-};
-
-inline std::vector<TurnPossibility>
+std::pair<util::guidance::EntryClass, util::guidance::BearingClass>
 classifyIntersection(NodeID nid,
-                     const util::NodeBasedDynamicGraph &graph,
+                     const Intersection &intersection,
+                     const util::NodeBasedDynamicGraph &node_based_graph,
                      const extractor::CompressedEdgeContainer &compressed_geometries,
-                     const std::vector<extractor::QueryNode> &query_nodes)
-{
-
-    std::vector<TurnPossibility> turns;
-
-    if (graph.BeginEdges(nid) == graph.EndEdges(nid))
-        return std::vector<TurnPossibility>();
-
-    const EdgeID base_id = graph.BeginEdges(nid);
-    const auto base_coordinate = getRepresentativeCoordinate(nid, graph.GetTarget(base_id), base_id,
-                                                             graph.GetEdgeData(base_id).reversed,
-                                                             compressed_geometries, query_nodes);
-    const auto node_coordinate = util::Coordinate(query_nodes[nid].lon, query_nodes[nid].lat);
-
-    // generate a list of all turn angles between a base edge, the node and a current edge
-    for (const EdgeID eid : graph.GetAdjacentEdgeRange(nid))
-    {
-        const auto edge_coordinate = getRepresentativeCoordinate(
-            nid, graph.GetTarget(eid), eid, false, compressed_geometries, query_nodes);
-
-        double angle = util::coordinate_calculation::computeAngle(base_coordinate, node_coordinate,
-                                                                  edge_coordinate);
-        turns.emplace_back(discretizeAngle(angle), eid);
-    }
-
-    std::sort(turns.begin(), turns.end(),
-              [](const TurnPossibility left, const TurnPossibility right) {
-                  return left.angle < right.angle;
-              });
-
-    turns.push_back(turns.front()); // sentinel
-    for (std::size_t turn_nr = 0; turn_nr + 1 < turns.size(); ++turn_nr)
-    {
-        turns[turn_nr].angle = (256 + static_cast<uint32_t>(turns[turn_nr + 1].angle) -
-                                static_cast<uint32_t>(turns[turn_nr].angle)) %
-                               256; // calculate the difference to the right
-    }
-    turns.pop_back(); // remove sentinel again
-
-    // find largest:
-    std::size_t best_id = 0;
-    DiscreteAngle largest_turn_angle = turns.front().angle;
-    for (std::size_t current_turn_id = 1; current_turn_id < turns.size(); ++current_turn_id)
-    {
-        if (turns[current_turn_id].angle > largest_turn_angle)
-        {
-            largest_turn_angle = turns[current_turn_id].angle;
-            best_id = current_turn_id;
-        }
-    }
-
-    // rotate all angles so the largest angle comes first
-    std::rotate(turns.begin(), turns.begin() + best_id, turns.end());
-
-    return turns;
-}
+                     const std::vector<extractor::QueryNode> &query_nodes);
 
 } // namespace guidance
 } // namespace extractor
diff --git a/include/extractor/guidance/turn_instruction.hpp b/include/extractor/guidance/turn_instruction.hpp
index f833ef6..dc3ef5a 100644
--- a/include/extractor/guidance/turn_instruction.hpp
+++ b/include/extractor/guidance/turn_instruction.hpp
@@ -21,61 +21,65 @@ const constexpr uint8_t num_direction_modifiers = 8;
 } // detail
 
 // direction modifiers based on angle
-// Would be nice to have
-// enum class DirectionModifier : unsigned char
-enum DirectionModifier
+namespace DirectionModifier
 {
-    UTurn,
-    SharpRight,
-    Right,
-    SlightRight,
-    Straight,
-    SlightLeft,
-    Left,
-    SharpLeft
-};
+typedef std::uint8_t Enum;
+const constexpr Enum UTurn = 0;
+const constexpr Enum SharpRight = 1;
+const constexpr Enum Right = 2;
+const constexpr Enum SlightRight = 3;
+const constexpr Enum Straight = 4;
+const constexpr Enum SlightLeft = 5;
+const constexpr Enum Left = 6;
+const constexpr Enum SharpLeft = 7;
+}
 
-// enum class TurnType : unsigned char
-enum TurnType // at the moment we can support 32 turn types, without increasing memory consumption
+namespace TurnType
 {
-    Invalid,                            // no valid turn instruction
-    NewName,                            // no turn, but name changes
-    Continue,                           // remain on a street
-    Turn,                               // basic turn
-    Merge,                              // merge onto a street
-    OnRamp,                             // special turn (highway ramp on-ramps)
-    OffRamp,                            // special turn, highway exit
-    Fork,                               // fork road splitting up
-    EndOfRoad,                          // T intersection
-    Notification,                       // Travel Mode Changes, Restrictions apply...
-    EnterRoundabout,                    // Entering a small Roundabout
-    EnterAndExitRoundabout,             // Touching a roundabout
-    EnterRotary,                        // Enter a rotary
-    EnterAndExitRotary,                 // Touching a rotary
-    EnterRoundaboutIntersection,        // Entering a small Roundabout
-    EnterAndExitRoundaboutIntersection, // Touching a roundabout
-    NoTurn,                             // end of segment without turn/middle of a segment
-    Suppressed,                         // location that suppresses a turn
-    EnterRoundaboutAtExit,              // Entering a small Roundabout at a countable exit
-    ExitRoundabout,                     // Exiting a small Roundabout
-    EnterRotaryAtExit,                  // Enter A Rotary at a countable exit
-    ExitRotary,                         // Exit a rotary
-    EnterRoundaboutIntersectionAtExit,  // Entering a small Roundabout at a countable exit
-    ExitRoundaboutIntersection,         // Exiting a small Roundabout
-    StayOnRoundabout                    // Continue on Either a small or a large Roundabout
-};
+typedef std::uint8_t Enum;
+const constexpr Enum Invalid = 0;                      // no valid turn instruction
+const constexpr Enum NewName = 1;                      // no turn, but name changes
+const constexpr Enum Continue = 2;                     // remain on a street
+const constexpr Enum Turn = 3;                         // basic turn
+const constexpr Enum Merge = 4;                        // merge onto a street
+const constexpr Enum OnRamp = 5;                       // special turn (highway ramp on-ramps)
+const constexpr Enum OffRamp = 6;                      // special turn, highway exit
+const constexpr Enum Fork = 7;                         // fork road splitting up
+const constexpr Enum EndOfRoad = 8;                    // T intersection
+const constexpr Enum Notification = 9;                 // Travel Mode Changes, Restrictions apply...
+const constexpr Enum EnterRoundabout = 10;             // Entering a small Roundabout
+const constexpr Enum EnterAndExitRoundabout = 11;      // Touching a roundabout
+const constexpr Enum EnterRotary = 12;                 // Enter a rotary
+const constexpr Enum EnterAndExitRotary = 13;          // Touching a rotary
+const constexpr Enum EnterRoundaboutIntersection = 14; // Entering a small Roundabout
+const constexpr Enum EnterAndExitRoundaboutIntersection = 15; // Touching a roundabout
+
+// Values below here are silent instructions
+const constexpr Enum NoTurn = 16;                // end of segment without turn/middle of a segment
+const constexpr Enum Suppressed = 17;            // location that suppresses a turn
+const constexpr Enum EnterRoundaboutAtExit = 18; // Entering a small Roundabout at a countable exit
+const constexpr Enum ExitRoundabout = 19;        // Exiting a small Roundabout
+const constexpr Enum EnterRotaryAtExit = 20;     // Enter A Rotary at a countable exit
+const constexpr Enum ExitRotary = 21;            // Exit a rotary
+const constexpr Enum EnterRoundaboutIntersectionAtExit =
+    22; // Entering a small Roundabout at a countable exit
+const constexpr Enum ExitRoundaboutIntersection = 23; // Exiting a small Roundabout
+const constexpr Enum StayOnRoundabout = 24; // Continue on Either a small or a large Roundabout
+const constexpr Enum Sliproad =
+    25; // Something that looks like a ramp, but is actually just a small sliproad
+}
 
 // turn angle in 1.40625 degree -> 128 == 180 degree
 struct TurnInstruction
 {
-    TurnInstruction(const TurnType type = TurnType::Invalid,
-                    const DirectionModifier direction_modifier = DirectionModifier::Straight)
+    TurnInstruction(const TurnType::Enum type = TurnType::Invalid,
+                    const DirectionModifier::Enum direction_modifier = DirectionModifier::Straight)
         : type(type), direction_modifier(direction_modifier)
     {
     }
 
-    TurnType type : 5;
-    DirectionModifier direction_modifier : 3;
+    TurnType::Enum type : 5;
+    DirectionModifier::Enum direction_modifier : 3;
 
     static TurnInstruction INVALID()
     {
@@ -87,53 +91,63 @@ struct TurnInstruction
         return TurnInstruction(TurnType::NoTurn, DirectionModifier::UTurn);
     }
 
-    static TurnInstruction REMAIN_ROUNDABOUT(const RoundaboutType, const DirectionModifier modifier)
+    static TurnInstruction REMAIN_ROUNDABOUT(const RoundaboutType,
+                                             const DirectionModifier::Enum modifier)
     {
         return TurnInstruction(TurnType::StayOnRoundabout, modifier);
     }
 
     static TurnInstruction ENTER_ROUNDABOUT(const RoundaboutType roundabout_type,
-                                            const DirectionModifier modifier)
+                                            const DirectionModifier::Enum modifier)
     {
-        const constexpr TurnType enter_instruction[] = {
-            TurnType::Invalid, TurnType::EnterRoundabout, TurnType::EnterRotary,
+        const constexpr TurnType::Enum enter_instruction[] = {
+            TurnType::Invalid,
+            TurnType::EnterRoundabout,
+            TurnType::EnterRotary,
             TurnType::EnterRoundaboutIntersection};
         return {enter_instruction[static_cast<int>(roundabout_type)], modifier};
     }
 
     static TurnInstruction EXIT_ROUNDABOUT(const RoundaboutType roundabout_type,
-                                           const DirectionModifier modifier)
+                                           const DirectionModifier::Enum modifier)
     {
-        const constexpr TurnType exit_instruction[] = {TurnType::Invalid, TurnType::ExitRoundabout,
-                                                       TurnType::ExitRotary,
-                                                       TurnType::ExitRoundaboutIntersection};
+        const constexpr TurnType::Enum exit_instruction[] = {TurnType::Invalid,
+                                                             TurnType::ExitRoundabout,
+                                                             TurnType::ExitRotary,
+                                                             TurnType::ExitRoundaboutIntersection};
         return {exit_instruction[static_cast<int>(roundabout_type)], modifier};
     }
 
     static TurnInstruction ENTER_AND_EXIT_ROUNDABOUT(const RoundaboutType roundabout_type,
-                                                     const DirectionModifier modifier)
+                                                     const DirectionModifier::Enum modifier)
     {
-        const constexpr TurnType exit_instruction[] = {
-            TurnType::Invalid, TurnType::EnterAndExitRoundabout, TurnType::EnterAndExitRotary,
+        const constexpr TurnType::Enum exit_instruction[] = {
+            TurnType::Invalid,
+            TurnType::EnterAndExitRoundabout,
+            TurnType::EnterAndExitRotary,
             TurnType::EnterAndExitRoundaboutIntersection};
         return {exit_instruction[static_cast<int>(roundabout_type)], modifier};
     }
 
     static TurnInstruction ENTER_ROUNDABOUT_AT_EXIT(const RoundaboutType roundabout_type,
-                                                    const DirectionModifier modifier)
+                                                    const DirectionModifier::Enum modifier)
     {
-        const constexpr TurnType enter_instruction[] = {
-            TurnType::Invalid, TurnType::EnterRoundaboutAtExit, TurnType::EnterRotaryAtExit,
+        const constexpr TurnType::Enum enter_instruction[] = {
+            TurnType::Invalid,
+            TurnType::EnterRoundaboutAtExit,
+            TurnType::EnterRotaryAtExit,
             TurnType::EnterRoundaboutIntersectionAtExit};
         return {enter_instruction[static_cast<int>(roundabout_type)], modifier};
     }
 
-    static TurnInstruction SUPPRESSED(const DirectionModifier modifier)
+    static TurnInstruction SUPPRESSED(const DirectionModifier::Enum modifier)
     {
         return {TurnType::Suppressed, modifier};
     }
 };
 
+static_assert(sizeof(TurnInstruction) == 1, "TurnInstruction does not fit one byte");
+
 inline bool operator!=(const TurnInstruction lhs, const TurnInstruction rhs)
 {
     return lhs.type != rhs.type || lhs.direction_modifier != rhs.direction_modifier;
diff --git a/include/extractor/internal_extractor_edge.hpp b/include/extractor/internal_extractor_edge.hpp
index 2ccdb2c..14ad198 100644
--- a/include/extractor/internal_extractor_edge.hpp
+++ b/include/extractor/internal_extractor_edge.hpp
@@ -1,15 +1,15 @@
 #ifndef INTERNAL_EXTRACTOR_EDGE_HPP
 #define INTERNAL_EXTRACTOR_EDGE_HPP
 
-#include "util/typedefs.hpp"
-#include "extractor/travel_mode.hpp"
 #include "extractor/node_based_edge.hpp"
+#include "extractor/travel_mode.hpp"
+#include "util/typedefs.hpp"
 
 #include <boost/assert.hpp>
 
+#include "extractor/guidance/classification_data.hpp"
 #include "osrm/coordinate.hpp"
 #include <utility>
-#include "extractor/guidance/classification_data.hpp"
 
 namespace osrm
 {
@@ -32,8 +32,7 @@ struct InternalExtractorEdge
 
         WeightData() : duration(0.0), type(WeightType::INVALID) {}
 
-        union
-        {
+        union {
             double duration;
             double speed;
         };
@@ -94,14 +93,32 @@ struct InternalExtractorEdge
     // necessary static util functions for stxxl's sorting
     static InternalExtractorEdge min_osm_value()
     {
-        return InternalExtractorEdge(MIN_OSM_NODEID, MIN_OSM_NODEID, 0, WeightData(), false, false,
-                                     false, false, true, TRAVEL_MODE_INACCESSIBLE, false,
+        return InternalExtractorEdge(MIN_OSM_NODEID,
+                                     MIN_OSM_NODEID,
+                                     0,
+                                     WeightData(),
+                                     false,
+                                     false,
+                                     false,
+                                     false,
+                                     true,
+                                     TRAVEL_MODE_INACCESSIBLE,
+                                     false,
                                      guidance::RoadClassificationData());
     }
     static InternalExtractorEdge max_osm_value()
     {
-        return InternalExtractorEdge(MAX_OSM_NODEID, MAX_OSM_NODEID, 0, WeightData(), false, false,
-                                     false, false, true, TRAVEL_MODE_INACCESSIBLE, false,
+        return InternalExtractorEdge(MAX_OSM_NODEID,
+                                     MAX_OSM_NODEID,
+                                     0,
+                                     WeightData(),
+                                     false,
+                                     false,
+                                     false,
+                                     false,
+                                     true,
+                                     TRAVEL_MODE_INACCESSIBLE,
+                                     false,
                                      guidance::RoadClassificationData());
     }
 
diff --git a/include/extractor/original_edge_data.hpp b/include/extractor/original_edge_data.hpp
index 17d234a..dce4fb7 100644
--- a/include/extractor/original_edge_data.hpp
+++ b/include/extractor/original_edge_data.hpp
@@ -1,10 +1,11 @@
 #ifndef ORIGINAL_EDGE_DATA_HPP
 #define ORIGINAL_EDGE_DATA_HPP
 
-#include "extractor/travel_mode.hpp"
 #include "extractor/guidance/turn_instruction.hpp"
+#include "extractor/travel_mode.hpp"
 #include "util/typedefs.hpp"
 
+#include <cstddef>
 #include <limits>
 
 namespace osrm
@@ -17,15 +18,16 @@ struct OriginalEdgeData
     explicit OriginalEdgeData(NodeID via_node,
                               unsigned name_id,
                               guidance::TurnInstruction turn_instruction,
+                              EntryClassID entry_classid,
                               TravelMode travel_mode)
-        : via_node(via_node), name_id(name_id), turn_instruction(turn_instruction),
-          travel_mode(travel_mode)
+        : via_node(via_node), name_id(name_id), entry_classid(entry_classid),
+          turn_instruction(turn_instruction), travel_mode(travel_mode)
     {
     }
 
     OriginalEdgeData()
         : via_node(std::numeric_limits<unsigned>::max()),
-          name_id(std::numeric_limits<unsigned>::max()),
+          name_id(std::numeric_limits<unsigned>::max()), entry_classid(INVALID_ENTRY_CLASSID),
           turn_instruction(guidance::TurnInstruction::INVALID()),
           travel_mode(TRAVEL_MODE_INACCESSIBLE)
     {
@@ -33,6 +35,7 @@ struct OriginalEdgeData
 
     NodeID via_node;
     unsigned name_id;
+    EntryClassID entry_classid;
     guidance::TurnInstruction turn_instruction;
     TravelMode travel_mode;
 };
diff --git a/include/extractor/profile_properties.hpp b/include/extractor/profile_properties.hpp
index 7976875..0d07885 100644
--- a/include/extractor/profile_properties.hpp
+++ b/include/extractor/profile_properties.hpp
@@ -11,24 +11,19 @@ namespace extractor
 struct ProfileProperties
 {
     ProfileProperties()
-        : traffic_signal_penalty(0), u_turn_penalty(0), continue_straight_at_waypoint(true), use_turn_restrictions(false)
+        : traffic_signal_penalty(0), u_turn_penalty(0), continue_straight_at_waypoint(true),
+          use_turn_restrictions(false)
     {
     }
 
-    double GetUturnPenalty() const
-    {
-        return u_turn_penalty / 10.;
-    }
+    double GetUturnPenalty() const { return u_turn_penalty / 10.; }
 
     void SetUturnPenalty(const double u_turn_penalty_)
     {
         u_turn_penalty = boost::numeric_cast<int>(u_turn_penalty_ * 10.);
     }
 
-    double GetTrafficSignalPenalty() const
-    {
-        return traffic_signal_penalty / 10.;
-    }
+    double GetTrafficSignalPenalty() const { return traffic_signal_penalty / 10.; }
 
     void SetTrafficSignalPenalty(const double traffic_signal_penalty_)
     {
diff --git a/include/extractor/query_node.hpp b/include/extractor/query_node.hpp
index 93c2d61..69edc08 100644
--- a/include/extractor/query_node.hpp
+++ b/include/extractor/query_node.hpp
@@ -36,13 +36,15 @@ struct QueryNode
     static QueryNode min_value()
     {
         return QueryNode(util::FixedLongitude(-180 * COORDINATE_PRECISION),
-                         util::FixedLatitude(-90 * COORDINATE_PRECISION), MIN_OSM_NODEID);
+                         util::FixedLatitude(-90 * COORDINATE_PRECISION),
+                         MIN_OSM_NODEID);
     }
 
     static QueryNode max_value()
     {
         return QueryNode(util::FixedLongitude(180 * COORDINATE_PRECISION),
-                         util::FixedLatitude(90 * COORDINATE_PRECISION), MAX_OSM_NODEID);
+                         util::FixedLatitude(90 * COORDINATE_PRECISION),
+                         MAX_OSM_NODEID);
     }
 };
 }
diff --git a/include/extractor/raster_source.hpp b/include/extractor/raster_source.hpp
index fe56b45..60c7bcb 100644
--- a/include/extractor/raster_source.hpp
+++ b/include/extractor/raster_source.hpp
@@ -1,18 +1,18 @@
 #ifndef RASTER_SOURCE_HPP
 #define RASTER_SOURCE_HPP
 
-#include "util/exception.hpp"
 #include "util/coordinate.hpp"
+#include "util/exception.hpp"
 
+#include <boost/algorithm/string/trim.hpp>
+#include <boost/assert.hpp>
 #include <boost/filesystem.hpp>
 #include <boost/filesystem/fstream.hpp>
-#include <boost/spirit/include/qi_int.hpp>
 #include <boost/spirit/include/qi.hpp>
-#include <boost/algorithm/string/trim.hpp>
-#include <boost/assert.hpp>
+#include <boost/spirit/include/qi_int.hpp>
 
-#include <unordered_map>
 #include <iterator>
+#include <unordered_map>
 
 namespace osrm
 {
diff --git a/include/extractor/restriction.hpp b/include/extractor/restriction.hpp
index 5916c85..dd76300 100644
--- a/include/extractor/restriction.hpp
+++ b/include/extractor/restriction.hpp
@@ -12,8 +12,7 @@ namespace extractor
 
 struct TurnRestriction
 {
-    union WayOrNode
-    {
+    union WayOrNode {
         OSMNodeID_weak node;
         OSMEdgeID_weak way;
     };
diff --git a/include/extractor/restriction_map.hpp b/include/extractor/restriction_map.hpp
index 3e47867..02c7191 100644
--- a/include/extractor/restriction_map.hpp
+++ b/include/extractor/restriction_map.hpp
@@ -76,7 +76,7 @@ namespace extractor
 class RestrictionMap
 {
   public:
-    RestrictionMap() : m_count(0){}
+    RestrictionMap() : m_count(0) {}
     RestrictionMap(const std::vector<TurnRestriction> &restriction_list);
 
     // Replace end v with w in each turn restriction containing u as via node
diff --git a/include/extractor/restriction_parser.hpp b/include/extractor/restriction_parser.hpp
index 8722bf1..77926c9 100644
--- a/include/extractor/restriction_parser.hpp
+++ b/include/extractor/restriction_parser.hpp
@@ -42,7 +42,7 @@ struct ProfileProperties;
 class RestrictionParser
 {
   public:
-    RestrictionParser(lua_State *lua_state, const ProfileProperties& properties);
+    RestrictionParser(lua_State *lua_state, const ProfileProperties &properties);
     boost::optional<InputRestrictionContainer> TryParse(const osmium::Relation &relation) const;
 
   private:
diff --git a/include/extractor/scripting_environment.hpp b/include/extractor/scripting_environment.hpp
index 5494c12..16c8da1 100644
--- a/include/extractor/scripting_environment.hpp
+++ b/include/extractor/scripting_environment.hpp
@@ -6,9 +6,9 @@
 
 #include "util/lua_util.hpp"
 
-#include <string>
 #include <memory>
 #include <mutex>
+#include <string>
 #include <tbb/enumerable_thread_specific.h>
 
 struct lua_State;
diff --git a/include/extractor/tarjan_scc.hpp b/include/extractor/tarjan_scc.hpp
index 71186ec..72904ea 100644
--- a/include/extractor/tarjan_scc.hpp
+++ b/include/extractor/tarjan_scc.hpp
@@ -1,11 +1,11 @@
 #ifndef TARJAN_SCC_HPP
 #define TARJAN_SCC_HPP
 
-#include "util/typedefs.hpp"
-#include "util/deallocating_vector.hpp"
 #include "extractor/node_based_edge.hpp"
 #include "extractor/query_node.hpp"
+#include "util/deallocating_vector.hpp"
 #include "util/percent.hpp"
+#include "util/typedefs.hpp"
 
 #include "util/integer_range.hpp"
 #include "util/simple_logger.hpp"
@@ -16,9 +16,9 @@
 #include <boost/assert.hpp>
 #include <cstdint>
 
-#include <memory>
 #include <algorithm>
 #include <climits>
+#include <memory>
 #include <stack>
 #include <vector>
 
@@ -160,11 +160,9 @@ template <typename GraphT> class TarjanSCC
         TIMER_STOP(SCC_RUN);
         util::SimpleLogger().Write() << "SCC run took: " << TIMER_MSEC(SCC_RUN) / 1000. << "s";
 
-        size_one_counter = std::count_if(component_size_vector.begin(), component_size_vector.end(),
-                                         [](unsigned value)
-                                         {
-                                             return 1 == value;
-                                         });
+        size_one_counter = std::count_if(component_size_vector.begin(),
+                                         component_size_vector.end(),
+                                         [](unsigned value) { return 1 == value; });
     }
 
     std::size_t GetNumberOfComponents() const { return component_size_vector.size(); }
diff --git a/include/osrm/bearing.hpp b/include/osrm/bearing.hpp
index 75ec110..817ce1f 100644
--- a/include/osrm/bearing.hpp
+++ b/include/osrm/bearing.hpp
@@ -32,7 +32,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 namespace osrm
 {
-  using engine::Bearing;
+using engine::Bearing;
 }
 
 #endif
diff --git a/include/server/api/base_parameters_grammar.hpp b/include/server/api/base_parameters_grammar.hpp
index cc10cf3..a039970 100644
--- a/include/server/api/base_parameters_grammar.hpp
+++ b/include/server/api/base_parameters_grammar.hpp
@@ -75,8 +75,8 @@ struct BaseParametersGrammar : boost::spirit::qi::grammar<Iterator, Signature>
     BaseParametersGrammar(qi::rule<Iterator, Signature> &root_rule)
         : BaseParametersGrammar::base_type(root_rule)
     {
-        const auto add_hint = [](engine::api::BaseParameters &base_parameters, const boost::optional<std::string> &hint_string)
-        {
+        const auto add_hint = [](engine::api::BaseParameters &base_parameters,
+                                 const boost::optional<std::string> &hint_string) {
             if (hint_string)
             {
                 base_parameters.hints.emplace_back(engine::Hint::FromBase64(hint_string.get()));
@@ -87,64 +87,63 @@ struct BaseParametersGrammar : boost::spirit::qi::grammar<Iterator, Signature>
             }
         };
 
-        const auto add_bearing = [](engine::api::BaseParameters &base_parameters,
-                                    boost::optional<boost::fusion::vector2<short, short>> bearing_range)
-        {
-            boost::optional<engine::Bearing> bearing;
-            if (bearing_range)
-            {
-                bearing = engine::Bearing{boost::fusion::at_c<0>(*bearing_range),
-                                          boost::fusion::at_c<1>(*bearing_range)};
-            }
-            base_parameters.bearings.push_back(std::move(bearing));
-        };
+        const auto add_bearing =
+            [](engine::api::BaseParameters &base_parameters,
+               boost::optional<boost::fusion::vector2<short, short>> bearing_range) {
+                boost::optional<engine::Bearing> bearing;
+                if (bearing_range)
+                {
+                    bearing = engine::Bearing{boost::fusion::at_c<0>(*bearing_range),
+                                              boost::fusion::at_c<1>(*bearing_range)};
+                }
+                base_parameters.bearings.push_back(std::move(bearing));
+            };
 
         polyline_chars = qi::char_("a-zA-Z0-9_.--[]{}@?|\\%~`^");
         base64_char = qi::char_("a-zA-Z0-9--_=");
         unlimited_rule = qi::lit("unlimited")[qi::_val = std::numeric_limits<double>::infinity()];
 
-        bearing_rule
-            = (qi::short_ > ',' > qi::short_)
-              [qi::_val = ph::bind([](short bearing, short range) {
-                  return osrm::engine::Bearing{bearing, range};
-              }, qi::_1, qi::_2)]
-            ;
-
-        location_rule
-            = (double_ > qi::lit(',') > double_)
-              [qi::_val = ph::bind([](double lon, double lat) {
-                  return util::Coordinate(util::FixedLongitude(lon * COORDINATE_PRECISION),
-                                          util::FixedLatitude(lat * COORDINATE_PRECISION));
-              }, qi::_1, qi::_2)]
-            ;
-
-        polyline_rule
-            = qi::as_string[qi::lit("polyline(") > +polyline_chars > ')']
-              [qi::_val = ph::bind([](const std::string &polyline) {
-                  return engine::decodePolyline(polyline);
-              }, qi::_1)]
-            ;
-
-        query_rule
-            = ((location_rule % ';') | polyline_rule)
-              [ph::bind(&engine::api::BaseParameters::coordinates, qi::_r1) = qi::_1]
-            ;
-
-        radiuses_rule
-            = qi::lit("radiuses=")
-            > (-(qi::double_ | unlimited_rule) % ';')
-              [ph::bind(&engine::api::BaseParameters::radiuses, qi::_r1) = qi::_1]
-            ;
-
-        hints_rule
-            = qi::lit("hints=")
-            > (-qi::as_string[qi::repeat(engine::ENCODED_HINT_SIZE)[base64_char]])[ph::bind(add_hint, qi::_r1, qi::_1)] % ';'
-            ;
-
-        bearings_rule
-            = qi::lit("bearings=") >
-            (-(qi::short_ > ',' > qi::short_))[ph::bind(add_bearing, qi::_r1, qi::_1)] % ';'
-            ;
+        bearing_rule =
+            (qi::short_ > ',' > qi::short_)[qi::_val = ph::bind(
+                                                [](short bearing, short range) {
+                                                    return osrm::engine::Bearing{bearing, range};
+                                                },
+                                                qi::_1,
+                                                qi::_2)];
+
+        location_rule = (double_ > qi::lit(',') >
+                         double_)[qi::_val = ph::bind(
+                                      [](double lon, double lat) {
+                                          return util::Coordinate(
+                                              util::FixedLongitude(lon * COORDINATE_PRECISION),
+                                              util::FixedLatitude(lat * COORDINATE_PRECISION));
+                                      },
+                                      qi::_1,
+                                      qi::_2)];
+
+        polyline_rule = qi::as_string[qi::lit("polyline(") > +polyline_chars > ')']
+                                     [qi::_val = ph::bind(
+                                          [](const std::string &polyline) {
+                                              return engine::decodePolyline(polyline);
+                                          },
+                                          qi::_1)];
+
+        query_rule =
+            ((location_rule % ';') |
+             polyline_rule)[ph::bind(&engine::api::BaseParameters::coordinates, qi::_r1) = qi::_1];
+
+        radiuses_rule = qi::lit("radiuses=") >
+                        (-(qi::double_ | unlimited_rule) %
+                         ';')[ph::bind(&engine::api::BaseParameters::radiuses, qi::_r1) = qi::_1];
+
+        hints_rule = qi::lit("hints=") >
+                     (-qi::as_string[qi::repeat(engine::ENCODED_HINT_SIZE)[base64_char]])[ph::bind(
+                         add_hint, qi::_r1, qi::_1)] %
+                         ';';
+
+        bearings_rule =
+            qi::lit("bearings=") >
+            (-(qi::short_ > ',' > qi::short_))[ph::bind(add_bearing, qi::_r1, qi::_1)] % ';';
 
         base_rule = radiuses_rule(qi::_r1) | hints_rule(qi::_r1) | bearings_rule(qi::_r1);
     }
diff --git a/include/server/api/match_parameter_grammar.hpp b/include/server/api/match_parameter_grammar.hpp
index a775e49..ac4c02c 100644
--- a/include/server/api/match_parameter_grammar.hpp
+++ b/include/server/api/match_parameter_grammar.hpp
@@ -1,8 +1,8 @@
 #ifndef MATCH_PARAMETERS_GRAMMAR_HPP
 #define MATCH_PARAMETERS_GRAMMAR_HPP
 
-#include "engine/api/match_parameters.hpp"
 #include "server/api/route_parameters_grammar.hpp"
+#include "engine/api/match_parameters.hpp"
 
 #include <boost/spirit/include/phoenix.hpp>
 #include <boost/spirit/include/qi.hpp>
@@ -28,15 +28,13 @@ struct MatchParametersGrammar final : public RouteParametersGrammar<Iterator, Si
 
     MatchParametersGrammar() : BaseGrammar(root_rule)
     {
-        timestamps_rule
-            = qi::lit("timestamps=")
-            > (qi::uint_ % ';')[ph::bind(&engine::api::MatchParameters::timestamps, qi::_r1) = qi::_1]
-            ;
-
-        root_rule
-            = BaseGrammar::query_rule(qi::_r1) > -qi::lit(".json")
-            > -('?' > (timestamps_rule(qi::_r1) | BaseGrammar::base_rule(qi::_r1)) % '&')
-            ;
+        timestamps_rule =
+            qi::lit("timestamps=") >
+            (qi::uint_ %
+             ';')[ph::bind(&engine::api::MatchParameters::timestamps, qi::_r1) = qi::_1];
+
+        root_rule = BaseGrammar::query_rule(qi::_r1) > -qi::lit(".json") >
+                    -('?' > (timestamps_rule(qi::_r1) | BaseGrammar::base_rule(qi::_r1)) % '&');
     }
 
   private:
diff --git a/include/server/api/nearest_parameter_grammar.hpp b/include/server/api/nearest_parameter_grammar.hpp
index b9bc21b..819cfed 100644
--- a/include/server/api/nearest_parameter_grammar.hpp
+++ b/include/server/api/nearest_parameter_grammar.hpp
@@ -1,8 +1,8 @@
 #ifndef NEAREST_PARAMETERS_GRAMMAR_HPP
 #define NEAREST_PARAMETERS_GRAMMAR_HPP
 
-#include "engine/api/nearest_parameters.hpp"
 #include "server/api/base_parameters_grammar.hpp"
+#include "engine/api/nearest_parameters.hpp"
 
 #include <boost/spirit/include/phoenix.hpp>
 #include <boost/spirit/include/qi.hpp>
@@ -28,15 +28,12 @@ struct NearestParametersGrammar final : public BaseParametersGrammar<Iterator, S
 
     NearestParametersGrammar() : BaseGrammar(root_rule)
     {
-        nearest_rule
-            = (qi::lit("number=") > qi::uint_)
-              [ph::bind(&engine::api::NearestParameters::number_of_results, qi::_r1) = qi::_1]
-            ;
-
-        root_rule
-            = BaseGrammar::query_rule(qi::_r1) > -qi::lit(".json")
-            > -('?' > (nearest_rule(qi::_r1) | BaseGrammar::base_rule(qi::_r1)) % '&')
-            ;
+        nearest_rule = (qi::lit("number=") >
+                        qi::uint_)[ph::bind(&engine::api::NearestParameters::number_of_results,
+                                            qi::_r1) = qi::_1];
+
+        root_rule = BaseGrammar::query_rule(qi::_r1) > -qi::lit(".json") >
+                    -('?' > (nearest_rule(qi::_r1) | BaseGrammar::base_rule(qi::_r1)) % '&');
     }
 
   private:
diff --git a/include/server/api/route_parameters_grammar.hpp b/include/server/api/route_parameters_grammar.hpp
index 46d10d1..845c342 100644
--- a/include/server/api/route_parameters_grammar.hpp
+++ b/include/server/api/route_parameters_grammar.hpp
@@ -1,8 +1,8 @@
 #ifndef ROUTE_PARAMETERS_GRAMMAR_HPP
 #define ROUTE_PARAMETERS_GRAMMAR_HPP
 
-#include "engine/api/route_parameters.hpp"
 #include "server/api/base_parameters_grammar.hpp"
+#include "engine/api/route_parameters.hpp"
 
 #include <boost/spirit/include/phoenix.hpp>
 #include <boost/spirit/include/qi.hpp>
@@ -28,38 +28,38 @@ struct RouteParametersGrammar : public BaseParametersGrammar<Iterator, Signature
 
     RouteParametersGrammar() : RouteParametersGrammar(root_rule)
     {
-        route_rule
-            = (qi::lit("alternatives=") > qi::bool_[ph::bind(&engine::api::RouteParameters::alternatives, qi::_r1) = qi::_1])
-            | (qi::lit("continue_straight=")
-               > (qi::lit("default")
-                 | qi::bool_[ph::bind(&engine::api::RouteParameters::continue_straight, qi::_r1) = qi::_1]))
-            ;
+        route_rule =
+            (qi::lit("alternatives=") >
+             qi::bool_[ph::bind(&engine::api::RouteParameters::alternatives, qi::_r1) = qi::_1]) |
+            (qi::lit("continue_straight=") >
+             (qi::lit("default") |
+              qi::bool_[ph::bind(&engine::api::RouteParameters::continue_straight, qi::_r1) =
+                            qi::_1]));
 
-        root_rule
-            = query_rule(qi::_r1) > -qi::lit(".json")
-            > -('?' > (route_rule(qi::_r1) | base_rule(qi::_r1)) % '&')
-            ;
+        root_rule = query_rule(qi::_r1) > -qi::lit(".json") >
+                    -('?' > (route_rule(qi::_r1) | base_rule(qi::_r1)) % '&');
     }
 
     RouteParametersGrammar(qi::rule<Iterator, Signature> &root_rule_) : BaseGrammar(root_rule_)
     {
-        geometries_type.add
-            ("geojson", engine::api::RouteParameters::GeometriesType::GeoJSON)
-            ("polyline", engine::api::RouteParameters::GeometriesType::Polyline)
-            ;
+        geometries_type.add("geojson", engine::api::RouteParameters::GeometriesType::GeoJSON)(
+            "polyline", engine::api::RouteParameters::GeometriesType::Polyline);
 
-        overview_type.add
-            ("simplified", engine::api::RouteParameters::OverviewType::Simplified)
-            ("full", engine::api::RouteParameters::OverviewType::Full)
-            ("false", engine::api::RouteParameters::OverviewType::False)
-            ;
+        overview_type.add("simplified", engine::api::RouteParameters::OverviewType::Simplified)(
+            "full", engine::api::RouteParameters::OverviewType::Full)(
+            "false", engine::api::RouteParameters::OverviewType::False);
 
         base_rule =
-            BaseGrammar::base_rule(qi::_r1)
-            | (qi::lit("steps=") > qi::bool_[ph::bind(&engine::api::RouteParameters::steps, qi::_r1) = qi::_1])
-            | (qi::lit("geometries=") > geometries_type[ph::bind(&engine::api::RouteParameters::geometries, qi::_r1) = qi::_1])
-            | (qi::lit("overview=") > overview_type[ph::bind(&engine::api::RouteParameters::overview, qi::_r1) = qi::_1])
-            ;
+            BaseGrammar::base_rule(qi::_r1) |
+            (qi::lit("steps=") >
+             qi::bool_[ph::bind(&engine::api::RouteParameters::steps, qi::_r1) = qi::_1]) |
+            (qi::lit("annotations=") >
+             qi::bool_[ph::bind(&engine::api::RouteParameters::annotations, qi::_r1) = qi::_1]) |
+            (qi::lit("geometries=") >
+             geometries_type[ph::bind(&engine::api::RouteParameters::geometries, qi::_r1) =
+                                 qi::_1]) |
+            (qi::lit("overview=") >
+             overview_type[ph::bind(&engine::api::RouteParameters::overview, qi::_r1) = qi::_1]);
 
         query_rule = BaseGrammar::query_rule(qi::_r1);
     }
diff --git a/include/server/api/table_parameter_grammar.hpp b/include/server/api/table_parameter_grammar.hpp
index 73ff3c6..5a97c8e 100644
--- a/include/server/api/table_parameter_grammar.hpp
+++ b/include/server/api/table_parameter_grammar.hpp
@@ -1,8 +1,8 @@
 #ifndef TABLE_PARAMETERS_GRAMMAR_HPP
 #define TABLE_PARAMETERS_GRAMMAR_HPP
 
-#include "engine/api/table_parameters.hpp"
 #include "server/api/base_parameters_grammar.hpp"
+#include "engine/api/table_parameters.hpp"
 
 #include <boost/spirit/include/phoenix.hpp>
 #include <boost/spirit/include/qi.hpp>
@@ -37,22 +37,21 @@ struct TableParametersGrammar final : public BaseParametersGrammar<Iterator, Sig
         size_t_ = qi::ulong_;
 #endif
 
-        destinations_rule
-            = qi::lit("destinations=")
-            > (qi::lit("all") | (size_t_ % ';')[ph::bind(&engine::api::TableParameters::destinations, qi::_r1) = qi::_1])
-            ;
+        destinations_rule =
+            qi::lit("destinations=") >
+            (qi::lit("all") |
+             (size_t_ %
+              ';')[ph::bind(&engine::api::TableParameters::destinations, qi::_r1) = qi::_1]);
 
-        sources_rule
-            = qi::lit("sources=")
-            > (qi::lit("all") | (size_t_ % ';')[ph::bind(&engine::api::TableParameters::sources, qi::_r1) = qi::_1])
-            ;
+        sources_rule =
+            qi::lit("sources=") >
+            (qi::lit("all") |
+             (size_t_ % ';')[ph::bind(&engine::api::TableParameters::sources, qi::_r1) = qi::_1]);
 
         table_rule = destinations_rule(qi::_r1) | sources_rule(qi::_r1);
 
-        root_rule
-            = BaseGrammar::query_rule(qi::_r1) > -qi::lit(".json")
-            > -('?' > (table_rule(qi::_r1) | BaseGrammar::base_rule(qi::_r1)) % '&')
-            ;
+        root_rule = BaseGrammar::query_rule(qi::_r1) > -qi::lit(".json") >
+                    -('?' > (table_rule(qi::_r1) | BaseGrammar::base_rule(qi::_r1)) % '&');
     }
 
   private:
diff --git a/include/server/api/tile_parameter_grammar.hpp b/include/server/api/tile_parameter_grammar.hpp
index f0d79b6..42e9584 100644
--- a/include/server/api/tile_parameter_grammar.hpp
+++ b/include/server/api/tile_parameter_grammar.hpp
@@ -30,13 +30,11 @@ struct TileParametersGrammar final : boost::spirit::qi::grammar<Iterator, Signat
 {
     TileParametersGrammar() : TileParametersGrammar::base_type(root_rule)
     {
-        root_rule
-            = qi::lit("tile(")
-            > qi::uint_[ph::bind(&engine::api::TileParameters::x, qi::_r1) = qi::_1] > ','
-            > qi::uint_[ph::bind(&engine::api::TileParameters::y, qi::_r1) = qi::_1] > ','
-            > qi::uint_[ph::bind(&engine::api::TileParameters::z, qi::_r1) = qi::_1]
-            > qi::lit(").mvt")
-            ;
+        root_rule = qi::lit("tile(") >
+                    qi::uint_[ph::bind(&engine::api::TileParameters::x, qi::_r1) = qi::_1] > ',' >
+                    qi::uint_[ph::bind(&engine::api::TileParameters::y, qi::_r1) = qi::_1] > ',' >
+                    qi::uint_[ph::bind(&engine::api::TileParameters::z, qi::_r1) = qi::_1] >
+                    qi::lit(").mvt");
     }
 
   private:
diff --git a/include/server/api/trip_parameter_grammar.hpp b/include/server/api/trip_parameter_grammar.hpp
index b9a7ee0..30809a3 100644
--- a/include/server/api/trip_parameter_grammar.hpp
+++ b/include/server/api/trip_parameter_grammar.hpp
@@ -1,8 +1,8 @@
 #ifndef TRIP_PARAMETERS_GRAMMAR_HPP
 #define TRIP_PARAMETERS_GRAMMAR_HPP
 
-#include "engine/api/trip_parameters.hpp"
 #include "server/api/route_parameters_grammar.hpp"
+#include "engine/api/trip_parameters.hpp"
 
 #include <boost/spirit/include/qi.hpp>
 
@@ -26,10 +26,8 @@ struct TripParametersGrammar final : public RouteParametersGrammar<Iterator, Sig
 
     TripParametersGrammar() : BaseGrammar(root_rule)
     {
-        root_rule
-            = BaseGrammar::query_rule(qi::_r1) > -qi::lit(".json")
-            > -('?' > (BaseGrammar::base_rule(qi::_r1)) % '&')
-            ;
+        root_rule = BaseGrammar::query_rule(qi::_r1) > -qi::lit(".json") >
+                    -('?' > (BaseGrammar::base_rule(qi::_r1)) % '&');
     }
 
   private:
diff --git a/include/server/http/header.hpp b/include/server/http/header.hpp
index 33cc4bb..48111a9 100644
--- a/include/server/http/header.hpp
+++ b/include/server/http/header.hpp
@@ -1,8 +1,8 @@
 #ifndef HEADER_HPP
 #define HEADER_HPP
 
-#include <string>
 #include <algorithm>
+#include <string>
 
 namespace osrm
 {
diff --git a/include/server/server.hpp b/include/server/server.hpp
index 8813074..7f0e1da 100644
--- a/include/server/server.hpp
+++ b/include/server/server.hpp
@@ -13,11 +13,16 @@
 
 #include <zlib.h>
 
+#ifndef _WIN32
+#include <sys/socket.h>
+#include <sys/types.h>
+#endif
+
 #include <functional>
 #include <memory>
+#include <string>
 #include <thread>
 #include <vector>
-#include <string>
 
 namespace osrm
 {
@@ -49,6 +54,10 @@ class Server
         boost::asio::ip::tcp::endpoint endpoint = *resolver.resolve(query);
 
         acceptor.open(endpoint.protocol());
+#ifdef SO_REUSEPORT
+        const int option = 1;
+        setsockopt(acceptor.native_handle(), SOL_SOCKET, SO_REUSEPORT, &option, sizeof(option));
+#endif
         acceptor.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
         acceptor.bind(endpoint);
         acceptor.listen();
diff --git a/include/server/service/base_service.hpp b/include/server/service/base_service.hpp
index cfa8581..11eccbb 100644
--- a/include/server/service/base_service.hpp
+++ b/include/server/service/base_service.hpp
@@ -2,8 +2,8 @@
 #define SERVER_SERVICE_BASE_SERVICE_HPP
 
 #include "engine/status.hpp"
-#include "util/coordinate.hpp"
 #include "osrm/osrm.hpp"
+#include "util/coordinate.hpp"
 
 #include <variant/variant.hpp>
 
@@ -25,7 +25,8 @@ class BaseService
     BaseService(OSRM &routing_machine) : routing_machine(routing_machine) {}
     virtual ~BaseService() = default;
 
-    virtual engine::Status RunQuery(std::size_t prefix_length, std::string &query, ResultT &result) = 0;
+    virtual engine::Status
+    RunQuery(std::size_t prefix_length, std::string &query, ResultT &result) = 0;
 
     virtual unsigned GetVersion() = 0;
 
diff --git a/include/server/service/match_service.hpp b/include/server/service/match_service.hpp
index a0aed94..07a95e2 100644
--- a/include/server/service/match_service.hpp
+++ b/include/server/service/match_service.hpp
@@ -4,8 +4,8 @@
 #include "server/service/base_service.hpp"
 
 #include "engine/status.hpp"
-#include "util/coordinate.hpp"
 #include "osrm/osrm.hpp"
+#include "util/coordinate.hpp"
 
 #include <string>
 #include <vector>
@@ -22,7 +22,8 @@ class MatchService final : public BaseService
   public:
     MatchService(OSRM &routing_machine) : BaseService(routing_machine) {}
 
-    engine::Status RunQuery(std::size_t prefix_length, std::string &query, ResultT &result) final override;
+    engine::Status
+    RunQuery(std::size_t prefix_length, std::string &query, ResultT &result) final override;
 
     unsigned GetVersion() final override { return 1; }
 };
diff --git a/include/server/service/nearest_service.hpp b/include/server/service/nearest_service.hpp
index 62fc1b1..ffbbaf8 100644
--- a/include/server/service/nearest_service.hpp
+++ b/include/server/service/nearest_service.hpp
@@ -4,8 +4,8 @@
 #include "server/service/base_service.hpp"
 
 #include "engine/status.hpp"
-#include "util/coordinate.hpp"
 #include "osrm/osrm.hpp"
+#include "util/coordinate.hpp"
 
 #include <string>
 #include <vector>
@@ -22,7 +22,8 @@ class NearestService final : public BaseService
   public:
     NearestService(OSRM &routing_machine) : BaseService(routing_machine) {}
 
-    engine::Status RunQuery(std::size_t prefix_length, std::string &query, ResultT &result) final override;
+    engine::Status
+    RunQuery(std::size_t prefix_length, std::string &query, ResultT &result) final override;
 
     unsigned GetVersion() final override { return 1; }
 };
diff --git a/include/server/service/route_service.hpp b/include/server/service/route_service.hpp
index 639aea2..3e9be1b 100644
--- a/include/server/service/route_service.hpp
+++ b/include/server/service/route_service.hpp
@@ -4,8 +4,8 @@
 #include "server/service/base_service.hpp"
 
 #include "engine/status.hpp"
-#include "util/coordinate.hpp"
 #include "osrm/osrm.hpp"
+#include "util/coordinate.hpp"
 
 #include <string>
 #include <vector>
@@ -22,7 +22,8 @@ class RouteService final : public BaseService
   public:
     RouteService(OSRM &routing_machine) : BaseService(routing_machine) {}
 
-    engine::Status RunQuery(std::size_t prefix_length, std::string &query, ResultT &result) final override;
+    engine::Status
+    RunQuery(std::size_t prefix_length, std::string &query, ResultT &result) final override;
 
     unsigned GetVersion() final override { return 1; }
 };
diff --git a/include/server/service/table_service.hpp b/include/server/service/table_service.hpp
index 7839bd4..ca32f4d 100644
--- a/include/server/service/table_service.hpp
+++ b/include/server/service/table_service.hpp
@@ -4,8 +4,8 @@
 #include "server/service/base_service.hpp"
 
 #include "engine/status.hpp"
-#include "util/coordinate.hpp"
 #include "osrm/osrm.hpp"
+#include "util/coordinate.hpp"
 
 #include <string>
 #include <vector>
@@ -22,7 +22,8 @@ class TableService final : public BaseService
   public:
     TableService(OSRM &routing_machine) : BaseService(routing_machine) {}
 
-    engine::Status RunQuery(std::size_t prefix_length, std::string &query, ResultT &result) final override;
+    engine::Status
+    RunQuery(std::size_t prefix_length, std::string &query, ResultT &result) final override;
 
     unsigned GetVersion() final override { return 1; }
 };
diff --git a/include/server/service/tile_service.hpp b/include/server/service/tile_service.hpp
index d984a0b..afa9a44 100644
--- a/include/server/service/tile_service.hpp
+++ b/include/server/service/tile_service.hpp
@@ -4,8 +4,8 @@
 #include "server/service/base_service.hpp"
 
 #include "engine/status.hpp"
-#include "util/coordinate.hpp"
 #include "osrm/osrm.hpp"
+#include "util/coordinate.hpp"
 
 #include <string>
 #include <vector>
@@ -22,7 +22,8 @@ class TileService final : public BaseService
   public:
     TileService(OSRM &routing_machine) : BaseService(routing_machine) {}
 
-    engine::Status RunQuery(std::size_t prefix_length, std::string &query, ResultT &result) final override;
+    engine::Status
+    RunQuery(std::size_t prefix_length, std::string &query, ResultT &result) final override;
 
     unsigned GetVersion() final override { return 1; }
 };
diff --git a/include/server/service/trip_service.hpp b/include/server/service/trip_service.hpp
index d8dfed4..7dd6fbf 100644
--- a/include/server/service/trip_service.hpp
+++ b/include/server/service/trip_service.hpp
@@ -4,8 +4,8 @@
 #include "server/service/base_service.hpp"
 
 #include "engine/status.hpp"
-#include "util/coordinate.hpp"
 #include "osrm/osrm.hpp"
+#include "util/coordinate.hpp"
 
 #include <string>
 #include <vector>
@@ -22,7 +22,8 @@ class TripService final : public BaseService
   public:
     TripService(OSRM &routing_machine) : BaseService(routing_machine) {}
 
-    engine::Status RunQuery(std::size_t prefix_length, std::string &query, ResultT &result) final override;
+    engine::Status
+    RunQuery(std::size_t prefix_length, std::string &query, ResultT &result) final override;
 
     unsigned GetVersion() final override { return 1; }
 };
diff --git a/include/storage/shared_barriers.hpp b/include/storage/shared_barriers.hpp
index 48d305c..d474e27 100644
--- a/include/storage/shared_barriers.hpp
+++ b/include/storage/shared_barriers.hpp
@@ -1,8 +1,8 @@
 #ifndef SHARED_BARRIERS_HPP
 #define SHARED_BARRIERS_HPP
 
-#include <boost/interprocess/sync/named_mutex.hpp>
 #include <boost/interprocess/sync/named_condition.hpp>
+#include <boost/interprocess/sync/named_mutex.hpp>
 
 namespace osrm
 {
diff --git a/include/storage/shared_datatype.hpp b/include/storage/shared_datatype.hpp
index ec1033a..866fc36 100644
--- a/include/storage/shared_datatype.hpp
+++ b/include/storage/shared_datatype.hpp
@@ -28,7 +28,9 @@ struct SharedDataLayout
         GRAPH_NODE_LIST,
         GRAPH_EDGE_LIST,
         COORDINATE_LIST,
+        OSM_NODE_ID_LIST,
         TURN_INSTRUCTION,
+        ENTRY_CLASSID,
         TRAVEL_MODE,
         R_SEARCH_TREE,
         GEOMETRIES_INDEX,
@@ -42,6 +44,11 @@ struct SharedDataLayout
         DATASOURCE_NAME_OFFSETS,
         DATASOURCE_NAME_LENGTHS,
         PROPERTIES,
+        BEARING_CLASSID,
+        BEARING_OFFSETS,
+        BEARING_BLOCKS,
+        BEARING_VALUES,
+        ENTRY_CLASS,
         NUM_BLOCKS
     };
 
diff --git a/include/storage/shared_memory.hpp b/include/storage/shared_memory.hpp
index 9ceef7d..579b17e 100644
--- a/include/storage/shared_memory.hpp
+++ b/include/storage/shared_memory.hpp
@@ -104,8 +104,8 @@ class SharedMemory
             {
                 Remove(key);
             }
-            shm = boost::interprocess::xsi_shared_memory(boost::interprocess::open_or_create, key,
-                                                         size);
+            shm = boost::interprocess::xsi_shared_memory(
+                boost::interprocess::open_or_create, key, size);
 #ifdef __linux__
             if (-1 == shmctl(shm.get_shmid(), SHM_LOCK, nullptr))
             {
@@ -236,7 +236,8 @@ class SharedMemory
         if (0 == size)
         { // read_only
             shm = boost::interprocess::shared_memory_object(
-                boost::interprocess::open_only, key,
+                boost::interprocess::open_only,
+                key,
                 read_write ? boost::interprocess::read_write : boost::interprocess::read_only);
             region = boost::interprocess::mapped_region(
                 shm, read_write ? boost::interprocess::read_write : boost::interprocess::read_only);
@@ -248,8 +249,8 @@ class SharedMemory
             {
                 Remove(key);
             }
-            shm = boost::interprocess::shared_memory_object(boost::interprocess::open_or_create,
-                                                            key, boost::interprocess::read_write);
+            shm = boost::interprocess::shared_memory_object(
+                boost::interprocess::open_or_create, key, boost::interprocess::read_write);
             shm.truncate(size);
             region = boost::interprocess::mapped_region(shm, boost::interprocess::read_write);
 
@@ -290,8 +291,8 @@ class SharedMemory
         bool result = true;
         try
         {
-            boost::interprocess::shared_memory_object shm(boost::interprocess::open_only, key,
-                                                          boost::interprocess::read_write);
+            boost::interprocess::shared_memory_object shm(
+                boost::interprocess::open_only, key, boost::interprocess::read_write);
         }
         catch (...)
         {
diff --git a/include/storage/storage_config.hpp b/include/storage/storage_config.hpp
index 62f2fdd..94e6e01 100644
--- a/include/storage/storage_config.hpp
+++ b/include/storage/storage_config.hpp
@@ -64,6 +64,7 @@ struct StorageConfig final
     boost::filesystem::path datasource_indexes_path;
     boost::filesystem::path names_data_path;
     boost::filesystem::path properties_path;
+    boost::filesystem::path intersection_class_path;
 };
 }
 }
diff --git a/include/util/bearing.hpp b/include/util/bearing.hpp
index f1ba624..9300691 100644
--- a/include/util/bearing.hpp
+++ b/include/util/bearing.hpp
@@ -89,6 +89,13 @@ inline bool CheckInBounds(const int A, const int B, const int range)
         return normalized_B - range <= normalized_A && normalized_A <= normalized_B + range;
     }
 }
+
+inline double reverseBearing(const double bearing)
+{
+    if (bearing >= 180)
+        return bearing - 180.;
+    return bearing + 180;
+}
 }
 }
 }
diff --git a/include/util/cast.hpp b/include/util/cast.hpp
index e78f630..0b4468d 100644
--- a/include/util/cast.hpp
+++ b/include/util/cast.hpp
@@ -1,9 +1,9 @@
 #ifndef CAST_HPP
 #define CAST_HPP
 
-#include <string>
-#include <sstream>
 #include <iomanip>
+#include <sstream>
+#include <string>
 #include <type_traits>
 
 #include <boost/algorithm/string/classification.hpp>
diff --git a/include/util/coordinate.hpp b/include/util/coordinate.hpp
index 662cbb5..97690f2 100644
--- a/include/util/coordinate.hpp
+++ b/include/util/coordinate.hpp
@@ -32,10 +32,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #include <boost/numeric/conversion/cast.hpp>
 
+#include <cstddef>
 #include <iosfwd> //for std::ostream
 #include <string>
 #include <type_traits>
-#include <cstddef>
 
 namespace osrm
 {
@@ -200,8 +200,10 @@ bool operator==(const FloatCoordinate lhs, const FloatCoordinate rhs);
 std::ostream &operator<<(std::ostream &out, const Coordinate coordinate);
 std::ostream &operator<<(std::ostream &out, const FloatCoordinate coordinate);
 
-inline Coordinate::Coordinate(const FloatCoordinate &other) : Coordinate(toFixed(other.lon), toFixed(other.lat)) {}
-
+inline Coordinate::Coordinate(const FloatCoordinate &other)
+    : Coordinate(toFixed(other.lon), toFixed(other.lat))
+{
+}
 }
 }
 
diff --git a/include/util/coordinate_calculation.hpp b/include/util/coordinate_calculation.hpp
index 5096ebe..9edc5bd 100644
--- a/include/util/coordinate_calculation.hpp
+++ b/include/util/coordinate_calculation.hpp
@@ -23,7 +23,6 @@ const constexpr long double RAD_TO_DEGREE = 1. / DEGREE_TO_RAD;
 const constexpr long double EARTH_RADIUS = 6372797.560856;
 }
 
-
 //! Takes the squared euclidean distance of the input coordinates. Does not return meters!
 std::uint64_t squaredEuclideanDistance(const Coordinate lhs, const Coordinate rhs);
 
@@ -32,8 +31,8 @@ double haversineDistance(const Coordinate first_coordinate, const Coordinate sec
 double greatCircleDistance(const Coordinate first_coordinate, const Coordinate second_coordinate);
 
 inline std::pair<double, FloatCoordinate> projectPointOnSegment(const FloatCoordinate &source,
-                                                         const FloatCoordinate &target,
-                                                         const FloatCoordinate &coordinate)
+                                                                const FloatCoordinate &target,
+                                                                const FloatCoordinate &coordinate)
 {
     const FloatCoordinate slope_vector{target.lon - source.lon, target.lat - source.lat};
     const FloatCoordinate rel_coordinate{coordinate.lon - source.lon, coordinate.lat - source.lat};
diff --git a/include/util/dist_table_wrapper.hpp b/include/util/dist_table_wrapper.hpp
index 7ebe3cb..b73796b 100644
--- a/include/util/dist_table_wrapper.hpp
+++ b/include/util/dist_table_wrapper.hpp
@@ -1,11 +1,11 @@
 #ifndef DIST_TABLE_WRAPPER_H
 #define DIST_TABLE_WRAPPER_H
 
-#include <vector>
-#include <utility>
+#include <algorithm>
 #include <boost/assert.hpp>
 #include <cstddef>
-#include <algorithm>
+#include <utility>
+#include <vector>
 
 namespace osrm
 {
diff --git a/include/util/for_each_pair.hpp b/include/util/for_each_pair.hpp
index 86fc75e..d051e65 100644
--- a/include/util/for_each_pair.hpp
+++ b/include/util/for_each_pair.hpp
@@ -1,8 +1,8 @@
 #ifndef FOR_EACH_PAIR_HPP
 #define FOR_EACH_PAIR_HPP
 
-#include <numeric>
 #include <iterator>
+#include <numeric>
 
 namespace osrm
 {
diff --git a/include/util/graph_loader.hpp b/include/util/graph_loader.hpp
index b611e04..7255270 100644
--- a/include/util/graph_loader.hpp
+++ b/include/util/graph_loader.hpp
@@ -1,13 +1,13 @@
 #ifndef GRAPH_LOADER_HPP
 #define GRAPH_LOADER_HPP
 
-#include "util/fingerprint.hpp"
-#include "util/exception.hpp"
-#include "util/simple_logger.hpp"
 #include "extractor/external_memory_node.hpp"
 #include "extractor/node_based_edge.hpp"
 #include "extractor/query_node.hpp"
 #include "extractor/restriction.hpp"
+#include "util/exception.hpp"
+#include "util/fingerprint.hpp"
+#include "util/simple_logger.hpp"
 #include "util/typedefs.hpp"
 
 #include <boost/assert.hpp>
@@ -121,12 +121,13 @@ NodeID loadEdgesFromFile(std::istream &input_stream,
 
 #ifndef NDEBUG
     SimpleLogger().Write() << "Validating loaded edges...";
-    tbb::parallel_sort(edge_list.begin(), edge_list.end(),
-                       [](const extractor::NodeBasedEdge &lhs, const extractor::NodeBasedEdge &rhs)
-                       {
-                           return (lhs.source < rhs.source) ||
-                                  (lhs.source == rhs.source && lhs.target < rhs.target);
-                       });
+    tbb::parallel_sort(
+        edge_list.begin(),
+        edge_list.end(),
+        [](const extractor::NodeBasedEdge &lhs, const extractor::NodeBasedEdge &rhs) {
+            return (lhs.source < rhs.source) ||
+                   (lhs.source == rhs.source && lhs.target < rhs.target);
+        });
     for (auto i = 1u; i < edge_list.size(); ++i)
     {
         const auto &edge = edge_list[i];
diff --git a/include/util/guidance/bearing_class.hpp b/include/util/guidance/bearing_class.hpp
new file mode 100644
index 0000000..1f2f9ac
--- /dev/null
+++ b/include/util/guidance/bearing_class.hpp
@@ -0,0 +1,79 @@
+#ifndef OSRM_UTIL_GUIDANCE_BEARING_CLASS_HPP_
+#define OSRM_UTIL_GUIDANCE_BEARING_CLASS_HPP_
+
+#include <cstddef>
+#include <cstdint>
+#include <functional>
+#include <vector>
+
+#include <boost/functional/hash.hpp>
+
+#include "util/typedefs.hpp"
+
+namespace osrm
+{
+namespace util
+{
+namespace guidance
+{
+class BearingClass;
+} // namespace guidance
+} // namespace util
+} // namespace osrm
+
+namespace std
+{
+template <> struct hash<::osrm::util::guidance::BearingClass>
+{
+    inline std::size_t operator()(const ::osrm::util::guidance::BearingClass &bearing_class) const;
+};
+} // namespace std
+
+namespace osrm
+{
+namespace util
+{
+namespace guidance
+{
+
+class BearingClass
+{
+  public:
+    // Add a bearing to the set
+    void add(const DiscreteBearing bearing);
+
+    // hashing
+    bool operator==(const BearingClass &other) const;
+
+    // sorting
+    bool operator<(const BearingClass &other) const;
+
+    const std::vector<DiscreteBearing> &getAvailableBearings() const;
+
+    std::size_t findMatchingBearing(const double bearing) const;
+
+    const constexpr static double discrete_step_size = 360. / 24.;
+    static DiscreteBearing getDiscreteBearing(const double bearing);
+
+  private:
+    std::vector<DiscreteBearing> available_bearings;
+
+    // allow hash access to internal representation
+    friend std::size_t std::hash<BearingClass>::operator()(const BearingClass &) const;
+};
+
+} // namespace guidance
+} // namespace util
+} // namespace osrm
+
+// make Bearing Class hasbable
+namespace std
+{
+inline size_t hash<::osrm::util::guidance::BearingClass>::
+operator()(const ::osrm::util::guidance::BearingClass &bearing_class) const
+{
+    return boost::hash_value(bearing_class.available_bearings);
+}
+} // namespace std
+
+#endif /* OSRM_UTIL_GUIDANCE_BEARING_CLASS_HPP_ */
diff --git a/include/util/guidance/entry_class.hpp b/include/util/guidance/entry_class.hpp
new file mode 100644
index 0000000..422c2d2
--- /dev/null
+++ b/include/util/guidance/entry_class.hpp
@@ -0,0 +1,80 @@
+#ifndef OSRM_UTIL_GUIDANCE_ENTRY_CLASS_HPP_
+#define OSRM_UTIL_GUIDANCE_ENTRY_CLASS_HPP_
+
+#include <cstddef>
+#include <cstdint>
+#include <functional>
+
+#include <bitset>
+
+namespace osrm
+{
+namespace util
+{
+namespace guidance
+{
+class EntryClass;
+} // namespace guidance
+} // namespace util
+} // namespace osrm
+
+namespace std
+{
+template <> struct hash<::osrm::util::guidance::EntryClass>
+{
+    inline std::size_t operator()(const ::osrm::util::guidance::EntryClass &entry_class) const;
+};
+} // namespace std
+
+namespace osrm
+{
+namespace util
+{
+namespace guidance
+{
+
+class EntryClass
+{
+    using FlagBaseType = std::uint32_t;
+
+  public:
+    EntryClass();
+
+    // we are hiding the access to the flags behind a protection wall, to make sure the bit logic
+    // isn't tempered with. zero based indexing
+    void activate(std::uint32_t index);
+
+    // check whether a certain turn allows entry
+    bool allowsEntry(std::uint32_t index) const;
+
+    // required for hashing
+    bool operator==(const EntryClass &) const;
+
+    // sorting
+    bool operator<(const EntryClass &) const;
+
+  private:
+    // given a list of possible discrete angles, the available angles flag indicates the presence of
+    // a given turn at the intersection
+    FlagBaseType enabled_entries_flags;
+
+    // allow hash access to internal representation
+    friend std::size_t std::hash<EntryClass>::operator()(const EntryClass &) const;
+};
+
+} // namespace guidance
+} // namespace utilr
+} // namespace osrm
+
+// make Entry Class hasbable
+namespace std
+{
+inline size_t hash<::osrm::util::guidance::EntryClass>::
+operator()(const ::osrm::util::guidance::EntryClass &entry_class) const
+{
+    return hash<::osrm::util::guidance::EntryClass::FlagBaseType>()(
+        entry_class.enabled_entries_flags);
+}
+} // namespace std
+
+#endif /* OSRM_UTIL_GUIDANCE_ENTRY_CLASS_HPP_ */
diff --git a/include/util/guidance/toolkit.hpp b/include/util/guidance/toolkit.hpp
index 24072a8..2c458ab 100644
--- a/include/util/guidance/toolkit.hpp
+++ b/include/util/guidance/toolkit.hpp
@@ -4,6 +4,14 @@
 /* A set of tools required for guidance in both pre and post-processing */
 
 #include "extractor/guidance/turn_instruction.hpp"
+#include "engine/guidance/route_step.hpp"
+#include "engine/phantom_node.hpp"
+#include "util/guidance/bearing_class.hpp"
+#include "util/guidance/entry_class.hpp"
+#include "util/simple_logger.hpp"
+
+#include <algorithm>
+#include <vector>
 
 namespace osrm
 {
@@ -18,7 +26,7 @@ inline double angularDeviation(const double angle, const double from)
     return std::min(360 - deviation, deviation);
 }
 
-inline extractor::guidance::DirectionModifier getTurnDirection(const double angle)
+inline extractor::guidance::DirectionModifier::Enum getTurnDirection(const double angle)
 {
     // An angle of zero is a u-turn
     // 180 goes perfectly straight
@@ -41,8 +49,24 @@ inline extractor::guidance::DirectionModifier getTurnDirection(const double angl
     return extractor::guidance::DirectionModifier::UTurn;
 }
 
-} /* namespace guidance */
-} /* namespace util */
-} /* namespace osrm */
+// swaps left <-> right modifier types
+inline extractor::guidance::DirectionModifier::Enum
+mirrorDirectionModifier(const extractor::guidance::DirectionModifier::Enum modifier)
+{
+    const constexpr extractor::guidance::DirectionModifier::Enum results[] = {
+        extractor::guidance::DirectionModifier::UTurn,
+        extractor::guidance::DirectionModifier::SharpLeft,
+        extractor::guidance::DirectionModifier::Left,
+        extractor::guidance::DirectionModifier::SlightLeft,
+        extractor::guidance::DirectionModifier::Straight,
+        extractor::guidance::DirectionModifier::SlightRight,
+        extractor::guidance::DirectionModifier::Right,
+        extractor::guidance::DirectionModifier::SharpRight};
+    return results[modifier];
+}
+
+} // namespace guidance
+} // namespace util
+} // namespace osrm
 
 #endif /* OSRM_UTIL_GUIDANCE_TOOLKIT_HPP_ */
diff --git a/include/util/io.hpp b/include/util/io.hpp
index 70ab64a..2f3177f 100644
--- a/include/util/io.hpp
+++ b/include/util/io.hpp
@@ -8,8 +8,8 @@
 #include <cstddef>
 #include <cstdint>
 
-#include <fstream>
 #include <bitset>
+#include <fstream>
 #include <vector>
 
 #include "util/fingerprint.hpp"
@@ -52,6 +52,16 @@ bool serializeVector(const std::string &filename, const std::vector<simple_type>
 }
 
 template <typename simple_type>
+bool serializeVector(std::ostream &stream, const std::vector<simple_type> &data)
+{
+    std::uint64_t count = data.size();
+    stream.write(reinterpret_cast<const char *>(&count), sizeof(count));
+    if (!data.empty())
+        stream.write(reinterpret_cast<const char *>(&data[0]), sizeof(simple_type) * count);
+    return static_cast<bool>(stream);
+}
+
+template <typename simple_type>
 bool deserializeVector(const std::string &filename, std::vector<simple_type> &data)
 {
     std::ifstream stream(filename, std::ios::binary);
@@ -67,6 +77,17 @@ bool deserializeVector(const std::string &filename, std::vector<simple_type> &da
     return static_cast<bool>(stream);
 }
 
+template <typename simple_type>
+bool deserializeVector(std::istream &stream, std::vector<simple_type> &data)
+{
+    std::uint64_t count = 0;
+    stream.read(reinterpret_cast<char *>(&count), sizeof(count));
+    data.resize(count);
+    if (count)
+        stream.read(reinterpret_cast<char *>(&data[0]), sizeof(simple_type) * count);
+    return static_cast<bool>(stream);
+}
+
 inline bool serializeFlags(const boost::filesystem::path &path, const std::vector<bool> &flags)
 {
     // TODO this should be replaced with a FILE-based write using error checking
diff --git a/include/util/iso_8601_duration_parser.hpp b/include/util/iso_8601_duration_parser.hpp
deleted file mode 100644
index 91f9d2d..0000000
--- a/include/util/iso_8601_duration_parser.hpp
+++ /dev/null
@@ -1,82 +0,0 @@
-#ifndef ISO_8601_DURATION_PARSER_HPP
-#define ISO_8601_DURATION_PARSER_HPP
-
-#include <boost/bind.hpp>
-#include <boost/spirit/include/qi.hpp>
-#include <boost/spirit/include/qi_action.hpp>
-
-namespace osrm
-{
-namespace util
-{
-
-namespace qi = boost::spirit::qi;
-
-template <typename Iterator> struct iso_8601_grammar : qi::grammar<Iterator>
-{
-    iso_8601_grammar()
-        : iso_8601_grammar::base_type(iso_period), temp(0), hours(0), minutes(0), seconds(0)
-    {
-        iso_period = qi::lit('P') >> qi::lit('T') >>
-                     ((value >> hour >> value >> minute >> value >> second) |
-                      (value >> hour >> value >> minute) | (value >> hour >> value >> second) |
-                      (value >> hour) | (value >> minute >> value >> second) | (value >> minute) |
-                      (value >> second));
-
-        value = qi::uint_[boost::bind(&iso_8601_grammar<Iterator>::set_temp, this, ::_1)];
-        second = (qi::lit('s') |
-                  qi::lit('S'))[boost::bind(&iso_8601_grammar<Iterator>::set_seconds, this)];
-        minute = (qi::lit('m') |
-                  qi::lit('M'))[boost::bind(&iso_8601_grammar<Iterator>::set_minutes, this)];
-        hour = (qi::lit('h') |
-                qi::lit('H'))[boost::bind(&iso_8601_grammar<Iterator>::set_hours, this)];
-    }
-
-    qi::rule<Iterator> iso_period;
-    qi::rule<Iterator, std::string()> value, hour, minute, second;
-
-    unsigned temp;
-    unsigned hours;
-    unsigned minutes;
-    unsigned seconds;
-
-    void set_temp(unsigned number) { temp = number; }
-
-    void set_hours()
-    {
-        if (temp < 24)
-        {
-            hours = temp;
-        }
-    }
-
-    void set_minutes()
-    {
-        if (temp < 60)
-        {
-            minutes = temp;
-        }
-    }
-
-    void set_seconds()
-    {
-        if (temp < 60)
-        {
-            seconds = temp;
-        }
-    }
-
-    unsigned get_duration() const
-    {
-        unsigned temp = (3600 * hours + 60 * minutes + seconds);
-        if (temp == 0)
-        {
-            temp = std::numeric_limits<unsigned>::max();
-        }
-        return temp;
-    }
-};
-}
-}
-
-#endif // ISO_8601_DURATION_PARSER_HPP
diff --git a/include/util/json_container.hpp b/include/util/json_container.hpp
index 9a383bb..302b71a 100644
--- a/include/util/json_container.hpp
+++ b/include/util/json_container.hpp
@@ -33,10 +33,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #include <variant/variant.hpp>
 
-#include <vector>
 #include <string>
-#include <utility>
 #include <unordered_map>
+#include <utility>
+#include <vector>
 
 namespace osrm
 {
diff --git a/include/util/json_deep_compare.hpp b/include/util/json_deep_compare.hpp
index 32b0bde..f5a6c8a 100644
--- a/include/util/json_deep_compare.hpp
+++ b/include/util/json_deep_compare.hpp
@@ -1,8 +1,8 @@
 #ifndef UTIL_JSON_DEEP_COMPARE_HPP
 #define UTIL_JSON_DEEP_COMPARE_HPP
 
-#include "util/json_container.hpp"
 #include "util/integer_range.hpp"
+#include "util/json_container.hpp"
 
 #include <boost/assert.hpp>
 
@@ -24,22 +24,26 @@ struct Comparator : mapbox::util::static_visitor<bool>
     {
     }
 
-    bool operator()(const String &lhs, const String &rhs) const {
-      bool is_same = lhs.value == rhs.value;
-      if (!is_same)
-      {
-        reason = lhs_path + " (= \"" + lhs.value + "\") != " + rhs_path + " (= \"" + rhs.value + "\")";
-      }
-      return is_same;
+    bool operator()(const String &lhs, const String &rhs) const
+    {
+        bool is_same = lhs.value == rhs.value;
+        if (!is_same)
+        {
+            reason = lhs_path + " (= \"" + lhs.value + "\") != " + rhs_path + " (= \"" + rhs.value +
+                     "\")";
+        }
+        return is_same;
     }
 
-    bool operator()(const Number &lhs, const Number &rhs) const {
-      bool is_same = lhs.value == rhs.value;
-      if (!is_same)
-      {
-        reason = lhs_path + " (= " + std::to_string(lhs.value) + ") != " + rhs_path + " (= " + std::to_string(rhs.value) + ")";
-      }
-      return is_same;
+    bool operator()(const Number &lhs, const Number &rhs) const
+    {
+        bool is_same = lhs.value == rhs.value;
+        if (!is_same)
+        {
+            reason = lhs_path + " (= " + std::to_string(lhs.value) + ") != " + rhs_path + " (= " +
+                     std::to_string(rhs.value) + ")";
+        }
+        return is_same;
     }
 
     bool operator()(const Object &lhs, const Object &rhs) const
@@ -82,7 +86,8 @@ struct Comparator : mapbox::util::static_visitor<bool>
             const auto &rhs_child = rhs.values.find(key)->second;
             const auto &lhs_child = lhs.values.find(key)->second;
             auto is_same = mapbox::util::apply_visitor(
-                Comparator(reason, lhs_path + "." + key, rhs_path + "." + key), lhs_child,
+                Comparator(reason, lhs_path + "." + key, rhs_path + "." + key),
+                lhs_child,
                 rhs_child);
             if (!is_same)
             {
@@ -103,10 +108,12 @@ struct Comparator : mapbox::util::static_visitor<bool>
 
         for (auto i = 0UL; i < lhs.values.size(); ++i)
         {
-            auto is_same = mapbox::util::apply_visitor(
-                Comparator(reason, lhs_path + "[" + std::to_string(i) + "]",
-                           rhs_path + "[" + std::to_string(i) + "]"),
-                lhs.values[i], rhs.values[i]);
+            auto is_same =
+                mapbox::util::apply_visitor(Comparator(reason,
+                                                       lhs_path + "[" + std::to_string(i) + "]",
+                                                       rhs_path + "[" + std::to_string(i) + "]"),
+                                            lhs.values[i],
+                                            rhs.values[i]);
             if (!is_same)
             {
                 return false;
@@ -148,8 +155,8 @@ struct Comparator : mapbox::util::static_visitor<bool>
 
 inline bool compare(const Value &reference, const Value &result, std::string &reason)
 {
-    return mapbox::util::apply_visitor(Comparator(reason, "reference", "result"), reference,
-                                       result);
+    return mapbox::util::apply_visitor(
+        Comparator(reason, "reference", "result"), reference, result);
 }
 }
 }
diff --git a/include/util/json_renderer.hpp b/include/util/json_renderer.hpp
index a1ac7a0..f83524b 100644
--- a/include/util/json_renderer.hpp
+++ b/include/util/json_renderer.hpp
@@ -9,10 +9,10 @@
 
 #include "osrm/json_container.hpp"
 
-#include <ostream>
-#include <vector>
 #include <iterator>
+#include <ostream>
 #include <string>
+#include <vector>
 
 namespace osrm
 {
diff --git a/include/util/lua_util.hpp b/include/util/lua_util.hpp
index 4af7473..a129df9 100644
--- a/include/util/lua_util.hpp
+++ b/include/util/lua_util.hpp
@@ -2,8 +2,8 @@
 #define LUA_UTIL_HPP
 
 extern "C" {
-#include <lua.h>
 #include <lauxlib.h>
+#include <lua.h>
 #include <lualib.h>
 }
 
diff --git a/include/util/matrix_graph_wrapper.hpp b/include/util/matrix_graph_wrapper.hpp
index 3b4ee5c..3dc9797 100644
--- a/include/util/matrix_graph_wrapper.hpp
+++ b/include/util/matrix_graph_wrapper.hpp
@@ -1,9 +1,9 @@
 #ifndef MATRIX_GRAPH_WRAPPER_H
 #define MATRIX_GRAPH_WRAPPER_H
 
-#include <vector>
 #include <cstddef>
 #include <iterator>
+#include <vector>
 
 #include "util/typedefs.hpp"
 
@@ -20,7 +20,9 @@ template <typename T> class MatrixGraphWrapper
 {
   public:
     MatrixGraphWrapper(std::vector<T> table, const std::size_t number_of_nodes)
-        : table_(std::move(table)), number_of_nodes_(number_of_nodes){}
+        : table_(std::move(table)), number_of_nodes_(number_of_nodes)
+    {
+    }
 
     std::size_t GetNumberOfNodes() const { return number_of_nodes_; }
 
diff --git a/include/util/node_based_graph.hpp b/include/util/node_based_graph.hpp
index 463fbb1..e2ac564 100644
--- a/include/util/node_based_graph.hpp
+++ b/include/util/node_based_graph.hpp
@@ -1,10 +1,10 @@
 #ifndef NODE_BASED_GRAPH_HPP
 #define NODE_BASED_GRAPH_HPP
 
-#include "util/dynamic_graph.hpp"
+#include "extractor/guidance/classification_data.hpp"
 #include "extractor/node_based_edge.hpp"
+#include "util/dynamic_graph.hpp"
 #include "util/graph_utils.hpp"
-#include "extractor/guidance/classification_data.hpp"
 
 #include <tbb/parallel_sort.h>
 
@@ -68,9 +68,9 @@ NodeBasedDynamicGraphFromEdges(std::size_t number_of_nodes,
                                const std::vector<extractor::NodeBasedEdge> &input_edge_list)
 {
     auto edges_list = directedEdgesFromCompressed<NodeBasedDynamicGraph::InputEdge>(
-        input_edge_list, [](NodeBasedDynamicGraph::InputEdge &output_edge,
-                            const extractor::NodeBasedEdge &input_edge)
-        {
+        input_edge_list,
+        [](NodeBasedDynamicGraph::InputEdge &output_edge,
+           const extractor::NodeBasedEdge &input_edge) {
             output_edge.data.distance = static_cast<int>(input_edge.weight);
             BOOST_ASSERT(output_edge.data.distance > 0);
 
diff --git a/include/util/packed_vector.hpp b/include/util/packed_vector.hpp
new file mode 100644
index 0000000..d4a5ee8
--- /dev/null
+++ b/include/util/packed_vector.hpp
@@ -0,0 +1,195 @@
+#ifndef PACKED_VECTOR_HPP
+#define PACKED_VECTOR_HPP
+
+#include "util/shared_memory_vector_wrapper.hpp"
+#include "util/typedefs.hpp"
+
+#include <cmath>
+#include <vector>
+
+namespace osrm
+{
+namespace util
+{
+
+
+/**
+ * Since OSM node IDs are (at the time of writing) not quite yet overflowing 32 bits, and
+ * will predictably be containable within 33 bits for a long time, the following packs
+ * 64-bit OSM IDs as 33-bit numbers within a 64-bit vector.
+ *
+ * NOTE: this type is templated for future use, but will require a slight refactor to
+ * configure BITSIZE and ELEMSIZE
+ */
+template <typename T, bool UseSharedMemory=false> class PackedVector
+{
+    static const constexpr std::size_t BITSIZE = 33;
+    static const constexpr std::size_t ELEMSIZE = 64;
+    static const constexpr std::size_t PACKSIZE = BITSIZE * ELEMSIZE;
+
+  public:
+
+    /**
+     * Returns the size of the packed vector datastructure with `elements` packed elements (the size of
+     * its underlying uint64 vector)
+     */
+    inline static std::size_t elements_to_blocks(std::size_t elements)
+    {
+        return std::ceil(static_cast<double>(elements) * BITSIZE / ELEMSIZE);
+    }
+
+    void push_back(T incoming_node_id)
+    {
+        std::uint64_t node_id = static_cast<std::uint64_t>(incoming_node_id);
+
+        // mask incoming values, just in case they are > bitsize
+        const std::uint64_t incoming_mask = static_cast<std::uint64_t>(pow(2, BITSIZE)) - 1;
+        node_id = node_id & incoming_mask;
+
+        const std::size_t available = (PACKSIZE - BITSIZE * num_elements) % ELEMSIZE;
+
+        if (available == 0)
+        {
+            // insert ID at the left side of this element
+            std::uint64_t at_left = node_id << (ELEMSIZE - BITSIZE);
+
+            add_last_elem(at_left);
+        }
+        else if (available >= BITSIZE)
+        {
+            // insert ID somewhere in the middle of this element; ID can be contained
+            // entirely within one element
+            const std::uint64_t shifted = node_id << (available - BITSIZE);
+
+            replace_last_elem(vec_back() | shifted);
+        }
+        else
+        {
+            // ID will be split between the end of this element and the beginning
+            // of the next element
+            const std::uint64_t left = node_id >> (BITSIZE - available);
+
+            std::uint64_t right = node_id << (ELEMSIZE - (BITSIZE - available));
+
+            replace_last_elem(vec_back() | left);
+            add_last_elem(right);
+        }
+
+        num_elements++;
+    }
+
+    T at(const std::size_t &a_index) const
+    {
+        BOOST_ASSERT(a_index < num_elements);
+
+        const std::size_t pack_group = trunc(a_index / ELEMSIZE);
+        const std::size_t pack_index = (a_index + ELEMSIZE) % ELEMSIZE;
+        const std::size_t left_index = (PACKSIZE - BITSIZE * pack_index) % ELEMSIZE;
+
+        const bool back_half = pack_index >= BITSIZE;
+        const std::size_t index = pack_group * BITSIZE + trunc(pack_index / BITSIZE) +
+                                  trunc((pack_index - back_half) / 2);
+
+        BOOST_ASSERT(index < vec.size());
+        const std::uint64_t elem = static_cast<std::uint64_t>(vec.at(index));
+
+        if (left_index == 0)
+        {
+            // ID is at the far left side of this element
+            return static_cast<T>(elem >> (ELEMSIZE - BITSIZE));
+        }
+        else if (left_index >= BITSIZE)
+        {
+            // ID is entirely contained within this element
+            const std::uint64_t at_right = elem >> (left_index - BITSIZE);
+            const std::uint64_t left_mask = static_cast<std::uint64_t>(pow(2, BITSIZE)) - 1;
+            return static_cast<T>(at_right & left_mask);
+        }
+        else
+        {
+            // ID is split between this and the next element
+            const std::uint64_t left_mask = static_cast<std::uint64_t>(pow(2, left_index)) - 1;
+            const std::uint64_t left_side = (elem & left_mask) << (BITSIZE - left_index);
+
+            BOOST_ASSERT(index < vec.size() - 1);
+            const std::uint64_t next_elem = static_cast<std::uint64_t>(vec.at(index + 1));
+
+            const std::uint64_t right_side = next_elem >> (ELEMSIZE - (BITSIZE - left_index));
+            return static_cast<T>(left_side | right_side);
+        }
+    }
+
+    std::size_t size() const { return num_elements; }
+
+    template <bool enabled = UseSharedMemory>
+    void reserve(typename std::enable_if<!enabled, std::size_t>::type capacity)
+    {
+        vec.reserve(elements_to_blocks(capacity));
+    }
+
+    template <bool enabled = UseSharedMemory>
+    void reset(typename std::enable_if<enabled, std::uint64_t>::type *ptr,
+               typename std::enable_if<enabled, std::size_t>::type size)
+    {
+        vec.reset(ptr, size);
+    }
+
+    template <bool enabled = UseSharedMemory>
+    void set_number_of_entries(typename std::enable_if<enabled, std::size_t>::type count)
+    {
+        num_elements = count;
+    }
+
+    std::size_t capacity() const
+    {
+        return std::floor(static_cast<double>(vec.capacity()) * ELEMSIZE / BITSIZE);
+    }
+
+  private:
+    typename util::ShM<std::uint64_t, UseSharedMemory>::vector vec;
+
+    std::size_t num_elements = 0;
+
+    signed cursor = -1;
+
+    template <bool enabled = UseSharedMemory>
+    void replace_last_elem(typename std::enable_if<enabled, std::uint64_t>::type last_elem)
+    {
+        vec[cursor] = last_elem;
+    }
+
+    template <bool enabled = UseSharedMemory>
+    void replace_last_elem(typename std::enable_if<!enabled, std::uint64_t>::type last_elem)
+    {
+        vec.back() = last_elem;
+    }
+
+    template <bool enabled = UseSharedMemory>
+    void add_last_elem(typename std::enable_if<enabled, std::uint64_t>::type last_elem)
+    {
+        vec[cursor + 1] = last_elem;
+        cursor++;
+    }
+
+    template <bool enabled = UseSharedMemory>
+    void add_last_elem(typename std::enable_if<!enabled, std::uint64_t>::type last_elem)
+    {
+        vec.push_back(last_elem);
+    }
+
+    template <bool enabled = UseSharedMemory>
+    std::uint64_t vec_back(typename std::enable_if<enabled>::type * = nullptr)
+    {
+        return vec[cursor];
+    }
+
+    template <bool enabled = UseSharedMemory>
+    std::uint64_t vec_back(typename std::enable_if<!enabled>::type * = nullptr)
+    {
+        return vec.back();
+    }
+};
+}
+}
+
+#endif /* PACKED_VECTOR_HPP */
diff --git a/include/util/percent.hpp b/include/util/percent.hpp
index 31e328f..b65c876 100644
--- a/include/util/percent.hpp
+++ b/include/util/percent.hpp
@@ -1,8 +1,8 @@
 #ifndef PERCENT_HPP
 #define PERCENT_HPP
 
-#include <iostream>
 #include <atomic>
+#include <iostream>
 
 namespace osrm
 {
diff --git a/include/util/rectangle.hpp b/include/util/rectangle.hpp
index ca7d84b..712818e 100644
--- a/include/util/rectangle.hpp
+++ b/include/util/rectangle.hpp
@@ -7,10 +7,10 @@
 
 #include "osrm/coordinate.hpp"
 
-#include <iomanip>
 #include <algorithm>
-#include <utility>
+#include <iomanip>
 #include <limits>
+#include <utility>
 
 #include <cstdint>
 
@@ -136,20 +136,20 @@ struct RectangleInt2D
                 location, Coordinate(max_lon, location.lat));
             break;
         case NORTH_EAST:
-            min_dist =
-                coordinate_calculation::squaredEuclideanDistance(location, Coordinate(max_lon, max_lat));
+            min_dist = coordinate_calculation::squaredEuclideanDistance(
+                location, Coordinate(max_lon, max_lat));
             break;
         case NORTH_WEST:
-            min_dist =
-                coordinate_calculation::squaredEuclideanDistance(location, Coordinate(min_lon, max_lat));
+            min_dist = coordinate_calculation::squaredEuclideanDistance(
+                location, Coordinate(min_lon, max_lat));
             break;
         case SOUTH_EAST:
-            min_dist =
-                coordinate_calculation::squaredEuclideanDistance(location, Coordinate(max_lon, min_lat));
+            min_dist = coordinate_calculation::squaredEuclideanDistance(
+                location, Coordinate(max_lon, min_lat));
             break;
         case SOUTH_WEST:
-            min_dist =
-                coordinate_calculation::squaredEuclideanDistance(location, Coordinate(min_lon, min_lat));
+            min_dist = coordinate_calculation::squaredEuclideanDistance(
+                location, Coordinate(min_lon, min_lat));
             break;
         default:
             break;
@@ -167,6 +167,14 @@ struct RectangleInt2D
         return lons_contained && lats_contained;
     }
 
+    bool IsValid() const
+    {
+        return min_lon != FixedLongitude(std::numeric_limits<std::int32_t>::max()) &&
+               max_lon != FixedLongitude(std::numeric_limits<std::int32_t>::min()) &&
+               min_lat != FixedLatitude(std::numeric_limits<std::int32_t>::max()) &&
+               max_lat != FixedLatitude(std::numeric_limits<std::int32_t>::min());
+    }
+
     friend std::ostream &operator<<(std::ostream &out, const RectangleInt2D &rect);
 };
 inline std::ostream &operator<<(std::ostream &out, const RectangleInt2D &rect)
diff --git a/include/util/shared_memory_vector_wrapper.hpp b/include/util/shared_memory_vector_wrapper.hpp
index 9245d4c..2fec536 100644
--- a/include/util/shared_memory_vector_wrapper.hpp
+++ b/include/util/shared_memory_vector_wrapper.hpp
@@ -3,13 +3,15 @@
 
 #include <boost/assert.hpp>
 
+#include "util/simple_logger.hpp"
+
 #include <cstddef>
 
 #include <algorithm>
 #include <iterator>
 #include <type_traits>
-#include <vector>
 #include <utility>
+#include <vector>
 
 namespace osrm
 {
diff --git a/include/util/static_graph.hpp b/include/util/static_graph.hpp
index a4fa2d7..7b4fa8e 100644
--- a/include/util/static_graph.hpp
+++ b/include/util/static_graph.hpp
@@ -1,9 +1,9 @@
 #ifndef STATIC_GRAPH_HPP
 #define STATIC_GRAPH_HPP
 
+#include "util/integer_range.hpp"
 #include "util/percent.hpp"
 #include "util/shared_memory_vector_wrapper.hpp"
-#include "util/integer_range.hpp"
 #include "util/typedefs.hpp"
 
 #include <boost/assert.hpp>
diff --git a/include/util/static_rtree.hpp b/include/util/static_rtree.hpp
index 2d9f63d..12da61c 100644
--- a/include/util/static_rtree.hpp
+++ b/include/util/static_rtree.hpp
@@ -1,15 +1,15 @@
 #ifndef STATIC_RTREE_HPP
 #define STATIC_RTREE_HPP
 
+#include "util/bearing.hpp"
+#include "util/coordinate_calculation.hpp"
 #include "util/deallocating_vector.hpp"
+#include "util/exception.hpp"
 #include "util/hilbert_value.hpp"
+#include "util/integer_range.hpp"
 #include "util/rectangle.hpp"
 #include "util/shared_memory_vector_wrapper.hpp"
-#include "util/bearing.hpp"
-#include "util/exception.hpp"
-#include "util/integer_range.hpp"
 #include "util/typedefs.hpp"
-#include "util/coordinate_calculation.hpp"
 #include "util/web_mercator.hpp"
 
 #include "osrm/coordinate.hpp"
@@ -22,8 +22,6 @@
 #include <tbb/parallel_for.h>
 #include <tbb/parallel_sort.h>
 
-#include <variant/variant.hpp>
-
 #include <algorithm>
 #include <array>
 #include <limits>
@@ -32,6 +30,16 @@
 #include <string>
 #include <vector>
 
+// An extended alignment is implementation-defined, so use compiler attributes
+// until alignas(LEAF_PAGE_SIZE) is compiler-independent.
+#if defined(_MSC_VER)
+#define ALIGNED(x) __declspec(align(x))
+#elif defined(__GNUC__)
+#define ALIGNED(x) __attribute__((aligned(x)))
+#else
+#define ALIGNED(x)
+#endif
+
 namespace osrm
 {
 namespace util
@@ -52,9 +60,10 @@ class StaticRTree
     using EdgeData = EdgeDataT;
     using CoordinateList = CoordinateListT;
 
-    static_assert(LEAF_PAGE_SIZE >= sizeof(uint32_t) + sizeof(EdgeDataT), "LEAF_PAGE_SIZE is too small");
-    static_assert(((LEAF_PAGE_SIZE - 1) & LEAF_PAGE_SIZE) == 0, "LEAF_PAGE_SIZE is not a power of 2");
-    static constexpr std::uint32_t LEAF_NODE_SIZE = (LEAF_PAGE_SIZE - sizeof(uint32_t)) / sizeof(EdgeDataT);
+    static_assert(LEAF_PAGE_SIZE >= sizeof(uint32_t) + sizeof(EdgeDataT), "page size is too small");
+    static_assert(((LEAF_PAGE_SIZE - 1) & LEAF_PAGE_SIZE) == 0, "page size is not a power of 2");
+    static constexpr std::uint32_t LEAF_NODE_SIZE =
+        (LEAF_PAGE_SIZE - sizeof(uint32_t) - sizeof(Rectangle)) / sizeof(EdgeDataT);
 
     struct CandidateSegment
     {
@@ -62,21 +71,28 @@ class StaticRTree
         EdgeDataT data;
     };
 
+    struct TreeIndex
+    {
+        TreeIndex() : index(0), is_leaf(false) {}
+        TreeIndex(std::size_t index, bool is_leaf) : index(index), is_leaf(is_leaf) {}
+        std::uint32_t index : 31;
+        std::uint32_t is_leaf : 1;
+    };
+
     struct TreeNode
     {
-        TreeNode() : child_count(0), child_is_on_disk(false) {}
+        TreeNode() : child_count(0) {}
+        std::uint32_t child_count;
         Rectangle minimum_bounding_rectangle;
-        std::uint32_t child_count : 31;
-        bool child_is_on_disk : 1;
-        std::uint32_t children[BRANCHING_FACTOR];
+        TreeIndex children[BRANCHING_FACTOR];
     };
 
-    struct LeafNode
+    struct ALIGNED(LEAF_PAGE_SIZE) LeafNode
     {
         LeafNode() : object_count(0), objects() {}
         std::uint32_t object_count;
+        Rectangle minimum_bounding_rectangle;
         std::array<EdgeDataT, LEAF_NODE_SIZE> objects;
-        unsigned char leaf_page_padding[LEAF_PAGE_SIZE - sizeof(std::uint32_t) - sizeof(std::array<EdgeDataT, LEAF_NODE_SIZE>)];
     };
     static_assert(sizeof(LeafNode) == LEAF_PAGE_SIZE, "LeafNode size does not fit the page size");
 
@@ -100,11 +116,28 @@ class StaticRTree
         }
     };
 
-    struct TreeIndex { std::uint32_t index; };
-    struct SegmentIndex { std::uint32_t index; std::uint32_t object; Coordinate fixed_projected_coordinate; };
-    using QueryNodeType = mapbox::util::variant<TreeIndex, SegmentIndex>;
     struct QueryCandidate
     {
+        QueryCandidate(std::uint64_t squared_min_dist, TreeIndex tree_index)
+            : squared_min_dist(squared_min_dist), tree_index(tree_index),
+              segment_index(std::numeric_limits<std::uint32_t>::max())
+        {
+        }
+
+        QueryCandidate(std::uint64_t squared_min_dist,
+                       TreeIndex tree_index,
+                       std::uint32_t segment_index,
+                       const Coordinate &coordinate)
+            : squared_min_dist(squared_min_dist), tree_index(tree_index),
+              segment_index(segment_index), fixed_projected_coordinate(coordinate)
+        {
+        }
+
+        inline bool is_segment() const
+        {
+            return segment_index != std::numeric_limits<std::uint32_t>::max();
+        }
+
         inline bool operator<(const QueryCandidate &other) const
         {
             // Attn: this is reversed order. std::pq is a max pq!
@@ -112,11 +145,13 @@ class StaticRTree
         }
 
         std::uint64_t squared_min_dist;
-        QueryNodeType node;
+        TreeIndex tree_index;
+        std::uint32_t segment_index;
+        Coordinate fixed_projected_coordinate;
     };
 
     typename ShM<TreeNode, UseSharedMemory>::vector m_search_tree;
-    const CoordinateListT& m_coordinate_list;
+    const CoordinateListT &m_coordinate_list;
 
     boost::iostreams::mapped_file_source m_leaves_region;
     // read-only view of leaves
@@ -132,7 +167,7 @@ class StaticRTree
                          const std::string &tree_node_filename,
                          const std::string &leaf_node_filename,
                          const std::vector<CoordinateT> &coordinate_list)
-    : m_coordinate_list(coordinate_list)
+        : m_coordinate_list(coordinate_list)
     {
         const uint64_t element_count = input_data_vector.size();
         std::vector<WrappedInputElement> input_wrapper_vector(element_count);
@@ -140,11 +175,11 @@ class StaticRTree
         // generate auxiliary vector of hilbert-values
         tbb::parallel_for(
             tbb::blocked_range<uint64_t>(0, element_count),
-            [&input_data_vector, &input_wrapper_vector,
-             this](const tbb::blocked_range<uint64_t> &range)
-            {
+            [&input_data_vector, &input_wrapper_vector, this](
+                const tbb::blocked_range<uint64_t> &range) {
                 for (uint64_t element_counter = range.begin(), end = range.end();
-                     element_counter != end; ++element_counter)
+                     element_counter != end;
+                     ++element_counter)
                 {
                     WrappedInputElement &current_wrapper = input_wrapper_vector[element_counter];
                     current_wrapper.m_array_index = element_counter;
@@ -172,37 +207,63 @@ class StaticRTree
         tbb::parallel_sort(input_wrapper_vector.begin(), input_wrapper_vector.end());
         std::vector<TreeNode> tree_nodes_in_level;
 
-        // pack M elements into leaf node and write to leaf file
-        uint64_t processed_objects_count = 0;
-        while (processed_objects_count < element_count)
+        // pack M elements into leaf node, write to leaf file and add child index to the parent node
+        uint64_t wrapped_element_index = 0;
+        for (std::uint32_t node_index = 0; wrapped_element_index < element_count; ++node_index)
         {
-
-            LeafNode current_leaf;
             TreeNode current_node;
-            for (std::uint32_t current_element_index = 0; LEAF_NODE_SIZE > current_element_index;
-                 ++current_element_index)
+            for (std::uint32_t leaf_index = 0;
+                 leaf_index < BRANCHING_FACTOR && wrapped_element_index < element_count;
+                 ++leaf_index)
             {
-                if (element_count > (processed_objects_count + current_element_index))
+                LeafNode current_leaf;
+                Rectangle &rectangle = current_leaf.minimum_bounding_rectangle;
+                for (std::uint32_t object_index = 0;
+                     object_index < LEAF_NODE_SIZE && wrapped_element_index < element_count;
+                     ++object_index, ++wrapped_element_index)
                 {
-                    std::uint32_t index_of_next_object =
-                        input_wrapper_vector[processed_objects_count + current_element_index]
-                            .m_array_index;
-                    current_leaf.objects[current_element_index] =
-                        input_data_vector[index_of_next_object];
-                    ++current_leaf.object_count;
+                    const std::uint32_t input_object_index =
+                        input_wrapper_vector[wrapped_element_index].m_array_index;
+                    const EdgeDataT &object = input_data_vector[input_object_index];
+
+                    current_leaf.object_count += 1;
+                    current_leaf.objects[object_index] = object;
+
+                    Coordinate projected_u{
+                        web_mercator::fromWGS84(Coordinate{m_coordinate_list[object.u]})};
+                    Coordinate projected_v{
+                        web_mercator::fromWGS84(Coordinate{m_coordinate_list[object.v]})};
+
+                    BOOST_ASSERT(std::abs(toFloating(projected_u.lon).operator double()) <= 180.);
+                    BOOST_ASSERT(std::abs(toFloating(projected_u.lat).operator double()) <= 180.);
+                    BOOST_ASSERT(std::abs(toFloating(projected_v.lon).operator double()) <= 180.);
+                    BOOST_ASSERT(std::abs(toFloating(projected_v.lat).operator double()) <= 180.);
+
+                    rectangle.min_lon =
+                        std::min(rectangle.min_lon, std::min(projected_u.lon, projected_v.lon));
+                    rectangle.max_lon =
+                        std::max(rectangle.max_lon, std::max(projected_u.lon, projected_v.lon));
+
+                    rectangle.min_lat =
+                        std::min(rectangle.min_lat, std::min(projected_u.lat, projected_v.lat));
+                    rectangle.max_lat =
+                        std::max(rectangle.max_lat, std::max(projected_u.lat, projected_v.lat));
+
+                    BOOST_ASSERT(rectangle.IsValid());
                 }
+
+                // append the leaf node to the current tree node
+                current_node.child_count += 1;
+                current_node.children[leaf_index] =
+                    TreeIndex{node_index * BRANCHING_FACTOR + leaf_index, true};
+                current_node.minimum_bounding_rectangle.MergeBoundingBoxes(
+                    current_leaf.minimum_bounding_rectangle);
+
+                // write leaf_node to leaf node file
+                leaf_node_file.write((char *)&current_leaf, sizeof(current_leaf));
             }
 
-            // generate tree node that resemble the objects in leaf and store it for next level
-            InitializeMBRectangle(current_node.minimum_bounding_rectangle, current_leaf.objects,
-                                  current_leaf.object_count, m_coordinate_list);
-            current_node.child_is_on_disk = true;
-            current_node.children[0] = tree_nodes_in_level.size();
             tree_nodes_in_level.emplace_back(current_node);
-
-            // write leaf_node to leaf node file
-            leaf_node_file.write((char *)&current_leaf, sizeof(current_leaf));
-            processed_objects_count += current_leaf.object_count;
         }
         leaf_node_file.flush();
         leaf_node_file.close();
@@ -217,14 +278,16 @@ class StaticRTree
                 TreeNode parent_node;
                 // pack BRANCHING_FACTOR elements into tree_nodes each
                 for (std::uint32_t current_child_node_index = 0;
-                     BRANCHING_FACTOR > current_child_node_index; ++current_child_node_index)
+                     current_child_node_index < BRANCHING_FACTOR;
+                     ++current_child_node_index)
                 {
                     if (processed_tree_nodes_in_level < tree_nodes_in_level.size())
                     {
                         TreeNode &current_child_node =
                             tree_nodes_in_level[processed_tree_nodes_in_level];
                         // add tree node to parent entry
-                        parent_node.children[current_child_node_index] = m_search_tree.size();
+                        parent_node.children[current_child_node_index] =
+                            TreeIndex{m_search_tree.size(), false};
                         m_search_tree.emplace_back(current_child_node);
                         // merge MBRs
                         parent_node.minimum_bounding_rectangle.MergeBoundingBoxes(
@@ -239,7 +302,7 @@ class StaticRTree
             tree_nodes_in_level.swap(tree_nodes_in_next_level);
             ++processing_level;
         }
-        BOOST_ASSERT_MSG(1 == tree_nodes_in_level.size(), "tree broken, more than one root node");
+        BOOST_ASSERT_MSG(tree_nodes_in_level.size() == 1, "tree broken, more than one root node");
         // last remaining entry is the root node, store it
         m_search_tree.emplace_back(tree_nodes_in_level[0]);
 
@@ -247,21 +310,23 @@ class StaticRTree
         std::reverse(m_search_tree.begin(), m_search_tree.end());
 
         std::uint32_t search_tree_size = m_search_tree.size();
-        tbb::parallel_for(tbb::blocked_range<std::uint32_t>(0, search_tree_size),
-                          [this, &search_tree_size](const tbb::blocked_range<std::uint32_t> &range)
-                          {
-                              for (std::uint32_t i = range.begin(), end = range.end(); i != end;
-                                   ++i)
-                              {
-                                  TreeNode &current_tree_node = this->m_search_tree[i];
-                                  for (std::uint32_t j = 0; j < current_tree_node.child_count; ++j)
-                                  {
-                                      const std::uint32_t old_id = current_tree_node.children[j];
-                                      const std::uint32_t new_id = search_tree_size - old_id - 1;
-                                      current_tree_node.children[j] = new_id;
-                                  }
-                              }
-                          });
+        tbb::parallel_for(
+            tbb::blocked_range<std::uint32_t>(0, search_tree_size),
+            [this, &search_tree_size](const tbb::blocked_range<std::uint32_t> &range) {
+                for (std::uint32_t i = range.begin(), end = range.end(); i != end; ++i)
+                {
+                    TreeNode &current_tree_node = this->m_search_tree[i];
+                    for (std::uint32_t j = 0; j < current_tree_node.child_count; ++j)
+                    {
+                        if (!current_tree_node.children[j].is_leaf)
+                        {
+                            const std::uint32_t old_id = current_tree_node.children[j].index;
+                            const std::uint32_t new_id = search_tree_size - old_id - 1;
+                            current_tree_node.children[j].index = new_id;
+                        }
+                    }
+                }
+            });
 
         // open tree file
         boost::filesystem::ofstream tree_node_file(tree_node_filename, std::ios::binary);
@@ -276,15 +341,15 @@ class StaticRTree
 
     explicit StaticRTree(const boost::filesystem::path &node_file,
                          const boost::filesystem::path &leaf_file,
-                         const CoordinateListT& coordinate_list)
-      : m_coordinate_list(coordinate_list)
+                         const CoordinateListT &coordinate_list)
+        : m_coordinate_list(coordinate_list)
     {
         // open tree node file and load into RAM.
         if (!boost::filesystem::exists(node_file))
         {
             throw exception("ram index file does not exist");
         }
-        if (0 == boost::filesystem::file_size(node_file))
+        if (boost::filesystem::file_size(node_file) == 0)
         {
             throw exception("ram index file is empty");
         }
@@ -305,9 +370,8 @@ class StaticRTree
     explicit StaticRTree(TreeNode *tree_node_ptr,
                          const uint64_t number_of_nodes,
                          const boost::filesystem::path &leaf_file,
-                         const CoordinateListT& coordinate_list)
-        : m_search_tree(tree_node_ptr, number_of_nodes)
-        , m_coordinate_list(coordinate_list)
+                         const CoordinateListT &coordinate_list)
+        : m_search_tree(tree_node_ptr, number_of_nodes), m_coordinate_list(coordinate_list)
     {
         MapLeafNodesFile(leaf_file);
     }
@@ -315,12 +379,16 @@ class StaticRTree
     void MapLeafNodesFile(const boost::filesystem::path &leaf_file)
     {
         // open leaf node file and return a pointer to the mapped leaves data
-        try {
+        try
+        {
             m_leaves_region.open(leaf_file);
             std::size_t num_leaves = m_leaves_region.size() / sizeof(LeafNode);
-            m_leaves.reset(reinterpret_cast<const LeafNode*>(m_leaves_region.data()), num_leaves);
-        } catch (std::exception& exc) {
-            throw exception(boost::str(boost::format("Leaf file %1% mapping failed: %2%") % leaf_file % exc.what()));
+            m_leaves.reset(reinterpret_cast<const LeafNode *>(m_leaves_region.data()), num_leaves);
+        }
+        catch (std::exception &exc)
+        {
+            throw exception(boost::str(boost::format("Leaf file %1% mapping failed: %2%") %
+                                       leaf_file % exc.what()));
         }
     }
 
@@ -329,24 +397,25 @@ class StaticRTree
     std::vector<EdgeDataT> SearchInBox(const Rectangle &search_rectangle) const
     {
         const Rectangle projected_rectangle{
-            search_rectangle.min_lon, search_rectangle.max_lon,
+            search_rectangle.min_lon,
+            search_rectangle.max_lon,
             toFixed(FloatLatitude{
                 web_mercator::latToY(toFloating(FixedLatitude(search_rectangle.min_lat)))}),
             toFixed(FloatLatitude{
                 web_mercator::latToY(toFloating(FixedLatitude(search_rectangle.max_lat)))})};
         std::vector<EdgeDataT> results;
 
-        std::queue<std::uint32_t> traversal_queue;
-        traversal_queue.push(0);
+        std::queue<TreeIndex> traversal_queue;
+        traversal_queue.push(TreeIndex{});
 
         while (!traversal_queue.empty())
         {
-            auto const &current_tree_node = m_search_tree[traversal_queue.front()];
+            auto const current_tree_index = traversal_queue.front();
             traversal_queue.pop();
 
-            if (current_tree_node.child_is_on_disk)
+            if (current_tree_index.is_leaf)
             {
-                const LeafNode& current_leaf_node = m_leaves[current_tree_node.children[0]];
+                const LeafNode &current_leaf_node = m_leaves[current_tree_index.index];
 
                 for (const auto i : irange(0u, current_leaf_node.object_count))
                 {
@@ -372,13 +441,16 @@ class StaticRTree
             }
             else
             {
+                const TreeNode &current_tree_node = m_search_tree[current_tree_index.index];
+
                 // If it's a tree node, look at all children and add them
                 // to the search queue if their bounding boxes intersect
                 for (std::uint32_t i = 0; i < current_tree_node.child_count; ++i)
                 {
-                    const std::int32_t child_id = current_tree_node.children[i];
-                    const auto &child_tree_node = m_search_tree[child_id];
-                    const auto &child_rectangle = child_tree_node.minimum_bounding_rectangle;
+                    const TreeIndex child_id = current_tree_node.children[i];
+                    const auto &child_rectangle =
+                        child_id.is_leaf ? m_leaves[child_id.index].minimum_bounding_rectangle
+                                         : m_search_tree[child_id.index].minimum_bounding_rectangle;
 
                     if (child_rectangle.Intersects(projected_rectangle))
                     {
@@ -391,23 +463,21 @@ class StaticRTree
     }
 
     // Override filter and terminator for the desired behaviour.
-    std::vector<EdgeDataT> Nearest(const Coordinate input_coordinate, const std::size_t max_results) const
+    std::vector<EdgeDataT> Nearest(const Coordinate input_coordinate,
+                                   const std::size_t max_results) const
     {
         return Nearest(input_coordinate,
-                       [](const CandidateSegment &)
-                       {
-                           return std::make_pair(true, true);
-                       },
-                       [max_results](const std::size_t num_results, const CandidateSegment &)
-                       {
+                       [](const CandidateSegment &) { return std::make_pair(true, true); },
+                       [max_results](const std::size_t num_results, const CandidateSegment &) {
                            return num_results >= max_results;
                        });
     }
 
     // Override filter and terminator for the desired behaviour.
     template <typename FilterT, typename TerminationT>
-    std::vector<EdgeDataT>
-    Nearest(const Coordinate input_coordinate, const FilterT filter, const TerminationT terminate) const
+    std::vector<EdgeDataT> Nearest(const Coordinate input_coordinate,
+                                   const FilterT filter,
+                                   const TerminationT terminate) const
     {
         std::vector<EdgeDataT> results;
         auto projected_coordinate = web_mercator::fromWGS84(input_coordinate);
@@ -415,39 +485,41 @@ class StaticRTree
 
         // initialize queue with root element
         std::priority_queue<QueryCandidate> traversal_queue;
-        traversal_queue.push(QueryCandidate{0, TreeIndex{0}});
+        traversal_queue.push(QueryCandidate{0, TreeIndex{}});
 
         while (!traversal_queue.empty())
         {
             QueryCandidate current_query_node = traversal_queue.top();
             traversal_queue.pop();
 
-            if (current_query_node.node.template is<TreeIndex>())
+            const TreeIndex &current_tree_index = current_query_node.tree_index;
+            if (!current_query_node.is_segment())
             { // current object is a tree node
-                const TreeNode &current_tree_node =
-                    m_search_tree[current_query_node.node.template get<TreeIndex>().index];
-                if (current_tree_node.child_is_on_disk)
+                if (current_tree_index.is_leaf)
                 {
-                    ExploreLeafNode(current_tree_node.children[0], fixed_projected_coordinate,
-                                    projected_coordinate, traversal_queue);
+                    ExploreLeafNode(current_tree_index,
+                                    fixed_projected_coordinate,
+                                    projected_coordinate,
+                                    traversal_queue);
                 }
                 else
                 {
-                    ExploreTreeNode(current_tree_node, fixed_projected_coordinate, traversal_queue);
+                    ExploreTreeNode(
+                        current_tree_index, fixed_projected_coordinate, traversal_queue);
                 }
             }
             else
-            {
-                // inspecting an actual road segment
-                const auto &segment_index = current_query_node.node.template get<SegmentIndex>();
-                auto edge_data = m_leaves[segment_index.index].objects[segment_index.object];
-                const auto &current_candidate = CandidateSegment{segment_index.fixed_projected_coordinate, edge_data};
-
-                // to allow returns of no-results if too restrictive filtering, this needs to be done here
-                // even though performance would indicate that we want to stop after adding the first candidate
+            { // current candidate is an actual road segment
+                auto edge_data =
+                    m_leaves[current_tree_index.index].objects[current_query_node.segment_index];
+                const auto &current_candidate =
+                    CandidateSegment{current_query_node.fixed_projected_coordinate, edge_data};
+
+                // to allow returns of no-results if too restrictive filtering, this needs to be
+                // done here even though performance would indicate that we want to stop after
+                // adding the first candidate
                 if (terminate(results.size(), current_candidate))
                 {
-                    traversal_queue = std::priority_queue<QueryCandidate>{};
                     break;
                 }
 
@@ -469,12 +541,12 @@ class StaticRTree
 
   private:
     template <typename QueueT>
-    void ExploreLeafNode(const std::uint32_t leaf_id,
-                         const Coordinate projected_input_coordinate_fixed,
+    void ExploreLeafNode(const TreeIndex &leaf_id,
+                         const Coordinate &projected_input_coordinate_fixed,
                          const FloatCoordinate &projected_input_coordinate,
                          QueueT &traversal_queue) const
     {
-        const LeafNode& current_leaf_node = m_leaves[leaf_id];
+        const LeafNode &current_leaf_node = m_leaves[leaf_id.index];
 
         // current object represents a block on disk
         for (const auto i : irange(0u, current_leaf_node.object_count))
@@ -485,73 +557,34 @@ class StaticRTree
 
             FloatCoordinate projected_nearest;
             std::tie(std::ignore, projected_nearest) =
-                coordinate_calculation::projectPointOnSegment(projected_u, projected_v,
-                                                              projected_input_coordinate);
+                coordinate_calculation::projectPointOnSegment(
+                    projected_u, projected_v, projected_input_coordinate);
 
             const auto squared_distance = coordinate_calculation::squaredEuclideanDistance(
                 projected_input_coordinate_fixed, projected_nearest);
             // distance must be non-negative
             BOOST_ASSERT(0. <= squared_distance);
-            traversal_queue.push(QueryCandidate{squared_distance, SegmentIndex{leaf_id, i, Coordinate{projected_nearest}}});
+            traversal_queue.push(
+                QueryCandidate{squared_distance, leaf_id, i, Coordinate{projected_nearest}});
         }
     }
 
     template <class QueueT>
-    void ExploreTreeNode(const TreeNode &parent,
-                         const Coordinate fixed_projected_input_coordinate,
+    void ExploreTreeNode(const TreeIndex &parent_id,
+                         const Coordinate &fixed_projected_input_coordinate,
                          QueueT &traversal_queue) const
     {
+        const TreeNode &parent = m_search_tree[parent_id.index];
         for (std::uint32_t i = 0; i < parent.child_count; ++i)
         {
-            const std::int32_t child_id = parent.children[i];
-            const auto &child_tree_node = m_search_tree[child_id];
-            const auto &child_rectangle = child_tree_node.minimum_bounding_rectangle;
+            const TreeIndex child_id = parent.children[i];
+            const auto &child_rectangle =
+                child_id.is_leaf ? m_leaves[child_id.index].minimum_bounding_rectangle
+                                 : m_search_tree[child_id.index].minimum_bounding_rectangle;
             const auto squared_lower_bound_to_element =
                 child_rectangle.GetMinSquaredDist(fixed_projected_input_coordinate);
-            traversal_queue.push(
-                QueryCandidate{squared_lower_bound_to_element, TreeIndex{static_cast<std::uint32_t>(child_id)}});
-        }
-    }
-
-    template <typename CoordinateT>
-    void InitializeMBRectangle(Rectangle &rectangle,
-                               const std::array<EdgeDataT, LEAF_NODE_SIZE> &objects,
-                               const std::uint32_t element_count,
-                               const std::vector<CoordinateT> &coordinate_list)
-    {
-        for (std::uint32_t i = 0; i < element_count; ++i)
-        {
-            BOOST_ASSERT(objects[i].u < coordinate_list.size());
-            BOOST_ASSERT(objects[i].v < coordinate_list.size());
-
-            Coordinate projected_u{
-                web_mercator::fromWGS84(Coordinate{coordinate_list[objects[i].u]})};
-            Coordinate projected_v{
-                web_mercator::fromWGS84(Coordinate{coordinate_list[objects[i].v]})};
-
-            BOOST_ASSERT(toFloating(projected_u.lon) <= FloatLongitude(180.));
-            BOOST_ASSERT(toFloating(projected_u.lon) >= FloatLongitude(-180.));
-            BOOST_ASSERT(toFloating(projected_u.lat) <= FloatLatitude(180.));
-            BOOST_ASSERT(toFloating(projected_u.lat) >= FloatLatitude(-180.));
-            BOOST_ASSERT(toFloating(projected_v.lon) <= FloatLongitude(180.));
-            BOOST_ASSERT(toFloating(projected_v.lon) >= FloatLongitude(-180.));
-            BOOST_ASSERT(toFloating(projected_v.lat) <= FloatLatitude(180.));
-            BOOST_ASSERT(toFloating(projected_v.lat) >= FloatLatitude(-180.));
-
-            rectangle.min_lon =
-                std::min(rectangle.min_lon, std::min(projected_u.lon, projected_v.lon));
-            rectangle.max_lon =
-                std::max(rectangle.max_lon, std::max(projected_u.lon, projected_v.lon));
-
-            rectangle.min_lat =
-                std::min(rectangle.min_lat, std::min(projected_u.lat, projected_v.lat));
-            rectangle.max_lat =
-                std::max(rectangle.max_lat, std::max(projected_u.lat, projected_v.lat));
+            traversal_queue.push(QueryCandidate{squared_lower_bound_to_element, child_id});
         }
-        BOOST_ASSERT(rectangle.min_lon != FixedLongitude(std::numeric_limits<int>::min()));
-        BOOST_ASSERT(rectangle.min_lat != FixedLatitude(std::numeric_limits<int>::min()));
-        BOOST_ASSERT(rectangle.max_lon != FixedLongitude(std::numeric_limits<int>::min()));
-        BOOST_ASSERT(rectangle.max_lat != FixedLatitude(std::numeric_limits<int>::min()));
     }
 };
 
diff --git a/include/util/string_util.hpp b/include/util/string_util.hpp
index 41c5f09..35a7076 100644
--- a/include/util/string_util.hpp
+++ b/include/util/string_util.hpp
@@ -20,8 +20,7 @@ template <int length, int precision> char *printInt(char *buffer, int value)
     static_assert(length > 0, "length must be positive");
     static_assert(precision > 0, "precision must be positive");
 
-    const bool minus = [&value]
-    {
+    const bool minus = [&value] {
         if (value >= 0)
         {
             value = -value;
diff --git a/include/util/strong_typedef.hpp b/include/util/strong_typedef.hpp
index eabcaa3..6385435 100644
--- a/include/util/strong_typedef.hpp
+++ b/include/util/strong_typedef.hpp
@@ -28,9 +28,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #ifndef STRONG_TYPEDEF_HPP
 #define STRONG_TYPEDEF_HPP
 
+#include <functional>
 #include <iostream>
 #include <type_traits>
-#include <functional>
 
 namespace osrm
 {
diff --git a/include/util/timing_util.hpp b/include/util/timing_util.hpp
index 537f0dc..c248559 100644
--- a/include/util/timing_util.hpp
+++ b/include/util/timing_util.hpp
@@ -1,55 +1,14 @@
 #ifndef TIMING_UTIL_HPP
 #define TIMING_UTIL_HPP
 
-#include <atomic>
 #include <chrono>
 #include <cstdint>
-#include <mutex>
-#include <unordered_map>
 
 namespace osrm
 {
 namespace util
 {
 
-struct GlobalTimer
-{
-    GlobalTimer() : time(0) {}
-    std::atomic<uint64_t> time;
-};
-
-class GlobalTimerFactory
-{
-  public:
-    static GlobalTimerFactory &get()
-    {
-        static GlobalTimerFactory instance;
-        return instance;
-    }
-
-    GlobalTimer &getGlobalTimer(const std::string &name)
-    {
-        std::lock_guard<std::mutex> lock(map_mutex);
-        return timer_map[name];
-    }
-
-  private:
-    std::mutex map_mutex;
-    std::unordered_map<std::string, GlobalTimer> timer_map;
-};
-
-#define GLOBAL_TIMER_AQUIRE(_X)                                                                    \
-    auto &_X##_global_timer = GlobalTimerFactory::get().getGlobalTimer(#_X)
-#define GLOBAL_TIMER_RESET(_X) _X##_global_timer.time = 0
-#define GLOBAL_TIMER_START(_X) TIMER_START(_X)
-#define GLOBAL_TIMER_STOP(_X)                                                                      \
-    TIMER_STOP(_X);                                                                                \
-    _X##_global_timer.time += TIMER_NSEC(_X)
-#define GLOBAL_TIMER_NSEC(_X) static_cast<double>(_X##_global_timer.time)
-#define GLOBAL_TIMER_USEC(_X) (_X##_global_timer.time / 1000.0)
-#define GLOBAL_TIMER_MSEC(_X) (_X##_global_timer.time / 1000.0 / 1000.0)
-#define GLOBAL_TIMER_SEC(_X) (_X##_global_timer.time / 1000.0 / 1000.0 / 1000.0)
-
 #define TIMER_START(_X) auto _X##_start = std::chrono::steady_clock::now(), _X##_stop = _X##_start
 #define TIMER_STOP(_X) _X##_stop = std::chrono::steady_clock::now()
 #define TIMER_NSEC(_X)                                                                             \
diff --git a/include/util/trigonometry_table.hpp b/include/util/trigonometry_table.hpp
index fe1d316..86cca41 100644
--- a/include/util/trigonometry_table.hpp
+++ b/include/util/trigonometry_table.hpp
@@ -358,13 +358,12 @@ constexpr unsigned short atan_table[4096] = {
     0xffe0, 0xffea, 0xfff4, 0xffff};
 
 // max value is pi/4
-#ifdef _MSC_VER //TODO: remove as soon as boost allows C++14 features with Visual Studio
+#ifdef _MSC_VER // TODO: remove as soon as boost allows C++14 features with Visual Studio
 const constexpr double SCALING_FACTOR = 4. / M_PI * 0xFFFF;
 #else
 const constexpr double SCALING_FACTOR = 4. / boost::math::constants::pi<double>() * 0xFFFF;
 #endif
 
-
 inline double atan2_lookup(double y, double x)
 {
 
diff --git a/include/util/typedefs.hpp b/include/util/typedefs.hpp
index f36a241..423d306 100644
--- a/include/util/typedefs.hpp
+++ b/include/util/typedefs.hpp
@@ -32,15 +32,16 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #include <boost/assert.hpp>
 
-#include <limits>
 #include <cstddef>
+#include <cstdint>
+#include <limits>
 
 // OpenStreetMap node ids are higher than 2^32
-OSRM_STRONG_TYPEDEF(uint64_t, OSMNodeID)
-OSRM_STRONG_TYPEDEF_HASHABLE(uint64_t, OSMNodeID)
+OSRM_STRONG_TYPEDEF(std::uint64_t, OSMNodeID)
+OSRM_STRONG_TYPEDEF_HASHABLE(std::uint64_t, OSMNodeID)
 
-OSRM_STRONG_TYPEDEF(uint32_t, OSMWayID)
-OSRM_STRONG_TYPEDEF_HASHABLE(uint32_t, OSMWayID)
+OSRM_STRONG_TYPEDEF(std::uint32_t, OSMWayID)
+OSRM_STRONG_TYPEDEF_HASHABLE(std::uint32_t, OSMWayID)
 
 static const OSMNodeID SPECIAL_OSM_NODEID = OSMNodeID(std::numeric_limits<std::uint64_t>::max());
 static const OSMWayID SPECIAL_OSM_WAYID = OSMWayID(std::numeric_limits<std::uint32_t>::max());
@@ -55,12 +56,22 @@ using OSMEdgeID_weak = std::uint64_t;
 
 using NodeID = unsigned int;
 using EdgeID = unsigned int;
+using NameID = std::uint32_t;
 using EdgeWeight = int;
 
+using BearingClassID = std::uint32_t;
+static const BearingClassID INVALID_BEARING_CLASSID = std::numeric_limits<std::uint32_t>::max();
+
+using DiscreteBearing = std::uint16_t;
+
+using EntryClassID = std::uint16_t;
+static const EntryClassID INVALID_ENTRY_CLASSID = std::numeric_limits<std::uint16_t>::max();
+
 static const NodeID SPECIAL_NODEID = std::numeric_limits<unsigned>::max();
 static const NodeID SPECIAL_SEGMENTID = std::numeric_limits<int>::max();
 static const EdgeID SPECIAL_EDGEID = std::numeric_limits<unsigned>::max();
 static const unsigned INVALID_NAMEID = std::numeric_limits<unsigned>::max();
+static const unsigned EMPTY_NAMEID = 0;
 static const unsigned INVALID_COMPONENTID = 0;
 static const EdgeWeight INVALID_EDGE_WEIGHT = std::numeric_limits<int>::max();
 
@@ -71,8 +82,8 @@ struct SegmentID
         BOOST_ASSERT(!enabled || id != SPECIAL_SEGMENTID);
     }
 
-    NodeID  id              : 31;
-    std::uint32_t enabled   :  1;
+    NodeID id : 31;
+    std::uint32_t enabled : 1;
 };
 
 static_assert(sizeof(SegmentID) == 4, "SegmentID needs to be 4 bytes big");
diff --git a/include/util/vector_tile.hpp b/include/util/vector_tile.hpp
index 8d816da..c0a8c10 100644
--- a/include/util/vector_tile.hpp
+++ b/include/util/vector_tile.hpp
@@ -30,7 +30,6 @@ const constexpr std::uint32_t VARIANT_TYPE_DOUBLE = 3;
 // Vector tiles are 4096 virtual pixels on each side
 const constexpr double EXTENT = 4096.0;
 const constexpr double BUFFER = 128.0;
-
 }
 }
 }
diff --git a/include/util/web_mercator.hpp b/include/util/web_mercator.hpp
index a2bea88..b5819ff 100644
--- a/include/util/web_mercator.hpp
+++ b/include/util/web_mercator.hpp
@@ -35,7 +35,7 @@ inline FloatLatitude yToLat(const double y)
     const double normalized_lat =
         detail::RAD_TO_DEGREE * 2. * std::atan(std::exp(clamped_y * detail::DEGREE_TO_RAD));
 
-    return FloatLatitude(std::round(normalized_lat * COORDINATE_PRECISION) / COORDINATE_PRECISION - 90.);
+    return FloatLatitude(normalized_lat - 90.);
 }
 
 inline double latToY(const FloatLatitude latitude)
@@ -47,11 +47,12 @@ inline double latToY(const FloatLatitude latitude)
     return clamped_y;
 }
 
-template<typename T>
-constexpr double horner(double, T an) { return an; }
+template <typename T> constexpr double horner(double, T an) { return an; }
 
-template<typename T, typename... U>
-constexpr double horner(double x, T an, U ...a) { return horner(x, a...) * x + an; }
+template <typename T, typename... U> constexpr double horner(double x, T an, U... a)
+{
+    return horner(x, a...) * x + an;
+}
 
 inline double latToYapprox(const FloatLatitude latitude)
 {
@@ -59,17 +60,35 @@ inline double latToYapprox(const FloatLatitude latitude)
         return latToY(latitude);
 
     // Approximate the inverse Gudermannian function with the Padé approximant [11/11]: deg → deg
-    // Coefficients are computed for the argument range [-70°,70°] by Remez algorithm |err|_∞=3.387e-12
+    // Coefficients are computed for the argument range [-70°,70°] by Remez algorithm
+    // |err|_∞=3.387e-12
     const auto x = static_cast<double>(latitude);
-    return
-        horner(x, 0.00000000000000000000000000e+00,  1.00000000000089108431373566e+00,  2.34439410386997223035693483e-06,
-                 -3.21291701673364717170998957e-04, -6.62778508496089940141103135e-10,  3.68188055470304769936079078e-08,
-                  6.31192702320492485752941578e-14, -1.77274453235716299127325443e-12, -2.24563810831776747318521450e-18,
-                  3.13524754818073129982475171e-17,  2.09014225025314211415458228e-23, -9.82938075991732185095509716e-23) /
-        horner(x, 1.00000000000000000000000000e+00,  2.34439410398970701719081061e-06, -3.72061271627251952928813333e-04,
-                 -7.81802389685429267252612620e-10,  5.18418724186576447072888605e-08,  9.37468561198098681003717477e-14,
-                 -3.30833288607921773936702558e-12, -4.78446279888774903983338274e-18,  9.32999229169156878168234191e-17,
-                  9.17695141954265959600965170e-23, -8.72130728982012387640166055e-22, -3.23083224835967391884404730e-28);
+    return horner(x,
+                  0.00000000000000000000000000e+00,
+                  1.00000000000089108431373566e+00,
+                  2.34439410386997223035693483e-06,
+                  -3.21291701673364717170998957e-04,
+                  -6.62778508496089940141103135e-10,
+                  3.68188055470304769936079078e-08,
+                  6.31192702320492485752941578e-14,
+                  -1.77274453235716299127325443e-12,
+                  -2.24563810831776747318521450e-18,
+                  3.13524754818073129982475171e-17,
+                  2.09014225025314211415458228e-23,
+                  -9.82938075991732185095509716e-23) /
+           horner(x,
+                  1.00000000000000000000000000e+00,
+                  2.34439410398970701719081061e-06,
+                  -3.72061271627251952928813333e-04,
+                  -7.81802389685429267252612620e-10,
+                  5.18418724186576447072888605e-08,
+                  9.37468561198098681003717477e-14,
+                  -3.30833288607921773936702558e-12,
+                  -4.78446279888774903983338274e-18,
+                  9.32999229169156878168234191e-17,
+                  9.17695141954265959600965170e-23,
+                  -8.72130728982012387640166055e-22,
+                  -3.23083224835967391884404730e-28);
 }
 
 inline FloatLatitude clamp(const FloatLatitude lat)
diff --git a/include/util/xor_fast_hash.hpp b/include/util/xor_fast_hash.hpp
index 6ad79d6..0f2f3ba 100644
--- a/include/util/xor_fast_hash.hpp
+++ b/include/util/xor_fast_hash.hpp
@@ -3,8 +3,8 @@
 
 #include <boost/assert.hpp>
 
-#include <array>
 #include <algorithm>
+#include <array>
 #include <iterator>
 #include <numeric>
 #include <random>
diff --git a/profiles/bicycle.lua b/profiles/bicycle.lua
index 79d7b13..7bbc621 100644
--- a/profiles/bicycle.lua
+++ b/profiles/bicycle.lua
@@ -205,6 +205,8 @@ function way_function (way, result)
   local service = way:get_value_by_key("service")
   local area = way:get_value_by_key("area")
   local foot = way:get_value_by_key("foot")
+  local foot_forward = way:get_value_by_key("foot:forward")
+  local foot_backward = way:get_value_by_key("foot:backward")
   local surface = way:get_value_by_key("surface")
   local bicycle = way:get_value_by_key("bicycle")
 
@@ -315,7 +317,7 @@ function way_function (way, result)
 
   -- direction
   local impliedOneway = false
-  if junction == "roundabout" or highway == "motorway_link" or highway == "motorway" then
+  if junction == "roundabout" or highway == "motorway" then
     impliedOneway = true
   end
 
diff --git a/profiles/car.lua b/profiles/car.lua
index ea50b86..becf502 100644
--- a/profiles/car.lua
+++ b/profiles/car.lua
@@ -348,6 +348,7 @@ function way_function (way, result)
 
   -- parse the remaining tags
   local name = way:get_value_by_key("name")
+  local pronunciation = way:get_value_by_key("name:pronunciation")
   local ref = way:get_value_by_key("ref")
   local junction = way:get_value_by_key("junction")
   -- local barrier = way:get_value_by_key("barrier", "")
@@ -357,6 +358,7 @@ function way_function (way, result)
   -- Set the name that will be used for instructions
   local has_ref = ref and "" ~= ref
   local has_name = name and "" ~= name
+  local has_pronunciation = pronunciation and "" ~= pronunciation
 
   if has_name and has_ref then
     result.name = name .. " (" .. ref .. ")"
@@ -364,8 +366,10 @@ function way_function (way, result)
     result.name = ref
   elseif has_name then
     result.name = name
---  else
-      --    result.name = highway  -- if no name exists, use way type
+  end
+
+  if has_pronunciation then
+    result.pronunciation = pronunciation
   end
 
   if junction and "roundabout" == junction then
@@ -390,7 +394,6 @@ function way_function (way, result)
     oneway == "1" or
     oneway == "true" or
     junction == "roundabout" or
-    (highway == "motorway_link" and oneway ~="no") or
     (highway == "motorway" and oneway ~= "no") then
       result.backward_mode = mode.inaccessible
 
@@ -401,6 +404,8 @@ function way_function (way, result)
       if has_destination and has_name and not has_ref then
         result.name = name .. " (" .. destination .. ")"
       end
+
+      result.destinations = destination
     end
   end
 
diff --git a/src/benchmarks/CMakeLists.txt b/src/benchmarks/CMakeLists.txt
index 754dc75..ef049df 100644
--- a/src/benchmarks/CMakeLists.txt
+++ b/src/benchmarks/CMakeLists.txt
@@ -1,5 +1,5 @@
-file(GLOB RTreeBenchmarkSources
-    *.cpp)
+file(GLOB RTreeBenchmarkSources static_rtree.cpp)
+file(GLOB MatchBenchmarkSources match.cpp)
 
 add_executable(rtree-bench
 	EXCLUDE_FROM_ALL
@@ -15,6 +15,18 @@ target_link_libraries(rtree-bench
 	${CMAKE_THREAD_LIBS_INIT}
 	${TBB_LIBRARIES})
 
+add_executable(match-bench
+	EXCLUDE_FROM_ALL
+	${MatchBenchmarkSources}
+	$<TARGET_OBJECTS:UTIL>)
+
+target_link_libraries(match-bench
+	osrm
+	${Boost_LIBRARIES}
+	${CMAKE_THREAD_LIBS_INIT}
+	${TBB_LIBRARIES})
+
 add_custom_target(benchmarks
 	DEPENDS
-	rtree-bench)
+	rtree-bench
+	match-bench)
diff --git a/src/benchmarks/match.cpp b/src/benchmarks/match.cpp
new file mode 100644
index 0000000..788ab2f
--- /dev/null
+++ b/src/benchmarks/match.cpp
@@ -0,0 +1,236 @@
+#include "util/timing_util.hpp"
+
+#include "osrm/match_parameters.hpp"
+
+#include "osrm/coordinate.hpp"
+#include "osrm/engine_config.hpp"
+#include "osrm/json_container.hpp"
+
+#include "osrm/osrm.hpp"
+#include "osrm/status.hpp"
+
+#include <boost/assert.hpp>
+
+#include <exception>
+#include <iostream>
+#include <string>
+#include <utility>
+
+#include <cstdlib>
+
+int main(int argc, const char *argv[]) try
+{
+    if (argc < 2)
+    {
+        std::cerr << "Usage: " << argv[0] << " data.osrm\n";
+        return EXIT_FAILURE;
+    }
+
+    using namespace osrm;
+
+    // Configure based on a .osrm base path, and no datasets in shared mem from osrm-datastore
+    EngineConfig config;
+    config.storage_config = {argv[1]};
+    config.use_shared_memory = false;
+
+    // Routing machine with several services (such as Route, Table, Nearest, Trip, Match)
+    OSRM osrm{config};
+
+    // Route in monaco
+    MatchParameters params;
+    params.overview = RouteParameters::OverviewType::False;
+    params.steps = false;
+
+    using osrm::util::FloatCoordinate;
+    using osrm::util::FloatLatitude;
+    using osrm::util::FloatLongitude;
+
+    params.coordinates.push_back(
+        FloatCoordinate{FloatLongitude{7.422176599502563}, FloatLatitude{43.73754595167546}});
+    params.coordinates.push_back(
+        FloatCoordinate{FloatLongitude{7.421715259552002}, FloatLatitude{43.73744517900973}});
+    params.coordinates.push_back(
+        FloatCoordinate{FloatLongitude{7.421489953994752}, FloatLatitude{43.73738316497729}});
+    params.coordinates.push_back(
+        FloatCoordinate{FloatLongitude{7.421286106109619}, FloatLatitude{43.737274640266}});
+    params.coordinates.push_back(
+        FloatCoordinate{FloatLongitude{7.420910596847533}, FloatLatitude{43.73714285999499}});
+    params.coordinates.push_back(
+        FloatCoordinate{FloatLongitude{7.420696020126342}, FloatLatitude{43.73699557581948}});
+    params.coordinates.push_back(
+        FloatCoordinate{FloatLongitude{7.42049217224121}, FloatLatitude{43.73690255404829}});
+    params.coordinates.push_back(
+        FloatCoordinate{FloatLongitude{7.420309782028198}, FloatLatitude{43.73672426191624}});
+    params.coordinates.push_back(
+        FloatCoordinate{FloatLongitude{7.420159578323363}, FloatLatitude{43.7366622471372}});
+    params.coordinates.push_back(
+        FloatCoordinate{FloatLongitude{7.420148849487305}, FloatLatitude{43.736623487867654}});
+    params.coordinates.push_back(
+        FloatCoordinate{FloatLongitude{7.419934272766113}, FloatLatitude{43.73647620241466}});
+    params.coordinates.push_back(
+        FloatCoordinate{FloatLongitude{7.419805526733398}, FloatLatitude{43.736228141885455}});
+    params.coordinates.push_back(
+        FloatCoordinate{FloatLongitude{7.419601678848267}, FloatLatitude{43.736142870841206}});
+    params.coordinates.push_back(
+        FloatCoordinate{FloatLongitude{7.419376373291015}, FloatLatitude{43.735956824504974}});
+    params.coordinates.push_back(
+        FloatCoordinate{FloatLongitude{7.419247627258301}, FloatLatitude{43.73574752168583}});
+    params.coordinates.push_back(
+        FloatCoordinate{FloatLongitude{7.419043779373169}, FloatLatitude{43.73566224995717}});
+    params.coordinates.push_back(
+        FloatCoordinate{FloatLongitude{7.418732643127442}, FloatLatitude{43.735406434042645}});
+    params.coordinates.push_back(
+        FloatCoordinate{FloatLongitude{7.418657541275024}, FloatLatitude{43.735321161828274}});
+    params.coordinates.push_back(
+        FloatCoordinate{FloatLongitude{7.418593168258667}, FloatLatitude{43.73521263337983}});
+    params.coordinates.push_back(
+        FloatCoordinate{FloatLongitude{7.418367862701416}, FloatLatitude{43.73508084857086}});
+    params.coordinates.push_back(
+        FloatCoordinate{FloatLongitude{7.418346405029297}, FloatLatitude{43.73484828643578}});
+    params.coordinates.push_back(
+        FloatCoordinate{FloatLongitude{7.4180567264556885}, FloatLatitude{43.734437424456566}});
+    params.coordinates.push_back(
+        FloatCoordinate{FloatLongitude{7.417809963226318}, FloatLatitude{43.73414284243448}});
+    params.coordinates.push_back(
+        FloatCoordinate{FloatLongitude{7.417863607406615}, FloatLatitude{43.73375523230292}});
+    params.coordinates.push_back(
+        FloatCoordinate{FloatLongitude{7.417809963226318}, FloatLatitude{43.73386376339265}});
+    params.coordinates.push_back(
+        FloatCoordinate{FloatLongitude{7.417895793914795}, FloatLatitude{43.73365445325776}});
+    params.coordinates.push_back(
+        FloatCoordinate{FloatLongitude{7.418067455291747}, FloatLatitude{43.73343739012297}});
+    params.coordinates.push_back(
+        FloatCoordinate{FloatLongitude{7.41803526878357}, FloatLatitude{43.73319706930599}});
+    params.coordinates.push_back(
+        FloatCoordinate{FloatLongitude{7.418024539947509}, FloatLatitude{43.73295674752463}});
+    params.coordinates.push_back(
+        FloatCoordinate{FloatLongitude{7.417906522750854}, FloatLatitude{43.73284821479115}});
+    params.coordinates.push_back(
+        FloatCoordinate{FloatLongitude{7.417917251586914}, FloatLatitude{43.7327551865773}});
+    params.coordinates.push_back(
+        FloatCoordinate{FloatLongitude{7.417434453964233}, FloatLatitude{43.73281720540258}});
+    params.coordinates.push_back(
+        FloatCoordinate{FloatLongitude{7.4173808097839355}, FloatLatitude{43.73307303237796}});
+    params.coordinates.push_back(
+        FloatCoordinate{FloatLongitude{7.41750955581665}, FloatLatitude{43.73328234454499}});
+    params.coordinates.push_back(
+        FloatCoordinate{FloatLongitude{7.417563199996948}, FloatLatitude{43.73352266501975}});
+    params.coordinates.push_back(
+        FloatCoordinate{FloatLongitude{7.41750955581665}, FloatLatitude{43.733770736756355}});
+    params.coordinates.push_back(
+        FloatCoordinate{FloatLongitude{7.417466640472412}, FloatLatitude{43.73409632935116}});
+    params.coordinates.push_back(
+        FloatCoordinate{FloatLongitude{7.417230606079102}, FloatLatitude{43.73428238146768}});
+    params.coordinates.push_back(
+        FloatCoordinate{FloatLongitude{7.41724133491516}, FloatLatitude{43.73405756842078}});
+    params.coordinates.push_back(
+        FloatCoordinate{FloatLongitude{7.4169838428497314}, FloatLatitude{43.73449168940785}});
+    params.coordinates.push_back(
+        FloatCoordinate{FloatLongitude{7.41701602935791}, FloatLatitude{43.734615723397525}});
+    params.coordinates.push_back(
+        FloatCoordinate{FloatLongitude{7.41704821586609}, FloatLatitude{43.73487929477265}});
+    params.coordinates.push_back(
+        FloatCoordinate{FloatLongitude{7.41725206375122}, FloatLatitude{43.734949063471895}});
+    params.coordinates.push_back(
+        FloatCoordinate{FloatLongitude{7.4173808097839355}, FloatLatitude{43.73533666587628}});
+    params.coordinates.push_back(
+        FloatCoordinate{FloatLongitude{7.41750955581665}, FloatLatitude{43.735623490040375}});
+    params.coordinates.push_back(
+        FloatCoordinate{FloatLongitude{7.417799234390259}, FloatLatitude{43.73577852955704}});
+    params.coordinates.push_back(
+        FloatCoordinate{FloatLongitude{7.4180781841278085}, FloatLatitude{43.735972328388435}});
+    params.coordinates.push_back(
+        FloatCoordinate{FloatLongitude{7.41850733757019}, FloatLatitude{43.73608860738618}});
+    params.coordinates.push_back(
+        FloatCoordinate{FloatLongitude{7.418850660324096}, FloatLatitude{43.736228141885455}});
+    params.coordinates.push_back(
+        FloatCoordinate{FloatLongitude{7.419086694717407}, FloatLatitude{43.73636767605958}});
+    params.coordinates.push_back(
+        FloatCoordinate{FloatLongitude{7.419333457946777}, FloatLatitude{43.73664674343239}});
+    params.coordinates.push_back(
+        FloatCoordinate{FloatLongitude{7.419633865356444}, FloatLatitude{43.73676302112054}});
+    params.coordinates.push_back(
+        FloatCoordinate{FloatLongitude{7.419784069061279}, FloatLatitude{43.737096349241845}});
+    params.coordinates.push_back(
+        FloatCoordinate{FloatLongitude{7.420030832290649}, FloatLatitude{43.73720487427631}});
+    params.coordinates.push_back(
+        FloatCoordinate{FloatLongitude{7.419601678848267}, FloatLatitude{43.73708084564945}});
+    params.coordinates.push_back(
+        FloatCoordinate{FloatLongitude{7.419333457946777}, FloatLatitude{43.73708084564945}});
+    params.coordinates.push_back(
+        FloatCoordinate{FloatLongitude{7.419043779373169}, FloatLatitude{43.737158363571325}});
+    params.coordinates.push_back(
+        FloatCoordinate{FloatLongitude{7.418915033340454}, FloatLatitude{43.737305647346446}});
+    params.coordinates.push_back(
+        FloatCoordinate{FloatLongitude{7.41848587989807}, FloatLatitude{43.7374916894919}});
+    params.coordinates.push_back(
+        FloatCoordinate{FloatLongitude{7.418271303176879}, FloatLatitude{43.73746843425534}});
+    params.coordinates.push_back(
+        FloatCoordinate{FloatLongitude{7.417960166931152}, FloatLatitude{43.73744517900973}});
+    params.coordinates.push_back(
+        FloatCoordinate{FloatLongitude{7.417885065078735}, FloatLatitude{43.737212626056944}});
+    params.coordinates.push_back(
+        FloatCoordinate{FloatLongitude{7.417563199996948}, FloatLatitude{43.73703433484817}});
+    params.coordinates.push_back(
+        FloatCoordinate{FloatLongitude{7.4173057079315186}, FloatLatitude{43.73692580950463}});
+    params.coordinates.push_back(
+        FloatCoordinate{FloatLongitude{7.417144775390625}, FloatLatitude{43.7367707729584}});
+    params.coordinates.push_back(
+        FloatCoordinate{FloatLongitude{7.416973114013672}, FloatLatitude{43.73653821738638}});
+    params.coordinates.push_back(
+        FloatCoordinate{FloatLongitude{7.416855096817017}, FloatLatitude{43.73639868360965}});
+    params.coordinates.push_back(
+        FloatCoordinate{FloatLongitude{7.4167799949646}, FloatLatitude{43.736142870841206}});
+    params.coordinates.push_back(
+        FloatCoordinate{FloatLongitude{7.41675853729248}, FloatLatitude{43.735848297208605}});
+    params.coordinates.push_back(
+        FloatCoordinate{FloatLongitude{7.416619062423706}, FloatLatitude{43.73567000193752}});
+    params.coordinates.push_back(
+        FloatCoordinate{FloatLongitude{7.416543960571288}, FloatLatitude{43.735406434042645}});
+    params.coordinates.push_back(
+        FloatCoordinate{FloatLongitude{7.416479587554932}, FloatLatitude{43.73529790574875}});
+    params.coordinates.push_back(
+        FloatCoordinate{FloatLongitude{7.416415214538574}, FloatLatitude{43.73515061703527}});
+    params.coordinates.push_back(
+        FloatCoordinate{FloatLongitude{7.416350841522218}, FloatLatitude{43.73490255101476}});
+    params.coordinates.push_back(
+        FloatCoordinate{FloatLongitude{7.416340112686156}, FloatLatitude{43.73475526132885}});
+    params.coordinates.push_back(
+        FloatCoordinate{FloatLongitude{7.416222095489501}, FloatLatitude{43.73446068087028}});
+    params.coordinates.push_back(
+        FloatCoordinate{FloatLongitude{7.416243553161621}, FloatLatitude{43.73430563794159}});
+    params.coordinates.push_back(
+        FloatCoordinate{FloatLongitude{7.416050434112548}, FloatLatitude{43.73403431185051}});
+    params.coordinates.push_back(
+        FloatCoordinate{FloatLongitude{7.415814399719239}, FloatLatitude{43.73382500231174}});
+    params.coordinates.push_back(
+        FloatCoordinate{FloatLongitude{7.415750026702881}, FloatLatitude{43.73354592178871}});
+    params.coordinates.push_back(
+        FloatCoordinate{FloatLongitude{7.415513992309569}, FloatLatitude{43.73347615145474}});
+    params.coordinates.push_back(
+        FloatCoordinate{FloatLongitude{7.415342330932617}, FloatLatitude{43.733251335381205}});
+
+    TIMER_START(routes);
+    auto NUM = 100;
+    for (int i = 0; i < NUM; ++i)
+    {
+        json::Object result;
+        const auto rc = osrm.Match(params, result);
+        if (rc != Status::Ok || result.values.at("matchings").get<json::Array>().values.size() != 1)
+        {
+            return EXIT_FAILURE;
+        }
+    }
+    TIMER_STOP(routes);
+    std::cout << (TIMER_MSEC(routes) / NUM) << "ms/req at " << params.coordinates.size()
+              << " coordinate" << std::endl;
+    std::cout << (TIMER_MSEC(routes) / NUM / params.coordinates.size()) << "ms/coordinate"
+              << std::endl;
+
+    return EXIT_SUCCESS;
+}
+catch (const std::exception &e)
+{
+    std::cerr << "Error: " << e.what() << std::endl;
+    return EXIT_FAILURE;
+}
diff --git a/src/benchmarks/static_rtree.cpp b/src/benchmarks/static_rtree.cpp
index 42206f0..7316ad2 100644
--- a/src/benchmarks/static_rtree.cpp
+++ b/src/benchmarks/static_rtree.cpp
@@ -1,10 +1,10 @@
-#include "engine/geospatial_query.hpp"
-#include "extractor/query_node.hpp"
-#include "extractor/edge_based_node.hpp"
 #include "util/static_rtree.hpp"
-#include "util/timing_util.hpp"
-#include "util/coordinate.hpp"
+#include "extractor/edge_based_node.hpp"
+#include "extractor/query_node.hpp"
 #include "mocks/mock_datafacade.hpp"
+#include "engine/geospatial_query.hpp"
+#include "util/coordinate.hpp"
+#include "util/timing_util.hpp"
 
 #include <iostream>
 #include <random>
@@ -79,14 +79,12 @@ void benchmark(BenchStaticRTree &rtree, unsigned num_queries)
                              util::FixedLatitude{lat_udist(mt_rand)});
     }
 
-    benchmarkQuery(queries, "raw RTree queries (1 result)", [&rtree](const util::Coordinate &q)
-                   {
-                       return rtree.Nearest(q, 1);
-                   });
-    benchmarkQuery(queries, "raw RTree queries (10 results)", [&rtree](const util::Coordinate &q)
-                   {
-                       return rtree.Nearest(q, 10);
-                   });
+    benchmarkQuery(queries, "raw RTree queries (1 result)", [&rtree](const util::Coordinate &q) {
+        return rtree.Nearest(q, 1);
+    });
+    benchmarkQuery(queries, "raw RTree queries (10 results)", [&rtree](const util::Coordinate &q) {
+        return rtree.Nearest(q, 10);
+    });
 }
 }
 }
diff --git a/src/contractor/contractor.cpp b/src/contractor/contractor.cpp
index 742c561..c4adcef 100644
--- a/src/contractor/contractor.cpp
+++ b/src/contractor/contractor.cpp
@@ -2,42 +2,52 @@
 #include "contractor/crc32_processor.hpp"
 #include "contractor/graph_contractor.hpp"
 
-#include "extractor/node_based_edge.hpp"
 #include "extractor/compressed_edge_container.hpp"
+#include "extractor/node_based_edge.hpp"
 
-#include "util/static_graph.hpp"
-#include "util/static_rtree.hpp"
+#include "util/exception.hpp"
 #include "util/graph_loader.hpp"
-#include "util/io.hpp"
 #include "util/integer_range.hpp"
-#include "util/exception.hpp"
+#include "util/io.hpp"
 #include "util/simple_logger.hpp"
+#include "util/static_graph.hpp"
+#include "util/static_rtree.hpp"
 #include "util/string_util.hpp"
 #include "util/timing_util.hpp"
 #include "util/typedefs.hpp"
 
-#include <fast-cpp-csv-parser/csv.h>
-
 #include <boost/assert.hpp>
 #include <boost/filesystem/fstream.hpp>
 #include <boost/functional/hash.hpp>
-
+#include <boost/interprocess/file_mapping.hpp>
+#include <boost/interprocess/mapped_region.hpp>
+#include <boost/spirit/include/qi.hpp>
+
+#include <tbb/blocked_range.h>
+#include <tbb/concurrent_unordered_map.h>
+#include <tbb/enumerable_thread_specific.h>
+#include <tbb/parallel_for.h>
+#include <tbb/parallel_for_each.h>
+#include <tbb/parallel_invoke.h>
 #include <tbb/parallel_sort.h>
+#include <tbb/spin_mutex.h>
 
-#include <cstdint>
+#include <algorithm>
 #include <bitset>
-#include <chrono>
+#include <cstdint>
+#include <fstream>
+#include <iterator>
 #include <memory>
 #include <thread>
-#include <iterator>
 #include <tuple>
+#include <vector>
 
 namespace std
 {
 
 template <> struct hash<std::pair<OSMNodeID, OSMNodeID>>
 {
-    std::size_t operator()(const std::pair<OSMNodeID, OSMNodeID> &k) const
+    std::size_t operator()(const std::pair<OSMNodeID, OSMNodeID> &k) const noexcept
     {
         return static_cast<uint64_t>(k.first) ^ (static_cast<uint64_t>(k.second) << 12);
     }
@@ -45,7 +55,7 @@ template <> struct hash<std::pair<OSMNodeID, OSMNodeID>>
 
 template <> struct hash<std::tuple<OSMNodeID, OSMNodeID, OSMNodeID>>
 {
-    std::size_t operator()(const std::tuple<OSMNodeID, OSMNodeID, OSMNodeID> &k) const
+    std::size_t operator()(const std::tuple<OSMNodeID, OSMNodeID, OSMNodeID> &k) const noexcept
     {
         std::size_t seed = 0;
         boost::hash_combine(seed, static_cast<uint64_t>(std::get<0>(k)));
@@ -83,11 +93,17 @@ int Contractor::Run()
 
     util::DeallocatingVector<extractor::EdgeBasedEdge> edge_based_edge_list;
 
-    std::size_t max_edge_id = LoadEdgeExpandedGraph(
-        config.edge_based_graph_path, edge_based_edge_list, config.edge_segment_lookup_path,
-        config.edge_penalty_path, config.segment_speed_lookup_paths,
-        config.turn_penalty_lookup_paths, config.node_based_graph_path, config.geometry_path,
-        config.datasource_names_path, config.datasource_indexes_path, config.rtree_leaf_path);
+    std::size_t max_edge_id = LoadEdgeExpandedGraph(config.edge_based_graph_path,
+                                                    edge_based_edge_list,
+                                                    config.edge_segment_lookup_path,
+                                                    config.edge_penalty_path,
+                                                    config.segment_speed_lookup_paths,
+                                                    config.turn_penalty_lookup_paths,
+                                                    config.node_based_graph_path,
+                                                    config.geometry_path,
+                                                    config.datasource_names_path,
+                                                    config.datasource_indexes_path,
+                                                    config.rtree_leaf_path);
 
     // Contracting the edge-expanded graph
 
@@ -112,8 +128,12 @@ int Contractor::Run()
     }
 
     util::DeallocatingVector<QueryEdge> contracted_edge_list;
-    ContractGraph(max_edge_id, edge_based_edge_list, contracted_edge_list, std::move(node_weights),
-                  is_core_node, node_levels);
+    ContractGraph(max_edge_id,
+                  edge_based_edge_list,
+                  contracted_edge_list,
+                  std::move(node_weights),
+                  is_core_node,
+                  node_levels);
     TIMER_STOP(contraction);
 
     util::SimpleLogger().Write() << "Contraction took " << TIMER_SEC(contraction) << " sec";
@@ -137,6 +157,198 @@ int Contractor::Run()
     return 0;
 }
 
+// Utilities for LoadEdgeExpandedGraph to restore my sanity
+namespace
+{
+
+struct Segment final
+{
+    OSMNodeID from, to;
+};
+
+struct SpeedSource final
+{
+    unsigned speed;
+    std::uint8_t source;
+};
+
+struct SegmentSpeedSource final
+{
+    Segment segment;
+    SpeedSource speed_source;
+};
+
+using SegmentSpeedSourceFlatMap = std::vector<SegmentSpeedSource>;
+
+// Binary Search over a flattened key,val Segment storage
+SegmentSpeedSourceFlatMap::iterator find(SegmentSpeedSourceFlatMap &map, const Segment &key)
+{
+    const auto last = end(map);
+
+    const auto by_segment = [](const SegmentSpeedSource &lhs, const SegmentSpeedSource &rhs) {
+        return std::tie(lhs.segment.from, lhs.segment.to) >
+               std::tie(rhs.segment.from, rhs.segment.to);
+    };
+
+    auto it = std::lower_bound(begin(map), last, SegmentSpeedSource{key, {0, 0}}, by_segment);
+
+    if (it != last && (std::tie(it->segment.from, it->segment.to) == std::tie(key.from, key.to)))
+        return it;
+
+    return last;
+}
+
+// Convenience aliases. TODO: make actual types at some point in time.
+// TODO: turn penalties need flat map + binary search optimization, take a look at segment speeds
+
+using Turn = std::tuple<OSMNodeID, OSMNodeID, OSMNodeID>;
+using TurnHasher = std::hash<Turn>;
+using PenaltySource = std::pair<double, std::uint8_t>;
+using TurnPenaltySourceMap = tbb::concurrent_unordered_map<Turn, PenaltySource, TurnHasher>;
+
+// Functions for parsing files and creating lookup tables
+
+SegmentSpeedSourceFlatMap
+parse_segment_lookup_from_csv_files(const std::vector<std::string> &segment_speed_filenames)
+{
+    // TODO: shares code with turn penalty lookup parse function
+
+    using Mutex = tbb::spin_mutex;
+
+    // Loaded and parsed in parallel, at the end we combine results in a flattened map-ish view
+    SegmentSpeedSourceFlatMap flatten;
+    Mutex flatten_mutex;
+
+    const auto parse_segment_speed_file = [&](const std::size_t idx) {
+        const auto file_id = idx + 1; // starts at one, zero means we assigned the weight
+        const auto filename = segment_speed_filenames[idx];
+
+        std::ifstream segment_speed_file{filename, std::ios::binary};
+        if (!segment_speed_file)
+            throw util::exception{"Unable to open segment speed file " + filename};
+
+        SegmentSpeedSourceFlatMap local;
+
+        std::uint64_t from_node_id{};
+        std::uint64_t to_node_id{};
+        unsigned speed{};
+
+        for (std::string line; std::getline(segment_speed_file, line);)
+        {
+            using namespace boost::spirit::qi;
+
+            auto it = begin(line);
+            const auto last = end(line);
+
+            // The ulong_long -> uint64_t will likely break on 32bit platforms
+            const auto ok = parse(it,
+                                  last,                                              //
+                                  (ulong_long >> ',' >> ulong_long >> ',' >> uint_), //
+                                  from_node_id,
+                                  to_node_id,
+                                  speed); //
+
+            if (!ok || it != last)
+                throw util::exception{"Segment speed file " + filename + " malformed"};
+
+            SegmentSpeedSource val{
+                {static_cast<OSMNodeID>(from_node_id), static_cast<OSMNodeID>(to_node_id)},
+                {speed, static_cast<std::uint8_t>(file_id)}};
+
+            local.push_back(std::move(val));
+        }
+
+        util::SimpleLogger().Write() << "Loaded speed file " << filename << " with " << local.size()
+                                     << " speeds";
+
+        {
+            Mutex::scoped_lock _{flatten_mutex};
+
+            flatten.insert(end(flatten),
+                           std::make_move_iterator(begin(local)),
+                           std::make_move_iterator(end(local)));
+        }
+    };
+
+    tbb::parallel_for(std::size_t{0}, segment_speed_filenames.size(), parse_segment_speed_file);
+
+    // With flattened map-ish view of all the files, sort and unique them on from,to,source
+    // The greater '>' is used here since we want to give files later on higher precedence
+    const auto sort_by = [](const SegmentSpeedSource &lhs, const SegmentSpeedSource &rhs) {
+        return std::tie(lhs.segment.from, lhs.segment.to, lhs.speed_source.source) >
+               std::tie(rhs.segment.from, rhs.segment.to, rhs.speed_source.source);
+    };
+
+    std::stable_sort(begin(flatten), end(flatten), sort_by);
+
+    // Unique only on from,to to take the source precedence into account and remove duplicates
+    const auto unique_by = [](const SegmentSpeedSource &lhs, const SegmentSpeedSource &rhs) {
+        return std::tie(lhs.segment.from, lhs.segment.to) ==
+               std::tie(rhs.segment.from, rhs.segment.to);
+    };
+
+    const auto it = std::unique(begin(flatten), end(flatten), unique_by);
+
+    flatten.erase(it, end(flatten));
+
+    util::SimpleLogger().Write() << "In total loaded " << segment_speed_filenames.size()
+                                 << " speed file(s) with a total of " << flatten.size()
+                                 << " unique values";
+
+    return flatten;
+}
+
+TurnPenaltySourceMap
+parse_turn_penalty_lookup_from_csv_files(const std::vector<std::string> &turn_penalty_filenames)
+{
+    // TODO: shares code with turn penalty lookup parse function
+    TurnPenaltySourceMap map;
+
+    const auto parse_turn_penalty_file = [&](const std::size_t idx) {
+        const auto file_id = idx + 1; // starts at one, zero means we assigned the weight
+        const auto filename = turn_penalty_filenames[idx];
+
+        std::ifstream turn_penalty_file{filename, std::ios::binary};
+        if (!turn_penalty_file)
+            throw util::exception{"Unable to open turn penalty file " + filename};
+
+        std::uint64_t from_node_id{};
+        std::uint64_t via_node_id{};
+        std::uint64_t to_node_id{};
+        double penalty{};
+
+        for (std::string line; std::getline(turn_penalty_file, line);)
+        {
+            using namespace boost::spirit::qi;
+
+            auto it = begin(line);
+            const auto last = end(line);
+
+            // The ulong_long -> uint64_t will likely break on 32bit platforms
+            const auto ok =
+                parse(it,
+                      last,                                                                     //
+                      (ulong_long >> ',' >> ulong_long >> ',' >> ulong_long >> ',' >> double_), //
+                      from_node_id,
+                      via_node_id,
+                      to_node_id,
+                      penalty); //
+
+            if (!ok || it != last)
+                throw util::exception{"Turn penalty file " + filename + " malformed"};
+
+            map[std::make_tuple(
+                OSMNodeID(from_node_id), OSMNodeID(via_node_id), OSMNodeID(to_node_id))] =
+                std::make_pair(penalty, file_id);
+        }
+    };
+
+    tbb::parallel_for(std::size_t{0}, turn_penalty_filenames.size(), parse_turn_penalty_file);
+
+    return map;
+}
+} // anon ns
+
 std::size_t Contractor::LoadEdgeExpandedGraph(
     std::string const &edge_based_graph_filename,
     util::DeallocatingVector<extractor::EdgeBasedEdge> &edge_based_edge_list,
@@ -150,8 +362,13 @@ std::size_t Contractor::LoadEdgeExpandedGraph(
     const std::string &datasource_indexes_filename,
     const std::string &rtree_leaf_filename)
 {
+    if (segment_speed_filenames.size() > 255 || turn_penalty_filenames.size() > 255)
+        throw util::exception("Limit of 255 segment speed and turn penalty files each reached");
+
     util::SimpleLogger().Write() << "Opening " << edge_based_graph_filename;
     boost::filesystem::ifstream input_stream(edge_based_graph_filename, std::ios::binary);
+    if (!input_stream)
+        throw util::exception("Could not load edge based graph file");
 
     const bool update_edge_weights = !segment_speed_filenames.empty();
     const bool update_turn_penalties = !turn_penalty_filenames.empty();
@@ -186,134 +403,91 @@ std::size_t Contractor::LoadEdgeExpandedGraph(
     util::SimpleLogger().Write() << "Reading " << number_of_edges
                                  << " edges from the edge based graph";
 
-    std::unordered_map<std::pair<OSMNodeID, OSMNodeID>, std::pair<unsigned, uint8_t>>
-        segment_speed_lookup;
-    std::unordered_map<std::tuple<OSMNodeID, OSMNodeID, OSMNodeID>, std::pair<double, uint8_t>>
-        turn_penalty_lookup;
+    SegmentSpeedSourceFlatMap segment_speed_lookup;
+    TurnPenaltySourceMap turn_penalty_lookup;
+
+    const auto parse_segment_speeds = [&] {
+        if (update_edge_weights)
+            segment_speed_lookup = parse_segment_lookup_from_csv_files(segment_speed_filenames);
+    };
+
+    const auto parse_turn_penalties = [&] {
+        if (update_turn_penalties)
+            turn_penalty_lookup = parse_turn_penalty_lookup_from_csv_files(turn_penalty_filenames);
+    };
 
-    // If we update the edge weights, this file will hold the datasource information
-    // for each segment
+    // 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
     std::vector<uint8_t> m_geometry_datasource;
 
-    if (update_edge_weights || update_turn_penalties)
-    {
-        std::uint8_t segment_file_id = 1;
-        std::uint8_t turn_file_id = 1;
+    std::vector<extractor::QueryNode> internal_to_external_node_map;
+    std::vector<unsigned> m_geometry_indices;
+    std::vector<extractor::CompressedEdgeContainer::CompressedEdge> m_geometry_list;
 
-        if (update_edge_weights)
-        {
-            for (auto segment_speed_filename : segment_speed_filenames)
-            {
-                util::SimpleLogger().Write()
-                    << "Segment speed data supplied, will update edge weights from "
-                    << segment_speed_filename;
-                io::CSVReader<3> csv_in(segment_speed_filename);
-                csv_in.set_header("from_node", "to_node", "speed");
-                std::uint64_t from_node_id{};
-                std::uint64_t to_node_id{};
-                unsigned speed{};
-                while (csv_in.read_row(from_node_id, to_node_id, speed))
-                {
-                    segment_speed_lookup[std::make_pair(OSMNodeID(from_node_id),
-                                                        OSMNodeID(to_node_id))] =
-                        std::make_pair(speed, segment_file_id);
-                }
-                ++segment_file_id;
+    const auto maybe_load_internal_to_external_node_map = [&] {
+        if (!(update_edge_weights || update_turn_penalties))
+            return;
 
-                // Check for overflow
-                if (segment_file_id == 0)
-                {
-                    throw util::exception(
-                        "Sorry, there's a limit of 255 segment speed files; you supplied too many");
-                }
-            }
-        }
+        boost::filesystem::ifstream nodes_input_stream(nodes_filename, std::ios::binary);
 
-        if (update_turn_penalties)
+        if (!nodes_input_stream)
         {
-            for (auto turn_penalty_filename : turn_penalty_filenames)
-            {
-                util::SimpleLogger().Write()
-                    << "Turn penalty data supplied, will update turn penalties from "
-                    << turn_penalty_filename;
-                io::CSVReader<4> csv_in(turn_penalty_filename);
-                csv_in.set_header("from_node", "via_node", "to_node", "penalty");
-                uint64_t from_node_id{};
-                uint64_t via_node_id{};
-                uint64_t to_node_id{};
-                double penalty{};
-                while (csv_in.read_row(from_node_id, via_node_id, to_node_id, penalty))
-                {
-                    turn_penalty_lookup[std::make_tuple(
-                        OSMNodeID(from_node_id), OSMNodeID(via_node_id), OSMNodeID(to_node_id))] =
-                        std::make_pair(penalty, turn_file_id);
-                }
-                ++turn_file_id;
-
-                // Check for overflow
-                if (turn_file_id == 0)
-                {
-                    throw util::exception(
-                        "Sorry, there's a limit of 255 turn penalty files; you supplied too many");
-                }
-            }
+            throw util::exception("Failed to open " + nodes_filename);
         }
 
-        std::vector<extractor::QueryNode> internal_to_external_node_map;
-
-        // Here, we have to update the compressed geometry weights
-        // First, we need the external-to-internal node lookup table
-        {
-            boost::filesystem::ifstream nodes_input_stream(nodes_filename, std::ios::binary);
+        unsigned number_of_nodes = 0;
+        nodes_input_stream.read((char *)&number_of_nodes, sizeof(unsigned));
+        internal_to_external_node_map.resize(number_of_nodes);
 
-            if (!nodes_input_stream)
-            {
-                throw util::exception("Failed to open " + nodes_filename);
-            }
+        // Load all the query nodes into a vector
+        nodes_input_stream.read(reinterpret_cast<char *>(&(internal_to_external_node_map[0])),
+                                number_of_nodes * sizeof(extractor::QueryNode));
+    };
 
-            unsigned number_of_nodes = 0;
-            nodes_input_stream.read((char *)&number_of_nodes, sizeof(unsigned));
-            internal_to_external_node_map.resize(number_of_nodes);
+    const auto maybe_load_geometries = [&] {
+        if (!(update_edge_weights || update_turn_penalties))
+            return;
 
-            // Load all the query nodes into a vector
-            nodes_input_stream.read(reinterpret_cast<char *>(&(internal_to_external_node_map[0])),
-                                    number_of_nodes * sizeof(extractor::QueryNode));
+        std::ifstream geometry_stream(geometry_filename, std::ios::binary);
+        if (!geometry_stream)
+        {
+            throw util::exception("Failed to open " + geometry_filename);
         }
+        unsigned number_of_indices = 0;
+        unsigned number_of_compressed_geometries = 0;
 
-        std::vector<unsigned> m_geometry_indices;
-        std::vector<extractor::CompressedEdgeContainer::CompressedEdge> m_geometry_list;
+        geometry_stream.read((char *)&number_of_indices, sizeof(unsigned));
 
+        m_geometry_indices.resize(number_of_indices);
+        if (number_of_indices > 0)
         {
-            std::ifstream geometry_stream(geometry_filename, std::ios::binary);
-            if (!geometry_stream)
-            {
-                throw util::exception("Failed to open " + geometry_filename);
-            }
-            unsigned number_of_indices = 0;
-            unsigned number_of_compressed_geometries = 0;
+            geometry_stream.read((char *)&(m_geometry_indices[0]),
+                                 number_of_indices * sizeof(unsigned));
+        }
 
-            geometry_stream.read((char *)&number_of_indices, sizeof(unsigned));
+        geometry_stream.read((char *)&number_of_compressed_geometries, sizeof(unsigned));
 
-            m_geometry_indices.resize(number_of_indices);
-            if (number_of_indices > 0)
-            {
-                geometry_stream.read((char *)&(m_geometry_indices[0]),
-                                     number_of_indices * sizeof(unsigned));
-            }
+        BOOST_ASSERT(m_geometry_indices.back() == number_of_compressed_geometries);
+        m_geometry_list.resize(number_of_compressed_geometries);
 
-            geometry_stream.read((char *)&number_of_compressed_geometries, sizeof(unsigned));
+        if (number_of_compressed_geometries > 0)
+        {
+            geometry_stream.read((char *)&(m_geometry_list[0]),
+                                 number_of_compressed_geometries *
+                                     sizeof(extractor::CompressedEdgeContainer::CompressedEdge));
+        }
+    };
 
-            BOOST_ASSERT(m_geometry_indices.back() == number_of_compressed_geometries);
-            m_geometry_list.resize(number_of_compressed_geometries);
+    // Folds all our actions into independently concurrently executing lambdas
+    tbb::parallel_invoke(parse_segment_speeds,
+                         parse_turn_penalties, //
+                         maybe_load_internal_to_external_node_map,
+                         maybe_load_geometries);
 
-            if (number_of_compressed_geometries > 0)
-            {
-                geometry_stream.read(
-                    (char *)&(m_geometry_list[0]),
-                    number_of_compressed_geometries *
-                        sizeof(extractor::CompressedEdgeContainer::CompressedEdge));
-            }
-        }
+    if (update_edge_weights || update_turn_penalties)
+    {
+        // Here, we have to update the compressed geometry weights
+        // First, we need the external-to-internal node lookup table
 
         // This is a list of the "data source id" for every segment in the compressed
         // geometry container.  We assume that everything so far has come from the
@@ -326,141 +500,187 @@ std::size_t Contractor::LoadEdgeExpandedGraph(
         // Now, we iterate over all the segments stored in the StaticRTree, updating
         // the packed geometry weights in the `.geometries` file (note: we do not
         // update the RTree itself, we just use the leaf nodes to iterate over all segments)
-        {
-
-            using LeafNode = util::StaticRTree<extractor::EdgeBasedNode>::LeafNode;
-
-            std::ifstream leaf_node_file(rtree_leaf_filename, std::ios::binary | std::ios::in | std::ios::ate);
-            if (!leaf_node_file)
+        using LeafNode = util::StaticRTree<extractor::EdgeBasedNode>::LeafNode;
+
+        using boost::interprocess::file_mapping;
+        using boost::interprocess::mapped_region;
+        using boost::interprocess::read_only;
+
+        const file_mapping mapping{rtree_leaf_filename.c_str(), read_only};
+        mapped_region region{mapping, read_only};
+        region.advise(mapped_region::advice_willneed);
+
+        const auto bytes = region.get_size();
+        const auto first = static_cast<const LeafNode *>(region.get_address());
+        const auto last = first + (bytes / sizeof(LeafNode));
+
+        // vector to count used speeds for logging
+        // size offset by one since index 0 is used for speeds not from external file
+        using counters_type = std::vector<std::size_t>;
+        std::size_t num_counters = segment_speed_filenames.size() + 1;
+        tbb::enumerable_thread_specific<counters_type> segment_speeds_counters(
+            counters_type(num_counters, 0));
+        const constexpr auto LUA_SOURCE = 0;
+
+        tbb::parallel_for_each(first, last, [&](const LeafNode &current_node) {
+            auto &counters = segment_speeds_counters.local();
+            for (size_t i = 0; i < current_node.object_count; i++)
             {
-                throw util::exception("Failed to open " + rtree_leaf_filename);
-            }
-            std::size_t leaf_nodes_count = leaf_node_file.tellg() / sizeof(LeafNode);
-            leaf_node_file.seekg(0, std::ios::beg);
+                const auto &leaf_object = current_node.objects[i];
+                extractor::QueryNode *u;
+                extractor::QueryNode *v;
 
-            LeafNode current_node;
-            while (leaf_nodes_count > 0)
-            {
-                leaf_node_file.read(reinterpret_cast<char *>(&current_node), sizeof(current_node));
+                if (leaf_object.forward_packed_geometry_id != SPECIAL_EDGEID)
+                {
+                    const unsigned forward_begin =
+                        m_geometry_indices.at(leaf_object.forward_packed_geometry_id);
+
+                    if (leaf_object.fwd_segment_position == 0)
+                    {
+                        u = &(internal_to_external_node_map[leaf_object.u]);
+                        v = &(
+                            internal_to_external_node_map[m_geometry_list[forward_begin].node_id]);
+                    }
+                    else
+                    {
+                        u = &(internal_to_external_node_map
+                                  [m_geometry_list[forward_begin +
+                                                   leaf_object.fwd_segment_position - 1]
+                                       .node_id]);
+                        v = &(internal_to_external_node_map
+                                  [m_geometry_list[forward_begin + leaf_object.fwd_segment_position]
+                                       .node_id]);
+                    }
+                    const double segment_length = util::coordinate_calculation::greatCircleDistance(
+                        util::Coordinate{u->lon, u->lat}, util::Coordinate{v->lon, v->lat});
 
-                for (size_t i = 0; i < current_node.object_count; i++)
+                    auto forward_speed_iter =
+                        find(segment_speed_lookup, Segment{u->node_id, v->node_id});
+                    if (forward_speed_iter != segment_speed_lookup.end())
+                    {
+                        int new_segment_weight =
+                            std::max(1,
+                                     static_cast<int>(std::floor(
+                                         (segment_length * 10.) /
+                                             (forward_speed_iter->speed_source.speed / 3.6) +
+                                         .5)));
+                        m_geometry_list[forward_begin + leaf_object.fwd_segment_position].weight =
+                            new_segment_weight;
+                        m_geometry_datasource[forward_begin + leaf_object.fwd_segment_position] =
+                            forward_speed_iter->speed_source.source;
+
+                        // count statistics for logging
+                        counters[forward_speed_iter->speed_source.source] += 1;
+                    }
+                    else
+                    {
+                        // count statistics for logging
+                        counters[LUA_SOURCE] += 1;
+                    }
+                }
+                if (leaf_object.reverse_packed_geometry_id != SPECIAL_EDGEID)
                 {
-                    auto &leaf_object = current_node.objects[i];
-                    extractor::QueryNode *u;
-                    extractor::QueryNode *v;
+                    const unsigned reverse_begin =
+                        m_geometry_indices.at(leaf_object.reverse_packed_geometry_id);
+                    const unsigned reverse_end =
+                        m_geometry_indices.at(leaf_object.reverse_packed_geometry_id + 1);
+
+                    int rev_segment_position =
+                        (reverse_end - reverse_begin) - leaf_object.fwd_segment_position - 1;
+                    if (rev_segment_position == 0)
+                    {
+                        u = &(internal_to_external_node_map[leaf_object.v]);
+                        v = &(
+                            internal_to_external_node_map[m_geometry_list[reverse_begin].node_id]);
+                    }
+                    else
+                    {
+                        u = &(
+                            internal_to_external_node_map[m_geometry_list[reverse_begin +
+                                                                          rev_segment_position - 1]
+                                                              .node_id]);
+                        v = &(internal_to_external_node_map
+                                  [m_geometry_list[reverse_begin + rev_segment_position].node_id]);
+                    }
+                    const double segment_length = util::coordinate_calculation::greatCircleDistance(
+                        util::Coordinate{u->lon, u->lat}, util::Coordinate{v->lon, v->lat});
 
-                    if (leaf_object.forward_packed_geometry_id != SPECIAL_EDGEID)
+                    auto reverse_speed_iter =
+                        find(segment_speed_lookup, Segment{u->node_id, v->node_id});
+                    if (reverse_speed_iter != segment_speed_lookup.end())
                     {
-                        const unsigned forward_begin =
-                            m_geometry_indices.at(leaf_object.forward_packed_geometry_id);
-
-                        if (leaf_object.fwd_segment_position == 0)
-                        {
-                            u = &(internal_to_external_node_map[leaf_object.u]);
-                            v = &(internal_to_external_node_map[m_geometry_list[forward_begin]
-                                                                    .node_id]);
-                        }
-                        else
-                        {
-                            u = &(internal_to_external_node_map
-                                      [m_geometry_list[forward_begin +
-                                                       leaf_object.fwd_segment_position - 1]
-                                           .node_id]);
-                            v = &(internal_to_external_node_map
-                                      [m_geometry_list[forward_begin +
-                                                       leaf_object.fwd_segment_position]
-                                           .node_id]);
-                        }
-                        const double segment_length =
-                            util::coordinate_calculation::greatCircleDistance(
-                                util::Coordinate{u->lon, u->lat}, util::Coordinate{v->lon, v->lat});
-
-                        auto forward_speed_iter =
-                            segment_speed_lookup.find(std::make_pair(u->node_id, v->node_id));
-                        if (forward_speed_iter != segment_speed_lookup.end())
-                        {
-                            int new_segment_weight =
-                                std::max(1, static_cast<int>(std::floor(
-                                                (segment_length * 10.) /
-                                                    (forward_speed_iter->second.first / 3.6) +
-                                                .5)));
-                            m_geometry_list[forward_begin + leaf_object.fwd_segment_position]
-                                .weight = new_segment_weight;
-                            m_geometry_datasource[forward_begin +
-                                                  leaf_object.fwd_segment_position] =
-                                forward_speed_iter->second.second;
-                        }
+                        int new_segment_weight =
+                            std::max(1,
+                                     static_cast<int>(std::floor(
+                                         (segment_length * 10.) /
+                                             (reverse_speed_iter->speed_source.speed / 3.6) +
+                                         .5)));
+                        m_geometry_list[reverse_begin + rev_segment_position].weight =
+                            new_segment_weight;
+                        m_geometry_datasource[reverse_begin + rev_segment_position] =
+                            reverse_speed_iter->speed_source.source;
+
+                        // count statistics for logging
+                        counters[reverse_speed_iter->speed_source.source] += 1;
                     }
-                    if (leaf_object.reverse_packed_geometry_id != SPECIAL_EDGEID)
+                    else
                     {
-                        const unsigned reverse_begin =
-                            m_geometry_indices.at(leaf_object.reverse_packed_geometry_id);
-                        const unsigned reverse_end =
-                            m_geometry_indices.at(leaf_object.reverse_packed_geometry_id + 1);
-
-                        int rev_segment_position =
-                            (reverse_end - reverse_begin) - leaf_object.fwd_segment_position - 1;
-                        if (rev_segment_position == 0)
-                        {
-                            u = &(internal_to_external_node_map[leaf_object.v]);
-                            v = &(internal_to_external_node_map[m_geometry_list[reverse_begin]
-                                                                    .node_id]);
-                        }
-                        else
-                        {
-                            u = &(internal_to_external_node_map
-                                      [m_geometry_list[reverse_begin + rev_segment_position - 1]
-                                           .node_id]);
-                            v = &(internal_to_external_node_map
-                                      [m_geometry_list[reverse_begin + rev_segment_position]
-                                           .node_id]);
-                        }
-                        const double segment_length =
-                            util::coordinate_calculation::greatCircleDistance(
-                                util::Coordinate{u->lon, u->lat}, util::Coordinate{v->lon, v->lat});
-
-                        auto reverse_speed_iter =
-                            segment_speed_lookup.find(std::make_pair(u->node_id, v->node_id));
-                        if (reverse_speed_iter != segment_speed_lookup.end())
-                        {
-                            int new_segment_weight =
-                                std::max(1, static_cast<int>(std::floor(
-                                                (segment_length * 10.) /
-                                                    (reverse_speed_iter->second.first / 3.6) +
-                                                .5)));
-                            m_geometry_list[reverse_begin + rev_segment_position].weight =
-                                new_segment_weight;
-                            m_geometry_datasource[reverse_begin + rev_segment_position] =
-                                reverse_speed_iter->second.second;
-                        }
+                        // count statistics for logging
+                        counters[LUA_SOURCE] += 1;
                     }
                 }
-                --leaf_nodes_count;
+            }
+        }); // parallel_for_each
+
+        counters_type merged_counters(num_counters, 0);
+        for (const auto &counters : segment_speeds_counters)
+        {
+            for (std::size_t i = 0; i < counters.size(); i++)
+            {
+                merged_counters[i] += counters[i];
             }
         }
 
-        // Now save out the updated compressed geometries
+        for (std::size_t i = 0; i < merged_counters.size(); i++)
         {
-            std::ofstream geometry_stream(geometry_filename, std::ios::binary);
-            if (!geometry_stream)
+            if (i == LUA_SOURCE)
+            {
+                util::SimpleLogger().Write() << "Used " << merged_counters[LUA_SOURCE]
+                                             << " speeds from LUA profile or input map";
+            }
+            else
             {
-                throw util::exception("Failed to open " + geometry_filename + " for writing");
+                // segments_speeds_counters has 0 as LUA, segment_speed_filenames not, thus we need
+                // to susbstract 1 to avoid off-by-one error
+                util::SimpleLogger().Write() << "Used " << merged_counters[i] << " speeds from "
+                                             << segment_speed_filenames[i - 1];
             }
-            const unsigned number_of_indices = m_geometry_indices.size();
-            const unsigned number_of_compressed_geometries = m_geometry_list.size();
-            geometry_stream.write(reinterpret_cast<const char *>(&number_of_indices),
-                                  sizeof(unsigned));
-            geometry_stream.write(reinterpret_cast<char *>(&(m_geometry_indices[0])),
-                                  number_of_indices * sizeof(unsigned));
-            geometry_stream.write(reinterpret_cast<const char *>(&number_of_compressed_geometries),
-                                  sizeof(unsigned));
-            geometry_stream.write(reinterpret_cast<char *>(&(m_geometry_list[0])),
-                                  number_of_compressed_geometries *
-                                      sizeof(extractor::CompressedEdgeContainer::CompressedEdge));
         }
     }
 
-    {
+    const auto maybe_save_geometries = [&] {
+        if (!(update_edge_weights || update_turn_penalties))
+            return;
+
+        // Now save out the updated compressed geometries
+        std::ofstream geometry_stream(geometry_filename, std::ios::binary);
+        if (!geometry_stream)
+        {
+            throw util::exception("Failed to open " + geometry_filename + " for writing");
+        }
+        const unsigned number_of_indices = m_geometry_indices.size();
+        const unsigned number_of_compressed_geometries = m_geometry_list.size();
+        geometry_stream.write(reinterpret_cast<const char *>(&number_of_indices), sizeof(unsigned));
+        geometry_stream.write(reinterpret_cast<char *>(&(m_geometry_indices[0])),
+                              number_of_indices * sizeof(unsigned));
+        geometry_stream.write(reinterpret_cast<const char *>(&number_of_compressed_geometries),
+                              sizeof(unsigned));
+        geometry_stream.write(reinterpret_cast<char *>(&(m_geometry_list[0])),
+                              number_of_compressed_geometries *
+                                  sizeof(extractor::CompressedEdgeContainer::CompressedEdge));
+    };
+
+    const auto save_datasource_indexes = [&] {
         std::ofstream datasource_stream(datasource_indexes_filename, std::ios::binary);
         if (!datasource_stream)
         {
@@ -474,9 +694,9 @@ std::size_t Contractor::LoadEdgeExpandedGraph(
             datasource_stream.write(reinterpret_cast<char *>(&(m_geometry_datasource[0])),
                                     number_of_datasource_entries * sizeof(uint8_t));
         }
-    }
+    };
 
-    {
+    const auto save_datastore_names = [&] {
         std::ofstream datasource_stream(datasource_names_filename, std::ios::binary);
         if (!datasource_stream)
         {
@@ -485,9 +705,15 @@ std::size_t Contractor::LoadEdgeExpandedGraph(
         datasource_stream << "lua profile" << std::endl;
         for (auto const &name : segment_speed_filenames)
         {
-            datasource_stream << name << std::endl;
+            // Only write the filename, without path or extension.
+            // This prevents information leakage, and keeps names short
+            // for rendering in the debug tiles.
+            const boost::filesystem::path p(name);
+            datasource_stream << p.stem().string() << std::endl;
         }
-    }
+    };
+
+    tbb::parallel_invoke(maybe_save_geometries, save_datasource_indexes, save_datastore_names);
 
     // TODO: can we read this in bulk?  util::DeallocatingVector isn't necessarily
     // all stored contiguously
@@ -524,16 +750,17 @@ std::size_t Contractor::LoadEdgeExpandedGraph(
                 edge_segment_input_stream.read(reinterpret_cast<char *>(&segment_weight),
                                                sizeof(segment_weight));
 
-                auto speed_iter = segment_speed_lookup.find(
-                    std::make_pair(previous_osm_node_id, this_osm_node_id));
+                auto speed_iter =
+                    find(segment_speed_lookup, Segment{previous_osm_node_id, this_osm_node_id});
                 if (speed_iter != segment_speed_lookup.end())
                 {
                     // This sets the segment weight using the same formula as the
                     // EdgeBasedGraphFactory for consistency.  The *why* of this formula
                     // is lost in the annals of time.
                     int new_segment_weight = std::max(
-                        1, static_cast<int>(std::floor(
-                               (segment_length * 10.) / (speed_iter->second.first / 3.6) + .5)));
+                        1,
+                        static_cast<int>(std::floor(
+                            (segment_length * 10.) / (speed_iter->speed_source.speed / 3.6) + .5)));
                     new_weight += new_segment_weight;
                 }
                 else
@@ -554,7 +781,8 @@ std::size_t Contractor::LoadEdgeExpandedGraph(
                                                    sizeof(via_id));
             edge_fixed_penalties_input_stream.read(reinterpret_cast<char *>(&to_id), sizeof(to_id));
 
-            auto turn_iter = turn_penalty_lookup.find(std::make_tuple(from_id, via_id, to_id));
+            const auto turn_iter =
+                turn_penalty_lookup.find(std::make_tuple(from_id, via_id, to_id));
             if (turn_iter != turn_penalty_lookup.end())
             {
                 int new_turn_weight = static_cast<int>(turn_iter->second.first * 10);
@@ -633,8 +861,7 @@ Contractor::WriteContractedGraph(unsigned max_node_id,
     const util::FingerPrint fingerprint = util::FingerPrint::GetValid();
     boost::filesystem::ofstream hsgr_output_stream(config.graph_output_path, std::ios::binary);
     hsgr_output_stream.write((char *)&fingerprint, sizeof(util::FingerPrint));
-    const unsigned max_used_node_id = [&contracted_edge_list]
-    {
+    const unsigned max_used_node_id = [&contracted_edge_list] {
         unsigned tmp_max = 0;
         for (const QueryEdge &edge : contracted_edge_list)
         {
@@ -753,8 +980,8 @@ void Contractor::ContractGraph(
     std::vector<float> node_levels;
     node_levels.swap(inout_node_levels);
 
-    GraphContractor graph_contractor(max_edge_id + 1, edge_based_edge_list, std::move(node_levels),
-                                     std::move(node_weights));
+    GraphContractor graph_contractor(
+        max_edge_id + 1, edge_based_edge_list, std::move(node_levels), std::move(node_weights));
     graph_contractor.Run(config.core_factor);
     graph_contractor.GetEdges(contracted_edge_list);
     graph_contractor.GetCoreMarker(is_core_node);
diff --git a/src/engine/api/json_factory.cpp b/src/engine/api/json_factory.cpp
index c23ca9c..464003a 100644
--- a/src/engine/api/json_factory.cpp
+++ b/src/engine/api/json_factory.cpp
@@ -4,6 +4,10 @@
 #include "engine/polyline_compressor.hpp"
 #include "util/integer_range.hpp"
 
+#include "util/guidance/bearing_class.hpp"
+#include "util/guidance/entry_class.hpp"
+#include "util/guidance/toolkit.hpp"
+
 #include <boost/assert.hpp>
 #include <boost/optional.hpp>
 
@@ -13,8 +17,8 @@
 #include <utility>
 #include <vector>
 
-using TurnType = osrm::extractor::guidance::TurnType;
-using DirectionModifier = osrm::extractor::guidance::DirectionModifier;
+namespace TurnType = osrm::extractor::guidance::TurnType;
+namespace DirectionModifier = osrm::extractor::guidance::DirectionModifier;
 using TurnInstruction = osrm::extractor::guidance::TurnInstruction;
 
 namespace osrm
@@ -28,36 +32,24 @@ namespace json
 namespace detail
 {
 
-const constexpr char *modifier_names[] = {"uturn",    "sharp right", "right", "slight right",
-                                          "straight", "slight left", "left",  "sharp left"};
+const constexpr char *modifier_names[] = {"uturn",
+                                          "sharp right",
+                                          "right",
+                                          "slight right",
+                                          "straight",
+                                          "slight left",
+                                          "left",
+                                          "sharp left"};
 
 // translations of TurnTypes. Not all types are exposed to the outside world.
 // invalid types should never be returned as part of the API
-const constexpr char *turn_type_names[] = {"invalid",
-                                           "new name",
-                                           "continue",
-                                           "turn",
-                                           "merge",
-                                           "on ramp",
-                                           "off ramp",
-                                           "fork",
-                                           "end of road",
-                                           "notification",
-                                           "roundabout",
-                                           "roundabout",
-                                           "rotary",
-                                           "rotary",
-                                           "roundabout turn",
-                                           "roundabout turn",
-                                           "invalid",
-                                           "invalid",
-                                           "invalid",
-                                           "invalid",
-                                           "invalid",
-                                           "invalid",
-                                           "invalid",
-                                           "invalid",
-                                           "invalid"};
+const constexpr char *turn_type_names[] = {
+    "invalid",         "new name",   "continue", "turn",        "merge",
+    "on ramp",         "off ramp",   "fork",     "end of road", "notification",
+    "roundabout",      "roundabout", "rotary",   "rotary",      "roundabout turn",
+    "roundabout turn", "invalid",    "invalid",  "invalid",     "invalid",
+    "invalid",         "invalid",    "invalid",  "invalid",     "invalid",
+    "invalid"};
 
 const constexpr char *waypoint_type_names[] = {"invalid", "arrive", "depart"};
 
@@ -70,12 +62,12 @@ inline bool isValidModifier(const guidance::StepManeuver maneuver)
     return true;
 }
 
-std::string instructionTypeToString(const TurnType type)
+std::string instructionTypeToString(const TurnType::Enum type)
 {
     return turn_type_names[static_cast<std::size_t>(type)];
 }
 
-std::string instructionModifierToString(const DirectionModifier modifier)
+std::string instructionModifierToString(const DirectionModifier::Enum modifier)
 {
     return modifier_names[static_cast<std::size_t>(modifier)];
 }
@@ -148,9 +140,7 @@ util::json::Object makeStepManeuver(const guidance::StepManeuver &maneuver)
 {
     util::json::Object step_maneuver;
     if (maneuver.waypoint_type == guidance::WaypointType::None)
-    {
         step_maneuver.values["type"] = detail::instructionTypeToString(maneuver.instruction.type);
-    }
     else
         step_maneuver.values["type"] = detail::waypointTypeToString(maneuver.waypoint_type);
 
@@ -164,26 +154,67 @@ util::json::Object makeStepManeuver(const guidance::StepManeuver &maneuver)
     if (maneuver.exit != 0)
         step_maneuver.values["exit"] = maneuver.exit;
 
-    // TODO currently we need this to comply with the api.
-    // We should move this to an additional entry, the moment we
-    // actually compute the correct locations of the intersections
-    if (!maneuver.intersections.empty() && maneuver.exit == 0)
-        step_maneuver.values["exit"] = maneuver.intersections.size();
     return step_maneuver;
 }
 
+util::json::Object makeIntersection(const guidance::Intersection &intersection)
+{
+    util::json::Object result;
+    util::json::Array bearings;
+    util::json::Array entry;
+
+    bearings.values.reserve(intersection.bearings.size());
+    std::copy(intersection.bearings.begin(),
+              intersection.bearings.end(),
+              std::back_inserter(bearings.values));
+
+    entry.values.reserve(intersection.entry.size());
+    std::transform(intersection.entry.begin(),
+                   intersection.entry.end(),
+                   std::back_inserter(entry.values),
+                   [](const bool has_entry) -> util::json::Value {
+                       if (has_entry)
+                           return util::json::True();
+                       else
+                           return util::json::False();
+                   });
+
+    result.values["location"] = detail::coordinateToLonLat(intersection.location);
+    result.values["bearings"] = bearings;
+    result.values["entry"] = entry;
+    if (intersection.in != guidance::Intersection::NO_INDEX)
+        result.values["in"] = intersection.in;
+    if (intersection.out != guidance::Intersection::NO_INDEX)
+        result.values["out"] = intersection.out;
+
+    return result;
+}
+
 util::json::Object makeRouteStep(guidance::RouteStep step, util::json::Value geometry)
 {
     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["name"] = std::move(step.name);
+    if (!step.pronunciation.empty())
+        route_step.values["pronunciation"] = std::move(step.pronunciation);
+    if (!step.destinations.empty())
+        route_step.values["destinations"] = std::move(step.destinations);
     if (!step.rotary_name.empty())
         route_step.values["rotary_name"] = std::move(step.rotary_name);
 
     route_step.values["mode"] = detail::modeToString(std::move(step.mode));
     route_step.values["maneuver"] = makeStepManeuver(std::move(step.maneuver));
     route_step.values["geometry"] = std::move(geometry);
+
+    util::json::Array intersections;
+    intersections.values.reserve(step.intersections.size());
+    std::transform(step.intersections.begin(),
+                   step.intersections.end(),
+                   std::back_inserter(intersections.values),
+                   makeIntersection);
+    route_step.values["intersections"] = std::move(intersections);
+
     return route_step;
 }
 
@@ -221,8 +252,17 @@ util::json::Object makeRouteLeg(guidance::RouteLeg leg, util::json::Array steps)
     return route_leg;
 }
 
+util::json::Object
+makeRouteLeg(guidance::RouteLeg leg, util::json::Array steps, util::json::Object annotation)
+{
+    util::json::Object route_leg = makeRouteLeg(std::move(leg), std::move(steps));
+    route_leg.values["annotation"] = std::move(annotation);
+    return route_leg;
+}
+
 util::json::Array makeRouteLegs(std::vector<guidance::RouteLeg> legs,
-                                std::vector<util::json::Value> step_geometries)
+                                std::vector<util::json::Value> step_geometries,
+                                std::vector<util::json::Object> annotations)
 {
     util::json::Array json_legs;
     auto step_geometry_iter = step_geometries.begin();
@@ -231,12 +271,21 @@ util::json::Array makeRouteLegs(std::vector<guidance::RouteLeg> legs,
         auto leg = std::move(legs[idx]);
         util::json::Array json_steps;
         json_steps.values.reserve(leg.steps.size());
-        std::transform(
-            std::make_move_iterator(leg.steps.begin()), std::make_move_iterator(leg.steps.end()),
-            std::back_inserter(json_steps.values), [&step_geometry_iter](guidance::RouteStep step) {
-                return makeRouteStep(std::move(step), std::move(*step_geometry_iter++));
-            });
-        json_legs.values.push_back(makeRouteLeg(std::move(leg), std::move(json_steps)));
+        std::transform(std::make_move_iterator(leg.steps.begin()),
+                       std::make_move_iterator(leg.steps.end()),
+                       std::back_inserter(json_steps.values),
+                       [&step_geometry_iter](guidance::RouteStep step) {
+                           return makeRouteStep(std::move(step), std::move(*step_geometry_iter++));
+                       });
+        if (annotations.size() > 0)
+        {
+            json_legs.values.push_back(
+                makeRouteLeg(std::move(leg), std::move(json_steps), annotations[idx]));
+        }
+        else
+        {
+            json_legs.values.push_back(makeRouteLeg(std::move(leg), std::move(json_steps)));
+        }
     }
     return json_legs;
 }
diff --git a/src/engine/douglas_peucker.cpp b/src/engine/douglas_peucker.cpp
index 7237fef..9a7bd50 100644
--- a/src/engine/douglas_peucker.cpp
+++ b/src/engine/douglas_peucker.cpp
@@ -6,8 +6,8 @@
 
 #include <boost/assert.hpp>
 
-#include <cmath>
 #include <algorithm>
+#include <cmath>
 #include <iterator>
 #include <stack>
 #include <utility>
@@ -24,8 +24,8 @@ std::uint64_t fastPerpendicularDistance(const util::FloatCoordinate &projected_s
 {
     util::FloatCoordinate projected_point_on_segment;
     std::tie(std::ignore, projected_point_on_segment) =
-        util::coordinate_calculation::projectPointOnSegment(projected_start, projected_target,
-                                                            projected);
+        util::coordinate_calculation::projectPointOnSegment(
+            projected_start, projected_target, projected);
     auto squared_distance = util::coordinate_calculation::squaredEuclideanDistance(
         projected, projected_point_on_segment);
     return squared_distance;
@@ -45,10 +45,9 @@ std::vector<util::Coordinate> douglasPeucker(std::vector<util::Coordinate>::cons
     }
 
     std::vector<util::FloatCoordinate> projected_coordinates(size);
-    std::transform(begin, end, projected_coordinates.begin(), [](const util::Coordinate coord)
-                   {
-                       return util::web_mercator::fromWGS84(coord);
-                   });
+    std::transform(begin, end, projected_coordinates.begin(), [](const util::Coordinate coord) {
+        return util::web_mercator::fromWGS84(coord);
+    });
 
     std::vector<bool> is_necessary(size, false);
     BOOST_ASSERT(is_necessary.size() >= 2);
diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp
index 9207c44..22eb150 100644
--- a/src/engine/engine.cpp
+++ b/src/engine/engine.cpp
@@ -1,14 +1,14 @@
 #include "engine/engine.hpp"
-#include "engine/engine_config.hpp"
 #include "engine/api/route_parameters.hpp"
+#include "engine/engine_config.hpp"
 #include "engine/status.hpp"
 
-#include "engine/plugins/table.hpp"
+#include "engine/plugins/match.hpp"
 #include "engine/plugins/nearest.hpp"
+#include "engine/plugins/table.hpp"
+#include "engine/plugins/tile.hpp"
 #include "engine/plugins/trip.hpp"
 #include "engine/plugins/viaroute.hpp"
-#include "engine/plugins/tile.hpp"
-#include "engine/plugins/match.hpp"
 
 #include "engine/datafacade/datafacade_base.hpp"
 #include "engine/datafacade/internal_datafacade.hpp"
@@ -137,7 +137,8 @@ Engine::Engine(EngineConfig &config)
         {
             throw util::exception("Invalid file paths given!");
         }
-        query_data_facade = util::make_unique<datafacade::InternalDataFacade>(config.storage_config);
+        query_data_facade =
+            util::make_unique<datafacade::InternalDataFacade>(config.storage_config);
     }
 
     // Register plugins
diff --git a/src/engine/guidance/assemble_overview.cpp b/src/engine/guidance/assemble_overview.cpp
index 6a59249..a53ff4c 100644
--- a/src/engine/guidance/assemble_overview.cpp
+++ b/src/engine/guidance/assemble_overview.cpp
@@ -1,16 +1,16 @@
 #ifndef ENGINE_GUIDANCE_ASSEMBLE_OVERVIEW_HPP
 #define ENGINE_GUIDANCE_ASSEMBLE_OVERVIEW_HPP
 
-#include "engine/guidance/leg_geometry.hpp"
 #include "engine/douglas_peucker.hpp"
+#include "engine/guidance/leg_geometry.hpp"
 #include "util/viewport.hpp"
 
-#include <vector>
-#include <tuple>
-#include <numeric>
-#include <utility>
 #include <iterator>
 #include <limits>
+#include <numeric>
+#include <tuple>
+#include <utility>
+#include <vector>
 
 namespace osrm
 {
@@ -23,8 +23,10 @@ namespace
 
 unsigned calculateOverviewZoomLevel(const std::vector<LegGeometry> &leg_geometries)
 {
-    util::Coordinate south_west{util::FixedLongitude{std::numeric_limits<int>::max()}, util::FixedLatitude{std::numeric_limits<int>::max()}};
-    util::Coordinate north_east{util::FixedLongitude{std::numeric_limits<int>::min()}, util::FixedLatitude{std::numeric_limits<int>::min()}};
+    util::Coordinate south_west{util::FixedLongitude{std::numeric_limits<int>::max()},
+                                util::FixedLatitude{std::numeric_limits<int>::max()}};
+    util::Coordinate north_east{util::FixedLongitude{std::numeric_limits<int>::min()},
+                                util::FixedLatitude{std::numeric_limits<int>::min()}};
 
     for (const auto &leg_geometry : leg_geometries)
     {
@@ -46,7 +48,7 @@ std::vector<util::Coordinate> simplifyGeometry(const std::vector<LegGeometry> &l
 {
     std::vector<util::Coordinate> overview_geometry;
     auto leg_index = 0UL;
-    for (const auto& geometry : leg_geometries)
+    for (const auto &geometry : leg_geometries)
     {
         auto simplified_geometry =
             douglasPeucker(geometry.locations.begin(), geometry.locations.end(), zoom_level);
@@ -55,8 +57,8 @@ std::vector<util::Coordinate> simplifyGeometry(const std::vector<LegGeometry> &l
         {
             simplified_geometry.pop_back();
         }
-        overview_geometry.insert(overview_geometry.end(), simplified_geometry.begin(),
-                                 simplified_geometry.end());
+        overview_geometry.insert(
+            overview_geometry.end(), simplified_geometry.begin(), simplified_geometry.end());
     }
     return overview_geometry;
 }
@@ -72,17 +74,19 @@ std::vector<util::Coordinate> assembleOverview(const std::vector<LegGeometry> &l
     }
     BOOST_ASSERT(!use_simplification);
 
-    auto overview_size = std::accumulate(leg_geometries.begin(), leg_geometries.end(), 0,
-                                         [](const std::size_t sum, const LegGeometry &leg_geometry)
-                                         {
-                                             return sum + leg_geometry.locations.size();
-                                         }) -
-                         leg_geometries.size() + 1;
+    auto overview_size =
+        std::accumulate(leg_geometries.begin(),
+                        leg_geometries.end(),
+                        0,
+                        [](const std::size_t sum, const LegGeometry &leg_geometry) {
+                            return sum + leg_geometry.locations.size();
+                        }) -
+        leg_geometries.size() + 1;
     std::vector<util::Coordinate> overview_geometry;
     overview_geometry.reserve(overview_size);
 
     auto leg_index = 0UL;
-    for (const auto& geometry : leg_geometries)
+    for (const auto &geometry : leg_geometries)
     {
         auto begin = geometry.locations.begin();
         auto end = geometry.locations.end();
diff --git a/src/engine/guidance/assemble_route.cpp b/src/engine/guidance/assemble_route.cpp
index cdc3a98..c30d821 100644
--- a/src/engine/guidance/assemble_route.cpp
+++ b/src/engine/guidance/assemble_route.cpp
@@ -11,16 +11,14 @@ namespace guidance
 
 Route assembleRoute(const std::vector<RouteLeg> &route_legs)
 {
-    auto distance = std::accumulate(route_legs.begin(), route_legs.end(), 0.,
-                                    [](const double sum, const RouteLeg &leg)
-                                    {
-                                        return sum + leg.distance;
-                                    });
-    auto duration = std::accumulate(route_legs.begin(), route_legs.end(), 0.,
-                                    [](const double sum, const RouteLeg &leg)
-                                    {
-                                        return sum + leg.duration;
-                                    });
+    auto distance = std::accumulate(
+        route_legs.begin(), route_legs.end(), 0., [](const double sum, const RouteLeg &leg) {
+            return sum + leg.distance;
+        });
+    auto duration = std::accumulate(
+        route_legs.begin(), route_legs.end(), 0., [](const double sum, const RouteLeg &leg) {
+            return sum + leg.duration;
+        });
 
     return Route{duration, distance};
 }
diff --git a/src/engine/guidance/assemble_steps.cpp b/src/engine/guidance/assemble_steps.cpp
index 207a6d2..dc75fc9 100644
--- a/src/engine/guidance/assemble_steps.cpp
+++ b/src/engine/guidance/assemble_steps.cpp
@@ -2,6 +2,7 @@
 
 #include <boost/assert.hpp>
 
+#include <cmath>
 #include <cstddef>
 
 namespace osrm
@@ -12,45 +13,8 @@ namespace guidance
 {
 namespace detail
 {
-
-StepManeuver stepManeuverFromGeometry(extractor::guidance::TurnInstruction instruction,
-                                      const WaypointType waypoint_type,
-                                      const LegGeometry &leg_geometry)
-{
-    BOOST_ASSERT(waypoint_type != WaypointType::None);
-    BOOST_ASSERT(leg_geometry.locations.size() >= 2);
-
-    double pre_turn_bearing = 0, post_turn_bearing = 0;
-    Coordinate turn_coordinate;
-    if (waypoint_type == WaypointType::Depart)
-    {
-        turn_coordinate = leg_geometry.locations.front();
-        const auto post_turn_coordinate = *(leg_geometry.locations.begin() + 1);
-        post_turn_bearing =
-            util::coordinate_calculation::bearing(turn_coordinate, post_turn_coordinate);
-    }
-    else
-    {
-        BOOST_ASSERT(waypoint_type == WaypointType::Arrive);
-        turn_coordinate = leg_geometry.locations.back();
-        const auto pre_turn_coordinate = *(leg_geometry.locations.end() - 2);
-        pre_turn_bearing =
-            util::coordinate_calculation::bearing(pre_turn_coordinate, turn_coordinate);
-    }
-    return StepManeuver{
-        std::move(turn_coordinate),
-        pre_turn_bearing,
-        post_turn_bearing,
-        std::move(instruction),
-        waypoint_type,
-        INVALID_EXIT_NR,
-        {} // no intermediate intersections
-    };
-}
-
-StepManeuver stepManeuverFromGeometry(extractor::guidance::TurnInstruction instruction,
-                                      const LegGeometry &leg_geometry,
-                                      const std::size_t segment_index)
+std::pair<short, short> getIntermediateBearings(const LegGeometry &leg_geometry,
+                                                const std::size_t segment_index)
 {
     auto turn_index = leg_geometry.BackIndex(segment_index);
     BOOST_ASSERT(turn_index > 0);
@@ -61,20 +25,28 @@ StepManeuver stepManeuverFromGeometry(extractor::guidance::TurnInstruction instr
     const auto turn_coordinate = leg_geometry.locations[turn_index];
     const auto post_turn_coordinate = leg_geometry.locations[turn_index + 1];
 
-    const double pre_turn_bearing =
-        util::coordinate_calculation::bearing(pre_turn_coordinate, turn_coordinate);
-    const double post_turn_bearing =
-        util::coordinate_calculation::bearing(turn_coordinate, post_turn_coordinate);
+    return std::make_pair<short, short>(
+        std::round(util::coordinate_calculation::bearing(pre_turn_coordinate, turn_coordinate)),
+        std::round(util::coordinate_calculation::bearing(turn_coordinate, post_turn_coordinate)));
+}
 
-    return StepManeuver{
-        std::move(turn_coordinate),
-        pre_turn_bearing,
-        post_turn_bearing,
-        std::move(instruction),
-        WaypointType::None,
-        INVALID_EXIT_NR,
-        {} // no intermediate intersections
-    };
+std::pair<short, short> getDepartBearings(const LegGeometry &leg_geometry)
+{
+    BOOST_ASSERT(leg_geometry.locations.size() >= 2);
+    const auto turn_coordinate = leg_geometry.locations.front();
+    const auto post_turn_coordinate = *(leg_geometry.locations.begin() + 1);
+    return std::make_pair<short, short>(
+        0,
+        std::round(util::coordinate_calculation::bearing(turn_coordinate, post_turn_coordinate)));
+}
+
+std::pair<short, short> getArriveBearings(const LegGeometry &leg_geometry)
+{
+    BOOST_ASSERT(leg_geometry.locations.size() >= 2);
+    const auto turn_coordinate = leg_geometry.locations.back();
+    const auto pre_turn_coordinate = *(leg_geometry.locations.end() - 2);
+    return std::make_pair<short, short>(
+        std::round(util::coordinate_calculation::bearing(pre_turn_coordinate, turn_coordinate)), 0);
 }
 } // ns detail
 } // ns engine
diff --git a/src/engine/guidance/post_processing.cpp b/src/engine/guidance/post_processing.cpp
index e52ac37..04f9644 100644
--- a/src/engine/guidance/post_processing.cpp
+++ b/src/engine/guidance/post_processing.cpp
@@ -1,5 +1,5 @@
-#include "engine/guidance/post_processing.hpp"
 #include "extractor/guidance/turn_instruction.hpp"
+#include "engine/guidance/post_processing.hpp"
 
 #include "engine/guidance/assemble_steps.hpp"
 #include "engine/guidance/toolkit.hpp"
@@ -17,8 +17,8 @@
 #include <utility>
 
 using TurnInstruction = osrm::extractor::guidance::TurnInstruction;
-using TurnType = osrm::extractor::guidance::TurnType;
-using DirectionModifier = osrm::extractor::guidance::DirectionModifier;
+namespace TurnType = osrm::extractor::guidance::TurnType;
+namespace DirectionModifier = osrm::extractor::guidance::DirectionModifier;
 using osrm::util::guidance::angularDeviation;
 using osrm::util::guidance::getTurnDirection;
 
@@ -31,44 +31,147 @@ namespace guidance
 
 namespace
 {
+const constexpr double MAX_COLLAPSE_DISTANCE = 25;
+
+inline bool choiceless(const RouteStep &step, const RouteStep &previous)
+{
+    // if the next turn is choiceless, we consider longer turn roads collapsable than usually
+    // accepted. We might need to improve this to find out whether we merge onto a through-street.
+    return previous.distance < 3 * MAX_COLLAPSE_DISTANCE &&
+           1 >= std::count(step.intersections.front().entry.begin(),
+                           step.intersections.front().entry.end(),
+                           true);
+}
+
+// List of types that can be collapsed, if all other restrictions pass
+bool isCollapsableInstruction(const TurnInstruction instruction)
+{
+    return instruction.type == TurnType::NewName ||
+           (instruction.type == TurnType::Suppressed &&
+            instruction.direction_modifier == DirectionModifier::Straight) ||
+           (instruction.type == TurnType::Turn &&
+            instruction.direction_modifier == DirectionModifier::Straight) ||
+           (instruction.type == TurnType::Continue &&
+            instruction.direction_modifier == DirectionModifier::Straight) ||
+           (instruction.type == TurnType::Merge);
+}
+
+// A check whether two instructions can be treated as one. This is only the case for very short
+// maneuvers that can, in some form, be seen as one. The additional in_step is to find out about
+// a possible u-turn.
+bool collapsable(const RouteStep &step)
+{
+
+    return step.distance < MAX_COLLAPSE_DISTANCE &&
+           isCollapsableInstruction(step.maneuver.instruction);
+}
+
+bool compatible(const RouteStep &lhs, const RouteStep &rhs) { return lhs.mode == rhs.mode; }
+
+double nameSegmentLength(std::size_t at, const std::vector<RouteStep> &steps)
+{
+    double result = steps[at].distance;
+    while (at + 1 < steps.size() && steps[at + 1].name_id == steps[at].name_id)
+    {
+        ++at;
+        result += steps[at].distance;
+    }
+
+    return result;
+}
 
 // invalidate a step and set its content to nothing
 void invalidateStep(RouteStep &step) { step = getInvalidRouteStep(); }
 
+void print(const RouteStep &step)
+{
+    std::cout << static_cast<int>(step.maneuver.instruction.type) << " "
+              << static_cast<int>(step.maneuver.instruction.direction_modifier) << "  "
+              << static_cast<int>(step.maneuver.waypoint_type) << " Duration: " << step.duration
+              << " Distance: " << step.distance << " Geometry: " << step.geometry_begin << " "
+              << step.geometry_end << " exit: " << step.maneuver.exit
+              << " Intersections: " << step.intersections.size() << " [";
+
+    for (const auto &intersection : step.intersections)
+    {
+        std::cout << "(bearings:";
+        for (auto bearing : intersection.bearings)
+            std::cout << " " << bearing;
+        std::cout << ", entry: ";
+        for (auto entry : intersection.entry)
+            std::cout << " " << entry;
+        std::cout << ")";
+    }
+    std::cout << "] name[" << step.name_id << "]: " << step.name;
+}
+
 void print(const std::vector<RouteStep> &steps)
 {
     std::cout << "Path\n";
     int segment = 0;
     for (const auto &step : steps)
     {
-        const auto type =
-            static_cast<std::underlying_type<TurnType>::type>(step.maneuver.instruction.type);
-        const auto modifier = static_cast<std::underlying_type<DirectionModifier>::type>(
-            step.maneuver.instruction.direction_modifier);
-
-        std::cout << "\t[" << ++segment << "]: " << type << " " << modifier
-                  << " Duration: " << step.duration << " Distance: " << step.distance
-                  << " Geometry: " << step.geometry_begin << " " << step.geometry_end
-                  << " exit: " << step.maneuver.exit
-                  << " Intersections: " << step.maneuver.intersections.size() << " [";
-
-        for (const auto &intersection : step.maneuver.intersections)
-            std::cout << "(" << intersection.duration << " " << intersection.distance << ")";
-
-        std::cout << "] name[" << step.name_id << "]: " << step.name
-                  << " Bearings: " << step.maneuver.bearing_before << " "
-                  << step.maneuver.bearing_after << std::endl;
+        std::cout << "\t[" << segment++ << "]: ";
+        print(step);
+        std::cout << std::endl;
     }
 }
 
+// Compute the angle between two bearings on a normal turn circle
+//
+//      Bearings                      Angles
+//
+//         0                           180
+//   315         45               225       135
+//
+// 270     x       90           270     x      90
+//
+//   225        135               315        45
+//        180                           0
+//
+// A turn from north to north-east offerst bearing 0 and 45 has to be translated
+// into a turn of 135 degrees. The same holdes for 90 - 135 (east to south
+// east).
+// For north, the transformation works by angle = 540 (360 + 180) - exit_bearing
+// % 360;
+// All other cases are handled by first rotating both bearings to an
+// entry_bearing of 0.
+double turn_angle(const double entry_bearing, const double exit_bearing)
+{
+    const double offset = 360 - entry_bearing;
+    const double rotated_exit = [](double bearing, const double offset) {
+        bearing += offset;
+        return bearing > 360 ? bearing - 360 : bearing;
+    }(exit_bearing, offset);
+
+    const auto angle = 540 - rotated_exit;
+    return angle > 360 ? angle - 360 : angle;
+}
+
 RouteStep forwardInto(RouteStep destination, const RouteStep &source)
 {
     // Merge a turn into a silent turn
     // Overwrites turn instruction and increases exit NR
     destination.duration += source.duration;
     destination.distance += source.distance;
+
+    if (destination.geometry_begin < source.geometry_begin)
+    {
+        destination.intersections.insert(destination.intersections.end(),
+                                         source.intersections.begin(),
+                                         source.intersections.end());
+    }
+    else
+    {
+        destination.intersections.insert(destination.intersections.begin(),
+                                         source.intersections.begin(),
+                                         source.intersections.end());
+    }
+
     destination.geometry_begin = std::min(destination.geometry_begin, source.geometry_begin);
     destination.geometry_end = std::max(destination.geometry_end, source.geometry_end);
+    destination.maneuver.exit = destination.intersections.size() - 1;
+
     return destination;
 }
 
@@ -167,7 +270,7 @@ void closeOffRoundabout(const bool on_roundabout,
         steps[1] = forwardInto(steps[1], steps[0]);
         steps[0].duration = 0;
         steps[0].distance = 0;
-        const auto exitToEnter = [](const TurnType type) {
+        const auto exitToEnter = [](const TurnType::Enum type) {
             if (TurnType::ExitRotary == type)
                 return TurnType::EnterRotary;
             // if we do not enter the roundabout Intersection, we cannot treat the full traversal as
@@ -185,7 +288,12 @@ void closeOffRoundabout(const bool on_roundabout,
     // Normal exit from the roundabout, or exit from a previously fixed roundabout. Propagate the
     // index back to the entering location and prepare the current silent set of instructions for
     // removal.
-    const auto exit_bearing = steps[step_index].maneuver.bearing_after;
+    std::vector<std::size_t> intermediate_steps;
+    BOOST_ASSERT(!steps[step_index].intersections.empty());
+    // the very first intersection in the steps represents the location of the turn. Following
+    // intersections are locations passed along the way
+    const auto exit_intersection = steps[step_index].intersections.front();
+    const auto exit_bearing = exit_intersection.bearings[exit_intersection.out];
     if (step_index > 1)
     {
         // The very first route-step is head, so we cannot iterate past that one
@@ -198,6 +306,8 @@ void closeOffRoundabout(const bool on_roundabout,
             {
                 propagation_step.maneuver.exit = step.maneuver.exit;
                 propagation_step.geometry_end = step.geometry_end;
+                const auto entry_intersection = propagation_step.intersections.front();
+
                 // remember rotary name
                 if (propagation_step.maneuver.instruction.type == TurnType::EnterRotary ||
                     propagation_step.maneuver.instruction.type == TurnType::EnterRotaryAtExit)
@@ -209,35 +319,11 @@ void closeOffRoundabout(const bool on_roundabout,
                          propagation_step.maneuver.instruction.type ==
                              TurnType::EnterRoundaboutIntersectionAtExit)
                 {
-                    // Compute the angle between two bearings on a normal turn circle
-                    //
-                    //      Bearings                      Angles
-                    //
-                    //         0                           180
-                    //   315         45               225       135
-                    //
-                    // 270     x       90           270     x      90
-                    //
-                    //   225        135               315        45
-                    //        180                           0
-                    //
-                    // A turn from north to north-east offerst bearing 0 and 45 has to be translated
-                    // into a turn of 135 degrees. The same holdes for 90 - 135 (east to south
-                    // east).
-                    // For north, the transformation works by angle = 540 (360 + 180) - exit_bearing
-                    // % 360;
-                    // All other cases are handled by first rotating both bearings to an
-                    // entry_bearing of 0.
-                    const double angle = [](const double entry_bearing, const double exit_bearing) {
-                        const double offset = 360 - entry_bearing;
-                        const double rotated_exit = [](double bearing, const double offset) {
-                            bearing += offset;
-                            return bearing > 360 ? bearing - 360 : bearing;
-                        }(exit_bearing, offset);
-
-                        const auto angle = 540 - rotated_exit;
-                        return angle > 360 ? angle - 360 : angle;
-                    }(propagation_step.maneuver.bearing_before, exit_bearing);
+                    BOOST_ASSERT(!propagation_step.intersections.empty());
+                    const double angle =
+                        turn_angle(util::bearing::reverseBearing(
+                                       entry_intersection.bearings[entry_intersection.in]),
+                                   exit_bearing);
 
                     propagation_step.maneuver.instruction.direction_modifier =
                         ::osrm::util::guidance::getTurnDirection(angle);
@@ -268,16 +354,17 @@ RouteStep elongate(RouteStep step, const RouteStep &by_step)
     step.duration += by_step.duration;
     step.distance += by_step.distance;
 
+    // by_step comes after step -> we append at the end
     if (step.geometry_end == by_step.geometry_begin + 1)
     {
         step.geometry_end = by_step.geometry_end;
 
         // if we elongate in the back, we only need to copy the intersections to the beginning.
         // the bearings remain the same, as the location of the turn doesn't change
-        step.maneuver.intersections.insert(step.maneuver.intersections.end(),
-                                           by_step.maneuver.intersections.begin(),
-                                           by_step.maneuver.intersections.end());
+        step.intersections.insert(
+            step.intersections.end(), by_step.intersections.begin(), by_step.intersections.end());
     }
+    // by_step comes before step -> we append at the front
     else
     {
         BOOST_ASSERT(step.maneuver.waypoint_type == WaypointType::None &&
@@ -286,27 +373,14 @@ RouteStep elongate(RouteStep step, const RouteStep &by_step)
         step.geometry_begin = by_step.geometry_begin;
 
         // elongating in the front changes the location of the maneuver
-        step.maneuver.location = by_step.maneuver.location;
-        step.maneuver.bearing_before = by_step.maneuver.bearing_before;
-        step.maneuver.bearing_after = by_step.maneuver.bearing_after;
-        step.maneuver.instruction = by_step.maneuver.instruction;
-
-        step.maneuver.intersections.insert(step.maneuver.intersections.begin(),
-                                           by_step.maneuver.intersections.begin(),
-                                           by_step.maneuver.intersections.end());
+        step.maneuver = by_step.maneuver;
+
+        step.intersections.insert(
+            step.intersections.begin(), by_step.intersections.begin(), by_step.intersections.end());
     }
     return step;
 }
 
-// A check whether two instructions can be treated as one. This is only the case for very short
-// maneuvers that can, in some form, be seen as one. The additional in_step is to find out about
-// a possible u-turn.
-bool collapsable(const RouteStep &step)
-{
-    const constexpr double MAX_COLLAPSE_DISTANCE = 25;
-    return step.distance < MAX_COLLAPSE_DISTANCE;
-}
-
 void collapseTurnAt(std::vector<RouteStep> &steps,
                     const std::size_t two_back_index,
                     const std::size_t one_back_index,
@@ -323,38 +397,82 @@ void collapseTurnAt(std::vector<RouteStep> &steps,
         return angularDeviation(bearing_in, bearing_out) > 170;
     };
 
+    BOOST_ASSERT(!one_back_step.intersections.empty() && !current_step.intersections.empty());
     // Very Short New Name
-    if (TurnType::NewName == one_back_step.maneuver.instruction.type)
+    if (((collapsable(one_back_step) ||
+          (isCollapsableInstruction(one_back_step.maneuver.instruction) &&
+           choiceless(current_step, one_back_step))) &&
+         !(one_back_step.maneuver.instruction.type == TurnType::Merge)))
+    // the check against merge is a workaround for motorways
     {
         BOOST_ASSERT(two_back_index < steps.size());
-        if (one_back_step.mode == steps[two_back_index].mode)
+        if (compatible(one_back_step, steps[two_back_index]))
         {
+            BOOST_ASSERT(!one_back_step.intersections.empty());
+            if (TurnType::Continue == current_step.maneuver.instruction.type ||
+                TurnType::Suppressed == current_step.maneuver.instruction.type)
+                steps[step_index].maneuver.instruction.type = TurnType::Turn;
+            else if (TurnType::Merge == current_step.maneuver.instruction.type)
+            {
+                steps[step_index].maneuver.instruction.direction_modifier =
+                    util::guidance::mirrorDirectionModifier(
+                        steps[step_index].maneuver.instruction.direction_modifier);
+                steps[step_index].maneuver.instruction.type = TurnType::Turn;
+            }
+            else if (TurnType::NewName == current_step.maneuver.instruction.type &&
+                     current_step.maneuver.instruction.direction_modifier !=
+                         DirectionModifier::Straight &&
+                     one_back_step.intersections.front().bearings.size() > 2)
+                steps[step_index].maneuver.instruction.type = TurnType::Turn;
+
             steps[two_back_index] = elongate(std::move(steps[two_back_index]), one_back_step);
             // If the previous instruction asked to continue, the name change will have to
             // be changed into a turn
             invalidateStep(steps[one_back_index]);
-
-            if (TurnType::Continue == current_step.maneuver.instruction.type)
-                steps[step_index].maneuver.instruction.type = TurnType::Turn;
         }
     }
     // very short segment after turn
-    else if (TurnType::NewName == current_step.maneuver.instruction.type)
+    else if (one_back_step.distance <= MAX_COLLAPSE_DISTANCE &&
+             isCollapsableInstruction(current_step.maneuver.instruction))
     {
-        if (one_back_step.mode == current_step.mode)
+        if (compatible(one_back_step, current_step))
         {
-            steps[step_index] = elongate(std::move(steps[step_index]), steps[one_back_index]);
-            invalidateStep(steps[one_back_index]);
-
-            if (TurnType::Continue == current_step.maneuver.instruction.type)
+            steps[one_back_index] = elongate(std::move(steps[one_back_index]), steps[step_index]);
+            if ((TurnType::Continue == one_back_step.maneuver.instruction.type ||
+                 TurnType::Suppressed == one_back_step.maneuver.instruction.type) &&
+                current_step.name_id != steps[two_back_index].name_id)
             {
-                steps[step_index].maneuver.instruction.type = TurnType::Turn;
+                steps[one_back_index].maneuver.instruction.type = TurnType::Turn;
             }
+            else if (TurnType::Turn == one_back_step.maneuver.instruction.type &&
+                     current_step.name_id == steps[two_back_index].name_id)
+            {
+                steps[one_back_index].maneuver.instruction.type = TurnType::Continue;
+            }
+            else if (TurnType::Merge == one_back_step.maneuver.instruction.type &&
+                     current_step.maneuver.instruction.type !=
+                         TurnType::Suppressed) // This suppressed is a check for highways. We might
+                                               // need a highway-suppressed to get the turn onto a
+                                               // highway...
+            {
+                steps[one_back_index].maneuver.instruction.direction_modifier =
+                    util::guidance::mirrorDirectionModifier(
+                        steps[one_back_index].maneuver.instruction.direction_modifier);
+            }
+            steps[one_back_index].name = current_step.name;
+            steps[one_back_index].name_id = current_step.name_id;
+            invalidateStep(steps[step_index]);
         }
     }
     // Potential U-Turn
-    else if (bearingsAreReversed(one_back_step.maneuver.bearing_before,
-                                 current_step.maneuver.bearing_after))
+    else if ((one_back_step.distance <= MAX_COLLAPSE_DISTANCE ||
+              choiceless(current_step, one_back_step)) &&
+             bearingsAreReversed(util::bearing::reverseBearing(
+                                     one_back_step.intersections.front()
+                                         .bearings[one_back_step.intersections.front().in]),
+                                 current_step.intersections.front()
+                                     .bearings[current_step.intersections.front().out]) &&
+             compatible(one_back_step, current_step))
 
     {
         BOOST_ASSERT(two_back_index < steps.size());
@@ -365,10 +483,9 @@ void collapseTurnAt(std::vector<RouteStep> &steps,
         // additionall collapse a name-change as well
         const bool continues_with_name_change =
             (step_index + 1 < steps.size()) &&
-            (TurnType::NewName == steps[step_index + 1].maneuver.instruction.type);
+            isCollapsableInstruction(steps[step_index + 1].maneuver.instruction);
         const bool u_turn_with_name_change =
-            collapsable(current_step) && continues_with_name_change &&
-            steps[step_index + 1].name == steps[two_back_index].name;
+            continues_with_name_change && steps[step_index + 1].name == steps[two_back_index].name;
 
         if (direct_u_turn || u_turn_with_name_change)
         {
@@ -384,6 +501,7 @@ void collapseTurnAt(std::vector<RouteStep> &steps,
             }
 
             steps[one_back_index].name = steps[two_back_index].name;
+            steps[one_back_index].name_id = steps[two_back_index].name_id;
             steps[one_back_index].maneuver.instruction.type = TurnType::Continue;
             steps[one_back_index].maneuver.instruction.direction_modifier =
                 DirectionModifier::UTurn;
@@ -411,6 +529,16 @@ std::vector<RouteStep> removeNoTurnInstructions(std::vector<RouteStep> steps)
 
     boost::remove_erase_if(steps, not_is_valid);
 
+    BOOST_ASSERT(steps.front().intersections.size() >= 1);
+    BOOST_ASSERT(steps.front().intersections.front().bearings.size() == 1);
+    BOOST_ASSERT(steps.front().intersections.front().entry.size() == 1);
+    BOOST_ASSERT(steps.front().maneuver.waypoint_type == WaypointType::Depart);
+
+    BOOST_ASSERT(steps.back().intersections.size() == 1);
+    BOOST_ASSERT(steps.back().intersections.front().bearings.size() == 1);
+    BOOST_ASSERT(steps.back().intersections.front().entry.size() == 1);
+    BOOST_ASSERT(steps.back().maneuver.waypoint_type == WaypointType::Arrive);
+
     return steps;
 }
 
@@ -420,7 +548,6 @@ std::vector<RouteStep> removeNoTurnInstructions(std::vector<RouteStep> steps)
 // They are required for maintenance purposes. We can calculate the number
 // of exits to pass in a roundabout and the number of intersections
 // that we come across.
-
 std::vector<RouteStep> postProcess(std::vector<RouteStep> steps)
 {
     // the steps should always include the first/last step in form of a location
@@ -432,29 +559,16 @@ std::vector<RouteStep> postProcess(std::vector<RouteStep> steps)
     bool on_roundabout = false;
     bool has_entered_roundabout = false;
 
-    // adds an intersection to the initial route step
-    // It includes the length of the last step, until the intersection
-    // Also updates the length of the respective segment
-    auto addIntersection = [](RouteStep into, const RouteStep &last_step,
-                              const RouteStep &intersection) {
-        into.maneuver.intersections.push_back(
-            {last_step.duration, last_step.distance, intersection.maneuver.location});
-
-        return forwardInto(std::move(into), intersection);
-    };
-
     // count the exits forward. if enter/exit roundabout happen both, no further treatment is
     // required. We might end up with only one of them (e.g. starting within a roundabout)
     // or having a via-point in the roundabout.
     // In this case, exits are numbered from the start of the lag.
-    std::size_t last_valid_instruction = 0;
     for (std::size_t step_index = 0; step_index < steps.size(); ++step_index)
     {
         auto &step = steps[step_index];
         const auto instruction = step.maneuver.instruction;
         if (entersRoundabout(instruction))
         {
-            last_valid_instruction = step_index;
             has_entered_roundabout = setUpRoundabout(step);
 
             if (has_entered_roundabout && step_index + 1 < steps.size())
@@ -475,26 +589,13 @@ std::vector<RouteStep> postProcess(std::vector<RouteStep> steps)
                 // in case the we are not on a roundabout, the very first instruction
                 // after the depart will be transformed into a roundabout and become
                 // the first valid instruction
-                last_valid_instruction = 1;
             }
             closeOffRoundabout(has_entered_roundabout, steps, step_index);
             has_entered_roundabout = false;
             on_roundabout = false;
         }
-        else if (instruction.type == TurnType::Suppressed)
-        {
-            // count intersections. We cannot use exit, since intersections can follow directly
-            // after a roundabout
-            steps[last_valid_instruction] = addIntersection(
-                std::move(steps[last_valid_instruction]), steps[step_index - 1], step);
-            step.maneuver.instruction = TurnInstruction::NO_TURN();
-        }
-        else if (!isSilent(instruction))
-        {
-            // Remember the last non silent instruction
-            last_valid_instruction = step_index;
-        }
     }
+
     // unterminated roundabout
     // Move backwards through the instructions until the start and remove the exit number
     // A roundabout without exit translates to enter-roundabout.
@@ -503,6 +604,16 @@ std::vector<RouteStep> postProcess(std::vector<RouteStep> steps)
         fixFinalRoundabout(steps);
     }
 
+    BOOST_ASSERT(steps.front().intersections.size() >= 1);
+    BOOST_ASSERT(steps.front().intersections.front().bearings.size() == 1);
+    BOOST_ASSERT(steps.front().intersections.front().entry.size() == 1);
+    BOOST_ASSERT(steps.front().maneuver.waypoint_type == WaypointType::Depart);
+
+    BOOST_ASSERT(steps.back().intersections.size() == 1);
+    BOOST_ASSERT(steps.back().intersections.front().bearings.size() == 1);
+    BOOST_ASSERT(steps.back().intersections.front().entry.size() == 1);
+    BOOST_ASSERT(steps.back().maneuver.waypoint_type == WaypointType::Arrive);
+
     return removeNoTurnInstructions(std::move(steps));
 }
 
@@ -517,58 +628,118 @@ std::vector<RouteStep> collapseTurns(std::vector<RouteStep> steps)
         BOOST_ASSERT(index > 0);
         BOOST_ASSERT(index < steps.size());
         --index;
-        while (index > 0 && steps[index].maneuver.instruction == TurnInstruction::NO_TURN())
+        while (index > 0 && steps[index].maneuver.instruction.type == TurnType::NoTurn)
             --index;
 
         return index;
     };
 
-    // Check for an initial unwanted new-name
-    {
-        const auto &current_step = steps[1];
-        if (TurnType::NewName == current_step.maneuver.instruction.type &&
-            current_step.name == steps[0].name)
+    const auto getPreviousNameIndex = [&steps](std::size_t index) {
+        BOOST_ASSERT(index > 0);
+        BOOST_ASSERT(index < steps.size());
+        --index; // make sure to skip the current name
+        while (index > 0 && steps[index].name_id == EMPTY_NAMEID)
         {
-            steps[0] = elongate(std::move(steps[0]), steps[1]);
-            invalidateStep(steps[1]);
+            --index;
         }
-    }
+        return index;
+    };
+
+    // a series of turns is only possible to collapse if its only name changes and suppressed turns.
+    const auto canCollapseAll = [&steps](std::size_t index, const std::size_t end_index) {
+        BOOST_ASSERT(end_index <= steps.size());
+        for (; index < end_index; ++index)
+        {
+            if (steps[index].maneuver.instruction.type != TurnType::Suppressed &&
+                steps[index].maneuver.instruction.type != TurnType::NewName)
+                return false;
+        }
+        return true;
+    };
 
     // first and last instructions are waypoints that cannot be collapsed
-    for (std::size_t step_index = 2; step_index < steps.size(); ++step_index)
+    for (std::size_t step_index = 1; step_index + 1 < steps.size(); ++step_index)
     {
         const auto &current_step = steps[step_index];
+        if( current_step.maneuver.instruction.type == TurnType::NoTurn )
+            continue;
         const auto one_back_index = getPreviousIndex(step_index);
         BOOST_ASSERT(one_back_index < steps.size());
 
-        // cannot collapse the depart instruction
-        if (one_back_index == 0 || current_step.maneuver.instruction == TurnInstruction::NO_TURN())
-            continue;
-
         const auto &one_back_step = steps[one_back_index];
-        const auto two_back_index = getPreviousIndex(one_back_index);
-        BOOST_ASSERT(two_back_index < steps.size());
+        // how long has a name change to be so that we announce it, even as a bridge?
+        const constexpr auto name_segment_cutoff_length = 100;
+        const auto isBasicNameChange = [](const RouteStep &step) {
+            return step.intersections.size() == 1 &&
+                   step.intersections.front().bearings.size() == 2 &&
+                   DirectionModifier::Straight == step.maneuver.instruction.direction_modifier;
+        };
 
+        // Handle sliproads from motorways in urban areas, save from modifying depart, since
+        // TurnType::Sliproad != TurnType::NoTurn
+        if (one_back_step.maneuver.instruction.type == TurnType::Sliproad)
+        {
+            // Handle possible u-turns between highways that look like slip-roads
+            if (steps[getPreviousIndex(one_back_index)].name_id == steps[step_index].name_id &&
+                steps[step_index].name_id != EMPTY_NAMEID)
+            {
+                steps[one_back_index].maneuver.instruction.type = TurnType::Continue;
+            }
+            else
+            {
+                steps[one_back_index].maneuver.instruction.type = TurnType::Turn;
+            }
+            if (compatible(one_back_step, current_step))
+            {
+                steps[one_back_index] =
+                    elongate(std::move(steps[one_back_index]), steps[step_index]);
+                steps[one_back_index].name_id = steps[step_index].name_id;
+                steps[one_back_index].name = steps[step_index].name;
+
+                const auto exit_intersection = steps[step_index].intersections.front();
+                const auto exit_bearing = exit_intersection.bearings[exit_intersection.out];
+
+                const auto entry_intersection = steps[one_back_index].intersections.front();
+                const auto entry_bearing = entry_intersection.bearings[entry_intersection.in];
+
+                const double angle =
+                    turn_angle(util::bearing::reverseBearing(entry_bearing), exit_bearing);
+                steps[one_back_index].maneuver.instruction.direction_modifier =
+                    ::osrm::util::guidance::getTurnDirection(angle);
+                invalidateStep(steps[step_index]);
+            }
+        }
         // Due to empty segments, we can get name-changes from A->A
         // These have to be handled in post-processing
-        if (TurnType::NewName == current_step.maneuver.instruction.type &&
-            current_step.name == steps[one_back_index].name)
+        else if (isCollapsableInstruction(current_step.maneuver.instruction) &&
+                 current_step.maneuver.instruction.type != TurnType::Suppressed &&
+                 steps[getPreviousNameIndex(step_index)].name == current_step.name &&
+                 canCollapseAll(getPreviousNameIndex(step_index) + 1, step_index + 1))
         {
-            steps[one_back_index] = elongate(std::move(steps[one_back_index]), steps[step_index]);
-            invalidateStep(steps[step_index]);
+            BOOST_ASSERT(step_index > 0);
+            const std::size_t last_available_name_index = getPreviousNameIndex(step_index);
+
+            for (std::size_t index = last_available_name_index + 1; index <= step_index; ++index)
+            {
+                steps[last_available_name_index] =
+                    elongate(std::move(steps[last_available_name_index]), steps[index]);
+                invalidateStep(steps[index]);
+            }
         }
         // If we look at two consecutive name changes, we can check for a name oszillation.
         // A name oszillation changes from name A shortly to name B and back to A.
         // In these cases, the name change will be suppressed.
-        else if (TurnType::NewName == current_step.maneuver.instruction.type &&
-                 TurnType::NewName == one_back_step.maneuver.instruction.type)
+        else if (one_back_index > 0 && compatible(current_step, one_back_step) &&
+                 isCollapsableInstruction(current_step.maneuver.instruction) &&
+                 isCollapsableInstruction(one_back_step.maneuver.instruction))
         {
-            // valid due to step_index starting at 2
+            const auto two_back_index = getPreviousIndex(one_back_index);
+            BOOST_ASSERT(two_back_index < steps.size());
+            // valid, since one_back is collapsable:
             const auto &coming_from_name = steps[two_back_index].name;
             if (current_step.name == coming_from_name)
             {
-                if (current_step.mode == one_back_step.mode &&
-                    one_back_step.mode == steps[two_back_index].mode)
+                if (compatible(one_back_step, steps[two_back_index]))
                 {
                     steps[two_back_index] =
                         elongate(elongate(std::move(steps[two_back_index]), steps[one_back_index]),
@@ -579,26 +750,68 @@ std::vector<RouteStep> collapseTurns(std::vector<RouteStep> steps)
                 // TODO discuss: we could think about changing the new-name to a pure notification
                 // about mode changes
             }
+            else if (nameSegmentLength(one_back_index, steps) < name_segment_cutoff_length &&
+                     isBasicNameChange(one_back_step) && isBasicNameChange(current_step))
+            {
+                steps[two_back_index] =
+                    elongate(std::move(steps[two_back_index]), steps[one_back_index]);
+                invalidateStep(steps[one_back_index]);
+                if (nameSegmentLength(step_index, steps) < name_segment_cutoff_length)
+                {
+                    steps[two_back_index] =
+                        elongate(std::move(steps[two_back_index]), steps[step_index]);
+                    invalidateStep(steps[step_index]);
+                }
+            }
+            else if (choiceless(current_step, one_back_step) ||
+                     one_back_step.distance <= MAX_COLLAPSE_DISTANCE)
+            {
+                // check for one of the multiple collapse scenarios and, if possible, collapse the
+                // turn
+                const auto two_back_index = getPreviousIndex(one_back_index);
+                BOOST_ASSERT(two_back_index < steps.size());
+                collapseTurnAt(steps, two_back_index, one_back_index, step_index);
+            }
         }
-        else if (collapsable(one_back_step))
+        else if (one_back_index > 0 && (one_back_step.distance <= MAX_COLLAPSE_DISTANCE ||
+                                        choiceless(current_step, one_back_step)))
         {
             // check for one of the multiple collapse scenarios and, if possible, collapse the turn
+            const auto two_back_index = getPreviousIndex(one_back_index);
+            BOOST_ASSERT(two_back_index < steps.size());
             collapseTurnAt(steps, two_back_index, one_back_index, step_index);
         }
     }
+
+    // handle final sliproad
+    if (steps.size() >= 3 &&
+        steps[steps.size() - 2].maneuver.instruction.type == TurnType::Sliproad)
+    {
+        steps[steps.size() - 2].maneuver.instruction.type = TurnType::Turn;
+    }
+
+    BOOST_ASSERT(steps.front().intersections.size() >= 1);
+    BOOST_ASSERT(steps.front().intersections.front().bearings.size() == 1);
+    BOOST_ASSERT(steps.front().intersections.front().entry.size() == 1);
+    BOOST_ASSERT(steps.front().maneuver.waypoint_type == WaypointType::Depart);
+
+    BOOST_ASSERT(steps.back().intersections.size() == 1);
+    BOOST_ASSERT(steps.back().intersections.front().bearings.size() == 1);
+    BOOST_ASSERT(steps.back().intersections.front().entry.size() == 1);
+    BOOST_ASSERT(steps.back().maneuver.waypoint_type == WaypointType::Arrive);
+
     return removeNoTurnInstructions(std::move(steps));
 }
 
+// Doing this step in post-processing provides a few challenges we cannot overcome.
+// The removal of an initial step imposes some copy overhead in the steps, moving all later
+// steps to the front. In addition, we cannot reduce the travel time that is accumulated at a
+// different location.
+// As a direct implication, we have to keep the time of the initial/final turns (which adds a
+// few seconds of inaccuracy at both ends. This is acceptable, however, since the turn should
+// usually not be as relevant.
 void trimShortSegments(std::vector<RouteStep> &steps, LegGeometry &geometry)
 {
-    // Doing this step in post-processing provides a few challenges we cannot overcome.
-    // The removal of an initial step imposes some copy overhead in the steps, moving all later
-    // steps to the front. In addition, we cannot reduce the travel time that is accumulated at a
-    // different location.
-    // As a direct implication, we have to keep the time of the initial/final turns (which adds a
-    // few seconds of inaccuracy at both ends. This is acceptable, however, since the turn should
-    // usually not be as relevant.
-
     if (steps.size() < 2 || geometry.locations.size() <= 2)
         return;
 
@@ -626,6 +839,8 @@ void trimShortSegments(std::vector<RouteStep> &steps, LegGeometry &geometry)
     {
         // fixup the coordinate
         geometry.locations.erase(geometry.locations.begin());
+        geometry.annotations.erase(geometry.annotations.begin());
+        geometry.osm_node_ids.erase(geometry.osm_node_ids.begin());
 
         // remove the initial distance value
         geometry.segment_distances.erase(geometry.segment_distances.begin());
@@ -637,7 +852,8 @@ void trimShortSegments(std::vector<RouteStep> &steps, LegGeometry &geometry)
             BOOST_ASSERT(geometry.segment_offsets[1] == 1);
             // geometry offsets have to be adjusted. Move all offsets to the front and reduce by
             // one. (This is an inplace forward one and reduce by one)
-            std::transform(geometry.segment_offsets.begin() + 1, geometry.segment_offsets.end(),
+            std::transform(geometry.segment_offsets.begin() + 1,
+                           geometry.segment_offsets.end(),
                            geometry.segment_offsets.begin(),
                            [](const std::size_t val) { return val - 1; });
 
@@ -651,22 +867,30 @@ void trimShortSegments(std::vector<RouteStep> &steps, LegGeometry &geometry)
 
             // update initial turn direction/bearings. Due to the duplicated first coordinate,
             // the initial bearing is invalid
-            designated_depart.maneuver = detail::stepManeuverFromGeometry(
-                TurnInstruction::NO_TURN(), WaypointType::Depart, geometry);
+            designated_depart.maneuver.waypoint_type = WaypointType::Depart;
+            designated_depart.maneuver.bearing_before = 0;
+            designated_depart.maneuver.instruction = TurnInstruction::NO_TURN();
+            // we need to make this conform with the intersection format for the first intersection
+            auto &first_intersection = designated_depart.intersections.front();
+            first_intersection.bearings = {first_intersection.bearings[first_intersection.out]};
+            first_intersection.entry = {true};
+            first_intersection.in = Intersection::NO_INDEX;
+            first_intersection.out = 0;
 
             // finally remove the initial (now duplicated move)
             steps.erase(steps.begin());
         }
         else
         {
+            // we need to make this at least 1 because we will substract 1
+            // from all offsets at the end of the loop.
             steps.front().geometry_begin = 1;
+
             // reduce all offsets by one (inplace)
-            std::transform(geometry.segment_offsets.begin(), geometry.segment_offsets.end(),
+            std::transform(geometry.segment_offsets.begin(),
+                           geometry.segment_offsets.end(),
                            geometry.segment_offsets.begin(),
                            [](const std::size_t val) { return val - 1; });
-
-            steps.front().maneuver = detail::stepManeuverFromGeometry(
-                TurnInstruction::NO_TURN(), WaypointType::Depart, geometry);
         }
 
         // and update the leg geometry indices for the removed entry
@@ -674,8 +898,26 @@ void trimShortSegments(std::vector<RouteStep> &steps, LegGeometry &geometry)
             --step.geometry_begin;
             --step.geometry_end;
         });
+
+        auto &first_step = steps.front();
+        // we changed the geometry, we need to recalculate the bearing
+        auto bearing = std::round(util::coordinate_calculation::bearing(
+            geometry.locations[first_step.geometry_begin],
+            geometry.locations[first_step.geometry_begin + 1]));
+        first_step.maneuver.bearing_after = bearing;
+        first_step.intersections.front().bearings.front() = bearing;
     }
 
+    BOOST_ASSERT(steps.front().intersections.size() >= 1);
+    BOOST_ASSERT(steps.front().intersections.front().bearings.size() == 1);
+    BOOST_ASSERT(steps.front().intersections.front().entry.size() == 1);
+    BOOST_ASSERT(steps.front().maneuver.waypoint_type == WaypointType::Depart);
+
+    BOOST_ASSERT(steps.back().intersections.size() == 1);
+    BOOST_ASSERT(steps.back().intersections.front().bearings.size() == 1);
+    BOOST_ASSERT(steps.back().intersections.front().entry.size() == 1);
+    BOOST_ASSERT(steps.back().maneuver.waypoint_type == WaypointType::Arrive);
+
     // make sure we still have enough segments
     if (steps.size() < 2 || geometry.locations.size() == 2)
         return;
@@ -687,12 +929,21 @@ void trimShortSegments(std::vector<RouteStep> &steps, LegGeometry &geometry)
     if (next_to_last_step.distance <= 1)
     {
         geometry.locations.pop_back();
+        geometry.annotations.pop_back();
+        geometry.osm_node_ids.pop_back();
         geometry.segment_offsets.pop_back();
         BOOST_ASSERT(geometry.segment_distances.back() < 1);
         geometry.segment_distances.pop_back();
 
-        next_to_last_step.maneuver = detail::stepManeuverFromGeometry(
-            TurnInstruction::NO_TURN(), WaypointType::Arrive, geometry);
+        next_to_last_step.maneuver.waypoint_type = WaypointType::Arrive;
+        next_to_last_step.maneuver.instruction = TurnInstruction::NO_TURN();
+        next_to_last_step.maneuver.bearing_after = 0;
+        BOOST_ASSERT(next_to_last_step.intersections.size() == 1);
+        auto &last_intersection = next_to_last_step.intersections.back();
+        last_intersection.bearings = {last_intersection.bearings[last_intersection.in]};
+        last_intersection.entry = {true};
+        last_intersection.out = Intersection::NO_INDEX;
+        last_intersection.in = 0;
         steps.pop_back();
 
         // Because we eliminated a really short segment, it was probably
@@ -713,17 +964,36 @@ void trimShortSegments(std::vector<RouteStep> &steps, LegGeometry &geometry)
         // correct steps but duplicated coordinate in the end.
         // This can happen if the last coordinate snaps to a node in the unpacked geometry
         geometry.locations.pop_back();
+        geometry.annotations.pop_back();
         geometry.segment_offsets.back()--;
         // since the last geometry includes the location of arrival, the arrival instruction
         // geometry overlaps with the previous segment
         BOOST_ASSERT(next_to_last_step.geometry_end == steps.back().geometry_begin + 1);
         BOOST_ASSERT(next_to_last_step.geometry_begin < next_to_last_step.geometry_end);
         next_to_last_step.geometry_end--;
-        steps.back().geometry_begin--;
-        steps.back().geometry_end--;
-        steps.back().maneuver = detail::stepManeuverFromGeometry(TurnInstruction::NO_TURN(),
-                                                                 WaypointType::Arrive, geometry);
+        auto &last_step = steps.back();
+        last_step.geometry_begin--;
+        last_step.geometry_end--;
+        BOOST_ASSERT(next_to_last_step.geometry_end == last_step.geometry_begin + 1);
+        BOOST_ASSERT(last_step.geometry_begin == last_step.geometry_end - 1);
+        BOOST_ASSERT(next_to_last_step.geometry_end >= 2);
+        // we changed the geometry, we need to recalculate the bearing
+        auto bearing = std::round(util::coordinate_calculation::bearing(
+            geometry.locations[next_to_last_step.geometry_end - 2],
+            geometry.locations[last_step.geometry_begin]));
+        last_step.maneuver.bearing_before = bearing;
+        last_step.intersections.front().bearings.front() = util::bearing::reverseBearing(bearing);
     }
+
+    BOOST_ASSERT(steps.front().intersections.size() >= 1);
+    BOOST_ASSERT(steps.front().intersections.front().bearings.size() == 1);
+    BOOST_ASSERT(steps.front().intersections.front().entry.size() == 1);
+    BOOST_ASSERT(steps.front().maneuver.waypoint_type == WaypointType::Depart);
+
+    BOOST_ASSERT(steps.back().intersections.size() == 1);
+    BOOST_ASSERT(steps.back().intersections.front().bearings.size() == 1);
+    BOOST_ASSERT(steps.back().intersections.front().entry.size() == 1);
+    BOOST_ASSERT(steps.back().maneuver.waypoint_type == WaypointType::Arrive);
 }
 
 // assign relative locations to depart/arrive instructions
@@ -760,6 +1030,16 @@ std::vector<RouteStep> assignRelativeLocations(std::vector<RouteStep> steps,
             : extractor::guidance::DirectionModifier::UTurn;
 
     steps.back().maneuver.instruction.direction_modifier = final_modifier;
+
+    BOOST_ASSERT(steps.front().intersections.size() >= 1);
+    BOOST_ASSERT(steps.front().intersections.front().bearings.size() == 1);
+    BOOST_ASSERT(steps.front().intersections.front().entry.size() == 1);
+    BOOST_ASSERT(steps.front().maneuver.waypoint_type == WaypointType::Depart);
+
+    BOOST_ASSERT(steps.back().intersections.size() == 1);
+    BOOST_ASSERT(steps.back().intersections.front().bearings.size() == 1);
+    BOOST_ASSERT(steps.back().intersections.front().entry.size() == 1);
+    BOOST_ASSERT(steps.back().maneuver.waypoint_type == WaypointType::Arrive);
     return steps;
 }
 
@@ -786,6 +1066,29 @@ LegGeometry resyncGeometry(LegGeometry leg_geometry, const std::vector<RouteStep
     return leg_geometry;
 }
 
+std::vector<RouteStep> buildIntersections(std::vector<RouteStep> steps)
+{
+    std::size_t last_valid_instruction = 0;
+    for (std::size_t step_index = 0; step_index < steps.size(); ++step_index)
+    {
+        auto &step = steps[step_index];
+        const auto instruction = step.maneuver.instruction;
+        if (instruction.type == TurnType::Suppressed)
+        {
+            // count intersections. We cannot use exit, since intersections can follow directly
+            // after a roundabout
+            steps[last_valid_instruction] = elongate(steps[last_valid_instruction], step);
+            step.maneuver.instruction = TurnInstruction::NO_TURN();
+        }
+        else if (!isSilent(instruction))
+        {
+            // Remember the last non silent instruction
+            last_valid_instruction = step_index;
+        }
+    }
+    return removeNoTurnInstructions(std::move(steps));
+}
+
 } // namespace guidance
 } // namespace engine
 } // namespace osrm
diff --git a/src/engine/hint.cpp b/src/engine/hint.cpp
index d031fbe..b2a1c0f 100644
--- a/src/engine/hint.cpp
+++ b/src/engine/hint.cpp
@@ -4,8 +4,8 @@
 
 #include <boost/assert.hpp>
 
-#include <iterator>
 #include <algorithm>
+#include <iterator>
 #include <ostream>
 #include <tuple>
 
diff --git a/src/engine/plugins/match.cpp b/src/engine/plugins/match.cpp
index ac53c07..41e0d79 100644
--- a/src/engine/plugins/match.cpp
+++ b/src/engine/plugins/match.cpp
@@ -1,9 +1,9 @@
-#include "engine/plugins/plugin_base.hpp"
 #include "engine/plugins/match.hpp"
+#include "engine/plugins/plugin_base.hpp"
 
-#include "engine/map_matching/bayes_classifier.hpp"
-#include "engine/api/match_parameters.hpp"
 #include "engine/api/match_api.hpp"
+#include "engine/api/match_parameters.hpp"
+#include "engine/map_matching/bayes_classifier.hpp"
 #include "util/coordinate_calculation.hpp"
 #include "util/integer_range.hpp"
 #include "util/json_logger.hpp"
@@ -34,9 +34,10 @@ void filterCandidates(const std::vector<util::Coordinate> &coordinates,
 
         if (coordinates.size() - 1 > current_coordinate && 0 < current_coordinate)
         {
-            double turn_angle = util::coordinate_calculation::computeAngle(
-                coordinates[current_coordinate - 1], coordinates[current_coordinate],
-                coordinates[current_coordinate + 1]);
+            double turn_angle =
+                util::coordinate_calculation::computeAngle(coordinates[current_coordinate - 1],
+                                                           coordinates[current_coordinate],
+                                                           coordinates[current_coordinate + 1]);
 
             // sharp turns indicate a possible uturn
             if (turn_angle <= 90.0 || turn_angle >= 270.0)
@@ -52,24 +53,29 @@ void filterCandidates(const std::vector<util::Coordinate> &coordinates,
         }
 
         // sort by forward id, then by reverse id and then by distance
-        std::sort(
-            candidates.begin(), candidates.end(),
-            [](const PhantomNodeWithDistance &lhs, const PhantomNodeWithDistance &rhs)
-            {
-                return lhs.phantom_node.forward_segment_id.id < rhs.phantom_node.forward_segment_id.id ||
-                       (lhs.phantom_node.forward_segment_id.id == rhs.phantom_node.forward_segment_id.id &&
-                        (lhs.phantom_node.reverse_segment_id.id < rhs.phantom_node.reverse_segment_id.id ||
-                         (lhs.phantom_node.reverse_segment_id.id == rhs.phantom_node.reverse_segment_id.id &&
-                          lhs.distance < rhs.distance)));
-            });
-
-        auto new_end = std::unique(
-            candidates.begin(), candidates.end(),
-            [](const PhantomNodeWithDistance &lhs, const PhantomNodeWithDistance &rhs)
-            {
-                return lhs.phantom_node.forward_segment_id.id == rhs.phantom_node.forward_segment_id.id &&
-                       lhs.phantom_node.reverse_segment_id.id == rhs.phantom_node.reverse_segment_id.id;
-            });
+        std::sort(candidates.begin(),
+                  candidates.end(),
+                  [](const PhantomNodeWithDistance &lhs, const PhantomNodeWithDistance &rhs) {
+                      return lhs.phantom_node.forward_segment_id.id <
+                                 rhs.phantom_node.forward_segment_id.id ||
+                             (lhs.phantom_node.forward_segment_id.id ==
+                                  rhs.phantom_node.forward_segment_id.id &&
+                              (lhs.phantom_node.reverse_segment_id.id <
+                                   rhs.phantom_node.reverse_segment_id.id ||
+                               (lhs.phantom_node.reverse_segment_id.id ==
+                                    rhs.phantom_node.reverse_segment_id.id &&
+                                lhs.distance < rhs.distance)));
+                  });
+
+        auto new_end =
+            std::unique(candidates.begin(),
+                        candidates.end(),
+                        [](const PhantomNodeWithDistance &lhs, const PhantomNodeWithDistance &rhs) {
+                            return lhs.phantom_node.forward_segment_id.id ==
+                                       rhs.phantom_node.forward_segment_id.id &&
+                                   lhs.phantom_node.reverse_segment_id.id ==
+                                       rhs.phantom_node.reverse_segment_id.id;
+                        });
         candidates.resize(new_end - candidates.begin());
 
         if (!allow_uturn)
@@ -92,9 +98,9 @@ void filterCandidates(const std::vector<util::Coordinate> &coordinates,
         }
 
         // sort by distance to make pruning effective
-        std::sort(candidates.begin(), candidates.end(),
-                  [](const PhantomNodeWithDistance &lhs, const PhantomNodeWithDistance &rhs)
-                  {
+        std::sort(candidates.begin(),
+                  candidates.end(),
+                  [](const PhantomNodeWithDistance &lhs, const PhantomNodeWithDistance &rhs) {
                       return lhs.distance < rhs.distance;
                   });
     }
@@ -129,9 +135,10 @@ Status MatchPlugin::HandleRequest(const api::MatchParameters &parameters,
     else
     {
         search_radiuses.resize(parameters.coordinates.size());
-        std::transform(parameters.radiuses.begin(), parameters.radiuses.end(),
-                       search_radiuses.begin(), [](const boost::optional<double> &maybe_radius)
-                       {
+        std::transform(parameters.radiuses.begin(),
+                       parameters.radiuses.end(),
+                       search_radiuses.begin(),
+                       [](const boost::optional<double> &maybe_radius) {
                            if (maybe_radius)
                            {
                                return *maybe_radius * RADIUS_MULTIPLIER;
@@ -147,9 +154,9 @@ Status MatchPlugin::HandleRequest(const api::MatchParameters &parameters,
     auto candidates_lists = GetPhantomNodesInRange(parameters, search_radiuses);
 
     filterCandidates(parameters.coordinates, candidates_lists);
-    if (std::all_of(candidates_lists.begin(), candidates_lists.end(),
-                    [](const std::vector<PhantomNodeWithDistance> &candidates)
-                    {
+    if (std::all_of(candidates_lists.begin(),
+                    candidates_lists.end(),
+                    [](const std::vector<PhantomNodeWithDistance> &candidates) {
                         return candidates.empty();
                     }))
     {
@@ -159,8 +166,8 @@ Status MatchPlugin::HandleRequest(const api::MatchParameters &parameters,
     }
 
     // call the actual map matching
-    SubMatchingList sub_matchings = map_matching(candidates_lists, parameters.coordinates,
-                                                 parameters.timestamps, parameters.radiuses);
+    SubMatchingList sub_matchings = map_matching(
+        candidates_lists, parameters.coordinates, parameters.timestamps, parameters.radiuses);
 
     if (sub_matchings.size() == 0)
     {
@@ -183,7 +190,8 @@ Status MatchPlugin::HandleRequest(const api::MatchParameters &parameters,
             BOOST_ASSERT(current_phantom_node_pair.target_phantom.IsValid());
             sub_routes[index].segment_end_coordinates.emplace_back(current_phantom_node_pair);
         }
-        // force uturns to be on, since we split the phantom nodes anyway and only have bi-directional
+        // force uturns to be on, since we split the phantom nodes anyway and only have
+        // bi-directional
         // phantom nodes for possible uturns
         shortest_path(sub_routes[index].segment_end_coordinates, {false}, sub_routes[index]);
         BOOST_ASSERT(sub_routes[index].shortest_path_length != INVALID_EDGE_WEIGHT);
diff --git a/src/engine/plugins/nearest.cpp b/src/engine/plugins/nearest.cpp
index 96b63e3..3e54d33 100644
--- a/src/engine/plugins/nearest.cpp
+++ b/src/engine/plugins/nearest.cpp
@@ -1,6 +1,6 @@
 #include "engine/plugins/nearest.hpp"
-#include "engine/api/nearest_parameters.hpp"
 #include "engine/api/nearest_api.hpp"
+#include "engine/api/nearest_parameters.hpp"
 #include "engine/phantom_node.hpp"
 #include "util/integer_range.hpp"
 
diff --git a/src/engine/plugins/table.cpp b/src/engine/plugins/table.cpp
index 27e3100..3cad405 100644
--- a/src/engine/plugins/table.cpp
+++ b/src/engine/plugins/table.cpp
@@ -1,11 +1,11 @@
 #include "engine/plugins/table.hpp"
 
-#include "engine/api/table_parameters.hpp"
 #include "engine/api/table_api.hpp"
+#include "engine/api/table_parameters.hpp"
 #include "engine/routing_algorithms/many_to_many.hpp"
 #include "engine/search_engine_data.hpp"
-#include "util/string_util.hpp"
 #include "util/json_container.hpp"
+#include "util/string_util.hpp"
 
 #include <cstdlib>
 
@@ -40,8 +40,8 @@ Status TablePlugin::HandleRequest(const api::TableParameters &params, util::json
 
     if (params.bearings.size() > 0 && params.coordinates.size() != params.bearings.size())
     {
-        return Error("InvalidOptions", "Number of bearings does not match number of coordinates",
-                     result);
+        return Error(
+            "InvalidOptions", "Number of bearings does not match number of coordinates", result);
     }
 
     // Empty sources or destinations means the user wants all of them included, respectively
diff --git a/src/engine/plugins/tile.cpp b/src/engine/plugins/tile.cpp
index 6ce85c7..4f1ce64 100644
--- a/src/engine/plugins/tile.cpp
+++ b/src/engine/plugins/tile.cpp
@@ -1,21 +1,21 @@
-#include "engine/plugins/plugin_base.hpp"
 #include "engine/plugins/tile.hpp"
+#include "engine/plugins/plugin_base.hpp"
 
 #include "util/coordinate_calculation.hpp"
-#include "util/web_mercator.hpp"
 #include "util/vector_tile.hpp"
+#include "util/web_mercator.hpp"
 
 #include <boost/geometry.hpp>
-#include <boost/geometry/geometries/point_xy.hpp>
 #include <boost/geometry/geometries/geometries.hpp>
+#include <boost/geometry/geometries/point_xy.hpp>
 #include <boost/geometry/multi/geometries/multi_linestring.hpp>
 
-#include <protozero/varint.hpp>
 #include <protozero/pbf_writer.hpp>
+#include <protozero/varint.hpp>
 
 #include <string>
-#include <vector>
 #include <utility>
+#include <vector>
 
 #include <cmath>
 #include <cstdint>
@@ -171,8 +171,8 @@ Status TilePlugin::HandleRequest(const api::TileParameters &parameters, std::str
     double min_lon, min_lat, max_lon, max_lat;
 
     // Convert the z,x,y mercator tile coordinates into WGS84 lon/lat values
-    util::web_mercator::xyzToWGS84(parameters.x, parameters.y, parameters.z, min_lon, min_lat,
-                                   max_lon, max_lat);
+    util::web_mercator::xyzToWGS84(
+        parameters.x, parameters.y, parameters.z, min_lon, min_lat, max_lon, max_lat);
 
     util::Coordinate southwest{util::FloatLongitude(min_lon), util::FloatLatitude(min_lat)};
     util::Coordinate northeast{util::FloatLongitude(max_lon), util::FloatLatitude(max_lat)};
@@ -184,6 +184,8 @@ Status TilePlugin::HandleRequest(const api::TileParameters &parameters, std::str
     std::vector<int> used_weights;
     std::unordered_map<int, std::size_t> weight_offsets;
     uint8_t max_datasource_id = 0;
+    std::vector<std::string> names;
+    std::unordered_map<std::string, std::size_t> name_offsets;
 
     // Loop over all edges once to tally up all the attributes we'll need.
     // We need to do this so that we know the attribute offsets to use
@@ -237,13 +239,21 @@ Status TilePlugin::HandleRequest(const api::TileParameters &parameters, std::str
         // data to the layer attribute values
         max_datasource_id = std::max(max_datasource_id, forward_datasource);
         max_datasource_id = std::max(max_datasource_id, reverse_datasource);
+
+        std::string name = facade.GetNameForID(edge.name_id);
+
+        if (name_offsets.find(name) == name_offsets.end())
+        {
+          names.push_back(name);
+          name_offsets[name] = names.size() - 1;
+        }
     }
 
     // TODO: extract speed values for compressed and uncompressed geometries
 
     // Convert tile coordinates into mercator coordinates
-    util::web_mercator::xyzToMercator(parameters.x, parameters.y, parameters.z, min_lon, min_lat,
-                                      max_lon, max_lat);
+    util::web_mercator::xyzToMercator(
+        parameters.x, parameters.y, parameters.z, min_lon, min_lat, max_lon, max_lat);
     const detail::BBox tile_bbox{min_lon, min_lat, max_lon, max_lat};
 
     // Protobuf serialized blocks when objects go out of scope, hence
@@ -279,6 +289,8 @@ Status TilePlugin::HandleRequest(const api::TileParameters &parameters, std::str
                 uint8_t forward_datasource = 0;
                 uint8_t reverse_datasource = 0;
 
+                std::string name = facade.GetNameForID(edge.name_id);
+
                 if (edge.forward_packed_geometry_id != SPECIAL_EDGEID)
                 {
                     std::vector<EdgeWeight> forward_weight_vector;
@@ -316,11 +328,14 @@ Status TilePlugin::HandleRequest(const api::TileParameters &parameters, std::str
                 max_datasource_id = std::max(max_datasource_id, forward_datasource);
                 max_datasource_id = std::max(max_datasource_id, reverse_datasource);
 
-                const auto encode_tile_line = [&layer_writer, &edge, &id, &max_datasource_id](
-                    const detail::FixedLine &tile_line, const std::uint32_t speed_kmh,
-                    const std::size_t duration, const std::uint8_t datasource,
-                    std::int32_t &start_x, std::int32_t &start_y)
-                {
+                const auto encode_tile_line = [&layer_writer, &edge, &id, &max_datasource_id, &used_weights](
+                    const detail::FixedLine &tile_line,
+                    const std::uint32_t speed_kmh,
+                    const std::size_t duration,
+                    const std::uint8_t datasource,
+                    const std::size_t name,
+                    std::int32_t &start_x,
+                    std::int32_t &start_y) {
                     // Here, we save the two attributes for our feature: the speed and the
                     // is_small
                     // boolean.  We onl serve up speeds from 0-139, so all we do is save the
@@ -354,6 +369,10 @@ Status TilePlugin::HandleRequest(const api::TileParameters &parameters, std::str
                         field.add_element(3);                // "duration" tag key offset
                         field.add_element(130 + max_datasource_id + 1 +
                                           duration); // duration value offset
+                        field.add_element(4); // "name" tag key offset
+
+                        field.add_element(130 + max_datasource_id + 1 +
+                                          used_weights.size() + name); // name value offset
                     }
                     {
 
@@ -377,8 +396,13 @@ Status TilePlugin::HandleRequest(const api::TileParameters &parameters, std::str
                     auto tile_line = coordinatesToTileLine(a, b, tile_bbox);
                     if (!tile_line.empty())
                     {
-                        encode_tile_line(tile_line, speed_kmh, weight_offsets[forward_weight],
-                                         forward_datasource, start_x, start_y);
+                        encode_tile_line(tile_line,
+                                         speed_kmh,
+                                         weight_offsets[forward_weight],
+                                         forward_datasource,
+                                         name_offsets[name],
+                                         start_x,
+                                         start_y);
                     }
                 }
 
@@ -396,8 +420,13 @@ Status TilePlugin::HandleRequest(const api::TileParameters &parameters, std::str
                     auto tile_line = coordinatesToTileLine(b, a, tile_bbox);
                     if (!tile_line.empty())
                     {
-                        encode_tile_line(tile_line, speed_kmh, weight_offsets[reverse_weight],
-                                         reverse_datasource, start_x, start_y);
+                        encode_tile_line(tile_line,
+                                         speed_kmh,
+                                         weight_offsets[reverse_weight],
+                                         reverse_datasource,
+                                         name_offsets[name],
+                                         start_x,
+                                         start_y);
                     }
                 }
             }
@@ -410,6 +439,7 @@ Status TilePlugin::HandleRequest(const api::TileParameters &parameters, std::str
         layer_writer.add_string(util::vector_tile::KEY_TAG, "is_small");
         layer_writer.add_string(util::vector_tile::KEY_TAG, "datasource");
         layer_writer.add_string(util::vector_tile::KEY_TAG, "duration");
+        layer_writer.add_string(util::vector_tile::KEY_TAG, "name");
 
         // Now, we write out the possible speed value arrays and possible is_tiny
         // values.  Field type 4 is the "values" field.  It's a variable type field,
@@ -448,6 +478,14 @@ Status TilePlugin::HandleRequest(const api::TileParameters &parameters, std::str
             // to seconds with a simple /10 for display
             values_writer.add_double(util::vector_tile::VARIANT_TYPE_DOUBLE, weight / 10.);
         }
+
+        for (const auto& name : names)
+        {
+            // Writing field type 4 == variant type
+            protozero::pbf_writer values_writer(layer_writer, util::vector_tile::VARIANT_TAG);
+            // Attribute value 1 == string type
+            values_writer.add_string(util::vector_tile::VARIANT_TYPE_STRING, name);
+        }
     }
 
     return Status::Ok;
diff --git a/src/engine/plugins/trip.cpp b/src/engine/plugins/trip.cpp
index 4303a47..2b463b9 100644
--- a/src/engine/plugins/trip.cpp
+++ b/src/engine/plugins/trip.cpp
@@ -4,22 +4,22 @@
 
 #include "engine/api/trip_api.hpp"
 #include "engine/api/trip_parameters.hpp"
-#include "engine/trip/trip_nearest_neighbour.hpp"
-#include "engine/trip/trip_farthest_insertion.hpp"
 #include "engine/trip/trip_brute_force.hpp"
-#include "util/dist_table_wrapper.hpp"   // to access the dist table more easily
-#include "util/matrix_graph_wrapper.hpp" // wrapper to use tarjan scc on dist table
+#include "engine/trip/trip_farthest_insertion.hpp"
+#include "engine/trip/trip_nearest_neighbour.hpp"
+#include "util/dist_table_wrapper.hpp" // to access the dist table more easily
 #include "util/json_container.hpp"
+#include "util/matrix_graph_wrapper.hpp" // wrapper to use tarjan scc on dist table
 
 #include <boost/assert.hpp>
 
-#include <cstdlib>
 #include <algorithm>
+#include <cstdlib>
+#include <iterator>
 #include <memory>
 #include <string>
 #include <utility>
 #include <vector>
-#include <iterator>
 
 namespace osrm
 {
@@ -210,8 +210,8 @@ Status TripPlugin::HandleRequest(const api::TripParameters &parameters,
             }
             else
             {
-                scc_route = trip::FarthestInsertionTrip(route_begin, route_end, number_of_locations,
-                                                        result_table);
+                scc_route = trip::FarthestInsertionTrip(
+                    route_begin, route_end, number_of_locations, result_table);
             }
         }
         else
diff --git a/src/engine/plugins/viaroute.cpp b/src/engine/plugins/viaroute.cpp
index 748151a..10c3101 100644
--- a/src/engine/plugins/viaroute.cpp
+++ b/src/engine/plugins/viaroute.cpp
@@ -1,6 +1,6 @@
 #include "engine/plugins/viaroute.hpp"
-#include "engine/datafacade/datafacade_base.hpp"
 #include "engine/api/route_api.hpp"
+#include "engine/datafacade/datafacade_base.hpp"
 #include "engine/status.hpp"
 
 #include "util/for_each_pair.hpp"
@@ -50,8 +50,9 @@ Status ViaRoutePlugin::HandleRequest(const api::RouteParameters &route_parameter
     auto phantom_node_pairs = GetPhantomNodes(route_parameters);
     if (phantom_node_pairs.size() != route_parameters.coordinates.size())
     {
-        return Error("NoSegment", std::string("Could not find a matching segment for coordinate ") +
-                                      std::to_string(phantom_node_pairs.size()),
+        return Error("NoSegment",
+                     std::string("Could not find a matching segment for coordinate ") +
+                         std::to_string(phantom_node_pairs.size()),
                      json_result);
     }
     BOOST_ASSERT(phantom_node_pairs.size() == route_parameters.coordinates.size());
@@ -64,8 +65,7 @@ Status ViaRoutePlugin::HandleRequest(const api::RouteParameters &route_parameter
 
     InternalRouteResult raw_route;
     auto build_phantom_pairs = [&raw_route, continue_straight_at_waypoint](
-        const PhantomNode &first_node, const PhantomNode &second_node)
-    {
+        const PhantomNode &first_node, const PhantomNode &second_node) {
         raw_route.segment_end_coordinates.push_back(PhantomNodes{first_node, second_node});
         auto &last_inserted = raw_route.segment_end_coordinates.back();
         // enable forward direction if possible
@@ -77,7 +77,8 @@ Status ViaRoutePlugin::HandleRequest(const api::RouteParameters &route_parameter
         // enable reverse direction if possible
         if (last_inserted.source_phantom.reverse_segment_id.id != SPECIAL_SEGMENTID)
         {
-            last_inserted.source_phantom.reverse_segment_id.enabled |= !continue_straight_at_waypoint;
+            last_inserted.source_phantom.reverse_segment_id.enabled |=
+                !continue_straight_at_waypoint;
         }
     };
     util::for_each_pair(snapped_phantoms, build_phantom_pairs);
@@ -95,7 +96,8 @@ Status ViaRoutePlugin::HandleRequest(const api::RouteParameters &route_parameter
     }
     else
     {
-        shortest_path(raw_route.segment_end_coordinates, route_parameters.continue_straight, raw_route);
+        shortest_path(
+            raw_route.segment_end_coordinates, route_parameters.continue_straight, raw_route);
     }
 
     // we can only know this after the fact, different SCC ids still
@@ -108,9 +110,9 @@ Status ViaRoutePlugin::HandleRequest(const api::RouteParameters &route_parameter
     else
     {
         auto first_component_id = snapped_phantoms.front().component.id;
-        auto not_in_same_component = std::any_of(snapped_phantoms.begin(), snapped_phantoms.end(),
-                                                 [first_component_id](const PhantomNode &node)
-                                                 {
+        auto not_in_same_component = std::any_of(snapped_phantoms.begin(),
+                                                 snapped_phantoms.end(),
+                                                 [first_component_id](const PhantomNode &node) {
                                                      return node.component.id != first_component_id;
                                                  });
 
diff --git a/src/engine/polyline_compressor.cpp b/src/engine/polyline_compressor.cpp
index 4cbfe1c..906ccb4 100644
--- a/src/engine/polyline_compressor.cpp
+++ b/src/engine/polyline_compressor.cpp
@@ -1,10 +1,10 @@
 #include "engine/polyline_compressor.hpp"
 
+#include <algorithm>
 #include <boost/assert.hpp>
+#include <cmath>
 #include <cstddef>
 #include <cstdlib>
-#include <cmath>
-#include <algorithm>
 
 namespace osrm
 {
@@ -70,20 +70,19 @@ std::string encodePolyline(CoordVectorForwardIter begin, CoordVectorForwardIter
     delta_numbers.reserve((size - 1) * 2);
     int current_lat = 0;
     int current_lon = 0;
-    std::for_each(begin, end,
-                  [&delta_numbers, &current_lat, &current_lon](const util::Coordinate loc)
-                  {
-                      const int lat_diff =
-                          std::round(static_cast<int>(loc.lat) * detail::COORDINATE_TO_POLYLINE) -
-                          current_lat;
-                      const int lon_diff =
-                          std::round(static_cast<int>(loc.lon) * detail::COORDINATE_TO_POLYLINE) -
-                          current_lon;
-                      delta_numbers.emplace_back(lat_diff);
-                      delta_numbers.emplace_back(lon_diff);
-                      current_lat += lat_diff;
-                      current_lon += lon_diff;
-                  });
+    std::for_each(
+        begin, end, [&delta_numbers, &current_lat, &current_lon](const util::Coordinate loc) {
+            const int lat_diff =
+                std::round(static_cast<int>(loc.lat) * detail::COORDINATE_TO_POLYLINE) -
+                current_lat;
+            const int lon_diff =
+                std::round(static_cast<int>(loc.lon) * detail::COORDINATE_TO_POLYLINE) -
+                current_lon;
+            delta_numbers.emplace_back(lat_diff);
+            delta_numbers.emplace_back(lon_diff);
+            current_lat += lat_diff;
+            current_lon += lon_diff;
+        });
     return encode(delta_numbers);
 }
 
diff --git a/src/extractor/compressed_edge_container.cpp b/src/extractor/compressed_edge_container.cpp
index 878d749..c0529ad 100644
--- a/src/extractor/compressed_edge_container.cpp
+++ b/src/extractor/compressed_edge_container.cpp
@@ -159,8 +159,8 @@ void CompressedEdgeContainer::CompressEdge(const EdgeID edge_id_1,
             m_compressed_geometries[list_to_remove_index];
 
         // found an existing list, append it to the list of edge_id_1
-        edge_bucket_list1.insert(edge_bucket_list1.end(), edge_bucket_list2.begin(),
-                                 edge_bucket_list2.end());
+        edge_bucket_list1.insert(
+            edge_bucket_list1.end(), edge_bucket_list2.begin(), edge_bucket_list2.end());
 
         // remove the list of edge_id_2
         m_edge_id_to_list_index_map.erase(edge_id_2);
diff --git a/src/extractor/edge_based_graph_factory.cpp b/src/extractor/edge_based_graph_factory.cpp
index 51f67d5..a9c4d3b 100644
--- a/src/extractor/edge_based_graph_factory.cpp
+++ b/src/extractor/edge_based_graph_factory.cpp
@@ -1,5 +1,5 @@
-#include "extractor/edge_based_edge.hpp"
 #include "extractor/edge_based_graph_factory.hpp"
+#include "extractor/edge_based_edge.hpp"
 #include "util/coordinate.hpp"
 #include "util/coordinate_calculation.hpp"
 #include "util/exception.hpp"
@@ -9,8 +9,9 @@
 #include "util/simple_logger.hpp"
 #include "util/timing_util.hpp"
 
-#include "extractor/suffix_table.hpp"
 #include "extractor/guidance/toolkit.hpp"
+#include "extractor/guidance/turn_analysis.hpp"
+#include "extractor/suffix_table.hpp"
 
 #include <boost/assert.hpp>
 #include <boost/numeric/conversion/cast.hpp>
@@ -22,6 +23,7 @@
 #include <limits>
 #include <sstream>
 #include <string>
+#include <unordered_map>
 
 namespace osrm
 {
@@ -123,8 +125,7 @@ void EdgeBasedGraphFactory::InsertEdgeBasedNode(const NodeID node_u, const NodeI
 
     NodeID current_edge_source_coordinate_id = node_u;
 
-    const auto edge_id_to_segment_id = [](const NodeID edge_based_node_id)
-    {
+    const auto edge_id_to_segment_id = [](const NodeID edge_based_node_id) {
         if (edge_based_node_id == SPECIAL_NODEID)
         {
             return SegmentID{SPECIAL_SEGMENTID, false};
@@ -144,13 +145,18 @@ void EdgeBasedGraphFactory::InsertEdgeBasedNode(const NodeID node_u, const NodeI
         BOOST_ASSERT(current_edge_target_coordinate_id != current_edge_source_coordinate_id);
 
         // build edges
-        m_edge_based_node_list.emplace_back(
-            edge_id_to_segment_id(forward_data.edge_id),
-            edge_id_to_segment_id(reverse_data.edge_id), current_edge_source_coordinate_id,
-            current_edge_target_coordinate_id, forward_data.name_id,
-            m_compressed_edge_container.GetPositionForID(edge_id_1),
-            m_compressed_edge_container.GetPositionForID(edge_id_2), false, INVALID_COMPONENTID, i,
-            forward_data.travel_mode, reverse_data.travel_mode);
+        m_edge_based_node_list.emplace_back(edge_id_to_segment_id(forward_data.edge_id),
+                                            edge_id_to_segment_id(reverse_data.edge_id),
+                                            current_edge_source_coordinate_id,
+                                            current_edge_target_coordinate_id,
+                                            forward_data.name_id,
+                                            m_compressed_edge_container.GetPositionForID(edge_id_1),
+                                            m_compressed_edge_container.GetPositionForID(edge_id_2),
+                                            false,
+                                            INVALID_COMPONENTID,
+                                            i,
+                                            forward_data.travel_mode,
+                                            reverse_data.travel_mode);
 
         m_edge_based_node_is_startpoint.push_back(forward_data.startpoint ||
                                                   reverse_data.startpoint);
@@ -188,8 +194,11 @@ void EdgeBasedGraphFactory::Run(const std::string &original_edge_data_filename,
     TIMER_STOP(generate_nodes);
 
     TIMER_START(generate_edges);
-    GenerateEdgeExpandedEdges(original_edge_data_filename, lua_state, edge_segment_lookup_filename,
-                              edge_penalty_filename, generate_edge_lookup);
+    GenerateEdgeExpandedEdges(original_edge_data_filename,
+                              lua_state,
+                              edge_segment_lookup_filename,
+                              edge_penalty_filename,
+                              generate_edge_lookup);
 
     TIMER_STOP(generate_edges);
 
@@ -323,9 +332,17 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
     // linear number of turns only.
     util::Percent progress(m_node_based_graph->GetNumberOfNodes());
     SuffixTable street_name_suffix_table(lua_state);
-    guidance::TurnAnalysis turn_analysis(*m_node_based_graph, m_node_info_list, *m_restriction_map,
-                                         m_barrier_nodes, m_compressed_edge_container, name_table,
+    guidance::TurnAnalysis turn_analysis(*m_node_based_graph,
+                                         m_node_info_list,
+                                         *m_restriction_map,
+                                         m_barrier_nodes,
+                                         m_compressed_edge_container,
+                                         name_table,
                                          street_name_suffix_table);
+
+    bearing_class_by_node_based_node.resize(m_node_based_graph->GetNumberOfNodes(),
+                                            std::numeric_limits<std::uint32_t>::max());
+
     for (const auto node_u : util::irange(0u, m_node_based_graph->GetNumberOfNodes()))
     {
         progress.PrintStatus(node_u);
@@ -340,6 +357,43 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
             auto possible_turns = turn_analysis.getTurns(node_u, edge_from_u);
 
             const NodeID node_v = m_node_based_graph->GetTarget(edge_from_u);
+
+            // the entry class depends on the turn, so we have to classify the interesction for
+            // every edge
+            const auto turn_classification =
+                classifyIntersection(node_v,
+                                     turn_analysis.getIntersection(node_u, edge_from_u),
+                                     *m_node_based_graph,
+                                     m_compressed_edge_container,
+                                     m_node_info_list);
+
+            const auto entry_class_id = [&](const util::guidance::EntryClass entry_class) {
+                if (0 == entry_class_hash.count(entry_class))
+                {
+                    const auto id = static_cast<std::uint16_t>(entry_class_hash.size());
+                    entry_class_hash[entry_class] = id;
+                    return id;
+                }
+                else
+                {
+                    return entry_class_hash.find(entry_class)->second;
+                }
+            }(turn_classification.first);
+
+            const auto bearing_class_id = [&](const util::guidance::BearingClass bearing_class) {
+                if (0 == bearing_class_hash.count(bearing_class))
+                {
+                    const auto id = static_cast<std::uint32_t>(bearing_class_hash.size());
+                    bearing_class_hash[bearing_class] = id;
+                    return id;
+                }
+                else
+                {
+                    return bearing_class_hash.find(bearing_class)->second;
+                }
+            }(turn_classification.second);
+            bearing_class_by_node_based_node[node_v] = bearing_class_id;
+
             for (const auto turn : possible_turns)
             {
                 const double turn_angle = turn.angle;
@@ -372,8 +426,11 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
 
                 BOOST_ASSERT(m_compressed_edge_container.HasEntryForID(edge_from_u));
                 original_edge_data_vector.emplace_back(
-                    m_compressed_edge_container.GetPositionForID(edge_from_u), edge_data1.name_id,
-                    turn_instruction, edge_data1.travel_mode);
+                    m_compressed_edge_container.GetPositionForID(edge_from_u),
+                    edge_data1.name_id,
+                    turn_instruction,
+                    entry_class_id,
+                    edge_data1.travel_mode);
 
                 ++original_edges_counter;
 
@@ -387,8 +444,11 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
 
                 // NOTE: potential overflow here if we hit 2^32 routable edges
                 BOOST_ASSERT(m_edge_based_edge_list.size() <= std::numeric_limits<NodeID>::max());
-                m_edge_based_edge_list.emplace_back(edge_data1.edge_id, edge_data2.edge_id,
-                                                    m_edge_based_edge_list.size(), distance, true,
+                m_edge_based_edge_list.emplace_back(edge_data1.edge_id,
+                                                    edge_data2.edge_id,
+                                                    m_edge_based_edge_list.size(),
+                                                    distance,
+                                                    true,
                                                     false);
 
                 // Here is where we write out the mapping between the edge-expanded edges, and
@@ -469,6 +529,9 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
         }
     }
 
+    util::SimpleLogger().Write() << "Created " << entry_class_hash.size() << " entry classes and "
+                                 << bearing_class_hash.size() << " Bearing Classes";
+
     FlushVectorToStream(edge_data_file, original_edge_data_vector);
 
     // Finally jump back to the empty space at the beginning and write length prefix
@@ -493,6 +556,38 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
                                  << " turns over barriers";
 }
 
+std::vector<util::guidance::BearingClass> EdgeBasedGraphFactory::GetBearingClasses() const
+{
+    std::vector<util::guidance::BearingClass> result(bearing_class_hash.size());
+    for (const auto &pair : bearing_class_hash)
+    {
+        BOOST_ASSERT(pair.second < result.size());
+        result[pair.second] = pair.first;
+    }
+    return result;
+}
+
+const std::vector<BearingClassID> &EdgeBasedGraphFactory::GetBearingClassIds() const
+{
+    return bearing_class_by_node_based_node;
+}
+
+std::vector<BearingClassID> &EdgeBasedGraphFactory::GetBearingClassIds()
+{
+    return bearing_class_by_node_based_node;
+}
+
+std::vector<util::guidance::EntryClass> EdgeBasedGraphFactory::GetEntryClasses() const
+{
+    std::vector<util::guidance::EntryClass> result(entry_class_hash.size());
+    for (const auto &pair : entry_class_hash)
+    {
+        BOOST_ASSERT(pair.second < result.size());
+        result[pair.second] = pair.first;
+    }
+    return result;
+}
+
 int EdgeBasedGraphFactory::GetTurnPenalty(double angle, lua_State *lua_state) const
 {
     BOOST_ASSERT(lua_state != nullptr);
diff --git a/src/extractor/extraction_containers.cpp b/src/extractor/extraction_containers.cpp
index 5106ca9..138552a 100644
--- a/src/extractor/extraction_containers.cpp
+++ b/src/extractor/extraction_containers.cpp
@@ -5,10 +5,10 @@
 #include "util/range_table.hpp"
 
 #include "util/exception.hpp"
-#include "util/simple_logger.hpp"
-#include "util/timing_util.hpp"
 #include "util/fingerprint.hpp"
 #include "util/lua_util.hpp"
+#include "util/simple_logger.hpp"
+#include "util/timing_util.hpp"
 
 #include <boost/assert.hpp>
 #include <boost/filesystem.hpp>
@@ -47,20 +47,10 @@ ExtractionContainers::ExtractionContainers()
 {
     // Check if stxxl can be instantiated
     stxxl::vector<unsigned> dummy_vector;
-    // Insert the empty string, it has no data and is zero length
+    // Insert three empty strings for name, destination and pronunciation
+    name_lengths.push_back(0);
+    name_lengths.push_back(0);
     name_lengths.push_back(0);
-}
-
-ExtractionContainers::~ExtractionContainers()
-{
-    // FIXME isn't this done implicitly of the stxxl::vectors go out of scope?
-    used_node_id_list.clear();
-    all_nodes_list.clear();
-    all_edges_list.clear();
-    name_char_data.clear();
-    name_lengths.clear();
-    restrictions_list.clear();
-    way_start_end_id_list.clear();
 }
 
 /**
@@ -109,7 +99,7 @@ void ExtractionContainers::WriteNames(const std::string &names_file_name) const
 
     unsigned total_length = 0;
 
-    for (const unsigned &name_length : name_lengths)
+    for (const auto name_length : name_lengths)
     {
         total_length += name_length;
     }
@@ -124,7 +114,7 @@ void ExtractionContainers::WriteNames(const std::string &names_file_name) const
     char write_buffer[WRITE_BLOCK_BUFFER_SIZE];
     unsigned buffer_len = 0;
 
-    for (const char &c : name_char_data)
+    for (const auto c : name_char_data)
     {
         write_buffer[buffer_len++] = c;
 
@@ -145,8 +135,8 @@ void ExtractionContainers::PrepareNodes()
 {
     std::cout << "[extractor] Sorting used nodes        ... " << std::flush;
     TIMER_START(sorting_used_nodes);
-    stxxl::sort(used_node_id_list.begin(), used_node_id_list.end(), OSMNodeIDSTXXLLess(),
-                stxxl_memory);
+    stxxl::sort(
+        used_node_id_list.begin(), used_node_id_list.end(), OSMNodeIDSTXXLLess(), stxxl_memory);
     TIMER_STOP(sorting_used_nodes);
     std::cout << "ok, after " << TIMER_SEC(sorting_used_nodes) << "s" << std::endl;
 
@@ -159,7 +149,9 @@ void ExtractionContainers::PrepareNodes()
 
     std::cout << "[extractor] Sorting all nodes         ... " << std::flush;
     TIMER_START(sorting_nodes);
-    stxxl::sort(all_nodes_list.begin(), all_nodes_list.end(), ExternalMemoryNodeSTXXLCompare(),
+    stxxl::sort(all_nodes_list.begin(),
+                all_nodes_list.end(),
+                ExternalMemoryNodeSTXXLCompare(),
                 stxxl_memory);
     TIMER_STOP(sorting_nodes);
     std::cout << "ok, after " << TIMER_SEC(sorting_nodes) << "s" << std::endl;
@@ -262,8 +254,7 @@ void ExtractionContainers::PrepareEdges(lua_State *segment_state)
 
     // Remove all remaining edges. They are invalid because there are no corresponding nodes for
     // them. This happens when using osmosis with bbox or polygon to extract smaller areas.
-    auto markSourcesInvalid = [](InternalExtractorEdge &edge)
-    {
+    auto markSourcesInvalid = [](InternalExtractorEdge &edge) {
         util::SimpleLogger().Write(LogLevel::logWARNING) << "Found invalid node reference "
                                                          << edge.result.source;
         edge.result.source = SPECIAL_NODEID;
@@ -327,13 +318,15 @@ void ExtractionContainers::PrepareEdges(lua_State *segment_state)
 
         if (has_segment_function)
         {
-            luabind::call_function<void>(
-                segment_state, "segment_function", boost::cref(edge_iterator->source_coordinate),
-                boost::cref(*node_iterator), distance, boost::ref(edge_iterator->weight_data));
+            luabind::call_function<void>(segment_state,
+                                         "segment_function",
+                                         boost::cref(edge_iterator->source_coordinate),
+                                         boost::cref(*node_iterator),
+                                         distance,
+                                         boost::ref(edge_iterator->weight_data));
         }
 
-        const double weight = [distance](const InternalExtractorEdge::WeightData &data)
-        {
+        const double weight = [distance](const InternalExtractorEdge::WeightData &data) {
             switch (data.type)
             {
             case InternalExtractorEdge::WeightType::EDGE_DURATION:
@@ -373,8 +366,7 @@ void ExtractionContainers::PrepareEdges(lua_State *segment_state)
 
     // Remove all remaining edges. They are invalid because there are no corresponding nodes for
     // them. This happens when using osmosis with bbox or polygon to extract smaller areas.
-    auto markTargetsInvalid = [](InternalExtractorEdge &edge)
-    {
+    auto markTargetsInvalid = [](InternalExtractorEdge &edge) {
         util::SimpleLogger().Write(LogLevel::logWARNING) << "Found invalid node reference "
                                                          << edge.result.target;
         edge.result.target = SPECIAL_NODEID;
@@ -386,8 +378,10 @@ void ExtractionContainers::PrepareEdges(lua_State *segment_state)
     // Sort edges by start.
     std::cout << "[extractor] Sorting edges by renumbered start ... " << std::flush;
     TIMER_START(sort_edges_by_renumbered_start);
-    stxxl::sort(all_edges_list.begin(), all_edges_list.end(),
-                CmpEdgeByInternalStartThenInternalTargetID(), stxxl_memory);
+    stxxl::sort(all_edges_list.begin(),
+                all_edges_list.end(),
+                CmpEdgeByInternalStartThenInternalTargetID(),
+                stxxl_memory);
     TIMER_STOP(sort_edges_by_renumbered_start);
     std::cout << "ok, after " << TIMER_SEC(sort_edges_by_renumbered_start) << "s" << std::endl;
 
@@ -594,15 +588,19 @@ void ExtractionContainers::PrepareRestrictions()
 {
     std::cout << "[extractor] Sorting used ways         ... " << std::flush;
     TIMER_START(sort_ways);
-    stxxl::sort(way_start_end_id_list.begin(), way_start_end_id_list.end(),
-                FirstAndLastSegmentOfWayStxxlCompare(), stxxl_memory);
+    stxxl::sort(way_start_end_id_list.begin(),
+                way_start_end_id_list.end(),
+                FirstAndLastSegmentOfWayStxxlCompare(),
+                stxxl_memory);
     TIMER_STOP(sort_ways);
     std::cout << "ok, after " << TIMER_SEC(sort_ways) << "s" << std::endl;
 
     std::cout << "[extractor] Sorting " << restrictions_list.size() << " restriction. by from... "
               << std::flush;
     TIMER_START(sort_restrictions);
-    stxxl::sort(restrictions_list.begin(), restrictions_list.end(), CmpRestrictionContainerByFrom(),
+    stxxl::sort(restrictions_list.begin(),
+                restrictions_list.end(),
+                CmpRestrictionContainerByFrom(),
                 stxxl_memory);
     TIMER_STOP(sort_restrictions);
     std::cout << "ok, after " << TIMER_SEC(sort_restrictions) << "s" << std::endl;
@@ -694,7 +692,9 @@ void ExtractionContainers::PrepareRestrictions()
 
     std::cout << "[extractor] Sorting restrictions. by to  ... " << std::flush;
     TIMER_START(sort_restrictions_to);
-    stxxl::sort(restrictions_list.begin(), restrictions_list.end(), CmpRestrictionContainerByTo(),
+    stxxl::sort(restrictions_list.begin(),
+                restrictions_list.end(),
+                CmpRestrictionContainerByTo(),
                 stxxl_memory);
     TIMER_STOP(sort_restrictions_to);
     std::cout << "ok, after " << TIMER_SEC(sort_restrictions_to) << "s" << std::endl;
diff --git a/src/extractor/extractor.cpp b/src/extractor/extractor.cpp
index c744c38..1406bdb 100644
--- a/src/extractor/extractor.cpp
+++ b/src/extractor/extractor.cpp
@@ -9,20 +9,19 @@
 #include "extractor/scripting_environment.hpp"
 
 #include "extractor/raster_source.hpp"
+#include "util/graph_loader.hpp"
 #include "util/io.hpp"
+#include "util/lua_util.hpp"
 #include "util/make_unique.hpp"
+#include "util/name_table.hpp"
+#include "util/range_table.hpp"
 #include "util/simple_logger.hpp"
 #include "util/timing_util.hpp"
-#include "util/lua_util.hpp"
-#include "util/graph_loader.hpp"
-#include "util/name_table.hpp"
-
-#include "util/typedefs.hpp"
 
-#include "util/static_graph.hpp"
-#include "util/static_rtree.hpp"
 #include "extractor/compressed_edge_container.hpp"
 #include "extractor/restriction_map.hpp"
+#include "util/static_graph.hpp"
+#include "util/static_rtree.hpp"
 
 #include "extractor/tarjan_scc.hpp"
 
@@ -41,14 +40,15 @@
 
 #include <algorithm>
 #include <atomic>
+#include <bitset>
+#include <chrono>
 #include <chrono>
 #include <fstream>
 #include <iostream>
 #include <thread>
+#include <type_traits>
 #include <unordered_map>
 #include <vector>
-#include <bitset>
-#include <chrono>
 
 namespace osrm
 {
@@ -159,8 +159,7 @@ int Extractor::run()
             // parse OSM entities in parallel, store in resulting vectors
             tbb::parallel_for(
                 tbb::blocked_range<std::size_t>(0, osm_elements.size()),
-                [&](const tbb::blocked_range<std::size_t> &range)
-                {
+                [&](const tbb::blocked_range<std::size_t> &range) {
                     ExtractionNode result_node;
                     ExtractionWay result_way;
                     auto &local_context = scripting_environment.GetContex();
@@ -175,19 +174,21 @@ int Extractor::run()
                             result_node.clear();
                             ++number_of_nodes;
                             luabind::call_function<void>(
-                                local_context.state, "node_function",
+                                local_context.state,
+                                "node_function",
                                 boost::cref(static_cast<const osmium::Node &>(*entity)),
                                 boost::ref(result_node));
-                            resulting_nodes.push_back(std::make_pair(x, result_node));
+                            resulting_nodes.push_back(std::make_pair(x, std::move(result_node)));
                             break;
                         case osmium::item_type::way:
                             result_way.clear();
                             ++number_of_ways;
                             luabind::call_function<void>(
-                                local_context.state, "way_function",
+                                local_context.state,
+                                "way_function",
                                 boost::cref(static_cast<const osmium::Way &>(*entity)),
                                 boost::ref(result_way));
-                            resulting_ways.push_back(std::make_pair(x, result_way));
+                            resulting_ways.push_back(std::make_pair(x, std::move(result_way)));
                             break;
                         case osmium::item_type::relation:
                             ++number_of_relations;
@@ -235,8 +236,10 @@ int Extractor::run()
             return 1;
         }
 
-        extraction_containers.PrepareData(config.output_file_name, config.restriction_file_name,
-                                          config.names_file_name, main_context.state);
+        extraction_containers.PrepareData(config.output_file_name,
+                                          config.restriction_file_name,
+                                          config.names_file_name,
+                                          main_context.state);
 
         WriteProfileProperties(config.profile_properties_output_path, main_context.properties);
 
@@ -269,10 +272,14 @@ int Extractor::run()
         std::vector<bool> node_is_startpoint;
         std::vector<EdgeWeight> edge_based_node_weights;
         std::vector<QueryNode> internal_to_external_node_map;
-        auto graph_size = BuildEdgeExpandedGraph(main_context.state, main_context.properties,
+        auto graph_size = BuildEdgeExpandedGraph(main_context.state,
+                                                 main_context.properties,
                                                  internal_to_external_node_map,
-                                                 edge_based_node_list, node_is_startpoint,
-                                                 edge_based_node_weights, edge_based_edge_list);
+                                                 edge_based_node_list,
+                                                 node_is_startpoint,
+                                                 edge_based_node_weights,
+                                                 edge_based_edge_list,
+                                                 config.intersection_class_data_output_path);
 
         auto number_of_node_based_nodes = graph_size.first;
         auto max_edge_id = graph_size.second;
@@ -291,7 +298,8 @@ int Extractor::run()
 
         FindComponents(max_edge_id, edge_based_edge_list, edge_based_node_list);
 
-        BuildRTree(std::move(edge_based_node_list), std::move(node_is_startpoint),
+        BuildRTree(std::move(edge_based_node_list),
+                   std::move(node_is_startpoint),
                    internal_to_external_node_map);
 
         TIMER_STOP(rtree);
@@ -474,7 +482,8 @@ Extractor::BuildEdgeExpandedGraph(lua_State *lua_state,
                                   std::vector<EdgeBasedNode> &node_based_edge_list,
                                   std::vector<bool> &node_is_startpoint,
                                   std::vector<EdgeWeight> &edge_based_node_weights,
-                                  util::DeallocatingVector<EdgeBasedEdge> &edge_based_edge_list)
+                                  util::DeallocatingVector<EdgeBasedEdge> &edge_based_edge_list,
+                                  const std::string &intersection_class_output_file)
 {
     std::unordered_set<NodeID> barrier_nodes;
     std::unordered_set<NodeID> traffic_lights;
@@ -485,7 +494,10 @@ Extractor::BuildEdgeExpandedGraph(lua_State *lua_state,
 
     CompressedEdgeContainer compressed_edge_container;
     GraphCompressor graph_compressor;
-    graph_compressor.Compress(barrier_nodes, traffic_lights, *restriction_map, *node_based_graph,
+    graph_compressor.Compress(barrier_nodes,
+                              traffic_lights,
+                              *restriction_map,
+                              *node_based_graph,
                               compressed_edge_container);
 
     compressed_edge_container.SerializeInternalVector(config.geometry_output_path);
@@ -493,12 +505,19 @@ Extractor::BuildEdgeExpandedGraph(lua_State *lua_state,
     util::NameTable name_table(config.names_file_name);
 
     EdgeBasedGraphFactory edge_based_graph_factory(
-        node_based_graph, compressed_edge_container, barrier_nodes, traffic_lights,
+        node_based_graph,
+        compressed_edge_container,
+        barrier_nodes,
+        traffic_lights,
         std::const_pointer_cast<RestrictionMap const>(restriction_map),
-        internal_to_external_node_map, profile_properties, name_table);
-
-    edge_based_graph_factory.Run(config.edge_output_path, lua_state,
-                                 config.edge_segment_lookup_path, config.edge_penalty_path,
+        internal_to_external_node_map,
+        profile_properties,
+        name_table);
+
+    edge_based_graph_factory.Run(config.edge_output_path,
+                                 lua_state,
+                                 config.edge_segment_lookup_path,
+                                 config.edge_penalty_path,
                                  config.generate_edge_lookup);
 
     edge_based_graph_factory.GetEdgeBasedEdges(edge_based_edge_list);
@@ -508,6 +527,12 @@ Extractor::BuildEdgeExpandedGraph(lua_State *lua_state,
     auto max_edge_id = edge_based_graph_factory.GetHighestEdgeID();
 
     const std::size_t number_of_node_based_nodes = node_based_graph->GetNumberOfNodes();
+
+    WriteIntersectionClassificationData(intersection_class_output_file,
+                                        edge_based_graph_factory.GetBearingClassIds(),
+                                        edge_based_graph_factory.GetBearingClasses(),
+                                        edge_based_graph_factory.GetEntryClasses());
+
     return std::make_pair(number_of_node_based_nodes, max_edge_id);
 }
 
@@ -563,9 +588,10 @@ void Extractor::BuildRTree(std::vector<EdgeBasedNode> node_based_edge_list,
     node_based_edge_list.resize(new_size);
 
     TIMER_START(construction);
-    util::StaticRTree<EdgeBasedNode, std::vector<QueryNode>> rtree(
-        node_based_edge_list, config.rtree_nodes_output_path, config.rtree_leafs_output_path,
-        internal_to_external_node_map);
+    util::StaticRTree<EdgeBasedNode, std::vector<QueryNode>> rtree(node_based_edge_list,
+                                                                   config.rtree_nodes_output_path,
+                                                                   config.rtree_leafs_output_path,
+                                                                   internal_to_external_node_map);
 
     TIMER_STOP(construction);
     util::SimpleLogger().Write() << "finished r-tree construction in " << TIMER_SEC(construction)
@@ -601,5 +627,59 @@ void Extractor::WriteEdgeBasedGraph(
 
     util::SimpleLogger().Write() << "Processed " << number_of_used_edges << " edges";
 }
+
+void Extractor::WriteIntersectionClassificationData(
+    const std::string &output_file_name,
+    const std::vector<BearingClassID> &node_based_intersection_classes,
+    const std::vector<util::guidance::BearingClass> &bearing_classes,
+    const std::vector<util::guidance::EntryClass> &entry_classes) const
+{
+    std::ofstream file_out_stream(output_file_name.c_str(), std::ios::binary);
+    if (!file_out_stream)
+    {
+        util::SimpleLogger().Write(logWARNING) << "Failed to open " << output_file_name
+                                               << " for writing";
+        return;
+    }
+
+    util::SimpleLogger().Write() << "Writing Intersection Classification Data";
+    TIMER_START(write_edges);
+    util::writeFingerprint(file_out_stream);
+    util::serializeVector(file_out_stream, node_based_intersection_classes);
+
+    // create range table for vectors:
+    std::vector<unsigned> bearing_counts;
+    bearing_counts.reserve(bearing_classes.size());
+    std::uint64_t total_bearings = 0;
+    for (const auto &bearing_class : bearing_classes)
+    {
+        bearing_counts.push_back(
+            static_cast<unsigned>(bearing_class.getAvailableBearings().size()));
+        total_bearings += bearing_class.getAvailableBearings().size();
+    }
+
+    util::RangeTable<> bearing_class_range_table(bearing_counts);
+    file_out_stream << bearing_class_range_table;
+
+    file_out_stream << total_bearings;
+    for (const auto &bearing_class : bearing_classes)
+    {
+        const auto &bearings = bearing_class.getAvailableBearings();
+        file_out_stream.write(reinterpret_cast<const char *>(&bearings[0]),
+                              sizeof(bearings[0]) * bearings.size());
+    }
+
+    // FIXME
+    // This should be here, but g++4.8 does not have it...
+    // static_assert(std::is_trivially_copyable<util::guidance::EntryClass>::value,
+    //              "EntryClass Serialization requires trivial copyable entry classes");
+
+    util::serializeVector(file_out_stream, entry_classes);
+    TIMER_STOP(write_edges);
+    util::SimpleLogger().Write() << "ok, after " << TIMER_SEC(write_edges) << "s for "
+                                 << node_based_intersection_classes.size() << " Indices into "
+                                 << bearing_classes.size() << " bearing classes and "
+                                 << entry_classes.size() << " entry classes";
+}
 }
 }
diff --git a/src/extractor/extractor_callbacks.cpp b/src/extractor/extractor_callbacks.cpp
index 0e07d93..09ef699 100644
--- a/src/extractor/extractor_callbacks.cpp
+++ b/src/extractor/extractor_callbacks.cpp
@@ -1,13 +1,13 @@
-#include "extractor/extractor_callbacks.hpp"
 #include "extractor/extraction_containers.hpp"
 #include "extractor/extraction_node.hpp"
 #include "extractor/extraction_way.hpp"
 
 #include "extractor/external_memory_node.hpp"
 #include "extractor/restriction.hpp"
-#include "util/simple_logger.hpp"
 #include "util/for_each_pair.hpp"
+#include "util/simple_logger.hpp"
 
+#include "extractor/extractor_callbacks.hpp"
 #include <boost/optional/optional.hpp>
 
 #include <osmium/osm.hpp>
@@ -27,7 +27,8 @@ namespace extractor
 ExtractorCallbacks::ExtractorCallbacks(ExtractionContainers &extraction_containers)
     : external_memory(extraction_containers)
 {
-    string_map[""] = 0;
+    // we reserved 0, 1, 2 for the empty case
+    string_map[MapKey("", "")] = 0;
 }
 
 /**
@@ -42,7 +43,9 @@ void ExtractorCallbacks::ProcessNode(const osmium::Node &input_node,
     external_memory.all_nodes_list.push_back(
         {util::toFixed(util::FloatLongitude(input_node.location().lon())),
          util::toFixed(util::FloatLatitude(input_node.location().lat())),
-         OSMNodeID(input_node.id()), result_node.barrier, result_node.traffic_lights});
+         OSMNodeID(input_node.id()),
+         result_node.barrier,
+         result_node.traffic_lights});
 }
 
 void ExtractorCallbacks::ProcessRestriction(
@@ -137,20 +140,50 @@ void ExtractorCallbacks::ProcessWay(const osmium::Way &input_way, const Extracti
         road_classification.road_class = guidance::functionalRoadClassFromTag(data);
     }
 
+    // Deduplicates street names and street destination names based on the street_map map.
+    // In case we do not already store the name, inserts (name, id) tuple and return id.
+    // Otherwise fetches the id based on the name and returns it without insertion.
+
+    const constexpr auto MAX_STRING_LENGTH = 255u;
+
     // Get the unique identifier for the street name
-    const auto &string_map_iterator = string_map.find(parsed_way.name);
+    // Get the unique identifier for the street name and destination
+    const auto name_iterator = string_map.find(MapKey(parsed_way.name, parsed_way.destinations));
     unsigned name_id = external_memory.name_lengths.size();
-    if (string_map.end() == string_map_iterator)
+    if (string_map.end() == name_iterator)
     {
-        auto name_length = std::min<unsigned>(255u, parsed_way.name.size());
-        std::copy(parsed_way.name.c_str(), parsed_way.name.c_str() + name_length,
+        auto name_length = std::min<unsigned>(MAX_STRING_LENGTH, parsed_way.name.size());
+        auto destinations_length =
+            std::min<unsigned>(MAX_STRING_LENGTH, parsed_way.destinations.size());
+        auto pronunciation_length =
+            std::min<unsigned>(MAX_STRING_LENGTH, parsed_way.pronunciation.size());
+
+        external_memory.name_char_data.reserve(name_id + name_length + destinations_length +
+                                               pronunciation_length);
+
+        std::copy(parsed_way.name.c_str(),
+                  parsed_way.name.c_str() + name_length,
                   std::back_inserter(external_memory.name_char_data));
+
+        std::copy(parsed_way.destinations.c_str(),
+                  parsed_way.destinations.c_str() + destinations_length,
+                  std::back_inserter(external_memory.name_char_data));
+
+        std::copy(parsed_way.pronunciation.c_str(),
+                  parsed_way.pronunciation.c_str() + pronunciation_length,
+                  std::back_inserter(external_memory.name_char_data));
+
         external_memory.name_lengths.push_back(name_length);
-        string_map.insert(std::make_pair(parsed_way.name, name_id));
+        external_memory.name_lengths.push_back(destinations_length);
+        external_memory.name_lengths.push_back(pronunciation_length);
+
+        auto k = MapKey{parsed_way.name, parsed_way.destinations};
+        auto v = MapVal{name_id};
+        string_map.emplace(std::move(k), std::move(v));
     }
     else
     {
-        name_id = string_map_iterator->second;
+        name_id = name_iterator->second;
     }
 
     const bool split_edge = (parsed_way.forward_speed > 0) &&
@@ -160,12 +193,13 @@ void ExtractorCallbacks::ProcessWay(const osmium::Way &input_way, const Extracti
                             ((parsed_way.forward_speed != parsed_way.backward_speed) ||
                              (parsed_way.forward_travel_mode != parsed_way.backward_travel_mode));
 
-    std::transform(input_way.nodes().begin(), input_way.nodes().end(),
+    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(ref.ref());
-                   });
+                   [](const osmium::NodeRef &ref) { return OSMNodeID(ref.ref()); });
 
     const bool is_opposite_way = TRAVEL_MODE_INACCESSIBLE == parsed_way.forward_travel_mode;
 
@@ -174,55 +208,83 @@ void ExtractorCallbacks::ProcessWay(const osmium::Way &input_way, const Extracti
     {
         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(first_node.ref()), OSMNodeID(last_node.ref()),
-                                    name_id, backward_weight_data, true, false,
-                                    parsed_way.roundabout, parsed_way.is_access_restricted,
-                                    parsed_way.is_startpoint, parsed_way.backward_travel_mode,
-                                    false, road_classification));
-                            });
+        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(first_node.ref()),
+                                          OSMNodeID(last_node.ref()),
+                                          name_id,
+                                          backward_weight_data,
+                                          true,
+                                          false,
+                                          parsed_way.roundabout,
+                                          parsed_way.is_access_restricted,
+                                          parsed_way.is_startpoint,
+                                          parsed_way.backward_travel_mode,
+                                          false,
+                                          road_classification));
+            });
 
         external_memory.way_start_end_id_list.push_back(
-            {OSMWayID(input_way.id()), OSMNodeID(input_way.nodes().back().ref()),
+            {OSMWayID(input_way.id()),
+             OSMNodeID(input_way.nodes().back().ref()),
              OSMNodeID(input_way.nodes()[input_way.nodes().size() - 2].ref()),
-             OSMNodeID(input_way.nodes()[1].ref()), OSMNodeID(input_way.nodes()[0].ref())});
+             OSMNodeID(input_way.nodes()[1].ref()),
+             OSMNodeID(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(),
-                            [&](const osmium::NodeRef &first_node, const osmium::NodeRef &last_node)
-                            {
-                                external_memory.all_edges_list.push_back(InternalExtractorEdge(
-                                    OSMNodeID(first_node.ref()), OSMNodeID(last_node.ref()),
-                                    name_id, forward_weight_data, true, !forward_only,
-                                    parsed_way.roundabout, parsed_way.is_access_restricted,
-                                    parsed_way.is_startpoint, parsed_way.forward_travel_mode,
-                                    split_edge, road_classification));
-                            });
+        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(first_node.ref()),
+                                          OSMNodeID(last_node.ref()),
+                                          name_id,
+                                          forward_weight_data,
+                                          true,
+                                          !forward_only,
+                                          parsed_way.roundabout,
+                                          parsed_way.is_access_restricted,
+                                          parsed_way.is_startpoint,
+                                          parsed_way.forward_travel_mode,
+                                          split_edge,
+                                          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(first_node.ref()), OSMNodeID(last_node.ref()), name_id,
-                        backward_weight_data, false, true, parsed_way.roundabout,
-                        parsed_way.is_access_restricted, parsed_way.is_startpoint,
-                        parsed_way.backward_travel_mode, true, road_classification));
+                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(first_node.ref()),
+                                              OSMNodeID(last_node.ref()),
+                                              name_id,
+                                              backward_weight_data,
+                                              false,
+                                              true,
+                                              parsed_way.roundabout,
+                                              parsed_way.is_access_restricted,
+                                              parsed_way.is_startpoint,
+                                              parsed_way.backward_travel_mode,
+                                              true,
+                                              road_classification));
                 });
         }
 
         external_memory.way_start_end_id_list.push_back(
-            {OSMWayID(input_way.id()), OSMNodeID(input_way.nodes().back().ref()),
+            {OSMWayID(input_way.id()),
+             OSMNodeID(input_way.nodes().back().ref()),
              OSMNodeID(input_way.nodes()[input_way.nodes().size() - 2].ref()),
-             OSMNodeID(input_way.nodes()[1].ref()), OSMNodeID(input_way.nodes()[0].ref())});
+             OSMNodeID(input_way.nodes()[1].ref()),
+             OSMNodeID(input_way.nodes()[0].ref())});
     }
 }
 }
diff --git a/src/extractor/graph_compressor.cpp b/src/extractor/graph_compressor.cpp
index 282087d..a30684e 100644
--- a/src/extractor/graph_compressor.cpp
+++ b/src/extractor/graph_compressor.cpp
@@ -151,10 +151,10 @@ void GraphCompressor::Compress(const std::unordered_set<NodeID> &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);
+            geometry_compressor.CompressEdge(
+                reverse_e1, reverse_e2, node_v, node_u, reverse_weight1, reverse_weight2);
         }
     }
 
diff --git a/src/extractor/guidance/intersection_generator.cpp b/src/extractor/guidance/intersection_generator.cpp
index 8d0d072..f11da13 100644
--- a/src/extractor/guidance/intersection_generator.cpp
+++ b/src/extractor/guidance/intersection_generator.cpp
@@ -1,5 +1,5 @@
-#include "extractor/guidance/constants.hpp"
 #include "extractor/guidance/intersection_generator.hpp"
+#include "extractor/guidance/constants.hpp"
 #include "extractor/guidance/toolkit.hpp"
 
 #include <algorithm>
@@ -176,7 +176,7 @@ Intersection IntersectionGenerator::mergeSegregatedRoads(Intersection intersecti
         const auto &first_data = node_based_graph.GetEdgeData(intersection[first].turn.eid);
         const auto &second_data = node_based_graph.GetEdgeData(intersection[second].turn.eid);
 
-        return first_data.name_id != INVALID_NAME_ID && first_data.name_id == second_data.name_id &&
+        return first_data.name_id != EMPTY_NAMEID && first_data.name_id == second_data.name_id &&
                !first_data.roundabout && !second_data.roundabout &&
                first_data.travel_mode == second_data.travel_mode &&
                first_data.road_classification == second_data.road_classification &&
@@ -240,14 +240,16 @@ Intersection IntersectionGenerator::mergeSegregatedRoads(Intersection intersecti
 
         if (is_connected_to_roundabout)
         {
-            // We are merging a u-turn against the direction of a roundabout
-            //
-            //    -----------> roundabout
-            //       /    \
-            //    out      in
-            //
-            // These cases have to be disabled, even if they are not forbidden specifically by a
-            // relation
+            /*
+             * We are merging a u-turn against the direction of a roundabout
+             *
+             *     -----------> roundabout
+             *        /    \
+             *     out      in
+             *
+             * These cases have to be disabled, even if they are not forbidden specifically by a
+             * relation
+             */
             intersection[0].entry_allowed = false;
         }
 
diff --git a/src/extractor/guidance/intersection_handler.cpp b/src/extractor/guidance/intersection_handler.cpp
index c03db79..6b0b2fe 100644
--- a/src/extractor/guidance/intersection_handler.cpp
+++ b/src/extractor/guidance/intersection_handler.cpp
@@ -1,5 +1,5 @@
-#include "extractor/guidance/constants.hpp"
 #include "extractor/guidance/intersection_handler.hpp"
+#include "extractor/guidance/constants.hpp"
 #include "extractor/guidance/toolkit.hpp"
 
 #include "util/guidance/toolkit.hpp"
@@ -38,12 +38,13 @@ IntersectionHandler::~IntersectionHandler() {}
 
 std::size_t IntersectionHandler::countValid(const Intersection &intersection) const
 {
-    return std::count_if(intersection.begin(), intersection.end(),
-                         [](const ConnectedRoad &road) { return road.entry_allowed; });
+    return std::count_if(intersection.begin(), intersection.end(), [](const ConnectedRoad &road) {
+        return road.entry_allowed;
+    });
 }
 
-TurnType IntersectionHandler::findBasicTurnType(const EdgeID via_edge,
-                                                const ConnectedRoad &road) const
+TurnType::Enum IntersectionHandler::findBasicTurnType(const EdgeID via_edge,
+                                                      const ConnectedRoad &road) const
 {
 
     const auto &in_data = node_based_graph.GetEdgeData(via_edge);
@@ -56,7 +57,7 @@ TurnType IntersectionHandler::findBasicTurnType(const EdgeID via_edge,
     if (!on_ramp && onto_ramp)
         return TurnType::OnRamp;
 
-    if (in_data.name_id == out_data.name_id && in_data.name_id != INVALID_NAME_ID)
+    if (in_data.name_id == out_data.name_id && in_data.name_id != EMPTY_NAMEID)
     {
         return TurnType::Continue;
     }
@@ -94,9 +95,9 @@ TurnInstruction IntersectionHandler::getInstructionForObvious(const std::size_t
             // obvious turn onto a through street is a merge
             if (through_street)
             {
-                return {TurnType::Merge, road.turn.angle > STRAIGHT_ANGLE
-                                             ? DirectionModifier::SlightRight
-                                             : DirectionModifier::SlightLeft};
+                return {TurnType::Merge,
+                        road.turn.angle > STRAIGHT_ANGLE ? DirectionModifier::SlightRight
+                                                         : DirectionModifier::SlightLeft};
             }
             else
             {
@@ -305,7 +306,7 @@ void IntersectionHandler::assignTrivialTurns(const EdgeID via_eid,
 bool IntersectionHandler::isThroughStreet(const std::size_t index,
                                           const Intersection &intersection) const
 {
-    if (node_based_graph.GetEdgeData(intersection[index].turn.eid).name_id == INVALID_NAME_ID)
+    if (node_based_graph.GetEdgeData(intersection[index].turn.eid).name_id == EMPTY_NAMEID)
         return false;
     for (const auto &road : intersection)
     {
diff --git a/src/extractor/guidance/intersection_scenario_three_way.cpp b/src/extractor/guidance/intersection_scenario_three_way.cpp
index ae28688..7241eb7 100644
--- a/src/extractor/guidance/intersection_scenario_three_way.cpp
+++ b/src/extractor/guidance/intersection_scenario_three_way.cpp
@@ -1,5 +1,5 @@
-#include "extractor/guidance/constants.hpp"
 #include "extractor/guidance/intersection_scenario_three_way.hpp"
+#include "extractor/guidance/constants.hpp"
 #include "extractor/guidance/toolkit.hpp"
 
 #include "util/guidance/toolkit.hpp"
diff --git a/src/extractor/guidance/motorway_handler.cpp b/src/extractor/guidance/motorway_handler.cpp
index dd4f1e9..cc81bc9 100644
--- a/src/extractor/guidance/motorway_handler.cpp
+++ b/src/extractor/guidance/motorway_handler.cpp
@@ -1,5 +1,5 @@
-#include "extractor/guidance/constants.hpp"
 #include "extractor/guidance/motorway_handler.hpp"
+#include "extractor/guidance/constants.hpp"
 #include "extractor/guidance/toolkit.hpp"
 
 #include "util/guidance/toolkit.hpp"
@@ -45,8 +45,10 @@ inline bool isRampClass(EdgeID eid, const util::NodeBasedDynamicGraph &node_base
 MotorwayHandler::MotorwayHandler(const util::NodeBasedDynamicGraph &node_based_graph,
                                  const std::vector<QueryNode> &node_info_list,
                                  const util::NameTable &name_table,
-                                 const SuffixTable &street_name_suffix_table)
-    : IntersectionHandler(node_based_graph, node_info_list, name_table, street_name_suffix_table)
+                                 const SuffixTable &street_name_suffix_table,
+                                 const IntersectionGenerator &intersection_generator)
+    : IntersectionHandler(node_based_graph, node_info_list, name_table, street_name_suffix_table),
+      intersection_generator(intersection_generator)
 {
 }
 
@@ -127,7 +129,7 @@ Intersection MotorwayHandler::fromMotorway(const EdgeID via_eid, Intersection in
         {
             const auto &out_data = node_based_graph.GetEdgeData(road.turn.eid);
             if (road.turn.angle != 0 && in_data.name_id == out_data.name_id &&
-                in_data.name_id != INVALID_NAME_ID &&
+                in_data.name_id != EMPTY_NAMEID &&
                 detail::isMotorwayClass(out_data.road_classification.road_class))
                 return road.turn.angle;
         }
@@ -231,8 +233,10 @@ Intersection MotorwayHandler::fromMotorway(const EdgeID via_eid, Intersection in
                 BOOST_ASSERT(!detail::isRampClass(intersection[1].turn.eid, node_based_graph));
 
                 intersection[1].turn.instruction =
-                    getInstructionForObvious(intersection.size(), via_eid,
-                                             isThroughStreet(1, intersection), intersection[1]);
+                    getInstructionForObvious(intersection.size(),
+                                             via_eid,
+                                             isThroughStreet(1, intersection),
+                                             intersection[1]);
             }
             else
             {
@@ -251,18 +255,16 @@ Intersection MotorwayHandler::fromMotorway(const EdgeID via_eid, Intersection in
                     else if (road.turn.angle < continue_angle)
                     {
                         road.turn.instruction = {
-                            detail::isRampClass(road.turn.eid, node_based_graph)
-                                ? TurnType::OffRamp
-                                : TurnType::Turn,
+                            detail::isRampClass(road.turn.eid, node_based_graph) ? TurnType::OffRamp
+                                                                                 : TurnType::Turn,
                             (road.turn.angle < 145) ? DirectionModifier::Right
                                                     : DirectionModifier::SlightRight};
                     }
                     else if (road.turn.angle > continue_angle)
                     {
                         road.turn.instruction = {
-                            detail::isRampClass(road.turn.eid, node_based_graph)
-                                ? TurnType::OffRamp
-                                : TurnType::Turn,
+                            detail::isRampClass(road.turn.eid, node_based_graph) ? TurnType::OffRamp
+                                                                                 : TurnType::Turn,
                             (road.turn.angle > 215) ? DirectionModifier::Left
                                                     : DirectionModifier::SlightLeft};
                     }
@@ -275,8 +277,10 @@ Intersection MotorwayHandler::fromMotorway(const EdgeID via_eid, Intersection in
             if (exiting_motorways == 2 && intersection.size() == 2)
             {
                 intersection[1].turn.instruction =
-                    getInstructionForObvious(intersection.size(), via_eid,
-                                             isThroughStreet(1, intersection), intersection[1]);
+                    getInstructionForObvious(intersection.size(),
+                                             via_eid,
+                                             isThroughStreet(1, intersection),
+                                             intersection[1]);
                 util::SimpleLogger().Write(logDEBUG) << "Disabled U-Turn on a freeway";
                 intersection[0].entry_allowed = false; // UTURN on the freeway
             }
@@ -329,7 +333,9 @@ Intersection MotorwayHandler::fromMotorway(const EdgeID via_eid, Intersection in
                         }
                     }
                 }
-                assignFork(via_eid, intersection[third_valid], intersection[second_valid],
+                assignFork(via_eid,
+                           intersection[third_valid],
+                           intersection[second_valid],
                            intersection[first_valid]);
             }
             else
@@ -373,13 +379,13 @@ Intersection MotorwayHandler::fromRamp(const EdgeID via_eid, Intersection inters
             {
                 if (detail::isMotorwayClass(intersection[1].turn.eid, node_based_graph) &&
                     node_based_graph.GetEdgeData(intersection[2].turn.eid).name_id !=
-                        INVALID_NAME_ID &&
+                        EMPTY_NAMEID &&
                     node_based_graph.GetEdgeData(intersection[2].turn.eid).name_id ==
                         node_based_graph.GetEdgeData(intersection[1].turn.eid).name_id)
                 {
                     // circular order indicates a merge to the left (0-3 onto 4
                     if (angularDeviation(intersection[1].turn.angle, STRAIGHT_ANGLE) <
-                        NARROW_TURN_ANGLE)
+                        2*NARROW_TURN_ANGLE)
                         intersection[1].turn.instruction = {TurnType::Merge,
                                                             DirectionModifier::SlightLeft};
                     else // fallback
@@ -389,8 +395,10 @@ Intersection MotorwayHandler::fromRamp(const EdgeID via_eid, Intersection inters
                 else // passing by the end of a motorway
                 {
                     intersection[1].turn.instruction =
-                        getInstructionForObvious(intersection.size(), via_eid,
-                                                 isThroughStreet(1, intersection), intersection[1]);
+                        getInstructionForObvious(intersection.size(),
+                                                 via_eid,
+                                                 isThroughStreet(1, intersection),
+                                                 intersection[1]);
                 }
             }
             else
@@ -398,13 +406,13 @@ Intersection MotorwayHandler::fromRamp(const EdgeID via_eid, Intersection inters
                 BOOST_ASSERT(intersection[2].entry_allowed);
                 if (detail::isMotorwayClass(intersection[2].turn.eid, node_based_graph) &&
                     node_based_graph.GetEdgeData(intersection[1].turn.eid).name_id !=
-                        INVALID_NAME_ID &&
-                    node_based_graph.GetEdgeData(intersection[1].turn.eid).name_id ==
-                        node_based_graph.GetEdgeData(intersection[0].turn.eid).name_id)
+                        EMPTY_NAMEID &&
+                    node_based_graph.GetEdgeData(intersection[2].turn.eid).name_id ==
+                        node_based_graph.GetEdgeData(intersection[1].turn.eid).name_id)
                 {
                     // circular order (5-0) onto 4
                     if (angularDeviation(intersection[2].turn.angle, STRAIGHT_ANGLE) <
-                        NARROW_TURN_ANGLE)
+                        2 * NARROW_TURN_ANGLE)
                         intersection[2].turn.instruction = {TurnType::Merge,
                                                             DirectionModifier::SlightRight};
                     else // fallback
@@ -414,8 +422,10 @@ Intersection MotorwayHandler::fromRamp(const EdgeID via_eid, Intersection inters
                 else // passing the end of a highway
                 {
                     intersection[2].turn.instruction =
-                        getInstructionForObvious(intersection.size(), via_eid,
-                                                 isThroughStreet(2, intersection), intersection[2]);
+                        getInstructionForObvious(intersection.size(),
+                                                 via_eid,
+                                                 isThroughStreet(2, intersection),
+                                                 intersection[2]);
                 }
             }
         }
@@ -472,8 +482,8 @@ Intersection MotorwayHandler::fromRamp(const EdgeID via_eid, Intersection inters
             }
             else if (detail::isMotorwayClass(edge_data.road_classification.road_class))
             {
-                road.turn.instruction = {TurnType::Merge, passed_highway_entry
-                                                              ? DirectionModifier::SlightRight
+                road.turn.instruction = {TurnType::Merge,
+                                         passed_highway_entry ? DirectionModifier::SlightRight
                                                               : DirectionModifier::SlightLeft};
             }
             else
@@ -516,16 +526,18 @@ Intersection MotorwayHandler::fallback(Intersection intersection) const
                 road.turn.instruction = {type, DirectionModifier::Straight};
             else
             {
-                road.turn.instruction = {type, road.turn.angle > STRAIGHT_ANGLE
-                                                   ? DirectionModifier::SlightLeft
-                                                   : DirectionModifier::SlightRight};
+                road.turn.instruction = {type,
+                                         road.turn.angle > STRAIGHT_ANGLE
+                                             ? DirectionModifier::SlightLeft
+                                             : DirectionModifier::SlightRight};
             }
         }
         else
         {
-            road.turn.instruction = {type, road.turn.angle < STRAIGHT_ANGLE
-                                               ? DirectionModifier::SlightLeft
-                                               : DirectionModifier::SlightRight};
+            road.turn.instruction = {type,
+                                     road.turn.angle < STRAIGHT_ANGLE
+                                         ? DirectionModifier::SlightLeft
+                                         : DirectionModifier::SlightRight};
         }
     }
     return intersection;
diff --git a/src/extractor/guidance/roundabout_handler.cpp b/src/extractor/guidance/roundabout_handler.cpp
index 603e27d..a79075d 100644
--- a/src/extractor/guidance/roundabout_handler.cpp
+++ b/src/extractor/guidance/roundabout_handler.cpp
@@ -1,5 +1,5 @@
-#include "extractor/guidance/constants.hpp"
 #include "extractor/guidance/roundabout_handler.hpp"
+#include "extractor/guidance/constants.hpp"
 #include "extractor/guidance/toolkit.hpp"
 
 #include "util/coordinate_calculation.hpp"
@@ -52,8 +52,11 @@ operator()(const NodeID from_nid, const EdgeID via_eid, Intersection intersectio
     const auto flags = getRoundaboutFlags(from_nid, via_eid, intersection);
     const auto roundabout_type = getRoundaboutType(node_based_graph.GetTarget(via_eid));
     // find the radius of the roundabout
-    return handleRoundabouts(roundabout_type, via_eid, flags.on_roundabout,
-                             flags.can_exit_separately, std::move(intersection));
+    return handleRoundabouts(roundabout_type,
+                             via_eid,
+                             flags.on_roundabout,
+                             flags.can_exit_separately,
+                             std::move(intersection));
 }
 
 detail::RoundaboutFlags RoundaboutHandler::getRoundaboutFlags(
@@ -169,9 +172,13 @@ bool RoundaboutHandler::qualifiesAsRoundaboutIntersection(
 
                 // there is a single non-roundabout edge
                 const auto src_coordinate = getCoordinate(node);
-                const auto next_coordinate = getRepresentativeCoordinate(
-                    node, node_based_graph.GetTarget(edge), edge, edge_data.reversed,
-                    compressed_edge_container, node_info_list);
+                const auto next_coordinate =
+                    getRepresentativeCoordinate(node,
+                                                node_based_graph.GetTarget(edge),
+                                                edge,
+                                                edge_data.reversed,
+                                                compressed_edge_container,
+                                                node_info_list);
                 result.push_back(
                     util::coordinate_calculation::bearing(src_coordinate, next_coordinate));
                 break;
@@ -206,40 +213,40 @@ RoundaboutType RoundaboutHandler::getRoundaboutType(const NodeID nid) const
     unsigned roundabout_name_id = 0;
     std::unordered_set<unsigned> connected_names;
 
-    const auto getNextOnRoundabout = [this, &roundabout_name_id,
-                                      &connected_names](const NodeID node) {
-        EdgeID continue_edge = SPECIAL_EDGEID;
-        for (const auto edge : node_based_graph.GetAdjacentEdgeRange(node))
-        {
-            const auto &edge_data = node_based_graph.GetEdgeData(edge);
-            if (!edge_data.reversed && edge_data.roundabout)
+    const auto getNextOnRoundabout =
+        [this, &roundabout_name_id, &connected_names](const NodeID node) {
+            EdgeID continue_edge = SPECIAL_EDGEID;
+            for (const auto edge : node_based_graph.GetAdjacentEdgeRange(node))
             {
-                if (SPECIAL_EDGEID != continue_edge)
+                const auto &edge_data = node_based_graph.GetEdgeData(edge);
+                if (!edge_data.reversed && edge_data.roundabout)
                 {
-                    // fork in roundabout
-                    return SPECIAL_EDGEID;
+                    if (SPECIAL_EDGEID != continue_edge)
+                    {
+                        // fork in roundabout
+                        return SPECIAL_EDGEID;
+                    }
+                    // roundabout does not keep its name
+                    if (roundabout_name_id != 0 && roundabout_name_id != edge_data.name_id &&
+                        requiresNameAnnounced(name_table.GetNameForID(roundabout_name_id),
+                                              name_table.GetNameForID(edge_data.name_id),
+                                              street_name_suffix_table))
+                    {
+                        return SPECIAL_EDGEID;
+                    }
+
+                    roundabout_name_id = edge_data.name_id;
+
+                    continue_edge = edge;
                 }
-                // roundabout does not keep its name
-                if (roundabout_name_id != 0 && roundabout_name_id != edge_data.name_id &&
-                    requiresNameAnnounced(name_table.GetNameForID(roundabout_name_id),
-                                          name_table.GetNameForID(edge_data.name_id),
-                                          street_name_suffix_table))
+                else if (!edge_data.roundabout)
                 {
-                    return SPECIAL_EDGEID;
+                    // remember all connected road names
+                    connected_names.insert(edge_data.name_id);
                 }
-
-                roundabout_name_id = edge_data.name_id;
-
-                continue_edge = edge;
-            }
-            else if (!edge_data.roundabout)
-            {
-                // remember all connected road names
-                connected_names.insert(edge_data.name_id);
             }
-        }
-        return continue_edge;
-    };
+            return continue_edge;
+        };
     // the roundabout radius has to be the same for all locations we look at it from
     // to guarantee this, we search the full roundabout for its vertices
     // and select the three smalles ids
diff --git a/src/extractor/guidance/turn_analysis.cpp b/src/extractor/guidance/turn_analysis.cpp
index a573194..c87d443 100644
--- a/src/extractor/guidance/turn_analysis.cpp
+++ b/src/extractor/guidance/turn_analysis.cpp
@@ -9,7 +9,9 @@
 #include <cstddef>
 #include <iomanip>
 #include <limits>
+#include <map>
 #include <set>
+#include <unordered_map>
 #include <unordered_set>
 
 using osrm::util::guidance::getTurnDirection;
@@ -40,9 +42,17 @@ TurnAnalysis::TurnAnalysis(const util::NodeBasedDynamicGraph &node_based_graph,
                                                                  barrier_nodes,
                                                                  node_info_list,
                                                                  compressed_edge_container),
-      roundabout_handler(node_based_graph, node_info_list, compressed_edge_container, name_table, street_name_suffix_table),
-      motorway_handler(node_based_graph, node_info_list, name_table,street_name_suffix_table),
-      turn_handler(node_based_graph, node_info_list, name_table,street_name_suffix_table)
+      roundabout_handler(node_based_graph,
+                         node_info_list,
+                         compressed_edge_container,
+                         name_table,
+                         street_name_suffix_table),
+      motorway_handler(node_based_graph,
+                       node_info_list,
+                       name_table,
+                       street_name_suffix_table,
+                       intersection_generator),
+      turn_handler(node_based_graph, node_info_list, name_table, street_name_suffix_table)
 {
 }
 
@@ -71,6 +81,9 @@ std::vector<TurnOperation> TurnAnalysis::getTurns(const NodeID from_nid, const E
         }
     }
 
+    // Handle sliproads
+    intersection = handleSliproads(via_eid, std::move(intersection));
+
     std::vector<TurnOperation> turns;
     for (auto road : intersection)
         if (road.entry_allowed)
@@ -79,6 +92,11 @@ std::vector<TurnOperation> TurnAnalysis::getTurns(const NodeID from_nid, const E
     return turns;
 }
 
+Intersection TurnAnalysis::getIntersection(const NodeID from_nid, const EdgeID via_eid) const
+{
+    return intersection_generator(from_nid, via_eid);
+}
+
 // Sets basic turn types as fallback for otherwise unhandled turns
 Intersection
 TurnAnalysis::setTurnTypes(const NodeID from_nid, const EdgeID, Intersection intersection) const
@@ -91,10 +109,120 @@ TurnAnalysis::setTurnTypes(const NodeID from_nid, const EdgeID, Intersection int
         const EdgeID onto_edge = road.turn.eid;
         const NodeID to_nid = node_based_graph.GetTarget(onto_edge);
 
-        road.turn.instruction = {TurnType::Turn, (from_nid == to_nid)
-                                                     ? DirectionModifier::UTurn
-                                                     : getTurnDirection(road.turn.angle)};
+        road.turn.instruction = {TurnType::Turn,
+                                 (from_nid == to_nid) ? DirectionModifier::UTurn
+                                                      : getTurnDirection(road.turn.angle)};
+    }
+    return intersection;
+}
+
+// "Sliproads" occur when we've got a link between two roads (MOTORWAY_LINK, etc), but
+// the two roads are *also* directly connected shortly afterwards.
+// In these cases, we tag the turn-type as "sliproad", and then in post-processing
+// we emit a "turn", instead of "take the ramp"+"merge"
+Intersection TurnAnalysis::handleSliproads(const EdgeID source_edge_id,
+                                           Intersection intersection) const
+{
+
+    auto intersection_node_id = node_based_graph.GetTarget(source_edge_id);
+
+    const auto linkTest = [this](const ConnectedRoad &road) {
+        return // isLinkClass(
+            //    node_based_graph.GetEdgeData(road.turn.eid).road_classification.road_class) &&
+            !node_based_graph.GetEdgeData(road.turn.eid).roundabout && road.entry_allowed &&
+            angularDeviation(road.turn.angle, STRAIGHT_ANGLE) <= 2 * NARROW_TURN_ANGLE;
+    };
+
+    bool hasNarrow =
+        std::find_if(intersection.begin(), intersection.end(), linkTest) != intersection.end();
+    if (!hasNarrow)
+        return intersection;
+
+    const auto source_edge_data = node_based_graph.GetEdgeData(source_edge_id);
+
+    // Find the continuation of the intersection we're on
+    auto next_road = std::find_if(
+        intersection.begin(),
+        intersection.end(),
+        [this, source_edge_data](const ConnectedRoad &road) {
+            const auto road_edge_data = node_based_graph.GetEdgeData(road.turn.eid);
+            // Test to see if the source edge and the one we're looking at are the same road
+            return road_edge_data.road_classification.road_class ==
+                       source_edge_data.road_classification.road_class &&
+                   road_edge_data.name_id != EMPTY_NAMEID &&
+                   road_edge_data.name_id == source_edge_data.name_id && road.entry_allowed &&
+                   angularDeviation(road.turn.angle, STRAIGHT_ANGLE) < FUZZY_ANGLE_DIFFERENCE;
+        });
+
+    const bool hasNext = next_road != intersection.end();
+
+    if (!hasNext)
+    {
+        return intersection;
     }
+
+    // Threshold check, if the intersection is too far away, don't bother continuing
+    const auto &next_road_data = node_based_graph.GetEdgeData(next_road->turn.eid);
+    if (next_road_data.distance > MAX_SLIPROAD_THRESHOLD)
+    {
+        return intersection;
+    }
+
+    const auto next_road_next_intersection =
+        intersection_generator(intersection_node_id, next_road->turn.eid);
+
+    const auto next_intersection_node = node_based_graph.GetTarget(next_road->turn.eid);
+
+    std::unordered_set<NameID> target_road_names;
+
+    for (const auto &road : next_road_next_intersection)
+    {
+        const auto &target_data = node_based_graph.GetEdgeData(road.turn.eid);
+        target_road_names.insert(target_data.name_id);
+    }
+
+    for (auto &road : intersection)
+    {
+        if (linkTest(road))
+        {
+            auto target_intersection = intersection_generator(intersection_node_id, road.turn.eid);
+            for (const auto &candidate_road : target_intersection)
+            {
+                const auto &candidate_data = node_based_graph.GetEdgeData(candidate_road.turn.eid);
+                if (target_road_names.count(candidate_data.name_id) > 0 &&
+                    node_based_graph.GetTarget(candidate_road.turn.eid) == next_intersection_node)
+                {
+                    road.turn.instruction.type = TurnType::Sliproad;
+                    break;
+                }
+            }
+        }
+    }
+
+    if (next_road->turn.instruction.type == TurnType::Fork)
+    {
+        const auto &next_data = node_based_graph.GetEdgeData(next_road->turn.eid);
+        if (next_data.name_id == source_edge_data.name_id)
+        {
+            if (angularDeviation(next_road->turn.angle, STRAIGHT_ANGLE) < 5)
+                next_road->turn.instruction.type = TurnType::Suppressed;
+            else
+                next_road->turn.instruction.type = TurnType::Continue;
+            next_road->turn.instruction.direction_modifier =
+                getTurnDirection(next_road->turn.angle);
+        }
+        else if (next_data.name_id != EMPTY_NAMEID)
+        {
+            next_road->turn.instruction.type = TurnType::NewName;
+            next_road->turn.instruction.direction_modifier =
+                getTurnDirection(next_road->turn.angle);
+        }
+        else
+        {
+            next_road->turn.instruction.type = TurnType::Suppressed;
+        }
+    }
+
     return intersection;
 }
 
diff --git a/src/extractor/guidance/turn_classification.cpp b/src/extractor/guidance/turn_classification.cpp
new file mode 100644
index 0000000..45ec22c
--- /dev/null
+++ b/src/extractor/guidance/turn_classification.cpp
@@ -0,0 +1,116 @@
+#include "extractor/guidance/turn_classification.hpp"
+
+#include "util/simple_logger.hpp"
+
+#include <cstddef>
+#include <cstdint>
+#include <iomanip>
+
+namespace osrm
+{
+namespace extractor
+{
+namespace guidance
+{
+
+struct TurnPossibility
+{
+    TurnPossibility(bool entry_allowed, double bearing)
+        : entry_allowed(entry_allowed), bearing(std::move(bearing))
+    {
+    }
+
+    TurnPossibility() : entry_allowed(false), bearing(0) {}
+
+    bool entry_allowed;
+    double bearing;
+};
+
+std::pair<util::guidance::EntryClass, util::guidance::BearingClass>
+classifyIntersection(NodeID nid,
+                     const Intersection &intersection,
+                     const util::NodeBasedDynamicGraph &node_based_graph,
+                     const extractor::CompressedEdgeContainer &compressed_geometries,
+                     const std::vector<extractor::QueryNode> &query_nodes)
+{
+    if (intersection.empty())
+        return {};
+
+    std::vector<TurnPossibility> turns;
+
+    const auto node_coordinate = util::Coordinate(query_nodes[nid].lon, query_nodes[nid].lat);
+
+    // generate a list of all turn angles between a base edge, the node and a current edge
+    for (const auto &road : intersection)
+    {
+        const auto eid = road.turn.eid;
+        const auto edge_coordinate = getRepresentativeCoordinate(
+            nid, node_based_graph.GetTarget(eid), eid, false, compressed_geometries, query_nodes);
+
+        const double bearing =
+            util::coordinate_calculation::bearing(node_coordinate, edge_coordinate);
+        turns.push_back({road.entry_allowed, bearing});
+    }
+
+    std::sort(
+        turns.begin(), turns.end(), [](const TurnPossibility left, const TurnPossibility right) {
+            return left.bearing < right.bearing;
+        });
+
+    util::guidance::EntryClass entry_class;
+    util::guidance::BearingClass bearing_class;
+
+    const bool canBeDiscretized = [&]() {
+        if (turns.size() <= 1)
+            return true;
+
+        DiscreteBearing last_discrete_bearing =
+            util::guidance::BearingClass::getDiscreteBearing(std::round(turns.back().bearing));
+        for (const auto turn : turns)
+        {
+            const DiscreteBearing discrete_bearing =
+                util::guidance::BearingClass::getDiscreteBearing(std::round(turn.bearing));
+            if (discrete_bearing == last_discrete_bearing)
+                return false;
+            last_discrete_bearing = discrete_bearing;
+        }
+        return true;
+    }();
+
+    // finally transfer data to the entry/bearing classes
+    std::size_t number = 0;
+    if (canBeDiscretized)
+    {
+        if (util::guidance::BearingClass::getDiscreteBearing(turns.back().bearing) <
+            util::guidance::BearingClass::getDiscreteBearing(turns.front().bearing))
+        {
+            turns.insert(turns.begin(), turns.back());
+            turns.pop_back();
+        }
+        for (const auto turn : turns)
+        {
+            if (turn.entry_allowed)
+                entry_class.activate(number);
+            auto discrete_bearing_class =
+                util::guidance::BearingClass::getDiscreteBearing(std::round(turn.bearing));
+            bearing_class.add(std::round(discrete_bearing_class *
+                                         util::guidance::BearingClass::discrete_step_size));
+            ++number;
+        }
+    }
+    else
+    {
+        for (const auto turn : turns)
+        {
+            if (turn.entry_allowed)
+                entry_class.activate(number);
+            bearing_class.add(std::round(turn.bearing));
+            ++number;
+        }
+    }
+    return std::make_pair(entry_class, bearing_class);
+}
+
+} // namespace guidance
+} // namespace extractor
+} // namespace osrm
diff --git a/src/extractor/guidance/turn_handler.cpp b/src/extractor/guidance/turn_handler.cpp
index b6d8987..ccb6559 100644
--- a/src/extractor/guidance/turn_handler.cpp
+++ b/src/extractor/guidance/turn_handler.cpp
@@ -3,7 +3,6 @@
 #include "extractor/guidance/toolkit.hpp"
 #include "extractor/guidance/turn_handler.hpp"
 
-#include "util/simple_logger.hpp"
 #include "util/guidance/toolkit.hpp"
 
 #include <limits>
@@ -70,14 +69,14 @@ Intersection TurnHandler::handleTwoWayTurn(const EdgeID via_edge, Intersection i
     intersection[1].turn.instruction =
         getInstructionForObvious(intersection.size(), via_edge, false, intersection[1]);
 
-    if (intersection[1].turn.instruction.type == TurnType::Suppressed)
-        intersection[1].turn.instruction.type = TurnType::NoTurn;
-
     return intersection;
 }
 
 Intersection TurnHandler::handleThreeWayTurn(const EdgeID via_edge, Intersection intersection) const
 {
+    const auto &in_data = node_based_graph.GetEdgeData(via_edge);
+    const auto &first_data = node_based_graph.GetEdgeData(intersection[1].turn.eid);
+    const auto &second_data = node_based_graph.GetEdgeData(intersection[2].turn.eid);
     BOOST_ASSERT(intersection[0].turn.angle < 0.001);
     const auto isObviousOfTwo = [this](const ConnectedRoad road, const ConnectedRoad other) {
         const auto first_class =
@@ -110,8 +109,12 @@ Intersection TurnHandler::handleThreeWayTurn(const EdgeID via_edge, Intersection
 
         const bool is_much_narrower_than_other =
             angularDeviation(other.turn.angle, STRAIGHT_ANGLE) /
-                angularDeviation(road.turn.angle, STRAIGHT_ANGLE) >
-            INCREASES_BY_FOURTY_PERCENT;
+                    angularDeviation(road.turn.angle, STRAIGHT_ANGLE) >
+                INCREASES_BY_FOURTY_PERCENT &&
+            angularDeviation(angularDeviation(other.turn.angle, STRAIGHT_ANGLE),
+                             angularDeviation(road.turn.angle, STRAIGHT_ANGLE)) >
+                FUZZY_ANGLE_DIFFERENCE;
+
         return is_much_narrower_than_other;
     };
 
@@ -130,24 +133,28 @@ Intersection TurnHandler::handleThreeWayTurn(const EdgeID via_edge, Intersection
                                         .road_classification.road_class;
             const auto right_class = node_based_graph.GetEdgeData(intersection[1].turn.eid)
                                          .road_classification.road_class;
-            if (canBeSeenAsFork(left_class, right_class))
-            {
-                assignFork(via_edge, intersection[2], intersection[1]);
-            }
-            else if (isObviousOfTwo(intersection[1], intersection[2]))
+            if (isObviousOfTwo(intersection[1], intersection[2]) &&
+                (second_data.name_id != in_data.name_id ||
+                 first_data.name_id == second_data.name_id))
             {
                 intersection[1].turn.instruction =
                     getInstructionForObvious(intersection.size(), via_edge, false, intersection[1]);
                 intersection[2].turn.instruction = {findBasicTurnType(via_edge, intersection[2]),
                                                     DirectionModifier::SlightLeft};
             }
-            else if (isObviousOfTwo(intersection[2], intersection[1]))
+            else if (isObviousOfTwo(intersection[2], intersection[1]) &&
+                     (first_data.name_id != in_data.name_id ||
+                      first_data.name_id == second_data.name_id))
             {
                 intersection[2].turn.instruction =
                     getInstructionForObvious(intersection.size(), via_edge, false, intersection[2]);
                 intersection[1].turn.instruction = {findBasicTurnType(via_edge, intersection[1]),
                                                     DirectionModifier::SlightRight};
             }
+            else if (canBeSeenAsFork(left_class, right_class))
+            {
+                assignFork(via_edge, intersection[2], intersection[1]);
+            }
             else
             {
                 intersection[1].turn.instruction = {findBasicTurnType(via_edge, intersection[1]),
@@ -193,7 +200,8 @@ Intersection TurnHandler::handleThreeWayTurn(const EdgeID via_edge, Intersection
     }
     else
     {
-        if (isObviousOfTwo(intersection[1], intersection[2]))
+        if (isObviousOfTwo(intersection[1], intersection[2]) &&
+            (in_data.name_id != second_data.name_id || first_data.name_id == second_data.name_id))
         {
             intersection[1].turn.instruction = getInstructionForObvious(
                 3, via_edge, isThroughStreet(1, intersection), intersection[1]);
@@ -204,7 +212,8 @@ Intersection TurnHandler::handleThreeWayTurn(const EdgeID via_edge, Intersection
                                                 getTurnDirection(intersection[1].turn.angle)};
         }
 
-        if (isObviousOfTwo(intersection[2], intersection[1]))
+        if (isObviousOfTwo(intersection[2], intersection[1]) &&
+            (in_data.name_id != first_data.name_id || first_data.name_id == second_data.name_id))
         {
             intersection[2].turn.instruction = getInstructionForObvious(
                 3, via_edge, isThroughStreet(2, intersection), intersection[2]);
@@ -234,12 +243,46 @@ Intersection TurnHandler::handleComplexTurn(const EdgeID via_edge, Intersection
         }
     }
 
+    // check whether there is a turn of the same name
+    const auto &in_data = node_based_graph.GetEdgeData(via_edge);
+
+    const bool has_same_name_turn = [&]() {
+        for (std::size_t i = 1; i < intersection.size(); ++i)
+        {
+            if (node_based_graph.GetEdgeData(intersection[i].turn.eid).name_id == in_data.name_id)
+                return true;
+        }
+        return false;
+    }();
+
     // check whether the obvious choice is actually a through street
     if (obvious_index != 0)
     {
-        intersection[obvious_index].turn.instruction = getInstructionForObvious(
-            intersection.size(), via_edge, isThroughStreet(obvious_index, intersection),
-            intersection[obvious_index]);
+        intersection[obvious_index].turn.instruction =
+            getInstructionForObvious(intersection.size(),
+                                     via_edge,
+                                     isThroughStreet(obvious_index, intersection),
+                                     intersection[obvious_index]);
+        if (has_same_name_turn &&
+            node_based_graph.GetEdgeData(intersection[obvious_index].turn.eid).name_id !=
+                in_data.name_id &&
+            intersection[obvious_index].turn.instruction.type == TurnType::NewName)
+        {
+            // this is a special case that is necessary to correctly handle obvious turns on
+            // continuing streets. Right now osrm does not know about right of way. If a street
+            // turns to the left just like:
+            //
+            //       a
+            //       a
+            // aaaaaaa b b
+            //
+            // And another road exits here, we don't want to call it a new name, even though the
+            // turn is obvious and does not require steering. To correctly handle these situations
+            // in turn collapsing, we use the turn + straight combination here
+            intersection[obvious_index].turn.instruction.type = TurnType::Turn;
+            intersection[obvious_index].turn.instruction.direction_modifier =
+                DirectionModifier::Straight;
+        }
 
         // assign left/right turns
         intersection = assignLeftTurns(via_edge, std::move(intersection), obvious_index + 1);
@@ -274,8 +317,10 @@ Intersection TurnHandler::handleComplexTurn(const EdgeID via_edge, Intersection
         }
         else if (fork_range.second - fork_range.first == 2)
         {
-            assignFork(via_edge, intersection[fork_range.second],
-                       intersection[fork_range.first + 1], intersection[fork_range.first]);
+            assignFork(via_edge,
+                       intersection[fork_range.second],
+                       intersection[fork_range.first + 1],
+                       intersection[fork_range.first]);
         }
         // assign left/right turns
         intersection = assignLeftTurns(via_edge, std::move(intersection), fork_range.second + 1);
@@ -302,7 +347,7 @@ Intersection TurnHandler::handleComplexTurn(const EdgeID via_edge, Intersection
     }
     else
     {
-        assignTrivialTurns(via_edge,intersection,1,intersection.size());
+        assignTrivialTurns(via_edge, intersection, 1, intersection.size());
     }
     return intersection;
 }
@@ -336,8 +381,12 @@ std::size_t TurnHandler::findObviousTurn(const EdgeID via_edge,
         }
 
         const auto out_data = node_based_graph.GetEdgeData(intersection[i].turn.eid);
+        auto continue_class = node_based_graph.GetEdgeData(intersection[best_continue].turn.eid)
+                                  .road_classification.road_class;
         if (intersection[i].entry_allowed && out_data.name_id == in_data.name_id &&
-            deviation < best_continue_deviation)
+            (best_continue == 0 || continue_class > out_data.road_classification.road_class ||
+             (deviation < best_continue_deviation &&
+              out_data.road_classification.road_class == continue_class)))
         {
             best_continue_deviation = deviation;
             best_continue = i;
@@ -351,7 +400,12 @@ std::size_t TurnHandler::findObviousTurn(const EdgeID via_edge,
         return 0;
 
     // has no obvious continued road
-    if (best_continue == 0 || true)
+    if (best_continue == 0 || best_continue_deviation >= 2 * NARROW_TURN_ANGLE ||
+        (node_based_graph.GetEdgeData(intersection[best_continue].turn.eid)
+                 .road_classification.road_class ==
+             node_based_graph.GetEdgeData(intersection[best].turn.eid)
+                 .road_classification.road_class &&
+         std::abs(best_continue_deviation) > 1 && best_deviation / best_continue_deviation < 0.75))
     {
         // Find left/right deviation
         const double left_deviation = angularDeviation(
@@ -381,8 +435,31 @@ std::size_t TurnHandler::findObviousTurn(const EdgeID via_edge,
             return best;
         }
     }
+    else
+    {
+        const double deviation =
+            angularDeviation(intersection[best_continue].turn.angle, STRAIGHT_ANGLE);
+        const auto &continue_data =
+            node_based_graph.GetEdgeData(intersection[best_continue].turn.eid);
+        if (std::abs(deviation) < 1)
+            return best_continue;
+
+        // check if any other similar best continues exist
+        for (std::size_t i = 1; i < intersection.size(); ++i)
+        {
+            if (i == best_continue || !intersection[i].entry_allowed)
+                continue;
+
+            if (angularDeviation(intersection[i].turn.angle, STRAIGHT_ANGLE) / deviation < 1.1 &&
+                continue_data.road_classification.road_class ==
+                    node_based_graph.GetEdgeData(intersection[i].turn.eid)
+                        .road_classification.road_class)
+                return 0;
+        }
+        return best_continue; // no obvious turn
+    }
 
-    return 0; // no obvious turn
+    return 0;
 }
 
 // Assignment of left turns hands of to right turns.
@@ -394,8 +471,7 @@ Intersection TurnHandler::assignLeftTurns(const EdgeID via_edge,
                                           const std::size_t starting_at) const
 {
     BOOST_ASSERT(starting_at <= intersection.size());
-    const auto switch_left_and_right = []( Intersection &intersection )
-    {
+    const auto switch_left_and_right = [](Intersection &intersection) {
         BOOST_ASSERT(!intersection.empty());
 
         for (auto &road : intersection)
@@ -410,7 +486,6 @@ Intersection TurnHandler::assignLeftTurns(const EdgeID via_edge,
     intersection = assignRightTurns(via_edge, std::move(intersection), count);
     switch_left_and_right(intersection);
 
-
     return intersection;
 }
 
@@ -537,18 +612,6 @@ Intersection TurnHandler::assignRightTurns(const EdgeID via_edge,
         }
         else
         {
-            util::SimpleLogger().Write(logWARNING)
-                << "Reached fallback for right turns, size 3 "
-                << " Valids: " << (intersection[1].entry_allowed + intersection[2].entry_allowed +
-                                   intersection[3].entry_allowed);
-            for (const auto road : intersection)
-            {
-                const auto &out_data = node_based_graph.GetEdgeData(road.turn.eid);
-                util::SimpleLogger().Write(logWARNING)
-                    << "\troad: " << toString(road) << " Name: " << out_data.name_id
-                    << " Road Class: " << (int)out_data.road_classification.road_class;
-            }
-
             assignTrivialTurns(via_edge, intersection, 1, up_to);
         }
     }
diff --git a/src/extractor/restriction_map.cpp b/src/extractor/restriction_map.cpp
index d41b1c4..8cba4b6 100644
--- a/src/extractor/restriction_map.cpp
+++ b/src/extractor/restriction_map.cpp
@@ -48,8 +48,8 @@ RestrictionMap::RestrictionMap(const std::vector<TurnRestriction> &restriction_l
         }
         ++m_count;
         BOOST_ASSERT(restriction.to.node < std::numeric_limits<NodeID>::max());
-        m_restriction_bucket_list.at(index)
-            .emplace_back(restriction.to.node, restriction.flags.is_only);
+        m_restriction_bucket_list.at(index).emplace_back(restriction.to.node,
+                                                         restriction.flags.is_only);
     }
 }
 
diff --git a/src/extractor/restriction_parser.cpp b/src/extractor/restriction_parser.cpp
index 66cd259..a6cc0e7 100644
--- a/src/extractor/restriction_parser.cpp
+++ b/src/extractor/restriction_parser.cpp
@@ -2,13 +2,13 @@
 #include "extractor/profile_properties.hpp"
 
 #include "extractor/external_memory_node.hpp"
-#include "util/lua_util.hpp"
 #include "util/exception.hpp"
+#include "util/lua_util.hpp"
 #include "util/simple_logger.hpp"
 
 #include <boost/algorithm/string.hpp>
-#include <boost/algorithm/string/regex.hpp>
 #include <boost/algorithm/string/predicate.hpp>
+#include <boost/algorithm/string/regex.hpp>
 #include <boost/optional/optional.hpp>
 #include <boost/ref.hpp>
 #include <boost/regex.hpp>
@@ -48,8 +48,8 @@ void RestrictionParser::ReadRestrictionExceptions(lua_State *lua_state)
     {
         luabind::set_pcall_callback(&luaErrorCallback);
         // get list of turn restriction exceptions
-        luabind::call_function<void>(lua_state, "get_exceptions",
-                                     boost::ref(restriction_exceptions));
+        luabind::call_function<void>(
+            lua_state, "get_exceptions", boost::ref(restriction_exceptions));
         const unsigned exception_count = restriction_exceptions.size();
         util::SimpleLogger().Write() << "Found " << exception_count
                                      << " exceptions to turn restrictions:";
@@ -117,8 +117,7 @@ RestrictionParser::TryParse(const osmium::Relation &relation) const
         // "restriction:<transportation_type>")
         if (key.size() > 11)
         {
-            const auto ex_suffix = [&](const std::string &exception)
-            {
+            const auto ex_suffix = [&](const std::string &exception) {
                 return boost::algorithm::ends_with(key, exception);
             };
             bool is_actually_restricted =
@@ -201,13 +200,12 @@ bool RestrictionParser::ShouldIgnoreRestriction(const std::string &except_tag_st
     std::vector<std::string> exceptions;
     boost::algorithm::split_regex(exceptions, except_tag_string, boost::regex("[;][ ]*"));
 
-    return std::any_of(std::begin(exceptions), std::end(exceptions),
-                       [&](const std::string &current_string)
-                       {
-                           return std::end(restriction_exceptions) !=
-                                  std::find(std::begin(restriction_exceptions),
-                                            std::end(restriction_exceptions), current_string);
-                       });
+    return std::any_of(
+        std::begin(exceptions), std::end(exceptions), [&](const std::string &current_string) {
+            return std::end(restriction_exceptions) != std::find(std::begin(restriction_exceptions),
+                                                                 std::end(restriction_exceptions),
+                                                                 current_string);
+        });
 }
 }
 }
diff --git a/src/extractor/scripting_environment.cpp b/src/extractor/scripting_environment.cpp
index 3ffe016..cfdbb57 100644
--- a/src/extractor/scripting_environment.cpp
+++ b/src/extractor/scripting_environment.cpp
@@ -1,21 +1,21 @@
 #include "extractor/scripting_environment.hpp"
 
+#include "extractor/external_memory_node.hpp"
 #include "extractor/extraction_helper_functions.hpp"
 #include "extractor/extraction_node.hpp"
 #include "extractor/extraction_way.hpp"
 #include "extractor/internal_extractor_edge.hpp"
-#include "extractor/external_memory_node.hpp"
-#include "extractor/raster_source.hpp"
 #include "extractor/profile_properties.hpp"
+#include "extractor/raster_source.hpp"
+#include "util/exception.hpp"
 #include "util/lua_util.hpp"
 #include "util/make_unique.hpp"
-#include "util/exception.hpp"
 #include "util/simple_logger.hpp"
 #include "util/typedefs.hpp"
 
-#include <luabind/tag_function.hpp>
 #include <luabind/iterator_policy.hpp>
 #include <luabind/operator.hpp>
+#include <luabind/tag_function.hpp>
 
 #include <osmium/osm.hpp>
 
@@ -44,10 +44,9 @@ template <class T> double lonToDouble(T const &object)
     return static_cast<double>(util::toFloating(object.lon));
 }
 
-// Luabind does not like memr funs: instead of casting to the function's signature (mem fun ptr) we simply wrap it
-auto get_nodes_for_way(const osmium::Way& way) -> decltype(way.nodes()) {
-  return way.nodes();
-}
+// Luabind does not like memr funs: instead of casting to the function's signature (mem fun ptr) we
+// simply wrap it
+auto get_nodes_for_way(const osmium::Way &way) -> decltype(way.nodes()) { return way.nodes(); }
 
 // Error handler
 int luaErrorCallback(lua_State *state)
@@ -78,19 +77,19 @@ void ScriptingEnvironment::InitContext(ScriptingEnvironment::Context &context)
     luabind::module(context.state)
         [luabind::def("durationIsValid", durationIsValid),
          luabind::def("parseDuration", parseDuration),
-         luabind::class_<TravelMode>("mode")
-             .enum_("enums")[luabind::value("inaccessible", TRAVEL_MODE_INACCESSIBLE),
-                             luabind::value("driving", TRAVEL_MODE_DRIVING),
-                             luabind::value("cycling", TRAVEL_MODE_CYCLING),
-                             luabind::value("walking", TRAVEL_MODE_WALKING),
-                             luabind::value("ferry", TRAVEL_MODE_FERRY),
-                             luabind::value("train", TRAVEL_MODE_TRAIN),
-                             luabind::value("pushing_bike", TRAVEL_MODE_PUSHING_BIKE),
-                             luabind::value("steps_up", TRAVEL_MODE_STEPS_UP),
-                             luabind::value("steps_down", TRAVEL_MODE_STEPS_DOWN),
-                             luabind::value("river_up", TRAVEL_MODE_RIVER_UP),
-                             luabind::value("river_down", TRAVEL_MODE_RIVER_DOWN),
-                             luabind::value("route", TRAVEL_MODE_ROUTE)],
+         luabind::class_<TravelMode>("mode").enum_(
+             "enums")[luabind::value("inaccessible", TRAVEL_MODE_INACCESSIBLE),
+                      luabind::value("driving", TRAVEL_MODE_DRIVING),
+                      luabind::value("cycling", TRAVEL_MODE_CYCLING),
+                      luabind::value("walking", TRAVEL_MODE_WALKING),
+                      luabind::value("ferry", TRAVEL_MODE_FERRY),
+                      luabind::value("train", TRAVEL_MODE_TRAIN),
+                      luabind::value("pushing_bike", TRAVEL_MODE_PUSHING_BIKE),
+                      luabind::value("steps_up", TRAVEL_MODE_STEPS_UP),
+                      luabind::value("steps_down", TRAVEL_MODE_STEPS_DOWN),
+                      luabind::value("river_up", TRAVEL_MODE_RIVER_UP),
+                      luabind::value("river_down", TRAVEL_MODE_RIVER_DOWN),
+                      luabind::value("route", TRAVEL_MODE_ROUTE)],
          luabind::class_<SourceContainer>("sources")
              .def(luabind::constructor<>())
              .def("load", &SourceContainer::LoadRasterSource)
@@ -101,16 +100,20 @@ void ScriptingEnvironment::InitContext(ScriptingEnvironment::Context &context)
 
          luabind::class_<ProfileProperties>("ProfileProperties")
              .def(luabind::constructor<>())
-             .property("traffic_signal_penalty", &ProfileProperties::GetTrafficSignalPenalty,
+             .property("traffic_signal_penalty",
+                       &ProfileProperties::GetTrafficSignalPenalty,
                        &ProfileProperties::SetTrafficSignalPenalty)
-             .property("u_turn_penalty", &ProfileProperties::GetUturnPenalty,
+             .property("u_turn_penalty",
+                       &ProfileProperties::GetUturnPenalty,
                        &ProfileProperties::SetUturnPenalty)
              .def_readwrite("use_turn_restrictions", &ProfileProperties::use_turn_restrictions)
-             .def_readwrite("continue_straight_at_waypoint", &ProfileProperties::continue_straight_at_waypoint),
+             .def_readwrite("continue_straight_at_waypoint",
+                            &ProfileProperties::continue_straight_at_waypoint),
 
-         luabind::class_<std::vector<std::string>>("vector")
-             .def("Add", static_cast<void (std::vector<std::string>::*)(const std::string &)>(
-                             &std::vector<std::string>::push_back)),
+         luabind::class_<std::vector<std::string>>("vector").def(
+             "Add",
+             static_cast<void (std::vector<std::string>::*)(const std::string &)>(
+                 &std::vector<std::string>::push_back)),
 
          luabind::class_<osmium::Location>("Location")
              .def<location_member_ptr_type>("lat", &osmium::Location::lat)
@@ -132,22 +135,25 @@ void ScriptingEnvironment::InitContext(ScriptingEnvironment::Context &context)
              .def_readwrite("forward_speed", &ExtractionWay::forward_speed)
              .def_readwrite("backward_speed", &ExtractionWay::backward_speed)
              .def_readwrite("name", &ExtractionWay::name)
+             .def_readwrite("pronunciation", &ExtractionWay::pronunciation)
+             .def_readwrite("destinations", &ExtractionWay::destinations)
              .def_readwrite("roundabout", &ExtractionWay::roundabout)
              .def_readwrite("is_access_restricted", &ExtractionWay::is_access_restricted)
              .def_readwrite("is_startpoint", &ExtractionWay::is_startpoint)
              .def_readwrite("duration", &ExtractionWay::duration)
-             .property("forward_mode", &ExtractionWay::get_forward_mode,
-                       &ExtractionWay::set_forward_mode)
-             .property("backward_mode", &ExtractionWay::get_backward_mode,
+             .property(
+                 "forward_mode", &ExtractionWay::get_forward_mode, &ExtractionWay::set_forward_mode)
+             .property("backward_mode",
+                       &ExtractionWay::get_backward_mode,
                        &ExtractionWay::set_backward_mode),
-         luabind::class_<osmium::WayNodeList>("WayNodeList")
-           .def(luabind::constructor<>()),
+         luabind::class_<osmium::WayNodeList>("WayNodeList").def(luabind::constructor<>()),
          luabind::class_<osmium::NodeRef>("NodeRef")
-           .def(luabind::constructor<>())
-           // Dear ambitious reader: registering .location() as in:
-           // .def("location", +[](const osmium::NodeRef& nref){ return nref.location(); })
-           // will crash at runtime, since we're not (yet?) using libosnmium's NodeLocationsForWays cache
-           .def("id", &osmium::NodeRef::ref),
+             .def(luabind::constructor<>())
+             // Dear ambitious reader: registering .location() as in:
+             // .def("location", +[](const osmium::NodeRef& nref){ return nref.location(); })
+             // will crash at runtime, since we're not (yet?) using libosnmium's
+             // NodeLocationsForWays cache
+             .def("id", &osmium::NodeRef::ref),
          luabind::class_<osmium::Way>("Way")
              .def("get_value_by_key", &osmium::Way::get_value_by_key)
              .def("get_value_by_key", &get_value_by_key<osmium::Way>)
diff --git a/src/extractor/suffix_table.cpp b/src/extractor/suffix_table.cpp
index 3c4bc0f..10d54bb 100644
--- a/src/extractor/suffix_table.cpp
+++ b/src/extractor/suffix_table.cpp
@@ -5,7 +5,6 @@
 
 #include <boost/algorithm/string.hpp>
 #include <boost/assert.hpp>
-#include <boost/range/adaptor/transformed.hpp>
 #include <boost/ref.hpp>
 
 #include <iterator>
@@ -26,8 +25,8 @@ SuffixTable::SuffixTable(lua_State *lua_state)
     try
     {
         // call lua profile to compute turn penalty
-        luabind::call_function<void>(lua_state, "get_name_suffix_list",
-                                     boost::ref(suffixes_vector));
+        luabind::call_function<void>(
+            lua_state, "get_name_suffix_list", boost::ref(suffixes_vector));
     }
     catch (const luabind::error &er)
     {
diff --git a/src/osrm/osrm.cpp b/src/osrm/osrm.cpp
index 9a69e2e..f9add44 100644
--- a/src/osrm/osrm.cpp
+++ b/src/osrm/osrm.cpp
@@ -1,12 +1,12 @@
 #include "osrm/osrm.hpp"
+#include "engine/api/match_parameters.hpp"
+#include "engine/api/nearest_parameters.hpp"
 #include "engine/api/route_parameters.hpp"
 #include "engine/api/table_parameters.hpp"
-#include "engine/api/nearest_parameters.hpp"
 #include "engine/api/trip_parameters.hpp"
-#include "engine/api/match_parameters.hpp"
 #include "engine/engine.hpp"
-#include "engine/status.hpp"
 #include "engine/engine_config.hpp"
+#include "engine/status.hpp"
 #include "util/make_unique.hpp"
 
 namespace osrm
diff --git a/src/server/api/parameters_parser.cpp b/src/server/api/parameters_parser.cpp
index d4a530f..e4b201e 100644
--- a/src/server/api/parameters_parser.cpp
+++ b/src/server/api/parameters_parser.cpp
@@ -19,15 +19,21 @@ namespace api
 namespace detail
 {
 template <typename T>
-using is_grammar_t = std::integral_constant<bool, std::is_same<RouteParametersGrammar<>, T>::value ||
-   std::is_same<TableParametersGrammar<>, T>::value || std::is_same<NearestParametersGrammar<>, T>::value ||
-   std::is_same<TripParametersGrammar<>, T>::value || std::is_same<MatchParametersGrammar<>, T>::value ||
-   std::is_same<TileParametersGrammar<>, T>::value>;
-
-template <typename ParameterT, typename GrammarT,
+using is_grammar_t =
+    std::integral_constant<bool,
+                           std::is_same<RouteParametersGrammar<>, T>::value ||
+                               std::is_same<TableParametersGrammar<>, T>::value ||
+                               std::is_same<NearestParametersGrammar<>, T>::value ||
+                               std::is_same<TripParametersGrammar<>, T>::value ||
+                               std::is_same<MatchParametersGrammar<>, T>::value ||
+                               std::is_same<TileParametersGrammar<>, T>::value>;
+
+template <typename ParameterT,
+          typename GrammarT,
           typename std::enable_if<detail::is_parameter_t<ParameterT>::value, int>::type = 0,
           typename std::enable_if<detail::is_grammar_t<GrammarT>::value, int>::type = 0>
-boost::optional<ParameterT> parseParameters(std::string::iterator &iter, const std::string::iterator end)
+boost::optional<ParameterT> parseParameters(std::string::iterator &iter,
+                                            const std::string::iterator end)
 {
     using It = std::decay<decltype(iter)>::type;
 
@@ -36,7 +42,8 @@ boost::optional<ParameterT> parseParameters(std::string::iterator &iter, const s
     try
     {
         ParameterT parameters;
-        const auto ok = boost::spirit::qi::parse(iter, end, grammar(boost::phoenix::ref(parameters)));
+        const auto ok =
+            boost::spirit::qi::parse(iter, end, grammar(boost::phoenix::ref(parameters)));
 
         // return move(a.b) is needed to move b out of a and then return the rvalue by implicit move
         if (ok && iter == end)
@@ -54,37 +61,47 @@ boost::optional<ParameterT> parseParameters(std::string::iterator &iter, const s
 } // ns detail
 
 template <>
-boost::optional<engine::api::RouteParameters> parseParameters(std::string::iterator &iter, const std::string::iterator end)
+boost::optional<engine::api::RouteParameters> parseParameters(std::string::iterator &iter,
+                                                              const std::string::iterator end)
 {
-    return detail::parseParameters<engine::api::RouteParameters, RouteParametersGrammar<>>(iter, end);
+    return detail::parseParameters<engine::api::RouteParameters, RouteParametersGrammar<>>(iter,
+                                                                                           end);
 }
 
 template <>
-boost::optional<engine::api::TableParameters> parseParameters(std::string::iterator &iter, const std::string::iterator end)
+boost::optional<engine::api::TableParameters> parseParameters(std::string::iterator &iter,
+                                                              const std::string::iterator end)
 {
-    return detail::parseParameters<engine::api::TableParameters, TableParametersGrammar<>>(iter, end);
+    return detail::parseParameters<engine::api::TableParameters, TableParametersGrammar<>>(iter,
+                                                                                           end);
 }
 
 template <>
-boost::optional<engine::api::NearestParameters> parseParameters(std::string::iterator &iter, const std::string::iterator end)
+boost::optional<engine::api::NearestParameters> parseParameters(std::string::iterator &iter,
+                                                                const std::string::iterator end)
 {
-    return detail::parseParameters<engine::api::NearestParameters, NearestParametersGrammar<>>(iter, end);
+    return detail::parseParameters<engine::api::NearestParameters, NearestParametersGrammar<>>(iter,
+                                                                                               end);
 }
 
 template <>
-boost::optional<engine::api::TripParameters> parseParameters(std::string::iterator &iter, const std::string::iterator end)
+boost::optional<engine::api::TripParameters> parseParameters(std::string::iterator &iter,
+                                                             const std::string::iterator end)
 {
     return detail::parseParameters<engine::api::TripParameters, TripParametersGrammar<>>(iter, end);
 }
 
 template <>
-boost::optional<engine::api::MatchParameters> parseParameters(std::string::iterator &iter, const std::string::iterator end)
+boost::optional<engine::api::MatchParameters> parseParameters(std::string::iterator &iter,
+                                                              const std::string::iterator end)
 {
-    return detail::parseParameters<engine::api::MatchParameters, MatchParametersGrammar<>>(iter, end);
+    return detail::parseParameters<engine::api::MatchParameters, MatchParametersGrammar<>>(iter,
+                                                                                           end);
 }
 
 template <>
-boost::optional<engine::api::TileParameters> parseParameters(std::string::iterator &iter, const std::string::iterator end)
+boost::optional<engine::api::TileParameters> parseParameters(std::string::iterator &iter,
+                                                             const std::string::iterator end)
 {
     return detail::parseParameters<engine::api::TileParameters, TileParametersGrammar<>>(iter, end);
 }
diff --git a/src/server/api/url_parser.cpp b/src/server/api/url_parser.cpp
index 875e78a..5c7d323 100644
--- a/src/server/api/url_parser.cpp
+++ b/src/server/api/url_parser.cpp
@@ -10,11 +10,8 @@
 #include <type_traits>
 
 BOOST_FUSION_ADAPT_STRUCT(osrm::server::api::ParsedURL,
-    (std::string, service)
-    (unsigned, version)
-    (std::string, profile)
-    (std::string, query)
-)
+                          (std::string, service)(unsigned, version)(std::string,
+                                                                    profile)(std::string, query))
 
 // Keep impl. TU local
 namespace
@@ -40,14 +37,10 @@ struct URLParser final : qi::grammar<Iterator, Into>
 
         // Example input: /route/v1/driving/7.416351,43.731205;7.420363,43.736189
 
-        start
-            = qi::lit('/') > service
-            > qi::lit('/') > qi::lit('v') > version
-            > qi::lit('/') > profile
-            > qi::lit('/')
-            > qi::omit[iter_pos[ph::bind(&osrm::server::api::ParsedURL::prefix_length, qi::_val) = qi::_1 - qi::_r1]]
-            > query
-            ;
+        start = qi::lit('/') > service > qi::lit('/') > qi::lit('v') > version > qi::lit('/') >
+                profile > qi::lit('/') >
+                qi::omit[iter_pos[ph::bind(&osrm::server::api::ParsedURL::prefix_length, qi::_val) =
+                                      qi::_1 - qi::_r1]] > query;
 
         BOOST_SPIRIT_DEBUG_NODES((start)(service)(version)(profile)(query))
     }
diff --git a/src/server/connection.cpp b/src/server/connection.cpp
index 0fa31e6..9cb478a 100644
--- a/src/server/connection.cpp
+++ b/src/server/connection.cpp
@@ -4,8 +4,8 @@
 
 #include <boost/assert.hpp>
 #include <boost/bind.hpp>
-#include <boost/iostreams/filtering_stream.hpp>
 #include <boost/iostreams/filter/gzip.hpp>
+#include <boost/iostreams/filtering_stream.hpp>
 
 #include <iterator>
 #include <string>
@@ -28,7 +28,8 @@ void Connection::start()
 {
     TCP_socket.async_read_some(
         boost::asio::buffer(incoming_data_buffer),
-        strand.wrap(boost::bind(&Connection::handle_read, this->shared_from_this(),
+        strand.wrap(boost::bind(&Connection::handle_read,
+                                this->shared_from_this(),
                                 boost::asio::placeholders::error,
                                 boost::asio::placeholders::bytes_transferred)));
 }
@@ -44,7 +45,8 @@ void Connection::handle_read(const boost::system::error_code &error, std::size_t
     http::compression_type compression_type(http::no_compression);
     RequestParser::RequestStatus result;
     std::tie(result, compression_type) =
-        request_parser.parse(current_request, incoming_data_buffer.data(),
+        request_parser.parse(current_request,
+                             incoming_data_buffer.data(),
                              incoming_data_buffer.data() + bytes_transferred);
 
     // the request has been parsed
@@ -81,26 +83,29 @@ void Connection::handle_read(const boost::system::error_code &error, std::size_t
             break;
         }
         // write result to stream
-        boost::asio::async_write(
-            TCP_socket, output_buffer,
-            strand.wrap(boost::bind(&Connection::handle_write, this->shared_from_this(),
-                                    boost::asio::placeholders::error)));
+        boost::asio::async_write(TCP_socket,
+                                 output_buffer,
+                                 strand.wrap(boost::bind(&Connection::handle_write,
+                                                         this->shared_from_this(),
+                                                         boost::asio::placeholders::error)));
     }
     else if (result == RequestParser::RequestStatus::invalid)
     { // request is not parseable
         current_reply = http::reply::stock_reply(http::reply::bad_request);
 
-        boost::asio::async_write(
-            TCP_socket, current_reply.to_buffers(),
-            strand.wrap(boost::bind(&Connection::handle_write, this->shared_from_this(),
-                                    boost::asio::placeholders::error)));
+        boost::asio::async_write(TCP_socket,
+                                 current_reply.to_buffers(),
+                                 strand.wrap(boost::bind(&Connection::handle_write,
+                                                         this->shared_from_this(),
+                                                         boost::asio::placeholders::error)));
     }
     else
     {
         // we don't have a result yet, so continue reading
         TCP_socket.async_read_some(
             boost::asio::buffer(incoming_data_buffer),
-            strand.wrap(boost::bind(&Connection::handle_read, this->shared_from_this(),
+            strand.wrap(boost::bind(&Connection::handle_read,
+                                    this->shared_from_this(),
                                     boost::asio::placeholders::error,
                                     boost::asio::placeholders::bytes_transferred)));
     }
diff --git a/src/server/request_handler.cpp b/src/server/request_handler.cpp
index cfe74f2..7845125 100644
--- a/src/server/request_handler.cpp
+++ b/src/server/request_handler.cpp
@@ -11,18 +11,18 @@
 #include "util/typedefs.hpp"
 
 #include "engine/status.hpp"
-#include "util/json_container.hpp"
 #include "osrm/osrm.hpp"
+#include "util/json_container.hpp"
 
-#include <boost/iostreams/filtering_streambuf.hpp>
 #include <boost/iostreams/copy.hpp>
 #include <boost/iostreams/filter/gzip.hpp>
+#include <boost/iostreams/filtering_streambuf.hpp>
 
 #include <ctime>
 
 #include <algorithm>
-#include <iterator>
 #include <iostream>
+#include <iterator>
 #include <string>
 
 namespace osrm
@@ -50,32 +50,6 @@ void RequestHandler::HandleRequest(const http::request &current_request, http::r
         std::string request_string;
         util::URIDecode(current_request.uri, request_string);
 
-        // deactivated as GCC apparently does not implement that, not even in 4.9
-        // std::time_t t = std::time(nullptr);
-        // util::SimpleLogger().Write() << std::put_time(std::localtime(&t), "%m-%d-%Y %H:%M:%S") <<
-        //     " " << current_request.endpoint.to_string() << " " <<
-        //     current_request.referrer << ( 0 == current_request.referrer.length() ? "- " :" ") <<
-        //     current_request.agent << ( 0 == current_request.agent.length() ? "- " :" ") <<
-        //     request;
-
-        time_t ltime;
-        struct tm *time_stamp;
-
-        ltime = time(nullptr);
-        time_stamp = localtime(&ltime);
-
-        // log timestamp
-        util::SimpleLogger().Write()
-            << (time_stamp->tm_mday < 10 ? "0" : "") << time_stamp->tm_mday << "-"
-            << (time_stamp->tm_mon + 1 < 10 ? "0" : "") << (time_stamp->tm_mon + 1) << "-"
-            << 1900 + time_stamp->tm_year << " " << (time_stamp->tm_hour < 10 ? "0" : "")
-            << time_stamp->tm_hour << ":" << (time_stamp->tm_min < 10 ? "0" : "")
-            << time_stamp->tm_min << ":" << (time_stamp->tm_sec < 10 ? "0" : "")
-            << time_stamp->tm_sec << " " << current_request.endpoint.to_string() << " "
-            << current_request.referrer << (0 == current_request.referrer.length() ? "- " : " ")
-            << current_request.agent << (0 == current_request.agent.length() ? "- " : " ")
-            << request_string;
-
         auto api_iterator = request_string.begin();
         auto maybe_parsed_url = api::parseURL(api_iterator, request_string.end());
         ServiceHandler::ResultT result;
@@ -100,10 +74,11 @@ void RequestHandler::HandleRequest(const http::request &current_request, http::r
         {
             const auto position = std::distance(request_string.begin(), api_iterator);
             BOOST_ASSERT(position >= 0);
-            const auto context_begin = request_string.begin() + ((position < 3) ? 0 : (position - 3UL));
+            const auto context_begin =
+                request_string.begin() + ((position < 3) ? 0 : (position - 3UL));
             BOOST_ASSERT(context_begin >= request_string.begin());
-            const auto context_end =
-                request_string.begin() + std::min<std::size_t>(position + 3UL, request_string.size());
+            const auto context_end = request_string.begin() +
+                                     std::min<std::size_t>(position + 3UL, request_string.size());
             BOOST_ASSERT(context_end <= request_string.end());
             std::string context(context_begin, context_end);
 
@@ -130,8 +105,11 @@ void RequestHandler::HandleRequest(const http::request &current_request, http::r
         else
         {
             BOOST_ASSERT(result.is<std::string>());
-            std::copy(result.get<std::string>().cbegin(), result.get<std::string>().cend(),
-                      std::back_inserter(current_reply.content));
+            current_reply.content.resize(current_reply.content.size() +
+                                         result.get<std::string>().size());
+            std::copy(result.get<std::string>().cbegin(),
+                      result.get<std::string>().cend(),
+                      current_reply.content.end());
 
             current_reply.headers.emplace_back("Content-Type", "application/x-protobuf");
         }
@@ -139,6 +117,37 @@ void RequestHandler::HandleRequest(const http::request &current_request, http::r
         // set headers
         current_reply.headers.emplace_back("Content-Length",
                                            std::to_string(current_reply.content.size()));
+
+        if (!std::getenv("DISABLE_ACCESS_LOGGING"))
+        {
+            // deactivated as GCC apparently does not implement that, not even in 4.9
+            // std::time_t t = std::time(nullptr);
+            // util::SimpleLogger().Write() << std::put_time(std::localtime(&t), "%m-%d-%Y
+            // %H:%M:%S") <<
+            //     " " << current_request.endpoint.to_string() << " " <<
+            //     current_request.referrer << ( 0 == current_request.referrer.length() ? "- " :" ")
+            //     <<
+            //     current_request.agent << ( 0 == current_request.agent.length() ? "- " :" ") <<
+            //     request;
+
+            time_t ltime;
+            struct tm *time_stamp;
+
+            ltime = time(nullptr);
+            time_stamp = localtime(&ltime);
+            // log timestamp
+            util::SimpleLogger().Write()
+                << (time_stamp->tm_mday < 10 ? "0" : "") << time_stamp->tm_mday << "-"
+                << (time_stamp->tm_mon + 1 < 10 ? "0" : "") << (time_stamp->tm_mon + 1) << "-"
+                << 1900 + time_stamp->tm_year << " " << (time_stamp->tm_hour < 10 ? "0" : "")
+                << time_stamp->tm_hour << ":" << (time_stamp->tm_min < 10 ? "0" : "")
+                << time_stamp->tm_min << ":" << (time_stamp->tm_sec < 10 ? "0" : "")
+                << time_stamp->tm_sec << " " << current_request.endpoint.to_string() << " "
+                << current_request.referrer << (0 == current_request.referrer.length() ? "- " : " ")
+                << current_request.agent << (0 == current_request.agent.length() ? "- " : " ")
+                << current_reply.status << " " //
+                << request_string;
+        }
     }
     catch (const std::exception &e)
     {
diff --git a/src/server/service/match_service.cpp b/src/server/service/match_service.cpp
index 7321b86..c832751 100644
--- a/src/server/service/match_service.cpp
+++ b/src/server/service/match_service.cpp
@@ -1,8 +1,8 @@
 #include "server/service/match_service.hpp"
 
-#include "engine/api/match_parameters.hpp"
 #include "server/api/parameters_parser.hpp"
 #include "server/service/utils.hpp"
+#include "engine/api/match_parameters.hpp"
 
 #include "util/json_container.hpp"
 
@@ -22,14 +22,15 @@ std::string getWrongOptionHelp(const engine::api::MatchParameters &parameters)
 
     const auto coord_size = parameters.coordinates.size();
 
-    const bool param_size_mismatch = constrainParamSize(PARAMETER_SIZE_MISMATCH_MSG, "hints",
-                                                        parameters.hints, coord_size, help) ||
-                                     constrainParamSize(PARAMETER_SIZE_MISMATCH_MSG, "bearings",
-                                                        parameters.bearings, coord_size, help) ||
-                                     constrainParamSize(PARAMETER_SIZE_MISMATCH_MSG, "radiuses",
-                                                        parameters.radiuses, coord_size, help) ||
-                                     constrainParamSize(PARAMETER_SIZE_MISMATCH_MSG, "timestamps",
-                                                        parameters.timestamps, coord_size, help);
+    const bool param_size_mismatch =
+        constrainParamSize(
+            PARAMETER_SIZE_MISMATCH_MSG, "hints", parameters.hints, coord_size, help) ||
+        constrainParamSize(
+            PARAMETER_SIZE_MISMATCH_MSG, "bearings", parameters.bearings, coord_size, help) ||
+        constrainParamSize(
+            PARAMETER_SIZE_MISMATCH_MSG, "radiuses", parameters.radiuses, coord_size, help) ||
+        constrainParamSize(
+            PARAMETER_SIZE_MISMATCH_MSG, "timestamps", parameters.timestamps, coord_size, help);
 
     if (!param_size_mismatch && parameters.coordinates.size() < 2)
     {
@@ -40,7 +41,8 @@ std::string getWrongOptionHelp(const engine::api::MatchParameters &parameters)
 }
 } // anon. ns
 
-engine::Status MatchService::RunQuery(std::size_t prefix_length, std::string &query, ResultT &result)
+engine::Status
+MatchService::RunQuery(std::size_t prefix_length, std::string &query, ResultT &result)
 {
     result = util::json::Object();
     auto &json_result = result.get<util::json::Object>();
diff --git a/src/server/service/nearest_service.cpp b/src/server/service/nearest_service.cpp
index 4ffd021..ca7582a 100644
--- a/src/server/service/nearest_service.cpp
+++ b/src/server/service/nearest_service.cpp
@@ -1,8 +1,8 @@
 #include "server/service/nearest_service.hpp"
 #include "server/service/utils.hpp"
 
-#include "engine/api/nearest_parameters.hpp"
 #include "server/api/parameters_parser.hpp"
+#include "engine/api/nearest_parameters.hpp"
 
 #include "util/json_container.hpp"
 
@@ -23,12 +23,13 @@ std::string getWrongOptionHelp(const engine::api::NearestParameters &parameters)
 
     const auto coord_size = parameters.coordinates.size();
 
-    const bool param_size_mismatch = constrainParamSize(PARAMETER_SIZE_MISMATCH_MSG, "hints",
-                                                        parameters.hints, coord_size, help) ||
-                                     constrainParamSize(PARAMETER_SIZE_MISMATCH_MSG, "bearings",
-                                                        parameters.bearings, coord_size, help) ||
-                                     constrainParamSize(PARAMETER_SIZE_MISMATCH_MSG, "radiuses",
-                                                        parameters.radiuses, coord_size, help);
+    const bool param_size_mismatch =
+        constrainParamSize(
+            PARAMETER_SIZE_MISMATCH_MSG, "hints", parameters.hints, coord_size, help) ||
+        constrainParamSize(
+            PARAMETER_SIZE_MISMATCH_MSG, "bearings", parameters.bearings, coord_size, help) ||
+        constrainParamSize(
+            PARAMETER_SIZE_MISMATCH_MSG, "radiuses", parameters.radiuses, coord_size, help);
 
     if (!param_size_mismatch && parameters.coordinates.size() < 2)
     {
@@ -39,7 +40,8 @@ std::string getWrongOptionHelp(const engine::api::NearestParameters &parameters)
 }
 } // anon. ns
 
-engine::Status NearestService::RunQuery(std::size_t prefix_length, std::string &query, ResultT &result)
+engine::Status
+NearestService::RunQuery(std::size_t prefix_length, std::string &query, ResultT &result)
 {
     result = util::json::Object();
     auto &json_result = result.get<util::json::Object>();
diff --git a/src/server/service/route_service.cpp b/src/server/service/route_service.cpp
index a7b85b5..cbf4fa0 100644
--- a/src/server/service/route_service.cpp
+++ b/src/server/service/route_service.cpp
@@ -1,8 +1,8 @@
 #include "server/service/route_service.hpp"
 #include "server/service/utils.hpp"
 
-#include "engine/api/route_parameters.hpp"
 #include "server/api/parameters_parser.hpp"
+#include "engine/api/route_parameters.hpp"
 
 #include "util/json_container.hpp"
 
@@ -20,12 +20,13 @@ std::string getWrongOptionHelp(const engine::api::RouteParameters &parameters)
 
     const auto coord_size = parameters.coordinates.size();
 
-    const bool param_size_mismatch = constrainParamSize(PARAMETER_SIZE_MISMATCH_MSG, "hints",
-                                                        parameters.hints, coord_size, help) ||
-                                     constrainParamSize(PARAMETER_SIZE_MISMATCH_MSG, "bearings",
-                                                        parameters.bearings, coord_size, help) ||
-                                     constrainParamSize(PARAMETER_SIZE_MISMATCH_MSG, "radiuses",
-                                                        parameters.radiuses, coord_size, help);
+    const bool param_size_mismatch =
+        constrainParamSize(
+            PARAMETER_SIZE_MISMATCH_MSG, "hints", parameters.hints, coord_size, help) ||
+        constrainParamSize(
+            PARAMETER_SIZE_MISMATCH_MSG, "bearings", parameters.bearings, coord_size, help) ||
+        constrainParamSize(
+            PARAMETER_SIZE_MISMATCH_MSG, "radiuses", parameters.radiuses, coord_size, help);
 
     if (!param_size_mismatch && parameters.coordinates.size() < 2)
     {
@@ -36,7 +37,8 @@ std::string getWrongOptionHelp(const engine::api::RouteParameters &parameters)
 }
 } // anon. ns
 
-engine::Status RouteService::RunQuery(std::size_t prefix_length, std::string &query, ResultT &result)
+engine::Status
+RouteService::RunQuery(std::size_t prefix_length, std::string &query, ResultT &result)
 {
     result = util::json::Object();
     auto &json_result = result.get<util::json::Object>();
diff --git a/src/server/service/table_service.cpp b/src/server/service/table_service.cpp
index bb93019..5873a94 100644
--- a/src/server/service/table_service.cpp
+++ b/src/server/service/table_service.cpp
@@ -1,7 +1,7 @@
 #include "server/service/table_service.hpp"
 
-#include "engine/api/table_parameters.hpp"
 #include "server/api/parameters_parser.hpp"
+#include "engine/api/table_parameters.hpp"
 
 #include "util/json_container.hpp"
 
@@ -41,12 +41,13 @@ std::string getWrongOptionHelp(const engine::api::TableParameters &parameters)
 
     const auto coord_size = parameters.coordinates.size();
 
-    const bool param_size_mismatch = constrainParamSize(PARAMETER_SIZE_MISMATCH_MSG, "hints",
-                                                        parameters.hints, coord_size, help) ||
-                                     constrainParamSize(PARAMETER_SIZE_MISMATCH_MSG, "bearings",
-                                                        parameters.bearings, coord_size, help) ||
-                                     constrainParamSize(PARAMETER_SIZE_MISMATCH_MSG, "radiuses",
-                                                        parameters.radiuses, coord_size, help);
+    const bool param_size_mismatch =
+        constrainParamSize(
+            PARAMETER_SIZE_MISMATCH_MSG, "hints", parameters.hints, coord_size, help) ||
+        constrainParamSize(
+            PARAMETER_SIZE_MISMATCH_MSG, "bearings", parameters.bearings, coord_size, help) ||
+        constrainParamSize(
+            PARAMETER_SIZE_MISMATCH_MSG, "radiuses", parameters.radiuses, coord_size, help);
 
     if (!param_size_mismatch && parameters.coordinates.size() < 2)
     {
@@ -57,7 +58,8 @@ std::string getWrongOptionHelp(const engine::api::TableParameters &parameters)
 }
 } // anon. ns
 
-engine::Status TableService::RunQuery(std::size_t prefix_length, std::string &query, ResultT &result)
+engine::Status
+TableService::RunQuery(std::size_t prefix_length, std::string &query, ResultT &result)
 {
     result = util::json::Object();
     auto &json_result = result.get<util::json::Object>();
diff --git a/src/server/service/tile_service.cpp b/src/server/service/tile_service.cpp
index a4ded2c..35d5ea9 100644
--- a/src/server/service/tile_service.cpp
+++ b/src/server/service/tile_service.cpp
@@ -1,8 +1,8 @@
 #include "server/service/tile_service.hpp"
 #include "server/service/utils.hpp"
 
-#include "engine/api/tile_parameters.hpp"
 #include "server/api/parameters_parser.hpp"
+#include "engine/api/tile_parameters.hpp"
 
 #include "util/json_container.hpp"
 
diff --git a/src/server/service/trip_service.cpp b/src/server/service/trip_service.cpp
index e0b51c0..9726d36 100644
--- a/src/server/service/trip_service.cpp
+++ b/src/server/service/trip_service.cpp
@@ -1,8 +1,8 @@
 #include "server/service/trip_service.hpp"
 #include "server/service/utils.hpp"
 
-#include "engine/api/trip_parameters.hpp"
 #include "server/api/parameters_parser.hpp"
+#include "engine/api/trip_parameters.hpp"
 
 #include "util/json_container.hpp"
 
@@ -22,12 +22,13 @@ std::string getWrongOptionHelp(const engine::api::TripParameters &parameters)
 
     const auto coord_size = parameters.coordinates.size();
 
-    const bool param_size_mismatch = constrainParamSize(PARAMETER_SIZE_MISMATCH_MSG, "hints",
-                                                        parameters.hints, coord_size, help) ||
-                                     constrainParamSize(PARAMETER_SIZE_MISMATCH_MSG, "bearings",
-                                                        parameters.bearings, coord_size, help) ||
-                                     constrainParamSize(PARAMETER_SIZE_MISMATCH_MSG, "radiuses",
-                                                        parameters.radiuses, coord_size, help);
+    const bool param_size_mismatch =
+        constrainParamSize(
+            PARAMETER_SIZE_MISMATCH_MSG, "hints", parameters.hints, coord_size, help) ||
+        constrainParamSize(
+            PARAMETER_SIZE_MISMATCH_MSG, "bearings", parameters.bearings, coord_size, help) ||
+        constrainParamSize(
+            PARAMETER_SIZE_MISMATCH_MSG, "radiuses", parameters.radiuses, coord_size, help);
 
     if (!param_size_mismatch && parameters.coordinates.size() < 2)
     {
diff --git a/src/server/service_handler.cpp b/src/server/service_handler.cpp
index 03bcf86..1bba0f6 100644
--- a/src/server/service_handler.cpp
+++ b/src/server/service_handler.cpp
@@ -1,11 +1,11 @@
 #include "server/service_handler.hpp"
 
+#include "server/service/match_service.hpp"
+#include "server/service/nearest_service.hpp"
 #include "server/service/route_service.hpp"
 #include "server/service/table_service.hpp"
-#include "server/service/nearest_service.hpp"
-#include "server/service/trip_service.hpp"
-#include "server/service/match_service.hpp"
 #include "server/service/tile_service.hpp"
+#include "server/service/trip_service.hpp"
 
 #include "server/api/parsed_url.hpp"
 #include "util/json_util.hpp"
diff --git a/src/storage/storage.cpp b/src/storage/storage.cpp
index 43967dc..ca519d2 100644
--- a/src/storage/storage.cpp
+++ b/src/storage/storage.cpp
@@ -1,24 +1,26 @@
-#include "extractor/original_edge_data.hpp"
-#include "util/range_table.hpp"
+#include "storage/storage.hpp"
 #include "contractor/query_edge.hpp"
-#include "extractor/query_node.hpp"
-#include "extractor/profile_properties.hpp"
 #include "extractor/compressed_edge_container.hpp"
-#include "util/shared_memory_vector_wrapper.hpp"
-#include "util/static_graph.hpp"
-#include "util/static_rtree.hpp"
-#include "engine/datafacade/datafacade_base.hpp"
-#include "extractor/travel_mode.hpp"
 #include "extractor/guidance/turn_instruction.hpp"
-#include "storage/storage.hpp"
-#include "storage/shared_datatype.hpp"
+#include "extractor/original_edge_data.hpp"
+#include "extractor/profile_properties.hpp"
+#include "extractor/query_node.hpp"
+#include "extractor/travel_mode.hpp"
 #include "storage/shared_barriers.hpp"
+#include "storage/shared_datatype.hpp"
 #include "storage/shared_memory.hpp"
-#include "util/fingerprint.hpp"
+#include "engine/datafacade/datafacade_base.hpp"
+#include "util/coordinate.hpp"
 #include "util/exception.hpp"
+#include "util/fingerprint.hpp"
+#include "util/io.hpp"
+#include "util/packed_vector.hpp"
+#include "util/range_table.hpp"
+#include "util/shared_memory_vector_wrapper.hpp"
 #include "util/simple_logger.hpp"
+#include "util/static_graph.hpp"
+#include "util/static_rtree.hpp"
 #include "util/typedefs.hpp"
-#include "util/coordinate.hpp"
 
 #ifdef __linux__
 #include <sys/mman.h>
@@ -30,6 +32,7 @@
 #include <cstdint>
 
 #include <fstream>
+#include <iostream>
 #include <iterator>
 #include <new>
 #include <string>
@@ -49,8 +52,7 @@ void deleteRegion(const SharedDataType region)
 {
     if (SharedMemory::RegionExists(region) && !SharedMemory::Remove(region))
     {
-        const std::string name = [&]
-        {
+        const std::string name = [&] {
             switch (region)
             {
             case CURRENT_REGIONS:
@@ -105,20 +107,14 @@ int Storage::Run()
 
     // determine segment to use
     bool segment2_in_use = SharedMemory::RegionExists(LAYOUT_2);
-    const storage::SharedDataType layout_region = [&]
-    {
+    const storage::SharedDataType layout_region = [&] {
         return segment2_in_use ? LAYOUT_1 : LAYOUT_2;
     }();
-    const storage::SharedDataType data_region = [&]
-    {
-        return segment2_in_use ? DATA_1 : DATA_2;
-    }();
-    const storage::SharedDataType previous_layout_region = [&]
-    {
+    const storage::SharedDataType data_region = [&] { return segment2_in_use ? DATA_1 : DATA_2; }();
+    const storage::SharedDataType previous_layout_region = [&] {
         return segment2_in_use ? LAYOUT_2 : LAYOUT_1;
     }();
-    const storage::SharedDataType previous_data_region = [&]
-    {
+    const storage::SharedDataType previous_data_region = [&] {
         return segment2_in_use ? DATA_2 : DATA_1;
     }();
 
@@ -136,7 +132,8 @@ int Storage::Run()
     boost::filesystem::ifstream name_stream(config.names_data_path, std::ios::binary);
     if (!name_stream)
     {
-        throw util::exception("Could not open " + config.names_data_path.string() + " for reading.");
+        throw util::exception("Could not open " + config.names_data_path.string() +
+                              " for reading.");
     }
     unsigned name_blocks = 0;
     name_stream.read((char *)&name_blocks, sizeof(unsigned));
@@ -154,7 +151,8 @@ int Storage::Run()
     boost::filesystem::ifstream edges_input_stream(config.edges_data_path, std::ios::binary);
     if (!edges_input_stream)
     {
-        throw util::exception("Could not open " + config.edges_data_path.string() + " for reading.");
+        throw util::exception("Could not open " + config.edges_data_path.string() +
+                              " for reading.");
     }
     unsigned number_of_original_edges = 0;
     edges_input_stream.read((char *)&number_of_original_edges, sizeof(unsigned));
@@ -168,6 +166,8 @@ int Storage::Run()
                                                            number_of_original_edges);
     shared_layout_ptr->SetBlockSize<extractor::guidance::TurnInstruction>(
         SharedDataLayout::TURN_INSTRUCTION, number_of_original_edges);
+    shared_layout_ptr->SetBlockSize<EntryClassID>(SharedDataLayout::ENTRY_CLASSID,
+                                                  number_of_original_edges);
 
     boost::filesystem::ifstream hsgr_input_stream(config.hsgr_data_path, std::ios::binary);
     if (!hsgr_input_stream)
@@ -245,12 +245,17 @@ int Storage::Run()
     nodes_input_stream.read((char *)&coordinate_list_size, sizeof(unsigned));
     shared_layout_ptr->SetBlockSize<util::Coordinate>(SharedDataLayout::COORDINATE_LIST,
                                                       coordinate_list_size);
+    // we'll read a list of OSM node IDs from the same data, so set the block size for the same
+    // number of items:
+    shared_layout_ptr->SetBlockSize<std::uint64_t>(SharedDataLayout::OSM_NODE_ID_LIST,
+                                                   util::PackedVector<OSMNodeID>::elements_to_blocks(coordinate_list_size));
 
     // load geometries sizes
     boost::filesystem::ifstream geometry_input_stream(config.geometries_path, std::ios::binary);
     if (!geometry_input_stream)
     {
-        throw util::exception("Could not open " + config.geometries_path.string() + " for reading.");
+        throw util::exception("Could not open " + config.geometries_path.string() +
+                              " for reading.");
     }
     unsigned number_of_geometries_indices = 0;
     unsigned number_of_compressed_geometries = 0;
@@ -258,8 +263,8 @@ int Storage::Run()
     geometry_input_stream.read((char *)&number_of_geometries_indices, sizeof(unsigned));
     shared_layout_ptr->SetBlockSize<unsigned>(SharedDataLayout::GEOMETRIES_INDEX,
                                               number_of_geometries_indices);
-    boost::iostreams::seek(geometry_input_stream, number_of_geometries_indices * sizeof(unsigned),
-                           BOOST_IOS::cur);
+    boost::iostreams::seek(
+        geometry_input_stream, number_of_geometries_indices * sizeof(unsigned), BOOST_IOS::cur);
     geometry_input_stream.read((char *)&number_of_compressed_geometries, sizeof(unsigned));
     shared_layout_ptr->SetBlockSize<extractor::CompressedEdgeContainer::CompressedEdge>(
         SharedDataLayout::GEOMETRIES_LIST, number_of_compressed_geometries);
@@ -270,7 +275,8 @@ int Storage::Run()
                                                                  std::ios::binary);
     if (!geometry_datasource_input_stream)
     {
-        throw util::exception("Could not open " + config.datasource_indexes_path.string() + " for reading.");
+        throw util::exception("Could not open " + config.datasource_indexes_path.string() +
+                              " for reading.");
     }
     std::size_t number_of_compressed_datasources = 0;
     if (geometry_datasource_input_stream)
@@ -287,7 +293,8 @@ int Storage::Run()
                                                               std::ios::binary);
     if (!datasource_names_input_stream)
     {
-        throw util::exception("Could not open " + config.datasource_names_path.string() + " for reading.");
+        throw util::exception("Could not open " + config.datasource_names_path.string() +
+                              " for reading.");
     }
     std::vector<char> m_datasource_name_data;
     std::vector<std::size_t> m_datasource_name_offsets;
@@ -298,7 +305,8 @@ int Storage::Run()
         while (std::getline(datasource_names_input_stream, name))
         {
             m_datasource_name_offsets.push_back(m_datasource_name_data.size());
-            std::copy(name.c_str(), name.c_str() + name.size(),
+            std::copy(name.c_str(),
+                      name.c_str() + name.size(),
                       std::back_inserter(m_datasource_name_data));
             m_datasource_name_lengths.push_back(name.size());
         }
@@ -310,6 +318,67 @@ int Storage::Run()
     shared_layout_ptr->SetBlockSize<std::size_t>(SharedDataLayout::DATASOURCE_NAME_LENGTHS,
                                                  m_datasource_name_lengths.size());
 
+    boost::filesystem::ifstream intersection_stream(config.intersection_class_path,
+                                                    std::ios::binary);
+    if (!static_cast<bool>(intersection_stream))
+        throw util::exception("Could not open " + config.intersection_class_path.string() +
+                              " for reading.");
+
+    if (!util::readAndCheckFingerprint(intersection_stream))
+        throw util::exception("Fingerprint of " + config.intersection_class_path.string() +
+                              " does not match or could not read from file");
+
+    std::vector<BearingClassID> bearing_class_id_table;
+    if (!util::deserializeVector(intersection_stream, bearing_class_id_table))
+        throw util::exception("Failed to bearing class ids read from " +
+                              config.names_data_path.string());
+
+    shared_layout_ptr->SetBlockSize<BearingClassID>(SharedDataLayout::BEARING_CLASSID,
+                                                    bearing_class_id_table.size());
+    unsigned bearing_blocks = 0;
+    intersection_stream.read((char *)&bearing_blocks, sizeof(unsigned));
+    unsigned sum_lengths = 0;
+    intersection_stream.read((char *)&sum_lengths, sizeof(unsigned));
+
+    shared_layout_ptr->SetBlockSize<unsigned>(SharedDataLayout::BEARING_OFFSETS, bearing_blocks);
+    shared_layout_ptr->SetBlockSize<typename util::RangeTable<16, true>::BlockT>(
+        SharedDataLayout::BEARING_BLOCKS, bearing_blocks);
+
+    std::vector<unsigned> bearing_offsets_data(bearing_blocks);
+    std::vector<typename util::RangeTable<16, true>::BlockT> bearing_blocks_data(bearing_blocks);
+
+    if (bearing_blocks)
+    {
+        intersection_stream.read(reinterpret_cast<char *>(&bearing_offsets_data[0]),
+                                 bearing_blocks * sizeof(bearing_offsets_data[0]));
+    }
+
+    if (bearing_blocks)
+    {
+        intersection_stream.read(reinterpret_cast<char *>(&bearing_blocks_data[0]),
+                                 bearing_blocks * sizeof(bearing_blocks_data[0]));
+    }
+
+    std::uint64_t num_bearings;
+    intersection_stream >> num_bearings;
+
+    std::vector<DiscreteBearing> bearing_class_table(num_bearings);
+    intersection_stream.read(reinterpret_cast<char *>(&bearing_class_table[0]),
+                             sizeof(bearing_class_table[0]) * num_bearings);
+    shared_layout_ptr->SetBlockSize<DiscreteBearing>(SharedDataLayout::BEARING_VALUES,
+                                                     num_bearings);
+    if (!static_cast<bool>(intersection_stream))
+        throw util::exception("Failed to read bearing values from " +
+                              config.intersection_class_path.string());
+
+    std::vector<util::guidance::EntryClass> entry_class_table;
+    if (!util::deserializeVector(intersection_stream, entry_class_table))
+        throw util::exception("Failed to read entry classes from " +
+                              config.intersection_class_path.string());
+
+    shared_layout_ptr->SetBlockSize<util::guidance::EntryClass>(SharedDataLayout::ENTRY_CLASS,
+                                                                entry_class_table.size());
+
     // allocate shared memory block
     util::SimpleLogger().Write() << "allocating shared memory of "
                                  << shared_layout_ptr->GetSizeOfLayout() << " bytes";
@@ -331,7 +400,9 @@ int Storage::Run()
               file_index_path_ptr +
                   shared_layout_ptr->GetBlockSize(SharedDataLayout::FILE_INDEX_PATH),
               0);
-    std::copy(absolute_file_index_path.string().begin(), absolute_file_index_path.string().end(), file_index_path_ptr);
+    std::copy(absolute_file_index_path.string().begin(),
+              absolute_file_index_path.string().end(),
+              file_index_path_ptr);
 
     // Loading street names
     unsigned *name_offsets_ptr = shared_layout_ptr->GetBlockPtr<unsigned, true>(
@@ -382,6 +453,9 @@ int Storage::Run()
         shared_layout_ptr->GetBlockPtr<extractor::guidance::TurnInstruction, true>(
             shared_memory_ptr, SharedDataLayout::TURN_INSTRUCTION);
 
+    EntryClassID *entry_class_id_ptr = shared_layout_ptr->GetBlockPtr<EntryClassID, true>(
+        shared_memory_ptr, SharedDataLayout::ENTRY_CLASSID);
+
     extractor::OriginalEdgeData current_edge_data;
     for (unsigned i = 0; i < number_of_original_edges; ++i)
     {
@@ -390,6 +464,7 @@ int Storage::Run()
         name_id_ptr[i] = current_edge_data.name_id;
         travel_mode_ptr[i] = current_edge_data.travel_mode;
         turn_instructions_ptr[i] = current_edge_data.turn_instruction;
+        entry_class_id_ptr[i] = current_edge_data.entry_classid;
     }
     edges_input_stream.close();
 
@@ -440,15 +515,16 @@ int Storage::Run()
     {
         std::cout << "Copying " << (m_datasource_name_data.end() - m_datasource_name_data.begin())
                   << " chars into name data ptr\n";
-        std::copy(m_datasource_name_data.begin(), m_datasource_name_data.end(),
-                  datasource_name_data_ptr);
+        std::copy(
+            m_datasource_name_data.begin(), m_datasource_name_data.end(), datasource_name_data_ptr);
     }
 
     auto datasource_name_offsets_ptr = shared_layout_ptr->GetBlockPtr<std::size_t, true>(
         shared_memory_ptr, SharedDataLayout::DATASOURCE_NAME_OFFSETS);
     if (shared_layout_ptr->GetBlockSize(SharedDataLayout::DATASOURCE_NAME_OFFSETS) > 0)
     {
-        std::copy(m_datasource_name_offsets.begin(), m_datasource_name_offsets.end(),
+        std::copy(m_datasource_name_offsets.begin(),
+                  m_datasource_name_offsets.end(),
                   datasource_name_offsets_ptr);
     }
 
@@ -456,19 +532,26 @@ int Storage::Run()
         shared_memory_ptr, SharedDataLayout::DATASOURCE_NAME_LENGTHS);
     if (shared_layout_ptr->GetBlockSize(SharedDataLayout::DATASOURCE_NAME_LENGTHS) > 0)
     {
-        std::copy(m_datasource_name_lengths.begin(), m_datasource_name_lengths.end(),
+        std::copy(m_datasource_name_lengths.begin(),
+                  m_datasource_name_lengths.end(),
                   datasource_name_lengths_ptr);
     }
 
     // Loading list of coordinates
     util::Coordinate *coordinates_ptr = shared_layout_ptr->GetBlockPtr<util::Coordinate, true>(
         shared_memory_ptr, SharedDataLayout::COORDINATE_LIST);
+    std::uint64_t *osmnodeid_ptr = shared_layout_ptr->GetBlockPtr<std::uint64_t, true>(
+        shared_memory_ptr, SharedDataLayout::OSM_NODE_ID_LIST);
+    util::PackedVector<OSMNodeID, true> osmnodeid_list;
+    osmnodeid_list.reset(
+        osmnodeid_ptr, shared_layout_ptr->num_entries[storage::SharedDataLayout::OSM_NODE_ID_LIST]);
 
     extractor::QueryNode current_node;
     for (unsigned i = 0; i < coordinate_list_size; ++i)
     {
         nodes_input_stream.read((char *)&current_node, sizeof(extractor::QueryNode));
         coordinates_ptr[i] = util::Coordinate(current_node.lon, current_node.lat);
+        osmnodeid_list.push_back(OSMNodeID(current_node.node_id));
     }
     nodes_input_stream.close();
 
@@ -503,8 +586,7 @@ int Storage::Run()
         {
             const unsigned bucket = i / 32;
             const unsigned offset = i % 32;
-            const unsigned value = [&]
-            {
+            const unsigned value = [&] {
                 unsigned return_value = 0;
                 if (0 != offset)
                 {
@@ -539,13 +621,53 @@ int Storage::Run()
     hsgr_input_stream.close();
 
     // load profile properties
-    auto profile_properties_ptr = shared_layout_ptr->GetBlockPtr<extractor::ProfileProperties, true>(shared_memory_ptr, SharedDataLayout::PROPERTIES);
+    auto profile_properties_ptr =
+        shared_layout_ptr->GetBlockPtr<extractor::ProfileProperties, true>(
+            shared_memory_ptr, SharedDataLayout::PROPERTIES);
     boost::filesystem::ifstream profile_properties_stream(config.properties_path);
     if (!profile_properties_stream)
     {
         util::exception("Could not open " + config.properties_path.string() + " for reading!");
     }
-    profile_properties_stream.read(reinterpret_cast<char*>(profile_properties_ptr), sizeof(extractor::ProfileProperties));
+    profile_properties_stream.read(reinterpret_cast<char *>(profile_properties_ptr),
+                                   sizeof(extractor::ProfileProperties));
+
+    // load intersection classes
+    if (!bearing_class_id_table.empty())
+    {
+        auto bearing_id_ptr = shared_layout_ptr->GetBlockPtr<BearingClassID, true>(
+            shared_memory_ptr, SharedDataLayout::BEARING_CLASSID);
+        std::copy(bearing_class_id_table.begin(), bearing_class_id_table.end(), bearing_id_ptr);
+    }
+
+    if (shared_layout_ptr->GetBlockSize(SharedDataLayout::BEARING_OFFSETS) > 0)
+    {
+        auto *bearing_offsets_ptr = shared_layout_ptr->GetBlockPtr<unsigned, true>(
+            shared_memory_ptr, SharedDataLayout::BEARING_OFFSETS);
+        std::copy(bearing_offsets_data.begin(), bearing_offsets_data.end(), bearing_offsets_ptr);
+    }
+
+    if (shared_layout_ptr->GetBlockSize(SharedDataLayout::BEARING_BLOCKS) > 0)
+    {
+        auto *bearing_blocks_ptr =
+            shared_layout_ptr->GetBlockPtr<typename util::RangeTable<16, true>::BlockT, true>(
+                shared_memory_ptr, SharedDataLayout::BEARING_BLOCKS);
+        std::copy(bearing_blocks_data.begin(), bearing_blocks_data.end(), bearing_blocks_ptr);
+    }
+
+    if (!bearing_class_table.empty())
+    {
+        auto bearing_class_ptr = shared_layout_ptr->GetBlockPtr<DiscreteBearing, true>(
+            shared_memory_ptr, SharedDataLayout::BEARING_VALUES);
+        std::copy(bearing_class_table.begin(), bearing_class_table.end(), bearing_class_ptr);
+    }
+
+    if (!entry_class_table.empty())
+    {
+        auto entry_class_ptr = shared_layout_ptr->GetBlockPtr<util::guidance::EntryClass, true>(
+            shared_memory_ptr, SharedDataLayout::ENTRY_CLASS);
+        std::copy(entry_class_table.begin(), entry_class_table.end(), entry_class_ptr);
+    }
 
     // acquire lock
     SharedMemory *data_type_memory =
diff --git a/src/storage/storage_config.cpp b/src/storage/storage_config.cpp
index 6ade34a..c74d296 100644
--- a/src/storage/storage_config.cpp
+++ b/src/storage/storage_config.cpp
@@ -1,4 +1,5 @@
 #include "storage/storage_config.hpp"
+#include "util/simple_logger.hpp"
 
 #include <boost/filesystem/operations.hpp>
 
@@ -14,25 +15,39 @@ StorageConfig::StorageConfig(const boost::filesystem::path &base)
       geometries_path{base.string() + ".geometry"}, timestamp_path{base.string() + ".timestamp"},
       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"}
+      names_data_path{base.string() + ".names"}, properties_path{base.string() + ".properties"},
+      intersection_class_path{base.string() + ".icd"}
 {
 }
 
 bool StorageConfig::IsValid() const
 {
-    return boost::filesystem::is_regular_file(ram_index_path) &&
-           boost::filesystem::is_regular_file(file_index_path) &&
-           boost::filesystem::is_regular_file(hsgr_data_path) &&
-           boost::filesystem::is_regular_file(nodes_data_path) &&
-           boost::filesystem::is_regular_file(edges_data_path) &&
-           boost::filesystem::is_regular_file(core_data_path) &&
-           boost::filesystem::is_regular_file(geometries_path) &&
-           boost::filesystem::is_regular_file(timestamp_path) &&
-           boost::filesystem::is_regular_file(datasource_names_path) &&
-           boost::filesystem::is_regular_file(datasource_indexes_path) &&
-           boost::filesystem::is_regular_file(names_data_path) &&
-           boost::filesystem::is_regular_file(properties_path);
+    const constexpr auto num_files = 13;
+    const boost::filesystem::path paths[num_files] = {ram_index_path,
+                                                      file_index_path,
+                                                      hsgr_data_path,
+                                                      nodes_data_path,
+                                                      edges_data_path,
+                                                      core_data_path,
+                                                      geometries_path,
+                                                      timestamp_path,
+                                                      datasource_indexes_path,
+                                                      datasource_indexes_path,
+                                                      names_data_path,
+                                                      properties_path,
+                                                      intersection_class_path};
+
+    bool success = true;
+    for (auto path = paths; path != paths + num_files; ++path)
+    {
+        if (!boost::filesystem::is_regular_file(*path))
+        {
+            util::SimpleLogger().Write(logWARNING) << "Missing/Broken File: " << path->string();
+            success = false;
+        }
+    }
+
+    return success;
 }
 }
 }
diff --git a/src/tools/components.cpp b/src/tools/components.cpp
index 96d300b..71d59f2 100644
--- a/src/tools/components.cpp
+++ b/src/tools/components.cpp
@@ -1,13 +1,13 @@
-#include "util/typedefs.hpp"
 #include "extractor/tarjan_scc.hpp"
 #include "util/coordinate_calculation.hpp"
 #include "util/dynamic_graph.hpp"
-#include "util/static_graph.hpp"
+#include "util/exception.hpp"
 #include "util/fingerprint.hpp"
 #include "util/graph_loader.hpp"
 #include "util/make_unique.hpp"
-#include "util/exception.hpp"
 #include "util/simple_logger.hpp"
+#include "util/static_graph.hpp"
+#include "util/typedefs.hpp"
 
 #include <boost/filesystem.hpp>
 
@@ -65,8 +65,8 @@ std::size_t loadGraph(const char *path,
     std::vector<NodeID> traffic_light_node_list;
     std::vector<NodeID> barrier_node_list;
 
-    auto number_of_nodes = util::loadNodesFromFile(input_stream, barrier_node_list,
-                                                   traffic_light_node_list, coordinate_list);
+    auto number_of_nodes = util::loadNodesFromFile(
+        input_stream, barrier_node_list, traffic_light_node_list, coordinate_list);
 
     util::loadEdgesFromFile(input_stream, edge_list);
 
@@ -83,13 +83,17 @@ std::size_t loadGraph(const char *path,
 
         if (input_edge.forward)
         {
-            graph_edge_list.emplace_back(input_edge.source, input_edge.target,
-                                         (std::max)(input_edge.weight, 1), input_edge.name_id);
+            graph_edge_list.emplace_back(input_edge.source,
+                                         input_edge.target,
+                                         (std::max)(input_edge.weight, 1),
+                                         input_edge.name_id);
         }
         if (input_edge.backward)
         {
-            graph_edge_list.emplace_back(input_edge.target, input_edge.source,
-                                         (std::max)(input_edge.weight, 1), input_edge.name_id);
+            graph_edge_list.emplace_back(input_edge.target,
+                                         input_edge.source,
+                                         (std::max)(input_edge.weight, 1),
+                                         input_edge.name_id);
         }
     }
 
@@ -136,7 +140,6 @@ int main(int argc, char *argv[]) try
     osrm::tools::deleteFileIfExists("component.shx");
     osrm::tools::deleteFileIfExists("component.shp");
 
-
     OGRRegisterAll();
 
     const char *psz_driver_name = "ESRI Shapefile";
diff --git a/src/tools/contract.cpp b/src/tools/contract.cpp
index f743bec..c21af0c 100644
--- a/src/tools/contract.cpp
+++ b/src/tools/contract.cpp
@@ -39,23 +39,27 @@ return_code parseArguments(int argc, char *argv[], contractor::ContractorConfig
         "core,k",
         boost::program_options::value<double>(&contractor_config.core_factor)->default_value(1.0),
         "Percentage of the graph (in vertices) to contract [0..1]")(
-        "segment-speed-file", boost::program_options::value<std::vector<std::string>>(
-                                  &contractor_config.segment_speed_lookup_paths)
-                                  ->composing(),
+        "segment-speed-file",
+        boost::program_options::value<std::vector<std::string>>(
+            &contractor_config.segment_speed_lookup_paths)
+            ->composing(),
         "Lookup files containing nodeA, nodeB, speed data to adjust edge weights")(
-        "turn-penalty-file", boost::program_options::value<std::vector<std::string>>(
-                                 &contractor_config.turn_penalty_lookup_paths)
-                                 ->composing(),
+        "turn-penalty-file",
+        boost::program_options::value<std::vector<std::string>>(
+            &contractor_config.turn_penalty_lookup_paths)
+            ->composing(),
         "Lookup files containing from_, to_, via_nodes, and turn penalties to adjust turn weights")(
-        "level-cache,o", boost::program_options::value<bool>(&contractor_config.use_cached_priority)
-                             ->default_value(false),
+        "level-cache,o",
+        boost::program_options::value<bool>(&contractor_config.use_cached_priority)
+            ->default_value(false),
         "Use .level file to retain the contaction level for each node from the last run.");
 
     // hidden options, will be allowed on command line, but will not be shown to the user
     boost::program_options::options_description hidden_options("Hidden options");
-    hidden_options.add_options()("input,i", boost::program_options::value<boost::filesystem::path>(
-                                                &contractor_config.osrm_input_path),
-                                 "Input file in .osm, .osm.bz2 or .osm.pbf format");
+    hidden_options.add_options()(
+        "input,i",
+        boost::program_options::value<boost::filesystem::path>(&contractor_config.osrm_input_path),
+        "Input file in .osm, .osm.bz2 or .osm.pbf format");
 
     // positional option
     boost::program_options::positional_options_description positional_options;
@@ -65,9 +69,10 @@ return_code parseArguments(int argc, char *argv[], contractor::ContractorConfig
     boost::program_options::options_description cmdline_options;
     cmdline_options.add(generic_options).add(config_options).add(hidden_options);
 
-    const auto* executable = argv[0];
+    const auto *executable = argv[0];
     boost::program_options::options_description visible_options(
-        "Usage: " + boost::filesystem::path(executable).filename().string() + " <input.osrm> [options]");
+        "Usage: " + boost::filesystem::path(executable).filename().string() +
+        " <input.osrm> [options]");
     visible_options.add(generic_options).add(config_options);
 
     // parse command line options
diff --git a/src/tools/extract.cpp b/src/tools/extract.cpp
index 672ada2..cc5915c 100644
--- a/src/tools/extract.cpp
+++ b/src/tools/extract.cpp
@@ -52,9 +52,10 @@ return_code parseArguments(int argc, char *argv[], extractor::ExtractorConfig &e
     // hidden options, will be allowed on command line, but will not be
     // shown to the user
     boost::program_options::options_description hidden_options("Hidden options");
-    hidden_options.add_options()("input,i", boost::program_options::value<boost::filesystem::path>(
-                                                &extractor_config.input_path),
-                                 "Input file in .osm, .osm.bz2 or .osm.pbf format");
+    hidden_options.add_options()(
+        "input,i",
+        boost::program_options::value<boost::filesystem::path>(&extractor_config.input_path),
+        "Input file in .osm, .osm.bz2 or .osm.pbf format");
 
     // positional option
     boost::program_options::positional_options_description positional_options;
@@ -64,9 +65,10 @@ return_code parseArguments(int argc, char *argv[], extractor::ExtractorConfig &e
     boost::program_options::options_description cmdline_options;
     cmdline_options.add(generic_options).add(config_options).add(hidden_options);
 
-    const auto* executable = argv[0];
+    const auto *executable = argv[0];
     boost::program_options::options_description visible_options(
-        boost::filesystem::path(executable).filename().string() + " <input.osm/.osm.bz2/.osm.pbf> [options]");
+        boost::filesystem::path(executable).filename().string() +
+        " <input.osm/.osm.bz2/.osm.pbf> [options]");
     visible_options.add(generic_options).add(config_options);
 
     // parse command line options
diff --git a/src/tools/io-benchmark.cpp b/src/tools/io-benchmark.cpp
index 0030740..1cabd2b 100644
--- a/src/tools/io-benchmark.cpp
+++ b/src/tools/io-benchmark.cpp
@@ -40,8 +40,8 @@ void runStatistics(std::vector<double> &timings_vector, Statistics &stats)
     double primary_sum = std::accumulate(timings_vector.begin(), timings_vector.end(), 0.0);
     stats.mean = primary_sum / timings_vector.size();
 
-    double primary_sq_sum = std::inner_product(timings_vector.begin(), timings_vector.end(),
-                                               timings_vector.begin(), 0.0);
+    double primary_sq_sum = std::inner_product(
+        timings_vector.begin(), timings_vector.end(), timings_vector.begin(), 0.0);
     stats.dev = std::sqrt(primary_sq_sum / timings_vector.size() - (stats.mean * stats.mean));
 }
 }
diff --git a/src/tools/routed.cpp b/src/tools/routed.cpp
index 831a9cd..612f7dd 100644
--- a/src/tools/routed.cpp
+++ b/src/tools/routed.cpp
@@ -3,8 +3,8 @@
 #include "util/simple_logger.hpp"
 #include "util/version.hpp"
 
-#include "osrm/osrm.hpp"
 #include "osrm/engine_config.hpp"
+#include "osrm/osrm.hpp"
 #include "osrm/storage_config.hpp"
 
 #include <boost/any.hpp>
@@ -23,8 +23,8 @@
 #include <future>
 #include <iostream>
 #include <new>
-#include <thread>
 #include <string>
+#include <thread>
 
 #ifdef _WIN32
 boost::function0<void> console_ctrl_function;
@@ -52,19 +52,18 @@ const static unsigned INIT_OK_DO_NOT_START_ENGINE = 1;
 const static unsigned INIT_FAILED = -1;
 
 // generate boost::program_options object for the routing part
-inline unsigned
-generateServerProgramOptions(const int argc,
-                             const char *argv[],
-                             boost::filesystem::path &base_path,
-                             std::string &ip_address,
-                             int &ip_port,
-                             int &requested_num_threads,
-                             bool &use_shared_memory,
-                             bool &trial,
-                             int &max_locations_trip,
-                             int &max_locations_viaroute,
-                             int &max_locations_distance_table,
-                             int &max_locations_map_matching)
+inline unsigned generateServerProgramOptions(const int argc,
+                                             const char *argv[],
+                                             boost::filesystem::path &base_path,
+                                             std::string &ip_address,
+                                             int &ip_port,
+                                             int &requested_num_threads,
+                                             bool &use_shared_memory,
+                                             bool &trial,
+                                             int &max_locations_trip,
+                                             int &max_locations_viaroute,
+                                             int &max_locations_distance_table,
+                                             int &max_locations_map_matching)
 {
     using boost::program_options::value;
     using boost::filesystem::path;
@@ -77,29 +76,36 @@ generateServerProgramOptions(const int argc,
 
     // declare a group of options that will be allowed on command line
     boost::program_options::options_description config_options("Configuration");
-    config_options.add_options()                                                             //
-        ("ip,i", value<std::string>(&ip_address)->default_value("0.0.0.0"),
+    config_options.add_options() //
+        ("ip,i",
+         value<std::string>(&ip_address)->default_value("0.0.0.0"),
          "IP address") //
-        ("port,p", value<int>(&ip_port)->default_value(5000),
+        ("port,p",
+         value<int>(&ip_port)->default_value(5000),
          "TCP/IP port") //
-        ("threads,t", value<int>(&requested_num_threads)->default_value(8),
+        ("threads,t",
+         value<int>(&requested_num_threads)->default_value(8),
          "Number of threads to use") //
         ("shared-memory,s",
          value<bool>(&use_shared_memory)->implicit_value(true)->default_value(false),
          "Load data from shared memory") //
-        ("max-viaroute-size", value<int>(&max_locations_viaroute)->default_value(500),
+        ("max-viaroute-size",
+         value<int>(&max_locations_viaroute)->default_value(500),
          "Max. locations supported in viaroute query") //
-        ("max-trip-size", value<int>(&max_locations_trip)->default_value(100),
+        ("max-trip-size",
+         value<int>(&max_locations_trip)->default_value(100),
          "Max. locations supported in trip query") //
-        ("max-table-size", value<int>(&max_locations_distance_table)->default_value(100),
+        ("max-table-size",
+         value<int>(&max_locations_distance_table)->default_value(100),
          "Max. locations supported in distance table query") //
-        ("max-matching-size", value<int>(&max_locations_map_matching)->default_value(100),
+        ("max-matching-size",
+         value<int>(&max_locations_map_matching)->default_value(100),
          "Max. locations supported in map matching query");
 
     // hidden options, will be allowed on command line, but will not be shown to the user
     boost::program_options::options_description hidden_options("Hidden options");
-    hidden_options.add_options()("base,b", value<boost::filesystem::path>(&base_path),
-                                 "base path to .osrm file");
+    hidden_options.add_options()(
+        "base,b", value<boost::filesystem::path>(&base_path), "base path to .osrm file");
 
     // positional option
     boost::program_options::positional_options_description positional_options;
@@ -109,7 +115,7 @@ generateServerProgramOptions(const int argc,
     boost::program_options::options_description cmdline_options;
     cmdline_options.add(generic_options).add(config_options).add(hidden_options);
 
-    const auto* executable = argv[0];
+    const auto *executable = argv[0];
     boost::program_options::options_description visible_options(
         boost::filesystem::path(executable).filename().string() + " <base.osrm> [<options>]");
     visible_options.add(generic_options).add(config_options);
@@ -146,7 +152,8 @@ generateServerProgramOptions(const int argc,
     }
     else if (use_shared_memory && option_variables.count("base"))
     {
-        util::SimpleLogger().Write(logWARNING) << "Shared memory settings conflict with path settings.";
+        util::SimpleLogger().Write(logWARNING)
+            << "Shared memory settings conflict with path settings.";
     }
 
     util::SimpleLogger().Write() << visible_options;
@@ -163,11 +170,18 @@ int main(int argc, const char *argv[]) try
 
     EngineConfig config;
     boost::filesystem::path base_path;
-    const unsigned init_result = generateServerProgramOptions(
-        argc, argv, base_path, ip_address, ip_port, requested_thread_num,
-        config.use_shared_memory, trial_run, config.max_locations_trip,
-        config.max_locations_viaroute, config.max_locations_distance_table,
-        config.max_locations_map_matching);
+    const unsigned init_result = generateServerProgramOptions(argc,
+                                                              argv,
+                                                              base_path,
+                                                              ip_address,
+                                                              ip_port,
+                                                              requested_thread_num,
+                                                              config.use_shared_memory,
+                                                              trial_run,
+                                                              config.max_locations_trip,
+                                                              config.max_locations_viaroute,
+                                                              config.max_locations_distance_table,
+                                                              config.max_locations_map_matching);
     if (init_result == INIT_OK_DO_NOT_START_ENGINE)
     {
         return EXIT_SUCCESS;
@@ -180,7 +194,7 @@ int main(int argc, const char *argv[]) try
     {
         config.storage_config = storage::StorageConfig(base_path);
     }
-    if(!config.IsValid())
+    if (!config.IsValid())
     {
         if (base_path.empty() != config.use_shared_memory)
         {
@@ -188,53 +202,65 @@ int main(int argc, const char *argv[]) try
         }
         else
         {
-            if(!boost::filesystem::is_regular_file(config.storage_config.ram_index_path))
+            if (!boost::filesystem::is_regular_file(config.storage_config.ram_index_path))
             {
-                util::SimpleLogger().Write(logWARNING) << config.storage_config.ram_index_path << " is not found";
+                util::SimpleLogger().Write(logWARNING) << config.storage_config.ram_index_path
+                                                       << " is not found";
             }
-            if(!boost::filesystem::is_regular_file(config.storage_config.file_index_path))
+            if (!boost::filesystem::is_regular_file(config.storage_config.file_index_path))
             {
-                util::SimpleLogger().Write(logWARNING) << config.storage_config.file_index_path << " is not found";
+                util::SimpleLogger().Write(logWARNING) << config.storage_config.file_index_path
+                                                       << " is not found";
             }
-            if(!boost::filesystem::is_regular_file(config.storage_config.hsgr_data_path))
+            if (!boost::filesystem::is_regular_file(config.storage_config.hsgr_data_path))
             {
-                util::SimpleLogger().Write(logWARNING) << config.storage_config.hsgr_data_path << " is not found";
+                util::SimpleLogger().Write(logWARNING) << config.storage_config.hsgr_data_path
+                                                       << " is not found";
             }
-            if(!boost::filesystem::is_regular_file(config.storage_config.nodes_data_path))
+            if (!boost::filesystem::is_regular_file(config.storage_config.nodes_data_path))
             {
-                util::SimpleLogger().Write(logWARNING) << config.storage_config.nodes_data_path << " is not found";
+                util::SimpleLogger().Write(logWARNING) << config.storage_config.nodes_data_path
+                                                       << " is not found";
             }
-            if(!boost::filesystem::is_regular_file(config.storage_config.edges_data_path))
+            if (!boost::filesystem::is_regular_file(config.storage_config.edges_data_path))
             {
-                util::SimpleLogger().Write(logWARNING) << config.storage_config.edges_data_path << " is not found";
+                util::SimpleLogger().Write(logWARNING) << config.storage_config.edges_data_path
+                                                       << " is not found";
             }
-            if(!boost::filesystem::is_regular_file(config.storage_config.core_data_path))
+            if (!boost::filesystem::is_regular_file(config.storage_config.core_data_path))
             {
-                util::SimpleLogger().Write(logWARNING) << config.storage_config.core_data_path << " is not found";
+                util::SimpleLogger().Write(logWARNING) << config.storage_config.core_data_path
+                                                       << " is not found";
             }
-            if(!boost::filesystem::is_regular_file(config.storage_config.geometries_path))
+            if (!boost::filesystem::is_regular_file(config.storage_config.geometries_path))
             {
-                util::SimpleLogger().Write(logWARNING) << config.storage_config.geometries_path << " is not found";
+                util::SimpleLogger().Write(logWARNING) << config.storage_config.geometries_path
+                                                       << " is not found";
             }
-            if(!boost::filesystem::is_regular_file(config.storage_config.timestamp_path))
+            if (!boost::filesystem::is_regular_file(config.storage_config.timestamp_path))
             {
-                util::SimpleLogger().Write(logWARNING) << config.storage_config.timestamp_path << " is not found";
+                util::SimpleLogger().Write(logWARNING) << config.storage_config.timestamp_path
+                                                       << " is not found";
             }
-            if(!boost::filesystem::is_regular_file(config.storage_config.datasource_names_path))
+            if (!boost::filesystem::is_regular_file(config.storage_config.datasource_names_path))
             {
-                util::SimpleLogger().Write(logWARNING) << config.storage_config.datasource_names_path << " is not found";
+                util::SimpleLogger().Write(logWARNING)
+                    << config.storage_config.datasource_names_path << " is not found";
             }
-            if(!boost::filesystem::is_regular_file(config.storage_config.datasource_indexes_path))
+            if (!boost::filesystem::is_regular_file(config.storage_config.datasource_indexes_path))
             {
-                util::SimpleLogger().Write(logWARNING) << config.storage_config.datasource_indexes_path << " is not found";
+                util::SimpleLogger().Write(logWARNING)
+                    << config.storage_config.datasource_indexes_path << " is not found";
             }
-            if(!boost::filesystem::is_regular_file(config.storage_config.names_data_path))
+            if (!boost::filesystem::is_regular_file(config.storage_config.names_data_path))
             {
-                util::SimpleLogger().Write(logWARNING) << config.storage_config.names_data_path << " is not found";
+                util::SimpleLogger().Write(logWARNING) << config.storage_config.names_data_path
+                                                       << " is not found";
             }
-            if(!boost::filesystem::is_regular_file(config.storage_config.properties_path))
+            if (!boost::filesystem::is_regular_file(config.storage_config.properties_path))
             {
-                util::SimpleLogger().Write(logWARNING) << config.storage_config.properties_path << " is not found";
+                util::SimpleLogger().Write(logWARNING) << config.storage_config.properties_path
+                                                       << " is not found";
             }
         }
         return EXIT_FAILURE;
@@ -289,11 +315,10 @@ int main(int argc, const char *argv[]) try
     }
     else
     {
-        std::packaged_task<int()> server_task([&]
-                                              {
-                                                  routing_server->Run();
-                                                  return 0;
-                                              });
+        std::packaged_task<int()> server_task([&] {
+            routing_server->Run();
+            return 0;
+        });
         auto future = server_task.get_future();
         std::thread server_thread(std::move(server_task));
 
@@ -306,6 +331,10 @@ int main(int argc, const char *argv[]) try
         sigaddset(&wait_mask, SIGTERM);
         pthread_sigmask(SIG_BLOCK, &wait_mask, nullptr);
         util::SimpleLogger().Write() << "running and waiting for requests";
+        if (std::getenv("SIGNAL_PARENT_WHEN_READY"))
+        {
+            kill(getppid(), SIGUSR1);
+        }
         sigwait(&wait_mask, &sig);
 #else
         // Set console control handler to allow server to be stopped.
diff --git a/src/tools/springclean.cpp b/src/tools/springclean.cpp
index 69a7cc1..536456b 100644
--- a/src/tools/springclean.cpp
+++ b/src/tools/springclean.cpp
@@ -1,7 +1,7 @@
 #include <cstdio>
 
-#include "storage/shared_memory.hpp"
 #include "storage/shared_datatype.hpp"
+#include "storage/shared_memory.hpp"
 #include "util/simple_logger.hpp"
 
 namespace osrm
@@ -16,8 +16,7 @@ void deleteRegion(const SharedDataType region)
 {
     if (SharedMemory::RegionExists(region) && !SharedMemory::Remove(region))
     {
-        const std::string name = [&]
-        {
+        const std::string name = [&] {
             switch (region)
             {
             case CURRENT_REGIONS:
diff --git a/src/tools/store.cpp b/src/tools/store.cpp
index 9e7cbbc..c0f98ba 100644
--- a/src/tools/store.cpp
+++ b/src/tools/store.cpp
@@ -10,7 +10,9 @@
 using namespace osrm;
 
 // generate boost::program_options object for the routing part
-bool generateDataStoreOptions(const int argc, const char *argv[], boost::filesystem::path& base_path)
+bool generateDataStoreOptions(const int argc,
+                              const char *argv[],
+                              boost::filesystem::path &base_path)
 {
     // declare a group of options that will be allowed only on command line
     boost::program_options::options_description generic_options("Options");
@@ -23,9 +25,9 @@ bool generateDataStoreOptions(const int argc, const char *argv[], boost::filesys
 
     // hidden options, will be allowed on command line but will not be shown to the user
     boost::program_options::options_description hidden_options("Hidden options");
-    hidden_options.add_options()(
-        "base,b", boost::program_options::value<boost::filesystem::path>(&base_path),
-        "base path to .osrm file");
+    hidden_options.add_options()("base,b",
+                                 boost::program_options::value<boost::filesystem::path>(&base_path),
+                                 "base path to .osrm file");
 
     // positional option
     boost::program_options::positional_options_description positional_options;
@@ -35,7 +37,7 @@ bool generateDataStoreOptions(const int argc, const char *argv[], boost::filesys
     boost::program_options::options_description cmdline_options;
     cmdline_options.add(generic_options).add(config_options).add(hidden_options);
 
-    const auto* executable = argv[0];
+    const auto *executable = argv[0];
     boost::program_options::options_description visible_options(
         boost::filesystem::path(executable).filename().string() + " [<options>] <configuration>");
     visible_options.add(generic_options).add(config_options);
@@ -84,7 +86,7 @@ int main(const int argc, const char *argv[]) try
     storage::StorageConfig config(base_path);
     if (!config.IsValid())
     {
-        util::SimpleLogger().Write(logWARNING) << "Invalid file path given!";
+        util::SimpleLogger().Write(logWARNING) << "Config contains invalid file paths. Exiting!";
         return EXIT_FAILURE;
     }
     storage::Storage storage(std::move(config));
diff --git a/src/tools/unlock_all_mutexes.cpp b/src/tools/unlock_all_mutexes.cpp
index 2af8477..abb5d86 100644
--- a/src/tools/unlock_all_mutexes.cpp
+++ b/src/tools/unlock_all_mutexes.cpp
@@ -1,5 +1,5 @@
-#include "util/simple_logger.hpp"
 #include "storage/shared_barriers.hpp"
+#include "util/simple_logger.hpp"
 
 #include <iostream>
 
diff --git a/src/util/coordinate.cpp b/src/util/coordinate.cpp
index 0cec666..bd1d251 100644
--- a/src/util/coordinate.cpp
+++ b/src/util/coordinate.cpp
@@ -8,8 +8,8 @@
 #ifndef NDEBUG
 #include <bitset>
 #endif
-#include <iostream>
 #include <iomanip>
+#include <iostream>
 #include <limits>
 
 namespace osrm
@@ -17,7 +17,6 @@ namespace osrm
 namespace util
 {
 
-
 bool Coordinate::IsValid() const
 {
     return !(lat > FixedLatitude(90 * COORDINATE_PRECISION) ||
@@ -26,16 +25,12 @@ bool Coordinate::IsValid() const
              lon < FixedLongitude(-180 * COORDINATE_PRECISION));
 }
 
-
 bool FloatCoordinate::IsValid() const
 {
-    return !(lat > FloatLatitude(90) ||
-             lat < FloatLatitude(-90) ||
-             lon > FloatLongitude(180) ||
+    return !(lat > FloatLatitude(90) || lat < FloatLatitude(-90) || lon > FloatLongitude(180) ||
              lon < FloatLongitude(-180));
 }
 
-
 bool operator==(const Coordinate lhs, const Coordinate rhs)
 {
     return lhs.lat == rhs.lat && lhs.lon == rhs.lon;
@@ -56,8 +51,7 @@ std::ostream &operator<<(std::ostream &out, const Coordinate coordinate)
 }
 std::ostream &operator<<(std::ostream &out, const FloatCoordinate coordinate)
 {
-    out << std::setprecision(12) << "(lon:" << coordinate.lon
-        << ", lat:" << coordinate.lat << ")";
+    out << std::setprecision(12) << "(lon:" << coordinate.lon << ", lat:" << coordinate.lat << ")";
     return out;
 }
 }
diff --git a/src/util/coordinate_calculation.cpp b/src/util/coordinate_calculation.cpp
index 4e3492d..7dafb78 100644
--- a/src/util/coordinate_calculation.cpp
+++ b/src/util/coordinate_calculation.cpp
@@ -77,7 +77,6 @@ double greatCircleDistance(const Coordinate coordinate_1, const Coordinate coord
     return std::hypot(x_value, y_value) * detail::EARTH_RADIUS;
 }
 
-
 double perpendicularDistance(const Coordinate segment_source,
                              const Coordinate segment_target,
                              const Coordinate query_location,
@@ -89,9 +88,10 @@ double perpendicularDistance(const Coordinate segment_source,
     BOOST_ASSERT(query_location.IsValid());
 
     FloatCoordinate projected_nearest;
-    std::tie(ratio, projected_nearest) = projectPointOnSegment(
-        web_mercator::fromWGS84(segment_source), web_mercator::fromWGS84(segment_target),
-        web_mercator::fromWGS84(query_location));
+    std::tie(ratio, projected_nearest) =
+        projectPointOnSegment(web_mercator::fromWGS84(segment_source),
+                              web_mercator::fromWGS84(segment_target),
+                              web_mercator::fromWGS84(query_location));
     nearest_location = web_mercator::toWGS84(projected_nearest);
 
     const double approximate_distance = greatCircleDistance(query_location, nearest_location);
@@ -106,8 +106,8 @@ double perpendicularDistance(const Coordinate source_coordinate,
     double ratio;
     Coordinate nearest_location;
 
-    return perpendicularDistance(source_coordinate, target_coordinate, query_location,
-                                 nearest_location, ratio);
+    return perpendicularDistance(
+        source_coordinate, target_coordinate, query_location, nearest_location, ratio);
 }
 
 Coordinate centroid(const Coordinate lhs, const Coordinate rhs)
diff --git a/src/util/guidance/bearing_class.cpp b/src/util/guidance/bearing_class.cpp
new file mode 100644
index 0000000..4b58524
--- /dev/null
+++ b/src/util/guidance/bearing_class.cpp
@@ -0,0 +1,81 @@
+#include "util/guidance/bearing_class.hpp"
+#include "extractor/guidance/discrete_angle.hpp"
+#include "util/guidance/toolkit.hpp"
+
+#include <algorithm>
+#include <boost/assert.hpp>
+
+namespace osrm
+{
+namespace util
+{
+namespace guidance
+{
+
+bool BearingClass::operator==(const BearingClass &other) const
+{
+    BOOST_ASSERT(std::is_sorted(available_bearings.begin(), available_bearings.end()));
+    BOOST_ASSERT(std::is_sorted(other.available_bearings.begin(), other.available_bearings.end()));
+    if (other.available_bearings.size() != available_bearings.size())
+        return false;
+    for (std::size_t i = 0; i < available_bearings.size(); ++i)
+        if (available_bearings[i] != other.available_bearings[i])
+            return false;
+    return true;
+}
+
+bool BearingClass::operator<(const BearingClass &other) const
+{
+    BOOST_ASSERT(std::is_sorted(available_bearings.begin(), available_bearings.end()));
+    BOOST_ASSERT(std::is_sorted(other.available_bearings.begin(), other.available_bearings.end()));
+    if (available_bearings.size() < other.available_bearings.size())
+        return true;
+    if (available_bearings.size() > other.available_bearings.size())
+        return false;
+
+    for (std::size_t i = 0; i < available_bearings.size(); ++i)
+    {
+        if (available_bearings[i] < other.available_bearings[i])
+            return true;
+        if (available_bearings[i] > other.available_bearings[i])
+            return false;
+    }
+
+    return false;
+}
+
+void BearingClass::add(const DiscreteBearing bearing) { available_bearings.push_back(bearing); }
+
+const std::vector<DiscreteBearing> &BearingClass::getAvailableBearings() const
+{
+    return available_bearings;
+}
+
+DiscreteBearing BearingClass::getDiscreteBearing(const double bearing)
+{
+    BOOST_ASSERT(0. <= bearing && bearing <= 360.);
+    auto shifted_bearing = (bearing + 0.5 * discrete_step_size);
+    if (shifted_bearing > 360.)
+        shifted_bearing -= 360;
+    return static_cast<DiscreteBearing>(shifted_bearing / discrete_step_size);
+}
+
+std::size_t BearingClass::findMatchingBearing(const double bearing) const
+{
+    BOOST_ASSERT(!available_bearings.empty());
+    // the small size of the intersections allows a linear compare
+    auto discrete_bearing = static_cast<DiscreteBearing>(bearing);
+    auto max_element =
+        std::max_element(available_bearings.begin(),
+                         available_bearings.end(),
+                         [&](const DiscreteBearing first, const DiscreteBearing second) {
+                             return angularDeviation(first, discrete_bearing) >
+                                    angularDeviation(second, discrete_bearing);
+                         });
+
+    return std::distance(available_bearings.begin(), max_element);
+}
+
+} // namespace guidance
+} // namespace extractor
+} // namespace osrm
diff --git a/src/util/guidance/entry_class.cpp b/src/util/guidance/entry_class.cpp
new file mode 100644
index 0000000..9140eb0
--- /dev/null
+++ b/src/util/guidance/entry_class.cpp
@@ -0,0 +1,38 @@
+#include "util/guidance/entry_class.hpp"
+
+#include <boost/assert.hpp>
+
+namespace osrm
+{
+namespace util
+{
+namespace guidance
+{
+
+EntryClass::EntryClass() : enabled_entries_flags(0) {}
+
+void EntryClass::activate(std::uint32_t index)
+{
+    BOOST_ASSERT(index < 8 * sizeof(FlagBaseType));
+    enabled_entries_flags |= (1 << index);
+}
+
+bool EntryClass::allowsEntry(std::uint32_t index) const
+{
+    BOOST_ASSERT(index < 8 * sizeof(FlagBaseType));
+    return 0 != (enabled_entries_flags & (1 << index));
+}
+
+bool EntryClass::operator==(const EntryClass &other) const
+{
+    return enabled_entries_flags == other.enabled_entries_flags;
+}
+
+bool EntryClass::operator<(const EntryClass &other) const
+{
+    return enabled_entries_flags < other.enabled_entries_flags;
+}
+
+} // namespace guidance
+} // namespace extractor
+} // namespace osrm
diff --git a/src/util/name_table.cpp b/src/util/name_table.cpp
index fedc31f..1a2e407 100644
--- a/src/util/name_table.cpp
+++ b/src/util/name_table.cpp
@@ -1,5 +1,5 @@
-#include "util/exception.hpp"
 #include "util/name_table.hpp"
+#include "util/exception.hpp"
 #include "util/simple_logger.hpp"
 
 #include <algorithm>
@@ -57,7 +57,8 @@ std::string NameTable::GetNameForID(const unsigned name_id) const
     {
         result.resize(range.back() - range.front() + 1);
         std::copy(m_names_char_list.begin() + range.front(),
-                  m_names_char_list.begin() + range.back() + 1, result.begin());
+                  m_names_char_list.begin() + range.back() + 1,
+                  result.begin());
     }
     return result;
 }
diff --git a/taginfo.json b/taginfo.json
index 574b1dc..8d3df9d 100644
--- a/taginfo.json
+++ b/taginfo.json
@@ -57,6 +57,7 @@
         {"key": "maxspeed:advisory"},
         {"key": "maxspeed:advisory:forward"},
         {"key": "maxspeed:advisory:backward"},
+        {"key": "name:pronunciation", "description": "Pronunciation hint for names", "object_types": ["way"]},
         {"key": "bridge", "value": "movable", "description": "uses capacity and duration"},
         {"key": "capacity:car", "description": "used for movable bridges"},
         {"key": "side_road", "value": "yes", "description": "gets speed penalty"},
diff --git a/third_party/fast-cpp-csv-parser/LICENSE b/third_party/fast-cpp-csv-parser/LICENSE
deleted file mode 100644
index da603a9..0000000
--- a/third_party/fast-cpp-csv-parser/LICENSE
+++ /dev/null
@@ -1,28 +0,0 @@
-Copyright (c) 2015, ben-strasser
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-
-* Redistributions of source code must retain the above copyright notice, this
-  list of conditions and the following disclaimer.
-
-* Redistributions in binary form must reproduce the above copyright notice,
-  this list of conditions and the following disclaimer in the documentation
-  and/or other materials provided with the distribution.
-
-* Neither the name of fast-cpp-csv-parser nor the names of its
-  contributors may be used to endorse or promote products derived from
-  this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
diff --git a/third_party/fast-cpp-csv-parser/README.md b/third_party/fast-cpp-csv-parser/README.md
deleted file mode 100644
index 546fa1e..0000000
--- a/third_party/fast-cpp-csv-parser/README.md
+++ /dev/null
@@ -1,252 +0,0 @@
-# Fast C++ Csv Parser
-
-This is a small, easy-to-use and fast header-only library for reading comma separated value (CSV) files. 
-
-## Features
-
-  * Automatically rearranges columns by parsing the header line.
-  * Disk I/O and CSV-parsing are overlapped using threads for efficiency.
-  * Parsing features such as escaped strings can be enabled and disabled at compile time using templates. You only pay in speed for the features you actually use.
-  * Can read multiple GB files in reasonable time.
-  * Support for custom columns separators (i.e. Tab separated value files are supported), quote escaped strings, automatic space trimming. 
-  * Works with `*`nix and Windows newlines and automatically ignores UTF-8 BOMs.
-  * Exception classes with enough context to format useful error messages. what() returns error messages ready to be shown to a user. 
-
-## Getting Started
-
-The following small example should contain most of the syntax you need to use the library.
-
-```cpp
-# include "csv.h"
-
-int main(){
-  io::CSVReader<3> in("ram.csv");
-  in.read_header(io::ignore_extra_column, "vendor", "size", "speed");
-  std::string vendor; int size; double speed;
-  while(in.read_row(vendor, size, speed)){
-    // do stuff with the data
-  }
-}
-```
-
-## Installation
-
-The library only needs a standard conformant C++11 compiler. It has no further dependencies. The library is completely contained inside a single header file and therefore it is sufficient to copy this file to some place on your include path. The library does not have to be explicitly build. 
-
-Note however, that std::future is used and some compiler (f.e. GCC) require you to link against additional libraries (i.e. -lpthread) to make it work. With GCC it is important to add -lpthread as the last item when linking, i.e. the order in 
-
-```
-g++ a.o b.o -o prog -lpthread
-```
-
-is important.
-
-Remember that the library makes use of C++11 features and therefore you have to enable support for it (f.e. add -std=C++0x or -std=gnu++0x).
-
-The library was developed and tested with GCC 4.6.1
-
-Note that VS2013 is not C++11 compilant and will therefore not work out of the box. See [here](https://code.google.com/p/fast-cpp-csv-parser/issues/detail?id=6) for what needs to be adjusted to make the code work.
-
-## Documentation
-
-The libary provides two classes: 
-
-  * `LineReader`: A class to efficiently read large files line by line.
-  * `CSVReader`: A class that efficiently reads large CSV files.
-
-Note that everything is contained in the `io` namespace.
-
-### `LineReader`
-
-```cpp
-class LineReader{
-public:
-  // Constructors
-  LineReader(some_string_type file_name);
-  LineReader(some_string_type file_name, std::FILE*file);
-
-  // Reading
-  char*next_line();
-
-  // File Location
-  void set_file_line(unsigned);
-  unsigned get_file_line(unsigned)const;
-  void set_file_name(some_string_type file_name);
-  const char*get_truncated_file_name()const;
-};
-```
-
-The constructor takes a file name and optionally a `stdio.h` file handle. If no file handle is provided the class tries to open the file and throws an `error::can_not_open_file exception` on failure. If a file handle is provided then the file name is only used to format error messages. The library will call `std::fclose` on the file handle. `some_string_type` can be a `std::string` or a `char*`.
-
-Lines are read by calling the `next_line` function. It returns a pointer to a null terminated C-string that contains the line. If the end of file is reached a null pointer is returned. The newline character is not included in the string. You may modify the string as long as you do not write past the null terminator. The string stays valid until the destructor is called or until next_line is called again. Windows and `*`nix newlines are handled transparently. UTF-8 BOMs are automatically  [...]
-
-**Important:** There is a limit of 2^24-1 characters per line. If this limit is exceeded a `error::line_length_limit_exceeded` exception is thrown.
-
-Looping over all the lines in a file can be done in the following way.
-```cpp
-LineReader in(...);
-while(char*line = in.next_line()){
-  ...
-}
-```
-
-The remaining functions are mainly used used to format error messages. The file line indicates the current position in the file, i.e., after the first `next_line` call it is 1 and after the second 2. Before the first call it is 0. The file name is truncated as internally C-strings are used to avoid `std::bad_alloc` exceptions during error reporting.
-
-**Note:** It is not possible to exchange the line termination character.
-
-### `CSVReader`
-
-`CSVReader` uses policies. These are classes with only static members to allow core functionality to be exchanged in an efficient way.
-
-```cpp
-template<
-  unsigned column_count,
-  class trim_policy = trim_chars<' ', '\t'>, 
-  class quote_policy = no_quote_escape<','>,
-  class overflow_policy = throw_on_overflow,
-  class comment_policy = no_comment
->
-class CSVReader{
-public:
-  // Constructors
-  CSVReader(some_string_type file_name);
-  CSVReader(some_string_type file_name, std::FILE*file);
-
-  // Parsing Header
-  void read_header(ignore_column ignore_policy, some_string_type col_name1, some_string_type col_name2, ...);
-  void set_header(some_string_type col_name1, some_string_type col_name2, ...);
-  bool has_column(some_string_type col_name)const;
-
-  // Read
-  bool read_row(ColType1&col1, ColType2&col2, ...);
-
-  // File Location 
-  void set_file_line(unsigned);
-  unsigned get_file_line(unsigned)const;
-  void set_file_name(some_string_type file_name);
-  const char*get_truncated_file_name()const;
-};
-```
-
-The `column_count` template parameter indicates how many columns you want to read from the CSV file. This must not necessarily coincide with the actual number of columns in the file. The three policies govern various aspects of the parsing.
-
-The trim policy indicates what characters should be ignored at the begin and the end of every column. The default ignores spaces and tabs. This makes sure that
-
-```
-a,b,c
-1,2,3
-```
-
-is interpreted in the same way as
-
-```
-  a, b,   c
-1  , 2,   3
-```
-
-The trim_chars can take any number of template parameters. For example `trim_chars<' ', '\t', '_'> `is also valid. If no character should be trimmed use `trim_chars<>`.
-
-The quote policy indicates how string should be escaped. It also specifies the column separator. The predefined policies are:
-
-  * `no_quote_escape<sep>` : Strings are not escaped. "`sep`" is used as column separator.
-  * `double_quote_escape<sep, quote>` : Strings are escaped using quotes. Quotes are escaped using two consecutive quotes. "`sep`" is used as column separator and "`quote`" as quoting character.
-
-**Important**: When combining trimming and quoting the rows are first trimmed and then unquoted. A consequence is that spaces inside the quotes will be conserved. If you want to get rid of spaces inside the quotes, you need to remove them yourself.
-
-**Important**: Quoting can be quite expensive. Disable it if you do not need it.
-
-The overflow policy indicates what should be done if the integers in the input are too large to fit into the variables. There following policies are predefined:
-
-  * `throw_on_overflow` : Throw an `error::integer_overflow` or `error::integer_underflow` exception.
-  * `ignore_overflow` : Do nothing and let the overflow happen.
-  * `set_to_max_on_overflow` : Set the value to `numeric_limits<...>::max()` (or to the min-pendant).
-
-The comment policy allows to skip lines based on some criteria. Valid predefined policies are:
-
-  * `no_comment` : Do not ignore any line.
-  * `empty_line_comment` : Ignore all lines that are empty or only contains spaces and tabs. 
-  * `single_line_comment<com1, com2, ...>` : Ignore all lines that start with com1 or com2 or ... as the first character. There may not be any space between the beginning of the line and the comment character. 
-  * `single_and_empty_line_comment<com1, com2, ...>` : Ignore all empty lines and single line comments.
-
-Examples:
-
-  * `CSVReader<4, trim_chars<' '>, double_quote_escape<',','\"'> >` reads 4 columns from a normal CSV file with string escaping enabled.
-  * `CSVReader<3, trim_chars<' '>, no_quote_escape<'\t'>, single_line_comment<'#'> >` reads 3 columns from a tab separated file with string escaping disabled. Lines starting with a # are ignored.
-
-The constructors and the file location functions are exactly the same as for `LineReader`. See its documentation for details.
-
-There are three methods that deal with headers. The `read_header` methods reads a line from the file and rearranges the columns to match that order. It also checks whether all necessary columns are present. The `set_header` method does *not* read any input. Use it if the file does not have any header. Obviously it is impossible to rearrange columns or check for their availability when using it. The order in the file and in the program must match when using `set_header`. The `has_column`  [...]
-
-  * `ignore_no_column`: The default behavior, no flags are set
-  * `ignore_extra_column`: If a column with a name is in the file but not in the argument list, then it is silently ignored.
-  * `ignore_missing_column`: If a column with a name is not in the file but is in the argument list, then `read_row` will not modify the corresponding variable. 
-
-When using `ignore_column_missing` it is a good idea to initialize the variables passed to `read_row` with a default value, for example:
-
-```cpp
-// The file only contains column "a"
-CSVReader<2>in(...);
-in.read_header(ignore_missing_column, "a", "b");
-int a,b = 42;
-while(in.read_row(a,b)){
-  // a contains the value from the file
-  // b is left unchanged by read_row, i.e., it is 42
-}
-```
-
-If only some columns are optional or their default value depends on other columns you have to use `has_column`, for example:
-
-```cpp
-// The file only contains the columns "a" and "b"
-CSVReader<2>in(...);
-in.read_header(ignore_missing_column, "a", "b", "sum");
-if(!in.has_column("a") || !in.has_column("b"))
-  throw my_neat_error_class();
-bool has_sum = in.has_column("sum");
-int a,b,sum;
-while(in.read_row(a,b,sum)){
-  if(!has_sum)
-    sum = a+b;
-}
-```
-
-**Important**: Do not call `has_column` from within the read-loop. It would work correctly but significantly slowdown processing.
-
-If two columns have the same name an error::duplicated_column_in_header exception is thrown. If `read_header` is called but the file is empty a `error::header_missing` exception is thrown.
-
-The `read_row` function reads a line, splits it into the columns and arranges them correctly. It trims the entries and unescapes them. If requested the content is interpreted as integer or as floating point. The variables passed to read_row may be of the following types.
-
-  * builtin signed integer: These are `signed char`, `short`, `int`, `long` and `long long`. The input must be encoded as a base 10 ASCII number optionally preceded by a + or -. The function detects whether the integer is too large would overflow (or underflow) and behaves as indicated by overflow_policy.
-  * builtin unsigned integer: Just as the signed counterparts except that a leading + or - is not allowed.
-  * builtin floating point: These are `float`, `double` and `long double`. The input may have a leading + or -. The number must be base 10 encoded. The decimal point may either be a dot or a comma. (Note that a comma will only work if it is not also used as column separator or the number is escaped.) A base 10 exponent may be specified using the "1e10" syntax. The "e" may be lower- or uppercase. Examples for valid floating points are "1", "-42.42" and "+123.456E789". The input is rounded [...]
-  * `char`: The column content must be a single character.
-  * `std::string`: The column content is assigned to the string. The std::string is filled with the trimmed and unescaped version.
-  * `char*`: A pointer directly into the buffer. The string is trimmed and unescaped and null terminated. This pointer stays valid until read_row is called again or the CSVReader is destroyed. Use this for user defined types. 
-
-Note that there is no inherent overhead to using `char*` and then interpreting it compared to using one of the parsers directly build into `CSVReader`. The builtin number parsers are pure convenience. If you need a slightly different syntax then use `char*` and do the parsing yourself.
-
-## FAQ
-
-Q: The library is throwing a std::system_error with code -1. How to get it to work?
-
-A: Your compiler's std::thread implementation is broken. Define CSV\_IO\_NO\_THREAD to disable threading support.
-
-
-Q: My values are not just ints or strings. I want to parse my customized type. Is this possible?
-
-A: Read a `char*` and parse the string. At first this seems expensive but it is not as the pointer you get points directly into the memory buffer. In fact there is no inherent reason why a custom int-parser realized this way must be any slower than the int-parser build into the library. By reading a `char*` the library takes care of column reordering and quote escaping and leaves the actual parsing to you. Note that using a std::string is slower as it involves a memory copy.
-
-
-Q: I get lots of compiler errors when compiling the header! Please fix it. :(
-
-A: Have you enabled the C++11 mode of your compiler? If you use GCC you have to add -std=c++0x to the commandline. If this does not resolve the problem, then please open a ticket.
-
-
-Q: The library crashes when parsing large files! Please fix it. :(
-
-A: When using GCC have you linked against -lpthread? Read the installation section for details on how to do this. If this does not resolve the issue then please open a ticket. (The reason why it only crashes only on large files is that the first chuck is read synchronous and if the whole file fits into this chuck then no asynchronous call is performed.) Alternatively you can define CSV\_IO\_NO\_THREAD.
-
-
-Q: Does the library support UTF?
-
-A: The library has basic UTF-8 support, or to be more precise it does not break when passing UTF-8 strings through it. If you read a `char*` then you get a pointer to the UTF-8 string. You will have to decode the string on your own. The separator, quoting, and commenting characters used by the library can only be ASCII characters.
diff --git a/third_party/fast-cpp-csv-parser/csv.h b/third_party/fast-cpp-csv-parser/csv.h
deleted file mode 100644
index 3f0371d..0000000
--- a/third_party/fast-cpp-csv-parser/csv.h
+++ /dev/null
@@ -1,1068 +0,0 @@
-// Copyright: (2012-2014) Ben Strasser <code at ben-strasser.net>
-// License: BSD-3
-//
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-//    this list of conditions and the following disclaimer.
-//
-//2. Redistributions in binary form must reproduce the above copyright notice,
-//   this list of conditions and the following disclaimer in the documentation
-//   and/or other materials provided with the distribution.
-//
-//3. Neither the name of the copyright holder nor the names of its contributors
-//   may be used to endorse or promote products derived from this software
-//   without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-
-#ifndef CSV_H
-#define CSV_H
-
-#include <vector>
-#include <string>
-#include <cstring>
-#include <algorithm>
-#include <utility>
-#include <cstdio>
-#include <exception>
-#ifndef CSV_IO_NO_THREAD
-#include <future>
-#endif
-#include <cassert>
-#include <cerrno>
-
-namespace io{
-        ////////////////////////////////////////////////////////////////////////////
-        //                                 LineReader                             //
-        ////////////////////////////////////////////////////////////////////////////
-
-        namespace error{
-                struct base : std::exception{
-                        virtual void format_error_message()const = 0;                          
-                       
-                        const char*what()const throw(){
-                                format_error_message();
-                                return error_message_buffer;
-                        }
-
-                        mutable char error_message_buffer[256];
-                };
-
-                const int max_file_name_length = 255;
-
-                struct with_file_name{
-                        with_file_name(){
-                                std::memset(file_name, 0, max_file_name_length+1);
-                        }
-                       
-                        void set_file_name(const char*file_name){
-                                std::strncpy(this->file_name, file_name, max_file_name_length);
-                                this->file_name[max_file_name_length] = '\0';
-                        }
-
-                        char file_name[max_file_name_length+1];
-                };
-
-                struct with_file_line{
-                        with_file_line(){
-                                file_line = -1;
-                        }
-                       
-                        void set_file_line(int file_line){
-                                this->file_line = file_line;
-                        }
-
-                        int file_line;
-                };
-
-                struct with_errno{
-                        with_errno(){
-                                errno_value = 0;
-                        }
-                       
-                        void set_errno(int errno_value){
-                                this->errno_value = errno_value;
-                        }
-
-                        int errno_value;
-                };
-
-                struct can_not_open_file :
-                        base,
-                        with_file_name,
-                        with_errno{
-                        void format_error_message()const{
-                                if(errno_value != 0)
-                                        std::snprintf(error_message_buffer, sizeof(error_message_buffer),
-                                                "Can not open file \"%s\" because \"%s\"."
-                                                , file_name, std::strerror(errno_value));
-                                else
-                                        std::snprintf(error_message_buffer, sizeof(error_message_buffer),
-                                                "Can not open file \"%s\"."
-                                                , file_name);
-                        }
-                };
-
-                struct line_length_limit_exceeded :
-                        base,
-                        with_file_name,
-                        with_file_line{
-                        void format_error_message()const{
-                                std::snprintf(error_message_buffer, sizeof(error_message_buffer),
-                                        "Line number %d in file \"%s\" exceeds the maximum length of 2^24-1."
-                                        , file_line, file_name);
-                        }
-                };
-        }
-
-        class LineReader{
-        private:
-                static const int block_len = 1<<24;
-                #ifndef CSV_IO_NO_THREAD
-                std::future<int>bytes_read;
-                #endif
-                FILE*file;
-                char*buffer;
-                int data_begin;
-                int data_end;
-
-                char file_name[error::max_file_name_length+1];
-                unsigned file_line;
-
-                void open_file(const char*file_name){
-                        // We open the file in binary mode as it makes no difference under *nix
-                        // and under Windows we handle \r\n newlines ourself.
-                        file = std::fopen(file_name, "rb");
-                        if(file == 0){
-                                int x = errno; // store errno as soon as possible, doing it after constructor call can fail.
-                                error::can_not_open_file err;
-                                err.set_errno(x);
-                                err.set_file_name(file_name);
-                                throw err;
-                        }
-                }
-
-                void init(){
-                        file_line = 0;
-
-                        // Tell the std library that we want to do the buffering ourself.
-                        std::setvbuf(file, 0, _IONBF, 0);
-
-                        try{
-                                buffer = new char[3*block_len];
-                        }catch(...){
-                                std::fclose(file);
-                                throw;
-                        }
-
-                        data_begin = 0;
-                        data_end = std::fread(buffer, 1, 2*block_len, file);
-
-                        // Ignore UTF-8 BOM
-                        if(data_end >= 3 && buffer[0] == '\xEF' && buffer[1] == '\xBB' && buffer[2] == '\xBF')
-                                data_begin = 3;
-
-                        #ifndef CSV_IO_NO_THREAD
-                        if(data_end == 2*block_len){
-                                bytes_read = std::async(std::launch::async, [=]()->int{
-                                        return std::fread(buffer + 2*block_len, 1, block_len, file);
-                                });
-                        }
-                        #endif
-                }
-
-        public:
-                LineReader() = delete;
-                LineReader(const LineReader&) = delete;
-                LineReader&operator=(const LineReader&) = delete;
-
-                LineReader(const char*file_name, FILE*file):
-                        file(file){
-                        set_file_name(file_name);
-                        init();
-                }
-
-                LineReader(const std::string&file_name, FILE*file):
-                        file(file){
-                        set_file_name(file_name.c_str());
-                        init();
-                }
-
-                explicit LineReader(const char*file_name){
-                        set_file_name(file_name);
-                        open_file(file_name);
-                        init();
-                }
-
-                explicit LineReader(const std::string&file_name){
-                        set_file_name(file_name.c_str());
-                        open_file(file_name.c_str());
-                        init();
-                }
-
-                void set_file_name(const std::string&file_name){
-                        set_file_name(file_name.c_str());
-                }
-
-                void set_file_name(const char*file_name){
-                        strncpy(this->file_name, file_name, error::max_file_name_length);
-                        this->file_name[error::max_file_name_length] = '\0';
-                }
-
-                const char*get_truncated_file_name()const{
-                        return file_name;
-                }
-
-                void set_file_line(unsigned file_line){
-                        this->file_line = file_line;
-                }
-
-                unsigned get_file_line()const{
-                        return file_line;
-                }
-
-                char*next_line(){
-                        if(data_begin == data_end)
-                                return 0;
-
-                        ++file_line;
-
-                        assert(data_begin < data_end);
-                        assert(data_end <= block_len*2);
-
-                        if(data_begin >= block_len){
-                                std::memcpy(buffer, buffer+block_len, block_len);
-                                data_begin -= block_len;
-                                data_end -= block_len;
-                                #ifndef CSV_IO_NO_THREAD
-                                if(bytes_read.valid())
-                                #endif
-                                {
-                                        #ifndef CSV_IO_NO_THREAD
-                                        data_end += bytes_read.get();
-                                        #else
-                                        data_end += std::fread(buffer + 2*block_len, 1, block_len, file);
-                                        #endif
-                                        std::memcpy(buffer+block_len, buffer+2*block_len, block_len);
-
-                                        #ifndef CSV_IO_NO_THREAD
-                                        bytes_read = std::async(std::launch::async, [=]()->int{
-                                                return std::fread(buffer + 2*block_len, 1, block_len, file);
-                                        });
-                                        #endif
-                                }
-                        }
-
-                        int line_end = data_begin;
-                        while(buffer[line_end] != '\n' && line_end != data_end){
-                                ++line_end;
-                        }
-
-                        if(line_end - data_begin + 1 > block_len){
-                                error::line_length_limit_exceeded err;
-                                err.set_file_name(file_name);
-                                err.set_file_line(file_line);
-                                throw err;
-                        }
-
-                        if(buffer[line_end] == '\n'){
-                                buffer[line_end] = '\0';
-                        }else{
-                                // some files are missing the newline at the end of the
-                                // last line
-                                ++data_end;
-                                buffer[line_end] = '\0';
-                        }
-
-                        // handle windows \r\n-line breaks
-                        if(line_end != data_begin && buffer[line_end-1] == '\r')
-                                buffer[line_end-1] = '\0';
-
-                        char*ret = buffer + data_begin;
-                        data_begin = line_end+1;
-                        return ret;
-                }
-
-                ~LineReader(){
-                        #ifndef CSV_IO_NO_THREAD
-                        // GCC needs this or it will crash.
-                        if(bytes_read.valid())
-                                bytes_read.get();
-                        #endif
-
-                        delete[] buffer;
-                        std::fclose(file);
-                }
-        };
-
-        ////////////////////////////////////////////////////////////////////////////
-        //                                 CSV                                    //
-        ////////////////////////////////////////////////////////////////////////////
-
-        namespace error{
-                const int max_column_name_length = 63;
-                struct with_column_name{
-                        with_column_name(){
-                                std::memset(column_name, 0, max_column_name_length+1);
-                        }
-                       
-                        void set_column_name(const char*column_name){
-                                std::strncpy(this->column_name, column_name, max_column_name_length);
-                                this->column_name[max_column_name_length] = '\0';
-                        }
-
-                        char column_name[max_column_name_length+1];
-                };
-
-
-                const int max_column_content_length = 63;
-
-                struct with_column_content{
-                        with_column_content(){
-                                std::memset(column_content, 0, max_column_content_length+1);
-                        }
-                       
-                        void set_column_content(const char*column_content){
-                                std::strncpy(this->column_content, column_content, max_column_content_length);
-                                this->column_content[max_column_content_length] = '\0';
-                        }
-
-                        char column_content[max_column_content_length+1];
-                };
-
-
-                struct extra_column_in_header :
-                        base,
-                        with_file_name,
-                        with_column_name{
-                        void format_error_message()const{
-                                std::snprintf(error_message_buffer, sizeof(error_message_buffer),
-                                        "Extra column \"%s\" in header of file \"%s\"."
-                                        , column_name, file_name);
-                        }
-                };
-
-                struct missing_column_in_header :
-                        base,
-                        with_file_name,
-                        with_column_name{
-                        void format_error_message()const{
-                                std::snprintf(error_message_buffer, sizeof(error_message_buffer),
-                                        "Missing column \"%s\" in header of file \"%s\"."
-                                        , column_name, file_name);
-                        }
-                };
-
-                struct duplicated_column_in_header :
-                        base,
-                        with_file_name,
-                        with_column_name{
-                        void format_error_message()const{
-                                std::snprintf(error_message_buffer, sizeof(error_message_buffer),
-                                        "Duplicated column \"%s\" in header of file \"%s\"."
-                                        , column_name, file_name);
-                        }
-                };
-
-                struct header_missing :
-                        base,
-                        with_file_name{
-                        void format_error_message()const{
-                                std::snprintf(error_message_buffer, sizeof(error_message_buffer),
-                                        "Header missing in file \"%s\"."
-                                        , file_name);
-                        }
-                };
-
-                struct too_few_columns :
-                        base,
-                        with_file_name,
-                        with_file_line{
-                        void format_error_message()const{
-                                std::snprintf(error_message_buffer, sizeof(error_message_buffer),
-                                        "Too few columns in line %d in file \"%s\"."
-                                        , file_line, file_name);
-                        }
-                };
-
-                struct too_many_columns :
-                        base,
-                        with_file_name,
-                        with_file_line{
-                        void format_error_message()const{
-                                std::snprintf(error_message_buffer, sizeof(error_message_buffer),
-                                        "Too many columns in line %d in file \"%s\"."
-                                        , file_line, file_name);
-                        }
-                };
-
-                struct escaped_string_not_closed :
-                        base,
-                        with_file_name,
-                        with_file_line{
-                        void format_error_message()const{
-                                std::snprintf(error_message_buffer, sizeof(error_message_buffer),
-                                        "Escaped string was not closed in line %d in file \"%s\"."
-                                        , file_line, file_name);
-                        }
-                };
-
-                struct integer_must_be_positive :
-                        base,
-                        with_file_name,
-                        with_file_line,
-                        with_column_name,
-                        with_column_content{
-                        void format_error_message()const{
-                                std::snprintf(error_message_buffer, sizeof(error_message_buffer),
-                                        "The integer \"%s\" must be positive or 0 in column \"%s\" in file \"%s\" in line \"%d\"."
-                                        , column_content, column_name, file_name, file_line);
-                        }
-                };
-
-                struct no_digit :
-                        base,
-                        with_file_name,
-                        with_file_line,
-                        with_column_name,
-                        with_column_content{
-                        void format_error_message()const{
-                                std::snprintf(error_message_buffer, sizeof(error_message_buffer),
-                                        "The integer \"%s\" contains an invalid digit in column \"%s\" in file \"%s\" in line \"%d\"."
-                                        , column_content, column_name, file_name, file_line);
-                        }
-                };
-
-                struct integer_overflow :
-                        base,
-                        with_file_name,
-                        with_file_line,
-                        with_column_name,
-                        with_column_content{
-                        void format_error_message()const{
-                                std::snprintf(error_message_buffer, sizeof(error_message_buffer),
-                                        "The integer \"%s\" overflows in column \"%s\" in file \"%s\" in line \"%d\"."
-                                        , column_content, column_name, file_name, file_line);
-                        }
-                };
-
-                struct integer_underflow :
-                        base,
-                        with_file_name,
-                        with_file_line,
-                        with_column_name,
-                        with_column_content{
-                        void format_error_message()const{
-                                std::snprintf(error_message_buffer, sizeof(error_message_buffer),
-                                        "The integer \"%s\" underflows in column \"%s\" in file \"%s\" in line \"%d\"."
-                                        , column_content, column_name, file_name, file_line);
-                        }
-                };
-
-                struct invalid_single_character :
-                        base,
-                        with_file_name,
-                        with_file_line,
-                        with_column_name,
-                        with_column_content{
-                        void format_error_message()const{
-                                std::snprintf(error_message_buffer, sizeof(error_message_buffer),
-                                        "The content \"%s\" of column \"%s\" in file \"%s\" in line \"%d\" is not a single character."
-                                        , column_content, column_name, file_name, file_line);
-                        }
-                };
-        }
-
-        typedef unsigned ignore_column;
-        static const ignore_column ignore_no_column = 0;
-        static const ignore_column ignore_extra_column = 1;
-        static const ignore_column ignore_missing_column = 2;
-
-        template<char ... trim_char_list>
-        struct trim_chars{
-        private:
-                constexpr static bool is_trim_char(char c){
-                        return false;
-                }
-       
-                template<class ...OtherTrimChars>
-                constexpr static bool is_trim_char(char c, char trim_char, OtherTrimChars...other_trim_chars){
-                        return c == trim_char || is_trim_char(c, other_trim_chars...);
-                }
-
-        public:
-                static void trim(char*&str_begin, char*&str_end){
-                        while(is_trim_char(*str_begin, trim_char_list...) && str_begin != str_end)
-                                ++str_begin;
-                        while(is_trim_char(*(str_end-1), trim_char_list...) && str_begin != str_end)
-                                --str_end;
-                        *str_end = '\0';
-                }
-        };
-
-
-        struct no_comment{
-                static bool is_comment(const char*line){
-                        return false;
-                }
-        };
-
-        template<char ... comment_start_char_list>
-        struct single_line_comment{
-        private:
-                constexpr static bool is_comment_start_char(char c){
-                        return false;
-                }
-       
-                template<class ...OtherCommentStartChars>
-                constexpr static bool is_comment_start_char(char c, char comment_start_char, OtherCommentStartChars...other_comment_start_chars){
-                        return c == comment_start_char || is_comment_start_char(c, other_comment_start_chars...);
-                }
-
-        public:
-
-                static bool is_comment(const char*line){
-                        return is_comment_start_char(*line, comment_start_char_list...);
-                }
-        };
-
-        struct empty_line_comment{
-                static bool is_comment(const char*line){
-                        if(*line == '\0')
-                                return true;
-                        while(*line == ' ' || *line == '\t'){
-                                ++line;
-                                if(*line == 0)
-                                        return true;
-                        }
-                        return false;
-                }
-        };
-
-        template<char ... comment_start_char_list>
-        struct single_and_empty_line_comment{
-                static bool is_comment(const char*line){
-                        return single_line_comment<comment_start_char_list...>::is_comment(line) || empty_line_comment::is_comment(line);
-                }
-        };
-
-        template<char sep>
-        struct no_quote_escape{
-                static const char*find_next_column_end(const char*col_begin){
-                        while(*col_begin != sep && *col_begin != '\0')
-                                ++col_begin;
-                        return col_begin;
-                }
-
-                static void unescape(char*&col_begin, char*&col_end){
-
-                }
-        };
-
-        template<char sep, char quote>
-        struct double_quote_escape{
-                static const char*find_next_column_end(const char*col_begin){
-                        while(*col_begin != sep && *col_begin != '\0')
-                                if(*col_begin != quote)
-                                        ++col_begin;
-                                else{
-                                        do{
-                                                ++col_begin;
-                                                while(*col_begin != quote){
-                                                        if(*col_begin == '\0')
-                                                                throw error::escaped_string_not_closed();
-                                                        ++col_begin;
-                                                }
-                                                ++col_begin;
-                                        }while(*col_begin == quote);
-                                }      
-                        return col_begin;      
-                }
-
-                static void unescape(char*&col_begin, char*&col_end){
-                        if(col_end - col_begin >= 2){
-                                if(*col_begin == quote && *(col_end-1) == quote){
-                                        ++col_begin;
-                                        --col_end;
-                                        char*out = col_begin;
-                                        for(char*in = col_begin; in!=col_end; ++in){
-                                                if(*in == quote && *(in+1) == quote){
-                                                         ++in;
-                                                }
-                                                *out = *in;
-                                                ++out;
-                                        }
-                                        col_end = out;
-                                        *col_end = '\0';
-                                }
-                        }
-                       
-                }
-        };
-
-        struct throw_on_overflow{
-                template<class T>
-                static void on_overflow(T&){
-                        throw error::integer_overflow();
-                }
-               
-                template<class T>
-                static void on_underflow(T&){
-                        throw error::integer_underflow();
-                }
-        };
-
-        struct ignore_overflow{
-                template<class T>
-                static void on_overflow(T&){}
-               
-                template<class T>
-                static void on_underflow(T&){}
-        };
-
-        struct set_to_max_on_overflow{
-                template<class T>
-                static void on_overflow(T&x){
-                        x = std::numeric_limits<T>::max();
-                }
-               
-                template<class T>
-                static void on_underflow(T&x){
-                        x = std::numeric_limits<T>::min();
-                }
-        };
-
-
-        namespace detail{
-                template<class quote_policy>
-                void chop_next_column(
-                        char*&line, char*&col_begin, char*&col_end
-                ){
-                        assert(line != nullptr);
-
-                        col_begin = line;
-                        // the col_begin + (... - col_begin) removes the constness
-                        col_end = col_begin + (quote_policy::find_next_column_end(col_begin) - col_begin);
-                       
-                        if(*col_end == '\0'){
-                                line = nullptr;
-                        }else{
-                                *col_end = '\0';
-                                line = col_end + 1;    
-                        }
-                }
-
-                template<class trim_policy, class quote_policy>
-                void parse_line(
-                        char*line,
-                        char**sorted_col,
-                        const std::vector<int>&col_order
-                ){
-                        for(std::size_t i=0; i<col_order.size(); ++i){
-                                if(line == nullptr)
-                                        throw ::io::error::too_few_columns();
-                                char*col_begin, *col_end;
-                                chop_next_column<quote_policy>(line, col_begin, col_end);
-
-                                if(col_order[i] != -1){
-                                        trim_policy::trim(col_begin, col_end);
-                                        quote_policy::unescape(col_begin, col_end);
-                                                               
-                                        sorted_col[col_order[i]] = col_begin;
-                                }
-                        }
-                        if(line != nullptr)
-                                throw ::io::error::too_many_columns();
-                }
-
-                template<unsigned column_count, class trim_policy, class quote_policy>
-                void parse_header_line(
-                        char*line,
-                        std::vector<int>&col_order,
-                        const std::string*col_name,
-                        ignore_column ignore_policy
-                ){
-                        col_order.clear();
-
-                        bool found[column_count];
-                        std::fill(found, found + column_count, false);
-                        while(line){
-                                char*col_begin,*col_end;
-                                chop_next_column<quote_policy>(line, col_begin, col_end);
-
-                                trim_policy::trim(col_begin, col_end);
-                                quote_policy::unescape(col_begin, col_end);
-                               
-                                for(unsigned i=0; i<column_count; ++i)
-                                        if(col_begin == col_name[i]){
-                                                if(found[i]){
-                                                        error::duplicated_column_in_header err;
-                                                        err.set_column_name(col_begin);
-                                                        throw err;
-                                                }
-                                                found[i] = true;
-                                                col_order.push_back(i);
-                                                col_begin = 0;
-                                                break;
-                                        }
-                                if(col_begin){
-                                        if(ignore_policy & ::io::ignore_extra_column)
-                                                col_order.push_back(-1);
-                                        else{
-                                                error::extra_column_in_header err;
-                                                err.set_column_name(col_begin);
-                                                throw err;
-                                        }
-                                }
-                        }
-                        if(!(ignore_policy & ::io::ignore_missing_column)){
-                                for(unsigned i=0; i<column_count; ++i){
-                                        if(!found[i]){
-                                                error::missing_column_in_header err;
-                                                err.set_column_name(col_name[i].c_str());
-                                                throw err;
-                                        }
-                                }
-                        }
-                }
-
-                template<class overflow_policy>
-                void parse(char*col, char &x){
-                        if(!*col)
-                                throw error::invalid_single_character();
-                        x = *col;
-                        ++col;
-                        if(*col)
-                                throw error::invalid_single_character();
-                }
-               
-                template<class overflow_policy>
-                void parse(char*col, std::string&x){
-                        x = col;
-                }
-
-                template<class overflow_policy>
-                void parse(char*col, const char*&x){
-                        x = col;
-                }
-
-                template<class overflow_policy>
-                void parse(char*col, char*&x){
-                        x = col;
-                }
-
-                template<class overflow_policy, class T>
-                void parse_unsigned_integer(const char*col, T&x){
-                        x = 0;
-                        while(*col != '\0'){
-                                if('0' <= *col && *col <= '9'){
-                                        T y = *col - '0';
-                                        if(x > (std::numeric_limits<T>::max()-y)/10){
-                                                overflow_policy::on_overflow(x);
-                                                return;
-                                        }
-                                        x = 10*x+y;
-                                }else
-                                        throw error::no_digit();
-                                ++col;
-                        }
-                }
-
-                template<class overflow_policy>void parse(char*col, unsigned char &x)
-                        {parse_unsigned_integer<overflow_policy>(col, x);}
-                template<class overflow_policy>void parse(char*col, unsigned short &x)
-                        {parse_unsigned_integer<overflow_policy>(col, x);}
-                template<class overflow_policy>void parse(char*col, unsigned int &x)
-                        {parse_unsigned_integer<overflow_policy>(col, x);}
-                template<class overflow_policy>void parse(char*col, unsigned long &x)
-                        {parse_unsigned_integer<overflow_policy>(col, x);}
-                template<class overflow_policy>void parse(char*col, unsigned long long &x)
-                        {parse_unsigned_integer<overflow_policy>(col, x);}
-               
-                template<class overflow_policy, class T>
-                void parse_signed_integer(const char*col, T&x){
-                        if(*col == '-'){
-                                ++col;
-
-                                x = 0;
-                                while(*col != '\0'){
-                                        if('0' <= *col && *col <= '9'){
-                                                T y = *col - '0';
-                                                if(x < (std::numeric_limits<T>::min()+y)/10){
-                                                        overflow_policy::on_underflow(x);
-                                                        return;
-                                                }
-                                                x = 10*x-y;
-                                        }else
-                                                throw error::no_digit();
-                                        ++col;
-                                }
-                                return;
-                        }else if(*col == '+')
-                                ++col;
-                        parse_unsigned_integer<overflow_policy>(col, x);
-                }      
-
-                template<class overflow_policy>void parse(char*col, signed char &x)
-                        {parse_signed_integer<overflow_policy>(col, x);}
-                template<class overflow_policy>void parse(char*col, signed short &x)
-                        {parse_signed_integer<overflow_policy>(col, x);}
-                template<class overflow_policy>void parse(char*col, signed int &x)
-                        {parse_signed_integer<overflow_policy>(col, x);}
-                template<class overflow_policy>void parse(char*col, signed long &x)
-                        {parse_signed_integer<overflow_policy>(col, x);}
-                template<class overflow_policy>void parse(char*col, signed long long &x)
-                        {parse_signed_integer<overflow_policy>(col, x);}
-
-                template<class T>
-                void parse_float(const char*col, T&x){
-                        bool is_neg = false;
-                        if(*col == '-'){
-                                is_neg = true;
-                                ++col;
-                        }else if(*col == '+')
-                                ++col;
-
-                        x = 0;
-                        while('0' <= *col && *col <= '9'){
-                                int y = *col - '0';
-                                x *= 10;
-                                x += y;
-                                ++col;
-                        }
-                       
-                        if(*col == '.'|| *col == ','){
-                                ++col;
-                                T pos = 1;
-                                while('0' <= *col && *col <= '9'){
-                                        pos /= 10;
-                                        int y = *col - '0';
-                                        ++col;
-                                        x += y*pos;
-                                }
-                        }
-
-                        if(*col == 'e' || *col == 'E'){
-                                ++col;
-                                int e;
-
-                                parse_signed_integer<set_to_max_on_overflow>(col, e);
-                               
-                                if(e != 0){
-                                        T base;
-                                        if(e < 0){
-                                                base = 0.1;
-                                                e = -e;
-                                        }else{
-                                                base = 10;
-                                        }
-       
-                                        while(e != 1){
-                                                if((e & 1) == 0){
-                                                        base = base*base;
-                                                        e >>= 1;
-                                                }else{
-                                                        x *= base;
-                                                        --e;
-                                                }
-                                        }
-                                        x *= base;
-                                }
-                        }else{
-                                if(*col != '\0')
-                                        throw error::no_digit();
-                        }
-
-                        if(is_neg)
-                                x = -x;
-                }
-
-                template<class overflow_policy> void parse(char*col, float&x) { parse_float(col, x); }
-                template<class overflow_policy> void parse(char*col, double&x) { parse_float(col, x); }
-                template<class overflow_policy> void parse(char*col, long double&x) { parse_float(col, x); }
-
-                template<class overflow_policy, class T>
-                void parse(char*col, T&x){
-                        // GCC evalutes "false" when reading the template and
-                        // "sizeof(T)!=sizeof(T)" only when instantiating it. This is why
-                        // this strange construct is used.
-                        static_assert(sizeof(T)!=sizeof(T),
-                                "Can not parse this type. Only buildin integrals, floats, char, char*, const char* and std::string are supported");
-                }
-
-        }
-
-        template<unsigned column_count,
-                class trim_policy = trim_chars<' ', '\t'>,
-                class quote_policy = no_quote_escape<','>,
-                class overflow_policy = throw_on_overflow,
-                class comment_policy = no_comment
-        >
-        class CSVReader{
-        private:
-                LineReader in;
-
-                char*(row[column_count]);
-                std::string column_names[column_count];
-
-                std::vector<int>col_order;
-
-                template<class ...ColNames>
-                void set_column_names(std::string s, ColNames...cols){
-                        column_names[column_count-sizeof...(ColNames)-1] = std::move(s);
-                        set_column_names(std::forward<ColNames>(cols)...);
-                }
-
-                void set_column_names(){}
-
-
-        public:
-                CSVReader() = delete;
-                CSVReader(const CSVReader&) = delete;
-                CSVReader&operator=(const CSVReader&);
-
-                template<class ...Args>
-                explicit CSVReader(Args...args):in(std::forward<Args>(args)...){
-                        std::fill(row, row+column_count, nullptr);
-                        col_order.resize(column_count);
-                        for(unsigned i=0; i<column_count; ++i)
-                                col_order[i] = i;
-                        for(unsigned i=1; i<=column_count; ++i)
-                                column_names[i-1] = "col"+std::to_string(i);
-                }
-
-                template<class ...ColNames>
-                void read_header(ignore_column ignore_policy, ColNames...cols){
-                        static_assert(sizeof...(ColNames)>=column_count, "not enough column names specified");
-                        static_assert(sizeof...(ColNames)<=column_count, "too many column names specified");
-                        try{
-                                set_column_names(std::forward<ColNames>(cols)...);
-
-                                char*line;
-                                do{
-                                        line = in.next_line();
-                                        if(!line)
-                                                throw error::header_missing();
-                                }while(comment_policy::is_comment(line));
-
-                                detail::parse_header_line
-                                        <column_count, trim_policy, quote_policy>
-                                        (line, col_order, column_names, ignore_policy);
-                        }catch(error::with_file_name&err){
-                                err.set_file_name(in.get_truncated_file_name());
-                                throw;
-                        }
-                }
-
-                template<class ...ColNames>
-                void set_header(ColNames...cols){
-                        static_assert(sizeof...(ColNames)>=column_count,
-                                "not enough column names specified");
-                        static_assert(sizeof...(ColNames)<=column_count,
-                                "too many column names specified");
-                        set_column_names(std::forward<ColNames>(cols)...);
-                        std::fill(row, row+column_count, nullptr);
-                        col_order.resize(column_count);
-                        for(unsigned i=0; i<column_count; ++i)
-                                col_order[i] = i;
-                }
-
-                bool has_column(const std::string&name) const {
-                        return col_order.end() != std::find(
-                                col_order.begin(), col_order.end(),
-                                        std::find(std::begin(column_names), std::end(column_names), name)
-                                - std::begin(column_names));
-                }
-
-                void set_file_name(const std::string&file_name){
-                        in.set_file_name(file_name);
-                }
-
-                void set_file_name(const char*file_name){
-                        in.set_file_name(file_name);
-                }
-
-                const char*get_truncated_file_name()const{
-                        return in.get_truncated_file_name();
-                }
-
-                void set_file_line(unsigned file_line){
-                        in.set_file_line(file_line);
-                }
-
-                unsigned get_file_line()const{
-                        return in.get_file_line();
-                }
-
-        private:
-                void parse_helper(std::size_t r){}
-
-                template<class T, class ...ColType>
-                void parse_helper(std::size_t r, T&t, ColType&...cols){                        
-                        if(row[r]){
-                                try{
-                                        try{
-                                                ::io::detail::parse<overflow_policy>(row[r], t);
-                                        }catch(error::with_column_content&err){
-                                                err.set_column_content(row[r]);
-                                                throw;
-                                        }
-                                }catch(error::with_column_name&err){
-                                        err.set_column_name(column_names[r].c_str());
-                                        throw;
-                                }
-                        }
-                        parse_helper(r+1, cols...);
-                }
-
-       
-        public:
-                template<class ...ColType>
-                bool read_row(ColType& ...cols){
-                        static_assert(sizeof...(ColType)>=column_count,
-                                "not enough columns specified");
-                        static_assert(sizeof...(ColType)<=column_count,
-                                "too many columns specified");
-                        try{
-                                try{
-       
-                                        char*line;
-                                        do{
-                                                line = in.next_line();
-                                                if(!line)
-                                                        return false;
-                                        }while(comment_policy::is_comment(line));
-                                       
-                                        detail::parse_line<trim_policy, quote_policy>
-                                                (line, row, col_order);
-               
-                                        parse_helper(0, cols...);
-                                }catch(error::with_file_name&err){
-                                        err.set_file_name(in.get_truncated_file_name());
-                                        throw;
-                                }
-                        }catch(error::with_file_line&err){
-                                err.set_file_line(in.get_file_line());
-                                throw;
-                        }
-
-                        return true;
-                }
-        };
-}
-#endif
-
diff --git a/unit_tests/CMakeLists.txt b/unit_tests/CMakeLists.txt
index 63e10c7..6371904 100644
--- a/unit_tests/CMakeLists.txt
+++ b/unit_tests/CMakeLists.txt
@@ -49,7 +49,7 @@ set(AllBoostLibrariesExceptUnitTest ${Boost_LIBRARIES})
 
 find_package(Boost 1.49.0 REQUIRED COMPONENTS unit_test_framework)
 
-if(NOT WIN32)
+if(NOT WIN32 AND NOT Boost_USE_STATIC_LIBS)
   add_definitions(-DBOOST_TEST_DYN_LINK)
 endif()
 
@@ -67,7 +67,7 @@ target_include_directories(util-tests PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
 target_link_libraries(engine-tests ${ENGINE_LIBRARIES} ${BoostUnitTestLibrary})
 target_link_libraries(extractor-tests ${EXTRACTOR_LIBRARIES} ${BoostUnitTestLibrary})
 target_link_libraries(library-tests osrm ${Boost_LIBRARIES} ${BoostUnitTestLibrary})
-target_link_libraries(server-tests osrm ${Boost_LIBRARIES} ${BoostUnitTestLibrary} ${ZLIB_LIBRARY})
+target_link_libraries(server-tests osrm ${Boost_LIBRARIES} ${BoostUnitTestLibrary})
 target_link_libraries(util-tests ${UTIL_LIBRARIES} ${BoostUnitTestLibrary})
 
 
diff --git a/unit_tests/engine/base64.cpp b/unit_tests/engine/base64.cpp
index f0515fc..b5c2aa3 100644
--- a/unit_tests/engine/base64.cpp
+++ b/unit_tests/engine/base64.cpp
@@ -1,12 +1,12 @@
 #include "engine/base64.hpp"
-#include "engine/hint.hpp"
 #include "mocks/mock_datafacade.hpp"
+#include "engine/hint.hpp"
 
-#include <boost/test/unit_test.hpp>
 #include <boost/test/test_case_template.hpp>
+#include <boost/test/unit_test.hpp>
 
-#include <iostream>
 #include <algorithm>
+#include <iostream>
 
 // RFC 4648 "The Base16, Base32, and Base64 Data Encodings"
 BOOST_AUTO_TEST_SUITE(base64)
diff --git a/unit_tests/engine/douglas_peucker.cpp b/unit_tests/engine/douglas_peucker.cpp
index 2a1a7a3..6b43823 100644
--- a/unit_tests/engine/douglas_peucker.cpp
+++ b/unit_tests/engine/douglas_peucker.cpp
@@ -1,8 +1,8 @@
 #include "engine/douglas_peucker.hpp"
 #include "util/coordinate_calculation.hpp"
 
-#include <boost/test/unit_test.hpp>
 #include <boost/test/test_case_template.hpp>
+#include <boost/test/unit_test.hpp>
 
 #include <osrm/coordinate.hpp>
 
@@ -79,7 +79,6 @@ BOOST_AUTO_TEST_CASE(removed_middle_test_zoom_sensitive)
     }
 }
 
-
 BOOST_AUTO_TEST_CASE(remove_second_node_test)
 {
     // derived from the degreeToPixel function
@@ -99,14 +98,11 @@ BOOST_AUTO_TEST_CASE(remove_second_node_test)
                 x
         */
         std::vector<util::Coordinate> input = {
-            util::Coordinate(util::FloatLongitude(5),
-                             util::FloatLatitude(5)),
+            util::Coordinate(util::FloatLongitude(5), util::FloatLatitude(5)),
             util::Coordinate(util::FloatLongitude(5 + delta_pixel_to_delta_degree(2, z)),
                              util::FloatLatitude(5)),
-            util::Coordinate(util::FloatLongitude(10),
-                             util::FloatLatitude(10)),
-            util::Coordinate(util::FloatLongitude(5),
-                             util::FloatLatitude(15)),
+            util::Coordinate(util::FloatLongitude(10), util::FloatLatitude(10)),
+            util::Coordinate(util::FloatLongitude(5), util::FloatLatitude(15)),
             util::Coordinate(util::FloatLongitude(5),
                              util::FloatLatitude(15 + delta_pixel_to_delta_degree(2, z)))};
         BOOST_TEST_MESSAGE("Delta (" << z << "): " << delta_pixel_to_delta_degree(2, z));
diff --git a/unit_tests/engine/geometry_string.cpp b/unit_tests/engine/geometry_string.cpp
index 5209460..8f78810 100644
--- a/unit_tests/engine/geometry_string.cpp
+++ b/unit_tests/engine/geometry_string.cpp
@@ -36,9 +36,11 @@ BOOST_AUTO_TEST_CASE(decode)
     for (unsigned i = 0; i < cmp_coords.size(); ++i)
     {
         BOOST_CHECK_CLOSE(static_cast<double>(util::toFloating(coords[i].lat)),
-                          static_cast<double>(util::toFloating(cmp_coords[i].lat)), 0.0001);
+                          static_cast<double>(util::toFloating(cmp_coords[i].lat)),
+                          0.0001);
         BOOST_CHECK_CLOSE(static_cast<double>(util::toFloating(coords[i].lon)),
-                          static_cast<double>(util::toFloating(cmp_coords[i].lon)), 0.0001);
+                          static_cast<double>(util::toFloating(cmp_coords[i].lon)),
+                          0.0001);
     }
 }
 
diff --git a/unit_tests/engine/guidance_assembly.cpp b/unit_tests/engine/guidance_assembly.cpp
index 6f50529..2387b76 100644
--- a/unit_tests/engine/guidance_assembly.cpp
+++ b/unit_tests/engine/guidance_assembly.cpp
@@ -1,11 +1,11 @@
-#include "engine/guidance/assemble_overview.hpp"
 #include "engine/guidance/assemble_geometry.hpp"
-#include "engine/guidance/assemble_steps.hpp"
-#include "engine/guidance/assemble_route.hpp"
 #include "engine/guidance/assemble_leg.hpp"
+#include "engine/guidance/assemble_overview.hpp"
+#include "engine/guidance/assemble_route.hpp"
+#include "engine/guidance/assemble_steps.hpp"
 
-#include <boost/test/unit_test.hpp>
 #include <boost/test/test_case_template.hpp>
+#include <boost/test/unit_test.hpp>
 
 BOOST_AUTO_TEST_SUITE(guidance_assembly)
 
diff --git a/unit_tests/extractor/compressed_edge_container.cpp b/unit_tests/extractor/compressed_edge_container.cpp
index 593ec59..87d5496 100644
--- a/unit_tests/extractor/compressed_edge_container.cpp
+++ b/unit_tests/extractor/compressed_edge_container.cpp
@@ -1,8 +1,8 @@
 #include "extractor/compressed_edge_container.hpp"
 #include "util/typedefs.hpp"
 
-#include <boost/test/unit_test.hpp>
 #include <boost/test/test_case_template.hpp>
+#include <boost/test/unit_test.hpp>
 
 BOOST_AUTO_TEST_SUITE(compressed_edge_container)
 
diff --git a/unit_tests/extractor/graph_compressor.cpp b/unit_tests/extractor/graph_compressor.cpp
index 7a6da7a..98dd33f 100644
--- a/unit_tests/extractor/graph_compressor.cpp
+++ b/unit_tests/extractor/graph_compressor.cpp
@@ -4,8 +4,8 @@
 #include "util/node_based_graph.hpp"
 #include "util/typedefs.hpp"
 
-#include <boost/test/unit_test.hpp>
 #include <boost/test/test_case_template.hpp>
+#include <boost/test/unit_test.hpp>
 
 #include <iostream>
 
diff --git a/unit_tests/extractor/raster_source.cpp b/unit_tests/extractor/raster_source.cpp
index 606f273..25a4289 100644
--- a/unit_tests/extractor/raster_source.cpp
+++ b/unit_tests/extractor/raster_source.cpp
@@ -23,8 +23,8 @@ int normalize(double coord) { return static_cast<int>(coord * COORDINATE_PRECISI
 BOOST_AUTO_TEST_CASE(raster_test)
 {
     SourceContainer sources;
-    int source_id = sources.LoadRasterSource("../unit_tests/fixtures/raster_data.asc", 0, 0.09, 0,
-                                             0.09, 10, 10);
+    int source_id = sources.LoadRasterSource(
+        "../unit_tests/fixtures/raster_data.asc", 0, 0.09, 0, 0.09, 10, 10);
     BOOST_CHECK_EQUAL(source_id, 0);
 
     // Expected nearest-neighbor queries
diff --git a/unit_tests/library/args.hpp b/unit_tests/library/args.hpp
index b595e32..3244d4e 100644
--- a/unit_tests/library/args.hpp
+++ b/unit_tests/library/args.hpp
@@ -1,8 +1,8 @@
 #ifndef OSRM_UNIT_TEST_ARGS
 #define OSRM_UNIT_TEST_ARGS
 
-#include <vector>
 #include <string>
+#include <vector>
 
 inline std::vector<std::string> get_args()
 {
diff --git a/unit_tests/library/limits.cpp b/unit_tests/library/limits.cpp
index 8632171..ad4b3fb 100644
--- a/unit_tests/library/limits.cpp
+++ b/unit_tests/library/limits.cpp
@@ -1,18 +1,18 @@
-#include <boost/test/unit_test.hpp>
 #include <boost/test/test_case_template.hpp>
+#include <boost/test/unit_test.hpp>
 
 #include "args.hpp"
 
-#include "osrm/trip_parameters.hpp"
+#include "osrm/match_parameters.hpp"
 #include "osrm/route_parameters.hpp"
 #include "osrm/table_parameters.hpp"
-#include "osrm/match_parameters.hpp"
+#include "osrm/trip_parameters.hpp"
 
 #include "osrm/coordinate.hpp"
 #include "osrm/engine_config.hpp"
 #include "osrm/json_container.hpp"
-#include "osrm/status.hpp"
 #include "osrm/osrm.hpp"
+#include "osrm/status.hpp"
 
 BOOST_AUTO_TEST_SUITE(limits)
 
diff --git a/unit_tests/library/match.cpp b/unit_tests/library/match.cpp
index b6b01ab..528c95c 100644
--- a/unit_tests/library/match.cpp
+++ b/unit_tests/library/match.cpp
@@ -1,9 +1,9 @@
-#include <boost/test/unit_test.hpp>
 #include <boost/test/test_case_template.hpp>
+#include <boost/test/unit_test.hpp>
 
 #include "args.hpp"
-#include "fixture.hpp"
 #include "coordinates.hpp"
+#include "fixture.hpp"
 #include "waypoint_check.hpp"
 
 #include "osrm/match_parameters.hpp"
@@ -11,8 +11,8 @@
 #include "osrm/coordinate.hpp"
 #include "osrm/engine_config.hpp"
 #include "osrm/json_container.hpp"
-#include "osrm/status.hpp"
 #include "osrm/osrm.hpp"
+#include "osrm/status.hpp"
 
 BOOST_AUTO_TEST_SUITE(match)
 
@@ -49,14 +49,21 @@ BOOST_AUTO_TEST_CASE(test_match)
         {
             BOOST_CHECK(waypoint_check(waypoint));
             const auto &waypoint_object = waypoint.get<json::Object>();
-            const auto matchings_index = waypoint_object.values.at("matchings_index").get<json::Number>().value;
-            const auto waypoint_index = waypoint_object.values.at("waypoint_index").get<json::Number>().value;
-            const auto &route_legs = matchings[matchings_index].get<json::Object>().values.at("legs").get<json::Array>().values;
+            const auto matchings_index =
+                waypoint_object.values.at("matchings_index").get<json::Number>().value;
+            const auto waypoint_index =
+                waypoint_object.values.at("waypoint_index").get<json::Number>().value;
+            const auto &route_legs = matchings[matchings_index]
+                                         .get<json::Object>()
+                                         .values.at("legs")
+                                         .get<json::Array>()
+                                         .values;
             BOOST_CHECK_LT(waypoint_index, route_legs.size() + 1);
             BOOST_CHECK_LT(matchings_index, number_of_matchings);
-        } else
+        }
+        else
         {
-          BOOST_CHECK(waypoint.is<json::Null>());
+            BOOST_CHECK(waypoint.is<json::Null>());
         }
     }
 }
diff --git a/unit_tests/library/nearest.cpp b/unit_tests/library/nearest.cpp
index 1685d94..fe51282 100644
--- a/unit_tests/library/nearest.cpp
+++ b/unit_tests/library/nearest.cpp
@@ -1,17 +1,17 @@
-#include <boost/test/unit_test.hpp>
 #include <boost/test/test_case_template.hpp>
+#include <boost/test/unit_test.hpp>
 
 #include "args.hpp"
-#include "fixture.hpp"
 #include "coordinates.hpp"
+#include "fixture.hpp"
 
 #include "osrm/nearest_parameters.hpp"
 
 #include "osrm/coordinate.hpp"
 #include "osrm/engine_config.hpp"
 #include "osrm/json_container.hpp"
-#include "osrm/status.hpp"
 #include "osrm/osrm.hpp"
+#include "osrm/status.hpp"
 
 BOOST_AUTO_TEST_SUITE(nearest)
 
diff --git a/unit_tests/library/route.cpp b/unit_tests/library/route.cpp
index a090208..06248ed 100644
--- a/unit_tests/library/route.cpp
+++ b/unit_tests/library/route.cpp
@@ -55,30 +55,45 @@ BOOST_AUTO_TEST_CASE(test_route_same_coordinates_fixture)
                 json::Array{{json::Object{
                     {{"distance", 0.},
                      {"duration", 0.},
-                     {"summary", ""},
-                     {"steps", json::Array{{json::Object{{{"duration", 0.},
-                                                          {"distance", 0.},
-                                                          {"geometry", "yw_jGupkl@??"},
-                                                          {"name", "Boulevard du Larvotto"},
-                                                          {"mode", "driving"},
-                                                          {"maneuver", json::Object{{
-                                                                           {"type", "depart"},
-                                                                           {"location", location},
-                                                                           {"bearing_before", 0.},
-                                                                           {"bearing_after", 0.},
-                                                                       }}}}},
-
-                                            json::Object{{{"duration", 0.},
-                                                          {"distance", 0.},
-                                                          {"geometry", "yw_jGupkl@"},
-                                                          {"name", "Boulevard du Larvotto"},
-                                                          {"mode", "driving"},
-                                                          {"maneuver", json::Object{{
-                                                                           {"type", "arrive"},
-                                                                           {"location", location},
-                                                                           {"bearing_before", 0.},
-                                                                           {"bearing_after", 0.},
-                                                                       }}}}}}}}}}}}}}}}}}}};
+                     {"summary", "Boulevard du Larvotto"},
+                     {"steps",
+                      json::Array{{{json::Object{{{"duration", 0.},
+                                                  {"distance", 0.},
+                                                  {"geometry", "yw_jGupkl@??"},
+                                                  {"name", "Boulevard du Larvotto"},
+                                                  {"mode", "driving"},
+                                                  {"maneuver",
+                                                   json::Object{{
+                                                       {"location", location},
+                                                       {"bearing_before", 0},
+                                                       {"bearing_after", 0},
+                                                       {"type", "depart"},
+                                                   }}},
+                                                  {"intersections",
+                                                   json::Array{{json::Object{
+                                                       {{"location", location},
+                                                        {"bearings", json::Array{{0}}},
+                                                        {"entry", json::Array{{json::True()}}},
+                                                        {"out", 0}}}}}}}}},
+
+                                   json::Object{{{"duration", 0.},
+                                                 {"distance", 0.},
+                                                 {"geometry", "yw_jGupkl@"},
+                                                 {"name", "Boulevard du Larvotto"},
+                                                 {"mode", "driving"},
+                                                 {"maneuver",
+                                                  json::Object{{{"location", location},
+                                                                {"bearing_before", 0},
+                                                                {"bearing_after", 0},
+                                                                {"type", "arrive"}}}},
+                                                 {"intersections",
+                                                  json::Array{{json::Object{
+                                                      {{"location", location},
+                                                       {"bearings", json::Array{{180}}},
+                                                       {"entry", json::Array{{json::True()}}},
+                                                       {"in", 0}}}}}}
+
+                                   }}}}}}}}}}}}}}}}};
 
     CHECK_EQUAL_JSON(reference, result);
 }
@@ -161,6 +176,8 @@ BOOST_AUTO_TEST_CASE(test_route_same_coordinates)
             const auto &steps = leg_object.values.at("steps").get<json::Array>().values;
             BOOST_CHECK(!steps.empty());
 
+            std::size_t step_count = 0;
+
             for (const auto &step : steps)
             {
                 const auto &step_object = step.get<json::Object>();
@@ -185,25 +202,50 @@ BOOST_AUTO_TEST_CASE(test_route_same_coordinates)
 
                 const auto &maneuver = step_object.values.at("maneuver").get<json::Object>().values;
 
-                const auto location = maneuver.at("location").get<json::Array>().values;
-                const auto longitude = location[0].get<json::Number>().value;
-                const auto latitude = location[1].get<json::Number>().value;
-                BOOST_CHECK(longitude >= -180. && longitude <= 180.);
-                BOOST_CHECK(latitude >= -90. && latitude <= 90.);
-
-                const auto bearing_before = maneuver.at("bearing_before").get<json::Number>().value;
-                const auto bearing_after = maneuver.at("bearing_after").get<json::Number>().value;
-                BOOST_CHECK(bearing_before >= 0. && bearing_before <= 360.);
-                BOOST_CHECK(bearing_after >= 0. && bearing_after <= 360.);
-
                 const auto type = maneuver.at("type").get<json::String>().value;
                 BOOST_CHECK(!type.empty());
 
+                const auto &intersections =
+                    step_object.values.at("intersections").get<json::Array>().values;
+
+                for (auto &intersection : intersections)
+                {
+                    const auto &intersection_object = intersection.get<json::Object>().values;
+                    const auto location =
+                        intersection_object.at("location").get<json::Array>().values;
+                    const auto longitude = location[0].get<json::Number>().value;
+                    const auto latitude = location[1].get<json::Number>().value;
+                    BOOST_CHECK(longitude >= -180. && longitude <= 180.);
+                    BOOST_CHECK(latitude >= -90. && latitude <= 90.);
+
+                    const auto &bearings =
+                        intersection_object.at("bearings").get<json::Array>().values;
+                    BOOST_CHECK(!bearings.empty());
+                    const auto &entries = intersection_object.at("entry").get<json::Array>().values;
+                    BOOST_CHECK(bearings.size() == entries.size());
+
+                    for (const auto bearing : bearings)
+                        BOOST_CHECK(0. <= bearing.get<json::Number>().value &&
+                                    bearing.get<json::Number>().value <= 360.);
+
+                    if (step_count > 0)
+                    {
+                        const auto in = intersection_object.at("in").get<json::Number>().value;
+                        BOOST_CHECK(in < bearings.size());
+                    }
+                    if (step_count + 1 < steps.size())
+                    {
+                        const auto out = intersection_object.at("out").get<json::Number>().value;
+                        BOOST_CHECK(out < bearings.size());
+                    }
+                }
+
                 // modifier is optional
                 // TODO(daniel-j-h):
 
                 // exit is optional
                 // TODO(daniel-j-h):
+                ++step_count;
             }
         }
     }
diff --git a/unit_tests/library/table.cpp b/unit_tests/library/table.cpp
index 43b44a8..5d2d946 100644
--- a/unit_tests/library/table.cpp
+++ b/unit_tests/library/table.cpp
@@ -1,5 +1,5 @@
-#include <boost/test/unit_test.hpp>
 #include <boost/test/test_case_template.hpp>
+#include <boost/test/unit_test.hpp>
 
 #include "args.hpp"
 #include "coordinates.hpp"
@@ -11,8 +11,8 @@
 #include "osrm/coordinate.hpp"
 #include "osrm/engine_config.hpp"
 #include "osrm/json_container.hpp"
-#include "osrm/status.hpp"
 #include "osrm/osrm.hpp"
+#include "osrm/status.hpp"
 
 BOOST_AUTO_TEST_SUITE(table)
 
@@ -47,7 +47,8 @@ BOOST_AUTO_TEST_CASE(test_table_three_coords_one_source_one_dest_matrix)
     for (unsigned int i = 0; i < durations_array.size(); i++)
     {
         const auto durations_matrix = durations_array[i].get<json::Array>().values;
-        BOOST_CHECK_EQUAL(durations_matrix.size(), params.sources.size()*params.destinations.size());
+        BOOST_CHECK_EQUAL(durations_matrix.size(),
+                          params.sources.size() * params.destinations.size());
     }
     // check destinations array of waypoint objects
     const auto &destinations_array = result.values.at("destinations").get<json::Array>().values;
@@ -96,7 +97,8 @@ BOOST_AUTO_TEST_CASE(test_table_three_coords_one_source_matrix)
     {
         const auto durations_matrix = durations_array[i].get<json::Array>().values;
         BOOST_CHECK_EQUAL(durations_matrix[i].get<json::Number>().value, 0);
-        BOOST_CHECK_EQUAL(durations_matrix.size(), params.sources.size()*params.coordinates.size());
+        BOOST_CHECK_EQUAL(durations_matrix.size(),
+                          params.sources.size() * params.coordinates.size());
     }
     // check destinations array of waypoint objects
     const auto &destinations_array = result.values.at("destinations").get<json::Array>().values;
diff --git a/unit_tests/library/tile.cpp b/unit_tests/library/tile.cpp
index 6cacc30..258735e 100644
--- a/unit_tests/library/tile.cpp
+++ b/unit_tests/library/tile.cpp
@@ -1,5 +1,5 @@
-#include <boost/test/unit_test.hpp>
 #include <boost/test/test_case_template.hpp>
+#include <boost/test/unit_test.hpp>
 
 #include "args.hpp"
 #include "fixture.hpp"
@@ -9,8 +9,8 @@
 #include "osrm/coordinate.hpp"
 #include "osrm/engine_config.hpp"
 #include "osrm/json_container.hpp"
-#include "osrm/status.hpp"
 #include "osrm/osrm.hpp"
+#include "osrm/status.hpp"
 
 #include "util/vector_tile.hpp"
 
@@ -54,19 +54,22 @@ BOOST_AUTO_TEST_CASE(test_tile)
         BOOST_CHECK_EQUAL(feature_message.tag(), util::vector_tile::FEATURE_ATTRIBUTES_TAG);
         // properties
         std::tie(value_begin, value_end) = feature_message.get_packed_uint32();
-        BOOST_CHECK_EQUAL(std::distance(value_begin, value_end), 8);
+        BOOST_CHECK_EQUAL(std::distance(value_begin, value_end), 10);
         auto iter = value_begin;
         BOOST_CHECK_EQUAL(*iter++, 0); // speed key
-        BOOST_CHECK_LT(*iter++, 128); // speed value
+        BOOST_CHECK_LT(*iter++, 128);  // speed value
         BOOST_CHECK_EQUAL(*iter++, 1); // component key
         // component value
         BOOST_CHECK_GE(*iter, 128);
         BOOST_CHECK_LE(*iter, 129);
         iter++;
         BOOST_CHECK_EQUAL(*iter++, 2); // data source key
-        *iter++; // skip value check, can be valud uint32
+        *iter++;                       // skip value check, can be valud uint32
         BOOST_CHECK_EQUAL(*iter++, 3); // duration key
-        BOOST_CHECK_GT(*iter++, 130); // duration value
+        BOOST_CHECK_GT(*iter++, 130);  // duration value
+        // name
+        BOOST_CHECK_EQUAL(*iter++, 4);
+        BOOST_CHECK_GT(*iter++, 130);
         BOOST_CHECK(iter == value_end);
         // geometry
         feature_message.next();
@@ -77,20 +80,20 @@ BOOST_AUTO_TEST_CASE(test_tile)
     const auto check_value = [](protozero::pbf_reader value) {
         while (value.next())
         {
-            switch(value.tag())
+            switch (value.tag())
             {
-                case util::vector_tile::VARIANT_TYPE_BOOL:
-                    value.get_bool();
-                    break;
-                case util::vector_tile::VARIANT_TYPE_DOUBLE:
-                    value.get_double();
-                    break;
-                case util::vector_tile::VARIANT_TYPE_STRING:
-                    value.get_string();
-                    break;
-                case util::vector_tile::VARIANT_TYPE_UINT32:
-                    value.get_uint32();
-                    break;
+            case util::vector_tile::VARIANT_TYPE_BOOL:
+                value.get_bool();
+                break;
+            case util::vector_tile::VARIANT_TYPE_DOUBLE:
+                value.get_double();
+                break;
+            case util::vector_tile::VARIANT_TYPE_STRING:
+                value.get_string();
+                break;
+            case util::vector_tile::VARIANT_TYPE_UINT32:
+                value.get_uint32();
+                break;
             }
         }
     };
@@ -100,35 +103,35 @@ BOOST_AUTO_TEST_CASE(test_tile)
 
     while (layer_message.next())
     {
-        switch(layer_message.tag())
+        switch (layer_message.tag())
         {
-            case util::vector_tile::VERSION_TAG:
-                BOOST_CHECK_EQUAL(layer_message.get_uint32(), 2);
-                break;
-            case util::vector_tile::NAME_TAG:
-                BOOST_CHECK_EQUAL(layer_message.get_string(), "speeds");
-                break;
-            case util::vector_tile::EXTEND_TAG:
-                BOOST_CHECK_EQUAL(layer_message.get_uint32(), util::vector_tile::EXTENT);
-                break;
-            case util::vector_tile::FEATURE_TAG:
-                check_feature(layer_message.get_message());
-                break;
-            case util::vector_tile::KEY_TAG:
-                layer_message.get_string();
-                number_of_keys++;
-                break;
-            case util::vector_tile::VARIANT_TAG:
-                check_value(layer_message.get_message());
-                number_of_values++;
-                break;
-            default:
-                BOOST_CHECK(false); // invalid tag
-                break;
+        case util::vector_tile::VERSION_TAG:
+            BOOST_CHECK_EQUAL(layer_message.get_uint32(), 2);
+            break;
+        case util::vector_tile::NAME_TAG:
+            BOOST_CHECK_EQUAL(layer_message.get_string(), "speeds");
+            break;
+        case util::vector_tile::EXTEND_TAG:
+            BOOST_CHECK_EQUAL(layer_message.get_uint32(), util::vector_tile::EXTENT);
+            break;
+        case util::vector_tile::FEATURE_TAG:
+            check_feature(layer_message.get_message());
+            break;
+        case util::vector_tile::KEY_TAG:
+            layer_message.get_string();
+            number_of_keys++;
+            break;
+        case util::vector_tile::VARIANT_TAG:
+            check_value(layer_message.get_message());
+            number_of_values++;
+            break;
+        default:
+            BOOST_CHECK(false); // invalid tag
+            break;
         }
     }
 
-    BOOST_CHECK_EQUAL(number_of_keys, 4);
+    BOOST_CHECK_EQUAL(number_of_keys, 5);
     BOOST_CHECK_GT(number_of_values, 128); // speed value resolution
 }
 
diff --git a/unit_tests/library/trip.cpp b/unit_tests/library/trip.cpp
index e630706..4b68054 100644
--- a/unit_tests/library/trip.cpp
+++ b/unit_tests/library/trip.cpp
@@ -131,7 +131,8 @@ BOOST_AUTO_TEST_CASE(test_trip_response_for_locations_across_components)
 
     const auto &trips = result.values.at("trips").get<json::Array>().values;
     BOOST_CHECK_EQUAL(trips.size(), 1);
-    // ^ First snapping, then SCC decomposition (see plugins/trip.cpp). Therefore only a single trip.
+    // ^ First snapping, then SCC decomposition (see plugins/trip.cpp). Therefore only a single
+    // trip.
 
     for (const auto &waypoint : waypoints)
     {
diff --git a/unit_tests/mocks/mock_datafacade.hpp b/unit_tests/mocks/mock_datafacade.hpp
index a7ee834..c6fd567 100644
--- a/unit_tests/mocks/mock_datafacade.hpp
+++ b/unit_tests/mocks/mock_datafacade.hpp
@@ -3,9 +3,12 @@
 
 // implements all data storage when shared memory _IS_ used
 
+#include "contractor/query_edge.hpp"
 #include "extractor/guidance/turn_instruction.hpp"
 #include "engine/datafacade/datafacade_base.hpp"
-#include "contractor/query_edge.hpp"
+#include "util/guidance/bearing_class.hpp"
+#include "util/guidance/entry_class.hpp"
+#include "util/typedefs.hpp"
 
 namespace osrm
 {
@@ -47,6 +50,7 @@ class MockDataFacade final : public engine::datafacade::BaseDataFacade
     {
         return {util::FixedLongitude{0}, util::FixedLatitude{0}};
     }
+    OSMNodeID GetOSMNodeIDOfNode(const unsigned /* id */) const override { return OSMNodeID{0}; }
     bool EdgeIsCompressed(const unsigned /* id */) const { return false; }
     unsigned GetGeometryIndexForEdgeID(const unsigned /* id */) const override
     {
@@ -168,9 +172,33 @@ class MockDataFacade final : public engine::datafacade::BaseDataFacade
     bool IsCoreNode(const NodeID /* id */) const override { return false; }
     unsigned GetNameIndexFromEdgeID(const unsigned /* id */) const override { return 0; }
     std::string GetNameForID(const unsigned /* name_id */) const override { return ""; }
+    std::string GetPronunciationForID(const unsigned /* name_id */) const override { return ""; }
+    std::string GetDestinationsForID(const unsigned /* name_id */) const override { return ""; }
     std::size_t GetCoreSize() const override { return 0; }
     std::string GetTimestamp() const override { return ""; }
     bool GetContinueStraightDefault() const override { return true; }
+    BearingClassID GetBearingClassID(const NodeID /*id*/) const override { return 0; };
+    EntryClassID GetEntryClassID(const EdgeID /*id*/) const override { return 0; }
+
+    util::guidance::BearingClass
+    GetBearingClass(const BearingClassID /*bearing_class_id*/) const override
+    {
+        util::guidance::BearingClass result;
+        result.add(0);
+        result.add(90);
+        result.add(180);
+        result.add(270);
+        return result;
+    }
+
+    util::guidance::EntryClass GetEntryClass(const EntryClassID /*entry_class_id*/) const override
+    {
+        util::guidance::EntryClass result;
+        result.activate(1);
+        result.activate(2);
+        result.activate(3);
+        return result;
+    }
 };
 } // ns test
 } // ns osrm
diff --git a/unit_tests/server/parameters_parser.cpp b/unit_tests/server/parameters_parser.cpp
index 21ffd42..bf77b65 100644
--- a/unit_tests/server/parameters_parser.cpp
+++ b/unit_tests/server/parameters_parser.cpp
@@ -57,10 +57,11 @@ BOOST_AUTO_TEST_CASE(invalid_route_urls)
     BOOST_CHECK_EQUAL(testInvalidOptions<RouteParameters>("1,2;3,4.json?nooptions"), 13);
     BOOST_CHECK_EQUAL(testInvalidOptions<RouteParameters>("1,2;3,4..json?nooptions"), 14);
     BOOST_CHECK_EQUAL(testInvalidOptions<RouteParameters>("1,2;3,4.0.json?nooptions"), 15);
-    BOOST_CHECK_EQUAL(testInvalidOptions<RouteParameters>(std::string{"1,2;3,4"} + '\0' + ".json"), 7);
+    BOOST_CHECK_EQUAL(testInvalidOptions<RouteParameters>(std::string{"1,2;3,4"} + '\0' + ".json"),
+                      7);
     BOOST_CHECK_EQUAL(testInvalidOptions<RouteParameters>(std::string{"1,2;3,"} + '\0'), 6);
 
-    //BOOST_CHECK_EQUAL(testInvalidOptions<RouteParameters>(), );
+    // BOOST_CHECK_EQUAL(testInvalidOptions<RouteParameters>(), );
 }
 
 BOOST_AUTO_TEST_CASE(invalid_table_urls)
@@ -85,6 +86,7 @@ BOOST_AUTO_TEST_CASE(valid_route_urls)
     BOOST_CHECK_EQUAL(reference_1.steps, result_1->steps);
     BOOST_CHECK_EQUAL(reference_1.alternatives, result_1->alternatives);
     BOOST_CHECK_EQUAL(reference_1.geometries, result_1->geometries);
+    BOOST_CHECK_EQUAL(reference_1.annotations, result_1->annotations);
     BOOST_CHECK_EQUAL(reference_1.overview, result_1->overview);
     BOOST_CHECK_EQUAL(reference_1.continue_straight, result_1->continue_straight);
     CHECK_EQUAL_RANGE(reference_1.bearings, result_1->bearings);
@@ -95,13 +97,16 @@ BOOST_AUTO_TEST_CASE(valid_route_urls)
     RouteParameters reference_2{};
     reference_2.alternatives = true;
     reference_2.steps = true;
+    reference_2.annotations = true;
     reference_2.coordinates = coords_1;
-    auto result_2 = parseParameters<RouteParameters>(
-        "1,2;3,4?steps=true&alternatives=true&geometries=polyline&overview=simplified");
+    auto result_2 =
+        parseParameters<RouteParameters>("1,2;3,4?steps=true&alternatives=true&geometries=polyline&"
+                                         "overview=simplified&annotations=true");
     BOOST_CHECK(result_2);
     BOOST_CHECK_EQUAL(reference_2.steps, result_2->steps);
     BOOST_CHECK_EQUAL(reference_2.alternatives, result_2->alternatives);
     BOOST_CHECK_EQUAL(reference_2.geometries, result_2->geometries);
+    BOOST_CHECK_EQUAL(reference_2.annotations, result_2->annotations);
     BOOST_CHECK_EQUAL(reference_2.overview, result_2->overview);
     BOOST_CHECK_EQUAL(reference_2.continue_straight, result_2->continue_straight);
     CHECK_EQUAL_RANGE(reference_2.bearings, result_2->bearings);
@@ -109,8 +114,12 @@ BOOST_AUTO_TEST_CASE(valid_route_urls)
     CHECK_EQUAL_RANGE(reference_2.coordinates, result_2->coordinates);
     CHECK_EQUAL_RANGE(reference_2.hints, result_2->hints);
 
-    RouteParameters reference_3{false, false, RouteParameters::GeometriesType::GeoJSON,
-                                RouteParameters::OverviewType::False, true};
+    RouteParameters reference_3{false,
+                                false,
+                                false,
+                                RouteParameters::GeometriesType::GeoJSON,
+                                RouteParameters::OverviewType::False,
+                                true};
     reference_3.coordinates = coords_1;
     auto result_3 = api::parseParameters<engine::api::RouteParameters>(
         "1,2;3,4?steps=false&alternatives=false&geometries=geojson&overview=false&continue_"
@@ -119,6 +128,7 @@ BOOST_AUTO_TEST_CASE(valid_route_urls)
     BOOST_CHECK_EQUAL(reference_3.steps, result_3->steps);
     BOOST_CHECK_EQUAL(reference_3.alternatives, result_3->alternatives);
     BOOST_CHECK_EQUAL(reference_3.geometries, result_3->geometries);
+    BOOST_CHECK_EQUAL(reference_3.annotations, result_3->annotations);
     BOOST_CHECK_EQUAL(reference_3.overview, result_3->overview);
     BOOST_CHECK_EQUAL(reference_3.continue_straight, result_3->continue_straight);
     CHECK_EQUAL_RANGE(reference_3.bearings, result_3->bearings);
@@ -138,6 +148,7 @@ BOOST_AUTO_TEST_CASE(valid_route_urls)
                                  "IAAAEBl-Umfg==")};
     RouteParameters reference_4{false,
                                 false,
+                                false,
                                 RouteParameters::GeometriesType::Polyline,
                                 RouteParameters::OverviewType::Simplified,
                                 boost::optional<bool>{},
@@ -154,6 +165,7 @@ BOOST_AUTO_TEST_CASE(valid_route_urls)
     BOOST_CHECK_EQUAL(reference_4.steps, result_4->steps);
     BOOST_CHECK_EQUAL(reference_4.alternatives, result_4->alternatives);
     BOOST_CHECK_EQUAL(reference_4.geometries, result_4->geometries);
+    BOOST_CHECK_EQUAL(reference_4.annotations, result_4->annotations);
     BOOST_CHECK_EQUAL(reference_4.overview, result_4->overview);
     BOOST_CHECK_EQUAL(reference_4.continue_straight, result_4->continue_straight);
     CHECK_EQUAL_RANGE(reference_4.bearings, result_4->bearings);
@@ -166,6 +178,7 @@ BOOST_AUTO_TEST_CASE(valid_route_urls)
     };
     RouteParameters reference_5{false,
                                 false,
+                                false,
                                 RouteParameters::GeometriesType::Polyline,
                                 RouteParameters::OverviewType::Simplified,
                                 boost::optional<bool>{},
@@ -178,6 +191,7 @@ BOOST_AUTO_TEST_CASE(valid_route_urls)
     BOOST_CHECK_EQUAL(reference_5.steps, result_5->steps);
     BOOST_CHECK_EQUAL(reference_5.alternatives, result_5->alternatives);
     BOOST_CHECK_EQUAL(reference_5.geometries, result_5->geometries);
+    BOOST_CHECK_EQUAL(reference_5.annotations, result_5->annotations);
     BOOST_CHECK_EQUAL(reference_5.overview, result_5->overview);
     BOOST_CHECK_EQUAL(reference_5.continue_straight, result_5->continue_straight);
     CHECK_EQUAL_RANGE(reference_5.bearings, result_5->bearings);
@@ -196,6 +210,7 @@ BOOST_AUTO_TEST_CASE(valid_route_urls)
     BOOST_CHECK_EQUAL(reference_6.steps, result_6->steps);
     BOOST_CHECK_EQUAL(reference_6.alternatives, result_6->alternatives);
     BOOST_CHECK_EQUAL(reference_6.geometries, result_6->geometries);
+    BOOST_CHECK_EQUAL(reference_6.annotations, result_6->annotations);
     BOOST_CHECK_EQUAL(reference_6.overview, result_6->overview);
     BOOST_CHECK_EQUAL(reference_6.continue_straight, result_6->continue_straight);
     CHECK_EQUAL_RANGE(reference_6.bearings, result_6->bearings);
@@ -206,11 +221,13 @@ BOOST_AUTO_TEST_CASE(valid_route_urls)
     auto result_7 = parseParameters<RouteParameters>("1,2;3,4?radiuses=;unlimited");
     RouteParameters reference_7{};
     reference_7.coordinates = coords_1;
-    reference_7.radiuses = {boost::none, boost::make_optional(std::numeric_limits<double>::infinity())};
+    reference_7.radiuses = {boost::none,
+                            boost::make_optional(std::numeric_limits<double>::infinity())};
     BOOST_CHECK(result_7);
     BOOST_CHECK_EQUAL(reference_7.steps, result_7->steps);
     BOOST_CHECK_EQUAL(reference_7.alternatives, result_7->alternatives);
     BOOST_CHECK_EQUAL(reference_7.geometries, result_7->geometries);
+    BOOST_CHECK_EQUAL(reference_7.annotations, result_7->annotations);
     BOOST_CHECK_EQUAL(reference_7.overview, result_7->overview);
     BOOST_CHECK_EQUAL(reference_7.continue_straight, result_7->continue_straight);
     CHECK_EQUAL_RANGE(reference_7.bearings, result_7->bearings);
@@ -247,22 +264,25 @@ BOOST_AUTO_TEST_CASE(valid_route_urls)
                                  "IFAAEBl-Umfg=="),
         boost::none};
     RouteParameters reference_10{false,
-                                false,
-                                RouteParameters::GeometriesType::Polyline,
-                                RouteParameters::OverviewType::Simplified,
-                                boost::optional<bool>{},
-                                coords_3,
-                                hints_10,
-                                std::vector<boost::optional<double>>{},
-                                std::vector<boost::optional<engine::Bearing>>{}};
+                                 false,
+                                 false,
+                                 RouteParameters::GeometriesType::Polyline,
+                                 RouteParameters::OverviewType::Simplified,
+                                 boost::optional<bool>{},
+                                 coords_3,
+                                 hints_10,
+                                 std::vector<boost::optional<double>>{},
+                                 std::vector<boost::optional<engine::Bearing>>{}};
     auto result_10 = parseParameters<RouteParameters>(
         "1,2;3,4;5,6;7,8?steps=false&hints="
         "DAIAgP___38AAAAAAAAAAAIAAAAAAAAAEAAAAOgDAAD0AwAAGwAAAOUacQBQP5sCshpxAB0_mwIAAAEBl-Umfg==;;"
-        "cgAAgP___39jAAAADgAAACIAAABeAAAAkQAAANoDAABOAgAAGwAAAFVGcQCiRJsCR0VxAOZFmwIFAAEBl-Umfg==;");
+        "cgAAgP___39jAAAADgAAACIAAABeAAAAkQAAANoDAABOAgAAGwAAAFVGcQCiRJsCR0VxAOZFmwIFAAEBl-Umfg=="
+        ";");
     BOOST_CHECK(result_10);
     BOOST_CHECK_EQUAL(reference_10.steps, result_10->steps);
     BOOST_CHECK_EQUAL(reference_10.alternatives, result_10->alternatives);
     BOOST_CHECK_EQUAL(reference_10.geometries, result_10->geometries);
+    BOOST_CHECK_EQUAL(reference_10.annotations, result_10->annotations);
     BOOST_CHECK_EQUAL(reference_10.overview, result_10->overview);
     BOOST_CHECK_EQUAL(reference_10.continue_straight, result_10->continue_straight);
     CHECK_EQUAL_RANGE(reference_10.bearings, result_10->bearings);
diff --git a/unit_tests/util/bearing.cpp b/unit_tests/util/bearing.cpp
index 77e2ede..4a953c9 100644
--- a/unit_tests/util/bearing.cpp
+++ b/unit_tests/util/bearing.cpp
@@ -2,8 +2,8 @@
 #include "util/typedefs.hpp"
 
 #include <boost/functional/hash.hpp>
-#include <boost/test/unit_test.hpp>
 #include <boost/test/test_case_template.hpp>
+#include <boost/test/unit_test.hpp>
 
 BOOST_AUTO_TEST_SUITE(bearing_test)
 
diff --git a/unit_tests/util/binary_heap.cpp b/unit_tests/util/binary_heap.cpp
index 44aef13..ee60c83 100644
--- a/unit_tests/util/binary_heap.cpp
+++ b/unit_tests/util/binary_heap.cpp
@@ -1,9 +1,9 @@
 #include "util/binary_heap.hpp"
 #include "util/typedefs.hpp"
 
-#include <boost/test/unit_test.hpp>
-#include <boost/test/test_case_template.hpp>
 #include <boost/mpl/list.hpp>
+#include <boost/test/test_case_template.hpp>
+#include <boost/test/unit_test.hpp>
 
 #include <algorithm>
 #include <limits>
@@ -25,7 +25,8 @@ typedef int TestKey;
 typedef int TestWeight;
 typedef boost::mpl::list<ArrayStorage<TestNodeID, TestKey>,
                          MapStorage<TestNodeID, TestKey>,
-                         UnorderedMapStorage<TestNodeID, TestKey>> storage_types;
+                         UnorderedMapStorage<TestNodeID, TestKey>>
+    storage_types;
 
 template <unsigned NUM_ELEM> struct RandomDataFixture
 {
diff --git a/unit_tests/util/coordinate_calculation.cpp b/unit_tests/util/coordinate_calculation.cpp
index 0515a36..147baca 100644
--- a/unit_tests/util/coordinate_calculation.cpp
+++ b/unit_tests/util/coordinate_calculation.cpp
@@ -1,5 +1,5 @@
-#include <boost/test/unit_test.hpp>
 #include <boost/numeric/conversion/cast.hpp>
+#include <boost/test/unit_test.hpp>
 
 #include "util/coordinate_calculation.hpp"
 
@@ -180,9 +180,11 @@ BOOST_AUTO_TEST_CASE(regression_point_on_segment)
     FloatCoordinate diff{target.lon - start.lon, target.lat - start.lat};
 
     BOOST_CHECK_CLOSE(static_cast<double>(start.lon + FloatLongitude(ratio) * diff.lon),
-                      static_cast<double>(nearest.lon), 0.1);
+                      static_cast<double>(nearest.lon),
+                      0.1);
     BOOST_CHECK_CLOSE(static_cast<double>(start.lat + FloatLatitude(ratio) * diff.lat),
-                      static_cast<double>(nearest.lat), 0.1);
+                      static_cast<double>(nearest.lat),
+                      0.1);
 }
 
 BOOST_AUTO_TEST_CASE(point_on_segment)
@@ -192,9 +194,10 @@ BOOST_AUTO_TEST_CASE(point_on_segment)
     //  |---- i
     //  |
     //  s
-    auto result_1 = coordinate_calculation::projectPointOnSegment(
-        {FloatLongitude{0}, FloatLatitude{0}}, {FloatLongitude{0}, FloatLatitude{2}},
-        {FloatLongitude{2}, FloatLatitude{1}});
+    auto result_1 =
+        coordinate_calculation::projectPointOnSegment({FloatLongitude{0}, FloatLatitude{0}},
+                                                      {FloatLongitude{0}, FloatLatitude{2}},
+                                                      {FloatLongitude{2}, FloatLatitude{1}});
     auto reference_ratio_1 = 0.5;
     auto reference_point_1 = FloatCoordinate{FloatLongitude{0}, FloatLatitude{1}};
     BOOST_CHECK_EQUAL(result_1.first, reference_ratio_1);
@@ -208,9 +211,10 @@ BOOST_AUTO_TEST_CASE(point_on_segment)
     //  |
     //  |
     //  s
-    auto result_2 = coordinate_calculation::projectPointOnSegment(
-        {FloatLongitude{0.}, FloatLatitude{0.}}, {FloatLongitude{0}, FloatLatitude{2}},
-        {FloatLongitude{0}, FloatLatitude{3}});
+    auto result_2 =
+        coordinate_calculation::projectPointOnSegment({FloatLongitude{0.}, FloatLatitude{0.}},
+                                                      {FloatLongitude{0}, FloatLatitude{2}},
+                                                      {FloatLongitude{0}, FloatLatitude{3}});
     auto reference_ratio_2 = 1.;
     auto reference_point_2 = FloatCoordinate{FloatLongitude{0}, FloatLatitude{2}};
     BOOST_CHECK_EQUAL(result_2.first, reference_ratio_2);
@@ -224,9 +228,10 @@ BOOST_AUTO_TEST_CASE(point_on_segment)
     //  s
     //  :
     //  i
-    auto result_3 = coordinate_calculation::projectPointOnSegment(
-        {FloatLongitude{0.}, FloatLatitude{0.}}, {FloatLongitude{0}, FloatLatitude{2}},
-        {FloatLongitude{0}, FloatLatitude{-1}});
+    auto result_3 =
+        coordinate_calculation::projectPointOnSegment({FloatLongitude{0.}, FloatLatitude{0.}},
+                                                      {FloatLongitude{0}, FloatLatitude{2}},
+                                                      {FloatLongitude{0}, FloatLatitude{-1}});
     auto reference_ratio_3 = 0.;
     auto reference_point_3 = FloatCoordinate{FloatLongitude{0}, FloatLatitude{0}};
     BOOST_CHECK_EQUAL(result_3.first, reference_ratio_3);
@@ -240,7 +245,8 @@ BOOST_AUTO_TEST_CASE(point_on_segment)
     // s
     //
     auto result_4 = coordinate_calculation::projectPointOnSegment(
-        {FloatLongitude{0}, FloatLatitude{0}}, {FloatLongitude{1}, FloatLatitude{1}},
+        {FloatLongitude{0}, FloatLatitude{0}},
+        {FloatLongitude{1}, FloatLatitude{1}},
         {FloatLongitude{0.5 + 0.1}, FloatLatitude{0.5 - 0.1}});
     auto reference_ratio_4 = 0.5;
     auto reference_point_4 = FloatCoordinate{FloatLongitude{0.5}, FloatLatitude{0.5}};
diff --git a/unit_tests/util/duration_parsing.cpp b/unit_tests/util/duration_parsing.cpp
index 72a5b63..6b9d551 100644
--- a/unit_tests/util/duration_parsing.cpp
+++ b/unit_tests/util/duration_parsing.cpp
@@ -1,22 +1,37 @@
 #include "extractor/extraction_helper_functions.hpp"
 
-#include <boost/test/unit_test.hpp>
 #include <boost/test/test_case_template.hpp>
+#include <boost/test/unit_test.hpp>
 
 BOOST_AUTO_TEST_SUITE(durations_are_valid)
 
 using namespace osrm;
-using namespace osrm::util;
 
 BOOST_AUTO_TEST_CASE(all_necessary_test)
 {
+    BOOST_CHECK_EQUAL(extractor::durationIsValid("0"), true);
     BOOST_CHECK_EQUAL(extractor::durationIsValid("00:01"), true);
     BOOST_CHECK_EQUAL(extractor::durationIsValid("00:01:01"), true);
+    BOOST_CHECK_EQUAL(extractor::durationIsValid("61"), true);
+    BOOST_CHECK_EQUAL(extractor::durationIsValid("24:01"), true);
+    BOOST_CHECK_EQUAL(extractor::durationIsValid("00:01:60"), true);
     BOOST_CHECK_EQUAL(extractor::durationIsValid("PT15M"), true);
+
+    BOOST_CHECK_EQUAL(extractor::durationIsValid(""), false);
+    BOOST_CHECK_EQUAL(extractor::durationIsValid("PT15"), false);
+    BOOST_CHECK_EQUAL(extractor::durationIsValid("PT15A"), false);
+    BOOST_CHECK_EQUAL(extractor::durationIsValid("PT1H25:01"), false);
+    BOOST_CHECK_EQUAL(extractor::durationIsValid("PT12501"), false);
+    BOOST_CHECK_EQUAL(extractor::durationIsValid("PT0125:01"), false);
+    BOOST_CHECK_EQUAL(extractor::durationIsValid("PT016001"), false);
+    BOOST_CHECK_EQUAL(extractor::durationIsValid("PT240000"), false);
+    BOOST_CHECK_EQUAL(extractor::durationIsValid("PT24:00:00"), false);
 }
 
 BOOST_AUTO_TEST_CASE(common_durations_get_translated)
 {
+    BOOST_CHECK_EQUAL(extractor::parseDuration("00"), 0);
+    BOOST_CHECK_EQUAL(extractor::parseDuration("10"), 600);
     BOOST_CHECK_EQUAL(extractor::parseDuration("00:01"), 60);
     BOOST_CHECK_EQUAL(extractor::parseDuration("00:01:01"), 61);
     BOOST_CHECK_EQUAL(extractor::parseDuration("01:01"), 3660);
@@ -29,12 +44,22 @@ BOOST_AUTO_TEST_CASE(common_durations_get_translated)
     BOOST_CHECK_EQUAL(extractor::parseDuration("PT15H"), 54000);
     BOOST_CHECK_EQUAL(extractor::parseDuration("PT1H15M"), 4500);
     BOOST_CHECK_EQUAL(extractor::parseDuration("PT1H15M1S"), 4501);
+    BOOST_CHECK_EQUAL(extractor::parseDuration("PT2H25M6S"), 8706);
+    BOOST_CHECK_EQUAL(extractor::parseDuration("P1DT2H15M1S"), 94501);
+    BOOST_CHECK_EQUAL(extractor::parseDuration("P4D"), 345600);
+    BOOST_CHECK_EQUAL(extractor::parseDuration("PT4H"), 14400);
+    BOOST_CHECK_EQUAL(extractor::parseDuration("PT71M"), 4260);
+    BOOST_CHECK_EQUAL(extractor::parseDuration("PT022506"), 8706);
+    BOOST_CHECK_EQUAL(extractor::parseDuration("PT02:25:06"), 8706);
+    BOOST_CHECK_EQUAL(extractor::parseDuration("P3W"), 1814400);
 }
 
 BOOST_AUTO_TEST_CASE(iso_8601_durations_case_insensitive)
 {
     BOOST_CHECK_EQUAL(extractor::parseDuration("PT15m"), 900);
     BOOST_CHECK_EQUAL(extractor::parseDuration("PT1h15m"), 4500);
+    BOOST_CHECK_EQUAL(extractor::parseDuration("PT1h15m42s"), 4542);
+    BOOST_CHECK_EQUAL(extractor::parseDuration("P2dT1h15m42s"), 177342);
 }
 
 BOOST_AUTO_TEST_SUITE_END()
diff --git a/unit_tests/util/dynamic_graph.cpp b/unit_tests/util/dynamic_graph.cpp
index 263b60c..db6712f 100644
--- a/unit_tests/util/dynamic_graph.cpp
+++ b/unit_tests/util/dynamic_graph.cpp
@@ -1,8 +1,8 @@
 #include "util/dynamic_graph.hpp"
 #include "util/typedefs.hpp"
 
-#include <boost/test/unit_test.hpp>
 #include <boost/test/test_case_template.hpp>
+#include <boost/test/unit_test.hpp>
 
 #include <vector>
 
@@ -29,10 +29,11 @@ BOOST_AUTO_TEST_CASE(find_test)
      *  (3) -3-> (4)
      *      <-4-
      */
-    std::vector<TestInputEdge> input_edges = {
-        TestInputEdge{0, 1, TestData{1}}, TestInputEdge{3, 0, TestData{2}},
-        TestInputEdge{3, 0, TestData{5}}, TestInputEdge{3, 4, TestData{3}},
-        TestInputEdge{4, 3, TestData{4}}};
+    std::vector<TestInputEdge> input_edges = {TestInputEdge{0, 1, TestData{1}},
+                                              TestInputEdge{3, 0, TestData{2}},
+                                              TestInputEdge{3, 0, TestData{5}},
+                                              TestInputEdge{3, 4, TestData{3}},
+                                              TestInputEdge{4, 3, TestData{4}}};
     TestDynamicGraph simple_graph(5, input_edges);
 
     auto eit = simple_graph.FindEdge(0, 1);
diff --git a/unit_tests/util/io.cpp b/unit_tests/util/io.cpp
index e37fa13..925e3ad 100644
--- a/unit_tests/util/io.cpp
+++ b/unit_tests/util/io.cpp
@@ -1,8 +1,8 @@
 #include "util/io.hpp"
 #include "util/typedefs.hpp"
 
-#include <boost/test/unit_test.hpp>
 #include <boost/test/test_case_template.hpp>
+#include <boost/test/unit_test.hpp>
 
 #include <string>
 
@@ -21,8 +21,8 @@ BOOST_AUTO_TEST_CASE(io_flags)
     osrm::util::deserializeFlags(IO_TMP_FILE, flags_out);
 
     BOOST_REQUIRE_EQUAL(flags_in.size(), flags_out.size());
-    BOOST_CHECK_EQUAL_COLLECTIONS(flags_out.begin(), flags_out.end(), flags_in.begin(),
-                                  flags_in.end());
+    BOOST_CHECK_EQUAL_COLLECTIONS(
+        flags_out.begin(), flags_out.end(), flags_in.begin(), flags_in.end());
 }
 
 BOOST_AUTO_TEST_CASE(io_data)
diff --git a/unit_tests/util/packed_vector.cpp b/unit_tests/util/packed_vector.cpp
new file mode 100644
index 0000000..6c723a6
--- /dev/null
+++ b/unit_tests/util/packed_vector.cpp
@@ -0,0 +1,47 @@
+#include "util/packed_vector.hpp"
+#include "util/typedefs.hpp"
+
+#include <boost/test/test_case_template.hpp>
+#include <boost/test/unit_test.hpp>
+
+BOOST_AUTO_TEST_SUITE(packed_vector_test)
+
+using namespace osrm;
+using namespace osrm::util;
+
+// Verify that the packed vector behaves as expected
+BOOST_AUTO_TEST_CASE(insert_and_retrieve_packed_test)
+{
+    PackedVector<OSMNodeID, false> packed_ids;
+    std::vector<OSMNodeID> original_ids;
+
+    const constexpr std::size_t num_test_cases = 399;
+
+    for (std::size_t i = 0; i < num_test_cases; i++)
+    {
+        OSMNodeID r = static_cast<OSMNodeID>(rand() % 2147483647); // max 33-bit uint
+
+        packed_ids.push_back(r);
+        original_ids.push_back(r);
+    }
+
+    for (std::size_t i = 0; i < num_test_cases; i++)
+    {
+        BOOST_CHECK_EQUAL(original_ids.at(i), packed_ids.at(i));
+    }
+}
+
+BOOST_AUTO_TEST_CASE(packed_vector_capacity_test)
+{
+    PackedVector<OSMNodeID, false> packed_vec;
+    const std::size_t original_size = packed_vec.capacity();
+    std::vector<OSMNodeID> dummy_vec;
+
+    BOOST_CHECK_EQUAL(original_size, dummy_vec.capacity());
+
+    packed_vec.reserve(100);
+
+    BOOST_CHECK(packed_vec.capacity() >= 100);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/unit_tests/util/range_table.cpp b/unit_tests/util/range_table.cpp
index a5db4c5..b5f439e 100644
--- a/unit_tests/util/range_table.cpp
+++ b/unit_tests/util/range_table.cpp
@@ -1,8 +1,8 @@
 #include "util/range_table.hpp"
 #include "util/typedefs.hpp"
 
-#include <boost/test/unit_test.hpp>
 #include <boost/test/test_case_template.hpp>
+#include <boost/test/unit_test.hpp>
 
 #include <numeric>
 #include <stxxl/vector>
diff --git a/unit_tests/util/rectangle.cpp b/unit_tests/util/rectangle.cpp
index c68fbda..87da415 100644
--- a/unit_tests/util/rectangle.cpp
+++ b/unit_tests/util/rectangle.cpp
@@ -1,8 +1,8 @@
 #include "util/rectangle.hpp"
 #include "util/typedefs.hpp"
 
-#include <boost/test/unit_test.hpp>
 #include <boost/test/test_case_template.hpp>
+#include <boost/test/unit_test.hpp>
 
 BOOST_AUTO_TEST_SUITE(rectangle_test)
 
@@ -26,14 +26,14 @@ BOOST_AUTO_TEST_CASE(get_min_dist_test)
     //           |
     //           +- -80
     //           |
-    RectangleInt2D ne{FloatLongitude(10), FloatLongitude(100), FloatLatitude(10),
-                      FloatLatitude(80)};
-    RectangleInt2D nw{FloatLongitude(-100), FloatLongitude(-10), FloatLatitude(10),
-                      FloatLatitude(80)};
-    RectangleInt2D se{FloatLongitude(10), FloatLongitude(100), FloatLatitude(-80),
-                      FloatLatitude(-10)};
-    RectangleInt2D sw{FloatLongitude(-100), FloatLongitude(-10), FloatLatitude(-80),
-                      FloatLatitude(-10)};
+    RectangleInt2D ne{
+        FloatLongitude(10), FloatLongitude(100), FloatLatitude(10), FloatLatitude(80)};
+    RectangleInt2D nw{
+        FloatLongitude(-100), FloatLongitude(-10), FloatLatitude(10), FloatLatitude(80)};
+    RectangleInt2D se{
+        FloatLongitude(10), FloatLongitude(100), FloatLatitude(-80), FloatLatitude(-10)};
+    RectangleInt2D sw{
+        FloatLongitude(-100), FloatLongitude(-10), FloatLatitude(-80), FloatLatitude(-10)};
 
     Coordinate nw_sw{FloatLongitude(-100.1), FloatLatitude(9.9)};
     Coordinate nw_se{FloatLongitude(-9.9), FloatLatitude(9.9)};
@@ -43,14 +43,22 @@ BOOST_AUTO_TEST_CASE(get_min_dist_test)
     Coordinate nw_e{FloatLongitude(-9.9), FloatLatitude(45.0)};
     Coordinate nw_w{FloatLongitude(-100.1), FloatLatitude(45.0)};
     Coordinate nw_n{FloatLongitude(-55), FloatLatitude(80.1)};
-    BOOST_CHECK_CLOSE(nw.GetMinSquaredDist(nw_sw), 0.02 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
-    BOOST_CHECK_CLOSE(nw.GetMinSquaredDist(nw_se), 0.02 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
-    BOOST_CHECK_CLOSE(nw.GetMinSquaredDist(nw_ne), 0.02 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
-    BOOST_CHECK_CLOSE(nw.GetMinSquaredDist(nw_nw), 0.02 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
-    BOOST_CHECK_CLOSE(nw.GetMinSquaredDist(nw_s),  0.01 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
-    BOOST_CHECK_CLOSE(nw.GetMinSquaredDist(nw_e),  0.01 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
-    BOOST_CHECK_CLOSE(nw.GetMinSquaredDist(nw_w),  0.01 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
-    BOOST_CHECK_CLOSE(nw.GetMinSquaredDist(nw_n),  0.01 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
+    BOOST_CHECK_CLOSE(
+        nw.GetMinSquaredDist(nw_sw), 0.02 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
+    BOOST_CHECK_CLOSE(
+        nw.GetMinSquaredDist(nw_se), 0.02 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
+    BOOST_CHECK_CLOSE(
+        nw.GetMinSquaredDist(nw_ne), 0.02 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
+    BOOST_CHECK_CLOSE(
+        nw.GetMinSquaredDist(nw_nw), 0.02 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
+    BOOST_CHECK_CLOSE(
+        nw.GetMinSquaredDist(nw_s), 0.01 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
+    BOOST_CHECK_CLOSE(
+        nw.GetMinSquaredDist(nw_e), 0.01 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
+    BOOST_CHECK_CLOSE(
+        nw.GetMinSquaredDist(nw_w), 0.01 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
+    BOOST_CHECK_CLOSE(
+        nw.GetMinSquaredDist(nw_n), 0.01 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
 
     Coordinate ne_sw{FloatLongitude(9.9), FloatLatitude(9.9)};
     Coordinate ne_se{FloatLongitude(100.1), FloatLatitude(9.9)};
@@ -60,14 +68,22 @@ BOOST_AUTO_TEST_CASE(get_min_dist_test)
     Coordinate ne_e{FloatLongitude(100.1), FloatLatitude(45.0)};
     Coordinate ne_w{FloatLongitude(9.9), FloatLatitude(45.0)};
     Coordinate ne_n{FloatLongitude(55), FloatLatitude(80.1)};
-    BOOST_CHECK_CLOSE(ne.GetMinSquaredDist(ne_sw), 0.02 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
-    BOOST_CHECK_CLOSE(ne.GetMinSquaredDist(ne_se), 0.02 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
-    BOOST_CHECK_CLOSE(ne.GetMinSquaredDist(ne_ne), 0.02 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
-    BOOST_CHECK_CLOSE(ne.GetMinSquaredDist(ne_nw), 0.02 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
-    BOOST_CHECK_CLOSE(ne.GetMinSquaredDist(ne_s),  0.01 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
-    BOOST_CHECK_CLOSE(ne.GetMinSquaredDist(ne_e),  0.01 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
-    BOOST_CHECK_CLOSE(ne.GetMinSquaredDist(ne_w),  0.01 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
-    BOOST_CHECK_CLOSE(ne.GetMinSquaredDist(ne_n),  0.01 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
+    BOOST_CHECK_CLOSE(
+        ne.GetMinSquaredDist(ne_sw), 0.02 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
+    BOOST_CHECK_CLOSE(
+        ne.GetMinSquaredDist(ne_se), 0.02 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
+    BOOST_CHECK_CLOSE(
+        ne.GetMinSquaredDist(ne_ne), 0.02 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
+    BOOST_CHECK_CLOSE(
+        ne.GetMinSquaredDist(ne_nw), 0.02 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
+    BOOST_CHECK_CLOSE(
+        ne.GetMinSquaredDist(ne_s), 0.01 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
+    BOOST_CHECK_CLOSE(
+        ne.GetMinSquaredDist(ne_e), 0.01 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
+    BOOST_CHECK_CLOSE(
+        ne.GetMinSquaredDist(ne_w), 0.01 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
+    BOOST_CHECK_CLOSE(
+        ne.GetMinSquaredDist(ne_n), 0.01 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
 
     Coordinate se_ne{FloatLongitude(100.1), FloatLatitude(-9.9)};
     Coordinate se_nw{FloatLongitude(9.9), FloatLatitude(-9.9)};
@@ -77,14 +93,22 @@ BOOST_AUTO_TEST_CASE(get_min_dist_test)
     Coordinate se_w{FloatLongitude(9.9), FloatLatitude(-45.0)};
     Coordinate se_e{FloatLongitude(100.1), FloatLatitude(-45.0)};
     Coordinate se_s{FloatLongitude(55), FloatLatitude(-80.1)};
-    BOOST_CHECK_CLOSE(se.GetMinSquaredDist(se_sw), 0.02 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
-    BOOST_CHECK_CLOSE(se.GetMinSquaredDist(se_se), 0.02 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
-    BOOST_CHECK_CLOSE(se.GetMinSquaredDist(se_ne), 0.02 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
-    BOOST_CHECK_CLOSE(se.GetMinSquaredDist(se_nw), 0.02 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
-    BOOST_CHECK_CLOSE(se.GetMinSquaredDist(se_s),  0.01 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
-    BOOST_CHECK_CLOSE(se.GetMinSquaredDist(se_e),  0.01 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
-    BOOST_CHECK_CLOSE(se.GetMinSquaredDist(se_w),  0.01 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
-    BOOST_CHECK_CLOSE(se.GetMinSquaredDist(se_n),  0.01 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
+    BOOST_CHECK_CLOSE(
+        se.GetMinSquaredDist(se_sw), 0.02 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
+    BOOST_CHECK_CLOSE(
+        se.GetMinSquaredDist(se_se), 0.02 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
+    BOOST_CHECK_CLOSE(
+        se.GetMinSquaredDist(se_ne), 0.02 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
+    BOOST_CHECK_CLOSE(
+        se.GetMinSquaredDist(se_nw), 0.02 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
+    BOOST_CHECK_CLOSE(
+        se.GetMinSquaredDist(se_s), 0.01 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
+    BOOST_CHECK_CLOSE(
+        se.GetMinSquaredDist(se_e), 0.01 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
+    BOOST_CHECK_CLOSE(
+        se.GetMinSquaredDist(se_w), 0.01 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
+    BOOST_CHECK_CLOSE(
+        se.GetMinSquaredDist(se_n), 0.01 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
 
     Coordinate sw_ne{FloatLongitude(-9.9), FloatLatitude(-9.9)};
     Coordinate sw_nw{FloatLongitude(-100.1), FloatLatitude(-9.9)};
@@ -94,14 +118,22 @@ BOOST_AUTO_TEST_CASE(get_min_dist_test)
     Coordinate sw_w{FloatLongitude(-100.1), FloatLatitude(-45.0)};
     Coordinate sw_e{FloatLongitude(-9.9), FloatLatitude(-45.0)};
     Coordinate sw_s{FloatLongitude(-55), FloatLatitude(-80.1)};
-    BOOST_CHECK_CLOSE(sw.GetMinSquaredDist(sw_sw), 0.02 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
-    BOOST_CHECK_CLOSE(sw.GetMinSquaredDist(sw_se), 0.02 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
-    BOOST_CHECK_CLOSE(sw.GetMinSquaredDist(sw_ne), 0.02 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
-    BOOST_CHECK_CLOSE(sw.GetMinSquaredDist(sw_nw), 0.02 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
-    BOOST_CHECK_CLOSE(sw.GetMinSquaredDist(sw_s),  0.01 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
-    BOOST_CHECK_CLOSE(sw.GetMinSquaredDist(sw_e),  0.01 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
-    BOOST_CHECK_CLOSE(sw.GetMinSquaredDist(sw_w),  0.01 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
-    BOOST_CHECK_CLOSE(sw.GetMinSquaredDist(sw_n),  0.01 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
+    BOOST_CHECK_CLOSE(
+        sw.GetMinSquaredDist(sw_sw), 0.02 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
+    BOOST_CHECK_CLOSE(
+        sw.GetMinSquaredDist(sw_se), 0.02 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
+    BOOST_CHECK_CLOSE(
+        sw.GetMinSquaredDist(sw_ne), 0.02 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
+    BOOST_CHECK_CLOSE(
+        sw.GetMinSquaredDist(sw_nw), 0.02 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
+    BOOST_CHECK_CLOSE(
+        sw.GetMinSquaredDist(sw_s), 0.01 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
+    BOOST_CHECK_CLOSE(
+        sw.GetMinSquaredDist(sw_e), 0.01 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
+    BOOST_CHECK_CLOSE(
+        sw.GetMinSquaredDist(sw_w), 0.01 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
+    BOOST_CHECK_CLOSE(
+        sw.GetMinSquaredDist(sw_n), 0.01 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
 }
 
 BOOST_AUTO_TEST_SUITE_END()
diff --git a/unit_tests/util/static_graph.cpp b/unit_tests/util/static_graph.cpp
index c975401..05fe60d 100644
--- a/unit_tests/util/static_graph.cpp
+++ b/unit_tests/util/static_graph.cpp
@@ -1,8 +1,8 @@
 #include "util/static_graph.hpp"
 #include "util/typedefs.hpp"
 
-#include <boost/test/unit_test.hpp>
 #include <boost/test/test_case_template.hpp>
+#include <boost/test/unit_test.hpp>
 
 #include <algorithm>
 #include <random>
@@ -114,10 +114,11 @@ BOOST_AUTO_TEST_CASE(find_test)
      *  (3) -3-> (4)
      *      <-4-
      */
-    std::vector<TestInputEdge> input_edges = {
-        TestInputEdge{0, 1, TestData{1}}, TestInputEdge{3, 0, TestData{2}},
-        TestInputEdge{3, 0, TestData{5}}, TestInputEdge{3, 4, TestData{3}},
-        TestInputEdge{4, 3, TestData{4}}};
+    std::vector<TestInputEdge> input_edges = {TestInputEdge{0, 1, TestData{1}},
+                                              TestInputEdge{3, 0, TestData{2}},
+                                              TestInputEdge{3, 0, TestData{5}},
+                                              TestInputEdge{3, 4, TestData{3}},
+                                              TestInputEdge{4, 3, TestData{4}}};
     TestStaticGraph simple_graph(5, input_edges);
 
     auto eit = simple_graph.FindEdge(0, 1);
diff --git a/unit_tests/util/static_rtree.cpp b/unit_tests/util/static_rtree.cpp
index a20d0db..c962ed3 100644
--- a/unit_tests/util/static_rtree.cpp
+++ b/unit_tests/util/static_rtree.cpp
@@ -1,10 +1,10 @@
-#include "engine/geospatial_query.hpp"
+#include "util/static_rtree.hpp"
 #include "extractor/edge_based_node.hpp"
+#include "engine/geospatial_query.hpp"
 #include "util/coordinate.hpp"
 #include "util/coordinate_calculation.hpp"
 #include "util/exception.hpp"
 #include "util/rectangle.hpp"
-#include "util/static_rtree.hpp"
 #include "util/typedefs.hpp"
 
 #include "mocks/mock_datafacade.hpp"
@@ -52,8 +52,7 @@ static const int32_t WORLD_MAX_LON = 180 * COORDINATE_PRECISION;
 template <typename DataT> class LinearSearchNN
 {
   public:
-    LinearSearchNN(const std::vector<Coordinate> &coords,
-                   const std::vector<DataT> &edges)
+    LinearSearchNN(const std::vector<Coordinate> &coords, const std::vector<DataT> &edges)
         : coords(coords), edges(edges)
     {
     }
@@ -77,7 +76,9 @@ template <typename DataT> class LinearSearchNN
             return lhs_squared_dist < rhs_squared_dist;
         };
 
-        std::nth_element(local_edges.begin(), local_edges.begin() + num_results, local_edges.end(),
+        std::nth_element(local_edges.begin(),
+                         local_edges.begin() + num_results,
+                         local_edges.end(),
                          segment_comparator);
         local_edges.resize(num_results);
 
@@ -317,8 +318,8 @@ BOOST_AUTO_TEST_CASE(regression_test)
 
     std::string leaves_path;
     std::string nodes_path;
-    build_rtree<GraphFixture, MiniStaticRTree>("test_regression", &fixture, leaves_path,
-                                               nodes_path);
+    build_rtree<GraphFixture, MiniStaticRTree>(
+        "test_regression", &fixture, leaves_path, nodes_path);
     MiniStaticRTree rtree(nodes_path, leaves_path, fixture.coords);
     LinearSearchNN<TestData> lsnn(fixture.coords, fixture.edges);
 
@@ -351,8 +352,8 @@ BOOST_AUTO_TEST_CASE(radius_regression_test)
     build_rtree<GraphFixture, MiniStaticRTree>("test_angle", &fixture, leaves_path, nodes_path);
     MiniStaticRTree rtree(nodes_path, leaves_path, fixture.coords);
     MockDataFacade mockfacade;
-    engine::GeospatialQuery<MiniStaticRTree, MockDataFacade> query(rtree, fixture.coords,
-                                                                   mockfacade);
+    engine::GeospatialQuery<MiniStaticRTree, MockDataFacade> query(
+        rtree, fixture.coords, mockfacade);
 
     Coordinate input(FloatLongitude(5.2), FloatLatitude(5.0));
 
@@ -378,8 +379,8 @@ BOOST_AUTO_TEST_CASE(bearing_tests)
     build_rtree<GraphFixture, MiniStaticRTree>("test_bearing", &fixture, leaves_path, nodes_path);
     MiniStaticRTree rtree(nodes_path, leaves_path, fixture.coords);
     MockDataFacade mockfacade;
-    engine::GeospatialQuery<MiniStaticRTree, MockDataFacade> query(rtree, fixture.coords,
-                                                                   mockfacade);
+    engine::GeospatialQuery<MiniStaticRTree, MockDataFacade> query(
+        rtree, fixture.coords, mockfacade);
 
     Coordinate input(FloatLongitude(5.1), FloatLatitude(5.0));
 
@@ -452,19 +453,19 @@ BOOST_AUTO_TEST_CASE(bbox_search_tests)
     build_rtree<GraphFixture, MiniStaticRTree>("test_bbox", &fixture, leaves_path, nodes_path);
     MiniStaticRTree rtree(nodes_path, leaves_path, fixture.coords);
     MockDataFacade mockfacade;
-    engine::GeospatialQuery<MiniStaticRTree, MockDataFacade> query(rtree, fixture.coords,
-                                                                   mockfacade);
+    engine::GeospatialQuery<MiniStaticRTree, MockDataFacade> query(
+        rtree, fixture.coords, mockfacade);
 
     {
-        RectangleInt2D bbox = {FloatLongitude(0.5), FloatLongitude(1.5), FloatLatitude(0.5),
-                               FloatLatitude(1.5)};
+        RectangleInt2D bbox = {
+            FloatLongitude(0.5), FloatLongitude(1.5), FloatLatitude(0.5), FloatLatitude(1.5)};
         auto results = query.Search(bbox);
         BOOST_CHECK_EQUAL(results.size(), 2);
     }
 
     {
-        RectangleInt2D bbox = {FloatLongitude(1.5), FloatLongitude(3.5), FloatLatitude(1.5),
-                               FloatLatitude(3.5)};
+        RectangleInt2D bbox = {
+            FloatLongitude(1.5), FloatLongitude(3.5), FloatLatitude(1.5), FloatLatitude(3.5)};
         auto results = query.Search(bbox);
         BOOST_CHECK_EQUAL(results.size(), 3);
     }
diff --git a/unit_tests/util/string_util.cpp b/unit_tests/util/string_util.cpp
index e3a7204..793bf7c 100644
--- a/unit_tests/util/string_util.cpp
+++ b/unit_tests/util/string_util.cpp
@@ -1,7 +1,7 @@
 #include "util/string_util.hpp"
 
-#include <boost/test/unit_test.hpp>
 #include <boost/test/test_case_template.hpp>
+#include <boost/test/unit_test.hpp>
 
 #include <iostream>
 
diff --git a/unit_tests/util/viewport.cpp b/unit_tests/util/viewport.cpp
index f5f4f9a..e7e30b6 100644
--- a/unit_tests/util/viewport.cpp
+++ b/unit_tests/util/viewport.cpp
@@ -3,8 +3,8 @@
 using namespace osrm::util;
 
 #include <boost/functional/hash.hpp>
-#include <boost/test/unit_test.hpp>
 #include <boost/test/test_case_template.hpp>
+#include <boost/test/unit_test.hpp>
 
 #include <iostream>
 
diff --git a/unit_tests/util/web_mercator.cpp b/unit_tests/util/web_mercator.cpp
index 7b00396..8e96d98 100644
--- a/unit_tests/util/web_mercator.cpp
+++ b/unit_tests/util/web_mercator.cpp
@@ -22,16 +22,26 @@ BOOST_AUTO_TEST_CASE(lon_to_pixel)
 
 BOOST_AUTO_TEST_CASE(lat_to_pixel)
 {
-    BOOST_CHECK_CLOSE(web_mercator::latToY(util::FloatLatitude(43.733947)) * web_mercator::DEGREE_TO_PX,
-                      5424361.75863, 0.1);
-    BOOST_CHECK_CLOSE(web_mercator::latToY(util::FloatLatitude(43.733799)) * web_mercator::DEGREE_TO_PX,
-                      5424338.95731, 0.1);
-    BOOST_CHECK_CLOSE(web_mercator::latToY(util::FloatLatitude(43.733922)) * web_mercator::DEGREE_TO_PX,
-                      5424357.90705, 0.1);
-    BOOST_CHECK_CLOSE(web_mercator::latToY(util::FloatLatitude(43.733697)) * web_mercator::DEGREE_TO_PX,
-                      5424323.24293, 0.1);
-    BOOST_CHECK_CLOSE(web_mercator::latToY(util::FloatLatitude(43.733729)) * web_mercator::DEGREE_TO_PX,
-                      5424328.17293, 0.1);
+    BOOST_CHECK_CLOSE(web_mercator::latToY(util::FloatLatitude(43.733947)) *
+                          web_mercator::DEGREE_TO_PX,
+                      5424361.75863,
+                      0.1);
+    BOOST_CHECK_CLOSE(web_mercator::latToY(util::FloatLatitude(43.733799)) *
+                          web_mercator::DEGREE_TO_PX,
+                      5424338.95731,
+                      0.1);
+    BOOST_CHECK_CLOSE(web_mercator::latToY(util::FloatLatitude(43.733922)) *
+                          web_mercator::DEGREE_TO_PX,
+                      5424357.90705,
+                      0.1);
+    BOOST_CHECK_CLOSE(web_mercator::latToY(util::FloatLatitude(43.733697)) *
+                          web_mercator::DEGREE_TO_PX,
+                      5424323.24293,
+                      0.1);
+    BOOST_CHECK_CLOSE(web_mercator::latToY(util::FloatLatitude(43.733729)) *
+                          web_mercator::DEGREE_TO_PX,
+                      5424328.17293,
+                      0.1);
 }
 
 BOOST_AUTO_TEST_CASE(xyz_to_wgs84)

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-grass/osrm.git



More information about the Pkg-grass-devel mailing list