2026-08-21 20:56:20 +02:00
2026-08-21 20:56:20 +02:00
2026-08-21 13:44:15 +02:00
2026-08-21 14:17:41 +02:00
2026-08-21 15:04:46 +02:00
2026-08-21 13:44:15 +02:00
2026-08-21 15:04:46 +02:00

LibreMaps

A lightweight, self-hosted map viewer built with Flask and Leaflet, powered entirely by free open data from OpenStreetMap (via the Nominatim geocoding API) and MapTiler street tiles.

Features

  • Interactive map — pan/zoom over MapTiler "Streets" tiles, centered on Amsterdam by default.
  • Search — type a place or POI to search. Results are first restricted to the area currently visible on the map; if nothing is found locally, it automatically falls back to searching the entire world.
  • Category shortcuts — quick chips for common places (Supermarkt, Café, Kroeg, Camping, Park, Restaurant, Vegan, Hotel, Parkeerplek).
  • Typed pins — each result is drawn as a colored circular badge whose emoji and color reflect the POI type (e.g. 🍴 orange for food, 🛏 purple for hotels, 🚆 blue for transport, 💊 red for pharmacies, 🌳 green for parks). A small permanent label shows the location name above each pin.
  • Place details card — click a result or a pin to see the name, type, address, opening hours, phone, website, email, and extra tags (cuisine, brand, wheelchair access, payment methods, etc.).
  • Actions — get directions (Google Maps), web search (Qwant), call, visit website, or email directly from the detail card.
  • Reverse geocoding — click anywhere on the map (when zoomed in enough) to look up what's at that spot.
  • "My location" button using browser geolocation.
  • Responsive layout — adapts to mobile screens.

Tech Stack

Layer Technology
Backend Python 3, Flask
Frontend Vanilla JS, Leaflet 1.9.4
Geocoding OpenStreetMap Nominatim
Tiles MapTiler Streets v4

Project Structure

openmaps/
├── app.py               # Flask app: /, /search, /geocode routes
├── templates/
│   └── index.html       # Single-page UI (map + all client logic)
├── sync.py              # Sync helper script
├── requirements.txt     # Python dependencies
├── Dockerfile           # Container image definition
├── docker-compose.yml   # Local container orchestration
├── .dockerignore
└── .gitignore

How It Works

Backend (app.py)

Three endpoints proxy requests to Nominatim while applying a simple rate limit (minimum 1 second between calls) and a descriptive User-Agent:

  • GET / — serves the single-page UI.
  • GET /search?q=...&viewbox=left,top,right,bottom — forwards a forward-geocoding query. When a valid viewbox is supplied, results are bounded to that region (bounded=1). Returns normalized JSON (coordinates, address, extratags, namedetails, importance, bounding box).
  • GET /geocode?lat=...&lon=... — reverse-geocodes a coordinate into a place record.

Frontend (templates/index.html)

  • Initializes a Leaflet map with MapTiler tiles.
  • Builds a viewbox from the current map bounds and sends it with each search so results stay local.
  • Renders results in a scrollable card and drops typed pins on the map.
  • Falls back to a worldwide search when the local search returns nothing.
  • Shows a detail card with contact info, opening hours, and action buttons.

Getting Started

Prerequisites

  • Python 3.x
  • (Optional) Docker / Docker Compose

Run locally

pip install -r requirements.txt
python app.py

Then open http://localhost:5000.

Run with Docker

docker compose up --build

Notes

  • The MapTiler tile layer uses an embedded API key in templates/index.html; replace it with your own key for production use.
  • Nominatim has usage policies — keep the built-in rate limiting in place and identify your application via the User-Agent.
Description
No description provided
Readme 189 KiB
Languages
HTML 88.6%
Python 10.3%
Dockerfile 1.1%