# This file has arguments to process WorldView-3 B1 images and brief setup instructions below.

# 1. To install the required system level libraries run:
  # `conda create -n vhrharmonize -c conda-forge gdal python=3.11`
  # `conda activate vhrharmonize`

# 2. To install the library, depending on if you are installing from pip or source:
  # From source: `pip install -e ".[defaults]"`
  # From pip: `pip install vhrharmonize[defaults]`

# 3. Set the arguments in this yml file

# 4. To process images:
  # Navigate to the folder with this yml file (may need to be changed): `cd configs`
  # Run: `vhr-worldview --config-yaml example.worldview.yml`

shared:
  input_file_glob:
    - file_source: /path/to/worldview_bundle_root/**/*.TIF # Raw source inputs; should match both MUL and PAN images.
  # input_file_glob:
  #   - cloud_mask: "/path/to/multiline/worldivew/image/search/**/@(\
  # 17DEC08211835-[MP]1BS-016445318010_01_P011*|\
  # 19SEP09211403-[MP]1BS-200011908666_01_P001*\
  # )_cloudmasked.TIF" # Already completed cloud-mask inputs.
  output_dir: ../../processed # Relative output paths resolve from the MUL image folder. If unset, defaults to ../Processed from that folder
  epsg: 0000 # Projected epsg code for output products; for example 6635 for Hawaii Island
  
  concurrent_processing_backend: process_pool # process_pool | dask. Dask uses a running scheduler that can be setup. Search for "When dask is enabled" in the config files for all params to enable dask.
  concurrent_processing: 1 # Local process count or num_cpu for process_pool; must be 1 for dask because the cluster controls concurrency
  # dask_scheduler_file: ~/koa_scratch/vhrharmonize/run_{run_id}/logs/{run_id}_dask_scheduler.json # Enable for dask connection
  # dask_scheduler_address: tcp://scheduler-host:8786 # Alternative Dask connection
  
  dem_file_path: /path.tif  # /path/to/dem.tif or "online" to fetch from opentopography (must set API key). The DEM should be in WGS84 ellipsoidal-height and can be downloaded from https://portal.opentopography.org/raster?opentopoID=OTSRTM.082016.4326.1
  # dem_online_api_key: your-opentopography-api-key # Or set OPENTOPOGRAPHY_API_KEY in the environment
  # dem_online_source: SRTMGL1_Ellip
  # dem_online_api_endpoint: https://portal.opentopography.org/API/globaldem
  # dem_online_timeout_s: 120.0

  #temp_dir: /temp/folder # If unset, it defaults to creating a system temp dir
  keep_temp_dir: false
  nodata_value: -9999 # Shared nodata value used by raster-writing steps
  dtype: int16 # Shared output dtype for raster-writing steps
  log_to_console: true
  overview_scales: [2, 4, 8, 16, 32, 64, 128, 256, 512] # Common overview pyramid levels. Required when any calculate_overviews_* option is true
  skip_existing: true # Skip the whole scene only if all requested non-temp outputs already exist
  skip_existing_check_validity: true # Before skipping a whole scene, verify existing raster outputs are GDAL-readable
  run_from_existing: true # At each step, if the expected output exists, reuse it instead of rerunning that step
  run_from_existing_check_validity: true # Before reusing a step output, verify existing raster outputs are GDAL-readable
  validity_check_grid_size: 2048 # If validity checks above are enabled, this is an additional check for actual valid pixels: 0 disables pixel sampling; 1 checks every pixel; N samples one pixel per NxN grid cell
  max_cloud_cover_to_process: 75 # Skip scenes whose cloud cover percent exceeds this threshold

workflow:
  # File source
  run_file_source: false # Copy source WorldView bundle files before processing
  save_file_source: $temp/file_source
  calculate_overviews_file_source: false

  # Atmospheric correction
  run_atmospheric_correction: true
  atmospheric_correction_output_suffix: _py6s # Filename suffix for this step
  save_atmospheric_correction: $temp/atmospheric_correction # $temp/folder | $output/folder | ./custom/relative/to/input | /custom/absolute | relative/to/pwd
  calculate_overviews_atmospheric_correction: false # Weather or not to calculate overviews for this step
  run_fetch_atmosphere: true
  fetch_atmosphere_output_suffix: _atmosphere # Filename suffix for fetched atmosphere JSON
  save_fetch_atmosphere: $temp/fetch_atmosphere

  # Orthorectification
  run_orthorectification: true
  orthorectification_output_suffix: _ortho # Filename suffix for orthorectified multispectral output
  orthorectification_pan_output_suffix: _pan_ortho # Filename suffix for orthorectified panchromatic output
  save_orthorectification: $temp/orthorectification
  calculate_overviews_orthorectification: false

  # Pansharpening
  run_pansharpen: true
  pansharpen_output_suffix: _pansharpen # Filename suffix for pansharpened output
  save_pansharpen: $temp/pansharpen
  calculate_overviews_pansharpen: false

  # Cloud masking
  run_cloud_mask: true
  cloud_mask_output_suffix: _cloudmasked # Filename suffix for masked image output
  cloud_mask_mask_suffix: _cloudmask # Filename suffix for binary cloud mask output
  save_cloud_mask: $output # or $temp/cloud_mask if running next step
  calculate_overviews_cloud_mask: true

  # Spatially match to external dataset (remember to set reference dataset below)
  run_alignment: false
  alignment_output_suffix: _aligned # Filename suffix for aligned output
  save_alignment: $output
  calculate_overviews_alignment: true

  # Build footprint polygons plus IMD metadata for weighted seamline methods
  run_seamline_metadata: false
  save_seamline_metadata: $temp/seamline_metadata.gpkg # $temp/file.gpkg | /custom/absolute.gpkg | relative/to/pwd.gpkg

  # Radiometrically match images to each other, align pixel grids, mask, and merge images
  run_radiometric_normalization: false
  save_radiometric_normalization: $temp/radiometric_root.tif # $temp/file.tif | /custom/absolute.tif | relative/to/pwd.tif
  calculate_overviews_radiometric_normalization: true

# STEP SPECIFIC SETTINGS HEREAFTER (defaults are often okay)
atmospheric_correction:
  atmospheric_method: py6s # py6s | flaash | none

  fetch_atmosphere:
    fetch_atmosphere_source: auto # auto | nasa_power | modis_gee
    fetch_atmosphere_grid_size: 3 # Number of sample points per bbox side
    fetch_atmosphere_search_days: 1 # Search +/- this many days for matching atmosphere data
    fetch_atmosphere_timeout_s: 30.0 # HTTP timeout in seconds
    fetch_atmosphere_power_endpoint: https://power.larc.nasa.gov/api/temporal/daily/point # NASA POWER endpoint
    fetch_atmosphere_hours_window: 24 # MODIS/GEE scene-time search window in hours
    # fetch_atmosphere_ee_project: your-ee-project
    # fetch_atmosphere_env_file: /path/to/.env

  flaash:
    # Required only when atmospheric_method: flaash
    # envi_engine_path: "/mnt/c/Program Files/NV5/ENVI60/IDL90/bin/bin.x86_64/taskengine.exe"
    # flaash_modtran_atm: Mid-Latitude Summer
    # flaash_modtran_aer: Maritime
    # flaash_use_aerosol: Disabled
    # flaash_default_visibility: 20.0

    # Any extra FLAASH task params can be passed through with the `flaash_param_` prefix.
    # Example:
    # flaash_param_SENSOR_TYPE: "UNKNOWN"
    # flaash_param_INPUT_SCALE: 1.0
    # flaash_param_OUTPUT_SCALE: 10000.0
    # flaash_param_CALIBRATION_FILE: /path/to/calibration.txt
    # flaash_param_CALIBRATION_FORMAT: "ENVI"
    # flaash_param_CALIBRATION_UNITS: "uW/(cm^2 sr nm)"
    # flaash_param_LAT_LONG: [0.0, 0.0]
    # flaash_param_SENSOR_ALTITUDE: 617.0
    # flaash_param_DATE_TIME: "2026-01-01T00:00:00Z"
    # flaash_param_USE_ADJACENCY: true
    # flaash_param_DEFAULT_VISIBILITY: 20.0
    # flaash_param_USE_POLISHING: false
    # flaash_param_POLISHING_RESOLUTION: 100.0
    # flaash_param_SENSOR_AUTOCALIBRATION: false
    # flaash_param_SENSOR_CAL_PRECISION: 0.01
    # flaash_param_SENSOR_CAL_FEATURE_LIST: []
    # flaash_param_GROUND_ELEVATION: 0.5
    # flaash_param_SOLAR_AZIMUTH: 180.0
    # flaash_param_SOLAR_ZENITH: 30.0
    # flaash_param_LOS_AZIMUTH: 90.0
    # flaash_param_LOS_ZENITH: 10.0
    # flaash_param_IFOV: 0.002
    # flaash_param_MODTRAN_ATM: "Mid-Latitude Summer"
    # flaash_param_MODTRAN_AER: "Maritime"
    # flaash_param_MODTRAN_RES: 5.0
    # flaash_param_MODTRAN_MSCAT: "DISORT"
    # flaash_param_CO2_MIXING: 400.0
    # flaash_param_WATER_ABS_CHOICE: "BAND_MODEL"
    # flaash_param_WATER_MULT: 1.0
    # flaash_param_WATER_VAPOR_PRESET: 1.0
    # flaash_param_USE_AEROSOL: "Disabled"
    # flaash_param_AEROSOL_SCALE_HT: 2.0
    # flaash_param_AER_BAND_RATIO: 0.5
    # flaash_param_AER_BAND_WAVL: 660
    # flaash_param_AER_REFERENCE_VALUE: 0.1
    # flaash_param_AER_REFERENCE_PIXEL: [0, 0]
    # flaash_param_AER_BANDLOW_WAVL: 425
    # flaash_param_AER_BANDLOW_MAXREFL: 0.2
    # flaash_param_AER_BANDHIGH_WAVL: 660
    # flaash_param_AER_BANDHIGH_MAXREFL: 0.2
    # flaash_param_INPUT_RASTER: {url: /path/to/input, factory: URLRaster}
    # flaash_param_OUTPUT_RASTER_URI: /path/to/output.dat
    # flaash_param_CLOUD_RASTER_URI: /path/to/cloud_mask.tif
    # flaash_param_WATER_RASTER_URI: /path/to/water_mask.tif

  py6s:
    py6s_atmosphere_profile: user # user | tropical | midlatitude_summer | midlatitude_winter | subarctic_summer | subarctic_winter
    py6s_aerosol_profile: maritime # maritime | continental | urban | desert | biomass_burning | stratospheric
    py6s_aot550: 0.2 # Used when visibility is not set
    # py6s_visibility: 40.0
    py6s_water_vapor: 2.5 # g/cm^2
    py6s_ozone: 0.3 # cm-atm
    # py6s_executable: /usr/local/bin/sixsV1.1
    py6s_output_scale_factor: 10000.0 # Reflectance scale factor applied before writing
    py6s_output_dtype: int16 # Output dtype for Py6S rasters
    py6s_use_imd_radiance_calibration: true
    py6s_use_worldview_gain_offset_adjustment: true
    py6s_auto_atmos_source: nasa_power # none | nasa_power
    py6s_auto_atmos_grid_size: 3 # Number of sample points per bbox side
    py6s_auto_atmos_search_days: 1 # Search +/- this many days for matching atmosphere data
    py6s_auto_atmos_timeout_s: 30.0 # HTTP timeout in seconds

orthorectification:
  # orthorectification_rpc_refinement_geojson: /path/to/rpc_refinement_gcps.geojson # GCPs GeoJSON used to refine the RPC model before orthorectification in the standard orthority format: https://orthority.readthedocs.io/en/stable/file_formats/oty_gcps.html

cloud_mask:
  cloud_mask_method: omnicloudmask # omnicloudmask
  cloud_buffer_pixels: 10 # Buffer cloud mask outward by this many pixels
  cloud_mask_inference_resolution_m: 10.0 # Resolution used for OmniCloudMask inference
  # cloud_mask_omnicloud_kwargs_json:
  #   inference_device: cuda
  #   mosaic_device: cpu
  #   batch_size: 1
  #   patch_size: 768
  #   patch_overlap: 192

alignment:
  alignment_fixed_image: /path/to/fixed/reference.tif # Required when run_alignment step. This can be an image, LiDAR intensity raster, or another dataset
  alignment_band_index: 0 # 0-based band index used for registration metric
  # alignment_moving_band_index: 0 # Optional override for moving-image registration band
  # alignment_fixed_band_index: 0 # Optional override for fixed-image registration band
  # alignment_moving_nodata: -9999 # Optional override nodata for moving image
  # alignment_fixed_nodata: -9999 # Optional override nodata for fixed image
  # alignment_output_nodata: -9999 # Optional override nodata for output image
  alignment_min_valid_fraction: 0.01 # Required valid overlap fraction in registration ROI
  alignment_use_edge_proxies: true
  alignment_split_factor: 0 # 0 disables chunking, >0 splits solve/apply domains into 2^k chunks
  alignment_clip_fixed_to_moving: true
  alignment_output_on_moving_grid: true
  alignment_trim_edge_invalid: true
  alignment_edge_trim_depth: 8 # Number of edge pixels to trim
  alignment_edge_trim_detection_band_index: 0 # 0-based band used to detect edge artifacts
  alignment_edge_trim_invalid_below: -3000 # Values <= this are treated as invalid during edge trimming
  # alignment_edge_trim_invalid_above: 65535 # Values >= this are treated as invalid during edge trimming
  alignment_enforce_mutual_valid_mask: true
  # alignment_solve_resolution: 2.0 # Optional target pixel size for registration solve

seamline_metadata:
  seamline_metadata_layer: footprints
  seamline_metadata_image_field_name: image
  seamline_metadata_footprint_source: package_bounds # package_bounds | calculate_bounds. package_bounds uses the WorldView footprint shapefile; calculate_bounds polygonizes the processed raster valid-data mask
  seamline_metadata_calculate_bounds_eight_connected: true # Use 8-connectedness when footprint_source is calculate_bounds

radiometric_normalization:
  radiometric_normalization_method: spectralmatch # spectralmatch

  # Optionally, run named SpectralMatch groups. Keys are output file names saved under the resolved output folder. Values have two options: "auto:" patterns use wcmatch against finished scene output filenames; "file:" values are literal tif paths.
  # group_by_basename: {"name.tif": "auto:**/*.tif"}
  # group_by_basename: {"name.tif": ["auto:*893242343*", {"nested.tif": ["file:/path/to/reference.tif", "auto:*222222222*"]}, "auto:*123456789*"]}

  # Pass SpectralMatch arguments (see https://spectralmatch.github.io/spectralmatch/api/pipeline/) through using the `match_` prefix, which will be removed before passing to Spectralmatch
  # These are already passed in by vhrharmonize to spectralmatch automatically, to override them, set them
  # match_shared_input_images: ["/path/to/scene_output_1.tif", "/path/to/scene_output_2.tif"] # Resolved from the previous steps output
  # match_shared_output_image_path: /path/to/image.tif # Resolved from save_radiometric_normalization
  # match_shared_temp_dir: ./temp/spectralmatch # Resolved from temp_dir
  # match_delete_temp_dir: true # Resolved from keep_temp_dir
  # match_shared_debug_logs: false # Resolved from log_to_console
  # match_shared_output_dtype: int16 # Resolved from dtype
  # match_shared_concurrent_processing_backend:  # Resolved from concurrent_processing_backend
  # match_shared_dask_scheduler: null # Resolved from dask_scheduler_file/address, e.g. ["file", "/tmp/spectralmatch-dask-1.json"]

  # Other optional SpectralMatch pipeline kwargs, defaults are often sufficient:
  
  # Shared runtime/output options
  # match_shared_resume_from_steps: no # no | yes | validate
  # match_shared_cache: auto
  # match_shared_custom_nodata_value: -9999
  # match_shared_window_size: 1024
  # match_shared_image_threads: auto
  # match_shared_io_threads: auto
  # match_shared_tile_threads: auto
  # match_shared_calculation_dtype: float32
  # match_shared_save_as_cog: false

  # Pipeline order.
  # Allowed steps: joint_coregistration, align, global_regression, local_block_adjustment, voronoi_center_seamline, weighted_seamline, mask, merge. Only one seamline step may be included.
  # Below matches SpectralMatch's default pipeline; a separate align step is unnecessary after joint_coregistration.
  # match_steps: [joint_coregistration, global_regression, local_block_adjustment, voronoi_center_seamline, mask, merge] # For mosaic
  # match_steps: [joint_coregistration, global_regression, local_block_adjustment, align] # For a time series

  # Joint coregistration
  # match_joint_coregistration_global_model: translation # none | translation | similarity | affine
  # match_joint_coregistration_global_image_position_preservation_weights: null # Mapping of image basename to weight
  # match_joint_coregistration_global_tie_point_alignment_strength: 1.0
  # match_joint_coregistration_local_model: piecewise_affine # none | bilinear | piecewise_affine
  # match_joint_coregistration_local_image_position_preservation_weights: null # Mapping of image basename to weight
  # match_joint_coregistration_local_tie_point_alignment_strength: 1.0
  # match_joint_coregistration_local_grid_spacing: 500.0
  # match_joint_coregistration_local_smoothness_weight: 1.0
  # match_joint_coregistration_local_bending_weight: 1.0
  # match_joint_coregistration_local_anchor_falloff_distance: 500.0
  # match_joint_coregistration_feature_method: orb
  # match_joint_coregistration_maximum_tie_point_displacement: null
  # match_joint_coregistration_ransac_reprojection_threshold: null
  # match_joint_coregistration_robust_loss: huber # none | huber | soft_l1 | cauchy
  # match_joint_coregistration_robust_loss_scale: null
  # match_joint_coregistration_save_adjustments: /path/to/tie_points.json
  # match_joint_coregistration_load_adjustments: /path/to/tie_points.json
  # match_joint_coregistration_resampling_method: bilinear # nearest | bilinear | cubic | lanczos
  # match_joint_coregistration_tap: true
  # match_joint_coregistration_resolution: highest
  # match_joint_coregistration_build_overviews: false

  # Optional standalone alignment. Skip this when joint_coregistration performs the alignment/tap.
  # match_align_rasters_resampling_method: bilinear
  # match_align_rasters_tap: true
  # match_align_rasters_resolution: highest

  # Global regression
  # match_global_regression_vector_mask: [include, /path/to/global_mask.gpkg, image]
  # match_global_regression_estimate_stats: true
  # match_global_regression_specify_model_images: [include, [image_a, image_b]]
  # match_global_regression_custom_mean_factor: 1.0
  # match_global_regression_custom_std_factor: 1.0
  # match_global_regression_save_adjustments: /path/to/adjustments.json
  # match_global_regression_load_adjustments: /path/to/adjustments.json
  # match_global_regression_pif_method: entire
  # match_global_regression_pif_red_band_index: 2
  # match_global_regression_pif_nir_band_index: 6
  # match_global_regression_pif_vegetation_threshold: 0.2
  # match_global_regression_pif_inz_threshold: 0.25
  # match_global_regression_pif_region_radius: 5
  # match_global_regression_pif_max_samples: 100000
  # match_global_regression_pif_min_samples: 32
  # match_global_regression_pif_feature_method: orb
  # match_global_regression_pif_load_tie_points: /path/to/tie_points.json
  # match_global_regression_pif_save_inz: /path/to/inz_outputs
  # match_global_regression_build_overviews: false
  # Local block adjustment
  # match_local_block_adjustment_vector_mask: [include, /path/to/local_mask.gpkg, image]
  # match_local_block_adjustment_number_of_blocks: 100
  # match_local_block_adjustment_alpha: 1.0
  # match_local_block_adjustment_correction_method: offset
  # match_local_block_adjustment_save_block_maps: [/path/to/reference_block_map.tif, /path/to/local_block_maps/$.tif]
  # match_local_block_adjustment_load_block_maps: [/path/to/reference_block_map.tif, /path/to/local_block_maps]
  # match_local_block_adjustment_override_bounds_canvas_coords: [0, 0, 1000, 1000]
  # match_local_block_adjustment_build_overviews: false

  # Seamlines
  # Select the seamline algorithm by including either `voronoi_center_seamline` or `weighted_seamline` in match_steps.
  # match_voronoi_center_seamline_aoi_path: /path/to/aoi.gpkg
  # match_voronoi_center_seamline_vector_mask: [/path/to/footprints.gpkg, image]
  # match_voronoi_center_seamline_image_field_name: image
  # match_voronoi_center_seamline_min_point_spacing: 10
  # match_voronoi_center_seamline_min_cut_length: 0
  # match_voronoi_center_seamline_debug_vectors_path: /path/to/debug_vectors.gpkg
  # Weighted seamline inputs. These pair nicely with `run_seamline_metadata: true` above.
  # match_weighted_seamline_input_polygons: /path/to/seamline_metadata.gpkg
  # Rank functions support field placeholders plus Python expression operators: +, -, *, /, //, %, **, comparisons, and/or/not, parentheses, and math functions abs, min, max, round, sqrt, log, log10, exp, sin, cos, tan.
  # match_weighted_seamline_rank_function: "-{cloud_cover} + {sun_elevation}"
  # match_weighted_seamline_image_field_name: image
  # match_weighted_seamline_input_layer: footprints
  # match_weighted_seamline_output_layer: seamlines
  # match_weighted_seamline_rank_descending: true

  # Masking
  # match_mask_rasters_vector_mask: [include, /path/to/seamline_mask.gpkg, image]
  # match_mask_rasters_include_touched_pixels: false

  # Merge
  # match_merge_rasters_resolution: highest
  # match_merge_rasters_build_overviews: true
