Repository: r5py/r5py (language: python)

Issue:
Fail with a more meaningful error message when snapping fails for all points in destinations or origins
Currently, when choosing `snap_to_network=True` with, e.g., `TravelTimeMatrix`, r5py raises a warning if it cannot snap all points to the network. However, if it fails to snap *any* point (leaving an empty `destinations` or `origins`), routing can still proceed, but results in the rather cryptic exception `ValueError: No objects to concatenate`. 

Let’s add a sanity check before carrying out the actual computations


Discussion / hints:

Fix patch:
diff --git a/.binder/environment.yml b/.binder/environment.yml
index 5ff577bc..462b63a5 100644
--- a/.binder/environment.yml
+++ b/.binder/environment.yml
@@ -7,15 +7,15 @@ dependencies:
   - _openmp_mutex=4.5
   - accessible-pygments=0.0.4
   - aiofiles=22.1.0
-  - aiosqlite=0.18.0
+  - aiosqlite=0.19.0
   - alabaster=0.7.13
   - alsa-lib=1.2.8
-  - anyio=3.6.2
+  - anyio=3.7.0
   - argon2-cffi=21.3.0
   - argon2-cffi-bindings=21.2.0
   - asttokens=2.2.1
   - attr=2.5.1
-  - attrs=22.2.0
+  - attrs=23.1.0
   - babel=2.12.1
   - backcall=0.2.0
   - backports=1.0
@@ -24,31 +24,29 @@ dependencies:
   - black=23.3.0
   - black-jupyter=23.3.0
   - bleach=6.0.0
-  - blosc=1.21.3
+  - blosc=1.21.4
   - boost-cpp=1.78.0
   - branca=0.6.0
   - brotli=1.0.9
   - brotli-bin=1.0.9
-  - brotlipy=0.7.0
   - bzip2=1.0.8
-  - c-ares=1.18.1
-  - ca-certificates=2022.12.7
+  - c-ares=1.19.1
+  - ca-certificates=2023.5.7
   - cairo=1.16.0
-  - certifi=2022.12.7
+  - certifi=2023.5.7
   - cffi=1.15.1
   - cfitsio=4.2.0
   - charset-normalizer=3.1.0
   - click=8.1.3
   - click-plugins=1.1.1
   - cligj=0.7.2
-  - codecov=2.1.12
+  - codecov=2.1.13
   - colorama=0.4.6
   - comm=0.1.3
   - configargparse=1.5.3
-  - contourpy=1.0.7
-  - coverage=7.2.3
-  - cryptography=40.0.1
-  - curl=7.88.1
+  - contourpy=1.1.0
+  - coverage=7.2.7
+  - curl=8.1.2
   - cycler=0.11.0
   - dbus=1.13.6
   - debugpy=1.6.7
@@ -59,10 +57,9 @@ dependencies:
   - exceptiongroup=1.1.1
   - executing=1.2.0
   - expat=2.5.0
-  - fftw=3.3.10
-  - filelock=3.11.0
-  - fiona=1.9.3
-  - flit-core=3.8.0
+  - filelock=3.12.2
+  - fiona=1.9.4
+  - flit-core=3.9.0
   - folium=0.14.0
   - font-ttf-dejavu-sans-mono=2.37
   - font-ttf-inconsolata=3.000
@@ -71,39 +68,38 @@ dependencies:
   - fontconfig=2.14.2
   - fonts-conda-ecosystem=1
   - fonts-conda-forge=1
-  - fonttools=4.39.3
+  - fonttools=4.40.0
   - freetype=2.12.1
   - freexl=1.0.6
-  - gdal=3.6.3
-  - geopandas=0.12.2
-  - geopandas-base=0.12.2
+  - gdal=3.7.0
+  - geopandas=0.13.2
+  - geopandas-base=0.13.2
   - geos=3.11.2
   - geotiff=1.7.1
   - gettext=0.21.1
   - giflib=5.2.1
   - gitdb=4.0.10
   - gitpython=3.1.31
-  - glib=2.74.1
-  - glib-tools=2.74.1
+  - glib=2.76.3
+  - glib-tools=2.76.3
+  - gmp=6.2.1
   - graphite2=1.3.13
   - greenlet=2.0.2
-  - gst-plugins-base=1.22.0
-  - gstreamer=1.22.0
-  - gstreamer-orc=0.4.33
-  - harfbuzz=6.0.0
+  - gst-plugins-base=1.22.3
+  - gstreamer=1.22.3
+  - harfbuzz=7.3.0
   - hdf4=4.2.15
   - hdf5=1.14.0
-  - icu=70.1
+  - icu=72.1
   - idna=3.4
   - imagesize=1.4.1
-  - importlib-metadata=6.3.0
-  - importlib_metadata=6.3.0
+  - importlib-metadata=6.7.0
+  - importlib_metadata=6.7.0
   - importlib_resources=5.12.0
   - iniconfig=2.0.0
-  - ipykernel=6.22.0
-  - ipython=8.12.0
+  - ipykernel=6.23.1
+  - ipython=8.14.0
   - ipython_genutils=0.2.0
-  - jack=1.9.22
   - jedi=0.18.2
   - jinja2=3.1.2
   - joblib=1.2.0
@@ -111,21 +107,21 @@ dependencies:
   - json-c=0.16
   - json5=0.9.5
   - jsonschema=4.17.3
-  - jupyter-cache=0.5.0
-  - jupyter_client=8.1.0
-  - jupyter_core=5.3.0
+  - jupyter-cache=0.6.1
+  - jupyter_client=8.2.0
+  - jupyter_core=5.3.1
   - jupyter_events=0.6.3
-  - jupyter_server=2.5.0
+  - jupyter_server=2.6.0
   - jupyter_server_fileid=0.9.0
   - jupyter_server_terminals=0.4.4
   - jupyter_server_ydoc=0.8.0
-  - jupyter_ydoc=0.2.3
-  - jupyterlab=3.6.3
-  - jupyterlab-myst=1.1.3
+  - jupyter_ydoc=0.2.4
+  - jupyterlab=3.6.4
+  - jupyterlab-myst=1.2.0
   - jupyterlab_pygments=0.2.2
-  - jupyterlab_server=2.22.0
-  - jupytext=1.14.5
-  - kealib=1.5.0
+  - jupyterlab_server=2.23.0
+  - jupytext=1.14.6
+  - kealib=1.5.1
   - keyutils=1.6.1
   - kiwisolver=1.4.4
   - krb5=1.20.1
@@ -134,105 +130,105 @@ dependencies:
   - ld_impl_linux-64=2.40
   - lerc=4.0.0
   - libaec=1.0.6
+  - libarchive=3.6.2
   - libblas=3.9.0
   - libbrotlicommon=1.0.9
   - libbrotlidec=1.0.9
   - libbrotlienc=1.0.9
   - libcap=2.67
   - libcblas=3.9.0
-  - libclang=15.0.7
-  - libclang13=15.0.7
+  - libclang=16.0.6
+  - libclang13=16.0.6
   - libcups=2.3.3
-  - libcurl=7.88.1
-  - libdb=6.2.32
+  - libcurl=8.1.2
   - libdeflate=1.18
   - libedit=3.1.20191231
   - libev=4.33
-  - libevent=2.1.10
+  - libevent=2.1.12
   - libexpat=2.5.0
   - libffi=3.4.2
   - libflac=1.4.2
-  - libgcc-ng=12.2.0
+  - libgcc-ng=13.1.0
   - libgcrypt=1.10.1
-  - libgdal=3.6.3
-  - libgfortran-ng=12.2.0
-  - libgfortran5=12.2.0
-  - libglib=2.74.1
-  - libgomp=12.2.0
-  - libgpg-error=1.46
+  - libgdal=3.7.0
+  - libgfortran-ng=13.1.0
+  - libgfortran5=13.1.0
+  - libglib=2.76.3
+  - libgomp=13.1.0
+  - libgpg-error=1.47
   - libiconv=1.17
   - libjpeg-turbo=2.1.5.1
   - libkml=1.3.0
   - liblapack=3.9.0
-  - libllvm15=15.0.7
+  - libllvm16=16.0.6
   - libnetcdf=4.9.2
   - libnghttp2=1.52.0
   - libnsl=2.0.0
   - libogg=1.3.4
-  - libopenblas=0.3.21
+  - libopenblas=0.3.23
   - libopus=1.3.1
   - libpng=1.6.39
-  - libpq=15.2
+  - libpq=15.3
   - librttopo=1.1.0
   - libsndfile=1.2.0
   - libsodium=1.0.18
   - libspatialindex=1.9.3
   - libspatialite=5.0.1
-  - libsqlite=3.40.0
-  - libssh2=1.10.0
-  - libstdcxx-ng=12.2.0
+  - libsqlite=3.42.0
+  - libssh2=1.11.0
+  - libstdcxx-ng=13.1.0
   - libsystemd0=253
-  - libtiff=4.5.0
-  - libtool=2.4.7
-  - libudev1=253
+  - libtiff=4.5.1
   - libuuid=2.38.1
   - libvorbis=1.3.7
   - libwebp-base=1.3.0
-  - libxcb=1.13
+  - libxcb=1.15
   - libxkbcommon=1.5.0
-  - libxml2=2.10.3
+  - libxml2=2.11.4
   - libzip=1.9.2
   - libzlib=1.2.13
   - lz4-c=1.9.4
+  - lzo=2.10
   - mapclassify=2.5.0
   - markdown-it-py=2.2.0
-  - markupsafe=2.1.2
+  - markupsafe=2.1.3
   - matplotlib=3.7.1
   - matplotlib-base=3.7.1
   - matplotlib-inline=0.1.6
-  - mdit-py-plugins=0.3.5
+  - mdit-py-plugins=0.4.0
   - mdurl=0.1.0
-  - mistune=2.0.5
+  - mistune=3.0.0
   - mpg123=1.31.3
-  - munch=2.5.0
+  - munch=3.0.0
   - munkres=1.1.4
   - mypy_extensions=1.0.0
-  - mysql-common=8.0.32
-  - mysql-libs=8.0.32
-  - myst-nb=0.17.1
+  - mysql-common=8.0.33
+  - mysql-libs=8.0.33
+  - myst-nb=0.17.2
   - myst-parser=0.18.1
-  - nbclassic=0.5.5
-  - nbclient=0.5.13
-  - nbconvert=7.3.1
-  - nbconvert-core=7.3.1
-  - nbconvert-pandoc=7.3.1
-  - nbformat=5.8.0
-  - nbsphinx=0.9.1
+  - nbclassic=1.0.0
+  - nbclient=0.7.4
+  - nbconvert=7.6.0
+  - nbconvert-core=7.6.0
+  - nbconvert-pandoc=7.6.0
+  - nbformat=5.9.0
+  - nbsphinx=0.9.2
   - nbstripout=0.6.1
-  - ncurses=6.3
+  - ncurses=6.4
   - nest-asyncio=1.5.6
   - networkx=3.1
   - notebook=6.5.4
-  - notebook-shim=0.2.2
+  - notebook-shim=0.2.3
   - nspr=4.35
   - nss=3.89
-  - numpy=1.24.2
+  - numpy=1.25.0
   - openjdk=17.0.3
   - openjpeg=2.5.0
-  - openssl=3.1.0
-  - packaging=23.0
-  - pandas=2.0.0
-  - pandoc=2.19.2
+  - openssl=3.1.1
+  - overrides=7.3.1
+  - packaging=23.1
+  - pandas=2.0.2
+  - pandoc=3.1.3
   - pandocfilters=1.5.0
   - parso=0.8.3
   - pathspec=0.11.1
@@ -240,62 +236,59 @@ dependencies:
   - pexpect=4.8.0
   - pickleshare=0.7.5
   - pillow=9.5.0
-  - pip=23.0.1
+  - pip=23.1.2
   - pixman=0.40.0
   - pkgutil-resolve-name=1.3.10
-  - platformdirs=3.2.0
+  - platformdirs=3.6.0
   - pluggy=1.0.0
   - ply=3.11
   - pooch=1.7.0
-  - poppler=23.04.0
+  - poppler=23.05.0
   - poppler-data=0.4.12
-  - postgresql=15.2
-  - proj=9.2.0
-  - prometheus_client=0.16.0
+  - postgresql=15.3
+  - proj=9.2.1
+  - prometheus_client=0.17.0
   - prompt-toolkit=3.0.38
   - prompt_toolkit=3.0.38
-  - psutil=5.9.4
+  - psutil=5.9.5
   - pthread-stubs=0.4
   - ptyprocess=0.7.0
-  - pulseaudio=16.1
   - pulseaudio-client=16.1
-  - pulseaudio-daemon=16.1
   - pure_eval=0.2.2
   - pycparser=2.21
   - pydata-sphinx-theme=0.13.3
-  - pygments=2.15.0
-  - pyopenssl=23.1.1
-  - pyparsing=3.0.9
-  - pyproj=3.5.0
+  - pygments=2.15.1
+  - pyparsing=3.1.0
+  - pyproj=3.6.0
   - pyqt=5.15.7
   - pyqt5-sip=12.11.0
   - pyrsistent=0.19.3
   - pysocks=1.7.1
-  - pytest=7.3.0
+  - pytest=7.3.2
   - pytest-asyncio=0.21.0
-  - pytest-cov=4.0.0
+  - pytest-cov=4.1.0
   - pytest-lazy-fixture=0.6.3
-  - python=3.10.10
+  - python=3.10.11
   - python-dateutil=2.8.2
-  - python-fastjsonschema=2.16.3
+  - python-fastjsonschema=2.17.1
   - python-json-logger=2.0.7
   - python-tzdata=2023.3
   - python_abi=3.10
   - pytz=2023.3
   - pyyaml=6.0
-  - pyzmq=25.0.2
+  - pyzmq=25.1.0
   - qt-main=5.15.8
   - readline=8.2
-  - requests=2.28.2
+  - requests=2.31.0
   - rfc3339-validator=0.1.4
   - rfc3986-validator=0.1.1
   - rtree=1.0.1
   - scikit-learn=1.2.2
   - scipy=1.10.1
-  - send2trash=1.8.0
-  - setuptools=67.6.1
+  - send2trash=1.8.2
+  - setuptools=67.7.2
   - shapely=2.0.1
-  - sip=6.7.8
+  - sip=6.7.9
   - six=1.16.0
   - smmap=3.0.5
   - snappy=1.1.10
@@ -304,15 +297,15 @@ dependencies:
   - soupsieve=2.3.2.post1
   - sphinx=5.3.0
   - sphinx-book-theme=1.0.1
-  - sphinx-design=0.3.0
+  - sphinx-design=0.4.1
   - sphinxcontrib-applehelp=1.0.4
   - sphinxcontrib-devhelp=1.0.2
   - sphinxcontrib-htmlhelp=2.0.1
   - sphinxcontrib-jsmath=1.0.1
   - sphinxcontrib-qthelp=1.0.3
   - sphinxcontrib-serializinghtml=1.1.5
-  - sqlalchemy=1.4.46
-  - sqlite=3.40.0
+  - sqlalchemy=2.0.16
+  - sqlite=3.42.0
   - stack_data=0.6.2
   - tabulate=0.9.0
   - terminado=0.17.1
@@ -320,20 +313,21 @@ dependencies:
   - tiledb=2.13.2
   - tinycss2=1.2.1
   - tk=8.6.12
-  - tokenize-rt=5.0.0
+  - tokenize-rt=5.1.0
   - toml=0.10.2
   - tomli=2.0.1
-  - tornado=6.2
+  - tornado=6.3.2
   - traitlets=5.9.0
-  - typing-extensions=4.5.0
-  - typing_extensions=4.5.0
+  - typing-extensions=4.6.3
+  - typing_extensions=4.6.3
+  - typing_utils=0.1.0
   - tzcode=2023c
   - tzdata=2023c
   - unicodedata2=15.0.0
-  - urllib3=1.26.15
+  - urllib3=2.0.3
   - wcwidth=0.2.6
   - webencodings=0.5.1
-  - websocket-client=1.5.1
+  - websocket-client=1.6.0
   - wheel=0.40.0
   - xcb-util=0.4.0
   - xcb-util-image=0.4.0
@@ -341,14 +335,14 @@ dependencies:
   - xcb-util-renderutil=0.3.9
   - xcb-util-wm=0.4.1
   - xerces-c=3.2.4
-  - xkeyboard-config=2.38
+  - xkeyboard-config=2.39
   - xorg-fixesproto=5.0
   - xorg-inputproto=2.3.2
   - xorg-kbproto=1.0.7
-  - xorg-libice=1.0.10
-  - xorg-libsm=1.2.3
-  - xorg-libx11=1.8.4
-  - xorg-libxau=1.0.9
+  - xorg-libice=1.1.1
+  - xorg-libsm=1.2.4
+  - xorg-libx11=1.8.6
+  - xorg-libxau=1.0.11
   - xorg-libxdmcp=1.1.3
   - xorg-libxext=1.3.4
   - xorg-libxfixes=5.0.3
@@ -360,7 +354,7 @@ dependencies:
   - xorg-xextproto=7.3.0
   - xorg-xf86vidmodeproto=2.3.1
   - xorg-xproto=7.0.31
-  - xyzservices=2023.2.0
+  - xyzservices=2023.5.0
   - xz=5.2.6
   - y-py=0.5.9
   - yaml=0.2.5
diff --git a/ci/python_310.yaml b/ci/python_310.yaml
index ba6ed7d5..6883cd69 100644
--- a/ci/python_310.yaml
+++ b/ci/python_310.yaml
@@ -13,6 +13,7 @@ dependencies:
     - jpype1
     - numpy
     - pandas
+    - pyproj
     - psutil
     - requests
     - shapely
diff --git a/ci/python_310_dev.yaml b/ci/python_310_dev.yaml
index 655c56cf..dd672009 100644
--- a/ci/python_310_dev.yaml
+++ b/ci/python_310_dev.yaml
@@ -14,6 +14,7 @@ dependencies:
     - jpype1
     - numpy
     - pandas
+    - pyproj
     - psutil
     - requests
 
diff --git a/ci/python_311.yaml b/ci/python_311.yaml
index 1b6a373a..50d93297 100644
--- a/ci/python_311.yaml
+++ b/ci/python_311.yaml
@@ -14,6 +14,7 @@ dependencies:
     - jpype1
     - numpy
     - pandas
+    - pyproj
     - psutil
     - requests
     - shapely
diff --git a/ci/python_311_dev.yaml b/ci/python_311_dev.yaml
index 54b0ad71..2d82455d 100644
--- a/ci/python_311_dev.yaml
+++ b/ci/python_311_dev.yaml
@@ -14,6 +14,7 @@ dependencies:
     - jpype1
     - numpy
     - pandas
+    - pyproj
     - psutil
     - requests
 
diff --git a/ci/python_38.yaml b/ci/python_38.yaml
index 4a93ef4e..44caf19c 100644
--- a/ci/python_38.yaml
+++ b/ci/python_38.yaml
@@ -13,6 +13,7 @@ dependencies:
     - jpype1
     - numpy
     - pandas
+    - pyproj
     - psutil
     - requests
     - shapely
diff --git a/ci/python_39.yaml b/ci/python_39.yaml
index 61ff59d0..9b32641a 100644
--- a/ci/python_39.yaml
+++ b/ci/python_39.yaml
@@ -13,6 +13,7 @@ dependencies:
     - jpype1
     - numpy
     - pandas
+    - pyproj
     - psutil
     - requests
     - shapely
diff --git a/docs/data/Helsinki/GTFS.zip b/docs/data/Helsinki/GTFS.zip
index e0aeff45..330afa60 100644
Binary files a/docs/data/Helsinki/GTFS.zip and b/docs/data/Helsinki/GTFS.zip differ
diff --git a/docs/data/README.md b/docs/data/README.md
index 6f74e7f8..a37a37a8 100644
--- a/docs/data/README.md
+++ b/docs/data/README.md
@@ -14,9 +14,6 @@ The data was created by Helsinki Region Transport (HLS) and obtained from
 
 This GTFS data set is licensed under [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/).
 
-To reduce the size of the sample dataset, it was cropped to cover only parts of the City of Helsinki,
-and a limited time frame only.
-
 
 ### `kantakaupunki.osm.pbf`
 
diff --git a/docs/requirements.txt b/docs/requirements.txt
index 5fea61e3..df9d976a 100644
--- a/docs/requirements.txt
+++ b/docs/requirements.txt
@@ -20,6 +20,7 @@ nbsphinx
 numpy
 numpydoc
 pandas
+pyproj
 psutil
 rasterio>=1.3a3
 shapely
diff --git a/pyproject.toml b/pyproject.toml
index c6edea6f..825ef608 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -25,6 +25,7 @@ dependencies = [
     "numpy",
     "pandas",
     "psutil",
+    "pyproj",
     "requests",
     "shapely>=2.0"
 ]
@@ -51,14 +52,14 @@ Repository = "https://github.com/r5py/r5py.git"
 "Bug tracker" = "https://github.com/r5py/r5py/issues"
 
 [tool.coverage.run]
-omit = ["tests/*"]
+omit = ["tests/*", ".virtualenv/**/*"]
 
 [tool.jupytext.formats]
 "docs/notebooks/" = "ipynb"
 "docs/md/" = "md"
 
 [tool.pytest.ini_options]
-addopts = "-p no:faulthandler --cov --cov-report term-missing"
+addopts = "-p no:faulthandler --cov --cov-report term-missing --cov-report xml"
 filterwarnings = [
     "error",
     "ignore:Could not find R5 jar, trying to download it from upstream",
diff --git a/src/r5py/r5/access_leg.py b/src/r5py/r5/access_leg.py
new file mode 100644
index 00000000..ddf400ca
--- /dev/null
+++ b/src/r5py/r5/access_leg.py
@@ -0,0 +1,14 @@
+#!/usr/bin/env python3
+
+
+"""Represent one leg of a trip, specifically access to a public transport stop."""
+
+
+from .transfer_leg import TransferLeg
+
+
+__all__ = ["AccessLeg"]
+
+
+class AccessLeg(TransferLeg):
+    """Represent one leg of a trip, specifically access to a public transport stop."""
diff --git a/src/r5py/r5/base_travel_time_matrix_computer.py b/src/r5py/r5/base_travel_time_matrix_computer.py
index 91b3ef01..1b51cc68 100644
--- a/src/r5py/r5/base_travel_time_matrix_computer.py
+++ b/src/r5py/r5/base_travel_time_matrix_computer.py
@@ -2,22 +2,17 @@
 
 """Calculate travel times between many origins and destinations."""
 
-import copy
 import math
 import multiprocessing
 import warnings
 
-import joblib
 import numpy
-import pandas
 import shapely
 
 from ..util import check_od_data_set, Config
 from .regional_task import RegionalTask
 from .transport_network import TransportNetwork
 
-import com.conveyal.r5
-
 
 __all__ = ["BaseTravelTimeMatrixComputer"]
 
@@ -28,16 +23,20 @@
 # how many (Python) threads to start
 # (they still run many Java threads, so be careful what you wish for ;) )
 # TODO: benchmark the optimal number of threads
-NUM_THREADS = math.ceil(multiprocessing.cpu_count() * 0.75)
+NUM_THREADS = math.ceil(multiprocessing.cpu_count() * 0.5)
 
 
 class BaseTravelTimeMatrixComputer:
     """Base class for travel time computers between many origins and destinations."""
 
+    MAX_INT32 = MAX_INT32
+
+    NUM_THREADS = NUM_THREADS
+
     def __init__(
         self,
         transport_network,
-        origins,
+        origins=None,
         destinations=None,
         snap_to_network=False,
         **kwargs,
@@ -79,76 +78,27 @@ def __init__(
         self.snap_to_network = snap_to_network
 
         self.origins = origins
-        if destinations is None:
-            destinations = origins
         self.destinations = destinations
 
         self.request = RegionalTask(
             transport_network,
-            origins.iloc[0].geometry,  # any one origin (potentially overriden later)
-            destinations,
+            origin=None,
+            destinations=None,
             **kwargs,
         )
 
         self.verbose = Config().arguments.verbose
 
-    def compute_travel_times(self):
-        """
-        Compute travel times from all origins to all destinations.
-
-        Returns
-        -------
-        pandas.DataFrame
-            A data frame containing the columns ``from_id``, ``to_id``, and
-            ``travel_time``, where ``travel_time`` is the median calculated
-            travel time between ``from_id`` and ``to_id`` or ``numpy.nan``
-            if no connection with the given parameters was found.
-            If non-default ``percentiles`` were requested: one or more columns
-            ``travel_time_p{:02d}`` representing the particular percentile of
-            travel time.
-        """
-        # loop over all origins, modify the request, and compute the times
-        # to all destinations.
-        with joblib.Parallel(
-            prefer="threads",
-            verbose=(10 * self.verbose),  # joblib has a funny verbosity scale
-            n_jobs=NUM_THREADS,
-        ) as parallel:
-            od_matrix = pandas.concat(
-                parallel(
-                    joblib.delayed(self._travel_times_per_origin)(from_id)
-                    for from_id in self.origins.id
-                )
-            )
-
-        try:
-            od_matrix = od_matrix.to_crs(self._origins_crs)
-        except AttributeError:  # (not a GeoDataFrame)
-            pass
-        return od_matrix
-
     @property
     def destinations(self):
         return self._destinations
 
     @destinations.setter
     def destinations(self, destinations):
-        check_od_data_set(destinations)
-        self._destinations_crs = destinations.crs
-        self._destinations = destinations.to_crs("EPSG:4326").copy()
-        if self.snap_to_network:
-            self._destinations.geometry = self.transport_network.snap_to_network(
-                self._destinations.geometry
-            )
-            if len(self._destinations[self._destinations.geometry == shapely.Point()]):
-                # if there are destinations for which no snapped point could be found
-                warnings.warn(
-                    "Some destination points could not be snapped to the street network",
-                    RuntimeWarning,
-                )
-                self._destinations = self._destinations[
-                    self._destinations.geometry != shapely.Point()
-                ].copy()
+        if destinations is not None:
+            check_od_data_set(destinations)
+            self._destinations_crs = destinations.crs
+            self._destinations = destinations.to_crs("EPSG:4326").copy()
 
     def _fill_nulls(self, data_set):
         """
@@ -169,38 +119,57 @@ def _fill_nulls(self, data_set):
         """
         return data_set.applymap(lambda x: numpy.nan if x == MAX_INT32 else x)
 
-    @property
-    def origins(self):
-        return self._origins
+    def _prepare_origins_destinations(self):
+        """
+        Make sure we received enough information to route from origins to
+        destinations.
+        """
+        try:
+            self.origins
+        except AttributeError as exception:
+            raise ValueError("No routing origins defined") from exception
 
-    @origins.setter
-    def origins(self, origins):
-        check_od_data_set(origins)
-        self._origins_crs = origins.crs
-        self._origins = origins.to_crs("EPSG:4326").copy()
-        if self.snap_to_network:
-            self._origins.geometry = self.transport_network.snap_to_network(
-                self._origins.geometry
-            )
-            if len(self._origins[self._origins.geometry == shapely.Point()]):
-                # if there are origins for which no snapped point could be found
+        try:
+            self.destinations
+            assert self.destinations is not None
+        except (AssertionError, AttributeError):
+            self.destinations = self.origins.copy()
+            if self.verbose:
                 warnings.warn(
-                    "Some origin points could not be snapped to the street network",
+                    "No routing destinations defined, using origins as destinations, too.",
                     RuntimeWarning,
                 )
-                self._origins = self._origins[
-                    self._origins.geometry != shapely.Point()
-                ].copy()
 
-    def _travel_times_per_origin(self, from_id):
-        request = copy.copy(self.request)
-        request.origin = self.origins[self.origins.id == from_id].geometry.item()
+        if self.snap_to_network:
+            for which_end in ("origins", "destinations"):
+                points = getattr(self, f"_{which_end}")
+                points.geometry = self.transport_network.snap_to_network(
+                    points.geometry
+                )
+                if len(points[points.geometry == shapely.Point()]):
+                    # if there are origins/destinations for which no snapped point could be found
+                    points = points[points.geometry != shapely.Point()]
+                    warnings.warn(
+                        f"Some {which_end[:-1]} points could not be snapped to the street network",
+                        RuntimeWarning,
+                    )
+
+                    if points.empty:
+                        raise ValueError(
+                            f"After snapping, no valid {which_end[:-1]} points remain"
+                        )
 
-        travel_time_computer = com.conveyal.r5.analyst.TravelTimeComputer(
-            request, self.transport_network
-        )
-        results = travel_time_computer.computeTravelTimes()
+                setattr(self, f"_{which_end}", points.copy())
 
-        od_matrix = self._parse_results(from_id, results)
+            self.snap_to_network = False  # prevent repeated snapping on same point sets
 
-        return od_matrix
+    @property
+    def origins(self):
+        return self._origins
+
+    @origins.setter
+    def origins(self, origins):
+        if origins is not None:
+            check_od_data_set(origins)
+            self._origins_crs = origins.crs
+            self._origins = origins.to_crs("EPSG:4326").copy()
diff --git a/src/r5py/r5/detailed_itineraries_computer.py b/src/r5py/r5/detailed_itineraries_computer.py
index af7885b5..40cc4783 100644
--- a/src/r5py/r5/detailed_itineraries_computer.py
+++ b/src/r5py/r5/detailed_itineraries_computer.py
@@ -1,42 +1,35 @@
 #!/usr/bin/env python3
 
+
 """Calculate detailed itineraries between many origins and destinations."""
 
+
+import copy
+import warnings
+
+import joblib
 import pandas
 
-from .breakdown_stat import BreakdownStat
-from .travel_time_matrix_computer import TravelTimeMatrixComputer
+from .base_travel_time_matrix_computer import BaseTravelTimeMatrixComputer
+from .trip import Trip
+from .trip_planner import TripPlanner
 
 
 __all__ = ["DetailedItinerariesComputer"]
 
 
-# Which columns (and types) are returned by
-# com.conveyal.r5.analyst.cluster.PathResult.summarizeIterations?
-# Note that these are already camel_case (Python) names and pandas/numpy (d)types.
-DATA_COLUMNS = {
-    "routes": pandas.SparseDtype(str),
-    "board_stops": pandas.SparseDtype(str),
-    "alight_stops": pandas.SparseDtype(str),
-    "ride_times": pandas.SparseDtype(float),
-    "access_time": float,
-    "egress_time": float,
-    "transfer_time": float,
-    "wait_times": pandas.SparseDtype(float),
-    "total_time": float,
-    "n_iterations": int,
-}
-
-
-class DetailedItinerariesComputer(TravelTimeMatrixComputer):
+class DetailedItinerariesComputer(BaseTravelTimeMatrixComputer):
     """Compute detailed itineraries between many origins and destinations."""
 
+    COLUMNS = ["from_id", "to_id", "option"] + Trip.COLUMNS
+
     def __init__(
         self,
         transport_network,
-        origins,
+        origins=None,
         destinations=None,
-        breakdown_stat=BreakdownStat.MEAN,
+        snap_to_network=False,
+        force_all_to_all=False,
         **kwargs,
     ):
         """
@@ -57,161 +50,131 @@ def __init__(
             Places to find a route _to_
             Has to have a point geometry, and at least an `id` column
             If omitted, use same data set as for origins
-        breakdown_stat : r5py.BreakdownStat
-            Summarise the values of the detailed breakdown using this statistical function.
-            Default: r5py.BreakdownStat.MEAN
+        snap_to_network : bool or int, default False
+            Should origin an destination points be snapped to the street network
+            before routing? If `True`, the default search radius (defined in
+            `com.conveyal.r5.streets.StreetLayer.LINK_RADIUS_METERS`) is used,
+            if `int`, use `snap_to_network` meters as the search radius.
+        force_all_to_all : bool, default False
+            If ``origins`` and ``destinations`` have the same length, by
+            default, ``DetailedItinerariesComputer`` finds routes between pairs
+            of origins and destinations, i.e., it routes from origin #1 to
+            destination #1, origin #2 to destination #2, ... .
+            Set ``all_to_all=True`` to route from each origin to all
+            destinations (this is the default, if ``origins`` and ``destinations``
+            have different lengths, or if ``destinations`` is omitted)
         **kwargs : mixed
             Any arguments than can be passed to r5py.RegionalTask:
             ``departure``, ``departure_time_window``, ``percentiles``, ``transport_modes``,
             ``access_modes``, ``egress_modes``, ``max_time``, ``max_time_walking``,
             ``max_time_cycling``, ``max_time_driving``, ``speed_cycling``, ``speed_walking``,
             ``max_public_transport_rides``, ``max_bicycle_traffic_stress``
+            Not that not all arguments might make sense in this context, and the
+            underlying R5 engine might ignore some of them.
         """
-        super().__init__(transport_network, origins, destinations, **kwargs)
-
-        self.request.breakdown = self.breakdown = True
-        self.breakdown_stat = breakdown_stat
+        super().__init__(
+            transport_network,
+            origins,
+            destinations,
+            snap_to_network,
+            **kwargs,
+        )
 
-    def compute_travel_times(self):
+        if destinations is None:
+            self.all_to_all = True
+            if self.verbose:
+                warnings.warn(
+                    "No destinations specified, computing an all-to-all matrix",
+                    RuntimeWarning,
+                )
+
+        elif len(origins) != len(destinations):
+            self.all_to_all = True
+            if self.verbose:
+                warnings.warn(
+                    "Origins and destinations are of different length, computing an all-to-all matrix",
+                    RuntimeWarning,
+                )
+        else:
+            self.all_to_all = force_all_to_all
+
+    def compute_travel_details(self):
         """
         Compute travel times from all origins to all destinations.
 
         Returns
         -------
         pandas.DataFrame
-            A data frame containing the columns ``from_id``, ``to_id``, and
-            ``travel_time``, where ``travel_time`` is the median calculated
-            travel time between ``from_id`` and ``to_id`` or ``numpy.nan``
-            if no connection with the given parameters was found.
-            Detailed information about the routes found is reported in the
-            columns ``routes`` (route numbers of public transport lines),
-            ``board_stops`` and ``alight_stops`` (public transport stops at
-            which a vehicle/line is boarded or alighted), ``ride_times`` (times
-            aboard a public transport vehicle or line), ``access_time`` (time
-            to reach the road (when on car or bicycle) or the first public transport
-            stop from ``from_id``), ``egress_time`` (time from road or last
-            public transport stop to ``to_id``), ``total_time`` (overall travel
-            time), and ``n_iterations`` (number of McRAPTOR iterations used).
-            If non-default ``percentiles`` were requested: one or more columns
-            ``travel_time_p{:02d}`` representing the particular percentile of
-            travel time.
-
+            TODO: Add description of output data frame columns and format
         """
-        return super().compute_travel_times()
 
-    def _parse_results(self, from_id, results):
-        """
-        Parse the *detailed* results of an R5 TravelTimeMatrix.
-
-        Parse data as returned from `com.conveyal.r5.analyst.TravelTimeComputer.computeTravelTimes()`,
-        cast data to Python types, and return as a `pandas.Dataframe`. Because of the way r5py
-        and R5 interact, this parses the results of routing from one origin to many (all) destinations.
+        self._prepare_origins_destinations()
+
+        # loop over all origin/destination pairs, modify the request, and
+        # compute times, distance, and other details for each trip
+        with joblib.Parallel(
+            prefer="threads",
+            verbose=(10 * self.verbose),  # joblib has a funny verbosity scale
+            n_jobs=self.NUM_THREADS,
+        ) as parallel:
+            od_matrix = pandas.concat(
+                parallel(
+                    joblib.delayed(self._travel_details_per_od_pair)(from_id, to_id)
+                    for _, (from_id, to_id) in self.od_pairs.iterrows()
+                ),
+                ignore_index=True,
+            )
 
-        Arguments
-        ---------
-        from_id : mixed
-            The value of the ID column of the origin record to report on.
-        results : `com.conveyal.r5.OneOriginResult` (Java object)
+        try:
+            od_matrix = od_matrix.to_crs(self._origins_crs)
+        except AttributeError:  # (not a GeoDataFrame)
+            pass
+        return od_matrix
 
-        Returns
-        -------
-        pandas.DataFrame
-            A data frame containing the columns ``from_id``, ``to_id``, and
-            detailed information about the routes, reported in the
-            columns ``routes`` (route numbers of public transport lines),
-            ``board_stops`` and ``alight_stops`` (public transport stops at
-            which a vehicle/line is boarded or alighted), ``ride_times`` (times
-            aboard a public transport vehicle or line), ``access_time`` (time
-            to reach the road (when on car or bicycle) or the first public transport
-            stop from ``from_id``), ``egress_time`` (time from road or last
-            public transport stop to ``to_id``), ``total_time`` (overall travel
-            time), and ``n_iterations`` (number of McRAPTOR iterations used).
-
-            Results can contain more than one row per ``from_id``/``to_id`` pair.
+    def _prepare_origins_destinations(self):
         """
-        # Let’s start with an empty DataFrame with the correct columns (and types)
-        columns = {
-            "from_id": [],
-            "to_id": [],
-        }
-        columns.update(
-            {
-                column_name: pandas.Series(dtype=column_type)
-                for column_name, column_type in DATA_COLUMNS.items()
-            }
-        )
-        od_matrix = pandas.DataFrame(columns)
-
-        # R5 returns multiple rows per O/D-pair, in a nested array
-        # (first dimension == to_id, second dimension == different
-        # routes for O/D pair, third dimension == data records)
-        #
-        # On top of that, some of the columns are arrays themselves,
-        # and are represented as a pipe-separated string
-        #
-        # typically, data look something like this:
-        #
-        # [
-        #   [
-        #      ['1002|1003', '1020446|1090415', '1090415|1070417', '6.0|1.0', '7.3', '3.7', '0.0', '1.3|2.0', '21.3', '1']  # noqa: E501
-        #       ...
-        #   ],
-        #    ...
-        # ]
-
-        # First, unnest the data, and add `from_id` and `to_id` to each row
-        # (After this, we have a 2dim table of several rows per O/D pair)
-        details = [
-            [from_id, to_id] + list(row)
-            for to_id, record in zip(
-                self.destinations.id,
-                results.paths.summarizeIterations(self.breakdown_stat.value),
+        Make sure we received enough information to route from origins to
+        destinations.
+        """
+
+        super()._prepare_origins_destinations()
+
+        if self.all_to_all:
+            # manually create a list of all all-to-all permutations
+            self.od_pairs = self.origins[["id"]].join(
+                self.destinations[["id"]],
+                how="cross",
+                lsuffix="_origin",
+                rsuffix="_destination",
+            )
+        else:
+            # origins and destinations are same length, run one-to-one routing
+            self.od_pairs = pandas.DataFrame(
+                {
+                    "id_origin": self.origins.id.values,
+                    "id_destination": self.destinations.id.values,
+                }
             )
-            for row in record
-        ]
 
-        # Because casting directly from jpype’s Java types does not always work
-        # perfectly (probably because the Python types/‘cast function’ accept
-        # mixed values, so jpype cannot decide), let’s just cast _everything_ to
-        # ‘nullable’ `str` to be on the safe side
-        details = [
-            [None if value is None else str(value) for value in row] for row in details
-        ]
+    def _travel_details_per_od_pair(self, from_id, to_id):
+        origin = self.origins[self.origins.id == from_id]
+        destination = self.destinations[self.destinations.id == to_id]
 
-        # Then, cast the data to Python types, and split the pipe-separated arrays
-        #
-        # We do this by column, because it should be fast in pandas, and because
-        # more legible and comprehensible what we do.
-        # (`zip(*details)` transposes rows and columns)
-        details = list(zip(*details))
-        od_matrix["from_id"] = details[0]
-        od_matrix["to_id"] = details[1]
-
-        for column_name, column_type, column_data in zip(
-            DATA_COLUMNS.keys(),
-            DATA_COLUMNS.values(),
-            details[2:],
-        ):
-            # split the array columns (they are pipe-separated strings)
-            # also, cast to destination type
-            if isinstance(column_type, pandas.SparseDtype):
-                column_data = [
-                    [
-                        column_type.subtype.type(array_member_value)  # cast
-                        if array_member_value
-                        else None  # if False-ish -> None
-                        for array_member_value in value.split("|")
-                    ]
-                    if value is not None
-                    else []
-                    for value in column_data
-                ]
-            else:
-                column_data = [
-                    column_type(value) if value else None  # cast (nullable)
-                    for value in column_data
-                ]
-
-            od_matrix[column_name] = column_data
+        request = copy.copy(self.request)
+        request._regional_task.fromLat = origin.geometry.item().y
+        request._regional_task.fromLon = origin.geometry.item().x
+        request._regional_task.toLat = destination.geometry.item().y
+        request._regional_task.toLon = destination.geometry.item().x
 
-        return od_matrix
+        trip_planner = TripPlanner(self.transport_network, request)
+        trips = trip_planner.trips
+
+        # fmt: off
+        trips = [
+            [from_id, to_id, option] + segment
+            for option, trip in enumerate(trips)
+            for segment in trip.as_table()
+        ]
+        # fmt: on
+
+        return pandas.DataFrame(trips, columns=self.COLUMNS)
diff --git a/src/r5py/r5/direct_leg.py b/src/r5py/r5/direct_leg.py
new file mode 100644
index 00000000..a486614a
--- /dev/null
+++ b/src/r5py/r5/direct_leg.py
@@ -0,0 +1,42 @@
+#!/usr/bin/env python3
+
+
+"""Represent one leg of a direct mode (walk, cycle, car) trip."""
+
+
+import datetime
+
+import shapely
+
+from .trip_leg import TripLeg
+
+
+__all__ = ["DirectLeg"]
+
+
+class DirectLeg(TripLeg):
+    """
+    Represent one leg of a public transport trip.
+    """
+
+    def __init__(self, transport_mode, street_segment):
+        """
+        Represent one leg of a public transport trip.
+
+        Arguments
+        =========
+        transport_mode : r5py.TransportMode
+            mode of transport this trip leg was travelled
+        street_segment : com.conveyal.r5.profile.StreetSegment
+            the leg’s data as output by R5’s `StreetRouter`
+        """
+        distance = street_segment.distance / 1000.0  # millimetres!
+        travel_time = datetime.timedelta(seconds=street_segment.duration)
+        geometry = shapely.from_wkt(str(street_segment.geometry))
+
+        super().__init__(
+            transport_mode=transport_mode,
+            distance=distance,
+            travel_time=travel_time,
+            geometry=geometry,
+        )
diff --git a/src/r5py/r5/egress_leg.py b/src/r5py/r5/egress_leg.py
new file mode 100644
index 00000000..3eaeb140
--- /dev/null
+++ b/src/r5py/r5/egress_leg.py
@@ -0,0 +1,14 @@
+#!/usr/bin/env python3
+
+
+"""Represent one leg of a trip, specifically egress from a public transport stop."""
+
+
+from .transfer_leg import TransferLeg
+
+
+__all__ = ["EgressLeg"]
+
+
+class EgressLeg(TransferLeg):
+    """Represent one leg of a trip, specifically egress from a public transport stop."""
diff --git a/src/r5py/r5/regional_task.py b/src/r5py/r5/regional_task.py
index 5b2dd875..e68bcb89 100644
--- a/src/r5py/r5/regional_task.py
+++ b/src/r5py/r5/regional_task.py
@@ -29,8 +29,8 @@ class RegionalTask:
     def __init__(
         self,
         transport_network,
-        origin,
-        destinations,
+        origin=None,
+        destinations=None,
         departure=datetime.datetime.now(),
         departure_time_window=datetime.timedelta(hours=1),
         percentiles=[50],
@@ -204,10 +204,13 @@ def breakdown(self, breakdown):
         # information, and it’s set to 5000 by default.
         # The value is a static property of com.conveyal.r5.analyst.cluster.PathResult;
         # static properites of Java classes can be modified in a singleton kind of way
-        com.conveyal.r5.analyst.cluster.PathResult.maxDestinations = max(
-            com.conveyal.r5.analyst.cluster.PathResult.maxDestinations,
-            len(self.destinations) + 1,
-        )
+        try:
+            com.conveyal.r5.analyst.cluster.PathResult.maxDestinations = max(
+                com.conveyal.r5.analyst.cluster.PathResult.maxDestinations,
+                len(self.destinations) + 1,
+            )
+        except AttributeError:
+            pass
 
     @property
     def departure(self):
@@ -270,36 +273,37 @@ def destinations(self):
 
     @destinations.setter
     def destinations(self, destinations):
-        self._destinations = destinations
-
-        # wrap destinations in a few layers of streams (yeah, Java)
-        output_stream = java.io.ByteArrayOutputStream()
-        data_output_stream = java.io.DataOutputStream(output_stream)
-
-        # first: number of destinations
-        data_output_stream.writeInt(len(destinations))
-
-        # then, data columns, one by one, then still ‘opportunties’
-        for id_ in destinations.id.astype(str):
-            data_output_stream.writeUTF(id_)
-        for lat in destinations.geometry.y:
-            data_output_stream.writeDouble(lat)
-        for lon in destinations.geometry.x:
-            data_output_stream.writeDouble(lon)
-        for _ in range(len(destinations)):
-            data_output_stream.writeDouble(0)  # ‘opportunities’
-
-        # convert to input stream, then into a point set
-        destinations_point_set = com.conveyal.r5.analyst.FreeFormPointSet(
-            java.io.ByteArrayInputStream(output_stream.toByteArray())
-        )
+        if destinations is not None:
+            self._destinations = destinations
+
+            # wrap destinations in a few layers of streams (yeah, Java)
+            output_stream = java.io.ByteArrayOutputStream()
+            data_output_stream = java.io.DataOutputStream(output_stream)
+
+            # first: number of destinations
+            data_output_stream.writeInt(len(destinations))
+
+            # then, data columns, one by one, then still ‘opportunties’
+            for id_ in destinations.id.astype(str):
+                data_output_stream.writeUTF(id_)
+            for lat in destinations.geometry.y:
+                data_output_stream.writeDouble(lat)
+            for lon in destinations.geometry.x:
+                data_output_stream.writeDouble(lon)
+            for _ in range(len(destinations)):
+                data_output_stream.writeDouble(0)  # ‘opportunities’
+
+            # convert to input stream, then into a point set
+            destinations_point_set = com.conveyal.r5.analyst.FreeFormPointSet(
+                java.io.ByteArrayInputStream(output_stream.toByteArray())
+            )
 
-        self._regional_task.destinationPointSets = [destinations_point_set]
+            self._regional_task.destinationPointSets = [destinations_point_set]
 
-        # TODO: figure out whether we could cut this a bit shorter. We should be able
-        # to construct the ByteArray fed to java.io.ByteArrayInputStream as a Python `bytes`
-        # without the detour via two Java OutputStreams.
-        # (but not sure how to distinguish between the writeUTF/writeDouble/etc)
+            # TODO: figure out whether we could cut this a bit shorter. We should be able
+            # to construct the ByteArray fed to java.io.ByteArrayInputStream as a Python `bytes`
+            # without the detour via two Java OutputStreams.
+            # (but not sure how to distinguish between the writeUTF/writeDouble/etc)
 
     @property
     def egress_modes(self):
@@ -435,9 +439,10 @@ def origin(self, origin):
         origin : shapely.geometry.Point
             Point to route from
         """
-        self._origin = origin
-        self._regional_task.fromLat = origin.y
-        self._regional_task.fromLon = origin.x
+        if origin is not None:
+            self._origin = origin
+            self._regional_task.fromLat = origin.y
+            self._regional_task.fromLon = origin.x
 
     @property
     def scenario(self):
@@ -500,9 +505,10 @@ def transport_modes(self, transport_modes):
                 # let people walk to and from the stops
                 access_modes = direct_modes = [TransportMode.WALK]
             else:
-                access_modes = direct_modes
-        else:
-            # no public transport
+                # otherwise, include the supplied direct modes into access_modes
+                access_modes = set(list(self.access_modes) + direct_modes)
+
+        else:  # no public transport
             egress_modes = []  # ignore egress (why?)
 
             #     # this is weird (the following is the logic implemented in r5r)
@@ -518,9 +524,9 @@ def transport_modes(self, transport_modes):
             # let’s do that differently (even if potentially more expensive, computationally)
             access_modes = direct_modes
 
-        # assign the calculated modes
         self.access_modes = access_modes
         self.egress_modes = egress_modes
+
         self._regional_task.transitModes = RegionalTask._enum_set(
             transit_modes, com.conveyal.r5.api.util.TransitModes
         )
@@ -532,13 +538,14 @@ def transport_modes(self, transport_modes):
         # (for fully-interconnected travel time matrices this also covers all origin points,
         # but potentially this needs to be extended to run also for origins) //TODO
 
-        for mode in direct_modes:
-            for destination_point_set in self._regional_task.destinationPointSets:
-                self.transport_network.linkage_cache.getLinkage(
-                    destination_point_set,
-                    self.transport_network.street_layer,
-                    mode,
-                )
+        if self._regional_task.destinationPointSets is not None:
+            for mode in direct_modes:
+                for destination_point_set in self._regional_task.destinationPointSets:
+                    self.transport_network.linkage_cache.getLinkage(
+                        destination_point_set,
+                        self.transport_network.street_layer,
+                        mode,
+                    )
 
     @staticmethod
     def _enum_set(values, java_class):
@@ -552,6 +559,9 @@ def _enum_set(values, java_class):
 @jpype._jcustomizer.JConversion(
     "com.conveyal.r5.analyst.cluster.AnalysisWorkerTask", exact=RegionalTask
 )
+@jpype._jcustomizer.JConversion(
+    "com.conveyal.r5.profile.ProfileRequest", exact=RegionalTask
+)
 @jpype._jcustomizer.JConversion(
     "com.conveyal.r5.analyst.cluster.RegionalTask", exact=RegionalTask
 )
diff --git a/src/r5py/r5/street_layer.py b/src/r5py/r5/street_layer.py
index 2de72417..b7b3fb1a 100644
--- a/src/r5py/r5/street_layer.py
+++ b/src/r5py/r5/street_layer.py
@@ -4,6 +4,8 @@
 """Wraps a com.conveyal.r5.streets.StreetLayer."""
 
 
+import functools
+
 import jpype
 import jpype.types
 import shapely
@@ -36,6 +38,16 @@ def from_r5_street_layer(cls, street_layer):
         instance._street_layer = street_layer
         return instance
 
+    @functools.cached_property
+    def extent(self):
+        envelope = self._street_layer.envelope
+        return shapely.box(
+            envelope.getMinX(),
+            envelope.getMinY(),
+            envelope.getMaxX(),
+            envelope.getMaxY(),
+        )
+
     def find_split(
         self,
         point,
diff --git a/src/r5py/r5/transfer_leg.py b/src/r5py/r5/transfer_leg.py
new file mode 100644
index 00000000..6104bb78
--- /dev/null
+++ b/src/r5py/r5/transfer_leg.py
@@ -0,0 +1,18 @@
+#!/usr/bin/env python3
+
+
+"""Represent one leg of a trip, specifically transfers between public transport
+vehicles."""
+
+
+from .direct_leg import DirectLeg
+
+
+__all__ = ["TransferLeg"]
+
+
+class TransferLeg(DirectLeg):
+    """
+    Represent one leg of a trip, specifically transfers between public transport
+    vehicles.
+    """
diff --git a/src/r5py/r5/transit_layer.py b/src/r5py/r5/transit_layer.py
index 5f4c79c4..0fb690b3 100644
--- a/src/r5py/r5/transit_layer.py
+++ b/src/r5py/r5/transit_layer.py
@@ -71,6 +71,31 @@ def covers(self, point_in_time):
             covers = False
         return covers
 
+    def get_street_vertex_for_stop(self, stop):
+        """
+        Get the street layer’s vertex corresponding to `stop`.
+
+        Arguments
+        ---------
+        stop : int
+            ID of the public transport stop for which to find a vertex
+
+        Returns
+        -------
+        int
+            ID of the vertex corresponding to the public transport stop
+        """
+        street_vertex = self._transit_layer.streetVertexForStop.get(stop)
+        return street_vertex
+
+    @functools.cached_property
+    def routes(self):
+        return list(self._transit_layer.routes)
+
+    @functools.cached_property
+    def trip_patterns(self):
+        return list(self._transit_layer.tripPatterns)
+
 
 @jpype._jcustomizer.JConversion(
     "com.conveyal.r5.transit.TransitLayer", exact=TransitLayer
diff --git a/src/r5py/r5/transit_leg.py b/src/r5py/r5/transit_leg.py
new file mode 100644
index 00000000..785576bf
--- /dev/null
+++ b/src/r5py/r5/transit_leg.py
@@ -0,0 +1,18 @@
+#!/usr/bin/env python3
+
+
+"""Represent one leg of a public transport trip."""
+
+
+from .trip_leg import TripLeg
+
+
+__all__ = ["TransitLeg"]
+
+
+class TransitLeg(TripLeg):
+    """
+    Represent one leg of a public transport trip.
+    """
+
+    pass
diff --git a/src/r5py/r5/transport_mode.py b/src/r5py/r5/transport_mode.py
index 6f75550f..2049b05e 100644
--- a/src/r5py/r5/transport_mode.py
+++ b/src/r5py/r5/transport_mode.py
@@ -67,6 +67,24 @@ def _missing_(cls, value):
                 return member
         return None
 
+    def __add__(self, other):
+        if isinstance(other, self.__class__):
+            return [self, other]
+        elif isinstance(other, list):
+            return [self] + other
+        else:
+            raise TypeError(
+                f"unsupported operand type(s) for '+': '{type(other)}' and '{type(self)}'"
+            )
+
+    def __radd__(self, other):
+        if other == 0:  # first iteration of sum()
+            return self
+        elif isinstance(other, list):
+            return other + [self]
+        else:
+            return self.__add__(other)
+
     @property
     def is_leg_mode(self):
         """Can this TransportMode function as a LegMode?"""
diff --git a/src/r5py/r5/transport_network.py b/src/r5py/r5/transport_network.py
index a023e609..aed7b865 100644
--- a/src/r5py/r5/transport_network.py
+++ b/src/r5py/r5/transport_network.py
@@ -6,7 +6,9 @@
 
 import functools
 import pathlib
+import random
 import shutil
+import time
 import warnings
 
 import filelock
@@ -18,14 +20,17 @@
 from .transport_mode import TransportMode
 from ..util import Config, contains_gtfs_data, start_jvm
 
+import com.conveyal.gtfs
+import com.conveyal.osmlib
 import com.conveyal.r5
-import java.lang
-import java.util.ArrayList
 
 
 __all__ = ["TransportNetwork"]
 
 
+PACKAGE = __package__.split(".")[0]
+
+
 start_jvm()
 
 
@@ -46,14 +51,97 @@ def __init__(self, osm_pbf, gtfs=[]):
         osm_pbf = self._working_copy(pathlib.Path(osm_pbf)).absolute()
         gtfs = [str(self._working_copy(path).absolute()) for path in gtfs]
 
-        self._transport_network = com.conveyal.r5.transit.TransportNetwork.fromFiles(
-            java.lang.String(str(osm_pbf)),
-            java.util.ArrayList.of(gtfs),
-        )
-        self._transport_network.streetLayer.buildEdgeLists()
-        self._transport_network.streetLayer.indexStreets()
-        self._transport_network.rebuildTransientIndexes()
-        self._transport_network.transitLayer.buildDistanceTables(None)
+        transport_network = com.conveyal.r5.transit.TransportNetwork()
+        transport_network.scenarioId = PACKAGE
+
+        osm_mapdb = pathlib.Path(f"{osm_pbf}.mapdb")
+        osm_file = com.conveyal.osmlib.OSM(f"{osm_mapdb}")
+        osm_file.intersectionDetection = True
+        osm_file.readFromFile(f"{osm_pbf}")
+
+        self.osm_file = osm_file  # keep the mapdb open, close in destructor
+
+        transport_network.streetLayer = com.conveyal.r5.streets.StreetLayer()
+        transport_network.streetLayer.loadFromOsm(osm_file)
+        transport_network.streetLayer.parentNetwork = transport_network
+        transport_network.streetLayer.indexStreets()
+
+        transport_network.transitLayer = com.conveyal.r5.transit.TransitLayer()
+        for gtfs_file in gtfs:
+            gtfs_feed = com.conveyal.gtfs.GTFSFeed.readOnlyTempFileFromGtfs(gtfs_file)
+            transport_network.transitLayer.loadFromGtfs(gtfs_feed)
+            gtfs_feed.close()
+        transport_network.transitLayer.parentNetwork = transport_network
+
+        transport_network.streetLayer.associateStops(transport_network.transitLayer)
+        transport_network.streetLayer.buildEdgeLists()
+
+        transport_network.transitLayer.rebuildTransientIndexes()
+
+        transfer_finder = com.conveyal.r5.transit.TransferFinder(transport_network)
+        transfer_finder.findTransfers()
+        transfer_finder.findParkRideTransfer()
+
+        transport_network.transitLayer.buildDistanceTables(None)
+
+        self._transport_network = transport_network
+
+    def __del__(self):
+        """
+        Delete all temporary files upon destruction.
+        """
+        MAX_TRIES = 10
+
+        # first, close the open osm_file,
+        # delete Java objects, and
+        # trigger Java garbage collection
+        self.osm_file.close()
+        try:
+            del self.street_layer
+        except AttributeError:  # might not have been accessed a single time
+            pass
+        try:
+            del self.transit_layer
+        except AttributeError:
+            pass
+        del self._transport_network
+
+        time.sleep(0.5)
+        jpype.java.lang.System.gc()
+
+        # then, try to delete all files in cache directory
+        temporary_files = [child for child in self._cache_directory.iterdir()]
+        for _ in range(MAX_TRIES):
+            for temporary_file in temporary_files:
+                try:
+                    temporary_file.unlink()
+                    temporary_files.remove(temporary_file)
+                except (FileNotFoundError, IOError, OSError):
+                    print(
+                        f"could not delete {temporary_file}, keeping in {temporary_files}"
+                    )
+                    pass
+
+            if not temporary_files:  # empty
+                break
+
+            print(f"waiting for still open files, round {_}")
+            time.sleep(0.1)
+        else:
+            remaining_files = ", ".join(
+                [f"{temporary_file}" for temporary_file in temporary_files]
+            )
+            warnings.warn(
+                f"Failed to clean cache directory ‘{self._cache_directory}’. "
+                f"Remaining file(s): {remaining_files}",
+                RuntimeWarning,
+            )
+
+        # finally, try to delete the cache directory itself
+        try:
+            self._cache_directory.rmdir()
+        except OSError:  # not empty
+            pass  # the JVM destructor is going to take care of this
 
     @classmethod
     def from_directory(cls, path):
@@ -110,11 +198,17 @@ def __exit__(self, exception_type, exception_value, traceback):
         """Exit context."""
         return False
 
+    @property
+    def extent(self):
+        # TODO: figure out how to get the extent of the GTFS schedule,
+        # then find the smaller extent of the two (or the larger one?)
+        return self.street_layer.extent
+
     @functools.cached_property
     def _cache_directory(self):
         cache_dir = (
             pathlib.Path(Config().TEMP_DIR)
-            / f"{self.__class__.__name__:s}_{id(self):x}"
+            / f"{self.__class__.__name__:s}_{id(self):x}_{random.randrange(16**5):07x}"
         )
         cache_dir.mkdir(exist_ok=True)
         return cache_dir
@@ -191,7 +285,7 @@ def snap_to_network(
             )
         )
 
-    @property
+    @functools.cached_property
     def street_layer(self):
         """Expose the `TransportNetwork`’s `streetLayer` to Python."""
         return StreetLayer.from_r5_street_layer(self._transport_network.streetLayer)
@@ -204,10 +298,7 @@ def timezone(self):
     @functools.cached_property
     def transit_layer(self):
         """Expose the `TransportNetwork`’s `transitLayer` to Python."""
-        transit_layer = TransitLayer.from_r5_transit_layer(
-            self._transport_network.transitLayer
-        )
-        return transit_layer
+        return TransitLayer.from_r5_transit_layer(self._transport_network.transitLayer)
 
 
 @jpype._jcustomizer.JConversion(
diff --git a/src/r5py/r5/travel_time_matrix_computer.py b/src/r5py/r5/travel_time_matrix_computer.py
index 195c96d1..47e829ac 100644
--- a/src/r5py/r5/travel_time_matrix_computer.py
+++ b/src/r5py/r5/travel_time_matrix_computer.py
@@ -2,13 +2,23 @@
 
 """Calculate travel times between many origins and destinations."""
 
+import copy
+
+import joblib
 import pandas
 
 from .base_travel_time_matrix_computer import BaseTravelTimeMatrixComputer
+from ..util import start_jvm
+
+import com.conveyal.r5
+
 
 __all__ = ["TravelTimeMatrixComputer"]
 
 
+start_jvm()
+
+
 class TravelTimeMatrixComputer(BaseTravelTimeMatrixComputer):
     """Compute travel times between many origins and destinations."""
 
@@ -27,7 +37,29 @@ def compute_travel_times(self):
             ``travel_time_p{:02d}`` representing the particular percentile of
             travel time.
         """
-        return super().compute_travel_times()
+        self._prepare_origins_destinations()
+        self.request.destinations = self.destinations
+
+        # loop over all origins, modify the request, and compute the times
+        # to all destinations.
+        with joblib.Parallel(
+            prefer="threads",
+            verbose=(10 * self.verbose),  # joblib has a funny verbosity scale
+            n_jobs=self.NUM_THREADS,
+        ) as parallel:
+            od_matrix = pandas.concat(
+                parallel(
+                    joblib.delayed(self._travel_times_per_origin)(from_id)
+                    for from_id in self.origins.id
+                ),
+                ignore_index=True,
+            )
+
+        try:
+            od_matrix = od_matrix.to_crs(self._origins_crs)
+        except AttributeError:  # (not a GeoDataFrame)
+            pass
+        return od_matrix
 
     def _parse_results(self, from_id, results):
         """
@@ -87,3 +119,16 @@ def _parse_results(self, from_id, results):
         od_matrix = od_matrix.reset_index(drop=True)
 
         return od_matrix
+
+    def _travel_times_per_origin(self, from_id):
+        request = copy.copy(self.request)
+        request.origin = self.origins[self.origins.id == from_id].geometry.item()
+
+        travel_time_computer = com.conveyal.r5.analyst.TravelTimeComputer(
+            request, self.transport_network
+        )
+        results = travel_time_computer.computeTravelTimes()
+
+        od_matrix = self._parse_results(from_id, results)
+
+        return od_matrix
diff --git a/src/r5py/r5/trip.py b/src/r5py/r5/trip.py
new file mode 100644
index 00000000..42d3add3
--- /dev/null
+++ b/src/r5py/r5/trip.py
@@ -0,0 +1,94 @@
+#!/usr/bin/env python3
+
+
+"""Represent one trip, consisting of one or more `TripLeg`s."""
+
+
+import datetime
+
+import shapely
+
+from .trip_leg import TripLeg
+
+
+__all__ = ["Trip"]
+
+
+class Trip:
+    """
+    Represent one trip, consisting of one or more `TripLeg`s.
+    """
+
+    COLUMNS = [
+        "segment",
+    ] + TripLeg.COLUMNS
+
+    def __init__(self, legs=[]):
+        """
+        Represent one trip, consisting of one of more `TripLeg`s.
+
+        Arguments
+        =========
+        legs : collections.abc.Iterable
+            optional list of trip legs with which to initialise this trip
+        """
+        self.legs = legs
+
+    def __repr__(self):
+        legs = ", ".join([str(leg) for leg in self.legs])
+        return (
+            f"<{self.__class__.__name__}: "
+            f"{self.distance}m, "
+            f"{self.travel_time.total_seconds()}s, "
+            f"{legs}>"
+        )
+
+    def as_table(self):
+        """
+        Return a table (list of lists) of this trip’s legs.
+
+        Returns
+        =======
+        list : detailed information about this trip and its legs (segments):
+        ``segment``, ``transport_mode``, ``departure_time``, ``distance``,
+        ``travel_time``, ``wait_time``, ``route``, ``geometry``
+        """
+        return [[segment] + leg.as_table_row() for segment, leg in enumerate(self.legs)]
+
+    @property
+    def distance(self):
+        """Overall distance of this trip in metres (float)."""
+        try:
+            distance = sum([leg.distance for leg in self.legs])
+        except TypeError:  # distance of a leg can be None
+            distance = None
+        return distance
+
+    @property
+    def geometry(self):
+        """Joined geometries of all legs of this trip (shapely.LineString or shapely.MultiLineString)."""
+        return shapely.line_merge(
+            shapely.MultiLineString([leg.geometry for leg in self.legs])
+        )
+
+    @property
+    def routes(self):
+        """The public transport route(s) used on this trip."""
+        return [leg.route for leg in self.legs]
+
+    @property
+    def transport_modes(self):
+        """The transport mode(s) used on this trip."""
+        return [leg.transport_mode for leg in self.legs]
+
+    @property
+    def travel_time(self):
+        """Overall travel_time of this trip (datetime.timedelta)."""
+        return sum(
+            [leg.travel_time for leg in self.legs], datetime.timedelta(seconds=0)
+        )
+
+    @property
+    def wait_time(self):
+        """Overall wait_time of this trip (datetime.timedelta)."""
+        return sum([leg.wait_time for leg in self.legs], datetime.timedelta(seconds=0))
diff --git a/src/r5py/r5/trip_leg.py b/src/r5py/r5/trip_leg.py
new file mode 100644
index 00000000..42ee3710
--- /dev/null
+++ b/src/r5py/r5/trip_leg.py
@@ -0,0 +1,144 @@
+#!/usr/bin/env python3
+
+
+"""Represent one leg of a trip."""
+
+
+__all__ = ["TripLeg"]
+
+
+class TripLeg:
+    """
+    Represent one leg of a trip.
+
+    This is a base class, use one the specific classes,
+    e.g., TransitLeg, or DirectLeg
+    """
+
+    COLUMNS = [
+        "transport_mode",
+        "departure_time",
+        "distance",
+        "travel_time",
+        "wait_time",
+        "route",
+        "geometry",
+    ]
+
+    def __init__(
+        self,
+        transport_mode=None,
+        departure_time=None,
+        distance=None,
+        travel_time=None,
+        wait_time=None,
+        route=None,
+        geometry=None,
+    ):
+        """
+        Represent one leg of a trip.
+
+        This is a base class, use one the specific classes,
+        e.g., TransitLeg, or DirectLeg
+
+        Arguments
+        =========
+        transport_mode : r5py.TransportMode
+            mode of transport this trip leg was travelled
+        departure_time : datetime.datetime,
+        distance : float
+            distance covered by this trip leg, in metres
+        travel_time : datetime.timedelta
+            time spent travelling on this trip leg
+        wait_time : datetime.timedelta
+            time spent waiting for a connection on this trip leg
+        route : str
+            public transport route used for this trip leg
+        geometry : shapely.LineString
+            spatial representation of this trip leg
+        """
+        self.transport_mode = transport_mode
+        self.departure_time = departure_time
+        self.distance = distance
+        self.travel_time = travel_time
+        self.wait_time = wait_time
+        self.route = route
+        self.geometry = geometry
+
+    def __add__(self, other):
+        from .trip import Trip
+
+        if isinstance(other, self.__class__):
+            trip = Trip([self, other])
+            return trip
+        elif isinstance(other, Trip):
+            other.legs = [self] + other.legs
+            return other
+        else:
+            raise TypeError(
+                f"unsupported operand type(s) for '+': '{type(other)}' and '{type(self)}'"
+            )
+
+    def __radd__(self, other):
+        from .trip import Trip
+
+        if other == 0:  # first iteration of sum()
+            return self
+        elif isinstance(other, Trip):
+            other.legs.append(self)
+            return other
+        else:
+            return self.__add__(other)
+
+    def __gt__(self, other):
+        if isinstance(other, TripLeg):
+            return (self.travel_time + self.wait_time) > (
+                other.travel_time + other.wait_time
+            )
+
+    def __ge__(self, other):
+        if isinstance(other, TripLeg):
+            return (self.travel_time + self.wait_time) >= (
+                other.travel_time + other.wait_time
+            )
+
+    def __lt__(self, other):
+        if isinstance(other, TripLeg):
+            return (self.travel_time + self.wait_time) < (
+                other.travel_time + other.wait_time
+            )
+
+    def __le__(self, other):
+        if isinstance(other, TripLeg):
+            return (self.travel_time + self.wait_time) <= (
+                other.travel_time + other.wait_time
+            )
+
+    def __repr__(self):
+        try:
+            first_point = self.geometry.coords[0]
+            last_point = self.geometry.coords[-1]
+            _repr = (
+                "<"
+                f"{self.__class__.__name__}: "
+                f"{self.transport_mode}, "
+                f"{self.distance}m, "
+                f"{self.travel_time.total_seconds():0.1f}s, "
+                f"{first_point} -> {last_point}"
+                ">"
+            )
+        except AttributeError:
+            _repr = f"<{self.__class__.__name__}>"
+        return _repr
+
+    def as_table_row(self):
+        """
+        Return a table row (list) of this trip leg’s details.
+
+        Returns
+        =======
+        list : detailed information about this trip leg: ``transport_mode``,
+        ``departure_time``, ``distance``, ``travel_time``, ``wait_time``,
+        ``route``, ``geometry``
+        """
+        return [getattr(self, column) for column in self.COLUMNS]
diff --git a/src/r5py/r5/trip_planner.py b/src/r5py/r5/trip_planner.py
new file mode 100644
index 00000000..34f0fb9d
--- /dev/null
+++ b/src/r5py/r5/trip_planner.py
@@ -0,0 +1,549 @@
+#!/usr/bin/env python3
+
+
+"""Find detailed routes between two points."""
+
+
+import copy
+import collections
+import datetime
+import functools
+import warnings
+
+import jpype
+import pyproj
+import shapely
+
+from .access_leg import AccessLeg
+from .direct_leg import DirectLeg
+from .egress_leg import EgressLeg
+from .transfer_leg import TransferLeg
+from .transit_leg import TransitLeg
+from .transport_mode import TransportMode
+from .trip import Trip
+from ..util import GoodEnoughEquidistantCrs, start_jvm
+
+import com.conveyal.r5
+import gnu.trove.map
+import java.lang
+
+
+__all__ = ["TripPlanner"]
+
+
+start_jvm()
+
+
+ACCURATE_GEOMETRIES = com.conveyal.r5.transit.TransitLayer.SAVE_SHAPES
+COORDINATE_CORRECTION_FACTOR = com.conveyal.r5.streets.VertexStore.FIXED_FACTOR
+R5_CRS = "EPSG:4326"
+
+ONE_MINUTE = datetime.timedelta(minutes=1)
+ZERO_SECONDS = datetime.timedelta(seconds=0)
+
+
+# Show the ACCURATE_GEOMETRIES warning once, only
+warnings.filterwarnings("once", "TransitLayer.SAVE_SHAPES = false")
+
+
+class TripPlanner:
+    """
+    Find detailed routes between two points.
+    """
+
+    MAX_ACCESS_TIME = datetime.timedelta(hours=1)
+    MAX_EGRESS_TIME = MAX_ACCESS_TIME
+
+    def __init__(self, transport_network, request):
+        """
+        Find detailed routes between two points.
+
+        Arguments
+        =========
+        transport_network : r5py.r5.TransportNetwork
+            A transport network to route on
+        request : r5py.r5.regional_task
+            The parameters that should be used when finding a route
+        """
+        self.transport_network = transport_network
+        self.request = request
+
+        EQUIDISTANT_CRS = GoodEnoughEquidistantCrs(self.transport_network.extent)
+        self._crs_transformer_function = pyproj.Transformer.from_crs(
+            R5_CRS, EQUIDISTANT_CRS
+        ).transform
+
+    @property
+    def trips(self):
+        """
+        Find detailed routes between two points.
+
+        Returns
+        =======
+        list[r5py.r5.Trip]
+            Detailed routes that meet the requested parameters
+        """
+        trips = self.direct_paths + self.transit_paths
+        return trips
+
+    @property
+    def direct_paths(self):
+        direct_paths = []
+        request = copy.copy(self.request)
+
+        direct_modes = [mode for mode in request.transport_modes if mode.is_street_mode]
+
+        for transport_mode in direct_modes:
+            # short-circuit identical from_id and to_id:
+            if (
+                request._regional_task.fromLat == request._regional_task.toLat
+                and request._regional_task.fromLon == request._regional_task.toLon
+            ):
+                lat = request._regional_task.fromLat
+                lon = request._regional_task.fromLon
+                direct_paths.append(
+                    Trip(
+                        [
+                            DirectLeg(
+                                transport_mode,
+                                collections.namedtuple(
+                                    "StreetSegment",
+                                    ["distance", "duration", "geometry"],
+                                )(0.0, 0.0, f"LINESTRING({lon} {lat}, {lon} {lat})"),
+                            )
+                        ]
+                    )
+                )
+            else:
+                street_router = com.conveyal.r5.streets.StreetRouter(
+                    self.transport_network.street_layer
+                )
+                street_router.profileRequest = request
+                street_router.streetMode = transport_mode
+
+                street_router.setOrigin(
+                    request._regional_task.fromLat,
+                    request._regional_task.fromLon,
+                )
+                street_router.setDestination(
+                    request._regional_task.toLat,
+                    request._regional_task.toLon,
+                )
+
+                street_router.route()
+
+                try:
+                    router_state = street_router.getState(
+                        street_router.getDestinationSplit()
+                    )
+                    street_segment = self._street_segment_from_router_state(
+                        router_state,
+                        transport_mode,
+                    )
+                    direct_paths.append(
+                        Trip(
+                            [
+                                DirectLeg(transport_mode, street_segment),
+                            ]
+                        )
+                    )
+                except java.lang.NullPointerException:
+                    warnings.warn(
+                        f"Could not find route between origin "
+                        f"({self.request.fromLon}, {self.request.fromLat}) "
+                        f"and destination ({self.request.toLon}, {self.request.toLat})",
+                        RuntimeWarning,
+                    )
+        return direct_paths
+
+    def _street_segment_from_router_state(self, router_state, transport_mode):
+        """Retrieve a com.conveyal.r5.street.StreetSegment for a route."""
+        street_path = com.conveyal.r5.profile.StreetPath(
+            router_state,
+            self.transport_network,
+            False,
+        )
+        street_segment = com.conveyal.r5.api.util.StreetSegment(
+            street_path,
+            transport_mode,
+            self.transport_network.street_layer,
+        )
+        return street_segment
+
+    @functools.cached_property
+    def transit_paths(self):
+        transit_paths = []
+
+        # if any transit mode requested:
+        if [mode for mode in self.request.transport_modes if mode.is_transit_mode]:
+            request = copy.copy(self.request)
+
+            midnight = self.request.departure.replace(
+                hour=0, minute=0, second=0, microsecond=0
+            )
+            suboptimal_minutes = max(self.request._regional_task.suboptimalMinutes, 0)
+            transit_layer = self.transport_network.transit_layer
+
+            if not ACCURATE_GEOMETRIES:
+                warnings.warn(
+                    (
+                        "R5 has been compiled with "
+                        "`TransitLayer.SAVE_SHAPES = false` (the default). "
+                        "The geometries of public transport routes are "
+                        "inaccurate (straight lines between stops), and "
+                        "distances can not be computed."
+                        # TODO: Add documentation on how to compile own version,
+                        # link to that doc page from here
+                    ),
+                    RuntimeWarning,
+                )
+
+            if (
+                request._regional_task.fromLat == request._regional_task.toLat
+                and request._regional_task.fromLon == request._regional_task.toLon
+            ):
+                lat = request._regional_task.fromLat
+                lon = request._regional_task.fromLon
+                transit_paths.append(
+                    Trip(
+                        [
+                            TransitLeg(
+                                transport_mode=TransportMode.TRANSIT,
+                                departure_time=None,
+                                distance=0.0,
+                                travel_time=ZERO_SECONDS,
+                                wait_time=ZERO_SECONDS,
+                                route=None,
+                                geometry=shapely.LineString(((lon, lat), (lon, lat))),
+                            )
+                        ]
+                    )
+                )
+            else:
+                # McRapterSuboptimalPathProfileRouter needs this simple callback,
+                # this could, of course, be a lambda function, but this way it’s
+                # cleaner
+                def list_supplier_callback(departure_time):
+                    return com.conveyal.r5.profile.SuboptimalDominatingList(
+                        suboptimal_minutes
+                    )
+
+                transit_router = (
+                    com.conveyal.r5.profile.McRaptorSuboptimalPathProfileRouter(
+                        self.transport_network,
+                        request,
+                        self.transit_access_times,
+                        self.transit_egress_times,
+                        list_supplier_callback,
+                        None,
+                        True,
+                    )
+                )
+                transit_router.route()
+
+                # `finalStatesByDepartureTime` is a hashmap of lists of router
+                # states, indexed by departure times (in seconds since midnight)
+                final_states = {
+                    (midnight + datetime.timedelta(seconds=departure_time)): state
+                    for departure_time, states in zip(
+                        transit_router.finalStatesByDepartureTime.keys(),
+                        transit_router.finalStatesByDepartureTime.values(),
+                    )
+                    for state in list(states)  # some departure times yield no results
+                }
+
+                for departure_time, state in final_states.items():
+                    trip = Trip()
+                    while state:
+                        if state.stop == -1:  # EgressLeg
+                            leg = min(
+                                [
+                                    self.transit_egress_paths[transport_mode][
+                                        state.back.stop
+                                    ]
+                                    for transport_mode in self.transit_egress_paths.keys()
+                                ]
+                            )
+                            leg.wait_time = ZERO_SECONDS
+                            leg.departure_time = (
+                                midnight
+                                + datetime.timedelta(seconds=state.back.time)
+                                + ONE_MINUTE
+                            )
+                            leg.arrival_time = leg.departure_time + leg.travel_time
+
+                        elif state.back is None:  # AccessLeg
+                            leg = min(
+                                [
+                                    self.transit_access_paths[transport_mode][
+                                        state.stop
+                                    ]
+                                    for transport_mode in self.transit_access_paths.keys()
+                                ]
+                            )
+                            leg.wait_time = ZERO_SECONDS
+                            leg.arrival_time = midnight + datetime.timedelta(
+                                seconds=state.time
+                            )
+                            leg.departure_time = leg.arrival_time - leg.travel_time
+
+                        else:
+                            if state.pattern == -1:  # TransferLeg
+                                departure_stop = state.back.stop
+                                arrival_stop = state.stop
+
+                                leg = self.transit_transfer_path(
+                                    departure_stop, arrival_stop
+                                )
+
+                                leg.departure_time = (
+                                    midnight
+                                    + datetime.timedelta(seconds=state.back.time)
+                                    + ONE_MINUTE
+                                )
+                                leg.arrival_time = leg.departure_time + leg.travel_time
+                                leg.wait_time = (
+                                    datetime.timedelta(
+                                        seconds=(state.time - state.back.time)
+                                    )
+                                    - leg.travel_time
+                                    + ONE_MINUTE  # the slack added above
+                                )
+
+                            else:  # TransitLeg
+                                pattern = transit_layer.trip_patterns[state.pattern]
+                                route = transit_layer.routes[pattern.routeIndex]
+                                transport_mode = TransportMode(
+                                    com.conveyal.r5.transit.TransitLayer.getTransitModes(
+                                        route.route_type
+                                    ).toString()
+                                )
+                                departure_time = midnight + datetime.timedelta(
+                                    seconds=state.boardTime
+                                )
+                                travel_time = datetime.timedelta(
+                                    seconds=(state.time - state.boardTime)
+                                )
+                                wait_time = datetime.timedelta(
+                                    seconds=(state.boardTime - state.back.time)
+                                )
+
+                                # ‘hops’ in R5 terminology are the LineStrings
+                                # between each pair of consecutive stops of a route
+                                hops = list(pattern.getHopGeometries(transit_layer))
+
+                                # select only the ‘hops’ between our stops, and merge
+                                # them into one LineString
+                                hops = hops[
+                                    state.boardStopPosition : state.alightStopPosition
+                                ]
+                                geometry = shapely.line_merge(
+                                    shapely.MultiLineString(
+                                        [
+                                            shapely.from_wkt(str(geometry.toText()))
+                                            for geometry in hops
+                                        ]
+                                    )
+                                )
+
+                                # distance: based on the geometry, which might
+                                # be inaccurate.
+
+                                # We do not compute distance values if the
+                                # geometry is straight lines between stops
+                                # the user can still do that themselves from the
+                                # inaccurate geometries: then they at least know
+                                # what they committed to.
+                                # TODO: add to documentation
+                                if ACCURATE_GEOMETRIES:
+                                    distance = shapely.ops.transform(
+                                        self._crs_transformer_function,
+                                        geometry,
+                                    ).length
+                                else:
+                                    distance = None
+
+                                leg = TransitLeg(
+                                    transport_mode,
+                                    departure_time,
+                                    distance,
+                                    travel_time,
+                                    wait_time,
+                                    str(route.route_short_name),
+                                    geometry,
+                                )
+
+                        # we traverse in reverse order:
+                        # add leg to beginning of trip,
+                        # then fetch previous state (=leg)
+                        trip = leg + trip
+                        state = state.back
+
+                    transit_paths.append(trip)
+
+        return transit_paths
+
+    @functools.cached_property
+    def transit_access_paths(self):
+        access_paths = {}
+
+        request = copy.copy(self.request)
+        request._regional_task.reverseSearch = False
+
+        street_router = com.conveyal.r5.streets.StreetRouter(
+            self.transport_network.street_layer
+        )
+        street_router.profileRequest = request
+        street_router.setOrigin(
+            self.request._regional_task.fromLat,
+            self.request._regional_task.fromLon,
+        )
+        street_router.transitStopSearch = True
+        street_router.timeLimitSeconds = round(self.MAX_ACCESS_TIME.total_seconds())
+
+        transit_layer = self.transport_network.transit_layer
+
+        for transport_mode in request.access_modes:
+            access_paths[transport_mode] = {}
+
+            street_router.streetMode = transport_mode
+            street_router.route()
+            reached_stops = street_router.getReachedStops()
+
+            for stop in reached_stops.keys():
+                router_state = street_router.getStateAtVertex(
+                    transit_layer.get_street_vertex_for_stop(stop)
+                )
+                street_segment = self._street_segment_from_router_state(
+                    router_state,
+                    transport_mode,
+                )
+                access_paths[transport_mode][stop] = AccessLeg(
+                    transport_mode, street_segment
+                )
+        return access_paths
+
+    @functools.cached_property
+    def transit_access_times(self):
+        """Times to reached stops in the format required by
+        McRaptorSuboptimalPathProfileRouter."""
+        access_times = jpype.JObject(
+            {
+                com.conveyal.r5.api.util.LegMode
+                @ mode: gnu.trove.map.hash.TIntIntHashMap(
+                    [stop for stop in reached_stops.keys()],
+                    [
+                        round(transfer_leg.travel_time.total_seconds())
+                        for transfer_leg in reached_stops.values()
+                    ],
+                )
+                for mode, reached_stops in self.transit_access_paths.items()
+            },
+            "java.util.Map<com.conveyal.r5.LegMode, gnu.trove.map.TIntIntMap>",
+        )
+        return access_times
+
+    @functools.cached_property
+    def transit_egress_paths(self):
+        egress_paths = {}
+
+        request = copy.copy(self.request)
+        request._regional_task.reverseSearch = True
+
+        street_router = com.conveyal.r5.streets.StreetRouter(
+            self.transport_network.street_layer
+        )
+        street_router.profileRequest = request
+        street_router.setOrigin(
+            self.request._regional_task.toLat,
+            self.request._regional_task.toLon,
+        )
+        street_router.transitStopSearch = True
+        street_router.timeLimitSeconds = round(self.MAX_EGRESS_TIME.total_seconds())
+
+        transit_layer = self.transport_network.transit_layer
+
+        for transport_mode in request.egress_modes:
+            egress_paths[transport_mode] = {}
+
+            street_router.streetMode = transport_mode
+
+            street_router.route()
+            reached_stops = street_router.getReachedStops()
+
+            for stop in reached_stops.keys():
+                router_state = street_router.getStateAtVertex(
+                    transit_layer.get_street_vertex_for_stop(stop)
+                )
+                street_segment = self._street_segment_from_router_state(
+                    router_state,
+                    transport_mode,
+                )
+                egress_paths[transport_mode][stop] = EgressLeg(
+                    transport_mode, street_segment
+                )
+        return egress_paths
+
+    @functools.cached_property
+    def transit_egress_times(self):
+        """Times to reached stops in the format required by
+        McRaptorSuboptimalPathProfileRouter."""
+        egress_times = jpype.JObject(
+            {
+                com.conveyal.r5.api.util.LegMode
+                @ mode: gnu.trove.map.hash.TIntIntHashMap(
+                    [stop for stop in reached_stops.keys()],
+                    [
+                        round(transfer_leg.travel_time.total_seconds())
+                        for transfer_leg in reached_stops.values()
+                    ],
+                )
+                for mode, reached_stops in self.transit_egress_paths.items()
+            },
+            "java.util.Map<com.conveyal.r5.LegMode, gnu.trove.map.TIntIntMap>",
+        )
+        return egress_times
+
+    def transit_transfer_path(self, from_stop, to_stop):
+        self._transfer_paths = {}
+        while True:
+            try:
+                transfer_path = self._transfer_paths[(from_stop, to_stop)]
+            except KeyError:
+                request = copy.copy(self.request)
+
+                street_router = com.conveyal.r5.streets.StreetRouter(
+                    self.transport_network.street_layer
+                )
+                street_router.profileRequest = request
+                street_router.streetMode = TransportMode.WALK
+
+                get_coordinates_for_stop = (
+                    self.transport_network.transit_layer._transit_layer.getCoordinateForStopFixed
+                )
+                from_stop_coordinates = get_coordinates_for_stop(from_stop)
+                to_stop_coordinates = get_coordinates_for_stop(to_stop)
+
+                from_lat = from_stop_coordinates.getY() / COORDINATE_CORRECTION_FACTOR
+                from_lon = from_stop_coordinates.getX() / COORDINATE_CORRECTION_FACTOR
+                to_lat = to_stop_coordinates.getY() / COORDINATE_CORRECTION_FACTOR
+                to_lon = to_stop_coordinates.getX() / COORDINATE_CORRECTION_FACTOR
+
+                street_router.setOrigin(from_lat, from_lon)
+                street_router.setDestination(to_lat, to_lon)
+
+                street_router.route()
+
+                router_state = street_router.getState(
+                    street_router.getDestinationSplit()
+                )
+                street_segment = self._street_segment_from_router_state(
+                    router_state,
+                    TransportMode.WALK,
+                )
+
+                transfer_path = self._transfer_paths[
+                    (from_stop, to_stop)
+                ] = TransferLeg(TransportMode.WALK, street_segment)
+
+                return transfer_path
diff --git a/src/r5py/util/__init__.py b/src/r5py/util/__init__.py
index 4def15ac..8205f199 100644
--- a/src/r5py/util/__init__.py
+++ b/src/r5py/util/__init__.py
@@ -2,10 +2,11 @@
 
 """Utility functions, e.g., starting a JVM, and accessing configuration."""
 
-from .config import Config
 from .camel_to_snake_case import camel_to_snake_case
-from .data_validation import check_od_data_set
+from .config import Config
 from .contains_gtfs_data import contains_gtfs_data
+from .data_validation import check_od_data_set
+from .good_enough_equidistant_crs import GoodEnoughEquidistantCrs
 from .jvm import start_jvm
 from .parse_int_date import parse_int_date
 from .snake_to_camel_case import snake_to_camel_case
@@ -15,6 +16,7 @@
     "check_od_data_set",
     "Config",
     "contains_gtfs_data",
+    "GoodEnoughEquidistantCrs",
     "parse_int_date",
     "snake_to_camel_case",
     "start_jvm",
diff --git a/src/r5py/util/exceptions.py b/src/r5py/util/exceptions.py
index 5b4d3e61..a506c42b 100644
--- a/src/r5py/util/exceptions.py
+++ b/src/r5py/util/exceptions.py
@@ -6,14 +6,20 @@
 import requests
 
 
+# base class for r5py-related exceptions
 class R5pyError(Exception):
     """Generic base exception for r5py errors."""
 
 
+# more specific exceptions
 class ChecksumFailed(requests.RequestException, R5pyError):
     """Requested resource did not pass checksum test."""
 
 
+class MissingColumnError(ValueError, R5pyError):
+    """An input data set is missing a required column."""
+
+
 class NoCrsError(ValueError, R5pyError):
     """An input data set’s geometry column does not have a reference system defined."""
 
@@ -22,13 +28,13 @@ class NonUniqueIDError(ValueError, R5pyError):
     """An input data set’s `id` column has non-unique values."""
 
 
-class MissingColumnError(ValueError, R5pyError):
-    """An input data set is missing a required column."""
-
-
 class NoIDColumnError(MissingColumnError):
     """An input data set does not have a required `id` column."""
 
 
 class UnexpectedClasspathSchema(ValueError, R5pyError):
     """A classpath was supplied as an URI, but could not be parsed."""
+
+
+class UnexpectedCrsError(ValueError, R5pyError):
+    """A geometry is in an unexpected reference system."""
diff --git a/src/r5py/util/good_enough_equidistant_crs.py b/src/r5py/util/good_enough_equidistant_crs.py
new file mode 100644
index 00000000..c4b92175
--- /dev/null
+++ b/src/r5py/util/good_enough_equidistant_crs.py
@@ -0,0 +1,71 @@
+#!/usr/bin/env python3
+
+
+"""Find the most appropriate equidistant (UTM) reference system for an extent."""
+
+
+import pyproj
+import shapely
+
+from .exceptions import UnexpectedCrsError
+
+
+class GoodEnoughEquidistantCrs(pyproj.CRS):
+    """
+    Find the most appropriate UTM reference system for the current extent.
+
+    (We need this to be able to calculate lengths in meters.
+    Results don’t have to be perfect, so also the neighbouring UTM grid will do.)
+    """
+
+    def __new__(cls, extent):
+        """
+        Find the most appropriate UTM reference system for the current extent.
+
+        (We need this to be able to calculate lengths in meters.
+        Results don’t have to be perfect, so also the neighbouring UTM grid will do.)
+
+        Arguments
+        ---------
+        extent: shapely.Geometry
+            The geographical extent for which to find an equidistant reference
+            system, in `EPSG:4326`
+        """
+
+        if GoodEnoughEquidistantCrs._is_plausible_in_epsg4326(extent):
+            # default CRS in case we do not find any better match
+            crs = pyproj.CRS.from_epsg(3857)
+
+            # buffer extent (so everything is a polygon)
+            extent = extent.buffer(0.1)
+
+            crsinfo = pyproj.database.query_utm_crs_info(
+                datum_name="WGS 84",
+                area_of_interest=pyproj.aoi.AreaOfInterest(*extent.bounds),
+            )
+            for candidate_crs in crsinfo:
+                area_of_use = shapely.box(*candidate_crs.area_of_use.bounds)
+                coverage = shapely.intersection(extent, area_of_use).area / extent.area
+
+                if coverage > 0.5:
+                    # more than half of extent covered by crs’ area of use
+                    # -> good enough
+                    crs = pyproj.CRS.from_authority(
+                        candidate_crs.auth_name, candidate_crs.code
+                    )
+                    break
+
+            return crs
+
+        else:
+            raise UnexpectedCrsError("`extent` does not seem to be in `EPSG:4326`")
+
+    @staticmethod
+    def _is_plausible_in_epsg4326(geometry):
+        try:
+            minx, miny, maxx, maxy = geometry.bounds
+            assert -180 <= minx <= maxx <= 180
+            assert -90 <= miny <= maxy <= 90
+            return True
+        except AssertionError:
+            return False


Test patch:
diff --git a/docs/data/test_data/test_detailed_itineraries_bicycle.gpkg.zip b/docs/data/test_data/test_detailed_itineraries_bicycle.gpkg.zip
new file mode 100644
index 00000000..4d137beb
Binary files /dev/null and b/docs/data/test_data/test_detailed_itineraries_bicycle.gpkg.zip differ
diff --git a/docs/data/test_data/test_detailed_itineraries_car.gpkg.zip b/docs/data/test_data/test_detailed_itineraries_car.gpkg.zip
new file mode 100644
index 00000000..4d2fba8a
Binary files /dev/null and b/docs/data/test_data/test_detailed_itineraries_car.gpkg.zip differ
diff --git a/docs/data/test_data/test_detailed_itineraries_transit.gpkg.zip b/docs/data/test_data/test_detailed_itineraries_transit.gpkg.zip
new file mode 100644
index 00000000..22cd41cf
Binary files /dev/null and b/docs/data/test_data/test_detailed_itineraries_transit.gpkg.zip differ
diff --git a/docs/data/test_data/test_detailed_itineraries_walk.gpkg.zip b/docs/data/test_data/test_detailed_itineraries_walk.gpkg.zip
new file mode 100644
index 00000000..a9981af4
Binary files /dev/null and b/docs/data/test_data/test_detailed_itineraries_walk.gpkg.zip differ
diff --git a/docs/data/test_data/test_walking_details_not_snapped.csv b/docs/data/test_data/test_walking_details_not_snapped.csv
new file mode 100644
index 00000000..1cb731a8
--- /dev/null
+++ b/docs/data/test_data/test_walking_details_not_snapped.csv
@@ -0,0 +1,362 @@
+from_id,to_id,option,segment,distance
+0,0,0,0,0.0
+0,5,0,0,497.851
+0,10,0,0,2019.107
+0,15,0,0,786.414
+0,20,0,0,1614.219
+0,25,0,0,1030.957
+0,30,0,0,1550.107
+0,35,0,0,2258.631
+0,40,0,0,1413.111
+0,45,0,0,1738.125
+0,50,0,0,2173.95
+0,55,0,0,2129.408
+0,60,0,0,2707.938
+0,65,0,0,2393.391
+0,70,0,0,2732.033
+0,75,0,0,2798.871
+0,80,0,0,2849.311
+0,85,0,0,3538.432
+0,90,0,0,3237.423
+5,0,0,0,482.293
+5,5,0,0,0.0
+5,10,0,0,1608.732
+5,15,0,0,709.28
+5,20,0,0,1203.889
+5,25,0,0,950.526
+5,30,0,0,1139.84
+5,35,0,0,1848.364
+5,40,0,0,1190.046
+5,45,0,0,1402.438
+5,50,0,0,1998.798
+5,55,0,0,1891.94
+5,60,0,0,2299.592
+5,65,0,0,2154.454
+5,70,0,0,2377.863
+5,75,0,0,2615.823
+5,80,0,0,2507.05
+5,85,0,0,3130.126
+5,90,0,0,3019.628
+10,0,0,0,2011.339
+10,5,0,0,1616.522
+10,10,0,0,0.0
+10,15,0,0,2029.204
+10,20,0,0,663.187
+10,25,0,0,1951.195
+10,30,0,0,1172.708
+10,35,0,0,986.03
+10,40,0,0,2035.776
+10,45,0,0,1693.315
+10,50,0,0,2845.851
+10,55,0,0,2210.425
+10,60,0,0,1811.569
+10,65,0,0,2449.626
+10,70,0,0,2203.346
+10,75,0,0,3157.255
+10,80,0,0,2493.83
+10,85,0,0,2788.444
+10,90,0,0,3392.004
+15,0,0,0,777.448
+15,5,0,0,715.872
+15,10,0,0,2028.006
+15,15,0,0,0.0
+15,20,0,0,1438.214
+15,25,0,0,340.533
+15,30,0,0,1250.78
+15,35,0,0,2005.797
+15,40,0,0,965.611
+15,45,0,0,1327.902
+15,50,0,0,1726.609
+15,55,0,0,1682.067
+15,60,0,0,2373.594
+15,65,0,0,1945.878
+15,70,0,0,2361.291
+15,75,0,0,2352.725
+15,80,0,0,2426.69
+15,85,0,0,3183.506
+15,90,0,0,2787.879
+20,0,0,0,1618.232
+20,5,0,0,1223.415
+20,10,0,0,674.923
+20,15,0,0,1452.549
+20,20,0,0,0.0
+20,25,0,0,1373.292
+20,30,0,0,546.382
+20,35,0,0,805.022
+20,40,0,0,1409.45
+20,45,0,0,1113.108
+20,50,0,0,2218.804
+20,55,0,0,1629.355
+20,60,0,0,1527.475
+20,65,0,0,1879.382
+20,70,0,0,1775.098
+20,75,0,0,2565.756
+20,80,0,0,2042.288
+20,85,0,0,2496.828
+20,90,0,0,2810.934
+25,0,0,0,1018.967
+25,5,0,0,954.094
+25,10,0,0,1946.973
+25,15,0,0,337.497
+25,20,0,0,1357.181
+25,25,0,0,0.0
+25,30,0,0,1169.747
+25,35,0,0,1924.764
+25,40,0,0,812.716
+25,45,0,0,1183.573
+25,50,0,0,1573.714
+25,55,0,0,1529.172
+25,60,0,0,2262.713
+25,65,0,0,1792.375
+25,70,0,0,2216.9
+25,75,0,0,2199.83
+25,80,0,0,2283.704
+25,85,0,0,3039.391
+25,90,0,0,2636.945
+30,0,0,0,1539.573
+30,5,0,0,1144.864
+30,10,0,0,1169.771
+30,15,0,0,1249.212
+30,20,0,0,531.88
+30,25,0,0,1171.203
+30,30,0,0,0.0
+30,35,0,0,781.284
+30,40,0,0,1056.829
+30,45,0,0,734.366
+30,50,0,0,1865.606
+30,55,0,0,1250.613
+30,60,0,0,1265.762
+30,65,0,0,1500.873
+30,70,0,0,1513.345
+30,75,0,0,2198.306
+30,80,0,0,1778.361
+30,85,0,0,2235.075
+30,90,0,0,2432.524
+35,0,0,0,2256.577
+35,5,0,0,1861.76
+35,10,0,0,994.069
+35,15,0,0,2012.709
+35,20,0,0,798.164
+35,25,0,0,1934.7
+35,30,0,0,789.764
+35,35,0,0,0.0
+35,40,0,0,1697.313
+35,45,0,0,1076.867
+35,50,0,0,2505.574
+35,55,0,0,1637.156
+35,60,0,0,1032.771
+35,65,0,0,1619.44
+35,70,0,0,1393.902
+35,75,0,0,2421.746
+35,80,0,0,1671.016
+35,85,0,0,2009.676
+35,90,0,0,2623.343
+40,0,0,0,1401.108
+40,5,0,0,1193.868
+40,10,0,0,2032.829
+40,15,0,0,962.733
+40,20,0,0,1395.272
+40,25,0,0,812.862
+40,30,0,0,1055.519
+40,35,0,0,1687.523
+40,40,0,0,0.0
+40,45,0,0,759.375
+40,50,0,0,871.969
+40,55,0,0,721.631
+40,60,0,0,1698.982
+40,65,0,0,985.012
+40,70,0,0,1579.998
+40,75,0,0,1475.306
+40,80,0,0,1624.75
+40,85,0,0,2458.981
+40,90,0,0,1849.427
+45,0,0,0,1725.87
+45,5,0,0,1405.9
+45,10,0,0,1688.987
+45,15,0,0,1324.772
+45,20,0,0,1097.044
+45,25,0,0,1183.681
+45,30,0,0,732.804
+45,35,0,0,1066.825
+45,40,0,0,759.163
+45,45,0,0,0.0
+45,50,0,0,1573.852
+45,55,0,0,622.889
+45,60,0,0,1132.256
+45,65,0,0,821.474
+45,70,0,0,1085.175
+45,75,0,0,1570.765
+45,80,0,0,1150.775
+45,85,0,0,1906.462
+45,90,0,0,1804.34
+50,0,0,0,2166.305
+50,5,0,0,2006.552
+50,10,0,0,2844.737
+50,15,0,0,1727.771
+50,20,0,0,2206.975
+50,25,0,0,1577.9
+50,30,0,0,1868.311
+50,35,0,0,2500.315
+50,40,0,0,876.415
+50,45,0,0,1579.526
+50,50,0,0,0.0
+50,55,0,0,1069.841
+50,60,0,0,2471.089
+50,65,0,0,1111.814
+50,70,0,0,2265.716
+50,75,0,0,912.471
+50,80,0,0,2278.864
+50,85,0,0,3148.334
+50,90,0,0,1539.092
+55,0,0,0,2129.021
+55,5,0,0,1906.362
+55,10,0,0,2216.517
+55,15,0,0,1690.646
+55,20,0,0,1624.158
+55,25,0,0,1540.775
+55,30,0,0,1259.918
+55,35,0,0,1637.49
+55,40,0,0,732.768
+55,45,0,0,633.756
+55,50,0,0,1076.416
+55,55,0,0,0.0
+55,60,0,0,1547.362
+55,65,0,0,330.972
+55,70,0,0,1344.479
+55,75,0,0,1028.805
+55,80,0,0,1389.391
+55,85,0,0,2223.775
+55,90,0,0,1262.508
+60,0,0,0,2700.594
+60,5,0,0,2307.647
+60,10,0,0,1811.813
+60,15,0,0,2375.097
+60,20,0,0,1516.044
+60,25,0,0,2268.329
+60,30,0,0,1268.793
+60,35,0,0,1027.362
+60,40,0,0,1703.323
+60,45,0,0,1136.849
+60,50,0,0,2472.375
+60,55,0,0,1540.66
+60,60,0,0,0.0
+60,65,0,0,1389.204
+60,70,0,0,523.713
+60,75,0,0,1977.227
+60,80,0,0,955.77
+60,85,0,0,1016.017
+60,90,0,0,2069.198
+65,0,0,0,2385.083
+65,5,0,0,2163.591
+65,10,0,0,2448.693
+65,15,0,0,1946.284
+65,20,0,0,1867.727
+65,25,0,0,1797.217
+65,30,0,0,1503.72
+65,35,0,0,1615.64
+65,40,0,0,988.476
+65,45,0,0,827.232
+65,50,0,0,1111.931
+65,55,0,0,324.514
+65,60,0,0,1389.02
+65,65,0,0,0.0
+65,70,0,0,1182.763
+65,75,0,0,818.073
+65,80,0,0,1195.399
+65,85,0,0,2065.28
+65,90,0,0,1043.867
+70,0,0,0,2737.186
+70,5,0,0,2398.574
+70,10,0,0,2216.286
+70,15,0,0,2376.667
+70,20,0,0,1776.283
+70,25,0,0,2235.448
+70,30,0,0,1529.032
+70,35,0,0,1401.6
+70,40,0,0,1597.223
+70,45,0,0,1103.568
+70,50,0,0,2278.862
+70,55,0,0,1350.668
+70,60,0,0,536.369
+70,65,0,0,1195.276
+70,70,0,0,0.0
+70,75,0,0,1657.825
+70,80,0,0,486.214
+70,85,0,0,1005.947
+70,90,0,0,1636.637
+75,0,0,0,2794.017
+75,5,0,0,2625.173
+75,10,0,0,3160.193
+75,15,0,0,2356.678
+75,20,0,0,2556.769
+75,25,0,0,2205.612
+75,30,0,0,2203.827
+75,35,0,0,2418.223
+75,40,0,0,1482.137
+75,45,0,0,1577.72
+75,50,0,0,915.755
+75,55,0,0,1024.775
+75,60,0,0,1979.717
+75,65,0,0,820.642
+75,70,0,0,1647.499
+75,75,0,0,0.0
+75,80,0,0,1592.829
+75,85,0,0,2557.888
+75,90,0,0,659.969
+80,0,0,0,2839.727
+80,5,0,0,2511.101
+80,10,0,0,2490.113
+80,15,0,0,2425.504
+80,20,0,0,2027.039
+80,25,0,0,2284.413
+80,30,0,0,1777.407
+80,35,0,0,1661.789
+80,40,0,0,1625.313
+80,45,0,0,1152.719
+80,50,0,0,2275.387
+80,55,0,0,1378.758
+80,60,0,0,951.442
+80,65,0,0,1192.33
+80,70,0,0,469.78
+80,75,0,0,1586.561
+80,80,0,0,0.0
+80,85,0,0,1029.85
+80,90,0,0,1361.044
+85,0,0,0,3531.175
+85,5,0,0,3138.268
+85,10,0,0,2791.902
+85,15,0,0,3183.927
+85,20,0,0,2485.444
+85,25,0,0,3043.965
+85,30,0,0,2238.193
+85,35,0,0,2004.354
+85,40,0,0,2463.409
+85,45,0,0,1912.271
+85,50,0,0,3148.948
+85,55,0,0,2217.435
+85,60,0,0,1016.104
+85,65,0,0,2065.551
+85,70,0,0,993.378
+85,75,0,0,2541.569
+85,80,0,0,1033.992
+85,85,0,0,0.0
+85,90,0,0,2293.633
+90,0,0,0,3255.563
+90,5,0,0,3053.624
+90,10,0,0,3418.327
+90,15,0,0,2817.029
+90,20,0,0,2825.521
+90,25,0,0,2667.158
+90,30,0,0,2461.281
+90,35,0,0,2643.62
+90,40,0,0,1879.314
+90,45,0,0,1834.991
+90,50,0,0,1564.908
+90,55,0,0,1282.125
+90,60,0,0,2094.924
+90,65,0,0,1069.777
+90,70,0,0,1649.707
+90,75,0,0,683.205
+90,80,0,0,1390.48
+90,85,0,0,2316.538
+90,90,0,0,0.0
diff --git a/docs/data/test_data/test_walking_details_snapped.csv b/docs/data/test_data/test_walking_details_snapped.csv
new file mode 100644
index 00000000..9773756f
--- /dev/null
+++ b/docs/data/test_data/test_walking_details_snapped.csv
@@ -0,0 +1,362 @@
+from_id,to_id,option,segment,distance
+0,0,0,0,0.0
+0,5,0,0,481.082
+0,10,0,0,2002.338
+0,15,0,0,769.645
+0,20,0,0,1597.45
+0,25,0,0,1014.199
+0,30,0,0,1533.338
+0,35,0,0,2241.862
+0,40,0,0,1396.342
+0,45,0,0,1721.356
+0,50,0,0,2157.181
+0,55,0,0,2112.639
+0,60,0,0,2691.288
+0,65,0,0,2376.622
+0,70,0,0,2715.253
+0,75,0,0,2783.528
+0,80,0,0,2832.542
+0,85,0,0,3521.663
+0,90,0,0,3220.412
+5,0,0,0,481.082
+5,5,0,0,0.0
+5,10,0,0,1607.521
+5,15,0,0,708.069
+5,20,0,0,1202.678
+5,25,0,0,949.326
+5,30,0,0,1138.629
+5,35,0,0,1847.153
+5,40,0,0,1188.835
+5,45,0,0,1401.227
+5,50,0,0,1997.587
+5,55,0,0,1890.729
+5,60,0,0,2298.381
+5,65,0,0,2153.243
+5,70,0,0,2376.641
+5,75,0,0,2613.406
+5,80,0,0,2505.839
+5,85,0,0,3128.915
+5,90,0,0,3018.345
+10,0,0,0,2002.338
+10,5,0,0,1607.521
+10,10,0,0,0.0
+10,15,0,0,2020.203
+10,20,0,0,654.186
+10,25,0,0,1942.205
+10,30,0,0,1163.707
+10,35,0,0,977.029
+10,40,0,0,2026.775
+10,45,0,0,1684.314
+10,50,0,0,2836.85
+10,55,0,0,2201.424
+10,60,0,0,1802.528
+10,65,0,0,2440.625
+10,70,0,0,2194.334
+10,75,0,0,3148.265
+10,80,0,0,2484.829
+10,85,0,0,2779.443
+10,90,0,0,3383.003
+15,0,0,0,769.645
+15,5,0,0,708.069
+15,10,0,0,2020.203
+15,15,0,0,0.0
+15,20,0,0,1430.411
+15,25,0,0,332.741
+15,30,0,0,1242.977
+15,35,0,0,1997.994
+15,40,0,0,957.808
+15,45,0,0,1320.099
+15,50,0,0,1718.806
+15,55,0,0,1674.264
+15,60,0,0,2365.791
+15,65,0,0,1938.075
+15,70,0,0,2353.477
+15,75,0,0,2345.153
+15,80,0,0,2418.887
+15,85,0,0,3175.703
+15,90,0,0,2782.279
+20,0,0,0,1597.483
+20,5,0,0,1202.666
+20,10,0,0,654.174
+20,15,0,0,1431.8
+20,20,0,0,0.0
+20,25,0,0,1352.554
+20,30,0,0,525.633
+20,35,0,0,784.273
+20,40,0,0,1388.701
+20,45,0,0,1092.359
+20,50,0,0,2198.055
+20,55,0,0,1608.606
+20,60,0,0,1506.766
+20,65,0,0,1858.633
+20,70,0,0,1754.338
+20,75,0,0,2544.99
+20,80,0,0,2021.539
+20,85,0,0,2476.079
+20,90,0,0,2790.185
+25,0,0,0,1014.199
+25,5,0,0,949.326
+25,10,0,0,1942.205
+25,15,0,0,332.729
+25,20,0,0,1352.413
+25,25,0,0,0.0
+25,30,0,0,1164.979
+25,35,0,0,1919.996
+25,40,0,0,807.948
+25,45,0,0,1178.805
+25,50,0,0,1568.946
+25,55,0,0,1524.404
+25,60,0,0,2259.074
+25,65,0,0,1787.607
+25,70,0,0,2212.121
+25,75,0,0,2193.856
+25,80,0,0,2278.936
+25,85,0,0,3034.623
+25,90,0,0,2629.974
+30,0,0,0,1533.338
+30,5,0,0,1138.629
+30,10,0,0,1163.536
+30,15,0,0,1242.977
+30,20,0,0,525.645
+30,25,0,0,1164.979
+30,30,0,0,0.0
+30,35,0,0,775.049
+30,40,0,0,1050.594
+30,45,0,0,728.131
+30,50,0,0,1859.371
+30,55,0,0,1244.378
+30,60,0,0,1259.487
+30,65,0,0,1494.638
+30,70,0,0,1507.099
+30,75,0,0,2192.082
+30,80,0,0,1772.126
+30,85,0,0,2228.84
+30,90,0,0,2425.957
+35,0,0,0,2241.842
+35,5,0,0,1847.025
+35,10,0,0,979.334
+35,15,0,0,1997.974
+35,20,0,0,783.429
+35,25,0,0,1919.976
+35,30,0,0,775.029
+35,35,0,0,0.0
+35,40,0,0,1682.578
+35,45,0,0,1062.132
+35,50,0,0,2490.839
+35,55,0,0,1622.421
+35,60,0,0,1018.076
+35,65,0,0,1604.705
+35,70,0,0,1379.156
+35,75,0,0,2406.949
+35,80,0,0,1656.281
+35,85,0,0,1994.941
+35,90,0,0,2608.608
+40,0,0,0,1396.342
+40,5,0,0,1188.835
+40,10,0,0,2027.904
+40,15,0,0,957.808
+40,20,0,0,1390.347
+40,25,0,0,807.948
+40,30,0,0,1050.594
+40,35,0,0,1682.598
+40,40,0,0,0.0
+40,45,0,0,754.45
+40,50,0,0,867.044
+40,55,0,0,716.706
+40,60,0,0,1694.057
+40,65,0,0,980.087
+40,70,0,0,1575.062
+40,75,0,0,1470.37
+40,80,0,0,1619.825
+40,85,0,0,2454.056
+40,90,0,0,1844.322
+45,0,0,0,1721.332
+45,5,0,0,1401.203
+45,10,0,0,1684.29
+45,15,0,0,1320.075
+45,20,0,0,1092.347
+45,25,0,0,1178.995
+45,30,0,0,728.107
+45,35,0,0,1062.128
+45,40,0,0,754.466
+45,45,0,0,0.0
+45,50,0,0,1569.155
+45,55,0,0,618.192
+45,60,0,0,1127.559
+45,65,0,0,816.777
+45,70,0,0,1080.467
+45,75,0,0,1565.896
+45,80,0,0,1146.078
+45,85,0,0,1901.765
+45,90,0,0,1799.643
+50,0,0,0,2157.34
+50,5,0,0,1997.587
+50,10,0,0,2835.772
+50,15,0,0,1718.806
+50,20,0,0,2198.01
+50,25,0,0,1568.946
+50,30,0,0,1859.346
+50,35,0,0,2491.35
+50,40,0,0,867.45
+50,45,0,0,1570.561
+50,50,0,0,0.0
+50,55,0,0,1060.876
+50,60,0,0,2462.124
+50,65,0,0,1102.849
+50,70,0,0,2256.74
+50,75,0,0,903.495
+50,80,0,0,2269.899
+50,85,0,0,3139.369
+50,90,0,0,1530.127
+55,0,0,0,2112.709
+55,5,0,0,1890.05
+55,10,0,0,2200.977
+55,15,0,0,1675.106
+55,20,0,0,1608.618
+55,25,0,0,1525.246
+55,30,0,0,1244.378
+55,35,0,0,1621.95
+55,40,0,0,717.228
+55,45,0,0,618.216
+55,50,0,0,1060.876
+55,55,0,0,0.0
+55,60,0,0,1531.975
+55,65,0,0,315.432
+55,70,0,0,1328.928
+55,75,0,0,1013.276
+55,80,0,0,1373.851
+55,85,0,0,2208.235
+55,90,0,0,1246.968
+60,0,0,0,2691.169
+60,5,0,0,2298.381
+60,10,0,0,1802.547
+60,15,0,0,2365.831
+60,20,0,0,1506.778
+60,25,0,0,2259.074
+60,30,0,0,1259.527
+60,35,0,0,1018.096
+60,40,0,0,1694.057
+60,45,0,0,1127.583
+60,50,0,0,2463.109
+60,55,0,0,1531.394
+60,60,0,0,0.0
+60,65,0,0,1379.938
+60,70,0,0,514.436
+60,75,0,0,1967.972
+60,80,0,0,946.504
+60,85,0,0,1006.751
+60,90,0,0,2059.932
+65,0,0,0,2375.842
+65,5,0,0,2153.239
+65,10,0,0,2439.611
+65,15,0,0,1937.202
+65,20,0,0,1858.645
+65,25,0,0,1788.146
+65,30,0,0,1494.638
+65,35,0,0,1606.558
+65,40,0,0,979.394
+65,45,0,0,818.15
+65,50,0,0,1102.849
+65,55,0,0,315.432
+65,60,0,0,1378.953
+65,65,0,0,0.0
+65,70,0,0,1173.67
+65,75,0,0,809.002
+65,80,0,0,1186.317
+65,85,0,0,2056.198
+65,90,0,0,1034.785
+70,0,0,0,2715.412
+70,5,0,0,2376.641
+70,10,0,0,2194.353
+70,15,0,0,2354.734
+70,20,0,0,1754.35
+70,25,0,0,2213.526
+70,30,0,0,1507.099
+70,35,0,0,1379.667
+70,40,0,0,1575.29
+70,45,0,0,1081.635
+70,50,0,0,2256.929
+70,55,0,0,1328.735
+70,60,0,0,514.436
+70,65,0,0,1173.343
+70,70,0,0,0.0
+70,75,0,0,1635.903
+70,80,0,0,464.303
+70,85,0,0,984.014
+70,90,0,0,1614.726
+75,0,0,0,2782.008
+75,5,0,0,2613.298
+75,10,0,0,3148.448
+75,15,0,0,2344.911
+75,20,0,0,2545.002
+75,25,0,0,2193.856
+75,30,0,0,2192.082
+75,35,0,0,2406.478
+75,40,0,0,1470.37
+75,45,0,0,1565.975
+75,50,0,0,903.988
+75,55,0,0,1013.03
+75,60,0,0,1967.972
+75,65,0,0,808.897
+75,70,0,0,1635.743
+75,75,0,0,0.0
+75,80,0,0,1581.084
+75,85,0,0,2546.143
+75,90,0,0,648.224
+80,0,0,0,2833.576
+80,5,0,0,2505.839
+80,10,0,0,2484.625
+80,15,0,0,2420.016
+80,20,0,0,2021.551
+80,25,0,0,2278.936
+80,30,0,0,1771.919
+80,35,0,0,1656.301
+80,40,0,0,1619.825
+80,45,0,0,1147.231
+80,50,0,0,2269.899
+80,55,0,0,1373.27
+80,60,0,0,945.954
+80,65,0,0,1186.842
+80,70,0,0,464.303
+80,75,0,0,1581.084
+80,80,0,0,0.0
+80,85,0,0,1024.362
+80,90,0,0,1355.556
+85,0,0,0,3521.822
+85,5,0,0,3128.915
+85,10,0,0,2782.549
+85,15,0,0,3174.574
+85,20,0,0,2476.091
+85,25,0,0,3034.623
+85,30,0,0,2228.84
+85,35,0,0,1995.001
+85,40,0,0,2454.056
+85,45,0,0,1902.918
+85,50,0,0,3139.595
+85,55,0,0,2208.082
+85,60,0,0,1006.751
+85,65,0,0,2056.198
+85,70,0,0,984.014
+85,75,0,0,2532.227
+85,80,0,0,1024.639
+85,85,0,0,0.0
+85,90,0,0,2284.28
+90,0,0,0,3220.571
+90,5,0,0,3017.776
+90,10,0,0,3383.335
+90,15,0,0,2782.037
+90,20,0,0,2790.529
+90,25,0,0,2632.177
+90,30,0,0,2426.289
+90,35,0,0,2608.628
+90,40,0,0,1844.322
+90,45,0,0,1799.999
+90,50,0,0,1529.916
+90,55,0,0,1247.133
+90,60,0,0,2059.932
+90,65,0,0,1034.785
+90,70,0,0,1614.726
+90,75,0,0,648.224
+90,80,0,0,1355.488
+90,85,0,0,2281.546
+90,90,0,0,0.0
diff --git a/docs/data/test_data/test_walking_times_snapped.csv b/docs/data/test_data/test_walking_times_snapped.csv
index 26932b9d..023ce873 100644
--- a/docs/data/test_data/test_walking_times_snapped.csv
+++ b/docs/data/test_data/test_walking_times_snapped.csv
@@ -2,13 +2,13 @@ from_id,to_id,travel_time
 0,0,0
 0,1,6
 0,2,10
-0,3,18
-0,4,13
+0,3,17
+0,4,12
 0,5,8
-0,6,5
-0,7,9
+0,6,4
+0,7,8
 0,8,12
-0,9,18
+0,9,17
 0,10,33
 0,11,29
 0,12,24
@@ -18,7 +18,7 @@ from_id,to_id,travel_time
 0,16,16
 0,17,17
 0,18,36
-0,19,32
+0,19,31
 0,20,27
 0,21,22
 0,22,20
@@ -27,30 +27,30 @@ from_id,to_id,travel_time
 0,25,17
 0,26,20
 0,27,39
-0,28,37
+0,28,36
 0,29,31
 0,30,26
 0,31,23
-0,32,20
+0,32,19
 0,33,18
 0,34,42
 0,35,38
-0,36,33
+0,36,32
 0,37,29
 0,38,25
 0,39,23
 0,40,23
 0,41,26
 0,42,53
-0,43,43
+0,43,42
 0,44,32
 0,45,29
 0,46,29
-0,47,29
-0,48,32
-0,49,33
+0,47,28
+0,48,31
+0,49,32
 0,50,36
-0,51,46
+0,51,45
 0,52,35
 0,53,34
 0,54,35
@@ -66,7 +66,7 @@ from_id,to_id,travel_time
 0,64,40
 0,65,40
 0,66,42
-0,67,45
+0,67,44
 0,68,44
 0,69,49
 0,70,46
@@ -78,7 +78,7 @@ from_id,to_id,travel_time
 0,76,48
 0,77,57
 0,78,52
-0,79,51
+0,79,50
 0,80,48
 0,81,50
 0,82,51
@@ -86,10 +86,10 @@ from_id,to_id,travel_time
 0,84,52
 0,85,60
 0,86,59
-0,87,55
+0,87,54
 0,88,53
 0,89,55
-0,90,55
+0,90,54
 0,91,56
 1,0,6
 1,1,0
@@ -100,7 +100,7 @@ from_id,to_id,travel_time
 1,6,9
 1,7,5
 1,8,8
-1,9,14
+1,9,13
 1,10,38
 1,11,33
 1,12,29
@@ -118,7 +118,7 @@ from_id,to_id,travel_time
 1,24,14
 1,25,16
 1,26,17
-1,27,44
+1,27,43
 1,28,41
 1,29,36
 1,30,29
@@ -138,21 +138,21 @@ from_id,to_id,travel_time
 1,44,34
 1,45,31
 1,46,30
-1,47,30
+1,47,29
 1,48,32
 1,49,33
 1,50,37
 1,51,49
-1,52,38
+1,52,37
 1,53,35
 1,54,36
-1,55,37
+1,55,36
 1,56,37
 1,57,38
 1,58,41
 1,59,53
 1,60,48
-1,61,45
+1,61,44
 1,62,40
 1,63,40
 1,64,41
@@ -160,7 +160,7 @@ from_id,to_id,travel_time
 1,66,43
 1,67,45
 1,68,45
-1,69,52
+1,69,51
 1,70,48
 1,71,45
 1,72,45
@@ -173,26 +173,26 @@ from_id,to_id,travel_time
 1,79,53
 1,80,49
 1,81,51
-1,82,52
+1,82,51
 1,83,51
 1,84,53
 1,85,62
 1,86,61
 1,87,56
 1,88,54
-1,89,56
-1,90,56
+1,89,55
+1,90,55
 1,91,57
 2,0,10
-2,1,5
+2,1,4
 2,2,0
-2,3,9
-2,4,24
+2,3,8
+2,4,23
 2,5,18
 2,6,13
 2,7,7
 2,8,7
-2,9,10
+2,9,9
 2,10,42
 2,11,38
 2,12,33
@@ -210,26 +210,26 @@ from_id,to_id,travel_time
 2,24,18
 2,25,15
 2,26,16
-2,27,48
-2,28,44
-2,29,39
+2,27,47
+2,28,43
+2,29,38
 2,30,32
 2,31,28
 2,32,25
 2,33,22
 2,34,49
-2,35,45
+2,35,44
 2,36,39
 2,37,34
 2,38,30
 2,39,27
 2,40,27
-2,41,30
-2,42,57
+2,41,29
+2,42,56
 2,43,46
 2,44,37
 2,45,33
-2,46,33
+2,46,32
 2,47,32
 2,48,35
 2,49,36
@@ -255,10 +255,10 @@ from_id,to_id,travel_time
 2,69,54
 2,70,51
 2,71,48
-2,72,48
+2,72,47
 2,73,50
 2,74,49
-2,75,51
+2,75,50
 2,76,52
 2,77,62
 2,78,57
@@ -266,13 +266,13 @@ from_id,to_id,travel_time
 2,80,52
 2,81,54
 2,82,54
-2,83,54
+2,83,53
 2,84,55
 2,85,65
 2,86,63
 2,87,59
 2,88,57
-2,89,59
+2,89,58
 2,90,58
 2,91,60
 3,0,17
@@ -284,7 +284,7 @@ from_id,to_id,travel_time
 3,6,20
 3,7,13
 3,8,8
-3,9,4
+3,9,3
 3,10,48
 3,11,44
 3,12,39
@@ -300,10 +300,10 @@ from_id,to_id,travel_time
 3,22,30
 3,23,26
 3,24,21
-3,25,18
+3,25,17
 3,26,16
 3,27,51
-3,28,48
+3,28,47
 3,29,42
 3,30,35
 3,31,32
@@ -316,8 +316,8 @@ from_id,to_id,travel_time
 3,38,34
 3,39,31
 3,40,31
-3,41,34
-3,42,61
+3,41,33
+3,42,60
 3,43,50
 3,44,41
 3,45,37
@@ -325,7 +325,7 @@ from_id,to_id,travel_time
 3,47,36
 3,48,39
 3,49,40
-3,50,44
+3,50,43
 3,51,56
 3,52,44
 3,53,41
@@ -346,7 +346,7 @@ from_id,to_id,travel_time
 3,68,51
 3,69,58
 3,70,55
-3,71,52
+3,71,51
 3,72,51
 3,73,54
 3,74,53
@@ -358,7 +358,7 @@ from_id,to_id,travel_time
 3,80,56
 3,81,58
 3,82,58
-3,83,58
+3,83,57
 3,84,59
 3,85,69
 3,86,67
@@ -366,8 +366,8 @@ from_id,to_id,travel_time
 3,88,60
 3,89,62
 3,90,62
-3,91,64
-4,0,13
+3,91,63
+4,0,12
 4,1,19
 4,2,23
 4,3,30
@@ -376,7 +376,7 @@ from_id,to_id,travel_time
 4,6,12
 4,7,17
 4,8,23
-4,9,31
+4,9,30
 4,10,22
 4,11,17
 4,12,13
@@ -393,8 +393,8 @@ from_id,to_id,travel_time
 4,23,17
 4,24,18
 4,25,22
-4,26,25
-4,27,29
+4,26,24
+4,27,28
 4,28,26
 4,29,22
 4,30,16
@@ -420,20 +420,20 @@ from_id,to_id,travel_time
 4,50,39
 4,51,36
 4,52,31
-4,53,31
+4,53,30
 4,54,32
 4,55,34
 4,56,35
 4,57,40
-4,58,43
+4,58,42
 4,59,42
 4,60,37
 4,61,35
 4,62,35
 4,63,35
-4,64,37
+4,64,36
 4,65,38
-4,66,41
+4,66,40
 4,67,45
 4,68,46
 4,69,42
@@ -445,14 +445,14 @@ from_id,to_id,travel_time
 4,75,48
 4,76,51
 4,77,50
-4,78,46
-4,79,46
+4,78,45
+4,79,45
 4,80,44
 4,81,47
 4,82,49
 4,83,49
 4,84,51
-4,85,54
+4,85,53
 4,86,52
 4,87,50
 4,88,49
@@ -462,13 +462,13 @@ from_id,to_id,travel_time
 5,0,8
 5,1,14
 5,2,18
-5,3,25
-5,4,9
+5,3,24
+5,4,8
 5,5,0
 5,6,5
 5,7,11
-5,8,18
-5,9,25
+5,8,17
+5,9,24
 5,10,27
 5,11,22
 5,12,18
@@ -482,37 +482,37 @@ from_id,to_id,travel_time
 5,20,20
 5,21,15
 5,22,13
-5,23,11
+5,23,10
 5,24,12
 5,25,16
 5,26,19
-5,27,33
+5,27,32
 5,28,30
 5,29,25
 5,30,19
 5,31,16
 5,32,13
 5,33,16
-5,34,36
+5,34,35
 5,35,31
 5,36,26
 5,37,22
 5,38,18
 5,39,19
 5,40,20
-5,41,24
+5,41,23
 5,42,50
-5,43,40
+5,43,39
 5,44,25
 5,45,23
 5,46,24
-5,47,25
+5,47,24
 5,48,28
-5,49,30
+5,49,29
 5,50,33
 5,51,39
 5,52,29
-5,53,29
+5,53,28
 5,54,30
 5,55,32
 5,56,32
@@ -523,7 +523,7 @@ from_id,to_id,travel_time
 5,61,35
 5,62,33
 5,63,33
-5,64,35
+5,64,34
 5,65,36
 5,66,38
 5,67,41
@@ -551,7 +551,7 @@ from_id,to_id,travel_time
 5,89,51
 5,90,51
 5,91,53
-6,0,5
+6,0,4
 6,1,9
 6,2,13
 6,3,20
@@ -560,7 +560,7 @@ from_id,to_id,travel_time
 6,6,0
 6,7,7
 6,8,14
-6,9,21
+6,9,20
 6,10,31
 6,11,27
 6,12,22
@@ -570,7 +570,7 @@ from_id,to_id,travel_time
 6,16,13
 6,17,16
 6,18,34
-6,19,30
+6,19,29
 6,20,25
 6,21,20
 6,22,17
@@ -579,45 +579,45 @@ from_id,to_id,travel_time
 6,25,12
 6,26,15
 6,27,37
-6,28,35
+6,28,34
 6,29,29
 6,30,23
 6,31,19
 6,32,16
 6,33,14
 6,34,40
-6,35,36
+6,35,35
 6,36,30
 6,37,26
 6,38,21
-6,39,20
+6,39,19
 6,40,19
 6,41,22
 6,42,49
 6,43,38
-6,44,29
+6,44,28
 6,45,25
-6,46,25
+6,46,24
 6,47,24
 6,48,27
 6,49,28
 6,50,32
 6,51,43
-6,52,32
+6,52,31
 6,53,30
-6,54,31
+6,54,30
 6,55,31
-6,56,32
+6,56,31
 6,57,33
-6,58,36
-6,59,48
+6,58,35
+6,59,47
 6,60,42
-6,61,39
+6,61,38
 6,62,35
 6,63,35
 6,64,36
 6,65,36
-6,66,38
+6,66,37
 6,67,40
 6,68,39
 6,69,46
@@ -634,25 +634,25 @@ from_id,to_id,travel_time
 6,80,44
 6,81,46
 6,82,46
-6,83,46
+6,83,45
 6,84,48
 6,85,57
 6,86,55
 6,87,51
 6,88,49
-6,89,51
-6,90,51
+6,89,50
+6,90,50
 6,91,52
-7,0,9
+7,0,8
 7,1,5
 7,2,7
-7,3,14
-7,4,18
+7,3,13
+7,4,17
 7,5,11
 7,6,7
 7,7,0
 7,8,6
-7,9,14
+7,9,13
 7,10,35
 7,11,31
 7,12,26
@@ -675,9 +675,9 @@ from_id,to_id,travel_time
 7,29,32
 7,30,25
 7,31,22
-7,32,19
+7,32,18
 7,33,16
-7,34,43
+7,34,42
 7,35,38
 7,36,33
 7,37,28
@@ -685,7 +685,7 @@ from_id,to_id,travel_time
 7,39,21
 7,40,21
 7,41,23
-7,42,51
+7,42,50
 7,43,40
 7,44,31
 7,45,27
@@ -693,7 +693,7 @@ from_id,to_id,travel_time
 7,47,26
 7,48,29
 7,49,30
-7,50,34
+7,50,33
 7,51,45
 7,52,34
 7,53,31
@@ -713,20 +713,20 @@ from_id,to_id,travel_time
 7,67,42
 7,68,41
 7,69,48
-7,70,45
-7,71,42
+7,70,44
+7,71,41
 7,72,41
 7,73,44
 7,74,43
 7,75,44
-7,76,46
+7,76,45
 7,77,55
 7,78,51
 7,79,49
 7,80,46
 7,81,47
 7,82,48
-7,83,48
+7,83,47
 7,84,49
 7,85,59
 7,86,57
@@ -734,17 +734,17 @@ from_id,to_id,travel_time
 7,88,50
 7,89,52
 7,90,52
-7,91,54
+7,91,53
 8,0,12
-8,1,9
+8,1,8
 8,2,7
-8,3,9
-8,4,24
+8,3,8
+8,4,23
 8,5,17
 8,6,14
 8,7,6
 8,8,0
-8,9,9
+8,9,8
 8,10,42
 8,11,37
 8,12,33
@@ -762,22 +762,22 @@ from_id,to_id,travel_time
 8,24,15
 8,25,11
 8,26,11
-8,27,45
-8,28,41
+8,27,44
+8,28,40
 8,29,35
 8,30,29
 8,31,25
 8,32,22
 8,33,19
 8,34,46
-8,35,42
+8,35,41
 8,36,36
 8,37,31
 8,38,27
 8,39,24
 8,40,24
-8,41,27
-8,42,54
+8,41,26
+8,42,53
 8,43,43
 8,44,34
 8,45,30
@@ -788,7 +788,7 @@ from_id,to_id,travel_time
 8,50,37
 8,51,49
 8,52,37
-8,53,35
+8,53,34
 8,54,35
 8,55,36
 8,56,36
@@ -799,8 +799,8 @@ from_id,to_id,travel_time
 8,61,44
 8,62,40
 8,63,39
-8,64,41
-8,65,41
+8,64,40
+8,65,40
 8,66,42
 8,67,45
 8,68,44
@@ -818,7 +818,7 @@ from_id,to_id,travel_time
 8,80,49
 8,81,51
 8,82,51
-8,83,51
+8,83,50
 8,84,52
 8,85,62
 8,86,60
@@ -826,12 +826,12 @@ from_id,to_id,travel_time
 8,88,53
 8,89,55
 8,90,55
-8,91,57
-9,0,18
-9,1,14
+8,91,56
+9,0,17
+9,1,13
 9,2,9
-9,3,4
-9,4,31
+9,3,3
+9,4,30
 9,5,24
 9,6,20
 9,7,13
@@ -841,7 +841,7 @@ from_id,to_id,travel_time
 9,11,43
 9,12,39
 9,13,25
-9,14,20
+9,14,19
 9,15,14
 9,16,9
 9,17,4
@@ -868,10 +868,10 @@ from_id,to_id,travel_time
 9,38,33
 9,39,30
 9,40,30
-9,41,33
-9,42,60
+9,41,32
+9,42,59
 9,43,49
-9,44,41
+9,44,40
 9,45,36
 9,46,35
 9,47,35
@@ -879,20 +879,20 @@ from_id,to_id,travel_time
 9,49,39
 9,50,43
 9,51,55
-9,52,44
+9,52,43
 9,53,41
 9,54,41
 9,55,42
 9,56,42
 9,57,44
 9,58,46
-9,59,60
-9,60,55
+9,59,59
+9,60,54
 9,61,50
 9,62,46
 9,63,45
-9,64,47
-9,65,47
+9,64,46
+9,65,46
 9,66,48
 9,67,51
 9,68,50
@@ -910,25 +910,25 @@ from_id,to_id,travel_time
 9,80,55
 9,81,57
 9,82,57
-9,83,57
+9,83,56
 9,84,58
 9,85,68
 9,86,66
 9,87,62
 9,88,60
-9,89,62
+9,89,61
 9,90,61
-9,91,63
-10,0,34
+9,91,62
+10,0,33
 10,1,38
 10,2,42
-10,3,49
+10,3,48
 10,4,22
 10,5,27
 10,6,31
 10,7,35
 10,8,42
-10,9,49
+10,9,48
 10,10,0
 10,11,6
 10,12,9
@@ -944,7 +944,7 @@ from_id,to_id,travel_time
 10,22,19
 10,23,26
 10,24,27
-10,25,33
+10,25,32
 10,26,37
 10,27,10
 10,28,14
@@ -961,11 +961,11 @@ from_id,to_id,travel_time
 10,39,30
 10,40,34
 10,41,38
-10,42,65
+10,42,64
 10,43,54
 10,44,26
-10,45,29
-10,46,32
+10,45,28
+10,46,31
 10,47,36
 10,48,41
 10,49,44
@@ -973,7 +973,7 @@ from_id,to_id,travel_time
 10,51,26
 10,52,29
 10,53,33
-10,54,36
+10,54,35
 10,55,37
 10,56,41
 10,57,47
@@ -988,39 +988,39 @@ from_id,to_id,travel_time
 10,66,44
 10,67,51
 10,68,52
-10,69,38
+10,69,37
 10,70,37
-10,71,40
+10,71,39
 10,72,43
 10,73,46
 10,74,49
-10,75,54
-10,76,57
+10,75,53
+10,76,56
 10,77,44
 10,78,41
 10,79,41
 10,80,42
 10,81,48
-10,82,52
-10,83,54
+10,82,51
+10,83,53
 10,84,56
 10,85,47
 10,86,46
 10,87,45
 10,88,46
-10,89,54
+10,89,53
 10,90,58
-10,91,60
+10,91,59
 11,0,29
-11,1,34
+11,1,33
 11,2,38
-11,3,45
-11,4,18
+11,3,44
+11,4,17
 11,5,22
 11,6,27
 11,7,31
 11,8,37
-11,9,44
+11,9,43
 11,10,6
 11,11,0
 11,12,5
@@ -1038,40 +1038,40 @@ from_id,to_id,travel_time
 11,24,23
 11,25,28
 11,26,32
-11,27,12
+11,27,11
 11,28,11
 11,29,11
 11,30,15
 11,31,18
 11,32,23
 11,33,26
-11,34,18
+11,34,17
 11,35,14
 11,36,15
 11,37,17
 11,38,21
-11,39,26
+11,39,25
 11,40,29
 11,41,33
 11,42,60
 11,43,49
 11,44,22
 11,45,25
-11,46,28
+11,46,27
 11,47,31
-11,48,37
+11,48,36
 11,49,39
 11,50,43
 11,51,27
 11,52,26
 11,53,29
-11,54,32
-11,55,34
+11,54,31
+11,55,33
 11,56,37
 11,57,43
-11,58,46
-11,59,36
-11,60,31
+11,58,45
+11,59,35
+11,60,30
 11,61,29
 11,62,30
 11,63,32
@@ -1083,7 +1083,7 @@ from_id,to_id,travel_time
 11,69,36
 11,70,35
 11,71,37
-11,72,40
+11,72,39
 11,73,42
 11,74,45
 11,75,49
@@ -1094,7 +1094,7 @@ from_id,to_id,travel_time
 11,80,39
 11,81,45
 11,82,48
-11,83,50
+11,83,49
 11,84,52
 11,85,47
 11,86,46
@@ -1102,17 +1102,17 @@ from_id,to_id,travel_time
 11,88,44
 11,89,51
 11,90,54
-11,91,56
-12,0,25
+11,91,55
+12,0,24
 12,1,29
 12,2,33
-12,3,40
-12,4,14
+12,3,39
+12,4,13
 12,5,18
 12,6,22
 12,7,26
 12,8,33
-12,9,40
+12,9,39
 12,10,9
 12,11,5
 12,12,0
@@ -1130,12 +1130,12 @@ from_id,to_id,travel_time
 12,24,19
 12,25,24
 12,26,28
-12,27,16
+12,27,15
 12,28,13
 12,29,10
 12,30,11
 12,31,14
-12,32,20
+12,32,19
 12,33,22
 12,34,21
 12,35,17
@@ -1143,22 +1143,22 @@ from_id,to_id,travel_time
 12,37,15
 12,38,17
 12,39,22
-12,40,26
+12,40,25
 12,41,29
 12,42,56
 12,43,45
 12,44,20
 12,45,22
-12,46,24
-12,47,28
-12,48,33
+12,46,23
+12,47,27
+12,48,32
 12,49,35
 12,50,39
 12,51,28
-12,52,25
+12,52,24
 12,53,27
-12,54,29
-12,55,31
+12,54,28
+12,55,30
 12,56,33
 12,57,39
 12,58,42
@@ -1178,7 +1178,7 @@ from_id,to_id,travel_time
 12,72,37
 12,73,40
 12,74,42
-12,75,46
+12,75,45
 12,76,48
 12,77,42
 12,78,38
@@ -1186,7 +1186,7 @@ from_id,to_id,travel_time
 12,80,38
 12,81,43
 12,82,45
-12,83,47
+12,83,46
 12,84,49
 12,85,46
 12,86,44
@@ -1194,18 +1194,18 @@ from_id,to_id,travel_time
 12,88,42
 12,89,49
 12,90,51
-12,91,53
+12,91,52
 13,0,12
 13,1,15
 13,2,19
-13,3,26
+13,3,25
 13,4,11
 13,5,4
 13,6,8
 13,7,12
-13,8,19
-13,9,26
-13,10,27
+13,8,18
+13,9,25
+13,10,26
 13,11,22
 13,12,18
 13,13,0
@@ -1214,8 +1214,8 @@ from_id,to_id,travel_time
 13,16,17
 13,17,21
 13,18,26
-13,19,22
-13,20,17
+13,19,21
+13,20,16
 13,21,12
 13,22,9
 13,23,6
@@ -1223,33 +1223,33 @@ from_id,to_id,travel_time
 13,25,12
 13,26,17
 13,27,29
-13,28,27
+13,28,26
 13,29,21
 13,30,15
 13,31,14
 13,32,10
 13,33,12
 13,34,32
-13,35,28
+13,35,27
 13,36,22
 13,37,19
 13,38,15
 13,39,16
 13,40,16
 13,41,19
-13,42,47
+13,42,46
 13,43,36
 13,44,22
 13,45,21
 13,46,21
 13,47,22
-13,48,25
+13,48,24
 13,49,26
 13,50,29
 13,51,35
 13,52,26
 13,53,26
-13,54,28
+13,54,27
 13,55,29
 13,56,29
 13,57,30
@@ -1262,7 +1262,7 @@ from_id,to_id,travel_time
 13,64,32
 13,65,33
 13,66,35
-13,67,38
+13,67,37
 13,68,37
 13,69,39
 13,70,37
@@ -1271,7 +1271,7 @@ from_id,to_id,travel_time
 13,73,39
 13,74,39
 13,75,40
-13,76,42
+13,76,41
 13,77,47
 13,78,42
 13,79,42
@@ -1286,9 +1286,9 @@ from_id,to_id,travel_time
 13,88,44
 13,89,48
 13,90,48
-13,91,50
+13,91,49
 14,0,9
-14,1,11
+14,1,10
 14,2,14
 14,3,20
 14,4,14
@@ -1296,7 +1296,7 @@ from_id,to_id,travel_time
 14,6,4
 14,7,7
 14,8,13
-14,9,20
+14,9,19
 14,10,30
 14,11,26
 14,12,21
@@ -1306,7 +1306,7 @@ from_id,to_id,travel_time
 14,16,12
 14,17,15
 14,18,30
-14,19,26
+14,19,25
 14,20,21
 14,21,16
 14,22,12
@@ -1315,14 +1315,14 @@ from_id,to_id,travel_time
 14,25,8
 14,26,12
 14,27,33
-14,28,31
+14,28,30
 14,29,25
-14,30,19
+14,30,18
 14,31,15
 14,32,12
 14,33,10
 14,34,36
-14,35,32
+14,35,31
 14,36,26
 14,37,21
 14,38,17
@@ -1330,12 +1330,12 @@ from_id,to_id,travel_time
 14,40,14
 14,41,17
 14,42,44
-14,43,34
+14,43,33
 14,44,24
 14,45,21
 14,46,20
 14,47,20
-14,48,23
+14,48,22
 14,49,24
 14,50,27
 14,51,39
@@ -1354,20 +1354,20 @@ from_id,to_id,travel_time
 14,64,31
 14,65,31
 14,66,33
-14,67,36
+14,67,35
 14,68,35
 14,69,41
-14,70,39
-14,71,36
+14,70,38
+14,71,35
 14,72,35
 14,73,37
 14,74,37
 14,75,38
-14,76,40
+14,76,39
 14,77,49
 14,78,44
 14,79,43
-14,80,40
+14,80,39
 14,81,41
 14,82,42
 14,83,41
@@ -1388,7 +1388,7 @@ from_id,to_id,travel_time
 15,6,8
 15,7,7
 15,8,8
-15,9,15
+15,9,14
 15,10,34
 15,11,29
 15,12,25
@@ -1405,27 +1405,27 @@ from_id,to_id,travel_time
 15,23,11
 15,24,7
 15,25,5
-15,26,8
-15,27,37
-15,28,33
-15,29,28
+15,26,7
+15,27,36
+15,28,32
+15,29,27
 15,30,21
 15,31,17
 15,32,14
 15,33,11
 15,34,38
-15,35,34
+15,35,33
 15,36,28
 15,37,23
 15,38,19
 15,39,16
 15,40,16
-15,41,19
-15,42,46
+15,41,18
+15,42,45
 15,43,35
 15,44,26
 15,45,22
-15,46,22
+15,46,21
 15,47,21
 15,48,24
 15,49,25
@@ -1451,10 +1451,10 @@ from_id,to_id,travel_time
 15,69,43
 15,70,40
 15,71,37
-15,72,37
+15,72,36
 15,73,39
 15,74,38
-15,75,40
+15,75,39
 15,76,41
 15,77,51
 15,78,46
@@ -1462,36 +1462,36 @@ from_id,to_id,travel_time
 15,80,41
 15,81,43
 15,82,43
-15,83,43
+15,83,42
 15,84,44
 15,85,54
 15,86,52
 15,87,48
-15,88,46
-15,89,48
+15,88,45
+15,89,47
 15,90,47
 15,91,49
 16,0,16
 16,1,12
 16,2,11
-16,3,13
-16,4,23
+16,3,12
+16,4,22
 16,5,16
 16,6,13
 16,7,7
 16,8,5
-16,9,10
+16,9,9
 16,10,40
 16,11,36
 16,12,31
 16,13,17
 16,14,12
-16,15,7
+16,15,6
 16,16,0
 16,17,5
 16,18,40
 16,19,35
-16,20,31
+16,20,30
 16,21,26
 16,22,22
 16,23,17
@@ -1502,10 +1502,10 @@ from_id,to_id,travel_time
 16,28,39
 16,29,34
 16,30,27
-16,31,24
-16,32,21
+16,31,23
+16,32,20
 16,33,17
-16,34,45
+16,34,44
 16,35,40
 16,36,35
 16,37,30
@@ -1525,22 +1525,22 @@ from_id,to_id,travel_time
 16,51,47
 16,52,36
 16,53,33
-16,54,34
-16,55,35
+16,54,33
+16,55,34
 16,56,35
 16,57,36
 16,58,39
 16,59,52
 16,60,47
-16,61,43
+16,61,42
 16,62,38
 16,63,38
 16,64,39
 16,65,39
-16,66,41
+16,66,40
 16,67,43
 16,68,43
-16,69,50
+16,69,49
 16,70,46
 16,71,43
 16,72,43
@@ -1549,30 +1549,30 @@ from_id,to_id,travel_time
 16,75,46
 16,76,47
 16,77,57
-16,78,53
+16,78,52
 16,79,51
 16,80,47
 16,81,49
 16,82,49
-16,83,49
+16,83,48
 16,84,51
 16,85,60
 16,86,59
 16,87,54
 16,88,52
-16,89,54
-16,90,54
+16,89,53
+16,90,53
 16,91,55
 17,0,17
 17,1,13
 17,2,12
-17,3,9
+17,3,8
 17,4,26
 17,5,20
 17,6,16
 17,7,11
 17,8,7
-17,9,5
+17,9,4
 17,10,44
 17,11,39
 17,12,35
@@ -1590,26 +1590,26 @@ from_id,to_id,travel_time
 17,24,17
 17,25,11
 17,26,7
-17,27,47
+17,27,46
 17,28,43
 17,29,38
 17,30,31
 17,31,27
-17,32,24
+17,32,23
 17,33,20
 17,34,48
 17,35,44
-17,36,39
+17,36,38
 17,37,34
 17,38,29
-17,39,26
+17,39,25
 17,40,25
 17,41,28
 17,42,55
 17,43,44
 17,44,36
-17,45,32
-17,46,31
+17,45,31
+17,46,30
 17,47,30
 17,48,33
 17,49,34
@@ -1617,19 +1617,19 @@ from_id,to_id,travel_time
 17,51,51
 17,52,39
 17,53,36
-17,54,37
+17,54,36
 17,55,37
-17,56,38
+17,56,37
 17,57,39
-17,58,42
+17,58,41
 17,59,55
 17,60,50
-17,61,46
+17,61,45
 17,62,41
 17,63,41
 17,64,42
 17,65,42
-17,66,44
+17,66,43
 17,67,46
 17,68,45
 17,69,52
@@ -1642,29 +1642,29 @@ from_id,to_id,travel_time
 17,76,50
 17,77,60
 17,78,55
-17,79,54
+17,79,53
 17,80,50
 17,81,52
 17,82,52
-17,83,52
+17,83,51
 17,84,54
 17,85,63
 17,86,62
 17,87,57
 17,88,55
-17,89,57
-17,90,57
+17,89,56
+17,90,56
 17,91,58
 18,0,36
 18,1,41
 18,2,44
-18,3,49
+18,3,48
 18,4,25
 18,5,29
 18,6,34
 18,7,37
 18,8,41
-18,9,49
+18,9,48
 18,10,5
 18,11,8
 18,12,12
@@ -1675,14 +1675,14 @@ from_id,to_id,travel_time
 18,17,44
 18,18,0
 18,19,5
-18,20,10
+18,20,9
 18,21,14
 18,22,18
 18,23,25
 18,24,27
 18,25,32
 18,26,37
-18,27,5
+18,27,4
 18,28,8
 18,29,10
 18,30,15
@@ -1716,16 +1716,16 @@ from_id,to_id,travel_time
 18,58,47
 18,59,30
 18,60,25
-18,61,26
+18,61,25
 18,62,27
 18,63,29
 18,64,33
-18,65,37
+18,65,36
 18,66,39
 18,67,47
 18,68,47
 18,69,32
-18,70,32
+18,70,31
 18,71,34
 18,72,37
 18,73,40
@@ -1738,16 +1738,16 @@ from_id,to_id,travel_time
 18,80,36
 18,81,42
 18,82,46
-18,83,49
+18,83,48
 18,84,51
-18,85,42
+18,85,41
 18,86,40
 18,87,40
 18,88,41
 18,89,48
 18,90,53
 18,91,55
-19,0,32
+19,0,31
 19,1,36
 19,2,40
 19,3,44
@@ -1756,12 +1756,12 @@ from_id,to_id,travel_time
 19,6,29
 19,7,33
 19,8,37
-19,9,44
+19,9,43
 19,10,8
 19,11,5
 19,12,8
 19,13,21
-19,14,26
+19,14,25
 19,15,29
 19,16,35
 19,17,39
@@ -1774,8 +1774,8 @@ from_id,to_id,travel_time
 19,24,22
 19,25,27
 19,26,32
-19,27,8
-19,28,7
+19,27,7
+19,28,6
 19,29,7
 19,30,12
 19,31,17
@@ -1788,26 +1788,26 @@ from_id,to_id,travel_time
 19,38,17
 19,39,23
 19,40,28
-19,41,32
-19,42,59
+19,41,31
+19,42,58
 19,43,48
 19,44,18
 19,45,21
-19,46,24
+19,46,23
 19,47,28
-19,48,35
+19,48,34
 19,49,37
-19,50,42
+19,50,41
 19,51,22
 19,52,22
 19,53,25
-19,54,28
-19,55,30
+19,54,27
+19,55,29
 19,56,33
 19,57,39
 19,58,44
-19,59,32
-19,60,27
+19,59,31
+19,60,26
 19,61,25
 19,62,26
 19,63,28
@@ -1822,15 +1822,15 @@ from_id,to_id,travel_time
 19,72,35
 19,73,38
 19,74,41
-19,75,46
-19,76,49
+19,75,45
+19,76,48
 19,77,40
 19,78,35
 19,79,35
 19,80,35
 19,81,41
 19,82,44
-19,83,46
+19,83,45
 19,84,48
 19,85,43
 19,86,42
@@ -1838,17 +1838,17 @@ from_id,to_id,travel_time
 19,88,40
 19,89,47
 19,90,50
-19,91,52
+19,91,51
 20,0,27
 20,1,31
 20,2,35
 20,3,39
-20,4,18
+20,4,17
 20,5,20
 20,6,25
 20,7,28
 20,8,32
-20,9,39
+20,9,38
 20,10,11
 20,11,7
 20,12,5
@@ -1866,11 +1866,11 @@ from_id,to_id,travel_time
 20,24,18
 20,25,23
 20,26,27
-20,27,13
-20,28,10
+20,27,12
+20,28,9
 20,29,6
 20,30,9
-20,31,12
+20,31,11
 20,32,17
 20,33,20
 20,34,17
@@ -1904,16 +1904,16 @@ from_id,to_id,travel_time
 20,62,25
 20,63,27
 20,64,29
-20,65,32
-20,66,34
+20,65,31
+20,66,33
 20,67,41
 20,68,41
-20,69,31
+20,69,30
 20,70,30
-20,71,32
+20,71,31
 20,72,34
 20,73,37
-20,74,39
+20,74,38
 20,75,43
 20,76,46
 20,77,39
@@ -1928,13 +1928,13 @@ from_id,to_id,travel_time
 20,86,41
 20,87,38
 20,88,39
-20,89,46
-20,90,48
-20,91,50
+20,89,45
+20,90,47
+20,91,49
 21,0,22
 21,1,27
 21,2,30
-21,3,35
+21,3,34
 21,4,13
 21,5,15
 21,6,20
@@ -1972,10 +1972,10 @@ from_id,to_id,travel_time
 21,38,10
 21,39,15
 21,40,19
-21,41,23
-21,42,50
+21,41,22
+21,42,49
 21,43,39
-21,44,17
+21,44,16
 21,45,16
 21,46,17
 21,47,21
@@ -1997,12 +1997,12 @@ from_id,to_id,travel_time
 21,63,26
 21,64,27
 21,65,29
-21,66,32
+21,66,31
 21,67,36
 21,68,37
-21,69,32
+21,69,31
 21,70,31
-21,71,32
+21,71,31
 21,72,32
 21,73,35
 21,74,36
@@ -2015,7 +2015,7 @@ from_id,to_id,travel_time
 21,81,38
 21,82,40
 21,83,40
-21,84,43
+21,84,42
 21,85,43
 21,86,42
 21,87,39
@@ -2026,11 +2026,11 @@ from_id,to_id,travel_time
 22,0,20
 22,1,23
 22,2,26
-22,3,31
-22,4,11
+22,3,30
+22,4,10
 22,5,13
 22,6,17
-22,7,20
+22,7,19
 22,8,23
 22,9,30
 22,10,19
@@ -2042,14 +2042,14 @@ from_id,to_id,travel_time
 22,16,22
 22,17,26
 22,18,18
-22,19,14
-22,20,9
+22,19,13
+22,20,8
 22,21,4
 22,22,0
 22,23,7
 22,24,9
 22,25,14
-22,26,19
+22,26,18
 22,27,21
 22,28,18
 22,29,13
@@ -2064,8 +2064,8 @@ from_id,to_id,travel_time
 22,38,10
 22,39,13
 22,40,15
-22,41,19
-22,42,46
+22,41,18
+22,42,45
 22,43,35
 22,44,17
 22,45,16
@@ -2089,12 +2089,12 @@ from_id,to_id,travel_time
 22,63,26
 22,64,27
 22,65,29
-22,66,32
+22,66,31
 22,67,36
 22,68,36
 22,69,33
 22,70,32
-22,71,32
+22,71,31
 22,72,32
 22,73,34
 22,74,36
@@ -2106,15 +2106,15 @@ from_id,to_id,travel_time
 22,80,35
 22,81,38
 22,82,40
-22,83,40
+22,83,39
 22,84,42
-22,85,45
+22,85,44
 22,86,43
 22,87,41
-22,88,40
+22,88,39
 22,89,44
 22,90,45
-22,91,47
+22,91,46
 23,0,16
 23,1,19
 23,2,22
@@ -2124,7 +2124,7 @@ from_id,to_id,travel_time
 23,6,12
 23,7,15
 23,8,19
-23,9,26
+23,9,25
 23,10,26
 23,11,21
 23,12,17
@@ -2142,8 +2142,8 @@ from_id,to_id,travel_time
 23,24,5
 23,25,10
 23,26,14
-23,27,29
-23,28,26
+23,27,28
+23,28,25
 23,29,20
 23,30,13
 23,31,10
@@ -2154,10 +2154,10 @@ from_id,to_id,travel_time
 23,36,21
 23,37,16
 23,38,12
-23,39,13
+23,39,12
 23,40,13
-23,41,16
-23,42,43
+23,41,15
+23,42,42
 23,43,32
 23,44,19
 23,45,18
@@ -2180,7 +2180,7 @@ from_id,to_id,travel_time
 23,62,27
 23,63,27
 23,64,29
-23,65,30
+23,65,29
 23,66,31
 23,67,34
 23,68,33
@@ -2190,34 +2190,34 @@ from_id,to_id,travel_time
 23,72,33
 23,73,36
 23,74,35
-23,75,37
+23,75,36
 23,76,38
 23,77,44
 23,78,39
-23,79,39
+23,79,38
 23,80,36
 23,81,39
 23,82,40
-23,83,40
+23,83,39
 23,84,41
 23,85,47
 23,86,46
 23,87,43
 23,88,41
-23,89,45
+23,89,44
 23,90,44
 23,91,46
-24,0,14
-24,1,15
+24,0,13
+24,1,14
 24,2,18
-24,3,22
-24,4,19
+24,3,21
+24,4,18
 24,5,12
 24,6,9
 24,7,11
 24,8,15
-24,9,22
-24,10,28
+24,9,21
+24,10,27
 24,11,23
 24,12,19
 24,13,8
@@ -2226,7 +2226,7 @@ from_id,to_id,travel_time
 24,16,13
 24,17,17
 24,18,27
-24,19,23
+24,19,22
 24,20,18
 24,21,13
 24,22,9
@@ -2235,7 +2235,7 @@ from_id,to_id,travel_time
 24,25,6
 24,26,11
 24,27,30
-24,28,27
+24,28,26
 24,29,21
 24,30,14
 24,31,11
@@ -2250,13 +2250,13 @@ from_id,to_id,travel_time
 24,40,10
 24,41,13
 24,42,40
-24,43,30
+24,43,29
 24,44,20
 24,45,17
 24,46,16
-24,47,16
-24,48,19
-24,49,20
+24,47,15
+24,48,18
+24,49,19
 24,50,23
 24,51,35
 24,52,23
@@ -2274,7 +2274,7 @@ from_id,to_id,travel_time
 24,64,27
 24,65,27
 24,66,29
-24,67,32
+24,67,31
 24,68,31
 24,69,37
 24,70,34
@@ -2297,19 +2297,19 @@ from_id,to_id,travel_time
 24,87,43
 24,88,40
 24,89,42
-24,90,42
+24,90,41
 24,91,43
 25,0,17
-25,1,17
+25,1,16
 25,2,15
-25,3,18
+25,3,17
 25,4,22
 25,5,16
 25,6,12
 25,7,11
 25,8,11
-25,9,17
-25,10,33
+25,9,16
+25,10,32
 25,11,28
 25,12,24
 25,13,12
@@ -2318,7 +2318,7 @@ from_id,to_id,travel_time
 25,16,9
 25,17,11
 25,18,32
-25,19,28
+25,19,27
 25,20,23
 25,21,18
 25,22,14
@@ -2346,7 +2346,7 @@ from_id,to_id,travel_time
 25,44,25
 25,45,20
 25,46,19
-25,47,19
+25,47,18
 25,48,21
 25,49,22
 25,50,26
@@ -2354,7 +2354,7 @@ from_id,to_id,travel_time
 25,52,27
 25,53,24
 25,54,25
-25,55,26
+25,55,25
 25,56,26
 25,57,27
 25,58,30
@@ -2368,8 +2368,8 @@ from_id,to_id,travel_time
 25,66,32
 25,67,34
 25,68,34
-25,69,41
-25,70,38
+25,69,40
+25,70,37
 25,71,34
 25,72,34
 25,73,36
@@ -2379,34 +2379,34 @@ from_id,to_id,travel_time
 25,77,48
 25,78,44
 25,79,42
-25,80,39
+25,80,38
 25,81,40
-25,82,41
+25,82,40
 25,83,40
 25,84,42
-25,85,52
+25,85,51
 25,86,50
 25,87,46
 25,88,43
-25,89,45
-25,90,45
+25,89,44
+25,90,44
 25,91,46
 26,0,20
 26,1,17
 26,2,16
 26,3,16
-26,4,25
+26,4,24
 26,5,19
 26,6,15
 26,7,12
 26,8,11
-26,9,13
+26,9,12
 26,10,37
 26,11,32
 26,12,28
 26,13,17
 26,14,12
-26,15,8
+26,15,7
 26,16,8
 26,17,7
 26,18,37
@@ -2418,7 +2418,7 @@ from_id,to_id,travel_time
 26,24,11
 26,25,4
 26,26,0
-26,27,40
+26,27,39
 26,28,36
 26,29,31
 26,30,24
@@ -2427,31 +2427,31 @@ from_id,to_id,travel_time
 26,33,13
 26,34,41
 26,35,37
-26,36,32
+26,36,31
 26,37,26
 26,38,22
 26,39,18
 26,40,18
 26,41,20
-26,42,48
+26,42,47
 26,43,37
 26,44,29
 26,45,24
 26,46,23
 26,47,23
-26,48,26
+26,48,25
 26,49,27
 26,50,30
 26,51,44
-26,52,32
+26,52,31
 26,53,28
 26,54,29
 26,55,30
 26,56,30
-26,57,32
+26,57,31
 26,58,34
 26,59,47
-26,60,43
+26,60,42
 26,61,38
 26,62,34
 26,63,33
@@ -2462,12 +2462,12 @@ from_id,to_id,travel_time
 26,68,38
 26,69,45
 26,70,42
-26,71,39
+26,71,38
 26,72,38
 26,73,41
 26,74,40
 26,75,41
-26,76,43
+26,76,42
 26,77,52
 26,78,48
 26,79,46
@@ -2482,12 +2482,12 @@ from_id,to_id,travel_time
 26,88,47
 26,89,49
 26,90,49
-26,91,51
+26,91,50
 27,0,39
-27,1,44
+27,1,43
 27,2,47
-27,3,52
-27,4,29
+27,3,51
+27,4,28
 27,5,32
 27,6,37
 27,7,40
@@ -2502,8 +2502,8 @@ from_id,to_id,travel_time
 27,16,43
 27,17,46
 27,18,4
-27,19,8
-27,20,13
+27,19,7
+27,20,12
 27,21,17
 27,22,21
 27,23,28
@@ -2511,7 +2511,7 @@ from_id,to_id,travel_time
 27,25,35
 27,26,39
 27,27,0
-27,28,8
+27,28,7
 27,29,12
 27,30,17
 27,31,24
@@ -2525,13 +2525,13 @@ from_id,to_id,travel_time
 27,39,28
 27,40,33
 27,41,37
-27,42,64
+27,42,63
 27,43,53
 27,44,17
 27,45,23
 27,46,28
 27,47,32
-27,48,40
+27,48,39
 27,49,42
 27,50,47
 27,51,16
@@ -2540,21 +2540,21 @@ from_id,to_id,travel_time
 27,54,28
 27,55,33
 27,56,38
-27,57,43
+27,57,42
 27,58,47
-27,59,26
+27,59,25
 27,60,20
 27,61,21
 27,62,23
 27,63,25
 27,64,29
 27,65,33
-27,66,39
+27,66,38
 27,67,46
 27,68,47
-27,69,28
+27,69,27
 27,70,27
-27,71,30
+27,71,29
 27,72,33
 27,73,36
 27,74,40
@@ -2562,29 +2562,29 @@ from_id,to_id,travel_time
 27,76,49
 27,77,34
 27,78,31
-27,79,32
+27,79,31
 27,80,32
 27,81,38
-27,82,42
-27,83,45
+27,82,41
+27,83,44
 27,84,48
 27,85,37
 27,86,36
 27,87,36
-27,88,37
-27,89,44
-27,90,49
-27,91,51
+27,88,36
+27,89,43
+27,90,48
+27,91,50
 28,0,36
 28,1,41
 28,2,43
-28,3,48
+28,3,47
 28,4,26
 28,5,30
 28,6,34
 28,7,37
 28,8,40
-28,9,48
+28,9,47
 28,10,14
 28,11,11
 28,12,13
@@ -2595,24 +2595,24 @@ from_id,to_id,travel_time
 28,17,43
 28,18,8
 28,19,6
-28,20,10
+28,20,9
 28,21,14
 28,22,18
 28,23,25
 28,24,26
 28,25,31
 28,26,36
-28,27,8
+28,27,7
 28,28,0
 28,29,6
 28,30,12
 28,31,18
-28,32,22
+28,32,21
 28,33,27
 28,34,8
-28,35,5
+28,35,4
 28,36,8
-28,37,11
+28,37,10
 28,38,16
 28,39,22
 28,40,27
@@ -2627,9 +2627,9 @@ from_id,to_id,travel_time
 28,49,36
 28,50,41
 28,51,17
-28,52,18
-28,53,22
-28,54,25
+28,52,17
+28,53,21
+28,54,24
 28,55,26
 28,56,31
 28,57,37
@@ -2639,7 +2639,7 @@ from_id,to_id,travel_time
 28,61,20
 28,62,22
 28,63,23
-28,64,28
+28,64,27
 28,65,30
 28,66,33
 28,67,40
@@ -2649,40 +2649,40 @@ from_id,to_id,travel_time
 28,71,28
 28,72,32
 28,73,35
-28,74,38
+28,74,37
 28,75,42
 28,76,46
 28,77,35
 28,78,30
 28,79,30
-28,80,31
+28,80,30
 28,81,36
 28,82,40
-28,83,43
+28,83,42
 28,84,45
 28,85,38
 28,86,37
 28,87,34
 28,88,35
-28,89,43
+28,89,42
 28,90,47
-28,91,49
-29,0,32
+28,91,48
+29,0,31
 29,1,36
 29,2,38
-29,3,43
+29,3,42
 29,4,22
 29,5,25
 29,6,29
 29,7,32
 29,8,35
-29,9,43
+29,9,42
 29,10,15
 29,11,11
 29,12,10
 29,13,21
 29,14,25
-29,15,28
+29,15,27
 29,16,34
 29,17,38
 29,18,10
@@ -2694,15 +2694,15 @@ from_id,to_id,travel_time
 29,24,21
 29,25,26
 29,26,31
-29,27,13
-29,28,7
+29,27,12
+29,28,6
 29,29,0
 29,30,7
 29,31,14
 29,32,17
 29,33,23
 29,34,12
-29,35,8
+29,35,7
 29,36,5
 29,37,7
 29,38,12
@@ -2714,7 +2714,7 @@ from_id,to_id,travel_time
 29,44,11
 29,45,14
 29,46,18
-29,47,23
+29,47,22
 29,48,30
 29,49,32
 29,50,37
@@ -2750,7 +2750,7 @@ from_id,to_id,travel_time
 29,80,28
 29,81,34
 29,82,37
-29,83,39
+29,83,38
 29,84,41
 29,85,36
 29,86,35
@@ -2762,18 +2762,18 @@ from_id,to_id,travel_time
 30,0,26
 30,1,29
 30,2,32
-30,3,36
-30,4,17
+30,3,35
+30,4,16
 30,5,19
 30,6,23
 30,7,25
 30,8,29
-30,9,36
+30,9,35
 30,10,19
 30,11,15
 30,12,11
 30,13,15
-30,14,19
+30,14,18
 30,15,21
 30,16,27
 30,17,31
@@ -2786,12 +2786,12 @@ from_id,to_id,travel_time
 30,24,14
 30,25,19
 30,26,24
-30,27,18
+30,27,17
 30,28,12
 30,29,7
 30,30,0
 30,31,7
-30,32,12
+30,32,11
 30,33,16
 30,34,17
 30,35,13
@@ -2806,8 +2806,8 @@ from_id,to_id,travel_time
 30,44,11
 30,45,12
 30,46,15
-30,47,20
-30,48,25
+30,47,19
+30,48,24
 30,49,27
 30,50,31
 30,51,20
@@ -2820,23 +2820,23 @@ from_id,to_id,travel_time
 30,58,34
 30,59,26
 30,60,21
-30,61,20
+30,61,19
 30,62,21
 30,63,22
 30,64,23
 30,65,25
-30,66,28
+30,66,27
 30,67,35
 30,68,36
-30,69,27
-30,70,26
+30,69,26
+30,70,25
 30,71,27
 30,72,28
 30,73,30
 30,74,32
 30,75,37
 30,76,40
-30,77,35
+30,77,34
 30,78,30
 30,79,30
 30,80,30
@@ -2849,29 +2849,29 @@ from_id,to_id,travel_time
 30,87,34
 30,88,34
 30,89,40
-30,90,42
+30,90,41
 30,91,43
 31,0,23
-31,1,26
+31,1,25
 31,2,28
 31,3,32
-31,4,17
+31,4,16
 31,5,16
 31,6,19
 31,7,22
 31,8,25
-31,9,32
+31,9,31
 31,10,22
 31,11,18
 31,12,14
 31,13,14
 31,14,15
 31,15,17
-31,16,24
+31,16,23
 31,17,27
 31,18,21
 31,19,17
-31,20,12
+31,20,11
 31,21,7
 31,22,7
 31,23,10
@@ -2879,18 +2879,18 @@ from_id,to_id,travel_time
 31,25,16
 31,26,20
 31,27,24
-31,28,19
+31,28,18
 31,29,14
 31,30,7
 31,31,0
-31,32,6
+31,32,5
 31,33,11
 31,34,23
 31,35,19
 31,36,14
 31,37,7
 31,38,5
-31,39,8
+31,39,7
 31,40,11
 31,41,15
 31,42,42
@@ -2899,7 +2899,7 @@ from_id,to_id,travel_time
 31,45,11
 31,46,12
 31,47,14
-31,48,19
+31,48,18
 31,49,21
 31,50,25
 31,51,27
@@ -2917,19 +2917,19 @@ from_id,to_id,travel_time
 31,63,21
 31,64,22
 31,65,24
-31,66,27
+31,66,26
 31,67,31
 31,68,32
-31,69,30
-31,70,28
-31,71,27
+31,69,29
+31,70,27
+31,71,26
 31,72,27
 31,73,29
 31,74,31
 31,75,34
-31,76,37
+31,76,36
 31,77,37
-31,78,33
+31,78,32
 31,79,32
 31,80,30
 31,81,33
@@ -2942,8 +2942,8 @@ from_id,to_id,travel_time
 31,88,35
 31,89,39
 31,90,40
-31,91,42
-32,0,20
+31,91,41
+32,0,19
 32,1,22
 32,2,25
 32,3,29
@@ -2952,7 +2952,7 @@ from_id,to_id,travel_time
 32,6,16
 32,7,18
 32,8,22
-32,9,29
+32,9,28
 32,10,28
 32,11,23
 32,12,19
@@ -2971,7 +2971,7 @@ from_id,to_id,travel_time
 32,25,12
 32,26,16
 32,27,27
-32,28,22
+32,28,21
 32,29,17
 32,30,11
 32,31,5
@@ -2989,27 +2989,27 @@ from_id,to_id,travel_time
 32,43,26
 32,44,13
 32,45,11
-32,46,11
+32,46,10
 32,47,11
 32,48,15
 32,49,16
 32,50,20
 32,51,29
-32,52,17
+32,52,16
 32,53,16
-32,54,18
+32,54,17
 32,55,18
 32,56,19
 32,57,21
-32,58,24
+32,58,23
 32,59,32
 32,60,27
 32,61,23
-32,62,21
+32,62,20
 32,63,21
 32,64,22
 32,65,23
-32,66,25
+32,66,24
 32,67,28
 32,68,27
 32,69,30
@@ -3026,25 +3026,25 @@ from_id,to_id,travel_time
 32,80,29
 32,81,32
 32,82,33
-32,83,33
+32,83,32
 32,84,35
 32,85,41
 32,86,40
 32,87,36
 32,88,34
-32,89,38
+32,89,37
 32,90,38
-32,91,40
-33,0,19
+32,91,39
+33,0,18
 33,1,20
 33,2,22
-33,3,27
+33,3,26
 33,4,22
 33,5,16
 33,6,14
 33,7,16
 33,8,19
-33,9,26
+33,9,25
 33,10,31
 33,11,26
 33,12,22
@@ -3063,13 +3063,13 @@ from_id,to_id,travel_time
 33,25,9
 33,26,13
 33,27,33
-33,28,28
+33,28,27
 33,29,23
 33,30,16
-33,31,12
+33,31,11
 33,32,6
 33,33,0
-33,34,33
+33,34,32
 33,35,28
 33,36,23
 33,37,17
@@ -3089,43 +3089,43 @@ from_id,to_id,travel_time
 33,51,35
 33,52,21
 33,53,15
-33,54,17
+33,54,16
 33,55,17
 33,56,18
 33,57,19
 33,58,22
 33,59,36
 33,60,31
-33,61,27
+33,61,26
 33,62,22
 33,63,20
 33,64,21
 33,65,22
-33,66,24
+33,66,23
 33,67,26
 33,68,26
-33,69,34
+33,69,33
 33,70,30
 33,71,25
 33,72,25
 33,73,28
-33,74,28
+33,74,27
 33,75,29
 33,76,30
 33,77,41
-33,78,37
-33,79,35
+33,78,36
+33,79,34
 33,80,31
 33,81,32
 33,82,32
-33,83,32
+33,83,31
 33,84,34
 33,85,44
 33,86,43
 33,87,38
 33,88,35
-33,89,37
-33,90,37
+33,89,36
+33,90,36
 33,91,38
 34,0,42
 34,1,46
@@ -3136,7 +3136,7 @@ from_id,to_id,travel_time
 34,6,40
 34,7,42
 34,8,46
-34,9,53
+34,9,52
 34,10,16
 34,11,17
 34,12,21
@@ -3155,11 +3155,11 @@ from_id,to_id,travel_time
 34,25,37
 34,26,41
 34,27,6
-34,28,9
+34,28,8
 34,29,12
 34,30,17
 34,31,23
-34,32,27
+34,32,26
 34,33,32
 34,34,0
 34,35,5
@@ -3174,8 +3174,8 @@ from_id,to_id,travel_time
 34,44,16
 34,45,22
 34,46,27
-34,47,32
-34,48,39
+34,47,31
+34,48,38
 34,49,41
 34,50,46
 34,51,12
@@ -3186,8 +3186,8 @@ from_id,to_id,travel_time
 34,56,37
 34,57,41
 34,58,46
-34,59,23
-34,60,18
+34,59,22
+34,60,17
 34,61,18
 34,62,20
 34,63,22
@@ -3196,45 +3196,45 @@ from_id,to_id,travel_time
 34,66,37
 34,67,43
 34,68,46
-34,69,25
+34,69,24
 34,70,24
-34,71,27
+34,71,26
 34,72,30
 34,73,33
 34,74,37
 34,75,42
-34,76,47
+34,76,46
 34,77,31
 34,78,28
 34,79,29
 34,80,29
 34,81,35
 34,82,39
-34,83,42
+34,83,41
 34,84,45
 34,85,34
 34,86,33
 34,87,33
 34,88,34
-34,89,41
+34,89,40
 34,90,46
-34,91,48
+34,91,47
 35,0,38
 35,1,42
 35,2,44
-35,3,49
-35,4,29
+35,3,48
+35,4,28
 35,5,31
-35,6,36
+35,6,35
 35,7,38
 35,8,41
-35,9,49
+35,9,48
 35,10,16
 35,11,14
 35,12,17
 35,13,27
 35,14,31
-35,15,34
+35,15,33
 35,16,40
 35,17,44
 35,18,11
@@ -3246,8 +3246,8 @@ from_id,to_id,travel_time
 35,24,27
 35,25,32
 35,26,37
-35,27,7
-35,28,5
+35,27,6
+35,28,4
 35,29,7
 35,30,13
 35,31,19
@@ -3255,7 +3255,7 @@ from_id,to_id,travel_time
 35,33,28
 35,34,5
 35,35,0
-35,36,7
+35,36,6
 35,37,12
 35,38,17
 35,39,23
@@ -3263,7 +3263,7 @@ from_id,to_id,travel_time
 35,41,32
 35,42,59
 35,43,48
-35,44,12
+35,44,11
 35,45,18
 35,46,23
 35,47,27
@@ -3273,7 +3273,7 @@ from_id,to_id,travel_time
 35,51,13
 35,52,15
 35,53,19
-35,54,23
+35,54,22
 35,55,27
 35,56,32
 35,57,37
@@ -3308,13 +3308,13 @@ from_id,to_id,travel_time
 35,86,33
 35,87,32
 35,88,32
-35,89,40
-35,90,45
+35,89,39
+35,90,44
 35,91,46
-36,0,33
+36,0,32
 36,1,37
 36,2,39
-36,3,44
+36,3,43
 36,4,23
 36,5,26
 36,6,30
@@ -3328,16 +3328,16 @@ from_id,to_id,travel_time
 36,14,26
 36,15,28
 36,16,35
-36,17,39
+36,17,38
 36,18,15
-36,19,12
+36,19,11
 36,20,10
 36,21,12
 36,22,14
 36,23,21
 36,24,22
 36,25,27
-36,26,32
+36,26,31
 36,27,12
 36,28,8
 36,29,5
@@ -3346,33 +3346,33 @@ from_id,to_id,travel_time
 36,32,17
 36,33,23
 36,34,11
-36,35,7
+36,35,6
 36,36,0
 36,37,6
 36,38,11
 36,39,17
 36,40,23
-36,41,27
-36,42,54
+36,41,26
+36,42,53
 36,43,43
-36,44,7
+36,44,6
 36,45,13
-36,46,18
+36,46,17
 36,47,22
 36,48,29
 36,49,32
-36,50,37
+36,50,36
 36,51,15
 36,52,11
 36,53,14
-36,54,18
+36,54,17
 36,55,22
 36,56,27
 36,57,32
-36,58,37
+36,58,36
 36,59,20
 36,60,15
-36,61,14
+36,61,13
 36,62,15
 36,63,17
 36,64,20
@@ -3380,9 +3380,9 @@ from_id,to_id,travel_time
 36,66,28
 36,67,36
 36,68,36
-36,69,21
+36,69,20
 36,70,20
-36,71,22
+36,71,21
 36,72,24
 36,73,27
 36,74,31
@@ -3393,21 +3393,21 @@ from_id,to_id,travel_time
 36,79,24
 36,80,24
 36,81,30
-36,82,33
-36,83,36
+36,82,32
+36,83,35
 36,84,38
 36,85,32
 36,86,31
 36,87,28
-36,88,29
-36,89,36
-36,90,40
-36,91,42
+36,88,28
+36,89,35
+36,90,39
+36,91,41
 37,0,29
 37,1,32
 37,2,34
-37,3,39
-37,4,22
+37,3,38
+37,4,21
 37,5,22
 37,6,26
 37,7,28
@@ -3422,8 +3422,8 @@ from_id,to_id,travel_time
 37,16,30
 37,17,34
 37,18,17
-37,19,14
-37,20,12
+37,19,13
+37,20,11
 37,21,11
 37,22,12
 37,23,16
@@ -3431,11 +3431,11 @@ from_id,to_id,travel_time
 37,25,22
 37,26,26
 37,27,17
-37,28,11
+37,28,10
 37,29,7
 37,30,5
-37,31,8
-37,32,11
+37,31,7
+37,32,10
 37,33,17
 37,34,16
 37,35,12
@@ -3446,10 +3446,10 @@ from_id,to_id,travel_time
 37,40,17
 37,41,20
 37,42,47
-37,43,37
+37,43,36
 37,44,5
 37,45,7
-37,46,12
+37,46,11
 37,47,16
 37,48,23
 37,49,26
@@ -3468,13 +3468,13 @@ from_id,to_id,travel_time
 37,62,15
 37,63,17
 37,64,18
-37,65,21
-37,66,23
+37,65,20
+37,66,22
 37,67,30
 37,68,31
 37,69,22
 37,70,20
-37,71,22
+37,71,21
 37,72,23
 37,73,26
 37,74,27
@@ -3493,18 +3493,18 @@ from_id,to_id,travel_time
 37,87,29
 37,88,29
 37,89,35
-37,90,37
-37,91,39
+37,90,36
+37,91,38
 38,0,25
-38,1,28
+38,1,27
 38,2,30
 38,3,34
-38,4,20
+38,4,19
 38,5,18
 38,6,21
 38,7,24
 38,8,27
-38,9,34
+38,9,33
 38,10,25
 38,11,21
 38,12,17
@@ -3537,23 +3537,23 @@ from_id,to_id,travel_time
 38,39,6
 38,40,11
 38,41,16
-38,42,43
+38,42,42
 38,43,32
 38,44,7
 38,45,6
-38,46,7
+38,46,6
 38,47,10
-38,48,18
+38,48,17
 38,49,20
-38,50,26
-38,51,23
+38,50,25
+38,51,22
 38,52,11
 38,53,11
-38,54,13
-38,55,15
+38,54,12
+38,55,14
 38,56,16
 38,57,22
-38,58,27
+38,58,26
 38,59,26
 38,60,21
 38,61,17
@@ -3578,25 +3578,25 @@ from_id,to_id,travel_time
 38,80,24
 38,81,27
 38,82,29
-38,83,30
+38,83,29
 38,84,32
 38,85,35
 38,86,34
 38,87,30
 38,88,29
-38,89,34
-38,90,35
+38,89,33
+38,90,34
 38,91,36
-39,0,24
+39,0,23
 39,1,25
 39,2,27
-39,3,32
+39,3,31
 39,4,24
 39,5,19
 39,6,19
 39,7,21
 39,8,24
-39,9,31
+39,9,30
 39,10,30
 39,11,25
 39,12,22
@@ -3610,7 +3610,7 @@ from_id,to_id,travel_time
 39,20,19
 39,21,15
 39,22,13
-39,23,13
+39,23,12
 39,24,11
 39,25,14
 39,26,18
@@ -3618,7 +3618,7 @@ from_id,to_id,travel_time
 39,28,22
 39,29,18
 39,30,14
-39,31,8
+39,31,7
 39,32,5
 39,33,7
 39,34,27
@@ -3631,15 +3631,15 @@ from_id,to_id,travel_time
 39,41,11
 39,42,38
 39,43,27
-39,44,13
+39,44,12
 39,45,7
-39,46,7
-39,47,9
+39,46,6
+39,47,8
 39,48,13
 39,49,16
 39,50,21
 39,51,28
-39,52,15
+39,52,14
 39,53,12
 39,54,13
 39,55,15
@@ -3652,12 +3652,12 @@ from_id,to_id,travel_time
 39,62,16
 39,63,16
 39,64,18
-39,65,20
+39,65,19
 39,66,21
 39,67,26
 39,68,27
 39,69,28
-39,70,25
+39,70,24
 39,71,22
 39,72,22
 39,73,25
@@ -3667,23 +3667,23 @@ from_id,to_id,travel_time
 39,77,35
 39,78,31
 39,79,29
-39,80,26
+39,80,25
 39,81,28
 39,82,30
-39,83,30
+39,83,29
 39,84,32
 39,85,39
 39,86,37
 39,87,33
 39,88,30
 39,89,34
-39,90,35
+39,90,34
 39,91,36
-40,0,24
-40,1,25
+40,0,23
+40,1,24
 40,2,27
 40,3,31
-40,4,26
+40,4,25
 40,5,20
 40,6,19
 40,7,21
@@ -3706,23 +3706,23 @@ from_id,to_id,travel_time
 40,24,10
 40,25,13
 40,26,18
-40,27,34
-40,28,28
+40,27,33
+40,28,27
 40,29,23
 40,30,18
-40,31,12
-40,32,7
+40,31,11
+40,32,6
 40,33,5
-40,34,33
+40,34,32
 40,35,28
 40,36,23
 40,37,17
 40,38,11
-40,39,7
+40,39,6
 40,40,0
 40,41,4
 40,42,31
-40,43,21
+40,43,20
 40,44,18
 40,45,12
 40,46,7
@@ -3738,7 +3738,7 @@ from_id,to_id,travel_time
 40,56,12
 40,57,15
 40,58,18
-40,59,34
+40,59,33
 40,60,29
 40,61,24
 40,62,20
@@ -3774,13 +3774,13 @@ from_id,to_id,travel_time
 41,0,26
 41,1,27
 41,2,29
-41,3,34
+41,3,33
 41,4,29
 41,5,23
 41,6,22
 41,7,23
 41,8,26
-41,9,33
+41,9,32
 41,10,38
 41,11,33
 41,12,29
@@ -3790,7 +3790,7 @@ from_id,to_id,travel_time
 41,16,25
 41,17,28
 41,18,35
-41,19,32
+41,19,31
 41,20,27
 41,21,22
 41,22,18
@@ -3813,7 +3813,7 @@ from_id,to_id,travel_time
 41,39,11
 41,40,4
 41,41,0
-41,42,29
+41,42,28
 41,43,18
 41,44,22
 41,45,17
@@ -3823,30 +3823,30 @@ from_id,to_id,travel_time
 41,49,8
 41,50,12
 41,51,37
-41,52,24
+41,52,23
 41,53,18
 41,54,12
-41,55,12
+41,55,11
 41,56,11
 41,57,13
 41,58,15
 41,59,38
 41,60,33
-41,61,29
+41,61,28
 41,62,24
 41,63,19
 41,64,17
 41,65,16
-41,66,18
+41,66,17
 41,67,20
 41,68,19
-41,69,35
-41,70,31
-41,71,23
+41,69,34
+41,70,30
+41,71,22
 41,72,20
 41,73,22
 41,74,22
-41,75,23
+41,75,22
 41,76,24
 41,77,42
 41,78,37
@@ -3854,31 +3854,31 @@ from_id,to_id,travel_time
 41,80,31
 41,81,26
 41,82,26
-41,83,26
+41,83,25
 41,84,27
 41,85,45
 41,86,43
 41,87,38
 41,88,35
-41,89,31
+41,89,30
 41,90,30
 41,91,32
 42,0,53
 42,1,54
 42,2,56
-42,3,61
+42,3,60
 42,4,56
 42,5,50
 42,6,49
 42,7,50
 42,8,53
-42,9,60
-42,10,65
+42,9,59
+42,10,64
 42,11,60
 42,12,56
 42,13,46
 42,14,44
-42,15,46
+42,15,45
 42,16,52
 42,17,55
 42,18,62
@@ -3886,11 +3886,11 @@ from_id,to_id,travel_time
 42,20,54
 42,21,49
 42,22,45
-42,23,43
+42,23,42
 42,24,40
 42,25,43
 42,26,47
-42,27,64
+42,27,63
 42,28,58
 42,29,54
 42,30,48
@@ -3904,26 +3904,26 @@ from_id,to_id,travel_time
 42,38,42
 42,39,38
 42,40,31
-42,41,29
+42,41,28
 42,42,0
 42,43,10
 42,44,49
 42,45,43
 42,46,37
 42,47,32
-42,48,28
-42,49,23
+42,48,27
+42,49,22
 42,50,17
 42,51,63
-42,52,50
+42,52,49
 42,53,44
 42,54,38
 42,55,34
 42,56,29
 42,57,26
 42,58,23
-42,59,63
-42,60,59
+42,59,62
+42,60,58
 42,61,55
 42,62,50
 42,63,44
@@ -3934,7 +3934,7 @@ from_id,to_id,travel_time
 42,68,26
 42,69,59
 42,70,55
-42,71,47
+42,71,46
 42,72,42
 42,73,40
 42,74,36
@@ -3946,30 +3946,30 @@ from_id,to_id,travel_time
 42,80,55
 42,81,47
 42,82,42
-42,83,40
+42,83,39
 42,84,36
 42,85,70
 42,86,67
 42,87,62
-42,88,59
-42,89,46
+42,88,58
+42,89,45
 42,90,43
-42,91,41
-43,0,43
-43,1,44
+42,91,40
+43,0,42
+43,1,43
 43,2,46
 43,3,50
-43,4,46
+43,4,45
 43,5,39
 43,6,38
 43,7,40
 43,8,43
-43,9,50
+43,9,49
 43,10,54
 43,11,49
 43,12,45
 43,13,36
-43,14,34
+43,14,33
 43,15,35
 43,16,41
 43,17,44
@@ -3983,7 +3983,7 @@ from_id,to_id,travel_time
 43,25,32
 43,26,37
 43,27,53
-43,28,48
+43,28,47
 43,29,43
 43,30,37
 43,31,31
@@ -3997,15 +3997,15 @@ from_id,to_id,travel_time
 43,39,27
 43,40,20
 43,41,18
-43,42,11
+43,42,10
 43,43,0
 43,44,38
 43,45,32
-43,46,27
+43,46,26
 43,47,21
 43,48,17
 43,49,12
-43,50,7
+43,50,6
 43,51,52
 43,52,39
 43,53,33
@@ -4021,42 +4021,42 @@ from_id,to_id,travel_time
 43,63,34
 43,64,30
 43,65,25
-43,66,23
+43,66,22
 43,67,19
 43,68,16
-43,69,49
-43,70,45
+43,69,48
+43,70,44
 43,71,36
 43,72,31
 43,73,29
 43,74,25
 43,75,21
-43,76,20
+43,76,19
 43,77,56
-43,78,52
+43,78,51
 43,79,48
 43,80,45
 43,81,36
 43,82,32
 43,83,29
 43,84,26
-43,85,60
+43,85,59
 43,86,56
 43,87,52
 43,88,48
-43,89,35
+43,89,34
 43,90,32
 43,91,30
 44,0,32
-44,1,35
+44,1,34
 44,2,37
-44,3,42
-44,4,27
+44,3,41
+44,4,26
 44,5,25
 44,6,28
 44,7,31
 44,8,34
-44,9,41
+44,9,40
 44,10,26
 44,11,22
 44,12,20
@@ -4074,26 +4074,26 @@ from_id,to_id,travel_time
 44,24,20
 44,25,25
 44,26,29
-44,27,18
-44,28,15
+44,27,17
+44,28,14
 44,29,11
 44,30,11
 44,31,12
-44,32,14
+44,32,13
 44,33,19
 44,34,16
-44,35,12
-44,36,7
+44,35,11
+44,36,6
 44,37,5
 44,38,7
-44,39,13
+44,39,12
 44,40,18
-44,41,23
+44,41,22
 44,42,49
 44,43,38
 44,44,0
 44,45,6
-44,46,13
+44,46,12
 44,47,17
 44,48,24
 44,49,27
@@ -4101,7 +4101,7 @@ from_id,to_id,travel_time
 44,51,16
 44,52,6
 44,53,7
-44,54,11
+44,54,10
 44,55,16
 44,56,21
 44,57,25
@@ -4117,7 +4117,7 @@ from_id,to_id,travel_time
 44,67,29
 44,68,30
 44,69,17
-44,70,16
+44,70,15
 44,71,17
 44,72,17
 44,73,20
@@ -4130,44 +4130,44 @@ from_id,to_id,travel_time
 44,80,19
 44,81,23
 44,82,26
-44,83,29
+44,83,28
 44,84,31
 44,85,28
 44,86,27
 44,87,24
 44,88,24
-44,89,30
+44,89,29
 44,90,33
-44,91,35
+44,91,34
 45,0,29
 45,1,31
 45,2,33
-45,3,38
-45,4,26
+45,3,37
+45,4,25
 45,5,23
 45,6,25
 45,7,27
 45,8,30
-45,9,37
-45,10,29
+45,9,36
+45,10,28
 45,11,25
 45,12,22
 45,13,21
 45,14,21
 45,15,22
 45,16,29
-45,17,32
+45,17,31
 45,18,24
 45,19,21
-45,20,19
+45,20,18
 45,21,16
 45,22,16
 45,23,18
 45,24,17
 45,25,20
 45,26,24
-45,27,24
-45,28,18
+45,27,23
+45,28,17
 45,29,14
 45,30,12
 45,31,11
@@ -4182,7 +4182,7 @@ from_id,to_id,travel_time
 45,40,12
 45,41,17
 45,42,43
-45,43,33
+45,43,32
 45,44,6
 45,45,0
 45,46,7
@@ -4196,7 +4196,7 @@ from_id,to_id,travel_time
 45,54,8
 45,55,10
 45,56,16
-45,57,21
+45,57,20
 45,58,25
 45,59,24
 45,60,19
@@ -4205,12 +4205,12 @@ from_id,to_id,travel_time
 45,63,10
 45,64,11
 45,65,14
-45,66,17
+45,66,16
 45,67,24
 45,68,25
 45,69,21
 45,70,18
-45,71,16
+45,71,15
 45,72,16
 45,73,18
 45,74,21
@@ -4228,19 +4228,19 @@ from_id,to_id,travel_time
 45,86,30
 45,87,26
 45,88,24
-45,89,28
-45,90,31
-45,91,33
+45,89,27
+45,90,30
+45,91,32
 46,0,29
 46,1,30
 46,2,32
-46,3,37
+46,3,36
 46,4,26
 46,5,24
-46,6,25
+46,6,24
 46,7,26
 46,8,29
-46,9,36
+46,9,35
 46,10,31
 46,11,27
 46,12,23
@@ -4248,9 +4248,9 @@ from_id,to_id,travel_time
 46,14,20
 46,15,21
 46,16,28
-46,17,31
+46,17,30
 46,18,27
-46,19,24
+46,19,23
 46,20,21
 46,21,17
 46,22,17
@@ -4263,7 +4263,7 @@ from_id,to_id,travel_time
 46,29,18
 46,30,15
 46,31,12
-46,32,11
+46,32,10
 46,33,10
 46,34,27
 46,35,23
@@ -4274,7 +4274,7 @@ from_id,to_id,travel_time
 46,40,7
 46,41,11
 46,42,37
-46,43,27
+46,43,26
 46,44,12
 46,45,7
 46,46,0
@@ -4298,11 +4298,11 @@ from_id,to_id,travel_time
 46,64,13
 46,65,14
 46,66,16
-46,67,21
+46,67,20
 46,68,21
 46,69,26
 46,70,23
-46,71,18
+46,71,17
 46,72,18
 46,73,20
 46,74,20
@@ -4313,18 +4313,18 @@ from_id,to_id,travel_time
 46,79,27
 46,80,24
 46,81,24
-46,82,25
+46,82,24
 46,83,24
-46,84,27
+46,84,26
 46,85,37
 46,86,35
 46,87,31
 46,88,27
-46,89,29
+46,89,28
 46,90,29
 46,91,31
-47,0,29
-47,1,30
+47,0,28
+47,1,29
 47,2,32
 47,3,36
 47,4,30
@@ -4332,7 +4332,7 @@ from_id,to_id,travel_time
 47,6,24
 47,7,26
 47,8,29
-47,9,36
+47,9,35
 47,10,36
 47,11,31
 47,12,27
@@ -4350,9 +4350,9 @@ from_id,to_id,travel_time
 47,24,15
 47,25,18
 47,26,23
-47,27,33
+47,27,32
 47,28,26
-47,29,23
+47,29,22
 47,30,19
 47,31,14
 47,32,11
@@ -4362,7 +4362,7 @@ from_id,to_id,travel_time
 47,36,22
 47,37,16
 47,38,10
-47,39,9
+47,39,8
 47,40,5
 47,41,6
 47,42,32
@@ -4375,48 +4375,48 @@ from_id,to_id,travel_time
 47,49,10
 47,50,15
 47,51,31
-47,52,18
+47,52,17
 47,53,12
 47,54,6
-47,55,8
+47,55,7
 47,56,8
 47,57,13
 47,58,16
 47,59,32
 47,60,27
-47,61,23
+47,61,22
 47,62,18
 47,63,13
-47,64,12
+47,64,11
 47,65,12
-47,66,14
+47,66,13
 47,67,18
 47,68,19
-47,69,29
-47,70,25
-47,71,17
+47,69,28
+47,70,24
+47,71,16
 47,72,16
 47,73,18
 47,74,18
 47,75,21
-47,76,24
+47,76,23
 47,77,36
-47,78,32
+47,78,31
 47,79,29
 47,80,25
 47,81,22
-47,82,23
-47,83,22
+47,82,22
+47,83,21
 47,84,24
-47,85,40
+47,85,39
 47,86,37
 47,87,32
 47,88,29
-47,89,27
+47,89,26
 47,90,27
-47,91,29
-48,0,32
-48,1,33
+47,91,28
+48,0,31
+48,1,32
 48,2,35
 48,3,39
 48,4,34
@@ -4434,22 +4434,22 @@ from_id,to_id,travel_time
 48,16,30
 48,17,33
 48,18,38
-48,19,35
+48,19,34
 48,20,30
 48,21,26
 48,22,24
 48,23,21
 48,24,18
 48,25,21
-48,26,26
-48,27,40
+48,26,25
+48,27,39
 48,28,33
 48,29,30
-48,30,25
+48,30,24
 48,31,18
 48,32,15
 48,33,13
-48,34,39
+48,34,38
 48,35,34
 48,36,29
 48,37,23
@@ -4457,19 +4457,19 @@ from_id,to_id,travel_time
 48,39,13
 48,40,9
 48,41,7
-48,42,28
+48,42,27
 48,43,17
 48,44,24
 48,45,18
-48,46,13
+48,46,12
 48,47,7
 48,48,0
 48,49,5
 48,50,11
 48,51,38
-48,52,25
+48,52,24
 48,53,19
-48,54,14
+48,54,13
 48,55,8
 48,56,5
 48,57,8
@@ -4484,8 +4484,8 @@ from_id,to_id,travel_time
 48,66,11
 48,67,14
 48,68,15
-48,69,34
-48,70,30
+48,69,33
+48,70,29
 48,71,21
 48,72,16
 48,73,16
@@ -4493,30 +4493,30 @@ from_id,to_id,travel_time
 48,75,16
 48,76,19
 48,77,41
-48,78,37
+48,78,36
 48,79,33
 48,80,30
 48,81,22
 48,82,20
-48,83,20
+48,83,19
 48,84,21
 48,85,44
 48,86,41
 48,87,37
 48,88,33
-48,89,25
+48,89,24
 48,90,24
-48,91,26
-49,0,33
-49,1,34
+48,91,25
+49,0,32
+49,1,33
 49,2,36
 49,3,40
-49,4,36
+49,4,35
 49,5,29
 49,6,28
 49,7,30
 49,8,33
-49,9,40
+49,9,39
 49,10,44
 49,11,39
 49,12,35
@@ -4534,7 +4534,7 @@ from_id,to_id,travel_time
 49,24,19
 49,25,22
 49,26,27
-49,27,43
+49,27,42
 49,28,36
 49,29,32
 49,30,27
@@ -4549,7 +4549,7 @@ from_id,to_id,travel_time
 49,39,16
 49,40,10
 49,41,8
-49,42,23
+49,42,22
 49,43,12
 49,44,27
 49,45,21
@@ -4559,19 +4559,19 @@ from_id,to_id,travel_time
 49,49,0
 49,50,6
 49,51,41
-49,52,28
+49,52,27
 49,53,22
 49,54,16
 49,55,12
 49,56,7
 49,57,5
 49,58,8
-49,59,41
+49,59,40
 49,60,37
-49,61,33
+49,61,32
 49,62,28
 49,63,23
-49,64,19
+49,64,18
 49,65,13
 49,66,11
 49,67,12
@@ -4582,7 +4582,7 @@ from_id,to_id,travel_time
 49,72,20
 49,73,18
 49,74,15
-49,75,15
+49,75,14
 49,76,17
 49,77,45
 49,78,40
@@ -4590,23 +4590,23 @@ from_id,to_id,travel_time
 49,80,34
 49,81,25
 49,82,21
-49,83,19
+49,83,18
 49,84,19
 49,85,48
 49,86,45
 49,87,41
 49,88,37
-49,89,25
+49,89,24
 49,90,22
 49,91,24
 50,0,36
 50,1,37
 50,2,40
-50,3,44
-50,4,40
+50,3,43
+50,4,39
 50,5,33
 50,6,32
-50,7,34
+50,7,33
 50,8,37
 50,9,43
 50,10,48
@@ -4618,7 +4618,7 @@ from_id,to_id,travel_time
 50,16,35
 50,17,38
 50,18,45
-50,19,42
+50,19,41
 50,20,37
 50,21,33
 50,22,29
@@ -4627,7 +4627,7 @@ from_id,to_id,travel_time
 50,25,26
 50,26,30
 50,27,47
-50,28,42
+50,28,41
 50,29,37
 50,30,31
 50,31,25
@@ -4635,17 +4635,17 @@ from_id,to_id,travel_time
 50,33,18
 50,34,46
 50,35,42
-50,36,37
+50,36,36
 50,37,30
 50,38,25
 50,39,21
 50,40,14
 50,41,12
 50,42,17
-50,43,7
+50,43,6
 50,44,32
 50,45,26
-50,46,21
+50,46,20
 50,47,15
 50,48,11
 50,49,6
@@ -4653,7 +4653,7 @@ from_id,to_id,travel_time
 50,51,46
 50,52,33
 50,53,27
-50,54,22
+50,54,21
 50,55,18
 50,56,12
 50,57,9
@@ -4663,9 +4663,9 @@ from_id,to_id,travel_time
 50,61,38
 50,62,34
 50,63,28
-50,64,24
+50,64,23
 50,65,18
-50,66,17
+50,66,16
 50,67,12
 50,68,9
 50,69,42
@@ -4682,16 +4682,16 @@ from_id,to_id,travel_time
 50,80,39
 50,81,30
 50,82,25
-50,83,23
+50,83,22
 50,84,19
 50,85,53
 50,86,50
 50,87,46
 50,88,42
-50,89,29
+50,89,28
 50,90,26
 50,91,24
-51,0,46
+51,0,45
 51,1,49
 51,2,52
 51,3,56
@@ -4700,7 +4700,7 @@ from_id,to_id,travel_time
 51,6,43
 51,7,45
 51,8,49
-51,9,56
+51,9,55
 51,10,26
 51,11,27
 51,12,28
@@ -4710,8 +4710,8 @@ from_id,to_id,travel_time
 51,16,47
 51,17,51
 51,18,20
-51,19,23
-51,20,25
+51,19,22
+51,20,24
 51,21,26
 51,22,27
 51,23,34
@@ -4719,7 +4719,7 @@ from_id,to_id,travel_time
 51,25,39
 51,26,44
 51,27,16
-51,28,18
+51,28,17
 51,29,19
 51,30,20
 51,31,27
@@ -4734,7 +4734,7 @@ from_id,to_id,travel_time
 51,40,33
 51,41,37
 51,42,63
-51,43,53
+51,43,52
 51,44,16
 51,45,22
 51,46,28
@@ -4744,7 +4744,7 @@ from_id,to_id,travel_time
 51,50,46
 51,51,0
 51,52,13
-51,53,20
+51,53,19
 51,54,25
 51,55,30
 51,56,35
@@ -4773,26 +4773,26 @@ from_id,to_id,travel_time
 51,79,22
 51,80,24
 51,81,31
-51,82,36
-51,83,39
+51,82,35
+51,83,38
 51,84,42
 51,85,27
 51,86,26
 51,87,26
 51,88,28
-51,89,38
-51,90,43
-51,91,45
+51,89,37
+51,90,42
+51,91,44
 52,0,35
-52,1,38
+52,1,37
 52,2,40
-52,3,45
+52,3,44
 52,4,31
 52,5,29
 52,6,31
 52,7,34
 52,8,37
-52,9,44
+52,9,43
 52,10,29
 52,11,26
 52,12,24
@@ -4811,34 +4811,34 @@ from_id,to_id,travel_time
 52,25,27
 52,26,31
 52,27,19
-52,28,18
+52,28,17
 52,29,15
 52,30,16
 52,31,17
-52,32,17
+52,32,16
 52,33,21
 52,34,16
 52,35,15
 52,36,11
 52,37,11
 52,38,11
-52,39,15
+52,39,14
 52,40,19
-52,41,24
-52,42,50
+52,41,23
+52,42,49
 52,43,39
 52,44,6
 52,45,8
 52,46,14
-52,47,18
-52,48,25
+52,47,17
+52,48,24
 52,49,27
 52,50,33
 52,51,13
 52,52,0
 52,53,6
 52,54,11
-52,55,17
+52,55,16
 52,56,21
 52,57,26
 52,58,30
@@ -4854,36 +4854,36 @@ from_id,to_id,travel_time
 52,68,30
 52,69,14
 52,70,11
-52,71,11
+52,71,10
 52,72,14
 52,73,17
 52,74,21
 52,75,26
-52,76,31
+52,76,30
 52,77,21
 52,78,17
 52,79,15
 52,80,13
 52,81,19
 52,82,23
-52,83,26
+52,83,25
 52,84,29
 52,85,25
 52,86,23
 52,87,19
 52,88,18
-52,89,25
-52,90,30
-52,91,32
+52,89,24
+52,90,29
+52,91,31
 53,0,34
 53,1,35
 53,2,38
-53,3,42
-53,4,31
+53,3,41
+53,4,30
 53,5,28
 53,6,30
 53,7,31
-53,8,35
+53,8,34
 53,9,41
 53,10,33
 53,11,29
@@ -4895,15 +4895,15 @@ from_id,to_id,travel_time
 53,17,36
 53,18,28
 53,19,25
-53,20,24
+53,20,23
 53,21,21
 53,22,21
 53,23,23
 53,24,21
 53,25,24
 53,26,28
-53,27,25
-53,28,22
+53,27,24
+53,28,21
 53,29,18
 53,30,17
 53,31,16
@@ -4929,12 +4929,12 @@ from_id,to_id,travel_time
 53,51,19
 53,52,6
 53,53,0
-53,54,6
+53,54,5
 53,55,11
 53,56,16
 53,57,20
 53,58,25
-53,59,21
+53,59,20
 53,60,16
 53,61,11
 53,62,7
@@ -4952,7 +4952,7 @@ from_id,to_id,travel_time
 53,74,18
 53,75,23
 53,76,27
-53,77,26
+53,77,25
 53,78,21
 53,79,19
 53,80,16
@@ -4970,23 +4970,23 @@ from_id,to_id,travel_time
 54,0,35
 54,1,36
 54,2,38
-54,3,43
-54,4,33
+54,3,42
+54,4,32
 54,5,30
 54,6,30
 54,7,32
 54,8,35
-54,9,42
+54,9,41
 54,10,35
 54,11,31
 54,12,28
 54,13,27
 54,14,26
 54,15,27
-54,16,34
+54,16,33
 54,17,36
 54,18,31
-54,19,28
+54,19,27
 54,20,25
 54,21,23
 54,22,23
@@ -4995,27 +4995,27 @@ from_id,to_id,travel_time
 54,25,25
 54,26,29
 54,27,28
-54,28,25
+54,28,24
 54,29,21
 54,30,19
 54,31,18
-54,32,18
+54,32,17
 54,33,16
 54,34,27
-54,35,23
+54,35,22
 54,36,17
 54,37,14
 54,38,12
 54,39,13
 54,40,11
 54,41,12
-54,42,39
+54,42,38
 54,43,28
-54,44,11
+54,44,10
 54,45,8
 54,46,8
 54,47,6
-54,48,14
+54,48,13
 54,49,16
 54,50,21
 54,51,25
@@ -5026,20 +5026,20 @@ from_id,to_id,travel_time
 54,56,11
 54,57,15
 54,58,20
-54,59,26
+54,59,25
 54,60,21
 54,61,16
 54,62,12
 54,63,6
 54,64,5
 54,65,6
-54,66,12
+54,66,11
 54,67,19
 54,68,20
 54,69,22
 54,70,18
 54,71,10
-54,72,10
+54,72,9
 54,73,12
 54,74,15
 54,75,20
@@ -5050,25 +5050,25 @@ from_id,to_id,travel_time
 54,80,19
 54,81,16
 54,82,18
-54,83,20
+54,83,19
 54,84,22
 54,85,33
 54,86,30
 54,87,26
 54,88,22
-54,89,22
+54,89,21
 54,90,24
 54,91,26
 55,0,36
-55,1,37
+55,1,36
 55,2,39
-55,3,44
-55,4,35
+55,3,43
+55,4,34
 55,5,32
 55,6,31
 55,7,33
 55,8,36
-55,9,43
+55,9,42
 55,10,37
 55,11,33
 55,12,30
@@ -5078,20 +5078,20 @@ from_id,to_id,travel_time
 55,16,34
 55,17,37
 55,18,33
-55,19,30
+55,19,29
 55,20,27
 55,21,25
 55,22,25
 55,23,25
 55,24,23
-55,25,26
+55,25,25
 55,26,30
 55,27,33
-55,28,27
+55,28,26
 55,29,23
 55,30,21
 55,31,20
-55,32,19
+55,32,18
 55,33,17
 55,34,32
 55,35,27
@@ -5100,18 +5100,18 @@ from_id,to_id,travel_time
 55,38,14
 55,39,15
 55,40,12
-55,41,12
-55,42,35
+55,41,11
+55,42,34
 55,43,24
 55,44,16
 55,45,10
 55,46,10
-55,47,8
+55,47,7
 55,48,8
 55,49,12
 55,50,18
 55,51,30
-55,52,17
+55,52,16
 55,53,11
 55,54,5
 55,55,0
@@ -5128,39 +5128,39 @@ from_id,to_id,travel_time
 55,66,7
 55,67,15
 55,68,15
-55,69,27
-55,70,23
-55,71,15
+55,69,26
+55,70,22
+55,71,14
 55,72,10
 55,73,11
 55,74,12
 55,75,17
 55,76,20
 55,77,34
-55,78,30
+55,78,29
 55,79,27
 55,80,23
 55,81,16
-55,82,16
-55,83,17
+55,82,15
+55,83,16
 55,84,19
 55,85,37
 55,86,35
 55,87,30
 55,88,27
-55,89,20
+55,89,19
 55,90,21
 55,91,23
 56,0,36
 56,1,37
 56,2,39
-56,3,44
-56,4,36
+56,3,43
+56,4,35
 56,5,32
-56,6,32
+56,6,31
 56,7,33
 56,8,36
-56,9,43
+56,9,42
 56,10,41
 56,11,37
 56,12,33
@@ -5168,7 +5168,7 @@ from_id,to_id,travel_time
 56,14,27
 56,15,28
 56,16,35
-56,17,38
+56,17,37
 56,18,36
 56,19,33
 56,20,31
@@ -5179,7 +5179,7 @@ from_id,to_id,travel_time
 56,25,26
 56,26,30
 56,27,38
-56,28,32
+56,28,31
 56,29,28
 56,30,25
 56,31,21
@@ -5192,25 +5192,25 @@ from_id,to_id,travel_time
 56,38,16
 56,39,16
 56,40,12
-56,41,12
+56,41,11
 56,42,29
-56,43,19
+56,43,18
 56,44,21
 56,45,16
-56,46,11
+56,46,10
 56,47,8
 56,48,5
 56,49,7
 56,50,12
 56,51,35
-56,52,22
+56,52,21
 56,53,16
 56,54,11
 56,55,6
 56,56,0
 56,57,6
 56,58,11
-56,59,34
+56,59,33
 56,60,30
 56,61,26
 56,62,22
@@ -5222,12 +5222,12 @@ from_id,to_id,travel_time
 56,68,11
 56,69,30
 56,70,26
-56,71,18
+56,71,17
 56,72,13
 56,73,11
 56,74,10
 56,75,13
-56,76,16
+56,76,15
 56,77,38
 56,78,33
 56,79,30
@@ -5240,14 +5240,14 @@ from_id,to_id,travel_time
 56,86,38
 56,87,33
 56,88,30
-56,89,20
+56,89,19
 56,90,19
 56,91,21
-57,0,38
-57,1,39
+57,0,37
+57,1,38
 57,2,41
 57,3,45
-57,4,41
+57,4,40
 57,5,34
 57,6,33
 57,7,35
@@ -5263,14 +5263,14 @@ from_id,to_id,travel_time
 57,17,39
 57,18,43
 57,19,39
-57,20,37
+57,20,36
 57,21,32
 57,22,30
 57,23,27
 57,24,24
 57,25,27
-57,26,32
-57,27,43
+57,26,31
+57,27,42
 57,28,37
 57,29,33
 57,30,31
@@ -5288,7 +5288,7 @@ from_id,to_id,travel_time
 57,42,26
 57,43,15
 57,44,25
-57,45,21
+57,45,20
 57,46,17
 57,47,13
 57,48,8
@@ -5297,7 +5297,7 @@ from_id,to_id,travel_time
 57,51,39
 57,52,26
 57,53,20
-57,54,16
+57,54,15
 57,55,11
 57,56,6
 57,57,0
@@ -5312,13 +5312,13 @@ from_id,to_id,travel_time
 57,66,8
 57,67,7
 57,68,7
-57,69,35
-57,70,31
+57,69,34
+57,70,30
 57,71,22
 57,72,17
 57,73,15
 57,74,11
-57,75,10
+57,75,9
 57,76,12
 57,77,42
 57,78,38
@@ -5326,25 +5326,25 @@ from_id,to_id,travel_time
 57,80,31
 57,81,22
 57,82,17
-57,83,15
+57,83,14
 57,84,15
-57,85,46
+57,85,45
 57,86,42
 57,87,38
 57,88,34
-57,89,21
+57,89,20
 57,90,18
-57,91,20
+57,91,19
 58,0,40
 58,1,41
 58,2,43
-58,3,48
-58,4,43
+58,3,47
+58,4,42
 58,5,37
-58,6,36
+58,6,35
 58,7,37
 58,8,40
-58,9,47
+58,9,46
 58,10,50
 58,11,45
 58,12,42
@@ -5352,50 +5352,50 @@ from_id,to_id,travel_time
 58,14,31
 58,15,32
 58,16,39
-58,17,42
+58,17,41
 58,18,47
 58,19,44
-58,20,40
+58,20,39
 58,21,35
 58,22,32
 58,23,29
 58,24,27
 58,25,30
 58,26,34
-58,27,48
-58,28,42
+58,27,47
+58,28,41
 58,29,38
 58,30,34
 58,31,28
-58,32,24
+58,32,23
 58,33,22
 58,34,46
 58,35,42
-58,36,37
+58,36,36
 58,37,31
 58,38,26
 58,39,22
 58,40,18
-58,41,16
+58,41,15
 58,42,23
 58,43,12
 58,44,30
 58,45,25
 58,46,21
 58,47,16
-58,48,12
+58,48,11
 58,49,8
 58,50,6
 58,51,44
 58,52,30
 58,53,25
 58,54,20
-58,55,16
+58,55,15
 58,56,11
 58,57,5
 58,58,0
 58,59,42
-58,60,39
+58,60,38
 58,61,35
 58,62,31
 58,63,24
@@ -5406,29 +5406,29 @@ from_id,to_id,travel_time
 58,68,6
 58,69,39
 58,70,35
-58,71,27
+58,71,26
 58,72,22
 58,73,18
-58,74,14
+58,74,13
 58,75,10
-58,76,11
+58,76,10
 58,77,47
 58,78,42
 58,79,39
 58,80,35
 58,81,25
 58,82,20
-58,83,18
+58,83,17
 58,84,15
 58,85,50
 58,86,46
 58,87,42
 58,88,38
-58,89,24
-58,90,21
+58,89,23
+58,90,20
 58,91,20
 59,0,51
-59,1,54
+59,1,53
 59,2,56
 59,3,60
 59,4,42
@@ -5436,7 +5436,7 @@ from_id,to_id,travel_time
 59,6,47
 59,7,50
 59,8,53
-59,9,60
+59,9,59
 59,10,35
 59,11,35
 59,12,34
@@ -5446,7 +5446,7 @@ from_id,to_id,travel_time
 59,16,52
 59,17,55
 59,18,30
-59,19,32
+59,19,31
 59,20,30
 59,21,31
 59,22,33
@@ -5454,14 +5454,14 @@ from_id,to_id,travel_time
 59,24,39
 59,25,43
 59,26,47
-59,27,26
-59,28,27
+59,27,25
+59,28,26
 59,29,25
 59,30,26
 59,31,31
 59,32,32
 59,33,36
-59,34,23
+59,34,22
 59,35,22
 59,36,20
 59,37,23
@@ -5469,25 +5469,25 @@ from_id,to_id,travel_time
 59,39,30
 59,40,33
 59,41,38
-59,42,63
+59,42,62
 59,43,52
 59,44,19
 59,45,24
 59,46,29
 59,47,32
 59,48,37
-59,49,41
+59,49,40
 59,50,46
 59,51,16
 59,52,16
 59,53,20
-59,54,26
+59,54,25
 59,55,30
 59,56,33
 59,57,38
 59,58,42
 59,59,0
-59,60,5
+59,60,4
 59,61,9
 59,62,14
 59,63,19
@@ -5499,7 +5499,7 @@ from_id,to_id,travel_time
 59,69,8
 59,70,12
 59,71,19
-59,72,24
+59,72,23
 59,73,28
 59,74,32
 59,75,37
@@ -5510,7 +5510,7 @@ from_id,to_id,travel_time
 59,80,19
 59,81,27
 59,82,32
-59,83,35
+59,83,34
 59,84,39
 59,85,16
 59,86,17
@@ -5518,9 +5518,9 @@ from_id,to_id,travel_time
 59,88,23
 59,89,33
 59,90,39
-59,91,41
+59,91,40
 60,0,46
-60,1,49
+60,1,48
 60,2,51
 60,3,55
 60,4,37
@@ -5528,7 +5528,7 @@ from_id,to_id,travel_time
 60,6,42
 60,7,45
 60,8,48
-60,9,55
+60,9,54
 60,10,30
 60,11,30
 60,12,29
@@ -5538,22 +5538,22 @@ from_id,to_id,travel_time
 60,16,47
 60,17,50
 60,18,25
-60,19,27
+60,19,26
 60,20,25
 60,21,26
 60,22,28
 60,23,33
 60,24,34
 60,25,38
-60,26,43
-60,27,21
-60,28,22
+60,26,42
+60,27,20
+60,28,21
 60,29,20
 60,30,21
 60,31,26
 60,32,27
 60,33,31
-60,34,18
+60,34,17
 60,35,17
 60,36,15
 60,37,18
@@ -5561,7 +5561,7 @@ from_id,to_id,travel_time
 60,39,25
 60,40,29
 60,41,33
-60,42,59
+60,42,58
 60,43,48
 60,44,14
 60,45,19
@@ -5577,50 +5577,50 @@ from_id,to_id,travel_time
 60,55,26
 60,56,30
 60,57,34
-60,58,39
-60,59,5
+60,58,38
+60,59,4
 60,60,0
-60,61,5
+60,61,4
 60,62,10
 60,63,15
 60,64,19
 60,65,23
-60,66,29
+60,66,28
 60,67,34
 60,68,37
 60,69,7
-60,70,9
+60,70,8
 60,71,15
 60,72,20
 60,73,24
 60,74,28
 60,75,33
-60,76,38
-60,77,14
+60,76,37
+60,77,13
 60,78,10
 60,79,13
 60,80,16
 60,81,23
 60,82,28
-60,83,31
-60,84,36
+60,83,30
+60,84,35
 60,85,17
 60,86,16
 60,87,17
 60,88,20
-60,89,30
+60,89,29
 60,90,35
 60,91,37
 61,0,42
-61,1,45
+61,1,44
 61,2,47
 61,3,51
-61,4,36
+61,4,35
 61,5,35
 61,6,38
 61,7,41
 61,8,44
-61,9,51
+61,9,50
 61,10,31
 61,11,29
 61,12,27
@@ -5638,7 +5638,7 @@ from_id,to_id,travel_time
 61,24,30
 61,25,34
 61,26,38
-61,27,22
+61,27,21
 61,28,20
 61,29,18
 61,30,19
@@ -5647,20 +5647,20 @@ from_id,to_id,travel_time
 61,33,26
 61,34,18
 61,35,17
-61,36,14
+61,36,13
 61,37,15
 61,38,17
 61,39,21
 61,40,24
-61,41,29
+61,41,28
 61,42,55
 61,43,44
 61,44,10
 61,45,14
 61,46,19
-61,47,23
+61,47,22
 61,48,29
-61,49,33
+61,49,32
 61,50,38
 61,51,12
 61,52,7
@@ -5677,16 +5677,16 @@ from_id,to_id,travel_time
 61,63,10
 61,64,14
 61,65,19
-61,66,25
+61,66,24
 61,67,30
 61,68,33
 61,69,8
-61,70,7
+61,70,6
 61,71,11
 61,72,16
 61,73,20
 61,74,24
-61,75,30
+61,75,29
 61,76,34
 61,77,16
 61,78,11
@@ -5700,19 +5700,19 @@ from_id,to_id,travel_time
 61,86,18
 61,87,15
 61,88,16
-61,89,26
+61,89,25
 61,90,31
 61,91,33
 62,0,39
-62,1,41
+62,1,40
 62,2,43
 62,3,47
-62,4,36
+62,4,35
 62,5,33
 62,6,35
 62,7,37
 62,8,40
-62,9,47
+62,9,46
 62,10,33
 62,11,30
 62,12,29
@@ -5732,20 +5732,20 @@ from_id,to_id,travel_time
 62,26,34
 62,27,23
 62,28,22
-62,29,20
+62,29,19
 62,30,21
 62,31,21
-62,32,21
+62,32,20
 62,33,22
 62,34,20
 62,35,19
 62,36,15
 62,37,15
 62,38,15
-62,39,17
+62,39,16
 62,40,20
 62,41,24
-62,42,51
+62,42,50
 62,43,40
 62,44,10
 62,45,10
@@ -5755,14 +5755,14 @@ from_id,to_id,travel_time
 62,49,28
 62,50,34
 62,51,16
-62,52,5
+62,52,4
 62,53,7
 62,54,12
 62,55,17
 62,56,22
 62,57,26
 62,58,31
-62,59,15
+62,59,14
 62,60,10
 62,61,5
 62,62,0
@@ -5773,11 +5773,11 @@ from_id,to_id,travel_time
 62,67,26
 62,68,29
 62,69,11
-62,70,8
-62,71,8
+62,70,7
+62,71,7
 62,72,12
 62,73,16
-62,74,21
+62,74,20
 62,75,26
 62,76,30
 62,77,19
@@ -5792,19 +5792,19 @@ from_id,to_id,travel_time
 62,86,20
 62,87,16
 62,88,13
-62,89,22
+62,89,21
 62,90,27
 62,91,29
 63,0,39
 63,1,40
 63,2,42
-63,3,47
-63,4,36
+63,3,46
+63,4,35
 63,5,33
 63,6,35
 63,7,36
 63,8,39
-63,9,46
+63,9,45
 63,10,35
 63,11,32
 63,12,30
@@ -5823,7 +5823,7 @@ from_id,to_id,travel_time
 63,25,29
 63,26,33
 63,27,25
-63,28,24
+63,28,23
 63,29,21
 63,30,22
 63,31,21
@@ -5837,11 +5837,11 @@ from_id,to_id,travel_time
 63,39,16
 63,40,15
 63,41,19
-63,42,45
+63,42,44
 63,43,34
 63,44,12
 63,45,10
-63,46,13
+63,46,12
 63,47,13
 63,48,19
 63,49,23
@@ -5849,7 +5849,7 @@ from_id,to_id,travel_time
 63,51,19
 63,52,6
 63,53,5
-63,54,7
+63,54,6
 63,55,11
 63,56,15
 63,57,20
@@ -5862,10 +5862,10 @@ from_id,to_id,travel_time
 63,64,4
 63,65,9
 63,66,15
-63,67,21
+63,67,20
 63,68,23
 63,69,15
-63,70,12
+63,70,11
 63,71,5
 63,72,8
 63,73,11
@@ -5874,29 +5874,29 @@ from_id,to_id,travel_time
 63,76,24
 63,77,23
 63,78,18
-63,79,16
+63,79,15
 63,80,12
 63,81,13
 63,82,16
-63,83,20
+63,83,19
 63,84,22
 63,85,26
 63,86,24
 63,87,19
-63,88,16
-63,89,19
-63,90,24
+63,88,15
+63,89,18
+63,90,23
 63,91,25
 64,0,40
 64,1,41
 64,2,44
-64,3,48
-64,4,37
+64,3,47
+64,4,36
 64,5,34
 64,6,36
 64,7,37
-64,8,41
-64,9,47
+64,8,40
+64,9,46
 64,10,39
 64,11,35
 64,12,33
@@ -5907,7 +5907,7 @@ from_id,to_id,travel_time
 64,17,42
 64,18,33
 64,19,31
-64,20,30
+64,20,29
 64,21,27
 64,22,27
 64,23,29
@@ -5915,7 +5915,7 @@ from_id,to_id,travel_time
 64,25,30
 64,26,34
 64,27,29
-64,28,28
+64,28,27
 64,29,24
 64,30,23
 64,31,22
@@ -5929,15 +5929,15 @@ from_id,to_id,travel_time
 64,39,18
 64,40,16
 64,41,17
-64,42,41
+64,42,40
 64,43,30
 64,44,13
 64,45,11
-64,46,14
-64,47,12
+64,46,13
+64,47,11
 64,48,15
-64,49,19
-64,50,24
+64,49,18
+64,50,23
 64,51,23
 64,52,10
 64,53,6
@@ -5948,47 +5948,47 @@ from_id,to_id,travel_time
 64,58,20
 64,59,23
 64,60,19
-64,61,15
+64,61,14
 64,62,10
 64,63,4
 64,64,0
 64,65,5
 64,66,11
-64,67,17
+64,67,16
 64,68,19
-64,69,20
-64,70,16
-64,71,8
+64,69,19
+64,70,15
+64,71,7
 64,72,4
 64,73,7
 64,74,11
 64,75,16
 64,76,20
 64,77,27
-64,78,23
+64,78,22
 64,79,20
 64,80,16
 64,81,10
 64,82,12
-64,83,16
-64,84,19
-64,85,31
+64,83,15
+64,84,18
+64,85,30
 64,86,28
 64,87,23
 64,88,20
 64,89,16
-64,90,20
+64,90,19
 64,91,21
 65,0,40
 65,1,41
 65,2,44
-65,3,48
-65,4,39
+65,3,47
+65,4,38
 65,5,36
 65,6,36
 65,7,37
-65,8,41
-65,9,47
+65,8,40
+65,9,46
 65,10,41
 65,11,37
 65,12,35
@@ -5998,11 +5998,11 @@ from_id,to_id,travel_time
 65,16,39
 65,17,42
 65,18,36
-65,19,34
-65,20,32
+65,19,33
+65,20,31
 65,21,29
 65,22,29
-65,23,30
+65,23,29
 65,24,27
 65,25,30
 65,26,34
@@ -6018,10 +6018,10 @@ from_id,to_id,travel_time
 65,36,22
 65,37,20
 65,38,19
-65,39,20
+65,39,19
 65,40,16
 65,41,16
-65,42,36
+65,42,35
 65,43,25
 65,44,15
 65,45,14
@@ -6040,12 +6040,12 @@ from_id,to_id,travel_time
 65,58,15
 65,59,27
 65,60,23
-65,61,20
+65,61,19
 65,62,15
 65,63,9
 65,64,5
 65,65,0
-65,66,7
+65,66,6
 65,67,14
 65,68,15
 65,69,24
@@ -6055,7 +6055,7 @@ from_id,to_id,travel_time
 65,73,6
 65,74,8
 65,75,13
-65,76,18
+65,76,17
 65,77,31
 65,78,27
 65,79,24
@@ -6069,41 +6069,41 @@ from_id,to_id,travel_time
 65,87,27
 65,88,23
 65,89,16
-65,90,18
+65,90,17
 65,91,19
 66,0,42
 66,1,43
 66,2,45
-66,3,50
-66,4,41
+66,3,49
+66,4,40
 66,5,38
 66,6,37
 66,7,39
 66,8,42
-66,9,49
+66,9,48
 66,10,44
 66,11,40
 66,12,37
 66,13,35
 66,14,33
 66,15,34
-66,16,41
+66,16,40
 66,17,43
 66,18,39
 66,19,36
-66,20,34
+66,20,33
 66,21,31
 66,22,31
 66,23,31
 66,24,29
 66,25,32
 66,26,36
-66,27,39
+66,27,38
 66,28,33
 66,29,29
 66,30,27
 66,31,26
-66,32,25
+66,32,24
 66,33,23
 66,34,37
 66,35,33
@@ -6112,33 +6112,33 @@ from_id,to_id,travel_time
 66,38,21
 66,39,21
 66,40,18
-66,41,18
+66,41,17
 66,42,33
 66,43,22
 66,44,21
 66,45,16
 66,46,16
-66,47,14
+66,47,13
 66,48,11
 66,49,11
 66,50,16
 66,51,34
 66,52,21
 66,53,16
-66,54,12
+66,54,11
 66,55,7
 66,56,6
 66,57,8
 66,58,13
 66,59,32
 66,60,28
-66,61,25
+66,61,24
 66,62,21
 66,63,15
 66,64,11
 66,65,6
 66,66,0
-66,67,11
+66,67,10
 66,68,12
 66,69,29
 66,70,25
@@ -6160,24 +6160,24 @@ from_id,to_id,travel_time
 66,86,36
 66,87,32
 66,88,28
-66,89,16
+66,89,15
 66,90,15
 66,91,17
-67,0,45
-67,1,46
+67,0,44
+67,1,45
 67,2,48
 67,3,52
-67,4,46
+67,4,45
 67,5,41
 67,6,40
 67,7,42
 67,8,45
-67,9,52
+67,9,51
 67,10,51
 67,11,47
 67,12,43
 67,13,37
-67,14,36
+67,14,35
 67,15,37
 67,16,43
 67,17,46
@@ -6191,7 +6191,7 @@ from_id,to_id,travel_time
 67,25,34
 67,26,39
 67,27,46
-67,28,41
+67,28,40
 67,29,37
 67,30,35
 67,31,31
@@ -6205,11 +6205,11 @@ from_id,to_id,travel_time
 67,39,26
 67,40,22
 67,41,20
-67,42,30
+67,42,29
 67,43,19
 67,44,29
 67,45,24
-67,46,21
+67,46,20
 67,47,18
 67,48,14
 67,49,12
@@ -6227,44 +6227,44 @@ from_id,to_id,travel_time
 67,61,30
 67,62,26
 67,63,20
-67,64,17
+67,64,16
 67,65,14
-67,66,11
+67,66,10
 67,67,0
 67,68,5
-67,69,35
+67,69,34
 67,70,30
-67,71,22
+67,71,21
 67,72,17
 67,73,13
 67,74,8
 67,75,5
-67,76,8
+67,76,7
 67,77,42
 67,78,37
 67,79,34
 67,80,30
 67,81,20
 67,82,15
-67,83,13
+67,83,12
 67,84,10
 67,85,45
 67,86,41
 67,87,37
 67,88,33
 67,89,18
-67,90,16
+67,90,15
 67,91,15
 68,0,44
 68,1,45
 68,2,47
-68,3,52
-68,4,47
+68,3,51
+68,4,46
 68,5,41
-68,6,40
+68,6,39
 68,7,41
 68,8,44
-68,9,51
+68,9,50
 68,10,52
 68,11,48
 68,12,44
@@ -6272,10 +6272,10 @@ from_id,to_id,travel_time
 68,14,35
 68,15,36
 68,16,43
-68,17,46
+68,17,45
 68,18,47
 68,19,44
-68,20,42
+68,20,41
 68,21,37
 68,22,36
 68,23,33
@@ -6283,21 +6283,21 @@ from_id,to_id,travel_time
 68,25,34
 68,26,38
 68,27,47
-68,28,42
+68,28,41
 68,29,38
 68,30,36
 68,31,32
-68,32,28
+68,32,27
 68,33,26
 68,34,46
-68,35,42
+68,35,41
 68,36,36
 68,37,31
 68,38,26
 68,39,27
 68,40,22
-68,41,20
-68,42,27
+68,41,19
+68,42,26
 68,43,16
 68,44,30
 68,45,25
@@ -6305,23 +6305,23 @@ from_id,to_id,travel_time
 68,47,19
 68,48,15
 68,49,12
-68,50,10
+68,50,9
 68,51,42
 68,52,30
 68,53,25
 68,54,20
-68,55,16
+68,55,15
 68,56,11
 68,57,7
 68,58,6
-68,59,41
+68,59,40
 68,60,37
 68,61,33
 68,62,29
 68,63,23
 68,64,19
 68,65,15
-68,66,13
+68,66,12
 68,67,5
 68,68,0
 68,69,37
@@ -6330,33 +6330,33 @@ from_id,to_id,travel_time
 68,72,20
 68,73,15
 68,74,11
-68,75,8
-68,76,5
+68,75,7
+68,76,4
 68,77,45
 68,78,40
-68,79,37
+68,79,36
 68,80,33
 68,81,23
 68,82,18
-68,83,16
+68,83,15
 68,84,10
 68,85,48
 68,86,44
 68,87,40
 68,88,36
-68,89,20
+68,89,19
 68,90,16
 68,91,14
 69,0,49
-69,1,52
+69,1,51
 69,2,54
 69,3,58
-69,4,43
+69,4,42
 69,5,42
 69,6,46
 69,7,48
 69,8,51
-69,9,58
+69,9,57
 69,10,37
 69,11,36
 69,12,34
@@ -6367,51 +6367,51 @@ from_id,to_id,travel_time
 69,17,52
 69,18,32
 69,19,32
-69,20,31
-69,21,32
+69,20,30
+69,21,31
 69,22,33
 69,23,36
 69,24,37
-69,25,41
+69,25,40
 69,26,45
-69,27,28
+69,27,27
 69,28,27
 69,29,25
 69,30,26
 69,31,29
-69,32,31
+69,32,30
 69,33,33
-69,34,25
+69,34,24
 69,35,24
-69,36,21
+69,36,20
 69,37,22
 69,38,24
 69,39,28
 69,40,31
-69,41,35
+69,41,34
 69,42,59
-69,43,49
+69,43,48
 69,44,17
 69,45,21
 69,46,26
-69,47,29
-69,48,34
+69,47,28
+69,48,33
 69,49,37
 69,50,42
 69,51,18
 69,52,14
 69,53,18
 69,54,22
-69,55,27
+69,55,26
 69,56,30
-69,57,35
+69,57,34
 69,58,39
 69,59,8
 69,60,7
 69,61,8
 69,62,11
 69,63,15
-69,64,20
+69,64,19
 69,65,24
 69,66,29
 69,67,34
@@ -6423,32 +6423,32 @@ from_id,to_id,travel_time
 69,73,23
 69,74,27
 69,75,32
-69,76,37
+69,76,36
 69,77,9
 69,78,4
 69,79,8
 69,80,12
 69,81,20
 69,82,25
-69,83,28
+69,83,27
 69,84,34
 69,85,12
 69,86,11
 69,87,12
 69,88,16
-69,89,26
+69,89,25
 69,90,32
 69,91,34
 70,0,46
 70,1,48
 70,2,51
 70,3,55
-70,4,42
+70,4,41
 70,5,40
 70,6,43
-70,7,45
+70,7,44
 70,8,48
-70,9,55
+70,9,54
 70,10,37
 70,11,35
 70,12,33
@@ -6466,11 +6466,11 @@ from_id,to_id,travel_time
 70,24,34
 70,25,37
 70,26,42
-70,27,28
+70,27,27
 70,28,26
 70,29,24
 70,30,25
-70,31,28
+70,31,27
 70,32,28
 70,33,30
 70,34,24
@@ -6478,32 +6478,32 @@ from_id,to_id,travel_time
 70,36,20
 70,37,20
 70,38,22
-70,39,25
+70,39,24
 70,40,27
-70,41,31
+70,41,30
 70,42,55
 70,43,44
 70,44,15
 70,45,18
 70,46,23
-70,47,25
-70,48,30
+70,47,24
+70,48,29
 70,49,33
 70,50,38
 70,51,18
 70,52,11
 70,53,15
 70,54,18
-70,55,23
+70,55,22
 70,56,26
 70,57,30
 70,58,35
 70,59,12
 70,60,8
-70,61,7
-70,62,8
+70,61,6
+70,62,7
 70,63,11
-70,64,16
+70,64,15
 70,65,20
 70,66,25
 70,67,30
@@ -6514,7 +6514,7 @@ from_id,to_id,travel_time
 70,72,14
 70,73,18
 70,74,22
-70,75,28
+70,75,27
 70,76,32
 70,77,13
 70,78,8
@@ -6522,19 +6522,19 @@ from_id,to_id,travel_time
 70,80,7
 70,81,16
 70,82,20
-70,83,24
+70,83,23
 70,84,29
-70,85,17
+70,85,16
 70,86,12
 70,87,8
 70,88,12
-70,89,22
-70,90,28
+70,89,21
+70,90,27
 70,91,29
 71,0,44
 71,1,45
 71,2,48
-71,3,52
+71,3,51
 71,4,41
 71,5,39
 71,6,40
@@ -6551,48 +6551,48 @@ from_id,to_id,travel_time
 71,17,46
 71,18,34
 71,19,33
-71,20,32
+71,20,31
 71,21,31
 71,22,31
 71,23,33
 71,24,31
 71,25,34
 71,26,38
-71,27,30
+71,27,29
 71,28,28
 71,29,26
 71,30,27
 71,31,26
 71,32,26
 71,33,25
-71,34,27
+71,34,26
 71,35,25
 71,36,21
-71,37,22
+71,37,21
 71,38,21
 71,39,22
 71,40,21
-71,41,23
-71,42,47
+71,41,22
+71,42,46
 71,43,36
 71,44,17
 71,45,15
-71,46,18
-71,47,17
+71,46,17
+71,47,16
 71,48,21
 71,49,25
 71,50,30
 71,51,23
-71,52,11
+71,52,10
 71,53,10
 71,54,10
-71,55,15
+71,55,14
 71,56,17
 71,57,22
 71,58,26
 71,59,19
 71,60,15
-71,61,12
+71,61,11
 71,62,7
 71,63,5
 71,64,7
@@ -6614,35 +6614,35 @@ from_id,to_id,travel_time
 71,80,9
 71,81,8
 71,82,12
-71,83,16
+71,83,15
 71,84,20
 71,85,25
 71,86,21
 71,87,16
 71,88,12
 71,89,14
-71,90,20
+71,90,19
 71,91,21
 72,0,44
 72,1,45
 72,2,47
-72,3,52
+72,3,51
 72,4,41
 72,5,39
 72,6,40
 72,7,41
 72,8,44
-72,9,51
+72,9,50
 72,10,43
 72,11,39
 72,12,37
 72,13,36
 72,14,35
-72,15,37
+72,15,36
 72,16,43
 72,17,46
 72,18,37
-72,19,36
+72,19,35
 72,20,34
 72,21,32
 72,22,32
@@ -6650,7 +6650,7 @@ from_id,to_id,travel_time
 72,24,31
 72,25,34
 72,26,38
-72,27,34
+72,27,33
 72,28,32
 72,29,29
 72,30,28
@@ -6664,25 +6664,25 @@ from_id,to_id,travel_time
 72,38,21
 72,39,22
 72,40,20
-72,41,21
+72,41,20
 72,42,42
 72,43,31
 72,44,17
 72,45,16
 72,46,18
 72,47,16
-72,48,17
+72,48,16
 72,49,20
 72,50,25
 72,51,27
 72,52,14
 72,53,11
-72,54,10
+72,54,9
 72,55,10
 72,56,13
 72,57,17
 72,58,22
-72,59,24
+72,59,23
 72,60,20
 72,61,16
 72,62,12
@@ -6692,31 +6692,31 @@ from_id,to_id,travel_time
 72,66,12
 72,67,17
 72,68,20
-72,69,19
+72,69,18
 72,70,14
 72,71,4
 72,72,0
 72,73,4
 72,74,8
-72,75,14
+72,75,13
 72,76,18
 72,77,26
 72,78,21
 72,79,17
 72,80,13
 72,81,6
-72,82,9
-72,83,12
-72,84,16
+72,82,8
+72,83,11
+72,84,15
 72,85,29
 72,86,24
 72,87,20
 72,88,16
 72,89,12
-72,90,16
+72,90,15
 72,91,17
-73,0,47
-73,1,48
+73,0,46
+73,1,47
 73,2,50
 73,3,54
 73,4,44
@@ -6724,12 +6724,12 @@ from_id,to_id,travel_time
 73,6,42
 73,7,44
 73,8,47
-73,9,54
+73,9,53
 73,10,46
 73,11,42
 73,12,40
 73,13,39
-73,14,38
+73,14,37
 73,15,39
 73,16,45
 73,17,48
@@ -6745,8 +6745,8 @@ from_id,to_id,travel_time
 73,27,36
 73,28,35
 73,29,32
-73,30,31
-73,31,30
+73,30,30
+73,31,29
 73,32,29
 73,33,28
 73,34,33
@@ -6761,7 +6761,7 @@ from_id,to_id,travel_time
 73,43,29
 73,44,20
 73,45,18
-73,46,21
+73,46,20
 73,47,18
 73,48,16
 73,49,18
@@ -6769,7 +6769,7 @@ from_id,to_id,travel_time
 73,51,30
 73,52,17
 73,53,14
-73,54,13
+73,54,12
 73,55,11
 73,56,11
 73,57,15
@@ -6780,13 +6780,13 @@ from_id,to_id,travel_time
 73,62,16
 73,63,11
 73,64,7
-73,65,7
-73,66,8
+73,65,6
+73,66,7
 73,67,13
 73,68,15
 73,69,23
 73,70,18
-73,71,9
+73,71,8
 73,72,4
 73,73,0
 73,74,4
@@ -6798,25 +6798,25 @@ from_id,to_id,travel_time
 73,80,17
 73,81,8
 73,82,7
-73,83,9
+73,83,8
 73,84,11
 73,85,33
 73,86,28
 73,87,24
 73,88,20
-73,89,12
+73,89,11
 73,90,13
 73,91,15
 74,0,46
 74,1,47
 74,2,49
-74,3,54
+74,3,53
 74,4,45
 74,5,42
 74,6,42
 74,7,43
 74,8,46
-74,9,53
+74,9,52
 74,10,49
 74,11,45
 74,12,42
@@ -6827,15 +6827,15 @@ from_id,to_id,travel_time
 74,17,48
 74,18,44
 74,19,41
-74,20,39
+74,20,38
 74,21,36
 74,22,36
 74,23,35
 74,24,33
 74,25,36
 74,26,40
-74,27,41
-74,28,38
+74,27,40
+74,28,37
 74,29,34
 74,30,32
 74,31,31
@@ -6855,7 +6855,7 @@ from_id,to_id,travel_time
 74,45,21
 74,46,20
 74,47,18
-74,48,16
+74,48,15
 74,49,15
 74,50,19
 74,51,34
@@ -6865,11 +6865,11 @@ from_id,to_id,travel_time
 74,55,12
 74,56,10
 74,57,11
-74,58,14
+74,58,13
 74,59,32
 74,60,28
-74,61,25
-74,62,21
+74,61,24
+74,62,20
 74,63,15
 74,64,11
 74,65,8
@@ -6890,7 +6890,7 @@ from_id,to_id,travel_time
 74,80,21
 74,81,11
 74,82,6
-74,83,5
+74,83,4
 74,84,7
 74,85,38
 74,86,33
@@ -6902,32 +6902,32 @@ from_id,to_id,travel_time
 75,0,47
 75,1,48
 75,2,50
-75,3,55
+75,3,54
 75,4,48
 75,5,44
 75,6,43
 75,7,44
 75,8,47
-75,9,54
+75,9,53
 75,10,53
 75,11,49
 75,12,45
 75,13,40
 75,14,38
-75,15,40
+75,15,39
 75,16,46
 75,17,49
 75,18,49
-75,19,46
+75,19,45
 75,20,43
 75,21,39
 75,22,39
-75,23,37
+75,23,36
 75,24,34
 75,25,37
 75,26,41
-75,27,46
-75,28,43
+75,27,45
+75,28,42
 75,29,39
 75,30,37
 75,31,34
@@ -6940,27 +6940,27 @@ from_id,to_id,travel_time
 75,38,28
 75,39,28
 75,40,25
-75,41,23
+75,41,22
 75,42,32
 75,43,21
 75,44,29
 75,45,26
 75,46,23
 75,47,21
-75,48,17
-75,49,15
+75,48,16
+75,49,14
 75,50,15
 75,51,39
-75,52,27
+75,52,26
 75,53,23
 75,54,20
 75,55,17
 75,56,13
-75,57,10
+75,57,9
 75,58,10
 75,59,37
 75,60,33
-75,61,30
+75,61,29
 75,62,26
 75,63,20
 75,64,16
@@ -6968,10 +6968,10 @@ from_id,to_id,travel_time
 75,66,10
 75,67,5
 75,68,7
-75,69,33
-75,70,28
+75,69,32
+75,70,27
 75,71,18
-75,72,14
+75,72,13
 75,73,9
 75,74,5
 75,75,0
@@ -6988,8 +6988,8 @@ from_id,to_id,travel_time
 75,86,38
 75,87,34
 75,88,30
-75,89,14
-75,90,11
+75,89,13
+75,90,10
 75,91,10
 76,0,48
 76,1,49
@@ -6998,7 +6998,7 @@ from_id,to_id,travel_time
 76,4,51
 76,5,45
 76,6,44
-76,7,46
+76,7,45
 76,8,49
 76,9,55
 76,10,56
@@ -7010,22 +7010,22 @@ from_id,to_id,travel_time
 76,16,47
 76,17,50
 76,18,52
-76,19,49
+76,19,48
 76,20,46
 76,21,42
 76,22,41
 76,23,38
 76,24,35
 76,25,38
-76,26,43
-76,27,50
+76,26,42
+76,27,49
 76,28,46
 76,29,42
 76,30,40
-76,31,37
+76,31,36
 76,32,32
 76,33,30
-76,34,47
+76,34,46
 76,35,45
 76,36,40
 76,37,36
@@ -7038,29 +7038,29 @@ from_id,to_id,travel_time
 76,44,33
 76,45,30
 76,46,26
-76,47,24
+76,47,23
 76,48,19
 76,49,17
 76,50,13
 76,51,43
-76,52,31
+76,52,30
 76,53,27
 76,54,24
 76,55,20
-76,56,16
+76,56,15
 76,57,12
-76,58,11
+76,58,10
 76,59,41
-76,60,38
+76,60,37
 76,61,34
 76,62,30
 76,63,24
 76,64,20
 76,65,17
 76,66,14
-76,67,8
+76,67,7
 76,68,4
-76,69,37
+76,69,36
 76,70,32
 76,71,22
 76,72,18
@@ -7070,7 +7070,7 @@ from_id,to_id,travel_time
 76,76,0
 76,77,44
 76,78,39
-76,79,35
+76,79,34
 76,80,31
 76,81,20
 76,82,16
@@ -7080,14 +7080,14 @@ from_id,to_id,travel_time
 76,86,42
 76,87,37
 76,88,33
-76,89,16
-76,90,13
+76,89,15
+76,90,12
 76,91,10
 77,0,57
 77,1,59
 77,2,62
-77,3,66
-77,4,51
+77,3,65
+77,4,50
 77,5,50
 77,6,53
 77,7,55
@@ -7109,16 +7109,16 @@ from_id,to_id,travel_time
 77,23,44
 77,24,45
 77,25,48
-77,26,53
+77,26,52
 77,27,34
 77,28,35
 77,29,33
-77,30,35
+77,30,34
 77,31,37
 77,32,38
 77,33,41
 77,34,31
-77,35,31
+77,35,30
 77,36,29
 77,37,30
 77,38,31
@@ -7135,15 +7135,15 @@ from_id,to_id,travel_time
 77,49,45
 77,50,50
 77,51,24
-77,52,22
-77,53,26
+77,52,21
+77,53,25
 77,54,30
 77,55,34
 77,56,38
 77,57,42
 77,58,47
-77,59,12
-77,60,14
+77,59,11
+77,60,13
 77,61,16
 77,62,19
 77,63,23
@@ -7154,7 +7154,7 @@ from_id,to_id,travel_time
 77,68,45
 77,69,9
 77,70,13
-77,71,22
+77,71,21
 77,72,26
 77,73,30
 77,74,34
@@ -7172,19 +7172,19 @@ from_id,to_id,travel_time
 77,86,9
 77,87,13
 77,88,17
-77,89,29
-77,90,36
-77,91,38
+77,89,28
+77,90,35
+77,91,37
 78,0,52
-78,1,55
+78,1,54
 78,2,57
 78,3,61
-78,4,46
+78,4,45
 78,5,45
 78,6,49
 78,7,51
 78,8,54
-78,9,61
+78,9,60
 78,10,41
 78,11,39
 78,12,38
@@ -7203,11 +7203,11 @@ from_id,to_id,travel_time
 78,25,44
 78,26,48
 78,27,31
-78,28,31
+78,28,30
 78,29,28
 78,30,30
-78,31,33
-78,32,34
+78,31,32
+78,32,33
 78,33,36
 78,34,28
 78,35,27
@@ -7216,36 +7216,36 @@ from_id,to_id,travel_time
 78,38,27
 78,39,31
 78,40,34
-78,41,38
-78,42,63
-78,43,52
+78,41,37
+78,42,62
+78,43,51
 78,44,20
 78,45,24
 78,46,29
-78,47,32
-78,48,37
+78,47,31
+78,48,36
 78,49,40
 78,50,45
 78,51,21
 78,52,17
 78,53,21
 78,54,25
-78,55,30
+78,55,29
 78,56,33
 78,57,38
 78,58,42
-78,59,12
+78,59,11
 78,60,10
 78,61,11
 78,62,14
 78,63,18
-78,64,23
+78,64,22
 78,65,27
 78,66,32
-78,67,38
+78,67,37
 78,68,40
 78,69,4
-78,70,9
+78,70,8
 78,71,17
 78,72,21
 78,73,25
@@ -7257,45 +7257,45 @@ from_id,to_id,travel_time
 78,79,4
 78,80,9
 78,81,18
-78,82,24
+78,82,23
 78,83,27
 78,84,32
 78,85,8
 78,86,7
 78,87,8
 78,88,12
-78,89,24
-78,90,31
-78,91,33
-79,0,51
+78,89,23
+78,90,30
+78,91,32
+79,0,50
 79,1,53
 79,2,55
-79,3,60
-79,4,46
+79,3,59
+79,4,45
 79,5,44
 79,6,47
 79,7,49
 79,8,52
-79,9,59
-79,10,42
+79,9,58
+79,10,41
 79,11,39
 79,12,38
 79,13,42
 79,14,43
 79,15,44
 79,16,51
-79,17,54
+79,17,53
 79,18,36
 79,19,35
 79,20,34
 79,21,35
 79,22,37
-79,23,39
+79,23,38
 79,24,39
 79,25,42
 79,26,46
-79,27,32
-79,28,31
+79,27,31
+79,28,30
 79,29,28
 79,30,30
 79,31,32
@@ -7315,22 +7315,22 @@ from_id,to_id,travel_time
 79,45,22
 79,46,27
 79,47,29
-79,48,34
+79,48,33
 79,49,37
 79,50,42
 79,51,22
 79,52,15
 79,53,19
-79,54,23
+79,54,22
 79,55,27
 79,56,30
 79,57,34
 79,58,39
-79,59,16
+79,59,15
 79,60,13
 79,61,11
 79,62,12
-79,63,16
+79,63,15
 79,64,20
 79,65,24
 79,66,29
@@ -7343,7 +7343,7 @@ from_id,to_id,travel_time
 79,73,21
 79,74,25
 79,75,30
-79,76,35
+79,76,34
 79,77,9
 79,78,4
 79,79,0
@@ -7356,24 +7356,24 @@ from_id,to_id,travel_time
 79,86,8
 79,87,4
 79,88,8
-79,89,20
-79,90,27
-79,91,29
+79,89,19
+79,90,26
+79,91,28
 80,0,48
-80,1,50
+80,1,49
 80,2,52
 80,3,56
-80,4,45
+80,4,44
 80,5,42
 80,6,44
 80,7,46
 80,8,49
-80,9,56
+80,9,55
 80,10,42
 80,11,39
 80,12,38
 80,13,39
-80,14,40
+80,14,39
 80,15,41
 80,16,47
 80,17,50
@@ -7387,21 +7387,21 @@ from_id,to_id,travel_time
 80,25,38
 80,26,43
 80,27,32
-80,28,31
+80,28,30
 80,29,28
 80,30,30
 80,31,30
-80,32,30
+80,32,29
 80,33,31
 80,34,29
 80,35,28
 80,36,24
 80,37,24
 80,38,24
-80,39,26
+80,39,25
 80,40,27
 80,41,31
-80,42,56
+80,42,55
 80,43,45
 80,44,19
 80,45,19
@@ -7429,7 +7429,7 @@ from_id,to_id,travel_time
 80,67,30
 80,68,33
 80,69,12
-80,70,8
+80,70,7
 80,71,9
 80,72,13
 80,73,17
@@ -7442,13 +7442,13 @@ from_id,to_id,travel_time
 80,80,0
 80,81,10
 80,82,16
-80,83,20
+80,83,19
 80,84,25
 80,85,17
 80,86,12
 80,87,8
 80,88,5
-80,89,16
+80,89,15
 80,90,23
 80,91,25
 81,0,50
@@ -7458,7 +7458,7 @@ from_id,to_id,travel_time
 81,4,47
 81,5,45
 81,6,46
-81,7,48
+81,7,47
 81,8,51
 81,9,57
 81,10,48
@@ -7477,25 +7477,25 @@ from_id,to_id,travel_time
 81,23,39
 81,24,37
 81,25,40
-81,26,45
+81,26,44
 81,27,38
-81,28,37
+81,28,36
 81,29,34
 81,30,34
 81,31,33
-81,32,33
+81,32,32
 81,33,32
 81,34,35
-81,35,34
+81,35,33
 81,36,30
 81,37,29
 81,38,27
 81,39,28
 81,40,26
-81,41,27
+81,41,26
 81,42,47
-81,43,37
-81,44,24
+81,43,36
+81,44,23
 81,45,22
 81,46,24
 81,47,22
@@ -7506,7 +7506,7 @@ from_id,to_id,travel_time
 81,52,19
 81,53,17
 81,54,16
-81,55,17
+81,55,16
 81,56,18
 81,57,22
 81,58,25
@@ -7527,24 +7527,24 @@ from_id,to_id,travel_time
 81,73,8
 81,74,11
 81,75,17
-81,76,21
+81,76,20
 81,77,23
 81,78,18
 81,79,14
 81,80,10
 81,81,0
 81,82,5
-81,83,9
+81,83,8
 81,84,14
 81,85,26
 81,86,21
 81,87,17
 81,88,13
-81,89,6
-81,90,13
+81,89,5
+81,90,12
 81,91,14
 82,0,51
-82,1,52
+82,1,51
 82,2,54
 82,3,58
 82,4,49
@@ -7552,8 +7552,8 @@ from_id,to_id,travel_time
 82,6,46
 82,7,48
 82,8,51
-82,9,58
-82,10,52
+82,9,57
+82,10,51
 82,11,48
 82,12,45
 82,13,44
@@ -7570,8 +7570,8 @@ from_id,to_id,travel_time
 82,24,38
 82,25,40
 82,26,45
-82,27,42
-82,28,41
+82,27,41
+82,28,40
 82,29,37
 82,30,36
 82,31,35
@@ -7579,18 +7579,18 @@ from_id,to_id,travel_time
 82,33,32
 82,34,39
 82,35,37
-82,36,33
+82,36,32
 82,37,31
 82,38,29
 82,39,30
 82,40,27
-82,41,27
-82,42,43
+82,41,26
+82,42,42
 82,43,32
 82,44,26
 82,45,24
-82,46,25
-82,47,23
+82,46,24
+82,47,22
 82,48,20
 82,49,21
 82,50,25
@@ -7598,7 +7598,7 @@ from_id,to_id,travel_time
 82,52,23
 82,53,19
 82,54,18
-82,55,16
+82,55,15
 82,56,15
 82,57,17
 82,58,20
@@ -7613,9 +7613,9 @@ from_id,to_id,travel_time
 82,67,15
 82,68,18
 82,69,25
-82,70,21
-82,71,13
-82,72,9
+82,70,20
+82,71,12
+82,72,8
 82,73,7
 82,74,6
 82,75,12
@@ -7632,19 +7632,19 @@ from_id,to_id,travel_time
 82,86,26
 82,87,22
 82,88,18
-82,89,6
+82,89,5
 82,90,8
 82,91,10
 83,0,50
 83,1,51
 83,2,53
-83,3,58
+83,3,57
 83,4,49
 83,5,46
 83,6,45
 83,7,47
 83,8,50
-83,9,57
+83,9,56
 83,10,53
 83,11,49
 83,12,46
@@ -7662,12 +7662,12 @@ from_id,to_id,travel_time
 83,24,37
 83,25,40
 83,26,44
-83,27,45
+83,27,44
 83,28,42
-83,29,39
+83,29,38
 83,30,37
 83,31,35
-83,32,33
+83,32,32
 83,33,31
 83,34,41
 83,35,40
@@ -7676,15 +7676,15 @@ from_id,to_id,travel_time
 83,38,29
 83,39,29
 83,40,26
-83,41,26
-83,42,40
+83,41,25
+83,42,39
 83,43,29
 83,44,28
 83,45,26
 83,46,24
-83,47,22
+83,47,21
 83,48,19
-83,49,19
+83,49,18
 83,50,22
 83,51,38
 83,52,25
@@ -7704,7 +7704,7 @@ from_id,to_id,travel_time
 83,66,10
 83,67,12
 83,68,15
-83,69,28
+83,69,27
 83,70,23
 83,71,15
 83,72,11
@@ -7724,19 +7724,19 @@ from_id,to_id,travel_time
 83,86,30
 83,87,25
 83,88,21
-83,89,6
-83,90,6
+83,89,5
+83,90,5
 83,91,7
 84,0,52
 84,1,53
 84,2,55
-84,3,60
-84,4,52
+84,3,59
+84,4,51
 84,5,48
 84,6,48
 84,7,49
 84,8,52
-84,9,59
+84,9,58
 84,10,56
 84,11,52
 84,12,49
@@ -7748,7 +7748,7 @@ from_id,to_id,travel_time
 84,18,51
 84,19,48
 84,20,46
-84,21,43
+84,21,42
 84,22,42
 84,23,41
 84,24,39
@@ -7768,38 +7768,38 @@ from_id,to_id,travel_time
 84,38,32
 84,39,32
 84,40,29
-84,41,28
-84,42,37
+84,41,27
+84,42,36
 84,43,26
 84,44,31
 84,45,29
-84,46,27
+84,46,26
 84,47,24
 84,48,21
-84,49,20
+84,49,19
 84,50,19
 84,51,42
 84,52,29
 84,53,25
-84,54,23
+84,54,22
 84,55,19
 84,56,17
 84,57,15
 84,58,15
-84,59,40
-84,60,36
+84,59,39
+84,60,35
 84,61,32
 84,62,28
 84,63,22
-84,64,19
+84,64,18
 84,65,16
 84,66,13
-84,67,11
+84,67,10
 84,68,10
 84,69,34
 84,70,29
 84,71,20
-84,72,16
+84,72,15
 84,73,11
 84,74,7
 84,75,5
@@ -7810,25 +7810,25 @@ from_id,to_id,travel_time
 84,80,25
 84,81,14
 84,82,10
-84,83,8
+84,83,7
 84,84,0
-84,85,41
+84,85,40
 84,86,35
 84,87,31
 84,88,27
-84,89,10
+84,89,9
 84,90,6
-84,91,5
+84,91,4
 85,0,60
-85,1,63
+85,1,62
 85,2,65
 85,3,69
-85,4,54
+85,4,53
 85,5,53
 85,6,57
 85,7,59
 85,8,62
-85,9,69
+85,9,68
 85,10,47
 85,11,47
 85,12,46
@@ -7844,14 +7844,14 @@ from_id,to_id,travel_time
 85,22,44
 85,23,47
 85,24,48
-85,25,52
+85,25,51
 85,26,56
-85,27,38
+85,27,37
 85,28,38
 85,29,36
 85,30,38
 85,31,40
-85,32,42
+85,32,41
 85,33,44
 85,34,34
 85,35,34
@@ -7860,36 +7860,36 @@ from_id,to_id,travel_time
 85,38,35
 85,39,39
 85,40,42
-85,41,46
+85,41,45
 85,42,70
-85,43,60
+85,43,59
 85,44,28
 85,45,32
 85,46,37
-85,47,40
-85,48,45
+85,47,39
+85,48,44
 85,49,48
 85,50,53
 85,51,27
 85,52,25
 85,53,29
 85,54,33
-85,55,38
+85,55,37
 85,56,41
-85,57,46
+85,57,45
 85,58,50
 85,59,16
 85,60,17
 85,61,19
 85,62,22
 85,63,26
-85,64,31
+85,64,30
 85,65,35
 85,66,40
 85,67,45
 85,68,48
 85,69,12
-85,70,17
+85,70,16
 85,71,25
 85,72,29
 85,73,33
@@ -7901,26 +7901,26 @@ from_id,to_id,travel_time
 85,79,13
 85,80,17
 85,81,26
-85,82,32
+85,82,31
 85,83,35
 85,84,40
 85,85,0
 85,86,6
 85,87,10
 85,88,15
-85,89,32
-85,90,39
-85,91,41
+85,89,31
+85,90,38
+85,91,40
 86,0,59
 86,1,61
 86,2,63
-86,3,68
-86,4,53
+86,3,67
+86,4,52
 86,5,52
 86,6,55
 86,7,57
 86,8,60
-86,9,67
+86,9,66
 86,10,46
 86,11,46
 86,12,44
@@ -7938,7 +7938,7 @@ from_id,to_id,travel_time
 86,24,47
 86,25,50
 86,26,54
-86,27,37
+86,27,36
 86,28,37
 86,29,35
 86,30,37
@@ -7957,19 +7957,19 @@ from_id,to_id,travel_time
 86,43,56
 86,44,27
 86,45,30
-86,46,36
+86,46,35
 86,47,37
 86,48,41
 86,49,45
 86,50,50
 86,51,26
-86,52,24
+86,52,23
 86,53,27
-86,54,31
+86,54,30
 86,55,35
 86,56,38
 86,57,42
-86,58,47
+86,58,46
 86,59,17
 86,60,16
 86,61,18
@@ -7977,11 +7977,11 @@ from_id,to_id,travel_time
 86,63,24
 86,64,28
 86,65,31
-86,66,37
+86,66,36
 86,67,41
 86,68,44
 86,69,11
-86,70,13
+86,70,12
 86,71,21
 86,72,24
 86,73,28
@@ -8000,11 +8000,11 @@ from_id,to_id,travel_time
 86,86,0
 86,87,5
 86,88,9
-86,89,27
-86,90,34
-86,91,36
-87,0,55
-87,1,57
+86,89,26
+86,90,33
+86,91,35
+87,0,54
+87,1,56
 87,2,59
 87,3,63
 87,4,50
@@ -8012,8 +8012,8 @@ from_id,to_id,travel_time
 87,6,51
 87,7,53
 87,8,56
-87,9,63
-87,10,46
+87,9,62
+87,10,45
 87,11,43
 87,12,42
 87,13,46
@@ -8031,8 +8031,8 @@ from_id,to_id,travel_time
 87,25,46
 87,26,50
 87,27,36
-87,28,35
-87,29,33
+87,28,34
+87,29,32
 87,30,34
 87,31,36
 87,32,36
@@ -8044,13 +8044,13 @@ from_id,to_id,travel_time
 87,38,30
 87,39,33
 87,40,35
-87,41,39
-87,42,63
+87,41,38
+87,42,62
 87,43,52
 87,44,24
 87,45,26
 87,46,31
-87,47,33
+87,47,32
 87,48,37
 87,49,41
 87,50,46
@@ -8058,7 +8058,7 @@ from_id,to_id,travel_time
 87,52,19
 87,53,23
 87,54,26
-87,55,31
+87,55,30
 87,56,33
 87,57,38
 87,58,42
@@ -8067,14 +8067,14 @@ from_id,to_id,travel_time
 87,61,15
 87,62,16
 87,63,19
-87,64,24
+87,64,23
 87,65,27
 87,66,32
 87,67,37
 87,68,40
 87,69,12
 87,70,8
-87,71,17
+87,71,16
 87,72,20
 87,73,24
 87,74,28
@@ -8086,31 +8086,31 @@ from_id,to_id,travel_time
 87,80,8
 87,81,17
 87,82,22
-87,83,26
+87,83,25
 87,84,31
 87,85,10
 87,86,5
 87,87,0
 87,88,5
 87,89,22
-87,90,30
+87,90,29
 87,91,31
 88,0,53
 88,1,54
 88,2,57
-88,3,61
+88,3,60
 88,4,49
 88,5,47
 88,6,49
 88,7,50
-88,8,54
+88,8,53
 88,9,60
-88,10,47
+88,10,46
 88,11,44
 88,12,42
 88,13,44
 88,14,44
-88,15,46
+88,15,45
 88,16,52
 88,17,55
 88,18,41
@@ -8122,8 +8122,8 @@ from_id,to_id,travel_time
 88,24,40
 88,25,43
 88,26,47
-88,27,37
-88,28,36
+88,27,36
+88,28,35
 88,29,33
 88,30,34
 88,31,35
@@ -8131,17 +8131,17 @@ from_id,to_id,travel_time
 88,33,35
 88,34,34
 88,35,32
-88,36,29
+88,36,28
 88,37,29
 88,38,29
 88,39,30
 88,40,31
 88,41,35
-88,42,59
+88,42,58
 88,43,48
 88,44,24
 88,45,24
-88,46,28
+88,46,27
 88,47,29
 88,48,33
 88,49,37
@@ -8149,14 +8149,14 @@ from_id,to_id,travel_time
 88,51,28
 88,52,18
 88,53,19
-88,54,23
+88,54,22
 88,55,27
 88,56,30
 88,57,34
 88,58,38
-88,59,24
+88,59,23
 88,60,20
-88,61,17
+88,61,16
 88,62,13
 88,63,15
 88,64,20
@@ -8166,37 +8166,37 @@ from_id,to_id,travel_time
 88,68,36
 88,69,16
 88,70,12
-88,71,13
+88,71,12
 88,72,16
 88,73,20
-88,74,25
+88,74,24
 88,75,30
-88,76,34
+88,76,33
 88,77,17
 88,78,12
 88,79,8
 88,80,5
 88,81,13
 88,82,18
-88,83,22
+88,83,21
 88,84,27
 88,85,15
 88,86,9
 88,87,5
 88,88,0
-88,89,19
-88,90,26
+88,89,18
+88,90,25
 88,91,27
 89,0,55
-89,1,56
+89,1,55
 89,2,58
-89,3,63
+89,3,62
 89,4,53
 89,5,51
 89,6,50
 89,7,52
 89,8,55
-89,9,62
+89,9,61
 89,10,53
 89,11,51
 89,12,49
@@ -8207,70 +8207,70 @@ from_id,to_id,travel_time
 89,17,56
 89,18,48
 89,19,47
-89,20,46
+89,20,45
 89,21,44
 89,22,44
 89,23,44
 89,24,42
-89,25,45
+89,25,44
 89,26,49
-89,27,44
+89,27,43
 89,28,42
 89,29,40
 89,30,40
 89,31,39
-89,32,38
+89,32,37
 89,33,36
-89,34,41
+89,34,40
 89,35,39
 89,36,35
 89,37,35
 89,38,33
 89,39,34
 89,40,31
-89,41,31
+89,41,30
 89,42,45
-89,43,35
+89,43,34
 89,44,29
-89,45,28
-89,46,29
-89,47,27
+89,45,27
+89,46,28
+89,47,26
 89,48,24
 89,49,24
 89,50,28
 89,51,37
-89,52,25
+89,52,24
 89,53,23
-89,54,22
-89,55,20
+89,54,21
+89,55,19
 89,56,19
 89,57,20
 89,58,23
 89,59,33
 89,60,29
-89,61,26
+89,61,25
 89,62,21
 89,63,18
 89,64,16
 89,65,16
-89,66,16
+89,66,15
 89,67,18
 89,68,19
-89,69,26
+89,69,25
 89,70,21
 89,71,14
 89,72,12
 89,73,11
 89,74,10
-89,75,14
+89,75,13
 89,76,15
 89,77,28
 89,78,23
 89,79,19
-89,80,16
+89,80,15
 89,81,5
 89,82,5
-89,83,6
+89,83,5
 89,84,9
 89,85,31
 89,86,26
@@ -8279,8 +8279,8 @@ from_id,to_id,travel_time
 89,89,0
 89,90,7
 89,91,9
-90,0,55
-90,1,56
+90,0,54
+90,1,55
 90,2,58
 90,3,62
 90,4,54
@@ -8288,7 +8288,7 @@ from_id,to_id,travel_time
 90,6,50
 90,7,52
 90,8,55
-90,9,62
+90,9,61
 90,10,58
 90,11,54
 90,12,51
@@ -8299,14 +8299,14 @@ from_id,to_id,travel_time
 90,17,56
 90,18,53
 90,19,50
-90,20,48
+90,20,47
 90,21,45
 90,22,45
 90,23,44
 90,24,41
 90,25,44
 90,26,49
-90,27,49
+90,27,48
 90,28,47
 90,29,43
 90,30,41
@@ -8318,7 +8318,7 @@ from_id,to_id,travel_time
 90,36,39
 90,37,36
 90,38,34
-90,39,35
+90,39,34
 90,40,31
 90,41,30
 90,42,43
@@ -8331,7 +8331,7 @@ from_id,to_id,travel_time
 90,49,22
 90,50,26
 90,51,42
-90,52,30
+90,52,29
 90,53,26
 90,54,24
 90,55,21
@@ -8349,38 +8349,38 @@ from_id,to_id,travel_time
 90,67,15
 90,68,16
 90,69,32
-90,70,28
+90,70,27
 90,71,19
 90,72,15
 90,73,13
 90,74,10
-90,75,11
+90,75,10
 90,76,12
-90,77,36
-90,78,31
-90,79,27
+90,77,35
+90,78,30
+90,79,26
 90,80,23
 90,81,12
 90,82,8
-90,83,6
+90,83,5
 90,84,6
-90,85,39
+90,85,38
 90,86,33
 90,87,29
 90,88,25
 90,89,7
 90,90,0
-90,91,6
+90,91,5
 91,0,56
 91,1,57
 91,2,60
-91,3,64
+91,3,63
 91,4,56
 91,5,53
 91,6,52
 91,7,53
 91,8,56
-91,9,63
+91,9,62
 91,10,59
 91,11,55
 91,12,52
@@ -8390,7 +8390,7 @@ from_id,to_id,travel_time
 91,16,55
 91,17,58
 91,18,55
-91,19,52
+91,19,51
 91,20,49
 91,21,47
 91,22,46
@@ -8398,12 +8398,12 @@ from_id,to_id,travel_time
 91,24,43
 91,25,46
 91,26,50
-91,27,51
-91,28,49
+91,27,50
+91,28,48
 91,29,45
 91,30,43
-91,31,42
-91,32,40
+91,31,41
+91,32,39
 91,33,38
 91,34,47
 91,35,46
@@ -8413,24 +8413,24 @@ from_id,to_id,travel_time
 91,39,36
 91,40,33
 91,41,32
-91,42,41
+91,42,40
 91,43,30
 91,44,34
 91,45,32
 91,46,31
-91,47,29
-91,48,26
+91,47,28
+91,48,25
 91,49,24
 91,50,24
 91,51,44
-91,52,32
+91,52,31
 91,53,28
 91,54,26
 91,55,23
 91,56,21
 91,57,19
 91,58,20
-91,59,41
+91,59,40
 91,60,37
 91,61,33
 91,62,29
@@ -8454,12 +8454,12 @@ from_id,to_id,travel_time
 91,80,25
 91,81,14
 91,82,10
-91,83,8
+91,83,7
 91,84,4
 91,85,40
 91,86,35
 91,87,31
 91,88,27
 91,89,9
-91,90,6
+91,90,5
 91,91,0
diff --git a/tests/conftest.py b/tests/conftest.py
index a2a26768..c52842fd 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -11,9 +11,12 @@
 import fiona  # noqa: F401
 
 import datetime
+import pathlib
+import time
+
+import jpype
 import geopandas
 import pandas
-import pathlib
 import pytest
 import shapely
 
@@ -53,10 +56,29 @@
 WALKING_TIMES_NOT_SNAPPED = (
     DATA_DIRECTORY / "test_data" / "test_walking_times_not_snapped.csv"
 )
+WALKING_DETAILS_SNAPPED = (
+    DATA_DIRECTORY / "test_data" / "test_walking_details_snapped.csv"
+)
+WALKING_DETAILS_NOT_SNAPPED = (
+    DATA_DIRECTORY / "test_data" / "test_walking_details_not_snapped.csv"
+)
+
+DETAILED_ITINERARIES_BICYCLE = (
+    DATA_DIRECTORY / "test_data" / "test_detailed_itineraries_bicycle.gpkg.zip"
+)
+DETAILED_ITINERARIES_CAR = (
+    DATA_DIRECTORY / "test_data" / "test_detailed_itineraries_car.gpkg.zip"
+)
+DETAILED_ITINERARIES_TRANSIT = (
+    DATA_DIRECTORY / "test_data" / "test_detailed_itineraries_transit.gpkg.zip"
+)
+DETAILED_ITINERARIES_WALK = (
+    DATA_DIRECTORY / "test_data" / "test_detailed_itineraries_walk.gpkg.zip"
+)
 
 
 @pytest.fixture
-def data_columns_with_breakdown(scope="session"):
+def data_columns_with_breakdown():
     yield [
         "from_id",
         "to_id",
@@ -74,11 +96,31 @@ def data_columns_with_breakdown(scope="session"):
     ]
 
 
-@pytest.fixture
+@pytest.fixture(scope="session")
 def departure_datetime():
     yield datetime.datetime(2022, 2, 22, 8, 30)
 
 
+@pytest.fixture
+def detailed_itineraries_bicycle():
+    yield geopandas.read_file(DETAILED_ITINERARIES_BICYCLE)
+
+
+@pytest.fixture
+def detailed_itineraries_car():
+    yield geopandas.read_file(DETAILED_ITINERARIES_CAR)
+
+
+@pytest.fixture
+def detailed_itineraries_transit():
+    yield geopandas.read_file(DETAILED_ITINERARIES_TRANSIT)
+
+
+@pytest.fixture
+def detailed_itineraries_walk():
+    yield geopandas.read_file(DETAILED_ITINERARIES_WALK)
+
+
 @pytest.fixture
 def gtfs_file():
     yield GTFS
@@ -94,24 +136,24 @@ def gtfs_timezone_helsinki():
     yield "Europe/Helsinki"
 
 
-@pytest.fixture(scope="session")
+@pytest.fixture()
 def origins_invalid_no_id():
     origins = geopandas.read_file(ORIGINS_INVALID_NO_ID)
     yield origins
 
 
-@pytest.fixture(scope="session")
+@pytest.fixture()
 def origins_invalid_duplicate_ids():
     origins = geopandas.read_file(ORIGINS_INVALID_DUPLICATE_IDS)
     yield origins
 
 
 @pytest.fixture
-def origin_point(scope="session"):
+def origin_point():
     yield geopandas.read_file(SINGLE_VALID_ORIGIN)
 
 
-@pytest.fixture(scope="session")
+@pytest.fixture()
 def origins_valid_ids():
     origins = geopandas.read_file(ORIGINS_VALID_IDS)
     yield origins
@@ -131,6 +173,21 @@ def population_grid_points(population_grid):
     yield population_grid_points
 
 
+@pytest.fixture(scope="session")
+def population_grid_points_first_three(population_grid_points):
+    yield population_grid_points[0:3]
+
+
+@pytest.fixture(scope="session")
+def population_grid_points_second_three(population_grid_points):
+    yield population_grid_points[4:7]
+
+
+@pytest.fixture(scope="session")
+def population_grid_points_four(population_grid_points):
+    yield population_grid_points[10:14]
+
+
 @pytest.fixture(scope="session")
 def r5_jar_cached():
     from r5py.util.config import Config
@@ -138,36 +195,35 @@ def r5_jar_cached():
     yield str(Config().CACHE_DIR / pathlib.Path(R5_JAR_URL).name)
 
 
-@pytest.fixture(scope="session")
+@pytest.fixture
 def r5_jar_cached_invalid():
     yield "/definitely/invalid/path/to/r5.jar"
 
 
-@pytest.fixture(scope="session")
+@pytest.fixture
 def r5_jar_sha256():
     yield R5_JAR_SHA256
 
 
-@pytest.fixture(scope="session")
+@pytest.fixture
 def r5_jar_sha256_invalid():
     yield R5_JAR_SHA256_INVALID
 
 
-@pytest.fixture(scope="session")
+@pytest.fixture
 def r5_jar_sha256_github_error_message_when_posting():
     yield R5_JAR_SHA256_GITHUB_ERROR_MESSAGE_WHEN_POSTING
 
 
-@pytest.fixture(scope="session")
+@pytest.fixture()
 def r5_jar_url():
     yield R5_JAR_URL
 
 
-@pytest.fixture
-def regional_task(population_grid_points, departure_datetime):
+@pytest.fixture()
+def regional_task(transport_network, population_grid_points, departure_datetime):
     import r5py
 
-    transport_network = r5py.TransportNetwork(OSM_PBF, [GTFS])
     regional_task = r5py.RegionalTask(
         transport_network,
         population_grid_points.at[1, "geometry"],
@@ -176,6 +232,10 @@ def regional_task(population_grid_points, departure_datetime):
     )
     yield regional_task
 
+    del regional_task
+    time.sleep(0.5)
+    jpype.java.lang.System.gc()
+
 
 @pytest.fixture(scope="session")
 def snapped_population_grid_points():
@@ -183,11 +243,11 @@ def snapped_population_grid_points():
 
 
 @pytest.fixture
-def transport_network_files_tuple(scope="session"):
+def transport_network_files_tuple():
     yield OSM_PBF, [GTFS]
 
 
-@pytest.fixture(scope="session")
+@pytest.fixture
 def transport_network(transport_network_from_test_files):
     yield transport_network_from_test_files
 
@@ -196,7 +256,15 @@ def transport_network(transport_network_from_test_files):
 def transport_network_from_test_directory():
     import r5py
 
-    yield r5py.TransportNetwork.from_directory(DATA_DIRECTORY / "Helsinki")
+    transport_network = r5py.TransportNetwork.from_directory(
+        DATA_DIRECTORY / "Helsinki"
+    )
+    yield transport_network
+
+    del transport_network
+
+    time.sleep(0.5)
+    jpype.java.lang.System.gc()
 
 
 @pytest.fixture(scope="session")
@@ -206,6 +274,11 @@ def transport_network_from_test_files():
     transport_network = r5py.TransportNetwork(OSM_PBF, [GTFS])
     yield transport_network
 
+    del transport_network
+
+    time.sleep(0.5)
+    jpype.java.lang.System.gc()
+
 
 @pytest.fixture(scope="session")
 def transport_network_from_test_files_without_gtfs():
@@ -214,8 +287,22 @@ def transport_network_from_test_files_without_gtfs():
     transport_network = r5py.TransportNetwork(OSM_PBF, [])
     yield transport_network
 
+    del transport_network
 
-@pytest.fixture(scope="session")
+    time.sleep(0.5)
+    jpype.java.lang.System.gc()
+
+
+@pytest.fixture
+def unreachable_stops():
+    yield [
+        1294132,
+        1174101,
+        1452601,
+    ]
+
+
+@pytest.fixture
 def unsnappable_points():
     yield geopandas.GeoDataFrame(
         {
@@ -229,11 +316,21 @@ def unsnappable_points():
     )
 
 
-@pytest.fixture(scope="session")
+@pytest.fixture
+def walking_details_snapped():
+    yield pandas.read_csv(WALKING_DETAILS_SNAPPED)
+
+
+@pytest.fixture
+def walking_details_not_snapped():
+    yield pandas.read_csv(WALKING_DETAILS_NOT_SNAPPED)
+
+
+@pytest.fixture
 def walking_times_snapped():
     yield pandas.read_csv(WALKING_TIMES_SNAPPED)
 
 
-@pytest.fixture(scope="session")
+@pytest.fixture
 def walking_times_not_snapped():
     yield pandas.read_csv(WALKING_TIMES_NOT_SNAPPED)
diff --git a/tests/test_classpath.py b/tests/test_classpath.py
index 220744ff..1adb4209 100644
--- a/tests/test_classpath.py
+++ b/tests/test_classpath.py
@@ -39,6 +39,8 @@ def test_download_classpath_with_verbose(
             digest = hashlib.sha256(r5_jar.read()).hexdigest()
         assert digest == r5_jar_sha256
 
+        sys.argv = sys.argv[:-3]
+
     def test_use_classpath_from_local_file(
         self, r5_jar_url, r5_jar_sha256, r5_jar_cached
     ):
@@ -55,6 +57,8 @@ def test_use_classpath_from_local_file(
         assert digest == r5_jar_sha256
         assert r5_classpath == r5_jar_cached
 
+        sys.argv = sys.argv[:-2]
+
     def test_use_classpath_from_local_uri(
         self, r5_jar_url, r5_jar_sha256, r5_jar_cached
     ):
@@ -71,6 +75,8 @@ def test_use_classpath_from_local_uri(
         assert digest == r5_jar_sha256
         assert r5_classpath == r5_jar_cached
 
+        sys.argv = sys.argv[:-2]
+
     def test_use_classpath_from_remote_uri(
         self, r5_jar_url, r5_jar_sha256, r5_jar_cached
     ):
@@ -84,12 +90,16 @@ def test_use_classpath_from_remote_uri(
         assert digest == r5_jar_sha256
         assert r5_classpath == r5_jar_cached
 
+        sys.argv = sys.argv[:-2]
+
     def test_use_classpath_from_invalid_uri(self):
         sys.argv.extend(["--r5-classpath", "invalid://schema/and/path"])
 
         with pytest.raises(UnexpectedClasspathSchema):
             _ = find_r5_classpath(Config().arguments)
 
+        sys.argv = sys.argv[:-2]
+
     def test_find_classpath_download(
         self, r5_jar_url, r5_jar_sha256, r5_jar_cached_invalid
     ):
@@ -99,6 +109,8 @@ def test_find_classpath_download(
             digest = hashlib.sha256(r5_jar.read()).hexdigest()
         assert digest == r5_jar_sha256
 
+        sys.argv = sys.argv[:-2]
+
     @pytest.mark.skipif(
         sys.platform == "win32", reason="No signal chaining library for Windows"
     )
diff --git a/tests/test_data_validation.py b/tests/test_data_validation.py
new file mode 100644
index 00000000..c916c9c5
--- /dev/null
+++ b/tests/test_data_validation.py
@@ -0,0 +1,72 @@
+#!/usr/bin/env python3
+
+
+import geopandas
+import pandas
+import pytest
+import shapely
+
+import r5py
+
+
+class TestDataValidation:
+    @pytest.fixture
+    def geodataframe_with_all_required_columns(self):
+        df = geopandas.GeoDataFrame(
+            {
+                "id": [1],
+                "geometry": [shapely.Point()],
+            },
+            crs="EPSG:4326",
+        )
+        yield df
+
+    @pytest.fixture
+    def geodataframe_without_id_column(self, geodataframe_with_all_required_columns):
+        yield geodataframe_with_all_required_columns[["geometry"]]
+
+    @pytest.fixture
+    def geodataframe_with_nonunique_id_column(
+        self, geodataframe_with_all_required_columns
+    ):
+        yield pandas.concat(
+            [
+                geodataframe_with_all_required_columns,
+                geodataframe_with_all_required_columns,
+            ]
+        )
+
+    @pytest.fixture
+    def geodataframe_without_crs(self, geodataframe_with_all_required_columns):
+        df = geopandas.GeoDataFrame(
+            {
+                "id": [1],
+                "geometry": [shapely.Point()],
+            }
+        )
+        yield df
+
+    @pytest.mark.parametrize(
+        ["geodataframe", "expected_exception"],
+        [
+            (pytest.lazy_fixture("geodataframe_with_all_required_columns"), False),
+            (
+                pytest.lazy_fixture("geodataframe_without_id_column"),
+                r5py.util.exceptions.NoIDColumnError,
+            ),
+            (
+                pytest.lazy_fixture("geodataframe_with_nonunique_id_column"),
+                r5py.util.exceptions.NonUniqueIDError,
+            ),
+            (
+                pytest.lazy_fixture("geodataframe_without_crs"),
+                r5py.util.exceptions.NoCrsError,
+            ),
+        ],
+    )
+    def test_check_od_data_set(self, geodataframe, expected_exception):
+        if expected_exception:
+            with pytest.raises(expected_exception):
+                r5py.util.data_validation.check_od_data_set(geodataframe)
+        else:
+            r5py.util.data_validation.check_od_data_set(geodataframe)
diff --git a/tests/test_detailed_itineraries_computer.py b/tests/test_detailed_itineraries_computer.py
index 3ee214c4..3dec6f35 100644
--- a/tests/test_detailed_itineraries_computer.py
+++ b/tests/test_detailed_itineraries_computer.py
@@ -1,51 +1,195 @@
 #!/usr/bin/env python3
 
+import datetime
+
 import geopandas
+import geopandas.testing
+import numpy
+import pandas
+import pytest
 
 import r5py
 import r5py.util.exceptions
 
 
+class TestDetailedItinerariesComputerInputValidation:
+    @pytest.mark.parametrize(
+        [
+            "origins",
+            "expected_error",
+        ],
+        [
+            (
+                pytest.lazy_fixture("origins_invalid_no_id"),
+                r5py.util.exceptions.NoIDColumnError,
+            ),
+            (
+                pytest.lazy_fixture("origins_invalid_duplicate_ids"),
+                r5py.util.exceptions.NonUniqueIDError,
+            ),
+        ],
+    )
+    def test_origins_invalid_data(
+        self,
+        transport_network,
+        origins,
+        departure_datetime,
+        expected_error,
+    ):
+        with pytest.raises(expected_error):
+            detailed_itineraries_computer_computer = r5py.DetailedItinerariesComputer(
+                transport_network,
+                origins=origins,
+                departure=departure_datetime,
+            )
+            del detailed_itineraries_computer_computer
+
+    def test_origins_valid_data(
+        self,
+        transport_network,
+        origins_valid_ids,
+        departure_datetime,
+    ):
+        detailed_itineraries_computer_computer = r5py.DetailedItinerariesComputer(
+            transport_network,
+            origins=origins_valid_ids,
+            departure=departure_datetime,
+        )
+        del detailed_itineraries_computer_computer
+
+    @pytest.mark.parametrize(
+        [
+            "origins",
+            "destinations",
+            "expected_error",
+        ],
+        [
+            (
+                pytest.lazy_fixture("origins_invalid_no_id"),
+                pytest.lazy_fixture("origins_invalid_no_id"),
+                r5py.util.exceptions.NoIDColumnError,
+            ),
+            (
+                pytest.lazy_fixture("origins_invalid_duplicate_ids"),
+                pytest.lazy_fixture("origins_invalid_duplicate_ids"),
+                r5py.util.exceptions.NonUniqueIDError,
+            ),
+            (
+                pytest.lazy_fixture("origins_invalid_no_id"),
+                pytest.lazy_fixture("origins_invalid_duplicate_ids"),
+                r5py.util.exceptions.NoIDColumnError,
+            ),
+            (
+                pytest.lazy_fixture("origins_invalid_duplicate_ids"),
+                pytest.lazy_fixture("origins_invalid_no_id"),
+                r5py.util.exceptions.NonUniqueIDError,
+            ),
+        ],
+    )
+    def test_origins_and_destinations_invalid_data(
+        self,
+        transport_network,
+        origins,
+        destinations,
+        departure_datetime,
+        expected_error,
+    ):
+        with pytest.raises(expected_error):
+            detailed_itineraries_computer_computer = r5py.DetailedItinerariesComputer(
+                transport_network,
+                origins=origins,
+                destinations=destinations,
+                departure=departure_datetime,
+            )
+            del detailed_itineraries_computer_computer
+
+    @pytest.mark.parametrize(
+        [
+            "origins",
+            "destinations",
+        ],
+        [
+            (
+                pytest.lazy_fixture("origins_valid_ids"),
+                pytest.lazy_fixture("origins_valid_ids"),
+            )
+        ],
+    )
+    def test_origins_and_destinations_valid_data(
+        self,
+        transport_network,
+        origins,
+        destinations,
+        departure_datetime,
+    ):
+        detailed_itineraries_computer_computer = r5py.DetailedItinerariesComputer(
+            transport_network,
+            origins=origins,
+            destinations=destinations,
+            departure=departure_datetime,
+        )
+        with pytest.warns(
+            RuntimeWarning,
+            match="R5 has been compiled with `TransitLayer.SAVE_SHAPES = false`",
+        ):
+            _ = detailed_itineraries_computer_computer.compute_travel_details()
+
+    def test_try_to_route_without_origins(
+        self,
+        transport_network,
+        departure_datetime,
+    ):
+        detailed_itineraries_computer_computer = r5py.DetailedItinerariesComputer(
+            transport_network,
+            departure=departure_datetime,
+        )
+        with pytest.raises(ValueError, match="No routing origins defined"):
+            _ = detailed_itineraries_computer_computer.compute_travel_details()
+
+    def test_try_to_route_without_destinations(
+        self,
+        transport_network,
+        population_grid_points,
+        departure_datetime,
+    ):
+        detailed_itineraries_computer = r5py.DetailedItinerariesComputer(
+            transport_network,
+            origins=population_grid_points[0:3],
+            departure=departure_datetime,
+        )
+        with pytest.warns(
+            RuntimeWarning,
+            match="R5 has been compiled with `TransitLayer.SAVE_SHAPES = false`",
+        ):
+            _ = detailed_itineraries_computer.compute_travel_details()
+        pandas.testing.assert_frame_equal(
+            detailed_itineraries_computer.origins,
+            detailed_itineraries_computer.destinations,
+        )
+
+
 class TestDetailedItinerariesComputer:
     def test_detailed_itineraries_initialization(
         self,
         transport_network,
-        population_grid_points,
-        origin_point,
         departure_datetime,
     ):
         detailed_itineraries_computer = r5py.DetailedItinerariesComputer(
             transport_network,
-            origins=origin_point,
-            destinations=population_grid_points,
             departure=departure_datetime,
             transport_modes=[r5py.TransportMode.TRANSIT, r5py.TransportMode.WALK],
         )
         assert isinstance(
             detailed_itineraries_computer.transport_network, r5py.TransportNetwork
         )
-        assert isinstance(detailed_itineraries_computer.origins, geopandas.GeoDataFrame)
-        assert isinstance(
-            detailed_itineraries_computer.destinations, geopandas.GeoDataFrame
-        )
-
-        assert detailed_itineraries_computer.origins.shape == origin_point.shape
-        assert (
-            detailed_itineraries_computer.destinations.shape
-            == population_grid_points.shape
-        )
 
     def test_detailed_itineraries_initialization_with_files(
         self,
         transport_network_files_tuple,
-        population_grid_points,
-        origin_point,
         departure_datetime,
     ):
         detailed_itineraries_computer = r5py.DetailedItinerariesComputer(
             transport_network_files_tuple,
-            origins=origin_point,
-            destinations=population_grid_points,
             departure=departure_datetime,
             transport_modes=[r5py.TransportMode.TRANSIT, r5py.TransportMode.WALK],
         )
@@ -53,28 +197,366 @@ def test_detailed_itineraries_initialization_with_files(
             detailed_itineraries_computer.transport_network, r5py.TransportNetwork
         )
 
-    def test_one_to_all_with_breakdown(
+    @pytest.mark.parametrize(
+        [
+            "origins",
+            "destinations",
+            "force_all_to_all",
+            "expected_all_to_all",
+            "expected_od_pairs_len",
+        ],
+        [
+            (
+                pytest.lazy_fixture("population_grid_points_first_three"),
+                None,
+                False,
+                True,
+                9,
+            ),
+            (
+                pytest.lazy_fixture("population_grid_points_first_three"),
+                pytest.lazy_fixture("population_grid_points_second_three"),
+                False,
+                False,
+                3,
+            ),
+            (
+                pytest.lazy_fixture("population_grid_points_first_three"),
+                pytest.lazy_fixture("population_grid_points_second_three"),
+                True,
+                True,
+                9,
+            ),
+            (
+                pytest.lazy_fixture("population_grid_points_first_three"),
+                pytest.lazy_fixture("population_grid_points_four"),
+                False,
+                True,
+                12,
+            ),
+        ],
+    )
+    def test_od_pairs_all_to_all(
         self,
         transport_network,
+        origins,
+        destinations,
+        force_all_to_all,
+        departure_datetime,
+        expected_all_to_all,
+        expected_od_pairs_len,
+    ):
+        detailed_itineraries_computer = r5py.DetailedItinerariesComputer(
+            transport_network,
+            origins=origins,
+            destinations=destinations,
+            force_all_to_all=force_all_to_all,
+            departure=departure_datetime,
+            transport_modes=[r5py.TransportMode.WALK],
+        )
+        _ = detailed_itineraries_computer.compute_travel_details()
+        assert detailed_itineraries_computer.all_to_all == expected_all_to_all
+        assert len(detailed_itineraries_computer.od_pairs) == expected_od_pairs_len
+
+    @pytest.mark.filterwarnings(
+        "ignore:Departure time .* is outside of the time range covered by currently loaded GTFS data sets."
+    )
+    def test_gtfs_date_range_warnings(
+        self,
+        transport_network,
+        population_grid_points,
+        origin_point,
+        departure_datetime,
+    ):
+        with pytest.warns(RuntimeWarning):
+            travel_details_matrix_computer = r5py.DetailedItinerariesComputer(
+                transport_network,
+                origins=origin_point,
+                destinations=population_grid_points,
+                departure=datetime.datetime(2021, 2, 22, 8, 30),  # not in GTFS data set
+                transport_modes=[r5py.TransportMode.TRANSIT, r5py.TransportMode.WALK],
+            )
+            del travel_details_matrix_computer
+
+    @pytest.mark.filterwarnings(
+        "ignore:Departure time .* is outside of the time range covered by currently loaded GTFS data sets."
+    )
+    def test_gtfs_date_range_warnings_without_gtfs_file(
+        self,
+        transport_network_from_test_files_without_gtfs,
         population_grid_points,
         origin_point,
         departure_datetime,
-        data_columns_with_breakdown,
+    ):
+        with pytest.warns(RuntimeWarning):
+            detailed_itineraries_computer = r5py.DetailedItinerariesComputer(
+                transport_network_from_test_files_without_gtfs,
+                origins=origin_point,
+                destinations=population_grid_points,
+                departure=departure_datetime,
+                transport_modes=[r5py.TransportMode.TRANSIT, r5py.TransportMode.WALK],
+            )
+            del detailed_itineraries_computer
+
+    @pytest.mark.parametrize(
+        [
+            "snap_to_network",
+            "expected_snap_to_network",
+        ],
+        [
+            (
+                True,
+                True,
+            ),
+            (
+                False,
+                False,
+            ),
+        ],
+    )
+    def test_snap_to_network_parameter(
+        self,
+        transport_network,
+        population_grid_points,
+        departure_datetime,
+        snap_to_network,
+        expected_snap_to_network,
     ):
         detailed_itineraries_computer = r5py.DetailedItinerariesComputer(
             transport_network,
-            origins=origin_point,
-            destinations=population_grid_points,
+            population_grid_points,
             departure=departure_datetime,
-            transport_modes=[r5py.TransportMode.TRANSIT, r5py.TransportMode.WALK],
+            snap_to_network=snap_to_network,
+        )
+        assert detailed_itineraries_computer.snap_to_network == expected_snap_to_network
+
+    @pytest.mark.parametrize(
+        [
+            "snap_to_network",
+            "expected_travel_details",
+        ],
+        [
+            (
+                True,
+                pytest.lazy_fixture("walking_details_snapped"),
+            ),
+            (
+                False,
+                pytest.lazy_fixture("walking_details_not_snapped"),
+            ),
+        ],
+    )
+    def test_snap_to_network(
+        self,
+        transport_network,
+        population_grid_points,
+        departure_datetime,
+        snap_to_network,
+        expected_travel_details,
+    ):
+        # subset to keep test comparison data sets small
+        origins = population_grid_points[::5]
+        detailed_itineraries_computer = r5py.DetailedItinerariesComputer(
+            transport_network,
+            origins=origins,
+            departure=departure_datetime,
+            snap_to_network=snap_to_network,
+            transport_modes=[r5py.TransportMode.WALK],
         )
-        detailed_itineraries = detailed_itineraries_computer.compute_travel_times()
+        travel_details = detailed_itineraries_computer.compute_travel_details()
 
-        # assert detailed_itineraries.shape == (689, 12)  # number of rows varies greatly
-        assert len(detailed_itineraries.columns) == 12
+        travel_details = (
+            travel_details.groupby(["from_id", "to_id", "option"])
+            .sum(["travel_time", "distance"])
+            .reset_index()
+        )
 
-        for col in detailed_itineraries.columns.to_list():
-            assert col in data_columns_with_breakdown
-        assert detailed_itineraries["n_iterations"].min() > 0
-        assert detailed_itineraries["total_time"].min() > 0
-        assert detailed_itineraries["transfer_time"].min() >= 0
+        travel_details = travel_details.set_index(["from_id", "to_id"]).sort_index()
+        expected_travel_details = expected_travel_details.set_index(
+            ["from_id", "to_id"]
+        ).sort_index()
+
+        pandas.testing.assert_frame_equal(travel_details, expected_travel_details)
+
+    def test_snap_to_network_with_unsnappable_origins(
+        self,
+        transport_network,
+        unsnappable_points,
+        population_grid_points,
+        departure_datetime,
+    ):
+        origins = pandas.concat(
+            [population_grid_points[-3:], unsnappable_points]
+        ).reset_index(drop=False)
+        with pytest.warns(
+            RuntimeWarning,
+            match="Some destination points could not be snapped to the street network",
+        ):
+            travel_time_matrix = r5py.TravelTimeMatrixComputer(
+                transport_network,
+                origins,
+                departure=departure_datetime,
+                snap_to_network=True,
+                transport_modes=[r5py.TransportMode.WALK],
+            )
+            _ = travel_time_matrix.compute_travel_times()
+
+    def test_snap_to_network_with_only_unsnappable_origins(
+        self,
+        transport_network,
+        unsnappable_points,
+        departure_datetime,
+    ):
+        with pytest.raises(
+            ValueError, match="After snapping, no valid origin points remain"
+        ):
+            with pytest.warns(
+                RuntimeWarning,
+                match="Some origin points could not be snapped to the street network",
+            ):
+                travel_time_matrix = r5py.TravelTimeMatrixComputer(
+                    transport_network,
+                    unsnappable_points,
+                    departure=departure_datetime,
+                    snap_to_network=True,
+                    transport_modes=[r5py.TransportMode.WALK],
+                )
+                _ = travel_time_matrix.compute_travel_times()
+
+    def test_snap_to_network_with_unsnappable_destinations(
+        self,
+        transport_network,
+        population_grid_points,
+        unsnappable_points,
+        departure_datetime,
+    ):
+        destinations = pandas.concat(
+            [population_grid_points[-3:], unsnappable_points]
+        ).reset_index(drop=False)
+        with pytest.warns(
+            RuntimeWarning,
+            match="Some destination points could not be snapped to the street network",
+        ):
+            travel_time_matrix = r5py.TravelTimeMatrixComputer(
+                transport_network,
+                origins=population_grid_points,
+                destinations=destinations,
+                departure=departure_datetime,
+                snap_to_network=True,
+                transport_modes=[r5py.TransportMode.WALK],
+            )
+            _ = travel_time_matrix.compute_travel_times()
+
+    def test_snap_to_network_with_only_unsnappable_destinations(
+        self,
+        transport_network,
+        population_grid_points,
+        unsnappable_points,
+        departure_datetime,
+    ):
+        with pytest.raises(
+            ValueError, match="After snapping, no valid destination points remain"
+        ):
+            with pytest.warns(
+                RuntimeWarning,
+                match="Some destination points could not be snapped to the street network",
+            ):
+                travel_time_matrix = r5py.TravelTimeMatrixComputer(
+                    transport_network,
+                    origins=population_grid_points,
+                    destinations=unsnappable_points,
+                    departure=departure_datetime,
+                    snap_to_network=True,
+                    transport_modes=[r5py.TransportMode.WALK],
+                )
+                _ = travel_time_matrix.compute_travel_times()
+
+    @pytest.mark.parametrize("snap_to_network", [True, False])
+    def test_travel_details_between_identical_from_and_to_ids(
+        self,
+        transport_network,
+        population_grid_points,
+        departure_datetime,
+        snap_to_network,
+    ):
+        detailed_itineraries = r5py.DetailedItinerariesComputer(
+            transport_network,
+            origins=population_grid_points,
+            transport_modes=[r5py.TransportMode.WALK],
+            departure=departure_datetime,
+            snap_to_network=snap_to_network,
+        ).compute_travel_details()
+
+        assert detailed_itineraries[
+            detailed_itineraries["from_id"] == detailed_itineraries["to_id"]
+        ].travel_time.max() == datetime.timedelta(seconds=0)
+
+    @pytest.mark.parametrize(
+        [
+            "transport_mode",
+            "expected_travel_details",
+        ],
+        [
+            (
+                r5py.TransportMode.BICYCLE,
+                pytest.lazy_fixture("detailed_itineraries_bicycle"),
+            ),
+            (
+                r5py.TransportMode.CAR,
+                pytest.lazy_fixture("detailed_itineraries_car"),
+            ),
+            (
+                r5py.TransportMode.TRANSIT,
+                pytest.lazy_fixture("detailed_itineraries_transit"),
+            ),
+            (
+                r5py.TransportMode.WALK,
+                pytest.lazy_fixture("detailed_itineraries_walk"),
+            ),
+        ],
+    )
+    def test_travel_details_by_transport_mode(
+        self,
+        transport_network,
+        population_grid_points,
+        departure_datetime,
+        transport_mode,
+        expected_travel_details,
+    ):
+        # subset to keep test comparison data sets small
+        origins = population_grid_points[::5].copy()
+        detailed_itineraries_computer = r5py.DetailedItinerariesComputer(
+            transport_network,
+            origins=origins,
+            departure=departure_datetime,
+            transport_modes=[transport_mode],
+        )
+        if transport_mode == r5py.TransportMode.TRANSIT:
+            with pytest.warns(
+                RuntimeWarning,
+                match="R5 has been compiled with `TransitLayer.SAVE_SHAPES = false`",
+            ):
+                travel_details = detailed_itineraries_computer.compute_travel_details()
+        else:
+            travel_details = detailed_itineraries_computer.compute_travel_details()
+
+        travel_details.travel_time = travel_details.travel_time.apply(
+            lambda t: t.total_seconds()
+        )
+        travel_details.wait_time = travel_details.wait_time.apply(
+            lambda t: None if t is None else t.total_seconds()
+        )
+        travel_details.transport_mode = travel_details.transport_mode.apply(
+            lambda t: t.value
+        )
+
+        travel_details.replace(to_replace=[None], value=numpy.nan, inplace=True)
+
+        travel_details = geopandas.GeoDataFrame(travel_details, crs="EPSG:4326")
+
+        geopandas.testing.assert_geodataframe_equal(
+            travel_details,
+            expected_travel_details,
+            check_less_precise=True,  # geometries
+            normalize=True,
+        )
diff --git a/tests/test_good_enough_equidistant_crs.py b/tests/test_good_enough_equidistant_crs.py
new file mode 100644
index 00000000..05b0b0c9
--- /dev/null
+++ b/tests/test_good_enough_equidistant_crs.py
@@ -0,0 +1,105 @@
+#!/usr/bin/env python3
+
+
+import pyproj
+import pytest
+import shapely
+
+import r5py
+
+
+class TestGoodEnoughEquidistantCrs:
+    @pytest.mark.parametrize(
+        ["extent", "expected"],
+        [
+            (
+                shapely.box(-180, -90, 180, 90),
+                pyproj.CRS.from_epsg(3857),
+            ),
+            (
+                shapely.Point((8, 9)),
+                pyproj.CRS.from_epsg(32632),
+            ),
+            (
+                shapely.LineString(
+                    (
+                        (24.9501156641155788, 60.16947122421193228),
+                        (24.95352425020545439, 60.17556887351519634),
+                        (24.96257830700666247, 60.2044859777149739),
+                        (25.01556632713091588, 60.2265357685618099),
+                        (24.90620106775895692, 60.19040368495365811),
+                    )
+                ),
+                pyproj.CRS.from_epsg(32635),
+            ),
+            (
+                shapely.Polygon(
+                    (
+                        (-46.63688, -23.53505),
+                        (-46.62654, -23.53754),
+                        (-46.62731, -23.53931),
+                        (-46.62627, -23.54493),
+                        (-46.62646, -23.54691),
+                        (-46.62499, -23.55171),
+                        (-46.62781, -23.55615),
+                        (-46.63637, -23.55647),
+                        (-46.64494, -23.55441),
+                        (-46.64788, -23.54677),
+                        (-46.64772, -23.54137),
+                        (-46.64660, -23.54109),
+                        (-46.64614, -23.53907),
+                        (-46.64017, -23.53491),
+                        (-46.63688, -23.53505),
+                    )
+                ),
+                pyproj.CRS.from_epsg(32723),
+            ),
+        ],
+    )
+    def test_equidistant_crs(self, extent, expected):
+        assert r5py.util.GoodEnoughEquidistantCrs(extent) == expected
+
+    @pytest.mark.parametrize(
+        [
+            "extent",
+        ],
+        [
+            (shapely.box(-180000, -90000, 180000, 90000),),
+            (shapely.Point(800000, 9000000),),
+            (
+                shapely.LineString(
+                    (
+                        (249501156641155788, 6016947122421193228),
+                        (2495352425020545439, 6017556887351519634),
+                        (2496257830700666247, 602044859777149739),
+                        (2501556632713091588, 602265357685618099),
+                        (2490620106775895692, 6019040368495365811),
+                    )
+                ),
+            ),
+            (
+                shapely.Polygon(
+                    (
+                        (-4663688, -2353505),
+                        (-4662654, -2353754),
+                        (-4662731, -2353931),
+                        (-4662627, -2354493),
+                        (-4662646, -2354691),
+                        (-4662499, -2355171),
+                        (-4662781, -2355615),
+                        (-4663637, -2355647),
+                        (-4664494, -2355441),
+                        (-4664788, -2354677),
+                        (-4664772, -2354137),
+                        (-4664660, -2354109),
+                        (-4664614, -2353907),
+                        (-4664017, -2353491),
+                        (-4663688, -2353505),
+                    )
+                ),
+            ),
+        ],
+    )
+    def test_invalid_equidistant_crs(self, extent):
+        with pytest.raises(r5py.util.exceptions.UnexpectedCrsError):
+            _ = r5py.util.GoodEnoughEquidistantCrs(extent)
diff --git a/tests/test_java_casting.py b/tests/test_java_casting.py
new file mode 100644
index 00000000..06375f67
--- /dev/null
+++ b/tests/test_java_casting.py
@@ -0,0 +1,54 @@
+#!/usr/bin/env python3
+
+import pytest  # noqa: F401
+
+import r5py
+import com.conveyal.r5
+
+
+class TestJavaCasting:
+    @pytest.mark.parametrize(
+        ["python_object", "java_class"],
+        [
+            (r5py.TransportMode.AIR, com.conveyal.r5.api.util.TransitModes),
+            (r5py.TransportMode.BUS, com.conveyal.r5.api.util.TransitModes),
+            (r5py.TransportMode.CABLE_CAR, com.conveyal.r5.api.util.TransitModes),
+            (r5py.TransportMode.FERRY, com.conveyal.r5.api.util.TransitModes),
+            (r5py.TransportMode.FUNICULAR, com.conveyal.r5.api.util.TransitModes),
+            (r5py.TransportMode.GONDOLA, com.conveyal.r5.api.util.TransitModes),
+            (r5py.TransportMode.RAIL, com.conveyal.r5.api.util.TransitModes),
+            (r5py.TransportMode.SUBWAY, com.conveyal.r5.api.util.TransitModes),
+            (r5py.TransportMode.TRAM, com.conveyal.r5.api.util.TransitModes),
+            (r5py.TransportMode.TRANSIT, com.conveyal.r5.api.util.TransitModes),
+            (r5py.TransportMode.BICYCLE, com.conveyal.r5.profile.StreetMode),
+            (r5py.TransportMode.CAR, com.conveyal.r5.profile.StreetMode),
+            (r5py.TransportMode.WALK, com.conveyal.r5.profile.StreetMode),
+            (r5py.TransportMode.BICYCLE, com.conveyal.r5.api.util.LegMode),
+            (r5py.TransportMode.CAR, com.conveyal.r5.api.util.LegMode),
+            (r5py.TransportMode.WALK, com.conveyal.r5.api.util.LegMode),
+            (r5py.TransportMode.BICYCLE_RENT, com.conveyal.r5.api.util.LegMode),
+            (r5py.TransportMode.CAR_PARK, com.conveyal.r5.api.util.LegMode),
+        ],
+    )
+    def test_transport_modes_cast(self, python_object, java_class):
+        # see https://jpype.readthedocs.io/en/latest/quickguide.html#classes-objects
+        _ = (java_class) @ python_object
+
+    @pytest.mark.parametrize(
+        ["python_object", "java_class"],
+        [
+            (r5py.TransportMode.WALK, com.conveyal.r5.api.util.TransitModes),
+            (r5py.TransportMode.CAR, com.conveyal.r5.api.util.TransitModes),
+            (r5py.TransportMode.BICYCLE_RENT, com.conveyal.r5.api.util.TransitModes),
+            (r5py.TransportMode.TRAM, com.conveyal.r5.profile.StreetMode),
+            (r5py.TransportMode.BUS, com.conveyal.r5.profile.StreetMode),
+            (r5py.TransportMode.CAR_PARK, com.conveyal.r5.profile.StreetMode),
+            (r5py.TransportMode.TRANSIT, com.conveyal.r5.api.util.LegMode),
+            (r5py.TransportMode.FUNICULAR, com.conveyal.r5.api.util.LegMode),
+            (r5py.TransportMode.GONDOLA, com.conveyal.r5.api.util.LegMode),
+        ],
+    )
+    def test_transport_modes_invalid_cast(self, python_object, java_class):
+        # see https://jpype.readthedocs.io/en/latest/quickguide.html#classes-objects
+        with pytest.raises(ValueError, match="is not a valid R5"):
+            _ = (java_class) @ python_object
diff --git a/tests/test_transport_mode.py b/tests/test_transport_mode.py
index 46ede162..63742e07 100644
--- a/tests/test_transport_mode.py
+++ b/tests/test_transport_mode.py
@@ -120,3 +120,86 @@ def test_str_transportmode(self, str_transport_mode, expected_transport_mode):
     def test_invalid_transportmode(self, invalid_transport_mode):
         with pytest.raises(ValueError, match="is not a valid TransportMode"):
             _ = r5py.TransportMode(invalid_transport_mode)
+
+    @pytest.mark.parametrize(
+        ["mode", "expected"],
+        [
+            ("CAR", True),
+            ("BICYCLE", True),
+            (r5py.TransportMode.WALK, True),
+            (r5py.TransportMode.BICYCLE_RENT, True),
+            (r5py.TransportMode.BUS, False),
+            ("TRANSIT", False),
+        ],
+    )
+    def test_is_leg_mode(self, mode, expected):
+        assert r5py.TransportMode(mode).is_leg_mode == expected
+
+    @pytest.mark.parametrize(
+        ["mode", "expected"],
+        [
+            ("CAR", True),
+            ("BICYCLE", True),
+            (r5py.TransportMode.WALK, True),
+            (r5py.TransportMode.BICYCLE_RENT, False),
+            (r5py.TransportMode.BUS, False),
+            ("TRANSIT", False),
+        ],
+    )
+    def test_is_street_mode(self, mode, expected):
+        assert r5py.TransportMode(mode).is_street_mode == expected
+
+    @pytest.mark.parametrize(
+        ["mode", "expected"],
+        [
+            ("CAR", False),
+            ("BICYCLE", False),
+            (r5py.TransportMode.WALK, False),
+            (r5py.TransportMode.BICYCLE_RENT, False),
+            (r5py.TransportMode.BUS, True),
+            ("TRANSIT", True),
+        ],
+    )
+    def test_is_transit_mode(self, mode, expected):
+        assert r5py.TransportMode(mode).is_transit_mode == expected
+
+    def test_add(self):
+        transport_mode1 = r5py.TransportMode.TRANSIT
+        transport_mode2 = r5py.TransportMode.WALK
+
+        transport_modes = transport_mode1 + transport_mode2
+        assert isinstance(transport_modes, list)
+        assert transport_modes == [transport_mode1, transport_mode2]
+
+        transport_modes = transport_mode2 + transport_mode1
+        assert isinstance(transport_modes, list)
+        assert transport_modes == [transport_mode2, transport_mode1]
+
+        transport_modes = sum([transport_mode1, transport_mode2])
+        assert isinstance(transport_modes, list)
+        assert transport_modes == [transport_mode1, transport_mode2]
+
+        transport_modes = [transport_mode1] + transport_mode2
+        assert isinstance(transport_modes, list)
+        assert transport_modes == [transport_mode1, transport_mode2]
+
+        transport_modes = transport_mode1 + [transport_mode2]
+        assert isinstance(transport_modes, list)
+        assert transport_modes == [transport_mode1, transport_mode2]
+
+    @pytest.mark.parametrize(
+        ["invalid_other"],
+        [
+            (123.0,),
+            (1,),
+            ("asdfasdf",),
+            ({},),
+        ],
+    )
+    def test_add_invalid_type(self, invalid_other):
+        transport_mode = r5py.TransportMode.TRANSIT
+        with pytest.raises(TypeError, match="unsupported operand type"):
+            _ = transport_mode + invalid_other
+
+        with pytest.raises(TypeError, match="unsupported operand type"):
+            _ = invalid_other + transport_mode
diff --git a/tests/test_transport_network.py b/tests/test_transport_network.py
index 38d2d35e..fcca4201 100644
--- a/tests/test_transport_network.py
+++ b/tests/test_transport_network.py
@@ -4,6 +4,7 @@
 import pathlib
 import shutil
 
+import geopandas
 import pytest
 import shapely
 
@@ -121,7 +122,6 @@ def test_fromdirectory_multiple_osm_files(self, transport_network_files_tuple):
                 transport_network = r5py.TransportNetwork.from_directory(  # noqa: F841
                     temp_directory
                 )
-                del transport_network
 
     def test_fromdirectory_no_osm_files(self):
         # try to create transport network from a directory without osm file
@@ -131,7 +131,6 @@ def test_fromdirectory_no_osm_files(self):
                 transport_network = r5py.TransportNetwork.from_directory(  # noqa: F841
                     temp_directory
                 )
-                del transport_network
 
     def test_snap_to_network(
         self,
@@ -140,7 +139,9 @@ def test_snap_to_network(
         snapped_population_grid_points,
     ):
         snapped = transport_network.snap_to_network(population_grid_points.geometry)
-        assert snapped.geometry.equals(snapped_population_grid_points.geometry)
+        geopandas.testing.assert_geoseries_equal(
+            snapped.geometry, snapped_population_grid_points.geometry
+        )
 
     def test_snap_to_network_with_unsnappable_points(
         self,
@@ -149,3 +150,33 @@ def test_snap_to_network_with_unsnappable_points(
     ):
         snapped = transport_network.snap_to_network(unsnappable_points.geometry)
         assert snapped.geometry.unique() == [shapely.Point()]
+
+    def test_failed_symlink(self, transport_network_files_tuple, monkeypatch):
+        def _symlink_to(*args, **kwargs):
+            raise OSError
+
+        monkeypatch.setattr(pathlib.Path, "symlink_to", _symlink_to)
+
+        transport_network = r5py.TransportNetwork(*transport_network_files_tuple)
+        del transport_network
+
+    def test_failed_unlinking_of_temporary_files(
+        self, transport_network_files_tuple, monkeypatch
+    ):
+        def _unlink(*args, **kwargs):
+            raise OSError
+
+        monkeypatch.setattr(pathlib.Path, "unlink", _unlink)
+
+        transport_network = r5py.TransportNetwork(*transport_network_files_tuple)
+        with pytest.warns(RuntimeWarning, match="Failed to clean cache directory"):
+            del transport_network
+
+    def test_failed_cachedir_rmdir(self, transport_network_files_tuple, monkeypatch):
+        def _rmdir(*args, **kwargs):
+            raise OSError
+
+        monkeypatch.setattr(pathlib.Path, "rmdir", _rmdir)
+
+        transport_network = r5py.TransportNetwork(*transport_network_files_tuple)
+        del transport_network
diff --git a/tests/test_travel_time_matrix_computer.py b/tests/test_travel_time_matrix_computer.py
index 48100bca..631cd8fa 100644
--- a/tests/test_travel_time_matrix_computer.py
+++ b/tests/test_travel_time_matrix_computer.py
@@ -125,7 +125,36 @@ def test_origins_and_destinations_valid_data(
             destinations=destinations,
             departure=departure_datetime,
         )
-        del travel_time_matrix_computer
+        _ = travel_time_matrix_computer.compute_travel_times()
+
+    def test_try_to_route_without_origins(
+        self,
+        transport_network,
+        departure_datetime,
+    ):
+        travel_time_matrix_computer = r5py.TravelTimeMatrixComputer(
+            transport_network,
+            departure=departure_datetime,
+        )
+        with pytest.raises(ValueError, match="No routing origins defined"):
+            _ = travel_time_matrix_computer.compute_travel_times()
+
+    def test_try_to_route_without_destinations(
+        self,
+        transport_network,
+        population_grid_points,
+        departure_datetime,
+    ):
+        travel_time_matrix_computer = r5py.TravelTimeMatrixComputer(
+            transport_network,
+            origins=population_grid_points[0:3],
+            departure=departure_datetime,
+        )
+        _ = travel_time_matrix_computer.compute_travel_times()
+        pandas.testing.assert_frame_equal(
+            travel_time_matrix_computer.origins,
+            travel_time_matrix_computer.destinations,
+        )
 
 
 class TestTravelTimeMatrixComputer:
@@ -252,9 +281,6 @@ def test_one_to_all_with_percentiles(
         )
         assert False not in check.to_list()
 
-    @pytest.mark.filterwarnings(
-        "ignore:Departure time .* is outside of the time range covered by currently loaded GTFS data sets."
-    )
     def test_gtfs_date_range_warnings(
         self,
         transport_network,
@@ -262,7 +288,7 @@ def test_gtfs_date_range_warnings(
         origin_point,
         departure_datetime,
     ):
-        with pytest.warns(RuntimeWarning):
+        with pytest.warns(RuntimeWarning, match="Departure time"):
             travel_time_matrix_computer = r5py.TravelTimeMatrixComputer(
                 transport_network,
                 origins=origin_point,
@@ -272,9 +298,6 @@ def test_gtfs_date_range_warnings(
             )
             del travel_time_matrix_computer
 
-    @pytest.mark.filterwarnings(
-        "ignore:Departure time .* is outside of the time range covered by currently loaded GTFS data sets."
-    )
     def test_gtfs_date_range_warnings_without_gtfs_file(
         self,
         transport_network_from_test_files_without_gtfs,
@@ -282,7 +305,7 @@ def test_gtfs_date_range_warnings_without_gtfs_file(
         origin_point,
         departure_datetime,
     ):
-        with pytest.warns(RuntimeWarning):
+        with pytest.warns(RuntimeWarning, match="Departure"):
             travel_time_matrix_computer = r5py.TravelTimeMatrixComputer(
                 transport_network_from_test_files_without_gtfs,
                 origins=origin_point,
@@ -362,22 +385,51 @@ def test_snap_to_network(
             ["from_id", "to_id"]
         ).sort_index()
 
-        assert travel_times.equals(expected_travel_times)
+        pandas.testing.assert_frame_equal(travel_times, expected_travel_times)
 
     def test_snap_to_network_with_unsnappable_origins(
         self,
         transport_network,
         unsnappable_points,
+        population_grid_points,
         departure_datetime,
     ):
-        with pytest.warns(RuntimeWarning):
-            _ = r5py.TravelTimeMatrixComputer(
+        origins = pandas.concat(
+            [population_grid_points[-3:], unsnappable_points]
+        ).reset_index(drop=False)
+        with pytest.warns(
+            RuntimeWarning,
+            match="Some destination points could not be snapped to the street network",
+        ):
+            travel_time_matrix = r5py.TravelTimeMatrixComputer(
+                transport_network,
+                origins,
+                departure=departure_datetime,
+                snap_to_network=True,
+                transport_modes=[r5py.TransportMode.WALK],
+            )
+            _ = travel_time_matrix.compute_travel_times()
+
+    def test_snap_to_network_with_only_unsnappable_origins(
+        self,
+        transport_network,
+        unsnappable_points,
+        departure_datetime,
+    ):
+        with pytest.raises(
+            ValueError, match="After snapping, no valid origin points remain"
+        ), pytest.warns(
+            RuntimeWarning,
+            match="Some origin points could not be snapped to the street network",
+        ):
+            travel_time_matrix = r5py.TravelTimeMatrixComputer(
                 transport_network,
                 unsnappable_points,
                 departure=departure_datetime,
                 snap_to_network=True,
                 transport_modes=[r5py.TransportMode.WALK],
             )
+            _ = travel_time_matrix.compute_travel_times()
 
     def test_snap_to_network_with_unsnappable_destinations(
         self,
@@ -386,8 +438,37 @@ def test_snap_to_network_with_unsnappable_destinations(
         unsnappable_points,
         departure_datetime,
     ):
-        with pytest.warns(RuntimeWarning):
-            _ = r5py.TravelTimeMatrixComputer(
+        destinations = pandas.concat(
+            [population_grid_points[-3:], unsnappable_points]
+        ).reset_index(drop=False)
+        with pytest.warns(
+            RuntimeWarning,
+            match="Some destination points could not be snapped to the street network",
+        ):
+            travel_time_matrix = r5py.TravelTimeMatrixComputer(
+                transport_network,
+                origins=population_grid_points,
+                destinations=destinations,
+                departure=departure_datetime,
+                snap_to_network=True,
+                transport_modes=[r5py.TransportMode.WALK],
+            )
+            _ = travel_time_matrix.compute_travel_times()
+
+    def test_snap_to_network_with_only_unsnappable_destinations(
+        self,
+        transport_network,
+        population_grid_points,
+        unsnappable_points,
+        departure_datetime,
+    ):
+        with pytest.raises(
+            ValueError, match="After snapping, no valid destination points remain"
+        ), pytest.warns(
+            RuntimeWarning,
+            match="Some destination points could not be snapped to the street network",
+        ):
+            travel_time_matrix = r5py.TravelTimeMatrixComputer(
                 transport_network,
                 origins=population_grid_points,
                 destinations=unsnappable_points,
@@ -395,6 +476,7 @@ def test_snap_to_network_with_unsnappable_destinations(
                 snap_to_network=True,
                 transport_modes=[r5py.TransportMode.WALK],
             )
+            _ = travel_time_matrix.compute_travel_times()
 
     @pytest.mark.parametrize("snap_to_network", [True, False])
     def test_travel_time_between_identical_from_and_to_ids(
diff --git a/tests/test_trip.py b/tests/test_trip.py
new file mode 100644
index 00000000..e9ff6517
--- /dev/null
+++ b/tests/test_trip.py
@@ -0,0 +1,92 @@
+#!/usr/bin/env python3
+
+import datetime
+
+import pytest
+import shapely
+
+import r5py
+
+
+class TestTrip:
+    def test_str(self):
+        trip_leg = r5py.r5.trip_leg.TripLeg(
+            r5py.TransportMode.TRANSIT,
+            datetime.datetime(2023, 4, 25, 15, 30),
+            12.67,
+            datetime.timedelta(minutes=10),
+            datetime.timedelta(minutes=2),
+            "48A",
+            shapely.LineString([[0, 0], [1, 1], [2, 2]]),
+        )
+        trip = r5py.r5.trip.Trip([trip_leg])
+        assert (
+            repr(trip)
+            == "<Trip: 12.67m, 600.0s, <TripLeg: TransportMode.TRANSIT, 12.67m, 600.0s, (0.0, 0.0) -> (2.0, 2.0)>>"
+        )
+
+    def test_summarised_properties(self):
+        trip_leg1 = r5py.r5.trip_leg.TripLeg(
+            r5py.TransportMode.TRANSIT,
+            datetime.datetime(2023, 4, 25, 15, 30),
+            12.34,
+            datetime.timedelta(minutes=10),
+            datetime.timedelta(minutes=2),
+            "56A",
+            shapely.LineString([[0, 0], [1, 1], [2, 2]]),
+        )
+        trip_leg2 = r5py.r5.trip_leg.TripLeg(
+            r5py.TransportMode.TRANSIT,
+            datetime.datetime(2023, 4, 25, 15, 30),
+            98.76,
+            datetime.timedelta(minutes=30),
+            datetime.timedelta(minutes=5),
+            "54A",
+            shapely.LineString([[2, 2], [34, 34]]),
+        )
+
+        trip = trip_leg1 + trip_leg2
+
+        assert trip.distance == pytest.approx(111.10)
+        assert trip.geometry.wkt == "LINESTRING (0 0, 1 1, 2 2, 34 34)"
+        assert trip.routes == ["56A", "54A"]
+        assert trip.transport_modes == [
+            r5py.TransportMode.TRANSIT,
+            r5py.TransportMode.TRANSIT,
+        ]
+        assert trip.travel_time == datetime.timedelta(minutes=(30 + 10))
+        assert trip.wait_time == datetime.timedelta(minutes=(2 + 5))
+
+    def test_as_table(self):
+        trip_leg = r5py.r5.trip_leg.TripLeg(
+            r5py.TransportMode.TRANSIT,
+            datetime.datetime(2023, 4, 25, 15, 30),
+            12.67,
+            datetime.timedelta(minutes=10),
+            datetime.timedelta(minutes=2),
+            "48A",
+            shapely.LineString([[0, 0], [1, 1], [2, 2]]),
+        )
+        trip = r5py.r5.trip.Trip([trip_leg])
+
+        assert trip.as_table() == [
+            [
+                0,
+                r5py.TransportMode.TRANSIT,
+                datetime.datetime(2023, 4, 25, 15, 30),
+                12.67,
+                datetime.timedelta(minutes=10),
+                datetime.timedelta(minutes=2),
+                "48A",
+                shapely.LineString([[0, 0], [1, 1], [2, 2]]),
+            ]
+        ]
+
+    def test_distance_none(self):
+        trip = sum(
+            [
+                r5py.r5.trip_leg.TripLeg(distance=None),
+                r5py.r5.trip_leg.TripLeg(distance=None),
+            ]
+        )
+        assert trip.distance is None
diff --git a/tests/test_trip_leg.py b/tests/test_trip_leg.py
new file mode 100644
index 00000000..15c382f3
--- /dev/null
+++ b/tests/test_trip_leg.py
@@ -0,0 +1,140 @@
+#!/usr/bin/env python3
+
+import datetime
+
+import pytest
+import shapely
+
+import r5py
+
+
+class TestTripLeg:
+    def test_repr(self):
+        trip_leg = r5py.r5.trip_leg.TripLeg(
+            r5py.TransportMode.TRANSIT,
+            datetime.datetime(2023, 4, 25, 15, 30),
+            12.67,
+            datetime.timedelta(minutes=10),
+            datetime.timedelta(minutes=2),
+            "48A",
+            shapely.LineString([[0, 0], [1, 1], [2, 2]]),
+        )
+        assert (
+            repr(trip_leg)
+            == "<TripLeg: TransportMode.TRANSIT, 12.67m, 600.0s, (0.0, 0.0) -> (2.0, 2.0)>"
+        )
+
+        trip_leg = r5py.r5.trip_leg.TripLeg()
+        assert repr(trip_leg) == "<TripLeg>"
+
+    def test_add(self):
+        trip_leg1 = r5py.r5.trip_leg.TripLeg(
+            r5py.TransportMode.TRANSIT,
+            datetime.datetime(2023, 4, 25, 15, 30),
+            12.34,
+            datetime.timedelta(minutes=10),
+            datetime.timedelta(minutes=2),
+            "56A",
+            shapely.LineString([[0, 0], [1, 1], [2, 2]]),
+        )
+        trip_leg2 = r5py.r5.trip_leg.TripLeg(
+            r5py.TransportMode.TRANSIT,
+            datetime.datetime(2023, 4, 25, 15, 30),
+            98.76,
+            datetime.timedelta(minutes=30),
+            datetime.timedelta(minutes=5),
+            "54A",
+            shapely.LineString([[2, 2], [34, 34]]),
+        )
+
+        trip = trip_leg1 + trip_leg2
+        assert isinstance(trip, r5py.r5.trip.Trip)
+        assert trip.legs == [trip_leg1, trip_leg2]
+
+        trip = trip_leg2 + trip_leg1
+        assert isinstance(trip, r5py.r5.trip.Trip)
+        assert trip.legs == [trip_leg2, trip_leg1]
+
+        trip = sum([trip_leg1, trip_leg2])
+        assert isinstance(trip, r5py.r5.trip.Trip)
+        assert trip.legs == [trip_leg1, trip_leg2]
+
+        trip = r5py.r5.trip.Trip([trip_leg1])
+        trip += trip_leg2
+        assert isinstance(trip, r5py.r5.trip.Trip)
+        assert trip.legs == [trip_leg1, trip_leg2]
+
+        trip = r5py.r5.trip.Trip([trip_leg1])
+        trip = trip_leg2 + trip
+        assert isinstance(trip, r5py.r5.trip.Trip)
+        assert trip.legs == [trip_leg2, trip_leg1]
+
+    @pytest.mark.parametrize(
+        ["invalid_other"],
+        [
+            (123.0,),
+            (1,),
+            ("asdfasdf",),
+            ({},),
+        ],
+    )
+    def test_add_invalid_type(self, invalid_other):
+        trip_leg = r5py.r5.trip_leg.TripLeg(
+            r5py.TransportMode.TRANSIT,
+            datetime.datetime(2023, 4, 25, 15, 30),
+            98.76,
+            datetime.timedelta(minutes=30),
+            datetime.timedelta(minutes=5),
+            "54A",
+            shapely.LineString([[2, 2], [34, 34]]),
+        )
+        with pytest.raises(TypeError, match="unsupported operand type"):
+            _ = trip_leg + invalid_other
+
+        with pytest.raises(TypeError, match="unsupported operand type"):
+            _ = invalid_other + trip_leg
+
+    def test_as_table_row(self):
+        trip_leg = r5py.r5.trip_leg.TripLeg(
+            r5py.TransportMode.TRANSIT,
+            datetime.datetime(2023, 4, 25, 15, 30),
+            12.67,
+            datetime.timedelta(minutes=10),
+            datetime.timedelta(minutes=2),
+            "48A",
+            shapely.LineString([[0, 0], [1, 1], [2, 2]]),
+        )
+        assert trip_leg.as_table_row() == [
+            r5py.TransportMode.TRANSIT,
+            datetime.datetime(2023, 4, 25, 15, 30),
+            12.67,
+            datetime.timedelta(minutes=10),
+            datetime.timedelta(minutes=2),
+            "48A",
+            shapely.LineString([[0, 0], [1, 1], [2, 2]]),
+        ]
+
+    def test_compare(self):
+        trip_leg1 = r5py.r5.trip_leg.TripLeg(
+            r5py.TransportMode.TRANSIT,
+            datetime.datetime(2023, 4, 25, 15, 30),
+            12.34,
+            datetime.timedelta(minutes=10),
+            datetime.timedelta(minutes=2),
+            "56A",
+            shapely.LineString([[0, 0], [1, 1], [2, 2]]),
+        )
+        trip_leg2 = r5py.r5.trip_leg.TripLeg(
+            r5py.TransportMode.TRANSIT,
+            datetime.datetime(2023, 4, 25, 15, 30),
+            98.76,
+            datetime.timedelta(minutes=30),
+            datetime.timedelta(minutes=5),
+            "54A",
+            shapely.LineString([[2, 2], [34, 34]]),
+        )
+
+        assert trip_leg1 < trip_leg2
+        assert trip_leg1 <= trip_leg2
+        assert trip_leg2 > trip_leg1
+        assert trip_leg2 >= trip_leg1
diff --git a/tests/test_trip_planner.py b/tests/test_trip_planner.py
new file mode 100644
index 00000000..122b1e00
--- /dev/null
+++ b/tests/test_trip_planner.py
@@ -0,0 +1,13 @@
+#!/usr/bin/env python3
+
+
+import r5py
+
+
+class TestTripPlanner:
+    def test_trip_planner_initialization(
+        self,
+        transport_network,
+        regional_task,
+    ):
+        _ = r5py.r5.trip_planner.TripPlanner(transport_network, regional_task)
diff --git a/tests/test_verbose_warnings.py b/tests/test_verbose_warnings.py
new file mode 100644
index 00000000..6d8d0f01
--- /dev/null
+++ b/tests/test_verbose_warnings.py
@@ -0,0 +1,91 @@
+#!/usr/bin/env python3
+
+
+import sys
+
+import pytest  # noqa: F401
+
+import r5py
+
+
+class TestVerboseWarnings:
+    @pytest.fixture
+    def setup_verbose_mode(self):
+        original_sys_argv = sys.argv.copy()
+        sys.argv.append("--verbose")
+        yield
+        sys.argv = original_sys_argv
+
+    def test_trip_planner_warn_if_origin_or_destination_not_found(
+        self,
+        transport_network,
+        regional_task,
+        setup_verbose_mode,
+    ):
+        regional_task.transport_modes = [r5py.TransportMode.WALK]
+
+        regional_task.fromLat = 48.20
+        regional_task.fromLon = 16.37
+        regional_task.toLat = 0
+        regional_task.toLon = -78.5
+
+        with pytest.warns(RuntimeWarning, match="Could not find"):
+            _ = r5py.r5.trip_planner.TripPlanner(
+                transport_network,
+                regional_task,
+            ).trips
+
+    def test_detailed_itineraries_warn_no_destinations_all_to_all(
+        self,
+        transport_network,
+        population_grid_points_first_three,
+        departure_datetime,
+        setup_verbose_mode,
+    ):
+        with pytest.warns(
+            RuntimeWarning,
+            match="No destinations specified, computing an all-to-all matrix",
+        ):
+            r5py.DetailedItinerariesComputer(
+                transport_network=transport_network,
+                origins=population_grid_points_first_three,
+                departure=departure_datetime,
+                transport_modes=[r5py.TransportMode.WALK],
+            ).compute_travel_details()
+
+    def test_detailed_itineraries_warn_diff_length_all_to_all(
+        self,
+        transport_network,
+        population_grid_points_first_three,
+        population_grid_points_four,
+        departure_datetime,
+        setup_verbose_mode,
+    ):
+        with pytest.warns(
+            RuntimeWarning,
+            match="Origins and destinations are of different length, computing an all-to-all matrix",
+        ):
+            r5py.DetailedItinerariesComputer(
+                transport_network=transport_network,
+                origins=population_grid_points_first_three,
+                destinations=population_grid_points_four,
+                departure=departure_datetime,
+                transport_modes=[r5py.TransportMode.WALK],
+            ).compute_travel_details()
+
+    def test_travel_time_matrix_warn_no_destinations(
+        self,
+        transport_network,
+        population_grid_points_first_three,
+        departure_datetime,
+        setup_verbose_mode,
+    ):
+        with pytest.warns(
+            RuntimeWarning,
+            match="No routing destinations defined, using origins as destinations, too.",
+        ):
+            r5py.TravelTimeMatrixComputer(
+                transport_network,
+                origins=population_grid_points_first_three,
+                departure=departure_datetime,
+            ).compute_travel_times()