# CNAIR road conditions (Romania) > The live state of Romania's national roads, express roads and motorways, as the CNAIR road dispatch centre publishes it: incidents, roadworks, weather closures and other traffic restrictions, the condition of the carriageway county by county, winter maintenance, and the visibility, sky, wind and temperature reports that reach the dispatchers — together with the geometry of every event from the map behind the same display. The dispatch page shows only the present moment; this dataset turns hourly snapshots of it into a queryable history of the national road network. - Dataset id: `andnet-road-conditions` - Update cadence: hourly - Upstream source: https://dispecerat.andnet.ro/index.php - Tags: roads, traffic, transport, romania, cnair, infrastructure, road-closures, roadworks, weather, winter-maintenance, geospatial, safety - Current version: ingest `01M5HWS58GJ2808Z7ZZ6QPCCZ8`, snapshot 2026-10-22, generated 2026-10-22T13:32:29.663Z ## Licence and attribution - Licence: Romanian official acts and public-interest information — outside copyright (`ro-official-acts-public-domain`) - Licence text: https://legislatie.just.ro/Public/DetaliiDocument/7816 - Required attribution: Date despre starea drumurilor publicate de CNAIR — dispecerat.andnet.ro - Notes: Legea 8/1996 privind dreptul de autor, art. 9, excludes from copyright both official texts of an administrative nature and simple facts and data. CNAIR's road-dispatch reports are both: each entry is the official record of a traffic-management act — a restriction, closure or works order CNAIR itself imposes and publishes — and the numbers underneath it (kilometre positions, validity periods, weather and temperature readings) are bare facts. They are also public-interest information under Legea 544/2001 and re-usable public-sector information under Legea 179/2022, which transposes the EU Open Data Directive. HOWEVER, and a reuser must weigh this: no explicit reuse licence is published for this data, and that was checked rather than assumed: dispecerat.andnet.ro has no /license, /licence, /terms or /legal page, no terms-of-use link anywhere in its navigation or footer, and no rights statement in the payload it serves. The only ownership statement on the whole site is the footer line "© 2026 Copyright: C.N.A.I.R. - S.A.". (Note for anyone repeating the check: www.cnair.ro is NOT the authority's site — it currently resolves to an unrelated parked domain — so it should not be cited as a source of terms.) This dataset therefore rests on the legal position above rather than on any grant from CNAIR, and anyone republishing it should make their own assessment. What is republished here is a DERIVED work: the factual content of the dispatch tables and map — road indicatives, kilometre positions, validity periods, localities, counties and communes, the dispatchers' own Romanian cause / remediation / works / detour text, carriageway condition, weather and temperature readings, and the coordinates of each event — plus the timestamp of each snapshot. Nothing else is taken: no page markup, no styling, no images, no map tiles, no CNAIR logo, and no other content from the site. robots.txt returns 404, so no crawler path is disallowed; the pages do send an "X-Robots-Tag: none" header, which asks search engines not to index or follow them and is a directive about indexing, not about reuse — a reuser should be aware of it and form their own judgement. This connector polls once an hour and identifies itself through DATADIRECTORY_USER_AGENT. FINALLY, AND MOST IMPORTANTLY: this is live, safety-relevant traffic information that changes by the minute. A row in this dataset is what CNAIR displayed at one past instant, and nothing more. It must NOT be presented to anyone as the current state of the road network, embedded in a live traffic service, or relied on for a journey decision. Anyone who needs to know whether a road is open right now must consult dispecerat.andnet.ro itself, or 112. Every published row carries snapshot_at for exactly this reason: state it wherever the data is shown. Reproduce the attribution line verbatim anywhere you republish this data. ## Manifest - Start here for how this directory works: https://data.bunnytech.app/llms.txt - This dataset: https://data-directory.fsn1.your-objectstorage.com/public/andnet-road-conditions/manifest.json - All datasets: https://data-directory.fsn1.your-objectstorage.com/public/index.json - Human page: https://data.bunnytech.app/datasets/andnet-road-conditions The manifest is the contract: file URLs, row counts, column types, min/max statistics and example queries all live there. Re-read it to discover a new version. ## Fastest path — Parquet exports Immutable, physically sorted snapshot exports. No catalog fetch, best row-group pruning, safe to cache forever. Register one view per table: ```sql INSTALL httpfs; LOAD httpfs; CREATE OR REPLACE VIEW restrictions AS SELECT * FROM read_parquet('https://data-directory.fsn1.your-objectstorage.com/public/andnet-road-conditions/v/01M5HWS58GJ2808Z7ZZ6QPCCZ8/restrictions.parquet'); CREATE OR REPLACE VIEW roadworks AS SELECT * FROM read_parquet('https://data-directory.fsn1.your-objectstorage.com/public/andnet-road-conditions/v/01M5HWS58GJ2808Z7ZZ6QPCCZ8/roadworks.parquet'); CREATE OR REPLACE VIEW carriageway_condition AS SELECT * FROM read_parquet('https://data-directory.fsn1.your-objectstorage.com/public/andnet-road-conditions/v/01M5HWS58GJ2808Z7ZZ6QPCCZ8/carriageway_condition.parquet'); CREATE OR REPLACE VIEW weather_observations AS SELECT * FROM read_parquet('https://data-directory.fsn1.your-objectstorage.com/public/andnet-road-conditions/v/01M5HWS58GJ2808Z7ZZ6QPCCZ8/weather_observations.parquet'); CREATE OR REPLACE VIEW temperatures AS SELECT * FROM read_parquet('https://data-directory.fsn1.your-objectstorage.com/public/andnet-road-conditions/v/01M5HWS58GJ2808Z7ZZ6QPCCZ8/temperatures.parquet'); CREATE OR REPLACE VIEW winter_closed_roads AS SELECT * FROM read_parquet('https://data-directory.fsn1.your-objectstorage.com/public/andnet-road-conditions/v/01M5HWS58GJ2808Z7ZZ6QPCCZ8/winter_closed_roads.parquet'); CREATE OR REPLACE VIEW map_features AS SELECT * FROM read_parquet('https://data-directory.fsn1.your-objectstorage.com/public/andnet-road-conditions/v/01M5HWS58GJ2808Z7ZZ6QPCCZ8/map_features.parquet'); CREATE OR REPLACE VIEW map_geometries AS SELECT * FROM read_parquet('https://data-directory.fsn1.your-objectstorage.com/public/andnet-road-conditions/v/01M5HWS58GJ2808Z7ZZ6QPCCZ8/map_geometries.parquet'); CREATE OR REPLACE VIEW other_sections AS SELECT * FROM read_parquet('https://data-directory.fsn1.your-objectstorage.com/public/andnet-road-conditions/v/01M5HWS58GJ2808Z7ZZ6QPCCZ8/other_sections.parquet'); ``` Example queries: -- What was restricted on DN1 during a given week: The date range comes first because snapshot_date is the leading sort column, and road_code is the second — both halves of this predicate prune. ```sql ATTACH IF NOT EXISTS 'ducklake:https://data-directory.fsn1.your-objectstorage.com/catalog/catalog.ducklake' AS "datadirectory" (READ_ONLY); SELECT snapshot_date, snapshot_hour, restriction_kind, km_from_text, km_to_text, between_localities, cause FROM datadirectory.andnet_road_conditions.restrictions WHERE snapshot_date BETWEEN DATE '2026-09-01' AND DATE '2026-09-07' AND road_code = 'DN1' ORDER BY snapshot_date, snapshot_hour, km_from_km ``` -- How long each closure lasted, in hours actually observed: A closure has no id, so it is identified by the key CNAIR itself draws between the table and the map. Counting snapshots is what turns "now" into a duration. ```sql ATTACH IF NOT EXISTS 'ducklake:https://data-directory.fsn1.your-objectstorage.com/catalog/catalog.ducklake' AS "datadirectory" (READ_ONLY); SELECT road_code, km_from_text, km_to_text, min(snapshot_at) AS first_seen, max(snapshot_at) AS last_seen, count(*) AS hourly_snapshots, any_value(cause) AS cause FROM datadirectory.andnet_road_conditions.restrictions WHERE restriction_kind IN ('interrupted', 'interrupted_weather') AND snapshot_date >= DATE '2026-09-01' GROUP BY road_code, km_from_text, km_to_text ORDER BY hourly_snapshots DESC LIMIT 20 ``` ## Live lake — DuckLake catalog with time travel Attach the published catalog read-only over plain https. This works in native DuckDB AND in duckdb-wasm in the browser — the reader catalog resolves its data files through public https URLs, so no credentials and no backend are involved. ```sql INSTALL ducklake; LOAD ducklake; ATTACH 'ducklake:https://data-directory.fsn1.your-objectstorage.com/catalog/andnet-road-conditions/catalog.ducklake' AS andnet_road_conditions (READ_ONLY); SELECT * FROM andnet_road_conditions.main.restrictions LIMIT 5; USE andnet_road_conditions; ``` - Latest catalog: https://data-directory.fsn1.your-objectstorage.com/catalog/andnet-road-conditions/catalog.ducklake - This exact version: https://data-directory.fsn1.your-objectstorage.com/catalog/andnet-road-conditions/v/01M5HWS58GJ2808Z7ZZ6QPCCZ8/catalog.ducklake (sha256 e9fe9ddd5f113a62bc4d657d523c12b19383da19f2db7babfe749cd7e44f3029) - Written by DuckDB v1.5.5 - Time travel: this ingest is snapshot 132 — e.g. `SELECT * FROM andnet_road_conditions.restrictions AT (VERSION => 132)` ## Wholesale — S3 access For bulk downloads, read the bucket prefix directly over S3 with httpfs: ```sql INSTALL httpfs; LOAD httpfs; CREATE SECRET hetzner ( TYPE s3, ENDPOINT 'fsn1.your-objectstorage.com', URL_STYLE 'path', KEY_ID '', SECRET '' ); SELECT * FROM read_parquet('s3://data-directory/public/andnet-road-conditions/v/01M5HWS58GJ2808Z7ZZ6QPCCZ8/**/*.parquet'); ``` ## Schema ### restrictions One row per traffic restriction on a Romanian national road, express road or motorway, per hourly snapshot: incidents, short-duration works, weather closures and any other stretch where traffic is stopped, hampered or restricted. The dispatch page shows only what is in force right now; this table is what turns repeated snapshots of it into a queryable history of the network. - 8,452 rows · 68 KB · write mode `snapshot_history` - Physically sorted by: `snapshot_date`, `snapshot_hour`, `road_code`, `km_from_km`, `section_index`, `row_seq` - Primary key: `snapshot_date`, `snapshot_hour`, `section_index`, `row_seq` - Text search columns: `cause`, `remedy`, `between_localities` - Files: 1 - https://data-directory.fsn1.your-objectstorage.com/public/andnet-road-conditions/v/01M5HWS58GJ2808Z7ZZ6QPCCZ8/restrictions.parquet (8,452 rows) Columns: - `snapshot_date` DATE NOT NULL — The calendar day (UTC) this snapshot was taken. Leading sort column and, with snapshot_hour, the reload unit: re-ingesting the same (date, hour) replaces exactly those rows and nothing else. (range: "2026-09-02" … "2026-10-22") (~11 distinct) - `snapshot_hour` INTEGER NOT NULL [hour of day (UTC)] — The hour of day (0-23, UTC) this snapshot belongs to — this connector's own poll slot, not a value CNAIR publishes. Keying the reload unit on the hour rather than the date alone is what stops a later run in the same day from erasing an earlier one; see the file comment in src/tables.ts. (range: 3 … 23) (~21 distinct) - `snapshot_at` TIMESTAMPTZ NOT NULL — The exact instant (UTC) this snapshot was fetched — ctx.runAt, unrounded. snapshot_date/snapshot_hour bucket the run; this keeps the precise minute. - `section_index` INTEGER NOT NULL — Zero-based position of the section this row came from, counted down the page as served that hour. Part of the primary key, and the only positional identity that is safe: the section NUMBER printed in the heading (section_code) is renumbered by CNAIR from time to time, and the heading text is reworded. (range: 0 … 5) (~4 distinct) - `row_seq` INTEGER NOT NULL — Zero-based position of this row within its section's table, as served. Together with section_index and the snapshot columns it is the primary key. It is a position, not an identifier: CNAIR does not promise the same closure keeps the same position between two fetches, so do not use it to follow one event through time — use (road_code, km_from_text, km_to_text) or map_key for that. (range: 0 … 55) (~50 distinct) - `restriction_kind` VARCHAR NOT NULL — What kind of restriction this is, derived from the meaning of its section heading rather than from the heading text or its number: "incident" (a crash, fire or spill), "short_works" (a crew on the road for hours or days), "interrupted" (traffic stopped), "hampered" (traffic restricted but moving), and the two weather-caused variants "interrupted_weather" and "hampered_weather". This is the column to filter on; section_code and section_title are published for traceability but are not stable. - `section_code` VARCHAR — The section number exactly as the heading prints it — "1", "1.1", "5", "7.1", "8.4". This is the label CNAIR itself cites, and the map payload's `categorie` field mirrors it, so it is published. It is deliberately NOT what decides which table a row lands in: CNAIR renumbers these sections, and a renumber that silently reclassified every closure would be the worst failure this dataset could have. NULL for the winter-closure list, whose heading carries no number. - `section_title` VARCHAR — The section heading verbatim, number and trailing colon included, exactly as CNAIR wrote it that hour. Keep an eye on it: this is the string whose wording moves, and comparing it across snapshots is how you see CNAIR reorganise the page. - `row_no` VARCHAR — The source's own "NR. CRT." value for this row, verbatim — CNAIR numbers rows within a section as "5.1", "5.2", … It is a display number that renumbers whenever a row above is removed, so it identifies nothing across snapshots. - `unmapped_columns` VARCHAR — Column labels this row carried that the connector does not map to a column of its own, comma-separated and lowercased — normally NULL. A non-null value is the early warning that CNAIR has added a field: the data is not lost (it is still on the page) but it is not in this table yet, and the connector logs a warning naming the label on every run. - `road_code` VARCHAR — The road indicative, verbatim — "A1", "A7", "DN1", "DN67C", "A3Cj", and occasionally a bypass code such as "VO_CARANSEBES". CNAIR writes motorways as A, express roads as DEx and national roads as DN with an optional letter suffix. The HTML tables write it without an internal space; the winter-closure list writes "DN 7C" and is normalised here to match. (range: "A0" … "VO6G") - `km_from_text` VARCHAR — Where the restriction starts, in CNAIR's own kilometre+metres notation — "34+800" means 800 m past kilometre 34, the notation used on road signs and in CNAIR notices. Canonicalised: metres are zero-padded to three digits and the "km " prefix the winter-closure list writes is dropped, so one position has one spelling across every table here and map_key joins. km_from_km is the same position as a number. - `km_from_km` DECIMAL(9,3) [km] — km_from_text as a number of kilometres, metres folded into the fraction ("34+800" -> 34.800). This is the column to filter and order on — "everything on DN1 between km 10 and km 20" cannot be asked of the text form. NULL when the source cell was not in + shape. (range: 0 … 583) - `km_to_text` VARCHAR — Where the restriction ends, same notation as km_from_text. Equal to km_from_text for a point restriction (an incident at a single kilometre). - `km_to_km` DECIMAL(9,3) [km] — km_to_text as a number of kilometres. See km_from_km. - `valid_from` TIMESTAMPTZ — When the restriction started, as an instant. **Timezone assumption:** CNAIR publishes bare wall-clock times ("2026-08-17 01:00:00") and declares no timezone anywhere on the page or in its headers. They are read as Europe/Bucharest local time (EET/EEST, UTC+2 in winter and UTC+3 in summer, switching on the EU rule) and converted to UTC. valid_from_text carries the source string untouched, so a reader who disagrees can redo the conversion. - `valid_from_text` VARCHAR — valid_from exactly as CNAIR printed it, before any timezone interpretation. - `valid_to` TIMESTAMPTZ — When the restriction is scheduled to end, as an instant, under the same Europe/Bucharest assumption as valid_from. It is a PLAN, not an observation: CNAIR routinely publishes "31-12- 23:59" as a placeholder for "until further notice", and a restriction can be lifted early or run over. Whether it was still in force at a given hour is answered by the snapshot columns, not by this one. - `valid_to_text` VARCHAR — valid_to exactly as CNAIR printed it. - `between_localities` VARCHAR — The stretch in CNAIR's own words, as "FROM (county) - TO (county)" — e.g. "BACAU (BC) - BOGDAN VODA (BC)". The second half is frequently blank ("JITIA (VN) - ") for a restriction at one place rather than between two, and the whole cell is sometimes just "-". Written without diacritics by the source; search between_localities_lc. - `between_localities_lc` VARCHAR — between_localities, lowercased and accent-folded, for case- and accent-insensitive search. Query this, not lower(between_localities) — a function on a column cannot use parquet statistics. - `cause` TEXT — Why traffic is restricted, in the dispatcher's own Romanian prose — "Alunecare de teren", "Accident rutier. O camioneta a intrat in spatele unui TIR.", or several sentences about a bridge. Free text, written without diacritics, occasionally with a typo, and not drawn from any fixed vocabulary. `
` separators in the source become newlines here. Unlike the map payload's copy of the same text, this one is NOT truncated. - `cause_lc` TEXT — cause, lowercased and accent-folded, for search. Query this rather than lower(cause). - `remedy` TEXT — What is being done about it and how traffic is being managed — "Viteza - 30 km/h", "Circulatia se desfasoara pe o singura banda, dirijata alternativ prin semafoare". In sections where CNAIR heads this column "Masuri de remediere si variante de ocolire" it also carries the diversion; the cell label is what is matched, so the wording of the heading does not matter. Free text, newlines preserved from `
`. - `remedy_lc` TEXT — remedy, lowercased and accent-folded, for search. - `map_key` VARCHAR — The key that joins this row to its geometry in map_features: road indicative, start kilometre and end kilometre in CNAIR's own notation, pipe-separated. It is built from the map-zoom target encoded in the row's "VEZI PE HARTĂ" cell (onclick="actualizareharta('DN10',76,795,76,870)"), which is the link CNAIR itself draws between the table and the map. On the 2026-09-02 capture all 123 table rows carried one and the 123 keys matched the 123 map features exactly, with nothing left over on either side. NULL when the row has no map cell. ### roadworks One row per stretch of national road, express road or motorway under rehabilitation, modernisation, maintenance or repair, per hourly snapshot — CNAIR's standing works programme rather than today's incidents. Entries here run for months or years, so the interesting query is usually "when did this works appear and when did it stop being listed". - 7,162 rows · 46 KB · write mode `snapshot_history` - Physically sorted by: `snapshot_date`, `snapshot_hour`, `road_code`, `km_from_km`, `section_index`, `row_seq` - Primary key: `snapshot_date`, `snapshot_hour`, `section_index`, `row_seq` - Text search columns: `works_type`, `detours`, `between_localities` - Files: 1 - https://data-directory.fsn1.your-objectstorage.com/public/andnet-road-conditions/v/01M5HWS58GJ2808Z7ZZ6QPCCZ8/roadworks.parquet (7,162 rows) Columns: - `snapshot_date` DATE NOT NULL — The calendar day (UTC) this snapshot was taken. Leading sort column and, with snapshot_hour, the reload unit: re-ingesting the same (date, hour) replaces exactly those rows and nothing else. (range: "2026-09-02" … "2026-10-22") (~11 distinct) - `snapshot_hour` INTEGER NOT NULL [hour of day (UTC)] — The hour of day (0-23, UTC) this snapshot belongs to — this connector's own poll slot, not a value CNAIR publishes. Keying the reload unit on the hour rather than the date alone is what stops a later run in the same day from erasing an earlier one; see the file comment in src/tables.ts. (range: 3 … 23) (~21 distinct) - `snapshot_at` TIMESTAMPTZ NOT NULL — The exact instant (UTC) this snapshot was fetched — ctx.runAt, unrounded. snapshot_date/snapshot_hour bucket the run; this keeps the precise minute. - `section_index` INTEGER NOT NULL — Zero-based position of the section this row came from, counted down the page as served that hour. Part of the primary key, and the only positional identity that is safe: the section NUMBER printed in the heading (section_code) is renumbered by CNAIR from time to time, and the heading text is reworded. (range: 6 … 6) (~1 distinct) - `row_seq` INTEGER NOT NULL — Zero-based position of this row within its section's table, as served. Together with section_index and the snapshot columns it is the primary key. It is a position, not an identifier: CNAIR does not promise the same closure keeps the same position between two fetches, so do not use it to follow one event through time — use (road_code, km_from_text, km_to_text) or map_key for that. (range: 0 … 63) (~59 distinct) - `section_code` VARCHAR — The section number exactly as the heading prints it — "1", "1.1", "5", "7.1", "8.4". This is the label CNAIR itself cites, and the map payload's `categorie` field mirrors it, so it is published. It is deliberately NOT what decides which table a row lands in: CNAIR renumbers these sections, and a renumber that silently reclassified every closure would be the worst failure this dataset could have. NULL for the winter-closure list, whose heading carries no number. - `section_title` VARCHAR — The section heading verbatim, number and trailing colon included, exactly as CNAIR wrote it that hour. Keep an eye on it: this is the string whose wording moves, and comparing it across snapshots is how you see CNAIR reorganise the page. - `row_no` VARCHAR — The source's own "NR. CRT." value for this row, verbatim — CNAIR numbers rows within a section as "5.1", "5.2", … It is a display number that renumbers whenever a row above is removed, so it identifies nothing across snapshots. - `unmapped_columns` VARCHAR — Column labels this row carried that the connector does not map to a column of its own, comma-separated and lowercased — normally NULL. A non-null value is the early warning that CNAIR has added a field: the data is not lost (it is still on the page) but it is not in this table yet, and the connector logs a warning naming the label on every run. - `road_code` VARCHAR — The road indicative, verbatim — "A1", "A7", "DN1", "DN67C", "A3Cj", and occasionally a bypass code such as "VO_CARANSEBES". CNAIR writes motorways as A, express roads as DEx and national roads as DN with an optional letter suffix. The HTML tables write it without an internal space; the winter-closure list writes "DN 7C" and is normalised here to match. (range: "22J" … "VO_CARANSEBES") - `km_from_text` VARCHAR — Where the restriction starts, in CNAIR's own kilometre+metres notation — "34+800" means 800 m past kilometre 34, the notation used on road signs and in CNAIR notices. Canonicalised: metres are zero-padded to three digits and the "km " prefix the winter-closure list writes is dropped, so one position has one spelling across every table here and map_key joins. km_from_km is the same position as a number. - `km_from_km` DECIMAL(9,3) [km] — km_from_text as a number of kilometres, metres folded into the fraction ("34+800" -> 34.800). This is the column to filter and order on — "everything on DN1 between km 10 and km 20" cannot be asked of the text form. NULL when the source cell was not in + shape. (range: 0 … 556) - `km_to_text` VARCHAR — Where the restriction ends, same notation as km_from_text. Equal to km_from_text for a point restriction (an incident at a single kilometre). - `km_to_km` DECIMAL(9,3) [km] — km_to_text as a number of kilometres. See km_from_km. - `valid_from` TIMESTAMPTZ — When the restriction started, as an instant. **Timezone assumption:** CNAIR publishes bare wall-clock times ("2026-08-17 01:00:00") and declares no timezone anywhere on the page or in its headers. They are read as Europe/Bucharest local time (EET/EEST, UTC+2 in winter and UTC+3 in summer, switching on the EU rule) and converted to UTC. valid_from_text carries the source string untouched, so a reader who disagrees can redo the conversion. - `valid_from_text` VARCHAR — valid_from exactly as CNAIR printed it, before any timezone interpretation. - `valid_to` TIMESTAMPTZ — When the restriction is scheduled to end, as an instant, under the same Europe/Bucharest assumption as valid_from. It is a PLAN, not an observation: CNAIR routinely publishes "31-12- 23:59" as a placeholder for "until further notice", and a restriction can be lifted early or run over. Whether it was still in force at a given hour is answered by the snapshot columns, not by this one. - `valid_to_text` VARCHAR — valid_to exactly as CNAIR printed it. - `between_localities` VARCHAR — The stretch in CNAIR's own words, as "FROM (county) - TO (county)". Frequently half-empty or just "-" for works that are not between two named places. - `between_localities_lc` VARCHAR — between_localities, lowercased and accent-folded, for search. - `works_type` TEXT — What is being built or repaired, in CNAIR's own Romanian prose — "Lucrari de reparatii pod peste bratul Borcea…", "Lucrari de intretinere periodica pe timp de vara - Straturi bituminoase foarte subtiri executate la rece". This is the cell CNAIR heads "Tip lucrare" (and labels LUCRĂRI). Free text; newlines come from the source's `
`. - `works_type_lc` TEXT — works_type, lowercased and accent-folded, for search. - `detours` TEXT — The alternative routes CNAIR suggests, or the speed and lane restrictions in force through the works. Often blank — an empty cell means CNAIR published no diversion, not that none exists. - `detours_lc` TEXT — detours, lowercased and accent-folded, for search. - `map_key` VARCHAR — The key that joins this row to its geometry in map_features: road indicative, start kilometre and end kilometre in CNAIR's own notation, pipe-separated. It is built from the map-zoom target encoded in the row's "VEZI PE HARTĂ" cell (onclick="actualizareharta('DN10',76,795,76,870)"), which is the link CNAIR itself draws between the table and the map. On the 2026-09-02 capture all 123 table rows carried one and the 123 keys matched the 123 map features exactly, with nothing left over on either side. NULL when the row has no map cell. ### carriageway_condition One row per reported condition of the carriageway itself — dry, wet, snow-covered, treated — per hourly snapshot. Reported per county in CNAIR's general table, and (in winter) per road in its detailed one. This is the surface underneath the traffic, not the traffic: a county can be reported "Uscat" while a road in it is closed for works. - 4,511 rows · 32 KB · write mode `snapshot_history` - Physically sorted by: `snapshot_date`, `snapshot_hour`, `county_code`, `section_index`, `row_seq` - Primary key: `snapshot_date`, `snapshot_hour`, `section_index`, `row_seq` - Text search columns: `condition_text` - Files: 1 - https://data-directory.fsn1.your-objectstorage.com/public/andnet-road-conditions/v/01M5HWS58GJ2808Z7ZZ6QPCCZ8/carriageway_condition.parquet (4,511 rows) Columns: - `snapshot_date` DATE NOT NULL — The calendar day (UTC) this snapshot was taken. Leading sort column and, with snapshot_hour, the reload unit: re-ingesting the same (date, hour) replaces exactly those rows and nothing else. (range: "2026-09-02" … "2026-10-22") (~11 distinct) - `snapshot_hour` INTEGER NOT NULL [hour of day (UTC)] — The hour of day (0-23, UTC) this snapshot belongs to — this connector's own poll slot, not a value CNAIR publishes. Keying the reload unit on the hour rather than the date alone is what stops a later run in the same day from erasing an earlier one; see the file comment in src/tables.ts. (range: 3 … 23) (~21 distinct) - `snapshot_at` TIMESTAMPTZ NOT NULL — The exact instant (UTC) this snapshot was fetched — ctx.runAt, unrounded. snapshot_date/snapshot_hour bucket the run; this keeps the precise minute. - `section_index` INTEGER NOT NULL — Zero-based position of the section this row came from, counted down the page as served that hour. Part of the primary key, and the only positional identity that is safe: the section NUMBER printed in the heading (section_code) is renumbered by CNAIR from time to time, and the heading text is reworded. (range: 7 … 8) (~2 distinct) - `row_seq` INTEGER NOT NULL — Zero-based position of this row within its section's table, as served. Together with section_index and the snapshot columns it is the primary key. It is a position, not an identifier: CNAIR does not promise the same closure keeps the same position between two fetches, so do not use it to follow one event through time — use (road_code, km_from_text, km_to_text) or map_key for that. (range: 0 … 42) (~43 distinct) - `scope` VARCHAR NOT NULL — Which of CNAIR's two carriageway tables this row came from: "general" (the per-county overview, section 7.1) or "detailed" (the per-road table, section 7.2, which is empty outside winter). Derived from the section heading's own wording, so it survives a renumbering. - `section_code` VARCHAR — The section number exactly as the heading prints it — "1", "1.1", "5", "7.1", "8.4". This is the label CNAIR itself cites, and the map payload's `categorie` field mirrors it, so it is published. It is deliberately NOT what decides which table a row lands in: CNAIR renumbers these sections, and a renumber that silently reclassified every closure would be the worst failure this dataset could have. NULL for the winter-closure list, whose heading carries no number. - `section_title` VARCHAR — The section heading verbatim, number and trailing colon included, exactly as CNAIR wrote it that hour. Keep an eye on it: this is the string whose wording moves, and comparing it across snapshots is how you see CNAIR reorganise the page. - `row_no` VARCHAR — The source's own "NR. CRT." value for this row, verbatim — CNAIR numbers rows within a section as "5.1", "5.2", … It is a display number that renumbers whenever a row above is removed, so it identifies nothing across snapshots. - `unmapped_columns` VARCHAR — Column labels this row carried that the connector does not map to a column of its own, comma-separated and lowercased — normally NULL. A non-null value is the early warning that CNAIR has added a field: the data is not lost (it is still on the page) but it is not in this table yet, and the connector logs a warning naming the label on every run. - `reported_at` TIMESTAMPTZ — When the reporting unit last updated this observation, as an instant. CNAIR publishes a bare wall-clock time and declares no timezone; it is read as Europe/Bucharest (EET/EEST) and converted to UTC — see reported_at_text for the untouched string. Note that this is usually EARLIER than snapshot_at, often by hours: the page shows the last report, not a live reading. - `reported_at_text` VARCHAR — reported_at exactly as CNAIR printed it. - `county_text` VARCHAR — The county cell verbatim, name and plate code together as CNAIR writes it. - `county_name` VARCHAR — The county (județ) name, split out of county_text. Written in capitals and without diacritics by the source ("BISTRITA-NASAUD", not "Bistrița-Năsăud"), and left that way rather than prettified, so it matches the page. - `county_code` VARCHAR — The county plate code — AB, AR, BZ, … and B for Bucharest. The stable key for joining to any other Romanian county dataset; the name is not. (range: "AB" … "VS") - `road_code` VARCHAR — The road indicative, when the row names one. NULL on every row from the per-county general table (scope = "general"), which reports a whole county at once. CNAIR's detailed winter table is the one that names roads, and it was empty throughout the 2026-09-02 capture — this column is populated by matching the cell CNAIR labels "INDICATIV DRUM", the same label its other tables use, rather than by a column position nobody has observed. - `km_from_text` VARCHAR — Start of the stretch this condition applies to, in kilometre+metres notation, when the source gives one. NULL for a per-county row. See the note on road_code. - `km_from_km` DECIMAL(9,3) [km] — km_from_text as a number of kilometres. - `km_to_text` VARCHAR — End of the stretch, same notation as km_from_text. - `km_to_km` DECIMAL(9,3) [km] — km_to_text as a number of kilometres. - `condition_text` VARCHAR — The condition as reported, in the reporting unit's own words — "Uscat" (dry), "Umed" (damp), "Umed cu portiuni de polei", "Acoperit cu zapada framantata". Every value in the 2026-09-02 capture was "Uscat", which is what a warm September looks like; the vocabulary widens in winter and is not drawn from a fixed list, so filter with condition_text_lc rather than equality. - `condition_text_lc` VARCHAR — condition_text, lowercased and accent-folded, for search. - `map_key` VARCHAR — The key that joins this row to its geometry in map_features: road indicative, start kilometre and end kilometre in CNAIR's own notation, pipe-separated. It is built from the map-zoom target encoded in the row's "VEZI PE HARTĂ" cell (onclick="actualizareharta('DN10',76,795,76,870)"), which is the link CNAIR itself draws between the table and the map. On the 2026-09-02 capture all 123 table rows carried one and the 123 keys matched the 123 map features exactly, with nothing left over on either side. NULL when the row has no map cell. ### weather_observations One row per road-weather observation reported to the CNAIR dispatch centre, per hourly snapshot: visibility, state of the sky, and wind speed and direction, per weather station. Most rows are automatic readings relayed from ANM (the national meteorological administration) stations; report_type says which. - 21.6K rows · 172 KB · write mode `snapshot_history` - Physically sorted by: `snapshot_date`, `snapshot_hour`, `observation_kind`, `county_code`, `locality` - Primary key: `snapshot_date`, `snapshot_hour`, `section_index`, `row_seq` - Text search columns: `locality`, `visibility_text`, `weather_conditions_text`, `sky_state_text` - Files: 1 - https://data-directory.fsn1.your-objectstorage.com/public/andnet-road-conditions/v/01M5HWS58GJ2808Z7ZZ6QPCCZ8/weather_observations.parquet (21.6K rows) Columns: - `snapshot_date` DATE NOT NULL — The calendar day (UTC) this snapshot was taken. Leading sort column and, with snapshot_hour, the reload unit: re-ingesting the same (date, hour) replaces exactly those rows and nothing else. (range: "2026-09-02" … "2026-10-22") (~11 distinct) - `snapshot_hour` INTEGER NOT NULL [hour of day (UTC)] — The hour of day (0-23, UTC) this snapshot belongs to — this connector's own poll slot, not a value CNAIR publishes. Keying the reload unit on the hour rather than the date alone is what stops a later run in the same day from erasing an earlier one; see the file comment in src/tables.ts. (range: 3 … 23) (~21 distinct) - `snapshot_at` TIMESTAMPTZ NOT NULL — The exact instant (UTC) this snapshot was fetched — ctx.runAt, unrounded. snapshot_date/snapshot_hour bucket the run; this keeps the precise minute. - `section_index` INTEGER NOT NULL — Zero-based position of the section this row came from, counted down the page as served that hour. Part of the primary key, and the only positional identity that is safe: the section NUMBER printed in the heading (section_code) is renumbered by CNAIR from time to time, and the heading text is reworded. (~3 distinct) - `row_seq` INTEGER NOT NULL — Zero-based position of this row within its section's table, as served. Together with section_index and the snapshot columns it is the primary key. It is a position, not an identifier: CNAIR does not promise the same closure keeps the same position between two fetches, so do not use it to follow one event through time — use (road_code, km_from_text, km_to_text) or map_key for that. (~102 distinct) - `observation_kind` VARCHAR NOT NULL — What this row measures: "visibility", "sky" or "wind". Derived from the meaning of the sub-section heading it came from, not from its number. Which of the measurement columns below are populated follows directly from it. (range: "sky" … "wind") - `section_code` VARCHAR — The section number exactly as the heading prints it — "1", "1.1", "5", "7.1", "8.4". This is the label CNAIR itself cites, and the map payload's `categorie` field mirrors it, so it is published. It is deliberately NOT what decides which table a row lands in: CNAIR renumbers these sections, and a renumber that silently reclassified every closure would be the worst failure this dataset could have. NULL for the winter-closure list, whose heading carries no number. - `section_title` VARCHAR — The section heading verbatim, number and trailing colon included, exactly as CNAIR wrote it that hour. Keep an eye on it: this is the string whose wording moves, and comparing it across snapshots is how you see CNAIR reorganise the page. - `row_no` VARCHAR — The source's own "NR. CRT." value for this row, verbatim — CNAIR numbers rows within a section as "5.1", "5.2", … It is a display number that renumbers whenever a row above is removed, so it identifies nothing across snapshots. - `unmapped_columns` VARCHAR — Column labels this row carried that the connector does not map to a column of its own, comma-separated and lowercased — normally NULL. A non-null value is the early warning that CNAIR has added a field: the data is not lost (it is still on the page) but it is not in this table yet, and the connector logs a warning naming the label on every run. - `report_type` VARCHAR — How the observation reached CNAIR, in its own words — "Automată, Stație meteo ANM" for a reading relayed automatically from a national meteorological station, other wording for a human report from a road district. Worth filtering on: an automatic station reports a number, a person reports a judgement. - `reported_at` TIMESTAMPTZ — When the observation was made, as an instant, read as Europe/Bucharest local time and converted to UTC (CNAIR declares no timezone — see reported_at_text). Usually minutes to hours before snapshot_at. - `reported_at_text` VARCHAR — reported_at exactly as CNAIR printed it. - `county_text` VARCHAR — The county cell verbatim, where the table has one. - `county_name` VARCHAR — The county name, split out of county_text. - `county_code` VARCHAR — The county plate code. Present either from a county column of its own (visibility, wind) or parsed out of the bracketed suffix on the locality (sky, e.g. "ALEXANDRIA (TR)"). (range: "AB" … "VS") - `locality` VARCHAR — The reporting station or place, as CNAIR names it — "ALBA IULIA", "BALEA LAC (SB)", "INTORSURA BUZAULUI". Capitals, no diacritics. It is the practical key for following one station through time, but it is a name CNAIR types, not an identifier: a station renamed upstream becomes a different value here. (range: "ADAMCLISI" … "ZIMNICEA") - `locality_lc` VARCHAR — locality, lowercased and accent-folded, for search. - `visibility_text` VARCHAR — The visibility report, in the reporter's own words. Populated only when observation_kind = "visibility". Automatic stations write the standing sentence "Raportare automata statie meteo, nu s-a efectuat observatia." — which means the station did not make a visibility observation at all, NOT that visibility was good. - `visibility_text_lc` VARCHAR — visibility_text, lowercased and accent-folded. - `weather_conditions_text` VARCHAR — The accompanying weather note CNAIR prints beside a visibility report — "Aer cetos" (hazy air), "Ceata" (fog). Populated only when observation_kind = "visibility". - `weather_conditions_text_lc` VARCHAR — weather_conditions_text, lowercased and accent-folded. - `sky_state_text` VARCHAR — The state of the sky — "Cer senin" (clear), "Cer noros" (cloudy), "Cer acoperit" (overcast). Populated only when observation_kind = "sky". - `sky_state_text_lc` VARCHAR — sky_state_text, lowercased and accent-folded. - `wind_speed_kmh` DECIMAL(6,2) [km/h] — Wind speed at the station. Populated only when observation_kind = "wind". CNAIR heads the column "Viteza(km/h)" and prints two decimals ("1.08", "7.56"), which is the signature of a metres-per-second reading converted upstream rather than an anemometer reporting in km/h. NULL when the cell was not a plain number — a station reporting a word gets NULL and its text stays in wind_speed_text, never a fabricated zero. - `wind_speed_text` VARCHAR — The wind speed cell verbatim, before parsing. - `wind_direction` VARCHAR — Where the wind is blowing from, in Romanian compass words — "est, sud-est", "vest, nord-vest". Two adjacent points separated by a comma is the source's way of writing a direction between them. Populated only when observation_kind = "wind". - `wind_direction_lc` VARCHAR — wind_direction, lowercased and accent-folded. ### temperatures One row per air temperature reading relayed to the CNAIR dispatch centre, per weather station, per hourly snapshot — around 100 stations across Romania. Reported to whole degrees Celsius. This is air temperature at the station, NOT road surface temperature, which is the number that decides whether a road ices over. - 13.4K rows · 89 KB · write mode `snapshot_history` - Physically sorted by: `snapshot_date`, `snapshot_hour`, `station_name`, `row_seq` - Primary key: `snapshot_date`, `snapshot_hour`, `section_index`, `row_seq` - Text search columns: `station_name` - Files: 1 - https://data-directory.fsn1.your-objectstorage.com/public/andnet-road-conditions/v/01M5HWS58GJ2808Z7ZZ6QPCCZ8/temperatures.parquet (13.4K rows) Columns: - `snapshot_date` DATE NOT NULL — The calendar day (UTC) this snapshot was taken. Leading sort column and, with snapshot_hour, the reload unit: re-ingesting the same (date, hour) replaces exactly those rows and nothing else. (range: "2026-09-02" … "2026-10-22") (~11 distinct) - `snapshot_hour` INTEGER NOT NULL [hour of day (UTC)] — The hour of day (0-23, UTC) this snapshot belongs to — this connector's own poll slot, not a value CNAIR publishes. Keying the reload unit on the hour rather than the date alone is what stops a later run in the same day from erasing an earlier one; see the file comment in src/tables.ts. (range: 3 … 23) (~21 distinct) - `snapshot_at` TIMESTAMPTZ NOT NULL — The exact instant (UTC) this snapshot was fetched — ctx.runAt, unrounded. snapshot_date/snapshot_hour bucket the run; this keeps the precise minute. - `section_index` INTEGER NOT NULL — Zero-based position of the section this row came from, counted down the page as served that hour. Part of the primary key, and the only positional identity that is safe: the section NUMBER printed in the heading (section_code) is renumbered by CNAIR from time to time, and the heading text is reworded. (~1 distinct) - `row_seq` INTEGER NOT NULL — Zero-based position of this row within its section's table, as served. Together with section_index and the snapshot columns it is the primary key. It is a position, not an identifier: CNAIR does not promise the same closure keeps the same position between two fetches, so do not use it to follow one event through time — use (road_code, km_from_text, km_to_text) or map_key for that. (range: 0 … 115) (~117 distinct) - `section_code` VARCHAR — The section number exactly as the heading prints it — "1", "1.1", "5", "7.1", "8.4". This is the label CNAIR itself cites, and the map payload's `categorie` field mirrors it, so it is published. It is deliberately NOT what decides which table a row lands in: CNAIR renumbers these sections, and a renumber that silently reclassified every closure would be the worst failure this dataset could have. NULL for the winter-closure list, whose heading carries no number. - `section_title` VARCHAR — The section heading verbatim, number and trailing colon included, exactly as CNAIR wrote it that hour. Keep an eye on it: this is the string whose wording moves, and comparing it across snapshots is how you see CNAIR reorganise the page. - `row_no` VARCHAR — The source's own "NR. CRT." value for this row, verbatim — CNAIR numbers rows within a section as "5.1", "5.2", … It is a display number that renumbers whenever a row above is removed, so it identifies nothing across snapshots. - `unmapped_columns` VARCHAR — Column labels this row carried that the connector does not map to a column of its own, comma-separated and lowercased — normally NULL. A non-null value is the early warning that CNAIR has added a field: the data is not lost (it is still on the page) but it is not in this table yet, and the connector logs a warning naming the label on every run. - `report_type` VARCHAR — How the reading reached CNAIR — "Automată, Stație meteo ANM" for an automatic relay from a national meteorological station, other wording for a human report. - `reported_at` TIMESTAMPTZ — When the reading was taken, as an instant, read as Europe/Bucharest local time and converted to UTC (CNAIR declares no timezone — see reported_at_text). - `reported_at_text` VARCHAR — reported_at exactly as CNAIR printed it. - `station_text` VARCHAR — The station cell verbatim, as CNAIR writes it: a place in capitals, then its county plate code in brackets. - `station_name` VARCHAR — The station name, split out of station_text — "BUCURESTI FILARET", "BALEA LAC". Bucharest has several stations and they are distinguished only by this name, so it is the column to group by. A name CNAIR types, not an identifier. (range: "ADAMCLISI" … "ZIMNICEA") - `station_name_lc` VARCHAR — station_name, lowercased and accent-folded, for search. - `county_code` VARCHAR — The county plate code parsed out of the station cell's bracketed suffix. - `temperature_c` DECIMAL(5,2) [°C] — Air temperature at the station. The source prints it as an integer with a degree suffix ("23°C", "-3°C"); the suffix is stripped and the number kept, with the unit on the column where it belongs. DECIMAL rather than DOUBLE so a reading is exact and a monthly average is reproducible. NULL when the cell was not a number — never a fabricated zero, which on this column would read as freezing. - `temperature_text` VARCHAR — The temperature cell verbatim, degree sign included, before parsing. ### winter_closed_roads One row per national road segment classed at viability level IV and therefore closed to traffic for the whole winter — CNAIR's standing list, captured at each hourly snapshot. These are high mountain roads (Transfăgărășan, Transalpina and similar). A closure here is policy, not an event: the road is on this list all year, and being on it does not by itself say whether the barrier is down today. - 1,080 rows · 7.1 KB · write mode `snapshot_history` - Physically sorted by: `snapshot_date`, `snapshot_hour`, `road_code`, `km_from_km`, `row_seq` - Primary key: `snapshot_date`, `snapshot_hour`, `section_index`, `row_seq` - Text search columns: `between_localities` - Files: 1 - https://data-directory.fsn1.your-objectstorage.com/public/andnet-road-conditions/v/01M5HWS58GJ2808Z7ZZ6QPCCZ8/winter_closed_roads.parquet (1,080 rows) Columns: - `snapshot_date` DATE NOT NULL — The calendar day (UTC) this snapshot was taken. Leading sort column and, with snapshot_hour, the reload unit: re-ingesting the same (date, hour) replaces exactly those rows and nothing else. (range: "2026-09-02" … "2026-10-22") (~11 distinct) - `snapshot_hour` INTEGER NOT NULL [hour of day (UTC)] — The hour of day (0-23, UTC) this snapshot belongs to — this connector's own poll slot, not a value CNAIR publishes. Keying the reload unit on the hour rather than the date alone is what stops a later run in the same day from erasing an earlier one; see the file comment in src/tables.ts. (range: 3 … 23) (~21 distinct) - `snapshot_at` TIMESTAMPTZ NOT NULL — The exact instant (UTC) this snapshot was fetched — ctx.runAt, unrounded. snapshot_date/snapshot_hour bucket the run; this keeps the precise minute. - `section_index` INTEGER NOT NULL — Zero-based position of the section this row came from, counted down the page as served that hour. Part of the primary key, and the only positional identity that is safe: the section NUMBER printed in the heading (section_code) is renumbered by CNAIR from time to time, and the heading text is reworded. (~1 distinct) - `row_seq` INTEGER NOT NULL — Zero-based position of this row within its section's table, as served. Together with section_index and the snapshot columns it is the primary key. It is a position, not an identifier: CNAIR does not promise the same closure keeps the same position between two fetches, so do not use it to follow one event through time — use (road_code, km_from_text, km_to_text) or map_key for that. (range: 0 … 8) (~10 distinct) - `section_title` VARCHAR — The note CNAIR prints above the list, verbatim — it is what states that these are level-IV roads closed in winter, and it is the only place the list explains itself. - `row_no` VARCHAR — The list's own running number for this row. - `road_code` VARCHAR — The road indicative with the internal space removed — "DN 7C" on the page becomes "DN7C" here, which is the form every other table on this page uses and therefore the form that joins. road_code_text keeps the original. (range: "DN17D" … "DN7C") - `road_code_text` VARCHAR — The road indicative verbatim, space included. - `km_text` VARCHAR — The kilometre range verbatim, as the list writes it — this is the one table whose source adds "km" prefixes, and km_text is where they survive; km_from_text and km_to_text carry the same positions in the canonical form every other table uses. - `km_from_text` VARCHAR — Start of the closed segment, in kilometre+metres notation. - `km_from_km` DECIMAL(9,3) [km] — km_from_text as a number of kilometres. (range: 0 … 104) - `km_to_text` VARCHAR — End of the closed segment. - `km_to_km` DECIMAL(9,3) [km] — km_to_text as a number of kilometres. - `between_localities` VARCHAR — The two ends of the closed segment in CNAIR's words, with their counties. Unlike the live tables, this list is written WITH diacritics ("Bâlea Cascadă, jud. Sibiu"). - `between_localities_lc` VARCHAR — between_localities, lowercased and accent-folded — which matters more here than anywhere else in this dataset, because this is the one table whose text carries diacritics. ### map_features One row per feature on the CNAIR dispatch map, per hourly snapshot: what the event is, which counties and communes it touches, its kilometre range and period, and a reference to its geometry in map_geometries. Joins one-to-one to restrictions and roadworks on map_key. - 15.6K rows · 170 KB · write mode `snapshot_history` - Physically sorted by: `snapshot_date`, `snapshot_hour`, `road_code`, `km_from_km`, `feature_index` - Primary key: `snapshot_date`, `snapshot_hour`, `feature_index` - Text search columns: `communes`, `localities`, `cause`, `remedy`, `works_type`, `detours` - Files: 1 - https://data-directory.fsn1.your-objectstorage.com/public/andnet-road-conditions/v/01M5HWS58GJ2808Z7ZZ6QPCCZ8/map_features.parquet (15.6K rows) Columns: - `snapshot_date` DATE NOT NULL — The calendar day (UTC) this snapshot was taken. Leading sort column and, with snapshot_hour, the reload unit: re-ingesting the same (date, hour) replaces exactly those rows and nothing else. (range: "2026-09-02" … "2026-10-22") (~11 distinct) - `snapshot_hour` INTEGER NOT NULL [hour of day (UTC)] — The hour of day (0-23, UTC) this snapshot belongs to — this connector's own poll slot, not a value CNAIR publishes. Keying the reload unit on the hour rather than the date alone is what stops a later run in the same day from erasing an earlier one; see the file comment in src/tables.ts. (range: 3 … 23) (~21 distinct) - `snapshot_at` TIMESTAMPTZ NOT NULL — The exact instant (UTC) this snapshot was fetched — ctx.runAt, unrounded. snapshot_date/snapshot_hour bucket the run; this keeps the precise minute. - `feature_index` INTEGER NOT NULL — Zero-based position of this feature in the payload, counting the polyline array first and the point array after it. Part of the primary key. A position, not an identifier: use map_key to follow one event across snapshots. (range: 0 … 149) (~154 distinct) - `source_array` VARCHAR NOT NULL — Which JavaScript array declared this feature: "data" (the polylines) or "data2" (the single points). Published because the naming invites the opposite guess and a reader checking against the raw payload will want to know which is which. - `geometry_kind` VARCHAR NOT NULL — The shape of this feature: "path" for a polyline along the affected stretch, "point" for a single marker. Follows directly from source_array. - `category_kind` VARCHAR — What the feature is, in the same vocabulary as restrictions.restriction_kind — "incident", "short_works", "interrupted", "hampered" or "roadworks" — derived from the words of its own headline. NULL when the headline used wording no rule recognises, which the connector logs. - `category_code` VARCHAR — CNAIR's own numeric category for this feature, verbatim — "1.0", "1.1", "4.0", "5.0", "6.0". On the 2026-09-02 capture these mirrored the HTML section numbers exactly, which is a useful thing to know and a dangerous thing to depend on: category_kind is derived from the headline words instead, precisely so a CNAIR renumbering cannot silently reclassify every closure on the map. - `headline` VARCHAR — The bold first line of the feature's description — "Circulatie intrerupta A7". CNAIR writes it as the category followed by the road indicative, separated by a double space in the raw payload; runs of whitespace are collapsed to one here, as everywhere else in this dataset. - `road_code` VARCHAR — The road indicative, taken from the last token of the headline. (range: "22J" … "VO_CARANSEBES") - `map_key` VARCHAR — road indicative, start kilometre and end kilometre in CNAIR's notation, pipe-separated. The join key to restrictions.map_key and roadworks.map_key: on the capture day the 123 map features and the 123 table rows matched on it exactly, as a multiset, with nothing unmatched on either side. Built here from the feature's own headline and Km field, and on the table side from the map-zoom target CNAIR puts in each row — two independent derivations that agree, which is why the join is trustworthy rather than hopeful. - `counties` VARCHAR — The counties this feature crosses, as the payload's `Judet` field writes them: plate codes separated by ", " — "PH", "IF, DB, PH". Occasionally blank for a bypass that CNAIR has not attributed to a county. - `communes` VARCHAR — The administrative-territorial units (UAT — communes, towns and cities) the feature crosses, comma-separated and, unusually for this source, WITH diacritics: "Mogoșoaia, Buftea, Crevedia". This is the finest geography CNAIR publishes about an event and the HTML tables do not carry it at all. - `communes_lc` VARCHAR — communes, lowercased and accent-folded, for search. - `localities` VARCHAR — The two ends of the stretch, as the payload's `Localitati` field writes them. The same value as between_localities on the table side. - `localities_lc` VARCHAR — localities, lowercased and accent-folded, for search. - `km_text` VARCHAR — The kilometre range verbatim from the payload's `Km` field — either a range ("0+000 - 20+299") or a single position ("454+000") for a point event. - `km_from_text` VARCHAR — Start of the range, split out of km_text. - `km_from_km` DECIMAL(9,3) [km] — km_from_text as a number of kilometres. (range: 0 … 583) - `km_to_text` VARCHAR — End of the range. Equal to km_from_text when the payload gave a single position. - `km_to_km` DECIMAL(9,3) [km] — km_to_text as a number of kilometres. - `period_text` VARCHAR — The validity window verbatim from the payload's `Perioada` field, day-first and to the minute — "17-08-2026 01:00 - 15-09-2026 23:59". Note the format differs from the HTML tables', which are year-first and to the second. - `valid_from` TIMESTAMPTZ — Start of the window, as an instant, read as Europe/Bucharest local time and converted to UTC — CNAIR declares no timezone. period_text keeps the untouched string. - `valid_to` TIMESTAMPTZ — End of the window, under the same Europe/Bucharest assumption as valid_from. A plan, not an observation — see the same column on restrictions. - `cause` TEXT — The payload's `Cauza` field. **Frequently truncated by the source** — see cause_truncated — and the untruncated text for the same event is in restrictions.cause. Published anyway because the map and the tables can disagree between two refreshes, and a reader comparing them needs both. - `cause_lc` TEXT — cause, lowercased and accent-folded, for search. - `cause_truncated` BOOLEAN — True when CNAIR cut this value short and marked it with its "*Pentru descrierea completa consultati sectiunea drumuri." note. The note itself is stripped from the text. - `remedy` TEXT — The payload's `Masuri de remediere` field (or `Masuri de remediere si variante de ocolire`, which two features used on the capture day — both labels are accepted). May be truncated; see remedy_truncated. - `remedy_lc` TEXT — remedy, lowercased and accent-folded, for search. - `remedy_truncated` BOOLEAN — True when the source cut remedy short. - `works_type` TEXT — The payload's `Tip Lucrari` field. May be truncated. - `works_type_lc` TEXT — works_type, lowercased and accent-folded, for search. - `works_type_truncated` BOOLEAN — True when the source cut works_type short. - `detours` TEXT — The payload's `Variante de ocolire` field. May be truncated. - `detours_lc` TEXT — detours, lowercased and accent-folded, for search. - `detours_truncated` BOOLEAN — True when the source cut detours short. - `extra_fields` VARCHAR — Labels present in this feature's description that the connector does not map to a column of its own, comma-separated and lowercased — normally NULL. A non-null value means CNAIR has started publishing a field this connector has not caught up with; the connector logs a warning naming it on every run, and the text itself remains on the source page. - `latitude` DOUBLE [degrees] — Latitude of a point feature (WGS 84). NULL for a path (its extent is in the bounding-box columns), NULL when the source gave the [0, 0] placeholder — see coordinates_missing — and NULL when the pair fell outside Romania's bounding box, in which case the row is kept and coordinates_out_of_bounds is set. Coordinates are never a reason to drop a row. - `longitude` DOUBLE [degrees] — Longitude of a point feature (WGS 84). Note the payload stores coordinates as [longitude, latitude], GeoJSON order, not the [lat, lon] most map libraries take. Nulled together with latitude — a coordinate pair is accepted or rejected as a pair. - `coordinates_missing` BOOLEAN — True when CNAIR published the literal placeholder [0, 0] for this feature instead of a position. It is not the Gulf of Guinea; it is the source saying "no coordinate", and it appeared on 1 of the 41 point features on the 2026-09-02 capture. The row keeps all its attributes and its latitude/longitude are NULL. - `coordinates_out_of_bounds` BOOLEAN — True when the coordinates parsed as numbers but fell outside Romania (43.5-48.4°N, 20.2-29.8°E). latitude, longitude, the bounding box and geometry_hash are all NULL on such a row, and the connector logs it with the offending pair. A polyline is judged whole: one stray vertex rejects the whole shape, because publishing the in-bounds half would draw a line CNAIR never drew — worse than drawing none. The row itself, with all its attributes, is always kept. None occurred on the capture day. - `geometry_hash` VARCHAR — Fingerprint of this feature's geometry, and the foreign key into map_geometries where the coordinates themselves live. A 64-bit FNV-1a digest of the coordinate list, as 16 hex characters. NULL when the feature carried no usable coordinate at all. - `geometry_point_count` INTEGER [vertices] — How many vertices the geometry has: 1 for a point, up to a few thousand for a polyline (the longest on the capture day had 5,189). Carried on the fact row as well as on map_geometries so that a geometry_hash collision, however unlikely, would be visible rather than silent. - `bbox_min_latitude` DOUBLE [degrees] — South edge of the geometry's bounding box (WGS 84). Equal to latitude for a point. Present on the fact row so "everything within this map view during this period" can be answered without joining to map_geometries. - `bbox_max_latitude` DOUBLE [degrees] — North edge of the geometry's bounding box. - `bbox_min_longitude` DOUBLE [degrees] — West edge of the geometry's bounding box. - `bbox_max_longitude` DOUBLE [degrees] — East edge of the geometry's bounding box. - `colour_code` INTEGER — The payload's `culoare` field — which colour CNAIR draws this feature in. On the capture day it agreed perfectly with the category: 0 for hampered traffic, 1 for incidents and closures, 2 for works. A rendering hint, published because it is a second, independent signal of severity; category_kind is what to filter on. - `marker_radius` INTEGER [pixels] — The payload's `raza` field — the radius CNAIR draws the marker at, 7 or 10 on the capture day, with 10 reserved for road incidents. A rendering hint, nothing more. ### map_geometries One row per distinct geometry that has appeared on the CNAIR dispatch map, stored once rather than per snapshot: the polyline or point, as WKT, with its vertex count and bounding box. Join it from map_features.geometry_hash. first_seen_date and last_seen_date give the window over which this exact shape was being published. - 266 rows · 738 KB · write mode `upsert` - Physically sorted by: `geometry_hash` - Primary key: `geometry_hash` - Files: 1 - https://data-directory.fsn1.your-objectstorage.com/public/andnet-road-conditions/v/01M5HWS58GJ2808Z7ZZ6QPCCZ8/map_geometries.parquet (266 rows) Columns: - `geometry_hash` VARCHAR NOT NULL — The geometry's fingerprint and primary key: a 64-bit FNV-1a digest of its coordinate list, as 16 hex characters. Content-addressed, so the same shape reappearing next winter reuses this row instead of creating another. It is a fingerprint, not a cryptographic hash — a collision is possible at roughly 3 in 100 million over millions of shapes, and would show as one line drawn for another, never as a wrong number. (range: "01ba52c0cbcabb2a" … "fd00b508439490a2") (~252 distinct) - `geometry_kind` VARCHAR — "point" or "path". - `geometry_wkt` TEXT — The geometry as WKT in WGS 84 (EPSG:4326) — "POINT (21.9096267 45.80571621)" or "LINESTRING (26.19876199 44.85679144, …)". WKT rather than a coordinate array because DuckDB's spatial extension reads it directly: `INSTALL spatial; LOAD spatial;` then `ST_GeomFromText(geometry_wkt)` gives a real geometry to intersect, buffer and measure. Longitude first, as WKT requires. - `point_count` INTEGER [vertices] — Number of vertices — 1 for a point, up to a few thousand for a polyline. - `bbox_min_latitude` DOUBLE [degrees] — South edge of the bounding box (WGS 84). - `bbox_max_latitude` DOUBLE [degrees] — North edge of the bounding box. - `bbox_min_longitude` DOUBLE [degrees] — West edge of the bounding box. - `bbox_max_longitude` DOUBLE [degrees] — East edge of the bounding box. - `first_seen_date` DATE NOT NULL — The first run day (UTC) this exact shape appeared on the map. Maintained by the upsert loader — never overwritten, only widened. - `last_seen_date` DATE NOT NULL — The most recent run day (UTC) this shape was still on the map. A shape whose last_seen_date has stopped moving is a closure that has been lifted. ### other_sections One row per cell of every section on the CNAIR page that this connector has no dedicated table for — either recognised but never yet observed with data (winter-maintenance measures), or a section CNAIR has added since. Long and thin: each row is one cell, labelled with the column header the source printed. Nothing on the page is ever dropped; this is where it lands until it is modelled. - 9,258 rows · 40 KB · write mode `snapshot_history` - Physically sorted by: `snapshot_date`, `snapshot_hour`, `section_index`, `row_seq`, `column_index` - Primary key: `snapshot_date`, `snapshot_hour`, `section_index`, `row_seq`, `column_index` - Text search columns: `section_title`, `cell_text` - Files: 1 - https://data-directory.fsn1.your-objectstorage.com/public/andnet-road-conditions/v/01M5HWS58GJ2808Z7ZZ6QPCCZ8/other_sections.parquet (9,258 rows) Columns: - `snapshot_date` DATE NOT NULL — The calendar day (UTC) this snapshot was taken. Leading sort column and, with snapshot_hour, the reload unit: re-ingesting the same (date, hour) replaces exactly those rows and nothing else. (range: "2026-09-02" … "2026-10-22") (~11 distinct) - `snapshot_hour` INTEGER NOT NULL [hour of day (UTC)] — The hour of day (0-23, UTC) this snapshot belongs to — this connector's own poll slot, not a value CNAIR publishes. Keying the reload unit on the hour rather than the date alone is what stops a later run in the same day from erasing an earlier one; see the file comment in src/tables.ts. (range: 3 … 23) (~21 distinct) - `snapshot_at` TIMESTAMPTZ NOT NULL — The exact instant (UTC) this snapshot was fetched — ctx.runAt, unrounded. snapshot_date/snapshot_hour bucket the run; this keeps the precise minute. - `section_index` INTEGER NOT NULL — Zero-based position of the section this row came from, counted down the page as served that hour. Part of the primary key, and the only positional identity that is safe: the section NUMBER printed in the heading (section_code) is renumbered by CNAIR from time to time, and the heading text is reworded. (range: 9 … 11) (~2 distinct) - `row_seq` INTEGER NOT NULL — Zero-based position of this row within its section's table, as served. Together with section_index and the snapshot columns it is the primary key. It is a position, not an identifier: CNAIR does not promise the same closure keeps the same position between two fetches, so do not use it to follow one event through time — use (road_code, km_from_text, km_to_text) or map_key for that. (range: 0 … 38) (~39 distinct) - `column_index` INTEGER NOT NULL — Zero-based position of this cell within its row. With the snapshot columns, section_index and row_seq it forms the primary key. (range: 0 … 5) (~6 distinct) - `section_code` VARCHAR — The section number as the heading prints it, when it has one. - `section_title` VARCHAR — The section heading verbatim. - `section_title_lc` VARCHAR — section_title, lowercased and accent-folded, for search — the column to scan when you want to know whether CNAIR has started publishing something new. - `section_kind` VARCHAR — What this connector believes the section is about, when it recognised the heading but has no table for it — "winter_measures", "weather_precipitation". NULL when the heading matched no rule at all, which is the case worth investigating. - `recognised` BOOLEAN NOT NULL — False when the heading matched none of the connector's rules. Every such section is also logged as a warning naming the heading verbatim, on every run, so it cannot go unnoticed: `SELECT DISTINCT section_title FROM other_sections WHERE NOT recognised` is the query that says this connector needs updating. - `row_no` VARCHAR — The source's own "NR. CRT." value for this row, when it has one. - `column_label` VARCHAR — The column header CNAIR printed for this cell, verbatim from the table's `` — the source's own name for what the value means, which is why these rows are readable without a schema. NULL when the table had no header for this position. - `column_placeholder` VARCHAR — The cell's own `placeholder` attribute, verbatim — CNAIR repeats the column name in capitals on every cell, and this attribute is the more stable of the two labels. - `column_key` VARCHAR — The label this connector matched on: the placeholder if present, otherwise the header, lowercased and accent-folded. This is the column to GROUP BY when pivoting a section back into a table. - `cell_text` TEXT — The cell's text, verbatim, with `
` separators turned into newlines and HTML entities decoded. Nothing is normalised beyond that: a value here is what CNAIR published. - `cell_text_lc` TEXT — cell_text, lowercased and accent-folded, for search. - `numeric_value` DOUBLE — cell_text as a number, when it is a plain number (optionally with a comma or point decimal separator) and nothing else. NULL for text, for a number with a unit glued to it, and for a blank. It exists so a quantity in an unmodelled section — tonnes of anti-skid material, litres of CaCl2, a count of vehicles — can be summed without parsing text in SQL, and it is DOUBLE rather than DECIMAL because the scale of a column nobody has modelled yet is by definition unknown. When precision matters, parse cell_text yourself. ## Performance notes - There are no indexes in a lakehouse. The physical sort order plus Parquet row-group min/max statistics ARE the index. Predicates on the leading sort columns prune whole row groups; everything else is a full column scan. - `restrictions` prunes on `snapshot_date` → `snapshot_hour` → `road_code` → `km_from_km` → `section_index` → `row_seq`. - `roadworks` prunes on `snapshot_date` → `snapshot_hour` → `road_code` → `km_from_km` → `section_index` → `row_seq`. - `carriageway_condition` prunes on `snapshot_date` → `snapshot_hour` → `county_code` → `section_index` → `row_seq`. - `weather_observations` prunes on `snapshot_date` → `snapshot_hour` → `observation_kind` → `county_code` → `locality`. - `temperatures` prunes on `snapshot_date` → `snapshot_hour` → `station_name` → `row_seq`. - `winter_closed_roads` prunes on `snapshot_date` → `snapshot_hour` → `road_code` → `km_from_km` → `row_seq`. - `map_features` prunes on `snapshot_date` → `snapshot_hour` → `road_code` → `km_from_km` → `feature_index`. - `map_geometries` prunes on `geometry_hash`. - `other_sections` prunes on `snapshot_date` → `snapshot_hour` → `section_index` → `row_seq` → `column_index`. - `ILIKE '%needle%'` cannot prune: it decompresses that whole column. Prefer prefix predicates on the lowercased search companion columns where they exist. - Every file URL under `/v//` is immutable. Cache it as aggressively as you like. - Files are never rewritten in place — a new ingest writes a new version path. Re-read the manifest to pick up updates rather than polling the file URLs. - Pull only the columns you need. Parquet is columnar and the reader range-GETs what it reads. ## Do not - Do not scrape https://data.bunnytech.app HTML. Every number on those pages comes from the manifest above. - Do not hit the upstream source (https://dispecerat.andnet.ro/index.php) in bulk. This mirror exists so you do not have to. - Do not read DuckLake internal data files with a plain Parquet reader. They come with positional delete files, and ignoring those silently returns deleted rows. Use the ducklake extension, or use the published exports above. - Do not republish without the attribution line, and do not imply endorsement.