From dbad2154e669eda381e32d8b3940471124665def Mon Sep 17 00:00:00 2001 From: critical-path <37241479+critical-path@users.noreply.github.com> Date: Sat, 2 Jun 2018 22:26:08 -0400 Subject: [PATCH 01/19] Update README.rst --- README.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.rst b/README.rst index 63a26f7..cc69473 100644 --- a/README.rst +++ b/README.rst @@ -60,6 +60,10 @@ Services - Manage editable collections of GeoJSON features - Persistent storage for custom geographic data +- **Optimization V1** `examples <./docs/optimization.md#optimization>`__, `website `__ + + - Retrieve a duration-optimized route + Please note that there may be some lag between the release of new Mapbox web services and releases of this package. From d4396cd669e1c8089d2efda37ff4c9ba5f2576ee Mon Sep 17 00:00:00 2001 From: critical-path <37241479+critical-path@users.noreply.github.com> Date: Sat, 2 Jun 2018 22:27:21 -0400 Subject: [PATCH 02/19] Update index.rst --- docs/index.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/index.rst b/docs/index.rst index 40d1117..e5b9edf 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -56,6 +56,10 @@ Services - Manage editable collections of GeoJSON features - Persistent storage for custom geographic data +- **Optimization V1** `examples <./docs/optimization.md#optimization>`__, `website `__ + + - Retrieve a duration-optimized route + Please note that there may be some lag between the release of new Mapbox web services and releases of this package. @@ -103,6 +107,7 @@ Documentation analytics.md mapmatching.md static_style.md + optimization.md api/mapbox.rst api/mapbox.services.rst api/modules.rst From 139471316c6430a7abc331724226cc97e2b313c2 Mon Sep 17 00:00:00 2001 From: critical-path <37241479+critical-path@users.noreply.github.com> Date: Sat, 2 Jun 2018 22:33:11 -0400 Subject: [PATCH 03/19] Create optimization.md --- docs/optimization.md | 77 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 docs/optimization.md diff --git a/docs/optimization.md b/docs/optimization.md new file mode 100644 index 0000000..a250f82 --- /dev/null +++ b/docs/optimization.md @@ -0,0 +1,77 @@ +# Optimization + +The `Optimization` class provides access to the Mapbox Optimization API. You can import it from either the `mapbox` module or the `mapbox.services.optimization` module. + +__mapbox__: + +```python +>>> from mapbox import Optimization + +``` + +__mapbox.services.optimization__: + +```python +>>> from mapbox.services.optimization import Optimization + +``` + +See https://www.mapbox.com/api-documentation/#optimization for general documentation of the API. + +Use of the Optimization API requires an access token, which you should set in your environment. For more information, see the [access tokens](access_tokens.md) documentation. + +## Optimization Method + +The public method of the `Optimization` class provides access to the Optimization API and returns an instance of [`requests.Response`](http://docs.python-requests.org/en/latest/api/#requests.Response). + + +## Usage: Retrieving Optimizations + +Instantiate `Optimization`. + +```python +>>> optimization = Optimization() + +``` + +Call the `route` method, passing in values for `features` and `profile`. Pass in values for optional arguments as necessary - `geometries`, `overview`, `steps`, `waypoint_snapping`, `roundtrip`, `source`, `destination`, `distributions`, `annotations`, and `language`. + +```python +>>> features = [ +... { +... "type": "Feature", +... "properties": {}, +... "geometry": +... { +... "type": "Point", +... "coordinates": +... [ +... 0.0, +... 0.0 +... ] +... } +... }, +... { +... "type": "Feature", +... "properties": {}, +... "geometry": +... { +... "type": "Point", +... "coordinates": +... [ +... 1.0, +... 1.0 +... ] +... } +... } +...] +>>> results = optimization.route(features, profile="mapbox/driving") + +``` + +Evaluate whether the request succeeded, and retrieve the optimization object from the response object. + +```python +>>> if results.status_code == 200: +... optimization_object = response.json() +``` From 95effca695dea9a6d951733c29b58e00c0076aad Mon Sep 17 00:00:00 2001 From: critical-path <37241479+critical-path@users.noreply.github.com> Date: Sat, 2 Jun 2018 22:35:16 -0400 Subject: [PATCH 04/19] Update __init__.py --- mapbox/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/mapbox/__init__.py b/mapbox/__init__.py index 82aab53..924db3d 100644 --- a/mapbox/__init__.py +++ b/mapbox/__init__.py @@ -12,3 +12,4 @@ from .services.static_style import StaticStyle from .services.uploads import Uploader from .services.analytics import Analytics +from .services.optimization import Optimization From 6bbb63b090d9abc483f6015497a389a586764ea8 Mon Sep 17 00:00:00 2001 From: critical-path <37241479+critical-path@users.noreply.github.com> Date: Sat, 2 Jun 2018 22:38:01 -0400 Subject: [PATCH 05/19] Update encoding.py --- mapbox/encoding.py | 82 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 81 insertions(+), 1 deletion(-) diff --git a/mapbox/encoding.py b/mapbox/encoding.py index a6e6f96..897ff49 100644 --- a/mapbox/encoding.py +++ b/mapbox/encoding.py @@ -1,6 +1,14 @@ import json -from .errors import InvalidFeatureError +from numbers import Number + +from .compat import string_type + +from .errors import ( + InvalidFeatureError, + InvalidParameterError +) + import polyline @@ -93,3 +101,75 @@ def encode_coordinates_json(features): coords = { 'coordinates': list(read_points(features))} return json.dumps(coords) + + +def validate_snapping(snaps, features): + bearings = [] + radii = [] + if snaps is None: + return (None, None) + if len(snaps) != len(features): + raise InvalidParameterError( + 'Must provide exactly one snapping element for each input feature') + for snap in snaps: + if snap is None: + bearings.append(None) + radii.append(None) + else: + try: + # radius-only + radius = validate_radius(snap) + bearing = None + except InvalidParameterError: + # (radius, angle, range) tuple + try: + radius, angle, rng = snap + except ValueError: + raise InvalidParameterError( + 'waypoint snapping should contain 3 elements: ' + '(bearing, angle, range)') + validate_radius(radius) + + try: + assert angle >= 0 + assert angle <= 360 + assert rng >= 0 + assert rng <= 360 + except (TypeError, AssertionError): + raise InvalidParameterError( + 'angle and range must be between 0 and 360') + bearing = (angle, rng) + + bearings.append(bearing) + radii.append(radius) + + if all([b is None for b in bearings]): + bearings = None + + return (bearings, radii) + + +def validate_radius(radius): + if radius is None: + return None + + if isinstance(radius, string_type): + if radius != 'unlimited': + raise InvalidParameterError( + '{0} is not a valid radius'.format(radius)) + elif isinstance(radius, Number): + if radius <= 0: + raise InvalidParameterError( + 'radius must be greater than zero'.format(radius)) + else: + raise InvalidParameterError( + '{0} is not a valid radius'.format(radius)) + + return radius + + +def encode_bearing(b): + if b is None: + return '' + else: + return '{},{}'.format(*b) From 784d8f8e965472d2db6e52f9a612e079ce8b5827 Mon Sep 17 00:00:00 2001 From: critical-path <37241479+critical-path@users.noreply.github.com> Date: Sat, 2 Jun 2018 22:43:08 -0400 Subject: [PATCH 06/19] Create optimization.py --- mapbox/services/optimization.py | 392 ++++++++++++++++++++++++++++++++ 1 file changed, 392 insertions(+) create mode 100644 mapbox/services/optimization.py diff --git a/mapbox/services/optimization.py b/mapbox/services/optimization.py new file mode 100644 index 0000000..ab5c9c2 --- /dev/null +++ b/mapbox/services/optimization.py @@ -0,0 +1,392 @@ +from mapbox.encoding import ( + encode_bearing, + encode_waypoints, + validate_radius, + validate_snapping +) + +from mapbox.errors import ( + InvalidParameterError, + InvalidProfileError, + ValidationError +) + +from mapbox.services.base import Service + +from uritemplate import URITemplate + + +class Optimization(Service): + """Access to Optimization API V1 + + Attributes + ---------- + api_name : str + The API's name. + + api_version : str + The API's version number. + + valid_profiles : list + The possible values for profile. + + valid_geometries : list + The possible values for geometries. + + valid_overviews : list + The possible values for overview. + + valid_sources : list + The possible valies for source. + + valid_destinations : list + The possible values for destination. + + valid_annotations : list + The possible values for annotations. + + base_uri : str + The API's base URI, currently + https://api.mapbox.com/optimized-trips/v1. + """ + + api_name = "optimized-trips" + + api_version = "v1" + + valid_profiles = [ + "mapbox/cycling", + "mapbox/driving", + "mapbox/walking" + ] + + valid_geometries = [ + "geojson", + "polyline", + "polyline6" + ] + + valid_overviews = [ + "full", + "simplified", + False + ] + + valid_sources = [ + "any", + "first" + ] + + valid_destinations = [ + "any", + "last" + ] + + valid_annotations = [ + "distance", + "duration", + "speed" + ] + + @property + def base_uri(self): + """Forms base URI.""" + + return "https://{}/{}/{}".format( + self.host, + self.api_name, + self.api_version + ) + + def _validate_profile(self, profile): + """Validates profile, raising error if invalid.""" + + if profile not in self.valid_profiles: + raise InvalidProfileError( + "{} is not a valid profile".format(profile) + ) + + return profile + + def _validate_geometry(self, geometry): + """Validates geometry, raising error if invalid.""" + + if geometry is not None\ + and geometry not in self.valid_geometries: + raise InvalidParameterError( + "{} is not a valid geometry format".format(geometry) + ) + + return geometry + + def _validate_overview(self, overview): + """Validates overview, raising error if invalid.""" + + if overview is not None\ + and overview not in self.valid_overviews: + raise InvalidParameterError( + "{} is not a valid geometry overview type".format(overview) + ) + + return overview + + def _validate_source(self, source): + """Validates source, raising error if invalid.""" + + if source is not None\ + and source not in self.valid_sources: + raise InvalidParameterError( + "{} is not a valid source".format(source) + ) + + return source + + def _validate_destination(self, destination): + """Validates destination, raising error if invalid.""" + + if destination is not None\ + and destination not in self.valid_destinations: + raise InvalidParameterError( + "{} is not a valid destination".format(destination) + ) + + return destination + + def _validate_distributions(self, distributions, coordinates): + """Validates distribution pairs, raising error if invalid.""" + + if distributions is None: + return None + + results = [] + + coordinates = coordinates.split(";") + + # The number of distribution pairs must be less + # than or equal to the number of coordinate pairs. + + if len(distributions) > len(coordinates): + raise InvalidParameterError( + "{} are not valid distributions".format(str(distributions)) + ) + + # There must be two values in each distribution pair, + # a pick-up and a drop-off. + + for distribution in distributions: + if len(distribution) != 2: + raise InvalidParameterError( + "{} is not a valid distribution".format(str(distribution)) + ) + + # The values for pick-up and drop-off must not be + # the same. + + pick_up, drop_off = distribution + + if pick_up == drop_off: + raise InvalidParameterError( + "{} is not a valid distribution".format(str(distribution)) + ) + + # The values for pick-up and drop-off must correspond + # to indices of the list of coordinate pairs. + + try: + pick_up = int(pick_up) + coordinates[pick_up] + except IndexError as exception: + raise InvalidParameterError( + "{} is not a valid distribution".format(str(distribution)) + ) + + try: + drop_off = int(drop_off) + coordinates[drop_off] + except IndexError as exception: + raise InvalidParameterError( + "{} is not a valid distribution".format(str(distribution)) + ) + + result = "{},{}".format(pick_up, drop_off) + results.append(result) + + return ";".join(results) + + def _validate_annotations(self, annotations): + """Validates annotations, raising error if invalid.""" + + if annotations is None: + return None + + for annotation in annotations: + if annotation not in self.valid_annotations: + raise InvalidParameterError( + "{} is not a valid annotation".format(annotation) + ) + + return ",".join(annotations) + + def route(self, features, profile="mapbox/driving", + geometries=None, overview=None, steps=None, + waypoint_snapping=None, roundtrip=None, source=None, + destination=None, distributions=None, annotations=None, + language=None): + """The Optimization API returns a duration-optimized route + between the input coordinates. + + Parameters + ---------- + features : iterable + The collection of GeoJSON features used + to define the returned route. + + profile : str + The routing profile. + + The default value is mapbox/driving. + + geometries : str, optional + The format of the returned geometries. + + If None, the default value is polyline. + + overview : str, optional + The type of the returned overview geometry. + + If None, the default value is simplified. + + steps : bool, optional + Whether to return steps and turn-by-turn + instructions. + + If None, the default value is False. + + waypoint_snapping : list, optional + The bearings and radiuses of waypoints in the + returned route. + + roundtrip : bool, optional + Whether the returned route is roundtrip + (the trip ends at the first location). + + If None, the default value is True. + + source : str, optional + The first location of the returned route. + + If None, the default value is any. + + destination : str, optional + The last location of the returned route. + + If None, the default value is any. + + distributions : list, optional + The pick-up and drop-off locations along the + returned route. + + annotations : list, optional + The metadata provided with the returned route. + + language : str, optional + The language of the returned step-by-step + instructions. + + If None, the default value is en. + + Returns + ------- + request.Response + The respone object with the optimization object. + """ + + # Check roundtrip, source, and destination. + + if roundtrip == False\ + and ((source is None) or (destination is None)): + raise ValidationError( + "Source and destination are required if roundtrip is False" + ) + + # Create dict to assist in building URI resource path. + + path_values = dict() + + # Validate profile and update dict. + + profile = self._validate_profile(profile) + name, mode = profile.split("/") + path_values["name"] = name + path_values["mode"] = mode + + # Obtain coordinates and update dict. + + coordinates = encode_waypoints( + features, + precision=6, + min_limit=2, + max_limit=12 + ) + + path_values["coordinates"] = coordinates + + # Build URI resource path. + + path_part = "/{name}/{mode}/{coordinates}" + uri = URITemplate(self.base_uri + path_part).expand(**path_values) + + # Build URI query parameters. + + query_parameters = dict() + + if geometries is not None: + geometries = self._validate_geometry(geometries) + query_parameters["geometries"] = geometries + + if overview is not None: + overview = self._validate_overview(overview) + query_parameters["overview"] = "false" if overview is False else overview + + if steps is not None: + query_parameters["steps"] = "true" if steps is True else "false" + + if waypoint_snapping is not None: + bearings, radiuses = validate_snapping(waypoint_snapping, features) + + if bearings is not None: + bearings = ";".join(encode_bearing(bearing) for bearing in bearings) + query_parameters["bearings"] = bearings + + if radiuses is not None: + radiuses = ";".join(str(radius) for radius in radiuses) + query_parameters["radiuses"] = radiuses + + if roundtrip is not None: + query_parameters["roundtrip"] = "true" if roundtrip is True else "false" + + if source is not None: + source = self._validate_source(source) + query_parameters["source"] = source + + if destination is not None: + destination = self._validate_destination(destination) + query_parameters["destination"] = destination + + if distributions is not None: + distributions = self._validate_distributions(distributions, coordinates) + query_parameters["distributions"] = distributions + + if annotations is not None: + annotations = self._validate_annotations(annotations) + query_parameters["annotations"] = annotations + + if language is not None: + query_parameters["language"] = language + + # Send HTTP GET request. + + response = self.session.get(uri, params=query_parameters) + self.handle_http_error(response) + + return response From 8e14cfb8b8439012509356909806750a15820c49 Mon Sep 17 00:00:00 2001 From: critical-path <37241479+critical-path@users.noreply.github.com> Date: Sat, 2 Jun 2018 22:45:28 -0400 Subject: [PATCH 07/19] Create test_optimization.py --- tests/test_optimization.py | 2508 ++++++++++++++++++++++++++++++++++++ 1 file changed, 2508 insertions(+) create mode 100644 tests/test_optimization.py diff --git a/tests/test_optimization.py b/tests/test_optimization.py new file mode 100644 index 0000000..584e5ee --- /dev/null +++ b/tests/test_optimization.py @@ -0,0 +1,2508 @@ +from mapbox.errors import ( + InvalidParameterError, + InvalidProfileError, + ValidationError +) + +from mapbox.services.optimization import Optimization + +from pytest import raises + +from responses import ( + activate, + add, + GET +) + + +ACCESS_TOKEN = "pk.test" + +FEATURES = [ + { + "type": "Feature", + "properties": {}, + "geometry": + { + "type": "Point", + "coordinates": + [ + 0.0, + 0.0 + ] + } + }, + { + "type": "Feature", + "properties": {}, + "geometry": + { + "type": "Point", + "coordinates": + [ + 1.0, + 1.0 + ] + } + } +] + + +COORDINATES = "0.0,0.0;1.0,1.0" + + +def test_object_properties(): + optimization = Optimization() + + assert optimization.api_name + assert optimization.api_version + assert optimization.valid_profiles + assert optimization.valid_geometries + assert optimization.valid_overviews + assert optimization.valid_annotations + assert optimization.valid_sources + assert optimization.valid_destinations + + +def test_validate_profile(): + optimization = Optimization() + + # invalid value + + with raises(InvalidProfileError) as exception: + profile = "invalid" + result = optimization._validate_profile(profile) + + # valid values + + profiles = [ + "mapbox/cycling", + "mapbox/driving", + "mapbox/walking" + ] + + for profile in profiles: + result = optimization._validate_profile(profile) + assert result == profile + + +def test_validate_geometry(): + optimization = Optimization() + + # invalid value + + with raises(InvalidParameterError) as exception: + geometry = "invalid" + result = optimization._validate_geometry(geometry) + + # valid values + + geometries = [ + "geojson", + "polyline", + "polyline6" + ] + + for geometry in geometries: + result = optimization._validate_geometry(geometry) + assert result == geometry + + +def test_validate_overview(): + optimization = Optimization() + + # invalid value + + with raises(InvalidParameterError) as exception: + overview = "invalid" + result = optimization._validate_overview(overview) + + # valid values + + overviews = [ + "full", + "simplified", + False + ] + + for overview in overviews: + result = optimization._validate_overview(overview) + assert result == overview + + +def test_validate_source(): + optimization = Optimization() + + # invalid value + + with raises(InvalidParameterError) as exception: + source = "invalid" + result = optimization._validate_source(source) + + # valid values + + sources = [ + "any", + "first" + ] + + for source in sources: + result = optimization._validate_source(source) + assert result == source + + +def test_validate_destination(): + optimization = Optimization() + + # invalid value + + with raises(InvalidParameterError) as exception: + destination = "invalid" + result = optimization._validate_destination(destination) + + # valid values + + destinations = [ + "any", + "last" + ] + + for destination in destinations: + result = optimization._validate_destination(destination) + assert result == destination + + +def test_validate_distributions(): + optimization = Optimization() + + # None + + distributions = None + result = optimization._validate_distributions(distributions, COORDINATES) + assert result == distributions + + # invalid value - too many distribution pairs + + with raises(InvalidParameterError) as exception: + distributions = [[0, 1], [0, 1], [0, 1]] + result = optimization._validate_distributions(distributions, COORDINATES) + + # invalid value - too few values in each pair + + with raises(InvalidParameterError) as exception: + distributions = [[0], [0]] + result = optimization._validate_distributions(distributions, COORDINATES) + + # invalid value - too many values in each pair + + with raises(InvalidParameterError) as exception: + distributions = [[0, 1, 0], [0, 1, 0]] + result = optimization._validate_distributions(distributions, COORDINATES) + + # invalid value - values are the same + + with raises(InvalidParameterError) as exception: + distributions = [[0, 0], [0, 0]] + result = optimization._validate_distributions(distributions, COORDINATES) + + # invalid value - first value is not a valid index + + with raises(InvalidParameterError) as exception: + distributions = [[100, 0], [0, 0]] + result = optimization._validate_distributions(distributions, COORDINATES) + + # invalid value - second value is not a valid index + + with raises(InvalidParameterError) as exception: + distributions = [[0, 100], [0, 0]] + result = optimization._validate_distributions(distributions, COORDINATES) + + # valid value + + distributions = [[0, 1],[0, 1]] + result = optimization._validate_distributions(distributions, COORDINATES) + assert result == "0,1;0,1" + + +def test_validate_annotations(): + optimization = Optimization() + + # None + + annotation = None + result = optimization._validate_annotations(annotation) + assert result == annotation + + # invalid value + + with raises(InvalidParameterError) as exception: + annotation = ["invalid"] + result = optimization._validate_annotations(annotation) + + # valid values + + annotations = [ + "distance", + "duration", + "speed" + ] + + result = optimization._validate_annotations(annotations) + assert result == ",".join(annotations) + + +def test_route_error(): + optimization = Optimization(access_token=ACCESS_TOKEN) + + # no source + + with raises(ValidationError) as exception: + response = optimization.route( + FEATURES, + roundtrip=False, + destination="any" + ) + + # no destination + + with raises(ValidationError) as exception: + response = optimization.route( + FEATURES, + roundtrip=False, + source="any" + ) + + # no source, no destination + + with raises(ValidationError) as exception: + response = optimization.route( + FEATURES, + roundtrip=False + ) + + +@activate +def test_route(): + add( + url="https://api.mapbox.com" + + "/optimized-trips/v1" + + "/mapbox/driving" + + "/0.0%2C0.0%3B1.0%2C1.0" + + "?access_token=pk.test", + method=GET, + match_querystring=True, + body="{\"key\": \"value\"}", + status=200 + ) + + optimization = Optimization(access_token=ACCESS_TOKEN) + + response = optimization.route(FEATURES) + + assert response.status_code == 200 + + +@activate +def test_route_with_profile(): + add( + url="https://api.mapbox.com" + + "/optimized-trips/v1" + + "/mapbox/cycling" + + "/0.0%2C0.0%3B1.0%2C1.0" + + "?access_token=pk.test", + method=GET, + match_querystring=True, + body="{\"key\": \"value\"}", + status=200 + ) + + optimization = Optimization(access_token=ACCESS_TOKEN) + + response = optimization.route( + FEATURES, + profile="mapbox/cycling" + ) + + assert response.status_code == 200 + + +@activate +def test_route_with_geometries(): + add( + url="https://api.mapbox.com" + + "/optimized-trips/v1" + + "/mapbox/driving" + + "/0.0%2C0.0%3B1.0%2C1.0" + + "?access_token=pk.test" + + "&geometries=geojson", + method=GET, + match_querystring=True, + body="{\"key\": \"value\"}", + status=200 + ) + + optimization = Optimization(access_token=ACCESS_TOKEN) + + response = optimization.route( + FEATURES, + geometries="geojson" + ) + + assert response.status_code == 200 + + +@activate +def test_route_with_overview(): + add( + url="https://api.mapbox.com" + + "/optimized-trips/v1" + + "/mapbox/driving" + + "/0.0%2C0.0%3B1.0%2C1.0" + + "?access_token=pk.test" + + "&overview=false", + method=GET, + match_querystring=True, + body="{\"key\": \"value\"}", + status=200 + ) + + optimization = Optimization(access_token=ACCESS_TOKEN) + + response = optimization.route( + FEATURES, + overview=False + ) + + assert response.status_code == 200 + + +@activate +def test_route_with_steps(): + add( + url="https://api.mapbox.com" + + "/optimized-trips/v1" + + "/mapbox/driving" + + "/0.0%2C0.0%3B1.0%2C1.0" + + "?access_token=pk.test" + + "&steps=false", + method=GET, + match_querystring=True, + body="{\"key\": \"value\"}", + status=200 + ) + + optimization = Optimization(access_token=ACCESS_TOKEN) + + response = optimization.route( + FEATURES, + steps=False + ) + + assert response.status_code == 200 + + +@activate +def test_route_with_waypoint_snapping(): + add( + url="https://api.mapbox.com" + + "/optimized-trips/v1" + + "/mapbox/driving" + + "/0.0%2C0.0%3B1.0%2C1.0" + + "?access_token=pk.test" + + "&bearings=1%2C1%3B1%2C1" + + "&radiuses=1%3B1", + method=GET, + match_querystring=True, + body="{\"key\": \"value\"}", + status=200 + ) + + optimization = Optimization(access_token=ACCESS_TOKEN) + + response = optimization.route( + FEATURES, + waypoint_snapping=[ + (1, 1, 1), + (1, 1, 1) + ] + ) + + assert response.status_code == 200 + + +@activate +def test_route_with_roundtrip(): + add( + url="https://api.mapbox.com" + + "/optimized-trips/v1" + + "/mapbox/driving" + + "/0.0%2C0.0%3B1.0%2C1.0" + + "?access_token=pk.test" + + "&roundtrip=true", + method=GET, + match_querystring=True, + body="{\"key\": \"value\"}", + status=200 + ) + + optimization = Optimization(access_token=ACCESS_TOKEN) + + response = optimization.route( + FEATURES, + roundtrip=True + ) + + assert response.status_code == 200 + + +@activate +def test_route_with_source(): + add( + url="https://api.mapbox.com" + + "/optimized-trips/v1" + + "/mapbox/driving" + + "/0.0%2C0.0%3B1.0%2C1.0" + + "?access_token=pk.test" + + "&source=any", + method=GET, + match_querystring=True, + body="{\"key\": \"value\"}", + status=200 + ) + + optimization = Optimization(access_token=ACCESS_TOKEN) + + response = optimization.route( + FEATURES, + source="any" + ) + + assert response.status_code == 200 + + +@activate +def test_route_with_destination(): + add( + url="https://api.mapbox.com" + + "/optimized-trips/v1" + + "/mapbox/driving" + + "/0.0%2C0.0%3B1.0%2C1.0" + + "?access_token=pk.test" + + "&destination=any", + method=GET, + match_querystring=True, + body="{\"key\": \"value\"}", + status=200 + ) + + optimization = Optimization(access_token=ACCESS_TOKEN) + + response = optimization.route( + FEATURES, + destination="any" + ) + + assert response.status_code == 200 + + +@activate +def test_route_with_distributions(): + add( + url="https://api.mapbox.com" + + "/optimized-trips/v1" + + "/mapbox/driving" + + "/0.0%2C0.0%3B1.0%2C1.0" + + "?access_token=pk.test" + + "&distributions=0,1", + method=GET, + match_querystring=True, + body="{\"key\": \"value\"}", + status=200 + ) + + optimization = Optimization(access_token=ACCESS_TOKEN) + + response = optimization.route( + FEATURES, + distributions=[ + [0, 1] + ] + ) + + assert response.status_code == 200 + + +@activate +def test_route_with_annotations(): + add( + url="https://api.mapbox.com" + + "/optimized-trips/v1" + + "/mapbox/driving" + + "/0.0%2C0.0%3B1.0%2C1.0" + + "?access_token=pk.test" + + "&annotations=distance,duration,speed", + method=GET, + match_querystring=True, + body="{\"key\": \"value\"}", + status=200 + ) + + optimization = Optimization(access_token=ACCESS_TOKEN) + + response = optimization.route( + FEATURES, + annotations=[ + "distance", + "duration", + "speed" + ] + ) + + assert response.status_code == 200 + + +@activate +def test_route_with_language(): + add( + url="https://api.mapbox.com" + + "/optimized-trips/v1" + + "/mapbox/driving" + + "/0.0%2C0.0%3B1.0%2C1.0" + + "?access_token=pk.test" + + "&language=en", + method=GET, + match_querystring=True, + body="{\"key\": \"value\"}", + status=200 + ) + + optimization = Optimization(access_token=ACCESS_TOKEN) + + response = optimization.route( + FEATURES, + language="en" + ) + + assert response.status_code == 200 + + +@activate +def test_route_with_profile_and_geometries(): + add( + url="https://api.mapbox.com" + + "/optimized-trips/v1" + + "/mapbox/cycling" + + "/0.0%2C0.0%3B1.0%2C1.0" + + "?access_token=pk.test" + + "&geometries=geojson", + method=GET, + match_querystring=True, + body="{\"key\": \"value\"}", + status=200 + ) + + optimization = Optimization(access_token=ACCESS_TOKEN) + + response = optimization.route( + FEATURES, + profile="mapbox/cycling", + geometries="geojson" + ) + + assert response.status_code == 200 + + +@activate +def test_route_with_profile_and_overview(): + add( + url="https://api.mapbox.com" + + "/optimized-trips/v1" + + "/mapbox/cycling" + + "/0.0%2C0.0%3B1.0%2C1.0" + + "?access_token=pk.test" + + "&overview=full", + method=GET, + match_querystring=True, + body="{\"key\": \"value\"}", + status=200 + ) + + optimization = Optimization(access_token=ACCESS_TOKEN) + + response = optimization.route( + FEATURES, + profile="mapbox/cycling", + overview="full" + ) + + assert response.status_code == 200 + + +@activate +def test_route_with_profile_and_steps(): + add( + url="https://api.mapbox.com" + + "/optimized-trips/v1" + + "/mapbox/cycling" + + "/0.0%2C0.0%3B1.0%2C1.0" + + "?access_token=pk.test" + + "&steps=true", + method=GET, + match_querystring=True, + body="{\"key\": \"value\"}", + status=200 + ) + + optimization = Optimization(access_token=ACCESS_TOKEN) + + response = optimization.route( + FEATURES, + profile="mapbox/cycling", + steps=True + ) + + assert response.status_code == 200 + + +@activate +def test_route_with_profile_and_waypoint_snapping(): + add( + url="https://api.mapbox.com" + + "/optimized-trips/v1" + + "/mapbox/cycling" + + "/0.0%2C0.0%3B1.0%2C1.0" + + "?access_token=pk.test" + + "&bearings=1%2C1%3B1%2C1" + + "&radiuses=1%3B1", + method=GET, + match_querystring=True, + body="{\"key\": \"value\"}", + status=200 + ) + + optimization = Optimization(access_token=ACCESS_TOKEN) + + response = optimization.route( + FEATURES, + profile="mapbox/cycling", + waypoint_snapping=[ + (1, 1, 1), + (1, 1, 1) + ] + ) + + assert response.status_code == 200 + + +@activate +def test_route_with_profile_and_roundtrip(): + add( + url="https://api.mapbox.com" + + "/optimized-trips/v1" + + "/mapbox/cycling" + + "/0.0%2C0.0%3B1.0%2C1.0" + + "?access_token=pk.test" + + "&roundtrip=true", + method=GET, + match_querystring=True, + body="{\"key\": \"value\"}", + status=200 + ) + + optimization = Optimization(access_token=ACCESS_TOKEN) + + response = optimization.route( + FEATURES, + profile="mapbox/cycling", + roundtrip=True + ) + + assert response.status_code == 200 + + +@activate +def test_route_with_profile_and_source(): + add( + url="https://api.mapbox.com" + + "/optimized-trips/v1" + + "/mapbox/cycling" + + "/0.0%2C0.0%3B1.0%2C1.0" + + "?access_token=pk.test" + + "&source=any", + method=GET, + match_querystring=True, + body="{\"key\": \"value\"}", + status=200 + ) + + optimization = Optimization(access_token=ACCESS_TOKEN) + + response = optimization.route( + FEATURES, + profile="mapbox/cycling", + source="any" + ) + + assert response.status_code == 200 + + +@activate +def test_route_with_profile_and_destination(): + add( + url="https://api.mapbox.com" + + "/optimized-trips/v1" + + "/mapbox/cycling" + + "/0.0%2C0.0%3B1.0%2C1.0" + + "?access_token=pk.test" + + "&destination=any", + method=GET, + match_querystring=True, + body="{\"key\": \"value\"}", + status=200 + ) + + optimization = Optimization(access_token=ACCESS_TOKEN) + + response = optimization.route( + FEATURES, + profile="mapbox/cycling", + destination="any" + ) + + assert response.status_code == 200 + + +@activate +def test_route_with_profile_and_distributions(): + add( + url="https://api.mapbox.com" + + "/optimized-trips/v1" + + "/mapbox/cycling" + + "/0.0%2C0.0%3B1.0%2C1.0" + + "?access_token=pk.test" + + "&distributions=0,1", + method=GET, + match_querystring=True, + body="{\"key\": \"value\"}", + status=200 + ) + + optimization = Optimization(access_token=ACCESS_TOKEN) + + response = optimization.route( + FEATURES, + profile="mapbox/cycling", + distributions=[ + [0, 1] + ] + ) + + assert response.status_code == 200 + + +@activate +def test_route_with_profile_and_annotations(): + add( + url="https://api.mapbox.com" + + "/optimized-trips/v1" + + "/mapbox/cycling" + + "/0.0%2C0.0%3B1.0%2C1.0" + + "?access_token=pk.test" + + "&annotations=distance,duration,speed", + method=GET, + match_querystring=True, + body="{\"key\": \"value\"}", + status=200 + ) + + optimization = Optimization(access_token=ACCESS_TOKEN) + + response = optimization.route( + FEATURES, + profile="mapbox/cycling", + annotations=[ + "distance", + "duration", + "speed" + ] + ) + + assert response.status_code == 200 + + +@activate +def test_route_with_profile_and_language(): + add( + url="https://api.mapbox.com" + + "/optimized-trips/v1" + + "/mapbox/cycling" + + "/0.0%2C0.0%3B1.0%2C1.0" + + "?access_token=pk.test" + + "&language=en", + method=GET, + match_querystring=True, + body="{\"key\": \"value\"}", + status=200 + ) + + optimization = Optimization(access_token=ACCESS_TOKEN) + + response = optimization.route( + FEATURES, + profile="mapbox/cycling", + language="en" + ) + + assert response.status_code == 200 + + +@activate +def test_route_with_profile_geometries_and_overview(): + add( + url="https://api.mapbox.com" + + "/optimized-trips/v1" + + "/mapbox/cycling" + + "/0.0%2C0.0%3B1.0%2C1.0" + + "?access_token=pk.test" + + "&geometries=geojson" + + "&overview=full", + method=GET, + match_querystring=True, + body="{\"key\": \"value\"}", + status=200 + ) + + optimization = Optimization(access_token=ACCESS_TOKEN) + + response = optimization.route( + FEATURES, + profile="mapbox/cycling", + geometries="geojson", + overview="full" + ) + + assert response.status_code == 200 + + +@activate +def test_route_with_profile_geometries_and_steps(): + add( + url="https://api.mapbox.com" + + "/optimized-trips/v1" + + "/mapbox/cycling" + + "/0.0%2C0.0%3B1.0%2C1.0" + + "?access_token=pk.test" + + "&geometries=geojson" + + "&steps=true", + method=GET, + match_querystring=True, + body="{\"key\": \"value\"}", + status=200 + ) + + optimization = Optimization(access_token=ACCESS_TOKEN) + + response = optimization.route( + FEATURES, + profile="mapbox/cycling", + geometries="geojson", + steps=True + ) + + assert response.status_code == 200 + + +@activate +def test_route_with_profile_geometries_and_waypoint_snapping(): + add( + url="https://api.mapbox.com" + + "/optimized-trips/v1" + + "/mapbox/cycling" + + "/0.0%2C0.0%3B1.0%2C1.0" + + "?access_token=pk.test" + + "&geometries=geojson" + + "&bearings=1%2C1%3B1%2C1" + + "&radiuses=1%3B1", + method=GET, + match_querystring=True, + body="{\"key\": \"value\"}", + status=200 + ) + + optimization = Optimization(access_token=ACCESS_TOKEN) + + response = optimization.route( + FEATURES, + profile="mapbox/cycling", + geometries="geojson", + waypoint_snapping=[ + (1, 1, 1), + (1, 1, 1) + ] + ) + + assert response.status_code == 200 + + +@activate +def test_route_with_profile_geometries_and_roundtrip(): + add( + url="https://api.mapbox.com" + + "/optimized-trips/v1" + + "/mapbox/cycling" + + "/0.0%2C0.0%3B1.0%2C1.0" + + "?access_token=pk.test" + + "&geometries=geojson" + + "&roundtrip=true", + method=GET, + match_querystring=True, + body="{\"key\": \"value\"}", + status=200 + ) + + optimization = Optimization(access_token=ACCESS_TOKEN) + + response = optimization.route( + FEATURES, + profile="mapbox/cycling", + geometries="geojson", + roundtrip=True + ) + + assert response.status_code == 200 + + +@activate +def test_route_with_profile_geometries_and_source(): + add( + url="https://api.mapbox.com" + + "/optimized-trips/v1" + + "/mapbox/cycling" + + "/0.0%2C0.0%3B1.0%2C1.0" + + "?access_token=pk.test" + + "&geometries=geojson" + + "&source=any", + method=GET, + match_querystring=True, + body="{\"key\": \"value\"}", + status=200 + ) + + optimization = Optimization(access_token=ACCESS_TOKEN) + + response = optimization.route( + FEATURES, + profile="mapbox/cycling", + geometries="geojson", + source="any" + ) + + assert response.status_code == 200 + + +@activate +def test_route_with_profile_geometries_and_destination(): + add( + url="https://api.mapbox.com" + + "/optimized-trips/v1" + + "/mapbox/cycling" + + "/0.0%2C0.0%3B1.0%2C1.0" + + "?access_token=pk.test" + + "&geometries=geojson" + + "&destination=any", + method=GET, + match_querystring=True, + body="{\"key\": \"value\"}", + status=200 + ) + + optimization = Optimization(access_token=ACCESS_TOKEN) + + response = optimization.route( + FEATURES, + profile="mapbox/cycling", + geometries="geojson", + destination="any" + ) + + assert response.status_code == 200 + + +@activate +def test_route_with_profile_geometries_and_distributions(): + add( + url="https://api.mapbox.com" + + "/optimized-trips/v1" + + "/mapbox/cycling" + + "/0.0%2C0.0%3B1.0%2C1.0" + + "?access_token=pk.test" + + "&geometries=geojson" + + "&distributions=0,1", + method=GET, + match_querystring=True, + body="{\"key\": \"value\"}", + status=200 + ) + + optimization = Optimization(access_token=ACCESS_TOKEN) + + response = optimization.route( + FEATURES, + profile="mapbox/cycling", + geometries="geojson", + distributions=[ + [0, 1] + ] + ) + + assert response.status_code == 200 + + +@activate +def test_route_with_profile_geometries_and_annotations(): + add( + url="https://api.mapbox.com" + + "/optimized-trips/v1" + + "/mapbox/cycling" + + "/0.0%2C0.0%3B1.0%2C1.0" + + "?access_token=pk.test" + + "&geometries=geojson" + + "&annotations=distance,duration,speed", + method=GET, + match_querystring=True, + body="{\"key\": \"value\"}", + status=200 + ) + + optimization = Optimization(access_token=ACCESS_TOKEN) + + response = optimization.route( + FEATURES, + profile="mapbox/cycling", + geometries="geojson", + annotations=[ + "distance", + "duration", + "speed" + ] + ) + + assert response.status_code == 200 + + +@activate +def test_route_with_profile_geometries_and_language(): + add( + url="https://api.mapbox.com" + + "/optimized-trips/v1" + + "/mapbox/cycling" + + "/0.0%2C0.0%3B1.0%2C1.0" + + "?access_token=pk.test" + + "&geometries=geojson" + + "&language=en", + method=GET, + match_querystring=True, + body="{\"key\": \"value\"}", + status=200 + ) + + optimization = Optimization(access_token=ACCESS_TOKEN) + + response = optimization.route( + FEATURES, + profile="mapbox/cycling", + geometries="geojson", + language="en" + ) + + assert response.status_code == 200 + + +@activate +def test_route_with_profile_geometries_overview_and_steps(): + add( + url="https://api.mapbox.com" + + "/optimized-trips/v1" + + "/mapbox/cycling" + + "/0.0%2C0.0%3B1.0%2C1.0" + + "?access_token=pk.test" + + "&geometries=geojson" + + "&overview=full" + + "&steps=true", + method=GET, + match_querystring=True, + body="{\"key\": \"value\"}", + status=200 + ) + + optimization = Optimization(access_token=ACCESS_TOKEN) + + response = optimization.route( + FEATURES, + profile="mapbox/cycling", + geometries="geojson", + overview="full", + steps=True + ) + + assert response.status_code == 200 + + +@activate +def test_route_with_profile_geometries_overview_and_waypoint_snapping(): + add( + url="https://api.mapbox.com" + + "/optimized-trips/v1" + + "/mapbox/cycling" + + "/0.0%2C0.0%3B1.0%2C1.0" + + "?access_token=pk.test" + + "&geometries=geojson" + + "&overview=full" + + "&bearings=1%2C1%3B1%2C1" + + "&radiuses=1%3B1", + method=GET, + match_querystring=True, + body="{\"key\": \"value\"}", + status=200 + ) + + optimization = Optimization(access_token=ACCESS_TOKEN) + + response = optimization.route( + FEATURES, + profile="mapbox/cycling", + geometries="geojson", + overview="full", + waypoint_snapping=[ + (1, 1, 1), + (1, 1, 1) + ] + ) + + assert response.status_code == 200 + + + +@activate +def test_route_with_profile_geometries_overview_and_roundtrip(): + add( + url="https://api.mapbox.com" + + "/optimized-trips/v1" + + "/mapbox/cycling" + + "/0.0%2C0.0%3B1.0%2C1.0" + + "?access_token=pk.test" + + "&geometries=geojson" + + "&overview=full" + + "&roundtrip=true", + method=GET, + match_querystring=True, + body="{\"key\": \"value\"}", + status=200 + ) + + optimization = Optimization(access_token=ACCESS_TOKEN) + + response = optimization.route( + FEATURES, + profile="mapbox/cycling", + geometries="geojson", + overview="full", + roundtrip=True + ) + + assert response.status_code == 200 + + +@activate +def test_route_with_profile_geometries_overview_and_source(): + add( + url="https://api.mapbox.com" + + "/optimized-trips/v1" + + "/mapbox/cycling" + + "/0.0%2C0.0%3B1.0%2C1.0" + + "?access_token=pk.test" + + "&geometries=geojson" + + "&overview=full" + + "&source=any", + method=GET, + match_querystring=True, + body="{\"key\": \"value\"}", + status=200 + ) + + optimization = Optimization(access_token=ACCESS_TOKEN) + + response = optimization.route( + FEATURES, + profile="mapbox/cycling", + geometries="geojson", + overview="full", + source="any" + ) + + assert response.status_code == 200 + + +@activate +def test_route_with_profile_geometries_overview_and_destination(): + add( + url="https://api.mapbox.com" + + "/optimized-trips/v1" + + "/mapbox/cycling" + + "/0.0%2C0.0%3B1.0%2C1.0" + + "?access_token=pk.test" + + "&geometries=geojson" + + "&overview=full" + + "&destination=any", + method=GET, + match_querystring=True, + body="{\"key\": \"value\"}", + status=200 + ) + + optimization = Optimization(access_token=ACCESS_TOKEN) + + response = optimization.route( + FEATURES, + profile="mapbox/cycling", + geometries="geojson", + overview="full", + destination="any" + ) + + assert response.status_code == 200 + + +@activate +def test_route_with_profile_geometries_overview_and_distributions(): + add( + url="https://api.mapbox.com" + + "/optimized-trips/v1" + + "/mapbox/cycling" + + "/0.0%2C0.0%3B1.0%2C1.0" + + "?access_token=pk.test" + + "&geometries=geojson" + + "&overview=full" + + "&distributions=0,1", + method=GET, + match_querystring=True, + body="{\"key\": \"value\"}", + status=200 + ) + + optimization = Optimization(access_token=ACCESS_TOKEN) + + response = optimization.route( + FEATURES, + profile="mapbox/cycling", + geometries="geojson", + overview="full", + distributions=[ + [0, 1] + ] + ) + + assert response.status_code == 200 + + +@activate +def test_route_with_profile_geometries_overview_and_annotations(): + add( + url="https://api.mapbox.com" + + "/optimized-trips/v1" + + "/mapbox/cycling" + + "/0.0%2C0.0%3B1.0%2C1.0" + + "?access_token=pk.test" + + "&geometries=geojson" + + "&overview=full" + + "&annotations=distance,duration,speed", + method=GET, + match_querystring=True, + body="{\"key\": \"value\"}", + status=200 + ) + + optimization = Optimization(access_token=ACCESS_TOKEN) + + response = optimization.route( + FEATURES, + profile="mapbox/cycling", + geometries="geojson", + overview="full", + annotations=[ + "distance", + "duration", + "speed" + ] + ) + + assert response.status_code == 200 + + +@activate +def test_route_with_profile_geometries_overview_and_language(): + add( + url="https://api.mapbox.com" + + "/optimized-trips/v1" + + "/mapbox/cycling" + + "/0.0%2C0.0%3B1.0%2C1.0" + + "?access_token=pk.test" + + "&geometries=geojson" + + "&overview=full" + + "&language=en", + method=GET, + match_querystring=True, + body="{\"key\": \"value\"}", + status=200 + ) + + optimization = Optimization(access_token=ACCESS_TOKEN) + + response = optimization.route( + FEATURES, + profile="mapbox/cycling", + geometries="geojson", + overview="full", + language="en" + ) + + assert response.status_code == 200 + + +@activate +def test_route_with_profile_geometries_overview_steps_and_waypoint_snapping(): + add( + url="https://api.mapbox.com" + + "/optimized-trips/v1" + + "/mapbox/cycling" + + "/0.0%2C0.0%3B1.0%2C1.0" + + "?access_token=pk.test" + + "&geometries=geojson" + + "&overview=full" + + "&steps=true" + + "&bearings=1%2C1%3B1%2C1" + + "&radiuses=1%3B1", + method=GET, + match_querystring=True, + body="{\"key\": \"value\"}", + status=200 + ) + + optimization = Optimization(access_token=ACCESS_TOKEN) + + response = optimization.route( + FEATURES, + profile="mapbox/cycling", + geometries="geojson", + overview="full", + steps=True, + waypoint_snapping=[ + (1, 1, 1), + (1, 1, 1) + ] + ) + + assert response.status_code == 200 + + +@activate +def test_route_with_profile_geometries_overview_steps_and_roundtrip(): + add( + url="https://api.mapbox.com" + + "/optimized-trips/v1" + + "/mapbox/cycling" + + "/0.0%2C0.0%3B1.0%2C1.0" + + "?access_token=pk.test" + + "&geometries=geojson" + + "&overview=full" + + "&steps=true" + + "&roundtrip=true", + method=GET, + match_querystring=True, + body="{\"key\": \"value\"}", + status=200 + ) + + optimization = Optimization(access_token=ACCESS_TOKEN) + + response = optimization.route( + FEATURES, + profile="mapbox/cycling", + geometries="geojson", + overview="full", + steps=True, + roundtrip=True + ) + + assert response.status_code == 200 + + +@activate +def test_route_with_profile_geometries_overview_steps_and_source(): + add( + url="https://api.mapbox.com" + + "/optimized-trips/v1" + + "/mapbox/cycling" + + "/0.0%2C0.0%3B1.0%2C1.0" + + "?access_token=pk.test" + + "&geometries=geojson" + + "&overview=full" + + "&steps=true" + + "&source=any", + method=GET, + match_querystring=True, + body="{\"key\": \"value\"}", + status=200 + ) + + optimization = Optimization(access_token=ACCESS_TOKEN) + + response = optimization.route( + FEATURES, + profile="mapbox/cycling", + geometries="geojson", + overview="full", + steps=True, + source="any" + ) + + assert response.status_code == 200 + + +@activate +def test_route_with_profile_geometries_overview_steps_and_destination(): + add( + url="https://api.mapbox.com" + + "/optimized-trips/v1" + + "/mapbox/cycling" + + "/0.0%2C0.0%3B1.0%2C1.0" + + "?access_token=pk.test" + + "&geometries=geojson" + + "&overview=full" + + "&steps=true" + + "&destination=any", + method=GET, + match_querystring=True, + body="{\"key\": \"value\"}", + status=200 + ) + + optimization = Optimization(access_token=ACCESS_TOKEN) + + response = optimization.route( + FEATURES, + profile="mapbox/cycling", + geometries="geojson", + overview="full", + steps=True, + destination="any" + ) + + assert response.status_code == 200 + + +@activate +def test_route_with_profile_geometries_overview_steps_and_distributions(): + add( + url="https://api.mapbox.com" + + "/optimized-trips/v1" + + "/mapbox/cycling" + + "/0.0%2C0.0%3B1.0%2C1.0" + + "?access_token=pk.test" + + "&geometries=geojson" + + "&overview=full" + + "&steps=true" + + "&distributions=0,1", + method=GET, + match_querystring=True, + body="{\"key\": \"value\"}", + status=200 + ) + + optimization = Optimization(access_token=ACCESS_TOKEN) + + response = optimization.route( + FEATURES, + profile="mapbox/cycling", + geometries="geojson", + overview="full", + steps=True, + distributions=[ + [0, 1] + ] + ) + + assert response.status_code == 200 + + +@activate +def test_route_with_profile_geometries_overview_steps_and_annotations(): + add( + url="https://api.mapbox.com" + + "/optimized-trips/v1" + + "/mapbox/cycling" + + "/0.0%2C0.0%3B1.0%2C1.0" + + "?access_token=pk.test" + + "&geometries=geojson" + + "&overview=full" + + "&steps=true" + + "&annotations=distance,duration,speed", + method=GET, + match_querystring=True, + body="{\"key\": \"value\"}", + status=200 + ) + + optimization = Optimization(access_token=ACCESS_TOKEN) + + response = optimization.route( + FEATURES, + profile="mapbox/cycling", + geometries="geojson", + overview="full", + steps=True, + annotations=[ + "distance", + "duration", + "speed" + ] + ) + + assert response.status_code == 200 + + +@activate +def test_route_with_profile_geometries_overview_steps_and_language(): + add( + url="https://api.mapbox.com" + + "/optimized-trips/v1" + + "/mapbox/cycling" + + "/0.0%2C0.0%3B1.0%2C1.0" + + "?access_token=pk.test" + + "&geometries=geojson" + + "&overview=full" + + "&steps=true" + + "&language=en", + method=GET, + match_querystring=True, + body="{\"key\": \"value\"}", + status=200 + ) + + optimization = Optimization(access_token=ACCESS_TOKEN) + + response = optimization.route( + FEATURES, + profile="mapbox/cycling", + geometries="geojson", + overview="full", + steps=True, + language="en" + ) + + assert response.status_code == 200 + + +@activate +def test_route_with_profile_geometries_overview_steps_waypoint_snapping_and_roundtrip(): + add( + url="https://api.mapbox.com" + + "/optimized-trips/v1" + + "/mapbox/cycling" + + "/0.0%2C0.0%3B1.0%2C1.0" + + "?access_token=pk.test" + + "&geometries=geojson" + + "&overview=full" + + "&steps=true" + + "&bearings=1%2C1%3B1%2C1" + + "&radiuses=1%3B1" + + "&roundtrip=true", + method=GET, + match_querystring=True, + body="{\"key\": \"value\"}", + status=200 + ) + + optimization = Optimization(access_token=ACCESS_TOKEN) + + response = optimization.route( + FEATURES, + profile="mapbox/cycling", + geometries="geojson", + overview="full", + steps=True, + waypoint_snapping=[ + (1, 1, 1), + (1, 1, 1) + ], + roundtrip=True + ) + + assert response.status_code == 200 + + +@activate +def test_route_with_profile_geometries_overview_steps_waypoint_snapping_and_source(): + add( + url="https://api.mapbox.com" + + "/optimized-trips/v1" + + "/mapbox/cycling" + + "/0.0%2C0.0%3B1.0%2C1.0" + + "?access_token=pk.test" + + "&geometries=geojson" + + "&overview=full" + + "&steps=true" + + "&bearings=1%2C1%3B1%2C1" + + "&radiuses=1%3B1" + + "&source=any", + method=GET, + match_querystring=True, + body="{\"key\": \"value\"}", + status=200 + ) + + optimization = Optimization(access_token=ACCESS_TOKEN) + + response = optimization.route( + FEATURES, + profile="mapbox/cycling", + geometries="geojson", + overview="full", + steps=True, + waypoint_snapping=[ + (1, 1, 1), + (1, 1, 1) + ], + source="any" + ) + + assert response.status_code == 200 + + +@activate +def test_route_with_profile_geometries_overview_steps_waypoint_snapping_and_destination(): + add( + url="https://api.mapbox.com" + + "/optimized-trips/v1" + + "/mapbox/cycling" + + "/0.0%2C0.0%3B1.0%2C1.0" + + "?access_token=pk.test" + + "&geometries=geojson" + + "&overview=full" + + "&steps=true" + + "&bearings=1%2C1%3B1%2C1" + + "&radiuses=1%3B1" + + "&destination=any", + method=GET, + match_querystring=True, + body="{\"key\": \"value\"}", + status=200 + ) + + optimization = Optimization(access_token=ACCESS_TOKEN) + + response = optimization.route( + FEATURES, + profile="mapbox/cycling", + geometries="geojson", + overview="full", + steps=True, + waypoint_snapping=[ + (1, 1, 1), + (1, 1, 1) + ], + destination="any" + ) + + assert response.status_code == 200 + + +@activate +def test_route_with_profile_geometries_overview_steps_waypoint_snapping_and_distributions(): + add( + url="https://api.mapbox.com" + + "/optimized-trips/v1" + + "/mapbox/cycling" + + "/0.0%2C0.0%3B1.0%2C1.0" + + "?access_token=pk.test" + + "&geometries=geojson" + + "&overview=full" + + "&steps=true" + + "&bearings=1%2C1%3B1%2C1" + + "&radiuses=1%3B1" + + "&distributions=0,1", + method=GET, + match_querystring=True, + body="{\"key\": \"value\"}", + status=200 + ) + + optimization = Optimization(access_token=ACCESS_TOKEN) + + response = optimization.route( + FEATURES, + profile="mapbox/cycling", + geometries="geojson", + overview="full", + steps=True, + waypoint_snapping=[ + (1, 1, 1), + (1, 1, 1) + ], + distributions=[ + [0, 1] + ] + ) + + assert response.status_code == 200 + + +@activate +def test_route_with_profile_geometries_overview_steps_waypoint_snapping_and_annotations(): + add( + url="https://api.mapbox.com" + + "/optimized-trips/v1" + + "/mapbox/cycling" + + "/0.0%2C0.0%3B1.0%2C1.0" + + "?access_token=pk.test" + + "&geometries=geojson" + + "&overview=full" + + "&steps=true" + + "&bearings=1%2C1%3B1%2C1" + + "&radiuses=1%3B1" + + "&annotations=distance,duration,speed", + method=GET, + match_querystring=True, + body="{\"key\": \"value\"}", + status=200 + ) + + optimization = Optimization(access_token=ACCESS_TOKEN) + + response = optimization.route( + FEATURES, + profile="mapbox/cycling", + geometries="geojson", + overview="full", + steps=True, + waypoint_snapping=[ + (1, 1, 1), + (1, 1, 1) + ], + annotations=[ + "distance", + "duration", + "speed" + ] + ) + + assert response.status_code == 200 + + +@activate +def test_route_with_profile_geometries_overview_steps_waypoint_snapping_and_language(): + add( + url="https://api.mapbox.com" + + "/optimized-trips/v1" + + "/mapbox/cycling" + + "/0.0%2C0.0%3B1.0%2C1.0" + + "?access_token=pk.test" + + "&geometries=geojson" + + "&overview=full" + + "&steps=true" + + "&bearings=1%2C1%3B1%2C1" + + "&radiuses=1%3B1" + + "&language=en", + method=GET, + match_querystring=True, + body="{\"key\": \"value\"}", + status=200 + ) + + optimization = Optimization(access_token=ACCESS_TOKEN) + + response = optimization.route( + FEATURES, + profile="mapbox/cycling", + geometries="geojson", + overview="full", + steps=True, + waypoint_snapping=[ + (1, 1, 1), + (1, 1, 1) + ], + language="en" + ) + + assert response.status_code == 200 + + +@activate +def test_route_with_profile_geometries_overview_steps_waypoint_snapping_roundtrip_and_source(): + add( + url="https://api.mapbox.com" + + "/optimized-trips/v1" + + "/mapbox/cycling" + + "/0.0%2C0.0%3B1.0%2C1.0" + + "?access_token=pk.test" + + "&geometries=geojson" + + "&overview=full" + + "&steps=true" + + "&bearings=1%2C1%3B1%2C1" + + "&radiuses=1%3B1" + + "&roundtrip=true" + + "&source=any", + method=GET, + match_querystring=True, + body="{\"key\": \"value\"}", + status=200 + ) + + optimization = Optimization(access_token=ACCESS_TOKEN) + + response = optimization.route( + FEATURES, + profile="mapbox/cycling", + geometries="geojson", + overview="full", + steps=True, + waypoint_snapping=[ + (1, 1, 1), + (1, 1, 1) + ], + roundtrip=True, + source="any" + ) + + assert response.status_code == 200 + + +@activate +def test_route_with_profile_geometries_overview_steps_waypoint_snapping_roundtrip_and_destination(): + add( + url="https://api.mapbox.com" + + "/optimized-trips/v1" + + "/mapbox/cycling" + + "/0.0%2C0.0%3B1.0%2C1.0" + + "?access_token=pk.test" + + "&geometries=geojson" + + "&overview=full" + + "&steps=true" + + "&bearings=1%2C1%3B1%2C1" + + "&radiuses=1%3B1" + + "&roundtrip=true" + + "&destination=any", + method=GET, + match_querystring=True, + body="{\"key\": \"value\"}", + status=200 + ) + + optimization = Optimization(access_token=ACCESS_TOKEN) + + response = optimization.route( + FEATURES, + profile="mapbox/cycling", + geometries="geojson", + overview="full", + steps=True, + waypoint_snapping=[ + (1, 1, 1), + (1, 1, 1) + ], + roundtrip=True, + destination="any" + ) + + assert response.status_code == 200 + + +@activate +def test_route_with_profile_geometries_overview_steps_waypoint_snapping_roundtrip_and_distributions(): + add( + url="https://api.mapbox.com" + + "/optimized-trips/v1" + + "/mapbox/cycling" + + "/0.0%2C0.0%3B1.0%2C1.0" + + "?access_token=pk.test" + + "&geometries=geojson" + + "&overview=full" + + "&steps=true" + + "&bearings=1%2C1%3B1%2C1" + + "&radiuses=1%3B1" + + "&roundtrip=true" + + "&distributions=0,1", + method=GET, + match_querystring=True, + body="{\"key\": \"value\"}", + status=200 + ) + + optimization = Optimization(access_token=ACCESS_TOKEN) + + response = optimization.route( + FEATURES, + profile="mapbox/cycling", + geometries="geojson", + overview="full", + steps=True, + waypoint_snapping=[ + (1, 1, 1), + (1, 1, 1) + ], + roundtrip=True, + distributions=[ + [0, 1] + ] + ) + + assert response.status_code == 200 + + +@activate +def test_route_with_profile_geometries_overview_steps_waypoint_snapping_roundtrip_and_annotations(): + add( + url="https://api.mapbox.com" + + "/optimized-trips/v1" + + "/mapbox/cycling" + + "/0.0%2C0.0%3B1.0%2C1.0" + + "?access_token=pk.test" + + "&geometries=geojson" + + "&overview=full" + + "&steps=true" + + "&bearings=1%2C1%3B1%2C1" + + "&radiuses=1%3B1" + + "&roundtrip=true" + + "&annotations=distance,duration,speed", + method=GET, + match_querystring=True, + body="{\"key\": \"value\"}", + status=200 + ) + + optimization = Optimization(access_token=ACCESS_TOKEN) + + response = optimization.route( + FEATURES, + profile="mapbox/cycling", + geometries="geojson", + overview="full", + steps=True, + waypoint_snapping=[ + (1, 1, 1), + (1, 1, 1) + ], + roundtrip=True, + annotations=[ + "distance", + "duration", + "speed" + ] + ) + + assert response.status_code == 200 + + +@activate +def test_route_with_profile_geometries_overview_steps_waypoint_snapping_roundtrip_and_language(): + add( + url="https://api.mapbox.com" + + "/optimized-trips/v1" + + "/mapbox/cycling" + + "/0.0%2C0.0%3B1.0%2C1.0" + + "?access_token=pk.test" + + "&geometries=geojson" + + "&overview=full" + + "&steps=true" + + "&bearings=1%2C1%3B1%2C1" + + "&radiuses=1%3B1" + + "&roundtrip=true" + + "&language=en", + method=GET, + match_querystring=True, + body="{\"key\": \"value\"}", + status=200 + ) + + optimization = Optimization(access_token=ACCESS_TOKEN) + + response = optimization.route( + FEATURES, + profile="mapbox/cycling", + geometries="geojson", + overview="full", + steps=True, + waypoint_snapping=[ + (1, 1, 1), + (1, 1, 1) + ], + roundtrip=True, + language="en" + ) + + assert response.status_code == 200 + + +@activate +def test_route_with_profile_geometries_overview_steps_waypoint_snapping_roundtrip_source_and_destination(): + add( + url="https://api.mapbox.com" + + "/optimized-trips/v1" + + "/mapbox/cycling" + + "/0.0%2C0.0%3B1.0%2C1.0" + + "?access_token=pk.test" + + "&geometries=geojson" + + "&overview=full" + + "&steps=true" + + "&bearings=1%2C1%3B1%2C1" + + "&radiuses=1%3B1" + + "&roundtrip=true" + + "&source=any" + + "&destination=any", + method=GET, + match_querystring=True, + body="{\"key\": \"value\"}", + status=200 + ) + + optimization = Optimization(access_token=ACCESS_TOKEN) + + response = optimization.route( + FEATURES, + profile="mapbox/cycling", + geometries="geojson", + overview="full", + roundtrip=True, + steps=True, + waypoint_snapping=[ + (1, 1, 1), + (1, 1, 1) + ], + source="any", + destination="any" + ) + + assert response.status_code == 200 + + +@activate +def test_route_with_profile_geometries_overview_steps_waypoint_snapping_roundtrip_source_and_distributions(): + add( + url="https://api.mapbox.com" + + "/optimized-trips/v1" + + "/mapbox/cycling" + + "/0.0%2C0.0%3B1.0%2C1.0" + + "?access_token=pk.test" + + "&geometries=geojson" + + "&overview=full" + + "&steps=true" + + "&bearings=1%2C1%3B1%2C1" + + "&radiuses=1%3B1" + + "&roundtrip=true" + + "&source=any" + + "&distributions=0,1", + method=GET, + match_querystring=True, + body="{\"key\": \"value\"}", + status=200 + ) + + optimization = Optimization(access_token=ACCESS_TOKEN) + + response = optimization.route( + FEATURES, + profile="mapbox/cycling", + geometries="geojson", + overview="full", + steps=True, + waypoint_snapping=[ + (1, 1, 1), + (1, 1, 1) + ], + roundtrip=True, + source="any", + distributions=[ + [0, 1] + ] + ) + + assert response.status_code == 200 + + +@activate +def test_route_with_profile_geometries_overview_steps_waypoint_snapping_roundtrip_source_and_annotations(): + add( + url="https://api.mapbox.com" + + "/optimized-trips/v1" + + "/mapbox/cycling" + + "/0.0%2C0.0%3B1.0%2C1.0" + + "?access_token=pk.test" + + "&geometries=geojson" + + "&overview=full" + + "&steps=true" + + "&bearings=1%2C1%3B1%2C1" + + "&radiuses=1%3B1" + + "&roundtrip=true" + + "&source=any" + + "&annotations=distance,duration,speed", + method=GET, + match_querystring=True, + body="{\"key\": \"value\"}", + status=200 + ) + + optimization = Optimization(access_token=ACCESS_TOKEN) + + response = optimization.route( + FEATURES, + profile="mapbox/cycling", + geometries="geojson", + overview="full", + steps=True, + waypoint_snapping=[ + (1, 1, 1), + (1, 1, 1) + ], + roundtrip=True, + source="any", + annotations=[ + "distance", + "duration", + "speed" + ] + ) + + assert response.status_code == 200 + + +@activate +def test_route_with_profile_geometries_overview_steps_waypoint_snapping_roundtrip_source_and_language(): + add( + url="https://api.mapbox.com" + + "/optimized-trips/v1" + + "/mapbox/cycling" + + "/0.0%2C0.0%3B1.0%2C1.0" + + "?access_token=pk.test" + + "&geometries=geojson" + + "&overview=full" + + "&steps=true" + + "&bearings=1%2C1%3B1%2C1" + + "&radiuses=1%3B1" + + "&roundtrip=true" + + "&source=any" + + "&language=en", + method=GET, + match_querystring=True, + body="{\"key\": \"value\"}", + status=200 + ) + + optimization = Optimization(access_token=ACCESS_TOKEN) + + response = optimization.route( + FEATURES, + profile="mapbox/cycling", + geometries="geojson", + overview="full", + steps=True, + waypoint_snapping=[ + (1, 1, 1), + (1, 1, 1) + ], + roundtrip=True, + source="any", + language="en" + ) + + assert response.status_code == 200 + + +@activate +def test_route_with_profile_geometries_overview_steps_waypoint_snapping_roundtrip_source_destination_and_distributions(): + add( + url="https://api.mapbox.com" + + "/optimized-trips/v1" + + "/mapbox/cycling" + + "/0.0%2C0.0%3B1.0%2C1.0" + + "?access_token=pk.test" + + "&geometries=geojson" + + "&overview=full" + + "&steps=true" + + "&bearings=1%2C1%3B1%2C1" + + "&radiuses=1%3B1" + + "&roundtrip=true" + + "&source=any" + + "&destination=any" + + "&distributions=0,1", + method=GET, + match_querystring=True, + body="{\"key\": \"value\"}", + status=200 + ) + + optimization = Optimization(access_token=ACCESS_TOKEN) + + response = optimization.route( + FEATURES, + profile="mapbox/cycling", + geometries="geojson", + overview="full", + steps=True, + waypoint_snapping=[ + (1, 1, 1), + (1, 1, 1) + ], + roundtrip=True, + source="any", + destination="any", + distributions=[ + [0, 1] + ] + ) + + assert response.status_code == 200 + + +@activate +def test_route_with_profile_geometries_overview_steps_waypoint_snapping_roundtrip_source_destination_and_annotations(): + add( + url="https://api.mapbox.com" + + "/optimized-trips/v1" + + "/mapbox/cycling" + + "/0.0%2C0.0%3B1.0%2C1.0" + + "?access_token=pk.test" + + "&geometries=geojson" + + "&overview=full" + + "&steps=true" + + "&bearings=1%2C1%3B1%2C1" + + "&radiuses=1%3B1" + + "&roundtrip=true" + + "&source=any" + + "&destination=any" + + "&annotations=distance,duration,speed", + method=GET, + match_querystring=True, + body="{\"key\": \"value\"}", + status=200 + ) + + optimization = Optimization(access_token=ACCESS_TOKEN) + + response = optimization.route( + FEATURES, + profile="mapbox/cycling", + geometries="geojson", + overview="full", + steps=True, + waypoint_snapping=[ + (1, 1, 1), + (1, 1, 1) + ], + roundtrip=True, + source="any", + destination="any", + annotations=[ + "distance", + "duration", + "speed" + ] + ) + + assert response.status_code == 200 + + +@activate +def test_route_with_profile_geometries_overview_steps_waypoint_snapping_roundtrip_source_destination_and_language(): + add( + url="https://api.mapbox.com" + + "/optimized-trips/v1" + + "/mapbox/cycling" + + "/0.0%2C0.0%3B1.0%2C1.0" + + "?access_token=pk.test" + + "&geometries=geojson" + + "&overview=full" + + "&steps=true" + + "&bearings=1%2C1%3B1%2C1" + + "&radiuses=1%3B1" + + "&roundtrip=true" + + "&source=any" + + "&destination=any" + + "&language=en", + method=GET, + match_querystring=True, + body="{\"key\": \"value\"}", + status=200 + ) + + optimization = Optimization(access_token=ACCESS_TOKEN) + + response = optimization.route( + FEATURES, + profile="mapbox/cycling", + geometries="geojson", + overview="full", + steps=True, + waypoint_snapping=[ + (1, 1, 1), + (1, 1, 1) + ], + roundtrip=True, + source="any", + destination="any", + language="en" + ) + + assert response.status_code == 200 + + +@activate +def test_route_with_profile_geometries_overview_steps_waypoint_snapping_roundtrip_source_destination_distributions_and_annotations(): + add( + url="https://api.mapbox.com" + + "/optimized-trips/v1" + + "/mapbox/cycling" + + "/0.0%2C0.0%3B1.0%2C1.0" + + "?access_token=pk.test" + + "&geometries=geojson" + + "&overview=full" + + "&steps=true" + + "&bearings=1%2C1%3B1%2C1" + + "&radiuses=1%3B1" + + "&roundtrip=true" + + "&source=any" + + "&destination=any" + + "&distributions=0,1" + + "&annotations=distance,duration,speed", + method=GET, + match_querystring=True, + body="{\"key\": \"value\"}", + status=200 + ) + + optimization = Optimization(access_token=ACCESS_TOKEN) + + response = optimization.route( + FEATURES, + profile="mapbox/cycling", + geometries="geojson", + overview="full", + steps=True, + waypoint_snapping=[ + (1, 1, 1), + (1, 1, 1) + ], + roundtrip=True, + source="any", + destination="any", + distributions=[ + [0, 1] + ], + annotations=[ + "distance", + "duration", + "speed" + ] + ) + + assert response.status_code == 200 + + +@activate +def test_route_with_profile_geometries_overview_steps_waypoint_snapping_roundtrip_source_destination_distributions_and_language(): + add( + url="https://api.mapbox.com" + + "/optimized-trips/v1" + + "/mapbox/cycling" + + "/0.0%2C0.0%3B1.0%2C1.0" + + "?access_token=pk.test" + + "&geometries=geojson" + + "&overview=full" + + "&steps=true" + + "&bearings=1%2C1%3B1%2C1" + + "&radiuses=1%3B1" + + "&roundtrip=true" + + "&source=any" + + "&destination=any" + + "&distributions=0,1" + + "&language=en", + method=GET, + match_querystring=True, + body="{\"key\": \"value\"}", + status=200 + ) + + optimization = Optimization(access_token=ACCESS_TOKEN) + + response = optimization.route( + FEATURES, + profile="mapbox/cycling", + geometries="geojson", + overview="full", + steps=True, + waypoint_snapping=[ + (1, 1, 1), + (1, 1, 1) + ], + roundtrip=True, + source="any", + destination="any", + distributions=[ + [0, 1] + ], + language="en" + ) + + assert response.status_code == 200 + + +@activate +def test_route_with_profile_geometries_overview_steps_waypoint_snapping_roundtrip_source_destination_distributions_annotations_and_language(): + add( + url="https://api.mapbox.com" + + "/optimized-trips/v1" + + "/mapbox/cycling" + + "/0.0%2C0.0%3B1.0%2C1.0" + + "?access_token=pk.test" + + "&geometries=geojson" + + "&overview=full" + + "&steps=true" + + "&bearings=1%2C1%3B1%2C1" + + "&radiuses=1%3B1" + + "&roundtrip=true" + + "&source=any" + + "&destination=any" + + "&distributions=0,1" + + "&annotations=distance,duration,speed" + + "&language=en", + method=GET, + match_querystring=True, + body="{\"key\": \"value\"}", + status=200 + ) + + optimization = Optimization(access_token=ACCESS_TOKEN) + + response = optimization.route( + FEATURES, + profile="mapbox/cycling", + geometries="geojson", + overview="full", + steps=True, + waypoint_snapping=[ + (1, 1, 1), + (1, 1, 1) + ], + roundtrip=True, + source="any", + destination="any", + distributions=[ + [0, 1] + ], + annotations=[ + "distance", + "duration", + "speed" + ], + language="en" + ) + + assert response.status_code == 200 From 7f967556ae43fa59c897f60cb57d0cb7b8b89d65 Mon Sep 17 00:00:00 2001 From: critical-path <37241479+critical-path@users.noreply.github.com> Date: Tue, 12 Jun 2018 21:41:16 -0400 Subject: [PATCH 08/19] Update test_encoding.py --- tests/test_encoding.py | 76 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/tests/test_encoding.py b/tests/test_encoding.py index 8fb4b5e..45e0989 100644 --- a/tests/test_encoding.py +++ b/tests/test_encoding.py @@ -141,3 +141,79 @@ def test_encode_waypoints_rounding(): "properties": {}}] assert expected == encode_waypoints(int_coord_features) + + +# copied from test_directions.py + +def test_invalid_radiuses(): + service = mapbox.Directions(access_token='pk.test') + with pytest.raises(mapbox.errors.InvalidParameterError) as e: + service._validate_radius('forever') + assert 'not a valid radius' in str(e) + + +def test_invalid_number_of_bearings(): + service = mapbox.Directions(access_token='pk.test') + with pytest.raises(mapbox.errors.InvalidParameterError) as e: + service._validate_snapping([1, 2, 3], points) + assert 'exactly one' in str(e) + + +def test_invalid_bearing_tuple(): + service = mapbox.Directions(access_token='pk.test') + with pytest.raises(mapbox.errors.InvalidParameterError) as e: + service._validate_snapping([(270, 45, 'extra'), (315,)], points) + assert 'bearing tuple' in str(e) + + +def test_snapping_bearing_none(): + service = mapbox.Directions(access_token='pk.test') + bearings, radii = service._validate_snapping([(10, 270, 45), None], points) + assert bearings == [(270, 45), None] + assert radii == [10, None] + + +def test_snapping_radii_none(): + service = mapbox.Directions(access_token='pk.test') + bearings, radii = service._validate_snapping([(10, 270, 45), None], points) + assert bearings == [(270, 45), None] + assert radii == [10, None] + + +def test_validate_radius_none(): + service = mapbox.Directions(access_token='pk.test') + assert service._validate_radius(None) is None + + +def test_validate_radius_unlimited(): + service = mapbox.Directions(access_token='pk.test') + assert service._validate_radius('unlimited') == 'unlimited' + + +def test_validate_radius_invalid(): + service = mapbox.Directions(access_token='pk.test') + with pytest.raises(mapbox.errors.InvalidParameterError) as e: + service._validate_radius(-1) + with pytest.raises(mapbox.errors.InvalidParameterError) as e: + service._validate_radius('nothing') + + +def test_invalid_bearing_domain(): + service = mapbox.Directions(access_token='pk.test') + with pytest.raises(mapbox.errors.InvalidParameterError) as e: + service._validate_snapping([(-1, 90), (315, 90)], points) + assert 'between 0 and 360' in str(e) + + +def test_bearings_without_radius(): + with pytest.raises(TypeError): + mapbox.Directions(access_token='pk.test').directions( + waypoint_snapping=[(270, 45), (270, 45)]) + + +def test_validate_snapping(): + service = mapbox.Directions(access_token='pk.test') + snaps = service._validate_snapping( + [(1, 1, 1), u'unlimited'], [None, None]) + + assert snaps == ([(1, 1), None], [1, 'unlimited']) From 8cad2ecd34b38e81e262317b3f92ded6f3094dfb Mon Sep 17 00:00:00 2001 From: critical-path <37241479+critical-path@users.noreply.github.com> Date: Tue, 12 Jun 2018 21:45:24 -0400 Subject: [PATCH 09/19] Update test_encoding.py --- tests/test_encoding.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_encoding.py b/tests/test_encoding.py index 45e0989..30fed8f 100644 --- a/tests/test_encoding.py +++ b/tests/test_encoding.py @@ -5,6 +5,7 @@ encode_waypoints, encode_polyline, encode_coordinates_json) +import mapbox gj_point_features = [{ From 8d44411dd3ee5ae8ea26547fccd6f055ee806916 Mon Sep 17 00:00:00 2001 From: critical-path <37241479+critical-path@users.noreply.github.com> Date: Tue, 12 Jun 2018 21:48:30 -0400 Subject: [PATCH 10/19] Update test_encoding.py --- tests/test_encoding.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/test_encoding.py b/tests/test_encoding.py index 30fed8f..7327ea6 100644 --- a/tests/test_encoding.py +++ b/tests/test_encoding.py @@ -146,6 +146,23 @@ def test_encode_waypoints_rounding(): # copied from test_directions.py +points = [{ + "type": "Feature", + "properties": {}, + "geometry": { + "type": "Point", + "coordinates": [ + -87.33787536621092, + 36.539156961321574]}}, { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "Point", + "coordinates": [ + -88.2476806640625, + 36.92217534275667]}}] + + def test_invalid_radiuses(): service = mapbox.Directions(access_token='pk.test') with pytest.raises(mapbox.errors.InvalidParameterError) as e: From ea869941c89ea85ddc54b486c20ccc6318a0f31b Mon Sep 17 00:00:00 2001 From: critical-path <37241479+critical-path@users.noreply.github.com> Date: Tue, 12 Jun 2018 22:13:29 -0400 Subject: [PATCH 11/19] Update test_encoding.py --- tests/test_encoding.py | 51 ++++++++++++++++++------------------------ 1 file changed, 22 insertions(+), 29 deletions(-) diff --git a/tests/test_encoding.py b/tests/test_encoding.py index 7327ea6..b866731 100644 --- a/tests/test_encoding.py +++ b/tests/test_encoding.py @@ -1,11 +1,16 @@ import pytest import copy import json -from mapbox.encoding import (read_points, - encode_waypoints, - encode_polyline, - encode_coordinates_json) -import mapbox +from mapbox.encoding import ( + read_points, + encode_waypoints, + encode_polyline, + encode_coordinates_json, + encode_bearing, + validate_radius, + validate_snapping +) + gj_point_features = [{ @@ -164,74 +169,62 @@ def test_encode_waypoints_rounding(): def test_invalid_radiuses(): - service = mapbox.Directions(access_token='pk.test') with pytest.raises(mapbox.errors.InvalidParameterError) as e: - service._validate_radius('forever') + validate_radius('forever') assert 'not a valid radius' in str(e) def test_invalid_number_of_bearings(): - service = mapbox.Directions(access_token='pk.test') with pytest.raises(mapbox.errors.InvalidParameterError) as e: - service._validate_snapping([1, 2, 3], points) + validate_snapping([1, 2, 3], points) assert 'exactly one' in str(e) def test_invalid_bearing_tuple(): - service = mapbox.Directions(access_token='pk.test') with pytest.raises(mapbox.errors.InvalidParameterError) as e: - service._validate_snapping([(270, 45, 'extra'), (315,)], points) + validate_snapping([(270, 45, 'extra'), (315,)], points) assert 'bearing tuple' in str(e) def test_snapping_bearing_none(): - service = mapbox.Directions(access_token='pk.test') - bearings, radii = service._validate_snapping([(10, 270, 45), None], points) + bearings, radii = validate_snapping([(10, 270, 45), None], points) assert bearings == [(270, 45), None] assert radii == [10, None] def test_snapping_radii_none(): - service = mapbox.Directions(access_token='pk.test') - bearings, radii = service._validate_snapping([(10, 270, 45), None], points) + bearings, radii = validate_snapping([(10, 270, 45), None], points) assert bearings == [(270, 45), None] assert radii == [10, None] def test_validate_radius_none(): - service = mapbox.Directions(access_token='pk.test') - assert service._validate_radius(None) is None + assert validate_radius(None) is None def test_validate_radius_unlimited(): - service = mapbox.Directions(access_token='pk.test') - assert service._validate_radius('unlimited') == 'unlimited' + assert validate_radius('unlimited') == 'unlimited' def test_validate_radius_invalid(): - service = mapbox.Directions(access_token='pk.test') with pytest.raises(mapbox.errors.InvalidParameterError) as e: - service._validate_radius(-1) + validate_radius(-1) with pytest.raises(mapbox.errors.InvalidParameterError) as e: - service._validate_radius('nothing') + validate_radius('nothing') def test_invalid_bearing_domain(): - service = mapbox.Directions(access_token='pk.test') with pytest.raises(mapbox.errors.InvalidParameterError) as e: - service._validate_snapping([(-1, 90), (315, 90)], points) + validate_snapping([(-1, 90), (315, 90)], points) assert 'between 0 and 360' in str(e) def test_bearings_without_radius(): with pytest.raises(TypeError): - mapbox.Directions(access_token='pk.test').directions( - waypoint_snapping=[(270, 45), (270, 45)]) + validate_snapping([(270, 45), (270, 45)]) def test_validate_snapping(): - service = mapbox.Directions(access_token='pk.test') - snaps = service._validate_snapping( + snaps = validate_snapping( [(1, 1, 1), u'unlimited'], [None, None]) - assert snaps == ([(1, 1), None], [1, 'unlimited']) From 59407adedc73e586c5db3a3240fce13897011edc Mon Sep 17 00:00:00 2001 From: critical-path <37241479+critical-path@users.noreply.github.com> Date: Tue, 12 Jun 2018 22:15:22 -0400 Subject: [PATCH 12/19] Update test_encoding.py --- tests/test_encoding.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_encoding.py b/tests/test_encoding.py index b866731..4003553 100644 --- a/tests/test_encoding.py +++ b/tests/test_encoding.py @@ -10,7 +10,7 @@ validate_radius, validate_snapping ) - +import mapbox gj_point_features = [{ From 94d5ab80281d4c0af04be43347102cc940253cec Mon Sep 17 00:00:00 2001 From: critical-path <37241479+critical-path@users.noreply.github.com> Date: Tue, 12 Jun 2018 22:24:38 -0400 Subject: [PATCH 13/19] Update optimization.py --- mapbox/services/optimization.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/mapbox/services/optimization.py b/mapbox/services/optimization.py index ab5c9c2..e9bcc42 100644 --- a/mapbox/services/optimization.py +++ b/mapbox/services/optimization.py @@ -232,6 +232,7 @@ def route(self, features, profile="mapbox/driving", waypoint_snapping=None, roundtrip=None, source=None, destination=None, distributions=None, annotations=None, language=None): + """The Optimization API returns a duration-optimized route between the input coordinates. @@ -353,14 +354,17 @@ def route(self, features, profile="mapbox/driving", if waypoint_snapping is not None: bearings, radiuses = validate_snapping(waypoint_snapping, features) + else: + bearings = None + radiuses = None - if bearings is not None: - bearings = ";".join(encode_bearing(bearing) for bearing in bearings) - query_parameters["bearings"] = bearings + if bearings is not None: + bearings = ";".join(encode_bearing(bearing) for bearing in bearings) + query_parameters["bearings"] = bearings - if radiuses is not None: - radiuses = ";".join(str(radius) for radius in radiuses) - query_parameters["radiuses"] = radiuses + if radiuses is not None: + radiuses = ";".join(str(radius) for radius in radiuses) + query_parameters["radiuses"] = radiuses if roundtrip is not None: query_parameters["roundtrip"] = "true" if roundtrip is True else "false" From cbe8467c9cb8aad055b6b7809a9ca46f5dd62697 Mon Sep 17 00:00:00 2001 From: critical-path <37241479+critical-path@users.noreply.github.com> Date: Tue, 12 Jun 2018 22:33:16 -0400 Subject: [PATCH 14/19] Update test_encoding.py --- tests/test_encoding.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/tests/test_encoding.py b/tests/test_encoding.py index 4003553..74c2236 100644 --- a/tests/test_encoding.py +++ b/tests/test_encoding.py @@ -149,7 +149,7 @@ def test_encode_waypoints_rounding(): assert expected == encode_waypoints(int_coord_features) -# copied from test_directions.py +# copied from test_directions.py and modified points = [{ "type": "Feature", @@ -228,3 +228,13 @@ def test_validate_snapping(): snaps = validate_snapping( [(1, 1, 1), u'unlimited'], [None, None]) assert snaps == ([(1, 1), None], [1, 'unlimited']) + + +def test_validate_snapping_none(): + snaps = validate_snapping(None, points) + assert snaps == (None, None) + + +def test_encode_bearing_none(): + bearing = encode_bearing(None) + assert bearing == "" From 0ec7d6abd25284122d21546602c3d60a8546bd0e Mon Sep 17 00:00:00 2001 From: critical-path <37241479+critical-path@users.noreply.github.com> Date: Fri, 22 Jun 2018 21:51:36 -0400 Subject: [PATCH 15/19] Update optimization.py --- mapbox/services/optimization.py | 45 +++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/mapbox/services/optimization.py b/mapbox/services/optimization.py index e9bcc42..5c5f4fd 100644 --- a/mapbox/services/optimization.py +++ b/mapbox/services/optimization.py @@ -227,6 +227,41 @@ def _validate_annotations(self, annotations): return ",".join(annotations) + # Copied from directions.py and modified. + + def _geojson(self, data, geometry_format=None): + """Converts JSON to GeoJSON in response object.""" + + feature_collection = { + "type": "FeatureCollection", + "features": [] + } + + for route in data["trips"]: + if geometry_format == "geojson": + geometry = route["geometry"] + + else: + geometry = { + "type": "LineString", + "coordinates": polyline.decode( + route["geometry"] + ) + } + + feature = { + "type": "Feature", + "geometry": geometry, + "properties": { + "distance": route["distance"], + "duration": route["duration"] + } + } + + feature_collection["features"].append(feature) + + return feature_collection + def route(self, features, profile="mapbox/driving", geometries=None, overview=None, steps=None, waypoint_snapping=None, roundtrip=None, source=None, @@ -393,4 +428,14 @@ def route(self, features, profile="mapbox/driving", response = self.session.get(uri, params=query_parameters) self.handle_http_error(response) + # Add geojson method to response object. + + def geojson(): + return self._geojson( + response.json(), + geometry_format=geometries + ) + + response.geojson = geojson + return response From b5f6dec081961e9cf64c9a64269324972ed7286c Mon Sep 17 00:00:00 2001 From: critical-path <37241479+critical-path@users.noreply.github.com> Date: Fri, 22 Jun 2018 22:01:42 -0400 Subject: [PATCH 16/19] Update optimization.py --- mapbox/services/optimization.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mapbox/services/optimization.py b/mapbox/services/optimization.py index 5c5f4fd..67c5b22 100644 --- a/mapbox/services/optimization.py +++ b/mapbox/services/optimization.py @@ -13,6 +13,8 @@ from mapbox.services.base import Service +import polyline + from uritemplate import URITemplate From 7547d7163120bef51b09544d4e9db8f46b51ccd5 Mon Sep 17 00:00:00 2001 From: critical-path <37241479+critical-path@users.noreply.github.com> Date: Tue, 10 Jul 2018 19:10:33 -0400 Subject: [PATCH 17/19] Update index.rst --- docs/index.rst | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/index.rst b/docs/index.rst index 26e1184..6bec2b6 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -15,52 +15,52 @@ The Mapbox Python SDK is a low-level client API, not a Resource API such as the Services ======== -- **Analytics V1** `examples <./docs/analytics.md>`__, `website `__ +- **Analytics V1** `examples <./analytics.html>`__, `website `__ - API usage for services by resource. - available for premium and enterprise plans. -- **Directions V4** `examples <./docs/directions.md#directions>`__, `website `__ +- **Directions V4** `examples <./directions.html#directions>`__, `website `__ - Profiles for driving, walking, and cycling - GeoJSON & Polyline formatting - Instructions as text or HTML -- **Geocoding V5** `examples <./docs/geocoding.md#geocoding>`__, `website `__ +- **Geocoding V5** `examples <./geocoding.html#geocoding>`__, `website `__ - Forward (place names ⇢ longitude, latitude) - Reverse (longitude, latitude ⇢ place names) -- **Map Matching V4** `examples <./docs/mapmatching.md#map-matching>`__, `website `__ +- **Map Matching V4** `examples <./mapmatching.html#map-matching>`__, `website `__ - Snap GPS traces to OpenStreetMap data -- **Static Maps V4** `examples <./docs/static.md#static-maps>`__, `website `__ +- **Static Maps V4** `examples <./static.html#static-maps>`__, `website `__ - Generate standalone images from existing Mapbox *mapids* (tilesets) - Render with GeoJSON overlays -- **Static Styles V1** `examples <./docs/static.md#static-maps>`__, `website `__ +- **Static Styles V1** `examples <./static.html#static-maps>`__, `website `__ - Generate standalone images from existing Mapbox *styles* - Render with GeoJSON overlays - Adjust pitch and bearing, decimal zoom levels - **Surface V4** **DEPRECATED** -- **Uploads V1** `examples <./docs/uploads.md#uploads>`__, `website `__ +- **Uploads V1** `examples <./uploads.html#uploads>`__, `website `__ - Upload data to be processed and hosted by Mapbox. -- **Datasets V1** `examples <./docs/datasets.md#datasets>`__, `website `__ +- **Datasets V1** `examples <./datasets.html#datasets>`__, `website `__ - Manage editable collections of GeoJSON features - Persistent storage for custom geographic data -- **Optimization V1** `examples <./docs/optimization.md#optimization>`__, `website `__ +- **Optimization V1** `examples <./optimization.html#optimization>`__, `website `__ - Retrieve a duration-optimized route -- **Maps V4** `examples <./docs/maps.md#maps>`__, `website `__ +- **Maps V4** `examples <./maps.html#maps>`__, `website `__ - Retrieve an image tile, vector tile, or UTFGrid in the specified format - Retrieve vector features from Mapbox Editor projects as GeoJSON or KML From 25a8f01e4291d53c8a4334e6fae4cbd315fdaf90 Mon Sep 17 00:00:00 2001 From: critical-path <37241479+critical-path@users.noreply.github.com> Date: Tue, 10 Jul 2018 19:11:44 -0400 Subject: [PATCH 18/19] Update optimization.py --- mapbox/services/optimization.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mapbox/services/optimization.py b/mapbox/services/optimization.py index 67c5b22..7f9e568 100644 --- a/mapbox/services/optimization.py +++ b/mapbox/services/optimization.py @@ -1,3 +1,5 @@ +"""The Optimization class provides access to Mapbox's Optimization API.""" + from mapbox.encoding import ( encode_bearing, encode_waypoints, From ae94573863b978ee5e0cd172d5334655a1bf3726 Mon Sep 17 00:00:00 2001 From: critical-path <37241479+critical-path@users.noreply.github.com> Date: Tue, 10 Jul 2018 19:13:40 -0400 Subject: [PATCH 19/19] Update test_optimization.py --- tests/test_optimization.py | 251 ++++++++++++++++--------------------- 1 file changed, 105 insertions(+), 146 deletions(-) diff --git a/tests/test_optimization.py b/tests/test_optimization.py index 584e5ee..1726a8a 100644 --- a/tests/test_optimization.py +++ b/tests/test_optimization.py @@ -6,7 +6,10 @@ from mapbox.services.optimization import Optimization -from pytest import raises +from pytest import ( + mark, + raises +) from responses import ( activate, @@ -21,28 +24,24 @@ { "type": "Feature", "properties": {}, - "geometry": - { - "type": "Point", - "coordinates": - [ - 0.0, - 0.0 - ] - } + "geometry": { + "type": "Point", + "coordinates": [ + 0.0, + 0.0 + ] + } }, { "type": "Feature", "properties": {}, - "geometry": - { - "type": "Point", - "coordinates": - [ - 1.0, - 1.0 - ] - } + "geometry": { + "type": "Point", + "coordinates": [ + 1.0, + 1.0 + ] + } } ] @@ -63,197 +62,155 @@ def test_object_properties(): assert optimization.valid_destinations -def test_validate_profile(): +def test_validate_profile_invalid(): optimization = Optimization() - # invalid value - with raises(InvalidProfileError) as exception: profile = "invalid" result = optimization._validate_profile(profile) - # valid values - profiles = [ - "mapbox/cycling", - "mapbox/driving", - "mapbox/walking" - ] - - for profile in profiles: - result = optimization._validate_profile(profile) - assert result == profile +@mark.parametrize("profile", ["mapbox/cycling", "mapbox/driving", "mapbox/walking"]) +def test_validate_profile_valid(profile): + optimization = Optimization() + result = optimization._validate_profile(profile) + assert result == profile -def test_validate_geometry(): +def test_validate_geometry_invalid(): optimization = Optimization() - # invalid value - with raises(InvalidParameterError) as exception: geometry = "invalid" result = optimization._validate_geometry(geometry) - # valid values - geometries = [ - "geojson", - "polyline", - "polyline6" - ] - - for geometry in geometries: - result = optimization._validate_geometry(geometry) - assert result == geometry +@mark.parametrize("geometry", ["geojson", "polyline", "polyline6"]) +def test_validate_geometry_valid(geometry): + optimization = Optimization() + result = optimization._validate_geometry(geometry) + assert result == geometry -def test_validate_overview(): +def test_validate_overview_invalid(): optimization = Optimization() - # invalid value - with raises(InvalidParameterError) as exception: overview = "invalid" result = optimization._validate_overview(overview) - # valid values - overviews = [ - "full", - "simplified", - False - ] - - for overview in overviews: - result = optimization._validate_overview(overview) - assert result == overview +@mark.parametrize("overview", ["full", "simplified", False]) +def test_validate_overview_valid(overview): + optimization = Optimization() + result = optimization._validate_overview(overview) + assert result == overview -def test_validate_source(): +def test_validate_source_invalid(): optimization = Optimization() - # invalid value - with raises(InvalidParameterError) as exception: source = "invalid" result = optimization._validate_source(source) - # valid values - - sources = [ - "any", - "first" - ] - for source in sources: - result = optimization._validate_source(source) - assert result == source +@mark.parametrize("source", ["any", "first"]) +def test_validate_source_valid(source): + optimization = Optimization() + result = optimization._validate_source(source) + assert result == source -def test_validate_destination(): +def test_validate_destination_invalid(): optimization = Optimization() - # invalid value - with raises(InvalidParameterError) as exception: destination = "invalid" result = optimization._validate_destination(destination) - # valid values - destinations = [ - "any", - "last" +@mark.parametrize("destination", ["any", "last"]) +def test_validate_destination_valid(destination): + optimization = Optimization() + result = optimization._validate_destination(destination) + assert result == destination + + +# too many distribution pairs +# too few values in each pair +# too many values in each pair +# values are the same +# first value is not a valid index +# second value is not a valid index + +@mark.parametrize( + "distributions", + [ + [[0, 1], [0, 1], [0, 1]], + [[0], [0]], + [[0, 1, 0], [0, 1, 0]], + [[0, 0], [0, 0]], + [[100, 0], [0, 0]], + [[0, 100], [0, 0]] ] - - for destination in destinations: - result = optimization._validate_destination(destination) - assert result == destination - - -def test_validate_distributions(): +) +def test_validate_distributions_invalid(distributions): optimization = Optimization() - # None - - distributions = None - result = optimization._validate_distributions(distributions, COORDINATES) - assert result == distributions - - # invalid value - too many distribution pairs - - with raises(InvalidParameterError) as exception: - distributions = [[0, 1], [0, 1], [0, 1]] - result = optimization._validate_distributions(distributions, COORDINATES) - - # invalid value - too few values in each pair - with raises(InvalidParameterError) as exception: - distributions = [[0], [0]] result = optimization._validate_distributions(distributions, COORDINATES) - # invalid value - too many values in each pair - with raises(InvalidParameterError) as exception: - distributions = [[0, 1, 0], [0, 1, 0]] - result = optimization._validate_distributions(distributions, COORDINATES) - - # invalid value - values are the same - - with raises(InvalidParameterError) as exception: - distributions = [[0, 0], [0, 0]] - result = optimization._validate_distributions(distributions, COORDINATES) - - # invalid value - first value is not a valid index - - with raises(InvalidParameterError) as exception: - distributions = [[100, 0], [0, 0]] - result = optimization._validate_distributions(distributions, COORDINATES) - - # invalid value - second value is not a valid index - - with raises(InvalidParameterError) as exception: - distributions = [[0, 100], [0, 0]] - result = optimization._validate_distributions(distributions, COORDINATES) - - # valid value - - distributions = [[0, 1],[0, 1]] +def test_validate_distributions_valid(): + optimization = Optimization() + distributions = [[0, 1], [0, 1]] result = optimization._validate_distributions(distributions, COORDINATES) assert result == "0,1;0,1" -def test_validate_annotations(): +def test_validate_distributions_none(): optimization = Optimization() + distributions = None + result = optimization._validate_distributions(distributions, COORDINATES) + assert result == distributions - # None - - annotation = None - result = optimization._validate_annotations(annotation) - assert result == annotation - # invalid value +def test_validate_annotations_invalid(): + optimization = Optimization() with raises(InvalidParameterError) as exception: annotation = ["invalid"] result = optimization._validate_annotations(annotation) - # valid values - annotations = [ - "distance", - "duration", - "speed" +@mark.parametrize( + "annotations", + [ + ["distance"], + ["duration"], + ["speed"], + ["distance", "duration"], + ["distance", "speed"], + ["duration", "speed"], + ["distance", "duration", "speed"] ] - +) +def test_validate_annotations_valid(annotations): + optimization = Optimization() result = optimization._validate_annotations(annotations) assert result == ",".join(annotations) -def test_route_error(): - optimization = Optimization(access_token=ACCESS_TOKEN) +def test_validate_annotations_none(): + optimization = Optimization() + annotations = None + result = optimization._validate_annotations(annotations) + assert result == annotations - # no source + +def test_route_error_no_source(): + optimization = Optimization(access_token=ACCESS_TOKEN) with raises(ValidationError) as exception: response = optimization.route( @@ -262,16 +219,20 @@ def test_route_error(): destination="any" ) - # no destination + +def test_route_error_no_destination(): + optimization = Optimization(access_token=ACCESS_TOKEN) with raises(ValidationError) as exception: response = optimization.route( FEATURES, roundtrip=False, source="any" - ) + ) - # no source, no destination + +def test_route_error_no_source_no_destination(): + optimization = Optimization(access_token=ACCESS_TOKEN) with raises(ValidationError) as exception: response = optimization.route( @@ -295,9 +256,7 @@ def test_route(): ) optimization = Optimization(access_token=ACCESS_TOKEN) - response = optimization.route(FEATURES) - assert response.status_code == 200