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

Sebastiaan Couwenberg sebastic at moszumanska.debian.org
Tue May 10 20:12:27 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 d28a1245a7c7b42b91c7429548152e8bad5ec7b1
Author: Bas Couwenberg <sebastic at xs4all.nl>
Date:   Tue May 10 21:30:17 2016 +0200

    Imported Upstream version 5.1.0+ds
---
 .gitignore                                         |   1 +
 .travis.yml                                        | 158 +++---
 CHANGELOG.md                                       |  36 +-
 CMakeLists.txt                                     |   7 +-
 README.md                                          |  53 +-
 appveyor-build.bat                                 |   2 +-
 appveyor.yml                                       |   1 -
 docs/http.md                                       | 509 +++++++++++++++++
 docs/libosrm.md                                    |  26 +
 docs/profiles.md                                   |  34 ++
 docs/releasing.md                                  |  50 ++
 example/CMakeLists.txt                             |   4 +-
 features/bicycle/names.feature                     |  10 +-
 features/bicycle/ref.feature                       |   2 +-
 .../{traffic.feature => traffic_speeds.feature}    |   2 +-
 features/car/traffic_turn_penalties.feature        |  97 ++++
 features/foot/names.feature                        |  10 +-
 features/foot/ref.feature                          |   2 +-
 features/guidance/bridges_and_tunnels.feature      | 120 ++++
 features/guidance/collapse.feature                 | 347 +++++++++++
 features/guidance/destination-signs.feature        |  41 ++
 features/guidance/end-of-road.feature              |   6 +-
 features/guidance/fork.feature                     |   1 -
 features/guidance/motorway.feature                 |  56 +-
 features/guidance/new-name.feature                 |  31 +
 features/guidance/ramp.feature                     |  64 +--
 features/guidance/rotary.feature                   |  20 +-
 ...{roundabout.feature => roundabout-turn.feature} | 202 +++++--
 features/guidance/roundabout.feature               | 108 +++-
 features/guidance/suffix-changes.feature           | 101 ++++
 features/guidance/turn.feature                     |   7 +-
 features/step_definitions/data.js                  |  39 +-
 features/step_definitions/options.js               |   2 +-
 features/step_definitions/routability.js           |   1 +
 features/step_definitions/routing.js               |   3 +-
 features/support/config.js                         |  11 +-
 features/support/data.js                           |   6 +-
 features/support/env.js                            |  29 +-
 features/support/hooks.js                          |  10 +-
 features/support/http.js                           |   2 +-
 features/support/launch_classes.js                 |   4 +-
 features/support/route.js                          |  13 +-
 features/support/shared_steps.js                   |   7 +-
 features/testbot/status.feature                    |  18 +-
 features/testbot/summary.feature                   |  77 +++
 features/testbot/traffic_turn_penalties.feature    |  37 ++
 features/testbot/via.feature                       |  41 ++
 include/contractor/contractor.hpp                  |   1 +
 include/contractor/contractor_config.hpp           |   1 +
 include/contractor/crc32_processor.hpp             |  16 +-
 include/contractor/graph_contractor.hpp            |  30 +-
 include/engine/api/route_api.hpp                   |   1 +
 include/engine/base64.hpp                          |   6 +-
 include/engine/datafacade/datafacade_base.hpp      |  22 +-
 include/engine/datafacade/internal_datafacade.hpp  | 116 ++--
 include/engine/datafacade/shared_datafacade.hpp    | 187 +++---
 include/engine/geospatial_query.hpp                |  64 +--
 include/engine/guidance/assemble_leg.hpp           |  24 +-
 include/engine/guidance/assemble_steps.hpp         |  21 +-
 include/engine/guidance/post_processing.hpp        |  10 +
 include/engine/guidance/route_step.hpp             |   5 +
 include/engine/guidance/step_maneuver.hpp          |  16 +-
 include/engine/guidance/toolkit.hpp                |  21 +-
 include/engine/hint.hpp                            |  11 +-
 include/engine/map_matching/bayes_classifier.hpp   |   8 +-
 .../engine/map_matching/hidden_markov_model.hpp    |   4 +-
 include/engine/phantom_node.hpp                    |  12 +-
 include/engine/plugins/trip.hpp                    |   1 -
 include/engine/routing_algorithms/map_matching.hpp |   2 +-
 include/engine/routing_algorithms/routing_base.hpp |  66 ++-
 .../engine/routing_algorithms/shortest_path.hpp    |   7 +-
 include/engine/trip/trip_tabu_search.hpp           |  41 --
 include/extractor/compressed_edge_container.hpp    |   2 +
 include/extractor/guidance/constants.hpp           |   5 +-
 .../extractor/guidance/intersection_handler.hpp    |  11 +-
 .../guidance/intersection_scenario_three_way.hpp   |  27 +
 include/extractor/guidance/motorway_handler.hpp    |   5 +-
 include/extractor/guidance/roundabout_handler.hpp  |  31 +-
 include/extractor/guidance/roundabout_type.hpp     |  21 +
 include/extractor/guidance/toolkit.hpp             | 126 ++--
 include/extractor/guidance/turn_analysis.hpp       |   8 +-
 include/extractor/guidance/turn_classification.hpp |   5 +-
 include/extractor/guidance/turn_handler.hpp        |   7 +-
 include/extractor/guidance/turn_instruction.hpp    |  92 +--
 include/extractor/raster_source.hpp                |  12 +-
 include/extractor/suffix_table.hpp                 |  30 +
 include/extractor/tarjan_scc.hpp                   |  10 +-
 include/server/api/base_parameters_grammar.hpp     | 140 +++--
 include/server/api/match_parameter_grammar.hpp     |  68 +--
 include/server/api/nearest_parameter_grammar.hpp   |  35 +-
 include/server/api/parameters_parser.hpp           |   3 +-
 include/server/api/parsed_url.hpp                  |  10 +-
 include/server/api/route_parameters_grammar.hpp    |  96 ++--
 include/server/api/table_parameter_grammar.hpp     |  64 ++-
 include/server/api/tile_parameter_grammar.hpp      |  33 +-
 include/server/api/trip_parameter_grammar.hpp      |  54 +-
 include/server/service/base_service.hpp            |   2 +-
 include/server/service/match_service.hpp           |   2 +-
 include/server/service/nearest_service.hpp         |   2 +-
 include/server/service/route_service.hpp           |   2 +-
 include/server/service/table_service.hpp           |   2 +-
 include/server/service/tile_service.hpp            |   2 +-
 include/server/service/trip_service.hpp            |   2 +-
 include/storage/shared_memory.hpp                  |   4 +-
 include/util/coordinate.hpp                        |  35 +-
 include/util/coordinate_calculation.hpp            |  43 +-
 include/util/dist_table_wrapper.hpp                |   1 +
 include/util/guidance/toolkit.hpp                  |  48 ++
 include/util/lua_util.hpp                          |   3 +-
 include/util/percent.hpp                           |  18 +-
 include/util/shared_memory_vector_wrapper.hpp      |  12 +
 include/util/static_rtree.hpp                      | 184 +++---
 include/util/typedefs.hpp                          |   7 +-
 include/util/web_mercator.hpp                      |  29 +-
 profiles/bicycle.lua                               |   2 +-
 profiles/car.lua                                   |  18 +
 profiles/foot.lua                                  |   2 +-
 profiles/lib/destination.lua                       |  27 +
 scripts/check_taginfo.py                           |   3 +
 src/benchmarks/static_rtree.cpp                    |   7 +-
 src/contractor/contractor.cpp                      | 156 +++--
 src/engine/api/json_factory.cpp                    |  34 +-
 src/engine/douglas_peucker.cpp                     |  48 +-
 src/engine/guidance/assemble_overview.cpp          |   4 +-
 src/engine/guidance/post_processing.cpp            | 418 +++++++++++---
 src/engine/plugins/trip.cpp                        |  15 +-
 src/extractor/compressed_edge_container.cpp        |  18 +-
 src/extractor/edge_based_graph_factory.cpp         |  43 +-
 src/extractor/extractor.cpp                        |  14 +-
 src/extractor/graph_compressor.cpp                 |   2 +-
 src/extractor/guidance/classification_data.cpp     |   6 +-
 src/extractor/guidance/intersection_generator.cpp  |  55 +-
 src/extractor/guidance/intersection_handler.cpp    |  31 +-
 .../guidance/intersection_scenario_three_way.cpp   |  36 ++
 src/extractor/guidance/motorway_handler.cpp        |  40 +-
 src/extractor/guidance/roundabout_handler.cpp      | 224 ++++++--
 src/extractor/guidance/turn_analysis.cpp           |  17 +-
 src/extractor/guidance/turn_handler.cpp            | 632 ++++-----------------
 src/extractor/raster_source.cpp                    |  18 +-
 src/extractor/scripting_environment.cpp            |  23 +-
 src/extractor/suffix_table.cpp                     |  48 ++
 src/server/api/parameters_parser.cpp               |  51 +-
 src/server/api/url_parser.cpp                      |  30 +-
 src/server/service/match_service.cpp               |   4 +-
 src/server/service/nearest_service.cpp             |   4 +-
 src/server/service/route_service.cpp               |   4 +-
 src/server/service/table_service.cpp               |   4 +-
 src/server/service/tile_service.cpp                |   4 +-
 src/server/service/trip_service.cpp                |   4 +-
 src/server/service_handler.cpp                     |   2 +-
 src/storage/storage.cpp                            |   2 +-
 src/tools/components.cpp                           |  15 +-
 src/tools/contract.cpp                             |   4 +
 src/util/coordinate.cpp                            |  36 --
 src/util/coordinate_calculation.cpp                |  41 +-
 taginfo.json                                       |  10 +
 unit_tests/extractor/raster_source.cpp             |  18 +-
 unit_tests/mocks/mock_datafacade.hpp               |  22 +-
 unit_tests/server/parameters_parser.cpp            |  71 ++-
 unit_tests/server/url_parser.cpp                   |  17 +-
 unit_tests/util/static_rtree.cpp                   |  35 +-
 161 files changed, 4564 insertions(+), 2149 deletions(-)

diff --git a/.gitignore b/.gitignore
index dd46b9f..17e0757 100644
--- a/.gitignore
+++ b/.gitignore
@@ -80,6 +80,7 @@ stxxl.errlog
 /test/profile.lua
 /test/cache
 /test/speeds.csv
+/test/penalties.csv
 /test/data/monaco.*
 node_modules
 
diff --git a/.travis.yml b/.travis.yml
index e62a965..83e6614 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,7 +1,7 @@
-#language: cpp
-# This makes travis use the thin image which boots faster
-language: generic
+language: cpp
 
+git:
+  depth: 10
 
 # sudo:required is needed for trusty images
 sudo: required
@@ -13,112 +13,120 @@ notifications:
 branches:
   only:
     - master
-    - "5.0"
+
+cache:
+  ccache: true
+  apt: true
+
+env:
+  global:
+   - CCACHE_TEMPDIR=/tmp/.ccache-temp
+   - CCACHE_COMPRESS=1
+   - JOBS=4
 
 matrix:
   fast_finish: true
 
+  # We override the compiler names here to yield better ccache behavior, which uses this as key
   include:
 
     # Debug Builds
     - os: linux
-      compiler: gcc
+      compiler: "gcc-5-debug"
       addons: &gcc5
         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', 'pip']
-      env: CCOMPILER='gcc-5' CXXCOMPILER='g++-5' BUILD_TYPE='Debug' COVERAGE=ON
+          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'
 
     - os: linux
-      compiler: gcc
+      compiler: "gcc-4.8-debug"
       addons: &gcc48
         apt:
           sources: ['ubuntu-toolchain-r-test']
-          packages: ['g++-4.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']
+          packages: ['g++-4.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='gcc-4.8' CXXCOMPILER='g++-4.8' BUILD_TYPE='Debug'
 
     - os: linux
-      compiler: clang
+      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']
+          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
 
     - os: osx
       osx_image: xcode7.3
       compiler: clang
-      env: CCOMPILER='clang' CXXCOMPILER='clang++' BUILD_TYPE='Debug'
+      env: CCOMPILER='clang' CXXCOMPILER='clang++' BUILD_TYPE='Debug' JOBS=1 CUCUMBER_TIMEOUT=60000
 
     # Release Builds
     - os: linux
-      compiler: gcc
+      compiler: "gcc-5-release"
       addons: &gcc5
         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']
+          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='Release'
 
-    - os: linux
-      compiler: gcc
-      addons: &gcc48
-        apt:
-          sources: ['ubuntu-toolchain-r-test']
-          packages: ['g++-4.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']
-      env: CCOMPILER='gcc-4.8' CXXCOMPILER='g++-4.8' BUILD_TYPE='Release'
-
-    - os: linux
-      compiler: clang
-      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']
-      env: CCOMPILER='clang-3.8' CXXCOMPILER='clang++-3.8' BUILD_TYPE='Release'
-
-    - os: osx
-      osx_image: xcode7.3
-      compiler: clang
-      env: CCOMPILER='clang' CXXCOMPILER='clang++' BUILD_TYPE='Release'
+      # Disabled because of CI slowness
+      #- os: linux
+      #- compiler: gcc
+      #- addons: &gcc48
+      #-   apt:
+      #-     sources: ['ubuntu-toolchain-r-test']
+      #-     packages: ['g++-4.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']
+      #- env: CCOMPILER='gcc-4.8' CXXCOMPILER='g++-4.8' BUILD_TYPE='Release'
+
+      # Disabled because of CI slowness
+      #- os: linux
+      #- compiler: clang
+      #- 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']
+      #- env: CCOMPILER='clang-3.8' CXXCOMPILER='clang++-3.8' BUILD_TYPE='Release'
+
+      # Disabled because of CI slowness
+      #- os: osx
+      #- osx_image: xcode7.3
+      #- compiler: clang
+      #- env: CCOMPILER='clang' CXXCOMPILER='clang++' BUILD_TYPE='Release'
 
     # Shared Library
     - os: linux
-      compiler: gcc
+      compiler: "gcc-5-release-shared"
       addons: &gcc5
         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']
+          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='Release' BUILD_SHARED_LIBS=ON
 
-    - os: linux
-      compiler: clang
-      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']
-      env: CCOMPILER='clang-3.8' CXXCOMPILER='clang++-3.8' BUILD_TYPE='Release' BUILD_SHARED_LIBS=ON
+      # Disabled because CI slowness
+      #- os: linux
+      #- compiler: clang
+      #- 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']
+      #- env: CCOMPILER='clang-3.8' CXXCOMPILER='clang++-3.8' BUILD_TYPE='Release' BUILD_SHARED_LIBS=ON
 
 before_install:
   - source ./scripts/install_node.sh 4
-
-install:
   - npm install
   - DEPS_DIR="${TRAVIS_BUILD_DIR}/deps"
   - mkdir -p ${DEPS_DIR} && cd ${DEPS_DIR}
   - |
-    if [[ -n "${COVERAGE}" ]]; then
-      pip install --user cpp-coveralls
-    fi
-  - |
     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
       # implicit deps, but seem to be installed by default with recent images: libxml2 GDAL boost
-      brew install cmake libzip libstxxl lua51 luabind tbb md5sha1sum
+      brew install cmake libzip libstxxl lua51 luabind tbb md5sha1sum ccache
     fi
 
-before_script:
+install:
   - cd ${TRAVIS_BUILD_DIR}
   - |
     if [[ "${TRAVIS_OS_NAME}" == "linux" ]]; then
@@ -126,41 +134,45 @@ before_script:
     fi
   - mkdir build && pushd build
   - export CC=${CCOMPILER} CXX=${CXXCOMPILER}
-  - export OSRM_PORT=5000 OSRM_TIMEOUT=6000
-  - cmake .. -DCMAKE_BUILD_TYPE=${BUILD_TYPE} -DBUILD_SHARED_LIBS=${BUILD_SHARED_LIBS:-OFF} -DCOVERAGE=${COVERAGE:-OFF} -DBUILD_TOOLS=1 -DENABLE_CCACHE=0
-
-script:
-  - make --jobs=2
-  - make tests --jobs=2
-  - make benchmarks
+  - 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 --jobs=${JOBS}
+  - make tests --jobs=${JOBS}
+  - make benchmarks --jobs=${JOBS}
+  - echo "travis_fold:end:MAKE"
+  - ccache -s
   - sudo make install
   - |
     if [[ "${TRAVIS_OS_NAME}" == "linux" ]]; then
       sudo ldconfig
     fi
-  - echo "travis_fold:start:UNIT_TESTS"
+  - popd
+  - mkdir example/build && pushd example/build
+  - cmake ..
+  - make
+  - popd
+
+script:
+  - echo "travis_fold:start:BENCHMARK"
+  - make -C test/data benchmark
+  - echo "travis_fold:end:BENCHMARK"
+  - ./example/build/osrm-example test/data/monaco.osrm
+  # All tests assume to be run from the build directory
+  - pushd build
+  - ./unit_tests/library-tests ../test/data/monaco.osrm
   - ./unit_tests/extractor-tests
   - ./unit_tests/engine-tests
   - ./unit_tests/util-tests
   - ./unit_tests/server-tests
-  - echo "travis_fold:end:UNIT_TESTS"
   - popd
-  - echo "travis_fold:start:CUCUMBER"
   - npm test
-  - echo "travis_fold:end:CUCUMBER"
-  - echo "travis_fold:start:BENCHMARK"
-  - make -C test/data benchmark
-  - echo "travis_fold:end:BENCHMARK"
-  - ./build/unit_tests/library-tests test/data/monaco.osrm
-  - mkdir example/build && pushd example/build
-  - cmake ..
-  - make
-  - ./osrm-example ../../test/data/monaco.osrm
-  - popd
 
 after_success:
   - |
-    if [ -n "$RUN_CLANG_FORMAT" ]; then
+    if [ -n "${RUN_CLANG_FORMAT}" ]; then
       ./scripts/format.sh || true # we don't want to fail just yet
     fi
-  - coveralls --build-root build --exclude unit_tests --exclude third_party --exclude node_modules --gcov-options '\-lp'
+  - |
+    if [ -n "${COVERAGE}" ]; then
+      coveralls --build-root build --exclude unit_tests --exclude third_party --exclude node_modules --gcov-options '\-lp'
+    fi
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c6ea195..54ad915 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,12 +1,34 @@
-# 5.0.2
-   - Fixes:
-     - Issue #2335, map matching was using shortest path with uturns disabled
+# 5.1.0
+   Changes with regard to 5.0.0
+
+   - API:
+     - added StepManeuver type `roundabout turn`. The type indicates a small roundabout that is treated as an intersection
+        (turn right at the roundabout for first exit, go straight at the roundabout...)
+     - added StepManeuver type `on ramp` and `off ramp` to distinguish between ramps that enter and exit a highway.
+     - reduced new name instructions for trivial changes
+     - combined multiple turns into a single instruction at segregated roads`
+
+   - Profile Changes:
+    - introduced a suffix_list / get_name_suffix_list to specify name suffices to be suppressed in name change announcements
+    - street names are now consistently assembled for the car, bike and walk profile as: "Name (Ref)" as in "Berlin (A5)"
+    - new `car.lua` dependency `lib/destination.lua`
+    - register a way's .nodes() function for use in the profile's way_function.
+
+   - Infrastructure
+    - BREAKING: reordered internal instruction types. This breaks the **data format**
+    - BREAKING: Changed the on-disk encoding of the StaticRTree for better performance. This breaks the **data format**
 
-# 5.0.1
    - Fixes:
-     - Issue #2309: Fixes local path looping, same coordinates crash
-     - Issue #2311: Fixes invalid assertion in loop unpacking
-     - Issue #2310: Local paths could falsely end up trying to remove the start step
+    - Issue #2310: post-processing for local paths, fixes #2310
+    - Issue #2309: local path looping, fixes #2309
+    - Issue #2356: Make hint values optional
+    - Issue #2349: Segmentation fault in some requests
+    - Issue #2335: map matching was using shortest path with uturns disabled
+    - Issue #2193: Fix syntax error position indicators in parameters queries
+    - Fix search with u-turn
+    - PhantomNode packing in MSVC now the same on other platforms
+    - Summary is now not malformed when including unnamed roads
+    - Emit new-name on when changing fron unanmed road to named road
 
 # 5.0.0
    Changes with regard 5.0.0 RC2:
diff --git a/CMakeLists.txt b/CMakeLists.txt
index dd3d188..8656752 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -8,7 +8,7 @@ endif()
 
 project(OSRM C CXX)
 set(OSRM_VERSION_MAJOR 5)
-set(OSRM_VERSION_MINOR 0)
+set(OSRM_VERSION_MINOR 1)
 set(OSRM_VERSION_PATCH 0)
 
 set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
@@ -25,8 +25,8 @@ else()
   message(WARNING "Building on a 32 bit system is unsupported")
 endif()
 
-if(WIN32 AND MSVC_VERSION LESS 1800)
-  message(FATAL_ERROR "Building with Microsoft compiler needs Visual Studio 2013 or later (Express version works too)")
+if(WIN32 AND MSVC_VERSION LESS 1900)
+  message(FATAL_ERROR "Building with Microsoft compiler needs Latest Visual Studio 2015 (Community or better)")
 endif()
 
 option(ENABLE_CCACHE "Speed up incremental rebuilds via ccache" ON)
@@ -140,6 +140,7 @@ if (COVERAGE)
   if (NOT CMAKE_BUILD_TYPE MATCHES "Debug")
     message(ERROR "COVERAGE=ON only make sense with a Debug build")
   endif()
+  message(INFO "Enabling coverage")
   set(MAYBE_COVERAGE_LIBRARIES "gcov")
   set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ftest-coverage -fprofile-arcs")
 endif()
diff --git a/README.md b/README.md
index 084cfba..f3ace6f 100644
--- a/README.md
+++ b/README.md
@@ -4,13 +4,11 @@ The Open Source Routing Machine is a high performance routing engine written in
 
 ## Current build status
 
-| build config |  branch | status |
-|:-------------|:--------|:------------|
-| Linux        | master  | [![Build Status](https://travis-ci.org/Project-OSRM/osrm-backend.png?branch=master)](https://travis-ci.org/Project-OSRM/osrm-backend) |
-| Linux        | develop | [![Build Status](https://travis-ci.org/Project-OSRM/osrm-backend.png?branch=develop)](https://travis-ci.org/Project-OSRM/osrm-backend) |
-| Windows      | master/develop | [![Build status](https://ci.appveyor.com/api/projects/status/4iuo3s9gxprmcjjh)](https://ci.appveyor.com/project/DennisOSRM/osrm-backend) |
-| LUAbind fork | master  | [![Build Status](https://travis-ci.org/DennisOSRM/luabind.png?branch=master)](https://travis-ci.org/DennisOSRM/luabind) |
-| Coverage     | develop | [![Coverage Status](https://coveralls.io/repos/github/Project-OSRM/osrm-backend/badge.svg?branch=develop)](https://coveralls.io/github/Project-OSRM/osrm-backend?branch=develop) |
+| build config | status |
+|:-------------|:-------|
+| 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) |
 
 ## Building
 
@@ -20,7 +18,46 @@ To quickly try OSRM use our [free and daily updated online service](http://map.p
 
 ## Documentation
 
-See the Wiki's [server API documentation](https://github.com/Project-OSRM/osrm-backend/wiki/Server-api) as well as the [library API documentation](https://github.com/Project-OSRM/osrm-backend/wiki/Library-api)
+### Full documentation
+
+- [osrm-routed HTTP API documentation](docs/http.md)
+- [libosrm API documentation](docs/libosrm.md)
+
+### Quick start
+
+Building OSRM assuming all dependencies are installed:
+
+```
+mkdir -p build
+cd build
+cmake .. -DCMAKE_BUILD_TYPE=Release
+cmake --build .
+sudo cmake --build . --target install
+```
+
+Loading preparing a dataset and starting the server:
+
+```
+osrm-extract data.osm.pbf -p profiles/car.lua
+osrm-contract data.osrm
+osrm-routed data.osrm
+```
+
+Running a query on your local server:
+
+```
+curl http://127.0.0.1:5000/13.388860,52.517037;13.385983,52.496891?steps=true&alternatives=true
+```
+
+### Running a request against the Demo Server
+
+First read the [API usage policy](https://github.com/Project-OSRM/osrm-backend/wiki/Api-usage-policy).
+
+Then run simple query with instructions and alternatives on Berlin:
+
+```
+curl https://router.project-osrm.org/13.388860,52.517037;13.385983,52.496891?steps=true&alternatives=true
+```
 
 ## References in publications
 
diff --git a/appveyor-build.bat b/appveyor-build.bat
index 3ef305d..ae266a5 100644
--- a/appveyor-build.bat
+++ b/appveyor-build.bat
@@ -129,7 +129,7 @@ ECHO running server-tests.exe ...
 unit_tests\%Configuration%\server-tests.exe
 IF %ERRORLEVEL% NEQ 0 GOTO ERROR
 
-IF NOT "%APPVEYOR_REPO_BRANCH%"=="develop" GOTO DONE
+IF NOT "%APPVEYOR_REPO_BRANCH%"=="master" GOTO DONE
 ECHO ========= CREATING PACKAGES ==========
 
 CD %PROJECT_DIR%\build\%Configuration%
diff --git a/appveyor.yml b/appveyor.yml
index b7e09ef..ff95328 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -28,7 +28,6 @@ artifacts:
 branches:
   only:
     - master
-    - develop
 
 deploy:
   provider: FTP
diff --git a/docs/http.md b/docs/http.md
new file mode 100644
index 0000000..cbf0fe8
--- /dev/null
+++ b/docs/http.md
@@ -0,0 +1,509 @@
+## HTTP API
+
+`osrm-routed` supports only `GET` requests of the form. If you your response size
+exceeds the limits of a simple URL encoding, consider using our [NodeJS bindings](https://github.com/Project-OSRM/node-osrm)
+or using the [C++ library directly](libosrm.md).
+
+### Request
+
+```
+http://{server}/{service}/{version}/{profile}/{coordinates}[.{format}]?option=value&option=value
+```
+
+- `server`: location of the server. Example: `127.0.0.1:5000` (default)
+- `service`: Name of the service to be used. Support are the following services:
+  
+    | Service     |           Description                                     |
+    |-------------|-----------------------------------------------------------|
+    | [`route`](#service-route)     | shortest path between given coordinates                   |
+    | [`nearest`](#service-nearest)   | returns the nearest street segment for a given coordinate |
+    | [`table`](#service-table)     | computes distance tables for given coordinates            |
+    | [`match`](#service-match)     | matches given coordinates to the road network             |
+    | [`trip`](#service-trip)      | Compute the shortest round trip between given coordinates |
+    | [`tile`](#service-tile)      | Return vector tiles containing debugging info             |
+  
+- `version`: Version of the protocol implemented by the service.
+- `profile`: Mode of transportation, is determined by the profile that is used to prepare the data
+- `coordinates`: String of format `{longitude},{latitude};{longitude},{latitude}[;{longitude},{latitude} ...]` or `polyline({polyline})`.
+- `format`: Only `json` is supportest at the moment. This parameter is optional and defaults to `json`.
+
+Passing any `option=value` is optional. `polyline` follows Google's polyline format with precision 5 and can be generated using [this package](https://www.npmjs.com/package/polyline).
+To pass parameters to each location some options support an array like encoding:
+
+```
+{option}={element};{element}[;{element} ... ]
+```
+
+The number of elements must match exactly the number of locations. If you don't want to pass a value but instead use the default you can pass an empty `element`.
+
+Example: 2nd location use the default value for `option`:
+
+```
+{option}={element};;{element}
+```
+
+## General options
+
+| Option     | Values                                                 | Description                                      |
+|------------|--------------------------------------------------------|--------------------------------------------------|
+|bearings    |`{bearing};{bearing}[;{bearing} ...]`                   |Limits the search to segments with given bearing in degrees towards true north in clockwise direction. |
+|radiuses    |`{radius};{radius}[;{radius} ...]`                      |Limits the search to given radius in meters.      |
+|hints       |`{hint};{hint}[;{hint} ...]`                            |Hint to derive position in street network.        |
+
+Where the elements follow the following format:
+
+| Element    | Values                                                 |
+|------------|--------------------------------------------------------|
+|bearing     |`{value},{range}` `integer 0 .. 360,integer 0 .. 180`  |
+|radius      |`double >= 0` or `unlimited` (default)                  |
+|hint        |Base64 `string`                                         |
+
+#### Examples
+
+Query on Berlin with three coordinates:
+
+```
+http://router.project-osrm.org/route/v1/driving/13.388860,52.517037;13.397634,52.529407;13.428555,52.523219?overview=false
+```
+
+Using polyline:
+
+```
+http://router.project-osrm.org/route/v1/driving/polyline(ofp_Ik_vpAilAyu@te@g`E)?overview=false
+```
+
+### Response
+
+Every response object has a `code` field.
+
+```json
+{
+"code": {code},
+"message": {message}
+}
+```
+
+Where `code` is on one of the strings below or service dependent:
+
+| Type              | Description                                                                      |
+|-------------------|----------------------------------------------------------------------------------|
+| `Ok`              | Request could be processed as expected.                                          |
+| `InvalidUrl`      | URL string is invalid.                                                           |
+| `InvalidService`  | Service name is invalid.                                                         |
+| `InvalidVersion`  | Version is not found.                                                            |
+| `InvalidOptions`  | Options are invalid.                                                             |
+| `NoSegment`       | One of the supplied input coordinates could not snap to street segment.          |
+| `TooBig`          | The request size violates one of the service specific request size restrictions. |
+
+`message` is a **optional** human-readable error message. All other status types are service dependent.
+
+In case of an error the HTTP status code will be `400`. Otherwise the HTTP status code will be `200` and `code` will be `Ok`.
+
+## Service `nearest`
+
+Snaps a coordinate to the street network and returns the nearest n matches.
+
+### Request
+
+```
+http://{server}/nearest/v1/{profile}/{coordinates}.json?number={number}
+```
+
+Where `coordinates` only supports a single `{longitude},{latitude}` entry.
+
+In addition to the [general options](#general-options) the following options are supported for this service:
+
+|Option      |Values                        |Description                                         |
+|------------|------------------------------|----------------------------------------------------|
+|number      |`integer >= 1` (default `1`)  |Number of nearest segments that should be returned. |
+
+### Response
+
+- `code` if the request was successful `Ok` otherwise see the service dependent and general status codes.
+- `waypoints` array of `Waypoint` objects sorted by distance to the input coordinate. Each object has at least the following additional properties:
+  - `distance`: Distance in meters to the supplied input coordinate.
+
+### Examples
+
+Querying nearest three snapped locations of `13.388860,52.517037` with a bearing between `20° - 340°`.
+
+```
+http://router.project-osrm.org/nearest/v1/driving/13.388860,52.517037?number=3&bearings=0,20
+```
+
+## Service `route`
+
+### Request
+
+```
+http://{server}/route/v1/{profile}/{coordinates}?alternatives={true|false}&steps={true|false}&geometries={polyline|geojson}&overview={full|simplified|false}
+```
+
+In addition to the [general options](#general-options) the following options are supported for this service:
+
+|Option      |Values                                    |Description                                                                    |
+|------------|------------------------------------------|-------------------------------------------------------------------------------|
+|alternatives|`true`, `false` (default)                 |Search for alternative routes and return as well.\*                            |
+|steps       |`true`, `false` (default)                 |Return route steps for each route leg                                          |
+|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. |
+
+\* Please note that even if an alternative route is requested, a result cannot be guaranteed.
+
+### Response
+
+- `code` if the request was successful `Ok` otherwise see the service dependent and general status codes.
+- `waypoints`: Array of `Waypoint` objects representing all waypoints in order:
+- `routes`: An array of `Route` objects, ordered by descending recommendation rank.
+
+In case of error the following `code`s are supported in addition to the general ones:
+
+| Type              | Description     |
+|-------------------|-----------------|
+| `NoRoute`        | No route found. |
+
+All other fields might be undefined.
+
+## Service `table`
+### Request
+```
+http://{server}/table/v1/{profile}/{coordinates}?{sources}=[{elem}...];&destinations=[{elem}...]`
+```
+
+This computes duration tables for the given locations. Allows for both symmetric and asymmetric tables.
+
+### Coordinates
+
+In addition to the [general options](#general-options) the following options are supported for this service:
+
+|Option      |Values                                            |Description                                  |
+|------------|--------------------------------------------------|---------------------------------------------|
+|sources     |`{index};{index}[;{index} ...]` or `all` (default)|Use location with given index as source.     |
+|destinations|`{index};{index}[;{index} ...]` or `all` (default)|Use location with given index as destination.|
+
+Unlike other array encoded options, the length of `sources` and `destinations` can be **smaller or equal**
+to number of input locations;
+
+Example:
+
+```
+sources=0;5;7&destinations=5;1;4;2;3;6
+```
+
+|Element     |Values                       |
+|------------|-----------------------------|
+|index       |`0 <= integer < #locations`  |
+
+### Response
+
+- `code` if the request was successful `Ok` otherwise see the service dependent and general status codes.
+- `durations` array of arrays that stores the matrix in row-major order. `durations[i][j]` gives the travel time from
+  the i-th waypoint to the j-th waypoint. Values are given in seconds.
+- `sources` array of `Waypoint` objects describing all sources in order
+- `destinations` array of `Waypoint` objects describing all destinations in order
+
+In case of error the following `code`s are supported in addition to the general ones:
+
+| Type              | Description     |
+|-------------------|-----------------|
+| `NoTable`        | No route found. |
+
+All other fields might be undefined.
+
+#### Examples
+
+Returns a `3x3` matrix:
+```
+http://router.project-osrm.org/table/v1/driving/13.388860,52.517037;13.397634,52.529407;13.428555,52.523219
+```
+
+Returns a `1x3` matrix:
+```
+http://router.project-osrm.org/table/v1/driving/13.388860,52.517037;13.397634,52.529407;13.428555,52.523219?sources=0
+```
+
+Returns a asymmetric 3x2 matrix with from the polyline encoded locations `qikdcB}~dpXkkHz`:
+```
+http://router.project-osrm.org/table/v1/driving/qikdcB}~dpXkkHz?sources=0;1;3&destinations=2;4
+```
+
+## Service `match`
+
+Map matching matches given GPS points to the road network in the most plausible way.
+Please note the request might result multiple sub-traces. Large jumps in the timestamps (>60s) or improbable transitions lead to trace splits if a complete matching could not be found.
+The algorithm might not be able to match all points. Outliers are removed if they can not be matched successfully.
+
+### Request
+
+```
+http://{server}/match/v1/{profile}/{coordinates}?steps={true|false}&geometries={polyline|geojson}&overview={simplified|full|false}
+```
+
+In addition to the [general options](#general-options) the following options are supported for this service:
+
+
+|Option      |Values                                          |Description                                                                               |
+|------------|------------------------------------------------|------------------------------------------------------------------------------------------|
+|steps       |`true`, `false` (default)                       |Return route steps for each route                                                         |
+|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.|
+|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.|
+
+|Parameter   |Values                        |
+|------------|------------------------------|
+|timestamp   |`integer` UNIX-like timestamp |
+|radius      |`double >= 0` (default 5m)    |
+
+### Response
+- `code` if the request was successful `Ok` otherwise see the service dependent and general status codes.
+- `tracepoints`: Array of `Ẁaypoint` objects representing all points of the trace in order.
+  If the trace point was ommited by map matching because it is an outlier, the entry will be `null`.
+  Each `Waypoint` object has the following additional properties:
+  - `matchings_index`: Index to the `Route` object in `matchings` the sub-trace was matched to.
+  - `waypoint_index`: Index of the waypoint inside the matched route.
+- `matchings`: An array of `Route` objects that assemble the trace. Each `Route` object has the following additional properties:
+  - `confidence`: Confidence of the matching. `float` value between 0 and 1. 1 is very confident that the matching is correct.
+
+In case of error the following `code`s are supported in addition to the general ones:
+
+| Type              | Description         |
+|-------------------|---------------------|
+| `NoMatch`         | No matchings found. |
+
+All other fields might be undefined.
+
+## Service `trip`
+
+The trip plugin solves the Traveling Salesman Problem using a greedy heuristic (farthest-insertion algorithm).
+The returned path does not have to be the shortest path, as TSP is NP-hard it is only an approximation.
+Note that if the input coordinates can not be joined by a single trip (e.g. the coordinates are on several disconnected islands)
+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}
+```
+
+In addition to the [general options](#general-options) the following options are supported for this service:
+
+|Option      |Values                                          |Description                                                                |
+|------------|------------------------------------------------|---------------------------------------------------------------------------|
+|steps       |`true`, `false` (default)                       |Return route instructions for each trip                                    |
+|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.|
+
+### Response
+
+- `code` if the request was successful `Ok` otherwise see the service dependent and general status codes.
+- `waypoints`: Array of `Waypoint` objects representing all waypoints in input order. Each `Waypoint` object has the following additional properties:
+  - `trips_index`: Index to `trips` of the sub-trip the point was matched to.
+  - `waypoint_index`: Index of the point in the trip.
+- `trips`: An array of `Route` objects that assemble the trace.
+
+In case of error the following `code`s are supported in addition to the general ones:
+
+| Type              | Description         |
+|-------------------|---------------------|
+| `NoTrips`        | No trips found.     |
+
+All other fields might be undefined.
+
+## Result objects
+
+### Route
+
+Represents a route through (potentially multiple) waypoints.
+
+#### Properties
+
+- `distance`: The distance traveled by the route, in `float` meters.
+- `duration`: The estimated travel time, in `float` number of seconds.
+- `geometry`: The whole geometry of the route value depending on `overview` parameter, format depending on the `geometries` parameter. See `RouteStep`'s `geometry` field for a parameter documentation.
+  
+  | overview   | Description                 |
+  |------------|-----------------------------|
+  | simplified | Geometry is simplified according to the highest zoom level it can still be displayed on full. |
+  | full       | Geometry is not simplified. |
+  | false      | Geometry is not added.      |
+  
+- `legs`: The legs between the given waypoints, an array of `RouteLeg` objects.
+
+#### Example
+
+Three input coordinates, `geometry=geojson`, `steps=false`:
+
+```json
+{
+  "distance": 90.0,
+  "duration": 300.0,
+  "geometry": {"type": "LineString", "coordinates": [[120., 10.], [120.1, 10.], [120.2, 10.], [120.3, 10.]]},
+  "legs": [
+    {
+      "distance": 30.0,
+      "duration": 100.0,
+      "steps": []
+    },
+    {
+      "distance": 60.0,
+      "duration": 200.0,
+      "steps": []
+    }
+  ]
+}
+```
+
+### RouteLeg
+
+Represents a route between two waypoints.
+
+#### Properties
+
+- `distance`: The distance traveled by this route leg, in `float` meters.
+- `duration`: The estimated travel time, in `float` number of seconds.
+- `summary`: Summary of the route taken as `string`. Depends on the `steps` parameter:
+   
+   | steps        |                                                                       |
+   |--------------|-----------------------------------------------------------------------|
+   | true         | Names of the two major roads used. Can be empty if route is too short.|
+   | false        | empty `string`                                                        |
+
+- `steps`: Depends on the `steps` parameter.
+   
+   | steps        |                                                                       |
+   |--------------|-----------------------------------------------------------------------|
+   | true         | array of `RouteStep` objects describing the turn-by-turn instructions |
+   | false        | empty array                                                           |
+
+#### Example
+
+With `steps=false`:
+
+```json
+{
+  "distance": 30.0,
+  "duration": 100.0,
+  "steps": []
+}
+```
+
+### RouteStep
+
+A step consists of a maneuver such as a turn or merge, followed
+by a distance of travel along a single way to the subsequent
+step.
+
+#### Properties
+
+- `distance`: The distance of travel from the maneuver to the subsequent step, in `float` meters.
+- `duration`: The estimated travel time, in `float` number of seconds.
+- `geometry`: The unsimplified geometry of the route segment, depending on the `geometries` parameter.
+  
+  | geometries |                                                                    |
+  |------------|--------------------------------------------------------------------|
+  | polyline   | [polyline](https://www.npmjs.com/package/polyline) with precision 5 in [latitude,longitude] encoding |
+  | 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.
+- `mode`: A string signifying the mode of transportation.
+- `maneuver`: A `StepManeuver` object representing the maneuver.
+
+#### Example
+
+### StepManeuver
+
+#### Properties
+
+- `location`: A `[longitude, latitude]` pair describing the location of the turn.
+- `bearing_before`: The clockwise angle from true north to the
+  direction of travel immediately before the maneuver.
+- `bearing_after`: The clockwise angle from true north to the
+  direction of travel immediately after the maneuver.
+- `type` A string indicating the type of maneuver. **new identifiers might be introduced without API change**
+   Types  unknown to the client should be handled like the `turn` type, the existance of correct `modifier` values is guranteed.
+  
+  | `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`                  |
+  | 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`) |
+  | ramp              | **Deprecated**. Replaced by `on_ramp` and `off_ramp`.        |
+  | on ramp           | take a ramp to enter a highway (direction given my `modifier`) |
+  | off ramp          | take a ramp to exit a highway (direction given my `modifier`)  |
+  | fork              | take the left/right side at a fork depending on `modifier`   |
+  | end of road       | road ends in a T intersection turn in direction of `modifier`|
+  | continue          | Turn in direction of `modifier` to stay on the same road     |
+  | roundabout        | traverse roundabout, has additional field `exit` with NR if the roundabout is left. `the modifier specifies the direction of entering the roundabout` |
+  | rotary            | a larger version of a roundabout, can offer `rotary_name` in addition to the `exit` parameter.  |
+  | roundabout turn   | Describes a turn at a small roundabout that should be treated as normal turn. The `modifier` indicates the turn direciton. Example instruction: `At the roundabout turn left`. |
+  | notification      | not an actual turn but a change in the driving conditions. For example the travel mode.  If the road takes a turn itself, the `modifier` describes the direction |
+
+  Please note that even though there are `new name` and `notification` instructions, the `mode` and `name` can change
+  between all instructions. They only offer a fallback in case nothing else is to report.
+
+
+- `modifier` An optional `string` indicating the direction change of the maneuver.
+
+  | `modifier`        | Description                               |
+  |-------------------|-------------------------------------------|
+  | uturn             | indicates  reversal of direction          |
+  | sharp right       | a sharp right turn                        |
+  | right             | a normal turn to the right                |
+  | slight right      | a slight turn to the right                |
+  | straight          | no relevant change in direction           |
+  | slight left       | a slight turn to the left                 |
+  | left              | a normal turn to the left                 |
+  | sharp left        | a sharp turn to the left                  |
+
+ The list of turns without a modifier is limited to: `depart/arrive`. If the source/target location is close enough to the `depart/arrive` location, no modifier will be given.
+  
+  The meaning depends on the `type` field.
+    
+  | `type`                 | Description                                                                                                               |
+  |------------------------|---------------------------------------------------------------------------------------------------------------------------|
+  | `turn`                 | `modifier` indicates the change in direction accomplished through the turn                                                |
+  | `depart`/`arrive`      | `modifier` indicates the position of departure point and arrival point in relation to the current direction of travel      |
+ 
+
+- `exit` An optional `integer` indicating number of the exit to take. The field exists for the following `type` field:
+  
+  | `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` |
+  
+
+New properties (potentially depending on `type`) may be introduced in the future without an API version change.
+
+### Waypoint
+
+Object used to describe waypoint on a route.
+
+#### Properties
+
+- `name` Name of the street the coordinate snapped to
+- `location` Array that contains the `[longitude, latitude]` pair of the snapped coordinate
+- `hint` Unique internal identifier of the segment (ephemeral, not constant over data updates)
+   This can be used on subsequent request to significantly speed up the query and to connect multiple services.
+   E.g. you can use the `hint` value obtained by the `nearest` query as `hint` values for `route` inputs.
+
+## Service `tile`
+
+This generates [Mapbox Vector Tiles](https://www.mapbox.com/developers/vector-tiles/) that can be viewed with a vector-tile capable slippy-map viewer.  The tiles contain road geometries and metadata that can be used to examine the routing graph.  The tiles are generated directly from the data in-memory, so are in sync with actual routing results, and let you examine which roads are actually routable, and what weights they have applied.
+
+### Request
+```
+http://{server}/tile/v1/{profile}/tile({x},{y},{zoom}).mvt
+```
+
+The `x`, `y`, and `zoom` values are the same as described at https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames, and are supported by vector tile viewers like [Mapbox GL JS](https://www.mapbox.com/mapbox-gl-js/api/).
+
+### Response
+
+The response object is either a binary encoded blob with a `Content-Type` of `application/x-protobuf`, or a `404` error.  Note that OSRM is hard-coded to only return tiles from zoom level 12 and higher (to avoid accidentally returning extremely large vector tiles).
+
+Vector tiles contain just a single layer named `speeds`.  Within that layer, features can have `speed` (int) and `is_small` (boolean) attributes.
diff --git a/docs/libosrm.md b/docs/libosrm.md
new file mode 100644
index 0000000..7329b8c
--- /dev/null
+++ b/docs/libosrm.md
@@ -0,0 +1,26 @@
+OSRM can be used as a library (libosrm) via C++ instead of using it through the HTTP interface and `osrm-routed`. This allows for fine-tuning OSRM and has much less overhead. Here is a quick introduction into how to use `libosrm` in the upcoming v5 release.
+
+Take a look at the example code that lives in the [example directory](https://github.com/Project-OSRM/osrm-backend/tree/master/example). Here is all you ever wanted to know about `libosrm`, that is a short description of what the types do and where to find documentation on it:
+
+- [`EngineConfig`](https://github.com/Project-OSRM/osrm-backend/blob/master/include/engine/engine_config.hpp) - for initializing an OSRM instance we can configure certain properties and constraints. E.g. the storage config is the base path such as `france.osm.osrm` from which we derive and load `france.osm.osrm.*` auxiliary files. This also lets you set constraints such as the maximum number of locations allowed for specific services.
+
+- [`OSRM`](https://github.com/Project-OSRM/osrm-backend/blob/master/include/osrm/osrm.hpp) - this is the main Routing Machine type with functions such as `Route` and `Table`. You initialize it with a `EngineConfig`. It does all the heavy lifting for you. Each function takes its own parameters, e.g. the `Route` function takes `RouteParameters`, and a out-reference to a JSON result that gets filled. The return value is a `Status`, indicating error or success.
+
+- [`Status`](https://github.com/Project-OSRM/osrm-backend/blob/master/include/engine/status.hpp) - this is a type wrapping `Error` or `Ok` for indicating error or success, respectively.
+
+- [`TableParameters`](https://github.com/Project-OSRM/osrm-backend/blob/master/include/engine/api/table_parameters.hpp) - this is an example of parameter types the Routing Machine functions expect. In this case `Table` expects its own parameters as `TableParameters`. You can see it wrapping two vectors, sources and destinations --- these are indices into your coordinates for the table service to construct a matrix from (empty sources or destinations means: use all of them). If you ask yo [...]
+
+- [`BaseParameter`](https://github.com/Project-OSRM/osrm-backend/blob/master/include/engine/api/base_parameters.hpp) - this most importantly holds coordinates (and a few other optional properties that you don't need for basic usage); the specific parameter types inherit from `BaseParameters` to get these member attributes. That means your `TableParameters` type has `coordinates`, `sources` and `destination` member attributes (and a few other that we ignore for now).
+
+- [`Coordinate`](https://github.com/Project-OSRM/osrm-backend/blob/master/include/util/coordinate.hpp) - this is a wrapper around a (longitude, latitude) pair. We really don't care about (lon,lat) vs (lat, lon) but we don't want you to accidentally mix them up, so both latitude and longitude are strictly typed wrappers around integers (fixed notation such as `13423240`) and floating points (floating notation such as `13.42324`).
+
+- [Parameters for other services](https://github.com/Project-OSRM/osrm-backend/tree/master/include/engine/api) - here are all other `*Parameters` you need for other Routing Machine services.
+
+- [JSON](https://github.com/Project-OSRM/osrm-backend/blob/master/include/util/json_container.hpp) - this is a sum type resembling JSON. The Routing Machine service functions take a out-ref to a JSON result and fill it accordingly. It is currently implemented using [mapbox/variant](https://github.com/mapbox/variant) which is similar to [Boost.Variant](http://www.boost.org/doc/libs/1_55_0/doc/html/variant.html) (Boost documentation is great). There are two ways to work with this sum type: [...]
+
+------------------------------------------------------------------------------------------------------------------
+
+To summarize:
+ - create an `OSRM` instance initialized with a `EngineConfig`
+ - call the service function on the `OSRM` object providing service specific `*Parameters`
+ - check the return code and use the JSON result
diff --git a/docs/profiles.md b/docs/profiles.md
new file mode 100644
index 0000000..c36267e
--- /dev/null
+++ b/docs/profiles.md
@@ -0,0 +1,34 @@
+OSRM supports "profiles". Configurations representing different routing behaviours for (typically) different transport modes. A profile describes whether or not we route along a particular type of way, or over a particular node in the OpenStreetMap data, and also how quickly we'll be travelling when we do. This feeds into the way the routing graph is created and thus influences the output routes.
+
+## Available profiles
+
+Out-of-the-box OSRM comes with several different profiles, including car, bicycle and foot.
+
+Profile configuration files have a 'lua' extension, and are found under the 'profiles' subdirectory.
+Alternatively commands will take a lua profile specified with an explicit -p param, for example:
+
+`osrm-extract -p ../profiles/car.lua planet-latest.osm.pbf`
+
+And then **you will need to extract and contract again** (A change to the profile will typically affect the extract step as well as the contract step. See [Processing Flow](https://github.com/Project-OSRM/osrm-backend/wiki/Processing-Flow))
+
+## lua scripts?
+
+Profiles are not just configuration files. They are scripts written in the "lua" scripting language ( http://www.lua.org )  The reason for this, is that OpenStreetMap data is not sufficiently straightforward, to simply define tag mappings. Lua scripting offers a powerful way of coping with the complexity of different node,way,relation,tag combinations found within OpenStreetMap data.
+
+## Basic structure of a profile
+
+You can understand these lua scripts enough to make interesting modifications, without needing to get to grips with how they work completely.
+
+Towards the top of the file, a profile (such as [car.lua](../profiles/car.lua)) will typically define various configurations as global variables. A lot of these are look-up hashes of one sort or another.
+
+As you scroll down the file you'll see local variables, and then local functions, and finally...
+
+`way_function` and `node_function` are the important functions which are called when extracting OpenStreetMap data with `osrm-extract`.
+
+## way_function
+
+Given an OpenStreetMap way, the way_function will either return nothing (meaning we are not going to route over this way at all), or it will set up a result hash to be returned. The most important thing it will do is set the value of `result.forward_speed` and `result.backward_speed` as a suitable integer value representing the speed for traversing the way.
+
+All other calculations stem from that, including the returned timings in driving directions, but also, less directly, it feeds into the actual routing decisions the engine will take (a way with a slow traversal speed, may be less favoured than a way with fast traversal speed, but it depends how long it is, and... what it connects to in the rest of the network graph)
+
+Using the power of the scripting language you wouldn't typically see something as simple as a `result.forward_speed = 20` line within the way_function. Instead a way_function will examine the tagging (e.g. `way:get_value_by_key("highway")` and many others), process this information in various ways, calling other local functions, referencing the global variables and look-up hashes, before arriving at the result.
diff --git a/docs/releasing.md b/docs/releasing.md
new file mode 100644
index 0000000..bae0809
--- /dev/null
+++ b/docs/releasing.md
@@ -0,0 +1,50 @@
+# Releasing a new OSRM version
+
+Do decide if this is a major or minor version bump use: http://semver.org/
+
+What we guarantee on major version changes:
+
+- Breaking changes will be in the changelog
+- If we break an HTTP API we bump the version
+
+What we guarantee on minor version changes:
+
+- HTTP API does not include breaking changes
+- C++ library API does not include breaking changes
+- node-osrm API does not include breaking changes
+
+What we DO NOT guarantee on minor version changes:
+
+- file format comp ability. Breakage will be listed in the changelog.
+- new turn types and fields may be introduced. How to handle this see [the HTTP API docs](http.md).
+
+What we guarantee on patch version changes:
+
+- HTTP API does not include breaking changes
+- C++ library API does not include breaking changes
+- node-osrm API does not include breaking changes
+- full file format compatibility
+
+## Major or Minor release x.y
+
+1. Make sure all tests are passing (e.g. Travis CI gives you a :thumbs_up:)
+2. Make sure `CHANGELOG.md` is up to date.
+3. Make sure the OSRM version in `CMakeLists.txt` is up to date
+4. Use an annotated tag to mark the release: `git tag vx.y.0 -a` Body of the tag description should be the changelog entries.
+5. Push tags and commits: `git push; git push --tags`
+6. Branch of the `vx.y.0` tag to create a release branch `x.y`:
+   `git branch x.y. vx.y.0; git push -u x.y:origin/x.y`
+7. Modify `.travis.yml` to allow builds for the `x.y` branch.
+8. Write a mailing-list post to osrm-talk at openstreetmap.org to announce the release
+
+## Patch release x.y.z
+
+1. Check out the appropriate release branch x.y
+2. Make sure all fixes are listed in the changelog and included in the branch
+3. Make sure all tests are passing (e.g. Travis CI gives you a :thumbs_up:)
+4. Make sure the OSRM version in `CMakeLists.txt` is up to date
+5. Use an annotated tag to mark the release: `git tag vx.y.z -a` Body of the tag description should be the changelog entries.
+6. Push tags and commits: `git push; git push --tags`
+7. Proceede with the `node-osrm` release as outlined in the repository.
+8. Write a mailing-list post to osrm-talk at openstreetmap.org to announce the release
+
diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt
index 86a8a7f..9ad67cb 100644
--- a/example/CMakeLists.txt
+++ b/example/CMakeLists.txt
@@ -19,8 +19,8 @@ else()
   message(WARNING "Building on a 32 bit system is unsupported")
 endif()
 
-if(WIN32 AND MSVC_VERSION LESS 1800)
-  message(FATAL_ERROR "Building with Microsoft compiler needs Visual Studio 2013 or later (Express version works too)")
+if(WIN32 AND MSVC_VERSION LESS 1900)
+  message(FATAL_ERROR "Building with Microsoft compiler needs Latest Visual Studio 2015 (Community or better)")
 endif()
 
 add_executable(osrm-example example.cpp)
diff --git a/features/bicycle/names.feature b/features/bicycle/names.feature
index f67f6b1..b196857 100644
--- a/features/bicycle/names.feature
+++ b/features/bicycle/names.feature
@@ -10,13 +10,13 @@ Feature: Bike - Street names in instructions
             |   | c |
 
         And the ways
-            | nodes | name     |
-            | ab    | My Way   |
-            | bc    | Your Way |
+            | nodes | name     | ref |
+            | ab    | My Way   | A6  |
+            | bc    | Your Way | A7  |
 
         When I route I should get
-            | from | to | route                    |
-            | a    | c  | My Way,Your Way,Your Way |
+            | from | to | route                                   |
+            | a    | c  | My Way (A6),Your Way (A7),Your Way (A7) |
 
     @unnamed
     Scenario: Bike - Use way type to describe unnamed ways
diff --git a/features/bicycle/ref.feature b/features/bicycle/ref.feature
index 195089e..a9c9084 100644
--- a/features/bicycle/ref.feature
+++ b/features/bicycle/ref.feature
@@ -14,7 +14,7 @@ Feature: Bike - Way ref
 
         When I route I should get
             | from | to | route                               |
-            | a    | b  | Utopia Drive / E7,Utopia Drive / E7 |
+            | a    | b  | Utopia Drive (E7),Utopia Drive (E7) |
 
     Scenario: Bike - Way with only ref
         Given the node map
diff --git a/features/car/traffic.feature b/features/car/traffic_speeds.feature
similarity index 96%
rename from features/car/traffic.feature
rename to features/car/traffic_speeds.feature
index 6d0c62d..84335d0 100644
--- a/features/car/traffic.feature
+++ b/features/car/traffic_speeds.feature
@@ -31,7 +31,7 @@ Feature: Traffic - speeds
         4,1,27
         """
 
-    Scenario: Weighting not based on raster sources
+    Scenario: Weighting based on speed file
         Given the profile "testbot"
         Given the extract extra arguments "--generate-edge-lookup"
         Given the contract extra arguments "--segment-speed-file speeds.csv"
diff --git a/features/car/traffic_turn_penalties.feature b/features/car/traffic_turn_penalties.feature
new file mode 100644
index 0000000..e0c3727
--- /dev/null
+++ b/features/car/traffic_turn_penalties.feature
@@ -0,0 +1,97 @@
+ at routing @speed @traffic
+Feature: Traffic - turn penalties
+
+    Background: Evenly spaced grid with multiple intersections
+        Given the node map
+            |      | a:1  |      | b:2  |      |
+            | c:3  | d:4  | e:5  | f:6  | g:7  |
+            |      | h:8  |      | i:9  |      |
+            | j:10 | k:11 | l:12 | m:13 | n:14 |
+            |      | o:15 |      | p:16 |      |
+        And the ways
+            | nodes | highway |
+            | ad    | primary |
+            | cd    | primary |
+            | de    | primary |
+            | dhk   | primary |
+
+            | bf    | primary |
+            | ef    | primary |
+            | fg    | primary |
+            | fim   | primary |
+
+            | jk    | primary |
+            | kl    | primary |
+            | ko    | primary |
+
+            | lm    | primary |
+            | mn    | primary |
+            | mp    | primary |
+        And the profile "car"
+        And the extract extra arguments "--generate-edge-lookup"
+
+    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 |
+                                                                  # straight
+            | i    | g  | fim,fg,fg      | 59 km/h | 12s  +-1  |
+                                                                  # right
+            | a    | e  | ad,de,de       | 57 km/h | 12.5s +-1 |
+                                                                  # left
+            | c    | g  | cd,de,ef,fg,fg | 63 km/h | 23s +-1   |
+                                                                  # double straight
+            | 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   |
+                                                                  # straight-left
+            | l    | e  | kl,dhk,de,de   | 59 km/h | 24.5s +-1 |
+                                                                  # double right
+            | g    | n  | fg,fim,mn,mn   | 57 km/h | 25s +-1   |
+                                                                  # double left
+
+    Scenario: Weighting based on turn penalty file
+        Given the turn penalty file
+            """
+            9,6,7,1.8
+            9,13,14,24.5
+            8,4,3,26
+            12,11,8,9
+            8,11,12,13
+            1,4,5,-0.2
+            """
+        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 |
+                                                                              # straight
+            | i    | g  | fim,fg,fg                | 55 km/h | 13s +-1   |
+                                                                              # right - ifg penalty
+            | a    | e  | ad,de,de                 | 64 km/h | 11s +-1   |
+                                                                              # left - faster because of negative ade penalty
+            | c    | g  | cd,de,ef,fg,fg           | 63 km/h | 23s +-1   |
+                                                                              # double straight
+            | 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 |
+                                                                              # was straight-left - forced around by hkl penalty
+            | l    | e  | lm,fim,ef,ef             | 57 km/h | 25s +-1   |
+                                                                              # double right - forced left by lkh penalty
+            | 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   |
+                                                                              # double left - hdc penalty ever so slightly higher than imn; forces all the way around
+
+    Scenario: Too-negative penalty clamps, but does not fail
+        Given the contract extra arguments "--turn-penalty-file penalties.csv"
+        And the profile "testbot"
+        And the turn penalty file
+            """
+            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 |
diff --git a/features/foot/names.feature b/features/foot/names.feature
index a765de6..6a28aaf 100644
--- a/features/foot/names.feature
+++ b/features/foot/names.feature
@@ -10,13 +10,13 @@ Feature: Foot - Street names in instructions
             |   | c |
 
         And the ways
-            | nodes | name     |
-            | ab    | My Way   |
-            | bc    | Your Way |
+            | nodes | name     | ref |
+            | ab    | My Way   | A6  |
+            | bc    | Your Way | B7  |
 
         When I route I should get
-            | from | to | route                    |
-            | a    | c  | My Way,Your Way,Your Way |
+            | from | to | route                                   |
+            | a    | c  | My Way (A6),Your Way (B7),Your Way (B7) |
 
     @unnamed
     Scenario: Foot - Use way type to describe unnamed ways
diff --git a/features/foot/ref.feature b/features/foot/ref.feature
index 8fed586..39bc3e8 100644
--- a/features/foot/ref.feature
+++ b/features/foot/ref.feature
@@ -14,7 +14,7 @@ Feature: Foot - Way ref
 
         When I route I should get
             | from | to | route                               |
-            | a    | b  | Utopia Drive / E7,Utopia Drive / E7 |
+            | a    | b  | Utopia Drive (E7),Utopia Drive (E7) |
 
     Scenario: Foot - Way with only ref
         Given the node map
diff --git a/features/guidance/bridges_and_tunnels.feature b/features/guidance/bridges_and_tunnels.feature
new file mode 100644
index 0000000..b1ee735
--- /dev/null
+++ b/features/guidance/bridges_and_tunnels.feature
@@ -0,0 +1,120 @@
+ at routing @car @bridge @tunnel @guidance
+Feature: Car - Guidance - Bridges and Tunnels
+    Background:
+        Given the profile "car"
+        And a grid size of 100 meters
+
+    Scenario: Simple Bridge
+        Given the node map
+            | a | b | c | d |
+
+        And the ways
+            | nodes | highway | bridge | name               |
+            | ab    | primary |        | Hauptstraße        |
+            | bc    | primary | yes    | Hauptstraßenbrücke |
+            | cd    | primary |        | Hauptstraße        |
+
+        When I route I should get
+            | from | to | route                   | turns         |
+            | a    | d  | Hauptstraße,Hauptstraße | depart,arrive |
+
+    Scenario: Bridge with Immediate Turn
+        Given the node map
+            |   |   |   | d |
+            | a |   | b | c |
+            |   |   |   | e |
+
+        And the ways
+            | nodes | highway | bridge | name               |
+            | ab    | primary |        | Hauptstraße        |
+            | bc    | primary | yes    | Hauptstraßenbrücke |
+            | dce   | primary |        | Nebenstraße        |
+
+        When I route I should get
+            | from | to | route                                      | turns                           |
+            | a    | d  | Hauptstraße,Nebenstraße,Nebenstraße        | depart,end of road left,arrive  |
+            | a    | e  | Hauptstraße,Nebenstraße,Nebenstraße        | depart,end of road right,arrive |
+            | e    | a  | Nebenstraße,Hauptstraßenbrücke,Hauptstraße | depart,turn left,arrive         |
+            | d    | a  | Nebenstraße,Hauptstraßenbrücke,Hauptstraße | depart,turn right,arrive        |
+
+    Scenario: Bridge with Immediate Turn Front and Back
+        Given the node map
+            | f |   |   | d |
+            | a |   | b | c |
+            | g |   |   | e |
+
+        And the ways
+            | nodes | highway | bridge | name               |
+            | ab    | primary |        | Hauptstraße        |
+            | bc    | primary | yes    | Hauptstraßenbrücke |
+            | dce   | primary |        | Nebenstraße        |
+            | gaf   | primary |        | Anderestraße       |
+
+        When I route I should get
+            | from | to | route                                                    | turns                                      |
+            | f    | d  | Anderestraße,Hauptstraße,Nebenstraße,Nebenstraße         | depart,turn left,end of road left,arrive   |
+            | f    | e  | Anderestraße,Hauptstraße,Nebenstraße,Nebenstraße         | depart,turn left,end of road right,arrive  |
+            | g    | d  | Anderestraße,Hauptstraße,Nebenstraße,Nebenstraße         | depart,turn right,end of road left,arrive  |
+            | g    | e  | Anderestraße,Hauptstraße,Nebenstraße,Nebenstraße         | depart,turn right,end of road right,arrive |
+            | e    | f  | Nebenstraße,Hauptstraßenbrücke,Anderestraße,Anderestraße | depart,turn left,end of road right,arrive  |
+            | e    | g  | Nebenstraße,Hauptstraßenbrücke,Anderestraße,Anderestraße | depart,turn left,end of road left,arrive   |
+            | d    | f  | Nebenstraße,Hauptstraßenbrücke,Anderestraße,Anderestraße | depart,turn right,end of road right,arrive |
+            | d    | g  | Nebenstraße,Hauptstraßenbrücke,Anderestraße,Anderestraße | depart,turn right,end of road left,arrive  |
+
+    Scenario: Simple Tunnel
+        Given the node map
+            | a | b | c | d |
+
+        And the ways
+            | nodes | highway | tunnel | name               |
+            | ab    | primary |        | Hauptstraße        |
+            | bc    | primary | yes    | Hauptstraßentunnel |
+            | cd    | primary |        | Hauptstraße        |
+
+        When I route I should get
+            | from | to | route                   | turns         |
+            | a    | d  | Hauptstraße,Hauptstraße | depart,arrive |
+
+    Scenario: Tunnel with Immediate Turn
+        Given the node map
+            |   |   |   | d |
+            | a |   | b | c |
+            |   |   |   | e |
+
+        And the ways
+            | nodes | highway | tunnel | name               |
+            | ab    | primary |        | Hauptstraße        |
+            | bc    | primary | yes    | Hauptstraßentunnel |
+            | dce   | primary |        | Nebenstraße        |
+
+        When I route I should get
+            | from | to | route                                      | turns                           |
+            | a    | d  | Hauptstraße,Nebenstraße,Nebenstraße        | depart,end of road left,arrive  |
+            | a    | e  | Hauptstraße,Nebenstraße,Nebenstraße        | depart,end of road right,arrive |
+            | e    | a  | Nebenstraße,Hauptstraßentunnel,Hauptstraße | depart,turn left,arrive         |
+            | d    | a  | Nebenstraße,Hauptstraßentunnel,Hauptstraße | depart,turn right,arrive        |
+
+    Scenario: Tunnel with Immediate Turn Front and Back
+        Given the node map
+            | f |   |   | d |
+            | a |   | b | c |
+            | g |   |   | e |
+
+        And the ways
+            | nodes | highway | bridge | name               |
+            | ab    | primary |        | Hauptstraße        |
+            | bc    | primary | yes    | Hauptstraßentunnel |
+            | dce   | primary |        | Nebenstraße        |
+            | gaf   | primary |        | Anderestraße       |
+
+        When I route I should get
+            | from | to | route                                                    | turns                                      |
+            | f    | d  | Anderestraße,Hauptstraße,Nebenstraße,Nebenstraße         | depart,turn left,end of road left,arrive   |
+            | f    | e  | Anderestraße,Hauptstraße,Nebenstraße,Nebenstraße         | depart,turn left,end of road right,arrive  |
+            | g    | d  | Anderestraße,Hauptstraße,Nebenstraße,Nebenstraße         | depart,turn right,end of road left,arrive  |
+            | g    | e  | Anderestraße,Hauptstraße,Nebenstraße,Nebenstraße         | depart,turn right,end of road right,arrive |
+            | e    | f  | Nebenstraße,Hauptstraßentunnel,Anderestraße,Anderestraße | depart,turn left,end of road right,arrive  |
+            | e    | g  | Nebenstraße,Hauptstraßentunnel,Anderestraße,Anderestraße | depart,turn left,end of road left,arrive   |
+            | d    | f  | Nebenstraße,Hauptstraßentunnel,Anderestraße,Anderestraße | depart,turn right,end of road right,arrive |
+            | d    | g  | Nebenstraße,Hauptstraßentunnel,Anderestraße,Anderestraße | depart,turn right,end of road left,arrive  |
+
diff --git a/features/guidance/collapse.feature b/features/guidance/collapse.feature
new file mode 100644
index 0000000..9cde9eb
--- /dev/null
+++ b/features/guidance/collapse.feature
@@ -0,0 +1,347 @@
+ at routing  @guidance @collapsing
+Feature: Collapse
+
+    Background:
+        Given the profile "car"
+        Given a grid size of 20 meters
+
+    Scenario: Segregated Intersection, Cross Belonging to Single Street
+        Given the node map
+            |   |   | i | l |   |   |
+            |   |   |   |   |   |   |
+            | d |   | c | b |   | a |
+            | e |   | f | g |   | h |
+            |   |   |   |   |   |   |
+            |   |   | j | k |   |   |
+
+        And the ways
+            | nodes | highway | name   | oneway |
+            | ab    | primary | first  | yes    |
+            | bc    | primary | first  | yes    |
+            | cd    | primary | first  | yes    |
+            | ef    | primary | first  | yes    |
+            | fg    | primary | first  | yes    |
+            | gh    | primary | first  | yes    |
+            | ic    | primary | second | yes    |
+            | bl    | primary | second | yes    |
+            | kg    | primary | second | yes    |
+            | fj    | primary | second | yes    |
+            | cf    | primary | first  | yes    |
+            | gb    | primary | first  | yes    |
+
+       When I route I should get
+            | waypoints | route                | turns                        |
+            | a,l       | first,second,second  | depart,turn right,arrive     |
+            | a,d       | first,first          | depart,arrive                |
+            | a,j       | first,second,second  | depart,turn left,arrive      |
+            | a,h       | first,first,first    | depart,continue uturn,arrive |
+            | e,j       | first,second,second  | depart,turn right,arrive     |
+            | e,h       | first,first          | depart,arrive                |
+            | e,l       | first,second,second  | depart,turn left,arrive      |
+            | e,d       | first,first,first    | depart,continue uturn,arrive |
+            | k,h       | second,first,first   | depart,turn right,arrive     |
+            | k,l       | second,second        | depart,arrive                |
+            | k,d       | second,first,first   | depart,turn left,arrive      |
+            | k,j       | second,second,second | depart,continue uturn,arrive |
+            | i,d       | second,first,first   | depart,turn right,arrive     |
+            | i,j       | second,second        | depart,arrive                |
+            | i,h       | second,first,first   | depart,turn left,arrive      |
+            | i,l       | second,second,second | depart,continue uturn,arrive |
+
+    Scenario: Segregated Intersection, Cross Belonging to Correct Street
+        Given the node map
+            |   |   | i | l |   |   |
+            |   |   |   |   |   |   |
+            | d |   | c | b |   | a |
+            | e |   | f | g |   | h |
+            |   |   |   |   |   |   |
+            |   |   | j | k |   |   |
+
+        And the ways
+            | nodes | highway | name   | oneway |
+            | ab    | primary | first  | yes    |
+            | bc    | primary | first  | yes    |
+            | cd    | primary | first  | yes    |
+            | ef    | primary | first  | yes    |
+            | fg    | primary | first  | yes    |
+            | gh    | primary | first  | yes    |
+            | ic    | primary | second | yes    |
+            | bl    | primary | second | yes    |
+            | kg    | primary | second | yes    |
+            | fj    | primary | second | yes    |
+            | cf    | primary | second | yes    |
+            | gb    | primary | second | yes    |
+
+       When I route I should get
+            | waypoints | route                | turns                        |
+            | a,l       | first,second,second  | depart,turn right,arrive     |
+            | a,d       | first,first          | depart,arrive                |
+            | a,j       | first,second,second  | depart,turn left,arrive      |
+            | a,h       | first,first,first    | depart,continue uturn,arrive |
+            | e,j       | first,second,second  | depart,turn right,arrive     |
+            | e,h       | first,first          | depart,arrive                |
+            | e,l       | first,second,second  | depart,turn left,arrive      |
+            | e,d       | first,first,first    | depart,continue uturn,arrive |
+            | k,h       | second,first,first   | depart,turn right,arrive     |
+            | k,l       | second,second        | depart,arrive                |
+            | k,d       | second,first,first   | depart,turn left,arrive      |
+            | k,j       | second,second,second | depart,continue uturn,arrive |
+            | i,d       | second,first,first   | depart,turn right,arrive     |
+            | i,j       | second,second        | depart,arrive                |
+            | i,h       | second,first,first   | depart,turn left,arrive      |
+            | i,l       | second,second,second | depart,continue uturn,arrive |
+
+    Scenario: Segregated Intersection, Cross Belonging to Mixed Streets
+        Given the node map
+            |   |   | i | l |   |   |
+            |   |   |   |   |   |   |
+            | d |   | c | b |   | a |
+            | e |   | f | g |   | h |
+            |   |   |   |   |   |   |
+            |   |   | j | k |   |   |
+
+        And the ways
+            | nodes | highway | name   | oneway |
+            | ab    | primary | first  | yes    |
+            | bc    | primary | second | yes    |
+            | cd    | primary | first  | yes    |
+            | ef    | primary | first  | yes    |
+            | fg    | primary | first  | yes    |
+            | gh    | primary | first  | yes    |
+            | ic    | primary | second | yes    |
+            | bl    | primary | second | yes    |
+            | kg    | primary | second | yes    |
+            | fj    | primary | second | yes    |
+            | cf    | primary | second | yes    |
+            | gb    | primary | first  | yes    |
+
+       When I route I should get
+            | waypoints | route                | turns                        |
+            | a,l       | first,second,second  | depart,turn right,arrive     |
+            | a,d       | first,first          | depart,arrive                |
+            | a,j       | first,second,second  | depart,turn left,arrive      |
+            | a,h       | first,first,first    | depart,continue uturn,arrive |
+            | e,j       | first,second,second  | depart,turn right,arrive     |
+            | e,h       | first,first          | depart,arrive                |
+            | e,l       | first,second,second  | depart,turn left,arrive      |
+            | e,d       | first,first,first    | depart,continue uturn,arrive |
+            | k,h       | second,first,first   | depart,turn right,arrive     |
+            | k,l       | second,second        | depart,arrive                |
+            | k,d       | second,first,first   | depart,turn left,arrive      |
+            | k,j       | second,second,second | depart,continue uturn,arrive |
+            | i,d       | second,first,first   | depart,turn right,arrive     |
+            | i,j       | second,second        | depart,arrive                |
+            | i,h       | second,first,first   | depart,turn left,arrive      |
+            | i,l       | second,second,second | depart,continue uturn,arrive |
+
+    Scenario: Partly Segregated Intersection, Two Segregated Roads
+        Given the node map
+            |   | g |   | h |   |
+            |   |   |   |   |   |
+            |   |   |   |   |   |
+            | c |   | b |   | a |
+            | d |   | e |   | f |
+            |   |   |   |   |   |
+            |   |   |   |   |   |
+            |   | j |   | i |   |
+
+        And the ways
+            | nodes | highway | name   | oneway |
+            | ab    | primary | first  | yes    |
+            | bc    | primary | first  | yes    |
+            | de    | primary | first  | yes    |
+            | ef    | primary | first  | yes    |
+            | be    | primary | first  | no     |
+            | gbh   | primary | second | yes    |
+            | iej   | primary | second | yes    |
+
+       When I route I should get
+            | waypoints | route                | turns                        |
+            | a,h       | first,second,second  | depart,turn right,arrive     |
+            | a,c       | first,first          | depart,arrive                |
+            | a,j       | first,second,second  | depart,turn left,arrive      |
+            | a,f       | first,first,first    | depart,continue uturn,arrive |
+            | d,j       | first,second,second  | depart,turn right,arrive     |
+            | d,f       | first,first          | depart,arrive                |
+            | d,h       | first,second,second  | depart,turn left,arrive      |
+            | d,c       | first,first,first    | depart,continue uturn,arrive |
+            | g,c       | second,first,first   | depart,turn right,arrive     |
+            | g,j       | second,second        | depart,arrive                |
+            | g,f       | second,first,first   | depart,turn left,arrive      |
+            | g,h       | second,second,second | depart,continue uturn,arrive |
+            | i,f       | second,first,first   | depart,turn right,arrive     |
+            | i,h       | second,second        | depart,arrive                |
+            | i,c       | second,first,first   | depart,turn left,arrive      |
+            | i,j       | second,second,second | depart,continue uturn,arrive |
+
+    Scenario: Partly Segregated Intersection, Two Segregated Roads, Intersection belongs to Second
+        Given the node map
+            |   | g |   | h |   |
+            |   |   |   |   |   |
+            |   |   |   |   |   |
+            | c |   | b |   | a |
+            | d |   | e |   | f |
+            |   |   |   |   |   |
+            |   |   |   |   |   |
+            |   | j |   | i |   |
+
+        And the ways
+            | nodes | highway | name   | oneway |
+            | ab    | primary | first  | yes    |
+            | bc    | primary | first  | yes    |
+            | de    | primary | first  | yes    |
+            | ef    | primary | first  | yes    |
+            | be    | primary | second | no     |
+            | gbh   | primary | second | yes    |
+            | iej   | primary | second | yes    |
+
+       When I route I should get
+            | waypoints | route                | turns                        |
+            | a,h       | first,second,second  | depart,turn right,arrive     |
+            | a,c       | first,first          | depart,arrive                |
+            | a,j       | first,second,second  | depart,turn left,arrive      |
+            | a,f       | first,first,first    | depart,continue uturn,arrive |
+            | d,j       | first,second,second  | depart,turn right,arrive     |
+            | d,f       | first,first          | depart,arrive                |
+            | d,h       | first,second,second  | depart,turn left,arrive      |
+            | d,c       | first,first,first    | depart,continue uturn,arrive |
+            | g,c       | second,first,first   | depart,turn right,arrive     |
+            | g,j       | second,second        | depart,arrive                |
+            | g,f       | second,first,first   | depart,turn left,arrive      |
+            | g,h       | second,second,second | depart,continue uturn,arrive |
+            | i,f       | second,first,first   | depart,turn right,arrive     |
+            | i,h       | second,second        | depart,arrive                |
+            | i,c       | second,first,first   | depart,turn left,arrive      |
+            | i,j       | second,second,second | depart,continue uturn,arrive |
+
+    Scenario: Segregated Intersection, Cross Belonging to Mixed Streets - Slight Angles
+        Given the node map
+            |   |   | i | l |   |   |
+            |   |   |   |   |   | a |
+            |   |   | c | b |   | h |
+            | d |   | f | g |   |   |
+            | e |   |   |   |   |   |
+            |   |   | j | k |   |   |
+
+        And the ways
+            | nodes | highway | name   | oneway |
+            | ab    | primary | first  | yes    |
+            | bc    | primary | second | yes    |
+            | cd    | primary | first  | yes    |
+            | ef    | primary | first  | yes    |
+            | fg    | primary | first  | yes    |
+            | gh    | primary | first  | yes    |
+            | ic    | primary | second | yes    |
+            | bl    | primary | second | yes    |
+            | kg    | primary | second | yes    |
+            | fj    | primary | second | yes    |
+            | cf    | primary | second | yes    |
+            | gb    | primary | first  | yes    |
+
+       When I route I should get
+            | waypoints | route                | turns                        |
+            | a,l       | first,second,second  | depart,turn right,arrive     |
+            | a,d       | first,first          | depart,arrive                |
+            | a,j       | first,second,second  | depart,turn left,arrive      |
+            | a,h       | first,first,first    | depart,continue uturn,arrive |
+            | e,j       | first,second,second  | depart,turn right,arrive     |
+            | e,h       | first,first          | depart,arrive                |
+            | e,l       | first,second,second  | depart,turn left,arrive      |
+            | e,d       | first,first,first    | depart,continue uturn,arrive |
+            | k,h       | second,first,first   | depart,turn right,arrive     |
+            | k,l       | second,second        | depart,arrive                |
+            | k,d       | second,first,first   | depart,turn left,arrive      |
+            | k,j       | second,second,second | depart,continue uturn,arrive |
+            | i,d       | second,first,first   | depart,turn right,arrive     |
+            | i,j       | second,second        | depart,arrive                |
+            | i,h       | second,first,first   | depart,turn left,arrive      |
+            | i,l       | second,second,second | depart,continue uturn,arrive |
+
+    Scenario: Segregated Intersection, Cross Belonging to Mixed Streets - Slight Angles (2)
+        Given the node map
+            |   |   | i | l |   |   |
+            |   |   |   |   |   |   |
+            |   |   | c | b |   |   |
+            | d |   | f | g |   | a |
+            | e |   |   |   |   | h |
+            |   |   | j | k |   |   |
+
+        And the ways
+            | nodes | highway | name   | oneway |
+            | ab    | primary | first  | yes    |
+            | bc    | primary | second | yes    |
+            | cd    | primary | first  | yes    |
+            | ef    | primary | first  | yes    |
+            | fg    | primary | first  | yes    |
+            | gh    | primary | first  | yes    |
+            | ic    | primary | second | yes    |
+            | bl    | primary | second | yes    |
+            | kg    | primary | second | yes    |
+            | fj    | primary | second | yes    |
+            | cf    | primary | second | yes    |
+            | gb    | primary | first  | yes    |
+
+       When I route I should get
+            | waypoints | route                | turns                        |
+            | a,l       | first,second,second  | depart,turn right,arrive     |
+            | a,d       | first,first          | depart,arrive                |
+            | a,j       | first,second,second  | depart,turn left,arrive      |
+            | a,h       | first,first,first    | depart,continue uturn,arrive |
+            | e,j       | first,second,second  | depart,turn right,arrive     |
+            | e,h       | first,first          | depart,arrive                |
+            | e,l       | first,second,second  | depart,turn left,arrive      |
+            | e,d       | first,first,first    | depart,continue uturn,arrive |
+            | k,h       | second,first,first   | depart,turn right,arrive     |
+            | k,l       | second,second        | depart,arrive                |
+            | k,d       | second,first,first   | depart,turn left,arrive      |
+            | k,j       | second,second,second | depart,continue uturn,arrive |
+            | i,d       | second,first,first   | depart,turn right,arrive     |
+            | i,j       | second,second        | depart,arrive                |
+            | i,h       | second,first,first   | depart,turn left,arrive      |
+            | i,l       | second,second,second | depart,continue uturn,arrive |
+
+    Scenario: Entering a segregated road
+        Given the node map
+            |   | a | f |   |   |
+            |   |   |   |   | g |
+            |   | b | e |   |   |
+            |   |   |   |   |   |
+            |   |   |   |   |   |
+            | c | d |   |   |   |
+
+        And the ways
+            | nodes | highway | name   | oneway |
+            | abc   | primary | first  | yes    |
+            | def   | primary | first  | yes    |
+            | be    | primary | first  | no     |
+            | ge    | primary | second | no     |
+
+        When I route I should get
+            | waypoints | route               | turns                          |
+            | d,c       | first,first,first   | depart,continue uturn,arrive   |
+            | a,f       | first,first,first   | depart,continue uturn,arrive   |
+            | a,g       | first,second,second | depart,turn left,arrive        |
+            | d,g       | first,second,second | depart,turn right,arrive       |
+            | g,f       | second,first,first  | depart,turn right,arrive       |
+            | g,c       | second,first,first  | depart,end of road left,arrive |
+
+
+    Scenario: Do not collapse turning roads
+        Given the node map
+            |   |   | e |   |   |
+            |   |   | c |   | d |
+            | a |   | b | f |   |
+
+        And the ways
+            | nodes | highway | name   |
+            | ab    | primary | first  |
+            | bc    | primary | first  |
+            | cd    | primary | first  |
+            | ce    | primary | second |
+            | bf    | primary | third  |
+
+        When I route I should get
+            | 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            |
diff --git a/features/guidance/destination-signs.feature b/features/guidance/destination-signs.feature
new file mode 100644
index 0000000..e2bbaea
--- /dev/null
+++ b/features/guidance/destination-signs.feature
@@ -0,0 +1,41 @@
+ at routing @guidance
+Feature: Destination Signs
+
+    Background:
+        Given the profile "car"
+
+    Scenario: Car - route name assembly with destination signs
+        Given the node map
+          | a | b |
+          | c | d |
+          | e | f |
+          | g | h |
+          | i | j |
+          | k | l |
+          | m | n |
+          | o | p |
+          | q | r |
+
+        And the ways
+          | nodes | name | ref | destination    | destination:ref | oneway | #                                    |
+          | ab    | AB   | E1  |                |                 | yes    |                                      |
+          | cd    | CD   |     | Berlin         |                 | yes    |                                      |
+          | ef    | EF   |     | Berlin         | A1              | yes    |                                      |
+          | gh    |      |     | Berlin         | A1              | yes    |                                      |
+          | ij    |      |     | Berlin         |                 | yes    |                                      |
+          | kl    | KL   | E1  | Berlin         | A1              | yes    |                                      |
+          | mn    | MN   |     | Berlin;Hamburg | A1;A2           | yes    |                                      |
+          | op    | OP   |     | Berlin;Hamburg | A1;A2           | no     | mis-tagged destination: not a oneway |
+          | 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)                                   |                           |
diff --git a/features/guidance/end-of-road.feature b/features/guidance/end-of-road.feature
index 323f5df..b0bfac3 100644
--- a/features/guidance/end-of-road.feature
+++ b/features/guidance/end-of-road.feature
@@ -117,7 +117,7 @@ Feature: End Of Road Instructions
             | bd     | motorway_link |
 
        When I route I should get
-            | waypoints | route    | turns                    |
-            | a,c       | ab,bc,bc | depart,ramp left,arrive  |
-            | a,d       | ab,bd,bd | depart,ramp right,arrive |
+            | waypoints | route    | turns                       |
+            | a,c       | ab,bc,bc | depart,on ramp left,arrive  |
+            | a,d       | ab,bd,bd | depart,on ramp right,arrive |
 
diff --git a/features/guidance/fork.feature b/features/guidance/fork.feature
index 916799e..f5c8087 100644
--- a/features/guidance/fork.feature
+++ b/features/guidance/fork.feature
@@ -245,7 +245,6 @@ Feature: Fork Instructions
             | a,c       | abd,bc,bc | depart,turn slight left,arrive |
             | a,d       | abd,abd   | depart,arrive                  |
 
-    @pr2275 @bug
     Scenario: Tripple fork
         Given the node map
             |   |   |   |   |   |   |   |   | c |
diff --git a/features/guidance/motorway.feature b/features/guidance/motorway.feature
index 5e9b312..4e79233 100644
--- a/features/guidance/motorway.feature
+++ b/features/guidance/motorway.feature
@@ -16,9 +16,9 @@ Feature: Motorway Guidance
             | bfg    | motorway_link |
 
        When I route I should get
-            | waypoints | route         | turns                           |
-            | a,e       | abcde,abcde   | depart,arrive                   |
-            | a,g       | abcde,bfg,bfg | depart,ramp slight right,arrive |
+            | waypoints | route         | turns                                |
+            | a,e       | abcde,abcde   | depart,arrive                        |
+            | a,g       | abcde,bfg,bfg | depart,off ramp slight right,arrive |
 
     Scenario: Ramp Exit Right Curved Right
         Given the node map
@@ -32,9 +32,9 @@ Feature: Motorway Guidance
             | bfg    | motorway_link |
 
        When I route I should get
-            | waypoints | route         | turns                    |
-            | a,e       | abcde,abcde   | depart,arrive            |
-            | a,g       | abcde,bfg,bfg | depart,ramp right,arrive |
+            | waypoints | route         | turns                         |
+            | a,e       | abcde,abcde   | depart,arrive                 |
+            | a,g       | abcde,bfg,bfg | depart,off ramp right,arrive |
 
     Scenario: Ramp Exit Right Curved Left
         Given the node map
@@ -49,9 +49,9 @@ Feature: Motorway Guidance
             | cfg    | motorway_link |
 
        When I route I should get
-            | waypoints | route         | turns                           |
-            | a,e       | abcde,abcde   | depart,arrive                   |
-            | a,g       | abcde,cfg,cfg | depart,ramp slight right,arrive |
+            | waypoints | route         | turns                                |
+            | a,e       | abcde,abcde   | depart,arrive                        |
+            | a,g       | abcde,cfg,cfg | depart,off ramp slight right,arrive |
 
 
     Scenario: Ramp Exit Left
@@ -65,9 +65,9 @@ Feature: Motorway Guidance
             | bfg    | motorway_link |
 
        When I route I should get
-            | waypoints | route         | turns                          |
-            | a,e       | abcde,abcde   | depart,arrive                  |
-            | a,g       | abcde,bfg,bfg | depart,ramp slight left,arrive |
+            | waypoints | route         | turns                               |
+            | a,e       | abcde,abcde   | depart,arrive                       |
+            | a,g       | abcde,bfg,bfg | depart,off ramp slight left,arrive |
 
     Scenario: Ramp Exit Left Curved Left
         Given the node map
@@ -81,9 +81,9 @@ Feature: Motorway Guidance
             | bfg    | motorway_link |
 
        When I route I should get
-            | waypoints | route         | turns                   |
-            | a,e       | abcde,abcde   | depart,arrive           |
-            | a,g       | abcde,bfg,bfg | depart,ramp left,arrive |
+            | waypoints | route         | turns                        |
+            | a,e       | abcde,abcde   | depart,arrive                |
+            | a,g       | abcde,bfg,bfg | depart,off ramp left,arrive |
 
     Scenario: Ramp Exit Left Curved Right
         Given the node map
@@ -97,9 +97,9 @@ Feature: Motorway Guidance
             | cfg    | motorway_link |
 
        When I route I should get
-            | waypoints | route         | turns                          |
-            | a,e       | abcde,abcde   | depart,arrive                  |
-            | a,g       | abcde,cfg,cfg | depart,ramp slight left,arrive |
+            | waypoints | route         | turns                               |
+            | a,e       | abcde,abcde   | depart,arrive                       |
+            | a,g       | abcde,cfg,cfg | depart,off ramp slight left,arrive |
 
     Scenario: On Ramp Right
         Given the node map
@@ -176,11 +176,11 @@ Feature: Motorway Guidance
             | chi    | motorway_link |
 
        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 |
-            | a,i       | abcde,chi,chi   | depart,ramp slight right,arrive |
-            | f,i       | fgc,chi,chi     | depart,ramp right,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        |
 
     Scenario: On And Off Ramp Left
        Given the node map
@@ -194,11 +194,11 @@ Feature: Motorway Guidance
             | chi    | motorway_link |
 
        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 |
-            | a,i       | abcde,chi,chi   | depart,ramp slight left,arrive   |
-            | f,i       | fgc,chi,chi     | depart,ramp left,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        |
 
     Scenario: Merging Motorways
         Given the node map
diff --git a/features/guidance/new-name.feature b/features/guidance/new-name.feature
index 1fdf1ce..984ae7b 100644
--- a/features/guidance/new-name.feature
+++ b/features/guidance/new-name.feature
@@ -133,3 +133,34 @@ Feature: New-Name Instructions
        When I route I should get
             | waypoints | route    | turns                               |
             | a,c       | ab,bc,bc | depart,new name slight right,arrive |
+
+    Scenario: Empty road names - Announce Change From, suppress Change To
+        Given the node map
+            | a |  | b |  | c |  | d |
+
+        And the ways
+            | nodes | name |
+            | ab    | ab   |
+            | bc    |      |
+            | cd    | cd   |
+
+        When I route I should get
+            | waypoints | route    | turns                           |
+            | a,d       | ab,cd,cd | depart,new name straight,arrive |
+            | a,c       | ab,      | depart,arrive                   |
+
+    Scenario: Empty road names - Loose name shortly
+        Given the node map
+            | a |  | b |  | c |  | d |  | e |
+
+        And the ways
+            | nodes | name      |
+            | ab    | name      |
+            | bc    | with-name |
+            | cd    |           |
+            | de    | with-name |
+
+        When I route I should get
+            | waypoints | route                    | turns                           |
+            | a,e       | name,with-name,with-name | depart,new name straight,arrive |
+            | b,e       | with-name,with-name      | depart,arrive                   |
diff --git a/features/guidance/ramp.feature b/features/guidance/ramp.feature
index 3c3ce39..2c40d24 100644
--- a/features/guidance/ramp.feature
+++ b/features/guidance/ramp.feature
@@ -16,8 +16,8 @@ Feature: Ramp Guidance
             | bd    | motorway_link |
 
        When I route I should get
-            | waypoints | route     | turns                    |
-            | a,d       | abc,bd,bd | depart,ramp right,arrive |
+            | waypoints | route     | turns                          |
+            | a,d       | abc,bd,bd | depart,on ramp right,arrive |
 
     Scenario: Ramp On Through Street Left
         Given the node map
@@ -30,8 +30,8 @@ Feature: Ramp Guidance
             | bd    | motorway_link |
 
        When I route I should get
-            | waypoints | route     | turns                   |
-            | a,d       | abc,bd,bd | depart,ramp left,arrive |
+            | waypoints | route     | turns                         |
+            | a,d       | abc,bd,bd | depart,on ramp left,arrive |
 
     Scenario: Ramp On Through Street Left and Right
         Given the node map
@@ -46,9 +46,9 @@ Feature: Ramp Guidance
             | bd    | motorway_link |
 
        When I route I should get
-            | waypoints | route     | turns                    |
-            | a,d       | abc,bd,bd | depart,ramp right,arrive |
-            | a,e       | abc,be,be | depart,ramp left,arrive  |
+            | waypoints | route     | turns                          |
+            | a,d       | abc,bd,bd | depart,on ramp right,arrive |
+            | a,e       | abc,be,be | depart,on ramp left,arrive  |
 
     Scenario: Ramp On Three Way Intersection Right
         Given the node map
@@ -62,8 +62,8 @@ Feature: Ramp Guidance
             | bd    | motorway_link |
 
        When I route I should get
-            | waypoints | route    | turns                    |
-            | a,d       | ab,bd,bd | depart,ramp right,arrive |
+            | waypoints | route    | turns                          |
+            | a,d       | ab,bd,bd | depart,on ramp right,arrive |
 
     Scenario: Ramp On Three Way Intersection Right
         Given the node map
@@ -78,8 +78,8 @@ Feature: Ramp Guidance
             | bd    | motorway_link |
 
        When I route I should get
-            | waypoints | route    | turns                    |
-            | a,d       | ab,bd,bd | depart,ramp right,arrive |
+            | waypoints | route    | turns                          |
+            | a,d       | ab,bd,bd | depart,on ramp right,arrive |
 
     Scenario: Ramp Off Though Street
         Given the node map
@@ -93,9 +93,9 @@ Feature: Ramp Guidance
             | bd    | motorway_link |
 
        When I route I should get
-            | waypoints | route     | turns                    |
-            | a,d       | abc,bd,bd | depart,ramp right,arrive |
-            | a,c       | abc,abc   | depart,arrive            |
+            | waypoints | route     | turns                          |
+            | a,d       | abc,bd,bd | depart,on ramp right,arrive |
+            | a,c       | abc,abc   | depart,arrive                  |
 
     Scenario: Straight Ramp Off Turning Though Street
         Given the node map
@@ -108,9 +108,9 @@ Feature: Ramp Guidance
             | bd    | motorway_link |
 
        When I route I should get
-            | waypoints | route       | turns                       |
-            | a,d       | abc,bd,bd   | depart,ramp straight,arrive |
-            | a,c       | abc,abc,abc | depart,continue left,arrive |
+            | waypoints | route       | turns                             |
+            | a,d       | abc,bd,bd   | depart,on ramp straight,arrive |
+            | a,c       | abc,abc,abc | depart,continue left,arrive       |
 
     Scenario: Fork Ramp Off Turning Though Street
         Given the node map
@@ -124,9 +124,9 @@ Feature: Ramp Guidance
             | bd    | motorway_link |
 
        When I route I should get
-            | waypoints | route       | turns                       |
-            | a,d       | abc,bd,bd   | depart,ramp right,arrive    |
-            | a,c       | abc,abc,abc | depart,continue left,arrive |
+            | waypoints | route       | turns                             |
+            | a,d       | abc,bd,bd   | depart,on ramp right,arrive    |
+            | a,c       | abc,abc,abc | depart,continue left,arrive       |
 
     Scenario: Fork Ramp
         Given the node map
@@ -141,9 +141,9 @@ Feature: Ramp Guidance
             | bd    | motorway_link |
 
        When I route I should get
-            | waypoints | route    | turns                    |
-            | a,d       | ab,bd,bd | depart,ramp right,arrive |
-            | a,c       | ab,bc,bc | depart,turn left,arrive  |
+            | waypoints | route    | turns                          |
+            | a,d       | ab,bd,bd | depart,on ramp right,arrive |
+            | a,c       | ab,bc,bc | depart,turn left,arrive        |
 
     Scenario: Fork Slight Ramp
         Given the node map
@@ -158,9 +158,9 @@ Feature: Ramp Guidance
             | bd    | motorway_link |
 
        When I route I should get
-            | waypoints | route    | turns                           |
-            | a,d       | ab,bd,bd | depart,ramp slight right,arrive |
-            | a,c       | ab,bc,bc | depart,turn slight left,arrive  |
+            | waypoints | route    | turns                                 |
+            | a,d       | ab,bd,bd | depart,on ramp slight right,arrive |
+            | a,c       | ab,bc,bc | depart,turn slight left,arrive        |
 
     Scenario: Fork Slight Ramp on Through Street
         Given the node map
@@ -174,9 +174,9 @@ Feature: Ramp Guidance
             | bd    | motorway_link |
 
        When I route I should get
-            | waypoints | route       | turns                              |
-            | a,d       | abc,bd,bd   | depart,ramp slight right,arrive    |
-            | a,c       | abc,abc,abc | depart,continue slight left,arrive |
+            | waypoints | route       | turns                                    |
+            | a,d       | abc,bd,bd   | depart,on ramp slight right,arrive    |
+            | a,c       | abc,abc,abc | depart,continue slight left,arrive       |
 
     Scenario: Fork Slight Ramp on Obvious Through Street
         Given the node map
@@ -190,9 +190,9 @@ Feature: Ramp Guidance
             | bd    | motorway_link |
 
        When I route I should get
-            | waypoints | route     | turns                           |
-            | a,d       | abc,bd,bd | depart,ramp slight right,arrive |
-            | a,c       | abc,abc   | depart,arrive                   |
+            | waypoints | route     | turns                                 |
+            | a,d       | abc,bd,bd | depart,on ramp slight right,arrive |
+            | a,c       | abc,abc   | depart,arrive                         |
 
     Scenario: Two Ramps Joining into common Motorway
         Given the node map
diff --git a/features/guidance/rotary.feature b/features/guidance/rotary.feature
index e1b14d3..1788476 100644
--- a/features/guidance/rotary.feature
+++ b/features/guidance/rotary.feature
@@ -194,15 +194,15 @@ Feature: Rotary
 
         And the ways
             | nodes | junction   |
-            | ab    |            |
+            | ad    |            |
             | bcdb  | roundabout |
-            | ce    |            |
-            | df    |            |
+            | be    |            |
+            | cf    |            |
 
         When I route I should get
             | waypoints | route    | turns                     |
-            | a,e       | ab,ce,ce | depart,bcdb-exit-1,arrive |
-            | a,f       | ab,df,df | depart,bcdb-exit-2,arrive |
+            | a,e       | ad,be,be | depart,bcdb-exit-1,arrive |
+            | a,f       | ad,cf,cf | depart,bcdb-exit-2,arrive |
 
        Scenario: Collinear in X,Y
         Given the node map
@@ -213,15 +213,15 @@ Feature: Rotary
 
         And the ways
             | nodes | junction   |
-            | ab    |            |
+            | ac    |            |
             | bcdb  | roundabout |
-            | ce    |            |
-            | df    |            |
+            | de    |            |
+            | bf    |            |
 
         When I route I should get
             | waypoints | route    | turns                     |
-            | a,e       | ab,ce,ce | depart,bcdb-exit-1,arrive |
-            | a,f       | ab,df,df | depart,bcdb-exit-2,arrive |
+            | a,e       | ac,de,de | depart,bcdb-exit-1,arrive |
+            | a,f       | ac,bf,bf | depart,bcdb-exit-2,arrive |
 
        Scenario: Collinear in X,Y
         Given the node map
diff --git a/features/guidance/roundabout.feature b/features/guidance/roundabout-turn.feature
similarity index 57%
copy from features/guidance/roundabout.feature
copy to features/guidance/roundabout-turn.feature
index d03737c..5a31045 100644
--- a/features/guidance/roundabout.feature
+++ b/features/guidance/roundabout-turn.feature
@@ -3,7 +3,7 @@ Feature: Basic Roundabout
 
     Background:
         Given the profile "car"
-        Given a grid size of 10 meters
+        Given a grid size of 3 meters
 
     Scenario: Enter and Exit
         Given the node map
@@ -22,19 +22,49 @@ Feature: Basic Roundabout
             | bgecb  | roundabout |
 
        When I route I should get
-           | waypoints | route    | turns                           |
-           | a,d       | ab,cd,cd | depart,roundabout-exit-3,arrive |
-           | a,f       | ab,ef,ef | depart,roundabout-exit-2,arrive |
-           | a,h       | ab,gh,gh | depart,roundabout-exit-1,arrive |
-           | d,f       | cd,ef,ef | depart,roundabout-exit-3,arrive |
-           | d,h       | cd,gh,gh | depart,roundabout-exit-2,arrive |
-           | d,a       | cd,ab,ab | depart,roundabout-exit-1,arrive |
-           | f,h       | ef,gh,gh | depart,roundabout-exit-3,arrive |
-           | f,a       | ef,ab,ab | depart,roundabout-exit-2,arrive |
-           | f,d       | ef,cd,cd | depart,roundabout-exit-1,arrive |
-           | h,a       | gh,ab,ab | depart,roundabout-exit-3,arrive |
-           | h,d       | gh,cd,cd | depart,roundabout-exit-2,arrive |
-           | h,f       | gh,ef,ef | depart,roundabout-exit-1,arrive |
+           | waypoints | route    | turns                                         |
+           | a,d       | ab,cd,cd | depart,roundabout turn left exit-3,arrive     |
+           | a,f       | ab,ef,ef | depart,roundabout turn straight exit-2,arrive |
+           | a,h       | ab,gh,gh | depart,roundabout turn right exit-1,arrive    |
+           | d,f       | cd,ef,ef | depart,roundabout turn left exit-3,arrive     |
+           | d,h       | cd,gh,gh | depart,roundabout turn straight exit-2,arrive |
+           | d,a       | cd,ab,ab | depart,roundabout turn right exit-1,arrive    |
+           | f,h       | ef,gh,gh | depart,roundabout turn left exit-3,arrive     |
+           | f,a       | ef,ab,ab | depart,roundabout turn straight exit-2,arrive |
+           | f,d       | ef,cd,cd | depart,roundabout turn right exit-1,arrive    |
+           | h,a       | gh,ab,ab | depart,roundabout turn left exit-3,arrive     |
+           | h,d       | gh,cd,cd | depart,roundabout turn straight exit-2,arrive |
+           | h,f       | gh,ef,ef | depart,roundabout turn right exit-1,arrive    |
+
+    Scenario: Enter and Exit - Rotated
+        Given the node map
+            | a |   |   | d |
+            |   | b | c |   |
+            |   | g | e |   |
+            | h |   |   | f |
+
+       And the ways
+            | nodes  | junction   |
+            | ab     |            |
+            | cd     |            |
+            | ef     |            |
+            | gh     |            |
+            | bgecb  | roundabout |
+
+       When I route I should get
+           | waypoints | route    | turns                                         |
+           | a,d       | ab,cd,cd | depart,roundabout turn left exit-3,arrive     |
+           | a,f       | ab,ef,ef | depart,roundabout turn straight exit-2,arrive |
+           | a,h       | ab,gh,gh | depart,roundabout turn right exit-1,arrive    |
+           | d,f       | cd,ef,ef | depart,roundabout turn left exit-3,arrive     |
+           | d,h       | cd,gh,gh | depart,roundabout turn straight exit-2,arrive |
+           | d,a       | cd,ab,ab | depart,roundabout turn right exit-1,arrive    |
+           | f,h       | ef,gh,gh | depart,roundabout turn left exit-3,arrive     |
+           | f,a       | ef,ab,ab | depart,roundabout turn straight exit-2,arrive |
+           | f,d       | ef,cd,cd | depart,roundabout turn right exit-1,arrive    |
+           | h,a       | gh,ab,ab | depart,roundabout turn left exit-3,arrive     |
+           | h,d       | gh,cd,cd | depart,roundabout turn straight exit-2,arrive |
+           | h,f       | gh,ef,ef | depart,roundabout turn right exit-1,arrive    |
 
     Scenario: Only Enter
         Given the node map
@@ -130,7 +160,7 @@ Feature: Basic Roundabout
            | g,c       | bcegb,bcegb | depart,arrive |
            | g,e       | bcegb,bcegb | depart,arrive |
 
-     Scenario: Mixed Entry and Exit
+     Scenario: Mixed Entry and Exit - Not an Intersection
         Given the node map
            |   | c |   | a |   |
            | j |   | b |   | f |
@@ -165,30 +195,45 @@ Feature: Basic Roundabout
            | j,f       | jkl,def,def | depart,roundabout-exit-3,arrive |
            | j,c       | jkl,abc,abc | depart,roundabout-exit-4,arrive |
 
-       Scenario: Collinear in X
+    Scenario: Segregated roads - Not an intersection
         Given the node map
-            | a | b | c | d | f |
-            |   |   | e |   |   |
+           |   | a |   | c |   |
+           | l |   | b |   | d |
+           |   | k |   | e |   |
+           | j |   | h |   | f |
+           |   | i |   | g |   |
 
         And the ways
-            | nodes | junction   |
-            | ab    |            |
-            | bcdb  | roundabout |
-            | ce    |            |
-            | df    |            |
+           | nodes | junction   | oneway |
+           | abc   |            | yes    |
+           | def   |            | yes    |
+           | ghi   |            | yes    |
+           | jkl   |            | yes    |
+           | bkheb | roundabout | yes    |
 
         When I route I should get
-            | waypoints | route    | turns                           |
-            | a,e       | ab,ce,ce | depart,roundabout-exit-1,arrive |
-            | a,f       | ab,df,df | depart,roundabout-exit-2,arrive |
+           | waypoints | route       | turns                           |
+           | a,c       | abc,abc,abc | depart,roundabout-exit-4,arrive |
+           | a,l       | abc,jkl,jkl | depart,roundabout-exit-1,arrive |
+           | a,i       | abc,ghi,ghi | depart,roundabout-exit-2,arrive |
+           | a,f       | abc,def,def | depart,roundabout-exit-3,arrive |
+           | d,f       | def,def,def | depart,roundabout-exit-4,arrive |
+           | d,c       | def,abc,abc | depart,roundabout-exit-1,arrive |
+           | d,l       | def,jkl,jkl | depart,roundabout-exit-2,arrive |
+           | d,i       | def,ghi,ghi | depart,roundabout-exit-3,arrive |
+           | g,i       | ghi,ghi,ghi | depart,roundabout-exit-4,arrive |
+           | g,f       | ghi,def,def | depart,roundabout-exit-1,arrive |
+           | g,c       | ghi,abc,abc | depart,roundabout-exit-2,arrive |
+           | g,l       | ghi,jkl,jkl | depart,roundabout-exit-3,arrive |
+           | j,l       | jkl,jkl,jkl | depart,roundabout-exit-4,arrive |
+           | j,i       | jkl,ghi,ghi | depart,roundabout-exit-1,arrive |
+           | j,f       | jkl,def,def | depart,roundabout-exit-2,arrive |
+           | j,c       | jkl,abc,abc | depart,roundabout-exit-3,arrive |
 
-       Scenario: Collinear in Y
+       Scenario: Collinear in X
         Given the node map
-            | a |   |
-            | b |   |
-            | c | e |
-            | d |   |
-            | f |   |
+            | a | b | c | d | f |
+            |   |   | e |   |   |
 
         And the ways
             | nodes | junction   |
@@ -217,9 +262,9 @@ Feature: Basic Roundabout
             | df    |            |
 
         When I route I should get
-            | waypoints | route    | turns                           |
-            | a,e       | ab,ce,ce | depart,roundabout-exit-1,arrive |
-            | a,f       | ab,df,df | depart,roundabout-exit-2,arrive |
+            | waypoints | route    | turns                                         |
+            | a,e       | ab,ce,ce | depart,roundabout turn straight exit-1,arrive |
+            | a,f       | ab,df,df | depart,roundabout turn left exit-2,arrive     |
 
        Scenario: Collinear in X,Y
         Given the node map
@@ -230,15 +275,15 @@ Feature: Basic Roundabout
 
         And the ways
             | nodes | junction   |
-            | ab    |            |
+            | ad    |            |
             | bcdb  | roundabout |
-            | ce    |            |
-            | df    |            |
+            | be    |            |
+            | cf    |            |
 
         When I route I should get
-            | waypoints | route    | turns                           |
-            | a,e       | ab,ce,ce | depart,roundabout-exit-1,arrive |
-            | a,f       | ab,df,df | depart,roundabout-exit-2,arrive |
+            | waypoints | route    | turns                                         |
+            | a,e       | ad,be,be | depart,roundabout turn straight exit-1,arrive |
+            | a,f       | ad,cf,cf | depart,roundabout turn left exit-2,arrive     |
 
        Scenario: Collinear in X,Y
         Given the node map
@@ -249,13 +294,76 @@ Feature: Basic Roundabout
 
         And the ways
             | nodes | junction   |
-            | ab    |            |
+            | ac    |            |
             | bcdb  | roundabout |
-            | ce    |            |
-            | df    |            |
+            | de    |            |
+            | bf    |            |
 
         When I route I should get
-            | waypoints | route    | turns                           |
-            | a,e       | ab,ce,ce | depart,roundabout-exit-1,arrive |
-            | a,f       | ab,df,df | depart,roundabout-exit-2,arrive |
+            | waypoints | route    | turns                                         |
+            | a,e       | ac,de,de | depart,roundabout turn straight exit-1,arrive |
+            | a,f       | ac,bf,bf | depart,roundabout turn left exit-2,arrive     |
+
+    Scenario: Enter and Exit -- too complex
+        Given the node map
+            | j |   | a |   |   |
+            |   | i | b |   |   |
+            |   | g |   | c | d |
+            | h |   | e |   |   |
+            |   |   | f |   |   |
+
+       And the ways
+            | nodes  | junction   |
+            | ab     |            |
+            | ij     |            |
+            | cd     |            |
+            | ef     |            |
+            | gh     |            |
+            | bigecb | roundabout |
+
+       When I route I should get
+           | waypoints | route    | turns                           |
+           | a,d       | ab,cd,cd | depart,roundabout-exit-4,arrive |
+           | a,f       | ab,ef,ef | depart,roundabout-exit-3,arrive |
+           | a,h       | ab,gh,gh | depart,roundabout-exit-2,arrive |
+           | d,f       | cd,ef,ef | depart,roundabout-exit-4,arrive |
+           | d,h       | cd,gh,gh | depart,roundabout-exit-3,arrive |
+           | d,a       | cd,ab,ab | depart,roundabout-exit-1,arrive |
+           | f,h       | ef,gh,gh | depart,roundabout-exit-4,arrive |
+           | f,a       | ef,ab,ab | depart,roundabout-exit-2,arrive |
+           | f,d       | ef,cd,cd | depart,roundabout-exit-1,arrive |
+           | h,a       | gh,ab,ab | depart,roundabout-exit-3,arrive |
+           | h,d       | gh,cd,cd | depart,roundabout-exit-2,arrive |
+           | h,f       | gh,ef,ef | depart,roundabout-exit-1,arrive |
+
+    Scenario: Enter and Exit -- Non-Distinct
+        Given the node map
+           |   |   | a |   |   |
+           |   |   | b |   |   |
+           |   | g |   | c | d |
+           |   |   | e |   |   |
+           | h |   | f |   |   |
+
+       And the ways
+            | nodes | junction   |
+            | ab    |            |
+            | cd    |            |
+            | ef    |            |
+            | gh    |            |
+            | bgecb | roundabout |
+
+       When I route I should get
+           | waypoints | route    | turns                           |
+           | a,d       | ab,cd,cd | depart,roundabout-exit-3,arrive |
+           | a,f       | ab,ef,ef | depart,roundabout-exit-2,arrive |
+           | a,h       | ab,gh,gh | depart,roundabout-exit-1,arrive |
+           | d,f       | cd,ef,ef | depart,roundabout-exit-3,arrive |
+           | d,h       | cd,gh,gh | depart,roundabout-exit-2,arrive |
+           | d,a       | cd,ab,ab | depart,roundabout-exit-1,arrive |
+           | f,h       | ef,gh,gh | depart,roundabout-exit-3,arrive |
+           | f,a       | ef,ab,ab | depart,roundabout-exit-2,arrive |
+           | f,d       | ef,cd,cd | depart,roundabout-exit-1,arrive |
+           | h,a       | gh,ab,ab | depart,roundabout-exit-3,arrive |
+           | h,d       | gh,cd,cd | depart,roundabout-exit-2,arrive |
+           | h,f       | gh,ef,ef | depart,roundabout-exit-1,arrive |
 
diff --git a/features/guidance/roundabout.feature b/features/guidance/roundabout.feature
index d03737c..c9eecc3 100644
--- a/features/guidance/roundabout.feature
+++ b/features/guidance/roundabout.feature
@@ -165,6 +165,84 @@ Feature: Basic Roundabout
            | j,f       | jkl,def,def | depart,roundabout-exit-3,arrive |
            | j,c       | jkl,abc,abc | depart,roundabout-exit-4,arrive |
 
+    Scenario: Mixed Entry and Exit - segregated roads
+        Given the node map
+           |   |   | a |   | c |   |   |
+           |   |   |   |   |   |   |   |
+           | l |   |   | b |   |   | d |
+           |   |   | k |   | e |   |   |
+           | j |   |   | h |   |   | f |
+           |   |   |   |   |   |   |   |
+           |   |   | i |   | g |   |   |
+
+        And the ways
+           | nodes | junction   | oneway |
+           | abc   |            | yes    |
+           | def   |            | yes    |
+           | ghi   |            | yes    |
+           | jkl   |            | yes    |
+           | bkheb | roundabout | yes    |
+
+        When I route I should get
+           | waypoints | route       | turns                           |
+           | a,c       | abc,abc,abc | depart,roundabout-exit-4,arrive |
+           | a,l       | abc,jkl,jkl | depart,roundabout-exit-1,arrive |
+           | a,i       | abc,ghi,ghi | depart,roundabout-exit-2,arrive |
+           | a,f       | abc,def,def | depart,roundabout-exit-3,arrive |
+           | d,f       | def,def,def | depart,roundabout-exit-4,arrive |
+           | d,c       | def,abc,abc | depart,roundabout-exit-1,arrive |
+           | d,l       | def,jkl,jkl | depart,roundabout-exit-2,arrive |
+           | d,i       | def,ghi,ghi | depart,roundabout-exit-3,arrive |
+           | g,i       | ghi,ghi,ghi | depart,roundabout-exit-4,arrive |
+           | g,f       | ghi,def,def | depart,roundabout-exit-1,arrive |
+           | g,c       | ghi,abc,abc | depart,roundabout-exit-2,arrive |
+           | g,l       | ghi,jkl,jkl | depart,roundabout-exit-3,arrive |
+           | j,l       | jkl,jkl,jkl | depart,roundabout-exit-4,arrive |
+           | j,i       | jkl,ghi,ghi | depart,roundabout-exit-1,arrive |
+           | j,f       | jkl,def,def | depart,roundabout-exit-2,arrive |
+           | j,c       | jkl,abc,abc | depart,roundabout-exit-3,arrive |
+
+    Scenario: Mixed Entry and Exit - segregated roads, different names
+        Given the node map
+           |   |   | a |   | c |   |   |
+           |   |   |   |   |   |   |   |
+           | l |   |   | b |   |   | d |
+           |   |   | k |   | e |   |   |
+           | j |   |   | h |   |   | f |
+           |   |   |   |   |   |   |   |
+           |   |   | i |   | g |   |   |
+
+        And the ways
+           | nodes | junction   | oneway |
+           | ab    |            | yes    |
+           | bc    |            | yes    |
+           | de    |            | yes    |
+           | ef    |            | yes    |
+           | gh    |            | yes    |
+           | hi    |            | yes    |
+           | jk    |            | yes    |
+           | kl    |            | yes    |
+           | bkheb | roundabout | yes    |
+
+        When I route I should get
+           | waypoints | route    | turns                           |
+           | a,c       | ab,bc,bc | depart,roundabout-exit-4,arrive |
+           | a,l       | ab,kl,kl | depart,roundabout-exit-1,arrive |
+           | a,i       | ab,hi,hi | depart,roundabout-exit-2,arrive |
+           | a,f       | ab,ef,ef | depart,roundabout-exit-3,arrive |
+           | d,f       | de,ef,ef | depart,roundabout-exit-4,arrive |
+           | d,c       | de,bc,bc | depart,roundabout-exit-1,arrive |
+           | d,l       | de,kl,kl | depart,roundabout-exit-2,arrive |
+           | d,i       | de,hi,hi | depart,roundabout-exit-3,arrive |
+           | g,i       | gh,hi,hi | depart,roundabout-exit-4,arrive |
+           | g,f       | gh,ef,ef | depart,roundabout-exit-1,arrive |
+           | g,c       | gh,bc,bc | depart,roundabout-exit-2,arrive |
+           | g,l       | gh,kl,kl | depart,roundabout-exit-3,arrive |
+           | j,l       | jk,kl,kl | depart,roundabout-exit-4,arrive |
+           | j,i       | jk,hi,hi | depart,roundabout-exit-1,arrive |
+           | j,f       | jk,ef,ef | depart,roundabout-exit-2,arrive |
+           | j,c       | jk,bc,bc | depart,roundabout-exit-3,arrive |
+
        Scenario: Collinear in X
         Given the node map
             | a | b | c | d | f |
@@ -184,11 +262,11 @@ Feature: Basic Roundabout
 
        Scenario: Collinear in Y
         Given the node map
-            | a |   |
-            | b |   |
-            | c | e |
-            | d |   |
-            | f |   |
+            |   | a |
+            |   | b |
+            | e | c |
+            |   | d |
+            |   | f |
 
         And the ways
             | nodes | junction   |
@@ -230,15 +308,15 @@ Feature: Basic Roundabout
 
         And the ways
             | nodes | junction   |
-            | ab    |            |
+            | ad    |            |
             | bcdb  | roundabout |
-            | ce    |            |
-            | df    |            |
+            | be    |            |
+            | cf    |            |
 
         When I route I should get
             | waypoints | route    | turns                           |
-            | a,e       | ab,ce,ce | depart,roundabout-exit-1,arrive |
-            | a,f       | ab,df,df | depart,roundabout-exit-2,arrive |
+            | a,e       | ad,be,be | depart,roundabout-exit-1,arrive |
+            | a,f       | ad,cf,cf | depart,roundabout-exit-2,arrive |
 
        Scenario: Collinear in X,Y
         Given the node map
@@ -249,13 +327,13 @@ Feature: Basic Roundabout
 
         And the ways
             | nodes | junction   |
-            | ab    |            |
+            | ac    |            |
             | bcdb  | roundabout |
-            | ce    |            |
-            | df    |            |
+            | de    |            |
+            | bf    |            |
 
         When I route I should get
             | waypoints | route    | turns                           |
-            | a,e       | ab,ce,ce | depart,roundabout-exit-1,arrive |
-            | a,f       | ab,df,df | depart,roundabout-exit-2,arrive |
+            | a,e       | ac,de,de | depart,roundabout-exit-1,arrive |
+            | a,f       | ac,bf,bf | depart,roundabout-exit-2,arrive |
 
diff --git a/features/guidance/suffix-changes.feature b/features/guidance/suffix-changes.feature
new file mode 100644
index 0000000..9b53adb
--- /dev/null
+++ b/features/guidance/suffix-changes.feature
@@ -0,0 +1,101 @@
+ at routing  @guidance
+Feature: Suppress New Names on dedicated Suffices
+
+    Background:
+        Given the profile "car"
+        Given a grid size of 10 meters
+
+    Scenario: Suffix To Suffix
+        Given the node map
+            | a |   | b |   | c |
+
+        And the ways
+            | nodes  | name |
+            | ab     | 42 N |
+            | bc     | 42 S |
+
+       When I route I should get
+            | waypoints | route     | turns         |
+            | a,c       | 42 N,42 S | depart,arrive |
+
+    Scenario: Suffix To Suffix Ref
+        Given the node map
+            | a |   | b |   | c |
+
+        And the ways
+            | nodes  | name | ref |
+            | ab     | 42 N |     |
+            | bc     | 42 S | 101 |
+
+       When I route I should get
+            | waypoints | route           | turns         |
+            | a,c       | 42 N,42 S (101) | depart,arrive |
+
+    Scenario: Prefix Change
+        Given the node map
+            | a |   | b |   | c |
+
+        And the ways
+            | nodes  | name    |
+            | ab     | West 42 |
+            | bc     | East 42 |
+
+       When I route I should get
+            | waypoints | route           | turns         |
+            | a,c       | West 42,East 42 | depart,arrive |
+
+    Scenario: Prefix Change and Reference
+        Given the node map
+            | a |   | b |   | c |
+
+        And the ways
+            | nodes  | name    | ref |
+            | ab     | West 42 | 101 |
+            | bc     | East 42 |     |
+
+       When I route I should get
+            | waypoints | route                 | turns         |
+            | a,c       | West 42 (101),East 42 | depart,arrive |
+
+    Scenario: Suffix To Suffix - Turn
+        Given the node map
+            | a |   | b |   | c |
+            |   |   | d |   |   |
+
+        And the ways
+            | nodes  | name |
+            | ab     | 42 N |
+            | bc     | 42 S |
+            | bd     | 42 E |
+
+       When I route I should get
+            | waypoints | route          | turns                    |
+            | a,c       | 42 N,42 S      | depart,arrive            |
+            | a,d       | 42 N,42 E,42 E | depart,turn right,arrive |
+
+    Scenario: Suffix To No Suffix
+        Given the node map
+            | a |   | b |   | c |
+
+        And the ways
+            | nodes  | name |
+            | ab     | 42 N |
+            | bc     | 42   |
+
+       When I route I should get
+            | waypoints | route   | turns         |
+            | a,c       | 42 N,42 | depart,arrive |
+
+    Scenario: No Suffix To Suffix
+        Given the node map
+            | a |   | b |   | c |
+
+        And the ways
+            | nodes  | name |
+            | ab     | 42   |
+            | bc     | 42 S |
+
+       When I route I should get
+            | waypoints | route   | turns         |
+            | a,c       | 42,42 S | depart,arrive |
+
diff --git a/features/guidance/turn.feature b/features/guidance/turn.feature
index 20f4226..47cd633 100644
--- a/features/guidance/turn.feature
+++ b/features/guidance/turn.feature
@@ -389,7 +389,6 @@ Feature: Simple Turns
             | waypoints | route     | turns                   |
             | a,d       | abc,bd,bd | depart,turn left,arrive |
 
-    @bug @pr2275
     Scenario: Left Turn Assignment (6)
         Given the node map
             | d |   |   |   |   |
@@ -511,7 +510,6 @@ Feature: Simple Turns
             | waypoints | route     | turns                    |
             | a,d       | abc,bd,bd | depart,turn right,arrive |
 
-    @bug @pr2275
     Scenario: Right Turn Assignment (6)
         Given the node map
             |   |   | e |   |   |
@@ -569,7 +567,7 @@ Feature: Simple Turns
 
    Scenario: Right Turn Assignment Two Turns (2)
         Given the node map
-            |   |   | f | c |   |
+            |   |   | f |   | c |
             | a |   | b |   |   |
             |   |   |   |   | e |
             |   |   |   | d |   |
@@ -667,7 +665,6 @@ Feature: Simple Turns
             | d,e       | dbe,dbe | depart,arrive |
             | e,d       | dbe,dbe | depart,arrive |
 
-    @bug @pr2275
     Scenario: Slight Turn involving Oneways
         Given the node map
             |   |   |   | a |   |
@@ -729,7 +726,6 @@ Feature: Simple Turns
             | a,e       | abc,eb,eb | depart,turn right,arrive        |
             | a,f       | abc,fb,fb | depart,turn slight right,arrive |
 
-     @bug @pr2275
      Scenario: Right Turn Assignment Three Conflicting Turns with invalid - 2
         Given the node map
             |   |   | g |   |   |
@@ -790,7 +786,6 @@ Feature: Simple Turns
             | a,e       | abc,be,be | depart,turn right,arrive        |
             | a,f       | abc,bf,bf | depart,turn sharp right,arrive  |
 
-    @bug @pr2275
     Scenario: Conflicting Turns with well distinguished turn (back)
         Given the node map
             | a |   |   | b |   |   | c |
diff --git a/features/step_definitions/data.js b/features/step_definitions/data.js
index db0071c..6026c0f 100644
--- a/features/step_definitions/data.js
+++ b/features/step_definitions/data.js
@@ -10,13 +10,11 @@ module.exports = function () {
     });
 
     this.Given(/^the extract extra arguments "(.*?)"$/, (args, callback) => {
-        this.setExtractArgs(args);
-        callback();
+        this.setExtractArgs(args, callback);
     });
 
     this.Given(/^the contract extra arguments "(.*?)"$/, (args, callback) => {
-        this.setContractArgs(args);
-        callback();
+        this.setContractArgs(args, callback);
     });
 
     this.Given(/^a grid size of (\d+) meters$/, (meters, callback) => {
@@ -49,18 +47,27 @@ module.exports = function () {
 
         var addNode = (name, ri, ci, cb) => {
             if (name) {
-                if (name.length !== 1) throw new Error(util.format('*** node invalid name %s, must be single characters', name));
-                if (!name.match(/[a-z0-9]/)) throw new Error(util.format('*** invalid node name %s, must me alphanumeric', name));
-
-                var lonLat;
-                if (name.match(/[a-z]/)) {
-                    if (this.nameNodeHash[name]) throw new Error(util.format('*** duplicate node %s', name));
+                var nodeWithID = name.match(/([a-z])\:([0-9]*)/);
+                if (nodeWithID) {
+                    var nodeName = nodeWithID[1],
+                        nodeID = nodeWithID[2];
+                    if (this.nameNodeHash[nodeName]) throw new Error(util.format('*** duplicate node %s', name));
                     lonLat = this.tableCoordToLonLat(ci, ri);
-                    this.addOSMNode(name, lonLat[0], lonLat[1], null);
+                    this.addOSMNode(nodeName, lonLat[0], lonLat[1], nodeID);
                 } else {
-                    if (this.locationHash[name]) throw new Error(util.format('*** duplicate node %s'), name);
-                    lonLat = this.tableCoordToLonLat(ci, ri);
-                    this.addLocation(name, lonLat[0], lonLat[1], null);
+                    if (name.length !== 1) throw new Error(util.format('*** node invalid name %s, must be single characters', name));
+                    if (!name.match(/[a-z0-9]/)) throw new Error(util.format('*** invalid node name %s, must me alphanumeric', name));
+
+                    var lonLat;
+                    if (name.match(/[a-z]/)) {
+                        if (this.nameNodeHash[name]) throw new Error(util.format('*** duplicate node %s', name));
+                        lonLat = this.tableCoordToLonLat(ci, ri);
+                        this.addOSMNode(name, lonLat[0], lonLat[1], null);
+                    } else {
+                        if (this.locationHash[name]) throw new Error(util.format('*** duplicate node %s'), name);
+                        lonLat = this.tableCoordToLonLat(ci, ri);
+                        this.addLocation(name, lonLat[0], lonLat[1], null);
+                    }
                 }
 
                 cb();
@@ -228,6 +235,10 @@ module.exports = function () {
         fs.writeFile(path.resolve(this.TEST_FOLDER, 'speeds.csv'), data, callback);
     });
 
+    this.Given(/^the turn penalty file$/, (data, callback) => {
+        fs.writeFile(path.resolve(this.TEST_FOLDER, 'penalties.csv'), data, callback);
+    });
+
     this.Given(/^the data has been saved to disk$/, (callback) => {
         try {
             this.reprocess(callback);
diff --git a/features/step_definitions/options.js b/features/step_definitions/options.js
index 27d6fd3..166c3b3 100644
--- a/features/step_definitions/options.js
+++ b/features/step_definitions/options.js
@@ -1,7 +1,7 @@
 var assert = require('assert');
 
 module.exports = function () {
-    this.When(/^I run "osrm\-routed\s?(.*?)"$/, { timeout: this.SHUTDOWN_TIMEOUT }, (options, callback) => {
+    this.When(/^I run "osrm\-routed\s?(.*?)"$/, { timeout: this.TIMEOUT }, (options, callback) => {
         this.runBin('osrm-routed', options, () => {
             callback();
         });
diff --git a/features/step_definitions/routability.js b/features/step_definitions/routability.js
index 1ef8643..245eb99 100644
--- a/features/step_definitions/routability.js
+++ b/features/step_definitions/routability.js
@@ -71,6 +71,7 @@ module.exports = function () {
                 r.status = res.statusCode === 200 ? 'x' : null;
                 if (r.status) {
                     r.route = this.wayList(r.json.routes[0]);
+                    r.summary = r.json.routes[0].legs.map(l => l.summary).join(',');
 
                     if (r.route.split(',')[0] === util.format('w%d', i)) {
                         r.time = r.json.routes[0].duration;
diff --git a/features/step_definitions/routing.js b/features/step_definitions/routing.js
index d48573e..c3e679a 100644
--- a/features/step_definitions/routing.js
+++ b/features/step_definitions/routing.js
@@ -3,8 +3,7 @@ var d3 = require('d3-queue');
 module.exports = function () {
     this.When(/^I route I should get$/, this.WhenIRouteIShouldGet);
 
-    // This is used to route 100 times; timeout for entire step is therefore set to 100 * STRESS_TIMEOUT
-    this.When(/^I route (\d+) times I should get$/, { timeout: 30000 }, (n, table, callback) => {
+    this.When(/^I route (\d+) times I should get$/, { timeout: 100 * this.TIMEOUT }, (n, table, callback) => {
         var q = d3.queue(1);
 
         for (var i=0; i<n; i++) {
diff --git a/features/support/config.js b/features/support/config.js
index cae7398..1d62030 100644
--- a/features/support/config.js
+++ b/features/support/config.js
@@ -19,8 +19,6 @@ module.exports = function () {
 
         this.osmData = new classes.osmData(this);
 
-        this.STRESS_TIMEOUT = 300;
-
         this.OSRMLoader = this._OSRMLoader();
 
         this.PREPROCESS_LOG_FILE = path.resolve(this.TEST_FOLDER, 'preprocessing.log');
@@ -105,11 +103,16 @@ module.exports = function () {
         } else cb();
     };
 
-    this.setExtractArgs = (args) => {
+    this.setExtractArgs = (args, callback) => {
         this.extractArgs = args;
+        this.forceExtract = true;
+        this.forceContract = true;
+        callback();
     };
 
-    this.setContractArgs = (args) => {
+    this.setContractArgs = (args, callback) => {
         this.contractArgs = args;
+        this.forceContract = true;
+        callback();
     };
 };
diff --git a/features/support/data.js b/features/support/data.js
index 29e5170..d37e555 100644
--- a/features/support/data.js
+++ b/features/support/data.js
@@ -295,9 +295,10 @@ module.exports = function () {
         this.writeAndExtract((e) => {
             if (e) return callback(e);
             this.isContracted((isContracted) => {
-                var contractFn = isContracted ? noop : this.contractData;
+                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();
                     callback();
@@ -311,9 +312,10 @@ module.exports = function () {
             this.writeInputData((e) => {
                 if (e) return callback(e);
                 this.isExtracted((isExtracted) => {
-                    var extractFn = isExtracted ? noop : this.extractData;
+                    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);
                     });
                 });
diff --git a/features/support/env.js b/features/support/env.js
index 5a337bc..621143b 100644
--- a/features/support/env.js
+++ b/features/support/env.js
@@ -6,10 +6,9 @@ var d3 = require('d3-queue');
 
 module.exports = function () {
     this.initializeEnv = (callback) => {
-        this.DEFAULT_PORT = 5000;
-	// OSX builds on Travis hit a timeout of ~2000 from time to time
-        this.DEFAULT_TIMEOUT = 5000;
-        this.setDefaultTimeout(this.DEFAULT_TIMEOUT);
+        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.setDefaultTimeout(this.TIMEOUT);
         this.ROOT_FOLDER = process.cwd();
         this.OSM_USER = 'osrm';
         this.OSM_GENERATOR = 'osrm-test';
@@ -25,8 +24,6 @@ module.exports = function () {
         this.BIN_PATH = path.resolve(this.ROOT_FOLDER, 'build');
         this.DEFAULT_INPUT_FORMAT = 'osm';
         this.DEFAULT_ORIGIN = [1,1];
-        this.LAUNCH_TIMEOUT = 1000;
-        this.SHUTDOWN_TIMEOUT = 10000;
         this.DEFAULT_LOAD_METHOD = 'datastore';
         this.OSRM_ROUTED_LOG_FILE = path.resolve(this.TEST_FOLDER, 'osrm-routed.log');
         this.ERROR_LOG_FILE = path.resolve(this.TEST_FOLDER, 'error.log');
@@ -51,26 +48,6 @@ module.exports = function () {
         console.info(util.format('Node Version', process.version));
         if (parseInt(process.version.match(/v(\d)/)[1]) < 4) throw new Error('*** PLease upgrade to Node 4.+ to run OSRM cucumber tests');
 
-        if (process.env.OSRM_PORT) {
-            this.OSRM_PORT = parseInt(process.env.OSRM_PORT);
-            // eslint-disable-next-line no-console
-            console.info(util.format('Port set to %d', this.OSRM_PORT));
-        } else {
-            this.OSRM_PORT = this.DEFAULT_PORT;
-            // eslint-disable-next-line no-console
-            console.info(util.format('Using default port %d', this.OSRM_PORT));
-        }
-
-        if (process.env.OSRM_TIMEOUT) {
-            this.OSRM_TIMEOUT = parseInt(process.env.OSRM_TIMEOUT);
-            // eslint-disable-next-line no-console
-            console.info(util.format('Timeout set to %d', this.OSRM_TIMEOUT));
-        } else {
-            this.OSRM_TIMEOUT = this.DEFAULT_TIMEOUT;
-            // eslint-disable-next-line no-console
-            console.info(util.format('Using default timeout %d', this.OSRM_TIMEOUT));
-        }
-
         fs.exists(this.TEST_FOLDER, (exists) => {
             if (!exists) throw new Error(util.format('*** Test folder %s doesn\'t exist.', this.TEST_FOLDER));
             callback();
diff --git a/features/support/hooks.js b/features/support/hooks.js
index f8c7a2f..5090264 100644
--- a/features/support/hooks.js
+++ b/features/support/hooks.js
@@ -24,10 +24,12 @@ module.exports = function () {
     });
 
     this.After((scenario, callback) => {
-        this.setExtractArgs('');
-        this.setContractArgs('');
-        if (this.loadMethod === 'directly' && !!this.OSRMLoader.loader) this.OSRMLoader.shutdown(callback);
-        else callback();
+        this.setExtractArgs('', () => {
+            this.setContractArgs('', () => {
+                if (this.loadMethod === 'directly' && !!this.OSRMLoader.loader) this.OSRMLoader.shutdown(callback);
+                else callback();
+            });
+        });
     });
 
     this.Around('@stress', (scenario, callback) => {
diff --git a/features/support/http.js b/features/support/http.js
index 5da59c4..3ae2edc 100644
--- a/features/support/http.js
+++ b/features/support/http.js
@@ -20,7 +20,7 @@ module.exports = function () {
     };
 
     this.sendRequest = (baseUri, parameters, callback) => {
-        var limit = Timeout(this.OSRM_TIMEOUT, { err: { statusCode: 408 } });
+        var limit = Timeout(this.TIMEOUT, { err: { statusCode: 408 } });
 
         var runRequest = (cb) => {
             var params = this.paramsToString(parameters);
diff --git a/features/support/launch_classes.js b/features/support/launch_classes.js
index 38f3b31..976f89e 100644
--- a/features/support/launch_classes.js
+++ b/features/support/launch_classes.js
@@ -12,7 +12,7 @@ var OSRMBaseLoader = class {
     }
 
     launch (callback) {
-        var limit = Timeout(this.scope.LAUNCH_TIMEOUT, { err: this.scope.RoutedError('Launching osrm-routed timed out.') });
+        var limit = Timeout(this.scope.TIMEOUT, { err: this.scope.RoutedError('Launching osrm-routed timed out.') });
 
         var runLaunch = (cb) => {
             this.osrmUp(() => {
@@ -24,7 +24,7 @@ var OSRMBaseLoader = class {
     }
 
     shutdown (callback) {
-        var limit = Timeout(this.scope.SHUTDOWN_TIMEOUT, { err: this.scope.RoutedError('Shutting down osrm-routed timed out.')});
+        var limit = Timeout(this.scope.TIMEOUT, { err: this.scope.RoutedError('Shutting down osrm-routed timed out.')});
 
         var runShutdown = (cb) => {
             this.osrmDown(cb);
diff --git a/features/support/route.js b/features/support/route.js
index 1dbce8c..99a6835 100644
--- a/features/support/route.js
+++ b/features/support/route.js
@@ -15,7 +15,7 @@ module.exports = function () {
 
     this.requestUrl = (path, callback) => {
         var uri = this.query = [this.HOST, path].join('/'),
-            limit = Timeout(this.OSRM_TIMEOUT, { err: { statusCode: 408 } });
+            limit = Timeout(this.TIMEOUT, { err: { statusCode: 408 } });
 
         function runRequest (cb) {
             request(uri, cb);
@@ -127,6 +127,12 @@ module.exports = function () {
         }
     };
 
+    this.summary = (instructions) => {
+        if (instructions) {
+            return instructions.legs.map(l => l.summary).join(',');
+        }
+    };
+
     this.wayList = (instructions) => {
         return this.extractInstructionList(instructions, s => s.name);
     };
@@ -142,6 +148,9 @@ module.exports = function () {
                 case 'depart':
                 case 'arrive':
                     return v.maneuver.type;
+                case 'on ramp':
+                case 'off ramp':
+                    return v.maneuver.type + ' ' + v.maneuver.modifier;
                 case 'roundabout':
                     return 'roundabout-exit-' + v.maneuver.exit;
                 case 'rotary':
@@ -149,6 +158,8 @@ module.exports = function () {
                         return v.rotary_name + '-exit-' + v.maneuver.exit;
                     else
                         return 'rotary-exit-' + v.maneuver.exit;
+                case 'roundabout turn':
+                    return v.maneuver.type + ' ' + v.maneuver.modifier + ' exit-' + v.maneuver.exit;
                 // FIXME this is a little bit over-simplistic for merge/fork instructions
                 default:
                     return v.maneuver.type + ' ' + v.maneuver.modifier;
diff --git a/features/support/shared_steps.js b/features/support/shared_steps.js
index b0be24b..aac42bd 100644
--- a/features/support/shared_steps.js
+++ b/features/support/shared_steps.js
@@ -31,7 +31,7 @@ module.exports = function () {
                 var afterRequest = (err, res, body) => {
                     if (err) return cb(err);
                     if (body && body.length) {
-                        var instructions, bearings, turns, modes, times, distances;
+                        var instructions, bearings, turns, modes, times, distances, summary;
 
                         var json = JSON.parse(body);
 
@@ -44,6 +44,7 @@ module.exports = function () {
                             modes = this.modeList(json.routes[0]);
                             times = this.timeList(json.routes[0]);
                             distances = this.distanceList(json.routes[0]);
+                            summary = this.summary(json.routes[0]);
                         }
 
                         if (headers.has('status')) {
@@ -66,6 +67,10 @@ module.exports = function () {
                         if (headers.has('route')) {
                             got.route = (instructions || '').trim();
 
+                            if (headers.has('summary')) {
+                                got.summary = (summary || '').trim();
+                            }
+
                             if (headers.has('alternative')) {
                                 // TODO examine more than first alternative?
                                 got.alternative ='';
diff --git a/features/testbot/status.feature b/features/testbot/status.feature
index 2221461..8da2b15 100644
--- a/features/testbot/status.feature
+++ b/features/testbot/status.feature
@@ -55,13 +55,13 @@ Feature: Status messages
             | ?                           | 400    | URL string malformed close to position 1: "/?"    |
             | route/v1/driving            | 400    | URL string malformed close to position 17: "ing"  |
             | route/v1/driving/           | 400    | URL string malformed close to position 18: "ng/"  |
-            | route/v1/driving/1          | 400    | Query string malformed close to position 1        |
+            | route/v1/driving/1          | 400    | Query string malformed close to position 19       |
             | route/v1/driving/1,1        | 400    | Number of coordinates needs to be at least two.   |
-            | route/v1/driving/1,1,1      | 400    | Query string malformed close to position 3        |
-            | route/v1/driving/x          | 400    | Query string malformed close to position 0        |
-            | route/v1/driving/x,y        | 400    | Query string malformed close to position 0        |
-            | route/v1/driving/1,1;       | 400    | Query string malformed close to position 3        |
-            | route/v1/driving/1,1;1      | 400    | Query string malformed close to position 5        |
-            | route/v1/driving/1,1;1,1,1  | 400    | Query string malformed close to position 7        |
-            | route/v1/driving/1,1;x      | 400    | Query string malformed close to position 3        |
-            | route/v1/driving/1,1;x,y    | 400    | Query string malformed close to position 3        |
+            | route/v1/driving/1,1,1      | 400    | Query string malformed close to position 21       |
+            | route/v1/driving/x          | 400    | Query string malformed close to position 18       |
+            | route/v1/driving/x,y        | 400    | Query string malformed close to position 18       |
+            | route/v1/driving/1,1;       | 400    | Query string malformed close to position 21       |
+            | route/v1/driving/1,1;1      | 400    | Query string malformed close to position 23       |
+            | route/v1/driving/1,1;1,1,1  | 400    | Query string malformed close to position 25       |
+            | route/v1/driving/1,1;x      | 400    | Query string malformed close to position 21       |
+            | route/v1/driving/1,1;x,y    | 400    | Query string malformed close to position 21       |
diff --git a/features/testbot/summary.feature b/features/testbot/summary.feature
new file mode 100644
index 0000000..3290e3c
--- /dev/null
+++ b/features/testbot/summary.feature
@@ -0,0 +1,77 @@
+ at routing @basic @testbot
+Feature: Basic Routing
+
+    Background:
+        Given the profile "testbot"
+
+    @smallest
+    Scenario: Checking 
+        Given the node map
+            | a | b |  | c | d | e |
+
+        And the ways
+            | nodes |
+            | ab    |
+            | bc    |
+            | cd    |
+            | de    |
+
+        When I route I should get
+            | from | to | route          | summary  |
+            | a    | e  | ab,bc,cd,de,de | ab, bc   |
+            | e    | a  | de,cd,bc,ab,ab | de, bc   |
+            | a    | b  | ab,ab          | ab       |
+            | b    | d  | bc,cd,cd       | bc, cd   |
+
+    @smallest
+    Scenario: Check handling empty values
+        Given the node map
+            | a | b |  | c |   | d | f |
+            |   |   |  |   |   | e |
+
+        And the ways
+            | nodes | name |
+            | ab    | ab   |
+            | bc    | bc   |
+            | cd    |      |
+            | de    | de   |
+            | df    | df   |
+
+        When I route I should get
+            | from | to | route          | summary  |
+            | e    | a  | de,,bc,ab,ab   | de, bc   |
+
+    @smallest @todo
+    Scenario: Summaries when routing on a simple network
+        Given the node map
+            | a | b |
+
+        And the ways
+            | nodes |
+            | ab    |
+
+        When I route I should get
+            | from | to | route | summary  |
+            | a    | b  | ab,ab | ab       |
+            | b    | a  | ab,ab | ab       |
+
+    @repeated
+    Scenario: Check handling empty values
+        Given the node map
+            | f |   |   | x |   |   |
+            | b | c | d | e | 1 | g |
+            | a |   |   | y |   |   |
+
+        And the ways
+            | nodes | name   | # |
+            | ab    | first  |   |
+            | bc    | first  |   |
+            | cd    | first  |   |
+            | deg   | second |   |
+            | bf    | third  |   |
+            | xey   | cross  |we need this because phantom node segments are not considered for the summary |
+
+        When I route I should get
+            | from | to | route                     | summary       |
+            | a    | 1  | first,first,second,second | first, second |
+
diff --git a/features/testbot/traffic_turn_penalties.feature b/features/testbot/traffic_turn_penalties.feature
new file mode 100644
index 0000000..6f6ab58
--- /dev/null
+++ b/features/testbot/traffic_turn_penalties.feature
@@ -0,0 +1,37 @@
+ at routing @speed @traffic
+Feature: Traffic - turn penalties applied to turn onto which a phantom node snaps
+
+    Background: Simple map with phantom nodes
+        Given the node map
+            |     | 1   |     | 2   |     | 3   |     |
+            | a:1 |     | b:2 |     | c:3 |     | d:4 |
+            |     |     |     |     |     |     |     |
+            |     |     | e:5 |     | f:6 |     | g:7 |
+        And the ways
+            | nodes | highway |
+            | ab    | primary |
+            | bc    | primary |
+            | cd    | primary |
+
+            | be    | primary |
+            | cf    | primary |
+            | dg    | primary |
+        And the profile "testbot"
+        # Since testbot doesn't have turn penalties, a penalty from file of 0 should produce a neutral effect
+        And the extract extra arguments "--generate-edge-lookup"
+
+    Scenario: Weighting based on turn penalty file, with an extreme negative value -- clamps and does not fail
+        Given the turn penalty file
+            """
+            1,2,5,0
+            3,4,7,-20
+            """
+        And the contract extra arguments "--turn-penalty-file penalties.csv"
+        When I route I should get
+            | from | to | route    | speed   | time    |
+            | a    | e  | ab,be,be | 36 km/h | 40s +-1 |
+            | 1    | e  | ab,be,be | 36 km/h | 30s +-1 |
+            | b    | f  | bc,cf,cf | 36 km/h | 40s +-1 |
+            | 2    | f  | bc,cf,cf | 36 km/h | 30s +-1 |
+            | c    | g  | cd,dg,dg | 71 km/h | 20s +-1 |
+            | 3    | g  | cd,dg,dg | 54 km/h | 20s +-1 |
diff --git a/features/testbot/via.feature b/features/testbot/via.feature
index e7ebd54..fd4a872 100644
--- a/features/testbot/via.feature
+++ b/features/testbot/via.feature
@@ -148,6 +148,47 @@ Feature: Via points
             | a,d,c     | abc,bd,bd,bd,abc,abc |
             | c,d,a     | abc,bd,bd,bd,abc,abc |
 
+    # See issue #2349
+    Scenario: Via point at a dead end with oneway
+        Given the node map
+            | a | b | c |
+            |   | d |   |
+            |   | e |   |
+
+        And the ways
+            | nodes | oneway |
+            | abc   |  no    |
+            | bd    |  no    |
+            | ed    |  yes   |
+
+        When I route I should get
+            | waypoints | route                |
+            | a,d,c     | abc,bd,bd,bd,abc,abc |
+            | c,d,a     | abc,bd,bd,bd,abc,abc |
+
+    # See issue #2349
+    @bug
+    Scenario: Via point at a dead end with oneway
+        Given the node map
+            | a | b | c |
+            |   | d |   |
+            |   | e | g |
+            |   | f |   |
+
+        And the ways
+            | nodes | oneway |
+            | abc   |  no    |
+            | bd    |  no    |
+            | ed    |  yes   |
+            | dg    |  yes   |
+            | ef    |  no    |
+            | fg    |  yes   |
+
+        When I route I should get
+            | waypoints | route                |
+            | a,d,c     | abc,bd,bd,bd,abc,abc |
+            | c,d,a     | abc,bd,bd,bd,abc,abc |
+
     # See issue #1896
     Scenario: Via point at a dead end with barrier
         Given the profile "car"
diff --git a/include/contractor/contractor.hpp b/include/contractor/contractor.hpp
index 7cfc3dc..a188a86 100644
--- a/include/contractor/contractor.hpp
+++ b/include/contractor/contractor.hpp
@@ -84,6 +84,7 @@ class Contractor
                           const std::string &edge_segment_lookup_path,
                           const std::string &edge_penalty_path,
                           const std::vector<std::string> &segment_speed_path,
+                          const std::vector<std::string> &turn_penalty_path,
                           const std::string &nodes_filename,
                           const std::string &geometry_filename,
                           const std::string &datasource_names_filename,
diff --git a/include/contractor/contractor_config.hpp b/include/contractor/contractor_config.hpp
index f557279..e820b3f 100644
--- a/include/contractor/contractor_config.hpp
+++ b/include/contractor/contractor_config.hpp
@@ -81,6 +81,7 @@ struct ContractorConfig
     double core_factor;
 
     std::vector<std::string> segment_speed_lookup_paths;
+    std::vector<std::string> turn_penalty_lookup_paths;
     std::string datasource_indexes_path;
     std::string datasource_names_path;
 };
diff --git a/include/contractor/crc32_processor.hpp b/include/contractor/crc32_processor.hpp
index eaef76c..cba99a9 100644
--- a/include/contractor/crc32_processor.hpp
+++ b/include/contractor/crc32_processor.hpp
@@ -17,9 +17,9 @@ namespace contractor
 class IteratorbasedCRC32
 {
   public:
-    bool using_hardware() const { return use_hardware_implementation; }
+    bool UsingHardware() const { return use_hardware_implementation; }
 
-    IteratorbasedCRC32() : crc(0) { use_hardware_implementation = detect_hardware_support(); }
+    IteratorbasedCRC32() : crc(0) { use_hardware_implementation = DetectHardwareSupport(); }
 
     template <class Iterator> unsigned operator()(Iterator iter, const Iterator end)
     {
@@ -31,11 +31,11 @@ class IteratorbasedCRC32
 
             if (use_hardware_implementation)
             {
-                crc = compute_in_hardware(data, sizeof(value_type));
+                crc = ComputeInHardware(data, sizeof(value_type));
             }
             else
             {
-                crc = compute_in_software(data, sizeof(value_type));
+                crc = ComputeInSoftware(data, sizeof(value_type));
             }
             ++iter;
         }
@@ -43,7 +43,7 @@ class IteratorbasedCRC32
     }
 
   private:
-    bool detect_hardware_support() const
+    bool DetectHardwareSupport() const
     {
         static const int sse42_bit = 0x00100000;
         const unsigned ecx = cpuid();
@@ -51,14 +51,14 @@ class IteratorbasedCRC32
         return sse42_found;
     }
 
-    unsigned compute_in_software(const char *str, unsigned len)
+    unsigned ComputeInSoftware(const char *str, unsigned len)
     {
         crc_processor.process_bytes(str, len);
         return crc_processor.checksum();
     }
 
     // adapted from http://byteworm.com/2010/10/13/crc32/
-    unsigned compute_in_hardware(const char *str, unsigned len)
+    unsigned ComputeInHardware(const char *str, unsigned len)
     {
 #if defined(__x86_64__)
         unsigned q = len / sizeof(unsigned);
@@ -114,7 +114,7 @@ struct RangebasedCRC32
         return crc32(std::begin(iterable), std::end(iterable));
     }
 
-    bool using_hardware() const { return crc32.using_hardware(); }
+    bool UsingHardware() const { return crc32.UsingHardware(); }
 
   private:
     IteratorbasedCRC32 crc32;
diff --git a/include/contractor/graph_contractor.hpp b/include/contractor/graph_contractor.hpp
index ce33d26..1e7831d 100644
--- a/include/contractor/graph_contractor.hpp
+++ b/include/contractor/graph_contractor.hpp
@@ -115,7 +115,7 @@ class GraphContractor
     {
         explicit ThreadDataContainer(int number_of_nodes) : number_of_nodes(number_of_nodes) {}
 
-        inline ContractorThreadData *getThreadData()
+        inline ContractorThreadData *GetThreadData()
         {
             bool exists = false;
             auto &ref = data.local(exists);
@@ -302,7 +302,7 @@ class GraphContractor
                               [this, &node_priorities, &node_depth,
                                &thread_data_list](const tbb::blocked_range<int> &range)
                               {
-                                  ContractorThreadData *data = thread_data_list.getThreadData();
+                                  ContractorThreadData *data = thread_data_list.GetThreadData();
                                   for (int x = range.begin(), end = range.end(); x != end; ++x)
                                   {
                                       node_priorities[x] =
@@ -424,7 +424,7 @@ class GraphContractor
                 [this, &node_priorities, &remaining_nodes,
                  &thread_data_list](const tbb::blocked_range<std::size_t> &range)
                 {
-                    ContractorThreadData *data = thread_data_list.getThreadData();
+                    ContractorThreadData *data = thread_data_list.GetThreadData();
                     // determine independent node set
                     for (auto i = range.begin(), end = range.end(); i != end; ++i)
                     {
@@ -481,7 +481,7 @@ class GraphContractor
                               [this, &remaining_nodes,
                                &thread_data_list](const tbb::blocked_range<std::size_t> &range)
                               {
-                                  ContractorThreadData *data = thread_data_list.getThreadData();
+                                  ContractorThreadData *data = thread_data_list.GetThreadData();
                                   for (int position = range.begin(), end = range.end();
                                        position != end; ++position)
                                   {
@@ -495,7 +495,7 @@ class GraphContractor
                                         DeleteGrainSize),
                 [this, &remaining_nodes, &thread_data_list](const tbb::blocked_range<int> &range)
                 {
-                    ContractorThreadData *data = thread_data_list.getThreadData();
+                    ContractorThreadData *data = thread_data_list.GetThreadData();
                     for (int position = range.begin(), end = range.end(); position != end;
                          ++position)
                     {
@@ -547,7 +547,7 @@ class GraphContractor
                     [this, &node_priorities, &remaining_nodes, &node_depth,
                      &thread_data_list](const tbb::blocked_range<int> &range)
                     {
-                        ContractorThreadData *data = thread_data_list.getThreadData();
+                        ContractorThreadData *data = thread_data_list.GetThreadData();
                         for (int position = range.begin(), end = range.end(); position != end;
                              ++position)
                         {
@@ -561,7 +561,7 @@ class GraphContractor
             number_of_contracted_nodes += end_independent_nodes_idx - begin_independent_nodes_idx;
             remaining_nodes.resize(begin_independent_nodes_idx);
 
-            p.printStatus(number_of_contracted_nodes);
+            p.PrintStatus(number_of_contracted_nodes);
             ++current_level;
         }
 
@@ -627,7 +627,7 @@ class GraphContractor
             Edge new_edge;
             for (const auto node : util::irange(0u, number_of_nodes))
             {
-                p.printStatus(node);
+                p.PrintStatus(node);
                 for (auto edge : contractor_graph->GetAdjacentEdgeRange(node))
                 {
                     const NodeID target = contractor_graph->GetTarget(edge);
@@ -710,9 +710,9 @@ class GraphContractor
 
     inline void Dijkstra(const int max_distance,
                          const unsigned number_of_targets,
-                         const int maxNodes,
+                         const int max_nodes,
                          ContractorThreadData &data,
-                         const NodeID middleNode)
+                         const NodeID middle_node)
     {
 
         ContractorHeap &heap = data.heap;
@@ -723,7 +723,7 @@ class GraphContractor
         {
             const NodeID node = heap.DeleteMin();
             const auto distance = heap.GetKey(node);
-            if (++nodes > maxNodes)
+            if (++nodes > max_nodes)
             {
                 return;
             }
@@ -742,7 +742,7 @@ class GraphContractor
                 }
             }
 
-            RelaxNode(node, middleNode, distance, heap);
+            RelaxNode(node, middle_node, distance, heap);
         }
     }
 
@@ -1032,7 +1032,7 @@ class GraphContractor
             }
             // tie breaking
             if (std::abs(priority - target_priority) < std::numeric_limits<float>::epsilon() &&
-                bias(node, target))
+                Bias(node, target))
             {
                 return false;
             }
@@ -1061,7 +1061,7 @@ class GraphContractor
                 }
                 // tie breaking
                 if (std::abs(priority - target_priority) < std::numeric_limits<float>::epsilon() &&
-                    bias(node, target))
+                    Bias(node, target))
                 {
                     return false;
                 }
@@ -1071,7 +1071,7 @@ class GraphContractor
     }
 
     // This bias function takes up 22 assembly instructions in total on X86
-    inline bool bias(const NodeID a, const NodeID b) const
+    inline bool Bias(const NodeID a, const NodeID b) const
     {
         const unsigned short hasha = fast_hash(a);
         const unsigned short hashb = fast_hash(b);
diff --git a/include/engine/api/route_api.hpp b/include/engine/api/route_api.hpp
index 1907bee..b8cbee2 100644
--- a/include/engine/api/route_api.hpp
+++ b/include/engine/api/route_api.hpp
@@ -132,6 +132,7 @@ 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,
                                                               phantoms.source_phantom,
                                                               phantoms.target_phantom);
diff --git a/include/engine/base64.hpp b/include/engine/base64.hpp
index 6482961..6b234b4 100644
--- a/include/engine/base64.hpp
+++ b/include/engine/base64.hpp
@@ -15,6 +15,9 @@
 #include <boost/algorithm/string/trim.hpp>
 #include <boost/range/algorithm/copy.hpp>
 
+namespace osrm
+{
+
 // RFC 4648 "The Base16, Base32, and Base64 Data Encodings"
 // See: https://tools.ietf.org/html/rfc4648
 
@@ -36,9 +39,6 @@ using BinaryFromBase64 = boost::archive::iterators::transform_width<
     6  // from a sequence of 6 bit
     >;
 } // ns detail
-
-namespace osrm
-{
 namespace engine
 {
 
diff --git a/include/engine/datafacade/datafacade_base.hpp b/include/engine/datafacade/datafacade_base.hpp
index 1f608d9..039b3a9 100644
--- a/include/engine/datafacade/datafacade_base.hpp
+++ b/include/engine/datafacade/datafacade_base.hpp
@@ -90,47 +90,47 @@ class BaseDataFacade
     virtual extractor::TravelMode GetTravelModeForEdgeID(const unsigned id) const = 0;
 
     virtual std::vector<RTreeLeaf> GetEdgesInBox(const util::Coordinate south_west,
-                                                 const util::Coordinate north_east) = 0;
+                                                 const util::Coordinate north_east) const = 0;
 
     virtual std::vector<PhantomNodeWithDistance>
     NearestPhantomNodesInRange(const util::Coordinate input_coordinate,
                                const float max_distance,
                                const int bearing,
-                               const int bearing_range) = 0;
+                               const int bearing_range) const = 0;
     virtual std::vector<PhantomNodeWithDistance>
     NearestPhantomNodesInRange(const util::Coordinate input_coordinate,
-                               const float max_distance) = 0;
+                               const float max_distance) const = 0;
 
     virtual std::vector<PhantomNodeWithDistance>
     NearestPhantomNodes(const util::Coordinate input_coordinate,
                         const unsigned max_results,
                         const double max_distance,
                         const int bearing,
-                        const int bearing_range) = 0;
+                        const int bearing_range) const = 0;
     virtual std::vector<PhantomNodeWithDistance>
     NearestPhantomNodes(const util::Coordinate input_coordinate,
                         const unsigned max_results,
                         const int bearing,
-                        const int bearing_range) = 0;
+                        const int bearing_range) const = 0;
     virtual std::vector<PhantomNodeWithDistance>
-    NearestPhantomNodes(const util::Coordinate input_coordinate, const unsigned max_results) = 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) = 0;
+                        const double max_distance) const = 0;
 
     virtual std::pair<PhantomNode, PhantomNode>
-    NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate) = 0;
+    NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate) const = 0;
     virtual std::pair<PhantomNode, PhantomNode>
     NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate,
-                                                      const double max_distance) = 0;
+                                                      const double max_distance) const = 0;
     virtual std::pair<PhantomNode, PhantomNode>
     NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate,
                                                       const double max_distance,
                                                       const int bearing,
-                                                      const int bearing_range) = 0;
+                                                      const int bearing_range) const = 0;
     virtual std::pair<PhantomNode, PhantomNode> NearestPhantomNodeWithAlternativeFromBigComponent(
-        const util::Coordinate input_coordinate, const int bearing, const int bearing_range) = 0;
+        const util::Coordinate input_coordinate, const int bearing, const int bearing_range) const = 0;
 
     virtual unsigned GetCheckSum() const = 0;
 
diff --git a/include/engine/datafacade/internal_datafacade.hpp b/include/engine/datafacade/internal_datafacade.hpp
index bda4869..ff9d4ad 100644
--- a/include/engine/datafacade/internal_datafacade.hpp
+++ b/include/engine/datafacade/internal_datafacade.hpp
@@ -7,6 +7,7 @@
 
 #include "extractor/guidance/turn_instruction.hpp"
 
+#include "storage/storage_config.hpp"
 #include "engine/geospatial_query.hpp"
 #include "extractor/original_edge_data.hpp"
 #include "extractor/profile_properties.hpp"
@@ -55,8 +56,8 @@ class InternalDataFacade final : public BaseDataFacade
   private:
     using super = BaseDataFacade;
     using QueryGraph = util::StaticGraph<typename super::EdgeData>;
-    using InputEdge = typename QueryGraph::InputEdge;
-    using RTreeLeaf = typename super::RTreeLeaf;
+    using InputEdge = QueryGraph::InputEdge;
+    using RTreeLeaf = super::RTreeLeaf;
     using InternalRTree =
         util::StaticRTree<RTreeLeaf, util::ShM<util::Coordinate, false>::vector, false>;
     using InternalGeospatialQuery = GeospatialQuery<InternalRTree, BaseDataFacade>;
@@ -68,7 +69,7 @@ class InternalDataFacade final : public BaseDataFacade
     std::unique_ptr<QueryGraph> m_query_graph;
     std::string m_timestamp;
 
-    std::shared_ptr<util::ShM<util::Coordinate, false>::vector> m_coordinate_list;
+    util::ShM<util::Coordinate, false>::vector m_coordinate_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;
@@ -82,8 +83,8 @@ class InternalDataFacade final : public BaseDataFacade
     util::ShM<std::string, false>::vector m_datasource_names;
     extractor::ProfileProperties m_profile_properties;
 
-    boost::thread_specific_ptr<InternalRTree> m_static_rtree;
-    boost::thread_specific_ptr<InternalGeospatialQuery> m_geospatial_query;
+    std::unique_ptr<InternalRTree> m_static_rtree;
+    std::unique_ptr<InternalGeospatialQuery> m_geospatial_query;
     boost::filesystem::path ram_index_path;
     boost::filesystem::path file_index_path;
     util::RangeTable<16, false> m_name_table;
@@ -112,8 +113,8 @@ class InternalDataFacade final : public BaseDataFacade
 
     void LoadGraph(const boost::filesystem::path &hsgr_path)
     {
-        typename util::ShM<typename QueryGraph::NodeArrayEntry, false>::vector node_list;
-        typename util::ShM<typename QueryGraph::EdgeArrayEntry, false>::vector edge_list;
+        util::ShM<QueryGraph::NodeArrayEntry, false>::vector node_list;
+        util::ShM<QueryGraph::EdgeArrayEntry, false>::vector edge_list;
 
         util::SimpleLogger().Write() << "loading graph from " << hsgr_path.string();
 
@@ -138,12 +139,12 @@ class InternalDataFacade final : public BaseDataFacade
         extractor::QueryNode current_node;
         unsigned number_of_coordinates = 0;
         nodes_input_stream.read((char *)&number_of_coordinates, sizeof(unsigned));
-        m_coordinate_list = std::make_shared<std::vector<util::Coordinate>>(number_of_coordinates);
+        m_coordinate_list.resize(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->at(i) = util::Coordinate(current_node.lon, current_node.lat);
-            BOOST_ASSERT(m_coordinate_list->at(i).IsValid());
+            m_coordinate_list[i] = util::Coordinate(current_node.lon, current_node.lat);
+            BOOST_ASSERT(m_coordinate_list[i].IsValid());
         }
 
         boost::filesystem::ifstream edges_input_stream(edges_file, std::ios::binary);
@@ -252,7 +253,7 @@ class InternalDataFacade final : public BaseDataFacade
 
     void LoadRTree()
     {
-        BOOST_ASSERT_MSG(!m_coordinate_list->empty(), "coordinates must be loaded before r-tree");
+        BOOST_ASSERT_MSG(!m_coordinate_list.empty(), "coordinates must be loaded before r-tree");
 
         m_static_rtree.reset(new InternalRTree(ram_index_path, file_index_path, m_coordinate_list));
         m_geospatial_query.reset(
@@ -312,6 +313,9 @@ class InternalDataFacade final : public BaseDataFacade
 
         util::SimpleLogger().Write() << "loading street names";
         LoadStreetNames(config.names_data_path);
+
+        util::SimpleLogger().Write() << "loading rtree";
+        LoadRTree();
     }
 
     // search graph access
@@ -360,7 +364,7 @@ class InternalDataFacade final : public BaseDataFacade
     // node and edge information access
     util::Coordinate GetCoordinateOfNode(const unsigned id) const override final
     {
-        return m_coordinate_list->at(id);
+        return m_coordinate_list[id];
     }
 
     extractor::guidance::TurnInstruction
@@ -375,13 +379,9 @@ class InternalDataFacade final : public BaseDataFacade
     }
 
     std::vector<RTreeLeaf> GetEdgesInBox(const util::Coordinate south_west,
-                                         const util::Coordinate north_east) override final
+                                         const util::Coordinate north_east) const override final
     {
-        if (!m_static_rtree.get())
-        {
-            LoadRTree();
-            BOOST_ASSERT(m_geospatial_query.get());
-        }
+        BOOST_ASSERT(m_geospatial_query.get());
         const util::RectangleInt2D bbox{south_west.lon, north_east.lon, south_west.lat,
                                         north_east.lat};
         return m_geospatial_query->Search(bbox);
@@ -389,13 +389,9 @@ class InternalDataFacade final : public BaseDataFacade
 
     std::vector<PhantomNodeWithDistance>
     NearestPhantomNodesInRange(const util::Coordinate input_coordinate,
-                               const float max_distance) override final
+                               const float max_distance) const override final
     {
-        if (!m_static_rtree.get())
-        {
-            LoadRTree();
-            BOOST_ASSERT(m_geospatial_query.get());
-        }
+        BOOST_ASSERT(m_geospatial_query.get());
 
         return m_geospatial_query->NearestPhantomNodesInRange(input_coordinate, max_distance);
     }
@@ -404,13 +400,9 @@ class InternalDataFacade final : public BaseDataFacade
     NearestPhantomNodesInRange(const util::Coordinate input_coordinate,
                                const float max_distance,
                                const int bearing,
-                               const int bearing_range) override final
+                               const int bearing_range) const override final
     {
-        if (!m_static_rtree.get())
-        {
-            LoadRTree();
-            BOOST_ASSERT(m_geospatial_query.get());
-        }
+        BOOST_ASSERT(m_geospatial_query.get());
 
         return m_geospatial_query->NearestPhantomNodesInRange(input_coordinate, max_distance,
                                                               bearing, bearing_range);
@@ -418,13 +410,9 @@ class InternalDataFacade final : public BaseDataFacade
 
     std::vector<PhantomNodeWithDistance>
     NearestPhantomNodes(const util::Coordinate input_coordinate,
-                        const unsigned max_results) override final
+                        const unsigned max_results) const override final
     {
-        if (!m_static_rtree.get())
-        {
-            LoadRTree();
-            BOOST_ASSERT(m_geospatial_query.get());
-        }
+        BOOST_ASSERT(m_geospatial_query.get());
 
         return m_geospatial_query->NearestPhantomNodes(input_coordinate, max_results);
     }
@@ -432,13 +420,9 @@ class InternalDataFacade final : public BaseDataFacade
     std::vector<PhantomNodeWithDistance>
     NearestPhantomNodes(const util::Coordinate input_coordinate,
                         const unsigned max_results,
-                        const double max_distance) override final
+                        const double max_distance) const override final
     {
-        if (!m_static_rtree.get())
-        {
-            LoadRTree();
-            BOOST_ASSERT(m_geospatial_query.get());
-        }
+        BOOST_ASSERT(m_geospatial_query.get());
 
         return m_geospatial_query->NearestPhantomNodes(input_coordinate, max_results, max_distance);
     }
@@ -447,13 +431,9 @@ class InternalDataFacade final : public BaseDataFacade
     NearestPhantomNodes(const util::Coordinate input_coordinate,
                         const unsigned max_results,
                         const int bearing,
-                        const int bearing_range) override final
+                        const int bearing_range) const override final
     {
-        if (!m_static_rtree.get())
-        {
-            LoadRTree();
-            BOOST_ASSERT(m_geospatial_query.get());
-        }
+        BOOST_ASSERT(m_geospatial_query.get());
 
         return m_geospatial_query->NearestPhantomNodes(input_coordinate, max_results, bearing,
                                                        bearing_range);
@@ -464,13 +444,9 @@ class InternalDataFacade final : public BaseDataFacade
                         const unsigned max_results,
                         const double max_distance,
                         const int bearing,
-                        const int bearing_range) override final
+                        const int bearing_range) const override final
     {
-        if (!m_static_rtree.get())
-        {
-            LoadRTree();
-            BOOST_ASSERT(m_geospatial_query.get());
-        }
+        BOOST_ASSERT(m_geospatial_query.get());
 
         return m_geospatial_query->NearestPhantomNodes(input_coordinate, max_results, max_distance,
                                                        bearing, bearing_range);
@@ -478,26 +454,18 @@ class InternalDataFacade final : public BaseDataFacade
 
     std::pair<PhantomNode, PhantomNode>
     NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate,
-                                                      const double max_distance) override final
+                                                      const double max_distance) const override final
     {
-        if (!m_static_rtree.get())
-        {
-            LoadRTree();
-            BOOST_ASSERT(m_geospatial_query.get());
-        }
+        BOOST_ASSERT(m_geospatial_query.get());
 
         return m_geospatial_query->NearestPhantomNodeWithAlternativeFromBigComponent(
             input_coordinate, max_distance);
     }
 
     std::pair<PhantomNode, PhantomNode> NearestPhantomNodeWithAlternativeFromBigComponent(
-        const util::Coordinate input_coordinate) override final
+        const util::Coordinate input_coordinate) const override final
     {
-        if (!m_static_rtree.get())
-        {
-            LoadRTree();
-            BOOST_ASSERT(m_geospatial_query.get());
-        }
+        BOOST_ASSERT(m_geospatial_query.get());
 
         return m_geospatial_query->NearestPhantomNodeWithAlternativeFromBigComponent(
             input_coordinate);
@@ -507,13 +475,9 @@ class InternalDataFacade final : public BaseDataFacade
     NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate,
                                                       const double max_distance,
                                                       const int bearing,
-                                                      const int bearing_range) override final
+                                                      const int bearing_range) const override final
     {
-        if (!m_static_rtree.get())
-        {
-            LoadRTree();
-            BOOST_ASSERT(m_geospatial_query.get());
-        }
+        BOOST_ASSERT(m_geospatial_query.get());
 
         return m_geospatial_query->NearestPhantomNodeWithAlternativeFromBigComponent(
             input_coordinate, max_distance, bearing, bearing_range);
@@ -522,13 +486,9 @@ class InternalDataFacade final : public BaseDataFacade
     std::pair<PhantomNode, PhantomNode>
     NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate,
                                                       const int bearing,
-                                                      const int bearing_range) override final
+                                                      const int bearing_range) const override final
     {
-        if (!m_static_rtree.get())
-        {
-            LoadRTree();
-            BOOST_ASSERT(m_geospatial_query.get());
-        }
+        BOOST_ASSERT(m_geospatial_query.get());
 
         return m_geospatial_query->NearestPhantomNodeWithAlternativeFromBigComponent(
             input_coordinate, bearing, bearing_range);
diff --git a/include/engine/datafacade/shared_datafacade.hpp b/include/engine/datafacade/shared_datafacade.hpp
index bd70e83..97e1ea2 100644
--- a/include/engine/datafacade/shared_datafacade.hpp
+++ b/include/engine/datafacade/shared_datafacade.hpp
@@ -9,6 +9,7 @@
 
 #include "extractor/guidance/turn_instruction.hpp"
 #include "extractor/profile_properties.hpp"
+#include "extractor/compressed_edge_container.hpp"
 
 #include "engine/geospatial_query.hpp"
 #include "util/range_table.hpp"
@@ -46,16 +47,15 @@ class SharedDataFacade final : public BaseDataFacade
   private:
     using super = BaseDataFacade;
     using QueryGraph = util::StaticGraph<EdgeData, true>;
-    using GraphNode = typename QueryGraph::NodeArrayEntry;
-    using GraphEdge = typename QueryGraph::EdgeArrayEntry;
-    using NameIndexBlock = typename util::RangeTable<16, true>::BlockT;
-    using InputEdge = typename QueryGraph::InputEdge;
-    using RTreeLeaf = typename super::RTreeLeaf;
+    using GraphNode = QueryGraph::NodeArrayEntry;
+    using GraphEdge = QueryGraph::EdgeArrayEntry;
+    using NameIndexBlock = util::RangeTable<16, true>::BlockT;
+    using InputEdge = QueryGraph::InputEdge;
+    using RTreeLeaf = super::RTreeLeaf;
     using SharedRTree =
         util::StaticRTree<RTreeLeaf, util::ShM<util::Coordinate, true>::vector, true>;
     using SharedGeospatialQuery = GeospatialQuery<SharedRTree, BaseDataFacade>;
-    using TimeStampedRTreePair = std::pair<unsigned, std::shared_ptr<SharedRTree>>;
-    using RTreeNode = typename SharedRTree::TreeNode;
+    using RTreeNode = SharedRTree::TreeNode;
 
     storage::SharedDataLayout *data_layout;
     char *shared_memory;
@@ -70,9 +70,9 @@ class SharedDataFacade final : public BaseDataFacade
     std::unique_ptr<storage::SharedMemory> m_layout_memory;
     std::unique_ptr<storage::SharedMemory> m_large_memory;
     std::string m_timestamp;
-    extractor::ProfileProperties* m_profile_properties;
+    extractor::ProfileProperties *m_profile_properties;
 
-    std::shared_ptr<util::ShM<util::Coordinate, true>::vector> m_coordinate_list;
+    util::ShM<util::Coordinate, true>::vector m_coordinate_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;
@@ -88,8 +88,8 @@ class SharedDataFacade final : public BaseDataFacade
     util::ShM<std::size_t, true>::vector m_datasource_name_offsets;
     util::ShM<std::size_t, true>::vector m_datasource_name_lengths;
 
-    boost::thread_specific_ptr<std::pair<unsigned, std::shared_ptr<SharedRTree>>> m_static_rtree;
-    boost::thread_specific_ptr<SharedGeospatialQuery> m_geospatial_query;
+    std::unique_ptr<SharedRTree> m_static_rtree;
+    std::unique_ptr<SharedGeospatialQuery> m_geospatial_query;
     boost::filesystem::path file_index_path;
 
     std::shared_ptr<util::RangeTable<16, true>> m_name_table;
@@ -103,8 +103,8 @@ class SharedDataFacade final : public BaseDataFacade
 
     void LoadProfileProperties()
     {
-        m_profile_properties =
-            data_layout->GetBlockPtr<extractor::ProfileProperties>(shared_memory, storage::SharedDataLayout::PROPERTIES);
+        m_profile_properties = data_layout->GetBlockPtr<extractor::ProfileProperties>(
+            shared_memory, storage::SharedDataLayout::PROPERTIES);
     }
 
     void LoadTimestamp()
@@ -119,17 +119,15 @@ class SharedDataFacade final : public BaseDataFacade
 
     void LoadRTree()
     {
-        BOOST_ASSERT_MSG(!m_coordinate_list->empty(), "coordinates must be loaded before r-tree");
+        BOOST_ASSERT_MSG(!m_coordinate_list.empty(), "coordinates must be loaded before r-tree");
 
         auto tree_ptr = data_layout->GetBlockPtr<RTreeNode>(
             shared_memory, storage::SharedDataLayout::R_SEARCH_TREE);
-        m_static_rtree.reset(new TimeStampedRTreePair(
-            CURRENT_TIMESTAMP,
-            util::make_unique<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->second, m_coordinate_list, *this));
+            new SharedGeospatialQuery(*m_static_rtree, m_coordinate_list, *this));
     }
 
     void LoadGraph()
@@ -140,9 +138,9 @@ class SharedDataFacade final : public BaseDataFacade
         auto graph_edges_ptr = data_layout->GetBlockPtr<GraphEdge>(
             shared_memory, storage::SharedDataLayout::GRAPH_EDGE_LIST);
 
-        typename util::ShM<GraphNode, true>::vector node_list(
+        util::ShM<GraphNode, true>::vector node_list(
             graph_nodes_ptr, data_layout->num_entries[storage::SharedDataLayout::GRAPH_NODE_LIST]);
-        typename util::ShM<GraphEdge, true>::vector edge_list(
+        util::ShM<GraphEdge, true>::vector edge_list(
             graph_edges_ptr, data_layout->num_entries[storage::SharedDataLayout::GRAPH_EDGE_LIST]);
         m_query_graph.reset(new QueryGraph(node_list, edge_list));
     }
@@ -151,28 +149,26 @@ class SharedDataFacade final : public BaseDataFacade
     {
         auto coordinate_list_ptr = data_layout->GetBlockPtr<util::Coordinate>(
             shared_memory, storage::SharedDataLayout::COORDINATE_LIST);
-        m_coordinate_list = util::make_unique<util::ShM<util::Coordinate, true>::vector>(
-            coordinate_list_ptr,
+        m_coordinate_list.reset(coordinate_list_ptr,
             data_layout->num_entries[storage::SharedDataLayout::COORDINATE_LIST]);
 
         auto travel_mode_list_ptr = data_layout->GetBlockPtr<extractor::TravelMode>(
             shared_memory, storage::SharedDataLayout::TRAVEL_MODE);
-        typename util::ShM<extractor::TravelMode, true>::vector travel_mode_list(
+        util::ShM<extractor::TravelMode, true>::vector travel_mode_list(
             travel_mode_list_ptr, data_layout->num_entries[storage::SharedDataLayout::TRAVEL_MODE]);
         m_travel_mode_list = std::move(travel_mode_list);
 
         auto turn_instruction_list_ptr =
             data_layout->GetBlockPtr<extractor::guidance::TurnInstruction>(
                 shared_memory, storage::SharedDataLayout::TURN_INSTRUCTION);
-        typename util::ShM<extractor::guidance::TurnInstruction, true>::vector
-            turn_instruction_list(
-                turn_instruction_list_ptr,
-                data_layout->num_entries[storage::SharedDataLayout::TURN_INSTRUCTION]);
+        util::ShM<extractor::guidance::TurnInstruction, true>::vector turn_instruction_list(
+            turn_instruction_list_ptr,
+            data_layout->num_entries[storage::SharedDataLayout::TURN_INSTRUCTION]);
         m_turn_instruction_list = std::move(turn_instruction_list);
 
         auto name_id_list_ptr = data_layout->GetBlockPtr<unsigned>(
             shared_memory, storage::SharedDataLayout::NAME_ID_LIST);
-        typename util::ShM<unsigned, true>::vector name_id_list(
+        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);
     }
@@ -181,7 +177,7 @@ class SharedDataFacade final : public BaseDataFacade
     {
         auto via_node_list_ptr = data_layout->GetBlockPtr<NodeID>(
             shared_memory, storage::SharedDataLayout::VIA_NODE_LIST);
-        typename util::ShM<NodeID, true>::vector via_node_list(
+        util::ShM<NodeID, true>::vector via_node_list(
             via_node_list_ptr, data_layout->num_entries[storage::SharedDataLayout::VIA_NODE_LIST]);
         m_via_node_list = std::move(via_node_list);
     }
@@ -192,14 +188,14 @@ class SharedDataFacade final : public BaseDataFacade
             shared_memory, storage::SharedDataLayout::NAME_OFFSETS);
         auto blocks_ptr = data_layout->GetBlockPtr<NameIndexBlock>(
             shared_memory, storage::SharedDataLayout::NAME_BLOCKS);
-        typename util::ShM<unsigned, true>::vector name_offsets(
+        util::ShM<unsigned, true>::vector name_offsets(
             offsets_ptr, data_layout->num_entries[storage::SharedDataLayout::NAME_OFFSETS]);
-        typename util::ShM<NameIndexBlock, true>::vector name_blocks(
+        util::ShM<NameIndexBlock, true>::vector name_blocks(
             blocks_ptr, data_layout->num_entries[storage::SharedDataLayout::NAME_BLOCKS]);
 
         auto names_list_ptr = data_layout->GetBlockPtr<char>(
             shared_memory, storage::SharedDataLayout::NAME_CHAR_LIST);
-        typename util::ShM<char, true>::vector names_char_list(
+        util::ShM<char, true>::vector names_char_list(
             names_list_ptr, data_layout->num_entries[storage::SharedDataLayout::NAME_CHAR_LIST]);
         m_name_table = util::make_unique<util::RangeTable<16, true>>(
             name_offsets, name_blocks, static_cast<unsigned>(names_char_list.size()));
@@ -216,7 +212,7 @@ class SharedDataFacade final : public BaseDataFacade
 
         auto core_marker_ptr = data_layout->GetBlockPtr<unsigned>(
             shared_memory, storage::SharedDataLayout::CORE_MARKER);
-        typename util::ShM<bool, true>::vector is_core_node(
+        util::ShM<bool, true>::vector is_core_node(
             core_marker_ptr, data_layout->num_entries[storage::SharedDataLayout::CORE_MARKER]);
         m_is_core_node = std::move(is_core_node);
     }
@@ -225,7 +221,7 @@ class SharedDataFacade final : public BaseDataFacade
     {
         auto geometries_index_ptr = data_layout->GetBlockPtr<unsigned>(
             shared_memory, storage::SharedDataLayout::GEOMETRIES_INDEX);
-        typename util::ShM<unsigned, true>::vector geometry_begin_indices(
+        util::ShM<unsigned, true>::vector geometry_begin_indices(
             geometries_index_ptr,
             data_layout->num_entries[storage::SharedDataLayout::GEOMETRIES_INDEX]);
         m_geometry_indices = std::move(geometry_begin_indices);
@@ -233,35 +229,35 @@ class SharedDataFacade final : public BaseDataFacade
         auto geometries_list_ptr =
             data_layout->GetBlockPtr<extractor::CompressedEdgeContainer::CompressedEdge>(
                 shared_memory, storage::SharedDataLayout::GEOMETRIES_LIST);
-        typename util::ShM<extractor::CompressedEdgeContainer::CompressedEdge, true>::vector
-            geometry_list(geometries_list_ptr,
-                          data_layout->num_entries[storage::SharedDataLayout::GEOMETRIES_LIST]);
+        util::ShM<extractor::CompressedEdgeContainer::CompressedEdge, true>::vector geometry_list(
+            geometries_list_ptr,
+            data_layout->num_entries[storage::SharedDataLayout::GEOMETRIES_LIST]);
         m_geometry_list = std::move(geometry_list);
 
         auto datasources_list_ptr = data_layout->GetBlockPtr<uint8_t>(
             shared_memory, storage::SharedDataLayout::DATASOURCES_LIST);
-        typename util::ShM<uint8_t, true>::vector datasources_list(
+        util::ShM<uint8_t, true>::vector datasources_list(
             datasources_list_ptr,
             data_layout->num_entries[storage::SharedDataLayout::DATASOURCES_LIST]);
         m_datasource_list = std::move(datasources_list);
 
         auto datasource_name_data_ptr = data_layout->GetBlockPtr<char>(
             shared_memory, storage::SharedDataLayout::DATASOURCE_NAME_DATA);
-        typename util::ShM<char, true>::vector datasource_name_data(
+        util::ShM<char, true>::vector datasource_name_data(
             datasource_name_data_ptr,
             data_layout->num_entries[storage::SharedDataLayout::DATASOURCE_NAME_DATA]);
         m_datasource_name_data = std::move(datasource_name_data);
 
         auto datasource_name_offsets_ptr = data_layout->GetBlockPtr<std::size_t>(
             shared_memory, storage::SharedDataLayout::DATASOURCE_NAME_OFFSETS);
-        typename util::ShM<std::size_t, true>::vector datasource_name_offsets(
+        util::ShM<std::size_t, true>::vector datasource_name_offsets(
             datasource_name_offsets_ptr,
             data_layout->num_entries[storage::SharedDataLayout::DATASOURCE_NAME_OFFSETS]);
         m_datasource_name_offsets = std::move(datasource_name_offsets);
 
         auto datasource_name_lengths_ptr = data_layout->GetBlockPtr<std::size_t>(
             shared_memory, storage::SharedDataLayout::DATASOURCE_NAME_LENGTHS);
-        typename util::ShM<std::size_t, true>::vector datasource_name_lengths(
+        util::ShM<std::size_t, true>::vector datasource_name_lengths(
             datasource_name_lengths_ptr,
             data_layout->num_entries[storage::SharedDataLayout::DATASOURCE_NAME_LENGTHS]);
         m_datasource_name_lengths = std::move(datasource_name_lengths);
@@ -353,15 +349,13 @@ class SharedDataFacade final : public BaseDataFacade
                 LoadNames();
                 LoadCoreInformation();
                 LoadProfileProperties();
+                LoadRTree();
 
                 util::SimpleLogger().Write() << "number of geometries: "
-                                             << m_coordinate_list->size();
-                for (unsigned i = 0; i < m_coordinate_list->size(); ++i)
+                                             << m_coordinate_list.size();
+                for (unsigned i = 0; i < m_coordinate_list.size(); ++i)
                 {
-                    if (!GetCoordinateOfNode(i).IsValid())
-                    {
-                        util::SimpleLogger().Write() << "coordinate " << i << " not valid";
-                    }
+                    BOOST_ASSERT(GetCoordinateOfNode(i).IsValid());
                 }
             }
             util::SimpleLogger().Write(logDEBUG) << "Releasing exclusive lock";
@@ -414,7 +408,7 @@ class SharedDataFacade final : public BaseDataFacade
     // node and edge information access
     util::Coordinate GetCoordinateOfNode(const NodeID id) const override final
     {
-        return m_coordinate_list->at(id);
+        return m_coordinate_list[id];
     }
 
     virtual void GetUncompressedGeometry(const EdgeID id,
@@ -465,13 +459,9 @@ class SharedDataFacade final : public BaseDataFacade
     }
 
     std::vector<RTreeLeaf> GetEdgesInBox(const util::Coordinate south_west,
-                                         const util::Coordinate north_east) override final
+                                         const util::Coordinate north_east) const override final
     {
-        if (!m_static_rtree.get() || CURRENT_TIMESTAMP != m_static_rtree->first)
-        {
-            LoadRTree();
-            BOOST_ASSERT(m_geospatial_query.get());
-        }
+        BOOST_ASSERT(m_geospatial_query.get());
         const util::RectangleInt2D bbox{south_west.lon, north_east.lon, south_west.lat,
                                         north_east.lat};
         return m_geospatial_query->Search(bbox);
@@ -479,13 +469,9 @@ class SharedDataFacade final : public BaseDataFacade
 
     std::vector<PhantomNodeWithDistance>
     NearestPhantomNodesInRange(const util::Coordinate input_coordinate,
-                               const float max_distance) override final
+                               const float max_distance) const override final
     {
-        if (!m_static_rtree.get() || CURRENT_TIMESTAMP != m_static_rtree->first)
-        {
-            LoadRTree();
-            BOOST_ASSERT(m_geospatial_query.get());
-        }
+        BOOST_ASSERT(m_geospatial_query.get());
 
         return m_geospatial_query->NearestPhantomNodesInRange(input_coordinate, max_distance);
     }
@@ -494,13 +480,9 @@ class SharedDataFacade final : public BaseDataFacade
     NearestPhantomNodesInRange(const util::Coordinate input_coordinate,
                                const float max_distance,
                                const int bearing,
-                               const int bearing_range) override final
+                               const int bearing_range) const override final
     {
-        if (!m_static_rtree.get() || CURRENT_TIMESTAMP != m_static_rtree->first)
-        {
-            LoadRTree();
-            BOOST_ASSERT(m_geospatial_query.get());
-        }
+        BOOST_ASSERT(m_geospatial_query.get());
 
         return m_geospatial_query->NearestPhantomNodesInRange(input_coordinate, max_distance,
                                                               bearing, bearing_range);
@@ -508,13 +490,9 @@ class SharedDataFacade final : public BaseDataFacade
 
     std::vector<PhantomNodeWithDistance>
     NearestPhantomNodes(const util::Coordinate input_coordinate,
-                        const unsigned max_results) override final
+                        const unsigned max_results) const override final
     {
-        if (!m_static_rtree.get() || CURRENT_TIMESTAMP != m_static_rtree->first)
-        {
-            LoadRTree();
-            BOOST_ASSERT(m_geospatial_query.get());
-        }
+        BOOST_ASSERT(m_geospatial_query.get());
 
         return m_geospatial_query->NearestPhantomNodes(input_coordinate, max_results);
     }
@@ -522,13 +500,9 @@ class SharedDataFacade final : public BaseDataFacade
     std::vector<PhantomNodeWithDistance>
     NearestPhantomNodes(const util::Coordinate input_coordinate,
                         const unsigned max_results,
-                        const double max_distance) override final
+                        const double max_distance) const override final
     {
-        if (!m_static_rtree.get() || CURRENT_TIMESTAMP != m_static_rtree->first)
-        {
-            LoadRTree();
-            BOOST_ASSERT(m_geospatial_query.get());
-        }
+        BOOST_ASSERT(m_geospatial_query.get());
 
         return m_geospatial_query->NearestPhantomNodes(input_coordinate, max_results, max_distance);
     }
@@ -537,13 +511,9 @@ class SharedDataFacade final : public BaseDataFacade
     NearestPhantomNodes(const util::Coordinate input_coordinate,
                         const unsigned max_results,
                         const int bearing,
-                        const int bearing_range) override final
+                        const int bearing_range) const override final
     {
-        if (!m_static_rtree.get() || CURRENT_TIMESTAMP != m_static_rtree->first)
-        {
-            LoadRTree();
-            BOOST_ASSERT(m_geospatial_query.get());
-        }
+        BOOST_ASSERT(m_geospatial_query.get());
 
         return m_geospatial_query->NearestPhantomNodes(input_coordinate, max_results, bearing,
                                                        bearing_range);
@@ -554,26 +524,18 @@ class SharedDataFacade final : public BaseDataFacade
                         const unsigned max_results,
                         const double max_distance,
                         const int bearing,
-                        const int bearing_range) override final
+                        const int bearing_range) const override final
     {
-        if (!m_static_rtree.get() || CURRENT_TIMESTAMP != m_static_rtree->first)
-        {
-            LoadRTree();
-            BOOST_ASSERT(m_geospatial_query.get());
-        }
+        BOOST_ASSERT(m_geospatial_query.get());
 
         return m_geospatial_query->NearestPhantomNodes(input_coordinate, max_results, max_distance,
                                                        bearing, bearing_range);
     }
 
     std::pair<PhantomNode, PhantomNode> NearestPhantomNodeWithAlternativeFromBigComponent(
-        const util::Coordinate input_coordinate) override final
+        const util::Coordinate input_coordinate) const override final
     {
-        if (!m_static_rtree.get() || CURRENT_TIMESTAMP != m_static_rtree->first)
-        {
-            LoadRTree();
-            BOOST_ASSERT(m_geospatial_query.get());
-        }
+        BOOST_ASSERT(m_geospatial_query.get());
 
         return m_geospatial_query->NearestPhantomNodeWithAlternativeFromBigComponent(
             input_coordinate);
@@ -581,13 +543,9 @@ class SharedDataFacade final : public BaseDataFacade
 
     std::pair<PhantomNode, PhantomNode>
     NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate,
-                                                      const double max_distance) override final
+                                                      const double max_distance) const override final
     {
-        if (!m_static_rtree.get() || CURRENT_TIMESTAMP != m_static_rtree->first)
-        {
-            LoadRTree();
-            BOOST_ASSERT(m_geospatial_query.get());
-        }
+        BOOST_ASSERT(m_geospatial_query.get());
 
         return m_geospatial_query->NearestPhantomNodeWithAlternativeFromBigComponent(
             input_coordinate, max_distance);
@@ -597,13 +555,9 @@ class SharedDataFacade final : public BaseDataFacade
     NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate,
                                                       const double max_distance,
                                                       const int bearing,
-                                                      const int bearing_range) override final
+                                                      const int bearing_range) const override final
     {
-        if (!m_static_rtree.get() || CURRENT_TIMESTAMP != m_static_rtree->first)
-        {
-            LoadRTree();
-            BOOST_ASSERT(m_geospatial_query.get());
-        }
+        BOOST_ASSERT(m_geospatial_query.get());
 
         return m_geospatial_query->NearestPhantomNodeWithAlternativeFromBigComponent(
             input_coordinate, max_distance, bearing, bearing_range);
@@ -612,13 +566,9 @@ class SharedDataFacade final : public BaseDataFacade
     std::pair<PhantomNode, PhantomNode>
     NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate,
                                                       const int bearing,
-                                                      const int bearing_range) override final
+                                                      const int bearing_range) const override final
     {
-        if (!m_static_rtree.get() || CURRENT_TIMESTAMP != m_static_rtree->first)
-        {
-            LoadRTree();
-            BOOST_ASSERT(m_geospatial_query.get());
-        }
+        BOOST_ASSERT(m_geospatial_query.get());
 
         return m_geospatial_query->NearestPhantomNodeWithAlternativeFromBigComponent(
             input_coordinate, bearing, bearing_range);
@@ -709,7 +659,10 @@ class SharedDataFacade 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;
+    }
 };
 }
 }
diff --git a/include/engine/geospatial_query.hpp b/include/engine/geospatial_query.hpp
index d2e90b8..81678c0 100644
--- a/include/engine/geospatial_query.hpp
+++ b/include/engine/geospatial_query.hpp
@@ -22,7 +22,7 @@ namespace engine
 
 // Implements complex queries on top of an RTree and builds PhantomNodes from it.
 //
-// Only holds a weak reference on the RTree!
+// Only holds a weak reference on the RTree and coordinates!
 template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
 {
     using EdgeData = typename RTreeT::EdgeData;
@@ -31,9 +31,9 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
 
   public:
     GeospatialQuery(RTreeT &rtree_,
-                    std::shared_ptr<CoordinateList> coordinates_,
+                    const CoordinateList &coordinates_,
                     DataFacadeT &datafacade_)
-        : rtree(rtree_), coordinates(std::move(coordinates_)), datafacade(datafacade_)
+        : rtree(rtree_), coordinates(coordinates_), datafacade(datafacade_)
     {
     }
 
@@ -45,7 +45,7 @@ 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)
+    NearestPhantomNodesInRange(const util::Coordinate input_coordinate, const double max_distance) const
     {
         auto results =
             rtree.Nearest(input_coordinate,
@@ -56,7 +56,7 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
                           [this, max_distance, input_coordinate](const std::size_t,
                                                                  const CandidateSegment &segment)
                           {
-                              return checkSegmentDistance(input_coordinate, segment, max_distance);
+                              return CheckSegmentDistance(input_coordinate, segment, max_distance);
                           });
 
         return MakePhantomNodes(input_coordinate, results);
@@ -68,18 +68,18 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
     NearestPhantomNodesInRange(const util::Coordinate input_coordinate,
                                const double max_distance,
                                const int bearing,
-                               const int bearing_range)
+                               const int bearing_range) const
     {
         auto results = rtree.Nearest(
             input_coordinate,
             [this, bearing, bearing_range, max_distance](const CandidateSegment &segment)
             {
-                return checkSegmentBearing(segment, bearing, bearing_range);
+                return CheckSegmentBearing(segment, bearing, bearing_range);
             },
             [this, max_distance, input_coordinate](const std::size_t,
                                                    const CandidateSegment &segment)
             {
-                return checkSegmentDistance(input_coordinate, segment, max_distance);
+                return CheckSegmentDistance(input_coordinate, segment, max_distance);
             });
 
         return MakePhantomNodes(input_coordinate, results);
@@ -91,13 +91,13 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
     NearestPhantomNodes(const util::Coordinate input_coordinate,
                         const unsigned max_results,
                         const int bearing,
-                        const int bearing_range)
+                        const int bearing_range) const
     {
         auto results =
             rtree.Nearest(input_coordinate,
                           [this, bearing, bearing_range](const CandidateSegment &segment)
                           {
-                              return checkSegmentBearing(segment, bearing, bearing_range);
+                              return CheckSegmentBearing(segment, bearing, bearing_range);
                           },
                           [max_results](const std::size_t num_results, const CandidateSegment &)
                           {
@@ -115,19 +115,19 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
                         const unsigned max_results,
                         const double max_distance,
                         const int bearing,
-                        const int bearing_range)
+                        const int bearing_range) const
     {
         auto results =
             rtree.Nearest(input_coordinate,
                           [this, bearing, bearing_range](const CandidateSegment &segment)
                           {
-                              return checkSegmentBearing(segment, bearing, bearing_range);
+                              return CheckSegmentBearing(segment, bearing, bearing_range);
                           },
                           [this, max_distance, max_results, input_coordinate](
                               const std::size_t num_results, const CandidateSegment &segment)
                           {
                               return num_results >= max_results ||
-                                     checkSegmentDistance(input_coordinate, segment, max_distance);
+                                     CheckSegmentDistance(input_coordinate, segment, max_distance);
                           });
 
         return MakePhantomNodes(input_coordinate, results);
@@ -136,7 +136,7 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
     // Returns max_results nearest PhantomNodes.
     // Does not filter by small/big component!
     std::vector<PhantomNodeWithDistance>
-    NearestPhantomNodes(const util::Coordinate input_coordinate, const unsigned max_results)
+    NearestPhantomNodes(const util::Coordinate input_coordinate, const unsigned max_results) const
     {
         auto results =
             rtree.Nearest(input_coordinate,
@@ -157,7 +157,7 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
     std::vector<PhantomNodeWithDistance>
     NearestPhantomNodes(const util::Coordinate input_coordinate,
                         const unsigned max_results,
-                        const double max_distance)
+                        const double max_distance) const
     {
         auto results =
             rtree.Nearest(input_coordinate,
@@ -169,7 +169,7 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
                               const std::size_t num_results, const CandidateSegment &segment)
                           {
                               return num_results >= max_results ||
-                                     checkSegmentDistance(input_coordinate, segment, max_distance);
+                                     CheckSegmentDistance(input_coordinate, segment, max_distance);
                           });
 
         return MakePhantomNodes(input_coordinate, results);
@@ -179,7 +179,7 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
     // a second phantom node is return that is the nearest coordinate in a big component.
     std::pair<PhantomNode, PhantomNode>
     NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate,
-                                                      const double max_distance)
+                                                      const double max_distance) const
     {
         bool has_small_component = false;
         bool has_big_component = false;
@@ -200,7 +200,7 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
              input_coordinate](const std::size_t num_results, const CandidateSegment &segment)
             {
                 return (num_results > 0 && has_big_component) ||
-                       checkSegmentDistance(input_coordinate, segment, max_distance);
+                       CheckSegmentDistance(input_coordinate, segment, max_distance);
             });
 
         if (results.size() == 0)
@@ -216,7 +216,7 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
     // Returns the nearest phantom node. If this phantom node is not from a big component
     // a second phantom node is return that is the nearest coordinate in a big component.
     std::pair<PhantomNode, PhantomNode>
-    NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate)
+    NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate) const
     {
         bool has_small_component = false;
         bool has_big_component = false;
@@ -251,7 +251,7 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
     // Returns the nearest phantom node. If this phantom node is not from a big component
     // a second phantom node is return that is the nearest coordinate in a big component.
     std::pair<PhantomNode, PhantomNode> NearestPhantomNodeWithAlternativeFromBigComponent(
-        const util::Coordinate input_coordinate, const int bearing, const int bearing_range)
+        const util::Coordinate input_coordinate, const int bearing, const int bearing_range) const
     {
         bool has_small_component = false;
         bool has_big_component = false;
@@ -266,7 +266,7 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
 
                 if (use_segment)
                 {
-                    use_directions = checkSegmentBearing(segment, bearing, bearing_range);
+                    use_directions = CheckSegmentBearing(segment, bearing, bearing_range);
                     if (use_directions.first || use_directions.second)
                     {
                         has_big_component = has_big_component || !segment.data.component.is_tiny;
@@ -297,7 +297,7 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
     NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate,
                                                       const double max_distance,
                                                       const int bearing,
-                                                      const int bearing_range)
+                                                      const int bearing_range) const
     {
         bool has_small_component = false;
         bool has_big_component = false;
@@ -312,7 +312,7 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
 
                 if (use_segment)
                 {
-                    use_directions = checkSegmentBearing(segment, bearing, bearing_range);
+                    use_directions = CheckSegmentBearing(segment, bearing, bearing_range);
                     if (use_directions.first || use_directions.second)
                     {
                         has_big_component = has_big_component || !segment.data.component.is_tiny;
@@ -326,7 +326,7 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
              input_coordinate](const std::size_t num_results, const CandidateSegment &segment)
             {
                 return (num_results > 0 && has_big_component) ||
-                       checkSegmentDistance(input_coordinate, segment, max_distance);
+                       CheckSegmentDistance(input_coordinate, segment, max_distance);
             });
 
         if (results.size() == 0)
@@ -360,7 +360,7 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
         double ratio;
         const auto current_perpendicular_distance =
             util::coordinate_calculation::perpendicularDistance(
-                coordinates->at(data.u), coordinates->at(data.v), input_coordinate,
+                coordinates[data.u], coordinates[data.v], input_coordinate,
                 point_on_segment, ratio);
 
         // Find the node-based-edge that this belongs to, and directly
@@ -416,9 +416,9 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
         return transformed;
     }
 
-    bool checkSegmentDistance(const Coordinate input_coordinate,
+    bool CheckSegmentDistance(const Coordinate input_coordinate,
                               const CandidateSegment &segment,
-                              const double max_distance)
+                              const double max_distance) const
     {
         BOOST_ASSERT(segment.data.forward_segment_id.id != SPECIAL_SEGMENTID ||
                      !segment.data.forward_segment_id.enabled);
@@ -432,9 +432,9 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
                max_distance;
     }
 
-    std::pair<bool, bool> checkSegmentBearing(const CandidateSegment &segment,
+    std::pair<bool, bool> CheckSegmentBearing(const CandidateSegment &segment,
                                               const int filter_bearing,
-                                              const int filter_bearing_range)
+                                              const int filter_bearing_range) const
     {
         BOOST_ASSERT(segment.data.forward_segment_id.id != SPECIAL_SEGMENTID ||
                      !segment.data.forward_segment_id.enabled);
@@ -442,7 +442,7 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
                      !segment.data.reverse_segment_id.enabled);
 
         const double forward_edge_bearing = util::coordinate_calculation::bearing(
-            coordinates->at(segment.data.u), coordinates->at(segment.data.v));
+            coordinates[segment.data.u], coordinates[segment.data.v]);
 
         const double backward_edge_bearing = (forward_edge_bearing + 180) > 360
                                                  ? (forward_edge_bearing - 180)
@@ -459,8 +459,8 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
         return std::make_pair(forward_bearing_valid, backward_bearing_valid);
     }
 
-    RTreeT &rtree;
-    const std::shared_ptr<CoordinateList> coordinates;
+    const RTreeT &rtree;
+    const CoordinateList &coordinates;
     DataFacadeT &datafacade;
 };
 }
diff --git a/include/engine/guidance/assemble_leg.hpp b/include/engine/guidance/assemble_leg.hpp
index 6f8e6cb..864fc26 100644
--- a/include/engine/guidance/assemble_leg.hpp
+++ b/include/engine/guidance/assemble_leg.hpp
@@ -34,6 +34,7 @@ struct NamedSegment
 };
 
 template <std::size_t SegmentNumber>
+
 std::array<std::uint32_t, SegmentNumber> summarizeRoute(const std::vector<PathData> &route_data)
 {
     // merges segments with same name id
@@ -41,7 +42,14 @@ std::array<std::uint32_t, SegmentNumber> summarizeRoute(const std::vector<PathDa
     {
         auto out = segments.begin();
         auto end = segments.end();
-        for (auto in = segments.begin(); in != end; ++in)
+
+        // Do nothing if we were given an empty array
+        if (out == end)
+        {
+            return end;
+        }
+
+        for (auto in = std::next(out); in != end; ++in)
         {
             if (in->name_id == out->name_id)
             {
@@ -54,7 +62,8 @@ std::array<std::uint32_t, SegmentNumber> summarizeRoute(const std::vector<PathDa
                 *out = *in;
             }
         }
-        return out;
+        BOOST_ASSERT(out != end);
+        return ++out;
     };
 
     std::vector<NamedSegment> segments(route_data.size());
@@ -72,10 +81,19 @@ std::array<std::uint32_t, SegmentNumber> summarizeRoute(const std::vector<PathDa
               });
     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;
+              });
+    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;
+                  return lhs.duration > rhs.duration ||
+                         (lhs.duration == rhs.duration && lhs.position < rhs.position);
               });
 
     // make sure the segments are sorted by position
diff --git a/include/engine/guidance/assemble_steps.hpp b/include/engine/guidance/assemble_steps.hpp
index a32811a..d798f04 100644
--- a/include/engine/guidance/assemble_steps.hpp
+++ b/include/engine/guidance/assemble_steps.hpp
@@ -14,6 +14,7 @@
 #include "util/coordinate_calculation.hpp"
 
 #include <boost/optional.hpp>
+#include <cstddef>
 #include <vector>
 
 namespace osrm
@@ -75,20 +76,30 @@ std::vector<RouteStep> assembleSteps(const DataFacadeT &facade,
         // We need to skip the first segment because it is already covered by the
         // initial start of a route
         int segment_duration = 0;
-        for (const auto &path_point : leg_data)
+
+        // some name changes are not announced in our processing. For these, we have to keep the
+        // first name on the segment
+        auto step_name_id = source_node.name_id;
+        for (std::size_t leg_data_index = 0; leg_data_index < leg_data.size(); ++leg_data_index)
         {
+            const auto &path_point = leg_data[leg_data_index];
             segment_duration += path_point.duration_until_turn;
 
             // all changes to this check have to be matched with assemble_geometry
             if (path_point.turn_instruction.type != extractor::guidance::TurnType::NoTurn)
             {
                 BOOST_ASSERT(segment_duration >= 0);
-                const auto name = facade.GetNameForID(path_point.name_id);
+                const auto name = facade.GetNameForID(step_name_id);
                 const auto distance = leg_geometry.segment_distances[segment_index];
-                steps.push_back(RouteStep{path_point.name_id, name, NO_ROTARY_NAME,
+                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()){
+                    step_name_id = leg_data[leg_data_index + 1].name_id;
+                } else {
+                    step_name_id = target_node.name_id;
+                }
                 maneuver = detail::stepManeuverFromGeometry(path_point.turn_instruction,
                                                             leg_geometry, segment_index);
                 segment_index++;
@@ -98,7 +109,7 @@ 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{target_node.name_id, facade.GetNameForID(target_node.name_id),
+        steps.push_back(RouteStep{step_name_id, facade.GetNameForID(step_name_id),
                                   NO_ROTARY_NAME, duration / 10., distance, target_mode, maneuver,
                                   leg_geometry.FrontIndex(segment_index),
                                   leg_geometry.BackIndex(segment_index) + 1});
@@ -132,7 +143,7 @@ std::vector<RouteStep> assembleSteps(const DataFacadeT &facade,
     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,
+                              final_maneuver, leg_geometry.locations.size() - 1,
                               leg_geometry.locations.size()});
 
     return steps;
diff --git a/include/engine/guidance/post_processing.hpp b/include/engine/guidance/post_processing.hpp
index 32f6ee5..790d5af 100644
--- a/include/engine/guidance/post_processing.hpp
+++ b/include/engine/guidance/post_processing.hpp
@@ -17,6 +17,13 @@ namespace guidance
 // passed as none-reference to modify in-place and move out again
 std::vector<RouteStep> postProcess(std::vector<RouteStep> steps);
 
+// Multiple possible reasons can result in unnecessary/confusing instructions
+// A prime example would be a segregated intersection. Turning around at this
+// intersection would result in two instructions to turn left.
+// Collapsing such turns into a single turn instruction, we give a clearer
+// set of instructionst that is not cluttered by unnecessary turns/name changes.
+std::vector<RouteStep> collapseTurns(std::vector<RouteStep> steps);
+
 // trim initial/final segment of very short length.
 // This function uses in/out parameter passing to modify both steps and geometry in place.
 // We use this method since both steps and geometry are closely coupled logically but
@@ -30,6 +37,9 @@ std::vector<RouteStep> assignRelativeLocations(std::vector<RouteStep> steps,
                                                const PhantomNode &source_node,
                                                const PhantomNode &target_node);
 
+//remove steps invalidated by post-processing
+std::vector<RouteStep> removeNoTurnInstructions(std::vector<RouteStep> steps);
+
 // postProcess will break the connection between the leg geometry
 // for which a segment is supposed to represent exactly the coordinates
 // between routing maneuvers and the route steps itself.
diff --git a/include/engine/guidance/route_step.hpp b/include/engine/guidance/route_step.hpp
index d5950a6..a527f85 100644
--- a/include/engine/guidance/route_step.hpp
+++ b/include/engine/guidance/route_step.hpp
@@ -34,6 +34,11 @@ struct RouteStep
     std::size_t geometry_begin;
     std::size_t geometry_end;
 };
+
+inline RouteStep getInvalidRouteStep()
+{
+    return {0, "", "", 0, 0, TRAVEL_MODE_INACCESSIBLE, getInvalidStepManeuver(), 0, 0};
+}
 }
 }
 }
diff --git a/include/engine/guidance/step_maneuver.hpp b/include/engine/guidance/step_maneuver.hpp
index 5ae70f9..084a999 100644
--- a/include/engine/guidance/step_maneuver.hpp
+++ b/include/engine/guidance/step_maneuver.hpp
@@ -1,8 +1,8 @@
 #ifndef ENGINE_GUIDANCE_STEP_MANEUVER_HPP
 #define ENGINE_GUIDANCE_STEP_MANEUVER_HPP
 
-#include "util/coordinate.hpp"
 #include "extractor/guidance/turn_instruction.hpp"
+#include "util/coordinate.hpp"
 
 #include <cstdint>
 #include <vector>
@@ -21,7 +21,7 @@ enum class WaypointType : std::uint8_t
     Depart,
 };
 
-//A represenetation of intermediate intersections
+// A represenetation of intermediate intersections
 struct IntermediateIntersection
 {
     double duration;
@@ -39,6 +39,18 @@ struct StepManeuver
     unsigned exit;
     std::vector<IntermediateIntersection> intersections;
 };
+
+inline StepManeuver getInvalidStepManeuver()
+{
+    return {util::Coordinate{util::FloatLongitude{0.0}, util::FloatLatitude{0.0}},
+            0,
+            0,
+            extractor::guidance::TurnInstruction::NO_TURN(),
+            WaypointType::None,
+            0,
+            {}};
+}
+
 } // namespace guidance
 } // namespace engine
 } // namespace osrmn
diff --git a/include/engine/guidance/toolkit.hpp b/include/engine/guidance/toolkit.hpp
index e9cbcf5..f3d534f 100644
--- a/include/engine/guidance/toolkit.hpp
+++ b/include/engine/guidance/toolkit.hpp
@@ -1,9 +1,11 @@
-#ifndef OSRM_UTIL_GUIDANCE_TOOLKIT_HPP_
-#define OSRM_UTIL_GUIDANCE_TOOLKIT_HPP_
+#ifndef OSRM_ENGINE_GUIDANCE_TOOLKIT_HPP_
+#define OSRM_ENGINE_GUIDANCE_TOOLKIT_HPP_
 
 #include "extractor/guidance/turn_instruction.hpp"
 #include "util/bearing.hpp"
 
+#include <algorithm>
+
 namespace osrm
 {
 namespace engine
@@ -23,9 +25,12 @@ inline bool entersRoundabout(const extractor::guidance::TurnInstruction instruct
 {
     return (instruction.type == extractor::guidance::TurnType::EnterRoundabout ||
             instruction.type == extractor::guidance::TurnType::EnterRotary ||
+            instruction.type == extractor::guidance::TurnType::EnterRoundaboutIntersection ||
             instruction.type == extractor::guidance::TurnType::EnterRoundaboutAtExit ||
             instruction.type == extractor::guidance::TurnType::EnterRotaryAtExit ||
+            instruction.type == extractor::guidance::TurnType::EnterRoundaboutIntersectionAtExit ||
             instruction.type == extractor::guidance::TurnType::EnterAndExitRoundabout ||
+            instruction.type == extractor::guidance::TurnType::EnterAndExitRotary ||
             instruction.type == extractor::guidance::TurnType::EnterAndExitRotary);
 }
 
@@ -33,8 +38,10 @@ inline bool leavesRoundabout(const extractor::guidance::TurnInstruction instruct
 {
     return (instruction.type == extractor::guidance::TurnType::ExitRoundabout ||
             instruction.type == extractor::guidance::TurnType::ExitRotary ||
+            instruction.type == extractor::guidance::TurnType::ExitRoundaboutIntersection ||
             instruction.type == extractor::guidance::TurnType::EnterAndExitRoundabout ||
-            instruction.type == extractor::guidance::TurnType::EnterAndExitRotary);
+            instruction.type == extractor::guidance::TurnType::EnterAndExitRotary ||
+            instruction.type == extractor::guidance::TurnType::EnterAndExitRoundaboutIntersection);
 }
 
 inline bool staysOnRoundabout(const extractor::guidance::TurnInstruction instruction)
@@ -56,8 +63,14 @@ inline extractor::guidance::DirectionModifier angleToDirectionModifier(const dou
     return extractor::guidance::DirectionModifier::Left;
 }
 
+inline double angularDeviation(const double angle, const double from)
+{
+    const double deviation = std::abs(angle - from);
+    return std::min(360 - deviation, deviation);
+}
+
 } // namespace guidance
 } // namespace engine
 } // namespace osrm
 
-#endif /* OSRM_UTIL_GUIDANCE_TOOLKIT_HPP_ */
+#endif /* OSRM_ENGINE_GUIDANCE_TOOLKIT_HPP_ */
diff --git a/include/engine/hint.hpp b/include/engine/hint.hpp
index 56dfbc4..c39b03c 100644
--- a/include/engine/hint.hpp
+++ b/include/engine/hint.hpp
@@ -63,18 +63,9 @@ struct Hint
     friend std::ostream &operator<<(std::ostream &, const Hint &);
 };
 
-#ifndef _MSC_VER
 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");
-#else
-// PhantomNode is bigger under windows because MSVC does not support bit packing
-static_assert(sizeof(Hint) == 72 + 4, "Hint is bigger than expected");
-constexpr std::size_t ENCODED_HINT_SIZE = 104;
-static_assert(ENCODED_HINT_SIZE / 4 * 3 >= sizeof(Hint),
-              "ENCODED_HINT_SIZE does not match size of Hint");
-#endif
+static_assert(ENCODED_HINT_SIZE / 4 * 3 >= sizeof(Hint), "ENCODED_HINT_SIZE does not match size of Hint");
 }
 }
 
diff --git a/include/engine/map_matching/bayes_classifier.hpp b/include/engine/map_matching/bayes_classifier.hpp
index c77fa84..733d5e4 100644
--- a/include/engine/map_matching/bayes_classifier.hpp
+++ b/include/engine/map_matching/bayes_classifier.hpp
@@ -23,7 +23,7 @@ struct NormalDistribution
     }
 
     // FIXME implement log-probability version since it's faster
-    double density_function(const double val) const
+    double Density(const double val) const
     {
         using namespace boost::math::constants;
 
@@ -44,7 +44,7 @@ struct LaplaceDistribution
     }
 
     // FIXME implement log-probability version since it's faster
-    double density_function(const double val) const
+    double Density(const double val) const
     {
         const double x = std::abs(val - location);
         return 1.0 / (2. * scale) * std::exp(-x / scale);
@@ -79,9 +79,9 @@ class BayesClassifier
     ClassificationT classify(const ValueT &v) const
     {
         const double positive_postpriori =
-            positive_apriori_probability * positive_distribution.density_function(v);
+            positive_apriori_probability * positive_distribution.Density(v);
         const double negative_postpriori =
-            negative_apriori_probability * negative_distribution.density_function(v);
+            negative_apriori_probability * negative_distribution.Density(v);
         const double norm = positive_postpriori + negative_postpriori;
 
         if (positive_postpriori > negative_postpriori)
diff --git a/include/engine/map_matching/hidden_markov_model.hpp b/include/engine/map_matching/hidden_markov_model.hpp
index 9b36343..6957240 100644
--- a/include/engine/map_matching/hidden_markov_model.hpp
+++ b/include/engine/map_matching/hidden_markov_model.hpp
@@ -82,10 +82,10 @@ template <class CandidateLists> struct HiddenMarkovModel
             }
         }
 
-        clear(0);
+        Clear(0);
     }
 
-    void clear(std::size_t initial_timestamp)
+    void Clear(std::size_t initial_timestamp)
     {
         BOOST_ASSERT(viterbi.size() == parents.size() && parents.size() == path_distances.size() &&
                      path_distances.size() == pruned.size() && pruned.size() == breakage.size());
diff --git a/include/engine/phantom_node.hpp b/include/engine/phantom_node.hpp
index 88724b1..7f8c29d 100644
--- a/include/engine/phantom_node.hpp
+++ b/include/engine/phantom_node.hpp
@@ -149,13 +149,11 @@ struct PhantomNode
     unsigned reverse_packed_geometry_id;
     struct ComponentType
     {
-        uint32_t id : 31;
-        bool is_tiny : 1;
+        std::uint32_t id         : 31;
+        std::uint32_t is_tiny    : 1;
     } component;
-// bit-fields are broken on Windows
-#ifndef _MSC_VER
     static_assert(sizeof(ComponentType) == 4, "ComponentType needs to be 4 bytes big");
-#endif
+
     util::Coordinate location;
     util::Coordinate input_location;
     unsigned short fwd_segment_position;
@@ -165,11 +163,7 @@ struct PhantomNode
     extractor::TravelMode backward_travel_mode;
 };
 
-#ifndef _MSC_VER
 static_assert(sizeof(PhantomNode) == 60, "PhantomNode has more padding then expected");
-#else
-static_assert(sizeof(PhantomNode) == 72, "PhantomNode has more padding then expected");
-#endif
 
 using PhantomNodePair = std::pair<PhantomNode, PhantomNode>;
 
diff --git a/include/engine/plugins/trip.hpp b/include/engine/plugins/trip.hpp
index cf36884..0e97ce7 100644
--- a/include/engine/plugins/trip.hpp
+++ b/include/engine/plugins/trip.hpp
@@ -35,7 +35,6 @@ class TripPlugin final : public BasePlugin
     int max_locations_trip;
 
     InternalRouteResult ComputeRoute(const std::vector<PhantomNode> &phantom_node_list,
-                                     const api::TripParameters &parameters,
                                      const std::vector<NodeID> &trip);
 
   public:
diff --git a/include/engine/routing_algorithms/map_matching.hpp b/include/engine/routing_algorithms/map_matching.hpp
index 0b1020a..e64ea4f 100644
--- a/include/engine/routing_algorithms/map_matching.hpp
+++ b/include/engine/routing_algorithms/map_matching.hpp
@@ -206,7 +206,7 @@ class MapMatching final : public BasicRoutingInterface<DataFacadeT, MapMatching<
                 split_points.push_back(split_index);
 
                 // note: this preserves everything before split_index
-                model.clear(split_index);
+                model.Clear(split_index);
                 std::size_t new_start = model.initialize(split_index);
                 // no new start was found -> stop viterbi calculation
                 if (new_start == map_matching::INVALID_STATE)
diff --git a/include/engine/routing_algorithms/routing_base.hpp b/include/engine/routing_algorithms/routing_base.hpp
index 360eb98..deeec64 100644
--- a/include/engine/routing_algorithms/routing_base.hpp
+++ b/include/engine/routing_algorithms/routing_base.hpp
@@ -301,8 +301,6 @@ template <class DataFacadeT, class Derived> class BasicRoutingInterface
                 auto total_weight = std::accumulate(weight_vector.begin(), weight_vector.end(), 0);
 
                 BOOST_ASSERT(weight_vector.size() == id_vector.size());
-                // ed.distance should be total_weight + penalties (turn, stop, etc)
-                BOOST_ASSERT(ed.distance >= total_weight);
                 const bool is_first_segment = unpacked_path.empty();
 
                 const std::size_t start_index =
@@ -350,7 +348,8 @@ template <class DataFacadeT, class Derived> class BasicRoutingInterface
                 start_index =
                     id_vector.size() - phantom_node_pair.source_phantom.fwd_segment_position - 1;
             }
-            end_index = id_vector.size() - phantom_node_pair.target_phantom.fwd_segment_position - 1;
+            end_index =
+                id_vector.size() - phantom_node_pair.target_phantom.fwd_segment_position - 1;
         }
         else
         {
@@ -396,8 +395,17 @@ template <class DataFacadeT, class Derived> class BasicRoutingInterface
             // However the first segment duration needs to be adjusted to the fact that the source
             // phantom is in the middle of the segment. We do this by subtracting v--s from the
             // duration.
-            BOOST_ASSERT(unpacked_path.front().duration_until_turn >= source_weight);
-            unpacked_path.front().duration_until_turn -= source_weight;
+
+            // Since it's possible duration_until_turn can be less than source_weight here if
+            // a negative enough turn penalty is used to modify this edge weight during
+            // osrm-contract, we clamp to 0 here so as not to return a negative duration
+            // for this segment.
+
+            // TODO this creates a scenario where it's possible the duration from a phantom
+            // node to the first turn would be the same as from end to end of a segment,
+            // which is obviously incorrect and not ideal...
+            unpacked_path.front().duration_until_turn =
+                std::max(unpacked_path.front().duration_until_turn - source_weight, 0);
         }
 
         // there is no equivalent to a node-based node in an edge-expanded graph.
@@ -771,18 +779,50 @@ template <class DataFacadeT, class Derived> class BasicRoutingInterface
         nodes.target_phantom = target_phantom;
         UnpackPath(packed_path.begin(), packed_path.end(), nodes, unpacked_path);
 
-        util::Coordinate previous_coordinate = source_phantom.location;
-        util::Coordinate current_coordinate;
+        using util::coordinate_calculation::detail::DEGREE_TO_RAD;
+        using util::coordinate_calculation::detail::EARTH_RADIUS;
+
         double distance = 0;
+        double prev_lat =
+            static_cast<double>(toFloating(source_phantom.location.lat)) * DEGREE_TO_RAD;
+        double prev_lon =
+            static_cast<double>(toFloating(source_phantom.location.lon)) * DEGREE_TO_RAD;
+        double prev_cos = std::cos(prev_lat);
         for (const auto &p : unpacked_path)
         {
-            current_coordinate = facade->GetCoordinateOfNode(p.turn_via_node);
-            distance += util::coordinate_calculation::haversineDistance(previous_coordinate,
-                                                                        current_coordinate);
-            previous_coordinate = current_coordinate;
+            const auto current_coordinate = facade->GetCoordinateOfNode(p.turn_via_node);
+
+            const double current_lat =
+                static_cast<double>(toFloating(current_coordinate.lat)) * DEGREE_TO_RAD;
+            const double current_lon =
+                static_cast<double>(toFloating(current_coordinate.lon)) * DEGREE_TO_RAD;
+            const double current_cos = std::cos(current_lat);
+
+            const double sin_dlon = std::sin((prev_lon - current_lon) / 2.0);
+            const double sin_dlat = std::sin((prev_lat - current_lat) / 2.0);
+
+            const double aharv = sin_dlat * sin_dlat + prev_cos * current_cos * sin_dlon * sin_dlon;
+            const double charv = 2. * std::atan2(std::sqrt(aharv), std::sqrt(1.0 - aharv));
+            distance += EARTH_RADIUS * charv;
+
+            prev_lat = current_lat;
+            prev_lon = current_lon;
+            prev_cos = current_cos;
         }
-        distance += util::coordinate_calculation::haversineDistance(previous_coordinate,
-                                                                    target_phantom.location);
+
+        const double current_lat =
+            static_cast<double>(toFloating(target_phantom.location.lat)) * DEGREE_TO_RAD;
+        const double current_lon =
+            static_cast<double>(toFloating(target_phantom.location.lon)) * DEGREE_TO_RAD;
+        const double current_cos = std::cos(current_lat);
+
+        const double sin_dlon = std::sin((prev_lon - current_lon) / 2.0);
+        const double sin_dlat = std::sin((prev_lat - current_lat) / 2.0);
+
+        const double aharv = sin_dlat * sin_dlat + prev_cos * current_cos * sin_dlon * sin_dlon;
+        const double charv = 2. * std::atan2(std::sqrt(aharv), std::sqrt(1.0 - aharv));
+        distance += EARTH_RADIUS * charv;
+
         return distance;
     }
 
diff --git a/include/engine/routing_algorithms/shortest_path.hpp b/include/engine/routing_algorithms/shortest_path.hpp
index 7b8fd7c..cc612a1 100644
--- a/include/engine/routing_algorithms/shortest_path.hpp
+++ b/include/engine/routing_algorithms/shortest_path.hpp
@@ -57,15 +57,13 @@ class ShortestPathRouting final
         if (search_from_forward_node)
         {
             forward_heap.Insert(source_phantom.forward_segment_id.id,
-                                total_distance_to_forward -
-                                    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,
-                                total_distance_to_reverse -
-                                    source_phantom.GetReverseWeightPlusOffset(),
+                                    -source_phantom.GetReverseWeightPlusOffset(),
                                 source_phantom.reverse_segment_id.id);
         }
         if (search_to_forward_node)
@@ -107,6 +105,7 @@ class ShortestPathRouting final
             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);
     }
 
     // searches shortest path between:
diff --git a/include/engine/trip/trip_tabu_search.hpp b/include/engine/trip/trip_tabu_search.hpp
deleted file mode 100644
index 24ff745..0000000
--- a/include/engine/trip/trip_tabu_search.hpp
+++ /dev/null
@@ -1,41 +0,0 @@
-#ifndef TRIP_BRUTE_FORCE_HPP
-#define TRIP_BRUTE_FORCE_HPP
-
-#include "engine/search_engine.hpp"
-#include "util/simple_logger.hpp"
-
-#include "osrm/json_container.hpp"
-
-#include <cstdlib>
-#include <algorithm>
-#include <string>
-#include <vector>
-#include <limits>
-
-namespace osrm
-{
-namespace engine
-{
-namespace trip
-{
-
-// todo: yet to be implemented
-void TabuSearchTrip(std::vector<unsigned> &location,
-                    const PhantomNodeArray &phantom_node_vector,
-                    const std::vector<EdgeWeight> &dist_table,
-                    InternalRouteResult &min_route,
-                    std::vector<int> &min_loc_permutation)
-{
-}
-
-void TabuSearchTrip(const PhantomNodeArray &phantom_node_vector,
-                    const std::vector<EdgeWeight> &dist_table,
-                    InternalRouteResult &min_route,
-                    std::vector<int> &min_loc_permutation)
-{
-}
-}
-}
-}
-
-#endif // TRIP_BRUTE_FORCE_HPP
diff --git a/include/extractor/compressed_edge_container.hpp b/include/extractor/compressed_edge_container.hpp
index 662c18e..9623d53 100644
--- a/include/extractor/compressed_edge_container.hpp
+++ b/include/extractor/compressed_edge_container.hpp
@@ -40,7 +40,9 @@ class CompressedEdgeContainer
     void SerializeInternalVector(const std::string &path) const;
     unsigned GetPositionForID(const EdgeID edge_id) const;
     const EdgeBucket &GetBucketReference(const EdgeID edge_id) const;
+    bool IsTrivial(const EdgeID edge_id) const;
     NodeID GetFirstEdgeTargetID(const EdgeID edge_id) const;
+    NodeID GetLastEdgeTargetID(const EdgeID edge_id) const;
     NodeID GetLastEdgeSourceID(const EdgeID edge_id) const;
 
   private:
diff --git a/include/extractor/guidance/constants.hpp b/include/extractor/guidance/constants.hpp
index 0418682..3e4ae6d 100644
--- a/include/extractor/guidance/constants.hpp
+++ b/include/extractor/guidance/constants.hpp
@@ -16,12 +16,13 @@ const double constexpr STRAIGHT_ANGLE = 180.;
 const double constexpr MAXIMAL_ALLOWED_NO_TURN_DEVIATION = 3.;
 // angle that lies between two nearly indistinguishable roads
 const double constexpr NARROW_TURN_ANGLE = 40.;
-const double constexpr GROUP_ANGLE = 90;
+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 = 15.;
+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_RADIUS = 15; // 30 m diameter as final distinction
 const double constexpr INCREASES_BY_FOURTY_PERCENT = 1.4;
 
diff --git a/include/extractor/guidance/intersection_handler.hpp b/include/extractor/guidance/intersection_handler.hpp
index 25d2f4a..f1d4981 100644
--- a/include/extractor/guidance/intersection_handler.hpp
+++ b/include/extractor/guidance/intersection_handler.hpp
@@ -3,6 +3,7 @@
 
 #include "extractor/guidance/intersection.hpp"
 #include "extractor/query_node.hpp"
+#include "extractor/suffix_table.hpp"
 
 #include "util/name_table.hpp"
 #include "util/node_based_graph.hpp"
@@ -26,7 +27,8 @@ class IntersectionHandler
   public:
     IntersectionHandler(const util::NodeBasedDynamicGraph &node_based_graph,
                         const std::vector<QueryNode> &node_info_list,
-                        const util::NameTable &name_table);
+                        const util::NameTable &name_table,
+                        const SuffixTable &street_name_suffix_table);
     virtual ~IntersectionHandler();
 
     // check whether the handler can actually handle the intersection
@@ -41,6 +43,7 @@ class IntersectionHandler
     const util::NodeBasedDynamicGraph &node_based_graph;
     const std::vector<QueryNode> &node_info_list;
     const util::NameTable &name_table;
+    const SuffixTable &street_name_suffix_table;
 
     // counts the number on allowed entry roads
     std::size_t countValid(const Intersection &intersection) const;
@@ -61,6 +64,12 @@ class IntersectionHandler
                     ConnectedRoad &center,
                     ConnectedRoad &right) const;
 
+    // Trivial Turns use findBasicTurnType and getTurnDirection as only criteria
+    void assignTrivialTurns(const EdgeID via_eid,
+                            Intersection &intersection,
+                            const std::size_t begin,
+                            const std::size_t end) const;
+
     bool isThroughStreet(const std::size_t index, const Intersection &intersection) const;
 };
 
diff --git a/include/extractor/guidance/intersection_scenario_three_way.hpp b/include/extractor/guidance/intersection_scenario_three_way.hpp
new file mode 100644
index 0000000..d611f91
--- /dev/null
+++ b/include/extractor/guidance/intersection_scenario_three_way.hpp
@@ -0,0 +1,27 @@
+#ifndef OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_SCENARIO_THREE_WAY_HPP_
+#define OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_SCENARIO_THREE_WAY_HPP_
+
+#include "extractor/guidance/intersection.hpp"
+
+namespace osrm
+{
+namespace extractor
+{
+namespace guidance
+{
+
+// possible fork
+bool isFork(const ConnectedRoad &uturn,
+            const ConnectedRoad &possible_right_fork,
+            const ConnectedRoad &possible_left_fork);
+
+// Ending in a T-Intersection
+bool isEndOfRoad(const ConnectedRoad &uturn,
+                 const ConnectedRoad &possible_right_turn,
+                 const ConnectedRoad &possible_left_turn);
+
+} // namespace guidance
+} // namespace extractor
+} // namespace osrm
+
+#endif /*OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_SCENARIO_THREE_WAY_HPP_*/
diff --git a/include/extractor/guidance/motorway_handler.hpp b/include/extractor/guidance/motorway_handler.hpp
index e3d9e34..42805c5 100644
--- a/include/extractor/guidance/motorway_handler.hpp
+++ b/include/extractor/guidance/motorway_handler.hpp
@@ -1,8 +1,8 @@
 #ifndef OSRM_EXTRACTOR_GUIDANCE_MOTORWAY_HANDLER_HPP_
 #define OSRM_EXTRACTOR_GUIDANCE_MOTORWAY_HANDLER_HPP_
 
-#include "extractor/guidance/intersection_handler.hpp"
 #include "extractor/guidance/intersection.hpp"
+#include "extractor/guidance/intersection_handler.hpp"
 #include "extractor/query_node.hpp"
 
 #include "util/name_table.hpp"
@@ -24,7 +24,8 @@ class MotorwayHandler : public IntersectionHandler
   public:
     MotorwayHandler(const util::NodeBasedDynamicGraph &node_based_graph,
                     const std::vector<QueryNode> &node_info_list,
-                    const util::NameTable &name_table);
+                    const util::NameTable &name_table,
+                    const SuffixTable &street_name_suffix_table);
     ~MotorwayHandler() override final;
 
     // check whether the handler can actually handle the intersection
diff --git a/include/extractor/guidance/roundabout_handler.hpp b/include/extractor/guidance/roundabout_handler.hpp
index 6a48387..055982a 100644
--- a/include/extractor/guidance/roundabout_handler.hpp
+++ b/include/extractor/guidance/roundabout_handler.hpp
@@ -1,14 +1,17 @@
 #ifndef OSRM_EXTRACTOR_GUIDANCE_ROUNDABOUT_HANDLER_HPP_
 #define OSRM_EXTRACTOR_GUIDANCE_ROUNDABOUT_HANDLER_HPP_
 
+#include "extractor/compressed_edge_container.hpp"
 #include "extractor/guidance/intersection.hpp"
 #include "extractor/guidance/intersection_handler.hpp"
+#include "extractor/guidance/roundabout_type.hpp"
 #include "extractor/query_node.hpp"
 
 #include "util/name_table.hpp"
 #include "util/node_based_graph.hpp"
 #include "util/typedefs.hpp"
 
+#include <set>
 #include <utility>
 #include <vector>
 
@@ -37,31 +40,47 @@ class RoundaboutHandler : public IntersectionHandler
   public:
     RoundaboutHandler(const util::NodeBasedDynamicGraph &node_based_graph,
                       const std::vector<QueryNode> &node_info_list,
-                      const util::NameTable &name_table);
+                      const CompressedEdgeContainer &compressed_edge_container,
+                      const util::NameTable &name_table,
+                      const SuffixTable &street_name_suffix_table);
 
     ~RoundaboutHandler() override final;
 
     // check whether the handler can actually handle the intersection
-    bool canProcess(const NodeID from_nid, const EdgeID via_eid, const Intersection &intersection) const override final;
+    bool canProcess(const NodeID from_nid,
+                    const EdgeID via_eid,
+                    const Intersection &intersection) const override final;
 
     // process the intersection
-    Intersection operator()(const NodeID from_nid, const EdgeID via_eid, Intersection intersection) const override final;
+    Intersection operator()(const NodeID from_nid,
+                            const EdgeID via_eid,
+                            Intersection intersection) const override final;
 
   private:
-    detail::RoundaboutFlags getRoundaboutFlags(const NodeID from_nid, const EdgeID via_eid, const Intersection &intersection) const;
+    detail::RoundaboutFlags getRoundaboutFlags(const NodeID from_nid,
+                                               const EdgeID via_eid,
+                                               const Intersection &intersection) const;
+
+    void invalidateExitAgainstDirection(const NodeID from_nid,
+                                        const EdgeID via_eid,
+                                        Intersection &intersection) const;
 
     // decide whether we lookk at a roundabout or a rotary
-    bool isRotary(const NodeID nid) const;
+    RoundaboutType getRoundaboutType(const NodeID nid) const;
 
     // TODO handle bike/walk cases that allow crossing a roundabout!
     // Processing of roundabouts
     // Produces instructions to enter/exit a roundabout or to stay on it.
     // Performs the distinction between roundabout and rotaries.
-    Intersection handleRoundabouts(const bool is_rotary,
+    Intersection handleRoundabouts(const RoundaboutType roundabout_type,
                                    const EdgeID via_edge,
                                    const bool on_roundabout,
                                    const bool can_exit_roundabout,
                                    Intersection intersection) const;
+
+    bool qualifiesAsRoundaboutIntersection(const std::set<NodeID> &roundabout_nodes) const;
+
+    const CompressedEdgeContainer &compressed_edge_container;
 };
 
 } // namespace guidance
diff --git a/include/extractor/guidance/roundabout_type.hpp b/include/extractor/guidance/roundabout_type.hpp
new file mode 100644
index 0000000..1cbb22d
--- /dev/null
+++ b/include/extractor/guidance/roundabout_type.hpp
@@ -0,0 +1,21 @@
+#ifndef OSRM_EXTRACTOR_GUIDANCE_ROUNDABOUT_TYPES_HPP_
+#define OSRM_EXTRACTOR_GUIDANCE_ROUNDABOUT_TYPES_HPP_
+
+namespace osrm
+{
+namespace extractor
+{
+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
+};
+} /* namespace guidance */
+} /* namespace extractor */
+} /* namespace osrm */
+
+#endif /* OSRM_EXTRACTOR_GUIDANCE_ROUNDABOUT_TYPES_HPP_ */
diff --git a/include/extractor/guidance/toolkit.hpp b/include/extractor/guidance/toolkit.hpp
index 35fd547..f7d0f61 100644
--- a/include/extractor/guidance/toolkit.hpp
+++ b/include/extractor/guidance/toolkit.hpp
@@ -4,12 +4,15 @@
 #include "util/bearing.hpp"
 #include "util/coordinate.hpp"
 #include "util/coordinate_calculation.hpp"
+#include "util/guidance/toolkit.hpp"
 
 #include "extractor/compressed_edge_container.hpp"
 #include "extractor/query_node.hpp"
+#include "extractor/suffix_table.hpp"
 
 #include "extractor/guidance/classification_data.hpp"
 #include "extractor/guidance/discrete_angle.hpp"
+#include "extractor/guidance/intersection.hpp"
 #include "extractor/guidance/turn_instruction.hpp"
 
 #include <algorithm>
@@ -17,6 +20,10 @@
 #include <cstdint>
 #include <map>
 #include <string>
+#include <utility>
+
+#include <boost/algorithm/string.hpp>
+#include <boost/algorithm/string/predicate.hpp>
 
 namespace osrm
 {
@@ -25,6 +32,8 @@ namespace extractor
 namespace guidance
 {
 
+using util::guidance::angularDeviation;
+
 namespace detail
 {
 const constexpr double DESIRED_SEGMENT_LENGTH = 10.0;
@@ -94,7 +103,7 @@ getCoordinateFromCompressedRange(util::Coordinate current_coordinate,
 }
 } // namespace detail
 
-// Finds a (potentially inteprolated) coordinate that is DESIRED_SEGMENT_LENGTH away
+// Finds a (potentially interpolated) coordinate that is DESIRED_SEGMENT_LENGTH away
 // from the start of an edge
 inline util::Coordinate
 getRepresentativeCoordinate(const NodeID from_node,
@@ -245,12 +254,6 @@ inline double angleFromDiscreteAngle(const DiscreteAngle angle)
     return static_cast<double>(angle) * detail::discrete_angle_step_size;
 }
 
-inline double angularDeviation(const double angle, const double from)
-{
-    const double deviation = std::abs(angle - from);
-    return std::min(360 - deviation, deviation);
-}
-
 inline double getAngularPenalty(const double angle, DirectionModifier modifier)
 {
     // these are not aligned with getTurnDirection but represent an ideal center
@@ -271,30 +274,6 @@ inline double getTurnConfidence(const double angle, TurnInstruction instruction)
     return 1.0 - (difference / max_deviation) * (difference / max_deviation);
 }
 
-// Translates between angles and their human-friendly directional representation
-inline DirectionModifier getTurnDirection(const double angle)
-{
-    // An angle of zero is a u-turn
-    // 180 goes perfectly straight
-    // 0-180 are right turns
-    // 180-360 are left turns
-    if (angle > 0 && angle < 60)
-        return DirectionModifier::SharpRight;
-    if (angle >= 60 && angle < 140)
-        return DirectionModifier::Right;
-    if (angle >= 140 && angle < 170)
-        return DirectionModifier::SlightRight;
-    if (angle >= 165 && angle <= 195)
-        return DirectionModifier::Straight;
-    if (angle > 190 && angle <= 220)
-        return DirectionModifier::SlightLeft;
-    if (angle > 220 && angle <= 300)
-        return DirectionModifier::Left;
-    if (angle > 300 && angle < 360)
-        return DirectionModifier::SharpLeft;
-    return DirectionModifier::UTurn;
-}
-
 // swaps left <-> right modifier types
 inline DirectionModifier mirrorDirectionModifier(const DirectionModifier modifier)
 {
@@ -329,8 +308,27 @@ inline bool isDistinct(const DirectionModifier first, const DirectionModifier se
     return true;
 }
 
-inline bool requiresNameAnnounced(const std::string &from, const std::string &to)
+inline std::pair<std::string, std::string> getPrefixAndSuffix(const std::string &data)
+{
+    const auto suffix_pos = data.find_last_of(' ');
+    if (suffix_pos == std::string::npos)
+        return {};
+
+    const auto prefix_pos = data.find_first_of(' ');
+    auto result = std::make_pair(data.substr(0, prefix_pos), data.substr(suffix_pos + 1));
+    boost::to_lower(result.first);
+    boost::to_lower(result.second);
+    return result;
+}
+
+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())
+        return true;
+
     // FIXME, handle in profile to begin with?
     // this uses the encoding of references in the profile, which is very BAD
     // Input for this function should be a struct separating streetname, suffix (e.g. road,
@@ -361,7 +359,43 @@ inline bool requiresNameAnnounced(const std::string &from, const std::string &to
 
     // check similarity of names
     const auto names_are_empty = from_name.empty() && to_name.empty();
-    const auto names_are_equal = from_name == to_name;
+    const auto name_is_contained =
+        boost::starts_with(from_name, to_name) || boost::starts_with(to_name, from_name);
+
+    const auto checkForPrefixOrSuffixChange =
+        [](const std::string &first, const std::string &second, const SuffixTable &suffix_table) {
+
+            const auto first_prefix_and_suffixes = getPrefixAndSuffix(first);
+            const auto second_prefix_and_suffixes = getPrefixAndSuffix(second);
+            // reverse strings, get suffices and reverse them to get prefixes
+            const auto checkTable = [&](const std::string str) {
+                return str.empty() || suffix_table.isSuffix(str);
+            };
+
+            const bool is_prefix_change = [&]() -> bool {
+                if (!checkTable(first_prefix_and_suffixes.first))
+                    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);
+            }();
+
+            const bool is_suffix_change = [&]() -> bool {
+                if (!checkTable(first_prefix_and_suffixes.second))
+                    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 is_prefix_change || is_suffix_change;
+        };
+
+    const auto is_suffix_change = checkForPrefixOrSuffixChange(from_name, to_name, suffix_table);
+    const auto names_are_equal = from_name == to_name || name_is_contained || is_suffix_change;
     const auto name_is_removed = !from_name.empty() && to_name.empty();
     // references are contained in one another
     const auto refs_are_empty = from_ref.empty() && to_ref.empty();
@@ -370,9 +404,10 @@ inline bool requiresNameAnnounced(const std::string &from, const std::string &to
         (from_ref.find(to_ref) != std::string::npos || to_ref.find(from_ref) != std::string::npos);
     const auto ref_is_removed = !from_ref.empty() && to_ref.empty();
 
-    const auto obvious_change =
-        (names_are_empty && refs_are_empty) || (names_are_equal && ref_is_contained) ||
-        (names_are_equal && refs_are_empty) || name_is_removed || ref_is_removed;
+    const auto obvious_change = (names_are_empty && refs_are_empty) ||
+                                (names_are_equal && ref_is_contained) ||
+                                (names_are_equal && refs_are_empty) || name_is_removed ||
+                                ref_is_removed || is_suffix_change;
 
     return !obvious_change;
 }
@@ -397,6 +432,25 @@ inline bool canBeSeenAsFork(const FunctionalRoadClass first, const FunctionalRoa
     return std::abs(getPriority(first) - getPriority(second)) <= 1;
 }
 
+// To simplify handling of Left/Right hand turns, we can mirror turns and write an intersection
+// handler only for one side. The mirror function turns a left-hand turn in a equivalent right-hand
+// 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};
+
+    if (angularDeviation(road.turn.angle, 0) > std::numeric_limits<double>::epsilon())
+    {
+        road.turn.angle = 360 - road.turn.angle;
+        road.turn.instruction.direction_modifier =
+            mirrored_modifiers[road.turn.instruction.direction_modifier];
+    }
+    return road;
+}
+
 } // namespace guidance
 } // namespace extractor
 } // namespace osrm
diff --git a/include/extractor/guidance/turn_analysis.hpp b/include/extractor/guidance/turn_analysis.hpp
index f52d1b2..3f202f3 100644
--- a/include/extractor/guidance/turn_analysis.hpp
+++ b/include/extractor/guidance/turn_analysis.hpp
@@ -4,13 +4,14 @@
 #include "extractor/compressed_edge_container.hpp"
 #include "extractor/guidance/intersection.hpp"
 #include "extractor/guidance/intersection_generator.hpp"
+#include "extractor/guidance/motorway_handler.hpp"
+#include "extractor/guidance/roundabout_handler.hpp"
 #include "extractor/guidance/toolkit.hpp"
 #include "extractor/guidance/turn_classification.hpp"
-#include "extractor/guidance/roundabout_handler.hpp"
-#include "extractor/guidance/motorway_handler.hpp"
 #include "extractor/guidance/turn_handler.hpp"
 #include "extractor/query_node.hpp"
 #include "extractor/restriction_map.hpp"
+#include "extractor/suffix_table.hpp"
 
 #include "util/name_table.hpp"
 #include "util/node_based_graph.hpp"
@@ -39,7 +40,8 @@ class TurnAnalysis
                  const RestrictionMap &restriction_map,
                  const std::unordered_set<NodeID> &barrier_nodes,
                  const CompressedEdgeContainer &compressed_edge_container,
-                 const util::NameTable &name_table);
+                 const util::NameTable &name_table,
+                 const SuffixTable &street_name_suffix_table);
 
     // the entry into the turn analysis
     std::vector<TurnOperation> getTurns(const NodeID from_node, const EdgeID via_eid) const;
diff --git a/include/extractor/guidance/turn_classification.hpp b/include/extractor/guidance/turn_classification.hpp
index 58f7666..6cf7a08 100644
--- a/include/extractor/guidance/turn_classification.hpp
+++ b/include/extractor/guidance/turn_classification.hpp
@@ -3,9 +3,9 @@
 
 #include "extractor/guidance/toolkit.hpp"
 
-#include "util/typedefs.hpp"
 #include "util/coordinate.hpp"
 #include "util/node_based_graph.hpp"
+#include "util/typedefs.hpp"
 
 #include "extractor/compressed_edge_container.hpp"
 #include "extractor/query_node.hpp"
@@ -84,8 +84,7 @@ classifyIntersection(NodeID nid,
     }
 
     std::sort(turns.begin(), turns.end(),
-              [](const TurnPossibility left, const TurnPossibility right)
-              {
+              [](const TurnPossibility left, const TurnPossibility right) {
                   return left.angle < right.angle;
               });
 
diff --git a/include/extractor/guidance/turn_handler.hpp b/include/extractor/guidance/turn_handler.hpp
index 3420aa7..e49e7e0 100644
--- a/include/extractor/guidance/turn_handler.hpp
+++ b/include/extractor/guidance/turn_handler.hpp
@@ -1,16 +1,16 @@
 #ifndef OSRM_EXTRACTOR_GUIDANCE_TURN_HANDLER_HPP_
 #define OSRM_EXTRACTOR_GUIDANCE_TURN_HANDLER_HPP_
 
-#include "extractor/guidance/intersection_handler.hpp"
 #include "extractor/guidance/intersection.hpp"
+#include "extractor/guidance/intersection_handler.hpp"
 #include "extractor/query_node.hpp"
 
 #include "util/name_table.hpp"
 #include "util/node_based_graph.hpp"
 
 #include <cstddef>
-#include <vector>
 #include <utility>
+#include <vector>
 
 namespace osrm
 {
@@ -26,7 +26,8 @@ class TurnHandler : public IntersectionHandler
   public:
     TurnHandler(const util::NodeBasedDynamicGraph &node_based_graph,
                 const std::vector<QueryNode> &node_info_list,
-                const util::NameTable &name_table);
+                const util::NameTable &name_table,
+                const SuffixTable &street_name_suffix_table);
     ~TurnHandler() override final;
 
     // check whether the handler can actually handle the intersection
diff --git a/include/extractor/guidance/turn_instruction.hpp b/include/extractor/guidance/turn_instruction.hpp
index d96c007..f833ef6 100644
--- a/include/extractor/guidance/turn_instruction.hpp
+++ b/include/extractor/guidance/turn_instruction.hpp
@@ -5,6 +5,8 @@
 
 #include <boost/assert.hpp>
 
+#include "extractor/guidance/roundabout_type.hpp"
+
 namespace osrm
 {
 namespace extractor
@@ -36,35 +38,31 @@ enum DirectionModifier
 // enum class TurnType : unsigned char
 enum TurnType // at the moment we can support 32 turn types, without increasing memory consumption
 {
-    Invalid,                // no valid turn instruction
-    NoTurn,                 // end of segment without turn/middle of a segment
-    Suppressed,             // location that suppresses a turn
-    NewName,                // no turn, but name changes
-    Continue,               // remain on a street
-    Turn,                   // basic turn
-    FirstTurn,              // First of x turns
-    SecondTurn,             // Second of x turns
-    ThirdTurn,              // Third of x turns
-    FourthTurn,             // Fourth of x turns
-    Merge,                  // merge onto a street
-    Ramp,                   // special turn (highway ramp exits)
-    FirstRamp,              // first turn onto a ramp
-    SecondRamp,             // second turn onto a ramp
-    ThirdRamp,              // third turn onto a ramp
-    FourthRamp,             // fourth turn onto a ramp
-    Fork,                   // fork road splitting up
-    EndOfRoad,              // T intersection
-    EnterRoundabout,        // Entering a small Roundabout
-    EnterRoundaboutAtExit,  // Entering a small Roundabout at a countable exit
-    EnterAndExitRoundabout, // Touching a roundabout
-    ExitRoundabout,         // Exiting a small Roundabout
-    EnterRotary,            // Enter a rotary
-    EnterRotaryAtExit,      // Enter A Rotary at a countable exit
-    EnterAndExitRotary,     // Touching a rotary
-    ExitRotary,             // Exit a rotary
-    StayOnRoundabout,       // Continue on Either a small or a large Roundabout
-    Restriction,            // Cross a Barrier, requires barrier penalties instead of full block
-    Notification            // Travel Mode Changes`
+    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
 };
 
 // turn angle in 1.40625 degree -> 128 == 180 degree
@@ -89,27 +87,45 @@ struct TurnInstruction
         return TurnInstruction(TurnType::NoTurn, DirectionModifier::UTurn);
     }
 
-    static TurnInstruction REMAIN_ROUNDABOUT(bool is_rotary, const DirectionModifier modifier)
+    static TurnInstruction REMAIN_ROUNDABOUT(const RoundaboutType, const DirectionModifier modifier)
     {
-        (void)is_rotary; // staying does not require a different instruction at the moment
         return TurnInstruction(TurnType::StayOnRoundabout, modifier);
     }
 
-    static TurnInstruction ENTER_ROUNDABOUT(bool is_rotary, const DirectionModifier modifier)
+    static TurnInstruction ENTER_ROUNDABOUT(const RoundaboutType roundabout_type,
+                                            const DirectionModifier modifier)
     {
-        return {is_rotary ? TurnType::EnterRotary : TurnType::EnterRoundabout, modifier};
+        const constexpr TurnType enter_instruction[] = {
+            TurnType::Invalid, TurnType::EnterRoundabout, TurnType::EnterRotary,
+            TurnType::EnterRoundaboutIntersection};
+        return {enter_instruction[static_cast<int>(roundabout_type)], modifier};
     }
 
-    static TurnInstruction EXIT_ROUNDABOUT(bool is_rotary, const DirectionModifier modifier)
+    static TurnInstruction EXIT_ROUNDABOUT(const RoundaboutType roundabout_type,
+                                           const DirectionModifier modifier)
     {
-        return {is_rotary ? TurnType::ExitRotary : TurnType::ExitRoundabout, modifier};
+        const constexpr TurnType 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(bool is_rotary,
+    static TurnInstruction ENTER_AND_EXIT_ROUNDABOUT(const RoundaboutType roundabout_type,
                                                      const DirectionModifier modifier)
     {
-        return {is_rotary ? TurnType::EnterAndExitRotary : TurnType::EnterAndExitRoundabout,
-                modifier};
+        const constexpr TurnType 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 constexpr TurnType 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)
diff --git a/include/extractor/raster_source.hpp b/include/extractor/raster_source.hpp
index 4cc7e02..fe56b45 100644
--- a/include/extractor/raster_source.hpp
+++ b/include/extractor/raster_source.hpp
@@ -104,7 +104,7 @@ class RasterSource
     const float xstep;
     const float ystep;
 
-    float calcSize(int min, int max, std::size_t count) const;
+    float CalcSize(int min, int max, std::size_t count) const;
 
   public:
     RasterGrid raster_data;
@@ -116,9 +116,9 @@ class RasterSource
     const int ymin;
     const int ymax;
 
-    RasterDatum getRasterData(const int lon, const int lat) const;
+    RasterDatum GetRasterData(const int lon, const int lat) const;
 
-    RasterDatum getRasterInterpolate(const int lon, const int lat) const;
+    RasterDatum GetRasterInterpolate(const int lon, const int lat) const;
 
     RasterSource(RasterGrid _raster_data,
                  std::size_t width,
@@ -134,7 +134,7 @@ class SourceContainer
   public:
     SourceContainer() = default;
 
-    int loadRasterSource(const std::string &path_string,
+    int LoadRasterSource(const std::string &path_string,
                          double xmin,
                          double xmax,
                          double ymin,
@@ -142,9 +142,9 @@ class SourceContainer
                          std::size_t nrows,
                          std::size_t ncols);
 
-    RasterDatum getRasterDataFromSource(unsigned int source_id, double lon, double lat);
+    RasterDatum GetRasterDataFromSource(unsigned int source_id, double lon, double lat);
 
-    RasterDatum getRasterInterpolateFromSource(unsigned int source_id, double lon, double lat);
+    RasterDatum GetRasterInterpolateFromSource(unsigned int source_id, double lon, double lat);
 
   private:
     std::vector<RasterSource> LoadedSources;
diff --git a/include/extractor/suffix_table.hpp b/include/extractor/suffix_table.hpp
new file mode 100644
index 0000000..af5f016
--- /dev/null
+++ b/include/extractor/suffix_table.hpp
@@ -0,0 +1,30 @@
+#ifndef OSRM_EXTRACTOR_SUFFIX_LIST_HPP_
+#define OSRM_EXTRACTOR_SUFFIX_LIST_HPP_
+
+#include <string>
+#include <unordered_set>
+
+struct lua_State;
+
+namespace osrm
+{
+namespace extractor
+{
+// A table containing suffixes.
+// At the moment, it is only a front for an unordered set. At some point we might want to make it
+// country dependent and have it behave accordingly
+class SuffixTable final
+{
+  public:
+    SuffixTable(lua_State *lua_state);
+
+    // check whether a string is part of the know suffix list
+    bool isSuffix(const std::string &possible_suffix) const;
+
+  private:
+    std::unordered_set<std::string> suffix_set;
+};
+} /* namespace extractor */
+} /* namespace osrm */
+
+#endif /* OSRM_EXTRACTOR_SUFFIX_LIST_HPP_ */
diff --git a/include/extractor/tarjan_scc.hpp b/include/extractor/tarjan_scc.hpp
index 874d3f6..71186ec 100644
--- a/include/extractor/tarjan_scc.hpp
+++ b/include/extractor/tarjan_scc.hpp
@@ -57,7 +57,7 @@ template <typename GraphT> class TarjanSCC
         BOOST_ASSERT(m_graph->GetNumberOfNodes() > 0);
     }
 
-    void run()
+    void Run()
     {
         TIMER_START(SCC_RUN);
         const NodeID max_node_id = m_graph->GetNumberOfNodes();
@@ -167,16 +167,16 @@ template <typename GraphT> class TarjanSCC
                                          });
     }
 
-    std::size_t get_number_of_components() const { return component_size_vector.size(); }
+    std::size_t GetNumberOfComponents() const { return component_size_vector.size(); }
 
-    std::size_t get_size_one_count() const { return size_one_counter; }
+    std::size_t GetSizeOneCount() const { return size_one_counter; }
 
-    unsigned get_component_size(const unsigned component_id) const
+    unsigned GetComponentSize(const unsigned component_id) const
     {
         return component_size_vector[component_id];
     }
 
-    unsigned get_component_id(const NodeID node) const { return components_index[node]; }
+    unsigned GetComponentID(const NodeID node) const { return components_index[node]; }
 };
 }
 }
diff --git a/include/server/api/base_parameters_grammar.hpp b/include/server/api/base_parameters_grammar.hpp
index 4d441a5..cc10cf3 100644
--- a/include/server/api/base_parameters_grammar.hpp
+++ b/include/server/api/base_parameters_grammar.hpp
@@ -8,7 +8,7 @@
 #include "engine/polyline_compressor.hpp"
 
 #include <boost/optional.hpp>
-//#define BOOST_SPIRIT_DEBUG
+#include <boost/spirit/include/phoenix.hpp>
 #include <boost/spirit/include/qi.hpp>
 
 #include <limits>
@@ -21,7 +21,11 @@ namespace server
 namespace api
 {
 
+namespace
+{
+namespace ph = boost::phoenix;
 namespace qi = boost::spirit::qi;
+}
 
 template <typename T, char... Fmt> struct no_trailing_dot_policy : qi::real_policies<T>
 {
@@ -63,76 +67,104 @@ template <typename T, char... Fmt> struct no_trailing_dot_policy : qi::real_poli
     }
 };
 
-struct BaseParametersGrammar : boost::spirit::qi::grammar<std::string::iterator>
+template <typename Iterator, typename Signature>
+struct BaseParametersGrammar : boost::spirit::qi::grammar<Iterator, Signature>
 {
-    using Iterator = std::string::iterator;
-    using RadiusesT = std::vector<boost::optional<double>>;
     using json_policy = no_trailing_dot_policy<double, 'j', 's', 'o', 'n'>;
 
-    BaseParametersGrammar(qi::rule<Iterator> &root_rule_, engine::api::BaseParameters &parameters_)
-        : BaseParametersGrammar::base_type(root_rule_), base_parameters(parameters_)
+    BaseParametersGrammar(qi::rule<Iterator, Signature> &root_rule)
+        : BaseParametersGrammar::base_type(root_rule)
     {
-        const auto add_bearing =
-            [this](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 set_radiuses = [this](RadiusesT radiuses) {
-            base_parameters.radiuses = std::move(radiuses);
-        };
-        const auto add_hint = [this](const std::string &hint_string) {
-            if (hint_string.size() > 0)
+        const auto add_hint = [](engine::api::BaseParameters &base_parameters, const boost::optional<std::string> &hint_string)
+        {
+            if (hint_string)
             {
-                base_parameters.hints.push_back(engine::Hint::FromBase64(hint_string));
+                base_parameters.hints.emplace_back(engine::Hint::FromBase64(hint_string.get()));
+            }
+            else
+            {
+                base_parameters.hints.emplace_back(boost::none);
             }
         };
-        const auto add_coordinate = [this](const boost::fusion::vector<double, double> &lonLat) {
-            base_parameters.coordinates.emplace_back(util::Coordinate(
-                util::FixedLongitude(boost::fusion::at_c<0>(lonLat) * COORDINATE_PRECISION),
-                util::FixedLatitude(boost::fusion::at_c<1>(lonLat) * COORDINATE_PRECISION)));
-        };
-        const auto polyline_to_coordinates = [this](const std::string &polyline) {
-            base_parameters.coordinates = engine::decodePolyline(polyline);
+
+        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));
         };
 
-        alpha_numeral = +qi::char_("a-zA-Z0-9");
         polyline_chars = qi::char_("a-zA-Z0-9_.--[]{}@?|\\%~`^");
         base64_char = qi::char_("a-zA-Z0-9--_=");
-
-        unlimited.add("unlimited", std::numeric_limits<double>::infinity());
-
-        radiuses_rule = qi::lit("radiuses=") > -(unlimited | qi::double_) % ";";
-        hints_rule =
-            qi::lit("hints=") >
-            qi::as_string[qi::repeat(engine::ENCODED_HINT_SIZE)[base64_char]][add_hint] % ";";
-        bearings_rule =
-            qi::lit("bearings=") > (-(qi::short_ > ',' > qi::short_))[add_bearing] % ";";
-        polyline_rule = qi::as_string[qi::lit("polyline(") > +polyline_chars > qi::lit(")")]
-                                     [polyline_to_coordinates];
-        location_rule = (double_ > qi::lit(',') > double_)[add_coordinate];
-        query_rule = (location_rule % ';') | polyline_rule;
-
-        base_rule = bearings_rule | radiuses_rule[set_radiuses] | hints_rule;
+        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)] % ';'
+            ;
+
+        base_rule = radiuses_rule(qi::_r1) | hints_rule(qi::_r1) | bearings_rule(qi::_r1);
     }
 
   protected:
-    qi::rule<Iterator> base_rule;
-    qi::rule<Iterator> query_rule;
+    qi::rule<Iterator, Signature> base_rule;
+    qi::rule<Iterator, Signature> query_rule;
 
   private:
-    engine::api::BaseParameters &base_parameters;
-    qi::rule<Iterator> bearings_rule;
-    qi::rule<Iterator> hints_rule;
-    qi::rule<Iterator> polyline_rule, location_rule;
-    qi::symbols<char, double> unlimited;
-    qi::rule<Iterator, RadiusesT()> radiuses_rule;
+    qi::rule<Iterator, Signature> bearings_rule;
+    qi::rule<Iterator, Signature> radiuses_rule;
+    qi::rule<Iterator, Signature> hints_rule;
+
+    qi::rule<Iterator, osrm::engine::Bearing()> bearing_rule;
+    qi::rule<Iterator, osrm::util::Coordinate()> location_rule;
+    qi::rule<Iterator, std::vector<osrm::util::Coordinate>()> polyline_rule;
+
     qi::rule<Iterator, unsigned char()> base64_char;
-    qi::rule<Iterator, std::string()> alpha_numeral, polyline_chars;
+    qi::rule<Iterator, std::string()> polyline_chars;
+    qi::rule<Iterator, double()> unlimited_rule;
     qi::real_parser<double, json_policy> double_;
 };
 }
diff --git a/include/server/api/match_parameter_grammar.hpp b/include/server/api/match_parameter_grammar.hpp
index 6671a58..a775e49 100644
--- a/include/server/api/match_parameter_grammar.hpp
+++ b/include/server/api/match_parameter_grammar.hpp
@@ -2,9 +2,9 @@
 #define MATCH_PARAMETERS_GRAMMAR_HPP
 
 #include "engine/api/match_parameters.hpp"
-#include "server/api/base_parameters_grammar.hpp"
+#include "server/api/route_parameters_grammar.hpp"
 
-//#define BOOST_SPIRIT_DEBUG
+#include <boost/spirit/include/phoenix.hpp>
 #include <boost/spirit/include/qi.hpp>
 
 namespace osrm
@@ -14,58 +14,34 @@ namespace server
 namespace api
 {
 
+namespace
+{
+namespace ph = boost::phoenix;
 namespace qi = boost::spirit::qi;
+}
 
-struct MatchParametersGrammar final : public BaseParametersGrammar
+template <typename Iterator = std::string::iterator,
+          typename Signature = void(engine::api::MatchParameters &)>
+struct MatchParametersGrammar final : public RouteParametersGrammar<Iterator, Signature>
 {
-    using Iterator = std::string::iterator;
-    using StepsT = bool;
-    using TimestampsT = std::vector<unsigned>;
-    using GeometriesT = engine::api::RouteParameters::GeometriesType;
-    using OverviewT = engine::api::RouteParameters::OverviewType;
+    using BaseGrammar = RouteParametersGrammar<Iterator, Signature>;
 
-    MatchParametersGrammar() : BaseParametersGrammar(root_rule, parameters)
+    MatchParametersGrammar() : BaseGrammar(root_rule)
     {
-        const auto set_geojson_type = [this] {
-            parameters.geometries = engine::api::RouteParameters::GeometriesType::GeoJSON;
-        };
-        const auto set_polyline_type = [this] {
-            parameters.geometries = engine::api::RouteParameters::GeometriesType::Polyline;
-        };
-
-        const auto set_simplified_type = [this] {
-            parameters.overview = engine::api::RouteParameters::OverviewType::Simplified;
-        };
-        const auto set_full_type = [this] {
-            parameters.overview = engine::api::RouteParameters::OverviewType::Full;
-        };
-        const auto set_false_type = [this] {
-            parameters.overview = engine::api::RouteParameters::OverviewType::False;
-        };
-        const auto set_steps = [this](const StepsT steps) { parameters.steps = steps; };
-        const auto set_timestamps = [this](TimestampsT timestamps) {
-            parameters.timestamps = std::move(timestamps);
-        };
-
-        steps_rule = qi::lit("steps=") > qi::bool_;
-        geometries_rule = qi::lit("geometries=geojson")[set_geojson_type] |
-                          qi::lit("geometries=polyline")[set_polyline_type];
-        overview_rule = qi::lit("overview=simplified")[set_simplified_type] |
-                        qi::lit("overview=full")[set_full_type] |
-                        qi::lit("overview=false")[set_false_type];
-        timestamps_rule = qi::lit("timestamps=") > qi::uint_ % ";";
-        match_rule = steps_rule[set_steps] | geometries_rule | overview_rule |
-                     timestamps_rule[set_timestamps];
-        root_rule =
-            query_rule > -qi::lit(".json") > -(qi::lit("?") > (match_rule | base_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)) % '&')
+            ;
     }
 
-    engine::api::MatchParameters parameters;
-
   private:
-    qi::rule<Iterator> root_rule, match_rule, geometries_rule, overview_rule;
-    qi::rule<Iterator, TimestampsT()> timestamps_rule;
-    qi::rule<Iterator, StepsT()> steps_rule;
+    qi::rule<Iterator, Signature> root_rule;
+    qi::rule<Iterator, Signature> timestamps_rule;
 };
 }
 }
diff --git a/include/server/api/nearest_parameter_grammar.hpp b/include/server/api/nearest_parameter_grammar.hpp
index 09b6c2a..b9bc21b 100644
--- a/include/server/api/nearest_parameter_grammar.hpp
+++ b/include/server/api/nearest_parameter_grammar.hpp
@@ -4,7 +4,7 @@
 #include "engine/api/nearest_parameters.hpp"
 #include "server/api/base_parameters_grammar.hpp"
 
-//#define BOOST_SPIRIT_DEBUG
+#include <boost/spirit/include/phoenix.hpp>
 #include <boost/spirit/include/qi.hpp>
 
 namespace osrm
@@ -14,27 +14,34 @@ namespace server
 namespace api
 {
 
+namespace
+{
+namespace ph = boost::phoenix;
 namespace qi = boost::spirit::qi;
+}
 
-struct NearestParametersGrammar final : public BaseParametersGrammar
+template <typename Iterator = std::string::iterator,
+          typename Signature = void(engine::api::NearestParameters &)>
+struct NearestParametersGrammar final : public BaseParametersGrammar<Iterator, Signature>
 {
-    using Iterator = std::string::iterator;
+    using BaseGrammar = BaseParametersGrammar<Iterator, Signature>;
 
-    NearestParametersGrammar() : BaseParametersGrammar(root_rule, parameters)
+    NearestParametersGrammar() : BaseGrammar(root_rule)
     {
-        const auto set_number = [this](const unsigned number) {
-            parameters.number_of_results = number;
-        };
-        nearest_rule = (qi::lit("number=") > qi::uint_)[set_number];
-        root_rule =
-            query_rule > -qi::lit(".json") > -(qi::lit("?") > (nearest_rule | base_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)) % '&')
+            ;
     }
 
-    engine::api::NearestParameters parameters;
-
   private:
-    qi::rule<Iterator> root_rule;
-    qi::rule<Iterator> nearest_rule;
+    qi::rule<Iterator, Signature> root_rule;
+    qi::rule<Iterator, Signature> nearest_rule;
 };
 }
 }
diff --git a/include/server/api/parameters_parser.hpp b/include/server/api/parameters_parser.hpp
index 161910d..08083ed 100644
--- a/include/server/api/parameters_parser.hpp
+++ b/include/server/api/parameters_parser.hpp
@@ -30,7 +30,8 @@ using is_parameter_t =
 // Starts parsing and iter and modifies it until iter == end or parsing failed
 template <typename ParameterT,
           typename std::enable_if<detail::is_parameter_t<ParameterT>::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);
 
 // Copy on purpose because we need mutability
 template <typename ParameterT,
diff --git a/include/server/api/parsed_url.hpp b/include/server/api/parsed_url.hpp
index c805196..0fefc65 100644
--- a/include/server/api/parsed_url.hpp
+++ b/include/server/api/parsed_url.hpp
@@ -3,8 +3,6 @@
 
 #include "util/coordinate.hpp"
 
-#include <boost/fusion/include/adapt_struct.hpp>
-
 #include <string>
 #include <vector>
 
@@ -21,17 +19,11 @@ struct ParsedURL final
     unsigned version;
     std::string profile;
     std::string query;
+    std::size_t prefix_length;
 };
 
 } // api
 } // server
 } // osrm
 
-BOOST_FUSION_ADAPT_STRUCT(osrm::server::api::ParsedURL,
-    (std::string, service)
-    (unsigned, version)
-    (std::string, profile)
-    (std::string, query)
-)
-
 #endif
diff --git a/include/server/api/route_parameters_grammar.hpp b/include/server/api/route_parameters_grammar.hpp
index d4280df..46d10d1 100644
--- a/include/server/api/route_parameters_grammar.hpp
+++ b/include/server/api/route_parameters_grammar.hpp
@@ -4,7 +4,7 @@
 #include "engine/api/route_parameters.hpp"
 #include "server/api/base_parameters_grammar.hpp"
 
-//#define BOOST_SPIRIT_DEBUG
+#include <boost/spirit/include/phoenix.hpp>
 #include <boost/spirit/include/qi.hpp>
 
 namespace osrm
@@ -14,64 +14,66 @@ namespace server
 namespace api
 {
 
+namespace
+{
+namespace ph = boost::phoenix;
 namespace qi = boost::spirit::qi;
+}
 
-struct RouteParametersGrammar : public BaseParametersGrammar
+template <typename Iterator = std::string::iterator,
+          typename Signature = void(engine::api::RouteParameters &)>
+struct RouteParametersGrammar : public BaseParametersGrammar<Iterator, Signature>
 {
-    using Iterator = std::string::iterator;
-    using StepsT = bool;
-    using AlternativeT = bool;
-    using GeometriesT = engine::api::RouteParameters::GeometriesType;
-    using OverviewT = engine::api::RouteParameters::OverviewType;
-    using UturnsT = bool;
+    using BaseGrammar = BaseParametersGrammar<Iterator, Signature>;
 
-    RouteParametersGrammar() : BaseParametersGrammar(root_rule, parameters)
+    RouteParametersGrammar() : RouteParametersGrammar(root_rule)
     {
-        const auto set_geojson_type = [this] {
-            parameters.geometries = engine::api::RouteParameters::GeometriesType::GeoJSON;
-        };
-        const auto set_polyline_type = [this] {
-            parameters.geometries = engine::api::RouteParameters::GeometriesType::Polyline;
-        };
+        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]))
+            ;
 
-        const auto set_simplified_type = [this] {
-            parameters.overview = engine::api::RouteParameters::OverviewType::Simplified;
-        };
-        const auto set_full_type = [this] {
-            parameters.overview = engine::api::RouteParameters::OverviewType::Full;
-        };
-        const auto set_false_type = [this] {
-            parameters.overview = engine::api::RouteParameters::OverviewType::False;
-        };
-        const auto set_steps = [this](const StepsT steps) { parameters.steps = steps; };
-        const auto set_alternatives = [this](const AlternativeT alternatives) {
-            parameters.alternatives = alternatives;
-        };
-        const auto set_continue_straight = [this](UturnsT continue_straight) { parameters.continue_straight = std::move(continue_straight); };
+        root_rule
+            = query_rule(qi::_r1) > -qi::lit(".json")
+            > -('?' > (route_rule(qi::_r1) | base_rule(qi::_r1)) % '&')
+            ;
+    }
 
-        alternatives_rule = qi::lit("alternatives=") > qi::bool_;
-        steps_rule = qi::lit("steps=") > qi::bool_;
-        geometries_rule = qi::lit("geometries=geojson")[set_geojson_type] |
-                          qi::lit("geometries=polyline")[set_polyline_type];
-        overview_rule = qi::lit("overview=simplified")[set_simplified_type] |
-                        qi::lit("overview=full")[set_full_type] |
-                        qi::lit("overview=false")[set_false_type];
-        continue_straight_rule = qi::lit("continue_straight=default") | (qi::lit("continue_straight=") > qi::bool_)[set_continue_straight];
-        route_rule = steps_rule[set_steps] | alternatives_rule[set_alternatives] | geometries_rule |
-                     overview_rule | continue_straight_rule;
+    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)
+            ;
 
-        root_rule =
-            query_rule > -qi::lit(".json") > -(qi::lit("?") > (route_rule | base_rule) % '&');
+        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])
+            ;
+
+        query_rule = BaseGrammar::query_rule(qi::_r1);
     }
 
-    engine::api::RouteParameters parameters;
+  protected:
+    qi::rule<Iterator, Signature> base_rule;
+    qi::rule<Iterator, Signature> query_rule;
 
   private:
-    qi::rule<Iterator> root_rule;
-    qi::rule<Iterator> route_rule, geometries_rule, overview_rule;
-    qi::rule<Iterator, UturnsT()> continue_straight_rule;
-    qi::rule<Iterator, StepsT()> steps_rule;
-    qi::rule<Iterator, AlternativeT()> alternatives_rule;
+    qi::rule<Iterator, Signature> root_rule;
+    qi::rule<Iterator, Signature> route_rule;
+
+    qi::symbols<char, engine::api::RouteParameters::GeometriesType> geometries_type;
+    qi::symbols<char, engine::api::RouteParameters::OverviewType> overview_type;
 };
 }
 }
diff --git a/include/server/api/table_parameter_grammar.hpp b/include/server/api/table_parameter_grammar.hpp
index b95b1b8..73ff3c6 100644
--- a/include/server/api/table_parameter_grammar.hpp
+++ b/include/server/api/table_parameter_grammar.hpp
@@ -4,7 +4,7 @@
 #include "engine/api/table_parameters.hpp"
 #include "server/api/base_parameters_grammar.hpp"
 
-//#define BOOST_SPIRIT_DEBUG
+#include <boost/spirit/include/phoenix.hpp>
 #include <boost/spirit/include/qi.hpp>
 
 namespace osrm
@@ -14,43 +14,53 @@ namespace server
 namespace api
 {
 
+namespace
+{
+namespace ph = boost::phoenix;
 namespace qi = boost::spirit::qi;
+}
 
-struct TableParametersGrammar final : public BaseParametersGrammar
+template <typename Iterator = std::string::iterator,
+          typename Signature = void(engine::api::TableParameters &)>
+struct TableParametersGrammar final : public BaseParametersGrammar<Iterator, Signature>
 {
-    using Iterator = std::string::iterator;
-    using SourcesT = std::vector<std::size_t>;
-    using DestinationsT = std::vector<std::size_t>;
+    using BaseGrammar = BaseParametersGrammar<Iterator, Signature>;
 
-    TableParametersGrammar() : BaseParametersGrammar(root_rule, parameters)
+    TableParametersGrammar() : BaseGrammar(root_rule)
     {
-        const auto set_destiantions = [this](DestinationsT dests) {
-            parameters.destinations = std::move(dests);
-        };
-        const auto set_sources = [this](SourcesT sources) {
-            parameters.sources = std::move(sources);
-        };
-// TODO: ulonglong -> size_t not only on Windows but on all 32 bit platforms; unsupported anyway as of now
-#ifdef WIN32
-        destinations_rule = qi::lit("destinations=all") | (qi::lit("destinations=") > (qi::ulong_long % ";")[set_destiantions]);
-        sources_rule = qi::lit("sources=all") | (qi::lit("sources=") > (qi::ulong_long % ";")[set_sources]);
+#ifdef BOOST_HAS_LONG_LONG
+        if (std::is_same<std::size_t, unsigned long long>::value)
+            size_t_ = qi::ulong_long;
+        else
+            size_t_ = qi::ulong_;
 #else
-        destinations_rule = qi::lit("destinations=all") | (qi::lit("destinations=") > (qi::ulong_ % ";")[set_destiantions]);
-        sources_rule = qi::lit("sources=all") | (qi::lit("sources=") > (qi::ulong_ % ";")[set_sources]);
+        size_t_ = qi::ulong_;
 #endif
-        table_rule = destinations_rule | sources_rule;
 
-        root_rule =
-            query_rule > -qi::lit(".json") > -(qi::lit("?") > (table_rule | base_rule) % '&');
-    }
+        destinations_rule
+            = qi::lit("destinations=")
+            > (qi::lit("all") | (size_t_ % ';')[ph::bind(&engine::api::TableParameters::destinations, qi::_r1) = qi::_1])
+            ;
 
-    engine::api::TableParameters parameters;
+        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)) % '&')
+            ;
+    }
 
   private:
-    qi::rule<Iterator> root_rule;
-    qi::rule<Iterator> table_rule;
-    qi::rule<Iterator> sources_rule;
-    qi::rule<Iterator> destinations_rule;
+    qi::rule<Iterator, Signature> root_rule;
+    qi::rule<Iterator, Signature> table_rule;
+    qi::rule<Iterator, Signature> sources_rule;
+    qi::rule<Iterator, Signature> destinations_rule;
+    qi::rule<Iterator, std::size_t()> size_t_;
 };
 }
 }
diff --git a/include/server/api/tile_parameter_grammar.hpp b/include/server/api/tile_parameter_grammar.hpp
index ef73357..f0d79b6 100644
--- a/include/server/api/tile_parameter_grammar.hpp
+++ b/include/server/api/tile_parameter_grammar.hpp
@@ -6,7 +6,7 @@
 #include "engine/hint.hpp"
 #include "engine/polyline_compressor.hpp"
 
-//#define BOOST_SPIRIT_DEBUG
+#include <boost/spirit/include/phoenix.hpp>
 #include <boost/spirit/include/qi.hpp>
 
 #include <string>
@@ -18,28 +18,29 @@ namespace server
 namespace api
 {
 
-namespace qi = boost::spirit::qi;
-struct TileParametersGrammar final : boost::spirit::qi::grammar<std::string::iterator>
+namespace
 {
-    using Iterator = std::string::iterator;
+namespace ph = boost::phoenix;
+namespace qi = boost::spirit::qi;
+}
 
+template <typename Iterator = std::string::iterator,
+          typename Signature = void(engine::api::TileParameters &)>
+struct TileParametersGrammar final : boost::spirit::qi::grammar<Iterator, Signature>
+{
     TileParametersGrammar() : TileParametersGrammar::base_type(root_rule)
     {
-        const auto set_x = [this](const unsigned x_) { parameters.x = x_; };
-        const auto set_y = [this](const unsigned y_) { parameters.y = y_; };
-        const auto set_z = [this](const unsigned z_) { parameters.z = z_; };
-
-        query_rule = qi::lit("tile(") > qi::uint_[set_x]             //
-                     > qi::lit(",") > qi::uint_[set_y] >             //
-                     qi::lit(",") > qi::uint_[set_z] > qi::lit(")"); //
-
-        root_rule = query_rule > 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")
+            ;
     }
-    engine::api::TileParameters parameters;
 
   private:
-    qi::rule<Iterator> root_rule;
-    qi::rule<Iterator> query_rule;
+    qi::rule<Iterator, Signature> root_rule;
 };
 }
 }
diff --git a/include/server/api/trip_parameter_grammar.hpp b/include/server/api/trip_parameter_grammar.hpp
index 52c6622..b9a7ee0 100644
--- a/include/server/api/trip_parameter_grammar.hpp
+++ b/include/server/api/trip_parameter_grammar.hpp
@@ -2,9 +2,8 @@
 #define TRIP_PARAMETERS_GRAMMAR_HPP
 
 #include "engine/api/trip_parameters.hpp"
-#include "server/api/base_parameters_grammar.hpp"
+#include "server/api/route_parameters_grammar.hpp"
 
-//#define BOOST_SPIRIT_DEBUG
 #include <boost/spirit/include/qi.hpp>
 
 namespace osrm
@@ -14,52 +13,27 @@ namespace server
 namespace api
 {
 
+namespace
+{
 namespace qi = boost::spirit::qi;
+}
 
-struct TripParametersGrammar final : public BaseParametersGrammar
+template <typename Iterator = std::string::iterator,
+          typename Signature = void(engine::api::TripParameters &)>
+struct TripParametersGrammar final : public RouteParametersGrammar<Iterator, Signature>
 {
-    using Iterator = std::string::iterator;
-    using StepsT = bool;
-    using GeometriesT = engine::api::RouteParameters::GeometriesType;
-    using OverviewT = engine::api::RouteParameters::OverviewType;
+    using BaseGrammar = RouteParametersGrammar<Iterator, Signature>;
 
-    TripParametersGrammar() : BaseParametersGrammar(root_rule, parameters)
+    TripParametersGrammar() : BaseGrammar(root_rule)
     {
-        const auto set_geojson_type = [this] {
-            parameters.geometries = engine::api::RouteParameters::GeometriesType::GeoJSON;
-        };
-        const auto set_polyline_type = [this] {
-            parameters.geometries = engine::api::RouteParameters::GeometriesType::Polyline;
-        };
-
-        const auto set_simplified_type = [this] {
-            parameters.overview = engine::api::RouteParameters::OverviewType::Simplified;
-        };
-        const auto set_full_type = [this] {
-            parameters.overview = engine::api::RouteParameters::OverviewType::Full;
-        };
-        const auto set_false_type = [this] {
-            parameters.overview = engine::api::RouteParameters::OverviewType::False;
-        };
-        const auto set_steps = [this](const StepsT steps) { parameters.steps = steps; };
-
-        steps_rule = qi::lit("steps=") > qi::bool_;
-        geometries_rule = qi::lit("geometries=geojson")[set_geojson_type] |
-                          qi::lit("geometries=polyline")[set_polyline_type];
-        overview_rule = qi::lit("overview=simplified")[set_simplified_type] |
-                        qi::lit("overview=full")[set_full_type] |
-                        qi::lit("overview=false")[set_false_type];
-        trip_rule = steps_rule[set_steps] | geometries_rule | overview_rule;
-
-        root_rule =
-            query_rule > -qi::lit(".json") > -(qi::lit("?") > (trip_rule | base_rule) % '&');
+        root_rule
+            = BaseGrammar::query_rule(qi::_r1) > -qi::lit(".json")
+            > -('?' > (BaseGrammar::base_rule(qi::_r1)) % '&')
+            ;
     }
 
-    engine::api::TripParameters parameters;
-
   private:
-    qi::rule<Iterator> root_rule, trip_rule, geometries_rule, overview_rule;
-    qi::rule<Iterator, StepsT()> steps_rule;
+    qi::rule<Iterator, Signature> root_rule;
 };
 }
 }
diff --git a/include/server/service/base_service.hpp b/include/server/service/base_service.hpp
index 36c82db..cfa8581 100644
--- a/include/server/service/base_service.hpp
+++ b/include/server/service/base_service.hpp
@@ -25,7 +25,7 @@ class BaseService
     BaseService(OSRM &routing_machine) : routing_machine(routing_machine) {}
     virtual ~BaseService() = default;
 
-    virtual engine::Status RunQuery(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 23212c1..a0aed94 100644
--- a/include/server/service/match_service.hpp
+++ b/include/server/service/match_service.hpp
@@ -22,7 +22,7 @@ class MatchService final : public BaseService
   public:
     MatchService(OSRM &routing_machine) : BaseService(routing_machine) {}
 
-    engine::Status RunQuery(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 222003c..62fc1b1 100644
--- a/include/server/service/nearest_service.hpp
+++ b/include/server/service/nearest_service.hpp
@@ -22,7 +22,7 @@ class NearestService final : public BaseService
   public:
     NearestService(OSRM &routing_machine) : BaseService(routing_machine) {}
 
-    engine::Status RunQuery(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 1115585..639aea2 100644
--- a/include/server/service/route_service.hpp
+++ b/include/server/service/route_service.hpp
@@ -22,7 +22,7 @@ class RouteService final : public BaseService
   public:
     RouteService(OSRM &routing_machine) : BaseService(routing_machine) {}
 
-    engine::Status RunQuery(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 61b9d84..7839bd4 100644
--- a/include/server/service/table_service.hpp
+++ b/include/server/service/table_service.hpp
@@ -22,7 +22,7 @@ class TableService final : public BaseService
   public:
     TableService(OSRM &routing_machine) : BaseService(routing_machine) {}
 
-    engine::Status RunQuery(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 05365d4..d984a0b 100644
--- a/include/server/service/tile_service.hpp
+++ b/include/server/service/tile_service.hpp
@@ -22,7 +22,7 @@ class TileService final : public BaseService
   public:
     TileService(OSRM &routing_machine) : BaseService(routing_machine) {}
 
-    engine::Status RunQuery(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 7daeba6..d8dfed4 100644
--- a/include/server/service/trip_service.hpp
+++ b/include/server/service/trip_service.hpp
@@ -22,7 +22,7 @@ class TripService final : public BaseService
   public:
     TripService(OSRM &routing_machine) : BaseService(routing_machine) {}
 
-    engine::Status RunQuery(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_memory.hpp b/include/storage/shared_memory.hpp
index 47238cf..9ceef7d 100644
--- a/include/storage/shared_memory.hpp
+++ b/include/storage/shared_memory.hpp
@@ -7,7 +7,7 @@
 #include <boost/filesystem.hpp>
 #include <boost/filesystem/fstream.hpp>
 #include <boost/interprocess/mapped_region.hpp>
-#ifndef WIN32
+#ifndef _WIN32
 #include <boost/interprocess/xsi_shared_memory.hpp>
 #else
 #include <boost/interprocess/shared_memory_object.hpp>
@@ -39,7 +39,7 @@ struct OSRMLockFile
     }
 };
 
-#ifndef WIN32
+#ifndef _WIN32
 class SharedMemory
 {
 
diff --git a/include/util/coordinate.hpp b/include/util/coordinate.hpp
index e249bb2..662cbb5 100644
--- a/include/util/coordinate.hpp
+++ b/include/util/coordinate.hpp
@@ -127,10 +127,16 @@ struct Coordinate
     FixedLongitude lon;
     FixedLatitude lat;
 
-    Coordinate();
+    Coordinate() : lon(std::numeric_limits<int>::min()), lat(std::numeric_limits<int>::min()) {}
+
     Coordinate(const FloatCoordinate &other);
-    Coordinate(const FixedLongitude lon_, const FixedLatitude lat_);
-    Coordinate(const FloatLongitude lon_, const FloatLatitude lat_);
+
+    Coordinate(const FloatLongitude lon_, const FloatLatitude lat_)
+        : Coordinate(toFixed(lon_), toFixed(lat_))
+    {
+    }
+
+    Coordinate(const FixedLongitude lon_, const FixedLatitude lat_) : lon(lon_), lat(lat_) {}
 
     template <class T> Coordinate(const T &coordinate) : lon(coordinate.lon), lat(coordinate.lat)
     {
@@ -166,10 +172,22 @@ struct FloatCoordinate
     FloatLongitude lon;
     FloatLatitude lat;
 
-    FloatCoordinate();
-    FloatCoordinate(const FixedLongitude lon_, const FixedLatitude lat_);
-    FloatCoordinate(const FloatLongitude lon_, const FloatLatitude lat_);
-    FloatCoordinate(const Coordinate other);
+    FloatCoordinate()
+        : lon(std::numeric_limits<double>::min()), lat(std::numeric_limits<double>::min())
+    {
+    }
+
+    FloatCoordinate(const Coordinate other)
+        : FloatCoordinate(toFloating(other.lon), toFloating(other.lat))
+    {
+    }
+
+    FloatCoordinate(const FixedLongitude lon_, const FixedLatitude lat_)
+        : FloatCoordinate(toFloating(lon_), toFloating(lat_))
+    {
+    }
+
+    FloatCoordinate(const FloatLongitude lon_, const FloatLatitude lat_) : lon(lon_), lat(lat_) {}
 
     bool IsValid() const;
     friend bool operator==(const FloatCoordinate lhs, const FloatCoordinate rhs);
@@ -181,6 +199,9 @@ bool operator==(const Coordinate lhs, const Coordinate rhs);
 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)) {}
+
 }
 }
 
diff --git a/include/util/coordinate_calculation.hpp b/include/util/coordinate_calculation.hpp
index afa6c65..5096ebe 100644
--- a/include/util/coordinate_calculation.hpp
+++ b/include/util/coordinate_calculation.hpp
@@ -25,16 +25,49 @@ 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);
+std::uint64_t squaredEuclideanDistance(const Coordinate lhs, const Coordinate rhs);
 
 double haversineDistance(const Coordinate first_coordinate, const Coordinate second_coordinate);
 
 double greatCircleDistance(const Coordinate first_coordinate, const Coordinate second_coordinate);
 
-std::pair<double, FloatCoordinate>
-projectPointOnSegment(const FloatCoordinate &projected_xy_source,
-                      const FloatCoordinate &projected_xy_target,
-                      const FloatCoordinate &projected_xy_coordinate);
+inline std::pair<double, FloatCoordinate> projectPointOnSegment(const FloatCoordinate &source,
+                                                         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};
+    // dot product of two un-normed vectors
+    const auto unnormed_ratio = static_cast<double>(slope_vector.lon * rel_coordinate.lon) +
+                                static_cast<double>(slope_vector.lat * rel_coordinate.lat);
+    // squared length of the slope vector
+    const auto squared_length = static_cast<double>(slope_vector.lon * slope_vector.lon) +
+                                static_cast<double>(slope_vector.lat * slope_vector.lat);
+
+    if (squared_length < std::numeric_limits<double>::epsilon())
+    {
+        return {0, source};
+    }
+
+    const double normed_ratio = unnormed_ratio / squared_length;
+    double clamped_ratio = normed_ratio;
+    if (clamped_ratio > 1.)
+    {
+        clamped_ratio = 1.;
+    }
+    else if (clamped_ratio < 0.)
+    {
+        clamped_ratio = 0.;
+    }
+
+    return {clamped_ratio,
+            {
+                FloatLongitude(1.0 - clamped_ratio) * source.lon +
+                    target.lon * FloatLongitude(clamped_ratio),
+                FloatLatitude(1.0 - clamped_ratio) * source.lat +
+                    target.lat * FloatLatitude(clamped_ratio),
+            }};
+}
 
 double perpendicularDistance(const Coordinate segment_source,
                              const Coordinate segment_target,
diff --git a/include/util/dist_table_wrapper.hpp b/include/util/dist_table_wrapper.hpp
index 714bcef..7ebe3cb 100644
--- a/include/util/dist_table_wrapper.hpp
+++ b/include/util/dist_table_wrapper.hpp
@@ -5,6 +5,7 @@
 #include <utility>
 #include <boost/assert.hpp>
 #include <cstddef>
+#include <algorithm>
 
 namespace osrm
 {
diff --git a/include/util/guidance/toolkit.hpp b/include/util/guidance/toolkit.hpp
new file mode 100644
index 0000000..24072a8
--- /dev/null
+++ b/include/util/guidance/toolkit.hpp
@@ -0,0 +1,48 @@
+#ifndef OSRM_UTIL_GUIDANCE_TOOLKIT_HPP_
+#define OSRM_UTIL_GUIDANCE_TOOLKIT_HPP_
+
+/* A set of tools required for guidance in both pre and post-processing */
+
+#include "extractor/guidance/turn_instruction.hpp"
+
+namespace osrm
+{
+namespace util
+{
+namespace guidance
+{
+
+inline double angularDeviation(const double angle, const double from)
+{
+    const double deviation = std::abs(angle - from);
+    return std::min(360 - deviation, deviation);
+}
+
+inline extractor::guidance::DirectionModifier getTurnDirection(const double angle)
+{
+    // An angle of zero is a u-turn
+    // 180 goes perfectly straight
+    // 0-180 are right turns
+    // 180-360 are left turns
+    if (angle > 0 && angle < 60)
+        return extractor::guidance::DirectionModifier::SharpRight;
+    if (angle >= 60 && angle < 140)
+        return extractor::guidance::DirectionModifier::Right;
+    if (angle >= 140 && angle < 170)
+        return extractor::guidance::DirectionModifier::SlightRight;
+    if (angle >= 165 && angle <= 195)
+        return extractor::guidance::DirectionModifier::Straight;
+    if (angle > 190 && angle <= 220)
+        return extractor::guidance::DirectionModifier::SlightLeft;
+    if (angle > 220 && angle <= 300)
+        return extractor::guidance::DirectionModifier::Left;
+    if (angle > 300 && angle < 360)
+        return extractor::guidance::DirectionModifier::SharpLeft;
+    return extractor::guidance::DirectionModifier::UTurn;
+}
+
+} /* namespace guidance */
+} /* namespace util */
+} /* namespace osrm */
+
+#endif /* OSRM_UTIL_GUIDANCE_TOOLKIT_HPP_ */
diff --git a/include/util/lua_util.hpp b/include/util/lua_util.hpp
index 4d4fe17..4af7473 100644
--- a/include/util/lua_util.hpp
+++ b/include/util/lua_util.hpp
@@ -43,8 +43,7 @@ inline bool luaFunctionExists(lua_State *lua_state, const char *name)
 inline void luaAddScriptFolderToLoadPath(lua_State *lua_state, const char *file_name)
 {
     boost::filesystem::path profile_path = boost::filesystem::canonical(file_name);
-    std::string folder = profile_path.parent_path().string();
-    // TODO: This code is most probably not Windows safe since it uses UNIX'ish path delimiters
+    std::string folder = profile_path.parent_path().generic_string();
     const std::string lua_code = "package.path = \"" + folder + "/?.lua;\" .. package.path";
     luaL_dostring(lua_state, lua_code.c_str());
 }
diff --git a/include/util/percent.hpp b/include/util/percent.hpp
index d34a77d..31e328f 100644
--- a/include/util/percent.hpp
+++ b/include/util/percent.hpp
@@ -12,10 +12,10 @@ namespace util
 class Percent
 {
   public:
-    explicit Percent(unsigned max_value, unsigned step = 5) { reinit(max_value, step); }
+    explicit Percent(unsigned max_value, unsigned step = 5) { Reinit(max_value, step); }
 
     // Reinitializes
-    void reinit(unsigned max_value, unsigned step = 5)
+    void Reinit(unsigned max_value, unsigned step = 5)
     {
         m_max_value = max_value;
         m_current_value = 0;
@@ -26,27 +26,27 @@ class Percent
     }
 
     // If there has been significant progress, display it.
-    void printStatus(unsigned current_value)
+    void PrintStatus(unsigned current_value)
     {
         if (current_value >= m_next_threshold)
         {
             m_next_threshold += m_percent_interval;
-            printPercent(current_value / static_cast<double>(m_max_value) * 100.);
+            PrintPercent(current_value / static_cast<double>(m_max_value) * 100.);
         }
         if (current_value + 1 == m_max_value)
             std::cout << " 100%" << std::endl;
     }
 
-    void printIncrement()
+    void PrintIncrement()
     {
         ++m_current_value;
-        printStatus(m_current_value);
+        PrintStatus(m_current_value);
     }
 
-    void printAddition(const unsigned addition)
+    void PrintAddition(const unsigned addition)
     {
         m_current_value += addition;
-        printStatus(m_current_value);
+        PrintStatus(m_current_value);
     }
 
   private:
@@ -58,7 +58,7 @@ class Percent
     unsigned m_step;
 
     // Displays progress.
-    void printPercent(double percent)
+    void PrintPercent(double percent)
     {
         while (percent >= m_last_percent + m_step)
         {
diff --git a/include/util/shared_memory_vector_wrapper.hpp b/include/util/shared_memory_vector_wrapper.hpp
index 74f4a8e..9245d4c 100644
--- a/include/util/shared_memory_vector_wrapper.hpp
+++ b/include/util/shared_memory_vector_wrapper.hpp
@@ -55,6 +55,12 @@ template <typename DataT> class SharedMemoryWrapper
 
     SharedMemoryWrapper(DataT *ptr, std::size_t size) : m_ptr(ptr), m_size(size) {}
 
+    void reset(DataT *ptr, std::size_t size)
+    {
+        m_ptr = ptr;
+        m_size = size;
+    }
+
     DataT &at(const std::size_t index) { return m_ptr[index]; }
 
     const DataT &at(const std::size_t index) const { return m_ptr[index]; }
@@ -101,6 +107,12 @@ template <> class SharedMemoryWrapper<bool>
         return m_ptr[bucket] & (1u << offset);
     }
 
+    void reset(unsigned *ptr, std::size_t size)
+    {
+        m_ptr = ptr;
+        m_size = size;
+    }
+
     std::size_t size() const { return m_size; }
 
     bool empty() const { return 0 == size(); }
diff --git a/include/util/static_rtree.hpp b/include/util/static_rtree.hpp
index 2e5a988..2d9f63d 100644
--- a/include/util/static_rtree.hpp
+++ b/include/util/static_rtree.hpp
@@ -17,6 +17,7 @@
 #include <boost/assert.hpp>
 #include <boost/filesystem.hpp>
 #include <boost/filesystem/fstream.hpp>
+#include <boost/iostreams/device/mapped_file.hpp>
 
 #include <tbb/parallel_for.h>
 #include <tbb/parallel_sort.h>
@@ -42,8 +43,8 @@ namespace util
 template <class EdgeDataT,
           class CoordinateListT = std::vector<Coordinate>,
           bool UseSharedMemory = false,
-          std::uint32_t BRANCHING_FACTOR = 64,
-          std::uint32_t LEAF_NODE_SIZE = 256>
+          std::uint32_t BRANCHING_FACTOR = 128,
+          std::uint32_t LEAF_PAGE_SIZE = 4096>
 class StaticRTree
 {
   public:
@@ -51,6 +52,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);
+
     struct CandidateSegment
     {
         Coordinate fixed_projected_coordinate;
@@ -69,9 +74,11 @@ class StaticRTree
     struct LeafNode
     {
         LeafNode() : object_count(0), objects() {}
-        uint32_t object_count;
+        std::uint32_t object_count;
         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");
 
   private:
     struct WrappedInputElement
@@ -93,7 +100,9 @@ class StaticRTree
         }
     };
 
-    using QueryNodeType = mapbox::util::variant<TreeNode, CandidateSegment>;
+    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
     {
         inline bool operator<(const QueryCandidate &other) const
@@ -107,10 +116,11 @@ class StaticRTree
     };
 
     typename ShM<TreeNode, UseSharedMemory>::vector m_search_tree;
-    uint64_t m_element_count;
-    const std::string m_leaf_node_filename;
-    std::shared_ptr<CoordinateListT> m_coordinate_list;
-    boost::filesystem::ifstream leaves_stream;
+    const CoordinateListT& m_coordinate_list;
+
+    boost::iostreams::mapped_file_source m_leaves_region;
+    // read-only view of leaves
+    typename ShM<const LeafNode, true>::vector m_leaves;
 
   public:
     StaticRTree(const StaticRTree &) = delete;
@@ -122,15 +132,16 @@ class StaticRTree
                          const std::string &tree_node_filename,
                          const std::string &leaf_node_filename,
                          const std::vector<CoordinateT> &coordinate_list)
-        : m_element_count(input_data_vector.size()), m_leaf_node_filename(leaf_node_filename)
+    : m_coordinate_list(coordinate_list)
     {
-        std::vector<WrappedInputElement> input_wrapper_vector(m_element_count);
+        const uint64_t element_count = input_data_vector.size();
+        std::vector<WrappedInputElement> input_wrapper_vector(element_count);
 
         // generate auxiliary vector of hilbert-values
         tbb::parallel_for(
-            tbb::blocked_range<uint64_t>(0, m_element_count),
+            tbb::blocked_range<uint64_t>(0, element_count),
             [&input_data_vector, &input_wrapper_vector,
-             &coordinate_list](const tbb::blocked_range<uint64_t> &range)
+             this](const tbb::blocked_range<uint64_t> &range)
             {
                 for (uint64_t element_counter = range.begin(), end = range.end();
                      element_counter != end; ++element_counter)
@@ -141,11 +152,11 @@ class StaticRTree
                     EdgeDataT const &current_element = input_data_vector[element_counter];
 
                     // Get Hilbert-Value for centroid in mercartor projection
-                    BOOST_ASSERT(current_element.u < coordinate_list.size());
-                    BOOST_ASSERT(current_element.v < coordinate_list.size());
+                    BOOST_ASSERT(current_element.u < m_coordinate_list.size());
+                    BOOST_ASSERT(current_element.v < m_coordinate_list.size());
 
                     Coordinate current_centroid = coordinate_calculation::centroid(
-                        coordinate_list[current_element.u], coordinate_list[current_element.v]);
+                        m_coordinate_list[current_element.u], m_coordinate_list[current_element.v]);
                     current_centroid.lat =
                         FixedLatitude(COORDINATE_PRECISION *
                                       web_mercator::latToY(toFloating(current_centroid.lat)));
@@ -156,7 +167,6 @@ class StaticRTree
 
         // open leaf file
         boost::filesystem::ofstream leaf_node_file(leaf_node_filename, std::ios::binary);
-        leaf_node_file.write((char *)&m_element_count, sizeof(uint64_t));
 
         // sort the hilbert-value representatives
         tbb::parallel_sort(input_wrapper_vector.begin(), input_wrapper_vector.end());
@@ -164,7 +174,7 @@ class StaticRTree
 
         // pack M elements into leaf node and write to leaf file
         uint64_t processed_objects_count = 0;
-        while (processed_objects_count < m_element_count)
+        while (processed_objects_count < element_count)
         {
 
             LeafNode current_leaf;
@@ -172,7 +182,7 @@ class StaticRTree
             for (std::uint32_t current_element_index = 0; LEAF_NODE_SIZE > current_element_index;
                  ++current_element_index)
             {
-                if (m_element_count > (processed_objects_count + current_element_index))
+                if (element_count > (processed_objects_count + current_element_index))
                 {
                     std::uint32_t index_of_next_object =
                         input_wrapper_vector[processed_objects_count + current_element_index]
@@ -185,7 +195,7 @@ class StaticRTree
 
             // 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, coordinate_list);
+                                  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);
@@ -194,6 +204,8 @@ class StaticRTree
             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();
 
         std::uint32_t processing_level = 0;
         while (1 < tree_nodes_in_level.size())
@@ -258,16 +270,16 @@ class StaticRTree
         BOOST_ASSERT_MSG(0 < size_of_tree, "tree empty");
         tree_node_file.write((char *)&size_of_tree, sizeof(std::uint32_t));
         tree_node_file.write((char *)&m_search_tree[0], sizeof(TreeNode) * size_of_tree);
+
+        MapLeafNodesFile(leaf_node_filename);
     }
 
     explicit StaticRTree(const boost::filesystem::path &node_file,
                          const boost::filesystem::path &leaf_file,
-                         const std::shared_ptr<CoordinateListT> coordinate_list)
-        : m_leaf_node_filename(leaf_file.string())
+                         const CoordinateListT& coordinate_list)
+      : m_coordinate_list(coordinate_list)
     {
         // open tree node file and load into RAM.
-        m_coordinate_list = coordinate_list;
-
         if (!boost::filesystem::exists(node_file))
         {
             throw exception("ram index file does not exist");
@@ -287,44 +299,34 @@ class StaticRTree
             tree_node_file.read((char *)&m_search_tree[0], sizeof(TreeNode) * tree_size);
         }
 
-        // open leaf node file and store thread specific pointer
-        if (!boost::filesystem::exists(leaf_file))
-        {
-            throw exception("mem index file does not exist");
-        }
-        if (0 == boost::filesystem::file_size(leaf_file))
-        {
-            throw exception("mem index file is empty");
-        }
-
-        leaves_stream.open(leaf_file, std::ios::binary);
-        leaves_stream.read((char *)&m_element_count, sizeof(uint64_t));
+        MapLeafNodesFile(leaf_file);
     }
 
     explicit StaticRTree(TreeNode *tree_node_ptr,
                          const uint64_t number_of_nodes,
                          const boost::filesystem::path &leaf_file,
-                         std::shared_ptr<CoordinateListT> coordinate_list)
-        : m_search_tree(tree_node_ptr, number_of_nodes), m_leaf_node_filename(leaf_file.string()),
-          m_coordinate_list(std::move(coordinate_list))
+                         const CoordinateListT& coordinate_list)
+        : m_search_tree(tree_node_ptr, number_of_nodes)
+        , m_coordinate_list(coordinate_list)
     {
-        // open leaf node file and store thread specific pointer
-        if (!boost::filesystem::exists(leaf_file))
-        {
-            throw exception("mem index file does not exist");
-        }
-        if (0 == boost::filesystem::file_size(leaf_file))
-        {
-            throw exception("mem index file is empty");
-        }
+        MapLeafNodesFile(leaf_file);
+    }
 
-        leaves_stream.open(leaf_file, std::ios::binary);
-        leaves_stream.read((char *)&m_element_count, sizeof(uint64_t));
+    void MapLeafNodesFile(const boost::filesystem::path &leaf_file)
+    {
+        // open leaf node file and return a pointer to the mapped leaves data
+        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()));
+        }
     }
 
     /* Returns all features inside the bounding box.
        Rectangle needs to be projected!*/
-    std::vector<EdgeDataT> SearchInBox(const Rectangle &search_rectangle)
+    std::vector<EdgeDataT> SearchInBox(const Rectangle &search_rectangle) const
     {
         const Rectangle projected_rectangle{
             search_rectangle.min_lon, search_rectangle.max_lon,
@@ -334,19 +336,17 @@ class StaticRTree
                 web_mercator::latToY(toFloating(FixedLatitude(search_rectangle.max_lat)))})};
         std::vector<EdgeDataT> results;
 
-        std::queue<TreeNode> traversal_queue;
-
-        traversal_queue.push(m_search_tree[0]);
+        std::queue<std::uint32_t> traversal_queue;
+        traversal_queue.push(0);
 
         while (!traversal_queue.empty())
         {
-            auto const current_tree_node = traversal_queue.front();
+            auto const &current_tree_node = m_search_tree[traversal_queue.front()];
             traversal_queue.pop();
 
             if (current_tree_node.child_is_on_disk)
             {
-                LeafNode current_leaf_node;
-                LoadLeafFromDisk(current_tree_node.children[0], current_leaf_node);
+                const LeafNode& current_leaf_node = m_leaves[current_tree_node.children[0]];
 
                 for (const auto i : irange(0u, current_leaf_node.object_count))
                 {
@@ -354,14 +354,14 @@ class StaticRTree
 
                     // we don't need to project the coordinates here,
                     // because we use the unprojected rectangle to test against
-                    const Rectangle bbox{std::min((*m_coordinate_list)[current_edge.u].lon,
-                                                  (*m_coordinate_list)[current_edge.v].lon),
-                                         std::max((*m_coordinate_list)[current_edge.u].lon,
-                                                  (*m_coordinate_list)[current_edge.v].lon),
-                                         std::min((*m_coordinate_list)[current_edge.u].lat,
-                                                  (*m_coordinate_list)[current_edge.v].lat),
-                                         std::max((*m_coordinate_list)[current_edge.u].lat,
-                                                  (*m_coordinate_list)[current_edge.v].lat)};
+                    const Rectangle bbox{std::min(m_coordinate_list[current_edge.u].lon,
+                                                  m_coordinate_list[current_edge.v].lon),
+                                         std::max(m_coordinate_list[current_edge.u].lon,
+                                                  m_coordinate_list[current_edge.v].lon),
+                                         std::min(m_coordinate_list[current_edge.u].lat,
+                                                  m_coordinate_list[current_edge.v].lat),
+                                         std::max(m_coordinate_list[current_edge.u].lat,
+                                                  m_coordinate_list[current_edge.v].lat)};
 
                     // use the _unprojected_ input rectangle here
                     if (bbox.Intersects(search_rectangle))
@@ -382,7 +382,7 @@ class StaticRTree
 
                     if (child_rectangle.Intersects(projected_rectangle))
                     {
-                        traversal_queue.push(m_search_tree[child_id]);
+                        traversal_queue.push(child_id);
                     }
                 }
             }
@@ -391,7 +391,7 @@ class StaticRTree
     }
 
     // Override filter and terminator for the desired behaviour.
-    std::vector<EdgeDataT> Nearest(const Coordinate input_coordinate, const std::size_t max_results)
+    std::vector<EdgeDataT> Nearest(const Coordinate input_coordinate, const std::size_t max_results) const
     {
         return Nearest(input_coordinate,
                        [](const CandidateSegment &)
@@ -407,7 +407,7 @@ class StaticRTree
     // 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)
+    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,17 +415,17 @@ class StaticRTree
 
         // initialize queue with root element
         std::priority_queue<QueryCandidate> traversal_queue;
-        traversal_queue.push(QueryCandidate{0, m_search_tree[0]});
+        traversal_queue.push(QueryCandidate{0, TreeIndex{0}});
 
         while (!traversal_queue.empty())
         {
             QueryCandidate current_query_node = traversal_queue.top();
             traversal_queue.pop();
 
-            if (current_query_node.node.template is<TreeNode>())
+            if (current_query_node.node.template is<TreeIndex>())
             { // current object is a tree node
                 const TreeNode &current_tree_node =
-                    current_query_node.node.template get<TreeNode>();
+                    m_search_tree[current_query_node.node.template get<TreeIndex>().index];
                 if (current_tree_node.child_is_on_disk)
                 {
                     ExploreLeafNode(current_tree_node.children[0], fixed_projected_coordinate,
@@ -439,7 +439,9 @@ class StaticRTree
             else
             {
                 // inspecting an actual road segment
-                auto &current_candidate = current_query_node.node.template get<CandidateSegment>();
+                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
@@ -454,11 +456,11 @@ class StaticRTree
                 {
                     continue;
                 }
-                current_candidate.data.forward_segment_id.enabled &= use_segment.first;
-                current_candidate.data.reverse_segment_id.enabled &= use_segment.second;
+                edge_data.forward_segment_id.enabled &= use_segment.first;
+                edge_data.reverse_segment_id.enabled &= use_segment.second;
 
                 // store phantom node in result vector
-                results.push_back(std::move(current_candidate.data));
+                results.push_back(std::move(edge_data));
             }
         }
 
@@ -470,17 +472,16 @@ class StaticRTree
     void ExploreLeafNode(const std::uint32_t leaf_id,
                          const Coordinate projected_input_coordinate_fixed,
                          const FloatCoordinate &projected_input_coordinate,
-                         QueueT &traversal_queue)
+                         QueueT &traversal_queue) const
     {
-        LeafNode current_leaf_node;
-        LoadLeafFromDisk(leaf_id, current_leaf_node);
+        const LeafNode& current_leaf_node = m_leaves[leaf_id];
 
         // current object represents a block on disk
         for (const auto i : irange(0u, current_leaf_node.object_count))
         {
             const auto &current_edge = current_leaf_node.objects[i];
-            const auto projected_u = web_mercator::fromWGS84((*m_coordinate_list)[current_edge.u]);
-            const auto projected_v = web_mercator::fromWGS84((*m_coordinate_list)[current_edge.v]);
+            const auto projected_u = web_mercator::fromWGS84(m_coordinate_list[current_edge.u]);
+            const auto projected_v = web_mercator::fromWGS84(m_coordinate_list[current_edge.v]);
 
             FloatCoordinate projected_nearest;
             std::tie(std::ignore, projected_nearest) =
@@ -491,16 +492,14 @@ class StaticRTree
                 projected_input_coordinate_fixed, projected_nearest);
             // distance must be non-negative
             BOOST_ASSERT(0. <= squared_distance);
-            traversal_queue.push(
-                QueryCandidate{squared_distance, CandidateSegment{Coordinate{projected_nearest},
-                                                                  std::move(current_edge)}});
+            traversal_queue.push(QueryCandidate{squared_distance, SegmentIndex{leaf_id, i, Coordinate{projected_nearest}}});
         }
     }
 
     template <class QueueT>
     void ExploreTreeNode(const TreeNode &parent,
                          const Coordinate fixed_projected_input_coordinate,
-                         QueueT &traversal_queue)
+                         QueueT &traversal_queue) const
     {
         for (std::uint32_t i = 0; i < parent.child_count; ++i)
         {
@@ -510,25 +509,8 @@ class StaticRTree
             const auto squared_lower_bound_to_element =
                 child_rectangle.GetMinSquaredDist(fixed_projected_input_coordinate);
             traversal_queue.push(
-                QueryCandidate{squared_lower_bound_to_element, m_search_tree[child_id]});
-        }
-    }
-
-    inline void LoadLeafFromDisk(const std::uint32_t leaf_id, LeafNode &result_node)
-    {
-        if (!leaves_stream.is_open())
-        {
-            leaves_stream.open(m_leaf_node_filename, std::ios::in | std::ios::binary);
-        }
-        if (!leaves_stream.good())
-        {
-            throw exception("Could not read from leaf file.");
+                QueryCandidate{squared_lower_bound_to_element, TreeIndex{static_cast<std::uint32_t>(child_id)}});
         }
-        const uint64_t seek_pos = sizeof(uint64_t) + leaf_id * sizeof(LeafNode);
-        leaves_stream.seekg(seek_pos);
-        BOOST_ASSERT_MSG(leaves_stream.good(), "Seeking to position in leaf file failed.");
-        leaves_stream.read((char *)&result_node, sizeof(LeafNode));
-        BOOST_ASSERT_MSG(leaves_stream.good(), "Reading from leaf file failed.");
     }
 
     template <typename CoordinateT>
diff --git a/include/util/typedefs.hpp b/include/util/typedefs.hpp
index b3404bc..f36a241 100644
--- a/include/util/typedefs.hpp
+++ b/include/util/typedefs.hpp
@@ -71,13 +71,10 @@ struct SegmentID
         BOOST_ASSERT(!enabled || id != SPECIAL_SEGMENTID);
     }
 
-    NodeID id : 31;
-    bool enabled : 1;
+    NodeID  id              : 31;
+    std::uint32_t enabled   :  1;
 };
 
-// bit-fields are broken on Windows
-#ifndef _MSC_VER
 static_assert(sizeof(SegmentID) == 4, "SegmentID needs to be 4 bytes big");
-#endif
 
 #endif /* TYPEDEFS_H */
diff --git a/include/util/web_mercator.hpp b/include/util/web_mercator.hpp
index 8c23de1..a2bea88 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(normalized_lat - 90.);
+    return FloatLatitude(std::round(normalized_lat * COORDINATE_PRECISION) / COORDINATE_PRECISION - 90.);
 }
 
 inline double latToY(const FloatLatitude latitude)
@@ -47,6 +47,31 @@ inline double latToY(const FloatLatitude latitude)
     return clamped_y;
 }
 
+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; }
+
+inline double latToYapprox(const FloatLatitude latitude)
+{
+    if (latitude < FloatLatitude(-70.) || latitude > FloatLatitude(70.))
+        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
+    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);
+}
+
 inline FloatLatitude clamp(const FloatLatitude lat)
 {
     return std::max(std::min(lat, FloatLatitude(detail::MAX_LATITUDE)),
@@ -87,7 +112,7 @@ inline double degreeToPixel(FloatLatitude lat, unsigned zoom)
 
 inline FloatCoordinate fromWGS84(const FloatCoordinate &wgs84_coordinate)
 {
-    return {wgs84_coordinate.lon, FloatLatitude{latToY(wgs84_coordinate.lat)}};
+    return {wgs84_coordinate.lon, FloatLatitude{latToYapprox(wgs84_coordinate.lat)}};
 }
 
 inline FloatCoordinate toWGS84(const FloatCoordinate &mercator_coordinate)
diff --git a/profiles/bicycle.lua b/profiles/bicycle.lua
index 644a254..79d7b13 100644
--- a/profiles/bicycle.lua
+++ b/profiles/bicycle.lua
@@ -210,7 +210,7 @@ function way_function (way, result)
 
   -- name
   if ref and "" ~= ref and name and "" ~= name then
-    result.name = name .. ' / ' .. ref
+    result.name = name .. " (" .. ref .. ")"
   elseif ref and "" ~= ref then
     result.name = ref
   elseif name and "" ~= name then
diff --git a/profiles/car.lua b/profiles/car.lua
index ffe62ca..ea50b86 100644
--- a/profiles/car.lua
+++ b/profiles/car.lua
@@ -1,6 +1,7 @@
 -- Car profile
 
 local find_access_tag = require("lib/access").find_access_tag
+local get_destination = require("lib/destination").get_destination
 
 -- Begin of globals
 barrier_whitelist = { ["cattle_grid"] = true, ["border_control"] = true, ["checkpoint"] = true, ["toll_booth"] = true, ["sally_port"] = true, ["gate"] = true, ["lift_gate"] = true, ["no"] = true, ["entrance"] = true }
@@ -11,6 +12,9 @@ access_tags_hierarchy = { "motorcar", "motor_vehicle", "vehicle", "access" }
 service_tag_restricted = { ["parking_aisle"] = true }
 restriction_exception_tags = { "motorcar", "motor_vehicle", "vehicle" }
 
+-- A list of suffixes to suppress in name change instructions
+suffix_list = { "N", "NE", "E", "SE", "S", "SW", "W", "NW", "North", "South", "West", "East" }
+
 speed_profile = {
   ["motorway"] = 90,
   ["motorway_link"] = 45,
@@ -150,6 +154,12 @@ local max = math.max
 
 local speed_reduction = 0.8
 
+function get_name_suffix_list(vector)
+  for index,suffix in ipairs(suffix_list) do
+      vector:Add(suffix)
+  end
+end
+
 function get_exceptions(vector)
   for i,v in ipairs(restriction_exception_tags) do
     vector:Add(v)
@@ -383,6 +393,14 @@ function way_function (way, result)
     (highway == "motorway_link" and oneway ~="no") or
     (highway == "motorway" and oneway ~= "no") then
       result.backward_mode = mode.inaccessible
+
+      -- If we're on a oneway and there is no ref tag, re-use destination tag as ref.
+      local destination = get_destination(way)
+      local has_destination = destination and "" ~= destination
+
+      if has_destination and has_name and not has_ref then
+        result.name = name .. " (" .. destination .. ")"
+      end
     end
   end
 
diff --git a/profiles/foot.lua b/profiles/foot.lua
index 04f16c2..002f06d 100644
--- a/profiles/foot.lua
+++ b/profiles/foot.lua
@@ -151,7 +151,7 @@ function way_function (way, result)
 
    -- name
   if ref and "" ~= ref and name and "" ~= name then
-    result.name = name .. ' / ' .. ref
+    result.name = name .. " (" .. ref .. ")"
     elseif ref and "" ~= ref then
       result.name = ref
   elseif name and "" ~= name then
diff --git a/profiles/lib/destination.lua b/profiles/lib/destination.lua
new file mode 100644
index 0000000..fcdd677
--- /dev/null
+++ b/profiles/lib/destination.lua
@@ -0,0 +1,27 @@
+local Destination = {}
+
+function Destination.get_destination(way)
+  local destination = way:get_value_by_key("destination")
+  local destination_ref = way:get_value_by_key("destination:ref")
+
+  -- Assemble destination as: "A59: Düsseldorf, Köln"
+  --          destination:ref  ^    ^  destination
+
+  local rv = ""
+
+  if destination_ref and destination_ref ~= "" then
+    rv = rv .. string.gsub(destination_ref, ";", ", ")
+  end
+
+  if destination and destination ~= "" then
+      if rv ~= "" then
+          rv = rv .. ": "
+      end
+
+      rv = rv .. string.gsub(destination, ";", ", ")
+  end
+
+  return rv
+end
+
+return Destination
diff --git a/scripts/check_taginfo.py b/scripts/check_taginfo.py
index c2c4c72..e1661b7 100755
--- a/scripts/check_taginfo.py
+++ b/scripts/check_taginfo.py
@@ -28,6 +28,9 @@ with open(profile_path) as f:
 
 n_errors = 0
 for n, line in enumerate(profile):
+    # allow arbitrary suffix lists
+    if line.strip().startswith("suffix_list"):
+        continue
     # ignore comments
     if line.strip().startswith("--"):
         continue
diff --git a/src/benchmarks/static_rtree.cpp b/src/benchmarks/static_rtree.cpp
index 32929eb..42206f0 100644
--- a/src/benchmarks/static_rtree.cpp
+++ b/src/benchmarks/static_rtree.cpp
@@ -26,22 +26,21 @@ constexpr int32_t WORLD_MIN_LON = -180 * COORDINATE_PRECISION;
 constexpr int32_t WORLD_MAX_LON = 180 * COORDINATE_PRECISION;
 
 using RTreeLeaf = extractor::EdgeBasedNode;
-using CoordinateListPtr = std::shared_ptr<std::vector<util::Coordinate>>;
 using BenchStaticRTree =
     util::StaticRTree<RTreeLeaf, util::ShM<util::Coordinate, false>::vector, false>;
 
-CoordinateListPtr loadCoordinates(const boost::filesystem::path &nodes_file)
+std::vector<util::Coordinate> loadCoordinates(const boost::filesystem::path &nodes_file)
 {
     boost::filesystem::ifstream nodes_input_stream(nodes_file, std::ios::binary);
 
     extractor::QueryNode current_node;
     unsigned coordinate_count = 0;
     nodes_input_stream.read((char *)&coordinate_count, sizeof(unsigned));
-    auto coords = std::make_shared<std::vector<Coordinate>>(coordinate_count);
+    std::vector<util::Coordinate> coords(coordinate_count);
     for (unsigned i = 0; i < coordinate_count; ++i)
     {
         nodes_input_stream.read((char *)&current_node, sizeof(extractor::QueryNode));
-        coords->at(i) = util::Coordinate(current_node.lon, current_node.lat);
+        coords[i] = util::Coordinate(current_node.lon, current_node.lat);
     }
     return coords;
 }
diff --git a/src/contractor/contractor.cpp b/src/contractor/contractor.cpp
index 72b2d34..742c561 100644
--- a/src/contractor/contractor.cpp
+++ b/src/contractor/contractor.cpp
@@ -20,6 +20,7 @@
 
 #include <boost/assert.hpp>
 #include <boost/filesystem/fstream.hpp>
+#include <boost/functional/hash.hpp>
 
 #include <tbb/parallel_sort.h>
 
@@ -29,6 +30,7 @@
 #include <memory>
 #include <thread>
 #include <iterator>
+#include <tuple>
 
 namespace std
 {
@@ -40,6 +42,18 @@ template <> struct hash<std::pair<OSMNodeID, OSMNodeID>>
         return static_cast<uint64_t>(k.first) ^ (static_cast<uint64_t>(k.second) << 12);
     }
 };
+
+template <> struct hash<std::tuple<OSMNodeID, OSMNodeID, OSMNodeID>>
+{
+    std::size_t operator()(const std::tuple<OSMNodeID, OSMNodeID, OSMNodeID> &k) const
+    {
+        std::size_t seed = 0;
+        boost::hash_combine(seed, static_cast<uint64_t>(std::get<0>(k)));
+        boost::hash_combine(seed, static_cast<uint64_t>(std::get<1>(k)));
+        boost::hash_combine(seed, static_cast<uint64_t>(std::get<2>(k)));
+        return seed;
+    }
+};
 }
 
 namespace osrm
@@ -71,9 +85,9 @@ int Contractor::Run()
 
     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.node_based_graph_path,
-        config.geometry_path, config.datasource_names_path, config.datasource_indexes_path,
-        config.rtree_leaf_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
 
@@ -129,6 +143,7 @@ std::size_t Contractor::LoadEdgeExpandedGraph(
     const std::string &edge_segment_lookup_filename,
     const std::string &edge_penalty_filename,
     const std::vector<std::string> &segment_speed_filenames,
+    const std::vector<std::string> &turn_penalty_filenames,
     const std::string &nodes_filename,
     const std::string &geometry_filename,
     const std::string &datasource_names_filename,
@@ -139,11 +154,12 @@ std::size_t Contractor::LoadEdgeExpandedGraph(
     boost::filesystem::ifstream input_stream(edge_based_graph_filename, std::ios::binary);
 
     const bool update_edge_weights = !segment_speed_filenames.empty();
+    const bool update_turn_penalties = !turn_penalty_filenames.empty();
 
     boost::filesystem::ifstream edge_segment_input_stream;
     boost::filesystem::ifstream edge_fixed_penalties_input_stream;
 
-    if (update_edge_weights)
+    if (update_edge_weights || update_turn_penalties)
     {
         edge_segment_input_stream.open(edge_segment_lookup_filename, std::ios::binary);
         edge_fixed_penalties_input_stream.open(edge_penalty_filename, std::ios::binary);
@@ -172,37 +188,74 @@ std::size_t Contractor::LoadEdgeExpandedGraph(
 
     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;
 
     // If we update the edge weights, this file will hold the datasource information
     // for each segment
     std::vector<uint8_t> m_geometry_datasource;
 
-    if (update_edge_weights)
+    if (update_edge_weights || update_turn_penalties)
     {
-        uint8_t file_id = 1;
-        for (auto segment_speed_filename : segment_speed_filenames)
+        std::uint8_t segment_file_id = 1;
+        std::uint8_t turn_file_id = 1;
+
+        if (update_edge_weights)
         {
-            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");
-            uint64_t from_node_id{};
-            uint64_t to_node_id{};
-            unsigned speed{};
-            while (csv_in.read_row(from_node_id, to_node_id, speed))
+            for (auto segment_speed_filename : segment_speed_filenames)
             {
-                segment_speed_lookup[std::make_pair(OSMNodeID(from_node_id),
-                                                    OSMNodeID(to_node_id))] =
-                    std::make_pair(speed, file_id);
+                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;
+
+                // 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");
+                }
             }
-            ++file_id;
+        }
 
-            // Check for overflow
-            if (file_id == 0)
+        if (update_turn_penalties)
+        {
+            for (auto turn_penalty_filename : turn_penalty_filenames)
             {
-                throw util::exception(
-                    "Sorry, there's a limit of 254 segment speed files, you supplied too many");
+                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");
+                }
             }
         }
 
@@ -277,16 +330,16 @@ std::size_t Contractor::LoadEdgeExpandedGraph(
 
             using LeafNode = util::StaticRTree<extractor::EdgeBasedNode>::LeafNode;
 
-            std::ifstream leaf_node_file(rtree_leaf_filename, std::ios::binary | std::ios::in);
+            std::ifstream leaf_node_file(rtree_leaf_filename, std::ios::binary | std::ios::in | std::ios::ate);
             if (!leaf_node_file)
             {
                 throw util::exception("Failed to open " + rtree_leaf_filename);
             }
-            uint64_t m_element_count;
-            leaf_node_file.read((char *)&m_element_count, sizeof(uint64_t));
+            std::size_t leaf_nodes_count = leaf_node_file.tellg() / sizeof(LeafNode);
+            leaf_node_file.seekg(0, std::ios::beg);
 
             LeafNode current_node;
-            while (m_element_count > 0)
+            while (leaf_nodes_count > 0)
             {
                 leaf_node_file.read(reinterpret_cast<char *>(&current_node), sizeof(current_node));
 
@@ -358,10 +411,9 @@ std::size_t Contractor::LoadEdgeExpandedGraph(
                             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]);
+                            v = &(internal_to_external_node_map
+                                      [m_geometry_list[reverse_begin + rev_segment_position]
+                                           .node_id]);
                         }
                         const double segment_length =
                             util::coordinate_calculation::greatCircleDistance(
@@ -383,7 +435,7 @@ std::size_t Contractor::LoadEdgeExpandedGraph(
                         }
                     }
                 }
-                m_element_count -= current_node.object_count;
+                --leaf_nodes_count;
             }
         }
 
@@ -412,8 +464,7 @@ std::size_t Contractor::LoadEdgeExpandedGraph(
         std::ofstream datasource_stream(datasource_indexes_filename, std::ios::binary);
         if (!datasource_stream)
         {
-            throw util::exception("Failed to open " + datasource_indexes_filename +
-                                  " for writing");
+            throw util::exception("Failed to open " + datasource_indexes_filename + " for writing");
         }
         auto number_of_datasource_entries = m_geometry_datasource.size();
         datasource_stream.write(reinterpret_cast<const char *>(&number_of_datasource_entries),
@@ -429,8 +480,7 @@ std::size_t Contractor::LoadEdgeExpandedGraph(
         std::ofstream datasource_stream(datasource_names_filename, std::ios::binary);
         if (!datasource_stream)
         {
-            throw util::exception("Failed to open " + datasource_names_filename +
-                                  " for writing");
+            throw util::exception("Failed to open " + datasource_names_filename + " for writing");
         }
         datasource_stream << "lua profile" << std::endl;
         for (auto const &name : segment_speed_filenames)
@@ -445,7 +495,7 @@ std::size_t Contractor::LoadEdgeExpandedGraph(
     {
         extractor::EdgeBasedEdge inbuffer;
         input_stream.read((char *)&inbuffer, sizeof(extractor::EdgeBasedEdge));
-        if (update_edge_weights)
+        if (update_edge_weights || update_turn_penalties)
         {
             // Processing-time edge updates
             unsigned fixed_penalty;
@@ -463,6 +513,7 @@ std::size_t Contractor::LoadEdgeExpandedGraph(
             OSMNodeID this_osm_node_id;
             double segment_length;
             int segment_weight;
+            int compressed_edge_nodes = static_cast<int>(num_osm_nodes);
             --num_osm_nodes;
             for (; num_osm_nodes != 0; --num_osm_nodes)
             {
@@ -494,7 +545,34 @@ std::size_t Contractor::LoadEdgeExpandedGraph(
                 previous_osm_node_id = this_osm_node_id;
             }
 
-            inbuffer.weight = fixed_penalty + new_weight;
+            OSMNodeID from_id;
+            OSMNodeID via_id;
+            OSMNodeID to_id;
+            edge_fixed_penalties_input_stream.read(reinterpret_cast<char *>(&from_id),
+                                                   sizeof(from_id));
+            edge_fixed_penalties_input_stream.read(reinterpret_cast<char *>(&via_id),
+                                                   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));
+            if (turn_iter != turn_penalty_lookup.end())
+            {
+                int new_turn_weight = static_cast<int>(turn_iter->second.first * 10);
+
+                if (new_turn_weight + new_weight < compressed_edge_nodes)
+                {
+                    util::SimpleLogger().Write(logWARNING)
+                        << "turn penalty " << turn_iter->second.first << " for turn " << from_id
+                        << ", " << via_id << ", " << to_id
+                        << " is too negative: clamping turn weight to " << compressed_edge_nodes;
+                }
+
+                inbuffer.weight = std::max(new_turn_weight + new_weight, compressed_edge_nodes);
+            }
+            else
+            {
+                inbuffer.weight = fixed_penalty + new_weight;
+            }
         }
 
         edge_based_edge_list.emplace_back(std::move(inbuffer));
diff --git a/src/engine/api/json_factory.cpp b/src/engine/api/json_factory.cpp
index 4d5ff9b..c23ca9c 100644
--- a/src/engine/api/json_factory.cpp
+++ b/src/engine/api/json_factory.cpp
@@ -33,12 +33,32 @@ const constexpr char *modifier_names[] = {"uturn",    "sharp right", "right", "s
 
 // 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",    "no turn", "invalid",    "new name",    "continue",    "turn",
-    "turn",       "turn",    "turn",       "turn",        "merge",       "ramp",
-    "ramp",       "ramp",    "ramp",       "ramp",        "fork",        "end of road",
-    "roundabout", "invalid", "roundabout", "invalid",     "rotary",      "invalid",
-    "rotary",     "invalid", "invalid",    "restriction", "notification"};
+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 *waypoint_type_names[] = {"invalid", "arrive", "depart"};
 
 // Check whether to include a modifier in the result of the API
@@ -128,7 +148,9 @@ 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);
 
diff --git a/src/engine/douglas_peucker.cpp b/src/engine/douglas_peucker.cpp
index a3eb762..7237fef 100644
--- a/src/engine/douglas_peucker.cpp
+++ b/src/engine/douglas_peucker.cpp
@@ -17,30 +17,19 @@ namespace osrm
 namespace engine
 {
 
-struct FastPerpendicularDistance
+// Normed to the thresholds table
+std::uint64_t fastPerpendicularDistance(const util::FloatCoordinate &projected_start,
+                                        const util::FloatCoordinate &projected_target,
+                                        const util::FloatCoordinate &projected)
 {
-    FastPerpendicularDistance(const util::Coordinate start, const util::Coordinate target)
-        : projected_start(util::web_mercator::fromWGS84(start)),
-          projected_target(util::web_mercator::fromWGS84(target))
-    {
-    }
-
-    // Normed to the thresholds table
-    std::uint64_t operator()(const util::Coordinate coordinate) const
-    {
-        auto projected = util::web_mercator::fromWGS84(coordinate);
-        util::FloatCoordinate projected_point_on_segment;
-        std::tie(std::ignore, projected_point_on_segment) =
-            util::coordinate_calculation::projectPointOnSegment(projected_start, projected_target,
-                                                                projected);
-        auto squared_distance = util::coordinate_calculation::squaredEuclideanDistance(projected,
-                                                                      projected_point_on_segment);
-        return squared_distance;
-    }
-
-    const util::FloatCoordinate projected_start;
-    const util::FloatCoordinate projected_target;
-};
+    util::FloatCoordinate projected_point_on_segment;
+    std::tie(std::ignore, projected_point_on_segment) =
+        util::coordinate_calculation::projectPointOnSegment(projected_start, projected_target,
+                                                            projected);
+    auto squared_distance = util::coordinate_calculation::squaredEuclideanDistance(
+        projected, projected_point_on_segment);
+    return squared_distance;
+}
 
 std::vector<util::Coordinate> douglasPeucker(std::vector<util::Coordinate>::const_iterator begin,
                                              std::vector<util::Coordinate>::const_iterator end,
@@ -55,6 +44,12 @@ std::vector<util::Coordinate> douglasPeucker(std::vector<util::Coordinate>::cons
         return {};
     }
 
+    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::vector<bool> is_necessary(size, false);
     BOOST_ASSERT(is_necessary.size() >= 2);
     is_necessary.front() = true;
@@ -80,13 +75,13 @@ std::vector<util::Coordinate> douglasPeucker(std::vector<util::Coordinate>::cons
         std::uint64_t max_distance = 0;
         auto farthest_entry_index = pair.second;
 
-        FastPerpendicularDistance perpendicular_distance(begin[pair.first], begin[pair.second]);
-
         // sweep over range to find the maximum
         for (auto idx = pair.first + 1; idx != pair.second; ++idx)
         {
             using namespace util::coordinate_calculation;
-            const auto distance = perpendicular_distance(begin[idx]);
+            const auto distance = fastPerpendicularDistance(projected_coordinates[pair.first],
+                                                            projected_coordinates[pair.second],
+                                                            projected_coordinates[idx]);
             // found new feasible maximum?
             if (distance > max_distance &&
                 distance > detail::DOUGLAS_PEUCKER_THRESHOLDS[zoom_level])
@@ -122,6 +117,7 @@ std::vector<util::Coordinate> douglasPeucker(std::vector<util::Coordinate>::cons
             simplified_geometry.push_back(begin[idx]);
         }
     }
+
     return simplified_geometry;
 }
 } // ns engine
diff --git a/src/engine/guidance/assemble_overview.cpp b/src/engine/guidance/assemble_overview.cpp
index aa25411..6a59249 100644
--- a/src/engine/guidance/assemble_overview.cpp
+++ b/src/engine/guidance/assemble_overview.cpp
@@ -46,7 +46,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);
@@ -82,7 +82,7 @@ std::vector<util::Coordinate> assembleOverview(const std::vector<LegGeometry> &l
     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/post_processing.cpp b/src/engine/guidance/post_processing.cpp
index a9bafc3..e52ac37 100644
--- a/src/engine/guidance/post_processing.cpp
+++ b/src/engine/guidance/post_processing.cpp
@@ -4,6 +4,8 @@
 #include "engine/guidance/assemble_steps.hpp"
 #include "engine/guidance/toolkit.hpp"
 
+#include "util/guidance/toolkit.hpp"
+
 #include <boost/assert.hpp>
 #include <boost/range/algorithm_ext/erase.hpp>
 
@@ -17,6 +19,8 @@
 using TurnInstruction = osrm::extractor::guidance::TurnInstruction;
 using TurnType = osrm::extractor::guidance::TurnType;
 using DirectionModifier = osrm::extractor::guidance::DirectionModifier;
+using osrm::util::guidance::angularDeviation;
+using osrm::util::guidance::getTurnDirection;
 
 namespace osrm
 {
@@ -25,12 +29,36 @@ namespace engine
 namespace guidance
 {
 
-namespace detail
+namespace
 {
-bool canMergeTrivially(const RouteStep &destination, const RouteStep &source)
+
+// invalidate a step and set its content to nothing
+void invalidateStep(RouteStep &step) { step = getInvalidRouteStep(); }
+
+void print(const std::vector<RouteStep> &steps)
 {
-    return destination.maneuver.exit == 0 && destination.name_id == source.name_id &&
-           isSilent(source.maneuver.instruction);
+    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;
+    }
 }
 
 RouteStep forwardInto(RouteStep destination, const RouteStep &source)
@@ -50,17 +78,25 @@ void fixFinalRoundabout(std::vector<RouteStep> &steps)
          --propagation_index)
     {
         auto &propagation_step = steps[propagation_index];
-        if (propagation_index == 0 || entersRoundabout(propagation_step.maneuver.instruction))
+        if (entersRoundabout(propagation_step.maneuver.instruction))
         {
             propagation_step.maneuver.exit = 0;
             propagation_step.geometry_end = steps.back().geometry_begin;
 
+            // remember the current name as rotary name in tha case we end in a rotary
             if (propagation_step.maneuver.instruction.type == TurnType::EnterRotary ||
                 propagation_step.maneuver.instruction.type == TurnType::EnterRotaryAtExit)
                 propagation_step.rotary_name = propagation_step.name;
 
-            break;
+            else if (propagation_step.maneuver.instruction.type ==
+                         TurnType::EnterRoundaboutIntersection ||
+                     propagation_step.maneuver.instruction.type ==
+                         TurnType::EnterRoundaboutIntersectionAtExit)
+                propagation_step.maneuver.instruction.type = TurnType::EnterRoundabout;
+
+            return;
         }
+        // accumulate turn data into the enter instructions
         else if (propagation_step.maneuver.instruction.type == TurnType::StayOnRoundabout)
         {
             // TODO this operates on the data that is in the instructions.
@@ -80,14 +116,17 @@ bool setUpRoundabout(RouteStep &step)
     // Special case handling, if an entry is directly tied to an exit
     const auto instruction = step.maneuver.instruction;
     if (instruction.type == TurnType::EnterRotaryAtExit ||
-        instruction.type == TurnType::EnterRoundaboutAtExit)
+        instruction.type == TurnType::EnterRoundaboutAtExit ||
+        instruction.type == TurnType::EnterRoundaboutIntersectionAtExit)
     {
         step.maneuver.exit = 1;
         // prevent futher special case handling of these two.
         if (instruction.type == TurnType::EnterRotaryAtExit)
             step.maneuver.instruction.type = TurnType::EnterRotary;
-        else
+        else if (instruction.type == TurnType::EnterRoundaboutAtExit)
             step.maneuver.instruction.type = TurnType::EnterRoundabout;
+        else
+            step.maneuver.instruction.type = TurnType::EnterRoundaboutIntersection;
     }
 
     if (leavesRoundabout(instruction))
@@ -97,8 +136,10 @@ bool setUpRoundabout(RouteStep &step)
         // prevent futher special case handling of these two.
         if (instruction.type == TurnType::EnterAndExitRotary)
             step.maneuver.instruction.type = TurnType::EnterRotary;
-        else
+        else if (instruction.type == TurnType::EnterAndExitRoundabout)
             step.maneuver.instruction.type = TurnType::EnterRoundabout;
+        else
+            step.maneuver.instruction.type = TurnType::EnterRoundaboutIntersection;
         return false;
     }
     else
@@ -116,28 +157,35 @@ void closeOffRoundabout(const bool on_roundabout,
     if (!on_roundabout)
     {
 
-        // We reached a special case that requires the addition of a special route step in
-        // the beginning.
-        // We started in a roundabout, so to announce the exit, we move use the exit
-        // instruction and
-        // move it right to the beginning to make sure to immediately announce the exit.
+        // We reached a special case that requires the addition of a special route step in the
+        // beginning. We started in a roundabout, so to announce the exit, we move use the exit
+        // instruction and move it right to the beginning to make sure to immediately announce the
+        // exit.
         BOOST_ASSERT(leavesRoundabout(steps[1].maneuver.instruction) ||
                      steps[1].maneuver.instruction.type == TurnType::StayOnRoundabout);
         steps[0].geometry_end = 1;
-        steps[1] = detail::forwardInto(steps[1], steps[0]);
+        steps[1] = forwardInto(steps[1], steps[0]);
         steps[0].duration = 0;
         steps[0].distance = 0;
-        steps[1].maneuver.instruction.type = step.maneuver.instruction.type == TurnType::ExitRotary
-                                                 ? TurnType::EnterRotary
-                                                 : TurnType::EnterRoundabout;
+        const auto exitToEnter = [](const TurnType type) {
+            if (TurnType::ExitRotary == type)
+                return TurnType::EnterRotary;
+            // if we do not enter the roundabout Intersection, we cannot treat the full traversal as
+            // a turn. So we switch it up to the roundabout type
+            else if (type == TurnType::ExitRoundaboutIntersection)
+                return TurnType::EnterRoundabout;
+            else
+                return TurnType::EnterRoundabout;
+        };
+        steps[1].maneuver.instruction.type = exitToEnter(step.maneuver.instruction.type);
         if (steps[1].maneuver.instruction.type == TurnType::EnterRotary)
             steps[1].rotary_name = steps[0].name;
     }
 
-    // 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.
+    // 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;
     if (step_index > 1)
     {
         // The very first route-step is head, so we cannot iterate past that one
@@ -145,18 +193,55 @@ void closeOffRoundabout(const bool on_roundabout,
              --propagation_index)
         {
             auto &propagation_step = steps[propagation_index];
-            propagation_step = detail::forwardInto(propagation_step, steps[propagation_index + 1]);
+            propagation_step = forwardInto(propagation_step, steps[propagation_index + 1]);
             if (entersRoundabout(propagation_step.maneuver.instruction))
             {
-                // TODO at this point, we can remember the additional name for a rotary
-                // This requires some initial thought on the data format, though
-
                 propagation_step.maneuver.exit = step.maneuver.exit;
                 propagation_step.geometry_end = step.geometry_end;
                 // remember rotary name
                 if (propagation_step.maneuver.instruction.type == TurnType::EnterRotary ||
                     propagation_step.maneuver.instruction.type == TurnType::EnterRotaryAtExit)
+                {
                     propagation_step.rotary_name = propagation_step.name;
+                }
+                else if (propagation_step.maneuver.instruction.type ==
+                             TurnType::EnterRoundaboutIntersection ||
+                         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);
+
+                    propagation_step.maneuver.instruction.direction_modifier =
+                        ::osrm::util::guidance::getTurnDirection(angle);
+                }
 
                 propagation_step.name = step.name;
                 propagation_step.name_id = step.name_id;
@@ -174,30 +259,161 @@ void closeOffRoundabout(const bool on_roundabout,
         step.maneuver.instruction = TurnInstruction::NO_TURN();
     }
 }
-} // namespace detail
 
-void print(const std::vector<RouteStep> &steps)
+// elongate a step by another. the data is added either at the front, or the back
+RouteStep elongate(RouteStep step, const RouteStep &by_step)
 {
-    std::cout << "Path\n";
-    int segment = 0;
-    for (const auto &step : steps)
+    BOOST_ASSERT(step.mode == by_step.mode);
+
+    step.duration += by_step.duration;
+    step.distance += by_step.distance;
+
+    if (step.geometry_end == by_step.geometry_begin + 1)
     {
-        const auto type = static_cast<int>(step.maneuver.instruction.type);
-        const auto modifier = static_cast<int>(step.maneuver.instruction.direction_modifier);
+        step.geometry_end = by_step.geometry_end;
 
-        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() << " [";
+        // 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());
+    }
+    else
+    {
+        BOOST_ASSERT(step.maneuver.waypoint_type == WaypointType::None &&
+                     by_step.maneuver.waypoint_type == WaypointType::None);
+        BOOST_ASSERT(by_step.geometry_end == step.geometry_begin + 1);
+        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());
+    }
+    return step;
+}
 
-        for (auto intersection : step.maneuver.intersections)
-            std::cout << "(" << intersection.duration << " " << intersection.distance << ")";
+// 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,
+                    const std::size_t step_index)
+{
+    BOOST_ASSERT(step_index < steps.size());
+    BOOST_ASSERT(one_back_index < steps.size());
+    const auto &current_step = steps[step_index];
+
+    const auto &one_back_step = steps[one_back_index];
+
+    const auto bearingsAreReversed = [](const double bearing_in, const double bearing_out) {
+        // Nearly perfectly reversed angles have a difference close to 180 degrees (straight)
+        return angularDeviation(bearing_in, bearing_out) > 170;
+    };
+
+    // Very Short New Name
+    if (TurnType::NewName == one_back_step.maneuver.instruction.type)
+    {
+        BOOST_ASSERT(two_back_index < steps.size());
+        if (one_back_step.mode == steps[two_back_index].mode)
+        {
+            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)
+    {
+        if (one_back_step.mode == current_step.mode)
+        {
+            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[step_index].maneuver.instruction.type = TurnType::Turn;
+            }
+        }
+    }
+    // Potential U-Turn
+    else if (bearingsAreReversed(one_back_step.maneuver.bearing_before,
+                                 current_step.maneuver.bearing_after))
 
-        std::cout << "] name[" << step.name_id << "]: " << step.name << std::endl;
+    {
+        BOOST_ASSERT(two_back_index < steps.size());
+        // the simple case is a u-turn that changes directly into the in-name again
+        const bool direct_u_turn = steps[two_back_index].name == current_step.name;
+
+        // however, we might also deal with a dual-collapse scenario in which we have to
+        // 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);
+        const bool u_turn_with_name_change =
+            collapsable(current_step) && continues_with_name_change &&
+            steps[step_index + 1].name == steps[two_back_index].name;
+
+        if (direct_u_turn || u_turn_with_name_change)
+        {
+            steps[one_back_index] = elongate(std::move(steps[one_back_index]), steps[step_index]);
+            invalidateStep(steps[step_index]);
+            if (u_turn_with_name_change)
+            {
+                steps[one_back_index] =
+                    elongate(std::move(steps[one_back_index]), steps[step_index + 1]);
+                invalidateStep(steps[step_index + 1]); // will be skipped due to the
+                                                       // continue statement at the
+                                                       // beginning of this function
+            }
+
+            steps[one_back_index].name = steps[two_back_index].name;
+            steps[one_back_index].maneuver.instruction.type = TurnType::Continue;
+            steps[one_back_index].maneuver.instruction.direction_modifier =
+                DirectionModifier::UTurn;
+        }
     }
 }
 
+} // namespace
+
+// Post processing can invalidate some instructions. For example StayOnRoundabout
+// is turned into exit counts. These instructions are removed by the following function
+
+std::vector<RouteStep> removeNoTurnInstructions(std::vector<RouteStep> steps)
+{
+    // finally clean up the post-processed instructions.
+    // Remove all invalid instructions from the set of instructions.
+    // An instruction is invalid, if its NO_TURN and has WaypointType::None.
+    // Two valid NO_TURNs exist in each leg in the form of Depart/Arrive
+
+    // keep valid instructions
+    const auto not_is_valid = [](const RouteStep &step) {
+        return step.maneuver.instruction == TurnInstruction::NO_TURN() &&
+               step.maneuver.waypoint_type == WaypointType::None;
+    };
+
+    boost::remove_erase_if(steps, not_is_valid);
+
+    return steps;
+}
+
 // Every Step Maneuver consists of the information until the turn.
 // This list contains a set of instructions, called silent, which should
 // not be part of the final output.
@@ -224,7 +440,7 @@ std::vector<RouteStep> postProcess(std::vector<RouteStep> steps)
         into.maneuver.intersections.push_back(
             {last_step.duration, last_step.distance, intersection.maneuver.location});
 
-        return detail::forwardInto(std::move(into), intersection);
+        return forwardInto(std::move(into), intersection);
     };
 
     // count the exits forward. if enter/exit roundabout happen both, no further treatment is
@@ -239,7 +455,7 @@ std::vector<RouteStep> postProcess(std::vector<RouteStep> steps)
         if (entersRoundabout(instruction))
         {
             last_valid_instruction = step_index;
-            has_entered_roundabout = detail::setUpRoundabout(step);
+            has_entered_roundabout = setUpRoundabout(step);
 
             if (has_entered_roundabout && step_index + 1 < steps.size())
                 steps[step_index + 1].maneuver.exit = step.maneuver.exit;
@@ -261,7 +477,7 @@ std::vector<RouteStep> postProcess(std::vector<RouteStep> steps)
                 // the first valid instruction
                 last_valid_instruction = 1;
             }
-            detail::closeOffRoundabout(has_entered_roundabout, steps, step_index);
+            closeOffRoundabout(has_entered_roundabout, steps, step_index);
             has_entered_roundabout = false;
             on_roundabout = false;
         }
@@ -284,31 +500,101 @@ std::vector<RouteStep> postProcess(std::vector<RouteStep> steps)
     // A roundabout without exit translates to enter-roundabout.
     if (has_entered_roundabout || on_roundabout)
     {
-        detail::fixFinalRoundabout(steps);
+        fixFinalRoundabout(steps);
     }
 
-    // finally clean up the post-processed instructions.
-    // Remove all invalid instructions from the set of instructions.
-    // An instruction is invalid, if its NO_TURN and has WaypointType::None.
-    // Two valid NO_TURNs exist in each leg in the form of Depart/Arrive
+    return removeNoTurnInstructions(std::move(steps));
+}
 
-    // keep valid instructions
-    const auto not_is_valid = [](const RouteStep &step) {
-        return step.maneuver.instruction == TurnInstruction::NO_TURN() &&
-               step.maneuver.waypoint_type == WaypointType::None;
+// Post Processing to collapse unnecessary sets of combined instructions into a single one
+std::vector<RouteStep> collapseTurns(std::vector<RouteStep> steps)
+{
+    if (steps.size() <= 2)
+        return steps;
+
+    // Get the previous non-invalid instruction
+    const auto getPreviousIndex = [&steps](std::size_t index) {
+        BOOST_ASSERT(index > 0);
+        BOOST_ASSERT(index < steps.size());
+        --index;
+        while (index > 0 && steps[index].maneuver.instruction == TurnInstruction::NO_TURN())
+            --index;
+
+        return index;
     };
 
-    boost::remove_erase_if(steps, not_is_valid);
+    // 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)
+        {
+            steps[0] = elongate(std::move(steps[0]), steps[1]);
+            invalidateStep(steps[1]);
+        }
+    }
 
-    return steps;
+    // first and last instructions are waypoints that cannot be collapsed
+    for (std::size_t step_index = 2; step_index < steps.size(); ++step_index)
+    {
+        const auto &current_step = steps[step_index];
+        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());
+
+        // 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)
+        {
+            steps[one_back_index] = elongate(std::move(steps[one_back_index]), steps[step_index]);
+            invalidateStep(steps[step_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)
+        {
+            // valid due to step_index starting at 2
+            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)
+                {
+                    steps[two_back_index] =
+                        elongate(elongate(std::move(steps[two_back_index]), steps[one_back_index]),
+                                 steps[step_index]);
+                    invalidateStep(steps[one_back_index]);
+                    invalidateStep(steps[step_index]);
+                }
+                // TODO discuss: we could think about changing the new-name to a pure notification
+                // about mode changes
+            }
+        }
+        else if (collapsable(one_back_step))
+        {
+            // check for one of the multiple collapse scenarios and, if possible, collapse the turn
+            collapseTurnAt(steps, two_back_index, one_back_index, step_index);
+        }
+    }
+    return removeNoTurnInstructions(std::move(steps));
 }
 
 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.
+    // 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.
@@ -316,14 +602,16 @@ void trimShortSegments(std::vector<RouteStep> &steps, LegGeometry &geometry)
     if (steps.size() < 2 || geometry.locations.size() <= 2)
         return;
 
-    // if phantom node is located at the connection of two segments, either one can be selected as
+    // if phantom node is located at the connection of two segments, either one can be selected
+    // as
     // turn
     //
     // a --- b
     //       |
     //       c
     //
-    // If a route from b to c is requested, both a--b and b--c could be selected as start segment.
+    // If a route from b to c is requested, both a--b and b--c could be selected as start
+    // segment.
     // In case of a--b, we end up with an unwanted turn saying turn-right onto b-c.
     // These cases start off with an initial segment which is of zero length.
     // We have to be careful though, since routing that starts in a roundabout has a valid.
@@ -357,12 +645,12 @@ void trimShortSegments(std::vector<RouteStep> &steps, LegGeometry &geometry)
             const auto &current_depart = steps.front();
             auto &designated_depart = *(steps.begin() + 1);
 
-            // FIXME this is required to be consistent with the route durations. The initial turn is
-            // not actually part of the route, though
+            // FIXME this is required to be consistent with the route durations. The initial
+            // turn is not actually part of the route, though
             designated_depart.duration += current_depart.duration;
 
-            // update initial turn direction/bearings. Due to the duplicated first coordinate, the
-            // initial bearing is invalid
+            // 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);
 
@@ -394,8 +682,8 @@ void trimShortSegments(std::vector<RouteStep> &steps, LegGeometry &geometry)
 
     BOOST_ASSERT(geometry.locations.size() >= steps.size());
     auto &next_to_last_step = *(steps.end() - 2);
-    // in the end, the situation with the roundabout cannot occur. As a result, we can remove all
-    // zero-length instructions
+    // in the end, the situation with the roundabout cannot occur. As a result, we can remove
+    // all zero-length instructions
     if (next_to_last_step.distance <= 1)
     {
         geometry.locations.pop_back();
diff --git a/src/engine/plugins/trip.cpp b/src/engine/plugins/trip.cpp
index 35c6514..4303a47 100644
--- a/src/engine/plugins/trip.cpp
+++ b/src/engine/plugins/trip.cpp
@@ -84,9 +84,9 @@ SCC_Component SplitUnaccessibleLocations(const std::size_t number_of_locations,
     auto wrapper = std::make_shared<util::MatrixGraphWrapper<EdgeWeight>>(result_table.GetTable(),
                                                                           number_of_locations);
     auto scc = extractor::TarjanSCC<util::MatrixGraphWrapper<EdgeWeight>>(wrapper);
-    scc.run();
+    scc.Run();
 
-    const auto number_of_components = scc.get_number_of_components();
+    const auto number_of_components = scc.GetNumberOfComponents();
 
     std::vector<std::size_t> range_insertion;
     std::vector<std::size_t> range;
@@ -100,22 +100,21 @@ SCC_Component SplitUnaccessibleLocations(const std::size_t number_of_locations,
     {
         range_insertion.push_back(prefix);
         range.push_back(prefix);
-        prefix += scc.get_component_size(j);
+        prefix += scc.GetComponentSize(j);
     }
     // senitel
     range.push_back(components.size());
 
     for (std::size_t i = 0; i < number_of_locations; ++i)
     {
-        components[range_insertion[scc.get_component_id(i)]] = i;
-        ++range_insertion[scc.get_component_id(i)];
+        components[range_insertion[scc.GetComponentID(i)]] = i;
+        ++range_insertion[scc.GetComponentID(i)];
     }
 
     return SCC_Component(std::move(components), std::move(range));
 }
 
 InternalRouteResult TripPlugin::ComputeRoute(const std::vector<PhantomNode> &snapped_phantoms,
-                                             const api::TripParameters &parameters,
                                              const std::vector<NodeID> &trip)
 {
     InternalRouteResult min_route;
@@ -135,7 +134,7 @@ InternalRouteResult TripPlugin::ComputeRoute(const std::vector<PhantomNode> &sna
     }
     BOOST_ASSERT(min_route.segment_end_coordinates.size() == trip.size());
 
-    shortest_path(min_route.segment_end_coordinates, parameters.continue_straight, min_route);
+    shortest_path(min_route.segment_end_coordinates, {false}, min_route);
 
     BOOST_ASSERT_MSG(min_route.shortest_path_length < INVALID_EDGE_WEIGHT, "unroutable route");
     return min_route;
@@ -232,7 +231,7 @@ Status TripPlugin::HandleRequest(const api::TripParameters &parameters,
     routes.reserve(trips.size());
     for (const auto &trip : trips)
     {
-        routes.push_back(ComputeRoute(snapped_phantoms, parameters, trip));
+        routes.push_back(ComputeRoute(snapped_phantoms, trip));
     }
 
     api::TripAPI trip_api{BasePlugin::facade, parameters};
diff --git a/src/extractor/compressed_edge_container.cpp b/src/extractor/compressed_edge_container.cpp
index 70b1f77..878d749 100644
--- a/src/extractor/compressed_edge_container.cpp
+++ b/src/extractor/compressed_edge_container.cpp
@@ -250,12 +250,28 @@ CompressedEdgeContainer::GetBucketReference(const EdgeID edge_id) const
     return m_compressed_geometries.at(index);
 }
 
+// Since all edges are technically in the compressed geometry container,
+// regardless of whether a compressed edge actually contains multiple
+// original segments, we use 'Trivial' here to describe compressed edges
+// that only contain one original segment
+bool CompressedEdgeContainer::IsTrivial(const EdgeID edge_id) const
+{
+    const auto &bucket = GetBucketReference(edge_id);
+    return bucket.size() == 1;
+}
+
 NodeID CompressedEdgeContainer::GetFirstEdgeTargetID(const EdgeID edge_id) const
 {
     const auto &bucket = GetBucketReference(edge_id);
-    BOOST_ASSERT(bucket.size() >= 2);
+    BOOST_ASSERT(bucket.size() >= 1);
     return bucket.front().node_id;
 }
+NodeID CompressedEdgeContainer::GetLastEdgeTargetID(const EdgeID edge_id) const
+{
+    const auto &bucket = GetBucketReference(edge_id);
+    BOOST_ASSERT(bucket.size() >= 1);
+    return bucket.back().node_id;
+}
 NodeID CompressedEdgeContainer::GetLastEdgeSourceID(const EdgeID edge_id) const
 {
     const auto &bucket = GetBucketReference(edge_id);
diff --git a/src/extractor/edge_based_graph_factory.cpp b/src/extractor/edge_based_graph_factory.cpp
index 6264fcd..51f67d5 100644
--- a/src/extractor/edge_based_graph_factory.cpp
+++ b/src/extractor/edge_based_graph_factory.cpp
@@ -9,6 +9,7 @@
 #include "util/simple_logger.hpp"
 #include "util/timing_util.hpp"
 
+#include "extractor/suffix_table.hpp"
 #include "extractor/guidance/toolkit.hpp"
 
 #include <boost/assert.hpp>
@@ -122,7 +123,8 @@ 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};
@@ -243,7 +245,7 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedNodes()
     {
         BOOST_ASSERT(node_u != SPECIAL_NODEID);
         BOOST_ASSERT(node_u < m_node_based_graph->GetNumberOfNodes());
-        progress.printStatus(node_u);
+        progress.PrintStatus(node_u);
         for (EdgeID e1 : m_node_based_graph->GetAdjacentEdgeRange(node_u))
         {
             const EdgeData &edge_data = m_node_based_graph->GetEdgeData(e1);
@@ -320,11 +322,13 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
     // Three nested loop look super-linear, but we are dealing with a (kind of)
     // 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);
+                                         m_barrier_nodes, m_compressed_edge_container, name_table,
+                                         street_name_suffix_table);
     for (const auto node_u : util::irange(0u, m_node_based_graph->GetNumberOfNodes()))
     {
-        progress.printStatus(node_u);
+        progress.PrintStatus(node_u);
         for (const EdgeID edge_from_u : m_node_based_graph->GetAdjacentEdgeRange(node_u))
         {
             if (m_node_based_graph->GetEdgeData(edge_from_u).reversed)
@@ -336,7 +340,6 @@ 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);
-
             for (const auto turn : possible_turns)
             {
                 const double turn_angle = turn.angle;
@@ -431,6 +434,36 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
                                                 sizeof(target_node.weight));
                         previous = target_node.node_id;
                     }
+
+                    // We also now write out the mapping between the edge-expanded edges and the
+                    // original nodes. Since each edge represents a possible maneuver, external
+                    // programs can use this to quickly perform updates to edge weights in order
+                    // to penalize certain turns.
+
+                    // If this edge is 'trivial' -- where the compressed edge corresponds
+                    // exactly to an original OSM segment -- we can pull the turn's preceding
+                    // node ID directly with `node_u`; otherwise, we need to look up the node
+                    // immediately preceding the turn from the compressed edge container.
+                    const bool isTrivial = m_compressed_edge_container.IsTrivial(edge_from_u);
+
+                    const auto &from_node =
+                        isTrivial
+                            ? m_node_info_list[node_u]
+                            : m_node_info_list[m_compressed_edge_container.GetLastEdgeSourceID(
+                                  edge_from_u)];
+                    const auto &via_node =
+                        m_node_info_list[m_compressed_edge_container.GetLastEdgeTargetID(
+                            edge_from_u)];
+                    const auto &to_node =
+                        m_node_info_list[m_compressed_edge_container.GetFirstEdgeTargetID(
+                            turn.eid)];
+
+                    edge_penalty_file.write(reinterpret_cast<const char *>(&from_node.node_id),
+                                            sizeof(from_node.node_id));
+                    edge_penalty_file.write(reinterpret_cast<const char *>(&via_node.node_id),
+                                            sizeof(via_node.node_id));
+                    edge_penalty_file.write(reinterpret_cast<const char *>(&to_node.node_id),
+                                            sizeof(to_node.node_id));
                 }
             }
         }
diff --git a/src/extractor/extractor.cpp b/src/extractor/extractor.cpp
index 977a59c..c744c38 100644
--- a/src/extractor/extractor.cpp
+++ b/src/extractor/extractor.cpp
@@ -392,16 +392,16 @@ void Extractor::FindComponents(unsigned max_edge_id,
 
     TarjanSCC<UncontractedGraph> component_search(
         std::const_pointer_cast<const UncontractedGraph>(uncontractor_graph));
-    component_search.run();
+    component_search.Run();
 
     for (auto &node : input_nodes)
     {
-        auto forward_component = component_search.get_component_id(node.forward_segment_id.id);
+        auto forward_component = component_search.GetComponentID(node.forward_segment_id.id);
         BOOST_ASSERT(!node.reverse_segment_id.enabled ||
                      forward_component ==
-                         component_search.get_component_id(node.reverse_segment_id.id));
+                         component_search.GetComponentID(node.reverse_segment_id.id));
 
-        const unsigned component_size = component_search.get_component_size(forward_component);
+        const unsigned component_size = component_search.GetComponentSize(forward_component);
         node.component.is_tiny = component_size < config.small_component_size;
         node.component.id = 1 + forward_component;
     }
@@ -563,9 +563,9 @@ void Extractor::BuildRTree(std::vector<EdgeBasedNode> node_based_edge_list,
     node_based_edge_list.resize(new_size);
 
     TIMER_START(construction);
-    util::StaticRTree<EdgeBasedNode> 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)
diff --git a/src/extractor/graph_compressor.cpp b/src/extractor/graph_compressor.cpp
index 6df9a38..282087d 100644
--- a/src/extractor/graph_compressor.cpp
+++ b/src/extractor/graph_compressor.cpp
@@ -26,7 +26,7 @@ void GraphCompressor::Compress(const std::unordered_set<NodeID> &barrier_nodes,
 
     for (const NodeID node_v : util::irange(0u, original_number_of_nodes))
     {
-        progress.printStatus(node_v);
+        progress.PrintStatus(node_v);
 
         // only contract degree 2 vertices
         if (2 != graph.GetOutDegree(node_v))
diff --git a/src/extractor/guidance/classification_data.cpp b/src/extractor/guidance/classification_data.cpp
index 70ccaff..78a8684 100644
--- a/src/extractor/guidance/classification_data.cpp
+++ b/src/extractor/guidance/classification_data.cpp
@@ -13,8 +13,7 @@ namespace guidance
 FunctionalRoadClass functionalRoadClassFromTag(std::string const &value)
 {
     // FIXME at some point this should be part of the profiles
-    const static auto class_hash = []
-    {
+    const static auto class_hash = [] {
         std::unordered_map<std::string, FunctionalRoadClass> hash;
         hash["motorway"] = FunctionalRoadClass::MOTORWAY;
         hash["motorway_link"] = FunctionalRoadClass::MOTORWAY_LINK;
@@ -43,7 +42,8 @@ FunctionalRoadClass functionalRoadClassFromTag(std::string const &value)
     }
     else
     {
-        util::SimpleLogger().Write(logDEBUG) << "Unknown road class encountered: " << value;
+        // TODO activate again, when road classes are moved to the profile
+        // util::SimpleLogger().Write(logDEBUG) << "Unknown road class encountered: " << value;
         return FunctionalRoadClass::UNKNOWN;
     }
 }
diff --git a/src/extractor/guidance/intersection_generator.cpp b/src/extractor/guidance/intersection_generator.cpp
index 5f01cda..8d0d072 100644
--- a/src/extractor/guidance/intersection_generator.cpp
+++ b/src/extractor/guidance/intersection_generator.cpp
@@ -7,6 +7,8 @@
 #include <limits>
 #include <utility>
 
+#include <boost/range/algorithm/count_if.hpp>
+
 namespace osrm
 {
 namespace extractor
@@ -14,11 +16,12 @@ namespace extractor
 namespace guidance
 {
 
-IntersectionGenerator::IntersectionGenerator(const util::NodeBasedDynamicGraph &node_based_graph,
-                      const RestrictionMap &restriction_map,
-                      const std::unordered_set<NodeID> &barrier_nodes,
-                      const std::vector<QueryNode> &node_info_list,
-                      const CompressedEdgeContainer &compressed_edge_container)
+IntersectionGenerator::IntersectionGenerator(
+    const util::NodeBasedDynamicGraph &node_based_graph,
+    const RestrictionMap &restriction_map,
+    const std::unordered_set<NodeID> &barrier_nodes,
+    const std::vector<QueryNode> &node_info_list,
+    const CompressedEdgeContainer &compressed_edge_container)
     : node_based_graph(node_based_graph), restriction_map(restriction_map),
       barrier_nodes(barrier_nodes), node_info_list(node_info_list),
       compressed_edge_container(compressed_edge_container)
@@ -53,6 +56,7 @@ Intersection IntersectionGenerator::getConnectedRoads(const NodeID from_node,
     const bool is_barrier_node = barrier_nodes.find(turn_node) != barrier_nodes.end();
 
     bool has_uturn_edge = false;
+    bool uturn_could_be_valid = false;
     for (const EdgeID onto_edge : node_based_graph.GetAdjacentEdgeRange(turn_node))
     {
         BOOST_ASSERT(onto_edge != SPECIAL_EDGEID);
@@ -73,6 +77,7 @@ Intersection IntersectionGenerator::getConnectedRoads(const NodeID from_node,
         auto angle = 0.;
         if (from_node == to_node)
         {
+            uturn_could_be_valid = turn_is_valid;
             if (turn_is_valid && !is_barrier_node)
             {
                 // we only add u-turns for dead-end streets.
@@ -89,7 +94,7 @@ Intersection IntersectionGenerator::getConnectedRoads(const NodeID from_node,
                             ++number_of_emmiting_bidirectional_edges;
                         }
                     }
-                    // is a dead-end
+                    // is a dead-end, only possible road is to go back
                     turn_is_valid = number_of_emmiting_bidirectional_edges <= 1;
                 }
             }
@@ -105,7 +110,7 @@ Intersection IntersectionGenerator::getConnectedRoads(const NodeID from_node,
                 turn_node, to_node, onto_edge, !INVERT, compressed_edge_container, node_info_list);
             angle = util::coordinate_calculation::computeAngle(
                 first_coordinate, node_info_list[turn_node], third_coordinate);
-            if (angle < std::numeric_limits<double>::epsilon())
+            if (std::abs(angle) < std::numeric_limits<double>::epsilon())
                 has_uturn_edge = true;
         }
 
@@ -115,8 +120,7 @@ Intersection IntersectionGenerator::getConnectedRoads(const NodeID from_node,
     }
 
     // We hit the case of a street leading into nothing-ness. Since the code here assumes that this
-    // will
-    // never happen we add an artificial invalid uturn in this case.
+    // will never happen we add an artificial invalid uturn in this case.
     if (!has_uturn_edge)
     {
         intersection.push_back(
@@ -131,6 +135,11 @@ Intersection IntersectionGenerator::getConnectedRoads(const NodeID from_node,
     BOOST_ASSERT(intersection[0].turn.angle >= 0. &&
                  intersection[0].turn.angle < std::numeric_limits<double>::epsilon());
 
+    const auto valid_count =
+        boost::count_if(intersection, [](const ConnectedRoad &road) { return road.entry_allowed; });
+    if (0 == valid_count && uturn_could_be_valid)
+        intersection[0].entry_allowed = true;
+
     return mergeSegregatedRoads(std::move(intersection));
 }
 
@@ -204,9 +213,18 @@ Intersection IntersectionGenerator::mergeSegregatedRoads(Intersection intersecti
             return result;
         }
     };
-    if (intersection.size() == 1)
+    if (intersection.size() <= 1)
         return intersection;
 
+    const bool is_connected_to_roundabout = [this, &intersection]() {
+        for (const auto &road : intersection)
+        {
+            if (node_based_graph.GetEdgeData(road.turn.eid).roundabout)
+                return true;
+        }
+        return false;
+    }();
+
     // check for merges including the basic u-turn
     // these result in an adjustment of all other angles
     if (mergable(0, intersection.size() - 1))
@@ -215,11 +233,26 @@ Intersection IntersectionGenerator::mergeSegregatedRoads(Intersection intersecti
             (360 - intersection[intersection.size() - 1].turn.angle) / 2;
         for (std::size_t i = 1; i + 1 < intersection.size(); ++i)
             intersection[i].turn.angle += correction_factor;
+
+        // FIXME if we have a left-sided country, we need to switch this off and enable it below
         intersection[0] = merge(intersection.front(), intersection.back());
         intersection[0].turn.angle = 0;
+
+        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
+            intersection[0].entry_allowed = false;
+        }
+
         intersection.pop_back();
     }
-
     else if (mergable(0, 1))
     {
         const double correction_factor = (intersection[1].turn.angle) / 2;
diff --git a/src/extractor/guidance/intersection_handler.cpp b/src/extractor/guidance/intersection_handler.cpp
index b77f4ed..c03db79 100644
--- a/src/extractor/guidance/intersection_handler.cpp
+++ b/src/extractor/guidance/intersection_handler.cpp
@@ -2,9 +2,13 @@
 #include "extractor/guidance/intersection_handler.hpp"
 #include "extractor/guidance/toolkit.hpp"
 
+#include "util/guidance/toolkit.hpp"
+#include "util/simple_logger.hpp"
+
 #include <algorithm>
 
 using EdgeData = osrm::util::NodeBasedDynamicGraph::EdgeData;
+using osrm::util::guidance::getTurnDirection;
 
 namespace osrm
 {
@@ -23,8 +27,10 @@ inline bool requiresAnnouncement(const EdgeData &from, const EdgeData &to)
 
 IntersectionHandler::IntersectionHandler(const util::NodeBasedDynamicGraph &node_based_graph,
                                          const std::vector<QueryNode> &node_info_list,
-                                         const util::NameTable &name_table)
-    : node_based_graph(node_based_graph), node_info_list(node_info_list), name_table(name_table)
+                                         const util::NameTable &name_table,
+                                         const SuffixTable &street_name_suffix_table)
+    : node_based_graph(node_based_graph), node_info_list(node_info_list), name_table(name_table),
+      street_name_suffix_table(street_name_suffix_table)
 {
 }
 
@@ -48,7 +54,7 @@ TurnType IntersectionHandler::findBasicTurnType(const EdgeID via_edge,
     bool onto_ramp = isRampClass(out_data.road_classification.road_class);
 
     if (!on_ramp && onto_ramp)
-        return TurnType::Ramp;
+        return TurnType::OnRamp;
 
     if (in_data.name_id == out_data.name_id && in_data.name_id != INVALID_NAME_ID)
     {
@@ -67,9 +73,9 @@ TurnInstruction IntersectionHandler::getInstructionForObvious(const std::size_t
     // handle travel modes:
     const auto in_mode = node_based_graph.GetEdgeData(via_edge).travel_mode;
     const auto out_mode = node_based_graph.GetEdgeData(road.turn.eid).travel_mode;
-    if (type == TurnType::Ramp)
+    if (type == TurnType::OnRamp)
     {
-        return {TurnType::Ramp, getTurnDirection(road.turn.angle)};
+        return {TurnType::OnRamp, getTurnDirection(road.turn.angle)};
     }
 
     if (angularDeviation(road.turn.angle, 0) < 0.01)
@@ -82,7 +88,8 @@ TurnInstruction IntersectionHandler::getInstructionForObvious(const std::size_t
         const auto &out_data = node_based_graph.GetEdgeData(road.turn.eid);
         if (in_data.name_id != out_data.name_id &&
             requiresNameAnnounced(name_table.GetNameForID(in_data.name_id),
-                                  name_table.GetNameForID(out_data.name_id)))
+                                  name_table.GetNameForID(out_data.name_id),
+                                  street_name_suffix_table))
         {
             // obvious turn onto a through street is a merge
             if (through_street)
@@ -283,6 +290,18 @@ void IntersectionHandler::assignFork(const EdgeID via_edge,
     }
 }
 
+void IntersectionHandler::assignTrivialTurns(const EdgeID via_eid,
+                                             Intersection &intersection,
+                                             const std::size_t begin,
+                                             const std::size_t end) const
+{
+    for (std::size_t index = begin; index != end; ++index)
+        if (intersection[index].entry_allowed)
+            intersection[index].turn.instruction = {
+                findBasicTurnType(via_eid, intersection[index]),
+                getTurnDirection(intersection[index].turn.angle)};
+}
+
 bool IntersectionHandler::isThroughStreet(const std::size_t index,
                                           const Intersection &intersection) const
 {
diff --git a/src/extractor/guidance/intersection_scenario_three_way.cpp b/src/extractor/guidance/intersection_scenario_three_way.cpp
new file mode 100644
index 0000000..ae28688
--- /dev/null
+++ b/src/extractor/guidance/intersection_scenario_three_way.cpp
@@ -0,0 +1,36 @@
+#include "extractor/guidance/constants.hpp"
+#include "extractor/guidance/intersection_scenario_three_way.hpp"
+#include "extractor/guidance/toolkit.hpp"
+
+#include "util/guidance/toolkit.hpp"
+
+using osrm::util::guidance::angularDeviation;
+
+namespace osrm
+{
+namespace extractor
+{
+namespace guidance
+{
+
+bool isFork(const ConnectedRoad &,
+            const ConnectedRoad &possible_right_fork,
+            const ConnectedRoad &possible_left_fork)
+{
+    return angularDeviation(possible_right_fork.turn.angle, STRAIGHT_ANGLE) < NARROW_TURN_ANGLE &&
+           angularDeviation(possible_left_fork.turn.angle, STRAIGHT_ANGLE) < NARROW_TURN_ANGLE;
+}
+
+bool isEndOfRoad(const ConnectedRoad &,
+                 const ConnectedRoad &possible_right_turn,
+                 const ConnectedRoad &possible_left_turn)
+{
+    return angularDeviation(possible_right_turn.turn.angle, 90) < NARROW_TURN_ANGLE &&
+           angularDeviation(possible_left_turn.turn.angle, 270) < NARROW_TURN_ANGLE &&
+           angularDeviation(possible_right_turn.turn.angle, possible_left_turn.turn.angle) >
+               2 * NARROW_TURN_ANGLE;
+}
+
+} // namespace guidance
+} // namespace extractor
+} // namespace osrm
diff --git a/src/extractor/guidance/motorway_handler.cpp b/src/extractor/guidance/motorway_handler.cpp
index c855b21..dd4f1e9 100644
--- a/src/extractor/guidance/motorway_handler.cpp
+++ b/src/extractor/guidance/motorway_handler.cpp
@@ -2,6 +2,7 @@
 #include "extractor/guidance/motorway_handler.hpp"
 #include "extractor/guidance/toolkit.hpp"
 
+#include "util/guidance/toolkit.hpp"
 #include "util/simple_logger.hpp"
 
 #include <limits>
@@ -9,6 +10,9 @@
 
 #include <boost/assert.hpp>
 
+using osrm::util::guidance::angularDeviation;
+using osrm::util::guidance::getTurnDirection;
+
 namespace osrm
 {
 namespace extractor
@@ -40,8 +44,9 @@ 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)
-    : IntersectionHandler(node_based_graph, node_info_list, name_table)
+                                 const util::NameTable &name_table,
+                                 const SuffixTable &street_name_suffix_table)
+    : IntersectionHandler(node_based_graph, node_info_list, name_table, street_name_suffix_table)
 {
 }
 
@@ -87,7 +92,12 @@ operator()(const NodeID, const EdgeID via_eid, Intersection intersection) const
     // coming from motorway
     if (detail::isMotorwayClass(in_data.road_classification.road_class))
     {
-        return fromMotorway(via_eid, std::move(intersection));
+        intersection = fromMotorway(via_eid, std::move(intersection));
+        std::for_each(intersection.begin(), intersection.end(), [](ConnectedRoad &road) {
+            if (road.turn.instruction.type == TurnType::OnRamp)
+                road.turn.instruction.type = TurnType::OffRamp;
+        });
+        return intersection;
     }
     else // coming from a ramp
     {
@@ -222,7 +232,7 @@ Intersection MotorwayHandler::fromMotorway(const EdgeID via_eid, Intersection in
 
                 intersection[1].turn.instruction =
                     getInstructionForObvious(intersection.size(), via_eid,
-                                             isThroughStreet(1,intersection), intersection[1]);
+                                             isThroughStreet(1, intersection), intersection[1]);
             }
             else
             {
@@ -236,21 +246,23 @@ Intersection MotorwayHandler::fromMotorway(const EdgeID via_eid, Intersection in
                     if (road.turn.angle == continue_angle)
                     {
                         road.turn.instruction = getInstructionForObvious(
-                            intersection.size(), via_eid, isThroughStreet(1,intersection), road);
+                            intersection.size(), via_eid, isThroughStreet(1, intersection), road);
                     }
                     else if (road.turn.angle < continue_angle)
                     {
                         road.turn.instruction = {
-                            detail::isRampClass(road.turn.eid, node_based_graph) ? TurnType::Ramp
-                                                                                 : 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::Ramp
-                                                                                 : TurnType::Turn,
+                            detail::isRampClass(road.turn.eid, node_based_graph)
+                                ? TurnType::OffRamp
+                                : TurnType::Turn,
                             (road.turn.angle > 215) ? DirectionModifier::Left
                                                     : DirectionModifier::SlightLeft};
                     }
@@ -264,7 +276,7 @@ Intersection MotorwayHandler::fromMotorway(const EdgeID via_eid, Intersection in
             {
                 intersection[1].turn.instruction =
                     getInstructionForObvious(intersection.size(), via_eid,
-                                             isThroughStreet(1,intersection), intersection[1]);
+                                             isThroughStreet(1, intersection), intersection[1]);
                 util::SimpleLogger().Write(logDEBUG) << "Disabled U-Turn on a freeway";
                 intersection[0].entry_allowed = false; // UTURN on the freeway
             }
@@ -341,7 +353,7 @@ Intersection MotorwayHandler::fromRamp(const EdgeID via_eid, Intersection inters
         BOOST_ASSERT(detail::isMotorwayClass(intersection[1].turn.eid, node_based_graph));
 
         intersection[1].turn.instruction = getInstructionForObvious(
-            intersection.size(), via_eid, isThroughStreet(1,intersection), intersection[1]);
+            intersection.size(), via_eid, isThroughStreet(1, intersection), intersection[1]);
     }
     else if (intersection.size() == 3)
     {
@@ -378,7 +390,7 @@ Intersection MotorwayHandler::fromRamp(const EdgeID via_eid, Intersection inters
                 {
                     intersection[1].turn.instruction =
                         getInstructionForObvious(intersection.size(), via_eid,
-                                                 isThroughStreet(1,intersection), intersection[1]);
+                                                 isThroughStreet(1, intersection), intersection[1]);
                 }
             }
             else
@@ -403,7 +415,7 @@ Intersection MotorwayHandler::fromRamp(const EdgeID via_eid, Intersection inters
                 {
                     intersection[2].turn.instruction =
                         getInstructionForObvious(intersection.size(), via_eid,
-                                                 isThroughStreet(2,intersection), intersection[2]);
+                                                 isThroughStreet(2, intersection), intersection[2]);
                 }
             }
         }
@@ -467,7 +479,7 @@ Intersection MotorwayHandler::fromRamp(const EdgeID via_eid, Intersection inters
             else
             {
                 BOOST_ASSERT(isRampClass(edge_data.road_classification.road_class));
-                road.turn.instruction = {TurnType::Ramp, getTurnDirection(road.turn.angle)};
+                road.turn.instruction = {TurnType::OffRamp, getTurnDirection(road.turn.angle)};
             }
         }
     }
diff --git a/src/extractor/guidance/roundabout_handler.cpp b/src/extractor/guidance/roundabout_handler.cpp
index 6bd72dc..603e27d 100644
--- a/src/extractor/guidance/roundabout_handler.cpp
+++ b/src/extractor/guidance/roundabout_handler.cpp
@@ -2,14 +2,18 @@
 #include "extractor/guidance/roundabout_handler.hpp"
 #include "extractor/guidance/toolkit.hpp"
 
+#include "util/coordinate_calculation.hpp"
+#include "util/guidance/toolkit.hpp"
 #include "util/simple_logger.hpp"
 
+#include <algorithm>
 #include <cmath>
-#include <set>
 #include <unordered_set>
 
 #include <boost/assert.hpp>
 
+using osrm::util::guidance::getTurnDirection;
+
 namespace osrm
 {
 namespace extractor
@@ -19,8 +23,11 @@ namespace guidance
 
 RoundaboutHandler::RoundaboutHandler(const util::NodeBasedDynamicGraph &node_based_graph,
                                      const std::vector<QueryNode> &node_info_list,
-                                     const util::NameTable &name_table)
-    : IntersectionHandler(node_based_graph, node_info_list, name_table)
+                                     const CompressedEdgeContainer &compressed_edge_container,
+                                     const util::NameTable &name_table,
+                                     const SuffixTable &street_name_suffix_table)
+    : IntersectionHandler(node_based_graph, node_info_list, name_table, street_name_suffix_table),
+      compressed_edge_container(compressed_edge_container)
 {
 }
 
@@ -31,17 +38,22 @@ bool RoundaboutHandler::canProcess(const NodeID from_nid,
                                    const Intersection &intersection) const
 {
     const auto flags = getRoundaboutFlags(from_nid, via_eid, intersection);
-    return flags.on_roundabout || flags.can_enter;
+    if (!flags.on_roundabout && !flags.can_enter)
+        return false;
+
+    const auto roundabout_type = getRoundaboutType(node_based_graph.GetTarget(via_eid));
+    return roundabout_type != RoundaboutType::None;
 }
 
 Intersection RoundaboutHandler::
 operator()(const NodeID from_nid, const EdgeID via_eid, Intersection intersection) const
 {
+    invalidateExitAgainstDirection(from_nid, via_eid, intersection);
     const auto flags = getRoundaboutFlags(from_nid, via_eid, intersection);
-    const bool is_rotary = isRotary(node_based_graph.GetTarget(via_eid));
+    const auto roundabout_type = getRoundaboutType(node_based_graph.GetTarget(via_eid));
     // find the radius of the roundabout
-    return handleRoundabouts(is_rotary, 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(
@@ -55,7 +67,7 @@ detail::RoundaboutFlags RoundaboutHandler::getRoundaboutFlags(
     {
         const auto &edge_data = node_based_graph.GetEdgeData(road.turn.eid);
         // only check actual outgoing edges
-        if (edge_data.reversed)
+        if (edge_data.reversed || !road.entry_allowed)
             continue;
 
         if (edge_data.roundabout)
@@ -79,7 +91,112 @@ detail::RoundaboutFlags RoundaboutHandler::getRoundaboutFlags(
     return {on_roundabout, can_enter_roundabout, can_exit_roundabout_separately};
 }
 
-bool RoundaboutHandler::isRotary(const NodeID nid) const
+void RoundaboutHandler::invalidateExitAgainstDirection(const NodeID from_nid,
+                                                       const EdgeID via_eid,
+                                                       Intersection &intersection) const
+{
+    const auto &in_edge_data = node_based_graph.GetEdgeData(via_eid);
+    if (in_edge_data.roundabout)
+        return;
+
+    bool past_roundabout_angle = false;
+    for (auto &road : intersection)
+    {
+        const auto &edge_data = node_based_graph.GetEdgeData(road.turn.eid);
+        // only check actual outgoing edges
+        if (edge_data.reversed)
+        {
+            // remember whether we have seen the roundabout in-part
+            if (edge_data.roundabout)
+                past_roundabout_angle = true;
+
+            continue;
+        }
+
+        // Exiting roundabouts at an entry point is technically a data-modelling issue.
+        // This workaround handles cases in which an exit precedes and entry. The resulting
+        // u-turn against the roundabout direction is invalidated.
+        // The sorting of the angles represents a problem for left-sided driving, though.
+        // FIXME in case of left-sided driving, we have to check whether we can enter the
+        // roundabout later in the cycle, rather than prior.
+        if (!edge_data.roundabout && node_based_graph.GetTarget(road.turn.eid) != from_nid &&
+            past_roundabout_angle)
+        {
+            road.entry_allowed = false;
+        }
+    }
+}
+
+// If we want to see a roundabout as a turn, the exits have to be distinct enough to be seen a
+// dedicated turns. We are limiting it to four-way intersections with well distinct bearings.
+// All entry/roads and exit roads have to be simple. Not segregated roads.
+// Processing segregated roads would technically require an angle of the turn to be available
+// in postprocessing since we correct the turn-angle in turn-generaion.
+bool RoundaboutHandler::qualifiesAsRoundaboutIntersection(
+    const std::set<NodeID> &roundabout_nodes) const
+{
+    // translate a node ID into its respective coordinate stored in the node_info_list
+    const auto getCoordinate = [this](const NodeID node) {
+        return util::Coordinate(node_info_list[node].lon, node_info_list[node].lat);
+    };
+    const bool has_limited_size = roundabout_nodes.size() <= 4;
+    if (!has_limited_size)
+        return false;
+
+    const bool simple_exits =
+        roundabout_nodes.end() ==
+        std::find_if(roundabout_nodes.begin(), roundabout_nodes.end(), [this](const NodeID node) {
+            return (node_based_graph.GetOutDegree(node) > 3);
+        });
+
+    if (!simple_exits)
+        return false;
+
+    // Find all exit bearings. Only if they are well distinct (at least 60 degrees between
+    // them), we allow a roundabout turn
+
+    const auto exit_bearings = [this, &roundabout_nodes, getCoordinate]() {
+        std::vector<double> result;
+        for (const auto node : roundabout_nodes)
+        {
+            // given the reverse edge and the forward edge on a roundabout, a simple entry/exit
+            // can only contain a single further road
+            for (const auto edge : node_based_graph.GetAdjacentEdgeRange(node))
+            {
+                const auto edge_data = node_based_graph.GetEdgeData(edge);
+                if (edge_data.roundabout)
+                    continue;
+
+                // 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);
+                result.push_back(
+                    util::coordinate_calculation::bearing(src_coordinate, next_coordinate));
+                break;
+            }
+        }
+        std::sort(result.begin(), result.end());
+        return result;
+    }();
+
+    const bool well_distinct_bearings = [](const std::vector<double> &bearings) {
+        for (std::size_t bearing_index = 0; bearing_index < bearings.size(); ++bearing_index)
+        {
+            const double difference =
+                std::abs(bearings[(bearing_index + 1) % bearings.size()] - bearings[bearing_index]);
+            // we assume non-narrow turns as well distinct
+            if (difference <= NARROW_TURN_ANGLE)
+                return false;
+        }
+        return true;
+    }(exit_bearings);
+
+    return well_distinct_bearings;
+}
+
+RoundaboutType RoundaboutHandler::getRoundaboutType(const NodeID nid) const
 {
     // translate a node ID into its respective coordinate stored in the node_info_list
     const auto getCoordinate = [this](const NodeID node) {
@@ -105,7 +222,8 @@ bool RoundaboutHandler::isRotary(const NodeID nid) const
                 // 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)))
+                                          name_table.GetNameForID(edge_data.name_id),
+                                          street_name_suffix_table))
                 {
                     return SPECIAL_EDGEID;
                 }
@@ -131,31 +249,33 @@ bool RoundaboutHandler::isRotary(const NodeID nid) const
     NodeID last_node = nid;
     while (0 == roundabout_nodes.count(last_node))
     {
-        roundabout_nodes.insert(last_node);
+        // only count exits/entry locations
+        if (node_based_graph.GetOutDegree(last_node) > 2)
+            roundabout_nodes.insert(last_node);
+
         const auto eid = getNextOnRoundabout(last_node);
 
         if (eid == SPECIAL_EDGEID)
         {
-            util::SimpleLogger().Write(logDEBUG) << "Non-Loop Roundabout found.";
-            return false;
+            return RoundaboutType::None;
         }
 
         last_node = node_based_graph.GetTarget(eid);
-    }
 
-    // do we have a dedicated name for the rotary, if not its a roundabout
-    // This function can theoretically fail if the roundabout name is partly
-    // used with a reference and without. This will be fixed automatically
-    // when we handle references separately or if the useage is more consistent
-    if (roundabout_name_id == 0 || connected_names.count(roundabout_name_id))
-    {
-        return false;
+        if (last_node == nid)
+            break;
     }
 
-    if (roundabout_nodes.size() <= 1)
+    // a roundabout that cannot be entered or exited should not get here
+    if (roundabout_nodes.size() == 0)
+        return RoundaboutType::None;
+
+    // More a traffic loop than anything else, currently treated as roundabout turn
+    if (roundabout_nodes.size() == 1)
     {
-        return false;
+        return RoundaboutType::RoundaboutIntersection;
     }
+
     // calculate the radius of the roundabout/rotary. For two coordinates, we assume a minimal
     // circle
     // with both vertices right at the other side (so half their distance in meters).
@@ -180,18 +300,47 @@ bool RoundaboutHandler::isRotary(const NodeID nid) const
     // The radius computation can result in infinity, if the three coordinates are non-distinct.
     // To stay on the safe side, we say its not a rotary
     if (std::isinf(radius))
-        return false;
+        return RoundaboutType::Roundabout;
+
+    // not within the dedicated radii for special roundabouts
+    if (radius > MAX_ROUNDABOUT_INTERSECTION_RADIUS && radius <= MAX_ROUNDABOUT_RADIUS)
+        return RoundaboutType::Roundabout;
 
-    return radius > MAX_ROUNDABOUT_RADIUS;
+    if (radius > MAX_ROUNDABOUT_RADIUS)
+    {
+        // do we have a dedicated name for the rotary, if not its a roundabout
+        // This function can theoretically fail if the roundabout name is partly
+        // used with a reference and without. This will be fixed automatically
+        // when we handle references separately or if the useage is more consistent
+
+        if (0 != roundabout_name_id && 0 == connected_names.count(roundabout_name_id))
+            return RoundaboutType::Rotary;
+        else
+            return RoundaboutType::Roundabout;
+    }
+
+    if (radius <= MAX_ROUNDABOUT_INTERSECTION_RADIUS)
+    {
+        const bool qualifies_as_roundabout_nitersection =
+            qualifiesAsRoundaboutIntersection(roundabout_nodes);
+        if (qualifies_as_roundabout_nitersection)
+        {
+            return RoundaboutType::RoundaboutIntersection;
+        }
+        else
+        {
+            return RoundaboutType::Roundabout;
+        }
+    }
+    return RoundaboutType::Roundabout;
 }
 
-Intersection RoundaboutHandler::handleRoundabouts(const bool is_rotary,
+Intersection RoundaboutHandler::handleRoundabouts(const RoundaboutType roundabout_type,
                                                   const EdgeID via_eid,
                                                   const bool on_roundabout,
                                                   const bool can_exit_roundabout_separately,
                                                   Intersection intersection) const
 {
-    // TODO requires differentiation between roundabouts and rotaries
     // detect via radius (get via circle through three vertices)
     NodeID node_v = node_based_graph.GetTarget(via_eid);
     if (on_roundabout)
@@ -212,14 +361,14 @@ Intersection RoundaboutHandler::handleRoundabouts(const bool is_rotary,
                 }
                 else
                 {
-                    turn.instruction =
-                        TurnInstruction::REMAIN_ROUNDABOUT(is_rotary, getTurnDirection(turn.angle));
+                    turn.instruction = TurnInstruction::REMAIN_ROUNDABOUT(
+                        roundabout_type, getTurnDirection(turn.angle));
                 }
             }
             else
             {
                 turn.instruction =
-                    TurnInstruction::EXIT_ROUNDABOUT(is_rotary, getTurnDirection(turn.angle));
+                    TurnInstruction::EXIT_ROUNDABOUT(roundabout_type, getTurnDirection(turn.angle));
             }
         }
         return intersection;
@@ -233,20 +382,17 @@ Intersection RoundaboutHandler::handleRoundabouts(const bool is_rotary,
             const auto &out_data = node_based_graph.GetEdgeData(turn.eid);
             if (out_data.roundabout)
             {
-                turn.instruction =
-                    TurnInstruction::ENTER_ROUNDABOUT(is_rotary, getTurnDirection(turn.angle));
                 if (can_exit_roundabout_separately)
-                {
-                    if (turn.instruction.type == TurnType::EnterRotary)
-                        turn.instruction.type = TurnType::EnterRotaryAtExit;
-                    if (turn.instruction.type == TurnType::EnterRoundabout)
-                        turn.instruction.type = TurnType::EnterRoundaboutAtExit;
-                }
+                    turn.instruction = TurnInstruction::ENTER_ROUNDABOUT_AT_EXIT(
+                        roundabout_type, getTurnDirection(turn.angle));
+                else
+                    turn.instruction = TurnInstruction::ENTER_ROUNDABOUT(
+                        roundabout_type, getTurnDirection(turn.angle));
             }
             else
             {
                 turn.instruction = TurnInstruction::ENTER_AND_EXIT_ROUNDABOUT(
-                    is_rotary, getTurnDirection(turn.angle));
+                    roundabout_type, getTurnDirection(turn.angle));
             }
         }
     return intersection;
diff --git a/src/extractor/guidance/turn_analysis.cpp b/src/extractor/guidance/turn_analysis.cpp
index 9413f87..a573194 100644
--- a/src/extractor/guidance/turn_analysis.cpp
+++ b/src/extractor/guidance/turn_analysis.cpp
@@ -3,6 +3,7 @@
 
 #include "util/coordinate.hpp"
 #include "util/coordinate_calculation.hpp"
+#include "util/guidance/toolkit.hpp"
 #include "util/simple_logger.hpp"
 
 #include <cstddef>
@@ -11,6 +12,8 @@
 #include <set>
 #include <unordered_set>
 
+using osrm::util::guidance::getTurnDirection;
+
 namespace osrm
 {
 namespace extractor
@@ -30,15 +33,16 @@ TurnAnalysis::TurnAnalysis(const util::NodeBasedDynamicGraph &node_based_graph,
                            const RestrictionMap &restriction_map,
                            const std::unordered_set<NodeID> &barrier_nodes,
                            const CompressedEdgeContainer &compressed_edge_container,
-                           const util::NameTable &name_table)
+                           const util::NameTable &name_table,
+                           const SuffixTable &street_name_suffix_table)
     : node_based_graph(node_based_graph), intersection_generator(node_based_graph,
                                                                  restriction_map,
                                                                  barrier_nodes,
                                                                  node_info_list,
                                                                  compressed_edge_container),
-      roundabout_handler(node_based_graph, node_info_list, name_table),
-      motorway_handler(node_based_graph, node_info_list, name_table),
-      turn_handler(node_based_graph, node_info_list, name_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),
+      turn_handler(node_based_graph, node_info_list, name_table,street_name_suffix_table)
 {
 }
 
@@ -76,9 +80,8 @@ std::vector<TurnOperation> TurnAnalysis::getTurns(const NodeID from_nid, const E
 }
 
 // Sets basic turn types as fallback for otherwise unhandled turns
-Intersection TurnAnalysis::setTurnTypes(const NodeID from_nid,
-                                        const EdgeID,
-                                        Intersection intersection) const
+Intersection
+TurnAnalysis::setTurnTypes(const NodeID from_nid, const EdgeID, Intersection intersection) const
 {
     for (auto &road : intersection)
     {
diff --git a/src/extractor/guidance/turn_handler.cpp b/src/extractor/guidance/turn_handler.cpp
index 57a29d6..b6d8987 100644
--- a/src/extractor/guidance/turn_handler.cpp
+++ b/src/extractor/guidance/turn_handler.cpp
@@ -1,8 +1,10 @@
 #include "extractor/guidance/constants.hpp"
+#include "extractor/guidance/intersection_scenario_three_way.hpp"
 #include "extractor/guidance/toolkit.hpp"
 #include "extractor/guidance/turn_handler.hpp"
 
 #include "util/simple_logger.hpp"
+#include "util/guidance/toolkit.hpp"
 
 #include <limits>
 #include <utility>
@@ -10,6 +12,8 @@
 #include <boost/assert.hpp>
 
 using EdgeData = osrm::util::NodeBasedDynamicGraph::EdgeData;
+using osrm::util::guidance::getTurnDirection;
+using osrm::util::guidance::angularDeviation;
 
 namespace osrm
 {
@@ -17,24 +21,12 @@ namespace extractor
 {
 namespace guidance
 {
-namespace detail
-{
-inline FunctionalRoadClass roadClass(const ConnectedRoad &road,
-                                     const util::NodeBasedDynamicGraph &graph)
-{
-    return graph.GetEdgeData(road.turn.eid).road_classification.road_class;
-}
-
-inline bool isRampClass(EdgeID eid, const util::NodeBasedDynamicGraph &node_based_graph)
-{
-    return isRampClass(node_based_graph.GetEdgeData(eid).road_classification.road_class);
-}
-}
 
 TurnHandler::TurnHandler(const util::NodeBasedDynamicGraph &node_based_graph,
                          const std::vector<QueryNode> &node_info_list,
-                         const util::NameTable &name_table)
-    : IntersectionHandler(node_based_graph, node_info_list, name_table)
+                         const util::NameTable &name_table,
+                         const SuffixTable &street_name_suffix_table)
+    : IntersectionHandler(node_based_graph, node_info_list, name_table, street_name_suffix_table)
 {
 }
 
@@ -90,25 +82,37 @@ Intersection TurnHandler::handleThreeWayTurn(const EdgeID via_edge, Intersection
     const auto isObviousOfTwo = [this](const ConnectedRoad road, const ConnectedRoad other) {
         const auto first_class =
             node_based_graph.GetEdgeData(road.turn.eid).road_classification.road_class;
-        const bool is_ramp = isRampClass(first_class);
+
         const auto second_class =
             node_based_graph.GetEdgeData(other.turn.eid).road_classification.road_class;
-        const bool is_narrow_turn =
-            angularDeviation(road.turn.angle, STRAIGHT_ANGLE) < NARROW_TURN_ANGLE;
-        const bool other_turn_is_at_least_orthogonal =
-            angularDeviation(other.turn.angle, STRAIGHT_ANGLE) > 85;
-        const bool turn_is_perfectly_straight = angularDeviation(road.turn.angle, STRAIGHT_ANGLE) <
-                                                std::numeric_limits<double>::epsilon();
+
+        const bool is_ramp = isRampClass(first_class);
         const bool is_obvious_by_road_class =
             (!is_ramp && (2 * getPriority(first_class) < getPriority(second_class))) ||
             (!isLowPriorityRoadClass(first_class) && isLowPriorityRoadClass(second_class));
+
+        if (is_obvious_by_road_class)
+            return true;
+
+        const bool other_is_obvious_by_road_flass =
+            (!isRampClass(second_class) &&
+             (2 * getPriority(second_class) < getPriority(first_class))) ||
+            (!isLowPriorityRoadClass(second_class) && isLowPriorityRoadClass(first_class));
+
+        if (other_is_obvious_by_road_flass)
+            return false;
+
+        const bool turn_is_perfectly_straight = angularDeviation(road.turn.angle, STRAIGHT_ANGLE) <
+                                                std::numeric_limits<double>::epsilon();
+
+        if (turn_is_perfectly_straight)
+            return true;
+
         const bool is_much_narrower_than_other =
             angularDeviation(other.turn.angle, STRAIGHT_ANGLE) /
                 angularDeviation(road.turn.angle, STRAIGHT_ANGLE) >
             INCREASES_BY_FOURTY_PERCENT;
-        return (is_narrow_turn && other_turn_is_at_least_orthogonal) ||
-               turn_is_perfectly_straight || is_much_narrower_than_other ||
-               is_obvious_by_road_class;
+        return is_much_narrower_than_other;
     };
 
     /* Two nearly straight turns -> FORK
@@ -118,8 +122,7 @@ Intersection TurnHandler::handleThreeWayTurn(const EdgeID via_edge, Intersection
              \
                OOOOOOO
      */
-    if (angularDeviation(intersection[1].turn.angle, STRAIGHT_ANGLE) < NARROW_TURN_ANGLE &&
-        angularDeviation(intersection[2].turn.angle, STRAIGHT_ANGLE) < NARROW_TURN_ANGLE)
+    if (isFork(intersection[0], intersection[1], intersection[2]))
     {
         if (intersection[1].entry_allowed && intersection[2].entry_allowed)
         {
@@ -170,134 +173,30 @@ Intersection TurnHandler::handleThreeWayTurn(const EdgeID via_edge, Intersection
                 I
                 I
      */
-    else if (angularDeviation(intersection[1].turn.angle, 90) < NARROW_TURN_ANGLE &&
-             angularDeviation(intersection[2].turn.angle, 270) < NARROW_TURN_ANGLE &&
-             angularDeviation(intersection[1].turn.angle, intersection[2].turn.angle) >
-                 NARROW_TURN_ANGLE)
+    else if (isEndOfRoad(intersection[0], intersection[1], intersection[2]))
     {
         if (intersection[1].entry_allowed)
         {
-            if (TurnType::Ramp != findBasicTurnType(via_edge, intersection[1]))
+            if (TurnType::OnRamp != findBasicTurnType(via_edge, intersection[1]))
                 intersection[1].turn.instruction = {TurnType::EndOfRoad, DirectionModifier::Right};
             else
-                intersection[1].turn.instruction = {TurnType::Ramp, DirectionModifier::Right};
+                intersection[1].turn.instruction = {TurnType::OnRamp, DirectionModifier::Right};
         }
         if (intersection[2].entry_allowed)
         {
-            if (TurnType::Ramp != findBasicTurnType(via_edge, intersection[2]))
+            if (TurnType::OnRamp != findBasicTurnType(via_edge, intersection[2]))
 
                 intersection[2].turn.instruction = {TurnType::EndOfRoad, DirectionModifier::Left};
             else
-                intersection[2].turn.instruction = {TurnType::Ramp, DirectionModifier::Left};
-        }
-    }
-    /* T Intersection, Cross left
-                O
-                O
-                O
-       IIIIIIII - OOOOOOOOOO
-     */
-    else if (angularDeviation(intersection[1].turn.angle, STRAIGHT_ANGLE) < NARROW_TURN_ANGLE &&
-             angularDeviation(intersection[2].turn.angle, 270) < NARROW_TURN_ANGLE &&
-             angularDeviation(intersection[1].turn.angle, intersection[2].turn.angle) >
-                 NARROW_TURN_ANGLE)
-    {
-        if (intersection[1].entry_allowed)
-        {
-            if (TurnType::Ramp != findBasicTurnType(via_edge, intersection[1]))
-                intersection[1].turn.instruction =
-                    getInstructionForObvious(intersection.size(), via_edge, false, intersection[1]);
-            else
-                intersection[1].turn.instruction = {TurnType::Ramp, DirectionModifier::Straight};
-        }
-        if (intersection[2].entry_allowed)
-        {
-            intersection[2].turn.instruction = {findBasicTurnType(via_edge, intersection[2]),
-                                                DirectionModifier::Left};
-        }
-    }
-    /* T Intersection, Cross right
-
-       IIIIIIII T OOOOOOOOOO
-                O
-                O
-                O
-     */
-    else if (angularDeviation(intersection[2].turn.angle, STRAIGHT_ANGLE) < NARROW_TURN_ANGLE &&
-             angularDeviation(intersection[1].turn.angle, 90) < NARROW_TURN_ANGLE &&
-             angularDeviation(intersection[1].turn.angle, intersection[2].turn.angle) >
-                 NARROW_TURN_ANGLE)
-    {
-        if (intersection[2].entry_allowed)
-            intersection[2].turn.instruction =
-                getInstructionForObvious(intersection.size(), via_edge, false, intersection[2]);
-        if (intersection[1].entry_allowed)
-            intersection[1].turn.instruction = {findBasicTurnType(via_edge, intersection[1]),
-                                                DirectionModifier::Right};
-    }
-    // merge onto a through street
-    else if (INVALID_NAME_ID != node_based_graph.GetEdgeData(intersection[1].turn.eid).name_id &&
-             node_based_graph.GetEdgeData(intersection[1].turn.eid).name_id ==
-                 node_based_graph.GetEdgeData(intersection[2].turn.eid).name_id)
-    {
-        const auto findTurn = [isObviousOfTwo](const ConnectedRoad turn,
-                                               const ConnectedRoad other) -> TurnInstruction {
-            if (isObviousOfTwo(turn, other))
-            {
-                return {TurnType::Merge, turn.turn.angle < STRAIGHT_ANGLE
-                                             ? DirectionModifier::SlightLeft
-                                             : DirectionModifier::SlightRight};
-            }
-            else
-            {
-                return {TurnType::Turn, getTurnDirection(turn.turn.angle)};
-            }
-        };
-        intersection[1].turn.instruction = findTurn(intersection[1], intersection[2]);
-        intersection[2].turn.instruction = findTurn(intersection[2], intersection[1]);
-    }
-    // other street merges from the left
-    else if (INVALID_NAME_ID != node_based_graph.GetEdgeData(via_edge).name_id &&
-             node_based_graph.GetEdgeData(via_edge).name_id ==
-                 node_based_graph.GetEdgeData(intersection[1].turn.eid).name_id)
-    {
-        if (isObviousOfTwo(intersection[1], intersection[2]))
-        {
-            intersection[1].turn.instruction =
-                TurnInstruction::SUPPRESSED(DirectionModifier::Straight);
+                intersection[2].turn.instruction = {TurnType::OnRamp, DirectionModifier::Left};
         }
-        else
-        {
-            intersection[1].turn.instruction = {TurnType::Continue,
-                                                getTurnDirection(intersection[1].turn.angle)};
-        }
-        intersection[2].turn.instruction = {findBasicTurnType(via_edge, intersection[2]),
-                                            getTurnDirection(intersection[2].turn.angle)};
-    }
-    // other street merges from the right
-    else if (INVALID_NAME_ID != node_based_graph.GetEdgeData(via_edge).name_id &&
-             node_based_graph.GetEdgeData(via_edge).name_id ==
-                 node_based_graph.GetEdgeData(intersection[2].turn.eid).name_id)
-    {
-        if (isObviousOfTwo(intersection[2], intersection[1]))
-        {
-            intersection[2].turn.instruction =
-                TurnInstruction::SUPPRESSED(DirectionModifier::Straight);
-        }
-        else
-        {
-            intersection[2].turn.instruction = {TurnType::Continue,
-                                                getTurnDirection(intersection[2].turn.angle)};
-        }
-        intersection[1].turn.instruction = {findBasicTurnType(via_edge, intersection[1]),
-                                            getTurnDirection(intersection[1].turn.angle)};
     }
     else
     {
         if (isObviousOfTwo(intersection[1], intersection[2]))
         {
-            intersection[1].turn.instruction =
-                getInstructionForObvious(3, via_edge, false, intersection[1]);
+            intersection[1].turn.instruction = getInstructionForObvious(
+                3, via_edge, isThroughStreet(1, intersection), intersection[1]);
         }
         else
         {
@@ -307,8 +206,8 @@ Intersection TurnHandler::handleThreeWayTurn(const EdgeID via_edge, Intersection
 
         if (isObviousOfTwo(intersection[2], intersection[1]))
         {
-            intersection[2].turn.instruction =
-                getInstructionForObvious(3, via_edge, false, intersection[2]);
+            intersection[2].turn.instruction = getInstructionForObvious(
+                3, via_edge, isThroughStreet(2, intersection), intersection[2]);
         }
         else
         {
@@ -321,7 +220,6 @@ Intersection TurnHandler::handleThreeWayTurn(const EdgeID via_edge, Intersection
 
 Intersection TurnHandler::handleComplexTurn(const EdgeID via_edge, Intersection intersection) const
 {
-    static int fallback_count = 0;
     const std::size_t obvious_index = findObviousTurn(via_edge, intersection);
     const auto fork_range = findFork(intersection);
     std::size_t straightmost_turn = 0;
@@ -339,9 +237,9 @@ Intersection TurnHandler::handleComplexTurn(const EdgeID via_edge, Intersection
     // 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]);
 
         // assign left/right turns
         intersection = assignLeftTurns(via_edge, std::move(intersection), obvious_index + 1);
@@ -374,7 +272,7 @@ Intersection TurnHandler::handleComplexTurn(const EdgeID via_edge, Intersection
                                           DirectionModifier::SlightRight};
             }
         }
-        else if (fork_range.second - fork_range.second == 2)
+        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]);
@@ -404,20 +302,7 @@ Intersection TurnHandler::handleComplexTurn(const EdgeID via_edge, Intersection
     }
     else
     {
-        if (fallback_count++ < 10)
-        {
-            util::SimpleLogger().Write(logWARNING)
-                << "Resolved to keep fallback on complex turn assignment"
-                << "Straightmost: " << straightmost_turn;
-            ;
-            for (const auto &road : intersection)
-            {
-                const auto &out_data = node_based_graph.GetEdgeData(road.turn.eid);
-                util::SimpleLogger().Write(logWARNING)
-                    << "road: " << toString(road) << " Name: " << out_data.name_id
-                    << " Road Class: " << (int)out_data.road_classification.road_class;
-            }
-        }
+        assignTrivialTurns(via_edge,intersection,1,intersection.size());
     }
     return intersection;
 }
@@ -465,12 +350,6 @@ std::size_t TurnHandler::findObviousTurn(const EdgeID via_edge,
     if (best_deviation >= 2 * NARROW_TURN_ANGLE)
         return 0;
 
-    // TODO incorporate road class in decision
-    if (best != 0 && best_deviation < MAXIMAL_ALLOWED_NO_TURN_DEVIATION)
-    {
-        return best;
-    }
-
     // has no obvious continued road
     if (best_continue == 0 || true)
     {
@@ -493,8 +372,11 @@ std::size_t TurnHandler::findObviousTurn(const EdgeID via_edge,
             return 0;
 
         // Well distinct turn that is nearly straight
-        if (left_deviation / best_deviation >= DISTINCTION_RATIO &&
-            right_deviation / best_deviation >= DISTINCTION_RATIO)
+        if ((left_deviation / best_deviation >= DISTINCTION_RATIO ||
+             (left_deviation > best_deviation &&
+              !intersection[(best + 1) % intersection.size()].entry_allowed)) &&
+            (right_deviation / best_deviation >= DISTINCTION_RATIO ||
+             (right_deviation > best_deviation && !intersection[best - 1].entry_allowed)))
         {
             return best;
         }
@@ -503,253 +385,32 @@ std::size_t TurnHandler::findObviousTurn(const EdgeID via_edge,
     return 0; // no obvious turn
 }
 
-// Can only assign three turns
+// Assignment of left turns hands of to right turns.
+// To do so, we mirror every road segment and reverse the order.
+// After the mirror and reversal / we assign right turns and
+// mirror again and restore the original order.
 Intersection TurnHandler::assignLeftTurns(const EdgeID via_edge,
                                           Intersection intersection,
                                           const std::size_t starting_at) const
 {
-    const auto count_valid = [&intersection, starting_at]() {
-        std::size_t count = 0;
-        for (std::size_t i = starting_at; i < intersection.size(); ++i)
-            if (intersection[i].entry_allowed)
-                ++count;
-        return count;
-    };
-    if (starting_at == intersection.size() || count_valid() == 0)
-        return intersection;
-    // handle single turn
-    if (intersection.size() - starting_at == 1)
+    BOOST_ASSERT(starting_at <= intersection.size());
+    const auto switch_left_and_right = []( Intersection &intersection )
     {
-        if (!intersection[starting_at].entry_allowed)
-            return intersection;
+        BOOST_ASSERT(!intersection.empty());
 
-        if (angularDeviation(intersection[starting_at].turn.angle, STRAIGHT_ANGLE) >
-                NARROW_TURN_ANGLE &&
-            angularDeviation(intersection[starting_at].turn.angle, 0) > NARROW_TURN_ANGLE)
-        {
-            // assign left turn
-            intersection[starting_at].turn.instruction = {
-                findBasicTurnType(via_edge, intersection[starting_at]), DirectionModifier::Left};
-        }
-        else if (angularDeviation(intersection[starting_at].turn.angle, STRAIGHT_ANGLE) <=
-                 NARROW_TURN_ANGLE)
-        {
-            intersection[starting_at].turn.instruction = {
-                findBasicTurnType(via_edge, intersection[starting_at]),
-                DirectionModifier::SlightLeft};
-        }
-        else
-        {
-            intersection[starting_at].turn.instruction = {
-                findBasicTurnType(via_edge, intersection[starting_at]),
-                DirectionModifier::SharpLeft};
-        }
-    }
-    // two turns on at the side
-    else if (intersection.size() - starting_at == 2)
-    {
-        const auto first_direction = getTurnDirection(intersection[starting_at].turn.angle);
-        const auto second_direction = getTurnDirection(intersection[starting_at + 1].turn.angle);
-        if (first_direction == second_direction)
-        {
-            // conflict
-            handleDistinctConflict(via_edge, intersection[starting_at + 1],
-                                   intersection[starting_at]);
-        }
-        else
-        {
-            intersection[starting_at].turn.instruction = {
-                findBasicTurnType(via_edge, intersection[starting_at]), first_direction};
-            intersection[starting_at + 1].turn.instruction = {
-                findBasicTurnType(via_edge, intersection[starting_at + 1]), second_direction};
-        }
-    }
-    else if (intersection.size() - starting_at == 3)
-    {
-        const auto first_direction = getTurnDirection(intersection[starting_at].turn.angle);
-        const auto second_direction = getTurnDirection(intersection[starting_at + 1].turn.angle);
-        const auto third_direction = getTurnDirection(intersection[starting_at + 2].turn.angle);
-        if (first_direction != second_direction && second_direction != third_direction)
-        {
-            // implies first != third, based on the angles and clockwise order
-            if (intersection[starting_at].entry_allowed)
-                intersection[starting_at].turn.instruction = {
-                    findBasicTurnType(via_edge, intersection[starting_at]), first_direction};
-            if (intersection[starting_at + 1].entry_allowed)
-                intersection[starting_at + 1].turn.instruction = {
-                    findBasicTurnType(via_edge, intersection[starting_at + 1]), second_direction};
-            if (intersection[starting_at + 2].entry_allowed)
-                intersection[starting_at + 2].turn.instruction = {
-                    findBasicTurnType(via_edge, intersection[starting_at + 2]), third_direction};
-        }
-        else if (2 >= (intersection[starting_at].entry_allowed +
-                       intersection[starting_at + 1].entry_allowed +
-                       intersection[starting_at + 2].entry_allowed))
-        {
+        for (auto &road : intersection)
+            road = mirror(std::move(road));
+
+        std::reverse(intersection.begin() + 1, intersection.end());
+    };
+
+    switch_left_and_right(intersection);
+    // account for the u-turn in the beginning
+    const auto count = intersection.size() - starting_at + 1;
+    intersection = assignRightTurns(via_edge, std::move(intersection), count);
+    switch_left_and_right(intersection);
 
-            // at least one invalid turn
-            if (!intersection[starting_at].entry_allowed)
-            {
-                handleDistinctConflict(via_edge, intersection[starting_at + 2],
-                                       intersection[starting_at + 1]);
-            }
-            else if (!intersection[starting_at + 1].entry_allowed)
-            {
-                handleDistinctConflict(via_edge, intersection[starting_at + 2],
-                                       intersection[starting_at]);
-            }
-            else
-            {
-                handleDistinctConflict(via_edge, intersection[starting_at + 1],
-                                       intersection[starting_at]);
-            }
-        }
-        else if (intersection[starting_at].entry_allowed &&
-                 intersection[starting_at + 1].entry_allowed &&
-                 intersection[starting_at + 2].entry_allowed &&
-                 angularDeviation(intersection[starting_at].turn.angle,
-                                  intersection[starting_at + 1].turn.angle) >= NARROW_TURN_ANGLE &&
-                 angularDeviation(intersection[starting_at + 1].turn.angle,
-                                  intersection[starting_at + 2].turn.angle) >= NARROW_TURN_ANGLE)
-        {
-            intersection[starting_at].turn.instruction = {
-                findBasicTurnType(via_edge, intersection[starting_at]),
-                DirectionModifier::SlightLeft};
-            intersection[starting_at + 1].turn.instruction = {
-                findBasicTurnType(via_edge, intersection[starting_at + 1]),
-                DirectionModifier::Left};
-            intersection[starting_at + 2].turn.instruction = {
-                findBasicTurnType(via_edge, intersection[starting_at + 2]),
-                DirectionModifier::SharpLeft};
-        }
-        else if (intersection[starting_at].entry_allowed &&
-                 intersection[starting_at + 1].entry_allowed &&
-                 intersection[starting_at + 2].entry_allowed &&
-                 ((first_direction == second_direction && second_direction == third_direction) ||
-                  (third_direction == second_direction &&
-                   angularDeviation(intersection[starting_at].turn.angle,
-                                    intersection[starting_at + 1].turn.angle) < GROUP_ANGLE) ||
-                  (second_direction == first_direction &&
-                   angularDeviation(intersection[starting_at + 1].turn.angle,
-                                    intersection[starting_at + 2].turn.angle) < GROUP_ANGLE)))
-        {
-            intersection[starting_at].turn.instruction = {
-                detail::isRampClass(intersection[starting_at].turn.eid, node_based_graph)
-                    ? FirstRamp
-                    : FirstTurn,
-                second_direction};
-            intersection[starting_at + 1].turn.instruction = {
-                detail::isRampClass(intersection[starting_at + 1].turn.eid, node_based_graph)
-                    ? SecondRamp
-                    : SecondTurn,
-                second_direction};
-            intersection[starting_at + 2].turn.instruction = {
-                detail::isRampClass(intersection[starting_at + 2].turn.eid, node_based_graph)
-                    ? ThirdRamp
-                    : ThirdTurn,
-                second_direction};
-        }
-        else if (intersection[starting_at].entry_allowed &&
-                 intersection[starting_at + 1].entry_allowed &&
-                 intersection[starting_at + 2].entry_allowed &&
-                 ((third_direction == second_direction &&
-                   angularDeviation(intersection[starting_at].turn.angle,
-                                    intersection[starting_at + 1].turn.angle) >= GROUP_ANGLE) ||
-                  (second_direction == first_direction &&
-                   angularDeviation(intersection[starting_at + 1].turn.angle,
-                                    intersection[starting_at + 2].turn.angle) >= GROUP_ANGLE)))
-        {
-            // conflict one side with an additional very sharp turn
-            if (angularDeviation(intersection[starting_at + 1].turn.angle,
-                                 intersection[starting_at + 2].turn.angle) >= GROUP_ANGLE)
-            {
-                handleDistinctConflict(via_edge, intersection[starting_at + 1],
-                                       intersection[starting_at]);
-                intersection[starting_at + 2].turn.instruction = {
-                    findBasicTurnType(via_edge, intersection[starting_at + 2]), third_direction};
-            }
-            else
-            {
-                intersection[starting_at].turn.instruction = {
-                    findBasicTurnType(via_edge, intersection[starting_at]), first_direction};
-                handleDistinctConflict(via_edge, intersection[starting_at + 2],
-                                       intersection[starting_at + 1]);
-            }
-        }
-        else if ((first_direction == second_direction &&
-                  intersection[starting_at].entry_allowed !=
-                      intersection[starting_at + 1].entry_allowed) ||
-                 (second_direction == third_direction &&
-                  intersection[starting_at + 1].entry_allowed !=
-                      intersection[starting_at + 2].entry_allowed))
-        {
-            // no conflict, due to conflict being restricted to valid/invalid
-            if (intersection[starting_at].entry_allowed)
-                intersection[starting_at].turn.instruction = {
-                    findBasicTurnType(via_edge, intersection[starting_at]), first_direction};
-            if (intersection[starting_at + 1].entry_allowed)
-                intersection[starting_at + 1].turn.instruction = {
-                    findBasicTurnType(via_edge, intersection[starting_at + 1]), second_direction};
-            if (intersection[starting_at + 2].entry_allowed)
-                intersection[starting_at + 2].turn.instruction = {
-                    findBasicTurnType(via_edge, intersection[starting_at + 2]), third_direction};
-        }
-        else
-        {
-            util::SimpleLogger().Write(logWARNING) << "Reached fallback for left turns, size 3";
-            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;
-            }
 
-            for (std::size_t i = starting_at; i < intersection.size(); ++i)
-                if (intersection[i].entry_allowed)
-                    intersection[i].turn.instruction = {
-                        findBasicTurnType(via_edge, intersection[i]),
-                        getTurnDirection(intersection[i].turn.angle)};
-        }
-    }
-    else if (intersection.size() - starting_at == 4)
-    {
-        if (intersection[starting_at].entry_allowed)
-            intersection[starting_at].turn.instruction = {
-                detail::isRampClass(intersection[starting_at].turn.eid, node_based_graph)
-                    ? FirstRamp
-                    : FirstTurn,
-                DirectionModifier::Left};
-        if (intersection[starting_at + 1].entry_allowed)
-            intersection[starting_at + 1].turn.instruction = {
-                detail::isRampClass(intersection[starting_at + 1].turn.eid, node_based_graph)
-                    ? SecondRamp
-                    : SecondTurn,
-                DirectionModifier::Left};
-        if (intersection[starting_at + 2].entry_allowed)
-            intersection[starting_at + 2].turn.instruction = {
-                detail::isRampClass(intersection[starting_at + 2].turn.eid, node_based_graph)
-                    ? ThirdRamp
-                    : ThirdTurn,
-                DirectionModifier::Left};
-        if (intersection[starting_at + 3].entry_allowed)
-            intersection[starting_at + 3].turn.instruction = {
-                detail::isRampClass(intersection[starting_at + 3].turn.eid, node_based_graph)
-                    ? FourthRamp
-                    : FourthTurn,
-                DirectionModifier::Left};
-    }
-    else
-    {
-        for (auto &road : intersection)
-        {
-            if (!road.entry_allowed)
-                continue;
-            road.turn.instruction = {detail::isRampClass(road.turn.eid, node_based_graph) ? Ramp
-                                                                                          : Turn,
-                                     getTurnDirection(road.turn.angle)};
-        }
-    }
     return intersection;
 }
 
@@ -771,24 +432,9 @@ Intersection TurnHandler::assignRightTurns(const EdgeID via_edge,
     // handle single turn
     if (up_to == 2)
     {
-        if (angularDeviation(intersection[1].turn.angle, STRAIGHT_ANGLE) > NARROW_TURN_ANGLE &&
-            angularDeviation(intersection[1].turn.angle, 0) > NARROW_TURN_ANGLE)
-        {
-            // assign left turn
-            intersection[1].turn.instruction = {findBasicTurnType(via_edge, intersection[1]),
-                                                DirectionModifier::Right};
-        }
-        else if (angularDeviation(intersection[1].turn.angle, STRAIGHT_ANGLE) <= NARROW_TURN_ANGLE)
-        {
-            intersection[1].turn.instruction = {findBasicTurnType(via_edge, intersection[1]),
-                                                DirectionModifier::SlightRight};
-        }
-        else
-        {
-            intersection[1].turn.instruction = {findBasicTurnType(via_edge, intersection[1]),
-                                                DirectionModifier::SharpRight};
-        }
+        assignTrivialTurns(via_edge, intersection, 1, up_to);
     }
+    // Handle Turns 1-3
     else if (up_to == 3)
     {
         const auto first_direction = getTurnDirection(intersection[1].turn.angle);
@@ -800,12 +446,10 @@ Intersection TurnHandler::assignRightTurns(const EdgeID via_edge,
         }
         else
         {
-            intersection[1].turn.instruction = {findBasicTurnType(via_edge, intersection[1]),
-                                                first_direction};
-            intersection[2].turn.instruction = {findBasicTurnType(via_edge, intersection[2]),
-                                                second_direction};
+            assignTrivialTurns(via_edge, intersection, 1, up_to);
         }
     }
+    // Handle Turns 1-4
     else if (up_to == 4)
     {
         const auto first_direction = getTurnDirection(intersection[1].turn.angle);
@@ -813,15 +457,10 @@ Intersection TurnHandler::assignRightTurns(const EdgeID via_edge,
         const auto third_direction = getTurnDirection(intersection[3].turn.angle);
         if (first_direction != second_direction && second_direction != third_direction)
         {
-            if (intersection[1].entry_allowed)
-                intersection[1].turn.instruction = {findBasicTurnType(via_edge, intersection[1]),
-                                                    first_direction};
-            if (intersection[2].entry_allowed)
-                intersection[2].turn.instruction = {findBasicTurnType(via_edge, intersection[2]),
-                                                    second_direction};
-            if (intersection[3].entry_allowed)
-                intersection[3].turn.instruction = {findBasicTurnType(via_edge, intersection[3]),
-                                                    third_direction};
+            // due to the circular order, the turn directions are unique
+            // first_direction != third_direction is implied
+            BOOST_ASSERT(first_direction != third_direction);
+            assignTrivialTurns(via_edge, intersection, 1, up_to);
         }
         else if (2 >= (intersection[1].entry_allowed + intersection[2].entry_allowed +
                        intersection[3].entry_allowed))
@@ -840,13 +479,18 @@ Intersection TurnHandler::assignRightTurns(const EdgeID via_edge,
                 handleDistinctConflict(via_edge, intersection[3], intersection[1]);
             }
         }
-        else if (intersection[1].entry_allowed && intersection[2].entry_allowed &&
-                 intersection[3].entry_allowed &&
-                 angularDeviation(intersection[1].turn.angle, intersection[2].turn.angle) >=
+        // From here on out, intersection[1-3].entry_allowed has to be true (Otherwise we would have
+        // triggered 2>= ...)
+        //
+        // Conflicting Turns, but at least farther than what we call a narrow turn
+        else if (angularDeviation(intersection[1].turn.angle, intersection[2].turn.angle) >=
                      NARROW_TURN_ANGLE &&
                  angularDeviation(intersection[2].turn.angle, intersection[3].turn.angle) >=
                      NARROW_TURN_ANGLE)
         {
+            BOOST_ASSERT(intersection[1].entry_allowed && intersection[2].entry_allowed &&
+                         intersection[3].entry_allowed);
+
             intersection[1].turn.instruction = {findBasicTurnType(via_edge, intersection[1]),
                                                 DirectionModifier::SharpRight};
             intersection[2].turn.instruction = {findBasicTurnType(via_edge, intersection[2]),
@@ -854,9 +498,7 @@ Intersection TurnHandler::assignRightTurns(const EdgeID via_edge,
             intersection[3].turn.instruction = {findBasicTurnType(via_edge, intersection[3]),
                                                 DirectionModifier::SlightRight};
         }
-        else if (intersection[1].entry_allowed && intersection[2].entry_allowed &&
-                 intersection[3].entry_allowed &&
-                 ((first_direction == second_direction && second_direction == third_direction) ||
+        else if (((first_direction == second_direction && second_direction == third_direction) ||
                   (first_direction == second_direction &&
                    angularDeviation(intersection[2].turn.angle, intersection[3].turn.angle) <
                        GROUP_ANGLE) ||
@@ -864,28 +506,21 @@ Intersection TurnHandler::assignRightTurns(const EdgeID via_edge,
                    angularDeviation(intersection[1].turn.angle, intersection[2].turn.angle) <
                        GROUP_ANGLE)))
         {
-            intersection[1].turn.instruction = {
-                detail::isRampClass(intersection[1].turn.eid, node_based_graph) ? ThirdRamp
-                                                                                : ThirdTurn,
-                second_direction};
-            intersection[2].turn.instruction = {
-                detail::isRampClass(intersection[2].turn.eid, node_based_graph) ? SecondRamp
-                                                                                : SecondTurn,
-                second_direction};
-            intersection[3].turn.instruction = {
-                detail::isRampClass(intersection[3].turn.eid, node_based_graph) ? FirstRamp
-                                                                                : FirstTurn,
-                second_direction};
+            BOOST_ASSERT(intersection[1].entry_allowed && intersection[2].entry_allowed &&
+                         intersection[3].entry_allowed);
+            // count backwards from the slightest turn
+            assignTrivialTurns(via_edge, intersection, 1, up_to);
         }
-        else if (intersection[1].entry_allowed && intersection[2].entry_allowed &&
-                 intersection[3].entry_allowed &&
-                 ((first_direction == second_direction &&
+        else if (((first_direction == second_direction &&
                    angularDeviation(intersection[2].turn.angle, intersection[3].turn.angle) >=
                        GROUP_ANGLE) ||
                   (second_direction == third_direction &&
                    angularDeviation(intersection[1].turn.angle, intersection[2].turn.angle) >=
                        GROUP_ANGLE)))
         {
+            BOOST_ASSERT(intersection[1].entry_allowed && intersection[2].entry_allowed &&
+                         intersection[3].entry_allowed);
+
             if (angularDeviation(intersection[2].turn.angle, intersection[3].turn.angle) >=
                 GROUP_ANGLE)
             {
@@ -900,21 +535,6 @@ Intersection TurnHandler::assignRightTurns(const EdgeID via_edge,
                 handleDistinctConflict(via_edge, intersection[3], intersection[2]);
             }
         }
-        else if ((first_direction == second_direction &&
-                  intersection[1].entry_allowed != intersection[2].entry_allowed) ||
-                 (second_direction == third_direction &&
-                  intersection[2].entry_allowed != intersection[3].entry_allowed))
-        {
-            if (intersection[1].entry_allowed)
-                intersection[1].turn.instruction = {findBasicTurnType(via_edge, intersection[1]),
-                                                    first_direction};
-            if (intersection[2].entry_allowed)
-                intersection[2].turn.instruction = {findBasicTurnType(via_edge, intersection[2]),
-                                                    second_direction};
-            if (intersection[3].entry_allowed)
-                intersection[3].turn.instruction = {findBasicTurnType(via_edge, intersection[3]),
-                                                    third_direction};
-        }
         else
         {
             util::SimpleLogger().Write(logWARNING)
@@ -929,47 +549,12 @@ Intersection TurnHandler::assignRightTurns(const EdgeID via_edge,
                     << " Road Class: " << (int)out_data.road_classification.road_class;
             }
 
-            for (std::size_t i = 1; i < up_to; ++i)
-                if (intersection[i].entry_allowed)
-                    intersection[i].turn.instruction = {
-                        findBasicTurnType(via_edge, intersection[i]),
-                        getTurnDirection(intersection[i].turn.angle)};
+            assignTrivialTurns(via_edge, intersection, 1, up_to);
         }
     }
-    else if (up_to == 5)
-    {
-        if (intersection[4].entry_allowed)
-            intersection[4].turn.instruction = {
-                detail::isRampClass(intersection[4].turn.eid, node_based_graph) ? FirstRamp
-                                                                                : FirstTurn,
-                DirectionModifier::Right};
-        if (intersection[3].entry_allowed)
-            intersection[3].turn.instruction = {
-                detail::isRampClass(intersection[3].turn.eid, node_based_graph) ? SecondRamp
-                                                                                : SecondTurn,
-                DirectionModifier::Right};
-        if (intersection[2].entry_allowed)
-            intersection[2].turn.instruction = {
-                detail::isRampClass(intersection[2].turn.eid, node_based_graph) ? ThirdRamp
-                                                                                : ThirdTurn,
-                DirectionModifier::Right};
-        if (intersection[1].entry_allowed)
-            intersection[1].turn.instruction = {
-                detail::isRampClass(intersection[1].turn.eid, node_based_graph) ? FourthRamp
-                                                                                : FourthTurn,
-                DirectionModifier::Right};
-    }
     else
     {
-        for (std::size_t i = 1; i < up_to; ++i)
-        {
-            auto &road = intersection[i];
-            if (!road.entry_allowed)
-                continue;
-            road.turn.instruction = {detail::isRampClass(road.turn.eid, node_based_graph) ? Ramp
-                                                                                          : Turn,
-                                     getTurnDirection(road.turn.angle)};
-        }
+        assignTrivialTurns(via_edge, intersection, 1, up_to);
     }
     return intersection;
 }
@@ -993,7 +578,7 @@ std::pair<std::size_t, std::size_t> TurnHandler::findFork(const Intersection &in
     if (best_deviation <= NARROW_TURN_ANGLE)
     {
         std::size_t left = best, right = best;
-        if (intersection[left].turn.angle >= 180)
+        if (intersection[best].turn.angle >= 180)
         {
             // due to best > 1, we can safely decrement right
             --right;
@@ -1010,20 +595,22 @@ std::pair<std::size_t, std::size_t> TurnHandler::findFork(const Intersection &in
         }
         while (left + 1 < intersection.size() &&
                angularDeviation(intersection[left].turn.angle, intersection[left + 1].turn.angle) <
-                   NARROW_TURN_ANGLE)
+                   NARROW_TURN_ANGLE &&
+               angularDeviation(intersection[left].turn.angle, STRAIGHT_ANGLE) <= GROUP_ANGLE)
             ++left;
         while (right > 1 &&
                angularDeviation(intersection[right].turn.angle,
-                                intersection[right - 1].turn.angle) < NARROW_TURN_ANGLE)
+                                intersection[right - 1].turn.angle) < NARROW_TURN_ANGLE &&
+               angularDeviation(intersection[right - 1].turn.angle, STRAIGHT_ANGLE) <= GROUP_ANGLE)
             --right;
 
         // TODO check whether 2*NARROW_TURN is too large
-        if (right < left &&
+        if (0 < right && right < left &&
             angularDeviation(intersection[left].turn.angle,
                              intersection[(left + 1) % intersection.size()].turn.angle) >=
-                2 * NARROW_TURN_ANGLE &&
+                GROUP_ANGLE &&
             angularDeviation(intersection[right].turn.angle, intersection[right - 1].turn.angle) >=
-                2 * NARROW_TURN_ANGLE)
+                GROUP_ANGLE)
             return std::make_pair(right, left);
     }
     return std::make_pair(std::size_t{0}, std::size_t{0});
@@ -1109,21 +696,6 @@ void TurnHandler::handleDistinctConflict(const EdgeID via_edge,
         right.turn.instruction = {right_type, DirectionModifier::Left};
         return;
     }
-    // Both turns?
-    if (TurnType::Ramp != left_type && TurnType::Ramp != right_type)
-    {
-        if (left.turn.angle < STRAIGHT_ANGLE)
-        {
-            left.turn.instruction = {TurnType::FirstTurn, getTurnDirection(left.turn.angle)};
-            right.turn.instruction = {TurnType::SecondTurn, getTurnDirection(right.turn.angle)};
-        }
-        else
-        {
-            left.turn.instruction = {TurnType::SecondTurn, getTurnDirection(left.turn.angle)};
-            right.turn.instruction = {TurnType::FirstTurn, getTurnDirection(right.turn.angle)};
-        }
-        return;
-    }
     // Shift the lesser penalty
     if (getTurnDirection(left.turn.angle) == DirectionModifier::SharpLeft)
     {
diff --git a/src/extractor/raster_source.cpp b/src/extractor/raster_source.cpp
index 16e41f9..66a5079 100644
--- a/src/extractor/raster_source.cpp
+++ b/src/extractor/raster_source.cpp
@@ -17,7 +17,7 @@ RasterSource::RasterSource(RasterGrid _raster_data,
                            int _xmax,
                            int _ymin,
                            int _ymax)
-    : xstep(calcSize(_xmin, _xmax, _width)), ystep(calcSize(_ymin, _ymax, _height)),
+    : xstep(CalcSize(_xmin, _xmax, _width)), ystep(CalcSize(_ymin, _ymax, _height)),
       raster_data(std::move(_raster_data)), width(_width), height(_height), xmin(_xmin),
       xmax(_xmax), ymin(_ymin), ymax(_ymax)
 {
@@ -25,14 +25,14 @@ RasterSource::RasterSource(RasterGrid _raster_data,
     BOOST_ASSERT(ystep != 0);
 }
 
-float RasterSource::calcSize(int min, int max, std::size_t count) const
+float RasterSource::CalcSize(int min, int max, std::size_t count) const
 {
     BOOST_ASSERT(count > 0);
     return (max - min) / (static_cast<float>(count) - 1);
 }
 
 // Query raster source for nearest data point
-RasterDatum RasterSource::getRasterData(const int lon, const int lat) const
+RasterDatum RasterSource::GetRasterData(const int lon, const int lat) const
 {
     if (lon < xmin || lon > xmax || lat < ymin || lat > ymax)
     {
@@ -46,7 +46,7 @@ RasterDatum RasterSource::getRasterData(const int lon, const int lat) const
 }
 
 // Query raster source using bilinear interpolation
-RasterDatum RasterSource::getRasterInterpolate(const int lon, const int lat) const
+RasterDatum RasterSource::GetRasterInterpolate(const int lon, const int lat) const
 {
     if (lon < xmin || lon > xmax || lat < ymin || lat > ymax)
     {
@@ -74,7 +74,7 @@ RasterDatum RasterSource::getRasterInterpolate(const int lon, const int lat) con
 }
 
 // Load raster source into memory
-int SourceContainer::loadRasterSource(const std::string &path_string,
+int SourceContainer::LoadRasterSource(const std::string &path_string,
                                       double xmin,
                                       double xmax,
                                       double ymin,
@@ -120,7 +120,7 @@ int SourceContainer::loadRasterSource(const std::string &path_string,
 }
 
 // External function for looking up nearest data point from a specified source
-RasterDatum SourceContainer::getRasterDataFromSource(unsigned int source_id, double lon, double lat)
+RasterDatum SourceContainer::GetRasterDataFromSource(unsigned int source_id, double lon, double lat)
 {
     if (LoadedSources.size() < source_id + 1)
     {
@@ -133,13 +133,13 @@ RasterDatum SourceContainer::getRasterDataFromSource(unsigned int source_id, dou
     BOOST_ASSERT(lon > -180);
 
     const auto &found = LoadedSources[source_id];
-    return found.getRasterData(static_cast<int>(util::toFixed(util::FloatLongitude(lon))),
+    return found.GetRasterData(static_cast<int>(util::toFixed(util::FloatLongitude(lon))),
                                static_cast<int>(util::toFixed(util::FloatLatitude(lat))));
 }
 
 // External function for looking up interpolated data from a specified source
 RasterDatum
-SourceContainer::getRasterInterpolateFromSource(unsigned int source_id, double lon, double lat)
+SourceContainer::GetRasterInterpolateFromSource(unsigned int source_id, double lon, double lat)
 {
     if (LoadedSources.size() < source_id + 1)
     {
@@ -152,7 +152,7 @@ SourceContainer::getRasterInterpolateFromSource(unsigned int source_id, double l
     BOOST_ASSERT(lon > -180);
 
     const auto &found = LoadedSources[source_id];
-    return found.getRasterInterpolate(static_cast<int>(util::toFixed(util::FloatLongitude(lon))),
+    return found.GetRasterInterpolate(static_cast<int>(util::toFixed(util::FloatLongitude(lon))),
                                       static_cast<int>(util::toFixed(util::FloatLatitude(lat))));
 }
 }
diff --git a/src/extractor/scripting_environment.cpp b/src/extractor/scripting_environment.cpp
index 0840f7d..3ffe016 100644
--- a/src/extractor/scripting_environment.cpp
+++ b/src/extractor/scripting_environment.cpp
@@ -14,6 +14,7 @@
 #include "util/typedefs.hpp"
 
 #include <luabind/tag_function.hpp>
+#include <luabind/iterator_policy.hpp>
 #include <luabind/operator.hpp>
 
 #include <osmium/osm.hpp>
@@ -43,6 +44,11 @@ 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();
+}
+
 // Error handler
 int luaErrorCallback(lua_State *state)
 {
@@ -87,9 +93,9 @@ void ScriptingEnvironment::InitContext(ScriptingEnvironment::Context &context)
                              luabind::value("route", TRAVEL_MODE_ROUTE)],
          luabind::class_<SourceContainer>("sources")
              .def(luabind::constructor<>())
-             .def("load", &SourceContainer::loadRasterSource)
-             .def("query", &SourceContainer::getRasterDataFromSource)
-             .def("interpolate", &SourceContainer::getRasterInterpolateFromSource),
+             .def("load", &SourceContainer::LoadRasterSource)
+             .def("query", &SourceContainer::GetRasterDataFromSource)
+             .def("interpolate", &SourceContainer::GetRasterInterpolateFromSource),
          luabind::class_<const float>("constants")
              .enum_("enums")[luabind::value("precision", COORDINATE_PRECISION)],
 
@@ -134,10 +140,19 @@ void ScriptingEnvironment::InitContext(ScriptingEnvironment::Context &context)
                        &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::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),
          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>)
-             .def("id", &osmium::Way::id),
+             .def("id", &osmium::Way::id)
+             .def("get_nodes", get_nodes_for_way, luabind::return_stl_iterator),
          luabind::class_<InternalExtractorEdge>("EdgeSource")
              .def_readonly("source_coordinate", &InternalExtractorEdge::source_coordinate)
              .def_readwrite("weight_data", &InternalExtractorEdge::weight_data),
diff --git a/src/extractor/suffix_table.cpp b/src/extractor/suffix_table.cpp
new file mode 100644
index 0000000..3c4bc0f
--- /dev/null
+++ b/src/extractor/suffix_table.cpp
@@ -0,0 +1,48 @@
+#include "extractor/suffix_table.hpp"
+
+#include "util/lua_util.hpp"
+#include "util/simple_logger.hpp"
+
+#include <boost/algorithm/string.hpp>
+#include <boost/assert.hpp>
+#include <boost/range/adaptor/transformed.hpp>
+#include <boost/ref.hpp>
+
+#include <iterator>
+#include <vector>
+
+namespace osrm
+{
+namespace extractor
+{
+
+SuffixTable::SuffixTable(lua_State *lua_state)
+{
+    BOOST_ASSERT(lua_state != nullptr);
+    if (!util::luaFunctionExists(lua_state, "get_name_suffix_list"))
+        return;
+
+    std::vector<std::string> suffixes_vector;
+    try
+    {
+        // call lua profile to compute turn penalty
+        luabind::call_function<void>(lua_state, "get_name_suffix_list",
+                                     boost::ref(suffixes_vector));
+    }
+    catch (const luabind::error &er)
+    {
+        util::SimpleLogger().Write(logWARNING) << er.what();
+    }
+
+    for (auto &suffix : suffixes_vector)
+        boost::algorithm::to_lower(suffix);
+    suffix_set.insert(std::begin(suffixes_vector), std::end(suffixes_vector));
+}
+
+bool SuffixTable::isSuffix(const std::string &possible_suffix) const
+{
+    return suffix_set.count(possible_suffix) > 0;
+}
+
+} /* namespace extractor */
+} /* namespace osrm */
diff --git a/src/server/api/parameters_parser.cpp b/src/server/api/parameters_parser.cpp
index 1d4485a..d4a530f 100644
--- a/src/server/api/parameters_parser.cpp
+++ b/src/server/api/parameters_parser.cpp
@@ -19,28 +19,28 @@ namespace api
 namespace detail
 {
 template <typename T>
-using is_grammar_t = std::integral_constant<bool,
-                                            std::is_base_of<BaseParametersGrammar, T>::value ||
-                                                std::is_same<TileParametersGrammar, T>::value>;
+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,
+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;
 
-    GrammarT grammar;
+    static const GrammarT grammar;
 
     try
     {
-        const auto ok = boost::spirit::qi::parse(iter, end, grammar);
+        ParameterT 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)
-            return std::move(grammar.parameters);
+            return std::move(parameters);
     }
     catch (const qi::expectation_failure<It> &failure)
     {
@@ -54,46 +54,39 @@ boost::optional<ParameterT> parseParameters(std::string::iterator &iter,
 } // 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);
+    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);
+    return detail::parseParameters<engine::api::TileParameters, TileParametersGrammar<>>(iter, end);
 }
 
 } // ns api
diff --git a/src/server/api/url_parser.cpp b/src/server/api/url_parser.cpp
index 7ef6db0..875e78a 100644
--- a/src/server/api/url_parser.cpp
+++ b/src/server/api/url_parser.cpp
@@ -1,15 +1,25 @@
 #include "server/api/url_parser.hpp"
 #include "engine/polyline_compressor.hpp"
 
-//#define BOOST_SPIRIT_DEBUG
+#include <boost/fusion/include/adapt_struct.hpp>
+#include <boost/spirit/include/phoenix.hpp>
 #include <boost/spirit/include/qi.hpp>
+#include <boost/spirit/repository/include/qi_iter_pos.hpp>
 
 #include <string>
 #include <type_traits>
 
+BOOST_FUSION_ADAPT_STRUCT(osrm::server::api::ParsedURL,
+    (std::string, service)
+    (unsigned, version)
+    (std::string, profile)
+    (std::string, query)
+)
+
 // Keep impl. TU local
 namespace
 {
+namespace ph = boost::phoenix;
 namespace qi = boost::spirit::qi;
 
 template <typename Iterator, typename Into> //
@@ -17,6 +27,8 @@ struct URLParser final : qi::grammar<Iterator, Into>
 {
     URLParser() : URLParser::base_type(start)
     {
+        using boost::spirit::repository::qi::iter_pos;
+
         alpha_numeral = qi::char_("a-zA-Z0-9");
         polyline_chars = qi::char_("a-zA-Z0-9_.--[]{}@?|\\%~`^");
         all_chars = polyline_chars | qi::char_("=,;:&().");
@@ -28,10 +40,14 @@ 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('/') > 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))
     }
@@ -61,12 +77,12 @@ boost::optional<ParsedURL> parseURL(std::string::iterator &iter, const std::stri
 {
     using It = std::decay<decltype(iter)>::type;
 
-    static URLParser<It, ParsedURL()> const parser;
+    static URLParser<It, ParsedURL(It)> const parser;
     ParsedURL out;
 
     try
     {
-        const auto ok = boost::spirit::qi::parse(iter, end, parser, out);
+        const auto ok = boost::spirit::qi::parse(iter, end, parser(boost::phoenix::val(iter)), out);
 
         if (ok && iter == end)
             return boost::make_optional(out);
diff --git a/src/server/service/match_service.cpp b/src/server/service/match_service.cpp
index dec8006..7321b86 100644
--- a/src/server/service/match_service.cpp
+++ b/src/server/service/match_service.cpp
@@ -40,7 +40,7 @@ std::string getWrongOptionHelp(const engine::api::MatchParameters &parameters)
 }
 } // anon. ns
 
-engine::Status MatchService::RunQuery(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>();
@@ -53,7 +53,7 @@ engine::Status MatchService::RunQuery(std::string &query, ResultT &result)
         const auto position = std::distance(query.begin(), query_iterator);
         json_result.values["code"] = "InvalidQuery";
         json_result.values["message"] =
-            "Query string malformed close to position " + std::to_string(position);
+            "Query string malformed close to position " + std::to_string(prefix_length + position);
         return engine::Status::Error;
     }
 
diff --git a/src/server/service/nearest_service.cpp b/src/server/service/nearest_service.cpp
index 1ea1fa8..4ffd021 100644
--- a/src/server/service/nearest_service.cpp
+++ b/src/server/service/nearest_service.cpp
@@ -39,7 +39,7 @@ std::string getWrongOptionHelp(const engine::api::NearestParameters &parameters)
 }
 } // anon. ns
 
-engine::Status NearestService::RunQuery(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>();
@@ -52,7 +52,7 @@ engine::Status NearestService::RunQuery(std::string &query, ResultT &result)
         const auto position = std::distance(query.begin(), query_iterator);
         json_result.values["code"] = "InvalidQuery";
         json_result.values["message"] =
-            "Query string malformed close to position " + std::to_string(position);
+            "Query string malformed close to position " + std::to_string(prefix_length + position);
         return engine::Status::Error;
     }
     BOOST_ASSERT(parameters);
diff --git a/src/server/service/route_service.cpp b/src/server/service/route_service.cpp
index f06cbdb..a7b85b5 100644
--- a/src/server/service/route_service.cpp
+++ b/src/server/service/route_service.cpp
@@ -36,7 +36,7 @@ std::string getWrongOptionHelp(const engine::api::RouteParameters &parameters)
 }
 } // anon. ns
 
-engine::Status RouteService::RunQuery(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>();
@@ -49,7 +49,7 @@ engine::Status RouteService::RunQuery(std::string &query, ResultT &result)
         const auto position = std::distance(query.begin(), query_iterator);
         json_result.values["code"] = "InvalidQuery";
         json_result.values["message"] =
-            "Query string malformed close to position " + std::to_string(position);
+            "Query string malformed close to position " + std::to_string(prefix_length + position);
         return engine::Status::Error;
     }
     BOOST_ASSERT(parameters);
diff --git a/src/server/service/table_service.cpp b/src/server/service/table_service.cpp
index 9b0f817..bb93019 100644
--- a/src/server/service/table_service.cpp
+++ b/src/server/service/table_service.cpp
@@ -57,7 +57,7 @@ std::string getWrongOptionHelp(const engine::api::TableParameters &parameters)
 }
 } // anon. ns
 
-engine::Status TableService::RunQuery(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>();
@@ -70,7 +70,7 @@ engine::Status TableService::RunQuery(std::string &query, ResultT &result)
         const auto position = std::distance(query.begin(), query_iterator);
         json_result.values["code"] = "InvalidQuery";
         json_result.values["message"] =
-            "Query string malformed close to position " + std::to_string(position);
+            "Query string malformed close to position " + std::to_string(prefix_length + position);
         return engine::Status::Error;
     }
     BOOST_ASSERT(parameters);
diff --git a/src/server/service/tile_service.cpp b/src/server/service/tile_service.cpp
index d8f1ba8..a4ded2c 100644
--- a/src/server/service/tile_service.cpp
+++ b/src/server/service/tile_service.cpp
@@ -15,7 +15,7 @@ namespace server
 namespace service
 {
 
-engine::Status TileService::RunQuery(std::string &query, ResultT &result)
+engine::Status TileService::RunQuery(std::size_t prefix_length, std::string &query, ResultT &result)
 {
     auto query_iterator = query.begin();
     auto parameters =
@@ -27,7 +27,7 @@ engine::Status TileService::RunQuery(std::string &query, ResultT &result)
         auto &json_result = result.get<util::json::Object>();
         json_result.values["code"] = "InvalidQuery";
         json_result.values["message"] =
-            "Query string malformed close to position " + std::to_string(position);
+            "Query string malformed close to position " + std::to_string(prefix_length + position);
         return engine::Status::Error;
     }
     BOOST_ASSERT(parameters);
diff --git a/src/server/service/trip_service.cpp b/src/server/service/trip_service.cpp
index 82a1bfd..e0b51c0 100644
--- a/src/server/service/trip_service.cpp
+++ b/src/server/service/trip_service.cpp
@@ -38,7 +38,7 @@ std::string getWrongOptionHelp(const engine::api::TripParameters &parameters)
 }
 } // anon. ns
 
-engine::Status TripService::RunQuery(std::string &query, ResultT &result)
+engine::Status TripService::RunQuery(std::size_t prefix_length, std::string &query, ResultT &result)
 {
     result = util::json::Object();
     auto &json_result = result.get<util::json::Object>();
@@ -53,7 +53,7 @@ engine::Status TripService::RunQuery(std::string &query, ResultT &result)
         auto &json_result = result.get<util::json::Object>();
         json_result.values["code"] = "InvalidQuery";
         json_result.values["message"] =
-            "Query string malformed close to position " + std::to_string(position);
+            "Query string malformed close to position " + std::to_string(prefix_length + position);
         return engine::Status::Error;
     }
     BOOST_ASSERT(parameters);
diff --git a/src/server/service_handler.cpp b/src/server/service_handler.cpp
index 98ba6a6..03bcf86 100644
--- a/src/server/service_handler.cpp
+++ b/src/server/service_handler.cpp
@@ -48,7 +48,7 @@ engine::Status ServiceHandler::RunQuery(api::ParsedURL parsed_url,
         return engine::Status::Error;
     }
 
-    return service->RunQuery(parsed_url.query, result);
+    return service->RunQuery(parsed_url.prefix_length, parsed_url.query, result);
 }
 }
 }
diff --git a/src/storage/storage.cpp b/src/storage/storage.cpp
index 67fd122..43967dc 100644
--- a/src/storage/storage.cpp
+++ b/src/storage/storage.cpp
@@ -39,7 +39,7 @@ namespace osrm
 namespace storage
 {
 
-using RTreeLeaf = typename engine::datafacade::BaseDataFacade::RTreeLeaf;
+using RTreeLeaf = engine::datafacade::BaseDataFacade::RTreeLeaf;
 using RTreeNode =
     util::StaticRTree<RTreeLeaf, util::ShM<util::Coordinate, true>::vector, true>::TreeNode;
 using QueryGraph = util::StaticGraph<contractor::QueryEdge::EdgeData>;
diff --git a/src/tools/components.cpp b/src/tools/components.cpp
index 45131a8..96d300b 100644
--- a/src/tools/components.cpp
+++ b/src/tools/components.cpp
@@ -122,10 +122,10 @@ int main(int argc, char *argv[]) try
 
     auto tarjan =
         osrm::util::make_unique<osrm::extractor::TarjanSCC<osrm::tools::TarjanGraph>>(graph);
-    tarjan->run();
-    osrm::util::SimpleLogger().Write() << "identified: " << tarjan->get_number_of_components()
+    tarjan->Run();
+    osrm::util::SimpleLogger().Write() << "identified: " << tarjan->GetNumberOfComponents()
                                        << " many components";
-    osrm::util::SimpleLogger().Write() << "identified " << tarjan->get_size_one_count()
+    osrm::util::SimpleLogger().Write() << "identified " << tarjan->GetSizeOneCount()
                                        << " size 1 SCCs";
 
     // output
@@ -136,7 +136,6 @@ int main(int argc, char *argv[]) try
     osrm::tools::deleteFileIfExists("component.shx");
     osrm::tools::deleteFileIfExists("component.shp");
 
-    osrm::util::Percent percentage(graph->GetNumberOfNodes());
 
     OGRRegisterAll();
 
@@ -167,11 +166,11 @@ int main(int argc, char *argv[]) try
                                        << TIMER_MSEC(SCC_RUN_SETUP) / 1000. << "s";
 
     uint64_t total_network_length = 0;
-    percentage.reinit(graph->GetNumberOfNodes());
+    osrm::util::Percent percentage(graph->GetNumberOfNodes());
     TIMER_START(SCC_OUTPUT);
     for (const NodeID source : osrm::util::irange(0u, graph->GetNumberOfNodes()))
     {
-        percentage.printIncrement();
+        percentage.PrintIncrement();
         for (const auto current_edge : graph->GetAdjacentEdgeRange(source))
         {
             const auto target = graph->GetTarget(current_edge);
@@ -187,8 +186,8 @@ int main(int argc, char *argv[]) try
                 BOOST_ASSERT(target != SPECIAL_NODEID);
 
                 const unsigned size_of_containing_component =
-                    std::min(tarjan->get_component_size(tarjan->get_component_id(source)),
-                             tarjan->get_component_size(tarjan->get_component_id(target)));
+                    std::min(tarjan->GetComponentSize(tarjan->GetComponentID(source)),
+                             tarjan->GetComponentSize(tarjan->GetComponentID(target)));
 
                 // edges that end on bollard nodes may actually be in two distinct components
                 if (size_of_containing_component < 1000)
diff --git a/src/tools/contract.cpp b/src/tools/contract.cpp
index c9460db..f743bec 100644
--- a/src/tools/contract.cpp
+++ b/src/tools/contract.cpp
@@ -43,6 +43,10 @@ return_code parseArguments(int argc, char *argv[], contractor::ContractorConfig
                                   &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(),
+        "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),
         "Use .level file to retain the contaction level for each node from the last run.");
diff --git a/src/util/coordinate.cpp b/src/util/coordinate.cpp
index 9ccbf17..0cec666 100644
--- a/src/util/coordinate.cpp
+++ b/src/util/coordinate.cpp
@@ -17,24 +17,6 @@ namespace osrm
 namespace util
 {
 
-Coordinate::Coordinate()
-    : lon(std::numeric_limits<int>::min()), lat(std::numeric_limits<int>::min())
-{
-}
-
-Coordinate::Coordinate(const FloatCoordinate &other)
-    : Coordinate(toFixed(other.lon), toFixed(other.lat))
-{
-}
-
-Coordinate::Coordinate(const FloatLongitude lon_, const FloatLatitude lat_)
-    : Coordinate(toFixed(lon_), toFixed(lat_))
-{
-}
-
-Coordinate::Coordinate(const FixedLongitude lon_, const FixedLatitude lat_) : lon(lon_), lat(lat_)
-{
-}
 
 bool Coordinate::IsValid() const
 {
@@ -44,24 +26,6 @@ bool Coordinate::IsValid() const
              lon < FixedLongitude(-180 * COORDINATE_PRECISION));
 }
 
-FloatCoordinate::FloatCoordinate()
-    : lon(std::numeric_limits<double>::min()), lat(std::numeric_limits<double>::min())
-{
-}
-
-FloatCoordinate::FloatCoordinate(const Coordinate other)
-    : FloatCoordinate(toFloating(other.lon), toFloating(other.lat))
-{
-}
-
-FloatCoordinate::FloatCoordinate(const FixedLongitude lon_, const FixedLatitude lat_)
-    : FloatCoordinate(toFloating(lon_), toFloating(lat_))
-{
-}
-
-FloatCoordinate::FloatCoordinate(const FloatLongitude lon_, const FloatLatitude lat_) : lon(lon_), lat(lat_)
-{
-}
 
 bool FloatCoordinate::IsValid() const
 {
diff --git a/src/util/coordinate_calculation.cpp b/src/util/coordinate_calculation.cpp
index 24b280f..4e3492d 100644
--- a/src/util/coordinate_calculation.cpp
+++ b/src/util/coordinate_calculation.cpp
@@ -19,7 +19,7 @@ namespace coordinate_calculation
 {
 
 // Does not project the coordinates!
-std::uint64_t squaredEuclideanDistance(const Coordinate &lhs, const Coordinate &rhs)
+std::uint64_t squaredEuclideanDistance(const Coordinate lhs, const Coordinate rhs)
 {
     const std::uint64_t dx = static_cast<std::int32_t>(lhs.lon - rhs.lon);
     const std::uint64_t dy = static_cast<std::int32_t>(lhs.lat - rhs.lat);
@@ -41,8 +41,8 @@ double haversineDistance(const Coordinate coordinate_1, const Coordinate coordin
     const double ln1 = lon1 / COORDINATE_PRECISION;
     const double lt2 = lat2 / COORDINATE_PRECISION;
     const double ln2 = lon2 / COORDINATE_PRECISION;
-    const double dlat1 = lt1 * detail::DEGREE_TO_RAD;
 
+    const double dlat1 = lt1 * detail::DEGREE_TO_RAD;
     const double dlong1 = ln1 * detail::DEGREE_TO_RAD;
     const double dlat2 = lt2 * detail::DEGREE_TO_RAD;
     const double dlong2 = ln2 * detail::DEGREE_TO_RAD;
@@ -77,43 +77,6 @@ double greatCircleDistance(const Coordinate coordinate_1, const Coordinate coord
     return std::hypot(x_value, y_value) * detail::EARTH_RADIUS;
 }
 
-std::pair<double, FloatCoordinate> projectPointOnSegment(const FloatCoordinate &source,
-                                                         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};
-    // dot product of two un-normed vectors
-    const auto unnormed_ratio = static_cast<double>(slope_vector.lon * rel_coordinate.lon) +
-                                static_cast<double>(slope_vector.lat * rel_coordinate.lat);
-    // squared length of the slope vector
-    const auto squared_length = static_cast<double>(slope_vector.lon * slope_vector.lon) +
-                                static_cast<double>(slope_vector.lat * slope_vector.lat);
-
-    if (squared_length < std::numeric_limits<double>::epsilon())
-    {
-        return {0, source};
-    }
-
-    const double normed_ratio = unnormed_ratio / squared_length;
-    double clamped_ratio = normed_ratio;
-    if (clamped_ratio > 1.)
-    {
-        clamped_ratio = 1.;
-    }
-    else if (clamped_ratio < 0.)
-    {
-        clamped_ratio = 0.;
-    }
-
-    return {clamped_ratio,
-            {
-                FloatLongitude(1.0 - clamped_ratio) * source.lon +
-                    target.lon * FloatLongitude(clamped_ratio),
-                FloatLatitude(1.0 - clamped_ratio) * source.lat +
-                    target.lat * FloatLatitude(clamped_ratio),
-            }};
-}
 
 double perpendicularDistance(const Coordinate segment_source,
                              const Coordinate segment_target,
diff --git a/taginfo.json b/taginfo.json
index 8acc7d7..574b1dc 100644
--- a/taginfo.json
+++ b/taginfo.json
@@ -205,6 +205,16 @@
             "description": "Ref of road for navigation instructions, overrides name."
         },
         {
+            "key": "destination",
+            "object_types": [ "way" ],
+            "description": "Destination of road for navigation instructions, supplements name."
+        },
+        {
+            "key": "destination:ref",
+            "object_types": [ "way" ],
+            "description": "Destination of road for navigation instructions, supplements name."
+        },
+        {
             "key": "junction",
             "object_types": [ "way" ],
             "value": "roundabout"
diff --git a/unit_tests/extractor/raster_source.cpp b/unit_tests/extractor/raster_source.cpp
index 1c66f12..606f273 100644
--- a/unit_tests/extractor/raster_source.cpp
+++ b/unit_tests/extractor/raster_source.cpp
@@ -1,6 +1,6 @@
 #include "extractor/raster_source.hpp"
-#include "util/typedefs.hpp"
 #include "util/exception.hpp"
+#include "util/typedefs.hpp"
 
 #include <osrm/coordinate.hpp>
 
@@ -15,19 +15,15 @@ using namespace osrm::extractor;
 int normalize(double coord) { return static_cast<int>(coord * COORDINATE_PRECISION); }
 
 #define CHECK_QUERY(source_id, lon, lat, expected)                                                 \
-    BOOST_CHECK_EQUAL(                                                                             \
-        sources.getRasterDataFromSource(source_id, lon, lat).datum,          \
-        expected)
+    BOOST_CHECK_EQUAL(sources.GetRasterDataFromSource(source_id, lon, lat).datum, expected)
 
 #define CHECK_INTERPOLATE(source_id, lon, lat, expected)                                           \
-    BOOST_CHECK_EQUAL(                                                                             \
-        sources.getRasterInterpolateFromSource(source_id, lon, lat).datum,   \
-        expected)
+    BOOST_CHECK_EQUAL(sources.GetRasterInterpolateFromSource(source_id, lon, lat).datum, expected)
 
 BOOST_AUTO_TEST_CASE(raster_test)
 {
     SourceContainer sources;
-    int source_id = sources.loadRasterSource("../unit_tests/fixtures/raster_data.asc", 0, 0.09, 0,
+    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);
 
@@ -71,15 +67,15 @@ BOOST_AUTO_TEST_CASE(raster_test)
     CHECK_INTERPOLATE(0, 0.056, 0.028, 68);
     CHECK_INTERPOLATE(0, 0.05, 0.028, 56);
 
-    int source_already_loaded_id = sources.loadRasterSource(
+    int source_already_loaded_id = sources.LoadRasterSource(
         "../unit_tests/fixtures/raster_data.asc", 0, 0.09, 0, 0.09, 10, 10);
 
     BOOST_CHECK_EQUAL(source_already_loaded_id, 0);
-    BOOST_CHECK_THROW(sources.getRasterDataFromSource(1, normalize(0.02), normalize(0.02)),
+    BOOST_CHECK_THROW(sources.GetRasterDataFromSource(1, normalize(0.02), normalize(0.02)),
                       util::exception);
 
     BOOST_CHECK_THROW(
-        sources.loadRasterSource("../unit_tests/fixtures/nonexistent.asc", 0, 0.1, 0, 0.1, 7, 7),
+        sources.LoadRasterSource("../unit_tests/fixtures/nonexistent.asc", 0, 0.1, 0, 0.1, 7, 7),
         util::exception);
 }
 
diff --git a/unit_tests/mocks/mock_datafacade.hpp b/unit_tests/mocks/mock_datafacade.hpp
index f608b66..a7ee834 100644
--- a/unit_tests/mocks/mock_datafacade.hpp
+++ b/unit_tests/mocks/mock_datafacade.hpp
@@ -78,7 +78,7 @@ class MockDataFacade final : public engine::datafacade::BaseDataFacade
         return TRAVEL_MODE_INACCESSIBLE;
     }
     std::vector<RTreeLeaf> GetEdgesInBox(const util::Coordinate /* south_west */,
-                                         const util::Coordinate /*north_east */) override
+                                         const util::Coordinate /*north_east */) const override
     {
         return {};
     }
@@ -87,14 +87,14 @@ class MockDataFacade final : public engine::datafacade::BaseDataFacade
     NearestPhantomNodesInRange(const util::Coordinate /*input_coordinate*/,
                                const float /*max_distance*/,
                                const int /*bearing*/,
-                               const int /*bearing_range*/) override
+                               const int /*bearing_range*/) const override
     {
         return {};
     }
 
     std::vector<engine::PhantomNodeWithDistance>
     NearestPhantomNodesInRange(const util::Coordinate /*input_coordinate*/,
-                               const float /*max_distance*/) override
+                               const float /*max_distance*/) const override
     {
         return {};
     }
@@ -104,7 +104,7 @@ class MockDataFacade final : public engine::datafacade::BaseDataFacade
                         const unsigned /*max_results*/,
                         const double /*max_distance*/,
                         const int /*bearing*/,
-                        const int /*bearing_range*/) override
+                        const int /*bearing_range*/) const override
     {
         return {};
     }
@@ -113,14 +113,14 @@ class MockDataFacade final : public engine::datafacade::BaseDataFacade
     NearestPhantomNodes(const util::Coordinate /*input_coordinate*/,
                         const unsigned /*max_results*/,
                         const int /*bearing*/,
-                        const int /*bearing_range*/) override
+                        const int /*bearing_range*/) const override
     {
         return {};
     }
 
     std::vector<engine::PhantomNodeWithDistance>
     NearestPhantomNodes(const util::Coordinate /*input_coordinate*/,
-                        const unsigned /*max_results*/) override
+                        const unsigned /*max_results*/) const override
     {
         return {};
     }
@@ -128,21 +128,21 @@ class MockDataFacade final : public engine::datafacade::BaseDataFacade
     std::vector<engine::PhantomNodeWithDistance>
     NearestPhantomNodes(const util::Coordinate /*input_coordinate*/,
                         const unsigned /*max_results*/,
-                        const double /*max_distance*/) override
+                        const double /*max_distance*/) const override
     {
         return {};
     }
 
     std::pair<engine::PhantomNode, engine::PhantomNode>
     NearestPhantomNodeWithAlternativeFromBigComponent(
-        const util::Coordinate /*input_coordinate*/) override
+        const util::Coordinate /*input_coordinate*/) const override
     {
         return {};
     }
 
     std::pair<engine::PhantomNode, engine::PhantomNode>
     NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate /*input_coordinate*/,
-                                                      const double /*max_distance*/) override
+                                                      const double /*max_distance*/) const override
     {
         return {};
     }
@@ -151,7 +151,7 @@ class MockDataFacade final : public engine::datafacade::BaseDataFacade
     NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate /*input_coordinate*/,
                                                       const double /*max_distance*/,
                                                       const int /*bearing*/,
-                                                      const int /*bearing_range*/) override
+                                                      const int /*bearing_range*/) const override
     {
         return {};
     }
@@ -159,7 +159,7 @@ class MockDataFacade final : public engine::datafacade::BaseDataFacade
     std::pair<engine::PhantomNode, engine::PhantomNode>
     NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate /*input_coordinate*/,
                                                       const int /*bearing*/,
-                                                      const int /*bearing_range*/) override
+                                                      const int /*bearing_range*/) const override
     {
         return {};
     };
diff --git a/unit_tests/server/parameters_parser.cpp b/unit_tests/server/parameters_parser.cpp
index ecf98f2..21ffd42 100644
--- a/unit_tests/server/parameters_parser.cpp
+++ b/unit_tests/server/parameters_parser.cpp
@@ -44,10 +44,12 @@ BOOST_AUTO_TEST_CASE(invalid_route_urls)
                       32UL);
     BOOST_CHECK_EQUAL(testInvalidOptions<RouteParameters>("1,2;3,4?overview=false&hints=foo"),
                       29UL);
+    BOOST_CHECK_EQUAL(testInvalidOptions<RouteParameters>("1,2;3,4?overview=false&hints=;;; ;"),
+                      32UL);
     BOOST_CHECK_EQUAL(testInvalidOptions<RouteParameters>("1,2;3,4?overview=false&geometries=foo"),
-                      22UL);
+                      34UL);
     BOOST_CHECK_EQUAL(testInvalidOptions<RouteParameters>("1,2;3,4?overview=false&overview=foo"),
-                      22L);
+                      32L);
     BOOST_CHECK_EQUAL(
         testInvalidOptions<RouteParameters>("1,2;3,4?overview=false&alternatives=foo"), 36UL);
     BOOST_CHECK_EQUAL(testInvalidOptions<RouteParameters>(""), 0);
@@ -88,6 +90,7 @@ BOOST_AUTO_TEST_CASE(valid_route_urls)
     CHECK_EQUAL_RANGE(reference_1.bearings, result_1->bearings);
     CHECK_EQUAL_RANGE(reference_1.radiuses, result_1->radiuses);
     CHECK_EQUAL_RANGE(reference_1.coordinates, result_1->coordinates);
+    CHECK_EQUAL_RANGE(reference_1.hints, result_1->hints);
 
     RouteParameters reference_2{};
     reference_2.alternatives = true;
@@ -104,6 +107,7 @@ BOOST_AUTO_TEST_CASE(valid_route_urls)
     CHECK_EQUAL_RANGE(reference_2.bearings, result_2->bearings);
     CHECK_EQUAL_RANGE(reference_2.radiuses, result_2->radiuses);
     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};
@@ -120,6 +124,7 @@ BOOST_AUTO_TEST_CASE(valid_route_urls)
     CHECK_EQUAL_RANGE(reference_3.bearings, result_3->bearings);
     CHECK_EQUAL_RANGE(reference_3.radiuses, result_3->radiuses);
     CHECK_EQUAL_RANGE(reference_3.coordinates, result_3->coordinates);
+    CHECK_EQUAL_RANGE(reference_3.hints, result_3->hints);
 
     std::vector<boost::optional<engine::Hint>> hints_4 = {
         engine::Hint::FromBase64("DAIAgP___"
@@ -154,6 +159,7 @@ BOOST_AUTO_TEST_CASE(valid_route_urls)
     CHECK_EQUAL_RANGE(reference_4.bearings, result_4->bearings);
     CHECK_EQUAL_RANGE(reference_4.radiuses, result_4->radiuses);
     CHECK_EQUAL_RANGE(reference_4.coordinates, result_4->coordinates);
+    CHECK_EQUAL_RANGE(reference_4.hints, result_4->hints);
 
     std::vector<boost::optional<engine::Bearing>> bearings_4 = {
         boost::none, engine::Bearing{200, 10}, engine::Bearing{100, 5},
@@ -177,6 +183,7 @@ BOOST_AUTO_TEST_CASE(valid_route_urls)
     CHECK_EQUAL_RANGE(reference_5.bearings, result_5->bearings);
     CHECK_EQUAL_RANGE(reference_5.radiuses, result_5->radiuses);
     CHECK_EQUAL_RANGE(reference_5.coordinates, result_5->coordinates);
+    CHECK_EQUAL_RANGE(reference_5.hints, result_5->hints);
 
     std::vector<util::Coordinate> coords_2 = {{util::FloatLongitude(0), util::FloatLatitude(1)},
                                               {util::FloatLongitude(2), util::FloatLatitude(3)},
@@ -194,6 +201,7 @@ BOOST_AUTO_TEST_CASE(valid_route_urls)
     CHECK_EQUAL_RANGE(reference_6.bearings, result_6->bearings);
     CHECK_EQUAL_RANGE(reference_6.radiuses, result_6->radiuses);
     CHECK_EQUAL_RANGE(reference_6.coordinates, result_6->coordinates);
+    CHECK_EQUAL_RANGE(reference_6.hints, result_6->hints);
 
     auto result_7 = parseParameters<RouteParameters>("1,2;3,4?radiuses=;unlimited");
     RouteParameters reference_7{};
@@ -208,6 +216,7 @@ BOOST_AUTO_TEST_CASE(valid_route_urls)
     CHECK_EQUAL_RANGE(reference_7.bearings, result_7->bearings);
     CHECK_EQUAL_RANGE(reference_7.radiuses, result_7->radiuses);
     CHECK_EQUAL_RANGE(reference_7.coordinates, result_7->coordinates);
+    CHECK_EQUAL_RANGE(reference_7.hints, result_7->hints);
 
     auto result_8 = parseParameters<RouteParameters>("1,2;3,4?radiuses=;");
     RouteParameters reference_8{};
@@ -222,6 +231,44 @@ BOOST_AUTO_TEST_CASE(valid_route_urls)
     reference_9.radiuses = {boost::none};
     BOOST_CHECK(result_9);
     CHECK_EQUAL_RANGE(reference_9.radiuses, result_9->radiuses);
+
+    // Some Hint's are empty
+    std::vector<util::Coordinate> coords_3 = {{util::FloatLongitude(1), util::FloatLatitude(2)},
+                                              {util::FloatLongitude(3), util::FloatLatitude(4)},
+                                              {util::FloatLongitude(5), util::FloatLatitude(6)},
+                                              {util::FloatLongitude(7), util::FloatLatitude(8)}};
+    std::vector<boost::optional<engine::Hint>> hints_10 = {
+        engine::Hint::FromBase64("DAIAgP___"
+                                 "38AAAAAAAAAAAIAAAAAAAAAEAAAAOgDAAD0AwAAGwAAAOUacQBQP5sCshpxAB0_"
+                                 "mwIAAAEBl-Umfg=="),
+        boost::none,
+        engine::Hint::FromBase64("cgAAgP___"
+                                 "39jAAAADgAAACIAAABeAAAAkQAAANoDAABOAgAAGwAAAFVGcQCiRJsCR0VxAOZFmw"
+                                 "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>>{}};
+    auto result_10 = parseParameters<RouteParameters>(
+        "1,2;3,4;5,6;7,8?steps=false&hints="
+        "DAIAgP___38AAAAAAAAAAAIAAAAAAAAAEAAAAOgDAAD0AwAAGwAAAOUacQBQP5sCshpxAB0_mwIAAAEBl-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.overview, result_10->overview);
+    BOOST_CHECK_EQUAL(reference_10.continue_straight, result_10->continue_straight);
+    CHECK_EQUAL_RANGE(reference_10.bearings, result_10->bearings);
+    CHECK_EQUAL_RANGE(reference_10.radiuses, result_10->radiuses);
+    CHECK_EQUAL_RANGE(reference_10.coordinates, result_10->coordinates);
+    CHECK_EQUAL_RANGE(reference_10.hints, result_10->hints);
 }
 
 BOOST_AUTO_TEST_CASE(valid_table_urls)
@@ -273,6 +320,16 @@ BOOST_AUTO_TEST_CASE(valid_match_urls)
     CHECK_EQUAL_RANGE(reference_1.bearings, result_1->bearings);
     CHECK_EQUAL_RANGE(reference_1.radiuses, result_1->radiuses);
     CHECK_EQUAL_RANGE(reference_1.coordinates, result_1->coordinates);
+
+    MatchParameters reference_2{};
+    reference_2.coordinates = coords_1;
+    reference_2.timestamps = {5, 6};
+    auto result_2 = parseParameters<MatchParameters>("1,2;3,4?timestamps=5;6");
+    BOOST_CHECK(result_2);
+    CHECK_EQUAL_RANGE(reference_2.timestamps, result_2->timestamps);
+    CHECK_EQUAL_RANGE(reference_2.bearings, result_2->bearings);
+    CHECK_EQUAL_RANGE(reference_2.radiuses, result_2->radiuses);
+    CHECK_EQUAL_RANGE(reference_2.coordinates, result_2->coordinates);
 }
 
 BOOST_AUTO_TEST_CASE(valid_nearest_urls)
@@ -287,6 +344,16 @@ BOOST_AUTO_TEST_CASE(valid_nearest_urls)
     CHECK_EQUAL_RANGE(reference_1.bearings, result_1->bearings);
     CHECK_EQUAL_RANGE(reference_1.radiuses, result_1->radiuses);
     CHECK_EQUAL_RANGE(reference_1.coordinates, result_1->coordinates);
+
+    NearestParameters reference_2{};
+    reference_2.coordinates = coords_1;
+    reference_2.number_of_results = 42;
+    auto result_2 = parseParameters<NearestParameters>("1,2?number=42");
+    BOOST_CHECK(result_2);
+    BOOST_CHECK_EQUAL(reference_2.number_of_results, result_2->number_of_results);
+    CHECK_EQUAL_RANGE(reference_2.bearings, result_2->bearings);
+    CHECK_EQUAL_RANGE(reference_2.radiuses, result_2->radiuses);
+    CHECK_EQUAL_RANGE(reference_2.coordinates, result_2->coordinates);
 }
 
 BOOST_AUTO_TEST_CASE(valid_tile_urls)
diff --git a/unit_tests/server/url_parser.cpp b/unit_tests/server/url_parser.cpp
index cca1f61..fd1f110 100644
--- a/unit_tests/server/url_parser.cpp
+++ b/unit_tests/server/url_parser.cpp
@@ -51,52 +51,57 @@ BOOST_AUTO_TEST_CASE(invalid_urls)
 
 BOOST_AUTO_TEST_CASE(valid_urls)
 {
-    api::ParsedURL reference_1{"route", 1, "profile", "0,1;2,3;4,5?options=value&foo=bar"};
+    api::ParsedURL reference_1{"route", 1, "profile", "0,1;2,3;4,5?options=value&foo=bar", 18UL};
     auto result_1 = api::parseURL("/route/v1/profile/0,1;2,3;4,5?options=value&foo=bar");
     BOOST_CHECK(result_1);
     BOOST_CHECK_EQUAL(reference_1.service, result_1->service);
     BOOST_CHECK_EQUAL(reference_1.version, result_1->version);
     BOOST_CHECK_EQUAL(reference_1.profile, result_1->profile);
     CHECK_EQUAL_RANGE(reference_1.query, result_1->query);
+    BOOST_CHECK_EQUAL(reference_1.prefix_length, result_1->prefix_length);
 
     // no options
-    api::ParsedURL reference_2{"route", 1, "profile", "0,1;2,3;4,5"};
+    api::ParsedURL reference_2{"route", 1, "profile", "0,1;2,3;4,5", 18UL};
     auto result_2 = api::parseURL("/route/v1/profile/0,1;2,3;4,5");
     BOOST_CHECK(result_2);
     BOOST_CHECK_EQUAL(reference_2.service, result_2->service);
     BOOST_CHECK_EQUAL(reference_2.version, result_2->version);
     BOOST_CHECK_EQUAL(reference_2.profile, result_2->profile);
     CHECK_EQUAL_RANGE(reference_2.query, result_2->query);
+    BOOST_CHECK_EQUAL(reference_2.prefix_length, result_2->prefix_length);
 
     // one coordinate
     std::vector<util::Coordinate> coords_3 = {
         util::Coordinate(util::FloatLongitude(0), util::FloatLatitude(1)),
     };
-    api::ParsedURL reference_3{"route", 1, "profile", "0,1"};
+    api::ParsedURL reference_3{"route", 1, "profile", "0,1", 18UL};
     auto result_3 = api::parseURL("/route/v1/profile/0,1");
     BOOST_CHECK(result_3);
     BOOST_CHECK_EQUAL(reference_3.service, result_3->service);
     BOOST_CHECK_EQUAL(reference_3.version, result_3->version);
     BOOST_CHECK_EQUAL(reference_3.profile, result_3->profile);
     CHECK_EQUAL_RANGE(reference_3.query, result_3->query);
+    BOOST_CHECK_EQUAL(reference_3.prefix_length, result_3->prefix_length);
 
     // polyline
-    api::ParsedURL reference_5{"route", 1, "profile", "polyline(_ibE?_seK_seK_seK_seK)?"};
+    api::ParsedURL reference_5{"route", 1, "profile", "polyline(_ibE?_seK_seK_seK_seK)?", 18UL};
     auto result_5 = api::parseURL("/route/v1/profile/polyline(_ibE?_seK_seK_seK_seK)?");
     BOOST_CHECK(result_5);
     BOOST_CHECK_EQUAL(reference_5.service, result_5->service);
     BOOST_CHECK_EQUAL(reference_5.version, result_5->version);
     BOOST_CHECK_EQUAL(reference_5.profile, result_5->profile);
     CHECK_EQUAL_RANGE(reference_5.query, result_5->query);
+    BOOST_CHECK_EQUAL(reference_5.prefix_length, result_5->prefix_length);
 
     // tile
-    api::ParsedURL reference_6{"route", 1, "profile", "tile(1,2,3).mvt"};
+    api::ParsedURL reference_6{"route", 1, "profile", "tile(1,2,3).mvt", 18UL};
     auto result_6 = api::parseURL("/route/v1/profile/tile(1,2,3).mvt");
-    BOOST_CHECK(result_5);
+    BOOST_CHECK(result_6);
     BOOST_CHECK_EQUAL(reference_6.service, result_6->service);
     BOOST_CHECK_EQUAL(reference_6.version, result_6->version);
     BOOST_CHECK_EQUAL(reference_6.profile, result_6->profile);
     CHECK_EQUAL_RANGE(reference_6.query, result_6->query);
+    BOOST_CHECK_EQUAL(reference_6.prefix_length, result_6->prefix_length);
 }
 
 BOOST_AUTO_TEST_SUITE_END()
diff --git a/unit_tests/util/static_rtree.cpp b/unit_tests/util/static_rtree.cpp
index f8be130..a20d0db 100644
--- a/unit_tests/util/static_rtree.cpp
+++ b/unit_tests/util/static_rtree.cpp
@@ -40,7 +40,7 @@ using TestStaticRTree = StaticRTree<TestData,
                                     false,
                                     TEST_BRANCHING_FACTOR,
                                     TEST_LEAF_NODE_SIZE>;
-using MiniStaticRTree = StaticRTree<TestData, std::vector<Coordinate>, false, 2, 3>;
+using MiniStaticRTree = StaticRTree<TestData, std::vector<Coordinate>, false, 2, 128>;
 
 // Choosen by a fair W20 dice roll (this value is completely arbitrary)
 constexpr unsigned RANDOM_SEED = 42;
@@ -52,7 +52,7 @@ static const int32_t WORLD_MAX_LON = 180 * COORDINATE_PRECISION;
 template <typename DataT> class LinearSearchNN
 {
   public:
-    LinearSearchNN(const std::shared_ptr<std::vector<Coordinate>> &coords,
+    LinearSearchNN(const std::vector<Coordinate> &coords,
                    const std::vector<DataT> &edges)
         : coords(coords), edges(edges)
     {
@@ -67,9 +67,9 @@ template <typename DataT> class LinearSearchNN
                                                                  const DataT &rhs) {
             using web_mercator::fromWGS84;
             const auto lhs_result = coordinate_calculation::projectPointOnSegment(
-                fromWGS84(coords->at(lhs.u)), fromWGS84(coords->at(lhs.v)), projected_input);
+                fromWGS84(coords[lhs.u]), fromWGS84(coords[lhs.v]), projected_input);
             const auto rhs_result = coordinate_calculation::projectPointOnSegment(
-                fromWGS84(coords->at(rhs.u)), fromWGS84(coords->at(rhs.v)), projected_input);
+                fromWGS84(coords[rhs.u]), fromWGS84(coords[rhs.v]), projected_input);
             const auto lhs_squared_dist = coordinate_calculation::squaredEuclideanDistance(
                 lhs_result.second, projected_input);
             const auto rhs_squared_dist = coordinate_calculation::squaredEuclideanDistance(
@@ -85,7 +85,7 @@ template <typename DataT> class LinearSearchNN
     }
 
   private:
-    const std::shared_ptr<std::vector<Coordinate>> &coords;
+    const std::vector<Coordinate> &coords;
     const std::vector<TestData> &edges;
 };
 
@@ -105,7 +105,7 @@ template <unsigned NUM_NODES, unsigned NUM_EDGES> struct RandomGraphFixture
         }
     };
 
-    RandomGraphFixture() : coords(std::make_shared<std::vector<Coordinate>>())
+    RandomGraphFixture()
     {
         std::mt19937 g(RANDOM_SEED);
 
@@ -116,10 +116,10 @@ template <unsigned NUM_NODES, unsigned NUM_EDGES> struct RandomGraphFixture
         {
             int lon = lon_udist(g);
             int lat = lat_udist(g);
-            coords->emplace_back(Coordinate(FixedLongitude(lon), FixedLatitude(lat)));
+            coords.emplace_back(Coordinate(FixedLongitude(lon), FixedLatitude(lat)));
         }
 
-        std::uniform_int_distribution<> edge_udist(0, coords->size() - 1);
+        std::uniform_int_distribution<> edge_udist(0, coords.size() - 1);
 
         std::unordered_set<std::pair<unsigned, unsigned>, TupleHash> used_edges;
 
@@ -138,7 +138,7 @@ template <unsigned NUM_NODES, unsigned NUM_EDGES> struct RandomGraphFixture
         }
     }
 
-    std::shared_ptr<std::vector<Coordinate>> coords;
+    std::vector<Coordinate> coords;
     std::vector<TestData> edges;
 };
 
@@ -146,12 +146,11 @@ struct GraphFixture
 {
     GraphFixture(const std::vector<std::pair<FloatLongitude, FloatLatitude>> &input_coords,
                  const std::vector<std::pair<unsigned, unsigned>> &input_edges)
-        : coords(std::make_shared<std::vector<Coordinate>>())
     {
 
         for (unsigned i = 0; i < input_coords.size(); i++)
         {
-            coords->emplace_back(input_coords[i].first, input_coords[i].second);
+            coords.emplace_back(input_coords[i].first, input_coords[i].second);
         }
 
         for (const auto &pair : input_edges)
@@ -169,7 +168,7 @@ struct GraphFixture
         }
     }
 
-    std::shared_ptr<std::vector<Coordinate>> coords;
+    std::vector<Coordinate> coords;
     std::vector<TestData> edges;
 };
 
@@ -189,13 +188,13 @@ typedef RandomGraphFixture<10, 30> TestRandomGraphFixture_10_30;
 
 template <typename RTreeT>
 void simple_verify_rtree(RTreeT &rtree,
-                         const std::shared_ptr<std::vector<Coordinate>> &coords,
+                         const std::vector<Coordinate> &coords,
                          const std::vector<TestData> &edges)
 {
     for (const auto &e : edges)
     {
-        const Coordinate &pu = coords->at(e.u);
-        const Coordinate &pv = coords->at(e.v);
+        const Coordinate &pu = coords[e.u];
+        const Coordinate &pv = coords[e.v];
         auto result_u = rtree.Nearest(pu, 1);
         auto result_v = rtree.Nearest(pv, 1);
         BOOST_CHECK(result_u.size() == 1 && result_v.size() == 1);
@@ -248,7 +247,7 @@ void build_rtree(const std::string &prefix,
     nodes_path = prefix + ".ramIndex";
     leaves_path = prefix + ".fileIndex";
 
-    RTreeT r(fixture->edges, nodes_path, leaves_path, *fixture->coords);
+    RTreeT r(fixture->edges, nodes_path, leaves_path, fixture->coords);
 }
 
 template <typename RTreeT = TestStaticRTree, typename FixtureT>
@@ -261,12 +260,12 @@ void construction_test(const std::string &prefix, FixtureT *fixture)
     LinearSearchNN<TestData> lsnn(fixture->coords, fixture->edges);
 
     simple_verify_rtree(rtree, fixture->coords, fixture->edges);
-    sampling_verify_rtree(rtree, lsnn, *fixture->coords, 100);
+    sampling_verify_rtree(rtree, lsnn, fixture->coords, 100);
 }
 
 BOOST_FIXTURE_TEST_CASE(construct_tiny, TestRandomGraphFixture_10_30)
 {
-    using TinyTestTree = StaticRTree<TestData, std::vector<Coordinate>, false, 2, 1>;
+    using TinyTestTree = StaticRTree<TestData, std::vector<Coordinate>, false, 2, 64>;
     construction_test<TinyTestTree>("test_tiny", this);
 }
 

-- 
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