Structures and Metadata from Geometry Files¶
This notebook demonstrates parsing bridge, culvert, inline weir, and geometry metadata from HEC-RAS plain text geometry files, including: - GeomMetadata: Efficient geometry element counts (HDF-first with text fallback) - GeomBridge: Bridge deck, piers, coefficients, and HTAB parameters - GeomCulvert: Culvert inventory across entire geometry files - GeomInlineWeir: Inline weir profiles and gate configurations
Overview¶
HEC-RAS geometry files contain inline structures (bridges, culverts, inline weirs) that control hydraulic behavior at specific cross-section locations. This notebook demonstrates the geom subpackage modules for extracting structure data.
What You'll Learn¶
- Get a quick summary of geometry contents with
GeomMetadata - Extract bridge deck geometry, pier data, and hydraulic coefficients with
GeomBridge - List all culverts and their dimensions with
GeomCulvert - Read inline weir profiles and gate configurations with
GeomInlineWeir
LLM Forward Approach¶
- Verification: Compare extracted DataFrames against HEC-RAS GUI
- Visual Outputs: Plot bridge deck profiles and weir crest lines
- Audit Trail: Log all geometry file paths and structure counts
Reference Documentation¶
# =============================================================================
# DEVELOPMENT MODE TOGGLE
# =============================================================================
from pathlib import Path
import sys
USE_LOCAL_SOURCE = True # <-- TOGGLE THIS
if USE_LOCAL_SOURCE:
local_path = str(Path.cwd().parent)
if local_path not in sys.path:
sys.path.insert(0, local_path)
print(f"LOCAL SOURCE MODE: Loading from {local_path}/ras_commander")
else:
print("PIP PACKAGE MODE: Loading installed ras-commander")
# Import RAS Commander modules
from ras_commander import (
RasExamples, init_ras_project, RasPrj,
GeomBridge, GeomCulvert, GeomInlineWeir, GeomLandCover,
)
from ras_commander.geom import GeomMetadata
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from IPython import display
# Verify which version loaded
import ras_commander
print(f"Loaded: {ras_commander.__file__}")
print(f"Working directory: {Path.cwd()}")
Parameters¶
# =============================================================================
# PARAMETERS - Edit these to customize the notebook
# =============================================================================
# Projects containing structures
PROJECT_BRIDGE = "Bridge Hydraulics" # Has bridge structures
PROJECT_CULVERT = "ConSpan Culvert" # Has culvert structures
PROJECT_WEIR = "Example 12 - Inline Structure" # Has inline weir with gates
PROJECT_2D = "BaldEagleCrkMulti2D" # For metadata comparison
PROJECT_SUFFIX = "206" # Folder suffix for extracted projects
RAS_VERSION = "7.0" # HEC-RAS version
Setup: Extract Example Projects¶
# Extract all projects needed for this notebook
project_paths = RasExamples.extract_project(
[PROJECT_BRIDGE, PROJECT_CULVERT, PROJECT_WEIR, PROJECT_2D],
suffix=PROJECT_SUFFIX
)
bridge_path = project_paths[0]
culvert_path = project_paths[1]
weir_path = project_paths[2]
bald_eagle_path = project_paths[3]
# Initialize each project
bridge_ras = RasPrj()
init_ras_project(bridge_path, RAS_VERSION, ras_object=bridge_ras)
print(f"Bridge project: {bridge_ras.project_name} ({len(bridge_ras.geom_df)} geom files)")
culvert_ras = RasPrj()
init_ras_project(culvert_path, RAS_VERSION, ras_object=culvert_ras)
print(f"Culvert project: {culvert_ras.project_name} ({len(culvert_ras.geom_df)} geom files)")
weir_ras = RasPrj()
init_ras_project(weir_path, RAS_VERSION, ras_object=weir_ras)
print(f"Weir project: {weir_ras.project_name} ({len(weir_ras.geom_df)} geom files)")
bald_eagle_ras = RasPrj()
init_ras_project(bald_eagle_path, RAS_VERSION, ras_object=bald_eagle_ras)
print(f"2D project: {bald_eagle_ras.project_name} ({len(bald_eagle_ras.geom_df)} geom files)")
Section 1: GeomMetadata - Geometry Element Counts¶
GeomMetadata.get_geometry_counts() provides a quick summary of geometry file contents. It prefers HDF-based extraction (~10-50ms) and falls back to plain text parsing (~100-500ms) when HDF is unavailable.
# Get geometry counts for the bridge project
bridge_geom_row = bridge_ras.geom_df.iloc[0]
bridge_geom_path = Path(bridge_geom_row['full_path'])
bridge_hdf_path = Path(bridge_geom_row['hdf_path']) if pd.notna(bridge_geom_row.get('hdf_path')) else None
print(f"Geometry file: {bridge_geom_path.name}")
print(f"HDF available: {bridge_hdf_path is not None and bridge_hdf_path.exists()}")
print()
counts = GeomMetadata.get_geometry_counts(bridge_geom_path, bridge_hdf_path)
print("Geometry Element Counts:")
print(f" Cross sections: {counts['num_cross_sections']}")
print(f" Bridges: {counts['num_bridges']}")
print(f" Culverts: {counts['num_culverts']}")
print(f" Inline weirs: {counts['num_weirs']}")
print(f" Gates: {counts['num_gates']}")
print(f" Inline structures: {counts['num_inline_structures']} (bridges + culverts + weirs)")
print(f" Lateral structures: {counts['num_lateral_structures']}")
print(f" SA/2D connections: {counts['num_sa_2d_connections']}")
print(f" Has 1D XS: {counts['has_1d_xs']}")
print(f" Has 2D mesh: {counts['has_2d_mesh']}")
print(f" Mesh areas: {counts['mesh_area_names']}")
print(f" Mesh cells: {counts['mesh_cell_count']}")
Compare Counts Across Projects¶
Run get_geometry_counts() on multiple projects to see the range of geometry types.
# Compare counts across all four projects
projects = {
'Bridge Hydraulics': bridge_ras,
'ConSpan Culvert': culvert_ras,
'Inline Structure': weir_ras,
'BaldEagleCrk 2D': bald_eagle_ras,
}
summary_rows = []
for name, ras_obj in projects.items():
geom_row = ras_obj.geom_df.iloc[0]
geom_path = Path(geom_row['full_path'])
hdf_path = Path(geom_row['hdf_path']) if pd.notna(geom_row.get('hdf_path')) else None
c = GeomMetadata.get_geometry_counts(geom_path, hdf_path)
summary_rows.append({
'Project': name,
'XS': c['num_cross_sections'],
'Bridges': c['num_bridges'],
'Culverts': c['num_culverts'],
'Weirs': c['num_weirs'],
'Gates': c['num_gates'],
'Laterals': c['num_lateral_structures'],
'2D Areas': len(c['mesh_area_names']),
'Mesh Cells': c['mesh_cell_count'],
})
summary_df = pd.DataFrame(summary_rows)
print("Geometry Element Summary Across Projects:")
display.display(summary_df)
Section 2: GeomBridge - Bridge Extraction¶
The Bridge Hydraulics example project contains a bridge structure with deck geometry, piers, and hydraulic coefficients.
2.1 List All Bridges¶
# List all bridges in the geometry file
bridges_df = GeomBridge.get_bridges(bridge_geom_path)
print(f"Bridges found: {len(bridges_df)}")
print()
display.display(bridges_df)
2.2 Bridge Deck Geometry¶
The deck profile defines the station-elevation points of the bridge deck (high chord) and low chord.
# Extract deck geometry for the first bridge
br = bridges_df.iloc[0]
river = br['River']
reach = br['Reach']
rs = br['RS']
print(f"Bridge at {river}, {reach}, RS {rs}")
print()
deck_df = GeomBridge.get_deck(bridge_geom_path, river, reach, rs)
print(f"Deck points: {len(deck_df)}")
display.display(deck_df)
# Visualize bridge deck profile
if len(deck_df) > 0:
fig, ax = plt.subplots(figsize=(12, 5))
if 'HighChord' in deck_df.columns and 'LowChord' in deck_df.columns:
ax.plot(deck_df['Station'], deck_df['HighChord'], 'b-o', label='High Chord (Top of Deck)', markersize=4)
ax.plot(deck_df['Station'], deck_df['LowChord'], 'r-o', label='Low Chord (Bottom of Deck)', markersize=4)
ax.fill_between(deck_df['Station'], deck_df['LowChord'], deck_df['HighChord'],
alpha=0.3, color='gray', label='Deck Cross-Section')
elif 'Elevation' in deck_df.columns:
ax.plot(deck_df['Station'], deck_df['Elevation'], 'b-o', label='Deck Elevation', markersize=4)
ax.set_xlabel('Station (ft)', fontsize=11)
ax.set_ylabel('Elevation (ft)', fontsize=11)
ax.set_title(f'Bridge Deck Profile: {river}, {reach}, RS {rs}', fontsize=13, fontweight='bold')
ax.legend(fontsize=10)
ax.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
2.3 Pier Data¶
# Extract pier definitions
piers_df = GeomBridge.get_piers(bridge_geom_path, river, reach, rs)
print(f"Piers found: {len(piers_df)}")
if len(piers_df) > 0:
print()
display.display(piers_df)
2.4 Hydraulic Coefficients¶
# Extract hydraulic coefficients
coeffs_df = GeomBridge.get_coefficients(bridge_geom_path, river, reach, rs)
print(f"Coefficient rows: {len(coeffs_df)}")
if len(coeffs_df) > 0:
print()
display.display(coeffs_df)
2.5 HTAB Parameters¶
# Extract hydraulic table parameters
htab_df = GeomBridge.get_htab(bridge_geom_path, river, reach, rs)
print("HTAB Parameters (DataFrame):")
display.display(htab_df)
# Also available as dict (includes invert elevation)
htab_dict = GeomBridge.get_htab_dict(bridge_geom_path, river, reach, rs)
print()
print("HTAB Parameters (dict):")
for key, value in htab_dict.items():
print(f" {key}: {value}")
2.6 Approach Sections and Abutment¶
# Extract approach sections (BR U and BR D cross sections)
approach_df = GeomBridge.get_approach_sections(bridge_geom_path, river, reach, rs)
print(f"Approach section points: {len(approach_df)}")
if len(approach_df) > 0:
display.display(approach_df.head(10))
# Extract abutment geometry (not all bridges have abutments)
if br.get('HasAbutment', False):
abutment_df = GeomBridge.get_abutment(bridge_geom_path, river, reach, rs)
print(f"\nAbutment points: {len(abutment_df)}")
if len(abutment_df) > 0:
display.display(abutment_df)
else:
print("\nNo abutment data for this bridge (HasAbutment=False)")
Section 3: GeomCulvert - Culvert Extraction¶
The ConSpan Culvert example project contains culvert structures. GeomCulvert.get_all() scans the entire geometry file for all culverts.
# Get the culvert geometry file
culvert_geom_row = culvert_ras.geom_df.iloc[0]
culvert_geom_path = Path(culvert_geom_row['full_path'])
print(f"Geometry file: {culvert_geom_path.name}")
print()
# List all culverts in the entire geometry file
all_culverts = GeomCulvert.get_all(culvert_geom_path)
print(f"Total culverts found: {len(all_culverts)}")
if len(all_culverts) > 0:
print(f"Columns: {list(all_culverts.columns)}")
print()
display.display(all_culverts)
# Summarize culvert properties
if len(all_culverts) > 0:
print("Culvert Summary:")
# Group by shape type
if 'ShapeName' in all_culverts.columns:
print("\nCulverts by shape type:")
print(all_culverts.groupby('ShapeName').size().to_string())
# Show dimensions
for _, culvert in all_culverts.iterrows():
name = culvert.get('CulvertName', 'Unknown')
shape = culvert.get('ShapeName', 'Unknown')
span = culvert.get('Span', 0)
rise = culvert.get('Rise', 0)
length = culvert.get('Length', 0)
mannings = culvert.get('ManningsN', 0)
print(f"\n {name} ({shape}):")
print(f" Span: {span:.1f} ft, Rise: {rise:.1f} ft")
print(f" Length: {length:.1f} ft")
print(f" Manning's n: {mannings}")
if 'UpstreamInvert' in all_culverts.columns:
print(f" US Invert: {culvert['UpstreamInvert']:.2f} ft, DS Invert: {culvert['DownstreamInvert']:.2f} ft")
Section 4: GeomInlineWeir - Inline Weir and Gate Extraction¶
The Example 12 project contains an inline weir structure with gate groups. GeomInlineWeir extracts the weir profile and gate configurations.
4.1 List Inline Weirs¶
# Get the weir geometry file
weir_geom_row = weir_ras.geom_df.iloc[0]
weir_geom_path = Path(weir_geom_row['full_path'])
print(f"Geometry file: {weir_geom_path.name}")
print()
# List all inline weirs
weirs_df = GeomInlineWeir.get_weirs(weir_geom_path)
print(f"Inline weirs found: {len(weirs_df)}")
if len(weirs_df) > 0:
display.display(weirs_df)
4.2 Weir Crest Profile¶
The weir profile defines the station-elevation points along the weir crest.
if len(weirs_df) > 0:
# Get the first weir's location
weir = weirs_df.iloc[0]
w_river = weir['River']
w_reach = weir['Reach']
w_rs = weir['RS']
print(f"Weir at {w_river}, {w_reach}, RS {w_rs}")
print()
# Extract weir profile
profile_df = GeomInlineWeir.get_profile(weir_geom_path, w_river, w_reach, w_rs)
print(f"Profile points: {len(profile_df)}")
display.display(profile_df)
else:
print("No inline weirs found")
# Visualize weir crest profile
if len(weirs_df) > 0 and len(profile_df) > 0:
fig, ax = plt.subplots(figsize=(12, 5))
ax.plot(profile_df['Station'], profile_df['Elevation'], 'b-o',
linewidth=2, markersize=5, label='Weir Crest')
ax.fill_between(profile_df['Station'], profile_df['Elevation'],
profile_df['Elevation'].min() - 5,
alpha=0.3, color='brown')
ax.set_xlabel('Station (ft)', fontsize=11)
ax.set_ylabel('Elevation (ft)', fontsize=11)
ax.set_title(f'Inline Weir Crest Profile: {w_river}, {w_reach}, RS {w_rs}',
fontsize=13, fontweight='bold')
ax.legend(fontsize=10)
ax.grid(True, alpha=0.3)
# Add statistics
crest_len = profile_df['Station'].max() - profile_df['Station'].min()
stats_text = '\n'.join([
f'Points: {len(profile_df)}',
f'Length: {crest_len:.0f} ft',
f'Elev: {profile_df["Elevation"].min():.1f} - {profile_df["Elevation"].max():.1f} ft',
])
ax.text(0.02, 0.98, stats_text, transform=ax.transAxes,
verticalalignment='top',
bbox=dict(boxstyle='round', facecolor='wheat', alpha=0.5), fontsize=10)
plt.tight_layout()
plt.show()
4.3 Gate Configurations¶
Inline weirs may include gate groups that control flow. Each gate group has opening stations defining where along the weir the gate is located.
if len(weirs_df) > 0:
# Extract gate configurations
gates_df = GeomInlineWeir.get_gates(weir_geom_path, w_river, w_reach, w_rs)
print(f"Gate groups found: {len(gates_df)}")
if len(gates_df) > 0:
print()
# Show gate parameters (excluding OpeningStations list for cleaner display)
display_cols = [c for c in gates_df.columns if c != 'OpeningStations']
display.display(gates_df[display_cols])
# Show opening stations for each gate group
print("\nGate opening stations:")
for _, gate in gates_df.iterrows():
name = gate.get('GateName', gate.get('Name', f'Gate {_}'))
stations = gate.get('OpeningStations', [])
n_openings = gate.get('NumOpenings', len(stations) if isinstance(stations, list) else 0)
print(f" {name}: {n_openings} openings")
if isinstance(stations, list) and len(stations) > 0:
for i, sta in enumerate(stations):
print(f" Opening {i+1}: station {sta}")
else:
print("No gates defined for this weir")
else:
print("No inline weirs found")
Summary¶
Methods Demonstrated¶
GeomMetadata (geometry element counting):
- get_geometry_counts(geom_path, hdf_path) - Quick inventory of all geometry elements
GeomBridge (bridge structures):
- get_bridges() - List all bridges with river/reach/RS
- get_deck() - Deck high chord and low chord geometry
- get_piers() - Pier widths and elevations
- get_coefficients() - Hydraulic loss coefficients
- get_htab() / get_htab_dict() - Hydraulic table parameters
- get_approach_sections() - BR U/BR D cross sections
- get_abutment() - Abutment geometry
GeomCulvert (culvert structures):
- get_all() - List all culverts across entire geometry file
- get_culverts() - List culverts at a specific bridge/culvert structure
GeomInlineWeir (inline weirs and gates):
- get_weirs() - List all inline weirs
- get_profile() - Station-elevation weir crest profile
- get_gates() - Gate group configurations with opening stations
Key Patterns¶
- All classes use static methods (no instantiation needed)
- Methods accept geometry file path as first argument
- Bridge/culvert/weir methods require river, reach, RS to identify the structure
- All methods return DataFrames for consistent data handling