# catalog/operations/hydrology/catchment_network.yaml

id: hydrology_catchment_network
name: Named Catchment Network
description: >
  Build a hierarchical network of named catchments by linking USGS GNIS
  (Geographic Names Information System) features to NHD/UHD catchment polygons.
  Produces drainage polygons with official names, categories, centroids,
  poles of inaccessibility, and parent-child relationships.
version: 1.0.0
category: hydrology
type: composite
default_implementation: native

# External data sources required
sources:
  gnis:
    connector: usgs_gnis
    params:
      feature_classes: [valley, basin, stream, flat, arroyo, channel, creek, river, fork, branch]
      include_historical: false
    description: GNIS point features for drainage names

  gnis_historical:
    connector: usgs_gnis
    params:
      include_historical: true
    description: Historical GNIS records (optional, for gap filling)
    required: false

  catchments:
    connector: usgs_nhd
    params:
      layer: catchment
      resolution: high  # NHDPlus HR or UHD
    description: NHD/UHD catchment polygons

inputs:
  - name: gnis
    type: vector
    format: geojson
    geometry: Point
    description: GNIS point features with names and categories
    required: true

  - name: catchments
    type: vector
    format: geoparquet
    geometry: Polygon
    description: Catchment/drainage polygons
    required: true

  - name: gnis_historical
    type: vector
    format: geojson
    geometry: Point
    description: Historical GNIS records for gap filling
    required: false

# requires: omitted — this is a pure vector op over GNIS + NHD catchment polygons.
# No raster/elevation concept is consumed. The "catchment" concept does not yet
# exist in registry/concepts/; output concept omitted pending its addition.

outputs:
  - name: named_catchments
    type: vector
    format: geoparquet
    geometry: Polygon
    description: Catchment polygons with GNIS linkage and hierarchy
    properties:
      gnis_id:
        type: string
        description: GNIS feature ID
      name:
        type: string
        description: Official drainage name from GNIS
      category:
        type: string
        enum: [valley, basin, canyon, creek, river, flat, fork, branch, arroyo, channel, unknown]
        description: Drainage type category derived from GNIS feature class
      geom:
        type: polygon
        description: Catchment boundary
      centroid:
        type: point
        description: Geographic centroid of catchment
      pole_of_inaccessibility:
        type: point
        description: Point furthest from catchment boundary (visual label placement)
      area_km2:
        type: number
        description: Catchment area in square kilometers
      parent_gnis_id:
        type: string
        nullable: true
        description: GNIS ID of parent drainage (if nested)
      parent_name:
        type: string
        nullable: true
        description: Name of parent drainage
      hierarchy_level:
        type: integer
        description: Nesting depth (0 = top-level drainage)
      centerline:
        type: linestring
        nullable: true
        description: Main channel/river linestring through catchment
      confidence:
        type: number
        description: Confidence score of GNIS-to-catchment linkage (0-1)

  - name: hierarchy_edges
    type: vector
    format: geoparquet
    geometry: null
    description: Parent-child relationships as graph edges
    properties:
      child_gnis_id:
        type: string
      parent_gnis_id:
        type: string
      relationship:
        type: string
        enum: [contains, flows_into, tributary_of]

params:
  min_area_km2:
    type: number
    default: 0.5
    min: 0.01
    max: 1000
    description: Minimum catchment area to include (filters tiny drainages)

  max_hierarchy_depth:
    type: integer
    default: 5
    min: 1
    max: 10
    description: Maximum nesting depth for parent-child relationships

  gnis_distance_threshold_km:
    type: number
    default: 5.0
    min: 0.1
    max: 50
    description: >
      Maximum distance from GNIS point to catchment centroid for matching.
      GNIS points should be close to the drainage they name.

  centroid_method:
    type: enum
    default: geographic
    enum: [geographic, weighted, pole_of_inaccessibility]
    description: Method for computing representative point

  include_centerlines:
    type: boolean
    default: true
    description: Include main channel linestring in output

  include_historical:
    type: boolean
    default: false
    description: Use historical GNIS records to fill naming gaps

  category_mapping:
    type: object
    default:
      Valley: valley
      Basin: basin
      Arroyo: canyon
      Channel: creek
      Stream: creek
      Creek: creek
      River: river
      Fork: fork
      Branch: branch
      Flat: flat
    description: Mapping from GNIS feature class to output category

# DAG definition
dag:
  - id: filter_catchments
    op: vector_filter
    inputs: [catchments]
    params:
      expression: "area_km2 >= ${min_area_km2}"
    description: Remove catchments below minimum area threshold

  - id: compute_centroids
    op: vector_centroid
    inputs: [filter_catchments]
    params:
      method: ${centroid_method}
      include_pole_of_inaccessibility: true
    description: Compute centroid and pole of inaccessibility for each catchment

  - id: spatial_join_gnis
    op: vector_spatial_join
    inputs: [compute_centroids, gnis]
    params:
      how: left
      predicate: nearest
      max_distance_km: ${gnis_distance_threshold_km}
      distance_col: gnis_distance
    description: Link GNIS points to nearest catchment centroid

  - id: categorize
    op: vector_map
    inputs: [spatial_join_gnis]
    params:
      mappings:
        category: "category_mapping.get(gnis_feature_class, 'unknown')"
        confidence: "1.0 - (gnis_distance / ${gnis_distance_threshold_km})"
    description: Map GNIS feature class to category and compute confidence

  - id: build_hierarchy
    op: vector_hierarchy
    inputs: [categorize]
    params:
      method: spatial_containment
      max_depth: ${max_hierarchy_depth}
      parent_id_col: parent_gnis_id
      parent_name_col: parent_name
      level_col: hierarchy_level
    description: Build parent-child relationships based on spatial nesting

  - id: extract_centerlines
    op: vector_centerline
    inputs: [build_hierarchy]
    params:
      enabled: ${include_centerlines}
    description: Extract main channel linestring through each catchment
    condition: include_centerlines

  - id: finalize
    op: vector_select
    inputs: [extract_centerlines]
    params:
      columns:
        - gnis_id
        - name
        - category
        - geom
        - centroid
        - pole_of_inaccessibility
        - area_km2
        - parent_gnis_id
        - parent_name
        - hierarchy_level
        - centerline
        - confidence
    description: Select final output columns

output: finalize

# Execution hints
execution:
  realtime_max_km2: null  # Always batch (requires full region processing)
  cost_per_km2: 0.0001
  time_per_km2_sec: 0.01
  memory_profile: high  # Large vector processing
  gpu_required: false
  parallelizable: true  # Can split by HUC regions

cache_policy:
  regional_precompute: [western_us, eastern_us]
  ttl_days: 365  # GNIS updates infrequently
  invalidate_on: [source_update]

# backends: audited 2026-08-14 (defect 50). NOTHING dispatches this op: no arm in folia-engine
# `dispatch_op`, no `registerOp`/OP_TABLE entry in packages/compute, no `_BUILTIN_OP_MAP`
# key in folia/compute.py, no backend manifest. Declared EMPTY on purpose — an absent
# block would be indistinguishable from one nobody ever wrote.
# this record composes other ops in its `dag:` block rather than naming a kernel — but nothing reads an op record's `dag:` (no expander exists in folia/, packages/compute or folia-engine), so the composition is documentation, not a run path.
backends: {}

display_hints:
  map:
    renderer: maplibre
    type: fill
    paint:
      fill-color: "rgba(30, 144, 255, 0.2)"
      fill-outline-color: "#1E90FF"
  table:
    renderer: tanstack-table

ui:
  icon: git-branch
  color: "#1E90FF"
  preview:
    type: vector
    style:
      fill: "rgba(30, 144, 255, 0.2)"
      stroke: "#1E90FF"
      stroke_width: 1
