332 lines
10 KiB
Python
Executable file
332 lines
10 KiB
Python
Executable file
#!/usr/bin/env python3
|
||
"""
|
||
Bulgarian City-to-Region Lookup Table Generator.
|
||
|
||
Creates a comprehensive mapping of Bulgarian cities to their administrative regions (oblasts)
|
||
to enable 100% GHCID generation coverage for Bulgarian heritage institutions.
|
||
|
||
Data source: GeoNames + Bulgarian administrative divisions (ISO 3166-2:BG)
|
||
"""
|
||
|
||
import json
|
||
import sqlite3
|
||
from pathlib import Path
|
||
from typing import Dict, Optional
|
||
|
||
# Bulgarian regions (28 oblasts) with ISO 3166-2 codes
|
||
BULGARIAN_REGIONS = {
|
||
# Region name (Bulgarian) → ISO code
|
||
'Благоевград': 'BG-01', # Blagoevgrad
|
||
'Бургас': 'BG-02', # Burgas
|
||
'Варна': 'BG-03', # Varna
|
||
'Велико Търново': 'BG-04', # Veliko Tarnovo
|
||
'Видин': 'BG-05', # Vidin
|
||
'Враца': 'BG-06', # Vratsa
|
||
'Габрово': 'BG-07', # Gabrovo
|
||
'Добрич': 'BG-08', # Dobrich
|
||
'Кърджали': 'BG-09', # Kardzhali
|
||
'Кюстендил': 'BG-10', # Kyustendil
|
||
'Ловеч': 'BG-11', # Lovech
|
||
'Монтана': 'BG-12', # Montana
|
||
'Пазарджик': 'BG-13', # Pazardzhik
|
||
'Перник': 'BG-14', # Pernik
|
||
'Плевен': 'BG-15', # Pleven
|
||
'Пловдив': 'BG-16', # Plovdiv
|
||
'Разград': 'BG-17', # Razgrad
|
||
'Русе': 'BG-18', # Ruse
|
||
'Силистра': 'BG-19', # Silistra
|
||
'Сливен': 'BG-20', # Sliven
|
||
'Смолян': 'BG-21', # Smolyan
|
||
'София': 'BG-22', # Sofia (capital)
|
||
'София област': 'BG-23', # Sofia Province
|
||
'Стара Загора': 'BG-24', # Stara Zagora
|
||
'Търговище': 'BG-25', # Targovishte
|
||
'Хасково': 'BG-26', # Haskovo
|
||
'Шумен': 'BG-27', # Shumen
|
||
'Ямбол': 'BG-28', # Yambol
|
||
}
|
||
|
||
# Major city → region mappings (manually curated)
|
||
# Source: Bulgarian administrative divisions + GeoNames
|
||
CITY_REGION_MANUAL = {
|
||
# English city name → Region Bulgarian name
|
||
|
||
# BG-01: Blagoevgrad
|
||
'Blagoevgrad': 'Благоевград',
|
||
'Bansko': 'Благоевград',
|
||
'Gotse Delchev': 'Благоевград',
|
||
'Petrich': 'Благоевград',
|
||
'Sandanski': 'Благоевград',
|
||
'Razlog': 'Благоевград',
|
||
'Simitli': 'Благоевград',
|
||
'Yakoruda': 'Благоевград',
|
||
'Belitsa': 'Благоевград',
|
||
'Hadjidimovo': 'Благоевград',
|
||
|
||
# BG-02: Burgas
|
||
'Burgas': 'Бургас',
|
||
'Nesebar': 'Бургас',
|
||
'Aytos': 'Бургас',
|
||
'Karnobat': 'Бургас',
|
||
'Pomorie': 'Бургас',
|
||
'Sozopol': 'Бургас',
|
||
'Tsarevo': 'Бургас',
|
||
'Sredets': 'Бургас',
|
||
'Malko Tarnovo': 'Бургас',
|
||
|
||
# BG-03: Varna
|
||
'Varna': 'Варна',
|
||
'Devnya': 'Варна',
|
||
'Provadiya': 'Варна',
|
||
'Beloslav': 'Варна',
|
||
'Aksakovo': 'Варна',
|
||
'Byala': 'Варна',
|
||
|
||
# BG-04: Veliko Tarnovo
|
||
'Veliko Tarnovo': 'Велико Търново',
|
||
'Gorna Oryahovitsa': 'Велико Търново',
|
||
'Svishtov': 'Велико Търново',
|
||
'Pavlikeni': 'Велико Търново',
|
||
'Elena': 'Велико Търново',
|
||
'Suhindol': 'Велико Търново',
|
||
|
||
# BG-05: Vidin
|
||
'Vidin': 'Видин',
|
||
'Belogradchik': 'Видин',
|
||
'Kula': 'Видин',
|
||
'Gramada': 'Видин',
|
||
'Bregovo': 'Видин',
|
||
|
||
# BG-06: Vratsa
|
||
'Vratsa': 'Враца',
|
||
'Kozloduy': 'Враца',
|
||
'Mezdra': 'Враца',
|
||
'Oryahovo': 'Враца',
|
||
'Byala Slatina': 'Враца',
|
||
'Roman': 'Враца',
|
||
|
||
# BG-07: Gabrovo
|
||
'Gabrovo': 'Габрово',
|
||
'Sevlievo': 'Габрово',
|
||
'Dryanovo': 'Габрово',
|
||
'Tryavna': 'Габрово',
|
||
|
||
# BG-08: Dobrich
|
||
'Dobrich': 'Добрич',
|
||
'Balchik': 'Добрич',
|
||
'Kavarna': 'Добрич',
|
||
'General Toshevo': 'Добрич',
|
||
|
||
# BG-09: Kardzhali
|
||
'Kardzhali': 'Кърджали',
|
||
'Momchilgrad': 'Кърджали',
|
||
'Krumovgrad': 'Кърджали',
|
||
'Ardino': 'Кърджали',
|
||
|
||
# BG-10: Kyustendil
|
||
'Kyustendil': 'Кюстендил',
|
||
'Dupnitsa': 'Кюстендил',
|
||
'Bobov Dol': 'Кюстендил',
|
||
'Sapareva Banya': 'Кюстендил',
|
||
|
||
# BG-11: Lovech
|
||
'Lovech': 'Ловеч',
|
||
'Troyan': 'Ловеч',
|
||
'Teteven': 'Ловеч',
|
||
'Lukovit': 'Ловеч',
|
||
'Apriltsi': 'Ловеч',
|
||
|
||
# BG-12: Montana
|
||
'Montana': 'Монтана',
|
||
'Lom': 'Монтана',
|
||
'Berkovitsa': 'Монтана',
|
||
'Valchedram': 'Монтана',
|
||
|
||
# BG-13: Pazardzhik
|
||
'Pazardzhik': 'Пазарджик',
|
||
'Panagyurishte': 'Пазарджик',
|
||
'Velingrad': 'Пазарджик',
|
||
'Peshtera': 'Пазарджик',
|
||
'Septemvri': 'Пазарджик',
|
||
|
||
# BG-14: Pernik
|
||
'Pernik': 'Перник',
|
||
'Radomir': 'Перник',
|
||
'Breznik': 'Перник',
|
||
|
||
# BG-15: Pleven
|
||
'Pleven': 'Плевен',
|
||
'Cherven Bryag': 'Плевен',
|
||
'Knezha': 'Плевен',
|
||
'Levski': 'Плевен',
|
||
'Nikopol': 'Плевен',
|
||
|
||
# BG-16: Plovdiv
|
||
'Plovdiv': 'Пловдив',
|
||
'Asenovgrad': 'Пловдив',
|
||
'Karlovo': 'Пловдив',
|
||
'Hisarya': 'Пловдив',
|
||
'Rakovski': 'Пловдив',
|
||
'Parvomay': 'Пловдив',
|
||
|
||
# BG-17: Razgrad
|
||
'Razgrad': 'Разград',
|
||
'Isperih': 'Разград',
|
||
'Kubrat': 'Разград',
|
||
'Zavet': 'Разград',
|
||
|
||
# BG-18: Ruse
|
||
'Ruse': 'Русе',
|
||
'Byala': 'Русе',
|
||
'Borovo': 'Русе',
|
||
'Vetovo': 'Русе',
|
||
|
||
# BG-19: Silistra
|
||
'Silistra': 'Силистра',
|
||
'Tutrakan': 'Силистра',
|
||
'Dulovo': 'Силистра',
|
||
'Alfatar': 'Силистра',
|
||
|
||
# BG-20: Sliven
|
||
'Sliven': 'Сливен',
|
||
'Nova Zagora': 'Сливен',
|
||
'Kotel': 'Сливен',
|
||
'Tvarditsa': 'Сливен',
|
||
|
||
# BG-21: Smolyan
|
||
'Smolyan': 'Смолян',
|
||
'Madan': 'Смолян',
|
||
'Zlatograd': 'Смолян',
|
||
'Banite': 'Смолян',
|
||
'Chepelare': 'Смолян',
|
||
|
||
# BG-22: Sofia (capital city)
|
||
'Sofia': 'София',
|
||
|
||
# BG-23: Sofia Province (surrounding region)
|
||
'Bozhurishte': 'София област',
|
||
'Botevgrad': 'София област',
|
||
'Elin Pelin': 'София област',
|
||
'Etropole': 'София област',
|
||
'Ihtiman': 'София област',
|
||
'Kostinbrod': 'София област',
|
||
'Pirdop': 'София област',
|
||
'Samokov': 'София област',
|
||
'Slivnitsa': 'София област',
|
||
'Svoge': 'София област',
|
||
|
||
# BG-24: Stara Zagora
|
||
'Stara Zagora': 'Стара Загора',
|
||
'Kazanlak': 'Стара Загора',
|
||
'Chirpan': 'Стара Загора',
|
||
'Radnevo': 'Стара Загора',
|
||
'Galabovo': 'Стара Загора',
|
||
|
||
# BG-25: Targovishte
|
||
'Targovishte': 'Търговище',
|
||
'Omurtag': 'Търговище',
|
||
'Popovo': 'Търговище',
|
||
'Opaka': 'Търговище',
|
||
|
||
# BG-26: Haskovo
|
||
'Haskovo': 'Хасково',
|
||
'Dimitrovgrad': 'Хасково',
|
||
'Svilengrad': 'Хасково',
|
||
'Harmanli': 'Хасково',
|
||
'Madzharovo': 'Хасково',
|
||
|
||
# BG-27: Shumen
|
||
'Shumen': 'Шумен',
|
||
'Veliki Preslav': 'Шумен',
|
||
'Kaspichan': 'Шумен',
|
||
'Novi Pazar': 'Шумен',
|
||
|
||
# BG-28: Yambol
|
||
'Yambol': 'Ямбол',
|
||
'Elhovo': 'Ямбол',
|
||
'Bolyarovo': 'Ямбол',
|
||
}
|
||
|
||
|
||
def get_region_from_city(city_name: str) -> Optional[str]:
|
||
"""
|
||
Get ISO 3166-2 region code for a Bulgarian city.
|
||
|
||
Args:
|
||
city_name: City name (English, e.g., "Sofia", "Burgas")
|
||
|
||
Returns:
|
||
ISO 3166-2 code (e.g., "BG-22") or None if not found
|
||
"""
|
||
region_bg = CITY_REGION_MANUAL.get(city_name)
|
||
if region_bg:
|
||
return BULGARIAN_REGIONS.get(region_bg)
|
||
return None
|
||
|
||
|
||
def get_region_code_from_iso(iso_code: str) -> str:
|
||
"""
|
||
Extract numeric region code from ISO 3166-2 code.
|
||
|
||
Args:
|
||
iso_code: ISO code (e.g., "BG-22")
|
||
|
||
Returns:
|
||
Numeric code (e.g., "22")
|
||
"""
|
||
return iso_code.split('-')[1]
|
||
|
||
|
||
def export_lookup_table(output_path: Path) -> None:
|
||
"""Export city-region lookup table to JSON."""
|
||
lookup = {}
|
||
for city_en, region_bg in CITY_REGION_MANUAL.items():
|
||
iso_code = BULGARIAN_REGIONS[region_bg]
|
||
lookup[city_en] = {
|
||
'region_bulgarian': region_bg,
|
||
'region_iso_code': iso_code,
|
||
'region_numeric': get_region_code_from_iso(iso_code)
|
||
}
|
||
|
||
with open(output_path, 'w', encoding='utf-8') as f:
|
||
json.dump(lookup, f, ensure_ascii=False, indent=2)
|
||
|
||
print(f"✓ Exported {len(lookup)} city-region mappings to {output_path}")
|
||
|
||
|
||
def main():
|
||
"""Generate Bulgarian city-region lookup table."""
|
||
output_file = Path(__file__).parent.parent / 'data' / 'reference' / 'bulgarian_city_regions.json'
|
||
output_file.parent.mkdir(parents=True, exist_ok=True)
|
||
|
||
print("Generating Bulgarian city-region lookup table...")
|
||
print(f"Total regions: {len(BULGARIAN_REGIONS)}")
|
||
print(f"Total cities mapped: {len(CITY_REGION_MANUAL)}")
|
||
print()
|
||
|
||
# Verify all regions are covered
|
||
covered_regions = set(CITY_REGION_MANUAL.values())
|
||
all_regions = set(BULGARIAN_REGIONS.keys())
|
||
missing_regions = all_regions - covered_regions
|
||
|
||
if missing_regions:
|
||
print(f"⚠️ Warning: {len(missing_regions)} regions have no cities mapped:")
|
||
for region in sorted(missing_regions):
|
||
print(f" - {region} ({BULGARIAN_REGIONS[region]})")
|
||
print()
|
||
|
||
export_lookup_table(output_file)
|
||
|
||
# Test lookups
|
||
print("\nTest Lookups:")
|
||
test_cities = ['Sofia', 'Burgas', 'Varna', 'Plovdiv', 'Belitsa']
|
||
for city in test_cities:
|
||
region_code = get_region_from_city(city)
|
||
if region_code:
|
||
print(f" {city:20s} → {region_code}")
|
||
else:
|
||
print(f" {city:20s} → NOT FOUND")
|
||
|
||
|
||
if __name__ == '__main__':
|
||
main()
|