def distance(ctx, features, profile, output): """The Distance API returns all travel times between many points (also known as Distance Matrix). This is often used as input for solving routing optimization problems. $ mapbox distance "[-122.681, 45.528]" "[-122.716, 45.525]" The output is a json object with a "durations" key containing a 2D array of travel times between waypoints. 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 service = mapbox.Distance(access_token=access_token) try: res = service.distances( features, profile=profile) except mapbox.errors.ValidationError as exc: raise click.BadParameter(str(exc)) if res.status_code == 200: click.echo(res.text, file=stdout) else: raise MapboxCLIException(res.text.strip())
def test_distance(): responses.add( responses.POST, 'https://api.mapbox.com/distances/v1/mapbox/driving?access_token=pk.test', match_querystring=True, body='{"durations":[[0,4977,5951],[4963,0,9349],[5881,9317,0]]}', status=200, content_type='application/json') res = mapbox.Distance(access_token='pk.test').distances(points) assert res.status_code == 200 assert list(res.json().keys()) == ["durations", ]
def test_distances_matrix(): responses.add( responses.POST, 'https://api.mapbox.com/distances/v1/mapbox/driving?access_token=pk.test', match_querystring=True, body='{"durations":[[0,4977,5951],[4963,0,9349],[5881,9317,0]]}', status=200, content_type='application/json') res = mapbox.Distance(access_token='pk.test').distances(points) matrix = res.json()['durations'] # 3x3 list assert len(matrix) == 3 assert len(matrix[0]) == 3
def test_distances_matrix(): responses.add( responses.GET, 'https://api.mapbox.com/directions-matrix/v1/mapbox/driving/-87.337875,36.539156;-86.577791,36.722137;-88.247685,36.922175?access_token=pk.test', match_querystring=True, body='{"durations":[[0,4977,5951],[4963,0,9349],[5881,9317,0]]}', status=200, content_type='application/json') with pytest.warns(MapboxDeprecationWarning): res = mapbox.Distance(access_token='pk.test').distances(points) matrix = res.json()['durations'] # 3x3 list assert len(matrix) == 3 assert len(matrix[0]) == 3
import click import cligj import mapbox from mapboxcli.errors import MapboxCLIException @click.command(short_help="Distance matrix of travel times between waypoints.") @cligj.features_in_arg @click.option('--profile', default="driving", type=click.Choice(mapbox.Distance().valid_profiles), help="Mapbox direction profile id") @click.option('--output', '-o', default='-', help="Save output to a file.") @click.pass_context def distance(ctx, features, profile, output): """The Distance API returns all travel times between many points (also known as Distance Matrix). This is often used as input for solving routing optimization problems. $ mapbox distance "[-122.681, 45.528]" "[-122.716, 45.525]" The output is a json object with a "durations" key containing a 2D array of travel times between waypoints. 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 service = mapbox.Distance(access_token=access_token)
def test_profile_valid(profile): """Profiles are valid""" assert profile == mapbox.Distance( access_token='pk.test')._validate_profile(profile)
def test_profile_invalid(): """'jetpack' is not a valid profile.""" with pytest.raises(ValueError): mapbox.Distance(access_token='pk.test')._validate_profile('jetpack')
def test_class_attrs(): """Get expected class attr values""" serv = mapbox.Distance() assert serv.api_name == 'distances' assert serv.api_version == 'v1'
def optimal_tour(features, mode, profile, out_points, solver): """ A command line interface for solving the traveling salesman problem Input geojson features with point geometries and output the optimal tour as geojson feature collection. \b $ optimal_tour waypoints.geojson | geojson-summary 19 points and 1 line If using geodesic or directions modes, input must be in lonlat coordinates Directions mode requires a Mapbox account and a valid token set as the MAPBOX_ACCESS_TOKEN environment variable. """ log("Get point features") features = [f for f in features if f['geometry']['type'] == 'Point'] if len(features) <= 2: raise click.UsageError( "Need at least 3 point features to create route") if mode != 'cartesian' and not is_lonlat(features): raise click.UsageError( "For this {} mode, input must be in lonlat coordinates".format( mode)) log("Create travel cost matrix") if mode == 'cartesian': matrix = local_matrix(features, 'cartesian') elif mode == 'geodesic': matrix = local_matrix(features, 'geodesic') elif mode == 'directions': dist_api = mapbox.Distance() res = dist_api.distances(features, profile=profile) if res.status_code == 200: matrix = res.json()['durations'] else: raise Exception( "Got a {0} error from the Distances API: {1}".format( res.status_code, res.content)) log("Prep data") matrix_sym = atsp_tsp(matrix, strategy="avg") outf = "/tmp/myroute.tsp" with open(outf, 'w') as dest: dest.write(dumps_matrix(matrix_sym, name="My Route")) log("Run TSP solver") tour = run(outf, start=0, solver=solver) order = tour['tour'] features_ordered = [features[i] for i in order] log("Create lines connecting the tour") if mode == 'directions': # gather geojson linestring features along actual route via directions directions_api = mapbox.Directions() route_features = [] for chunk in split_overlap(features_ordered + [features_ordered[0]], 24): res = directions_api.directions(chunk, profile='mapbox.' + profile) if res.status_code == 200: route_features.append(res.geojson()['features'][0]) else: raise Exception( "Got a {0} error from the Directions API: {1}".format( res.status_code, res.content)) else: # Alternative, straight line distance between points route_coords = [f['geometry']['coordinates'] for f in features_ordered] route_coords.append(features_ordered[0]['geometry']['coordinates']) route_features = [{ 'type': 'Feature', 'properties': { 'tour': tour }, 'geometry': { 'type': 'LineString', 'coordinates': route_coords } }] # meld into one geojson feature collection log("Output feature collection") out_features = route_features if out_points: out_features += features_ordered collection = {'type': 'FeatureCollection', 'features': out_features} click.echo(json.dumps(collection))