Ukraine's administrative register (КАТОТТГ) as open data, with boundary polygons and centre points for every place.
The register itself is published by the Ministry of Development as a spreadsheet: 31744 places, each under a code, with a parent and a category. It carries no geometry. OpenStreetMap has the geometry but no hierarchy worth trusting - no parent codes, names in whichever language a mapper typed, and the same ground traced twice under different relations.
This joins the two: the register decides what a place is, what it is called and what it belongs to; OpenStreetMap supplies its outline.
| file | size | where | what it holds |
|---|---|---|---|
katottg.csv |
6.2 MB | data/out/, in git |
the register, with lat/lon for every matched place |
katottg.parquet |
1.6 MB | data/out/, in git |
the same, typed |
boundaries.geojson.gz |
29 MB | releases | 20357 polygons, one feature per line |
The register is committed, so it can be pinned as a submodule and read without fetching anything:
git submodule add https://github.com/alexbabintsev/ua-geo.gitThe boundaries are 98 MB unpacked and would add that to the history with every edition, so they live in the releases instead. They are read by hand - into QGIS, or a database - rather than by anything that pins a commit.
| column | type | example |
|---|---|---|
code |
string | UA53080370010073240 |
name |
string | Полтава |
full_name |
string | Полтава |
level |
int | 4 |
category |
string | M |
category_name |
string | місто |
parent_code |
string | UA53080370000025447 |
region_code |
string | UA53000000000028050 |
raion_code |
string | UA53080000000068029 |
hromada_code |
string | UA53080370000025447 |
koatuu |
string | 5310100000 |
lat |
float | 49.589742 |
lon |
float | 34.550795 |
area_km2 |
float | 103.4 |
osm_id |
int | 1641691 |
osm_type |
string | R |
match |
string | code |
| level | what | how many |
|---|---|---|
| 1 | область | 27 |
| 2 | район | 136 |
| 3 | громада | 1772 |
| 4 | населений пункт | 29701 |
| 5 | район у місті | 108 |
match says which rule found the geometry, so a bad match can be traced:
| rule | places | what it means |
|---|---|---|
code |
31218 | the OSM object carries the register's own katotth code |
name |
159 | the name matched within its region |
parent |
6 | the name matched among the children of a parent already placed |
none |
361 | nothing was found |
The 361 without geometry are 360 places in Crimea, where OpenStreetMap is mapped under the Russian administrative scheme and the Ukrainian hromadas are not drawn at all, and one village in Luhansk oblast. They keep their names, their codes and their place in the hierarchy; they simply have no outline.
A match is one or the other, which is why there are 31383 places with a centre but only 20357 polygons:
| places | what you get | |
|---|---|---|
| an outline | 20357 | the polygon, its centre and its area |
| a point | 11037 | the centre |
| nothing | 361 | the name and its place in the hierarchy |
The 11037 are almost all villages. In OpenStreetMap a village is usually a
single node carrying a name - Бабин in Vinnytsia oblast is N337570475, tagged
place=village with the state's code on it and nothing drawn around it. Nobody
has traced outlines for 27000 villages, and few ever will.
So a village can be found, opened on a map and measured a distance to. It cannot be selected by drawing a shape around it, and its area is unknown.
import pandas as pd
places = pd.read_parquet("katottg.parquet")
# Every city in Poltava oblast
cities = places[
(places.region_code == "UA53000000000028050") & (places.category == "M")
]-- DuckDB reads the parquet directly, over HTTP if you like
SELECT name, lat, lon FROM 'katottg.parquet' WHERE category = 'M';The pipeline needs a local Nominatim holding Ukraine. The public instance allows one request a second and its usage policy names systematic queries as unacceptable; there are 31744 places to place.
# 1. Ukraine's OSM extract, 830 MB
curl -o data/raw/ukraine.osm.pbf https://download.geofabrik.de/europe/ukraine-latest.osm.pbf
# 2. Import it. Give Docker 8 GB or the indexing will swap.
docker compose up -d
docker compose logs -f nominatim
# 3. The current register from the ministry
ua-geo fetch
# 4. Match and write the files
ua-geo build
ua-geo checkThe ministry amends the register several times a year and OSM changes daily.
ua-geo fetch # the newest register
docker compose exec nominatim \
sudo -u nominatim nominatim replication --once # OSM diffs
ua-geo buildThe code is MIT. The register is open data published by the Ministry of Development of Communities and Territories of Ukraine. The boundaries are derived from OpenStreetMap and are therefore ODbL 1.0: if you distribute them, or anything derived from them, you must say so and share alike.
Кодифікатор адміністративно-територіальних одиниць та територій територіальних громад (КАТОТТГ) як відкриті дані - з полігонами меж та центральними точками для кожного місця.
Сам кодифікатор Мінрозвитку публікує як книгу Excel: 31744 місця, кожне під своїм кодом, з батьківською одиницею та категорією. Геометрії в ньому немає. В OpenStreetMap геометрія є, але немає ієрархії, якій можна довіряти: батьків не записано, назви тією мовою, яку набрав картограф, і та сама земля обведена двічі різними відношеннями.
Цей набір з'єднує обидва: кодифікатор вирішує, чим є місце, як воно називається і чому належить; OpenStreetMap дає його обрис.
| файл | розмір | де | що містить |
|---|---|---|---|
katottg.csv |
6.2 МБ | data/out/, у git |
кодифікатор з lat/lon для кожного знайденого місця |
katottg.parquet |
1.6 МБ | data/out/, у git |
те саме, з типами |
boundaries.geojson.gz |
29 МБ | релізи | 20357 полігонів, по одному об'єкту на рядок |
Кодифікатор лежить у git, тож його можна підключити підмодулем і читати, нічого не звантажуючи:
git submodule add https://github.com/alexbabintsev/ua-geo.gitМежі важать 98 МБ розпакованими і додавали б стільки до історії з кожною редакцією, тому живуть у релізах. Їх читають руками - у QGIS чи в базу - а не чимось, що фіксує коміт.
| стовпець | тип | приклад |
|---|---|---|
code |
string | UA53080370010073240 |
name |
string | Полтава |
full_name |
string | Полтава |
level |
int | 4 |
category |
string | M |
category_name |
string | місто |
parent_code |
string | UA53080370000025447 |
region_code |
string | UA53000000000028050 |
raion_code |
string | UA53080000000068029 |
hromada_code |
string | UA53080370000025447 |
koatuu |
string | 5310100000 |
lat |
float | 49.602750 |
lon |
float | 34.548763 |
area_km2 |
float | 105.27 |
osm_id |
int | 1641691 |
osm_type |
string | R |
match |
string | code |
| рівень | що | скільки |
|---|---|---|
| 1 | область | 27 |
| 2 | район | 136 |
| 3 | громада | 1772 |
| 4 | населений пункт | 29701 |
| 5 | район у місті | 108 |
Стовпець match каже, яке правило спрацювало, тож хибну прив'язку можна
простежити:
| правило | місць | що означає |
|---|---|---|
code |
31218 | об'єкт OSM несе код КАТОТТГ - найнадійніше свідчення |
name |
159 | назва збіглася в межах області |
parent |
6 | назва збіглася серед дітей уже знайденого батька |
none |
361 | геометрії не знайдено |
Ті 361 без геометрії - це 360 місць у Криму, де OSM розмічено за російською схемою і українських громад не намальовано взагалі, та одне село на Луганщині. Вони зберігають назви, коди та місце в ієрархії - просто не мають обрису.
Збіг дає одне з двох - тому місць із центром 31383, а полігонів лише 20346:
| місць | що маєте | |
|---|---|---|
| обрис | 20357 | полігон, його центр і площа |
| точка | 11037 | центр |
| нічого | 361 | назву та місце в ієрархії |
Ті 11037 - майже все села. В OpenStreetMap село зазвичай позначене одним вузлом
із назвою: Бабин на Вінниччині - це N337570475, place=village, з державним
кодом на ньому і без жодного обрису навколо. Обвести 27000 сіл ніхто не брався
і навряд чи візьметься.
Тож село можна знайти, відкрити на карті й виміряти до нього відстань. Але не можна вибрати, обвівши область на карті, і площа його невідома.
import pandas as pd
places = pd.read_parquet("katottg.parquet")
# Усі міста Полтавської області
cities = places[
(places.region_code == "UA53000000000028050") & (places.category == "M")
]-- DuckDB читає parquet напряму, хоч і через HTTP
SELECT name, lat, lon FROM 'katottg.parquet' WHERE category = 'M';Потрібен локальний Nominatim з даними України. Публічний дозволяє один запит на секунду, а його правила користування прямо називають систематичні запити неприйнятними - тут же 31744 місця.
# 1. Витяг OSM по Україні, 830 МБ
curl -o data/raw/ukraine.osm.pbf https://download.geofabrik.de/europe/ukraine-latest.osm.pbf
# 2. Імпортувати. Дайте Docker 8 ГБ, інакше індексація піде у своп.
docker compose up -d
docker compose logs -f nominatim
# 3. Свіжий кодифікатор із сайту міністерства
ua-geo fetch
# 4. Зіставити й записати файли
ua-geo build
ua-geo checkМіністерство змінює кодифікатор кілька разів на рік, а OSM змінюється щодня.
ua-geo fetch # свіжий кодифікатор
docker compose exec nominatim \
sudo -u nominatim nominatim replication --once # добові дифи OSM
ua-geo buildКод - MIT. Кодифікатор - відкриті дані Міністерства розвитку громад та територій України. Межі походять з OpenStreetMap, тому на них поширюється ODbL 1.0: поширюючи їх або будь-що на їх основі, треба вказати джерело та зберегти ту саму ліцензію.