[osrm] 03/22: Imported Upstream version 5.0.0~rc2+ds

Sebastiaan Couwenberg sebastic at moszumanska.debian.org
Fri Apr 29 22:44:13 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 0f417a46547154585fb801e7c7e373fc7452001f
Author: Bas Couwenberg <sebastic at xs4all.nl>
Date:   Fri Apr 29 23:13:13 2016 +0200

    Imported Upstream version 5.0.0~rc2+ds
---
 .gitignore                                         |    1 +
 .travis.yml                                        |   83 +-
 CHANGELOG.md                                       |   25 +
 CMakeLists.txt                                     |   80 +-
 README.md                                          |    1 +
 appveyor-build.bat                                 |    6 +-
 cmake/pkgconfig.in                                 |    4 +-
 cucumber.js                                        |    4 +-
 features/bicycle/access.feature                    |    5 +-
 features/bicycle/access_node.feature               |    5 +-
 features/bicycle/destination.feature               |   46 +-
 features/bicycle/mode.feature                      |   84 +-
 features/bicycle/pushing.feature                   |   22 +-
 features/bicycle/roundabout.feature                |   30 -
 features/car/access.feature                        |    6 +-
 features/car/destination.feature                   |   46 +-
 features/car/mode.feature                          |   18 +-
 features/car/roundabout.feature                    |   30 -
 features/foot/access.feature                       |    4 +-
 features/foot/access_node.feature                  |    4 +-
 features/guidance/continue.feature                 |   90 +
 features/guidance/end-of-road.feature              |  106 +
 features/guidance/fork.feature                     |  213 ++
 features/guidance/merge.feature                    |   52 +
 features/guidance/motorway.feature                 |  100 +-
 features/guidance/new-name.feature                 |  135 ++
 features/guidance/ramp.feature                     |  229 ++
 features/guidance/rotary-bike.feature              |  167 ++
 features/guidance/rotary.feature                   |  262 +++
 ...{roundabout.feature => roundabout-bike.feature} |   87 +-
 features/guidance/roundabout.feature               |  182 +-
 features/guidance/suppressed.feature               |   66 +
 features/guidance/turn.feature                     |  284 +++
 features/step_definitions/requests.js              |    9 +-
 features/step_definitions/trip.js                  |    1 +
 features/support/env.js                            |    3 +-
 features/support/route.js                          |    7 +-
 features/support/shared_steps.js                   |   10 +-
 features/testbot/graph.feature                     |    4 +-
 features/testbot/matching.feature                  |   19 +
 features/testbot/overlap.feature                   |   20 +-
 features/testbot/roundabout.feature                |   76 -
 features/testbot/status.feature                    |   38 +-
 features/testbot/turn_angles.feature               |   74 -
 features/testbot/turns.feature                     |  115 -
 features/testbot/uturn.feature                     |   93 -
 include/contractor/graph_contractor.hpp            |    6 +-
 include/engine/api/json_factory.hpp                |    3 +-
 include/engine/api/match_api.hpp                   |    4 +-
 include/engine/api/nearest_api.hpp                 |    2 +-
 include/engine/api/route_api.hpp                   |   20 +-
 include/engine/api/route_parameters.hpp            |   12 +-
 include/engine/api/table_api.hpp                   |    6 +-
 include/engine/api/trip_api.hpp                    |    8 +-
 include/engine/datafacade/datafacade_base.hpp      |    2 +-
 include/engine/datafacade/internal_datafacade.hpp  |    2 +-
 include/engine/datafacade/shared_datafacade.hpp    |    3 +-
 include/engine/douglas_peucker.hpp                 |   61 +-
 include/engine/geospatial_query.hpp                |    8 +-
 include/engine/guidance/assemble_leg.hpp           |  117 +-
 include/engine/guidance/assemble_steps.hpp         |   54 +-
 include/engine/guidance/route_leg.hpp              |    4 -
 include/engine/guidance/route_step.hpp             |    3 +-
 include/engine/hint.hpp                            |    4 +-
 .../engine/map_matching/hidden_markov_model.hpp    |    4 +-
 include/engine/phantom_node.hpp                    |    2 +-
 include/engine/plugins/plugin_base.hpp             |    7 +-
 .../engine/routing_algorithms/alternative_path.hpp |   40 +-
 include/engine/routing_algorithms/map_matching.hpp |    4 +-
 .../engine/routing_algorithms/shortest_path.hpp    |   10 +-
 include/extractor/guidance/constants.hpp           |   32 +
 include/extractor/guidance/intersection.hpp        |   66 +
 .../extractor/guidance/intersection_generator.hpp  |   68 +
 .../extractor/guidance/intersection_handler.hpp    |   71 +
 include/extractor/guidance/motorway_handler.hpp    |   51 +
 include/extractor/guidance/roundabout_handler.hpp  |   71 +
 include/extractor/guidance/toolkit.hpp             |   74 +-
 include/extractor/guidance/turn_analysis.hpp       |  175 +-
 include/extractor/guidance/turn_handler.hpp        |   74 +
 include/extractor/guidance/turn_instruction.hpp    |   20 +-
 include/extractor/profile_properties.hpp           |    4 +-
 include/extractor/restriction_parser.hpp           |    2 +-
 include/server/api/base_parameters_grammar.hpp     |  110 +-
 include/server/api/match_parameter_grammar.hpp     |   38 +-
 include/server/api/nearest_parameter_grammar.hpp   |   15 +-
 include/server/api/parameters_parser.hpp           |    2 +-
 include/server/api/parsed_url.hpp                  |   18 +-
 include/server/api/route_parameters_grammar.hpp    |   48 +-
 include/server/api/table_parameter_grammar.hpp     |   28 +-
 include/server/api/tile_parameter_grammar.hpp      |   35 +-
 include/server/api/trip_parameter_grammar.hpp      |   33 +-
 include/server/api/url_parser.hpp                  |    4 +-
 include/storage/storage.hpp                        |   27 +
 include/storage/storage_config.hpp                 |   27 +
 include/util/coordinate_calculation.hpp            |   48 +-
 include/util/name_table.hpp                        |    2 +-
 include/util/range_table.hpp                       |    7 +-
 include/util/rectangle.hpp                         |    6 +-
 include/util/static_rtree.hpp                      |   64 +-
 include/util/viewport.hpp                          |   22 +-
 include/util/web_mercator.hpp                      |  127 ++
 profiles/bicycle.lua                               |   16 +-
 profiles/car.lua                                   |   13 +-
 profiles/foot.lua                                  |   16 +-
 profiles/lib/access.lua                            |    4 +-
 profiles/testbot.lua                               |    8 +-
 scripts/poly2req.js                                |  132 ++
 scripts/timer.sh                                   |   12 +
 src/benchmarks/CMakeLists.txt                      |   20 +
 src/contractor/contractor.cpp                      |    2 +-
 src/engine/api/json_factory.cpp                    |   38 +-
 src/engine/douglas_peucker.cpp                     |   40 +-
 src/engine/guidance/post_processing.cpp            |   63 +-
 src/engine/plugins/match.cpp                       |    6 +-
 src/engine/plugins/tile.cpp                        |   24 +-
 src/engine/plugins/trip.cpp                        |    2 +-
 src/engine/plugins/viaroute.cpp                    |   16 +-
 src/extractor/edge_based_graph_factory.cpp         |   13 +-
 src/extractor/extractor.cpp                        |    2 +-
 src/extractor/extractor_callbacks.cpp              |    1 +
 src/extractor/guidance/intersection.cpp            |   31 +
 src/extractor/guidance/intersection_generator.cpp  |  255 +++
 src/extractor/guidance/intersection_handler.cpp    |  307 +++
 src/extractor/guidance/motorway_handler.cpp        |  524 +++++
 src/extractor/guidance/roundabout_handler.cpp      |  257 +++
 src/extractor/guidance/turn_analysis.cpp           | 2281 +-------------------
 src/extractor/guidance/turn_handler.cpp            | 1171 ++++++++++
 src/extractor/restriction_parser.cpp               |    2 +-
 src/extractor/scripting_environment.cpp            |    2 +-
 src/server/api/parameters_parser.cpp               |   45 +-
 src/server/api/url_parser.cpp                      |  120 +-
 src/server/connection.cpp                          |    1 +
 src/server/request_handler.cpp                     |    6 +-
 src/storage/storage.cpp                            |    1 +
 src/tools/contract.cpp                             |    3 +-
 src/tools/extract.cpp                              |    3 +-
 src/tools/routed.cpp                               |    3 +-
 src/tools/store.cpp                                |    3 +-
 src/util/coordinate_calculation.cpp                |  239 +-
 src/util/name_table.cpp                            |   25 +-
 taginfo.json                                       |    1 +
 test/data/Makefile                                 |   47 +-
 test/data/data.md5sum                              |    1 +
 unit_tests/CMakeLists.txt                          |   76 +
 unit_tests/engine/base64.cpp                       |    2 -
 unit_tests/engine/douglas_peucker.cpp              |   80 +-
 unit_tests/library/equal_json.hpp                  |    7 +-
 unit_tests/library/match.cpp                       |   34 +-
 unit_tests/library/nearest.cpp                     |    4 +-
 unit_tests/library/route.cpp                       |   96 +-
 unit_tests/mocks/mock_datafacade.hpp               |    2 +-
 unit_tests/server/parameters_parser.cpp            |   74 +-
 unit_tests/server/url_parser.cpp                   |   14 +-
 unit_tests/util/coordinate_calculation.cpp         |  246 ++-
 unit_tests/util/rectangle.cpp                      |   64 +-
 unit_tests/util/static_rtree.cpp                   |   40 +-
 unit_tests/util/web_mercator.cpp                   |   74 +
 157 files changed, 6933 insertions(+), 4337 deletions(-)

diff --git a/.gitignore b/.gitignore
index 83f572d..dd46b9f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -80,6 +80,7 @@ stxxl.errlog
 /test/profile.lua
 /test/cache
 /test/speeds.csv
+/test/data/monaco.*
 node_modules
 
 # Deprecated config file #
diff --git a/.travis.yml b/.travis.yml
index 553cf56..df8df0a 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -26,8 +26,8 @@ matrix:
       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']
-      env: COMPILER='g++-5' BUILD_TYPE='Debug'
+          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
 
     - os: linux
       compiler: gcc
@@ -35,20 +35,20 @@ matrix:
         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: COMPILER='g++-4.8' BUILD_TYPE='Debug'
+      env: CCOMPILER='gcc-4.8' CXXCOMPILER='g++-4.8' BUILD_TYPE='Debug'
 
     - os: linux
       compiler: clang
       addons: &clang38
         apt:
-          sources: ['llvm-toolchain-precise', 'ubuntu-toolchain-r-test']
+          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: COMPILER='clang++-3.8' BUILD_TYPE='Debug' RUN_CLANG_FORMAT=ON
+      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: COMPILER='clang++' BUILD_TYPE='Debug'
+      env: CCOMPILER='clang' CXXCOMPILER='clang++' BUILD_TYPE='Debug'
 
     # Release Builds
     - os: linux
@@ -57,7 +57,7 @@ matrix:
         apt:
           sources: ['ubuntu-toolchain-r-test']
           packages: ['g++-5', 'libbz2-dev', 'libstxxl-dev', 'libstxxl1', 'libxml2-dev', 'libzip-dev', 'lua5.1', 'liblua5.1-0-dev', 'libtbb-dev', 'libgdal-dev', 'libluabind-dev', 'libboost-all-dev']
-      env: COMPILER='g++-5' BUILD_TYPE='Release'
+      env: CCOMPILER='gcc-5' CXXCOMPILER='g++-5' BUILD_TYPE='Release'
 
     - os: linux
       compiler: gcc
@@ -65,20 +65,20 @@ matrix:
         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: COMPILER='g++-4.8' BUILD_TYPE='Release'
+      env: CCOMPILER='gcc-4.8' CXXCOMPILER='g++-4.8' BUILD_TYPE='Release'
 
     - os: linux
       compiler: clang
       addons: &clang38
         apt:
-          sources: ['llvm-toolchain-precise', 'ubuntu-toolchain-r-test']
+          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: COMPILER='clang++-3.8' BUILD_TYPE='Release'
+      env: CCOMPILER='clang-3.8' CXXCOMPILER='clang++-3.8' BUILD_TYPE='Release'
 
     - os: osx
       osx_image: xcode7.3
       compiler: clang
-      env: COMPILER='clang++' BUILD_TYPE='Release'
+      env: CCOMPILER='clang' CXXCOMPILER='clang++' BUILD_TYPE='Release'
 
     # Shared Library
     - os: linux
@@ -87,39 +87,15 @@ matrix:
         apt:
           sources: ['ubuntu-toolchain-r-test']
           packages: ['g++-5', 'libbz2-dev', 'libstxxl-dev', 'libstxxl1', 'libxml2-dev', 'libzip-dev', 'lua5.1', 'liblua5.1-0-dev', 'libtbb-dev', 'libgdal-dev', 'libluabind-dev', 'libboost-all-dev']
-      env: COMPILER='g++-5' BUILD_TYPE='Release' BUILD_SHARED_LIBS=ON
+      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', 'ubuntu-toolchain-r-test']
+          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: COMPILER='clang++-3.8' BUILD_TYPE='Release' BUILD_SHARED_LIBS=ON
-
-
-    # Disabled until tests all pass on OSX:
-    #
-    # 3/ OSX Clang Builds
-    #- os: osx
-    #  osx_image: xcode6.4
-    #  compiler: clang
-    #  env: COMPILER='clang++' BUILD_TYPE='Debug'
-
-    #- os: osx
-    #  osx_image: xcode6.4
-    #  compiler: clang
-    #  env: COMPILER='clang++' BUILD_TYPE='Release'
-
-    #- os: osx
-    #  osx_image: xcode6.4
-    #  compiler: clang
-    #  env: COMPILER='clang++' BUILD_TYPE='Release' BUILD_SHARED_LIBS=ON
-
-    #- os: osx
-    #  osx_image: xcode7
-    #  compiler: clang
-    #  env: COMPILER='clang++' BUILD_TYPE='Release' BUILD_SHARED_LIBS=ON
+      env: CCOMPILER='clang-3.8' CXXCOMPILER='clang++-3.8' BUILD_TYPE='Release' BUILD_SHARED_LIBS=ON
 
 before_install:
   - source ./scripts/install_node.sh 4
@@ -129,11 +105,14 @@ 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.3/cmake-3.3.2-Linux-x86_64.tar.gz"
+      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
@@ -146,9 +125,9 @@ before_script:
       ./scripts/check_taginfo.py taginfo.json profiles/car.lua
     fi
   - mkdir build && pushd build
-  - export CXX=${COMPILER}
+  - 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} -DBUILD_TOOLS=1 -DENABLE_CCACHE=0
+  - 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
@@ -159,19 +138,29 @@ script:
     if [[ "${TRAVIS_OS_NAME}" == "linux" ]]; then
       sudo ldconfig
     fi
-  - ./extractor-tests
-  - ./engine-tests
-  - ./util-tests
+  - echo "travis_fold:start:UNIT_TESTS"
+  - ./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
-  - make -C test/data
-  - ./build/library-tests test/data/monaco.osrm
+  - 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
       ./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'
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 168b5ad..4430ff0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,28 @@
+# 5.0.0 RC2
+   - Profiles:
+      - `properties.allow_uturns_at_via` -> `properties.continue_straight_at_waypoint` (value is inverted!)
+   - API:
+      - Removed summary from legs property
+      - Disable steps and alternatives by default
+      - Fix `code` field: 'ok' -> 'Ok'
+      - Allow 4.json and 4.3.json format
+      - Conform to v5 spec and support "unlimited" as radiuses value.
+      - `uturns` parameter was replaced by `continue_straight` (value is inverted!)
+   - Features:
+      - Report progress for gennerating edge expanded edges in the edge based graph factory
+      - Add maxspeed=none tag to car profile.
+      - Optimize StaticRTree code: speedup 2x (to RC1)
+      - Optimize DouglasPeucker code: speedup 10x (to RC1)
+      - Optimize WebMercator projection: speedup 2x (to RC1)
+   - Bugs:
+      - #2195: Resolves issues with multiple includedirs in pkg-config file
+      - #2219: Internal server error when using the match plugin
+      - #2027: basename -> filename
+      - #2168: Report correct position where parsing failed
+      - #2036: Add license to storage and storage config exposed in public API
+      - Fix uturn detection in match plugin
+      - Add missing -lz to fix linking of server-tests
+
 # 5.0.0 RC1
    - Renamed osrm-prepare into osrm-contract
    - osrm-contract does not need a profile parameter anymore
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 3ca9c8a..dd3d188 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -32,8 +32,10 @@ endif()
 option(ENABLE_CCACHE "Speed up incremental rebuilds via ccache" ON)
 option(ENABLE_JSON_LOGGING "Adds additional JSON debug logging to the response" OFF)
 option(BUILD_TOOLS "Build OSRM tools" OFF)
-option(BUILD_COMPONENTS "Build OSRM tools" ON)
+option(BUILD_COMPONENTS "Build osrm-components" ON)
 option(ENABLE_ASSERTIONS OFF)
+option(COVERAGE OFF)
+option(SANITIZER OFF)
 
 include_directories(BEFORE ${CMAKE_CURRENT_BINARY_DIR}/include/)
 include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR}/include/)
@@ -46,10 +48,7 @@ add_custom_target(FingerPrintConfigure ALL ${CMAKE_COMMAND}
   COMMENT "Configuring revision fingerprint"
   VERBATIM)
 
-add_custom_target(tests DEPENDS engine-tests extractor-tests util-tests server-tests library-tests)
-add_custom_target(benchmarks DEPENDS rtree-bench)
-
-set(BOOST_COMPONENTS date_time filesystem iostreams program_options regex system thread unit_test_framework)
+set(BOOST_COMPONENTS date_time filesystem iostreams program_options regex system thread)
 
 configure_file(
   ${CMAKE_CURRENT_SOURCE_DIR}/include/util/version.hpp.in
@@ -61,12 +60,6 @@ file(GLOB ContractorGlob src/contractor/*.cpp)
 file(GLOB StorageGlob src/storage/*.cpp)
 file(GLOB ServerGlob src/server/*.cpp src/server/**/*.cpp)
 file(GLOB EngineGlob src/engine/*.cpp src/engine/**/*.cpp)
-file(GLOB ExtractorTestsGlob unit_tests/extractor/*.cpp)
-file(GLOB EngineTestsGlob unit_tests/engine/*.cpp)
-file(GLOB UtilTestsGlob unit_tests/util/*.cpp)
-file(GLOB ServerTestsGlob unit_tests/server/*.cpp)
-file(GLOB LibraryTestsGlob unit_tests/library/*.cpp)
-file(GLOB IOTestsGlob unit_tests/io/*.cpp)
 
 add_library(UTIL OBJECT ${UtilGlob})
 add_library(EXTRACTOR OBJECT ${ExtractorGlob})
@@ -87,21 +80,6 @@ add_library(osrm_extract $<TARGET_OBJECTS:EXTRACTOR> $<TARGET_OBJECTS:UTIL>)
 add_library(osrm_contract $<TARGET_OBJECTS:CONTRACTOR> $<TARGET_OBJECTS:UTIL>)
 add_library(osrm_store $<TARGET_OBJECTS:STORAGE> $<TARGET_OBJECTS:UTIL>)
 
-# Unit tests
-add_executable(engine-tests EXCLUDE_FROM_ALL unit_tests/engine_tests.cpp ${EngineTestsGlob} $<TARGET_OBJECTS:ENGINE> $<TARGET_OBJECTS:STORAGE> $<TARGET_OBJECTS:UTIL>)
-add_executable(extractor-tests EXCLUDE_FROM_ALL unit_tests/extractor_tests.cpp ${ExtractorTestsGlob} $<TARGET_OBJECTS:EXTRACTOR> $<TARGET_OBJECTS:UTIL>)
-add_executable(util-tests EXCLUDE_FROM_ALL unit_tests/util_tests.cpp ${UtilTestsGlob} $<TARGET_OBJECTS:UTIL>)
-add_executable(server-tests EXCLUDE_FROM_ALL unit_tests/server_tests.cpp ${ServerTestsGlob} $<TARGET_OBJECTS:UTIL> $<TARGET_OBJECTS:SERVER>)
-add_executable(library-tests EXCLUDE_FROM_ALL unit_tests/library_tests.cpp ${LibraryTestsGlob})
-
-# Benchmarks
-add_executable(rtree-bench EXCLUDE_FROM_ALL src/benchmarks/static_rtree.cpp $<TARGET_OBJECTS:UTIL>)
-
-target_include_directories(engine-tests PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/unit_tests)
-target_include_directories(util-tests PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/unit_tests)
-target_include_directories(library-tests PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include ${CMAKE_CURRENT_SOURCE_DIR}/)
-target_include_directories(rtree-bench PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/unit_tests)
-
 # Check the release mode
 if(NOT CMAKE_BUILD_TYPE MATCHES Debug)
   set(CMAKE_BUILD_TYPE Release)
@@ -157,8 +135,19 @@ if(CMAKE_BUILD_TYPE MATCHES Release)
   endif()
 endif()
 
-if(NOT WIN32)
-  add_definitions(-DBOOST_TEST_DYN_LINK)
+set(MAYBE_COVERAGE_LIBRARIES "")
+if (COVERAGE)
+  if (NOT CMAKE_BUILD_TYPE MATCHES "Debug")
+    message(ERROR "COVERAGE=ON only make sense with a Debug build")
+  endif()
+  set(MAYBE_COVERAGE_LIBRARIES "gcov")
+  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ftest-coverage -fprofile-arcs")
+endif()
+if (SANITIZER)
+  if (NOT CMAKE_BUILD_TYPE MATCHES "Debug")
+    message(ERROR "SANITIZER=ON only make sense with a Debug build")
+  endif()
+  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address")
 endif()
 
 # Configuring compilers
@@ -242,11 +231,13 @@ find_package(Osmium REQUIRED COMPONENTS io)
 include_directories(SYSTEM ${OSMIUM_INCLUDE_DIRS})
 
 
-find_package(Boost 1.49.0 COMPONENTS ${BOOST_COMPONENTS} REQUIRED)
+find_package(Boost 1.49.0 REQUIRED COMPONENTS ${BOOST_COMPONENTS})
 if(NOT WIN32)
   add_definitions(-DBOOST_TEST_DYN_LINK)
 endif()
-add_definitions(-DBOOST_SPIRIT_USE_PHOENIX_V3 -DBOOST_RESULT_OF_USE_DECLTYPE)
+add_definitions(-DBOOST_SPIRIT_USE_PHOENIX_V3)
+add_definitions(-DBOOST_RESULT_OF_USE_DECLTYPE)
+add_definitions(-DBOOST_FILESYSTEM_NO_DEPRECATED)
 include_directories(SYSTEM ${Boost_INCLUDE_DIRS})
 
 find_package(Threads REQUIRED)
@@ -257,7 +248,7 @@ if(WIN32 AND CMAKE_BUILD_TYPE MATCHES Debug)
   set(TBB_LIBRARIES ${TBB_DEBUG_LIBRARIES})
 endif()
 
-find_package( Luabind REQUIRED )
+find_package(Luabind REQUIRED)
 include(check_luabind)
 include_directories(SYSTEM ${LUABIND_INCLUDE_DIR})
 
@@ -307,7 +298,8 @@ set(EXTRACTOR_LIBRARIES
     ${OSMIUM_LIBRARIES}
     ${STXXL_LIBRARY}
     ${TBB_LIBRARIES}
-    ${ZLIB_LIBRARY})
+    ${ZLIB_LIBRARY}
+    ${MAYBE_COVERAGE_LIBRARIES})
 set(CONTRACTOR_LIBRARIES
     ${Boost_LIBRARIES}
     ${CMAKE_THREAD_LIBS_INIT}
@@ -315,35 +307,32 @@ set(CONTRACTOR_LIBRARIES
     ${USED_LUA_LIBRARIES}
     ${STXXL_LIBRARY}
     ${TBB_LIBRARIES}
-    ${MAYBE_RT_LIBRARY})
+    ${MAYBE_RT_LIBRARY}
+    ${MAYBE_COVERAGE_LIBRARIES})
 set(ENGINE_LIBRARIES
     ${Boost_LIBRARIES}
     ${CMAKE_THREAD_LIBS_INIT}
     ${STXXL_LIBRARY}
     ${TBB_LIBRARIES}
-    ${MAYBE_RT_LIBRARY})
+    ${MAYBE_RT_LIBRARY}
+    ${MAYBE_COVERAGE_LIBRARIES})
 set(STORAGE_LIBRARIES
     ${Boost_LIBRARIES}
     ${CMAKE_THREAD_LIBS_INIT}
     ${TBB_LIBRARIES}
-    ${MAYBE_RT_LIBRARY})
+    ${MAYBE_RT_LIBRARY}
+    ${MAYBE_COVERAGE_LIBRARIES})
 set(UTIL_LIBRARIES
     ${Boost_LIBRARIES}
     ${CMAKE_THREAD_LIBS_INIT}
     ${STXXL_LIBRARY}
-    ${TBB_LIBRARIES})
+    ${TBB_LIBRARIES}
+    ${MAYBE_COVERAGE_LIBRARIES})
 # Libraries
 target_link_libraries(osrm ${ENGINE_LIBRARIES})
 target_link_libraries(osrm_contract ${CONTRACTOR_LIBRARIES})
 target_link_libraries(osrm_extract ${EXTRACTOR_LIBRARIES})
 target_link_libraries(osrm_store ${STORAGE_LIBRARIES})
-# Tests
-target_link_libraries(engine-tests ${ENGINE_LIBRARIES})
-target_link_libraries(server-tests osrm ${Boost_LIBRARIES})
-target_link_libraries(extractor-tests ${EXTRACTOR_LIBRARIES})
-target_link_libraries(rtree-bench ${Boost_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} ${TBB_LIBRARIES})
-target_link_libraries(util-tests ${UTIL_LIBRARIES})
-target_link_libraries(library-tests osrm ${Boost_LIBRARIES})
 
 if(BUILD_COMPONENTS)
   find_package(GDAL)
@@ -460,3 +449,8 @@ configure_file(
 
 add_custom_target(uninstall
     COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake/cmake_uninstall.cmake)
+
+
+# Modular build system: each directory registered here provides its own CMakeLists.txt
+add_subdirectory(unit_tests)
+add_subdirectory(src/benchmarks)
diff --git a/README.md b/README.md
index d319d26..084cfba 100644
--- a/README.md
+++ b/README.md
@@ -10,6 +10,7 @@ The Open Source Routing Machine is a high performance routing engine written in
 | 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) |
 
 ## Building
 
diff --git a/appveyor-build.bat b/appveyor-build.bat
index 568998b..5dbdfa2 100644
--- a/appveyor-build.bat
+++ b/appveyor-build.bat
@@ -117,13 +117,13 @@ IF %ERRORLEVEL% NEQ 0 GOTO ERROR
 SET PATH=%PROJECT_DIR%\osrm-deps\libs\bin;%PATH%
 
 ECHO running engine-tests.exe ...
-%Configuration%\engine-tests.exe
+%Configuration%\unit_tests\engine-tests.exe
 IF %ERRORLEVEL% NEQ 0 GOTO ERROR
 ECHO running extractor-tests.exe ...
-%Configuration%\extractor-tests.exe
+%Configuration%\unit_tests\extractor-tests.exe
 IF %ERRORLEVEL% NEQ 0 GOTO ERROR
 ECHO running util-tests.exe ...
-%Configuration%\util-tests.exe
+%Configuration%\unit_tests\util-tests.exe
 IF %ERRORLEVEL% NEQ 0 GOTO ERROR
 
 IF NOT "%APPVEYOR_REPO_BRANCH%"=="develop" GOTO DONE
diff --git a/cmake/pkgconfig.in b/cmake/pkgconfig.in
index 92fe403..c85a076 100644
--- a/cmake/pkgconfig.in
+++ b/cmake/pkgconfig.in
@@ -1,5 +1,5 @@
 prefix=@CMAKE_INSTALL_PREFIX@
-includedir=${prefix}/include ${prefix}/include/osrm
+includedir=${prefix}/include
 libdir=${prefix}/lib
 
 Name: libOSRM
@@ -8,4 +8,4 @@ Version: v at OSRM_VERSION_MAJOR@. at OSRM_VERSION_MINOR@. at OSRM_VERSION_PATCH@
 Requires:
 Libs: -L${libdir} -losrm
 Libs.private: @ENGINE_LIBRARY_LISTING@
-Cflags: -I${includedir}
+Cflags: -I${includedir} -I${includedir}/osrm
diff --git a/cucumber.js b/cucumber.js
index c16104c..5a87dd7 100644
--- a/cucumber.js
+++ b/cucumber.js
@@ -1,6 +1,6 @@
 module.exports = {
-    default: '--require features --tags ~@todo --tags ~@bug --tags ~@stress --tags ~@guidance',
-    verify: '--require features --tags ~@todo --tags ~@bug --tags ~@stress -f progress --tags ~@guidance',
+    default: '--require features --tags ~@stress --tags ~@todo',
+    verify: '--require features --tags ~@todo --tags ~@bug --tags ~@stress -f progress',
     jenkins: '--require features --tags ~@todo --tags ~@bug --tags ~@stress --tags ~@options -f progress',
     bugs: '--require features --tags @bug',
     todo: '--require features --tags @todo',
diff --git a/features/bicycle/access.feature b/features/bicycle/access.feature
index 3a2fffb..67c8180 100644
--- a/features/bicycle/access.feature
+++ b/features/bicycle/access.feature
@@ -5,7 +5,7 @@ Feature: Bike - Access tags on ways
     Background:
         Given the profile "bicycle"
 
-    Scenario: Bike - Access tag hierachy on ways
+    Scenario: Bike - Access tag hierarchy on ways
         Then routability should be
             | highway | access | vehicle | bicycle | bothw |
             |         |        |         |         | x     |
@@ -121,6 +121,7 @@ Feature: Bike - Access tags on ways
             | private      |              |              |       |
             | agricultural |              |              |       |
             | forestry     |              |              |       |
+            | delivery     |              |              |       |
             |              | yes          |              | x     |
             |              | permissive   |              | x     |
             |              | designated   |              | x     |
@@ -129,6 +130,7 @@ Feature: Bike - Access tags on ways
             |              | private      |              |       |
             |              | agricultural |              |       |
             |              | forestry     |              |       |
+            |              | delivery     |              |       |
             |              |              | yes          | x     |
             |              |              | permissive   | x     |
             |              |              | designated   | x     |
@@ -137,6 +139,7 @@ Feature: Bike - Access tags on ways
             |              |              | private      |       |
             |              |              | agricultural |       |
             |              |              | forestry     |       |
+            |              |              | delivery     |       |
 
     Scenario: Bike - Access tags on both node and way
         Then routability should be
diff --git a/features/bicycle/access_node.feature b/features/bicycle/access_node.feature
index 05e94fe..3c1a75a 100644
--- a/features/bicycle/access_node.feature
+++ b/features/bicycle/access_node.feature
@@ -5,7 +5,7 @@ Feature: Bike - Access tags on nodes
     Background:
         Given the profile "bicycle"
 
-    Scenario: Bike - Access tag hierachy on nodes
+    Scenario: Bike - Access tag hierarchy on nodes
         Then routability should be
             | node/access | node/vehicle | node/bicycle | node/highway  | bothw |
             |             |              |              |               | x     |
@@ -47,6 +47,7 @@ Feature: Bike - Access tags on nodes
             | private      |              |              |       |
             | agricultural |              |              |       |
             | forestry     |              |              |       |
+            | delivery     |              |              |       |
             |              | yes          |              | x     |
             |              | permissive   |              | x     |
             |              | designated   |              | x     |
@@ -55,6 +56,7 @@ Feature: Bike - Access tags on nodes
             |              | private      |              |       |
             |              | agricultural |              |       |
             |              | forestry     |              |       |
+            |              | delivery     |              |       |
             |              |              | yes          | x     |
             |              |              | permissive   | x     |
             |              |              | designated   | x     |
@@ -63,3 +65,4 @@ Feature: Bike - Access tags on nodes
             |              |              | private      |       |
             |              |              | agricultural |       |
             |              |              | forestry     |       |
+            |              |              | delivery     |       |
diff --git a/features/bicycle/destination.feature b/features/bicycle/destination.feature
index 9c71198..c3a0173 100644
--- a/features/bicycle/destination.feature
+++ b/features/bicycle/destination.feature
@@ -19,15 +19,15 @@ Feature: Bike - Destination only, no passing through
             | axye  |             |
 
         When I route I should get
-            | from | to | route  |
-            | a    | b  | ab     |
-            | a    | c  | ab,bcd |
-            | a    | d  | ab,bcd |
-            | a    | e  | axye   |
-            | e    | d  | de     |
-            | e    | c  | de,bcd |
-            | e    | b  | de,bcd |
-            | e    | a  | axye   |
+            | from | to | route      |
+            | a    | b  | ab,ab      |
+            | a    | c  | ab,bcd,bcd |
+            | a    | d  | ab,bcd,bcd |
+            | a    | e  | axye,axye  |
+            | e    | d  | de,de      |
+            | e    | c  | de,bcd,bcd |
+            | e    | b  | de,bcd,bcd |
+            | e    | a  | axye,axye  |
 
     Scenario: Bike - Destination only street
         Given the node map
@@ -45,15 +45,15 @@ Feature: Bike - Destination only, no passing through
             | axye  |             |
 
         When I route I should get
-            | from | to | route    |
-            | a    | b  | ab       |
-            | a    | c  | ab,bc    |
-            | a    | d  | ab,bc,cd |
-            | a    | e  | axye     |
-            | e    | d  | de       |
-            | e    | c  | de,dc    |
-            | e    | b  | de,dc,bc |
-            | e    | a  | axye     |
+            | from | to | route       |
+            | a    | b  | ab,ab       |
+            | a    | c  | ab,bc,bc    |
+            | a    | d  | ab,bc,cd,cd |
+            | a    | e  | axye,axye   |
+            | e    | d  | de,de       |
+            | e    | c  | de,cd,cd    |
+            | e    | b  | de,cd,bc,bc |
+            | e    | a  | axye,axye   |
 
     Scenario: Bike - Routing inside a destination only area
         Given the node map
@@ -70,8 +70,8 @@ Feature: Bike - Destination only, no passing through
             | axye  |             |
 
         When I route I should get
-            | from | to | route       |
-            | a    | e  | ab,bc,cd,de |
-            | e    | a  | de,cd,bc,ab |
-            | b    | d  | bc,cd       |
-            | d    | b  | cd,bc       |
+            | from | to | route          |
+            | a    | e  | ab,bc,cd,de,de |
+            | e    | a  | de,cd,bc,ab,ab |
+            | b    | d  | bc,cd,cd       |
+            | d    | b  | cd,bc,bc       |
diff --git a/features/bicycle/mode.feature b/features/bicycle/mode.feature
index 3587f6c..852402a 100644
--- a/features/bicycle/mode.feature
+++ b/features/bicycle/mode.feature
@@ -16,13 +16,13 @@ Feature: Bike - Mode flag
     	 | cd    | primary |       |          |
 
     	When I route I should get
-    	 | from | to | route       | turns                    | modes                         |
-    	 | a    | d  | ab,bc,cd,cd | depart,right,left,arrive | cycling,ferry,cycling,cycling |
-    	 | d    | a  | cd,bc,ab,ab | depart,right,left,arrive | cycling,ferry,cycling,cycling |
-    	 | c    | a  | bc,ab,ab    | depart,left,arrive       | ferry,cycling,cycling         |
-    	 | d    | b  | cd,bc,bc    | depart,right,arrive      | cycling,ferry,ferry           |
-    	 | a    | c  | ab,bc,bc    | depart,right,arrive      | cycling,ferry,ferry           |
-    	 | b    | d  | bc,cd,cd    | depart,left,arrive       | ferry,cycling,cycling         |
+    	 | from | to | route       | modes                         |
+    	 | a    | d  | ab,bc,cd,cd | cycling,ferry,cycling,cycling |
+    	 | d    | a  | cd,bc,ab,ab | cycling,ferry,cycling,cycling |
+    	 | c    | a  | bc,ab,ab    | ferry,cycling,cycling         |
+    	 | d    | b  | cd,bc,bc    | cycling,ferry,ferry           |
+    	 | a    | c  | ab,bc,bc    | cycling,ferry,ferry           |
+    	 | b    | d  | bc,cd,cd    | ferry,cycling,cycling         |
 
      Scenario: Bike - Mode when using a train
      	Given the node map
@@ -36,13 +36,13 @@ Feature: Bike - Mode flag
      	 | cd    | primary |         |         |
 
      	When I route I should get
-     	 | from | to | route       | turns                    | modes                         |
-     	 | a    | d  | ab,bc,cd,cd | depart,right,left,arrive | cycling,train,cycling,cycling |
-     	 | d    | a  | cd,bc,ab,ab | depart,right,left,arrive | cycling,train,cycling,cycling |
-     	 | c    | a  | bc,ab,ab    | depart,left,arrive       | train,cycling,cycling         |
-     	 | d    | b  | cd,bc,bc    | depart,right,arrive      | cycling,train,train           |
-     	 | a    | c  | ab,bc,bc    | depart,right,arrive      | cycling,train,train           |
-     	 | b    | d  | bc,cd,cd    | depart,left,arrive       | train,cycling,cycling         |
+     	 | from | to | route       | modes                         |
+     	 | a    | d  | ab,bc,cd,cd | cycling,train,cycling,cycling |
+     	 | d    | a  | cd,bc,ab,ab | cycling,train,cycling,cycling |
+     	 | c    | a  | bc,ab,ab    | train,cycling,cycling         |
+     	 | d    | b  | cd,bc,bc    | cycling,train,train           |
+     	 | a    | c  | ab,bc,bc    | cycling,train,train           |
+     	 | b    | d  | bc,cd,cd    | train,cycling,cycling         |
 
      Scenario: Bike - Mode when pushing bike against oneways
      	Given the node map
@@ -56,13 +56,13 @@ Feature: Bike - Mode flag
      	 | cd    | primary |        |
 
      	When I route I should get
-     	 | from | to | route       | turns                                   | modes                                |
-     	 | a    | d  | ab,bc,cd,cd | depart,right,left,arrive                | cycling,cycling,cycling,cycling      |
-     	 | d    | a  | cd,bc,ab,ab | depart,right,left,arrive                | cycling,pushing bike,cycling,cycling |
-     	 | c    | a  | bc,ab,ab    | depart,left,arrive                      | pushing bike,cycling,cycling         |
-     	 | d    | b  | cd,bc,bc    | depart,right,arrive                     | cycling,pushing bike,pushing bike    |
-     	 | a    | c  | ab,bc,bc    | depart,right,arrive                     | cycling,cycling,cycling              |
-     	 | b    | d  | bc,cd,cd    | depart,left,arrive                      | cycling,cycling,cycling              |
+     	 | from | to | route       | modes                                |
+     	 | a    | d  | ab,bc,cd,cd | cycling,cycling,cycling,cycling      |
+     	 | d    | a  | cd,bc,ab,ab | cycling,pushing bike,cycling,cycling |
+     	 | c    | a  | bc,ab,ab    | pushing bike,cycling,cycling         |
+     	 | d    | b  | cd,bc,bc    | cycling,pushing bike,pushing bike    |
+     	 | a    | c  | ab,bc,bc    | cycling,cycling,cycling              |
+     	 | b    | d  | bc,cd,cd    | cycling,cycling,cycling              |
 
      Scenario: Bike - Mode when pushing on pedestrain streets
      	Given the node map
@@ -76,13 +76,13 @@ Feature: Bike - Mode flag
      	 | cd    | primary    |
 
      	When I route I should get
-     	 | from | to | route       | turns                    | modes                                |
-     	 | a    | d  | ab,bc,cd,cd | depart,right,left,arrive | cycling,pushing bike,cycling,cycling |
-     	 | d    | a  | cd,bc,ab,ab | depart,right,left,arrive | cycling,pushing bike,cycling,cycling |
-     	 | c    | a  | bc,ab,ab    | depart,left,arrive       | pushing bike,cycling,cycling         |
-     	 | d    | b  | cd,bc,bc    | depart,right,arrive      | cycling,pushing bike,pushing bike    |
-     	 | a    | c  | ab,bc,bc    | depart,right,arrive      | cycling,pushing bike,pushing bike    |
-     	 | b    | d  | bc,cd,cd    | depart,left,arrive       | pushing bike,cycling,cycling         |
+     	 | from | to | route       | modes                                |
+     	 | a    | d  | ab,bc,cd,cd | cycling,pushing bike,cycling,cycling |
+     	 | d    | a  | cd,bc,ab,ab | cycling,pushing bike,cycling,cycling |
+     	 | c    | a  | bc,ab,ab    | pushing bike,cycling,cycling         |
+     	 | d    | b  | cd,bc,bc    | cycling,pushing bike,pushing bike    |
+     	 | a    | c  | ab,bc,bc    | cycling,pushing bike,pushing bike    |
+     	 | b    | d  | bc,cd,cd    | pushing bike,cycling,cycling         |
 
      Scenario: Bike - Mode when pushing on pedestrain areas
      	Given the node map
@@ -116,13 +116,13 @@ Feature: Bike - Mode flag
     	 | cd    | primary |
 
      	When I route I should get
-    	 | from | to | route       | turns                    | modes                                |
-    	 | a    | d  | ab,bc,cd,cd | depart,right,left,arrive | cycling,pushing bike,cycling,cycling |
-    	 | d    | a  | cd,bc,ab,ab | depart,right,left,arrive | cycling,pushing bike,cycling,cycling |
-    	 | c    | a  | bc,ab,ab    | depart,left,arrive       | pushing bike,cycling,cycling         |
-    	 | d    | b  | cd,bc,bc    | depart,right,arrive      | cycling,pushing bike,pushing bike    |
-    	 | a    | c  | ab,bc,bc    | depart,right,arrive      | cycling,pushing bike,pushing bike    |
-    	 | b    | d  | bc,cd,cd    | depart,left,arrive       | pushing bike,cycling,cycling         |
+    	 | from | to | route       | modes                                |
+    	 | a    | d  | ab,bc,cd,cd | cycling,pushing bike,cycling,cycling |
+    	 | d    | a  | cd,bc,ab,ab | cycling,pushing bike,cycling,cycling |
+    	 | c    | a  | bc,ab,ab    | pushing bike,cycling,cycling         |
+    	 | d    | b  | cd,bc,bc    | cycling,pushing bike,pushing bike    |
+    	 | a    | c  | ab,bc,bc    | cycling,pushing bike,pushing bike    |
+    	 | b    | d  | bc,cd,cd    | pushing bike,cycling,cycling         |
 
      Scenario: Bike - Mode when bicycle=dismount
      	Given the node map
@@ -136,13 +136,13 @@ Feature: Bike - Mode flag
     	 | cd    | primary |          |
 
      	When I route I should get
-    	 | from | to | route       | turns                    | modes                                |
-    	 | a    | d  | ab,bc,cd,cd | depart,right,left,arrive | cycling,pushing bike,cycling,cycling |
-    	 | d    | a  | cd,bc,ab,ab | depart,right,left,arrive | cycling,pushing bike,cycling,cycling |
-    	 | c    | a  | bc,ab,ab    | depart,left,arrive       | pushing bike,cycling,cycling         |
-    	 | d    | b  | cd,bc,bc    | depart,right,arrive      | cycling,pushing bike,pushing bike    |
-    	 | a    | c  | ab,bc,bc    | depart,right,arrive      | cycling,pushing bike,pushing bike    |
-         | b    | d  | bc,cd,cd    | depart,left,arrive       | pushing bike,cycling,cycling         |
+    	 | from | to | route       | modes                                |
+    	 | a    | d  | ab,bc,cd,cd | cycling,pushing bike,cycling,cycling |
+    	 | d    | a  | cd,bc,ab,ab | cycling,pushing bike,cycling,cycling |
+    	 | c    | a  | bc,ab,ab    | pushing bike,cycling,cycling         |
+    	 | d    | b  | cd,bc,bc    | cycling,pushing bike,pushing bike    |
+    	 | a    | c  | ab,bc,bc    | cycling,pushing bike,pushing bike    |
+         | b    | d  | bc,cd,cd    | pushing bike,cycling,cycling         |
 
     Scenario: Bicycle - Modes when starting on forward oneway
         Given the node map
diff --git a/features/bicycle/pushing.feature b/features/bicycle/pushing.feature
index 3d00b53..d672821 100644
--- a/features/bicycle/pushing.feature
+++ b/features/bicycle/pushing.feature
@@ -63,7 +63,6 @@ Feature: Bike - Accessability of different way types
             | runway   |      |      |       |
             | runway   | yes  | foot | foot  |
 
-    @todo
     Scenario: Bike - Pushing bikes on ways with foot=yes in one direction
         Then routability should be
             | highway  | foot:forward | foot:backward | forw | backw |
@@ -98,13 +97,12 @@ Feature: Bike - Accessability of different way types
             | cd    | primary |        |
 
         When I route I should get
-            | from | to | route       | turns                                   |
-            | a    | d  | ab,bc,cd,cd | depart,right,left,arrive                |
-            | d    | a  | cd,bc,ab,ab | depart,right,left,arrive                |
-            | c    | a  | bc,ab,ab    | depart,left,arrive                      |
-            | d    | b  | cd,bc,bc    | depart,right,arrive                     |
+            | from | to | route       | modes                                   |
+            | a    | d  | ab,bc,cd,cd | cycling,cycling,cycling,cycling         |
+            | d    | a  | cd,bc,ab,ab | cycling,pushing bike,cycling,cycling    |
+            | c    | a  | bc,ab,ab    | pushing bike,cycling,cycling            |
+            | d    | b  | cd,bc,bc    | cycling,pushing bike,pushing bike       |
 
-    @todo
     Scenario: Bike - Instructions when pushing bike on footway/pedestrian, etc.
         Given the node map
             | a | b |   |
@@ -117,8 +115,8 @@ Feature: Bike - Accessability of different way types
             | cd    | primary |
 
         When I route I should get
-            | from | to | route       | turns                    |
-            | a    | d  | ab,bc,cd,cd | depart,right,left,arrive |
-            | d    | a  | cd,bc,ab,ab | depart,right,left,arrive |
-            | c    | a  | bc,ab,ab    | depart,left,arrive       |
-            | d    | b  | cd,bc,bc    | depart,right,arrive      |
+            | from | to | route       | modes                                |
+            | a    | d  | ab,bc,cd,cd | cycling,pushing bike,cycling,cycling |
+            | d    | a  | cd,bc,ab,ab | cycling,pushing bike,cycling,cycling |
+            | c    | a  | bc,ab,ab    | pushing bike,cycling,cycling         |
+            | d    | b  | cd,bc,bc    | cycling,pushing bike,pushing bike    |
diff --git a/features/bicycle/roundabout.feature b/features/bicycle/roundabout.feature
deleted file mode 100644
index 2e0d099..0000000
--- a/features/bicycle/roundabout.feature
+++ /dev/null
@@ -1,30 +0,0 @@
- at routing @bicycle @roundabout @instruction
-Feature: Roundabout Instructions
-
-    Background:
-        Given the profile "bicycle"
-
-    Scenario: Bicycle - Roundabout instructions
-        Given the node map
-            |   |   | v |   |   |
-            |   |   | d |   |   |
-            | s | a |   | c | u |
-            |   |   | b |   |   |
-            |   |   | t |   |   |
-
-        And the ways
-            | nodes | junction   |
-            | sa    |            |
-            | tb    |            |
-            | uc    |            |
-            | vd    |            |
-            | abcda | roundabout |
-
-        When I route I should get
-            | from | to | route    | turns                           |
-            | s    | t  | sa,tb,tb | depart,roundabout-exit-1,arrive |
-            | s    | u  | sa,uc,uc | depart,roundabout-exit-2,arrive |
-            | s    | v  | sa,vd,vd | depart,roundabout-exit-3,arrive |
-            | u    | v  | uc,vd,vd | depart,roundabout-exit-1,arrive |
-            | u    | s  | uc,sa,sa | depart,roundabout-exit-2,arrive |
-            | u    | t  | uc,tb,tb | depart,roundabout-exit-3,arrive |
diff --git a/features/car/access.feature b/features/car/access.feature
index 8f05ccb..93691d4 100644
--- a/features/car/access.feature
+++ b/features/car/access.feature
@@ -5,7 +5,7 @@ Feature: Car - Restricted access
     Background:
         Given the profile "car"
 
-    Scenario: Car - Access tag hierachy    on ways
+    Scenario: Car - Access tag hierarchy    on ways
         Then routability should be
             | access | vehicle | motor_vehicle | motorcar | bothw |
             |        |         |               |          | x     |
@@ -30,7 +30,7 @@ Feature: Car - Restricted access
             |        |         | no            | yes      | x     |
             |        |         | yes           | no       |       |
 
-    Scenario: Car - Access tag hierachy on nodes
+    Scenario: Car - Access tag hierarchy on nodes
         Then routability should be
             | node/access | node/vehicle | node/motor_vehicle | node/motorcar | bothw |
             |             |              |                    |               | x     |
@@ -94,6 +94,7 @@ Feature: Car - Restricted access
             | agricultural |       |
             | forestry     |       |
             | psv          |       |
+            | delivery     |       |
             | some_tag     | x     |
 
 
@@ -108,6 +109,7 @@ Feature: Car - Restricted access
             | agricultural |       |
             | forestry     |       |
             | psv          |       |
+            | delivery     |       |
             | some_tag     | x     |
 
     Scenario: Car - Access tags on both node and way
diff --git a/features/car/destination.feature b/features/car/destination.feature
index 506aa21..3bf0733 100644
--- a/features/car/destination.feature
+++ b/features/car/destination.feature
@@ -19,15 +19,15 @@ Feature: Car - Destination only, no passing through
             | axye  |             |
 
         When I route I should get
-            | from | to | route  |
-            | a    | b  | ab     |
-            | a    | c  | ab,bcd |
-            | a    | d  | ab,bcd |
-            | a    | e  | axye   |
-            | e    | d  | de     |
-            | e    | c  | de,bcd |
-            | e    | b  | de,bcd |
-            | e    | a  | axye   |
+            | from | to | route      |
+            | a    | b  | ab,ab      |
+            | a    | c  | ab,bcd,bcd |
+            | a    | d  | ab,bcd,bcd |
+            | a    | e  | axye,axye  |
+            | e    | d  | de,de      |
+            | e    | c  | de,bcd,bcd |
+            | e    | b  | de,bcd,bcd |
+            | e    | a  | axye,axye  |
 
     Scenario: Car - Destination only street
         Given the node map
@@ -45,15 +45,15 @@ Feature: Car - Destination only, no passing through
             | axye  |             |
 
         When I route I should get
-            | from | to | route    |
-            | a    | b  | ab       |
-            | a    | c  | ab,bc    |
-            | a    | d  | ab,bc,cd |
-            | a    | e  | axye     |
-            | e    | d  | de       |
-            | e    | c  | de,dc    |
-            | e    | b  | de,dc,bc |
-            | e    | a  | axye     |
+            | from | to | route       |
+            | a    | b  | ab,ab       |
+            | a    | c  | ab,bc,bc    |
+            | a    | d  | ab,bc,cd,cd |
+            | a    | e  | axye,axye   |
+            | e    | d  | de,de       |
+            | e    | c  | de,cd,cd    |
+            | e    | b  | de,cd,bc,bc |
+            | e    | a  | axye,axye   |
 
     Scenario: Car - Routing inside a destination only area
         Given the node map
@@ -70,8 +70,8 @@ Feature: Car - Destination only, no passing through
             | axye  |             |
 
         When I route I should get
-            | from | to | route       |
-            | a    | e  | ab,bc,cd,de |
-            | e    | a  | de,cd,bc,ab |
-            | b    | d  | bc,cd       |
-            | d    | b  | cd,bc       |
+            | from | to | route          |
+            | a    | e  | ab,bc,cd,de,de |
+            | e    | a  | de,cd,bc,ab,ab |
+            | b    | d  | bc,cd,cd       |
+            | d    | b  | cd,bc,bc       |
diff --git a/features/car/mode.feature b/features/car/mode.feature
index f390982..e0feff1 100644
--- a/features/car/mode.feature
+++ b/features/car/mode.feature
@@ -15,13 +15,13 @@ Feature: Car - Mode flag
             | cd    | primary |       |          |
 
         When I route I should get
-            | from | to | route       | turns                    | modes                         |
-            | a    | d  | ab,bc,cd,cd | depart,right,left,arrive | driving,ferry,driving,driving |
-            | d    | a  | cd,bc,ab,ab | depart,right,left,arrive | driving,ferry,driving,driving |
-            | c    | a  | bc,ab,ab    | depart,left,arrive       | ferry,driving,driving         |
-            | d    | b  | cd,bc,bc    | depart,right,arrive      | driving,ferry,ferry           |
-            | a    | c  | ab,bc,bc    | depart,right,arrive      | driving,ferry,ferry           |
-            | b    | d  | bc,cd,cd    | depart,left,arrive       | ferry,driving,driving         |
+            | from | to | route       | modes                         |
+            | a    | d  | ab,bc,cd,cd | driving,ferry,driving,driving |
+            | d    | a  | cd,bc,ab,ab | driving,ferry,driving,driving |
+            | c    | a  | bc,ab,ab    | ferry,driving,driving         |
+            | d    | b  | cd,bc,bc    | driving,ferry,ferry           |
+            | a    | c  | ab,bc,bc    | driving,ferry,ferry           |
+            | b    | d  | bc,cd,cd    | ferry,driving,driving         |
 
     Scenario: Car - Snapping when using a ferry
         Given the node map
@@ -34,7 +34,7 @@ Feature: Car - Mode flag
             | ef    | primary |       |          |
 
         When I route I should get
-            | from | to | route     | turns         | modes       | time  |
-            | c    | d  | bcde,bcde | depart,arrive | ferry,ferry | 600s  |
+            | from | to | route     | modes       | time  |
+            | c    | d  | bcde,bcde | ferry,ferry | 600s  |
 
 
diff --git a/features/car/roundabout.feature b/features/car/roundabout.feature
deleted file mode 100644
index beb5d4e..0000000
--- a/features/car/roundabout.feature
+++ /dev/null
@@ -1,30 +0,0 @@
- at routing @car @roundabout @instruction
-Feature: Roundabout Instructions
-
-    Background:
-        Given the profile "car"
-
-    Scenario: Car - Roundabout instructions
-        Given the node map
-            |   |   | v |   |   |
-            |   |   | d |   |   |
-            | s | a |   | c | u |
-            |   |   | b |   |   |
-            |   |   | t |   |   |
-
-        And the ways
-            | nodes | junction   |
-            | sa    |            |
-            | tb    |            |
-            | uc    |            |
-            | vd    |            |
-            | abcda | roundabout |
-
-        When I route I should get
-            | from | to | route    | turns                            |
-            | s    | t  | sa,tb,tb | depart,roundabout-exit-1,arrive |
-            | s    | u  | sa,uc,uc | depart,roundabout-exit-2,arrive |
-            | s    | v  | sa,vd,vd | depart,roundabout-exit-3,arrive |
-            | u    | v  | uc,vd,vd | depart,roundabout-exit-1,arrive |
-            | u    | s  | uc,sa,sa | depart,roundabout-exit-2,arrive |
-            | u    | t  | uc,tb,tb | depart,roundabout-exit-3,arrive |
diff --git a/features/foot/access.feature b/features/foot/access.feature
index 353076c..4642a3b 100644
--- a/features/foot/access.feature
+++ b/features/foot/access.feature
@@ -5,7 +5,7 @@ Feature: Foot - Access tags on ways
     Background:
         Given the profile "foot"
 
-    Scenario: Foot - Access tag hierachy on ways
+    Scenario: Foot - Access tag hierarchy on ways
         Then routability should be
             | highway  | access | foot | bothw |
             | footway  |        |      | x     |
@@ -52,6 +52,7 @@ Feature: Foot - Access tags on ways
             | private      |              |       |
             | agricultural |              |       |
             | forestry     |              |       |
+            | delivery     |              |       |
             |              | yes          | x     |
             |              | permissive   | x     |
             |              | designated   | x     |
@@ -60,6 +61,7 @@ Feature: Foot - Access tags on ways
             |              | private      |       |
             |              | agricultural |       |
             |              | forestry     |       |
+            |              | delivery     |       |
 
     Scenario: Foot - Access tags on both node and way
         Then routability should be
diff --git a/features/foot/access_node.feature b/features/foot/access_node.feature
index 8519d05..8dc0ebe 100644
--- a/features/foot/access_node.feature
+++ b/features/foot/access_node.feature
@@ -5,7 +5,7 @@ Feature: Foot - Access tags on nodes
     Background:
         Given the profile "foot"
 
-    Scenario: Foot - Access tag hierachy on nodes
+    Scenario: Foot - Access tag hierarchy on nodes
         Then routability should be
             | node/access | node/foot | bothw |
             |             |           | x     |
@@ -40,6 +40,7 @@ Feature: Foot - Access tags on nodes
             | private      |              |       |
             | agricultural |              |       |
             | forestry     |              |       |
+            | delivery     |              |       |
             | no           | yes          | x     |
             | no           | permissive   | x     |
             | no           | designated   | x     |
@@ -48,3 +49,4 @@ Feature: Foot - Access tags on nodes
             | yes          | private      |       |
             | yes          | agricultural |       |
             | yes          | forestry     |       |
+            | yes          | delivery     |       |
diff --git a/features/guidance/continue.feature b/features/guidance/continue.feature
new file mode 100644
index 0000000..b3abe48
--- /dev/null
+++ b/features/guidance/continue.feature
@@ -0,0 +1,90 @@
+ at routing  @guidance
+Feature: Continue Instructions
+
+    Background:
+        Given the profile "car"
+        Given a grid size of 10 meters
+
+    Scenario: Road turning left
+        Given the node map
+            |   |   | c |   |
+            | a |   | b | d |
+
+        And the ways
+            | nodes  | highway |
+            | abc    | primary |
+            | bd     | primary |
+
+       When I route I should get
+            | waypoints | route       | turns                           |
+            | a,c       | abc,abc,abc | depart,continue left,arrive     |
+            | a,d       | abc,bd,bd   | depart,new name straight,arrive |
+
+    Scenario: Road turning right
+        Given the node map
+            | a |   | b | d |
+            |   |   | c |   |
+
+        And the ways
+            | nodes  | highway |
+            | abc    | primary |
+            | bd     | primary |
+
+       When I route I should get
+            | waypoints | route       | turns                           |
+            | a,c       | abc,abc,abc | depart,continue right,arrive    |
+            | a,d       | abc,bd,bd   | depart,new name straight,arrive |
+
+    Scenario: Road turning slight left
+        Given the node map
+            |   |   |   |   | c |
+            |   |   |   |   |   |
+            | a |   | b |   |   |
+            |   |   |   | d |   |
+
+        And the ways
+            | nodes  | highway |
+            | abc    | primary |
+            | bd     | primary |
+
+       When I route I should get
+            | waypoints | route       | turns                       |
+            | a,c       | abc,abc,abc | depart,continue left,arrive |
+            | a,d       | abc,bd,bd   | depart,turn right,arrive    |
+
+    Scenario: Road turning slight right
+        Given the node map
+            |   |   |   | d |   |
+            | a |   | b |   |   |
+            |   |   |   |   |   |
+            |   |   |   |   | c |
+
+        And the ways
+            | nodes  | highway |
+            | abc    | primary |
+            | bd     | primary |
+
+       When I route I should get
+            | waypoints | route       | turns                        |
+            | a,c       | abc,abc,abc | depart,continue right,arrive |
+            | a,d       | abc,bd,bd   | depart,turn left,arrive      |
+
+    Scenario: Road Loop
+       Given the node map
+           |   |   | f |   | e |
+           |   |   |   |   |   |
+           | a |   | b | g |   |
+           |   |   |   |   |   |
+           |   |   | c |   | d |
+
+       And the ways
+          | nodes   | highway |
+          | abcdefb | primary |
+          | bg      | primary |
+
+       When I route I should get
+          | waypoints | route                   | turns                        |
+          | a,c       | abcdefb,abcdefb,abcdefb | depart,continue right,arrive |
+          | a,f       | abcdefb,abcdefb,abcdefb | depart,continue left,arrive  |
+          | a,d       | abcdefb,abcdefb,abcdefb | depart,continue right,arrive |
+          | a,e       | abcdefb,abcdefb,abcdefb | depart,continue left,arrive  |
diff --git a/features/guidance/end-of-road.feature b/features/guidance/end-of-road.feature
new file mode 100644
index 0000000..7f8231b
--- /dev/null
+++ b/features/guidance/end-of-road.feature
@@ -0,0 +1,106 @@
+ at routing  @guidance
+Feature: End Of Road Instructions
+
+    Background:
+        Given the profile "car"
+        Given a grid size of 10 meters
+
+    Scenario: End of Road with through street
+        Given the node map
+            |   |   | c |
+            | a |   | b |
+            |   |   | d |
+
+        And the ways
+            | nodes  | highway |
+            | ab     | primary |
+            | cbd    | primary |
+
+       When I route I should get
+            | waypoints | route      | turns                           |
+            | a,c       | ab,cbd,cbd | depart,end of road left,arrive  |
+            | a,d       | ab,cbd,cbd | depart,end of road right,arrive |
+
+
+    Scenario: End of Road with three streets
+        Given the node map
+            |   |   | c |
+            | a |   | b |
+            |   |   | d |
+
+        And the ways
+            | nodes  | highway |
+            | ab     | primary |
+            | cb     | primary |
+            | bd     | primary |
+
+       When I route I should get
+            | waypoints | route    | turns                           |
+            | a,c       | ab,cb,cb | depart,end of road left,arrive  |
+            | a,d       | ab,bd,bd | depart,end of road right,arrive |
+
+    Scenario: End of Road with three streets, slightly angled
+        Given the node map
+            | a |   |   |   |   | c |
+            |   |   |   |   |   | b |
+            |   |   |   |   |   | d |
+
+        And the ways
+            | nodes  | highway |
+            | ab     | primary |
+            | cb     | primary |
+            | bd     | primary |
+
+       When I route I should get
+            | waypoints | route    | turns                           |
+            | a,c       | ab,cb,cb | depart,end of road left,arrive  |
+            | a,d       | ab,bd,bd | depart,end of road right,arrive |
+
+    Scenario: End of Road with three streets, slightly angled
+        Given the node map
+            |   |   |   |   |   | c |
+            |   |   |   |   |   | b |
+            | a |   |   |   |   | d |
+
+        And the ways
+            | nodes  | highway |
+            | ab     | primary |
+            | cb     | primary |
+            | bd     | primary |
+
+       When I route I should get
+            | waypoints | route    | turns                           |
+            | a,c       | ab,cb,cb | depart,end of road left,arrive  |
+            | a,d       | ab,bd,bd | depart,end of road right,arrive |
+
+    Scenario: End of Road with through street, slightly angled
+        Given the node map
+            | a |   |   |   |   | c |
+            |   |   |   |   |   | b |
+            |   |   |   |   |   | d |
+
+        And the ways
+            | nodes  | highway |
+            | ab     | primary |
+            | cbd    | primary |
+
+       When I route I should get
+            | waypoints | route      | turns                           |
+            | a,c       | ab,cbd,cbd | depart,end of road left,arrive  |
+            | a,d       | ab,cbd,cbd | depart,end of road right,arrive |
+
+    Scenario: End of Road with through street, slightly angled
+        Given the node map
+            |   |   |   |   |   | c |
+            |   |   |   |   |   | b |
+            | a |   |   |   |   | d |
+
+        And the ways
+            | nodes  | highway |
+            | ab     | primary |
+            | cbd    | primary |
+
+       When I route I should get
+            | waypoints | route      | turns                           |
+            | a,c       | ab,cbd,cbd | depart,end of road left,arrive  |
+            | a,d       | ab,cbd,cbd | depart,end of road right,arrive |
diff --git a/features/guidance/fork.feature b/features/guidance/fork.feature
new file mode 100644
index 0000000..7b78a28
--- /dev/null
+++ b/features/guidance/fork.feature
@@ -0,0 +1,213 @@
+ at routing  @guidance
+Feature: Fork Instructions
+
+    Background:
+        Given the profile "car"
+        Given a grid size of 10 meters
+
+    Scenario: Fork Same Road Class
+        Given the node map
+            |   |   |   |   | c |
+            | a |   | b |   |   |
+            |   |   |   |   | d |
+
+        And the ways
+            | nodes  | highway |
+            | ab     | primary |
+            | bc     | primary |
+            | bd     | primary |
+
+       When I route I should get
+            | waypoints | route    | turns                           |
+            | a,c       | ab,bc,bc | depart,fork slight left,arrive  |
+            | a,d       | ab,bd,bd | depart,fork slight right,arrive |
+
+    Scenario: Do not fork on link type
+        Given the node map
+            |   |   |   |   | c |
+            | a |   | b |   |   |
+            |   |   |   |   | d |
+
+        And the ways
+            | nodes  | highway      |
+            | abc    | primary      |
+            | bd     | primary_link |
+
+
+       When I route I should get
+            | waypoints | route      | turns                           |
+            | a,c       | abc,abc    | depart,arrive                   |
+            | a,d       | abc,bd,bd  | depart,turn slight right,arrive |
+
+    Scenario: Fork in presence of other roads
+        Given the node map
+            |   |   |   |   | c |
+            | a |   | b |   |   |
+            |   | e |   |   | d |
+
+        And the ways
+            | nodes  | highway |
+            | ab     | primary |
+            | bc     | primary |
+            | bd     | primary |
+            | eb     | primary |
+
+       When I route I should get
+            | waypoints | route    | turns                           |
+            | a,c       | ab,bc,bc | depart,fork slight left,arrive  |
+            | a,d       | ab,bd,bd | depart,fork slight right,arrive |
+
+    Scenario: Fork Turning Slight Left
+        Given the node map
+            |   |   |   |   |   | c |
+            |   |   |   |   |   |   |
+            | a |   | b |   |   |   |
+            |   |   |   |   | d |   |
+
+        And the ways
+            | nodes  | highway |
+            | ab     | primary |
+            | bc     | primary |
+            | bd     | primary |
+
+       When I route I should get
+            | waypoints | route    | turns                           |
+            | a,c       | ab,bc,bc | depart,fork slight left,arrive  |
+            | a,d       | ab,bd,bd | depart,fork slight right,arrive |
+
+    Scenario: Fork Turning Slight Right
+        Given the node map
+            |   |   |   |   | c |   |
+            | a |   | b |   |   |   |
+            |   |   |   |   |   |   |
+            |   |   |   |   |   | d |
+
+        And the ways
+            | nodes  | highway |
+            | ab     | primary |
+            | bc     | primary |
+            | bd     | primary |
+
+       When I route I should get
+            | waypoints | route    | turns                           |
+            | a,c       | ab,bc,bc | depart,fork slight left,arrive  |
+            | a,d       | ab,bd,bd | depart,fork slight right,arrive |
+
+    Scenario: Do not fork on service
+        Given the node map
+            |   |   |   |   | c |
+            | a |   | b |   |   |
+            |   |   |   |   | d |
+
+        And the ways
+            | nodes  | highway     |
+            | abc    | residential |
+            | bd     | service     |
+
+       When I route I should get
+            | waypoints | route     | turns                           |
+            | a,c       | abc,abc   | depart,arrive                   |
+            | a,d       | abc,bd,bd | depart,turn slight right,arrive |
+
+    Scenario: Fork Both Turning Slight Right
+        Given the node map
+            | a |   | b |   |   |   |
+            |   |   |   |   |   | c |
+            |   |   |   |   |   | d |
+
+        And the ways
+            | nodes  | highway |
+            | ab     | primary |
+            | bc     | primary |
+            | bd     | primary |
+
+       When I route I should get
+            | waypoints | route    | turns                           |
+            | a,c       | ab,bc,bc | depart,fork slight left,arrive  |
+            | a,d       | ab,bd,bd | depart,fork slight right,arrive |
+
+    Scenario: Fork Both Turning Slight Left
+        Given the node map
+            |   |   |   |   |   | c |
+            |   |   |   |   |   | d |
+            | a |   | b |   |   |   |
+
+        And the ways
+            | nodes  | highway |
+            | ab     | primary |
+            | bc     | primary |
+            | bd     | primary |
+
+       When I route I should get
+            | waypoints | route    | turns                           |
+            | a,c       | ab,bc,bc | depart,fork slight left,arrive  |
+            | a,d       | ab,bd,bd | depart,fork slight right,arrive |
+
+    Scenario: Fork Both Turning Slight Right - Unnamed
+        Given the node map
+            | a |   | b |   |   |   |
+            |   |   |   |   |   | c |
+            |   |   |   |   |   | d |
+
+        And the ways
+            | nodes  | highway | name |
+            | ab     | primary |      |
+            | bc     | primary |      |
+            | bd     | primary |      |
+
+       When I route I should get
+            | waypoints | route | turns                           |
+            | a,c       | ,,    | depart,fork slight left,arrive  |
+            | a,d       | ,,    | depart,fork slight right,arrive |
+
+    Scenario: Fork Both Turning Slight Left - Unnamed
+        Given the node map
+            |   |   |   |   |   | c |
+            |   |   |   |   |   | d |
+            | a |   | b |   |   |   |
+
+        And the ways
+            | nodes  | highway | name |
+            | ab     | primary |      |
+            | bc     | primary |      |
+            | bd     | primary |      |
+
+       When I route I should get
+            | waypoints | route | turns                           |
+            | a,c       | ,,    | depart,fork slight left,arrive  |
+            | a,d       | ,,    | depart,fork slight right,arrive |
+
+    Scenario: Fork Both Turning Very Slightly Right - Unnamed
+        Given the node map
+            | a |   | b |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |
+            |   |   |   |   |   |   |   |   |   |   |   | c |   |   |   |   |   |   |
+            |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   | d |
+
+        And the ways
+            | nodes  | highway | name |
+            | ab     | primary |      |
+            | bc     | primary |      |
+            | bd     | primary |      |
+
+       When I route I should get
+            | waypoints | route | turns                           |
+            | a,c       | ,,    | depart,fork slight left,arrive  |
+            | a,d       | ,,    | depart,fork slight right,arrive |
+
+    Scenario: Fork Both Turning Very Slightly Right - Unnamed Ramps
+        Given the node map
+            | a |   | b |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |
+            |   |   |   |   |   |   |   |   |   |   |   | c |   |   |   |   |   |   |
+            |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   | d |
+
+        And the ways
+            | nodes  | highway       | name |
+            | ab     | motorway_link |      |
+            | bc     | motorway_link |      |
+            | bd     | motorway_link |      |
+
+       When I route I should get
+            | waypoints | route | turns                           |
+            | a,c       | ,,    | depart,fork slight left,arrive  |
+            | a,d       | ,,    | depart,fork slight right,arrive |
+
diff --git a/features/guidance/merge.feature b/features/guidance/merge.feature
new file mode 100644
index 0000000..0736e8f
--- /dev/null
+++ b/features/guidance/merge.feature
@@ -0,0 +1,52 @@
+ at routing  @guidance
+Feature: Merging
+
+    Background:
+        Given the profile "car"
+        Given a grid size of 10 meters
+
+    Scenario: Merge on Four Way Intersection
+        Given the node map
+            | d |   |   |
+            | a | b | c |
+            | e |   |   |
+
+        And the ways
+            | nodes | highway |
+            | abc   | primary |
+            | db    | primary |
+            | eb    | primary |
+
+       When I route I should get
+            | waypoints | route      | turns                            |
+            | d,c       | db,abc,abc | depart,merge slight right,arrive |
+            | e,c       | eb,abc,abc | depart,merge slight left,arrive  |
+
+    Scenario: Merge on Three Way Intersection Right
+        Given the node map
+            | d |   |   |
+            | a | b | c |
+
+        And the ways
+            | nodes | highway |
+            | abc   | primary |
+            | db    | primary |
+
+       When I route I should get
+            | waypoints | route      | turns                            |
+            | d,c       | db,abc,abc | depart,merge slight right,arrive |
+
+    Scenario: Merge on Three Way Intersection Right
+        Given the node map
+            | a | b | c |
+            | d |   |   |
+
+        And the ways
+            | nodes | highway |
+            | abc   | primary |
+            | db    | primary |
+
+       When I route I should get
+            | waypoints | route      | turns                           |
+            | d,c       | db,abc,abc | depart,merge slight left,arrive |
+
diff --git a/features/guidance/motorway.feature b/features/guidance/motorway.feature
index bcde322..5e9b312 100644
--- a/features/guidance/motorway.feature
+++ b/features/guidance/motorway.feature
@@ -1,8 +1,8 @@
 @routing  @guidance
-Feature: Basic Roundabout
+Feature: Motorway Guidance
 
     Background:
-        Given the profile "testbot"
+        Given the profile "car"
         Given a grid size of 10 meters
 
     Scenario: Ramp Exit Right
@@ -16,9 +16,9 @@ Feature: Basic Roundabout
             | 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,ramp slight right,arrive |
 
     Scenario: Ramp Exit Right Curved Right
         Given the node map
@@ -32,9 +32,9 @@ Feature: Basic Roundabout
             | 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,ramp right,arrive |
 
     Scenario: Ramp Exit Right Curved Left
         Given the node map
@@ -49,9 +49,9 @@ Feature: Basic Roundabout
             | 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,ramp slight right,arrive |
 
 
     Scenario: Ramp Exit Left
@@ -65,9 +65,9 @@ Feature: Basic Roundabout
             | 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,ramp slight left,arrive |
 
     Scenario: Ramp Exit Left Curved Left
         Given the node map
@@ -81,9 +81,9 @@ Feature: Basic Roundabout
             | 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,ramp left,arrive |
 
     Scenario: Ramp Exit Left Curved Right
         Given the node map
@@ -97,9 +97,9 @@ Feature: Basic Roundabout
             | 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,ramp slight left,arrive |
 
     Scenario: On Ramp Right
         Given the node map
@@ -112,9 +112,9 @@ Feature: Basic Roundabout
             | fgd    | motorway_link |
 
        When I route I should get
-            | waypoints | route           | turns                             |
-            | a,e       | abcde, abcde    | depart, arrive                    |
-            | f,e       | abcde, fgd, fgd | depart, merge-slight-left, arrive |
+            | waypoints | route           | turns                           |
+            | a,e       | abcde,abcde     | depart,arrive                   |
+            | f,e       | fgd,abcde,abcde | depart,merge slight left,arrive |
 
     Scenario: On Ramp Left
         Given the node map
@@ -127,9 +127,9 @@ Feature: Basic Roundabout
             | fgd    | motorway_link |
 
        When I route I should get
-            | waypoints | route           | turns                              |
-            | a,e       | abcde, abcde    | depart, arrive                     |
-            | f,e       | abcde, fgd, fgd | depart, merge-slight-right, arrive |
+            | waypoints | route           | turns                            |
+            | a,e       | abcde,abcde     | depart,arrive                    |
+            | f,e       | fgd,abcde,abcde | depart,merge slight right,arrive |
 
     Scenario: Highway Fork
         Given the node map
@@ -143,9 +143,9 @@ Feature: Basic Roundabout
             | cfg    | motorway |
 
        When I route I should get
-            | waypoints | route               | turns                      |
-            | a,e       | abcde, abcde, abcde | depart, fork-left, arrive  |
-            | a,g       | abcde, cfg, cfg     | depart, fork-right, arrive |
+            | waypoints | route             | turns                           |
+            | a,e       | abcde,abcde,abcde | depart,fork slight left,arrive  |
+            | a,g       | abcde,cfg,cfg     | depart,fork slight right,arrive |
 
      Scenario: Fork After Ramp
        Given the node map
@@ -160,9 +160,9 @@ Feature: Basic Roundabout
             | cfg    | motorway      |
 
        When I route I should get
-            | waypoints | route         | turns                      |
-            | a,e       | abc, cde, cde | depart, fork-left, arrive  |
-            | a,g       | abc, cfg, cfg | depart, fork-right, arrive |
+            | waypoints | route       | turns                           |
+            | a,e       | abc,cde,cde | depart,fork slight left,arrive  |
+            | a,g       | abc,cfg,cfg | depart,fork slight right,arrive |
 
      Scenario: On And Off Ramp Right
        Given the node map
@@ -176,11 +176,11 @@ Feature: Basic Roundabout
             | 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, turn-slight-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,ramp slight right,arrive |
+            | f,i       | fgc,chi,chi     | depart,ramp right,arrive        |
 
     Scenario: On And Off Ramp Left
        Given the node map
@@ -194,9 +194,25 @@ Feature: Basic Roundabout
             | 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, turn-slight-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,ramp slight left,arrive   |
+            | f,i       | fgc,chi,chi     | depart,ramp left,arrive          |
+
+    Scenario: Merging Motorways
+        Given the node map
+            | e |   |   |
+            | a | b | c |
+            | d |   |   |
 
+        And the ways
+            | nodes | highway  |
+            | abc   | motorway |
+            | db    | motorway |
+            | eb    | motorway |
+
+        When I route I should get
+            | waypoints | route      | turns                            |
+            | d,c       | db,abc,abc | depart,merge slight left,arrive  |
+            | e,c       | eb,abc,abc | depart,merge slight right,arrive |
diff --git a/features/guidance/new-name.feature b/features/guidance/new-name.feature
new file mode 100644
index 0000000..1fdf1ce
--- /dev/null
+++ b/features/guidance/new-name.feature
@@ -0,0 +1,135 @@
+ at routing  @guidance
+Feature: New-Name Instructions
+
+    Background:
+        Given the profile "car"
+        Given a grid size of 10 meters
+
+    Scenario: Undisturbed name Change
+        Given the node map
+            | a |   | b |   | c |
+
+        And the ways
+            | nodes  |
+            | ab     |
+            | bc     |
+
+       When I route I should get
+            | waypoints | route    | turns                           |
+            | a,c       | ab,bc,bc | depart,new name straight,arrive |
+
+
+    Scenario: Undisturbed Name Change with unannounced Turn Right
+        Given the node map
+            | a |   | b |   |   |
+            |   |   |   |   | c |
+
+        And the ways
+            | nodes  |
+            | ab     |
+            | bc     |
+
+       When I route I should get
+            | waypoints | route    | turns                               |
+            | a,c       | ab,bc,bc | depart,new name slight right,arrive |
+
+    Scenario: Undisturbed Name Change with unannounced Turn Left
+        Given the node map
+            |   |   |   |   | c |
+            | a |   | b |   |   |
+
+        And the ways
+            | nodes  |
+            | ab     |
+            | bc     |
+
+       When I route I should get
+            | waypoints | route    | turns                              |
+            | a,c       | ab,bc,bc | depart,new name slight left,arrive |
+
+    Scenario: Disturbed Name Change with Turn
+        Given the node map
+            | a |   | b |   |   |
+            |   | d |   |   | c |
+
+        And the ways
+            | nodes  |
+            | ab     |
+            | bc     |
+            | db     |
+
+       When I route I should get
+            | waypoints | route    | turns                               |
+            | a,c       | ab,bc,bc | depart,new name slight right,arrive |
+
+    Scenario: Undisturbed Name Change with announced Turn Left
+        Given the node map
+            |   |   | c |
+            | a |   | b |
+
+        And the ways
+            | nodes  |
+            | ab     |
+            | bc     |
+
+       When I route I should get
+            | waypoints | route    | turns                       |
+            | a,c       | ab,bc,bc | depart,new name left,arrive |
+
+    Scenario: Undisturbed Name Change with announced Turn Sharp Left
+        Given the node map
+            | c |   |   |
+            | a |   | b |
+
+        And the ways
+            | nodes  |
+            | ab     |
+            | bc     |
+
+       When I route I should get
+            | waypoints | route    | turns                             |
+            | a,c       | ab,bc,bc | depart,new name sharp left,arrive |
+
+    Scenario: Undisturbed Name Change with announced Turn Right
+        Given the node map
+            | a |   | b |
+            |   |   | c |
+
+        And the ways
+            | nodes  |
+            | ab     |
+            | bc     |
+
+       When I route I should get
+            | waypoints | route    | turns                        |
+            | a,c       | ab,bc,bc | depart,new name right,arrive |
+
+    Scenario: Undisturbed Name Change with announced Turn Sharp Right
+        Given the node map
+            | a |   | b |
+            | c |   |   |
+
+        And the ways
+            | nodes  |
+            | ab     |
+            | bc     |
+
+       When I route I should get
+            | waypoints | route    | turns                              |
+            | a,c       | ab,bc,bc | depart,new name sharp right,arrive |
+
+
+    Scenario: Disturbed Name Change with minor road class
+        Given the node map
+            | a |   | b |   | d |
+            |   |   |   |   | c |
+
+        And the ways
+            | nodes  | highway     |
+            | ab     | residential |
+            | bc     | residential |
+            | bd     | service     |
+
+       When I route I should get
+            | waypoints | route    | turns                               |
+            | a,c       | ab,bc,bc | depart,new name slight right,arrive |
diff --git a/features/guidance/ramp.feature b/features/guidance/ramp.feature
new file mode 100644
index 0000000..3c3ce39
--- /dev/null
+++ b/features/guidance/ramp.feature
@@ -0,0 +1,229 @@
+ at routing  @guidance
+Feature: Ramp Guidance
+
+    Background:
+        Given the profile "car"
+        Given a grid size of 10 meters
+
+    Scenario: Ramp On Through Street Right
+        Given the node map
+            | a | b | c |
+            |   | d |   |
+
+        And the ways
+            | nodes | highway       |
+            | abc   | tertiary      |
+            | bd    | motorway_link |
+
+       When I route I should get
+            | waypoints | route     | turns                    |
+            | a,d       | abc,bd,bd | depart,ramp right,arrive |
+
+    Scenario: Ramp On Through Street Left
+        Given the node map
+            |   | d |   |
+            | a | b | c |
+
+        And the ways
+            | nodes | highway       |
+            | abc   | tertiary      |
+            | bd    | motorway_link |
+
+       When I route I should get
+            | waypoints | route     | turns                   |
+            | a,d       | abc,bd,bd | depart,ramp left,arrive |
+
+    Scenario: Ramp On Through Street Left and Right
+        Given the node map
+            |   | e |   |
+            | a | b | c |
+            |   | d |   |
+
+        And the ways
+            | nodes | highway       |
+            | be    | motorway_link |
+            | abc   | tertiary      |
+            | 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  |
+
+    Scenario: Ramp On Three Way Intersection Right
+        Given the node map
+            | a | b | c |
+            |   | d |   |
+
+        And the ways
+            | nodes | highway       |
+            | ab    | tertiary      |
+            | bc    | tertiary      |
+            | bd    | motorway_link |
+
+       When I route I should get
+            | waypoints | route    | turns                    |
+            | a,d       | ab,bd,bd | depart,ramp right,arrive |
+
+    Scenario: Ramp On Three Way Intersection Right
+        Given the node map
+            |   |   | c |
+            | a | b |   |
+            |   | d |   |
+
+        And the ways
+            | nodes | highway       |
+            | ab    | tertiary      |
+            | bc    | tertiary      |
+            | bd    | motorway_link |
+
+       When I route I should get
+            | waypoints | route    | turns                    |
+            | a,d       | ab,bd,bd | depart,ramp right,arrive |
+
+    Scenario: Ramp Off Though Street
+        Given the node map
+            |   |   | c |
+            | a | b |   |
+            |   | d |   |
+
+        And the ways
+            | nodes | highway       |
+            | abc   | tertiary      |
+            | 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            |
+
+    Scenario: Straight Ramp Off Turning Though Street
+        Given the node map
+            |   |   | c |
+            | a | b | d |
+
+        And the ways
+            | nodes | highway       |
+            | abc   | tertiary      |
+            | 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 |
+
+    Scenario: Fork Ramp Off Turning Though Street
+        Given the node map
+            |   |   | c |
+            | a | b |   |
+            |   |   | d |
+
+        And the ways
+            | nodes | highway       |
+            | abc   | tertiary      |
+            | 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 |
+
+    Scenario: Fork Ramp
+        Given the node map
+            |   |   | c |
+            | a | b |   |
+            |   |   | d |
+
+        And the ways
+            | nodes | highway       |
+            | ab    | tertiary      |
+            | bc    | tertiary      |
+            | 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  |
+
+    Scenario: Fork Slight Ramp
+        Given the node map
+            |   |   |   | c |
+            | a | b |   |   |
+            |   |   |   | d |
+
+        And the ways
+            | nodes | highway       |
+            | ab    | tertiary      |
+            | bc    | tertiary      |
+            | 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  |
+
+    Scenario: Fork Slight Ramp on Through Street
+        Given the node map
+            |   |   |   | c |
+            | a | b |   |   |
+            |   |   |   | d |
+
+        And the ways
+            | nodes | highway       |
+            | abc   | tertiary      |
+            | 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 |
+
+    Scenario: Fork Slight Ramp on Obvious Through Street
+        Given the node map
+            |   |   |   | c |
+            | a | b |   |   |
+            |   |   |   | d |
+
+        And the ways
+            | nodes | highway       |
+            | abc   | primary       |
+            | 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                   |
+
+    Scenario: Two Ramps Joining into common Motorway
+        Given the node map
+            | a |   |   |   |
+            |   |   | c | d |
+            | b |   |   |   |
+
+        And the ways
+            | nodes | highway       |
+            | ac    | motorway_link |
+            | bc    | motorway_link |
+            | cd    | motorway      |
+
+        When I route I should get
+            | waypoints | route    | turns                               |
+            | a,d       | ac,cd,cd | depart,new name slight left,arrive  |
+            | b,d       | bc,cd,cd | depart,new name slight right,arrive |
+
+    Scenario: Two Ramps Joining into common Motorway Unnamed
+        Given the node map
+            | a |   |   |   |
+            |   |   | c | d |
+            | b |   |   |   |
+
+        And the ways
+            | nodes | highway       | name |
+            | ac    | motorway_link |      |
+            | bc    | motorway_link |      |
+            | cd    | motorway      |      |
+
+        When I route I should get
+            | waypoints | route    | turns         |
+            | a,d       | ,        | depart,arrive |
+            | b,d       | ,        | depart,arrive |
diff --git a/features/guidance/rotary-bike.feature b/features/guidance/rotary-bike.feature
new file mode 100644
index 0000000..512b815
--- /dev/null
+++ b/features/guidance/rotary-bike.feature
@@ -0,0 +1,167 @@
+ at routing  @guidance
+Feature: Rotary
+
+    Background:
+        Given the profile "bicycle"
+        Given a grid size of 30 meters
+
+    Scenario: Enter and Exit
+        Given the node map
+            |   |   | a |   |   |
+            |   |   | b |   |   |
+            | h | g |   | c | d |
+            |   |   | e |   |   |
+            |   |   | f |   |   |
+
+       And the ways
+            | nodes  | junction   |
+            | ab     |            |
+            | cd     |            |
+            | ef     |            |
+            | gh     |            |
+            | bgecb  | roundabout |
+
+       When I route I should get
+           | waypoints | route    | turns                      |
+           | a,d       | ab,cd,cd | depart,bgecb-exit-3,arrive |
+           | a,f       | ab,ef,ef | depart,bgecb-exit-2,arrive |
+           | a,h       | ab,gh,gh | depart,bgecb-exit-1,arrive |
+           | d,f       | cd,ef,ef | depart,bgecb-exit-3,arrive |
+           | d,h       | cd,gh,gh | depart,bgecb-exit-2,arrive |
+           | d,a       | cd,ab,ab | depart,bgecb-exit-1,arrive |
+           | f,h       | ef,gh,gh | depart,bgecb-exit-3,arrive |
+           | f,a       | ef,ab,ab | depart,bgecb-exit-2,arrive |
+           | f,d       | ef,cd,cd | depart,bgecb-exit-1,arrive |
+           | h,a       | gh,ab,ab | depart,bgecb-exit-3,arrive |
+           | h,d       | gh,cd,cd | depart,bgecb-exit-2,arrive |
+           | h,f       | gh,ef,ef | depart,bgecb-exit-1,arrive |
+
+    Scenario: Only Enter
+        Given the node map
+            |   |   | a |   |   |
+            |   |   | b |   |   |
+            | d | c |   | g | h |
+            |   |   | e |   |   |
+            |   |   | f |   |   |
+
+       And the ways
+            | nodes  | junction   |
+            | ab     |            |
+            | cd     |            |
+            | ef     |            |
+            | gh     |            |
+            | bcegb  | roundabout |
+
+       When I route I should get
+           | waypoints | route          | turns                              |
+           | a,c       | ab,bcegb,bcegb | depart,bcegb-exit-undefined,arrive |
+           | a,e       | ab,bcegb,bcegb | depart,bcegb-exit-undefined,arrive |
+           | a,g       | ab,bcegb,bcegb | depart,bcegb-exit-undefined,arrive |
+           | d,e       | cd,bcegb,bcegb | depart,bcegb-exit-undefined,arrive |
+           | d,g       | cd,bcegb,bcegb | depart,bcegb-exit-undefined,arrive |
+           | d,b       | cd,bcegb,bcegb | depart,bcegb-exit-undefined,arrive |
+           | f,g       | ef,bcegb,bcegb | depart,bcegb-exit-undefined,arrive |
+           | f,b       | ef,bcegb,bcegb | depart,bcegb-exit-undefined,arrive |
+           | f,c       | ef,bcegb,bcegb | depart,bcegb-exit-undefined,arrive |
+           | h,b       | gh,bcegb,bcegb | depart,bcegb-exit-undefined,arrive |
+           | h,c       | gh,bcegb,bcegb | depart,bcegb-exit-undefined,arrive |
+           | h,e       | gh,bcegb,bcegb | depart,bcegb-exit-undefined,arrive |
+
+    Scenario: Only Exit
+        Given the node map
+            |   |   | a |   |   |
+            |   |   | b |   |   |
+            | d | c |   | g | h |
+            |   |   | e |   |   |
+            |   |   | f |   |   |
+
+       And the ways
+            | nodes  | junction   |
+            | ab     |            |
+            | cd     |            |
+            | ef     |            |
+            | gh     |            |
+            | bcegb  | roundabout |
+
+       When I route I should get
+           | waypoints | route       | turns                      |
+           | b,d       | bcegb,cd,cd | depart,bcegb-exit-1,arrive |
+           | b,f       | bcegb,ef,ef | depart,bcegb-exit-2,arrive |
+           | b,h       | bcegb,gh,gh | depart,bcegb-exit-3,arrive |
+           | c,f       | bcegb,ef,ef | depart,bcegb-exit-1,arrive |
+           | c,h       | bcegb,gh,gh | depart,bcegb-exit-2,arrive |
+           | c,a       | bcegb,ab,ab | depart,bcegb-exit-3,arrive |
+           | e,h       | bcegb,gh,gh | depart,bcegb-exit-1,arrive |
+           | e,a       | bcegb,ab,ab | depart,bcegb-exit-2,arrive |
+           | e,d       | bcegb,cd,cd | depart,bcegb-exit-3,arrive |
+           | g,a       | bcegb,ab,ab | depart,bcegb-exit-1,arrive |
+           | g,d       | bcegb,cd,cd | depart,bcegb-exit-2,arrive |
+           | g,f       | bcegb,ef,ef | depart,bcegb-exit-3,arrive |
+      #phantom node snapping can result in a full round-trip here, therefore we cannot test b->a and the other direct exits
+
+    Scenario: Drive Around
+        Given the node map
+            |   |   | a |   |   |
+            |   |   | b |   |   |
+            | d | c |   | g | h |
+            |   |   | e |   |   |
+            |   |   | f |   |   |
+
+       And the ways
+            | nodes  | junction   |
+            | ab     |            |
+            | cd     |            |
+            | ef     |            |
+            | gh     |            |
+            | bcegb  | roundabout |
+
+       When I route I should get
+           | waypoints | route       | turns         |
+           | b,c       | bcegb,bcegb | depart,arrive |
+           | b,e       | bcegb,bcegb | depart,arrive |
+           | b,g       | bcegb,bcegb | depart,arrive |
+           | c,e       | bcegb,bcegb | depart,arrive |
+           | c,g       | bcegb,bcegb | depart,arrive |
+           | c,b       | bcegb,bcegb | depart,arrive |
+           | e,g       | bcegb,bcegb | depart,arrive |
+           | e,b       | bcegb,bcegb | depart,arrive |
+           | e,c       | bcegb,bcegb | depart,arrive |
+           | g,b       | bcegb,bcegb | depart,arrive |
+           | g,c       | bcegb,bcegb | depart,arrive |
+           | g,e       | bcegb,bcegb | depart,arrive |
+
+     #needs to be adjusted when name-discovery works for entrys
+     Scenario: Mixed Entry and Exit
+        Given the node map
+           |   | c |   | a |   |
+           | j |   | b |   | f |
+           |   | k |   | e |   |
+           | l |   | h |   | d |
+           |   | g |   | i |   |
+
+        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,rotary-exit-1,arrive |
+           | a,l       | abc,jkl,jkl | depart,bkheb-exit-2,arrive  |
+           | a,i       | abc,ghi,ghi | depart,bkheb-exit-3,arrive  |
+           | a,f       | abc,def,def | depart,bkheb-exit-4,arrive  |
+           | d,f       | def,def,def | depart,rotary-exit-1,arrive |
+           | d,c       | def,abc,abc | depart,bkheb-exit-2,arrive  |
+           | d,l       | def,jkl,jkl | depart,bkheb-exit-3,arrive  |
+           | d,i       | def,ghi,ghi | depart,bkheb-exit-4,arrive  |
+           | g,i       | ghi,ghi,ghi | depart,rotary-exit-1,arrive |
+           | g,f       | ghi,def,def | depart,bkheb-exit-2,arrive  |
+           | g,c       | ghi,abc,abc | depart,bkheb-exit-3,arrive  |
+           | g,l       | ghi,jkl,jkl | depart,bkheb-exit-4,arrive  |
+           | j,l       | jkl,jkl,jkl | depart,rotary-exit-1,arrive |
+           | j,i       | jkl,ghi,ghi | depart,bkheb-exit-2,arrive  |
+           | j,f       | jkl,def,def | depart,bkheb-exit-3,arrive  |
+           | j,c       | jkl,abc,abc | depart,bkheb-exit-4,arrive  |
diff --git a/features/guidance/rotary.feature b/features/guidance/rotary.feature
new file mode 100644
index 0000000..e1b14d3
--- /dev/null
+++ b/features/guidance/rotary.feature
@@ -0,0 +1,262 @@
+ at routing  @guidance
+Feature: Rotary
+
+    Background:
+        Given the profile "car"
+        Given a grid size of 30 meters
+
+    Scenario: Enter and Exit
+        Given the node map
+            |   |   | a |   |   |
+            |   |   | b |   |   |
+            | h | g |   | c | d |
+            |   |   | e |   |   |
+            |   |   | f |   |   |
+
+       And the ways
+            | nodes  | junction   |
+            | ab     |            |
+            | cd     |            |
+            | ef     |            |
+            | gh     |            |
+            | bgecb  | roundabout |
+
+       When I route I should get
+           | waypoints | route    | turns                      |
+           | a,d       | ab,cd,cd | depart,bgecb-exit-3,arrive |
+           | a,f       | ab,ef,ef | depart,bgecb-exit-2,arrive |
+           | a,h       | ab,gh,gh | depart,bgecb-exit-1,arrive |
+           | d,f       | cd,ef,ef | depart,bgecb-exit-3,arrive |
+           | d,h       | cd,gh,gh | depart,bgecb-exit-2,arrive |
+           | d,a       | cd,ab,ab | depart,bgecb-exit-1,arrive |
+           | f,h       | ef,gh,gh | depart,bgecb-exit-3,arrive |
+           | f,a       | ef,ab,ab | depart,bgecb-exit-2,arrive |
+           | f,d       | ef,cd,cd | depart,bgecb-exit-1,arrive |
+           | h,a       | gh,ab,ab | depart,bgecb-exit-3,arrive |
+           | h,d       | gh,cd,cd | depart,bgecb-exit-2,arrive |
+           | h,f       | gh,ef,ef | depart,bgecb-exit-1,arrive |
+
+    Scenario: Only Enter
+        Given the node map
+            |   |   | a |   |   |
+            |   |   | b |   |   |
+            | d | c |   | g | h |
+            |   |   | e |   |   |
+            |   |   | f |   |   |
+
+       And the ways
+            | nodes  | junction   |
+            | ab     |            |
+            | cd     |            |
+            | ef     |            |
+            | gh     |            |
+            | bcegb  | roundabout |
+
+       When I route I should get
+           | waypoints | route          | turns                              |
+           | a,c       | ab,bcegb,bcegb | depart,bcegb-exit-undefined,arrive |
+           | a,e       | ab,bcegb,bcegb | depart,bcegb-exit-undefined,arrive |
+           | a,g       | ab,bcegb,bcegb | depart,bcegb-exit-undefined,arrive |
+           | d,e       | cd,bcegb,bcegb | depart,bcegb-exit-undefined,arrive |
+           | d,g       | cd,bcegb,bcegb | depart,bcegb-exit-undefined,arrive |
+           | d,b       | cd,bcegb,bcegb | depart,bcegb-exit-undefined,arrive |
+           | f,g       | ef,bcegb,bcegb | depart,bcegb-exit-undefined,arrive |
+           | f,b       | ef,bcegb,bcegb | depart,bcegb-exit-undefined,arrive |
+           | f,c       | ef,bcegb,bcegb | depart,bcegb-exit-undefined,arrive |
+           | h,b       | gh,bcegb,bcegb | depart,bcegb-exit-undefined,arrive |
+           | h,c       | gh,bcegb,bcegb | depart,bcegb-exit-undefined,arrive |
+           | h,e       | gh,bcegb,bcegb | depart,bcegb-exit-undefined,arrive |
+
+    Scenario: Only Exit
+        Given the node map
+            |   |   | a |   |   |
+            |   |   | b |   |   |
+            | d | c |   | g | h |
+            |   |   | e |   |   |
+            |   |   | f |   |   |
+
+       And the ways
+            | nodes  | junction   |
+            | ab     |            |
+            | cd     |            |
+            | ef     |            |
+            | gh     |            |
+            | bcegb  | roundabout |
+
+       When I route I should get
+           | waypoints | route       | turns                      |
+           | b,d       | bcegb,cd,cd | depart,bcegb-exit-1,arrive |
+           | b,f       | bcegb,ef,ef | depart,bcegb-exit-2,arrive |
+           | b,h       | bcegb,gh,gh | depart,bcegb-exit-3,arrive |
+           | c,f       | bcegb,ef,ef | depart,bcegb-exit-1,arrive |
+           | c,h       | bcegb,gh,gh | depart,bcegb-exit-2,arrive |
+           | c,a       | bcegb,ab,ab | depart,bcegb-exit-3,arrive |
+           | e,h       | bcegb,gh,gh | depart,bcegb-exit-1,arrive |
+           | e,a       | bcegb,ab,ab | depart,bcegb-exit-2,arrive |
+           | e,d       | bcegb,cd,cd | depart,bcegb-exit-3,arrive |
+           | g,a       | bcegb,ab,ab | depart,bcegb-exit-1,arrive |
+           | g,d       | bcegb,cd,cd | depart,bcegb-exit-2,arrive |
+           | g,f       | bcegb,ef,ef | depart,bcegb-exit-3,arrive |
+      #phantom node snapping can result in a full round-trip here, therefore we cannot test b->a and the other direct exits
+
+    Scenario: Drive Around
+        Given the node map
+            |   |   | a |   |   |
+            |   |   | b |   |   |
+            | d | c |   | g | h |
+            |   |   | e |   |   |
+            |   |   | f |   |   |
+
+       And the ways
+            | nodes  | junction   |
+            | ab     |            |
+            | cd     |            |
+            | ef     |            |
+            | gh     |            |
+            | bcegb  | roundabout |
+
+       When I route I should get
+           | waypoints | route       | turns         |
+           | b,c       | bcegb,bcegb | depart,arrive |
+           | b,e       | bcegb,bcegb | depart,arrive |
+           | b,g       | bcegb,bcegb | depart,arrive |
+           | c,e       | bcegb,bcegb | depart,arrive |
+           | c,g       | bcegb,bcegb | depart,arrive |
+           | c,b       | bcegb,bcegb | depart,arrive |
+           | e,g       | bcegb,bcegb | depart,arrive |
+           | e,b       | bcegb,bcegb | depart,arrive |
+           | e,c       | bcegb,bcegb | depart,arrive |
+           | g,b       | bcegb,bcegb | depart,arrive |
+           | g,c       | bcegb,bcegb | depart,arrive |
+           | g,e       | bcegb,bcegb | depart,arrive |
+
+     #needs to be adjusted when name-discovery works for entrys
+     Scenario: Mixed Entry and Exit
+        Given the node map
+           |   | c |   | a |   |
+           | j |   | b |   | f |
+           |   | k |   | e |   |
+           | l |   | h |   | d |
+           |   | g |   | i |   |
+
+        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,rotary-exit-1,arrive |
+           | a,l       | abc,jkl,jkl | depart,bkheb-exit-2,arrive  |
+           | a,i       | abc,ghi,ghi | depart,bkheb-exit-3,arrive  |
+           | a,f       | abc,def,def | depart,bkheb-exit-4,arrive  |
+           | d,f       | def,def,def | depart,rotary-exit-1,arrive |
+           | d,c       | def,abc,abc | depart,bkheb-exit-2,arrive  |
+           | d,l       | def,jkl,jkl | depart,bkheb-exit-3,arrive  |
+           | d,i       | def,ghi,ghi | depart,bkheb-exit-4,arrive  |
+           | g,i       | ghi,ghi,ghi | depart,rotary-exit-1,arrive |
+           | g,f       | ghi,def,def | depart,bkheb-exit-2,arrive  |
+           | g,c       | ghi,abc,abc | depart,bkheb-exit-3,arrive  |
+           | g,l       | ghi,jkl,jkl | depart,bkheb-exit-4,arrive  |
+           | j,l       | jkl,jkl,jkl | depart,rotary-exit-1,arrive |
+           | j,i       | jkl,ghi,ghi | depart,bkheb-exit-2,arrive  |
+           | j,f       | jkl,def,def | depart,bkheb-exit-3,arrive  |
+           | j,c       | jkl,abc,abc | depart,bkheb-exit-4,arrive  |
+
+       Scenario: Collinear in X,Y
+        Given the node map
+            | a |   |   |
+            | b |   |   |
+            | c | d | f |
+            | e |   |   |
+
+        And the ways
+            | nodes | junction   |
+            | ab    |            |
+            | bcdb  | roundabout |
+            | ce    |            |
+            | df    |            |
+
+        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 |
+
+       Scenario: Collinear in X,Y
+        Given the node map
+            | a |   |   |
+            | d |   |   |
+            | b | c | f |
+            | e |   |   |
+
+        And the ways
+            | nodes | junction   |
+            | ab    |            |
+            | bcdb  | roundabout |
+            | ce    |            |
+            | df    |            |
+
+        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 |
+
+       Scenario: Collinear in X,Y
+        Given the node map
+            | a |   |   |
+            | c |   |   |
+            | d | b | f |
+            | e |   |   |
+
+        And the ways
+            | nodes | junction   |
+            | ab    |            |
+            | bcdb  | roundabout |
+            | ce    |            |
+            | df    |            |
+
+        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 |
+
+       Scenario: Collinear in X,Y
+        Given the node map
+            | f |   |   |
+            | d | c | e |
+            |   | b |   |
+            |   | a |   |
+
+        And the ways
+            | nodes | junction   |
+            | ab    |            |
+            | bcdb  | roundabout |
+            | ce    |            |
+            | df    |            |
+
+        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 |
+
+       Scenario: Collinear in X,Y
+        Given the node map
+            | f |   |   |
+            | d | c | e |
+            | b |   |   |
+            | a |   |   |
+
+        And the ways
+            | nodes | junction   |
+            | ab    |            |
+            | bcdb  | roundabout |
+            | ce    |            |
+            | df    |            |
+
+        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 |
diff --git a/features/guidance/roundabout.feature b/features/guidance/roundabout-bike.feature
similarity index 64%
copy from features/guidance/roundabout.feature
copy to features/guidance/roundabout-bike.feature
index 21d6530..3652c15 100644
--- a/features/guidance/roundabout.feature
+++ b/features/guidance/roundabout-bike.feature
@@ -2,7 +2,7 @@
 Feature: Basic Roundabout
 
     Background:
-        Given the profile "testbot"
+        Given the profile "bicycle"
         Given a grid size of 10 meters
 
     Scenario: Enter and Exit
@@ -19,28 +19,28 @@ Feature: Basic Roundabout
             | cd     |            |
             | ef     |            |
             | gh     |            |
-            | bcegb  | roundabout |
+            | bgecb  | roundabout |
 
        When I route I should get
            | waypoints | route    | turns                           |
-           | a,d       | ab,cd,cd | depart,roundabout-exit-1,arrive |
+           | 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-3,arrive |
-           | d,f       | cd,ef,ef | depart,roundabout-exit-1,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-3,arrive |
-           | f,h       | ef,gh,gh | depart,roundabout-exit-1,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-3,arrive |
-           | h,a       | gh,ab,ab | depart,roundabout-exit-1,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-3,arrive |
+           | h,f       | gh,ef,ef | depart,roundabout-exit-1,arrive |
 
     Scenario: Only Enter
         Given the node map
             |   |   | a |   |   |
             |   |   | b |   |   |
-            | h | g |   | c | d |
+            | d | c |   | g | h |
             |   |   | e |   |   |
             |   |   | f |   |   |
 
@@ -53,29 +53,25 @@ Feature: Basic Roundabout
             | bcegb  | roundabout |
 
        When I route I should get
-           | waypoints | route    | turns                          |
-           | a,b       | ab,ab    | depart,arrive                  |
-           | a,c       | ab,bcegb | depart,roundabout-enter,arrive |
-           | a,e       | ab,bcegb | depart,roundabout-enter,arrive |
-           | a,g       | ab,bcegb | depart,roundabout-enter,arrive |
-           | d,c       | cd,cd    | depart,arrive                  |
-           | d,e       | cd,bcegb | depart,roundabout-enter,arrive |
-           | d,g       | cd,bcegb | depart,roundabout-enter,arrive |
-           | d,b       | cd,bcegb | depart,roundabout-enter,arrive |
-           | f,e       | ef,ef    | depart,arrive                  |
-           | f,g       | ef,bcegb | depart,roundabout-enter,arrive |
-           | f,b       | ef,bcegb | depart,roundabout-enter,arrive |
-           | f,c       | ef,bcegb | depart,roundabout-enter,arrive |
-           | h,g       | gh,gh    | depart,arrive                  |
-           | h,b       | gh,bcegb | depart,roundabout-enter,arrive |
-           | h,c       | gh,bcegb | depart,roundabout-enter,arrive |
-           | h,e       | gh,bcegb | depart,roundabout-enter,arrive |
+           | waypoints | route          | turns                                   |
+           | a,c       | ab,bcegb,bcegb | depart,roundabout-exit-undefined,arrive |
+           | a,e       | ab,bcegb,bcegb | depart,roundabout-exit-undefined,arrive |
+           | a,g       | ab,bcegb,bcegb | depart,roundabout-exit-undefined,arrive |
+           | d,e       | cd,bcegb,bcegb | depart,roundabout-exit-undefined,arrive |
+           | d,g       | cd,bcegb,bcegb | depart,roundabout-exit-undefined,arrive |
+           | d,b       | cd,bcegb,bcegb | depart,roundabout-exit-undefined,arrive |
+           | f,g       | ef,bcegb,bcegb | depart,roundabout-exit-undefined,arrive |
+           | f,b       | ef,bcegb,bcegb | depart,roundabout-exit-undefined,arrive |
+           | f,c       | ef,bcegb,bcegb | depart,roundabout-exit-undefined,arrive |
+           | h,b       | gh,bcegb,bcegb | depart,roundabout-exit-undefined,arrive |
+           | h,c       | gh,bcegb,bcegb | depart,roundabout-exit-undefined,arrive |
+           | h,e       | gh,bcegb,bcegb | depart,roundabout-exit-undefined,arrive |
 
     Scenario: Only Exit
         Given the node map
             |   |   | a |   |   |
             |   |   | b |   |   |
-            | h | g |   | c | d |
+            | d | c |   | g | h |
             |   |   | e |   |   |
             |   |   | f |   |   |
 
@@ -89,28 +85,25 @@ Feature: Basic Roundabout
 
        When I route I should get
            | waypoints | route       | turns                           |
-           | b,a       | ab,ab       | depart,arrive                   |
            | b,d       | bcegb,cd,cd | depart,roundabout-exit-1,arrive |
            | b,f       | bcegb,ef,ef | depart,roundabout-exit-2,arrive |
            | b,h       | bcegb,gh,gh | depart,roundabout-exit-3,arrive |
-           | c,d       | cd,cd       | depart,arrive                   |
            | c,f       | bcegb,ef,ef | depart,roundabout-exit-1,arrive |
            | c,h       | bcegb,gh,gh | depart,roundabout-exit-2,arrive |
            | c,a       | bcegb,ab,ab | depart,roundabout-exit-3,arrive |
-           | e,f       | ef,ef       | depart,arrive                   |
            | e,h       | bcegb,gh,gh | depart,roundabout-exit-1,arrive |
            | e,a       | bcegb,ab,ab | depart,roundabout-exit-2,arrive |
            | e,d       | bcegb,cd,cd | depart,roundabout-exit-3,arrive |
-           | g,h       | gh,gh       | depart,arrive                   |
            | g,a       | bcegb,ab,ab | depart,roundabout-exit-1,arrive |
            | g,d       | bcegb,cd,cd | depart,roundabout-exit-2,arrive |
            | g,f       | bcegb,ef,ef | depart,roundabout-exit-3,arrive |
+      #phantom node snapping can result in a full round-trip here, therefore we cannot test b->a and the other direct exits
 
     Scenario: Drive Around
         Given the node map
             |   |   | a |   |   |
             |   |   | b |   |   |
-            | h | g |   | c | d |
+            | d | c |   | g | h |
             |   |   | e |   |   |
             |   |   | f |   |   |
 
@@ -139,11 +132,11 @@ Feature: Basic Roundabout
 
      Scenario: Mixed Entry and Exit
         Given the node map
-           |   | a |   | c |   |
-           | l |   | b |   | d |
+           |   | c |   | a |   |
+           | j |   | b |   | f |
            |   | k |   | e |   |
-           | j |   | h |   | f |
-           |   | i |   | g |   |
+           | l |   | h |   | d |
+           |   | g |   | i |   |
 
         And the ways
            | nodes | junction   | oneway |
@@ -151,23 +144,23 @@ Feature: Basic Roundabout
            | def   |            | yes    |
            | ghi   |            | yes    |
            | jkl   |            | yes    |
-           | behkb | roundabout | yes    |
+           | bkheb | roundabout | yes    |
 
         When I route I should get
            | waypoints | route       | turns                           |
            | a,c       | abc,abc,abc | depart,roundabout-exit-1,arrive |
-           | a,f       | abc,def,def | depart,roundabout-exit-2,arrive |
+           | a,l       | abc,jkl,jkl | depart,roundabout-exit-2,arrive |
            | a,i       | abc,ghi,ghi | depart,roundabout-exit-3,arrive |
-           | a,l       | abc,jkl,jkl | depart,roundabout-exit-4,arrive |
+           | a,f       | abc,def,def | depart,roundabout-exit-4,arrive |
            | d,f       | def,def,def | depart,roundabout-exit-1,arrive |
-           | d,i       | def,ghi,ghi | depart,roundabout-exit-2,arrive |
+           | d,c       | def,abc,abc | depart,roundabout-exit-2,arrive |
            | d,l       | def,jkl,jkl | depart,roundabout-exit-3,arrive |
-           | d,c       | def,abc,abc | depart,roundabout-exit-4,arrive |
+           | d,i       | def,ghi,ghi | depart,roundabout-exit-4,arrive |
            | g,i       | ghi,ghi,ghi | depart,roundabout-exit-1,arrive |
-           | g,l       | ghi,jkl,jkl | depart,roundabout-exit-2,arrive |
+           | g,f       | ghi,def,def | depart,roundabout-exit-2,arrive |
            | g,c       | ghi,abc,abc | depart,roundabout-exit-3,arrive |
-           | g,f       | ghi,edf,edf | depart,roundabout-exit-4,arrive |
+           | g,l       | ghi,jkl,jkl | depart,roundabout-exit-4,arrive |
            | j,l       | jkl,jkl,jkl | depart,roundabout-exit-1,arrive |
-           | j,c       | jkl,abc,abc | depart,roundabout-exit-2,arrive |
+           | j,i       | jkl,ghi,ghi | depart,roundabout-exit-2,arrive |
            | j,f       | jkl,def,def | depart,roundabout-exit-3,arrive |
-           | j,i       | jkl,ghi,ghi | depart,roundabout-exit-4,arrive |
+           | j,c       | jkl,abc,abc | depart,roundabout-exit-4,arrive |
diff --git a/features/guidance/roundabout.feature b/features/guidance/roundabout.feature
index 21d6530..d03737c 100644
--- a/features/guidance/roundabout.feature
+++ b/features/guidance/roundabout.feature
@@ -2,7 +2,7 @@
 Feature: Basic Roundabout
 
     Background:
-        Given the profile "testbot"
+        Given the profile "car"
         Given a grid size of 10 meters
 
     Scenario: Enter and Exit
@@ -19,28 +19,28 @@ Feature: Basic Roundabout
             | cd     |            |
             | ef     |            |
             | gh     |            |
-            | bcegb  | roundabout |
+            | bgecb  | roundabout |
 
        When I route I should get
            | waypoints | route    | turns                           |
-           | a,d       | ab,cd,cd | depart,roundabout-exit-1,arrive |
+           | 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-3,arrive |
-           | d,f       | cd,ef,ef | depart,roundabout-exit-1,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-3,arrive |
-           | f,h       | ef,gh,gh | depart,roundabout-exit-1,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-3,arrive |
-           | h,a       | gh,ab,ab | depart,roundabout-exit-1,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-3,arrive |
+           | h,f       | gh,ef,ef | depart,roundabout-exit-1,arrive |
 
     Scenario: Only Enter
         Given the node map
             |   |   | a |   |   |
             |   |   | b |   |   |
-            | h | g |   | c | d |
+            | d | c |   | g | h |
             |   |   | e |   |   |
             |   |   | f |   |   |
 
@@ -53,29 +53,25 @@ Feature: Basic Roundabout
             | bcegb  | roundabout |
 
        When I route I should get
-           | waypoints | route    | turns                          |
-           | a,b       | ab,ab    | depart,arrive                  |
-           | a,c       | ab,bcegb | depart,roundabout-enter,arrive |
-           | a,e       | ab,bcegb | depart,roundabout-enter,arrive |
-           | a,g       | ab,bcegb | depart,roundabout-enter,arrive |
-           | d,c       | cd,cd    | depart,arrive                  |
-           | d,e       | cd,bcegb | depart,roundabout-enter,arrive |
-           | d,g       | cd,bcegb | depart,roundabout-enter,arrive |
-           | d,b       | cd,bcegb | depart,roundabout-enter,arrive |
-           | f,e       | ef,ef    | depart,arrive                  |
-           | f,g       | ef,bcegb | depart,roundabout-enter,arrive |
-           | f,b       | ef,bcegb | depart,roundabout-enter,arrive |
-           | f,c       | ef,bcegb | depart,roundabout-enter,arrive |
-           | h,g       | gh,gh    | depart,arrive                  |
-           | h,b       | gh,bcegb | depart,roundabout-enter,arrive |
-           | h,c       | gh,bcegb | depart,roundabout-enter,arrive |
-           | h,e       | gh,bcegb | depart,roundabout-enter,arrive |
+           | waypoints | route          | turns                                   |
+           | a,c       | ab,bcegb,bcegb | depart,roundabout-exit-undefined,arrive |
+           | a,e       | ab,bcegb,bcegb | depart,roundabout-exit-undefined,arrive |
+           | a,g       | ab,bcegb,bcegb | depart,roundabout-exit-undefined,arrive |
+           | d,e       | cd,bcegb,bcegb | depart,roundabout-exit-undefined,arrive |
+           | d,g       | cd,bcegb,bcegb | depart,roundabout-exit-undefined,arrive |
+           | d,b       | cd,bcegb,bcegb | depart,roundabout-exit-undefined,arrive |
+           | f,g       | ef,bcegb,bcegb | depart,roundabout-exit-undefined,arrive |
+           | f,b       | ef,bcegb,bcegb | depart,roundabout-exit-undefined,arrive |
+           | f,c       | ef,bcegb,bcegb | depart,roundabout-exit-undefined,arrive |
+           | h,b       | gh,bcegb,bcegb | depart,roundabout-exit-undefined,arrive |
+           | h,c       | gh,bcegb,bcegb | depart,roundabout-exit-undefined,arrive |
+           | h,e       | gh,bcegb,bcegb | depart,roundabout-exit-undefined,arrive |
 
     Scenario: Only Exit
         Given the node map
             |   |   | a |   |   |
             |   |   | b |   |   |
-            | h | g |   | c | d |
+            | d | c |   | g | h |
             |   |   | e |   |   |
             |   |   | f |   |   |
 
@@ -89,28 +85,25 @@ Feature: Basic Roundabout
 
        When I route I should get
            | waypoints | route       | turns                           |
-           | b,a       | ab,ab       | depart,arrive                   |
            | b,d       | bcegb,cd,cd | depart,roundabout-exit-1,arrive |
            | b,f       | bcegb,ef,ef | depart,roundabout-exit-2,arrive |
            | b,h       | bcegb,gh,gh | depart,roundabout-exit-3,arrive |
-           | c,d       | cd,cd       | depart,arrive                   |
            | c,f       | bcegb,ef,ef | depart,roundabout-exit-1,arrive |
            | c,h       | bcegb,gh,gh | depart,roundabout-exit-2,arrive |
            | c,a       | bcegb,ab,ab | depart,roundabout-exit-3,arrive |
-           | e,f       | ef,ef       | depart,arrive                   |
            | e,h       | bcegb,gh,gh | depart,roundabout-exit-1,arrive |
            | e,a       | bcegb,ab,ab | depart,roundabout-exit-2,arrive |
            | e,d       | bcegb,cd,cd | depart,roundabout-exit-3,arrive |
-           | g,h       | gh,gh       | depart,arrive                   |
            | g,a       | bcegb,ab,ab | depart,roundabout-exit-1,arrive |
            | g,d       | bcegb,cd,cd | depart,roundabout-exit-2,arrive |
            | g,f       | bcegb,ef,ef | depart,roundabout-exit-3,arrive |
+      #phantom node snapping can result in a full round-trip here, therefore we cannot test b->a and the other direct exits
 
     Scenario: Drive Around
         Given the node map
             |   |   | a |   |   |
             |   |   | b |   |   |
-            | h | g |   | c | d |
+            | d | c |   | g | h |
             |   |   | e |   |   |
             |   |   | f |   |   |
 
@@ -139,11 +132,11 @@ Feature: Basic Roundabout
 
      Scenario: Mixed Entry and Exit
         Given the node map
-           |   | a |   | c |   |
-           | l |   | b |   | d |
+           |   | c |   | a |   |
+           | j |   | b |   | f |
            |   | k |   | e |   |
-           | j |   | h |   | f |
-           |   | i |   | g |   |
+           | l |   | h |   | d |
+           |   | g |   | i |   |
 
         And the ways
            | nodes | junction   | oneway |
@@ -151,23 +144,118 @@ Feature: Basic Roundabout
            | def   |            | yes    |
            | ghi   |            | yes    |
            | jkl   |            | yes    |
-           | behkb | roundabout | yes    |
+           | bkheb | roundabout | yes    |
 
         When I route I should get
            | waypoints | route       | turns                           |
            | a,c       | abc,abc,abc | depart,roundabout-exit-1,arrive |
-           | a,f       | abc,def,def | depart,roundabout-exit-2,arrive |
+           | a,l       | abc,jkl,jkl | depart,roundabout-exit-2,arrive |
            | a,i       | abc,ghi,ghi | depart,roundabout-exit-3,arrive |
-           | a,l       | abc,jkl,jkl | depart,roundabout-exit-4,arrive |
+           | a,f       | abc,def,def | depart,roundabout-exit-4,arrive |
            | d,f       | def,def,def | depart,roundabout-exit-1,arrive |
-           | d,i       | def,ghi,ghi | depart,roundabout-exit-2,arrive |
+           | d,c       | def,abc,abc | depart,roundabout-exit-2,arrive |
            | d,l       | def,jkl,jkl | depart,roundabout-exit-3,arrive |
-           | d,c       | def,abc,abc | depart,roundabout-exit-4,arrive |
+           | d,i       | def,ghi,ghi | depart,roundabout-exit-4,arrive |
            | g,i       | ghi,ghi,ghi | depart,roundabout-exit-1,arrive |
-           | g,l       | ghi,jkl,jkl | depart,roundabout-exit-2,arrive |
+           | g,f       | ghi,def,def | depart,roundabout-exit-2,arrive |
            | g,c       | ghi,abc,abc | depart,roundabout-exit-3,arrive |
-           | g,f       | ghi,edf,edf | depart,roundabout-exit-4,arrive |
+           | g,l       | ghi,jkl,jkl | depart,roundabout-exit-4,arrive |
            | j,l       | jkl,jkl,jkl | depart,roundabout-exit-1,arrive |
-           | j,c       | jkl,abc,abc | depart,roundabout-exit-2,arrive |
+           | j,i       | jkl,ghi,ghi | depart,roundabout-exit-2,arrive |
            | j,f       | jkl,def,def | depart,roundabout-exit-3,arrive |
-           | j,i       | jkl,ghi,ghi | depart,roundabout-exit-4,arrive |
+           | j,c       | jkl,abc,abc | depart,roundabout-exit-4,arrive |
+
+       Scenario: Collinear in X
+        Given the node map
+            | a | b | c | d | f |
+            |   |   | e |   |   |
+
+        And the ways
+            | nodes | junction   |
+            | ab    |            |
+            | bcdb  | roundabout |
+            | ce    |            |
+            | 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 |
+
+       Scenario: Collinear in Y
+        Given the node map
+            | a |   |
+            | b |   |
+            | c | e |
+            | d |   |
+            | f |   |
+
+        And the ways
+            | nodes | junction   |
+            | ab    |            |
+            | bcdb  | roundabout |
+            | ce    |            |
+            | 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 |
+
+       Scenario: Collinear in X,Y
+        Given the node map
+            | a |   |   |
+            | b |   |   |
+            | c | d | f |
+            | e |   |   |
+
+        And the ways
+            | nodes | junction   |
+            | ab    |            |
+            | bcdb  | roundabout |
+            | ce    |            |
+            | 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 |
+
+       Scenario: Collinear in X,Y
+        Given the node map
+            | a |   |   |
+            | d |   |   |
+            | b | c | f |
+            | e |   |   |
+
+        And the ways
+            | nodes | junction   |
+            | ab    |            |
+            | bcdb  | roundabout |
+            | ce    |            |
+            | 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 |
+
+       Scenario: Collinear in X,Y
+        Given the node map
+            | a |   |   |
+            | c |   |   |
+            | d | b | f |
+            | e |   |   |
+
+        And the ways
+            | nodes | junction   |
+            | ab    |            |
+            | bcdb  | roundabout |
+            | ce    |            |
+            | 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 |
+
diff --git a/features/guidance/suppressed.feature b/features/guidance/suppressed.feature
new file mode 100644
index 0000000..89b5865
--- /dev/null
+++ b/features/guidance/suppressed.feature
@@ -0,0 +1,66 @@
+ at routing  @guidance
+Feature: Suppressed Turns
+
+    Background:
+        Given the profile "car"
+        Given a grid size of 10 meters
+
+    Scenario: Do not announce passing a exit ramp
+        Given the node map
+            | a | b | c | d | e |
+            |   |   |   | f | g |
+
+        And the ways
+            | nodes  | highway       |
+            | abcde  | motorway      |
+            | bfg    | motorway_link |
+
+       When I route I should get
+            | waypoints | route         | turns         |
+            | a,e       | abcde,abcde   | depart,arrive |
+
+    Scenario: Do not announce reference changes
+        Given the node map
+            | a | b | c | d | e | f |
+
+        And the ways
+            | nodes | highway  | name     | ref   |
+            | ab    | motorway | highway  | A1    |
+            | bc    | motorway | highway  | A1,A2 |
+            | cd    | motorway | highway  | A2    |
+            | de    | motorway | highway  |       |
+            | ef    | motorway | highway  | A1    |
+
+        When I route I should get
+            | waypoints | route                     | turns         |
+            | a,f       | highway (A1),highway (A1) | depart,arrive |
+
+
+    Scenario: Don't Announce Turn on following major road class -- service
+        Given the node map
+            | a | b | d |
+            |   |   | c |
+
+        And the ways
+            | nodes | highway |
+            | abc   | primary |
+            | bd    | service |
+
+        When I route I should get
+            | waypoints | route   | turns         |
+            | a,c       | abc,abc | depart,arrive |
+
+    Scenario: Don't Announce Turn on following major road class -- residential
+        Given the node map
+            | a | b | d |
+            |   |   | c |
+
+        And the ways
+            | nodes | highway     |
+            | abc   | primary     |
+            | bd    | residential |
+
+        When I route I should get
+            | waypoints | route     | turns                       |
+            | a,c       | abc,abc   | depart,arrive               |
+            | a,d       | abc,bd,bd | depart,turn straight,arrive |
diff --git a/features/guidance/turn.feature b/features/guidance/turn.feature
new file mode 100644
index 0000000..3eb5fc3
--- /dev/null
+++ b/features/guidance/turn.feature
@@ -0,0 +1,284 @@
+ at routing  @guidance
+Feature: Simple Turns
+
+    Background:
+        Given the profile "car"
+        Given a grid size of 10 meters
+
+    Scenario: Four Way Intersection
+        Given the node map
+            |   | c |   |
+            | a | b | e |
+            |   | d |   |
+
+        And the ways
+            | nodes  | highway |
+            | ab     | primary |
+            | cb     | primary |
+            | db     | primary |
+            | eb     | primary |
+
+       When I route I should get
+            | waypoints | route    | turns                           |
+            | a,c       | ab,cb,cb | depart,turn left,arrive         |
+            | a,e       | ab,eb,eb | depart,new name straight,arrive |
+            | a,d       | ab,db,db | depart,turn right,arrive        |
+            | c,a       | cb,ab,ab | depart,turn right,arrive        |
+            | c,d       | cb,db,db | depart,new name straight,arrive |
+            | c,e       | cb,eb,eb | depart,turn left,arrive         |
+            | d,a       | db,ab,ab | depart,turn left,arrive         |
+            | d,c       | db,cb,cb | depart,new name straight,arrive |
+            | d,e       | db,eb,eb | depart,turn right,arrive        |
+            | e,a       | eb,ab,ab | depart,new name straight,arrive |
+            | e,c       | eb,cb,cb | depart,turn right,arrive        |
+            | e,d       | eb,db,db | depart,turn left,arrive         |
+
+    Scenario: Rotated Four Way Intersection
+        Given the node map
+            | a |   | c |
+            |   | b |   |
+            | d |   | e |
+
+        And the ways
+            | nodes  | highway |
+            | ab     | primary |
+            | cb     | primary |
+            | db     | primary |
+            | eb     | primary |
+
+       When I route I should get
+            | waypoints | route    | turns                           |
+            | a,c       | ab,cb,cb | depart,turn left,arrive         |
+            | a,e       | ab,eb,eb | depart,new name straight,arrive |
+            | a,d       | ab,db,db | depart,turn right,arrive        |
+            | c,a       | cb,ab,ab | depart,turn right,arrive        |
+            | c,d       | cb,db,db | depart,new name straight,arrive |
+            | c,e       | cb,eb,eb | depart,turn left,arrive         |
+            | d,a       | db,ab,ab | depart,turn left,arrive         |
+            | d,c       | db,cb,cb | depart,new name straight,arrive |
+            | d,e       | db,eb,eb | depart,turn right,arrive        |
+            | e,a       | eb,ab,ab | depart,new name straight,arrive |
+            | e,c       | eb,cb,cb | depart,turn right,arrive        |
+            | e,d       | eb,db,db | depart,turn left,arrive         |
+
+
+    Scenario: Four Way Intersection Through Street
+        Given the node map
+            |   | c |   |
+            | a | b | e |
+            |   | d |   |
+
+        And the ways
+            | nodes  | highway |
+            | abe    | primary |
+            | cb     | primary |
+            | db     | primary |
+
+       When I route I should get
+            | waypoints | route      | turns                           |
+            | a,c       | abe,cb,cb  | depart,turn left,arrive         |
+            | a,e       | abe,abe    | depart,arrive                   |
+            | a,d       | abe,db,db  | depart,turn right,arrive        |
+            | c,a       | cb,abe,abe | depart,turn right,arrive        |
+            | c,d       | cb,db,db   | depart,new name straight,arrive |
+            | c,e       | cb,abe,abe | depart,turn left,arrive         |
+            | d,a       | db,abe,abe | depart,turn left,arrive         |
+            | d,c       | db,cb,cb   | depart,new name straight,arrive |
+            | d,e       | db,abe,abe | depart,turn right,arrive        |
+            | e,a       | abe,abe    | depart,arrive                   |
+            | e,c       | abe,cb,cb  | depart,turn right,arrive        |
+            | e,d       | abe,db,db  | depart,turn left,arrive         |
+
+    Scenario: Four Way Intersection Double Through Street
+        Given the node map
+            |   | c |   |
+            | a | b | e |
+            |   | d |   |
+
+        And the ways
+            | nodes  | highway |
+            | abe    | primary |
+            | cbd    | primary |
+
+       When I route I should get
+            | waypoints | route       | turns                    |
+            | a,c       | abe,cbd,cbd | depart,turn left,arrive  |
+            | a,e       | abe,abe     | depart,arrive            |
+            | a,d       | abe,cbd,cbd | depart,turn right,arrive |
+            | c,a       | cbd,abe,abe | depart,turn right,arrive |
+            | c,d       | cbd,cbd     | depart,arrive            |
+            | c,e       | cbd,abe,abe | depart,turn left,arrive  |
+            | d,a       | cbd,abe,abe | depart,turn left,arrive  |
+            | d,c       | cbd,cbd     | depart,arrive            |
+            | d,e       | cbd,abe,abe | depart,turn right,arrive |
+            | e,a       | abe,abe     | depart,arrive            |
+            | e,c       | abe,cbd,cbd | depart,turn right,arrive |
+            | e,d       | abe,cbd,cbd | depart,turn left,arrive  |
+
+    Scenario: Three Way Intersection
+        Given the node map
+            |   | c |   |
+            | a | b | d |
+
+        And the ways
+            | nodes  | highway |
+            | ab     | primary |
+            | cb     | primary |
+            | db     | primary |
+
+       When I route I should get
+            | waypoints | route    | turns                           |
+            | a,c       | ab,cb,cb | depart,turn left,arrive         |
+            | a,d       | ab,db,db | depart,new name straight,arrive |
+            | d,c       | db,cb,cb | depart,turn right,arrive        |
+            | d,a       | db,ab,ab | depart,new name straight,arrive |
+
+    Scenario: Three Way Intersection on Through Street
+        Given the node map
+            |   | d |   |
+            | a | b | c |
+
+        And the ways
+            | nodes  | highway |
+            | abc    | primary |
+            | db     | primary |
+
+       When I route I should get
+            | waypoints | route     | turns                    |
+            | a,c       | abc,abc   | depart,arrive            |
+            | a,d       | abc,db,db | depart,turn left,arrive  |
+            | c,a       | abc,abc   | depart,arrive            |
+            | c,d       | abc,db,db | depart,turn right,arrive |
+
+     Scenario: High Degree Intersection
+        Given the node map
+            | i |   | b |   | c |
+            |   |   |   |   |   |
+            |   |   |   |   |   |
+            | h |   | a |   | d |
+            |   |   |   |   |   |
+            |   |   |   |   |   |
+            | g |   | f |   | e |
+
+        And the ways
+            | nodes | highway |
+            | ab    | primary |
+            | ac    | primary |
+            | ad    | primary |
+            | ae    | primary |
+            | af    | primary |
+            | ag    | primary |
+            | ah    | primary |
+            | ai    | primary |
+
+        When I route I should get
+            | waypoints | route    | turns                           |
+            | b,c       | ab,ac,ac | depart,turn sharp left,arrive   |
+            | b,d       | ab,ad,ad | depart,turn left,arrive         |
+            | b,e       | ab,ae,ae | depart,turn slight left,arrive  |
+            | b,f       | ab,af,af | depart,new name straight,arrive |
+            | b,g       | ab,ag,ag | depart,turn slight right,arrive |
+            | b,h       | ab,ah,ah | depart,turn right,arrive        |
+            | b,i       | ab,ai,ai | depart,turn sharp right,arrive  |
+
+    Scenario: Disturbed High Degree Intersection
+        Given the node map
+            |   |   | b |   |   |
+            | i |   |   |   | c |
+            |   |   |   |   |   |
+            | h |   | a |   | d |
+            |   |   |   |   |   |
+            | g |   |   |   | e |
+            |   |   | f |   |   |
+
+        And the ways
+            | nodes | highway |
+            | ab    | primary |
+            | ac    | primary |
+            | ad    | primary |
+            | ae    | primary |
+            | af    | primary |
+            | ag    | primary |
+            | ah    | primary |
+            | ai    | primary |
+
+        When I route I should get
+            | waypoints | route    | turns                           |
+            | b,c       | ab,ac,ac | depart,turn sharp left,arrive   |
+            | b,d       | ab,ad,ad | depart,turn left,arrive         |
+            | b,e       | ab,ae,ae | depart,turn slight left,arrive  |
+            | b,f       | ab,af,af | depart,new name straight,arrive |
+            | b,g       | ab,ag,ag | depart,turn slight right,arrive |
+            | b,h       | ab,ah,ah | depart,turn right,arrive        |
+            | b,i       | ab,ai,ai | depart,turn sharp right,arrive  |
+
+    Scenario: Turn instructions at high latitude
+        Given the node locations
+            | node | lat       | lon      |
+            | a    | 55.68740  | 12.52430 |
+            | b    | 55.68745  | 12.52409 |
+            | c    | 55.68711  | 12.52383 |
+            | d    | 55.68745  | 12.52450 |
+            | e    | 55.68755  | 12.52450 |
+            | x    | -55.68740 | 12.52430 |
+            | y    | -55.68745 | 12.52409 |
+            | z    | -55.68711 | 12.52383 |
+            | v    | -55.68745 | 12.52450 |
+            | w    | -55.68755 | 12.52450 |
+
+        And the ways
+            | nodes |
+            | ab    |
+            | bc    |
+            | bd    |
+            | be    |
+            | xy    |
+            | yz    |
+            | vy    |
+            | wy    |
+
+        When I route I should get
+            | from | to | route    | turns                    |
+            | a    | c  | ab,bc,bc | depart,turn left,arrive  |
+            | c    | a  | bc,ab,ab | depart,turn right,arrive |
+            | x    | z  | xy,yz,yz | depart,turn right,arrive |
+            | z    | x  | yz,xy,xy | depart,turn left,arrive  |
+
+    Scenario: Four Way Intersection Double Through Street Segregated
+        Given the node map
+            |   | b |   | c |   |
+            | i |   |   |   | d |
+            |   |   | a |   |   |
+            | h |   |   |   | e |
+            |   | g |   | f |   |
+
+        And the ways
+            | nodes  | highway | oneway | name   |
+            | ha     | primary | yes    | first  |
+            | ai     | primary | yes    | first  |
+            | ae     | primary | yes    | first  |
+            | da     | primary | yes    | first  |
+            | ba     | primary | yes    | second |
+            | ac     | primary | yes    | second |
+            | fa     | primary | yes    | second |
+            | ag     | primary | yes    | second |
+
+       When I route I should get
+            | waypoints | route                | turns                        |
+            | f,e       | second,first,first   | depart,turn right,arrive     |
+            | f,c       | second,second        | depart,arrive                |
+            | f,i       | second,first,first   | depart,turn left,arrive      |
+            | f,g       | second,second,second | depart,continue uturn,arrive |
+            | d,c       | first,second,second  | depart,turn right,arrive     |
+            | d,i       | first,first          | depart,arrive                |
+            | d,g       | first,second,second  | depart,turn left,arrive      |
+            | d,e       | first,first,first    | depart,continue uturn,arrive |
+            | b,i       | second,first,first   | depart,turn right,arrive     |
+            | b,g       | second,second        | depart,arrive                |
+            | b,e       | second,first,first   | depart,turn left,arrive      |
+            | b,c       | second,second,second | depart,continue uturn,arrive |
+            | h,g       | first,second,second  | depart,turn right,arrive     |
+            | h,e       | first,first          | depart,arrive                |
+            | h,c       | first,second,second  | depart,turn left,arrive      |
+            | h,i       | first,first,first    | depart,continue uturn,arrive |
+
diff --git a/features/step_definitions/requests.js b/features/step_definitions/requests.js
index dcaf5cc..99adee6 100644
--- a/features/step_definitions/requests.js
+++ b/features/step_definitions/requests.js
@@ -44,12 +44,9 @@ module.exports = function () {
 
     this.Then(/^response should be a well-formed route$/, () => {
         this.ShouldBeWellFormed();
-        assert.equal(typeof this.json.status_message, 'string');
-        assert.equal(typeof this.json.route_summary, 'object');
-        assert.equal(typeof this.json.route_geometry, 'string');
-        assert.ok(Array.isArray(this.json.route_instructions));
-        assert.ok(Array.isArray(this.json.via_points));
-        assert.ok(Array.isArray(this.json.via_indices));
+        assert.equal(this.json.code, 'ok');
+        assert.ok(Array.isArray(this.json.routes));
+        assert.ok(Array.isArray(this.json.waypoints));
     });
 
     this.Then(/^"([^"]*)" should return code (\d+)$/, (binary, code) => {
diff --git a/features/step_definitions/trip.js b/features/step_definitions/trip.js
index b1727da..9662ac6 100644
--- a/features/step_definitions/trip.js
+++ b/features/step_definitions/trip.js
@@ -110,6 +110,7 @@ module.exports = function () {
                 } else {
                     var params = this.queryParams,
                         waypoints = [];
+                    params['steps'] = 'true';
                     if (row.from && row.to) {
                         var fromNode = this.findNodeByName(row.from);
                         if (!fromNode) throw new Error(util.format('*** unknown from-node "%s"', row.from));
diff --git a/features/support/env.js b/features/support/env.js
index cae361c..5a337bc 100644
--- a/features/support/env.js
+++ b/features/support/env.js
@@ -7,7 +7,8 @@ var d3 = require('d3-queue');
 module.exports = function () {
     this.initializeEnv = (callback) => {
         this.DEFAULT_PORT = 5000;
-        this.DEFAULT_TIMEOUT = 2000;
+	// OSX builds on Travis hit a timeout of ~2000 from time to time
+        this.DEFAULT_TIMEOUT = 5000;
         this.setDefaultTimeout(this.DEFAULT_TIMEOUT);
         this.ROOT_FOLDER = process.cwd();
         this.OSM_USER = 'osrm';
diff --git a/features/support/route.js b/features/support/route.js
index 7c208ff..1dbce8c 100644
--- a/features/support/route.js
+++ b/features/support/route.js
@@ -144,9 +144,14 @@ module.exports = function () {
                     return v.maneuver.type;
                 case 'roundabout':
                     return 'roundabout-exit-' + v.maneuver.exit;
+                case 'rotary':
+                    if( 'rotary_name' in v )
+                        return v.rotary_name + '-exit-' + v.maneuver.exit;
+                    else
+                        return 'rotary-exit-' + v.maneuver.exit;
                 // FIXME this is a little bit over-simplistic for merge/fork instructions
                 default:
-                    return v.maneuver.modifier;
+                    return v.maneuver.type + ' ' + v.maneuver.modifier;
                 }
             })
             .join(',');
diff --git a/features/support/shared_steps.js b/features/support/shared_steps.js
index 5751492..b0be24b 100644
--- a/features/support/shared_steps.js
+++ b/features/support/shared_steps.js
@@ -35,7 +35,7 @@ module.exports = function () {
 
                         var json = JSON.parse(body);
 
-                        var hasRoute = json.code === 'ok';
+                        var hasRoute = json.code === 'Ok';
 
                         if (hasRoute) {
                             instructions = this.wayList(json.routes[0]);
@@ -59,14 +59,6 @@ module.exports = function () {
                             got['#'] = row['#'];
                         }
 
-                        if (headers.has('start')) {
-                            got.start = instructions ? json.route_summary.start_point : null;
-                        }
-
-                        if (headers.has('end')) {
-                            got.end = instructions ? json.route_summary.end_point : null;
-                        }
-
                         if (headers.has('geometry')) {
                             got.geometry = json.routes[0].geometry;
                         }
diff --git a/features/testbot/graph.feature b/features/testbot/graph.feature
index f86b994..81741aa 100644
--- a/features/testbot/graph.feature
+++ b/features/testbot/graph.feature
@@ -36,5 +36,5 @@ Feature: Basic Routing
             | fy     | last  |
 
         When I route I should get
-            | from | to | route                 | turns                    |
-            | x    | y  | first,compr,last,last | depart,right,left,arrive |
+            | from | to | route                 |
+            | x    | y  | first,compr,last,last |
diff --git a/features/testbot/matching.feature b/features/testbot/matching.feature
index bee3d9c..71ac077 100644
--- a/features/testbot/matching.feature
+++ b/features/testbot/matching.feature
@@ -85,3 +85,22 @@ Feature: Basic Map Matching
             | trace | matchings |
             | dcba  | hgfe      |
 
+    Scenario: Testbot - Matching with oneway streets
+        Given a grid size of 10 meters
+        Given the node map
+            | a | b | c | d |
+            | e | f | g | h |
+
+        And the ways
+            | nodes | oneway |
+            | ab    | yes    |
+            | bc    | yes    |
+            | cd    | yes    |
+            | hg    | yes    |
+            | gf    | yes    |
+            | fe    | yes    |
+
+        When I match I should get
+            | trace | matchings |
+            | dcba  | hg,gf,fe  |
+            | efgh  | ab,bc,cd  |
diff --git a/features/testbot/overlap.feature b/features/testbot/overlap.feature
index 509867f..c76cff2 100644
--- a/features/testbot/overlap.feature
+++ b/features/testbot/overlap.feature
@@ -1,10 +1,9 @@
- at routing @testbot @overlap
+ at routing @testbot @overlap @todo
 Feature: Testbot - overlapping ways
- 
+
     Background:
         Given the profile "testbot"
 
-    @bug @610
     Scenario: Testbot - multiple way between same nodes 
     Note that cb is connecting the same two nodes as bc
         Given the node map
@@ -18,11 +17,10 @@ Feature: Testbot - overlapping ways
             | cb    | secondary |
 
         When I route I should get
-            | from | to | route    |
-            | a    | d  | ab,bc,cd |
-            | d    | a  | cd,bc,ab |
-    
-    @bug @610
+            | from | to | route       |
+            | a    | d  | ab,bc,cd,cd |
+            | d    | a  | cd,bc,ab,ab |
+
     Scenario: Testbot - area on top of way
         Given the node map
             | x | a | b | y |
@@ -34,6 +32,6 @@ Feature: Testbot - overlapping ways
             | abcda | secondary | yes  |
 
         When I route I should get
-            | from | to | route |
-            | x    | y  | xaby  |
-            | y    | x  | xaby  |
+            | from | to | route      |
+            | x    | y  | xaby,xaby  |
+            | y    | x  | xaby,xaby  |
diff --git a/features/testbot/roundabout.feature b/features/testbot/roundabout.feature
deleted file mode 100644
index 90f2fc0..0000000
--- a/features/testbot/roundabout.feature
+++ /dev/null
@@ -1,76 +0,0 @@
- at routing @testbot @roundabout @instruction
-Feature: Roundabout Instructions
-
-    Background:
-        Given the profile "testbot"
-
-    Scenario: Testbot - Roundabout
-        Given the node map
-            |   |   | v |   |   |
-            |   |   | d |   |   |
-            | s | a |   | c | u |
-            |   |   | b |   |   |
-            |   |   | t |   |   |
-
-        And the ways
-            | nodes | junction   |
-            | sa    |            |
-            | tb    |            |
-            | uc    |            |
-            | vd    |            |
-            | abcda | roundabout |
-
-        When I route I should get
-            | from | to | route    | turns                            |
-            | s    | t  | sa,tb,tb | depart,roundabout-exit-1,arrive |
-            | s    | u  | sa,uc,uc | depart,roundabout-exit-2,arrive |
-            | s    | v  | sa,vd,vd | depart,roundabout-exit-3,arrive |
-            | t    | u  | tb,uc,uc | depart,roundabout-exit-1,arrive |
-            | t    | v  | tb,vd,vd | depart,roundabout-exit-2,arrive |
-            | t    | s  | tb,sa,sa | depart,roundabout-exit-3,arrive |
-            | u    | v  | uc,vd,vd | depart,roundabout-exit-1,arrive |
-            | u    | s  | uc,sa,sa | depart,roundabout-exit-2,arrive |
-            | u    | t  | uc,tb,tb | depart,roundabout-exit-3,arrive |
-            | v    | s  | vd,sa,sa | depart,roundabout-exit-1,arrive |
-            | v    | t  | vd,tb,tb | depart,roundabout-exit-2,arrive |
-            | v    | u  | vd,uc,uc | depart,roundabout-exit-3,arrive |
-
-    Scenario: Testbot - Roundabout with oneway links
-        Given the node map
-            |   |   | p | o |   |   |
-            |   |   | h | g |   |   |
-            | i | a |   |   | f | n |
-            | j | b |   |   | e | m |
-            |   |   | c | d |   |   |
-            |   |   | k | l |   |   |
-
-        And the ways
-            | nodes     | junction   | oneway |
-            | ai        |            | yes    |
-            | jb        |            | yes    |
-            | ck        |            | yes    |
-            | ld        |            | yes    |
-            | em        |            | yes    |
-            | nf        |            | yes    |
-            | go        |            | yes    |
-            | ph        |            | yes    |
-            | abcdefgha | roundabout |        |
-
-        When I route I should get
-            | from | to | route    | turns                            |
-            | j    | k  | jb,ck,ck | depart,roundabout-exit-1,arrive |
-            | j    | m  | jb,em,em | depart,roundabout-exit-2,arrive |
-            | j    | o  | jb,go,go | depart,roundabout-exit-3,arrive |
-            | j    | i  | jb,ai,ai | depart,roundabout-exit-4,arrive |
-            | l    | m  | ld,em,em | depart,roundabout-exit-1,arrive |
-            | l    | o  | ld,go,go | depart,roundabout-exit-2,arrive |
-            | l    | i  | ld,ai,ai | depart,roundabout-exit-3,arrive |
-            | l    | k  | ld,ck,ck | depart,roundabout-exit-4,arrive |
-            | n    | o  | nf,go,go | depart,roundabout-exit-1,arrive |
-            | n    | i  | nf,ai,ai | depart,roundabout-exit-2,arrive |
-            | n    | k  | nf,ck,ck | depart,roundabout-exit-3,arrive |
-            | n    | m  | nf,em,em | depart,roundabout-exit-4,arrive |
-            | p    | i  | ph,ai,ai | depart,roundabout-exit-1,arrive |
-            | p    | k  | ph,ck,ck | depart,roundabout-exit-2,arrive |
-            | p    | m  | ph,em,em | depart,roundabout-exit-3,arrive |
-            | p    | o  | ph,go,go | depart,roundabout-exit-4,arrive |
diff --git a/features/testbot/status.feature b/features/testbot/status.feature
index 41ad18b..2221461 100644
--- a/features/testbot/status.feature
+++ b/features/testbot/status.feature
@@ -46,22 +46,22 @@ Feature: Status messages
             | ab    |
 
         When I route I should get
-            | request                     | status | message                                         |
-            | route/v1/driving/1,1;1,2    | 200    |                                                 |
-            | nonsense                    | 400    | URL string malformed close to position 0: "/no" |
-            | nonsense/v1/driving/1,1;1,2 | 400    | Service nonsense not found!                     |
-            |                             | 400    | URL string malformed close to position 0: "/"   |
-            | /                           | 400    | URL string malformed close to position 0: "//"  |
-            | ?                           | 400    | URL string malformed close to position 0: "/?"  |
-            | route/v1/driving            | 400    | URL string malformed close to position 0: "/ro" |
-            | route/v1/driving/           | 400    | URL string malformed close to position 0: "/ro" |
-            | route/v1/driving/1          | 400    | Query string malformed close to position 0      |
-            | 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 3      |
-            | 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      |
+            | request                     | status | message                                           |
+            | route/v1/driving/1,1;1,2    | 200    |                                                   |
+            | nonsense                    | 400    | URL string malformed close to position 9: "nse"   |
+            | nonsense/v1/driving/1,1;1,2 | 400    | Service nonsense not found!                       |
+            |                             | 400    | URL string malformed close to position 1: "/"     |
+            | /                           | 400    | URL string malformed close to position 1: "//"    |
+            | ?                           | 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,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        |
diff --git a/features/testbot/turn_angles.feature b/features/testbot/turn_angles.feature
deleted file mode 100644
index 115fd23..0000000
--- a/features/testbot/turn_angles.feature
+++ /dev/null
@@ -1,74 +0,0 @@
- at routing @testbot @via
-Feature: Via points
-
-    Background:
-        Given the profile "testbot"
-
-        And a grid size of 4 meters
-
-    Scenario: Basic Right Turn
-        Given the node map
-            | a | b | c | d | e | f | g |
-            |   |   |   |   |   | h |   |
-            |   |   |   |   |   | i |   |
-            |   |   |   |   |   | j |   |
-            |   |   |   |   |   | k |   |
-
-        And the ways
-            | nodes   | oneway |
-            | abcdefg | yes    |
-            | ehijk   | yes    |
-
-        When I route I should get
-            | from | to | route               | distance  | turns               |
-            | a    | k  | abcdefg,ehijk,ehijk |  34m +-1  | depart,right,arrive |
-
-    Scenario: Slight Turn
-        Given the node map
-            | a | b | c | d | e | f | g |   |
-            |   |   |   |   |   | h | i |   |
-            |   |   |   |   |   |   |   | j |
-            |   |   |   |   |   |   |   | k |
-
-        And the ways
-            | nodes   | oneway |
-            | abcdefg | yes    |
-            | ehijk   | yes    |
-
-        When I route I should get
-            | from | to | route               | distance  | turns                      |
-            | a    | k  | abcdefg,ehijk,ehijk |  35m +-1  | depart,slight right,arrive |
-
-    Scenario: Nearly Slight Turn
-        Given the node map
-            | a | b | c | d | e | f | g |   |
-            |   |   |   |   |   | h |   |   |
-            |   |   |   |   |   |   | i |   |
-            |   |   |   |   |   |   |   | j |
-            |   |   |   |   |   |   |   | k |
-
-        And the ways
-            | nodes   | oneway |
-            | abcdefg | yes    |
-            | ehijk   | yes    |
-
-        When I route I should get
-            | from | to | route               | distance  | turns                      |
-            | a    | k  | abcdefg,ehijk,ehijk |  37m +-1  | depart,right,arrive        |
-
-    Scenario: Nearly Slight Turn (Variation)
-        Given the node map
-            | a | b | c | d | e | f | g |   |
-            |   |   |   |   |   | h |   |   |
-            |   |   |   |   |   |   | i |   |
-            |   |   |   |   |   |   | j |   |
-            |   |   |   |   |   |   |   | k |
-
-        And the ways
-            | nodes   | oneway |
-            | abcdefg | yes    |
-            | ehijk   | yes    |
-
-        When I route I should get
-            | from | to | route               | distance  | turns                      |
-            | a    | k  | abcdefg,ehijk,ehijk |  37m +-1  | depart,right,arrive        |
diff --git a/features/testbot/turns.feature b/features/testbot/turns.feature
deleted file mode 100644
index c80bdf7..0000000
--- a/features/testbot/turns.feature
+++ /dev/null
@@ -1,115 +0,0 @@
- at routing @turns @testbot
-Feature: Turn directions/codes
-
-    Background:
-        Given the profile "testbot"
-
-    Scenario: Turn directions
-        Given the node map
-            | o | p | a | b | c |
-            | n |   |   |   | d |
-            | m |   | x |   | e |
-            | l |   |   |   | f |
-            | k | j | i | h | g |
-
-        And the ways
-            | nodes |
-            | xi    |
-            | xk    |
-            | xm    |
-            | xo    |
-            | xa    |
-            | xc    |
-            | xe    |
-            | xg    |
-
-        When I route I should get
-            | from | to | route    | turns                      |
-            | i    | k  | xi,xk,xk | depart,sharp left,arrive   |
-            | i    | m  | xi,xm,xm | depart,left,arrive         |
-            | i    | o  | xi,xo,xo | depart,slight left,arrive  |
-            | i    | a  | xi,xa,xa | depart,straight,arrive     |
-            | i    | c  | xi,xc,xc | depart,slight right,arrive |
-            | i    | e  | xi,xe,xe | depart,right,arrive        |
-            | i    | g  | xi,xg,xg | depart,sharp right,arrive  |
-
-            | k | m | xk,xm,xm | depart,sharp left,arrive   |
-            | k | o | xk,xo,xo | depart,left,arrive         |
-            | k | a | xk,xa,xa | depart,slight left,arrive  |
-            | k | c | xk,xc,xc | depart,straight,arrive     |
-            | k | e | xk,xe,xe | depart,slight right,arrive |
-            | k | g | xk,xg,xg | depart,right,arrive        |
-            | k | i | xk,xi,xi | depart,sharp right,arrive  |
-
-            | m | o | xm,xo,xo | depart,sharp left,arrive   |
-            | m | a | xm,xa,xa | depart,left,arrive         |
-            | m | c | xm,xc,xc | depart,slight left,arrive  |
-            | m | e | xm,xe,xe | depart,straight,arrive     |
-            | m | g | xm,xg,xg | depart,slight right,arrive |
-            | m | i | xm,xi,xi | depart,right,arrive        |
-            | m | k | xm,xk,xk | depart,sharp right,arrive  |
-
-            | o | a | xo,xa,xa | depart,sharp left,arrive   |
-            | o | c | xo,xc,xc | depart,left,arrive         |
-            | o | e | xo,xe,xe | depart,slight left,arrive  |
-            | o | g | xo,xg,xg | depart,straight,arrive     |
-            | o | i | xo,xi,xi | depart,slight right,arrive |
-            | o | k | xo,xk,xk | depart,right,arrive        |
-            | o | m | xo,xm,xm | depart,sharp right,arrive  |
-
-            | a | c | xa,xc,xc | depart,sharp left,arrive   |
-            | a | e | xa,xe,xe | depart,left,arrive         |
-            | a | g | xa,xg,xg | depart,slight left,arrive  |
-            | a | i | xa,xi,xi | depart,straight,arrive     |
-            | a | k | xa,xk,xk | depart,slight right,arrive |
-            | a | m | xa,xm,xm | depart,right,arrive        |
-            | a | o | xa,xo,xo | depart,sharp right,arrive  |
-
-            | c | e | xc,xe,xe | depart,sharp left,arrive   |
-            | c | g | xc,xg,xg | depart,left,arrive         |
-            | c | i | xc,xi,xi | depart,slight left,arrive  |
-            | c | k | xc,xk,xk | depart,straight,arrive     |
-            | c | m | xc,xm,xm | depart,slight right,arrive |
-            | c | o | xc,xo,xo | depart,right,arrive        |
-            | c | a | xc,xa,xa | depart,sharp right,arrive  |
-
-            | e | g | xe,xg,xg | depart,sharp left,arrive   |
-            | e | i | xe,xi,xi | depart,left,arrive         |
-            | e | k | xe,xk,xk | depart,slight left,arrive  |
-            | e | m | xe,xm,xm | depart,straight,arrive     |
-            | e | o | xe,xo,xo | depart,slight right,arrive |
-            | e | a | xe,xa,xa | depart,right,arrive        |
-            | e | c | xe,xc,xc | depart,sharp right,arrive  |
-
-            | g | i | xg,xi,xi | depart,sharp left,arrive   |
-            | g | k | xg,xk,xk | depart,left,arrive         |
-            | g | m | xg,xm,xm | depart,slight left,arrive  |
-            | g | o | xg,xo,xo | depart,straight,arrive     |
-            | g | a | xg,xa,xa | depart,slight right,arrive |
-            | g | c | xg,xc,xc | depart,right,arrive        |
-            | g | e | xg,xe,xe | depart,sharp right,arrive  |
-
-    Scenario: Turn instructions at high latitude
-    # https://github.com/DennisOSRM/Project-OSRM/issues/532
-        Given the node locations
-            | node | lat       | lon      |
-            | a    | 55.68740  | 12.52430 |
-            | b    | 55.68745  | 12.52409 |
-            | c    | 55.68711  | 12.52383 |
-            | x    | -55.68740 | 12.52430 |
-            | y    | -55.68745 | 12.52409 |
-            | z    | -55.68711 | 12.52383 |
-
-        And the ways
-            | nodes |
-            | ab    |
-            | bc    |
-            | xy    |
-            | yz    |
-
-        When I route I should get
-            | from | to | route    | turns               |
-            | a    | c  | ab,bc,bc | depart,left,arrive  |
-            | c    | a  | bc,ab,ab | depart,right,arrive |
-            | x    | z  | xy,yz,yz | depart,right,arrive |
-            | z    | x  | yz,xy,xy | depart,left,arrive  |
diff --git a/features/testbot/uturn.feature b/features/testbot/uturn.feature
deleted file mode 100644
index f00f5fc..0000000
--- a/features/testbot/uturn.feature
+++ /dev/null
@@ -1,93 +0,0 @@
- at routing @uturn @via @testbot
-Feature: U-turns at via points
-
-    Background:
-        Given the profile "testbot"
-
-    Scenario: U-turns at via points disabled by default
-        Given the node map
-            | a | b | c | d |
-            |   | e | f | g |
-
-        And the ways
-            | nodes |
-            | ab    |
-            | bc    |
-            | cd    |
-            | be    |
-            | dg    |
-            | ef    |
-            | fg    |
-
-        When I route I should get
-            | waypoints | route                   | turns                                                |
-            | a,e,c     | ab,be,be,ef,fg,dg,cd,cd | depart,right,arrive,depart,straight,left,left,arrive |
-
-    Scenario: Query param to allow U-turns at all via points
-        Given the node map
-            | a | b | c | d |
-            |   | e | f | g |
-
-        And the query options
-            | uturns | true |
-
-        And the ways
-            | nodes |
-            | ab    |
-            | bc    |
-            | cd    |
-            | be    |
-            | dg    |
-            | ef    |
-            | fg    |
-
-        When I route I should get
-            | waypoints | route             |
-            | a,e,c     | ab,be,be,be,bc,bc |
-
-    @todo
-    Scenario: Instructions at via points at u-turns
-        Given the node map
-            | a | b | c | d |
-            |   | e | f | g |
-
-        And the query options
-            | uturns | true |
-
-        And the ways
-            | nodes |
-            | ab    |
-            | bc    |
-            | cd    |
-            | be    |
-            | dg    |
-            | ef    |
-            | fg    |
-
-        When I route I should get
-            | waypoints | route          | turns                           |
-            | a,e,c     | ab,be,be,bc,bc | depart,right,uturn,right,arrive |
-
-    Scenario: u-turn mixed with non-uturn vias
-        Given the node map
-            | a | 1 | b | 3 | c | 5 | d |
-            |   |   | 2 |   |   |   | 4 |
-            |   |   | e |   | f |   | g |
-
-        And the query options
-            | uturns | true |
-
-        And the ways
-            | nodes |
-            | ab    |
-            | bc    |
-            | cd    |
-            | be    |
-            | dg    |
-            | ef    |
-            | fg    |
-
-        When I route I should get
-            | waypoints | route                                        |
-            | 1,2,3,4,5 | ab,be,be,be,bc,bc,bc,be,ef,fg,dg,dg,dg,cd,cd |
-
diff --git a/include/contractor/graph_contractor.hpp b/include/contractor/graph_contractor.hpp
index 26be61d..ce33d26 100644
--- a/include/contractor/graph_contractor.hpp
+++ b/include/contractor/graph_contractor.hpp
@@ -342,7 +342,7 @@ class GraphContractor
                 // remaining graph
                 std::vector<NodeID> new_node_id_from_orig_id_map(number_of_nodes, SPECIAL_NODEID);
 
-                for (const auto new_node_id : util::irange<std::size_t>(0, remaining_nodes.size()))
+                for (const auto new_node_id : util::irange<std::size_t>(0UL, remaining_nodes.size()))
                 {
                     auto &node = remaining_nodes[new_node_id];
                     BOOST_ASSERT(node_priorities.size() > node.id);
@@ -352,7 +352,7 @@ class GraphContractor
                 }
 
                 // build forward and backward renumbering map and remap ids in remaining_nodes
-                for (const auto new_node_id : util::irange<std::size_t>(0, remaining_nodes.size()))
+                for (const auto new_node_id : util::irange<std::size_t>(0UL, remaining_nodes.size()))
                 {
                     auto &node = remaining_nodes[new_node_id];
                     // create renumbering maps in both directions
@@ -362,7 +362,7 @@ class GraphContractor
                 }
                 // walk over all nodes
                 for (const auto source :
-                     util::irange<NodeID>(0, contractor_graph->GetNumberOfNodes()))
+                     util::irange<NodeID>(0UL, contractor_graph->GetNumberOfNodes()))
                 {
                     for (auto current_edge : contractor_graph->GetAdjacentEdgeRange(source))
                     {
diff --git a/include/engine/api/json_factory.hpp b/include/engine/api/json_factory.hpp
index 49e81f2..0b1e5f3 100644
--- a/include/engine/api/json_factory.hpp
+++ b/include/engine/api/json_factory.hpp
@@ -14,8 +14,9 @@
 
 #include <boost/optional.hpp>
 
-#include <string>
 #include <algorithm>
+#include <iterator>
+#include <string>
 #include <vector>
 
 namespace osrm
diff --git a/include/engine/api/match_api.hpp b/include/engine/api/match_api.hpp
index 7ce4fa2..46b26ae 100644
--- a/include/engine/api/match_api.hpp
+++ b/include/engine/api/match_api.hpp
@@ -45,7 +45,7 @@ class MatchAPI final : public RouteAPI
         }
         response.values["tracepoints"] = MakeTracepoints(sub_matchings);
         response.values["matchings"] = std::move(routes);
-        response.values["code"] = "ok";
+        response.values["code"] = "Ok";
     }
 
     // FIXME gcc 4.8 doesn't support for lambdas to call protected member functions
@@ -90,7 +90,7 @@ class MatchAPI final : public RouteAPI
             }
         }
 
-        for (auto trace_index : util::irange(0UL, parameters.coordinates.size()))
+        for (auto trace_index : util::irange<std::size_t>(0UL, parameters.coordinates.size()))
         {
             auto matching_index = trace_idx_to_matching_idx[trace_index];
             if (matching_index.NotMatched())
diff --git a/include/engine/api/nearest_api.hpp b/include/engine/api/nearest_api.hpp
index ab3747e..ed09f62 100644
--- a/include/engine/api/nearest_api.hpp
+++ b/include/engine/api/nearest_api.hpp
@@ -43,7 +43,7 @@ class NearestAPI final : public BaseAPI
                            return waypoint;
                        });
 
-        response.values["code"] = "ok";
+        response.values["code"] = "Ok";
         response.values["waypoints"] = std::move(waypoints);
     }
 
diff --git a/include/engine/api/route_api.hpp b/include/engine/api/route_api.hpp
index e3b9ff8..f2b7a73 100644
--- a/include/engine/api/route_api.hpp
+++ b/include/engine/api/route_api.hpp
@@ -2,15 +2,15 @@
 #define ENGINE_API_ROUTE_HPP
 
 #include "engine/api/base_api.hpp"
-#include "engine/api/route_parameters.hpp"
 #include "engine/api/json_factory.hpp"
+#include "engine/api/route_parameters.hpp"
 
 #include "engine/datafacade/datafacade_base.hpp"
 
-#include "engine/guidance/assemble_leg.hpp"
-#include "engine/guidance/assemble_route.hpp"
 #include "engine/guidance/assemble_geometry.hpp"
+#include "engine/guidance/assemble_leg.hpp"
 #include "engine/guidance/assemble_overview.hpp"
+#include "engine/guidance/assemble_route.hpp"
 #include "engine/guidance/assemble_steps.hpp"
 #include "engine/guidance/post_processing.hpp"
 
@@ -19,6 +19,7 @@
 #include "util/coordinate.hpp"
 #include "util/integer_range.hpp"
 
+#include <iterator>
 #include <vector>
 
 namespace osrm
@@ -54,7 +55,7 @@ class RouteAPI : public BaseAPI
         }
         response.values["waypoints"] = BaseAPI::MakeWaypoints(raw_route.segment_end_coordinates);
         response.values["routes"] = std::move(routes);
-        response.values["code"] = "ok";
+        response.values["code"] = "Ok";
     }
 
     // FIXME gcc 4.8 doesn't support for lambdas to call protected member functions
@@ -82,7 +83,7 @@ class RouteAPI : public BaseAPI
         legs.reserve(number_of_legs);
         leg_geometries.reserve(number_of_legs);
 
-        for (auto idx : util::irange(0UL, number_of_legs))
+        for (auto idx : util::irange<std::size_t>(0UL, number_of_legs))
         {
             const auto &phantoms = segment_end_coordinates[idx];
             const auto &path_data = unpacked_path_segments[idx];
@@ -92,8 +93,8 @@ class RouteAPI : public BaseAPI
 
             auto leg_geometry = guidance::assembleGeometry(
                 BaseAPI::facade, path_data, phantoms.source_phantom, phantoms.target_phantom);
-            auto leg = guidance::assembleLeg(BaseAPI::facade, path_data, leg_geometry,
-                                             phantoms.source_phantom, phantoms.target_phantom, reversed_target);
+            auto leg = guidance::assembleLeg(path_data, leg_geometry, phantoms.source_phantom,
+                                             phantoms.target_phantom, reversed_target);
 
             if (parameters.steps)
             {
@@ -155,13 +156,12 @@ class RouteAPI : public BaseAPI
         }
 
         std::vector<util::json::Value> step_geometries;
-        for (const auto idx : util::irange(0UL, legs.size()))
+        for (const auto idx : util::irange<std::size_t>(0UL, legs.size()))
         {
             auto &leg_geometry = leg_geometries[idx];
             std::transform(
                 legs[idx].steps.begin(), legs[idx].steps.end(), std::back_inserter(step_geometries),
-                [this, &leg_geometry](const guidance::RouteStep &step)
-                {
+                [this, &leg_geometry](const guidance::RouteStep &step) {
                     if (parameters.geometries == RouteParameters::GeometriesType::Polyline)
                     {
                         return static_cast<util::json::Value>(
diff --git a/include/engine/api/route_parameters.hpp b/include/engine/api/route_parameters.hpp
index b8704fc..5904020 100644
--- a/include/engine/api/route_parameters.hpp
+++ b/include/engine/api/route_parameters.hpp
@@ -48,7 +48,7 @@ namespace api
  *  - geometries: route geometry encoded in Polyline or GeoJSON
  *  - overview: adds overview geometry either Full, Simplified (according to highest zoom level) or
  *              False (not at all)
- *  - uturns: enable or disable uturns (disabled by default)
+ *  - continue_straight: enable or disable continue_straight (disabled by default)
  *
  * \see OSRM, Coordinate, Hint, Bearing, RouteParame, RouteParameters, TableParameters,
  *      NearestParameters, TripParameters, MatchParameters and TileParameters
@@ -74,18 +74,18 @@ struct RouteParameters : public BaseParameters
                     const bool alternatives_,
                     const GeometriesType geometries_,
                     const OverviewType overview_,
-                    const boost::optional<bool> uturns_,
+                    const boost::optional<bool> continue_straight_,
                     Args... args_)
         : BaseParameters{std::forward<Args>(args_)...}, steps{steps_}, alternatives{alternatives_},
-          geometries{geometries_}, overview{overview_}, uturns{uturns_}
+          geometries{geometries_}, overview{overview_}, continue_straight{continue_straight_}
     {
     }
 
-    bool steps = true;
-    bool alternatives = true;
+    bool steps = false;
+    bool alternatives = false;
     GeometriesType geometries = GeometriesType::Polyline;
     OverviewType overview = OverviewType::Simplified;
-    boost::optional<bool> uturns;
+    boost::optional<bool> continue_straight;
 
     bool IsValid() const { return coordinates.size() >= 2 && BaseParameters::IsValid(); }
 };
diff --git a/include/engine/api/table_api.hpp b/include/engine/api/table_api.hpp
index ac683c9..1655ce3 100644
--- a/include/engine/api/table_api.hpp
+++ b/include/engine/api/table_api.hpp
@@ -19,6 +19,8 @@
 
 #include <boost/range/algorithm/transform.hpp>
 
+#include <iterator>
+
 namespace osrm
 {
 namespace engine
@@ -65,7 +67,7 @@ class TableAPI final : public BaseAPI
 
         response.values["durations"] =
             MakeTable(durations, number_of_sources, number_of_destinations);
-        response.values["code"] = "ok";
+        response.values["code"] = "Ok";
     }
 
     // FIXME gcc 4.8 doesn't support for lambdas to call protected member functions
@@ -103,7 +105,7 @@ class TableAPI final : public BaseAPI
                                         std::size_t number_of_columns) const
     {
         util::json::Array json_table;
-        for (const auto row : util::irange<std::size_t>(0, number_of_rows))
+        for (const auto row : util::irange<std::size_t>(0UL, number_of_rows))
         {
             util::json::Array json_row;
             auto row_begin_iterator = values.begin() + (row * number_of_columns);
diff --git a/include/engine/api/trip_api.hpp b/include/engine/api/trip_api.hpp
index aea7676..9ed72ef 100644
--- a/include/engine/api/trip_api.hpp
+++ b/include/engine/api/trip_api.hpp
@@ -44,7 +44,7 @@ class TripAPI final : public RouteAPI
         }
         response.values["waypoints"] = MakeWaypoints(sub_trips, phantoms);
         response.values["trips"] = std::move(routes);
-        response.values["code"] = "ok";
+        response.values["code"] = "Ok";
     }
 
     // FIXME gcc 4.8 doesn't support for lambdas to call protected member functions
@@ -77,17 +77,17 @@ class TripAPI final : public RouteAPI
         };
 
         std::vector<TripIndex> input_idx_to_trip_idx(parameters.coordinates.size());
-        for (auto sub_trip_index : util::irange(0u, static_cast<unsigned>(sub_trips.size())))
+        for (auto sub_trip_index : util::irange<unsigned>(0u, sub_trips.size()))
         {
             for (auto point_index :
-                 util::irange(0u, static_cast<unsigned>(sub_trips[sub_trip_index].size())))
+                 util::irange<unsigned>(0u, sub_trips[sub_trip_index].size()))
             {
                 input_idx_to_trip_idx[sub_trips[sub_trip_index][point_index]] =
                     TripIndex{sub_trip_index, point_index};
             }
         }
 
-        for (auto input_index : util::irange(0UL, parameters.coordinates.size()))
+        for (auto input_index : util::irange<std::size_t>(0UL, parameters.coordinates.size()))
         {
             auto trip_index = input_idx_to_trip_idx[input_index];
             BOOST_ASSERT(!trip_index.NotUsed());
diff --git a/include/engine/datafacade/datafacade_base.hpp b/include/engine/datafacade/datafacade_base.hpp
index f4cb0c4..1f608d9 100644
--- a/include/engine/datafacade/datafacade_base.hpp
+++ b/include/engine/datafacade/datafacade_base.hpp
@@ -144,7 +144,7 @@ class BaseDataFacade
 
     virtual std::string GetTimestamp() const = 0;
 
-    virtual bool GetUTurnsDefault() const = 0;
+    virtual bool GetContinueStraightDefault() const = 0;
 };
 }
 }
diff --git a/include/engine/datafacade/internal_datafacade.hpp b/include/engine/datafacade/internal_datafacade.hpp
index 302af30..bda4869 100644
--- a/include/engine/datafacade/internal_datafacade.hpp
+++ b/include/engine/datafacade/internal_datafacade.hpp
@@ -649,7 +649,7 @@ class InternalDataFacade final : public BaseDataFacade
 
     std::string GetTimestamp() const override final { return m_timestamp; }
 
-    bool GetUTurnsDefault() const override final { return m_profile_properties.allow_u_turn_at_via; }
+    bool GetContinueStraightDefault() const override final { return m_profile_properties.continue_straight_at_waypoint; }
 };
 }
 }
diff --git a/include/engine/datafacade/shared_datafacade.hpp b/include/engine/datafacade/shared_datafacade.hpp
index dbb14bb..bd70e83 100644
--- a/include/engine/datafacade/shared_datafacade.hpp
+++ b/include/engine/datafacade/shared_datafacade.hpp
@@ -21,6 +21,7 @@
 #include <cstddef>
 
 #include <algorithm>
+#include <iterator>
 #include <limits>
 #include <memory>
 #include <string>
@@ -708,7 +709,7 @@ class SharedDataFacade final : public BaseDataFacade
 
     std::string GetTimestamp() const override final { return m_timestamp; }
 
-    bool GetUTurnsDefault() const override final { return m_profile_properties->allow_u_turn_at_via; }
+    bool GetContinueStraightDefault() const override final { return m_profile_properties->continue_straight_at_waypoint; }
 };
 }
 }
diff --git a/include/engine/douglas_peucker.hpp b/include/engine/douglas_peucker.hpp
index 468c6c7..e95d0cc 100644
--- a/include/engine/douglas_peucker.hpp
+++ b/include/engine/douglas_peucker.hpp
@@ -12,26 +12,47 @@ namespace engine
 {
 namespace detail
 {
-const constexpr int DOUGLAS_PEUCKER_THRESHOLDS[19] = {
-    512440, // z0
-    256720, // z1
-    122560, // z2
-    56780,  // z3
-    28800,  // z4
-    14400,  // z5
-    7200,   // z6
-    3200,   // z7
-    2400,   // z8
-    1000,   // z9
-    600,    // z10
-    120,    // z11
-    60,     // z12
-    45,     // z13
-    36,     // z14
-    20,     // z15
-    8,      // z16
-    6,      // z17
-    4,      // z18
+
+// This is derived from the following formular:
+// x = b * (1 + lon/180) => dx = b * dlon/180
+// y = b * (1 - lat/180) => dy = b * dlat/180
+// dx^2 + dy^2 < min_pixel^2
+// => dlon^2 + dlat^2 < min_pixel^2 / b^2 * 180^2
+inline std::vector<std::uint64_t> generateThreshold(double min_pixel, unsigned number_of_zoomlevels)
+{
+    std::vector<std::uint64_t> thresholds(number_of_zoomlevels);
+    for (unsigned zoom = 0; zoom < number_of_zoomlevels; ++zoom)
+    {
+        const double shift = (1u << zoom) * 256;
+        const double b = shift / 2.0;
+        const double pixel_to_deg = 180. / b;
+        const std::uint64_t min_deg = min_pixel * pixel_to_deg * COORDINATE_PRECISION;
+        thresholds[zoom] = min_deg * min_deg;
+    }
+
+    return thresholds;
+}
+
+const constexpr std::uint64_t DOUGLAS_PEUCKER_THRESHOLDS[19] = {
+    49438476562500, // z0
+    12359619140625, // z1
+    3089903027344,  // z2
+    772475756836,   // z3
+    193118939209,   // z4
+    48279515076,    // z5
+    12069878769,    // z6
+    3017414761,     // z7
+    754326225,      // z8
+    188567824,      // z9
+    47141956,       // z10
+    11785489,       // z11
+    2944656,        // z12
+    736164,         // z13
+    184041,         // z14
+    45796,          // z15
+    11449,          // z16
+    2809,           // z17
+    676,            // z18
 };
 
 const constexpr auto DOUGLAS_PEUCKER_THRESHOLDS_SIZE =
diff --git a/include/engine/geospatial_query.hpp b/include/engine/geospatial_query.hpp
index e880139..d2e90b8 100644
--- a/include/engine/geospatial_query.hpp
+++ b/include/engine/geospatial_query.hpp
@@ -6,6 +6,7 @@
 #include "engine/phantom_node.hpp"
 #include "util/bearing.hpp"
 #include "util/rectangle.hpp"
+#include "util/web_mercator.hpp"
 
 #include "osrm/coordinate.hpp"
 
@@ -424,10 +425,11 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
         BOOST_ASSERT(segment.data.reverse_segment_id.id != SPECIAL_SEGMENTID ||
                      !segment.data.reverse_segment_id.enabled);
 
-        Coordinate wsg84_coordinate = util::coordinate_calculation::mercator::toWGS84(
-            segment.fixed_projected_coordinate);
+        Coordinate wsg84_coordinate =
+            util::web_mercator::toWGS84(segment.fixed_projected_coordinate);
 
-        return util::coordinate_calculation::haversineDistance(input_coordinate, wsg84_coordinate) > max_distance;
+        return util::coordinate_calculation::haversineDistance(input_coordinate, wsg84_coordinate) >
+               max_distance;
     }
 
     std::pair<bool, bool> checkSegmentBearing(const CandidateSegment &segment,
diff --git a/include/engine/guidance/assemble_leg.hpp b/include/engine/guidance/assemble_leg.hpp
index bf34d74..b8d3dfe 100644
--- a/include/engine/guidance/assemble_leg.hpp
+++ b/include/engine/guidance/assemble_leg.hpp
@@ -1,20 +1,20 @@
 #ifndef ENGINE_GUIDANCE_ASSEMBLE_LEG_HPP_
 #define ENGINE_GUIDANCE_ASSEMBLE_LEG_HPP_
 
+#include "engine/guidance/leg_geometry.hpp"
 #include "engine/guidance/route_leg.hpp"
 #include "engine/guidance/route_step.hpp"
-#include "engine/guidance/leg_geometry.hpp"
 #include "engine/internal_route_result.hpp"
 
 #include <cstddef>
 #include <cstdint>
 
-#include <vector>
+#include <algorithm>
 #include <array>
+#include <numeric>
 #include <string>
 #include <utility>
-#include <numeric>
-#include <algorithm>
+#include <vector>
 
 namespace osrm
 {
@@ -22,86 +22,12 @@ namespace engine
 {
 namespace guidance
 {
-namespace detail
-{
-const constexpr std::size_t MAX_USED_SEGMENTS = 2;
-struct NamedSegment
-{
-    double duration;
-    std::uint32_t position;
-    std::uint32_t name_id;
-};
 
-template <std::size_t SegmentNumber>
-std::array<std::uint32_t, SegmentNumber> summarizeRoute(const std::vector<PathData> &route_data)
-{
-    // merges segments with same name id
-    const auto collapse_segments = [](std::vector<NamedSegment> &segments)
-    {
-        auto out = segments.begin();
-        auto end = segments.end();
-        for (auto in = segments.begin(); in != end; ++in)
-        {
-            if (in->name_id == out->name_id)
-            {
-                out->duration += in->duration;
-            }
-            else
-            {
-                ++out;
-                BOOST_ASSERT(out != end);
-                *out = *in;
-            }
-        }
-        return out;
-    };
-
-    std::vector<NamedSegment> segments(route_data.size());
-    std::uint32_t index = 0;
-    std::transform(
-        route_data.begin(), route_data.end(), segments.begin(), [&index](const PathData &point)
-        {
-            return NamedSegment{point.duration_until_turn / 10.0, index++, point.name_id};
-        });
-    // this makes sure that the segment with the lowest position comes first
-    std::sort(segments.begin(), segments.end(), [](const NamedSegment &lhs, const NamedSegment &rhs)
-              {
-                  return lhs.name_id < rhs.name_id ||
-                         (lhs.name_id == rhs.name_id && lhs.position < rhs.position);
-              });
-    auto new_end = collapse_segments(segments);
-    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;
-              });
-
-    // make sure the segments are sorted by position
-    segments.resize(std::min(segments.size(), SegmentNumber));
-    std::sort(segments.begin(), segments.end(), [](const NamedSegment &lhs, const NamedSegment &rhs)
-              {
-                  return lhs.position < rhs.position;
-              });
-
-    std::array<std::uint32_t, SegmentNumber> summary;
-    std::fill(summary.begin(), summary.end(), 0);
-    std::transform(segments.begin(), segments.end(), summary.begin(),
-                   [](const NamedSegment &segment)
-                   {
-                       return segment.name_id;
-                   });
-    return summary;
-}
-}
-
-template <typename DataFacadeT>
-RouteLeg assembleLeg(const DataFacadeT &facade,
-                     const std::vector<PathData> &route_data,
-                     const LegGeometry &leg_geometry,
-                     const PhantomNode &source_node,
-                     const PhantomNode &target_node,
-                     const bool target_traversed_in_reverse)
+inline RouteLeg assembleLeg(const std::vector<PathData> &route_data,
+                            const LegGeometry &leg_geometry,
+                            const PhantomNode &source_node,
+                            const PhantomNode &target_node,
+                            const bool target_traversed_in_reverse)
 {
     const auto target_duration =
         (target_traversed_in_reverse ? target_node.reverse_weight : target_node.forward_weight) /
@@ -110,8 +36,7 @@ RouteLeg assembleLeg(const DataFacadeT &facade,
     auto distance = std::accumulate(leg_geometry.segment_distances.begin(),
                                     leg_geometry.segment_distances.end(), 0.);
     auto duration = std::accumulate(route_data.begin(), route_data.end(), 0.,
-                                    [](const double sum, const PathData &data)
-                                    {
+                                    [](const double sum, const PathData &data) {
                                         return sum + data.duration_until_turn;
                                     }) /
                     10.;
@@ -140,26 +65,12 @@ RouteLeg assembleLeg(const DataFacadeT &facade,
     duration = duration + target_duration;
     if (route_data.empty())
     {
-        duration -=
-            (target_traversed_in_reverse ? source_node.reverse_weight : source_node.forward_weight) / 10;
+        duration -= (target_traversed_in_reverse ? source_node.reverse_weight
+                                                 : source_node.forward_weight) /
+                    10.0;
     }
-    auto summary_array = detail::summarizeRoute<detail::MAX_USED_SEGMENTS>(route_data);
-
-    BOOST_ASSERT(detail::MAX_USED_SEGMENTS > 0);
-    BOOST_ASSERT(summary_array.begin() != summary_array.end());
-    std::string summary =
-        std::accumulate(std::next(summary_array.begin()), summary_array.end(),
-                        facade.GetNameForID(summary_array.front()),
-                        [&facade](std::string previous, const std::uint32_t name_id)
-                        {
-                            if (name_id != 0)
-                            {
-                                previous += ", " + facade.GetNameForID(name_id);
-                            }
-                            return previous;
-                        });
 
-    return RouteLeg{duration, distance, summary, {}};
+    return RouteLeg{duration, distance, {}};
 }
 
 } // namespace guidance
diff --git a/include/engine/guidance/assemble_steps.hpp b/include/engine/guidance/assemble_steps.hpp
index c6d19ee..a32811a 100644
--- a/include/engine/guidance/assemble_steps.hpp
+++ b/include/engine/guidance/assemble_steps.hpp
@@ -1,20 +1,20 @@
 #ifndef ENGINE_GUIDANCE_ASSEMBLE_STEPS_HPP_
 #define ENGINE_GUIDANCE_ASSEMBLE_STEPS_HPP_
 
+#include "engine/guidance/leg_geometry.hpp"
 #include "engine/guidance/route_step.hpp"
 #include "engine/guidance/step_maneuver.hpp"
-#include "engine/guidance/leg_geometry.hpp"
 #include "engine/guidance/toolkit.hpp"
-#include "extractor/guidance/turn_instruction.hpp"
 #include "engine/internal_route_result.hpp"
 #include "engine/phantom_node.hpp"
-#include "util/coordinate_calculation.hpp"
-#include "util/coordinate.hpp"
-#include "util/bearing.hpp"
+#include "extractor/guidance/turn_instruction.hpp"
 #include "extractor/travel_mode.hpp"
+#include "util/bearing.hpp"
+#include "util/coordinate.hpp"
+#include "util/coordinate_calculation.hpp"
 
-#include <vector>
 #include <boost/optional.hpp>
+#include <vector>
 
 namespace osrm
 {
@@ -44,6 +44,7 @@ std::vector<RouteStep> assembleSteps(const DataFacadeT &facade,
                                      const bool target_traversed_in_reverse)
 {
     const double constexpr ZERO_DURATION = 0., ZERO_DISTANCE = 0.;
+    const constexpr char *NO_ROTARY_NAME = "";
     const EdgeWeight source_duration =
         source_traversed_in_reverse ? source_node.reverse_weight : source_node.forward_weight;
     const auto source_mode = source_traversed_in_reverse ? source_node.backward_travel_mode
@@ -84,13 +85,9 @@ std::vector<RouteStep> assembleSteps(const DataFacadeT &facade,
                 BOOST_ASSERT(segment_duration >= 0);
                 const auto name = facade.GetNameForID(path_point.name_id);
                 const auto distance = leg_geometry.segment_distances[segment_index];
-                steps.push_back(RouteStep{path_point.name_id,
-                                          name,
-                                          segment_duration / 10.0,
-                                          distance,
-                                          path_point.travel_mode,
-                                          maneuver,
-                                          leg_geometry.FrontIndex(segment_index),
+                steps.push_back(RouteStep{path_point.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});
                 maneuver = detail::stepManeuverFromGeometry(path_point.turn_instruction,
                                                             leg_geometry, segment_index);
@@ -101,12 +98,8 @@ 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),
-                                  duration / 10.,
-                                  distance,
-                                  target_mode,
-                                  maneuver,
+        steps.push_back(RouteStep{target_node.name_id, facade.GetNameForID(target_node.name_id),
+                                  NO_ROTARY_NAME, duration / 10., distance, target_mode, maneuver,
                                   leg_geometry.FrontIndex(segment_index),
                                   leg_geometry.BackIndex(segment_index) + 1});
     }
@@ -124,13 +117,10 @@ std::vector<RouteStep> assembleSteps(const DataFacadeT &facade,
         int duration = target_duration - source_duration;
         BOOST_ASSERT(duration >= 0);
 
-        steps.push_back(RouteStep{source_node.name_id,
-                                  facade.GetNameForID(source_node.name_id),
-                                  duration / 10.,
-                                  leg_geometry.segment_distances[segment_index],
-                                  source_mode,
-                                  std::move(maneuver),
-                                  leg_geometry.FrontIndex(segment_index),
+        steps.push_back(RouteStep{source_node.name_id, facade.GetNameForID(source_node.name_id),
+                                  NO_ROTARY_NAME, duration / 10.,
+                                  leg_geometry.segment_distances[segment_index], source_mode,
+                                  std::move(maneuver), leg_geometry.FrontIndex(segment_index),
                                   leg_geometry.BackIndex(segment_index) + 1});
     }
 
@@ -138,13 +128,11 @@ std::vector<RouteStep> assembleSteps(const DataFacadeT &facade,
     // This step has length zero, the only reason we need it is the target location
     auto final_maneuver = detail::stepManeuverFromGeometry(
         extractor::guidance::TurnInstruction::NO_TURN(), WaypointType::Arrive, leg_geometry);
-    steps.push_back(RouteStep{target_node.name_id,
-                              facade.GetNameForID(target_node.name_id),
-                              ZERO_DURATION,
-                              ZERO_DISTANCE,
-                              target_mode,
-                              final_maneuver,
-                              leg_geometry.locations.size(),
+
+    BOOST_ASSERT(!leg_geometry.locations.empty());
+    steps.push_back(RouteStep{target_node.name_id, facade.GetNameForID(target_node.name_id),
+                              NO_ROTARY_NAME, ZERO_DURATION, ZERO_DISTANCE, target_mode,
+                              final_maneuver, leg_geometry.locations.size()-1,
                               leg_geometry.locations.size()});
 
     return steps;
diff --git a/include/engine/guidance/route_leg.hpp b/include/engine/guidance/route_leg.hpp
index 5ecca4f..f3b8de1 100644
--- a/include/engine/guidance/route_leg.hpp
+++ b/include/engine/guidance/route_leg.hpp
@@ -3,9 +3,6 @@
 
 #include "engine/guidance/route_step.hpp"
 
-#include <boost/optional.hpp>
-
-#include <string>
 #include <vector>
 
 namespace osrm
@@ -19,7 +16,6 @@ struct RouteLeg
 {
     double duration;
     double distance;
-    std::string summary;
     std::vector<RouteStep> steps;
 };
 }
diff --git a/include/engine/guidance/route_step.hpp b/include/engine/guidance/route_step.hpp
index 1a53579..d5950a6 100644
--- a/include/engine/guidance/route_step.hpp
+++ b/include/engine/guidance/route_step.hpp
@@ -1,8 +1,8 @@
 #ifndef ROUTE_STEP_HPP
 #define ROUTE_STEP_HPP
 
-#include "extractor/travel_mode.hpp"
 #include "engine/guidance/step_maneuver.hpp"
+#include "extractor/travel_mode.hpp"
 
 #include <cstddef>
 
@@ -25,6 +25,7 @@ struct RouteStep
 {
     unsigned name_id;
     std::string name;
+    std::string rotary_name;
     double duration;
     double distance;
     extractor::TravelMode mode;
diff --git a/include/engine/hint.hpp b/include/engine/hint.hpp
index 0ecaff6..56dfbc4 100644
--- a/include/engine/hint.hpp
+++ b/include/engine/hint.hpp
@@ -70,8 +70,8 @@ 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) == 64 + 4, "Hint is bigger than expected");
-constexpr std::size_t ENCODED_HINT_SIZE = 92;
+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
diff --git a/include/engine/map_matching/hidden_markov_model.hpp b/include/engine/map_matching/hidden_markov_model.hpp
index 303a835..9b36343 100644
--- a/include/engine/map_matching/hidden_markov_model.hpp
+++ b/include/engine/map_matching/hidden_markov_model.hpp
@@ -69,7 +69,7 @@ template <class CandidateLists> struct HiddenMarkovModel
         path_distances.resize(candidates_list.size());
         pruned.resize(candidates_list.size());
         breakage.resize(candidates_list.size());
-        for (const auto i : util::irange<std::size_t>(0u, candidates_list.size()))
+        for (const auto i : util::irange<std::size_t>(0UL, candidates_list.size()))
         {
             const auto &num_candidates = candidates_list[i].size();
             // add empty vectors
@@ -107,7 +107,7 @@ template <class CandidateLists> struct HiddenMarkovModel
         {
             BOOST_ASSERT(initial_timestamp < num_points);
 
-            for (const auto s : util::irange<std::size_t>(0u, viterbi[initial_timestamp].size()))
+            for (const auto s : util::irange<std::size_t>(0UL, viterbi[initial_timestamp].size()))
             {
                 viterbi[initial_timestamp][s] = emission_log_probabilities[initial_timestamp][s];
                 parents[initial_timestamp][s] = std::make_pair(initial_timestamp, s);
diff --git a/include/engine/phantom_node.hpp b/include/engine/phantom_node.hpp
index 244276c..88724b1 100644
--- a/include/engine/phantom_node.hpp
+++ b/include/engine/phantom_node.hpp
@@ -168,7 +168,7 @@ struct PhantomNode
 #ifndef _MSC_VER
 static_assert(sizeof(PhantomNode) == 60, "PhantomNode has more padding then expected");
 #else
-static_assert(sizeof(PhantomNode) == 64, "PhantomNode has more padding then expected");
+static_assert(sizeof(PhantomNode) == 72, "PhantomNode has more padding then expected");
 #endif
 
 using PhantomNodePair = std::pair<PhantomNode, PhantomNode>;
diff --git a/include/engine/plugins/plugin_base.hpp b/include/engine/plugins/plugin_base.hpp
index aec9dd0..e030ff8 100644
--- a/include/engine/plugins/plugin_base.hpp
+++ b/include/engine/plugins/plugin_base.hpp
@@ -12,6 +12,7 @@
 #include "util/integer_range.hpp"
 
 #include <algorithm>
+#include <iterator>
 #include <string>
 #include <vector>
 
@@ -122,7 +123,7 @@ class BasePlugin
         const bool use_hints = !parameters.hints.empty();
         const bool use_bearings = !parameters.bearings.empty();
 
-        for (const auto i : util::irange<std::size_t>(0, parameters.coordinates.size()))
+        for (const auto i : util::irange<std::size_t>(0UL, parameters.coordinates.size()))
         {
             if (use_hints && parameters.hints[i] &&
                 parameters.hints[i]->IsValid(parameters.coordinates[i], facade))
@@ -161,7 +162,7 @@ class BasePlugin
         const bool use_radiuses = !parameters.radiuses.empty();
 
         BOOST_ASSERT(parameters.IsValid());
-        for (const auto i : util::irange<std::size_t>(0, parameters.coordinates.size()))
+        for (const auto i : util::irange<std::size_t>(0UL, parameters.coordinates.size()))
         {
             if (use_hints && parameters.hints[i] &&
                 parameters.hints[i]->IsValid(parameters.coordinates[i], facade))
@@ -221,7 +222,7 @@ class BasePlugin
         const bool use_radiuses = !parameters.radiuses.empty();
 
         BOOST_ASSERT(parameters.IsValid());
-        for (const auto i : util::irange<std::size_t>(0, parameters.coordinates.size()))
+        for (const auto i : util::irange<std::size_t>(0UL, parameters.coordinates.size()))
         {
             if (use_hints && parameters.hints[i] &&
                 parameters.hints[i]->IsValid(parameters.coordinates[i], facade))
diff --git a/include/engine/routing_algorithms/alternative_path.hpp b/include/engine/routing_algorithms/alternative_path.hpp
index 8dcc123..493d62f 100644
--- a/include/engine/routing_algorithms/alternative_path.hpp
+++ b/include/engine/routing_algorithms/alternative_path.hpp
@@ -84,19 +84,25 @@ class AlternativeRouting final
         int upper_bound_to_shortest_path_distance = INVALID_EDGE_WEIGHT;
         NodeID middle_node = SPECIAL_NODEID;
         const EdgeWeight min_edge_offset =
-            std::min(phantom_node_pair.source_phantom.forward_segment_id.enabled ? -phantom_node_pair.source_phantom.GetForwardWeightPlusOffset() : 0,
-                     phantom_node_pair.source_phantom.reverse_segment_id.enabled ? -phantom_node_pair.source_phantom.GetReverseWeightPlusOffset() : 0);
+            std::min(phantom_node_pair.source_phantom.forward_segment_id.enabled
+                         ? -phantom_node_pair.source_phantom.GetForwardWeightPlusOffset()
+                         : 0,
+                     phantom_node_pair.source_phantom.reverse_segment_id.enabled
+                         ? -phantom_node_pair.source_phantom.GetReverseWeightPlusOffset()
+                         : 0);
 
         if (phantom_node_pair.source_phantom.forward_segment_id.enabled)
         {
-            BOOST_ASSERT(phantom_node_pair.source_phantom.forward_segment_id.id != SPECIAL_SEGMENTID);
+            BOOST_ASSERT(phantom_node_pair.source_phantom.forward_segment_id.id !=
+                         SPECIAL_SEGMENTID);
             forward_heap1.Insert(phantom_node_pair.source_phantom.forward_segment_id.id,
                                  -phantom_node_pair.source_phantom.GetForwardWeightPlusOffset(),
                                  phantom_node_pair.source_phantom.forward_segment_id.id);
         }
         if (phantom_node_pair.source_phantom.reverse_segment_id.enabled)
         {
-            BOOST_ASSERT(phantom_node_pair.source_phantom.reverse_segment_id.id != SPECIAL_SEGMENTID);
+            BOOST_ASSERT(phantom_node_pair.source_phantom.reverse_segment_id.id !=
+                         SPECIAL_SEGMENTID);
             forward_heap1.Insert(phantom_node_pair.source_phantom.reverse_segment_id.id,
                                  -phantom_node_pair.source_phantom.GetReverseWeightPlusOffset(),
                                  phantom_node_pair.source_phantom.reverse_segment_id.id);
@@ -104,14 +110,16 @@ class AlternativeRouting final
 
         if (phantom_node_pair.target_phantom.forward_segment_id.enabled)
         {
-            BOOST_ASSERT(phantom_node_pair.target_phantom.forward_segment_id.id != SPECIAL_SEGMENTID);
+            BOOST_ASSERT(phantom_node_pair.target_phantom.forward_segment_id.id !=
+                         SPECIAL_SEGMENTID);
             reverse_heap1.Insert(phantom_node_pair.target_phantom.forward_segment_id.id,
                                  phantom_node_pair.target_phantom.GetForwardWeightPlusOffset(),
                                  phantom_node_pair.target_phantom.forward_segment_id.id);
         }
         if (phantom_node_pair.target_phantom.reverse_segment_id.enabled)
         {
-            BOOST_ASSERT(phantom_node_pair.target_phantom.reverse_segment_id.id != SPECIAL_SEGMENTID);
+            BOOST_ASSERT(phantom_node_pair.target_phantom.reverse_segment_id.id !=
+                         SPECIAL_SEGMENTID);
             reverse_heap1.Insert(phantom_node_pair.target_phantom.reverse_segment_id.id,
                                  phantom_node_pair.target_phantom.GetReverseWeightPlusOffset(),
                                  phantom_node_pair.target_phantom.reverse_segment_id.id);
@@ -308,9 +316,11 @@ class AlternativeRouting final
             BOOST_ASSERT(!packed_shortest_path.empty());
             raw_route_data.unpacked_path_segments.resize(1);
             raw_route_data.source_traversed_in_reverse.push_back(
-                (packed_shortest_path.front() != phantom_node_pair.source_phantom.forward_segment_id.id));
+                (packed_shortest_path.front() !=
+                 phantom_node_pair.source_phantom.forward_segment_id.id));
             raw_route_data.target_traversed_in_reverse.push_back(
-                (packed_shortest_path.back() != phantom_node_pair.target_phantom.forward_segment_id.id));
+                (packed_shortest_path.back() !=
+                 phantom_node_pair.target_phantom.forward_segment_id.id));
 
             super::UnpackPath(
                 // -- packed input
@@ -329,10 +339,12 @@ class AlternativeRouting final
             RetrievePackedAlternatePath(forward_heap1, reverse_heap1, forward_heap2, reverse_heap2,
                                         s_v_middle, v_t_middle, packed_alternate_path);
 
-            raw_route_data.alt_source_traversed_in_reverse.push_back((
-                packed_alternate_path.front() != phantom_node_pair.source_phantom.forward_segment_id.id));
+            raw_route_data.alt_source_traversed_in_reverse.push_back(
+                (packed_alternate_path.front() !=
+                 phantom_node_pair.source_phantom.forward_segment_id.id));
             raw_route_data.alt_target_traversed_in_reverse.push_back(
-                (packed_alternate_path.back() != phantom_node_pair.target_phantom.forward_segment_id.id));
+                (packed_alternate_path.back() !=
+                 phantom_node_pair.target_phantom.forward_segment_id.id));
 
             // unpack the alternate path
             super::UnpackPath(packed_alternate_path.begin(), packed_alternate_path.end(),
@@ -429,9 +441,9 @@ class AlternativeRouting final
 
         // partial unpacking, compute sharing
         // First partially unpack s-->v until paths deviate, note length of common path.
-        const int64_t s_v_min_path_size =
-            static_cast<int64_t>(std::min(packed_s_v_path.size(), packed_shortest_path.size())) - 1;
-        for (const int64_t current_node : util::irange<int64_t>(0, s_v_min_path_size))
+        const auto s_v_min_path_size =
+            std::min(packed_s_v_path.size(), packed_shortest_path.size()) - 1;
+        for (const auto current_node : util::irange<std::size_t>(0UL, s_v_min_path_size))
         {
             if (packed_s_v_path[current_node] == packed_shortest_path[current_node] &&
                 packed_s_v_path[current_node + 1] == packed_shortest_path[current_node + 1])
diff --git a/include/engine/routing_algorithms/map_matching.hpp b/include/engine/routing_algorithms/map_matching.hpp
index 7d8b04e..0b1020a 100644
--- a/include/engine/routing_algorithms/map_matching.hpp
+++ b/include/engine/routing_algorithms/map_matching.hpp
@@ -244,14 +244,14 @@ class MapMatching final : public BasicRoutingInterface<DataFacadeT, MapMatching<
                 ((haversine_distance + max_distance_delta) * 0.25) * 10;
 
             // compute d_t for this timestamp and the next one
-            for (const auto s : util::irange<std::size_t>(0u, prev_viterbi.size()))
+            for (const auto s : util::irange<std::size_t>(0UL, prev_viterbi.size()))
             {
                 if (prev_pruned[s])
                 {
                     continue;
                 }
 
-                for (const auto s_prime : util::irange<std::size_t>(0u, current_viterbi.size()))
+                for (const auto s_prime : util::irange<std::size_t>(0UL, current_viterbi.size()))
                 {
                     const double emission_pr = emission_log_probabilities[t][s_prime];
                     double new_value = prev_viterbi[s] + emission_pr;
diff --git a/include/engine/routing_algorithms/shortest_path.hpp b/include/engine/routing_algorithms/shortest_path.hpp
index 9720931..7b8fd7c 100644
--- a/include/engine/routing_algorithms/shortest_path.hpp
+++ b/include/engine/routing_algorithms/shortest_path.hpp
@@ -227,7 +227,7 @@ class ShortestPathRouting final
 
         raw_route_data.shortest_path_length = shortest_path_length;
 
-        for (const auto current_leg : util::irange<std::size_t>(0, packed_leg_begin.size() - 1))
+        for (const auto current_leg : util::irange<std::size_t>(0UL, packed_leg_begin.size() - 1))
         {
             auto leg_begin = total_packed_path.begin() + packed_leg_begin[current_leg];
             auto leg_end = total_packed_path.begin() + packed_leg_begin[current_leg + 1];
@@ -245,10 +245,10 @@ class ShortestPathRouting final
     }
 
     void operator()(const std::vector<PhantomNodes> &phantom_nodes_vector,
-                    const boost::optional<bool> uturns,
+                    const boost::optional<bool> continue_straight_at_waypoint,
                     InternalRouteResult &raw_route_data) const
     {
-        const bool allow_u_turn_at_via = uturns ? *uturns : super::facade->GetUTurnsDefault();
+        const bool allow_uturn_at_waypoint = !(continue_straight_at_waypoint ? *continue_straight_at_waypoint : super::facade->GetContinueStraightDefault());
 
         engine_working_data.InitializeOrClearFirstThreadLocalStorage(
             super::facade->GetNumberOfNodes());
@@ -299,7 +299,7 @@ class ShortestPathRouting final
 
             if (search_to_reverse_node || search_to_forward_node)
             {
-                if (allow_u_turn_at_via)
+                if (allow_uturn_at_waypoint)
                 {
                     SearchWithUTurn(forward_heap, reverse_heap, forward_core_heap,
                                     reverse_core_heap, search_from_forward_node,
@@ -309,7 +309,7 @@ class ShortestPathRouting final
                                     new_total_distance_to_forward, packed_leg_to_forward);
                     // if only the reverse node is valid (e.g. when using the match plugin) we
                     // actually need to move
-                    if (target_phantom.forward_segment_id.enabled)
+                    if (!target_phantom.forward_segment_id.enabled)
                     {
                         BOOST_ASSERT(target_phantom.reverse_segment_id.enabled);
                         new_total_distance_to_reverse = new_total_distance_to_forward;
diff --git a/include/extractor/guidance/constants.hpp b/include/extractor/guidance/constants.hpp
new file mode 100644
index 0000000..0418682
--- /dev/null
+++ b/include/extractor/guidance/constants.hpp
@@ -0,0 +1,32 @@
+#ifndef OSRM_EXTRACTOR_GUIDANCE_CONSTANTS_HPP_
+#define OSRM_EXTRACTOR_GUIDANCE_CONSTANTS_HPP_
+
+namespace osrm
+{
+namespace extractor
+{
+namespace guidance
+{
+
+const bool constexpr INVERT = true;
+
+// what angle is interpreted as going straight
+const double constexpr STRAIGHT_ANGLE = 180.;
+// if a turn deviates this much from going straight, it will be kept straight
+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;
+// angle difference that can be classified as straight, if its the only narrow turn
+const double constexpr FUZZY_ANGLE_DIFFERENCE = 15.;
+const double constexpr DISTINCTION_RATIO = 2;
+const unsigned constexpr INVALID_NAME_ID = 0;
+
+const double constexpr MAX_ROUNDABOUT_RADIUS = 15; // 30 m diameter as final distinction
+const double constexpr INCREASES_BY_FOURTY_PERCENT = 1.4;
+
+} // namespace guidance
+} // namespace extractor
+} // namespace osrm
+
+#endif // OSRM_EXTRACTOR_GUIDANCE_CONSTANTS_HPP_
diff --git a/include/extractor/guidance/intersection.hpp b/include/extractor/guidance/intersection.hpp
new file mode 100644
index 0000000..8b350e2
--- /dev/null
+++ b/include/extractor/guidance/intersection.hpp
@@ -0,0 +1,66 @@
+#ifndef OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_HPP_
+#define OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_HPP_
+
+#include <string>
+#include <vector>
+
+#include "extractor/guidance/turn_instruction.hpp"
+#include "util/typedefs.hpp" // EdgeID
+
+namespace osrm
+{
+namespace extractor
+{
+namespace guidance
+{
+
+// Every Turn Operation describes a way of switching onto a segment, indicated by an EdgeID. The
+// associated turn is described by an angle and an instruction that is used to announce it.
+// The Turn Operation indicates what is exposed to the outside of the turn analysis.
+struct TurnOperation final
+{
+    EdgeID eid;
+    double angle;
+    TurnInstruction instruction;
+};
+
+// A Connected Road is the internal representation of a potential turn. Internally, we require
+// full list of all connected roads to determine the outcome.
+// The reasoning behind is that even invalid turns can influence the perceived angles, or even
+// instructions themselves. An pososible example can be described like this:
+//
+// aaa(2)aa
+//          a - bbbbb
+// aaa(1)aa
+//
+// will not be perceived as a turn from (1) -> b, and as a U-turn from (1) -> (2).
+// In addition, they can influence whether a turn is obvious or not. b->(2) would also be no
+// turn-operation,
+// but rather a name change.
+//
+// If this were a normal intersection with
+//
+// cccccccc
+//            o  bbbbb
+// aaaaaaaa
+//
+// We would perceive a->c as a sharp turn, a->b as a slight turn, and b->c as a slight turn.
+struct ConnectedRoad final
+{
+    ConnectedRoad(const TurnOperation turn, const bool entry_allowed = false);
+
+    // a turn may be relevant to good instructions, even if we cannot enter the road
+    bool entry_allowed;
+    TurnOperation turn;
+};
+
+// small helper function to print the content of a connected road
+std::string toString(const ConnectedRoad &road);
+
+typedef std::vector<ConnectedRoad> Intersection;
+
+} // namespace guidance
+} // namespace extractor
+} // namespace osrm
+
+#endif /*OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_HPP_*/
diff --git a/include/extractor/guidance/intersection_generator.hpp b/include/extractor/guidance/intersection_generator.hpp
new file mode 100644
index 0000000..be99a71
--- /dev/null
+++ b/include/extractor/guidance/intersection_generator.hpp
@@ -0,0 +1,68 @@
+#ifndef OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_GENERATOR_HPP_
+#define OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_GENERATOR_HPP_
+
+#include "extractor/compressed_edge_container.hpp"
+#include "extractor/guidance/intersection.hpp"
+#include "extractor/query_node.hpp"
+#include "extractor/restriction_map.hpp"
+#include "util/node_based_graph.hpp"
+#include "util/typedefs.hpp"
+
+#include <unordered_set>
+#include <vector>
+
+namespace osrm
+{
+namespace extractor
+{
+namespace guidance
+{
+// The Intersection Generator is given a turn location and generates an intersection representation
+// from it. For this all turn possibilities are analysed.
+// We consider turn restrictions to indicate possible turns. U-turns are generated based on profile
+// decisions.
+
+class IntersectionGenerator
+{
+  public:
+    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);
+
+    Intersection operator()(const NodeID nid, const EdgeID via_eid) const;
+
+  private:
+    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;
+
+    // Check for restrictions/barriers and generate a list of valid and invalid turns present at
+    // the
+    // node reached
+    // from `from_node` via `via_eid`
+    // The resulting candidates have to be analysed for their actual instructions later on.
+    Intersection getConnectedRoads(const NodeID from_node, const EdgeID via_eid) const;
+
+    // Merge segregated roads to omit invalid turns in favor of treating segregated roads as
+    // one.
+    // This function combines roads the following way:
+    //
+    //     *                           *
+    //     *        is converted to    *
+    //   v   ^                         +
+    //   v   ^                         +
+    //
+    // The treatment results in a straight turn angle of 180º rather than a turn angle of approx
+    // 160
+    Intersection mergeSegregatedRoads(Intersection intersection) const;
+};
+
+} // namespace guidance
+} // namespace extractor
+} // namespace osrm
+
+#endif /* OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_GENERATOR_HPP_ */
diff --git a/include/extractor/guidance/intersection_handler.hpp b/include/extractor/guidance/intersection_handler.hpp
new file mode 100644
index 0000000..25d2f4a
--- /dev/null
+++ b/include/extractor/guidance/intersection_handler.hpp
@@ -0,0 +1,71 @@
+#ifndef OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_HANDLER_HPP_
+#define OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_HANDLER_HPP_
+
+#include "extractor/guidance/intersection.hpp"
+#include "extractor/query_node.hpp"
+
+#include "util/name_table.hpp"
+#include "util/node_based_graph.hpp"
+
+#include <cstddef>
+#include <vector>
+
+namespace osrm
+{
+namespace extractor
+{
+namespace guidance
+{
+
+// Intersection handlers deal with all issues related to intersections.
+// They assign appropriate turn operations to the TurnOperations.
+// This base class provides both the interface and implementations for
+// common functions.
+class IntersectionHandler
+{
+  public:
+    IntersectionHandler(const util::NodeBasedDynamicGraph &node_based_graph,
+                        const std::vector<QueryNode> &node_info_list,
+                        const util::NameTable &name_table);
+    virtual ~IntersectionHandler();
+
+    // check whether the handler can actually handle the intersection
+    virtual bool
+    canProcess(const NodeID nid, const EdgeID via_eid, const Intersection &intersection) const = 0;
+
+    // process the intersection
+    virtual Intersection
+    operator()(const NodeID nid, const EdgeID via_eid, Intersection intersection) const = 0;
+
+  protected:
+    const util::NodeBasedDynamicGraph &node_based_graph;
+    const std::vector<QueryNode> &node_info_list;
+    const util::NameTable &name_table;
+
+    // counts the number on allowed entry roads
+    std::size_t countValid(const Intersection &intersection) const;
+
+    // Decide on a basic turn types
+    TurnType findBasicTurnType(const EdgeID via_edge, const ConnectedRoad &candidate) const;
+
+    // Get the Instruction for an obvious turn
+    TurnInstruction getInstructionForObvious(const std::size_t number_of_candidates,
+                                             const EdgeID via_edge,
+                                             const bool through_street,
+                                             const ConnectedRoad &candidate) const;
+
+    // Treating potential forks
+    void assignFork(const EdgeID via_edge, ConnectedRoad &left, ConnectedRoad &right) const;
+    void assignFork(const EdgeID via_edge,
+                    ConnectedRoad &left,
+                    ConnectedRoad &center,
+                    ConnectedRoad &right) const;
+
+    bool isThroughStreet(const std::size_t index, const Intersection &intersection) const;
+};
+
+} // namespace guidance
+} // namespace extractor
+} // namespace osrm
+
+#endif /*OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_HANDLER_HPP_*/
diff --git a/include/extractor/guidance/motorway_handler.hpp b/include/extractor/guidance/motorway_handler.hpp
new file mode 100644
index 0000000..e3d9e34
--- /dev/null
+++ b/include/extractor/guidance/motorway_handler.hpp
@@ -0,0 +1,51 @@
+#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/query_node.hpp"
+
+#include "util/name_table.hpp"
+#include "util/node_based_graph.hpp"
+
+#include <vector>
+
+namespace osrm
+{
+namespace extractor
+{
+namespace guidance
+{
+
+// Intersection handlers deal with all issues related to intersections.
+// They assign appropriate turn operations to the TurnOperations.
+class MotorwayHandler : public IntersectionHandler
+{
+  public:
+    MotorwayHandler(const util::NodeBasedDynamicGraph &node_based_graph,
+                    const std::vector<QueryNode> &node_info_list,
+                    const util::NameTable &name_table);
+    ~MotorwayHandler() override final;
+
+    // check whether the handler can actually handle the intersection
+    bool canProcess(const NodeID nid,
+                    const EdgeID via_eid,
+                    const Intersection &intersection) const override final;
+
+    // process the intersection
+    Intersection operator()(const NodeID nid,
+                            const EdgeID via_eid,
+                            Intersection intersection) const override final;
+
+  private:
+    Intersection fromMotorway(const EdgeID via_edge, Intersection intersection) const;
+    Intersection fromRamp(const EdgeID via_edge, Intersection intersection) const;
+
+    Intersection fallback(Intersection intersection) const;
+};
+
+} // namespace guidance
+} // namespace extractor
+} // namespace osrm
+
+#endif /*OSRM_EXTRACTOR_GUIDANCE_MOTORWAY_HANDLER_HPP_*/
diff --git a/include/extractor/guidance/roundabout_handler.hpp b/include/extractor/guidance/roundabout_handler.hpp
new file mode 100644
index 0000000..6a48387
--- /dev/null
+++ b/include/extractor/guidance/roundabout_handler.hpp
@@ -0,0 +1,71 @@
+#ifndef OSRM_EXTRACTOR_GUIDANCE_ROUNDABOUT_HANDLER_HPP_
+#define OSRM_EXTRACTOR_GUIDANCE_ROUNDABOUT_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 "util/typedefs.hpp"
+
+#include <utility>
+#include <vector>
+
+namespace osrm
+{
+namespace extractor
+{
+namespace guidance
+{
+
+namespace detail
+{
+struct RoundaboutFlags
+{
+    bool on_roundabout;
+    bool can_enter;
+    bool can_exit_separately;
+};
+} // namespace detail
+
+// The roundabout handler processes all roundabout related instructions.
+// It performs both the distinction between rotaries and roundabouts and
+// assigns appropriate entry/exit instructions.
+class RoundaboutHandler : public IntersectionHandler
+{
+  public:
+    RoundaboutHandler(const util::NodeBasedDynamicGraph &node_based_graph,
+                      const std::vector<QueryNode> &node_info_list,
+                      const util::NameTable &name_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;
+
+    // process the intersection
+    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;
+
+    // decide whether we lookk at a roundabout or a rotary
+    bool isRotary(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,
+                                   const EdgeID via_edge,
+                                   const bool on_roundabout,
+                                   const bool can_exit_roundabout,
+                                   Intersection intersection) const;
+};
+
+} // namespace guidance
+} // namespace extractor
+} // namespace osrm
+
+#endif /*OSRM_EXTRACTOR_GUIDANCE_ROUNDABOUT_HANDLER_HPP_*/
diff --git a/include/extractor/guidance/toolkit.hpp b/include/extractor/guidance/toolkit.hpp
index 45ca897..35fd547 100644
--- a/include/extractor/guidance/toolkit.hpp
+++ b/include/extractor/guidance/toolkit.hpp
@@ -8,14 +8,14 @@
 #include "extractor/compressed_edge_container.hpp"
 #include "extractor/query_node.hpp"
 
-#include "extractor/guidance/discrete_angle.hpp"
 #include "extractor/guidance/classification_data.hpp"
+#include "extractor/guidance/discrete_angle.hpp"
 #include "extractor/guidance/turn_instruction.hpp"
 
 #include <algorithm>
-#include <map>
 #include <cmath>
 #include <cstdint>
+#include <map>
 #include <string>
 
 namespace osrm
@@ -42,22 +42,21 @@ getCoordinateFromCompressedRange(util::Coordinate current_coordinate,
                                  const util::Coordinate final_coordinate,
                                  const std::vector<extractor::QueryNode> &query_nodes)
 {
-    const auto extractCoordinateFromNode = [](const extractor::QueryNode &node) -> util::Coordinate
-    {
+    const auto extractCoordinateFromNode =
+        [](const extractor::QueryNode &node) -> util::Coordinate {
         return {node.lon, node.lat};
     };
     double distance_to_current_coordinate = 0;
     double distance_to_next_coordinate = 0;
 
     // get the length that is missing from the current segment to reach DESIRED_SEGMENT_LENGTH
-    const auto getFactor = [](const double first_distance, const double second_distance)
-    {
+    const auto getFactor = [](const double first_distance, const double second_distance) {
         BOOST_ASSERT(first_distance < detail::DESIRED_SEGMENT_LENGTH);
         double segment_length = second_distance - first_distance;
         BOOST_ASSERT(segment_length > 0);
         BOOST_ASSERT(second_distance >= detail::DESIRED_SEGMENT_LENGTH);
         double missing_distance = detail::DESIRED_SEGMENT_LENGTH - first_distance;
-        return missing_distance / segment_length;
+        return std::max(0., std::min(missing_distance / segment_length, 1.0));
     };
 
     for (auto compressed_geometry_itr = compressed_geometry_begin;
@@ -85,7 +84,8 @@ getCoordinateFromCompressedRange(util::Coordinate current_coordinate,
         util::coordinate_calculation::haversineDistance(current_coordinate, final_coordinate);
 
     // reached point where coordinates switch between
-    if (distance_to_next_coordinate >= detail::DESIRED_SEGMENT_LENGTH)
+    if (distance_to_current_coordinate < detail::DESIRED_SEGMENT_LENGTH &&
+        distance_to_next_coordinate >= detail::DESIRED_SEGMENT_LENGTH)
         return util::coordinate_calculation::interpolateLinear(
             getFactor(distance_to_current_coordinate, distance_to_next_coordinate),
             current_coordinate, final_coordinate);
@@ -104,8 +104,8 @@ getRepresentativeCoordinate(const NodeID from_node,
                             const extractor::CompressedEdgeContainer &compressed_geometries,
                             const std::vector<extractor::QueryNode> &query_nodes)
 {
-    const auto extractCoordinateFromNode = [](const extractor::QueryNode &node) -> util::Coordinate
-    {
+    const auto extractCoordinateFromNode =
+        [](const extractor::QueryNode &node) -> util::Coordinate {
         return {node.lon, node.lat};
     };
 
@@ -298,14 +298,10 @@ inline DirectionModifier getTurnDirection(const double angle)
 // swaps left <-> right modifier types
 inline DirectionModifier mirrorDirectionModifier(const DirectionModifier modifier)
 {
-    const constexpr DirectionModifier results[] = {DirectionModifier::UTurn,
-                                                   DirectionModifier::SharpLeft,
-                                                   DirectionModifier::Left,
-                                                   DirectionModifier::SlightLeft,
-                                                   DirectionModifier::Straight,
-                                                   DirectionModifier::SlightRight,
-                                                   DirectionModifier::Right,
-                                                   DirectionModifier::SharpRight};
+    const constexpr DirectionModifier results[] = {
+        DirectionModifier::UTurn,      DirectionModifier::SharpLeft, DirectionModifier::Left,
+        DirectionModifier::SlightLeft, DirectionModifier::Straight,  DirectionModifier::SlightRight,
+        DirectionModifier::Right,      DirectionModifier::SharpRight};
     return results[modifier];
 }
 
@@ -345,13 +341,14 @@ inline bool requiresNameAnnounced(const std::string &from, const std::string &to
     std::string to_ref;
 
     // Split from the format "{name} ({ref})" -> name, ref
-    auto split = [](const std::string &name, std::string &out_name, std::string &out_ref)
-    {
+    auto split = [](const std::string &name, std::string &out_name, std::string &out_ref) {
         const auto ref_begin = name.find_first_of('(');
         if (ref_begin != std::string::npos)
         {
-            out_name = name.substr(0, ref_begin);
-            out_ref = name.substr(ref_begin + 1, name.find_first_of(')') - 1);
+            if (ref_begin != 0)
+                out_name = name.substr(0, ref_begin - 1);
+            const auto ref_end = name.find_first_of(')');
+            out_ref = name.substr(ref_begin + 1, ref_end - ref_begin - 1);
         }
         else
         {
@@ -363,35 +360,38 @@ inline bool requiresNameAnnounced(const std::string &from, const std::string &to
     split(to, to_name, to_ref);
 
     // check similarity of names
-    auto names_are_empty = from_name.empty() && to_name.empty();
-    auto names_are_equal = from_name == to_name;
-    auto name_is_removed = !from_name.empty() && to_name.empty();
+    const auto names_are_empty = from_name.empty() && to_name.empty();
+    const auto names_are_equal = from_name == to_name;
+    const auto name_is_removed = !from_name.empty() && to_name.empty();
     // references are contained in one another
-    auto refs_are_empty = from_ref.empty() && to_ref.empty();
-    auto ref_is_contained =
-        !from_ref.empty() && !to_ref.empty() &&
+    const auto refs_are_empty = from_ref.empty() && to_ref.empty();
+    const auto ref_is_contained =
+        from_ref.empty() || to_ref.empty() ||
         (from_ref.find(to_ref) != std::string::npos || to_ref.find(from_ref) != std::string::npos);
-    auto ref_is_removed = !from_ref.empty() && to_ref.empty();
+    const auto ref_is_removed = !from_ref.empty() && to_ref.empty();
 
-    auto obvious_change = ref_is_contained || names_are_equal ||
-                          (names_are_empty && 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;
 
     return !obvious_change;
 }
 
-inline int getPriority( const FunctionalRoadClass road_class )
+inline int getPriority(const FunctionalRoadClass road_class)
 {
-    //The road priorities indicate which roads can bee seen as more or less equal.
-    //They are used in Fork-Discovery. Possibly should be moved to profiles post v5?
-    //A fork can happen between road types that are at most 1 priority apart from each other
-    const constexpr int road_priority[] = {10, 0, 10, 2, 10, 4, 10, 6, 10, 8, 10, 11, 10, 12, 10, 14};
+    // The road priorities indicate which roads can bee seen as more or less equal.
+    // They are used in Fork-Discovery. Possibly should be moved to profiles post v5?
+    // A fork can happen between road types that are at most 1 priority apart from each other
+    const constexpr int road_priority[] = {10, 0, 10, 2,  10, 4,  10, 6,
+                                           10, 8, 10, 11, 10, 12, 10, 14};
     return road_priority[static_cast<int>(road_class)];
 }
 
 inline bool canBeSeenAsFork(const FunctionalRoadClass first, const FunctionalRoadClass second)
 {
     // forks require similar road categories
-    // Based on the priorities assigned above, we can set forks only if the road priorities match closely.
+    // Based on the priorities assigned above, we can set forks only if the road priorities match
+    // closely.
     // Potentially we could include features like number of lanes here and others?
     // Should also be moved to profiles
     return std::abs(getPriority(first) - getPriority(second)) <= 1;
diff --git a/include/extractor/guidance/turn_analysis.hpp b/include/extractor/guidance/turn_analysis.hpp
index 538fabc..f52d1b2 100644
--- a/include/extractor/guidance/turn_analysis.hpp
+++ b/include/extractor/guidance/turn_analysis.hpp
@@ -1,20 +1,27 @@
 #ifndef OSRM_EXTRACTOR_TURN_ANALYSIS
 #define OSRM_EXTRACTOR_TURN_ANALYSIS
 
-#include "extractor/guidance/turn_classification.hpp"
+#include "extractor/compressed_edge_container.hpp"
+#include "extractor/guidance/intersection.hpp"
+#include "extractor/guidance/intersection_generator.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/compressed_edge_container.hpp"
 
 #include "util/name_table.hpp"
+#include "util/node_based_graph.hpp"
 
 #include <cstdint>
 
-#include <string>
-#include <vector>
 #include <memory>
-#include <utility>
+#include <string>
 #include <unordered_set>
+#include <utility>
+#include <vector>
 
 namespace osrm
 {
@@ -23,46 +30,6 @@ namespace extractor
 namespace guidance
 {
 
-// What is exposed to the outside
-struct TurnOperation final
-{
-    EdgeID eid;
-    double angle;
-    TurnInstruction instruction;
-};
-
-// For the turn analysis, we require a full list of all connected roads to determine the outcome.
-// Invalid turns can influence the perceived angles
-//
-// aaa(2)aa
-//          a - bbbbb
-// aaa(1)aa
-//
-// will not be perceived as a turn from (1) -> b, and as a U-turn from (1) -> (2).
-// In addition, they can influence whether a turn is obvious or not.
-struct ConnectedRoad final
-{
-    ConnectedRoad(const TurnOperation turn, const bool entry_allowed = false);
-
-    TurnOperation turn;
-    bool entry_allowed; // a turn may be relevant to good instructions, even if we cannot take
-                        // the road
-
-    std::string toString() const
-    {
-        std::string result = "[connection] ";
-        result += std::to_string(turn.eid);
-        result += " allows entry: ";
-        result += std::to_string(entry_allowed);
-        result += " angle: ";
-        result += std::to_string(turn.angle);
-        result += " instruction: ";
-        result += std::to_string(static_cast<std::int32_t>(turn.instruction.type)) + " " +
-                  std::to_string(static_cast<std::int32_t>(turn.instruction.direction_modifier));
-        return result;
-    }
-};
-
 class TurnAnalysis
 {
 
@@ -79,120 +46,14 @@ class TurnAnalysis
 
   private:
     const util::NodeBasedDynamicGraph &node_based_graph;
-    const std::vector<QueryNode> &node_info_list;
-    const RestrictionMap &restriction_map;
-    const std::unordered_set<NodeID> &barrier_nodes;
-    const CompressedEdgeContainer &compressed_edge_container;
-    const util::NameTable &name_table;
-
-    // Check for restrictions/barriers and generate a list of valid and invalid turns present at
-    // the
-    // node reached
-    // from `from_node` via `via_eid`
-    // The resulting candidates have to be analysed for their actual instructions later on.
-    std::vector<ConnectedRoad> getConnectedRoads(const NodeID from_node,
-                                                 const EdgeID via_eid) const;
-
-    // Merge segregated roads to omit invalid turns in favor of treating segregated roads as
-    // one.
-    // This function combines roads the following way:
-    //
-    //     *                           *
-    //     *        is converted to    *
-    //   v   ^                         +
-    //   v   ^                         +
-    //
-    // The treatment results in a straight turn angle of 180º rather than a turn angle of approx
-    // 160
-    std::vector<ConnectedRoad> mergeSegregatedRoads(std::vector<ConnectedRoad> intersection) const;
-
-    // TODO distinguish roundabouts and rotaries
-    // 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.
-    std::vector<ConnectedRoad> handleRoundabouts(const EdgeID via_edge,
-                                                 const bool on_roundabout,
-                                                 const bool can_exit_roundabout,
-                                                 std::vector<ConnectedRoad> intersection) const;
-
-    // Indicates a Junction containing a motoryway
-    bool isMotorwayJunction(const EdgeID via_edge,
-                            const std::vector<ConnectedRoad> &intersection) const;
-
-    // Decide whether a turn is a turn or a ramp access
-    TurnType findBasicTurnType(const EdgeID via_edge, const ConnectedRoad &candidate) const;
-
-    // Get the Instruction for an obvious turn
-    // Instruction will be a silent instruction
-    TurnInstruction getInstructionForObvious(const std::size_t number_of_candidates,
-                                             const EdgeID via_edge,
-                                             const ConnectedRoad &candidate) const;
-
-    // Helper Function that decides between NoTurn or NewName
-    TurnInstruction
-    noTurnOrNewName(const NodeID from, const EdgeID via_edge, const ConnectedRoad &candidate) const;
-
-    // Basic Turn Handling
-
-    // Dead end.
-    std::vector<ConnectedRoad> handleOneWayTurn(std::vector<ConnectedRoad> intersection) const;
-
-    // Mode Changes, new names...
-    std::vector<ConnectedRoad> handleTwoWayTurn(const EdgeID via_edge,
-                                                std::vector<ConnectedRoad> intersection) const;
-
-    // Forks, T intersections and similar
-    std::vector<ConnectedRoad> handleThreeWayTurn(const EdgeID via_edge,
-                                                  std::vector<ConnectedRoad> intersection) const;
-
-    // Handling of turns larger then degree three
-    std::vector<ConnectedRoad> handleComplexTurn(const EdgeID via_edge,
-                                                 std::vector<ConnectedRoad> intersection) const;
-
-    // Any Junction containing motorways
-    std::vector<ConnectedRoad> handleMotorwayJunction(
-        const EdgeID via_edge, std::vector<ConnectedRoad> intersection) const;
-
-    std::vector<ConnectedRoad> handleFromMotorway(const EdgeID via_edge,
-                                                  std::vector<ConnectedRoad> intersection) const;
-
-    std::vector<ConnectedRoad> handleMotorwayRamp(const EdgeID via_edge,
-                                                  std::vector<ConnectedRoad> intersection) const;
+    const IntersectionGenerator intersection_generator;
+    const RoundaboutHandler roundabout_handler;
+    const MotorwayHandler motorway_handler;
+    const TurnHandler turn_handler;
 
     // Utility function, setting basic turn types. Prepares for normal turn handling.
-    std::vector<ConnectedRoad> setTurnTypes(const NodeID from,
-                                            const EdgeID via_edge,
-                                            std::vector<ConnectedRoad> intersection) const;
-
-    // Assignment of specific turn types
-    void assignFork(const EdgeID via_edge, ConnectedRoad &left, ConnectedRoad &right) const;
-    void assignFork(const EdgeID via_edge,
-                    ConnectedRoad &left,
-                    ConnectedRoad &center,
-                    ConnectedRoad &right) const;
-
-    void
-    handleDistinctConflict(const EdgeID via_edge, ConnectedRoad &left, ConnectedRoad &right) const;
-
-    // Type specific fallbacks
-    std::vector<ConnectedRoad>
-    fallbackTurnAssignmentMotorway(std::vector<ConnectedRoad> intersection) const;
-
-    // Classification
-    std::size_t findObviousTurn(const EdgeID via_edge,
-                                const std::vector<ConnectedRoad> &intersection) const;
-    std::pair<std::size_t, std::size_t>
-    findFork(const std::vector<ConnectedRoad> &intersection) const;
-
-    std::vector<ConnectedRoad> assignLeftTurns(const EdgeID via_edge,
-                                               std::vector<ConnectedRoad> intersection,
-                                               const std::size_t starting_at) const;
-    std::vector<ConnectedRoad> assignRightTurns(const EdgeID via_edge,
-                                                std::vector<ConnectedRoad> intersection,
-                                                const std::size_t up_to) const;
-
+    Intersection
+    setTurnTypes(const NodeID from, const EdgeID via_edge, Intersection intersection) const;
 }; // class TurnAnalysis
 
 } // namespace guidance
diff --git a/include/extractor/guidance/turn_handler.hpp b/include/extractor/guidance/turn_handler.hpp
new file mode 100644
index 0000000..3420aa7
--- /dev/null
+++ b/include/extractor/guidance/turn_handler.hpp
@@ -0,0 +1,74 @@
+#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/query_node.hpp"
+
+#include "util/name_table.hpp"
+#include "util/node_based_graph.hpp"
+
+#include <cstddef>
+#include <vector>
+#include <utility>
+
+namespace osrm
+{
+namespace extractor
+{
+namespace guidance
+{
+
+// Intersection handlers deal with all issues related to intersections.
+// They assign appropriate turn operations to the TurnOperations.
+class TurnHandler : public IntersectionHandler
+{
+  public:
+    TurnHandler(const util::NodeBasedDynamicGraph &node_based_graph,
+                const std::vector<QueryNode> &node_info_list,
+                const util::NameTable &name_table);
+    ~TurnHandler() override final;
+
+    // check whether the handler can actually handle the intersection
+    bool canProcess(const NodeID nid,
+                    const EdgeID via_eid,
+                    const Intersection &intersection) const override final;
+
+    // process the intersection
+    Intersection operator()(const NodeID nid,
+                            const EdgeID via_eid,
+                            Intersection intersection) const override final;
+
+  private:
+    // Dead end.
+    Intersection handleOneWayTurn(Intersection intersection) const;
+
+    // Mode Changes, new names...
+    Intersection handleTwoWayTurn(const EdgeID via_edge, Intersection intersection) const;
+
+    // Forks, T intersections and similar
+    Intersection handleThreeWayTurn(const EdgeID via_edge, Intersection intersection) const;
+
+    // Handling of turns larger then degree three
+    Intersection handleComplexTurn(const EdgeID via_edge, Intersection intersection) const;
+
+    void
+    handleDistinctConflict(const EdgeID via_edge, ConnectedRoad &left, ConnectedRoad &right) const;
+
+    // Classification
+    std::size_t findObviousTurn(const EdgeID via_edge, const Intersection &intersection) const;
+    std::pair<std::size_t, std::size_t> findFork(const Intersection &intersection) const;
+
+    Intersection assignLeftTurns(const EdgeID via_edge,
+                                 Intersection intersection,
+                                 const std::size_t starting_at) const;
+    Intersection assignRightTurns(const EdgeID via_edge,
+                                  Intersection intersection,
+                                  const std::size_t up_to) const;
+};
+
+} // namespace guidance
+} // namespace extractor
+} // namespace osrm
+
+#endif /*OSRM_EXTRACTOR_GUIDANCE_TURN_HANDLER_HPP_*/
diff --git a/include/extractor/guidance/turn_instruction.hpp b/include/extractor/guidance/turn_instruction.hpp
index da3da62..d96c007 100644
--- a/include/extractor/guidance/turn_instruction.hpp
+++ b/include/extractor/guidance/turn_instruction.hpp
@@ -89,24 +89,32 @@ struct TurnInstruction
         return TurnInstruction(TurnType::NoTurn, DirectionModifier::UTurn);
     }
 
-    static TurnInstruction REMAIN_ROUNDABOUT(const DirectionModifier modifier)
+    static TurnInstruction REMAIN_ROUNDABOUT(bool is_rotary, 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(const DirectionModifier modifier)
+    static TurnInstruction ENTER_ROUNDABOUT(bool is_rotary, const DirectionModifier modifier)
     {
-        return TurnInstruction(TurnType::EnterRoundabout, modifier);
+        return {is_rotary ? TurnType::EnterRotary : TurnType::EnterRoundabout, modifier};
     }
 
-    static TurnInstruction EXIT_ROUNDABOUT(const DirectionModifier modifier)
+    static TurnInstruction EXIT_ROUNDABOUT(bool is_rotary, const DirectionModifier modifier)
     {
-        return TurnInstruction(TurnType::ExitRoundabout, modifier);
+        return {is_rotary ? TurnType::ExitRotary : TurnType::ExitRoundabout, modifier};
+    }
+
+    static TurnInstruction ENTER_AND_EXIT_ROUNDABOUT(bool is_rotary,
+                                                     const DirectionModifier modifier)
+    {
+        return {is_rotary ? TurnType::EnterAndExitRotary : TurnType::EnterAndExitRoundabout,
+                modifier};
     }
 
     static TurnInstruction SUPPRESSED(const DirectionModifier modifier)
     {
-        return TurnInstruction{TurnType::Suppressed, modifier};
+        return {TurnType::Suppressed, modifier};
     }
 };
 
diff --git a/include/extractor/profile_properties.hpp b/include/extractor/profile_properties.hpp
index 859034c..7976875 100644
--- a/include/extractor/profile_properties.hpp
+++ b/include/extractor/profile_properties.hpp
@@ -11,7 +11,7 @@ namespace extractor
 struct ProfileProperties
 {
     ProfileProperties()
-        : traffic_signal_penalty(0), u_turn_penalty(0), allow_u_turn_at_via(false), use_turn_restrictions(false)
+        : traffic_signal_penalty(0), u_turn_penalty(0), continue_straight_at_waypoint(true), use_turn_restrictions(false)
     {
     }
 
@@ -39,7 +39,7 @@ struct ProfileProperties
     int traffic_signal_penalty;
     //! penalty to do a uturn in deci-seconds
     int u_turn_penalty;
-    bool allow_u_turn_at_via;
+    bool continue_straight_at_waypoint;
     bool use_turn_restrictions;
 };
 }
diff --git a/include/extractor/restriction_parser.hpp b/include/extractor/restriction_parser.hpp
index d2be8c1..8722bf1 100644
--- a/include/extractor/restriction_parser.hpp
+++ b/include/extractor/restriction_parser.hpp
@@ -19,7 +19,7 @@ namespace osrm
 namespace extractor
 {
 
-class ProfileProperties;
+struct ProfileProperties;
 
 /**
  * Parses the relations that represents turn restrictions.
diff --git a/include/server/api/base_parameters_grammar.hpp b/include/server/api/base_parameters_grammar.hpp
index 2f9a95e..4d441a5 100644
--- a/include/server/api/base_parameters_grammar.hpp
+++ b/include/server/api/base_parameters_grammar.hpp
@@ -3,22 +3,15 @@
 
 #include "engine/api/base_parameters.hpp"
 
-#include "engine/polyline_compressor.hpp"
-#include "engine/hint.hpp"
 #include "engine/bearing.hpp"
+#include "engine/hint.hpp"
+#include "engine/polyline_compressor.hpp"
 
-#include <boost/spirit/include/qi_lit.hpp>
-#include <boost/spirit/include/qi_char_.hpp>
-#include <boost/spirit/include/qi_int.hpp>
-#include <boost/spirit/include/qi_real.hpp>
-#include <boost/spirit/include/qi_grammar.hpp>
-#include <boost/spirit/include/qi_action.hpp>
-#include <boost/spirit/include/qi_optional.hpp>
-#include <boost/spirit/include/qi_attr_cast.hpp>
-#include <boost/spirit/include/qi_operator.hpp>
-#include <boost/spirit/include/qi_repeat.hpp>
-#include <boost/spirit/include/qi_as_string.hpp>
+#include <boost/optional.hpp>
+//#define BOOST_SPIRIT_DEBUG
+#include <boost/spirit/include/qi.hpp>
 
+#include <limits>
 #include <string>
 
 namespace osrm
@@ -29,60 +22,99 @@ namespace api
 {
 
 namespace qi = boost::spirit::qi;
+
+template <typename T, char... Fmt> struct no_trailing_dot_policy : qi::real_policies<T>
+{
+    template <typename Iterator> static bool parse_dot(Iterator &first, Iterator const &last)
+    {
+        if (first == last || *first != '.')
+            return false;
+
+        static const constexpr char fmt[sizeof...(Fmt)] = {Fmt...};
+
+        if (first + sizeof(fmt) < last && std::equal(fmt, fmt + sizeof(fmt), first + 1u))
+            return false;
+
+        ++first;
+        return true;
+    }
+
+    template <typename Iterator> static bool parse_exp(Iterator &, const Iterator &)
+    {
+        return false;
+    }
+
+    template <typename Iterator, typename Attribute>
+    static bool parse_exp_n(Iterator &, const Iterator &, Attribute &)
+    {
+        return false;
+    }
+
+    template <typename Iterator, typename Attribute>
+    static bool parse_nan(Iterator &, const Iterator &, Attribute &)
+    {
+        return false;
+    }
+
+    template <typename Iterator, typename Attribute>
+    static bool parse_inf(Iterator &, const Iterator &, Attribute &)
+    {
+        return false;
+    }
+};
+
 struct BaseParametersGrammar : boost::spirit::qi::grammar<std::string::iterator>
 {
     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_)
     {
         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)
-        {
+            [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)
-        {
+        const auto add_hint = [this](const std::string &hint_string) {
             if (hint_string.size() > 0)
             {
                 base_parameters.hints.push_back(engine::Hint::FromBase64(hint_string));
             }
         };
-        const auto add_coordinate = [this](const boost::fusion::vector<double, double> &lonLat)
-        {
+        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)
-        {
+        const auto polyline_to_coordinates = [this](const std::string &polyline) {
             base_parameters.coordinates = engine::decodePolyline(polyline);
         };
 
         alpha_numeral = +qi::char_("a-zA-Z0-9");
         polyline_chars = qi::char_("a-zA-Z0-9_.--[]{}@?|\\%~`^");
-        base64_char = 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=") >> -qi::double_ % ";";
+        radiuses_rule = qi::lit("radiuses=") > -(unlimited | qi::double_) % ";";
         hints_rule =
-            qi::lit("hints=") >>
+            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 = (qi::double_ >> qi::lit(',') >> qi::double_)[add_coordinate];
+            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;
@@ -97,9 +129,11 @@ struct BaseParametersGrammar : boost::spirit::qi::grammar<std::string::iterator>
     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, unsigned char()> base64_char;
     qi::rule<Iterator, std::string()> alpha_numeral, polyline_chars;
+    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 7628e87..6671a58 100644
--- a/include/server/api/match_parameter_grammar.hpp
+++ b/include/server/api/match_parameter_grammar.hpp
@@ -2,15 +2,10 @@
 #define MATCH_PARAMETERS_GRAMMAR_HPP
 
 #include "engine/api/match_parameters.hpp"
-
 #include "server/api/base_parameters_grammar.hpp"
 
-#include <boost/spirit/include/qi_lit.hpp>
-#include <boost/spirit/include/qi_uint.hpp>
-#include <boost/spirit/include/qi_bool.hpp>
-#include <boost/spirit/include/qi_grammar.hpp>
-#include <boost/spirit/include/qi_action.hpp>
-#include <boost/spirit/include/qi_optional.hpp>
+//#define BOOST_SPIRIT_DEBUG
+#include <boost/spirit/include/qi.hpp>
 
 namespace osrm
 {
@@ -31,47 +26,38 @@ struct MatchParametersGrammar final : public BaseParametersGrammar
 
     MatchParametersGrammar() : BaseParametersGrammar(root_rule, parameters)
     {
-        const auto set_geojson_type = [this]()
-        {
+        const auto set_geojson_type = [this] {
             parameters.geometries = engine::api::RouteParameters::GeometriesType::GeoJSON;
         };
-        const auto set_polyline_type = [this]()
-        {
+        const auto set_polyline_type = [this] {
             parameters.geometries = engine::api::RouteParameters::GeometriesType::Polyline;
         };
 
-        const auto set_simplified_type = [this]()
-        {
+        const auto set_simplified_type = [this] {
             parameters.overview = engine::api::RouteParameters::OverviewType::Simplified;
         };
-        const auto set_full_type = [this]()
-        {
+        const auto set_full_type = [this] {
             parameters.overview = engine::api::RouteParameters::OverviewType::Full;
         };
-        const auto set_false_type = [this]()
-        {
+        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)
-        {
+        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_;
+        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_ % ";";
+        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) % '&');
+            query_rule > -qi::lit(".json") > -(qi::lit("?") > (match_rule | base_rule) % '&');
     }
 
     engine::api::MatchParameters parameters;
diff --git a/include/server/api/nearest_parameter_grammar.hpp b/include/server/api/nearest_parameter_grammar.hpp
index e51ccb5..09b6c2a 100644
--- a/include/server/api/nearest_parameter_grammar.hpp
+++ b/include/server/api/nearest_parameter_grammar.hpp
@@ -2,14 +2,10 @@
 #define NEAREST_PARAMETERS_GRAMMAR_HPP
 
 #include "engine/api/nearest_parameters.hpp"
-
 #include "server/api/base_parameters_grammar.hpp"
 
-#include <boost/spirit/include/qi_lit.hpp>
-#include <boost/spirit/include/qi_uint.hpp>
-#include <boost/spirit/include/qi_grammar.hpp>
-#include <boost/spirit/include/qi_action.hpp>
-#include <boost/spirit/include/qi_optional.hpp>
+//#define BOOST_SPIRIT_DEBUG
+#include <boost/spirit/include/qi.hpp>
 
 namespace osrm
 {
@@ -26,13 +22,12 @@ struct NearestParametersGrammar final : public BaseParametersGrammar
 
     NearestParametersGrammar() : BaseParametersGrammar(root_rule, parameters)
     {
-        const auto set_number = [this](const unsigned number)
-        {
+        const auto set_number = [this](const unsigned number) {
             parameters.number_of_results = number;
         };
-        nearest_rule = (qi::lit("number=") >> qi::uint_)[set_number];
+        nearest_rule = (qi::lit("number=") > qi::uint_)[set_number];
         root_rule =
-            query_rule >> -qi::lit(".json") >> -(qi::lit("?") >> (nearest_rule | base_rule) % '&');
+            query_rule > -qi::lit(".json") > -(qi::lit("?") > (nearest_rule | base_rule) % '&');
     }
 
     engine::api::NearestParameters parameters;
diff --git a/include/server/api/parameters_parser.hpp b/include/server/api/parameters_parser.hpp
index 5af5888..161910d 100644
--- a/include/server/api/parameters_parser.hpp
+++ b/include/server/api/parameters_parser.hpp
@@ -30,7 +30,7 @@ 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, 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 224f4a6..c805196 100644
--- a/include/server/api/parsed_url.hpp
+++ b/include/server/api/parsed_url.hpp
@@ -3,6 +3,8 @@
 
 #include "util/coordinate.hpp"
 
+#include <boost/fusion/include/adapt_struct.hpp>
+
 #include <string>
 #include <vector>
 
@@ -13,15 +15,23 @@ namespace server
 namespace api
 {
 
-struct ParsedURL
+struct ParsedURL final
 {
     std::string service;
     unsigned version;
     std::string profile;
     std::string query;
 };
-}
-}
-}
+
+} // 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 d85d435..d4280df 100644
--- a/include/server/api/route_parameters_grammar.hpp
+++ b/include/server/api/route_parameters_grammar.hpp
@@ -2,14 +2,10 @@
 #define ROUTE_PARAMETERS_GRAMMAR_HPP
 
 #include "engine/api/route_parameters.hpp"
-
 #include "server/api/base_parameters_grammar.hpp"
 
-#include <boost/spirit/include/qi_lit.hpp>
-#include <boost/spirit/include/qi_bool.hpp>
-#include <boost/spirit/include/qi_grammar.hpp>
-#include <boost/spirit/include/qi_action.hpp>
-#include <boost/spirit/include/qi_optional.hpp>
+//#define BOOST_SPIRIT_DEBUG
+#include <boost/spirit/include/qi.hpp>
 
 namespace osrm
 {
@@ -31,53 +27,41 @@ struct RouteParametersGrammar : public BaseParametersGrammar
 
     RouteParametersGrammar() : BaseParametersGrammar(root_rule, parameters)
     {
-        const auto set_geojson_type = [this]()
-        {
+        const auto set_geojson_type = [this] {
             parameters.geometries = engine::api::RouteParameters::GeometriesType::GeoJSON;
         };
-        const auto set_polyline_type = [this]()
-        {
+        const auto set_polyline_type = [this] {
             parameters.geometries = engine::api::RouteParameters::GeometriesType::Polyline;
         };
 
-        const auto set_simplified_type = [this]()
-        {
+        const auto set_simplified_type = [this] {
             parameters.overview = engine::api::RouteParameters::OverviewType::Simplified;
         };
-        const auto set_full_type = [this]()
-        {
+        const auto set_full_type = [this] {
             parameters.overview = engine::api::RouteParameters::OverviewType::Full;
         };
-        const auto set_false_type = [this]()
-        {
+        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)
-        {
+        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_uturns = [this](UturnsT &uturns)
-        {
-            parameters.uturns = std::move(uturns);
-        };
+        const auto set_continue_straight = [this](UturnsT continue_straight) { parameters.continue_straight = std::move(continue_straight); };
 
-        alternatives_rule = qi::lit("alternatives=") >> qi::bool_;
-        steps_rule = qi::lit("steps=") >> qi::bool_;
+        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];
-        uturns_rule = qi::lit("uturns=default") | (qi::lit("uturns=") >> qi::bool_)[set_uturns];
+        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 | uturns_rule;
+                     overview_rule | continue_straight_rule;
 
         root_rule =
-            query_rule >> -qi::lit(".json") >> -(qi::lit("?") >> (route_rule | base_rule) % '&');
+            query_rule > -qi::lit(".json") > -(qi::lit("?") > (route_rule | base_rule) % '&');
     }
 
     engine::api::RouteParameters parameters;
@@ -85,7 +69,7 @@ struct RouteParametersGrammar : public BaseParametersGrammar
   private:
     qi::rule<Iterator> root_rule;
     qi::rule<Iterator> route_rule, geometries_rule, overview_rule;
-    qi::rule<Iterator, UturnsT()> uturns_rule;
+    qi::rule<Iterator, UturnsT()> continue_straight_rule;
     qi::rule<Iterator, StepsT()> steps_rule;
     qi::rule<Iterator, AlternativeT()> alternatives_rule;
 };
diff --git a/include/server/api/table_parameter_grammar.hpp b/include/server/api/table_parameter_grammar.hpp
index d5acb04..00fe703 100644
--- a/include/server/api/table_parameter_grammar.hpp
+++ b/include/server/api/table_parameter_grammar.hpp
@@ -2,14 +2,10 @@
 #define TABLE_PARAMETERS_GRAMMAR_HPP
 
 #include "engine/api/table_parameters.hpp"
-
 #include "server/api/base_parameters_grammar.hpp"
 
-#include <boost/spirit/include/qi_lit.hpp>
-#include <boost/spirit/include/qi_uint.hpp>
-#include <boost/spirit/include/qi_grammar.hpp>
-#include <boost/spirit/include/qi_action.hpp>
-#include <boost/spirit/include/qi_optional.hpp>
+//#define BOOST_SPIRIT_DEBUG
+#include <boost/spirit/include/qi.hpp>
 
 namespace osrm
 {
@@ -28,22 +24,28 @@ struct TableParametersGrammar final : public BaseParametersGrammar
 
     TableParametersGrammar() : BaseParametersGrammar(root_rule, parameters)
     {
-        const auto set_destiantions = [this](DestinationsT &dests)
-        {
+        const auto set_destiantions = [this](DestinationsT dests) {
             parameters.destinations = std::move(dests);
         };
-        const auto set_sources = [this](SourcesT &sources)
-        {
+        const auto set_sources = [this](SourcesT sources) {
             parameters.sources = std::move(sources);
         };
-        destinations_rule = (qi::lit("destinations=") >> (qi::ulong_ % ";")[set_destiantions]) |
+// 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=") > (qi::ulong_long % ";")[set_destiantions]) |
+                            qi::lit("destinations=all");
+        sources_rule =
+            (qi::lit("sources=") > (qi::ulong_long % ";")[set_sources]) | qi::lit("sources=all");
+#else
+        destinations_rule = (qi::lit("destinations=") > (qi::ulong_ % ";")[set_destiantions]) |
                             qi::lit("destinations=all");
         sources_rule =
-            (qi::lit("sources=") >> (qi::ulong_ % ";")[set_sources]) | qi::lit("sources=all");
+            (qi::lit("sources=") > (qi::ulong_ % ";")[set_sources]) | qi::lit("sources=all");
+#endif
         table_rule = destinations_rule | sources_rule;
 
         root_rule =
-            query_rule >> -qi::lit(".json") >> -(qi::lit("?") >> (table_rule | base_rule) % '&');
+            query_rule > -qi::lit(".json") > -(qi::lit("?") > (table_rule | base_rule) % '&');
     }
 
     engine::api::TableParameters parameters;
diff --git a/include/server/api/tile_parameter_grammar.hpp b/include/server/api/tile_parameter_grammar.hpp
index 2212b81..ef73357 100644
--- a/include/server/api/tile_parameter_grammar.hpp
+++ b/include/server/api/tile_parameter_grammar.hpp
@@ -3,14 +3,11 @@
 
 #include "engine/api/tile_parameters.hpp"
 
-#include "engine/polyline_compressor.hpp"
 #include "engine/hint.hpp"
+#include "engine/polyline_compressor.hpp"
 
-#include <boost/spirit/include/qi_lit.hpp>
-#include <boost/spirit/include/qi_uint.hpp>
-#include <boost/spirit/include/qi_grammar.hpp>
-#include <boost/spirit/include/qi_action.hpp>
-#include <boost/spirit/include/qi_operator.hpp>
+//#define BOOST_SPIRIT_DEBUG
+#include <boost/spirit/include/qi.hpp>
 
 #include <string>
 
@@ -28,23 +25,15 @@ struct TileParametersGrammar final : boost::spirit::qi::grammar<std::string::ite
 
     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");
+        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");
     }
     engine::api::TileParameters parameters;
 
diff --git a/include/server/api/trip_parameter_grammar.hpp b/include/server/api/trip_parameter_grammar.hpp
index 3165d99..52c6622 100644
--- a/include/server/api/trip_parameter_grammar.hpp
+++ b/include/server/api/trip_parameter_grammar.hpp
@@ -2,15 +2,10 @@
 #define TRIP_PARAMETERS_GRAMMAR_HPP
 
 #include "engine/api/trip_parameters.hpp"
-
 #include "server/api/base_parameters_grammar.hpp"
 
-#include <boost/spirit/include/qi_lit.hpp>
-#include <boost/spirit/include/qi_uint.hpp>
-#include <boost/spirit/include/qi_bool.hpp>
-#include <boost/spirit/include/qi_grammar.hpp>
-#include <boost/spirit/include/qi_action.hpp>
-#include <boost/spirit/include/qi_optional.hpp>
+//#define BOOST_SPIRIT_DEBUG
+#include <boost/spirit/include/qi.hpp>
 
 namespace osrm
 {
@@ -30,33 +25,25 @@ struct TripParametersGrammar final : public BaseParametersGrammar
 
     TripParametersGrammar() : BaseParametersGrammar(root_rule, parameters)
     {
-        const auto set_geojson_type = [this]()
-        {
+        const auto set_geojson_type = [this] {
             parameters.geometries = engine::api::RouteParameters::GeometriesType::GeoJSON;
         };
-        const auto set_polyline_type = [this]()
-        {
+        const auto set_polyline_type = [this] {
             parameters.geometries = engine::api::RouteParameters::GeometriesType::Polyline;
         };
 
-        const auto set_simplified_type = [this]()
-        {
+        const auto set_simplified_type = [this] {
             parameters.overview = engine::api::RouteParameters::OverviewType::Simplified;
         };
-        const auto set_full_type = [this]()
-        {
+        const auto set_full_type = [this] {
             parameters.overview = engine::api::RouteParameters::OverviewType::Full;
         };
-        const auto set_false_type = [this]()
-        {
+        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_steps = [this](const StepsT steps) { parameters.steps = steps; };
 
-        steps_rule = qi::lit("steps=") >> 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] |
@@ -65,7 +52,7 @@ struct TripParametersGrammar final : public BaseParametersGrammar
         trip_rule = steps_rule[set_steps] | geometries_rule | overview_rule;
 
         root_rule =
-            query_rule >> -qi::lit(".json") >> -(qi::lit("?") >> (trip_rule | base_rule) % '&');
+            query_rule > -qi::lit(".json") > -(qi::lit("?") > (trip_rule | base_rule) % '&');
     }
 
     engine::api::TripParameters parameters;
diff --git a/include/server/api/url_parser.hpp b/include/server/api/url_parser.hpp
index 0bac20f..5fcca36 100644
--- a/include/server/api/url_parser.hpp
+++ b/include/server/api/url_parser.hpp
@@ -15,8 +15,8 @@ namespace api
 {
 
 // Starts parsing and iter and modifies it until iter == end or parsing failed
-boost::optional<ParsedURL> parseURL(std::string::iterator &iter, std::string::iterator end);
-// copy on purpose because we need mutability
+boost::optional<ParsedURL> parseURL(std::string::iterator &iter, const std::string::iterator end);
+
 inline boost::optional<ParsedURL> parseURL(std::string url_string)
 {
     auto iter = url_string.begin();
diff --git a/include/storage/storage.hpp b/include/storage/storage.hpp
index 31278aa..f5934dd 100644
--- a/include/storage/storage.hpp
+++ b/include/storage/storage.hpp
@@ -1,3 +1,30 @@
+/*
+
+Copyright (c) 2016, Project OSRM contributors
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+Redistributions of source code must retain the above copyright notice, this list
+of conditions and the following disclaimer.
+Redistributions in binary form must reproduce the above copyright notice, this
+list of conditions and the following disclaimer in the documentation and/or
+other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+*/
+
 #ifndef STORAGE_HPP
 #define STORAGE_HPP
 
diff --git a/include/storage/storage_config.hpp b/include/storage/storage_config.hpp
index e5e0e88..62f2fdd 100644
--- a/include/storage/storage_config.hpp
+++ b/include/storage/storage_config.hpp
@@ -1,3 +1,30 @@
+/*
+
+Copyright (c) 2016, Project OSRM contributors
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+Redistributions of source code must retain the above copyright notice, this list
+of conditions and the following disclaimer.
+Redistributions in binary form must reproduce the above copyright notice, this
+list of conditions and the following disclaimer in the documentation and/or
+other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+*/
+
 #ifndef STORAGE_CONFIG_HPP
 #define STORAGE_CONFIG_HPP
 
diff --git a/include/util/coordinate_calculation.hpp b/include/util/coordinate_calculation.hpp
index 806b538..afa6c65 100644
--- a/include/util/coordinate_calculation.hpp
+++ b/include/util/coordinate_calculation.hpp
@@ -3,7 +3,7 @@
 
 #include "util/coordinate.hpp"
 
-#include <boost/math/constants/constants.hpp>
+#include <boost/optional.hpp>
 
 #include <utility>
 
@@ -14,26 +14,18 @@ namespace util
 namespace coordinate_calculation
 {
 
+namespace detail
+{
 const constexpr long double DEGREE_TO_RAD = 0.017453292519943295769236907684886;
 const constexpr long double RAD_TO_DEGREE = 1. / DEGREE_TO_RAD;
 // earth radius varies between 6,356.750-6,378.135 km (3,949.901-3,963.189mi)
 // The IUGG value for the equatorial radius is 6378.137 km (3963.19 miles)
 const constexpr long double EARTH_RADIUS = 6372797.560856;
-// radius used by WGS84
-const constexpr double EARTH_RADIUS_WGS84 = 6378137.0;
-
-namespace detail
-{
-// earth circumference devided by 2
-const constexpr double MAXEXTENT = EARTH_RADIUS_WGS84 * boost::math::constants::pi<double>();
-// ^ math functions are not constexpr since they have side-effects (setting errno) :(
-const double MAX_LATITUDE = RAD_TO_DEGREE * (2.0 * std::atan(std::exp(180.0 * DEGREE_TO_RAD)) -
-                                             boost::math::constants::half_pi<double>());
-const constexpr double MAX_LONGITUDE = 180.0;
 }
 
+
 //! Takes the squared euclidean distance of the input coordinates. Does not return meters!
-double squaredEuclideanDistance(const FloatCoordinate &lhs, const FloatCoordinate &rhs);
+std::uint64_t squaredEuclideanDistance(const Coordinate &lhs, const Coordinate &rhs);
 
 double haversineDistance(const Coordinate first_coordinate, const Coordinate second_coordinate);
 
@@ -61,30 +53,20 @@ double bearing(const Coordinate first_coordinate, const Coordinate second_coordi
 // Get angle of line segment (A,C)->(C,B)
 double computeAngle(const Coordinate first, const Coordinate second, const Coordinate third);
 
+// find the center of a circle through three coordinates
+boost::optional<Coordinate> circleCenter(const Coordinate first_coordinate,
+                                         const Coordinate second_coordinate,
+                                         const Coordinate third_coordinate);
+
+// find the radius of a circle through three coordinates
+double circleRadius(const Coordinate first_coordinate,
+                    const Coordinate second_coordinate,
+                    const Coordinate third_coordinate);
+
 // factor in [0,1]. Returns point along the straight line between from and to. 0 returns from, 1
 // returns to
 Coordinate interpolateLinear(double factor, const Coordinate from, const Coordinate to);
 
-namespace mercator
-{
-// This is the global default tile size for all Mapbox Vector Tiles
-const constexpr double TILE_SIZE = 256.0;
-// Converts projected mercator degrees to PX
-const constexpr double DEGREE_TO_PX = detail::MAXEXTENT / 180.0;
-
-double degreeToPixel(FloatLatitude lat, unsigned zoom);
-double degreeToPixel(FloatLongitude lon, unsigned zoom);
-FloatLatitude yToLat(const double value);
-double latToY(const FloatLatitude latitude);
-
-FloatCoordinate fromWGS84(const FloatCoordinate &wgs84_coordinate);
-FloatCoordinate toWGS84(const FloatCoordinate &mercator_coordinate);
-
-void xyzToMercator(
-    const int x, const int y, const int z, double &minx, double &miny, double &maxx, double &maxy);
-void xyzToWGS84(
-    const int x, const int y, const int z, double &minx, double &miny, double &maxx, double &maxy);
-} // ns mercator
 } // ns coordinate_calculation
 } // ns util
 } // ns osrm
diff --git a/include/util/name_table.hpp b/include/util/name_table.hpp
index 2f1887a..318cffc 100644
--- a/include/util/name_table.hpp
+++ b/include/util/name_table.hpp
@@ -1,8 +1,8 @@
 #ifndef OSRM_UTIL_NAME_TABLE_HPP
 #define OSRM_UTIL_NAME_TABLE_HPP
 
-#include "util/shared_memory_vector_wrapper.hpp"
 #include "util/range_table.hpp"
+#include "util/shared_memory_vector_wrapper.hpp"
 
 #include <string>
 
diff --git a/include/util/range_table.hpp b/include/util/range_table.hpp
index f012349..c1a12c2 100644
--- a/include/util/range_table.hpp
+++ b/include/util/range_table.hpp
@@ -4,8 +4,8 @@
 #include "util/integer_range.hpp"
 #include "util/shared_memory_vector_wrapper.hpp"
 
-#include <fstream>
 #include <array>
+#include <fstream>
 #include <utility>
 
 namespace osrm
@@ -61,8 +61,7 @@ template <unsigned BLOCK_SIZE, bool USE_SHARED_MEMORY> class RangeTable
     // construct table from length vector
     template <typename VectorT> explicit RangeTable(const VectorT &lengths)
     {
-        const unsigned number_of_blocks = [&lengths]()
-        {
+        const unsigned number_of_blocks = [&lengths]() {
             unsigned num = (lengths.size() + 1) / (BLOCK_SIZE + 1);
             if ((lengths.size() + 1) % (BLOCK_SIZE + 1) != 0)
             {
@@ -169,7 +168,7 @@ template <unsigned BLOCK_SIZE, bool USE_SHARED_MEMORY> class RangeTable
             end_idx = block_offsets[block_idx + 1];
         }
 
-        BOOST_ASSERT(begin_idx < sum_lengths && end_idx <= sum_lengths);
+        BOOST_ASSERT(end_idx <= sum_lengths);
         BOOST_ASSERT(begin_idx <= end_idx);
 
         return irange(begin_idx, end_idx);
diff --git a/include/util/rectangle.hpp b/include/util/rectangle.hpp
index fd0f871..ca7d84b 100644
--- a/include/util/rectangle.hpp
+++ b/include/util/rectangle.hpp
@@ -83,7 +83,7 @@ struct RectangleInt2D
     // This code assumes that we are operating in euclidean space!
     // That means if you just put unprojected lat/lon in here you will
     // get invalid results.
-    double GetMinSquaredDist(const Coordinate location) const
+    std::uint64_t GetMinSquaredDist(const Coordinate location) const
     {
         const bool is_contained = Contains(location);
         if (is_contained)
@@ -116,7 +116,7 @@ struct RectangleInt2D
 
         BOOST_ASSERT(d != INVALID);
 
-        double min_dist = std::numeric_limits<double>::max();
+        std::uint64_t min_dist = std::numeric_limits<std::uint64_t>::max();
         switch (d)
         {
         case NORTH:
@@ -155,7 +155,7 @@ struct RectangleInt2D
             break;
         }
 
-        BOOST_ASSERT(min_dist < std::numeric_limits<double>::max());
+        BOOST_ASSERT(min_dist < std::numeric_limits<std::uint64_t>::max());
 
         return min_dist;
     }
diff --git a/include/util/static_rtree.hpp b/include/util/static_rtree.hpp
index 6924d94..32bd60b 100644
--- a/include/util/static_rtree.hpp
+++ b/include/util/static_rtree.hpp
@@ -5,11 +5,12 @@
 #include "util/hilbert_value.hpp"
 #include "util/rectangle.hpp"
 #include "util/shared_memory_vector_wrapper.hpp"
-
 #include "util/bearing.hpp"
-#include "util/integer_range.hpp"
 #include "util/exception.hpp"
+#include "util/integer_range.hpp"
 #include "util/typedefs.hpp"
+#include "util/coordinate_calculation.hpp"
+#include "util/web_mercator.hpp"
 
 #include "osrm/coordinate.hpp"
 
@@ -42,7 +43,7 @@ template <class EdgeDataT,
           class CoordinateListT = std::vector<Coordinate>,
           bool UseSharedMemory = false,
           std::uint32_t BRANCHING_FACTOR = 64,
-          std::uint32_t LEAF_NODE_SIZE = 1024>
+          std::uint32_t LEAF_NODE_SIZE = 256>
 class StaticRTree
 {
   public:
@@ -101,7 +102,7 @@ class StaticRTree
             return other.squared_min_dist < squared_min_dist;
         }
 
-        double squared_min_dist;
+        std::uint64_t squared_min_dist;
         QueryNodeType node;
     };
 
@@ -145,9 +146,9 @@ class StaticRTree
 
                     Coordinate current_centroid = coordinate_calculation::centroid(
                         coordinate_list[current_element.u], coordinate_list[current_element.v]);
-                    current_centroid.lat = FixedLatitude(
-                        COORDINATE_PRECISION *
-                        coordinate_calculation::mercator::latToY(toFloating(current_centroid.lat)));
+                    current_centroid.lat =
+                        FixedLatitude(COORDINATE_PRECISION *
+                                      web_mercator::latToY(toFloating(current_centroid.lat)));
 
                     current_wrapper.m_hilbert_value = hilbertCode(current_centroid);
                 }
@@ -327,10 +328,10 @@ class StaticRTree
     {
         const Rectangle projected_rectangle{
             search_rectangle.min_lon, search_rectangle.max_lon,
-            toFixed(FloatLatitude{coordinate_calculation::mercator::latToY(
-                toFloating(FixedLatitude(search_rectangle.min_lat)))}),
-            toFixed(FloatLatitude{coordinate_calculation::mercator::latToY(
-                toFloating(FixedLatitude(search_rectangle.max_lat)))})};
+            toFixed(FloatLatitude{
+                web_mercator::latToY(toFloating(FixedLatitude(search_rectangle.min_lat)))}),
+            toFixed(FloatLatitude{
+                web_mercator::latToY(toFloating(FixedLatitude(search_rectangle.max_lat)))})};
         std::vector<EdgeDataT> results;
 
         std::queue<TreeNode> traversal_queue;
@@ -409,12 +410,12 @@ class StaticRTree
     Nearest(const Coordinate input_coordinate, const FilterT filter, const TerminationT terminate)
     {
         std::vector<EdgeDataT> results;
-        auto projected_coordinate = coordinate_calculation::mercator::fromWGS84(input_coordinate);
+        auto projected_coordinate = web_mercator::fromWGS84(input_coordinate);
         Coordinate fixed_projected_coordinate{projected_coordinate};
 
         // initialize queue with root element
         std::priority_queue<QueryCandidate> traversal_queue;
-        traversal_queue.push(QueryCandidate{0.f, m_search_tree[0]});
+        traversal_queue.push(QueryCandidate{0, m_search_tree[0]});
 
         while (!traversal_queue.empty())
         {
@@ -427,8 +428,8 @@ class StaticRTree
                     current_query_node.node.template get<TreeNode>();
                 if (current_tree_node.child_is_on_disk)
                 {
-                    ExploreLeafNode(current_tree_node.children[0], projected_coordinate,
-                                    traversal_queue);
+                    ExploreLeafNode(current_tree_node.children[0], fixed_projected_coordinate,
+                                    projected_coordinate, traversal_queue);
                 }
                 else
                 {
@@ -439,11 +440,6 @@ class StaticRTree
             {
                 // inspecting an actual road segment
                 auto &current_candidate = current_query_node.node.template get<CandidateSegment>();
-                if (terminate(results.size(), current_candidate))
-                {
-                    traversal_queue = std::priority_queue<QueryCandidate>{};
-                    break;
-                }
 
                 auto use_segment = filter(current_candidate);
                 if (!use_segment.first && !use_segment.second)
@@ -455,6 +451,12 @@ class StaticRTree
 
                 // store phantom node in result vector
                 results.push_back(std::move(current_candidate.data));
+
+                if (terminate(results.size(), current_candidate))
+                {
+                    traversal_queue = std::priority_queue<QueryCandidate>{};
+                    break;
+                }
             }
         }
 
@@ -464,6 +466,7 @@ class StaticRTree
   private:
     template <typename QueueT>
     void ExploreLeafNode(const std::uint32_t leaf_id,
+                         const Coordinate projected_input_coordinate_fixed,
                          const FloatCoordinate &projected_input_coordinate,
                          QueueT &traversal_queue)
     {
@@ -473,22 +476,19 @@ class StaticRTree
         // current object represents a block on disk
         for (const auto i : irange(0u, current_leaf_node.object_count))
         {
-            auto &current_edge = current_leaf_node.objects[i];
-            auto projected_u =
-                coordinate_calculation::mercator::fromWGS84((*m_coordinate_list)[current_edge.u]);
-            auto projected_v =
-                coordinate_calculation::mercator::fromWGS84((*m_coordinate_list)[current_edge.v]);
+            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]);
 
             FloatCoordinate projected_nearest;
             std::tie(std::ignore, projected_nearest) =
                 coordinate_calculation::projectPointOnSegment(projected_u, projected_v,
                                                               projected_input_coordinate);
 
-            auto squared_distance = coordinate_calculation::squaredEuclideanDistance(
-                projected_input_coordinate, projected_nearest);
+            const auto squared_distance = coordinate_calculation::squaredEuclideanDistance(
+                projected_input_coordinate_fixed, projected_nearest);
             // distance must be non-negative
             BOOST_ASSERT(0. <= squared_distance);
-
             traversal_queue.push(
                 QueryCandidate{squared_distance, CandidateSegment{Coordinate{projected_nearest},
                                                                   std::move(current_edge)}});
@@ -540,10 +540,10 @@ class StaticRTree
             BOOST_ASSERT(objects[i].u < coordinate_list.size());
             BOOST_ASSERT(objects[i].v < coordinate_list.size());
 
-            Coordinate projected_u{coordinate_calculation::mercator::fromWGS84(
-                Coordinate{coordinate_list[objects[i].u]})};
-            Coordinate projected_v{coordinate_calculation::mercator::fromWGS84(
-                Coordinate{coordinate_list[objects[i].v]})};
+            Coordinate projected_u{
+                web_mercator::fromWGS84(Coordinate{coordinate_list[objects[i].u]})};
+            Coordinate projected_v{
+                web_mercator::fromWGS84(Coordinate{coordinate_list[objects[i].v]})};
 
             BOOST_ASSERT(toFloating(projected_u.lon) <= FloatLongitude(180.));
             BOOST_ASSERT(toFloating(projected_u.lon) >= FloatLongitude(-180.));
diff --git a/include/util/viewport.hpp b/include/util/viewport.hpp
index fd0ac73..211aa57 100644
--- a/include/util/viewport.hpp
+++ b/include/util/viewport.hpp
@@ -2,7 +2,7 @@
 #define UTIL_VIEWPORT_HPP
 
 #include "util/coordinate.hpp"
-#include "util/coordinate_calculation.hpp"
+#include "util/web_mercator.hpp"
 
 #include <boost/assert.hpp>
 
@@ -23,27 +23,27 @@ namespace detail
 static constexpr unsigned MAX_ZOOM = 18;
 static constexpr unsigned MIN_ZOOM = 1;
 // this is an upper bound to current display sizes
-static constexpr double VIEWPORT_WIDTH = 8 * coordinate_calculation::mercator::TILE_SIZE;
-static constexpr double VIEWPORT_HEIGHT = 5 * coordinate_calculation::mercator::TILE_SIZE;
+static constexpr double VIEWPORT_WIDTH = 8 * web_mercator::TILE_SIZE;
+static constexpr double VIEWPORT_HEIGHT = 5 * web_mercator::TILE_SIZE;
 static double INV_LOG_2 = 1. / std::log(2);
 }
 
-unsigned getFittedZoom(util::Coordinate south_west, util::Coordinate north_east)
+inline unsigned getFittedZoom(util::Coordinate south_west, util::Coordinate north_east)
 {
-    const auto min_x = coordinate_calculation::mercator::degreeToPixel(toFloating(south_west.lon), detail::MAX_ZOOM);
-    const auto max_y = coordinate_calculation::mercator::degreeToPixel(toFloating(south_west.lat), detail::MAX_ZOOM);
-    const auto max_x = coordinate_calculation::mercator::degreeToPixel(toFloating(north_east.lon), detail::MAX_ZOOM);
-    const auto min_y = coordinate_calculation::mercator::degreeToPixel(toFloating(north_east.lat), detail::MAX_ZOOM);
+    const auto min_x = web_mercator::degreeToPixel(toFloating(south_west.lon), detail::MAX_ZOOM);
+    const auto max_y = web_mercator::degreeToPixel(toFloating(south_west.lat), detail::MAX_ZOOM);
+    const auto max_x = web_mercator::degreeToPixel(toFloating(north_east.lon), detail::MAX_ZOOM);
+    const auto min_y = web_mercator::degreeToPixel(toFloating(north_east.lat), detail::MAX_ZOOM);
     const double width_ratio = (max_x - min_x) / detail::VIEWPORT_WIDTH;
     const double height_ratio = (max_y - min_y) / detail::VIEWPORT_HEIGHT;
-    const auto zoom = detail::MAX_ZOOM - std::max(std::log(width_ratio), std::log(height_ratio)) * detail::INV_LOG_2;
+    const auto zoom = detail::MAX_ZOOM -
+                      std::max(std::log(width_ratio), std::log(height_ratio)) * detail::INV_LOG_2;
 
     if (std::isfinite(zoom))
         return std::max<unsigned>(detail::MIN_ZOOM, zoom);
     else
-      return detail::MIN_ZOOM;
+        return detail::MIN_ZOOM;
 }
-
 }
 }
 }
diff --git a/include/util/web_mercator.hpp b/include/util/web_mercator.hpp
new file mode 100644
index 0000000..8c23de1
--- /dev/null
+++ b/include/util/web_mercator.hpp
@@ -0,0 +1,127 @@
+#ifndef OSRM_WEB_MERCATOR_HPP
+#define OSRM_WEB_MERCATOR_HPP
+
+#include "util/coordinate.hpp"
+
+#include <boost/math/constants/constants.hpp>
+
+namespace osrm
+{
+namespace util
+{
+namespace web_mercator
+{
+namespace detail
+{
+const constexpr long double DEGREE_TO_RAD = 0.017453292519943295769236907684886;
+const constexpr long double RAD_TO_DEGREE = 1. / DEGREE_TO_RAD;
+// radius used by WGS84
+const constexpr double EARTH_RADIUS_WGS84 = 6378137.0;
+// earth circumference devided by 2
+const constexpr double MAXEXTENT = EARTH_RADIUS_WGS84 * boost::math::constants::pi<double>();
+// ^ math functions are not constexpr since they have side-effects (setting errno) :(
+const constexpr double MAX_LATITUDE = 85.;
+const constexpr double MAX_LONGITUDE = 180.0;
+}
+
+// Converts projected mercator degrees to PX
+const constexpr double DEGREE_TO_PX = detail::MAXEXTENT / 180.0;
+// This is the global default tile size for all Mapbox Vector Tiles
+const constexpr double TILE_SIZE = 256.0;
+
+inline FloatLatitude yToLat(const double y)
+{
+    const auto clamped_y = std::max(-180., std::min(180., 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.);
+}
+
+inline double latToY(const FloatLatitude latitude)
+{
+    // apparently this is the (faster) version of the canonical log(tan()) version
+    const double f = std::sin(detail::DEGREE_TO_RAD * static_cast<double>(latitude));
+    const double y = detail::RAD_TO_DEGREE * 0.5 * std::log((1 + f) / (1 - f));
+    const auto clamped_y = std::max(-180., std::min(180., y));
+    return clamped_y;
+}
+
+inline FloatLatitude clamp(const FloatLatitude lat)
+{
+    return std::max(std::min(lat, FloatLatitude(detail::MAX_LATITUDE)),
+                    FloatLatitude(-detail::MAX_LATITUDE));
+}
+
+inline FloatLongitude clamp(const FloatLongitude lon)
+{
+    return std::max(std::min(lon, FloatLongitude(detail::MAX_LONGITUDE)),
+                    FloatLongitude(-detail::MAX_LONGITUDE));
+}
+
+inline void pixelToDegree(const double shift, double &x, double &y)
+{
+    const double b = shift / 2.0;
+    x = (x - b) / shift * 360.0;
+    // FIXME needs to be simplified
+    const double g = (y - b) / -(shift / (2 * M_PI)) / detail::DEGREE_TO_RAD;
+    static_assert(detail::DEGREE_TO_RAD / (2 * M_PI) - 1 / 360. < 0.0001, "");
+    y = static_cast<double>(yToLat(g));
+}
+
+inline double degreeToPixel(FloatLongitude lon, unsigned zoom)
+{
+    const double shift = (1u << zoom) * TILE_SIZE;
+    const double b = shift / 2.0;
+    const double x = b * (1 + static_cast<double>(lon) / 180.0);
+    return x;
+}
+
+inline double degreeToPixel(FloatLatitude lat, unsigned zoom)
+{
+    const double shift = (1u << zoom) * TILE_SIZE;
+    const double b = shift / 2.0;
+    const double y = b * (1. - latToY(lat) / 180.);
+    return y;
+}
+
+inline FloatCoordinate fromWGS84(const FloatCoordinate &wgs84_coordinate)
+{
+    return {wgs84_coordinate.lon, FloatLatitude{latToY(wgs84_coordinate.lat)}};
+}
+
+inline FloatCoordinate toWGS84(const FloatCoordinate &mercator_coordinate)
+{
+    return {mercator_coordinate.lon, yToLat(static_cast<double>(mercator_coordinate.lat))};
+}
+
+// Converts a WMS tile coordinate (z,x,y) into a wgs bounding box
+inline void xyzToWGS84(
+    const int x, const int y, const int z, double &minx, double &miny, double &maxx, double &maxy)
+{
+    minx = x * TILE_SIZE;
+    miny = (y + 1.0) * TILE_SIZE;
+    maxx = (x + 1.0) * TILE_SIZE;
+    maxy = y * TILE_SIZE;
+    // 2^z * TILE_SIZE
+    const double shift = (1u << static_cast<unsigned>(z)) * TILE_SIZE;
+    pixelToDegree(shift, minx, miny);
+    pixelToDegree(shift, maxx, maxy);
+}
+
+// Converts a WMS tile coordinate (z,x,y) into a mercator bounding box
+inline void xyzToMercator(
+    const int x, const int y, const int z, double &minx, double &miny, double &maxx, double &maxy)
+{
+    xyzToWGS84(x, y, z, minx, miny, maxx, maxy);
+
+    minx = static_cast<double>(clamp(util::FloatLongitude(minx))) * DEGREE_TO_PX;
+    miny = latToY(clamp(util::FloatLatitude(miny))) * DEGREE_TO_PX;
+    maxx = static_cast<double>(clamp(util::FloatLongitude(maxx))) * DEGREE_TO_PX;
+    maxy = latToY(clamp(util::FloatLatitude(maxy))) * DEGREE_TO_PX;
+}
+}
+}
+}
+
+#endif
diff --git a/profiles/bicycle.lua b/profiles/bicycle.lua
index 5575747..84e02d8 100644
--- a/profiles/bicycle.lua
+++ b/profiles/bicycle.lua
@@ -6,9 +6,9 @@ local limit = require("lib/maxspeed").limit
 -- Begin of globals
 barrier_whitelist = { [""] = true, ["cycle_barrier"] = true, ["bollard"] = true, ["entrance"] = true, ["cattle_grid"] = true, ["border_control"] = true, ["toll_booth"] = true, ["sally_port"] = true, ["gate"] = true, ["no"] = true, ["block"] = true }
 access_tag_whitelist = { ["yes"] = true, ["permissive"] = true, ["designated"] = true }
-access_tag_blacklist = { ["no"] = true, ["private"] = true, ["agricultural"] = true, ["forestry"] = true }
+access_tag_blacklist = { ["no"] = true, ["private"] = true, ["agricultural"] = true, ["forestry"] = true, ["delivery"] = true }
 access_tag_restricted = { ["destination"] = true, ["delivery"] = true }
-access_tags_hierachy = { "bicycle", "vehicle", "access" }
+access_tags_hierarchy = { "bicycle", "vehicle", "access" }
 cycleway_tags = {["track"]=true,["lane"]=true,["opposite"]=true,["opposite_lane"]=true,["opposite_track"]=true,["share_busway"]=true,["sharrow"]=true,["shared"]=true }
 service_tag_restricted = { ["parking_aisle"] = true }
 restriction_exception_tags = { "bicycle", "vehicle", "access" }
@@ -91,10 +91,10 @@ surface_speeds = {
 }
 
 -- these need to be global because they are accesed externaly
-properties.traffic_signal_penalty = 2
-properties.use_turn_restrictions  = false
-properties.u_turn_penalty         = 20
-properties.allow_u_turn_at_via    = true
+properties.traffic_signal_penalty        = 2
+properties.use_turn_restrictions         = false
+properties.u_turn_penalty                = 20
+properties.continue_straight_at_waypoint = false
 
 local obey_oneway               = true
 local ignore_areas              = true
@@ -131,7 +131,7 @@ function node_function (node, result)
   local highway = node:get_value_by_key("highway")
   local is_crossing = highway and highway == "crossing"
 
-  local access = find_access_tag(node, access_tags_hierachy)
+  local access = find_access_tag(node, access_tags_hierarchy)
   if access and access ~= "" then
     -- access restrictions on crossing nodes are not relevant for
     -- the traffic on the road
@@ -180,7 +180,7 @@ function way_function (way, result)
   end
 
   -- access
-  local access = find_access_tag(way, access_tags_hierachy)
+  local access = find_access_tag(way, access_tags_hierarchy)
   if access and access_tag_blacklist[access] then
     return
   end
diff --git a/profiles/car.lua b/profiles/car.lua
index 8b1e7f2..4033f31 100644
--- a/profiles/car.lua
+++ b/profiles/car.lua
@@ -5,10 +5,9 @@ local find_access_tag = require("lib/access").find_access_tag
 -- 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 }
 access_tag_whitelist = { ["yes"] = true, ["motorcar"] = true, ["motor_vehicle"] = true, ["vehicle"] = true, ["permissive"] = true, ["designated"] = true, ["destination"] = true }
-access_tag_blacklist = { ["no"] = true, ["private"] = true, ["agricultural"] = true, ["forestry"] = true, ["emergency"] = true, ["psv"] = true }
+access_tag_blacklist = { ["no"] = true, ["private"] = true, ["agricultural"] = true, ["forestry"] = true, ["emergency"] = true, ["psv"] = true, ["delivery"] = true }
 access_tag_restricted = { ["destination"] = true, ["delivery"] = true }
-access_tags = { "motorcar", "motor_vehicle", "vehicle" }
-access_tags_hierachy = { "motorcar", "motor_vehicle", "vehicle", "access" }
+access_tags_hierarchy = { "motorcar", "motor_vehicle", "vehicle", "access" }
 service_tag_restricted = { ["parking_aisle"] = true }
 restriction_exception_tags = { "motorcar", "motor_vehicle", "vehicle" }
 
@@ -125,13 +124,15 @@ maxspeed_table = {
   ["gb:motorway"] = (70*1609)/1000,
   ["uk:nsl_single"] = (60*1609)/1000,
   ["uk:nsl_dual"] = (70*1609)/1000,
-  ["uk:motorway"] = (70*1609)/1000
+  ["uk:motorway"] = (70*1609)/1000,
+  ["none"] = 140
 }
 
 -- set profile properties
 properties.u_turn_penalty                  = 20
 properties.traffic_signal_penalty          = 2
 properties.use_turn_restrictions           = true
+properties.continue_straight_at_waypoint   = true
 
 local side_road_speed_multiplier = 0.8
 
@@ -181,7 +182,7 @@ end
 
 function node_function (node, result)
   -- parse access and barrier tags
-  local access = find_access_tag(node, access_tags_hierachy)
+  local access = find_access_tag(node, access_tags_hierarchy)
   if access and access ~= "" then
     if access_tag_blacklist[access] then
       result.barrier = true
@@ -238,7 +239,7 @@ function way_function (way, result)
   end
 
   -- Check if we are allowed to access the way
-  local access = find_access_tag(way, access_tags_hierachy)
+  local access = find_access_tag(way, access_tags_hierarchy)
   if access_tag_blacklist[access] then
     return
   end
diff --git a/profiles/foot.lua b/profiles/foot.lua
index af1f76e..04f16c2 100644
--- a/profiles/foot.lua
+++ b/profiles/foot.lua
@@ -5,9 +5,9 @@ local find_access_tag = require("lib/access").find_access_tag
 -- Begin of globals
 barrier_whitelist = { [""] = true, ["cycle_barrier"] = true, ["bollard"] = true, ["entrance"] = true, ["cattle_grid"] = true, ["border_control"] = true, ["toll_booth"] = true, ["sally_port"] = true, ["gate"] = true, ["no"] = true, ["block"] = true}
 access_tag_whitelist = { ["yes"] = true, ["foot"] = true, ["permissive"] = true, ["designated"] = true  }
-access_tag_blacklist = { ["no"] = true, ["private"] = true, ["agricultural"] = true, ["forestry"] = true }
+access_tag_blacklist = { ["no"] = true, ["private"] = true, ["agricultural"] = true, ["forestry"] = true, ["delivery"] = true }
 access_tag_restricted = { ["destination"] = true, ["delivery"] = true }
-access_tags_hierachy = { "foot", "access" }
+access_tags_hierarchy = { "foot", "access" }
 service_tag_restricted = { ["parking_aisle"] = true }
 ignore_in_grid = { ["ferry"] = true }
 restriction_exception_tags = { "foot" }
@@ -64,10 +64,10 @@ leisure_speeds = {
   ["track"] = walking_speed
 }
 
-properties.traffic_signal_penalty   = 2
-properties.u_turn_penalty           = 2
-properties.use_turn_restrictions    = false
-properties.allow_u_turn_at_via      = true
+properties.traffic_signal_penalty        = 2
+properties.u_turn_penalty                = 2
+properties.use_turn_restrictions         = false
+properties.continue_straight_at_waypoint = false
 
 local fallback_names     = true
 
@@ -79,7 +79,7 @@ end
 
 function node_function (node, result)
   local barrier = node:get_value_by_key("barrier")
-  local access = find_access_tag(node, access_tags_hierachy)
+  local access = find_access_tag(node, access_tags_hierarchy)
   local traffic_signal = node:get_value_by_key("highway")
 
   -- flag node if it carries a traffic light
@@ -131,7 +131,7 @@ function way_function (way, result)
   end
 
   -- access
-  local access = find_access_tag(way, access_tags_hierachy)
+  local access = find_access_tag(way, access_tags_hierarchy)
   if access_tag_blacklist[access] then
     return
   end
diff --git a/profiles/lib/access.lua b/profiles/lib/access.lua
index 3fd4ff6..3433c65 100644
--- a/profiles/lib/access.lua
+++ b/profiles/lib/access.lua
@@ -2,8 +2,8 @@ local ipairs = ipairs
 
 local Access = {}
 
-function Access.find_access_tag(source,access_tags_hierachy)
-    for i,v in ipairs(access_tags_hierachy) do
+function Access.find_access_tag(source,access_tags_hierarchy)
+    for i,v in ipairs(access_tags_hierarchy) do
         local tag = source:get_value_by_key(v)
         if tag and tag ~= '' then
             return tag
diff --git a/profiles/testbot.lua b/profiles/testbot.lua
index aecffae..8bad810 100644
--- a/profiles/testbot.lua
+++ b/profiles/testbot.lua
@@ -16,10 +16,10 @@ speed_profile = {
 
 -- these settings are read directly by osrm
 
-properties.allow_u_turn_at_via     = false
-properties.use_turn_restrictions   = true
-properties.traffic_signal_penalty  = 7     -- seconds
-properties.u_turn_penalty          = 20
+properties.continue_straight_at_waypoint = true
+properties.use_turn_restrictions         = true
+properties.traffic_signal_penalty        = 7     -- seconds
+properties.u_turn_penalty                = 20
 
 function limit_speed(speed, limits)
   -- don't use ipairs(), since it stops at the first nil value
diff --git a/scripts/poly2req.js b/scripts/poly2req.js
new file mode 100755
index 0000000..5b446f3
--- /dev/null
+++ b/scripts/poly2req.js
@@ -0,0 +1,132 @@
+#!/usr/bin/env node
+
+'use strict';
+
+let fs = require('fs');
+
+let VERSION = "route_5.0";
+let SAMPLE_SIZE = 20;
+let NUM_REQUEST = 1000;
+let NUM_COORDS = 2;
+let PORT = 5000;
+let url_templates = {
+  "route_5.0": "http://127.0.0.1:{port}/route/v1/driving/{coords}?steps=false&alternatives=false",
+  "route_4.9": "http://127.0.0.1:{port}/viaroute?{coords}&instructions=false&alt=false",
+  "table_5.0": "http://127.0.0.1:{port}/table/v1/driving/{coords}",
+  "table_4.9": "http://127.0.0.1:{port}/table?{coords}"
+};
+
+let coord_templates =  {
+  "route_5.0": "{lon},{lat}",
+  "route_4.9": "loc={lat},{lon}",
+  "table_5.0": "{lon},{lat}",
+  "table_4.9": "loc={lat},{lon}"
+};
+
+let coords_separators = {
+  "route_5.0": ";",
+  "route_4.9": "&",
+  "table_5.0": ";",
+  "table_4.9": "&"
+};
+var axis = "distance";
+
+var sw = [Number.MAX_VALUE, Number.MAX_VALUE];
+var ne = [Number.MIN_VALUE, Number.MIN_VALUE];
+
+if (process.argv.length > 2 && process.argv[2] == "planet")
+{
+  sw = [-180., -85.];
+  ne = [180., 85.];
+}
+if (process.argv.length > 2 && process.argv[2] == "us")
+{
+  sw = [-127., 24.];
+  ne = [-67., 48.];
+}
+else if (process.argv.length > 2 && process.argv[2] == "dc")
+{
+  sw = [-77.138, 38.808];
+  ne = [-76.909, 38.974];
+}
+else if (process.argv.length > 2)
+{
+  let monaco_poly_path = process.argv[2];
+  let poly_data = fs.readFileSync(monaco_poly_path, 'utf-8');
+
+  // lets assume there is only one ring
+  // cut of name and ring number and the two END statements
+  let coordinates = poly_data.split('\n')
+    .filter((l) => l != '')
+    .slice(2, -2).map((coord_line) => coord_line.split(' ')
+    .filter((elem) => elem != ''))
+    .map((coord) => [parseFloat(coord[0]), parseFloat(coord[1])]);
+
+  coordinates.forEach((c) => {
+    sw[0] = Math.min(sw[0], c[0]);
+    sw[1] = Math.min(sw[1], c[1]);
+    ne[0] = Math.max(ne[0], c[0]);
+    ne[1] = Math.max(ne[1], c[1]);
+  });
+}
+
+if (process.argv.length > 3)
+{
+  axis = process.argv[3];
+}
+
+console.error(sw);
+console.error(ne);
+
+// Yes this an own seeded random number generator because its only a few lines
+var seed = 0x1337;
+function seededRandom(min, max) {
+  seed = (seed * 9301 + 49297) % 233280;
+  var rnd = seed / 233280;
+  return min + rnd * (max - min);
+}
+
+function getRandomCoordinate() {
+  let lon = seededRandom(sw[0], ne[0]);
+  let lat = seededRandom(sw[1], ne[1]);
+  return [lon, lat];
+}
+
+function makeQuery(coords) {
+  let coords_string = coords.map((c) => coord_templates[VERSION].replace("{lon}", c[0]).replace("{lat}", c[1])).join(coords_separators[VERSION]);
+  return url_templates[VERSION].replace("{coords}", coords_string).replace("{port}", PORT);
+}
+
+if (axis == "distance")
+{
+  for (var i = 0; i < NUM_REQUEST; ++i)
+  {
+    var coords = [];
+    for (var j = 0; j < NUM_COORDS; ++j)
+    {
+      coords.push(getRandomCoordinate());
+    }
+    console.log(makeQuery(coords));
+  }
+}
+else if (axis == "waypoints")
+{
+  for (var power = 0; power <= 1; ++power)
+  {
+    for (var factor = 1; factor <= 10; ++factor)
+    {
+      let num_coords = factor*Math.pow(10, power);
+      console.error(num_coords);
+      for (var i = 0; i < SAMPLE_SIZE; ++i)
+      {
+        var coords = [];
+        for (var j = 0; j < num_coords; ++j)
+        {
+          coords.push(getRandomCoordinate());
+        }
+        console.log(makeQuery(coords));
+      }
+    }
+  }
+}
+
diff --git a/scripts/timer.sh b/scripts/timer.sh
new file mode 100755
index 0000000..39d8e5b
--- /dev/null
+++ b/scripts/timer.sh
@@ -0,0 +1,12 @@
+#!/bin/bash
+
+TIMINGS_FILE=/tmp/osrm.timings
+NAME=$1
+CMD=${@:2}
+START=$(date "+%s.%N")
+/bin/bash -c "$CMD"
+END=$(date "+%s.%N")
+TIME="$(echo "$END - $START" | bc)s"
+NEW_ENTRY="$NAME\t$TIME\t$(date -Iseconds)"
+
+echo -e "$NEW_ENTRY" >> $TIMINGS_FILE
diff --git a/src/benchmarks/CMakeLists.txt b/src/benchmarks/CMakeLists.txt
new file mode 100644
index 0000000..754dc75
--- /dev/null
+++ b/src/benchmarks/CMakeLists.txt
@@ -0,0 +1,20 @@
+file(GLOB RTreeBenchmarkSources
+    *.cpp)
+
+add_executable(rtree-bench
+	EXCLUDE_FROM_ALL
+	${RTreeBenchmarkSources}
+	$<TARGET_OBJECTS:UTIL>)
+
+target_include_directories(rtree-bench
+	PUBLIC
+	${PROJECT_SOURCE_DIR}/unit_tests)
+
+target_link_libraries(rtree-bench
+	${Boost_LIBRARIES}
+	${CMAKE_THREAD_LIBS_INIT}
+	${TBB_LIBRARIES})
+
+add_custom_target(benchmarks
+	DEPENDS
+	rtree-bench)
diff --git a/src/contractor/contractor.cpp b/src/contractor/contractor.cpp
index 39d6f48..72b2d34 100644
--- a/src/contractor/contractor.cpp
+++ b/src/contractor/contractor.cpp
@@ -626,7 +626,7 @@ Contractor::WriteContractedGraph(unsigned max_node_id,
     int number_of_used_edges = 0;
 
     util::StaticGraph<EdgeData>::EdgeArrayEntry current_edge;
-    for (const auto edge : util::irange<std::size_t>(0, contracted_edge_list.size()))
+    for (const auto edge : util::irange<std::size_t>(0UL, contracted_edge_list.size()))
     {
         // some self-loops are required for oneway handling. Need to assertthat we only keep these
         // (TODO)
diff --git a/src/engine/api/json_factory.cpp b/src/engine/api/json_factory.cpp
index 5c3663d..b1f5da5 100644
--- a/src/engine/api/json_factory.cpp
+++ b/src/engine/api/json_factory.cpp
@@ -1,16 +1,16 @@
 #include "engine/api/json_factory.hpp"
 
-#include "engine/polyline_compressor.hpp"
 #include "engine/hint.hpp"
+#include "engine/polyline_compressor.hpp"
+#include "util/integer_range.hpp"
 
 #include <boost/assert.hpp>
-#include <boost/range/irange.hpp>
 #include <boost/optional.hpp>
 
-#include <string>
-#include <utility>
 #include <algorithm>
 #include <iterator>
+#include <string>
+#include <utility>
 #include <vector>
 
 using TurnType = osrm::extractor::guidance::TurnType;
@@ -28,23 +28,17 @@ namespace json
 namespace detail
 {
 
-const constexpr char *modifier_names[] = {"uturn",
-                                          "sharp right",
-                                          "right",
-                                          "slight right",
-                                          "straight",
-                                          "slight left",
-                                          "left",
-                                          "sharp left"};
+const constexpr char *modifier_names[] = {"uturn",    "sharp right", "right", "slight right",
+                                          "straight", "slight left", "left",  "sharp left"};
 
 // translations of TurnTypes. Not all types are exposed to the outside world.
 // invalid types should never be returned as part of the API
 const constexpr char *turn_type_names[] = {
-    "invalid",        "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",     "traffic circle", "invalid",
-    "traffic circle", "invalid", "invalid",    "restriction", "notification"};
+    "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 *waypoint_type_names[] = {"invalid", "arrive", "depart"};
 
 // Check whether to include a modifier in the result of the API
@@ -144,6 +138,7 @@ util::json::Object makeStepManeuver(const guidance::StepManeuver &maneuver)
     if (detail::isValidModifier(maneuver))
         step_maneuver.values["modifier"] =
             detail::instructionModifierToString(maneuver.instruction.direction_modifier);
+
     step_maneuver.values["location"] = detail::coordinateToLonLat(maneuver.location);
     step_maneuver.values["bearing_before"] = std::round(maneuver.bearing_before);
     step_maneuver.values["bearing_after"] = std::round(maneuver.bearing_after);
@@ -164,6 +159,9 @@ util::json::Object makeRouteStep(guidance::RouteStep step, util::json::Value geo
     route_step.values["distance"] = std::round(step.distance * 10) / 10.;
     route_step.values["duration"] = std::round(step.duration * 10) / 10.;
     route_step.values["name"] = std::move(step.name);
+    if (!step.rotary_name.empty())
+        route_step.values["rotary_name"] = std::move(step.rotary_name);
+
     route_step.values["mode"] = detail::modeToString(std::move(step.mode));
     route_step.values["maneuver"] = makeStepManeuver(std::move(step.maneuver));
     route_step.values["geometry"] = std::move(geometry);
@@ -199,7 +197,6 @@ util::json::Object makeRouteLeg(guidance::RouteLeg leg, util::json::Array steps)
     util::json::Object route_leg;
     route_leg.values["distance"] = std::round(leg.distance * 10) / 10.;
     route_leg.values["duration"] = std::round(leg.duration * 10) / 10.;
-    route_leg.values["summary"] = std::move(leg.summary);
     route_leg.values["steps"] = std::move(steps);
     return route_leg;
 }
@@ -209,15 +206,14 @@ util::json::Array makeRouteLegs(std::vector<guidance::RouteLeg> legs,
 {
     util::json::Array json_legs;
     auto step_geometry_iter = step_geometries.begin();
-    for (const auto idx : boost::irange(0UL, legs.size()))
+    for (const auto idx : util::irange<std::size_t>(0UL, legs.size()))
     {
         auto leg = std::move(legs[idx]);
         util::json::Array json_steps;
         json_steps.values.reserve(leg.steps.size());
         std::transform(
             std::make_move_iterator(leg.steps.begin()), std::make_move_iterator(leg.steps.end()),
-            std::back_inserter(json_steps.values), [&step_geometry_iter](guidance::RouteStep step)
-            {
+            std::back_inserter(json_steps.values), [&step_geometry_iter](guidance::RouteStep step) {
                 return makeRouteStep(std::move(step), std::move(*step_geometry_iter++));
             });
         json_legs.values.push_back(makeRouteLeg(std::move(leg), std::move(json_steps)));
diff --git a/src/engine/douglas_peucker.cpp b/src/engine/douglas_peucker.cpp
index e7e2f90..a3eb762 100644
--- a/src/engine/douglas_peucker.cpp
+++ b/src/engine/douglas_peucker.cpp
@@ -1,9 +1,10 @@
 #include "engine/douglas_peucker.hpp"
-#include "util/coordinate_calculation.hpp"
 #include "util/coordinate.hpp"
+#include "util/coordinate_calculation.hpp"
+#include "util/integer_range.hpp"
+#include "util/web_mercator.hpp"
 
 #include <boost/assert.hpp>
-#include <boost/range/irange.hpp>
 
 #include <cmath>
 #include <algorithm>
@@ -16,6 +17,31 @@ namespace osrm
 namespace engine
 {
 
+struct FastPerpendicularDistance
+{
+    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;
+};
+
 std::vector<util::Coordinate> douglasPeucker(std::vector<util::Coordinate>::const_iterator begin,
                                              std::vector<util::Coordinate>::const_iterator end,
                                              const unsigned zoom_level)
@@ -23,7 +49,7 @@ std::vector<util::Coordinate> douglasPeucker(std::vector<util::Coordinate>::cons
     BOOST_ASSERT_MSG(zoom_level < detail::DOUGLAS_PEUCKER_THRESHOLDS_SIZE,
                      "unsupported zoom level");
 
-    const auto size = std::distance(begin, end);
+    const std::size_t size = std::distance(begin, end);
     if (size < 2)
     {
         return {};
@@ -51,14 +77,16 @@ std::vector<util::Coordinate> douglasPeucker(std::vector<util::Coordinate>::cons
         BOOST_ASSERT_MSG(pair.second < size, "right border outside of geometry");
         BOOST_ASSERT_MSG(pair.first <= pair.second, "left border on the wrong side");
 
-        double max_distance = 0;
+        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 = perpendicularDistance(begin[pair.first], begin[pair.second], begin[idx]);
+            const auto distance = perpendicular_distance(begin[idx]);
             // found new feasible maximum?
             if (distance > max_distance &&
                 distance > detail::DOUGLAS_PEUCKER_THRESHOLDS[zoom_level])
@@ -87,7 +115,7 @@ std::vector<util::Coordinate> douglasPeucker(std::vector<util::Coordinate>::cons
     auto simplified_size = std::count(is_necessary.begin(), is_necessary.end(), true);
     std::vector<util::Coordinate> simplified_geometry;
     simplified_geometry.reserve(simplified_size);
-    for (auto idx : boost::irange<std::size_t>(0UL, size))
+    for (auto idx : util::irange<std::size_t>(0UL, size))
     {
         if (is_necessary[idx])
         {
diff --git a/src/engine/guidance/post_processing.cpp b/src/engine/guidance/post_processing.cpp
index 0f3fc34..6d02a07 100644
--- a/src/engine/guidance/post_processing.cpp
+++ b/src/engine/guidance/post_processing.cpp
@@ -1,16 +1,16 @@
 #include "engine/guidance/post_processing.hpp"
 #include "extractor/guidance/turn_instruction.hpp"
 
-#include "engine/guidance/toolkit.hpp"
 #include "engine/guidance/assemble_steps.hpp"
+#include "engine/guidance/toolkit.hpp"
 
 #include <boost/assert.hpp>
 #include <boost/range/algorithm_ext/erase.hpp>
 
 #include <algorithm>
-#include <iostream>
 #include <cmath>
 #include <cstddef>
+#include <iostream>
 #include <limits>
 #include <utility>
 
@@ -54,6 +54,11 @@ void fixFinalRoundabout(std::vector<RouteStep> &steps)
         {
             propagation_step.maneuver.exit = 0;
             propagation_step.geometry_end = steps.back().geometry_begin;
+
+            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::StayOnRoundabout)
@@ -80,18 +85,20 @@ bool setUpRoundabout(RouteStep &step)
         step.maneuver.exit = 1;
         // prevent futher special case handling of these two.
         if (instruction.type == TurnType::EnterRotaryAtExit)
-            step.maneuver.instruction = TurnType::EnterRotary;
+            step.maneuver.instruction.type = TurnType::EnterRotary;
         else
-            step.maneuver.instruction = TurnType::EnterRoundabout;
+            step.maneuver.instruction.type = TurnType::EnterRoundabout;
     }
 
     if (leavesRoundabout(instruction))
     {
         step.maneuver.exit = 1; // count the otherwise missing exit
-        if (instruction.type == TurnType::EnterRotaryAtExit)
-            step.maneuver.instruction = TurnType::EnterRotary;
+
+        // prevent futher special case handling of these two.
+        if (instruction.type == TurnType::EnterAndExitRotary)
+            step.maneuver.instruction.type = TurnType::EnterRotary;
         else
-            step.maneuver.instruction = TurnType::EnterRoundabout;
+            step.maneuver.instruction.type = TurnType::EnterRoundabout;
         return false;
     }
     else
@@ -123,6 +130,8 @@ void closeOffRoundabout(const bool on_roundabout,
         steps[1].maneuver.instruction.type = step.maneuver.instruction.type == TurnType::ExitRotary
                                                  ? TurnType::EnterRotary
                                                  : TurnType::EnterRoundabout;
+        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.
@@ -144,6 +153,11 @@ void closeOffRoundabout(const bool on_roundabout,
 
                 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;
+
                 propagation_step.name = step.name;
                 propagation_step.name_id = step.name_id;
                 break;
@@ -205,9 +219,8 @@ std::vector<RouteStep> postProcess(std::vector<RouteStep> steps)
     // adds an intersection to the initial route step
     // It includes the length of the last step, until the intersection
     // Also updates the length of the respective segment
-    auto addIntersection =
-        [](RouteStep into, const RouteStep &last_step, const RouteStep &intersection)
-    {
+    auto addIntersection = [](RouteStep into, const RouteStep &last_step,
+                              const RouteStep &intersection) {
         into.maneuver.intersections.push_back(
             {last_step.duration, last_step.distance, intersection.maneuver.location});
 
@@ -280,8 +293,7 @@ std::vector<RouteStep> postProcess(std::vector<RouteStep> steps)
     // 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)
-    {
+    const auto not_is_valid = [](const RouteStep &step) {
         return step.maneuver.instruction == TurnInstruction::NO_TURN() &&
                step.maneuver.waypoint_type == WaypointType::None;
     };
@@ -338,10 +350,8 @@ void trimShortSegments(std::vector<RouteStep> &steps, LegGeometry &geometry)
             // geometry offsets have to be adjusted. Move all offsets to the front and reduce by
             // one. (This is an inplace forward one and reduce by one)
             std::transform(geometry.segment_offsets.begin() + 1, geometry.segment_offsets.end(),
-                           geometry.segment_offsets.begin(), [](const std::size_t val)
-                           {
-                               return val - 1;
-                           });
+                           geometry.segment_offsets.begin(),
+                           [](const std::size_t val) { return val - 1; });
 
             geometry.segment_offsets.pop_back();
             const auto &current_depart = steps.front();
@@ -364,21 +374,18 @@ void trimShortSegments(std::vector<RouteStep> &steps, LegGeometry &geometry)
             steps.front().geometry_begin = 1;
             // reduce all offsets by one (inplace)
             std::transform(geometry.segment_offsets.begin(), geometry.segment_offsets.end(),
-                           geometry.segment_offsets.begin(), [](const std::size_t val)
-                           {
-                               return val - 1;
-                           });
+                           geometry.segment_offsets.begin(),
+                           [](const std::size_t val) { return val - 1; });
 
             steps.front().maneuver = detail::stepManeuverFromGeometry(
                 TurnInstruction::NO_TURN(), WaypointType::Depart, geometry);
         }
 
         // and update the leg geometry indices for the removed entry
-        std::for_each(steps.begin(), steps.end(), [](RouteStep &step)
-                      {
-                          --step.geometry_begin;
-                          --step.geometry_end;
-                      });
+        std::for_each(steps.begin(), steps.end(), [](RouteStep &step) {
+            --step.geometry_begin;
+            --step.geometry_end;
+        });
     }
 
     // make sure we still have enough segments
@@ -419,7 +426,9 @@ void trimShortSegments(std::vector<RouteStep> &steps, LegGeometry &geometry)
         // This can happen if the last coordinate snaps to a node in the unpacked geometry
         geometry.locations.pop_back();
         geometry.segment_offsets.back()--;
-        BOOST_ASSERT(next_to_last_step.geometry_end == steps.back().geometry_begin);
+        // since the last geometry includes the location of arrival, the arrival instruction
+        // geometry overlaps with the previous segment
+        BOOST_ASSERT(next_to_last_step.geometry_end == steps.back().geometry_begin + 1);
         BOOST_ASSERT(next_to_last_step.geometry_begin < next_to_last_step.geometry_end);
         next_to_last_step.geometry_end--;
         steps.back().geometry_begin--;
@@ -482,7 +491,7 @@ LegGeometry resyncGeometry(LegGeometry leg_geometry, const std::vector<RouteStep
         leg_geometry.segment_offsets.push_back(step.geometry_end - 1);
     }
 
-    // remove the data fromt the reached-target step again
+    // remove the data from the reached-target step again
     leg_geometry.segment_offsets.pop_back();
     leg_geometry.segment_distances.pop_back();
 
diff --git a/src/engine/plugins/match.cpp b/src/engine/plugins/match.cpp
index 1fff3fe..bd3a970 100644
--- a/src/engine/plugins/match.cpp
+++ b/src/engine/plugins/match.cpp
@@ -168,7 +168,7 @@ Status MatchPlugin::HandleRequest(const api::MatchParameters &parameters,
     }
 
     std::vector<InternalRouteResult> sub_routes(sub_matchings.size());
-    for (auto index : util::irange(0UL, sub_matchings.size()))
+    for (auto index : util::irange<std::size_t>(0UL, sub_matchings.size()))
     {
         BOOST_ASSERT(sub_matchings[index].nodes.size() > 1);
 
@@ -183,7 +183,9 @@ Status MatchPlugin::HandleRequest(const api::MatchParameters &parameters,
             BOOST_ASSERT(current_phantom_node_pair.target_phantom.IsValid());
             sub_routes[index].segment_end_coordinates.emplace_back(current_phantom_node_pair);
         }
-        shortest_path(sub_routes[index].segment_end_coordinates, {}, sub_routes[index]);
+        // force uturns to be on, since we split the phantom nodes anyway and only have bi-directional
+        // phantom nodes for possible uturns
+        shortest_path(sub_routes[index].segment_end_coordinates, {true}, sub_routes[index]);
         BOOST_ASSERT(sub_routes[index].shortest_path_length != INVALID_EDGE_WEIGHT);
     }
 
diff --git a/src/engine/plugins/tile.cpp b/src/engine/plugins/tile.cpp
index 7d18d90..87586ea 100644
--- a/src/engine/plugins/tile.cpp
+++ b/src/engine/plugins/tile.cpp
@@ -2,6 +2,7 @@
 #include "engine/plugins/tile.hpp"
 
 #include "util/coordinate_calculation.hpp"
+#include "util/web_mercator.hpp"
 
 #include <boost/geometry.hpp>
 #include <boost/geometry/geometries/point_xy.hpp>
@@ -119,7 +120,6 @@ FixedLine coordinatesToTileLine(const util::Coordinate start,
                                 const util::Coordinate target,
                                 const detail::BBox &tile_bbox)
 {
-    using namespace util::coordinate_calculation;
     FloatLine geo_line;
     geo_line.emplace_back(static_cast<double>(util::toFloating(start.lon)),
                           static_cast<double>(util::toFloating(start.lat)));
@@ -130,15 +130,16 @@ FixedLine coordinatesToTileLine(const util::Coordinate start,
 
     for (auto const &pt : geo_line)
     {
-        double px_merc = pt.x * mercator::DEGREE_TO_PX;
-        double py_merc = mercator::latToY(util::FloatLatitude(pt.y)) * mercator::DEGREE_TO_PX;
+        double px_merc = pt.x * util::web_mercator::DEGREE_TO_PX;
+        double py_merc = util::web_mercator::latToY(util::FloatLatitude(pt.y)) *
+                         util::web_mercator::DEGREE_TO_PX;
         // convert lon/lat to tile coordinates
         const auto px = std::round(
-            ((px_merc - tile_bbox.minx) * mercator::TILE_SIZE / tile_bbox.width()) *
-            detail::VECTOR_TILE_EXTENT / util::coordinate_calculation::mercator::TILE_SIZE);
+            ((px_merc - tile_bbox.minx) * util::web_mercator::TILE_SIZE / tile_bbox.width()) *
+            detail::VECTOR_TILE_EXTENT / util::web_mercator::TILE_SIZE);
         const auto py = std::round(
-            ((tile_bbox.maxy - py_merc) * mercator::TILE_SIZE / tile_bbox.height()) *
-            detail::VECTOR_TILE_EXTENT / util::coordinate_calculation::mercator::TILE_SIZE);
+            ((tile_bbox.maxy - py_merc) * util::web_mercator::TILE_SIZE / tile_bbox.height()) *
+            detail::VECTOR_TILE_EXTENT / util::web_mercator::TILE_SIZE);
 
         boost::geometry::append(unclipped_line, point_t(px, py));
     }
@@ -170,12 +171,11 @@ Status TilePlugin::HandleRequest(const api::TileParameters &parameters, std::str
 {
     BOOST_ASSERT(parameters.IsValid());
 
-    using namespace util::coordinate_calculation;
     double min_lon, min_lat, max_lon, max_lat;
 
     // Convert the z,x,y mercator tile coordinates into WGS84 lon/lat values
-    mercator::xyzToWGS84(parameters.x, parameters.y, parameters.z, min_lon, min_lat, max_lon,
-                         max_lat);
+    util::web_mercator::xyzToWGS84(parameters.x, parameters.y, parameters.z, min_lon, min_lat,
+                                   max_lon, max_lat);
 
     util::Coordinate southwest{util::FloatLongitude(min_lon), util::FloatLatitude(min_lat)};
     util::Coordinate northeast{util::FloatLongitude(max_lon), util::FloatLatitude(max_lat)};
@@ -245,8 +245,8 @@ Status TilePlugin::HandleRequest(const api::TileParameters &parameters, std::str
     // TODO: extract speed values for compressed and uncompressed geometries
 
     // Convert tile coordinates into mercator coordinates
-    mercator::xyzToMercator(parameters.x, parameters.y, parameters.z, min_lon, min_lat, max_lon,
-                            max_lat);
+    util::web_mercator::xyzToMercator(parameters.x, parameters.y, parameters.z, min_lon, min_lat,
+                                      max_lon, max_lat);
     const detail::BBox tile_bbox{min_lon, min_lat, max_lon, max_lat};
 
     // Protobuf serialized blocks when objects go out of scope, hence
diff --git a/src/engine/plugins/trip.cpp b/src/engine/plugins/trip.cpp
index c834f46..35c6514 100644
--- a/src/engine/plugins/trip.cpp
+++ b/src/engine/plugins/trip.cpp
@@ -135,7 +135,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.uturns, min_route);
+    shortest_path(min_route.segment_end_coordinates, parameters.continue_straight, min_route);
 
     BOOST_ASSERT_MSG(min_route.shortest_path_length < INVALID_EDGE_WEIGHT, "unroutable route");
     return min_route;
diff --git a/src/engine/plugins/viaroute.cpp b/src/engine/plugins/viaroute.cpp
index ffd62b3..748151a 100644
--- a/src/engine/plugins/viaroute.cpp
+++ b/src/engine/plugins/viaroute.cpp
@@ -58,24 +58,26 @@ Status ViaRoutePlugin::HandleRequest(const api::RouteParameters &route_parameter
 
     auto snapped_phantoms = SnapPhantomNodes(phantom_node_pairs);
 
-    const bool allow_u_turn_at_via =
-        route_parameters.uturns ? *route_parameters.uturns : facade.GetUTurnsDefault();
+    const bool continue_straight_at_waypoint = route_parameters.continue_straight
+                                                   ? *route_parameters.continue_straight
+                                                   : facade.GetContinueStraightDefault();
 
     InternalRouteResult raw_route;
-    auto build_phantom_pairs = [&raw_route, allow_u_turn_at_via](const PhantomNode &first_node,
-                                                                 const PhantomNode &second_node)
+    auto build_phantom_pairs = [&raw_route, continue_straight_at_waypoint](
+        const PhantomNode &first_node, const PhantomNode &second_node)
     {
         raw_route.segment_end_coordinates.push_back(PhantomNodes{first_node, second_node});
         auto &last_inserted = raw_route.segment_end_coordinates.back();
         // enable forward direction if possible
         if (last_inserted.source_phantom.forward_segment_id.id != SPECIAL_SEGMENTID)
         {
-            last_inserted.source_phantom.forward_segment_id.enabled |= allow_u_turn_at_via;
+            last_inserted.source_phantom.forward_segment_id.enabled |=
+                !continue_straight_at_waypoint;
         }
         // enable reverse direction if possible
         if (last_inserted.source_phantom.reverse_segment_id.id != SPECIAL_SEGMENTID)
         {
-            last_inserted.source_phantom.reverse_segment_id.enabled |= allow_u_turn_at_via;
+            last_inserted.source_phantom.reverse_segment_id.enabled |= !continue_straight_at_waypoint;
         }
     };
     util::for_each_pair(snapped_phantoms, build_phantom_pairs);
@@ -93,7 +95,7 @@ Status ViaRoutePlugin::HandleRequest(const api::RouteParameters &route_parameter
     }
     else
     {
-        shortest_path(raw_route.segment_end_coordinates, route_parameters.uturns, raw_route);
+        shortest_path(raw_route.segment_end_coordinates, route_parameters.continue_straight, raw_route);
     }
 
     // we can only know this after the fact, different SCC ids still
diff --git a/src/extractor/edge_based_graph_factory.cpp b/src/extractor/edge_based_graph_factory.cpp
index 4cbde57..6264fcd 100644
--- a/src/extractor/edge_based_graph_factory.cpp
+++ b/src/extractor/edge_based_graph_factory.cpp
@@ -2,12 +2,12 @@
 #include "extractor/edge_based_graph_factory.hpp"
 #include "util/coordinate.hpp"
 #include "util/coordinate_calculation.hpp"
-#include "util/percent.hpp"
+#include "util/exception.hpp"
 #include "util/integer_range.hpp"
 #include "util/lua_util.hpp"
+#include "util/percent.hpp"
 #include "util/simple_logger.hpp"
 #include "util/timing_util.hpp"
-#include "util/exception.hpp"
 
 #include "extractor/guidance/toolkit.hpp"
 
@@ -122,8 +122,7 @@ void EdgeBasedGraphFactory::InsertEdgeBasedNode(const NodeID node_u, const NodeI
 
     NodeID current_edge_source_coordinate_id = node_u;
 
-    const auto edge_id_to_segment_id = [](const NodeID edge_based_node_id)
-    {
+    const auto edge_id_to_segment_id = [](const NodeID edge_based_node_id) {
         if (edge_based_node_id == SPECIAL_NODEID)
         {
             return SegmentID{SPECIAL_SEGMENTID, false};
@@ -133,7 +132,7 @@ void EdgeBasedGraphFactory::InsertEdgeBasedNode(const NodeID node_u, const NodeI
     };
 
     // traverse arrays from start and end respectively
-    for (const auto i : util::irange(std::size_t{ 0 }, geometry_size))
+    for (const auto i : util::irange(std::size_t{0}, geometry_size))
     {
         BOOST_ASSERT(
             current_edge_source_coordinate_id ==
@@ -325,7 +324,7 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
                                          m_barrier_nodes, m_compressed_edge_container, name_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)
@@ -468,6 +467,8 @@ int EdgeBasedGraphFactory::GetTurnPenalty(double angle, lua_State *lua_state) co
     {
         // call lua profile to compute turn penalty
         double penalty = luabind::call_function<double>(lua_state, "turn_function", 180. - angle);
+        BOOST_ASSERT(penalty < std::numeric_limits<int>::max());
+        BOOST_ASSERT(penalty > std::numeric_limits<int>::min());
         return boost::numeric_cast<int>(penalty);
     }
     catch (const luabind::error &er)
diff --git a/src/extractor/extractor.cpp b/src/extractor/extractor.cpp
index 368867d..99f25ae 100644
--- a/src/extractor/extractor.cpp
+++ b/src/extractor/extractor.cpp
@@ -544,7 +544,7 @@ void Extractor::BuildRTree(std::vector<EdgeBasedNode> node_based_edge_list,
     // Filter node based edges based on startpoint
     auto out_iter = node_based_edge_list.begin();
     auto in_iter = node_based_edge_list.begin();
-    for (auto index : util::irange<std::size_t>(0, node_is_startpoint.size()))
+    for (auto index : util::irange<std::size_t>(0UL, node_is_startpoint.size()))
     {
         BOOST_ASSERT(in_iter != node_based_edge_list.end());
         if (node_is_startpoint[index])
diff --git a/src/extractor/extractor_callbacks.cpp b/src/extractor/extractor_callbacks.cpp
index a289ec6..0e07d93 100644
--- a/src/extractor/extractor_callbacks.cpp
+++ b/src/extractor/extractor_callbacks.cpp
@@ -14,6 +14,7 @@
 
 #include "osrm/coordinate.hpp"
 
+#include <iterator>
 #include <limits>
 #include <string>
 #include <vector>
diff --git a/src/extractor/guidance/intersection.cpp b/src/extractor/guidance/intersection.cpp
new file mode 100644
index 0000000..8902ba4
--- /dev/null
+++ b/src/extractor/guidance/intersection.cpp
@@ -0,0 +1,31 @@
+#include "extractor/guidance/intersection.hpp"
+
+namespace osrm
+{
+namespace extractor
+{
+namespace guidance
+{
+
+ConnectedRoad::ConnectedRoad(const TurnOperation turn, const bool entry_allowed)
+    : entry_allowed(entry_allowed), turn(turn)
+{
+}
+
+std::string toString(const ConnectedRoad &road)
+{
+    std::string result = "[connection] ";
+    result += std::to_string(road.turn.eid);
+    result += " allows entry: ";
+    result += std::to_string(road.entry_allowed);
+    result += " angle: ";
+    result += std::to_string(road.turn.angle);
+    result += " instruction: ";
+    result += std::to_string(static_cast<std::int32_t>(road.turn.instruction.type)) + " " +
+              std::to_string(static_cast<std::int32_t>(road.turn.instruction.direction_modifier));
+    return result;
+}
+
+} // namespace guidance
+} // namespace extractor
+} // namespace osrm
diff --git a/src/extractor/guidance/intersection_generator.cpp b/src/extractor/guidance/intersection_generator.cpp
new file mode 100644
index 0000000..5f01cda
--- /dev/null
+++ b/src/extractor/guidance/intersection_generator.cpp
@@ -0,0 +1,255 @@
+#include "extractor/guidance/constants.hpp"
+#include "extractor/guidance/intersection_generator.hpp"
+#include "extractor/guidance/toolkit.hpp"
+
+#include <algorithm>
+#include <iterator>
+#include <limits>
+#include <utility>
+
+namespace osrm
+{
+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)
+    : 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)
+{
+}
+
+Intersection IntersectionGenerator::operator()(const NodeID from_node, const EdgeID via_eid) const
+{
+    return getConnectedRoads(from_node, via_eid);
+}
+
+//                                               a
+//                                               |
+//                                               |
+//                                               v
+// For an intersection from_node --via_edi--> turn_node ----> c
+//                                               ^
+//                                               |
+//                                               |
+//                                               b
+// This functions returns _all_ turns as if the graph was undirected.
+// That means we not only get (from_node, turn_node, c) in the above example
+// but also (from_node, turn_node, a), (from_node, turn_node, b). These turns are
+// marked as invalid and only needed for intersection classification.
+Intersection IntersectionGenerator::getConnectedRoads(const NodeID from_node,
+                                                      const EdgeID via_eid) const
+{
+    Intersection intersection;
+    const NodeID turn_node = node_based_graph.GetTarget(via_eid);
+    const NodeID only_restriction_to_node =
+        restriction_map.CheckForEmanatingIsOnlyTurn(from_node, turn_node);
+    const bool is_barrier_node = barrier_nodes.find(turn_node) != barrier_nodes.end();
+
+    bool has_uturn_edge = false;
+    for (const EdgeID onto_edge : node_based_graph.GetAdjacentEdgeRange(turn_node))
+    {
+        BOOST_ASSERT(onto_edge != SPECIAL_EDGEID);
+        const NodeID to_node = node_based_graph.GetTarget(onto_edge);
+
+        bool turn_is_valid =
+            // reverse edges are never valid turns because the resulting turn would look like this:
+            // from_node --via_edge--> turn_node <--onto_edge-- to_node
+            // however we need this for capture intersection shape for incoming one-ways
+            !node_based_graph.GetEdgeData(onto_edge).reversed &&
+            // we are not turning over a barrier
+            (!is_barrier_node || from_node == to_node) &&
+            // We are at an only_-restriction but not at the right turn.
+            (only_restriction_to_node == SPECIAL_NODEID || to_node == only_restriction_to_node) &&
+            // the turn is not restricted
+            !restriction_map.CheckIfTurnIsRestricted(from_node, turn_node, to_node);
+
+        auto angle = 0.;
+        if (from_node == to_node)
+        {
+            if (turn_is_valid && !is_barrier_node)
+            {
+                // we only add u-turns for dead-end streets.
+                if (node_based_graph.GetOutDegree(turn_node) > 1)
+                {
+                    auto number_of_emmiting_bidirectional_edges = 0;
+                    for (auto edge : node_based_graph.GetAdjacentEdgeRange(turn_node))
+                    {
+                        auto target = node_based_graph.GetTarget(edge);
+                        auto reverse_edge = node_based_graph.FindEdge(target, turn_node);
+                        BOOST_ASSERT(reverse_edge != SPECIAL_EDGEID);
+                        if (!node_based_graph.GetEdgeData(reverse_edge).reversed)
+                        {
+                            ++number_of_emmiting_bidirectional_edges;
+                        }
+                    }
+                    // is a dead-end
+                    turn_is_valid = number_of_emmiting_bidirectional_edges <= 1;
+                }
+            }
+            has_uturn_edge = true;
+            BOOST_ASSERT(angle >= 0. && angle < std::numeric_limits<double>::epsilon());
+        }
+        else
+        {
+            // unpack first node of second segment if packed
+            const auto first_coordinate = getRepresentativeCoordinate(
+                from_node, turn_node, via_eid, INVERT, compressed_edge_container, node_info_list);
+            const auto third_coordinate = getRepresentativeCoordinate(
+                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())
+                has_uturn_edge = true;
+        }
+
+        intersection.push_back(ConnectedRoad(
+            TurnOperation{onto_edge, angle, {TurnType::Invalid, DirectionModifier::UTurn}},
+            turn_is_valid));
+    }
+
+    // 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.
+    if (!has_uturn_edge)
+    {
+        intersection.push_back(
+            {TurnOperation{via_eid, 0., {TurnType::Invalid, DirectionModifier::UTurn}}, false});
+    }
+
+    const auto ByAngle = [](const ConnectedRoad &first, const ConnectedRoad second) {
+        return first.turn.angle < second.turn.angle;
+    };
+    std::sort(std::begin(intersection), std::end(intersection), ByAngle);
+
+    BOOST_ASSERT(intersection[0].turn.angle >= 0. &&
+                 intersection[0].turn.angle < std::numeric_limits<double>::epsilon());
+
+    return mergeSegregatedRoads(std::move(intersection));
+}
+
+/*
+ * Segregated Roads often merge onto a single intersection.
+ * While technically representing different roads, they are
+ * often looked at as a single road.
+ * Due to the merging, turn Angles seem off, wenn we compute them from the
+ * initial positions.
+ *
+ *         b<b<b<b(1)<b<b<b
+ * aaaaa-b
+ *         b>b>b>b(2)>b>b>b
+ *
+ * Would be seen as a slight turn going fro a to (2). A Sharp turn going from
+ * (1) to (2).
+ *
+ * In cases like these, we megre this segregated roads into a single road to
+ * end up with a case like:
+ *
+ * aaaaa-bbbbbb
+ *
+ * for the turn representation.
+ * Anything containing the first u-turn in a merge affects all other angles
+ * and is handled separately from all others.
+ */
+Intersection IntersectionGenerator::mergeSegregatedRoads(Intersection intersection) const
+{
+    const auto getRight = [&](std::size_t index) {
+        return (index + intersection.size() - 1) % intersection.size();
+    };
+
+    const auto mergable = [&](std::size_t first, std::size_t second) -> bool {
+        const auto &first_data = node_based_graph.GetEdgeData(intersection[first].turn.eid);
+        const auto &second_data = node_based_graph.GetEdgeData(intersection[second].turn.eid);
+
+        return first_data.name_id != INVALID_NAME_ID && first_data.name_id == second_data.name_id &&
+               !first_data.roundabout && !second_data.roundabout &&
+               first_data.travel_mode == second_data.travel_mode &&
+               first_data.road_classification == second_data.road_classification &&
+               // compatible threshold
+               angularDeviation(intersection[first].turn.angle, intersection[second].turn.angle) <
+                   60 &&
+               first_data.reversed != second_data.reversed;
+    };
+
+    const auto merge = [](const ConnectedRoad &first,
+                          const ConnectedRoad &second) -> ConnectedRoad {
+        if (!first.entry_allowed)
+        {
+            ConnectedRoad result = second;
+            result.turn.angle = (first.turn.angle + second.turn.angle) / 2;
+            if (first.turn.angle - second.turn.angle > 180)
+                result.turn.angle += 180;
+            if (result.turn.angle > 360)
+                result.turn.angle -= 360;
+
+            return result;
+        }
+        else
+        {
+            BOOST_ASSERT(!second.entry_allowed);
+            ConnectedRoad result = first;
+            result.turn.angle = (first.turn.angle + second.turn.angle) / 2;
+
+            if (first.turn.angle - second.turn.angle > 180)
+                result.turn.angle += 180;
+            if (result.turn.angle > 360)
+                result.turn.angle -= 360;
+
+            return result;
+        }
+    };
+    if (intersection.size() == 1)
+        return intersection;
+
+    // check for merges including the basic u-turn
+    // these result in an adjustment of all other angles
+    if (mergable(0, intersection.size() - 1))
+    {
+        const double correction_factor =
+            (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;
+        intersection[0] = merge(intersection.front(), intersection.back());
+        intersection[0].turn.angle = 0;
+        intersection.pop_back();
+    }
+
+    else if (mergable(0, 1))
+    {
+        const double correction_factor = (intersection[1].turn.angle) / 2;
+        for (std::size_t i = 2; i < intersection.size(); ++i)
+            intersection[i].turn.angle += correction_factor;
+        intersection[0] = merge(intersection[0], intersection[1]);
+        intersection[0].turn.angle = 0;
+        intersection.erase(intersection.begin() + 1);
+    }
+
+    // a merge including the first u-turn requres an adjustment of the turn angles
+    // therefore these are handled prior to this step
+    for (std::size_t index = 2; index < intersection.size(); ++index)
+    {
+        if (mergable(index, getRight(index)))
+        {
+            intersection[getRight(index)] =
+                merge(intersection[getRight(index)], intersection[index]);
+            intersection.erase(intersection.begin() + index);
+            --index;
+        }
+    }
+
+    const auto ByAngle = [](const ConnectedRoad &first, const ConnectedRoad second) {
+        return first.turn.angle < second.turn.angle;
+    };
+    std::sort(std::begin(intersection), std::end(intersection), ByAngle);
+    return intersection;
+}
+
+} // namespace guidance
+} // namespace extractor
+} // namespace osrm
diff --git a/src/extractor/guidance/intersection_handler.cpp b/src/extractor/guidance/intersection_handler.cpp
new file mode 100644
index 0000000..b77f4ed
--- /dev/null
+++ b/src/extractor/guidance/intersection_handler.cpp
@@ -0,0 +1,307 @@
+#include "extractor/guidance/constants.hpp"
+#include "extractor/guidance/intersection_handler.hpp"
+#include "extractor/guidance/toolkit.hpp"
+
+#include <algorithm>
+
+using EdgeData = osrm::util::NodeBasedDynamicGraph::EdgeData;
+
+namespace osrm
+{
+namespace extractor
+{
+namespace guidance
+{
+
+namespace detail
+{
+inline bool requiresAnnouncement(const EdgeData &from, const EdgeData &to)
+{
+    return !from.IsCompatibleTo(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)
+{
+}
+
+IntersectionHandler::~IntersectionHandler() {}
+
+std::size_t IntersectionHandler::countValid(const Intersection &intersection) const
+{
+    return std::count_if(intersection.begin(), intersection.end(),
+                         [](const ConnectedRoad &road) { return road.entry_allowed; });
+}
+
+TurnType IntersectionHandler::findBasicTurnType(const EdgeID via_edge,
+                                                const ConnectedRoad &road) const
+{
+
+    const auto &in_data = node_based_graph.GetEdgeData(via_edge);
+    const auto &out_data = node_based_graph.GetEdgeData(road.turn.eid);
+
+    bool on_ramp = isRampClass(in_data.road_classification.road_class);
+
+    bool onto_ramp = isRampClass(out_data.road_classification.road_class);
+
+    if (!on_ramp && onto_ramp)
+        return TurnType::Ramp;
+
+    if (in_data.name_id == out_data.name_id && in_data.name_id != INVALID_NAME_ID)
+    {
+        return TurnType::Continue;
+    }
+
+    return TurnType::Turn;
+}
+
+TurnInstruction IntersectionHandler::getInstructionForObvious(const std::size_t num_roads,
+                                                              const EdgeID via_edge,
+                                                              const bool through_street,
+                                                              const ConnectedRoad &road) const
+{
+    const auto type = findBasicTurnType(via_edge, road);
+    // 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)
+    {
+        return {TurnType::Ramp, getTurnDirection(road.turn.angle)};
+    }
+
+    if (angularDeviation(road.turn.angle, 0) < 0.01)
+    {
+        return {TurnType::Turn, DirectionModifier::UTurn};
+    }
+    if (type == TurnType::Turn)
+    {
+        const auto &in_data = node_based_graph.GetEdgeData(via_edge);
+        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)))
+        {
+            // obvious turn onto a through street is a merge
+            if (through_street)
+            {
+                return {TurnType::Merge, road.turn.angle > STRAIGHT_ANGLE
+                                             ? DirectionModifier::SlightRight
+                                             : DirectionModifier::SlightLeft};
+            }
+            else
+            {
+                return {TurnType::NewName, getTurnDirection(road.turn.angle)};
+            }
+        }
+        else
+        {
+            if (in_mode == out_mode)
+                return {TurnType::Suppressed, getTurnDirection(road.turn.angle)};
+            else
+                return {TurnType::Notification, getTurnDirection(road.turn.angle)};
+        }
+    }
+    BOOST_ASSERT(type == TurnType::Continue);
+    if (in_mode != out_mode)
+    {
+        return {TurnType::Notification, getTurnDirection(road.turn.angle)};
+    }
+    if (num_roads > 2)
+    {
+        return {TurnType::Suppressed, getTurnDirection(road.turn.angle)};
+    }
+    else
+    {
+        return {TurnType::NoTurn, getTurnDirection(road.turn.angle)};
+    }
+}
+
+void IntersectionHandler::assignFork(const EdgeID via_edge,
+                                     ConnectedRoad &left,
+                                     ConnectedRoad &right) const
+{
+    const auto &in_data = node_based_graph.GetEdgeData(via_edge);
+    const bool low_priority_left = isLowPriorityRoadClass(
+        node_based_graph.GetEdgeData(left.turn.eid).road_classification.road_class);
+    const bool low_priority_right = isLowPriorityRoadClass(
+        node_based_graph.GetEdgeData(right.turn.eid).road_classification.road_class);
+    if ((angularDeviation(left.turn.angle, STRAIGHT_ANGLE) < MAXIMAL_ALLOWED_NO_TURN_DEVIATION &&
+         angularDeviation(right.turn.angle, STRAIGHT_ANGLE) > FUZZY_ANGLE_DIFFERENCE))
+    {
+        // left side is actually straight
+        const auto &out_data = node_based_graph.GetEdgeData(left.turn.eid);
+        if (detail::requiresAnnouncement(in_data, out_data))
+        {
+            if (low_priority_right && !low_priority_left)
+            {
+                left.turn.instruction = getInstructionForObvious(3, via_edge, false, left);
+                right.turn.instruction = {findBasicTurnType(via_edge, right),
+                                          DirectionModifier::SlightRight};
+            }
+            else
+            {
+                if (low_priority_left && !low_priority_right)
+                {
+                    left.turn.instruction = {findBasicTurnType(via_edge, left),
+                                             DirectionModifier::SlightLeft};
+                    right.turn.instruction = {findBasicTurnType(via_edge, right),
+                                              DirectionModifier::SlightRight};
+                }
+                else
+                {
+                    left.turn.instruction = {TurnType::Fork, DirectionModifier::SlightLeft};
+                    right.turn.instruction = {TurnType::Fork, DirectionModifier::SlightRight};
+                }
+            }
+        }
+        else
+        {
+            left.turn.instruction = {TurnType::Suppressed, DirectionModifier::Straight};
+            right.turn.instruction = {findBasicTurnType(via_edge, right),
+                                      DirectionModifier::SlightRight};
+        }
+    }
+    else if (angularDeviation(right.turn.angle, STRAIGHT_ANGLE) <
+                 MAXIMAL_ALLOWED_NO_TURN_DEVIATION &&
+             angularDeviation(left.turn.angle, STRAIGHT_ANGLE) > FUZZY_ANGLE_DIFFERENCE)
+    {
+        // right side is actually straight
+        const auto &out_data = node_based_graph.GetEdgeData(right.turn.eid);
+        if (angularDeviation(right.turn.angle, STRAIGHT_ANGLE) <
+                MAXIMAL_ALLOWED_NO_TURN_DEVIATION &&
+            angularDeviation(left.turn.angle, STRAIGHT_ANGLE) > FUZZY_ANGLE_DIFFERENCE)
+        {
+            if (detail::requiresAnnouncement(in_data, out_data))
+            {
+                if (low_priority_left && !low_priority_right)
+                {
+                    left.turn.instruction = {findBasicTurnType(via_edge, left),
+                                             DirectionModifier::SlightLeft};
+                    right.turn.instruction = getInstructionForObvious(3, via_edge, false, right);
+                }
+                else
+                {
+                    if (low_priority_right && !low_priority_left)
+                    {
+                        left.turn.instruction = {findBasicTurnType(via_edge, left),
+                                                 DirectionModifier::SlightLeft};
+                        right.turn.instruction = {findBasicTurnType(via_edge, right),
+                                                  DirectionModifier::SlightRight};
+                    }
+                    else
+                    {
+                        right.turn.instruction = {TurnType::Fork, DirectionModifier::SlightRight};
+                        left.turn.instruction = {TurnType::Fork, DirectionModifier::SlightLeft};
+                    }
+                }
+            }
+            else
+            {
+                right.turn.instruction = {TurnType::Suppressed, DirectionModifier::Straight};
+                left.turn.instruction = {findBasicTurnType(via_edge, left),
+                                         DirectionModifier::SlightLeft};
+            }
+        }
+    }
+    // left side of fork
+    if (low_priority_right && !low_priority_left)
+        left.turn.instruction = {TurnType::Suppressed, DirectionModifier::SlightLeft};
+    else
+    {
+        if (low_priority_left && !low_priority_right)
+            left.turn.instruction = {TurnType::Turn, DirectionModifier::SlightLeft};
+        else
+            left.turn.instruction = {TurnType::Fork, DirectionModifier::SlightLeft};
+    }
+
+    // right side of fork
+    if (low_priority_left && !low_priority_right)
+        right.turn.instruction = {TurnType::Suppressed, DirectionModifier::SlightLeft};
+    else
+    {
+        if (low_priority_right && !low_priority_left)
+            right.turn.instruction = {TurnType::Turn, DirectionModifier::SlightRight};
+        else
+            right.turn.instruction = {TurnType::Fork, DirectionModifier::SlightRight};
+    }
+}
+
+void IntersectionHandler::assignFork(const EdgeID via_edge,
+                                     ConnectedRoad &left,
+                                     ConnectedRoad &center,
+                                     ConnectedRoad &right) const
+{
+    // TODO handle low priority road classes in a reasonable way
+    if (left.entry_allowed && center.entry_allowed && right.entry_allowed)
+    {
+        left.turn.instruction = {TurnType::Fork, DirectionModifier::SlightLeft};
+        if (angularDeviation(center.turn.angle, 180) < MAXIMAL_ALLOWED_NO_TURN_DEVIATION)
+        {
+            const auto &in_data = node_based_graph.GetEdgeData(via_edge);
+            const auto &out_data = node_based_graph.GetEdgeData(center.turn.eid);
+            if (detail::requiresAnnouncement(in_data, out_data))
+            {
+                center.turn.instruction = {TurnType::Fork, DirectionModifier::Straight};
+            }
+            else
+            {
+                center.turn.instruction = {TurnType::Suppressed, DirectionModifier::Straight};
+            }
+        }
+        else
+        {
+            center.turn.instruction = {TurnType::Fork, DirectionModifier::Straight};
+        }
+        right.turn.instruction = {TurnType::Fork, DirectionModifier::SlightRight};
+    }
+    else if (left.entry_allowed)
+    {
+        if (right.entry_allowed)
+            assignFork(via_edge, left, right);
+        else if (center.entry_allowed)
+            assignFork(via_edge, left, center);
+        else
+            left.turn.instruction = {findBasicTurnType(via_edge, left),
+                                     getTurnDirection(left.turn.angle)};
+    }
+    else if (right.entry_allowed)
+    {
+        if (center.entry_allowed)
+            assignFork(via_edge, center, right);
+        else
+            right.turn.instruction = {findBasicTurnType(via_edge, right),
+                                      getTurnDirection(right.turn.angle)};
+    }
+    else
+    {
+        if (center.entry_allowed)
+            center.turn.instruction = {findBasicTurnType(via_edge, center),
+                                       getTurnDirection(center.turn.angle)};
+    }
+}
+
+bool IntersectionHandler::isThroughStreet(const std::size_t index,
+                                          const Intersection &intersection) const
+{
+    if (node_based_graph.GetEdgeData(intersection[index].turn.eid).name_id == INVALID_NAME_ID)
+        return false;
+    for (const auto &road : intersection)
+    {
+        // a through street cannot start at our own position
+        if (road.turn.angle < std::numeric_limits<double>::epsilon())
+            continue;
+        if (angularDeviation(road.turn.angle, intersection[index].turn.angle) >
+                (STRAIGHT_ANGLE - NARROW_TURN_ANGLE) &&
+            node_based_graph.GetEdgeData(road.turn.eid).name_id ==
+                node_based_graph.GetEdgeData(intersection[index].turn.eid).name_id)
+            return true;
+    }
+    return false;
+}
+
+} // namespace guidance
+} // namespace extractor
+} // namespace osrm
diff --git a/src/extractor/guidance/motorway_handler.cpp b/src/extractor/guidance/motorway_handler.cpp
new file mode 100644
index 0000000..c855b21
--- /dev/null
+++ b/src/extractor/guidance/motorway_handler.cpp
@@ -0,0 +1,524 @@
+#include "extractor/guidance/constants.hpp"
+#include "extractor/guidance/motorway_handler.hpp"
+#include "extractor/guidance/toolkit.hpp"
+
+#include "util/simple_logger.hpp"
+
+#include <limits>
+#include <utility>
+
+#include <boost/assert.hpp>
+
+namespace osrm
+{
+namespace extractor
+{
+namespace guidance
+{
+namespace detail
+{
+inline bool isMotorwayClass(const FunctionalRoadClass road_class)
+{
+    return road_class == FunctionalRoadClass::MOTORWAY || road_class == FunctionalRoadClass::TRUNK;
+}
+
+inline bool isMotorwayClass(EdgeID eid, const util::NodeBasedDynamicGraph &node_based_graph)
+{
+    return isMotorwayClass(node_based_graph.GetEdgeData(eid).road_classification.road_class);
+}
+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);
+}
+}
+
+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)
+{
+}
+
+MotorwayHandler::~MotorwayHandler() {}
+
+bool MotorwayHandler::canProcess(const NodeID,
+                                 const EdgeID via_eid,
+                                 const Intersection &intersection) const
+{
+    bool has_motorway = false;
+    bool has_normal_roads = false;
+
+    for (const auto &road : intersection)
+    {
+        const auto &out_data = node_based_graph.GetEdgeData(road.turn.eid);
+        // not merging or forking?
+        if (road.entry_allowed && angularDeviation(road.turn.angle, STRAIGHT_ANGLE) > 60)
+            return false;
+        else if (out_data.road_classification.road_class == FunctionalRoadClass::MOTORWAY ||
+                 out_data.road_classification.road_class == FunctionalRoadClass::TRUNK)
+        {
+            if (road.entry_allowed)
+                has_motorway = true;
+        }
+        else if (!isRampClass(out_data.road_classification.road_class))
+            has_normal_roads = true;
+    }
+
+    if (has_normal_roads)
+        return false;
+
+    const auto &in_data = node_based_graph.GetEdgeData(via_eid);
+    return has_motorway ||
+           in_data.road_classification.road_class == FunctionalRoadClass::MOTORWAY ||
+           in_data.road_classification.road_class == FunctionalRoadClass::TRUNK;
+}
+
+Intersection MotorwayHandler::
+operator()(const NodeID, const EdgeID via_eid, Intersection intersection) const
+{
+    const auto &in_data = node_based_graph.GetEdgeData(via_eid);
+
+    // coming from motorway
+    if (detail::isMotorwayClass(in_data.road_classification.road_class))
+    {
+        return fromMotorway(via_eid, std::move(intersection));
+    }
+    else // coming from a ramp
+    {
+        return fromRamp(via_eid, std::move(intersection));
+        // ramp merging straight onto motorway
+    }
+}
+
+Intersection MotorwayHandler::fromMotorway(const EdgeID via_eid, Intersection intersection) const
+{
+    const auto &in_data = node_based_graph.GetEdgeData(via_eid);
+    BOOST_ASSERT(detail::isMotorwayClass(in_data.road_classification.road_class));
+
+    const auto countExitingMotorways = [this](const Intersection &intersection) {
+        unsigned count = 0;
+        for (const auto &road : intersection)
+        {
+            if (road.entry_allowed && detail::isMotorwayClass(road.turn.eid, node_based_graph))
+                ++count;
+        }
+        return count;
+    };
+
+    // find the angle that continues on our current highway
+    const auto getContinueAngle = [this, in_data](const Intersection &intersection) {
+        for (const auto &road : intersection)
+        {
+            const auto &out_data = node_based_graph.GetEdgeData(road.turn.eid);
+            if (road.turn.angle != 0 && in_data.name_id == out_data.name_id &&
+                in_data.name_id != INVALID_NAME_ID &&
+                detail::isMotorwayClass(out_data.road_classification.road_class))
+                return road.turn.angle;
+        }
+        return intersection[0].turn.angle;
+    };
+
+    const auto getMostLikelyContinue = [this, in_data](const Intersection &intersection) {
+        double angle = intersection[0].turn.angle;
+        double best = 180;
+        for (const auto &road : intersection)
+        {
+            const auto &out_data = node_based_graph.GetEdgeData(road.turn.eid);
+            if (detail::isMotorwayClass(out_data.road_classification.road_class) &&
+                angularDeviation(road.turn.angle, STRAIGHT_ANGLE) < best)
+            {
+                best = angularDeviation(road.turn.angle, STRAIGHT_ANGLE);
+                angle = road.turn.angle;
+            }
+        }
+        return angle;
+    };
+
+    const auto findBestContinue = [&]() {
+        const double continue_angle = getContinueAngle(intersection);
+        if (continue_angle != intersection[0].turn.angle)
+            return continue_angle;
+        else
+            return getMostLikelyContinue(intersection);
+    };
+
+    // find continue angle
+    const double continue_angle = findBestContinue();
+    // highway does not continue and has no obvious choice
+    if (continue_angle == intersection[0].turn.angle)
+    {
+        if (intersection.size() == 2)
+        {
+            // do not announce ramps at the end of a highway
+            intersection[1].turn.instruction = {TurnType::NoTurn,
+                                                getTurnDirection(intersection[1].turn.angle)};
+        }
+        else if (intersection.size() == 3)
+        {
+            // splitting ramp at the end of a highway
+            if (intersection[1].entry_allowed && intersection[2].entry_allowed)
+            {
+                assignFork(via_eid, intersection[2], intersection[1]);
+            }
+            else
+            {
+                // ending in a passing ramp
+                if (intersection[1].entry_allowed)
+                    intersection[1].turn.instruction = {
+                        TurnType::NoTurn, getTurnDirection(intersection[1].turn.angle)};
+                else
+                    intersection[2].turn.instruction = {
+                        TurnType::NoTurn, getTurnDirection(intersection[2].turn.angle)};
+            }
+        }
+        else if (intersection.size() == 4 &&
+                 detail::roadClass(intersection[1], node_based_graph) ==
+                     detail::roadClass(intersection[2], node_based_graph) &&
+                 detail::roadClass(intersection[2], node_based_graph) ==
+                     detail::roadClass(intersection[3], node_based_graph))
+        {
+            // tripple fork at the end
+            assignFork(via_eid, intersection[3], intersection[2], intersection[1]);
+        }
+
+        else if (countValid(intersection) > 0) // check whether turns exist at all
+        {
+            // FALLBACK, this should hopefully never be reached
+            util::SimpleLogger().Write(logDEBUG)
+                << "Fallback reached from motorway, no continue angle, " << intersection.size()
+                << " roads, " << countValid(intersection) << " valid ones.";
+            return fallback(std::move(intersection));
+        }
+    }
+    else
+    {
+        const unsigned exiting_motorways = countExitingMotorways(intersection);
+
+        if (exiting_motorways == 0)
+        {
+            // Ending in Ramp
+            for (auto &road : intersection)
+            {
+                if (road.entry_allowed)
+                {
+                    BOOST_ASSERT(detail::isRampClass(road.turn.eid, node_based_graph));
+                    road.turn.instruction =
+                        TurnInstruction::SUPPRESSED(getTurnDirection(road.turn.angle));
+                }
+            }
+        }
+        else if (exiting_motorways == 1)
+        {
+            // normal motorway passing some ramps or mering onto another motorway
+            if (intersection.size() == 2)
+            {
+                BOOST_ASSERT(!detail::isRampClass(intersection[1].turn.eid, node_based_graph));
+
+                intersection[1].turn.instruction =
+                    getInstructionForObvious(intersection.size(), via_eid,
+                                             isThroughStreet(1,intersection), intersection[1]);
+            }
+            else
+            {
+                // Normal Highway exit or merge
+                for (auto &road : intersection)
+                {
+                    // ignore invalid uturns/other
+                    if (!road.entry_allowed)
+                        continue;
+
+                    if (road.turn.angle == continue_angle)
+                    {
+                        road.turn.instruction = getInstructionForObvious(
+                            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,
+                            (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,
+                            (road.turn.angle > 215) ? DirectionModifier::Left
+                                                    : DirectionModifier::SlightLeft};
+                    }
+                }
+            }
+        }
+        // handle motorway forks
+        else if (exiting_motorways > 1)
+        {
+            if (exiting_motorways == 2 && intersection.size() == 2)
+            {
+                intersection[1].turn.instruction =
+                    getInstructionForObvious(intersection.size(), via_eid,
+                                             isThroughStreet(1,intersection), intersection[1]);
+                util::SimpleLogger().Write(logDEBUG) << "Disabled U-Turn on a freeway";
+                intersection[0].entry_allowed = false; // UTURN on the freeway
+            }
+            else if (exiting_motorways == 2)
+            {
+                // standard fork
+                std::size_t first_valid = std::numeric_limits<std::size_t>::max(),
+                            second_valid = std::numeric_limits<std::size_t>::max();
+                for (std::size_t i = 0; i < intersection.size(); ++i)
+                {
+                    if (intersection[i].entry_allowed &&
+                        detail::isMotorwayClass(intersection[i].turn.eid, node_based_graph))
+                    {
+                        if (first_valid < intersection.size())
+                        {
+                            second_valid = i;
+                            break;
+                        }
+                        else
+                        {
+                            first_valid = i;
+                        }
+                    }
+                }
+                assignFork(via_eid, intersection[second_valid], intersection[first_valid]);
+            }
+            else if (exiting_motorways == 3)
+            {
+                // triple fork
+                std::size_t first_valid = std::numeric_limits<std::size_t>::max(),
+                            second_valid = std::numeric_limits<std::size_t>::max(),
+                            third_valid = std::numeric_limits<std::size_t>::max();
+                for (std::size_t i = 0; i < intersection.size(); ++i)
+                {
+                    if (intersection[i].entry_allowed &&
+                        detail::isMotorwayClass(intersection[i].turn.eid, node_based_graph))
+                    {
+                        if (second_valid < intersection.size())
+                        {
+                            third_valid = i;
+                            break;
+                        }
+                        else if (first_valid < intersection.size())
+                        {
+                            second_valid = i;
+                        }
+                        else
+                        {
+                            first_valid = i;
+                        }
+                    }
+                }
+                assignFork(via_eid, intersection[third_valid], intersection[second_valid],
+                           intersection[first_valid]);
+            }
+            else
+            {
+                util::SimpleLogger().Write(logDEBUG) << "Found motorway junction with more than "
+                                                        "2 exiting motorways or additional ramps";
+                return fallback(std::move(intersection));
+            }
+        } // done for more than one highway exit
+    }
+    return intersection;
+}
+
+Intersection MotorwayHandler::fromRamp(const EdgeID via_eid, Intersection intersection) const
+{
+    auto num_valid_turns = countValid(intersection);
+    // ramp straight into a motorway/ramp
+    if (intersection.size() == 2 && num_valid_turns == 1)
+    {
+        BOOST_ASSERT(!intersection[0].entry_allowed);
+        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]);
+    }
+    else if (intersection.size() == 3)
+    {
+        // merging onto a passing highway / or two ramps merging onto the same highway
+        if (num_valid_turns == 1)
+        {
+            BOOST_ASSERT(!intersection[0].entry_allowed);
+            // check order of highways
+            //          4
+            //     5         3
+            //
+            //   6              2
+            //
+            //     7         1
+            //          0
+            if (intersection[1].entry_allowed)
+            {
+                if (detail::isMotorwayClass(intersection[1].turn.eid, node_based_graph) &&
+                    node_based_graph.GetEdgeData(intersection[2].turn.eid).name_id !=
+                        INVALID_NAME_ID &&
+                    node_based_graph.GetEdgeData(intersection[2].turn.eid).name_id ==
+                        node_based_graph.GetEdgeData(intersection[1].turn.eid).name_id)
+                {
+                    // circular order indicates a merge to the left (0-3 onto 4
+                    if (angularDeviation(intersection[1].turn.angle, STRAIGHT_ANGLE) <
+                        NARROW_TURN_ANGLE)
+                        intersection[1].turn.instruction = {TurnType::Merge,
+                                                            DirectionModifier::SlightLeft};
+                    else // fallback
+                        intersection[1].turn.instruction = {
+                            TurnType::Merge, getTurnDirection(intersection[1].turn.angle)};
+                }
+                else // passing by the end of a motorway
+                {
+                    intersection[1].turn.instruction =
+                        getInstructionForObvious(intersection.size(), via_eid,
+                                                 isThroughStreet(1,intersection), intersection[1]);
+                }
+            }
+            else
+            {
+                BOOST_ASSERT(intersection[2].entry_allowed);
+                if (detail::isMotorwayClass(intersection[2].turn.eid, node_based_graph) &&
+                    node_based_graph.GetEdgeData(intersection[1].turn.eid).name_id !=
+                        INVALID_NAME_ID &&
+                    node_based_graph.GetEdgeData(intersection[1].turn.eid).name_id ==
+                        node_based_graph.GetEdgeData(intersection[0].turn.eid).name_id)
+                {
+                    // circular order (5-0) onto 4
+                    if (angularDeviation(intersection[2].turn.angle, STRAIGHT_ANGLE) <
+                        NARROW_TURN_ANGLE)
+                        intersection[2].turn.instruction = {TurnType::Merge,
+                                                            DirectionModifier::SlightRight};
+                    else // fallback
+                        intersection[2].turn.instruction = {
+                            TurnType::Merge, getTurnDirection(intersection[2].turn.angle)};
+                }
+                else // passing the end of a highway
+                {
+                    intersection[2].turn.instruction =
+                        getInstructionForObvious(intersection.size(), via_eid,
+                                                 isThroughStreet(2,intersection), intersection[2]);
+                }
+            }
+        }
+        else
+        {
+            BOOST_ASSERT(num_valid_turns == 2);
+            // UTurn on ramps is not possible
+            BOOST_ASSERT(!intersection[0].entry_allowed);
+            BOOST_ASSERT(intersection[1].entry_allowed);
+            BOOST_ASSERT(intersection[2].entry_allowed);
+            // two motorways starting at end of ramp (fork)
+            //  M       M
+            //    \   /
+            //      |
+            //      R
+            if (detail::isMotorwayClass(intersection[1].turn.eid, node_based_graph) &&
+                detail::isMotorwayClass(intersection[2].turn.eid, node_based_graph))
+            {
+                assignFork(via_eid, intersection[2], intersection[1]);
+            }
+            else
+            {
+                // continued ramp passing motorway entry
+                //      M  R
+                //      M  R
+                //      | /
+                //      R
+                if (detail::isMotorwayClass(node_based_graph.GetEdgeData(intersection[1].turn.eid)
+                                                .road_classification.road_class))
+                {
+                    intersection[1].turn.instruction = {TurnType::Turn,
+                                                        DirectionModifier::SlightRight};
+                    intersection[2].turn.instruction = {TurnType::Continue,
+                                                        DirectionModifier::SlightLeft};
+                }
+                else
+                {
+                    assignFork(via_eid, intersection[2], intersection[1]);
+                }
+            }
+        }
+    }
+    // On - Off Ramp on passing Motorway, Ramp onto Fork(?)
+    else if (intersection.size() == 4)
+    {
+        bool passed_highway_entry = false;
+        for (auto &road : intersection)
+        {
+            const auto &edge_data = node_based_graph.GetEdgeData(road.turn.eid);
+            if (!road.entry_allowed &&
+                detail::isMotorwayClass(edge_data.road_classification.road_class))
+            {
+                passed_highway_entry = true;
+            }
+            else if (detail::isMotorwayClass(edge_data.road_classification.road_class))
+            {
+                road.turn.instruction = {TurnType::Merge, passed_highway_entry
+                                                              ? DirectionModifier::SlightRight
+                                                              : DirectionModifier::SlightLeft};
+            }
+            else
+            {
+                BOOST_ASSERT(isRampClass(edge_data.road_classification.road_class));
+                road.turn.instruction = {TurnType::Ramp, getTurnDirection(road.turn.angle)};
+            }
+        }
+    }
+    else
+    { // FALLBACK, hopefully this should never been reached
+        util::SimpleLogger().Write(logDEBUG) << "Reached fallback on motorway ramp with "
+                                             << intersection.size() << " roads and "
+                                             << countValid(intersection) << " valid turns.";
+        return fallback(std::move(intersection));
+    }
+    return intersection;
+}
+
+Intersection MotorwayHandler::fallback(Intersection intersection) const
+{
+    for (auto &road : intersection)
+    {
+        const auto &out_data = node_based_graph.GetEdgeData(road.turn.eid);
+
+        util::SimpleLogger().Write(logDEBUG)
+            << "road: " << toString(road) << " Name: " << out_data.name_id
+            << " Road Class: " << (int)out_data.road_classification.road_class;
+
+        if (!road.entry_allowed)
+            continue;
+
+        const auto type = detail::isMotorwayClass(out_data.road_classification.road_class)
+                              ? TurnType::Merge
+                              : TurnType::Turn;
+
+        if (type == TurnType::Turn)
+        {
+            if (angularDeviation(road.turn.angle, STRAIGHT_ANGLE) < FUZZY_ANGLE_DIFFERENCE)
+                road.turn.instruction = {type, DirectionModifier::Straight};
+            else
+            {
+                road.turn.instruction = {type, road.turn.angle > STRAIGHT_ANGLE
+                                                   ? DirectionModifier::SlightLeft
+                                                   : DirectionModifier::SlightRight};
+            }
+        }
+        else
+        {
+            road.turn.instruction = {type, road.turn.angle < STRAIGHT_ANGLE
+                                               ? DirectionModifier::SlightLeft
+                                               : DirectionModifier::SlightRight};
+        }
+    }
+    return intersection;
+}
+
+} // namespace guidance
+} // namespace extractor
+} // namespace osrm
diff --git a/src/extractor/guidance/roundabout_handler.cpp b/src/extractor/guidance/roundabout_handler.cpp
new file mode 100644
index 0000000..6bd72dc
--- /dev/null
+++ b/src/extractor/guidance/roundabout_handler.cpp
@@ -0,0 +1,257 @@
+#include "extractor/guidance/constants.hpp"
+#include "extractor/guidance/roundabout_handler.hpp"
+#include "extractor/guidance/toolkit.hpp"
+
+#include "util/simple_logger.hpp"
+
+#include <cmath>
+#include <set>
+#include <unordered_set>
+
+#include <boost/assert.hpp>
+
+namespace osrm
+{
+namespace extractor
+{
+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)
+{
+}
+
+RoundaboutHandler::~RoundaboutHandler() {}
+
+bool RoundaboutHandler::canProcess(const NodeID from_nid,
+                                   const EdgeID via_eid,
+                                   const Intersection &intersection) const
+{
+    const auto flags = getRoundaboutFlags(from_nid, via_eid, intersection);
+    return flags.on_roundabout || flags.can_enter;
+}
+
+Intersection RoundaboutHandler::
+operator()(const NodeID from_nid, const EdgeID via_eid, Intersection intersection) const
+{
+    const auto flags = getRoundaboutFlags(from_nid, via_eid, intersection);
+    const bool is_rotary = isRotary(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));
+}
+
+detail::RoundaboutFlags RoundaboutHandler::getRoundaboutFlags(
+    const NodeID from_nid, const EdgeID via_eid, const Intersection &intersection) const
+{
+    const auto &in_edge_data = node_based_graph.GetEdgeData(via_eid);
+    bool on_roundabout = in_edge_data.roundabout;
+    bool can_enter_roundabout = false;
+    bool can_exit_roundabout_separately = false;
+    for (const auto &road : intersection)
+    {
+        const auto &edge_data = node_based_graph.GetEdgeData(road.turn.eid);
+        // only check actual outgoing edges
+        if (edge_data.reversed)
+            continue;
+
+        if (edge_data.roundabout)
+        {
+            can_enter_roundabout = true;
+        }
+        // Exiting roundabouts at an entry point is technically a data-modelling issue.
+        // This workaround handles cases in which an exit follows the entry.
+        // To correctly represent perceived exits, we only count exits leading to a
+        // separate vertex than the one we are coming from that are in the direction of
+        // the roundabout.
+        // 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.
+        // FIXME requires consideration of crossing the roundabout
+        else if (node_based_graph.GetTarget(road.turn.eid) != from_nid && !can_enter_roundabout)
+        {
+            can_exit_roundabout_separately = true;
+        }
+    }
+    return {on_roundabout, can_enter_roundabout, can_exit_roundabout_separately};
+}
+
+bool RoundaboutHandler::isRotary(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) {
+        return util::Coordinate(node_info_list[node].lon, node_info_list[node].lat);
+    };
+
+    unsigned roundabout_name_id = 0;
+    std::unordered_set<unsigned> connected_names;
+
+    const auto getNextOnRoundabout = [this, &roundabout_name_id,
+                                      &connected_names](const NodeID node) {
+        EdgeID continue_edge = SPECIAL_EDGEID;
+        for (const auto edge : node_based_graph.GetAdjacentEdgeRange(node))
+        {
+            const auto &edge_data = node_based_graph.GetEdgeData(edge);
+            if (!edge_data.reversed && edge_data.roundabout)
+            {
+                if (SPECIAL_EDGEID != continue_edge)
+                {
+                    // fork in roundabout
+                    return SPECIAL_EDGEID;
+                }
+                // roundabout does not keep its name
+                if (roundabout_name_id != 0 && roundabout_name_id != edge_data.name_id &&
+                    requiresNameAnnounced(name_table.GetNameForID(roundabout_name_id),
+                                          name_table.GetNameForID(edge_data.name_id)))
+                {
+                    return SPECIAL_EDGEID;
+                }
+
+                roundabout_name_id = edge_data.name_id;
+
+                continue_edge = edge;
+            }
+            else if (!edge_data.roundabout)
+            {
+                // remember all connected road names
+                connected_names.insert(edge_data.name_id);
+            }
+        }
+        return continue_edge;
+    };
+    // the roundabout radius has to be the same for all locations we look at it from
+    // to guarantee this, we search the full roundabout for its vertices
+    // and select the three smalles ids
+    std::set<NodeID> roundabout_nodes; // needs to be sorted
+
+    // this value is a hard abort to deal with potential self-loops
+    NodeID last_node = nid;
+    while (0 == roundabout_nodes.count(last_node))
+    {
+        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;
+        }
+
+        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 (roundabout_nodes.size() <= 1)
+    {
+        return false;
+    }
+    // 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).
+    // Otherwise, we construct a circle through the first tree vertices.
+    const auto getRadius = [&roundabout_nodes, &getCoordinate]() {
+        auto node_itr = roundabout_nodes.begin();
+        if (roundabout_nodes.size() == 2)
+        {
+            const auto first = getCoordinate(*node_itr++), second = getCoordinate(*node_itr++);
+            return 0.5 * util::coordinate_calculation::haversineDistance(first, second);
+        }
+        else
+        {
+            const auto first = getCoordinate(*node_itr++), second = getCoordinate(*node_itr++),
+                       third = getCoordinate(*node_itr++);
+            return util::coordinate_calculation::circleRadius(first, second, third);
+        }
+    };
+    const double radius = getRadius();
+
+    // check whether the circle computation has gone wrong
+    // 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 radius > MAX_ROUNDABOUT_RADIUS;
+}
+
+Intersection RoundaboutHandler::handleRoundabouts(const bool is_rotary,
+                                                  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)
+    {
+        // Shoule hopefully have only a single exit and continue
+        // at least for cars. How about bikes?
+        for (auto &road : intersection)
+        {
+            auto &turn = road.turn;
+            const auto &out_data = node_based_graph.GetEdgeData(road.turn.eid);
+            if (out_data.roundabout)
+            {
+                // TODO can forks happen in roundabouts? E.g. required lane changes
+                if (1 == node_based_graph.GetDirectedOutDegree(node_v))
+                {
+                    // No turn possible.
+                    turn.instruction = TurnInstruction::NO_TURN();
+                }
+                else
+                {
+                    turn.instruction =
+                        TurnInstruction::REMAIN_ROUNDABOUT(is_rotary, getTurnDirection(turn.angle));
+                }
+            }
+            else
+            {
+                turn.instruction =
+                    TurnInstruction::EXIT_ROUNDABOUT(is_rotary, getTurnDirection(turn.angle));
+            }
+        }
+        return intersection;
+    }
+    else
+        for (auto &road : intersection)
+        {
+            if (!road.entry_allowed)
+                continue;
+            auto &turn = road.turn;
+            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;
+                }
+            }
+            else
+            {
+                turn.instruction = TurnInstruction::ENTER_AND_EXIT_ROUNDABOUT(
+                    is_rotary, getTurnDirection(turn.angle));
+            }
+        }
+    return intersection;
+}
+
+} // namespace guidance
+} // namespace extractor
+} // namespace osrm
diff --git a/src/extractor/guidance/turn_analysis.cpp b/src/extractor/guidance/turn_analysis.cpp
index 0281e16..9413f87 100644
--- a/src/extractor/guidance/turn_analysis.cpp
+++ b/src/extractor/guidance/turn_analysis.cpp
@@ -1,11 +1,15 @@
+#include "extractor/guidance/constants.hpp"
 #include "extractor/guidance/turn_analysis.hpp"
 
-#include "util/simple_logger.hpp"
 #include "util/coordinate.hpp"
+#include "util/coordinate_calculation.hpp"
+#include "util/simple_logger.hpp"
 
 #include <cstddef>
-#include <limits>
 #include <iomanip>
+#include <limits>
+#include <set>
+#include <unordered_set>
 
 namespace osrm
 {
@@ -13,154 +17,54 @@ namespace extractor
 {
 namespace guidance
 {
-// configuration of turn classification
-const bool constexpr INVERT = true;
-
-// what angle is interpreted as going straight
-const double constexpr STRAIGHT_ANGLE = 180.;
-// if a turn deviates this much from going straight, it will be kept straight
-const double constexpr MAXIMAL_ALLOWED_NO_TURN_DEVIATION = 3.;
-// angle that lies between two nearly indistinguishable roads
-const double constexpr NARROW_TURN_ANGLE = 30.;
-const double constexpr GROUP_ANGLE = 90;
-// angle difference that can be classified as straight, if its the only narrow turn
-const double constexpr FUZZY_ANGLE_DIFFERENCE = 15.;
-const double constexpr DISTINCTION_RATIO = 2;
-const unsigned constexpr INVALID_NAME_ID = 0;
 
 using EdgeData = util::NodeBasedDynamicGraph::EdgeData;
 
-ConnectedRoad::ConnectedRoad(const TurnOperation turn, const bool entry_allowed)
-    : turn(turn), entry_allowed(entry_allowed)
-{
-}
-
 bool requiresAnnouncement(const EdgeData &from, const EdgeData &to)
 {
     return !from.IsCompatibleTo(to);
 }
 
-struct Localizer
-{
-    const std::vector<QueryNode> *node_info_list = nullptr;
-
-    util::Coordinate operator()(const NodeID nid)
-    {
-        if (node_info_list)
-        {
-            return {(*node_info_list)[nid].lon, (*node_info_list)[nid].lat};
-        }
-        return {};
-    }
-};
-
-static Localizer localizer;
-
 TurnAnalysis::TurnAnalysis(const util::NodeBasedDynamicGraph &node_based_graph,
                            const std::vector<QueryNode> &node_info_list,
                            const RestrictionMap &restriction_map,
                            const std::unordered_set<NodeID> &barrier_nodes,
                            const CompressedEdgeContainer &compressed_edge_container,
                            const util::NameTable &name_table)
-    : node_based_graph(node_based_graph), node_info_list(node_info_list),
-      restriction_map(restriction_map), barrier_nodes(barrier_nodes),
-      compressed_edge_container(compressed_edge_container), name_table(name_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)
 {
 }
 
-// some small tool functions to simplify decisions down the line
-namespace detail
+std::vector<TurnOperation> TurnAnalysis::getTurns(const NodeID from_nid, const EdgeID via_eid) const
 {
+    auto intersection = intersection_generator(from_nid, via_eid);
 
-inline FunctionalRoadClass roadClass(const ConnectedRoad &road,
-                                     const util::NodeBasedDynamicGraph &graph)
-{
-    return graph.GetEdgeData(road.turn.eid).road_classification.road_class;
-}
-
-inline bool isMotorwayClass(FunctionalRoadClass road_class)
-{
-    return road_class == FunctionalRoadClass::MOTORWAY || road_class == FunctionalRoadClass::TRUNK;
-}
-
-inline bool isMotorwayClass(EdgeID eid, const util::NodeBasedDynamicGraph &node_based_graph)
-{
-    return isMotorwayClass(node_based_graph.GetEdgeData(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);
-}
-
-} // namespace detail
-
-std::vector<TurnOperation> TurnAnalysis::getTurns(const NodeID from, const EdgeID via_edge) const
-{
-    localizer.node_info_list = &node_info_list;
-    auto intersection = getConnectedRoads(from, via_edge);
-
-    const auto &in_edge_data = node_based_graph.GetEdgeData(via_edge);
-
-    // main priority: roundabouts
-    bool on_roundabout = in_edge_data.roundabout;
-    bool can_enter_roundabout = false;
-    bool can_exit_roundabout_separately = false;
-    for (const auto &road : intersection)
+    // Roundabouts are a main priority. If there is a roundabout instruction present, we process the
+    // turn as a roundabout
+    if (roundabout_handler.canProcess(from_nid, via_eid, intersection))
     {
-        const auto &edge_data = node_based_graph.GetEdgeData(road.turn.eid);
-        // only check actual outgoing edges
-        if (edge_data.reversed)
-            continue;
-
-        if (edge_data.roundabout)
-        {
-            can_enter_roundabout = true;
-        }
-        // Exiting roundabouts at an entry point is technically a data-modelling issue.
-        // This workaround handles cases in which an exit follows the entry.
-        // To correctly represent perceived exits, we only count exits leading to a
-        // separate vertex than the one we are coming from that are in the direction of
-        // the roundabout.
-        // 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.
-        else if (node_based_graph.GetTarget(road.turn.eid) != from && !can_enter_roundabout)
-        {
-            can_exit_roundabout_separately = true;
-        }
-    }
-    if (on_roundabout || can_enter_roundabout)
-    {
-        intersection = handleRoundabouts(via_edge, on_roundabout, can_exit_roundabout_separately,
-                                         std::move(intersection));
+        intersection = roundabout_handler(from_nid, via_eid, std::move(intersection));
     }
     else
     {
         // set initial defaults for normal turns and modifier based on angle
-        intersection = setTurnTypes(from, via_edge, std::move(intersection));
-        if (isMotorwayJunction(via_edge, intersection))
+        intersection = setTurnTypes(from_nid, via_eid, std::move(intersection));
+        if (motorway_handler.canProcess(from_nid, via_eid, intersection))
         {
-            intersection = handleMotorwayJunction(via_edge, std::move(intersection));
-        }
-
-        else if (intersection.size() == 1)
-        {
-            intersection = handleOneWayTurn(std::move(intersection));
-        }
-        else if (intersection.size() == 2)
-        {
-            intersection = handleTwoWayTurn(via_edge, std::move(intersection));
-        }
-        else if (intersection.size() == 3)
-        {
-            intersection = handleThreeWayTurn(via_edge, std::move(intersection));
+            intersection = motorway_handler(from_nid, via_eid, std::move(intersection));
         }
         else
         {
-            intersection = handleComplexTurn(via_edge, std::move(intersection));
+            BOOST_ASSERT(turn_handler.canProcess(from_nid, via_eid, intersection));
+            intersection = turn_handler(from_nid, via_eid, std::move(intersection));
         }
-        // complex intersection, potentially requires conflict resolution
     }
 
     std::vector<TurnOperation> turns;
@@ -171,2139 +75,22 @@ std::vector<TurnOperation> TurnAnalysis::getTurns(const NodeID from, const EdgeI
     return turns;
 }
 
-inline std::size_t countValid(const std::vector<ConnectedRoad> &intersection)
-{
-    return std::count_if(intersection.begin(), intersection.end(), [](const ConnectedRoad &road)
-                         {
-                             return road.entry_allowed;
-                         });
-}
-
-std::vector<ConnectedRoad>
-TurnAnalysis::handleRoundabouts(const EdgeID via_edge,
-                                const bool on_roundabout,
-                                const bool can_exit_roundabout_separately,
-                                std::vector<ConnectedRoad> 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_edge);
-    if (on_roundabout)
-    {
-        // Shoule hopefully have only a single exit and continue
-        // at least for cars. How about bikes?
-        for (auto &road : intersection)
-        {
-            auto &turn = road.turn;
-            const auto &out_data = node_based_graph.GetEdgeData(road.turn.eid);
-            if (out_data.roundabout)
-            {
-                // TODO can forks happen in roundabouts? E.g. required lane changes
-                if (1 == node_based_graph.GetDirectedOutDegree(node_v))
-                {
-                    // No turn possible.
-                    turn.instruction = TurnInstruction::NO_TURN();
-                }
-                else
-                {
-                    turn.instruction =
-                        TurnInstruction::REMAIN_ROUNDABOUT(getTurnDirection(turn.angle));
-                }
-            }
-            else
-            {
-                turn.instruction = TurnInstruction::EXIT_ROUNDABOUT(getTurnDirection(turn.angle));
-            }
-        }
-        return intersection;
-    }
-    else
-    {
-        for (auto &road : intersection)
-        {
-            if (!road.entry_allowed)
-                continue;
-            auto &turn = road.turn;
-            const auto &out_data = node_based_graph.GetEdgeData(turn.eid);
-            if (out_data.roundabout)
-            {
-                turn.instruction = TurnInstruction::ENTER_ROUNDABOUT(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;
-                }
-            }
-            else
-            {
-                turn.instruction = {TurnType::EnterAndExitRoundabout, getTurnDirection(turn.angle)};
-            }
-        }
-        return intersection;
-    }
-}
-
-std::vector<ConnectedRoad>
-TurnAnalysis::fallbackTurnAssignmentMotorway(std::vector<ConnectedRoad> intersection) const
+// Sets basic turn types as fallback for otherwise unhandled turns
+Intersection TurnAnalysis::setTurnTypes(const NodeID from_nid,
+                                        const EdgeID,
+                                        Intersection intersection) const
 {
     for (auto &road : intersection)
     {
-        const auto &out_data = node_based_graph.GetEdgeData(road.turn.eid);
-
-        util::SimpleLogger().Write(logWARNING)
-            << "road: " << road.toString() << " Name: " << out_data.name_id
-            << " Road Class: " << (int)out_data.road_classification.road_class
-            << " At: " << localizer(node_based_graph.GetTarget(road.turn.eid));
-
         if (!road.entry_allowed)
             continue;
 
-        const auto type = detail::isMotorwayClass(out_data.road_classification.road_class)
-                              ? TurnType::Merge
-                              : TurnType::Turn;
-        if (angularDeviation(road.turn.angle, STRAIGHT_ANGLE) < FUZZY_ANGLE_DIFFERENCE)
-            road.turn.instruction = {type, DirectionModifier::Straight};
-        else
-        {
-            road.turn.instruction = {type,
-                                     road.turn.angle > STRAIGHT_ANGLE
-                                         ? DirectionModifier::SlightLeft
-                                         : DirectionModifier::SlightRight};
-        }
-    }
-    return intersection;
-}
-
-std::vector<ConnectedRoad>
-TurnAnalysis::handleFromMotorway(const EdgeID via_edge,
-                                 std::vector<ConnectedRoad> intersection) const
-{
-    const auto &in_data = node_based_graph.GetEdgeData(via_edge);
-    BOOST_ASSERT(detail::isMotorwayClass(in_data.road_classification.road_class));
-
-    const auto countExitingMotorways = [this](const std::vector<ConnectedRoad> &intersection)
-    {
-        unsigned count = 0;
-        for (const auto &road : intersection)
-        {
-            if (road.entry_allowed && detail::isMotorwayClass(road.turn.eid, node_based_graph))
-                ++count;
-        }
-        return count;
-    };
-
-    // find the angle that continues on our current highway
-    const auto getContinueAngle = [this, in_data](const std::vector<ConnectedRoad> &intersection)
-    {
-        for (const auto &road : intersection)
-        {
-            const auto &out_data = node_based_graph.GetEdgeData(road.turn.eid);
-            if (road.turn.angle != 0 && in_data.name_id == out_data.name_id &&
-                in_data.name_id != 0 &&
-                detail::isMotorwayClass(out_data.road_classification.road_class))
-                return road.turn.angle;
-        }
-        return intersection[0].turn.angle;
-    };
-
-    const auto getMostLikelyContinue =
-        [this, in_data](const std::vector<ConnectedRoad> &intersection)
-    {
-        double angle = intersection[0].turn.angle;
-        double best = 180;
-        for (const auto &road : intersection)
-        {
-            const auto &out_data = node_based_graph.GetEdgeData(road.turn.eid);
-            if (detail::isMotorwayClass(out_data.road_classification.road_class) &&
-                angularDeviation(road.turn.angle, STRAIGHT_ANGLE) < best)
-            {
-                best = angularDeviation(road.turn.angle, STRAIGHT_ANGLE);
-                angle = road.turn.angle;
-            }
-        }
-        return angle;
-    };
-
-    const auto findBestContinue = [&]()
-    {
-        const double continue_angle = getContinueAngle(intersection);
-        if (continue_angle != intersection[0].turn.angle)
-            return continue_angle;
-        else
-            return getMostLikelyContinue(intersection);
-    };
-
-    // find continue angle
-    const double continue_angle = findBestContinue();
-
-    // highway does not continue and has no obvious choice
-    if (continue_angle == intersection[0].turn.angle)
-    {
-        if (intersection.size() == 2)
-        {
-            // do not announce ramps at the end of a highway
-            intersection[1].turn.instruction = {TurnType::NoTurn,
-                                                getTurnDirection(intersection[1].turn.angle)};
-        }
-        else if (intersection.size() == 3)
-        {
-            // splitting ramp at the end of a highway
-            if (intersection[1].entry_allowed && intersection[2].entry_allowed)
-            {
-                assignFork(via_edge, intersection[2], intersection[1]);
-            }
-            else
-            {
-                // ending in a passing ramp
-                if (intersection[1].entry_allowed)
-                    intersection[1].turn.instruction = {
-                        TurnType::NoTurn, getTurnDirection(intersection[1].turn.angle)};
-                else
-                    intersection[2].turn.instruction = {
-                        TurnType::NoTurn, getTurnDirection(intersection[2].turn.angle)};
-            }
-        }
-        else if (intersection.size() == 4 &&
-                 detail::roadClass(intersection[1], node_based_graph) ==
-                     detail::roadClass(intersection[2], node_based_graph) &&
-                 detail::roadClass(intersection[2], node_based_graph) ==
-                     detail::roadClass(intersection[3], node_based_graph))
-        {
-            // tripple fork at the end
-            assignFork(via_edge, intersection[3], intersection[2], intersection[1]);
-        }
-        else if (countValid(intersection) > 0) // check whether turns exist at all
-        {
-            // FALLBACK, this should hopefully never be reached
-            auto coord = localizer(node_based_graph.GetTarget(via_edge));
-            util::SimpleLogger().Write(logWARNING)
-                << "Fallback reached from motorway at " << std::setprecision(12)
-                << toFloating(coord.lat) << " " << toFloating(coord.lon) << ", no continue angle, "
-                << intersection.size() << " roads, " << countValid(intersection) << " valid ones.";
-            fallbackTurnAssignmentMotorway(intersection);
-        }
-    }
-    else
-    {
-        const unsigned exiting_motorways = countExitingMotorways(intersection);
-
-        if (exiting_motorways == 0)
-        {
-            // Ending in Ramp
-            for (auto &road : intersection)
-            {
-                if (road.entry_allowed)
-                {
-                    BOOST_ASSERT(detail::isRampClass(road.turn.eid, node_based_graph));
-                    road.turn.instruction =
-                        TurnInstruction::SUPPRESSED(getTurnDirection(road.turn.angle));
-                }
-            }
-        }
-        else if (exiting_motorways == 1)
-        {
-            // normal motorway passing some ramps or mering onto another motorway
-            if (intersection.size() == 2)
-            {
-                BOOST_ASSERT(!detail::isRampClass(intersection[1].turn.eid, node_based_graph));
-
-                intersection[1].turn.instruction =
-                    getInstructionForObvious(intersection.size(), via_edge, intersection[1]);
-            }
-            else
-            {
-                // continue on the same highway
-                bool continues = (getContinueAngle(intersection) != intersection[0].turn.angle);
-                // Normal Highway exit or merge
-                for (auto &road : intersection)
-                {
-                    // ignore invalid uturns/other
-                    if (!road.entry_allowed)
-                        continue;
-
-                    if (road.turn.angle == continue_angle)
-                    {
-                        if (continues)
-                            road.turn.instruction =
-                                TurnInstruction::SUPPRESSED(DirectionModifier::Straight);
-                        else // TODO handle turn direction correctly
-                            road.turn.instruction = {TurnType::Merge, DirectionModifier::Straight};
-                    }
-                    else if (road.turn.angle < continue_angle)
-                    {
-                        road.turn.instruction = {
-                            detail::isRampClass(road.turn.eid, node_based_graph) ? TurnType::Ramp
-                                                                                 : 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,
-                            (road.turn.angle > 215) ? DirectionModifier::Left
-                                                    : DirectionModifier::SlightLeft};
-                    }
-                }
-            }
-        }
-        // handle motorway forks
-        else if (exiting_motorways > 1)
-        {
-            if (exiting_motorways == 2 && intersection.size() == 2)
-            {
-                intersection[1].turn.instruction =
-                    getInstructionForObvious(intersection.size(), via_edge, intersection[1]);
-                util::SimpleLogger().Write(logWARNING)
-                    << "Disabled U-Turn on a freeway at "
-                    << localizer(node_based_graph.GetTarget(via_edge));
-                intersection[0].entry_allowed = false; // UTURN on the freeway
-            }
-            else if (exiting_motorways == 2)
-            {
-                // standard fork
-                std::size_t first_valid = std::numeric_limits<std::size_t>::max(),
-                            second_valid = std::numeric_limits<std::size_t>::max();
-                for (std::size_t i = 0; i < intersection.size(); ++i)
-                {
-                    if (intersection[i].entry_allowed &&
-                        detail::isMotorwayClass(intersection[i].turn.eid, node_based_graph))
-                    {
-                        if (first_valid < intersection.size())
-                        {
-                            second_valid = i;
-                            break;
-                        }
-                        else
-                        {
-                            first_valid = i;
-                        }
-                    }
-                }
-                assignFork(via_edge, intersection[second_valid], intersection[first_valid]);
-            }
-            else if (exiting_motorways == 3)
-            {
-                // triple fork
-                std::size_t first_valid = std::numeric_limits<std::size_t>::max(),
-                            second_valid = std::numeric_limits<std::size_t>::max(),
-                            third_valid = std::numeric_limits<std::size_t>::max();
-                for (std::size_t i = 0; i < intersection.size(); ++i)
-                {
-                    if (intersection[i].entry_allowed &&
-                        detail::isMotorwayClass(intersection[i].turn.eid, node_based_graph))
-                    {
-                        if (second_valid < intersection.size())
-                        {
-                            third_valid = i;
-                            break;
-                        }
-                        else if (first_valid < intersection.size())
-                        {
-                            second_valid = i;
-                        }
-                        else
-                        {
-                            first_valid = i;
-                        }
-                    }
-                }
-                assignFork(via_edge, intersection[third_valid], intersection[second_valid],
-                           intersection[first_valid]);
-            }
-            else
-            {
-                auto coord = localizer(node_based_graph.GetTarget(via_edge));
-                util::SimpleLogger().Write(logWARNING)
-                    << "Found motorway junction with more than "
-                       "2 exiting motorways or additional ramps at " << std::setprecision(12)
-                    << toFloating(coord.lat) << " " << toFloating(coord.lon);
-                fallbackTurnAssignmentMotorway(intersection);
-            }
-        } // done for more than one highway exit
-    }
-    return intersection;
-}
-
-std::vector<ConnectedRoad>
-TurnAnalysis::handleMotorwayRamp(const EdgeID via_edge,
-                                 std::vector<ConnectedRoad> intersection) const
-{
-    auto num_valid_turns = countValid(intersection);
-    // ramp straight into a motorway/ramp
-    if (intersection.size() == 2 && num_valid_turns == 1)
-    {
-        BOOST_ASSERT(!intersection[0].entry_allowed);
-        BOOST_ASSERT(detail::isMotorwayClass(intersection[1].turn.eid, node_based_graph));
-
-        intersection[1].turn.instruction =
-            getInstructionForObvious(intersection.size(), via_edge, intersection[1]);
-    }
-    else if (intersection.size() == 3)
-    {
-        // merging onto a passing highway / or two ramps merging onto the same highway
-        if (num_valid_turns == 1)
-        {
-            BOOST_ASSERT(!intersection[0].entry_allowed);
-            // check order of highways
-            //          4
-            //     5         3
-            //
-            //   6              2
-            //
-            //     7         1
-            //          0
-            if (intersection[1].entry_allowed)
-            {
-                if (detail::isMotorwayClass(intersection[1].turn.eid, node_based_graph))
-                {
-                    // circular order indicates a merge to the left (0-3 onto 4
-                    if (angularDeviation(intersection[1].turn.angle, STRAIGHT_ANGLE) <
-                        NARROW_TURN_ANGLE)
-                        intersection[1].turn.instruction = {TurnType::Merge,
-                                                            DirectionModifier::SlightLeft};
-                    else // fallback
-                        intersection[1].turn.instruction = {
-                            TurnType::Merge, getTurnDirection(intersection[1].turn.angle)};
-                }
-                else // passing by the end of a motorway
-                    intersection[1].turn.instruction =
-                        getInstructionForObvious(intersection.size(), via_edge, intersection[1]);
-            }
-            else
-            {
-                BOOST_ASSERT(intersection[2].entry_allowed);
-                if (detail::isMotorwayClass(intersection[2].turn.eid, node_based_graph))
-                {
-                    // circular order (5-0) onto 4
-                    if (angularDeviation(intersection[2].turn.angle, STRAIGHT_ANGLE) <
-                        NARROW_TURN_ANGLE)
-                        intersection[2].turn.instruction = {TurnType::Merge,
-                                                            DirectionModifier::SlightRight};
-                    else // fallback
-                        intersection[2].turn.instruction = {
-                            TurnType::Merge, getTurnDirection(intersection[2].turn.angle)};
-                }
-                else // passing the end of a highway
-                    intersection[1].turn.instruction =
-                        getInstructionForObvious(intersection.size(), via_edge, intersection[1]);
-            }
-        }
-        else
-        {
-            BOOST_ASSERT(num_valid_turns == 2);
-            // UTurn on ramps is not possible
-            BOOST_ASSERT(!intersection[0].entry_allowed);
-            BOOST_ASSERT(intersection[1].entry_allowed);
-            BOOST_ASSERT(intersection[2].entry_allowed);
-            // two motorways starting at end of ramp (fork)
-            //  M       M
-            //    \   /
-            //      |
-            //      R
-            if (detail::isMotorwayClass(intersection[1].turn.eid, node_based_graph) &&
-                detail::isMotorwayClass(intersection[2].turn.eid, node_based_graph))
-            {
-                assignFork(via_edge, intersection[2], intersection[1]);
-            }
-            else
-            {
-                // continued ramp passing motorway entry
-                //      M  R
-                //      M  R
-                //      | /
-                //      R
-                if (detail::isMotorwayClass(node_based_graph.GetEdgeData(intersection[1].turn.eid)
-                                                .road_classification.road_class))
-                {
-                    intersection[1].turn.instruction = {TurnType::Merge,
-                                                        DirectionModifier::SlightRight};
-                    intersection[2].turn.instruction = {TurnType::Fork,
-                                                        DirectionModifier::SlightLeft};
-                }
-                else
-                {
-                    intersection[1].turn.instruction = {TurnType::Fork,
-                                                        DirectionModifier::SlightRight};
-                    intersection[2].turn.instruction = {TurnType::Merge,
-                                                        DirectionModifier::SlightLeft};
-                }
-            }
-        }
-    }
-    // On - Off Ramp on passing Motorway, Ramp onto Fork(?)
-    else if (intersection.size() == 4)
-    {
-        bool passed_highway_entry = false;
-        for (auto &road : intersection)
-        {
-            const auto &edge_data = node_based_graph.GetEdgeData(road.turn.eid);
-            if (!road.entry_allowed &&
-                detail::isMotorwayClass(edge_data.road_classification.road_class))
-            {
-                passed_highway_entry = true;
-            }
-            else if (detail::isMotorwayClass(edge_data.road_classification.road_class))
-            {
-                road.turn.instruction = {TurnType::Merge,
-                                         passed_highway_entry ? DirectionModifier::SlightRight
-                                                              : DirectionModifier::SlightLeft};
-            }
-            else
-            {
-                BOOST_ASSERT(isRampClass(edge_data.road_classification.road_class));
-                road.turn.instruction = {TurnType::Ramp, getTurnDirection(road.turn.angle)};
-            }
-        }
-    }
-    else
-    { // FALLBACK, hopefully this should never been reached
-        util::SimpleLogger().Write(logWARNING) << "Reached fallback on motorway ramp with "
-                                               << intersection.size() << " roads and "
-                                               << countValid(intersection) << " valid turns.";
-        fallbackTurnAssignmentMotorway(intersection);
-    }
-    return intersection;
-}
-
-std::vector<ConnectedRoad>
-TurnAnalysis::handleMotorwayJunction(const EdgeID via_edge,
-                                     std::vector<ConnectedRoad> intersection) const
-{
-    // BOOST_ASSERT(!intersection[0].entry_allowed); //This fails due to @themarex handling of dead
-    // end
-    // streets
-    const auto &in_data = node_based_graph.GetEdgeData(via_edge);
-
-    // coming from motorway
-    if (detail::isMotorwayClass(in_data.road_classification.road_class))
-    {
-        return handleFromMotorway(via_edge, std::move(intersection));
-    }
-    else // coming from a ramp
-    {
-        return handleMotorwayRamp(via_edge, std::move(intersection));
-        // ramp merging straight onto motorway
-    }
-}
-
-bool TurnAnalysis::isMotorwayJunction(const EdgeID via_edge,
-                                      const std::vector<ConnectedRoad> &intersection) const
-{
-    bool has_motorway = false;
-    bool has_normal_roads = false;
-
-    for (const auto &road : intersection)
-    {
-        const auto &out_data = node_based_graph.GetEdgeData(road.turn.eid);
-        // not merging or forking?
-        if ((angularDeviation(road.turn.angle, 0) > 35 &&
-             angularDeviation(road.turn.angle, 180) > 35) ||
-            (road.entry_allowed && angularDeviation(road.turn.angle, 0) < 35))
-            return false;
-        else if (out_data.road_classification.road_class == FunctionalRoadClass::MOTORWAY ||
-                 out_data.road_classification.road_class == FunctionalRoadClass::TRUNK)
-        {
-            if (road.entry_allowed)
-                has_motorway = true;
-        }
-        else if (!isRampClass(out_data.road_classification.road_class))
-            has_normal_roads = true;
-    }
-
-    if (has_normal_roads)
-        return false;
-
-    const auto &in_data = node_based_graph.GetEdgeData(via_edge);
-    return has_motorway ||
-           in_data.road_classification.road_class == FunctionalRoadClass::MOTORWAY ||
-           in_data.road_classification.road_class == FunctionalRoadClass::TRUNK;
-}
-
-TurnType TurnAnalysis::findBasicTurnType(const EdgeID via_edge, const ConnectedRoad &road) const
-{
-
-    const auto &in_data = node_based_graph.GetEdgeData(via_edge);
-    const auto &out_data = node_based_graph.GetEdgeData(road.turn.eid);
-
-    bool on_ramp = isRampClass(in_data.road_classification.road_class);
-
-    bool onto_ramp = isRampClass(out_data.road_classification.road_class);
-
-    if (!on_ramp && onto_ramp)
-        return TurnType::Ramp;
-
-    if (in_data.name_id == out_data.name_id && in_data.name_id != INVALID_NAME_ID)
-    {
-        return TurnType::Continue;
-    }
-
-    return TurnType::Turn;
-}
-
-TurnInstruction TurnAnalysis::getInstructionForObvious(const std::size_t num_roads,
-                                                       const EdgeID via_edge,
-                                                       const ConnectedRoad &road) const
-{
-    const auto type = findBasicTurnType(via_edge, road);
-    // 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)
-    {
-        return {TurnType::Ramp, getTurnDirection(road.turn.angle)};
-    }
-
-    if (angularDeviation(road.turn.angle, 0) < 0.01)
-    {
-        return {TurnType::Turn, DirectionModifier::UTurn};
-    }
-    if (type == TurnType::Turn)
-    {
-        const auto &in_data = node_based_graph.GetEdgeData(via_edge);
-        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)))
-            return {TurnType::NewName, getTurnDirection(road.turn.angle)};
-        else
-        {
-            if (in_mode == out_mode)
-                return {TurnType::Suppressed, getTurnDirection(road.turn.angle)};
-            else
-                return {TurnType::Notification, getTurnDirection(road.turn.angle)};
-        }
-    }
-    BOOST_ASSERT(type == TurnType::Continue);
-    if (in_mode != out_mode)
-    {
-        return {TurnType::Notification, getTurnDirection(road.turn.angle)};
-    }
-    if (num_roads > 2)
-    {
-        return {TurnType::Suppressed, getTurnDirection(road.turn.angle)};
-    }
-    else
-    {
-        return {TurnType::NoTurn, getTurnDirection(road.turn.angle)};
-    }
-}
-
-std::vector<ConnectedRoad>
-TurnAnalysis::handleOneWayTurn(std::vector<ConnectedRoad> intersection) const
-{
-    BOOST_ASSERT(intersection[0].turn.angle < 0.001);
-    return intersection;
-}
-
-std::vector<ConnectedRoad>
-TurnAnalysis::handleTwoWayTurn(const EdgeID via_edge, std::vector<ConnectedRoad> intersection) const
-{
-    BOOST_ASSERT(intersection[0].turn.angle < 0.001);
-    intersection[1].turn.instruction =
-        getInstructionForObvious(intersection.size(), via_edge, intersection[1]);
-
-    if (intersection[1].turn.instruction.type == TurnType::Suppressed)
-        intersection[1].turn.instruction.type = TurnType::NoTurn;
-
-    return intersection;
-}
-
-std::vector<ConnectedRoad>
-TurnAnalysis::handleThreeWayTurn(const EdgeID via_edge,
-                                 std::vector<ConnectedRoad> intersection) const
-{
-    BOOST_ASSERT(intersection[0].turn.angle < 0.001);
-    const auto isObviousOfTwo = [](const ConnectedRoad road, const ConnectedRoad other)
-    {
-        return (angularDeviation(road.turn.angle, STRAIGHT_ANGLE) < NARROW_TURN_ANGLE &&
-                angularDeviation(other.turn.angle, STRAIGHT_ANGLE) > 85) ||
-               (angularDeviation(road.turn.angle, STRAIGHT_ANGLE) <
-                std::numeric_limits<double>::epsilon()) ||
-               (angularDeviation(other.turn.angle, STRAIGHT_ANGLE) /
-                    angularDeviation(road.turn.angle, STRAIGHT_ANGLE) >
-                1.4);
-    };
-
-    /* Two nearly straight turns -> FORK
-                OOOOOOO
-              /
-       IIIIII
-              \
-                OOOOOOO
-    */
-    if (angularDeviation(intersection[1].turn.angle, STRAIGHT_ANGLE) < NARROW_TURN_ANGLE &&
-        angularDeviation(intersection[2].turn.angle, STRAIGHT_ANGLE) < NARROW_TURN_ANGLE)
-    {
-        if (intersection[1].entry_allowed && intersection[2].entry_allowed)
-        {
-            const auto left_class = node_based_graph.GetEdgeData(intersection[2].turn.eid)
-                                        .road_classification.road_class;
-            const auto right_class = node_based_graph.GetEdgeData(intersection[1].turn.eid)
-                                         .road_classification.road_class;
-            if (canBeSeenAsFork(left_class, right_class))
-                assignFork(via_edge, intersection[2], intersection[1]);
-            else if (getPriority(left_class) > getPriority(right_class))
-            {
-                intersection[1].turn.instruction =
-                    getInstructionForObvious(intersection.size(), via_edge, intersection[1]);
-                intersection[2].turn.instruction = {findBasicTurnType(via_edge, intersection[2]),
-                                                    DirectionModifier::SlightLeft};
-            }
-            else
-            {
-                intersection[2].turn.instruction =
-                    getInstructionForObvious(intersection.size(), via_edge, intersection[2]);
-                intersection[1].turn.instruction = {findBasicTurnType(via_edge, intersection[1]),
-                                                    DirectionModifier::SlightRight};
-            }
-        }
-        else
-        {
-            if (intersection[1].entry_allowed)
-                intersection[1].turn.instruction =
-                    getInstructionForObvious(intersection.size(), via_edge, intersection[1]);
-            if (intersection[2].entry_allowed)
-                intersection[2].turn.instruction =
-                    getInstructionForObvious(intersection.size(), via_edge, intersection[2]);
-        }
-    }
-    /*  T Intersection
-
-      OOOOOOO T OOOOOOOO
-              I
-              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)
-    {
-        if (intersection[1].entry_allowed)
-        {
-            if (TurnType::Ramp != findBasicTurnType(via_edge, intersection[1]))
-                intersection[1].turn.instruction = {TurnType::EndOfRoad, DirectionModifier::Right};
-            else
-                intersection[1].turn.instruction = {TurnType::Ramp, DirectionModifier::Right};
-        }
-        if (intersection[2].entry_allowed)
-        {
-            if (TurnType::Ramp != 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, 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, 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
-        {
-            return {isObviousOfTwo(turn, other) ? TurnType::Merge : 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);
-        }
-        else
-        {
-            intersection[1].turn.instruction = {TurnType::Continue,
-                                                getTurnDirection(intersection[1].turn.angle)};
-        }
-        intersection[2].turn.instruction = {TurnType::Turn,
-                                            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 = {TurnType::Turn,
-                                            getTurnDirection(intersection[1].turn.angle)};
-    }
-    else
-    {
-        if (isObviousOfTwo(intersection[1], intersection[2]))
-        {
-            intersection[1].turn.instruction = getInstructionForObvious(3,via_edge,intersection[1]);
-        }
-        else
-        {
-            intersection[1].turn.instruction = {TurnType::Turn,
-                                                getTurnDirection(intersection[1].turn.angle)};
-        }
-
-        if (isObviousOfTwo(intersection[2], intersection[1]))
-        {
-            intersection[2].turn.instruction = getInstructionForObvious(3,via_edge,intersection[2]);
-        }
-        else
-        {
-            intersection[2].turn.instruction = {TurnType::Turn,
-                                                getTurnDirection(intersection[2].turn.angle)};
-        }
-    }
-    // unnamed intersections or basic three way turn
-
-    // remain at basic turns
-    // TODO handle obviousness, Handle Merges
-    return intersection;
-}
+        const EdgeID onto_edge = road.turn.eid;
+        const NodeID to_nid = node_based_graph.GetTarget(onto_edge);
 
-void TurnAnalysis::handleDistinctConflict(const EdgeID via_edge,
-                                          ConnectedRoad &left,
-                                          ConnectedRoad &right) const
-{
-    // single turn of both is valid (don't change the valid one)
-    // or multiple identical angles -> bad OSM intersection
-    if ((!left.entry_allowed || !right.entry_allowed) || (left.turn.angle == right.turn.angle))
-    {
-        if (left.entry_allowed)
-            left.turn.instruction = {findBasicTurnType(via_edge, left),
-                                     getTurnDirection(left.turn.angle)};
-        if (right.entry_allowed)
-            right.turn.instruction = {findBasicTurnType(via_edge, right),
-                                      getTurnDirection(right.turn.angle)};
-        return;
-    }
-
-    if (getTurnDirection(left.turn.angle) == DirectionModifier::Straight ||
-        getTurnDirection(left.turn.angle) == DirectionModifier::SlightLeft ||
-        getTurnDirection(right.turn.angle) == DirectionModifier::SlightRight)
-    {
-        const auto left_class =
-            node_based_graph.GetEdgeData(left.turn.eid).road_classification.road_class;
-        const auto right_class =
-            node_based_graph.GetEdgeData(right.turn.eid).road_classification.road_class;
-        if (canBeSeenAsFork(left_class, right_class))
-            assignFork(via_edge, left, right);
-        else if (getPriority(left_class) > getPriority(right_class))
-        {
-            // FIXME this should possibly know about the actual roads?
-            right.turn.instruction = getInstructionForObvious(4, via_edge, right);
-            left.turn.instruction = {findBasicTurnType(via_edge, left),
-                                     DirectionModifier::SlightLeft};
-        }
-        else
-        {
-            // FIXME this should possibly know about the actual roads?
-            left.turn.instruction = getInstructionForObvious(4, via_edge, left);
-            right.turn.instruction = {findBasicTurnType(via_edge, right),
-                                      DirectionModifier::SlightRight};
-        }
-    }
-
-    const auto left_type = findBasicTurnType(via_edge, left);
-    const auto right_type = findBasicTurnType(via_edge, right);
-    // Two Right Turns
-    if (angularDeviation(left.turn.angle, 90) < MAXIMAL_ALLOWED_NO_TURN_DEVIATION)
-    {
-        // Keep left perfect, shift right
-        left.turn.instruction = {left_type, DirectionModifier::Right};
-        right.turn.instruction = {right_type, DirectionModifier::SharpRight};
-        return;
-    }
-    if (angularDeviation(right.turn.angle, 90) < MAXIMAL_ALLOWED_NO_TURN_DEVIATION)
-    {
-        // Keep Right perfect, shift left
-        left.turn.instruction = {left_type, DirectionModifier::SlightRight};
-        right.turn.instruction = {right_type, DirectionModifier::Right};
-        return;
-    }
-    // Two Right Turns
-    if (angularDeviation(left.turn.angle, 270) < MAXIMAL_ALLOWED_NO_TURN_DEVIATION)
-    {
-        // Keep left perfect, shift right
-        left.turn.instruction = {left_type, DirectionModifier::Left};
-        right.turn.instruction = {right_type, DirectionModifier::SlightLeft};
-        return;
-    }
-    if (angularDeviation(right.turn.angle, 270) < MAXIMAL_ALLOWED_NO_TURN_DEVIATION)
-    {
-        // Keep Right perfect, shift left
-        left.turn.instruction = {left_type, DirectionModifier::SharpLeft};
-        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)
-    {
-        left.turn.instruction = {left_type, DirectionModifier::SharpLeft};
-        right.turn.instruction = {right_type, DirectionModifier::Left};
-        return;
-    }
-    if (getTurnDirection(right.turn.angle) == DirectionModifier::SharpRight)
-    {
-        left.turn.instruction = {left_type, DirectionModifier::Right};
-        right.turn.instruction = {right_type, DirectionModifier::SharpRight};
-        return;
-    }
-
-    if (getTurnDirection(left.turn.angle) == DirectionModifier::Right)
-    {
-        if (angularDeviation(left.turn.angle, 90) > angularDeviation(right.turn.angle, 90))
-        {
-            left.turn.instruction = {left_type, DirectionModifier::SlightRight};
-            right.turn.instruction = {right_type, DirectionModifier::Right};
-        }
-        else
-        {
-            left.turn.instruction = {left_type, DirectionModifier::Right};
-            right.turn.instruction = {right_type, DirectionModifier::SharpRight};
-        }
-    }
-    else
-    {
-        if (angularDeviation(left.turn.angle, 270) > angularDeviation(right.turn.angle, 270))
-        {
-            left.turn.instruction = {left_type, DirectionModifier::SharpLeft};
-            right.turn.instruction = {right_type, DirectionModifier::Left};
-        }
-        else
-        {
-            left.turn.instruction = {left_type, DirectionModifier::Left};
-            right.turn.instruction = {right_type, DirectionModifier::SlightLeft};
-        }
-    }
-}
-
-std::vector<ConnectedRoad>
-TurnAnalysis::handleComplexTurn(const EdgeID via_edge,
-                                std::vector<ConnectedRoad> 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;
-    double straightmost_deviation = 180;
-    for (std::size_t i = 0; i < intersection.size(); ++i)
-    {
-        const double deviation = angularDeviation(intersection[i].turn.angle, STRAIGHT_ANGLE);
-        if (deviation < straightmost_deviation)
-        {
-            straightmost_deviation = deviation;
-            straightmost_turn = i;
-        }
-    }
-
-    if (obvious_index != 0)
-    {
-        intersection[obvious_index].turn.instruction =
-            getInstructionForObvious(intersection.size(), via_edge, intersection[obvious_index]);
-
-        // assign left/right turns
-        intersection = assignLeftTurns(via_edge, std::move(intersection), obvious_index + 1);
-        intersection = assignRightTurns(via_edge, std::move(intersection), obvious_index);
-    }
-    else if (fork_range.first != 0 && fork_range.second - fork_range.first <= 2) // found fork
-    {
-        if (fork_range.second - fork_range.first == 1)
-        {
-            auto &left = intersection[fork_range.second];
-            auto &right = intersection[fork_range.first];
-            const auto left_class =
-                node_based_graph.GetEdgeData(left.turn.eid).road_classification.road_class;
-            const auto right_class =
-                node_based_graph.GetEdgeData(right.turn.eid).road_classification.road_class;
-            if (canBeSeenAsFork(left_class, right_class))
-                assignFork(via_edge, left, right);
-            else if (getPriority(left_class) > getPriority(right_class))
-            {
-                right.turn.instruction =
-                    getInstructionForObvious(intersection.size(), via_edge, right);
-                left.turn.instruction = {findBasicTurnType(via_edge, left),
-                                         DirectionModifier::SlightLeft};
-            }
-            else
-            {
-                left.turn.instruction =
-                    getInstructionForObvious(intersection.size(), via_edge, left);
-                right.turn.instruction = {findBasicTurnType(via_edge, right),
-                                          DirectionModifier::SlightRight};
-            }
-        }
-        else if (fork_range.second - fork_range.second == 2)
-        {
-            assignFork(via_edge, intersection[fork_range.second],
-                       intersection[fork_range.first + 1], intersection[fork_range.first]);
-        }
-        // assign left/right turns
-        intersection = assignLeftTurns(via_edge, std::move(intersection), fork_range.second + 1);
-        intersection = assignRightTurns(via_edge, std::move(intersection), fork_range.first);
-    }
-    else if (straightmost_deviation < FUZZY_ANGLE_DIFFERENCE &&
-             !intersection[straightmost_turn].entry_allowed)
-    {
-        // invalid straight turn
-        intersection = assignLeftTurns(via_edge, std::move(intersection), straightmost_turn + 1);
-        intersection = assignRightTurns(via_edge, std::move(intersection), straightmost_turn);
-    }
-    // no straight turn
-    else if (intersection[straightmost_turn].turn.angle > 180)
-    {
-        // at most three turns on either side
-        intersection = assignLeftTurns(via_edge, std::move(intersection), straightmost_turn);
-        intersection = assignRightTurns(via_edge, std::move(intersection), straightmost_turn);
-    }
-    else if (intersection[straightmost_turn].turn.angle < 180)
-    {
-        intersection = assignLeftTurns(via_edge, std::move(intersection), straightmost_turn + 1);
-        intersection = assignRightTurns(via_edge, std::move(intersection), straightmost_turn + 1);
-    }
-    else
-    {
-        if (fallback_count++ < 10)
-        {
-            const auto coord = localizer(node_based_graph.GetTarget(via_edge));
-            util::SimpleLogger().Write(logWARNING)
-                << "Resolved to keep fallback on complex turn assignment at "
-                << std::setprecision(12) << toFloating(coord.lat) << " " << toFloating(coord.lon)
-                << "Straightmost: " << straightmost_turn;
-            ;
-            for (const auto &road : intersection)
-            {
-                const auto &out_data = node_based_graph.GetEdgeData(road.turn.eid);
-                util::SimpleLogger().Write(logWARNING)
-                    << "road: " << road.toString() << " Name: " << out_data.name_id
-                    << " Road Class: " << (int)out_data.road_classification.road_class
-                    << " At: " << localizer(node_based_graph.GetTarget(road.turn.eid));
-            }
-        }
-    }
-    return intersection;
-}
-
-// Sets basic turn types as fallback for otherwise unhandled turns
-std::vector<ConnectedRoad> TurnAnalysis::setTurnTypes(const NodeID from,
-                                                      const EdgeID via_edge,
-                                                      std::vector<ConnectedRoad> intersection) const
-{
-    for (auto &road : intersection)
-    {
-        if (!road.entry_allowed)
-            continue;
-
-        const EdgeID onto_edge = road.turn.eid;
-        const NodeID to_node = node_based_graph.GetTarget(onto_edge);
-
-        road.turn.instruction = (from == to_node)
-                                    ? TurnInstruction{TurnType::Turn, DirectionModifier::UTurn}
-                                    : TurnInstruction{findBasicTurnType(via_edge, road),
-                                                      getTurnDirection(road.turn.angle)};
-    }
-    return intersection;
-}
-
-//                                               a
-//                                               |
-//                                               |
-//                                               v
-// For an intersection from_node --via_edi--> turn_node ----> c
-//                                               ^
-//                                               |
-//                                               |
-//                                               b
-// This functions returns _all_ turns as if the graph was undirected.
-// That means we not only get (from_node, turn_node, c) in the above example
-// but also (from_node, turn_node, a), (from_node, turn_node, b). These turns are
-// marked as invalid and only needed for intersection classification.
-std::vector<ConnectedRoad> TurnAnalysis::getConnectedRoads(const NodeID from_node,
-                                                           const EdgeID via_eid) const
-{
-    std::vector<ConnectedRoad> intersection;
-    const NodeID turn_node = node_based_graph.GetTarget(via_eid);
-    const NodeID only_restriction_to_node =
-        restriction_map.CheckForEmanatingIsOnlyTurn(from_node, turn_node);
-    const bool is_barrier_node = barrier_nodes.find(turn_node) != barrier_nodes.end();
-
-    bool has_uturn_edge = false;
-    for (const EdgeID onto_edge : node_based_graph.GetAdjacentEdgeRange(turn_node))
-    {
-        BOOST_ASSERT(onto_edge != SPECIAL_EDGEID);
-        const NodeID to_node = node_based_graph.GetTarget(onto_edge);
-
-        bool turn_is_valid =
-            // reverse edges are never valid turns because the resulting turn would look like this:
-            // from_node --via_edge--> turn_node <--onto_edge-- to_node
-            // however we need this for capture intersection shape for incoming one-ways
-            !node_based_graph.GetEdgeData(onto_edge).reversed &&
-            // we are not turning over a barrier
-            (!is_barrier_node || from_node == to_node) &&
-            // We are at an only_-restriction but not at the right turn.
-            (only_restriction_to_node == SPECIAL_NODEID || to_node == only_restriction_to_node) &&
-            // the turn is not restricted
-            !restriction_map.CheckIfTurnIsRestricted(from_node, turn_node, to_node);
-
-        auto angle = 0.;
-        if (from_node == to_node)
-        {
-            if (turn_is_valid && !is_barrier_node)
-            {
-                // we only add u-turns for dead-end streets.
-                if (node_based_graph.GetOutDegree(turn_node) > 1)
-                {
-                    auto number_of_emmiting_bidirectional_edges = 0;
-                    for (auto edge : node_based_graph.GetAdjacentEdgeRange(turn_node))
-                    {
-                        auto target = node_based_graph.GetTarget(edge);
-                        auto reverse_edge = node_based_graph.FindEdge(target, turn_node);
-                        BOOST_ASSERT(reverse_edge != SPECIAL_EDGEID);
-                        if (!node_based_graph.GetEdgeData(reverse_edge).reversed)
-                        {
-                            ++number_of_emmiting_bidirectional_edges;
-                        }
-                    }
-                    // is a dead-end
-                    turn_is_valid = number_of_emmiting_bidirectional_edges <= 1;
-                }
-            }
-            has_uturn_edge = true;
-            BOOST_ASSERT(angle >= 0. && angle < std::numeric_limits<double>::epsilon());
-        }
-        else
-        {
-            // unpack first node of second segment if packed
-            const auto first_coordinate = getRepresentativeCoordinate(
-                from_node, turn_node, via_eid, INVERT, compressed_edge_container, node_info_list);
-            const auto third_coordinate = getRepresentativeCoordinate(
-                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())
-                has_uturn_edge = true;
-        }
-
-        intersection.push_back(ConnectedRoad(
-            TurnOperation{onto_edge, angle, {TurnType::Invalid, DirectionModifier::UTurn}},
-            turn_is_valid));
-    }
-
-    // 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.
-    if (!has_uturn_edge)
-    {
-        intersection.push_back(
-            {TurnOperation{via_eid, 0., {TurnType::Invalid, DirectionModifier::UTurn}}, false});
-    }
-
-    const auto ByAngle = [](const ConnectedRoad &first, const ConnectedRoad second)
-    {
-        return first.turn.angle < second.turn.angle;
-    };
-    std::sort(std::begin(intersection), std::end(intersection), ByAngle);
-
-    BOOST_ASSERT(intersection[0].turn.angle >= 0. &&
-                 intersection[0].turn.angle < std::numeric_limits<double>::epsilon());
-
-    return mergeSegregatedRoads(std::move(intersection));
-}
-
-/*
- * Segregated Roads often merge onto a single intersection.
- * While technically representing different roads, they are
- * often looked at as a single road.
- * Due to the merging, turn Angles seem off, wenn we compute them from the
- * initial positions.
- *
- *         b<b<b<b(1)<b<b<b
- * aaaaa-b
- *         b>b>b>b(2)>b>b>b
- *
- * Would be seen as a slight turn going fro a to (2). A Sharp turn going from
- * (1) to (2).
- *
- * In cases like these, we megre this segregated roads into a single road to
- * end up with a case like:
- *
- * aaaaa-bbbbbb
- *
- * for the turn representation.
- * Anything containing the first u-turn in a merge affects all other angles
- * and is handled separately from all others.
- */
-std::vector<ConnectedRoad>
-TurnAnalysis::mergeSegregatedRoads(std::vector<ConnectedRoad> intersection) const
-{
-    const auto getRight = [&](std::size_t index)
-    {
-        return (index + intersection.size() - 1) % intersection.size();
-    };
-
-    const auto mergable = [&](std::size_t first, std::size_t second) -> bool
-    {
-        const auto &first_data = node_based_graph.GetEdgeData(intersection[first].turn.eid);
-        const auto &second_data = node_based_graph.GetEdgeData(intersection[second].turn.eid);
-
-        return first_data.name_id != INVALID_NAME_ID && first_data.name_id == second_data.name_id &&
-               !first_data.roundabout && !second_data.roundabout &&
-               first_data.travel_mode == second_data.travel_mode &&
-               first_data.road_classification == second_data.road_classification &&
-               // compatible threshold
-               angularDeviation(intersection[first].turn.angle, intersection[second].turn.angle) <
-                   60 &&
-               first_data.reversed != second_data.reversed;
-    };
-
-    const auto merge = [](const ConnectedRoad &first, const ConnectedRoad &second) -> ConnectedRoad
-    {
-        if (!first.entry_allowed)
-        {
-            ConnectedRoad result = second;
-            result.turn.angle = (first.turn.angle + second.turn.angle) / 2;
-            if (first.turn.angle - second.turn.angle > 180)
-                result.turn.angle += 180;
-            if (result.turn.angle > 360)
-                result.turn.angle -= 360;
-
-            return result;
-        }
-        else
-        {
-            BOOST_ASSERT(!second.entry_allowed);
-            ConnectedRoad result = first;
-            result.turn.angle = (first.turn.angle + second.turn.angle) / 2;
-
-            if (first.turn.angle - second.turn.angle > 180)
-                result.turn.angle += 180;
-            if (result.turn.angle > 360)
-                result.turn.angle -= 360;
-
-            return result;
-        }
-    };
-    if (intersection.size() == 1)
-        return intersection;
-
-    // check for merges including the basic u-turn
-    // these result in an adjustment of all other angles
-    if (mergable(0, intersection.size() - 1))
-    {
-        // std::cout << "First merge" << std::endl;
-        const double correction_factor =
-            (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;
-        intersection[0] = merge(intersection.front(), intersection.back());
-        intersection[0].turn.angle = 0;
-        intersection.pop_back();
-    }
-    else if (mergable(0, 1))
-    {
-        // std::cout << "First merge" << std::endl;
-        const double correction_factor = (intersection[1].turn.angle) / 2;
-        for (std::size_t i = 2; i < intersection.size(); ++i)
-            intersection[i].turn.angle += correction_factor;
-        intersection[0] = merge(intersection[0], intersection[1]);
-        intersection[0].turn.angle = 0;
-        intersection.erase(intersection.begin() + 1);
-    }
-
-    // a merge including the first u-turn requres an adjustment of the turn angles
-    // therefore these are handled prior to this step
-    for (std::size_t index = 2; index < intersection.size(); ++index)
-    {
-        if (mergable(index, getRight(index)))
-        {
-            intersection[getRight(index)] =
-                merge(intersection[getRight(index)], intersection[index]);
-            intersection.erase(intersection.begin() + index);
-            --index;
-        }
-    }
-
-    const auto ByAngle = [](const ConnectedRoad &first, const ConnectedRoad second)
-    {
-        return first.turn.angle < second.turn.angle;
-    };
-    std::sort(std::begin(intersection), std::end(intersection), ByAngle);
-    return intersection;
-}
-
-void TurnAnalysis::assignFork(const EdgeID via_edge,
-                              ConnectedRoad &left,
-                              ConnectedRoad &right) const
-{
-    const auto &in_data = node_based_graph.GetEdgeData(via_edge);
-    const bool low_priority_left = isLowPriorityRoadClass(
-        node_based_graph.GetEdgeData(left.turn.eid).road_classification.road_class);
-    const bool low_priority_right = isLowPriorityRoadClass(
-        node_based_graph.GetEdgeData(right.turn.eid).road_classification.road_class);
-    if ((angularDeviation(left.turn.angle, STRAIGHT_ANGLE) < MAXIMAL_ALLOWED_NO_TURN_DEVIATION &&
-         angularDeviation(right.turn.angle, STRAIGHT_ANGLE) > FUZZY_ANGLE_DIFFERENCE))
-    {
-        // left side is actually straight
-        const auto &out_data = node_based_graph.GetEdgeData(left.turn.eid);
-        if (requiresAnnouncement(in_data, out_data))
-        {
-            if (low_priority_right && !low_priority_left)
-            {
-                left.turn.instruction = getInstructionForObvious(3, via_edge, left);
-                right.turn.instruction = {findBasicTurnType(via_edge, right),
-                                          DirectionModifier::SlightRight};
-            }
-            else
-            {
-                if (low_priority_left && !low_priority_right)
-                {
-                    left.turn.instruction = {findBasicTurnType(via_edge, left),
-                                             DirectionModifier::SlightLeft};
-                    right.turn.instruction = {findBasicTurnType(via_edge, right),
-                                              DirectionModifier::SlightRight};
-                }
-                else
-                {
-                    left.turn.instruction = {TurnType::Fork, DirectionModifier::SlightLeft};
-                    right.turn.instruction = {TurnType::Fork, DirectionModifier::SlightRight};
-                }
-            }
-        }
-        else
-        {
-            left.turn.instruction = {TurnType::Suppressed, DirectionModifier::Straight};
-            right.turn.instruction = {findBasicTurnType(via_edge, right),
-                                      DirectionModifier::SlightRight};
-        }
-    }
-    else if (angularDeviation(right.turn.angle, STRAIGHT_ANGLE) <
-                 MAXIMAL_ALLOWED_NO_TURN_DEVIATION &&
-             angularDeviation(left.turn.angle, STRAIGHT_ANGLE) > FUZZY_ANGLE_DIFFERENCE)
-    {
-        // right side is actually straight
-        const auto &out_data = node_based_graph.GetEdgeData(right.turn.eid);
-        if (angularDeviation(right.turn.angle, STRAIGHT_ANGLE) <
-                MAXIMAL_ALLOWED_NO_TURN_DEVIATION &&
-            angularDeviation(left.turn.angle, STRAIGHT_ANGLE) > FUZZY_ANGLE_DIFFERENCE)
-        {
-            if (requiresAnnouncement(in_data, out_data))
-            {
-                if (low_priority_left && !low_priority_right)
-                {
-                    left.turn.instruction = {findBasicTurnType(via_edge, left),
-                                             DirectionModifier::SlightLeft};
-                    right.turn.instruction = getInstructionForObvious(3, via_edge, right);
-                }
-                else
-                {
-                    if (low_priority_right && !low_priority_left)
-                    {
-                        left.turn.instruction = {findBasicTurnType(via_edge, left),
-                                                 DirectionModifier::SlightLeft};
-                        right.turn.instruction = {findBasicTurnType(via_edge, right),
-                                                  DirectionModifier::SlightRight};
-                    }
-                    else
-                    {
-                        right.turn.instruction = {TurnType::Fork, DirectionModifier::SlightRight};
-                        left.turn.instruction = {TurnType::Fork, DirectionModifier::SlightLeft};
-                    }
-                }
-            }
-            else
-            {
-                right.turn.instruction = {TurnType::Suppressed, DirectionModifier::Straight};
-                left.turn.instruction = {findBasicTurnType(via_edge, left),
-                                         DirectionModifier::SlightLeft};
-            }
-        }
-    }
-    else
-    {
-        // left side of fork
-        if (low_priority_right && !low_priority_left)
-            left.turn.instruction = {TurnType::Suppressed, DirectionModifier::SlightLeft};
-        else
-        {
-            if (low_priority_left && !low_priority_right)
-                left.turn.instruction = {TurnType::Turn, DirectionModifier::SlightLeft};
-            else
-                left.turn.instruction = {TurnType::Fork, DirectionModifier::SlightLeft};
-        }
-
-        // right side of fork
-        if (low_priority_left && !low_priority_right)
-            right.turn.instruction = {TurnType::Suppressed, DirectionModifier::SlightLeft};
-        else
-        {
-            if (low_priority_right && !low_priority_left)
-                right.turn.instruction = {TurnType::Turn, DirectionModifier::SlightRight};
-            else
-                right.turn.instruction = {TurnType::Fork, DirectionModifier::SlightRight};
-        }
-    }
-}
-
-void TurnAnalysis::assignFork(const EdgeID via_edge,
-                              ConnectedRoad &left,
-                              ConnectedRoad &center,
-                              ConnectedRoad &right) const
-{
-    // TODO handle low priority road classes in a reasonable way
-    if (left.entry_allowed && center.entry_allowed && right.entry_allowed)
-    {
-        left.turn.instruction = {TurnType::Fork, DirectionModifier::SlightLeft};
-        if (angularDeviation(center.turn.angle, 180) < MAXIMAL_ALLOWED_NO_TURN_DEVIATION)
-        {
-            const auto &in_data = node_based_graph.GetEdgeData(via_edge);
-            const auto &out_data = node_based_graph.GetEdgeData(center.turn.eid);
-            if (requiresAnnouncement(in_data, out_data))
-            {
-                center.turn.instruction = {TurnType::Fork, DirectionModifier::Straight};
-            }
-            else
-            {
-                center.turn.instruction = {TurnType::Suppressed, DirectionModifier::Straight};
-            }
-        }
-        else
-        {
-            center.turn.instruction = {TurnType::Fork, DirectionModifier::Straight};
-        }
-        right.turn.instruction = {TurnType::Fork, DirectionModifier::SlightRight};
-    }
-    else if (left.entry_allowed)
-    {
-        if (right.entry_allowed)
-            assignFork(via_edge, left, right);
-        else if (center.entry_allowed)
-            assignFork(via_edge, left, center);
-        else
-            left.turn.instruction = {findBasicTurnType(via_edge, left),
-                                     getTurnDirection(left.turn.angle)};
-    }
-    else if (right.entry_allowed)
-    {
-        if (center.entry_allowed)
-            assignFork(via_edge, center, right);
-        else
-            right.turn.instruction = {findBasicTurnType(via_edge, right),
-                                      getTurnDirection(right.turn.angle)};
-    }
-    else
-    {
-        if (center.entry_allowed)
-            center.turn.instruction = {findBasicTurnType(via_edge, center),
-                                       getTurnDirection(center.turn.angle)};
-    }
-}
-
-std::size_t TurnAnalysis::findObviousTurn(const EdgeID via_edge,
-                                          const std::vector<ConnectedRoad> &intersection) const
-{
-    // no obvious road
-    if (intersection.size() == 1)
-        return 0;
-
-    // a single non u-turn is obvious
-    if (intersection.size() == 2)
-        return 1;
-
-    // at least three roads
-    std::size_t best = 0;
-    double best_deviation = 180;
-
-    std::size_t best_continue = 0;
-    double best_continue_deviation = 180;
-
-    const EdgeData &in_data = node_based_graph.GetEdgeData(via_edge);
-    for (std::size_t i = 1; i < intersection.size(); ++i)
-    {
-        const double deviation = angularDeviation(intersection[i].turn.angle, STRAIGHT_ANGLE);
-        if (intersection[i].entry_allowed && deviation < best_deviation)
-        {
-            best_deviation = deviation;
-            best = i;
-        }
-
-        const auto out_data = node_based_graph.GetEdgeData(intersection[i].turn.eid);
-        if (intersection[i].entry_allowed && out_data.name_id == in_data.name_id &&
-            deviation < best_continue_deviation)
-        {
-            best_continue_deviation = deviation;
-            best_continue = i;
-        }
-    }
-
-    if (best == 0)
-        return 0;
-
-    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)
-    {
-        // Find left/right deviation
-        const double left_deviation = angularDeviation(
-            intersection[(best + 1) % intersection.size()].turn.angle, STRAIGHT_ANGLE);
-        const double right_deviation =
-            angularDeviation(intersection[best - 1].turn.angle, STRAIGHT_ANGLE);
-
-        if (best_deviation < MAXIMAL_ALLOWED_NO_TURN_DEVIATION &&
-            std::min(left_deviation, right_deviation) > FUZZY_ANGLE_DIFFERENCE)
-            return best;
-
-        // other narrow turns?
-        if (angularDeviation(intersection[best - 1].turn.angle, STRAIGHT_ANGLE) <=
-            FUZZY_ANGLE_DIFFERENCE)
-            return 0;
-        if (angularDeviation(intersection[(best + 1) % intersection.size()].turn.angle,
-                             STRAIGHT_ANGLE) <= FUZZY_ANGLE_DIFFERENCE)
-            return 0;
-
-        // Well distinct turn that is nearly straight
-        if (left_deviation / best_deviation >= DISTINCTION_RATIO &&
-            right_deviation / best_deviation >= DISTINCTION_RATIO)
-        {
-            return best;
-        }
-    }
-
-    return 0; // no obvious turn
-}
-
-std::pair<std::size_t, std::size_t>
-TurnAnalysis::findFork(const std::vector<ConnectedRoad> &intersection) const
-{
-
-    std::size_t best = 0;
-    double best_deviation = 180;
-
-    // TODO handle road classes
-    for (std::size_t i = 1; i < intersection.size(); ++i)
-    {
-        const double deviation = angularDeviation(intersection[i].turn.angle, STRAIGHT_ANGLE);
-        if (intersection[i].entry_allowed && deviation < best_deviation)
-        {
-            best_deviation = deviation;
-            best = i;
-        }
-    }
-
-    if (best_deviation <= NARROW_TURN_ANGLE)
-    {
-        std::size_t left = best, right = best;
-        while (left + 1 < intersection.size() &&
-               angularDeviation(intersection[left].turn.angle, intersection[left + 1].turn.angle) <
-                   NARROW_TURN_ANGLE)
-            ++left;
-        while (right > 1 &&
-               angularDeviation(intersection[right].turn.angle,
-                                intersection[right - 1].turn.angle) < NARROW_TURN_ANGLE)
-            --right;
-
-        // TODO check whether 2*NARROW_TURN is too large
-        if (right < left &&
-            angularDeviation(intersection[left].turn.angle,
-                             intersection[(left + 1) % intersection.size()].turn.angle) >=
-                2 * NARROW_TURN_ANGLE &&
-            angularDeviation(intersection[right].turn.angle, intersection[right - 1].turn.angle) >=
-                2 * NARROW_TURN_ANGLE)
-            return std::make_pair(right, left);
-    }
-    return std::make_pair(0llu, 0llu);
-}
-
-// Can only assign three turns
-std::vector<ConnectedRoad> TurnAnalysis::assignLeftTurns(const EdgeID via_edge,
-                                                         std::vector<ConnectedRoad> 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)
-    {
-        if (!intersection[starting_at].entry_allowed)
-            return intersection;
-
-        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]), second_direction};
-        }
-        else if (2 >= (intersection[starting_at].entry_allowed +
-                       intersection[starting_at + 1].entry_allowed +
-                       intersection[starting_at + 2].entry_allowed))
-        {
-            // 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
-        {
-            auto coord = localizer(node_based_graph.GetTarget(via_edge));
-            util::SimpleLogger().Write(logWARNING)
-                << "Reached fallback for left turns, size 3: " << std::setprecision(12)
-                << toFloating(coord.lat) << " " << toFloating(coord.lon);
-            for (const auto road : intersection)
-            {
-                const auto &out_data = node_based_graph.GetEdgeData(road.turn.eid);
-                util::SimpleLogger().Write(logWARNING)
-                    << "\troad: " << road.toString() << " Name: " << out_data.name_id
-                    << " Road Class: " << (int)out_data.road_classification.road_class
-                    << " At: " << localizer(node_based_graph.GetTarget(road.turn.eid));
-            }
-
-            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)};
-        }
-        /*
-        auto coord = localizer(node_based_graph.GetTarget(via_edge));
-        util::SimpleLogger().Write(logWARNING)
-            << "Reached fallback for left turns (" << starting_at << ") " << std::setprecision(12)
-            << toFloating(coord.lat) << " " << toFloating(coord.lon);
-        for (const auto road : intersection)
-        {
-            const auto &out_data = node_based_graph.GetEdgeData(road.turn.eid);
-            util::SimpleLogger().Write(logWARNING)
-                << "\troad: " << road.toString() << " Name: " << out_data.name_id
-                << " Road Class: " << (int)out_data.road_classification.road_class
-                << " At: " << localizer(node_based_graph.GetTarget(road.turn.eid));
-        }
-        */
-    }
-    return intersection;
-}
-
-// can only assign three turns
-std::vector<ConnectedRoad> TurnAnalysis::assignRightTurns(const EdgeID via_edge,
-                                                          std::vector<ConnectedRoad> intersection,
-                                                          const std::size_t up_to) const
-{
-    BOOST_ASSERT(up_to <= intersection.size());
-    const auto count_valid = [&intersection, up_to]()
-    {
-        std::size_t count = 0;
-        for (std::size_t i = 1; i < up_to; ++i)
-            if (intersection[i].entry_allowed)
-                ++count;
-        return count;
-    };
-    if (up_to <= 1 || count_valid() == 0)
-        return intersection;
-    // 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};
-        }
-    }
-    else if (up_to == 3)
-    {
-        const auto first_direction = getTurnDirection(intersection[1].turn.angle);
-        const auto second_direction = getTurnDirection(intersection[2].turn.angle);
-        if (first_direction == second_direction)
-        {
-            // conflict
-            handleDistinctConflict(via_edge, intersection[2], intersection[1]);
-        }
-        else
-        {
-            intersection[1].turn.instruction = {findBasicTurnType(via_edge, intersection[1]),
-                                                first_direction};
-            intersection[2].turn.instruction = {findBasicTurnType(via_edge, intersection[2]),
-                                                second_direction};
-        }
-    }
-    else if (up_to == 4)
-    {
-        const auto first_direction = getTurnDirection(intersection[1].turn.angle);
-        const auto second_direction = getTurnDirection(intersection[2].turn.angle);
-        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};
-        }
-        else if (2 >= (intersection[1].entry_allowed + intersection[2].entry_allowed +
-                       intersection[3].entry_allowed))
-        {
-            // at least a single invalid
-            if (!intersection[3].entry_allowed)
-            {
-                handleDistinctConflict(via_edge, intersection[2], intersection[1]);
-            }
-            else if (!intersection[1].entry_allowed)
-            {
-                handleDistinctConflict(via_edge, intersection[3], intersection[2]);
-            }
-            else // handles one-valid as well as two valid (1,3)
-            {
-                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) >=
-                     NARROW_TURN_ANGLE &&
-                 angularDeviation(intersection[2].turn.angle, intersection[3].turn.angle) >=
-                     NARROW_TURN_ANGLE)
-        {
-            intersection[1].turn.instruction = {findBasicTurnType(via_edge, intersection[1]),
-                                                DirectionModifier::SharpRight};
-            intersection[2].turn.instruction = {findBasicTurnType(via_edge, intersection[2]),
-                                                DirectionModifier::Right};
-            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) ||
-                  (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)))
-        {
-            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};
-        }
-        else if (intersection[1].entry_allowed && intersection[2].entry_allowed &&
-                 intersection[3].entry_allowed &&
-                 ((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)))
-        {
-            if (angularDeviation(intersection[2].turn.angle, intersection[3].turn.angle) >=
-                GROUP_ANGLE)
-            {
-                handleDistinctConflict(via_edge, intersection[2], intersection[1]);
-                intersection[3].turn.instruction = {findBasicTurnType(via_edge, intersection[3]),
-                                                    third_direction};
-            }
-            else
-            {
-                intersection[1].turn.instruction = {findBasicTurnType(via_edge, intersection[1]),
-                                                    first_direction};
-                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
-        {
-            auto coord = localizer(node_based_graph.GetTarget(via_edge));
-            util::SimpleLogger().Write(logWARNING)
-                << "Reached fallback for right turns, size 3: " << std::setprecision(12)
-                << toFloating(coord.lat) << " " << toFloating(coord.lon)
-                << " Valids: " << (intersection[1].entry_allowed + intersection[2].entry_allowed +
-                                   intersection[3].entry_allowed);
-            for (const auto road : intersection)
-            {
-                const auto &out_data = node_based_graph.GetEdgeData(road.turn.eid);
-                util::SimpleLogger().Write(logWARNING)
-                    << "\troad: " << road.toString() << " Name: " << out_data.name_id
-                    << " Road Class: " << (int)out_data.road_classification.road_class
-                    << " At: " << localizer(node_based_graph.GetTarget(road.turn.eid));
-            }
-
-            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)};
-        }
-    }
-    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)};
-        }
-
-        /*
-        auto coord = localizer(node_based_graph.GetTarget(via_edge));
-        util::SimpleLogger().Write(logWARNING)
-            << "Reached fallback for right turns (" << up_to << ") " << std::setprecision(12)
-            << toFloating(coord.lat) << " " << toFloating(coord.lon);
-        for (const auto road : intersection)
-        {
-            const auto &out_data = node_based_graph.GetEdgeData(road.turn.eid);
-            util::SimpleLogger().Write(logWARNING)
-                << "\troad: " << road.toString() << " Name: " << out_data.name_id
-                << " Road Class: " << (int)out_data.road_classification.road_class
-                << " At: " << localizer(node_based_graph.GetTarget(road.turn.eid));
-        }
-        */
+        road.turn.instruction = {TurnType::Turn, (from_nid == to_nid)
+                                                     ? DirectionModifier::UTurn
+                                                     : getTurnDirection(road.turn.angle)};
     }
     return intersection;
 }
diff --git a/src/extractor/guidance/turn_handler.cpp b/src/extractor/guidance/turn_handler.cpp
new file mode 100644
index 0000000..57a29d6
--- /dev/null
+++ b/src/extractor/guidance/turn_handler.cpp
@@ -0,0 +1,1171 @@
+#include "extractor/guidance/constants.hpp"
+#include "extractor/guidance/toolkit.hpp"
+#include "extractor/guidance/turn_handler.hpp"
+
+#include "util/simple_logger.hpp"
+
+#include <limits>
+#include <utility>
+
+#include <boost/assert.hpp>
+
+using EdgeData = osrm::util::NodeBasedDynamicGraph::EdgeData;
+
+namespace osrm
+{
+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)
+{
+}
+
+TurnHandler::~TurnHandler() {}
+
+bool TurnHandler::canProcess(const NodeID, const EdgeID, const Intersection &) const
+{
+    return true;
+}
+
+Intersection TurnHandler::
+operator()(const NodeID, const EdgeID via_eid, Intersection intersection) const
+{
+    if (intersection.size() == 1)
+        return handleOneWayTurn(std::move(intersection));
+
+    if (intersection[0].entry_allowed)
+    {
+        intersection[0].turn.instruction = {findBasicTurnType(via_eid, intersection[0]),
+                                            DirectionModifier::UTurn};
+    }
+
+    if (intersection.size() == 2)
+        return handleTwoWayTurn(via_eid, std::move(intersection));
+
+    if (intersection.size() == 3)
+        return handleThreeWayTurn(via_eid, std::move(intersection));
+
+    return handleComplexTurn(via_eid, std::move(intersection));
+}
+
+Intersection TurnHandler::handleOneWayTurn(Intersection intersection) const
+{
+    BOOST_ASSERT(intersection[0].turn.angle < 0.001);
+    return intersection;
+}
+
+Intersection TurnHandler::handleTwoWayTurn(const EdgeID via_edge, Intersection intersection) const
+{
+    BOOST_ASSERT(intersection[0].turn.angle < 0.001);
+    intersection[1].turn.instruction =
+        getInstructionForObvious(intersection.size(), via_edge, false, intersection[1]);
+
+    if (intersection[1].turn.instruction.type == TurnType::Suppressed)
+        intersection[1].turn.instruction.type = TurnType::NoTurn;
+
+    return intersection;
+}
+
+Intersection TurnHandler::handleThreeWayTurn(const EdgeID via_edge, Intersection intersection) const
+{
+    BOOST_ASSERT(intersection[0].turn.angle < 0.001);
+    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_obvious_by_road_class =
+            (!is_ramp && (2 * getPriority(first_class) < getPriority(second_class))) ||
+            (!isLowPriorityRoadClass(first_class) && isLowPriorityRoadClass(second_class));
+        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;
+    };
+
+    /* Two nearly straight turns -> FORK
+               OOOOOOO
+             /
+      IIIIII
+             \
+               OOOOOOO
+     */
+    if (angularDeviation(intersection[1].turn.angle, STRAIGHT_ANGLE) < NARROW_TURN_ANGLE &&
+        angularDeviation(intersection[2].turn.angle, STRAIGHT_ANGLE) < NARROW_TURN_ANGLE)
+    {
+        if (intersection[1].entry_allowed && intersection[2].entry_allowed)
+        {
+            const auto left_class = node_based_graph.GetEdgeData(intersection[2].turn.eid)
+                                        .road_classification.road_class;
+            const auto right_class = node_based_graph.GetEdgeData(intersection[1].turn.eid)
+                                         .road_classification.road_class;
+            if (canBeSeenAsFork(left_class, right_class))
+            {
+                assignFork(via_edge, intersection[2], intersection[1]);
+            }
+            else if (isObviousOfTwo(intersection[1], intersection[2]))
+            {
+                intersection[1].turn.instruction =
+                    getInstructionForObvious(intersection.size(), via_edge, false, intersection[1]);
+                intersection[2].turn.instruction = {findBasicTurnType(via_edge, intersection[2]),
+                                                    DirectionModifier::SlightLeft};
+            }
+            else if (isObviousOfTwo(intersection[2], intersection[1]))
+            {
+                intersection[2].turn.instruction =
+                    getInstructionForObvious(intersection.size(), via_edge, false, intersection[2]);
+                intersection[1].turn.instruction = {findBasicTurnType(via_edge, intersection[1]),
+                                                    DirectionModifier::SlightRight};
+            }
+            else
+            {
+                intersection[1].turn.instruction = {findBasicTurnType(via_edge, intersection[1]),
+                                                    DirectionModifier::SlightRight};
+                intersection[2].turn.instruction = {findBasicTurnType(via_edge, intersection[2]),
+                                                    DirectionModifier::SlightLeft};
+            }
+        }
+        else
+        {
+            if (intersection[1].entry_allowed)
+                intersection[1].turn.instruction =
+                    getInstructionForObvious(intersection.size(), via_edge, false, intersection[1]);
+            if (intersection[2].entry_allowed)
+                intersection[2].turn.instruction =
+                    getInstructionForObvious(intersection.size(), via_edge, false, intersection[2]);
+        }
+    }
+    /*  T Intersection
+
+        OOOOOOO T OOOOOOOO
+                I
+                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)
+    {
+        if (intersection[1].entry_allowed)
+        {
+            if (TurnType::Ramp != findBasicTurnType(via_edge, intersection[1]))
+                intersection[1].turn.instruction = {TurnType::EndOfRoad, DirectionModifier::Right};
+            else
+                intersection[1].turn.instruction = {TurnType::Ramp, DirectionModifier::Right};
+        }
+        if (intersection[2].entry_allowed)
+        {
+            if (TurnType::Ramp != 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);
+        }
+        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]);
+        }
+        else
+        {
+            intersection[1].turn.instruction = {findBasicTurnType(via_edge, intersection[1]),
+                                                getTurnDirection(intersection[1].turn.angle)};
+        }
+
+        if (isObviousOfTwo(intersection[2], intersection[1]))
+        {
+            intersection[2].turn.instruction =
+                getInstructionForObvious(3, via_edge, false, intersection[2]);
+        }
+        else
+        {
+            intersection[2].turn.instruction = {findBasicTurnType(via_edge, intersection[2]),
+                                                getTurnDirection(intersection[2].turn.angle)};
+        }
+    }
+    return 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;
+    double straightmost_deviation = 180;
+    for (std::size_t i = 0; i < intersection.size(); ++i)
+    {
+        const double deviation = angularDeviation(intersection[i].turn.angle, STRAIGHT_ANGLE);
+        if (deviation < straightmost_deviation)
+        {
+            straightmost_deviation = deviation;
+            straightmost_turn = i;
+        }
+    }
+
+    // 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]);
+
+        // assign left/right turns
+        intersection = assignLeftTurns(via_edge, std::move(intersection), obvious_index + 1);
+        intersection = assignRightTurns(via_edge, std::move(intersection), obvious_index);
+    }
+    else if (fork_range.first != 0 && fork_range.second - fork_range.first <= 2) // found fork
+    {
+        if (fork_range.second - fork_range.first == 1)
+        {
+            auto &left = intersection[fork_range.second];
+            auto &right = intersection[fork_range.first];
+            const auto left_class =
+                node_based_graph.GetEdgeData(left.turn.eid).road_classification.road_class;
+            const auto right_class =
+                node_based_graph.GetEdgeData(right.turn.eid).road_classification.road_class;
+            if (canBeSeenAsFork(left_class, right_class))
+                assignFork(via_edge, left, right);
+            else if (getPriority(left_class) > getPriority(right_class))
+            {
+                right.turn.instruction =
+                    getInstructionForObvious(intersection.size(), via_edge, false, right);
+                left.turn.instruction = {findBasicTurnType(via_edge, left),
+                                         DirectionModifier::SlightLeft};
+            }
+            else
+            {
+                left.turn.instruction =
+                    getInstructionForObvious(intersection.size(), via_edge, false, left);
+                right.turn.instruction = {findBasicTurnType(via_edge, right),
+                                          DirectionModifier::SlightRight};
+            }
+        }
+        else if (fork_range.second - fork_range.second == 2)
+        {
+            assignFork(via_edge, intersection[fork_range.second],
+                       intersection[fork_range.first + 1], intersection[fork_range.first]);
+        }
+        // assign left/right turns
+        intersection = assignLeftTurns(via_edge, std::move(intersection), fork_range.second + 1);
+        intersection = assignRightTurns(via_edge, std::move(intersection), fork_range.first);
+    }
+    else if (straightmost_deviation < FUZZY_ANGLE_DIFFERENCE &&
+             !intersection[straightmost_turn].entry_allowed)
+    {
+        // invalid straight turn
+        intersection = assignLeftTurns(via_edge, std::move(intersection), straightmost_turn + 1);
+        intersection = assignRightTurns(via_edge, std::move(intersection), straightmost_turn);
+    }
+    // no straight turn
+    else if (intersection[straightmost_turn].turn.angle > 180)
+    {
+        // at most three turns on either side
+        intersection = assignLeftTurns(via_edge, std::move(intersection), straightmost_turn);
+        intersection = assignRightTurns(via_edge, std::move(intersection), straightmost_turn);
+    }
+    else if (intersection[straightmost_turn].turn.angle < 180)
+    {
+        intersection = assignLeftTurns(via_edge, std::move(intersection), straightmost_turn + 1);
+        intersection = assignRightTurns(via_edge, std::move(intersection), straightmost_turn + 1);
+    }
+    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;
+            }
+        }
+    }
+    return intersection;
+}
+
+std::size_t TurnHandler::findObviousTurn(const EdgeID via_edge,
+                                         const Intersection &intersection) const
+{
+    // no obvious road
+    if (intersection.size() == 1)
+        return 0;
+
+    // a single non u-turn is obvious
+    if (intersection.size() == 2)
+        return 1;
+
+    // at least three roads
+    std::size_t best = 0;
+    double best_deviation = 180;
+
+    std::size_t best_continue = 0;
+    double best_continue_deviation = 180;
+
+    const EdgeData &in_data = node_based_graph.GetEdgeData(via_edge);
+    for (std::size_t i = 1; i < intersection.size(); ++i)
+    {
+        const double deviation = angularDeviation(intersection[i].turn.angle, STRAIGHT_ANGLE);
+        if (intersection[i].entry_allowed && deviation < best_deviation)
+        {
+            best_deviation = deviation;
+            best = i;
+        }
+
+        const auto out_data = node_based_graph.GetEdgeData(intersection[i].turn.eid);
+        if (intersection[i].entry_allowed && out_data.name_id == in_data.name_id &&
+            deviation < best_continue_deviation)
+        {
+            best_continue_deviation = deviation;
+            best_continue = i;
+        }
+    }
+
+    if (best == 0)
+        return 0;
+
+    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)
+    {
+        // Find left/right deviation
+        const double left_deviation = angularDeviation(
+            intersection[(best + 1) % intersection.size()].turn.angle, STRAIGHT_ANGLE);
+        const double right_deviation =
+            angularDeviation(intersection[best - 1].turn.angle, STRAIGHT_ANGLE);
+
+        if (best_deviation < MAXIMAL_ALLOWED_NO_TURN_DEVIATION &&
+            std::min(left_deviation, right_deviation) > FUZZY_ANGLE_DIFFERENCE)
+            return best;
+
+        // other narrow turns?
+        if (angularDeviation(intersection[best - 1].turn.angle, STRAIGHT_ANGLE) <=
+            FUZZY_ANGLE_DIFFERENCE)
+            return 0;
+        if (angularDeviation(intersection[(best + 1) % intersection.size()].turn.angle,
+                             STRAIGHT_ANGLE) <= FUZZY_ANGLE_DIFFERENCE)
+            return 0;
+
+        // Well distinct turn that is nearly straight
+        if (left_deviation / best_deviation >= DISTINCTION_RATIO &&
+            right_deviation / best_deviation >= DISTINCTION_RATIO)
+        {
+            return best;
+        }
+    }
+
+    return 0; // no obvious turn
+}
+
+// Can only assign three turns
+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)
+    {
+        if (!intersection[starting_at].entry_allowed)
+            return intersection;
+
+        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))
+        {
+
+            // 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;
+}
+
+// can only assign three turns
+Intersection TurnHandler::assignRightTurns(const EdgeID via_edge,
+                                           Intersection intersection,
+                                           const std::size_t up_to) const
+{
+    BOOST_ASSERT(up_to <= intersection.size());
+    const auto count_valid = [&intersection, up_to]() {
+        std::size_t count = 0;
+        for (std::size_t i = 1; i < up_to; ++i)
+            if (intersection[i].entry_allowed)
+                ++count;
+        return count;
+    };
+    if (up_to <= 1 || count_valid() == 0)
+        return intersection;
+    // 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};
+        }
+    }
+    else if (up_to == 3)
+    {
+        const auto first_direction = getTurnDirection(intersection[1].turn.angle);
+        const auto second_direction = getTurnDirection(intersection[2].turn.angle);
+        if (first_direction == second_direction)
+        {
+            // conflict
+            handleDistinctConflict(via_edge, intersection[2], intersection[1]);
+        }
+        else
+        {
+            intersection[1].turn.instruction = {findBasicTurnType(via_edge, intersection[1]),
+                                                first_direction};
+            intersection[2].turn.instruction = {findBasicTurnType(via_edge, intersection[2]),
+                                                second_direction};
+        }
+    }
+    else if (up_to == 4)
+    {
+        const auto first_direction = getTurnDirection(intersection[1].turn.angle);
+        const auto second_direction = getTurnDirection(intersection[2].turn.angle);
+        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};
+        }
+        else if (2 >= (intersection[1].entry_allowed + intersection[2].entry_allowed +
+                       intersection[3].entry_allowed))
+        {
+            // at least a single invalid
+            if (!intersection[3].entry_allowed)
+            {
+                handleDistinctConflict(via_edge, intersection[2], intersection[1]);
+            }
+            else if (!intersection[1].entry_allowed)
+            {
+                handleDistinctConflict(via_edge, intersection[3], intersection[2]);
+            }
+            else // handles one-valid as well as two valid (1,3)
+            {
+                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) >=
+                     NARROW_TURN_ANGLE &&
+                 angularDeviation(intersection[2].turn.angle, intersection[3].turn.angle) >=
+                     NARROW_TURN_ANGLE)
+        {
+            intersection[1].turn.instruction = {findBasicTurnType(via_edge, intersection[1]),
+                                                DirectionModifier::SharpRight};
+            intersection[2].turn.instruction = {findBasicTurnType(via_edge, intersection[2]),
+                                                DirectionModifier::Right};
+            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) ||
+                  (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)))
+        {
+            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};
+        }
+        else if (intersection[1].entry_allowed && intersection[2].entry_allowed &&
+                 intersection[3].entry_allowed &&
+                 ((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)))
+        {
+            if (angularDeviation(intersection[2].turn.angle, intersection[3].turn.angle) >=
+                GROUP_ANGLE)
+            {
+                handleDistinctConflict(via_edge, intersection[2], intersection[1]);
+                intersection[3].turn.instruction = {findBasicTurnType(via_edge, intersection[3]),
+                                                    third_direction};
+            }
+            else
+            {
+                intersection[1].turn.instruction = {findBasicTurnType(via_edge, intersection[1]),
+                                                    first_direction};
+                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)
+                << "Reached fallback for right turns, size 3 "
+                << " Valids: " << (intersection[1].entry_allowed + intersection[2].entry_allowed +
+                                   intersection[3].entry_allowed);
+            for (const auto road : intersection)
+            {
+                const auto &out_data = node_based_graph.GetEdgeData(road.turn.eid);
+                util::SimpleLogger().Write(logWARNING)
+                    << "\troad: " << toString(road) << " Name: " << out_data.name_id
+                    << " Road Class: " << (int)out_data.road_classification.road_class;
+            }
+
+            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)};
+        }
+    }
+    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)};
+        }
+    }
+    return intersection;
+}
+
+std::pair<std::size_t, std::size_t> TurnHandler::findFork(const Intersection &intersection) const
+{
+
+    std::size_t best = 0;
+    double best_deviation = 180;
+
+    // TODO handle road classes
+    for (std::size_t i = 1; i < intersection.size(); ++i)
+    {
+        const double deviation = angularDeviation(intersection[i].turn.angle, STRAIGHT_ANGLE);
+        if (intersection[i].entry_allowed && deviation < best_deviation)
+        {
+            best_deviation = deviation;
+            best = i;
+        }
+    }
+    if (best_deviation <= NARROW_TURN_ANGLE)
+    {
+        std::size_t left = best, right = best;
+        if (intersection[left].turn.angle >= 180)
+        {
+            // due to best > 1, we can safely decrement right
+            --right;
+            if (angularDeviation(intersection[right].turn.angle, STRAIGHT_ANGLE) >
+                NARROW_TURN_ANGLE)
+                return std::make_pair(std::size_t{0}, std::size_t{0});
+        }
+        else
+        {
+            ++left;
+            if (left >= intersection.size() ||
+                angularDeviation(intersection[left].turn.angle, STRAIGHT_ANGLE) > NARROW_TURN_ANGLE)
+                return std::make_pair(std::size_t{0}, std::size_t{0});
+        }
+        while (left + 1 < intersection.size() &&
+               angularDeviation(intersection[left].turn.angle, intersection[left + 1].turn.angle) <
+                   NARROW_TURN_ANGLE)
+            ++left;
+        while (right > 1 &&
+               angularDeviation(intersection[right].turn.angle,
+                                intersection[right - 1].turn.angle) < NARROW_TURN_ANGLE)
+            --right;
+
+        // TODO check whether 2*NARROW_TURN is too large
+        if (right < left &&
+            angularDeviation(intersection[left].turn.angle,
+                             intersection[(left + 1) % intersection.size()].turn.angle) >=
+                2 * NARROW_TURN_ANGLE &&
+            angularDeviation(intersection[right].turn.angle, intersection[right - 1].turn.angle) >=
+                2 * NARROW_TURN_ANGLE)
+            return std::make_pair(right, left);
+    }
+    return std::make_pair(std::size_t{0}, std::size_t{0});
+}
+
+void TurnHandler::handleDistinctConflict(const EdgeID via_edge,
+                                         ConnectedRoad &left,
+                                         ConnectedRoad &right) const
+{
+    // single turn of both is valid (don't change the valid one)
+    // or multiple identical angles -> bad OSM intersection
+    if ((!left.entry_allowed || !right.entry_allowed) || (left.turn.angle == right.turn.angle))
+    {
+        if (left.entry_allowed)
+            left.turn.instruction = {findBasicTurnType(via_edge, left),
+                                     getTurnDirection(left.turn.angle)};
+        if (right.entry_allowed)
+            right.turn.instruction = {findBasicTurnType(via_edge, right),
+                                      getTurnDirection(right.turn.angle)};
+        return;
+    }
+
+    if (getTurnDirection(left.turn.angle) == DirectionModifier::Straight ||
+        getTurnDirection(left.turn.angle) == DirectionModifier::SlightLeft ||
+        getTurnDirection(right.turn.angle) == DirectionModifier::SlightRight)
+    {
+        const auto left_class =
+            node_based_graph.GetEdgeData(left.turn.eid).road_classification.road_class;
+        const auto right_class =
+            node_based_graph.GetEdgeData(right.turn.eid).road_classification.road_class;
+        if (canBeSeenAsFork(left_class, right_class))
+            assignFork(via_edge, left, right);
+        else if (getPriority(left_class) > getPriority(right_class))
+        {
+            // FIXME this should possibly know about the actual roads?
+            // here we don't know about the intersection size. To be on the save side,
+            // we declare it
+            // as complex (at least size 4)
+            right.turn.instruction = getInstructionForObvious(4, via_edge, false, right);
+            left.turn.instruction = {findBasicTurnType(via_edge, left),
+                                     DirectionModifier::SlightLeft};
+        }
+        else
+        {
+            // FIXME this should possibly know about the actual roads?
+            // here we don't know about the intersection size. To be on the save side,
+            // we declare it
+            // as complex (at least size 4)
+            left.turn.instruction = getInstructionForObvious(4, via_edge, false, left);
+            right.turn.instruction = {findBasicTurnType(via_edge, right),
+                                      DirectionModifier::SlightRight};
+        }
+    }
+    const auto left_type = findBasicTurnType(via_edge, left);
+    const auto right_type = findBasicTurnType(via_edge, right);
+    // Two Right Turns
+    if (angularDeviation(left.turn.angle, 90) < MAXIMAL_ALLOWED_NO_TURN_DEVIATION)
+    {
+        // Keep left perfect, shift right
+        left.turn.instruction = {left_type, DirectionModifier::Right};
+        right.turn.instruction = {right_type, DirectionModifier::SharpRight};
+        return;
+    }
+    if (angularDeviation(right.turn.angle, 90) < MAXIMAL_ALLOWED_NO_TURN_DEVIATION)
+    {
+        // Keep Right perfect, shift left
+        left.turn.instruction = {left_type, DirectionModifier::SlightRight};
+        right.turn.instruction = {right_type, DirectionModifier::Right};
+        return;
+    }
+    // Two Right Turns
+    if (angularDeviation(left.turn.angle, 270) < MAXIMAL_ALLOWED_NO_TURN_DEVIATION)
+    {
+        // Keep left perfect, shift right
+        left.turn.instruction = {left_type, DirectionModifier::Left};
+        right.turn.instruction = {right_type, DirectionModifier::SlightLeft};
+        return;
+    }
+    if (angularDeviation(right.turn.angle, 270) < MAXIMAL_ALLOWED_NO_TURN_DEVIATION)
+    {
+        // Keep Right perfect, shift left
+        left.turn.instruction = {left_type, DirectionModifier::SharpLeft};
+        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)
+    {
+        left.turn.instruction = {left_type, DirectionModifier::SharpLeft};
+        right.turn.instruction = {right_type, DirectionModifier::Left};
+        return;
+    }
+    if (getTurnDirection(right.turn.angle) == DirectionModifier::SharpRight)
+    {
+        left.turn.instruction = {left_type, DirectionModifier::Right};
+        right.turn.instruction = {right_type, DirectionModifier::SharpRight};
+        return;
+    }
+
+    if (getTurnDirection(left.turn.angle) == DirectionModifier::Right)
+    {
+        if (angularDeviation(left.turn.angle, 90) > angularDeviation(right.turn.angle, 90))
+        {
+            left.turn.instruction = {left_type, DirectionModifier::SlightRight};
+            right.turn.instruction = {right_type, DirectionModifier::Right};
+        }
+        else
+        {
+            left.turn.instruction = {left_type, DirectionModifier::Right};
+            right.turn.instruction = {right_type, DirectionModifier::SharpRight};
+        }
+    }
+    else
+    {
+        if (angularDeviation(left.turn.angle, 270) > angularDeviation(right.turn.angle, 270))
+        {
+            left.turn.instruction = {left_type, DirectionModifier::SharpLeft};
+            right.turn.instruction = {right_type, DirectionModifier::Left};
+        }
+        else
+        {
+            left.turn.instruction = {left_type, DirectionModifier::Left};
+            right.turn.instruction = {right_type, DirectionModifier::SlightLeft};
+        }
+    }
+}
+
+} // namespace guidance
+} // namespace extractor
+} // namespace osrm
diff --git a/src/extractor/restriction_parser.cpp b/src/extractor/restriction_parser.cpp
index b56af20..66cd259 100644
--- a/src/extractor/restriction_parser.cpp
+++ b/src/extractor/restriction_parser.cpp
@@ -188,7 +188,7 @@ bool RestrictionParser::ShouldIgnoreRestriction(const std::string &except_tag_st
     // should this restriction be ignored? yes if there's an overlap between:
     // a) the list of modes in the except tag of the restriction
     //    (except_tag_string), eg: except=bus;bicycle
-    // b) the lua profile defines a hierachy of modes,
+    // b) the lua profile defines a hierarchy of modes,
     //    eg: [access, vehicle, bicycle]
 
     if (except_tag_string.empty())
diff --git a/src/extractor/scripting_environment.cpp b/src/extractor/scripting_environment.cpp
index 53cc8e1..e3f9b6e 100644
--- a/src/extractor/scripting_environment.cpp
+++ b/src/extractor/scripting_environment.cpp
@@ -101,7 +101,7 @@ void ScriptingEnvironment::InitContext(ScriptingEnvironment::Context &context)
              .property("u_turn_penalty", &ProfileProperties::GetUturnPenalty,
                        &ProfileProperties::SetUturnPenalty)
              .def_readwrite("use_turn_restrictions", &ProfileProperties::use_turn_restrictions)
-             .def_readwrite("allow_u_turn_at_via", &ProfileProperties::allow_u_turn_at_via),
+             .def_readwrite("continue_straight_at_waypoint", &ProfileProperties::continue_straight_at_waypoint),
 
          luabind::class_<std::vector<std::string>>("vector")
              .def("Add", static_cast<void (std::vector<std::string>::*)(const std::string &)>(
diff --git a/src/server/api/parameters_parser.cpp b/src/server/api/parameters_parser.cpp
index 1cf3050..1d4485a 100644
--- a/src/server/api/parameters_parser.cpp
+++ b/src/server/api/parameters_parser.cpp
@@ -1,11 +1,13 @@
 #include "server/api/parameters_parser.hpp"
 
+#include "server/api/match_parameter_grammar.hpp"
+#include "server/api/nearest_parameter_grammar.hpp"
 #include "server/api/route_parameters_grammar.hpp"
 #include "server/api/table_parameter_grammar.hpp"
-#include "server/api/nearest_parameter_grammar.hpp"
-#include "server/api/trip_parameter_grammar.hpp"
-#include "server/api/match_parameter_grammar.hpp"
 #include "server/api/tile_parameter_grammar.hpp"
+#include "server/api/trip_parameter_grammar.hpp"
+
+#include <type_traits>
 
 namespace osrm
 {
@@ -25,36 +27,49 @@ 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, 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;
-    const auto result = boost::spirit::qi::parse(iter, end, grammar);
 
-    boost::optional<ParameterT> parameters;
-    if (result && iter == end)
-        parameters = std::move(grammar.parameters);
+    try
+    {
+        const auto ok = boost::spirit::qi::parse(iter, end, grammar);
+
+        // 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);
+    }
+    catch (const qi::expectation_failure<It> &failure)
+    {
+        // The grammar above using expectation parsers ">" does not automatically increment the
+        // iterator to the failing position. Extract the position from the exception ourselves.
+        iter = failure.first;
+    }
 
-    return parameters;
+    return boost::none;
 }
 } // ns detail
 
 template <>
 boost::optional<engine::api::RouteParameters> parseParameters(std::string::iterator &iter,
-                                                              std::string::iterator end)
+                                                              const std::string::iterator end)
 {
     return detail::parseParameters<engine::api::RouteParameters, RouteParametersGrammar>(iter, end);
 }
 
 template <>
 boost::optional<engine::api::TableParameters> parseParameters(std::string::iterator &iter,
-                                                              std::string::iterator end)
+                                                              const std::string::iterator end)
 {
     return detail::parseParameters<engine::api::TableParameters, TableParametersGrammar>(iter, end);
 }
 
 template <>
 boost::optional<engine::api::NearestParameters> parseParameters(std::string::iterator &iter,
-                                                                std::string::iterator end)
+                                                                const std::string::iterator end)
 {
     return detail::parseParameters<engine::api::NearestParameters, NearestParametersGrammar>(iter,
                                                                                              end);
@@ -62,21 +77,21 @@ boost::optional<engine::api::NearestParameters> parseParameters(std::string::ite
 
 template <>
 boost::optional<engine::api::TripParameters> parseParameters(std::string::iterator &iter,
-                                                             std::string::iterator end)
+                                                             const std::string::iterator end)
 {
     return detail::parseParameters<engine::api::TripParameters, TripParametersGrammar>(iter, end);
 }
 
 template <>
 boost::optional<engine::api::MatchParameters> parseParameters(std::string::iterator &iter,
-                                                              std::string::iterator end)
+                                                              const std::string::iterator end)
 {
     return detail::parseParameters<engine::api::MatchParameters, MatchParametersGrammar>(iter, end);
 }
 
 template <>
 boost::optional<engine::api::TileParameters> parseParameters(std::string::iterator &iter,
-                                                             std::string::iterator end)
+                                                             const std::string::iterator end)
 {
     return detail::parseParameters<engine::api::TileParameters, TileParametersGrammar>(iter, end);
 }
diff --git a/src/server/api/url_parser.cpp b/src/server/api/url_parser.cpp
index 661b1f6..7ef6db0 100644
--- a/src/server/api/url_parser.cpp
+++ b/src/server/api/url_parser.cpp
@@ -1,88 +1,86 @@
 #include "server/api/url_parser.hpp"
-
 #include "engine/polyline_compressor.hpp"
 
-#include <boost/bind.hpp>
-#include <boost/spirit/include/qi_char_.hpp>
-#include <boost/spirit/include/qi_grammar.hpp>
-#include <boost/spirit/include/qi_uint.hpp>
-#include <boost/spirit/include/qi_real.hpp>
-#include <boost/spirit/include/qi_lit.hpp>
-#include <boost/spirit/include/qi_action.hpp>
-#include <boost/spirit/include/qi_as_string.hpp>
-#include <boost/spirit/include/qi_operator.hpp>
-#include <boost/spirit/include/qi_plus.hpp>
+//#define BOOST_SPIRIT_DEBUG
+#include <boost/spirit/include/qi.hpp>
 
-namespace osrm
-{
-namespace server
-{
-namespace api
-{
+#include <string>
+#include <type_traits>
 
+// Keep impl. TU local
 namespace
 {
-
 namespace qi = boost::spirit::qi;
-using Iterator = std::string::iterator;
-struct URLGrammar : boost::spirit::qi::grammar<Iterator>
+
+template <typename Iterator, typename Into> //
+struct URLParser final : qi::grammar<Iterator, Into>
 {
-    URLGrammar() : URLGrammar::base_type(url_rule)
+    URLParser() : URLParser::base_type(start)
     {
-        const auto set_service = [this](std::string &service)
-        {
-            parsed_url.service = std::move(service);
-        };
-        const auto set_version = [this](const unsigned version)
-        {
-            parsed_url.version = version;
-        };
-        const auto set_profile = [this](std::string &profile)
-        {
-            parsed_url.profile = std::move(profile);
-        };
-        const auto set_query = [this](std::string &query)
-        {
-            parsed_url.query = std::move(query);
-        };
-
         alpha_numeral = qi::char_("a-zA-Z0-9");
         polyline_chars = qi::char_("a-zA-Z0-9_.--[]{}@?|\\%~`^");
         all_chars = polyline_chars | qi::char_("=,;:&().");
 
-        service_rule = +alpha_numeral;
-        version_rule = qi::uint_;
-        profile_rule = +alpha_numeral;
-        query_rule = +all_chars;
+        service = +alpha_numeral;
+        version = qi::uint_;
+        profile = +alpha_numeral;
+        query = +all_chars;
+
+        // 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;                 //
 
-        url_rule = qi::lit('/') >> service_rule[set_service] >> qi::lit('/') >> qi::lit('v') >>
-                   version_rule[set_version] >> qi::lit('/') >> profile_rule[set_profile] >>
-                   qi::lit('/') >> query_rule[set_query];
+        BOOST_SPIRIT_DEBUG_NODES((start)(service)(version)(profile)(query))
     }
 
-    ParsedURL parsed_url;
+    qi::rule<Iterator, Into> start;
 
-    qi::rule<Iterator> url_rule;
-    qi::rule<Iterator, std::string()> service_rule, profile_rule, query_rule;
-    qi::rule<Iterator, unsigned()> version_rule;
-    qi::rule<Iterator, char()> alpha_numeral, all_chars, polyline_chars;
+    qi::rule<Iterator, std::string()> service;
+    qi::rule<Iterator, unsigned()> version;
+    qi::rule<Iterator, std::string()> profile;
+    qi::rule<Iterator, std::string()> query;
+
+    qi::rule<Iterator, char()> alpha_numeral;
+    qi::rule<Iterator, char()> all_chars;
+    qi::rule<Iterator, char()> polyline_chars;
 };
-}
 
-boost::optional<ParsedURL> parseURL(std::string::iterator &iter, std::string::iterator end)
+} // anon.
+
+namespace osrm
+{
+namespace server
+{
+namespace api
+{
+
+boost::optional<ParsedURL> parseURL(std::string::iterator &iter, const std::string::iterator end)
 {
-    boost::optional<ParsedURL> parsed_url;
+    using It = std::decay<decltype(iter)>::type;
 
-    URLGrammar grammar;
-    const auto result = boost::spirit::qi::parse(iter, end, grammar);
+    static URLParser<It, ParsedURL()> const parser;
+    ParsedURL out;
 
-    if (result && iter == end)
+    try
     {
-        parsed_url = std::move(grammar.parsed_url);
+        const auto ok = boost::spirit::qi::parse(iter, end, parser, out);
+
+        if (ok && iter == end)
+            return boost::make_optional(out);
+    }
+    catch (const qi::expectation_failure<It> &failure)
+    {
+        // The grammar above using expectation parsers ">" does not automatically increment the
+        // iterator to the failing position. Extract the position from the exception ourselves.
+        iter = failure.first;
     }
 
-    return parsed_url;
-}
-}
-}
+    return boost::none;
 }
+
+} // api
+} // server
+} // osrm
diff --git a/src/server/connection.cpp b/src/server/connection.cpp
index e7725fb..0fa31e6 100644
--- a/src/server/connection.cpp
+++ b/src/server/connection.cpp
@@ -7,6 +7,7 @@
 #include <boost/iostreams/filtering_stream.hpp>
 #include <boost/iostreams/filter/gzip.hpp>
 
+#include <iterator>
 #include <string>
 #include <vector>
 
diff --git a/src/server/request_handler.cpp b/src/server/request_handler.cpp
index 888a92b..cfe74f2 100644
--- a/src/server/request_handler.cpp
+++ b/src/server/request_handler.cpp
@@ -21,6 +21,7 @@
 #include <ctime>
 
 #include <algorithm>
+#include <iterator>
 #include <iostream>
 #include <string>
 
@@ -77,7 +78,6 @@ void RequestHandler::HandleRequest(const http::request &current_request, http::r
 
         auto api_iterator = request_string.begin();
         auto maybe_parsed_url = api::parseURL(api_iterator, request_string.end());
-        ;
         ServiceHandler::ResultT result;
 
         // check if the was an error with the request
@@ -85,7 +85,7 @@ void RequestHandler::HandleRequest(const http::request &current_request, http::r
         {
 
             const engine::Status status =
-                service_handler->RunQuery(std::move(*maybe_parsed_url), result);
+                service_handler->RunQuery(*std::move(maybe_parsed_url), result);
             if (status != engine::Status::Ok)
             {
                 // 4xx bad request return code
@@ -103,7 +103,7 @@ void RequestHandler::HandleRequest(const http::request &current_request, http::r
             const auto context_begin = request_string.begin() + ((position < 3) ? 0 : (position - 3UL));
             BOOST_ASSERT(context_begin >= request_string.begin());
             const auto context_end =
-                request_string.begin() + std::min(position + 3UL, request_string.size());
+                request_string.begin() + std::min<std::size_t>(position + 3UL, request_string.size());
             BOOST_ASSERT(context_end <= request_string.end());
             std::string context(context_begin, context_end);
 
diff --git a/src/storage/storage.cpp b/src/storage/storage.cpp
index 862ca3c..67fd122 100644
--- a/src/storage/storage.cpp
+++ b/src/storage/storage.cpp
@@ -30,6 +30,7 @@
 #include <cstdint>
 
 #include <fstream>
+#include <iterator>
 #include <new>
 #include <string>
 
diff --git a/src/tools/contract.cpp b/src/tools/contract.cpp
index 5bc65c5..c9460db 100644
--- a/src/tools/contract.cpp
+++ b/src/tools/contract.cpp
@@ -61,8 +61,9 @@ return_code parseArguments(int argc, char *argv[], contractor::ContractorConfig
     boost::program_options::options_description cmdline_options;
     cmdline_options.add(generic_options).add(config_options).add(hidden_options);
 
+    const auto* executable = argv[0];
     boost::program_options::options_description visible_options(
-        "Usage: " + boost::filesystem::basename(argv[0]) + " <input.osrm> [options]");
+        "Usage: " + boost::filesystem::path(executable).filename().string() + " <input.osrm> [options]");
     visible_options.add(generic_options).add(config_options);
 
     // parse command line options
diff --git a/src/tools/extract.cpp b/src/tools/extract.cpp
index 4c5fa4c..672ada2 100644
--- a/src/tools/extract.cpp
+++ b/src/tools/extract.cpp
@@ -64,8 +64,9 @@ return_code parseArguments(int argc, char *argv[], extractor::ExtractorConfig &e
     boost::program_options::options_description cmdline_options;
     cmdline_options.add(generic_options).add(config_options).add(hidden_options);
 
+    const auto* executable = argv[0];
     boost::program_options::options_description visible_options(
-        boost::filesystem::basename(argv[0]) + " <input.osm/.osm.bz2/.osm.pbf> [options]");
+        boost::filesystem::path(executable).filename().string() + " <input.osm/.osm.bz2/.osm.pbf> [options]");
     visible_options.add(generic_options).add(config_options);
 
     // parse command line options
diff --git a/src/tools/routed.cpp b/src/tools/routed.cpp
index 6c49d4e..831a9cd 100644
--- a/src/tools/routed.cpp
+++ b/src/tools/routed.cpp
@@ -109,8 +109,9 @@ generateServerProgramOptions(const int argc,
     boost::program_options::options_description cmdline_options;
     cmdline_options.add(generic_options).add(config_options).add(hidden_options);
 
+    const auto* executable = argv[0];
     boost::program_options::options_description visible_options(
-        boost::filesystem::path(argv[0]).stem().string() + " <base.osrm> [<options>]");
+        boost::filesystem::path(executable).filename().string() + " <base.osrm> [<options>]");
     visible_options.add(generic_options).add(config_options);
 
     // parse command line options
diff --git a/src/tools/store.cpp b/src/tools/store.cpp
index 7866cf3..9e7cbbc 100644
--- a/src/tools/store.cpp
+++ b/src/tools/store.cpp
@@ -35,8 +35,9 @@ bool generateDataStoreOptions(const int argc, const char *argv[], boost::filesys
     boost::program_options::options_description cmdline_options;
     cmdline_options.add(generic_options).add(config_options).add(hidden_options);
 
+    const auto* executable = argv[0];
     boost::program_options::options_description visible_options(
-        boost::filesystem::basename(argv[0]) + " [<options>] <configuration>");
+        boost::filesystem::path(executable).filename().string() + " [<options>] <configuration>");
     visible_options.add(generic_options).add(config_options);
 
     // print help options if no infile is specified
diff --git a/src/util/coordinate_calculation.cpp b/src/util/coordinate_calculation.cpp
index 8ed69c9..24b280f 100644
--- a/src/util/coordinate_calculation.cpp
+++ b/src/util/coordinate_calculation.cpp
@@ -1,6 +1,7 @@
 #include "util/coordinate.hpp"
 #include "util/coordinate_calculation.hpp"
 #include "util/trigonometry_table.hpp"
+#include "util/web_mercator.hpp"
 
 #include <boost/assert.hpp>
 
@@ -18,10 +19,10 @@ namespace coordinate_calculation
 {
 
 // Does not project the coordinates!
-double squaredEuclideanDistance(const FloatCoordinate &lhs, const FloatCoordinate &rhs)
+std::uint64_t squaredEuclideanDistance(const Coordinate &lhs, const Coordinate &rhs)
 {
-    const double dx = static_cast<double>(lhs.lon - rhs.lon);
-    const double dy = static_cast<double>(lhs.lat - rhs.lat);
+    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);
 
     return dx * dx + dy * dy;
 }
@@ -40,11 +41,11 @@ 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 * DEGREE_TO_RAD;
+    const double dlat1 = lt1 * detail::DEGREE_TO_RAD;
 
-    const double dlong1 = ln1 * DEGREE_TO_RAD;
-    const double dlat2 = lt2 * DEGREE_TO_RAD;
-    const double dlong2 = ln2 * 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;
 
     const double dlong = dlong1 - dlong2;
     const double dlat = dlat1 - dlat2;
@@ -52,7 +53,7 @@ double haversineDistance(const Coordinate coordinate_1, const Coordinate coordin
     const double aharv = std::pow(std::sin(dlat / 2.0), 2.0) +
                          std::cos(dlat1) * std::cos(dlat2) * std::pow(std::sin(dlong / 2.), 2);
     const double charv = 2. * std::atan2(std::sqrt(aharv), std::sqrt(1.0 - aharv));
-    return EARTH_RADIUS * charv;
+    return detail::EARTH_RADIUS * charv;
 }
 
 double greatCircleDistance(const Coordinate coordinate_1, const Coordinate coordinate_2)
@@ -66,14 +67,14 @@ double greatCircleDistance(const Coordinate coordinate_1, const Coordinate coord
     BOOST_ASSERT(lat2 != std::numeric_limits<int>::min());
     BOOST_ASSERT(lon2 != std::numeric_limits<int>::min());
 
-    const double float_lat1 = (lat1 / COORDINATE_PRECISION) * DEGREE_TO_RAD;
-    const double float_lon1 = (lon1 / COORDINATE_PRECISION) * DEGREE_TO_RAD;
-    const double float_lat2 = (lat2 / COORDINATE_PRECISION) * DEGREE_TO_RAD;
-    const double float_lon2 = (lon2 / COORDINATE_PRECISION) * DEGREE_TO_RAD;
+    const double float_lat1 = (lat1 / COORDINATE_PRECISION) * detail::DEGREE_TO_RAD;
+    const double float_lon1 = (lon1 / COORDINATE_PRECISION) * detail::DEGREE_TO_RAD;
+    const double float_lat2 = (lat2 / COORDINATE_PRECISION) * detail::DEGREE_TO_RAD;
+    const double float_lon2 = (lon2 / COORDINATE_PRECISION) * detail::DEGREE_TO_RAD;
 
     const double x_value = (float_lon2 - float_lon1) * std::cos((float_lat1 + float_lat2) / 2.0);
     const double y_value = float_lat2 - float_lat1;
-    return std::hypot(x_value, y_value) * EARTH_RADIUS;
+    return std::hypot(x_value, y_value) * detail::EARTH_RADIUS;
 }
 
 std::pair<double, FloatCoordinate> projectPointOnSegment(const FloatCoordinate &source,
@@ -104,10 +105,13 @@ std::pair<double, FloatCoordinate> projectPointOnSegment(const FloatCoordinate &
     {
         clamped_ratio = 0.;
     }
+
     return {clamped_ratio,
             {
-                source.lon + slope_vector.lon * FloatLongitude(clamped_ratio),
-                source.lat + slope_vector.lat * FloatLatitude(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),
             }};
 }
 
@@ -122,9 +126,10 @@ double perpendicularDistance(const Coordinate segment_source,
     BOOST_ASSERT(query_location.IsValid());
 
     FloatCoordinate projected_nearest;
-    std::tie(ratio, projected_nearest) =
-        projectPointOnSegment(mercator::fromWGS84(segment_source), mercator::fromWGS84(segment_target), mercator::fromWGS84(query_location));
-    nearest_location = mercator::toWGS84(projected_nearest);
+    std::tie(ratio, projected_nearest) = projectPointOnSegment(
+        web_mercator::fromWGS84(segment_source), web_mercator::fromWGS84(segment_target),
+        web_mercator::fromWGS84(query_location));
+    nearest_location = web_mercator::toWGS84(projected_nearest);
 
     const double approximate_distance = greatCircleDistance(query_location, nearest_location);
     BOOST_ASSERT(0.0 <= approximate_distance);
@@ -192,12 +197,19 @@ double computeAngle(const Coordinate first, const Coordinate second, const Coord
     using namespace boost::math::constants;
     using namespace coordinate_calculation;
 
+    if (first == second || second == third)
+        return 180;
+
+    BOOST_ASSERT(first.IsValid());
+    BOOST_ASSERT(second.IsValid());
+    BOOST_ASSERT(third.IsValid());
+
     const double v1x = static_cast<double>(toFloating(first.lon - second.lon));
     const double v1y =
-        mercator::latToY(toFloating(first.lat)) - mercator::latToY(toFloating(second.lat));
+        web_mercator::latToY(toFloating(first.lat)) - web_mercator::latToY(toFloating(second.lat));
     const double v2x = static_cast<double>(toFloating(third.lon - second.lon));
     const double v2y =
-        mercator::latToY(toFloating(third.lat)) - mercator::latToY(toFloating(second.lat));
+        web_mercator::latToY(toFloating(third.lat)) - web_mercator::latToY(toFloating(second.lat));
 
     double angle = (atan2_lookup(v2y, v2x) - atan2_lookup(v1y, v1x)) * 180. / pi<double>();
 
@@ -206,120 +218,109 @@ double computeAngle(const Coordinate first, const Coordinate second, const Coord
         angle += 360.;
     }
 
+    BOOST_ASSERT(angle >= 0);
     return angle;
 }
 
-Coordinate interpolateLinear(double factor, const Coordinate from, const Coordinate to)
-{
-    BOOST_ASSERT(0 <= factor && factor <= 1.0);
-
-    FixedLongitude interpolated_lon(((1. - factor) * static_cast<std::int32_t>(from.lon)) +
-                                    (factor * static_cast<std::int32_t>(to.lon)));
-    FixedLatitude interpolated_lat(((1. - factor) * static_cast<std::int32_t>(from.lat)) +
-                                   (factor * static_cast<std::int32_t>(to.lat)));
-
-    return {std::move(interpolated_lon), std::move(interpolated_lat)};
-}
-
-namespace mercator
-{
-FloatLatitude yToLat(const double y)
-{
-    const auto clamped_y = std::max(-180., std::min(180., y));
-    const double normalized_lat =
-        RAD_TO_DEGREE * 2. * std::atan(std::exp(clamped_y * DEGREE_TO_RAD));
-
-    return FloatLatitude(normalized_lat - 90.);
-}
-
-double latToY(const FloatLatitude latitude)
-{
-    const double normalized_lat = 90. + static_cast<double>(latitude);
-
-    const double y = RAD_TO_DEGREE * std::log(std::tan(normalized_lat * DEGREE_TO_RAD * 0.5));
-    const auto clamped_y = std::max(-180., std::min(180., y));
-    return clamped_y;
-}
-
-FloatLatitude clamp(const FloatLatitude lat)
-{
-    return std::max(std::min(lat, FloatLatitude(detail::MAX_LATITUDE)),
-                    FloatLatitude(-detail::MAX_LATITUDE));
-}
-
-FloatLongitude clamp(const FloatLongitude lon)
-{
-    return std::max(std::min(lon, FloatLongitude(detail::MAX_LONGITUDE)),
-                    FloatLongitude(-detail::MAX_LONGITUDE));
-}
-
-inline void pixelToDegree(const double shift, double &x, double &y)
-{
-    const double b = shift / 2.0;
-    x = (x - b) / shift * 360.0;
-    // FIXME needs to be simplified
-    const double g = (y - b) / -(shift / (2 * M_PI)) / DEGREE_TO_RAD;
-    static_assert(DEGREE_TO_RAD / (2 * M_PI) - 1 / 360. < 0.0001, "");
-    y = static_cast<double>(util::coordinate_calculation::mercator::yToLat(g));
-}
-
-double degreeToPixel(FloatLongitude lon, unsigned zoom)
+boost::optional<Coordinate>
+circleCenter(const Coordinate C1, const Coordinate C2, const Coordinate C3)
 {
-    const double shift = (1u << zoom) * TILE_SIZE;
-    const double b = shift / 2.0;
-    const double x = b * (1 + static_cast<double>(lon) / 180.0);
-    return x;
-}
-
-double degreeToPixel(FloatLatitude lat, unsigned zoom)
-{
-    const double shift = (1u << zoom) * TILE_SIZE;
-    const double b = shift / 2.0;
-    const double y = b * (1. - latToY(lat) / 180.);
-    return y;
-}
-
-FloatCoordinate fromWGS84(const FloatCoordinate &wgs84_coordinate)
-{
-    return {wgs84_coordinate.lon, FloatLatitude{coordinate_calculation::mercator::latToY(wgs84_coordinate.lat)}};
-}
+    // free after http://paulbourke.net/geometry/circlesphere/
+    // require three distinct points
+    if (C1 == C2 || C2 == C3 || C1 == C3)
+    {
+        return boost::none;
+    }
 
-FloatCoordinate toWGS84(const FloatCoordinate &mercator_coordinate)
-{
-    return {mercator_coordinate.lon, coordinate_calculation::mercator::yToLat(static_cast<double>(mercator_coordinate.lat))};
+    // define line through c1, c2 and c2,c3
+    const double C2C1_lat = static_cast<double>(toFloating(C2.lat - C1.lat)); // yDelta_a
+    const double C2C1_lon = static_cast<double>(toFloating(C2.lon - C1.lon)); // xDelta_a
+    const double C3C2_lat = static_cast<double>(toFloating(C3.lat - C2.lat)); // yDelta_b
+    const double C3C2_lon = static_cast<double>(toFloating(C3.lon - C2.lon)); // xDelta_b
+
+    // check for collinear points in X-Direction / Y-Direction
+    if ((std::abs(C2C1_lon) < std::numeric_limits<double>::epsilon() &&
+         std::abs(C3C2_lon) < std::numeric_limits<double>::epsilon()) ||
+        (std::abs(C2C1_lat) < std::numeric_limits<double>::epsilon() &&
+         std::abs(C3C2_lat) < std::numeric_limits<double>::epsilon()))
+    {
+        return boost::none;
+    }
+    else if (std::abs(C2C1_lon) < std::numeric_limits<double>::epsilon())
+    {
+        // vertical line C2C1
+        // due to c1.lon == c2.lon && c1.lon != c3.lon we can rearrange this way
+        BOOST_ASSERT(std::abs(static_cast<double>(toFloating(C3.lon - C1.lon))) >=
+                         std::numeric_limits<double>::epsilon() &&
+                     std::abs(static_cast<double>(toFloating(C2.lon - C3.lon))) >=
+                         std::numeric_limits<double>::epsilon());
+        return circleCenter(C1, C3, C2);
+    }
+    else if (std::abs(C3C2_lon) < std::numeric_limits<double>::epsilon())
+    {
+        // vertical line C3C2
+        // due to c2.lon == c3.lon && c1.lon != c3.lon we can rearrange this way
+        // after rearrangement both deltas will be zero
+        BOOST_ASSERT(std::abs(static_cast<double>(toFloating(C1.lon - C2.lon))) >=
+                         std::numeric_limits<double>::epsilon() &&
+                     std::abs(static_cast<double>(toFloating(C3.lon - C1.lon))) >=
+                         std::numeric_limits<double>::epsilon());
+        return circleCenter(C2, C1, C3);
+    }
+    else
+    {
+        const double C2C1_slope = C2C1_lat / C2C1_lon;
+        const double C3C2_slope = C3C2_lat / C3C2_lon;
+
+        if (std::abs(C2C1_slope) < std::numeric_limits<double>::epsilon())
+        {
+            // Three non-collinear points with C2,C1 on same latitude.
+            // Due to the x-values correct, we can swap C3 and C1 to obtain the correct slope value
+            return circleCenter(C3, C2, C1);
+        }
+        // valid slope values for both lines, calculate the center as intersection of the lines
+
+        // can this ever happen?
+        if (std::abs(C2C1_slope - C3C2_slope) < std::numeric_limits<double>::epsilon())
+            return boost::none;
+
+        const double C1_y = static_cast<double>(toFloating(C1.lat));
+        const double C1_x = static_cast<double>(toFloating(C1.lon));
+        const double C2_y = static_cast<double>(toFloating(C2.lat));
+        const double C2_x = static_cast<double>(toFloating(C2.lon));
+        const double C3_y = static_cast<double>(toFloating(C3.lat));
+        const double C3_x = static_cast<double>(toFloating(C3.lon));
+
+        const double lon = (C2C1_slope * C3C2_slope * (C1_y - C3_y) + C3C2_slope * (C1_x + C2_x) -
+                            C2C1_slope * (C2_x + C3_x)) /
+                           (2 * (C3C2_slope - C2C1_slope));
+        const double lat = (0.5 * (C1_x + C2_x) - lon) / C2C1_slope + 0.5 * (C1_y + C2_y);
+        return Coordinate(FloatLongitude(lon), FloatLatitude(lat));
+    }
 }
 
-// Converts a WMS tile coordinate (z,x,y) into a wgs bounding box
-void xyzToWGS84(
-    const int x, const int y, const int z, double &minx, double &miny, double &maxx, double &maxy)
+double circleRadius(const Coordinate C1, const Coordinate C2, const Coordinate C3)
 {
-    using util::coordinate_calculation::mercator::TILE_SIZE;
-
-    minx = x * TILE_SIZE;
-    miny = (y + 1.0) * TILE_SIZE;
-    maxx = (x + 1.0) * TILE_SIZE;
-    maxy = y * TILE_SIZE;
-    // 2^z * TILE_SIZE
-    const double shift = (1u << static_cast<unsigned>(z)) * TILE_SIZE;
-    pixelToDegree(shift, minx, miny);
-    pixelToDegree(shift, maxx, maxy);
+    // a circle by three points requires thee distinct points
+    auto center = circleCenter(C1, C2, C3);
+    if (center)
+        return haversineDistance(C1, *center);
+    else
+        return std::numeric_limits<double>::infinity();
 }
 
-// Converts a WMS tile coordinate (z,x,y) into a mercator bounding box
-void xyzToMercator(
-    const int x, const int y, const int z, double &minx, double &miny, double &maxx, double &maxy)
+Coordinate interpolateLinear(double factor, const Coordinate from, const Coordinate to)
 {
-    using namespace util::coordinate_calculation::mercator;
+    BOOST_ASSERT(0 <= factor && factor <= 1.0);
 
-    xyzToWGS84(x, y, z, minx, miny, maxx, maxy);
+    FixedLongitude interpolated_lon(((1. - factor) * static_cast<std::int32_t>(from.lon)) +
+                                    (factor * static_cast<std::int32_t>(to.lon)));
+    FixedLatitude interpolated_lat(((1. - factor) * static_cast<std::int32_t>(from.lat)) +
+                                   (factor * static_cast<std::int32_t>(to.lat)));
 
-    minx = static_cast<double>(clamp(util::FloatLongitude(minx))) * DEGREE_TO_PX;
-    miny = latToY(clamp(util::FloatLatitude(miny))) * DEGREE_TO_PX;
-    maxx = static_cast<double>(clamp(util::FloatLongitude(maxx))) * DEGREE_TO_PX;
-    maxy = latToY(clamp(util::FloatLatitude(maxy))) * DEGREE_TO_PX;
+    return {std::move(interpolated_lon), std::move(interpolated_lat)};
 }
 
-} // ns mercato // ns mercatorr
 } // ns coordinate_calculation
 } // ns util
 } // ns osrm
diff --git a/src/util/name_table.cpp b/src/util/name_table.cpp
index e3d5cef..fedc31f 100644
--- a/src/util/name_table.cpp
+++ b/src/util/name_table.cpp
@@ -1,10 +1,10 @@
+#include "util/exception.hpp"
 #include "util/name_table.hpp"
 #include "util/simple_logger.hpp"
-#include "util/exception.hpp"
 
 #include <algorithm>
-#include <limits>
 #include <fstream>
+#include <limits>
 
 #include <boost/filesystem/fstream.hpp>
 
@@ -17,27 +17,30 @@ NameTable::NameTable(const std::string &filename)
 {
     boost::filesystem::ifstream name_stream(filename, std::ios::binary);
 
-    if( !name_stream )
+    if (!name_stream)
         throw exception("Failed to open " + filename + " for reading.");
 
     name_stream >> m_name_table;
 
     unsigned number_of_chars = 0;
     name_stream.read(reinterpret_cast<char *>(&number_of_chars), sizeof(number_of_chars));
-    if( !name_stream )
+    if (!name_stream)
         throw exception("Encountered invalid file, failed to read number of contained chars");
 
-    BOOST_ASSERT_MSG(0 != number_of_chars, "name file broken");
     m_names_char_list.resize(number_of_chars + 1); //+1 gives sentinel element
-    name_stream.read(reinterpret_cast<char *>(&m_names_char_list[0]),
-                     number_of_chars * sizeof(m_names_char_list[0]));
-    if( !name_stream )
-        throw exception("Failed to read " + std::to_string(number_of_chars) + " characters from file.");
-
-    if (0 == m_names_char_list.size())
+    m_names_char_list.back() = 0;
+    if (number_of_chars > 0)
+    {
+        name_stream.read(reinterpret_cast<char *>(&m_names_char_list[0]),
+                         number_of_chars * sizeof(m_names_char_list[0]));
+    }
+    else
     {
         util::SimpleLogger().Write(logWARNING) << "list of street names is empty";
     }
+    if (!name_stream)
+        throw exception("Failed to read " + std::to_string(number_of_chars) +
+                        " characters from file.");
 }
 
 std::string NameTable::GetNameForID(const unsigned name_id) const
diff --git a/taginfo.json b/taginfo.json
index 4ef48de..8acc7d7 100644
--- a/taginfo.json
+++ b/taginfo.json
@@ -77,6 +77,7 @@
         {"key": "access", "value": "emergency"},
         {"key": "access", "value": "psv"},
         {"key": "access", "value": "delivery"},
+        {"key": "maxspeed", "value": "none"},
         {"key": "maxspeed", "value": "urban"},
         {"key": "maxspeed", "value": "rural"},
         {"key": "maxspeed", "value": "trunk"},
diff --git a/test/data/Makefile b/test/data/Makefile
index 0ddda31..8684bc8 100755
--- a/test/data/Makefile
+++ b/test/data/Makefile
@@ -1,29 +1,54 @@
-MONACO_URL:=https://s3.amazonaws.com/mapbox/osrm/testing/monaco.osm.pbf
+DATA_NAME:=monaco
+DATA_URL:=https://s3.amazonaws.com/mapbox/osrm/testing/$(DATA_NAME).osm.pbf
+DATA_POLY_URL:=https://s3.amazonaws.com/mapbox/osrm/testing/$(DATA_NAME).poly
 TOOL_ROOT:=../../build
 PROFILE_ROOT:=../../profiles
+SCRIPT_ROOT:=../../scripts
 OSRM_EXTRACT:=$(TOOL_ROOT)/osrm-extract
 OSRM_CONTRACT:=$(TOOL_ROOT)/osrm-contract
+OSRM_ROUTED:=$(TOOL_ROOT)/osrm-routed
+POLY2REQ:=$(SCRIPT_ROOT)/poly2req.js
+TIMER:=$(SCRIPT_ROOT)/timer.sh
 PROFILE:=$(PROFILE_ROOT)/car.lua
 
-all: monaco.osrm.hsgr
+all: $(DATA_NAME).osrm.hsgr
 
 clean:
-	rm monaco.*
+	rm $(DATA_NAME).*
 
-monaco.osm.pbf:
-	wget $(MONACO_URL) -O monaco.osm.pbf
+$(DATA_NAME).osm.pbf:
+	wget $(DATA_URL) -O $(DATA_NAME).osm.pbf
 
-monaco.osrm: monaco.osm.pbf $(PROFILE) $(OSRM_EXTRACT)
+$(DATA_NAME).poly:
+	wget $(DATA_POLY_URL) -O $(DATA_NAME).poly
+
+$(DATA_NAME).osrm: $(DATA_NAME).osm.pbf $(DATA_NAME).poly $(PROFILE) $(OSRM_EXTRACT)
 	@echo "Verifiyng data file integrity..."
 	md5sum -c data.md5sum
 	@echo "Running osrm-extract..."
-	$(OSRM_EXTRACT) monaco.osm.pbf -p $(PROFILE)
+	$(TIMER) "osrm-extract" $(OSRM_EXTRACT) $(DATA_NAME).osm.pbf -p $(PROFILE)
 
-monaco.osrm.hsgr: monaco.osrm $(PROFILE) $(OSRM_CONTRACT)
+$(DATA_NAME).osrm.hsgr: $(DATA_NAME).osrm $(PROFILE) $(OSRM_CONTRACT)
 	@echo "Running osrm-contract..."
-	$(OSRM_CONTRACT) monaco.osrm
+	$(TIMER) "osrm-contract" $(OSRM_CONTRACT) $(DATA_NAME).osrm
+
+$(DATA_NAME).requests: $(DATA_NAME).poly
+	$(POLY2REQ) $(DATA_NAME).poly > $(DATA_NAME).requests
+
+osrm-routed.pid: $(DATA_NAME).osrm.hsgr
+	@/bin/sh -c '$(OSRM_ROUTED) $(DATA_NAME).osrm& echo "$$!" > osrm-routed.pid'
+	sleep 1
+
+benchmark: $(DATA_NAME).requests osrm-routed.pid
+	@echo "Running benchmark..."
+	$(TIMER) "queries" "cat $(DATA_NAME).requests | xargs curl &> /dev/null"
+	@cat osrm-routed.pid | xargs kill
+	@rm osrm-routed.pid
+	@echo "**** timings ***"
+	@cat /tmp/osrm.timings
+	@echo "****************"
 
 checksum:
-	md5sum monaco.osm.pbf > data.md5sum
+	md5sum $(DATA_NAME).osm.pbf $(DATA_NAME).poly > data.md5sum
 
-.PHONY: clean checksum
+.PHONY: clean checksum benchmark
diff --git a/test/data/data.md5sum b/test/data/data.md5sum
index 47151b4..9bcfd33 100644
--- a/test/data/data.md5sum
+++ b/test/data/data.md5sum
@@ -1 +1,2 @@
 2b8dd9343d5e615afc9c67bcc7028a63  monaco.osm.pbf
+b0788991ab3791d53c1c20b6281f81ad  monaco.poly
diff --git a/unit_tests/CMakeLists.txt b/unit_tests/CMakeLists.txt
new file mode 100644
index 0000000..63e10c7
--- /dev/null
+++ b/unit_tests/CMakeLists.txt
@@ -0,0 +1,76 @@
+file(GLOB EngineTestsSources
+    engine_tests.cpp
+    engine/*.cpp)
+
+file(GLOB ExtractorTestsSources
+    extractor_tests.cpp
+    extractor/*.cpp)
+
+file(GLOB LibraryTestsSources
+    library_tests.cpp
+    library/*.cpp)
+
+file(GLOB ServerTestsSources
+    server_tests.cpp
+    server/*.cpp)
+
+file(GLOB UtilTestsSources
+    util_tests.cpp
+    util/*.cpp)
+
+
+add_executable(engine-tests
+	EXCLUDE_FROM_ALL
+	${EngineTestsSources}
+	$<TARGET_OBJECTS:ENGINE> $<TARGET_OBJECTS:STORAGE> $<TARGET_OBJECTS:UTIL>)
+
+add_executable(extractor-tests
+	EXCLUDE_FROM_ALL
+	${ExtractorTestsSources}
+	$<TARGET_OBJECTS:EXTRACTOR> $<TARGET_OBJECTS:UTIL>)
+
+add_executable(library-tests
+	EXCLUDE_FROM_ALL
+	${LibraryTestsSources})
+
+add_executable(server-tests
+	EXCLUDE_FROM_ALL
+	${ServerTestsSources}
+	$<TARGET_OBJECTS:UTIL> $<TARGET_OBJECTS:SERVER>)
+
+add_executable(util-tests
+	EXCLUDE_FROM_ALL
+	${UtilTestsSources}
+	$<TARGET_OBJECTS:UTIL>)
+
+
+# FindPackage below overwrites Boost_LIBRARIES
+set(AllBoostLibrariesExceptUnitTest ${Boost_LIBRARIES})
+
+find_package(Boost 1.49.0 REQUIRED COMPONENTS unit_test_framework)
+
+if(NOT WIN32)
+  add_definitions(-DBOOST_TEST_DYN_LINK)
+endif()
+
+# After the find_package call we got only the unit test library
+set(BoostUnitTestLibrary ${Boost_LIBRARIES})
+
+include_directories(SYSTEM ${Boost_INCLUDE_DIRS})
+
+
+target_include_directories(engine-tests PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
+target_include_directories(library-tests PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
+target_include_directories(util-tests PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
+
+
+target_link_libraries(engine-tests ${ENGINE_LIBRARIES} ${BoostUnitTestLibrary})
+target_link_libraries(extractor-tests ${EXTRACTOR_LIBRARIES} ${BoostUnitTestLibrary})
+target_link_libraries(library-tests osrm ${Boost_LIBRARIES} ${BoostUnitTestLibrary})
+target_link_libraries(server-tests osrm ${Boost_LIBRARIES} ${BoostUnitTestLibrary} ${ZLIB_LIBRARY})
+target_link_libraries(util-tests ${UTIL_LIBRARIES} ${BoostUnitTestLibrary})
+
+
+add_custom_target(tests
+	DEPENDS
+	engine-tests extractor-tests library-tests server-tests util-tests)
diff --git a/unit_tests/engine/base64.cpp b/unit_tests/engine/base64.cpp
index 4896965..f0515fc 100644
--- a/unit_tests/engine/base64.cpp
+++ b/unit_tests/engine/base64.cpp
@@ -16,7 +16,6 @@ BOOST_AUTO_TEST_CASE(rfc4648_test_vectors)
 {
     using namespace osrm::engine;
 
-    BOOST_CHECK_EQUAL(encodeBase64(""), "");
     BOOST_CHECK_EQUAL(encodeBase64("f"), "Zg==");
     BOOST_CHECK_EQUAL(encodeBase64("fo"), "Zm8=");
     BOOST_CHECK_EQUAL(encodeBase64("foo"), "Zm9v");
@@ -29,7 +28,6 @@ BOOST_AUTO_TEST_CASE(rfc4648_test_vectors_roundtrip)
 {
     using namespace osrm::engine;
 
-    BOOST_CHECK_EQUAL(decodeBase64(encodeBase64("")), "");
     BOOST_CHECK_EQUAL(decodeBase64(encodeBase64("f")), "f");
     BOOST_CHECK_EQUAL(decodeBase64(encodeBase64("fo")), "fo");
     BOOST_CHECK_EQUAL(decodeBase64(encodeBase64("foo")), "foo");
diff --git a/unit_tests/engine/douglas_peucker.cpp b/unit_tests/engine/douglas_peucker.cpp
index b280c13..2a1a7a3 100644
--- a/unit_tests/engine/douglas_peucker.cpp
+++ b/unit_tests/engine/douglas_peucker.cpp
@@ -1,4 +1,5 @@
 #include "engine/douglas_peucker.hpp"
+#include "util/coordinate_calculation.hpp"
 
 #include <boost/test/unit_test.hpp>
 #include <boost/test/test_case_template.hpp>
@@ -12,6 +13,16 @@ BOOST_AUTO_TEST_SUITE(douglas_peucker_simplification)
 using namespace osrm;
 using namespace osrm::engine;
 
+BOOST_AUTO_TEST_CASE(calibrate_thresholds)
+{
+    auto thresholds = detail::generateThreshold(5, 19);
+    auto z = 0;
+    for (auto t : thresholds)
+    {
+        BOOST_TEST_MESSAGE(t << ", // z" << z++);
+    }
+}
+
 BOOST_AUTO_TEST_CASE(removed_middle_test)
 {
     /*
@@ -23,9 +34,9 @@ BOOST_AUTO_TEST_CASE(removed_middle_test)
     */
     std::vector<util::Coordinate> coordinates = {
         util::Coordinate(util::FloatLongitude(5), util::FloatLatitude(5)),
-        util::Coordinate(util::FloatLongitude(5.995715), util::FloatLatitude(6)),
-        util::Coordinate(util::FloatLongitude(10), util::FloatLatitude(10)),
-        util::Coordinate(util::FloatLongitude(15), util::FloatLatitude(5))};
+        util::Coordinate(util::FloatLongitude(12.5), util::FloatLatitude(12.6096298302)),
+        util::Coordinate(util::FloatLongitude(20), util::FloatLatitude(20)),
+        util::Coordinate(util::FloatLongitude(25), util::FloatLatitude(5))};
 
     for (unsigned z = 0; z < detail::DOUGLAS_PEUCKER_THRESHOLDS_SIZE; z++)
     {
@@ -49,19 +60,19 @@ BOOST_AUTO_TEST_CASE(removed_middle_test_zoom_sensitive)
     std::vector<util::Coordinate> coordinates = {
         util::Coordinate(util::FloatLongitude(5), util::FloatLatitude(5)),
         util::Coordinate(util::FloatLongitude(6), util::FloatLatitude(6)),
-        util::Coordinate(util::FloatLongitude(10), util::FloatLatitude(10)),
-        util::Coordinate(util::FloatLongitude(15), util::FloatLatitude(5))};
+        util::Coordinate(util::FloatLongitude(20), util::FloatLatitude(20)),
+        util::Coordinate(util::FloatLongitude(25), util::FloatLatitude(5))};
 
-    // Coordinate 6,6 should start getting included at Z12 and higher
+    // Coordinate 6,6 should start getting included at Z9 and higher
     // Note that 5,5->6,6->10,10 is *not* a straight line on the surface
     // of the earth
-    for (unsigned z = 0; z < 11; z++)
+    for (unsigned z = 0; z < 9; z++)
     {
         auto result = douglasPeucker(coordinates, z);
         BOOST_CHECK_EQUAL(result.size(), 3);
     }
-    // From 12 to max zoom, we should get all coordinates
-    for (unsigned z = 12; z < detail::DOUGLAS_PEUCKER_THRESHOLDS_SIZE; z++)
+    // From 9 to max zoom, we should get all coordinates
+    for (unsigned z = 9; z < detail::DOUGLAS_PEUCKER_THRESHOLDS_SIZE; z++)
     {
         auto result = douglasPeucker(coordinates, z);
         BOOST_CHECK_EQUAL(result.size(), 4);
@@ -71,38 +82,39 @@ BOOST_AUTO_TEST_CASE(removed_middle_test_zoom_sensitive)
 
 BOOST_AUTO_TEST_CASE(remove_second_node_test)
 {
-    for (unsigned z = 0; z < detail::DOUGLAS_PEUCKER_THRESHOLDS_SIZE; z++)
+    // derived from the degreeToPixel function
+    const auto delta_pixel_to_delta_degree = [](const int pixel, const unsigned zoom) {
+        const double shift = (1u << zoom) * 256;
+        const double b = shift / 2.0;
+        return pixel * 180. / b;
+    };
+    // For zoom level 0 all gets reduced to a line
+    for (unsigned z = 1; z < detail::DOUGLAS_PEUCKER_THRESHOLDS_SIZE; z++)
     {
         /*
-             x--x
-             |   \
-           x-x    x
-                  |
-                  x
+             x
+             | \
+           x-x  x
+                |
+                x
         */
         std::vector<util::Coordinate> input = {
-            util::Coordinate(util::FixedLongitude(5 * COORDINATE_PRECISION),
-                             util::FixedLatitude(5 * COORDINATE_PRECISION)),
-            util::Coordinate(util::FixedLongitude(5 * COORDINATE_PRECISION),
-                             util::FixedLatitude(5 * COORDINATE_PRECISION +
-                                                 detail::DOUGLAS_PEUCKER_THRESHOLDS[z])),
-            util::Coordinate(util::FixedLongitude(10 * COORDINATE_PRECISION),
-                             util::FixedLatitude(10 * COORDINATE_PRECISION)),
-            util::Coordinate(util::FixedLongitude(10 * COORDINATE_PRECISION),
-                             util::FixedLatitude(10 + COORDINATE_PRECISION +
-                                                 detail::DOUGLAS_PEUCKER_THRESHOLDS[z] * 2)),
-            util::Coordinate(util::FixedLongitude(5 * COORDINATE_PRECISION),
-                             util::FixedLatitude(15 * COORDINATE_PRECISION)),
-            util::Coordinate(util::FixedLongitude(5 * COORDINATE_PRECISION +
-                                                  detail::DOUGLAS_PEUCKER_THRESHOLDS[z]),
-                             util::FixedLatitude(15 * COORDINATE_PRECISION))};
-        BOOST_TEST_MESSAGE("Threshold (" << z << "): " << detail::DOUGLAS_PEUCKER_THRESHOLDS[z]);
+            util::Coordinate(util::FloatLongitude(5),
+                             util::FloatLatitude(5)),
+            util::Coordinate(util::FloatLongitude(5 + delta_pixel_to_delta_degree(2, z)),
+                             util::FloatLatitude(5)),
+            util::Coordinate(util::FloatLongitude(10),
+                             util::FloatLatitude(10)),
+            util::Coordinate(util::FloatLongitude(5),
+                             util::FloatLatitude(15)),
+            util::Coordinate(util::FloatLongitude(5),
+                             util::FloatLatitude(15 + delta_pixel_to_delta_degree(2, z)))};
+        BOOST_TEST_MESSAGE("Delta (" << z << "): " << delta_pixel_to_delta_degree(2, z));
         auto result = douglasPeucker(input, z);
-        BOOST_CHECK_EQUAL(result.size(), 4);
+        BOOST_CHECK_EQUAL(result.size(), 3);
         BOOST_CHECK_EQUAL(input[0], result[0]);
         BOOST_CHECK_EQUAL(input[2], result[1]);
-        BOOST_CHECK_EQUAL(input[3], result[2]);
-        BOOST_CHECK_EQUAL(input[5], result[3]);
+        BOOST_CHECK_EQUAL(input[4], result[2]);
     }
 }
 
diff --git a/unit_tests/library/equal_json.hpp b/unit_tests/library/equal_json.hpp
index 8e2f9ac..1453086 100644
--- a/unit_tests/library/equal_json.hpp
+++ b/unit_tests/library/equal_json.hpp
@@ -1,12 +1,13 @@
 #ifndef UNIT_TESTS_JSON_EQUAL
 #define UNIT_TESTS_JSON_EQUAL
 
-#include <boost/test/included/unit_test.hpp>
+#include <boost/test/unit_test.hpp>
 
+#include "osrm/json_container.hpp"
 #include "util/json_deep_compare.hpp"
 
-boost::test_tools::predicate_result compareJSON(const osrm::util::json::Value &reference,
-                                                  const osrm::util::json::Value &result)
+inline boost::test_tools::predicate_result compareJSON(const osrm::util::json::Value &reference,
+                                                       const osrm::util::json::Value &result)
 {
     std::string reason;
     auto is_same = osrm::util::json::compare(reference, result, reason);
diff --git a/unit_tests/library/match.cpp b/unit_tests/library/match.cpp
index 5cd2739..37d36c2 100644
--- a/unit_tests/library/match.cpp
+++ b/unit_tests/library/match.cpp
@@ -3,6 +3,7 @@
 
 #include "args.hpp"
 #include "fixture.hpp"
+#include "coordinates.hpp"
 
 #include "osrm/match_parameters.hpp"
 
@@ -23,15 +24,44 @@ BOOST_AUTO_TEST_CASE(test_match)
 
     auto osrm = getOSRM(args[0]);
 
-    /*
     MatchParameters params;
+    params.coordinates.push_back(get_dummy_location());
+    params.coordinates.push_back(get_dummy_location());
+    params.coordinates.push_back(get_dummy_location());
 
     json::Object result;
 
     const auto rc = osrm.Match(params, result);
 
     BOOST_CHECK(rc == Status::Ok || rc == Status::Error);
-    */
+    const auto code = result.values.at("code").get<json::String>().value;
+    BOOST_CHECK_EQUAL(code, "Ok");
+
+    const auto &tracepoints = result.values.at("tracepoints").get<json::Array>().values;
+    BOOST_CHECK_EQUAL(tracepoints.size(), params.coordinates.size());
+
+    const auto &matchings = result.values.at("matchings").get<json::Array>().values;
+    const auto &number_of_matchings = matchings.size();
+    for (const auto &waypoint : tracepoints)
+    {
+        if (waypoint.is<mapbox::util::recursive_wrapper<util::json::Object>>())
+        {
+            const auto &waypoint_object = waypoint.get<json::Object>();
+            const auto &waypoint_object_location = waypoint_object.values.at("location").get<json::Array>().values;
+            util::FloatLongitude lon(waypoint_object_location[0].get<json::Number>().value);
+            util::FloatLatitude lat(waypoint_object_location[1].get<json::Number>().value);
+            util::Coordinate location_coordinate(lon, lat);
+            BOOST_CHECK(location_coordinate.IsValid());
+            const auto matchings_index = waypoint_object.values.at("matchings_index").get<json::Number>().value;
+            const auto waypoint_index = waypoint_object.values.at("waypoint_index").get<json::Number>().value;
+            const auto &route_legs = matchings[matchings_index].get<json::Object>().values.at("legs").get<json::Array>().values;
+            BOOST_CHECK_LT(waypoint_index, route_legs.size() + 1);
+            BOOST_CHECK_LT(matchings_index, number_of_matchings);
+        } else
+        {
+          BOOST_CHECK(waypoint.is<json::Null>());
+        }
+    }
 }
 
 BOOST_AUTO_TEST_SUITE_END()
diff --git a/unit_tests/library/nearest.cpp b/unit_tests/library/nearest.cpp
index b56f0f7..1685d94 100644
--- a/unit_tests/library/nearest.cpp
+++ b/unit_tests/library/nearest.cpp
@@ -30,7 +30,7 @@ BOOST_AUTO_TEST_CASE(test_nearest_response)
     BOOST_REQUIRE(rc == Status::Ok);
 
     const auto code = result.values.at("code").get<json::String>().value;
-    BOOST_CHECK_EQUAL(code, "ok");
+    BOOST_CHECK_EQUAL(code, "Ok");
 
     const auto &waypoints = result.values.at("waypoints").get<json::Array>().values;
     BOOST_CHECK(!waypoints.empty()); // the dataset has at least one nearest coordinate
@@ -97,7 +97,7 @@ BOOST_AUTO_TEST_CASE(test_nearest_response_for_location_in_small_component)
     BOOST_REQUIRE(rc == Status::Ok);
 
     const auto code = result.values.at("code").get<json::String>().value;
-    BOOST_CHECK_EQUAL(code, "ok");
+    BOOST_CHECK_EQUAL(code, "Ok");
 
     const auto &waypoints = result.values.at("waypoints").get<json::Array>().values;
     BOOST_CHECK(!waypoints.empty());
diff --git a/unit_tests/library/route.cpp b/unit_tests/library/route.cpp
index e93ceba..f9aaae9 100644
--- a/unit_tests/library/route.cpp
+++ b/unit_tests/library/route.cpp
@@ -1,18 +1,18 @@
-#include <boost/test/unit_test.hpp>
 #include <boost/test/test_case_template.hpp>
+#include <boost/test/unit_test.hpp>
 
 #include "args.hpp"
-#include "fixture.hpp"
-#include "equal_json.hpp"
 #include "coordinates.hpp"
-
-#include "osrm/route_parameters.hpp"
+#include "equal_json.hpp"
+#include "fixture.hpp"
 
 #include "osrm/coordinate.hpp"
 #include "osrm/engine_config.hpp"
 #include "osrm/json_container.hpp"
-#include "osrm/status.hpp"
+#include "osrm/json_container.hpp"
 #include "osrm/osrm.hpp"
+#include "osrm/route_parameters.hpp"
+#include "osrm/status.hpp"
 
 BOOST_AUTO_TEST_SUITE(route)
 
@@ -24,6 +24,7 @@ BOOST_AUTO_TEST_CASE(test_route_same_coordinates_fixture)
     using namespace osrm;
 
     RouteParameters params;
+    params.steps = true;
     params.coordinates.push_back(get_dummy_location());
     params.coordinates.push_back(get_dummy_location());
 
@@ -31,33 +32,52 @@ BOOST_AUTO_TEST_CASE(test_route_same_coordinates_fixture)
     const auto rc = osrm.Route(params, result);
     BOOST_CHECK(rc == Status::Ok);
 
+    // unset snapping dependent hint
+    for (auto &itr : result.values["waypoints"].get<json::Array>().values)
+        itr.get<json::Object>().values["hint"] = "";
+
+    const auto location = json::Array{{{7.437070}, {43.749247}}};
+
     json::Object reference{
-        {{"code", "ok"},
+        {{"code", "Ok"},
          {"waypoints",
-          json::Array{{json::Object{{{"name", ""}, {"location", json::Array{}}, {"hint", ""}}},
-                       json::Object{{{"name", ""}, {"location", json::Array{}}, {"hint", ""}}}}}},
-         {"routes", json::Array{{json::Object{
-                        {{"distance", 0.},
-                         {"duration", 0.},
-                         {"geometry", ""},
-                         {"legs", json::Array{{json::Object{
-                                      {{"distance", 0.},
-                                       {"duration", 0.},
-                                       {"summary", ""},
-                                       {"steps", json::Array{{json::Object{
-                                                     {{"duration", 0.},
-                                                      {"distance", 0.},
-                                                      {"geometry", ""},
-                                                      {"name", ""},
-                                                      {"mode", "driving"},
-                                                      {"maneuver", json::Object{{
-                                                                       {"type", "depart"},
-                                                                       {"location", json::Array{}},
-                                                                       {"modifier", ""},
-                                                                       {"bearing_before", 0.},
-                                                                       {"bearing_after", 0.},
-                                                                       {"exit", 0},
-                                                                   }}}}}}}}}}}}}}}}}}}};
+          json::Array{
+              {json::Object{
+                   {{"name", "Boulevard du Larvotto"}, {"location", location}, {"hint", ""}}},
+               json::Object{
+                   {{"name", "Boulevard du Larvotto"}, {"location", location}, {"hint", ""}}}}}},
+         {"routes",
+          json::Array{{json::Object{
+              {{"distance", 0.},
+               {"duration", 0.},
+               {"geometry", "yw_jGupkl@??"},
+               {"legs",
+                json::Array{{json::Object{
+                    {{"distance", 0.},
+                     {"duration", 0.},
+                     {"steps", json::Array{{json::Object{{{"duration", 0.},
+                                                          {"distance", 0.},
+                                                          {"geometry", "yw_jGupkl@??"},
+                                                          {"name", "Boulevard du Larvotto"},
+                                                          {"mode", "driving"},
+                                                          {"maneuver", json::Object{{
+                                                                           {"type", "depart"},
+                                                                           {"location", location},
+                                                                           {"bearing_before", 0.},
+                                                                           {"bearing_after", 0.},
+                                                                       }}}}},
+
+                                            json::Object{{{"duration", 0.},
+                                                          {"distance", 0.},
+                                                          {"geometry", "yw_jGupkl@"},
+                                                          {"name", "Boulevard du Larvotto"},
+                                                          {"mode", "driving"},
+                                                          {"maneuver", json::Object{{
+                                                                           {"type", "arrive"},
+                                                                           {"location", location},
+                                                                           {"bearing_before", 0.},
+                                                                           {"bearing_after", 0.},
+                                                                       }}}}}}}}}}}}}}}}}}}};
 
     CHECK_EQUAL_JSON(reference, result);
 }
@@ -70,6 +90,7 @@ BOOST_AUTO_TEST_CASE(test_route_same_coordinates)
     using namespace osrm;
 
     RouteParameters params;
+    params.steps = true;
     params.coordinates.push_back(get_dummy_location());
     params.coordinates.push_back(get_dummy_location());
     params.coordinates.push_back(get_dummy_location());
@@ -79,7 +100,7 @@ BOOST_AUTO_TEST_CASE(test_route_same_coordinates)
     BOOST_CHECK(rc == Status::Ok);
 
     const auto code = result.values.at("code").get<json::String>().value;
-    BOOST_CHECK_EQUAL(code, "ok");
+    BOOST_CHECK_EQUAL(code, "Ok");
 
     const auto &waypoints = result.values.at("waypoints").get<json::Array>().values;
     BOOST_CHECK(waypoints.size() == params.coordinates.size());
@@ -102,10 +123,8 @@ BOOST_AUTO_TEST_CASE(test_route_same_coordinates)
         BOOST_CHECK(!hint.empty());
     }
 
-    // alternative=true by default
     const auto &routes = result.values.at("routes").get<json::Array>().values;
-    BOOST_CHECK(!routes.empty());
-    BOOST_CHECK(routes.size() > 1);
+    BOOST_REQUIRE_GT(routes.size(), 0);
 
     for (const auto &route : routes)
     {
@@ -134,11 +153,6 @@ BOOST_AUTO_TEST_CASE(test_route_same_coordinates)
             const auto duration = leg_object.values.at("duration").get<json::Number>().value;
             BOOST_CHECK_EQUAL(duration, 0);
 
-            // nothing can be said about summary, empty or contains human readable summary
-            const auto summary = leg_object.values.at("summary").get<json::String>().value;
-            BOOST_CHECK(((void)summary, true));
-
-            // steps=true by default
             const auto &steps = leg_object.values.at("steps").get<json::Array>().values;
             BOOST_CHECK(!steps.empty());
 
@@ -209,7 +223,7 @@ BOOST_AUTO_TEST_CASE(test_route_response_for_locations_in_small_component)
     BOOST_CHECK(rc == Status::Ok);
 
     const auto code = result.values.at("code").get<json::String>().value;
-    BOOST_CHECK_EQUAL(code, "ok");
+    BOOST_CHECK_EQUAL(code, "Ok");
 
     const auto &waypoints = result.values.at("waypoints").get<json::Array>().values;
     BOOST_CHECK_EQUAL(waypoints.size(), params.coordinates.size());
diff --git a/unit_tests/mocks/mock_datafacade.hpp b/unit_tests/mocks/mock_datafacade.hpp
index cb05a94..f608b66 100644
--- a/unit_tests/mocks/mock_datafacade.hpp
+++ b/unit_tests/mocks/mock_datafacade.hpp
@@ -170,7 +170,7 @@ class MockDataFacade final : public engine::datafacade::BaseDataFacade
     std::string GetNameForID(const unsigned /* name_id */) const override { return ""; }
     std::size_t GetCoreSize() const override { return 0; }
     std::string GetTimestamp() const override { return ""; }
-    bool GetUTurnsDefault() const override { return true; }
+    bool GetContinueStraightDefault() const override { return true; }
 };
 } // ns test
 } // ns osrm
diff --git a/unit_tests/server/parameters_parser.cpp b/unit_tests/server/parameters_parser.cpp
index e6f2d82..1b5161f 100644
--- a/unit_tests/server/parameters_parser.cpp
+++ b/unit_tests/server/parameters_parser.cpp
@@ -1,12 +1,12 @@
 #include "server/api/parameters_parser.hpp"
 
 #include "engine/api/base_parameters.hpp"
+#include "engine/api/match_parameters.hpp"
+#include "engine/api/nearest_parameters.hpp"
 #include "engine/api/route_parameters.hpp"
 #include "engine/api/table_parameters.hpp"
-#include "engine/api/match_parameters.hpp"
-#include "engine/api/trip_parameters.hpp"
 #include "engine/api/tile_parameters.hpp"
-#include "engine/api/nearest_parameters.hpp"
+#include "engine/api/trip_parameters.hpp"
 
 #include <fstream>
 
@@ -58,9 +58,9 @@ std::ostream &operator<<(std::ostream &out, Bearing bearing)
 }
 }
 
-#include <boost/test/unit_test.hpp>
-#include <boost/test/test_tools.hpp>
 #include <boost/optional/optional_io.hpp>
+#include <boost/test/test_tools.hpp>
+#include <boost/test/unit_test.hpp>
 
 #define CHECK_EQUAL_RANGE(R1, R2)                                                                  \
     BOOST_CHECK_EQUAL_COLLECTIONS(R1.begin(), R1.end(), R2.begin(), R2.end());
@@ -87,13 +87,13 @@ BOOST_AUTO_TEST_CASE(invalid_route_urls)
         testInvalidOptions<engine::api::RouteParameters>("1,2;3,4?overview=false&bearings=foo"),
         32UL);
     BOOST_CHECK_EQUAL(
-        testInvalidOptions<engine::api::RouteParameters>("1,2;3,4?overview=false&uturns=foo"),
-        30UL);
+        testInvalidOptions<engine::api::RouteParameters>("1,2;3,4?overview=false&continue_straight=foo"),
+        41UL);
     BOOST_CHECK_EQUAL(
         testInvalidOptions<engine::api::RouteParameters>("1,2;3,4?overview=false&radiuses=foo"),
         32UL);
     BOOST_CHECK_EQUAL(
-        testInvalidOptions<engine::api::RouteParameters>("1,2;3,4?overview=false&hints=foo"), 22UL);
+        testInvalidOptions<engine::api::RouteParameters>("1,2;3,4?overview=false&hints=foo"), 29UL);
     BOOST_CHECK_EQUAL(
         testInvalidOptions<engine::api::RouteParameters>("1,2;3,4?overview=false&geometries=foo"),
         22UL);
@@ -102,7 +102,7 @@ BOOST_AUTO_TEST_CASE(invalid_route_urls)
         22L);
     BOOST_CHECK_EQUAL(
         testInvalidOptions<engine::api::RouteParameters>("1,2;3,4?overview=false&alternatives=foo"),
-        22UL);
+        36UL);
 }
 
 BOOST_AUTO_TEST_CASE(invalid_table_urls)
@@ -114,9 +114,10 @@ BOOST_AUTO_TEST_CASE(invalid_table_urls)
     BOOST_CHECK_EQUAL(testInvalidOptions<engine::api::TableParameters>(
                           "1,2;3,4?sources=1&destinations=1&bla=foo"),
                       32UL);
-    BOOST_CHECK_EQUAL(testInvalidOptions<engine::api::TableParameters>("1,2;3,4?sources=foo"), 7UL);
+    BOOST_CHECK_EQUAL(testInvalidOptions<engine::api::TableParameters>("1,2;3,4?sources=foo"),
+                      16UL);
     BOOST_CHECK_EQUAL(testInvalidOptions<engine::api::TableParameters>("1,2;3,4?destinations=foo"),
-                      7UL);
+                      21UL);
 }
 
 BOOST_AUTO_TEST_CASE(valid_route_urls)
@@ -132,12 +133,14 @@ BOOST_AUTO_TEST_CASE(valid_route_urls)
     BOOST_CHECK_EQUAL(reference_1.alternatives, result_1->alternatives);
     BOOST_CHECK_EQUAL(reference_1.geometries, result_1->geometries);
     BOOST_CHECK_EQUAL(reference_1.overview, result_1->overview);
-    BOOST_CHECK_EQUAL(reference_1.uturns, result_1->uturns);
+    BOOST_CHECK_EQUAL(reference_1.continue_straight, result_1->continue_straight);
     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);
 
     engine::api::RouteParameters reference_2{};
+    reference_2.alternatives = true;
+    reference_2.steps = true;
     reference_2.coordinates = coords_1;
     auto result_2 = api::parseParameters<engine::api::RouteParameters>(
         "1,2;3,4?steps=true&alternatives=true&geometries=polyline&overview=simplified");
@@ -146,39 +149,39 @@ BOOST_AUTO_TEST_CASE(valid_route_urls)
     BOOST_CHECK_EQUAL(reference_2.alternatives, result_2->alternatives);
     BOOST_CHECK_EQUAL(reference_2.geometries, result_2->geometries);
     BOOST_CHECK_EQUAL(reference_2.overview, result_2->overview);
-    BOOST_CHECK_EQUAL(reference_2.uturns, result_2->uturns);
+    BOOST_CHECK_EQUAL(reference_2.continue_straight, result_2->continue_straight);
     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);
 
-    engine::api::RouteParameters reference_3{false,
-                                             false,
-                                             engine::api::RouteParameters::GeometriesType::GeoJSON,
-                                             engine::api::RouteParameters::OverviewType::False,
-                                             true};
+    engine::api::RouteParameters reference_3{
+        false, false, engine::api::RouteParameters::GeometriesType::GeoJSON,
+        engine::api::RouteParameters::OverviewType::False, true};
     reference_3.coordinates = coords_1;
     auto result_3 = api::parseParameters<engine::api::RouteParameters>(
-        "1,2;3,4?steps=false&alternatives=false&geometries=geojson&overview=false&uturns=true"
-        "false;");
+        "1,2;3,4?steps=false&alternatives=false&geometries=geojson&overview=false&continue_straight=true");
     BOOST_CHECK(result_3);
     BOOST_CHECK_EQUAL(reference_3.steps, result_3->steps);
     BOOST_CHECK_EQUAL(reference_3.alternatives, result_3->alternatives);
     BOOST_CHECK_EQUAL(reference_3.geometries, result_3->geometries);
     BOOST_CHECK_EQUAL(reference_3.overview, result_3->overview);
-    BOOST_CHECK_EQUAL(reference_3.uturns, result_3->uturns);
+    BOOST_CHECK_EQUAL(reference_3.continue_straight, result_3->continue_straight);
     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);
 
     std::vector<boost::optional<engine::Hint>> hints_4 = {
-        engine::Hint::FromBase64(
-            "rVghAzxMzABMAwAA5h4CAKMIAAAQAAAAGAAAAAYAAAAAAAAAch8BAJ4AAACpWCED_0vMAAEAAQGLSzmR"),
-        engine::Hint::FromBase64(
-            "_4ghA4JuzAD_IAAAo28BAOYAAAAzAAAAAgAAAEwAAAAAAAAAdIwAAJ4AAAAXiSEDfm7MAAEAAQGLSzmR"),
-        engine::Hint::FromBase64(
-            "03AhA0vnzAA_SAAA_____3wEAAAYAAAAQAAAAB4AAABAAAAAoUYBAJ4AAADlcCEDSefMAAMAAQGLSzmR")};
+        engine::Hint::FromBase64("DAIAgP___"
+                                 "38AAAAAAAAAAAIAAAAAAAAAEAAAAOgDAAD0AwAAGwAAAOUacQBQP5sCshpxAB0_"
+                                 "mwIAAAEBl-Umfg=="),
+        engine::Hint::FromBase64("cgAAgP___"
+                                 "39jAAAADgAAACIAAABeAAAAkQAAANoDAABOAgAAGwAAAFVGcQCiRJsCR0VxAOZFmw"
+                                 "IFAAEBl-Umfg=="),
+        engine::Hint::FromBase64("3gAAgP___"
+                                 "39KAAAAHgAAACEAAAAAAAAAGAAAAE0BAABOAQAAGwAAAIAzcQBkUJsC1zNxAHBQmw"
+                                 "IAAAEBl-Umfg==")};
     engine::api::RouteParameters reference_4{false,
-                                             true,
+                                             false,
                                              engine::api::RouteParameters::GeometriesType::Polyline,
                                              engine::api::RouteParameters::OverviewType::Simplified,
                                              boost::optional<bool>{},
@@ -188,16 +191,15 @@ BOOST_AUTO_TEST_CASE(valid_route_urls)
                                              std::vector<boost::optional<engine::Bearing>>{}};
     auto result_4 = api::parseParameters<engine::api::RouteParameters>(
         "1,2;3,4?steps=false&hints="
-        "rVghAzxMzABMAwAA5h4CAKMIAAAQAAAAGAAAAAYAAAAAAAAAch8BAJ4AAACpWCED_"
-        "0vMAAEAAQGLSzmR;_4ghA4JuzAD_"
-        "IAAAo28BAOYAAAAzAAAAAgAAAEwAAAAAAAAAdIwAAJ4AAAAXiSEDfm7MAAEAAQGLSzmR;03AhA0vnzAA_SAAA_____"
-        "3wEAAAYAAAAQAAAAB4AAABAAAAAoUYBAJ4AAADlcCEDSefMAAMAAQGLSzmR");
+        "DAIAgP___38AAAAAAAAAAAIAAAAAAAAAEAAAAOgDAAD0AwAAGwAAAOUacQBQP5sCshpxAB0_mwIAAAEBl-Umfg==;"
+        "cgAAgP___39jAAAADgAAACIAAABeAAAAkQAAANoDAABOAgAAGwAAAFVGcQCiRJsCR0VxAOZFmwIFAAEBl-Umfg==;"
+        "3gAAgP___39KAAAAHgAAACEAAAAAAAAAGAAAAE0BAABOAQAAGwAAAIAzcQBkUJsC1zNxAHBQmwIAAAEBl-Umfg==");
     BOOST_CHECK(result_4);
     BOOST_CHECK_EQUAL(reference_4.steps, result_4->steps);
     BOOST_CHECK_EQUAL(reference_4.alternatives, result_4->alternatives);
     BOOST_CHECK_EQUAL(reference_4.geometries, result_4->geometries);
     BOOST_CHECK_EQUAL(reference_4.overview, result_4->overview);
-    BOOST_CHECK_EQUAL(reference_4.uturns, result_4->uturns);
+    BOOST_CHECK_EQUAL(reference_4.continue_straight, result_4->continue_straight);
     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);
@@ -206,7 +208,7 @@ BOOST_AUTO_TEST_CASE(valid_route_urls)
         boost::none, engine::Bearing{200, 10}, engine::Bearing{100, 5},
     };
     engine::api::RouteParameters reference_5{false,
-                                             true,
+                                             false,
                                              engine::api::RouteParameters::GeometriesType::Polyline,
                                              engine::api::RouteParameters::OverviewType::Simplified,
                                              boost::optional<bool>{},
@@ -221,7 +223,7 @@ BOOST_AUTO_TEST_CASE(valid_route_urls)
     BOOST_CHECK_EQUAL(reference_5.alternatives, result_5->alternatives);
     BOOST_CHECK_EQUAL(reference_5.geometries, result_5->geometries);
     BOOST_CHECK_EQUAL(reference_5.overview, result_5->overview);
-    BOOST_CHECK_EQUAL(reference_5.uturns, result_5->uturns);
+    BOOST_CHECK_EQUAL(reference_5.continue_straight, result_5->continue_straight);
     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);
@@ -239,7 +241,7 @@ BOOST_AUTO_TEST_CASE(valid_route_urls)
     BOOST_CHECK_EQUAL(reference_6.alternatives, result_6->alternatives);
     BOOST_CHECK_EQUAL(reference_6.geometries, result_6->geometries);
     BOOST_CHECK_EQUAL(reference_6.overview, result_6->overview);
-    BOOST_CHECK_EQUAL(reference_6.uturns, result_6->uturns);
+    BOOST_CHECK_EQUAL(reference_6.continue_straight, result_6->continue_straight);
     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);
diff --git a/unit_tests/server/url_parser.cpp b/unit_tests/server/url_parser.cpp
index 1405674..cca1f61 100644
--- a/unit_tests/server/url_parser.cpp
+++ b/unit_tests/server/url_parser.cpp
@@ -19,8 +19,8 @@ std::ostream &operator<<(std::ostream &out, const osrm::server::api::ParsedURL &
 }
 }
 
-#include <boost/test/unit_test.hpp>
 #include <boost/test/test_tools.hpp>
+#include <boost/test/unit_test.hpp>
 
 #define CHECK_EQUAL_RANGE(R1, R2)                                                                  \
     BOOST_CHECK_EQUAL_COLLECTIONS(R1.begin(), R1.end(), R2.begin(), R2.end());
@@ -41,12 +41,12 @@ std::size_t testInvalidURL(std::string url)
 
 BOOST_AUTO_TEST_CASE(invalid_urls)
 {
-    BOOST_CHECK_EQUAL(testInvalidURL("/route/"), 0UL);
-    BOOST_CHECK_EQUAL(testInvalidURL("/route/bla"), 0UL);
-    BOOST_CHECK_EQUAL(testInvalidURL("/route/1/1,2;3;4"), 0UL);
-    BOOST_CHECK_EQUAL(testInvalidURL("/route/v1/pro_file/1,2;3,4"), 0UL);
-    BOOST_CHECK_EQUAL(testInvalidURL("/route/v1/profile"), 0UL);
-    BOOST_CHECK_EQUAL(testInvalidURL("/route/v1/profile/"), 0UL);
+    BOOST_CHECK_EQUAL(testInvalidURL("/route/"), 7UL);
+    BOOST_CHECK_EQUAL(testInvalidURL("/route/bla"), 7UL);
+    BOOST_CHECK_EQUAL(testInvalidURL("/route/1/1,2;3;4"), 7UL);
+    BOOST_CHECK_EQUAL(testInvalidURL("/route/v1/pro_file/1,2;3,4"), 13UL);
+    BOOST_CHECK_EQUAL(testInvalidURL("/route/v1/profile"), 17UL);
+    BOOST_CHECK_EQUAL(testInvalidURL("/route/v1/profile/"), 18UL);
 }
 
 BOOST_AUTO_TEST_CASE(valid_urls)
diff --git a/unit_tests/util/coordinate_calculation.cpp b/unit_tests/util/coordinate_calculation.cpp
index d159441..0515a36 100644
--- a/unit_tests/util/coordinate_calculation.cpp
+++ b/unit_tests/util/coordinate_calculation.cpp
@@ -1,4 +1,5 @@
 #include <boost/test/unit_test.hpp>
+#include <boost/numeric/conversion/cast.hpp>
 
 #include "util/coordinate_calculation.hpp"
 
@@ -11,6 +12,130 @@ using namespace osrm::util;
 
 BOOST_AUTO_TEST_SUITE(coordinate_calculation_tests)
 
+BOOST_AUTO_TEST_CASE(compute_angle)
+{
+    // Simple cases
+    // North-South straight line
+    Coordinate first(FloatLongitude(1), FloatLatitude(-1));
+    Coordinate middle(FloatLongitude(1), FloatLatitude(0));
+    Coordinate end(FloatLongitude(1), FloatLatitude(1));
+    auto angle = coordinate_calculation::computeAngle(first, middle, end);
+    BOOST_CHECK_EQUAL(angle, 180);
+
+    // North-South-North u-turn
+    first = Coordinate(FloatLongitude(1), FloatLatitude(0));
+    middle = Coordinate(FloatLongitude(1), FloatLatitude(1));
+    end = Coordinate(FloatLongitude(1), FloatLatitude(0));
+    angle = coordinate_calculation::computeAngle(first, middle, end);
+    BOOST_CHECK_EQUAL(angle, 0);
+
+    // East-west straight lines are harder, *simple* coordinates only
+    // work at the equator.  For other locations, we need to follow
+    // a rhumb line.
+    first = Coordinate(FloatLongitude(1), FloatLatitude(0));
+    middle = Coordinate(FloatLongitude(2), FloatLatitude(0));
+    end = Coordinate(FloatLongitude(3), FloatLatitude(0));
+    angle = coordinate_calculation::computeAngle(first, middle, end);
+    BOOST_CHECK_EQUAL(angle, 180);
+
+    // East-West-East u-turn
+    first = Coordinate(FloatLongitude(1), FloatLatitude(0));
+    middle = Coordinate(FloatLongitude(2), FloatLatitude(0));
+    end = Coordinate(FloatLongitude(1), FloatLatitude(0));
+    angle = coordinate_calculation::computeAngle(first, middle, end);
+    BOOST_CHECK_EQUAL(angle, 0);
+
+    // 90 degree left turn
+    first = Coordinate(FloatLongitude(1), FloatLatitude(1));
+    middle = Coordinate(FloatLongitude(0), FloatLatitude(1));
+    end = Coordinate(FloatLongitude(0), FloatLatitude(2));
+    angle = coordinate_calculation::computeAngle(first, middle, end);
+    BOOST_CHECK_EQUAL(angle, 90);
+
+    // 90 degree right turn
+    first = Coordinate(FloatLongitude(1), FloatLatitude(1));
+    middle = Coordinate(FloatLongitude(0), FloatLatitude(1));
+    end = Coordinate(FloatLongitude(0), FloatLatitude(0));
+    angle = coordinate_calculation::computeAngle(first, middle, end);
+    BOOST_CHECK_EQUAL(angle, 270);
+
+    // Weird cases
+    // Crossing both the meridians
+    first = Coordinate(FloatLongitude(-1), FloatLatitude(-1));
+    middle = Coordinate(FloatLongitude(0), FloatLatitude(1));
+    end = Coordinate(FloatLongitude(1), FloatLatitude(-1));
+    angle = coordinate_calculation::computeAngle(first, middle, end);
+    BOOST_CHECK_CLOSE(angle, 53.1, 0.2);
+
+    // All coords in the same spot
+    first = Coordinate(FloatLongitude(-1), FloatLatitude(-1));
+    middle = Coordinate(FloatLongitude(-1), FloatLatitude(-1));
+    end = Coordinate(FloatLongitude(-1), FloatLatitude(-1));
+    angle = coordinate_calculation::computeAngle(first, middle, end);
+    BOOST_CHECK_EQUAL(angle, 180);
+
+    // First two coords in the same spot, then heading north-east
+    first = Coordinate(FloatLongitude(-1), FloatLatitude(-1));
+    middle = Coordinate(FloatLongitude(-1), FloatLatitude(-1));
+    end = Coordinate(FloatLongitude(1), FloatLatitude(1));
+    angle = coordinate_calculation::computeAngle(first, middle, end);
+    BOOST_CHECK_EQUAL(angle, 180);
+
+    // First two coords in the same spot, then heading west
+    first = Coordinate(FloatLongitude(1), FloatLatitude(1));
+    middle = Coordinate(FloatLongitude(1), FloatLatitude(1));
+    end = Coordinate(FloatLongitude(2), FloatLatitude(1));
+    angle = coordinate_calculation::computeAngle(first, middle, end);
+    BOOST_CHECK_EQUAL(angle, 180);
+
+    // First two coords in the same spot then heading north
+    first = Coordinate(FloatLongitude(1), FloatLatitude(1));
+    middle = Coordinate(FloatLongitude(1), FloatLatitude(1));
+    end = Coordinate(FloatLongitude(1), FloatLatitude(2));
+    angle = coordinate_calculation::computeAngle(first, middle, end);
+    BOOST_CHECK_EQUAL(angle, 180);
+
+    // Second two coords in the same spot
+    first = Coordinate(FloatLongitude(1), FloatLatitude(1));
+    middle = Coordinate(FloatLongitude(-1), FloatLatitude(-1));
+    end = Coordinate(FloatLongitude(-1), FloatLatitude(-1));
+    angle = coordinate_calculation::computeAngle(first, middle, end);
+    BOOST_CHECK_EQUAL(angle, 180);
+
+    // First and last coords on the same spot
+    first = Coordinate(FloatLongitude(1), FloatLatitude(1));
+    middle = Coordinate(FloatLongitude(-1), FloatLatitude(-1));
+    end = Coordinate(FloatLongitude(1), FloatLatitude(1));
+    angle = coordinate_calculation::computeAngle(first, middle, end);
+    BOOST_CHECK_EQUAL(angle, 0);
+
+    // Check the antimeridian
+    first = Coordinate(FloatLongitude(180), FloatLatitude(90));
+    middle = Coordinate(FloatLongitude(180), FloatLatitude(0));
+    end = Coordinate(FloatLongitude(180), FloatLatitude(-90));
+    angle = coordinate_calculation::computeAngle(first, middle, end);
+    BOOST_CHECK_EQUAL(angle, 180);
+
+    // Tiny changes below our calculation resolution
+    // This should be equivalent to having two points on the same
+    // spot.
+    first = Coordinate(FloatLongitude(0), FloatLatitude(0));
+    middle = Coordinate(FloatLongitude(1), FloatLatitude(0));
+    end = Coordinate(FloatLongitude(1 + std::numeric_limits<double>::epsilon()), FloatLatitude(0));
+    angle = coordinate_calculation::computeAngle(first, middle, end);
+    BOOST_CHECK_EQUAL(angle, 180);
+
+    // Invalid values
+    /* TODO: Enable this when I figure out how to use BOOST_CHECK_THROW
+     *       and not have the whole test case fail...
+    first = Coordinate(FloatLongitude(0), FloatLatitude(0));
+    middle = Coordinate(FloatLongitude(1), FloatLatitude(0));
+    end = Coordinate(FloatLongitude(std::numeric_limits<double>::max()), FloatLatitude(0));
+    BOOST_CHECK_THROW( coordinate_calculation::computeAngle(first,middle,end),
+                       boost::numeric::positive_overflow);
+                       */
+}
+
 // Regression test for bug captured in #1347
 BOOST_AUTO_TEST_CASE(regression_test_1347)
 {
@@ -27,72 +152,6 @@ BOOST_AUTO_TEST_CASE(regression_test_1347)
     BOOST_CHECK_LE(std::abs(d1 - d2), 0.01);
 }
 
-BOOST_AUTO_TEST_CASE(lon_to_pixel)
-{
-    using namespace coordinate_calculation;
-    BOOST_CHECK_CLOSE(7.416042 * mercator::DEGREE_TO_PX, 825550.019142, 0.1);
-    BOOST_CHECK_CLOSE(7.415892 * mercator::DEGREE_TO_PX, 825533.321218, 0.1);
-    BOOST_CHECK_CLOSE(7.416016 * mercator::DEGREE_TO_PX, 825547.124835, 0.1);
-    BOOST_CHECK_CLOSE(7.41577 * mercator::DEGREE_TO_PX, 825519.74024, 0.1);
-    BOOST_CHECK_CLOSE(7.415808 * mercator::DEGREE_TO_PX, 825523.970381, 0.1);
-}
-
-BOOST_AUTO_TEST_CASE(lat_to_pixel)
-{
-    using namespace coordinate_calculation;
-    BOOST_CHECK_CLOSE(mercator::latToY(util::FloatLatitude(43.733947)) * mercator::DEGREE_TO_PX,
-                      5424361.75863, 0.1);
-    BOOST_CHECK_CLOSE(mercator::latToY(util::FloatLatitude(43.733799)) * mercator::DEGREE_TO_PX,
-                      5424338.95731, 0.1);
-    BOOST_CHECK_CLOSE(mercator::latToY(util::FloatLatitude(43.733922)) * mercator::DEGREE_TO_PX,
-                      5424357.90705, 0.1);
-    BOOST_CHECK_CLOSE(mercator::latToY(util::FloatLatitude(43.733697)) * mercator::DEGREE_TO_PX,
-                      5424323.24293, 0.1);
-    BOOST_CHECK_CLOSE(mercator::latToY(util::FloatLatitude(43.733729)) * mercator::DEGREE_TO_PX,
-                      5424328.17293, 0.1);
-}
-
-BOOST_AUTO_TEST_CASE(xyz_to_wgs84)
-{
-    using namespace coordinate_calculation;
-
-    double minx_1;
-    double miny_1;
-    double maxx_1;
-    double maxy_1;
-    mercator::xyzToWGS84(2, 2, 1, minx_1, miny_1, maxx_1, maxy_1);
-    BOOST_CHECK_CLOSE(minx_1, 180, 0.0001);
-    BOOST_CHECK_CLOSE(miny_1, -85.0511, 0.0001);
-    BOOST_CHECK_CLOSE(maxx_1, 360, 0.0001);
-    BOOST_CHECK_CLOSE(maxy_1, -85.0511, 0.0001);
-
-    double minx_2;
-    double miny_2;
-    double maxx_2;
-    double maxy_2;
-    mercator::xyzToWGS84(100, 0, 13, minx_2, miny_2, maxx_2, maxy_2);
-    BOOST_CHECK_CLOSE(minx_2, -175.6054, 0.0001);
-    BOOST_CHECK_CLOSE(miny_2, 85.0473, 0.0001);
-    BOOST_CHECK_CLOSE(maxx_2, -175.5615, 0.0001);
-    BOOST_CHECK_CLOSE(maxy_2, 85.0511, 0.0001);
-}
-
-BOOST_AUTO_TEST_CASE(xyz_to_mercator)
-{
-    using namespace coordinate_calculation;
-
-    double minx;
-    double miny;
-    double maxx;
-    double maxy;
-    mercator::xyzToMercator(100, 0, 13, minx, miny, maxx, maxy);
-
-    BOOST_CHECK_CLOSE(minx, -19548311.361764118075, 0.0001);
-    BOOST_CHECK_CLOSE(miny, 20032616.372979003936, 0.0001);
-    BOOST_CHECK_CLOSE(maxx, -19543419.391953866929, 0.0001);
-    BOOST_CHECK_CLOSE(maxy, 20037508.342789277434, 0.0001);
-}
-
 BOOST_AUTO_TEST_CASE(regression_point_on_segment)
 {
     //  ^
@@ -120,8 +179,10 @@ BOOST_AUTO_TEST_CASE(regression_point_on_segment)
 
     FloatCoordinate diff{target.lon - start.lon, target.lat - start.lat};
 
-    BOOST_CHECK_CLOSE(static_cast<double>(start.lon + FloatLongitude(ratio) * diff.lon), static_cast<double>(nearest.lon), 0.1);
-    BOOST_CHECK_CLOSE(static_cast<double>(start.lat + FloatLatitude(ratio) * diff.lat), static_cast<double>(nearest.lat), 0.1);
+    BOOST_CHECK_CLOSE(static_cast<double>(start.lon + FloatLongitude(ratio) * diff.lon),
+                      static_cast<double>(nearest.lon), 0.1);
+    BOOST_CHECK_CLOSE(static_cast<double>(start.lat + FloatLatitude(ratio) * diff.lat),
+                      static_cast<double>(nearest.lat), 0.1);
 }
 
 BOOST_AUTO_TEST_CASE(point_on_segment)
@@ -188,4 +249,53 @@ BOOST_AUTO_TEST_CASE(point_on_segment)
     BOOST_CHECK_EQUAL(result_4.second.lat, reference_point_4.lat);
 }
 
+BOOST_AUTO_TEST_CASE(circleCenter)
+{
+    Coordinate a(FloatLongitude(-100.), FloatLatitude(10.));
+    Coordinate b(FloatLongitude(-100.002), FloatLatitude(10.001));
+    Coordinate c(FloatLongitude(-100.001), FloatLatitude(10.002));
+
+    auto result = coordinate_calculation::circleCenter(a, b, c);
+    BOOST_CHECK(result);
+    BOOST_CHECK_EQUAL(*result, Coordinate(FloatLongitude(-100.000833), FloatLatitude(10.000833)));
+
+    // Co-linear longitude
+    a = Coordinate(FloatLongitude(-100.), FloatLatitude(10.));
+    b = Coordinate(FloatLongitude(-100.001), FloatLatitude(10.001));
+    c = Coordinate(FloatLongitude(-100.001), FloatLatitude(10.002));
+    result = coordinate_calculation::circleCenter(a, b, c);
+    BOOST_CHECK(result);
+    BOOST_CHECK_EQUAL(*result, Coordinate(FloatLongitude(-99.9995), FloatLatitude(10.0015)));
+
+    // Co-linear longitude, impossible to calculate
+    a = Coordinate(FloatLongitude(-100.001), FloatLatitude(10.));
+    b = Coordinate(FloatLongitude(-100.001), FloatLatitude(10.001));
+    c = Coordinate(FloatLongitude(-100.001), FloatLatitude(10.002));
+    result = coordinate_calculation::circleCenter(a, b, c);
+    BOOST_CHECK(!result);
+
+    // Co-linear latitude, this is a real case that failed
+    a = Coordinate(FloatLongitude(-112.096234), FloatLatitude(41.147101));
+    b = Coordinate(FloatLongitude(-112.096606), FloatLatitude(41.147101));
+    c = Coordinate(FloatLongitude(-112.096419), FloatLatitude(41.147259));
+    result = coordinate_calculation::circleCenter(a, b, c);
+    BOOST_CHECK(result);
+    BOOST_CHECK_EQUAL(*result, Coordinate(FloatLongitude(-112.09642), FloatLatitude(41.14707)));
+
+    // Co-linear latitude, variation
+    a = Coordinate(FloatLongitude(-112.096234), FloatLatitude(41.147101));
+    b = Coordinate(FloatLongitude(-112.096606), FloatLatitude(41.147259));
+    c = Coordinate(FloatLongitude(-112.096419), FloatLatitude(41.147259));
+    result = coordinate_calculation::circleCenter(a, b, c);
+    BOOST_CHECK(result);
+    BOOST_CHECK_EQUAL(*result, Coordinate(FloatLongitude(-112.096512), FloatLatitude(41.146962)));
+
+    // Co-linear latitude, impossible to calculate
+    a = Coordinate(FloatLongitude(-112.096234), FloatLatitude(41.147259));
+    b = Coordinate(FloatLongitude(-112.096606), FloatLatitude(41.147259));
+    c = Coordinate(FloatLongitude(-112.096419), FloatLatitude(41.147259));
+    result = coordinate_calculation::circleCenter(a, b, c);
+    BOOST_CHECK(!result);
+}
+
 BOOST_AUTO_TEST_SUITE_END()
diff --git a/unit_tests/util/rectangle.cpp b/unit_tests/util/rectangle.cpp
index f85d073..c68fbda 100644
--- a/unit_tests/util/rectangle.cpp
+++ b/unit_tests/util/rectangle.cpp
@@ -43,14 +43,14 @@ BOOST_AUTO_TEST_CASE(get_min_dist_test)
     Coordinate nw_e{FloatLongitude(-9.9), FloatLatitude(45.0)};
     Coordinate nw_w{FloatLongitude(-100.1), FloatLatitude(45.0)};
     Coordinate nw_n{FloatLongitude(-55), FloatLatitude(80.1)};
-    BOOST_CHECK_CLOSE(nw.GetMinSquaredDist(nw_sw), 0.02, 0.1);
-    BOOST_CHECK_CLOSE(nw.GetMinSquaredDist(nw_se), 0.02, 0.1);
-    BOOST_CHECK_CLOSE(nw.GetMinSquaredDist(nw_ne), 0.02, 0.1);
-    BOOST_CHECK_CLOSE(nw.GetMinSquaredDist(nw_nw), 0.02, 0.1);
-    BOOST_CHECK_CLOSE(nw.GetMinSquaredDist(nw_s),  0.01, 0.1);
-    BOOST_CHECK_CLOSE(nw.GetMinSquaredDist(nw_e),  0.01, 0.1);
-    BOOST_CHECK_CLOSE(nw.GetMinSquaredDist(nw_w),  0.01, 0.1);
-    BOOST_CHECK_CLOSE(nw.GetMinSquaredDist(nw_n),  0.01, 0.1);
+    BOOST_CHECK_CLOSE(nw.GetMinSquaredDist(nw_sw), 0.02 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
+    BOOST_CHECK_CLOSE(nw.GetMinSquaredDist(nw_se), 0.02 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
+    BOOST_CHECK_CLOSE(nw.GetMinSquaredDist(nw_ne), 0.02 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
+    BOOST_CHECK_CLOSE(nw.GetMinSquaredDist(nw_nw), 0.02 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
+    BOOST_CHECK_CLOSE(nw.GetMinSquaredDist(nw_s),  0.01 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
+    BOOST_CHECK_CLOSE(nw.GetMinSquaredDist(nw_e),  0.01 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
+    BOOST_CHECK_CLOSE(nw.GetMinSquaredDist(nw_w),  0.01 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
+    BOOST_CHECK_CLOSE(nw.GetMinSquaredDist(nw_n),  0.01 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
 
     Coordinate ne_sw{FloatLongitude(9.9), FloatLatitude(9.9)};
     Coordinate ne_se{FloatLongitude(100.1), FloatLatitude(9.9)};
@@ -60,14 +60,14 @@ BOOST_AUTO_TEST_CASE(get_min_dist_test)
     Coordinate ne_e{FloatLongitude(100.1), FloatLatitude(45.0)};
     Coordinate ne_w{FloatLongitude(9.9), FloatLatitude(45.0)};
     Coordinate ne_n{FloatLongitude(55), FloatLatitude(80.1)};
-    BOOST_CHECK_CLOSE(ne.GetMinSquaredDist(ne_sw), 0.02, 0.1);
-    BOOST_CHECK_CLOSE(ne.GetMinSquaredDist(ne_se), 0.02, 0.1);
-    BOOST_CHECK_CLOSE(ne.GetMinSquaredDist(ne_ne), 0.02, 0.1);
-    BOOST_CHECK_CLOSE(ne.GetMinSquaredDist(ne_nw), 0.02, 0.1);
-    BOOST_CHECK_CLOSE(ne.GetMinSquaredDist(ne_s),  0.01, 0.1);
-    BOOST_CHECK_CLOSE(ne.GetMinSquaredDist(ne_e),  0.01, 0.1);
-    BOOST_CHECK_CLOSE(ne.GetMinSquaredDist(ne_w),  0.01, 0.1);
-    BOOST_CHECK_CLOSE(ne.GetMinSquaredDist(ne_n),  0.01, 0.1);
+    BOOST_CHECK_CLOSE(ne.GetMinSquaredDist(ne_sw), 0.02 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
+    BOOST_CHECK_CLOSE(ne.GetMinSquaredDist(ne_se), 0.02 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
+    BOOST_CHECK_CLOSE(ne.GetMinSquaredDist(ne_ne), 0.02 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
+    BOOST_CHECK_CLOSE(ne.GetMinSquaredDist(ne_nw), 0.02 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
+    BOOST_CHECK_CLOSE(ne.GetMinSquaredDist(ne_s),  0.01 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
+    BOOST_CHECK_CLOSE(ne.GetMinSquaredDist(ne_e),  0.01 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
+    BOOST_CHECK_CLOSE(ne.GetMinSquaredDist(ne_w),  0.01 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
+    BOOST_CHECK_CLOSE(ne.GetMinSquaredDist(ne_n),  0.01 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
 
     Coordinate se_ne{FloatLongitude(100.1), FloatLatitude(-9.9)};
     Coordinate se_nw{FloatLongitude(9.9), FloatLatitude(-9.9)};
@@ -77,14 +77,14 @@ BOOST_AUTO_TEST_CASE(get_min_dist_test)
     Coordinate se_w{FloatLongitude(9.9), FloatLatitude(-45.0)};
     Coordinate se_e{FloatLongitude(100.1), FloatLatitude(-45.0)};
     Coordinate se_s{FloatLongitude(55), FloatLatitude(-80.1)};
-    BOOST_CHECK_CLOSE(se.GetMinSquaredDist(se_sw), 0.02, 0.1);
-    BOOST_CHECK_CLOSE(se.GetMinSquaredDist(se_se), 0.02, 0.1);
-    BOOST_CHECK_CLOSE(se.GetMinSquaredDist(se_ne), 0.02, 0.1);
-    BOOST_CHECK_CLOSE(se.GetMinSquaredDist(se_nw), 0.02, 0.1);
-    BOOST_CHECK_CLOSE(se.GetMinSquaredDist(se_s),  0.01, 0.1);
-    BOOST_CHECK_CLOSE(se.GetMinSquaredDist(se_e),  0.01, 0.1);
-    BOOST_CHECK_CLOSE(se.GetMinSquaredDist(se_w),  0.01, 0.1);
-    BOOST_CHECK_CLOSE(se.GetMinSquaredDist(se_n),  0.01, 0.1);
+    BOOST_CHECK_CLOSE(se.GetMinSquaredDist(se_sw), 0.02 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
+    BOOST_CHECK_CLOSE(se.GetMinSquaredDist(se_se), 0.02 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
+    BOOST_CHECK_CLOSE(se.GetMinSquaredDist(se_ne), 0.02 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
+    BOOST_CHECK_CLOSE(se.GetMinSquaredDist(se_nw), 0.02 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
+    BOOST_CHECK_CLOSE(se.GetMinSquaredDist(se_s),  0.01 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
+    BOOST_CHECK_CLOSE(se.GetMinSquaredDist(se_e),  0.01 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
+    BOOST_CHECK_CLOSE(se.GetMinSquaredDist(se_w),  0.01 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
+    BOOST_CHECK_CLOSE(se.GetMinSquaredDist(se_n),  0.01 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
 
     Coordinate sw_ne{FloatLongitude(-9.9), FloatLatitude(-9.9)};
     Coordinate sw_nw{FloatLongitude(-100.1), FloatLatitude(-9.9)};
@@ -94,14 +94,14 @@ BOOST_AUTO_TEST_CASE(get_min_dist_test)
     Coordinate sw_w{FloatLongitude(-100.1), FloatLatitude(-45.0)};
     Coordinate sw_e{FloatLongitude(-9.9), FloatLatitude(-45.0)};
     Coordinate sw_s{FloatLongitude(-55), FloatLatitude(-80.1)};
-    BOOST_CHECK_CLOSE(sw.GetMinSquaredDist(sw_sw), 0.02, 0.1);
-    BOOST_CHECK_CLOSE(sw.GetMinSquaredDist(sw_se), 0.02, 0.1);
-    BOOST_CHECK_CLOSE(sw.GetMinSquaredDist(sw_ne), 0.02, 0.1);
-    BOOST_CHECK_CLOSE(sw.GetMinSquaredDist(sw_nw), 0.02, 0.1);
-    BOOST_CHECK_CLOSE(sw.GetMinSquaredDist(sw_s),  0.01, 0.1);
-    BOOST_CHECK_CLOSE(sw.GetMinSquaredDist(sw_e),  0.01, 0.1);
-    BOOST_CHECK_CLOSE(sw.GetMinSquaredDist(sw_w),  0.01, 0.1);
-    BOOST_CHECK_CLOSE(sw.GetMinSquaredDist(sw_n),  0.01, 0.1);
+    BOOST_CHECK_CLOSE(sw.GetMinSquaredDist(sw_sw), 0.02 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
+    BOOST_CHECK_CLOSE(sw.GetMinSquaredDist(sw_se), 0.02 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
+    BOOST_CHECK_CLOSE(sw.GetMinSquaredDist(sw_ne), 0.02 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
+    BOOST_CHECK_CLOSE(sw.GetMinSquaredDist(sw_nw), 0.02 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
+    BOOST_CHECK_CLOSE(sw.GetMinSquaredDist(sw_s),  0.01 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
+    BOOST_CHECK_CLOSE(sw.GetMinSquaredDist(sw_e),  0.01 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
+    BOOST_CHECK_CLOSE(sw.GetMinSquaredDist(sw_w),  0.01 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
+    BOOST_CHECK_CLOSE(sw.GetMinSquaredDist(sw_n),  0.01 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
 }
 
 BOOST_AUTO_TEST_SUITE_END()
diff --git a/unit_tests/util/static_rtree.cpp b/unit_tests/util/static_rtree.cpp
index a9170be..b3c2c9e 100644
--- a/unit_tests/util/static_rtree.cpp
+++ b/unit_tests/util/static_rtree.cpp
@@ -1,28 +1,28 @@
-#include "extractor/edge_based_node.hpp"
 #include "engine/geospatial_query.hpp"
-#include "util/typedefs.hpp"
-#include "util/rectangle.hpp"
-#include "util/exception.hpp"
-#include "util/coordinate_calculation.hpp"
+#include "extractor/edge_based_node.hpp"
 #include "util/coordinate.hpp"
+#include "util/coordinate_calculation.hpp"
+#include "util/exception.hpp"
+#include "util/rectangle.hpp"
 #include "util/static_rtree.hpp"
+#include "util/typedefs.hpp"
 
 #include "mocks/mock_datafacade.hpp"
 
-#include <boost/test/unit_test.hpp>
+#include <boost/functional/hash.hpp>
 #include <boost/test/auto_unit_test.hpp>
 #include <boost/test/test_case_template.hpp>
-#include <boost/functional/hash.hpp>
+#include <boost/test/unit_test.hpp>
 
-#include <cstdint>
 #include <cmath>
+#include <cstdint>
 
 #include <algorithm>
 #include <memory>
 #include <random>
 #include <string>
-#include <utility>
 #include <unordered_set>
+#include <utility>
 #include <vector>
 
 BOOST_AUTO_TEST_SUITE(static_rtree)
@@ -62,10 +62,10 @@ template <typename DataT> class LinearSearchNN
     {
         std::vector<DataT> local_edges(edges);
 
-        auto projected_input = coordinate_calculation::mercator::fromWGS84(input_coordinate);
-        const auto segment_comparator = [this, &projected_input](const DataT &lhs, const DataT &rhs)
-        {
-            using coordinate_calculation::mercator::fromWGS84;
+        auto projected_input = web_mercator::fromWGS84(input_coordinate);
+        const auto segment_comparator = [this, &projected_input](const DataT &lhs,
+                                                                 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);
             const auto rhs_result = coordinate_calculation::projectPointOnSegment(
@@ -230,13 +230,10 @@ void sampling_verify_rtree(RTreeT &rtree,
         auto lsnn_u = result_lsnn.back().u;
         auto lsnn_v = result_lsnn.back().v;
 
-        Coordinate rtree_nearest;
-        Coordinate lsnn_nearest;
-        double ratio;
         const double rtree_dist = coordinate_calculation::perpendicularDistance(
-            coords[rtree_u], coords[rtree_v], q, rtree_nearest, ratio);
+            coords[rtree_u], coords[rtree_v], q);
         const double lsnn_dist = coordinate_calculation::perpendicularDistance(
-            coords[lsnn_u], coords[lsnn_v], q, lsnn_nearest, ratio);
+            coords[lsnn_u], coords[lsnn_v], q);
 
         BOOST_CHECK_CLOSE(rtree_dist, lsnn_dist, 0.0001);
     }
@@ -331,13 +328,6 @@ BOOST_AUTO_TEST_CASE(regression_test)
     auto result_rtree = rtree.Nearest(input, 1);
     auto result_ls = lsnn.Nearest(input, 1);
 
-    auto distance_rtree = coordinate_calculation::perpendicularDistance(
-        fixture.coords->at(result_rtree.front().u), fixture.coords->at(result_rtree.front().v),
-        input);
-
-    auto distance_lsnn = coordinate_calculation::perpendicularDistance(
-        fixture.coords->at(result_ls.front().u), fixture.coords->at(result_ls.front().v), input);
-
     BOOST_CHECK(result_rtree.size() == 1);
     BOOST_CHECK(result_ls.size() == 1);
 
diff --git a/unit_tests/util/web_mercator.cpp b/unit_tests/util/web_mercator.cpp
new file mode 100644
index 0000000..7b00396
--- /dev/null
+++ b/unit_tests/util/web_mercator.cpp
@@ -0,0 +1,74 @@
+#include <boost/test/unit_test.hpp>
+
+#include "util/web_mercator.hpp"
+
+#include <osrm/coordinate.hpp>
+
+#include <cmath>
+
+using namespace osrm;
+using namespace osrm::util;
+
+BOOST_AUTO_TEST_SUITE(web_mercator_tests)
+
+BOOST_AUTO_TEST_CASE(lon_to_pixel)
+{
+    BOOST_CHECK_CLOSE(7.416042 * web_mercator::DEGREE_TO_PX, 825550.019142, 0.1);
+    BOOST_CHECK_CLOSE(7.415892 * web_mercator::DEGREE_TO_PX, 825533.321218, 0.1);
+    BOOST_CHECK_CLOSE(7.416016 * web_mercator::DEGREE_TO_PX, 825547.124835, 0.1);
+    BOOST_CHECK_CLOSE(7.41577 * web_mercator::DEGREE_TO_PX, 825519.74024, 0.1);
+    BOOST_CHECK_CLOSE(7.415808 * web_mercator::DEGREE_TO_PX, 825523.970381, 0.1);
+}
+
+BOOST_AUTO_TEST_CASE(lat_to_pixel)
+{
+    BOOST_CHECK_CLOSE(web_mercator::latToY(util::FloatLatitude(43.733947)) * web_mercator::DEGREE_TO_PX,
+                      5424361.75863, 0.1);
+    BOOST_CHECK_CLOSE(web_mercator::latToY(util::FloatLatitude(43.733799)) * web_mercator::DEGREE_TO_PX,
+                      5424338.95731, 0.1);
+    BOOST_CHECK_CLOSE(web_mercator::latToY(util::FloatLatitude(43.733922)) * web_mercator::DEGREE_TO_PX,
+                      5424357.90705, 0.1);
+    BOOST_CHECK_CLOSE(web_mercator::latToY(util::FloatLatitude(43.733697)) * web_mercator::DEGREE_TO_PX,
+                      5424323.24293, 0.1);
+    BOOST_CHECK_CLOSE(web_mercator::latToY(util::FloatLatitude(43.733729)) * web_mercator::DEGREE_TO_PX,
+                      5424328.17293, 0.1);
+}
+
+BOOST_AUTO_TEST_CASE(xyz_to_wgs84)
+{
+    double minx_1;
+    double miny_1;
+    double maxx_1;
+    double maxy_1;
+    web_mercator::xyzToWGS84(2, 2, 1, minx_1, miny_1, maxx_1, maxy_1);
+    BOOST_CHECK_CLOSE(minx_1, 180, 0.0001);
+    BOOST_CHECK_CLOSE(miny_1, -85.0511, 0.0001);
+    BOOST_CHECK_CLOSE(maxx_1, 360, 0.0001);
+    BOOST_CHECK_CLOSE(maxy_1, -85.0511, 0.0001);
+
+    double minx_2;
+    double miny_2;
+    double maxx_2;
+    double maxy_2;
+    web_mercator::xyzToWGS84(100, 0, 13, minx_2, miny_2, maxx_2, maxy_2);
+    BOOST_CHECK_CLOSE(minx_2, -175.6054, 0.0001);
+    BOOST_CHECK_CLOSE(miny_2, 85.0473, 0.0001);
+    BOOST_CHECK_CLOSE(maxx_2, -175.5615, 0.0001);
+    BOOST_CHECK_CLOSE(maxy_2, 85.0511, 0.0001);
+}
+
+BOOST_AUTO_TEST_CASE(xyz_to_mercator)
+{
+    double minx;
+    double miny;
+    double maxx;
+    double maxy;
+    web_mercator::xyzToMercator(100, 0, 13, minx, miny, maxx, maxy);
+
+    BOOST_CHECK_CLOSE(minx, -19548311.361764118075, 0.0001);
+    BOOST_CHECK_CLOSE(miny, 19971868.8804085782, 0.0001);
+    BOOST_CHECK_CLOSE(maxx, -19543419.391953866929, 0.0001);
+    BOOST_CHECK_CLOSE(maxy, 19971868.880408578, 0.0001);
+}
+
+BOOST_AUTO_TEST_SUITE_END()

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