Skip to content

Architecture

Locus is a toolkit, not a fixed dataset. It builds and serves location intelligence on top of any standard spatial grid system and any number of geographic data layers, without hardcoding one choice into the runtime. Consumers pick the grid system and the layers they need; the builder composes artifacts from pluggable source adapters; the runtime decoder stays agnostic to both.

Principles

  • Consumers choose, we don't. A GridSchemeInterface is the seam between "how cells are addressed" and everything built on top of it. Geohash ships first because it's what's already proven end-to-end, but it is one implementation of the interface, not the interface itself.
  • The builder composes from adapters, not from one hardcoded pipeline. Every external data source (Natural Earth, US Census TIGER, IANA tzdata, country-coder) is a SourceAdapterInterface implementation. Building an artifact means picking a grid scheme + a set of adapters + parameters (region bounds, precision, which layers).
  • The runtime decoder reads a scheme identifier, not an assumption. An artifact declares which GridSchemeInterface it was built with; the decoder dispatches to the matching scheme implementation. Adding a new grid system never requires touching a consumer's code, only registering a new scheme.
  • No layer is mandatory. A consumer building for a single country doesn't pay for congressional-district data they'll never query. Layers are independently selectable at build time, tree-shakeable at the module level.

Grid systems

@studnicky/grid-schemes holds the GridSchemeInterface contract and every concrete scheme implementation. Both geo-data-builder and geo-resolver depend on it; neither hardcodes geohash-specific logic:

ts
interface GridSchemeInterface {
  readonly schemeId: string;                 // e.g. "geohash", "s2", "h3"
  encode(lat: number, lon: number, precision: number): CellIdType;
  decode(cellId: CellIdType): BboxType | null;
  parent(cellId: CellIdType): CellIdType | null;
  children(cellId: CellIdType): readonly CellIdType[];
  neighbors(cellId: CellIdType): readonly CellIdType[];
  covering(bbox: BboxType, maxCells?: number): readonly CellIdType[];
  resolution(cellId: CellIdType): { approxEdgeMeters: number };
}
SystemStructureStrengthStatus
GeohashBase32 string, alternating lat/lon bit interleaveHuman-readable, huge library ecosystem, good enough for point lookupShipped
S264-bit cell ID, quadtree over a cube-projected sphereBest cell-shape uniformity, best-in-class cell-union covering algorithmsPlanned — gated on a concrete coverage/region-query consumer
H3Hexagonal hierarchical grid, uniform 6-neighbor adjacencyBest for radius search / flood-fill / k-NN queriesPlanned — gated on a concrete radius/proximity-query consumer

Data layer taxonomy

Every layer is backed by whatever a region's own recognized system is, not a single hardcoded source. A layer like "county" isn't "US Census TIGER" — it's "whichever official administrative-division system the queried region actually publishes," with TIGER as the US implementation and other countries' systems addable later as their own implementations of the same contract.

LayerSource (first implementation)Coverage
State/provinceNatural Earth admin-1Global
County/parishUS Census TIGER/LineUS-only
Postal code (ZCTA)US Census TIGERUS-only
Voting/electoral districtUS Census TIGERUS-only

Source adapter architecture

Two contracts separate fetch mechanics from region-specific recognition:

ts
interface SourceAdapterInterface {
  readonly sourceId: string;
  readonly license: string;
  fetch(params: FetchParamsType): Promise<RawDatasetType>;
  readonly refreshPolicy: 'static' | 'check-for-updates';
}

interface AdministrativeSystemAdapterInterface {
  readonly systemId: string;            // e.g. "us-census-tiger"
  readonly coverage: readonly string[]; // ISO 3166-1 alpha-2 codes this system covers
  readonly layers: readonly AdminLayerType[];
  fetchLayer(layer: AdminLayerType, params: FetchParamsType): Promise<RawDatasetType>;
}

USCensusTigerAdapter is the first AdministrativeSystemAdapterInterface implementation (coverage: ['US']), not a special case baked into the pipeline. Adding a second region means writing a new implementation, not touching the contract or anything that already consumes it.

Package shape

One independently-loadable package per data layer — a consumer who only wants postal codes never installs country/voting-district/census code:

packages/
  geo-layer-state/            @studnicky/geo-layer-state
  geo-layer-county/           @studnicky/geo-layer-county
  geo-layer-postal/           @studnicky/geo-layer-postal
  geo-layer-voting-district/  @studnicky/geo-layer-voting-district

Each layer package depends on @studnicky/grid-schemes for cell addressing, ships its own binary artifact built by its own geo-data-builder-style pipeline, and exposes a lookup surface following the workspace's established conventions. A layer backed by an AdministrativeSystemAdapterInterface additionally exposes which systemId/region implementations it currently ships.

Further reading

The full design — grid-system tradeoffs, the phased roadmap, the data-ingest pipeline rails built on Dagonizer, and open questions on coverage and artifact shape — lives in-repo: