GeoGenius.Providers.ImpliedAreas (geo_genius v0.2.0)

Copy Markdown View Source

Areas a source row implies through columns other than its own code.

A row that carries a grouping code -- a statistical grouping on an administrative record, an administrative parent on a place record -- describes that grouping as well as itself. This module reads those columns from a manifest option and returns both the implied areas and the edges from each implied area to the row's own area.

An implied area carries no geometry and no centroid. A grouping a source names only by code has no shape of its own in that source, and deriving one from the row's geometry would claim the grouping's boundary is one member's boundary.

The option is a list under "implied_areas". Each entry names an "area_type", the column carrying the code, an optional "names" map from code to display name, an optional "authority" override, and an optional "relation" defaulting to "contains". The column key is the calling provider's own: "code_property" for GeoGenius.Providers.GeoJSON, "code_column" for GeoGenius.Providers.CSV.

Argument order matches GeoGenius.Providers.ManifestOptions, this module's sibling: payload first, option keys last.

Summary

Types

One parsed implied_areas entry.

Functions

The areas payload implies, one per entry whose code column is populated.

The edges payload asserts, one per entry whose code column is populated, each running from the implied area to the row's own area.

Parses the implied_areas option, reading each entry's code column under code_option_key.

:ok when every implied_areas entry parses under code_option_key, or the first entry's error.

area alone when the manifest implies nothing, or area followed by every area payload implies.

Types

entry()

@type entry() :: %{
  area_type_key: String.t(),
  code_field: String.t(),
  names: %{optional(String.t()) => String.t()},
  relation: String.t(),
  authority_key: String.t() | nil
}

One parsed implied_areas entry.

Functions

areas(payload, entries, authority_key)

@spec areas(map(), [entry()], String.t()) ::
  {:ok, [GeoGenius.Provider.Area.t()]} | {:error, String.t()}

The areas payload implies, one per entry whose code column is populated.

A blank code implies no area and is not an error: a source whose grouping column is populated for most rows and blank for a few is ordinary, and failing the release over it would be wrong. A populated code with no entry in names is an error, because keying an area with no name would publish an unlabelled area.

authority_key is the row's own authority, used for any entry that declares no "authority" of its own.

edges(payload, entries, authority_key, child_area_key)

@spec edges(map(), [entry()], String.t(), String.t()) :: [
  {String.t(), String.t(), String.t()}
]

The edges payload asserts, one per entry whose code column is populated, each running from the implied area to the row's own area.

The parent end is composed through GeoGenius.Provider.Area.key/1 rather than by joining strings, and child_area_key must have been composed the same way by the caller: an edge composed a second way can name a key normalize/2 never produced, and GeoGenius.Catalog.put_relation_many/4, which is what the relating phase calls, refuses an area the release does not carry.

Returns a bare list because GeoGenius.Provider.asserted_relations/2 does. An entry that cannot be parsed has already failed normalize/2, which runs first.

parse(options, code_option_key)

@spec parse(map(), String.t()) :: {:ok, [entry()]} | {:error, String.t()}

Parses the implied_areas option, reading each entry's code column under code_option_key.

An absent option is not an error: most manifests imply nothing.

validate(options, code_option_key)

@spec validate(map(), String.t()) :: :ok | {:error, String.t()}

:ok when every implied_areas entry parses under code_option_key, or the first entry's error.

This is the shape GeoGenius.Provider.validate_options/1 returns, so a provider whose only manifest-wide check is its implied areas delegates here rather than discarding parse/2's entries at each call site.

with_implied(area, payload, entries, authority_key)

@spec with_implied(GeoGenius.Provider.Area.t(), map(), [entry()], String.t()) ::
  {:ok, GeoGenius.Provider.Area.t()}
  | {:ok, [GeoGenius.Provider.Area.t()]}
  | {:error, String.t()}

area alone when the manifest implies nothing, or area followed by every area payload implies.

A provider whose manifest declares no implied_areas keeps returning a bare {:ok, %Area{}} from GeoGenius.Provider.normalize/2, which is the shape every existing manifest depends on. Only a manifest that actually implies something sees the list form.

This lives here rather than in each provider because both generic providers need exactly this, and the substance it wraps -- areas/3 -- is already this module's.