Parses INSPIRE GML administrative unit data (as published by CUZK) into GeoJSON, split into one file per administrative level.
Parsed data can be found here: https://github.com/siwekm/czech-geojson
pip install -r requirements.txt
python generate_geojson.py [options]
| Option | Default | Description |
|---|---|---|
--input |
data/1.xml |
Path to the source GML file. |
--output-dir |
data_parsed/ |
Directory to write the output GeoJSON files into. |
--limit-num |
1000 |
Max number of coordinates to keep per ring for levels listed in --limit-units. Coordinates beyond this are thinned evenly (every Nth point kept) to reduce file size/detail. 0 disables thinning. |
--limit-units |
obce |
Space-separated list of level names (see Output below) whose ring coordinates get thinned to --limit-num. Pass nothing to disable thinning entirely. |
Example: python generate_geojson.py --input data/1.xml --limit-units obce okresy --limit-num 500
One GeoJSON FeatureCollection file is written per administrative level found
in the source file:
| File | Level | INSPIRE order | Count (CZ, 2026) |
|---|---|---|---|
czech_republic.json |
Country | 1stOrder | 1 |
kraje.json |
Regions | 2ndOrder | 14 |
okresy.json |
Districts | 3rdOrder | 77 |
obce.json |
Municipalities | 4thOrder | 6258 |
The level-to-filename mapping lives in LEVEL_NAMES at the top of
generate_geojson.py - any unit whose level isn't in that map is skipped with
a warning.
Each feature carries nationalCode, localId, id and name properties
alongside its geometry. Geometry is a Polygon normally, or a MultiPolygon
for the small number of units (~100 in the full CZ dataset) that consist of
multiple disconnected parts - e.g. a municipality with a river island or an
exclave. Interior rings (holes - an enclave of a different unit fully
surrounded by this one) are preserved as holes in the polygon.
The input is a GML/INSPIRE AdministrativeUnits dump (an
elf-au:AdministrativeUnit schema WFS export from CUZK), a single XML file
that is one <base:SpatialDataSet> root containing a flat list of
<base:member> elements. Each base:member wraps exactly one feature, and
two different feature types are mixed together in the same list:
elf-au:AdministrativeUnit- an actual administrative unit (country / region / district / municipality). This is the only feature type the parser reads. Relevant children:au:geometry/gml:MultiSurface/gml:surfaceMember(one or more) - eachsurfaceMemberis one disconnected polygon part (normally just one; more than one means the unit has islands/exclaves). Each contains agml:Polygonwith:gml:exterior/gml:LinearRing/gml:posList- the outer ring, required.gml:interior/gml:LinearRing/gml:posList(zero or more) - holes (enclaves belonging to another unit).- Coordinates in
gml:posListare whitespace-separatedlat lon lat lon ...pairs (EPSG:4258/WGS84-like, lat first) - the parser swaps them to GeoJSON's[lon, lat]order. GML rings here are already closed (first point == last point).
au:nationalCode- the unit's official numeric code.au:nationalLevel(xlink:titleattribute) - the hierarchy level:1stOrder(country),2ndOrder(region/kraj),3rdOrder(district/okres),4thOrder(municipality/obec). This is what the parser uses to sort each unit into the right output file.au:inspireId/base:Identifier/base:localId- a GML-internal id.au:name/gn:GeographicalName/gn:spelling/gn:SpellingOfName/gn:text- the human-readable name (can contain Czech diacritics - the file is UTF-8, so read/write code must useencoding="utf-8"explicitly rather than relying on the system locale).
au:AdministrativeBoundary- a shared-border line segment between two neighbouring units (au:geometry/gml:Curve/gml:segments/gml:LineStringSegment/gml:posList, an open line, not a closed ring). These vastly outnumber the actual units (in the full CZ dump: ~47k boundary segments vs ~6.3k units) and carry no name/code/level of their own - they must be ignored, not treated as unit polygons. The parser does this by selectingelf-au:AdministrativeUnitelements directly.