Raster Preprocessing¶
Some pfdf routines requires multiple rasters as input. When this is the case, the routine will require each raster to have the same shape, CRS and transform. Other routines require a raster to fall within a valid data range. In reality, most datasets are not this clean, and so Raster objects support a number of preprocessing commands to help meet these requirements.
Note
The commands in this section usually alter an existing raster in-place, rather than returning a new Raster object.
Tip
Read the Preprocessing Tutorial for in-depth examples of these commands.
buffer¶
Use the buffer command to add NoData pixels to the edges of a raster for a specified distance:
>>> # Create a Raster from a fire perimeter
>>> perimeter = Raster.from_polygons('perimeter.shp')
>>> perimeter.resolution
(10, 10)
>>> perimeter.shape
(3000, 2500)
>>> # Buffer the perimeter by 1000 meters
>>> perimeter.buffer(distance=1000)
>>> perimeter.shape
(3200, 2700)
Note that buffering distance is interpreted in meters by default. Use the units
option to specify the buffer in other units instead:
# Buffer by 12 pixels
perimeter.buffer(distance=12, units="pixels")
# Buffer by 1000 CRS units
perimeter.buffer(distance=1000, units="base")
# Other unit options
perimeter.buffer(distance=1, units="kilometers")
perimeter.buffer(distance=1000, units="feet")
perimeter.buffer(distance=1000, units="miles")
reproject¶
Use the reproject method to reproject a Raster to the same CRS, resolution, and alignment of another (template) Raster:
>>> # The CRS of the DEM is 26911
>>> dem = Raster('dem.tif')
>>> print(dem.CRS)
EPSG:26911
>>> # The CRS of the dNBR is 4326
>>> dnbr = Raster('dnbr.tif')
>>> print(dnbr.CRS)
EPSG:4326
>>> # Reprojects the dNBR to 26911
>>> dnbr.reproject(template=dem)
>>> print(dnbr.CRS)
EPSG:26911
Alternatively, use the crs
and/or transform
options to reproject to specific spatial metadata:
# Reproject to an explicit CRS
dnbr.reproject(crs=26910)
# Reproject to an explicit transform
dnbr.reproject(transform=(5,-5,100,0))
By default, this method will use nearest-neighbor interpolation to reproject the raster. Use the resampling
option to select a different algorithm:
# Uses bilinear resampling
dnbr.reproject(template=dem, resampling='bilinear')
Refer to the reproject API for a complete list of supported algorithms.
clip¶
Use the clip command to match a raster’s bounds to the bounds of a second raster:
>>> # The DEM's spatial bounds
>>> dem = Raster('dem.tif')
>>> dem.bounds
BoundingBox(left=0, bottom=0, right=100, top=100)
>>> # The dNBR has different bounds
>>> dnbr = Raster('dnbr.tif')
>>> dnbr.bounds
BoundingBox(left=20, bottom=20, right=150, top=150)
>>> # Clip the dNBR to the bounds of the DEM
>>> dnbr.clip(bounds=dem)
>>> dnbr.bounds
BoundingBox(left=0, bottom=0, right=100, top=100)
Alternatively, you can clip the raster to a known bounding box:
bounds = {'left': -124, 'right': -121, 'bottom': 30, 'top': 33, 'crs': 4326}
dnbr.clip(bounds)
Note that if a raster is clipped outside its initial bounds, then the exterior pixels will be filled with NoData.
set_range¶
Use the set_range method to constrain a dataset to a valid data range:
>>> # A raw dNBR dataset has a large range of data values
>>> import numpy as np
>>> dnbr = Raster('dnbr.tif')
>>> np.min(dnbr.values)
-9000
>>> np.max(dnbr.max)
3520
>>> # Constrain the dNBR to an expected data range
>>> dnbr.set_range(min=-1000, max=1000)
>>> np.min(dnbr.values)
-1000
>>> np.max(dnbr.values)
1000
By default, out-of-range pixels are set to the value of the nearest bound. Use the fill
option to replace these pixels with NoData instead:
# Replaces out-of-range pixels with NoData values
dnbr.set_range(min=-1000, max=1000, fill=True)
When fill=True
, you can also use the exclude_bounds
option to indicate that the bounds are excluded from the valid range. In this case, pixels exactly matching one of the bounds are also replaced with NoData. For example:
# Enforce strictly positive values (Replace 0 with NoData)
kf.set_range(min=0, fill=True, exclude_bounds=True)
find¶
Use the find method to locate raster pixels that match the indicated data values This command is particularly useful for building terrain masks from existing vegetation type (EVT) datasets:
>>> # 7292 is sometimes used to classify a pixel as open water
>>> evt = Raster('evt.tif')
>>> iswater = evt.find(7292)
>>> print(iswater.dtype)
bool
>>> # These values are used to classify human-developed terrain and roads
>>> development = [7296, 7297, 7298, 7299, 7300]
>>> isdeveloped = evt.find(development)
>>> print(isdeveloped.dtype)
bool
Note
Unlike the other preprocessing routines, this command produces a new Raster as output.