Skip to content

from_template: use CF Conventions metadata instead of crs_units/crs_name#3532

Merged
brendancol merged 2 commits into
mainfrom
issue-3531
Jun 26, 2026
Merged

from_template: use CF Conventions metadata instead of crs_units/crs_name#3532
brendancol merged 2 commits into
mainfrom
issue-3531

Conversation

@brendancol

Copy link
Copy Markdown
Contributor

Closes #3531.

from_template was tagging its output with two non-standard attrs, crs_units and crs_name. This swaps them for CF (Climate and Forecast) Conventions metadata, the standard xarray and the geoscience stack already use.

Changes

  • Drop crs_units. Units now live on the x/y coordinates where they belong. EPSG:4326 axes get degrees_east/degrees_north (was a non-CF 'degree'); projected axes get m, each with the matching CF standard_name (longitude/latitude, projection_x_coordinate/projection_y_coordinate).
  • Replace crs_name with the CF grid-mapping keys grid_mapping_name (e.g. albers_conical_equal_area) and crs_wkt (full WKT, which carries the human-readable name). This lines up with the rest of the library, which keys off crs/crs_wkt.
  • Source the CF keys from pyproj.CRS.to_cf(), kept best-effort: without pyproj the default path stays dependency-free and the keys are omitted, same as crs_name before. Equal Earth has no CF grid mapping, so grid_mapping_name is left off there and crs_wkt stands alone.

Backends

Metadata-only change; behaviour is identical across numpy / cupy / dask+numpy / dask+cupy.

Test plan

  • test_cf_coordinate_units — CF units + standard_name on x/y, no crs_units
  • test_cf_grid_mapping_attrsgrid_mapping_name + crs_wkt, no crs_name
  • test_cf_grid_mapping_preserve_path — preserve path keys present
  • test_grid_mapping_omitted_for_equal_earthcrs_wkt only for 8857
  • test_cf_attrs_omitted_without_pyproj — graceful dependency-free fallback
  • Full test_templates.py suite green (51 passed)

Replace the ad-hoc crs_units/crs_name attrs with CF grid-mapping keys
grid_mapping_name + crs_wkt (via pyproj.CRS.to_cf, best-effort) and move
units onto the x/y coordinates with CF standard_names. EPSG:4326 axes use
degrees_east/degrees_north; projected axes use metres.

@brendancol brendancol left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

PR Review: from_template — CF Conventions metadata

Blockers

None.

Suggestions

  • Flat attrs vs. a real CF grid mapping (templates.py:325, :338-345). Strict CF puts grid_mapping_name on a separate scalar grid-mapping variable that the data variable points to via a grid_mapping attribute; here both grid_mapping_name and the coordinate standard_names sit directly on the data variable. That matches how this library already models CRS (flat attrs, crs/crs_wkt), so it's a reasonable call — but a strict CF reader like cf_xarray won't auto-detect it as a grid mapping. Worth a one-line docstring caveat, or a follow-up if fuller CF encoding is ever wanted. Not blocking.
  • Narrower exception catch than before (templates.py:194-200). _cf_crs_attrs catches only ImportError; the old _crs_name also swallowed ValueError/TypeError. Every caller passes a curated/queried EPSG int, so from_epsg().to_cf() won't fail in practice — but if an unknown code ever reached here it would now raise instead of degrading to "no CF keys." Low risk given current call sites; flagging for awareness.

Nits

  • Inconsistent wrapping (templates.py:339-345). The 4326 branch keeps .attrs.update(...) on one line (83 chars) while the projected branch wraps onto two. Both fit the 100-char limit; pick one style for symmetry.

What looks good

  • Clean swap: no code outside the tests still reads crs_units/crs_name from a template, so nothing downstream breaks.
  • crs_wkt lines up with the geotiff layer, which already documents crs/crs_wkt as the canonical, additive CRS keys.
  • Test coverage is solid — both coordinate flavors, both grid-mapping branches, plus the two real edge cases (Equal Earth has no CF mapping, and the pyproj-absent fallback).

@brendancol brendancol left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Follow-up review (after fixes)

Re-reviewed the two follow-up commits. Disposition of the first-pass findings:

  • Nit — inconsistent wrapping: fixed. Both branches of the coordinate-units block now use single-line .attrs.update(...) (all within the 100-char limit).
  • Suggestion — flat attrs vs. CF grid-mapping variable: addressed by docstring. The Returns section now states the keys sit on attrs (the library's flat CRS convention), not a separate CF grid-mapping variable, so a strict CF reader won't auto-detect them.
  • Suggestion — narrower exception catch in _cf_crs_attrs: dismissed. Every call site passes a curated or pyproj-queried EPSG int, so from_epsg().to_cf() can't fail in practice. CLAUDE.md asks for no error handling on unreachable paths, so I left the catch scoped to ImportError (the only real branch — the dependency-free fallback).

flake8 clean; test_templates.py green (51 passed). No blockers outstanding.

@brendancol brendancol merged commit 12f8b00 into main Jun 26, 2026
10 checks passed
brendancol added a commit that referenced this pull request Jun 26, 2026
main replaced crs_units/crs_name with CF grid-mapping attrs. Update the
city sample test to assert CF coord units (x.units=='m', standard_name)
and the notebook to print grid_mapping_name instead of crs_name.
brendancol added a commit that referenced this pull request Jun 26, 2026
)

* Add world-city bounding-box templates to from_template (#3533)

519 cities (national capitals + metros >=1.2M pop) from Natural Earth,
each in its UTM zone (EPSG:326xx/327xx). Wired into _resolve between the
curated regions and country codes; preserve='area'/'shape' work as for nyc.

* Add city tests and flake8/isort-clean formatting (#3533)

- 5 tests: registry integrity over all 519 cities, sample builds, UTM
  spot-checks, case-insensitivity, name-collision disambiguation.
- Reformat generated _CITIES entries to a hanging indent (flake8 max-line
  100) and apply isort to the touched imports.

* Document world-city templates: rst, README, user-guide notebook (#3533)

* Address review nit: exercise southern-hemisphere city build path (#3533)

Add sao_paulo/sydney (327xx) to test_city_sample_builds so the southern
build is explicit, not only implied by the UTM spot-checks. Discoverability
suggestion deferred to follow-up #3535 (new public listing API needs its
own design).

* Reconcile city templates with CF-metadata switch from main #3532 (#3533)

main replaced crs_units/crs_name with CF grid-mapping attrs. Update the
city sample test to assert CF coord units (x.units=='m', standard_name)
and the notebook to print grid_mapping_name instead of crs_name.

* Add recognizable US secondary cities to from_template (#3533)

Natural Earth's POP_MAX/SCALERANK underrate US metros (Las Vegas listed at
17k, scalerank 8), so a population cutoff misses Austin, New Orleans, Las
Vegas, Portland, etc. Add a curated US name list (top match per name) run
through the same UTM-zone + metro-buffer machinery. 519 -> 558 cities.
@brendancol brendancol deleted the issue-3531 branch July 1, 2026 14:39
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.

from_template: use CF Conventions metadata instead of crs_units/crs_name

1 participant