def shapes(ctx, input, precision, indent, compact, projection, sequence, use_rs, geojson_type, band, bandidx, sampling, with_nodata, as_mask): """Extracts shapes from one band or mask of a dataset and writes them out as GeoJSON. Unless otherwise specified, the shapes will be transformed to WGS 84 coordinates. The default action of this command is to extract shapes from the first band of the input dataset. The shapes are polygons bounding contiguous regions (or features) of the same raster value. This command performs poorly for int16 or float type datasets. Bands other than the first can be specified using the `--bidx` option: $ rio shapes --bidx 3 tests/data/RGB.byte.tif The valid data footprint of a dataset's i-th band can be extracted by using the `--mask` and `--bidx` options: $ rio shapes --mask --bidx 1 tests/data/RGB.byte.tif Omitting the `--bidx` option results in a footprint extracted from the conjunction of all band masks. This is generally smaller than any individual band's footprint. A dataset band may be analyzed as though it were a binary mask with the `--as-mask` option: $ rio shapes --as-mask --bidx 1 tests/data/RGB.byte.tif """ # These import numpy, which we don't want to do unless it's needed. import numpy import rasterio.features import rasterio.warp verbosity = ctx.obj['verbosity'] if ctx.obj else 1 logger = logging.getLogger('rio') dump_kwds = {'sort_keys': True} if indent: dump_kwds['indent'] = indent if compact: dump_kwds['separators'] = (',', ':') stdout = click.get_text_stream('stdout') bidx = 1 if bandidx is None and band else bandidx # This is the generator for (feature, bbox) pairs. class Collection(object): def __init__(self): self._xs = [] self._ys = [] @property def bbox(self): return min(self._xs), min(self._ys), max(self._xs), max(self._ys) def __call__(self): with rasterio.open(input) as src: img = None msk = None # Adjust transforms. if sampling == 1: transform = src.affine else: transform = src.affine * Affine.scale(float(sampling)) # Most of the time, we'll use the valid data mask. # We skip reading it if we're extracting every possible # feature (even invalid data features) from a band. if not band or (band and not as_mask and not with_nodata): if sampling == 1: msk = src.read_masks(bidx) else: msk_shape = (src.height // sampling, src.width // sampling) if bidx is None: msk = numpy.zeros((src.count, ) + msk_shape, 'uint8') else: msk = numpy.zeros(msk_shape, 'uint8') msk = src.read_masks(bidx, msk) if bidx is None: msk = numpy.logical_or.reduce(msk).astype('uint8') # Possibly overidden below. img = msk # Read the band data unless the --mask option is given. if band: if sampling == 1: img = src.read(bidx, masked=False) else: img = numpy.zeros( (src.height // sampling, src.width // sampling), dtype=src.dtypes[src.indexes.index(bidx)]) img = src.read(bidx, img, masked=False) # If --as-mask option was given, convert the image # to a binary image. This reduces the number of shape # categories to 2 and likely reduces the number of # shapes. if as_mask: tmp = numpy.ones_like(img, 'uint8') * 255 tmp[img == 0] = 0 img = tmp if not with_nodata: msk = tmp # Transform the raster bounds. bounds = src.bounds xs = [bounds[0], bounds[2]] ys = [bounds[1], bounds[3]] if projection == 'geographic': xs, ys = rasterio.warp.transform(src.crs, {'init': 'epsg:4326'}, xs, ys) if precision >= 0: xs = [round(v, precision) for v in xs] ys = [round(v, precision) for v in ys] self._xs = xs self._ys = ys # Prepare keyword arguments for shapes(). kwargs = {'transform': transform} if not with_nodata: kwargs['mask'] = msk # Yield GeoJSON features. for g, i in rasterio.features.shapes(img, **kwargs): if projection == 'geographic': g = rasterio.warp.transform_geom( src.crs, 'EPSG:4326', g, antimeridian_cutting=True, precision=precision) xs, ys = zip(*coords(g)) yield { 'type': 'Feature', 'id': str(i), 'properties': { 'val': i, 'filename': os.path.basename(src.name) }, 'bbox': [min(xs), min(ys), max(xs), max(ys)], 'geometry': g } if not sequence: geojson_type = 'collection' try: with rasterio.drivers(CPL_DEBUG=(verbosity > 2)): write_features(stdout, Collection(), sequence=sequence, geojson_type=geojson_type, use_rs=use_rs, **dump_kwds) sys.exit(0) except Exception: logger.exception("Failed. Exception caught") sys.exit(1)
def shapes( ctx, input, precision, indent, compact, projection, sequence, use_rs, geojson_type, bands, bidx, sampling, with_nodata): """Writes features of a dataset out as GeoJSON. It's intended for use with single-band rasters and reads from the first band. """ # These import numpy, which we don't want to do unless it's needed. import numpy import rasterio.features import rasterio.warp verbosity = ctx.obj['verbosity'] if ctx.obj else 1 logger = logging.getLogger('rio') dump_kwds = {'sort_keys': True} if indent: dump_kwds['indent'] = indent if compact: dump_kwds['separators'] = (',', ':') stdout = click.get_text_stream('stdout') # This is the generator for (feature, bbox) pairs. class Collection(object): def __init__(self): self._xs = [] self._ys = [] @property def bbox(self): return min(self._xs), min(self._ys), max(self._xs), max(self._ys) def __call__(self): with rasterio.open(input) as src: img = None nodata_mask = None if bands: if sampling == 1: img = src.read(bidx, masked=False) transform = src.transform # Decimate the band. else: img = numpy.zeros( (src.height//sampling, src.width//sampling), dtype=src.dtypes[src.indexes.index(bidx)]) img = src.read(bidx, img, masked=False) transform = src.affine * Affine.scale(float(sampling)) if not bands or not with_nodata: if sampling == 1: nodata_mask = src.read_masks(bidx) transform = src.transform # Decimate the mask. else: nodata_mask = numpy.zeros( (src.height//sampling, src.width//sampling), dtype=numpy.uint8) nodata_mask = src.read_masks(bidx, nodata_mask) transform = src.affine * Affine.scale(float(sampling)) bounds = src.bounds xs = [bounds[0], bounds[2]] ys = [bounds[1], bounds[3]] if projection == 'geographic': xs, ys = rasterio.warp.transform( src.crs, {'init': 'epsg:4326'}, xs, ys) if precision >= 0: xs = [round(v, precision) for v in xs] ys = [round(v, precision) for v in ys] self._xs = xs self._ys = ys kwargs = {'transform': transform} # Default is to exclude nodata features. if nodata_mask is not None: kwargs['mask'] = (nodata_mask==255) if img is None: img = nodata_mask for g, i in rasterio.features.shapes(img, **kwargs): if projection == 'geographic': g = rasterio.warp.transform_geom( src.crs, 'EPSG:4326', g, antimeridian_cutting=True, precision=precision) xs, ys = zip(*coords(g)) yield { 'type': 'Feature', 'id': str(i), 'properties': {'val': i}, 'bbox': [min(xs), min(ys), max(xs), max(ys)], 'geometry': g } if not sequence: geojson_type = 'collection' try: with rasterio.drivers(CPL_DEBUG=verbosity>2): write_features( stdout, Collection(), sequence=sequence, geojson_type=geojson_type, use_rs=use_rs, **dump_kwds) sys.exit(0) except Exception: logger.exception("Failed. Exception caught") sys.exit(1)
def shapes( ctx, input, precision, indent, compact, projection, sequence, use_rs, geojson_type, band, bandidx, sampling, with_nodata, as_mask): """Extracts shapes from one band or mask of a dataset and writes them out as GeoJSON. Unless otherwise specified, the shapes will be transformed to WGS 84 coordinates. The default action of this command is to extract shapes from the first band of the input dataset. The shapes are polygons bounding contiguous regions (or features) of the same raster value. This command performs poorly for int16 or float type datasets. Bands other than the first can be specified using the `--bidx` option: $ rio shapes --bidx 3 tests/data/RGB.byte.tif The valid data footprint of a dataset's i-th band can be extracted by using the `--mask` and `--bidx` options: $ rio shapes --mask --bidx 1 tests/data/RGB.byte.tif Omitting the `--bidx` option results in a footprint extracted from the conjunction of all band masks. This is generally smaller than any individual band's footprint. A dataset band may be analyzed as though it were a binary mask with the `--as-mask` option: $ rio shapes --as-mask --bidx 1 tests/data/RGB.byte.tif """ # These import numpy, which we don't want to do unless it's needed. import numpy import rasterio.features import rasterio.warp verbosity = ctx.obj['verbosity'] if ctx.obj else 1 logger = logging.getLogger('rio') dump_kwds = {'sort_keys': True} if indent: dump_kwds['indent'] = indent if compact: dump_kwds['separators'] = (',', ':') stdout = click.get_text_stream('stdout') bidx = 1 if bandidx is None and band else bandidx # This is the generator for (feature, bbox) pairs. class Collection(object): def __init__(self): self._xs = [] self._ys = [] @property def bbox(self): return min(self._xs), min(self._ys), max(self._xs), max(self._ys) def __call__(self): with rasterio.open(input) as src: img = None msk = None # Adjust transforms. if sampling == 1: transform = src.affine else: transform = src.affine * Affine.scale(float(sampling)) # Most of the time, we'll use the valid data mask. # We skip reading it if we're extracting every possible # feature (even invalid data features) from a band. if not band or (band and not as_mask and not with_nodata): if sampling == 1: msk = src.read_masks(bidx) else: msk_shape = ( src.height//sampling, src.width//sampling) if bidx is None: msk = numpy.zeros( (src.count,) + msk_shape, 'uint8') else: msk = numpy.zeros(msk_shape, 'uint8') msk = src.read_masks(bidx, msk) if bidx is None: msk = numpy.logical_or.reduce(msk).astype('uint8') # Possibly overidden below. img = msk # Read the band data unless the --mask option is given. if band: if sampling == 1: img = src.read(bidx, masked=False) else: img = numpy.zeros( (src.height//sampling, src.width//sampling), dtype=src.dtypes[src.indexes.index(bidx)]) img = src.read(bidx, img, masked=False) # If --as-mask option was given, convert the image # to a binary image. This reduces the number of shape # categories to 2 and likely reduces the number of # shapes. if as_mask: tmp = numpy.ones_like(img, 'uint8') * 255 tmp[img == 0] = 0 img = tmp if not with_nodata: msk = tmp # Transform the raster bounds. bounds = src.bounds xs = [bounds[0], bounds[2]] ys = [bounds[1], bounds[3]] if projection == 'geographic': xs, ys = rasterio.warp.transform( src.crs, {'init': 'epsg:4326'}, xs, ys) if precision >= 0: xs = [round(v, precision) for v in xs] ys = [round(v, precision) for v in ys] self._xs = xs self._ys = ys # Prepare keyword arguments for shapes(). kwargs = {'transform': transform} if not with_nodata: kwargs['mask'] = msk # Yield GeoJSON features. for g, i in rasterio.features.shapes(img, **kwargs): if projection == 'geographic': g = rasterio.warp.transform_geom( src.crs, 'EPSG:4326', g, antimeridian_cutting=True, precision=precision) xs, ys = zip(*coords(g)) yield { 'type': 'Feature', 'id': str(i), 'properties': { 'val': i, 'filename': os.path.basename(src.name) }, 'bbox': [min(xs), min(ys), max(xs), max(ys)], 'geometry': g } if not sequence: geojson_type = 'collection' try: with rasterio.drivers(CPL_DEBUG=(verbosity > 2)): write_features( stdout, Collection(), sequence=sequence, geojson_type=geojson_type, use_rs=use_rs, **dump_kwds) sys.exit(0) except Exception: logger.exception("Failed. Exception caught") sys.exit(1)
def bounds(ctx, input, precision, indent, compact, projection, sequence, use_rs, geojson_type): """Write bounding boxes to stdout as GeoJSON for use with, e.g., geojsonio $ rio bounds *.tif | geojsonio """ import rasterio.warp verbosity = (ctx.obj and ctx.obj.get('verbosity')) or 1 logger = logging.getLogger('rio') dump_kwds = {'sort_keys': True} if indent: dump_kwds['indent'] = indent if compact: dump_kwds['separators'] = (',', ':') stdout = click.get_text_stream('stdout') # This is the generator for (feature, bbox) pairs. class Collection(object): def __init__(self): self._xs = [] self._ys = [] @property def bbox(self): return min(self._xs), min(self._ys), max(self._xs), max(self._ys) def __call__(self): for i, path in enumerate(input): with rasterio.open(path) as src: bounds = src.bounds xs = [bounds[0], bounds[2]] ys = [bounds[1], bounds[3]] if projection == 'geographic': xs, ys = rasterio.warp.transform( src.crs, {'init': 'epsg:4326'}, xs, ys) if projection == 'mercator': xs, ys = rasterio.warp.transform( src.crs, {'init': 'epsg:3857'}, xs, ys) if precision >= 0: xs = [round(v, precision) for v in xs] ys = [round(v, precision) for v in ys] bbox = [min(xs), min(ys), max(xs), max(ys)] yield { 'type': 'Feature', 'bbox': bbox, 'geometry': { 'type': 'Polygon', 'coordinates': [[ [xs[0], ys[0]], [xs[1], ys[0]], [xs[1], ys[1]], [xs[0], ys[1]], [xs[0], ys[0]] ]]}, 'properties': { 'id': str(i), 'title': path, 'filename': os.path.basename(path)} } self._xs.extend(bbox[::2]) self._ys.extend(bbox[1::2]) col = Collection() # Use the generator defined above as input to the generic output # writing function. try: with rasterio.drivers(CPL_DEBUG=verbosity>2): write_features( stdout, col, sequence=sequence, geojson_type=geojson_type, use_rs=use_rs, **dump_kwds) sys.exit(0) except Exception: logger.exception("Failed. Exception caught") sys.exit(1)
def shapes(ctx, input, precision, indent, compact, projected, json_mode, x_json_seq_rs, output_mode, bands, bidx, sampling, with_nodata): """Writes features of a dataset out as GeoJSON. It's intended for use with single-band rasters and reads from the first band. """ # These import numpy, which we don't want to do unless its needed. import numpy import rasterio.features import rasterio.warp verbosity = ctx.obj['verbosity'] logger = logging.getLogger('rio') dump_kwds = {'sort_keys': True} if indent: dump_kwds['indent'] = indent if compact: dump_kwds['separators'] = (',', ':') stdout = click.get_text_stream('stdout') # This is the generator for (feature, bbox) pairs. class Collection(object): def __init__(self): self._xs = [] self._ys = [] @property def bbox(self): return min(self._xs), min(self._ys), max(self._xs), max(self._ys) def __call__(self): with rasterio.open(input) as src: if bands: if sampling == 1: img = src.read_band(bidx) transform = src.transform # Decimate the band. else: img = numpy.zeros( (src.height // sampling, src.width // sampling), dtype=src.dtypes[src.indexes.index(bidx)]) img = src.read_band(bidx, img) transform = src.affine * Affine.scale(float(sampling)) else: if sampling == 1: img = src.read_mask() transform = src.transform # Decimate the mask. else: img = numpy.zeros( (src.height // sampling, src.width // sampling), dtype=numpy.uint8) img = src.read_mask(img) transform = src.affine * Affine.scale(float(sampling)) bounds = src.bounds xs = [bounds[0], bounds[2]] ys = [bounds[1], bounds[3]] if projected == 'geographic': xs, ys = rasterio.warp.transform(src.crs, {'init': 'epsg:4326'}, xs, ys) if precision >= 0: xs = [round(v, precision) for v in xs] ys = [round(v, precision) for v in ys] self._xs = xs self._ys = ys kwargs = {'transform': transform} if not bands and not with_nodata: kwargs['mask'] = (img == 255) for g, i in rasterio.features.shapes(img, **kwargs): if projected == 'geographic': g = rasterio.warp.transform_geom( src.crs, 'EPSG:4326', g, antimeridian_cutting=True, precision=precision) xs, ys = zip(*coords(g)) yield { 'type': 'Feature', 'id': str(i), 'properties': { 'val': i }, 'bbox': [min(xs), min(ys), max(xs), max(ys)], 'geometry': g } try: with rasterio.drivers(CPL_DEBUG=verbosity > 2): write_features(stdout, Collection(), agg_mode=json_mode, expression=output_mode, use_rs=x_json_seq_rs, **dump_kwds) sys.exit(0) except Exception: logger.exception("Failed. Exception caught") sys.exit(1)
def bounds(ctx, input, precision, indent, compact, projection, sequence, use_rs, geojson_type): """Write bounding boxes to stdout as GeoJSON for use with, e.g., geojsonio $ rio bounds *.tif | geojsonio """ import rasterio.warp verbosity = (ctx.obj and ctx.obj.get('verbosity')) or 1 logger = logging.getLogger('rio') dump_kwds = {'sort_keys': True} if indent: dump_kwds['indent'] = indent if compact: dump_kwds['separators'] = (',', ':') stdout = click.get_text_stream('stdout') # This is the generator for (feature, bbox) pairs. class Collection(object): def __init__(self): self._xs = [] self._ys = [] @property def bbox(self): return min(self._xs), min(self._ys), max(self._xs), max(self._ys) def __call__(self): for i, path in enumerate(input): with rasterio.open(path) as src: bounds = src.bounds xs = [bounds[0], bounds[2]] ys = [bounds[1], bounds[3]] if projection == 'geographic': xs, ys = rasterio.warp.transform( src.crs, {'init': 'epsg:4326'}, xs, ys) if projection == 'mercator': xs, ys = rasterio.warp.transform( src.crs, {'init': 'epsg:3857'}, xs, ys) if precision >= 0: xs = [round(v, precision) for v in xs] ys = [round(v, precision) for v in ys] bbox = [min(xs), min(ys), max(xs), max(ys)] yield { 'type': 'Feature', 'bbox': bbox, 'geometry': { 'type': 'Polygon', 'coordinates': [[[xs[0], ys[0]], [xs[1], ys[0]], [xs[1], ys[1]], [xs[0], ys[1]], [xs[0], ys[0]]]] }, 'properties': { 'id': str(i), 'title': path, 'filename': os.path.basename(path) } } self._xs.extend(bbox[::2]) self._ys.extend(bbox[1::2]) col = Collection() # Use the generator defined above as input to the generic output # writing function. try: with rasterio.drivers(CPL_DEBUG=verbosity > 2): write_features(stdout, col, sequence=sequence, geojson_type=geojson_type, use_rs=use_rs, **dump_kwds) sys.exit(0) except Exception: logger.exception("Failed. Exception caught") sys.exit(1)