Source code for geographer.cutters.single_raster_cutter_bbox
"""SingleRasterCutter that extracts pre defined bboxes from a raster."""from__future__importannotationsimportloggingfrompathlibimportPathfromtypingimportAnyimportgeopandasasgpdimportrasterioasriofromgeopandasimportGeoDataFramefrompydanticimportPrivateAttr,field_validatorfromrasterio.windowsimportWindow,from_boundsfromshapely.geometryimportboxfromgeographer.connectorimportConnectorfromgeographer.cutters.single_raster_cutter_baseimportSingleRasterCutterfromgeographer.cutters.type_aliasesimportRasterSizelogger=logging.getLogger(__name__)def_correct_window_offset(offset:int|float,size:int|float,new_size:int)->int:center=offset+size/2returnint(center-new_size/2)
[docs]classSingleRasterCutterFromBBoxes(SingleRasterCutter):"""SingleRasterCutter that extracts pre defined bboxes from a raster. The new size of the rasters must be specified as it is used to ensure a standardised output. """new_raster_size:RasterSizebbox_geojson_path:Path_bboxes_df:GeoDataFrame=PrivateAttr()def__init__(self,**data)->None:"""Initialize a SingleRasterCutterFromBBoxes. Args: new_raster_size: size of new raster bbox_geojson_path: path to geojson file containing the bboxes """super().__init__(**data)self._bboxes_df=gpd.read_file(self.bbox_geojson_path)@field_validator("bbox_geojson_path")defpath_points_to_geojson(cls,value:Path):"""Validate path exists and points to geojson."""ifvalue.suffix!=".geojson":raiseValueError("Path should point to .geojson file")ifnotvalue.is_file():raiseFileNotFoundError(f".geojson file does not exist: {value}")returnvalue@field_validator("new_raster_size")defnew_raster_size_type_correctness(cls,value:RasterSize)->RasterSize:"""Validate new_raster_size has correct type."""is_int:bool=isinstance(value,int)is_pair_of_ints:bool=(isinstance(value,tuple)andlen(value)==2andall(isinstance(entry,int)forentryinvalue))ifnot(is_intoris_pair_of_ints):raiseTypeError("new_raster_size needs to be an integer or a pair of integers!")returnvalue@field_validator("new_raster_size")defnew_raster_size_side_lengths_must_be_positive(cls,value:RasterSize)->RasterSize:"""Validate new_raster_size side lengths are positive."""ifisinstance(value,tuple)andnotall(val>0forvalinvalue):logger.error("new_raster_size: need positive side length(s)")raiseValueError("new_raster_size: need positive side length(s)")elifisinstance(value,int)andvalue<=0:logger.error("new_raster_size: need positive side length(s)")raiseValueError("new_raster_size: need positive side length(s)")returnvalue@propertydefnew_raster_size_rows(self)->int:"""Return number of rows of new raster size."""ifisinstance(self.new_raster_size,tuple):returnself.new_raster_size[0]else:returnself.new_raster_size@propertydefnew_raster_size_cols(self)->int:"""Return number of columns of new raster size."""ifisinstance(self.new_raster_size,tuple):returnself.new_raster_size[1]else:returnself.new_raster_sizedef_get_windows_transforms_raster_names(self,source_raster_name:str,source_connector:Connector,target_connector:Connector|None=None,new_rasters_dict:dict|None=None,**kwargs:Any,)->list[str]:source_raster_path=source_connector.rasters_dir/source_raster_namewithrio.open(source_raster_path)assrc:raster_bounds=box(*src.bounds)bounding_boxes=self.bounding_boxes.to_crs(src.crs)bounding_boxes=bounding_boxes.loc[bounding_boxes.geometry.within(raster_bounds)]windows_transforms_raster_names=[]fori,geometryinenumerate(bounding_boxes.geometry):initial_window=from_bounds(*geometry.bounds,src.transform)new_col_off=_correct_window_offset(initial_window.col_off,initial_window.width,self.new_raster_size_cols,)new_row_off=_correct_window_offset(initial_window.row_off,initial_window.height,self.new_raster_size_rows,)window=Window(new_col_off,new_row_off,self.new_raster_size_cols,self.new_raster_size_rows,)window_transform=src.window_transform(window)new_raster_name=f"{Path(source_raster_name).stem}_{i}.tif"windows_transforms_raster_names.append((window,window_transform,new_raster_name))returnwindows_transforms_raster_names