Visualization#
The visualization modules provide tools for exporting model data to various formats for visualization in external tools.
GIS Export#
The GIS export module provides functionality for exporting model data to GIS formats including GeoPackage, Shapefile, and GeoJSON.
GIS export functionality for IWFM models.
This module provides the GISExporter class for exporting IWFM model
data to various GIS formats including GeoPackage, Shapefile, and GeoJSON.
Supported Formats#
GeoPackage (.gpkg): Recommended format, single file with multiple layers
Shapefile (.shp): Widely compatible but limited to 10-char field names
GeoJSON (.geojson): Text-based, good for web applications
Example
Export a mesh to GeoPackage:
>>> from pyiwfm.core.mesh import AppGrid, Node, Element
>>> from pyiwfm.visualization.gis_export import GISExporter
>>>
>>> # Create simple mesh
>>> nodes = {1: Node(id=1, x=0.0, y=0.0), 2: Node(id=2, x=100.0, y=0.0),
... 3: Node(id=3, x=50.0, y=100.0)}
>>> elements = {1: Element(id=1, vertices=(1, 2, 3))}
>>> grid = AppGrid(nodes=nodes, elements=elements)
>>> grid.compute_connectivity()
>>>
>>> # Export to GeoPackage
>>> exporter = GISExporter(grid=grid, crs="EPSG:26910")
>>> exporter.export_geopackage("model.gpkg")
- class pyiwfm.visualization.gis_export.GISExporter(grid, stratigraphy=None, streams=None, crs=None)[source]#
Bases:
objectExport IWFM model data to GIS formats.
This class converts model meshes, stratigraphy, and stream networks to GeoDataFrames that can be exported to various GIS formats.
- Parameters:
grid (
AppGrid) – Model mesh to export.stratigraphy (
Stratigraphy, optional) – Model stratigraphy. If provided, layer elevations are added as attributes to the nodes GeoDataFrame.streams (
AppStream, optional) – Stream network. If provided, enables stream layer export.crs (
str, optional) – Coordinate reference system (e.g., ‘EPSG:26910’, ‘EPSG:2227’). If None, output files will have no CRS defined.
- Raises:
ImportError – If geopandas or shapely are not installed.
Examples
Basic export to GeoPackage:
>>> exporter = GISExporter(grid=grid, crs="EPSG:26910") >>> exporter.export_geopackage("model.gpkg")
Export with stratigraphy data:
>>> exporter = GISExporter(grid=grid, stratigraphy=strat, crs="EPSG:26910") >>> gdf = exporter.nodes_to_geodataframe() >>> # GeoDataFrame includes gs_elev, layer_1_top, layer_1_bottom, etc.
Export with custom attributes:
>>> head_data = {1: 50.0, 2: 52.0, 3: 48.0} # node_id -> value >>> gdf = exporter.nodes_to_geodataframe(attributes={"head_ft": head_data}) >>> gdf.to_file("nodes_with_heads.gpkg", driver="GPKG")
Export to multiple formats:
>>> exporter.export_geopackage("model.gpkg") # GeoPackage >>> exporter.export_shapefiles("shapefiles/") # Shapefiles >>> exporter.export_geojson("elements.geojson", layer="elements")
- __init__(grid, stratigraphy=None, streams=None, crs=None)[source]#
Initialize the GIS exporter.
- Parameters:
grid (AppGrid) – Model mesh
stratigraphy (Stratigraphy | None) – Model stratigraphy (optional)
streams (AppStream | None) – Stream network (optional)
crs (str | None) – Coordinate reference system (e.g., ‘EPSG:26910’)
- streams_to_geodataframe()[source]#
Convert stream network to a GeoDataFrame.
- Returns:
GeoDataFrame with stream reach linestrings
- Return type:
- subregions_to_geodataframe()[source]#
Convert subregions to a GeoDataFrame (dissolved elements).
- Returns:
GeoDataFrame with subregion polygons
- Return type:
- boundary_to_geodataframe()[source]#
Extract model boundary as a GeoDataFrame.
- Returns:
GeoDataFrame with model boundary polygon
- Return type:
- export_geopackage(output_path, include_streams=True, include_subregions=True, include_boundary=True)[source]#
Export model to GeoPackage format.
VTK Export#
The VTK export module provides functionality for exporting 3D model data to VTK formats for visualization in ParaView.
VTK export functionality for IWFM models.
This module provides classes for exporting IWFM model data to VTK formats for 3D visualization in tools like ParaView.
- class pyiwfm.visualization.vtk_export.VTKExporter(grid, stratigraphy=None)[source]#
Bases:
objectExport IWFM model data to VTK formats.
This class converts model meshes and stratigraphy to VTK UnstructuredGrid objects that can be exported to VTU or legacy VTK formats for visualization in ParaView.
- Variables:
grid – Model mesh
stratigraphy – Model stratigraphy (optional, required for 3D)
- __init__(grid, stratigraphy=None)[source]#
Initialize the VTK exporter.
- Parameters:
grid (AppGrid) – Model mesh
stratigraphy (Stratigraphy | None) – Model stratigraphy (optional, required for 3D)
- create_2d_mesh()[source]#
Create a 2D VTK UnstructuredGrid from the mesh.
- Returns:
VTK UnstructuredGrid with 2D mesh
- Return type:
vtk.vtkUnstructuredGrid
- create_3d_mesh()[source]#
Create a 3D VTK UnstructuredGrid from mesh and stratigraphy.
Quad elements become hexahedra, triangles become wedges.
- Returns:
VTK UnstructuredGrid with 3D mesh
- Raises:
ValueError – If stratigraphy is not set
- Return type:
vtk.vtkUnstructuredGrid
- add_node_scalar(vtk_grid, name, values)[source]#
Add scalar data to mesh nodes.
- Parameters:
vtk_grid (vtk.vtkUnstructuredGrid) – VTK grid to add data to
name (str) – Scalar array name
values (NDArray[np.float64]) – Scalar values (one per node)
- add_cell_scalar(vtk_grid, name, values)[source]#
Add scalar data to mesh cells.
- Parameters:
vtk_grid (vtk.vtkUnstructuredGrid) – VTK grid to add data to
name (str) – Scalar array name
values (NDArray[np.float64]) – Scalar values (one per cell)
- export_vtu(output_path, mode='2d', node_scalars=None, cell_scalars=None)[source]#
Export mesh to VTU format (XML-based VTK).
- Parameters:
mode (Literal['2d', '3d']) – ‘2d’ for surface mesh, ‘3d’ for volumetric mesh
node_scalars (dict[str, ndarray[tuple[Any, ...], dtype[float64]]] | None) – Dict of name -> values for node data
cell_scalars (dict[str, ndarray[tuple[Any, ...], dtype[float64]]] | None) – Dict of name -> values for cell data
- export_vtk(output_path, mode='2d', node_scalars=None, cell_scalars=None)[source]#
Export mesh to legacy VTK format.
- Parameters:
mode (Literal['2d', '3d']) – ‘2d’ for surface mesh, ‘3d’ for volumetric mesh
node_scalars (dict[str, ndarray[tuple[Any, ...], dtype[float64]]] | None) – Dict of name -> values for node data
cell_scalars (dict[str, ndarray[tuple[Any, ...], dtype[float64]]] | None) – Dict of name -> values for cell data
- to_pyvista_3d(node_scalars=None, cell_scalars=None)[source]#
Create a PyVista UnstructuredGrid from mesh and stratigraphy.
This method converts the IWFM mesh and stratigraphy to a PyVista UnstructuredGrid for use in interactive 3D visualization. The resulting mesh can be used with PyVista plotting functions or the Trame web visualization framework.
- Parameters:
node_scalars (
dict[str,NDArray], optional) – Dictionary of scalar arrays to add to mesh nodes. Keys are array names, values are 1D arrays with one value per node (for 2D) or per node-surface point (for 3D).cell_scalars (
dict[str,NDArray], optional) – Dictionary of scalar arrays to add to mesh cells. Keys are array names, values are 1D arrays with one value per cell.
- Returns:
PyVista UnstructuredGrid with 3D volumetric mesh if stratigraphy is available, otherwise 2D surface mesh.
- Return type:
pv.UnstructuredGrid- Raises:
ImportError – If PyVista is not installed.
Examples
Create a 3D mesh for visualization:
>>> exporter = VTKExporter(grid=grid, stratigraphy=strat) >>> pv_mesh = exporter.to_pyvista_3d() >>> pv_mesh.plot()
Add scalar data:
>>> kh_values = np.random.rand(n_cells) >>> pv_mesh = exporter.to_pyvista_3d(cell_scalars={"Kh": kh_values}) >>> pv_mesh.plot(scalars="Kh", cmap="viridis")
Plotting#
The plotting module provides matplotlib-based visualization tools for creating 2D plots of model meshes and data.
Plotting functionality for IWFM models.
This module re-exports all public plotting functions and classes from the
sub-modules plot_mesh,
plot_timeseries,
plot_budget, and
plot_calibration.
Importing from pyiwfm.visualization.plotting continues to work exactly
as before – every name listed in __all__ is available at this level.
- class pyiwfm.visualization.plotting.MeshPlotter(grid, streams=None, figsize=(10, 8))[source]#
Bases:
objectHigh-level class for creating mesh visualizations.
This class provides a convenient interface for creating multi-layer visualizations of IWFM model meshes.
- Variables:
grid – Model mesh
streams – Stream network (optional)
- plot_mesh(show_edges=True, show_node_ids=False, show_element_ids=False, show_streams=False, **kwargs)[source]#
Plot the mesh with optional overlays.
- Parameters:
- Returns:
Tuple of (Figure, Axes)
- Return type:
- plot_composite(show_mesh=True, show_streams=False, node_values=None, cell_values=None, title=None, cmap='viridis', **kwargs)[source]#
Create a composite plot with multiple layers.
- Parameters:
show_mesh (bool) – Show mesh edges
show_streams (bool) – Overlay stream network
node_values (ndarray[tuple[Any, ...], dtype[float64]] | None) – Scalar values at nodes (optional)
cell_values (ndarray[tuple[Any, ...], dtype[float64]] | None) – Scalar values at cells (optional)
title (str | None) – Plot title
cmap (str) – Colormap for scalar values
**kwargs (Any) – Additional arguments
- Returns:
Tuple of (Figure, Axes)
- Return type:
- pyiwfm.visualization.plotting.plot_mesh(grid, ax=None, show_edges=True, show_node_ids=False, show_element_ids=False, edge_color='black', edge_width=0.5, fill_color='lightblue', alpha=0.3, figsize=(10, 8))[source]#
Plot the mesh with elements and optional annotations.
- Parameters:
grid (AppGrid) – Model mesh
ax (Axes | None) – Existing axes to plot on (creates new if None)
show_edges (bool) – Show element edges
show_node_ids (bool) – Label nodes with their IDs
show_element_ids (bool) – Label elements with their IDs
edge_color (str) – Color for element edges
edge_width (float) – Width of edge lines
fill_color (str) – Fill color for elements
alpha (float) – Transparency of element fill
- Returns:
Tuple of (Figure, Axes)
- Return type:
tuple[Figure, Axes]
- pyiwfm.visualization.plotting.plot_nodes(grid, ax=None, highlight_boundary=False, marker_size=20, color='blue', boundary_color='red', figsize=(10, 8))[source]#
Plot mesh nodes as points.
- Parameters:
- Returns:
Tuple of (Figure, Axes)
- Return type:
tuple[Figure, Axes]
- pyiwfm.visualization.plotting.plot_elements(grid, ax=None, color_by='none', cmap='viridis', show_colorbar=True, edge_color='black', edge_width=0.5, alpha=0.7, figsize=(10, 8))[source]#
Plot mesh elements with optional coloring by attribute.
- Parameters:
grid (AppGrid) – Model mesh
ax (Axes | None) – Existing axes to plot on
color_by (Literal['subregion', 'area', 'none']) – Attribute to color elements by
cmap (str) – Colormap name
show_colorbar (bool) – Show colorbar for colored plots
edge_color (str) – Color for element edges
edge_width (float) – Width of edge lines
alpha (float) – Transparency of element fill
- Returns:
Tuple of (Figure, Axes)
- Return type:
tuple[Figure, Axes]
- pyiwfm.visualization.plotting.plot_scalar_field(grid, values, field_type='node', ax=None, cmap='viridis', show_colorbar=True, vmin=None, vmax=None, show_mesh=True, edge_color='gray', edge_width=0.3, n_subdiv=4, figsize=(10, 8))[source]#
Plot scalar field values on the mesh.
- Parameters:
grid (AppGrid) – Model mesh
values (NDArray[np.float64]) – Scalar values (one per node or cell)
field_type (Literal['node', 'cell']) – ‘node’ for node values, ‘cell’ for cell values
ax (Axes | None) – Existing axes to plot on
cmap (str) – Colormap name
show_colorbar (bool) – Show colorbar
vmin (float | None) – Minimum value for colormap
vmax (float | None) – Maximum value for colormap
show_mesh (bool) – Show mesh edges
edge_color (str) – Color for mesh edges
edge_width (float) – Width of mesh edges
n_subdiv (int) – Subdivision level for bilinear quad interpolation (>=2 enables FE subdivision; 1 uses legacy diagonal-split triangulation)
- Returns:
Tuple of (Figure, Axes)
- Return type:
tuple[Figure, Axes]
- pyiwfm.visualization.plotting.plot_streams(streams, ax=None, show_nodes=False, line_color='blue', line_width=2.0, node_color='blue', node_size=30, figsize=(10, 8))[source]#
Plot stream network.
- Parameters:
streams (AppStream) – Stream network
ax (Axes | None) – Existing axes to plot on
show_nodes (bool) – Show stream node markers
line_color (str) – Color for stream lines
line_width (float) – Width of stream lines
node_color (str) – Color for stream nodes
node_size (float) – Size of stream node markers
- Returns:
Tuple of (Figure, Axes)
- Return type:
tuple[Figure, Axes]
- pyiwfm.visualization.plotting.plot_lakes(lakes, grid, ax=None, fill_color='cyan', edge_color='blue', edge_width=1.5, alpha=0.5, show_labels=True, label_fontsize=9, cmap=None, figsize=(10, 8))[source]#
Plot lake elements on the mesh.
- Parameters:
lakes (
AppLake) – Lake component containing lake definitions and element assignments.grid (
AppGrid) – Model mesh used to look up element vertex coordinates.ax (
Axes, optional) – Existing axes to plot on. Creates new figure if None.fill_color (
str, default"cyan") – Fill color for lake elements (used when cmap is None).edge_color (
str, default"blue") – Edge color for lake element polygons.edge_width (
float, default1.5) – Width of lake element edges.alpha (
float, default0.5) – Transparency of lake element fill.show_labels (
bool, defaultTrue) – Show lake name labels at the centroid of each lake.label_fontsize (
float, default9) – Font size for lake labels.cmap (
str, optional) – If provided, color each lake with a different color from this colormap instead of using fill_color.figsize (
tuple, default(10,8)) – Figure size in inches.
- Returns:
(Figure, Axes) matplotlib objects.
- Return type:
- pyiwfm.visualization.plotting.plot_boundary(grid, ax=None, line_color='black', line_width=2.0, fill=False, fill_color='lightgray', alpha=0.3, figsize=(10, 8))[source]#
Plot model boundary.
- Parameters:
- Returns:
Tuple of (Figure, Axes)
- Return type:
tuple[Figure, Axes]
- pyiwfm.visualization.plotting.plot_timeseries(timeseries, ax=None, title=None, xlabel='Date', ylabel=None, legend=True, colors=None, linestyles=None, markers=None, figsize=(12, 6), grid=True, date_format=None)[source]#
Plot one or more time series as line charts.
- Parameters:
timeseries (
TimeSeriesorsequenceofTimeSeries) – Time series data to plot. Can be a single TimeSeries or a list.ax (
Axes, optional) – Existing axes to plot on. Creates new figure if None.title (
str, optional) – Plot title.xlabel (
str, default"Date") – X-axis label.ylabel (
str, optional) – Y-axis label. Uses units from first time series if not specified.colors (
sequenceofstr, optional) – Line colors for each series.linestyles (
sequenceofstr, optional) – Line styles for each series (e.g., ‘-’, ‘–’, ‘:’).markers (
sequenceofstr, optional) – Markers for each series (e.g., ‘o’, ‘s’, ‘^’).figsize (
tuple, default(12,6)) – Figure size in inches.date_format (
str, optional) – Date format for x-axis (e.g., ‘%Y-%m’).
- Returns:
(Figure, Axes) matplotlib objects.
- Return type:
Examples
Plot a single time series:
>>> ts = TimeSeries(times=times, values=values, name='Head', units='ft') >>> fig, ax = plot_timeseries(ts, title='Groundwater Head')
Plot multiple time series:
>>> fig, ax = plot_timeseries([ts1, ts2, ts3], legend=True)
- pyiwfm.visualization.plotting.plot_timeseries_comparison(observed, simulated, ax=None, title=None, show_residuals=False, show_metrics=True, obs_color='blue', sim_color='red', obs_marker='o', figsize=(12, 8))[source]#
Plot observed vs simulated time series comparison.
- Parameters:
observed (
TimeSeries) – Observed data time series.simulated (
TimeSeries) – Simulated/modeled data time series.ax (
Axes, optional) – Existing axes. Creates new figure if None.title (
str, optional) – Plot title.show_residuals (
bool, defaultFalse) – Show residual subplot below main plot.show_metrics (
bool, defaultTrue) – Display comparison metrics (RMSE, NSE, etc.) on plot.obs_color (
str, default"blue") – Color for observed data.sim_color (
str, default"red") – Color for simulated data.obs_marker (
str, default"o") – Marker for observed data points.figsize (
tuple, default(12,8)) – Figure size.
- Returns:
(Figure, Axes) matplotlib objects.
- Return type:
Examples
>>> fig, ax = plot_timeseries_comparison( ... observed=obs_ts, ... simulated=sim_ts, ... title='Head Calibration - Well 1', ... show_metrics=True ... )
- pyiwfm.visualization.plotting.plot_timeseries_collection(collection, locations=None, ax=None, title=None, max_series=10, figsize=(12, 6), **kwargs)[source]#
Plot multiple time series from a collection.
- Parameters:
collection (
TimeSeriesCollection) – Collection of time series data.locations (
sequenceofstr, optional) – Specific locations to plot. Plots all if None.ax (
Axes, optional) – Existing axes to plot on.title (
str, optional) – Plot title. Uses collection name if not specified.max_series (
int, default10) – Maximum number of series to plot (for readability).figsize (
tuple, default(12,6)) – Figure size.**kwargs (Any) – Additional arguments passed to plot_timeseries.
- Returns:
(Figure, Axes) matplotlib objects.
- Return type:
- pyiwfm.visualization.plotting.plot_timeseries_statistics(collection, ax=None, band='minmax', mean_color='steelblue', band_alpha=0.25, show_individual=False, individual_alpha=0.15, title=None, ylabel=None, figsize=(12, 6))[source]#
Plot ensemble mean with min/max or standard-deviation bands.
- Parameters:
collection (
TimeSeriesCollection) – Collection of time series data.ax (
Axes, optional) – Existing axes to plot on.band (
{"minmax", "std"}, default"minmax") – Band type: min/max envelope or +/- 1 standard deviation.mean_color (
str, default"steelblue") – Color for the mean line.band_alpha (
float, default0.25) – Transparency for the shaded band.show_individual (
bool, defaultFalse) – If True, draw each individual series behind the statistics.individual_alpha (
float, default0.15) – Alpha for individual series lines.title (
str, optional) – Plot title.ylabel (
str, optional) – Y-axis label.figsize (
tuple, default(12,6)) – Figure size in inches.
- Returns:
(Figure, Axes) matplotlib objects.
- Return type:
- pyiwfm.visualization.plotting.plot_dual_axis(ts1, ts2, ax=None, color1='steelblue', color2='coral', style1='-', style2='-', label1=None, label2=None, ylabel1=None, ylabel2=None, title=None, figsize=(12, 6))[source]#
Dual y-axis comparison of two time series.
- Parameters:
ts1 (
TimeSeries) – The two time series to plot.ts2 (
TimeSeries) – The two time series to plot.ax (
Axes, optional) – Primary axes. If None, a new figure is created.color1 (
str) – Colors for the two series.color2 (
str) – Colors for the two series.style1 (
str) – Line styles (e.g., “-”, “–”, “o-“).style2 (
str) – Line styles (e.g., “-”, “–”, “o-“).label1 (
str, optional) – Legend labels. Falls back tots.name.label2 (
str, optional) – Legend labels. Falls back tots.name.ylabel1 (
str, optional) – Y-axis labels. Falls back tots.units.ylabel2 (
str, optional) – Y-axis labels. Falls back tots.units.title (
str, optional) – Plot title.figsize (
tuple, default(12,6)) – Figure size in inches.
- Returns:
(Figure, (Axes_left, Axes_right)).
- Return type:
- pyiwfm.visualization.plotting.plot_streamflow_hydrograph(times, flows, baseflow=None, ax=None, flow_color='steelblue', baseflow_color='darkorange', fill_alpha=0.3, log_scale=False, title='Streamflow Hydrograph', ylabel='Flow', units='cfs', figsize=(14, 6))[source]#
Plot streamflow hydrograph with optional baseflow separation.
- Parameters:
times (
ndarray) – Datetime array for x-axis.flows (
ndarray) – Total streamflow values.baseflow (
ndarray, optional) – Baseflow component. If provided, the area between total flow and baseflow is shaded to highlight the quickflow component.ax (
Axes, optional) – Existing axes to plot on.flow_color (
str, default"steelblue") – Color for the total flow line.baseflow_color (
str, default"darkorange") – Color for the baseflow line.fill_alpha (
float, default0.3) – Alpha for the shaded quickflow area.log_scale (
bool, defaultFalse) – If True, use log scale for the y-axis.title (
str, default"Streamflow Hydrograph") – Plot title.ylabel (
str, default"Flow") – Y-axis label prefix.units (
str, default"cfs") – Flow units appended to ylabel.figsize (
tuple, default(14,6)) – Figure size in inches.
- Returns:
(Figure, Axes) matplotlib objects.
- Return type:
- class pyiwfm.visualization.plotting.BudgetPlotter(budgets, times=None, units='AF')[source]#
Bases:
objectHigh-level class for creating budget visualizations.
This class provides convenience methods for creating various budget-related plots from IWFM model output data.
- Parameters:
Examples
>>> plotter = BudgetPlotter(budgets={'Precip': 1000, 'ET': -600}) >>> fig, ax = plotter.bar_chart() >>> plotter.save('budget.png')
- pyiwfm.visualization.plotting.plot_budget_bar(components, ax=None, title='Water Budget', orientation='vertical', inflow_color='steelblue', outflow_color='coral', show_values=True, units='AF', figsize=(10, 6))[source]#
Plot water budget components as a bar chart.
- Parameters:
components (
dict) – Dictionary of component names to values. Positive values are inflows, negative values are outflows.ax (
Axes, optional) – Existing axes to plot on.title (
str, default"Water Budget") – Plot title.orientation (
{'vertical', 'horizontal'}, default'vertical') – Bar orientation.inflow_color (
str, default"steelblue") – Color for inflow (positive) bars.outflow_color (
str, default"coral") – Color for outflow (negative) bars.units (
str, default"AF") – Units for y-axis label and values.figsize (
tuple, default(10,6)) – Figure size.
- Returns:
(Figure, Axes) matplotlib objects.
- Return type:
Examples
>>> budget = { ... 'Precipitation': 1500, ... 'Stream Inflow': 800, ... 'Pumping': -1200, ... 'Evapotranspiration': -600, ... 'Stream Outflow': -400, ... } >>> fig, ax = plot_budget_bar(budget, title='Annual Water Budget')
- pyiwfm.visualization.plotting.plot_budget_horizontal_bars(inflows, outflows, storage_change=0.0, discrepancy=0.0, ax=None, table_ax=None, title='Water Budget', inflow_label='Inflows', outflow_label='Outflows', units='AF/yr', inflow_colors=None, outflow_colors=None, color_map=None, show_table=True, table_fontsize=7.0, number_fontsize=6.5, figsize=(6.5, 4.0))[source]#
Horizontal stacked bar chart for water budget data.
Two horizontal bars (inflows, outflows) with stacked colored segments, plus a smaller bar for storage change. Segments are numbered with white-on-outline text. An optional legend table maps numbers to component names and values.
- Parameters:
inflows (
listof(name,value) tuples) – Inflow components sorted largest-first.outflows (
listof(name,value) tuples) – Outflow components sorted largest-first.storage_change (
float) – Net storage change (positive = increase).discrepancy (
float) – Balance error / discrepancy.ax (
Axes, optional) – Axes for the bar chart. Creates new figure if None.table_ax (
Axes, optional) – Axes for the legend table. If None and show_table is True, a table is placed below the chart.title (
str) – Chart title.inflow_label (
str) – Category labels for the y-axis.outflow_label (
str) – Category labels for the y-axis.units (
str) – Units string for x-axis label and table header.inflow_colors (
sequenceofstr, optional) – Override palettes for inflow/outflow segments.outflow_colors (
sequenceofstr, optional) – Override palettes for inflow/outflow segments.color_map (
dict, optional) – Map of component name -> hex color. Falls back to palette.show_table (
bool) – Whether to draw the legend table.table_fontsize (
float) – Font size for the legend table.number_fontsize (
float) – Font size for segment numbers on bars.figsize (
tuple) – Figure size when creating a new figure.
- Returns:
(Figure, Axes) matplotlib objects.
- Return type:
- pyiwfm.visualization.plotting.plot_budget_stacked(times, components, ax=None, title='Water Budget Over Time', inflows_above=True, cmap='tab10', alpha=0.8, units='AF', show_legend=True, legend_loc='outside lower center', figsize=(14, 7))[source]#
Plot water budget components as stacked area chart over time.
- Parameters:
times (
array) – Time array (datetime64).components (
dict) – Dictionary of component names to time series arrays. Positive values are inflows, negative values are outflows.ax (
Axes, optional) – Existing axes to plot on.title (
str, default"Water Budget Over Time") – Plot title.inflows_above (
bool, defaultTrue) – Plot inflows above x-axis and outflows below.cmap (
str, default"tab10") – Colormap for components.alpha (
float, default0.8) – Fill transparency.units (
str, default"AF") – Units for y-axis label.legend_loc (
str, default"outside lower center") – Legend location string passed tofig.legend(loc=...).figsize (
tuple, default(14,7)) – Figure size.
- Returns:
(Figure, Axes) matplotlib objects.
- Return type:
Examples
>>> times = np.array(['2020-01', '2020-02', '2020-03'], dtype='datetime64') >>> components = { ... 'Precipitation': np.array([100, 150, 80]), ... 'Pumping': np.array([-50, -60, -55]), ... } >>> fig, ax = plot_budget_stacked(times, components)
- pyiwfm.visualization.plotting.plot_budget_pie(components, ax=None, title='Water Budget Distribution', budget_type='both', cmap='tab10', show_values=True, units='AF', figsize=(10, 8))[source]#
Plot water budget components as pie chart(s).
- Parameters:
components (
dict) – Dictionary of component names to values.ax (
Axes, optional) – Existing axes (ignored if budget_type=’both’).title (
str, default"Water Budget Distribution") – Plot title.budget_type (
{'inflow', 'outflow', 'both'}, default'both') – Which components to show.cmap (
str, default"tab10") – Colormap for slices.units (
str, default"AF") – Units for value labels.figsize (
tuple, default(10,8)) – Figure size.
- Returns:
(Figure, Axes) matplotlib objects.
- Return type:
- pyiwfm.visualization.plotting.plot_water_balance(inflows, outflows, storage_change=0.0, ax=None, title='Water Balance Summary', units='AF', figsize=(12, 6))[source]#
Plot comprehensive water balance summary with inflows, outflows, and storage.
- Parameters:
inflows (
dict) – Dictionary of inflow component names to values.outflows (
dict) – Dictionary of outflow component names to values (positive values).storage_change (
float, default0.0) – Change in storage (positive = increase).ax (
Axes, optional) – Existing axes to plot on.title (
str, default"Water Balance Summary") – Plot title.units (
str, default"AF") – Volume units.figsize (
tuple, default(12,6)) – Figure size.
- Returns:
(Figure, Axes) matplotlib objects.
- Return type:
Examples
>>> inflows = {'Precip': 1000, 'Stream In': 500, 'Recharge': 200} >>> outflows = {'ET': 600, 'Pumping': 800, 'Stream Out': 300} >>> fig, ax = plot_water_balance(inflows, outflows, storage_change=-100)
- pyiwfm.visualization.plotting.plot_zbudget(zone_budgets, ax=None, title='Zone Budget Summary', plot_type='bar', units='AF', cmap='RdYlBu', figsize=(12, 8))[source]#
Plot zone budget data for multiple zones.
- Parameters:
zone_budgets (
dict) – Dictionary mapping zone ID to budget component dictionaries. Example: {1: {‘Inflow’: 100, ‘Outflow’: -80}, 2: {…}}ax (
Axes, optional) – Existing axes to plot on.title (
str, default"Zone Budget Summary") – Plot title.plot_type (
{'bar', 'heatmap'}, default'bar') – Type of plot to create.units (
str, default"AF") – Volume units.cmap (
str, default"RdYlBu") – Colormap for heatmap.figsize (
tuple, default(12,8)) – Figure size.
- Returns:
(Figure, Axes) matplotlib objects.
- Return type:
Examples
>>> zone_budgets = { ... 1: {'Recharge': 500, 'Pumping': -300, 'Flow to Zone 2': -100}, ... 2: {'Recharge': 300, 'Pumping': -200, 'Flow from Zone 1': 100}, ... } >>> fig, ax = plot_zbudget(zone_budgets, title='Subregion Budgets')
- pyiwfm.visualization.plotting.plot_budget_timeseries(times, budgets, cumulative=False, ax=None, title='Budget Components Over Time', units='AF', show_net=True, figsize=(14, 6))[source]#
Plot budget component time series as line charts.
- Parameters:
times (
array) – Time array (datetime64).budgets (
dict) – Dictionary of component names to value arrays.ax (
Axes, optional) – Existing axes to plot on.title (
str, default"Budget Components Over Time") – Plot title.units (
str, default"AF") – Volume units.figsize (
tuple, default(14,6)) – Figure size.
- Returns:
(Figure, Axes) matplotlib objects.
- Return type:
- pyiwfm.visualization.plotting.plot_streams_colored(grid, streams, values, ax=None, cmap='Blues', vmin=None, vmax=None, line_width=2.0, show_colorbar=True, colorbar_label='', show_mesh=True, mesh_alpha=0.15, figsize=(10, 8))[source]#
Color stream reaches by a scalar value (e.g., flow rate, gaining/losing).
- Parameters:
grid (
AppGrid) – Model mesh (plotted as background when show_mesh is True).streams (
AppStream) – Stream network.values (
ndarray) – One value per reach, used for coloring.ax (
Axes, optional) – Existing axes to plot on.cmap (
str, default"Blues") – Matplotlib colormap name.vmin (
float, optional) – Limits for the color scale.vmax (
float, optional) – Limits for the color scale.line_width (
float, default2.0) – Width of stream lines.show_colorbar (
bool, defaultTrue) – Whether to add a colorbar.colorbar_label (
str, default"") – Label for the colorbar.show_mesh (
bool, defaultTrue) – Whether to draw the mesh as background.mesh_alpha (
float, default0.15) – Alpha for mesh background.figsize (
tuple, default(10,8)) – Figure size in inches.
- Returns:
(Figure, Axes) matplotlib objects.
- Return type:
- pyiwfm.visualization.plotting.plot_cross_section(cross_section, ax=None, layer_colors=None, layer_labels=None, scalar_name=None, layer_property_name=None, layer_property_cmap='viridis', show_ground_surface=True, alpha=0.7, title=None, figsize=(14, 6))[source]#
Plot a cross-section through an IWFM model.
Supports three rendering modes that can be combined:
Default (no property): Layers filled with flat colors via
fill_between.Layer property (
layer_property_name): Each layer band is color-mapped by a per-layer property (e.g. hydraulic conductivity).Scalar overlay (
scalar_name): A dashed line showing a per-sample scalar value (e.g. water table elevation).
- Parameters:
cross_section (
CrossSection) – Cross-section data fromCrossSectionExtractor.ax (
Axes, optional) – Existing axes to plot on. Creates a new figure if None.layer_colors (
sequenceofstr, optional) – Fill colors for each layer (used whenlayer_property_nameis None). Defaults to brown tones.layer_labels (
sequenceofstr, optional) – Legend labels for each layer. Defaults to “Layer 1”, “Layer 2”, etc.scalar_name (
str, optional) – Key intocross_section.scalar_valuesto overlay as a line.layer_property_name (
str, optional) – Key intocross_section.layer_propertiesto color-map layers.layer_property_cmap (
str, default"viridis") – Colormap used for layer property rendering.show_ground_surface (
bool, defaultTrue) – Draw the ground surface as a green line.alpha (
float, default0.7) – Fill transparency.title (
str, optional) – Plot title.figsize (
tuple, default(14,6)) – Figure size in inches.
- Returns:
(Figure, Axes)matplotlib objects.- Return type:
- pyiwfm.visualization.plotting.plot_cross_section_location(grid, cross_section, ax=None, line_color='red', line_width=2.5, mesh_alpha=0.3, show_labels=True, figsize=(10, 8))[source]#
Plot a plan-view map showing the cross-section line on the mesh.
- Parameters:
grid (
AppGrid) – Model mesh.cross_section (
CrossSection) – Cross-section whose path will be drawn.ax (
Axes, optional) – Existing axes. Creates a new figure if None.line_color (
str, default"red") – Color of the cross-section line.line_width (
float, default2.5) – Width of the cross-section line.mesh_alpha (
float, default0.3) – Transparency of the mesh underlay.show_labels (
bool, defaultTrue) – Show A / A’ labels at line endpoints.figsize (
tuple, default(10,8)) – Figure size in inches.
- Returns:
(Figure, Axes)matplotlib objects.- Return type:
- pyiwfm.visualization.plotting.plot_one_to_one(observed, simulated, ax=None, color_by=None, show_metrics=True, show_identity=True, show_regression=True, title=None, units='', figsize=(8, 8))[source]#
Plot a 1:1 comparison of observed vs simulated values.
- Parameters:
observed (
NDArray[np.float64]) – Observed values.simulated (
NDArray[np.float64]) – Simulated values.ax (
Axes | None) – Existing axes to plot on.color_by (
NDArray[np.float64] | None) – Optional array to color scatter points by.show_metrics (
bool) – Show a text box with RMSE, NSE, etc.show_identity (
bool) – Show the 1:1 identity line.show_regression (
bool) – Show a linear regression line.title (
str | None) – Plot title.units (
str) – Unit label for axes.figsize (
tuple[float,float]) – Figure size.
- Returns:
The figure and axes.
- Return type:
tuple[Figure,Axes]
- pyiwfm.visualization.plotting.plot_residual_cdf(residuals, ax=None, show_percentile_lines=True, title='Cumulative Frequency of Residuals', figsize=(8, 6))[source]#
Plot an empirical CDF (cumulative frequency) of residuals.
- Parameters:
residuals (
NDArray[np.float64]) – Array of residual values (simulated - observed).ax (
Axes | None) – Existing axes to plot on.show_percentile_lines (
bool, defaultTrue) – Draw reference lines at the 5th and 95th percentiles and a vertical line at zero.title (
str, default"Cumulative Frequency of Residuals") – Plot title.figsize (
tuple[float,float], default(8,6)) – Figure size in inches.
- Returns:
The figure and axes.
- Return type:
tuple[Figure,Axes]
- pyiwfm.visualization.plotting.plot_spatial_bias(grid, x, y, bias, ax=None, show_mesh=True, cmap='RdBu_r', symmetric_colorbar=True, title='Spatial Bias', units='', figsize=(10, 8))[source]#
Plot spatial bias (simulated - observed) at observation locations.
- Parameters:
grid (
AppGrid) – Model grid for background mesh.x (
NDArray[np.float64]) – X coordinates of observation points.y (
NDArray[np.float64]) – Y coordinates of observation points.bias (
NDArray[np.float64]) – Bias values (simulated - observed).ax (
Axes | None) – Existing axes to plot on.show_mesh (
bool) – Show the mesh as background.cmap (
str) – Colormap name (should be diverging).symmetric_colorbar (
bool) – Center the colorbar at 0.title (
str) – Plot title.units (
str) – Unit label for colorbar.figsize (
tuple[float,float]) – Figure size.
- Returns:
The figure and axes.
- Return type:
tuple[Figure,Axes]
Interactive Web Viewer#
The web viewer is a FastAPI backend + React SPA frontend with four tabs: Overview, 3D Mesh (vtk.js), Results Map (deck.gl + MapLibre), and Budgets (Plotly).
Configuration#
The ModelState singleton that holds the loaded IWFMModel and provides
lazy getters for head data, budget data, stream reach boundaries, and
coordinate reprojection.
Configuration settings for the FastAPI web viewer.
- class pyiwfm.visualization.webapi.config.ViewerSettings(*args, **kwargs)[source]#
Bases:
BaseModelSettings for the web viewer.
- model_config = {'extra': 'forbid'}#
- class pyiwfm.visualization.webapi.config.ModelState[source]#
Bases:
MeshStateMixin,ResultsStateMixin,BudgetStateMixin,CacheStateMixinHolds the loaded model and derived data for the API.
This is a singleton-like container that holds the model and precomputed meshes for efficient API responses.
- MAX_OBSERVATIONS = 100#
- set_model(model, crs='+proj=utm +zone=10 +datum=NAD83 +units=us-ft +no_defs', no_cache=False, rebuild_cache=False)[source]#
Set the model and reset caches.
Thread-safe: acquires lock to prevent concurrent requests from seeing partially reset state.
- add_observation(obs_id, data)[source]#
Store an uploaded observation dataset.
- Raises:
ValueError – If the maximum number of observations has been reached.
Server#
FastAPI application creation, CRS configuration, and static file serving.
FastAPI server for the IWFM web viewer.
- pyiwfm.visualization.webapi.server.create_app(model=None, settings=None)[source]#
Create the FastAPI application.
- Parameters:
model (
IWFMModel, optional) – Pre-loaded IWFM modelsettings (
ViewerSettings, optional) – Viewer settings
- Returns:
Configured FastAPI application
- Return type:
FastAPI
- pyiwfm.visualization.webapi.server.launch_viewer(model, host='127.0.0.1', port=8080, title='IWFM Viewer', open_browser=True, debug=False, crs='+proj=utm +zone=10 +datum=NAD83 +units=us-ft +no_defs', no_cache=False, rebuild_cache=False, observation_paths=None)[source]#
Launch the web viewer server.
- Parameters:
model (
IWFMModel) – The model to visualizehost (
str) – Server host addressport (
int) – Server porttitle (
str) – Application titleopen_browser (
bool) – Whether to open browser on startdebug (
bool) – Enable debug modecrs (
str) – Source coordinate reference system (default: UTM Zone 10N, NAD83, US survey feet)no_cache (
bool) – Disable SQLite cache layerrebuild_cache (
bool) – Force rebuild of SQLite cacheobservation_paths (
list[Path], optional) – Observation files (.smp, .csv) or directories to load at startup
Head Data Loader#
Moved to pyiwfm.io.head_loader. See I/O Modules for full docs.
Hydrograph Reader#
Moved to pyiwfm.io.hydrograph_reader. See I/O Modules for full docs.
Properties#
Property extraction and caching for the web viewer.
Property visualization component for IWFM web visualization.
This module provides the PropertyVisualizer class for managing the display of aquifer properties on the 3D mesh.
- class pyiwfm.visualization.webapi.properties.PropertyVisualizer(mesh, stratigraphy=None, aquifer_params=None)[source]#
Bases:
objectManages visualization of aquifer properties on the mesh.
This class handles the selection and display of different aquifer properties (Kh, Kv, Ss, Sy, head) on the 3D mesh visualization. It manages colormaps, value ranges, and layer filtering.
- Parameters:
mesh (
pv.UnstructuredGrid) – The PyVista mesh to add properties to.stratigraphy (
Stratigraphy, optional) – Model stratigraphy for computing layer-based properties.aquifer_params (
object, optional) – Aquifer parameters object containing Kh, Kv, Ss, Sy values.
- Variables:
- DEFAULT_COLORMAPS = {'bottom_elev': 'terrain', 'head': 'coolwarm', 'kh': 'turbo', 'kv': 'turbo', 'layer': 'viridis', 'ss': 'plasma', 'sy': 'plasma', 'thickness': 'viridis', 'top_elev': 'terrain'}#
- property active_scalars: ndarray[tuple[Any, ...], dtype[float64]] | None#
Get the scalar array for the currently active property.
- set_active_property(name)[source]#
Set the displayed property.
- Parameters:
name (
str) – Property name: ‘layer’, ‘kh’, ‘kv’, ‘ss’, ‘sy’, ‘head’, ‘thickness’, ‘top_elev’, ‘bottom_elev’.- Raises:
ValueError – If the property name is not recognized or not available.
- set_layer(layer)[source]#
Set the active layer for display.
- Parameters:
layer (
int) – Layer number (1-indexed). Use 0 to show all layers.
- set_colormap(cmap)[source]#
Set the colormap for property display.
- Parameters:
cmap (
str) – Matplotlib colormap name (e.g., ‘viridis’, ‘coolwarm’, ‘jet’).
- set_auto_range(auto=True)[source]#
Enable or disable automatic range calculation.
- Parameters:
auto (
bool) – If True, automatically calculate range from data.
- add_head_data(head, layer=None)[source]#
Add head data to the mesh.
- Parameters:
head (
NDArray) – Head values. Shape should be (n_nodes,) for single layer or (n_nodes, n_layers) for all layers.layer (
int, optional) – If provided, the layer for 1D head data.
Slicing#
Cross-section slice plane computation.
Slicing controller for IWFM web visualization.
This module provides the SlicingController class for interactive slicing of 3D meshes along various planes.
- class pyiwfm.visualization.webapi.slicing.SlicingController(mesh)[source]#
Bases:
objectControls for slicing the 3D model along planes.
This class provides methods for creating slices through the model mesh along axis-aligned planes, arbitrary angles, and custom cross-sections defined by two points on the map.
- Parameters:
mesh (
pv.UnstructuredGrid) – The PyVista mesh to slice.- Variables:
mesh (
pv.UnstructuredGrid) – The mesh being sliced.bounds (
tuple) – Mesh bounding box (xmin, xmax, ymin, ymax, zmin, zmax).
Examples
>>> slicer = SlicingController(mesh) >>> slice_x = slicer.slice_x(position=1000.0) >>> slice_z = slicer.slice_z(position=-50.0)
- property bounds: tuple[float, float, float, float, float, float]#
Get mesh bounds (xmin, xmax, ymin, ymax, zmin, zmax).
- slice_x(position)[source]#
Create a slice perpendicular to the X axis (YZ plane).
- Parameters:
position (
float) – X coordinate for the slice plane.- Returns:
The slice result as a surface mesh.
- Return type:
pv.PolyData
Examples
>>> slice_mesh = slicer.slice_x(position=1500.0) >>> slice_mesh.plot()
- slice_y(position)[source]#
Create a slice perpendicular to the Y axis (XZ plane).
- Parameters:
position (
float) – Y coordinate for the slice plane.- Returns:
The slice result as a surface mesh.
- Return type:
pv.PolyData
Examples
>>> slice_mesh = slicer.slice_y(position=2000.0) >>> slice_mesh.plot()
- slice_z(position)[source]#
Create a horizontal slice at a specific elevation (XY plane).
- Parameters:
position (
float) – Z (elevation) coordinate for the slice plane.- Returns:
The slice result as a surface mesh.
- Return type:
pv.PolyData
Examples
>>> # Slice at -100 ft elevation >>> slice_mesh = slicer.slice_z(position=-100.0) >>> slice_mesh.plot()
- slice_arbitrary(normal, origin=None)[source]#
Create a slice along an arbitrary plane.
- Parameters:
- Returns:
The slice result as a surface mesh.
- Return type:
pv.PolyData
Examples
>>> # Slice at 45 degrees in XZ plane >>> slice_mesh = slicer.slice_arbitrary( ... normal=(0.707, 0, 0.707), ... origin=(1000, 2000, 0), ... )
- create_cross_section(start, end, n_samples=100)[source]#
Create a vertical cross-section between two map points.
This method creates a vertical slice through the model along a line drawn on the map (2D view). The resulting cross-section shows the full depth of the model along that transect.
- Parameters:
start (
tuple[float,float]) – Starting point (x, y) on the map.end (
tuple[float,float]) – Ending point (x, y) on the map.n_samples (
int, optional) – Number of sample points along the transect line. Default is 100.
- Returns:
The cross-section as a surface mesh.
- Return type:
pv.PolyData
Examples
>>> # Create cross-section from point A to point B >>> cross_section = slicer.create_cross_section( ... start=(1000, 2000), ... end=(5000, 3000), ... ) >>> cross_section.plot()
Notes
The cross-section is computed by creating a vertical slice plane that passes through both start and end points. The plane extends from the top to the bottom of the mesh.
- slice_along_polyline(points, resolution=100)[source]#
Create a vertical cross-section along a polyline path.
- Parameters:
points (
list[tuple[float,float]]) – List of (x, y) points defining the polyline path.resolution (
int, optional) – Number of sample points per segment. Default is 100.
- Returns:
Combined cross-section mesh.
- Return type:
pv.PolyData
Examples
>>> # Create cross-section along a stream path >>> path = [(1000, 2000), (2000, 2500), (3000, 2000)] >>> cross_section = slicer.slice_along_polyline(path)
- slice_box(bounds=None, invert=False)[source]#
Extract a box region from the mesh.
- Parameters:
- Returns:
Extracted mesh region.
- Return type:
pv.UnstructuredGrid
- slice_multiple_z(positions=None, n_slices=5)[source]#
Create multiple horizontal slices at specified elevations.
- Parameters:
positions (
list[float], optional) – Z (elevation) coordinates for slice planes. If None, evenly space n_slices through the mesh.n_slices (
int, optional) – Number of slices if positions not specified. Default is 5.
- Returns:
List of slice meshes.
- Return type:
list[pv.PolyData]
Examples
>>> # Get 5 evenly spaced horizontal slices >>> slices = slicer.slice_multiple_z(n_slices=5) >>> for i, s in enumerate(slices): ... s.save(f"slice_{i}.vtk")
- get_slice_properties(slice_mesh)[source]#
Get properties of a slice result.
- Parameters:
slice_mesh (
pv.PolyData) – A slice mesh.- Returns:
Dictionary with slice properties: - n_cells: Number of cells in the slice - n_points: Number of points in the slice - area: Total area of the slice - bounds: Bounding box of the slice - arrays: Available data arrays
- Return type:
- normalized_to_position_along(normal, normalized)[source]#
Convert a 0-1 normalized position to world coordinates along an arbitrary normal.
Projects the mesh bounding box onto the given normal direction and interpolates between the minimum and maximum projections.
API Routes#
The web viewer backend exposes a REST API under /api/. Key route groups:
Model (
/api/model): Load model info, compare with a second modelMesh (
/api/mesh): GeoJSON mesh, element details, node lookupsResults (
/api/results): Head values, drawdown with pagination (offset/limit/skip), head statistics (min/max/mean/std across timesteps), subsidence surfaceDiagnostics (
/api/diagnostics): Simulation messages, convergence tracking, mass balance errors, spatial summaryBudgets (
/api/budgets): Water budget time series by type and locationExport (
/api/export): CSV downloads (heads, budgets, hydrographs), GeoJSON mesh, GeoPackage (multi-layer), matplotlib plot generation (PNG/SVG), and HTML/JSON diagnostic reportsObservations (
/api/observations): Upload observed data for hydrograph comparisonGroundwater, Streams, Lakes, Root Zone, Small Watersheds, Unsaturated Zone (
/api/groundwater,/api/streams,/api/small-watersheds,/api/unsaturated-zone, etc.): Component-specific endpoints
Diagnostics Routes#
Endpoints for simulation diagnostic analysis including convergence tracking, mass balance error timeseries, and spatial message summaries.
Diagnostics API routes for simulation messages, convergence, and mass balance.
- pyiwfm.visualization.webapi.routes.diagnostics.get_messages(severity=fastapi.Query, limit=fastapi.Query, offset=fastapi.Query)#
Parse and return simulation messages with optional severity filtering.
- pyiwfm.visualization.webapi.routes.diagnostics.get_convergence()#
Return convergence iteration data from simulation messages.
- pyiwfm.visualization.webapi.routes.diagnostics.get_convergence_hotspots()#
Return convergence hotspot variables with spatial coordinates.
- pyiwfm.visualization.webapi.routes.diagnostics.get_hotspot_context(variable=fastapi.Query, timestep_index=fastapi.Query)#
Return spatial context and iteration history for a convergence hotspot.
- pyiwfm.visualization.webapi.routes.diagnostics.get_element_budget(element_id=fastapi.Query, timestep_index=fastapi.Query, layer=fastapi.Query)#
Fetch GW ZBudget data for a single element.
- pyiwfm.visualization.webapi.routes.diagnostics.get_stream_node_budget(stream_node_id=fastapi.Query, timestep_index=fastapi.Query)#
Fetch stream node budget data for a specific stream node.
- pyiwfm.visualization.webapi.routes.diagnostics.get_mass_balance(component=fastapi.Query)#
Return mass balance error timeseries from simulation messages.
- pyiwfm.visualization.webapi.routes.diagnostics.get_summary()#
Combined diagnostics summary.
- pyiwfm.visualization.webapi.routes.diagnostics.get_spatial_summary()#
Spatial summary of message counts for map overlay.
Unsaturated Zone Routes#
Endpoints for unsaturated zone component information and parameter summaries.
Unsaturated zone API routes.
- pyiwfm.visualization.webapi.routes.unsaturated_zone.get_unsaturated_zone()#
Get unsaturated zone component information.
- pyiwfm.visualization.webapi.routes.unsaturated_zone.get_unsaturated_zone_summary()#
Get summary of unsaturated zone parameters.