logo elektroda
logo elektroda
X
logo elektroda

Custom hosting and API of official maps on Raspberry Pi - State Register of Borders - Python

p.kaczmarek2 1155 3

TL;DR

  • A Raspberry Pi 4 runs a local reverse-geocoding service built from official PRG boundary maps instead of OpenStreetMap/Nominatim.
  • Python 3, Flask, GeoPandas, and Shapely load ESRI Shapefiles into separate GeoDataFrames, while Leaflet.js provides a browser map with OSM as a background.
  • The PRG data include annual boundary updates and historical downloads from 2004-2024, and the smaller precinct layer alone takes about 300 MB.
  • The server can query provinces, counties, municipalities, and units by latitude and longitude, and several thousand polygons can be displayed interactively.
  • The system does not provide accurate address details such as streets, and it avoids the Nominatim 1 query per second limit by running locally.
Generated by the language model.
ADVERTISEMENT
Treść została przetłumaczona polish » english Zobacz oryginalną wersję tematu
📢 Listen (AI):
  • Map of Poland with borders of 16 voivodeships and a control panel on the left
    Some time ago I presented how reverse geocoding (converting geographic coordinates into address data) can be run locally based on the Nominatim service from OpenStreetMap. Here I will show a similar system, this time realised manually in Python based on ready-made libraries and ready-made official maps of administrative units provided by the State Boundary Office (PRG). These include the boundaries of the state, provinces, counties, municipalities and units and registration precincts. The whole thing will be light enough to run the server on a Raspberry Pi 4.

    Previous topic in the series:
    How to run OpenStreetMap locally? Reverse geocoding without limits on your computer

    State Register of Boundaries
    The State Register of Borders (PRG) is the official database describing the boundaries and administrative division of the country, used by other spatial information systems. It contains data on municipalities, counties, provinces, administrative units and addresses along with their location and basic descriptions (e.g. name, TERYT code).
    Data on boundaries and areas of administrative units are updated once a year according to the status as at 1 January on the basis of legal acts or changes in the land register. Information on addresses is supplemented on an ongoing basis according to changes made by the municipal offices.
    The source data for the experiment can be downloaded on the Geoportal . There we have a choice of data for each year separately - also the historical ones, 2004 - 2024.

    Format of data provided
    PRG data are provided in ESRI Shapefile format, i.e. for a single layer you get a set of several files (including .shp, .dbf, .shx, .prj) that together describe the geometry and attributes of objects.
    These files contain vector data, mainly polygons of administrative boundaries.
    Shapefile packages for voivodeships and counties in Windows file explorer
    The files are not that large at all and have the potential to run even on a single-board computer. The situation is further simplified when we dispense with the smallest administrative unit - the precincts, something that is unlikely to be of practical use to us. The precincts take up 300 MB and the next layer (units) only 100 MB. Units are, to simplify, already villages or thereabouts, although towns often consist of several units. I will show this later in the screenshots.
    Screenshot of PRG folder with 2024 Shapefile administrative boundary data


    Technologies used
    For the project I used Python 3 with the Flask libraries (HTTP backend) and GeoPandas and Shapely to handle the GIS data in ESRI Shapefile format.
    The visual part (frontend) was realised in the browser using Leaflet.js with an OpenStreetMap backend.

    Shapefile service and custom server on Raspberry Pi
    The simplest and also most cross-platform method of handling ESRI Shapefiles is the GeoPandas library available in Python. It is an overlay for Pandas, extended to support geometry (Shapely) and GIS formats.
    GeoPandas runs easily on Linux (also ARM - Raspberry Pi), Windows and macOS. In practice, all you need is Python 3.x and a few system dependencies (gdal, proj), which on the Raspberry Pi are available in repositories.
    The command that installs the dependencies on the Raspberry Pi 4:
    
    apt update
    apt install -y \
      python3-pip \
      python3-dev \
      python3-geopandas \
      gdal-bin \
      libgdal-dev \
      libgeos-dev \
      libproj-dev
    

    Example screenshot of the installation:
    Installing GIS and Python packages on Raspberry Pi 4 in a terminal window
    The Flask framework was used to set up the server itself - it allows you to create a simple HTTP server and handle incoming requests and queries.


    Handling PRG data in Python
    Each PRG layer (countries, provinces, counties, municipalities, etc.) is loaded into a separate GeoDataFrame object.
    In the example, each geometry is assigned a simple numerical uid, which is later used in the web interface.
    Code: Python
    Log in, to see the code

    All layers are stored in the LAYERS dictionary, allowing dynamic switching between administrative levels.

    Page presentation
    The page is a simple interface for viewing PRG boundaries in a browser. On the left we have a panel: layer selection, search engine, checkboxes and "Show all / Hide all" buttons. On the right, the Leaflet map with OSM underlay.
    Just please don't confuse PRG with OSM here - OSM is just a pad/background, the borders go from PRG, from files on disk. The backend does not use OSM at all!
    Clicking on an entity in the list or on the map turns its border on/off, which is drawn in a random colour. The number of visible objects updates dynamically and the map adapts the view to the polygons displayed, so up to several thousand shapes can be displayed at once.

    Screenshots - Poland:
    Map interface showing Poland's border with control panel on the left
    Provinces (16 polygons):
    Web interface showing a map of Poland with boundaries of 16 voivodeships displayed Counties (380 polygons)
    Counties (380 polygons):
    Map interface with Poland's county borders, 380 colored polygons over OSM background
    Municipalities (2476 polygons):
    Poland map interface with 2476 municipality boundaries in random colors
    Units (3212 polygons):
    Map of Polish cadastral units with colorful administrative boundary outlines Units
    Circles (53925 polygons):
    Map of Poland with colorful boundary polygons and a control panel on the left
    There are already too many of these to show everything....
    Browser error message indicating Out of Memory

    Presentation of the API
    We already have Flask to handle HTTP, and we have already loaded data from Shapefile into separate layers. Now it's easy to add support for simple queries for information about what's at a given latitude and longitude. For the sake of clarity, I have opted for a GET query format, because then the arguments can be seen in the URL itself. Optionally, you can provide the name of the layer to be queried. Example endpoint:
    Code: Python
    Log in, to see the code

    JSON response from local reverse geocoding for Warsaw coordinates
    Browser screenshot showing reverse geocoding API response for Mazowieckie voivodeship. Browser interface showing reverse geocoding response for Warsaw coordinates Reverse geocoding query result from local Flask server using cadastral units layer

    Demonstration code
    For review and commissioning:
    Code: Python
    Log in, to see the code

    Screenshot of the tests (but without the hoops) on the Raspberry:
    System monitor htop on Raspberry Pi with PRG Python script running


    Summary
    With this simple Flask server and Shapefile support from GeoPandas, I was able to run my own reverse geocoding for zones from PRG, all with no limits and up-to-date data. The whole thing lends itself well to a variety of experiments and services, as long as all you need is a map of our country and you don't need accurate address data (streets, etc.), as PRG doesn't have it.
    In addition, I enriched the system with a simple control/test panel running in the browser thanks to the Leaflet library - I use layers from OpenStreetMap as a background there, which is already downloaded directly from the Internet, but I don't consider it a problem. The main thing is that the actual reverse geocoding works just fine on the Raspberry.
    Now you can use this for your own projects, without worrying about limits - the free Nominatim API for reverse geocoding has a limit of 1 query per second and doesn't allow for bulk querying, and here we can explore the map to our heart's content, as much as our CPU will allow.
    Have you used a map API such as OSM or similar, and if so, for what?

    Cool? Ranking DIY
    Helpful post? Buy me a coffee.
    About Author
    p.kaczmarek2
    Moderator Smart Home
    Offline 
    p.kaczmarek2 wrote 14439 posts with rating 12405, helped 650 times. Been with us since 2014 year.
  • ADVERTISEMENT
  • #2 21794292
    krzbor
    Level 29  
    Posts: 1732
    Help: 40
    Rate: 1045
    p.kaczmarek2 wrote:
    administrative precincts

    Minor correction - it is supposed to be administrative precincts. For those who are "not in geodesy" - a registration unit is mostly equal to a municipality, but for urban-rural municipalities we have one municipality and 2 registration units: urban and rural.
  • ADVERTISEMENT
  • #3 21795439
    gulson
    System Administrator
    Posts: 29250
    Help: 148
    Rate: 5985
    Is it somehow possible to get development plans out of this? Soon a huge number of plots of land will be unbuildable, the Wuzetts are coming to an end.
  • #4 21795824
    krzbor
    Level 29  
    Posts: 1732
    Help: 40
    Rate: 1045
    gulson wrote:
    It is somehow possible to extract land use plans from this?

    It is not possible - PRG is only the boundaries of provinces, counties, municipalities and registration units and registration precincts. The data is aggregated by GUGiK (Central Office of Geodesy and Cartography) on the basis of county data. Spatial planning is the domain of municipalities, as they are responsible for the plans.
📢 Listen (AI):

FAQ

TL;DR: Host Poland’s official PRG borders locally on a Raspberry Pi 4 with Flask+GeoPandas; the municipalities layer has 2,476 polygons, and “The backend does not use OSM at all!” Build a /reverse API for fast lookups. [Elektroda, p.kaczmarek2, post #21793642] Why it matters: You get unlimited, on‑prem reverse geocoding of official administrative areas without third‑party rate limits—ideal for edge deployments and bulk mapping.

Quick Facts

What is the State Register of Borders (PRG)?

PRG is Poland’s official database of administrative boundaries and divisions. It covers state, provinces, counties, municipalities, registration units, and precincts. It includes names and TERYT codes but not street‑level address geometry. It’s published for each year, including historical sets. [Elektroda, p.kaczmarek2, post #21793642]

Which PRG layers are available and how many polygons do they contain?

Available layers include provinces (16), counties (380), municipalities (2,476), units (3,212), and precincts (53,925). You can browse and toggle any combination in the provided Leaflet UI. Large layers render, but displaying all precincts at once is impractical. [Elektroda, p.kaczmarek2, post #21793642]

Can I run this on a Raspberry Pi 4 and what do I install?

Yes. Install Python 3, Flask, GeoPandas, and system deps (gdal, proj, geos). Example on Pi: apt update; apt install -y python3-pip python3-dev python3-geopandas gdal-bin libgdal-dev libgeos-dev libproj-dev. The setup handles shapefiles reliably on ARM. [Elektroda, p.kaczmarek2, post #21793642]

How do I spin up the Flask + GeoPandas reverse-geocoding API?

  1. Load PRG shapefiles into GeoDataFrames and add a uid column. 2. Expose /reverse in Flask; accept lat,lng and optional layer key; test with GET queries. 3. Optionally add /search, /polygon, and map click (/pick) for a Leaflet UI. [Elektroda, p.kaczmarek2, post #21793642]

How does the /reverse endpoint work?

Send /reverse?lat=..&lng=.. and optionally m=layer. The server builds a Point(lng,lat) and returns the matching feature’s id and name per layer, or across all layers if m is omitted. It uses geometry.contains for point-in-polygon. [Elektroda, p.kaczmarek2, post #21793642]

Does this rely on OpenStreetMap data or its limits?

No. The API serves PRG shapefiles from local storage. Leaflet uses OSM tiles only as a visual background. As the author notes, “The backend does not use OSM at all!” Your geocoding runs entirely offline. [Elektroda, p.kaczmarek2, post #21793642]

Can I display thousands of polygons at once? Any performance notes?

Yes. The app can toggle and render several thousand shapes and auto‑fit the view. Start with municipalities or units; avoid loading all 53,925 precincts at once. Consider progressive toggling and delayed map fits to keep the UI smooth. [Elektroda, p.kaczmarek2, post #21793642]

Can I extract local zoning/development plans (MPZP) from PRG?

No. PRG aggregates administrative boundaries only. Spatial planning data is managed by municipalities. You won’t get MPZP from PRG exports. “Spatial planning is the domain of municipalities.” [Elektroda, krzbor, post #21795824]

What’s the correct terminology: administrative units vs. precincts?

A registration unit often aligns with a municipality; urban‑rural municipalities can have one municipality but two units (urban and rural). The term “administrative precincts” refers to registration precincts in this context. [Elektroda, krzbor, post #21794292]

Can users pick a polygon by clicking the map?

Yes. The example adds a /pick endpoint. A Leaflet map click sends lat/lon, finds the containing feature, and toggles its visibility in the UI. This speeds ad‑hoc inspection and manual testing. [Elektroda, p.kaczmarek2, post #21793642]

How does text search work in the UI?

Use /search with q=term and exact=0/1. The server lowercases names and returns ids+names per layer. The side panel lists results with checkboxes to toggle polygons and keep a live visible count. [Elektroda, p.kaczmarek2, post #21793642]

Edge case: Why do some coordinates return empty near borders?

The code uses geometry.contains. Points exactly on a boundary are not considered inside and may return no match. Switch to a relation like covers if you need boundary inclusions. “contains” is deliberate here for clarity. [Elektroda, p.kaczmarek2, post #21793642]

How current is the PRG dataset used here?

Boundaries are updated once per year to reflect the status on 1 January. Historical annual sets are available from 2004–2024. Address attributes are supplemented continuously by municipalities. [Elektroda, p.kaczmarek2, post #21793642]

Why build this instead of using public Nominatim?

Public Nominatim enforces about 1 request per second and discourages bulk use. A local PRG server removes rate limits for administrative reverse geocoding, enabling batch analyses and responsive dashboards on your own hardware. [Elektroda, p.kaczmarek2, post #21793642]

What is Tuya?

Tuya is an IoT platform that provides device modules, cloud services, and mobile apps for smart‑home and commercial devices, enabling fast OEM development. [“Tuya IoT Platform Overview”]

What is Arduino Nano?

Arduino Nano is a compact Arduino board family for embedded projects, offering USB programming, multiple GPIO, and 5V or 3.3V variants suited to breadboards. [“Arduino Nano — Tech Specs”]

What is OpenBeken?

OpenBeken is an open‑source firmware for certain IoT devices that replaces vendor stacks, enabling local control, MQTT, and customization without cloud dependency. [“OpenBeken Documentation”]

What is CAN bus?

CAN bus (Controller Area Network) is a robust multi‑master serial bus for real‑time messaging between ECUs, standardized in ISO 11898 and widely used in vehicles and industry. [“CAN bus — Controller Area Network (ISO 11898) Overview”]
Generated by the language model.
ADVERTISEMENT