[osrm] 01/04: Imported Upstream version 5.3.0+ds

Bas Couwenberg sebastic at debian.org
Sat Jul 23 10:16:34 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 1df4402eb39ee00523bbecc3dca4d6f0c1359114
Author: Bas Couwenberg <sebastic at xs4all.nl>
Date:   Sat Jul 23 11:42:34 2016 +0200

    Imported Upstream version 5.3.0+ds
---
 .clang-format                                      |   2 +-
 .travis.yml                                        |  16 +-
 CHANGELOG.md                                       |  63 +-
 CMakeLists.txt                                     |  11 +-
 CONTRIBUTING.md                                    |  55 ++
 README.md                                          |   4 +-
 docker/Dockerfile                                  |  17 +-
 docker/run-gcc.sh                                  |   3 +-
 docs/http.md                                       | 138 ++--
 example/example.cpp                                |   4 +-
 features/bicycle/mode.feature                      |   7 +-
 features/bicycle/oneway.feature                    |   6 +-
 features/bicycle/pushing.feature                   |   6 +-
 features/bicycle/surface.feature                   |  17 +
 features/car/oneway.feature                        |   6 +-
 features/guidance/anticipate-lanes.feature         | 500 ++++++++++++++
 features/guidance/collapse.feature                 |  18 +-
 features/guidance/dedicated-turn-roads.feature     |  64 +-
 features/guidance/end-of-road.feature              | 124 ++--
 features/guidance/fork.feature                     |  18 +
 features/guidance/intersections.feature            |  59 +-
 features/guidance/merge.feature                    |  24 +
 features/guidance/motorway.feature                 |  18 +
 features/guidance/post-processing.feature          |  22 +
 features/guidance/roundabout.feature               |  39 ++
 features/guidance/turn-lanes.feature               | 739 +++++++++++++++++++++
 features/guidance/turn.feature                     |  46 ++
 features/step_definitions/hooks.js                 |  12 -
 features/support/data.js                           |   4 +-
 features/support/env.js                            |   2 +-
 features/support/hooks.js                          |   5 -
 features/support/route.js                          |  11 +
 features/support/shared_steps.js                   |   8 +-
 features/testbot/basic.feature                     |  38 +-
 features/testbot/bearing.feature                   |  20 +-
 features/testbot/bearing_param.feature             |  38 +-
 features/testbot/continue_straight.feature         |   2 +-
 features/testbot/loop.feature                      |   8 +-
 features/testbot/oneway.feature                    |   7 +-
 features/testbot/summary.feature                   |   2 +-
 features/testbot/time.feature                      |   6 +-
 features/testbot/via.feature                       |  18 +-
 include/contractor/contractor.hpp                  |   2 +-
 include/contractor/crc32_processor.hpp             |  10 +-
 include/engine/api/route_api.hpp                   |   3 +
 include/engine/datafacade/datafacade_base.hpp      |   6 +
 include/engine/datafacade/internal_datafacade.hpp  |  65 +-
 include/engine/datafacade/shared_datafacade.hpp    |  62 +-
 include/engine/guidance/assemble_geometry.hpp      |  10 +-
 include/engine/guidance/assemble_steps.hpp         |  36 +-
 include/engine/guidance/lane_processing.hpp        |  27 +
 include/engine/guidance/post_processing.hpp        |   9 +
 include/engine/guidance/route_step.hpp             |  11 +-
 include/engine/guidance/step_maneuver.hpp          |   2 +
 include/engine/guidance/toolkit.hpp                |  68 +-
 include/engine/internal_route_result.hpp           |   6 +-
 .../engine/routing_algorithms/alternative_path.hpp |   4 +-
 include/engine/routing_algorithms/routing_base.hpp |   5 +
 include/extractor/edge_based_graph_factory.hpp     |  60 +-
 include/extractor/external_memory_node.hpp         |   8 +-
 include/extractor/extraction_containers.hpp        |  18 +-
 include/extractor/extraction_helper_functions.hpp  |   8 +
 include/extractor/extraction_way.hpp               |   5 +
 include/extractor/extractor.hpp                    |   4 +-
 include/extractor/extractor_callbacks.hpp          |   3 +
 include/extractor/extractor_config.hpp             |   4 +
 include/extractor/guidance/classification_data.hpp |   6 +
 include/extractor/guidance/intersection.hpp        |   4 +
 .../extractor/guidance/intersection_generator.hpp  |   1 +
 include/extractor/guidance/motorway_handler.hpp    |   6 +-
 include/extractor/guidance/toolkit.hpp             |  59 +-
 include/extractor/guidance/turn_analysis.hpp       |  10 +-
 include/extractor/guidance/turn_discovery.hpp      |  38 ++
 include/extractor/guidance/turn_instruction.hpp    |  41 +-
 .../extractor/guidance/turn_lane_augmentation.hpp  |  24 +
 include/extractor/guidance/turn_lane_data.hpp      |  44 ++
 include/extractor/guidance/turn_lane_handler.hpp   |  85 +++
 include/extractor/guidance/turn_lane_matcher.hpp   |  51 ++
 include/extractor/guidance/turn_lane_types.hpp     | 102 +++
 include/extractor/internal_extractor_edge.hpp      |  47 +-
 include/extractor/node_based_edge.hpp              |  10 +-
 include/extractor/original_edge_data.hpp           |   9 +-
 include/extractor/query_node.hpp                   |  17 +-
 include/server/api/base_parameters_grammar.hpp     |  18 +-
 include/storage/shared_datatype.hpp                |  62 +-
 include/storage/storage_config.hpp                 |   2 +
 include/util/assert.hpp                            |  20 -
 include/util/coordinate.hpp                        |  12 +-
 include/util/coordinate_calculation.hpp            |   8 +-
 include/util/debug.hpp                             |  91 +++
 include/util/dynamic_graph.hpp                     |   4 +-
 include/util/group_by.hpp                          |  40 ++
 include/util/guidance/entry_class.hpp              |   7 +
 include/util/guidance/toolkit.hpp                  |  64 ++
 include/util/guidance/turn_lanes.hpp               |  81 +++
 include/util/io.hpp                                |  80 +++
 include/util/node_based_graph.hpp                  |  15 +-
 include/util/packed_vector.hpp                     |  13 +-
 include/util/rectangle.hpp                         |  28 +-
 include/util/static_rtree.hpp                      |   4 +-
 include/util/strong_typedef.hpp                    |  52 +-
 include/util/typedefs.hpp                          |  41 +-
 include/util/web_mercator.hpp                      |  20 +-
 package.json                                       |   8 +-
 profiles/bicycle.lua                               |  18 +-
 profiles/car.lua                                   |  75 +++
 profiles/testbot.lua                               |   1 +
 scripts/poly2req.js                                |   2 +-
 scripts/travis/before_install.armhf.sh             |  28 +
 scripts/travis/before_install.i686.sh              |  10 +
 src/contractor/contractor.cpp                      | 253 +++----
 src/engine/api/json_factory.cpp                    |  40 +-
 src/engine/guidance/lane_processing.cpp            | 109 +++
 src/engine/guidance/post_processing.cpp            | 235 +++++--
 src/engine/plugins/tile.cpp                        |  37 +-
 src/engine/polyline_compressor.cpp                 |   4 +-
 src/extractor/edge_based_graph_factory.cpp         |  89 ++-
 src/extractor/extraction_containers.cpp            | 197 ++++--
 src/extractor/extractor.cpp                        |  37 +-
 src/extractor/extractor_callbacks.cpp              | 180 ++++-
 src/extractor/graph_compressor.cpp                 |  38 +-
 src/extractor/guidance/intersection.cpp            |  23 +-
 src/extractor/guidance/intersection_generator.cpp  |  13 +-
 src/extractor/guidance/intersection_handler.cpp    |   2 +-
 src/extractor/guidance/motorway_handler.cpp        |  37 +-
 src/extractor/guidance/roundabout_handler.cpp      |  13 +-
 src/extractor/guidance/turn_analysis.cpp           |  61 +-
 src/extractor/guidance/turn_discovery.cpp          |  85 +++
 src/extractor/guidance/turn_handler.cpp            |  29 +-
 src/extractor/guidance/turn_lane_augmentation.cpp  | 303 +++++++++
 src/extractor/guidance/turn_lane_data.cpp          | 130 ++++
 src/extractor/guidance/turn_lane_handler.cpp       | 538 +++++++++++++++
 src/extractor/guidance/turn_lane_matcher.cpp       | 255 +++++++
 src/extractor/raster_source.cpp                    |  16 +-
 src/extractor/scripting_environment.cpp            |   3 +
 src/server/request_handler.cpp                     |   1 +
 src/storage/storage.cpp                            |  82 ++-
 src/storage/storage_config.cpp                     |   3 +-
 src/tools/store.cpp                                |   3 +-
 src/util/assert.cpp                                |  13 +-
 src/util/coordinate.cpp                            |  12 +-
 src/util/coordinate_calculation.cpp                |  17 +-
 src/util/guidance/turn_lanes.cpp                   |  36 +
 src/util/name_table.cpp                            |   8 +-
 taginfo.json                                       |  30 +
 unit_tests/engine/douglas_peucker.cpp              |  30 +-
 unit_tests/engine/geometry_string.cpp              |  10 +-
 unit_tests/extractor/graph_compressor.cpp          | 408 +++++++++++-
 unit_tests/library/waypoint_check.hpp              |   4 +-
 unit_tests/mocks/mock_datafacade.hpp               |  12 +
 unit_tests/server/parameters_parser.cpp            |  32 +-
 unit_tests/server/url_parser.cpp                   |   2 +-
 unit_tests/util/coordinate_calculation.cpp         | 156 ++---
 unit_tests/util/group_by.cpp                       |  68 ++
 unit_tests/util/packed_vector.cpp                  |   2 +-
 unit_tests/util/rectangle.cpp                      |  72 +-
 unit_tests/util/static_rtree.cpp                   |  32 +-
 unit_tests/util/web_mercator.cpp                   |  10 +-
 158 files changed, 6585 insertions(+), 1186 deletions(-)

diff --git a/.clang-format b/.clang-format
index 285ed38..8f7cd4f 100644
--- a/.clang-format
+++ b/.clang-format
@@ -46,7 +46,7 @@ DerivePointerAlignment: false
 DisableFormat:   false
 ExperimentalAutoDetectBinPacking: false
 ForEachMacros:   [ foreach, Q_FOREACH, BOOST_FOREACH ]
-IncludeCategories: 
+IncludeCategories:
   - Regex:           '^<'
     Priority:        3
   - Regex:           '^"(osrm|util|engine|extract|contract)/'
diff --git a/.travis.yml b/.travis.yml
index a81b29c..fc47053 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -13,7 +13,6 @@ notifications:
 branches:
   only:
     - master
-    - "5.2"
 
 cache:
   ccache: true
@@ -25,6 +24,7 @@ env:
   global:
    - CCACHE_TEMPDIR=/tmp/.ccache-temp
    - CCACHE_COMPRESS=1
+   - CASHER_TIME_OUT=1000
    - JOBS=4
 
 matrix:
@@ -72,6 +72,14 @@ matrix:
           packages: ['g++-5', 'libbz2-dev', 'libstxxl-dev', 'libstxxl1', 'libxml2-dev', 'libzip-dev', 'lua5.1', 'liblua5.1-0-dev', 'libtbb-dev', 'libgdal-dev', 'libluabind-dev', 'libboost-all-dev', 'ccache']
       env: CCOMPILER='gcc-5' CXXCOMPILER='g++-5' BUILD_TYPE='Release'
 
+    - os: linux
+      compiler: "gcc-5-release-i686"
+      env: TARGET_ARCH='i686' CCOMPILER='gcc-5' CXXCOMPILER='g++-5' BUILD_TYPE='Release'
+
+    - os: linux
+      compiler: "gcc-4.8-release-armhf"
+      env: TARGET_ARCH='armhf' CCOMPILER='arm-linux-gnueabihf-gcc-4.8' CXXCOMPILER='arm-linux-gnueabihf-g++-4.8' BUILD_TYPE='Release'
+
       # Disabled because of CI slowness
       #- os: linux
       #- compiler: gcc
@@ -115,19 +123,20 @@ matrix:
       #- env: CCOMPILER='clang-3.8' CXXCOMPILER='clang++-3.8' BUILD_TYPE='Release' BUILD_SHARED_LIBS=ON
 
 before_install:
+  - if [[ ! -z $TARGET_ARCH ]] ; then source ./scripts/travis/before_install.$TARGET_ARCH.sh ; fi
   - if [[ $(uname -s) == 'Darwin' ]]; then sudo mdutil -i off /; fi;
   - source ./scripts/install_node.sh 4
   - npm install
   - DEPS_DIR="${TRAVIS_BUILD_DIR}/deps"
   - export PATH=${DEPS_DIR}/bin:${PATH} && mkdir -p ${DEPS_DIR}
   - CMAKE_URL="https://mason-binaries.s3.amazonaws.com/${TRAVIS_OS_NAME}-x86_64/cmake/3.5.2.tar.gz"
-  - travis_retry wget --quiet -O - ${CMAKE_URL} | tar --strip-components=1 -xz -C ${DEPS_DIR}
+  - travis_retry wget --quiet -O - ${CMAKE_URL} | tar --strip-components=1 -xz -C ${DEPS_DIR} || exit 1
   - |
     if [[ ${CLANG_VERSION:-false} != false ]]; then
       export CCOMPILER='clang'
       export CXXCOMPILER='clang++'
       CLANG_URL="https://mason-binaries.s3.amazonaws.com/${TRAVIS_OS_NAME}-x86_64/clang/${CLANG_VERSION}.tar.gz"
-      travis_retry wget --quiet -O - ${CLANG_URL} | tar --strip-components=1 -xz -C ${DEPS_DIR}
+      travis_retry wget --quiet -O - ${CLANG_URL} | tar --strip-components=1 -xz -C ${DEPS_DIR} || exit 1
     fi
   - |
     if [[ "${TRAVIS_OS_NAME}" == "osx" ]]; then
@@ -162,6 +171,7 @@ install:
   - popd
 
 script:
+  - if [[ $TARGET_ARCH == armhf ]] ; then echo "Skip tests for $TARGET_ARCH" && exit 0 ; fi
   - echo "travis_fold:start:BENCHMARK"
   - make -C test/data benchmark
   - echo "travis_fold:end:BENCHMARK"
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a6bae25..d89652a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,19 +1,64 @@
-# 5.2.6
-  - Bugfixes
-    - Fix numeric overflow in roundabout center calculation which throws an exception
+# 5.3.0
+  Changes from 5.3.0-rc.3
+    - Guidance
+      - Only announce `use lane` on required turns (not using all lanes to go straight)
+      - Moved `lanes` to the intersection objects. This is BREAKING in relation to other Release Candidates but not with respect to other releases.
+    - Bugfixes
+      - Fix BREAKING: bug that could result in failure to load 'osrm.icd' files. This breaks the dataformat
+      - Fix: bug that results in segfaults when `use lane` instructions are suppressed
+
+  Changes form 5.2.7
+    - API
+      - Introduces new `TurnType` in the form of `use lane`. The type indicates that you have to stick to a lane without turning
+      - Introduces `lanes` to the `Intersection` object. The lane data contains both the markings at the intersection and a flag indicating if they can be chosen for the next turn
+      - Removed unused `-s` from `osrm-datastore`
+    - Guidance
+      - Only announce `use lane` on required turns (not using all lanes to go straight)
+      - Improved detection of obvious turns
+      - Improved turn lane detection
+      - Reduce the number of end-of-road instructions in obvious cases
+    - Profile:
+      - bicycle.lua: Surface speeds never increase the actual speed
+    - Infrastructure
+      - Add 32bit support
+      - Add ARM NEON/VFP support
+      - Fix Windows builds
+      - Optimize speed file updates using mmap
+      - Add option to disable LTO for older compilers
+      - BREAKING: The new turn type changes the turn-type order. This breaks the **data format**.
+      - BREAKING: Turn lane data introduces two new files (osrm.tld,osrm.tls). This breaks the fileformat for older versions.
+    - Bugfixes:
+      - Fix devide by zero on updating speed data using osrm-contract
+
+# 5.3.0 RC3
+  Changes from 5.3.0-rc.2
+    - Guidance
+      - Improved detection of obvious turns
+      - Improved turn lane detection
+    - Bugfixes
+      - Fix bug that didn't chose minimal weights on overlapping edges
+
+# 5.3.0 RC2
+  Changes from 5.3.0-rc.1
+    - Bugfixes
+      - Fixes invalid checks in the lane-extraction part of the car profile
+
+# 5.3.0 RC1
+    - API
+     - Introduces new `TurnType` in the form of `use lane`. The type indicates that you have to stick to a lane without turning
+     - Introduces lanes to the route response. The lane data contains both the markings at the intersection and a flag indicating their involvement in the turn
+
+    - Infrastructure
+     - BREAKING: The new turn type changes the turn-type order. This breaks the **data format**.
+     - BREAKING: Turn lane data introduces two new files (osrm.tld,osrm.tls). This breaks the fileformat for older versions.
 
 # 5.2.5
   - Bugfixes
     - Fixes a segfault caused by incorrect trimming logic for very short steps.
 
 # 5.2.4
-  Changes from 5.2.3:
   - Bugfixes:
-    - Fixed a crash that arised on roundabouts in combination with intermediate intersections and sliproads
-    - Fixed #2518: Invalid bilinear interpolation when using raster data
-    - Fixed #2546: The geometry returned by the match service was missing the last coordinate.
-
-  BREAKING: The data format changed between 5.2.3 and 5.2.4, you need to reprocess.
+    - Fixed in issue that arised on roundabouts in combination with intermediate intersections and sliproads
 
 # 5.2.3
   - Bugfixes:
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 6697ab7..5ee57b4 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -9,8 +9,8 @@ endif()
 
 project(OSRM C CXX)
 set(OSRM_VERSION_MAJOR 5)
-set(OSRM_VERSION_MINOR 2)
-set(OSRM_VERSION_PATCH 6)
+set(OSRM_VERSION_MINOR 3)
+set(OSRM_VERSION_PATCH 0)
 
 # these two functions build up custom variables:
 #   OSRM_INCLUDE_PATHS and OSRM_DEFINES
@@ -39,7 +39,7 @@ if(CMAKE_SIZEOF_VOID_P EQUAL 8)
   set(bitness 64)
   message(STATUS "Building on a 64 bit system")
 else()
-  message(WARNING "Building on a 32 bit system is unsupported")
+  message(STATUS "Building on a 32 bit system")
 endif()
 
 if(WIN32 AND MSVC_VERSION LESS 1900)
@@ -53,6 +53,7 @@ option(BUILD_COMPONENTS "Build osrm-components" OFF)
 option(ENABLE_ASSERTIONS OFF)
 option(COVERAGE OFF)
 option(SANITIZER OFF)
+option(ENABLE_LTO "Use LTO if available" ON)
 
 include_directories(BEFORE ${CMAKE_CURRENT_BINARY_DIR}/include/)
 include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR}/include/)
@@ -118,7 +119,7 @@ if(CMAKE_BUILD_TYPE MATCHES Release)
   message(STATUS "Configuring OSRM in release mode")
   # Check if LTO is available
   check_cxx_compiler_flag("-flto" LTO_AVAILABLE)
-  if(LTO_AVAILABLE)
+  if(ENABLE_LTO AND LTO_AVAILABLE)
     set(OLD_CXX_FLAGS ${CMAKE_CXX_FLAGS})
     # GCC in addition allows parallelizing LTO
     if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
@@ -319,7 +320,7 @@ include_directories(SYSTEM ${OSRM_INCLUDE_PATHS})
 # Binaries
 target_link_libraries(osrm-datastore osrm_store ${Boost_LIBRARIES})
 target_link_libraries(osrm-extract osrm_extract ${Boost_LIBRARIES})
-target_link_libraries(osrm-contract osrm_contract ${Boost_LIBRARIES})
+target_link_libraries(osrm-contract ${Boost_LIBRARIES} ${TBB_LIBRARIES} osrm_contract)
 target_link_libraries(osrm-routed osrm ${Boost_LIBRARIES} ${OPTIONAL_SOCKET_LIBS} ${ZLIB_LIBRARY})
 
 set(EXTRACTOR_LIBRARIES
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..8a41e73
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,55 @@
+# User
+
+Before you open a new issue, please search for older ones that cover the same issue.
+In general "me too" comments/issues are frowned upon.
+You can add a :+1: emoji to the issue if you want to express interest in this.
+
+# Developer
+
+We use `clang-format` version `3.8` to consistently format the code base. There is a helper script under `scripts/format.sh`.
+
+In general changes that affect the API and/or increase the memory consumption need to be discussed first.
+Often we don't include changes that would increase the memory consumption a lot if they are not generally usable (e.g. elevation data is a good example).
+
+## Pull Request
+
+Every pull-request that changes the API needs to update the docs in `docs/http.md` and add an entry to `CHANGELOG.md`.
+Breaking changes need to have a BREAKING prefix. See the [releasing documentation](docs/releasing.md) on how this affects the version.
+
+Early feedback is also important.
+You will see that a lot of the PR have tags like `[not ready]` or `[wip]`.
+We like to open PRs as soon as we are starting to work on something to make it visible to the rest of the team.
+If your work is going in entirely the wrong direction, there is a good chance someone will pick up on this before it is too late.
+Everyone is encouraged to read PRs of other people and give feedback.
+
+For every significant code change we require a pull request review before it is merged.
+If your pull request modifies the API this need to be signed of by a team discussion.
+This means you will need to find another member of the team with commit access and request a review of your pull request.
+
+Once your pull request is reviewed you can merge it! If you don't have commit access, ping someone that has commit access.
+If you do have commit access there are in general two accepted styles to merging:
+
+1. Make sure the branch is up to date with `master`. Run `git rebase master` to find out.
+2. Once that is ensured you can either:
+  - Click the nice green merge button (for a non-fast-forward merge)
+  - Merge by hand using a fast-forward merge
+
+Which merge you prefer is up to personal preference. In general it is recommended to use fast-forward merges because it creates a history that is sequential and easier to understand.
+
+# Maintainer
+
+## Doing a release
+
+There is an in-depth guide around how to push out a release once it is ready [here](docs/releasing.md).
+
+## The API
+
+Changes to the API need to be discussed and signed off by the team. Breaking changes even more so than additive changes.
+
+## Milestones
+
+If a pull request or an issue is applicable for the current or next milestone, depends on the target version number.
+Since we use semantic versioning we restrict breaking changes to major releases.
+After a Release Candidate is released we usually don't change the API anymore if it is not critical.
+Bigger code changes after a RC was released should also be avoided.
+
diff --git a/README.md b/README.md
index ef6d870..f1a28a3 100644
--- a/README.md
+++ b/README.md
@@ -46,7 +46,7 @@ osrm-routed data.osrm
 Running a query on your local server:
 
 ```
-curl http://127.0.0.1:5000/13.388860,52.517037;13.385983,52.496891?steps=true&alternatives=true
+curl http://127.0.0.1:5000/route/v1/driving/13.388860,52.517037;13.385983,52.496891?steps=true&alternatives=true
 ```
 
 ### Running a request against the Demo Server
@@ -56,7 +56,7 @@ First read the [API usage policy](https://github.com/Project-OSRM/osrm-backend/w
 Then run simple query with instructions and alternatives on Berlin:
 
 ```
-curl https://router.project-osrm.org/13.388860,52.517037;13.385983,52.496891?steps=true&alternatives=true
+curl https://router.project-osrm.org/route/v1/driving/13.388860,52.517037;13.385983,52.496891?steps=true&alternatives=true
 ```
 
 ## References in publications
diff --git a/docker/Dockerfile b/docker/Dockerfile
index 72ec608..ea5e6a1 100644
--- a/docker/Dockerfile
+++ b/docker/Dockerfile
@@ -1,18 +1,9 @@
 FROM ubuntu:14.04
 
-RUN apt-get update -y
-RUN apt-get install -y build-essential git-core python-pip python-software-properties software-properties-common
-
-RUN apt-get -y install gcc-4.8 g++-4.8 libboost1.55-all-dev llvm-3.4
-RUN apt-get -y install libbz2-dev libstxxl-dev libstxxl1 libxml2-dev
-RUN apt-get -y install libzip-dev lua5.1 liblua5.1-0-dev libtbb-dev libgdal-dev
-RUN apt-get -y install curl cmake cmake-curses-gui
-
-RUN pip install awscli
-
-
-# luabind
-RUN curl https://gist.githubusercontent.com/DennisOSRM/f2eb7b948e6fe1ae319e/raw/install-luabind.sh | sudo bash
+RUN apt-get update -y && apt-get install -y  software-properties-common
+RUN add-apt-repository ppa:ubuntu-toolchain-r/test
+RUN apt-get update -y && apt-get install -y g++-5 libbz2-dev libstxxl-dev libstxxl1 libxml2-dev libzip-dev lua5.1 liblua5.1-0-dev libtbb-dev libgdal-dev libluabind-dev libboost-all-dev ccache
+RUN apt-get -y install curl cmake cmake-curses-gui git
 
 WORKDIR /opt
 RUN git clone --depth 1 --branch v0.31.0 https://github.com/creationix/nvm.git
diff --git a/docker/run-gcc.sh b/docker/run-gcc.sh
index 2b2527b..672d641 100755
--- a/docker/run-gcc.sh
+++ b/docker/run-gcc.sh
@@ -5,7 +5,8 @@ set -o pipefail
 
 docker run \
     -i \
-    -e "CXX=g++" \
+    -e "CXX=g++-5" \
+    -e "CC=gcc-5" \
     -v `pwd`:/home/mapbox/osrm-backend \
     -t mapbox/osrm:linux \
     /bin/bash -lc "osrm-backend/docker/test.sh"
diff --git a/docs/http.md b/docs/http.md
index 1b02153..fca0680 100644
--- a/docs/http.md
+++ b/docs/http.md
@@ -357,7 +357,7 @@ Three input coordinates, `geometry=geojson`, `steps=false`:
 {
   "distance": 90.0,
   "duration": 300.0,
-  "geometry": {"type": "LineString", "coordinates": [[120., 10.], [120.1, 10.], [120.2, 10.], [120.3, 10.]]},
+  "geometry": {"type": "LineString", "coordinates": [[120.0, 10.0], [120.1, 10.0], [120.2, 10.0], [120.3, 10.0]]},
   "legs": [
     {
       "distance": 30.0,
@@ -410,7 +410,7 @@ With `steps=false` and `annotations=true`:
 {
   "distance": 30.0,
   "duration": 100.0,
-  "steps": []
+  "steps": [],
   "annotation": {
     "distance": [5,5,10,5,5],
     "duration": [15,15,40,15,15],
@@ -441,7 +441,7 @@ step.
 - `destinations`: The destinations of the way. Will be `undefined` if there are no destinations.
 - `mode`: A string signifying the mode of transportation.
 - `maneuver`: A `StepManeuver` object representing the maneuver.
-- `intersections`: A list of `Intersections` that are passed along the segment, the very first belonging to the StepManeuver
+- `intersections`: A list of `Intersection` objects that are passed along the segment, the very first belonging to the StepManeuver
 
 #### Example
 
@@ -451,22 +451,30 @@ step.
  "duration":15.6,
  "name":"Lortzingstraße",
  "maneuver":{
-     "type":"depart",
-     "modifier":"left"
- },
+     "type":"turn",
+     "modifier":"right",
+     },
  "geometry":"{lu_IypwpAVrAvAdI",
  "mode":"driving",
  "intersections":[
     {"location":[13.39677,52.54366],
-    "out":1,
-    "bearings":[66,246],
-    "entry":["true","true"]},
+    "in":3,
+    "out":2,
+    "bearings":[10,92,184,270],
+    "entry":["true","true","true","false"],
+    "lanes":[
+         {"indications":["left","straight"], "valid":"false"},
+         {"indications":["right"], "valid":"true"}
+    ]},
     {"location":[13.394718,52.543096],
     "in":0,
-    "out":2,
-    "bearings":[60,150,240,330],
-    "entry":["false","true","true","true"]
-    }
+    "out":1,
+    "bearings":[60,240,330],
+    "entry":["false","true","true"]
+    "lanes":[
+         {"indications":["straight"], "valid":"true"},
+         {"indications":["right"], "valid":"false"}
+     ]}
 ]}
 ```
 
@@ -482,62 +490,92 @@ step.
 - `type` A string indicating the type of maneuver. **new identifiers might be introduced without API change**
    Types  unknown to the client should be handled like the `turn` type, the existance of correct `modifier` values is guranteed.
   
-  | `type`            | Description                                                  |
-  |-------------------|--------------------------------------------------------------|
-  | turn              | a basic turn into direction of the `modifier`                |
-  | new name          | no turn is taken/possible, but the road name changes. The road can take a turn itself, following `modifier`.                  |
-  | depart            | indicates the departure of the leg                           |
-  | arrive            | indicates the destination of the leg                         |
-  | merge             | merge onto a street (e.g. getting on the highway from a ramp, the `modifier specifies the direction of the merge`) |
-  | ramp              | **Deprecated**. Replaced by `on_ramp` and `off_ramp`.        |
-  | on ramp           | take a ramp to enter a highway (direction given my `modifier`) |
-  | off ramp          | take a ramp to exit a highway (direction given my `modifier`)  |
-  | fork              | take the left/right side at a fork depending on `modifier`   |
-  | end of road       | road ends in a T intersection turn in direction of `modifier`|
-  | continue          | Turn in direction of `modifier` to stay on the same road     |
-  | roundabout        | traverse roundabout, has additional field `exit` with NR if the roundabout is left. `the modifier specifies the direction of entering the roundabout` |
-  | rotary            | a larger version of a roundabout, can offer `rotary_name` in addition to the `exit` parameter.  |
-  | roundabout turn   | Describes a turn at a small roundabout that should be treated as normal turn. The `modifier` indicates the turn direciton. Example instruction: `At the roundabout turn left`. |
-  | notification      | not an actual turn but a change in the driving conditions. For example the travel mode.  If the road takes a turn itself, the `modifier` describes the direction |
+  | `type`           | Description                                                  |
+  |------------------|--------------------------------------------------------------|
+  | `turn`           | a basic turn into direction of the `modifier`                |
+  | `new name`       | no turn is taken/possible, but the road name changes. The road can take a turn itself, following `modifier`.                  |
+  | `depart`         | indicates the departure of the leg                           |
+  | `arrive`         | indicates the destination of the leg                         |
+  | `merge`          | merge onto a street (e.g. getting on the highway from a ramp, the `modifier specifies the direction of the merge`) |
+  | `ramp`           | **Deprecated**. Replaced by `on_ramp` and `off_ramp`.        |
+  | `on ramp`        | take a ramp to enter a highway (direction given my `modifier`) |
+  | `off ramp`       | take a ramp to exit a highway (direction given my `modifier`)  |
+  | `fork`           | take the left/right side at a fork depending on `modifier`   |
+  | `end of road`    | road ends in a T intersection turn in direction of `modifier`|
+  | `use lane`       | going straight on a specific lane                            |
+  | `continue`       | Turn in direction of `modifier` to stay on the same road     |
+  | `roundabout`     | traverse roundabout, has additional field `exit` with NR if the roundabout is left. `the modifier specifies the direction of entering the roundabout` |
+  | `rotary`         | a larger version of a roundabout, can offer `rotary_name` in addition to the `exit` parameter.  |
+  | `roundabout turn`| Describes a turn at a small roundabout that should be treated as normal turn. The `modifier` indicates the turn direciton. Example instruction: `At the roundabout turn left`. |
+  | `notification`   | not an actual turn but a change in the driving conditions. For example the travel mode.  If the road takes a turn itself, the `modifier` describes the direction |
 
   Please note that even though there are `new name` and `notification` instructions, the `mode` and `name` can change
   between all instructions. They only offer a fallback in case nothing else is to report.
 
-
 - `modifier` An optional `string` indicating the direction change of the maneuver.
-
+  
   | `modifier`        | Description                               |
   |-------------------|-------------------------------------------|
-  | uturn             | indicates  reversal of direction          |
-  | sharp right       | a sharp right turn                        |
-  | right             | a normal turn to the right                |
-  | slight right      | a slight turn to the right                |
-  | straight          | no relevant change in direction           |
-  | slight left       | a slight turn to the left                 |
-  | left              | a normal turn to the left                 |
-  | sharp left        | a sharp turn to the left                  |
-
- The list of turns without a modifier is limited to: `depart/arrive`. If the source/target location is close enough to the `depart/arrive` location, no modifier will be given.
+  | `uturn`           | indicates  reversal of direction          |
+  | `sharp right`     | a sharp right turn                        |
+  | `right`           | a normal turn to the right                |
+  | `slight right`    | a slight turn to the right                |
+  | `straight`        | no relevant change in direction           |
+  | `slight left`     | a slight turn to the left                 |
+  | `left`            | a normal turn to the left                 |
+  | `sharp left`      | a sharp turn to the left                  |
+  
+  The list of turns without a modifier is limited to: `depart/arrive`. If the source/target location is close enough to the `depart/arrive` location, no modifier will be given.
   
   The meaning depends on the `type` field.
-    
+  
   | `type`                 | Description                                                                                                               |
   |------------------------|---------------------------------------------------------------------------------------------------------------------------|
   | `turn`                 | `modifier` indicates the change in direction accomplished through the turn                                                |
   | `depart`/`arrive`      | `modifier` indicates the position of departure point and arrival point in relation to the current direction of travel      |
- 
-
+  
 - `exit` An optional `integer` indicating number of the exit to take. The field exists for the following `type` field:
   
   | `type`                 | Description                                                                                                               |
   |------------------------|---------------------------------------------------------------------------------------------------------------------------|
   | `roundabout`           | Number of the roundabout exit to take. If exit is `undefined` the destination is on the roundabout.                       |
   | else                   | Indicates the number of intersections passed until the turn. Example instruction: `at the fourth intersection, turn left` |
-  
+
 
 New properties (potentially depending on `type`) may be introduced in the future without an API version change.
 
-### Intersections
+### Lane
+
+A `Lane` represents a turn lane at the corresponding turn location.
+
+#### Properties
+
+- `indications`: a indication (e.g. marking on the road) specifying the turn lane. A road can have multiple indications (e.g. an arrow pointing straight and left). The indications are given in an array, each containing one of the following types. Further indications might be added on without an API version change.
+  
+  | `value`                | Description                                                                                                               |
+  |------------------------|---------------------------------------------------------------------------------------------------------------------------|
+  | `none`                 | No dedicated indication is shown.                                                                                         |
+  | `uturn`                | An indication signaling the possibility to reverse (i.e. fully bend arrow).                                               |
+  | `sharp right`          | An indication indicating a sharp right turn (i.e. strongly bend arrow).                                                   |
+  | `right`                | An indication indicating a right turn (i.e. bend arrow).                                                                  |
+  | `slight right`         | An indication indicating a slight right turn (i.e. slightly bend arrow).                                                  |
+  | `straight`             | No dedicated indication is shown (i.e. straight arrow).                                                                   |
+  | `slight left`          | An indication indicating a slight left turn (i.e. slightly bend arrow).                                                   |
+  | `left`                 | An indication indicating a left turn (i.e. bend arrow).                                                                   |
+  | `sharp left`           | An indication indicating a sharp left turn (i.e. strongly bend arrow).                                                    |
+  
+- `valid`: a boolean flag indicating whether the lane is a valid choice in the current maneuver
+
+#### Example
+
+```json
+{
+    "indications": ["left", "straight"],
+    "valid": "false"
+}
+ ```
+
+### Intersection
 
 An intersection gives a full representation of any cross-way the path passes bay. For every step, the very first intersection (`intersections[0]`) corresponds to the
 location of the StepManeuver. Further intersections are listed for every cross-way until the next turn instruction.
@@ -553,6 +591,7 @@ location of the StepManeuver. Further intersections are listed for every cross-w
   in the direction of driving, the bearing has to be rotated by a value of 180. The value is not supplied for `depart` maneuvers.
 - `out`: index into the bearings/entry array. Used to extract the bearing just after the turn. Namely, The clockwise angle from true north to the
   direction of travel immediately after the maneuver/passing the intersection. The value is not supplied for `arrive` maneuvers.
+- `lanes`: Array of `Lane` objects that denote the available turn lanes at the turn location
 
 #### Example
 ```
@@ -562,6 +601,11 @@ location of the StepManeuver. Further intersections are listed for every cross-w
     "out":2,
     "bearings":[60,150,240,330],
     "entry":["false","true","true","true"]
+    "lanes":{
+        "indications": ["left", "straight"],
+        "valid": "false"
+    }
+                                                                                                                               ]}
 }
 ```
 
diff --git a/example/example.cpp b/example/example.cpp
index 5061343..c3008f4 100644
--- a/example/example.cpp
+++ b/example/example.cpp
@@ -40,8 +40,8 @@ int main(int argc, const char *argv[]) try
     RouteParameters params;
 
     // Route in monaco
-    params.coordinates.push_back({util::FloatLongitude(7.419758), util::FloatLatitude(43.731142)});
-    params.coordinates.push_back({util::FloatLongitude(7.419505), util::FloatLatitude(43.736825)});
+    params.coordinates.push_back({util::FloatLongitude{7.419758}, util::FloatLatitude{43.731142}});
+    params.coordinates.push_back({util::FloatLongitude{7.419505}, util::FloatLatitude{43.736825}});
 
     // Response is in JSON format
     json::Object result;
diff --git a/features/bicycle/mode.feature b/features/bicycle/mode.feature
index 852402a..98d23c4 100644
--- a/features/bicycle/mode.feature
+++ b/features/bicycle/mode.feature
@@ -46,14 +46,17 @@ Feature: Bike - Mode flag
 
      Scenario: Bike - Mode when pushing bike against oneways
      	Given the node map
-     	 | a | b |   |
-     	 |   | c | d |
+     	 | a | b | e |
+     	 | f | c | d |
 
      	And the ways
      	 | nodes | highway | oneway |
      	 | ab    | primary |        |
      	 | bc    | primary | yes    |
      	 | cd    | primary |        |
+     	 | be    | primary |        |
+     	 | cf    | primary |        |
+
 
      	When I route I should get
      	 | from | to | route       | modes                                |
diff --git a/features/bicycle/oneway.feature b/features/bicycle/oneway.feature
index 1d363eb..17e4a25 100644
--- a/features/bicycle/oneway.feature
+++ b/features/bicycle/oneway.feature
@@ -18,8 +18,8 @@ Feature: Bike - Oneway streets
 
     Scenario: Bike - Around the Block
         Given the node map
-            | a | b |
-            | d | c |
+            |   | a | b |   |
+            | f | d | c | e |
 
         And the ways
             | nodes | oneway | foot |
@@ -27,6 +27,8 @@ Feature: Bike - Oneway streets
             | bc    |        | no   |
             | cd    |        | no   |
             | da    |        | no   |
+            | df    |        | no   |
+            | ce    |        | no   |
 
         When I route I should get
             | from | to | route       |
diff --git a/features/bicycle/pushing.feature b/features/bicycle/pushing.feature
index d672821..d06a96d 100644
--- a/features/bicycle/pushing.feature
+++ b/features/bicycle/pushing.feature
@@ -87,14 +87,16 @@ Feature: Bike - Accessability of different way types
 
     Scenario: Bike - Instructions when pushing bike on oneways
         Given the node map
-            | a | b |   |
-            |   | c | d |
+            | a | b | e |
+            | f | c | d |
 
         And the ways
             | nodes | highway | oneway |
             | ab    | primary |        |
             | bc    | primary | yes    |
             | cd    | primary |        |
+            | be    | primary |        |
+            | cf    | primary |        |
 
         When I route I should get
             | from | to | route       | modes                                   |
diff --git a/features/bicycle/surface.feature b/features/bicycle/surface.feature
index 8d303aa..4f95ee6 100644
--- a/features/bicycle/surface.feature
+++ b/features/bicycle/surface.feature
@@ -38,3 +38,20 @@ Feature: Bike - Surfaces
         | cycleway |         | 48s   |
         | nosense  |         |       |
         | nosense  | asphalt |       |
+
+    Scenario: Bicycle - Surfaces should not increase speed when pushing bikes
+      Given the node map
+         | a | b |
+         | c | d |
+
+      And the ways
+        | nodes | highway | oneway | surface |
+        | ab    | primary | yes    | asphalt |
+        | cd    | footway |        | asphalt |
+
+      When I route I should get
+        | from | to | route | modes                     | speed   |
+        | a    | b  | ab,ab | cycling,cycling           | 15 km/h |
+        | b    | a  | ab,ab | pushing bike,pushing bike | 6 km/h  |
+        | c    | d  | cd,cd | pushing bike,pushing bike | 6 km/h  |
+        | d    | c  | cd,cd | pushing bike,pushing bike | 6 km/h  |
diff --git a/features/car/oneway.feature b/features/car/oneway.feature
index d048afb..72fd394 100644
--- a/features/car/oneway.feature
+++ b/features/car/oneway.feature
@@ -35,8 +35,8 @@ Feature: Car - Oneway streets
 
     Scenario: Car - Around the Block
         Given the node map
-            | a | b |
-            | d | c |
+            |   | a | b |   |
+            | f | d | c | e |
 
         And the ways
             | nodes | oneway |
@@ -44,6 +44,8 @@ Feature: Car - Oneway streets
             | bc    |        |
             | cd    |        |
             | da    |        |
+            | ce    |        |
+            | df    |        |
 
         When I route I should get
             | from | to | route       |
diff --git a/features/guidance/anticipate-lanes.feature b/features/guidance/anticipate-lanes.feature
new file mode 100644
index 0000000..cb77895
--- /dev/null
+++ b/features/guidance/anticipate-lanes.feature
@@ -0,0 +1,500 @@
+ at routing @guidance @turn-lanes
+Feature: Turn Lane Guidance
+
+    Background:
+        Given the profile "car"
+        Given a grid size of 20 meters
+
+    @anticipate
+    Scenario: Anticipate Lane Change for subsequent multi-lane intersections
+        Given the node map
+            | a |   | b |   | x |   |   |
+            |   |   |   |   |   |   |   |
+            |   |   | c |   | d |   | z |
+            |   |   |   |   |   |   |   |
+            |   |   | y |   | e |   |   |
+
+        And the ways
+            | nodes | turn:lanes:forward           |
+            | ab    | through\|right\|right\|right |
+            | bx    |                              |
+            | bc    | left\|left\|through          |
+            | cd    | through\|right               |
+            | cy    |                              |
+            | dz    |                              |
+            | de    |                              |
+
+       When I route I should get
+            | waypoints | route          | turns                                         | lanes                                                                                                             | #      |
+            | a,d       | ab,bc,cd,cd    | depart,turn right,turn left,arrive            | ,straight:false right:true right:true right:false,left:true left:true straight:false,                             | 2 hops |
+            | a,e       | ab,bc,cd,de,de | depart,turn right,turn left,turn right,arrive | ,straight:false right:false right:true right:false,left:false left:true straight:false,straight:false right:true, | 3 hops |
+
+    @anticipate
+    Scenario: Anticipate Lane Change for quick same direction turns, staying on the same street
+        Given the node map
+            | a |   | b | x |
+            |   |   |   |   |
+            |   |   | c |   |
+            |   |   |   |   |
+            | e |   | d | y |
+
+        And the ways
+            | nodes | turn:lanes:forward    | turn:lanes:backward | name |
+            | ab    | through\|right\|right |                     | MySt |
+            | bx    |                       |                     | XSt  |
+            | bc    |                       | left\|right         | MySt |
+            | cd    | left\|right           | through\|through    | MySt |
+            | de    |                       | left\|left\|through | MySt |
+            | dy    |                       |                     | YSt  |
+
+       When I route I should get
+            | waypoints | route               | turns                                   | lanes                                                         |
+            | a,e       | MySt,MySt,MySt,MySt | depart,continue right,turn right,arrive | ,straight:false right:false right:true,left:false right:true, |
+            | e,a       | MySt,MySt,MySt,MySt | depart,continue left,turn left,arrive   | ,left:true left:false straight:false,left:true right:false,   |
+
+    @anticipate
+    Scenario: Anticipate Lane Change for quick same direction turns, changing between streets
+        Given the node map
+            | a |   | b | x |
+            |   |   |   |   |
+            |   |   | c |   |
+            |   |   |   |   |
+            | e |   | d | y |
+
+        And the ways
+            | nodes | turn:lanes:forward    | turn:lanes:backward | name |
+            | ab    | through\|right\|right |                     | AXSt |
+            | bx    |                       |                     | AXSt |
+            | bc    |                       | left\|right         | BDSt |
+            | cd    | left\|right           | through\|through    | BDSt |
+            | de    |                       | left\|left\|through | EYSt |
+            | dy    |                       |                     | EYSt |
+
+       When I route I should get
+            | waypoints | route               | turns                               | lanes                                                         |
+            | a,e       | AXSt,BDSt,EYSt,EYSt | depart,turn right,turn right,arrive | ,straight:false right:false right:true,left:false right:true, |
+            | e,a       | EYSt,BDSt,AXSt,AXSt | depart,turn left,turn left,arrive   | ,left:true left:false straight:false,left:true right:false,   |
+
+
+    @anticipate
+    Scenario: Anticipate Lane Change for quick turns during a merge
+        Given the node map
+            | a |   |   |   |   |
+            | x | b |   | c | y |
+            |   |   |   |   | d |
+
+        And the ways
+            | nodes | turn:lanes:forward       | name | highway       | oneway |
+            | ab    | slight_left\|slight_left | On   | motorway_link | yes    |
+            | xb    |                          | Hwy  | motorway      |        |
+            | bc    | through\|slight_right    | Hwy  | motorway      |        |
+            | cd    |                          | Off  | motorway_link | yes    |
+            | cy    |                          | Hwy  | motorway      |        |
+
+       When I route I should get
+            | waypoints | route          | turns                                           | lanes                                                                 |
+            | a,d       | On,Hwy,Off,Off | depart,merge slight right,off ramp right,arrive | ,slight left:false slight left:true,straight:false slight right:true, |
+
+
+    @anticipate
+    Scenario: Schoenefelder Kreuz
+    # https://www.openstreetmap.org/way/264306388#map=16/52.3202/13.5568
+        Given the node map
+            | a | b | x |   |   | i |
+            |   |   | c | d |   |   |
+            |   |   |   |   |   | j |
+
+        And the ways
+            | nodes | turn:lanes:forward                                  | lanes | highway       | oneway | name |
+            | ab    | none\|none\|none\|slight_right\|slight_right        |   5   | motorway      |        | abx  |
+            | bx    |                                                     |   3   | motorway      |        | abx  |
+            | bc    |                                                     |   2   | motorway_link | yes    | bcd  |
+            | cd    | slight_left\|slight_left;slight_right\|slight_right |   3   | motorway_link | yes    | bcd  |
+            | di    | slight_left\|slight_right                           |   2   | motorway_link | yes    | di   |
+            | dj    |                                                     |   2   | motorway_link | yes    | dj   |
+
+       When I route I should get
+            | waypoints | route         | turns                                          | lanes                                                                                                                                    |
+            | a,i       | abx,bcd,di,di | depart,off ramp right,fork slight left,arrive  | ,none:false none:false none:false slight right:true slight right:true,slight left:true slight left;slight right:true slight right:false, |
+            | a,j       | abx,bcd,dj,dj | depart,off ramp right,fork slight right,arrive | ,none:false none:false none:false slight right:true slight right:true,slight left:false slight left;slight right:true slight right:true, |
+
+
+    @anticipate
+    Scenario: Kreuz Oranienburg
+    # https://www.openstreetmap.org/way/4484007#map=18/52.70439/13.20269
+        Given the node map
+            | i |   |   |   |   | a |
+            | j |   | c | b |   | x |
+
+        And the ways
+            | nodes | turn:lanes:forward | lanes | highway       | oneway | name |
+            | ab    |                    | 1     | motorway_link | yes    | ab   |
+            | xb    |                    | 1     | motorway_link | yes    | xbcj |
+            | bc    | none\|slight_right | 2     | motorway_link | yes    | xbcj |
+            | ci    |                    | 1     | motorway_link | yes    | ci   |
+            | cj    |                    | 1     | motorway_link | yes    | xbcj |
+
+       When I route I should get
+            | waypoints | route         | turns                                             | lanes                           |
+            | a,i       | ab,xbcj,ci,ci | depart,merge slight left,turn slight right,arrive | ,,none:false slight right:true, |
+            | a,j       | ab,xbcj,xbcj  | depart,merge slight left,arrive                   | ,,                              |
+
+
+    @anticipate
+    Scenario: Lane anticipation for fan-in
+        Given the node map
+            | a |   | b |   | x |   |   |
+            |   |   |   |   |   |   |   |
+            |   |   | c |   | d |   | z |
+            |   |   |   |   |   |   |   |
+            |   |   | y |   | e |   |   |
+
+        And the ways
+            | nodes | turn:lanes:forward           | name |
+            | ab    | through\|right\|right\|right | abx  |
+            | bx    |                              | abx  |
+            | bc    | left\|left\|through          | bcy  |
+            | cy    |                              | bcy  |
+            | cd    | through\|right               | cdz  |
+            | dz    |                              | cdz  |
+            | de    |                              | de   |
+
+       When I route I should get
+            | waypoints | route             | turns                                         | lanes                                                                                                             |
+            | a,e       | abx,bcy,cdz,de,de | depart,turn right,turn left,turn right,arrive | ,straight:false right:false right:true right:false,left:false left:true straight:false,straight:false right:true, |
+
+    @anticipate
+    Scenario: Lane anticipation for fan-out
+        Given the node map
+            | a |   | b |   | x |   |   |
+            |   |   |   |   |   |   |   |
+            |   |   | c |   | d |   | z |
+            |   |   |   |   |   |   |   |
+            |   |   | y |   | e |   |   |
+
+        And the ways
+            | nodes | turn:lanes:forward           | name |
+            | ab    | through\|right               | abx  |
+            | bx    |                              | abx  |
+            | bc    | left\|left\|through          | bcy  |
+            | cy    |                              | bcy  |
+            | cd    | through\|right\|right\|right | cdz  |
+            | dz    |                              | cdz  |
+            | de    |                              | de   |
+
+       When I route I should get
+            | waypoints | route             | turns                                         | lanes                                                                                                          |
+            | a,e       | abx,bcy,cdz,de,de | depart,turn right,turn left,turn right,arrive | ,straight:false right:true,left:true left:true straight:false,straight:false right:true right:true right:true, |
+
+    @anticipate
+    Scenario: Lane anticipation for fan-in followed by fan-out
+        Given the node map
+            | a |   | b |   | x |   |   |
+            |   |   |   |   |   |   |   |
+            |   |   | c |   | d |   | z |
+            |   |   |   |   |   |   |   |
+            |   |   | y |   | e |   |   |
+
+        And the ways
+            | nodes | turn:lanes:forward           | name |
+            | ab    | through\|right\|right\|right | abx  |
+            | bx    |                              | abx  |
+            | bc    | left\|left\|through          | bcy  |
+            | cy    |                              | bcy  |
+            | cd    | through\|right\|right\|right | cdz  |
+            | dz    |                              | cdz  |
+            | de    |                              | de   |
+
+       When I route I should get
+            | waypoints | route             | turns                                         | lanes                                                                                                                                 |
+            | a,e       | abx,bcy,cdz,de,de | depart,turn right,turn left,turn right,arrive | ,straight:false right:true right:true right:false,left:true left:true straight:false,straight:false right:true right:true right:true, |
+
+    @anticipate
+    Scenario: Lane anticipation for fan-out followed by fan-in
+        Given the node map
+            | a |   | b |   | x |   |   |
+            |   |   |   |   |   |   |   |
+            |   |   | c |   | d |   | z |
+            |   |   |   |   |   |   |   |
+            |   |   | y |   | e |   |   |
+
+        And the ways
+            | nodes | turn:lanes:forward  | name |
+            | ab    | through\|right      | abx  |
+            | bx    |                     | abx  |
+            | bc    | left\|left\|through | bcy  |
+            | cy    |                     | bcy  |
+            | cd    | through\|right      | cdz  |
+            | dz    |                     | cdz  |
+            | de    |                     | de   |
+
+       When I route I should get
+            | waypoints | route             | turns                                         | lanes                                                                                     |
+            | a,e       | abx,bcy,cdz,de,de | depart,turn right,turn left,turn right,arrive | ,straight:false right:true,left:false left:true straight:false,straight:false right:true, |
+
+    @anticipate
+    Scenario: Lane anticipation for multiple hops with same number of lanes
+        Given the node map
+            | a |   | b |   | x |   |   |
+            |   |   |   |   |   |   |   |
+            |   |   | c |   | d |   | z |
+            |   |   |   |   |   |   |   |
+            |   |   | y |   | e |   | f |
+            |   |   |   |   |   |   |   |
+            |   |   |   |   | w |   |   |
+
+        And the ways
+            | nodes | turn:lanes:forward           | name |
+            | ab    | through\|right\|right\|right | abx  |
+            | bx    |                              | abx  |
+            | bc    | left\|left\|through          | bcy  |
+            | cy    |                              | bcy  |
+            | cd    | through\|right\|right        | cdz  |
+            | dz    |                              | cdz  |
+            | de    | left\|through                | dew  |
+            | ew    |                              | dew  |
+            | ef    |                              | ef   |
+
+       When I route I should get
+            | waypoints | route                 | turns                                                   | lanes                                                                                                                                                  |
+            | a,f       | abx,bcy,cdz,dew,ef,ef | depart,turn right,turn left,turn right,turn left,arrive | ,straight:false right:false right:true right:false,left:false left:true straight:false,straight:false right:true right:false,left:true straight:false, |
+
+    @anticipate @todo @bug @2661
+    Scenario: Anticipate with lanes in roundabout: roundabouts as the unit of anticipation
+        Given the node map
+            |   |   | e |   |   |
+            | a | b |   | d | f |
+            |   |   | c |   |   |
+            |   |   |   |   |   |
+            |   |   | g |   |   |
+            | k | h |   | j | l |
+            |   |   | i |   |   |
+
+        And the ways
+            | nodes | turn:lanes:forward                      | highway | junction   | #   |
+            | ab    | slight_right\|slight_right\|slight_right | primary |            |     |
+            | bc    | slight_left\|slight_right\|slight_right  | primary | roundabout | top |
+            | cd    |                                         | primary | roundabout | top |
+            | de    |                                         | primary | roundabout | top |
+            | eb    |                                         | primary | roundabout | top |
+            | df    |                                         | primary |            |     |
+            | cg    | slight_right\|slight_right              | primary |            |     |
+            | gh    | slight_left\|slight_right               | primary | roundabout | bot |
+            | hi    |                                         | primary | roundabout | bot |
+            | ij    | slight_left\|slight_right               | primary | roundabout | bot |
+            | jg    |                                         | primary | roundabout | bot |
+            | hk    |                                         | primary |            |     |
+            | jl    |                                         | primary |            |     |
+
+        When I route I should get
+            | #           | waypoints | route       | turns                                             | lanes                                                                                          |
+            | right-right | a,k       | ab,cg,hk,hk | depart,roundabout-exit-1,roundabout-exit-1,arrive | ,slight right:false slight right:false slight right:true,slight right:false slight right:true, |
+            | right-left  | a,l       | ab,cg,jl,jl | depart,roundabout-exit-1,roundabout-exit-2,arrive | ,slight right:false slight right:false slight right:true,slight right:false slight right:true, |
+            | todo exits  | a,f       | ab,df,df    | depart,roundabout-exit-2,arrive                   | ,slight right:false slight right:false slight right:true,                                      |
+            | todo exits  | a,e       | ab,bc,eb    | depart,roundabout-exit-undefined,arrive           | ,slight right:true slight right:true slight right:true,                                        |
+
+    @anticipate @todo
+    Scenario: Roundabout with lanes only tagged on exit
+        Given the node map
+            |   |   | e |   |   |
+            | a | b |   | d | f |
+            |   |   | c |   |   |
+
+        And the ways
+            | nodes | turn:lanes:forward                    | highway | junction   |
+            | ab    |                                       | primary |            |
+            | bc    |                                       | primary | roundabout |
+            | cd    | slight_left\|slight_left\|slight_right | primary | roundabout |
+            | de    |                                       | primary | roundabout |
+            | eb    |                                       | primary | roundabout |
+            | df    |                                       | primary |            |
+
+        When I route I should get
+            | waypoints | route    | turns                                                 | lanes                                                    |
+            | a,f       | ab,df,df | depart,roundabout-exit-1,use lane slight right,arrive | ,,slight left:false slight left:false slight right:true, |
+
+    @anticipate
+    Scenario: Anticipate with lanes in roundabout where we stay on the roundabout for multiple exits
+        Given the node map
+            |   |   | a |   |   |
+            |   |   | b |   |   |
+            |   | c |   | g | h |
+            |   |   |   |   |   |
+            |   | d |   | f |   |
+            |   |   | e |   |   |
+            | x |   |   |   | y |
+
+        And the ways
+            | nodes | turn:lanes:forward         | highway | junction   |
+            | ab    | slight_right\|slight_right | primary |            |
+            | bc    |                            | primary | roundabout |
+            | cd    |                            | primary | roundabout |
+            | de    |                            | primary | roundabout |
+            | ef    |                            | primary | roundabout |
+            | fg    | slight_right               | primary | roundabout |
+            | gb    |                            | primary | roundabout |
+            | gh    |                            | primary |            |
+            | cx    |                            | primary |            |
+            | dx    |                            | primary |            |
+            | ey    |                            | primary |            |
+            | fy    |                            | primary |            |
+
+        When I route I should get
+            | waypoints | route    | turns                           | lanes                                  |
+            | a,h       | ab,gh,gh | depart,roundabout-exit-5,arrive | ,slight right:false slight right:true, |
+
+    @anticipate
+    Scenario: Departing or arriving inside a roundabout does not yet anticipate lanes
+        Given the node map
+            |   |   | a |   |   |
+            | x | b |   | d | y |
+            |   |   | c |   |   |
+
+        And the ways
+            | nodes | turn:lanes:forward         | highway | junction   | name       |
+            | xb    | slight_right\|slight_right | primary |            | xb         |
+            | dy    |                            | primary |            | dy         |
+            | ab    |                            | primary | roundabout | roundabout |
+            | bc    |                            | primary | roundabout | roundabout |
+            | cd    | left\|slight_right         | primary | roundabout | roundabout |
+            | da    |                            | primary | roundabout | roundabout |
+
+        When I route I should get
+            | waypoints | route                    | turns                                   | lanes                                  |
+            | x,y       | xb,dy,dy                 | depart,roundabout-exit-1,arrive         | ,slight right:false slight right:true, |
+            | x,c       | xb,roundabout,roundabout | depart,roundabout-exit-undefined,arrive | ,slight right:true slight right:true,  |
+            | x,a       | xb,roundabout,roundabout | depart,roundabout-exit-undefined,arrive | ,slight right:true slight right:true,  |
+
+    @anticipate
+    Scenario: Departing or arriving inside a roundabout does not yet anticipate lanes (BIG version)
+        Given the node map
+            |   |   | a |   |   |
+            | x | b |   | d | y |
+            |   |   |   |   |   |
+            |   |   |   |   |   |
+            |   |   |   |   |   |
+            |   |   |   |   |   |
+            |   |   |   |   |   |
+            |   |   |   |   |   |
+            |   |   |   |   |   |
+            |   |   |   |   |   |
+            |   |   |   |   |   |
+            |   |   |   |   |   |
+            |   |   |   |   |   |
+            |   |   |   |   |   |
+            |   |   |   |   |   |
+            |   |   |   |   |   |
+            |   |   |   |   |   |
+            |   |   |   |   |   |
+            |   |   |   |   |   |
+            |   |   |   |   |   |
+            |   |   |   |   |   |
+            |   |   |   |   |   |
+            |   |   |   |   |   |
+            |   |   |   |   |   |
+            |   |   |   |   |   |
+            |   |   |   |   |   |
+            |   |   |   |   |   |
+            |   |   |   |   |   |
+            |   |   |   |   |   |
+            |   |   |   |   |   |
+            |   |   |   |   |   |
+            |   |   |   |   |   |
+            |   |   |   |   |   |
+            |   |   |   |   |   |
+            |   |   |   |   |   |
+            |   |   |   |   |   |
+            |   |   | c |   |   |
+
+        And the ways
+            | nodes | turn:lanes:forward         | highway | junction   | name       |
+            | xb    | slight_right\|slight_right | primary |            | xb         |
+            | dy    |                            | primary |            | dy         |
+            | ab    |                            | primary | roundabout | roundabout |
+            | bc    |                            | primary | roundabout | roundabout |
+            | cd    | left\|slight_right         | primary | roundabout | roundabout |
+            | da    |                            | primary | roundabout | roundabout |
+
+        When I route I should get
+            | waypoints | route                    | turns                                   | lanes                                  |
+            | x,y       | xb,dy,dy                 | depart,roundabout-exit-1,arrive         | ,slight right:false slight right:true, |
+            | x,c       | xb,roundabout,roundabout | depart,roundabout-exit-undefined,arrive | ,slight right:true slight right:true,  |
+            | x,a       | xb,roundabout,roundabout | depart,roundabout-exit-undefined,arrive | ,slight right:true slight right:true,  |
+
+    @anticipate
+    Scenario: Anticipate Lanes for turns before and / or after roundabout
+        Given the node map
+            | a | b |   |   | x |
+            |   | c |   |   |   |
+            | d |   | f | g | z |
+            |   | e |   | h |   |
+            |   |   |   |   |   |
+            |   | y |   |   |   |
+
+        And the ways
+            | nodes | turn:lanes:forward                                 | highway | junction   | name  |
+            | ab    | through\|right\|right\|right\|right                | primary |            | abx   |
+            | bx    |                                                    | primary |            | abx   |
+            | bc    | right\|right\|right\|right                         | primary |            | bc    |
+            | cd    |                                                    | primary | roundabout | cdefc |
+            | de    | slight_left\|slight_left&slight_left\|slight_right | primary | roundabout | cdefc |
+            | ef    | left\|slight_right\|slight_right                   | primary | roundabout | cdefc |
+            | fc    |                                                    | primary | roundabout | cdefc |
+            | ey    |                                                    | primary |            | ey    |
+            | fg    | through\|right                                     | primary |            | fg    |
+            | gz    |                                                    | primary |            | gz    |
+            | gh    |                                                    | primary |            | gh    |
+
+        When I route I should get
+            | waypoints | route           | turns                                            | lanes                                                                                                                                    |
+            | a,h       | abx,bc,fg,gh,gh | depart,turn right,cdefc-exit-2,turn right,arrive | ,straight:false right:false right:false right:false right:true,right:false right:false right:false right:true,straight:false right:true, |
+
+    @anticipate @bug @todo
+    Scenario: Tripple Right keeping Left
+        Given the node map
+            | a |   |   |   | b |   | i |
+            |   |   |   |   |   |   |   |
+            |   |   |   |   |   |   |   |
+            | f |   | e |   |   |   | g |
+            |   |   |   |   |   |   |   |
+            |   |   |   |   |   |   |   |
+            |   | j | d |   | c |   |   |
+            |   |   |   |   | h |   |   |
+
+        And the ways
+            | nodes | turn:lanes:forward | highway   | name   |
+            | abi   | \|\|right\|right   | primary   | start  |
+            | bch   | \|\|right\|right   | primary   | first  |
+            | cdj   | \|\|right\|right   | primary   | second |
+            | de    | left\|right\|right | secondary | third  |
+            | feg   |                    | tertiary  | fourth |
+
+        When I route I should get
+            | waypoints | route                                  | turns                                                            | lanes                                                                                                                                                                      |
+            | a,f       | start,first,second,third,fourth,fourth | depart,turn right,turn right,turn right,end of road left,arrive  | ,none:false none:true right:false right:false,none:false none:true right:false right:false,none:false none:true right:false right:false,left:true right:false right:false, |
+            | a,g       | start,first,second,third,fourth,fourth | depart,turn right,turn right,turn right,end of road right,arrive | ,none:false none:false right:true right:true,none:false none:false right:true right:true,none:false none:false right:true right:true,left:false right:true right:true,     |
+
+    @anticipate @bug @todo
+    Scenario: Tripple Left keeping Right
+        Given the node map
+            | i |   | b |   |   |   | a |
+            |   |   |   |   |   |   |   |
+            |   |   |   |   |   |   |   |
+            | g |   |   |   | e |   | f |
+            |   |   |   |   |   |   |   |
+            |   |   |   |   |   |   |   |
+            |   |   | c |   | d | j |   |
+            |   |   | h |   |   |   |   |
+
+        And the ways
+            | nodes | turn:lanes:forward | highway   | name   |
+            | abi   | left\|left\|\|     | primary   | start  |
+            | bch   | left\|left\|\|     | primary   | first  |
+            | cdj   | left\|left\|\|     | primary   | second |
+            | de    | left\|left\|right  | secondary | third  |
+            | feg   |                    | tertiary  | fourth |
+
+        When I route I should get
+            | waypoints | route                                  | turns                                                         | lanes                                                                                                                                                               |
+            | a,f       | start,first,second,third,fourth,fourth | depart,turn left,turn left,turn left,end of road right,arrive | ,left:false left:false none:true none:false,left:false left:false none:true none:false,left:false left:false none:true none:false,left:false left:false right:true, |
+            | a,g       | start,first,second,third,fourth,fourth | depart,turn left,turn left,turn left,end of road left,arrive  | ,left:true left:true none:false none:false,left:true left:true none:false none:false,left:true left:true none:false none:false,left:true left:true right:false,     |
diff --git a/features/guidance/collapse.feature b/features/guidance/collapse.feature
index b5669ec..709cf7c 100644
--- a/features/guidance/collapse.feature
+++ b/features/guidance/collapse.feature
@@ -525,9 +525,9 @@ Feature: Collapse
             | cf    | secondary | bottom |
 
         When I route I should get
-            | waypoints | turns                                          | route               |
-            | a,d       | depart,continue right,end of road right,arrive | road,road,road,road |
-            | d,a       | depart,continue left,end of road left,arrive   | road,road,road,road |
+            | waypoints | turns                                   | route               |
+            | a,d       | depart,continue right,turn right,arrive | road,road,road,road |
+            | d,a       | depart,continue left,turn left,arrive   | road,road,road,road |
 
     Scenario: Forking before a turn
         Given the node map
@@ -606,13 +606,13 @@ Feature: Collapse
             | restriction | bc       | fdcg   | c        | no_right_turn |
 
         When I route I should get
-          | waypoints | route                 | turns                                          |
-          | a,g       | road,cross,cross      | depart,turn left,arrive                        |
-          | a,e       | road,road,road        | depart,continue slight right,arrive            |
-          # We should discuss whether the next item should be collapsed to depart,turn right,arrive.
-          | a,f       | road,road,cross,cross | depart,continue slight right,turn right,arrive |
+            | waypoints | route                 | turns                                          |
+            | a,g       | road,cross,cross      | depart,turn left,arrive                        |
+            | a,e       | road,road,road        | depart,continue slight right,arrive            |
+            # We should discuss whether the next item should be collapsed to depart,turn right,arrive.
+            | a,f       | road,road,cross,cross | depart,continue slight right,turn right,arrive |
 
-     Scenario: On-Off on Highway
+    Scenario: On-Off on Highway
         Given the node map
             | f |   |   |   |
             | a | b | c | d |
diff --git a/features/guidance/dedicated-turn-roads.feature b/features/guidance/dedicated-turn-roads.feature
index 3513ad3..30cb87e 100644
--- a/features/guidance/dedicated-turn-roads.feature
+++ b/features/guidance/dedicated-turn-roads.feature
@@ -57,20 +57,21 @@ Feature: Slipways and Dedicated Turn Lanes
 
     Scenario: Inner city expressway with on road
         Given the node map
-            | a | b |   |   |   | c |
-            |   |   |   |   | f |   |
-            |   |   |   |   |   |   |
-            |   |   |   |   |   |   |
-            |   |   |   |   |   |   |
-            |   |   |   |   |   | d |
-            |   |   |   |   |   |   |
-            |   |   |   |   |   |   |
-            |   |   |   |   |   |   |
-            |   |   |   |   |   | e |
+            | a | b |   |   |   | c | g |
+            |   |   |   |   | f |   |   |
+            |   |   |   |   |   |   |   |
+            |   |   |   |   |   |   |   |
+            |   |   |   |   |   |   |   |
+            |   |   |   |   |   | d |   |
+            |   |   |   |   |   |   |   |
+            |   |   |   |   |   |   |   |
+            |   |   |   |   |   |   |   |
+            |   |   |   |   |   | e |   |
 
         And the ways
             | nodes | highway      | name  |
             | abc   | primary      | road  |
+            | cg    | primary      | road  |
             | bfd   | trunk_link   |       |
             | cde   | trunk        | trunk |
 
@@ -123,3 +124,46 @@ Feature: Slipways and Dedicated Turn Lanes
        When I route I should get
             | waypoints | route          | turns                        |
             | a,f       | road,road,road | depart,continue uturn,arrive |
+
+    Scenario: Schwarzwaldstrasse Autobahn
+        Given the node map
+            |   |   |   |   | i |   |   |   |   |   | h |   |   |   |   | g |
+            |   |   | j |   |   |   |   |   |   |   |   |   |   |   |   |   |
+            | a |   |   |   |   |   |   | k |   |   |   |   |   |   |   |   |
+            |   |   |   | b |   | r | c |   | d |   | e |   |   |   |   | f |
+            |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |
+            |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |
+            |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |
+            |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |
+            |   |   |   |   |   | l |   |   |   |   |   |   |   |   |   |   |
+            |   |   |   |   |   | m |   |   |   |   |   |   |   |   |   |   |
+            |   |   |   |   |   |   | n |   | q |   |   |   |   |   |   |   |
+            |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |
+            |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |
+            |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |
+            |   |   |   |   |   |   | o |   | p |   |   |   |   |   |   |   |
+
+        And the nodes
+            # the traffic light at `l` is not actually in the data, but necessary for the test to check everything
+            # http://www.openstreetmap.org/#map=19/48.99211/8.40336
+            | node | highway         |
+            | r    | traffic_signals |
+            | l    | traffic_signals |
+
+        And the ways
+            | nodes | highway        | name               | ref  | oneway |
+            | abrcd | secondary      | Schwarzwaldstrasse | L561 | yes    |
+            | hija  | secondary      | Schwarzwaldstrasse | L561 | yes    |
+            | def   | secondary      | Ettlinger Strasse  |      | yes    |
+            | gh    | secondary      | Ettlinger Strasse  |      | yes    |
+            | blmn  | secondary_link |                    | L561 | yes    |
+            | hkc   | secondary_link | Ettlinger Strasse  |      | yes    |
+            | qdki  | secondary_link | Ettlinger Allee    |      | yes    |
+            | cn    | secondary_link | Ettlinger Allee    |      | yes    |
+            | no    | primary        | Ettlinger Allee    |      | yes    |
+            | pq    | primary        | Ettlinger Allee    |      | yes    |
+            | qe    | secondary_link | Ettlinger Allee    |      | yes    |
+
+        When I route I should get
+            | waypoints | route                                                     | turns                    |
+            | a,o       | Schwarzwaldstrasse (L561),Ettlinger Allee,Ettlinger Allee | depart,turn right,arrive |
diff --git a/features/guidance/end-of-road.feature b/features/guidance/end-of-road.feature
index b0bfac3..9ac6b72 100644
--- a/features/guidance/end-of-road.feature
+++ b/features/guidance/end-of-road.feature
@@ -8,116 +8,156 @@ Feature: End Of Road Instructions
     Scenario: End of Road with through street
         Given the node map
             |   |   | c |
-            | a |   | b |
-            |   |   | d |
+            | a | e | b |
+            |   | f | d |
 
         And the ways
             | nodes  | highway |
-            | ab     | primary |
+            | aeb    | primary |
             | cbd    | primary |
+            | ef     | 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 |
+            | waypoints | route       | turns                           |
+            | a,c       | aeb,cbd,cbd | depart,end of road left,arrive  |
+            | a,d       | aeb,cbd,cbd | depart,end of road right,arrive |
 
     Scenario: End of Road with three streets
         Given the node map
             |   |   | c |
-            | a |   | b |
-            |   |   | d |
+            | a | e | b |
+            |   | f | d |
 
         And the ways
             | nodes  | highway |
-            | ab     | primary |
+            | aeb    | primary |
             | cb     | primary |
             | bd     | primary |
+            | ef     | 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 |
+            | waypoints | route     | turns                           |
+            | a,c       | aeb,cb,cb | depart,end of road left,arrive  |
+            | a,d       | aeb,bd,bd | depart,end of road right,arrive |
 
     Scenario: End of Road with three streets, slightly angled
         Given the node map
-            | a |   |   |   |   | c |
-            |   |   |   |   |   | b |
+            | a | e |   |   |   | c |
+            |   | f |   |   |   | b |
             |   |   |   |   |   | d |
 
         And the ways
             | nodes  | highway |
-            | ab     | primary |
+            | aeb    | primary |
             | cb     | primary |
             | bd     | primary |
+            | ef     | 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 |
+            | waypoints | route     | turns                           |
+            | a,c       | aeb,cb,cb | depart,end of road left,arrive  |
+            | a,d       | aeb,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 |
+            |   | f |   |   |   | b |
+            | a | e |   |   |   | d |
 
         And the ways
             | nodes  | highway |
-            | ab     | primary |
+            | aeb    | primary |
+            | ef     | 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 |
+            | waypoints | route     | turns                           |
+            | a,c       | aeb,cb,cb | depart,end of road left,arrive  |
+            | a,d       | aeb,bd,bd | depart,end of road right,arrive |
 
     Scenario: End of Road with through street, slightly angled
         Given the node map
-            | a |   |   |   |   | c |
-            |   |   |   |   |   | b |
+            | a | e |   |   |   | c |
+            |   | f |   |   |   | b |
             |   |   |   |   |   | d |
 
         And the ways
             | nodes  | highway |
-            | ab     | primary |
+            | aeb    | primary |
+            | ef     | 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 |
+            | waypoints | route       | turns                           |
+            | a,c       | aeb,cbd,cbd | depart,end of road left,arrive  |
+            | a,d       | aeb,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 |
+            |   | f |   |   |   | b |
+            | a | e |   |   |   | d |
 
         And the ways
             | nodes  | highway |
-            | ab     | primary |
+            | aeb    | primary |
+            | ef     | 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 |
+            | waypoints | route       | turns                           |
+            | a,c       | aeb,cbd,cbd | depart,end of road left,arrive  |
+            | a,d       | aeb,cbd,cbd | depart,end of road right,arrive |
 
     Scenario: End of Road with two ramps - prefer ramp over end of road
         Given the node map
             |   |   | c |
-            | a |   | b |
-            |   |   | d |
+            | a | e | b |
+            |   | f | d |
 
         And the ways
             | nodes  | highway       |
-            | ab     | primary       |
+            | aeb    | primary       |
+            | ef     | primary       |
             | bc     | motorway_link |
             | bd     | motorway_link |
 
        When I route I should get
-            | waypoints | route    | turns                       |
-            | a,c       | ab,bc,bc | depart,on ramp left,arrive  |
-            | a,d       | ab,bd,bd | depart,on ramp right,arrive |
+            | waypoints | route     | turns                       |
+            | a,c       | aeb,bc,bc | depart,on ramp left,arrive  |
+            | a,d       | aeb,bd,bd | depart,on ramp right,arrive |
 
+    # http://www.openstreetmap.org/#map=19/52.49907/13.41836
+    @end-of-road @negative
+    Scenario: Don't Handle Circles as End-Of-Road
+        Given the node map
+            |   | r |   |   |   | q |   |   |   |   |   |   |
+            |   |   |   |   |   | a | s |   |   |   |   |   |
+            |   |   |   | b |   |   |   |   |   |   |   |   |
+            |   |   |   |   |   |   |   | j |   |   |   |   |
+            |   |   |   |   |   |   |   |   |   |   |   |   |
+            | l |   | c |   |   |   |   | i |   |   |   | k |
+            |   |   |   |   |   |   |   |   |   |   |   |   |
+            |   |   |   |   |   |   |   | h |   |   |   |   |
+            | m |   |   |   |   |   |   |   |   |   |   |   |
+            |   |   | d |   |   |   |   |   |   |   |   | n |
+            |   |   |   | e |   |   | g |   |   |   |   |   |
+            |   |   |   |   | f |   |   |   |   |   |   |   |
+            |   |   |   |   | o |   | p |   |   |   |   |   |
+
+        And the ways
+            | nodes        | highway     | name  | oneway |
+            | abcdefghijsa | secondary   | kotti | yes    |
+            | ki           | secondary   | skal  | yes    |
+            | cl           | secondary   | skal  | yes    |
+            | md           | secondary   | skal  | yes    |
+            | gn           | secondary   | skal  | yes    |
+            | qa           | tertiary    | adal  | no     |
+            | br           | residential | rei   | yes    |
+            | fo           | secondary   | kstr  | yes    |
+            | pg           | secondary   | kstr  | yes    |
+
+        When I route I should get
+            | waypoints | route                | turns                               | #                                                      |
+            | k,l       | skal,kotti,skal,skal | depart,turn right,turn right,arrive | # could be a case to find better turn instructions for |
diff --git a/features/guidance/fork.feature b/features/guidance/fork.feature
index 835b772..2c8d592 100644
--- a/features/guidance/fork.feature
+++ b/features/guidance/fork.feature
@@ -296,3 +296,21 @@ Feature: Fork Instructions
             | waypoints | route     | turns                           |
             | a,c       | abc,abc   | depart,arrive                   |
             | a,d       | abc,bd,bd | depart,turn slight right,arrive |
+
+     Scenario: Fork on motorway links - don't fork on through
+        Given the node map
+            | i |   |   |   |   | a |
+            | j |   | c | b |   | x |
+
+        And the ways
+            | nodes | name | highway       |
+            | xb    | xbcj | motorway_link |
+            | bc    | xbcj | motorway_link |
+            | cj    | xbcj | motorway_link |
+            | ci    | off  | motorway_link |
+            | ab    | on   | motorway_link |
+
+        When I route I should get
+            | waypoints | route           | turns                                             |
+            | a,j       | on,xbcj,xbcj    | depart,merge slight left,arrive                   |
+            | a,i       | on,xbcj,off,off | depart,merge slight left,turn slight right,arrive |
diff --git a/features/guidance/intersections.feature b/features/guidance/intersections.feature
index 22ccac6..78f3fdf 100644
--- a/features/guidance/intersections.feature
+++ b/features/guidance/intersections.feature
@@ -17,8 +17,8 @@ Feature: Intersections Data
             | bd     | corner  |
 
        When I route I should get
-            | waypoints | route           | turns         | intersections                               |
-            | a,c       | through,through | depart,arrive | true:90,true:90 true:180 false:270;true:270 |
+            | waypoints | route           | intersections                               |
+            | a,c       | through,through | true:90,true:90 true:180 false:270;true:270 |
 
     Scenario: Passing Three Way North
         Given the node map
@@ -32,8 +32,8 @@ Feature: Intersections Data
             | bd     | corner  |
 
        When I route I should get
-            | waypoints | route           | turns         | intersections                             |
-            | a,c       | through,through | depart,arrive | true:90,true:0 true:90 false:270;true:270 |
+            | waypoints | route           | intersections                             |
+            | a,c       | through,through | true:90,true:0 true:90 false:270;true:270 |
 
     Scenario: Passing Oneway Street In
         Given the node map
@@ -47,8 +47,8 @@ Feature: Intersections Data
             | db     | corner  | yes    |
 
        When I route I should get
-            | waypoints | route           | turns         | intersections                              |
-            | a,c       | through,through | depart,arrive | true:90,false:0 true:90 false:270;true:270 |
+            | waypoints | route           | intersections                              |
+            | a,c       | through,through | true:90,false:0 true:90 false:270;true:270 |
 
     Scenario: Passing Oneway Street Out
         Given the node map
@@ -62,8 +62,8 @@ Feature: Intersections Data
             | bd     | corner  | yes    |
 
        When I route I should get
-            | waypoints | route           | turns         | intersections                             |
-            | a,c       | through,through | depart,arrive | true:90,true:0 true:90 false:270;true:270 |
+            | waypoints | route           | intersections                             |
+            | a,c       | through,through | true:90,true:0 true:90 false:270;true:270 |
 
     Scenario: Passing Two Intersections
         Given the node map
@@ -80,27 +80,8 @@ Feature: Intersections Data
             | cf     | corner  |
 
        When I route I should get
-            | waypoints | route           | turns         | intersections                                                        |
-            | a,d       | through,through | depart,arrive | true:90,true:0 true:90 false:270,true:90 true:180 false:270;true:270 |
-
-    Scenario: Regression test #2424
-        Given the node map
-            |   |   | e |   |   |   |   |   | i |   |   |   |   |
-            | a |   | b |   | c |   | d |   | h |   | k |   | m |
-            |   |   |   |   | f |   |   |   |   |   | l |   |   |
-
-        And the ways
-            | nodes  | name    |
-            | abcd   | Fritz-Elsas-Straße |
-            | hkm    | Fritz-Elsas-Straße |
-            | dhi    | Martin-Luther-Straße |
-            | be     | corner  |
-            | kl     | corner  |
-            | cf     | corner  |
-
-       When I route I should get
-            | waypoints | route | turns         |
-            | a,m       | Fritz-Elsas-Straße,Fritz-Elsas-Straße| depart,arrive |
+            | waypoints | route           | intersections                                                        |
+            | a,d       | through,through | true:90,true:0 true:90 false:270,true:90 true:180 false:270;true:270 |
 
     Scenario: Passing Two Intersections, Collapsing
         Given the node map
@@ -117,9 +98,9 @@ Feature: Intersections Data
             | cf     | corner        |
 
        When I route I should get
-            | waypoints | route                  | turns                          | intersections                                                        |
-            | a,d       | through,through        | depart,arrive                  | true:90,true:0 true:90 false:270,true:90 true:180 false:270;true:270 |
-            | f,a       | corner,through,through | depart,end of road left,arrive | true:0;true:90 false:180 true:270,true:0 false:90 true:270;true:90   |
+            | waypoints | route                  | intersections                                                        |
+            | a,d       | through,through        | true:90,true:0 true:90 false:270,true:90 true:180 false:270;true:270 |
+            | f,a       | corner,through,through | true:0;true:90 false:180 true:270,true:0 false:90 true:270;true:90   |
 
     Scenario: Roundabouts
         Given the node map
@@ -144,10 +125,10 @@ Feature: Intersections Data
             | hd    |            |
 
         When I route I should get
-            | waypoints | route          | turns                              | intersections                                                                                                                 |
-            | e,f       | ea,fb,fb       | depart,abcda-exit-1,arrive         | true:180;false:0 false:150 true:210,false:30 true:150 true:270;true:90                                                        |
-            | e,g       | ea,gc,gc       | depart,abcda-exit-2,arrive         | true:180;false:0 false:150 true:210,false:30 true:150 true:270,true:30 true:180 false:330;true:0                              |
-            | e,h       | ea,hd,hd       | depart,abcda-exit-3,arrive         | true:180;false:0 false:150 true:210,false:30 true:150 true:270,true:30 true:180 false:330,true:90 false:210 true:330;true:270 |
-            | e,2       | ea,abcda,abcda | depart,abcda-exit-undefined,arrive | true:180;false:0 false:150 true:210,false:30 true:150 true:270;true:327 +-1                                                   |
-            | 1,g       | abcda,gc,gc    | depart,abcda-exit-2,arrive         | true:214;false:30 true:150 true:270,true:30 true:180 false:330;true:0                                                         |
-            | 1,3       | abcda,abcda    | depart,arrive                      | true:214,false:30 true:150 true:270,true:30 true:180 false:330;true:214                                                       |
+            | waypoints | route          | intersections                                                                                                                 |
+            | e,f       | ea,fb,fb       | true:180;false:0 false:150 true:210,false:30 true:150 true:270;true:90                                                        |
+            | e,g       | ea,gc,gc       | true:180;false:0 false:150 true:210,false:30 true:150 true:270,true:30 true:180 false:330;true:0                              |
+            | e,h       | ea,hd,hd       | true:180;false:0 false:150 true:210,false:30 true:150 true:270,true:30 true:180 false:330,true:90 false:210 true:330;true:270 |
+            | e,2       | ea,abcda,abcda | true:180;false:0 false:150 true:210,false:30 true:150 true:270;true:327 +-1                                                   |
+            | 1,g       | abcda,gc,gc    | true:214;false:30 true:150 true:270,true:30 true:180 false:330;true:0                                                         |
+            | 1,3       | abcda,abcda    | true:214,false:30 true:150 true:270,true:30 true:180 false:330;true:214                                                       |
diff --git a/features/guidance/merge.feature b/features/guidance/merge.feature
index 0736e8f..0190c8b 100644
--- a/features/guidance/merge.feature
+++ b/features/guidance/merge.feature
@@ -50,3 +50,27 @@ Feature: Merging
             | waypoints | route      | turns                           |
             | d,c       | db,abc,abc | depart,merge slight left,arrive |
 
+    Scenario: Merge onto a turning road
+        Given the node map
+            |   |   |   |   |   |   | e |
+            |   |   |   |   |   |   |   |
+            |   |   |   |   |   |   |   |
+            |   |   |   |   |   |   |   |
+            |   |   |   |   |   | d |   |
+            |   |   |   |   |   |   |   |
+            |   |   |   |   |   |   |   |
+            |   |   |   |   |   |   |   |
+            |   |   |   |   |   |   |   |
+            |   |   |   |   | c |   |   |
+            |   |   |   | b |   |   |   |
+            | a |   |   |   |   |   | f |
+
+        And the ways
+            | nodes | highway     | name |
+            | abcde | primary     | road |
+            | fd    | residential | in   |
+
+        When I route I should get
+            | waypoints | turns                           | route        |
+            | f,e       | depart,merge slight left,arrive | in,road,road |
+            | f,a       | depart,turn sharp left,arrive  | in,road,road |
diff --git a/features/guidance/motorway.feature b/features/guidance/motorway.feature
index f39c077..f560a3b 100644
--- a/features/guidance/motorway.feature
+++ b/features/guidance/motorway.feature
@@ -216,3 +216,21 @@ Feature: Motorway Guidance
             | waypoints | route      | turns                            |
             | d,c       | db,abc,abc | depart,merge slight left,arrive  |
             | e,c       | eb,abc,abc | depart,merge slight right,arrive |
+
+    Scenario: Handle 90 degree off ramps correctly
+        Given the node map
+            | a |   |   |   |   |
+            | x | b |   | c | y |
+            |   |   |   | d |   |
+
+        And the ways
+            | nodes | name | highway       | oneway |
+            | ab    | On   | motorway_link | yes    |
+            | xb    | Hwy  | motorway      |        |
+            | bc    | Hwy  | motorway      |        |
+            | cd    | Off  | motorway_link | yes    |
+            | cy    | Hwy  | motorway      |        |
+
+       When I route I should get
+            | waypoints | route               | turns                                           |
+            | a,d       | On,Hwy,Off,Off      | depart,merge slight right,off ramp right,arrive |
diff --git a/features/guidance/post-processing.feature b/features/guidance/post-processing.feature
new file mode 100644
index 0000000..27e19b0
--- /dev/null
+++ b/features/guidance/post-processing.feature
@@ -0,0 +1,22 @@
+ at routing  @guidance @post-processing
+Feature: General Post-Processing related features
+
+    Background:
+        Given the profile "car"
+        Given a grid size of 10 meters
+
+    # this testcase used to crash geometry generation (at that time handled during intersection generation)
+    Scenario: Regression test #2424
+        Given the node map
+            |   |   | e |   |   |   |   |   | i |   |   |   |   |
+            | a |   | b |   | c |   | d |   | h |   | k |   | m |
+            |   |   |   |   | f |   |   |   |   |   | l |   |   |
+
+        And the ways
+            | nodes  | name                 |
+            | abcd   | Fritz-Elsas-Straße   |
+            | hkm    | Fritz-Elsas-Straße   |
+            | dhi    | Martin-Luther-Straße |
+            | be     | corner               |
+            | kl     | corner               |
+            | cf     | corner               |
diff --git a/features/guidance/roundabout.feature b/features/guidance/roundabout.feature
index 9b622f6..fe6605b 100644
--- a/features/guidance/roundabout.feature
+++ b/features/guidance/roundabout.feature
@@ -437,3 +437,42 @@ Feature: Basic Roundabout
         When I route I should get
             | waypoints | route                                                   | turns                                                     |
             | a,k       | massachusetts,massachusetts,massachusetts,massachusetts | depart,sheridan circle-exit-2,dupont circle-exit-1,arrive |
+
+    Scenario: Enter and Exit - Traffic Signals
+        Given the node map
+            |   |   | a |   |   |
+            |   | i | b | l |   |
+            | h | g |   | c | d |
+            |   | j | e | k |   |
+            |   |   | f |   |   |
+
+       And the nodes
+            | node | highway         |
+            | i    | traffic_signals |
+            | j    | traffic_signals |
+            | k    | traffic_signals |
+            | l    | traffic_signals |
+
+       And the ways
+            | nodes     | junction   |
+            | ab        |            |
+            | cd        |            |
+            | ef        |            |
+            | gh        |            |
+            | bigjekclb | roundabout |
+
+       When I route I should get
+           | waypoints | route    | turns                           |
+           | a,d       | ab,cd,cd | depart,roundabout-exit-3,arrive |
+           | a,f       | ab,ef,ef | depart,roundabout-exit-2,arrive |
+           | a,h       | ab,gh,gh | depart,roundabout-exit-1,arrive |
+           | d,f       | cd,ef,ef | depart,roundabout-exit-3,arrive |
+           | d,h       | cd,gh,gh | depart,roundabout-exit-2,arrive |
+           | d,a       | cd,ab,ab | depart,roundabout-exit-1,arrive |
+           | f,h       | ef,gh,gh | depart,roundabout-exit-3,arrive |
+           | f,a       | ef,ab,ab | depart,roundabout-exit-2,arrive |
+           | f,d       | ef,cd,cd | depart,roundabout-exit-1,arrive |
+           | h,a       | gh,ab,ab | depart,roundabout-exit-3,arrive |
+           | h,d       | gh,cd,cd | depart,roundabout-exit-2,arrive |
+           | h,f       | gh,ef,ef | depart,roundabout-exit-1,arrive |
+
diff --git a/features/guidance/turn-lanes.feature b/features/guidance/turn-lanes.feature
new file mode 100644
index 0000000..0a8e0e7
--- /dev/null
+++ b/features/guidance/turn-lanes.feature
@@ -0,0 +1,739 @@
+ at routing @guidance @turn-lanes
+Feature: Turn Lane Guidance
+
+    Background:
+        Given the profile "car"
+        Given a grid size of 20 meters
+
+    @bug
+    Scenario: Basic Turn Lane 3-way Turn with empty lanes
+        Given the node map
+            | a |   | b |   | c |
+            |   |   | d |   |   |
+
+        And the ways
+            | nodes  | turn:lanes     | turn:lanes:forward | turn:lanes:backward | name     |
+            | ab     |                | through\|right     |                     | in       |
+            | bc     |                |                    | left\|through\|\|   | straight |
+            | bd     |                |                    | left\|right         | right    |
+
+       When I route I should get
+            | waypoints | route                | turns                           | lanes                                            |
+            | a,c       | in,straight,straight | depart,new name straight,arrive | ,straight:true right:false,                      |
+            | a,d       | in,right,right       | depart,turn right,arrive        | ,straight:false right:true,                      |
+            | c,a       | straight,in,in       | depart,new name straight,arrive | ,left:false straight:true none:true none:true,   |
+            | c,d       | straight,right,right | depart,turn left,arrive         | ,left:true straight:false none:false none:false, |
+
+    Scenario: Basic Turn Lane 4-Way Turn
+        Given the node map
+            |   |   | e |   |   |
+            | a |   | b |   | c |
+            |   |   | d |   |   |
+
+        And the ways
+            | nodes  | turn:lanes     | turn:lanes:forward | turn:lanes:backward | name     |
+            | ab     |                | \|right            |                     | in       |
+            | bc     |                |                    |                     | straight |
+            | bd     |                |                    | left\|              | right    |
+            | be     |                |                    |                     | left     |
+
+       When I route I should get
+            | waypoints | route                   | turns                           | lanes                   |
+            | a,c       | in,straight,straight    | depart,new name straight,arrive | ,none:true right:false, |
+            | a,d       | in,right,right          | depart,turn right,arrive        | ,none:false right:true, |
+            | a,e       | in,left,left            | depart,turn left,arrive         | ,none:true right:false, |
+            | d,a       | right,in,in             | depart,turn left,arrive         | ,left:true none:false,  |
+            | d,e       | right,left,left         | depart,new name straight,arrive | ,left:false none:true,  |
+            | d,c       | right,straight,straight | depart,turn right,arrive        | ,left:false none:true,  |
+
+    Scenario: Basic Turn Lane 4-Way Turn using none
+        Given the node map
+            |   |   | e |   |   |
+            | a |   | b |   | c |
+            |   |   | d |   |   |
+
+        And the ways
+            | nodes  | turn:lanes     | turn:lanes:forward | turn:lanes:backward | name     |
+            | ab     |                | none\|right        |                     | in       |
+            | bc     |                |                    |                     | straight |
+            | bd     |                |                    | left\|none          | right    |
+            | be     |                |                    |                     | left     |
+
+       When I route I should get
+            | waypoints | route                | turns                           | lanes                   |
+            | a,c       | in,straight,straight | depart,new name straight,arrive | ,none:true right:false, |
+            | a,d       | in,right,right       | depart,turn right,arrive        | ,none:false right:true, |
+            | a,e       | in,left,left         | depart,turn left,arrive         | ,none:true right:false, |
+
+    Scenario: Basic Turn Lane 4-Way With U-Turn Lane
+        Given the node map
+            |   |   | e |   |   |
+            | a | 1 | b |   | c |
+            |   |   | d |   |   |
+
+        And the ways
+            | nodes  | turn:lanes     | turn:lanes:forward          | name     |
+            | ab     |                | reverse;left\|through;right | in       |
+            | bc     |                |                             | straight |
+            | bd     |                |                             | right    |
+            | be     |                |                             | left     |
+
+       When I route I should get
+            | from | to | bearings        | route                | turns                           | lanes                                  |
+            | a    | c  | 180,180 180,180 | in,straight,straight | depart,new name straight,arrive | ,left;uturn:false straight;right:true, |
+            | a    | d  | 180,180 180,180 | in,right,right       | depart,turn right,arrive        | ,left;uturn:false straight;right:true, |
+            | a    | e  | 180,180 180,180 | in,left,left         | depart,turn left,arrive         | ,left;uturn:true straight;right:false, |
+            | 1    | a  | 90,2 270,2      | in,in,in             | depart,turn uturn,arrive        | ,left;uturn:true straight;right:false, |
+
+
+    #this next test requires decision on how to announce lanes for going straight if there is no turn
+    Scenario: Turn with Bus-Lane
+        Given the node map
+            | a |   | b |   | c |
+            |   |   |   |   |   |
+            |   |   | d |   |   |
+
+        And the ways
+            | nodes | name | turn:lanes:forward | lanes:psv:forward |
+            | ab    | road | through\|right\|   | 1                 |
+            | bc    | road |                    |                   |
+            | bd    | turn |                    |                   |
+
+        When I route I should get
+            | waypoints | route          | turns                    | lanes                       |
+            | a,d       | road,turn,turn | depart,turn right,arrive | ,straight:false right:true, |
+            | a,c       | road,road      | depart,arrive            | ,                           |
+
+    @PROFILE @LANES
+    Scenario: Turn with Bus-Lane but without lanes
+        Given the node map
+            | a |   | b |   | c |
+            |   |   |   |   |   |
+            |   |   | d |   |   |
+
+        And the ways
+            | nodes | name | lanes:psv |
+            | ab    | road | 1         |
+            | bc    | road | yes       |
+            | bd    | turn |           |
+
+        When I route I should get
+            | waypoints | route          | turns                    |
+            | a,d       | road,turn,turn | depart,turn right,arrive |
+            | a,c       | road,road      | depart,arrive            |
+
+    #turn lanes are often drawn at the incoming road, even though the actual turn requires crossing the intersection first
+    @todo @bug
+    Scenario: Turn Lanes at Segregated Road
+        Given the node map
+            |   |   | i | l |   |   |
+            |   |   |   |   |   |   |
+            | h |   | g | f |   | e |
+            | a |   | b | c |   | d |
+            |   |   |   |   |   |   |
+            |   |   | j | k |   |   |
+
+        And the ways
+            | nodes | name  | turn:lanes:forward       | oneway |
+            | ab    | road  | left\|through\|right     | yes    |
+            | bc    | road  | left\|through            | yes    |
+            | cd    | road  |                          | yes    |
+            | ef    | road  | \|through\|through;right | yes    |
+            | fg    | road  | left;through\|through\|  | yes    |
+            | gh    | road  |                          | yes    |
+            | ig    | cross |                          | yes    |
+            | gb    | cross | left\|through            | yes    |
+            | bj    | cross |                          | yes    |
+            | kc    | cross | left\|through;right      | yes    |
+            | cf    | cross | left\|through            | yes    |
+            | fl    | cross |                          | yes    |
+
+        When I route I should get
+            | waypoints | route             | turns                           | lanes                                           |
+            | a,j       | road,cross,cross  | depart,turn right,arrive        | ,left:false straight:false right:true           |
+            | a,d       | road,road,road    | depart,use lane straight,arrive | ,left:false straight:true right:false,          |
+            | a,l       | road,cross,cross  | depart,turn left,arrive         | ,left:true straight:false right:false,          |
+            | a,h       | road,road,road    | depart,continue uturn,arrive    | ,left:true straight:false right:false,          |
+            | k,d       | cross,road,road   | depart,turn right,arrive        | ,left:false straight;right:true,                |
+            | k,l       | cross,cross,cross | depart,use lane straight,arrive | ,left:false straight;right:true,                |
+            | k,h       | cross,road,road   | depart,turn left,arrive         | ,left:true straight;right:false,                |
+            | k,j       | cross,cross,cross | depart,continue uturn,arrive    | ,left:true straight;right:false,                |
+            | e,l       | road,cross,cross  | depart,turn right,arrive        | ,none:false straight:false straight;right:true, |
+            | e,h       | road,road         | depart,arrive                   | ,none:false straight:true straight;right:true   |
+            | e,j       | road,cross,cross  | depart,turn left,arrive         | ,none:true straight:false straight;right:false, |
+            | e,d       | road,road,road    | depart,continue uturn,arrive    | ,none:true straight:false straight;right:false, |
+            | i,h       | cross,road,road   | depart,turn right,arrive        | ,,                                              |
+            | i,j       | cross,cross,cross | depart,use lane straight,arrive | ,left:false straight:true,                      |
+            | i,d       | cross,road,road   | depart,turn left,arrive         | ,left:true straight:false,                      |
+            | i,l       | cross,cross,cross | depart,continue uturn,arrive    | ,left:true straight:false,                      |
+
+    #copy of former case to prevent further regression
+    Scenario: Turn Lanes at Segregated Road
+        Given the node map
+            |   |   | i | l |   |   |
+            |   |   |   |   |   |   |
+            | h |   | g | f |   | e |
+            | a |   | b | c |   | d |
+            |   |   |   |   |   |   |
+            |   |   | j | k |   |   |
+
+        And the ways
+            | nodes | name  | turn:lanes:forward       | oneway |
+            | ab    | road  | left\|through\|right     | yes    |
+            | bc    | road  | left\|through            | yes    |
+            | cd    | road  |                          | yes    |
+            | ef    | road  | \|through\|through;right | yes    |
+            | fg    | road  | left;through\|through\|  | yes    |
+            | gh    | road  |                          | yes    |
+            | ig    | cross |                          | yes    |
+            | gb    | cross | left\|through            | yes    |
+            | bj    | cross |                          | yes    |
+            | kc    | cross | left\|through;right      | yes    |
+            | cf    | cross | left\|through            | yes    |
+            | fl    | cross |                          | yes    |
+
+        When I route I should get
+            | waypoints | route             | turns                        | lanes                                           |
+            | a,j       | road,cross,cross  | depart,turn right,arrive     | ,left:false straight:false right:true,          |
+            | k,d       | cross,road,road   | depart,turn right,arrive     | ,left:false straight;right:true,                |
+            | e,l       | road,cross,cross  | depart,turn right,arrive     | ,none:false straight:false straight;right:true, |
+            | i,h       | cross,road,road   | depart,turn right,arrive     | ,,                                              |
+            | i,j       | cross,cross       | depart,arrive                | ,                                               |
+            | i,l       | cross,cross,cross | depart,continue uturn,arrive | ,left:true straight:false,                      |
+
+    Scenario: Turn Lanes at Segregated Road
+        Given the node map
+            |   |   | g | f |   |   |
+            | a |   | b | c |   | d |
+            |   |   |   |   |   |   |
+            |   |   | j | k |   |   |
+
+        And the ways
+            | nodes | name  | turn:lanes:forward   | oneway |
+            | ab    | road  | left\|through\|right | yes    |
+            | bc    | road  |                      | yes    |
+            | cd    | road  |                      | yes    |
+            | gb    | cross |                      | yes    |
+            | bj    | cross |                      | yes    |
+            | kc    | cross |                      | yes    |
+            | cf    | cross |                      | yes    |
+
+        When I route I should get
+            | waypoints | route             | turns                           | lanes                                  |
+            | a,j       | road,cross,cross  | depart,turn right,arrive        | ,left:false straight:false right:true, |
+
+    #this can happen due to traffic lights / lanes not drawn up to the intersection itself
+    Scenario: Turn Lanes Given earlier than actual turn
+        Given the node map
+            | a |   | b | c |   | d |
+            |   |   |   |   |   |   |
+            |   |   |   | e |   |   |
+
+        And the ways
+            | nodes | name | turn:lanes:forward |
+            | ab    | road | \|right            |
+            | bc    | road |                    |
+            | cd    | road |                    |
+            | ce    | turn |                    |
+
+        When I route I should get
+            | waypoints | route          | turns                    | lanes                   |
+            | a,e       | road,turn,turn | depart,turn right,arrive | ,none:false right:true, |
+            | a,d       | road,road      | depart,arrive            | ,                       |
+
+    Scenario: Turn Lanes Given earlier than actual turn
+        Given the node map
+            | a |   | b | c | d |   | e |   | f | g | h |   | i |
+            |   |   | j |   |   |   |   |   |   |   | k |   |   |
+
+        And the ways
+            | nodes | name        | turn:lanes:forward | turn:lanes:backward |
+            | abc   | road        |                    |                     |
+            | cd    | road        |                    | left\|              |
+            | def   | road        |                    |                     |
+            | fg    | road        | \|right            |                     |
+            | ghi   | road        |                    |                     |
+            | bj    | first-turn  |                    |                     |
+            | hk    | second-turn |                    |                     |
+
+        When I route I should get
+            | waypoints | route                        | turns                    | lanes                   |
+            | a,k       | road,second-turn,second-turn | depart,turn right,arrive | ,none:false right:true, |
+            | a,i       | road,road                    | depart,arrive            | ,                       |
+            | i,j       | road,first-turn,first-turn   | depart,turn left,arrive  | ,left:true none:false,  |
+            | i,a       | road,road                    | depart,arrive            | ,                       |
+
+    Scenario: Passing a one-way street
+        Given the node map
+            | e |   |   | f |   |
+            | a |   | b | c | d |
+
+        And the ways
+            | nodes | name | turn:lanes:forward | oneway |
+            | ab    | road | left\|through      | no     |
+            | bcd   | road |                    | no     |
+            | eb    | owi  |                    | yes    |
+            | cf    | turn |                    |        |
+
+        When I route I should get
+            | waypoints | route          | turns                   | lanes                      |
+            | a,f       | road,turn,turn | depart,turn left,arrive | ,left:true straight:false, |
+
+    Scenario: Passing a one-way street, partly pulled back lanes
+        Given the node map
+            | e |   |   | f |   |
+            | a |   | b | c | d |
+            |   |   | g |   |   |
+
+        And the ways
+            | nodes | name  | turn:lanes:forward  | oneway |
+            | ab    | road  | left\|through;right | no     |
+            | bcd   | road  |                     | no     |
+            | eb    | owi   |                     | yes    |
+            | cf    | turn  |                     | no     |
+            | bg    | right |                     | no     |
+
+        When I route I should get
+            | waypoints | route            | turns                    | lanes                            |
+            | a,f       | road,turn,turn   | depart,turn left,arrive  | ,left:true straight;right:false, |
+            | a,g       | road,right,right | depart,turn right,arrive | ,left:false straight;right:true, |
+
+    Scenario: Passing a one-way street, partly pulled back lanes, no through
+        Given the node map
+            | e |   |   | f |
+            | a |   | b | c |
+            |   |   | g |   |
+
+        And the ways
+            | nodes | name  | turn:lanes:forward  | oneway |
+            | ab    | road  | left\|right         | no     |
+            | bc    | road  |                     | no     |
+            | eb    | owi   |                     | yes    |
+            | cf    | turn  |                     | no     |
+            | bg    | right |                     | no     |
+
+        When I route I should get
+            | waypoints | route            | turns                    | lanes                   |
+            | a,f       | road,turn,turn   | depart,turn left,arrive  | ,left:true right:false, |
+            | a,g       | road,right,right | depart,turn right,arrive | ,left:false right:true, |
+
+    @todo @bug
+    Scenario: Narrowing Turn Lanes
+        Given the node map
+            |   |   |   |   | g |   |
+            |   |   |   |   |   |   |
+            | a |   | b | c | d | e |
+            |   |   |   | f |   |   |
+
+        And the ways
+            | nodes | name    | turn:lanes:forward   |
+            | ab    | road    | left\|through\|right |
+            | bc    | road    |                      |
+            | cd    | road    | left\|through        |
+            | de    | through |                      |
+            | dg    | left    |                      |
+            | cf    | right   |                      |
+
+        When I route I should get
+            | waypoints | route                | turns                           | lanes                                  |
+            | a,g       | road,left,left       | depart,turn left,arrive         | ,left:true straight:false right:false, |
+            | a,e       | road,through,through | depart,new name straight,arrive | ,left:false straight:true right:false, |
+            | a,f       | road,right,right     | depart,turn right,arrive        | ,left:false straight:false right:true, |
+
+    Scenario: Turn at a traffic light
+        Given the node map
+            | a | b | c | d |
+            |   |   | e |   |
+
+        And the nodes
+            | node | highway         |
+            | b    | traffic_signals |
+
+        And the ways
+            | nodes | name | turn:lanes:forward |
+            | ab    | road | through\|right     |
+            | bc    | road |                    |
+            | cd    | road |                    |
+            | ce    | turn |                    |
+
+        When I route I should get
+            | waypoints | route          | turns                    | lanes                       |
+            | a,d       | road,road      | depart,arrive            | ,                           |
+            | a,e       | road,turn,turn | depart,turn right,arrive | ,straight:false right:true, |
+
+    @bug @todo
+    Scenario: Theodor Heuss Platz
+        Given the node map
+            |   |   |   | i | o |   |   | l |   |
+            |   |   | b |   |   |   | a |   | m |
+            |   | c |   |   |   |   |   |   |   |
+            |   |   |   |   |   |   |   | h |   |
+            |   |   |   |   |   |   |   |   |   |
+            | j |   |   |   |   |   |   |   |   |
+            |   |   |   |   |   |   |   | g |   |
+            |   |   |   |   |   |   |   |   |   |
+            |   | d |   |   |   |   |   |   |   |
+            |   |   | e |   |   |   | f |   |   |
+            |   |   |   |   | k |   |   |   | n |
+
+        And the nodes
+            | node | highway         |
+            | g    | traffic_signals |
+
+        And the ways
+            | nodes         | name          | turn:lanes:forward                                                | junction   | oneway | highway   |
+            | abcdef        | roundabout    |                                                                   | roundabout | yes    | primary   |
+            | gha           | roundabout    |                                                                   | roundabout | yes    | primary   |
+            | fg            | roundabout    | slight_left\|slight_left;slight_right\|slight_right\|slight_right | roundabout | yes    | primary   |
+            | aoib          | top           |                                                                   |            | yes    | primary   |
+            | cjd           | left          |                                                                   |            | yes    | primary   |
+            | ekf           | bottom        |                                                                   |            | yes    | primary   |
+            | fng           | bottom-right  |                                                                   |            | yes    | primary   |
+            | hma           | top-right     |                                                                   |            | yes    | primary   |
+            | hl            | top-right-out |                                                                   |            | yes    | secondary |
+
+        When I route I should get
+            | waypoints | route                           | turns                           | lanes                                                                                  |
+            | i,m       | top,top-right,top-right         | depart,roundabout-exit-4,arrive | ,slight left:false slight left;slight right:true slight right:true slight right:true,  |
+            | i,l       | top,top-right-out,top-right-out | depart,roundabout-exit-4,arrive | ,slight left:true slight left;slight right:true slight right:false slight right:false, |
+            | i,o       | top,top,top                     | depart,roundabout-exit-5,arrive | ,,                                                                                     |
+
+    Scenario: Turn Lanes Breaking up
+        Given the node map
+            |   |   |   | g |   |
+            |   |   |   |   |   |
+            |   |   |   | c |   |
+            | a | b |   | d | e |
+            |   |   |   |   |   |
+            |   |   |   | f |   |
+
+        And the ways
+            | nodes | name  | turn:lanes:forward           | oneway | highway   |
+            | ab    | road  | left\|left\|through\|through | yes    | primary   |
+            | bd    | road  | through\|through             | yes    | primary   |
+            | bc    | road  | left\|left                   | yes    | primary   |
+            | de    | road  |                              | yes    | primary   |
+            | fdcg  | cross |                              |        | secondary |
+
+        And the relations
+            | type        | way:from | way:to | node:via | restriction   |
+            | restriction | bd       | fdcg   | d        | no_left_turn  |
+            | restriction | bc       | fdcg   | c        | no_right_turn |
+
+        When I route I should get
+            | waypoints | route            | turns                   | lanes                                               |
+            | a,g       | road,cross,cross | depart,turn left,arrive | ,left:true left:true straight:false straight:false, |
+            | a,e       | road,road        | depart,arrive           | ,                                                   |
+
+    Scenario: U-Turn Road at Intersection
+        Given the node map
+            |   |   |   |   |   | h |   |
+            |   |   |   |   | f | e | j |
+            | a | b |   |   |   |   |   |
+            |   |   |   |   | c | d | i |
+            |   |   |   |   |   | g |   |
+
+        And the ways
+            | nodes | name  | turn:lanes:forward | oneway | highway  |
+            | ab    | road  |                    | no     | primary  |
+            | di    | road  |                    | yes    | primary  |
+            | bc    | road  | \|through\|right   | yes    | primary  |
+            | cd    | road  | \|through\|right   | yes    | primary  |
+            | fc    | road  |                    | no     | tertiary |
+            | jefb  | road  |                    | yes    | primary  |
+            | gdeh  | cross |                    | no     | primary  |
+
+       When I route I should get
+            | from | to | bearings        | route            | turns                        | lanes                                  |
+            | a    | g  | 180,180 180,180 | road,cross,cross | depart,turn right,arrive     | ,none:false straight:false right:true, |
+            | a    | h  | 180,180 180,180 | road,cross,cross | depart,turn left,arrive      | ,none:true straight:false right:false, |
+            | a    | i  | 180,180 180,180 | road,road        | depart,arrive                | ,                                      |
+            | b    | a  | 90,2 270,2      | road,road,road   | depart,continue uturn,arrive | ,none:true straight:false right:false, |
+
+    Scenario: Segregated Intersection Merges With Lanes
+        Given the node map
+            |   |   |   |   |   |   | f |
+            |   |   |   |   |   |   |   |
+            | e |   |   | d |   |   |   |
+            |   |   |   |   |   | c | g |
+            | a |   |   | b |   |   |   |
+            |   |   |   |   |   |   |   |
+            |   |   |   |   |   | h |   |
+
+        And the ways
+            | nodes | name     | turn:lanes:forward                 | oneway | highway   |
+            | abc   | road     | left\|left\|left\|through\|through | yes    | primary   |
+            | cde   | road     |                                    | yes    | primary   |
+            | hc    | cross    |                                    | yes    | secondary |
+            | cg    | straight |                                    | no     | tertiary  |
+            | cf    | left     |                                    | yes    | primary   |
+
+        When I route I should get
+            | waypoints | route                  | turns                           | lanes                                                           |
+            | a,f       | road,left,left         | depart,turn left,arrive         | ,left:true left:true left:true straight:false straight:false,   |
+            | a,e       | road,road,road         | depart,turn uturn,arrive        | ,left:true left:false left:false straight:false straight:false, |
+            | a,g       | road,straight,straight | depart,new name straight,arrive | ,left:false left:false left:false straight:true straight:true,  |
+
+    @bug @todo
+    Scenario: Passing Through a Roundabout
+        Given the node map
+            |   |   | h |   | g |   |   |
+            |   | a |   |   |   | f | k |
+            | i |   |   |   |   |   |   |
+            |   |   |   |   |   |   |   |
+            |   | b |   |   |   | e |   |
+            |   |   | c |   | d |   |   |
+            |   |   |   |   | j |   |   |
+
+        And the ways
+            | nodes | name   | turn:lanes:forward                     | oneway | highway   | junction   |
+            | efgha | round  |                                        | yes    | primary   | roundabout |
+            | ab    | round  |                                        | yes    | primary   | roundabout |
+            | bc    | round  | slight_left\|slight_left\|slight_right | yes    | primary   | roundabout |
+            | cd    | round  |                                        | yes    | primary   | roundabout |
+            | de    | round  | slight_left\|slight_right              | yes    | primary   | roundabout |
+            | ib    | left   | slight_left\|slight_left\|slight_right | yes    | primary   |            |
+            | cj    | bottom |                                        | yes    | primary   |            |
+            | ek    | right  |                                        | yes    | primary   |            |
+
+        When I route I should get
+            | waypoints | route              | turns                      | lanes |
+            | i,j       | left,bottom,bottom | depart,round-exit-1,arrive | ,0,   |
+            | i,k       | left,right,right   | depart,round-exit-2,arrive | ,1,   |
+
+    Scenario: Crossing Traffic Light
+        Given the node map
+            | a |   | b |   | c |   | d |
+            |   |   |   |   |   |   | e |
+
+        And the nodes
+            | node | highway         |
+            | b    | traffic_signals |
+
+        And the ways
+            | nodes | name  | turn:lanes:forward                                   | highway |
+            | abc   | road  | through\|through\|through;slight_right\|slight_right | primary |
+            | cd    | road  |                                                      | primary |
+            | ce    | cross |                                                      | primary |
+
+        When I route I should get
+            | waypoints | route            | turns                           | lanes                                                                        |
+            | a,d       | road,road        | depart,arrive                   | ,                                                                            |
+            | a,e       | road,cross,cross | depart,turn slight right,arrive | ,straight:false straight:false straight;slight right:true slight right:true, |
+
+    Scenario: Highway Ramp
+        Given the node map
+            | a |   | b |   | c |   | d |
+            |   |   |   |   |   |   | e |
+
+        And the ways
+            | nodes | name | turn:lanes:forward                                   | highway       |
+            | abc   | hwy  | through\|through\|through;slight_right\|slight_right | motorway      |
+            | cd    | hwy  |                                                      | motorway      |
+            | ce    | ramp |                                                      | motorway_link |
+
+        When I route I should get
+            | waypoints | route         | turns                               | lanes                                                                        |
+            | a,d       | hwy,hwy       | depart,arrive                       | ,                                                                            |
+            | a,e       | hwy,ramp,ramp | depart,off ramp slight right,arrive | ,straight:false straight:false straight;slight right:true slight right:true, |
+
+    @bug @todo
+    Scenario: Turning Off Ramp
+        Given the node map
+            |   | a |   |
+            | d | c | b |
+            | e | f | g |
+            |   | h |   |
+
+        And the ways
+            | nodes | name | turn:lanes:forward | highway       | oneway |
+            | ac    | off  | left\|right        | motorway_link | yes    |
+            | bcd   | road |                    | primary       | yes    |
+            | cf    | road |                    | primary       |        |
+            | efg   | road |                    | primary       | yes    |
+            | fh    | on   |                    | motorway_link | yes    |
+
+        When I route I should get
+            | waypoints | route         | turns                    | lanes                   |
+            | a,d       | off,road,road | depart,turn_right,arrive | ,left:false right:true, |
+            | a,g       | off,road,road | depart,turn_left,arrive  | ,left:true right:false, |
+            | a,h       |               |                          |                         |
+
+    Scenario: Off Ramp In a Turn
+        Given the node map
+            | a |   |   |   |   |   |   |   |   |   |   |   |
+            |   |   |   |   |   |   |   |   |   |   |   |   |
+            |   |   |   |   |   | b |   |   |   |   |   | c |
+            |   |   |   |   |   |   |   |   |   |   | d |   |
+
+        And the ways
+            | nodes | name | turn:lanes:forward             | highway       | oneway |
+            | ab    | hwy  | through\|through\|slight_right | motorway      | yes    |
+            | bc    | hwy  |                                | motorway      | yes    |
+            | bd    | ramp |                                | motorway_link | yes    |
+
+        When I route I should get
+            | waypoints | route         | turns                               | lanes                                             |
+            | a,c       | hwy,hwy       | depart,arrive                       | ,                                                 |
+            | a,d       | hwy,ramp,ramp | depart,off ramp slight right,arrive | ,straight:false straight:false slight right:true, |
+
+    Scenario: Reverse Lane in Segregated Road
+        Given the node map
+            | h |   |   |   |   | g |   |   |   |   |   | f |
+            |   |   |   |   |   |   |   | e |   |   |   |   |
+            |   |   |   |   |   |   |   | d |   |   |   |   |
+            | a |   |   |   |   | b |   |   |   |   |   | c |
+
+        And the ways
+            | nodes | name | turn:lanes:forward        | highway      | oneway |
+            | ab    | road | reverse\|through\|through | primary      | yes    |
+            | bc    | road |                           | primary      | yes    |
+            | bdeg  | road |                           | primary_link | yes    |
+            | fgh   | road |                           | primary      | yes    |
+
+        When I route I should get
+            | waypoints | route          | turns                        | lanes                                     |
+            | a,h       | road,road,road | depart,continue uturn,arrive | ,uturn:true straight:false straight:false,|
+
+    Scenario: Reverse Lane in Segregated Road with none
+        Given the node map
+            | h |   |   |   |   | g |   |   |   |   |   | f |
+            |   |   |   |   |   |   |   | e |   |   |   |   |
+            |   |   |   |   |   |   |   | d |   |   |   |   |
+            | a |   |   |   |   | b |   |   |   |   |   | c |
+
+        And the ways
+            | nodes | name | turn:lanes:forward     | highway      | oneway |
+            | ab    | road | reverse\|through\|none | primary      | yes    |
+            | bc    | road |                        | primary      | yes    |
+            | bdeg  | road |                        | primary_link | yes    |
+            | fgh   | road |                        | primary      | yes    |
+
+        When I route I should get
+            | waypoints | route          | turns                        | lanes                                  |
+            | a,h       | road,road,road | depart,continue uturn,arrive | ,uturn:true straight:false none:false, |
+
+    Scenario: Reverse Lane in Segregated Road with none, Service Turn Prior
+        Given the node map
+            | h |   |   |   |   | g |   |   |   |   |   | f |
+            |   |   |   |   |   |   |   | e |   |   |   |   |
+            |   |   |   |   |   |   |   | d |   |   |   |   |
+            | a |   | j |   |   | b |   |   |   |   |   | c |
+            |   |   | i |   |   |   |   |   |   |   |   |   |
+
+        And the ways
+            | nodes | name | turn:lanes:forward     | highway      | oneway |
+            | ajb   | road | reverse\|through\|none | primary      | yes    |
+            | bc    | road |                        | primary      | yes    |
+            | bdeg  | road |                        | primary_link | yes    |
+            | fgh   | road |                        | primary      | yes    |
+            | ji    | park |                        | service      | no     |
+
+        When I route I should get
+            | waypoints | route          | turns                        | lanes                                  |
+            | a,h       | road,road,road | depart,continue uturn,arrive | ,uturn:true straight:false none:false, |
+
+    Scenario: Don't collapse everything to u-turn / too wide
+        Given the node map
+            | a |   | b |   | e |
+            |   |   |   |   |   |
+            | d |   | c |   | f |
+
+        And the ways
+            | nodes | highway   | name   | turn:lanes:forward |
+            | ab    | primary   | road   | through\|right     |
+            | bc    | primary   | road   |                    |
+            | dc    | primary   | road   | left\|through      |
+            | be    | secondary | top    |                    |
+            | cf    | secondary | bottom |                    |
+
+        When I route I should get
+            | waypoints | turns                                   | route               | lanes                        |
+            | a,d       | depart,continue right,turn right,arrive | road,road,road,road | ,straight:false right:true,, |
+            | d,a       | depart,continue left,turn left,arrive   | road,road,road,road | ,left:true straight:false,,  |
+
+    Scenario: Merge Lanes Onto Freeway
+        Given the node map
+            | a |   |   | b | c |
+            |   | d |   |   |   |
+
+        And the ways
+            | nodes | highway       | name | turn:lanes:forward         |
+            | abc   | motorway      | Hwy  |                            |
+            | db    | motorway_link | ramp | slight_right\|slight_right |
+
+        When I route I should get
+            | waypoints | turns                           | route        | lanes                                 |
+            | d,c       | depart,merge slight left,arrive | ramp,Hwy,Hwy | ,slight right:true slight right:true, |
+
+    Scenario: Fork on motorway links - don't fork on through but use lane
+        Given the node map
+            | i |   |   |   |   | a |
+            | j |   | c | b |   | x |
+
+        And the ways
+            | nodes | name | highway       | turn:lanes:forward |
+            | xb    | xbcj | motorway_link |                    |
+            | bc    | xbcj | motorway_link | none\|slight_right |
+            | cj    | xbcj | motorway_link |                    |
+            | ci    | off  | motorway_link |                    |
+            | ab    | on   | motorway_link |                    |
+
+        When I route I should get
+            | waypoints | route           | turns                                             | lanes                           |
+            | a,j       | on,xbcj,xbcj    | depart,merge slight left,arrive                   | ,,                              |
+            | a,i       | on,xbcj,off,off | depart,merge slight left,turn slight right,arrive | ,,none:false slight right:true, |
+
+    #http://www.openstreetmap.org/#map=17/52.47414/13.35712
+    @todo @ramp @2645
+    Scenario: Kreuz Schoeneberg - Continue on ramp, don't merge
+        Given the node map
+            | i |   |   |   |   | j |   |   |   |   |   |   |   |   |   |   |   |
+            |   |   |   |   | k |   |   |   |   |   |   |   |   |   |   |   |   |
+            | h | g |   | l |   |   | f |   |   |   |   |   |   |   |   |   | e |
+            | d |   |   |   |   |   |   |   | c |   |   |   |   | b |   |   | a |
+
+        And the ways
+            | nodes | highway       | name  | oneway | lanes | turn:lanes            |
+            | ab    | motorway      | A 100 | yes    | 3     |                       |
+            | bc    | motorway      | A 100 | yes    | 4     | \|\|\|slight_right    |
+            | cd    | motorway      | A 100 | yes    | 3     |                       |
+            | eb    | motorway_link |       | yes    | 1     |                       |
+            | cf    | motorway_link |       | yes    | 1     |                       |
+            | fj    | motorway_link |       | yes    | 1     |                       |
+            | fl    | motorway_link |       | yes    | 1     |                       |
+            | kl    | motorway_link |       | yes    | 1     |                       |
+            | lg    | motorway_link |       | yes    | 2     | through\|slight_right |
+            | gi    | motorway_link |       | yes    | 1     |                       |
+            | gh    | motorway_link |       | yes    | 1     |                       |
+
+        When I route I should get
+            | waypoints | route        | turns                                                           | lanes                                                                                  |
+            | a,d       | A 100,A 100  | depart,arrive                                                   |                                                                                        |
+            | a,i       | A 100,,,,    | depart,off ramp slight right,fork slight left,turn right,arrive | ,none:false none:false none:false slight right:true,,straight:false slight right:true, |
+            | e,j       | ,,,          | depart,off ramp slight right,fork slight right,arrive           | ,none:false none:false none:false slight right:true,,,                                 |
+            | e,i       | ,,,,         | depart,off ramp right,fork slight left,use lane straight,arrive | ,none:false none:false none:false slight right:true,,straight:false slight right:true, |
+            | e,d       | ,A 100,A 100 | depart,merge slight left,arrive                                 | ,,                                                                                     |
+            | e,h       | ,,,,         | depart,off ramp right,fork left,use lane straight,arrive        | ,none:false none:false none:false slight right:true,,straight:true slight right:false, |
+
+    @collapse @use-lane
+    Scenario: Collapse Multiple Use Lanes
+        Given the node map
+            | x | a |   | b |   |   | c |   |   | d |
+            |   |   |   | e |   |   | f |   |   |   |
+
+        And the ways
+            | nodes | name | highway | turn:lanes:forward |
+            | ab    | road | primary | through,right      |
+            | bc    | road | primary | through,right      |
+            | cd    | road | primary |                    |
+            | xa    | road | primary |                    |
+            | be    | turn | primary |                    |
+            | cf    | turn | primary |                    |
+
+        When I route I should get
+            | waypoints | route     | turns         | lanes |
+            | x,d       | road,road | depart,arrive | ,     |
+
+
+
diff --git a/features/guidance/turn.feature b/features/guidance/turn.feature
index d82b849..e61f850 100644
--- a/features/guidance/turn.feature
+++ b/features/guidance/turn.feature
@@ -833,3 +833,49 @@ Feature: Simple Turns
             | a,d       | road,road        | depart,arrive           |
             | d,i       | road,cross,cross | depart,turn left,arrive |
             | d,g       | road,road        | depart,arrive           |
+
+     Scenario: Go onto turning major road
+        Given the node map
+            |   |   |   | c |
+            |   |   |   |   |
+            |   |   |   |   |
+            | a |   |   | b |
+            |   |   |   |   |
+            |   |   |   | d |
+
+        And the ways
+            | nodes | highway     | name |
+            | abc   | primary     | road |
+            | bd    | residential | in   |
+
+        When I route I should get
+            | waypoints | turns                           | route        |
+            | a,c       | depart,arrive                   | road,road    |
+            | d,a       | depart,turn left,arrive         | in,road,road |
+            | d,c       | depart,new name straight,arrive | in,road,road |
+
+    Scenario: Channing Street
+        Given the node map
+            |   |   | g | f |   |
+            |   |   |   |   |   |
+            | d |   | c | b | a |
+            |   |   |   |   |   |
+            |   |   |   |   |   |
+            |   |   | h | e |   |
+
+        And the nodes
+            | node | highway         |
+            | c    | traffic_signals |
+            | b    | traffic_signals |
+
+        And the ways
+            | nodes | name                           | highway     | oneway |
+            | ab    | Channing Street Northeast      | residential | no     |
+            | bcd   | Channing Street Northwest      | residential | yes    |
+            | ebf   | North Capitol Street Northeast | primary     | yes    |
+            | gch   | North Capitol Street Northeast | primary     | yes    |
+
+        When I route I should get
+            | waypoints | turns                   | route                                                                                   |
+            | a,d       | depart,arrive           | Channing Street Northeast,Channing Street Northwest                                     |
+            | a,h       | depart,turn left,arrive | Channing Street Northeast,North Capitol Street Northeast,North Capitol Street Northeast |
diff --git a/features/step_definitions/hooks.js b/features/step_definitions/hooks.js
index 07b4719..d6ed251 100644
--- a/features/step_definitions/hooks.js
+++ b/features/step_definitions/hooks.js
@@ -15,16 +15,4 @@ module.exports = function () {
         this.setOrigin(this.DEFAULT_ORIGIN);
         callback();
     });
-
-    this.Before('@ignore-platform-windows', () => {
-        this.skipThisScenario();
-    });
-
-    this.Before('@ignore-platform-unix', () => {
-        this.skipThisScenario();
-    });
-
-    this.Before('@ignore-platform-mac', () => {
-        this.skipThisScenario();
-    });
 };
diff --git a/features/support/data.js b/features/support/data.js
index 375229d..17c5bbf 100644
--- a/features/support/data.js
+++ b/features/support/data.js
@@ -223,7 +223,7 @@ module.exports = function () {
             };
 
             ['osrm', 'osrm.ebg', 'osrm.edges', 'osrm.enw', 'osrm.fileIndex', 'osrm.geometry', 'osrm.icd',
-             'osrm.names', 'osrm.nodes', 'osrm.properties', 'osrm.ramIndex', 'osrm.restrictions'].forEach(file => {
+             'osrm.names', 'osrm.nodes', 'osrm.properties', 'osrm.ramIndex', 'osrm.restrictions', 'osrm.tld', 'osrm.tls'].forEach(file => {
                  q.defer(rename, file);
              });
 
@@ -283,7 +283,7 @@ module.exports = function () {
 
             ['osrm', 'osrm.core', 'osrm.datasource_indexes', 'osrm.datasource_names', 'osrm.ebg','osrm.edges',
              'osrm.enw', 'osrm.fileIndex', 'osrm.geometry', 'osrm.hsgr', 'osrm.icd','osrm.level', 'osrm.names',
-             'osrm.nodes', 'osrm.properties', 'osrm.ramIndex', 'osrm.restrictions'].forEach((file) => {
+             'osrm.nodes', 'osrm.properties', 'osrm.ramIndex', 'osrm.restrictions', 'osrm.tld', 'osrm.tls'].forEach((file) => {
                  q.defer(rename, file);
              });
 
diff --git a/features/support/env.js b/features/support/env.js
index 32ff82d..c99d544 100644
--- a/features/support/env.js
+++ b/features/support/env.js
@@ -21,7 +21,7 @@ module.exports = function () {
         this.DEFAULT_GRID_SIZE = 100;    // meters
         this.PROFILES_PATH = path.resolve(this.ROOT_FOLDER, 'profiles');
         this.FIXTURES_PATH = path.resolve(this.ROOT_FOLDER, 'unit_tests/fixtures');
-        this.BIN_PATH = path.resolve(this.ROOT_FOLDER, 'build');
+        this.BIN_PATH = process.env.OSRM_BUILD_DIR && process.env.OSRM_BUILD_DIR || path.resolve(this.ROOT_FOLDER, 'build');
         this.DEFAULT_INPUT_FORMAT = 'osm';
         this.DEFAULT_ORIGIN = [1,1];
         this.DEFAULT_LOAD_METHOD = 'datastore';
diff --git a/features/support/hooks.js b/features/support/hooks.js
index 753bdf7..1e265ea 100644
--- a/features/support/hooks.js
+++ b/features/support/hooks.js
@@ -33,9 +33,4 @@ module.exports = function () {
             });
         });
     });
-
-    this.Around('@stress', (scenario, callback) => {
-        // TODO implement stress timeout? Around support is being dropped in cucumber-js anyway
-        callback();
-    });
 };
diff --git a/features/support/route.js b/features/support/route.js
index ac214fc..a453ca4 100644
--- a/features/support/route.js
+++ b/features/support/route.js
@@ -168,6 +168,17 @@ module.exports = function () {
         return instructions.legs.map(l => l.annotation.nodes.map(n => n.toString()).join(',')).join(',');
     };
 
+    this.lanesList = (instructions) => {
+        return this.extractInstructionList(instructions, instruction => {
+            if( 'lanes' in instruction.intersections[0] )
+            {
+                return instruction.intersections[0].lanes.map( p => { return (p.indications).join(';') + ':' + p.valid; } ).join(' ');
+            } else
+            {
+                return '';
+            }});
+    };
+
     this.turnList = (instructions) => {
         return instructions.legs.reduce((m, v) => m.concat(v.steps), [])
             .map(v => {
diff --git a/features/support/shared_steps.js b/features/support/shared_steps.js
index a6783bf..6db5400 100644
--- a/features/support/shared_steps.js
+++ b/features/support/shared_steps.js
@@ -33,7 +33,8 @@ module.exports = function () {
                 var afterRequest = (err, res, body) => {
                     if (err) return cb(err);
                     if (body && body.length) {
-                        let destinations, pronunciations, instructions, bearings, turns, modes, times, distances, summary, intersections;
+                        let destinations, pronunciations, instructions, bearings, turns, modes, times,
+                            distances, summary, intersections, lanes;
 
                         let json = JSON.parse(body);
 
@@ -49,6 +50,7 @@ module.exports = function () {
                             modes = this.modeList(json.routes[0]);
                             times = this.timeList(json.routes[0]);
                             distances = this.distanceList(json.routes[0]);
+                            lanes = this.lanesList(json.routes[0]);
                             summary = this.summary(json.routes[0]);
                         }
 
@@ -102,6 +104,10 @@ module.exports = function () {
                                 got.time = instructions ? util.format('%ds', time) : '';
                             }
 
+                            if (headers.has('lanes')) {
+                                got.lanes = (lanes || '').trim();
+                            }
+
                             if (headers.has('speed')) {
                                 if (row.speed !== '' && instructions) {
                                     if (!row.speed.match(/\d+ km\/h/))
diff --git a/features/testbot/basic.feature b/features/testbot/basic.feature
index 03b4892..466fff2 100644
--- a/features/testbot/basic.feature
+++ b/features/testbot/basic.feature
@@ -233,7 +233,7 @@ Feature: Basic Routing
             | d    | a  | abcd,abcd  |
             | a    | m  | aeim,aeim  |
             | m    | a  | aeim,aeim  |
-    
+
     Scenario: Testbot - Triangle challenge
         Given the node map
             |   |   |   | d |
@@ -251,3 +251,39 @@ Feature: Basic Routing
             | from | to | route |
             | d    | c  | de,ce,ce |
             | e    | d  | de,de    |
+
+    Scenario: Ambiguous edge weights - Use minimal edge weight
+        Given the node map
+            | a | b |
+
+        And the ways
+            | nodes | highway   | name |
+            | ab    | tertiary  |      |
+            | ab    | primary   |      |
+            | ab    | secondary |      |
+
+        When I route I should get
+            | from | to | route | time |
+            | a    | b  | ,     | 10s  |
+            | b    | a  | ,     | 10s  |
+
+    Scenario: Ambiguous edge names - Use lexicographically smallest name
+        Given the node map
+            | a | b | c |
+
+        And the ways
+            | nodes | highway | name |
+            | ab    | primary |      |
+            | ab    | primary | Αβγ  |
+            | ab    | primary |      |
+            | ab    | primary | Abc  |
+            | ab    | primary |      |
+            | ab    | primary | Абв  |
+            | bc    | primary | Ηθι  |
+            | bc    | primary | Δεζ  |
+            | bc    | primary | Где  |
+
+        When I route I should get
+            | from | to | route       |
+            | a    | c  | Abc,Δεζ,Δεζ |
+            | c    | a  | Δεζ,Abc,Abc |
diff --git a/features/testbot/bearing.feature b/features/testbot/bearing.feature
index b8aa493..8ca7e91 100644
--- a/features/testbot/bearing.feature
+++ b/features/testbot/bearing.feature
@@ -59,10 +59,10 @@ Feature: Compass bearing
 
     Scenario: Bearing in a roundabout
         Given the node map
-            |   | d | c |   |
+            | k | d | c | j |
             | e |   |   | b |
             | f |   |   | a |
-            |   | g | h |   |
+            | l | g | h | i |
 
         And the ways
             | nodes | oneway |
@@ -74,6 +74,14 @@ Feature: Compass bearing
             | fg    | yes    |
             | gh    | yes    |
             | ha    | yes    |
+            | dk    | no     |
+            | ke    | no     |
+            | fl    | no     |
+            | lg    | no     |
+            | hi    | no     |
+            | ia    | no     |
+            | bj    | no     |
+            | cj    | no     |
 
         When I route I should get
             | from | to | route                   | bearing                                                     |
@@ -82,8 +90,10 @@ Feature: Compass bearing
 
     Scenario: Bearing should stay constant when zig-zagging
         Given the node map
+            | i | j | k |   |
             | b | d | f | h |
             | a | c | e | g |
+            |   | m | n | o |
 
         And the ways
             | nodes |
@@ -94,6 +104,12 @@ Feature: Compass bearing
             | ef    |
             | fg    |
             | gh    |
+            | bi    |
+            | cm    |
+            | dj    |
+            | en    |
+            | fk    |
+            | go    |
 
         When I route I should get
             | from | to | route                   | bearing               |
diff --git a/features/testbot/bearing_param.feature b/features/testbot/bearing_param.feature
index c0c4072..8650398 100644
--- a/features/testbot/bearing_param.feature
+++ b/features/testbot/bearing_param.feature
@@ -14,11 +14,11 @@ Feature: Bearing parameter
             | ad    |
 
         When I route I should get
-            | from | to | bearings  | route | bearing |
+            | from | to | bearings  | route | bearing    |
             | b    | c  | 90 90     | ad,ad | 0->90,90->0|
-            | b    | c  | 180 90    |       |         |
+            | b    | c  | 180 90    |       |            |
             | b    | c  | 80 100    | ad,ad | 0->90,90->0|
-            | b    | c  | 79 100    |       |         |
+            | b    | c  | 79 100    |       |            |
             | b    | c  | 79,11 100 | ad,ad | 0->90,90->0|
 
     Scenario: Testbot - Intial bearing in simple case
@@ -33,13 +33,13 @@ Feature: Bearing parameter
             | bc    |
 
         When I route I should get
-            | from | to | bearings | route | bearing |
-            | 0    | c  | 0 0      |       |         |
-            | 0    | c  | 45 45    | bc,bc | 0->44,44->0 +- 1|
-            | 0    | c  | 85 85    |       |         |
-            | 0    | c  | 95 95    |       |         |
+            | from | to | bearings | route | bearing           |
+            | 0    | c  | 0 0      |       |                   |
+            | 0    | c  | 45 45    | bc,bc | 0->44,44->0 +- 1  |
+            | 0    | c  | 85 85    |       |                   |
+            | 0    | c  | 95 95    |       |                   |
             | 0    | c  | 135 135  | ac,ac | 0->135,135->0 +- 1|
-            | 0    | c  | 180 180  |       |         |
+            | 0    | c  | 180 180  |       |                   |
 
     Scenario: Testbot - Initial bearing on split way
         Given the node map
@@ -58,21 +58,21 @@ Feature: Bearing parameter
             | ha    | yes    |
 
         When I route I should get
-            | from | to | bearings | route       | bearing       |
-            | 0    | b  | 10 10    | bc,bc       | 0->0,0->0     |
-            | 0    | b  | 90 90    | ab,ab       | 0->90,90->0   |
+            | from | to | bearings | route          | bearing                             |
+            | 0    | b  | 10 10    | bc,bc          | 0->0,0->0                           |
+            | 0    | b  | 90 90    | ab,ab          | 0->90,90->0                         |
             # The returned bearing is wrong here, it's based on the snapped
             # coordinates, not the acutal edge bearing.  This should be
             # fixed one day, but it's only a problem when we snap two vias
             # to the same point - DP
-            #| 0    | b  | 170 170  | da          | 180           |
-            #| 0    | b  | 189 189  | da          | 180           |
-            | 0    | 1  | 90 270   | ab,bc,cd,cd    | 0->90,90->0,0->270,270->0      |
-            | 1    | d  | 10 10    | bc,bc          | 0->0,0->0  |
+            #| 0    | b  | 170 170  | da          | 180                                   |
+            #| 0    | b  | 189 189  | da          | 180                                   |
+            | 0    | 1  | 90 270   | ab,bc,cd,cd    | 0->90,90->0,0->270,270->0           |
+            | 1    | d  | 10 10    | bc,bc          | 0->0,0->0                           |
             | 1    | d  | 90 90    | ab,bc,cd,da,da | 0->90,90->0,0->270,270->180,180->0  |
-            | 1    | 0  | 189 189  | da,da       | 0->180,180->0 |
-            | 1    | d  | 270 270  | cd,cd       | 0->270,270->0 |
-            | 1    | d  | 349 349  |             |               |
+            | 1    | 0  | 189 189  | da,da          | 0->180,180->0                       |
+            | 1    | d  | 270 270  | cd,cd          | 0->270,270->0                       |
+            | 1    | d  | 349 349  |                |                                     |
 
     Scenario: Testbot - Initial bearing in all direction
         Given the node map
diff --git a/features/testbot/continue_straight.feature b/features/testbot/continue_straight.feature
index d761ead..df435bd 100644
--- a/features/testbot/continue_straight.feature
+++ b/features/testbot/continue_straight.feature
@@ -3,7 +3,7 @@ Feature: U-turns at via points
 
     Background:
         Given the profile "testbot"
-        Given a grid size of 100 meters
+        Given a grid size of 250 meters
 
     Scenario: Continue straight at waypoints enabled by default
         Given the node map
diff --git a/features/testbot/loop.feature b/features/testbot/loop.feature
index f035ab0..d3d19bb 100644
--- a/features/testbot/loop.feature
+++ b/features/testbot/loop.feature
@@ -75,12 +75,12 @@ Feature: Avoid weird loops caused by rounding errors
         And the node map
             | a |   |   |
             | b | e |   |
-            |   |   | 1 |
+            | h |   | 1 |
             |   |   |   |
             |   |   | 2 |
-            |   |   |   |
+            | g |   |   |
             |   | c | f |
-            |   | d |   |
+            | d |   |   |
 
         And the ways
             | nodes | highway     |
@@ -90,6 +90,8 @@ Feature: Avoid weird loops caused by rounding errors
             | be    | primary     |
             | ef    | primary     |
             | cf    | primary     |
+            | cg    | primary     |
+            | bh    | primary     |
 
         When I route I should get
             | waypoints | route                   |
diff --git a/features/testbot/oneway.feature b/features/testbot/oneway.feature
index 6e2326d..5165f2b 100644
--- a/features/testbot/oneway.feature
+++ b/features/testbot/oneway.feature
@@ -3,6 +3,7 @@ Feature: Testbot - oneways
 
     Background:
         Given the profile "testbot"
+        Given a grid size of 250 meters
 
     Scenario: Routing on a oneway roundabout
         Given the node map
@@ -59,8 +60,8 @@ Feature: Testbot - oneways
 
     Scenario: Testbot - Around the Block
         Given the node map
-            | a | b |
-            | d | c |
+            |   | a | b |   |
+            | e | d | c | f |
 
         And the ways
             | nodes | oneway | foot |
@@ -68,6 +69,8 @@ Feature: Testbot - oneways
             | bc    |        | no   |
             | cd    |        | no   |
             | da    |        | no   |
+            | de    |        | no   |
+            | cf    |        | no   |
 
         When I route I should get
             | from | to | route       |
diff --git a/features/testbot/summary.feature b/features/testbot/summary.feature
index 028c863..563230a 100644
--- a/features/testbot/summary.feature
+++ b/features/testbot/summary.feature
@@ -29,7 +29,7 @@ Feature: Basic Routing
     Scenario: Check handling empty values
         Given the node map
             | a | b |  | c |   | d | f |
-            |   |   |  |   |   | e |
+            |   |   |  |   |   | e |   |
 
         And the ways
             | nodes | name |
diff --git a/features/testbot/time.feature b/features/testbot/time.feature
index 834b13b..6fa32e3 100644
--- a/features/testbot/time.feature
+++ b/features/testbot/time.feature
@@ -145,14 +145,16 @@ Feature: Estimation of travel time
 
     Scenario: Time of travel on a series of ways
         Given the node map
-            | a | b |   |
-            |   | c | d |
+            | a | b | e |
+            | f | c | d |
 
         And the ways
             | nodes | highway |
             | ab    | primary |
             | bc    | primary |
             | cd    | primary |
+            | be    | primary |
+            | cf    | primary |
 
         When I route I should get
             | from | to | route       | time    |
diff --git a/features/testbot/via.feature b/features/testbot/via.feature
index fd4a872..08a684a 100644
--- a/features/testbot/via.feature
+++ b/features/testbot/via.feature
@@ -85,13 +85,16 @@ Feature: Via points
     Scenario: Via points on ring of oneways
     # xa it to avoid only having a single ring, which cna trigger edge cases
         Given the node map
-            | x |   |   |   |   |   |   |
-            | a | 1 | b | 2 | c | 3 | d |
-            | f |   |   |   |   |   | e |
+            |   | x |   |   |   |   |   | g |   |
+            |   | a | 1 | b | 2 | c | 3 | d |   |
+            | i | f |   |   |   |   |   | e | h |
 
         And the ways
             | nodes | oneway |
             | xa    |        |
+            | if    |        |
+            | gd    |        |
+            | eh    |        |
             | ab    | yes    |
             | bc    | yes    |
             | cd    | yes    |
@@ -110,13 +113,16 @@ Feature: Via points
     Scenario: Via points on ring on the same oneway
     # xa it to avoid only having a single ring, which cna trigger edge cases
         Given the node map
-            | x |   |   |   |   |
-            | a | 1 | 2 | 3 | b |
-            | d |   |   |   | c |
+            |   | x |   |   |   | e |   |
+            |   | a | 1 | 2 | 3 | b |   |
+            | g | d |   |   |   | c | f |
 
         And the ways
             | nodes | oneway |
             | xa    |        |
+            | eb    |        |
+            | cf    |        |
+            | dg    |        |
             | ab    | yes    |
             | bc    | yes    |
             | cd    | yes    |
diff --git a/include/contractor/contractor.hpp b/include/contractor/contractor.hpp
index 66f9980..3c9dff9 100644
--- a/include/contractor/contractor.hpp
+++ b/include/contractor/contractor.hpp
@@ -78,7 +78,7 @@ class Contractor
   private:
     ContractorConfig config;
 
-    std::size_t
+    EdgeID
     LoadEdgeExpandedGraph(const std::string &edge_based_graph_path,
                           util::DeallocatingVector<extractor::EdgeBasedEdge> &edge_based_edge_list,
                           const std::string &edge_segment_lookup_path,
diff --git a/include/contractor/crc32_processor.hpp b/include/contractor/crc32_processor.hpp
index cba99a9..4130ed5 100644
--- a/include/contractor/crc32_processor.hpp
+++ b/include/contractor/crc32_processor.hpp
@@ -82,6 +82,9 @@ class IteratorbasedCRC32
                                  : "0"(crc), "c"(*str));
             ++str;
         }
+#else
+        (void)str;
+        (void)len;
 #endif
         return crc;
     }
@@ -95,8 +98,11 @@ class IteratorbasedCRC32
     }
 
 #if defined(__MINGW64__) || defined(_MSC_VER) || !defined(__x86_64__)
-    inline void
-    __get_cpuid(int param, unsigned *eax, unsigned *ebx, unsigned *ecx, unsigned *edx) const
+    inline void __get_cpuid(int /*param*/,
+                            unsigned * /*eax*/,
+                            unsigned * /*ebx*/,
+                            unsigned *ecx,
+                            unsigned * /*edx*/) const
     {
         *ecx = 0;
     }
diff --git a/include/engine/api/route_api.hpp b/include/engine/api/route_api.hpp
index 76ff9a7..25e9f7a 100644
--- a/include/engine/api/route_api.hpp
+++ b/include/engine/api/route_api.hpp
@@ -12,6 +12,7 @@
 #include "engine/guidance/assemble_overview.hpp"
 #include "engine/guidance/assemble_route.hpp"
 #include "engine/guidance/assemble_steps.hpp"
+#include "engine/guidance/lane_processing.hpp"
 #include "engine/guidance/post_processing.hpp"
 
 #include "engine/internal_route_result.hpp"
@@ -149,6 +150,8 @@ class RouteAPI : public BaseAPI
                                                               leg_geometry,
                                                               phantoms.source_phantom,
                                                               phantoms.target_phantom);
+                leg.steps = guidance::anticipateLaneChange(std::move(leg.steps));
+                leg.steps = guidance::collapseUseLane(std::move(leg.steps));
                 leg_geometry = guidance::resyncGeometry(std::move(leg_geometry), leg.steps);
             }
 
diff --git a/include/engine/datafacade/datafacade_base.hpp b/include/engine/datafacade/datafacade_base.hpp
index 22b8ad1..81cdde1 100644
--- a/include/engine/datafacade/datafacade_base.hpp
+++ b/include/engine/datafacade/datafacade_base.hpp
@@ -7,6 +7,7 @@
 #include "extractor/edge_based_node.hpp"
 #include "extractor/external_memory_node.hpp"
 #include "extractor/guidance/turn_instruction.hpp"
+#include "extractor/guidance/turn_lane_types.hpp"
 #include "engine/phantom_node.hpp"
 #include "util/exception.hpp"
 #include "util/guidance/bearing_class.hpp"
@@ -138,6 +139,11 @@ class BaseDataFacade
                                                       const int bearing,
                                                       const int bearing_range) const = 0;
 
+    virtual bool hasLaneData(const EdgeID id) const = 0;
+    virtual util::guidance::LaneTupelIdPair GetLaneData(const EdgeID id) const = 0;
+    virtual extractor::guidance::TurnLaneDescription
+    GetTurnDescription(const LaneDescriptionID lane_description_id) const = 0;
+
     virtual unsigned GetCheckSum() const = 0;
 
     virtual bool IsCoreNode(const NodeID id) const = 0;
diff --git a/include/engine/datafacade/internal_datafacade.hpp b/include/engine/datafacade/internal_datafacade.hpp
index 476ab17..92c2c7e 100644
--- a/include/engine/datafacade/internal_datafacade.hpp
+++ b/include/engine/datafacade/internal_datafacade.hpp
@@ -16,6 +16,7 @@
 #include "storage/storage_config.hpp"
 #include "engine/geospatial_query.hpp"
 #include "util/graph_loader.hpp"
+#include "util/guidance/turn_lanes.hpp"
 #include "util/io.hpp"
 #include "util/packed_vector.hpp"
 #include "util/range_table.hpp"
@@ -78,6 +79,8 @@ class InternalDataFacade final : public BaseDataFacade
     util::ShM<NodeID, false>::vector m_via_node_list;
     util::ShM<unsigned, false>::vector m_name_ID_list;
     util::ShM<extractor::guidance::TurnInstruction, false>::vector m_turn_instruction_list;
+    util::ShM<LaneDataID, false>::vector m_lane_data_id;
+    util::ShM<util::guidance::LaneTupelIdPair, false>::vector m_lane_tupel_id_pairs;
     util::ShM<extractor::TravelMode, false>::vector m_travel_mode_list;
     util::ShM<char, false>::vector m_names_char_list;
     util::ShM<unsigned, false>::vector m_geometry_indices;
@@ -86,6 +89,8 @@ class InternalDataFacade final : public BaseDataFacade
     util::ShM<unsigned, false>::vector m_segment_weights;
     util::ShM<uint8_t, false>::vector m_datasource_list;
     util::ShM<std::string, false>::vector m_datasource_names;
+    util::ShM<std::uint32_t, false>::vector m_lane_description_offsets;
+    util::ShM<extractor::guidance::TurnLaneType::Mask, false>::vector m_lane_description_masks;
     extractor::ProfileProperties m_profile_properties;
 
     std::unique_ptr<InternalRTree> m_static_rtree;
@@ -118,6 +123,20 @@ class InternalDataFacade final : public BaseDataFacade
                        sizeof(m_profile_properties));
     }
 
+    void LoadLaneTupelIdPairs(const boost::filesystem::path &lane_data_path)
+    {
+        boost::filesystem::ifstream in_stream(lane_data_path);
+        if (!in_stream)
+        {
+            throw util::exception("Could not open " + lane_data_path.string() + " for reading.");
+        }
+        std::uint64_t size;
+        in_stream.read(reinterpret_cast<char *>(&size), sizeof(size));
+        m_lane_tupel_id_pairs.resize(size);
+        in_stream.read(reinterpret_cast<char *>(&m_lane_tupel_id_pairs[0]),
+                       sizeof(m_lane_tupel_id_pairs) * size);
+    }
+
     void LoadTimestamp(const boost::filesystem::path &timestamp_path)
     {
         util::SimpleLogger().Write() << "Loading Timestamp";
@@ -173,6 +192,7 @@ class InternalDataFacade final : public BaseDataFacade
         m_via_node_list.resize(number_of_edges);
         m_name_ID_list.resize(number_of_edges);
         m_turn_instruction_list.resize(number_of_edges);
+        m_lane_data_id.resize(number_of_edges);
         m_travel_mode_list.resize(number_of_edges);
         m_entry_class_id_list.resize(number_of_edges);
 
@@ -184,6 +204,7 @@ class InternalDataFacade final : public BaseDataFacade
             m_via_node_list[i] = current_edge_data.via_node;
             m_name_ID_list[i] = current_edge_data.name_id;
             m_turn_instruction_list[i] = current_edge_data.turn_instruction;
+            m_lane_data_id[i] = current_edge_data.lane_data_id;
             m_travel_mode_list[i] = current_edge_data.travel_mode;
             m_entry_class_id_list[i] = current_edge_data.entry_classid;
         }
@@ -251,9 +272,9 @@ class InternalDataFacade final : public BaseDataFacade
         }
         BOOST_ASSERT(datasources_stream);
 
-        std::size_t number_of_datasources = 0;
+        std::uint64_t number_of_datasources = 0;
         datasources_stream.read(reinterpret_cast<char *>(&number_of_datasources),
-                                sizeof(std::size_t));
+                                sizeof(number_of_datasources));
         if (number_of_datasources > 0)
         {
             m_datasource_list.resize(number_of_datasources);
@@ -284,6 +305,15 @@ class InternalDataFacade final : public BaseDataFacade
             new InternalGeospatialQuery(*m_static_rtree, m_coordinate_list, *this));
     }
 
+    void LoadLaneDescriptions(const boost::filesystem::path &lane_description_file)
+    {
+        if (!util::deserializeAdjacencyArray(lane_description_file.string(),
+                                             m_lane_description_offsets,
+                                             m_lane_description_masks))
+            util::SimpleLogger().Write(logWARNING) << "Failed to read turn lane descriptions from "
+                                                   << lane_description_file.string();
+    }
+
     void LoadStreetNames(const boost::filesystem::path &names_file)
     {
         boost::filesystem::ifstream name_stream(names_file, std::ios::binary);
@@ -330,7 +360,7 @@ class InternalDataFacade final : public BaseDataFacade
             std::vector<util::guidance::BearingClass> bearing_classes;
             // and the actual bearing values
             std::uint64_t num_bearings;
-            intersection_stream >> num_bearings;
+            intersection_stream.read(reinterpret_cast<char*>(&num_bearings),sizeof(num_bearings));
             m_bearing_values_table.resize(num_bearings);
             intersection_stream.read(reinterpret_cast<char *>(&m_bearing_values_table[0]),
                                      sizeof(m_bearing_values_table[0]) * num_bearings);
@@ -386,11 +416,17 @@ class InternalDataFacade final : public BaseDataFacade
         util::SimpleLogger().Write() << "loading street names";
         LoadStreetNames(config.names_data_path);
 
+        util::SimpleLogger().Write() << "loading lane tags";
+        LoadLaneDescriptions(config.turn_lane_description_path);
+
         util::SimpleLogger().Write() << "loading rtree";
         LoadRTree();
 
         util::SimpleLogger().Write() << "loading intersection class data";
         LoadIntersectionClasses(config.intersection_class_path);
+
+        util::SimpleLogger().Write() << "Loading Lane Data Pairs";
+        LoadLaneTupelIdPairs(config.turn_lane_data_path);
     }
 
     // search graph access
@@ -741,6 +777,29 @@ class InternalDataFacade final : public BaseDataFacade
     {
         return m_entry_class_table.at(entry_class_id);
     }
+
+    bool hasLaneData(const EdgeID id) const override final
+    {
+        return m_lane_data_id[id] != INVALID_LANE_DATAID;
+    }
+
+    util::guidance::LaneTupelIdPair GetLaneData(const EdgeID id) const override final
+    {
+        BOOST_ASSERT(hasLaneData(id));
+        return m_lane_tupel_id_pairs[m_lane_data_id[id]];
+    }
+
+    extractor::guidance::TurnLaneDescription
+    GetTurnDescription(const LaneDescriptionID lane_description_id) const override final
+    {
+        if (lane_description_id == INVALID_LANE_DESCRIPTIONID)
+            return {};
+        else
+            return extractor::guidance::TurnLaneDescription(
+                m_lane_description_masks.begin() + m_lane_description_offsets[lane_description_id],
+                m_lane_description_masks.begin() +
+                    m_lane_description_offsets[lane_description_id + 1]);
+    }
 };
 }
 }
diff --git a/include/engine/datafacade/shared_datafacade.hpp b/include/engine/datafacade/shared_datafacade.hpp
index 12bce18..91e2a02 100644
--- a/include/engine/datafacade/shared_datafacade.hpp
+++ b/include/engine/datafacade/shared_datafacade.hpp
@@ -9,9 +9,11 @@
 
 #include "extractor/compressed_edge_container.hpp"
 #include "extractor/guidance/turn_instruction.hpp"
+#include "extractor/guidance/turn_lane_types.hpp"
 #include "extractor/profile_properties.hpp"
 #include "util/guidance/bearing_class.hpp"
 #include "util/guidance/entry_class.hpp"
+#include "util/guidance/turn_lanes.hpp"
 
 #include "engine/geospatial_query.hpp"
 #include "util/make_unique.hpp"
@@ -79,6 +81,7 @@ class SharedDataFacade final : public BaseDataFacade
     util::PackedVector<OSMNodeID, true> m_osmnodeid_list;
     util::ShM<NodeID, true>::vector m_via_node_list;
     util::ShM<unsigned, true>::vector m_name_ID_list;
+    util::ShM<LaneDataID, true>::vector m_lane_data_id;
     util::ShM<extractor::guidance::TurnInstruction, true>::vector m_turn_instruction_list;
     util::ShM<extractor::TravelMode, true>::vector m_travel_mode_list;
     util::ShM<char, true>::vector m_names_char_list;
@@ -87,10 +90,13 @@ class SharedDataFacade final : public BaseDataFacade
     util::ShM<extractor::CompressedEdgeContainer::CompressedEdge, true>::vector m_geometry_list;
     util::ShM<bool, true>::vector m_is_core_node;
     util::ShM<uint8_t, true>::vector m_datasource_list;
+    util::ShM<std::uint32_t, true>::vector m_lane_description_offsets;
+    util::ShM<extractor::guidance::TurnLaneType::Mask, true>::vector m_lane_description_masks;
 
     util::ShM<char, true>::vector m_datasource_name_data;
     util::ShM<std::size_t, true>::vector m_datasource_name_offsets;
     util::ShM<std::size_t, true>::vector m_datasource_name_lengths;
+    util::ShM<util::guidance::LaneTupelIdPair, true>::vector m_lane_tupel_id_pairs;
 
     std::unique_ptr<SharedRTree> m_static_rtree;
     std::unique_ptr<SharedGeospatialQuery> m_geospatial_query;
@@ -177,7 +183,8 @@ class SharedDataFacade final : public BaseDataFacade
             osmnodeid_list_ptr,
             data_layout->num_entries[storage::SharedDataLayout::OSM_NODE_ID_LIST]);
         // We (ab)use the number of coordinates here because we know we have the same amount of ids
-        m_osmnodeid_list.set_number_of_entries(data_layout->num_entries[storage::SharedDataLayout::COORDINATE_LIST]);
+        m_osmnodeid_list.set_number_of_entries(
+            data_layout->num_entries[storage::SharedDataLayout::COORDINATE_LIST]);
 
         auto travel_mode_list_ptr = data_layout->GetBlockPtr<extractor::TravelMode>(
             shared_memory, storage::SharedDataLayout::TRAVEL_MODE);
@@ -185,6 +192,19 @@ class SharedDataFacade final : public BaseDataFacade
             travel_mode_list_ptr, data_layout->num_entries[storage::SharedDataLayout::TRAVEL_MODE]);
         m_travel_mode_list = std::move(travel_mode_list);
 
+        auto lane_data_id_ptr = data_layout->GetBlockPtr<LaneDataID>(
+            shared_memory, storage::SharedDataLayout::LANE_DATA_ID);
+        util::ShM<LaneDataID, true>::vector lane_data_id(
+            lane_data_id_ptr, data_layout->num_entries[storage::SharedDataLayout::LANE_DATA_ID]);
+        m_lane_data_id = std::move(lane_data_id);
+
+        auto lane_tupel_id_pair_ptr = data_layout->GetBlockPtr<util::guidance::LaneTupelIdPair>(
+            shared_memory, storage::SharedDataLayout::TURN_LANE_DATA);
+        util::ShM<util::guidance::LaneTupelIdPair, true>::vector lane_tupel_id_pair(
+            lane_tupel_id_pair_ptr,
+            data_layout->num_entries[storage::SharedDataLayout::TURN_LANE_DATA]);
+        m_lane_tupel_id_pairs = std::move(lane_tupel_id_pair);
+
         auto turn_instruction_list_ptr =
             data_layout->GetBlockPtr<extractor::guidance::TurnInstruction>(
                 shared_memory, storage::SharedDataLayout::TURN_INSTRUCTION);
@@ -237,6 +257,23 @@ class SharedDataFacade final : public BaseDataFacade
         m_names_char_list = std::move(names_char_list);
     }
 
+    void LoadTurnLaneDescriptions()
+    {
+        auto offsets_ptr = data_layout->GetBlockPtr<std::uint32_t>(
+            shared_memory, storage::SharedDataLayout::LANE_DESCRIPTION_OFFSETS);
+        util::ShM<std::uint32_t, true>::vector offsets(
+            offsets_ptr,
+            data_layout->num_entries[storage::SharedDataLayout::LANE_DESCRIPTION_OFFSETS]);
+        m_lane_description_offsets = std::move(offsets);
+
+        auto masks_ptr = data_layout->GetBlockPtr<extractor::guidance::TurnLaneType::Mask>(
+            shared_memory, storage::SharedDataLayout::LANE_DESCRIPTION_MASKS);
+
+        util::ShM<extractor::guidance::TurnLaneType::Mask, true>::vector masks(
+            masks_ptr, data_layout->num_entries[storage::SharedDataLayout::LANE_DESCRIPTION_MASKS]);
+        m_lane_description_masks = std::move(masks);
+    }
+
     void LoadCoreInformation()
     {
         if (data_layout->num_entries[storage::SharedDataLayout::CORE_MARKER] <= 0)
@@ -415,6 +452,7 @@ class SharedDataFacade final : public BaseDataFacade
                 LoadTimestamp();
                 LoadViaNodeList();
                 LoadNames();
+                LoadTurnLaneDescriptions();
                 LoadCoreInformation();
                 LoadProfileProperties();
                 LoadRTree();
@@ -782,6 +820,28 @@ class SharedDataFacade final : public BaseDataFacade
     {
         return m_entry_class_table.at(entry_class_id);
     }
+
+    bool hasLaneData(const EdgeID id) const override final
+    {
+        return INVALID_LANE_DATAID != m_lane_data_id.at(id);
+    }
+
+    util::guidance::LaneTupelIdPair GetLaneData(const EdgeID id) const override final
+    {
+        BOOST_ASSERT(hasLaneData(id));
+        return m_lane_tupel_id_pairs.at(m_lane_data_id.at(id));
+    }
+
+    extractor::guidance::TurnLaneDescription
+    GetTurnDescription(const LaneDescriptionID lane_description_id) const override final
+    {
+        if (lane_description_id == INVALID_LANE_DESCRIPTIONID)
+            return {};
+        else
+            return extractor::guidance::TurnLaneDescription(
+                m_lane_description_masks.begin() + m_lane_description_offsets[lane_description_id],
+                m_lane_description_masks.begin() + m_lane_description_offsets[lane_description_id + 1]);
+    }
 };
 }
 }
diff --git a/include/engine/guidance/assemble_geometry.hpp b/include/engine/guidance/assemble_geometry.hpp
index 1c2b588..991a3e4 100644
--- a/include/engine/guidance/assemble_geometry.hpp
+++ b/include/engine/guidance/assemble_geometry.hpp
@@ -3,6 +3,7 @@
 
 #include "extractor/guidance/turn_instruction.hpp"
 #include "extractor/travel_mode.hpp"
+#include "engine/datafacade/datafacade_base.hpp"
 #include "engine/guidance/leg_geometry.hpp"
 #include "engine/guidance/route_step.hpp"
 #include "engine/guidance/toolkit.hpp"
@@ -31,11 +32,10 @@ namespace guidance
 //             |---| segment 1
 //                 |---| segment 2
 //                     |---| segment 3
-template <typename DataFacadeT>
-LegGeometry assembleGeometry(const DataFacadeT &facade,
-                             const std::vector<PathData> &leg_data,
-                             const PhantomNode &source_node,
-                             const PhantomNode &target_node)
+inline LegGeometry assembleGeometry(const datafacade::BaseDataFacade &facade,
+                                    const std::vector<PathData> &leg_data,
+                                    const PhantomNode &source_node,
+                                    const PhantomNode &target_node)
 {
     LegGeometry geometry;
 
diff --git a/include/engine/guidance/assemble_steps.hpp b/include/engine/guidance/assemble_steps.hpp
index 815ad03..2febaea 100644
--- a/include/engine/guidance/assemble_steps.hpp
+++ b/include/engine/guidance/assemble_steps.hpp
@@ -2,7 +2,9 @@
 #define ENGINE_GUIDANCE_ASSEMBLE_STEPS_HPP_
 
 #include "extractor/guidance/turn_instruction.hpp"
+#include "extractor/guidance/turn_lane_types.hpp"
 #include "extractor/travel_mode.hpp"
+#include "engine/datafacade/datafacade_base.hpp"
 #include "engine/guidance/leg_geometry.hpp"
 #include "engine/guidance/route_step.hpp"
 #include "engine/guidance/step_maneuver.hpp"
@@ -14,6 +16,7 @@
 #include "util/coordinate_calculation.hpp"
 #include "util/guidance/entry_class.hpp"
 #include "util/guidance/toolkit.hpp"
+#include "util/guidance/turn_lanes.hpp"
 #include "util/typedefs.hpp"
 
 #include <boost/optional.hpp>
@@ -34,14 +37,13 @@ std::pair<short, short> getIntermediateBearings(const LegGeometry &leg_geometry,
                                                 const std::size_t segment_index);
 } // ns detail
 
-template <typename DataFacadeT>
-std::vector<RouteStep> assembleSteps(const DataFacadeT &facade,
-                                     const std::vector<PathData> &leg_data,
-                                     const LegGeometry &leg_geometry,
-                                     const PhantomNode &source_node,
-                                     const PhantomNode &target_node,
-                                     const bool source_traversed_in_reverse,
-                                     const bool target_traversed_in_reverse)
+inline std::vector<RouteStep> assembleSteps(const datafacade::BaseDataFacade &facade,
+                                            const std::vector<PathData> &leg_data,
+                                            const LegGeometry &leg_geometry,
+                                            const PhantomNode &source_node,
+                                            const PhantomNode &target_node,
+                                            const bool source_traversed_in_reverse,
+                                            const bool target_traversed_in_reverse)
 {
     const double constexpr ZERO_DURATION = 0., ZERO_DISTANCE = 0.;
     const constexpr char *NO_ROTARY_NAME = "";
@@ -71,11 +73,14 @@ std::vector<RouteStep> assembleSteps(const DataFacadeT &facade,
                           extractor::guidance::TurnInstruction::NO_TURN(),
                           WaypointType::Depart,
                           0};
+
     Intersection intersection{source_node.location,
                               std::vector<short>({bearings.second}),
                               std::vector<bool>({true}),
                               Intersection::NO_INDEX,
-                              0};
+                              0,
+                              util::guidance::LaneTupel(),
+                              {}};
 
     if (leg_data.size() > 0)
     {
@@ -134,6 +139,11 @@ std::vector<RouteStep> assembleSteps(const DataFacadeT &facade,
                 intersection.location = facade.GetCoordinateOfNode(path_point.turn_via_node);
                 intersection.bearings.clear();
                 intersection.bearings.reserve(bearing_class.getAvailableBearings().size());
+                intersection.lanes = path_point.lane_data.first;
+                intersection.lane_description =
+                    path_point.lane_data.second != INVALID_LANE_DESCRIPTIONID
+                        ? facade.GetTurnDescription(path_point.lane_data.second)
+                        : extractor::guidance::TurnLaneDescription();
                 std::copy(bearing_class.getAvailableBearings().begin(),
                           bearing_class.getAvailableBearings().end(),
                           std::back_inserter(intersection.bearings));
@@ -203,12 +213,15 @@ std::vector<RouteStep> assembleSteps(const DataFacadeT &facade,
                 extractor::guidance::TurnInstruction::NO_TURN(),
                 WaypointType::Arrive,
                 0};
+
     intersection = {
         target_node.location,
         std::vector<short>({static_cast<short>(util::bearing::reverseBearing(bearings.first))}),
         std::vector<bool>({true}),
         0,
-        Intersection::NO_INDEX};
+        Intersection::NO_INDEX,
+        util::guidance::LaneTupel(),
+        {}};
 
     BOOST_ASSERT(!leg_geometry.locations.empty());
     steps.push_back(RouteStep{target_node.name_id,
@@ -233,6 +246,9 @@ std::vector<RouteStep> assembleSteps(const DataFacadeT &facade,
     BOOST_ASSERT(steps.back().intersections.front().bearings.size() == 1);
     BOOST_ASSERT(steps.back().intersections.front().entry.size() == 1);
     BOOST_ASSERT(steps.back().maneuver.waypoint_type == WaypointType::Arrive);
+    BOOST_ASSERT(steps.back().intersections.front().lanes.lanes_in_turn == 0);
+    BOOST_ASSERT(steps.back().intersections.front().lanes.first_lane_from_the_right == INVALID_LANEID);
+    BOOST_ASSERT(steps.back().intersections.front().lane_description.empty());
     return steps;
 }
 
diff --git a/include/engine/guidance/lane_processing.hpp b/include/engine/guidance/lane_processing.hpp
new file mode 100644
index 0000000..8e6f6e1
--- /dev/null
+++ b/include/engine/guidance/lane_processing.hpp
@@ -0,0 +1,27 @@
+#ifndef OSRM_ENGINE_GUIDANCE_LANE_PROCESSING_HPP_
+#define OSRM_ENGINE_GUIDANCE_LANE_PROCESSING_HPP_
+
+#include <vector>
+
+#include "engine/guidance/route_step.hpp"
+
+namespace osrm
+{
+namespace engine
+{
+namespace guidance
+{
+
+// Constrains lanes for multi-hop situations where lane changes depend on earlier ones.
+// Instead of forcing users to change lanes rapidly in a short amount of time,
+// we anticipate lane changes emitting only matching lanes early on.
+// the second parameter describes the duration that we feel two segments need to be apart to count
+// as separate maneuvers.
+std::vector<RouteStep> anticipateLaneChange(std::vector<RouteStep> steps,
+                                            const double min_duration_needed_for_lane_change = 15);
+
+} // namespace guidance
+} // namespace engine
+} // namespace osrm
+
+#endif /* OSRM_ENGINE_GUIDANCE_LANE_PROCESSING_HPP_ */
diff --git a/include/engine/guidance/post_processing.hpp b/include/engine/guidance/post_processing.hpp
index 16d383d..ad0b3d6 100644
--- a/include/engine/guidance/post_processing.hpp
+++ b/include/engine/guidance/post_processing.hpp
@@ -43,6 +43,15 @@ std::vector<RouteStep> buildIntersections(std::vector<RouteStep> steps);
 // remove steps invalidated by post-processing
 std::vector<RouteStep> removeNoTurnInstructions(std::vector<RouteStep> steps);
 
+// remove use lane information that is not actually a turn. For post-processing, we need to
+// associate lanes with every turn. Some of these use-lane instructions are not required after lane
+// anticipation anymore. This function removes all use lane instructions that are not actually used
+// anymore since all lanes going straight are used anyhow.
+// FIXME this is currently only a heuristic. We need knowledge on which lanes actually might become
+// turn lanes. If a straight lane becomes a turn lane, this might be something to consider. Right
+// now we bet on lane-anticipation to catch this.
+std::vector<RouteStep> collapseUseLane(std::vector<RouteStep> steps);
+
 // postProcess will break the connection between the leg geometry
 // for which a segment is supposed to represent exactly the coordinates
 // between routing maneuvers and the route steps itself.
diff --git a/include/engine/guidance/route_step.hpp b/include/engine/guidance/route_step.hpp
index 1687698..7106091 100644
--- a/include/engine/guidance/route_step.hpp
+++ b/include/engine/guidance/route_step.hpp
@@ -7,6 +7,9 @@
 #include "util/guidance/bearing_class.hpp"
 #include "util/guidance/entry_class.hpp"
 
+#include "extractor/guidance/turn_lane_types.hpp"
+#include "util/guidance/turn_lanes.hpp"
+
 #include <cstddef>
 
 #include <string>
@@ -34,6 +37,10 @@ struct Intersection
     std::vector<bool> entry;
     std::size_t in;
     std::size_t out;
+
+    // turn lane information
+    util::guidance::LaneTupel lanes;
+    extractor::guidance::TurnLaneDescription lane_description;
 };
 
 inline Intersection getInvalidIntersection()
@@ -42,7 +49,9 @@ inline Intersection getInvalidIntersection()
             {},
             {},
             Intersection::NO_INDEX,
-            Intersection::NO_INDEX};
+            Intersection::NO_INDEX,
+            util::guidance::LaneTupel(),
+            {}};
 }
 
 struct RouteStep
diff --git a/include/engine/guidance/step_maneuver.hpp b/include/engine/guidance/step_maneuver.hpp
index 1c2b55a..114a822 100644
--- a/include/engine/guidance/step_maneuver.hpp
+++ b/include/engine/guidance/step_maneuver.hpp
@@ -5,6 +5,7 @@
 #include "util/coordinate.hpp"
 
 #include <cstdint>
+#include <string>
 #include <vector>
 
 namespace osrm
@@ -27,6 +28,7 @@ struct StepManeuver
     short bearing_before;
     short bearing_after;
     extractor::guidance::TurnInstruction instruction;
+
     WaypointType waypoint_type;
     unsigned exit;
 };
diff --git a/include/engine/guidance/toolkit.hpp b/include/engine/guidance/toolkit.hpp
index 0562013..98b7f2a 100644
--- a/include/engine/guidance/toolkit.hpp
+++ b/include/engine/guidance/toolkit.hpp
@@ -2,9 +2,13 @@
 #define OSRM_ENGINE_GUIDANCE_TOOLKIT_HPP_
 
 #include "extractor/guidance/turn_instruction.hpp"
+#include "engine/guidance/route_step.hpp"
 #include "util/bearing.hpp"
+#include "util/guidance/toolkit.hpp"
 
 #include <algorithm>
+#include <iterator>
+#include <utility>
 
 namespace osrm
 {
@@ -13,6 +17,10 @@ namespace engine
 namespace guidance
 {
 
+using util::guidance::entersRoundabout;
+using util::guidance::leavesRoundabout;
+using util::guidance::staysOnRoundabout;
+
 // Silent Turn Instructions are not to be mentioned to the outside world but
 inline bool isSilent(const extractor::guidance::TurnInstruction instruction)
 {
@@ -21,34 +29,6 @@ inline bool isSilent(const extractor::guidance::TurnInstruction instruction)
            instruction.type == extractor::guidance::TurnType::StayOnRoundabout;
 }
 
-inline bool entersRoundabout(const extractor::guidance::TurnInstruction instruction)
-{
-    return (instruction.type == extractor::guidance::TurnType::EnterRoundabout ||
-            instruction.type == extractor::guidance::TurnType::EnterRotary ||
-            instruction.type == extractor::guidance::TurnType::EnterRoundaboutIntersection ||
-            instruction.type == extractor::guidance::TurnType::EnterRoundaboutAtExit ||
-            instruction.type == extractor::guidance::TurnType::EnterRotaryAtExit ||
-            instruction.type == extractor::guidance::TurnType::EnterRoundaboutIntersectionAtExit ||
-            instruction.type == extractor::guidance::TurnType::EnterAndExitRoundabout ||
-            instruction.type == extractor::guidance::TurnType::EnterAndExitRotary ||
-            instruction.type == extractor::guidance::TurnType::EnterAndExitRotary);
-}
-
-inline bool leavesRoundabout(const extractor::guidance::TurnInstruction instruction)
-{
-    return (instruction.type == extractor::guidance::TurnType::ExitRoundabout ||
-            instruction.type == extractor::guidance::TurnType::ExitRotary ||
-            instruction.type == extractor::guidance::TurnType::ExitRoundaboutIntersection ||
-            instruction.type == extractor::guidance::TurnType::EnterAndExitRoundabout ||
-            instruction.type == extractor::guidance::TurnType::EnterAndExitRotary ||
-            instruction.type == extractor::guidance::TurnType::EnterAndExitRoundaboutIntersection);
-}
-
-inline bool staysOnRoundabout(const extractor::guidance::TurnInstruction instruction)
-{
-    return instruction.type == extractor::guidance::TurnType::StayOnRoundabout;
-}
-
 inline extractor::guidance::DirectionModifier::Enum angleToDirectionModifier(const double bearing)
 {
     if (bearing < 135)
@@ -63,10 +43,36 @@ inline extractor::guidance::DirectionModifier::Enum angleToDirectionModifier(con
     return extractor::guidance::DirectionModifier::Left;
 }
 
-inline double angularDeviation(const double angle, const double from)
+// Runs fn on RouteStep sub-ranges determined to be roundabouts.
+// The function fn is getting called with a roundabout range as in: [enter, .., leave].
+//
+// The following situations are taken care for (i.e. we discard them):
+//  - partial roundabout: enter without exit or exit without enter
+//  - data issues: no roundabout, exit before enter
+template <typename Iter, typename Fn> inline Fn forEachRoundabout(Iter first, Iter last, Fn fn)
 {
-    const double deviation = std::abs(angle - from);
-    return std::min(360 - deviation, deviation);
+    while (first != last)
+    {
+        const auto enter = std::find_if(first, last, [](const RouteStep &step) {
+            return entersRoundabout(step.maneuver.instruction);
+        });
+
+        // enter has to come before leave, otherwise: faulty data / partial roundabout, skip those
+        const auto leave = std::find_if(enter, last, [](const RouteStep &step) {
+            return leavesRoundabout(step.maneuver.instruction);
+        });
+
+        // No roundabouts, or partial one (like start / end inside a roundabout)
+        if (enter == last || leave == last)
+            break;
+
+        (void)fn(std::make_pair(enter, leave));
+
+        // Skip to first step after the currently handled enter / leave pair
+        first = std::next(leave);
+    }
+
+    return fn;
 }
 
 } // namespace guidance
diff --git a/include/engine/internal_route_result.hpp b/include/engine/internal_route_result.hpp
index af58705..6f10769 100644
--- a/include/engine/internal_route_result.hpp
+++ b/include/engine/internal_route_result.hpp
@@ -4,9 +4,9 @@
 #include "extractor/guidance/turn_instruction.hpp"
 #include "extractor/travel_mode.hpp"
 #include "engine/phantom_node.hpp"
-#include "util/typedefs.hpp"
-
 #include "osrm/coordinate.hpp"
+#include "util/guidance/turn_lanes.hpp"
+#include "util/typedefs.hpp"
 
 #include <vector>
 
@@ -27,6 +27,8 @@ struct PathData
     EdgeWeight duration_until_turn;
     // instruction to execute at the turn
     extractor::guidance::TurnInstruction turn_instruction;
+    // turn lane data
+    util::guidance::LaneTupelIdPair lane_data;
     // travel mode of the street that leads to the turn
     extractor::TravelMode travel_mode : 4;
     // entry class of the turn, indicating possibility of turns
diff --git a/include/engine/routing_algorithms/alternative_path.hpp b/include/engine/routing_algorithms/alternative_path.hpp
index df5cfd8..8bffd26 100644
--- a/include/engine/routing_algorithms/alternative_path.hpp
+++ b/include/engine/routing_algorithms/alternative_path.hpp
@@ -546,8 +546,8 @@ class AlternativeRouting final
             }
         }
 
-        via_path_index = partially_unpacked_via_path.size() - 1;
-        shortest_path_index = partially_unpacked_shortest_path.size() - 1;
+        via_path_index = static_cast<int64_t>(partially_unpacked_via_path.size()) - 1;
+        shortest_path_index = static_cast<int64_t>(partially_unpacked_shortest_path.size()) - 1;
         for (; via_path_index > 0 && shortest_path_index > 0;
              --via_path_index, --shortest_path_index)
         {
diff --git a/include/engine/routing_algorithms/routing_base.hpp b/include/engine/routing_algorithms/routing_base.hpp
index 8181dff..4f77c09 100644
--- a/include/engine/routing_algorithms/routing_base.hpp
+++ b/include/engine/routing_algorithms/routing_base.hpp
@@ -327,10 +327,14 @@ template <class DataFacadeT, class Derived> class BasicRoutingInterface
                                  name_index,
                                  weight_vector[i],
                                  extractor::guidance::TurnInstruction::NO_TURN(),
+                                 {{0, INVALID_LANEID}, INVALID_LANE_DESCRIPTIONID},
                                  travel_mode,
                                  INVALID_ENTRY_CLASSID});
                 }
                 BOOST_ASSERT(unpacked_path.size() > 0);
+                if (facade->hasLaneData(ed.id))
+                    unpacked_path.back().lane_data = facade->GetLaneData(ed.id);
+
                 unpacked_path.back().entry_classid = facade->GetEntryClassID(ed.id);
                 unpacked_path.back().turn_instruction = turn_instruction;
                 unpacked_path.back().duration_until_turn += (ed.distance - total_weight);
@@ -389,6 +393,7 @@ template <class DataFacadeT, class Derived> class BasicRoutingInterface
                 phantom_node_pair.target_phantom.name_id,
                 weight_vector[i],
                 extractor::guidance::TurnInstruction::NO_TURN(),
+                {{0, INVALID_LANEID}, INVALID_LANE_DESCRIPTIONID},
                 target_traversed_in_reverse ? phantom_node_pair.target_phantom.backward_travel_mode
                                             : phantom_node_pair.target_phantom.forward_travel_mode,
                 INVALID_ENTRY_CLASSID});
diff --git a/include/extractor/edge_based_graph_factory.hpp b/include/extractor/edge_based_graph_factory.hpp
index 0280501..f8d1327 100644
--- a/include/extractor/edge_based_graph_factory.hpp
+++ b/include/extractor/edge_based_graph_factory.hpp
@@ -13,6 +13,7 @@
 
 #include "extractor/guidance/turn_analysis.hpp"
 #include "extractor/guidance/turn_instruction.hpp"
+#include "extractor/guidance/turn_lane_types.hpp"
 #include "util/guidance/bearing_class.hpp"
 #include "util/guidance/entry_class.hpp"
 
@@ -42,22 +43,60 @@ namespace osrm
 namespace extractor
 {
 
+namespace lookup
+{
+// Set to 1 byte alignment
+#pragma pack(push, 1)
+struct SegmentHeaderBlock
+{
+    std::uint32_t num_osm_nodes;
+    OSMNodeID previous_osm_node_id;
+};
+#pragma pack(pop)
+static_assert(sizeof(SegmentHeaderBlock) == 12, "SegmentHeaderBlock is not packed correctly");
+
+#pragma pack(push, 1)
+struct SegmentBlock
+{
+    OSMNodeID this_osm_node_id;
+    double segment_length;
+    std::int32_t segment_weight;
+};
+#pragma pack(pop)
+static_assert(sizeof(SegmentBlock) == 20, "SegmentBlock is not packed correctly");
+
+#pragma pack(push, 1)
+struct PenaltyBlock
+{
+    std::uint32_t fixed_penalty;
+    OSMNodeID from_id;
+    OSMNodeID via_id;
+    OSMNodeID to_id;
+};
+#pragma pack(pop)
+static_assert(sizeof(PenaltyBlock) == 28, "PenaltyBlock is not packed correctly");
+}
+
 class EdgeBasedGraphFactory
 {
   public:
     EdgeBasedGraphFactory(const EdgeBasedGraphFactory &) = delete;
     EdgeBasedGraphFactory &operator=(const EdgeBasedGraphFactory &) = delete;
 
-    explicit EdgeBasedGraphFactory(std::shared_ptr<util::NodeBasedDynamicGraph> node_based_graph,
-                                   const CompressedEdgeContainer &compressed_edge_container,
-                                   const std::unordered_set<NodeID> &barrier_nodes,
-                                   const std::unordered_set<NodeID> &traffic_lights,
-                                   std::shared_ptr<const RestrictionMap> restriction_map,
-                                   const std::vector<QueryNode> &node_info_list,
-                                   ProfileProperties profile_properties,
-                                   const util::NameTable &name_table);
+    explicit EdgeBasedGraphFactory(
+        std::shared_ptr<util::NodeBasedDynamicGraph> node_based_graph,
+        const CompressedEdgeContainer &compressed_edge_container,
+        const std::unordered_set<NodeID> &barrier_nodes,
+        const std::unordered_set<NodeID> &traffic_lights,
+        std::shared_ptr<const RestrictionMap> restriction_map,
+        const std::vector<QueryNode> &node_info_list,
+        ProfileProperties profile_properties,
+        const util::NameTable &name_table,
+        const std::vector<std::uint32_t> &turn_lane_offsets,
+        const std::vector<guidance::TurnLaneType::Mask> &turn_lane_masks);
 
     void Run(const std::string &original_edge_data_filename,
+             const std::string &turn_lane_data_filename,
              lua_State *lua_state,
              const std::string &edge_segment_lookup_filename,
              const std::string &edge_penalty_filename,
@@ -104,7 +143,7 @@ class EdgeBasedGraphFactory
     //! list of edge based nodes (compressed segments)
     std::vector<EdgeBasedNode> m_edge_based_node_list;
     util::DeallocatingVector<EdgeBasedEdge> m_edge_based_edge_list;
-    unsigned m_max_edge_id;
+    EdgeID m_max_edge_id;
 
     const std::vector<QueryNode> &m_node_info_list;
     std::shared_ptr<util::NodeBasedDynamicGraph> m_node_based_graph;
@@ -117,11 +156,14 @@ class EdgeBasedGraphFactory
     ProfileProperties profile_properties;
 
     const util::NameTable &name_table;
+    const std::vector<std::uint32_t> &turn_lane_offsets;
+    const std::vector<guidance::TurnLaneType::Mask> &turn_lane_masks;
 
     void CompressGeometry();
     unsigned RenumberEdges();
     void GenerateEdgeExpandedNodes();
     void GenerateEdgeExpandedEdges(const std::string &original_edge_data_filename,
+                                   const std::string &turn_lane_data_filename,
                                    lua_State *lua_state,
                                    const std::string &edge_segment_lookup_filename,
                                    const std::string &edge_fixed_penalties_filename,
diff --git a/include/extractor/external_memory_node.hpp b/include/extractor/external_memory_node.hpp
index e531580..2d48013 100644
--- a/include/extractor/external_memory_node.hpp
+++ b/include/extractor/external_memory_node.hpp
@@ -5,6 +5,8 @@
 
 #include "util/typedefs.hpp"
 
+#include <cstdint>
+
 namespace osrm
 {
 namespace extractor
@@ -26,13 +28,13 @@ struct ExternalMemoryNode : QueryNode
     static ExternalMemoryNode min_value()
     {
         return ExternalMemoryNode(
-            util::FixedLongitude(0), util::FixedLatitude(0), MIN_OSM_NODEID, false, false);
+            util::FixedLongitude{0}, util::FixedLatitude{0}, MIN_OSM_NODEID, false, false);
     }
 
     static ExternalMemoryNode max_value()
     {
-        return ExternalMemoryNode(util::FixedLongitude(std::numeric_limits<int>::max()),
-                                  util::FixedLatitude(std::numeric_limits<int>::max()),
+        return ExternalMemoryNode(util::FixedLongitude{std::numeric_limits<std::int32_t>::max()},
+                                  util::FixedLatitude{std::numeric_limits<std::int32_t>::max()},
                                   MAX_OSM_NODEID,
                                   false,
                                   false);
diff --git a/include/extractor/extraction_containers.hpp b/include/extractor/extraction_containers.hpp
index ca27e8d..ccd7104 100644
--- a/include/extractor/extraction_containers.hpp
+++ b/include/extractor/extraction_containers.hpp
@@ -3,10 +3,12 @@
 
 #include "extractor/external_memory_node.hpp"
 #include "extractor/first_and_last_segment_of_way.hpp"
+#include "extractor/guidance/turn_lane_types.hpp"
 #include "extractor/internal_extractor_edge.hpp"
 #include "extractor/restriction.hpp"
 #include "extractor/scripting_environment.hpp"
 
+#include <cstdint>
 #include <stxxl/vector>
 #include <unordered_map>
 
@@ -37,7 +39,11 @@ class ExtractionContainers
     void WriteNodes(std::ofstream &file_out_stream) const;
     void WriteRestrictions(const std::string &restrictions_file_name) const;
     void WriteEdges(std::ofstream &file_out_stream) const;
-    void WriteNames(const std::string &names_file_name) const;
+    void WriteCharData(const std::string &file_name);
+    void
+    WriteTurnLaneMasks(const std::string &file_name,
+                       const stxxl::vector<std::uint32_t> &turn_lane_offsets,
+                       const stxxl::vector<guidance::TurnLaneType::Mask> &turn_lane_masks) const;
 
   public:
     using STXXLNodeIDVector = stxxl::vector<OSMNodeID>;
@@ -45,12 +51,17 @@ class ExtractionContainers
     using STXXLEdgeVector = stxxl::vector<InternalExtractorEdge>;
     using STXXLRestrictionsVector = stxxl::vector<InputRestrictionContainer>;
     using STXXLWayIDStartEndVector = stxxl::vector<FirstAndLastSegmentOfWay>;
+    using STXXLNameCharData = stxxl::vector<unsigned char>;
+    using STXXLNameOffsets = stxxl::vector<unsigned>;
 
     STXXLNodeIDVector used_node_id_list;
     STXXLNodeVector all_nodes_list;
     STXXLEdgeVector all_edges_list;
-    stxxl::vector<char> name_char_data;
-    stxxl::vector<unsigned> name_lengths;
+    STXXLNameCharData name_char_data;
+    STXXLNameOffsets name_offsets;
+    // an adjacency array containing all turn lane masks
+    stxxl::vector<std::uint32_t> turn_lane_offsets;
+    stxxl::vector<guidance::TurnLaneType::Mask> turn_lane_masks;
     STXXLRestrictionsVector restrictions_list;
     STXXLWayIDStartEndVector way_start_end_id_list;
     std::unordered_map<OSMNodeID, NodeID> external_to_internal_node_id_map;
@@ -61,6 +72,7 @@ class ExtractionContainers
     void PrepareData(const std::string &output_file_name,
                      const std::string &restrictions_file_name,
                      const std::string &names_file_name,
+                     const std::string &turn_lane_file_name,
                      lua_State *segment_state);
 };
 }
diff --git a/include/extractor/extraction_helper_functions.hpp b/include/extractor/extraction_helper_functions.hpp
index aefac66..9fb5477 100644
--- a/include/extractor/extraction_helper_functions.hpp
+++ b/include/extractor/extraction_helper_functions.hpp
@@ -7,6 +7,8 @@
 #include <limits>
 #include <string>
 
+#include "extractor/guidance/toolkit.hpp"
+
 namespace osrm
 {
 namespace extractor
@@ -98,6 +100,12 @@ inline unsigned parseDuration(const std::string &s)
 
     return !s.empty() && iter == s.end() ? duration : std::numeric_limits<unsigned>::max();
 }
+
+inline std::string
+trimLaneString(std::string lane_string, std::int32_t count_left, std::int32_t count_right)
+{
+    return extractor::guidance::trimLaneString(std::move(lane_string), count_left, count_right);
+}
 }
 }
 
diff --git a/include/extractor/extraction_way.hpp b/include/extractor/extraction_way.hpp
index 437d17c..8ae948a 100644
--- a/include/extractor/extraction_way.hpp
+++ b/include/extractor/extraction_way.hpp
@@ -2,6 +2,7 @@
 #define EXTRACTION_WAY_HPP
 
 #include "extractor/travel_mode.hpp"
+#include "util/guidance/turn_lanes.hpp"
 #include "util/typedefs.hpp"
 
 #include <string>
@@ -35,6 +36,8 @@ struct ExtractionWay
         destinations.clear();
         forward_travel_mode = TRAVEL_MODE_INACCESSIBLE;
         backward_travel_mode = TRAVEL_MODE_INACCESSIBLE;
+        turn_lanes_forward.clear();
+        turn_lanes_backward.clear();
     }
 
     // These accessors exists because it's not possible to take the address of a bitfield,
@@ -50,6 +53,8 @@ struct ExtractionWay
     std::string name;
     std::string pronunciation;
     std::string destinations;
+    std::string turn_lanes_forward;
+    std::string turn_lanes_backward;
     bool roundabout;
     bool is_access_restricted;
     bool is_startpoint;
diff --git a/include/extractor/extractor.hpp b/include/extractor/extractor.hpp
index b929523..fa355c8 100644
--- a/include/extractor/extractor.hpp
+++ b/include/extractor/extractor.hpp
@@ -54,7 +54,7 @@ class Extractor
   private:
     ExtractorConfig config;
 
-    std::pair<std::size_t, std::size_t>
+    std::pair<std::size_t, EdgeID>
     BuildEdgeExpandedGraph(lua_State *lua_state,
                            const ProfileProperties &profile_properties,
                            std::vector<QueryNode> &internal_to_external_node_map,
@@ -79,7 +79,7 @@ class Extractor
                        std::vector<QueryNode> &internal_to_external_node_map);
 
     void WriteEdgeBasedGraph(const std::string &output_file_filename,
-                             const size_t max_edge_id,
+                             const EdgeID max_edge_id,
                              util::DeallocatingVector<EdgeBasedEdge> const &edge_based_edge_list);
 
     void WriteIntersectionClassificationData(
diff --git a/include/extractor/extractor_callbacks.hpp b/include/extractor/extractor_callbacks.hpp
index 120884d..2adfb7d 100644
--- a/include/extractor/extractor_callbacks.hpp
+++ b/include/extractor/extractor_callbacks.hpp
@@ -2,6 +2,8 @@
 #define EXTRACTOR_CALLBACKS_HPP
 
 #include "util/typedefs.hpp"
+#include "extractor/guidance/turn_lane_types.hpp"
+
 #include <boost/functional/hash.hpp>
 #include <boost/optional/optional_fwd.hpp>
 
@@ -38,6 +40,7 @@ class ExtractorCallbacks
     using MapKey = std::pair<std::string, std::string>;
     using MapVal = unsigned;
     std::unordered_map<MapKey, MapVal, boost::hash<MapKey>> string_map;
+    std::unordered_map<guidance::TurnLaneDescription,LaneDescriptionID,guidance::TurnLaneDescription_hash> lane_description_map;
     ExtractionContainers &external_memory;
 
   public:
diff --git a/include/extractor/extractor_config.hpp b/include/extractor/extractor_config.hpp
index ec4d086..d0b5fd2 100644
--- a/include/extractor/extractor_config.hpp
+++ b/include/extractor/extractor_config.hpp
@@ -61,6 +61,8 @@ struct ExtractorConfig
         output_file_name = basepath + ".osrm";
         restriction_file_name = basepath + ".osrm.restrictions";
         names_file_name = basepath + ".osrm.names";
+        turn_lane_descriptions_file_name = basepath + ".osrm.tls";
+        turn_lane_data_file_name = basepath + ".osrm.tld";
         timestamp_file_name = basepath + ".osrm.timestamp";
         geometry_output_path = basepath + ".osrm.geometry";
         node_output_path = basepath + ".osrm.nodes";
@@ -82,6 +84,8 @@ struct ExtractorConfig
     std::string output_file_name;
     std::string restriction_file_name;
     std::string names_file_name;
+    std::string turn_lane_data_file_name;
+    std::string turn_lane_descriptions_file_name;
     std::string timestamp_file_name;
     std::string geometry_output_path;
     std::string edge_output_path;
diff --git a/include/extractor/guidance/classification_data.hpp b/include/extractor/guidance/classification_data.hpp
index fd63e4f..0454041 100644
--- a/include/extractor/guidance/classification_data.hpp
+++ b/include/extractor/guidance/classification_data.hpp
@@ -58,6 +58,12 @@ inline bool isLinkClass(const FunctionalRoadClass road_class)
            road_class == FunctionalRoadClass::TERTIARY_LINK;
 }
 
+// check wheter a class is a motorway like class
+inline bool isMotorwayClass(const FunctionalRoadClass road_class)
+{
+    return road_class == FunctionalRoadClass::MOTORWAY || road_class == FunctionalRoadClass::TRUNK;
+}
+
 // TODO augment this with all data required for guidance generation
 struct RoadClassificationData
 {
diff --git a/include/extractor/guidance/intersection.hpp b/include/extractor/guidance/intersection.hpp
index 8b350e2..940d26f 100644
--- a/include/extractor/guidance/intersection.hpp
+++ b/include/extractor/guidance/intersection.hpp
@@ -22,6 +22,7 @@ struct TurnOperation final
     EdgeID eid;
     double angle;
     TurnInstruction instruction;
+    LaneDataID lane_data_id;
 };
 
 // A Connected Road is the internal representation of a potential turn. Internally, we require
@@ -59,6 +60,9 @@ std::string toString(const ConnectedRoad &road);
 
 typedef std::vector<ConnectedRoad> Intersection;
 
+Intersection::const_iterator findClosestTurn(const Intersection &intersection, const double angle);
+Intersection::iterator findClosestTurn(Intersection &intersection, const double angle);
+
 } // namespace guidance
 } // namespace extractor
 } // namespace osrm
diff --git a/include/extractor/guidance/intersection_generator.hpp b/include/extractor/guidance/intersection_generator.hpp
index be99a71..6dcba08 100644
--- a/include/extractor/guidance/intersection_generator.hpp
+++ b/include/extractor/guidance/intersection_generator.hpp
@@ -5,6 +5,7 @@
 #include "extractor/guidance/intersection.hpp"
 #include "extractor/query_node.hpp"
 #include "extractor/restriction_map.hpp"
+#include "util/name_table.hpp"
 #include "util/node_based_graph.hpp"
 #include "util/typedefs.hpp"
 
diff --git a/include/extractor/guidance/motorway_handler.hpp b/include/extractor/guidance/motorway_handler.hpp
index 75abe88..e8830aa 100644
--- a/include/extractor/guidance/motorway_handler.hpp
+++ b/include/extractor/guidance/motorway_handler.hpp
@@ -2,7 +2,6 @@
 #define OSRM_EXTRACTOR_GUIDANCE_MOTORWAY_HANDLER_HPP_
 
 #include "extractor/guidance/intersection.hpp"
-#include "extractor/guidance/intersection_generator.hpp"
 #include "extractor/guidance/intersection_handler.hpp"
 #include "extractor/query_node.hpp"
 
@@ -26,8 +25,7 @@ class MotorwayHandler : public IntersectionHandler
     MotorwayHandler(const util::NodeBasedDynamicGraph &node_based_graph,
                     const std::vector<QueryNode> &node_info_list,
                     const util::NameTable &name_table,
-                    const SuffixTable &street_name_suffix_table,
-                    const IntersectionGenerator &intersection_generator);
+                    const SuffixTable &street_name_suffix_table);
     ~MotorwayHandler() override final;
 
     // check whether the handler can actually handle the intersection
@@ -47,8 +45,6 @@ class MotorwayHandler : public IntersectionHandler
     Intersection fromRamp(const EdgeID via_edge, Intersection intersection) const;
 
     Intersection fallback(Intersection intersection) const;
-
-    const IntersectionGenerator &intersection_generator;
 };
 
 } // namespace guidance
diff --git a/include/extractor/guidance/toolkit.hpp b/include/extractor/guidance/toolkit.hpp
index 5eb3fa8..611cf17 100644
--- a/include/extractor/guidance/toolkit.hpp
+++ b/include/extractor/guidance/toolkit.hpp
@@ -5,6 +5,8 @@
 #include "util/coordinate.hpp"
 #include "util/coordinate_calculation.hpp"
 #include "util/guidance/toolkit.hpp"
+#include "util/guidance/turn_lanes.hpp"
+#include "util/typedefs.hpp"
 
 #include "extractor/compressed_edge_container.hpp"
 #include "extractor/query_node.hpp"
@@ -20,10 +22,12 @@
 #include <cstdint>
 #include <map>
 #include <string>
+#include <unordered_map>
 #include <utility>
 
 #include <boost/algorithm/string.hpp>
 #include <boost/algorithm/string/predicate.hpp>
+#include <boost/functional/hash.hpp>
 
 namespace osrm
 {
@@ -32,7 +36,12 @@ namespace extractor
 namespace guidance
 {
 
+using util::guidance::LaneTupelIdPair;
+using LaneDataIdMap = std::unordered_map<LaneTupelIdPair, LaneDataID, boost::hash<LaneTupelIdPair>>;
+
 using util::guidance::angularDeviation;
+using util::guidance::entersRoundabout;
+using util::guidance::leavesRoundabout;
 
 namespace detail
 {
@@ -474,7 +483,55 @@ inline bool hasRoundaboutType(const TurnInstruction instruction)
                                                     TurnType::StayOnRoundabout};
     const auto valid_end = valid_types + 13;
     return std::find(valid_types, valid_end, instruction.type) != valid_end;
-};
+}
+
+// Public service vehicle lanes and similar can introduce additional lanes into the lane string that
+// are not specifically marked for left/right turns. This function can be used from the profile to
+// trim the lane string appropriately
+//
+// left|throught|
+// in combination with lanes:psv:forward=1
+// will be corrected to left|throught, since the final lane is not drivable.
+// This is in contrast to a situation with lanes:psv:forward=0 (or not set) where left|through|
+// represents left|through|through
+inline std::string
+trimLaneString(std::string lane_string, std::int32_t count_left, std::int32_t count_right)
+{
+    if (count_left)
+    {
+        bool sane = count_left < static_cast<std::int32_t>(lane_string.size());
+        for (std::int32_t i = 0; i < count_left; ++i)
+            // this is adjusted for our fake pipe. The moment cucumber can handle multiple escaped
+            // pipes, the '&' part can be removed
+            if (lane_string[i] != '|' && lane_string[i] != '&')
+            {
+                sane = false;
+                break;
+            }
+
+        if (sane)
+        {
+            lane_string.erase(lane_string.begin(), lane_string.begin() + count_left);
+        }
+    }
+    if (count_right)
+    {
+        bool sane = count_right < static_cast<std::int32_t>(lane_string.size());
+        for (auto itr = lane_string.rbegin();
+             itr != lane_string.rend() && itr != lane_string.rbegin() + count_right;
+             ++itr)
+        {
+            if (*itr != '|' && *itr != '&')
+            {
+                sane = false;
+                break;
+            }
+        }
+        if (sane)
+            lane_string.resize(lane_string.size() - count_right);
+    }
+    return lane_string;
+}
 
 } // namespace guidance
 } // namespace extractor
diff --git a/include/extractor/guidance/turn_analysis.hpp b/include/extractor/guidance/turn_analysis.hpp
index cade181..5642ea5 100644
--- a/include/extractor/guidance/turn_analysis.hpp
+++ b/include/extractor/guidance/turn_analysis.hpp
@@ -44,10 +44,14 @@ class TurnAnalysis
                  const SuffixTable &street_name_suffix_table);
 
     // the entry into the turn analysis
-    std::vector<TurnOperation> getTurns(const NodeID from_node, const EdgeID via_eid) const;
-
-    // access to the intersection representation for classification purposes
     Intersection getIntersection(const NodeID from_node, const EdgeID via_eid) const;
+    Intersection
+    assignTurnTypes(const NodeID from_node, const EdgeID via_eid, Intersection intersection) const;
+
+    std::vector<TurnOperation>
+    transformIntersectionIntoTurns(const Intersection &intersection) const;
+
+    const IntersectionGenerator &getGenerator() const;
 
   private:
     const util::NodeBasedDynamicGraph &node_based_graph;
diff --git a/include/extractor/guidance/turn_discovery.hpp b/include/extractor/guidance/turn_discovery.hpp
new file mode 100644
index 0000000..3a36d3d
--- /dev/null
+++ b/include/extractor/guidance/turn_discovery.hpp
@@ -0,0 +1,38 @@
+#ifndef OSRM_EXTRACTOR_GUIDANCE_TURN_DISCOVERY_HPP_
+#define OSRM_EXTRACTOR_GUIDANCE_TURN_DISCOVERY_HPP_
+
+#include "extractor/guidance/intersection.hpp"
+#include "extractor/guidance/turn_analysis.hpp"
+#include "util/typedefs.hpp"
+
+#include <string>
+
+namespace osrm
+{
+namespace extractor
+{
+namespace guidance
+{
+namespace lanes
+{
+
+// OSRM processes edges by looking at a via_edge, coming into an intersection. For turn lanes, we
+// might require to actually look back a turn. We do so in the hope that the turn lanes match up at
+// the previous intersection for all incoming lanes.
+bool findPreviousIntersection(
+    const NodeID node,
+    const EdgeID via_edge,
+    const Intersection intersection,
+    const TurnAnalysis &turn_analysis,                   // to generate other intersections
+    const util::NodeBasedDynamicGraph &node_based_graph, // query edge data
+    // output parameters, will be in an arbitrary state on failure
+    NodeID &result_node,
+    EdgeID &result_via_edge,
+    Intersection &result_intersection);
+
+} // namespace lanes
+} // namespace guidance
+} // namespace extractor
+} // namespace osrm
+
+#endif /*OSRM_EXTRACTOR_GUIDANCE_TURN_DISCOVERY_HPP_*/
diff --git a/include/extractor/guidance/turn_instruction.hpp b/include/extractor/guidance/turn_instruction.hpp
index dc3ef5a..c85c0d2 100644
--- a/include/extractor/guidance/turn_instruction.hpp
+++ b/include/extractor/guidance/turn_instruction.hpp
@@ -6,6 +6,8 @@
 #include <boost/assert.hpp>
 
 #include "extractor/guidance/roundabout_type.hpp"
+#include "util/guidance/turn_lanes.hpp"
+#include "util/typedefs.hpp"
 
 namespace osrm
 {
@@ -53,48 +55,45 @@ const constexpr Enum EnterRotary = 12;                 // Enter a rotary
 const constexpr Enum EnterAndExitRotary = 13;          // Touching a rotary
 const constexpr Enum EnterRoundaboutIntersection = 14; // Entering a small Roundabout
 const constexpr Enum EnterAndExitRoundaboutIntersection = 15; // Touching a roundabout
+const constexpr Enum UseLane = 16; // No Turn, but you need to stay on a given lane!
 
 // Values below here are silent instructions
-const constexpr Enum NoTurn = 16;                // end of segment without turn/middle of a segment
-const constexpr Enum Suppressed = 17;            // location that suppresses a turn
-const constexpr Enum EnterRoundaboutAtExit = 18; // Entering a small Roundabout at a countable exit
-const constexpr Enum ExitRoundabout = 19;        // Exiting a small Roundabout
-const constexpr Enum EnterRotaryAtExit = 20;     // Enter A Rotary at a countable exit
-const constexpr Enum ExitRotary = 21;            // Exit a rotary
+const constexpr Enum NoTurn = 17;                // end of segment without turn/middle of a segment
+const constexpr Enum Suppressed = 18;            // location that suppresses a turn
+const constexpr Enum EnterRoundaboutAtExit = 19; // Entering a small Roundabout at a countable exit
+const constexpr Enum ExitRoundabout = 20;        // Exiting a small Roundabout
+const constexpr Enum EnterRotaryAtExit = 21;     // Enter A Rotary at a countable exit
+const constexpr Enum ExitRotary = 22;            // Exit a rotary
 const constexpr Enum EnterRoundaboutIntersectionAtExit =
-    22; // Entering a small Roundabout at a countable exit
-const constexpr Enum ExitRoundaboutIntersection = 23; // Exiting a small Roundabout
-const constexpr Enum StayOnRoundabout = 24; // Continue on Either a small or a large Roundabout
+    23; // Entering a small Roundabout at a countable exit
+const constexpr Enum ExitRoundaboutIntersection = 24; // Exiting a small Roundabout
+const constexpr Enum StayOnRoundabout = 25; // Continue on Either a small or a large Roundabout
 const constexpr Enum Sliproad =
-    25; // Something that looks like a ramp, but is actually just a small sliproad
+    26; // Something that looks like a ramp, but is actually just a small sliproad
 }
 
 // turn angle in 1.40625 degree -> 128 == 180 degree
 struct TurnInstruction
 {
+    using LaneTupel = util::guidance::LaneTupel;
     TurnInstruction(const TurnType::Enum type = TurnType::Invalid,
-                    const DirectionModifier::Enum direction_modifier = DirectionModifier::Straight)
+                    const DirectionModifier::Enum direction_modifier = DirectionModifier::UTurn)
         : type(type), direction_modifier(direction_modifier)
     {
     }
 
     TurnType::Enum type : 5;
     DirectionModifier::Enum direction_modifier : 3;
+    // the lane tupel that is used for the turn
 
-    static TurnInstruction INVALID()
-    {
-        return TurnInstruction(TurnType::Invalid, DirectionModifier::UTurn);
-    }
+    static TurnInstruction INVALID() { return {TurnType::Invalid, DirectionModifier::UTurn}; }
 
-    static TurnInstruction NO_TURN()
-    {
-        return TurnInstruction(TurnType::NoTurn, DirectionModifier::UTurn);
-    }
+    static TurnInstruction NO_TURN() { return {TurnType::NoTurn, DirectionModifier::UTurn}; }
 
     static TurnInstruction REMAIN_ROUNDABOUT(const RoundaboutType,
                                              const DirectionModifier::Enum modifier)
     {
-        return TurnInstruction(TurnType::StayOnRoundabout, modifier);
+        return {TurnType::StayOnRoundabout, modifier};
     }
 
     static TurnInstruction ENTER_ROUNDABOUT(const RoundaboutType roundabout_type,
@@ -146,7 +145,7 @@ struct TurnInstruction
     }
 };
 
-static_assert(sizeof(TurnInstruction) == 1, "TurnInstruction does not fit one byte");
+static_assert(sizeof(TurnInstruction) == 1, "TurnInstruction does not fit a byte");
 
 inline bool operator!=(const TurnInstruction lhs, const TurnInstruction rhs)
 {
diff --git a/include/extractor/guidance/turn_lane_augmentation.hpp b/include/extractor/guidance/turn_lane_augmentation.hpp
new file mode 100644
index 0000000..81de8f9
--- /dev/null
+++ b/include/extractor/guidance/turn_lane_augmentation.hpp
@@ -0,0 +1,24 @@
+#ifndef OSRM_EXTRACTOR_GUIDANCE_TURN_LANE_AUGMENTATION_HPP_
+#define OSRM_EXTRACTOR_GUIDANCE_TURN_LANE_AUGMENTATION_HPP_
+
+#include "extractor/guidance/intersection.hpp"
+#include "extractor/guidance/turn_lane_data.hpp"
+
+namespace osrm
+{
+namespace extractor
+{
+namespace guidance
+{
+namespace lanes
+{
+
+LaneDataVector handleNoneValueAtSimpleTurn(LaneDataVector lane_data,
+                                           const Intersection &intersection);
+
+} // namespace lanes
+} // namespace guidance
+} // namespace extractor
+} // namespace osrm
+
+#endif /* OSRM_EXTRACTOR_GUIDANCE_TURN_LANE_AUGMENTATION_HPP_ */
diff --git a/include/extractor/guidance/turn_lane_data.hpp b/include/extractor/guidance/turn_lane_data.hpp
new file mode 100644
index 0000000..f1a7d7e
--- /dev/null
+++ b/include/extractor/guidance/turn_lane_data.hpp
@@ -0,0 +1,44 @@
+#ifndef OSRM_EXTRACTOR_GUIDANCE_TURN_LANE_DATA_HPP_
+#define OSRM_EXTRACTOR_GUIDANCE_TURN_LANE_DATA_HPP_
+
+#include "util/typedefs.hpp"
+#include "extractor/guidance/turn_lane_types.hpp"
+#include <string>
+#include <vector>
+
+namespace osrm
+{
+namespace extractor
+{
+namespace guidance
+{
+namespace lanes
+{
+
+struct TurnLaneData
+{
+    TurnLaneType::Mask tag;
+    LaneID from;
+    LaneID to;
+
+    bool operator<(const TurnLaneData &other) const;
+};
+typedef std::vector<TurnLaneData> LaneDataVector;
+
+// convertes a string given in the OSM format into a TurnLaneData vector
+LaneDataVector laneDataFromDescription(const TurnLaneDescription &turn_lane_description);
+
+// Locate A Tag in a lane data vector (if multiple tags are set, the first one found is returned)
+LaneDataVector::const_iterator findTag(const TurnLaneType::Mask tag, const LaneDataVector &data);
+LaneDataVector::iterator findTag(const TurnLaneType::Mask tag, LaneDataVector &data);
+
+// Returns true if any of the queried tags is contained
+bool hasTag(const TurnLaneType::Mask tag, const LaneDataVector &data);
+
+} // namespace lane_data_generation
+
+} // namespace guidance
+} // namespace extractor
+} // namespace osrm
+
+#endif /* OSRM_EXTRACTOR_GUIDANCE_TURN_LANE_DATA_HPP_ */
diff --git a/include/extractor/guidance/turn_lane_handler.hpp b/include/extractor/guidance/turn_lane_handler.hpp
new file mode 100644
index 0000000..2fb5416
--- /dev/null
+++ b/include/extractor/guidance/turn_lane_handler.hpp
@@ -0,0 +1,85 @@
+#ifndef OSRM_EXTRACTOR_GUIDANCE_TURN_LANE_HANDLER_HPP_
+#define OSRM_EXTRACTOR_GUIDANCE_TURN_LANE_HANDLER_HPP_
+
+#include "extractor/guidance/intersection.hpp"
+#include "extractor/guidance/toolkit.hpp"
+#include "extractor/guidance/turn_analysis.hpp"
+#include "extractor/guidance/turn_lane_data.hpp"
+#include "extractor/guidance/turn_lane_types.hpp"
+#include "extractor/query_node.hpp"
+
+#include "util/guidance/turn_lanes.hpp"
+#include "util/name_table.hpp"
+#include "util/node_based_graph.hpp"
+#include "util/typedefs.hpp"
+
+#include <cstdint>
+#include <map>
+#include <string>
+#include <utility>
+#include <vector>
+
+namespace osrm
+{
+namespace extractor
+{
+namespace guidance
+{
+
+// Given an Intersection, the graph to access the data and  the turn lanes, the turn lane matcher
+// assigns appropriate turn tupels to the different turns.
+namespace lanes
+{
+class TurnLaneHandler
+{
+  public:
+    typedef std::vector<TurnLaneData> LaneDataVector;
+
+    TurnLaneHandler(const util::NodeBasedDynamicGraph &node_based_graph,
+                    const std::vector<std::uint32_t> &turn_lane_offsets,
+                    const std::vector<TurnLaneType::Mask> &turn_lane_masks,
+                    const std::vector<QueryNode> &node_info_list,
+                    const TurnAnalysis &turn_analysis);
+
+    Intersection assignTurnLanes(const NodeID at,
+                                 const EdgeID via_edge,
+                                 Intersection intersection,
+                                 LaneDataIdMap &id_map) const;
+
+  private:
+    // we need to be able to look at previous intersections to, in some cases, find the correct turn
+    // lanes for a turn
+    const util::NodeBasedDynamicGraph &node_based_graph;
+    const std::vector<std::uint32_t> &turn_lane_offsets;
+    const std::vector<TurnLaneType::Mask> &turn_lane_masks;
+    const std::vector<QueryNode> &node_info_list;
+    const TurnAnalysis &turn_analysis;
+
+    // check whether we can handle an intersection
+    bool isSimpleIntersection(const LaneDataVector &turn_lane_data,
+                              const Intersection &intersection) const;
+
+    // in case of a simple intersection, assign the lane entries
+    Intersection simpleMatchTuplesToTurns(Intersection intersection,
+                                          const LaneDataVector &lane_data,
+                                          const LaneDescriptionID lane_string_id,
+                                          LaneDataIdMap &id_map) const;
+
+    // partition lane data into lane data relevant at current turn and at next turn
+    std::pair<TurnLaneHandler::LaneDataVector, TurnLaneHandler::LaneDataVector> partitionLaneData(
+        const NodeID at, LaneDataVector turn_lane_data, const Intersection &intersection) const;
+
+    // if the current intersections turn string is empty, we check whether there is an incoming
+    // intersection whose turns might be related to this current intersection
+    Intersection handleTurnAtPreviousIntersection(const NodeID at,
+                                                  const EdgeID via_edge,
+                                                  Intersection intersection,
+                                                  LaneDataIdMap &id_map) const;
+};
+
+} // namespace lanes
+} // namespace guidance
+} // namespace extractor
+} // namespace osrm
+
+#endif // OSRM_EXTRACTOR_GUIDANCE_TURN_LANE_HANDLER_HPP_
diff --git a/include/extractor/guidance/turn_lane_matcher.hpp b/include/extractor/guidance/turn_lane_matcher.hpp
new file mode 100644
index 0000000..8dfcfe8
--- /dev/null
+++ b/include/extractor/guidance/turn_lane_matcher.hpp
@@ -0,0 +1,51 @@
+#ifndef OSRM_EXTRACTOR_GUIDANCE_TURN_LANE_MATCHER_HPP_
+#define OSRM_EXTRACTOR_GUIDANCE_TURN_LANE_MATCHER_HPP_
+
+#include "extractor/guidance/intersection.hpp"
+#include "extractor/guidance/toolkit.hpp"
+#include "extractor/guidance/turn_instruction.hpp"
+#include "extractor/guidance/turn_lane_data.hpp"
+
+#include "util/guidance/turn_lanes.hpp"
+#include "util/node_based_graph.hpp"
+
+#include <unordered_map>
+
+namespace osrm
+{
+namespace extractor
+{
+namespace guidance
+{
+namespace lanes
+{
+
+// Translate Turn Lane Tags into a matching modifier
+DirectionModifier::Enum getMatchingModifier(const TurnLaneType::Mask &tag);
+
+// check whether a match of a given tag and a turn instruction can be seen as valid
+bool isValidMatch(const TurnLaneType::Mask &tag, const TurnInstruction instruction);
+
+// localisation of the best possible match for a tag
+typename Intersection::const_iterator findBestMatch(const TurnLaneType::Mask &tag,
+                                                    const Intersection &intersection);
+typename Intersection::const_iterator
+findBestMatchForReverse(const TurnLaneType::Mask &leftmost_tag, const Intersection &intersection);
+
+// a match is trivial if all turns can be associated with their best match in a valid way and the
+// matches occur in order
+bool canMatchTrivially(const Intersection &intersection, const LaneDataVector &lane_data);
+
+// perform a trivial match on the turn lanes
+Intersection triviallyMatchLanesToTurns(Intersection intersection,
+                                        const LaneDataVector &lane_data,
+                                        const util::NodeBasedDynamicGraph &node_based_graph,
+                                        const LaneDescriptionID lane_string_id,
+                                        LaneDataIdMap &lane_data_to_id);
+
+} // namespace lanes
+} // namespace guidance
+} // namespace extractor
+} // namespace osrm
+
+#endif /*OSRM_EXTRACTOR_GUIDANCE_TURN_LANE_MATCHER_HPP_*/
diff --git a/include/extractor/guidance/turn_lane_types.hpp b/include/extractor/guidance/turn_lane_types.hpp
new file mode 100644
index 0000000..3a06a3b
--- /dev/null
+++ b/include/extractor/guidance/turn_lane_types.hpp
@@ -0,0 +1,102 @@
+#ifndef OSRM_GUIDANCE_TURN_LANE_TYPES_HPP_
+#define OSRM_GUIDANCE_TURN_LANE_TYPES_HPP_
+
+#include <bitset>
+#include <cstddef>
+#include <cstdint>
+#include <string>
+#include <vector>
+
+#include <boost/assert.hpp>
+#include <boost/functional/hash_fwd.hpp>
+
+#include "util/simple_logger.hpp"
+#include "util/typedefs.hpp"
+#include "util/json_container.hpp"
+
+namespace osrm
+{
+namespace extractor
+{
+namespace guidance
+{
+
+namespace TurnLaneType
+{
+namespace detail
+{
+const constexpr std::size_t num_supported_lane_types = 11;
+
+const constexpr char *translations[detail::num_supported_lane_types] = {"none",
+                                                                        "straight",
+                                                                        "sharp left",
+                                                                        "left",
+                                                                        "slight left",
+                                                                        "slight right",
+                                                                        "right",
+                                                                        "sharp right",
+                                                                        "uturn",
+                                                                        "merge to left",
+                                                                        "merge to right"};
+
+} // namespace detail
+
+typedef std::uint16_t Mask;
+const constexpr Mask empty = 0u;
+const constexpr Mask none = 1u << 0u;
+const constexpr Mask straight = 1u << 1u;
+const constexpr Mask sharp_left = 1u << 2u;
+const constexpr Mask left = 1u << 3u;
+const constexpr Mask slight_left = 1u << 4u;
+const constexpr Mask slight_right = 1u << 5u;
+const constexpr Mask right = 1u << 6u;
+const constexpr Mask sharp_right = 1u << 7u;
+const constexpr Mask uturn = 1u << 8u;
+const constexpr Mask merge_to_left = 1u << 9u;
+const constexpr Mask merge_to_right = 1u << 10u;
+
+inline std::string toString(const Mask lane_type)
+{
+    if (lane_type == 0)
+        return "none";
+
+    std::bitset<8 * sizeof(Mask)> mask(lane_type);
+    std::string result = "";
+    for (std::size_t lane_id_nr = 0; lane_id_nr < detail::num_supported_lane_types; ++lane_id_nr)
+        if (mask[lane_id_nr])
+            result += (result.empty() ? detail::translations[lane_id_nr]
+                                      : (std::string(";") + detail::translations[lane_id_nr]));
+
+    return result;
+}
+
+inline util::json::Array toJsonArray(const Mask lane_type)
+{
+    util::json::Array result;
+    std::bitset<8 * sizeof(Mask)> mask(lane_type);
+    for (std::size_t lane_id_nr = 0; lane_id_nr < detail::num_supported_lane_types; ++lane_id_nr)
+        if (mask[lane_id_nr])
+            result.values.push_back(detail::translations[lane_id_nr]);
+    return result;
+}
+} // TurnLaneType
+
+typedef std::vector<TurnLaneType::Mask> TurnLaneDescription;
+
+// hash function for TurnLaneDescription
+struct TurnLaneDescription_hash
+{
+    std::size_t operator()(const TurnLaneDescription &lane_description) const
+    {
+        std::size_t seed = 0;
+        for (auto val : lane_description)
+            boost::hash_combine(seed, val);
+        return seed;
+    }
+};
+
+} // guidance
+} // extractor
+} // osrm
+
+#endif /* OSRM_GUIDANCE_TURN_LANE_TYPES_HPP_ */
diff --git a/include/extractor/internal_extractor_edge.hpp b/include/extractor/internal_extractor_edge.hpp
index 14ad198..376e587 100644
--- a/include/extractor/internal_extractor_edge.hpp
+++ b/include/extractor/internal_extractor_edge.hpp
@@ -29,7 +29,6 @@ struct InternalExtractorEdge
 
     struct WeightData
     {
-
         WeightData() : duration(0.0), type(WeightType::INVALID) {}
 
         union {
@@ -51,6 +50,7 @@ struct InternalExtractorEdge
                  true,
                  TRAVEL_MODE_INACCESSIBLE,
                  false,
+                 guidance::TurnLaneType::empty,
                  guidance::RoadClassificationData())
     {
     }
@@ -66,9 +66,10 @@ struct InternalExtractorEdge
                                    bool startpoint,
                                    TravelMode travel_mode,
                                    bool is_split,
+                                   LaneDescriptionID lane_description,
                                    guidance::RoadClassificationData road_classification)
-        : result(OSMNodeID(source),
-                 OSMNodeID(target),
+        : result(source,
+                 target,
                  name_id,
                  0,
                  forward,
@@ -78,6 +79,7 @@ struct InternalExtractorEdge
                  startpoint,
                  travel_mode,
                  is_split,
+                 lane_description,
                  std::move(road_classification)),
           weight_data(std::move(weight_data))
     {
@@ -104,6 +106,7 @@ struct InternalExtractorEdge
                                      true,
                                      TRAVEL_MODE_INACCESSIBLE,
                                      false,
+                                     INVALID_LANE_DESCRIPTIONID,
                                      guidance::RoadClassificationData());
     }
     static InternalExtractorEdge max_osm_value()
@@ -119,6 +122,7 @@ struct InternalExtractorEdge
                                      true,
                                      TRAVEL_MODE_INACCESSIBLE,
                                      false,
+                                     INVALID_LANE_DESCRIPTIONID,
                                      guidance::RoadClassificationData());
     }
 
@@ -138,43 +142,6 @@ struct InternalExtractorEdge
     }
 };
 
-struct CmpEdgeByInternalStartThenInternalTargetID
-{
-    using value_type = InternalExtractorEdge;
-    bool operator()(const InternalExtractorEdge &lhs, const InternalExtractorEdge &rhs) const
-    {
-        return (lhs.result.source < rhs.result.source) ||
-               ((lhs.result.source == rhs.result.source) &&
-                (lhs.result.target < rhs.result.target));
-    }
-
-    value_type max_value() { return InternalExtractorEdge::max_internal_value(); }
-    value_type min_value() { return InternalExtractorEdge::min_internal_value(); }
-};
-
-struct CmpEdgeByOSMStartID
-{
-    using value_type = InternalExtractorEdge;
-    bool operator()(const InternalExtractorEdge &lhs, const InternalExtractorEdge &rhs) const
-    {
-        return lhs.result.osm_source_id < rhs.result.osm_source_id;
-    }
-
-    value_type max_value() { return InternalExtractorEdge::max_osm_value(); }
-    value_type min_value() { return InternalExtractorEdge::min_osm_value(); }
-};
-
-struct CmpEdgeByOSMTargetID
-{
-    using value_type = InternalExtractorEdge;
-    bool operator()(const InternalExtractorEdge &lhs, const InternalExtractorEdge &rhs) const
-    {
-        return lhs.result.osm_target_id < rhs.result.osm_target_id;
-    }
-
-    value_type max_value() { return InternalExtractorEdge::max_osm_value(); }
-    value_type min_value() { return InternalExtractorEdge::min_osm_value(); }
-};
 }
 }
 
diff --git a/include/extractor/node_based_edge.hpp b/include/extractor/node_based_edge.hpp
index 47090e6..1009776 100644
--- a/include/extractor/node_based_edge.hpp
+++ b/include/extractor/node_based_edge.hpp
@@ -26,6 +26,7 @@ struct NodeBasedEdge
                   bool startpoint,
                   TravelMode travel_mode,
                   bool is_split,
+                  const LaneDescriptionID lane_description_id,
                   guidance::RoadClassificationData road_classification);
 
     bool operator<(const NodeBasedEdge &other) const;
@@ -41,6 +42,7 @@ struct NodeBasedEdge
     bool startpoint : 1;
     bool is_split : 1;
     TravelMode travel_mode : 4;
+    LaneDescriptionID lane_description_id;
     guidance::RoadClassificationData road_classification;
 };
 
@@ -57,6 +59,7 @@ struct NodeBasedEdgeWithOSM : NodeBasedEdge
                          bool startpoint,
                          TravelMode travel_mode,
                          bool is_split,
+                         const LaneDescriptionID lane_description_id,
                          guidance::RoadClassificationData road_classification);
 
     OSMNodeID osm_source_id;
@@ -68,7 +71,7 @@ struct NodeBasedEdgeWithOSM : NodeBasedEdge
 inline NodeBasedEdge::NodeBasedEdge()
     : source(SPECIAL_NODEID), target(SPECIAL_NODEID), name_id(0), weight(0), forward(false),
       backward(false), roundabout(false), access_restricted(false), startpoint(true),
-      is_split(false), travel_mode(false)
+      is_split(false), travel_mode(false), lane_description_id(INVALID_LANE_DESCRIPTIONID)
 {
 }
 
@@ -83,11 +86,12 @@ inline NodeBasedEdge::NodeBasedEdge(NodeID source,
                                     bool startpoint,
                                     TravelMode travel_mode,
                                     bool is_split,
+                                    const LaneDescriptionID lane_description_id,
                                     guidance::RoadClassificationData road_classification)
     : source(source), target(target), name_id(name_id), weight(weight), forward(forward),
       backward(backward), roundabout(roundabout), access_restricted(access_restricted),
       startpoint(startpoint), is_split(is_split), travel_mode(travel_mode),
-      road_classification(std::move(road_classification))
+      lane_description_id(lane_description_id), road_classification(std::move(road_classification))
 {
 }
 
@@ -120,6 +124,7 @@ inline NodeBasedEdgeWithOSM::NodeBasedEdgeWithOSM(
     bool startpoint,
     TravelMode travel_mode,
     bool is_split,
+    const LaneDescriptionID lane_description_id,
     guidance::RoadClassificationData road_classification)
     : NodeBasedEdge(SPECIAL_NODEID,
                     SPECIAL_NODEID,
@@ -132,6 +137,7 @@ inline NodeBasedEdgeWithOSM::NodeBasedEdgeWithOSM(
                     startpoint,
                     travel_mode,
                     is_split,
+                    lane_description_id,
                     std::move(road_classification)),
       osm_source_id(std::move(source)), osm_target_id(std::move(target))
 {
diff --git a/include/extractor/original_edge_data.hpp b/include/extractor/original_edge_data.hpp
index dce4fb7..558fcb6 100644
--- a/include/extractor/original_edge_data.hpp
+++ b/include/extractor/original_edge_data.hpp
@@ -17,18 +17,19 @@ struct OriginalEdgeData
 {
     explicit OriginalEdgeData(NodeID via_node,
                               unsigned name_id,
+                              LaneDataID lane_data_id,
                               guidance::TurnInstruction turn_instruction,
                               EntryClassID entry_classid,
                               TravelMode travel_mode)
         : via_node(via_node), name_id(name_id), entry_classid(entry_classid),
-          turn_instruction(turn_instruction), travel_mode(travel_mode)
+          lane_data_id(lane_data_id), turn_instruction(turn_instruction), travel_mode(travel_mode)
     {
     }
 
     OriginalEdgeData()
         : via_node(std::numeric_limits<unsigned>::max()),
           name_id(std::numeric_limits<unsigned>::max()), entry_classid(INVALID_ENTRY_CLASSID),
-          turn_instruction(guidance::TurnInstruction::INVALID()),
+          lane_data_id(INVALID_LANE_DATAID), turn_instruction(guidance::TurnInstruction::INVALID()),
           travel_mode(TRAVEL_MODE_INACCESSIBLE)
     {
     }
@@ -36,9 +37,13 @@ struct OriginalEdgeData
     NodeID via_node;
     unsigned name_id;
     EntryClassID entry_classid;
+    LaneDataID lane_data_id;
     guidance::TurnInstruction turn_instruction;
     TravelMode travel_mode;
 };
+
+static_assert(sizeof(OriginalEdgeData) == 16,
+              "Increasing the size of OriginalEdgeData increases memory consumption");
 }
 }
 
diff --git a/include/extractor/query_node.hpp b/include/extractor/query_node.hpp
index 69edc08..f66d9c5 100644
--- a/include/extractor/query_node.hpp
+++ b/include/extractor/query_node.hpp
@@ -6,6 +6,7 @@
 #include "util/coordinate.hpp"
 
 #include <limits>
+#include <cstdint>
 
 namespace osrm
 {
@@ -15,16 +16,16 @@ namespace extractor
 struct QueryNode
 {
     using key_type = OSMNodeID; // type of NodeID
-    using value_type = int;     // type of lat,lons
+    using value_type = std::int32_t;     // type of lat,lons
 
     explicit QueryNode(const util::FixedLongitude lon_,
                        const util::FixedLatitude lat_,
-                       key_type node_id)
-        : lon(lon_), lat(lat_), node_id(std::move(node_id))
+                       const key_type node_id_)
+        : lon(lon_), lat(lat_), node_id(node_id_)
     {
     }
     QueryNode()
-        : lon(std::numeric_limits<int>::max()), lat(std::numeric_limits<int>::max()),
+        : lon{std::numeric_limits<value_type>::max()}, lat{std::numeric_limits<value_type>::max()},
           node_id(SPECIAL_OSM_NODEID)
     {
     }
@@ -35,15 +36,15 @@ struct QueryNode
 
     static QueryNode min_value()
     {
-        return QueryNode(util::FixedLongitude(-180 * COORDINATE_PRECISION),
-                         util::FixedLatitude(-90 * COORDINATE_PRECISION),
+        return QueryNode(util::FixedLongitude{static_cast<value_type>(-180 * COORDINATE_PRECISION)},
+                         util::FixedLatitude{static_cast<value_type>(-90 * COORDINATE_PRECISION)},
                          MIN_OSM_NODEID);
     }
 
     static QueryNode max_value()
     {
-        return QueryNode(util::FixedLongitude(180 * COORDINATE_PRECISION),
-                         util::FixedLatitude(90 * COORDINATE_PRECISION),
+        return QueryNode(util::FixedLongitude{static_cast<value_type>(180 * COORDINATE_PRECISION)},
+                         util::FixedLatitude{static_cast<value_type>(90 * COORDINATE_PRECISION)},
                          MAX_OSM_NODEID);
     }
 };
diff --git a/include/server/api/base_parameters_grammar.hpp b/include/server/api/base_parameters_grammar.hpp
index a039970..8e59035 100644
--- a/include/server/api/base_parameters_grammar.hpp
+++ b/include/server/api/base_parameters_grammar.hpp
@@ -111,15 +111,15 @@ struct BaseParametersGrammar : boost::spirit::qi::grammar<Iterator, Signature>
                                                 qi::_1,
                                                 qi::_2)];
 
-        location_rule = (double_ > qi::lit(',') >
-                         double_)[qi::_val = ph::bind(
-                                      [](double lon, double lat) {
-                                          return util::Coordinate(
-                                              util::FixedLongitude(lon * COORDINATE_PRECISION),
-                                              util::FixedLatitude(lat * COORDINATE_PRECISION));
-                                      },
-                                      qi::_1,
-                                      qi::_2)];
+        location_rule =
+            (double_ > qi::lit(',') >
+             double_)[qi::_val = ph::bind(
+                          [](double lon, double lat) {
+                              return util::Coordinate(util::toFixed(util::FloatLongitude{lon}),
+                                                      util::toFixed(util::FloatLatitude{lat}));
+                          },
+                          qi::_1,
+                          qi::_2)];
 
         polyline_rule = qi::as_string[qi::lit("polyline(") > +polyline_chars > ')']
                                      [qi::_val = ph::bind(
diff --git a/include/storage/shared_datatype.hpp b/include/storage/shared_datatype.hpp
index 866fc36..8c05c3a 100644
--- a/include/storage/shared_datatype.hpp
+++ b/include/storage/shared_datatype.hpp
@@ -14,7 +14,41 @@ namespace storage
 {
 
 // Added at the start and end of each block as sanity check
-const constexpr char CANARY[] = "OSRM";
+const constexpr char CANARY[4] = {'O', 'S', 'R', 'M'};
+
+const constexpr char *block_id_to_name[] = {"NAME_OFFSETS",
+                                            "NAME_BLOCKS",
+                                            "NAME_CHAR_LIST",
+                                            "NAME_ID_LIST",
+                                            "VIA_NODE_LIST",
+                                            "GRAPH_NODE_LIST",
+                                            "GRAPH_EDGE_LIST",
+                                            "COORDINATE_LIST",
+                                            "OSM_NODE_ID_LIST",
+                                            "TURN_INSTRUCTION",
+                                            "TRAVEL_MODE",
+                                            "ENTRY_CLASSID",
+                                            "R_SEARCH_TREE",
+                                            "GEOMETRIES_INDEX",
+                                            "GEOMETRIES_LIST",
+                                            "HSGR_CHECKSUM",
+                                            "TIMESTAMP",
+                                            "FILE_INDEX_PATH",
+                                            "CORE_MARKER",
+                                            "DATASOURCES_LIST",
+                                            "DATASOURCE_NAME_DATA",
+                                            "DATASOURCE_NAME_OFFSETS",
+                                            "DATASOURCE_NAME_LENGTHS",
+                                            "PROPERTIES",
+                                            "BEARING_CLASSID",
+                                            "BEARING_OFFSETS",
+                                            "BEARING_BLOCKS",
+                                            "BEARING_VALUES",
+                                            "ENTRY_CLASS",
+                                            "LANE_DATA_ID",
+                                            "TURN_LANE_DATA",
+                                            "LANE_DESCRIPTION_OFFSETS",
+                                            "LANE_DESCRIPTION_MASKS"};
 
 struct SharedDataLayout
 {
@@ -30,8 +64,8 @@ struct SharedDataLayout
         COORDINATE_LIST,
         OSM_NODE_ID_LIST,
         TURN_INSTRUCTION,
-        ENTRY_CLASSID,
         TRAVEL_MODE,
+        ENTRY_CLASSID,
         R_SEARCH_TREE,
         GEOMETRIES_INDEX,
         GEOMETRIES_LIST,
@@ -49,6 +83,10 @@ struct SharedDataLayout
         BEARING_BLOCKS,
         BEARING_VALUES,
         ENTRY_CLASS,
+        LANE_DATA_ID,
+        TURN_LANE_DATA,
+        LANE_DESCRIPTION_OFFSETS,
+        LANE_DESCRIPTION_MASKS,
         NUM_BLOCKS
     };
 
@@ -63,15 +101,20 @@ struct SharedDataLayout
         entry_size[bid] = sizeof(T);
     }
 
+    inline uint64_t AlignBlockSize(uint64_t block_size) const
+    {
+        const uint64_t alignment = 4;
+        return (block_size + (alignment - 1)) & ~(alignment - 1);
+    }
+
     inline uint64_t GetBlockSize(BlockID bid) const
     {
         // special bit encoding
         if (bid == CORE_MARKER)
         {
-            return (num_entries[bid] / 32 + 1) * entry_size[bid];
+            return AlignBlockSize((num_entries[bid] / 32 + 1) * entry_size[bid]);
         }
-
-        return num_entries[bid] * entry_size[bid];
+        return AlignBlockSize(num_entries[bid] * entry_size[bid]);
     }
 
     inline uint64_t GetSizeOfLayout() const
@@ -108,11 +151,13 @@ struct SharedDataLayout
             bool end_canary_alive = std::equal(CANARY, CANARY + sizeof(CANARY), end_canary_ptr);
             if (!start_canary_alive)
             {
-                throw util::exception("Start canary of block corrupted.");
+                throw util::exception(std::string("Start canary of block corrupted. (") +
+                                      block_id_to_name[bid] + ")");
             }
             if (!end_canary_alive)
             {
-                throw util::exception("End canary of block corrupted.");
+                throw util::exception(std::string("End canary of block corrupted. (") +
+                                      block_id_to_name[bid] + ")");
             }
         }
 
@@ -137,6 +182,9 @@ struct SharedDataTimestamp
     SharedDataType data;
     unsigned timestamp;
 };
+
+static_assert(sizeof(block_id_to_name) / sizeof(*block_id_to_name) == SharedDataLayout::NUM_BLOCKS,
+              "Number of blocks needs to match the number of Block names.");
 }
 }
 
diff --git a/include/storage/storage_config.hpp b/include/storage/storage_config.hpp
index 94e6e01..e68129a 100644
--- a/include/storage/storage_config.hpp
+++ b/include/storage/storage_config.hpp
@@ -65,6 +65,8 @@ struct StorageConfig final
     boost::filesystem::path names_data_path;
     boost::filesystem::path properties_path;
     boost::filesystem::path intersection_class_path;
+    boost::filesystem::path turn_lane_data_path;
+    boost::filesystem::path turn_lane_description_path;
 };
 }
 }
diff --git a/include/util/assert.hpp b/include/util/assert.hpp
deleted file mode 100644
index d9bfb95..0000000
--- a/include/util/assert.hpp
+++ /dev/null
@@ -1,20 +0,0 @@
-#ifndef OSRM_ASSERT_HPP
-#define OSRM_ASSERT_HPP
-
-#include <boost/assert.hpp>
-
-#include <stdexcept>
-
-namespace osrm
-{
-namespace util
-{
-// Assertion type to be thrown for stack unwinding
-struct assertionError final : std::logic_error
-{
-    assertionError(const char *msg) : std::logic_error{msg} {}
-};
-}
-}
-
-#endif
diff --git a/include/util/coordinate.hpp b/include/util/coordinate.hpp
index 97690f2..e2b2a53 100644
--- a/include/util/coordinate.hpp
+++ b/include/util/coordinate.hpp
@@ -61,7 +61,7 @@ inline FixedLatitude toFixed(const FloatLatitude floating)
 {
     const auto latitude = static_cast<double>(floating);
     const auto fixed = boost::numeric_cast<std::int32_t>(latitude * COORDINATE_PRECISION);
-    return FixedLatitude(fixed);
+    return FixedLatitude{fixed};
 }
 
 /**
@@ -75,7 +75,7 @@ inline FixedLongitude toFixed(const FloatLongitude floating)
 {
     const auto longitude = static_cast<double>(floating);
     const auto fixed = boost::numeric_cast<std::int32_t>(longitude * COORDINATE_PRECISION);
-    return FixedLongitude(fixed);
+    return FixedLongitude{fixed};
 }
 
 /**
@@ -89,7 +89,7 @@ inline FloatLatitude toFloating(const FixedLatitude fixed)
 {
     const auto latitude = static_cast<std::int32_t>(fixed);
     const auto floating = boost::numeric_cast<double>(latitude / COORDINATE_PRECISION);
-    return FloatLatitude(floating);
+    return FloatLatitude{floating};
 }
 
 /**
@@ -103,7 +103,7 @@ inline FloatLongitude toFloating(const FixedLongitude fixed)
 {
     const auto longitude = static_cast<std::int32_t>(fixed);
     const auto floating = boost::numeric_cast<double>(longitude / COORDINATE_PRECISION);
-    return FloatLongitude(floating);
+    return FloatLongitude{floating};
 }
 
 // fwd. decl.
@@ -127,7 +127,7 @@ struct Coordinate
     FixedLongitude lon;
     FixedLatitude lat;
 
-    Coordinate() : lon(std::numeric_limits<int>::min()), lat(std::numeric_limits<int>::min()) {}
+    Coordinate() : lon{std::numeric_limits<int>::min()}, lat{std::numeric_limits<int>::min()} {}
 
     Coordinate(const FloatCoordinate &other);
 
@@ -173,7 +173,7 @@ struct FloatCoordinate
     FloatLatitude lat;
 
     FloatCoordinate()
-        : lon(std::numeric_limits<double>::min()), lat(std::numeric_limits<double>::min())
+        : lon{std::numeric_limits<double>::min()}, lat{std::numeric_limits<double>::min()}
     {
     }
 
diff --git a/include/util/coordinate_calculation.hpp b/include/util/coordinate_calculation.hpp
index 9edc5bd..61fa7bf 100644
--- a/include/util/coordinate_calculation.hpp
+++ b/include/util/coordinate_calculation.hpp
@@ -61,10 +61,10 @@ inline std::pair<double, FloatCoordinate> projectPointOnSegment(const FloatCoord
 
     return {clamped_ratio,
             {
-                FloatLongitude(1.0 - clamped_ratio) * source.lon +
-                    target.lon * FloatLongitude(clamped_ratio),
-                FloatLatitude(1.0 - clamped_ratio) * source.lat +
-                    target.lat * FloatLatitude(clamped_ratio),
+                FloatLongitude{1.0 - clamped_ratio} * source.lon +
+                    target.lon * FloatLongitude{clamped_ratio},
+                FloatLatitude{1.0 - clamped_ratio} * source.lat +
+                    target.lat * FloatLatitude{clamped_ratio},
             }};
 }
 
diff --git a/include/util/debug.hpp b/include/util/debug.hpp
new file mode 100644
index 0000000..4d6ba1f
--- /dev/null
+++ b/include/util/debug.hpp
@@ -0,0 +1,91 @@
+#ifndef OSRM_ENGINE_GUIDANCE_DEBUG_HPP_
+#define OSRM_ENGINE_GUIDANCE_DEBUG_HPP_
+
+#include "extractor/guidance/intersection.hpp"
+#include "extractor/guidance/turn_lane_data.hpp"
+#include "extractor/query_node.hpp"
+#include "engine/guidance/route_step.hpp"
+#include "util/typedefs.hpp"
+
+#include <iomanip>
+#include <iostream>
+#include <vector>
+
+namespace osrm
+{
+namespace util
+{
+namespace guidance
+{
+inline void print(const engine::guidance::RouteStep &step)
+{
+    std::cout << static_cast<int>(step.maneuver.instruction.type) << " "
+              << static_cast<int>(step.maneuver.instruction.direction_modifier) << "  "
+              << static_cast<int>(step.maneuver.waypoint_type) << " "
+              << " Lanes: (" << static_cast<int>(step.maneuver.lanes.lanes_in_turn) << ", "
+              << static_cast<int>(step.maneuver.lanes.first_lane_from_the_right) << ")"
+              << " Duration: " << step.duration << " Distance: " << step.distance
+              << " Geometry: " << step.geometry_begin << " " << step.geometry_end
+              << " exit: " << step.maneuver.exit << " Intersections: " << step.intersections.size()
+              << " [";
+
+    for (const auto &intersection : step.intersections)
+    {
+        std::cout << "(bearings:";
+        for (auto bearing : intersection.bearings)
+            std::cout << " " << bearing;
+        std::cout << ", entry: ";
+        for (auto entry : intersection.entry)
+            std::cout << " " << (entry ? "true" : "false");
+        std::cout << ")";
+    }
+    std::cout << "] name[" << step.name_id << "]: " << step.name;
+}
+
+inline void print(const std::vector<engine::guidance::RouteStep> &steps)
+{
+    std::cout << "Path\n";
+    int segment = 0;
+    for (const auto &step : steps)
+    {
+        std::cout << "\t[" << segment++ << "]: ";
+        print(step);
+        std::cout << std::endl;
+    }
+}
+
+inline void print(const extractor::guidance::lanes::LaneDataVector &turn_lane_data)
+{
+    std::cout << " Tags:\n";
+    for (auto entry : turn_lane_data)
+        std::cout << "\t" << entry.tag << "("
+                  << extractor::guidance::TurnLaneType::toString(entry.tag)
+                  << ") from: " << static_cast<int>(entry.from)
+                  << " to: " << static_cast<int>(entry.to) << "\n";
+    std::cout << std::flush;
+}
+
+inline void
+printTurnAssignmentData(const NodeID at,
+                        const extractor::guidance::lanes::LaneDataVector &turn_lane_data,
+                        const extractor::guidance::Intersection &intersection,
+                        const std::vector<extractor::QueryNode> &node_info_list)
+{
+    std::cout << "[Turn Assignment Progress]\nLocation:";
+    auto coordinate = node_info_list[at];
+    std::cout << std::setprecision(12) << toFloating(coordinate.lat) << " "
+              << toFloating(coordinate.lon) << "\n";
+
+    std::cout << "  Intersection:\n";
+    for (const auto &road : intersection)
+        std::cout << "\t" << toString(road) << "\n";
+
+    // flushes as well
+    print(turn_lane_data);
+}
+
+} // namespace guidance
+} // namespace util
+} // namespace osrm
+
+#endif /*OSRM_ENGINE_GUIDANCE_DEBUG_HPP_*/
diff --git a/include/util/dynamic_graph.hpp b/include/util/dynamic_graph.hpp
index 7df32a9..e47990b 100644
--- a/include/util/dynamic_graph.hpp
+++ b/include/util/dynamic_graph.hpp
@@ -24,8 +24,8 @@ template <typename EdgeDataT> class DynamicGraph
 {
   public:
     using EdgeData = EdgeDataT;
-    using NodeIterator = unsigned;
-    using EdgeIterator = unsigned;
+    using NodeIterator = std::uint32_t;
+    using EdgeIterator = std::uint32_t;
     using EdgeRange = range<EdgeIterator>;
 
     class InputEdge
diff --git a/include/util/group_by.hpp b/include/util/group_by.hpp
new file mode 100644
index 0000000..db8a312
--- /dev/null
+++ b/include/util/group_by.hpp
@@ -0,0 +1,40 @@
+#ifndef OSRM_GROUP_BY
+#define OSRM_GROUP_BY
+
+#include <algorithm>
+#include <utility>
+
+namespace osrm
+{
+namespace util
+{
+
+// Runs fn on consecutive items in sub-ranges determined by pred.
+//
+// Example:
+//   vector<int> v{1,2,2,2,3,4,4};
+//   group_by(first, last, even, print);
+//   >>> 2,2,2
+//   >>> 4,4
+//
+// Note: this mimics Python's itertools.groupby
+template <typename Iter, typename Pred, typename Fn>
+Fn group_by(Iter first, Iter last, Pred pred, Fn fn)
+{
+    while (first != last)
+    {
+        first = std::find_if(first, last, pred);
+        auto next = std::find_if_not(first, last, pred);
+
+        (void)fn(std::make_pair(first, next));
+
+        first = next;
+    }
+
+    return fn;
+}
+
+} // ns util
+} // ns osrm
+
+#endif
diff --git a/include/util/guidance/entry_class.hpp b/include/util/guidance/entry_class.hpp
index 422c2d2..715e123 100644
--- a/include/util/guidance/entry_class.hpp
+++ b/include/util/guidance/entry_class.hpp
@@ -62,6 +62,13 @@ class EntryClass
     friend std::size_t std::hash<EntryClass>::operator()(const EntryClass &) const;
 };
 
+#if not defined __GNUC__ or __GNUC__ > 4
+static_assert(std::is_trivially_copyable<EntryClass>::value,
+              "Class is serialized trivially in "
+              "the datafacades. Bytewise writing "
+              "requires trivially copyable type");
+#endif
+
 } // namespace guidance
 } // namespace utilr
 } // namespace osrm
diff --git a/include/util/guidance/toolkit.hpp b/include/util/guidance/toolkit.hpp
index 2c458ab..12a3977 100644
--- a/include/util/guidance/toolkit.hpp
+++ b/include/util/guidance/toolkit.hpp
@@ -65,6 +65,70 @@ mirrorDirectionModifier(const extractor::guidance::DirectionModifier::Enum modif
     return results[modifier];
 }
 
+inline bool hasLeftModifier(const extractor::guidance::TurnInstruction instruction)
+{
+    return instruction.direction_modifier == extractor::guidance::DirectionModifier::SharpLeft ||
+           instruction.direction_modifier == extractor::guidance::DirectionModifier::Left ||
+           instruction.direction_modifier == extractor::guidance::DirectionModifier::SlightLeft;
+}
+
+inline bool hasRightModifier(const extractor::guidance::TurnInstruction instruction)
+{
+    return instruction.direction_modifier == extractor::guidance::DirectionModifier::SharpRight ||
+           instruction.direction_modifier == extractor::guidance::DirectionModifier::Right ||
+           instruction.direction_modifier == extractor::guidance::DirectionModifier::SlightRight;
+}
+
+inline bool isLeftTurn(const extractor::guidance::TurnInstruction instruction)
+{
+    switch (instruction.type)
+    {
+    case extractor::guidance::TurnType::Merge:
+        return hasRightModifier(instruction);
+    default:
+        return hasLeftModifier(instruction);
+    }
+}
+
+inline bool isRightTurn(const extractor::guidance::TurnInstruction instruction)
+{
+    switch (instruction.type)
+    {
+    case extractor::guidance::TurnType::Merge:
+        return hasLeftModifier(instruction);
+    default:
+        return hasRightModifier(instruction);
+    }
+}
+
+inline bool entersRoundabout(const extractor::guidance::TurnInstruction instruction)
+{
+    return (instruction.type == extractor::guidance::TurnType::EnterRoundabout ||
+            instruction.type == extractor::guidance::TurnType::EnterRotary ||
+            instruction.type == extractor::guidance::TurnType::EnterRoundaboutIntersection ||
+            instruction.type == extractor::guidance::TurnType::EnterRoundaboutAtExit ||
+            instruction.type == extractor::guidance::TurnType::EnterRotaryAtExit ||
+            instruction.type == extractor::guidance::TurnType::EnterRoundaboutIntersectionAtExit ||
+            instruction.type == extractor::guidance::TurnType::EnterAndExitRoundabout ||
+            instruction.type == extractor::guidance::TurnType::EnterAndExitRotary ||
+            instruction.type == extractor::guidance::TurnType::EnterAndExitRotary);
+}
+
+inline bool leavesRoundabout(const extractor::guidance::TurnInstruction instruction)
+{
+    return (instruction.type == extractor::guidance::TurnType::ExitRoundabout ||
+            instruction.type == extractor::guidance::TurnType::ExitRotary ||
+            instruction.type == extractor::guidance::TurnType::ExitRoundaboutIntersection ||
+            instruction.type == extractor::guidance::TurnType::EnterAndExitRoundabout ||
+            instruction.type == extractor::guidance::TurnType::EnterAndExitRotary ||
+            instruction.type == extractor::guidance::TurnType::EnterAndExitRoundaboutIntersection);
+}
+
+inline bool staysOnRoundabout(const extractor::guidance::TurnInstruction instruction)
+{
+    return instruction.type == extractor::guidance::TurnType::StayOnRoundabout;
+}
+
 } // namespace guidance
 } // namespace util
 } // namespace osrm
diff --git a/include/util/guidance/turn_lanes.hpp b/include/util/guidance/turn_lanes.hpp
new file mode 100644
index 0000000..6a73267
--- /dev/null
+++ b/include/util/guidance/turn_lanes.hpp
@@ -0,0 +1,81 @@
+#ifndef OSRM_UTIL_GUIDANCE_TURN_LANES_HPP
+#define OSRM_UTIL_GUIDANCE_TURN_LANES_HPP
+
+#include <cstddef>
+#include <cstdint>
+#include <functional>
+#include <vector>
+
+#include "util/typedefs.hpp"
+
+#include <boost/functional/hash.hpp>
+
+namespace osrm
+{
+namespace util
+{
+namespace guidance
+{
+class LaneTupel;
+} // namespace guidance
+} // namespace util
+} // namespace osrm
+
+namespace std
+{
+template <> struct hash<::osrm::util::guidance::LaneTupel>
+{
+    inline std::size_t operator()(const ::osrm::util::guidance::LaneTupel &bearing_class) const;
+};
+} // namespace std
+
+namespace osrm
+{
+namespace util
+{
+namespace guidance
+{
+
+// The mapping of turn lanes can be done two values. We describe every turn by the number of
+// contributing lanes and the first lane from the right..
+// Given a road like this:
+//           |   |   |
+//           |   |   |
+// -----------       |
+//      -^           |
+// -----------        -------------
+//      -^ ->
+// --------------------------------
+//      -v       |
+// ----------    |
+//          |    |
+//
+// we generate a set of tuples in the form of:
+// (2,1), (1,1), (1,0) for left, through and right respectively
+class LaneTupel
+{
+  public:
+    LaneTupel();
+    LaneTupel(const LaneID lanes_in_turn, const LaneID first_lane_from_the_right);
+
+    bool operator==(const LaneTupel other) const;
+    bool operator!=(const LaneTupel other) const;
+
+    LaneID lanes_in_turn;
+    LaneID first_lane_from_the_right;
+
+    friend std::size_t hash_value(const LaneTupel &tup)
+    {
+        std::size_t seed{0};
+        boost::hash_combine(seed, tup.lanes_in_turn);
+        boost::hash_combine(seed, tup.first_lane_from_the_right);
+        return seed;
+    }
+};
+
+using LaneTupelIdPair = std::pair<util::guidance::LaneTupel, LaneDescriptionID>;
+} // namespace guidance
+} // namespace util
+} // namespace osrm
+
+#endif /* OSRM_UTIL_GUIDANCE_TURN_LANES_HPP */
diff --git a/include/util/io.hpp b/include/util/io.hpp
index 2f3177f..cb4473d 100644
--- a/include/util/io.hpp
+++ b/include/util/io.hpp
@@ -4,12 +4,14 @@
 #include "util/simple_logger.hpp"
 
 #include <boost/filesystem.hpp>
+#include <boost/numeric/conversion/cast.hpp>
 
 #include <cstddef>
 #include <cstdint>
 
 #include <bitset>
 #include <fstream>
+#include <stxxl/vector>
 #include <vector>
 
 #include "util/fingerprint.hpp"
@@ -88,6 +90,84 @@ bool deserializeVector(std::istream &stream, std::vector<simple_type> &data)
     return static_cast<bool>(stream);
 }
 
+// serializes a vector of vectors into an adjacency array (creates a copy of the data internally)
+template <typename simple_type>
+bool serializeVectorIntoAdjacencyArray(const std::string &filename,
+                                       const std::vector<std::vector<simple_type>> &data)
+{
+    std::ofstream out_stream(filename, std::ios::binary);
+    std::vector<std::uint32_t> offsets;
+    offsets.reserve(data.size() + 1);
+    std::uint64_t current_offset = 0;
+    offsets.push_back(current_offset);
+    for (auto const &vec : data)
+    {
+        current_offset += vec.size();
+        offsets.push_back(boost::numeric_cast<std::uint32_t>(current_offset));
+    }
+    if (!serializeVector(out_stream, offsets))
+        return false;
+
+    std::vector<simple_type> all_data;
+    all_data.reserve(offsets.back());
+    for (auto const &vec : data)
+        all_data.insert(all_data.end(), vec.begin(), vec.end());
+
+    if (!serializeVector(out_stream, all_data))
+        return false;
+
+    return static_cast<bool>(out_stream);
+}
+
+template <typename simple_type, std::size_t WRITE_BLOCK_BUFFER_SIZE = 1024>
+bool serializeVector(std::ofstream &out_stream, const stxxl::vector<simple_type> &data)
+{
+    const std::uint64_t size = data.size();
+    out_stream.write(reinterpret_cast<const char *>(&size), sizeof(size));
+
+    simple_type write_buffer[WRITE_BLOCK_BUFFER_SIZE];
+    std::size_t buffer_len = 0;
+
+    for (const auto entry : data)
+    {
+        write_buffer[buffer_len++] = entry;
+
+        if (buffer_len >= WRITE_BLOCK_BUFFER_SIZE)
+        {
+            out_stream.write(reinterpret_cast<const char *>(write_buffer),
+                             WRITE_BLOCK_BUFFER_SIZE * sizeof(simple_type));
+            buffer_len = 0;
+        }
+    }
+
+    // write remaining entries
+    if (buffer_len > 0)
+        out_stream.write(reinterpret_cast<const char *>(write_buffer),
+                         buffer_len * sizeof(simple_type));
+
+    return static_cast<bool>(out_stream);
+}
+
+template <typename simple_type>
+bool deserializeAdjacencyArray(const std::string &filename,
+                               std::vector<std::uint32_t> &offsets,
+                               std::vector<simple_type>& data)
+{
+    std::ifstream in_stream(filename, std::ios::binary);
+
+    if (!deserializeVector(in_stream, offsets))
+        return false;
+
+    if (!deserializeVector(in_stream, data))
+        return false;
+
+    // offsets have to match up with the size of the data
+    if (offsets.empty() || (offsets.back() != boost::numeric_cast<std::uint32_t>(data.size())))
+        return false;
+
+    return static_cast<bool>(in_stream);
+}
+
 inline bool serializeFlags(const boost::filesystem::path &path, const std::vector<bool> &flags)
 {
     // TODO this should be replaced with a FILE-based write using error checking
diff --git a/include/util/node_based_graph.hpp b/include/util/node_based_graph.hpp
index e2ac564..957b83c 100644
--- a/include/util/node_based_graph.hpp
+++ b/include/util/node_based_graph.hpp
@@ -20,7 +20,8 @@ struct NodeBasedEdgeData
     NodeBasedEdgeData()
         : distance(INVALID_EDGE_WEIGHT), edge_id(SPECIAL_NODEID),
           name_id(std::numeric_limits<unsigned>::max()), access_restricted(false), reversed(false),
-          roundabout(false), travel_mode(TRAVEL_MODE_INACCESSIBLE)
+          roundabout(false), travel_mode(TRAVEL_MODE_INACCESSIBLE),
+          lane_description_id(INVALID_LANE_DESCRIPTIONID)
     {
     }
 
@@ -31,10 +32,11 @@ struct NodeBasedEdgeData
                       bool reversed,
                       bool roundabout,
                       bool startpoint,
-                      extractor::TravelMode travel_mode)
+                      extractor::TravelMode travel_mode,
+                      const LaneDescriptionID lane_description_id)
         : distance(distance), edge_id(edge_id), name_id(name_id),
           access_restricted(access_restricted), reversed(reversed), roundabout(roundabout),
-          startpoint(startpoint), travel_mode(travel_mode)
+          startpoint(startpoint), travel_mode(travel_mode), lane_description_id(lane_description_id)
     {
     }
 
@@ -46,6 +48,7 @@ struct NodeBasedEdgeData
     bool roundabout : 1;
     bool startpoint : 1;
     extractor::TravelMode travel_mode : 4;
+    LaneDescriptionID lane_description_id;
     extractor::guidance::RoadClassificationData road_classification;
 
     bool IsCompatibleTo(const NodeBasedEdgeData &other) const
@@ -64,7 +67,7 @@ using NodeBasedDynamicGraph = DynamicGraph<NodeBasedEdgeData>;
 /// Since DynamicGraph expects directed edges, we need to insert
 /// two edges for undirected edges.
 inline std::shared_ptr<NodeBasedDynamicGraph>
-NodeBasedDynamicGraphFromEdges(std::size_t number_of_nodes,
+NodeBasedDynamicGraphFromEdges(NodeID number_of_nodes,
                                const std::vector<extractor::NodeBasedEdge> &input_edge_list)
 {
     auto edges_list = directedEdgesFromCompressed<NodeBasedDynamicGraph::InputEdge>(
@@ -80,12 +83,12 @@ NodeBasedDynamicGraphFromEdges(std::size_t number_of_nodes,
             output_edge.data.travel_mode = input_edge.travel_mode;
             output_edge.data.startpoint = input_edge.startpoint;
             output_edge.data.road_classification = input_edge.road_classification;
+            output_edge.data.lane_description_id = input_edge.lane_description_id;
         });
 
     tbb::parallel_sort(edges_list.begin(), edges_list.end());
 
-    auto graph = std::make_shared<NodeBasedDynamicGraph>(
-        static_cast<NodeBasedDynamicGraph::NodeIterator>(number_of_nodes), edges_list);
+    auto graph = std::make_shared<NodeBasedDynamicGraph>(number_of_nodes, edges_list);
 
     return graph;
 }
diff --git a/include/util/packed_vector.hpp b/include/util/packed_vector.hpp
index d4a5ee8..343763a 100644
--- a/include/util/packed_vector.hpp
+++ b/include/util/packed_vector.hpp
@@ -12,7 +12,6 @@ namespace osrm
 namespace util
 {
 
-
 /**
  * Since OSM node IDs are (at the time of writing) not quite yet overflowing 32 bits, and
  * will predictably be containable within 33 bits for a long time, the following packs
@@ -21,16 +20,16 @@ namespace util
  * NOTE: this type is templated for future use, but will require a slight refactor to
  * configure BITSIZE and ELEMSIZE
  */
-template <typename T, bool UseSharedMemory=false> class PackedVector
+template <typename T, bool UseSharedMemory = false> class PackedVector
 {
     static const constexpr std::size_t BITSIZE = 33;
     static const constexpr std::size_t ELEMSIZE = 64;
     static const constexpr std::size_t PACKSIZE = BITSIZE * ELEMSIZE;
 
   public:
-
     /**
-     * Returns the size of the packed vector datastructure with `elements` packed elements (the size of
+     * Returns the size of the packed vector datastructure with `elements` packed elements (the size
+     * of
      * its underlying uint64 vector)
      */
     inline static std::size_t elements_to_blocks(std::size_t elements)
@@ -96,14 +95,14 @@ template <typename T, bool UseSharedMemory=false> class PackedVector
         if (left_index == 0)
         {
             // ID is at the far left side of this element
-            return static_cast<T>(elem >> (ELEMSIZE - BITSIZE));
+            return T{elem >> (ELEMSIZE - BITSIZE)};
         }
         else if (left_index >= BITSIZE)
         {
             // ID is entirely contained within this element
             const std::uint64_t at_right = elem >> (left_index - BITSIZE);
             const std::uint64_t left_mask = static_cast<std::uint64_t>(pow(2, BITSIZE)) - 1;
-            return static_cast<T>(at_right & left_mask);
+            return T{at_right & left_mask};
         }
         else
         {
@@ -115,7 +114,7 @@ template <typename T, bool UseSharedMemory=false> class PackedVector
             const std::uint64_t next_elem = static_cast<std::uint64_t>(vec.at(index + 1));
 
             const std::uint64_t right_side = next_elem >> (ELEMSIZE - (BITSIZE - left_index));
-            return static_cast<T>(left_side | right_side);
+            return T{left_side | right_side};
         }
     }
 
diff --git a/include/util/rectangle.hpp b/include/util/rectangle.hpp
index 712818e..8e2501b 100644
--- a/include/util/rectangle.hpp
+++ b/include/util/rectangle.hpp
@@ -23,10 +23,10 @@ namespace util
 struct RectangleInt2D
 {
     RectangleInt2D()
-        : min_lon(std::numeric_limits<std::int32_t>::max()),
-          max_lon(std::numeric_limits<std::int32_t>::min()),
-          min_lat(std::numeric_limits<std::int32_t>::max()),
-          max_lat(std::numeric_limits<std::int32_t>::min())
+        : min_lon{std::numeric_limits<std::int32_t>::max()},
+          max_lon{std::numeric_limits<std::int32_t>::min()},
+          min_lat{std::numeric_limits<std::int32_t>::max()},
+          max_lat{std::numeric_limits<std::int32_t>::min()}
     {
     }
 
@@ -56,10 +56,10 @@ struct RectangleInt2D
         max_lon = std::max(max_lon, other.max_lon);
         min_lat = std::min(min_lat, other.min_lat);
         max_lat = std::max(max_lat, other.max_lat);
-        BOOST_ASSERT(min_lon != FixedLongitude(std::numeric_limits<std::int32_t>::min()));
-        BOOST_ASSERT(min_lat != FixedLatitude(std::numeric_limits<std::int32_t>::min()));
-        BOOST_ASSERT(max_lon != FixedLongitude(std::numeric_limits<std::int32_t>::min()));
-        BOOST_ASSERT(max_lat != FixedLatitude(std::numeric_limits<std::int32_t>::min()));
+        BOOST_ASSERT(min_lon != FixedLongitude{std::numeric_limits<std::int32_t>::min()});
+        BOOST_ASSERT(min_lat != FixedLatitude{std::numeric_limits<std::int32_t>::min()});
+        BOOST_ASSERT(max_lon != FixedLongitude{std::numeric_limits<std::int32_t>::min()});
+        BOOST_ASSERT(max_lat != FixedLatitude{std::numeric_limits<std::int32_t>::min()});
     }
 
     Coordinate Centroid() const
@@ -67,8 +67,8 @@ struct RectangleInt2D
         Coordinate centroid;
         // The coordinates of the midpoints are given by:
         // x = (x1 + x2) /2 and y = (y1 + y2) /2.
-        centroid.lon = (min_lon + max_lon) / FixedLongitude(2);
-        centroid.lat = (min_lat + max_lat) / FixedLatitude(2);
+        centroid.lon = (min_lon + max_lon) / FixedLongitude{2};
+        centroid.lat = (min_lat + max_lat) / FixedLatitude{2};
         return centroid;
     }
 
@@ -169,10 +169,10 @@ struct RectangleInt2D
 
     bool IsValid() const
     {
-        return min_lon != FixedLongitude(std::numeric_limits<std::int32_t>::max()) &&
-               max_lon != FixedLongitude(std::numeric_limits<std::int32_t>::min()) &&
-               min_lat != FixedLatitude(std::numeric_limits<std::int32_t>::max()) &&
-               max_lat != FixedLatitude(std::numeric_limits<std::int32_t>::min());
+        return min_lon != FixedLongitude{std::numeric_limits<std::int32_t>::max()} &&
+               max_lon != FixedLongitude{std::numeric_limits<std::int32_t>::min()} &&
+               min_lat != FixedLatitude{std::numeric_limits<std::int32_t>::max()} &&
+               max_lat != FixedLatitude{std::numeric_limits<std::int32_t>::min()};
     }
 
     friend std::ostream &operator<<(std::ostream &out, const RectangleInt2D &rect);
diff --git a/include/util/static_rtree.hpp b/include/util/static_rtree.hpp
index 12da61c..c955151 100644
--- a/include/util/static_rtree.hpp
+++ b/include/util/static_rtree.hpp
@@ -193,8 +193,8 @@ class StaticRTree
                     Coordinate current_centroid = coordinate_calculation::centroid(
                         m_coordinate_list[current_element.u], m_coordinate_list[current_element.v]);
                     current_centroid.lat =
-                        FixedLatitude(COORDINATE_PRECISION *
-                                      web_mercator::latToY(toFloating(current_centroid.lat)));
+                        FixedLatitude{static_cast<std::int32_t>(COORDINATE_PRECISION *
+                                      web_mercator::latToY(toFloating(current_centroid.lat)))};
 
                     current_wrapper.m_hilbert_value = hilbertCode(current_centroid);
                 }
diff --git a/include/util/strong_typedef.hpp b/include/util/strong_typedef.hpp
index 6385435..0d45a2d 100644
--- a/include/util/strong_typedef.hpp
+++ b/include/util/strong_typedef.hpp
@@ -40,32 +40,32 @@ namespace osrm
  * etc.  Also clarifies what this random "int" value is
  * being used for.
  */
-#define OSRM_STRONG_TYPEDEF(From, To)                                                              \
-    class To final                                                                                 \
-    {                                                                                              \
-        static_assert(std::is_arithmetic<From>(), "");                                             \
-        From x;                                                                                    \
-        friend std::ostream &operator<<(std::ostream &stream, const To &inst);                     \
-                                                                                                   \
-      public:                                                                                      \
-        To() = default;                                                                            \
-        explicit To(const From x_) : x(x_) {}                                                      \
-        explicit operator From &() { return x; }                                                   \
-        explicit operator From() const { return x; }                                               \
-        To operator+(const To rhs_) const { return To(x + static_cast<const From>(rhs_)); }        \
-        To operator-(const To rhs_) const { return To(x - static_cast<const From>(rhs_)); }        \
-        To operator*(const To rhs_) const { return To(x * static_cast<const From>(rhs_)); }        \
-        To operator/(const To rhs_) const { return To(x / static_cast<const From>(rhs_)); }        \
-        bool operator<(const To z_) const { return x < static_cast<const From>(z_); }              \
-        bool operator>(const To z_) const { return x > static_cast<const From>(z_); }              \
-        bool operator<=(const To z_) const { return x <= static_cast<const From>(z_); }            \
-        bool operator>=(const To z_) const { return x >= static_cast<const From>(z_); }            \
-        bool operator==(const To z_) const { return x == static_cast<const From>(z_); }            \
-        bool operator!=(const To z_) const { return x != static_cast<const From>(z_); }            \
-    };                                                                                             \
-    inline std::ostream &operator<<(std::ostream &stream, const To &inst)                          \
-    {                                                                                              \
-        return stream << inst.x;                                                                   \
+#define OSRM_STRONG_TYPEDEF(From, To)                                                                    \
+    struct To final                                                                                      \
+    {                                                                                                    \
+        static_assert(std::is_arithmetic<From>(), "");                                                   \
+        From __value;                                                                                    \
+        friend std::ostream &operator<<(std::ostream &stream, const To &inst);                           \
+                                                                                                         \
+        explicit operator From &() { return __value; }                                                   \
+        explicit operator From() const { return __value; }                                               \
+        To operator+(const To rhs_) const { return To{__value + static_cast<const From>(rhs_)}; }        \
+        To operator-(const To rhs_) const { return To{__value - static_cast<const From>(rhs_)}; }        \
+        To operator*(const To rhs_) const { return To{__value * static_cast<const From>(rhs_)}; }        \
+        To operator/(const To rhs_) const { return To{__value / static_cast<const From>(rhs_)}; }        \
+        bool operator<(const To z_) const { return __value < static_cast<const From>(z_); }              \
+        bool operator>(const To z_) const { return __value > static_cast<const From>(z_); }              \
+        bool operator<=(const To z_) const { return __value <= static_cast<const From>(z_); }            \
+        bool operator>=(const To z_) const { return __value >= static_cast<const From>(z_); }            \
+        bool operator==(const To z_) const { return __value == static_cast<const From>(z_); }            \
+        bool operator!=(const To z_) const { return __value != static_cast<const From>(z_); }            \
+    };                                                                                                   \
+    static_assert(std::is_trivial<To>(), #To " is not a trivial type");                                \
+    static_assert(std::is_standard_layout<To>(), #To " is not a standart layout");                     \
+    static_assert(std::is_pod<To>(), #To " is not a POD layout");                                      \
+    inline std::ostream &operator<<(std::ostream &stream, const To &inst)                                \
+    {                                                                                                    \
+        return stream << inst.__value;                                                                   \
     }
 
 #define OSRM_STRONG_TYPEDEF_HASHABLE(From, To)                                                     \
diff --git a/include/util/typedefs.hpp b/include/util/typedefs.hpp
index 423d306..2ffb577 100644
--- a/include/util/typedefs.hpp
+++ b/include/util/typedefs.hpp
@@ -43,37 +43,44 @@ OSRM_STRONG_TYPEDEF_HASHABLE(std::uint64_t, OSMNodeID)
 OSRM_STRONG_TYPEDEF(std::uint32_t, OSMWayID)
 OSRM_STRONG_TYPEDEF_HASHABLE(std::uint32_t, OSMWayID)
 
-static const OSMNodeID SPECIAL_OSM_NODEID = OSMNodeID(std::numeric_limits<std::uint64_t>::max());
-static const OSMWayID SPECIAL_OSM_WAYID = OSMWayID(std::numeric_limits<std::uint32_t>::max());
+static const OSMNodeID SPECIAL_OSM_NODEID = OSMNodeID{std::numeric_limits<std::uint64_t>::max()};
+static const OSMWayID SPECIAL_OSM_WAYID = OSMWayID{std::numeric_limits<std::uint32_t>::max()};
 
-static const OSMNodeID MAX_OSM_NODEID = OSMNodeID(std::numeric_limits<std::uint64_t>::max());
-static const OSMNodeID MIN_OSM_NODEID = OSMNodeID(std::numeric_limits<std::uint64_t>::min());
-static const OSMWayID MAX_OSM_WAYID = OSMWayID(std::numeric_limits<std::uint32_t>::max());
-static const OSMWayID MIN_OSM_WAYID = OSMWayID(std::numeric_limits<std::uint32_t>::min());
+static const OSMNodeID MAX_OSM_NODEID = OSMNodeID{std::numeric_limits<std::uint64_t>::max()};
+static const OSMNodeID MIN_OSM_NODEID = OSMNodeID{std::numeric_limits<std::uint64_t>::min()};
+static const OSMWayID MAX_OSM_WAYID = OSMWayID{std::numeric_limits<std::uint32_t>::max()};
+static const OSMWayID MIN_OSM_WAYID = OSMWayID{std::numeric_limits<std::uint32_t>::min()};
 
 using OSMNodeID_weak = std::uint64_t;
 using OSMEdgeID_weak = std::uint64_t;
 
-using NodeID = unsigned int;
-using EdgeID = unsigned int;
+using NodeID = std::uint32_t;
+using EdgeID = std::uint32_t;
 using NameID = std::uint32_t;
-using EdgeWeight = int;
+using EdgeWeight = std::int32_t;
+
+using LaneID = std::uint8_t;
+static const LaneID INVALID_LANEID = std::numeric_limits<LaneID>::max();
+using LaneDataID = std::uint16_t;
+static const LaneDataID INVALID_LANE_DATAID = std::numeric_limits<LaneDataID>::max();
+using LaneDescriptionID = std::uint16_t;
+static const LaneDescriptionID INVALID_LANE_DESCRIPTIONID = std::numeric_limits<LaneDescriptionID>::max();
 
 using BearingClassID = std::uint32_t;
-static const BearingClassID INVALID_BEARING_CLASSID = std::numeric_limits<std::uint32_t>::max();
+static const BearingClassID INVALID_BEARING_CLASSID = std::numeric_limits<BearingClassID>::max();
 
 using DiscreteBearing = std::uint16_t;
 
 using EntryClassID = std::uint16_t;
-static const EntryClassID INVALID_ENTRY_CLASSID = std::numeric_limits<std::uint16_t>::max();
+static const EntryClassID INVALID_ENTRY_CLASSID = std::numeric_limits<EntryClassID>::max();
 
-static const NodeID SPECIAL_NODEID = std::numeric_limits<unsigned>::max();
-static const NodeID SPECIAL_SEGMENTID = std::numeric_limits<int>::max();
-static const EdgeID SPECIAL_EDGEID = std::numeric_limits<unsigned>::max();
-static const unsigned INVALID_NAMEID = std::numeric_limits<unsigned>::max();
-static const unsigned EMPTY_NAMEID = 0;
+static const NodeID SPECIAL_NODEID = std::numeric_limits<NodeID>::max();
+static const NodeID SPECIAL_SEGMENTID = std::numeric_limits<NodeID>::max() >> 1;
+static const EdgeID SPECIAL_EDGEID = std::numeric_limits<EdgeID>::max();
+static const NameID INVALID_NAMEID = std::numeric_limits<NameID>::max();
+static const NameID EMPTY_NAMEID = 0;
 static const unsigned INVALID_COMPONENTID = 0;
-static const EdgeWeight INVALID_EDGE_WEIGHT = std::numeric_limits<int>::max();
+static const EdgeWeight INVALID_EDGE_WEIGHT = std::numeric_limits<EdgeWeight>::max();
 
 struct SegmentID
 {
diff --git a/include/util/web_mercator.hpp b/include/util/web_mercator.hpp
index b5819ff..d3fd755 100644
--- a/include/util/web_mercator.hpp
+++ b/include/util/web_mercator.hpp
@@ -35,7 +35,7 @@ inline FloatLatitude yToLat(const double y)
     const double normalized_lat =
         detail::RAD_TO_DEGREE * 2. * std::atan(std::exp(clamped_y * detail::DEGREE_TO_RAD));
 
-    return FloatLatitude(normalized_lat - 90.);
+    return FloatLatitude{normalized_lat - 90.};
 }
 
 inline double latToY(const FloatLatitude latitude)
@@ -56,7 +56,7 @@ template <typename T, typename... U> constexpr double horner(double x, T an, U..
 
 inline double latToYapprox(const FloatLatitude latitude)
 {
-    if (latitude < FloatLatitude(-70.) || latitude > FloatLatitude(70.))
+    if (latitude < FloatLatitude{-70.} || latitude > FloatLatitude{70.})
         return latToY(latitude);
 
     // Approximate the inverse Gudermannian function with the Padé approximant [11/11]: deg → deg
@@ -93,14 +93,14 @@ inline double latToYapprox(const FloatLatitude latitude)
 
 inline FloatLatitude clamp(const FloatLatitude lat)
 {
-    return std::max(std::min(lat, FloatLatitude(detail::MAX_LATITUDE)),
-                    FloatLatitude(-detail::MAX_LATITUDE));
+    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));
+    return std::max(std::min(lon, FloatLongitude{detail::MAX_LONGITUDE}),
+                    FloatLongitude{-detail::MAX_LONGITUDE});
 }
 
 inline void pixelToDegree(const double shift, double &x, double &y)
@@ -159,10 +159,10 @@ inline void xyzToMercator(
 {
     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;
+    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;
 }
 }
 }
diff --git a/package.json b/package.json
index f03a47b..87d21ce 100644
--- a/package.json
+++ b/package.json
@@ -4,13 +4,13 @@
   "private": true,
   "description": "The Open Source Routing Machine is a high performance routing engine written in C++11 designed to run on OpenStreetMap data.",
   "dependencies": {
-    "cucumber": "^0.9.4",
+    "chalk": "^1.1.3",
+    "cucumber": "^1.2.1",
     "d3-queue": "^2.0.3",
     "node-timeout": "0.0.4",
+    "polyline": "^0.2.0",
     "request": "^2.69.0",
-    "xmlbuilder": "^4.2.1",
-    "chalk": "^1.1.3",
-    "polyline": "^0.2.0"
+    "xmlbuilder": "^4.2.1"
   },
   "bin": {
     "cucumber": "./node_modules/cucumber/bin/cucumber.js"
diff --git a/profiles/bicycle.lua b/profiles/bicycle.lua
index 7bbc621..be97de3 100644
--- a/profiles/bicycle.lua
+++ b/profiles/bicycle.lua
@@ -207,7 +207,6 @@ function way_function (way, result)
   local foot = way:get_value_by_key("foot")
   local foot_forward = way:get_value_by_key("foot:forward")
   local foot_backward = way:get_value_by_key("foot:backward")
-  local surface = way:get_value_by_key("surface")
   local bicycle = way:get_value_by_key("bicycle")
 
   -- name
@@ -385,17 +384,12 @@ function way_function (way, result)
     result.backward_speed = walking_speed
   end
 
-  -- surfaces
-  if surface then
-    surface_speed = surface_speeds[surface]
-    if surface_speed then
-      if result.forward_speed > 0 then
-        result.forward_speed = surface_speed
-      end
-      if result.backward_speed > 0 then
-        result.backward_speed  = surface_speed
-      end
-    end
+  -- reduce speed on bad surfaces
+  local surface = way:get_value_by_key("surface")
+
+  if surface and surface_speeds[surface] then
+    result.forward_speed = math.min(surface_speeds[surface], result.forward_speed)
+    result.backward_speed = math.min(surface_speeds[surface], result.backward_speed)
   end
 
   -- maxspeed
diff --git a/profiles/car.lua b/profiles/car.lua
index becf502..c138f86 100644
--- a/profiles/car.lua
+++ b/profiles/car.lua
@@ -166,6 +166,62 @@ function get_exceptions(vector)
   end
 end
 
+-- returns forward,backward psv lane count
+local function getPSVCounts(way)
+    local psv = way:get_value_by_key("lanes:psv")
+    local psv_forward = way:get_value_by_key("lanes:psv:forward");
+    local psv_backward = way:get_value_by_key("lanes:psv:backward");
+
+    local fw = 0;
+    local bw = 0;
+    if( psv and psv ~= "" ) then
+        fw = tonumber(psv)
+        if( fw == nil ) then
+            fw = 0
+        end
+    end
+    if( psv_forward and psv_forward ~= "" ) then
+        fw = tonumber(psv_forward)
+        if( fw == nil ) then
+            fw = 0
+        end
+    end
+    if( psv_backward and psv_backward ~= "" ) then
+        bw = tonumber(psv_backward);
+        if( bw == nil ) then
+            bw = 0
+        end
+    end
+    return fw, bw
+end
+
+-- this is broken for left-sided driving. It needs to switch left and right in case of left-sided driving
+local function getTurnLanes(way)
+    local fw_psv = 0
+    local bw_psv = 0
+    fw_psv, bw_psv = getPSVCounts(way)
+
+    local turn_lanes = way:get_value_by_key("turn:lanes")
+    local turn_lanes_fw = way:get_value_by_key("turn:lanes:forward")
+    local turn_lanes_bw = way:get_value_by_key("turn:lanes:backward")
+
+    if( fw_psv ~= 0 or bw_psv ~= 0 ) then
+        if  turn_lanes and turn_lanes ~= "" then
+            turn_lanes = trimLaneString(turn_lanes, bw_psv, fw_psv )
+        end
+        if  turn_lanes_fw and turn_lanes_fw ~= ""  then
+            turn_lanes_fw = trimLaneString(turn_lanes_fw, bw_psv, fw_psv )
+        end
+        --backwards turn lanes need to treat bw_psv as fw_psv and vice versa
+        if  turn_lanes_bw and turn_lanes_bw ~= ""  then
+            turn_lanes_bw = trimLaneString(turn_lanes_bw, fw_psv, bw_psv )
+        end
+    end
+
+    return turn_lanes, turn_lanes_fw, turn_lanes_bw
+end
+
+
 local function parse_maxspeed(source)
   if not source then
     return 0
@@ -372,6 +428,25 @@ function way_function (way, result)
     result.pronunciation = pronunciation
   end
 
+  local turn_lanes = ""
+  local turn_lanes_forward = ""
+  local turn_lanes_backward = ""
+
+  turn_lanes, turn_lanes_forward, turn_lanes_backward = getTurnLanes(way)
+  if  turn_lanes and turn_lanes ~= "" then
+    result.turn_lanes_forward = turn_lanes;
+    result.turn_lanes_backward = turn_lanes;
+  else
+    if turn_lanes_forward and turn_lanes_forward ~= ""  then
+        result.turn_lanes_forward = turn_lanes_forward;
+    end
+
+    if turn_lanes_backward and turn_lanes_backward ~= "" then
+        result.turn_lanes_backward = turn_lanes_backward;
+    end
+  end
+
+
   if junction and "roundabout" == junction then
     result.roundabout = true
   end
diff --git a/profiles/testbot.lua b/profiles/testbot.lua
index 8bad810..57ae763 100644
--- a/profiles/testbot.lua
+++ b/profiles/testbot.lua
@@ -57,6 +57,7 @@ function way_function (way, result)
   if name then
     result.name = name
   end
+
   result.forward_mode = mode.driving
   result.backward_mode = mode.driving
 
diff --git a/scripts/poly2req.js b/scripts/poly2req.js
index 5b446f3..369e8a9 100755
--- a/scripts/poly2req.js
+++ b/scripts/poly2req.js
@@ -2,7 +2,7 @@
 
 'use strict';
 
-let fs = require('fs');
+let fs = require('fs');  // Node 4.x required!
 
 let VERSION = "route_5.0";
 let SAMPLE_SIZE = 20;
diff --git a/scripts/travis/before_install.armhf.sh b/scripts/travis/before_install.armhf.sh
new file mode 100644
index 0000000..b90ca42
--- /dev/null
+++ b/scripts/travis/before_install.armhf.sh
@@ -0,0 +1,28 @@
+#!/bin/sh -ex
+
+# workaround for gcc4.8 https://gcc.gnu.org/bugzilla/show_bug.cgi?id=55642
+export CXXFLAGS=-Wa,-mimplicit-it=thumb
+
+UBUNTU_RELEASE=$(lsb_release -sc)
+
+sudo dpkg --add-architecture armhf
+
+sudo tee -a /etc/apt/sources.list > /dev/null <<EOF
+deb [arch=armhf] http://ports.ubuntu.com/ubuntu-ports ${UBUNTU_RELEASE} restricted main multiverse universe
+deb [arch=armhf] http://ports.ubuntu.com/ubuntu-ports ${UBUNTU_RELEASE}-security restricted main multiverse universe
+deb [arch=armhf] http://ports.ubuntu.com/ubuntu-ports ${UBUNTU_RELEASE}-updates restricted main multiverse universe
+EOF
+cat /etc/apt/sources.list
+sudo apt-get update -qq --yes || true
+
+
+sudo apt-get install -qq --yes --force-yes g++-4.8-arm-linux-gnueabihf g++-4.8-multilib-arm-linux-gnueabihf gcc-4.8-arm-linux-gnueabihf gcc-4.8-multilib-arm-linux-gnueabihf
+sudo apt-get install -qq --yes --force-yes libexpat1-dev:armhf zlib1g-dev:armhf libbz2-dev:armhf libboost-date-time-dev:armhf libboost-filesystem-dev:armhf libboost-iostreams-dev:armhf libboost-program-options-dev:armhf libboost-regex-dev:armhf libboost-system-dev:armhf libboost-thread-dev:armhf libtbb-dev:armhf libboost-test-dev:armhf libluabind-dev:armhf
+
+## build libstxxl1v5:armhf from sources, no package in trusty
+if [ $UBUNTU_RELEASE == trusty ] ; then
+    ( cd /tmp && wget http://sourceforge.net/projects/stxxl/files/stxxl/1.4.1/stxxl-1.4.1.tar.gz && tar xf stxxl-1.4.1.tar.gz )
+    ( cd /tmp/stxxl-1.4.1 && mkdir build && cd build && CC=arm-linux-gnueabihf-gcc-4.8 CXX=arm-linux-gnueabihf-g++-4.8 cmake .. && make && sudo make install )
+else
+    sudo apt-get install -qq --yes --force-yes libstxxl1v5:armhf
+fi
diff --git a/scripts/travis/before_install.i686.sh b/scripts/travis/before_install.i686.sh
new file mode 100644
index 0000000..ce8414b
--- /dev/null
+++ b/scripts/travis/before_install.i686.sh
@@ -0,0 +1,10 @@
+#!/bin/sh -ex
+
+## Generate code for 32-bit ABI with default for x86_84 fpmath
+export CFLAGS='-m32 -msse2 -mfpmath=sse'
+export CXXFLAGS='-m32 -msse2 -mfpmath=sse'
+
+sudo dpkg --add-architecture i386
+sudo add-apt-repository --yes ppa:ubuntu-toolchain-r/test && ( sudo apt-get update -qq --yes || true )
+
+sudo apt-get install -qq --yes --force-yes g++-5-multilib libxml2-dev:i386 libexpat1-dev:i386 libzip-dev:i386 libbz2-dev:i386 libstxxl-dev:i386 libtbb-dev:i386 lua5.2:i386 liblua5.2-dev:i386 libluabind-dev:i386 libboost-date-time-dev:i386 libboost-filesystem-dev:i386 libboost-iostreams-dev:i386 libboost-program-options-dev:i386 libboost-regex-dev:i386 libboost-system-dev:i386 libboost-thread-dev:i386 libboost-test-dev:i386
diff --git a/src/contractor/contractor.cpp b/src/contractor/contractor.cpp
index c4adcef..e77a6b0 100644
--- a/src/contractor/contractor.cpp
+++ b/src/contractor/contractor.cpp
@@ -3,6 +3,7 @@
 #include "contractor/graph_contractor.hpp"
 
 #include "extractor/compressed_edge_container.hpp"
+#include "extractor/edge_based_graph_factory.hpp"
 #include "extractor/node_based_edge.hpp"
 
 #include "util/exception.hpp"
@@ -71,12 +72,21 @@ namespace osrm
 namespace contractor
 {
 
+// Returns duration in deci-seconds
+inline EdgeWeight distanceAndSpeedToWeight(double distance_in_meters, double speed_in_kmh)
+{
+    BOOST_ASSERT(speed_in_kmh > 0);
+    const double speed_in_ms = speed_in_kmh / 3.6;
+    const double duration = distance_in_meters / speed_in_ms;
+    return std::max<EdgeWeight>(1, static_cast<EdgeWeight>(std::round(duration * 10)));
+}
+
 int Contractor::Run()
 {
 #ifdef WIN32
 #pragma message("Memory consumption on Windows can be higher due to different bit packing")
 #else
-    static_assert(sizeof(extractor::NodeBasedEdge) == 20,
+    static_assert(sizeof(extractor::NodeBasedEdge) == 24,
                   "changing extractor::NodeBasedEdge type has influence on memory consumption!");
     static_assert(sizeof(extractor::EdgeBasedEdge) == 16,
                   "changing EdgeBasedEdge type has influence on memory consumption!");
@@ -93,17 +103,17 @@ int Contractor::Run()
 
     util::DeallocatingVector<extractor::EdgeBasedEdge> edge_based_edge_list;
 
-    std::size_t max_edge_id = LoadEdgeExpandedGraph(config.edge_based_graph_path,
-                                                    edge_based_edge_list,
-                                                    config.edge_segment_lookup_path,
-                                                    config.edge_penalty_path,
-                                                    config.segment_speed_lookup_paths,
-                                                    config.turn_penalty_lookup_paths,
-                                                    config.node_based_graph_path,
-                                                    config.geometry_path,
-                                                    config.datasource_names_path,
-                                                    config.datasource_indexes_path,
-                                                    config.rtree_leaf_path);
+    EdgeID max_edge_id = LoadEdgeExpandedGraph(config.edge_based_graph_path,
+                                               edge_based_edge_list,
+                                               config.edge_segment_lookup_path,
+                                               config.edge_penalty_path,
+                                               config.segment_speed_lookup_paths,
+                                               config.turn_penalty_lookup_paths,
+                                               config.node_based_graph_path,
+                                               config.geometry_path,
+                                               config.datasource_names_path,
+                                               config.datasource_indexes_path,
+                                               config.rtree_leaf_path);
 
     // Contracting the edge-expanded graph
 
@@ -252,7 +262,7 @@ parse_segment_lookup_from_csv_files(const std::vector<std::string> &segment_spee
                 throw util::exception{"Segment speed file " + filename + " malformed"};
 
             SegmentSpeedSource val{
-                {static_cast<OSMNodeID>(from_node_id), static_cast<OSMNodeID>(to_node_id)},
+                {OSMNodeID{from_node_id}, OSMNodeID{to_node_id}},
                 {speed, static_cast<std::uint8_t>(file_id)}};
 
             local.push_back(std::move(val));
@@ -338,7 +348,7 @@ parse_turn_penalty_lookup_from_csv_files(const std::vector<std::string> &turn_pe
                 throw util::exception{"Turn penalty file " + filename + " malformed"};
 
             map[std::make_tuple(
-                OSMNodeID(from_node_id), OSMNodeID(via_node_id), OSMNodeID(to_node_id))] =
+                OSMNodeID{from_node_id}, OSMNodeID{via_node_id}, OSMNodeID{to_node_id})] =
                 std::make_pair(penalty, file_id);
         }
     };
@@ -349,7 +359,7 @@ parse_turn_penalty_lookup_from_csv_files(const std::vector<std::string> &turn_pe
 }
 } // anon ns
 
-std::size_t Contractor::LoadEdgeExpandedGraph(
+EdgeID Contractor::LoadEdgeExpandedGraph(
     std::string const &edge_based_graph_filename,
     util::DeallocatingVector<extractor::EdgeBasedEdge> &edge_based_edge_list,
     const std::string &edge_segment_lookup_filename,
@@ -366,41 +376,58 @@ std::size_t Contractor::LoadEdgeExpandedGraph(
         throw util::exception("Limit of 255 segment speed and turn penalty files each reached");
 
     util::SimpleLogger().Write() << "Opening " << edge_based_graph_filename;
-    boost::filesystem::ifstream input_stream(edge_based_graph_filename, std::ios::binary);
-    if (!input_stream)
-        throw util::exception("Could not load edge based graph file");
+
+    auto mmap_file = [](const std::string &filename) {
+        using boost::interprocess::file_mapping;
+        using boost::interprocess::mapped_region;
+        using boost::interprocess::read_only;
+
+        const file_mapping mapping{ filename.c_str(), read_only };
+        mapped_region region{mapping, read_only};
+        region.advise(mapped_region::advice_sequential);
+        return region;
+    };
+
+    const auto edge_based_graph_region = mmap_file(edge_based_graph_filename);
 
     const bool update_edge_weights = !segment_speed_filenames.empty();
     const bool update_turn_penalties = !turn_penalty_filenames.empty();
 
-    boost::filesystem::ifstream edge_segment_input_stream;
-    boost::filesystem::ifstream edge_fixed_penalties_input_stream;
+    const auto edge_penalty_region = [&] {
+        if (update_edge_weights || update_turn_penalties)
+        {
+            return mmap_file(edge_penalty_filename);
+        }
+        return boost::interprocess::mapped_region();
+    }();
 
-    if (update_edge_weights || update_turn_penalties)
-    {
-        edge_segment_input_stream.open(edge_segment_lookup_filename, std::ios::binary);
-        edge_fixed_penalties_input_stream.open(edge_penalty_filename, std::ios::binary);
-        if (!edge_segment_input_stream || !edge_fixed_penalties_input_stream)
+    const auto edge_segment_region = [&] {
+        if (update_edge_weights || update_turn_penalties)
         {
-            throw util::exception("Could not load .edge_segment_lookup or .edge_penalties, did you "
-                                  "run osrm-extract with '--generate-edge-lookup'?");
+            return mmap_file(edge_segment_lookup_filename);
         }
-    }
+        return boost::interprocess::mapped_region();
+    }();
+
+    // Set the struct packing to 1 byte word sizes.  This prevents any padding.  We only use
+    // this struct once, so any alignment penalty is trivial.  If this is *not* done, then
+    // the struct will be padded out by an extra 4 bytes, and sizeof() will mean we read
+    // too much data from the original file.
+    #pragma pack(push, r1, 1)
+    struct EdgeBasedGraphHeader {
+        util::FingerPrint fingerprint;
+        std::uint64_t number_of_edges;
+        EdgeID max_edge_id;
+    };
+    #pragma pack(pop, r1)
+
+    const EdgeBasedGraphHeader graph_header = *(reinterpret_cast<const EdgeBasedGraphHeader *>(edge_based_graph_region.get_address()));
 
     const util::FingerPrint fingerprint_valid = util::FingerPrint::GetValid();
-    util::FingerPrint fingerprint_loaded;
-    input_stream.read((char *)&fingerprint_loaded, sizeof(util::FingerPrint));
-    fingerprint_loaded.TestContractor(fingerprint_valid);
-
-    // TODO std::size_t can vary on systems. Our files are not transferable, but we might want to
-    // consider using a fixed size type for I/O
-    std::size_t number_of_edges = 0;
-    std::size_t max_edge_id = SPECIAL_EDGEID;
-    input_stream.read((char *)&number_of_edges, sizeof(std::size_t));
-    input_stream.read((char *)&max_edge_id, sizeof(std::size_t));
-
-    edge_based_edge_list.resize(number_of_edges);
-    util::SimpleLogger().Write() << "Reading " << number_of_edges
+    graph_header.fingerprint.TestContractor(fingerprint_valid);
+
+    edge_based_edge_list.resize(graph_header.number_of_edges);
+    util::SimpleLogger().Write() << "Reading " << graph_header.number_of_edges
                                  << " edges from the edge based graph";
 
     SegmentSpeedSourceFlatMap segment_speed_lookup;
@@ -502,12 +529,9 @@ std::size_t Contractor::LoadEdgeExpandedGraph(
         // update the RTree itself, we just use the leaf nodes to iterate over all segments)
         using LeafNode = util::StaticRTree<extractor::EdgeBasedNode>::LeafNode;
 
-        using boost::interprocess::file_mapping;
         using boost::interprocess::mapped_region;
-        using boost::interprocess::read_only;
 
-        const file_mapping mapping{rtree_leaf_filename.c_str(), read_only};
-        mapped_region region{mapping, read_only};
+        auto region = mmap_file(rtree_leaf_filename.c_str());
         region.advise(mapped_region::advice_willneed);
 
         const auto bytes = region.get_size();
@@ -558,12 +582,11 @@ std::size_t Contractor::LoadEdgeExpandedGraph(
                         find(segment_speed_lookup, Segment{u->node_id, v->node_id});
                     if (forward_speed_iter != segment_speed_lookup.end())
                     {
-                        int new_segment_weight =
-                            std::max(1,
-                                     static_cast<int>(std::floor(
-                                         (segment_length * 10.) /
-                                             (forward_speed_iter->speed_source.speed / 3.6) +
-                                         .5)));
+                        auto new_segment_weight =
+                            (forward_speed_iter->speed_source.speed > 0)
+                                ? distanceAndSpeedToWeight(segment_length,
+                                                           forward_speed_iter->speed_source.speed)
+                                : INVALID_EDGE_WEIGHT;
                         m_geometry_list[forward_begin + leaf_object.fwd_segment_position].weight =
                             new_segment_weight;
                         m_geometry_datasource[forward_begin + leaf_object.fwd_segment_position] =
@@ -609,12 +632,11 @@ std::size_t Contractor::LoadEdgeExpandedGraph(
                         find(segment_speed_lookup, Segment{u->node_id, v->node_id});
                     if (reverse_speed_iter != segment_speed_lookup.end())
                     {
-                        int new_segment_weight =
-                            std::max(1,
-                                     static_cast<int>(std::floor(
-                                         (segment_length * 10.) /
-                                             (reverse_speed_iter->speed_source.speed / 3.6) +
-                                         .5)));
+                        auto new_segment_weight =
+                            (reverse_speed_iter->speed_source.speed > 0)
+                                ? distanceAndSpeedToWeight(segment_length,
+                                                           reverse_speed_iter->speed_source.speed)
+                                : INVALID_EDGE_WEIGHT;
                         m_geometry_list[reverse_begin + rev_segment_position].weight =
                             new_segment_weight;
                         m_geometry_datasource[reverse_begin + rev_segment_position] =
@@ -686,7 +708,7 @@ std::size_t Contractor::LoadEdgeExpandedGraph(
         {
             throw util::exception("Failed to open " + datasource_indexes_filename + " for writing");
         }
-        auto number_of_datasource_entries = m_geometry_datasource.size();
+        std::uint64_t number_of_datasource_entries = m_geometry_datasource.size();
         datasource_stream.write(reinterpret_cast<const char *>(&number_of_datasource_entries),
                                 sizeof(number_of_datasource_entries));
         if (number_of_datasource_entries > 0)
@@ -715,74 +737,67 @@ std::size_t Contractor::LoadEdgeExpandedGraph(
 
     tbb::parallel_invoke(maybe_save_geometries, save_datasource_indexes, save_datastore_names);
 
-    // TODO: can we read this in bulk?  util::DeallocatingVector isn't necessarily
-    // all stored contiguously
-    for (; number_of_edges > 0; --number_of_edges)
+    auto penaltyblock =
+        reinterpret_cast<const extractor::lookup::PenaltyBlock *>(edge_penalty_region.get_address());
+    auto edge_segment_byte_ptr = reinterpret_cast<const char *>(edge_segment_region.get_address());
+    auto edge_based_edge_ptr = reinterpret_cast<extractor::EdgeBasedEdge *>(
+        reinterpret_cast<char *>(edge_based_graph_region.get_address()) + sizeof(EdgeBasedGraphHeader));
+
+    const auto edge_based_edge_last = reinterpret_cast<extractor::EdgeBasedEdge *>(
+        reinterpret_cast<char *>(edge_based_graph_region.get_address()) + sizeof(EdgeBasedGraphHeader) + sizeof(extractor::EdgeBasedEdge) * graph_header.number_of_edges);
+
+    while (edge_based_edge_ptr != edge_based_edge_last)
     {
-        extractor::EdgeBasedEdge inbuffer;
-        input_stream.read((char *)&inbuffer, sizeof(extractor::EdgeBasedEdge));
+        // Make a copy of the data from the memory map
+        extractor::EdgeBasedEdge inbuffer = *edge_based_edge_ptr;
+        edge_based_edge_ptr++;
+
         if (update_edge_weights || update_turn_penalties)
         {
-            // Processing-time edge updates
-            unsigned fixed_penalty;
-            edge_fixed_penalties_input_stream.read(reinterpret_cast<char *>(&fixed_penalty),
-                                                   sizeof(fixed_penalty));
+            auto header = reinterpret_cast<const extractor::lookup::SegmentHeaderBlock *>(
+                edge_segment_byte_ptr);
+            edge_segment_byte_ptr += sizeof(extractor::lookup::SegmentHeaderBlock);
 
+            auto previous_osm_node_id = header->previous_osm_node_id;
             int new_weight = 0;
+            int compressed_edge_nodes = static_cast<int>(header->num_osm_nodes);
 
-            unsigned num_osm_nodes = 0;
-            edge_segment_input_stream.read(reinterpret_cast<char *>(&num_osm_nodes),
-                                           sizeof(num_osm_nodes));
-            OSMNodeID previous_osm_node_id;
-            edge_segment_input_stream.read(reinterpret_cast<char *>(&previous_osm_node_id),
-                                           sizeof(previous_osm_node_id));
-            OSMNodeID this_osm_node_id;
-            double segment_length;
-            int segment_weight;
-            int compressed_edge_nodes = static_cast<int>(num_osm_nodes);
-            --num_osm_nodes;
-            for (; num_osm_nodes != 0; --num_osm_nodes)
-            {
-                edge_segment_input_stream.read(reinterpret_cast<char *>(&this_osm_node_id),
-                                               sizeof(this_osm_node_id));
-                edge_segment_input_stream.read(reinterpret_cast<char *>(&segment_length),
-                                               sizeof(segment_length));
-                edge_segment_input_stream.read(reinterpret_cast<char *>(&segment_weight),
-                                               sizeof(segment_weight));
+            auto segmentblocks =
+                reinterpret_cast<const extractor::lookup::SegmentBlock *>(edge_segment_byte_ptr);
+            edge_segment_byte_ptr +=
+                sizeof(extractor::lookup::SegmentBlock) * (header->num_osm_nodes - 1);
 
+            const auto num_segments = header->num_osm_nodes - 1;
+            for (auto i : util::irange<std::size_t>(0, num_segments))
+            {
                 auto speed_iter =
-                    find(segment_speed_lookup, Segment{previous_osm_node_id, this_osm_node_id});
+                    find(segment_speed_lookup,
+                         Segment{previous_osm_node_id, segmentblocks[i].this_osm_node_id});
                 if (speed_iter != segment_speed_lookup.end())
                 {
-                    // This sets the segment weight using the same formula as the
-                    // EdgeBasedGraphFactory for consistency.  The *why* of this formula
-                    // is lost in the annals of time.
-                    int new_segment_weight = std::max(
-                        1,
-                        static_cast<int>(std::floor(
-                            (segment_length * 10.) / (speed_iter->speed_source.speed / 3.6) + .5)));
-                    new_weight += new_segment_weight;
+                    if (speed_iter->speed_source.speed > 0)
+                    {
+                        auto new_segment_weight = distanceAndSpeedToWeight(segmentblocks[i].segment_length, speed_iter->speed_source.speed);
+                        new_weight += new_segment_weight;
+                    }
+                    else
+                    {
+                        // This edge is blocked, we don't need to continue updating
+                        new_weight = INVALID_EDGE_WEIGHT;
+                        break;
+                    }
                 }
                 else
                 {
                     // If no lookup found, use the original weight value for this segment
-                    new_weight += segment_weight;
+                    new_weight += segmentblocks[i].segment_weight;
                 }
 
-                previous_osm_node_id = this_osm_node_id;
+                previous_osm_node_id = segmentblocks[i].this_osm_node_id;
             }
 
-            OSMNodeID from_id;
-            OSMNodeID via_id;
-            OSMNodeID to_id;
-            edge_fixed_penalties_input_stream.read(reinterpret_cast<char *>(&from_id),
-                                                   sizeof(from_id));
-            edge_fixed_penalties_input_stream.read(reinterpret_cast<char *>(&via_id),
-                                                   sizeof(via_id));
-            edge_fixed_penalties_input_stream.read(reinterpret_cast<char *>(&to_id), sizeof(to_id));
-
-            const auto turn_iter =
-                turn_penalty_lookup.find(std::make_tuple(from_id, via_id, to_id));
+            const auto turn_iter = turn_penalty_lookup.find(
+                std::make_tuple(penaltyblock->from_id, penaltyblock->via_id, penaltyblock->to_id));
             if (turn_iter != turn_penalty_lookup.end())
             {
                 int new_turn_weight = static_cast<int>(turn_iter->second.first * 10);
@@ -790,24 +805,28 @@ std::size_t Contractor::LoadEdgeExpandedGraph(
                 if (new_turn_weight + new_weight < compressed_edge_nodes)
                 {
                     util::SimpleLogger().Write(logWARNING)
-                        << "turn penalty " << turn_iter->second.first << " for turn " << from_id
-                        << ", " << via_id << ", " << to_id
-                        << " is too negative: clamping turn weight to " << compressed_edge_nodes;
+                        << "turn penalty " << turn_iter->second.first << " for turn "
+                        << penaltyblock->from_id << ", " << penaltyblock->via_id << ", "
+                        << penaltyblock->to_id << " is too negative: clamping turn weight to "
+                        << compressed_edge_nodes;
                 }
 
                 inbuffer.weight = std::max(new_turn_weight + new_weight, compressed_edge_nodes);
             }
             else
             {
-                inbuffer.weight = fixed_penalty + new_weight;
+                inbuffer.weight = penaltyblock->fixed_penalty + new_weight;
             }
+
+            // Increment the pointer
+            penaltyblock++;
         }
 
         edge_based_edge_list.emplace_back(std::move(inbuffer));
     }
 
     util::SimpleLogger().Write() << "Done reading edges";
-    return max_edge_id;
+    return graph_header.max_edge_id;
 }
 
 void Contractor::ReadNodeLevels(std::vector<float> &node_levels) const
@@ -861,8 +880,8 @@ Contractor::WriteContractedGraph(unsigned max_node_id,
     const util::FingerPrint fingerprint = util::FingerPrint::GetValid();
     boost::filesystem::ofstream hsgr_output_stream(config.graph_output_path, std::ios::binary);
     hsgr_output_stream.write((char *)&fingerprint, sizeof(util::FingerPrint));
-    const unsigned max_used_node_id = [&contracted_edge_list] {
-        unsigned tmp_max = 0;
+    const NodeID max_used_node_id = [&contracted_edge_list] {
+        NodeID tmp_max = 0;
         for (const QueryEdge &edge : contracted_edge_list)
         {
             BOOST_ASSERT(SPECIAL_NODEID != edge.source);
@@ -928,7 +947,7 @@ Contractor::WriteContractedGraph(unsigned max_node_id,
 
     // serialize all edges
     util::SimpleLogger().Write() << "Building edge array";
-    int number_of_used_edges = 0;
+    std::size_t number_of_used_edges = 0;
 
     util::StaticGraph<EdgeData>::EdgeArrayEntry current_edge;
     for (const auto edge : util::irange<std::size_t>(0UL, contracted_edge_list.size()))
@@ -970,7 +989,7 @@ Contractor::WriteContractedGraph(unsigned max_node_id,
  \brief Build contracted graph.
  */
 void Contractor::ContractGraph(
-    const unsigned max_edge_id,
+    const EdgeID max_edge_id,
     util::DeallocatingVector<extractor::EdgeBasedEdge> &edge_based_edge_list,
     util::DeallocatingVector<QueryEdge> &contracted_edge_list,
     std::vector<EdgeWeight> &&node_weights,
diff --git a/src/engine/api/json_factory.cpp b/src/engine/api/json_factory.cpp
index 464003a..70579b7 100644
--- a/src/engine/api/json_factory.cpp
+++ b/src/engine/api/json_factory.cpp
@@ -7,6 +7,7 @@
 #include "util/guidance/bearing_class.hpp"
 #include "util/guidance/entry_class.hpp"
 #include "util/guidance/toolkit.hpp"
+#include "util/typedefs.hpp"
 
 #include <boost/assert.hpp>
 #include <boost/optional.hpp>
@@ -47,7 +48,7 @@ const constexpr char *turn_type_names[] = {
     "invalid",         "new name",   "continue", "turn",        "merge",
     "on ramp",         "off ramp",   "fork",     "end of road", "notification",
     "roundabout",      "roundabout", "rotary",   "rotary",      "roundabout turn",
-    "roundabout turn", "invalid",    "invalid",  "invalid",     "invalid",
+    "roundabout turn", "use lane",   "invalid",  "invalid",     "invalid",
     "invalid",         "invalid",    "invalid",  "invalid",     "invalid",
     "invalid"};
 
@@ -56,10 +57,13 @@ const constexpr char *waypoint_type_names[] = {"invalid", "arrive", "depart"};
 // Check whether to include a modifier in the result of the API
 inline bool isValidModifier(const guidance::StepManeuver maneuver)
 {
-    if (maneuver.waypoint_type != guidance::WaypointType::None &&
-        maneuver.instruction.direction_modifier == DirectionModifier::UTurn)
-        return false;
-    return true;
+    return (maneuver.waypoint_type == guidance::WaypointType::None ||
+            maneuver.instruction.direction_modifier != DirectionModifier::UTurn);
+}
+
+inline bool hasValidLanes(const guidance::Intersection &intersection)
+{
+    return intersection.lanes.lanes_in_turn > 0;
 }
 
 std::string instructionTypeToString(const TurnType::Enum type)
@@ -67,6 +71,29 @@ std::string instructionTypeToString(const TurnType::Enum type)
     return turn_type_names[static_cast<std::size_t>(type)];
 }
 
+util::json::Array lanesFromIntersection(const guidance::Intersection &intersection)
+{
+    BOOST_ASSERT(intersection.lanes.lanes_in_turn >= 1);
+    util::json::Array result;
+    LaneID lane_id = intersection.lane_description.size();
+
+    for (const auto &lane_desc : intersection.lane_description)
+    {
+        --lane_id;
+        util::json::Object lane;
+        lane.values["indications"] = extractor::guidance::TurnLaneType::toJsonArray(lane_desc);
+        if (lane_id >= intersection.lanes.first_lane_from_the_right &&
+            lane_id < intersection.lanes.first_lane_from_the_right + intersection.lanes.lanes_in_turn)
+            lane.values["valid"] = util::json::True();
+        else
+            lane.values["valid"] = util::json::False();
+
+        result.values.push_back(lane);
+    }
+
+    return result;
+}
+
 std::string instructionModifierToString(const DirectionModifier::Enum modifier)
 {
     return modifier_names[static_cast<std::size_t>(modifier)];
@@ -187,6 +214,9 @@ util::json::Object makeIntersection(const guidance::Intersection &intersection)
     if (intersection.out != guidance::Intersection::NO_INDEX)
         result.values["out"] = intersection.out;
 
+    if (detail::hasValidLanes(intersection))
+        result.values["lanes"] = detail::lanesFromIntersection(intersection);
+
     return result;
 }
 
diff --git a/src/engine/guidance/lane_processing.cpp b/src/engine/guidance/lane_processing.cpp
new file mode 100644
index 0000000..03bb3c4
--- /dev/null
+++ b/src/engine/guidance/lane_processing.cpp
@@ -0,0 +1,109 @@
+#include "util/for_each_pair.hpp"
+#include "util/group_by.hpp"
+#include "util/guidance/toolkit.hpp"
+
+#include "extractor/guidance/turn_instruction.hpp"
+
+#include <iterator>
+
+using TurnInstruction = osrm::extractor::guidance::TurnInstruction;
+namespace TurnType = osrm::extractor::guidance::TurnType;
+namespace DirectionModifier = osrm::extractor::guidance::DirectionModifier;
+
+using osrm::util::guidance::isLeftTurn;
+using osrm::util::guidance::isRightTurn;
+
+namespace osrm
+{
+namespace engine
+{
+namespace guidance
+{
+
+std::vector<RouteStep> anticipateLaneChange(std::vector<RouteStep> steps,
+                                            const double min_duration_needed_for_lane_change)
+{
+    // Postprocessing does not strictly guarantee for only turns
+    const auto is_turn = [](const RouteStep &step) {
+        return step.maneuver.instruction.type != TurnType::NewName &&
+               step.maneuver.instruction.type != TurnType::Notification;
+    };
+
+    const auto is_quick = [min_duration_needed_for_lane_change](const RouteStep &step) {
+        return step.duration < min_duration_needed_for_lane_change;
+    };
+
+    const auto is_quick_turn = [&](const RouteStep &step) {
+        return is_turn(step) && is_quick(step);
+    };
+
+    // Determine range of subsequent quick turns, candidates for possible lane anticipation
+    using StepIter = decltype(steps)::iterator;
+    using StepIterRange = std::pair<StepIter, StepIter>;
+
+    std::vector<StepIterRange> subsequent_quick_turns;
+
+    const auto keep_turn_range = [&](StepIterRange range) {
+        if (std::distance(range.first, range.second) > 1)
+            subsequent_quick_turns.push_back(std::move(range));
+    };
+
+    util::group_by(begin(steps), end(steps), is_quick_turn, keep_turn_range);
+
+    // Walk backwards over all turns, constraining possible turn lanes.
+    // Later turn lanes constrain earlier ones: we have to anticipate lane changes.
+    const auto constrain_lanes = [](const StepIterRange &turns) {
+        const std::reverse_iterator<StepIter> rev_first{turns.second};
+        const std::reverse_iterator<StepIter> rev_last{turns.first};
+
+        // We're walking backwards over all adjacent turns:
+        // the current turn lanes constrain the lanes we have to take in the previous turn.
+        util::for_each_pair(rev_first, rev_last, [](RouteStep &current, RouteStep &previous) {
+            const auto current_inst = current.maneuver.instruction;
+            const auto current_lanes = current.intersections.front().lanes;
+
+            // Constrain the previous turn's lanes
+            auto &previous_lanes = previous.intersections.front().lanes;
+            // Lane mapping (N:M) from previous lanes (N) to current lanes (M), with:
+            //  N > M, N > 1   fan-in situation, constrain N lanes to min(N,M) shared lanes
+            //  otherwise      nothing to constrain
+            const bool lanes_to_constrain = previous_lanes.lanes_in_turn > 1;
+            const bool lanes_fan_in = previous_lanes.lanes_in_turn > current_lanes.lanes_in_turn;
+
+            if (!lanes_to_constrain || !lanes_fan_in)
+                return;
+
+            // In case there is no lane information we work with one artificial lane
+            const auto current_adjusted_lanes = std::max(current_lanes.lanes_in_turn, LaneID{1});
+
+            const auto num_shared_lanes = std::min(current_adjusted_lanes, //
+                                                   previous_lanes.lanes_in_turn);
+
+            if (isRightTurn(current_inst))
+            {
+                // Current turn is right turn, already keep right during the previous turn.
+                // This implies constraining the leftmost lanes in the previous turn step.
+                previous_lanes = {num_shared_lanes, previous_lanes.first_lane_from_the_right};
+            }
+            else if (isLeftTurn(current_inst))
+            {
+                // Current turn is left turn, already keep left during previous turn.
+                // This implies constraining the rightmost lanes in the previous turn step.
+                const LaneID shared_lane_delta = previous_lanes.lanes_in_turn - num_shared_lanes;
+                const LaneID previous_adjusted_lanes =
+                    std::min(current_adjusted_lanes, shared_lane_delta);
+                const LaneID constraint_first_lane_from_the_right =
+                    previous_lanes.first_lane_from_the_right + previous_adjusted_lanes;
+
+                previous_lanes = {num_shared_lanes, constraint_first_lane_from_the_right};
+            }
+        });
+    };
+
+    std::for_each(begin(subsequent_quick_turns), end(subsequent_quick_turns), constrain_lanes);
+    return steps;
+}
+
+} // namespace guidance
+} // namespace engine
+} // namespace osrm
diff --git a/src/engine/guidance/post_processing.cpp b/src/engine/guidance/post_processing.cpp
index e593a7e..e859667 100644
--- a/src/engine/guidance/post_processing.cpp
+++ b/src/engine/guidance/post_processing.cpp
@@ -2,11 +2,14 @@
 #include "engine/guidance/post_processing.hpp"
 
 #include "engine/guidance/assemble_steps.hpp"
+#include "engine/guidance/lane_processing.hpp"
 #include "engine/guidance/toolkit.hpp"
 
 #include "util/guidance/toolkit.hpp"
+#include "util/guidance/turn_lanes.hpp"
 
 #include <boost/assert.hpp>
+#include <boost/numeric/conversion/cast.hpp>
 #include <boost/range/algorithm_ext/erase.hpp>
 
 #include <algorithm>
@@ -31,16 +34,19 @@ namespace guidance
 
 namespace
 {
-const constexpr double MAX_COLLAPSE_DISTANCE = 25;
+const constexpr std::size_t MIN_END_OF_ROAD_INTERSECTIONS = std::size_t{2};
+const constexpr double MAX_COLLAPSE_DISTANCE = 30;
 
 inline bool choiceless(const RouteStep &step, const RouteStep &previous)
 {
     // if the next turn is choiceless, we consider longer turn roads collapsable than usually
     // accepted. We might need to improve this to find out whether we merge onto a through-street.
-    return previous.distance < 3 * MAX_COLLAPSE_DISTANCE &&
-           1 >= std::count(step.intersections.front().entry.begin(),
-                           step.intersections.front().entry.end(),
-                           true);
+    BOOST_ASSERT(!step.intersections.empty());
+    const auto is_without_choice = previous.distance < 4 * MAX_COLLAPSE_DISTANCE &&
+                                   1 >= std::count(step.intersections.front().entry.begin(),
+                                                   step.intersections.front().entry.end(),
+                                                   true);
+    return is_without_choice;
 }
 
 // List of types that can be collapsed, if all other restrictions pass
@@ -61,9 +67,9 @@ bool isCollapsableInstruction(const TurnInstruction instruction)
 // a possible u-turn.
 bool collapsable(const RouteStep &step)
 {
-
     return step.distance < MAX_COLLAPSE_DISTANCE &&
-           isCollapsableInstruction(step.maneuver.instruction);
+           (step.maneuver.instruction.type == TurnType::UseLane ||
+            isCollapsableInstruction(step.maneuver.instruction));
 }
 
 bool compatible(const RouteStep &lhs, const RouteStep &rhs) { return lhs.mode == rhs.mode; }
@@ -83,40 +89,6 @@ double nameSegmentLength(std::size_t at, const std::vector<RouteStep> &steps)
 // invalidate a step and set its content to nothing
 void invalidateStep(RouteStep &step) { step = getInvalidRouteStep(); }
 
-void print(const RouteStep &step)
-{
-    std::cout << static_cast<int>(step.maneuver.instruction.type) << " "
-              << static_cast<int>(step.maneuver.instruction.direction_modifier) << "  "
-              << static_cast<int>(step.maneuver.waypoint_type) << " Duration: " << step.duration
-              << " Distance: " << step.distance << " Geometry: " << step.geometry_begin << " "
-              << step.geometry_end << " exit: " << step.maneuver.exit
-              << " Intersections: " << step.intersections.size() << " [";
-
-    for (const auto &intersection : step.intersections)
-    {
-        std::cout << "(bearings:";
-        for (auto bearing : intersection.bearings)
-            std::cout << " " << bearing;
-        std::cout << ", entry: ";
-        for (auto entry : intersection.entry)
-            std::cout << " " << entry;
-        std::cout << ")";
-    }
-    std::cout << "] name[" << step.name_id << "]: " << step.name;
-}
-
-void print(const std::vector<RouteStep> &steps)
-{
-    std::cout << "Path\n";
-    int segment = 0;
-    for (const auto &step : steps)
-    {
-        std::cout << "\t[" << segment++ << "]: ";
-        print(step);
-        std::cout << std::endl;
-    }
-}
-
 // Compute the angle between two bearings on a normal turn circle
 //
 //      Bearings                      Angles
@@ -204,8 +176,7 @@ void fixFinalRoundabout(std::vector<RouteStep> &steps)
             // instruction though. it is not contained somewhere until now
             steps[propagation_index - 1] =
                 forwardInto(std::move(steps[propagation_index - 1]), propagation_step);
-            propagation_step.maneuver.instruction =
-                TurnInstruction::NO_TURN(); // mark intermediate instructions invalid
+            invalidateStep(propagation_step);
         }
     }
 }
@@ -256,7 +227,6 @@ void closeOffRoundabout(const bool on_roundabout,
     step.maneuver.exit += 1;
     if (!on_roundabout)
     {
-
         // We reached a special case that requires the addition of a special route step in the
         // beginning. We started in a roundabout, so to announce the exit, we move use the exit
         // instruction and move it right to the beginning to make sure to immediately announce the
@@ -298,6 +268,8 @@ void closeOffRoundabout(const bool on_roundabout,
     // intersections are locations passed along the way
     const auto exit_intersection = steps[step_index].intersections.front();
     const auto exit_bearing = exit_intersection.bearings[exit_intersection.out];
+    const auto destination_name = step.name;
+    const auto destinatino_name_id = step.name_id;
     if (step_index > 1)
     {
         // The very first route-step is head, so we cannot iterate past that one
@@ -308,7 +280,6 @@ void closeOffRoundabout(const bool on_roundabout,
             propagation_step = forwardInto(propagation_step, steps[propagation_index + 1]);
             if (entersRoundabout(propagation_step.maneuver.instruction))
             {
-                propagation_step.maneuver.exit = step.maneuver.exit;
                 const auto entry_intersection = propagation_step.intersections.front();
 
                 // remember rotary name
@@ -332,22 +303,17 @@ void closeOffRoundabout(const bool on_roundabout,
                         ::osrm::util::guidance::getTurnDirection(angle);
                 }
 
-                propagation_step.name = step.name;
-                propagation_step.name_id = step.name_id;
+                propagation_step.name = destination_name;
+                propagation_step.name_id = destinatino_name_id;
+                invalidateStep(steps[propagation_index + 1]);
                 break;
             }
             else
             {
-                BOOST_ASSERT(propagation_step.maneuver.instruction.type ==
-                                 TurnType::StayOnRoundabout ||
-                             propagation_step.maneuver.instruction.type == TurnType::Suppressed ||
-                             propagation_step.maneuver.instruction.type == TurnType::NoTurn);
-                propagation_step.maneuver.instruction =
-                    TurnInstruction::NO_TURN(); // mark intermediate instructions invalid
+                invalidateStep(steps[propagation_index + 1]);
             }
         }
         // remove exit
-        step.maneuver.instruction = TurnInstruction::NO_TURN();
     }
 }
 
@@ -397,9 +363,15 @@ void collapseTurnAt(std::vector<RouteStep> &steps,
 
     const auto &one_back_step = steps[one_back_index];
 
+    // This function assumes driving on the right hand side of the streat
     const auto bearingsAreReversed = [](const double bearing_in, const double bearing_out) {
         // Nearly perfectly reversed angles have a difference close to 180 degrees (straight)
-        return angularDeviation(bearing_in, bearing_out) > 170;
+        const double left_turn_angle = [&]() {
+            if (0 <= bearing_out && bearing_out <= bearing_in)
+                return bearing_in - bearing_out;
+            return bearing_in + 360 - bearing_out;
+        }();
+        return angularDeviation(left_turn_angle, 180) <= 35;
     };
 
     BOOST_ASSERT(!one_back_step.intersections.empty() && !current_step.intersections.empty());
@@ -415,7 +387,9 @@ void collapseTurnAt(std::vector<RouteStep> &steps,
         {
             BOOST_ASSERT(!one_back_step.intersections.empty());
             if (TurnType::Continue == current_step.maneuver.instruction.type ||
-                TurnType::Suppressed == current_step.maneuver.instruction.type)
+                (TurnType::Suppressed == current_step.maneuver.instruction.type &&
+                 current_step.maneuver.instruction.direction_modifier !=
+                     DirectionModifier::Straight))
                 steps[step_index].maneuver.instruction.type = TurnType::Turn;
             else if (TurnType::Merge == current_step.maneuver.instruction.type)
             {
@@ -440,19 +414,31 @@ void collapseTurnAt(std::vector<RouteStep> &steps,
     else if (one_back_step.distance <= MAX_COLLAPSE_DISTANCE &&
              isCollapsableInstruction(current_step.maneuver.instruction))
     {
+        // TODO check for lanes (https://github.com/Project-OSRM/osrm-backend/issues/2553)
         if (compatible(one_back_step, current_step))
         {
             steps[one_back_index] = elongate(std::move(steps[one_back_index]), steps[step_index]);
             if ((TurnType::Continue == one_back_step.maneuver.instruction.type ||
                  TurnType::Suppressed == one_back_step.maneuver.instruction.type) &&
                 current_step.name_id != steps[two_back_index].name_id)
-            {
                 steps[one_back_index].maneuver.instruction.type = TurnType::Turn;
-            }
             else if (TurnType::Turn == one_back_step.maneuver.instruction.type &&
                      current_step.name_id == steps[two_back_index].name_id)
             {
                 steps[one_back_index].maneuver.instruction.type = TurnType::Continue;
+
+                const auto getBearing = [](bool in, const RouteStep &step) {
+                    const auto index =
+                        in ? step.intersections.front().in : step.intersections.front().out;
+                    return step.intersections.front().bearings[index];
+                };
+
+                // If we Merge onto the same street, we end up with a u-turn in some cases
+                if (bearingsAreReversed(
+                        util::bearing::reverseBearing(getBearing(true, one_back_step)),
+                        getBearing(false, current_step)))
+                    steps[one_back_index].maneuver.instruction.direction_modifier =
+                        DirectionModifier::UTurn;
             }
             else if (TurnType::Merge == one_back_step.maneuver.instruction.type &&
                      current_step.maneuver.instruction.type !=
@@ -487,7 +473,8 @@ void collapseTurnAt(std::vector<RouteStep> &steps,
         // additionall collapse a name-change as well
         const bool continues_with_name_change =
             (step_index + 1 < steps.size()) &&
-            isCollapsableInstruction(steps[step_index + 1].maneuver.instruction);
+            (steps[step_index + 1].maneuver.instruction.type == TurnType::UseLane ||
+             isCollapsableInstruction(steps[step_index + 1].maneuver.instruction));
         const bool u_turn_with_name_change =
             continues_with_name_change && steps[step_index + 1].name == steps[two_back_index].name;
 
@@ -513,11 +500,51 @@ void collapseTurnAt(std::vector<RouteStep> &steps,
     }
 }
 
+// Works on steps including silent and invalid instructions in order to do lane anticipation for
+// roundabouts which later on get collapsed into a single multi-hop instruction.
+std::vector<RouteStep> anticipateLaneChangeForRoundabouts(std::vector<RouteStep> steps)
+{
+    using namespace util::guidance;
+
+    using StepIter = decltype(steps)::iterator;
+    using StepIterRange = std::pair<StepIter, StepIter>;
+
+    const auto anticipate_lanes_in_roundabout = [&](StepIterRange roundabout) {
+        // We do lane anticipation on the roundabout's enter and leave step only.
+        // TODO: This means, lanes _inside_ the roundabout are ignored at the moment.
+
+        auto enter = *roundabout.first;
+        const auto leave = *roundabout.second;
+
+        // Although the enter instruction may be a left/right turn, for right-sided driving the
+        // roundabout is counter-clockwise and therefore we need to always set it to a left turn.
+        // FIXME: assumes right-side driving (counter-clockwise roundabout flow)
+        const auto enter_direction = enter.maneuver.instruction.direction_modifier;
+
+        if (util::guidance::isRightTurn(enter.maneuver.instruction))
+            enter.maneuver.instruction.direction_modifier =
+                mirrorDirectionModifier(enter_direction);
+
+        // a roundabout is a continuous maneuver. We don't switch lanes within a roundabout, as long
+        // as it can be avoided.
+        auto enterAndLeave =
+            anticipateLaneChange({enter, leave}, std::numeric_limits<double>::max());
+
+        // Undo flipping direction on a right turn in a right-sided counter-clockwise roundabout.
+        // FIXME: assumes right-side driving (counter-clockwise roundabout flow)
+        enterAndLeave[0].maneuver.instruction.direction_modifier = enter_direction;
+
+        std::swap(*roundabout.first, enterAndLeave[0]);
+        std::swap(*roundabout.second, enterAndLeave[1]);
+    };
+
+    forEachRoundabout(begin(steps), end(steps), anticipate_lanes_in_roundabout);
+    return steps;
+}
 } // namespace
 
 // Post processing can invalidate some instructions. For example StayOnRoundabout
 // is turned into exit counts. These instructions are removed by the following function
-
 std::vector<RouteStep> removeNoTurnInstructions(std::vector<RouteStep> steps)
 {
     // finally clean up the post-processed instructions.
@@ -533,6 +560,9 @@ std::vector<RouteStep> removeNoTurnInstructions(std::vector<RouteStep> steps)
 
     boost::remove_erase_if(steps, not_is_valid);
 
+    // the steps should still include depart and arrive at least
+    BOOST_ASSERT(steps.size() >= 2);
+
     BOOST_ASSERT(steps.front().intersections.size() >= 1);
     BOOST_ASSERT(steps.front().intersections.front().bearings.size() == 1);
     BOOST_ASSERT(steps.front().intersections.front().entry.size() == 1);
@@ -559,6 +589,11 @@ std::vector<RouteStep> postProcess(std::vector<RouteStep> steps)
     if (steps.size() == 2)
         return steps;
 
+    // Before we invalidate and remove silent instructions, we handle roundabouts (before they're
+    // getting collapsed into a single multi-hop instruction) by back-propagating exit lane
+    // constraints already to a roundabout's enter instruction.
+    steps = anticipateLaneChangeForRoundabouts(std::move(steps));
+
     // Count Street Exits forward
     bool on_roundabout = false;
     bool has_entered_roundabout = false;
@@ -566,7 +601,7 @@ std::vector<RouteStep> postProcess(std::vector<RouteStep> steps)
     // count the exits forward. if enter/exit roundabout happen both, no further treatment is
     // required. We might end up with only one of them (e.g. starting within a roundabout)
     // or having a via-point in the roundabout.
-    // In this case, exits are numbered from the start of the lag.
+    // In this case, exits are numbered from the start of the leg.
     for (std::size_t step_index = 0; step_index < steps.size(); ++step_index)
     {
         auto &step = steps[step_index];
@@ -596,6 +631,10 @@ std::vector<RouteStep> postProcess(std::vector<RouteStep> steps)
             has_entered_roundabout = false;
             on_roundabout = false;
         }
+        else if (on_roundabout && step_index + 1 < steps.size())
+        {
+            steps[step_index + 1].maneuver.exit = step.maneuver.exit;
+        }
     }
 
     // unterminated roundabout
@@ -874,6 +913,8 @@ void trimShortSegments(std::vector<RouteStep> &steps, LegGeometry &geometry)
             designated_depart.maneuver.instruction = TurnInstruction::NO_TURN();
             // we need to make this conform with the intersection format for the first intersection
             auto &first_intersection = designated_depart.intersections.front();
+            designated_depart.intersections.front().lanes = util::guidance::LaneTupel();
+            designated_depart.intersections.front().lane_description.clear();
             first_intersection.bearings = {first_intersection.bearings[first_intersection.out]};
             first_intersection.entry = {true};
             first_intersection.in = Intersection::NO_INDEX;
@@ -940,6 +981,8 @@ void trimShortSegments(std::vector<RouteStep> &steps, LegGeometry &geometry)
         next_to_last_step.maneuver.waypoint_type = WaypointType::Arrive;
         next_to_last_step.maneuver.instruction = TurnInstruction::NO_TURN();
         next_to_last_step.maneuver.bearing_after = 0;
+        next_to_last_step.intersections.front().lanes = util::guidance::LaneTupel();
+        next_to_last_step.intersections.front().lane_description.clear();
         BOOST_ASSERT(next_to_last_step.intersections.size() == 1);
         auto &last_intersection = next_to_last_step.intersections.back();
         last_intersection.bearings = {last_intersection.bearings[last_intersection.in]};
@@ -1084,6 +1127,22 @@ std::vector<RouteStep> buildIntersections(std::vector<RouteStep> steps)
         }
         else if (!isSilent(instruction))
         {
+
+            // End of road is a turn that helps to identify the location of a turn. If the turn does
+            // not pass by any oter intersections, the end-of-road characteristic does not improve
+            // the instructions.
+            // Here we reduce the verbosity of our output by reducing end-of-road emissions in cases
+            // where no intersections have been passed in between.
+            // Since the instruction is located at the beginning of a step, we need to check the
+            // previous instruction.
+            if (instruction.type == TurnType::EndOfRoad)
+            {
+                BOOST_ASSERT(step_index > 0 && step_index + 1 < steps.size());
+                const auto &previous_step = steps[last_valid_instruction];
+                if (previous_step.intersections.size() < MIN_END_OF_ROAD_INTERSECTIONS)
+                    step.maneuver.instruction.type = TurnType::Turn;
+            }
+
             // Remember the last non silent instruction
             last_valid_instruction = step_index;
         }
@@ -1091,6 +1150,60 @@ std::vector<RouteStep> buildIntersections(std::vector<RouteStep> steps)
     return removeNoTurnInstructions(std::move(steps));
 }
 
+std::vector<RouteStep> collapseUseLane(std::vector<RouteStep> steps)
+{
+    const auto containsTag = [](const extractor::guidance::TurnLaneType::Mask mask,
+                                const extractor::guidance::TurnLaneType::Mask tag) {
+        return (mask & tag) != extractor::guidance::TurnLaneType::empty;
+    };
+
+    const auto getPreviousIndex = [&steps](std::size_t index) {
+        BOOST_ASSERT(index > 0);
+        BOOST_ASSERT(index < steps.size());
+        --index;
+        while (index > 0 && steps[index].maneuver.instruction.type == TurnType::NoTurn)
+            --index;
+
+        return index;
+    };
+
+    const auto canCollapeUseLane =
+        [containsTag](const util::guidance::LaneTupel lanes,
+                      extractor::guidance::TurnLaneDescription lane_description) {
+            // the lane description is given left to right, lanes are counted from the right.
+            // Therefore we access the lane description yousing the reverse iterator
+            if (lanes.first_lane_from_the_right > 0 &&
+                containsTag(*(lane_description.rbegin() + (lanes.first_lane_from_the_right - 1)),
+                            (extractor::guidance::TurnLaneType::straight |
+                             extractor::guidance::TurnLaneType::none)))
+                return false;
+
+            const auto lane_to_the_right = lanes.first_lane_from_the_right + lanes.lanes_in_turn;
+            if (lane_to_the_right < boost::numeric_cast<int>(lane_description.size()) &&
+                containsTag(*(lane_description.rbegin() + lane_to_the_right),
+                            (extractor::guidance::TurnLaneType::straight |
+                             extractor::guidance::TurnLaneType::none)))
+                return false;
+
+            return true;
+        };
+
+    for (std::size_t step_index = 1; step_index < steps.size(); ++step_index)
+    {
+        const auto &step = steps[step_index];
+        if (step.maneuver.instruction.type == TurnType::UseLane &&
+            canCollapeUseLane(step.intersections.front().lanes,
+                              step.intersections.front().lane_description))
+        {
+            const auto previous = getPreviousIndex(step_index);
+            steps[previous] = elongate(steps[previous], steps[step_index]);
+            //elongate(steps[step_index-1], steps[step_index]);
+            invalidateStep(steps[step_index]);
+        }
+    }
+    return removeNoTurnInstructions(std::move(steps));
+}
+
 } // namespace guidance
 } // namespace engine
 } // namespace osrm
diff --git a/src/engine/plugins/tile.cpp b/src/engine/plugins/tile.cpp
index 4f1ce64..3877b07 100644
--- a/src/engine/plugins/tile.cpp
+++ b/src/engine/plugins/tile.cpp
@@ -128,7 +128,7 @@ FixedLine coordinatesToTileLine(const util::Coordinate start,
     for (auto const &pt : geo_line)
     {
         double px_merc = pt.x * util::web_mercator::DEGREE_TO_PX;
-        double py_merc = util::web_mercator::latToY(util::FloatLatitude(pt.y)) *
+        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(
@@ -174,8 +174,8 @@ Status TilePlugin::HandleRequest(const api::TileParameters &parameters, std::str
     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)};
+    util::Coordinate southwest{util::FloatLongitude{min_lon}, util::FloatLatitude{min_lat}};
+    util::Coordinate northeast{util::FloatLongitude{max_lon}, util::FloatLatitude{max_lat}};
 
     // Fetch all the segments that are in our bounding box.
     // This hits the OSRM StaticRTree
@@ -244,8 +244,8 @@ Status TilePlugin::HandleRequest(const api::TileParameters &parameters, std::str
 
         if (name_offsets.find(name) == name_offsets.end())
         {
-          names.push_back(name);
-          name_offsets[name] = names.size() - 1;
+            names.push_back(name);
+            name_offsets[name] = names.size() - 1;
         }
     }
 
@@ -328,14 +328,17 @@ Status TilePlugin::HandleRequest(const api::TileParameters &parameters, std::str
                 max_datasource_id = std::max(max_datasource_id, forward_datasource);
                 max_datasource_id = std::max(max_datasource_id, reverse_datasource);
 
-                const auto encode_tile_line = [&layer_writer, &edge, &id, &max_datasource_id, &used_weights](
-                    const detail::FixedLine &tile_line,
-                    const std::uint32_t speed_kmh,
-                    const std::size_t duration,
-                    const std::uint8_t datasource,
-                    const std::size_t name,
-                    std::int32_t &start_x,
-                    std::int32_t &start_y) {
+                const auto encode_tile_line = [&layer_writer,
+                                               &edge,
+                                               &id,
+                                               &max_datasource_id,
+                                               &used_weights](const detail::FixedLine &tile_line,
+                                                              const std::uint32_t speed_kmh,
+                                                              const std::size_t duration,
+                                                              const std::uint8_t datasource,
+                                                              const std::size_t name,
+                                                              std::int32_t &start_x,
+                                                              std::int32_t &start_y) {
                     // Here, we save the two attributes for our feature: the speed and the
                     // is_small
                     // boolean.  We onl serve up speeds from 0-139, so all we do is save the
@@ -369,10 +372,10 @@ Status TilePlugin::HandleRequest(const api::TileParameters &parameters, std::str
                         field.add_element(3);                // "duration" tag key offset
                         field.add_element(130 + max_datasource_id + 1 +
                                           duration); // duration value offset
-                        field.add_element(4); // "name" tag key offset
+                        field.add_element(4);        // "name" tag key offset
 
-                        field.add_element(130 + max_datasource_id + 1 +
-                                          used_weights.size() + name); // name value offset
+                        field.add_element(130 + max_datasource_id + 1 + used_weights.size() +
+                                          name); // name value offset
                     }
                     {
 
@@ -479,7 +482,7 @@ Status TilePlugin::HandleRequest(const api::TileParameters &parameters, std::str
             values_writer.add_double(util::vector_tile::VARIANT_TYPE_DOUBLE, weight / 10.);
         }
 
-        for (const auto& name : names)
+        for (const auto &name : names)
         {
             // Writing field type 4 == variant type
             protozero::pbf_writer values_writer(layer_writer, util::vector_tile::VARIANT_TAG);
diff --git a/src/engine/polyline_compressor.cpp b/src/engine/polyline_compressor.cpp
index 906ccb4..701dac0 100644
--- a/src/engine/polyline_compressor.cpp
+++ b/src/engine/polyline_compressor.cpp
@@ -116,8 +116,8 @@ std::vector<util::Coordinate> decodePolyline(const std::string &geometry_string)
         lng += dlng;
 
         util::Coordinate p;
-        p.lat = util::FixedLatitude(lat * detail::POLYLINE_TO_COORDINATE);
-        p.lon = util::FixedLongitude(lng * detail::POLYLINE_TO_COORDINATE);
+        p.lat = util::FixedLatitude{static_cast<std::int32_t>(lat * detail::POLYLINE_TO_COORDINATE)};
+        p.lon = util::FixedLongitude{static_cast<std::int32_t>(lng * detail::POLYLINE_TO_COORDINATE)};
         new_coordinates.push_back(p);
     }
 
diff --git a/src/extractor/edge_based_graph_factory.cpp b/src/extractor/edge_based_graph_factory.cpp
index a9c4d3b..d7182f0 100644
--- a/src/extractor/edge_based_graph_factory.cpp
+++ b/src/extractor/edge_based_graph_factory.cpp
@@ -1,5 +1,5 @@
-#include "extractor/edge_based_graph_factory.hpp"
 #include "extractor/edge_based_edge.hpp"
+#include "extractor/edge_based_graph_factory.hpp"
 #include "util/coordinate.hpp"
 #include "util/coordinate_calculation.hpp"
 #include "util/exception.hpp"
@@ -11,6 +11,7 @@
 
 #include "extractor/guidance/toolkit.hpp"
 #include "extractor/guidance/turn_analysis.hpp"
+#include "extractor/guidance/turn_lane_handler.hpp"
 #include "extractor/suffix_table.hpp"
 
 #include <boost/assert.hpp>
@@ -39,12 +40,15 @@ EdgeBasedGraphFactory::EdgeBasedGraphFactory(
     std::shared_ptr<const RestrictionMap> restriction_map,
     const std::vector<QueryNode> &node_info_list,
     ProfileProperties profile_properties,
-    const util::NameTable &name_table)
+    const util::NameTable &name_table,
+    const std::vector<std::uint32_t> &turn_lane_offsets,
+    const std::vector<guidance::TurnLaneType::Mask> &turn_lane_masks)
     : m_max_edge_id(0), m_node_info_list(node_info_list),
       m_node_based_graph(std::move(node_based_graph)),
       m_restriction_map(std::move(restriction_map)), m_barrier_nodes(barrier_nodes),
       m_traffic_lights(traffic_lights), m_compressed_edge_container(compressed_edge_container),
-      profile_properties(std::move(profile_properties)), name_table(name_table)
+      profile_properties(std::move(profile_properties)), name_table(name_table),
+      turn_lane_offsets(turn_lane_offsets), turn_lane_masks(turn_lane_masks)
 {
 }
 
@@ -83,7 +87,7 @@ void EdgeBasedGraphFactory::GetEdgeBasedNodeWeights(std::vector<EdgeWeight> &out
     swap(m_edge_based_node_weights, output_node_weights);
 }
 
-unsigned EdgeBasedGraphFactory::GetHighestEdgeID() { return m_max_edge_id; }
+EdgeID EdgeBasedGraphFactory::GetHighestEdgeID() { return m_max_edge_id; }
 
 void EdgeBasedGraphFactory::InsertEdgeBasedNode(const NodeID node_u, const NodeID node_v)
 {
@@ -179,6 +183,7 @@ void EdgeBasedGraphFactory::FlushVectorToStream(
 }
 
 void EdgeBasedGraphFactory::Run(const std::string &original_edge_data_filename,
+                                const std::string &turn_lane_data_filename,
                                 lua_State *lua_state,
                                 const std::string &edge_segment_lookup_filename,
                                 const std::string &edge_penalty_filename,
@@ -195,6 +200,7 @@ void EdgeBasedGraphFactory::Run(const std::string &original_edge_data_filename,
 
     TIMER_START(generate_edges);
     GenerateEdgeExpandedEdges(original_edge_data_filename,
+                              turn_lane_data_filename,
                               lua_state,
                               edge_segment_lookup_filename,
                               edge_penalty_filename,
@@ -293,6 +299,7 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedNodes()
 /// Actually it also generates OriginalEdgeData and serializes them...
 void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
     const std::string &original_edge_data_filename,
+    const std::string &turn_lane_data_filename,
     lua_State *lua_state,
     const std::string &edge_segment_lookup_filename,
     const std::string &edge_fixed_penalties_filename,
@@ -339,10 +346,13 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
                                          m_compressed_edge_container,
                                          name_table,
                                          street_name_suffix_table);
+    guidance::lanes::TurnLaneHandler turn_lane_handler(
+        *m_node_based_graph, turn_lane_offsets, turn_lane_masks, m_node_info_list, turn_analysis);
 
     bearing_class_by_node_based_node.resize(m_node_based_graph->GetNumberOfNodes(),
                                             std::numeric_limits<std::uint32_t>::max());
 
+    guidance::LaneDataIdMap lane_data_map;
     for (const auto node_u : util::irange(0u, m_node_based_graph->GetNumberOfNodes()))
     {
         progress.PrintStatus(node_u);
@@ -353,19 +363,23 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
                 continue;
             }
 
+            const NodeID node_v = m_node_based_graph->GetTarget(edge_from_u);
             ++node_based_edge_counter;
-            auto possible_turns = turn_analysis.getTurns(node_u, edge_from_u);
+            auto intersection = turn_analysis.getIntersection(node_u, edge_from_u);
+            intersection =
+                turn_analysis.assignTurnTypes(node_u, edge_from_u, std::move(intersection));
 
-            const NodeID node_v = m_node_based_graph->GetTarget(edge_from_u);
+            intersection = turn_lane_handler.assignTurnLanes(
+                node_u, edge_from_u, std::move(intersection), lane_data_map);
+            const auto possible_turns = turn_analysis.transformIntersectionIntoTurns(intersection);
 
             // the entry class depends on the turn, so we have to classify the interesction for
             // every edge
-            const auto turn_classification =
-                classifyIntersection(node_v,
-                                     turn_analysis.getIntersection(node_u, edge_from_u),
-                                     *m_node_based_graph,
-                                     m_compressed_edge_container,
-                                     m_node_info_list);
+            const auto turn_classification = classifyIntersection(node_v,
+                                                                  intersection,
+                                                                  *m_node_based_graph,
+                                                                  m_compressed_edge_container,
+                                                                  m_node_info_list);
 
             const auto entry_class_id = [&](const util::guidance::EntryClass entry_class) {
                 if (0 == entry_class_hash.count(entry_class))
@@ -428,6 +442,7 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
                 original_edge_data_vector.emplace_back(
                     m_compressed_edge_container.GetPositionForID(edge_from_u),
                     edge_data1.name_id,
+                    turn.lane_data_id,
                     turn_instruction,
                     entry_class_id,
                     edge_data1.travel_mode);
@@ -465,19 +480,17 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
                 // updates to the edge-expanded-edge based directly on its ID.
                 if (generate_edge_lookup)
                 {
-                    unsigned fixed_penalty = distance - edge_data1.distance;
-                    edge_penalty_file.write(reinterpret_cast<const char *>(&fixed_penalty),
-                                            sizeof(fixed_penalty));
                     const auto node_based_edges =
                         m_compressed_edge_container.GetBucketReference(edge_from_u);
                     NodeID previous = node_u;
 
                     const unsigned node_count = node_based_edges.size() + 1;
-                    edge_segment_file.write(reinterpret_cast<const char *>(&node_count),
-                                            sizeof(node_count));
                     const QueryNode &first_node = m_node_info_list[previous];
-                    edge_segment_file.write(reinterpret_cast<const char *>(&first_node.node_id),
-                                            sizeof(first_node.node_id));
+
+                    lookup::SegmentHeaderBlock header = {node_count, first_node.node_id};
+
+                    edge_segment_file.write(reinterpret_cast<const char *>(&header),
+                                            sizeof(header));
 
                     for (auto target_node : node_based_edges)
                     {
@@ -486,12 +499,11 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
                         const double segment_length =
                             util::coordinate_calculation::greatCircleDistance(from, to);
 
-                        edge_segment_file.write(reinterpret_cast<const char *>(&to.node_id),
-                                                sizeof(to.node_id));
-                        edge_segment_file.write(reinterpret_cast<const char *>(&segment_length),
-                                                sizeof(segment_length));
-                        edge_segment_file.write(reinterpret_cast<const char *>(&target_node.weight),
-                                                sizeof(target_node.weight));
+                        lookup::SegmentBlock nodeblock = {
+                            to.node_id, segment_length, target_node.weight};
+
+                        edge_segment_file.write(reinterpret_cast<const char *>(&nodeblock),
+                                                sizeof(nodeblock));
                         previous = target_node.node_id;
                     }
 
@@ -518,12 +530,11 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
                         m_node_info_list[m_compressed_edge_container.GetFirstEdgeTargetID(
                             turn.eid)];
 
-                    edge_penalty_file.write(reinterpret_cast<const char *>(&from_node.node_id),
-                                            sizeof(from_node.node_id));
-                    edge_penalty_file.write(reinterpret_cast<const char *>(&via_node.node_id),
-                                            sizeof(via_node.node_id));
-                    edge_penalty_file.write(reinterpret_cast<const char *>(&to_node.node_id),
-                                            sizeof(to_node.node_id));
+                    const unsigned fixed_penalty = distance - edge_data1.distance;
+                    lookup::PenaltyBlock penaltyblock = {
+                        fixed_penalty, from_node.node_id, via_node.node_id, to_node.node_id};
+                    edge_penalty_file.write(reinterpret_cast<const char *>(&penaltyblock),
+                                            sizeof(penaltyblock));
                 }
             }
         }
@@ -532,6 +543,22 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
     util::SimpleLogger().Write() << "Created " << entry_class_hash.size() << " entry classes and "
                                  << bearing_class_hash.size() << " Bearing Classes";
 
+    util::SimpleLogger().Write() << "Writing Turn Lane Data to File...";
+    std::ofstream turn_lane_data_file(turn_lane_data_filename.c_str(), std::ios::binary);
+    std::vector<util::guidance::LaneTupelIdPair> lane_data(lane_data_map.size());
+    // extract lane data sorted by ID
+    for (auto itr : lane_data_map)
+        lane_data[itr.second] = itr.first;
+
+    std::uint64_t size = lane_data.size();
+    turn_lane_data_file.write(reinterpret_cast<const char *>(&size), sizeof(size));
+
+    if (!lane_data.empty())
+        turn_lane_data_file.write(reinterpret_cast<const char *>(&lane_data[0]),
+                                  sizeof(util::guidance::LaneTupelIdPair) * lane_data.size());
+
+    util::SimpleLogger().Write() << "done.";
+
     FlushVectorToStream(edge_data_file, original_edge_data_vector);
 
     // Finally jump back to the empty space at the beginning and write length prefix
diff --git a/src/extractor/extraction_containers.cpp b/src/extractor/extraction_containers.cpp
index 138552a..908083f 100644
--- a/src/extractor/extraction_containers.cpp
+++ b/src/extractor/extraction_containers.cpp
@@ -6,6 +6,7 @@
 
 #include "util/exception.hpp"
 #include "util/fingerprint.hpp"
+#include "util/io.hpp"
 #include "util/lua_util.hpp"
 #include "util/simple_logger.hpp"
 #include "util/timing_util.hpp"
@@ -25,6 +26,8 @@
 
 namespace
 {
+namespace oe = osrm::extractor;
+
 // Needed for STXXL comparison - STXXL requires max_value(), min_value(), so we can not use
 // std::less<OSMNodeId>{}. Anonymous namespace to keep translation unit local.
 struct OSMNodeIDSTXXLLess
@@ -34,6 +37,69 @@ struct OSMNodeIDSTXXLLess
     value_type max_value() { return MAX_OSM_NODEID; }
     value_type min_value() { return MIN_OSM_NODEID; }
 };
+
+struct CmpEdgeByOSMStartID
+{
+    using value_type = oe::InternalExtractorEdge;
+    bool operator()(const value_type &lhs, const value_type &rhs) const
+    {
+        return lhs.result.osm_source_id < rhs.result.osm_source_id;
+    }
+
+    value_type max_value() { return value_type::max_osm_value(); }
+    value_type min_value() { return value_type::min_osm_value(); }
+};
+
+struct CmpEdgeByOSMTargetID
+{
+    using value_type = oe::InternalExtractorEdge;
+    bool operator()(const value_type &lhs, const value_type &rhs) const
+    {
+        return lhs.result.osm_target_id < rhs.result.osm_target_id;
+    }
+
+    value_type max_value() { return value_type::max_osm_value(); }
+    value_type min_value() { return value_type::min_osm_value(); }
+};
+
+struct CmpEdgeByInternalSourceTargetAndName
+{
+    using value_type = oe::InternalExtractorEdge;
+    bool operator()(const value_type &lhs, const value_type &rhs) const
+    {
+        if (lhs.result.source != rhs.result.source)
+            return lhs.result.source < rhs.result.source;
+
+        if (lhs.result.target != rhs.result.target)
+            return lhs.result.target < rhs.result.target;
+
+        if (lhs.result.source == SPECIAL_NODEID)
+            return false;
+
+        if (lhs.result.name_id == rhs.result.name_id)
+            return false;
+
+        if (lhs.result.name_id == EMPTY_NAMEID)
+            return false;
+
+        if (rhs.result.name_id == EMPTY_NAMEID)
+            return true;
+
+        BOOST_ASSERT(!name_offsets.empty() && name_offsets.back() == name_data.size());
+        const oe::ExtractionContainers::STXXLNameCharData::const_iterator data = name_data.begin();
+        return std::lexicographical_compare(data + name_offsets[lhs.result.name_id],
+                                            data + name_offsets[lhs.result.name_id + 1],
+                                            data + name_offsets[rhs.result.name_id],
+                                            data + name_offsets[rhs.result.name_id + 1]);
+    }
+
+    value_type max_value() { return value_type::max_internal_value(); }
+    value_type min_value() { return value_type::min_internal_value(); }
+
+    const oe::ExtractionContainers::STXXLNameCharData &name_data;
+    const oe::ExtractionContainers::STXXLNameOffsets &name_offsets;
+};
+
 }
 
 namespace osrm
@@ -47,10 +113,18 @@ ExtractionContainers::ExtractionContainers()
 {
     // Check if stxxl can be instantiated
     stxxl::vector<unsigned> dummy_vector;
-    // Insert three empty strings for name, destination and pronunciation
-    name_lengths.push_back(0);
-    name_lengths.push_back(0);
-    name_lengths.push_back(0);
+
+    // Insert three empty strings offsets for name, destination and pronunciation
+    name_offsets.push_back(0);
+    name_offsets.push_back(0);
+    name_offsets.push_back(0);
+    // Insert the total length sentinel (corresponds to the next name string offset)
+    name_offsets.push_back(0);
+
+    // the offsets have to be initialized with two values, since we have the empty turn string for
+    // the first id
+    turn_lane_offsets.push_back(0);
+    turn_lane_offsets.push_back(0);
 }
 
 /**
@@ -66,6 +140,7 @@ ExtractionContainers::ExtractionContainers()
 void ExtractionContainers::PrepareData(const std::string &output_file_name,
                                        const std::string &restrictions_file_name,
                                        const std::string &name_file_name,
+                                       const std::string &turn_lane_file_name,
                                        lua_State *segment_state)
 {
     try
@@ -83,7 +158,8 @@ void ExtractionContainers::PrepareData(const std::string &output_file_name,
         PrepareRestrictions();
         WriteRestrictions(restrictions_file_name);
 
-        WriteNames(name_file_name);
+        WriteCharData(name_file_name);
+        WriteTurnLaneMasks(turn_lane_file_name, turn_lane_offsets, turn_lane_masks);
     }
     catch (const std::exception &e)
     {
@@ -91,24 +167,55 @@ void ExtractionContainers::PrepareData(const std::string &output_file_name,
     }
 }
 
-void ExtractionContainers::WriteNames(const std::string &names_file_name) const
+void ExtractionContainers::WriteTurnLaneMasks(
+    const std::string &file_name,
+    const stxxl::vector<std::uint32_t> &offsets,
+    const stxxl::vector<guidance::TurnLaneType::Mask> &masks) const
 {
-    std::cout << "[extractor] writing street name index ... " << std::flush;
-    TIMER_START(write_name_index);
-    boost::filesystem::ofstream name_file_stream(names_file_name, std::ios::binary);
+    util::SimpleLogger().Write() << "Writing turn lane masks...";
+    TIMER_START(turn_lane_timer);
+
+    std::ofstream ofs(file_name, std::ios::binary);
+
+    if (!util::serializeVector(ofs, offsets))
+    {
+        util::SimpleLogger().Write(logWARNING) << "Error while writing.";
+        return;
+    }
+
+    if (!util::serializeVector(ofs, masks))
+    {
+        util::SimpleLogger().Write(logWARNING) << "Error while writing.";
+        return;
+    }
 
-    unsigned total_length = 0;
+    TIMER_STOP(turn_lane_timer);
+    util::SimpleLogger().Write() << "done (" << TIMER_SEC(turn_lane_timer) << ")";
+}
 
-    for (const auto name_length : name_lengths)
+void ExtractionContainers::WriteCharData(const std::string &file_name)
+{
+    std::cout << "[extractor] writing street name index ... " << std::flush;
+    TIMER_START(write_index);
+    boost::filesystem::ofstream file_stream(file_name, std::ios::binary);
+
+    // transforms in-place name offsets to name lengths
+    BOOST_ASSERT(!name_offsets.empty());
+    for (auto curr = name_offsets.begin(), next = name_offsets.begin() + 1;
+         next != name_offsets.end(); ++curr, ++next)
     {
-        total_length += name_length;
+        *curr = *next - *curr;
     }
 
+    // removes the total length sentinel
+    unsigned total_length = name_offsets.back();
+    name_offsets.pop_back();
+
     // builds and writes the index
-    util::RangeTable<> name_index_range(name_lengths);
-    name_file_stream << name_index_range;
+    util::RangeTable<> index_range(name_offsets);
+    file_stream << index_range;
 
-    name_file_stream.write((char *)&total_length, sizeof(unsigned));
+    file_stream.write((char *)&total_length, sizeof(unsigned));
 
     // write all chars consecutively
     char write_buffer[WRITE_BLOCK_BUFFER_SIZE];
@@ -120,15 +227,15 @@ void ExtractionContainers::WriteNames(const std::string &names_file_name) const
 
         if (buffer_len >= WRITE_BLOCK_BUFFER_SIZE)
         {
-            name_file_stream.write(write_buffer, WRITE_BLOCK_BUFFER_SIZE);
+            file_stream.write(write_buffer, WRITE_BLOCK_BUFFER_SIZE);
             buffer_len = 0;
         }
     }
 
-    name_file_stream.write(write_buffer, buffer_len);
+    file_stream.write(write_buffer, buffer_len);
 
-    TIMER_STOP(write_name_index);
-    std::cout << "ok, after " << TIMER_SEC(write_name_index) << "s" << std::endl;
+    TIMER_STOP(write_index);
+    std::cout << "ok, after " << TIMER_SEC(write_index) << "s" << std::endl;
 }
 
 void ExtractionContainers::PrepareNodes()
@@ -167,7 +274,7 @@ void ExtractionContainers::PrepareNodes()
     // handle > uint32_t actual usable nodes.  This should be OK for a while
     // because we usually route on a *lot* less than 2^32 of the OSM
     // graph nodes.
-    std::size_t internal_id = 0;
+    std::uint64_t internal_id = 0;
 
     // compute the intersection of nodes that were referenced and nodes we actually have
     while (node_iter != all_nodes_list_end && ref_iter != used_node_id_list_end)
@@ -308,9 +415,9 @@ void ExtractionContainers::PrepareEdges(lua_State *segment_state)
         BOOST_ASSERT(edge_iterator->result.osm_target_id == node_iterator->node_id);
         BOOST_ASSERT(edge_iterator->weight_data.speed >= 0);
         BOOST_ASSERT(edge_iterator->source_coordinate.lat !=
-                     util::FixedLatitude(std::numeric_limits<int>::min()));
+                     util::FixedLatitude{std::numeric_limits<std::int32_t>::min()});
         BOOST_ASSERT(edge_iterator->source_coordinate.lon !=
-                     util::FixedLongitude(std::numeric_limits<int>::min()));
+                     util::FixedLongitude{std::numeric_limits<std::int32_t>::min()});
 
         const double distance = util::coordinate_calculation::greatCircleDistance(
             edge_iterator->source_coordinate,
@@ -380,7 +487,7 @@ void ExtractionContainers::PrepareEdges(lua_State *segment_state)
     TIMER_START(sort_edges_by_renumbered_start);
     stxxl::sort(all_edges_list.begin(),
                 all_edges_list.end(),
-                CmpEdgeByInternalStartThenInternalTargetID(),
+                CmpEdgeByInternalSourceTargetAndName{name_char_data, name_offsets},
                 stxxl_memory);
     TIMER_STOP(sort_edges_by_renumbered_start);
     std::cout << "ok, after " << TIMER_SEC(sort_edges_by_renumbered_start) << "s" << std::endl;
@@ -417,11 +524,13 @@ void ExtractionContainers::PrepareEdges(lua_State *segment_state)
                 all_edges_list[i].result.weight < min_forward_weight)
             {
                 min_forward_idx = i;
+                min_forward_weight = all_edges_list[i].result.weight;
             }
             if (all_edges_list[i].result.backward &&
                 all_edges_list[i].result.weight < min_backward_weight)
             {
                 min_backward_idx = i;
+                min_backward_weight = all_edges_list[i].result.weight;
             }
 
             // this also increments the outer loop counter!
@@ -479,11 +588,11 @@ void ExtractionContainers::WriteEdges(std::ofstream &file_out_stream) const
     std::cout << "[extractor] Writing used edges       ... " << std::flush;
     TIMER_START(write_edges);
     // Traverse list of edges and nodes in parallel and set target coord
-    std::size_t used_edges_counter = 0;
-    unsigned used_edges_counter_buffer = 0;
+    std::uint64_t used_edges_counter = 0;
+    std::uint32_t used_edges_counter_buffer = 0;
 
     auto start_position = file_out_stream.tellp();
-    file_out_stream.write((char *)&used_edges_counter_buffer, sizeof(unsigned));
+    file_out_stream.write((char *)&used_edges_counter_buffer, sizeof(used_edges_counter_buffer));
 
     for (const auto &edge : all_edges_list)
     {
@@ -508,10 +617,10 @@ void ExtractionContainers::WriteEdges(std::ofstream &file_out_stream) const
 
     std::cout << "[extractor] setting number of edges   ... " << std::flush;
 
-    used_edges_counter_buffer = boost::numeric_cast<unsigned>(used_edges_counter);
+    used_edges_counter_buffer = boost::numeric_cast<std::uint32_t>(used_edges_counter);
 
     file_out_stream.seekp(start_position);
-    file_out_stream.write((char *)&used_edges_counter_buffer, sizeof(unsigned));
+    file_out_stream.write((char *)&used_edges_counter_buffer, sizeof(used_edges_counter_buffer));
     std::cout << "ok" << std::endl;
 
     util::SimpleLogger().Write() << "Processed " << used_edges_counter << " edges";
@@ -616,14 +725,14 @@ void ExtractionContainers::PrepareRestrictions()
            restrictions_iterator != restrictions_list_end)
     {
         if (way_start_and_end_iterator->way_id <
-            OSMWayID(restrictions_iterator->restriction.from.way))
+            OSMWayID{static_cast<std::uint32_t>(restrictions_iterator->restriction.from.way)})
         {
             ++way_start_and_end_iterator;
             continue;
         }
 
         if (way_start_and_end_iterator->way_id >
-            OSMWayID(restrictions_iterator->restriction.from.way))
+            OSMWayID{static_cast<std::uint32_t>(restrictions_iterator->restriction.from.way)})
         {
             util::SimpleLogger().Write(LogLevel::logWARNING)
                 << "Restriction references invalid way: "
@@ -634,9 +743,9 @@ void ExtractionContainers::PrepareRestrictions()
         }
 
         BOOST_ASSERT(way_start_and_end_iterator->way_id ==
-                     OSMWayID(restrictions_iterator->restriction.from.way));
+                     OSMWayID{static_cast<std::uint32_t>(restrictions_iterator->restriction.from.way)});
         // we do not remap the via id yet, since we will need it for the to node as well
-        const OSMNodeID via_node_id = OSMNodeID(restrictions_iterator->restriction.via.node);
+        const OSMNodeID via_node_id = OSMNodeID{restrictions_iterator->restriction.via.node};
 
         // check if via is actually valid, if not invalidate
         auto via_id_iter = external_to_internal_node_id_map.find(via_node_id);
@@ -650,11 +759,11 @@ void ExtractionContainers::PrepareRestrictions()
             continue;
         }
 
-        if (OSMNodeID(way_start_and_end_iterator->first_segment_source_id) == via_node_id)
+        if (way_start_and_end_iterator->first_segment_source_id == via_node_id)
         {
             // assign new from node id
             auto id_iter = external_to_internal_node_id_map.find(
-                OSMNodeID(way_start_and_end_iterator->first_segment_target_id));
+                way_start_and_end_iterator->first_segment_target_id);
             if (id_iter == external_to_internal_node_id_map.end())
             {
                 util::SimpleLogger().Write(LogLevel::logWARNING)
@@ -667,11 +776,11 @@ void ExtractionContainers::PrepareRestrictions()
             }
             restrictions_iterator->restriction.from.node = id_iter->second;
         }
-        else if (OSMNodeID(way_start_and_end_iterator->last_segment_target_id) == via_node_id)
+        else if (way_start_and_end_iterator->last_segment_target_id == via_node_id)
         {
             // assign new from node id
             auto id_iter = external_to_internal_node_id_map.find(
-                OSMNodeID(way_start_and_end_iterator->last_segment_source_id));
+                way_start_and_end_iterator->last_segment_source_id);
             if (id_iter == external_to_internal_node_id_map.end())
             {
                 util::SimpleLogger().Write(LogLevel::logWARNING)
@@ -710,7 +819,7 @@ void ExtractionContainers::PrepareRestrictions()
            restrictions_iterator != restrictions_list_end_)
     {
         if (way_start_and_end_iterator->way_id <
-            OSMWayID(restrictions_iterator->restriction.to.way))
+            OSMWayID{static_cast<std::uint32_t>(restrictions_iterator->restriction.to.way)})
         {
             ++way_start_and_end_iterator;
             continue;
@@ -722,7 +831,7 @@ void ExtractionContainers::PrepareRestrictions()
             continue;
         }
         if (way_start_and_end_iterator->way_id >
-            OSMWayID(restrictions_iterator->restriction.to.way))
+            OSMWayID{static_cast<std::uint32_t>(restrictions_iterator->restriction.to.way)})
         {
             util::SimpleLogger().Write(LogLevel::logDEBUG)
                 << "Restriction references invalid way: "
@@ -732,18 +841,18 @@ void ExtractionContainers::PrepareRestrictions()
             continue;
         }
         BOOST_ASSERT(way_start_and_end_iterator->way_id ==
-                     OSMWayID(restrictions_iterator->restriction.to.way));
-        const OSMNodeID via_node_id = OSMNodeID(restrictions_iterator->restriction.via.node);
+                     OSMWayID{static_cast<std::uint32_t>(restrictions_iterator->restriction.to.way)});
+        const OSMNodeID via_node_id = OSMNodeID{restrictions_iterator->restriction.via.node};
 
         // assign new via node id
         auto via_id_iter = external_to_internal_node_id_map.find(via_node_id);
         BOOST_ASSERT(via_id_iter != external_to_internal_node_id_map.end());
         restrictions_iterator->restriction.via.node = via_id_iter->second;
 
-        if (OSMNodeID(way_start_and_end_iterator->first_segment_source_id) == via_node_id)
+        if (way_start_and_end_iterator->first_segment_source_id == via_node_id)
         {
             auto to_id_iter = external_to_internal_node_id_map.find(
-                OSMNodeID(way_start_and_end_iterator->first_segment_target_id));
+                way_start_and_end_iterator->first_segment_target_id);
             if (to_id_iter == external_to_internal_node_id_map.end())
             {
                 util::SimpleLogger().Write(LogLevel::logWARNING)
@@ -756,10 +865,10 @@ void ExtractionContainers::PrepareRestrictions()
             }
             restrictions_iterator->restriction.to.node = to_id_iter->second;
         }
-        else if (OSMNodeID(way_start_and_end_iterator->last_segment_target_id) == via_node_id)
+        else if (way_start_and_end_iterator->last_segment_target_id == via_node_id)
         {
             auto to_id_iter = external_to_internal_node_id_map.find(
-                OSMNodeID(way_start_and_end_iterator->last_segment_source_id));
+                way_start_and_end_iterator->last_segment_source_id);
             if (to_id_iter == external_to_internal_node_id_map.end())
             {
                 util::SimpleLogger().Write(LogLevel::logWARNING)
diff --git a/src/extractor/extractor.cpp b/src/extractor/extractor.cpp
index 1406bdb..45ed06e 100644
--- a/src/extractor/extractor.cpp
+++ b/src/extractor/extractor.cpp
@@ -239,6 +239,7 @@ int Extractor::run()
         extraction_containers.PrepareData(config.output_file_name,
                                           config.restriction_file_name,
                                           config.names_file_name,
+                                          config.turn_lane_descriptions_file_name,
                                           main_context.state);
 
         WriteProfileProperties(config.profile_properties_output_path, main_context.properties);
@@ -475,7 +476,7 @@ Extractor::LoadNodeBasedGraph(std::unordered_set<NodeID> &barrier_nodes,
 /**
  \brief Building an edge-expanded graph from node-based input and turn restrictions
 */
-std::pair<std::size_t, std::size_t>
+std::pair<std::size_t, EdgeID>
 Extractor::BuildEdgeExpandedGraph(lua_State *lua_state,
                                   const ProfileProperties &profile_properties,
                                   std::vector<QueryNode> &internal_to_external_node_map,
@@ -504,6 +505,14 @@ Extractor::BuildEdgeExpandedGraph(lua_State *lua_state,
 
     util::NameTable name_table(config.names_file_name);
 
+    std::vector<std::uint32_t> turn_lane_offsets;
+    std::vector<guidance::TurnLaneType::Mask> turn_lane_masks;
+    if (!util::deserializeAdjacencyArray(
+            config.turn_lane_descriptions_file_name, turn_lane_offsets, turn_lane_masks))
+    {
+        util::SimpleLogger().Write(logWARNING) << "Reading Turn Lane Masks failed.";
+    }
+
     EdgeBasedGraphFactory edge_based_graph_factory(
         node_based_graph,
         compressed_edge_container,
@@ -512,9 +521,12 @@ Extractor::BuildEdgeExpandedGraph(lua_State *lua_state,
         std::const_pointer_cast<RestrictionMap const>(restriction_map),
         internal_to_external_node_map,
         profile_properties,
-        name_table);
+        name_table,
+        turn_lane_offsets,
+        turn_lane_masks);
 
     edge_based_graph_factory.Run(config.edge_output_path,
+                                 config.turn_lane_data_file_name,
                                  lua_state,
                                  config.edge_segment_lookup_path,
                                  config.edge_penalty_path,
@@ -600,7 +612,7 @@ void Extractor::BuildRTree(std::vector<EdgeBasedNode> node_based_edge_list,
 
 void Extractor::WriteEdgeBasedGraph(
     std::string const &output_file_filename,
-    size_t const max_edge_id,
+    EdgeID const max_edge_id,
     util::DeallocatingVector<EdgeBasedEdge> const &edge_based_edge_list)
 {
 
@@ -613,9 +625,9 @@ void Extractor::WriteEdgeBasedGraph(
                                  << std::flush;
     TIMER_START(write_edges);
 
-    size_t number_of_used_edges = edge_based_edge_list.size();
-    file_out_stream.write((char *)&number_of_used_edges, sizeof(size_t));
-    file_out_stream.write((char *)&max_edge_id, sizeof(size_t));
+    std::uint64_t number_of_used_edges = edge_based_edge_list.size();
+    file_out_stream.write((char *)&number_of_used_edges, sizeof(number_of_used_edges));
+    file_out_stream.write((char *)&max_edge_id, sizeof(max_edge_id));
 
     for (const auto &edge : edge_based_edge_list)
     {
@@ -661,7 +673,7 @@ void Extractor::WriteIntersectionClassificationData(
     util::RangeTable<> bearing_class_range_table(bearing_counts);
     file_out_stream << bearing_class_range_table;
 
-    file_out_stream << total_bearings;
+    file_out_stream.write(reinterpret_cast<const char *>(&total_bearings), sizeof(total_bearings));
     for (const auto &bearing_class : bearing_classes)
     {
         const auto &bearings = bearing_class.getAvailableBearings();
@@ -669,17 +681,18 @@ void Extractor::WriteIntersectionClassificationData(
                               sizeof(bearings[0]) * bearings.size());
     }
 
-    // FIXME
-    // This should be here, but g++4.8 does not have it...
-    // static_assert(std::is_trivially_copyable<util::guidance::EntryClass>::value,
-    //              "EntryClass Serialization requires trivial copyable entry classes");
+    if (!static_cast<bool>(file_out_stream))
+    {
+        throw util::exception("Failed to write to " + output_file_name + ".");
+    }
 
     util::serializeVector(file_out_stream, entry_classes);
     TIMER_STOP(write_edges);
     util::SimpleLogger().Write() << "ok, after " << TIMER_SEC(write_edges) << "s for "
                                  << node_based_intersection_classes.size() << " Indices into "
                                  << bearing_classes.size() << " bearing classes and "
-                                 << entry_classes.size() << " entry classes";
+                                 << entry_classes.size() << " entry classes and " << total_bearings
+                                 << " bearing values." << std::endl;
 }
 }
 }
diff --git a/src/extractor/extractor_callbacks.cpp b/src/extractor/extractor_callbacks.cpp
index 09ef699..4287255 100644
--- a/src/extractor/extractor_callbacks.cpp
+++ b/src/extractor/extractor_callbacks.cpp
@@ -1,14 +1,17 @@
+#include "extractor/extractor_callbacks.hpp"
+#include "extractor/external_memory_node.hpp"
 #include "extractor/extraction_containers.hpp"
 #include "extractor/extraction_node.hpp"
 #include "extractor/extraction_way.hpp"
-
-#include "extractor/external_memory_node.hpp"
 #include "extractor/restriction.hpp"
+
 #include "util/for_each_pair.hpp"
+#include "util/guidance/turn_lanes.hpp"
 #include "util/simple_logger.hpp"
 
-#include "extractor/extractor_callbacks.hpp"
+#include <boost/numeric/conversion/cast.hpp>
 #include <boost/optional/optional.hpp>
+#include <boost/tokenizer.hpp>
 
 #include <osmium/osm.hpp>
 
@@ -24,11 +27,15 @@ namespace osrm
 namespace extractor
 {
 
+using TurnLaneDescription = guidance::TurnLaneDescription;
+namespace TurnLaneType = guidance::TurnLaneType;
+
 ExtractorCallbacks::ExtractorCallbacks(ExtractionContainers &extraction_containers)
     : external_memory(extraction_containers)
 {
     // we reserved 0, 1, 2 for the empty case
     string_map[MapKey("", "")] = 0;
+    lane_description_map[TurnLaneDescription()] = 0;
 }
 
 /**
@@ -41,9 +48,9 @@ void ExtractorCallbacks::ProcessNode(const osmium::Node &input_node,
                                      const ExtractionNode &result_node)
 {
     external_memory.all_nodes_list.push_back(
-        {util::toFixed(util::FloatLongitude(input_node.location().lon())),
-         util::toFixed(util::FloatLatitude(input_node.location().lat())),
-         OSMNodeID(input_node.id()),
+        {util::toFixed(util::FloatLongitude{input_node.location().lon()}),
+         util::toFixed(util::FloatLatitude{input_node.location().lat()}),
+         OSMNodeID{static_cast<std::uint64_t>(input_node.id())},
          result_node.barrier,
          result_node.traffic_lights});
 }
@@ -140,42 +147,141 @@ void ExtractorCallbacks::ProcessWay(const osmium::Way &input_way, const Extracti
         road_classification.road_class = guidance::functionalRoadClassFromTag(data);
     }
 
+    const auto laneStringToDescription = [](std::string lane_string) -> TurnLaneDescription {
+        if (lane_string.empty())
+            return {};
+
+        TurnLaneDescription lane_description;
+
+        typedef boost::tokenizer<boost::char_separator<char>> tokenizer;
+        boost::char_separator<char> sep("|", "", boost::keep_empty_tokens);
+        boost::char_separator<char> inner_sep(";", "");
+        tokenizer tokens(lane_string, sep);
+
+        const constexpr std::size_t num_osm_tags = 11;
+        const constexpr char *osm_lane_strings[num_osm_tags] = {"none",
+                                                                "through",
+                                                                "sharp_left",
+                                                                "left",
+                                                                "slight_left",
+                                                                "slight_right",
+                                                                "right",
+                                                                "sharp_right",
+                                                                "reverse",
+                                                                "merge_to_left",
+                                                                "merge_to_right"};
+        const constexpr TurnLaneType::Mask masks_by_osm_string[num_osm_tags + 1] = {
+            TurnLaneType::none,
+            TurnLaneType::straight,
+            TurnLaneType::sharp_left,
+            TurnLaneType::left,
+            TurnLaneType::slight_left,
+            TurnLaneType::slight_right,
+            TurnLaneType::right,
+            TurnLaneType::sharp_right,
+            TurnLaneType::uturn,
+            TurnLaneType::merge_to_left,
+            TurnLaneType::merge_to_right,
+            TurnLaneType::empty}; // fallback, if string not found
+
+        for (auto iter = tokens.begin(); iter != tokens.end(); ++iter)
+        {
+            tokenizer inner_tokens(*iter, inner_sep);
+            guidance::TurnLaneType::Mask lane_mask = inner_tokens.begin() == inner_tokens.end()
+                                                         ? TurnLaneType::none
+                                                         : TurnLaneType::empty;
+            for (auto token_itr = inner_tokens.begin(); token_itr != inner_tokens.end();
+                 ++token_itr)
+            {
+                auto position = std::find(osm_lane_strings, osm_lane_strings + num_osm_tags, *token_itr);
+                const auto translated_mask =
+                    masks_by_osm_string[std::distance(osm_lane_strings, position)];
+                if (translated_mask == TurnLaneType::empty)
+                {
+                    // if we have unsupported tags, don't handle them
+                    util::SimpleLogger().Write(logDEBUG) << "Unsupported lane tag found: \""
+                                                         << *token_itr << "\"";
+                    return {};
+                }
+                BOOST_ASSERT((lane_mask & translated_mask) == 0); // make sure the mask is valid
+                lane_mask |= translated_mask;
+            }
+            // add the lane to the description
+            lane_description.push_back(lane_mask);
+        }
+        return lane_description;
+    };
+
+    // convert the lane description into an ID and, if necessary, remembr the description in the
+    // description_map
+    const auto requestId = [&](std::string lane_string) {
+        if (lane_string.empty())
+            return INVALID_LANE_DESCRIPTIONID;
+        TurnLaneDescription lane_description = laneStringToDescription(std::move(lane_string));
+
+        const auto lane_description_itr = lane_description_map.find(lane_description);
+        if (lane_description_itr == lane_description_map.end())
+        {
+            const LaneDescriptionID new_id =
+                boost::numeric_cast<LaneDescriptionID>(lane_description_map.size());
+            lane_description_map[lane_description] = new_id;
+
+            // since we are getting a new ID, we can augment the current offsets
+
+            // and store the turn lane masks, sadly stxxl does not support insert
+            for (const auto mask : lane_description)
+                external_memory.turn_lane_masks.push_back(mask);
+
+            external_memory.turn_lane_offsets.push_back(external_memory.turn_lane_offsets.back() +
+                                                        lane_description.size());
+
+            return new_id;
+        }
+        else
+        {
+            return lane_description_itr->second;
+        }
+    };
+
     // Deduplicates street names and street destination names based on the street_map map.
     // In case we do not already store the name, inserts (name, id) tuple and return id.
     // Otherwise fetches the id based on the name and returns it without insertion.
+    const auto turn_lane_id_forward = requestId(parsed_way.turn_lanes_forward);
+    const auto turn_lane_id_backward = requestId(parsed_way.turn_lanes_backward);
 
     const constexpr auto MAX_STRING_LENGTH = 255u;
-
     // Get the unique identifier for the street name
     // Get the unique identifier for the street name and destination
     const auto name_iterator = string_map.find(MapKey(parsed_way.name, parsed_way.destinations));
-    unsigned name_id = external_memory.name_lengths.size();
+    unsigned name_id = EMPTY_NAMEID;
     if (string_map.end() == name_iterator)
     {
-        auto name_length = std::min<unsigned>(MAX_STRING_LENGTH, parsed_way.name.size());
-        auto destinations_length =
+        const auto name_length = std::min<unsigned>(MAX_STRING_LENGTH, parsed_way.name.size());
+        const auto destinations_length =
             std::min<unsigned>(MAX_STRING_LENGTH, parsed_way.destinations.size());
-        auto pronunciation_length =
+        const auto pronunciation_length =
             std::min<unsigned>(MAX_STRING_LENGTH, parsed_way.pronunciation.size());
 
-        external_memory.name_char_data.reserve(name_id + name_length + destinations_length +
-                                               pronunciation_length);
+        // name_offsets already has an offset of a new name, take the offset index as the name id
+        name_id = external_memory.name_offsets.size() - 1;
+
+        external_memory.name_char_data.reserve(external_memory.name_char_data.size() + name_length
+                                               + destinations_length + pronunciation_length);
 
         std::copy(parsed_way.name.c_str(),
                   parsed_way.name.c_str() + name_length,
                   std::back_inserter(external_memory.name_char_data));
+        external_memory.name_offsets.push_back(external_memory.name_char_data.size());
 
         std::copy(parsed_way.destinations.c_str(),
                   parsed_way.destinations.c_str() + destinations_length,
                   std::back_inserter(external_memory.name_char_data));
+        external_memory.name_offsets.push_back(external_memory.name_char_data.size());
 
         std::copy(parsed_way.pronunciation.c_str(),
                   parsed_way.pronunciation.c_str() + pronunciation_length,
                   std::back_inserter(external_memory.name_char_data));
-
-        external_memory.name_lengths.push_back(name_length);
-        external_memory.name_lengths.push_back(destinations_length);
-        external_memory.name_lengths.push_back(pronunciation_length);
+        external_memory.name_offsets.push_back(external_memory.name_char_data.size());
 
         auto k = MapKey{parsed_way.name, parsed_way.destinations};
         auto v = MapVal{name_id};
@@ -191,7 +297,8 @@ void ExtractorCallbacks::ProcessWay(const osmium::Way &input_way, const Extracti
                             (parsed_way.backward_speed > 0) &&
                             (TRAVEL_MODE_INACCESSIBLE != parsed_way.backward_travel_mode) &&
                             ((parsed_way.forward_speed != parsed_way.backward_speed) ||
-                             (parsed_way.forward_travel_mode != parsed_way.backward_travel_mode));
+                             (parsed_way.forward_travel_mode != parsed_way.backward_travel_mode) ||
+                             (turn_lane_id_forward != turn_lane_id_backward));
 
     external_memory.used_node_id_list.reserve(external_memory.used_node_id_list.size() +
                                               input_way.nodes().size());
@@ -199,7 +306,7 @@ void ExtractorCallbacks::ProcessWay(const osmium::Way &input_way, const Extracti
     std::transform(input_way.nodes().begin(),
                    input_way.nodes().end(),
                    std::back_inserter(external_memory.used_node_id_list),
-                   [](const osmium::NodeRef &ref) { return OSMNodeID(ref.ref()); });
+                   [](const osmium::NodeRef &ref) { return OSMNodeID{static_cast<std::uint64_t>(ref.ref())}; });
 
     const bool is_opposite_way = TRAVEL_MODE_INACCESSIBLE == parsed_way.forward_travel_mode;
 
@@ -213,8 +320,8 @@ void ExtractorCallbacks::ProcessWay(const osmium::Way &input_way, const Extracti
             input_way.nodes().crend(),
             [&](const osmium::NodeRef &first_node, const osmium::NodeRef &last_node) {
                 external_memory.all_edges_list.push_back(
-                    InternalExtractorEdge(OSMNodeID(first_node.ref()),
-                                          OSMNodeID(last_node.ref()),
+                    InternalExtractorEdge(OSMNodeID{static_cast<std::uint64_t>(first_node.ref())},
+                                          OSMNodeID{static_cast<std::uint64_t>(last_node.ref())},
                                           name_id,
                                           backward_weight_data,
                                           true,
@@ -224,15 +331,16 @@ void ExtractorCallbacks::ProcessWay(const osmium::Way &input_way, const Extracti
                                           parsed_way.is_startpoint,
                                           parsed_way.backward_travel_mode,
                                           false,
+                                          turn_lane_id_backward,
                                           road_classification));
             });
 
         external_memory.way_start_end_id_list.push_back(
-            {OSMWayID(input_way.id()),
-             OSMNodeID(input_way.nodes().back().ref()),
-             OSMNodeID(input_way.nodes()[input_way.nodes().size() - 2].ref()),
-             OSMNodeID(input_way.nodes()[1].ref()),
-             OSMNodeID(input_way.nodes()[0].ref())});
+            {OSMWayID{static_cast<std::uint32_t>(input_way.id())},
+             OSMNodeID{static_cast<std::uint64_t>(input_way.nodes().back().ref())},
+             OSMNodeID{static_cast<std::uint64_t>(input_way.nodes()[input_way.nodes().size() - 2].ref())},
+             OSMNodeID{static_cast<std::uint64_t>(input_way.nodes()[1].ref())},
+             OSMNodeID{static_cast<std::uint64_t>(input_way.nodes()[0].ref())}});
     }
     else
     {
@@ -243,8 +351,8 @@ void ExtractorCallbacks::ProcessWay(const osmium::Way &input_way, const Extracti
             input_way.nodes().cend(),
             [&](const osmium::NodeRef &first_node, const osmium::NodeRef &last_node) {
                 external_memory.all_edges_list.push_back(
-                    InternalExtractorEdge(OSMNodeID(first_node.ref()),
-                                          OSMNodeID(last_node.ref()),
+                    InternalExtractorEdge(OSMNodeID{static_cast<std::uint64_t>(first_node.ref())},
+                                          OSMNodeID{static_cast<std::uint64_t>(last_node.ref())},
                                           name_id,
                                           forward_weight_data,
                                           true,
@@ -254,6 +362,7 @@ void ExtractorCallbacks::ProcessWay(const osmium::Way &input_way, const Extracti
                                           parsed_way.is_startpoint,
                                           parsed_way.forward_travel_mode,
                                           split_edge,
+                                          turn_lane_id_forward,
                                           road_classification));
             });
         if (split_edge)
@@ -264,8 +373,8 @@ void ExtractorCallbacks::ProcessWay(const osmium::Way &input_way, const Extracti
                 input_way.nodes().cend(),
                 [&](const osmium::NodeRef &first_node, const osmium::NodeRef &last_node) {
                     external_memory.all_edges_list.push_back(
-                        InternalExtractorEdge(OSMNodeID(first_node.ref()),
-                                              OSMNodeID(last_node.ref()),
+                        InternalExtractorEdge(OSMNodeID{static_cast<std::uint64_t>(first_node.ref())},
+                                              OSMNodeID{static_cast<std::uint64_t>(last_node.ref())},
                                               name_id,
                                               backward_weight_data,
                                               false,
@@ -275,16 +384,17 @@ void ExtractorCallbacks::ProcessWay(const osmium::Way &input_way, const Extracti
                                               parsed_way.is_startpoint,
                                               parsed_way.backward_travel_mode,
                                               true,
+                                              turn_lane_id_backward,
                                               road_classification));
                 });
         }
 
         external_memory.way_start_end_id_list.push_back(
-            {OSMWayID(input_way.id()),
-             OSMNodeID(input_way.nodes().back().ref()),
-             OSMNodeID(input_way.nodes()[input_way.nodes().size() - 2].ref()),
-             OSMNodeID(input_way.nodes()[1].ref()),
-             OSMNodeID(input_way.nodes()[0].ref())});
+            {OSMWayID{static_cast<std::uint32_t>(input_way.id())},
+             OSMNodeID{static_cast<std::uint64_t>(input_way.nodes().back().ref())},
+             OSMNodeID{static_cast<std::uint64_t>(input_way.nodes()[input_way.nodes().size() - 2].ref())},
+             OSMNodeID{static_cast<std::uint64_t>(input_way.nodes()[1].ref())},
+             OSMNodeID{static_cast<std::uint64_t>(input_way.nodes()[0].ref())}});
     }
 }
 }
diff --git a/src/extractor/graph_compressor.cpp b/src/extractor/graph_compressor.cpp
index a30684e..18af7fd 100644
--- a/src/extractor/graph_compressor.cpp
+++ b/src/extractor/graph_compressor.cpp
@@ -114,9 +114,7 @@ void GraphCompressor::Compress(const std::unordered_set<NodeID> &barrier_nodes,
             // doesn't have access to.
             const bool has_node_penalty = traffic_lights.find(node_v) != traffic_lights.end();
             if (has_node_penalty)
-            {
                 continue;
-            }
 
             // Get distances before graph is modified
             const int forward_weight1 = graph.GetEdgeData(forward_e1).distance;
@@ -139,6 +137,42 @@ void GraphCompressor::Compress(const std::unordered_set<NodeID> &barrier_nodes,
             graph.SetTarget(forward_e1, node_w);
             graph.SetTarget(reverse_e1, node_u);
 
+            /*
+             * Remember Lane Data for compressed parts. This handles scenarios where lane-data is
+             * only kept up until a traffic light.
+             *
+             *                |    |
+             * ----------------    |
+             *         -^ |        |
+             * -----------         |
+             *         -v |        |
+             * ---------------     |
+             *                |    |
+             *
+             *  u ------- v ---- w
+             *
+             * Since the edge is compressable, we can transfer:
+             * "left|right" (uv) and "" (uw) into a string with "left|right" (uw) for the compressed
+             * edge.
+             * Doing so, we might mess up the point from where the lanes are shown. It should be
+             * reasonable, since the announcements have to come early anyhow. So there is a
+             * potential danger in here, but it saves us from adding a lot of additional edges for
+             * turn-lanes. Without this,we would have to treat any turn-lane beginning/ending just
+             * like a barrier.
+             */
+            const auto selectLaneID = [](const LaneDescriptionID front, const LaneDescriptionID back) {
+                // A lane has tags: u - (front) - v - (back) - w
+                // During contraction, we keep only one of the tags. Usually the one closer to the
+                // intersection is preferred. If its empty, however, we keep the non-empty one
+                if (back == INVALID_LANE_DESCRIPTIONID)
+                    return front;
+                return back;
+            };
+            graph.GetEdgeData(forward_e1).lane_description_id = selectLaneID(
+                graph.GetEdgeData(forward_e1).lane_description_id, fwd_edge_data2.lane_description_id);
+            graph.GetEdgeData(reverse_e1).lane_description_id = selectLaneID(
+                graph.GetEdgeData(reverse_e1).lane_description_id, rev_edge_data2.lane_description_id);
+
             // remove e2's (if bidir, otherwise only one)
             graph.DeleteEdge(node_v, forward_e2);
             graph.DeleteEdge(node_v, reverse_e2);
diff --git a/src/extractor/guidance/intersection.cpp b/src/extractor/guidance/intersection.cpp
index 8902ba4..350c1b8 100644
--- a/src/extractor/guidance/intersection.cpp
+++ b/src/extractor/guidance/intersection.cpp
@@ -1,4 +1,5 @@
 #include "extractor/guidance/intersection.hpp"
+#include "extractor/guidance/toolkit.hpp"
 
 namespace osrm
 {
@@ -22,10 +23,30 @@ std::string toString(const ConnectedRoad &road)
     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));
+              std::to_string(static_cast<std::int32_t>(road.turn.instruction.direction_modifier)) +
+              " " + std::to_string(static_cast<std::int32_t>(road.turn.lane_data_id));
     return result;
 }
 
+Intersection::iterator findClosestTurn(Intersection &intersection, const double angle)
+{
+    return std::min_element(intersection.begin(),
+                            intersection.end(),
+                            [angle](const ConnectedRoad &lhs, const ConnectedRoad &rhs) {
+                                return angularDeviation(lhs.turn.angle, angle) <
+                                       angularDeviation(rhs.turn.angle, angle);
+                            });
+}
+Intersection::const_iterator findClosestTurn(const Intersection &intersection, const double angle)
+{
+    return std::min_element(intersection.cbegin(),
+                            intersection.cend(),
+                            [angle](const ConnectedRoad &lhs, const ConnectedRoad &rhs) {
+                                return angularDeviation(lhs.turn.angle, angle) <
+                                       angularDeviation(rhs.turn.angle, angle);
+                            });
+}
+
 } // namespace guidance
 } // namespace extractor
 } // namespace osrm
diff --git a/src/extractor/guidance/intersection_generator.cpp b/src/extractor/guidance/intersection_generator.cpp
index f11da13..bd655a5 100644
--- a/src/extractor/guidance/intersection_generator.cpp
+++ b/src/extractor/guidance/intersection_generator.cpp
@@ -114,9 +114,12 @@ Intersection IntersectionGenerator::getConnectedRoads(const NodeID from_node,
                 has_uturn_edge = true;
         }
 
-        intersection.push_back(ConnectedRoad(
-            TurnOperation{onto_edge, angle, {TurnType::Invalid, DirectionModifier::UTurn}},
-            turn_is_valid));
+        intersection.push_back(
+            ConnectedRoad(TurnOperation{onto_edge,
+                                        angle,
+                                        {TurnType::Invalid, DirectionModifier::UTurn},
+                                        INVALID_LANE_DATAID},
+                          turn_is_valid));
     }
 
     // We hit the case of a street leading into nothing-ness. Since the code here assumes that this
@@ -124,7 +127,9 @@ Intersection IntersectionGenerator::getConnectedRoads(const NodeID from_node,
     if (!has_uturn_edge)
     {
         intersection.push_back(
-            {TurnOperation{via_eid, 0., {TurnType::Invalid, DirectionModifier::UTurn}}, false});
+            {TurnOperation{
+                 via_eid, 0., {TurnType::Invalid, DirectionModifier::UTurn}, INVALID_LANE_DATAID},
+             false});
     }
 
     const auto ByAngle = [](const ConnectedRoad &first, const ConnectedRoad second) {
diff --git a/src/extractor/guidance/intersection_handler.cpp b/src/extractor/guidance/intersection_handler.cpp
index 6b0b2fe..41023f8 100644
--- a/src/extractor/guidance/intersection_handler.cpp
+++ b/src/extractor/guidance/intersection_handler.cpp
@@ -34,7 +34,7 @@ IntersectionHandler::IntersectionHandler(const util::NodeBasedDynamicGraph &node
 {
 }
 
-IntersectionHandler::~IntersectionHandler() {}
+IntersectionHandler::~IntersectionHandler() = default;
 
 std::size_t IntersectionHandler::countValid(const Intersection &intersection) const
 {
diff --git a/src/extractor/guidance/motorway_handler.cpp b/src/extractor/guidance/motorway_handler.cpp
index cc81bc9..e08b034 100644
--- a/src/extractor/guidance/motorway_handler.cpp
+++ b/src/extractor/guidance/motorway_handler.cpp
@@ -1,4 +1,5 @@
 #include "extractor/guidance/motorway_handler.hpp"
+#include "extractor/guidance/classification_data.hpp"
 #include "extractor/guidance/constants.hpp"
 #include "extractor/guidance/toolkit.hpp"
 
@@ -21,10 +22,6 @@ 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)
 {
@@ -45,14 +42,12 @@ inline bool isRampClass(EdgeID eid, const util::NodeBasedDynamicGraph &node_base
 MotorwayHandler::MotorwayHandler(const util::NodeBasedDynamicGraph &node_based_graph,
                                  const std::vector<QueryNode> &node_info_list,
                                  const util::NameTable &name_table,
-                                 const SuffixTable &street_name_suffix_table,
-                                 const IntersectionGenerator &intersection_generator)
-    : IntersectionHandler(node_based_graph, node_info_list, name_table, street_name_suffix_table),
-      intersection_generator(intersection_generator)
+                                 const SuffixTable &street_name_suffix_table)
+    : IntersectionHandler(node_based_graph, node_info_list, name_table, street_name_suffix_table)
 {
 }
 
-MotorwayHandler::~MotorwayHandler() {}
+MotorwayHandler::~MotorwayHandler() = default;
 
 bool MotorwayHandler::canProcess(const NodeID,
                                  const EdgeID via_eid,
@@ -92,7 +87,7 @@ 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))
+    if (isMotorwayClass(in_data.road_classification.road_class))
     {
         intersection = fromMotorway(via_eid, std::move(intersection));
         std::for_each(intersection.begin(), intersection.end(), [](ConnectedRoad &road) {
@@ -111,7 +106,7 @@ operator()(const NodeID, const EdgeID via_eid, Intersection intersection) const
 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));
+    BOOST_ASSERT(isMotorwayClass(in_data.road_classification.road_class));
 
     const auto countExitingMotorways = [this](const Intersection &intersection) {
         unsigned count = 0;
@@ -130,7 +125,7 @@ Intersection MotorwayHandler::fromMotorway(const EdgeID via_eid, Intersection in
             const auto &out_data = node_based_graph.GetEdgeData(road.turn.eid);
             if (road.turn.angle != 0 && in_data.name_id == out_data.name_id &&
                 in_data.name_id != EMPTY_NAMEID &&
-                detail::isMotorwayClass(out_data.road_classification.road_class))
+                isMotorwayClass(out_data.road_classification.road_class))
                 return road.turn.angle;
         }
         return intersection[0].turn.angle;
@@ -142,7 +137,7 @@ Intersection MotorwayHandler::fromMotorway(const EdgeID via_eid, Intersection in
         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) &&
+            if (isMotorwayClass(out_data.road_classification.road_class) &&
                 angularDeviation(road.turn.angle, STRAIGHT_ANGLE) < best)
             {
                 best = angularDeviation(road.turn.angle, STRAIGHT_ANGLE);
@@ -385,7 +380,7 @@ Intersection MotorwayHandler::fromRamp(const EdgeID via_eid, Intersection inters
                 {
                     // circular order indicates a merge to the left (0-3 onto 4
                     if (angularDeviation(intersection[1].turn.angle, STRAIGHT_ANGLE) <
-                        2*NARROW_TURN_ANGLE)
+                        2 * NARROW_TURN_ANGLE)
                         intersection[1].turn.instruction = {TurnType::Merge,
                                                             DirectionModifier::SlightLeft};
                     else // fallback
@@ -453,8 +448,8 @@ Intersection MotorwayHandler::fromRamp(const EdgeID via_eid, Intersection inters
                 //      M  R
                 //      | /
                 //      R
-                if (detail::isMotorwayClass(node_based_graph.GetEdgeData(intersection[1].turn.eid)
-                                                .road_classification.road_class))
+                if (isMotorwayClass(node_based_graph.GetEdgeData(intersection[1].turn.eid)
+                                        .road_classification.road_class))
                 {
                     intersection[1].turn.instruction = {TurnType::Turn,
                                                         DirectionModifier::SlightRight};
@@ -475,12 +470,11 @@ Intersection MotorwayHandler::fromRamp(const EdgeID via_eid, Intersection inters
         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))
+            if (!road.entry_allowed && isMotorwayClass(edge_data.road_classification.road_class))
             {
                 passed_highway_entry = true;
             }
-            else if (detail::isMotorwayClass(edge_data.road_classification.road_class))
+            else if (isMotorwayClass(edge_data.road_classification.road_class))
             {
                 road.turn.instruction = {TurnType::Merge,
                                          passed_highway_entry ? DirectionModifier::SlightRight
@@ -516,9 +510,8 @@ Intersection MotorwayHandler::fallback(Intersection intersection) const
         if (!road.entry_allowed)
             continue;
 
-        const auto type = detail::isMotorwayClass(out_data.road_classification.road_class)
-                              ? TurnType::Merge
-                              : TurnType::Turn;
+        const auto type = isMotorwayClass(out_data.road_classification.road_class) ? TurnType::Merge
+                                                                                   : TurnType::Turn;
 
         if (type == TurnType::Turn)
         {
diff --git a/src/extractor/guidance/roundabout_handler.cpp b/src/extractor/guidance/roundabout_handler.cpp
index 2d0e54d..df0455b 100644
--- a/src/extractor/guidance/roundabout_handler.cpp
+++ b/src/extractor/guidance/roundabout_handler.cpp
@@ -1,5 +1,5 @@
-#include "extractor/guidance/constants.hpp"
 #include "extractor/guidance/roundabout_handler.hpp"
+#include "extractor/guidance/constants.hpp"
 #include "extractor/guidance/toolkit.hpp"
 
 #include "util/coordinate_calculation.hpp"
@@ -31,7 +31,7 @@ RoundaboutHandler::RoundaboutHandler(const util::NodeBasedDynamicGraph &node_bas
 {
 }
 
-RoundaboutHandler::~RoundaboutHandler() {}
+RoundaboutHandler::~RoundaboutHandler() = default;
 
 bool RoundaboutHandler::canProcess(const NodeID from_nid,
                                    const EdgeID via_eid,
@@ -373,7 +373,14 @@ Intersection RoundaboutHandler::handleRoundabouts(const RoundaboutType roundabou
                 if (1 == node_based_graph.GetDirectedOutDegree(node_v))
                 {
                     // No turn possible.
-                    turn.instruction = TurnInstruction::NO_TURN();
+                    if (intersection.size() == 2)
+                        turn.instruction = TurnInstruction::NO_TURN();
+                    else
+                    {
+                        turn.instruction.type =
+                            TurnType::Suppressed; // make sure to report intersection
+                        turn.instruction.direction_modifier = getTurnDirection(turn.angle);
+                    }
                 }
                 else
                 {
diff --git a/src/extractor/guidance/turn_analysis.cpp b/src/extractor/guidance/turn_analysis.cpp
index 507aaa8..a95ff90 100644
--- a/src/extractor/guidance/turn_analysis.cpp
+++ b/src/extractor/guidance/turn_analysis.cpp
@@ -48,19 +48,15 @@ TurnAnalysis::TurnAnalysis(const util::NodeBasedDynamicGraph &node_based_graph,
                          compressed_edge_container,
                          name_table,
                          street_name_suffix_table),
-      motorway_handler(node_based_graph,
-                       node_info_list,
-                       name_table,
-                       street_name_suffix_table,
-                       intersection_generator),
+      motorway_handler(node_based_graph, node_info_list, name_table, street_name_suffix_table),
       turn_handler(node_based_graph, node_info_list, name_table, street_name_suffix_table)
 {
 }
 
-std::vector<TurnOperation> TurnAnalysis::getTurns(const NodeID from_nid, const EdgeID via_eid) const
+Intersection TurnAnalysis::assignTurnTypes(const NodeID from_nid,
+                                           const EdgeID via_eid,
+                                           Intersection intersection) const
 {
-    auto intersection = intersection_generator(from_nid, via_eid);
-
     // 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))
@@ -81,10 +77,24 @@ std::vector<TurnOperation> TurnAnalysis::getTurns(const NodeID from_nid, const E
             intersection = turn_handler(from_nid, via_eid, std::move(intersection));
         }
     }
-
     // Handle sliproads
     intersection = handleSliproads(via_eid, std::move(intersection));
 
+    // Turn On Ramps Into Off Ramps, if we come from a motorway-like road
+    if (isMotorwayClass(node_based_graph.GetEdgeData(via_eid).road_classification.road_class))
+    {
+        std::for_each(intersection.begin(), intersection.end(), [](ConnectedRoad &road) {
+            if (road.turn.instruction.type == TurnType::OnRamp)
+                road.turn.instruction.type = TurnType::OffRamp;
+        });
+    }
+
+    return intersection;
+}
+
+std::vector<TurnOperation>
+TurnAnalysis::transformIntersectionIntoTurns(const Intersection &intersection) const
+{
     std::vector<TurnOperation> turns;
     for (auto road : intersection)
         if (road.entry_allowed)
@@ -154,7 +164,6 @@ Intersection TurnAnalysis::handleSliproads(const EdgeID source_edge_id,
         });
 
     const bool hasNext = next_road != intersection.end();
-
     if (!hasNext)
     {
         return intersection;
@@ -167,10 +176,21 @@ Intersection TurnAnalysis::handleSliproads(const EdgeID source_edge_id,
         return intersection;
     }
 
-    const auto next_road_next_intersection =
-        intersection_generator(intersection_node_id, next_road->turn.eid);
+    auto next_intersection_node = node_based_graph.GetTarget(next_road->turn.eid);
 
-    const auto next_intersection_node = node_based_graph.GetTarget(next_road->turn.eid);
+    const auto next_road_next_intersection = [&]() {
+        auto intersection = intersection_generator(intersection_node_id, next_road->turn.eid);
+        auto in_edge = next_road->turn.eid;
+        //skip over traffic lights
+        if(intersection.size() == 2)
+        {
+            const auto node = node_based_graph.GetTarget(in_edge);
+            in_edge = intersection[1].turn.eid;
+            next_intersection_node = node_based_graph.GetTarget(in_edge);
+            intersection = intersection_generator(node, in_edge);
+        }
+        return intersection;
+    }();
 
     std::unordered_set<NameID> target_road_names;
 
@@ -184,7 +204,18 @@ Intersection TurnAnalysis::handleSliproads(const EdgeID source_edge_id,
     {
         if (linkTest(road))
         {
-            auto target_intersection = intersection_generator(intersection_node_id, road.turn.eid);
+            const auto target_intersection = [&](NodeID node, EdgeID eid) {
+                auto intersection = intersection_generator(node, eid);
+                //skip over traffic lights
+                if(intersection.size() == 2)
+                {
+                    node = node_based_graph.GetTarget(eid);
+                    eid = intersection[1].turn.eid;
+                    intersection = intersection_generator(node, eid);
+                }
+                return intersection;
+            }(intersection_node_id, road.turn.eid);
+
             for (const auto &candidate_road : target_intersection)
             {
                 const auto &candidate_data = node_based_graph.GetEdgeData(candidate_road.turn.eid);
@@ -225,6 +256,8 @@ Intersection TurnAnalysis::handleSliproads(const EdgeID source_edge_id,
     return intersection;
 }
 
+const IntersectionGenerator &TurnAnalysis::getGenerator() const { return intersection_generator; }
+
 } // namespace guidance
 } // namespace extractor
 } // namespace osrm
diff --git a/src/extractor/guidance/turn_discovery.cpp b/src/extractor/guidance/turn_discovery.cpp
new file mode 100644
index 0000000..0bd2728
--- /dev/null
+++ b/src/extractor/guidance/turn_discovery.cpp
@@ -0,0 +1,85 @@
+#include "extractor/guidance/turn_discovery.hpp"
+#include "extractor/guidance/constants.hpp"
+
+namespace osrm
+{
+namespace extractor
+{
+namespace guidance
+{
+namespace lanes
+{
+
+bool findPreviousIntersection(const NodeID node_v,
+                              const EdgeID via_edge,
+                              const Intersection intersection,
+                              const TurnAnalysis &turn_analysis,
+                              const util::NodeBasedDynamicGraph &node_based_graph,
+                              // output parameters
+                              NodeID &result_node,
+                              EdgeID &result_via_edge,
+                              Intersection &result_intersection)
+{
+    /* We need to find the intersection that is located prior to via_edge.
+
+     *
+     * NODE_U  -> PREVIOUS_ID            -> NODE_V -> VIA_EDGE -> NODE_W:INTERSECTION
+     * NODE_U? <- STRAIGHTMOST           <- NODE_V <- UTURN
+     * NODE_U? -> UTURN == PREVIOUSE_ID? -> NODE_V -> VIA_EDGE
+     *
+     * To do so, we first get the intersection atNODE and find the straightmost turn from that
+     * node. This will result in NODE_X. The uturn in the intersection at NODE_X should be
+     * PREVIOUS_ID. To verify that find, we check the intersection using our PREVIOUS_ID candidate
+     * to check the intersection at NODE for via_edge
+     */
+    const constexpr double COMBINE_DISTANCE_CUTOFF = 30;
+
+    // we check if via-edge is too short. In this case the previous turn cannot influence the turn
+    // at via_edge and the intersection at NODE_W
+    if (node_based_graph.GetEdgeData(via_edge).distance > COMBINE_DISTANCE_CUTOFF)
+        return false;
+
+    // Node -> Via_Edge -> Intersection[0 == UTURN] -> reverse_of(via_edge) -> Intersection at node
+    // (looking at the reverse direction).
+    const auto node_w = node_based_graph.GetTarget(via_edge);
+    const auto u_turn_at_node_w = intersection[0].turn.eid;
+    const auto node_v_reverse_intersection =
+        turn_analysis.getIntersection(node_w, u_turn_at_node_w);
+
+    // Continue along the straightmost turn. If there is no straight turn, we cannot find a valid
+    // previous intersection.
+    const auto straightmost_at_v_in_reverse =
+        findClosestTurn(node_v_reverse_intersection, STRAIGHT_ANGLE);
+    if (angularDeviation(straightmost_at_v_in_reverse->turn.angle, STRAIGHT_ANGLE) >
+        FUZZY_ANGLE_DIFFERENCE)
+        return false;
+
+    const auto node_u = node_based_graph.GetTarget(straightmost_at_v_in_reverse->turn.eid);
+    const auto node_u_reverse_intersection =
+        turn_analysis.getIntersection(node_v, straightmost_at_v_in_reverse->turn.eid);
+
+    // now check that the u-turn at the given intersection connects to via-edge
+    // The u-turn at the now found intersection should, hopefully, represent the previous edge.
+    result_node = node_u;
+    result_via_edge = node_u_reverse_intersection[0].turn.eid;
+
+    // if the edge is not traversable, we obviously don't have a previous intersection or couldn't
+    // find it.
+    if (node_based_graph.GetEdgeData(result_via_edge).reversed)
+        return false;
+
+    result_intersection = turn_analysis.getIntersection(node_u, result_via_edge);
+    const auto check_via_edge = findClosestTurn(result_intersection, STRAIGHT_ANGLE)->turn.eid;
+    if (check_via_edge != via_edge)
+        return false;
+
+    result_intersection =
+        turn_analysis.assignTurnTypes(node_u, result_via_edge, std::move(result_intersection));
+
+    return true;
+}
+
+} // namespace lanes
+} // namespace guidance
+} // namespace extractor
+} // namespace osrm
diff --git a/src/extractor/guidance/turn_handler.cpp b/src/extractor/guidance/turn_handler.cpp
index ccb6559..ebfa044 100644
--- a/src/extractor/guidance/turn_handler.cpp
+++ b/src/extractor/guidance/turn_handler.cpp
@@ -29,7 +29,7 @@ TurnHandler::TurnHandler(const util::NodeBasedDynamicGraph &node_based_graph,
 {
 }
 
-TurnHandler::~TurnHandler() {}
+TurnHandler::~TurnHandler() = default;
 
 bool TurnHandler::canProcess(const NodeID, const EdgeID, const Intersection &) const
 {
@@ -78,7 +78,8 @@ Intersection TurnHandler::handleThreeWayTurn(const EdgeID via_edge, Intersection
     const auto &first_data = node_based_graph.GetEdgeData(intersection[1].turn.eid);
     const auto &second_data = node_based_graph.GetEdgeData(intersection[2].turn.eid);
     BOOST_ASSERT(intersection[0].turn.angle < 0.001);
-    const auto isObviousOfTwo = [this](const ConnectedRoad road, const ConnectedRoad other) {
+    const auto isObviousOfTwo = [this, in_data](const ConnectedRoad road,
+                                                const ConnectedRoad other) {
         const auto first_class =
             node_based_graph.GetEdgeData(road.turn.eid).road_classification.road_class;
 
@@ -87,24 +88,27 @@ Intersection TurnHandler::handleThreeWayTurn(const EdgeID via_edge, Intersection
 
         const bool is_ramp = isRampClass(first_class);
         const bool is_obvious_by_road_class =
-            (!is_ramp && (2 * getPriority(first_class) < getPriority(second_class))) ||
+            (!is_ramp && (2 * getPriority(first_class) < getPriority(second_class)) &&
+             in_data.road_classification.road_class == first_class) ||
             (!isLowPriorityRoadClass(first_class) && isLowPriorityRoadClass(second_class));
 
         if (is_obvious_by_road_class)
             return true;
 
-        const bool other_is_obvious_by_road_flass =
+        const bool other_is_obvious_by_road_class =
             (!isRampClass(second_class) &&
-             (2 * getPriority(second_class) < getPriority(first_class))) ||
+             (2 * getPriority(second_class) < getPriority(first_class)) &&
+             in_data.road_classification.road_class == second_class) ||
             (!isLowPriorityRoadClass(second_class) && isLowPriorityRoadClass(first_class));
 
-        if (other_is_obvious_by_road_flass)
+        if (other_is_obvious_by_road_class)
             return false;
 
         const bool turn_is_perfectly_straight = angularDeviation(road.turn.angle, STRAIGHT_ANGLE) <
                                                 std::numeric_limits<double>::epsilon();
 
-        if (turn_is_perfectly_straight)
+        if (turn_is_perfectly_straight && in_data.name_id != EMPTY_NAMEID &&
+            in_data.name_id == node_based_graph.GetEdgeData(road.turn.eid).name_id)
             return true;
 
         const bool is_much_narrower_than_other =
@@ -180,7 +184,9 @@ Intersection TurnHandler::handleThreeWayTurn(const EdgeID via_edge, Intersection
                 I
                 I
      */
-    else if (isEndOfRoad(intersection[0], intersection[1], intersection[2]))
+    else if (isEndOfRoad(intersection[0], intersection[1], intersection[2]) &&
+             !isObviousOfTwo(intersection[1], intersection[2]) &&
+             !isObviousOfTwo(intersection[2], intersection[1]))
     {
         if (intersection[1].entry_allowed)
         {
@@ -371,6 +377,7 @@ std::size_t TurnHandler::findObviousTurn(const EdgeID via_edge,
     double best_continue_deviation = 180;
 
     const EdgeData &in_data = node_based_graph.GetEdgeData(via_edge);
+    const auto in_class = in_data.road_classification.road_class;
     for (std::size_t i = 1; i < intersection.size(); ++i)
     {
         const double deviation = angularDeviation(intersection[i].turn.angle, STRAIGHT_ANGLE);
@@ -384,9 +391,11 @@ std::size_t TurnHandler::findObviousTurn(const EdgeID via_edge,
         auto continue_class = node_based_graph.GetEdgeData(intersection[best_continue].turn.eid)
                                   .road_classification.road_class;
         if (intersection[i].entry_allowed && out_data.name_id == in_data.name_id &&
-            (best_continue == 0 || continue_class > out_data.road_classification.road_class ||
+            (best_continue == 0 || (continue_class > out_data.road_classification.road_class &&
+                                    in_class != continue_class) ||
              (deviation < best_continue_deviation &&
-              out_data.road_classification.road_class == continue_class)))
+              (out_data.road_classification.road_class == continue_class ||
+               in_class == out_data.road_classification.road_class))))
         {
             best_continue_deviation = deviation;
             best_continue = i;
diff --git a/src/extractor/guidance/turn_lane_augmentation.cpp b/src/extractor/guidance/turn_lane_augmentation.cpp
new file mode 100644
index 0000000..eee972c
--- /dev/null
+++ b/src/extractor/guidance/turn_lane_augmentation.cpp
@@ -0,0 +1,303 @@
+#include "extractor/guidance/turn_lane_augmentation.hpp"
+#include "extractor/guidance/turn_lane_types.hpp"
+#include "util/simple_logger.hpp"
+
+#include <algorithm>
+#include <boost/assert.hpp>
+#include <cstddef>
+#include <utility>
+
+namespace osrm
+{
+namespace extractor
+{
+namespace guidance
+{
+namespace lanes
+{
+
+namespace
+{
+
+const constexpr TurnLaneType::Mask tag_by_modifier[] = {TurnLaneType::uturn,
+                                                        TurnLaneType::sharp_right,
+                                                        TurnLaneType::right,
+                                                        TurnLaneType::slight_right,
+                                                        TurnLaneType::straight,
+                                                        TurnLaneType::slight_left,
+                                                        TurnLaneType::left,
+                                                        TurnLaneType::sharp_left};
+
+std::size_t getNumberOfTurns(const Intersection &intersection)
+{
+    return std::count_if(intersection.begin(), intersection.end(), [](const ConnectedRoad &road) {
+        return road.entry_allowed;
+    });
+}
+
+LaneDataVector augmentMultiple(const std::size_t none_index,
+                               const std::size_t connection_count,
+                               LaneDataVector lane_data,
+                               const Intersection &intersection)
+{
+
+    // a none-turn is allowing multiple turns. we have to add a lane-data entry for
+    // every possible turn. This should, hopefully, only be the case for single lane
+    // entries?
+
+    // looking at the left side first
+    const auto range = [&]() {
+        if (none_index == 0)
+        {
+            // find first connection_count - lane_data.size() valid turns
+            std::size_t count = 0;
+            for (std::size_t intersection_index = 1; intersection_index < intersection.size();
+                 ++intersection_index)
+            {
+
+                count += static_cast<int>(intersection[intersection_index].entry_allowed);
+                if (count > connection_count - lane_data.size())
+                    return std::make_pair(std::size_t{1}, intersection_index + 1);
+            }
+        }
+        else if (none_index + 1 == lane_data.size())
+        {
+            BOOST_ASSERT(!lane_data.empty());
+            // find last connection-count - last_data.size() valid turns
+            std::size_t count = 0;
+            for (std::size_t intersection_index = intersection.size() - 1; intersection_index > 0;
+                 --intersection_index)
+            {
+                count += static_cast<int>(intersection[intersection_index].entry_allowed);
+                if (count > connection_count - lane_data.size())
+                    return std::make_pair(intersection_index, intersection.size());
+            }
+        }
+        else
+        {
+            // skip the first #index valid turns, find next connection_count -
+            // lane_data.size() valid ones
+
+            std::size_t begin = 1, count = 0, intersection_index;
+            for (intersection_index = 1; intersection_index < intersection.size();
+                 ++intersection_index)
+            {
+                count += static_cast<int>(intersection[intersection_index].entry_allowed);
+                // if we reach the amount of
+                if (count >= none_index)
+                {
+                    begin = intersection_index + 1;
+                    break;
+                }
+            }
+
+            // reset count to find the number of necessary entries
+            count = 0;
+            for (intersection_index = begin; intersection_index < intersection.size();
+                 ++intersection_index)
+            {
+                count += static_cast<int>(intersection[intersection_index].entry_allowed);
+                if (count > connection_count - lane_data.size())
+                {
+                    return std::make_pair(begin, intersection_index + 1);
+                }
+            }
+        }
+        // this should, theoretically, never be reached
+        util::SimpleLogger().Write(logWARNING) << "Failed lane assignment. Reached bad situation.";
+        return std::make_pair(std::size_t{0}, std::size_t{0});
+    }();
+    for (auto intersection_index = range.first; intersection_index < range.second;
+         ++intersection_index)
+    {
+        if (intersection[intersection_index].entry_allowed)
+        {
+            // FIXME this probably can be only a subset of these turns here?
+            lane_data.push_back({tag_by_modifier[intersection[intersection_index]
+                                                     .turn.instruction.direction_modifier],
+                                 lane_data[none_index].from,
+                                 lane_data[none_index].to});
+        }
+    }
+    lane_data.erase(lane_data.begin() + none_index);
+    return lane_data;
+}
+
+// Merging none-tag into its neighboring fields
+// This handles situations like "left | | | right".
+LaneDataVector mergeNoneTag(const std::size_t none_index, LaneDataVector lane_data)
+{
+    if (none_index == 0 || none_index + 1 == lane_data.size())
+    {
+        if (none_index == 0)
+        {
+            lane_data[1].from = lane_data[0].from;
+        }
+        else
+        {
+            lane_data[none_index - 1].to = lane_data[none_index].to;
+        }
+        lane_data.erase(lane_data.begin() + none_index);
+    }
+    else if (lane_data[none_index].to - lane_data[none_index].from <= 1)
+    {
+        lane_data[none_index - 1].to = lane_data[none_index].from;
+        lane_data[none_index + 1].from = lane_data[none_index].to;
+
+        lane_data.erase(lane_data.begin() + none_index);
+    }
+    return lane_data;
+}
+
+LaneDataVector handleRenamingSituations(const std::size_t none_index,
+                                        LaneDataVector lane_data,
+                                        const Intersection &intersection)
+{
+    bool has_right = false;
+    bool has_through = false;
+    bool has_left = false;
+    for (const auto &road : intersection)
+    {
+        if (!road.entry_allowed)
+            continue;
+
+        const auto modifier = road.turn.instruction.direction_modifier;
+        has_right |= modifier == DirectionModifier::Right;
+        has_right |= modifier == DirectionModifier::SlightRight;
+        has_right |= modifier == DirectionModifier::SharpRight;
+        has_through |= modifier == DirectionModifier::Straight;
+        has_left |= modifier == DirectionModifier::Left;
+        has_left |= modifier == DirectionModifier::SlightLeft;
+        has_left |= modifier == DirectionModifier::SharpLeft;
+    }
+
+    // find missing tag and augment neighboring, if possible
+    if (none_index == 0)
+    {
+        if (has_right &&
+            (lane_data.size() == 1 || (lane_data[none_index + 1].tag != TurnLaneType::sharp_right &&
+                                       lane_data[none_index + 1].tag != TurnLaneType::right)))
+        {
+            lane_data[none_index].tag = TurnLaneType::right;
+            if (lane_data.size() > 1 && lane_data[none_index + 1].tag == TurnLaneType::straight)
+            {
+                lane_data[none_index + 1].from = lane_data[none_index].from;
+                // turning right through a possible through lane is not possible
+                lane_data[none_index].to = lane_data[none_index].from;
+            }
+        }
+        else if (has_through &&
+                 (lane_data.size() == 1 || lane_data[none_index + 1].tag != TurnLaneType::straight))
+        {
+            lane_data[none_index].tag = TurnLaneType::straight;
+        }
+    }
+    else if (none_index + 1 == lane_data.size())
+    {
+        if (has_left && ((lane_data[none_index - 1].tag != TurnLaneType::sharp_left &&
+                          lane_data[none_index - 1].tag != TurnLaneType::left)))
+        {
+            lane_data[none_index].tag = TurnLaneType::left;
+            if (lane_data[none_index - 1].tag == TurnLaneType::straight)
+            {
+                lane_data[none_index - 1].to = lane_data[none_index].to;
+                // turning left through a possible through lane is not possible
+                lane_data[none_index].from = lane_data[none_index].to;
+            }
+        }
+        else if (has_through && lane_data[none_index - 1].tag != TurnLaneType::straight)
+        {
+            lane_data[none_index].tag = TurnLaneType::straight;
+        }
+    }
+    else
+    {
+        if ((lane_data[none_index + 1].tag == TurnLaneType::left ||
+             lane_data[none_index + 1].tag == TurnLaneType::slight_left ||
+             lane_data[none_index + 1].tag == TurnLaneType::sharp_left) &&
+            (lane_data[none_index - 1].tag == TurnLaneType::right ||
+             lane_data[none_index - 1].tag == TurnLaneType::slight_right ||
+             lane_data[none_index - 1].tag == TurnLaneType::sharp_right))
+        {
+            lane_data[none_index].tag = TurnLaneType::straight;
+        }
+    }
+    return lane_data;
+}
+
+} // namespace
+
+/*
+   Lanes can have the tag none. While a nice feature for visibility, it is a terrible feature
+   for parsing. None can be part of neighboring turns, or not. We have to look at both the
+   intersection and the lane data to see what turns we have to augment by the none-lanes
+ */
+LaneDataVector handleNoneValueAtSimpleTurn(LaneDataVector lane_data,
+                                           const Intersection &intersection)
+{
+    const bool needs_no_processing =
+        (intersection.empty() || lane_data.empty() || !hasTag(TurnLaneType::none, lane_data));
+
+    if (needs_no_processing)
+        return lane_data;
+
+    // FIXME all this needs to consider the number of lanes at the target to ensure that we
+    // augment lanes correctly, if the target lane allows for more turns
+    //
+    // -----------------
+    //
+    // -----        ----
+    //  -v          |
+    // -----        |
+    //      |   |   |
+    //
+    // A situation like this would allow a right turn from the through lane.
+    //
+    // -----------------
+    //
+    // -----    --------
+    //  -v      |
+    // -----    |
+    //      |   |
+    //
+    // Here, the number of lanes in the right road would not allow turns from both lanes, but
+    // only from the right one.
+
+    const std::size_t connection_count =
+        getNumberOfTurns(intersection) -
+        ((intersection[0].entry_allowed && lane_data.back().tag != TurnLaneType::uturn) ? 1 : 0);
+
+    // TODO check for impossible turns to see whether the turn lane is at the correct place
+    const std::size_t none_index = std::distance(lane_data.begin(), findTag(TurnLaneType::none, lane_data));
+    BOOST_ASSERT(none_index != lane_data.size());
+    // we have to create multiple turns
+    if (connection_count > lane_data.size())
+    {
+        lane_data =
+            augmentMultiple(none_index, connection_count, std::move(lane_data), intersection);
+    }
+    // we have to reduce it, assigning it to neighboring turns
+    else if (connection_count < lane_data.size())
+    {
+        // a pgerequisite is simple turns. Larger differences should not end up here
+        // an additional line at the side is only reasonable if it is targeting public
+        // service vehicles. Otherwise, we should not have it
+        BOOST_ASSERT(connection_count + 1 == lane_data.size());
+
+        lane_data = mergeNoneTag(none_index, std::move(lane_data));
+    }
+    // we have to rename and possibly augment existing ones. The pure count remains the
+    // same.
+    else
+    {
+        lane_data = handleRenamingSituations(none_index, std::move(lane_data), intersection);
+    }
+    // finally make sure we are still sorted
+    std::sort(lane_data.begin(), lane_data.end());
+    return lane_data;
+}
+
+} // namespace lanes
+} // namespace guidance
+} // namespace extractor
+} // namespace osrm
diff --git a/src/extractor/guidance/turn_lane_data.cpp b/src/extractor/guidance/turn_lane_data.cpp
new file mode 100644
index 0000000..f79f065
--- /dev/null
+++ b/src/extractor/guidance/turn_lane_data.cpp
@@ -0,0 +1,130 @@
+#include "extractor/guidance/turn_lane_data.hpp"
+#include "util/guidance/turn_lanes.hpp"
+
+#include <boost/numeric/conversion/cast.hpp>
+
+#include <algorithm>
+#include <cstddef>
+#include <string>
+#include <unordered_map>
+#include <utility>
+
+namespace osrm
+{
+namespace extractor
+{
+namespace guidance
+{
+namespace lanes
+{
+
+bool TurnLaneData::operator<(const TurnLaneData &other) const
+{
+    if (from < other.from)
+        return true;
+    if (from > other.from)
+        return false;
+
+    if (to < other.to)
+        return true;
+    if (to > other.to)
+        return false;
+
+    const constexpr TurnLaneType::Mask tag_by_modifier[] = {TurnLaneType::sharp_right,
+                                                            TurnLaneType::right,
+                                                            TurnLaneType::slight_right,
+                                                            TurnLaneType::straight,
+                                                            TurnLaneType::slight_left,
+                                                            TurnLaneType::left,
+                                                            TurnLaneType::sharp_left,
+                                                            TurnLaneType::uturn};
+    return std::find(tag_by_modifier, tag_by_modifier + 8, this->tag) <
+           std::find(tag_by_modifier, tag_by_modifier + 8, other.tag);
+}
+
+LaneDataVector laneDataFromDescription(const TurnLaneDescription &turn_lane_description)
+{
+    typedef std::unordered_map<TurnLaneType::Mask, std::pair<LaneID, LaneID>> LaneMap;
+    const auto num_lanes = boost::numeric_cast<LaneID>(turn_lane_description.size());
+    const auto setLaneData = [&](
+        LaneMap &map, TurnLaneType::Mask full_mask, const LaneID current_lane) {
+        const auto isSet = [&](const TurnLaneType::Mask test_mask) -> bool {
+            return (test_mask & full_mask) == test_mask;
+        };
+
+        for (std::size_t shift = 0; shift < TurnLaneType::detail::num_supported_lane_types; ++shift)
+        {
+            TurnLaneType::Mask mask = 1 << shift;
+            if (isSet(mask))
+            {
+                auto map_iterator = map.find(mask);
+                if (map_iterator == map.end())
+                    map[mask] = std::make_pair(current_lane, current_lane);
+                else
+                {
+                    map_iterator->second.first = current_lane;
+                }
+            }
+        }
+    };
+
+    LaneMap lane_map;
+    LaneID lane_nr = num_lanes - 1;
+    if (turn_lane_description.empty())
+        return {};
+
+    for (auto full_mask : turn_lane_description)
+    {
+        setLaneData(lane_map, full_mask, lane_nr);
+        --lane_nr;
+    }
+
+    // transform the map into the lane data vector
+    LaneDataVector lane_data;
+    for (const auto tag : lane_map)
+    {
+        lane_data.push_back({tag.first, tag.second.first, tag.second.second});
+    }
+
+    std::sort(lane_data.begin(), lane_data.end());
+
+    // check whether a given turn lane string resulted in valid lane data
+    const auto hasValidOverlaps = [](const LaneDataVector &lane_data) {
+        // Allow an overlap of at most one. Larger overlaps would result in crossing another turn,
+        // which is invalid
+        for (std::size_t index = 1; index < lane_data.size(); ++index)
+        {
+            if (lane_data[index - 1].to > lane_data[index].from)
+                return false;
+        }
+        return true;
+    };
+
+    if (!hasValidOverlaps(lane_data))
+        lane_data.clear();
+
+    return lane_data;
+}
+
+LaneDataVector::iterator findTag(const TurnLaneType::Mask tag, LaneDataVector &data)
+{
+    return std::find_if(data.begin(), data.end(), [&](const TurnLaneData &lane_data) {
+        return (tag & lane_data.tag) != TurnLaneType::empty;
+    });
+}
+LaneDataVector::const_iterator findTag(const TurnLaneType::Mask tag, const LaneDataVector &data)
+{
+    return std::find_if(data.cbegin(), data.cend(), [&](const TurnLaneData &lane_data) {
+        return (tag & lane_data.tag) != TurnLaneType::empty;
+    });
+}
+
+bool hasTag(const TurnLaneType::Mask tag, const LaneDataVector &data)
+{
+    return findTag(tag, data) != data.cend();
+}
+
+} // namespace lanes
+} // namespace guidance
+} // namespace extractor
+} // namespace osrm
diff --git a/src/extractor/guidance/turn_lane_handler.cpp b/src/extractor/guidance/turn_lane_handler.cpp
new file mode 100644
index 0000000..e1d2544
--- /dev/null
+++ b/src/extractor/guidance/turn_lane_handler.cpp
@@ -0,0 +1,538 @@
+#include "extractor/guidance/constants.hpp"
+#include "extractor/guidance/turn_discovery.hpp"
+#include "extractor/guidance/turn_lane_augmentation.hpp"
+#include "extractor/guidance/turn_lane_handler.hpp"
+#include "extractor/guidance/turn_lane_matcher.hpp"
+#include "util/simple_logger.hpp"
+#include "util/typedefs.hpp"
+
+#include <cstdint>
+
+#include <boost/algorithm/string/predicate.hpp>
+#include <boost/numeric/conversion/cast.hpp>
+
+namespace osrm
+{
+namespace extractor
+{
+namespace guidance
+{
+namespace lanes
+{
+
+namespace
+{
+std::size_t getNumberOfTurns(const Intersection &intersection)
+{
+    return std::count_if(intersection.begin(), intersection.end(), [](const ConnectedRoad &road) {
+        return road.entry_allowed;
+    });
+}
+} // namespace
+
+TurnLaneHandler::TurnLaneHandler(const util::NodeBasedDynamicGraph &node_based_graph,
+                                 const std::vector<std::uint32_t> &turn_lane_offsets,
+                                 const std::vector<TurnLaneType::Mask> &turn_lane_masks,
+                                 const std::vector<QueryNode> &node_info_list,
+                                 const TurnAnalysis &turn_analysis)
+    : node_based_graph(node_based_graph), turn_lane_offsets(turn_lane_offsets),
+      turn_lane_masks(turn_lane_masks), node_info_list(node_info_list), turn_analysis(turn_analysis)
+{
+}
+
+/*
+   Turn lanes are given in the form of strings that closely correspond to the direction modifiers
+   we use for our turn types. However, we still cannot simply perform a 1:1 assignment.
+
+   This function parses the turn_lane_descriptions of a format that describes an intersection as:
+
+    ----------
+    A -^
+    ----------
+    B -> -v
+    ----------
+    C -v
+    ----------
+
+    witch is the result of a string like looking |left|through;right|right| and performs an
+    assignment onto the turns.
+    For example: (130, turn slight right), (180, ramp straight), (320, turn sharp left).
+ */
+Intersection TurnLaneHandler::assignTurnLanes(const NodeID at,
+                                              const EdgeID via_edge,
+                                              Intersection intersection,
+                                              LaneDataIdMap &id_map) const
+{
+    //if only a uturn exists, there is nothing we can do
+    if( intersection.size() == 1 )
+        return intersection;
+
+    const auto &data = node_based_graph.GetEdgeData(via_edge);
+    // Extract a lane description for the ID
+
+    const auto turn_lane_description =
+        data.lane_description_id != INVALID_LANE_DESCRIPTIONID
+            ? TurnLaneDescription(
+                  turn_lane_masks.begin() + turn_lane_offsets[data.lane_description_id],
+                  turn_lane_masks.begin() + turn_lane_offsets[data.lane_description_id + 1])
+            : TurnLaneDescription();
+
+    BOOST_ASSERT( turn_lane_description.empty() || turn_lane_description.size() == (turn_lane_offsets[data.lane_description_id+1] - turn_lane_offsets[data.lane_description_id]));
+
+    // going straight, due to traffic signals, we can have uncompressed geometry
+    if (intersection.size() == 2 &&
+        ((data.lane_description_id != INVALID_LANE_DESCRIPTIONID &&
+          data.lane_description_id ==
+              node_based_graph.GetEdgeData(intersection[1].turn.eid).lane_description_id) ||
+         angularDeviation(intersection[1].turn.angle, STRAIGHT_ANGLE) < FUZZY_ANGLE_DIFFERENCE))
+        return intersection;
+
+    auto lane_data = laneDataFromDescription(turn_lane_description);
+
+    // if we see an invalid conversion, we stop immediately
+    if (!turn_lane_description.empty() && lane_data.empty())
+        return intersection;
+
+    // might be reasonable to handle multiple turns, if we know of a sequence of lanes
+    // e.g. one direction per lane, if three lanes and right, through, left available
+    if (!turn_lane_description.empty() && lane_data.size() == 1 &&
+        lane_data[0].tag == TurnLaneType::none)
+        return intersection;
+
+    const std::size_t possible_entries = getNumberOfTurns(intersection);
+
+    // merge does not justify an instruction
+    const bool has_merge_lane =
+        hasTag(TurnLaneType::merge_to_left | TurnLaneType::merge_to_right, lane_data);
+
+    // Dead end streets that don't have any left-tag. This can happen due to the fallbacks for
+    // broken data/barriers.
+    const bool has_non_usable_u_turn = (intersection[0].entry_allowed &&
+                                        !hasTag(TurnLaneType::none | TurnLaneType::left |
+                                                    TurnLaneType::sharp_left | TurnLaneType::uturn,
+                                                lane_data) &&
+                                        lane_data.size() + 1 == possible_entries);
+
+    if (has_merge_lane || has_non_usable_u_turn)
+        return intersection;
+
+    if (!lane_data.empty() && canMatchTrivially(intersection, lane_data) &&
+        lane_data.size() !=
+            static_cast<std::size_t>(
+                lane_data.back().tag != TurnLaneType::uturn && intersection[0].entry_allowed ? 1
+                                                                                             : 0) +
+                possible_entries &&
+        intersection[0].entry_allowed && !hasTag(TurnLaneType::none, lane_data))
+        lane_data.push_back({TurnLaneType::uturn, lane_data.back().to, lane_data.back().to});
+
+    bool is_simple = isSimpleIntersection(lane_data, intersection);
+    // simple intersections can be assigned directly
+    if (is_simple)
+    {
+        lane_data = handleNoneValueAtSimpleTurn(std::move(lane_data), intersection);
+        return simpleMatchTuplesToTurns(
+            std::move(intersection), lane_data, data.lane_description_id, id_map);
+    }
+    // if the intersection is not simple but we have lane data, we check for intersections with
+    // middle islands. We have two cases. The first one is providing lane data on the current
+    // segment and we only need to consider the part of the current segment. In this case we
+    // partition the data and only consider the first part.
+    else if (!lane_data.empty())
+    {
+        if (lane_data.size() >= possible_entries)
+        {
+            lane_data = partitionLaneData(node_based_graph.GetTarget(via_edge),
+                                          std::move(lane_data),
+                                          intersection)
+                            .first;
+
+            // check if we were successfull in trimming
+            if (lane_data.size() == possible_entries &&
+                isSimpleIntersection(lane_data, intersection))
+            {
+                lane_data = handleNoneValueAtSimpleTurn(std::move(lane_data), intersection);
+                return simpleMatchTuplesToTurns(
+                    std::move(intersection), lane_data, data.lane_description_id, id_map);
+            }
+        }
+    }
+    // The second part does not provide lane data on the current segment, but on the segment prior
+    // to the turn. We try to partition the data and only consider the second part.
+    else if (turn_lane_description.empty())
+    {
+        // acquire the lane data of a previous segment and, if possible, use it for the current
+        // intersection.
+        return handleTurnAtPreviousIntersection(at, via_edge, std::move(intersection), id_map);
+    }
+
+    return intersection;
+}
+
+// At segregated intersections, turn lanes will often only be specified up until the first turn. To
+// actually take the turn, we need to look back to the edge we drove onto the intersection with.
+Intersection TurnLaneHandler::handleTurnAtPreviousIntersection(const NodeID at,
+                                                               const EdgeID via_edge,
+                                                               Intersection intersection,
+                                                               LaneDataIdMap &id_map) const
+{
+    NodeID previous_node = SPECIAL_NODEID;
+    Intersection previous_intersection;
+    EdgeID previous_id = SPECIAL_EDGEID;
+    LaneDataVector lane_data;
+
+    // Get the previous lane string. We only accept strings that stem from a not-simple intersection
+    // and are not empty.
+    const auto previous_lane_description = [&]() -> TurnLaneDescription {
+        if (!findPreviousIntersection(at,
+                                      via_edge,
+                                      intersection,
+                                      turn_analysis,
+                                      node_based_graph,
+                                      previous_node,
+                                      previous_id,
+                                      previous_intersection))
+            return {};
+
+        BOOST_ASSERT(previous_id != SPECIAL_EDGEID);
+
+        const auto &previous_edge_data = node_based_graph.GetEdgeData(previous_id);
+        // TODO access correct data
+        const auto previous_description =
+            previous_edge_data.lane_description_id != INVALID_LANE_DESCRIPTIONID
+                ? TurnLaneDescription(
+                      turn_lane_masks.begin() +
+                          turn_lane_offsets[previous_edge_data.lane_description_id],
+                      turn_lane_masks.begin() +
+                          turn_lane_offsets[previous_edge_data.lane_description_id + 1])
+                : TurnLaneDescription();
+        if (previous_description.empty())
+            return previous_description;
+
+        previous_intersection = turn_analysis.assignTurnTypes(
+            previous_node, previous_id, std::move(previous_intersection));
+
+        lane_data = laneDataFromDescription(previous_description);
+
+        if (isSimpleIntersection(lane_data, previous_intersection))
+            return {};
+        else
+            return previous_description;
+    }();
+
+    // no lane string, no problems
+    if (previous_lane_description.empty())
+        return intersection;
+
+    // stop on invalid lane data conversion
+    if (lane_data.empty())
+        return intersection;
+
+    const auto &previous_data = node_based_graph.GetEdgeData(previous_id);
+    const auto is_simple = isSimpleIntersection(lane_data, intersection);
+    if (is_simple)
+    {
+        lane_data = handleNoneValueAtSimpleTurn(std::move(lane_data), intersection);
+        return simpleMatchTuplesToTurns(
+            std::move(intersection), lane_data, previous_data.lane_description_id, id_map);
+    }
+    else
+    {
+        if (lane_data.size() >= getNumberOfTurns(previous_intersection) &&
+            previous_intersection.size() != 2)
+        {
+            lane_data = partitionLaneData(node_based_graph.GetTarget(previous_id),
+                                          std::move(lane_data),
+                                          previous_intersection)
+                            .second;
+
+            std::sort(lane_data.begin(), lane_data.end());
+
+            // check if we were successfull in trimming
+            if (lane_data.size() == getNumberOfTurns(intersection) &&
+                isSimpleIntersection(lane_data, intersection))
+            {
+                lane_data = handleNoneValueAtSimpleTurn(std::move(lane_data), intersection);
+                return simpleMatchTuplesToTurns(
+                    std::move(intersection), lane_data, previous_data.lane_description_id, id_map);
+            }
+        }
+    }
+    return intersection;
+}
+
+/* A simple intersection does not depend on the next intersection coming up. This is important
+ * for turn lanes, since traffic signals and/or segregated a intersection can influence the
+ * interpretation of turn-lanes at a given turn.
+ *
+ * Here we check for a simple intersection. A simple intersection has a long enough segment
+ * followin the turn, offers no straight turn, or only non-trivial turn operations.
+ */
+bool TurnLaneHandler::isSimpleIntersection(const LaneDataVector &lane_data,
+                                           const Intersection &intersection) const
+{
+    if (lane_data.empty())
+        return false;
+    // if we are on a straight road, turn lanes are only reasonable in connection to the next
+    // intersection, or in case of a merge. If not all but one (straight) are merges, we don't
+    // consider the intersection simple
+    if (intersection.size() == 2)
+    {
+        return std::count_if(
+                   lane_data.begin(),
+                   lane_data.end(),
+                   [](const TurnLaneData &data) {
+                       return ((data.tag & TurnLaneType::merge_to_left) != TurnLaneType::empty) ||
+                              ((data.tag & TurnLaneType::merge_to_right) != TurnLaneType::empty);
+                   }) +
+                   std::size_t{1} >=
+               lane_data.size();
+    }
+
+    // in case an intersection offers far more lane data items than actual turns, some of them
+    // have
+    // to be for another intersection. A single additional item can be for an invalid bus lane.
+    const auto num_turns = [&]() {
+        auto count = getNumberOfTurns(intersection);
+        if (count < lane_data.size() && !intersection[0].entry_allowed &&
+            lane_data.back().tag == TurnLaneType::uturn)
+            return count + 1;
+        return count;
+    }();
+
+    // more than two additional lane data entries -> lanes target a different intersection
+    if (num_turns + std::size_t{2} <= lane_data.size())
+    {
+        return false;
+    }
+
+    // single additional lane data entry is alright, if it is none at the side. This usually
+    // refers to a bus-lane
+    if (num_turns + std::size_t{1} == lane_data.size() &&
+        lane_data.front().tag != TurnLaneType::none && lane_data.back().tag != TurnLaneType::none)
+    {
+        return false;
+    }
+
+    // more turns than lane data
+    if (num_turns > lane_data.size() &&
+        lane_data.end() ==
+            std::find_if(lane_data.begin(), lane_data.end(), [](const TurnLaneData &data) {
+                return data.tag == TurnLaneType::none;
+            }))
+    {
+        return false;
+    }
+
+    if (num_turns > lane_data.size() && intersection[0].entry_allowed &&
+        !(hasTag(TurnLaneType::uturn, lane_data) ||
+          (lane_data.back().tag != TurnLaneType::left &&
+           lane_data.back().tag != TurnLaneType::sharp_left)))
+    {
+        return false;
+    }
+
+    // check if we can find a valid 1:1 mapping in a straightforward manner
+    bool all_simple = true;
+    bool has_none = false;
+    std::unordered_set<std::size_t> matched_indices;
+    for (const auto &data : lane_data)
+    {
+        if (data.tag == TurnLaneType::none)
+        {
+            has_none = true;
+            continue;
+        }
+
+        const auto best_match = [&]() {
+            if (data.tag != TurnLaneType::uturn || lane_data.size() == 1)
+                return findBestMatch(data.tag, intersection);
+
+            // lane_data.size() > 1
+            if (lane_data.back().tag == TurnLaneType::uturn)
+                return findBestMatchForReverse(lane_data[lane_data.size() - 2].tag, intersection);
+
+            BOOST_ASSERT(lane_data.front().tag == TurnLaneType::uturn);
+            return findBestMatchForReverse(lane_data[1].tag, intersection);
+        }();
+        std::size_t match_index = std::distance(intersection.begin(), best_match);
+        all_simple &= (matched_indices.count(match_index) == 0);
+        matched_indices.insert(match_index);
+        // in case of u-turns, we might need to activate them first
+        all_simple &= (best_match->entry_allowed || match_index == 0);
+        all_simple &= isValidMatch(data.tag, best_match->turn.instruction);
+    }
+
+    // either all indices are matched, or we have a single none-value
+    if (all_simple && (matched_indices.size() == lane_data.size() ||
+                       (matched_indices.size() + 1 == lane_data.size() && has_none)))
+        return true;
+
+    // better save than sorry
+    return false;
+}
+
+std::pair<LaneDataVector, LaneDataVector> TurnLaneHandler::partitionLaneData(
+    const NodeID at, LaneDataVector turn_lane_data, const Intersection &intersection) const
+{
+    BOOST_ASSERT(turn_lane_data.size() >= getNumberOfTurns(intersection));
+    /*
+     * A Segregated intersection can provide turn lanes for turns that are not yet possible.
+     * The straightforward example would be coming up to the following situation:
+     *         (1)             (2)
+     *        | A |           | A |
+     *        | | |           | ^ |
+     *        | v |           | | |
+     * -------     -----------     ------
+     *  B ->-^                        B
+     * -------     -----------     ------
+     *  B ->-v                        B
+     * -------     -----------     ------
+     *        | A |           | A |
+     *
+     * Traveling on road B, we have to pass A at (1) to turn left onto A at (2). The turn
+     * lane itself may only be specified prior to (1) and/or could be repeated between (1)
+     * and (2). To make sure to announce the lane correctly, we need to treat the (in this
+     * case left) turn lane as if it were to continue straight onto the intersection and
+     * look back between (1) and (2) to make sure we find the correct lane for the left-turn.
+     *
+     * Intersections like these have two parts. Turns that can be made at the first intersection and
+     * turns that have to be made at the second. The partitioning returns the lane data split into
+     * two parts, one for the first and one for the second intersection.
+     */
+
+    // Try and maitch lanes to available turns. For Turns that are not directly matchable, check
+    // whether we can match them at the upcoming intersection.
+
+    const auto straightmost = findClosestTurn(intersection, STRAIGHT_ANGLE);
+
+    BOOST_ASSERT(straightmost < intersection.cend());
+
+    // we need to be able to enter the straightmost turn
+    if (!straightmost->entry_allowed)
+        return {turn_lane_data, {}};
+
+    std::vector<bool> matched_at_first(turn_lane_data.size(), false);
+    std::vector<bool> matched_at_second(turn_lane_data.size(), false);
+
+    // find out about the next intersection. To check for valid matches, we also need the turn types
+    auto next_intersection = turn_analysis.getIntersection(at, straightmost->turn.eid);
+    next_intersection =
+        turn_analysis.assignTurnTypes(at, straightmost->turn.eid, std::move(next_intersection));
+
+    // check where we can match turn lanes
+    std::size_t straightmost_tag_index = turn_lane_data.size();
+    for (std::size_t lane = 0; lane < turn_lane_data.size(); ++lane)
+    {
+        if ((turn_lane_data[lane].tag & (TurnLaneType::none | TurnLaneType::uturn)) !=
+            TurnLaneType::empty)
+            continue;
+
+        const auto best_match = findBestMatch(turn_lane_data[lane].tag, intersection);
+        if (isValidMatch(turn_lane_data[lane].tag, best_match->turn.instruction))
+        {
+            matched_at_first[lane] = true;
+
+            if (straightmost == best_match)
+                straightmost_tag_index = lane;
+        }
+
+        const auto best_match_at_next_intersection =
+            findBestMatch(turn_lane_data[lane].tag, next_intersection);
+        if (isValidMatch(turn_lane_data[lane].tag,
+                         best_match_at_next_intersection->turn.instruction))
+            matched_at_second[lane] = true;
+
+        // we need to match all items to either the current or the next intersection
+        if (!(matched_at_first[lane] || matched_at_second[lane]))
+            return {turn_lane_data, {}};
+    }
+
+    std::size_t none_index =
+        std::distance(turn_lane_data.begin(), findTag(TurnLaneType::none, turn_lane_data));
+
+    // if the turn lanes are pull forward, we might have to add an additional straight tag
+    // did we find something that matches against the straightmost road?
+    if (straightmost_tag_index == turn_lane_data.size())
+    {
+        if (none_index != turn_lane_data.size())
+            straightmost_tag_index = none_index;
+    }
+
+    // TODO handle reverse
+
+    // handle none values
+    if (none_index != turn_lane_data.size())
+    {
+        if (static_cast<std::size_t>(
+                std::count(matched_at_first.begin(), matched_at_first.end(), true)) <=
+            getNumberOfTurns(intersection))
+            matched_at_first[none_index] = true;
+
+        if (static_cast<std::size_t>(
+                std::count(matched_at_second.begin(), matched_at_second.end(), true)) <=
+            getNumberOfTurns(next_intersection))
+            matched_at_second[none_index] = true;
+    }
+
+    const auto augmentEntry = [&](TurnLaneData &data) {
+        for (std::size_t lane = 0; lane < turn_lane_data.size(); ++lane)
+            if (matched_at_second[lane])
+            {
+                data.from = std::min(turn_lane_data[lane].from, data.from);
+                data.to = std::max(turn_lane_data[lane].to, data.to);
+            }
+
+    };
+
+    LaneDataVector first, second;
+    for (std::size_t lane = 0; lane < turn_lane_data.size(); ++lane)
+    {
+
+        if (matched_at_second[lane])
+            second.push_back(turn_lane_data[lane]);
+
+        // augment straightmost at this intersection to match all turns that happen at the next
+        if (lane == straightmost_tag_index)
+        {
+            augmentEntry(turn_lane_data[straightmost_tag_index]);
+        }
+
+        if (matched_at_first[lane])
+            first.push_back(turn_lane_data[lane]);
+    }
+
+    if (straightmost_tag_index == turn_lane_data.size() &&
+        static_cast<std::size_t>(
+            std::count(matched_at_second.begin(), matched_at_second.end(), true)) ==
+            getNumberOfTurns(next_intersection))
+    {
+        TurnLaneData data = {TurnLaneType::straight, 255, 0};
+        augmentEntry(data);
+        first.push_back(data);
+        std::sort(first.begin(), first.end());
+    }
+
+    // TODO augment straightmost turn
+    return {std::move(first), std::move(second)};
+}
+
+Intersection TurnLaneHandler::simpleMatchTuplesToTurns(Intersection intersection,
+                                                       const LaneDataVector &lane_data,
+                                                       const LaneDescriptionID lane_description_id,
+                                                       LaneDataIdMap &id_map) const
+{
+    if (lane_data.empty() || !canMatchTrivially(intersection, lane_data))
+        return intersection;
+
+    BOOST_ASSERT(
+        !hasTag(TurnLaneType::none | TurnLaneType::merge_to_left | TurnLaneType::merge_to_right,
+                lane_data));
+
+    return triviallyMatchLanesToTurns(
+        std::move(intersection), lane_data, node_based_graph, lane_description_id, id_map);
+}
+
+} // namespace lanes
+} // namespace guidance
+} // namespace extractor
+} // namespace osrm
diff --git a/src/extractor/guidance/turn_lane_matcher.cpp b/src/extractor/guidance/turn_lane_matcher.cpp
new file mode 100644
index 0000000..7b0d413
--- /dev/null
+++ b/src/extractor/guidance/turn_lane_matcher.cpp
@@ -0,0 +1,255 @@
+#include "extractor/guidance/toolkit.hpp"
+#include "extractor/guidance/turn_lane_matcher.hpp"
+#include "util/guidance/toolkit.hpp"
+
+#include <boost/assert.hpp>
+#include <boost/numeric/conversion/cast.hpp>
+
+#include <functional>
+
+namespace osrm
+{
+namespace extractor
+{
+namespace guidance
+{
+namespace lanes
+{
+
+// Translate Turn Tags into a Matching Direction Modifier
+DirectionModifier::Enum getMatchingModifier(const TurnLaneType::Mask &tag)
+{
+    const constexpr TurnLaneType::Mask tag_by_modifier[] = {TurnLaneType::uturn,
+                                                            TurnLaneType::sharp_right,
+                                                            TurnLaneType::right,
+                                                            TurnLaneType::slight_right,
+                                                            TurnLaneType::straight,
+                                                            TurnLaneType::slight_left,
+                                                            TurnLaneType::left,
+                                                            TurnLaneType::sharp_left,
+                                                            TurnLaneType::merge_to_left,
+                                                            TurnLaneType::merge_to_right};
+    const auto index =
+        std::distance(tag_by_modifier, std::find(tag_by_modifier, tag_by_modifier + 10, tag));
+
+    BOOST_ASSERT(index <= 10);
+
+    const constexpr DirectionModifier::Enum modifiers[11] = {
+        DirectionModifier::UTurn,
+        DirectionModifier::SharpRight,
+        DirectionModifier::Right,
+        DirectionModifier::SlightRight,
+        DirectionModifier::Straight,
+        DirectionModifier::SlightLeft,
+        DirectionModifier::Left,
+        DirectionModifier::SharpLeft,
+        DirectionModifier::Straight,
+        DirectionModifier::Straight,
+        DirectionModifier::UTurn}; // fallback for invalid tags
+
+    return modifiers[index];
+}
+
+// check whether a match of a given tag and a turn instruction can be seen as valid
+bool isValidMatch(const TurnLaneType::Mask &tag, const TurnInstruction instruction)
+{
+    using util::guidance::hasLeftModifier;
+    using util::guidance::hasRightModifier;
+    const auto isMirroredModifier = [](const TurnInstruction instruction) {
+        return instruction.type == TurnType::Merge;
+    };
+
+    if (tag == TurnLaneType::uturn)
+    {
+        return hasLeftModifier(instruction) ||
+               instruction.direction_modifier == DirectionModifier::UTurn;
+    }
+    else if (tag == TurnLaneType::sharp_right || tag == TurnLaneType::right ||
+             tag == TurnLaneType::slight_right)
+    {
+        if (isMirroredModifier(instruction))
+            return hasLeftModifier(instruction);
+        else
+            // needs to be adjusted for left side driving
+            return leavesRoundabout(instruction) || hasRightModifier(instruction);
+    }
+    else if (tag == TurnLaneType::straight)
+    {
+        return instruction.direction_modifier == DirectionModifier::Straight ||
+               instruction.type == TurnType::Suppressed || instruction.type == TurnType::NewName ||
+               instruction.type == TurnType::StayOnRoundabout || entersRoundabout(instruction) ||
+               (instruction.type ==
+                    TurnType::Fork && // Forks can be experienced, even for straight segments
+                (instruction.direction_modifier == DirectionModifier::SlightLeft ||
+                 instruction.direction_modifier == DirectionModifier::SlightRight)) ||
+               (instruction.type ==
+                    TurnType::Continue && // Forks can be experienced, even for straight segments
+                (instruction.direction_modifier == DirectionModifier::SlightLeft ||
+                 instruction.direction_modifier == DirectionModifier::SlightRight)) ||
+               instruction.type == TurnType::UseLane;
+    }
+    else if (tag == TurnLaneType::slight_left || tag == TurnLaneType::left ||
+             tag == TurnLaneType::sharp_left)
+    {
+        if (isMirroredModifier(instruction))
+            return hasRightModifier(instruction);
+        else
+        {
+            // Needs to be fixed for left side driving
+            return (instruction.type == TurnType::StayOnRoundabout) || hasLeftModifier(instruction);
+        }
+    }
+    return false;
+}
+
+// Every tag is somewhat idealized in form of the expected angle. A through lane should go straight
+// (or follow a 180 degree turn angle between in/out segments.) The following function tries to find
+// the best possible match for every tag in a given intersection, considering a few corner cases
+// introduced to OSRM handling u-turns
+typename Intersection::const_iterator findBestMatch(const TurnLaneType::Mask &tag,
+                                                    const Intersection &intersection)
+{
+    const constexpr double idealized_turn_angles[] = {0, 35, 90, 135, 180, 225, 270, 315};
+    const auto idealized_angle = idealized_turn_angles[getMatchingModifier(tag)];
+    return std::min_element(
+        intersection.begin(),
+        intersection.end(),
+        [idealized_angle, &tag](const ConnectedRoad &lhs, const ConnectedRoad &rhs) {
+            // prefer valid matches
+            if (isValidMatch(tag, lhs.turn.instruction) != isValidMatch(tag, rhs.turn.instruction))
+                return isValidMatch(tag, lhs.turn.instruction);
+            // if the entry allowed flags don't match, we select the one with
+            // entry allowed set to true
+            if (lhs.entry_allowed != rhs.entry_allowed)
+                return lhs.entry_allowed;
+
+            return angularDeviation(idealized_angle, lhs.turn.angle) <
+                   angularDeviation(idealized_angle, rhs.turn.angle);
+        });
+}
+
+// Reverse is a special case, because it requires access to the leftmost tag. It has its own
+// matching function as a result of that. The leftmost tag is required, since u-turns are disabled
+// by default in OSRM. Therefor we cannot check whether a turn is allowed, since it could be
+// possible that it is forbidden. In addition, the best u-turn angle does not necessarily represent
+// the u-turn, since it could be a sharp-left turn instead on a road with a middle island.
+typename Intersection::const_iterator
+findBestMatchForReverse(const TurnLaneType::Mask &leftmost_tag, const Intersection &intersection)
+{
+    const auto leftmost_itr = findBestMatch(leftmost_tag, intersection);
+    if (leftmost_itr + 1 == intersection.cend())
+        return intersection.begin();
+
+    const constexpr double idealized_turn_angles[] = {0, 35, 90, 135, 180, 225, 270, 315};
+    const TurnLaneType::Mask tag = TurnLaneType::uturn;
+    const auto idealized_angle = idealized_turn_angles[getMatchingModifier(tag)];
+    return std::min_element(
+        intersection.begin() + std::distance(intersection.begin(), leftmost_itr),
+        intersection.end(),
+        [idealized_angle, &tag](const ConnectedRoad &lhs, const ConnectedRoad &rhs) {
+            // prefer valid matches
+            if (isValidMatch(tag, lhs.turn.instruction) != isValidMatch(tag, rhs.turn.instruction))
+                return isValidMatch(tag, lhs.turn.instruction);
+            // if the entry allowed flags don't match, we select the one with
+            // entry allowed set to true
+            if (lhs.entry_allowed != rhs.entry_allowed)
+                return lhs.entry_allowed;
+
+            return angularDeviation(idealized_angle, lhs.turn.angle) <
+                   angularDeviation(idealized_angle, rhs.turn.angle);
+        });
+}
+
+// a match is trivial if all turns can be associated with their best match in a valid way and the
+// matches occur in order
+bool canMatchTrivially(const Intersection &intersection, const LaneDataVector &lane_data)
+{
+    std::size_t road_index = 1, lane = 0;
+    for (; road_index < intersection.size() && lane < lane_data.size(); ++road_index)
+    {
+        if (intersection[road_index].entry_allowed)
+        {
+            BOOST_ASSERT(lane_data[lane].from != INVALID_LANEID);
+            if (!isValidMatch(lane_data[lane].tag, intersection[road_index].turn.instruction))
+                return false;
+
+            if (findBestMatch(lane_data[lane].tag, intersection) !=
+                intersection.begin() + road_index)
+                return false;
+
+            ++lane;
+        }
+    }
+    return lane == lane_data.size() ||
+           (lane + 1 == lane_data.size() && lane_data.back().tag == TurnLaneType::uturn);
+}
+
+Intersection triviallyMatchLanesToTurns(Intersection intersection,
+                                        const LaneDataVector &lane_data,
+                                        const util::NodeBasedDynamicGraph &node_based_graph,
+                                        const LaneDescriptionID lane_string_id,
+                                        LaneDataIdMap &lane_data_to_id)
+{
+    std::size_t road_index = 1, lane = 0;
+
+    const auto matchRoad = [&](ConnectedRoad &road, const TurnLaneData &data) {
+        LaneTupelIdPair key{{LaneID(data.to - data.from + 1), data.from}, lane_string_id};
+
+        auto lane_data_id = boost::numeric_cast<LaneDataID>(lane_data_to_id.size());
+        const auto it = lane_data_to_id.find(key);
+
+        if (it == lane_data_to_id.end())
+            lane_data_to_id.insert({key, lane_data_id});
+        else
+            lane_data_id = it->second;
+
+        // set lane id instead after the switch:
+        road.turn.lane_data_id = lane_data_id;
+    };
+
+    for (; road_index < intersection.size() && lane < lane_data.size(); ++road_index)
+    {
+        if (intersection[road_index].entry_allowed)
+        {
+            BOOST_ASSERT(lane_data[lane].from != INVALID_LANEID);
+            BOOST_ASSERT(
+                isValidMatch(lane_data[lane].tag, intersection[road_index].turn.instruction));
+            BOOST_ASSERT(findBestMatch(lane_data[lane].tag, intersection) ==
+                         intersection.begin() + road_index);
+
+            if (TurnType::Suppressed == intersection[road_index].turn.instruction.type)
+                intersection[road_index].turn.instruction.type = TurnType::UseLane;
+
+            matchRoad(intersection[road_index], lane_data[lane]);
+            ++lane;
+        }
+    }
+
+    // handle reverse tag, if present
+    if (lane + 1 == lane_data.size() && lane_data.back().tag == TurnLaneType::uturn)
+    {
+        std::size_t u_turn = 0;
+        if (node_based_graph.GetEdgeData(intersection[0].turn.eid).reversed)
+        {
+            if (intersection.back().entry_allowed ||
+                intersection.back().turn.instruction.direction_modifier !=
+                    DirectionModifier::SharpLeft)
+            {
+                // cannot match u-turn in a valid way
+                return intersection;
+            }
+            u_turn = intersection.size() - 1;
+        }
+        intersection[u_turn].entry_allowed = true;
+        intersection[u_turn].turn.instruction.type = TurnType::Turn;
+        intersection[u_turn].turn.instruction.direction_modifier = DirectionModifier::UTurn;
+
+        matchRoad(intersection[u_turn], lane_data.back());
+    }
+    return intersection;
+}
+
+} // namespace lane_matching
+} // namespace guidance
+} // namespace extractor
+} // namespace osrm
diff --git a/src/extractor/raster_source.cpp b/src/extractor/raster_source.cpp
index 33b7652..ee54b4a 100644
--- a/src/extractor/raster_source.cpp
+++ b/src/extractor/raster_source.cpp
@@ -84,10 +84,10 @@ int SourceContainer::LoadRasterSource(const std::string &path_string,
                                       std::size_t nrows,
                                       std::size_t ncols)
 {
-    const auto _xmin = static_cast<int>(util::toFixed(util::FloatLongitude(xmin)));
-    const auto _xmax = static_cast<int>(util::toFixed(util::FloatLongitude(xmax)));
-    const auto _ymin = static_cast<int>(util::toFixed(util::FloatLatitude(ymin)));
-    const auto _ymax = static_cast<int>(util::toFixed(util::FloatLatitude(ymax)));
+    const auto _xmin = static_cast<std::int32_t>(util::toFixed(util::FloatLongitude{xmin}));
+    const auto _xmax = static_cast<std::int32_t>(util::toFixed(util::FloatLongitude{xmax}));
+    const auto _ymin = static_cast<std::int32_t>(util::toFixed(util::FloatLatitude{ymin}));
+    const auto _ymax = static_cast<std::int32_t>(util::toFixed(util::FloatLatitude{ymax}));
 
     const auto itr = LoadedSourcePaths.find(path_string);
     if (itr != LoadedSourcePaths.end())
@@ -135,8 +135,8 @@ RasterDatum SourceContainer::GetRasterDataFromSource(unsigned int source_id, dou
     BOOST_ASSERT(lon > -180);
 
     const auto &found = LoadedSources[source_id];
-    return found.GetRasterData(static_cast<int>(util::toFixed(util::FloatLongitude(lon))),
-                               static_cast<int>(util::toFixed(util::FloatLatitude(lat))));
+    return found.GetRasterData(static_cast<std::int32_t>(util::toFixed(util::FloatLongitude{lon})),
+                               static_cast<std::int32_t>(util::toFixed(util::FloatLatitude{lat})));
 }
 
 // External function for looking up interpolated data from a specified source
@@ -154,8 +154,8 @@ SourceContainer::GetRasterInterpolateFromSource(unsigned int source_id, double l
     BOOST_ASSERT(lon > -180);
 
     const auto &found = LoadedSources[source_id];
-    return found.GetRasterInterpolate(static_cast<int>(util::toFixed(util::FloatLongitude(lon))),
-                                      static_cast<int>(util::toFixed(util::FloatLatitude(lat))));
+    return found.GetRasterInterpolate(static_cast<std::int32_t>(util::toFixed(util::FloatLongitude{lon})),
+                                      static_cast<std::int32_t>(util::toFixed(util::FloatLatitude{lat})));
 }
 }
 }
diff --git a/src/extractor/scripting_environment.cpp b/src/extractor/scripting_environment.cpp
index cfdbb57..899537e 100644
--- a/src/extractor/scripting_environment.cpp
+++ b/src/extractor/scripting_environment.cpp
@@ -77,6 +77,7 @@ void ScriptingEnvironment::InitContext(ScriptingEnvironment::Context &context)
     luabind::module(context.state)
         [luabind::def("durationIsValid", durationIsValid),
          luabind::def("parseDuration", parseDuration),
+         luabind::def("trimLaneString", trimLaneString),
          luabind::class_<TravelMode>("mode").enum_(
              "enums")[luabind::value("inaccessible", TRAVEL_MODE_INACCESSIBLE),
                       luabind::value("driving", TRAVEL_MODE_DRIVING),
@@ -141,6 +142,8 @@ void ScriptingEnvironment::InitContext(ScriptingEnvironment::Context &context)
              .def_readwrite("is_access_restricted", &ExtractionWay::is_access_restricted)
              .def_readwrite("is_startpoint", &ExtractionWay::is_startpoint)
              .def_readwrite("duration", &ExtractionWay::duration)
+             .def_readwrite("turn_lanes_forward", &ExtractionWay::turn_lanes_forward)
+             .def_readwrite("turn_lanes_backward", &ExtractionWay::turn_lanes_backward)
              .property(
                  "forward_mode", &ExtractionWay::get_forward_mode, &ExtractionWay::set_forward_mode)
              .property("backward_mode",
diff --git a/src/server/request_handler.cpp b/src/server/request_handler.cpp
index 7e49b5e..38d5484 100644
--- a/src/server/request_handler.cpp
+++ b/src/server/request_handler.cpp
@@ -49,6 +49,7 @@ void RequestHandler::HandleRequest(const http::request &current_request, http::r
     {
         std::string request_string;
         util::URIDecode(current_request.uri, request_string);
+        util::SimpleLogger().Write(logDEBUG) << "req: " << request_string;
 
         auto api_iterator = request_string.begin();
         auto maybe_parsed_url = api::parseURL(api_iterator, request_string.end());
diff --git a/src/storage/storage.cpp b/src/storage/storage.cpp
index ca519d2..119549d 100644
--- a/src/storage/storage.cpp
+++ b/src/storage/storage.cpp
@@ -1,4 +1,3 @@
-#include "storage/storage.hpp"
 #include "contractor/query_edge.hpp"
 #include "extractor/compressed_edge_container.hpp"
 #include "extractor/guidance/turn_instruction.hpp"
@@ -9,6 +8,7 @@
 #include "storage/shared_barriers.hpp"
 #include "storage/shared_datatype.hpp"
 #include "storage/shared_memory.hpp"
+#include "storage/storage.hpp"
 #include "engine/datafacade/datafacade_base.hpp"
 #include "util/coordinate.hpp"
 #include "util/exception.hpp"
@@ -147,6 +147,18 @@ int Storage::Run()
     name_stream.read((char *)&number_of_chars, sizeof(unsigned));
     shared_layout_ptr->SetBlockSize<char>(SharedDataLayout::NAME_CHAR_LIST, number_of_chars);
 
+    std::vector<std::uint32_t> lane_description_offsets;
+    std::vector<extractor::guidance::TurnLaneType::Mask> lane_description_masks;
+    if (!util::deserializeAdjacencyArray(config.turn_lane_description_path.string(),
+                                         lane_description_offsets,
+                                         lane_description_masks))
+        throw util::exception("Failed to read lane descriptions from: " +
+                              config.turn_lane_description_path.string());
+    shared_layout_ptr->SetBlockSize<std::uint32_t>(SharedDataLayout::LANE_DESCRIPTION_OFFSETS,
+                                                   lane_description_offsets.size());
+    shared_layout_ptr->SetBlockSize<extractor::guidance::TurnLaneType::Mask>(
+        SharedDataLayout::LANE_DESCRIPTION_MASKS, lane_description_masks.size());
+
     // Loading information for original edges
     boost::filesystem::ifstream edges_input_stream(config.edges_data_path, std::ios::binary);
     if (!edges_input_stream)
@@ -166,6 +178,8 @@ int Storage::Run()
                                                            number_of_original_edges);
     shared_layout_ptr->SetBlockSize<extractor::guidance::TurnInstruction>(
         SharedDataLayout::TURN_INSTRUCTION, number_of_original_edges);
+    shared_layout_ptr->SetBlockSize<LaneDataID>(SharedDataLayout::LANE_DATA_ID,
+                                                number_of_original_edges);
     shared_layout_ptr->SetBlockSize<EntryClassID>(SharedDataLayout::ENTRY_CLASSID,
                                                   number_of_original_edges);
 
@@ -247,8 +261,9 @@ int Storage::Run()
                                                       coordinate_list_size);
     // we'll read a list of OSM node IDs from the same data, so set the block size for the same
     // number of items:
-    shared_layout_ptr->SetBlockSize<std::uint64_t>(SharedDataLayout::OSM_NODE_ID_LIST,
-                                                   util::PackedVector<OSMNodeID>::elements_to_blocks(coordinate_list_size));
+    shared_layout_ptr->SetBlockSize<std::uint64_t>(
+        SharedDataLayout::OSM_NODE_ID_LIST,
+        util::PackedVector<OSMNodeID>::elements_to_blocks(coordinate_list_size));
 
     // load geometries sizes
     boost::filesystem::ifstream geometry_input_stream(config.geometries_path, std::ios::binary);
@@ -360,13 +375,21 @@ int Storage::Run()
     }
 
     std::uint64_t num_bearings;
-    intersection_stream >> num_bearings;
+    intersection_stream.read(reinterpret_cast<char*>(&num_bearings),sizeof(num_bearings));
 
     std::vector<DiscreteBearing> bearing_class_table(num_bearings);
     intersection_stream.read(reinterpret_cast<char *>(&bearing_class_table[0]),
                              sizeof(bearing_class_table[0]) * num_bearings);
     shared_layout_ptr->SetBlockSize<DiscreteBearing>(SharedDataLayout::BEARING_VALUES,
                                                      num_bearings);
+
+    // Loading turn lane data
+    boost::filesystem::ifstream lane_data_stream(config.turn_lane_data_path, std::ios::binary);
+    std::uint64_t lane_tupel_count = 0;
+    lane_data_stream.read(reinterpret_cast<char *>(&lane_tupel_count), sizeof(lane_tupel_count));
+    shared_layout_ptr->SetBlockSize<util::guidance::LaneTupelIdPair>(
+        SharedDataLayout::TURN_LANE_DATA, lane_tupel_count);
+
     if (!static_cast<bool>(intersection_stream))
         throw util::exception("Failed to read bearing values from " +
                               config.intersection_class_path.string());
@@ -423,10 +446,10 @@ int Storage::Run()
 
     char *name_char_ptr = shared_layout_ptr->GetBlockPtr<char, true>(
         shared_memory_ptr, SharedDataLayout::NAME_CHAR_LIST);
-    unsigned temp_length;
+    unsigned temp_length = 0;
     name_stream.read((char *)&temp_length, sizeof(unsigned));
 
-    BOOST_ASSERT_MSG(temp_length ==
+    BOOST_ASSERT_MSG(shared_layout_ptr->AlignBlockSize(temp_length) ==
                          shared_layout_ptr->GetBlockSize(SharedDataLayout::NAME_CHAR_LIST),
                      "Name file corrupted!");
 
@@ -435,9 +458,43 @@ int Storage::Run()
         name_stream.read(name_char_ptr,
                          shared_layout_ptr->GetBlockSize(SharedDataLayout::NAME_CHAR_LIST));
     }
-
     name_stream.close();
 
+    // make sure do write canary...
+    auto *turn_lane_data_ptr =
+        shared_layout_ptr->GetBlockPtr<util::guidance::LaneTupelIdPair, true>(
+            shared_memory_ptr, SharedDataLayout::TURN_LANE_DATA);
+    if (shared_layout_ptr->GetBlockSize(SharedDataLayout::TURN_LANE_DATA) > 0)
+    {
+        lane_data_stream.read(reinterpret_cast<char *>(turn_lane_data_ptr),
+                              shared_layout_ptr->GetBlockSize(SharedDataLayout::TURN_LANE_DATA));
+    }
+    lane_data_stream.close();
+
+    auto *turn_lane_offset_ptr = shared_layout_ptr->GetBlockPtr<std::uint32_t, true>(
+        shared_memory_ptr, SharedDataLayout::LANE_DESCRIPTION_OFFSETS);
+    if (!lane_description_offsets.empty())
+    {
+        BOOST_ASSERT(shared_layout_ptr->GetBlockSize(SharedDataLayout::LANE_DESCRIPTION_OFFSETS) >=
+                     sizeof(lane_description_offsets[0]) * lane_description_offsets.size());
+        std::copy(
+            lane_description_offsets.begin(), lane_description_offsets.end(), turn_lane_offset_ptr);
+        std::vector<std::uint32_t> tmp;
+        lane_description_offsets.swap(tmp);
+    }
+
+    auto *turn_lane_mask_ptr =
+        shared_layout_ptr->GetBlockPtr<extractor::guidance::TurnLaneType::Mask, true>(
+            shared_memory_ptr, SharedDataLayout::LANE_DESCRIPTION_MASKS);
+    if (!lane_description_masks.empty())
+    {
+        BOOST_ASSERT(shared_layout_ptr->GetBlockSize(SharedDataLayout::LANE_DESCRIPTION_MASKS) >=
+                     sizeof(lane_description_masks[0]) * lane_description_masks.size());
+        std::copy(lane_description_masks.begin(), lane_description_masks.end(), turn_lane_mask_ptr);
+        std::vector<extractor::guidance::TurnLaneType::Mask> tmp;
+        lane_description_masks.swap(tmp);
+    }
+
     // load original edge information
     NodeID *via_node_ptr = shared_layout_ptr->GetBlockPtr<NodeID, true>(
         shared_memory_ptr, SharedDataLayout::VIA_NODE_LIST);
@@ -449,6 +506,9 @@ int Storage::Run()
         shared_layout_ptr->GetBlockPtr<extractor::TravelMode, true>(shared_memory_ptr,
                                                                     SharedDataLayout::TRAVEL_MODE);
 
+    LaneDataID *lane_data_id_ptr = shared_layout_ptr->GetBlockPtr<LaneDataID, true>(
+        shared_memory_ptr, SharedDataLayout::LANE_DATA_ID);
+
     extractor::guidance::TurnInstruction *turn_instructions_ptr =
         shared_layout_ptr->GetBlockPtr<extractor::guidance::TurnInstruction, true>(
             shared_memory_ptr, SharedDataLayout::TURN_INSTRUCTION);
@@ -463,6 +523,7 @@ int Storage::Run()
         via_node_ptr[i] = current_edge_data.via_node;
         name_id_ptr[i] = current_edge_data.name_id;
         travel_mode_ptr[i] = current_edge_data.travel_mode;
+        lane_data_id_ptr[i] = current_edge_data.lane_data_id;
         turn_instructions_ptr[i] = current_edge_data.turn_instruction;
         entry_class_id_ptr[i] = current_edge_data.entry_classid;
     }
@@ -513,8 +574,9 @@ int Storage::Run()
         shared_memory_ptr, SharedDataLayout::DATASOURCE_NAME_DATA);
     if (shared_layout_ptr->GetBlockSize(SharedDataLayout::DATASOURCE_NAME_DATA) > 0)
     {
-        std::cout << "Copying " << (m_datasource_name_data.end() - m_datasource_name_data.begin())
-                  << " chars into name data ptr\n";
+        util::SimpleLogger().Write()
+            << "Copying " << (m_datasource_name_data.end() - m_datasource_name_data.begin())
+            << " chars into name data ptr";
         std::copy(
             m_datasource_name_data.begin(), m_datasource_name_data.end(), datasource_name_data_ptr);
     }
@@ -551,7 +613,7 @@ int Storage::Run()
     {
         nodes_input_stream.read((char *)&current_node, sizeof(extractor::QueryNode));
         coordinates_ptr[i] = util::Coordinate(current_node.lon, current_node.lat);
-        osmnodeid_list.push_back(OSMNodeID(current_node.node_id));
+        osmnodeid_list.push_back(current_node.node_id);
     }
     nodes_input_stream.close();
 
diff --git a/src/storage/storage_config.cpp b/src/storage/storage_config.cpp
index c74d296..c1edaf6 100644
--- a/src/storage/storage_config.cpp
+++ b/src/storage/storage_config.cpp
@@ -16,7 +16,8 @@ StorageConfig::StorageConfig(const boost::filesystem::path &base)
       datasource_names_path{base.string() + ".datasource_names"},
       datasource_indexes_path{base.string() + ".datasource_indexes"},
       names_data_path{base.string() + ".names"}, properties_path{base.string() + ".properties"},
-      intersection_class_path{base.string() + ".icd"}
+      intersection_class_path{base.string() + ".icd"}, turn_lane_data_path{base.string() + ".tld"},
+      turn_lane_description_path{base.string() + ".tls"}
 {
 }
 
diff --git a/src/tools/store.cpp b/src/tools/store.cpp
index c0f98ba..c249d88 100644
--- a/src/tools/store.cpp
+++ b/src/tools/store.cpp
@@ -16,8 +16,7 @@ bool generateDataStoreOptions(const int argc,
 {
     // declare a group of options that will be allowed only on command line
     boost::program_options::options_description generic_options("Options");
-    generic_options.add_options()("version,v", "Show version")("help,h", "Show this help message")(
-        "springclean,s", "Remove all regions in shared memory");
+    generic_options.add_options()("version,v", "Show version")("help,h", "Show this help message");
 
     // declare a group of options that will be allowed both on command line
     // as well as in a config file
diff --git a/src/util/assert.cpp b/src/util/assert.cpp
index 7051784..03c37ad 100644
--- a/src/util/assert.cpp
+++ b/src/util/assert.cpp
@@ -1,16 +1,17 @@
-#include "util/assert.hpp"
+#include <boost/assert.hpp>
 
-#include <sstream>
+#include <exception>
+#include <iostream>
 
 namespace
 {
-// We throw to guarantee for stack-unwinding and therefore our destructors being called.
+// We hard-abort on assertion violations.
 void assertion_failed_msg_helper(
     char const *expr, char const *msg, char const *function, char const *file, long line)
 {
-    std::ostringstream fmt;
-    fmt << file << ":" << line << "\nin: " << function << ": " << expr << "\n" << msg;
-    throw osrm::util::assertionError{fmt.str().c_str()};
+    std::cerr << "[assert] " << file << ":" << line << "\nin: " << function << ": " << expr << "\n"
+              << msg;
+    std::terminate();
 }
 }
 
diff --git a/src/util/coordinate.cpp b/src/util/coordinate.cpp
index bd1d251..c398037 100644
--- a/src/util/coordinate.cpp
+++ b/src/util/coordinate.cpp
@@ -19,16 +19,16 @@ namespace util
 
 bool Coordinate::IsValid() const
 {
-    return !(lat > FixedLatitude(90 * COORDINATE_PRECISION) ||
-             lat < FixedLatitude(-90 * COORDINATE_PRECISION) ||
-             lon > FixedLongitude(180 * COORDINATE_PRECISION) ||
-             lon < FixedLongitude(-180 * COORDINATE_PRECISION));
+    return !(lat > FixedLatitude{static_cast<std::int32_t>(90 * COORDINATE_PRECISION)} ||
+             lat < FixedLatitude{static_cast<std::int32_t>(-90 * COORDINATE_PRECISION)} ||
+             lon > FixedLongitude{static_cast<std::int32_t>(180 * COORDINATE_PRECISION)} ||
+             lon < FixedLongitude{static_cast<std::int32_t>(-180 * COORDINATE_PRECISION)});
 }
 
 bool FloatCoordinate::IsValid() const
 {
-    return !(lat > FloatLatitude(90) || lat < FloatLatitude(-90) || lon > FloatLongitude(180) ||
-             lon < FloatLongitude(-180));
+    return !(lat > FloatLatitude{90} || lat < FloatLatitude{-90} || lon > FloatLongitude{180} ||
+             lon < FloatLongitude{-180});
 }
 
 bool operator==(const Coordinate lhs, const Coordinate rhs)
diff --git a/src/util/coordinate_calculation.cpp b/src/util/coordinate_calculation.cpp
index b2bdd3e..029129b 100644
--- a/src/util/coordinate_calculation.cpp
+++ b/src/util/coordinate_calculation.cpp
@@ -115,8 +115,8 @@ Coordinate centroid(const Coordinate lhs, const Coordinate rhs)
     Coordinate centroid;
     // The coordinates of the midpoints are given by:
     // x = (x1 + x2) /2 and y = (y1 + y2) /2.
-    centroid.lon = (lhs.lon + rhs.lon) / FixedLongitude(2);
-    centroid.lat = (lhs.lat + rhs.lat) / FixedLatitude(2);
+    centroid.lon = (lhs.lon + rhs.lon) / FixedLongitude{2};
+    centroid.lat = (lhs.lat + rhs.lat) / FixedLatitude{2};
     return centroid;
 }
 
@@ -261,7 +261,7 @@ circleCenter(const Coordinate C1, const Coordinate C2, const Coordinate C3)
         if (lon < -180.0 || lon > 180.0 || lat < -90.0 || lat > 90.0)
             return boost::none;
         else
-            return Coordinate(FloatLongitude(lon), FloatLatitude(lat));
+            return Coordinate(FloatLongitude{lon}, FloatLatitude{lat});
     }
 }
 
@@ -279,10 +279,13 @@ Coordinate interpolateLinear(double factor, const Coordinate from, const Coordin
 {
     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)));
+    const auto from_lon = static_cast<std::int32_t>(from.lon);
+    const auto from_lat = static_cast<std::int32_t>(from.lat);
+    const auto to_lon = static_cast<std::int32_t>(to.lon);
+    const auto to_lat = static_cast<std::int32_t>(to.lat);
+
+    FixedLongitude interpolated_lon{static_cast<std::int32_t>(from_lon + factor * (to_lon - from_lon))};
+    FixedLatitude interpolated_lat{static_cast<std::int32_t>(from_lat + factor * (to_lat - from_lat))};
 
     return {std::move(interpolated_lon), std::move(interpolated_lat)};
 }
diff --git a/src/util/guidance/turn_lanes.cpp b/src/util/guidance/turn_lanes.cpp
new file mode 100644
index 0000000..c7c45a8
--- /dev/null
+++ b/src/util/guidance/turn_lanes.cpp
@@ -0,0 +1,36 @@
+#include "util/guidance/turn_lanes.hpp"
+
+#include <algorithm>
+#include <iostream>
+#include <tuple>
+
+#include <boost/assert.hpp>
+
+namespace osrm
+{
+namespace util
+{
+namespace guidance
+{
+LaneTupel::LaneTupel() : lanes_in_turn(0), first_lane_from_the_right(INVALID_LANEID)
+{
+    // basic constructor, set everything to zero
+}
+
+LaneTupel::LaneTupel(const LaneID lanes_in_turn, const LaneID first_lane_from_the_right)
+    : lanes_in_turn(lanes_in_turn), first_lane_from_the_right(first_lane_from_the_right)
+{
+}
+
+// comparation based on interpretation as unsigned 32bit integer
+bool LaneTupel::operator==(const LaneTupel other) const
+{
+    return std::tie(lanes_in_turn, first_lane_from_the_right) ==
+           std::tie(other.lanes_in_turn, other.first_lane_from_the_right);
+}
+
+bool LaneTupel::operator!=(const LaneTupel other) const { return !(*this == other); }
+
+} // namespace guidance
+} // namespace util
+} // namespace osrm
diff --git a/src/util/name_table.cpp b/src/util/name_table.cpp
index 1a2e407..e15a35f 100644
--- a/src/util/name_table.cpp
+++ b/src/util/name_table.cpp
@@ -36,11 +36,13 @@ NameTable::NameTable(const std::string &filename)
     }
     else
     {
-        util::SimpleLogger().Write(logWARNING) << "list of street names is empty";
+        util::SimpleLogger().Write(logINFO)
+            << "list of street names is empty in construction of name table from: \"" << filename
+            << "\"";
     }
     if (!name_stream)
-        throw exception("Failed to read " + std::to_string(number_of_chars) +
-                        " characters from file.");
+        throw exception("Failed to read " + std::to_string(number_of_chars) + " characters from " +
+                        filename);
 }
 
 std::string NameTable::GetNameForID(const unsigned name_id) const
diff --git a/taginfo.json b/taginfo.json
index 8d3df9d..f83dc0d 100644
--- a/taginfo.json
+++ b/taginfo.json
@@ -201,6 +201,36 @@
             "description": "Name of road for navigation instructions."
         },
         {
+            "key": "turn:lanes",
+            "object_types": [ "way" ],
+            "description": "Turn Lanes for lane guidance."
+        },
+        {
+            "key": "turn:lanes:forward",
+            "object_types": [ "way" ],
+            "description": "Turn Lanes for lane guidance."
+        },
+        {
+            "key": "turn:lanes:backward",
+            "object_types": [ "way" ],
+            "description": "Turn Lanes for lane guidance."
+        },
+         {
+            "key": "lanes:psv",
+            "object_types": [ "way" ],
+            "description": "Turn Lanes for lane guidance."
+        },
+        {
+            "key": "lanes:psv:forward",
+            "object_types": [ "way" ],
+            "description": "Turn Lanes for lane guidance."
+        },
+        {
+            "key": "lanes:psv:backward",
+            "object_types": [ "way" ],
+            "description": "Turn Lanes for lane guidance."
+        },
+        {
             "key": "ref",
             "object_types": [ "way" ],
             "description": "Ref of road for navigation instructions, overrides name."
diff --git a/unit_tests/engine/douglas_peucker.cpp b/unit_tests/engine/douglas_peucker.cpp
index 6b43823..2328921 100644
--- a/unit_tests/engine/douglas_peucker.cpp
+++ b/unit_tests/engine/douglas_peucker.cpp
@@ -33,10 +33,10 @@ BOOST_AUTO_TEST_CASE(removed_middle_test)
         x       x
     */
     std::vector<util::Coordinate> coordinates = {
-        util::Coordinate(util::FloatLongitude(5), 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))};
+        util::Coordinate{util::FloatLongitude{5}, 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++)
     {
@@ -58,10 +58,10 @@ BOOST_AUTO_TEST_CASE(removed_middle_test_zoom_sensitive)
         x       x
     */
     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(20), util::FloatLatitude(20)),
-        util::Coordinate(util::FloatLongitude(25), util::FloatLatitude(5))};
+        util::Coordinate{util::FloatLongitude{5}, util::FloatLatitude{5}},
+        util::Coordinate{util::FloatLongitude{6}, util::FloatLatitude{6}},
+        util::Coordinate{util::FloatLongitude{20}, util::FloatLatitude{20}},
+        util::Coordinate{util::FloatLongitude{25}, util::FloatLatitude{5}}};
 
     // 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
@@ -98,13 +98,13 @@ BOOST_AUTO_TEST_CASE(remove_second_node_test)
                 x
         */
         std::vector<util::Coordinate> input = {
-            util::Coordinate(util::FloatLongitude(5), util::FloatLatitude(5)),
-            util::Coordinate(util::FloatLongitude(5 + 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)))};
+            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(), 3);
diff --git a/unit_tests/engine/geometry_string.cpp b/unit_tests/engine/geometry_string.cpp
index 8f78810..bdb0c37 100644
--- a/unit_tests/engine/geometry_string.cpp
+++ b/unit_tests/engine/geometry_string.cpp
@@ -22,11 +22,11 @@ BOOST_AUTO_TEST_CASE(decode)
 
     // Test coordinates; these would be the coordinates we give the loc parameter,
     // e.g. loc=10.00,10.0&loc=10.01,10.1...
-    util::Coordinate coord1(util::FloatLongitude(10.0), util::FloatLatitude(10.00));
-    util::Coordinate coord2(util::FloatLongitude(10.1), util::FloatLatitude(10.01));
-    util::Coordinate coord3(util::FloatLongitude(10.2), util::FloatLatitude(10.02));
-    util::Coordinate coord4(util::FloatLongitude(10.3), util::FloatLatitude(10.03));
-    util::Coordinate coord5(util::FloatLongitude(10.4), util::FloatLatitude(10.04));
+    util::Coordinate coord1(util::FloatLongitude{10.0}, util::FloatLatitude{10.00});
+    util::Coordinate coord2(util::FloatLongitude{10.1}, util::FloatLatitude{10.01});
+    util::Coordinate coord3(util::FloatLongitude{10.2}, util::FloatLatitude{10.02});
+    util::Coordinate coord4(util::FloatLongitude{10.3}, util::FloatLatitude{10.03});
+    util::Coordinate coord5(util::FloatLongitude{10.4}, util::FloatLatitude{10.04});
 
     // Put the test coordinates into the vector for comparison
     std::vector<util::Coordinate> cmp_coords = {coord1, coord2, coord3, coord4, coord5};
diff --git a/unit_tests/extractor/graph_compressor.cpp b/unit_tests/extractor/graph_compressor.cpp
index 98dd33f..7ef65ae 100644
--- a/unit_tests/extractor/graph_compressor.cpp
+++ b/unit_tests/extractor/graph_compressor.cpp
@@ -30,14 +30,94 @@ BOOST_AUTO_TEST_CASE(long_road_test)
 
     std::vector<InputEdge> edges = {
         // src, tgt, dist, edge_id, name_id, access_restricted, fwd, bkwd, roundabout, travel_mode
-        {0, 1, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE},
-        {1, 0, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE},
-        {1, 2, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE},
-        {2, 1, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE},
-        {2, 3, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE},
-        {3, 2, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE},
-        {3, 4, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE},
-        {4, 3, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE}};
+        {0,
+         1,
+         1,
+         SPECIAL_EDGEID,
+         0,
+         false,
+         false,
+         false,
+         true,
+         TRAVEL_MODE_INACCESSIBLE,
+         INVALID_LANE_DESCRIPTIONID},
+        {1,
+         0,
+         1,
+         SPECIAL_EDGEID,
+         0,
+         false,
+         false,
+         false,
+         true,
+         TRAVEL_MODE_INACCESSIBLE,
+         INVALID_LANE_DESCRIPTIONID},
+        {1,
+         2,
+         1,
+         SPECIAL_EDGEID,
+         0,
+         false,
+         false,
+         false,
+         true,
+         TRAVEL_MODE_INACCESSIBLE,
+         INVALID_LANE_DESCRIPTIONID},
+        {2,
+         1,
+         1,
+         SPECIAL_EDGEID,
+         0,
+         false,
+         false,
+         false,
+         true,
+         TRAVEL_MODE_INACCESSIBLE,
+         INVALID_LANE_DESCRIPTIONID},
+        {2,
+         3,
+         1,
+         SPECIAL_EDGEID,
+         0,
+         false,
+         false,
+         false,
+         true,
+         TRAVEL_MODE_INACCESSIBLE,
+         INVALID_LANE_DESCRIPTIONID},
+        {3,
+         2,
+         1,
+         SPECIAL_EDGEID,
+         0,
+         false,
+         false,
+         false,
+         true,
+         TRAVEL_MODE_INACCESSIBLE,
+         INVALID_LANE_DESCRIPTIONID},
+        {3,
+         4,
+         1,
+         SPECIAL_EDGEID,
+         0,
+         false,
+         false,
+         false,
+         true,
+         TRAVEL_MODE_INACCESSIBLE,
+         INVALID_LANE_DESCRIPTIONID},
+        {4,
+         3,
+         1,
+         SPECIAL_EDGEID,
+         0,
+         false,
+         false,
+         false,
+         true,
+         TRAVEL_MODE_INACCESSIBLE,
+         INVALID_LANE_DESCRIPTIONID}};
 
     BOOST_ASSERT(edges[0].data.IsCompatibleTo(edges[2].data));
     BOOST_ASSERT(edges[2].data.IsCompatibleTo(edges[4].data));
@@ -69,18 +149,138 @@ BOOST_AUTO_TEST_CASE(loop_test)
 
     std::vector<InputEdge> edges = {
         // src, tgt, dist, edge_id, name_id, access_restricted, fwd, bkwd, roundabout, travel_mode
-        {0, 1, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE},
-        {0, 5, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE},
-        {1, 0, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE},
-        {1, 2, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE},
-        {2, 1, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE},
-        {2, 3, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE},
-        {3, 2, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE},
-        {3, 4, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE},
-        {4, 3, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE},
-        {4, 5, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE},
-        {5, 0, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE},
-        {5, 4, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE},
+        {0,
+         1,
+         1,
+         SPECIAL_EDGEID,
+         0,
+         false,
+         false,
+         false,
+         true,
+         TRAVEL_MODE_INACCESSIBLE,
+         INVALID_LANE_DESCRIPTIONID},
+        {0,
+         5,
+         1,
+         SPECIAL_EDGEID,
+         0,
+         false,
+         false,
+         false,
+         true,
+         TRAVEL_MODE_INACCESSIBLE,
+         INVALID_LANE_DESCRIPTIONID},
+        {1,
+         0,
+         1,
+         SPECIAL_EDGEID,
+         0,
+         false,
+         false,
+         false,
+         true,
+         TRAVEL_MODE_INACCESSIBLE,
+         INVALID_LANE_DESCRIPTIONID},
+        {1,
+         2,
+         1,
+         SPECIAL_EDGEID,
+         0,
+         false,
+         false,
+         false,
+         true,
+         TRAVEL_MODE_INACCESSIBLE,
+         INVALID_LANE_DESCRIPTIONID},
+        {2,
+         1,
+         1,
+         SPECIAL_EDGEID,
+         0,
+         false,
+         false,
+         false,
+         true,
+         TRAVEL_MODE_INACCESSIBLE,
+         INVALID_LANE_DESCRIPTIONID},
+        {2,
+         3,
+         1,
+         SPECIAL_EDGEID,
+         0,
+         false,
+         false,
+         false,
+         true,
+         TRAVEL_MODE_INACCESSIBLE,
+         INVALID_LANE_DESCRIPTIONID},
+        {3,
+         2,
+         1,
+         SPECIAL_EDGEID,
+         0,
+         false,
+         false,
+         false,
+         true,
+         TRAVEL_MODE_INACCESSIBLE,
+         INVALID_LANE_DESCRIPTIONID},
+        {3,
+         4,
+         1,
+         SPECIAL_EDGEID,
+         0,
+         false,
+         false,
+         false,
+         true,
+         TRAVEL_MODE_INACCESSIBLE,
+         INVALID_LANE_DESCRIPTIONID},
+        {4,
+         3,
+         1,
+         SPECIAL_EDGEID,
+         0,
+         false,
+         false,
+         false,
+         true,
+         TRAVEL_MODE_INACCESSIBLE,
+         INVALID_LANE_DESCRIPTIONID},
+        {4,
+         5,
+         1,
+         SPECIAL_EDGEID,
+         0,
+         false,
+         false,
+         false,
+         true,
+         TRAVEL_MODE_INACCESSIBLE,
+         INVALID_LANE_DESCRIPTIONID},
+        {5,
+         0,
+         1,
+         SPECIAL_EDGEID,
+         0,
+         false,
+         false,
+         false,
+         true,
+         TRAVEL_MODE_INACCESSIBLE,
+         INVALID_LANE_DESCRIPTIONID},
+        {5,
+         4,
+         1,
+         SPECIAL_EDGEID,
+         0,
+         false,
+         false,
+         false,
+         true,
+         TRAVEL_MODE_INACCESSIBLE,
+         INVALID_LANE_DESCRIPTIONID},
     };
 
     BOOST_ASSERT(edges.size() == 12);
@@ -124,12 +324,72 @@ BOOST_AUTO_TEST_CASE(t_intersection)
 
     std::vector<InputEdge> edges = {
         // src, tgt, dist, edge_id, name_id, access_restricted, fwd, bkwd, roundabout, travel_mode
-        {0, 1, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE},
-        {1, 0, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE},
-        {1, 2, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE},
-        {1, 3, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE},
-        {2, 1, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE},
-        {3, 1, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE},
+        {0,
+         1,
+         1,
+         SPECIAL_EDGEID,
+         0,
+         false,
+         false,
+         false,
+         true,
+         TRAVEL_MODE_INACCESSIBLE,
+         INVALID_LANE_DESCRIPTIONID},
+        {1,
+         0,
+         1,
+         SPECIAL_EDGEID,
+         0,
+         false,
+         false,
+         false,
+         true,
+         TRAVEL_MODE_INACCESSIBLE,
+         INVALID_LANE_DESCRIPTIONID},
+        {1,
+         2,
+         1,
+         SPECIAL_EDGEID,
+         0,
+         false,
+         false,
+         false,
+         true,
+         TRAVEL_MODE_INACCESSIBLE,
+         INVALID_LANE_DESCRIPTIONID},
+        {1,
+         3,
+         1,
+         SPECIAL_EDGEID,
+         0,
+         false,
+         false,
+         false,
+         true,
+         TRAVEL_MODE_INACCESSIBLE,
+         INVALID_LANE_DESCRIPTIONID},
+        {2,
+         1,
+         1,
+         SPECIAL_EDGEID,
+         0,
+         false,
+         false,
+         false,
+         true,
+         TRAVEL_MODE_INACCESSIBLE,
+         INVALID_LANE_DESCRIPTIONID},
+        {3,
+         1,
+         1,
+         SPECIAL_EDGEID,
+         0,
+         false,
+         false,
+         false,
+         true,
+         TRAVEL_MODE_INACCESSIBLE,
+         INVALID_LANE_DESCRIPTIONID},
     };
 
     BOOST_ASSERT(edges[0].data.IsCompatibleTo(edges[1].data));
@@ -160,10 +420,50 @@ BOOST_AUTO_TEST_CASE(street_name_changes)
 
     std::vector<InputEdge> edges = {
         // src, tgt, dist, edge_id, name_id, access_restricted, fwd, bkwd, roundabout, travel_mode
-        {0, 1, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE},
-        {1, 0, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE},
-        {1, 2, 1, SPECIAL_EDGEID, 1, false, false, false, true, TRAVEL_MODE_INACCESSIBLE},
-        {2, 1, 1, SPECIAL_EDGEID, 1, false, false, false, true, TRAVEL_MODE_INACCESSIBLE},
+        {0,
+         1,
+         1,
+         SPECIAL_EDGEID,
+         0,
+         false,
+         false,
+         false,
+         true,
+         TRAVEL_MODE_INACCESSIBLE,
+         INVALID_LANE_DESCRIPTIONID},
+        {1,
+         0,
+         1,
+         SPECIAL_EDGEID,
+         0,
+         false,
+         false,
+         false,
+         true,
+         TRAVEL_MODE_INACCESSIBLE,
+         INVALID_LANE_DESCRIPTIONID},
+        {1,
+         2,
+         1,
+         SPECIAL_EDGEID,
+         1,
+         false,
+         false,
+         false,
+         true,
+         TRAVEL_MODE_INACCESSIBLE,
+         INVALID_LANE_DESCRIPTIONID},
+        {2,
+         1,
+         1,
+         SPECIAL_EDGEID,
+         1,
+         false,
+         false,
+         false,
+         true,
+         TRAVEL_MODE_INACCESSIBLE,
+         INVALID_LANE_DESCRIPTIONID},
     };
 
     BOOST_ASSERT(edges[0].data.IsCompatibleTo(edges[1].data));
@@ -190,10 +490,50 @@ BOOST_AUTO_TEST_CASE(direction_changes)
 
     std::vector<InputEdge> edges = {
         // src, tgt, dist, edge_id, name_id, access_restricted, fwd, bkwd, roundabout, travel_mode
-        {0, 1, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE},
-        {1, 0, 1, SPECIAL_EDGEID, 0, false, true, false, true, TRAVEL_MODE_INACCESSIBLE},
-        {1, 2, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE},
-        {2, 1, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE},
+        {0,
+         1,
+         1,
+         SPECIAL_EDGEID,
+         0,
+         false,
+         false,
+         false,
+         true,
+         TRAVEL_MODE_INACCESSIBLE,
+         INVALID_LANE_DESCRIPTIONID},
+        {1,
+         0,
+         1,
+         SPECIAL_EDGEID,
+         0,
+         false,
+         true,
+         false,
+         true,
+         TRAVEL_MODE_INACCESSIBLE,
+         INVALID_LANE_DESCRIPTIONID},
+        {1,
+         2,
+         1,
+         SPECIAL_EDGEID,
+         0,
+         false,
+         false,
+         false,
+         true,
+         TRAVEL_MODE_INACCESSIBLE,
+         INVALID_LANE_DESCRIPTIONID},
+        {2,
+         1,
+         1,
+         SPECIAL_EDGEID,
+         0,
+         false,
+         false,
+         false,
+         true,
+         TRAVEL_MODE_INACCESSIBLE,
+         INVALID_LANE_DESCRIPTIONID},
     };
 
     Graph graph(5, edges);
diff --git a/unit_tests/library/waypoint_check.hpp b/unit_tests/library/waypoint_check.hpp
index 7bf1797..b14a209 100644
--- a/unit_tests/library/waypoint_check.hpp
+++ b/unit_tests/library/waypoint_check.hpp
@@ -15,8 +15,8 @@ inline bool waypoint_check(json::Value waypoint)
     }
     const auto waypoint_object = waypoint.get<json::Object>();
     const auto waypoint_location = waypoint_object.values.at("location").get<json::Array>().values;
-    util::FloatLongitude lon(waypoint_location[0].get<json::Number>().value);
-    util::FloatLatitude lat(waypoint_location[1].get<json::Number>().value);
+    util::FloatLongitude lon{waypoint_location[0].get<json::Number>().value};
+    util::FloatLatitude lat{waypoint_location[1].get<json::Number>().value};
     util::Coordinate location_coordinate(lon, lat);
     return location_coordinate.IsValid();
 }
diff --git a/unit_tests/mocks/mock_datafacade.hpp b/unit_tests/mocks/mock_datafacade.hpp
index c6fd567..f36276c 100644
--- a/unit_tests/mocks/mock_datafacade.hpp
+++ b/unit_tests/mocks/mock_datafacade.hpp
@@ -5,6 +5,7 @@
 
 #include "contractor/query_edge.hpp"
 #include "extractor/guidance/turn_instruction.hpp"
+#include "extractor/guidance/turn_lane_types.hpp"
 #include "engine/datafacade/datafacade_base.hpp"
 #include "util/guidance/bearing_class.hpp"
 #include "util/guidance/entry_class.hpp"
@@ -180,6 +181,17 @@ class MockDataFacade final : public engine::datafacade::BaseDataFacade
     BearingClassID GetBearingClassID(const NodeID /*id*/) const override { return 0; };
     EntryClassID GetEntryClassID(const EdgeID /*id*/) const override { return 0; }
 
+    bool hasLaneData(const EdgeID /*id*/) const override final { return true; };
+    util::guidance::LaneTupelIdPair GetLaneData(const EdgeID /*id*/) const override final
+    {
+        return {{0, 0}, 0};
+    }
+    extractor::guidance::TurnLaneDescription
+    GetTurnDescription(const LaneDescriptionID /*lane_description_id*/) const override final
+    {
+        return {};
+    }
+
     util::guidance::BearingClass
     GetBearingClass(const BearingClassID /*bearing_class_id*/) const override
     {
diff --git a/unit_tests/server/parameters_parser.cpp b/unit_tests/server/parameters_parser.cpp
index bf77b65..101fd2b 100644
--- a/unit_tests/server/parameters_parser.cpp
+++ b/unit_tests/server/parameters_parser.cpp
@@ -76,8 +76,8 @@ BOOST_AUTO_TEST_CASE(invalid_table_urls)
 
 BOOST_AUTO_TEST_CASE(valid_route_urls)
 {
-    std::vector<util::Coordinate> coords_1 = {{util::FloatLongitude(1), util::FloatLatitude(2)},
-                                              {util::FloatLongitude(3), util::FloatLatitude(4)}};
+    std::vector<util::Coordinate> coords_1 = {{util::FloatLongitude{1}, util::FloatLatitude{2}},
+                                              {util::FloatLongitude{3}, util::FloatLatitude{4}}};
 
     RouteParameters reference_1{};
     reference_1.coordinates = coords_1;
@@ -199,9 +199,9 @@ BOOST_AUTO_TEST_CASE(valid_route_urls)
     CHECK_EQUAL_RANGE(reference_5.coordinates, result_5->coordinates);
     CHECK_EQUAL_RANGE(reference_5.hints, result_5->hints);
 
-    std::vector<util::Coordinate> coords_2 = {{util::FloatLongitude(0), util::FloatLatitude(1)},
-                                              {util::FloatLongitude(2), util::FloatLatitude(3)},
-                                              {util::FloatLongitude(4), util::FloatLatitude(5)}};
+    std::vector<util::Coordinate> coords_2 = {{util::FloatLongitude{0}, util::FloatLatitude{1}},
+                                              {util::FloatLongitude{2}, util::FloatLatitude{3}},
+                                              {util::FloatLongitude{4}, util::FloatLatitude{5}}};
 
     RouteParameters reference_6{};
     reference_6.coordinates = coords_2;
@@ -250,10 +250,10 @@ BOOST_AUTO_TEST_CASE(valid_route_urls)
     CHECK_EQUAL_RANGE(reference_9.radiuses, result_9->radiuses);
 
     // Some Hint's are empty
-    std::vector<util::Coordinate> coords_3 = {{util::FloatLongitude(1), util::FloatLatitude(2)},
-                                              {util::FloatLongitude(3), util::FloatLatitude(4)},
-                                              {util::FloatLongitude(5), util::FloatLatitude(6)},
-                                              {util::FloatLongitude(7), util::FloatLatitude(8)}};
+    std::vector<util::Coordinate> coords_3 = {{util::FloatLongitude{1}, util::FloatLatitude{2}},
+                                              {util::FloatLongitude{3}, util::FloatLatitude{4}},
+                                              {util::FloatLongitude{5}, util::FloatLatitude{6}},
+                                              {util::FloatLongitude{7}, util::FloatLatitude{8}}};
     std::vector<boost::optional<engine::Hint>> hints_10 = {
         engine::Hint::FromBase64("DAIAgP___"
                                  "38AAAAAAAAAAAIAAAAAAAAAEAAAAOgDAAD0AwAAGwAAAOUacQBQP5sCshpxAB0_"
@@ -293,8 +293,8 @@ BOOST_AUTO_TEST_CASE(valid_route_urls)
 
 BOOST_AUTO_TEST_CASE(valid_table_urls)
 {
-    std::vector<util::Coordinate> coords_1 = {{util::FloatLongitude(1), util::FloatLatitude(2)},
-                                              {util::FloatLongitude(3), util::FloatLatitude(4)}};
+    std::vector<util::Coordinate> coords_1 = {{util::FloatLongitude{1}, util::FloatLatitude{2}},
+                                              {util::FloatLongitude{3}, util::FloatLatitude{4}}};
 
     TableParameters reference_1{};
     reference_1.coordinates = coords_1;
@@ -329,8 +329,8 @@ BOOST_AUTO_TEST_CASE(valid_table_urls)
 
 BOOST_AUTO_TEST_CASE(valid_match_urls)
 {
-    std::vector<util::Coordinate> coords_1 = {{util::FloatLongitude(1), util::FloatLatitude(2)},
-                                              {util::FloatLongitude(3), util::FloatLatitude(4)}};
+    std::vector<util::Coordinate> coords_1 = {{util::FloatLongitude{1}, util::FloatLatitude{2}},
+                                              {util::FloatLongitude{3}, util::FloatLatitude{4}}};
 
     MatchParameters reference_1{};
     reference_1.coordinates = coords_1;
@@ -354,7 +354,7 @@ BOOST_AUTO_TEST_CASE(valid_match_urls)
 
 BOOST_AUTO_TEST_CASE(valid_nearest_urls)
 {
-    std::vector<util::Coordinate> coords_1 = {{util::FloatLongitude(1), util::FloatLatitude(2)}};
+    std::vector<util::Coordinate> coords_1 = {{util::FloatLongitude{1}, util::FloatLatitude{2}}};
 
     NearestParameters reference_1{};
     reference_1.coordinates = coords_1;
@@ -388,8 +388,8 @@ BOOST_AUTO_TEST_CASE(valid_tile_urls)
 
 BOOST_AUTO_TEST_CASE(valid_trip_urls)
 {
-    std::vector<util::Coordinate> coords_1 = {{util::FloatLongitude(1), util::FloatLatitude(2)},
-                                              {util::FloatLongitude(3), util::FloatLatitude(4)}};
+    std::vector<util::Coordinate> coords_1 = {{util::FloatLongitude{1}, util::FloatLatitude{2}},
+                                              {util::FloatLongitude{3}, util::FloatLatitude{4}}};
 
     TripParameters reference_1{};
     reference_1.coordinates = coords_1;
diff --git a/unit_tests/server/url_parser.cpp b/unit_tests/server/url_parser.cpp
index fd1f110..1d13506 100644
--- a/unit_tests/server/url_parser.cpp
+++ b/unit_tests/server/url_parser.cpp
@@ -72,7 +72,7 @@ BOOST_AUTO_TEST_CASE(valid_urls)
 
     // one coordinate
     std::vector<util::Coordinate> coords_3 = {
-        util::Coordinate(util::FloatLongitude(0), util::FloatLatitude(1)),
+        util::Coordinate{util::FloatLongitude{0}, util::FloatLatitude{1}},
     };
     api::ParsedURL reference_3{"route", 1, "profile", "0,1", 18UL};
     auto result_3 = api::parseURL("/route/v1/profile/0,1");
diff --git a/unit_tests/util/coordinate_calculation.cpp b/unit_tests/util/coordinate_calculation.cpp
index 2d2510e..3bea3eb 100644
--- a/unit_tests/util/coordinate_calculation.cpp
+++ b/unit_tests/util/coordinate_calculation.cpp
@@ -16,121 +16,121 @@ 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));
+    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));
+    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));
+    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));
+    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));
+    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));
+    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));
+    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));
+    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));
+    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));
+    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));
+    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));
+    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));
+    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));
+    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));
+    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));
+    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);
                        */
@@ -139,9 +139,9 @@ BOOST_AUTO_TEST_CASE(compute_angle)
 // Regression test for bug captured in #1347
 BOOST_AUTO_TEST_CASE(regression_test_1347)
 {
-    Coordinate u(FloatLongitude(-100), FloatLatitude(10));
-    Coordinate v(FloatLongitude(-100.002), FloatLatitude(10.001));
-    Coordinate q(FloatLongitude(-100.001), FloatLatitude(10.002));
+    Coordinate u(FloatLongitude{-100}, FloatLatitude{10});
+    Coordinate v(FloatLongitude{-100.002}, FloatLatitude{10.001});
+    Coordinate q(FloatLongitude{-100.001}, FloatLatitude{10.002});
 
     double d1 = coordinate_calculation::perpendicularDistance(u, v, q);
 
@@ -179,10 +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),
+    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),
+    BOOST_CHECK_CLOSE(static_cast<double>(start.lat + FloatLatitude{ratio} * diff.lat),
                       static_cast<double>(nearest.lat),
                       0.1);
 }
@@ -257,56 +257,56 @@ BOOST_AUTO_TEST_CASE(point_on_segment)
 
 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));
+    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)));
+    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));
+    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)));
+    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));
+    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));
+    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)));
+    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));
+    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)));
+    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));
+    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);
 
     // Out of bounds
-    a = Coordinate(FloatLongitude(-112.096234), FloatLatitude(41.147258));
-    b = Coordinate(FloatLongitude(-112.106606), FloatLatitude(41.147259));
-    c = Coordinate(FloatLongitude(-113.096419), FloatLatitude(41.147258));
+    a = Coordinate(FloatLongitude{-112.096234}, FloatLatitude{41.147258});
+    b = Coordinate(FloatLongitude{-112.106606}, FloatLatitude{41.147259});
+    c = Coordinate(FloatLongitude{-113.096419}, FloatLatitude{41.147258});
     result = coordinate_calculation::circleCenter(a, b, c);
     BOOST_CHECK(!result);
 }
diff --git a/unit_tests/util/group_by.cpp b/unit_tests/util/group_by.cpp
new file mode 100644
index 0000000..d5ac08c
--- /dev/null
+++ b/unit_tests/util/group_by.cpp
@@ -0,0 +1,68 @@
+#include "util/group_by.hpp"
+
+#include <boost/test/test_case_template.hpp>
+#include <boost/test/unit_test.hpp>
+
+#include <iterator>
+#include <vector>
+
+BOOST_AUTO_TEST_SUITE(group_by_test)
+
+using namespace osrm;
+using namespace osrm::util;
+
+namespace
+{
+
+struct Yes
+{
+    template <typename T> bool operator()(T &&) { return true; }
+};
+
+struct No
+{
+    template <typename T> bool operator()(T &&) { return false; }
+};
+
+struct Alternating
+{
+    template <typename T> bool operator()(T &&) { return state = !state; }
+    bool state = true;
+};
+
+struct SubRangeCounter
+{
+    template <typename Range> void operator()(Range &&) { count += 1; }
+    std::size_t count = 0;
+};
+}
+
+BOOST_AUTO_TEST_CASE(grouped_empty_test)
+{
+    std::vector<int> v{};
+    auto ranges = group_by(begin(v), end(v), Yes{}, SubRangeCounter{});
+    BOOST_CHECK_EQUAL(ranges.count, 0);
+}
+
+BOOST_AUTO_TEST_CASE(grouped_all_match_range_test)
+{
+    std::vector<int> v{1, 2, 3};
+    auto ranges = group_by(begin(v), end(v), Yes{}, SubRangeCounter{});
+    BOOST_CHECK_EQUAL(ranges.count, 1);
+}
+
+BOOST_AUTO_TEST_CASE(grouped_no_match_range_test)
+{
+    std::vector<int> v{1, 2, 3};
+    auto ranges = group_by(begin(v), end(v), No{}, SubRangeCounter{});
+    BOOST_CHECK_EQUAL(ranges.count, 1);
+}
+
+BOOST_AUTO_TEST_CASE(grouped_alternating_matches_range_test)
+{
+    std::vector<int> v{1, 2, 3};
+    auto ranges = group_by(begin(v), end(v), Alternating{}, SubRangeCounter{});
+    BOOST_CHECK_EQUAL(ranges.count, v.size());
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/unit_tests/util/packed_vector.cpp b/unit_tests/util/packed_vector.cpp
index 6c723a6..e95bc03 100644
--- a/unit_tests/util/packed_vector.cpp
+++ b/unit_tests/util/packed_vector.cpp
@@ -19,7 +19,7 @@ BOOST_AUTO_TEST_CASE(insert_and_retrieve_packed_test)
 
     for (std::size_t i = 0; i < num_test_cases; i++)
     {
-        OSMNodeID r = static_cast<OSMNodeID>(rand() % 2147483647); // max 33-bit uint
+        OSMNodeID r {static_cast<std::uint64_t>(rand() % 2147483647)}; // max 33-bit uint
 
         packed_ids.push_back(r);
         original_ids.push_back(r);
diff --git a/unit_tests/util/rectangle.cpp b/unit_tests/util/rectangle.cpp
index 87da415..ca40ff8 100644
--- a/unit_tests/util/rectangle.cpp
+++ b/unit_tests/util/rectangle.cpp
@@ -27,22 +27,22 @@ BOOST_AUTO_TEST_CASE(get_min_dist_test)
     //           +- -80
     //           |
     RectangleInt2D ne{
-        FloatLongitude(10), FloatLongitude(100), FloatLatitude(10), FloatLatitude(80)};
+        FloatLongitude{10}, FloatLongitude{100}, FloatLatitude{10}, FloatLatitude{80}};
     RectangleInt2D nw{
-        FloatLongitude(-100), FloatLongitude(-10), FloatLatitude(10), FloatLatitude(80)};
+        FloatLongitude{-100}, FloatLongitude{-10}, FloatLatitude{10}, FloatLatitude{80}};
     RectangleInt2D se{
-        FloatLongitude(10), FloatLongitude(100), FloatLatitude(-80), FloatLatitude(-10)};
+        FloatLongitude{10}, FloatLongitude{100}, FloatLatitude{-80}, FloatLatitude{-10}};
     RectangleInt2D sw{
-        FloatLongitude(-100), FloatLongitude(-10), FloatLatitude(-80), FloatLatitude(-10)};
+        FloatLongitude{-100}, FloatLongitude{-10}, FloatLatitude{-80}, FloatLatitude{-10}};
 
-    Coordinate nw_sw{FloatLongitude(-100.1), FloatLatitude(9.9)};
-    Coordinate nw_se{FloatLongitude(-9.9), FloatLatitude(9.9)};
-    Coordinate nw_ne{FloatLongitude(-9.9), FloatLatitude(80.1)};
-    Coordinate nw_nw{FloatLongitude(-100.1), FloatLatitude(80.1)};
-    Coordinate nw_s{FloatLongitude(-55), FloatLatitude(9.9)};
-    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)};
+    Coordinate nw_sw{FloatLongitude{-100.1}, FloatLatitude{9.9}};
+    Coordinate nw_se{FloatLongitude{-9.9}, FloatLatitude{9.9}};
+    Coordinate nw_ne{FloatLongitude{-9.9}, FloatLatitude{80.1}};
+    Coordinate nw_nw{FloatLongitude{-100.1}, FloatLatitude{80.1}};
+    Coordinate nw_s{FloatLongitude{-55}, FloatLatitude{9.9}};
+    Coordinate nw_e{FloatLongitude{-9.9}, FloatLatitude{45.0}};
+    Coordinate nw_w{FloatLongitude{-100.1}, FloatLatitude{45.0}};
+    Coordinate nw_n{FloatLongitude{-55}, FloatLatitude{80.1}};
     BOOST_CHECK_CLOSE(
         nw.GetMinSquaredDist(nw_sw), 0.02 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
     BOOST_CHECK_CLOSE(
@@ -60,14 +60,14 @@ BOOST_AUTO_TEST_CASE(get_min_dist_test)
     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)};
-    Coordinate ne_ne{FloatLongitude(100.1), FloatLatitude(80.1)};
-    Coordinate ne_nw{FloatLongitude(9.9), FloatLatitude(80.1)};
-    Coordinate ne_s{FloatLongitude(55), FloatLatitude(9.9)};
-    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)};
+    Coordinate ne_sw{FloatLongitude{9.9}, FloatLatitude{9.9}};
+    Coordinate ne_se{FloatLongitude{100.1}, FloatLatitude{9.9}};
+    Coordinate ne_ne{FloatLongitude{100.1}, FloatLatitude{80.1}};
+    Coordinate ne_nw{FloatLongitude{9.9}, FloatLatitude{80.1}};
+    Coordinate ne_s{FloatLongitude{55}, FloatLatitude{9.9}};
+    Coordinate ne_e{FloatLongitude{100.1}, FloatLatitude{45.0}};
+    Coordinate ne_w{FloatLongitude{9.9}, FloatLatitude{45.0}};
+    Coordinate ne_n{FloatLongitude{55}, FloatLatitude{80.1}};
     BOOST_CHECK_CLOSE(
         ne.GetMinSquaredDist(ne_sw), 0.02 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
     BOOST_CHECK_CLOSE(
@@ -85,14 +85,14 @@ BOOST_AUTO_TEST_CASE(get_min_dist_test)
     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)};
-    Coordinate se_sw{FloatLongitude(9.9), FloatLatitude(-80.1)};
-    Coordinate se_se{FloatLongitude(100.1), FloatLatitude(-80.1)};
-    Coordinate se_n{FloatLongitude(55), FloatLatitude(-9.9)};
-    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)};
+    Coordinate se_ne{FloatLongitude{100.1}, FloatLatitude{-9.9}};
+    Coordinate se_nw{FloatLongitude{9.9}, FloatLatitude{-9.9}};
+    Coordinate se_sw{FloatLongitude{9.9}, FloatLatitude{-80.1}};
+    Coordinate se_se{FloatLongitude{100.1}, FloatLatitude{-80.1}};
+    Coordinate se_n{FloatLongitude{55}, FloatLatitude{-9.9}};
+    Coordinate se_w{FloatLongitude{9.9}, FloatLatitude{-45.0}};
+    Coordinate se_e{FloatLongitude{100.1}, FloatLatitude{-45.0}};
+    Coordinate se_s{FloatLongitude{55}, FloatLatitude{-80.1}};
     BOOST_CHECK_CLOSE(
         se.GetMinSquaredDist(se_sw), 0.02 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
     BOOST_CHECK_CLOSE(
@@ -110,14 +110,14 @@ BOOST_AUTO_TEST_CASE(get_min_dist_test)
     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)};
-    Coordinate sw_sw{FloatLongitude(-100.1), FloatLatitude(-80.1)};
-    Coordinate sw_se{FloatLongitude(-9.9), FloatLatitude(-80.1)};
-    Coordinate sw_n{FloatLongitude(-55), FloatLatitude(-9.9)};
-    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)};
+    Coordinate sw_ne{FloatLongitude{-9.9}, FloatLatitude{-9.9}};
+    Coordinate sw_nw{FloatLongitude{-100.1}, FloatLatitude{-9.9}};
+    Coordinate sw_sw{FloatLongitude{-100.1}, FloatLatitude{-80.1}};
+    Coordinate sw_se{FloatLongitude{-9.9}, FloatLatitude{-80.1}};
+    Coordinate sw_n{FloatLongitude{-55}, FloatLatitude{-9.9}};
+    Coordinate sw_w{FloatLongitude{-100.1}, FloatLatitude{-45.0}};
+    Coordinate sw_e{FloatLongitude{-9.9}, FloatLatitude{-45.0}};
+    Coordinate sw_s{FloatLongitude{-55}, FloatLatitude{-80.1}};
     BOOST_CHECK_CLOSE(
         sw.GetMinSquaredDist(sw_sw), 0.02 * COORDINATE_PRECISION * COORDINATE_PRECISION, 0.1);
     BOOST_CHECK_CLOSE(
diff --git a/unit_tests/util/static_rtree.cpp b/unit_tests/util/static_rtree.cpp
index c962ed3..66b81c5 100644
--- a/unit_tests/util/static_rtree.cpp
+++ b/unit_tests/util/static_rtree.cpp
@@ -117,7 +117,7 @@ template <unsigned NUM_NODES, unsigned NUM_EDGES> struct RandomGraphFixture
         {
             int lon = lon_udist(g);
             int lat = lat_udist(g);
-            coords.emplace_back(Coordinate(FixedLongitude(lon), FixedLatitude(lat)));
+            coords.emplace_back(Coordinate(FixedLongitude{lon}, FixedLatitude{lat}));
         }
 
         std::uniform_int_distribution<> edge_udist(0, coords.size() - 1);
@@ -216,7 +216,7 @@ void sampling_verify_rtree(RTreeT &rtree,
     std::vector<Coordinate> queries;
     for (unsigned i = 0; i < num_samples; i++)
     {
-        queries.emplace_back(FixedLongitude(lon_udist(g)), FixedLatitude(lat_udist(g)));
+        queries.emplace_back(FixedLongitude{lon_udist(g)}, FixedLatitude{lat_udist(g)});
     }
 
     for (const auto &q : queries)
@@ -324,7 +324,7 @@ BOOST_AUTO_TEST_CASE(regression_test)
     LinearSearchNN<TestData> lsnn(fixture.coords, fixture.edges);
 
     // query a node just right of the center of the gap
-    Coordinate input(FloatLongitude(55.1), FloatLatitude(20.0));
+    Coordinate input(FloatLongitude{55.1}, FloatLatitude{20.0});
     auto result_rtree = rtree.Nearest(input, 1);
     auto result_ls = lsnn.Nearest(input, 1);
 
@@ -342,8 +342,8 @@ BOOST_AUTO_TEST_CASE(radius_regression_test)
     using Edge = std::pair<unsigned, unsigned>;
     GraphFixture fixture(
         {
-            Coord(FloatLongitude(0.0), FloatLatitude(0.0)),
-            Coord(FloatLongitude(10.0), FloatLatitude(10.0)),
+            Coord(FloatLongitude{0.0}, FloatLatitude{0.0}),
+            Coord(FloatLongitude{10.0}, FloatLatitude{10.0}),
         },
         {Edge(0, 1), Edge(1, 0)});
 
@@ -355,7 +355,7 @@ BOOST_AUTO_TEST_CASE(radius_regression_test)
     engine::GeospatialQuery<MiniStaticRTree, MockDataFacade> query(
         rtree, fixture.coords, mockfacade);
 
-    Coordinate input(FloatLongitude(5.2), FloatLatitude(5.0));
+    Coordinate input(FloatLongitude{5.2}, FloatLatitude{5.0});
 
     {
         auto results = query.NearestPhantomNodesInRange(input, 0.01);
@@ -369,8 +369,8 @@ BOOST_AUTO_TEST_CASE(bearing_tests)
     using Edge = std::pair<unsigned, unsigned>;
     GraphFixture fixture(
         {
-            Coord(FloatLongitude(0.0), FloatLatitude(0.0)),
-            Coord(FloatLongitude(10.0), FloatLatitude(10.0)),
+            Coord(FloatLongitude{0.0}, FloatLatitude{0.0}),
+            Coord(FloatLongitude{10.0}, FloatLatitude{10.0}),
         },
         {Edge(0, 1), Edge(1, 0)});
 
@@ -382,7 +382,7 @@ BOOST_AUTO_TEST_CASE(bearing_tests)
     engine::GeospatialQuery<MiniStaticRTree, MockDataFacade> query(
         rtree, fixture.coords, mockfacade);
 
-    Coordinate input(FloatLongitude(5.1), FloatLatitude(5.0));
+    Coordinate input(FloatLongitude{5.1}, FloatLatitude{5.0});
 
     {
         auto results = query.NearestPhantomNodes(input, 5);
@@ -440,11 +440,11 @@ BOOST_AUTO_TEST_CASE(bbox_search_tests)
 
     GraphFixture fixture(
         {
-            Coord(FloatLongitude(0.0), FloatLatitude(0.0)),
-            Coord(FloatLongitude(1.0), FloatLatitude(1.0)),
-            Coord(FloatLongitude(2.0), FloatLatitude(2.0)),
-            Coord(FloatLongitude(3.0), FloatLatitude(3.0)),
-            Coord(FloatLongitude(4.0), FloatLatitude(4.0)),
+            Coord(FloatLongitude{0.0}, FloatLatitude{0.0}),
+            Coord(FloatLongitude{1.0}, FloatLatitude{1.0}),
+            Coord(FloatLongitude{2.0}, FloatLatitude{2.0}),
+            Coord(FloatLongitude{3.0}, FloatLatitude{3.0}),
+            Coord(FloatLongitude{4.0}, FloatLatitude{4.0}),
         },
         {Edge(0, 1), Edge(1, 2), Edge(2, 3), Edge(3, 4)});
 
@@ -458,14 +458,14 @@ BOOST_AUTO_TEST_CASE(bbox_search_tests)
 
     {
         RectangleInt2D bbox = {
-            FloatLongitude(0.5), FloatLongitude(1.5), FloatLatitude(0.5), FloatLatitude(1.5)};
+            FloatLongitude{0.5}, FloatLongitude{1.5}, FloatLatitude{0.5}, FloatLatitude{1.5}};
         auto results = query.Search(bbox);
         BOOST_CHECK_EQUAL(results.size(), 2);
     }
 
     {
         RectangleInt2D bbox = {
-            FloatLongitude(1.5), FloatLongitude(3.5), FloatLatitude(1.5), FloatLatitude(3.5)};
+            FloatLongitude{1.5}, FloatLongitude{3.5}, FloatLatitude{1.5}, FloatLatitude{3.5}};
         auto results = query.Search(bbox);
         BOOST_CHECK_EQUAL(results.size(), 3);
     }
diff --git a/unit_tests/util/web_mercator.cpp b/unit_tests/util/web_mercator.cpp
index 8e96d98..4a12c95 100644
--- a/unit_tests/util/web_mercator.cpp
+++ b/unit_tests/util/web_mercator.cpp
@@ -22,23 +22,23 @@ BOOST_AUTO_TEST_CASE(lon_to_pixel)
 
 BOOST_AUTO_TEST_CASE(lat_to_pixel)
 {
-    BOOST_CHECK_CLOSE(web_mercator::latToY(util::FloatLatitude(43.733947)) *
+    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)) *
+    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)) *
+    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)) *
+    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)) *
+    BOOST_CHECK_CLOSE(web_mercator::latToY(util::FloatLatitude{43.733729}) *
                           web_mercator::DEGREE_TO_PX,
                       5424328.17293,
                       0.1);

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