managarten/services/mana-geocoding/CLAUDE.md
Till JS a47a7bfdba feat(places): add self-hosted geocoding with Pelias (DACH)
New mana-geocoding service (port 3018) wraps a self-hosted Pelias
instance with LRU caching and OSM→PlaceCategory auto-mapping.
All geocoding queries stay within our infrastructure — no user
location data leaves the network.

Places module integration:
- Address autocomplete search in ListView (creates place with
  name, coords, address, category in one step)
- Address search + reverse geocoding button in DetailView
- Auto-fill address via reverse geocoding during tracking
- OSM category mapping (amenity:restaurant→food, shop:*→shopping, etc.)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 23:02:25 +02:00

4.1 KiB

mana-geocoding

Self-hosted geocoding service. Wraps a local Pelias instance (DACH region) with caching and automatic OSM → PlaceCategory mapping. All geocoding queries stay within our infrastructure — no user location data leaves the network.

Tech Stack

Layer Technology
Runtime Bun
Framework Hono
Geocoding Pelias (self-hosted, Elasticsearch-backed)
Data OpenStreetMap DACH extract (DE/AT/CH)
Caching In-memory LRU (5000 entries, 24h TTL)

Port: 3018

Quick Start

# 1. Start Pelias stack (first time: run setup.sh for data import)
cd services/mana-geocoding/pelias
docker compose up -d
# First time only:
chmod +x setup.sh && ./setup.sh

# 2. Start the Hono wrapper
cd services/mana-geocoding
bun run dev

API Endpoints

All endpoints are public (no auth required) — the service is internal-only, not exposed to the internet.

Method Path Description
GET /api/v1/geocode/search?q=... Forward geocoding / autocomplete
GET /api/v1/geocode/reverse?lat=...&lon=... Reverse geocoding
GET /api/v1/geocode/stats Cache statistics
GET /health Health check

Search params

Param Required Description
q yes Search query (min 2 chars)
limit no Max results (default 5, max 20)
lang no Language (default de)
focus.lat no Bias results towards this latitude
focus.lon no Bias results towards this longitude

Reverse params

Param Required Description
lat yes Latitude
lon yes Longitude
lang no Language (default de)

Response format

{
  "results": [
    {
      "label": "Münster Café, Münsterplatz 3, 78462 Konstanz",
      "name": "Münster Café",
      "latitude": 47.663,
      "longitude": 9.175,
      "address": {
        "street": "Münsterplatz",
        "houseNumber": "3",
        "postalCode": "78462",
        "city": "Konstanz",
        "country": "Germany"
      },
      "category": "food",
      "osmCategory": "amenity",
      "osmType": "cafe",
      "confidence": 0.95
    }
  ]
}

Category Mapping

The service maps OSM tags to our 7 PlaceCategories:

PlaceCategory OSM examples
home building:residential, building:house, building:apartments
work amenity:school, amenity:university, office:*, building:commercial
food amenity:restaurant, amenity:cafe, shop:bakery, shop:supermarket
shopping shop:*, amenity:marketplace
transit railway:station, highway:bus_stop, amenity:parking, aeroway:*
leisure tourism:, leisure:park, amenity:cinema, sport:
other Everything else

Architecture

Client (Places module)
  → mana-geocoding (Hono, port 3018)
    → LRU cache check
    → Pelias API (port 4000)
      → Elasticsearch (port 9200)

Configuration

PORT=3018
PELIAS_API_URL=http://localhost:4000/v1
CORS_ORIGINS=http://localhost:5173,https://mana.how
CACHE_MAX_ENTRIES=5000
CACHE_TTL_MS=86400000

Pelias Infrastructure

The Pelias stack runs as a separate docker-compose in pelias/:

  • elasticsearch — Index storage (~500MB for DACH)
  • api — HTTP API (port 4000)
  • libpostal — Address parsing (port 4400)
  • Import containers — Run once for initial data load, then stop

RAM usage (running): ~1.5GB (elasticsearch 512MB + api + libpostal)

Code Layout

src/
├── index.ts              # Bootstrap
├── app.ts                # Hono app factory
├── config.ts             # Environment config
├── routes/
│   ├── geocode.ts        # Forward + reverse endpoints with caching
│   └── health.ts
└── lib/
    ├── cache.ts          # LRU cache with TTL
    └── category-map.ts   # OSM → PlaceCategory mapping
pelias/
├── docker-compose.yml    # Pelias stack
├── pelias.json           # Pelias config (DACH region)
└── setup.sh              # Initial data import script