def staticmap(ctx, mapid, output, features, lat, lon, zoom, size): """ Generate static map images from existing Mapbox map ids. Optionally overlay with geojson features. $ mapbox staticmap --features features.geojson mapbox.satellite out.png $ mapbox staticmap --lon -61.7 --lat 12.1 --zoom 12 mapbox.satellite out2.png An access token is required, see `mapbox --help`. """ access_token = (ctx.obj and ctx.obj.get('access_token')) or None if features: features = list( cligj.normalize_feature_inputs(None, 'features', [features])) service = mapbox.Static(access_token=access_token) try: res = service.image(mapid, lon=lon, lat=lat, z=zoom, width=size[0], height=size[1], features=features, sort_keys=True) except mapbox.errors.ValidationError as exc: raise click.BadParameter(str(exc)) if res.status_code == 200: output.write(res.content) else: raise MapboxCLIException(res.text.strip())
def surface(ctx, mapid, layer, fields, features, zoom, interpolate, geojson, output): """Mapbox Surface API enables flexible querying of data stored in vector tiles on Mapbox, to create results like elevation profiles. $ mapbox surface mapbox.mapbox-terrain-v1 contour ele \\ \b "[-122.781, 45.528]" "[-122.716, 45.525]" 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 fields = fields.split(",") service = mapbox.Surface(access_token=access_token) try: res = service.surface(features, mapid=mapid, layer=layer, fields=fields, geojson=geojson, interpolate=interpolate, zoom=zoom) except mapbox.errors.ValidationError as exc: raise click.BadParameter(str(exc)) if res.status_code == 200: if geojson: click.echo(json.dumps(res.geojson()), file=stdout) else: click.echo(res.text, file=stdout) else: raise MapboxCLIException(res.text.strip())
def create_tileset(ctx, dataset, tileset, name): """Create a vector tileset from a dataset. $ mapbox datasets create-tileset dataset-id username.data Note that the tileset must start with your username and the dataset must be one that you own. To view processing status, visit https://www.mapbox.com/data/. You may not generate another tilesets from the same dataset until the first processing job has completed. All endpoints require authentication. An access token with `uploads:write` scope is required, see `mapbox --help`. """ access_token = (ctx.obj and ctx.obj.get('access_token')) or None service = mapbox.Uploader(access_token=access_token) uri = "mapbox://datasets/{username}/{dataset}".format( username=tileset.split('.')[0], dataset=dataset) res = service.create(uri, tileset, name) if res.status_code == 201: click.echo(res.text) else: raise MapboxCLIException(res.text.strip())
def match(ctx, features, profile, gps_precision): """Mapbox Map Matching API lets you use snap your GPS traces to the OpenStreetMap road and path network. $ mapbox mapmatching trace.geojson An access token is required, see `mapbox --help`. """ access_token = (ctx.obj and ctx.obj.get('access_token')) or None features = list(features) if len(features) != 1: raise click.BadParameter( "Mapmatching requires a single LineString feature") service = mapbox.MapMatcher(access_token=access_token) try: res = service.match(features[0], profile=profile, gps_precision=gps_precision) except mapbox.errors.ValidationError as exc: raise click.BadParameter(str(exc)) if res.status_code == 200: stdout = click.open_file('-', 'w') click.echo(res.text, file=stdout) else: raise MapboxCLIException(res.text.strip())
def directions(ctx, features, geojson, profile, alternatives, instructions, geometry, steps, output): """Calculate optimal route with turn-by-turn directions between up to 25 waypoints. $ mapbox directions "[-122.681032, 45.528334]" "[-122.71679, 45.525135]" 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' service = mapbox.Directions(access_token=access_token) try: res = service.directions( features, steps=steps, alternatives=alternatives, instructions=instructions, geometry=geometry, profile=profile) except mapbox.errors.ValidationError as exc: raise click.BadParameter(str(exc)) if res.status_code == 200: if geojson: click.echo(json.dumps(res.geojson()), file=stdout) else: click.echo(res.text, file=stdout) else: raise MapboxCLIException(res.text.strip())
def batch_update_features(ctx, dataset, puts, deletes, input): """Update features of a dataset. Up to 100 features may be deleted or modified in one request. PUTS should be a JSON array of GeoJSON features to insert or updated. DELETES should be a JSON array of feature ids to be deleted. $ mapbox datasets batch-update-features dataset-id 'puts' 'deletes' All endpoints require authentication. An access token with `datasets:write` scope is required, see `mapbox --help`. """ if puts: puts = json.loads(puts) if deletes: deletes = json.loads(deletes) if puts is None and deletes is None: stdin = click.open_file(input, 'r') input = json.loads(stdin.read()) puts = input['put'] deletes = input['delete'] service = ctx.obj.get('service') res = service.batch_update_features(dataset, puts, deletes) if res.status_code == 200: click.echo(res.text) else: raise MapboxCLIException(res.text.strip())
def put_feature(ctx, dataset, fid, feature, input): """Create or update a dataset feature. The semantics of HTTP PUT apply: if the dataset has no feature with the given `fid` a new feature will be created. Returns a GeoJSON representation of the new or updated feature. $ mapbox datasets put-feature dataset-id feature-id 'geojson-feature' All endpoints require authentication. An access token with `datasets:write` scope is required, see `mapbox --help`. """ if feature is None: stdin = click.open_file(input, 'r') feature = stdin.read() feature = json.loads(feature) service = ctx.obj.get('service') res = service.update_feature(dataset, fid, feature) if res.status_code == 200: click.echo(res.text) else: raise MapboxCLIException(res.text.strip())
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 upload(ctx, args, name): """Upload data to Mapbox accounts. All endpoints require authentication. Uploaded data lands at https://www.mapbox.com/data/ and can be used in new or existing projects. You can specify the input file and tileset id $ mapbox upload mydata.geojson username.data Or specify just the tileset id and take an input file on stdin $ cat mydata.geojson | mapbox upload username.data The --name option defines the title as it appears in Studio and defaults to the last part of the tileset id, e.g. "data" Note that the tileset must start with your username. An access token with upload scope is required, see `mapbox --help`. """ access_token = (ctx.obj and ctx.obj.get('access_token')) or None service = mapbox.Uploader(access_token=access_token) if len(args) == 1: # Tileset specified, file from stdin click.echo("Reading data from stdin (Hit Ctl-C to cancel) ...", err=True) infile = BytesIO(click.File("rb")("-").read()) tileset = args[0] elif len(args) == 2: # Infile and Tileset are specified try: infile = click.File("rb")(args[0]) except click.ClickException: raise click.UsageError( "Could not open file: {0} " "(check order of command arguments: INFILE TILESET)".format( args[0])) tileset = args[1] else: raise click.UsageError( "Must provide either one argument (TILESET) or two (INFILE TILESET)" ) if name is None: name = tileset.split(".")[-1] try: res = service.upload(infile, tileset, name) except (mapbox.errors.ValidationError, IOError) as exc: raise click.BadParameter(str(exc)) if res.status_code == 201: click.echo(res.text) else: raise MapboxCLIException(res.text.strip())
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 "[0, 0]" "[1, 1]" An access token is required. See "mapbox --help". """ 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, profile=profile, alternatives=alternatives, 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 geometries == "geojson": click.echo(json.dumps(res.geojson()), file=stdout) else: click.echo(res.text, file=stdout) else: raise MapboxCLIException(res.text.strip())
def delete_feature(ctx, dataset, fid): """Delete a feature. $ mapbox datasets delete-feature dataset-id feature-id All endpoints require authentication. An access token with `datasets:write` scope is required, see `mapbox --help`. """ service = ctx.obj.get('service') res = service.delete_feature(dataset, fid) if res.status_code != 204: raise MapboxCLIException(res.text.strip())
def update_dataset(ctx, dataset, name, description): """Update the name and description of a dataset. Prints a JSON object containing the updated dataset attributes. $ mapbox datasets update-dataset dataset-id All endpoints require authentication. An access token with `datasets:write` scope is required, see `mapbox --help`. """ service = ctx.obj.get('service') res = service.update_dataset(dataset, name, description) if res.status_code == 200: click.echo(res.text) else: raise MapboxCLIException(res.text.strip())
def create(ctx, name, description): """Create a new dataset. Prints a JSON object containing the attributes of the new dataset. $ mapbox datasets create All endpoints require authentication. An access token with `datasets:write` scope is required, see `mapbox --help`. """ service = ctx.obj.get('service') res = service.create(name, description) if res.status_code == 200: click.echo(res.text) else: raise MapboxCLIException(res.text.strip())
def list(ctx, output): """List datasets. Prints a list of objects describing datasets. $ mapbox datasets list All endpoints require authentication. An access token with `datasets:read` scope is required, see `mapbox --help`. """ stdout = click.open_file(output, 'w') service = ctx.obj.get('service') res = service.list() if res.status_code == 200: click.echo(res.text, file=stdout) else: raise MapboxCLIException(res.text.strip())
def list_features(ctx, dataset, reverse, start, limit, output): """Get features of a dataset. Prints the features of the dataset as a GeoJSON feature collection. $ mapbox datasets list-features dataset-id All endpoints require authentication. An access token with `datasets:read` scope is required, see `mapbox --help`. """ stdout = click.open_file(output, 'w') service = ctx.obj.get('service') res = service.list_features(dataset, reverse, start, limit) if res.status_code == 200: click.echo(res.text, file=stdout) else: raise MapboxCLIException(res.text.strip())
def read_feature(ctx, dataset, fid, output): """Read a dataset feature. Prints a GeoJSON representation of the feature. $ mapbox datasets read-feature dataset-id feature-id All endpoints require authentication. An access token with `datasets:read` scope is required, see `mapbox --help`. """ stdout = click.open_file(output, 'w') service = ctx.obj.get('service') res = service.read_feature(dataset, fid) if res.status_code == 200: click.echo(res.text, file=stdout) else: raise MapboxCLIException(res.text.strip())
def read_dataset(ctx, dataset, output): """Read the attributes of a dataset. Prints a JSON object containing the attributes of a dataset. The attributes: owner (a Mapbox account), id (dataset id), created (Unix timestamp), modified (timestamp), name (string), and description (string). $ mapbox datasets read-dataset dataset-id All endpoints require authentication. An access token with `datasets:read` scope is required, see `mapbox --help`. """ stdout = click.open_file(output, 'w') service = ctx.obj.get('service') res = service.read_dataset(dataset) if res.status_code == 200: click.echo(res.text, file=stdout) else: raise MapboxCLIException(res.text.strip())
def upload(ctx, tileset, datasource, name, patch): """Upload data to Mapbox accounts. Uploaded data lands at https://www.mapbox.com/data/ and can be used in new or existing projects. All endpoints require authentication. You can specify the tileset id and input file $ mapbox upload username.data mydata.geojson Or specify just the tileset id and take an input file on stdin $ cat mydata.geojson | mapbox upload username.data The --name option defines the title as it appears in Studio and defaults to the last part of the tileset id, e.g. "data" Note that the tileset must start with your username. An access token with upload scope is required, see `mapbox --help`. Your account must be flagged in order to use the patch mode feature. """ access_token = (ctx.obj and ctx.obj.get('access_token')) or None service = mapbox.Uploader(access_token=access_token) if name is None: name = tileset.split(".")[-1] if datasource.startswith('https://'): # Skip staging. Note this this only works for specific buckets. res = service.create(datasource, tileset, name=name, patch=patch) else: sourcefile = click.File('rb')(datasource) if hasattr(sourcefile, 'name'): filelen = (1 if sourcefile.name == '<stdin>' else os.stat( sourcefile.name).st_size) else: filelen = (len(sourcefile.getbuffer()) if hasattr( sourcefile, 'getbuffer') else 1) with click.progressbar(length=filelen, label='Uploading data source', fill_char="#", empty_char='-', file=sys.stderr) as bar: def callback(num_bytes): """Update the progress bar""" bar.update(num_bytes) res = service.upload(sourcefile, tileset, name, patch=patch, callback=callback) if res.status_code == 201: click.echo(res.text) else: raise MapboxCLIException(res.text.strip())
def geocoding(ctx, query, forward, include_headers, lat, lon, place_type, output, dataset, country, bbox, features, limit): """This command returns places matching an address (forward mode) or places matching coordinates (reverse mode). In forward (the default) mode the query argument shall be an address such as '1600 pennsylvania ave nw'. $ mapbox geocoding '1600 pennsylvania ave nw' In reverse mode the query argument shall be a JSON encoded array of longitude and latitude (in that order) in decimal degrees. $ mapbox geocoding --reverse '[-77.4371, 37.5227]' An access token is required, see `mapbox --help`. """ verbosity = (ctx.obj and ctx.obj.get('verbosity')) or 2 logger = logging.getLogger('mapbox') access_token = (ctx.obj and ctx.obj.get('access_token')) or None stdout = click.open_file(output, 'w') geocoder = mapbox.Geocoder(name=dataset, access_token=access_token) if forward: if country: country = [x.lower() for x in country.split(",")] if bbox: try: bbox = tuple(map(float, bbox.split(','))) except ValueError: bbox = json.loads(bbox) for q in iter_query(query): try: resp = geocoder.forward( q, types=place_type, lat=lat, lon=lon, country=country, bbox=bbox, limit=limit) except mapbox.errors.ValidationError as exc: raise click.BadParameter(str(exc)) if include_headers: echo_headers(resp.headers, file=stdout) if resp.status_code == 200: if features: collection = json.loads(resp.text) for feat in collection['features']: click.echo(json.dumps(feat), file=stdout) else: click.echo(resp.text, file=stdout) else: raise MapboxCLIException(resp.text.strip()) else: for lon, lat in map(coords_from_query, iter_query(query)): try: resp = geocoder.reverse( lon=lon, lat=lat, types=place_type, limit=limit) except mapbox.errors.ValidationError as exc: raise click.BadParameter(str(exc)) if include_headers: echo_headers(resp.headers, file=stdout) if resp.status_code == 200: if features: collection = json.loads(resp.text) for feat in collection['features']: click.echo(json.dumps(feat), file=stdout) else: click.echo(resp.text, file=stdout) else: raise MapboxCLIException(resp.text.strip())