Skip to content
This repository was archived by the owner on Feb 12, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 20 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,25 +70,31 @@ Note that functions that accept points only, any non-point feature is filtered o
```
Usage: mapbox directions [OPTIONS] FEATURES...

Calculate optimal route with turn-by-turn directions between up to 25
waypoints.
The Mapbox Directions API will show you how to get where you're going.

$ mapbox directions "[-122.681032, 45.528334]" "[-122.71679, 45.525135]"
mapbox directions "[0, 0]" "[1, 1]"

An access token is required, see `mapbox --help`.
An access token is required. See "mapbox --help".

Options:
--profile [mapbox.driving|mapbox.cycling|mapbox.walking]
Mapbox direction profile id
--profile [mapbox/driving|mapbox/driving-traffic|mapbox/walking|mapbox/cycling]
Routing profile
--alternatives / --no-alternatives
Generate alternative routes?
--instructions [text|html] Format for route instructions
--geometry [geojson|polyline|false]
Geometry encoding
--steps / --no-steps Include steps in the response
--geojson / --no-geojson Return geojson feature collection (default:
full response json)
-o, --output TEXT Save output to a file.
Whether to try to return alternative routes
--geometries [geojson|polyline|polyline6]
Format of returned geometry
--overview [full|simplified|False]
Type of returned overview geometry
--steps / --no-steps Whether to return steps and turn-by-turn
instructions
--continue-straight / --no-continue-straight
Whether to see the allowed direction of
travel when departing the original waypoint
--waypoint-snapping TEXT Controls waypoint snapping
--annotations TEXT Additional metadata along the route
--language TEXT Language of returned turn-by-turn
instructions
-o, --output TEXT Save output to a file
--help Show this message and exit.
```

Expand Down
222 changes: 188 additions & 34 deletions mapboxcli/scripts/directions.py
Original file line number Diff line number Diff line change
@@ -1,61 +1,215 @@
import json
import re

import click
import cligj
import mapbox

import mapbox
from mapboxcli.errors import MapboxCLIException


@click.command(short_help="Routing between waypoints.")
def waypoint_snapping_callback(ctx, param, value):
results = []

tuple_pattern = re.compile("[,]")
int_pattern = re.compile("[0-9]")

# value is an n-tuple, each element of
# which contains input from the user.
#
# Iterate over each element, determining
# whether to convert it to a tuple,
# convert it to an int, or leave it as
# a str.
#
# Append each element to results, which
# the Directions SDK will attempt to
# validate.

if len(value) == 0:
return None

for element in value:

# If the element contains a comma, then assume
# that the user intended to pass in a tuple.
#
# Convert each item in the element to an int,
# and create a tuple containing all items.
#
# Raise an error if the item is not a valid int.
#
# (The SDK accepts a three-tuple with ints for
# radius, angle, and range.)

if re.search(tuple_pattern, element):
element = re.split(tuple_pattern, element)

for index in range(0, len(element)):
try:
element[index] = int(element[index])
except ValueError as exc:
raise mapbox.errors.ValidationError(str(exc))

element = tuple(element)

results.append(element)

# If the element contains a decimal number but not
# a comma, then assume that the user intended to
# pass in an int.
#
# Convert the element to an int.
#
# Raise an error if the item is not a valid int.
#
# (The Directions SDK accepts an int for radius.)

elif re.search(int_pattern, element):
try:
element = int(element)
except ValueError as exc:
raise mapbox.errors.ValidationError(str(exc))

results.append(element)

# If the element contains neither a decimal number
# nor a comma, then assume that the user intended
# to pass in a str.
#
# Do nothing since the element is aready a str.
#
# (The Directions SDK accepts a str for unlimited radius.)

else:
results.append(element)

return results

@click.command(short_help="Routing between waypoints")

@cligj.features_in_arg
@click.option('--profile', default="mapbox.driving",
type=click.Choice(mapbox.Directions().valid_profiles),
help="Mapbox direction profile id")
@click.option('--alternatives/--no-alternatives', default=True,
help="Generate alternative routes?")
@click.option('--instructions', default="text",
type=click.Choice(mapbox.Directions().valid_instruction_formats),
help="Format for route instructions")
@click.option('--geometry', default="geojson",
type=click.Choice(mapbox.Directions().valid_geom_encoding),
help="Geometry encoding")
@click.option('--steps/--no-steps', default=True,
help="Include steps in the response")
@click.option('--geojson/--no-geojson', default=False,
help="Return geojson feature collection (default: full response json)")
@click.option('--output', '-o', default='-',
help="Save output to a file.")

@click.option(
"--profile",
type=click.Choice(mapbox.Directions.valid_profiles),
default="mapbox/driving",
help="Routing profile"
)

@click.option(
"--alternatives/--no-alternatives",
default=True,
help="Whether to try to return alternative routes"
)

@click.option(
"--geometries",
type=click.Choice(mapbox.Directions.valid_geom_encoding),
default="geojson",
help="Format of returned geometry"
)

# Directions.valid_geom_overview contains two
# elements of type str and one element of type bool.
# This causes the Directions CLI's --help option to
# raise a TypeError. To prevent this, we convert
# the bool to a str.

@click.option(
"--overview",
type=click.Choice(str(item) for item in mapbox.Directions.valid_geom_overview),
help="Type of returned overview geometry"
)

@click.option(
"--steps/--no-steps",
default=True,
help="Whether to return steps and turn-by-turn instructions"
)

@click.option(
"--continue-straight/--no-continue-straight",
default=True,
help="Whether to see the allowed direction of travel when departing the original waypoint"
)

@click.option(
"--waypoint-snapping",
multiple=True,
callback=waypoint_snapping_callback,
help="Controls waypoint snapping"
)

@click.option(
"--annotations",
help="Additional metadata along the route"
)

@click.option(
"--language",
help="Language of returned turn-by-turn instructions"
)

@click.option(
"-o",
"--output",
default="-",
help="Save output to a file"
)

@click.pass_context
def directions(ctx, features, geojson, profile, alternatives,
instructions, geometry, steps, output):
"""Calculate optimal route with turn-by-turn directions
between up to 25 waypoints.
def directions(ctx, features, profile, alternatives,
geometries, overview, steps, continue_straight,
waypoint_snapping, annotations, language, output):
"""The Mapbox Directions API will show you how to get
where you're going.

$ mapbox directions "[-122.681032, 45.528334]" "[-122.71679, 45.525135]"
mapbox directions "[0, 0]" "[1, 1]"

An access token is required, see `mapbox --help`.
An access token is required. See "mapbox --help".
"""
stdout = click.open_file(output, 'w')
access_token = (ctx.obj and ctx.obj.get('access_token')) or None
if geojson:
geometry = 'geojson'

access_token = (ctx.obj and ctx.obj.get("access_token")) or None

service = mapbox.Directions(access_token=access_token)

# The Directions SDK expects False to be
# a bool, not a str.

if overview == "False":
overview = False

# When using waypoint snapping, the
# Directions SDK expects features to be
# a list, not a generator.

if waypoint_snapping is not None:
features = list(features)

if annotations:
annotations = annotations.split(",")

stdout = click.open_file(output, "w")

try:
res = service.directions(
features,
steps=steps,
profile=profile,
alternatives=alternatives,
instructions=instructions,
geometry=geometry,
profile=profile)
geometries=geometries,
overview=overview,
steps=steps,
continue_straight=continue_straight,
waypoint_snapping=waypoint_snapping,
annotations=annotations,
language=language
)
except mapbox.errors.ValidationError as exc:
raise click.BadParameter(str(exc))

if res.status_code == 200:
if geojson:
if geometries == "geojson":
click.echo(json.dumps(res.geojson()), file=stdout)
else:
click.echo(res.text, file=stdout)
Expand Down
Loading