def calc_proximity( input_rasters, target_value=1, out_path=None, max_dist=1000, add_border=False, weighted=False, invert=False, return_array=False, postfix="_proximity", uuid=False, overwrite=True, skip_existing=False, ): """ Calculate the proximity of input_raster to values """ raster_list, path_list = ready_io_raster(input_rasters, out_path, overwrite, postfix=postfix, uuid=uuid) output = [] for index, input_raster in enumerate(raster_list): out_path = path_list[index] if skip_existing and os.path.exists(out_path): output.append(out_path) continue in_arr = raster_to_array(input_raster, filled=True) bin_arr = (in_arr != target_value).astype("uint8") bin_raster = array_to_raster(bin_arr, reference=input_raster) in_raster = open_raster(bin_raster) in_raster_path = bin_raster if add_border: border_size = 1 border_raster = add_border_to_raster( in_raster, border_size=border_size, border_value=0, overwrite=True, ) in_raster = open_raster(border_raster) gdal.Unlink(in_raster_path) in_raster_path = border_raster src_band = in_raster.GetRasterBand(1) driver_name = "GTiff" if out_path is None else path_to_driver_raster( out_path) if driver_name is None: raise ValueError(f"Unable to parse filetype from path: {out_path}") driver = gdal.GetDriverByName(driver_name) if driver is None: raise ValueError( f"Error while creating driver from extension: {out_path}") mem_path = f"/vsimem/raster_proximity_tmp_{uuid4().int}.tif" dest_raster = driver.Create( mem_path, in_raster.RasterXSize, in_raster.RasterYSize, 1, gdal.GetDataTypeByName("Float32"), ) dest_raster.SetGeoTransform(in_raster.GetGeoTransform()) dest_raster.SetProjection(in_raster.GetProjectionRef()) dst_band = dest_raster.GetRasterBand(1) gdal.ComputeProximity( src_band, dst_band, [ f"VALUES='1'", "DISTUNITS=GEO", f"MAXDIST={max_dist}", ], ) dst_arr = dst_band.ReadAsArray() gdal.Unlink(mem_path) gdal.Unlink(in_raster_path) dst_arr = np.where(dst_arr > max_dist, max_dist, dst_arr) if invert: dst_arr = max_dist - dst_arr if weighted: dst_arr = dst_arr / max_dist if add_border: dst_arr = dst_arr[border_size:-border_size, border_size:-border_size] src_band = None dst_band = None in_raster = None dest_raster = None if return_array: output.append(dst_arr) else: array_to_raster(dst_arr, reference=input_raster, out_path=out_path) output.append(out_path) dst_arr = None if isinstance(input_rasters, list): return output return output[0]
def add_border_to_raster( input_raster, out_path=None, border_size=100, border_size_unit="px", border_value=0, overwrite: bool = True, creation_options: list = [], ): in_raster = open_raster(input_raster) metadata = raster_to_metadata(in_raster) # Parse the driver driver_name = "GTiff" if out_path is None else path_to_driver_raster( out_path) if driver_name is None: raise ValueError(f"Unable to parse filetype from path: {out_path}") driver = gdal.GetDriverByName(driver_name) if driver is None: raise ValueError( f"Error while creating driver from extension: {out_path}") output_name = None if out_path is None: output_name = f"/vsimem/raster_proximity_{uuid4().int}.tif" else: output_name = out_path in_arr = raster_to_array(in_raster) if border_size_unit == "px": border_size_y = border_size border_size_x = border_size new_shape = ( in_arr.shape[0] + (2 * border_size_y), in_arr.shape[1] + (2 * border_size_x), in_arr.shape[2], ) else: border_size_y = round(border_size / metadata["pixel_height"]) border_size_x = round(border_size / metadata["pixel_width"]) new_shape = ( in_arr.shape[0] + (2 * border_size_y), in_arr.shape[1] + (2 * border_size_x), in_arr.shape[2], ) new_arr = np.full(new_shape, border_value, dtype=in_arr.dtype) new_arr[border_size_y:-border_size_y, border_size_x:-border_size_x, :] = in_arr if isinstance(in_arr, np.ma.MaskedArray): mask = np.zeros(new_shape, dtype=bool) mask[border_size_y:-border_size_y, border_size_x:-border_size_x, :] = in_arr.mask new_arr = np.ma.array(new_arr, mask=mask) new_arr.fill_value = in_arr.fill_value remove_if_overwrite(out_path, overwrite) dest_raster = driver.Create( output_name, new_shape[1], new_shape[0], metadata["band_count"], numpy_to_gdal_datatype(in_arr.dtype), default_options(creation_options), ) og_transform = in_raster.GetGeoTransform() new_transform = [] for i in og_transform: new_transform.append(i) new_transform[0] -= border_size_x * og_transform[1] new_transform[3] -= border_size_y * og_transform[5] dest_raster.SetGeoTransform(new_transform) dest_raster.SetProjection(in_raster.GetProjectionRef()) for band_num in range(1, metadata["band_count"] + 1): dst_band = dest_raster.GetRasterBand(band_num) dst_band.WriteArray(new_arr[:, :, band_num - 1]) if metadata["has_nodata"]: dst_band.SetNoDataValue(metadata["nodata_value"]) return output_name
def test_extraction( rasters: Union[list, str, gdal.Dataset], arrays: Union[list, np.ndarray], grid: Union[ogr.DataSource, str], samples: int = 1000, # if 0, all grid_layer_index: int = 0, verbose: int = 1, ) -> bool: """Validates the output of the patch_extractor. Useful if you need peace of mind. Set samples to 0 to tests everything. Args: rasters (list of rasters | path | raster): The raster(s) used. arrays (list of arrays | ndarray): The arrays generated. grid (vector | vector_path): The grid generated. **kwargs: samples (int): The amount of patches to randomly test. If 0 all patches will be tested. This is a long process, so consider only testing everything if absolutely necessary. grid_layer_index (int): If the grid is part of a multi-layer vector, specify the index of the grid. verbose (int): If 1 will output messages on progress. Returns: True if the extraction is valid. Raises an error otherwise. """ type_check(rasters, [list, str, gdal.Dataset], "rasters") type_check(arrays, [list, str, np.ndarray], "arrays") type_check(grid, [list, str, ogr.DataSource], "grid") type_check(samples, [int], "samples") type_check(grid_layer_index, [int], "clip_layer_index") type_check(verbose, [int], "verbose") in_rasters = to_raster_list(rasters) in_arrays = arrays if verbose == 1: print("Verifying integrity of output grid..") # grid_memory = open_vector(internal_vector_to_memory(grid)) grid_memory = open_vector(grid) grid_metadata = internal_vector_to_metadata(grid) grid_projection = grid_metadata["projection_osr"] if grid_layer_index > (grid_metadata["layer_count"] - 1): raise ValueError( f"Requested non-existing layer index: {grid_layer_index}") grid_layer = grid_memory.GetLayer(grid_layer_index) # Select sample fids feature_count = grid_metadata["layers"][grid_layer_index]["feature_count"] test_samples = samples if samples > 0 else feature_count max_test = min(test_samples, feature_count) - 1 test_fids = np.array(random.sample(range(0, feature_count), max_test), dtype="uint64") mem_driver = ogr.GetDriverByName("ESRI Shapefile") for index, raster in enumerate(in_rasters): test_rast = open_raster(raster) test_array = in_arrays[index] if isinstance(test_array, str): if not os.path.exists(test_array): raise ValueError(f"Numpy array does not exist: {test_array}") try: test_array = np.load(in_arrays[index]) except: raise Exception( f"Attempted to read numpy raster from: {in_arrays[index]}") base = os.path.basename(raster) basename = os.path.splitext(base)[0] if verbose == 1: print(f"Testing: {basename}") tested = 0 for test in test_fids: feature = grid_layer.GetFeature(test) if feature is None: raise Exception(f"Feature not found: {test}") test_ds_path = f"/vsimem/test_mem_grid_{uuid4().int}.gpkg" test_ds = mem_driver.CreateDataSource(test_ds_path) test_ds_lyr = test_ds.CreateLayer("test_mem_grid_layer", geom_type=ogr.wkbPolygon, srs=grid_projection) test_ds_lyr.CreateFeature(feature.Clone()) test_ds.SyncToDisk() clipped = internal_clip_raster( test_rast, test_ds_path, adjust_bbox=False, crop_to_geom=True, all_touch=False, ) if clipped is None: raise Exception( "Error while clipping raster. Likely a bad extraction.") ref_image = raster_to_array(clipped, filled=True) image_block = test_array[test] if not np.array_equal(ref_image, image_block): # from matplotlib import pyplot as plt; plt.imshow(ref_image[:,:,0]); plt.show() raise Exception( f"Image {basename} and grid cell did not match..") if verbose == 1: progress(tested, len(test_fids) - 1, "Verifying..") tested += 1 return True
def internal_reproject_raster( raster: Union[str, gdal.Dataset], projection: Union[int, str, gdal.Dataset, ogr.DataSource, osr.SpatialReference], out_path: Optional[str] = None, resample_alg: str = "nearest", copy_if_already_correct: bool = True, overwrite: bool = True, creation_options: list = [], dst_nodata: Union[str, int, float] = "infer", prefix: str = "", postfix: str = "_reprojected", ) -> str: """OBS: Internal. Single output. Reproject a raster(s) to a target coordinate reference system. """ type_check(raster, [str, gdal.Dataset], "raster") type_check( projection, [int, str, gdal.Dataset, ogr.DataSource, osr.SpatialReference], "projection", ) type_check(out_path, [str], "out_path", allow_none=True) type_check(resample_alg, [str], "resample_alg") type_check(copy_if_already_correct, [bool], "copy_if_already_correct") type_check(overwrite, [bool], "overwrite") type_check(creation_options, [list], "creation_options") type_check(dst_nodata, [str, int, float], "dst_nodata") type_check(prefix, [str], "prefix") type_check(postfix, [str], "postfix") raster_list, path_list = ready_io_raster(raster, out_path, overwrite, prefix, postfix) out_name = path_list[0] ref = open_raster(raster_list[0]) metadata = raster_to_metadata(ref) out_creation_options = default_options(creation_options) out_format = path_to_driver_raster(out_name) original_projection = parse_projection(ref) target_projection = parse_projection(projection) if not isinstance(original_projection, osr.SpatialReference): raise Exception("Error while parsing input projection.") if not isinstance(target_projection, osr.SpatialReference): raise Exception("Error while parsing target projection.") if original_projection.IsSame(target_projection): if not copy_if_already_correct: return get_raster_path(ref) src_nodata = metadata["nodata_value"] out_nodata = None if src_nodata is not None: out_nodata = src_nodata else: if dst_nodata == "infer": out_nodata = gdal_nodata_value_from_type( metadata["datatype_gdal_raw"]) elif isinstance(dst_nodata, str): raise TypeError(f"dst_nodata is in a wrong format: {dst_nodata}") else: out_nodata = dst_nodata remove_if_overwrite(out_path, overwrite) reprojected = gdal.Warp( out_name, ref, format=out_format, srcSRS=original_projection, dstSRS=target_projection, resampleAlg=translate_resample_method(resample_alg), creationOptions=out_creation_options, srcNodata=metadata["nodata_value"], dstNodata=out_nodata, multithread=True, ) if reprojected is None: raise Exception(f"Error while reprojecting raster: {raster}") return out_name
def _warp_raster( raster: Union[str, gdal.Dataset], out_path: Optional[str] = None, projection: Optional[Union[int, str, gdal.Dataset, ogr.DataSource, osr.SpatialReference]] = None, clip_geom: Optional[Union[str, ogr.DataSource]] = None, target_size: Optional[Union[Tuple[Number], Number]] = None, target_in_pixels: bool = False, resample_alg: str = "nearest", crop_to_geom: bool = True, all_touch: bool = True, adjust_bbox: bool = True, overwrite: bool = True, creation_options: Union[list, None] = None, src_nodata: Union[str, int, float] = "infer", dst_nodata: Union[str, int, float] = "infer", layer_to_clip: int = 0, prefix: str = "", postfix: str = "_resampled", ) -> str: """WARNING: INTERNAL. DO NOT USE.""" raster_list, path_list = ready_io_raster(raster, out_path, overwrite, prefix, postfix) origin = open_raster(raster_list[0]) out_name = path_list[0] raster_metadata = raster_to_metadata(origin, create_geometry=True) # options warp_options = [] if all_touch: warp_options.append("CUTLINE_ALL_TOUCHED=TRUE") else: warp_options.append("CUTLINE_ALL_TOUCHED=FALSE") origin_projection: osr.SpatialReference = raster_metadata["projection_osr"] origin_extent: ogr.Geometry = raster_metadata["extent_geom_latlng"] target_projection = origin_projection if projection is not None: target_projection = parse_projection(projection) if clip_geom is not None: if is_raster(clip_geom): opened_raster = open_raster(clip_geom) clip_metadata_raster = raster_to_metadata(opened_raster, create_geometry=True) clip_ds = clip_metadata_raster["extent_datasource"] clip_metadata = internal_vector_to_metadata(clip_ds, create_geometry=True) elif is_vector(clip_geom): clip_ds = open_vector(clip_geom) clip_metadata = internal_vector_to_metadata(clip_ds, create_geometry=True) else: if file_exists(clip_geom): raise ValueError(f"Unable to parse clip geometry: {clip_geom}") else: raise ValueError(f"Unable to find clip geometry {clip_geom}") if layer_to_clip > (clip_metadata["layer_count"] - 1): raise ValueError("Requested an unable layer_to_clip.") clip_projection = clip_metadata["projection_osr"] clip_extent = clip_metadata["extent_geom_latlng"] # Fast check: Does the extent of the two inputs overlap? if not origin_extent.Intersects(clip_extent): raise Exception("Clipping geometry did not intersect raster.") # Check if projections match, otherwise reproject target geom. if not target_projection.IsSame(clip_projection): clip_metadata["extent"] = reproject_extent( clip_metadata["extent"], clip_projection, target_projection, ) # The extent needs to be reprojected to the target. # this ensures that adjust_bbox works. x_min_og, y_max_og, x_max_og, y_min_og = reproject_extent( raster_metadata["extent"], origin_projection, target_projection, ) output_bounds = (x_min_og, y_min_og, x_max_og, y_max_og ) # gdal_warp format if crop_to_geom: if adjust_bbox: output_bounds = align_bbox( raster_metadata["extent"], clip_metadata["extent"], raster_metadata["pixel_width"], raster_metadata["pixel_height"], warp_format=True, ) else: x_min_og, y_max_og, x_max_og, y_min_og = clip_metadata[ "extent"] output_bounds = ( x_min_og, y_min_og, x_max_og, y_max_og, ) # gdal_warp format if clip_metadata["layer_count"] > 1: clip_ds = vector_to_memory( clip_ds, memory_path=f"clip_geom_{uuid4().int}.gpkg", layer_to_extract=layer_to_clip, ) elif not isinstance(clip_ds, str): clip_ds = vector_to_memory( clip_ds, memory_path=f"clip_geom_{uuid4().int}.gpkg", ) if clip_ds is None: raise ValueError(f"Unable to parse input clip geom: {clip_geom}") x_res, y_res, x_pixels, y_pixels = raster_size_from_list( target_size, target_in_pixels) out_format = path_to_driver_raster(out_name) out_creation_options = default_options(creation_options) # nodata out_nodata = None if src_nodata is not None: out_nodata = raster_metadata["nodata_value"] else: if dst_nodata == "infer": out_nodata = gdal_nodata_value_from_type( raster_metadata["datatype_gdal_raw"]) else: out_nodata = dst_nodata # Removes file if it exists and overwrite is True. remove_if_overwrite(out_path, overwrite) warped = gdal.Warp( out_name, origin, xRes=x_res, yRes=y_res, width=x_pixels, height=y_pixels, cutlineDSName=clip_ds, outputBounds=output_bounds, format=out_format, srcSRS=origin_projection, dstSRS=target_projection, resampleAlg=translate_resample_method(resample_alg), creationOptions=out_creation_options, warpOptions=warp_options, srcNodata=src_nodata, dstNodata=out_nodata, targetAlignedPixels=False, cropToCutline=False, multithread=True, ) if warped is None: raise Exception(f"Error while warping raster: {raster}") return out_name
def internal_resample_raster( raster: Union[str, gdal.Dataset], target_size: Union[tuple, int, float, str, gdal.Dataset], target_in_pixels: bool = False, out_path: Optional[str] = None, resample_alg: str = "nearest", overwrite: bool = True, creation_options: list = [], dtype=None, dst_nodata: Union[str, int, float] = "infer", prefix: str = "", postfix: str = "_resampled", add_uuid: bool = False, ) -> str: """OBS: Internal. Single output. Reprojects a raster given a target projection. Beware if your input is in latitude and longitude, you'll need to specify the target_size in degrees as well. """ type_check(raster, [str, gdal.Dataset], "raster") type_check(target_size, [tuple, int, float, str, gdal.Dataset], "target_size") type_check(target_in_pixels, [bool], "target_in_pixels") type_check(out_path, [list, str], "out_path", allow_none=True) type_check(resample_alg, [str], "resample_alg") type_check(overwrite, [bool], "overwrite") type_check(creation_options, [list], "creation_options") type_check(dst_nodata, [str, int, float], "dst_nodata") type_check(prefix, [str], "prefix") type_check(postfix, [str], "postfix") raster_list, path_list = ready_io_raster( raster, out_path, overwrite=overwrite, prefix=prefix, postfix=postfix, uuid=add_uuid, ) ref = open_raster(raster_list[0]) metadata = raster_to_metadata(ref) out_name = path_list[0] x_res, y_res, x_pixels, y_pixels = raster_size_from_list( target_size, target_in_pixels) out_creation_options = default_options(creation_options) out_format = path_to_driver_raster(out_name) src_nodata = metadata["nodata_value"] out_nodata = None if src_nodata is not None: out_nodata = src_nodata else: if dst_nodata == "infer": out_nodata = gdal_nodata_value_from_type( metadata["datatype_gdal_raw"]) elif isinstance(dst_nodata, str): raise TypeError(f"dst_nodata is in a wrong format: {dst_nodata}") else: out_nodata = dst_nodata remove_if_overwrite(out_path, overwrite) resampled = gdal.Warp( out_name, ref, width=x_pixels, height=y_pixels, xRes=x_res, yRes=y_res, format=out_format, outputType=translate_datatypes(dtype), resampleAlg=translate_resample_method(resample_alg), creationOptions=out_creation_options, srcNodata=metadata["nodata_value"], dstNodata=out_nodata, multithread=True, ) if resampled is None: raise Exception(f"Error while resampling raster: {out_name}") return out_name
def shift_raster( raster: Union[gdal.Dataset, str], shift: Union[Number, Tuple[Number, Number], List[Number]], out_path: Optional[str] = None, overwrite: bool = True, creation_options: list = [], ) -> Union[gdal.Dataset, str]: """Shifts a raster in a given direction. Returns: An in-memory raster. If an out_path is given the output is a string containing the path to the newly created raster. """ type_check(raster, [list, str, gdal.Dataset], "raster") type_check(shift, [tuple, list], "shift") type_check(out_path, [list, str], "out_path", allow_none=True) type_check(overwrite, [bool], "overwrite") type_check(creation_options, [list], "creation_options") ref = open_raster(raster) metadata = raster_to_metadata(ref) x_shift: float = 0.0 y_shift: float = 0.0 if isinstance(shift, tuple) or isinstance(shift, list): if len(shift) == 1: if is_number(shift[0]): x_shift = float(shift[0]) y_shift = float(shift[0]) else: raise ValueError( "shift is not a number or a list/tuple of numbers.") elif len(shift) == 2: if is_number(shift[0]) and is_number(shift[1]): x_shift = float(shift[0]) y_shift = float(shift[1]) else: raise ValueError("shift is either empty or larger than 2.") elif is_number(shift): x_shift = float(shift) y_shift = float(shift) else: raise ValueError("shift is invalid.") out_name = None out_format = None out_creation_options = [] if out_path is None: raster_name = metadata["basename"] out_name = f"/vsimem/{raster_name}_{uuid4().int}_resampled.tif" out_format = "GTiff" else: out_creation_options = default_options(creation_options) out_name = out_path out_format = path_to_driver_raster(out_path) remove_if_overwrite(out_path, overwrite) driver = gdal.GetDriverByName(out_format) shifted = driver.Create( out_name, # Location of the saved raster, ignored if driver is memory. metadata["width"], # Dataframe width in pixels (e.g. 1920px). metadata["height"], # Dataframe height in pixels (e.g. 1280px). metadata["band_count"], # The number of bands required. metadata["datatype_gdal_raw"], # Datatype of the destination. out_creation_options, ) new_transform = list(metadata["transform"]) new_transform[0] += x_shift new_transform[3] += y_shift shifted.SetGeoTransform(new_transform) shifted.SetProjection(metadata["projection"]) src_nodata = metadata["nodata_value"] for band in range(metadata["band_count"]): origin_raster_band = ref.GetRasterBand(band + 1) target_raster_band = shifted.GetRasterBand(band + 1) target_raster_band.WriteArray(origin_raster_band.ReadAsArray()) target_raster_band.SetNoDataValue(src_nodata) if out_path is not None: shifted = None return out_path else: return shifted
def _clip_raster( raster: Union[str, gdal.Dataset], clip_geom: Union[str, ogr.DataSource, gdal.Dataset], out_path: Optional[str] = None, resample_alg: str = "nearest", crop_to_geom: bool = True, adjust_bbox: bool = True, all_touch: bool = True, overwrite: bool = True, creation_options: list = [], dst_nodata: Union[str, int, float] = "infer", layer_to_clip: int = 0, prefix: str = "", postfix: str = "_clipped", verbose: int = 1, uuid: bool = False, ram: int = 8000, ) -> str: """OBS: Internal. Single output. Clips a raster(s) using a vector geometry or the extents of a raster. """ type_check(raster, [str, gdal.Dataset], "raster") type_check(clip_geom, [str, ogr.DataSource, gdal.Dataset], "clip_geom") type_check(out_path, [str], "out_path", allow_none=True) type_check(resample_alg, [str], "resample_alg") type_check(crop_to_geom, [bool], "crop_to_geom") type_check(adjust_bbox, [bool], "adjust_bbox") type_check(all_touch, [bool], "all_touch") type_check(dst_nodata, [str, int, float], "dst_nodata") type_check(layer_to_clip, [int], "layer_to_clip") type_check(overwrite, [bool], "overwrite") type_check(creation_options, [list], "creation_options") type_check(prefix, [str], "prefix") type_check(postfix, [str], "postfix") type_check(verbose, [int], "verbose") type_check(uuid, [bool], "uuid") _, path_list = ready_io_raster(raster, out_path, overwrite=overwrite, prefix=prefix, postfix=postfix, uuid=uuid) if out_path is not None: if "vsimem" not in out_path: if not os.path.isdir(os.path.split(os.path.normpath(out_path))[0]): raise ValueError( f"out_path folder does not exists: {out_path}") # Input is a vector. if is_vector(clip_geom): clip_ds = open_vector(clip_geom) clip_metadata = internal_vector_to_metadata( clip_ds, process_layer=layer_to_clip) if clip_metadata["layer_count"] > 1: clip_ds = internal_vector_to_memory(clip_ds, layer_to_extract=layer_to_clip) if isinstance(clip_ds, ogr.DataSource): clip_ds = clip_ds.GetName() # Input is a raster (use extent) elif is_raster(clip_geom): clip_metadata = raster_to_metadata(clip_geom, create_geometry=True) clip_metadata["layer_count"] = 1 clip_ds = clip_metadata["extent_datasource"].GetName() else: if file_exists(clip_geom): raise ValueError(f"Unable to parse clip geometry: {clip_geom}") else: raise ValueError(f"Unable to locate clip geometry {clip_geom}") if layer_to_clip > (clip_metadata["layer_count"] - 1): raise ValueError("Requested an unable layer_to_clip.") if clip_ds is None: raise ValueError(f"Unable to parse input clip geom: {clip_geom}") clip_projection = clip_metadata["projection_osr"] clip_extent = clip_metadata["extent"] # options warp_options = [] if all_touch: warp_options.append("CUTLINE_ALL_TOUCHED=TRUE") else: warp_options.append("CUTLINE_ALL_TOUCHED=FALSE") origin_layer = open_raster(raster) raster_metadata = raster_to_metadata(raster) origin_projection = raster_metadata["projection_osr"] origin_extent = raster_metadata["extent"] # Check if projections match, otherwise reproject target geom. if not origin_projection.IsSame(clip_projection): clip_metadata["extent"] = reproject_extent( clip_metadata["extent"], clip_projection, origin_projection, ) # Fast check: Does the extent of the two inputs overlap? if not gdal_bbox_intersects(origin_extent, clip_extent): raise Exception("Geometries did not intersect.") output_bounds = raster_metadata["extent_gdal_warp"] if crop_to_geom: if adjust_bbox: output_bounds = align_bbox( raster_metadata["extent"], clip_metadata["extent"], raster_metadata["pixel_width"], raster_metadata["pixel_height"], warp_format=True, ) else: output_bounds = clip_metadata["extent_gdal_warp"] # formats out_name = path_list[0] out_format = path_to_driver_raster(out_name) out_creation_options = default_options(creation_options) # nodata src_nodata = raster_metadata["nodata_value"] out_nodata = None if src_nodata is not None: out_nodata = src_nodata else: if dst_nodata == "infer": out_nodata = gdal_nodata_value_from_type( raster_metadata["datatype_gdal_raw"]) elif dst_nodata is None: out_nodata = None elif isinstance(dst_nodata, (int, float)): out_nodata = dst_nodata else: raise ValueError(f"Unable to parse nodata_value: {dst_nodata}") # Removes file if it exists and overwrite is True. remove_if_overwrite(out_path, overwrite) if verbose == 0: gdal.PushErrorHandler("CPLQuietErrorHandler") clipped = gdal.Warp( out_name, origin_layer, format=out_format, resampleAlg=translate_resample_method(resample_alg), targetAlignedPixels=False, outputBounds=output_bounds, xRes=raster_metadata["pixel_width"], yRes=raster_metadata["pixel_height"], cutlineDSName=clip_ds, cropToCutline= False, # GDAL does this incorrectly when targetAlignedPixels is True. creationOptions=out_creation_options, warpMemoryLimit=ram, warpOptions=warp_options, srcNodata=raster_metadata["nodata_value"], dstNodata=out_nodata, multithread=True, ) if verbose == 0: gdal.PopErrorHandler() if clipped is None: raise Exception("Error while clipping raster.") return out_name
def raster_to_grid( raster: Union[str, gdal.Dataset], grid: Union[str, ogr.DataSource], out_dir: str, use_field: Optional[str] = None, generate_vrt: bool = True, overwrite: bool = True, process_layer: int = 0, creation_options: list = [], verbose: int = 1, ) -> Union[List[str], Tuple[Optional[List[str]], Optional[str]]]: """Clips a raster to a grid. Generate .vrt. Returns: The filepath for the newly created raster. """ type_check(raster, [str, gdal.Dataset], "raster") type_check(grid, [str, ogr.DataSource], "grid") type_check(out_dir, [str], "out_dir") type_check(overwrite, [bool], "overwrite") type_check(process_layer, [int], "process_layer") type_check(creation_options, [list], "creation_options") type_check(verbose, [int], "verbose") use_grid = open_vector(grid) grid_metadata = internal_vector_to_metadata(use_grid) raster_metadata = raster_to_metadata(raster, create_geometry=True) # Reproject raster if necessary. if not raster_metadata["projection_osr"].IsSame(grid_metadata["projection_osr"]): use_grid = reproject_vector(grid, raster_metadata["projection_osr"]) grid_metadata = internal_vector_to_metadata(use_grid) if not isinstance(grid_metadata, dict): raise Exception("Error while parsing metadata.") # Only use the polygons in the grid that intersect the extent of the raster. use_grid = intersect_vector(use_grid, raster_metadata["extent_datasource"]) ref = open_raster(raster) use_grid = open_vector(use_grid) layer = use_grid.GetLayer(process_layer) feature_count = layer.GetFeatureCount() raster_extent = raster_metadata["extent_ogr"] filetype = path_to_ext(raster) name = raster_metadata["name"] geom_type = grid_metadata["layers"][process_layer]["geom_type_ogr"] if use_field is not None: if use_field not in grid_metadata["layers"][process_layer]["field_names"]: names = grid_metadata["layers"][process_layer]["field_names"] raise ValueError( f"Requested field not found. Fields available are: {names}" ) generated = [] # For the sake of good reporting - lets first establish how many features intersect # the raster. if verbose: print("Finding intersections.") intersections = 0 for _ in range(feature_count): feature = layer.GetNextFeature() geom = feature.GetGeometryRef() if not ogr_bbox_intersects(raster_extent, geom.GetEnvelope()): continue intersections += 1 layer.ResetReading() if verbose: print(f"Found {intersections} intersections.") if intersections == 0: print("Warning: Found 0 intersections. Returning empty list.") return ([], None) # TODO: Replace this in gdal. 3.1 driver = ogr.GetDriverByName("Esri Shapefile") clipped = 0 for _ in range(feature_count): feature = layer.GetNextFeature() geom = feature.GetGeometryRef() if not ogr_bbox_intersects(raster_extent, geom.GetEnvelope()): continue if verbose == 1: progress(clipped, intersections - 1, "clip_grid") fid = feature.GetFID() test_ds_path = f"/vsimem/grid_{uuid4().int}.shp" test_ds = driver.CreateDataSource(test_ds_path) test_ds_lyr = test_ds.CreateLayer( "mem_layer_grid", geom_type=geom_type, srs=raster_metadata["projection_osr"], ) test_ds_lyr.CreateFeature(feature.Clone()) test_ds.FlushCache() out_name = None if use_field is not None: out_name = f"{out_dir}{feature.GetField(use_field)}{filetype}" else: out_name = f"{out_dir}{name}_{fid}{filetype}" clip_raster( ref, test_ds_path, out_path=out_name, adjust_bbox=True, crop_to_geom=True, all_touch=False, postfix="", prefix="", creation_options=default_options(creation_options), verbose=0, ) generated.append(out_name) clipped += 1 if generate_vrt: vrt_name = f"{out_dir}{name}.vrt" stack_rasters_vrt(generated, vrt_name, seperate=False) return (generated, vrt_name) return generated