We are using Docker to manage all the geographical data and perform calculations efficiently.
- Install Docker Desktop
- Create a folder for your project.
- Clone the repo in the created folder:
git clone https://github.com/causape/opensourceGIS_docker.git- Modify the
docker-compose.ymlfile if needed. - Open a terminal or command prompt and run:
docker compose upThis will create all the containers defined in your Docker Compose file. You can monitor them in Docker Desktop.
| Command | Description |
|---|---|
docker compose up |
Create and start the containers using the .yml file |
docker compose down |
Stop all running containers (stop before shutting down your computer) |
docker compose rm -f |
Delete containers if something is broken. Does not delete data volumes |
docker compose down -v |
Delete all containers and volumes |
All credentials and ports are configured in docker-compose.yml.
- Access via browser: http://localhost:5050/
- Use credentials from your
docker-compose.yml. - Register New Server:
- Right button Server:
- Register new server with the following parameters:
- Right button Server:
| Field | Value |
|---|---|
Name |
Choose the name you prefer |
Host name / address |
localhost |
Port |
5432 |
Username |
gis |
Database |
gis |
Password |
password |
-
Create a new PostgreSQL connection.
-
Use the following credentials:
- Name: (choose any)
- Host:
localhost - Port:
5433(check your yml file) - Database:
gis - User:
gis - Password:
password
psql -h localhost -p 5432 -U gis -d gis- Access via browser: http://localhost:8080/
- To access the GeoServer account, you need to use the username and password configured in the .yml
- Use the following credentials:
- e.g. The credentials will be found in the following line: "GEOSERVER_ADMIN_PASSWORD: admin_geoserver # GeoServer admin password (user: admin)"
This repository contains a containerized GIS environment designed for automated OpenStreetMap (OSM) data ingestion, incremental updates, and custom spatial analysis. The stack transforms raw OSM data into "Service Islands" with aggregated infrastructure data using a Python-based analytical engine.
| Service | Image | Purpose | Persistence & Access |
|---|---|---|---|
| postgis_db | postgis/postgis:15-3.3 |
Core PostGIS database. | Port: 5431 |
| geoserver_app | kartoza/geoserver |
OGC Map Server (WFS/WMS). | http://localhost:8080 (admin / admin_geoserver) |
| pgadmin_app | dpage/pgadmin4 |
Web-based Database UI. | http://localhost:5050 |
| osm2pgsql_importer | overv/tile-server |
Initial bootstrap loader. | Runs once (SQL Dump > PBF). |
| osm_updater | Custom Python/Alpine |
Analytical engine & updater. | Continuous 1-hour cycle. |
When the stack starts, import_osm.sh follows a priority-based bootstrap:
- Fast Import (SQL Dump): If
/data/filtered_osm_data.sql.gzexists, it restores the database directly for immediate deployment.
If no dump is found, the system downloads the latest Germany PBF and executes osm2pgsql in Flex Output mode using the styles/osm.lua schema.
How the Lua engine processes the Germany dataset:
Instead of importing the entire OpenStreetMap database (which would be hundreds of gigabytes), the osm.lua script acts as a high-performance filter that scans every node and way to build a specialized schema:
- Rule-Based Filtering: The engine inspects specific OSM tags (
amenity,leisure,highway,landuse) to determine which features are relevant for urban analysis. - Infrastructure Classification:
- Education: Filters nodes and polygons tagged as
school,kindergarten,childcare, orsocial_facility, splitting them into point (education_poi) and area (education_area) tables. - Leisure & Recreation: Isolates
playground,pitch,sports_centre, andtrackfeatures into dedicated leisure tables. - Public Transport: Specifically extracts
tram_stationnodes for transport accessibility analysis. - Connectivity: Filters
highway=pedestrianways to identify walkable zones.
- Education: Filters nodes and polygons tagged as
- On-the-fly Projection: All geometries are automatically projected to EPSG:3857 (Web Mercator) during the import process to ensure they are ready for high-speed spatial calculations in PostGIS.
This service runs a continuous 60-minute loop to keep data current:
- Apply Diffs: Scans
./data/diffs/for.osc.gzfiles and applies them viaosm2pgsql --appendto update source tables. - Trigger
app.py: Executes the Python analysis engine to recalculate spatial buffers. - Generate Dump: Generates a fresh
.sql.gzdump for future fast-start deployments. - Idle Phase: Sleeps for 1 hour before the next check.
The app.py script is the core analytical engine, transforming raw features into structured "Service Islands".
The system identifies infrastructure (schools, tram stops, playgrounds) and generates a 100-meter proximity buffer around each:
- Coordinate Transformation: Data is transformed from
EPSG:3857toEPSG:4326for consistent geography-based buffering. - Deduplication: A
UNIQUEconstraint onoriginal_osm_idprevents duplicate geometries even if an update process restarts.
To handle large-scale datasets like the whole of Germany without exceeding RAM limits:
- Grid Creation: The map is divided into a virtual grid of 0.005-degree squares.
- Clustering: Overlapping buffers are grouped within these cells using
ST_ClusterIntersecting.
When buffers overlap, they dissolve into a single "Service Island" that absorbs data from its constituent parts:
- Element Count: Calculates the total number of services within that specific island.
- Category Aggregation: Collects a distinct list of all categories present (e.g., "education, transport").
- Detailed Information String: Uses
string_aggto list every sub-type and facility name (e.g.,school: Grundschule Haidmühle | tram_station: Unknown). - Spatial Union: Geometries are dissolved using
ST_UnaryUnionwithST_SnapToGridfor clean multipolygons.
To streamline operations and visualization, the project includes a custom Node.js Dashboard that unifies DevOps and GIS workflows in a single interface. This allows for real-time monitoring of the Docker stack while visually inspecting the generated "Service Islands."
Unlike traditional WMS (server-side raster rendering), this dashboard consumes Vector Tiles (MVT) directly from GeoServer via GeoWebCache (application/vnd.mapbox-vector-tile).
-
Client-Side Rendering: Data is styled dynamically in the browser using MapLibre GL JS, allowing for smooth zooming and rotation without pixelation.
-
Dynamic Styling: Colors and categorizations are applied in real-time based on the sub_type attribute (e.g., 🟧 Education, 🟩 Leisure, 🟦 Social), eliminating the need to regenerate server-side styles (SLD).
-
Real-time Filtering: Users can toggle specific amenity categories (e.g., "Show only Schools and Playgrounds") instantly without reloading data, using MapLibre's setFilter capabilities.
-
Smart Selection Logic (QGIS-like Behavior): To handle overlapping service areas, the application implements a custom sorting algorithm using Turf.js: * 1. Spatial Verification: Verifies strict containment using turf.booleanPointInPolygon. * 2. Area Sorting: Calculates geodesic area (turf.area) and prioritizes the smallest feature first. This ensures that clicking on a small park inside a larger school zone selects the park preciseley, mimicking desktop GIS UX.
- Live Docker Logs: Integrates dockerode and socket.io to stream live logs from the PostGIS container directly to an embedded Xterm.js terminal in the browser. This allows for immediate verification of ETL processes and database triggers without context-switching.
-
WFS Data Retrieval: While visualization uses lightweight Vector Tiles, clicking a feature triggers a precise WFS GetFeature request to fetch comprehensive attribute data.
-
Automated UI Parsing: A custom side panel parses complex PostgreSQL arrays (DETAILED_INFO), automatically mapping keywords to visual icons (e.g., 🎓 for schools, 🧸 for kindergartens) for rapid readability.
-
Geocoding: Integrated Nominatim search for quick navigation to specific cities or neighborhoods.
-
Frontend: HTML5, CSS3, MapLibre GL JS.
-
Backend: Node.js, Express, Socket.io, Dockerode.
-
Geospatial Analysis: Turf.js (Client-side), PostGIS (Server-side).
-
Protocols: MVT (Vector Tiles), WFS (Web Feature Service), WSS (WebSockets).
The dashboard runs as a local Node.js service connecting to the Docker socket.
- Navigate to the dashboard folder:
Bash
cd Dashboard
Install dependencies: This installs express (web server), socket.io (real-time logs), dockerode (Docker API), and cors.
Bash
npm install
Start the server:
Bash
node server.js
Access the Interface: Open your browser and navigate to: http://localhost:3000
Beyond standard mapping, this application implements advanced logic to ensure strict compliance with the complexities of the German Cannabis Act (CanG), specifically regarding time-dependent restrictions.
Unlike schools or playgrounds (which are restricted 24/7), Pedestrian Zones (Fußgängerzonen) have a unique legal status: cannabis consumption is only prohibited between 07:00 and 20:00.
- Automatic Layer Control: A global clock monitors the system time every second.
- Day Mode (07:00 - 20:00): The system activates the Magenta layer for pedestrian zones, marking them as restricted areas.
- Night Mode (20:00 - 07:00): The system automatically filters out and hides the pedestrian layer from the map, visually indicating that the restriction is lifted.
- Simulation Mode: A developer tool (Time Slider) allows users to manually shift the "App Time" to test the map's behavior and layer transitions between day and night without waiting for real-time changes.
The app uses the HTML5 Geolocation API combined with WFS (Web Feature Service) queries to protect the user in real-time. The logic goes beyond simple intersection checks; it implements a Decision Tree Algorithm to determine the safety status:
- GPS Tracking: The app watches the user's coordinate
navigator.geolocation.watchPosition
- Spatial Query: It queries GeoServer to see if the user's location intersects with any buffered polygon.
- Context-Aware Status:
- Scenario A (Fixed Restriction): If the user is inside a School or Playground buffer → 🔴 NO SMOKING (Always Prohibited)
- Scenario B (Conditional Restriction): * If the user is inside a Pedestrian Zone:Is it Daytime? → 🔴 NO SMOKING (Restricted). * Is it Nighttime? → 🟢 SMOKING PERMITTED (The app ignores the polygon intersection based on the current time).
- RAM Tuning:
app.pyuseswork_mem = '1GB'for large joins. - Flex Schema: Filters specific OSM tags via
styles/osm.luafor Education, Leisure, Transport, and Landuse. - Slim Mode:
osm2pgsqluses disk-based intermediate storage for large datasets.
- Prerequisites: Install Docker/Compose. Ensure all
.shfiles use Unix (LF) line endings. - Configuration: Create a
.envfile with credentials matchingapp.py. - Deployment:
docker compose up -d