Skip to content

mapping

Result raster generation for HEC-RAS projects using RasProcess.exe.

Overview

The mapping module generates georeferenced raster files (GeoTIFF) from completed HEC-RAS plan results. It wraps RasProcess.store_maps() from ras-commander, which deploys RasStoreMapHelper.exe to set the correct water surface render mode via .NET reflection before calling StoreAllMapsCommand. This produces pixel-perfect rasters matching the RASMapper GUI output.

On Linux, RasProcess.exe runs under Wine. See the Linux/Wine Setup guide.

Supported Map Types

Map Type CLI Flag Default Description
wse --wse/--no-wse On Water Surface Elevation
depth --depth/--no-depth On Depth
velocity --velocity/--no-velocity On Velocity
froude --froude Off Froude Number
shear_stress --shear-stress Off Shear Stress
depth_x_velocity --dv Off Depth x Velocity
depth_x_velocity_sq --dv-sq Off Depth x Velocity²
inundation_boundary --inundation-boundary Off Inundation Boundary
arrival_time --arrival-time Off Arrival Time
duration --duration Off Duration
recession --recession Off Recession

Render Mode

The --render-mode option controls how the water surface is rendered to raster grids. This is critical for pixel-perfect output — HEC-RAS 6.x's RasProcess.exe ignores the render mode from the .rasmap file, so ras-commander uses RasStoreMapHelper.exe to set the mode explicitly before generating maps.

Mode Flag Description
horizontal --render-mode horizontal Flat water surface within each mesh cell (default)
sloping --render-mode sloping Interpolated sloping water surface
slopingPretty --render-mode slopingPretty Sloping with depth-weighted face reduction (HEC-RAS 6.4+)

If --render-mode is not specified, the mode is read from the project's .rasmap file (defaults to horizontal if not set).

# Generate maps with sloping render mode
ras2cng map /path/to/project /output/maps --render-mode sloping

# Archive with slopingPretty render mode (requires HEC-RAS 6.4+)
ras2cng archive /path/to/project ./archive/ --results --map --render-mode slopingPretty

Post-Processing Options

  • Minimum depth threshold (--min-depth): Set pixels below a depth threshold to NoData
  • WGS84 reprojection (--wgs84): Reproject output rasters to EPSG:4326 using rasterio
  • Cloud Optimized GeoTIFF (--cog): Convert output to COG using gdal_translate

API Reference

ras2cng.mapping.MapResult dataclass

Result of map generation for a single plan.

Source code in ras2cng/mapping.py
@dataclass
class MapResult:
    """Result of map generation for a single plan."""
    plan_id: str
    plan_number: str
    map_types: dict[str, list[Path]] = field(default_factory=dict)  # {"depth": [Path(...)]}
    output_dir: Path = field(default_factory=lambda: Path("."))
    errors: list[str] = field(default_factory=list)

ras2cng.mapping.generate_result_maps(project_path, output_dir, *, plans=None, profile='Max', wse=True, depth=True, velocity=True, froude=False, shear_stress=False, depth_x_velocity=False, depth_x_velocity_sq=False, inundation_boundary=False, arrival_time=False, duration=False, recession=False, terrain_name=None, ras_version=None, rasprocess_path=None, render_mode=None, min_depth=0.0, reproject_wgs84=False, convert_cog=False, timeout=10800, skip_errors=True)

Generate result rasters for plans in a HEC-RAS project.

Uses RasProcess.store_maps() with RasStoreMapHelper.exe to generate raw TIFs from completed plan HDF files.

Parameters:

Name Type Description Default
project_path Path

Path to .prj file or project directory

required
output_dir Path

Directory for output raster files

required
plans Optional[list[str]]

Plan IDs to process (e.g. ["p01", "p02"]). None = all with results

None
profile str

Output profile: "Max", "Min", or timestamp

'Max'
wse bool

Generate Water Surface Elevation rasters

True
depth bool

Generate Depth rasters

True
velocity bool

Generate Velocity rasters

True
froude bool

Generate Froude Number rasters

False
shear_stress bool

Generate Shear Stress rasters

False
depth_x_velocity bool

Generate Depth x Velocity rasters

False
depth_x_velocity_sq bool

Generate Depth x Velocity² rasters

False
inundation_boundary bool

Generate Inundation Boundary polygon (shapefile)

False
arrival_time bool

Generate Arrival Time rasters

False
duration bool

Generate Duration rasters

False
recession bool

Generate Recession rasters

False
terrain_name Optional[str]

Specific terrain name from rasmap to use for mapping

None
ras_version Optional[str]

HEC-RAS version (auto-detected if None)

None
rasprocess_path Optional[Path]

Path to HEC-RAS install directory (for helper deployment)

None
render_mode Optional[str]

Water surface render mode: "horizontal", "sloping", or "slopingPretty". If None, reads from the .rasmap file (default: horizontal).

None
min_depth float

Minimum depth threshold for depth rasters (default: 0.0)

0.0
reproject_wgs84 bool

Reproject output rasters to WGS84

False
convert_cog bool

Convert output to Cloud Optimized GeoTIFF

False
timeout int

Per-plan timeout in seconds (default: 10800 = 3 hours)

10800
skip_errors bool

If True, log and continue past errors

True

Returns:

Type Description
list[MapResult]

List of MapResult, one per processed plan

Source code in ras2cng/mapping.py
def generate_result_maps(
    project_path: Path,
    output_dir: Path,
    *,
    plans: Optional[list[str]] = None,
    profile: str = "Max",
    wse: bool = True,
    depth: bool = True,
    velocity: bool = True,
    froude: bool = False,
    shear_stress: bool = False,
    depth_x_velocity: bool = False,
    depth_x_velocity_sq: bool = False,
    inundation_boundary: bool = False,
    arrival_time: bool = False,
    duration: bool = False,
    recession: bool = False,
    terrain_name: Optional[str] = None,
    ras_version: Optional[str] = None,
    rasprocess_path: Optional[Path] = None,
    render_mode: Optional[str] = None,
    min_depth: float = 0.0,
    reproject_wgs84: bool = False,
    convert_cog: bool = False,
    timeout: int = 10800,
    skip_errors: bool = True,
) -> list[MapResult]:
    """Generate result rasters for plans in a HEC-RAS project.

    Uses RasProcess.store_maps() with RasStoreMapHelper.exe to generate raw TIFs
    from completed plan HDF files.

    Args:
        project_path: Path to .prj file or project directory
        output_dir: Directory for output raster files
        plans: Plan IDs to process (e.g. ["p01", "p02"]). None = all with results
        profile: Output profile: "Max", "Min", or timestamp
        wse: Generate Water Surface Elevation rasters
        depth: Generate Depth rasters
        velocity: Generate Velocity rasters
        froude: Generate Froude Number rasters
        shear_stress: Generate Shear Stress rasters
        depth_x_velocity: Generate Depth x Velocity rasters
        depth_x_velocity_sq: Generate Depth x Velocity² rasters
        inundation_boundary: Generate Inundation Boundary polygon (shapefile)
        arrival_time: Generate Arrival Time rasters
        duration: Generate Duration rasters
        recession: Generate Recession rasters
        terrain_name: Specific terrain name from rasmap to use for mapping
        ras_version: HEC-RAS version (auto-detected if None)
        rasprocess_path: Path to HEC-RAS install directory (for helper deployment)
        render_mode: Water surface render mode: "horizontal", "sloping", or "slopingPretty".
            If None, reads from the .rasmap file (default: horizontal).
        min_depth: Minimum depth threshold for depth rasters (default: 0.0)
        reproject_wgs84: Reproject output rasters to WGS84
        convert_cog: Convert output to Cloud Optimized GeoTIFF
        timeout: Per-plan timeout in seconds (default: 10800 = 3 hours)
        skip_errors: If True, log and continue past errors

    Returns:
        List of MapResult, one per processed plan
    """
    from ras2cng.project import resolve_project_path

    project_path = Path(project_path)
    output_dir = Path(output_dir)
    output_dir.mkdir(parents=True, exist_ok=True)

    project_dir, prj_file = resolve_project_path(project_path)

    console.print(f"\n[bold cyan]ras2cng map[/bold cyan] -> {output_dir}")
    console.print(f"  Project : {prj_file.name}")
    console.print(f"  Profile : {profile}")

    # Configure RasProcess
    _configure_rasprocess(rasprocess_path, ras_version)

    # Initialize project
    ras = init_ras_project(project_dir, ras_object="new", load_results_summary=True)

    # Build list of requested map types
    requested_types = _build_requested_types(
        wse=wse, depth=depth, velocity=velocity,
        froude=froude, shear_stress=shear_stress,
        depth_x_velocity=depth_x_velocity,
        depth_x_velocity_sq=depth_x_velocity_sq,
        inundation_boundary=inundation_boundary,
        arrival_time=arrival_time, duration=duration,
        recession=recession,
    )

    if not requested_types:
        console.print("[yellow]Warning:[/yellow] No map types selected")
        return []

    console.print(f"  Map types: {', '.join(requested_types)}")

    # Determine which plans to process
    plan_filter = set(plans) if plans else None
    plan_rows = ras.plan_df if ras.plan_df is not None and not ras.plan_df.empty else None

    if plan_rows is None:
        console.print("[yellow]Warning:[/yellow] No plans found in project")
        return []

    results: list[MapResult] = []

    for _, row in plan_rows.iterrows():
        plan_num = str(row.get("plan_number", "")).zfill(2)
        plan_id = f"p{plan_num}"

        if plan_filter and plan_id not in plan_filter:
            continue

        plan_hdf = project_dir / f"{ras.project_name}.p{plan_num}.hdf"
        if not plan_hdf.exists():
            console.print(f"  [{plan_id}] No HDF results - skipping")
            continue

        plan_output = output_dir / plan_id
        plan_output.mkdir(parents=True, exist_ok=True)

        map_result = MapResult(
            plan_id=plan_id,
            plan_number=plan_num,
            output_dir=plan_output,
        )

        console.print(f"  [{plan_id}] Generating maps...")

        try:
            # Build boolean flags for RasProcess.store_maps()
            type_flags = {t: (t in requested_types) for t in MAP_TYPE_VARIABLES}

            result_dict = _generate_plan_maps(
                ras=ras,
                plan_number=plan_num,
                profile=profile,
                output_dir=plan_output,
                terrain_name=terrain_name,
                render_mode=render_mode,
                timeout=timeout,
                **type_flags,
            )

            for map_type in requested_types:
                tif_paths = result_dict.get(map_type, [])

                # Post-process: depth threshold
                if map_type == "depth" and min_depth > 0.0:
                    tif_paths = _apply_depth_threshold(tif_paths, min_depth)

                # Post-process: reproject to WGS84
                if reproject_wgs84:
                    tif_paths = _reproject_tifs(tif_paths, "EPSG:4326")

                # Post-process: convert to COG
                if convert_cog:
                    tif_paths = _convert_to_cog(tif_paths)

                if tif_paths:
                    map_result.map_types[map_type] = tif_paths
                    console.print(f"    {map_type}: {len(tif_paths)} raster(s)")

        except Exception as e:
            error_msg = f"plan {plan_id}: {e}"
            map_result.errors.append(error_msg)
            console.print(f"    [yellow]Warning:[/yellow] {error_msg}")
            if not skip_errors:
                raise

        results.append(map_result)

    total_maps = sum(
        sum(len(paths) for paths in r.map_types.values())
        for r in results
    )
    total_errors = sum(len(r.errors) for r in results)
    console.print(f"\n[green]OK[/green] Generated {total_maps} raster(s) from {len(results)} plan(s)")
    if total_errors:
        console.print(f"  [yellow]{total_errors} error(s)[/yellow]")

    return results

ras2cng.mapping.MAP_TYPE_VARIABLES = {'wse': 'Water Surface', 'depth': 'Depth', 'velocity': 'Velocity', 'froude': 'Froude Number', 'shear_stress': 'Shear Stress', 'depth_x_velocity': 'Depth x Velocity', 'depth_x_velocity_sq': 'Depth x Velocity²', 'inundation_boundary': 'Inundation Boundary', 'arrival_time': 'Arrival Time', 'duration': 'Duration', 'recession': 'Recession'} module-attribute