Skip to content

Django Integration

Yoro Maps includes a full Django integration for serving map tiles and routes from a web application.

Install

pip install yoro-maps[django]

Configuration

1. Add to INSTALLED_APPS

# settings.py
INSTALLED_APPS = [
    ...
    "yoromaps.django",
]

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:

{
    "ENGINE": "django.db.backends.sqlite3",
    "NAME": "/data/mali.yoromaps",
}

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 tiles
  • GET /maps/route/?codes=ML-ABC,ML-XYZ — Calculate routes
  • GET /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:

GET /maps/tiles/12/1234/567.png

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:

GET /maps/route/?codes=ML-ABC,ML-XYZ

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: '&copy; 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: '&copy; OpenStreetMap contributors'
});

const onlineLayer = L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
    maxZoom: 19,
    attribution: '&copy; 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"),
}
# urls.py
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path("admin/", admin.site.urls),
    path("maps/", include("yoromaps.django.urls")),
]