# `GeoGenius.Providers.SimpleMaps`
[🔗](https://github.com/agoodway/geo_genius/blob/main/lib/geo_genius/providers/simplemaps.ex#L1)

The SimpleMaps US cities and ZIP codes datasets.

Two delimited files, `uscities` and `uszips`, that denormalise the whole
hierarchy into every row: a city row names its county and state, a ZIP row
names every county it touches. Counties exist only as those columns, never
as rows of their own, so they are derived and validated rather than read.

Each file carries dozens of demographic columns this provider never reads;
a row's payload keeps every one of them, keyed by its real header name, so
reading another column is a change to `GeoGenius.Providers.SimpleMaps.Rows`
alone, with nothing to restage.

The data carries centroids and no boundaries, so relations are asserted
from the FIPS columns rather than measured, and `relations/1` is `:none`.

## Options

No option is required. By default every `state_id` value uses the `state`
area type. A collection that requires geometry for its ordinary states can
keep the six USPS-only codes (`AA`, `AE`, `AP`, `FM`, `MH`, and `PW`) in a
separately declared, non-geometric type:

    "options": {"non_census_state_area_type": "postal_region"}

The value is an area-type key chosen by the host, not a type this provider
registers. The manifest must declare it in `area_types`; its default remains
`state` so existing manifests and area keys do not change.

# `artifacts`

```elixir
@spec artifacts(GeoGenius.Manifest.t()) :: [GeoGenius.Manifest.Artifact.t()]
```

Both declared artifacts; the logical name selects the parser.

# `asserted_relations`

```elixir
@spec asserted_relations(GeoGenius.Manifest.t(), GeoGenius.Staging.Row.t()) :: [
  {String.t(), String.t(), String.t()}
]
```

State-like, county, city and ZIP edges read from the row's FIPS columns.

# `normalize`

```elixir
@spec normalize(GeoGenius.Manifest.t(), GeoGenius.Staging.Row.t()) ::
  {:ok, [GeoGenius.Provider.Area.t()]} | {:error, GeoGenius.Provider.reason()}
```

Describes each row's city or ZIP, counties, and state-like area.

# `relations`

```elixir
@spec relations(GeoGenius.Manifest.t()) :: :none
```

SimpleMaps carries no geometry to measure relations from.

# `required_options`

```elixir
@spec required_options() :: [String.t()]
```

Reads its columns by name, so a manifest requires no options.

# `stage`

```elixir
@spec stage(
  GeoGenius.Manifest.t(),
  GeoGenius.Manifest.Artifact.t(),
  Path.t(),
  ([GeoGenius.Staging.Row.t()] -&gt; :ok),
  GeoGenius.Provider.stage_opts()
) :: :ok | {:error, GeoGenius.Provider.reason()}
```

Reads `path` as comma-delimited text and emits its data rows in chunks of
1000 rows.

`geom` is always `nil` -- SimpleMaps centroids ride on the `Area` in
`normalize/2`, not on the staged row, because the source has no boundary
for a centroid to summarise.

# `validate_manifest`

```elixir
@spec validate_manifest(GeoGenius.Manifest.t()) :: :ok | {:error, String.t()}
```

Ensures the effective non-Census state type is declared and non-geometric.

# `validate_options`

```elixir
@spec validate_options(map() | nil) :: :ok | {:error, String.t()}
```

Validates the optional area type used for the six non-Census USPS state codes.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
