Source code for hydromodpy.display.visualization_watershed

# -*- coding: utf-8 -*-
"""
 * Copyright (C) 2023-2025 Alexandre Gauvain, Ronan Abhervé, Jean-Raynald de Dreuzy
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
 * which is available at https://www.apache.org/licenses/LICENSE-2.0.
 *
 * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
"""

#%% LIBRAIRIES

# Python
import os
import numpy as np
import geopandas as gpd
import rasterio
from rasterio.plot import show
import contextily as cx
import matplotlib as mpl
import matplotlib.pyplot as plt
from matplotlib.font_manager import FontProperties
from mpl_toolkits.axes_grid1 import make_axes_locatable
from matplotlib_scalebar.scalebar import ScaleBar
import matplotlib.patches as mpatches
try:
    from colormap.colors import rgb2hex, hex2rgb
except:
    pass

# HydroModPy
from hydromodpy.tools import toolbox

#%% PLOT SETTINGS

# # # Classic
mpl.style.use('classic')
mpl.rcParams["figure.facecolor"] = 'white'
mpl.rcParams['grid.color'] = 'darkgrey'
mpl.rcParams['grid.linestyle'] = '-'
mpl.rcParams['grid.alpha'] = 0.8
mpl.rcParams['axes.axisbelow'] = True
mpl.rcParams['figure.dpi'] = 300
mpl.rcParams['savefig.dpi'] = 300
mpl.rcParams['patch.force_edgecolor'] = True
mpl.rcParams['image.interpolation'] = 'nearest'
mpl.rcParams['image.resample'] = True
mpl.rcParams['axes.autolimit_mode'] = 'data' # 'round_numbers'
# mpl.rcParams['axes.autolimit_mode'] = 'round_numbers' # 'data' 
mpl.rcParams['axes.xmargin'] = 0.1
mpl.rcParams['axes.ymargin'] = 0.1
mpl.rcParams['xtick.direction'] = 'in'
mpl.rcParams['ytick.direction'] = 'in'
mpl.rcParams['xtick.top'] = True
mpl.rcParams['ytick.right'] = True
mpl.rcParams['legend.numpoints'] = 1
mpl.rcParams['legend.scatterpoints'] = 1
mpl.rcParams['legend.edgecolor'] = 'grey'
mpl.rcParams['date.autoformatter.year'] = '%Y'
mpl.rcParams['date.autoformatter.month'] = '%Y-%m'
mpl.rcParams['date.autoformatter.day'] = '%Y-%m-%d'
mpl.rcParams['date.autoformatter.hour'] = '%H:%M'
mpl.rcParams['date.autoformatter.minute'] = '%H:%M:%S'
mpl.rcParams['date.autoformatter.second'] = '%H:%M:%S'

# Parameters size plot
smal = 8
medium = 10
large = 12

plt.rc('font', size=medium)                         # controls default text sizes **font
plt.rc('figure', titlesize=medium)                   # fontsize of the figure title
plt.rc('legend', fontsize=smal)                     # legend fontsize
plt.rc('axes', titlesize=medium, labelpad=8)        # fontsize of the axes title
plt.rc('axes', labelsize=smal, labelpad=0)        # fontsize of the x and y labels
plt.rc('xtick', labelsize=medium)                   # fontsize of the tick labels
plt.rc('ytick', labelsize=medium)                   # fontsize of the tick labels
plt.rcParams["font.family"] = "serif"

# Font label and legend properties
fontprop = FontProperties()
fontprop.set_family('serif') # for x and y label
fontdic = {'family' : 'serif'} # for legend

#%% FUNCTIONS

[docs] def watershed_dem(BV): """ Plot contour watershed and DEM. Parameters ---------- BV : object Variable object of the model domain (watershed). """ fontprop = toolbox.plot_params(8,15,18,20) fig, ax = plt.subplots(1, 1, figsize=(5,5), dpi=300) try: contour = gpd.read_file(BV.geographic.watershed_contour_shp) bounds_shp = contour.geometry.total_bounds except: pass dem = rasterio.open(BV.geographic.watershed_box_buff_dem) bounds = dem.bounds xlim = ([bounds[0], bounds[2]]) ylim = ([bounds[1], bounds[3]]) ax.set_xlim(xlim) ax.set_ylim(ylim) scalebar = ScaleBar(1,box_alpha=0, scale_loc = 'top', location='lower left', rotation='horizontal-only') ax.add_artist(scalebar) ax.get_xaxis().set_visible(False) ax.get_yaxis().set_visible(False) ax.set(aspect='equal') image_hidden = ax.imshow(np.ma.masked_where(dem.read(1) < -100, dem.read(1)), cmap='terrain') show(np.ma.masked_where(dem.read(1) < -100, dem.read(1)), ax=ax, transform=dem.transform, cmap='terrain', alpha=0.75, zorder=2, aspect="auto") legend_handles = [] try: streams = gpd.read_file(BV.hydrography.streams) legend_handles += streams.plot(ax=ax, lw=1.5, color='navy', zorder=3, legend=True, label='Streams').get_legend_handles_labels()[0] except Exception: pass try: h = contour.plot(ax=ax, lw=1.5, zorder=4, legend=True, label='Watershed', edgecolor='k', facecolor='None') legend_handles += h.get_legend_handles_labels()[0] except Exception: pass try: if os.path.exists(BV.piezometry.piezos_shp): piezos = gpd.read_file(BV.piezometry.piezos_shp) h = piezos.plot(ax=ax, color='blue', marker='^', zorder=6, edgecolor='k', lw=1, legend=True, label='Piezometers: continue') legend_handles += h.get_legend_handles_labels()[0] except Exception: pass try: if len(BV.piezometry.x_coord_discrete)>0: h = ax.scatter(BV.piezometry.x_coord_discrete, BV.piezometry.y_coord_discrete, c='darkorange', marker='^', zorder=5, label='Piezometers: discrete') legend_handles.append(h) except Exception: pass try: if os.path.exists(BV.hydrometry.hydrometric_clip): hydromet = gpd.read_file(BV.hydrometry.hydrometric_clip) h = hydromet.plot(ax=ax, color='white', zorder=7, marker='o', edgecolor='k', lw=1, legend=True, label='Hydrometric: continue') legend_handles += h.get_legend_handles_labels()[0] except Exception: pass try: if os.path.exists(BV.intermittency.onde_clip): intermit = gpd.read_file(BV.intermittency.onde_clip) h = intermit.plot(ax=ax, color='grey', zorder=8, marker='s', edgecolor='black', lw=1, legend=True, label='Intermittency: discrete') legend_handles += h.get_legend_handles_labels()[0] except Exception: pass if legend_handles: ax.legend(loc='lower right', title=BV.watershed_name, framealpha=0.8) divider = make_axes_locatable(ax) cax = divider.append_axes(size="4%",position='right', pad=0.05) fig.add_axes(cax) cbar = fig.colorbar(image_hidden, cax=cax, orientation="vertical") cbar.ax.get_ymajorticklabels() list(cbar.get_ticks()) val = np.ma.masked_where(BV.geographic.dem_box_data < 0, BV.geographic.dem_box_data) minVal = int(round(np.min(val[np.nonzero(val)],0))) maxVal = int(round(np.max(val[np.nonzero(val)],0))) meanVal = int(round(minVal+((maxVal-minVal)/2),0)) cbar.set_ticks([minVal, meanVal, maxVal]) cbar.set_ticklabels([minVal, meanVal, maxVal]) cbar.mappable.set_clim(minVal, maxVal) cbar.ax.tick_params(labelsize=10) cbar.ax.yaxis.set_ticks_position('right') cbar.ax.tick_params(size=2) # cbar.set_label('Elevation (m)', size=12, rotation=270) fig.tight_layout() try: fig.savefig(os.path.join(BV.figure_folder,'watershed_dem'+'_'+ BV.hydrography.streams.split('/')[-1].split('.')[0]+'.png'), dpi=300, bbox_inches='tight', transparent=False) except: fig.savefig(os.path.join(BV.figure_folder,'watershed_dem'+'.png'), dpi=300, bbox_inches='tight', transparent=False) pass
[docs] def watershed_local(regional_dem_path, BV): """ Plot location of the watershed at the regional scale. Parameters ---------- regional_dem_path : str Initial path of the regional DEM. BV : object Variable object of the model domain (watershed). """ fontprop = toolbox.plot_params(8,15,18,20) fig, ax = plt.subplots(1, 1, figsize=(5,5), dpi=300) shp = gpd.read_file(BV.geographic.watershed_shp) dem = rasterio.open(regional_dem_path) ax.get_xaxis().set_visible(False) ax.get_yaxis().set_visible(False) ax.set(aspect='equal') scalebar = ScaleBar(1, box_alpha=0, scale_loc='top', location='lower left', rotation='horizontal-only') ax.add_artist(scalebar) dem_data = np.ma.masked_where(dem.read(1) < 0, dem.read(1)) vmin = np.nanmin(dem_data) vmax = np.nanmax(dem_data) norm = plt.Normalize(vmin=vmin, vmax=vmax) im = plt.cm.ScalarMappable(norm=norm, cmap='terrain') im.set_array([]) show(dem_data, ax=ax, transform=dem.transform, cmap='terrain', alpha=1, zorder=2, aspect="auto") shp.plot(ax=ax, lw=2, color='yellow', zorder=4) cbar = fig.colorbar(im, ax=ax, orientation='horizontal', fraction=0.03, pad=0.02, shrink=0.8) cbar.set_label('Topographic elevation [mNGF]', fontsize=8, labelpad=2) cbar.ax.tick_params(labelsize=8) legend_elements = [ mpatches.Patch(facecolor='yellow', edgecolor='black', linewidth=1, label='Watershed') ] ax.legend(handles=legend_elements, loc='lower right', framealpha=0.8) fig.tight_layout() fig.savefig(os.path.join(BV.figure_folder,'watershed_local.png'), dpi=300, bbox_inches='tight', transparent=False)
[docs] def watershed_geology(BV): """ Plot lithology of the watershed from specific geological map at FRance scale. Parameters ---------- BV : object Variable object of the model domain (watershed). """ fontprop = toolbox.plot_params(8,15,18,20) fig, ax = plt.subplots(1, 1, figsize=(5,5), dpi=300) ax = plt.gca() dem = rasterio.open(BV.geographic.watershed_box_buff_dem) polyg = gpd.read_file(BV.geographic.watershed_shp) contour = gpd.read_file(BV.geographic.watershed_contour_shp) crs = contour.crs bounds = dem.bounds xlim = ([bounds[0], bounds[2]]) ylim = ([bounds[1], bounds[3]]) ax.set_xlim(xlim) ax.set_ylim(ylim) ax.get_xaxis().set_visible(False) ax.get_yaxis().set_visible(False) ax.set(aspect='equal') cx.add_basemap(ax,crs=crs,source='https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png') geol = gpd.read_file(BV.geology.geol_file) try: geol['hex'] except: geol = gpd.read_file(BV.geology.geol_file) geol['R_col'] = 255 * (1 - geol['C_FOND']/100) * (1 - geol['N_FOND']/100) geol['G_col'] = 255 * (1 - geol['M_FOND']/100) * (1 - geol['N_FOND']/100) geol['B_col'] = 255 * (1 - geol['J_FOND']/100) * (1 - geol['N_FOND']/100) geol['R_col'][geol['R_col']>255] = 255 geol['G_col'][geol['G_col']>255] = 255 geol['B_col'][geol['B_col']>255] = 255 geol['couleur'] = list(zip(round(geol['R_col']).astype(int), round(geol['G_col']).astype(int), round(geol['B_col']).astype(int))) for i in range(len(geol)): geol.loc[i,'hex'] = rgb2hex(geol.loc[i,'couleur'][0], geol.loc[i,'couleur'][1], geol.loc[i,'couleur'][2]) geol = geol.drop(columns=['couleur']) geol.to_file(BV.geology.geol_file) geol = gpd.read_file(BV.geology.geol_file) color = [] for i in list(geol['hex']): color.append(mpl.colors.to_rgb(i)) geol = geol.cx[bounds[0]:bounds[2], bounds[1]:bounds[3]] geol1 = gpd.clip(geol,polyg) handles = [] for ctype, data in geol.groupby('NATURE'): color = data['hex'].iloc[0] data.plot(color=color, ax=ax,alpha=0.5, edgecolor='dimgrey', zorder=2, label='_nolegend_') for ctype, data in geol.groupby('NATURE'): color = data['hex'].iloc[0] if ctype.find('Partie marine')!=0: ctype = ctype.split(':')[0] patch = mpatches.Patch(facecolor=color, alpha=0.5, label=ctype.upper(), edgecolor='k') handles.append(patch) l1 = ax.legend(handles=handles, loc='best', ncol=1, fancybox=False,prop={'size':6.5}) leg = ax.get_legend() leg.set_bbox_to_anchor((1,1, 0, 0)) try: streams = gpd.read_file(BV.hydrography.streams) streams.plot(ax=ax, lw=1.5, color='navy', zorder=3,legend=True, label='Streams') except: pass contour.plot(ax=ax, lw=1.5, color='k', zorder=4, legend=True, edgecolor='k', facecolor='None', label='Watershed') try: if len(BV.piezometry.x_coord_discrete)>0: piezod = ax.scatter(BV.piezometry.x_coord_discrete, BV.piezometry.y_coord_discrete, c='darkorange', marker='^', zorder=5, label='Piezometers: discrete') if os.path.exists(BV.piezometry.piezos_shp): piezos = gpd.read_file(BV.piezometry.piezos_shp) piezos.plot(ax=ax, color='blue', marker='^', zorder=6, edgecolor='k',legend=True, label='Piezometers: continue') except: pass scalebar = ScaleBar(1,box_alpha=0, scale_loc = 'top', location='lower left', rotation='horizontal-only') ax.add_artist(scalebar) handles2, labels2 = ax.get_legend_handles_labels() legend_items = [(h, lbl) for h, lbl in zip(handles2, labels2) if lbl and not lbl.startswith('_')] l2 = None if legend_items: handles2, labels2 = zip(*legend_items) l2 = ax.legend(handles2, labels2, loc='lower right', title=BV.watershed_name, framealpha=0.8) plt.gca().add_artist(l1) fig.tight_layout() try: fig.savefig(os.path.join(BV.figure_folder,'watershed_geology'+'_'+ BV.hydrography.streams.split('/')[-1].split('.')[0]+'.png'), dpi=300, bbox_inches='tight', transparent=False) except: fig.savefig(os.path.join(BV.figure_folder,'watershed_geology.png'), dpi=300, bbox_inches='tight', transparent=False) pass
[docs] def watershed_zones(BV): fontprop = toolbox.plot_params(8,15,18,20) fig, ax = plt.subplots(1, 1, figsize=(5,5), dpi=300) try: contour = gpd.read_file(BV.geographic.watershed_contour_shp) bounds_shp = contour.geometry.total_bounds except: pass dem = rasterio.open(BV.geographic.watershed_box_buff_dem) bounds = dem.bounds xlim = ([bounds[0], bounds[2]]) ylim = ([bounds[1], bounds[3]]) ax.set_xlim(xlim) ax.set_ylim(ylim) scalebar = ScaleBar(1,box_alpha=0, scale_loc = 'top', location='lower left') ax.add_artist(scalebar) ax.get_xaxis().set_visible(False) ax.get_yaxis().set_visible(False) ax.set(aspect='equal') image_hidden = ax.imshow(BV.hydrodynamic.calib_zones,cmap='jet') show(BV.hydrodynamic.calib_zones, ax=ax, transform=dem.transform, cmap='jet', alpha=0.75, zorder=2, aspect="auto") try: streams = gpd.read_file(BV.hydrography.streams) streams.plot(ax=ax, lw=1.5, color='navy', zorder=3,legend=True, label='Streams') except: pass try: contour.plot(ax=ax, lw=1.5, color='k', edgecolor='k', facecolor='None', zorder=4,legend=True, label='Watershed') except: pass try: if os.path.exists(BV.piezometry.piezos_shp): piezos = gpd.read_file(BV.piezometry.piezos_shp) piezos.plot(ax=ax, color='blue', marker='^', zorder=6, edgecolor='k', lw=1, legend=True, label='Piezometers: continue') except: pass try: if len(BV.piezometry.x_coord_discrete)>0: ax.scatter(BV.piezometry.x_coord_discrete, BV.piezometry.y_coord_discrete, c='darkorange', marker='^', zorder=5, label='Piezometers: discrete') except: pass try: if os.path.exists(BV.hydrometry.hydrometric_clip): hydromet = gpd.read_file(BV.hydrometry.hydrometric_clip) hydromet.plot(ax=ax, color='white', zorder=7, marker='o', edgecolor='k', lw=1, legend=True, label='Hydrometric: continue') except: pass try: if os.path.exists(BV.intermittency.onde_clip): intermit = gpd.read_file(BV.intermittency.onde_clip) intermit.plot(ax=ax, color='grey', zorder=8, marker='s', edgecolor='black', lw=1, legend=True, label='Intermittency: discrete') except: pass ax.legend(loc='lower right', title = BV.watershed_name,framealpha=0.8) divider = make_axes_locatable(ax) cax = divider.append_axes(size="4%",position='right', pad=0.05) fig.add_axes(cax) cbar = fig.colorbar(image_hidden, cax=cax, orientation="vertical") cbar.ax.get_ymajorticklabels() list(cbar.get_ticks()) val = np.ma.masked_where(BV.geographic.dem_box_data < 0, BV.geographic.dem_box_data) minVal = int(round(np.min(val[np.nonzero(val)],0))) maxVal = int(round(np.max(val[np.nonzero(val)],0))) meanVal = int(round(minVal+((maxVal-minVal)/2),0)) cbar.set_ticks([minVal, meanVal, maxVal]) cbar.set_ticklabels([minVal, meanVal, maxVal]) cbar.mappable.set_clim(minVal, maxVal) cbar.ax.tick_params(labelsize=10) cbar.ax.yaxis.set_ticks_position('right') cbar.ax.tick_params(size=2) # cbar.set_label('Elevation (m)', size=12, rotation=270) fig.tight_layout() fig.savefig(os.path.join(BV.figure_folder,'watershed_zones.png'), dpi=300, bbox_inches='tight', transparent=False)
#%% NOTES