CanICannabis is an open-source web mapping application that visualizes legal and prohibited cannabis consumption zones in Baden Wurttemberg, Germany, based on the German Cannabis Act (Cannabisgesetz – CanG) § 5 distance regulations.
- Interactive map showing restricted and allowed consumption zones
- Distance-based calculations from schools (100m minimum distance)
- Address search functionality with geocoding
- Toggle between zone layers (restricted/allowed)
- Full open-source stack: OpenStreetMap + PostgreSQL/PostGIS + FastAPI + Leaflet
The project follows a classic full-stack web GIS architecture:
OpenStreetMap Data (Geofabrik)
↓
osm2pgsql Import
↓
PostgreSQL/PostGIS Database
↓
SQL + PostGIS Logic (red_small, blue_small zones)
↓
FastAPI Backend (/zones/red, /zones/blue endpoints)
↓
Frontend (Leaflet + JavaScript)
↓
Web Browser Map
- School locations (buffer 100m = red_small, forbidden)
- Youth centers (buffer 100m = restrictions)
- Public transit hubs (conditional rules)
| Component | Technology |
|---|---|
| Environment | OSGeoLive Linux Virtual Machine |
| Database | PostgreSQL 15 + PostGIS |
| Backend | Python 3 + FastAPI + psycopg2 |
| Frontend | HTML/CSS + JavaScript + Leaflet.js |
| Map Tiles | OpenStreetMap (via Leaflet) |
| Geocoding | Nominatim (OSM geocoding service) |
| Version Control | GitHub |
CanICannabis/
├── backend/
│ ├── api.py # FastAPI application
│ ├── requirements.txt # Python dependencies
│ └── sql/
│ ├── import_osm.sql # Import OSM data into PostGIS
│ └── create_zones.sql# Create red/blue zone tables
├── frontend/
│ ├── map.html # Web map interface
│ └── app.js # Frontend logic (requests + map)
├── docs/
│ ├── screenshots/ # UI screenshots
│ └── API_DOCS.md # API documentation
├── README.md # Project overview & setup
└── LICENSE # Project license
OSGeoLive VM or Linux system with:
- PostgreSQL 15 + PostGIS
- Python 3.8+
- Git
PostgreSQL should auto-start in OSGeoLive. Verify it's running:
sudo systemctl status postgresql
Create database:
sudo -u postgres createdb canicannabis
Import Baden Wurttemberg OSM data:
osm2pgsql -d canicannabis --slim -C 2048 Baden Wurttemberg.osm.pbf
Connect to database and run zone creation SQL:
sudo -u postgres psql canicannabis < sql/create_zones.sql
cd backend
pip install -r requirements.txt
Example requirements.txt:
fastapi
0.104.1uvicorn0.24.0
psycopg2-binary
2.9.9pydantic2.5.0
cd backend
uvicorn api:app --reload --port 8000
INFO: Uvicorn running on http://127.0.0.1:8000 INFO: Application startup complete
Open in browser:
- http://localhost:8000/zones/red – Returns red zone GeoJSON
- http://localhost:8000/zones/blue – Returns blue zone GeoJSON
Response format:
{
"type":"FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": {
"type": "MultiPolygon",
"coordinates": [...]
},
"properties": {
"restriction": "Cannabis consumption forbidden (100m from school)"
}
}
]
}
cd frontend
firefox map.html
Or navigate directly:
file:///path/to/canicannabis/frontend/map.html
- Map Display: Baden Wurttemberg centered, OpenStreetMap tiles
- Zone Layers:
- Red zones load automatically (forbidden zones)
- Blue zones toggle via "Show/Hide Zones" button
- Address Search:
- Type address in search box (e.g., "HKA Karlsruhe")
- Click "Search"
- Map zooms and places marker
- See which zones apply
- Popups: Click any polygon to see restriction details
- Open map.html
- Type "Schlossplatz Karlsruhe" in search box
- Click "Search"
- Map zooms to location
- Check if area is in red (forbidden) or blue (allowed) zones
- Click "Show/Hide Blue Zones" button
- Blue zones appear/disappear on map
- Compare allowed vs. forbidden areas
- Click on any polygon (red or blue)
- Popup shows restriction text
- Close popup by clicking X or clicking elsewhere
Description: Fetch all restricted cannabis consumption zones (100m from schools)
Response: GeoJSON FeatureCollection
Example:
curl http://localhost:8000/zones/red | jq
Description: Fetch allowed cannabis consumption zones (outside restricted areas)
Response: GeoJSON FeatureCollection
Example:
curl http://localhost:8000/zones/blue | jq
Both endpoints support optional filtering:
- limit: Maximum number of features to return
Edit sql/create_zones.sql to change distance buffers or restriction logic:
-- Example: Change school buffer from 100m to 150m
UPDATE red_small SET geom = ST_Buffer(school_point, 150)
WHERE zone_type = 'school';
Restart FastAPI after database changes: Stop: Ctrl+C
Restart: uvicorn api:app --reload --port 8000
-
Create new zone table in PostgreSQL
-
Add new endpoint in api.py: @app.get("/zones/other") def get_other_zones(): #Query database #Return GeoJSON
-
Load in frontend app.js:
fetch('http://localhost:8000/zones/other')
.then(r => r.json())
.then(data => L.geoJSON(data).addTo(map));
- Running on OSGeoLive
- Backend at http://localhost:8000
- Frontend via file:// protocol or HTTP server
For deployment to a server:
-
Environment Configuration: Use .env file for database credentials, API URLs
-
Docker Containers: Optional containerization for FastAPI + PostgreSQL
-
Reverse Proxy: Nginx/Apache for HTTPS
-
CI/CD Pipeline: GitHub Actions for automated testing and deployment
-
Monitoring: Logs and error tracking
- Backend starts without errors (uvicorn)
- /zones/red returns valid GeoJSON
- /zones/blue returns valid GeoJSON
- Frontend loads in browser
- Map displays correctly
- Red zones visible by default
- Blue zones toggle on/off
- Search finds addresses in Karlsruhe
- Clicking polygons shows popups
- Search result zooms to correct location
- Unit tests for FastAPI endpoints
- Integration tests for database queries
- Frontend tests with Selenium or Cypress
| Problem | Solution |
|---|---|
| Error: address already in use :8000 | Change port: uvicorn api:app --port 8001 |
| psycopg2 connection refused | Check PostgreSQL is running: sudo systemctl start postgresql |
| No module named 'fastapi' | Install dependencies: pip install -r requirements.txt |
| Column 1 | Column 2 |
|---|---|
| 404 in browser console | Backend not running; start with uvicorn |
| CORS error | Add CORS headers in FastAPI: from fastapi.middleware.cors import CORSMiddleware |
| Search not working | Check Nominatim internet connection |
To contribute to this project:
-
Fork the repository
-
Create a branch: git checkout -b feature/my-feature
-
Make changes and test locally
-
Commit: git commit -m "Add feature description"
-
Push: git push origin feature/my-feature
-
Open a Pull Request
- Sami – Project Lead, Backend Architecture
- Jomert – Backend Development, PostGIS Queries
- Mustafa – Frontend Development, Leaflet Integration
- Brenda – Data Management, Documentation
- German Cannabis Act (Cannabisgesetz – CanG), § 5: Distance regulations
- https://www.gesetze-im-internet.de/canag/
- Leaflet.js: https://leafletjs.com/
- FastAPI: https://fastapi.tiangolo.com/
- PostGIS: https://postgis.net/
- OpenStreetMap: https://www.openstreetmap.org/
- Nominatim: https://nominatim.openstreetmap.org/
- OSGeoLive: https://live.osgeo.org/
- GeoJSON Specification: https://datatracker.ietf.org/doc/html/rfc7946
This project is licensed under the MIT License. See LICENSE file for details.
- Core map functionality implemented
- Red and blue zone visualization
- Address search with Nominatim
- Interactive popups on zone clicks
- GitHub repository and README