Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

4 new options for the directions layer (Issue: 151) #153

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
54 changes: 49 additions & 5 deletions gmaps/directions.py
@@ -1,7 +1,7 @@

import ipywidgets as widgets

from traitlets import Unicode, CUnicode, List, observe, validate
from traitlets import Bool, Unicode, CUnicode, List, observe, validate

from . import geotraitlets
from .locations import locations_to_list
Expand Down Expand Up @@ -49,6 +49,15 @@ class Directions(widgets.Widget):
between -180 (corresponding to 180 degrees west) and 180
(corresponding to 180 degrees east).
:type data: list of tuples of length >= 2
:param avoid_ferries: Avoids ferries where possible.
:type avoid_ferries: bool, optional
:param avoid_highways: Avoids highways where possible.
:type avoid_highways: bool, optional
:param avoid_tolls: Avoids toll roads where possible.
:type avoid_tolls: bool, optional
:param optimize_waypoints: Attempt to re-order the supplied intermediate
waypoints to minimize overall cost of the route.
:type optimize_waypoints: bool, optional
"""
has_bounds = True
_view_name = Unicode("DirectionsLayerView").tag(sync=True)
Expand All @@ -58,6 +67,10 @@ class Directions(widgets.Widget):

data = List(minlen=2).tag(sync=True)
data_bounds = List().tag(sync=True)
avoid_ferries = Bool(default_value=False).tag(sync=True)
avoid_highways = Bool(default_value=False).tag(sync=True)
avoid_tolls = Bool(default_value=False).tag(sync=True)
optimize_waypoints = Bool(default_value=False).tag(sync=True)

layer_status = CUnicode().tag(sync=True)

Expand Down Expand Up @@ -88,17 +101,29 @@ def _handle_layer_status(self, change):
"No directions returned: " + change["new"])


def _directions_options(start, end, waypoints):
def _directions_options(start, end, waypoints, avoid_ferries,
avoid_highways, avoid_tolls,
optimize_waypoints):
start = tuple(start)
end = tuple(end)
if waypoints is None:
data = [start, end]
else:
data = [start] + locations_to_list(waypoints) + [end]
return {"data": data}

model = {
"data": data,
"avoid_ferries": avoid_ferries,
"avoid_highways": avoid_highways,
"avoid_tolls": avoid_tolls,
"optimize_waypoints": optimize_waypoints
}
return model

def directions_layer(start, end, waypoints=None):

def directions_layer(
start, end, waypoints=None, avoid_ferries=False,
avoid_highways=False, avoid_tolls=False, optimize_waypoints=False):
"""
Create a directions layer.

Expand Down Expand Up @@ -133,6 +158,25 @@ def directions_layer(start, end, waypoints=None):
Google maps imposes a limitation on the total number of waypoints.
This limit is currently 23.
:type waypoints: List of 2-element tuples, optional

:param avoid_ferries:
Avoid ferries where possible.
:type avoid_ferries: bool, optional

:param avoid_highways:
Avoid highways where possible.
:type avoid_highways: bool, optional

:param avoid_tolls:
Avoid toll roads where possible.
:type avoid_tolls: bool, optional

:param optimize_waypoints:
If set to true, will attempt to re-order the supplied intermediate
waypoints to minimize overall cost of the route.
:type optimize_waypoints: bool, optional
"""
widget_args = _directions_options(start, end, waypoints)
widget_args = _directions_options(
start, end, waypoints, avoid_ferries,
avoid_highways, avoid_tolls, optimize_waypoints)
return Directions(**widget_args)
44 changes: 39 additions & 5 deletions gmaps/tests/test_directions.py
Expand Up @@ -13,33 +13,67 @@ def setUp(self):
self.waypoints = [(52.0, 1.0), (52.0, 0.0)]
self.data_array_no_waypoints = [self.start, self.end]
self.data_array = [self.start] + self.waypoints + [self.end]
self.kwargs = {
"avoid_ferries": False,
"avoid_highways": False,
"avoid_tolls": False,
"optimize_waypoints": False
}

def _add_default_options(self, **options):
new_options = self.kwargs.copy()
new_options.update(options)
return new_options

def test_no_waypoints(self):
options = _directions_options(
self.start, self.end, waypoints=None)["data"]
self.start, self.end, waypoints=None,
**self._add_default_options()
)["data"]
assert options == self.data_array_no_waypoints

def test_waypoints(self):
options = _directions_options(
self.start, self.end, self.waypoints)["data"]
self.start, self.end, self.waypoints,
**self._add_default_options()
)["data"]
assert options == self.data_array

def test_no_waypoints_numpy_array(self):
import numpy as np
options = _directions_options(
np.array(self.start), self.end, None)["data"]
np.array(self.start), self.end, None,
**self._add_default_options()
)["data"]
assert options == self.data_array_no_waypoints

def test_waypoints_numpy_array(self):
import numpy as np
options = _directions_options(
np.array(self.start), self.end, np.array(self.waypoints))["data"]
np.array(self.start), self.end, np.array(self.waypoints),
**self._add_default_options()
)["data"]
assert options == self.data_array

def test_pandas_df(self):
pd = pytest.importorskip("pandas")
waypoints = pd.DataFrame.from_records(
self.waypoints, columns=["latitude", "longitude"])
options = _directions_options(
self.start, self.end, waypoints)["data"]
self.start, self.end, waypoints,
**self._add_default_options()
)["data"]
assert options == self.data_array

def test_boolean_options(self):
options = _directions_options(
self.start, self.end, waypoints=None,
avoid_ferries=True,
avoid_highways=True,
avoid_tolls=True,
optimize_waypoints=True
)
assert options["avoid_ferries"]
assert options["avoid_highways"]
assert options["avoid_tolls"]
assert options["optimize_waypoints"]
6 changes: 5 additions & 1 deletion js/src/Directions.js
Expand Up @@ -35,7 +35,11 @@ export class DirectionsLayerView extends GMapsLayerView {
origin: this.getOrigin(modelData),
destination: this.getDestination(modelData),
waypoints: this.getWaypoints(modelData),
travelMode: google.maps.TravelMode.DRIVING
travelMode: google.maps.TravelMode.DRIVING,
avoidFerries: this.model.get("avoid_ferries"),
avoidHighways: this.model.get("avoid_highways"),
avoidTolls: this.model.get("avoid_tolls"),
optimizeWaypoints: this.model.get("optimize_waypoints")
};

const directionsService = new google.maps.DirectionsService();
Expand Down