-
Notifications
You must be signed in to change notification settings - Fork 32
Add departure and arrival datetime to Direction #116
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
certifi==2023.7.22 ; python_full_version >= "3.8.0" and python_full_version < "4.0.0" | ||
charset-normalizer==3.2.0 ; python_full_version >= "3.8.0" and python_full_version < "4.0.0" | ||
idna==3.4 ; python_full_version >= "3.8.0" and python_full_version < "4.0.0" | ||
pytz==2023.3 ; python_full_version >= "3.8.0" and python_full_version < "4.0.0" | ||
requests==2.31.0 ; python_full_version >= "3.8.0" and python_full_version < "4.0.0" | ||
urllib3==2.0.4 ; python_full_version >= "3.8.0" and python_full_version < "4.0.0" |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,9 +15,12 @@ | |
# the License. | ||
# | ||
|
||
import datetime | ||
from operator import itemgetter | ||
from typing import List, Optional, Tuple, Union | ||
|
||
import pytz | ||
|
||
from .. import convert, utils | ||
from ..client_base import DEFAULT | ||
from ..client_default import Client | ||
|
@@ -319,12 +322,40 @@ def directions( # noqa: C901 | |
if transit_routing_preference: | ||
params["transit_routing_preference"] = transit_routing_preference | ||
|
||
return self.parse_direction_json( | ||
return self._parse_direction_json( | ||
self.client._request("/directions/json", get_params=params, dry_run=dry_run), alternatives | ||
) | ||
|
||
@staticmethod | ||
def parse_direction_json(response, alternatives): | ||
def _time_object_to_aware_datetime(self, time_object): | ||
timestamp = time_object["value"] | ||
dt = datetime.datetime.fromtimestamp(timestamp) | ||
timezone = pytz.timezone(time_object["time_zone"]) | ||
return dt.astimezone(timezone) | ||
|
||
def _parse_legs(self, legs): | ||
duration = 0 | ||
distance = 0 | ||
geometry = [] | ||
departure_datetime = None | ||
arrival_datetime = None | ||
|
||
for leg in legs: | ||
duration += leg["duration"]["value"] | ||
distance += leg["distance"]["value"] | ||
for step in leg["steps"]: | ||
geometry.extend(utils.decode_polyline5(step["polyline"]["points"])) | ||
|
||
departure_time = legs[0].get("departure_time") | ||
if departure_time: | ||
departure_datetime = self._time_object_to_aware_datetime(departure_time) | ||
|
||
arrival_time = legs[-1].get("arrival_time") | ||
if arrival_time: | ||
arrival_datetime = self._time_object_to_aware_datetime(arrival_time) | ||
|
||
return duration, distance, geometry, departure_datetime, arrival_datetime | ||
|
||
def _parse_direction_json(self, response, alternatives): | ||
if response is None: # pragma: no cover | ||
if alternatives: | ||
return Directions() | ||
|
@@ -345,33 +376,27 @@ def parse_direction_json(response, alternatives): | |
|
||
raise error(STATUS_CODES[status]["code"], STATUS_CODES[status]["message"]) | ||
|
||
if alternatives: | ||
routes = [] | ||
for route in response["routes"]: | ||
geometry = [] | ||
duration, distance = 0, 0 | ||
for leg in route["legs"]: | ||
duration += leg["duration"]["value"] | ||
distance += leg["distance"]["value"] | ||
for step in leg["steps"]: | ||
geometry.extend(utils.decode_polyline5(step["polyline"]["points"])) | ||
|
||
routes.append( | ||
Direction( | ||
geometry=geometry, duration=int(duration), distance=int(distance), raw=route | ||
) | ||
directions = [] | ||
for route in response["routes"]: | ||
duration, distance, geometry, departure_datetime, arrival_datetime = self._parse_legs( | ||
route["legs"] | ||
) | ||
directions.append( | ||
Direction( | ||
geometry=geometry, | ||
duration=int(duration), | ||
distance=int(distance), | ||
departure_datetime=departure_datetime, | ||
arrival_datetime=arrival_datetime, | ||
raw=route, | ||
) | ||
return Directions(routes, response) | ||
else: | ||
geometry = [] | ||
duration, distance = 0, 0 | ||
for leg in response["routes"][0]["legs"]: | ||
duration += leg["duration"]["value"] | ||
distance += leg["distance"]["value"] | ||
for step in leg["steps"]: | ||
geometry.extend(utils.decode_polyline5(step["polyline"]["points"])) | ||
|
||
return Direction(geometry=geometry, duration=duration, distance=distance, raw=response) | ||
) | ||
|
||
if alternatives: | ||
return Directions(directions, raw=response) | ||
|
||
elif directions: | ||
return directions[0] | ||
|
||
def isochrones(self): # pragma: no cover | ||
raise NotImplementedError | ||
|
@@ -506,12 +531,11 @@ def matrix( # noqa: C901 | |
if transit_routing_preference: | ||
params["transit_routing_preference"] = transit_routing_preference | ||
|
||
return self.parse_matrix_json( | ||
return self._parse_matrix_json( | ||
self.client._request("/distancematrix/json", get_params=params, dry_run=dry_run) | ||
) | ||
|
||
@staticmethod | ||
def parse_matrix_json(response): | ||
def _parse_matrix_json(self, response): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. hm why this change? this is "public" and There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In this case, it prevented other class methods (_parse_legs) from being called. We'll have to figure out how to rewrite this. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I did a small rewrite, none of those functions needed access to the instance, they can all be static actually |
||
if response is None: # pragma: no cover | ||
return Matrix() | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,7 +15,7 @@ | |
# the License. | ||
# | ||
import datetime | ||
from typing import List, Optional # noqa: F401 | ||
from typing import List, Optional | ||
|
||
from .. import convert, utils | ||
from ..client_base import DEFAULT | ||
|
@@ -167,30 +167,35 @@ def directions( | |
) | ||
return self._parse_directions_response(response, num_itineraries) | ||
|
||
def _timestamp_to_utc_datetime(self, timestamp): | ||
dt = datetime.datetime.fromtimestamp(timestamp / 1000) | ||
return dt.astimezone(datetime.timezone.utc) | ||
|
||
def _parse_directions_response(self, response, num_itineraries): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. that brings up another good point: this should be static and "public" to be consistent with the other interfaces. we do use this function type in other projects, but also it doesn't need anything from its instance, so it can be static. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the same for the isochrones There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This one would fit nicely in a utils file because it's generic. |
||
if response is None: # pragma: no cover | ||
return Directions() if num_itineraries > 1 else Direction() | ||
|
||
routes = [] | ||
directions = [] | ||
for itinerary in response["data"]["plan"]["itineraries"]: | ||
geometry, distance = self._parse_legs(itinerary["legs"]) | ||
routes.append( | ||
distance, geometry = self._parse_legs(itinerary["legs"]) | ||
departure_datetime = self._timestamp_to_utc_datetime(itinerary["startTime"]) | ||
arrival_datetime = self._timestamp_to_utc_datetime(itinerary["endTime"]) | ||
directions.append( | ||
Direction( | ||
geometry=geometry, | ||
duration=int(itinerary["duration"]), | ||
distance=distance, | ||
departure_datetime=departure_datetime, | ||
arrival_datetime=arrival_datetime, | ||
raw=itinerary, | ||
) | ||
) | ||
|
||
if num_itineraries > 1: | ||
return Directions(routes, raw=response) | ||
|
||
elif routes: | ||
return routes[0] | ||
return Directions(directions, raw=response) | ||
|
||
else: | ||
return Direction() | ||
elif directions: | ||
return directions[0] | ||
|
||
def _parse_legs(self, legs): | ||
distance = 0 | ||
|
@@ -200,7 +205,7 @@ def _parse_legs(self, legs): | |
geometry.extend(list(reversed(points))) | ||
distance += int(leg["distance"]) | ||
|
||
return geometry, distance | ||
return distance, geometry | ||
|
||
def isochrones( | ||
self, | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we make these new methods util methods instead? I feel it's better to outsource those functions (here and otp) so they can be re-used. I'll do valhalla anyways and will PR to your PR, then you can see if you like that too:)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This one in particular is specific to Google because the input format is theirs. Do we output to a Google-specific utility file?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried to generalize it a bit in https://github.com/khamaileon/routingpy/pull/1/files