buteo.raster.grid

Create grids from rasters.

Cut rasters to grids. Use vectors or rasters as grids.

  1"""### Create grids from rasters.  ###
  2
  3Cut rasters to grids. Use vectors or rasters as grids.
  4"""
  5
  6# TODO: Verify this function and create more grid options.
  7
  8# Standard library
  9from uuid import uuid4
 10from typing import Union, Optional, List
 11
 12# External
 13from osgeo import gdal, ogr
 14
 15# Internal
 16from buteo.utils import utils_base, utils_gdal, utils_bbox, utils_path
 17from buteo.raster import core_raster, core_stack
 18from buteo.raster.clip import _raster_clip
 19from buteo.vector import core_vector
 20from buteo.vector.intersect import _vector_intersect
 21from buteo.vector.reproject import _vector_reproject
 22from buteo.vector.metadata import _vector_to_metadata
 23
 24
 25
 26def raster_to_grid(
 27    raster: Union[str, gdal.Dataset],
 28    grid: Union[str, ogr.DataSource],
 29    out_dir: str,
 30    *,
 31    use_field: bool = None,
 32    generate_vrt: bool = True,
 33    overwrite: bool = True,
 34    process_layer: int = 0,
 35    creation_options: Optional[List[str]] = None,
 36    verbose: int = 0,
 37) -> str:
 38    """Clips a raster to a grid. Generates .vrt.
 39
 40    Parameters
 41    ----------
 42    raster : Union[str, gdal.Dataset]
 43        The input raster.
 44
 45    grid : Union[str, ogr.DataSource]
 46        The grid to use.
 47
 48    out_dir : str
 49        The output directory.
 50
 51    use_field : bool, optional
 52        A field to use to name the grid cells, default: None.
 53
 54    generate_vrt : bool, optional
 55        If True, the output raster will be a .vrt, default: True.
 56
 57    overwrite : bool, optional
 58        If True, the output raster will be overwritten, default: True.
 59
 60    process_layer : int, optional
 61        The layer from the grid to process, default: 0.
 62
 63    creation_options : Optional[List[str]], optional
 64        Creation options for the output raster, default: None.
 65
 66    verbose : int, optional
 67        The verbosity level, default: 0.
 68
 69    Returns
 70    -------
 71    str
 72        The file path for the newly created raster.
 73    """
 74    utils_base._type_check(raster, [str, gdal.Dataset], "raster")
 75    utils_base._type_check(grid, [str, ogr.DataSource], "grid")
 76    utils_base._type_check(out_dir, [str], "out_dir")
 77    utils_base._type_check(overwrite, [bool], "overwrite")
 78    utils_base._type_check(process_layer, [int], "process_layer")
 79    utils_base._type_check(creation_options, [[str], None], "creation_options")
 80    utils_base._type_check(verbose, [int], "verbose")
 81
 82    use_grid = core_vector.vector_open(grid)
 83    grid_metadata = _vector_to_metadata(use_grid)
 84    raster_metadata = core_raster._get_basic_metadata_raster(raster)
 85
 86    # Reproject raster if necessary.
 87    if not raster_metadata["projection_osr"].IsSame(grid_metadata["projection_osr"]):
 88        use_grid = _vector_reproject(grid, raster_metadata["projection_osr"])
 89        grid_metadata = _vector_to_metadata(use_grid)
 90
 91        if not isinstance(grid_metadata, dict):
 92            raise RuntimeError("Error while parsing metadata.")
 93
 94    # Only use the polygons in the grid that intersect the extent of the raster.
 95    use_grid = _vector_intersect(use_grid, raster_metadata["extent_datasource"]())
 96
 97    ref = core_raster._raster_open(raster)
 98    use_grid = core_vector.vector_open(use_grid)
 99
100    layer = use_grid.GetLayer(process_layer)
101    feature_count = layer.GetFeatureCount()
102    raster_extent = raster_metadata["bbox"]
103    filetype = utils_path._get_ext_from_path(raster)
104    name = raster_metadata["name"]
105    geom_type = grid_metadata["layers"][process_layer]["geom_type_ogr"]
106
107    if use_field is not None:
108        if use_field not in grid_metadata["layers"][process_layer]["field_names"]:
109            names = grid_metadata["layers"][process_layer]["field_names"]
110            raise ValueError(
111                f"Requested field not found. Fields available are: {names}"
112            )
113
114    generated = []
115
116    # For the sake of good reporting - lets first establish how many features intersect
117    # the raster.
118
119    if verbose:
120        print("Finding intersections.")
121
122    intersections = 0
123    for _ in range(feature_count):
124        feature = layer.GetNextFeature()
125        geom = feature.GetGeometryRef()
126
127        if not utils_bbox._check_bboxes_intersect(raster_extent, geom.GetEnvelope()):
128            continue
129
130        intersections += 1
131
132    layer.ResetReading()
133
134    if verbose:
135        print(f"Found {intersections} intersections.")
136
137    if intersections == 0:
138        print("Warning: Found 0 intersections. Returning empty list.")
139        return ([], None)
140
141    driver = ogr.GetDriverByName("GPKG")
142
143    clipped = 0
144    for _ in range(feature_count):
145
146        feature = layer.GetNextFeature()
147        geom = feature.GetGeometryRef()
148
149        if not utils_bbox._check_bboxes_intersect(raster_extent, geom.GetEnvelope()):
150            continue
151
152        if verbose == 1:
153            utils_base.progress(clipped, intersections - 1, "clip_grid")
154
155        fid = feature.GetFID()
156
157        test_ds_path = f"/vsimem/grid_{uuid4().int}.gpkg"
158        test_ds = driver.CreateDataSource(test_ds_path)
159        test_ds_lyr = test_ds.CreateLayer(
160            "mem_layer_grid",
161            geom_type=geom_type,
162            srs=raster_metadata["projection_osr"],
163        )
164        test_ds_lyr.CreateFeature(feature.Clone())
165        test_ds.FlushCache()
166
167        out_name = None
168
169        if use_field is not None:
170            out_name = f"{out_dir}{feature.GetField(use_field)}{filetype}"
171        else:
172            out_name = f"{out_dir}{name}_{fid}{filetype}"
173
174        _raster_clip(
175            ref,
176            test_ds_path,
177            out_path=out_name,
178            adjust_bbox=True,
179            crop_to_geom=True,
180            all_touch=False,
181            suffix="",
182            prefix="",
183            creation_options=utils_gdal._get_default_creation_options(creation_options),
184            verbose=0,
185        )
186
187        generated.append(out_name)
188        clipped += 1
189
190    if generate_vrt:
191        vrt_name = f"{out_dir}{name}.vrt"
192
193        core_stack.raster_stack_vrt_list(generated, vrt_name, separate=False)
194
195        return (generated, vrt_name)
196
197    return generated
def raster_to_grid( raster: Union[str, osgeo.gdal.Dataset], grid: Union[str, osgeo.ogr.DataSource], out_dir: str, *, use_field: bool = None, generate_vrt: bool = True, overwrite: bool = True, process_layer: int = 0, creation_options: Optional[List[str]] = None, verbose: int = 0) -> str:
 27def raster_to_grid(
 28    raster: Union[str, gdal.Dataset],
 29    grid: Union[str, ogr.DataSource],
 30    out_dir: str,
 31    *,
 32    use_field: bool = None,
 33    generate_vrt: bool = True,
 34    overwrite: bool = True,
 35    process_layer: int = 0,
 36    creation_options: Optional[List[str]] = None,
 37    verbose: int = 0,
 38) -> str:
 39    """Clips a raster to a grid. Generates .vrt.
 40
 41    Parameters
 42    ----------
 43    raster : Union[str, gdal.Dataset]
 44        The input raster.
 45
 46    grid : Union[str, ogr.DataSource]
 47        The grid to use.
 48
 49    out_dir : str
 50        The output directory.
 51
 52    use_field : bool, optional
 53        A field to use to name the grid cells, default: None.
 54
 55    generate_vrt : bool, optional
 56        If True, the output raster will be a .vrt, default: True.
 57
 58    overwrite : bool, optional
 59        If True, the output raster will be overwritten, default: True.
 60
 61    process_layer : int, optional
 62        The layer from the grid to process, default: 0.
 63
 64    creation_options : Optional[List[str]], optional
 65        Creation options for the output raster, default: None.
 66
 67    verbose : int, optional
 68        The verbosity level, default: 0.
 69
 70    Returns
 71    -------
 72    str
 73        The file path for the newly created raster.
 74    """
 75    utils_base._type_check(raster, [str, gdal.Dataset], "raster")
 76    utils_base._type_check(grid, [str, ogr.DataSource], "grid")
 77    utils_base._type_check(out_dir, [str], "out_dir")
 78    utils_base._type_check(overwrite, [bool], "overwrite")
 79    utils_base._type_check(process_layer, [int], "process_layer")
 80    utils_base._type_check(creation_options, [[str], None], "creation_options")
 81    utils_base._type_check(verbose, [int], "verbose")
 82
 83    use_grid = core_vector.vector_open(grid)
 84    grid_metadata = _vector_to_metadata(use_grid)
 85    raster_metadata = core_raster._get_basic_metadata_raster(raster)
 86
 87    # Reproject raster if necessary.
 88    if not raster_metadata["projection_osr"].IsSame(grid_metadata["projection_osr"]):
 89        use_grid = _vector_reproject(grid, raster_metadata["projection_osr"])
 90        grid_metadata = _vector_to_metadata(use_grid)
 91
 92        if not isinstance(grid_metadata, dict):
 93            raise RuntimeError("Error while parsing metadata.")
 94
 95    # Only use the polygons in the grid that intersect the extent of the raster.
 96    use_grid = _vector_intersect(use_grid, raster_metadata["extent_datasource"]())
 97
 98    ref = core_raster._raster_open(raster)
 99    use_grid = core_vector.vector_open(use_grid)
100
101    layer = use_grid.GetLayer(process_layer)
102    feature_count = layer.GetFeatureCount()
103    raster_extent = raster_metadata["bbox"]
104    filetype = utils_path._get_ext_from_path(raster)
105    name = raster_metadata["name"]
106    geom_type = grid_metadata["layers"][process_layer]["geom_type_ogr"]
107
108    if use_field is not None:
109        if use_field not in grid_metadata["layers"][process_layer]["field_names"]:
110            names = grid_metadata["layers"][process_layer]["field_names"]
111            raise ValueError(
112                f"Requested field not found. Fields available are: {names}"
113            )
114
115    generated = []
116
117    # For the sake of good reporting - lets first establish how many features intersect
118    # the raster.
119
120    if verbose:
121        print("Finding intersections.")
122
123    intersections = 0
124    for _ in range(feature_count):
125        feature = layer.GetNextFeature()
126        geom = feature.GetGeometryRef()
127
128        if not utils_bbox._check_bboxes_intersect(raster_extent, geom.GetEnvelope()):
129            continue
130
131        intersections += 1
132
133    layer.ResetReading()
134
135    if verbose:
136        print(f"Found {intersections} intersections.")
137
138    if intersections == 0:
139        print("Warning: Found 0 intersections. Returning empty list.")
140        return ([], None)
141
142    driver = ogr.GetDriverByName("GPKG")
143
144    clipped = 0
145    for _ in range(feature_count):
146
147        feature = layer.GetNextFeature()
148        geom = feature.GetGeometryRef()
149
150        if not utils_bbox._check_bboxes_intersect(raster_extent, geom.GetEnvelope()):
151            continue
152
153        if verbose == 1:
154            utils_base.progress(clipped, intersections - 1, "clip_grid")
155
156        fid = feature.GetFID()
157
158        test_ds_path = f"/vsimem/grid_{uuid4().int}.gpkg"
159        test_ds = driver.CreateDataSource(test_ds_path)
160        test_ds_lyr = test_ds.CreateLayer(
161            "mem_layer_grid",
162            geom_type=geom_type,
163            srs=raster_metadata["projection_osr"],
164        )
165        test_ds_lyr.CreateFeature(feature.Clone())
166        test_ds.FlushCache()
167
168        out_name = None
169
170        if use_field is not None:
171            out_name = f"{out_dir}{feature.GetField(use_field)}{filetype}"
172        else:
173            out_name = f"{out_dir}{name}_{fid}{filetype}"
174
175        _raster_clip(
176            ref,
177            test_ds_path,
178            out_path=out_name,
179            adjust_bbox=True,
180            crop_to_geom=True,
181            all_touch=False,
182            suffix="",
183            prefix="",
184            creation_options=utils_gdal._get_default_creation_options(creation_options),
185            verbose=0,
186        )
187
188        generated.append(out_name)
189        clipped += 1
190
191    if generate_vrt:
192        vrt_name = f"{out_dir}{name}.vrt"
193
194        core_stack.raster_stack_vrt_list(generated, vrt_name, separate=False)
195
196        return (generated, vrt_name)
197
198    return generated

Clips a raster to a grid. Generates .vrt.

Parameters

raster : Union[str, gdal.Dataset] The input raster.

grid : Union[str, ogr.DataSource] The grid to use.

out_dir : str The output directory.

use_field : bool, optional A field to use to name the grid cells, default: None.

generate_vrt : bool, optional If True, the output raster will be a .vrt, default: True.

overwrite : bool, optional If True, the output raster will be overwritten, default: True.

process_layer : int, optional The layer from the grid to process, default: 0.

creation_options : Optional[List[str]], optional Creation options for the output raster, default: None.

verbose : int, optional The verbosity level, default: 0.

Returns

str The file path for the newly created raster.