def shapes( ctx, input, output, 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. dump_kwds = {'sort_keys': True} if indent: dump_kwds['indent'] = indent if compact: dump_kwds['separators'] = (',', ':') stdout = click.open_file( output, 'w') if output else click.get_text_stream('stdout') bidx = 1 if bandidx is None and band else bandidx if not sequence: geojson_type = 'collection' geographic = True if projection == 'geographic' else False try: with ctx.obj['env'] as env: with rasterio.open(input) as src: write_features( stdout, feature_gen( src, env, bidx, sampling=sampling, band=band, as_mask=as_mask, with_nodata=with_nodata, geographic=geographic, precision=precision), sequence=sequence, geojson_type=geojson_type, use_rs=use_rs, **dump_kwds) except Exception: logger.exception("Exception caught during processing") raise click.Abort()
def _shapes(in_fp, out_fp, bidx): """Code from rio CLI: https://github.com/mapbox/rasterio/blob/master/rasterio/rio/shapes.py All the click stuff is cut out, as well as some option which we don't need. Extracts shapes from one band or mask of a dataset and writes them out as GeoJSON. 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.""" dump_kwds = {'sort_keys': True} # 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(in_fp) as src: if bidx > src.count: raise ValueError('bidx is out of range for raster') # Adjust transforms. transform = src.transform # 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. msk = src.read_masks(bidx) img = src.read(bidx, masked=False) # Transform the raster bounds. bounds = src.bounds xs = [bounds[0], bounds[2]] ys = [bounds[1], bounds[3]] xs, ys = rasterio.warp.transform(src.crs, CRS({'init': 'epsg:4326'}), xs, ys) self._xs = xs self._ys = ys # Prepare keyword arguments for shapes(). kwargs = {'transform': transform} kwargs['mask'] = msk src_basename = os.path.basename(src.name) # Yield GeoJSON features. for i, (g, val) in enumerate( rasterio.features.shapes(img, **kwargs)): g = rasterio.warp.transform_geom(src.crs, 'EPSG:4326', g, antimeridian_cutting=True, precision=-1) xs, ys = zip(*coords(g)) yield { 'type': 'Feature', 'id': str(i), 'properties': { 'val': val, 'filename': src_basename, 'id': i }, 'bbox': [min(xs), min(ys), max(xs), max(ys)], 'geometry': g } with open(out_fp, "w") as f: write_features(f, Collection(), sequence=False, geojson_type='collection', use_rs=False, **dump_kwds)
def blocks(ctx, input, output, precision, indent, compact, projection, sequence, use_rs, bidx): """Write dataset blocks as GeoJSON features. This command writes features describing a raster's internal blocks, which are used directly for raster I/O. These features can be used to visualize how a windowed operation would operate using those blocks. Output features have two JSON encoded properties: block and window. Block is a two element array like '[0, 0]' describing the window's position in the input band's window layout. Window is a two element array containing two more two element arrays like '[[0, 256], [0, 256]' and describes the range of pixels the window covers in the input band. Values are JSON encoded for better interoperability. Block windows are extracted from the dataset (all bands must have matching block windows) by default, or from the band specified using the '--bidx option: \b $ rio shapes --bidx 3 tests/data/RGB.byte.tif By default a GeoJSON 'FeatureCollection' is written, but the --sequence' option produces a GeoJSON feature stream instead. \b $ rio shapes tests/data/RGB.byte.tif --sequence Output features are reprojected to 'WGS84' unless the '--projected' flag is provided, which causes the output to be kept in the input datasource's coordinate reference system. For more information on exactly what blocks and windows represent, see 'dataset.block_windows()'. """ dump_kwds = {'sort_keys': True} if indent: dump_kwds['indent'] = indent if compact: dump_kwds['separators'] = (',', ':') stdout = click.open_file( output, 'w') if output else click.get_text_stream('stdout') with ctx.obj['env'], rasterio.open(input) as src: if bidx and bidx not in src.indexes: raise click.BadParameter("Not a valid band index") collection = _Collection(dataset=src, bidx=bidx, precision=precision, geographic=projection != 'projected') write_features(stdout, collection, sequence=sequence, geojson_type='feature' if sequence else 'collection', use_rs=use_rs, **dump_kwds)
def blocks( ctx, input, output, precision, indent, compact, projection, sequence, use_rs, bidx): """Write dataset blocks as GeoJSON features. This command writes features describing a raster's internal blocks, which are used directly for raster I/O. These features can be used to visualize how a windowed operation would operate using those blocks. Output features have two JSON encoded properties: block and window. Block is a two element array like '[0, 0]' describing the window's position in the input band's window layout. Window is a two element array containing two more two element arrays like '[[0, 256], [0, 256]' and describes the range of pixels the window covers in the input band. Values are JSON encoded for better interoperability. Block windows are extracted from the dataset (all bands must have matching block windows) by default, or from the band specified using the '--bidx option: \b $ rio shapes --bidx 3 tests/data/RGB.byte.tif By default a GeoJSON 'FeatureCollection' is written, but the --sequence' option produces a GeoJSON feature stream instead. \b $ rio shapes tests/data/RGB.byte.tif --sequence Output features are reprojected to 'WGS84' unless the '--projected' flag is provided, which causes the output to be kept in the input datasource's coordinate reference system. For more information on exactly what blocks and windows represent, see 'src.block_windows()'. """ dump_kwds = {'sort_keys': True} if indent: dump_kwds['indent'] = indent if compact: dump_kwds['separators'] = (',', ':') stdout = click.open_file( output, 'w') if output else click.get_text_stream('stdout') with ctx.obj['env'], rasterio.open(input) as src: if bidx and bidx not in src.indexes: raise click.BadParameter("Not a valid band index") collection = _Collection( src=src, bidx=bidx, precision=precision, geographic=projection != 'projected') write_features( stdout, collection, sequence=sequence, geojson_type='feature' if sequence else 'collection', use_rs=use_rs, **dump_kwds)
def shapes(ctx, input, output, 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 as np import rasterio.features import rasterio.warp logger = logging.getLogger('rio') dump_kwds = {'sort_keys': True} if indent: dump_kwds['indent'] = indent if compact: dump_kwds['separators'] = (',', ':') stdout = click.open_file( output, 'w') if output else 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, env): self._xs = [] self._ys = [] self.env = env @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 bidx is not None and bidx > src.count: raise ValueError('bidx is out of range for raster') img = None msk = None # Adjust transforms. transform = src.transform if sampling > 1: # Determine the target shape (to decimate) shape = (int(math.ceil(src.height / sampling)), int(math.ceil(src.width / sampling))) # Calculate independent sampling factors x_sampling = src.width / shape[1] y_sampling = src.height / shape[0] # Decimation of the raster produces a georeferencing # shift that we correct with a translation. transform *= Affine.translation(src.width % x_sampling, src.height % y_sampling) # And follow by scaling. transform *= Affine.scale(x_sampling, y_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 = shape if bidx is None: msk = np.zeros((src.count, ) + msk_shape, 'uint8') else: msk = np.zeros(msk_shape, 'uint8') msk = src.read_masks(bidx, msk) if bidx is None: msk = np.logical_or.reduce(msk).astype('uint8') # Possibly overridden 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 = np.zeros( shape, 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 = np.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, 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 src_basename = os.path.basename(src.name) # Yield GeoJSON features. for i, (g, val) in enumerate( 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': "{0}:{1}".format(src_basename, i), 'properties': { 'val': val, 'filename': src_basename }, 'bbox': [min(xs), min(ys), max(xs), max(ys)], 'geometry': g } if not sequence: geojson_type = 'collection' try: with ctx.obj['env'] as env: write_features(stdout, Collection(env), sequence=sequence, geojson_type=geojson_type, use_rs=use_rs, **dump_kwds) except Exception: logger.exception("Exception caught during processing") raise click.Abort()
def shapes( ctx, input, output, 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 as np import rasterio.features import rasterio.warp logger = logging.getLogger('rio') dump_kwds = {'sort_keys': True} if indent: dump_kwds['indent'] = indent if compact: dump_kwds['separators'] = (',', ':') stdout = click.open_file( output, 'w') if output else 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, env): self._xs = [] self._ys = [] self.env = env @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 bidx is not None and bidx > src.count: raise ValueError('bidx is out of range for raster') img = None msk = None # Adjust transforms. transform = src.transform if sampling > 1: # Decimation of the raster produces a georeferencing # shift that we correct with a translation. transform *= Affine.translation( src.width % sampling, src.height % sampling) # And follow by scaling. transform *= 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 = np.zeros( (src.count,) + msk_shape, 'uint8') else: msk = np.zeros(msk_shape, 'uint8') msk = src.read_masks(bidx, msk) if bidx is None: msk = np.logical_or.reduce(msk).astype('uint8') # Possibly overridden 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 = np.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 = np.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, 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 src_basename = os.path.basename(src.name) # Yield GeoJSON features. for i, (g, val) in enumerate( 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': "{0}:{1}".format(src_basename, i), 'properties': { 'val': val, 'filename': src_basename }, 'bbox': [min(xs), min(ys), max(xs), max(ys)], 'geometry': g } if not sequence: geojson_type = 'collection' try: with ctx.obj['env'] as env: write_features( stdout, Collection(env), sequence=sequence, geojson_type=geojson_type, use_rs=use_rs, **dump_kwds) except Exception: logger.exception("Exception caught during processing") raise click.Abort()