364 lines
		
	
	
		
			No EOL
		
	
	
		
			16 KiB
		
	
	
	
		
			Twig
		
	
	
	
	
	
			
		
		
	
	
			364 lines
		
	
	
		
			No EOL
		
	
	
		
			16 KiB
		
	
	
	
		
			Twig
		
	
	
	
	
	
| {% extends 'base.html.twig' %}
 | |
| 
 | |
| {% block title %}Graphe thématique : {{ theme_label }} - {{ stats.name }}{% endblock %}
 | |
| 
 | |
| {% block stylesheets %}
 | |
|     {{ parent() }}
 | |
|     <link href='{{ asset('js/maplibre/maplibre-gl.css') }}' rel='stylesheet'/>
 | |
|     <style>
 | |
|         #themeMap {
 | |
|             height: 400px;
 | |
|             width: 100%;
 | |
|             border-radius: 8px;
 | |
|             margin-bottom: 1.5rem;
 | |
|         }
 | |
|         .maplibregl-popup-content {
 | |
|             font-size: 0.95em;
 | |
|         }
 | |
|         .osmose-popup-tag {
 | |
|             margin-bottom: 5px;
 | |
|         }
 | |
|         .osmose-popup-tag-key {
 | |
|             font-weight: bold;
 | |
|         }
 | |
|         .osmose-popup-buttons {
 | |
|             margin-top: 10px;
 | |
|             display: flex;
 | |
|             gap: 5px;
 | |
|         }
 | |
|     </style>
 | |
| {% endblock %}
 | |
| 
 | |
| {% block body %}
 | |
| <div class="container mt-4">
 | |
|     <div class="row">
 | |
|         <div class="col-12">
 | |
|             <div class="d-flex justify-content-between align-items-center mb-4">
 | |
|                 <div>
 | |
|                     <h1><i class="bi {{ icons[theme]|default('bi-question-circle') }}"></i> {{ theme_label }}</h1>
 | |
|                     <p class="text-muted mb-0">{{ stats.name }} ({{ stats.zone }})</p>
 | |
|                 </div>
 | |
|                 <div>
 | |
|                     <a href="{{ path('app_admin_stats', {'insee_code': stats.zone}) }}" class="btn btn-info me-2">
 | |
|                         <i class="bi bi-bar-chart"></i> Voir la page de la ville
 | |
|                     </a>
 | |
|                     <a href="{{ path('app_public_index') }}" class="btn btn-secondary">
 | |
|                         <i class="bi bi-arrow-left"></i> Retour à l'accueil
 | |
|                     </a>
 | |
|                 </div>
 | |
|             </div>
 | |
|             
 | |
|             <div class="card mb-4">
 | |
|                 <div class="card-header">
 | |
|                     <h3><i class="bi bi-map"></i> Carte des analyses Osmose</h3>
 | |
|                     <p class="mb-0">Suggestions d'intégration de {{ theme_label|lower }} basées sur les analyses Osmose</p>
 | |
|                 </div>
 | |
|                 <div class="card-body">
 | |
|                     <div id="themeMap"></div>
 | |
|                     <div class="alert alert-info">
 | |
|                         <i class="bi bi-info-circle"></i>
 | |
|                         <strong>Informations :</strong> Les points violets représentent des suggestions d'intégration de {{ theme_label|lower }} basées sur les analyses Osmose.
 | |
|                         Cliquez sur un point pour voir les détails et les actions possibles.
 | |
|                     </div>
 | |
|                 </div>
 | |
|             </div>
 | |
|             
 | |
|             <div class="card">
 | |
|                 <div class="card-header">
 | |
|                     <h3><i class="bi bi-graph-up"></i> Évolution du {{ theme_label|lower }}</h3>
 | |
|                     <p class="mb-0">Suivi de la progression du nombre d'objets et du taux de complétion</p>
 | |
|                 </div>
 | |
|                 <div class="card-body">
 | |
|                     <div class="row">
 | |
|                         <div class="col-md-6">
 | |
|                             <h5>Nombre d'objets</h5>
 | |
|                             <canvas id="countChart" height="300"></canvas>
 | |
|                         </div>
 | |
|                         <div class="col-md-6">
 | |
|                             <h5>Taux de complétion (%)</h5>
 | |
|                             <canvas id="completionChart" height="300"></canvas>
 | |
|                         </div>
 | |
|                     </div>
 | |
|                     
 | |
|                     <div class="mt-4">
 | |
|                         <div class="alert alert-info">
 | |
|                             <i class="bi bi-info-circle"></i>
 | |
|                             <strong>Informations :</strong> Ces graphiques montrent l'évolution du {{ theme_label|lower }} dans {{ stats.name }} au fil du temps.
 | |
|                             Le taux de complétion indique le pourcentage d'objets correctement renseignés.
 | |
|                         </div>
 | |
|                     </div>
 | |
|                 </div>
 | |
|             </div>
 | |
|         </div>
 | |
|     </div>
 | |
| </div>
 | |
| {% endblock %}
 | |
| 
 | |
| {% block javascripts %}
 | |
|     {{ parent() }}
 | |
|     <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script>
 | |
|     <script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-date-fns@3.0.0"></script>
 | |
|     <script src='{{ asset('js/maplibre/maplibre-gl.js') }}'></script>
 | |
|     <script>
 | |
|         // Mapping des thèmes vers les IDs d'items Osmose
 | |
|         const osmoseItemsMapping = {
 | |
|             'charging_station': [8410, 8411],
 | |
|             'school': [8031],
 | |
|             'healthcare': [8211, 7220, 8331],
 | |
|             'laboratory': [7240, 8351],
 | |
|             'police': [8190, 8191],
 | |
|             'defibrillator': [8370]
 | |
|             // Ajouter d'autres thèmes selon les besoins
 | |
|         };
 | |
| 
 | |
|         document.addEventListener('DOMContentLoaded', function() {
 | |
|             const countData = {{ count_data|raw }};
 | |
|             const completionData = {{ completion_data|raw }};
 | |
|             const currentTheme = '{{ theme }}';
 | |
|             const statsZone = '{{ stats.zone }}';
 | |
|             
 | |
|             // Initialiser la carte MapLibre
 | |
|             initMap(currentTheme, statsZone);
 | |
|             
 | |
|             // Graphique du nombre d'objets
 | |
|             const countCtx = document.getElementById('countChart').getContext('2d');
 | |
|             new Chart(countCtx, {
 | |
|                 type: 'line',
 | |
|                 data: {
 | |
|                     labels: countData.map(d => d.date),
 | |
|                     datasets: [{
 | |
|                         label: 'Nombre d\'objets',
 | |
|                         data: countData.map(d => d.value),
 | |
|                         borderColor: 'rgb(54, 162, 235)',
 | |
|                         backgroundColor: 'rgba(54, 162, 235, 0.1)',
 | |
|                         tension: 0.3,
 | |
|                         fill: false
 | |
|                     }]
 | |
|                 },
 | |
|                 options: {
 | |
|                     responsive: true,
 | |
|                     plugins: {
 | |
|                         title: {
 | |
|                             display: true,
 | |
|                             text: 'Évolution du nombre d\'objets'
 | |
|                         }
 | |
|                     },
 | |
|                     scales: {
 | |
|                         y: {
 | |
|                             beginAtZero: true,
 | |
|                             title: {
 | |
|                                 display: true,
 | |
|                                 text: 'Nombre d\'objets'
 | |
|                             }
 | |
|                         },
 | |
|                         x: {
 | |
|                             title: {
 | |
|                                 display: true,
 | |
|                                 text: 'Date'
 | |
|                             }
 | |
|                         }
 | |
|                     }
 | |
|                 }
 | |
|             });
 | |
|             
 | |
|             // Graphique du taux de complétion
 | |
|             const completionCtx = document.getElementById('completionChart').getContext('2d');
 | |
|             new Chart(completionCtx, {
 | |
|                 type: 'line',
 | |
|                 data: {
 | |
|                     labels: completionData.map(d => d.date),
 | |
|                     datasets: [{
 | |
|                         label: 'Taux de complétion (%)',
 | |
|                         data: completionData.map(d => d.value),
 | |
|                         borderColor: 'rgb(75, 192, 192)',
 | |
|                         backgroundColor: 'rgba(75, 192, 192, 0.1)',
 | |
|                         tension: 0.3,
 | |
|                         fill: false
 | |
|                     }]
 | |
|                 },
 | |
|                 options: {
 | |
|                     responsive: true,
 | |
|                     plugins: {
 | |
|                         title: {
 | |
|                             display: true,
 | |
|                             text: 'Évolution du taux de complétion'
 | |
|                         }
 | |
|                     },
 | |
|                     scales: {
 | |
|                         y: {
 | |
|                             beginAtZero: true,
 | |
|                             max: 100,
 | |
|                             title: {
 | |
|                                 display: true,
 | |
|                                 text: 'Taux de complétion (%)'
 | |
|                             }
 | |
|                         },
 | |
|                         x: {
 | |
|                             title: {
 | |
|                                 display: true,
 | |
|                                 text: 'Date'
 | |
|                             }
 | |
|                         }
 | |
|                     }
 | |
|                 }
 | |
|             });
 | |
|         });
 | |
| 
 | |
|         // Fonction pour initialiser la carte et charger les analyses Osmose
 | |
|         function initMap(theme, inseeCode) {
 | |
|             // Vérifier si le thème a des items Osmose associés
 | |
|             if (!osmoseItemsMapping[theme]) {
 | |
|                 document.getElementById('themeMap').innerHTML = '<div class="alert alert-warning">Aucune analyse Osmose disponible pour ce thème.</div>';
 | |
|                 return;
 | |
|             }
 | |
| 
 | |
|             // Initialiser la carte MapLibre
 | |
|             const mapToken = '{{ maptiler_token }}';
 | |
|             const map = new maplibregl.Map({
 | |
|                 container: 'themeMap',
 | |
|                 style: 'https://api.maptiler.com/maps/streets/style.json?key=' + mapToken,
 | |
|                 center: [2.3488, 48.8534], // Paris par défaut, sera ajusté en fonction des données
 | |
|                 zoom: 12
 | |
|             });
 | |
| 
 | |
|             map.addControl(new maplibregl.NavigationControl());
 | |
| 
 | |
|             // Charger les coordonnées de la commune à partir du code INSEE
 | |
|             fetch(`https://geo.api.gouv.fr/communes?code=${inseeCode}&fields=centre,nom,code,codesPostaux,population,surface,contour`)
 | |
|                 .then(response => response.json())
 | |
|                 .then(data => {
 | |
|                     if (data && data.length > 0) {
 | |
|                         const commune = data[0];
 | |
|                         if (commune.centre && commune.centre.coordinates) {
 | |
|                             // Centrer la carte sur la commune
 | |
|                             map.setCenter([commune.centre.coordinates[0], commune.centre.coordinates[1]]);
 | |
|                             
 | |
|                             // Calculer la bounding box pour la requête Osmose
 | |
|                             // Pour simplifier, on utilise un carré autour du centre
 | |
|                             const lon = commune.centre.coordinates[0];
 | |
|                             const lat = commune.centre.coordinates[1];
 | |
|                             const offset = 0.05; // Environ 5km
 | |
|                             const bbox = [
 | |
|                                 lon - offset, 
 | |
|                                 lat - offset, 
 | |
|                                 lon + offset, 
 | |
|                                 lat + offset
 | |
|                             ];
 | |
|                             
 | |
|                             // Charger les analyses Osmose
 | |
|                             loadOsmoseAnalyses(map, theme, bbox);
 | |
|                         }
 | |
|                     }
 | |
|                 })
 | |
|                 .catch(error => {
 | |
|                     console.error('Erreur lors du chargement des coordonnées de la commune:', error);
 | |
|                     document.getElementById('themeMap').innerHTML = '<div class="alert alert-danger">Erreur lors du chargement des coordonnées de la commune.</div>';
 | |
|                 });
 | |
|         }
 | |
| 
 | |
|         // Fonction pour charger les analyses Osmose
 | |
|         function loadOsmoseAnalyses(map, theme, bbox) {
 | |
|             const items = osmoseItemsMapping[theme];
 | |
|             if (!items || items.length === 0) return;
 | |
|             
 | |
|             const itemsParam = items.join(',');
 | |
|             const bboxParam = bbox.join(',');
 | |
|             const osmoseUrl = `https://osmose.openstreetmap.fr/api/0.3/issues?zoom=12&item=${itemsParam}&level=1,2,3&limit=500&bbox=${bboxParam}`;
 | |
|             
 | |
|             fetch(osmoseUrl)
 | |
|                 .then(response => response.json())
 | |
|                 .then(data => {
 | |
|                     if (!data.issues || data.issues.length === 0) {
 | |
|                         document.getElementById('themeMap').innerHTML += '<div class="alert alert-info mt-3">Aucune analyse Osmose trouvée pour ce thème dans cette zone.</div>';
 | |
|                         return;
 | |
|                     }
 | |
|                     
 | |
|                     // Ajouter les marqueurs pour chaque analyse
 | |
|                     data.issues.forEach(issue => {
 | |
|                         if (issue.lat && issue.lon) {
 | |
|                             // Créer un marqueur pour l'analyse
 | |
|                             const marker = new maplibregl.Marker({
 | |
|                                 color: '#8A2BE2' // Violet
 | |
|                             })
 | |
|                             .setLngLat([issue.lon, issue.lat])
 | |
|                             .addTo(map);
 | |
|                             
 | |
|                             // Ajouter un popup au marqueur
 | |
|                             marker.setPopup(
 | |
|                                 new maplibregl.Popup({ offset: 25 })
 | |
|                                 .setHTML(`<div id="osmose-popup-${issue.id}">Chargement des détails...</div>`)
 | |
|                                 .on('open', () => {
 | |
|                                     // Charger les détails de l'analyse lorsque le popup est ouvert
 | |
|                                     loadOsmoseIssueDetails(issue.id);
 | |
|                                 })
 | |
|                             );
 | |
|                         }
 | |
|                     });
 | |
|                 })
 | |
|                 .catch(error => {
 | |
|                     console.error('Erreur lors du chargement des analyses Osmose:', error);
 | |
|                     document.getElementById('themeMap').innerHTML += '<div class="alert alert-danger mt-3">Erreur lors du chargement des analyses Osmose.</div>';
 | |
|                 });
 | |
|         }
 | |
| 
 | |
|         // Fonction pour charger les détails d'une analyse Osmose
 | |
|         function loadOsmoseIssueDetails(issueId) {
 | |
|             const detailsUrl = `https://osmose.openstreetmap.fr/api/0.3/issue/${issueId}?langs=auto`;
 | |
|             
 | |
|             fetch(detailsUrl)
 | |
|                 .then(response => response.json())
 | |
|                 .then(data => {
 | |
|                     if (!data || !data.issue) return;
 | |
|                     
 | |
|                     const issue = data.issue;
 | |
|                     const popupElement = document.getElementById(`osmose-popup-${issueId}`);
 | |
|                     if (!popupElement) return;
 | |
|                     
 | |
|                     // Construire le contenu du popup
 | |
|                     let popupContent = `
 | |
|                         <h4>${issue.title || 'Analyse Osmose'}</h4>
 | |
|                         <p>${issue.subtitle || ''}</p>
 | |
|                     `;
 | |
|                     
 | |
|                     // Ajouter les tags proposés s'ils existent
 | |
|                     if (issue.fixes && issue.fixes.length > 0 && issue.fixes[0].tags) {
 | |
|                         popupContent += '<div class="osmose-popup-tags">';
 | |
|                         popupContent += '<h5>Tags proposés:</h5>';
 | |
|                         
 | |
|                         Object.entries(issue.fixes[0].tags).forEach(([key, value]) => {
 | |
|                             popupContent += `
 | |
|                                 <div class="osmose-popup-tag">
 | |
|                                     <span class="osmose-popup-tag-key">${key}</span>: 
 | |
|                                     <span class="osmose-popup-tag-value">${value}</span>
 | |
|                                 </div>
 | |
|                             `;
 | |
|                         });
 | |
|                         
 | |
|                         popupContent += '</div>';
 | |
|                     }
 | |
|                     
 | |
|                     // Ajouter les boutons d'action
 | |
|                     popupContent += `
 | |
|                         <div class="osmose-popup-buttons">
 | |
|                             <a href="https://osmose.openstreetmap.fr/fr/error/${issueId}" target="_blank" class="btn btn-sm btn-primary">
 | |
|                                 <i class="bi bi-eye"></i> Voir sur Osmose
 | |
|                             </a>
 | |
|                             <a href="http://localhost:8111/load_and_zoom?left=${issue.lon - 0.001}&right=${issue.lon + 0.001}&top=${issue.lat + 0.001}&bottom=${issue.lat - 0.001}" target="_blank" class="btn btn-sm btn-success">
 | |
|                                 <i class="bi bi-tools"></i> Réparer dans JOSM
 | |
|                             </a>
 | |
|                         </div>
 | |
|                     `;
 | |
|                     
 | |
|                     // Mettre à jour le contenu du popup
 | |
|                     popupElement.innerHTML = popupContent;
 | |
|                 })
 | |
|                 .catch(error => {
 | |
|                     console.error('Erreur lors du chargement des détails de l\'analyse Osmose:', error);
 | |
|                     const popupElement = document.getElementById(`osmose-popup-${issueId}`);
 | |
|                     if (popupElement) {
 | |
|                         popupElement.innerHTML = '<div class="alert alert-danger">Erreur lors du chargement des détails.</div>';
 | |
|                     }
 | |
|                 });
 | |
|         }
 | |
|     </script>
 | |
| {% endblock %} | 
