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 URL routes¶
# urls.py
from django.urls import path, include
urlpatterns = [
path("maps/", include("yoromaps.django.urls")),
...
]
This registers three endpoints:
GET /maps/tiles/{z}/{x}/{y}.png— Serve map tilesGET /maps/route/?codes=ML-ABC,ML-XYZ— Calculate routesGET /maps/pois/?cell=ML-ABC(or?lat=&lon=&radius=,?q=&category=) — Query points of interest
4. Point the yoro widget at your offline tiles¶
If you also use yoro's map picker widget, one setting switches every
widget to the offline tiles served by yoro-maps:
# settings.py
YORO_TILE_URL = "/maps/tiles/{z}/{x}/{y}.png"
YORO_TILE_ATTRIBUTION = "© OpenStreetMap contributors"
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"),
}