Gunnar

nordisk/korp-collectors

korp-collectors

The standalone LEAF of korp's open-data collector layer. One object-safe Collector trait + per-access-METHOD import collectors (Swedish + EU open data), parsing pure-Rust → Apache Arrow → (optionally) Apache Iceberg via skade. Plus a RegionSource seam that FETCHES OSM extracts on demand.

This crate depends on neither korp nor facett, so both can consume it — which breaks the facett ⇄ korp dependency cycle. (Mirrors how znippy-zoomies/osm-katana and Skidbladnir were extracted as leaves.) The ravens Hugin & Munin collect — hence "collectors" (née connectors).

Collectors (all Direction::Source)

namekindparsesfeature
dcat-ap-seDCAT-AP-SE crawlRDF/XML + SPARQL-JSON → dataset/distribution registryparsers
pxweb-jsonstat2PxWeb / JSON-stat2statistical cube → code-col-per-dim + valueparsers
eurostat-sdmx, oecd-sdmx, ecb-sdmx, insee-sdmx, ons-sdmx, istat-sdmx, worldbank-sdmxSDMX-JSON 2.1 / 1.0generalised series/observation dim split → per-(dims × time × measure) value rows — one collector, any SDMX provider (Eurostat/OECD/ECB/IMF/INSEE France/ONS UK/ISTAT Italy/World Bank/EEA), zero new parsing — presets differ only in branding + endpointparsers
entsoe, entsoe-generation, entsoe_day_ahead, nordpool, energy-charts-powerENERGY marketENTSO-E market-document XML → time-series points (SE1–SE4 ⇄ EIC): entsoe load/generic quantity, entsoe-generation per-psrType volume (A75), entsoe_day_ahead day-ahead SPOT PRICES (A44, price.amount per zone × hour → entsoe_day_ahead_prices; the open free-token price route, token via ENTSOE_TOKEN); Nord Pool day-ahead zones (licensed twin), Fraunhofer generation-mixparsers
opensky-flights, opensky-europe-bbox, opensky-tracksOPEN FLIGHT recorder (ADS-B)OpenSky states/all positional-array → flight position table → osm3d-flight scene (live auth: OAuth2 client-credentials, 2025 Keycloak, via OPENSKY_CLIENT_ID/OPENSKY_CLIENT_SECRET). opensky-tracks = the recorded flight PATH: /tracks path[] → ordered (time, lat, lon, baro_altitude, heading, on_ground) waypoint trajectory → opensky_tracks table → osm3d-flight replay (basic auth via OPENSKY_USER/OPENSKY_PASS)parsers
smhi-hydroobsWATERSMHI hydrological observations (river discharge / water level) — station value series → Arrowparsers
metno-forecastWEATHERMet.no Locationforecast point forecast → per-time forecast table (temp, wind, humidity, pressure, cloud) → osm3d sceneparsers
gbif-occurrencesbiodiversityGBIF occurrence search (keyless, CC0/CC-BY) → occurrence point table (taxon + WGS84 fix + country) → osm3d-poi sceneparsers
worldbank-indicators, restcountriesreference / statsWorld Bank Open Data ([meta,data] envelope) → per-(country, year, indicator); RESTCountries → ISO-3166 country join spineparsers
mobility-database, mobility-database-rttransit META-catalogueMobility Database (CC0, the TransitFeeds successor) → typed feed-descriptor rows that auto-drive the GTFS collectors; bearer-token via MOBILITY_DB_TOKEN. mobility-database enumerates STATIC GTFS feeds (/v1/gtfs_feeds); mobility-database-rt enumerates GTFS-Realtime feeds (/v1/gtfs_rt_feeds, id, entity_type vp/tu/sa, provider, country, .pb URL) that drive the gtfs-rt-vehicles protobuf reader for live vehicle positionsparsers
erddap-emodnet-physics, erddap-noaa-coastwatchERDDAP scientific dataERDDAP .json table (columnNames + columnTypes + rows matrix) → typed Arrow, uniform over any dataset id on any ERDDAP server (EMODnet ocean + global ocean/atmos); .csv variant via the csv collectorparsers
eea-wise-water, destatis-genesis, cbs-odataEU open dataEEA WISE water quality (Waterbase via the Discodata SQL API, /results) → per-(site, determinand, year) rows; Destatis GENESIS-Online table catalogue (/List, token via DESTATIS_GENESIS_TOKEN); CBS Open Data (Statistics Netherlands) OData TypedDataSet (/value) → typed observationsparsers
polisen-events, kolada-kpi, smhi-metobs, smhi-weather, ais-positions, marinetraffic-ais, gbfs-citybikes, osm-overpass, bolagsverket-organisationergeneric REST/JSONconfig-driven JSON field-map → Arrowparsers
usgs-earthquakesUSGS GeoJSON feedFeatureCollection Point features → quake rows (id, place, magnitude, depth, lon/lat, epoch-ms time) → osm3d sceneparsers
rss-atomRSS/Atom feedRSS 2.0 / Atom 1.0 syndication (dialect-agnostic quick-xml) → per-item rows (feed_title, id, title, link, summary, author, published, categories JSON)parsers
openaq-air-qualityOpenAQ RESTair-quality results[] JSON → measurement rows (station, parameter, value, unit, lon/lat, country, city, UTC time; nested coordinates+date.utc flatten) → osm3d sceneparsers
energy-charts-powerEnergy-Charts RESTgeneration-mix JSON — parallel unix_seconds[] axis ZIPPED against each production_types[].data[] by index → per-(time, type) power rows (Europe-wide via ?country=; CC-BY)parsers
wfsOGC WFSGetFeature GML FeatureCollection → feature rows (WKT geometry + properties JSON) → osm3d sceneparsers
gtfs-rt-vehiclesGTFS-Realtime feedVehiclePositions protobuf FeedMessage → live transit vehicle position table (entity/vehicle/route/trip id, lat/lon, bearing, speed, timestamp, stop) via a hand-rolled protobuf reader (no prost dep) → osm3d-flight sceneparsers
trafikverket-trainposTrafikverketPOST XML <REQUEST> → JSON position tableparsers
osm-geoparquet, osm-admin-boundariesOSM bulk filePBF → WKB ways / admin MultiPolygons (via osm-katana)osm
csvflat fileRFC 4180 (+ ;/TAB, BOM, Latin-1, decimal-comma) → typed Arrow via a two-pass schema sniffcsv
mainframe-copybookflat fileIBM EBCDIC fixed-width decoded against a COBOL copybook (PIC X/9/S9V, COMP-3 packed, zoned, COMP binary) → Arrow (COMP-3 → Decimal128)mainframe
fixed-widthflat fileASCII/Latin-1 fixed-width decoded against a COBOL copybook layout (DISPLAY usage)mainframe
copernicus-cds, dwd-iconNetCDF/GRIB weather modelhand-rolled pure-Rust GRIB2 + NetCDF-classic decode → a variable/time/bbox metadata catalog (discipline/param, reference time, Ni/Nj, lat/lon bbox, num_points) AND the data-ARRAY unpack: GRIB2 Section 5/6/7 simple (5.0) + complex (5.2) + complex-with-spatial-differencing (5.3) packing with Section-6 bitmaps, NetCDF-classic variable arrays (fixed + record dims). params.mode="values" emits per-point grid values. Copernicus CDS (retrieve/v1, key from CDSAPI_KEY) + DWD ICON. Only NetCDF-4/HDF5 stays deferredgribnc
copernicus-era5, copernicus-camsCopernicus reanalysis (value-first)ERA5 climate reanalysis (CDS retrieve/v1, key from CDSAPI_KEY) + CAMS atmosphere/air-quality (ADS retrieve/v1, key from ADSAPI_KEY) → per-point grid VALUE rows by default (reuses the gribnc GRIB2 Section 5/6/7 + NetCDF value unpack, mode="values")gribnc

Everything parses in pure Rust → Arrow RecordBatch. The memory BatchSink buffers batches for assertion; the skade-sink feature adds the Iceberg backend

The RegionSource seam (feature osm)

pub trait RegionSource: Send {
    /// Ensure the OSM source data for a named region is available locally,
    /// FETCHING it if missing. Returns the local .osm.pbf path.
    fn ensure(&self, region: &str) -> Result<std::path::PathBuf, String>;
    fn name(&self) -> &str;
}

This is what lets a facett region picker be "fed by a connector fetching data" without facett depending on korp.

Features

Test

cargo test                                 # parsers (offline inject/assert)
cargo test --features osm                  # + OSM ways + RegionSource fetch/cache/parse
cargo test --features znippy               # + znippy converter byte-identical round-trip
cargo test --all-features                  # + skade Iceberg round-trip + testmatrix

The RegionSource test spins an in-process HTTP server serving a bundled 454-byte .osm.pbf fixture, proves fetch → cache-hit (a request counter) → osm-katana parses ≥1 real way. Deterministic, no external network.

License

MIT OR Apache-2.0.

Files

.nornir/
benches/
crates/
src/
tests/
.gitignore
CLAUDE.md
Cargo.lock
Cargo.toml
README.md
bench_history.json
brokkr.toml
build.rs
rustfmt.toml

Newest commits

defe52bdcefbMerge fix/small-4-2026-10-25 into main: no file addresses our repositories on the old forge (A28 N7-left)2026-10-25
e924b54f2b09GREEN: the user agents and the supplied-file cards name korp-collectors' real home; the two per-file links become the repository's link, since git.gunnar.rs has no per-file page (A28 N7-left; guard 4/0, all-features suite 513 passed, 2 failed as on main)2026-10-25
ade7914d658aTEST: no file addresses our repositories on the old forge (A28 N7-left; RED: 8 lines, the user agents and the supplied-file cards' links)2026-10-25
d5d249914ab9Merge fix/small-3-2026-10-25 into main: osm_chain_kvm follows tunnr's GuestDisplay (cargo check --all-targets --all-features green)2026-10-25
0f5410e910b1GREEN: osm_chain_kvm follows tunnr's BootSpec: display = GuestDisplay::None replaces the removed headless = true (tunnr 03ff14a; RED: cargo check --all-targets --all-features, E0609 no field headless)2026-10-25