osm-labo/templates/admin/fraicheur_histogramme.html.twig
2025-06-26 23:40:37 +02:00

443 lines
No EOL
26 KiB
Twig

{% extends 'base.html.twig' %}
{% block title %}Histogramme de fraîcheur OSM{% endblock %}
{% block body %}
<div class="container mt-4">
<h1>Histogramme de fraîcheur des données OSM</h1>
<p>La fraîcheur des données agrère les informations de dernière modification de chaque lieu. Cette page montre les liens entre budget des villes, nombre d'habitants et budget par habitant et par lieu.</p>
<h2>Par année</h2>
<canvas id="fraicheurHistogrammeAnnee" width="800" height="400" style="max-width:100%; margin: 20px 0;"></canvas>
<h2>Par trimestre</h2>
<canvas id="fraicheurHistogrammeTrimestre" width="800" height="400" style="max-width:100%; margin: 20px 0;"></canvas>
<h2>Par mois</h2>
<canvas id="fraicheurHistogramme" width="800" height="400" style="max-width:100%; margin: 20px 0;"></canvas>
<h2>Distribution des villes selon le nombre d'habitants par lieu (par pas de 10)</h2>
<canvas id="distributionHabitantsParLieu" width="800" height="400" style="max-width:100%; margin: 20px 0;"></canvas>
<p>
Une ville avec un petit nombre d'habitants par lieu sera très fournie en services divers et variés, sur la gauche de ce graphique de répartition. Survolez pour voir les noms des villes les plus civilisées. À droite du graphique, vous êtes plus proche d'habiter dans un désert.
</p>
<h2>Distribution des villes selon le budget par habitant (par pas de 50€)</h2>
<canvas id="distributionBudgetParHabitant" width="800" height="400" style="max-width:100%; margin: 20px 0;"></canvas>
<p>
Ce graphique montre la répartition des villes selon leur budget annuel par habitant. Les villes avec un budget élevé par habitant sont sur la droite du graphique.
</p>
<h2>Distribution des villes selon l'écart à la moyenne du budget par habitant (par pas de 10%)</h2>
<canvas id="distributionEcartBudget" width="800" height="400" style="max-width:100%; margin: 20px 0;"></canvas>
<p>
Ce graphique montre les villes sous-cartographiées selon l'écart à la moyenne du budget par habitant. Les villes avec un écart négatif (gauche) ont un budget inférieur à la moyenne, celles avec un écart positif (droite) ont un budget supérieur à la moyenne.
</p>
<h2>Distribution des villes selon le budget par lieu (par pas de 5000€)</h2>
<canvas id="distributionBudgetParLieu" width="800" height="400" style="max-width:100%; margin: 20px 0;"></canvas>
<p>
Ce graphique montre la répartition des villes selon leur budget annuel par lieu. Les villes avec un budget élevé par commerce sont sur la droite du graphique.
</p>
<div id="fraicheurMeta" class="mb-3"></div>
<a href="{{ path('admin_fraicheur_calculate') }}" class="btn btn-primary">Régénérer les statistiques</a>
<a href="{{ path('admin_fraicheur_download') }}" class="btn btn-secondary">Télécharger le JSON des lieux</a>
<a href="{{ path('admin_distribution_villes_lieux_par_habitant_download') }}" class="btn btn-secondary">Télécharger le JSON villes/lieux/habitant</a>
<a href="{{ path('admin_budget_download') }}" class="btn btn-secondary">Télécharger le JSON budget</a>
</div>
{% endblock %}
{% block javascripts %}
{{ parent() }}
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
fetch('/admin/fraicheur/download')
.then(r => r.json())
.then(data => {
if(data.error){
document.getElementById('fraicheurMeta').innerHTML = '<div class="alert alert-danger">'+data.error+'</div>';
return;
}
const canvasFraicheur = document.getElementById('fraicheurHistogramme');
if (canvasFraicheur) {
const ctx = canvasFraicheur.getContext('2d');
const labels = Object.keys(data.histogram);
const values = Object.values(data.histogram);
document.getElementById('fraicheurMeta').innerHTML =
'<b>Date de génération :</b> ' + data.generated_at + '<br>' +
'<b>Total lieux :</b> ' + data.total;
new Chart(ctx, {
type: 'bar',
data: {
labels: labels,
datasets: [{
label: 'Nombre de lieux modifiés ce mois',
data: values,
backgroundColor: 'rgba(54, 162, 235, 0.7)',
borderColor: 'rgba(54, 162, 235, 1)',
borderWidth: 1
}]
},
options: {
responsive: true,
plugins: {
legend: { display: false },
title: { display: true, text: 'Fraîcheur des données OSM (par mois)' }
},
scales: {
y: { beginAtZero: true, title: { display: true, text: 'Nombre de lieux' } },
x: { title: { display: true, text: 'Mois' } }
}
}
});
}
// Agrégation par trimestre
const trimestreAgg = {};
for(const key in data.histogram){
// key = YYYY-MM
const [year, month] = key.split('-');
const m = parseInt(month, 10);
let trimestre = 1;
if(m >= 4 && m <= 6) trimestre = 2;
else if(m >= 7 && m <= 9) trimestre = 3;
else if(m >= 10 && m <= 12) trimestre = 4;
const trimestreKey = `${year}-T${trimestre}`;
if(!trimestreAgg[trimestreKey]) trimestreAgg[trimestreKey] = 0;
trimestreAgg[trimestreKey] += data.histogram[key];
}
const trimestreLabels = Object.keys(trimestreAgg).sort();
const trimestreValues = trimestreLabels.map(k => trimestreAgg[k]);
const canvasTrim = document.getElementById('fraicheurHistogrammeTrimestre');
if (canvasTrim) {
const ctxTrim = canvasTrim.getContext('2d');
new Chart(ctxTrim, {
type: 'bar',
data: {
labels: trimestreLabels,
datasets: [{
label: 'Nombre de lieux modifiés ce trimestre',
data: trimestreValues,
backgroundColor: 'rgba(255, 159, 64, 0.7)',
borderColor: 'rgba(255, 159, 64, 1)',
borderWidth: 1
}]
},
options: {
responsive: true,
plugins: {
legend: { display: false },
title: { display: true, text: 'Fraîcheur des données OSM (par trimestre)' }
},
scales: {
y: { beginAtZero: true, title: { display: true, text: 'Nombre de lieux' } },
x: { title: { display: true, text: 'Trimestre' } }
}
}
});
}
// Agrégation par année
const anneeAgg = {};
for(const key in data.histogram){
// key = YYYY-MM
const [year] = key.split('-');
if(!anneeAgg[year]) anneeAgg[year] = 0;
anneeAgg[year] += data.histogram[key];
}
const anneeLabels = Object.keys(anneeAgg).sort();
const anneeValues = anneeLabels.map(k => anneeAgg[k]);
const canvasAnnee = document.getElementById('fraicheurHistogrammeAnnee');
if (canvasAnnee) {
const ctxAnnee = canvasAnnee.getContext('2d');
new Chart(ctxAnnee, {
type: 'bar',
data: {
labels: anneeLabels,
datasets: [{
label: 'Nombre de lieux modifiés cette année',
data: anneeValues,
backgroundColor: 'rgba(75, 192, 192, 0.7)',
borderColor: 'rgba(75, 192, 192, 1)',
borderWidth: 1
}]
},
options: {
responsive: true,
plugins: {
legend: { display: false },
title: { display: true, text: 'Fraîcheur des données OSM (par année)' }
},
scales: {
y: { beginAtZero: true, title: { display: true, text: 'Nombre de lieux' } },
x: { title: { display: true, text: 'Année' } }
}
}
});
}
// Histogramme distribution villes/lieux/habitant
fetch('/admin/distribution_villes_lieux_par_habitant_download')
.then(r => r.json())
.then(data => {
if(data.error){
const canvasVLPH = document.getElementById('distributionVillesLieuxParHabitant');
if(canvasVLPH) canvasVLPH.insertAdjacentHTML('afterend', '<div class="alert alert-danger">Erreur de chargement des données : '+data.error+'</div>');
return;
}
// Histogramme habitants par lieu (pas de 10)
fetch('/admin/distribution_villes_lieux_par_habitant_villes')
.then(r2 => r2.json())
.then(villesData => {
// Histogramme habitants par lieu
const canvasHPL = document.getElementById('distributionHabitantsParLieu');
if(canvasHPL && data.histogram_10){
const ctxHPL = canvasHPL.getContext('2d');
const labelsHPL = Object.keys(data.histogram_10);
const valuesHPL = Object.values(data.histogram_10);
const villesByBin10 = villesData.villes_by_bin_10 || {};
new Chart(ctxHPL, {
type: 'bar',
data: {
labels: labelsHPL,
datasets: [{
label: "Nombre de villes",
data: valuesHPL,
backgroundColor: 'rgba(255, 99, 132, 0.7)',
borderColor: 'rgba(255, 99, 132, 1)',
borderWidth: 1
}]
},
options: {
responsive: true,
plugins: {
legend: { display: false },
title: { display: true, text: "Distribution des villes par habitants/lieu (par pas de 10)" },
tooltip: {
callbacks: {
afterBody: function(context) {
const bin = context[0].label;
const villes = villesByBin10[bin] || [];
if(villes.length > 0){
return ['Villes :'].concat(villes.map(v => '• ' + v));
}
return [];
}
}
}
},
scales: {
y: { beginAtZero: true, title: { display: true, text: 'Nombre de villes' } },
x: { title: { display: true, text: 'Habitants par lieu (arrondi à 10)' } }
}
}
});
}
// Histogramme lieux/habitant (pas de 0.01)
const canvasVLPH = document.getElementById('distributionVillesLieuxParHabitant');
if(canvasVLPH && data.histogram_001){
const ctx = canvasVLPH.getContext('2d');
const labels = Object.keys(data.histogram_001);
const values = Object.values(data.histogram_001);
const villesByBin001 = villesData.villes_by_bin_001 || {};
new Chart(ctx, {
type: 'bar',
data: {
labels: labels,
datasets: [{
label: "Nombre de villes",
data: values,
backgroundColor: 'rgba(153, 102, 255, 0.7)',
borderColor: 'rgba(153, 102, 255, 1)',
borderWidth: 1
}]
},
options: {
responsive: true,
plugins: {
legend: { display: false },
title: { display: true, text: "Distribution des villes par lieux/habitant (par pas de 0,01)" },
tooltip: {
callbacks: {
afterBody: function(context) {
const bin = context[0].label;
const villes = villesByBin001[bin] || [];
if(villes.length > 0){
return ['Villes :'].concat(villes.map(v => '• ' + v));
}
return [];
}
}
}
},
scales: {
y: { beginAtZero: true, title: { display: true, text: 'Nombre de villes' } },
x: { title: { display: true, text: 'Lieux par habitant (arrondi à 0,01)' } }
}
}
});
}
});
});
// Histogrammes budgétaires
fetch('/admin/budget/download')
.then(r => r.json())
.then(data => {
if(data.error){
const canvasBudget = document.getElementById('distributionBudgetParHabitant');
if(canvasBudget) canvasBudget.insertAdjacentHTML('afterend', '<div class="alert alert-danger">Erreur de chargement des données budgétaires : '+data.error+'</div>');
return;
}
fetch('/admin/budget/villes')
.then(r2 => r2.json())
.then(villesData => {
// Histogramme budget par habitant
const canvasBudget = document.getElementById('distributionBudgetParHabitant');
if(canvasBudget && data.histogram_budget_par_habitant){
const ctxBudget = canvasBudget.getContext('2d');
const labelsBudget = Object.keys(data.histogram_budget_par_habitant);
const valuesBudget = Object.values(data.histogram_budget_par_habitant);
const villesByBinBudget = villesData.villes_by_bin_budget || {};
new Chart(ctxBudget, {
type: 'bar',
data: {
labels: labelsBudget,
datasets: [{
label: "Nombre de villes",
data: valuesBudget,
backgroundColor: 'rgba(54, 162, 235, 0.7)',
borderColor: 'rgba(54, 162, 235, 1)',
borderWidth: 1
}]
},
options: {
responsive: true,
plugins: {
legend: { display: false },
title: { display: true, text: "Distribution des villes par budget par habitant (par pas de 50€)" },
tooltip: {
callbacks: {
afterBody: function(context) {
const bin = context[0].label;
const villes = villesByBinBudget[bin] || [];
if(villes.length > 0){
return ['Villes :'].concat(villes.map(v => '• ' + v));
}
return [];
}
}
}
},
scales: {
y: { beginAtZero: true, title: { display: true, text: 'Nombre de villes' } },
x: { title: { display: true, text: 'Budget par habitant (€, arrondi à 50)' } }
}
}
});
}
// Histogramme écart à la moyenne
const canvasEcart = document.getElementById('distributionEcartBudget');
if(canvasEcart && data.histogram_ecart_moyenne){
const ctxEcart = canvasEcart.getContext('2d');
const labelsEcart = Object.keys(data.histogram_ecart_moyenne);
const valuesEcart = Object.values(data.histogram_ecart_moyenne);
const villesByBinEcart = villesData.villes_by_bin_ecart || {};
new Chart(ctxEcart, {
type: 'bar',
data: {
labels: labelsEcart,
datasets: [{
label: "Nombre de villes",
data: valuesEcart,
backgroundColor: 'rgba(255, 206, 86, 0.7)',
borderColor: 'rgba(255, 206, 86, 1)',
borderWidth: 1
}]
},
options: {
responsive: true,
plugins: {
legend: { display: false },
title: { display: true, text: "Distribution des villes par écart à la moyenne du budget par habitant (par pas de 10%)" },
tooltip: {
callbacks: {
afterBody: function(context) {
const bin = context[0].label;
const villes = villesByBinEcart[bin] || [];
if(villes.length > 0){
return ['Villes :'].concat(villes.map(v => '• ' + v));
}
return [];
}
}
}
},
scales: {
y: { beginAtZero: true, title: { display: true, text: 'Nombre de villes' } },
x: { title: { display: true, text: 'Écart à la moyenne (%)' } }
}
}
});
}
// Histogramme budget par lieu
const canvasBudgetParLieu = document.getElementById('distributionBudgetParLieu');
if(canvasBudgetParLieu && data.histogram_budget_par_lieu){
const ctxBudgetParLieu = canvasBudgetParLieu.getContext('2d');
const labelsBudgetParLieu = Object.keys(data.histogram_budget_par_lieu);
const valuesBudgetParLieu = Object.values(data.histogram_budget_par_lieu);
const villesByBinBudgetParLieu = data.villes_by_bin_budget_par_lieu || {};
new Chart(ctxBudgetParLieu, {
type: 'bar',
data: {
labels: labelsBudgetParLieu,
datasets: [{
label: "Nombre de villes",
data: valuesBudgetParLieu,
backgroundColor: 'rgba(255, 99, 71, 0.7)',
borderColor: 'rgba(255, 99, 71, 1)',
borderWidth: 1
}]
},
options: {
responsive: true,
plugins: {
legend: { display: false },
title: { display: true, text: "Distribution des villes par budget par lieu (par pas de 5000€)" },
tooltip: {
callbacks: {
afterBody: function(context) {
const bin = context[0].label;
const villes = villesByBinBudgetParLieu[bin] || [];
if(villes.length > 0){
return ['Villes :'].concat(villes.map(v => '• ' + v));
}
return [];
}
}
}
},
scales: {
y: { beginAtZero: true, title: { display: true, text: 'Nombre de villes' } },
x: { title: { display: true, text: 'Budget par lieu (€, arrondi à 5000)' } }
}
}
});
}
});
});
});
});
</script>
{% endblock %}