Skip to content

Commit

Permalink
feat: router for OpenTripPlanner (#109)
Browse files Browse the repository at this point in the history
  • Loading branch information
khamaileon committed Jul 13, 2023
1 parent ae0fd22 commit 1e4094c
Show file tree
Hide file tree
Showing 16 changed files with 783 additions and 33 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,7 @@ test.py
*.pyc
*.idea/
.DS_Store

# Visual Studio Code
.vscode
.history
1 change: 1 addition & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ or **time-distance matrices**.
- `Here Maps`_
- `Google Maps`_
- `Graphhopper`_
- `OpenTripPlannerV2`_
- `Local Valhalla`_
- `Local OSRM`_

Expand Down
11 changes: 10 additions & 1 deletion docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,16 @@ MapboxValhalla

.. automethod:: __init__

OpenTripPlannerV2
--------------

.. autoclass:: routingpy.routers.OpenTripPlannerV2
:members:
:inherited-members:
:show-inheritance:

.. automethod:: __init__

ORS
---

Expand Down Expand Up @@ -176,4 +186,3 @@ Indices and search

* :ref:`genindex`
* :ref:`search`

39 changes: 23 additions & 16 deletions routingpy/client_default.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,8 +134,8 @@ def _request(
:raises routingpy.exceptions.TransportError: when something went wrong while trying to
execute a request.
:returns: raw JSON response.
:rtype: dict
:returns: raw JSON response or GeoTIFF image
:rtype: dict or bytes
"""

if not first_request_time:
Expand Down Expand Up @@ -192,13 +192,10 @@ def _request(
"Server down.\nRetrying for the {}{} time.".format(tried, get_ordinal(tried)),
UserWarning,
)

return self._request(url, get_params, post_params, first_request_time, retry_counter + 1)

try:
result = self._get_body(response)

return result
return self._get_body(response)

except exceptions.RouterApiError:
if self.skip_api_error:
Expand Down Expand Up @@ -229,22 +226,32 @@ def req(self):
@staticmethod
def _get_body(response):
status_code = response.status_code
content_type = response.headers["content-type"]

try:
body = response.json()
except json.decoder.JSONDecodeError:
raise exceptions.JSONParseError("Can't decode JSON response:{}".format(response.text))
if status_code == 200:
if content_type in ["application/json", "application/x-www-form-urlencoded"]:
try:
return response.json()

except json.decoder.JSONDecodeError:
raise exceptions.JSONParseError(
"Can't decode JSON response:{}".format(response.text)
)

elif content_type == "image/tiff":
return response.content

else:
raise exceptions.UnsupportedContentType(status_code, response.text)

if status_code == 429:
raise exceptions.OverQueryLimit(status_code, body)
raise exceptions.OverQueryLimit(status_code, response.text)

if 400 <= status_code < 500:
raise exceptions.RouterApiError(status_code, body)
raise exceptions.RouterApiError(status_code, response.text)

if 500 <= status_code:
raise exceptions.RouterServerError(status_code, body)
raise exceptions.RouterServerError(status_code, response.text)

if status_code != 200:
raise exceptions.RouterError(status_code, body)

return body
raise exceptions.RouterError(status_code, response.text)
32 changes: 32 additions & 0 deletions routingpy/convert.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#
"""Converts Python types to string representations suitable for GET queries.
"""
import datetime


def delimit_list(arg, delimiter=","):
Expand Down Expand Up @@ -76,3 +77,34 @@ def _has_method(arg, method):
:rtype: bool
"""
return hasattr(arg, method) and callable(getattr(arg, method))


def seconds_to_iso8601(seconds):
"""Convert the given number of seconds to ISO 8601 duration format.
Example:
>>> seconds_to_iso8601(3665)
'PT1H1M5S'
:param seconds: The number of seconds to convert.
:type seconds: int
:returns: The duration in ISO 8601 format.
:rtype: string
"""
duration = datetime.timedelta(seconds=seconds)
hours = duration.seconds // 3600
minutes = (duration.seconds // 60) % 60
seconds = duration.seconds % 60

iso8601_duration = "PT"
if hours:
iso8601_duration += f"{hours}H"

if minutes:
iso8601_duration += f"{minutes}M"

if seconds or not (hours or minutes):
iso8601_duration += f"{seconds}S"

return iso8601_duration
6 changes: 6 additions & 0 deletions routingpy/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,3 +71,9 @@ class OverQueryLimit(RouterError, RetriableRequest):
"""

pass


class UnsupportedContentType(Exception): # pragma: no cover
"""The response has an unsupported content type."""

pass
2 changes: 1 addition & 1 deletion routingpy/isochrone.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ def center(self) -> Optional[Union[List[float], Tuple[float]]]:
return self._center

@property
def interval(self) -> int:
def interval(self) -> Optional[int]:
"""
The interval of the isochrone in seconds or in meters.
Expand Down
51 changes: 51 additions & 0 deletions routingpy/raster.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2021 GIS OPS UG
#
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy of
# the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under
# the License.
#
"""
:class:`Raster` returns rasters results.
"""
from typing import Optional


class Raster(object):
"""
Contains a parsed single raster response. Access via properties ``image``, ``max_travel_time``
"""

def __init__(self, image=None, max_travel_time=None):
self._image = image
self._max_travel_time = max_travel_time

@property
def image(self) -> Optional[bytes]:
"""
The image of the raster.
:rtype: bytes
"""
return self._image

@property
def max_travel_time(self) -> int:
"""
The max travel time of the raster in seconds.
:return: int
"""
return self._max_travel_time

def __repr__(self): # pragma: no cover
return "Raster({})".format(self.max_travel_time)
26 changes: 14 additions & 12 deletions routingpy/routers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,26 +22,28 @@
from .mapbox_osrm import MapboxOSRM
from .mapbox_valhalla import MapboxValhalla
from .openrouteservice import ORS
from .opentripplanner_v2 import OpenTripPlannerV2
from .osrm import OSRM
from .valhalla import Valhalla

# Provide synonyms
_SERVICE_TO_ROUTER = {
"ors": ORS,
"openrouteservice": ORS,
"osrm": OSRM,
"google": Google,
"graphhopper": Graphhopper,
"here": HereMaps,
"heremaps": HereMaps,
"mapbox_osrm": MapboxOSRM,
"mapbox-osrm": MapboxOSRM,
"mapboxosrm": MapboxOSRM,
"mapbox": MapboxOSRM,
"valhalla": Valhalla,
"mapbox_valhalla": MapboxValhalla,
"mapbox-osrm": MapboxOSRM,
"mapbox-valhalla": MapboxValhalla,
"mapbox": MapboxOSRM,
"mapboxosrm": MapboxOSRM,
"mapboxvalhalla": MapboxValhalla,
"graphhopper": Graphhopper,
"google": Google,
"here": HereMaps,
"heremaps": HereMaps,
"openrouteservice": ORS,
"ors": ORS,
"osrm": OSRM,
"otp_v2": OpenTripPlannerV2,
"valhalla": Valhalla,
}


Expand All @@ -61,7 +63,7 @@ def get_router_by_name(router_name):
:param router_name: Name of the router as string.
:type router_name: str
:rtype: Union[:class:`routingpy.routers.google.Google`, :class:`routingpy.routers.graphhopper.Graphhopper`, :class:`routingpy.routers.heremaps.HereMaps`, :class:`routingpy.routers.mapbox_osrm.MapBoxOSRM`, :class:`routingpy.routers.mapbox_valhalla.MapBoxValhalla`, :class:`routingpy.routers.openrouteservice.ORS`, :class:`routingpy.routers.osrm.OSRM`, :class:`routingpy.routers.valhalla.Valhalla`]
:rtype: Union[:class:`routingpy.routers.google.Google`, :class:`routingpy.routers.graphhopper.Graphhopper`, :class:`routingpy.routers.heremaps.HereMaps`, :class:`routingpy.routers.mapbox_osrm.MapBoxOSRM`, :class:`routingpy.routers.mapbox_valhalla.MapBoxValhalla`, :class:`routingpy.routers.openrouteservice.ORS`, :class:`routingpy.routers.osrm.OSRM`, :class:`routingpy.routers.otp_v2.OpenTripPlannerV2`, :class:`routingpy.routers.valhalla.Valhalla`]
"""
try:
Expand Down
2 changes: 1 addition & 1 deletion routingpy/routers/openrouteservice.py
Original file line number Diff line number Diff line change
Expand Up @@ -380,7 +380,7 @@ def isochrones(
intervals: List[int],
interval_type: Optional[str] = "time",
units: Optional[str] = None,
location_type: Optional[str] = None,
location_type: Optional[str] = "start",
smoothing: Optional[float] = None,
attributes: Optional[List[str]] = None,
intersections: Optional[bool] = None,
Expand Down
Loading

0 comments on commit 1e4094c

Please sign in to comment.