← back to Restaurant Directory
src/web/views/map.ejs
34 lines
<%- include('partials/head', { title: 'Map of all restaurants' }) %>
<main class="container map-page">
<header>
<h1>Map · <%= total.toLocaleString() %> restaurants</h1>
<p class="muted">Pins load on demand by viewport. Click any to open the detail page.</p>
</header>
<div id="big-map"></div>
</main>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css">
<link rel="stylesheet" href="https://unpkg.com/leaflet.markercluster@1.5.3/dist/MarkerCluster.css">
<link rel="stylesheet" href="https://unpkg.com/leaflet.markercluster@1.5.3/dist/MarkerCluster.Default.css">
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<script src="https://unpkg.com/leaflet.markercluster@1.5.3/dist/leaflet.markercluster.js"></script>
<script>
const map = L.map('big-map').setView([34.05, -118.24], 11);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { attribution: '© OSM', maxZoom: 19 }).addTo(map);
const cluster = L.markerClusterGroup({ chunkedLoading: true, showCoverageOnHover: false });
fetch('/api/pins')
.then(r => r.json())
.then(({ pins }) => {
const esc = s => String(s).replace(/[&<>"']/g, c => ({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c]));
pins.forEach(p => {
const m = L.marker([p.lat, p.lng]);
m.bindPopup('<strong>' + esc(p.name) + '</strong><br><a href="/r/' + encodeURIComponent(p.slug) + '">Open →</a>');
cluster.addLayer(m);
});
map.addLayer(cluster);
});
</script>
<%- include('partials/foot') %>