mirror of
https://forge.chapril.org/tykayn/osm-commerces
synced 2025-10-04 17:04:53 +02:00
ajouts d'infos calculées au vol pour le graphe avancé vélo
This commit is contained in:
parent
979be016f2
commit
a56c4b052c
4 changed files with 175 additions and 9 deletions
|
@ -270,8 +270,8 @@ final class AdminController extends AbstractController
|
|||
{
|
||||
$stats = $this->entityManager->getRepository(Stats::class)->findOneBy(['zone' => $insee_code]);
|
||||
if (!$stats) {
|
||||
$this->addFlash('error', 'Aucune stats trouvée pour ce code INSEE.');
|
||||
return $this->redirectToRoute('app_admin');
|
||||
$this->addFlash('error', 'Aucune stats trouvée pour ce code INSEE. Veuillez d\'abord ajouter la ville.');
|
||||
return $this->redirectToRoute('app_admin_import_stats');
|
||||
}
|
||||
$followups = $stats->getCityFollowUps();
|
||||
$refresh = false;
|
||||
|
|
|
@ -458,7 +458,7 @@ class PublicController extends AbstractController
|
|||
|
||||
$stats = $place->getStats();
|
||||
if (!$stats) {
|
||||
$stats = $this->entityManager->getRepository(Stats::class)->findOneBy(['zip_code' => $place->getZipCode()]);
|
||||
$stats = $this->entityManager->getRepository(Stats::class)->findOneBy(['zone' => $place->getZipCode()]);
|
||||
}
|
||||
if (!$stats) {
|
||||
$stats = new Stats();
|
||||
|
|
165
templates/admin/_followup_bicycle_parking_extra.html.twig
Normal file
165
templates/admin/_followup_bicycle_parking_extra.html.twig
Normal file
|
@ -0,0 +1,165 @@
|
|||
{#
|
||||
Template d'infos supplémentaires pour la thématique "bicycle_parking"
|
||||
À inclure dans followup_theme_graph.html.twig si theme == 'bicycle_parking'
|
||||
Nécessite que la variable JS "countData" soit disponible (données du graphe)
|
||||
Nécessite que la variable stats.population soit transmise au template parent
|
||||
#}
|
||||
<div class="card mt-4">
|
||||
<div class="card-header">
|
||||
<i class="bi bi-bicycle"></i> Infos vélo : capacité totale des parkings à vélo et voies cyclables
|
||||
</div>
|
||||
<div class="card-body p-2">
|
||||
<div id="bicycle-parking-extra-info">
|
||||
<span class="text-muted">Chargement des statistiques vélo...</span>
|
||||
</div>
|
||||
<div class="mt-2 small text-muted">
|
||||
<i class="bi bi-info-circle"></i> Pensez à renseigner le tag <code>capacity</code> sur chaque parking vélo pour améliorer la connaissance de l'offre cyclable.<br>
|
||||
<i class="bi bi-info-circle"></i> Les longueurs de voies cyclables sont calculées à partir des géométries OSM (approximation).
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
if (typeof overpassQuery !== 'undefined' && '{{ theme }}' === 'bicycle_parking') {
|
||||
const population = {{ stats.population|default(0) }};
|
||||
// Requête pour parkings vélo + voies cyclables
|
||||
const insee = '{{ stats.zone|e('js') }}';
|
||||
const overpassVelo = `[out:json][timeout:60];\narea["ref:INSEE"="${insee}"]->.searchArea;\n(\n node["amenity"="bicycle_parking"](area.searchArea);\n way["amenity"="bicycle_parking"](area.searchArea);\n way["highway"="cycleway"](area.searchArea);\n way["cycleway"](area.searchArea);\n way["highway"](area.searchArea);\n node["amenity"="parking"](area.searchArea);\n way["amenity"="parking"](area.searchArea);\n);\nout body;\n>;\nout skel qt;`;
|
||||
fetch('https://overpass-api.de/api/interpreter', {
|
||||
method: 'POST',
|
||||
body: overpassVelo,
|
||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
let total = 0;
|
||||
let capacitySum = 0;
|
||||
let withCapacity = 0;
|
||||
let nodes = {};
|
||||
let cycleways_separees = [];
|
||||
let cycleways_melangees = [];
|
||||
let roads = [];
|
||||
let car_parkings = 0;
|
||||
let car_parking_capacity = 0;
|
||||
let car_parking_surface = 0;
|
||||
let bike_parking_surface = 0;
|
||||
if (data.elements) {
|
||||
// Indexer les nœuds pour calculs de longueur
|
||||
data.elements.forEach(e => {
|
||||
if (e.type === 'node') {
|
||||
nodes[e.id] = e;
|
||||
}
|
||||
});
|
||||
// Parkings vélo
|
||||
data.elements.forEach(e => {
|
||||
if (e.type === 'node' && e.tags && e.tags.amenity === 'bicycle_parking') {
|
||||
total++;
|
||||
if (e.tags.capacity) {
|
||||
const cap = parseInt(e.tags.capacity);
|
||||
if (!isNaN(cap)) {
|
||||
capacitySum += cap;
|
||||
withCapacity++;
|
||||
bike_parking_surface += cap * 2; // 2m² par vélo
|
||||
}
|
||||
} else {
|
||||
bike_parking_surface += 10; // 10m² par parking sans capacity
|
||||
}
|
||||
}
|
||||
if (e.type === 'way' && e.tags && e.tags.amenity === 'bicycle_parking') {
|
||||
total++;
|
||||
if (e.tags.capacity) {
|
||||
const cap = parseInt(e.tags.capacity);
|
||||
if (!isNaN(cap)) {
|
||||
capacitySum += cap;
|
||||
withCapacity++;
|
||||
bike_parking_surface += cap * 2;
|
||||
}
|
||||
} else {
|
||||
bike_parking_surface += 10;
|
||||
}
|
||||
}
|
||||
});
|
||||
// Parkings voiture
|
||||
data.elements.forEach(e => {
|
||||
if ((e.type === 'node' || e.type === 'way') && e.tags && e.tags.amenity === 'parking') {
|
||||
car_parkings++;
|
||||
if (e.tags.capacity) {
|
||||
const cap = parseInt(e.tags.capacity);
|
||||
if (!isNaN(cap)) {
|
||||
car_parking_capacity += cap;
|
||||
car_parking_surface += cap * 25; // 25m² par place voiture
|
||||
}
|
||||
} else {
|
||||
car_parking_surface += 200; // 200m² par parking sans capacity
|
||||
}
|
||||
}
|
||||
});
|
||||
// Voies cyclables et routes
|
||||
data.elements.forEach(e => {
|
||||
if (e.type === 'way' && e.tags) {
|
||||
if (e.tags.highway === 'cycleway') {
|
||||
cycleways_separees.push(e);
|
||||
} else if (e.tags.cycleway && e.tags.highway && e.tags.highway !== 'cycleway') {
|
||||
cycleways_melangees.push(e);
|
||||
}
|
||||
if (e.tags.highway && e.tags.highway !== 'cycleway') {
|
||||
roads.push(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
// Fonction pour calculer la longueur d'une way en km
|
||||
function wayLengthKm(way) {
|
||||
let len = 0;
|
||||
for (let i = 1; i < way.nodes.length; i++) {
|
||||
const n1 = nodes[way.nodes[i-1]];
|
||||
const n2 = nodes[way.nodes[i]];
|
||||
if (n1 && n2) {
|
||||
// Haversine
|
||||
const R = 6371;
|
||||
const dLat = (n2.lat-n1.lat)*Math.PI/180;
|
||||
const dLon = (n2.lon-n1.lon)*Math.PI/180;
|
||||
const a = Math.sin(dLat/2)*Math.sin(dLat/2) + Math.cos(n1.lat*Math.PI/180)*Math.cos(n2.lat*Math.PI/180)*Math.sin(dLon/2)*Math.sin(dLon/2);
|
||||
const c = 2*Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
|
||||
len += R*c;
|
||||
}
|
||||
}
|
||||
return len;
|
||||
}
|
||||
let len_separees = 0;
|
||||
let len_melangees = 0;
|
||||
let len_routes = 0;
|
||||
cycleways_separees.forEach(w => { len_separees += wayLengthKm(w); });
|
||||
cycleways_melangees.forEach(w => { len_melangees += wayLengthKm(w); });
|
||||
roads.forEach(w => { len_routes += wayLengthKm(w); });
|
||||
let html = '';
|
||||
html += `<div class='mb-3 p-2 border rounded bg-light'>
|
||||
<h5><i class='bi bi-bicycle'></i> Mobilité et stationnement : synthèse</h5>
|
||||
<table class='table table-sm align-middle mb-0' style='max-width:600px;'>
|
||||
<tbody>
|
||||
<tr><th><i class='bi bi-bicycle'></i> Parkings vélo</th><td class='text-end'><b>${total}</b></td></tr>`;
|
||||
if (population > 0) {
|
||||
const ratio = (total / population).toFixed(4);
|
||||
html += `<tr><th class='text-muted'>Parkings vélo par habitant</th><td class='text-end'>${ratio}</td></tr>`;
|
||||
}
|
||||
if (withCapacity > 0) {
|
||||
html += `<tr><th><i class='bi bi-people'></i> Capacité totale vélo</th><td class='text-end'>${capacitySum}</td></tr>`;
|
||||
} else {
|
||||
html += `<tr><th colspan='2' class='text-danger'>Aucune capacité renseignée sur les parkings vélo</th></tr>`;
|
||||
}
|
||||
html += `<tr><th><i class='bi bi-aspect-ratio'></i> Surface estimée parkings vélo (m²)</th><td class='text-end'>${bike_parking_surface.toLocaleString()}</td></tr>`;
|
||||
html += `<tr><th><i class='bi bi-signpost'></i> Voies cyclables séparées (km)</th><td class='text-end'>${len_separees.toFixed(2)}</td></tr>`;
|
||||
html += `<tr><th><i class='bi bi-signpost'></i> Voies cyclables sur route (km)</th><td class='text-end'>${len_melangees.toFixed(2)}</td></tr>`;
|
||||
html += `<tr><th><i class='bi bi-car-front'></i> Parkings voiture</th><td class='text-end'>${car_parkings}</td></tr>`;
|
||||
html += `<tr><th><i class='bi bi-people'></i> Capacité totale voiture</th><td class='text-end'>${car_parking_capacity}</td></tr>`;
|
||||
html += `<tr><th><i class='bi bi-aspect-ratio'></i> Surface estimée parkings voiture (m²)</th><td class='text-end'>${car_parking_surface.toLocaleString()}</td></tr>`;
|
||||
html += `<tr><th><i class='bi bi-road'></i> Longueur totale de routes (km)</th><td class='text-end'>${len_routes.toFixed(2)}</td></tr>`;
|
||||
html += `</tbody></table></div>`;
|
||||
document.getElementById('bicycle-parking-extra-info').innerHTML = html;
|
||||
})
|
||||
.catch(() => {
|
||||
document.getElementById('bicycle-parking-extra-info').innerHTML = '<span class="text-danger">Erreur lors du chargement des données vélo.</span>';
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
|
@ -123,6 +123,7 @@
|
|||
|
||||
{% block body %}
|
||||
<div class="container-fluid">
|
||||
|
||||
{# DEBUG : Affichage des objets Place trouvés pour cette ville #}
|
||||
{% if places is defined %}
|
||||
<div class="alert alert-warning" style="font-size:0.95em;">
|
||||
|
@ -187,6 +188,10 @@
|
|||
|
||||
</div>
|
||||
|
||||
{% if theme == 'bicycle_parking' %}
|
||||
{% include 'admin/_followup_bicycle_parking_extra.html.twig' %}
|
||||
{% endif %}
|
||||
|
||||
|
||||
{% if overpass_query is defined %}
|
||||
<a href="https://overpass-turbo.eu/?Q={{ overpass_query|url_encode }}" target="_blank" class="btn btn-outline-primary">
|
||||
|
@ -199,12 +204,7 @@
|
|||
</a>
|
||||
{% else %}
|
||||
<div class="alert alert-info mb-3">Aucun objet sélectionné pour ce thème, rien à charger dans JOSM.</div>
|
||||
{% endif %}
|
||||
{# <label for="basemapSelect" class="form-label mb-1">Fond de carte :</label>
|
||||
<select id="basemapSelect" class="form-select">
|
||||
<option value="streets">MapTiler Streets</option>
|
||||
<option value="satellite">BD Ortho IGN</option>
|
||||
</select> #}
|
||||
{% endif %}
|
||||
|
||||
<div id="themeMap"></div>
|
||||
|
||||
|
@ -290,6 +290,7 @@
|
|||
<a href="https://forum.openstreetmap.fr/t/osm-mon-commerce/34403/11" class="btn btn-info suggestion-footer-btn mt-4 mb-2" target="_blank" rel="noopener">
|
||||
<i class="bi bi-chat-dots"></i> Faire une suggestion
|
||||
</a>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block javascripts %}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue