Django + Leaflet Map¶
This guide shows how to build a complete Django application with an interactive map that displays Yoro codes. Users can click anywhere on the map to get a Yoro code, and see the Hilbert cell highlighted.
What you'll build¶
A Django page with:
- An interactive Leaflet map
- Click-to-encode: click anywhere → see the Yoro code + cell rectangle
- A search bar to decode a Yoro code → fly to its location
- The Hilbert grid overlay at the current zoom level
Prerequisites¶
Your Django project needs GeoDjango configured (PostGIS or SpatiaLite).
Step 1: Django Setup¶
settings.py¶
urls.py¶
from django.urls import include, path
from . import views
urlpatterns = [
path("", views.map_view, name="map"),
path("api/yoro/", include("yoro.django.urls")),
]
views.py¶
Step 2: The Template¶
Create templates/map.html:
<!DOCTYPE html>
<html>
<head>
<title>Yoro Map</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- Leaflet CSS -->
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9/dist/leaflet.css" />
<style>
body { margin: 0; font-family: system-ui, sans-serif; }
#map { height: 100vh; width: 100%; }
.yoro-panel {
position: absolute;
top: 10px;
right: 10px;
z-index: 1000;
background: white;
padding: 16px;
border-radius: 8px;
box-shadow: 0 2px 12px rgba(0,0,0,0.2);
min-width: 280px;
}
.yoro-panel h3 { margin: 0 0 12px; }
.yoro-code {
font-size: 24px;
font-weight: bold;
font-family: monospace;
color: #e65100;
margin: 8px 0;
}
.yoro-info { font-size: 13px; color: #666; margin: 4px 0; }
.yoro-search {
display: flex;
gap: 8px;
margin-top: 12px;
}
.yoro-search input {
flex: 1;
padding: 8px;
border: 1px solid #ccc;
border-radius: 4px;
font-family: monospace;
font-size: 14px;
}
.yoro-search button {
padding: 8px 16px;
background: #e65100;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
</style>
</head>
<body>
<div id="map"></div>
<div class="yoro-panel">
<h3>Yoro Code</h3>
<div class="yoro-code" id="code-display">Click on the map</div>
<div class="yoro-info" id="coords-display"></div>
<div class="yoro-info" id="resolution-display"></div>
<div class="yoro-info" id="precision-display"></div>
<div class="yoro-search">
<input type="text" id="code-input" placeholder="CI-PV9XD" />
<button onclick="decodeCode()">Go</button>
</div>
</div>
<!-- Leaflet JS -->
<script src="https://unpkg.com/leaflet@1.9/dist/leaflet.js"></script>
<script>
// Initialize map centered on Cote d'Ivoire
const map = L.map('map').setView([7.5, -5.5], 7);
// Add OpenStreetMap tiles
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© OpenStreetMap contributors',
maxZoom: 19,
}).addTo(map);
// Layer for the Yoro cell rectangle
let cellLayer = null;
// Click handler: encode the clicked location
map.on('click', async function(e) {
const { lat, lng } = e.latlng;
const domain = 'CI'; // Change for other countries
const precision = zoomToPrecision(map.getZoom());
const resp = await fetch(
`/api/yoro/encode/?lat=${lat}&lon=${lng}&domain=${domain}&precision=${precision}`
);
const data = await resp.json();
showResult(data);
});
function showResult(data) {
document.getElementById('code-display').textContent = data.code;
document.getElementById('coords-display').textContent =
`${data.lat.toFixed(6)}, ${data.lon.toFixed(6)}`;
document.getElementById('resolution-display').textContent =
`Resolution: ~${data.resolution_m.toFixed(1)} m`;
document.getElementById('precision-display').textContent =
`Precision: p=${data.precision} (${data.code.split('-')[1].length} chars)`;
// Draw the cell rectangle
if (cellLayer) map.removeLayer(cellLayer);
const b = data.bounds;
cellLayer = L.rectangle(
[[b.lat_min, b.lon_min], [b.lat_max, b.lon_max]],
{ color: '#e65100', weight: 2, fillOpacity: 0.15 }
).addTo(map);
}
// Decode a code and fly to it
async function decodeCode() {
const code = document.getElementById('code-input').value.trim();
if (!code) return;
const resp = await fetch(`/api/yoro/decode/?code=${encodeURIComponent(code)}`);
if (!resp.ok) {
alert('Invalid code');
return;
}
const data = resp.json().then(data => {
showResult(data);
map.flyTo([data.lat, data.lon], 14);
});
}
// Map zoom level to a sensible Yoro precision
function zoomToPrecision(zoom) {
if (zoom <= 8) return 4;
if (zoom <= 10) return 7;
if (zoom <= 12) return 9;
if (zoom <= 14) return 12;
if (zoom <= 16) return 14;
if (zoom <= 18) return 17;
return 19;
}
</script>
</body>
</html>
Step 3: Run¶
Open http://localhost:8000/ — click anywhere on the map to see Yoro codes.
How It Works¶
- Click → JavaScript sends
GET /api/yoro/encode/?lat=...&lon=...to the Django backend - Backend → Yoro encodes the coordinates, returns the code + cell bounds
- Frontend → Displays the code and draws the cell rectangle on the map
- Decode → Type a code in the search bar →
GET /api/yoro/decode/?code=...→ fly to location
The precision adapts to the zoom level: zoomed out = large cells (region-level codes), zoomed in = small cells (meter-level codes).
Domain selection
Change const domain = 'CI' in the JavaScript to match your country. Use 'XX' for worldwide coverage (lower resolution).
Next Steps¶
- Add the Hilbert grid overlay to show all cells in the viewport
- Try different tile providers (satellite, terrain)
- Store codes in your models with
assign_altius_code()