Add major world cities as bounding-box templates in from_template#3534
Merged
Conversation
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.
- 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.
brendancol
commented
Jun 26, 2026
brendancol
left a comment
Contributor
Author
There was a problem hiding this comment.
Review: world-city bounding-box templates
Blockers
None.
Suggestions
- Discoverability. The unknown-name error points to "the templates reference" (
templates.py:69-70), but neithertemplates.rstnor any public API lists the 519 city names —_CITIESis private. Country codes have the same gap, except ISO-3166 is a discoverable standard and an ad-hoc city list isn't. A follow-up that exposes a names listing (or a reference note on how to find them) would help. Not blocking.
Nits
test_city_sample_builds(test_templates.py:187) builds onlysorted(_CITIES)[:20], which is alphabetical and northern-heavy. A southern-hemisphere build is covered in practice becausetest_city_utm_spot_checkscallsfrom_template("sao_paulo"), but adding one southern city to the sample loop would make that explicit.
What looks good
- Cities slot into
_resolvebetween regions and countries (templates.py:49-55) and reuse_make_dataunchanged, so all four backends work with no dispatch edits (checked locally on dask+numpy / cupy / dask+cupy). - UTM-zone-from-centroid keeps every city on a standard EPSG code, and
preserve='area'/'shape'fall through to the samenycbehavior for free. - Data is committed static (no runtime geopandas or network), and the
_CITIESheader documents the source, the UTM rule, the buffer sizing, and the collision rule. test_city_registry_integritychecks the shape of all 519 entries, so a bad regeneration fails in CI instead of at call time.
Checklist
- CRS is a standard EPSG code (UTM), not synthesized
- All four backends consistent
- crs_units correct (metres for UTM cities)
- Integrity covered for every entry
- No dask/dispatch changes; reuses existing paths
- No premature materialization (static dict)
- No benchmark needed (allocation-only, matches existing from_template)
- README + reference + notebook updated
- Docstring updated with city example and collision rule
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).
brendancol
commented
Jun 26, 2026
brendancol
left a comment
Contributor
Author
There was a problem hiding this comment.
Follow-up review
Changes since the first pass:
- Nit fixed:
test_city_sample_buildsnow includessao_pauloandsydney, so the southern-hemisphere (327xx) build path is exercised directly. - Discoverability suggestion deferred to #3535 — a public listing API is a separate design decision (name, return shape, whether to include the 240 country codes).
- Merged
origin/mainand reconciled with #3532, which replacedcrs_units/crs_namewith CF grid-mapping attributes. The city test now asserts CF coord units (x.attrs["units"] == "m",standard_name == "projection_x_coordinate") and the user-guide cell printsgrid_mapping_nameinstead ofcrs_name.
No new blockers. Local: 56 passed in test_templates.py, flake8 + isort clean, backend parity holds on dask+numpy / cupy / dask+cupy.
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #3533
Adds world cities to
from_template, following the existingnycpattern._CITIESdata next to the country bboxes. No runtime network or geopandas dependency. Selection = every national capital + metros with POP_MAX >= 1.2M + a curated set of recognizable US secondary cities (Austin, New Orleans, Las Vegas, Portland, ...), since Natural Earth's POP_MAX/SCALERANK underrate US metros._resolvebetween the curated regions and the country codes, sopreserve='area'/'shape'work exactly as they do fornyc. No backend-dispatch changes._<iso2>suffix (e.g.hyderabadvshyderabad_pk,birminghamUK vsbirmingham_us).Backend coverage: numpy / cupy / dask+numpy / dask+cupy — cities reuse the same
_make_datapaths as every other template (verified locally on all four).Test plan
pytest xrspatial/tests/test_templates.py(56 passed)