Common raster operations#
To be updated
Creating a raster mosaic with rioxarray
#
Quite often you need to merge multiple raster files together and create a raster mosaic
. This can be done easily with the merge_datasets()
-function in rioxarray
.
Here, we will create a mosaic based on DEM files (altogether 4 files) covering Kilimanjaro region in Tanzania. First we will read elevation data from a S3 bucket for Kilimanjaro region in Africa.
import xarray as xr
import os
import rioxarray as rxr
from rioxarray.merge import merge_datasets
# S3 bucket containing the data
bucket = "https://a3s.fi/swift/v1/AUTH_0914d8aff9684df589041a759b549fc2/PythonGIS"
# Generate urls for the elevation files
urls = [
os.path.join(bucket, "elevation/kilimanjaro/ASTGTMV003_S03E036_dem.tif"),
os.path.join(bucket, "elevation/kilimanjaro/ASTGTMV003_S03E037_dem.tif"),
os.path.join(bucket, "elevation/kilimanjaro/ASTGTMV003_S04E036_dem.tif"),
os.path.join(bucket, "elevation/kilimanjaro/ASTGTMV003_S04E037_dem.tif"),
]
# Read the files
datasets = [
xr.open_dataset(url, engine="rasterio").squeeze("band", drop=True) for url in urls
]
Investigate how our data looks like:
datasets[0]
<xarray.Dataset> Dimensions: (x: 3601, y: 3601) Coordinates: * x (x) float64 36.0 36.0 36.0 36.0 36.0 ... 37.0 37.0 37.0 37.0 * y (y) float64 -2.0 -2.0 -2.001 -2.001 ... -2.999 -2.999 -3.0 -3.0 spatial_ref int64 0 Data variables: band_data (y, x) float32 ...
- x: 3601
- y: 3601
- x(x)float6436.0 36.0 36.0 ... 37.0 37.0 37.0
array([36. , 36.000278, 36.000556, ..., 36.999444, 36.999722, 37. ])
- y(y)float64-2.0 -2.0 -2.001 ... -3.0 -3.0
array([-2. , -2.000278, -2.000556, ..., -2.999444, -2.999722, -3. ])
- spatial_ref()int64...
- crs_wkt :
- GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AXIS["Latitude",NORTH],AXIS["Longitude",EAST],AUTHORITY["EPSG","4326"]]
- semi_major_axis :
- 6378137.0
- semi_minor_axis :
- 6356752.314245179
- inverse_flattening :
- 298.257223563
- reference_ellipsoid_name :
- WGS 84
- longitude_of_prime_meridian :
- 0.0
- prime_meridian_name :
- Greenwich
- geographic_crs_name :
- WGS 84
- grid_mapping_name :
- latitude_longitude
- spatial_ref :
- GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AXIS["Latitude",NORTH],AXIS["Longitude",EAST],AUTHORITY["EPSG","4326"]]
- GeoTransform :
- 35.9998611111111 0.000277777777777778 0.0 -1.99986111111111 0.0 -0.000277777777777778
array(0)
- band_data(y, x)float32...
- long_name :
- Band 1
[12967201 values with dtype=float32]
Visualize the tiles to see how they look like separately:
import matplotlib.pyplot as plt
fig, axes = plt.subplots(2, 2, figsize=(16, 16))
# Plot the tiles to see how they look separately
datasets[0]["band_data"].plot(ax=axes[0][0], vmax=5900, add_colorbar=False)
datasets[1]["band_data"].plot(ax=axes[0][1], vmax=5900, add_colorbar=False)
datasets[2]["band_data"].plot(ax=axes[1][0], vmax=5900, add_colorbar=False)
datasets[3]["band_data"].plot(ax=axes[1][1], vmax=5900, add_colorbar=False)
<matplotlib.collections.QuadMesh at 0x7f9df494a5b0>
As we can see we have multiple separate raster files that are actually located next to each other. Hence, we want to put them together into a single raster file that can by done by creating a raster mosaic.
Now we can create a raster mosaic by merging these datasets with merge_datasets()
function:
# Create a mosaic out of the tiles
mosaic = merge_datasets(datasets)
Rename the data variable to more intuitive one:
# Add a more intuitive name for the data variable
mosaic = mosaic.rename({"band_data": "elevation"})
Plot the end result where the tiles have been merged:
# Plot the mosaic
mosaic["elevation"].plot(figsize=(12, 12))
<matplotlib.collections.QuadMesh at 0x7f9df47b3a30>
Clipping raster#
Create a GeoDataFrame with bounding box that we can use for clipping the raster:
import geopandas as gpd
from shapely.geometry import box
# Bounding box coordinates
minx = 37.1
miny = -3.3
maxx = 37.6
maxy = -2.85
# Create a GeoDataFrame that will be used to clip the raster
geom = box(minx, miny, maxx, maxy)
clipping_gdf = gpd.GeoDataFrame({"geometry": [geom]}, index=[0], crs="epsg:4326")
# Explore the extent on a map
clipping_gdf.explore()
Clip the mosaic with GeoDataFrame:
# Clip the Mosaic with GeoDataFrame and specify CRS
kilimanjaro = mosaic.rio.clip(clipping_gdf.geometry, crs=mosaic.elevation.rio.crs)
kilimanjaro["elevation"].plot()
<matplotlib.collections.QuadMesh at 0x7f9df42ee040>
kilimanjaro
<xarray.Dataset> Dimensions: (y: 1620, x: 1800) Coordinates: * y (y) float64 -2.85 -2.85 -2.851 -2.851 ... -3.299 -3.299 -3.3 * x (x) float64 37.1 37.1 37.1 37.1 37.1 ... 37.6 37.6 37.6 37.6 spatial_ref int64 0 Data variables: elevation (y, x) float32 1.581e+03 1.581e+03 ... 1.295e+03 1.294e+03
- y: 1620
- x: 1800
- y(y)float64-2.85 -2.85 -2.851 ... -3.299 -3.3
- axis :
- Y
- long_name :
- latitude
- standard_name :
- latitude
- units :
- degrees_north
array([-2.85 , -2.850278, -2.850556, ..., -3.299167, -3.299444, -3.299722])
- x(x)float6437.1 37.1 37.1 ... 37.6 37.6 37.6
- axis :
- X
- long_name :
- longitude
- standard_name :
- longitude
- units :
- degrees_east
array([37.100278, 37.100556, 37.100833, ..., 37.599444, 37.599722, 37.6 ])
- spatial_ref()int640
- crs_wkt :
- GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AXIS["Latitude",NORTH],AXIS["Longitude",EAST],AUTHORITY["EPSG","4326"]]
- semi_major_axis :
- 6378137.0
- semi_minor_axis :
- 6356752.314245179
- inverse_flattening :
- 298.257223563
- reference_ellipsoid_name :
- WGS 84
- longitude_of_prime_meridian :
- 0.0
- prime_meridian_name :
- Greenwich
- geographic_crs_name :
- WGS 84
- grid_mapping_name :
- latitude_longitude
- spatial_ref :
- GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AXIS["Latitude",NORTH],AXIS["Longitude",EAST],AUTHORITY["EPSG","4326"]]
- GeoTransform :
- 37.10013888888888 0.00027777777777777924 0.0 -2.8498611111111107 0.0 -0.00027777777777777843
array(0)
- elevation(y, x)float321.581e+03 1.581e+03 ... 1.294e+03
- long_name :
- Band 1
array([[1581., 1581., 1581., ..., 1372., 1372., 1372.], [1587., 1584., 1582., ..., 1370., 1371., 1373.], [1587., 1588., 1584., ..., 1368., 1368., 1370.], ..., [1013., 1010., 1013., ..., 1301., 1301., 1300.], [1010., 1011., 1011., ..., 1302., 1301., 1301.], [1007., 1010., 1010., ..., 1302., 1295., 1294.]], dtype=float32)
Now we have a ready raster mosaic and we can save the raster to a GeoTIFF file:
# Save file to GeoTIFF
kilimanjaro.rio.to_raster("data/kilimanjaro.tif", compress="LZMA", tiled=True)