Topobathymetric elevation generation for flood modeling with geospatial Python

Elevation maps that represent surface and river bathymetry are an essential input for flood modeling in software like HEC-RAS. Even with the latest version of high profile open source GIS software as QGIS the combination of a surface elevation map and a river bottom elevation map is a challenge that requires many turnaround, conversion and manual labour. We have developed a useful script that works with surface and river bottom elevation in a "smart" way and creates a geospatial raster with the topobathymetric elevation by the use of geospatial Python libraries as Shapely and Rasterio. The script also includes some key steps to identify the river body and treat the missing values on the bathymetry map.

Tutorial

Code

import geopandas as gpd
import matplotlib.pyplot as plt
import numpy as np
import rasterio
from rasterio.plot import show
from shapely.geometry import Polygon, Point
from rasterio.features import shapes
from rtree import index
#open the raster utm datasets
riverBat = rasterio.open('../Rst/riverBathymetry.tif')
surfElev = rasterio.open('../Rst/surfaceElevation_15N.tif')
show(riverBat)
#get no data value
rivNodata = riverBat.nodata
rivNodata
-3.4028230607370965e+38
#create an array of the active river
bath = riverBat.read(1)
activeRiver = np.zeros(bath.shape)
activeRiver[bath > riverBat.nodata] = 1
plt.imshow(activeRiver)
<matplotlib.image.AxesImage at 0x262ac72a2a0>
#creating a contour over the active river
polys = shapes(activeRiver, connectivity=8, transform=riverBat.transform)
#looping over the polygon generator to create a geospatial polygon
hugePolys = []

for poly in polys:
    #print(poly)
    shpPol = Polygon(poly[0]['coordinates'][0])
    if shpPol.area > 100000 and poly[1]==1:
        #print(poly[0])
        print(shpPol.area)
        hugePolys.append(shpPol)
1187999.2424999955
# show resulting polygon
hugePolys[0]

svg

#open the aoi
aoi = gpd.read_file('../Shp/aoi_15N.shp')
aoi.iloc[0].geometry

svg

#explore the aoi bounds
bounds = aoi.total_bounds
bounds
array([ 430657.62111644, 4338698.67453457,  433523.37037566,
       4340610.00927973])
xMin, xMax = bounds[0], bounds[2]
yMin, yMax = bounds[1], bounds[3]
res = 25 #output raster resolution of 1m
#define the cell centers arrays
xArray = np.arange(xMin,xMax,res)
yArray = np.arange(yMin,yMax,res)
yArray[:5]
array([4338698.67453457, 4338723.67453457, 4338748.67453457,
       4338773.67453457, 4338798.67453457])
#get nrows and ncols
nrow = yArray.shape[0]
ncol = xArray.shape[0]
print(nrow,ncol)
77 115
#get no data value
riverNodata = riverBat.nodata

#create sample array
topoBath = np.zeros([nrow,ncol])
i = 0

for row in range(nrow):
    for col in range(ncol):
        
        colX = xArray[col]
        rowY = yArray[row]

        # create a point object of the xy
        pixel = Point(colX,rowY)
        #apply value of bathymetry if available
        if hugePolys[0].contains(pixel):
            #extract raster values
            bathValue = list(riverBat.sample([(colX,rowY)]))[0]
            bathValue = bathValue.tolist()[0]
            #check if sampled values are empty
            if bathValue == riverNodata:
                topoBath[row,col] = topoBath[row,col-1]
            else:
                topoBath[row,col] = bathValue
        else:
            surfValue = list(surfElev.sample([(colX,rowY)]))[0]
            surfValue = surfValue.tolist()[0]
            topoBath[row,col] = surfValue

        #counter
        if i % 100 ==0:
            print(f'\r %d'%i, end="", flush=True)

        i+=1
8800
#preview of the topo array (inverted)
plt.imshow(topoBath)
#definition of the raster transform array
from rasterio.transform import Affine
transform = Affine.translation(xArray[0]-res/2, yArray[0]-res/2)*Affine.scale(res,res)
transform
Affine(25.0, 0.0, 430645.12111643935,
       0.0, 25.0, 4338686.1745345695)
#get crs as wkt
from rasterio.crs import CRS
rasterCrs = CRS.from_epsg(26915)
rasterCrs.data
{'proj': 'utm', 'zone': 15, 'datum': 'NAD83', 'units': 'm', 'no_defs': True}
#definition, register and close of interpolated raster
interpRaster = rasterio.open('../Rst/topoBath_'+str(res)+'.tif',
                                'w',
                                driver='GTiff',
                                height=nrow,
                                width=ncol,
                                count=1,
                                dtype=topoBath.dtype,
                                crs=rasterCrs,
                                transform=transform,
                                )
interpRaster.write(topoBath,1)
interpRaster.close()

Input data

https://owncloud.hatarilabs.com/s/RpkGwYh8NHUXKje

Password to download data: Hatarilabs

Comment

Saul Montoya

Saul Montoya es Ingeniero Civil graduado de la Pontificia Universidad Católica del Perú en Lima con estudios de postgrado en Manejo e Ingeniería de Recursos Hídricos (Programa WAREM) de la Universidad de Stuttgart con mención en Ingeniería de Aguas Subterráneas y Hidroinformática.

 

Suscribe to our online newsletter

Subscribe for free newsletter, receive news, interesting facts and dates of our courses in water resources.