diff --git a/assets/app.js b/assets/app.js index 907dd017..d1825149 100644 --- a/assets/app.js +++ b/assets/app.js @@ -165,6 +165,10 @@ document.addEventListener('DOMContentLoaded', () => { + /** + * mettre à jour la barre de progression + * pour le formulaire de modification + */ function updateCompletionProgress() { const inputs = document.querySelectorAll('input[type="text"]'); let filledInputs = 0; diff --git a/public/js/sort-table/sort-table.js b/public/js/sort-table/sort-table.js index 932d13cc..fbd01b33 100644 --- a/public/js/sort-table/sort-table.js +++ b/public/js/sort-table/sort-table.js @@ -21,6 +21,9 @@ function sortTable(Table, col, dir) { var sortClass, i; + if (!Table) { + return; + } // get previous sort column sortTable.sortCol = -1; sortClass = Table.className.match(/js-sort-\d+/); @@ -94,7 +97,7 @@ function sortTable(Table, col, dir) { * @param RowB A TR DOM object * @returns {number} 1 if RowA is greater, -1 if RowB, 0 if equal */ -sortTable.compareRow = function(RowA, RowB) { +sortTable.compareRow = function (RowA, RowB) { var valA, valB; if ('function' != typeof sortTable[sortTable.sortFunc]) { sortTable.sortFunc = 'string'; @@ -110,7 +113,7 @@ sortTable.compareRow = function(RowA, RowB) { * @param html * @returns {string} */ -sortTable.stripTags = function(html) { +sortTable.stripTags = function (html) { return html.replace(/<\/?[a-z][a-z0-9]*\b[^>]*>/gi, ''); }; @@ -121,7 +124,7 @@ sortTable.stripTags = function(html) { * @param Cell A TD DOM object * @returns {Date} */ -sortTable.date = function(Cell) { +sortTable.date = function (Cell) { return new Date(sortTable.stripTags(Cell.innerHTML)); }; @@ -132,7 +135,7 @@ sortTable.date = function(Cell) { * @param Cell A TD DOM object * @returns {Number} */ -sortTable.number = function(Cell) { +sortTable.number = function (Cell) { return Number(sortTable.stripTags(Cell.innerHTML).replace(/[^-\d.]/g, '')); }; @@ -143,7 +146,7 @@ sortTable.number = function(Cell) { * @param Cell A TD DOM object * @returns {String} */ -sortTable.string = function(Cell) { +sortTable.string = function (Cell) { return sortTable.stripTags(Cell.innerHTML).toLowerCase(); }; @@ -154,7 +157,7 @@ sortTable.string = function(Cell) { * @param Cell A TD DOM object * @returns {String} */ -sortTable.last = function(Cell) { +sortTable.last = function (Cell) { return sortTable.stripTags(Cell.innerHTML).split(' ').pop().toLowerCase(); }; @@ -165,7 +168,7 @@ sortTable.last = function(Cell) { * @param Cell A TD DOM object * @returns {String} */ -sortTable.input = function(Cell) { +sortTable.input = function (Cell) { for (var i = 0; i < Cell.children.length; i++) { if ('object' == typeof Cell.children[i] && 'undefined' != typeof Cell.children[i].value @@ -184,8 +187,8 @@ sortTable.input = function(Cell) { * @param col Column to sort by * @returns {Function} Click Handler */ -sortTable.getClickHandler = function(Table, col) { - return function() { +sortTable.getClickHandler = function (Table, col) { + return function () { sortTable(Table, col); }; }; @@ -195,7 +198,7 @@ sortTable.getClickHandler = function(Table, col) { * If the table(s) do not have a THead node, one will be created around the * first row */ -sortTable.init = function() { +sortTable.init = function () { var THead, Tables, Handler; if (document.querySelectorAll) { Tables = document.querySelectorAll('table.js-sort-table'); diff --git a/templates/admin/stats.html.twig b/templates/admin/stats.html.twig index f84bdcdf..5c568496 100644 --- a/templates/admin/stats.html.twig +++ b/templates/admin/stats.html.twig @@ -118,7 +118,8 @@ let features = []; let maplibre; let map; - + let overpassData = {}; // Stockage des données Overpass + function getCompletionColor(completion) { if (completion === undefined || completion === null) { return '#808080'; // Gris pour pas d'information @@ -129,30 +130,27 @@ } function calculateCompletion(element) { - let completed = 0; - let total = 0; - - // Critères à vérifier - const criteria = [ + let completionCount = 0; + let totalFields = 0; + + const fieldsToCheck = [ 'name', - 'addr:street', - 'addr:housenumber', - 'addr:postcode', - 'addr:city', + 'contact:street', + 'contact:housenumber', 'opening_hours', - 'website', - 'wheelchair', - 'phone' + 'contact:website', + 'contact:phone', + 'wheelchair' ]; - criteria.forEach(criterion => { - if (element.tags && element.tags[criterion]) { - completed++; + fieldsToCheck.forEach(field => { + totalFields++; + if (element.tags && element.tags[field]) { + completionCount++; } - total++; }); - return total > 0 ? (completed / total) * 100 : 0; + return (completionCount / totalFields) * 100; } function createPopupContent(element) { @@ -254,8 +252,8 @@ let josm_elements = []; async function loadPlaces(map) { - map_is_loaded = false; try { + const request = `{{query_places |raw}}`; const response = await fetch('https://overpass-api.de/api/interpreter', { method: 'POST', body: request @@ -264,46 +262,113 @@ throw new Error(`HTTP error! status: ${response.status}`); } const data = await response.json(); - console.log('Lieux chargés:', data.elements); - mapElements = data.elements; - map_is_loaded = true; + console.log('Données reçues:', data); - // Créer le graphique de distribution - createCompletionChart(data.elements); + // Stocker les données Overpass + data.elements.forEach(element => { + overpassData[element.id] = element; + }); // Mettre à jour les cercles features = []; - josm_elements = []; data.elements.forEach(element => { const lat = element.lat || (element.center && element.center.lat); const lon = element.lon || (element.center && element.center.lon); - if (lat && lon) { const completion = calculateCompletion(element); - const color = getCompletionColor(completion); - - // Créer un cercle de 20 mètres de rayon (plus petit) - const circle = turf.circle([lon, lat], 0.04, { steps: 64, units: 'kilometers' }); - circle.properties = { - color: color, - completion: completion, - name: element.tags?.name || 'Sans nom', - popupContent: createPopupContent(element), - center: [lon, lat] // Stocker le centre comme un tableau [lon, lat] + console.log('Completion pour', element.tags?.name, ':', completion, '%'); + + const circle = { + id: `circle-${element.id}`, + type: 'Feature', + properties: { + id: element.id, + name: element.tags?.name || 'Sans nom', + completion: completion, + center: [lon, lat] + }, + geometry: { + type: 'Point', + coordinates: [lon, lat] + } }; features.push(circle); josm_elements.push(element); } }); - // Mettre à jour la source des cercles - map.getSource('completion-circles').setData({ - 'type': 'FeatureCollection', - 'features': features + // Créer les cercles sur la carte + features.forEach(feature => { + const layerId = `circle-${feature.properties.id}`; + if (map.getSource(layerId)) { + map.removeSource(layerId); + } + if (map.getLayer(layerId)) { + map.removeLayer(layerId); + } + + const circle = turf.circle( + feature.properties.center, + 0.02, // Rayon initial en kilomètres + { steps: 64, units: 'kilometers' } + ); + + map.addSource(layerId, { + 'type': 'geojson', + 'data': circle + }); + + map.addLayer({ + 'id': layerId, + 'type': 'fill', + 'source': layerId, + 'paint': { + 'fill-color': getCompletionColor(feature.properties.completion), + 'fill-opacity': 0.7 + } + }); }); - // Calculer les bounds pour tous les points + // Ajouter les popups sur les cercles + map.on('click', function(e) { + const clickedFeatures = map.queryRenderedFeatures(e.point, { + layers: features.map(f => `circle-${f.properties.id}`) + }); + + console.log('Clicked features:', clickedFeatures); + + if (clickedFeatures.length > 0) { + const feature = clickedFeatures[0]; + const elementId = feature.layer.id.replace('circle-', ''); + const element = overpassData[elementId]; + + if (element) { + const completion = calculateCompletion(element); + + // Créer le contenu de la popup + const popupContent = ` +
Taux de complétion: ${Math.round(completion)}%
+