Case study · 02 / 03

A parcel outline and general-plan zones appear beside the Polish analysis completeness panel.

System Analizy Uwarunkowań Działki — case study

Spatial / Investment Parcel Analysis Platform

My role

Sole contributor to the repository.

Repository

Public

Shown as

Screenshots and source

Canonical URL

igorkadziela.dev/projects/parcel-analysis

01

Project overview

A web application that analyses the planning and investment context of a Polish land parcel. It combines GIS data and spatial-planning datasets, shows the result on a map and produces a PDF report with a verifiable audit package.

Source
Public repository
02

Problem

Assessing a plot of land in Poland means collecting scattered public data (local and general plans, flood risk, protected areas, terrain, roads and neighbouring buildings) from separate registers, each with its own coverage and freshness. The dangerous mistake is reading a missing answer as “no restriction”. The platform automates the collection and keeps explicit what was checked, what was not, and where each value came from.

03

Product and users

For: anyone assessing the investment potential of a parcel in Poland. What the application lets them do:

  • Look up a parcel by its identifier or by address
  • See planning zones (POG), local plans (MPZP) and other layers on a map
  • Read a status for every section: checked, partial or no coverage
  • Download a PDF report and an audit package with a SHA-256 manifest
  • Verify the package independently with a short Python script
04

My contribution

GitHub lists a single contributor for this repository: me. Backend, frontend, data imports, tests and documentation are my own work.

Backend
FastAPI service split into domain modules for planning, imports, reports and audit export, with Alembic migrations.
Frontend and map
Next.js and React application with a MapLibre map, an object inspector and the analysis result panel.
Data and imports
PostGIS schema and importers for planning acts (POG) and BDOT10k releases, parallel, with progress and resume after interruption.
Quality and delivery
CI gates for backend and frontend, locked and hashed dependencies, and an evaluation against a reference corpus of 30 parcels.
05

Architecture

A FastAPI backend with domain modules reads planning data from PostGIS and calls live public services for the rest. A Next.js application renders the map and results. Importers load planning acts and BDOT10k releases into PostGIS ahead of time.

Browser
Next.js 16 · React 19
MapLibre map · result panel

Next.js 16
MapLibre map
FastAPI backend
Python · domain modules
Analysis · reports · audit export

Python · FastAPI
Analysis · reports
Public services
Live GIS and planning services
Parcel lookup · flood · terrain

Live GIS and
planning services
PostgreSQL + PostGISPostGIS
Planning zones · vector tiles
Analysis snapshots

PostgreSQL 16
Zones · snapshots
Importers
POG acts · BDOT10k releases
Parallel · resumable

POG · BDOT10k
Resumable
Arrows: documented calls between components. Schematic only.Arrows: documented calls. Schematic only.

Planning acts are read from local imports, so a municipality that was never imported shows as “no coverage” rather than as an empty answer. Live public services supply the rest, and their answers are cached for a short time when the result is only partial.

06

Key engineering challenges

  1. Honest status when data is missing

    Coverage differs by municipality and source. Sections report checked, partial or no coverage, and a completeness bar shows how much of the scope was actually verified.

  2. Importing national planning data

    Planning acts come from a public register. Imports run in parallel, report progress with an estimate and resume after an interruption.

  3. Reading planning documents

    Local-plan texts are unstructured. A deterministic engine with calibrated confidence extracts values; an optional LLM path only proposes candidates that need a verified quote.

  4. Reproducible output

    Reports come from immutable snapshots, and the audit ZIP carries a manifest with SHA-256 hashes that a script using only Python 3 can verify.

07

Technical decisions and trade-offs

DecisionWhyTrade-off
Missing data is never shown as the absence of a restrictionFor an investor, an unchecked source and a confirmed lack of restrictions mean opposite things.Most analyses report “partial” until the relevant data has been imported.
Docker Compose as the default way to runOne command starts the backend, the frontend and PostGIS, with no local PostgreSQL to install.It runs on a single machine. There is no hosted instance.
LLM extraction off by default, output always labelledA model reading is a candidate: it needs a verified quote and is marked as an automatic reading that requires confirmation.Evaluation against live model calls is a manual step outside CI, because it costs money.
Locked, hashed dependencies and pinned base imagesThe production image is reproducible and contains no test tooling.Updates are done by hand, with audits catching vulnerable versions.
08

Stack

Backend

  • Python
  • FastAPI
  • SQLAlchemy
  • GeoAlchemy
  • Alembic

Frontend & maps

  • Next.js 16
  • React 19
  • TypeScript
  • MapLibre

Spatial data & infrastructure

  • PostgreSQL 16
  • PostGIS 3.4
  • GIS / spatial data processing
  • Docker Compose
09

Testing and quality

Backend and frontend gates run in CI and in containers, so a clean machine needs only Docker. The analysis is informational and the README says it is not an official document.

Coverage gates
Backend tests run with a minimum of 80% coverage (pytest), alongside ruff, mypy with a baseline and pip-audit.
Kinds of tests
pytest for the backend, Vitest and React Testing Library for the frontend, plus a weekly live smoke run against the real services.
Evidence limits
All 30 reference parcels return a partial status until planning data is imported. MPZP extraction with a language model is experimental.
10

Screenshots

Pinned screenshots from a local run of the project. It is not deployed, so there is no live demo.

A parcel outline and general-plan zones appear beside the Polish analysis completeness panel.
POG is imported; ISOK and municipal MPZP discovery were unavailable. Analysis status: partial.
A selected general-plan zone appears on the vector map with planning parameters in the inspector.
The inspector displays the selected POG zone and its planning parameters.
Expanded completeness details show checked sections, partial results and missing coverage with explicit labels.
Section statuses distinguish missing coverage from a verified absence of restrictions.
The map panel explains that the general-plan layer is unavailable because no plans have been imported.
Before import, the POG panel explicitly reports that no general plans have been imported.
The first two complete pages of the downloaded PDF report show parcel details and the analysis summary.
The downloaded PDF report v2 preserves the parcel details and completeness summary.
The audit download text and its SHA-256 appear beside the unmodified output of the Python verifier.
The real verifier confirms 10 files and matches the package SHA-256 shown by the application.
11

Source code