Skip to content

Fortress basin aoi - #81

Merged
f-PLT merged 23 commits into
mainfrom
fortress-basin-aoi
Sep 3, 2026
Merged

f-PLT merged 23 commits into
mainfrom
fortress-basin-aoi

Conversation

@f-PLT

@f-PLT f-PLT commented Jul 10, 2026

Copy link
Copy Markdown
Collaborator

Summary

This PR adds a new data pipeline to enable processing AOIs directly instead of individual points, like was previously done to create the training datasets.

Changes

  • Add new Hybrid GEE - local pipeline
  • Add seamless native-utm GEE export
  • Fixes missing normalizer use from previous work with the local-data pipeline
  • Several QoL refactors and code cleanups

Description

Hybrid GEE→local pipeline

Allows FSC inference over an arbitrary AOI without a local raw archive: download cubes from GEE, infer locally.

Two new Typer CLIs in scripts/developer_scripts/fortress_mountain_basin/ :

  • build_aoi_cubes_gee_url.py : tiles AOI (Mode B lattice from the Local-data pipeline), creates cube CSV, downloads cubes via GEE URL mode.
  • infer_aoi_cubes.py : reads cube CSV + cubes on disk, runs the finetuned model, writes daily FSC COGs for the AOI (1 per inference day for the whole AOI).

Documentation can be found in docs/hybrid_inference_pipeline.md.

Seamless native-UTM GEE export

eo_eval.py: new export_from_csv_utm_native. Builds the request rectangle directly in the row's UTM CRS at scale=10 instead of round-tripping through 4326. Kills the grid-convergence 0-border seams between tiles.

Parallelised via ThreadPoolExecutor in url mode.

Both UTM readers now accept center_lat/lon or center_x/y dialects.

Bug fix : missing normalizer (correctness, affects prior results)

_loader_bridge.py set ds.normalizer = None, feeding the encoder raw physical units (DN, dB, m, K) against a checkpoint trained on std-normalized inputs. Now builds the same Normalizer the GEE/eval path uses.

Any FSC output produced (or that has not been re-run) before this commit is wrong-but-plausible.

Refactors

  • inference/model.py : build_model() extracted; both entry points had drifted private copies (one returned CPU model, one device-resident).
  • inference/prebuilt.py : PrebuiltCubeSource: re-run inference over already-exported cubes, no re-export.
  • driver.py : takes days: list[date] instead of window_start/window_end (non-contiguous dates now possible); adds read_workers threaded cube reads (~6.3→20.8 cubes/s at 4 workers).
  • layout.py : cell_centre_lat_lon() + GEOGRAPHIC_CRS hoisted out of exporter.py (was private, driver reached into exporter._cell_centre_lat_lon).
  • grid.py : build_cells() split from build_grid(); build_cube_csv_for_gee_utm() adapter; filter_cells→_filter_cells.
  • infer_bow_valley_daily_fsc.py : --cubes-only, --dates/--dates-file, --cube-dir, --read-workers.

Current limitations

  • There is no dynamic CRS reprojections currently - The CRS is hardcoded to The Fortress Mountain Basin's UTM zone.
    • This work is already planned and should be completed soon.

LLM Disclosure

This PR has been assisted by the use of Claude Code

f-PLT added 4 commits July 8, 2026 14:23
Export FSC input cubes directly in the cell's native UTM CRS at scale=10
instead of round-tripping through EPSG:4326. The 4326 path left a grid-
convergence tilt and a 0-valued border sliver around each tile, so adjacent
cubes did not tile seamlessly.

- eo_eval.py: new export_from_csv_utm_native — builds the request rectangle
  in the row's UTM crs, grows each cell by a buffer_m halo (neighbours overlap
  2x buffer_m for a downstream mean-mosaic to reconcile), and exports at
  crs=<UTM>+scale=10 so EE resamples every band onto one shared 10m lattice.
  Verified: tiles are 108x108, origins on a common 10m grid, zero 0-border
  pixels, 80m overlap between neighbours, across all dates.
- eo_eval.py: _export_for_polygon exports at scale=10 with the caller's crs
  (dropped an earlier crs_transform approach that failed with "inconsistent
  bounding boxes" on dates whose bands had mismatched native footprints).
- build_aoi_cubes_gee_url.py: tile the AOI (Mode B), emit the GEE-UTM cube
  CSV, and drive the native export; --buffer-m controls the overlap halo.

Assisted by Claude Code
url mode does a blocking getDownloadURL + download per cube, so it is
network-bound and benefits from a small thread pool. Run the per-row exports
through a ThreadPoolExecutor (default 4 workers); cloud/drive mode stays
serial since it only fires batch submits that mutate the shared ee_task_list.

- eo_eval.py: export_from_csv_utm_native gains max_workers (default 4); the
  per-row body is a closure dispatched via ThreadPoolExecutor for url mode.
- build_aoi_cubes_gee_url.py: --max-workers flag wired through and logged.

Workers are capped low deliberately: GEE throttles getDownloadURL, so a large
pool returns 429s rather than speed.

Assisted by Claude Code
@f-PLT f-PLT self-assigned this Jul 10, 2026
@f-PLT
f-PLT marked this pull request as ready for review July 17, 2026 15:56
@f-PLT
f-PLT requested a review from marlens123 July 17, 2026 16:05
@f-PLT

f-PLT commented Jul 17, 2026

Copy link
Copy Markdown
Collaborator Author

Entrypoint into this PR is the following documentation :

As well as these 2 scripts :

I'm not strictly done with refactoring, but it's far enough along for you to try it out and see if the results line up with what you have on your end.

No need to hurry, we can take a look together when I get back.


Also, I'm pretty confident I'll be able to produce longer time series, as a single month's worth of fsc prediction for the Fortress Mountain Basin is less than 1% of my monthly quota on the non-commercial license, and takes a few hours to produce the cubes and run inference.

@marlens123 marlens123 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Most of it looks good to me!
I left some reoccurring comments about the supported UTM coordinate system - in case it's dynamic, some comments have to be changed.
The snippets in hybrid_inference_pipeline.md are working for me - except there is a change required for the GEE authentication on my end (see comment).

Things I haven't checked:

  • the exported input and FSC tifs in more detail
  • the aoi file
  • the PLAN.md file
  • the test/ files

I also do not fully understand/check the grid.py file and how exactly it tiles the aoi into cells.

Comment thread docs/hybrid_inference_pipeline.md
Comment thread docs/hybrid_inference_pipeline.md
Comment thread docs/hybrid_inference_pipeline.md
Comment thread src/snow_galileo/inference/model.py
Comment thread src/snow_galileo/data/local_sources/grid.py
Comment thread src/snow_galileo/data/local_sources/grid.py
Comment thread src/snow_galileo/data/local_sources/grid.py
Comment thread src/snow_galileo/data/local_sources/grid.py
@f-PLT
f-PLT merged commit 2da5970 into main Sep 3, 2026
3 checks passed
@f-PLT
f-PLT deleted the fortress-basin-aoi branch September 3, 2026 20:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants