# Copyright (C) 2019-2021 HERE Europe B.V.
# SPDX-License-Identifier: Apache-2.0
"""This module contains classes for accessing `HERE Routing API <https://developer.here.com/documentation/routing-api/8.17.0/dev_guide/index.html>`_.
""" # noqa E501
from datetime import datetime
from typing import Dict, List, Optional
from here_location_services.config.base_config import PlaceOptions, Truck, WayPointOptions
from here_location_services.config.matrix_routing_config import AvoidBoundingBox
from here_location_services.config.routing_config import Scooter, Via
from here_location_services.platform.auth import Auth
from .apis import Api
from .exceptions import ApiError
[docs]class RoutingApi(Api):
"""A class for accessing HERE routing APIs."""
[docs] def __init__(
self,
api_key: Optional[str] = None,
auth: Optional[Auth] = None,
proxies: Optional[dict] = None,
country: str = "row",
):
super().__init__(api_key, auth=auth, proxies=proxies, country=country)
self._base_url = f"https://router.{self._get_url_string()}"
[docs] def route(
self,
transport_mode: str,
origin: List,
destination: List,
via: Optional[List[Via]] = None,
origin_place_options: Optional[PlaceOptions] = None,
origin_waypoint_options: Optional[WayPointOptions] = None,
destination_place_options: Optional[PlaceOptions] = None,
destination_waypoint_options: Optional[WayPointOptions] = None,
scooter: Optional[Scooter] = None,
departure_time: Optional[datetime] = None,
routing_mode: str = "fast",
alternatives: int = 0,
units: str = "metric",
lang: str = "en-US",
return_results: Optional[List] = None,
spans: Optional[List] = None,
truck: Optional[Truck] = None,
avoid_features: Optional[List[str]] = None,
avoid_areas: Optional[List[AvoidBoundingBox]] = None,
exclude: Optional[List[str]] = None,
):
"""Calculate route between two endpoints.
See further information `here <https://developer.here.com/documentation/routing-api/8.16.0/api-reference-swagger.html>_`.
:param transport_mode: A string to represent mode of transport.
:param origin: A list of ``latitude`` and ``longitude`` of origin point of route.
:param destination: A list of ``latitude`` and ``longitude`` of destination point of route.
:param via: A list of tuples of ``latitude`` and ``longitude`` of via points.
:param origin_place_options: :class:`PlaceOptions` optinal place options for ``origin``.
:param origin_waypoint_options: :class:`WayPointOptions` optional waypoint options
for ``origin``.
:param destination_place_options: :class:`PlaceOptions` optinal place options
for ``destination``.
:param destination_waypoint_options: :class:`WayPointOptions` optional waypoint options
for ``destination``.
:param scooter: Additional attributes for scooter route.
:param departure_time: :class:`datetime.datetime` object.
:param routing_mode: A string to represent routing mode.
:param alternatives: Number of alternative routes to return aside from the optimal route.
default value is ``0`` and maximum is ``6``.
:param units: A string representing units of measurement used in guidance instructions.
The default is metric.
:param lang: A string representing preferred language of the response.
The value should comply with the IETF BCP 47.
:param return_results: A list of strings.
:param spans: A list of strings to define which attributes are included in the response
spans.
:param truck: Different truck options to use during route calculation.
use object of :class:`Truck here_location_services.config.matrix_routing_config.Truck>`
:param avoid_features: Avoid routes that violate these properties. Avoid features are
defined in :attr:`AVOID_FEATURES <here_location_services.config.routing_config.AVOID_FEATURES>`
:param avoid_areas: A list of areas to avoid during route calculation. To define avoid area
use object of :class:`AvoidBoundingBox here_location_services.config.matrix_routing_config.AvoidBoundingBox>`.
:param exclude: A comma separated list of three-letter country codes
(ISO-3166-1 alpha-3 code) that routes will exclude.
:return: :class:`requests.Response` object.
:raises ApiError: If ``status_code`` of API response is not 200.
""" # noqa E501
path = "v8/routes"
url = f"{self._base_url}/{path}"
params: Dict[str, str] = {
"origin": ",".join([str(i) for i in origin]),
"destination": ",".join([str(i) for i in destination]),
"transportMode": transport_mode,
}
if via:
vias = []
for v in via:
via_str = f"via={v.lat},{v.lng}"
if v.place_options:
place_optns_str = ";".join(
key + "=" + str(val)
for key, val in vars(v.place_options).items()
if val is not None
)
via_str = via_str + ";" + place_optns_str
if v.waypoint_options:
waypoint_optns_str = "!".join(
key + "=" + str(val)
for key, val in vars(v.waypoint_options).items()
if val is not None
)
via_str = via_str + "!" + waypoint_optns_str
vias.append(via_str)
url = url + "?" + "&".join(vias)
if departure_time:
params["departureTime"] = departure_time.isoformat(timespec="seconds")
params["routingMode"] = routing_mode
params["alternatives"] = str(alternatives)
params["units"] = units
params["lang"] = lang
if return_results:
params["return"] = ",".join(return_results)
if spans:
params["spans"] = ",".join(spans)
if origin_place_options:
origin_place_opt = ";".join(
key + "=" + str(val)
for key, val in vars(origin_place_options).items()
if val is not None
)
params["origin"] = ";".join([params["origin"], origin_place_opt])
if origin_waypoint_options:
origin_way_opt = "!".join(
key + "=" + str(val)
for key, val in vars(origin_waypoint_options).items()
if val is not None
)
params["origin"] = "!".join([params["origin"], origin_way_opt])
if destination_place_options:
dest_place_opt = ";".join(
key + "=" + str(val)
for key, val in vars(destination_place_options).items()
if val is not None
)
params["destination"] = ";".join([params["destination"], dest_place_opt])
if destination_waypoint_options:
dest_way_opt = "!".join(
key + "=" + str(val)
for key, val in vars(destination_waypoint_options).items()
if val is not None
)
params["destination"] = "!".join([params["destination"], dest_way_opt])
if scooter:
if scooter.allowHighway is True:
params["scooter[allowHighway]"] = "true"
else:
params["scooter[allowHighway]"] = "false"
if truck:
for key, val in vars(truck).items():
if key == "shippedHazardousGoods" and val:
params[f"truck[{key}]"] = ",".join(val)
elif val is not None:
params[f"truck[{key}]"] = val
if avoid_features:
params["avoid[features]"] = ",".join(avoid_features)
if avoid_areas:
bbox_areas = []
for area in avoid_areas:
area_attrs = vars(area)
bbox_areas.append(
"bbox:{west},{south},{east},{north}".format(
west=area_attrs["west"],
south=area_attrs["south"],
east=area_attrs["east"],
north=area_attrs["north"],
)
)
params["avoid[areas]"] = "|".join(bbox_areas)
if exclude:
params["exclude"] = ",".join(exclude)
resp = self.get(url, params=params, proxies=self.proxies)
if resp.status_code == 200:
return resp
else:
raise ApiError(resp)