Hydrofabric Conflation¶
RasNetworkConflation starts with model-extent coverage, preserving the natural
one-model-to-many-edge relationship. NHDPlus, NWM, and NextGen are schema
adapters to the same generic directed-network core.
Extent-first edge classification¶
from ras_commander import RasNetworkConflation
coverage = RasNetworkConflation.classify_edges(
model_footprints=footprints_gdf,
network_edges=nwm_flowlines_gdf,
adapter="nwm",
)
edge_coverage_df = coverage.coverage_df
coverage_parts_df = coverage.coverage_parts_df
edge_summary_df = coverage.edge_summary_df
model_overlap_df = coverage.model_overlap_df
coverage_df has one row per (geometry_id, edge_id) with positive spatial
overlap by default. It reports inside_length, full edge_length,
inside_fraction, and extent_status (inside or partial) while preserving
the full edge geometry and normalized topology fields. Pass
include_outside=True when an audit also needs nearby non-intersecting edges.
The additional directed tables retain the information needed when several models cover one network edge:
| Property | Cardinality | Purpose |
|---|---|---|
coverage_parts_df |
One row per contiguous model/edge intersection | Start/end measures and fractions along the directed edge |
edge_summary_df |
One row per edge | Model count, union coverage, overlap, and uncovered gap |
model_overlap_df |
One row per contiguous model-pair overlap | Candidate handoff zones between source models |
Measures start at the network edge's first coordinate and increase toward its last coordinate. Network inputs therefore need to follow their intended upstream-to-downstream orientation.
All spatial inputs must have a CRS. Measurements prefer the model footprint's
projected CRS, an explicitly supplied analysis_crs, or an automatically
estimated local UTM CRS. No weighted score is used: the model extent is the
authoritative spatial filter, and every in-domain edge is retained.
This table is the starting point for NWM-sized RasBreakout1D models. Select an
edge_id and pass its geometry plus inside_fraction to
select_domains_by_network_edge(). A fully contained edge receives the default
10% upstream and 25% downstream hydraulic buffers, while its stricter
inundation-export selection retains the default one-cross-section downstream
overlap.
Plan multi-model edge coverage¶
plan_edge_coverage() selects the smallest deterministic source chain that
extends farthest downstream at every step. Contained models are not selected,
overlap seams are placed at the middle of the shared coverage interval, and
uncovered gaps remain explicit rather than being assigned to either source.
plan = RasNetworkConflation.plan_edge_coverage(
coverage,
edge_ids=[target_edge_id],
gap_tolerance=1.0,
)
print(plan.plans_df)
print(plan.source_slices_df)
print(plan.seams_df)
Plan status is single_source_ready, multi_source_ready, coverage_gap, or
uncovered. gap_tolerance handles measurement noise only; the returned
total_gap_length and maximum_gap_length always preserve the measured gap.
selected_model_count counts distinct models, while selected_slice_count
also exposes disconnected pieces from a repeated model. The parallel
source_geometry_ids and source_slice_geometry_ids fields preserve those two
views without hiding repetition.
This is a footprint-coverage plan, not approval of a hydraulic geometry seam.
Cross-section ordering, centerline joining, structures, station identifiers,
and flow compatibility must be validated before building a combined model.
Advanced reach-edge candidate audit¶
conflate() retains the earlier multi-signal candidate analysis for ambiguous
or poorly aligned networks. It is an advanced QA surface, not the default gate
for extent-based breakout generation. It crosswalks HEC-RAS model footprints,
reach centerlines, and cross sections to candidate flowpaths while retaining
the evidence instead of reducing a failed lookup to a numeric COMID.
result = RasNetworkConflation.conflate(
model_footprints=footprints_gdf,
centerlines=reach_centerlines_gdf,
cross_sections=xs_cut_lines_gdf,
flowpaths=network_edges_gdf,
adapter="nwm",
)
Measurements use the centerline's projected CRS, an explicitly supplied
analysis_crs, or an automatically estimated local UTM CRS when the
centerlines are geographic.
The method returns a NetworkConflationResult with four GeoDataFrames:
| Property | Contents | Active geometry |
|---|---|---|
matches |
One explicit geometry, reach, and cross-section resolution row | HEC-RAS model element |
candidates |
Every retained candidate, rank, score component, and reason code | Hydrofabric flowpath |
reach_metrics |
Edge/reach association, XS limits, offsets, lengths, coverage, and flags | HEC-RAS reach centerline |
huc_intersections |
Optional model-footprint/HUC overlap records | Intersection polygon |
Match states¶
matches.status is always one of:
matched— the top candidate clearsmin_confidenceand the runner-up separation clearsambiguity_margin;ambiguous— the best candidates are too close to resolve safely;unmatched— there is no spatial candidate or the best score is below the confidence threshold.
Only matched rows receive a feature_id. Ambiguous and unmatched rows retain
best_candidate_feature_id, confidence_score, reason_codes, and their full
candidate records, but feature_id remains null. This prevents failure states
from being mistaken for real numeric COMIDs.
accepted = result.matches.query("status == 'matched'")
review = result.matches.query("status in ['ambiguous', 'unmatched']")
print(result.summary)
# {'matched': ..., 'ambiguous': ..., 'unmatched': ..., 'total': ...}
Candidate scoring¶
Reach candidates combine seven evidence groups:
- flowpath-length overlap with the model footprint;
- sampled symmetric distance to the reach centerline;
- directed angular agreement;
- cross-section intersection coverage;
- connectivity across adjacent model reaches;
- stream-order and drainage-area support;
- agreement between cross-section order along the reach and flowpath.
When an evidence group is unavailable—for example, a reach has no cross sections—the method removes that group and renormalizes the remaining weights. Override only the weights that need adjustment:
result = RasNetworkConflation.conflate(
footprints_gdf,
reach_centerlines_gdf,
xs_cut_lines_gdf,
flowpaths_gdf,
weights={
"centerline_distance": 0.30,
"xs_intersections": 0.25,
},
min_confidence=0.60,
ambiguity_margin=0.08,
)
Candidate columns expose both normalized component scores and raw evidence such
as centerline_mean_distance, angular_difference_deg,
xs_intersection_count, stream_order, and drainage_area.
Cross-section measures¶
Accepted cross-section rows include:
flowpath_measure— distance from the flowpath geometry start;flowpath_measure_fraction— normalized measure from 0 to 1;flowpath_measure_from_end— distance from the flowpath geometry end;measure_method—intersectionornearest;offset_distance— zero for a direct intersection, otherwise the nearest offset.
The units are those of result.analysis_crs.
Reach limits, offsets, lengths, and flags¶
result.reach_metrics is the extraction-oriented network-edge ↔ RAS-reach
table. For the best edge candidate it reports:
upstream_xs_idanddownstream_xs_id, ordered from the directed network geometry start to its end;coverage_start,coverage_end, and the interveningcoverage_ratio;- RAS-centerline and network lengths between those sections, plus their ratio;
- centerline-offset distribution statistics at intersecting sections;
- thalweg-offset statistics when thalweg point geometry is supplied;
- explicit ambiguous, eclipsed, divergent, and insufficient-coverage flags.
Supply thalweg points as a keyed GeoDataFrame:
result = RasNetworkConflation.conflate(
footprints_gdf,
reach_centerlines_gdf,
xs_cut_lines_gdf,
network_edges_gdf,
thalweg_points=xs_thalweg_points_gdf,
adapter=custom,
min_coverage=0.50,
)
The thalweg layer must contain unique reach_id/xs_id Point rows. As a
convenience, a point-valued thalweg_point column on the cross-section frame is
also accepted. centerline_offset compares the RAS-centerline/XS crossing to
the network/XS crossing; thalweg_offset compares the supplied thalweg point
to the network/XS crossing.
An edge is marked eclipsed when it has no two distinct intersecting sections,
which is the network-neutral condition needed before a topology-specific
adapter walks neighboring edges. Divergence is evaluated from normalized
from_node/to_node fields; connectivity_evaluable=False keeps missing
connectivity from being silently treated as a non-divergence. Coverage below
min_coverage receives an INSUFFICIENT_COVERAGE reason code.
Hydrofabric adapters¶
Pass adapter="auto" or select a built-in schema explicitly:
| Adapter | Typical identifier | Common normalized fields |
|---|---|---|
nhdplus |
COMID |
StreamOrde, TotDASqKm, node IDs, Hydroseq |
nwm |
id / feature_id |
toid, order, areasqkm, hydroseq |
nextgen |
feature_id / flowpath_id |
downstream/nexus IDs, order, drainage area, sequence |
The NWM and NextGen adapters recognize native wb-* flowpaths and nex-*
connectivity. A downstream nex-123 is resolved to wb-123 only when that
flowpath is present in the supplied network; terminal, coastal, internal, or
clipped nexuses remain nodes rather than becoming invented edge IDs. When both
incremental and total drainage-area attributes are available, the adapters use
the total upstream area for scale agreement.
Topology contributes to candidate scoring only for flowpaths inside the local reach search buffer. This prevents a connected flowpath elsewhere in a broad model footprint from supplying false continuity evidence.
For another schema, supply a custom adapter:
from ras_commander import NetworkAdapter, RasNetworkConflation
custom = NetworkAdapter(
name="agency_flowpaths",
feature_id_fields=("agency_reach_id",),
to_feature_id_fields=("downstream_reach_id",),
stream_order_fields=("strahler",),
drainage_area_fields=("drainage_sq_km",),
)
result = RasNetworkConflation.conflate(
footprints_gdf,
reach_centerlines_gdf,
xs_cut_lines_gdf,
agency_flowpaths_gdf,
adapter=custom,
)
RasNetworkConflation
¶
Conflate HEC-RAS geometry with generic directed network flowpaths.
This is a static namespace; do not instantiate it.
get_adapter
staticmethod
¶
get_adapter(adapter: Union[str, NetworkAdapter], flowpaths: Optional[GeoDataFrame] = None) -> NetworkAdapter
Resolve a built-in, custom, or auto-detected adapter.
classify_edges
staticmethod
¶
classify_edges(model_footprints: GeoInput, network_edges: GeoInput, *, adapter: Union[str, NetworkAdapter] = 'auto', geometry_id_col: Optional[str] = None, network_edges_layer: Optional[str] = None, analysis_crs: Optional[Any] = None, include_outside: bool = False, min_inside_fraction: float = 0.0, inside_tolerance: float = 1e-09) -> NetworkEdgeCoverageResult
Classify every model-footprint/network-edge spatial relationship.
The model extent is authoritative. For each edge, inside_fraction
is the length inside the model footprint divided by the edge's full
length. Intersecting edges are returned as inside or partial;
outside edges are omitted unless include_outside=True.
This extent-first operation preserves the natural one-model-to-many-edge cardinality needed to create one breakout per NWM/NextGen reach. It performs no weighted candidate scoring.
plan_edge_coverage
staticmethod
¶
plan_edge_coverage(coverage: NetworkEdgeCoverageResult, *, edge_ids: Optional[Union[str, int, Sequence[Union[str, int]]]] = None, gap_tolerance: float = 0.0, measure_tolerance: float = 1e-09) -> NetworkEdgeCoveragePlanResult
Plan a minimal directed source-model chain for each network edge.
The planner operates only on model-footprint coverage. It does not run the seven-signal reach conflation scorer and does not yet approve a hydraulic geometry seam. Measures follow each edge's coordinate order.
A greedy interval-cover algorithm chooses the source interval that extends farthest downstream at each step. This produces a deterministic minimum-switch chain for ordinary nested and overlapping BLE model extents. Gaps remain unowned and are reported explicitly.
conflate
staticmethod
¶
conflate(model_footprints: GeoInput, centerlines: GeoInput, cross_sections: Optional[GeoInput], flowpaths: GeoInput, *, thalweg_points: Optional[GeoInput] = None, adapter: Union[str, NetworkAdapter] = 'auto', hucs: Optional[GeoInput] = None, analysis_crs: Optional[Any] = None, geometry_id_col: Optional[str] = 'geometry_id', centerline_geometry_id_col: Optional[str] = 'geometry_id', reach_id_col: Optional[str] = 'reach_id', xs_id_col: Optional[str] = 'xs_id', xs_reach_id_col: Optional[str] = 'reach_id', thalweg_xs_id_col: Optional[str] = 'xs_id', thalweg_reach_id_col: Optional[str] = 'reach_id', xs_thalweg_col: Optional[str] = 'thalweg_point', huc_id_col: Optional[str] = None, flowpaths_layer: Optional[str] = None, hucs_layer: Optional[str] = None, search_distance: Optional[float] = None, topology_tolerance: Optional[float] = None, max_candidates: int = 8, min_confidence: float = 0.55, ambiguity_margin: float = 0.05, min_coverage: float = 0.5, weights: Optional[Mapping[str, float]] = None, sample_count: int = 9) -> NetworkConflationResult
Conflate model footprints, reaches, and cross sections to flowpaths.
Candidate flowpaths are scored using model-footprint overlap, symmetric centerline distance, directed angular agreement, cross-section intersections, topological continuity, stream order/drainage area, and reach-to-flowpath sequence consistency. Missing evidence is excluded and the remaining weights are renormalized.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model_footprints
|
GeoInput
|
Polygon GeoDataFrame (or vector path), normally one row per HEC-RAS geometry/model realization. |
required |
centerlines
|
GeoInput
|
Reach centerlines. |
required |
cross_sections
|
Optional[GeoInput]
|
Cross-section cut lines, or |
required |
flowpaths
|
GeoInput
|
Hydrofabric flowpaths GeoDataFrame or vector path. |
required |
thalweg_points
|
Optional[GeoInput]
|
Optional point GeoDataFrame keyed by cross-section
and reach ID. If omitted, |
None
|
adapter
|
Union[str, NetworkAdapter]
|
|
'auto'
|
hucs
|
Optional[GeoInput]
|
Optional HUC polygon layer. Intersections are returned without influencing the candidate score. |
None
|
analysis_crs
|
Optional[Any]
|
Optional projected CRS for measurements. A local UTM CRS is estimated automatically when centerlines are geographic. |
None
|
search_distance
|
Optional[float]
|
Candidate-search radius in analysis-CRS units. |
None
|
topology_tolerance
|
Optional[float]
|
Endpoint-connectivity tolerance in analysis-CRS units. |
None
|
max_candidates
|
int
|
Maximum reach candidates retained after spatial preselection. |
8
|
min_confidence
|
float
|
Minimum accepted score in |
0.55
|
ambiguity_margin
|
float
|
Minimum first/second score separation. |
0.05
|
min_coverage
|
float
|
Minimum accepted span between upstream/downstream cross sections as a fraction of network-edge length. |
0.5
|
weights
|
Optional[Mapping[str, float]]
|
Optional overrides for :data: |
None
|
Returns:
| Type | Description |
|---|---|
NetworkConflationResult
|
class: |
NetworkConflationResult
|
have |
NetworkConflationResult
|
|