Some checks reported errors
continuous-integration/drone/push Build was killed
125 lines
4.1 KiB
HTML
125 lines
4.1 KiB
HTML
{% extends "pxy_dashboard/partials/base.html" %}
|
|
{% block content %}
|
|
<div class="container mt-4">
|
|
{% include "pxy_dashboard/partials/dashboard/kpi_row.html" %}
|
|
|
|
<div class="row g-3 mb-3">
|
|
<div class="col-auto">
|
|
<label for="subdivisionSelect" class="form-label">Subdivision:</label>
|
|
<select id="subdivisionSelect" class="form-select">
|
|
{% for subdiv in subdivisions %}
|
|
<option value="{{ subdiv }}">{{ subdiv }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
|
|
<div class="col-auto">
|
|
<label for="routeSelect" class="form-label">Vehicle / Route:</label>
|
|
<select id="routeSelect" class="form-select"></select>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="dispatch-map" style="height: 600px; width: 100%;"></div>
|
|
</div>
|
|
|
|
<link
|
|
rel="stylesheet"
|
|
href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"
|
|
/>
|
|
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
|
|
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
// 1) parse your two JSON-blobs
|
|
const geojsonData = JSON.parse('{{ geojson_by_subdivision|escapejs }}');
|
|
const routesBySubdivision = JSON.parse('{{ routes_by_subdivision|escapejs }}');
|
|
|
|
// 2) set up map
|
|
const map = L.map('dispatch-map');
|
|
L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
|
attribution: '© OpenStreetMap'
|
|
}).addTo(map);
|
|
|
|
// 3) icons as before
|
|
const iconOptions = {
|
|
iconSize: [25, 41],
|
|
iconAnchor: [12, 41],
|
|
popupAnchor: [1, -34],
|
|
shadowUrl: 'https://unpkg.com/leaflet@1.9.4/dist/images/marker-shadow.png',
|
|
shadowSize: [41, 41]
|
|
};
|
|
const icons = {
|
|
start: L.icon({ iconUrl: 'https://raw.githubusercontent.com/pointhi/leaflet-color-markers/master/img/marker-icon-green.png', ...iconOptions }),
|
|
end: L.icon({ iconUrl: 'https://raw.githubusercontent.com/pointhi/leaflet-color-markers/master/img/marker-icon-red.png', ...iconOptions }),
|
|
job: L.icon({ iconUrl: 'https://raw.githubusercontent.com/pointhi/leaflet-color-markers/master/img/marker-icon-orange.png', ...iconOptions }),
|
|
other: L.icon({ iconUrl: 'https://raw.githubusercontent.com/pointhi/leaflet-color-markers/master/img/marker-icon-grey.png', ...iconOptions })
|
|
};
|
|
|
|
let currentLayer = null;
|
|
const subdivSel = document.getElementById('subdivisionSelect');
|
|
const routeSel = document.getElementById('routeSelect');
|
|
|
|
// populate the “route” dropdown given a subdivision
|
|
function populateRoutes(subdiv) {
|
|
routeSel.innerHTML = '';
|
|
const list = routesBySubdivision[subdiv] || [];
|
|
list.forEach(rid => {
|
|
const o = document.createElement('option');
|
|
o.value = rid;
|
|
o.text = rid;
|
|
routeSel.append(o);
|
|
});
|
|
}
|
|
|
|
// render only the selected subdivision+route
|
|
function render(subdiv, routeId) {
|
|
if (currentLayer) map.removeLayer(currentLayer);
|
|
map.invalidateSize();
|
|
|
|
// filter to only those features matching this route
|
|
const allFC = geojsonData[subdiv] || { features: [] };
|
|
const filtered = {
|
|
type: 'FeatureCollection',
|
|
features: allFC.features.filter(f => f.properties.route_id === routeId)
|
|
};
|
|
|
|
currentLayer = L.geoJSON(filtered, {
|
|
pointToLayer: (f, latlng) => {
|
|
const type = f.properties.step_type || 'other';
|
|
return L.marker(latlng, { icon: icons[type] }).bindPopup(f.properties.popup);
|
|
},
|
|
style: feat => feat.geometry.type === 'LineString'
|
|
? { weight: 3, opacity: 0.7 }
|
|
: {}
|
|
}).addTo(map);
|
|
|
|
const b = currentLayer.getBounds();
|
|
if (b.isValid()) {
|
|
map.fitBounds(b, { padding: [20, 20] });
|
|
}
|
|
}
|
|
|
|
// wire up select events
|
|
subdivSel.addEventListener('change', () => {
|
|
const s = subdivSel.value;
|
|
populateRoutes(s);
|
|
const firstRoute = routeSel.options[0]?.value;
|
|
if (firstRoute) render(s, firstRoute);
|
|
});
|
|
|
|
routeSel.addEventListener('change', () => {
|
|
render(subdivSel.value, routeSel.value);
|
|
});
|
|
|
|
// initial load
|
|
const firstSub = subdivSel.options[0]?.value;
|
|
if (firstSub) {
|
|
subdivSel.value = firstSub;
|
|
populateRoutes(firstSub);
|
|
const firstRoute = routeSel.options[0]?.value;
|
|
if (firstRoute) render(firstSub, firstRoute);
|
|
}
|
|
});
|
|
</script>
|
|
{% endblock %}
|