def test_window_function(): with rasterio.open('tests/data/RGB.byte.tif') as src: left, bottom, right, top = src.bounds dx, dy = src.res height = src.height width = src.width assert from_bounds( left + EPS, bottom + EPS, right - EPS, top - EPS, src.transform, height, width) == ((0, height), (0, width)) assert from_bounds( left, top - 400, left + 400, top, src.transform, height, width) == ((0, 2), (0, 2)) assert from_bounds( left, top - 2 * dy - EPS, left + 2 * dx - EPS, top, src.transform, height, width) == ((0, 2), (0, 2)) # bounds cropped assert from_bounds( left - 2 * dx, top - 2 * dy, left + 2 * dx, top + 2 * dy, src.transform, height, width) == ((0, 2), (0, 2)) # boundless assert from_bounds( left - 2 * dx, top - 2 * dy, left + 2 * dx, top + 2 * dy, src.transform, boundless=True) == ((-2, 2), (-2, 2))
def extract_image(rst, polygon): with MemoryFile() as memfile: meta = rst.meta.copy() meta["count"] = 4 rgb = mask(rst, [polygon])[0] a = raster_geometry_mask(rst, [polygon], invert=True)[0].astype(rio.uint8) a = np.where(a == 1, 255, 0).astype(rio.uint8) img_data = np.stack((rgb[0], rgb[1], rgb[2], a)) with memfile.open(**meta) as masked: masked.write(img_data) r = masked.read(1, window=from_bounds(*polygon.bounds, rst.transform)) g = masked.read(2, window=from_bounds(*polygon.bounds, rst.transform)) b = masked.read(3, window=from_bounds(*polygon.bounds, rst.transform)) a = masked.read(4, window=from_bounds(*polygon.bounds, rst.transform)) img = Image.fromarray(np.dstack((r, g, b, a))) return img
def test_issue_2138(): """WindowError is raised if bounds and transform are inconsistent""" w, s, e, n = 1.0, 45.7, 1.2, 45.9 a = 0.001 transform = Affine.translation(w, n) * Affine.scale(a, -a) with pytest.raises(WindowError): from_bounds(w, n, e, s, transform)
def test_window_function_valuerror(): with rasterio.open('tests/data/RGB.byte.tif') as src: left, bottom, right, top = src.bounds with pytest.raises(ValueError): # No height or width from_bounds(left + EPS, bottom + EPS, right - EPS, top - EPS, src.transform)
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 write(self, process_tile, data): """ Write data from process tiles into GeoTIFF file(s). Parameters ---------- process_tile : ``BufferedTile`` must be member of process ``TilePyramid`` """ data = prepare_array(data, masked=True, nodata=self.output_params["nodata"], dtype=self.profile(process_tile)["dtype"]) if data.mask.all(): logger.debug("data empty, nothing to write") else: # Convert from process_tile to output_tiles and write for tile in self.pyramid.intersecting(process_tile): out_tile = BufferedTile(tile, self.pixelbuffer) write_window = from_bounds( *out_tile.bounds, transform=self.rio_file.transform, height=self.rio_file.height, width=self.rio_file.width).round_lengths( pixel_precision=0).round_offsets(pixel_precision=0) if _window_in_out_file(write_window, self.rio_file): logger.debug("write data to window: %s", write_window) self.rio_file.write( extract_from_array(in_raster=data, in_affine=process_tile.affine, out_tile=out_tile) if process_tile != out_tile else data, window=write_window, )
def reproject_band(self, band, src_transform, bbox): # shift dst_transform # bbox --> (left, bottom, right, top) assert self.out_res in ["10", "30", "20", "60" ], "output resolution must be 10, 20, 30 or 60" transform_out_res = self.transform_dict[self.out_res][0] dst_transform = rasterio.Affine( transform_out_res.a, transform_out_res.b, bbox[0] if transform_out_res.a > 0 else bbox[2], transform_out_res.d, transform_out_res.e, bbox[3] if transform_out_res.e < 0 else bbox[1]) window_read = windows.from_bounds(*bbox, dst_transform) shape_new = tuple([int(round(s)) for s in windows.shape(window_read)]) data_new_proj = np.ndarray(shape=shape_new, dtype=band.dtype) reproject(band, data_new_proj, src_transform=src_transform, src_crs=self.crs, dst_transform=dst_transform, dst_crs=self.crs, resampling=Resampling.cubic_spline) return data_new_proj
def _requested_tile_aligned_with_internal_tile( src_dst: Union[DatasetReader, DatasetWriter, WarpedVRT], bounds: Tuple[float, float, float, float], height: int, width: int, ) -> bool: """Check if tile is aligned with internal tiles.""" if not src_dst.is_tiled: return False if src_dst.crs != constants.WEB_MERCATOR_CRS: return False col_off, row_off, w, h = windows.from_bounds( *bounds, height=height, transform=src_dst.transform, width=width ).flatten() if round(w) % 64 and round(h) % 64: return False if (src_dst.width - round(col_off)) % 64: return False if (src_dst.height - round(row_off)) % 64: return False return True
def _bounds_to_ranges(bounds, affine, shape): return map(int, itertools.chain( *from_bounds( *bounds, transform=affine, height=shape[-2], width=shape[-1] ).round_lengths().round_offsets().toranges() ) )
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 windows.from_bounds( left, bottom, right, top, transform=transform, height=self.height, width=self.width, precision=precision)
def load_data(stand_path, chip_size=None, offsets=None): """Loads NAIP, LANDSAT, and stand delineation data""" dirname, cell_id, state_name, year, agency = parse_stand_path(stand_path) naip_path = get_naip_path(dirname, cell_id, state_name, year) landsat_path = get_landsat_path(dirname, cell_id, state_name, year) with rasterio.open(naip_path) as src: profile = src.profile height, width = src.shape if chip_size is not None: if offsets is not None: row_off, col_off = offsets else: row_off = np.random.randint(0, height - chip_size) col_off = np.random.randint(0, width - chip_size) window = windows.Window(col_off, row_off, chip_size, chip_size) else: window = None naip = reshape_as_image(src.read(window=window)) if window is not None: trf = src.window_transform(window) bbox = src.window_bounds(window) else: trf = src.transform bbox = src.bounds with rasterio.open(landsat_path) as src: if chip_size is not None: window = windows.from_bounds(*bbox, transform=src.transform, height=chip_size, width=chip_size) else: window = windows.from_bounds(*bbox, transform=src.transform, height=height, width=width) landsat = ((reshape_as_image( np.stack([src.read(band + 1, window=window) for band in range(4)])) / 3000).clip(0, 1) * 255).astype( np.uint8) stands = gpd.read_file(stand_path) stands = gpd.clip(stands, box(*bbox)) return naip, landsat, profile, trf, stands
def test_window_float(path_rgb_byte_tif): """Test window float values""" with rasterio.open(path_rgb_byte_tif) as src: left, bottom, right, top = src.bounds dx, dy = src.res height = src.height width = src.width assert_window_almost_equals(from_bounds( left, top - 400, left + 400, top, src.transform, height, width), Window.from_slices((0, 400 / src.res[1]), (0, 400 / src.res[0])))
def get_pixel_tree_cover(xy): arr = src.read(1, window=windows.from_bounds(xy[0] - x_inc, xy[1] - y_inc, xy[0] + x_inc, xy[1] + y_inc, transform=src.transform)) # ACHTUNG: gdalmerge might have messed with `src.nodata` # TODO: avoid UGLY HARDCODED zero below (inspect gdalmerge or # accept extra click CLI arg for tree nodata value) # return np.sum(arr != src.nodata) / arr.size return np.sum(arr != 0) / arr.size
def rect(self, fin, fout, bounds, mode='asc'): """crops a rectangle of Bounds, from a file. Returns a asc (grid) or geotiff Arguments: fin {string} -- input file fout {string} -- output file path bounds {bound class} -- a class with top,left,bottom,right attrs Keyword Arguments: mode {string} -- output file mode. can be 'asc' or 'geotiff' (default: {'asc'}) Returns: [bool] -- Nothing, for now. True """ with rasterio.open(fin, mode='r') as dataset: print(dataset.crs) print(dataset.bounds) #big lon_a, lat_a = self.wgs84_to_utm(bounds.left, bounds.top) # lon, lat # TOPLEFT lon_b, lat_b = self.wgs84_to_utm( bounds.right, bounds.bottom) # lon, lat #BOTTOMRIGHT print("NW:", lon_a, lat_a) print("SE:", lon_b, lat_b) # this return in row, col (Y,X) # py_a,px_a = dataset.index( lon_a, lat_a ) # py_b,px_b = dataset.index( lon_b, lat_b ) # width = px_b - px_a # height = py_b - py_a # print("A:",lon_a, lat_a,":", px_a, py_a) # print("B:",lon_b, lat_b,":", px_b, py_b) # print("width, height: ", width, height) bbox = from_bounds(lon_a, lat_b, lon_b, lat_a, dataset.transform) #left, bottom, right, top window = dataset.read(window=bbox) # 1 -> 1 channel, nothing, all # this is the point (A) scaled, so we can locate coords inside. # Affine.scale(res_x, res_x) should be Affine.scale(res_x, res_y) but this generates # non square pixels, and insert dx,dy values, that are not supported by noone. # tested with GlobalMapper, and it works fine. # res_x = (lon_b - lon_a) / width # res_y = (lat_b - lat_a) / height #transform = Affine.translation(lon_a + res_x, lat_a + res_y) * Affine.scale(res_x, res_x) transform = dataset.window_transform(bbox) self.outputs[mode](fout, width, height, window, transform, dataset.crs) self.add_prj(fout, dataset.crs) return True
def test_window_float(): """Test window float values""" with rasterio.open('tests/data/RGB.byte.tif') as src: left, bottom, right, top = src.bounds dx, dy = src.res height = src.height width = src.width assert_window_almost_equals( from_bounds(left, top - 400, left + 400, top, src.transform, height, width), Window.from_slices((0, 400 / src.res[1]), (0, 400 / src.res[0])))
def test_window_from_bounds(path_rgb_byte_tif): # TODO: break this test up. with rasterio.open(path_rgb_byte_tif) as src: left, bottom, right, top = src.bounds dx, dy = src.res height = src.height width = src.width assert_window_almost_equals(from_bounds( left + EPS, bottom + EPS, right - EPS, top - EPS, src.transform, height, width), Window.from_slices((0, height), (0, width))) assert_window_almost_equals(from_bounds( left, top - 2 * dy - EPS, left + 2 * dx - EPS, top, src.transform, height, width), Window.from_slices((0, 2), (0, 2))) # boundless assert_window_almost_equals( from_bounds(left - 2 * dx, top - 2 * dy, left + 2 * dx, top + 2 * dy, src.transform, height=height, width=width), Window.from_slices((-2, 2), (-2, 2), boundless=True, height=height, width=width))
def read(self, band_name, window=None, bbox=None, crs=None): # pragma: no cover """Read an asset given a band name. Notes: You must install the extra `geo` containing the `rasterio` and `Shapely` library in order to use this method: pip install stac.py[geo] :param band_name: Band name used in the asset :type band_name: str :param window: window crop :type window: raster.windows.Window :param bbox: The bounding box :type bbox: Union[str,Tuple[float],List[float],BaseGeometry] :param crs: The Coordinate Reference System :return: the asset as a numpy array :rtype: numpy.ndarray """ import rasterio from rasterio.crs import CRS from rasterio.warp import transform from rasterio.windows import from_bounds # Check Authorization _ = Utils.safe_request(self.assets[band_name]['href'], method='head') source_crs = CRS.from_string('EPSG:4326') if crs: source_crs = CRS.from_string(crs) with rasterio.open(self.assets[band_name]['href']) as dataset: if bbox: bbox = Utils.build_bbox(bbox) w, s, e, n = bbox.bounds t = transform(source_crs, dataset.crs, [w, e], [s, n]) window = from_bounds(t[0][0], t[1][0], t[0][1], t[1][1], dataset.transform) asset = dataset.read(1, window=window) return asset
def _get_rasterio_window(self, selector, dst_crs, transform): left_edge = self.LeftEdge right_edge = self.RightEdge transform_x, transform_y = warp.transform( self.ds.parameters["crs"], dst_crs, [left_edge[0], right_edge[0]], [left_edge[1], right_edge[1]], zs=None, ) window = from_bounds(transform_x[0], transform_y[0], transform_x[1], transform_y[1], transform) return window
def crop(src, geom, extend_box): left, bottom, right, top = geom.bounds window = from_bounds(left - extend_box, bottom - extend_box, right + extend_box, top + extend_box, transform=src.transform) masked_image = src.read(window=window) #Roll depth to channel last masked_image = np.rollaxis(masked_image, 0, 3) #Skip empty frames if masked_image.size == 0: raise ValueError("Empty Frame") return masked_image
def test_baselevels_output_buffer(mp_tmpdir, baselevels_output_buffer): # it should not contain masked values within bounds # (171.46155, -87.27184, 174.45159, -84.31281) with mapchete.open(baselevels_output_buffer.dict) as mp: # process all mp.batch_process() # read tile 6/62/125.tif with rasterio.open( os.path.join(mp.config.output.output_params["path"], "6/62/125.tif") ) as src: window = windows.from_bounds( 171.46155, -87.27184, 174.45159, -84.31281, transform=src.transform ) subset = src.read(window=window, masked=True) print(subset.shape) assert not subset.mask.any() pass
def _get_rasterio_window(self, selector, dst_crs, transform): """ Calculate position, width, and height for a rasterio window read. """ left_edge, right_edge = self._get_selection_window(selector) transform_x, transform_y = warp.transform( self.ds.parameters["crs"], dst_crs, [left_edge[0], right_edge[0]], [left_edge[1], right_edge[1]], zs=None, ) window = from_bounds(transform_x[0], transform_y[0], transform_x[1], transform_y[1], transform) return window
def merge(datasets, bounds=None, res=None, nodata=None, precision=7, indexes=None): """Copy valid pixels from input files to an output file. All files must have the same number of bands, data type, and coordinate reference system. Input files are merged in their listed order using the reverse painter's algorithm. If the output file exists, its values will be overwritten by input values. Geospatial bounds and resolution of a new output file in the units of the input file coordinate reference system may be provided and are otherwise taken from the first input file. Parameters ---------- datasets: list of dataset objects opened in 'r' mode source datasets to be merged. bounds: tuple, optional Bounds of the output image (left, bottom, right, top). If not set, bounds are determined from bounds of input rasters. res: tuple, optional Output resolution in units of coordinate reference system. If not set, the resolution of the first raster is used. If a single value is passed, output pixels will be square. nodata: float, optional nodata value to use in output file. If not set, uses the nodata value in the first input raster. precision: float, optional Number of decimal points of precision when computing inverse transform. indexes : list of ints or a single int, optional bands to read and merge Returns ------- tuple Two elements: dest: numpy ndarray Contents of all input rasters in single array out_transform: affine.Affine() Information for mapping pixel coordinates in `dest` to another coordinate system """ first = datasets[0] first_res = first.res nodataval = first.nodatavals[0] dtype = first.dtypes[0] # Determine output band count if indexes is None: output_count = first.count elif isinstance(indexes, int): output_count = 1 else: output_count = len(indexes) # Extent from option or extent of all inputs if bounds: dst_w, dst_s, dst_e, dst_n = bounds else: # scan input files xs = [] ys = [] for src in datasets: left, bottom, right, top = src.bounds xs.extend([left, right]) ys.extend([bottom, top]) dst_w, dst_s, dst_e, dst_n = min(xs), min(ys), max(xs), max(ys) logger.debug("Output bounds: %r", (dst_w, dst_s, dst_e, dst_n)) output_transform = Affine.translation(dst_w, dst_n) logger.debug("Output transform, before scaling: %r", output_transform) # Resolution/pixel size if not res: res = first_res elif not np.iterable(res): res = (res, res) elif len(res) == 1: res = (res[0], res[0]) output_transform *= Affine.scale(res[0], -res[1]) logger.debug("Output transform, after scaling: %r", output_transform) # Compute output array shape. We guarantee it will cover the output # bounds completely output_width = int(math.ceil((dst_e - dst_w) / res[0])) output_height = int(math.ceil((dst_n - dst_s) / res[1])) # Adjust bounds to fit dst_e, dst_s = output_transform * (output_width, output_height) logger.debug("Output width: %d, height: %d", output_width, output_height) logger.debug("Adjusted bounds: %r", (dst_w, dst_s, dst_e, dst_n)) # create destination array dest = np.zeros((output_count, output_height, output_width), dtype=dtype) if nodata is not None: nodataval = nodata logger.debug("Set nodataval: %r", nodataval) if nodataval is not None: # Only fill if the nodataval is within dtype's range inrange = False if np.dtype(dtype).kind in ('i', 'u'): info = np.iinfo(dtype) inrange = (info.min <= nodataval <= info.max) elif np.dtype(dtype).kind == 'f': info = np.finfo(dtype) if np.isnan(nodataval): inrange = True else: inrange = (info.min <= nodataval <= info.max) if inrange: dest.fill(nodataval) else: warnings.warn( "Input file's nodata value, %s, is beyond the valid " "range of its data type, %s. Consider overriding it " "using the --nodata option for better results." % ( nodataval, dtype)) else: nodataval = 0 for src in datasets: # Real World (tm) use of boundless reads. # This approach uses the maximum amount of memory to solve the # problem. Making it more efficient is a TODO. # 1. Compute spatial intersection of destination and source src_w, src_s, src_e, src_n = src.bounds int_w = src_w if src_w > dst_w else dst_w int_s = src_s if src_s > dst_s else dst_s int_e = src_e if src_e < dst_e else dst_e int_n = src_n if src_n < dst_n else dst_n # 2. Compute the source window src_window = windows.from_bounds( int_w, int_s, int_e, int_n, src.transform, precision=precision) logger.debug("Src %s window: %r", src.name, src_window) src_window = src_window.round_shape() # 3. Compute the destination window dst_window = windows.from_bounds( int_w, int_s, int_e, int_n, output_transform, precision=precision) # 4. Read data in source window into temp trows, tcols = ( int(round(dst_window.height)), int(round(dst_window.width))) temp_shape = (output_count, trows, tcols) temp = src.read(out_shape=temp_shape, window=src_window, boundless=False, masked=True, indexes=indexes) # 5. Copy elements of temp into dest roff, coff = ( int(round(dst_window.row_off)), int(round(dst_window.col_off))) region = dest[:, roff:roff + trows, coff:coff + tcols] if np.isnan(nodataval): region_nodata = np.isnan(region) temp_nodata = np.isnan(temp) else: region_nodata = region == nodataval temp_nodata = temp.mask mask = np.logical_and(region_nodata, ~temp_nodata) np.copyto(region, temp, where=mask) return dest, output_transform
def test_window_bounds_north_up(): transform = Affine.translation(0.0, 10.0) * Affine.scale(1.0, -1.0) * Affine.identity() assert_window_almost_equals( from_bounds(0, 0, 10, 10, transform, 10, 10), Window(0, 0, 10, 10))
def test_window_bounds_south_up(): identity = Affine.identity() assert_window_almost_equals( from_bounds(0, 10, 10, 0, identity, 10, 10), Window(0, 0, 10, 10))