def rasterize_geojson(geojson, resolution, dst_raster, src_crs): ''' Rasterize a geojson vector to a raster tiff file using rasterio library. It is mandatory to specify the coordinate reference system (crs) of the input geojson as the corresponding code in the ESPG system (ex:3857) resolution is the pixel size of the output raster in the unit system of crs. Usually metres for projected coordinates (ex: 3857) and degrees for non-projected crs (ex: 4326) Returns both the raster path and the numpy ndarray of the raster. ''' from rasterio.features import bounds as calculate_bounds from math import ceil from affine import Affine from rasterio.features import rasterize import rasterio bounds = calculate_bounds(geojson) res = (resolution, resolution) geometries = ((geojson["geometry"], 1), ) params = { 'count': 1, 'crs': 'EPSG:{}'.format(src_crs), 'width': max(int(ceil((bounds[2] - bounds[0]) / float(res[0]))), 1), 'height': max(int(ceil((bounds[3] - bounds[1]) / float(res[1]))), 1), 'driver': 'GTiff', 'transform': Affine(res[0], 0, bounds[0], 0, -res[1], bounds[3]), 'nodata': 0, 'dtype': 'uint8' } output = rasterize(geometries, out_shape=(params['height'], params['width']), transform=params['transform']) with rasterio.open(dst_raster, 'w', **params) as dst: dst.write(output, indexes=1) return dst_raster, output
def mask(ctx, input, output, geojson_mask, driver, all_touched, crop, invert): """Masks in raster using GeoJSON features (masks out all areas not covered by features), and optionally crops the output raster to the extent of the features. Features are assumed to be in the same coordinate reference system as the input raster. GeoJSON must be the first input file or provided from stdin: > rio mask input.tif output.tif --geojson-mask features.json > rio mask input.tif output.tif --geojson-mask - < features.json If the output raster exists, it will be completely overwritten with the results of this operation. The result is always equal to or within the bounds of the input raster. --crop and --invert options are mutually exclusive. --crop option is not valid if features are completely outside extent of input raster. """ from rasterio.features import geometry_mask from rasterio.features import bounds as calculate_bounds verbosity = (ctx.obj and ctx.obj.get('verbosity')) or 1 if geojson_mask is None: click.echo('No GeoJSON provided, INPUT will be copied to OUTPUT', err=True) shutil.copy(input, output) return if crop and invert: click.echo('Invert option ignored when using --crop', err=True) invert = False with rasterio.drivers(CPL_DEBUG=verbosity > 2): try: geojson = json.loads(click.open_file(geojson_mask).read()) except ValueError: raise click.BadParameter( 'GeoJSON could not be read from ' '--geojson-mask or stdin', param_hint='--geojson-mask') if 'features' in geojson: geometries = (f['geometry'] for f in geojson['features']) elif 'geometry' in geojson: geometries = (geojson['geometry'], ) else: raise click.BadParameter('Invalid GeoJSON', param=input, param_hint='input') bounds = geojson.get('bbox', calculate_bounds(geojson)) with rasterio.open(input) as src: disjoint_bounds = _disjoint_bounds(bounds, src.bounds) if crop: if disjoint_bounds: raise click.BadParameter( 'not allowed for GeoJSON outside ' 'the extent of the input raster', param=crop, param_hint='--crop') window = src.window(*bounds) transform = src.window_transform(window) (r1, r2), (c1, c2) = window mask_shape = (r2 - r1, c2 - c1) else: if disjoint_bounds: click.echo( 'GeoJSON outside bounds of existing output ' 'raster. Are they in different coordinate ' 'reference systems?', err=True) window = None transform = src.affine mask_shape = src.shape mask = geometry_mask(geometries, out_shape=mask_shape, transform=transform, all_touched=all_touched, invert=invert) meta = src.meta.copy() meta.update({ 'driver': driver, 'height': mask.shape[0], 'width': mask.shape[1], 'transform': transform }) with rasterio.open(output, 'w', **meta) as out: for bidx in range(1, src.count + 1): img = src.read(bidx, masked=True, window=window) img.mask = img.mask | mask out.write_band(bidx, img.filled(src.nodatavals[bidx - 1]))
def rasterize(ctx, files, driver, like, bounds, dimensions, res, src_crs, all_touched, default_value, fill, property): """Rasterize GeoJSON into a new or existing raster. If the output raster exists, rio-rasterize will rasterize feature values into all bands of that raster. The GeoJSON is assumed to be in the same coordinate reference system as the output unless --src-crs is provided. --default_value or property values when using --property must be using a data type valid for the data type of that raster. If a template raster is provided using the --like option, the affine transform and data type from that raster will be used to create the output. Only a single band will be output. The GeoJSON is assumed to be in the same coordinate reference system unless --src-crs is provided. --default_value or property values when using --property must be using a data type valid for the data type of that raster. --driver, --bounds, --dimensions, and --res are ignored when output exists or --like raster is provided If the output does not exist and --like raster is not provided, the input GeoJSON will be used to determine the bounds of the output unless provided using --bounds. --dimensions or --res are required in this case. If --res is provided, the bottom and right coordinates of bounds are ignored. Note: The GeoJSON is not projected to match the coordinate reference system of the output or --like rasters at this time. This functionality may be added in the future. """ from rasterio._base import is_geographic_crs, is_same_crs from rasterio.features import rasterize from rasterio.features import bounds as calculate_bounds verbosity = (ctx.obj and ctx.obj.get('verbosity')) or 1 files = list(files) output = files.pop() input = click.open_file(files.pop(0) if files else '-') has_src_crs = src_crs is not None src_crs = src_crs or 'EPSG:4326' # If values are actually meant to be integers, we need to cast them # as such or rasterize creates floating point outputs if default_value == int(default_value): default_value = int(default_value) if fill == int(fill): fill = int(fill) with rasterio.drivers(CPL_DEBUG=verbosity > 2): def feature_value(feature): if property and 'properties' in feature: return feature['properties'].get(property, default_value) return default_value geojson = json.loads(input.read()) if 'features' in geojson: geometries = [] for f in geojson['features']: geometries.append((f['geometry'], feature_value(f))) elif 'geometry' in geojson: geometries = ((geojson['geometry'], feature_value(geojson)), ) else: raise click.BadParameter('Invalid GeoJSON', param=input, param_hint='input') geojson_bounds = geojson.get('bbox', calculate_bounds(geojson)) if os.path.exists(output): with rasterio.open(output, 'r+') as out: if has_src_crs and not is_same_crs(src_crs, out.crs): raise click.BadParameter( 'GeoJSON does not match crs of ' 'existing output raster', param='input', param_hint='input') if _disjoint_bounds(geojson_bounds, out.bounds): click.echo( "GeoJSON outside bounds of existing output " "raster. Are they in different coordinate " "reference systems?", err=True) meta = out.meta.copy() result = rasterize(geometries, out_shape=(meta['height'], meta['width']), transform=meta.get('affine', meta['transform']), all_touched=all_touched, dtype=meta.get('dtype', None), default_value=default_value, fill=fill) for bidx in range(1, meta['count'] + 1): data = out.read_band(bidx, masked=True) # Burn in any non-fill pixels, and update mask accordingly ne = result != fill data[ne] = result[ne] data.mask[ne] = False out.write_band(bidx, data) else: if like is not None: template_ds = rasterio.open(like) if has_src_crs and not is_same_crs(src_crs, template_ds.crs): raise click.BadParameter( 'GeoJSON does not match crs of ' '--like raster', param='input', param_hint='input') if _disjoint_bounds(geojson_bounds, template_ds.bounds): click.echo( "GeoJSON outside bounds of --like raster. " "Are they in different coordinate reference " "systems?", err=True) kwargs = template_ds.meta.copy() kwargs['count'] = 1 template_ds.close() else: bounds = bounds or geojson_bounds if is_geographic_crs(src_crs): if (bounds[0] < -180 or bounds[2] > 180 or bounds[1] < -80 or bounds[3] > 80): raise click.BadParameter( "Bounds are beyond the valid extent for " "EPSG:4326.", ctx, param=bounds, param_hint='--bounds') if dimensions: width, height = dimensions res = ((bounds[2] - bounds[0]) / float(width), (bounds[3] - bounds[1]) / float(height)) else: if not res: raise click.BadParameter( 'pixel dimensions are required', ctx, param=res, param_hint='--res') elif len(res) == 1: res = (res[0], res[0]) width = max( int(ceil((bounds[2] - bounds[0]) / float(res[0]))), 1) height = max( int(ceil((bounds[3] - bounds[1]) / float(res[1]))), 1) src_crs = src_crs.upper() if not src_crs.count('EPSG:'): raise click.BadParameter( 'invalid CRS. Must be an EPSG code.', ctx, param=src_crs, param_hint='--src_crs') kwargs = { 'count': 1, 'crs': src_crs, 'width': width, 'height': height, 'transform': Affine(res[0], 0, bounds[0], 0, -res[1], bounds[3]), 'driver': driver } result = rasterize(geometries, out_shape=(kwargs['height'], kwargs['width']), transform=kwargs.get('affine', kwargs['transform']), all_touched=all_touched, dtype=kwargs.get('dtype', None), default_value=default_value, fill=fill) if 'dtype' not in kwargs: kwargs['dtype'] = result.dtype kwargs['nodata'] = fill with rasterio.open(output, 'w', **kwargs) as out: out.write_band(1, result)
def rasterize( ctx, files, driver, like, bounds, dimensions, res, src_crs, all_touched, default_value, fill, property): """Rasterize GeoJSON into a new or existing raster. If the output raster exists, rio-rasterize will rasterize feature values into all bands of that raster. The GeoJSON is assumed to be in the same coordinate reference system as the output. --default_value or property values when using --property must be using a data type valid for the data type of that raster. If a template raster is provided using the --like option, the affine transform, coordinate reference system, and data type from that raster will be used to create the output. --default_value or property values when using --property must be using a data type valid for the data type of that raster. --driver, --bounds, --dimensions, --res, and --src_crs are ignored when output exists or --like raster is provided The GeoJSON must be within the bounds of the existing output or --like raster, or an error will be raised. If the output does not exist and --like raster is not provided, the input GeoJSON will be used to determine the bounds of the output unless provided using --bounds. --dimensions or --res are required in this case. If --res is provided, the bottom and right coordinates of bounds are ignored. Note: The GeoJSON is not projected to match the coordinate reference system of the output or --like rasters at this time. This functionality may be added in the future. """ import numpy as np from rasterio.features import rasterize from rasterio.features import bounds as calculate_bounds verbosity = (ctx.obj and ctx.obj.get('verbosity')) or 1 files = list(files) output = files.pop() input = click.open_file(files.pop(0) if files else '-') # If values are actually meant to be integers, we need to cast them # as such or rasterize creates floating point outputs if default_value == int(default_value): default_value = int(default_value) if fill == int(fill): fill = int(fill) with rasterio.drivers(CPL_DEBUG=verbosity > 2): def feature_value(feature): if property and 'properties' in feature: return feature['properties'].get(property, default_value) return default_value def disjoint_bounds(bounds1, bounds2): return (bounds1[0] > bounds2[2] or bounds1[2] < bounds2[0] or bounds1[1] > bounds2[3] or bounds1[3] < bounds2[1]) geojson = json.loads(input.read()) if 'features' in geojson: geometries = [] for f in geojson['features']: geometries.append((f['geometry'], feature_value(f))) elif 'geometry' in geojson: geometries = ((geojson['geometry'], feature_value(geojson)), ) else: raise click.BadParameter('Invalid GeoJSON', param=input, param_hint='input') geojson_bounds = geojson.get('bbox', calculate_bounds(geojson)) if os.path.exists(output): with rasterio.open(output, 'r+') as out: if disjoint_bounds(geojson_bounds, out.bounds): raise click.BadParameter('GeoJSON outside bounds of ' 'existing output raster', param=input, param_hint='input') meta = out.meta.copy() result = rasterize( geometries, out_shape=(meta['height'], meta['width']), transform=meta.get('affine', meta['transform']), all_touched=all_touched, dtype=meta.get('dtype', None), default_value=default_value, fill = fill) for bidx in range(1, meta['count'] + 1): data = out.read_band(bidx, masked=True) # Burn in any non-fill pixels, and update mask accordingly ne = result != fill data[ne] = result[ne] data.mask[ne] = False out.write_band(bidx, data) else: if like is not None: template_ds = rasterio.open(like) if disjoint_bounds(geojson_bounds, template_ds.bounds): raise click.BadParameter('GeoJSON outside bounds of ' '--like raster', param=input, param_hint='input') kwargs = template_ds.meta.copy() kwargs['count'] = 1 template_ds.close() else: bounds = bounds or geojson_bounds if src_crs == 'EPSG:4326': if (bounds[0] < -180 or bounds[2] > 180 or bounds[1] < -80 or bounds[3] > 80): raise click.BadParameter( 'Bounds are beyond the valid extent for EPSG:4326.', ctx, param=bounds, param_hint='--bounds') if dimensions: width, height = dimensions res = ( (bounds[2] - bounds[0]) / float(width), (bounds[3] - bounds[1]) / float(height) ) else: if not res: raise click.BadParameter( 'pixel dimensions are required', ctx, param=res, param_hint='--res') elif len(res) == 1: res = (res[0], res[0]) width = max(int(ceil((bounds[2] - bounds[0]) / float(res[0]))), 1) height = max(int(ceil((bounds[3] - bounds[1]) / float(res[1]))), 1) src_crs = src_crs.upper() if not src_crs.count('EPSG:'): raise click.BadParameter( 'invalid CRS. Must be an EPSG code.', ctx, param=src_crs, param_hint='--src_crs') kwargs = { 'count': 1, 'crs': src_crs, 'width': width, 'height': height, 'transform': Affine(res[0], 0, bounds[0], 0, -res[1], bounds[3]), 'driver': driver } result = rasterize( geometries, out_shape=(kwargs['height'], kwargs['width']), transform=kwargs.get('affine', kwargs['transform']), all_touched=all_touched, dtype=kwargs.get('dtype', None), default_value=default_value, fill = fill) if 'dtype' not in kwargs: kwargs['dtype'] = result.dtype kwargs['nodata'] = fill with rasterio.open(output, 'w', **kwargs) as out: out.write_band(1, result)
def mask( ctx, files, output, geojson_mask, driver, all_touched, crop, invert, creation_options): """Masks in raster using GeoJSON features (masks out all areas not covered by features), and optionally crops the output raster to the extent of the features. Features are assumed to be in the same coordinate reference system as the input raster. GeoJSON must be the first input file or provided from stdin: > rio mask input.tif output.tif --geojson-mask features.json > rio mask input.tif output.tif --geojson-mask - < features.json If the output raster exists, it will be completely overwritten with the results of this operation. The result is always equal to or within the bounds of the input raster. --crop and --invert options are mutually exclusive. --crop option is not valid if features are completely outside extent of input raster. """ from rasterio.features import geometry_mask from rasterio.features import bounds as calculate_bounds verbosity = (ctx.obj and ctx.obj.get('verbosity')) or 1 output, files = resolve_inout(files=files, output=output) input = files[0] if geojson_mask is None: click.echo('No GeoJSON provided, INPUT will be copied to OUTPUT', err=True) shutil.copy(input, output) return if crop and invert: click.echo('Invert option ignored when using --crop', err=True) invert = False with rasterio.drivers(CPL_DEBUG=verbosity > 2): try: with click.open_file(geojson_mask) as f: geojson = json.loads(f.read()) except ValueError: raise click.BadParameter('GeoJSON could not be read from ' '--geojson-mask or stdin', param_hint='--geojson-mask') if 'features' in geojson: geometries = (f['geometry'] for f in geojson['features']) elif 'geometry' in geojson: geometries = (geojson['geometry'], ) else: raise click.BadParameter('Invalid GeoJSON', param=input, param_hint='input') bounds = geojson.get('bbox', calculate_bounds(geojson)) with rasterio.open(input) as src: # If y pixel value is positive, then invert y dimension in bounds invert_y = src.affine.e > 0 src_bounds = src.bounds if invert_y: src_bounds = [src.bounds[0], src.bounds[3], src.bounds[2], src.bounds[1]] has_disjoint_bounds = disjoint_bounds(bounds, src_bounds) if crop: if has_disjoint_bounds: raise click.BadParameter('not allowed for GeoJSON outside ' 'the extent of the input raster', param=crop, param_hint='--crop') if invert_y: bounds = (bounds[0], bounds[3], bounds[2], bounds[1]) window = src.window(*bounds) transform = src.window_transform(window) (r1, r2), (c1, c2) = window mask_shape = (r2 - r1, c2 - c1) else: if has_disjoint_bounds: click.echo('GeoJSON outside bounds of existing output ' 'raster. Are they in different coordinate ' 'reference systems?', err=True) window = None transform = src.affine mask_shape = src.shape mask = geometry_mask( geometries, out_shape=mask_shape, transform=transform, all_touched=all_touched, invert=invert) meta = src.meta.copy() meta.update(**creation_options) meta.update({ 'driver': driver, 'height': mask.shape[0], 'width': mask.shape[1], 'transform': transform }) with rasterio.open(output, 'w', **meta) as out: for bidx in range(1, src.count + 1): img = src.read(bidx, masked=True, window=window) img.mask = img.mask | mask out.write_band(bidx, img.filled(src.nodatavals[bidx-1]))
def rasterize( geojson, output, like=None, bounds=None, dimensions=None, res=None, src_crs=None, all_touched=None, default_value=1, fill=0, prop=None, force_overwrite=None, creation_options={}, driver='GTiff'): from rasterio._base import is_geographic_crs, is_same_crs from rasterio.features import rasterize from rasterio.features import bounds as calculate_bounds has_src_crs = src_crs is not None src_crs = src_crs or 'EPSG:4326' # If values are actually meant to be integers, we need to cast them # as such or rasterize creates floating point outputs if default_value == int(default_value): default_value = int(default_value) if fill == int(fill): fill = int(fill) with rasterio.drivers(): def feature_value(feature): if prop and 'properties' in feature: return feature['properties'].get(prop, default_value) return default_value if 'features' in geojson: geometries = [] for f in geojson['features']: geometries.append((f['geometry'], feature_value(f))) elif 'geometry' in geojson: geometries = ((geojson['geometry'], feature_value(geojson)), ) else: raise Exception geojson_bounds = geojson.get('bbox', calculate_bounds(geojson)) if like is not None: template_ds = rasterio.open(like) if has_src_crs and not is_same_crs(src_crs, template_ds.crs): raise Exception if disjoint_bounds(geojson_bounds, template_ds.bounds): print "GeoJSON outside bounds of --like raster. " kwargs = template_ds.meta.copy() kwargs['count'] = 1 # DEPRECATED # upgrade transform to affine object or we may get an invalid # transform set on output kwargs['transform'] = template_ds.affine template_ds.close() else: bounds = bounds or geojson_bounds if is_geographic_crs(src_crs): if (bounds[0] < -180 or bounds[2] > 180 or bounds[1] < -80 or bounds[3] > 80): raise Exception if dimensions: width, height = dimensions res = ( (bounds[2] - bounds[0]) / float(width), (bounds[3] - bounds[1]) / float(height) ) else: if not res: raise Exception elif len(res) == 1: res = (res[0], res[0]) width = max(int(ceil((bounds[2] - bounds[0]) / float(res[0]))), 1) height = max(int(ceil((bounds[3] - bounds[1]) / float(res[1]))), 1) src_crs = src_crs.upper() if not src_crs.count('EPSG:'): raise Exception kwargs = { 'count': 1, 'crs': src_crs, 'width': width, 'height': height, 'transform': Affine(res[0], 0, bounds[0], 0, -res[1], bounds[3]), 'driver': driver } kwargs.update(**creation_options) result = rasterize( geometries, out_shape=(kwargs['height'], kwargs['width']), transform=kwargs.get('affine', kwargs['transform']), all_touched=all_touched, dtype=kwargs.get('dtype', None), default_value=default_value, fill = fill) if 'dtype' not in kwargs: kwargs['dtype'] = result.dtype kwargs['nodata'] = fill with rasterio.open(output, 'w', **kwargs) as out: out.write_band(1, result)
def rasterize( ctx, files, output, driver, like, bounds, dimensions, res, src_crs, all_touched, default_value, fill, prop, overwrite, nodata, creation_options): """Rasterize GeoJSON into a new or existing raster. If the output raster exists, rio-rasterize will rasterize feature values into all bands of that raster. The GeoJSON is assumed to be in the same coordinate reference system as the output unless --src-crs is provided. --default_value or property values when using --property must be using a data type valid for the data type of that raster. If a template raster is provided using the --like option, the affine transform and data type from that raster will be used to create the output. Only a single band will be output. The GeoJSON is assumed to be in the same coordinate reference system unless --src-crs is provided. --default_value or property values when using --property must be using a data type valid for the data type of that raster. --driver, --bounds, --dimensions, --res, --nodata are ignored when output exists or --like raster is provided If the output does not exist and --like raster is not provided, the input GeoJSON will be used to determine the bounds of the output unless provided using --bounds. --dimensions or --res are required in this case. If --res is provided, the bottom and right coordinates of bounds are ignored. Note ---- The GeoJSON is not projected to match the coordinate reference system of the output or --like rasters at this time. This functionality may be added in the future. """ from rasterio.crs import CRS from rasterio.features import rasterize from rasterio.features import bounds as calculate_bounds output, files = resolve_inout( files=files, output=output, overwrite=overwrite) bad_param = click.BadParameter('invalid CRS. Must be an EPSG code.', ctx, param=src_crs, param_hint='--src_crs') has_src_crs = src_crs is not None try: src_crs = CRS.from_string(src_crs) if has_src_crs else CRS.from_string('EPSG:4326') except CRSError: raise bad_param # If values are actually meant to be integers, we need to cast them # as such or rasterize creates floating point outputs if default_value == int(default_value): default_value = int(default_value) if fill == int(fill): fill = int(fill) with ctx.obj['env']: def feature_value(feature): if prop and 'properties' in feature: return feature['properties'].get(prop, default_value) return default_value with click.open_file(files.pop(0) if files else '-') as gj_f: geojson = json.loads(gj_f.read()) if 'features' in geojson: geometries = [] for f in geojson['features']: geometries.append((f['geometry'], feature_value(f))) elif 'geometry' in geojson: geometries = ((geojson['geometry'], feature_value(geojson)), ) else: raise click.BadParameter('Invalid GeoJSON', param=input, param_hint='input') geojson_bounds = geojson.get('bbox', calculate_bounds(geojson)) if rasterio.shutil.exists(output): with rasterio.open(output, 'r+') as out: if has_src_crs and src_crs != out.crs: raise click.BadParameter('GeoJSON does not match crs of ' 'existing output raster', param='input', param_hint='input') if disjoint_bounds(geojson_bounds, out.bounds): click.echo("GeoJSON outside bounds of existing output " "raster. Are they in different coordinate " "reference systems?", err=True) meta = out.meta result = rasterize( geometries, out_shape=(meta['height'], meta['width']), transform=meta.get('affine', meta['transform']), all_touched=all_touched, dtype=meta.get('dtype', None), default_value=default_value, fill=fill) for bidx in range(1, meta['count'] + 1): data = out.read(bidx, masked=True) # Burn in any non-fill pixels, and update mask accordingly ne = result != fill data[ne] = result[ne] if data.mask.any(): data.mask[ne] = False out.write(data, indexes=bidx) else: if like is not None: template_ds = rasterio.open(like) if has_src_crs and src_crs != template_ds.crs: raise click.BadParameter('GeoJSON does not match crs of ' '--like raster', param='input', param_hint='input') if disjoint_bounds(geojson_bounds, template_ds.bounds): click.echo("GeoJSON outside bounds of --like raster. " "Are they in different coordinate reference " "systems?", err=True) kwargs = template_ds.profile kwargs['count'] = 1 kwargs['transform'] = template_ds.transform template_ds.close() else: bounds = bounds or geojson_bounds if src_crs.is_geographic: if (bounds[0] < -180 or bounds[2] > 180 or bounds[1] < -80 or bounds[3] > 80): raise click.BadParameter( "Bounds are beyond the valid extent for " "EPSG:4326.", ctx, param=bounds, param_hint='--bounds') if dimensions: width, height = dimensions res = ( (bounds[2] - bounds[0]) / float(width), (bounds[3] - bounds[1]) / float(height) ) else: if not res: raise click.BadParameter( 'pixel dimensions are required', ctx, param=res, param_hint='--res') elif len(res) == 1: res = (res[0], res[0]) width = max(int(ceil((bounds[2] - bounds[0]) / float(res[0]))), 1) height = max(int(ceil((bounds[3] - bounds[1]) / float(res[1]))), 1) kwargs = { 'count': 1, 'crs': src_crs, 'width': width, 'height': height, 'transform': Affine(res[0], 0, bounds[0], 0, -res[1], bounds[3]), 'driver': driver } kwargs.update(**creation_options) if nodata is not None: kwargs['nodata'] = nodata result = rasterize( geometries, out_shape=(kwargs['height'], kwargs['width']), transform=kwargs['transform'], all_touched=all_touched, dtype=kwargs.get('dtype', None), default_value=default_value, fill=fill) if 'dtype' not in kwargs: kwargs['dtype'] = result.dtype with rasterio.open(output, 'w', **kwargs) as out: out.write(result, indexes=1)
def mask( ctx, files, output, geojson_mask, driver, all_touched, crop, invert, force_overwrite, creation_options): """Masks in raster using GeoJSON features (masks out all areas not covered by features), and optionally crops the output raster to the extent of the features. Features are assumed to be in the same coordinate reference system as the input raster. GeoJSON must be the first input file or provided from stdin: > rio mask input.tif output.tif --geojson-mask features.json > rio mask input.tif output.tif --geojson-mask - < features.json If the output raster exists, it will be completely overwritten with the results of this operation. The result is always equal to or within the bounds of the input raster. --crop and --invert options are mutually exclusive. --crop option is not valid if features are completely outside extent of input raster. """ from rasterio.mask import mask as mask_tool from rasterio.features import bounds as calculate_bounds verbosity = (ctx.obj and ctx.obj.get('verbosity')) or 1 logger = logging.getLogger('rio') output, files = resolve_inout( files=files, output=output, force_overwrite=force_overwrite) input = files[0] if geojson_mask is None: click.echo('No GeoJSON provided, INPUT will be copied to OUTPUT', err=True) shutil.copy(input, output) return if crop and invert: click.echo('Invert option ignored when using --crop', err=True) invert = False with rasterio.drivers(CPL_DEBUG=verbosity > 2): try: with click.open_file(geojson_mask) as fh: geojson = json.loads(fh.read()) except ValueError: raise click.BadParameter('GeoJSON could not be read from ' '--geojson-mask or stdin', param_hint='--geojson-mask') if 'features' in geojson: geometries = [f['geometry'] for f in geojson['features']] elif 'geometry' in geojson: geometries = (geojson['geometry'], ) else: raise click.BadParameter('Invalid GeoJSON', param=input, param_hint='input') bounds = geojson.get('bbox', calculate_bounds(geojson)) with rasterio.open(input) as src: try: out_image, out_transform = mask_tool(src, geometries, crop=crop, invert=invert, all_touched=all_touched) except ValueError as e: if e.args[0] == 'Input shapes do not overlap raster.': if crop: raise click.BadParameter('not allowed for GeoJSON ' 'outside the extent of the ' 'input raster', param=crop, param_hint='--crop') meta = src.meta.copy() meta.update(**creation_options) meta.update({ 'driver': driver, 'height': out_image.shape[1], 'width': out_image.shape[2], 'transform': out_transform }) with rasterio.open(output, 'w', **meta) as out: out.write(out_image)
def mask(ctx, files, output, geojson_mask, driver, all_touched, crop, invert, force_overwrite, creation_options): """Masks in raster using GeoJSON features (masks out all areas not covered by features), and optionally crops the output raster to the extent of the features. Features are assumed to be in the same coordinate reference system as the input raster. GeoJSON must be the first input file or provided from stdin: > rio mask input.tif output.tif --geojson-mask features.json > rio mask input.tif output.tif --geojson-mask - < features.json If the output raster exists, it will be completely overwritten with the results of this operation. The result is always equal to or within the bounds of the input raster. --crop and --invert options are mutually exclusive. --crop option is not valid if features are completely outside extent of input raster. """ from rasterio.tools.mask import mask as mask_tool from rasterio.features import bounds as calculate_bounds verbosity = (ctx.obj and ctx.obj.get('verbosity')) or 1 logger = logging.getLogger('rio') output, files = resolve_inout(files=files, output=output, force_overwrite=force_overwrite) input = files[0] if geojson_mask is None: click.echo('No GeoJSON provided, INPUT will be copied to OUTPUT', err=True) shutil.copy(input, output) return if crop and invert: click.echo('Invert option ignored when using --crop', err=True) invert = False with rasterio.drivers(CPL_DEBUG=verbosity > 2): try: with click.open_file(geojson_mask) as f: geojson = json.loads(f.read()) except ValueError: raise click.BadParameter( 'GeoJSON could not be read from ' '--geojson-mask or stdin', param_hint='--geojson-mask') if 'features' in geojson: geometries = [f['geometry'] for f in geojson['features']] elif 'geometry' in geojson: geometries = (geojson['geometry'], ) else: raise click.BadParameter('Invalid GeoJSON', param=input, param_hint='input') bounds = geojson.get('bbox', calculate_bounds(geojson)) with rasterio.open(input) as src: try: out_image, out_transform = mask_tool(src, geometries, crop=crop, invert=invert, all_touched=all_touched) except ValueError as e: if e.args[0] == 'Input shapes do not overlap raster.': if crop: raise click.BadParameter( 'not allowed for GeoJSON ' 'outside the extent of the ' 'input raster', param=crop, param_hint='--crop') meta = src.meta.copy() meta.update(**creation_options) meta.update({ 'driver': driver, 'height': out_image.shape[1], 'width': out_image.shape[2], 'transform': out_transform }) with rasterio.open(output, 'w', **meta) as out: out.write(out_image)