def reproject( source, destination, src_transform=None, src_crs=None, dst_transform=None, dst_crs=None, resampling=RESAMPLING.nearest, **kwargs): """Reproject a source raster to a destination. If the source and destination are ndarrays, coordinate reference system definitions and affine transformation parameters are required for reprojection. If the source and destination are rasterio Bands, shorthand for bands of datasets on disk, the coordinate reference systems and transforms will be read from the appropriate datasets. """ if src_transform: src_transform = guard_transform(src_transform).to_gdal() if dst_transform: dst_transform = guard_transform(dst_transform).to_gdal() _reproject( source, destination, src_transform, src_crs, dst_transform, dst_crs, resampling, **kwargs)
def test_guard_transform_gdal_TypeError(path_rgb_byte_tif): """As part of the 1.0 migration, guard_transform() should raise a TypeError if a GDAL geotransform is encountered""" with rasterio.open(path_rgb_byte_tif) as src: aff = src.transform with pytest.raises(TypeError): transform.guard_transform(aff.to_gdal())
def reproject(source, destination, src_transform=None, src_crs=None, dst_transform=None, dst_crs=None, resampling=RESAMPLING.nearest, **kwargs): """ Reproject a source raster to a destination raster. Parameters ------------ source: ndarray or rasterio Band Source raster. destination: ndarray or rasterio Band Target raster. src_transform: affine transform object, optional Source affine transformation. Required if source and destination are ndarrays. Will be derived from source if it is a rasterio Band. src_crs: dict, optional Source coordinate reference system, in rasterio dict format. Required if source and destination are ndarrays. Will be derived from source if it is a rasterio Band. Example: {'init': 'EPSG:4326'} dst_transform: affine transform object, optional Target affine transformation. Required if source and destination are ndarrays. Will be derived from target if it is a rasterio Band. dst_crs: dict, optional Target coordinate reference system. Required if source and destination are ndarrays. Will be derived from target if it is a rasterio Band. resampling: int Resampling method to use. One of the following: RESAMPLING.nearest, RESAMPLING.bilinear, RESAMPLING.cubic, RESAMPLING.cubic_spline, RESAMPLING.lanczos, RESAMPLING.average, RESAMPLING.mode kwargs: dict, optional Additional arguments passed to transformation function. Returns --------- out: None Output is written to destination. """ if src_transform: src_transform = guard_transform(src_transform).to_gdal() if dst_transform: dst_transform = guard_transform(dst_transform).to_gdal() _reproject(source, destination, src_transform, src_crs, dst_transform, dst_crs, resampling, **kwargs)
def window(self, left, bottom, right, top, boundless=False): """Get the window corresponding to the bounding coordinates. Parameters ---------- left : float Left (west) bounding coordinate bottom : float Bottom (south) bounding coordinate right : float Right (east) bounding coordinate top : float Top (north) bounding coordinate boundless: boolean, optional If boundless is False, window is limited to extent of this dataset. Returns ------- window: tuple ((row_start, row_stop), (col_start, col_stop)) corresponding to the bounding coordinates """ transform = guard_transform(self.transform) return windows.from_bounds( left, bottom, right, top, transform=transform, height=self.height, width=self.width, boundless=boundless)
def window(self, left, bottom, right, top, boundless=False): """Get the window corresponding to the bounding coordinates. Parameters ---------- left : float Left (west) bounding coordinate bottom : float Bottom (south) bounding coordinate right : float Right (east) bounding coordinate top : float Top (north) bounding coordinate boundless: boolean, optional If boundless is False, window is limited to extent of this dataset. Returns ------- window: tuple ((row_start, row_stop), (col_start, col_stop)) corresponding to the bounding coordinates """ transform = guard_transform(self.transform) return windows.from_bounds(left, bottom, right, top, transform=transform, height=self.height, width=self.width, boundless=boundless)
def shapes(image, mask=None, connectivity=4, transform=IDENTITY): """Yields a (shape, image_value) pair for each feature in the image. The shapes are GeoJSON-like dicts and the image values are ints or floats depending on the data type of the image. Features are found using a connected-component labeling algorithm. The image must be one of int16, int32, uint8, uint16, float32 data types. Note: due to floating point precision issues, the floating point values returned from a floating point image may not exactly match the original values. If a mask is provided, pixels for which the mask is `False` will be excluded from feature generation. """ valid_dtypes = ('int16', 'int32', 'uint8', 'uint16', 'float32') if np.dtype(image.dtype).name not in valid_dtypes: raise ValueError('image dtype must be one of: %s' % (', '.join(valid_dtypes))) if mask is not None and np.dtype(mask.dtype) != np.dtype(rasterio.bool_): raise ValueError("Mask must be dtype rasterio.bool_") if connectivity not in (4, 8): raise ValueError("Connectivity Option must be 4 or 8") transform = guard_transform(transform) with rasterio.drivers(): for s, v in _shapes(image, mask, connectivity, transform.to_gdal()): yield s, v
def shapes(image, mask=None, connectivity=4, transform=IDENTITY): """Yields a (shape, image_value) pair for each feature in the image. The shapes are GeoJSON-like dicts and the image values are ints. Features are found using a connected-component labeling algorithm. The image must be of unsigned 8-bit integer (rasterio.byte or numpy.uint8) data type. If a mask is provided, pixels for which the mask is `False` will be excluded from feature generation. """ if np.dtype(image.dtype) != np.dtype(rasterio.ubyte): raise ValueError("Image must be dtype uint8/ubyte") if mask is not None and np.dtype(mask.dtype) != np.dtype(rasterio.bool_): raise ValueError("Mask must be dtype rasterio.bool_") if connectivity not in (4, 8): raise ValueError("Connectivity Option must be 4 or 8") transform = guard_transform(transform) with rasterio.drivers(): for s, v in _shapes(image, mask, connectivity, transform.to_gdal()): yield s, v
def run(self, processes=4): """TODO""" if processes == 1: self.pool = MockTub(init_worker, (self.inpaths, self.global_args)) else: self.pool = Pool(processes, init_worker, (self.inpaths, self.global_args)) self.options["transform"] = guard_transform(self.options["transform"]) if self.mode == "manual_read": reader_worker = manual_reader(self.run_function) elif self.mode == "array_read": reader_worker = array_reader(self.run_function) else: reader_worker = simple_reader(self.run_function) if isinstance(self.outpath_or_dataset, rasterio.io.DatasetWriter): destination = self.outpath_or_dataset else: destination = rasterio.open(self.outpath_or_dataset, "w", **self.options) # Open an output file, work through the function in parallel, # and write out the data. with destination as dst: for data, window in self.pool.imap_unordered(reader_worker, self.windows): dst.write(data, window=window) self.pool.close() self.pool.join()
def window(self, left, bottom, right, top, precision=None): """Get the window corresponding to the bounding coordinates. The resulting window is not cropped to the row and column limits of the dataset. Parameters ---------- left: float Left (west) bounding coordinate bottom: float Bottom (south) bounding coordinate right: float Right (east) bounding coordinate top: float Top (north) bounding coordinate precision: int, optional Number of decimal points of precision when computing inverse transform. Returns ------- window: Window """ transform = guard_transform(self.transform) return from_bounds( left, bottom, right, top, transform=transform, height=self.height, width=self.width, precision=precision)
def pad(array, transform, pad_width, mode=None, **kwargs): """pad array and adjust affine transform matrix. Parameters ---------- array: ndarray Numpy ndarray, for best results a 2D array transform: Affine transform transform object mapping pixel space to coordinates pad_width: int number of pixels to pad array on all four mode: str or function define the method for determining padded values Returns ------- (array, transform): tuple Tuple of new array and affine transform Notes ----- See numpy docs for details on mode and other kwargs: http://docs.scipy.org/doc/numpy-1.10.0/reference/generated/numpy.pad.html """ import numpy as np transform = guard_transform(transform) padded_array = np.pad(array, pad_width, mode, **kwargs) padded_trans = list(transform) padded_trans[2] -= pad_width * padded_trans[0] padded_trans[5] -= pad_width * padded_trans[4] return padded_array, Affine(*padded_trans[:6])
def plotting_extent(source, transform=None): """Returns an extent in the format needed for matplotlib's imshow (left, right, bottom, top) instead of rasterio's bounds (left, bottom, top, right) Parameters ---------- source : array or dataset object opened in 'r' mode input data transform: Affine, required if source is array Defines the affine transform if source is an array Returns ------- tuple of float left, right, bottom, top """ if hasattr(source, 'bounds'): extent = (source.bounds.left, source.bounds.right, source.bounds.bottom, source.bounds.top) elif not transform: raise ValueError( "transform is required if source is an array") else: transform = guard_transform(transform) rows, cols = source.shape[0:2] left, top = transform * (0, 0) right, bottom = transform * (cols, rows) extent = (left, right, bottom, top) return extent
def window(self, left, bottom, right, top, precision=6, **kwargs): """Get the window corresponding to the bounding coordinates. The resulting window is not cropped to the row and column limits of the dataset. Parameters ---------- left: float Left (west) bounding coordinate bottom: float Bottom (south) bounding coordinate right: float Right (east) bounding coordinate top: float Top (north) bounding coordinate precision: int, optional Number of decimal points of precision when computing inverse transform. kwargs: mapping For backwards compatibility: absorbs deprecated keyword args. Returns ------- window: Window """ if 'boundless' in kwargs: # pragma: no branch warnings.warn("boundless keyword arg should not be used", RasterioDeprecationWarning) transform = guard_transform(self.transform) return from_bounds( left, bottom, right, top, transform=transform, height=self.height, width=self.width, precision=precision)
def window(self, left, bottom, right, top, boundless=True, precision=6): """Get the window corresponding to the bounding coordinates. Parameters ---------- left : float Left (west) bounding coordinate bottom : float Bottom (south) bounding coordinate right : float Right (east) bounding coordinate top : float Top (north) bounding coordinate boundless: boolean, optional If boundless is False, window is limited to extent of this dataset. precision : int, optional Number of decimal points of precision when computing inverse transform. Returns ------- window: Window """ transform = guard_transform(self.transform) return windows.from_bounds(left, bottom, right, top, transform=transform, height=self.height, width=self.width, boundless=boundless, precision=precision)
def run(self, processes=4): if processes == 1: self.pool = MockTub( main_worker, (self.inpaths, self.run_function, self.global_args)) else: self.pool = Pool( processes, main_worker, (self.inpaths, self.run_function, self.global_args)) self.options['transform'] = guard_transform(self.options['transform']) if self.mode == 'manual_read': reader_worker = manualRead elif self.mode == 'array_read': reader_worker = arrayRead else: reader_worker = simpleRead ## Open an output file, work through the function in parallel, and write out the data with rio.open(self.outpath, 'w', **self.options) as dst: for data, window in self.pool.imap_unordered( reader_worker, self.windows): dst.write(data, window=window) self.pool.close() self.pool.join() return
def run(self, processes=4): """TODO""" if processes == 1: self.pool = MockTub(init_worker, (self.inpaths, self.global_args)) else: self.pool = Pool(processes, init_worker, (self.inpaths, self.global_args)) self.options["transform"] = guard_transform(self.options["transform"]) if self.mode == "manual_read": reader_worker = manual_reader(self.run_function) elif self.mode == "array_read": reader_worker = array_reader(self.run_function) else: reader_worker = simple_reader(self.run_function) if isinstance(self.outpath_or_dataset, rasterio.io.DatasetWriter): destination = self.outpath_or_dataset else: destination = rasterio.open(self.outpath_or_dataset, "w", **self.options) # Open an output file, work through the function in parallel, # and write out the data. with destination as dst: for data, window in self.pool.imap_unordered( reader_worker, self.windows): dst.write(data, window=window) self.pool.close() self.pool.join()
def plotting_extent(source, transform=None): """Returns an extent in the format needed for matplotlib's imshow (left, right, bottom, top) instead of rasterio's bounds (left, bottom, right, top) Parameters ---------- source : array or dataset object opened in 'r' mode If array, data in the order rows, columns and optionally bands. If array is band order (bands in the first dimension), use arr[0] transform: Affine, required if source is array Defines the affine transform if source is an array Returns ------- tuple of float left, right, bottom, top """ if hasattr(source, 'bounds'): extent = (source.bounds.left, source.bounds.right, source.bounds.bottom, source.bounds.top) elif not transform: raise ValueError("transform is required if source is an array") else: transform = guard_transform(transform) rows, cols = source.shape[0:2] left, top = transform * (0, 0) right, bottom = transform * (cols, rows) extent = (left, right, bottom, top) return extent
def atmos( ctx, atmo, contrast, bias, jobs, out_dtype, src_path, dst_path, creation_options, as_color, ): """Atmospheric correction """ if as_color: click.echo("rio color {} {} {}".format( src_path, dst_path, simple_atmo_opstring(atmo, contrast, bias))) exit(0) with rasterio.open(src_path) as src: opts = src.profile.copy() windows = [(window, ij) for ij, window in src.block_windows()] opts.update(**creation_options) opts["transform"] = guard_transform(opts["transform"]) out_dtype = out_dtype if out_dtype else opts["dtype"] opts["dtype"] = out_dtype args = { "atmo": atmo, "contrast": contrast, "bias": bias, "out_dtype": out_dtype } jobs = check_jobs(jobs) if jobs > 1: with riomucho.RioMucho( [src_path], dst_path, atmos_worker, windows=windows, options=opts, global_args=args, mode="manual_read", ) as mucho: mucho.run(jobs) else: with rasterio.open(dst_path, "w", **opts) as dest: with rasterio.open(src_path) as src: rasters = [src] for window, ij in windows: arr = atmos_worker(rasters, window, ij, args) dest.write(arr, window=window)
def shapes(image, mask=None, connectivity=4, transform=IDENTITY): """ Return a generator of (polygon, value) for each each set of adjacent pixels of the same value. Parameters ---------- image : numpy ndarray or rasterio Band object (RasterReader, bidx namedtuple). Data type must be one of rasterio.int16, rasterio.int32, rasterio.uint8, rasterio.uint16, or rasterio.float32. mask : numpy ndarray or rasterio Band object, optional Values of False or 0 will be excluded from feature generation Must evaluate to bool (rasterio.bool_ or rasterio.uint8) connectivity : int, optional Use 4 or 8 pixel connectivity for grouping pixels into features transform : Affine transformation, optional If not provided, feature coordinates will be generated based on pixel coordinates Returns ------- Generator of (polygon, value) Yields a pair of (polygon, value) for each feature found in the image. Polygons are GeoJSON-like dicts and the values are the associated value from the image, in the data type of the image. Note: due to floating point precision issues, values returned from a floating point image may not exactly match the original values. Notes ----- The amount of memory used by this algorithm is proportional to the number and complexity of polygons produced. This algorithm is most appropriate for simple thematic data. Data with high pixel-to-pixel variability, such as imagery, may produce one polygon per pixel and consume large amounts of memory. """ valid_dtypes = ('int16', 'int32', 'uint8', 'uint16', 'float32') if np.dtype(image.dtype).name not in valid_dtypes: raise ValueError('image dtype must be one of: %s' % (', '.join(valid_dtypes))) if mask is not None and np.dtype(mask.dtype).name not in ('bool', 'uint8'): raise ValueError("Mask must be dtype rasterio.bool_ or rasterio.uint8") if connectivity not in (4, 8): raise ValueError("Connectivity Option must be 4 or 8") transform = guard_transform(transform) with rasterio.drivers(): for s, v in _shapes(image, mask, connectivity, transform.to_gdal()): yield s, v
def pad(array, transform, pad_width, mode=None, **kwargs): """Returns a padded array and shifted affine transform matrix. Array is padded using `numpy.pad()`.""" transform = guard_transform(transform) padded_array = numpy.pad(array, pad_width, mode, **kwargs) padded_trans = list(transform) padded_trans[2] -= pad_width*padded_trans[0] padded_trans[5] -= pad_width*padded_trans[4] return padded_array, Affine(*padded_trans[:6])
def pad(array, transform, pad_width, mode=None, **kwargs): """Returns a padded array and shifted affine transform matrix. Array is padded using `numpy.pad()`.""" import numpy transform = guard_transform(transform) padded_array = numpy.pad(array, pad_width, mode, **kwargs) padded_trans = list(transform) padded_trans[2] -= pad_width*padded_trans[0] padded_trans[5] -= pad_width*padded_trans[4] return padded_array, Affine(*padded_trans[:6])
def shapes(source, mask=None, connectivity=4, transform=IDENTITY): """Get shapes and values of connected regions in a dataset or array. Parameters ---------- source : array, dataset object, Band, or tuple(dataset, bidx) Data type must be one of rasterio.int16, rasterio.int32, rasterio.uint8, rasterio.uint16, or rasterio.float32. mask : numpy ndarray or rasterio Band object, optional Must evaluate to bool (rasterio.bool_ or rasterio.uint8). Values of False or 0 will be excluded from feature generation. Note well that this is the inverse sense from Numpy's, where a mask value of True indicates invalid data in an array. If `source` is a Numpy masked array and `mask` is None, the source's mask will be inverted and used in place of `mask`. connectivity : int, optional Use 4 or 8 pixel connectivity for grouping pixels into features transform : Affine transformation, optional If not provided, feature coordinates will be generated based on pixel coordinates Yields ------- polygon, value A pair of (polygon, value) for each feature found in the image. Polygons are GeoJSON-like dicts and the values are the associated value from the image, in the data type of the image. Note: due to floating point precision issues, values returned from a floating point image may not exactly match the original values. Notes ----- The amount of memory used by this algorithm is proportional to the number and complexity of polygons produced. This algorithm is most appropriate for simple thematic data. Data with high pixel-to-pixel variability, such as imagery, may produce one polygon per pixel and consume large amounts of memory. Because the low-level implementation uses either an int32 or float32 buffer, uint32 and float64 data cannot be operated on without truncation issues. """ if hasattr(source, 'mask') and mask is None: mask = ~source.mask source = source.data transform = guard_transform(transform) for s, v in _shapes(source, mask, connectivity, transform): yield s, v
def __init__(self, raster, affine=None, nodata=None, band=None, driver=None, crs=None, variable=None): self.array = None self.src = None self.driver = None self.crs = None self.variable = variable if isinstance(raster, np.ndarray): if affine is None: # supply default affine transform: # (x res, row rotation, xmin, column rotation, y res, ymax) aff = default_transform(raster) warnings.warn('No affine transform supplied - using default') self.array = raster self.affine = affine self.shape = raster.shape if nodata is None: nodata = -9999 warnings.warn('No no data value supplied - using -9999') self.nodata = nodata if len(raster.shape) > 2: self.nbands = raster.shape[-1] # assumes 3rd dim is bands else: self.nbands = 1 self.band = band self.dtype = raster.dtype else: if self.variable: self.src = rasterio.open( 'netcdf:{0}:{1}'.format(raster, variable), 'r') else: self.src = rasterio.open(raster, 'r') self.affine = guard_transform(self.src.transform) self.shape = (self.src.height, self.src.width) if nodata is not None: # override with specified nodata self.nodata = float(nodata) else: self.nodata = self.src.nodata self.nbands = self.src.count self.band = band
def transform_handler(ctx, param, value): """Get transform value from a template file or command line.""" retval = options.from_like_context(ctx, param, value) if retval is None and value: try: value = json.loads(value) except ValueError: pass try: retval = guard_transform(value) except: raise click.BadParameter( "'%s' is not recognized as an Affine array." % value, param=param, param_hint='transform') return retval
def transform_handler(ctx, param, value): """Get transform value from a template file or command line.""" retval = options.from_like_context(ctx, param, value) if retval is None and value: try: value = json.loads(value) except ValueError: pass try: retval = guard_transform(value) except Exception: raise click.BadParameter( "'%s' is not recognized as an Affine array." % value, param=param, param_hint='transform') return retval
def write_cloud_mask(arr, profile, cloudmask, threshold=2): """ writes the cloud+alpha mask as single-band uint8 tiff suitable for stacking as an alpha band threshold defaults to 2; only 2 and above are considered clouds """ func = qa_vars['clouds'] data = func(arr) profile.update(dtype='uint8') profile.update(transform=guard_transform(profile['transform'])) with rasterio.open(cloudmask, 'w', **profile) as dest: clouds = (data >= threshold) nodata = (data == 0) yesdata = ((clouds + nodata) == 0) data = (yesdata * 255).astype('uint8') dest.write(data, 1)
def window_bounds(self, window): """Get the bounds of a window Parameters ---------- window: rasterio.windows.Window Dataset window Returns ------- bounds : tuple x_min, y_min, x_max, y_max for the given window """ transform = guard_transform(self.transform) return bounds(window, transform)
def window_transform(self, window): """Get the affine transform for a dataset window. Parameters ---------- window: tuple Dataset window tuple Returns ------- transform: Affine The affine transform matrix for the given window """ transform = guard_transform(self.transform) return windows.transform(window, transform)
def window_transform(self, window): """Get the affine transform for a dataset window. Parameters ---------- window: rasterio.windows.Window Dataset window Returns ------- transform: Affine The affine transform matrix for the given window """ gtransform = guard_transform(self.transform) return transform(window, gtransform)
def shapes(source, mask=None, connectivity=4, transform=IDENTITY): """Yield (polygon, value for each set of adjacent pixels of the same value. Parameters ---------- source : array or dataset object opened in 'r' mode or Band or tuple(dataset, bidx) Data type must be one of rasterio.int16, rasterio.int32, rasterio.uint8, rasterio.uint16, or rasterio.float32. mask : numpy ndarray or rasterio Band object, optional Must evaluate to bool (rasterio.bool_ or rasterio.uint8). Values of False or 0 will be excluded from feature generation. Note well that this is the inverse sense from Numpy's, where a mask value of True indicates invalid data in an array. If `source` is a Numpy masked array and `mask` is None, the source's mask will be inverted and used in place of `mask`. connectivity : int, optional Use 4 or 8 pixel connectivity for grouping pixels into features transform : Affine transformation, optional If not provided, feature coordinates will be generated based on pixel coordinates Yields ------- tuple A pair of (polygon, value) for each feature found in the image. Polygons are GeoJSON-like dicts and the values are the associated value from the image, in the data type of the image. Note: due to floating point precision issues, values returned from a floating point image may not exactly match the original values. Notes ----- The amount of memory used by this algorithm is proportional to the number and complexity of polygons produced. This algorithm is most appropriate for simple thematic data. Data with high pixel-to-pixel variability, such as imagery, may produce one polygon per pixel and consume large amounts of memory. """ if hasattr(source, 'mask') and mask is None: mask = ~source.mask source = source.data transform = guard_transform(transform) for s, v in _shapes(source, mask, connectivity, transform): yield s, v
def atmos(ctx, atmo, contrast, bias, jobs, out_dtype, src_path, dst_path, creation_options, as_color): """Atmospheric correction """ if as_color: click.echo("rio color {} {} {}".format( src_path, dst_path, simple_atmo_opstring(atmo, contrast, bias))) exit(0) with rasterio.open(src_path) as src: opts = src.profile.copy() windows = [(window, ij) for ij, window in src.block_windows()] opts.update(**creation_options) opts['transform'] = guard_transform(opts['transform']) out_dtype = out_dtype if out_dtype else opts['dtype'] opts['dtype'] = out_dtype args = { 'atmo': atmo, 'contrast': contrast, 'bias': bias, 'out_dtype': out_dtype } jobs = check_jobs(jobs) if jobs > 1: with riomucho.RioMucho( [src_path], dst_path, atmos_worker, windows=windows, options=opts, global_args=args, mode="manual_read" ) as mucho: mucho.run(jobs) else: with rasterio.open(dst_path, 'w', **opts) as dest: with rasterio.open(src_path) as src: rasters = [src] for window, ij in windows: arr = atmos_worker(rasters, window, ij, args) dest.write(arr, window=window)
def color(jobs, out_dtype, src_path, dst_path, operations, creation_options): with rasterio.open(src_path) as src: opts = src.profile.copy() windows = [(window, ij) for ij, window in src.block_windows()] opts.update(**creation_options) opts["transform"] = guard_transform(opts["transform"]) out_dtype = out_dtype if out_dtype else opts["dtype"] opts["dtype"] = out_dtype args = {"ops_string": " ".join(operations), "out_dtype": out_dtype} # Just run this for validation this time # parsing will be run again within the worker # where its returned value will be used try: parse_operations(args["ops_string"]) except ValueError as e: import sys sys.exit(1) print(e) jobs = check_jobs(jobs) if jobs > 1: with riomucho.RioMucho( [src_path], dst_path, color_worker, windows=windows, options=opts, global_args=args, mode="manual_read", ) as mucho: mucho.run(jobs) else: with rasterio.open(dst_path, "w", **opts) as dest: with rasterio.open(src_path) as src: rasters = [src] for window, ij in windows: arr = color_worker(rasters, window, ij, args) dest.write(arr, window=window) dest.colorinterp = src.colorinterp
def window(self, left, bottom, right, top, precision=None): """Get the window corresponding to the bounding coordinates. The resulting window is not cropped to the row and column limits of the dataset. Parameters ---------- left: float Left (west) bounding coordinate bottom: float Bottom (south) bounding coordinate right: float Right (east) bounding coordinate top: float Top (north) bounding coordinate precision: int, optional This parameters is unused, deprecated in rasterio 1.3.0, and will be removed in version 2.0.0. Returns ------- window: Window """ if precision is not None: warnings.warn( "The precision parameter is unused, deprecated, and will be removed in 2.0.0.", RasterioDeprecationWarning, ) transform = guard_transform(self.transform) return from_bounds( left, bottom, right, top, transform=transform, height=self.height, width=self.width, )
def _threshold_raster(file, out_name, threshold=0.9, dst_crs=None): """ writes thresholded ground-truth as single-band uint16 tiff """ file = rio.open(file) profile = file.profile # need masked array data = file.read(1) mask = (data == file.nodata) data = ma.array(data, mask=mask, fill_value=file.nodata) profile.update(dtype='int16') profile.update(transform=guard_transform(profile['transform'])) with rio.open(out_name, 'w', **profile) as dest: threshed = (data >= threshold) # masked array op data = (threshed).filled().astype('int16') # fill mask with nodata. dest.write(data, 1) if dst_crs is not None: reproject_raster(out_name, dst_crs, out_name)
def shapes(image, mask=None, connectivity=4, transform=IDENTITY): """Yield (polygon, value for each set of adjacent pixels of the same value. Parameters ---------- image : numpy ndarray or rasterio Band object (RasterReader, bidx namedtuple). Data type must be one of rasterio.int16, rasterio.int32, rasterio.uint8, rasterio.uint16, or rasterio.float32. mask : numpy ndarray or rasterio Band object, optional Values of False or 0 will be excluded from feature generation Must evaluate to bool (rasterio.bool_ or rasterio.uint8) connectivity : int, optional Use 4 or 8 pixel connectivity for grouping pixels into features transform : Affine transformation, optional If not provided, feature coordinates will be generated based on pixel coordinates Yields ------- tuple A pair of (polygon, value) for each feature found in the image. Polygons are GeoJSON-like dicts and the values are the associated value from the image, in the data type of the image. Note: due to floating point precision issues, values returned from a floating point image may not exactly match the original values. Notes ----- The amount of memory used by this algorithm is proportional to the number and complexity of polygons produced. This algorithm is most appropriate for simple thematic data. Data with high pixel-to-pixel variability, such as imagery, may produce one polygon per pixel and consume large amounts of memory. """ transform = guard_transform(transform) rasterio.env.setenv() for s, v in _shapes(image, mask, connectivity, transform.to_gdal()): yield s, v
def __init__(self, raster, affine=None, nodata=None, band=1): self.array = None self.src = None if isinstance(raster, np.ndarray): if affine is None: raise ValueError("Specify affine transform for numpy arrays") self.array = raster self.affine = affine self.shape = raster.shape self.nodata = nodata else: self.src = rasterio.open(raster, 'r') self.affine = guard_transform(self.src.transform) self.shape = (self.src.height, self.src.width) self.band = band if nodata is not None: # override with specified nodata self.nodata = float(nodata) else: self.nodata = self.src.nodata
def shapes(image, mask=None, connectivity=4, transform=IDENTITY): """Yield (polygon, value for each set of adjacent pixels of the same value. Parameters ---------- image : numpy ndarray or rasterio Band object (RasterReader, bidx namedtuple). Data type must be one of rasterio.int16, rasterio.int32, rasterio.uint8, rasterio.uint16, or rasterio.float32. mask : numpy ndarray or rasterio Band object, optional Values of False or 0 will be excluded from feature generation Must evaluate to bool (rasterio.bool_ or rasterio.uint8) connectivity : int, optional Use 4 or 8 pixel connectivity for grouping pixels into features transform : Affine transformation, optional If not provided, feature coordinates will be generated based on pixel coordinates Yields ------- tuple A pair of (polygon, value) for each feature found in the image. Polygons are GeoJSON-like dicts and the values are the associated value from the image, in the data type of the image. Note: due to floating point precision issues, values returned from a floating point image may not exactly match the original values. Notes ----- The amount of memory used by this algorithm is proportional to the number and complexity of polygons produced. This algorithm is most appropriate for simple thematic data. Data with high pixel-to-pixel variability, such as imagery, may produce one polygon per pixel and consume large amounts of memory. """ transform = guard_transform(transform) for s, v in _shapes(image, mask, connectivity, transform.to_gdal()): yield s, v
def plotting_extent(source, transform=None): """Returns an extent in the format needed for matplotlib's imshow (left, right, bottom, top) instead of rasterio's bounds (left, bottom, top, right) Parameters ---------- source : raster dataset or array in image order (see reshape_as_image) transform: Affine, required if source is array """ if hasattr(source, 'bounds'): extent = (source.bounds.left, source.bounds.right, source.bounds.bottom, source.bounds.top) elif not transform: raise ValueError("transform is required if source is an array") else: transform = guard_transform(transform) rows, cols = source.shape[0:2] left, top = transform * (0, 0) right, bottom = transform * (cols, rows) extent = (left, right, bottom, top) return extent
def plotting_extent(source, transform=None): """Returns an extent in the format needed for matplotlib's imshow (left, right, bottom, top) instead of rasterio's bounds (left, bottom, top, right) Parameters ---------- source : raster dataset or array in image order (see reshape_as_image) transform: Affine, required if source is array """ if hasattr(source, 'bounds'): extent = (source.bounds.left, source.bounds.right, source.bounds.bottom, source.bounds.top) elif not transform: raise ValueError( "transform is required if source is an array") else: transform = guard_transform(transform) rows, cols = source.shape[0:2] left, top = transform * (0, 0) right, bottom = transform * (cols, rows) extent = (left, right, bottom, top) return extent
def rasterize(shapes, out_shape=None, fill=0, output=None, transform=IDENTITY, all_touched=False, default_value=255): """Returns an image array with points, lines, or polygons burned in. A different value may be specified for each shape. The shapes may be georeferenced or may have image coordinates. An existing image array may be provided, or one may be created. By default, the center of image elements determines whether they are updated, but all touched elements may be optionally updated. :param shapes: an iterator over Fiona style geometry objects (with a default value of 255) or an iterator over (geometry, value) pairs. Values must be unsigned integer type (uint8). :param transform: GDAL style geotransform to be applied to the image. :param out_shape: shape of created image array :param fill: fill value for created image array :param output: alternatively, an existing image array :param all_touched: if True, will rasterize all pixels touched, otherwise will use GDAL default method. :param default_value: value burned in for shapes if not provided as part of shapes. Must be unsigned integer type (uint8). """ if not isinstance(default_value, int) or (default_value > 255 or default_value < 0): raise ValueError("default_value %s is not uint8/ubyte" % default_value) def shape_source(): """A generator that screens out non-geometric objects and does its best to make sure that no NULLs get through to GDALRasterizeGeometries.""" for index, item in enumerate(shapes): try: if isinstance(item, (tuple, list)): geom, value = item # TODO: relax this for other data types. if not isinstance(value, int) or value > 255 or value < 0: raise ValueError("Shape number %i, value '%s' is not uint8/ubyte" % (index, value)) else: geom = item value = default_value geom = getattr(geom, "__geo_interface__", None) or geom if not isinstance(geom, dict) or "type" not in geom or "coordinates" not in geom: raise ValueError("Object %r at index %d is not a geometric object" % (geom, index)) yield geom, value except Exception: log.exception("Exception caught, skipping shape %d", index) if out_shape is not None: out = np.empty(out_shape, dtype=rasterio.ubyte) out.fill(fill) elif output is not None: if np.dtype(output.dtype) != np.dtype(rasterio.ubyte): raise ValueError("Output image must be dtype uint8/ubyte") out = output else: raise ValueError("An output image must be provided or specified") transform = guard_transform(transform) with rasterio.drivers(): _rasterize(shape_source(), out, transform.to_gdal(), all_touched) return out
def open(fp, mode='r', driver=None, width=None, height=None, count=None, crs=None, transform=None, dtype=None, nodata=None, sharing=True, **kwargs): """Open a dataset for reading or writing. The dataset may be located in a local file, in a resource located by a URL, or contained within a stream of bytes. In read ('r') or read/write ('r+') mode, no keyword arguments are required: these attributes are supplied by the opened dataset. In write ('w' or 'w+') mode, the driver, width, height, count, and dtype keywords are strictly required. Parameters ---------- fp : str, file object or pathlib.Path object A filename or URL, a file object opened in binary ('rb') mode, or a Path object. mode : str, optional 'r' (read, the default), 'r+' (read/write), 'w' (write), or 'w+' (write/read). driver : str, optional A short format driver name (e.g. "GTiff" or "JPEG") or a list of such names (see GDAL docs at http://www.gdal.org/formats_list.html). In 'w' or 'w+' modes a single name is required. In 'r' or 'r+' modes the driver can usually be omitted. Registered drivers will be tried sequentially until a match is found. When multiple drivers are available for a format such as JPEG2000, one of them can be selected by using this keyword argument. width, height : int, optional The numbers of rows and columns of the raster dataset. Required in 'w' or 'w+' modes, they are ignored in 'r' or 'r+' modes. count : int, optional The count of dataset bands. Required in 'w' or 'w+' modes, it is ignored in 'r' or 'r+' modes. dtype : str or numpy dtype The data type for bands. For example: 'uint8' or ``rasterio.uint16``. Required in 'w' or 'w+' modes, it is ignored in 'r' or 'r+' modes. crs : str, dict, or CRS; optional The coordinate reference system. Required in 'w' or 'w+' modes, it is ignored in 'r' or 'r+' modes. transform : Affine instance, optional Affine transformation mapping the pixel space to geographic space. Required in 'w' or 'w+' modes, it is ignored in 'r' or 'r+' modes. nodata : int, float, or nan; optional Defines the pixel value to be interpreted as not valid data. Required in 'w' or 'w+' modes, it is ignored in 'r' or 'r+' modes. sharing : bool A flag that allows sharing of dataset handles. Default is `True`. Should be set to `False` in a multithreaded:w program. kwargs : optional These are passed to format drivers as directives for creating or interpreting datasets. For example: in 'w' or 'w+' modes a `tiled=True` keyword argument will direct the GeoTIFF format driver to create a tiled, rather than striped, TIFF. Returns ------- A ``DatasetReader`` or ``DatasetUpdater`` object. Examples -------- To open a GeoTIFF for reading using standard driver discovery and no directives: >>> import rasterio >>> with rasterio.open('example.tif') as dataset: ... print(dataset.profile) To open a JPEG2000 using only the JP2OpenJPEG driver: >>> with rasterio.open( ... 'example.jp2', driver='JP2OpenJPEG') as dataset: ... print(dataset.profile) To create a new 8-band, 16-bit unsigned, tiled, and LZW-compressed GeoTIFF with a global extent and 0.5 degree resolution: >>> from rasterio.transform import from_origin >>> with rasterio.open( ... 'example.tif', 'w', driver='GTiff', dtype='uint16', ... width=720, height=360, count=8, crs='EPSG:4326', ... transform=from_origin(-180.0, 90.0, 0.5, 0.5), ... nodata=0, tiled=True, compress='lzw') as dataset: ... dataset.write(...) """ if not isinstance(fp, string_types): if not (hasattr(fp, 'read') or hasattr(fp, 'write') or isinstance(fp, Path)): raise TypeError("invalid path or file: {0!r}".format(fp)) if mode and not isinstance(mode, string_types): raise TypeError("invalid mode: {0!r}".format(mode)) if driver and not isinstance(driver, string_types): raise TypeError("invalid driver: {0!r}".format(driver)) if dtype and not check_dtype(dtype): raise TypeError("invalid dtype: {0!r}".format(dtype)) if nodata is not None: nodata = float(nodata) if transform: transform = guard_transform(transform) # Check driver/mode blacklist. if driver and is_blacklisted(driver, mode): raise RasterioIOError( "Blacklisted: file cannot be opened by " "driver '{0}' in '{1}' mode".format(driver, mode)) # Special case for file object argument. if mode == 'r' and hasattr(fp, 'read'): @contextmanager def fp_reader(fp): memfile = MemoryFile(fp.read()) dataset = memfile.open() try: yield dataset finally: dataset.close() memfile.close() return fp_reader(fp) elif mode in ('w', 'w+') and hasattr(fp, 'write'): @contextmanager def fp_writer(fp): memfile = MemoryFile() dataset = memfile.open(driver=driver, width=width, height=height, count=count, crs=crs, transform=transform, dtype=dtype, nodata=nodata, **kwargs) try: yield dataset finally: dataset.close() memfile.seek(0) fp.write(memfile.read()) memfile.close() return fp_writer(fp) else: # If a pathlib.Path instance is given, convert it to a string path. if isinstance(fp, Path): fp = str(fp) # The 'normal' filename or URL path. path = parse_path(fp) # Create dataset instances and pass the given env, which will # be taken over by the dataset's context manager if it is not # None. if mode == 'r': s = DatasetReader(path, driver=driver, **kwargs) elif mode == 'r+': s = get_writer_for_path(path)(path, mode, driver=driver, **kwargs) elif mode.startswith("w"): s = get_writer_for_driver(driver)(path, mode, driver=driver, width=width, height=height, count=count, crs=crs, transform=transform, dtype=dtype, nodata=nodata, **kwargs) else: raise ValueError( "mode must be one of 'r', 'r+', or 'w', not %s" % mode) return s
def rasterize( shapes, out_shape=None, fill=0, out=None, output=None, transform=IDENTITY, all_touched=False, default_value=1, dtype=None): """ Returns an image array with input geometries burned in. Parameters ---------- shapes : iterable of (geometry, value) pairs or iterable over geometries. `geometry` can either be an object that implements the geo interface or GeoJSON-like object. out_shape : tuple or list Shape of output numpy ndarray. fill : int or float, optional Used as fill value for all areas not covered by input geometries. out : numpy ndarray, optional Array of same shape and data type as `image` in which to store results. output : older alias for `out`, will be removed before 1.0. transform : Affine transformation object, optional Transformation from pixel coordinates of `image` to the coordinate system of the input `shapes`. See the `transform` property of dataset objects. all_touched : boolean, optional If True, all pixels touched by geometries will be burned in. If false, only pixels whose center is within the polygon or that are selected by Bresenham's line algorithm will be burned in. default_value : int or float, optional Used as value for all geometries, if not provided in `shapes`. dtype : rasterio or numpy data type, optional Used as data type for results, if `out` is not provided. Returns ------- out : numpy ndarray Results Notes ----- Valid data types for `fill`, `default_value`, `out`, `dtype` and shape values are rasterio.int16, rasterio.int32, rasterio.uint8, rasterio.uint16, rasterio.uint32, rasterio.float32, rasterio.float64. """ valid_dtypes = ('int16', 'int32', 'uint8', 'uint16', 'uint32', 'float32', 'float64') def get_valid_dtype(values): values_dtype = values.dtype if values_dtype.kind == 'i': values_dtype = np.dtype(get_minimum_int_dtype(values)) if values_dtype.name in valid_dtypes: return values_dtype return None def can_cast_dtype(values, dtype): if values.dtype.name == np.dtype(dtype).name: return True elif values.dtype.kind == 'f': return np.allclose(values, values.astype(dtype)) else: return np.array_equal(values, values.astype(dtype)) if fill != 0: fill_array = np.array([fill]) if get_valid_dtype(fill_array) is None: raise ValueError('fill must be one of these types: %s' % (', '.join(valid_dtypes))) elif dtype is not None and not can_cast_dtype(fill_array, dtype): raise ValueError('fill value cannot be cast to specified dtype') if default_value != 1: default_value_array = np.array([default_value]) if get_valid_dtype(default_value_array) is None: raise ValueError('default_value must be one of these types: %s' % (', '.join(valid_dtypes))) elif dtype is not None and not can_cast_dtype(default_value_array, dtype): raise ValueError('default_value cannot be cast to specified dtype') valid_shapes = [] shape_values = [] for index, item in enumerate(shapes): try: if isinstance(item, (tuple, list)): geom, value = item else: geom = item value = default_value geom = getattr(geom, '__geo_interface__', None) or geom if (not isinstance(geom, dict) or 'type' not in geom or 'coordinates' not in geom): raise ValueError( 'Object %r at index %d is not a geometry object' % (geom, index)) valid_shapes.append((geom, value)) shape_values.append(value) except Exception: log.exception('Exception caught, skipping shape %d', index) if not valid_shapes: raise ValueError('No valid shapes found for rasterize. Shapes must be ' 'valid geometry objects') shape_values = np.array(shape_values) values_dtype = get_valid_dtype(shape_values) if values_dtype is None: raise ValueError('shape values must be one of these dtypes: %s' % (', '.join(valid_dtypes))) if dtype is None: dtype = values_dtype elif np.dtype(dtype).name not in valid_dtypes: raise ValueError('dtype must be one of: %s' % (', '.join(valid_dtypes))) elif not can_cast_dtype(shape_values, dtype): raise ValueError('shape values could not be cast to specified dtype') if output is not None: warnings.warn( "The 'output' keyword arg has been superceded by 'out' " "and will be removed before Rasterio 1.0.", FutureWarning, stacklevel=2) out = out if out is not None else output if out is not None: if np.dtype(out.dtype).name not in valid_dtypes: raise ValueError('Output image dtype must be one of: %s' % (', '.join(valid_dtypes))) if not can_cast_dtype(shape_values, out.dtype): raise ValueError('shape values cannot be cast to dtype of output ' 'image') elif out_shape is not None: out = np.empty(out_shape, dtype=dtype) out.fill(fill) else: raise ValueError('Either an output shape or image must be provided') transform = guard_transform(transform) with rasterio.drivers(): _rasterize(valid_shapes, out, transform.to_gdal(), all_touched) return out
def open(path, mode='r', driver=None, width=None, height=None, count=None, crs=None, transform=None, dtype=None, nodata=None, **kwargs): """Open file at ``path`` in ``mode`` 'r' (read), 'r+' (read and write), or 'w' (write) and return a dataset Reader or Updater object. In write mode, a driver name such as "GTiff" or "JPEG" (see GDAL docs or ``gdal_translate --help`` on the command line), ``width`` (number of pixels per line) and ``height`` (number of lines), the ``count`` number of bands in the new file must be specified. Additionally, the data type for bands such as ``rasterio.ubyte`` for 8-bit bands or ``rasterio.uint16`` for 16-bit bands must be specified using the ``dtype`` argument. Parameters ---------- mode: string "r" (read), "r+" (read/write), or "w" (write) driver: string driver code specifying the format name (e.g. "GTiff" or "JPEG"). See GDAL docs at http://www.gdal.org/formats_list.html (optional, required for writing). width: int number of pixels per line (optional, required for write) height: int number of lines (optional, required for write) count: int > 0 number of bands (optional, required for write) dtype: rasterio.dtype the data type for bands such as ``rasterio.ubyte`` for 8-bit bands or ``rasterio.uint16`` for 16-bit bands (optional, required for write) crs: dict or string Coordinate reference system (optional, recommended for write) transform: Affine instance Affine transformation mapping the pixel space to geographic space (optional, recommended for writing). nodata: number Defines pixel value to be interpreted as null/nodata (optional, recommended for write) Returns ------- A ``DatasetReader`` or ``DatasetUpdater`` object. Notes ----- In write mode, you must specify at least ``width``, ``height``, ``count`` and ``dtype``. A coordinate reference system for raster datasets in write mode can be defined by the ``crs`` argument. It takes Proj4 style mappings like .. code:: {'proj': 'longlat', 'ellps': 'WGS84', 'datum': 'WGS84', 'no_defs': True} An affine transformation that maps ``col,row`` pixel coordinates to ``x,y`` coordinates in the coordinate reference system can be specified using the ``transform`` argument. The value should be an instance of ``affine.Affine`` .. code:: python >>> from affine import Affine >>> Affine(0.5, 0.0, -180.0, 0.0, -0.5, 90.0) These coefficients are shown in the figure below. .. code:: | x | | a b c | | c | | y | = | d e f | | r | | 1 | | 0 0 1 | | 1 | a: rate of change of X with respect to increasing column, i.e. pixel width b: rotation, 0 if the raster is oriented "north up" c: X coordinate of the top left corner of the top left pixel d: rotation, 0 if the raster is oriented "north up" e: rate of change of Y with respect to increasing row, usually a negative number (i.e. -1 * pixel height) if north-up. f: Y coordinate of the top left corner of the top left pixel A 6-element sequence of the affine transformation matrix coefficients in ``c, a, b, f, d, e`` order, (i.e. GDAL geotransform order) will be accepted until 1.0 (deprecated). A virtual filesystem can be specified. The ``vfs`` parameter may be an Apache Commons VFS style string beginning with "zip://" or "tar://"". In this case, the ``path`` must be an absolute path within that container. """ if not isinstance(path, string_types): raise TypeError("invalid path: {0!r}".format(path)) if mode and not isinstance(mode, string_types): raise TypeError("invalid mode: {0!r}".format(mode)) if driver and not isinstance(driver, string_types): raise TypeError("invalid driver: {0!r}".format(driver)) if dtype and not check_dtype(dtype): raise TypeError("invalid dtype: {0!r}".format(dtype)) if transform: transform = guard_transform(transform) elif 'affine' in kwargs: affine = kwargs.pop('affine') transform = guard_transform(affine) # If there is no currently active GDAL/AWS environment, create one. defenv() # Get AWS credentials if we're attempting to access a raster # on S3. pth, archive, scheme = parse_path(path) if scheme == 's3': Env().get_aws_credentials() log.debug("AWS credentials have been obtained") # Create dataset instances and pass the given env, which will # be taken over by the dataset's context manager if it is not # None. if mode == 'r': from rasterio._io import RasterReader s = RasterReader(path) elif mode == 'r+': from rasterio._io import writer s = writer(path, mode) elif mode == 'r-': from rasterio._base import DatasetReader s = DatasetReader(path) elif mode == 'w': from rasterio._io import writer s = writer(path, mode, driver=driver, width=width, height=height, count=count, crs=crs, transform=transform, dtype=dtype, nodata=nodata, **kwargs) else: raise ValueError( "mode string must be one of 'r', 'r+', or 'w', not %s" % mode) s.start() return s
def reproject(source, destination, src_transform=None, gcps=None, src_crs=None, src_nodata=None, dst_transform=None, dst_crs=None, dst_nodata=None, resampling=Resampling.nearest, init_dest_nodata=True, **kwargs): """Reproject a source raster to a destination raster. If the source and destination are ndarrays, coordinate reference system definitions and affine transformation parameters or ground control points (gcps) are required for reprojection. If the source and destination are rasterio Bands, shorthand for bands of datasets on disk, the coordinate reference systems and transforms or GCPs will be read from the appropriate datasets. Parameters ------------ source, destination: ndarray or Band The source and destination are 2 or 3-D ndarrays, or a single or multiple Rasterio Band object. The dimensionality of source and destination must match, i.e., for multiband reprojection the lengths of the first axes of the source and destination must be the same. src_transform: affine.Affine(), optional Source affine transformation. Required if source and destination are ndarrays. Will be derived from source if it is a rasterio Band. An error will be raised if this parameter is defined together with gcps. gcps: sequence of GroundControlPoint, optional Ground control points for the source. An error will be raised if this parameter is defined together with src_transform. src_crs: CRS or dict, optional Source coordinate reference system, in rasterio dict format. Required if source and destination are ndarrays. Will be derived from source if it is a rasterio Band. Example: CRS({'init': 'EPSG:4326'}) src_nodata: int or float, optional The source nodata value.Pixels with this value will not be used for interpolation. If not set, it will be default to the nodata value of the source image if a masked ndarray or rasterio band, if available. Must be provided if dst_nodata is not None. dst_transform: affine.Affine(), optional Target affine transformation. Required if source and destination are ndarrays. Will be derived from target if it is a rasterio Band. dst_crs: CRS or dict, optional Target coordinate reference system. Required if source and destination are ndarrays. Will be derived from target if it is a rasterio Band. dst_nodata: int or float, optional The nodata value used to initialize the destination; it will remain in all areas not covered by the reprojected source. Defaults to the nodata value of the destination image (if set), the value of src_nodata, or 0 (GDAL default). resampling: int Resampling method to use. One of the following: Resampling.nearest, Resampling.bilinear, Resampling.cubic, Resampling.cubic_spline, Resampling.lanczos, Resampling.average, Resampling.mode init_dest_nodata: bool Flag to specify initialization of nodata in destination; prevents overwrite of previous warps. Defaults to True. kwargs: dict, optional Additional arguments passed to transformation function. Returns --------- out: None Output is written to destination. """ if src_transform and gcps: raise ValueError("src_transform and gcps parameters may not" "be used together.") # Resampling guard. try: Resampling(resampling) if resampling == 7: raise ValueError except ValueError: raise ValueError( "resampling must be one of: {0}".format(", ".join( ['Resampling.{0}'.format(k) for k in Resampling.__members__.keys() if k != 'gauss']))) # If working with identity transform, assume it is crs-less data # and that translating the matrix very slightly will avoid #674 eps = 1e-100 if src_transform and guard_transform(src_transform).is_identity: src_transform = src_transform.translation(eps, eps) if dst_transform and guard_transform(dst_transform).is_identity: dst_transform = dst_transform.translation(eps, eps) if src_transform: src_transform = guard_transform(src_transform).to_gdal() if dst_transform: dst_transform = guard_transform(dst_transform).to_gdal() # Passing None can cause segfault, use empty dict if src_crs is None: src_crs = {} if dst_crs is None: dst_crs = {} _reproject(source, destination, src_transform, gcps, src_crs, src_nodata, dst_transform, dst_crs, dst_nodata, resampling, init_dest_nodata, **kwargs)
def open(fp, mode='r', driver=None, width=None, height=None, count=None, crs=None, transform=None, dtype=None, nodata=None, **kwargs): """Open a dataset for reading or writing. The dataset may be located in a local file, in a resource located by a URL, or contained within a stream of bytes. To access a dataset within a zip file without unzipping the archive use an Apache VFS style zip:// URL like zip://path/to/archive.zip!path/to/example.tif In read ('r') or read/write ('r+') mode, no other keyword arguments are required: the attributes are supplied by the opened dataset. In write mode, a driver name such as "GTiff" or "JPEG" (see GDAL docs or ``gdal_translate --help`` on the command line), ``width`` (number of pixels per line) and ``height`` (number of lines), the ``count`` number of bands in the new file must be specified. Additionally, the data type for bands such as ``rasterio.ubyte`` for 8-bit bands or ``rasterio.uint16`` for 16-bit bands must be specified using the ``dtype`` argument. Parameters ---------- fp: string or file A filename or URL, or file object opened in binary mode. mode: string "r" (read), "r+" (read/write), or "w" (write) driver: string Driver code specifying the format name (e.g. "GTiff" or "JPEG"). See GDAL docs at http://www.gdal.org/formats_list.html (optional, required for writing). width: int Number of pixels per line (optional, required for write). height: int Number of lines (optional, required for write). count: int > 0 Count of bands (optional, required for write). dtype: rasterio.dtype the data type for bands such as ``rasterio.ubyte`` for 8-bit bands or ``rasterio.uint16`` for 16-bit bands (optional, required for write) crs: dict or string Coordinate reference system (optional, recommended for write). transform: Affine instance Affine transformation mapping the pixel space to geographic space (optional, recommended for writing). nodata: number Defines pixel value to be interpreted as null/nodata (optional, recommended for write, will be broadcast to all bands). Returns ------- A ``DatasetReader`` or ``DatasetUpdater`` object. Notes ----- In write mode, you must specify at least ``width``, ``height``, ``count`` and ``dtype``. A coordinate reference system for raster datasets in write mode can be defined by the ``crs`` argument. It takes Proj4 style mappings like .. code:: {'proj': 'longlat', 'ellps': 'WGS84', 'datum': 'WGS84', 'no_defs': True} An affine transformation that maps ``col,row`` pixel coordinates to ``x,y`` coordinates in the coordinate reference system can be specified using the ``transform`` argument. The value should be an instance of ``affine.Affine`` .. code:: python >>> from affine import Affine >>> transform = Affine(0.5, 0.0, -180.0, 0.0, -0.5, 90.0) These coefficients are shown in the figure below. .. code:: | x | | a b c | | c | | y | = | d e f | | r | | 1 | | 0 0 1 | | 1 | a: rate of change of X with respect to increasing column, i.e. pixel width b: rotation, 0 if the raster is oriented "north up" c: X coordinate of the top left corner of the top left pixel d: rotation, 0 if the raster is oriented "north up" e: rate of change of Y with respect to increasing row, usually a negative number (i.e. -1 * pixel height) if north-up. f: Y coordinate of the top left corner of the top left pixel A 6-element sequence of the affine transformation matrix coefficients in ``c, a, b, f, d, e`` order, (i.e. GDAL geotransform order) will be accepted until 1.0 (deprecated). A virtual filesystem can be specified. The ``vfs`` parameter may be an Apache Commons VFS style string beginning with "zip://" or "tar://"". In this case, the ``path`` must be an absolute path within that container. """ if not isinstance(fp, string_types): if not (hasattr(fp, 'read') or hasattr(fp, 'write')): raise TypeError("invalid path or file: {0!r}".format(fp)) if mode and not isinstance(mode, string_types): raise TypeError("invalid mode: {0!r}".format(mode)) if driver and not isinstance(driver, string_types): raise TypeError("invalid driver: {0!r}".format(driver)) if dtype and not check_dtype(dtype): raise TypeError("invalid dtype: {0!r}".format(dtype)) if nodata is not None: nodata = float(nodata) if 'affine' in kwargs: # DeprecationWarning's are ignored by default with warnings.catch_warnings(): warnings.warn( "The 'affine' kwarg in rasterio.open() is deprecated at 1.0 " "and only remains to ease the transition. Please switch to " "the 'transform' kwarg. See " "https://github.com/mapbox/rasterio/issues/86 for details.", DeprecationWarning, stacklevel=2) if transform: warnings.warn( "Found both 'affine' and 'transform' in rasterio.open() - " "choosing 'transform'") transform = transform else: transform = kwargs.pop('affine') if transform: transform = guard_transform(transform) # Check driver/mode blacklist. if driver and is_blacklisted(driver, mode): raise RasterioIOError( "Blacklisted: file cannot be opened by " "driver '{0}' in '{1}' mode".format(driver, mode)) # Special case for file object argument. if mode == 'r' and hasattr(fp, 'read'): @contextmanager def fp_reader(fp): memfile = MemoryFile(fp.read()) dataset = memfile.open() try: yield dataset finally: dataset.close() memfile.close() return fp_reader(fp) elif mode == 'w' and hasattr(fp, 'write'): @contextmanager def fp_writer(fp): memfile = MemoryFile() dataset = memfile.open(driver=driver, width=width, height=height, count=count, crs=crs, transform=transform, dtype=dtype, nodata=nodata, **kwargs) try: yield dataset finally: dataset.close() memfile.seek(0) fp.write(memfile.read()) memfile.close() return fp_writer(fp) else: # The 'normal' filename or URL path. _, _, scheme = parse_path(fp) with Env() as env: # Get AWS credentials only if we're attempting to access a # raster using the S3 scheme. if scheme == 's3': env.get_aws_credentials() log.debug("AWS credentials have been obtained") # Create dataset instances and pass the given env, which will # be taken over by the dataset's context manager if it is not # None. if mode == 'r': s = DatasetReader(fp) elif mode == 'r-': warnings.warn("'r-' mode is deprecated, use 'r'", DeprecationWarning) s = DatasetReader(fp) elif mode == 'r+': s = get_writer_for_path(fp)(fp, mode) elif mode == 'w': s = get_writer_for_driver(driver)(fp, mode, driver=driver, width=width, height=height, count=count, crs=crs, transform=transform, dtype=dtype, nodata=nodata, **kwargs) else: raise ValueError( "mode must be one of 'r', 'r+', or 'w', not %s" % mode) return s
def batch_hist_match_worker(ref_paths, match_proportion, creation_options, bands, color_space, plot, masked=True, dst_suffix='_hist_matched'): """Matches the histogram of every image in ref_paths to the average histogram across all of the included images. Outputs each file with _hist_matched appended to the filename. Modified from: https://github.com/mapbox/rio-hist/blob/81e3d5f0f59ba1e4e15f8850592a818501957ed2/rio_hist/match.py """ # we need one sample to setup the processing src_path = ref_paths[0] with rasterio.open(src_path) as src: profile = src.profile.copy() src_arr = src.read(masked=masked) if src_arr.dtype != np.float32: raise ("Currently, this only supports float32") if masked: src_mask, src_fill = calculate_mask(src, src_arr) src_arr = src_arr.filled() else: src_mask, src_fill = None, None ref_out = np.zeros([len(ref_paths)] + list(src_arr.shape), dtype=src_arr.dtype) # no bands, just masks ref_mask_out = np.zeros([len(ref_paths)] + list(src_arr.shape[1:]), dtype=src_arr.dtype) n_bands = src_arr.shape[0] for i, ref_path in enumerate(ref_paths): # import pdb;pdb.set_trace(); try: with rasterio.open(ref_path, 'r') as ref: ref_arr = ref.read(masked=masked) if masked: ref_mask, ref_fill = calculate_mask(ref, ref_arr) ref_arr = ref_arr.filled() else: ref_mask, ref_fill = None, None ref_out[i, :, :, :] = ref_arr if ref_mask is None: ref_mask_out = None else: ref_mask_out[i, :, :] = ref_mask except: import pdb pdb.set_trace() bixs = tuple([int(x) - 1 for x in bands.split(',')]) # arrnorm_raw = ref_out / np.iinfo(ref_out.dtype).max ref = ref_out #[:, bixs, :, :] output_paths = [] print("Matching histograms...") for src_path in tqdm(ref_paths): with rasterio.open(src_path) as src: profile = src.profile.copy() src_arr = src.read(masked=masked) if masked: src_mask, src_fill = calculate_mask(src, src_arr) src_arr = src_arr.filled() else: src_mask, src_fill = None, None # src = cs_forward(src_arr, color_space, band_range=range(n_bands)) src = src_arr target = src.copy() for i, b in enumerate(bixs): logger.debug("Processing band {}".format(b)) src_band = src[b] ref_band = ref[:, b, :, :] # Re-apply 2D mask to each band if src_mask is not None: logger.debug("apply src_mask to band {}".format(b)) src_band = np.ma.asarray(src_band) src_band.mask = src_mask src_band.fill_value = src_fill if ref_mask_out is not None: logger.debug("apply ref_mask to band {}".format(b)) ref_band = np.ma.asarray(ref_band) ref_band.mask = ref_mask_out ref_band.fill_value = ref_fill target[b] = histogram_match(src_band, ref_band, match_proportion) target_rgb = target # cs_backward(target, color_space) # re-apply src_mask to target_rgb and write ndv if src_mask is not None: logger.debug("apply src_mask to target_rgb") if not np.ma.is_masked(target_rgb): target_rgb = np.ma.asarray(target_rgb) target_rgb.mask = np.array((src_mask, src_mask, src_mask)) target_rgb.fill_value = src_fill profile['count'] = n_bands # profile['dtype'] = 'uint8' profile['nodata'] = None profile['transform'] = guard_transform(profile['transform']) profile.update(creation_options) input_base_filepath = os.path.splitext(os.path.abspath(src_path))[0] dst_path = '{}{}.tif'.format(input_base_filepath, dst_suffix) logger.info("Writing raster {}".format(dst_path)) with rasterio.open(dst_path, 'w', **profile) as dst: for b in bixs: dst.write(target_rgb[b], b + 1) if src_mask is not None: gdal_mask = (np.invert(src_mask) * 255).astype('uint8') # write to extra band dst.write(gdal_mask, bixs[-1] + 2) output_paths.append(dst_path) return output_paths
def open( path, mode='r', driver=None, width=None, height=None, count=None, crs=None, transform=None, dtype=None, nodata=None, **kwargs): """Open file at ``path`` in ``mode`` "r" (read), "r+" (read/write), or "w" (write) and return a ``Reader`` or ``Updater`` object. In write mode, a driver name such as "GTiff" or "JPEG" (see GDAL docs or ``gdal_translate --help`` on the command line), ``width`` (number of pixels per line) and ``height`` (number of lines), the ``count`` number of bands in the new file must be specified. Additionally, the data type for bands such as ``rasterio.ubyte`` for 8-bit bands or ``rasterio.uint16`` for 16-bit bands must be specified using the ``dtype`` argument. A coordinate reference system for raster datasets in write mode can be defined by the ``crs`` argument. It takes Proj4 style mappings like {'proj': 'longlat', 'ellps': 'WGS84', 'datum': 'WGS84', 'no_defs': True} An affine transformation that maps ``col,row`` pixel coordinates to ``x,y`` coordinates in the coordinate reference system can be specified using the ``transform`` argument. The value may be either an instance of ``affine.Affine`` or a 6-element sequence of the affine transformation matrix coefficients ``a, b, c, d, e, f``. These coefficients are shown in the figure below. | x | | a b c | | c | | y | = | d e f | | r | | 1 | | 0 0 1 | | 1 | a: rate of change of X with respect to increasing column, i.e. pixel width b: rotation, 0 if the raster is oriented "north up" c: X coordinate of the top left corner of the top left pixel f: Y coordinate of the top left corner of the top left pixel d: rotation, 0 if the raster is oriented "north up" e: rate of change of Y with respect to increasing row, usually a negative number i.e. -1 * pixel height f: Y coordinate of the top left corner of the top left pixel Finally, additional kwargs are passed to GDAL as driver-specific dataset creation parameters. """ if not isinstance(path, string_types): raise TypeError("invalid path: %r" % path) if mode and not isinstance(mode, string_types): raise TypeError("invalid mode: %r" % mode) if driver and not isinstance(driver, string_types): raise TypeError("invalid driver: %r" % driver) if mode in ('r', 'r+'): if not os.path.exists(path): raise IOError("no such file or directory: %r" % path) if transform: transform = guard_transform(transform) if mode == 'r': from rasterio._io import RasterReader s = RasterReader(path) elif mode == 'r+': from rasterio._io import RasterUpdater s = RasterUpdater(path, mode) elif mode == 'r-': from rasterio._base import DatasetReader s = DatasetReader(path) elif mode == 'w': from rasterio._io import RasterUpdater s = RasterUpdater( path, mode, driver, width, height, count, crs, transform, dtype, nodata, **kwargs) else: raise ValueError( "mode string must be one of 'r', 'r+', or 'w', not %s" % mode) s.start() return s
def rasterize( shapes, out_shape=None, fill=0, output=None, transform=IDENTITY, all_touched=False, default_value=255): """Returns an image array with points, lines, or polygons burned in. A different value may be specified for each shape. The shapes may be georeferenced or may have image coordinates. An existing image array may be provided, or one may be created. By default, the center of image elements determines whether they are updated, but all touched elements may be optionally updated. :param shapes: an iterator over Fiona style geometry objects (with a default value of 255) or an iterator over (geometry, value) pairs. Values must be unsigned integer type (uint8). :param transform: GDAL style geotransform to be applied to the image. :param out_shape: shape of created image array :param fill: fill value for created image array :param output: alternatively, an existing image array :param all_touched: if True, will rasterize all pixels touched, otherwise will use GDAL default method. :param default_value: value burned in for shapes if not provided as part of shapes. Must be unsigned integer type (uint8). """ if not isinstance(default_value, int) or ( default_value > 255 or default_value < 0): raise ValueError("default_value %s is not uint8/ubyte" % default_value) geoms = [] for index, entry in enumerate(shapes): if isinstance(entry, (tuple, list)): geometry, value = entry if not isinstance(value, int) or value > 255 or value < 0: raise ValueError( "Shape number %i, value '%s' is not uint8/ubyte" % ( index, value)) geoms.append((geometry, value)) else: geoms.append((entry, default_value)) if out_shape is not None: out = np.empty(out_shape, dtype=rasterio.ubyte) out.fill(fill) elif output is not None: if np.dtype(output.dtype) != np.dtype(rasterio.ubyte): raise ValueError("Output image must be dtype uint8/ubyte") out = output else: raise ValueError("An output image must be provided or specified") transform = guard_transform(transform) with rasterio.drivers(): _rasterize(geoms, out, transform.to_gdal(), all_touched) return out
def open( path, mode='r', driver=None, width=None, height=None, count=None, crs=None, transform=None, dtype=None, nodata=None, **kwargs): """Open file at ``path`` in ``mode`` "r" (read), "r+" (read/write), or "w" (write) and return a ``Reader`` or ``Updater`` object. In write mode, a driver name such as "GTiff" or "JPEG" (see GDAL docs or ``gdal_translate --help`` on the command line), ``width`` (number of pixels per line) and ``height`` (number of lines), the ``count`` number of bands in the new file must be specified. Additionally, the data type for bands such as ``rasterio.ubyte`` for 8-bit bands or ``rasterio.uint16`` for 16-bit bands must be specified using the ``dtype`` argument. A coordinate reference system for raster datasets in write mode can be defined by the ``crs`` argument. It takes Proj4 style mappings like {'proj': 'longlat', 'ellps': 'WGS84', 'datum': 'WGS84', 'no_defs': True} An affine transformation that maps ``col,row`` pixel coordinates to ``x,y`` coordinates in the coordinate reference system can be specified using the ``transform`` argument. The value may be either an instance of ``affine.Affine`` or a 6-element sequence of the affine transformation matrix coefficients ``a, b, c, d, e, f``. These coefficients are shown in the figure below. | x | | a b c | | c | | y | = | d e f | | r | | 1 | | 0 0 1 | | 1 | a: rate of change of X with respect to increasing column, i.e. pixel width b: rotation, 0 if the raster is oriented "north up" c: X coordinate of the top left corner of the top left pixel f: Y coordinate of the top left corner of the top left pixel d: rotation, 0 if the raster is oriented "north up" e: rate of change of Y with respect to increasing row, usually a negative number i.e. -1 * pixel height f: Y coordinate of the top left corner of the top left pixel A virtual filesystem can be specified. The ``vfs`` parameter may be an Apache Commons VFS style string beginning with "zip://" or "tar://"". In this case, the ``path`` must be an absolute path within that container. Finally, additional kwargs are passed to GDAL as driver-specific dataset creation parameters. """ if not isinstance(path, string_types): raise TypeError("invalid path: %r" % path) if mode and not isinstance(mode, string_types): raise TypeError("invalid mode: %r" % mode) if driver and not isinstance(driver, string_types): raise TypeError("invalid driver: %r" % driver) if transform: transform = guard_transform(transform) elif 'affine' in kwargs: affine = kwargs.pop('affine') transform = guard_transform(affine) if mode == 'r': from rasterio._io import RasterReader s = RasterReader(path) elif mode == 'r+': from rasterio._io import writer s = writer(path, mode) elif mode == 'r-': from rasterio._base import DatasetReader s = DatasetReader(path) elif mode == 'w': from rasterio._io import writer s = writer(path, mode, driver=driver, width=width, height=height, count=count, crs=crs, transform=transform, dtype=dtype, nodata=nodata, **kwargs) else: raise ValueError( "mode string must be one of 'r', 'r+', or 'w', not %s" % mode) s.start() return s
'uly': uly, 'filename': fnames, 'lrx': lrx, 'lry': lry}, index=range(len(ulx))) min_x = df.ulx.min() max_x = df.lrx.max() min_y = df.lry.min() max_y = df.uly.max() # pixel resolution res_x = geobox.affine[0] res_y = geobox.affine[4] # form the affine for the new image new_aff = guard_transform([min_x, res_x, 0, max_y, 0, res_y]) # get the LR in pixel co-ordinates lr_x, lr_y = (max_x, min_y) * ~new_aff cols = int(lr_x) rows = int(lr_y) # create a time/band index for the new time series mosaic image t_df = df['timestamp'].unique() t_index_df = pandas.DataFrame({'timestamp': t_df, 'band_index': numpy.arange(t_df.shape[0])}) n_bands = t_index_df.shape[0] # join with the original dataframe new_df = pandas.merge(df, t_index_df, on='timestamp')