OpenHunt Docs
The pipeline

Adding a state

A new state is source YAML plus one registry row — no new rendering code.

The design goal: covering your state is data work, not engineering. Each layer is one YAML file in sources/, and the map renders whatever the registry declares. Every state we cover went in this way — Montana, the fifth, took a day.

Pennsylvania, the sixth, is the one that tested the claim, because it is the first eastern state and the first that is barely a draw state at all. The layers went in as advertised. What did not generalize was everything around them: the game-species vocabulary was western-only, the weather rasters stopped at the 101st meridian, and the draw registry assumed a state whose agency publishes draw statistics. The full internal runbook, including the sweep of places a new state can go silently missing, is docs/state-expansion.md in the repo.

The source YAML

A real one, sources/us/padus-co.yaml:

id: padus-co
name: "PAD-US 4.1 — Colorado (Fee)"
description: >
  USGS Protected Areas Database of the U.S., v4.1, clipped to Colorado.
  Fee layer = fee-simple ownership; the land-status base for hunters.
layer: public_lands
attribution: "USGS Gap Analysis Project, PAD-US 4.1"
license: public-domain
redistributable: true
cadence: annual

download:
  url: "https://www.sciencebase.gov/catalog/file/get/6759abcfd34edfeb8710a004?name=PADUS4_1_State_CO_GDB_KMZ.zip"
  archive: zip
  dataset: "**/*.gdb"

normalize:
  sql: |
    SELECT
      Unit_Nm    AS unit_name,
      Mang_Name  AS manager,
      Mang_Type  AS manager_type,
      Own_Name   AS owner,
      Own_Type   AS owner_type,
      Pub_Access AS access,
      GAP_Sts    AS gap_status
    FROM PADUS4_1Fee_State_CO

The pieces:

FieldWhat it declares
layerThe common schema this source maps into: public_lands, parcels, hunt_units, access_programs, trails, motorized, …
license / redistributableRecorded per source, enforced at publish — see licensing rules.
cadenceHow often the upstream refreshes, so re-runs are scheduled honestly.
downloadURL plus archive handling; ArcGIS feature services page through automatically.
normalize.sqlThe field mapping from the source's schema to the layer's common schema — this is where most per-source work lives.
tileZoom range and tippecanoe flags.

The registry row

The web app's layer registry (apps/web/src/map/engine.ts) declares which datasets exist and how they group per state. A new state adds its metadata (label and the unit term its hunters use — GMU, elk area, zone) and one row per dataset:

{ id: 'padus-mt', kind: 'ownership', sourceLayer: 'public_lands' },

Array order is draw order; there is no per-state rendering code.

What a full state looks like

The complete layer set for one state, using Colorado as the template:

  • padus-<st> — public lands (PAD-US state extract)
  • <st>-parcels — parcels (statewide program, or per-county sources where no open statewide fabric exists)
  • a hunt-unit source from the state wildlife agency
  • a walk-in access source, if the state publishes spatial data for it
  • osm-roads-<st> — roads (feeds landlocked)
  • trails (state inventory if one exists, OSM otherwise)
  • MVUM roads + trails (national USFS service clipped to the state bbox)
  • slope, aspect, and landlocked — derived, not downloaded

When sources fight back

Real lessons from the registry, all documented in the YAML they belong to:

  • Restricted licenses — California's only statewide parcel fabric is a licensed composite; it's ingested with redistributable: false for local dev and excluded from published tiles, while county sources that publish owners openly (Mendocino, Humboldt) carry the layer.
  • Ancient TLS — one county's legacy server only offers cipher suites modern clients refuse; the YAML documents a curl pre-fetch into the pipeline's cache layout as the workaround.
  • No spatial data at all — California's SHARE walk-in program exists only as text in a license-draw UI. Nothing to ingest; the docs say so rather than fake it.

On this page