Skip to content

viz: --geojson auto for NAME-keyed region columns (city-name works via geocode; county-name needs a new mechanism) #4417

Description

@jqnatividad

Amended. As originally filed, this issue claimed forward_geocode_regions could resolve a county-NAME column ("Allegheny County") to a county FIPS. That is wrong, and the correction changes what this issue is actually asking for. The original mechanism is sound — but for a city-name column, not a county-name one. Details below.

Summary

--geojson auto (#4397, PR #4418) resolves boundaries from region codes (county FIPS, ZCTA, tract, place GEOID). Extend it to region columns that hold names rather than codes, by resolving them to FIPS through the Geonames engine qsv geocode already ships.

Why this is a real gap

REGION_CODE_LEAVES (src/cmd/viz.rs) already includes "county" as distinct from "county_fips"/"fips", and match_region_code already case-folds alphabetic region codes — so name-keyed region columns are a supported shape today whenever the user supplies their own GeoJSON. They are exactly the columns auto cannot serve.

What the geocode engine can and cannot do (the correction)

forward_geocode_regions (src/cmd/geocode.rs) calls:

engine.suggest::<String>(name, 1, None, None)

That searches the cities index — the Geonames citiesNNNNN gazetteer. It matches populated places.

admin2Codes.txt (DEFAULT_ADMIN2_CODES_URL) is loaded only to decorate a matched city record with cityrecord.admin2_division. Counties are not independently searchable, and the --admin1 option is a filter applied to city results, not a name search over administrative divisions.

So:

column holds resolvable today? how
a city name (Pittsburgh) yes suggest → city record → admin2_divisionus_fips_from_codes42003
a county name (Allegheny County) no suggest fuzzy-matches it against city names; it does not reach a county record

The original issue conflated these. A literal "Allegheny County" value does not resolve through this path.

Two separable pieces of work

A. City-name columns → county FIPS (the mechanism that works)

suggest → matched city → its admin2_division → county FIPS → the existing county boundary path from #4397. This is a lookup, not a spatial approximation, so the nearest-populated-place caveat below does not apply — the answer is "what county is this city in", which Geonames states directly.

Needs GeoRegion to expose the FIPS pair: it currently carries admin1_code / us_state_code but not admin2 or the FIPS codes, even though us_fips_from_codes(admin1_code, admin2_code) already exists as a pure function and us_fips_strings shows the admin2 access pattern. GeoRegion's own doc comment says the struct is "a complete region-identifier surface … populated for reuse by future callers" — this is that caller. ~5 lines.

Geonames index resolution is the binding constraint here. The default is cities15000 (~26k cities worldwide, ~800 US), which omits most US small towns, so a city-name column will under-resolve badly. cities1000 (~140k) or cities500 (~200k) largely fixes it.

But do not change the global default to fix this: it is a cost imposed on every geocode user (13mb → 56mb index, ~26k → ~200k records, slower lookups, more memory) and the help text explicitly warns that "search results will be different". The prebuilt index published from the qsv repo is English-only cities15000, so changing the default also means shipping a much larger prebuilt or pushing users to build locally.

Instead, detect and report — the same pattern #4397 uses for a vintage mismatch, which turns a dead end into an instruction:

--geojson auto: resolved 40 of 300 place names. The default cities15000 Geonames index
omits places under 15,000 population; rebuild with `qsv geocode index-update --cities-url 1000`.

B. County-name columns (what this issue originally asked for)

Needs a mechanism that does not exist yet. Options, roughly in order of appeal:

  1. Make admin2 records searchable — build a lookup over admin2Codes.txt (name → US.PA.003), which is already downloaded. Cheap, offline, and exactly keyed to the question. Ambiguity (Washington County exists ~30 times) is resolved by a state column, or reported.
  2. Match TIGERweb's NAME/BASENAME — the county layer carries both (Beaver County / Beaver). Tempting, but bare names are ambiguous across states, so it needs a state from somewhere regardless.
  3. Require the user to supply a state column alongside, and disambiguate with it.

Whichever is chosen, the honesty check is the same as the code path: resolution rate over distinct names, with ambiguous bare names reported rather than silently resolved to an arbitrary state.

Important: do NOT extend either of these to lat/lon reverse geocoding

Investigated and rejected during #4397. reverse_geocode_regions returns the nearest populated place's county, not the containing county. A point in rural Fayette County PA whose nearest city record sits in Westmoreland yields the wrong county — and nothing downstream detects it, because every derived FIPS is a real county, so any resolution-rate check reads 100%.

A denser index does not rescue this. It makes the error rarer without making it detectable, which is a worse failure mode than the common one: it survives testing, so you stop looking for it. The correct mechanism for bare lat/lon boundaries is point-in-polygon against fetched polygons, which #4397 also defers.

Reverse geocoding remains safe for deriving a state set (a nearest-city match essentially never crosses a state line, and when it does the set merely widens, which is harmless).

Acceptance

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request. Once marked with this label, its in the backlog.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions