2025-07-05 12:37:01 +02:00
|
|
|
|
{% extends 'base_embed.html.twig' %}
|
|
|
|
|
|
|
|
|
|
{% block title %}Graphique {{ theme_label }} - {{ stats.name }}{% endblock %}
|
|
|
|
|
|
|
|
|
|
{% block stylesheets %}
|
|
|
|
|
{{ parent() }}
|
2025-07-05 15:25:33 +02:00
|
|
|
|
<link href='{{ asset('js/maplibre/maplibre-gl.css') }}' rel='stylesheet'/>
|
2025-07-05 12:37:01 +02:00
|
|
|
|
<style>
|
|
|
|
|
.chart-container {
|
|
|
|
|
width: 100%;
|
|
|
|
|
height: 400px;
|
|
|
|
|
margin: 20px 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.stats-header {
|
|
|
|
|
background: #f8f9fa;
|
|
|
|
|
padding: 15px;
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
margin-bottom: 20px;
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-05 13:00:00 +02:00
|
|
|
|
.action-bar {
|
|
|
|
|
background: white;
|
|
|
|
|
padding: 15px;
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
border: 1px solid #dee2e6;
|
|
|
|
|
margin-bottom: 20px;
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
gap: 10px;
|
|
|
|
|
align-items: center;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.action-bar .btn {
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-05 12:37:01 +02:00
|
|
|
|
.stats-grid {
|
|
|
|
|
display: grid;
|
|
|
|
|
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
|
|
|
|
gap: 15px;
|
|
|
|
|
margin-bottom: 20px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.stat-card {
|
|
|
|
|
background: white;
|
|
|
|
|
padding: 15px;
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
border: 1px solid #dee2e6;
|
|
|
|
|
text-align: center;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.stat-value {
|
|
|
|
|
font-size: 24px;
|
|
|
|
|
font-weight: bold;
|
|
|
|
|
color: #0d6efd;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.stat-label {
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
color: #6c757d;
|
|
|
|
|
margin-top: 5px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.chart-tabs {
|
|
|
|
|
display: flex;
|
|
|
|
|
margin-bottom: 20px;
|
|
|
|
|
border-bottom: 1px solid #dee2e6;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.chart-tab {
|
|
|
|
|
padding: 10px 20px;
|
|
|
|
|
background: none;
|
|
|
|
|
border: none;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
border-bottom: 3px solid transparent;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.chart-tab.active {
|
|
|
|
|
border-bottom-color: #0d6efd;
|
|
|
|
|
color: #0d6efd;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.chart-content {
|
|
|
|
|
display: none;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.chart-content.active {
|
|
|
|
|
display: block;
|
|
|
|
|
}
|
2025-07-05 15:25:33 +02:00
|
|
|
|
|
|
|
|
|
#themeMap {
|
|
|
|
|
height: 400px;
|
|
|
|
|
width: 100%;
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
margin-bottom: 1.5rem;
|
|
|
|
|
}
|
|
|
|
|
.maplibregl-popup-content {
|
|
|
|
|
font-size: 0.95em;
|
|
|
|
|
}
|
|
|
|
|
.btn-josm {
|
|
|
|
|
margin-bottom: 1rem;
|
|
|
|
|
}
|
2025-07-05 12:37:01 +02:00
|
|
|
|
</style>
|
|
|
|
|
{% endblock %}
|
|
|
|
|
|
|
|
|
|
{% block body %}
|
|
|
|
|
<div class="container-fluid">
|
2025-07-05 15:25:33 +02:00
|
|
|
|
{# DEBUG : Affichage des objets Place trouvés pour cette ville #}
|
|
|
|
|
{% if places is defined %}
|
|
|
|
|
<div class="alert alert-warning" style="font-size:0.95em;">
|
|
|
|
|
<b>DEBUG : Objets Place trouvés pour cette ville (avant filtrage)</b><br>
|
|
|
|
|
<table class="table table-sm table-bordered mt-2 mb-0">
|
|
|
|
|
<thead><tr>
|
|
|
|
|
<th>#</th><th>id</th><th>main_tag</th><th>osm_kind</th><th>nom</th><th>lat</th><th>lon</th>
|
|
|
|
|
</tr></thead>
|
|
|
|
|
<tbody>
|
|
|
|
|
{% for p in places %}
|
|
|
|
|
<tr>
|
|
|
|
|
<td>{{ loop.index }}</td>
|
|
|
|
|
<td>{{ p.getOsmId() }}</td>
|
|
|
|
|
<td>{{ p.getMainTag() }}</td>
|
|
|
|
|
<td>{{ p.getOsmKind() }}</td>
|
|
|
|
|
<td>{{ p.getName() }}</td>
|
|
|
|
|
<td>{{ p.getLat() }}</td>
|
|
|
|
|
<td>{{ p.getLon() }}</td>
|
|
|
|
|
</tr>
|
|
|
|
|
{% endfor %}
|
|
|
|
|
</tbody>
|
|
|
|
|
</table>
|
|
|
|
|
</div>
|
|
|
|
|
{% endif %}
|
2025-07-05 12:37:01 +02:00
|
|
|
|
<div class="stats-header">
|
2025-07-05 13:00:00 +02:00
|
|
|
|
<div class="d-flex justify-content-between align-items-start">
|
|
|
|
|
<div>
|
|
|
|
|
<h2>
|
|
|
|
|
<i class="bi {{ icons[theme]|default('bi-question-circle') }}"></i>
|
|
|
|
|
{{ theme_label }} - {{ stats.name }}
|
|
|
|
|
</h2>
|
|
|
|
|
<p class="mb-0">Code INSEE: {{ stats.zone }}</p>
|
|
|
|
|
</div>
|
|
|
|
|
<a href="{{ path('app_admin_stats', {'insee_code': stats.zone}) }}" class="btn btn-outline-secondary">
|
|
|
|
|
<i class="bi bi-arrow-left"></i> Retour aux stats
|
|
|
|
|
</a>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="action-bar">
|
|
|
|
|
<a href="{{ path('app_admin_stats', {'insee_code': stats.zone}) }}" class="btn btn-primary">
|
|
|
|
|
<i class="bi bi-bar-chart"></i> Stats de la ville
|
|
|
|
|
</a>
|
|
|
|
|
<a href="{{ path('app_admin_labourer', {'insee_code': stats.zone}) }}" class="btn btn-warning">
|
|
|
|
|
<i class="bi bi-arrow-clockwise"></i> Labourer la ville
|
|
|
|
|
</a>
|
|
|
|
|
<a href="{{ path('admin_followup_graph', {'insee_code': stats.zone}) }}" class="btn btn-info">
|
|
|
|
|
<i class="bi bi-graph-up"></i> Suivi OSM (graphes)
|
|
|
|
|
</a>
|
|
|
|
|
<a href="{{ path('admin_followup_embed_graph', {'insee_code': stats.zone, 'theme': theme}) }}" class="btn btn-success" target="_blank">
|
|
|
|
|
<i class="bi bi-box-arrow-up-right"></i> Graphe embedded
|
|
|
|
|
</a>
|
|
|
|
|
<a href="{{ path('app_admin') }}" class="btn btn-secondary">
|
|
|
|
|
<i class="bi bi-house"></i> Accueil admin
|
|
|
|
|
</a>
|
2025-07-12 12:53:06 +02:00
|
|
|
|
<a href="{{ path('app_public_stats_evolutions', {'insee_code': stats.zone}) }}" class="btn btn-outline-primary">
|
|
|
|
|
<i class="bi bi-clock-history"></i> Évolutions temporelles
|
|
|
|
|
</a>
|
|
|
|
|
<a href="{{ path('admin_street_completion', {'insee_code': stats.zone}) }}" class="btn btn-outline-success">
|
|
|
|
|
<i class="bi bi-signpost"></i> Complétion des rues
|
|
|
|
|
</a>
|
2025-07-05 12:37:01 +02:00
|
|
|
|
</div>
|
|
|
|
|
|
2025-07-05 15:25:33 +02:00
|
|
|
|
{% if josm_url %}
|
|
|
|
|
<a href="{{ josm_url }}" class="btn btn-outline-dark btn-josm" target="_blank">
|
|
|
|
|
<i class="bi bi-box-arrow-up-right"></i> Ouvrir tous les objets dans JOSM
|
|
|
|
|
</a>
|
|
|
|
|
{% else %}
|
|
|
|
|
<div class="alert alert-info mb-3">Aucun objet sélectionné pour ce thème, rien à charger dans JOSM.</div>
|
|
|
|
|
{% endif %}
|
|
|
|
|
<div id="themeMap"></div>
|
2025-07-12 13:04:39 +02:00
|
|
|
|
{% if geojson is defined %}
|
|
|
|
|
{# On n'utilise plus geojson côté PHP, la carte sera alimentée dynamiquement via Overpass en JS #}
|
|
|
|
|
{% endif %}
|
2025-07-05 16:15:56 +02:00
|
|
|
|
{% if overpass_query is defined %}
|
2025-07-12 13:04:39 +02:00
|
|
|
|
<div class="mb-3">
|
|
|
|
|
<a href="https://overpass-turbo.eu/?Q={{ overpass_query|url_encode }}" target="_blank" class="btn btn-outline-primary mt-2">
|
|
|
|
|
<i class="bi bi-geo"></i> Vérifier sur Overpass Turbo
|
|
|
|
|
</a>
|
|
|
|
|
</div>
|
2025-07-05 16:15:56 +02:00
|
|
|
|
{% endif %}
|
|
|
|
|
|
2025-07-05 12:37:01 +02:00
|
|
|
|
<div class="stats-grid">
|
|
|
|
|
<div class="stat-card">
|
|
|
|
|
<div class="stat-value" id="currentCount">-</div>
|
|
|
|
|
<div class="stat-label">Nombre actuel</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="stat-card">
|
|
|
|
|
<div class="stat-value" id="currentCompletion">-</div>
|
|
|
|
|
<div class="stat-label">Complétion actuelle</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="stat-card">
|
|
|
|
|
<div class="stat-value" id="dataPoints">-</div>
|
|
|
|
|
<div class="stat-label">Points de données</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="stat-card">
|
|
|
|
|
<div class="stat-value" id="lastUpdate">-</div>
|
|
|
|
|
<div class="stat-label">Dernière mise à jour</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
2025-07-05 16:15:56 +02:00
|
|
|
|
<div class="row mb-3">
|
|
|
|
|
<div class="col-md-6">
|
|
|
|
|
{# <div class="card p-3">
|
|
|
|
|
<h5>Progression</h5>
|
|
|
|
|
<table class="table table-sm mb-0">
|
|
|
|
|
<thead><tr><th>Période</th><th>Nombre</th><th>Complétion (%)</th></tr></thead>
|
|
|
|
|
<tbody>
|
|
|
|
|
<tr><td>7 jours</td><td>{{ progressions.count['7j'] is not null ? '%+d'|format(progressions.count['7j']) : '–' }}</td><td>{{ progressions.completion['7j'] is not null ? '%+.1f'|format(progressions.completion['7j']) : '–' }}</td></tr>
|
|
|
|
|
<tr><td>30 jours</td><td>{{ progressions.count['30j'] is not null ? '%+d'|format(progressions.count['30j']) : '–' }}</td><td>{{ progressions.completion['30j'] is not null ? '%+.1f'|format(progressions.completion['30j']) : '–' }}</td></tr>
|
|
|
|
|
<tr><td>6 mois</td><td>{{ progressions.count['6m'] is not null ? '%+d'|format(progressions.count['6m']) : '–' }}</td><td>{{ progressions.completion['6m'] is not null ? '%+.1f'|format(progressions.completion['6m']) : '–' }}</td></tr>
|
|
|
|
|
<tr><td>1 an</td><td>{{ progressions.count['1a'] is not null ? '%+d'|format(progressions.count['1a']) : '–' }}</td><td>{{ progressions.completion['1a'] is not null ? '%+.1f'|format(progressions.completion['1a']) : '–' }}</td></tr>
|
|
|
|
|
</tbody>
|
|
|
|
|
</table>
|
|
|
|
|
</div> #}
|
|
|
|
|
</div>
|
|
|
|
|
<div class="col-md-6 d-flex align-items-end">
|
|
|
|
|
<div class="ms-auto">
|
|
|
|
|
<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>
|
|
|
|
|
</div>
|
2025-07-05 12:37:01 +02:00
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
2025-07-05 16:15:56 +02:00
|
|
|
|
<div class="chart-container">
|
|
|
|
|
<canvas id="themeChart"></canvas>
|
2025-07-05 12:37:01 +02:00
|
|
|
|
</div>
|
2025-07-05 17:21:18 +02:00
|
|
|
|
|
|
|
|
|
{% if completion_tags is defined and completion_tags[theme] is defined %}
|
|
|
|
|
<div class="card mt-4">
|
|
|
|
|
<div class="card-header">
|
|
|
|
|
<i class="bi bi-info-circle"></i> Critères de complétion attendus pour ce thème
|
|
|
|
|
</div>
|
|
|
|
|
<div class="card-body p-2">
|
|
|
|
|
<ul class="mb-0">
|
|
|
|
|
{% for tag in completion_tags[theme] %}
|
|
|
|
|
<li><code>{{ tag }}</code></li>
|
|
|
|
|
{% else %}
|
|
|
|
|
<li><span class="text-muted">Aucun critère défini</span></li>
|
|
|
|
|
{% endfor %}
|
|
|
|
|
</ul>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
{% endif %}
|
2025-07-05 12:37:01 +02:00
|
|
|
|
</div>
|
|
|
|
|
{% endblock %}
|
|
|
|
|
|
|
|
|
|
{% block javascripts %}
|
|
|
|
|
{{ parent() }}
|
2025-07-05 15:25:33 +02:00
|
|
|
|
<script src='{{ asset('js/maplibre/maplibre-gl.js') }}'></script>
|
|
|
|
|
<script>
|
2025-07-12 13:04:39 +02:00
|
|
|
|
const overpassQuery = `{% if overpass_query is defined %}{{ overpass_query|e('js') }}{% endif %}`;
|
2025-07-05 15:25:33 +02:00
|
|
|
|
const mapToken = '{{ maptiler_token }}';
|
2025-07-12 13:04:39 +02:00
|
|
|
|
let mapInstance = null;
|
2025-07-05 15:25:33 +02:00
|
|
|
|
document.addEventListener('DOMContentLoaded', function() {
|
2025-07-12 13:04:39 +02:00
|
|
|
|
if (!overpassQuery) {
|
|
|
|
|
document.getElementById('themeMap').innerHTML = '<div class="alert alert-warning">Aucune requête Overpass disponible pour ce thème.</div>';
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
// Initialiser la carte
|
|
|
|
|
mapInstance = new maplibregl.Map({
|
|
|
|
|
container: 'themeMap',
|
|
|
|
|
style: 'https://api.maptiler.com/maps/streets/style.json?key=' + mapToken,
|
|
|
|
|
center: [2, 48],
|
|
|
|
|
zoom: 13
|
|
|
|
|
});
|
|
|
|
|
mapInstance.addControl(new maplibregl.NavigationControl());
|
|
|
|
|
// Requête Overpass
|
|
|
|
|
fetch('https://overpass-api.de/api/interpreter', {
|
|
|
|
|
method: 'POST',
|
|
|
|
|
body: overpassQuery,
|
|
|
|
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
|
|
|
|
|
})
|
|
|
|
|
.then(response => response.json())
|
|
|
|
|
.then(data => {
|
|
|
|
|
if (!data.elements || data.elements.length === 0) {
|
|
|
|
|
document.getElementById('themeMap').innerHTML = '<div class="alert alert-warning">Aucun objet trouvé via Overpass pour ce thème.</div>';
|
|
|
|
|
return;
|
2025-07-05 15:25:33 +02:00
|
|
|
|
}
|
2025-07-12 13:04:39 +02:00
|
|
|
|
// Centrage carte
|
|
|
|
|
let lats = [], lons = [];
|
|
|
|
|
data.elements.forEach(e => {
|
|
|
|
|
if (e.lat && e.lon) {
|
|
|
|
|
lats.push(e.lat); lons.push(e.lon);
|
|
|
|
|
} else if (e.type === 'way' && e.center) {
|
|
|
|
|
lats.push(e.center.lat); lons.push(e.center.lon);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
if (lats.length && lons.length) {
|
|
|
|
|
const avgLat = lats.reduce((a,b)=>a+b,0)/lats.length;
|
|
|
|
|
const avgLon = lons.reduce((a,b)=>a+b,0)/lons.length;
|
|
|
|
|
mapInstance.setCenter([avgLon, avgLat]);
|
2025-07-05 15:25:33 +02:00
|
|
|
|
}
|
2025-07-12 13:04:39 +02:00
|
|
|
|
// Marqueurs
|
|
|
|
|
data.elements.forEach(e => {
|
|
|
|
|
let lat = e.lat, lon = e.lon;
|
|
|
|
|
if (!lat && !lon && e.type === 'way' && e.center) {
|
|
|
|
|
lat = e.center.lat; lon = e.center.lon;
|
|
|
|
|
}
|
|
|
|
|
if (!lat || !lon) return;
|
|
|
|
|
const popupHtml = `<div style='min-width:180px'>
|
|
|
|
|
<strong>${e.tags && e.tags.name ? e.tags.name : '(sans nom)'}</strong><br>
|
|
|
|
|
<span class='text-muted'>${e.type} ${e.id}</span><br>
|
|
|
|
|
<span style='font-size:0.95em;'>${e.tags ? Object.entries(e.tags).map(([k,v]) => `<span><b>${k}</b>: ${v}</span>`).join('<br>') : ''}</span><br>
|
|
|
|
|
<a href='https://www.openstreetmap.org/${e.type}/${e.id}' target='_blank'>Voir sur OSM</a>
|
|
|
|
|
</div>`;
|
|
|
|
|
new maplibregl.Marker({ color: '#198754' })
|
|
|
|
|
.setLngLat([lon, lat])
|
|
|
|
|
.setPopup(new maplibregl.Popup({ offset: 18 }).setHTML(popupHtml))
|
|
|
|
|
.addTo(mapInstance);
|
|
|
|
|
});
|
|
|
|
|
})
|
|
|
|
|
.catch(err => {
|
|
|
|
|
document.getElementById('themeMap').innerHTML = '<div class="alert alert-danger">Erreur lors de la requête Overpass : ' + err + '</div>';
|
|
|
|
|
});
|
2025-07-05 15:25:33 +02:00
|
|
|
|
});
|
|
|
|
|
</script>
|
2025-07-05 12:37:01 +02:00
|
|
|
|
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
|
|
|
|
<script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-date-fns/dist/chartjs-adapter-date-fns.bundle.min.js"></script>
|
|
|
|
|
|
|
|
|
|
<script>
|
|
|
|
|
const countData = {{ count_data|raw }};
|
|
|
|
|
const completionData = {{ completion_data|raw }};
|
|
|
|
|
|
|
|
|
|
// Mettre à jour les statistiques
|
|
|
|
|
function updateStats() {
|
|
|
|
|
if (countData.length > 0) {
|
|
|
|
|
const latestCount = countData[countData.length - 1];
|
|
|
|
|
document.getElementById('currentCount').textContent = latestCount.value;
|
|
|
|
|
document.getElementById('lastUpdate').textContent = new Date(latestCount.date).toLocaleDateString('fr-FR');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (completionData.length > 0) {
|
|
|
|
|
const latestCompletion = completionData[completionData.length - 1];
|
|
|
|
|
document.getElementById('currentCompletion').textContent = latestCompletion.value + '%';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
document.getElementById('dataPoints').textContent = Math.max(countData.length, completionData.length);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Configuration commune pour les graphiques
|
|
|
|
|
const commonOptions = {
|
|
|
|
|
responsive: true,
|
|
|
|
|
maintainAspectRatio: false,
|
|
|
|
|
plugins: {
|
|
|
|
|
legend: {
|
|
|
|
|
display: false
|
|
|
|
|
},
|
|
|
|
|
tooltip: {
|
|
|
|
|
mode: 'index',
|
|
|
|
|
intersect: false,
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
scales: {
|
|
|
|
|
x: {
|
|
|
|
|
type: 'time',
|
|
|
|
|
time: {
|
|
|
|
|
unit: 'day',
|
|
|
|
|
displayFormats: {
|
|
|
|
|
day: 'dd/MM/yyyy'
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
title: {
|
|
|
|
|
display: true,
|
|
|
|
|
text: 'Date'
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
y: {
|
|
|
|
|
beginAtZero: true,
|
|
|
|
|
title: {
|
|
|
|
|
display: true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
interaction: {
|
|
|
|
|
mode: 'nearest',
|
|
|
|
|
axis: 'x',
|
|
|
|
|
intersect: false
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2025-07-05 16:15:56 +02:00
|
|
|
|
// Graphique fusionné
|
|
|
|
|
const ctx = document.getElementById('themeChart').getContext('2d');
|
|
|
|
|
new Chart(ctx, {
|
2025-07-05 12:37:01 +02:00
|
|
|
|
type: 'line',
|
|
|
|
|
data: {
|
2025-07-05 16:15:56 +02:00
|
|
|
|
datasets: [
|
|
|
|
|
{
|
|
|
|
|
label: "Nombre d'objets",
|
|
|
|
|
data: countData.map(d => ({ x: new Date(d.date), y: d.value })),
|
|
|
|
|
borderColor: '#0d6efd',
|
|
|
|
|
backgroundColor: 'rgba(13, 110, 253, 0.1)',
|
|
|
|
|
borderWidth: 2,
|
|
|
|
|
fill: true,
|
|
|
|
|
tension: 0.1,
|
|
|
|
|
yAxisID: 'y1',
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
label: 'Pourcentage de complétion',
|
|
|
|
|
data: completionData.map(d => ({ x: new Date(d.date), y: d.value })),
|
|
|
|
|
borderColor: '#198754',
|
|
|
|
|
backgroundColor: 'rgba(25, 135, 84, 0.1)',
|
|
|
|
|
borderWidth: 2,
|
|
|
|
|
fill: true,
|
|
|
|
|
tension: 0.1,
|
|
|
|
|
yAxisID: 'y2',
|
2025-07-05 12:37:01 +02:00
|
|
|
|
}
|
2025-07-05 16:15:56 +02:00
|
|
|
|
]
|
2025-07-05 12:37:01 +02:00
|
|
|
|
},
|
|
|
|
|
options: {
|
2025-07-05 16:15:56 +02:00
|
|
|
|
responsive: true,
|
|
|
|
|
maintainAspectRatio: false,
|
|
|
|
|
plugins: {
|
|
|
|
|
legend: { display: true },
|
|
|
|
|
tooltip: { mode: 'index', intersect: false }
|
|
|
|
|
},
|
|
|
|
|
interaction: { mode: 'nearest', axis: 'x', intersect: false },
|
2025-07-05 12:37:01 +02:00
|
|
|
|
scales: {
|
2025-07-05 16:15:56 +02:00
|
|
|
|
x: {
|
|
|
|
|
type: 'time',
|
|
|
|
|
time: { unit: 'day', displayFormats: { day: 'dd/MM/yyyy' } },
|
|
|
|
|
title: { display: true, text: 'Date' }
|
|
|
|
|
},
|
|
|
|
|
y1: {
|
|
|
|
|
type: 'linear',
|
|
|
|
|
position: 'left',
|
|
|
|
|
title: { display: true, text: "Nombre d'objets" },
|
|
|
|
|
beginAtZero: true
|
|
|
|
|
},
|
|
|
|
|
y2: {
|
|
|
|
|
type: 'linear',
|
|
|
|
|
position: 'right',
|
|
|
|
|
title: { display: true, text: 'Complétion (%)' },
|
|
|
|
|
min: 0,
|
2025-07-05 12:37:01 +02:00
|
|
|
|
max: 100,
|
2025-07-05 16:15:56 +02:00
|
|
|
|
grid: { drawOnChartArea: false }
|
2025-07-05 12:37:01 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Initialiser les statistiques
|
|
|
|
|
updateStats();
|
2025-07-05 16:15:56 +02:00
|
|
|
|
|
|
|
|
|
// Ajout debug JS requête Overpass
|
|
|
|
|
{% if overpass_query is defined %}
|
|
|
|
|
console.log('[DEBUG][Overpass] Requête envoyée à Overpass :', `{{ overpass_query|e('js') }}`);
|
|
|
|
|
{% else %}
|
|
|
|
|
console.log('[DEBUG][Overpass] Aucune requête Overpass transmise à la page.');
|
|
|
|
|
{% endif %}
|
2025-07-05 12:37:01 +02:00
|
|
|
|
</script>
|
|
|
|
|
{% endblock %}
|