# catalog/operations/vector/harmonize.yaml

id: vector_harmonize
name: Harmonize
description: >
  Harmonize multiple vector datasets with different schemas, CRS projections,
  and field names into a single unified layer. Applies field mapping to rename
  and select columns, reprojects all inputs to a target CRS, and concatenates
  the results. Missing fields are filled with NULL. Optionally adds a source
  tag column to identify which input each row came from.
version: 1.0.0
category: vector
type: vector-to-vector
default_implementation: native

inputs:
  - name: sources
    type: vector
    format: geojson
    description: Array of input vector layers to harmonize
    required: true
    array: true

# Structural op — operates on any vector layers (schema/CRS harmonization).
requires: {}

outputs:
  - name: harmonized
    type: vector
    format: geoparquet
    description: Unified vector layer with consistent schema and CRS

params:
  field_mapping:
    type: object
    description: >
      Mapping of output field names to lists of input field aliases.
      Example: {id: [parcel_id, pid, APN], area: [area_sqft, area_m2]}.
      Each output field is populated by the first matching alias found in
      each input. If no field_mapping is provided, all columns from all
      inputs are auto-unioned.
  target_crs:
    type: string
    required: true
    description: Target CRS for output (e.g., EPSG:4326)
    default: "EPSG:4326"
  source_tag:
    type: string
    description: >
      If provided, adds a column with this name containing an identifier
      for which input layer each row came from (e.g., "source_0", "source_1").
  source_crs:
    type: object
    description: >
      Optional per-source CRS overrides. Keys are source indices (as strings),
      values are CRS strings (e.g., {"1": "EPSG:32613"}).
  crosswalk:
    type: string
    description: >
      Crosswalk ID to resolve from the registry.  Resolved to a field_mapping
      at execution time via crosswalk_to_field_mapping().
      If both crosswalk and field_mapping are provided, field_mapping takes precedence.
      Example: "era5-to-cf"
  include_broad:
    type: boolean
    default: false
    description: >
      When using crosswalk, include broad-predicate mappings in addition
      to exact and close matches (default: only exact + close).

cache_policy:
  ttl_days: 30
  invalidate_on: [source_update, param_change]

# backends: audited 2026-08-14 (defect 50). A key means a runtime that DISPATCHES this op —
# folia-engine `dispatch_op` (products/sdk/folia-engine/src/lib.rs), a `registerOp`/OP_TABLE
# entry in packages/compute, `_BUILTIN_OP_MAP` in folia/compute.py, or a backend manifest
# (folia/backends/*/backend.yaml).
backends:
  python:
    function: geo.vector.harmonize
    dispatch: folia/compute.py _BUILTIN_OP_MAP

display_hints:
  map:
    renderer: maplibre
    type: fill
    paint:
      fill-color: "rgba(60, 179, 113, 0.3)"
      fill-outline-color: "#3CB371"

ui:
  icon: git-merge
  color: "#3CB371"
