mirror of
https://forge.chapril.org/tykayn/osm-commerces
synced 2025-10-09 17:02:46 +02:00
ajout page speed limit
This commit is contained in:
parent
a56c4b052c
commit
3d767ffaae
3 changed files with 158 additions and 0 deletions
|
@ -1813,4 +1813,23 @@ final class AdminController extends AbstractController
|
||||||
'insee_code' => $insee_code,
|
'insee_code' => $insee_code,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[Route('/admin/speed-limit/{insee_code}', name: 'admin_speed_limit', requirements: ['insee_code' => '\d+'])]
|
||||||
|
public function speedLimit(string $insee_code): Response
|
||||||
|
{
|
||||||
|
$stats = $this->entityManager->getRepository(Stats::class)->findOneBy(['zone' => $insee_code]);
|
||||||
|
if (!$stats) {
|
||||||
|
$this->addFlash('error', 'Aucune stats trouvée pour ce code INSEE. Veuillez d\'abord ajouter la ville.');
|
||||||
|
return $this->redirectToRoute('app_admin_import_stats');
|
||||||
|
}
|
||||||
|
// Tags attendus pour la complétion
|
||||||
|
$expected_tags = ['maxspeed', 'highway'];
|
||||||
|
// On transmet le code INSEE et le nom de la ville au template
|
||||||
|
return $this->render('admin/speed_limit.html.twig', [
|
||||||
|
'stats' => $stats,
|
||||||
|
'insee_code' => $insee_code,
|
||||||
|
'expected_tags' => $expected_tags,
|
||||||
|
'maptiler_token' => $_ENV['MAPTILER_TOKEN'] ?? null,
|
||||||
|
]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
136
templates/admin/speed_limit.html.twig
Normal file
136
templates/admin/speed_limit.html.twig
Normal file
|
@ -0,0 +1,136 @@
|
||||||
|
{% extends 'base.html.twig' %}
|
||||||
|
|
||||||
|
{% block title %}Limites de vitesse - {{ stats.name }}{% endblock %}
|
||||||
|
|
||||||
|
{% block body %}
|
||||||
|
<div class="container mt-4">
|
||||||
|
<h1><i class="bi bi-speedometer2"></i> Limites de vitesse à {{ stats.name }} ({{ stats.zone }})</h1>
|
||||||
|
<p>Complétion des limitations de vitesse sur le réseau routier OSM.<br>
|
||||||
|
<span class="text-muted">Tags attendus :
|
||||||
|
{% for tag in expected_tags %}<code>{{ tag }}</code>{% if not loop.last %}, {% endif %}{% endfor %}
|
||||||
|
</span></p>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-8">
|
||||||
|
<div class="card mb-3">
|
||||||
|
<div class="card-header"><i class="bi bi-map"></i> Carte des routes (coloration selon maxspeed)</div>
|
||||||
|
<div class="card-body p-2">
|
||||||
|
<div id="speedlimit-map" style="height: 450px; width: 100%;"></div>
|
||||||
|
<div id="speedlimit-completion" class="mt-2"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card mb-3">
|
||||||
|
<div class="card-header"><i class="bi bi-traffic-cone"></i> Panneaux routiers & feux de circulation</div>
|
||||||
|
<div class="card-body p-2">
|
||||||
|
<div id="traffic-map" style="height: 350px; width: 100%;"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header"><i class="bi bi-info-circle"></i> Informations</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<ul>
|
||||||
|
<li><b>Rouge</b> : route sans <code>maxspeed</code></li>
|
||||||
|
<li><b>Vert</b> : route avec <code>maxspeed</code></li>
|
||||||
|
<li><b>Bleu</b> : panneau routier (<code>traffic_sign</code>)</li>
|
||||||
|
<li><b>Orange</b> : feu de circulation (<code>traffic_signals</code>)</li>
|
||||||
|
</ul>
|
||||||
|
<p>Cliquer sur un objet pour ouvrir dans OSM, iD ou JOSM.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block javascripts %}
|
||||||
|
{{ parent() }}
|
||||||
|
<script src="/js/maplibre/maplibre-gl.js"></script>
|
||||||
|
<script>
|
||||||
|
const insee = '{{ insee_code }}';
|
||||||
|
const overpassHighways = `[out:json][timeout:60];\narea["ref:INSEE"="${insee}"]->.searchArea;\nway["highway"](area.searchArea);\nout body;\n>;\nout skel qt;`;
|
||||||
|
const overpassTraffic = `[out:json][timeout:60];\narea["ref:INSEE"="${insee}"]->.searchArea;\n(
|
||||||
|
node["traffic_sign"](area.searchArea);
|
||||||
|
node["highway"="traffic_signals"](area.searchArea);
|
||||||
|
);\nout body;`;
|
||||||
|
|
||||||
|
function fetchOverpass(query) {
|
||||||
|
return fetch('https://overpass-api.de/api/interpreter', {
|
||||||
|
method: 'POST',
|
||||||
|
body: query,
|
||||||
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
|
||||||
|
}).then(r => r.json());
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
// --- Carte des highways ---
|
||||||
|
let map = new maplibregl.Map({
|
||||||
|
container: 'speedlimit-map',
|
||||||
|
style: 'https://api.maptiler.com/maps/streets/style.json?key={{ maptiler_token }}',
|
||||||
|
center: [2, 48],
|
||||||
|
zoom: 13
|
||||||
|
});
|
||||||
|
map.addControl(new maplibregl.NavigationControl());
|
||||||
|
fetchOverpass(overpassHighways).then(data => {
|
||||||
|
let nodes = {};
|
||||||
|
let withMaxspeed = 0, total = 0;
|
||||||
|
data.elements.forEach(e => { if (e.type === 'node') nodes[e.id] = e; });
|
||||||
|
data.elements.forEach(e => {
|
||||||
|
if (e.type === 'way' && e.tags && e.tags.highway) {
|
||||||
|
total++;
|
||||||
|
let hasMaxspeed = !!e.tags.maxspeed;
|
||||||
|
if (hasMaxspeed) withMaxspeed++;
|
||||||
|
let coords = e.nodes.map(nid => nodes[nid]).filter(n => n).map(n => [n.lon, n.lat]);
|
||||||
|
if (coords.length < 2) return;
|
||||||
|
map.addLayer({
|
||||||
|
id: 'way-' + e.id,
|
||||||
|
type: 'line',
|
||||||
|
source: {
|
||||||
|
type: 'geojson',
|
||||||
|
data: { type: 'Feature', geometry: { type: 'LineString', coordinates: coords }, properties: {} }
|
||||||
|
},
|
||||||
|
layout: { 'line-join': 'round', 'line-cap': 'round' },
|
||||||
|
paint: {
|
||||||
|
'line-color': hasMaxspeed ? '#28a745' : '#dc3545',
|
||||||
|
'line-width': 3
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// Popup OSM/iD/JOSM
|
||||||
|
let popupHtml = `<b>Highway ${e.tags.highway}</b><br>ID: ${e.id}`;
|
||||||
|
if (e.tags.maxspeed) popupHtml += `<br><b>maxspeed:</b> ${e.tags.maxspeed}`;
|
||||||
|
popupHtml += `<br><a href='https://www.openstreetmap.org/way/${e.id}' target='_blank'>OSM</a> | <a href='https://www.openstreetmap.org/edit?editor=id&way=${e.id}' target='_blank'>iD</a> | <a href='http://127.0.0.1:8111/load_object?objects=W${e.id}' target='_blank'>JOSM</a>`;
|
||||||
|
let marker = new maplibregl.Marker({ color: hasMaxspeed ? '#28a745' : '#dc3545' })
|
||||||
|
.setLngLat(coords[Math.floor(coords.length/2)])
|
||||||
|
.setPopup(new maplibregl.Popup({ offset: 18 }).setHTML(popupHtml));
|
||||||
|
// marker.addTo(map); // Optionnel : afficher un marker au milieu
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let completion = total > 0 ? Math.round(100 * withMaxspeed / total) : 0;
|
||||||
|
document.getElementById('speedlimit-completion').innerHTML = `<b>Complétion maxspeed :</b> ${withMaxspeed} / ${total} (${completion}%)`;
|
||||||
|
});
|
||||||
|
// --- Carte des panneaux et feux ---
|
||||||
|
let map2 = new maplibregl.Map({
|
||||||
|
container: 'traffic-map',
|
||||||
|
style: 'https://api.maptiler.com/maps/streets/style.json?key={{ maptiler_token }}',
|
||||||
|
center: [2, 48],
|
||||||
|
zoom: 13
|
||||||
|
});
|
||||||
|
map2.addControl(new maplibregl.NavigationControl());
|
||||||
|
fetchOverpass(overpassTraffic).then(data => {
|
||||||
|
data.elements.forEach(e => {
|
||||||
|
if (e.type === 'node') {
|
||||||
|
let color = e.tags && e.tags.highway === 'traffic_signals' ? 'orange' : 'blue';
|
||||||
|
let icon = e.tags && e.tags.highway === 'traffic_signals' ? '🚦' : '🛑';
|
||||||
|
let popupHtml = `<b>${icon} ${e.tags.highway === 'traffic_signals' ? 'Feu de circulation' : 'Panneau routier'}</b><br>ID: ${e.id}`;
|
||||||
|
if (e.tags.traffic_sign) popupHtml += `<br><b>traffic_sign:</b> ${e.tags.traffic_sign}`;
|
||||||
|
popupHtml += `<br><a href='https://www.openstreetmap.org/node/${e.id}' target='_blank'>OSM</a> | <a href='https://www.openstreetmap.org/edit?editor=id&node=${e.id}' target='_blank'>iD</a> | <a href='http://127.0.0.1:8111/load_object?objects=N${e.id}' target='_blank'>JOSM</a>`;
|
||||||
|
new maplibregl.Marker({ color: color })
|
||||||
|
.setLngLat([e.lon, e.lat])
|
||||||
|
.setPopup(new maplibregl.Popup({ offset: 18 }).setHTML(popupHtml))
|
||||||
|
.addTo(map2);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
|
@ -139,6 +139,9 @@
|
||||||
<a href="{{ path('admin_street_completion', {'insee_code': stats.zone}) }}" class="btn btn-outline-success ms-2">
|
<a href="{{ path('admin_street_completion', {'insee_code': stats.zone}) }}" class="btn btn-outline-success ms-2">
|
||||||
<i class="bi bi-signpost"></i> Complétion des rues
|
<i class="bi bi-signpost"></i> Complétion des rues
|
||||||
</a>
|
</a>
|
||||||
|
<a href="{{ path('admin_speed_limit', {'insee_code': stats.zone}) }}" class="btn btn-outline-danger ms-2">
|
||||||
|
<i class="bi bi-speedometer2"></i> Limites de vitesse
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% if stats.population %}
|
{% if stats.population %}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue