A geographical exploration tool for the overland and vanlife community. WildSpotter acts as a "radar" that processes geographic, topographic, legal, and satellite data to probabilistically discover undiscovered overnight parking spots in the wild.
Unlike user-generated directories (Park4night, iOverlander) that lead to spot overcrowding, WildSpotter discovers new spots from raw geographic data — no community reviews needed.
Target region: Spain (initial).
Full specification: SPEC_V2.md
WildSpotter uses a backend-centric architecture. All heavy processing (geographic filtering, terrain analysis, legal cross-referencing, AI inference) runs in a Dockerized backend stack. The React Native app is a lightweight client that consumes pre-processed data via a JSON API.
┌──────────────────────────────────────────────────────────────┐
│ Docker Compose Stack │
│ │
│ ┌──────────┐ ┌──────────────┐ ┌───────────────────┐ │
│ │ n8n │───>│ AI/GIS │───>│ PostgreSQL │ │
│ │ (cron) │ │ Worker │ │ + PostGIS │ │
│ │ │ │ (Python) │ │ │ │
│ └──────────┘ └──────────────┘ └─────────┬─────────┘ │
│ │ │ │
│ │ Geofabrik .osc diffs │ │
│ v │ │
│ ┌──────────┐ ┌───────┴─────────┐ │
│ │ osm2pgsql│─────────────────────────>│ Fastify │ │
│ │ (import) │ │ (JSON API) │ │
│ └──────────┘ └───────┬─────────┘ │
│ │ │
└────────────────────────────────────────────────┼─────────────┘
│ HTTP/JSON
┌────────┴────────┐
│ React Native │
│ (Expo) Client │
└─────────────────┘
| Component | Technology | Role |
|---|---|---|
| Mobile Client | React Native / Expo (TypeScript) | UI, map rendering, API consumption, offline caching |
| Backend API | Fastify (TypeScript) | Serves processed spot data as JSON |
| Database | PostgreSQL + PostGIS | Spain OSM data, terrain/legal/AI results, spatial indexing |
| Orchestration | n8n | Scheduled data ingestion and processing pipelines |
| Processing Worker | Python | Terrain-RGB analysis, WMS legal checks, AI model inference |
WildSpotter runs a 5-stage processing pipeline on OSM data, executed server-side:
Radar → Topographer → Legal Judge → Satellite Eye → Context & Amenities
Queries the local PostGIS database (imported from OpenStreetMap via Geofabrik) to find:
- Dead-end dirt/gravel tracks (
noexit=yes) - Informal unpaved parkings near points of interest
- Clearings accessible by motor vehicle (excludes hiking/cycling paths)
Python worker evaluates terrain slope using Terrain-RGB tiles (AWS Terrarium, z15). Slope and elevation are stored for every spot — no spots are rejected. Users filter by max slope at query time via preferences.
Python worker cross-references candidates against Spanish government data using local PostGIS spatial queries (offline) and the Catastro REST API:
- Red Natura 2000 — EU environmental protection zones (local PostGIS, MITECO shapefiles)
- National Parks — strict protection boundaries (local PostGIS, MITECO shapefiles)
- Coastal Law zones — maritime-terrestrial public domain: DPMT, Servidumbre de Protección, Terrenos Incluidos; minus Núcleos Excluidos (local PostGIS, MITECO shapefiles)
- Cadastre — land classification via Catastro REST API (
Consulta_RCCOOR)
Legal Zones Map Overlay: Users can toggle a red semi-transparent overlay on the map showing all restriction areas. This uses pre-generated Mapbox Vector Tiles (.pbf files at z4–z10, ~74 MB on disk) served as static files by the Fastify API — zero PostGIS cost at runtime. Tiles are generated by workers/generate_legal_tiles.py, which creates a materialized view with coastline-clipped geometries (marine zones excluded) and includes the DPMT boundary line as a 20m buffer polygon. Regenerate tiles when MITECO shapefiles are updated.
The original WMS-based approach was replaced because MITECO endpoints had wrong layer names, returned XML instead of JSON, and the Coastal Law endpoint was dead — causing all legal checks to silently pass every spot. Local PostGIS queries run in ~1ms/spot vs ~1s/spot with WMS.
Fuente: «© Ministerio para la Transición Ecológica y el Reto Demográfico»
Python worker downloads satellite imagery (IGN Spain PNOA orthophotos, 25cm/pixel) and sends tiles to Claude Haiku Vision for multi-factor analysis. It evaluates 5 sub-scores: surface quality, vehicle access, open space, van presence, and obstruction absence, generating a rich contextual score rather than a simple binary classification.
Evaluates spatial surroundings using local PostGIS queries to determine road noise, privacy, scenic value, and access to nearby amenities (drinking water and dog-friendly places). Adjusts the overall context score based on spatial proximity.
Each spot receives a 0-100 composite score: Terrain (20%) + AI (25%) + Context (55%). Legal data is informational only — it does not gate scoring. All spots receive a score regardless of legal status. Users can toggle "hide restricted areas" at query time.
| Layer | Technology |
|---|---|
| App | React Native + Expo (TypeScript strict) |
| Maps | MapLibre GL |
| AI | Claude Haiku Vision API (server-side, Python worker) |
| State | Zustand |
| Backend API | Fastify (TypeScript) |
| Database | PostgreSQL + PostGIS |
| Orchestration | n8n (cron pipelines) |
| Workers | Python (terrain, legal, AI, scoring) |
| Data | Local OSM (Geofabrik), Terrain-RGB, Spanish WMS (MITECO, Catastro, IGN) |
- Docker Desktop (includes Docker Compose)
- Node.js 18+
- Expo CLI (
npm install -g expo-cli) - For iOS: Xcode + iOS Simulator runtime
- For Android: Android Studio + Android SDK
cp .env.example .env
# Edit .env with your values (database password, optional API keys)docker-compose up -d --buildThis starts all 4 services:
- PostgreSQL + PostGIS on port 5433 (external)
- Fastify API on port 8000
- Python worker on port 8001 (Flask API, runs terrain+legal+AI in parallel threads)
- n8n on port 5678 (orchestrator, triggers pipeline every 20 min)
Verify the stack is running:
docker-compose ps
curl http://localhost:8000/healthDownload the Spain OSM extract (~1.3 GB) and import it:
mkdir -p data
wget -O data/spain-latest.osm.pbf https://download.geofabrik.de/europe/spain-latest.osm.pbf
docker-compose exec worker osm2pgsql \
-d wildspotter -H db -U wildspotter -s \
/data/spain-latest.osm.pbfnpm install
# Web (recommended for development)
npx expo start --web
# iOS Simulator
npx expo start --ios
# Android Emulator
npx expo start --androidnpm testsrc/ # React Native app (thin client)
├── app/ # Expo Router screens (Map, Spots, Legal, Config)
├── components/ # UI components (map/, spots/, legal/, ui/)
├── services/api/ # Typed API client for Fastify backend
├── stores/ # Zustand state management
├── constants/ # Theme tokens, config
└── types/ # Shared TypeScript types
backend/ # Fastify API (TypeScript)
├── src/routes/ # GET /spots, GET /spots/:id, GET /health, GET /legal/tiles/:z/:x/:y.pbf
├── src/services/ # PostGIS query builders, static tile serving
└── Dockerfile
workers/ # Processing scripts (Python)
├── terrain.py # Slope/elevation analysis
├── legal.py # PostGIS legal checks + Catastro REST API
├── ai_vision_labeler.py # Claude Haiku multi-factor satellite analysis
├── scoring.py # Composite score calculation
├── generate_legal_tiles.py # Pre-generate MVT tiles for legal zone map overlay
└── Dockerfile
db/ # Database schema
├── init.sql # PostGIS extension + tables + indexes
data/ # OSM extracts, satellite tiles, legal tiles (gitignored)
models/ # ML model files (.keras, .onnx)
design/ # Design mockups (.pen + PNG exports)
docker-compose.yml # Full stack orchestration
- Model: Claude Haiku (
claude-haiku-4-5-20251001) via Anthropic API - Task: Multi-factor scene understanding for vanlife suitability
- Input: 256x256 RGB satellite tiles
- Output: 5 sub-scores (0-10) generating a composite
ai_score(0-100) andai_detailsJSONB - Factors Analyzed:
surface_quality(30%): Mineral/dirt/gravel suitabilityvehicle_access(20%): Visible tracks/roadsopen_space(20%): Room for vehiclesvan_presence(15%): Bonus for already parked vansobstruction_absence(15%): Free from dense trees/buildings
- Inference: Server-side python worker calling Anthropic API (
workers/ai_vision_labeler.py)
docker-compose up -d --build # Start/rebuild all services
docker-compose logs -f api # Follow API logs
docker-compose logs -f worker # Follow worker logs
docker-compose exec db psql -U wildspotter # Database shell
docker-compose down # Stop all services
docker-compose down -v # Stop and destroy volumes (reset DB)
# Generate legal zone map overlay tiles (~25s, ~64 MB)
docker-compose exec worker python generate_legal_tiles.py- Spain only — Legal layers (MITECO shapefiles, Catastro REST API) are Spain-specific
- Satellite imagery lag — Tiles may be months/years old
- AI accuracy — Vision models can occasionally hallucinate tracks or misinterpret shadows
- Slope estimation — Terrain-RGB resolution (~30m) misses micro-terrain
- Legal data freshness — MITECO shapefiles are point-in-time snapshots; re-import periodically for updates
Private project — not open source.