Geo-module¶
Introduction¶
The pywatemsedem package makes use of a number of GDAL and SAGA command line
operators to process raster data (for reasons of processing speed). In order
to cope with this file-based system, but still make use of numpy arrays for
processing in Python, a pywatemsedem.geo.rasters.RasterFile and
pywatemsedem.geo.rasters.RasterMemory class is
implemented. The aim of these engines is to couple raster file/numpy arrays to
specific raster properties. Second, this engine aims to perform simple
clipping and masking for input data. Third, this engine makes use of a class
pywatemsedem.geo.rasterproperties.RasterProperties, which aims to enable
easy switch between raster geographic references of gdal and rasterio,
respectively named gdal_profile and rasterio_profile.
The aim of this tutorial is to examplify the use of the
pywatemsedem.geo.rasterproperties.RasterProperties
and the pywatemsedem.geo.rasters.RasterFile class. Note that the example
files in this tutorial can be found in pywatemsedem/pywatemsedem/test/geo/data.
For this example, we assume we have a raster available which defines the geographical boundaries, the resolution (20 m) and coordinate system (in this case Lambert72, i.e. EPSG:31370) of your model:
Defining the raster properties for this raster can be done by:
# imports
from pywatemsedem.geo.utils import load_raster
from pywatemsedem.geo.rasterproperties import RasterProperties
from pathlib import Path
# input file
input_folder = Path(r"$YOURINPUTFOLDER")
file_path = input_folder / "example_input_raster.tif"
# load raster and rasterio profile
arr, rasterio_profile = load_raster(file_path)
# define rasterproperties with rasterio profile
rp = RasterProperties.from_rasterio(rasterio_profile)
print(f"bounds: {rp.bounds}")
print(f"epsg: {rp.epsg}")
print(f"resolution: {rp.resolution}")
print(f"nodata: {rp.nodata}")
The instance rp of the class
pywatemsedem.geo.rasterproperties.RasterProperties can now be used te
define the extent of other rasters with the help of the class
pywatemsedem.geo.rasters.RasterFile.
Yet, before we do so, we first want to narrow our spatial domain by clipping
a part of the input raster. We do this by defining left x boundary, lower y
boundary, right x boundary and upper y boundary by the values
162300, 165760, 167560 and 169520 respectively (keeping in mind the coordinate
system Lambert72 and a resolution of 20 m):
bounds = [162300, 165760, 167560, 169520]
resolution = 20
nodata= -9999
epsg = 31370
rp = RasterProperties(bounds, resolution, nodata, epsg)
print(f"bounds: {rp.bounds}")
print(f"epsg: {rp.epsg}")
print(f"resolution: {rp.resolution}")
print(f"nodata: {rp.nodata}")
Rasters¶
We have now predefined our geospatial context, and we want to clip our input
according to this definition using
pywatemsedem.geo.rasters.RasterFile-engine:
# imports
from pywatemsedem.geo.rasters import RasterFile
# clip
raster = RasterFile(file_path, rp=rp)
# rp (and properties) is an attribute of the raster
print(raster.rp.bounds)
Which results to:
Do note that if no pywatemsedem.geo.rasterproperties.RasterProperties
rp is defined, no clipping will occur:
raster = RasterFile(file_path)
print(raster.rp.bounds)
And in addition, note that using incorrect bounds as follows:
rp_ = RasterProperties([230, 760, 560, 1000], 20, -9999, 31370)
raster = RasterFile(file_path, rp=rp_)
will lead to an error:
Error
Clipped output raster is empty. Make sure your input raster covers your defined spatial extent (bounds: [230, 760, 560, 1000], resolution: 20, espg: EPSG:31370).
Masking with a (1, 0)-array is also available. The ones indicate no masking, the zeros indicate masking.
from pywatemsedem.geo.utils import vct_to_rst_field
rst_mask = Path(input_folder) / "mask.tif"
mask = RasterFile(rst_mask)
Masking can easily be done as follows:
raster = RasterFile(file_path, arr_mask=mask.arr)
or
raster = RasterFile(file_path)
raster.mask(mask.arr)
We want to write our result to disk (note that the raster format should be defined):
raster.write("output.tif",format="tiff)
You can write it to an idrisi raster:
raster.write("output.rst",format="idrisis")
You can also raise an error when format and extension do not overlap:
raster.write("output.rst",format="tiff")
Error
Can not write file (‘output.rst’) in format ‘tiff’ with ‘.rst’ extension.
Or make a nice plot the result while ignoring nodata values:
raster.plot(nodata=nodata)
Our result should look like:
Besides static plots, also interactive plots can be made as follows:
raster.hv_plot(nodata = nodata)
Remark that coordinate system has changed to WGS 84 (EPSG:4326). The interactive features (such as zooming, hovering…) are only accessible in a Jupyter Notebook environment.
Note
In order to use the hv_plot functionality, one has to install hvplot and geoviews. See installation page.
The pywatemsedem.geo.rasters.RasterMemory class can be used in a
similar way:
from pywatemsedem.geo.rasters import RasterMemory
arr, profile = load_raster(file_path)
raster = RasterMemory(arr, RasterProperties.from_rasterio(profile))
raster.mask(mask.arr)
Do note that clipping in pywatemsedem.geo.rasters.RasterMemory is not
implemented in the current version.
Vectors¶
Similar to pywatemsedem.geo.rasters.RasterFile, one can make use
pywatemsedem.geo.vectors.VectorFile to define vector data:
from pywatemsedem.geo.vectors import VectorFile
input_folder = Path(r"$YOURINPUTFOLDER")
file_path = input_folder / "Wlas_langegracht.shp"
vector = VectorFile(file_path)
vector.plot()
and clip:
input_folder = Path(r"$YOURINPUTFOLDER")
file_path = input_folder / "Wlas_langegracht.shp"
file_path_clip = input_folder / "catchm_langegracht.shp"
vector = VectorFile(file_path,vct_clip=file_path_clip)
vector.plot()
with the option to rasterize to a pywatemsedem.geo.rasters.RasterFile-class
(using the mask as reference raster):
input_folder = Path(r"$YOURINPUTFOLDER")
reference_raster = input_folder / "mask.tif"
raster = vector.rasterize(reference_raster)
A numpy array is returned.