Django Integration¶
Yoro Maps includes a full Django integration for serving map tiles and routes from a web application.
Install¶
Configuration¶
1. Add to INSTALLED_APPS¶
2. Configure the maps database¶
The .yoromaps file is a SQLite database that Django can use as a second database:
# settings.py
import yoromaps
DATABASES = {
"default": {
"ENGINE": "django.db.backends.postgresql",
"NAME": "myapp",
...
},
"maps": yoromaps.db_config("/data/mali.yoromaps"),
}
The db_config() helper returns:
3. Add the database router¶
The router ensures that yoromaps queries go to the maps database, not your default:
The router:
- Directs reads and writes for
yoromapsmodels to themapsdatabase. - Prevents Django migrations from running on the
mapsdatabase (the schema is managed by yoromaps). - Leaves all other models unaffected.
4. Add URL routes¶
# urls.py
from django.urls import path, include
urlpatterns = [
path("maps/", include("yoromaps.django.urls")),
...
]
This registers two endpoints:
GET /maps/tiles/{z}/{x}/{y}.png— Serve map tilesGET /maps/route/?codes=ML-ABC,ML-XYZ— Calculate routes
Tile serving¶
Once configured, tiles stored in the .yoromaps database are served at:
Tiles are served with Cache-Control: public, max-age=86400 (24-hour cache). If a tile is not found, a 404 is returned.
Tip
Make sure your .yoromaps file was built with --tiles to include tile data. Without tiles, the tile endpoint will always return 404.
Route API¶
The route endpoint accepts Yoro codes as a comma-separated query parameter:
Response:
{
"total_distance_km": 642.3,
"total_duration_min": 578.1,
"legs": [
{
"distance_km": 642.3,
"duration_min": 578.1,
"found": true,
"geometry": {
"type": "LineString",
"coordinates": [[-8.0, 12.6], [-7.5, 13.0], ...]
},
"steps": [
{"instruction": "Continue on Route Nationale 6", "distance_m": 12340, "name": "Route Nationale 6"}
]
}
]
}
At least 2 codes are required; pass more for multi-leg routing.
Leaflet map display¶
Here is a complete example of displaying tiles and routes on a Leaflet map in a Django template:
{% load static %}
<!DOCTYPE html>
<html>
<head>
<title>Yoro Maps</title>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9/dist/leaflet.css" />
<script src="https://unpkg.com/leaflet@1.9/dist/leaflet.js"></script>
<style>
#map { height: 100vh; width: 100%; }
</style>
</head>
<body>
<div id="map"></div>
<script>
const map = L.map('map').setView([12.6, -8.0], 7);
// Use tiles from yoro-maps (offline)
L.tileLayer('/maps/tiles/{z}/{x}/{y}.png', {
maxZoom: 14,
attribution: '© OpenStreetMap contributors'
}).addTo(map);
// Calculate and display a route
fetch('/maps/route/?codes=ML-ABC,ML-XYZ')
.then(r => r.json())
.then(data => {
if (data.legs) {
data.legs.forEach(leg => {
if (leg.found && leg.geometry) {
// GeoJSON coordinates are [lon, lat], Leaflet needs [lat, lon]
const coords = leg.geometry.coordinates.map(c => [c[1], c[0]]);
L.polyline(coords, {color: '#FF5722', weight: 4}).addTo(map);
}
});
}
});
</script>
</body>
</html>
Fallback to online tiles¶
If you want to use online tiles as a fallback when offline tiles are missing, use multiple tile layers:
// Try offline tiles first, fall back to online
const offlineLayer = L.tileLayer('/maps/tiles/{z}/{x}/{y}.png', {
maxZoom: 14,
attribution: '© OpenStreetMap contributors'
});
const onlineLayer = L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 19,
attribution: '© OpenStreetMap contributors'
});
// Use online as base, offline overlay where available
onlineLayer.addTo(map);
L.control.layers({
"Online": onlineLayer,
"Offline": offlineLayer,
}).addTo(map);
Complete settings example¶
# settings.py
import yoromaps
INSTALLED_APPS = [
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
"yoromaps.django",
"myapp",
]
DATABASES = {
"default": {
"ENGINE": "django.db.backends.sqlite3",
"NAME": BASE_DIR / "db.sqlite3",
},
"maps": yoromaps.db_config(BASE_DIR / "data" / "mali.yoromaps"),
}
DATABASE_ROUTERS = ["yoromaps.django.router.YoroMapsRouter"]