Jakiś czas temu prezentowałem jak można uruchomić lokalnie reverse geocoding (zamianę współrzędnych geograficznych na dane adresowe) w oparciu o usługę Nominatim z OpenStreetMap. Tutaj pokażę podobny system, tym razem zrealizowany ręcznie w Pythonie w oparciu o gotowe biblioteki i gotowe urzędowe mapy jednostek administracyjnych udostępnionych przez Państwowy Urząd Granic (PRG). Należą do nich granice państwa, województw, powiatów, gmin i jednostek oraz obrębów ewidencyjnych. Całość będzie na tyle lekka, że serwer uda się uruchomić na Raspberry Pi 4.
Poprzedni temat z serii:
Jak uruchomić OpenStreetMap lokalnie? Reverse geocoding bez limitów na Twoim komputerze
Państwowy Rejestr Granic
Państwowy Rejestr Granic (PRG) to oficjalna baza danych opisująca granice i podział administracyjny kraju, wykorzystywana przez inne systemy informacji przestrzennej. Zawiera dane o gminach, powiatach, województwach, jednostkach ewidencyjnych oraz adresach wraz z ich lokalizacją i podstawowymi opisami (np. nazwa, kod TERYT).
Dane o granicach i powierzchniach jednostek administracyjnych są aktualizowane raz w roku według stanu na 1 stycznia na podstawie aktów prawnych lub zmian w ewidencji gruntów. Informacje o adresach są uzupełniane na bieżąco zgodnie ze zmianami wprowadzanymi przez urzędy gmin.
Dane źródłowe do eksperymentu można pobrać na Geoportalu. Mamy tam do wyboru osobno dane z każdego roku - też te historyczne, lata 2004 - 2024.
Format udostępnionych danych
Dane PRG udostępniane są w formacie ESRI Shapefile, czyli dla jednej warstwy otrzymujemy zestaw kilku plików (m.in. .shp, .dbf, .shx, .prj), które łącznie opisują geometrię i atrybuty obiektów.
Pliki te zawierają dane wektorowe, głównie wielokąty granic administracyjnych.
Pliki wcale nie są takie duże i mają potencjał do uruchomienia nawet na jednopłytkowym komputerze. Sytuacja dodatkowo upraszcza się, gdy rezygnujemy z najmniejszej jednostki administracyjnej - obrębów, czyli czegoś, co nie ma raczej dla nas praktycznego zastosowania. Obręby zajmują 300 MB, a kolejna warstwa (jednostki) tylko 100 MB. Jednostki to już są, upraszczając, m. in. wsie czy tam miasteczka, choć miasta często składają się z kilku jednostek. Pokażę to potem na zrzutach ekranu.
Wykorzystane technologie
Do projektu użyłem Pythona 3 z bibliotekami Flask (backend HTTP) oraz GeoPandas i Shapely do obsługi danych GIS w formacie ESRI Shapefile.
Część wizualna (frontend) została zrealizowana w przeglądarce przy użyciu Leaflet.js z podkładem OpenStreetMap.
Obsługa Shapefile i własny serwer na Raspberry Pi
Najprostszą i jednocześnie najbardziej wieloplatformową metodą obsługi plików ESRI Shapefile jest biblioteka GeoPandas dostępna w Pythonie. Jest to nakładka na Pandas, rozszerzona o obsługę geometrii (Shapely) i formatów GIS.
GeoPandas bez problemu działa na Linuxie (również ARM - Raspberry Pi), Windowsie oraz macOS. W praktyce wystarczy Python 3.x oraz kilka zależności systemowych (gdal, proj), które na Raspberry Pi są dostępne w repozytoriach.
Komenda instalująca zależności na Raspberry Pi 4:
apt update
apt install -y \
python3-pip \
python3-dev \
python3-geopandas \
gdal-bin \
libgdal-dev \
libgeos-dev \
libproj-dev
Przykładowy zrzut ekranu z instalacji:
Do samego postawienia serwera został użyty framework Flask - pozwala on utworzyć prosty serwer HTTP i obsługiwać nadchodzące zapytania i żądania.
Obsługa danych PRG w Pythonie
Każda warstwa PRG (państwa, województwa, powiaty, gminy itd.) jest wczytywana do osobnego obiektu GeoDataFrame.
W przykładzie każdej geometrii przypisywany jest prosty numeryczny identyfikator uid, który później wykorzystywany jest w interfejsie webowym.
Kod: Python
Wszystkie warstwy są przechowywane w słowniku LAYERS, co pozwala dynamicznie przełączać się między poziomami administracyjnymi.
Prezentacja strony
Strona to prosty interfejs do przeglądania granic PRG w przeglądarce. Po lewej mamy panel: wybór warstwy, wyszukiwarkę, checkboxy i przyciski "Pokaż wszystkie / Schowaj wszystkie". Po prawej - mapa Leaflet z podkładem OSM.
Proszę tylko nie mylić tutaj PRG z OSM - OSM jest tylko podkładką/tłem, granice idą z PRG, z plików na dysku. Backend wcale nie korzysta z OSM!
Kliknięcie w jednostkę na liście lub w mapę włącza/wyłącza jej granicę, która rysowana jest w losowym kolorze. Liczba widocznych obiektów aktualizuje się dynamicznie, a mapa dopasowuje widok do wyświetlanych wielokątów, więc można na raz wyświetlić nawet kilka tysięcy kształtów.
Zrzuty ekranu - Polska:
Województwa (16 wielokątów):
Powiaty (380 wielokątów):
Gminy (2476 wielokątów):
Jednostki (3212 wielokąty):
Obręby (53925 wielokąty):
Ich jest już za dużo, by pokazać wszystko...
Prezentacja API
Mamy już Flask do obsługi HTTP, i mamy już wczytane dane z Shapefile do osobnych warstw. Teraz można łatwo dodać obsługę prostych zapytań o informacje co znajduje się na danej szerokości i długości geograficznej. Dla przejrzystości zdecydowałem się na format zapytania GET, bo wtedy argumenty widać w samym URL. Opcjonalnie można podać nazwę warstwy do zbadania. Przykładowy endpoint:
Kod: Python
Kod demka
Do wglądu i uruchomienia:
Kod: Python
Zrzut ekranu z testów (ale bez obrębów) na Raspberry:
Podsumowanie
Dzięki temu prostemu serwerowi Flask i obsłudze Shapefile z GeoPandas udało mi się uruchomić własny reverse geocoding dla stref z PRG, a wszystko to bez limitów i z aktualnymi danymi. Całość dobrze nadaje się do różnorodnych eksperymentów i usług, o ile tylko starcza nam jedynie mapa naszego kraju i nie potrzebujemy dokładnych danych adresowych (ulic, itd), gdyż PRG ich nie posiada.
Dodatkowo system wzbogaciłem o prosty panel kontrolny/testowy działający w przeglądarce dzięki bibliotece Leaflet - w roli tła używam tam warstw z OpenStreetMap, co pobierane jest już bezpośrednio z Internetu, ale nie uważam tego za problem. Grunt, że właściwy reverse geocoding działa w pełni na Raspberry.
Teraz można tego użyć do własnych projektów, bez obaw o limity - darmowe API Nominatim do reverse geocoding ma limit 1 zapytania na sekundę i nie zezwala na masowe odpytywanie, a tu możemy badać mapę do woli, na ile nasz CPU pozwoli.
Czy korzystaliście z API map takich jak OSM lub podobnych, a jeśli tak, to do czego?
Fajne? Ranking DIY Pomogłem? Kup mi kawę.