/** * rechercher les bornes de recharge, * afficher des cercles colorés selon la puissance max de la station * lister les bornes trouvées dans la page * @type {boolean} */ import lcm_config from './lcm_config.js' import lcm_utils from './lcm_utils.js' import lcm_color_utils from './lcm_color_utils.js' import { sendToJOSM, createJOSMEditLink } from './lcm_editor.js' let geojsondata; let lastLatLng; let searchLocationMarker = null; let count_hidden_by_filters = 0; // serveurs de tuiles: https://wiki.openstreetmap.org/wiki/Tile_servers // https://stamen-tiles.a.ssl.fastly.net/toner/{z}/{x}/{y}.png // https://a.tile.openstreetmap.fr/osmfr/{z}/{x}/{y}.png // https://tile.openstreetmap.org/{z}/{x}/{y}.png // 'https://cartodb-basemaps-{s}.global.ssl.fastly.net/light_all/{z}/{x}/{y}.png' // Créer la carte centrée sur Rouen // Liste des 20 villes les plus peuplées de France avec leurs coordonnées géographiques // Initialisation de la carte avec la vue centrée sur la ville choisie let map = L.map('map') L.control.scale().addTo(map) /** * filtres à toggle par des boutons dans la page * à appliquer à chaque rafraîchissement des points geojson * TODO: make buttons and filter in refresh circles */ let filterStatesAvailable = ['hide', 'show', 'showOnly'] let filters_features = { display_unknown_max_power_station: filterStatesAvailable[1] } let display_type2_sockets = 'show'; let display_type2_combo_sockets = 'show'; let display_unknown_max_power_station = 'show'; let display_alert_cable_missing = 'show'; let display_known_max_power_station = 'show'; let display_type2_combo_sockets_with_cable = 'show'; let display_lower_than_50kw = 'show'; let display_higer_than_50kw = 'show'; let display_lower_than_200kw = 'show'; let display_higer_than_200kw = 'show'; let display_chelou = 'show'; // les stations avec une valeur suspecte, plus de 400kW // Ajouter cette fonction avant searchLocation function moveToLocation(place) { const lat = parseFloat(place.lat); const lon = parseFloat(place.lon); if (isNaN(lat) || isNaN(lon)) { console.error('Coordonnées invalides:', place); return; } // Supprimer l'ancien marqueur s'il existe if (searchLocationMarker) { map.removeLayer(searchLocationMarker); } // Créer un nouveau marqueur avec une icône personnalisée searchLocationMarker = L.marker([lat, lon], { icon: L.divIcon({ className: 'search-location-marker', html: '📍', iconSize: [30, 30], iconAnchor: [15, 30] }) }); // Ajouter un popup avec le nom du lieu const popupContent = ` ${place.display_name} ${place.type ? `
Type: ${place.type}` : ''} ${place.context ? `
${place.context}` : ''} `; searchLocationMarker.bindPopup(popupContent); // Ajouter le marqueur à la carte searchLocationMarker.addTo(map); // Centrer la carte sur le lieu map.setView([lat, lon], map.getZoom()); // Ouvrir le popup automatiquement searchLocationMarker.openPopup(); // Faire disparaître le marqueur après 10 secondes // setTimeout(() => { // if (searchLocationMarker) { // map.removeLayer(searchLocationMarker); // searchLocationMarker = null; // } // }, 10000); } // Déplacer searchLocationWithAddok avant searchLocation function searchLocationWithAddok(searchText, mapCenter) { const baseUrl = 'https://demo.addok.xyz/search'; const params = new URLSearchParams({ q: searchText, limit: 10, lat: mapCenter.lat, lon: mapCenter.lng }); const url = `${baseUrl}?${params.toString()}`; return fetch(url) .then(response => { if (!response.ok) { throw new Error('Erreur réseau lors de la recherche'); } return response.json(); }) .then(data => { if (!data.features || data.features.length === 0) { throw new Error('Aucun résultat trouvé'); } return data.features.map(feature => ({ lat: feature.geometry.coordinates[1], lon: feature.geometry.coordinates[0], display_name: feature.properties.label, importance: feature.properties.score, context: feature.properties.context, type: feature.properties.type, city: feature.properties.city, distance: feature.properties.distance })); }); } // Modifier la fonction searchLocation function searchLocation() { const location = $('#searchLocation').val(); if (!location) { alert('Veuillez entrer un lieu à rechercher.'); return; } const useAddok = $('#useAddok').is(':checked'); const searchPromise = useAddok ? searchLocationWithAddok(location, map.getCenter()) : fetch(`https://nominatim.openstreetmap.org/search?format=json&q=${encodeURIComponent(location)}`) .then(response => response.json()); searchPromise .then(data => { const resultsDropdown = $('#searchResults'); resultsDropdown.empty(); if (!data || data.length === 0) { alert('Lieu non trouvé. Veuillez essayer un autre terme de recherche.'); resultsDropdown.hide(); return; } // Toujours sélectionner le premier résultat moveToLocation(data[0]); // Si il y a plus d'un résultat, les afficher quand même dans la liste if (data.length > 1) { // Ajouter le bouton de fermeture avant la liste des résultats const closeButton = $(' ${popupContent}`; let zoom = map.getZoom(); let radius = 20; let opacity = 0.5; let ratio_circle = 10; if (zoom < 13) { ratio_circle = 5; } else if (zoom < 15) { ratio_circle = 1; opacity = 0.25; } else if (zoom <= 16) { ratio_circle = 0.5; } else if (zoom <= 18) { ratio_circle = 0.25; } if (!layer._latlng) { if (lastLatLng) { layer._latlng = lastLatLng; } } else { lastLatLng = layer._latlng; } if (!outPowerGuessed) { radius = radius * ratio_circle; } else { /** * limiter la taille du cercle pour les valeurs aberrantes * les mettre en valeur en les plafonnant à 1 de plus que le maximum attendu en lcm_config */ if (outPowerGuessed > lcm_config.max_possible_station_output) { console.error("valeur suspecte outPowerGuessed", outPowerGuessed, feature) outPowerGuessed = lcm_config.max_possible_station_output + 1 } radius = outPowerGuessed * ratio_circle; } /** * gestion des marqueurs d'alertes */ // info de câble manquant if (display_alert_cable_missing) { let keys = Object.keys(feature.properties) /** * on considère l'information de câble manquante uniquement dans le cas où une info de socket de type 2 est présente mais pas le tag socket:type2_cable. */ if (keys.indexOf('socket:type2') !== -1 && keys.indexOf('socket:type2_cable') === -1) { let circle_alert = L.circle(layer._latlng, { color: 'red', fillColor: 'orange', fillOpacity: 1, colorOpacity: 0.5, radius: 20 }) circle_alert.bindPopup("information de câble manquante"); circle_alert.on({ mouseover: function () { this.openPopup(); bindEventsOnJosmRemote(this.getPopup().getElement()); bindFullDetails(feature); }, mouseout: function () { // setTimeout(() => this.closePopup(), 15000); }, click: function () { this.openPopup(); bindEventsOnJosmRemote(this.getPopup().getElement()); bindFullDetails(feature); }, }); circle_alert.addTo(all_stations_markers); } } /** * affichage des marqueurs de stations de recharge */ let circle = L.circle(layer._latlng, { color: lcm_color_utils.getColor(feature), fillColor: lcm_color_utils.getColor(feature), fillOpacity: opacity, colorOpacity: opacity, radius: radius }).addTo(all_stations_markers); if (zoom > 15) { let circle_center = L.circle(layer._latlng, { color: 'black', fillColor: lcm_color_utils.getColor(feature), fillOpacity: 1, radius: 0.1 }).addTo(all_stations_markers); } circle.bindPopup(html, { autoPan: false, closeOnClick: false }); circle.on({ mouseover: function () { this.openPopup(); bindEventsOnJosmRemote(this.getPopup().getElement()); bindFullDetails(feature); }, mouseout: function () { // setTimeout(() => this.closePopup(), 15000); }, click: function () { this.openPopup(); bindEventsOnJosmRemote(this.getPopup().getElement()); bindFullDetails(feature); }, }); } function bindFullDetails(feature) { $('#fullDetails').on('click', () => { console.log('details', feature) console.log("$('#current_station_infos')", $('#current_station_infos')[0]) let content = '' + '' + '' let ii = 0 let keys = Object.keys(feature.properties.tags) keys.forEach((elem, val) => { console.log('elem, val', elem, val) content += ''; ii++; }) content += '' + '
' + elem + '' + feature.properties.tags[elem] + '
' console.log('content', content) $('#current_station_infos')[0].innerHTML = `

Détails

${content}` }) } let isLoading = false function loadOverpassQuery() { if (!isLoading) { isLoading = true; $('#spinning_icon').fadeIn(); let queryTextfieldValue = $('#query-textfield').val(); let overpassApiUrl = buildOverpassApiUrl(map, queryTextfieldValue); $.get(overpassApiUrl, function (geoDataPointsFromApi) { geojsondata = geoDataPointsFromApi; refreshDisplay(true); $('#spinning_icon').fadeOut(); $('#message-loading').fadeOut(); isLoading = false; }); } } function refreshDisplay(convert_points_to_osm = false) { supprimerMarqueurs(); count_hidden_by_filters = 0; // Réinitialiser le compteur if (geojsondata) { displayPointsFromApi(geojsondata, convert_points_to_osm); updateCounters(); updateFilteredStationsCount(); } // Mettre à jour le compteur dans la popup let count = geojsondata.features.length; let disabledFilters = 0; Object.keys(lcm_config.filterConfigs).forEach(filterId => { if (!lcm_config.filterConfigs[filterId]) disabledFilters++; }); $('#count_features_fond').html('⚡' + count + ' stations' + (disabledFilters > 0 ? ` (${disabledFilters} filtre${disabledFilters > 1 ? 's' : ''} désactivé${disabledFilters > 1 ? 's' : ''}, ${count_hidden_by_filters} masqué${count_hidden_by_filters > 1 ? 's' : ''})` : '')); } function onMapMoveEnd() { let center = map.getCenter() let zoom = map.getZoom() let infos = `Lat: ${center.lat}, Lon: ${center.lng}, Zoom : ${zoom}` if (zoom < 10) { $('#zoomMessage').show() } else { $('#zoomMessage').hide() loadOverpassQuery() } if (map.getZoom() > 14) { searchFoodPlaces(map); } else { food_places_markers.clearLayers(); } $('#infos_carte').html(infos) // Stocker les dernières coordonnées connues if (!window.lastKnownPosition) { window.lastKnownPosition = center; updateURLWithMapCoordinatesAndZoom(); } else { // Calculer la distance en km entre l'ancienne et la nouvelle position const distanceKm = map.distance(center, window.lastKnownPosition) / 1000; console.log('déplacement de ', distanceKm, 'km') // Ne mettre à jour que si on s'est déplacé de plus de 2km if (distanceKm > 2) { window.lastKnownPosition = center; updateURLWithMapCoordinatesAndZoom(); } } // Ajout d'un log pour déboguer console.log("Zoom actuel:", map.getZoom()); if (map.getZoom() >= 12) { // console.log("Recherche des issues Osmose..."); searchOsmoseIssues(map); } else { console.log("Zoom trop faible pour les issues Osmose"); osmose_markers.clearLayers(); } } setCoordinatesOfLeafletMapFromQueryParameters() $(document).ready(function () { init() }); function showActiveFilter(filterVariableName, selectorId) { $(selectorId).attr('class', 'filter-state-' + filterVariableName) } /** * mettre à jour les liens vers des éditeurs externes */ function updateExternalEditorsLinks() { const center = map.getCenter() const zoom = map.getZoom() mapCompleteLink(center.lat, center.lng, zoom) idLink(center.lat, center.lng, zoom) } function mapCompleteLink(lat, lon, zoom) { $("mapCompleteLink").attr('href', `https://mapcomplete.org/charging_stations?z=${zoom}&lat=${lat}&lon=${lon}`) } function idLink(lat, lon, zoom) { let href = `https://www.openstreetmap.org/edit?editor=id#map=${zoom}/${lat}/${lon}` console.log('idlink', href) $("idLink").attr('href', href) } function cycleVariableState(filterVariableName, selectorId) { console.log('filterVariableName', filterVariableName, filterStatesAvailable) if (filterVariableName) { if (filterVariableName == filterStatesAvailable[0]) { filterVariableName = filterStatesAvailable[1] } else if (filterVariableName == filterStatesAvailable[1]) { filterVariableName = filterStatesAvailable[2] } else if (filterVariableName == filterStatesAvailable[2]) { filterVariableName = filterStatesAvailable[0] } } else { filterVariableName = filterStatesAvailable[0] } showActiveFilter(filterVariableName, selectorId) console.log('filterVariableName after', filterVariableName) return filterVariableName } // toggle des stats $('#toggle-stats').on('click', function () { $('#found_charging_stations').slideToggle(); // Change le symbole de la flèche let text = $(this).text(); if (text.includes('🔽')) { $(this).text(text.replace('🔽', '🔼')); } else { $(this).text(text.replace('🔼', '🔽')); } }); // Ajouter ces variables avec les autres déclarations globales let food_places_markers = L.layerGroup(); const foodIcon = L.divIcon({ className: 'food-marker', html: '🍽️', iconSize: [20, 20], iconAnchor: [10, 10] }); // Ajouter cette fonction avec les autres fonctions de recherche function searchFoodPlaces(map) { const bounds = map.getBounds(); const bbox = bounds.getSouth() + ',' + bounds.getWest() + ',' + bounds.getNorth() + ',' + bounds.getEast(); const query = ` [out:json][timeout:25]; ( nwr["amenity"="restaurant"](${bbox}); nwr["amenity"="cafe"](${bbox}); ); out center;`; const url = `https://overpass-api.de/api/interpreter?data=${encodeURIComponent(query)}`; food_places_markers.clearLayers(); fetch(url) .then(response => response.json()) .then(data => { data.elements.forEach(element => { // Utiliser les coordonnées du centre pour les ways et relations const lat = element.lat || element.center.lat; const lon = element.lon || element.center.lon; const name = element.tags.name || 'Sans nom'; const type = element.tags.amenity; const marker = L.marker([lat, lon], { icon: foodIcon }); marker.bindPopup(` ${name}
Type: ${type}
${element.tags.cuisine ? 'Cuisine: ' + element.tags.cuisine : ''} `); food_places_markers.addLayer(marker); }); }) .catch(error => console.error('Erreur lors de la recherche des restaurants:', error)); } // Ajouter après la déclaration des autres variables globales let osmose_markers = L.layerGroup(); const osmoseIcon = L.divIcon({ className: 'osmose-marker-drop', html: '
', iconSize: [30, 40], iconAnchor: [15, 40] }); // Ajouter cette fonction utilitaire pour calculer la distance entre deux points function calculateDistance(lat1, lon1, lat2, lon2) { return L.latLng(lat1, lon1).distanceTo([lat2, lon2]); } // Ajouter cette fonction pour vérifier si le calque des stations est actif function isChargingStationLayerActive() { return map.hasLayer(all_stations_markers); } // Modifier la fonction searchOsmoseIssues function searchOsmoseIssues(map) { const bounds = map.getBounds(); const zoom = map.getZoom(); const bbox = `${bounds.getWest()}%2C${bounds.getSouth()}%2C${bounds.getEast()}%2C${bounds.getNorth()}`; const url = `https://osmose.openstreetmap.fr/api/0.3/issues?zoom=${zoom}&item=8410%2C8411&level=1%2C2%2C3&limit=500&bbox=${bbox}`; osmose_markers.clearLayers(); // Modifier la vérification des stations existantes const existingStations = []; if (lcm_config.hide_osmose_markers_if_close_to_existing_charging_stations && isChargingStationLayerActive() && // Nouvelle condition geojsondata && geojsondata.features) { geojsondata.features.forEach(feature => { if (feature.geometry && feature.geometry.coordinates) { existingStations.push({ lat: feature.geometry.coordinates[1], lon: feature.geometry.coordinates[0] }); } }); } fetch(url) .then(response => { if (!response.ok) { return response.text().then(text => { throw new Error(`Erreur HTTP ${response.status}: ${response.statusText}. Réponse : ${text}`); }); } return response.json(); }) .then(data => { if (!data || !Array.isArray(data.issues)) { console.warn("Réponse Osmose (liste) inattendue ou pas d'issues:", data); osmoseIssuesCount = 0; updateCounters(); return; } const issuesList = data.issues; osmoseIssuesCount = issuesList.length; console.log(`Nombre d'issues Osmose (liste) trouvées: ${osmoseIssuesCount}`); issuesList.forEach(issueInfo => { if (issueInfo.lat == null || issueInfo.lon == null || !issueInfo.id) { return; } const lat = parseFloat(issueInfo.lat); const lon = parseFloat(issueInfo.lon); // Vérifier la distance avec les stations existantes if (lcm_config.hide_osmose_markers_if_close_to_existing_charging_stations) { const tooClose = existingStations.some(station => { const distance = calculateDistance(lat, lon, station.lat, station.lon); return distance <= lcm_config.hide_osmose_markers_if_close_to_existing_charging_stations_distance; }); if (tooClose) { // console.log(`Marqueur Osmose ignoré car trop proche d'une station existante: ${lat},${lon}`); return; } } // Créer le marqueur Osmose si il n'est pas trop proche d'une station existante const osmoseMarker = L.circle([lat, lon], { color: "purple", fillColor: "purple", fillOpacity: 0.8, radius: 10, className: 'leaflet-osmose-layer', pane: 'markerPane', issueId: issueInfo.id }); // Préparer une popup de chargement simple osmoseMarker.bindPopup("Chargement des détails..."); osmoseMarker.on('click', function (e) { const clickedMarker = e.target; const storedIssueId = clickedMarker.options.issueId; handleMarkerClick(clickedMarker, map); // Nouvelle gestion du clic const detailUrl = `https://osmose.openstreetmap.fr/api/0.3/issue/${storedIssueId}?langs=auto`; console.log("Récupération des détails pour l'issue:", detailUrl); fetch(detailUrl) .then(response => { if (!response.ok) { return response.text().then(text => { throw new Error(`Erreur HTTP ${response.status}: ${response.statusText}. Réponse : ${text}`); }); } return response.json(); }) .then(issueDetails => { // Construire le contenu de la popup avec les détails let popupContent = `${issueDetails.title?.auto || 'Titre non disponible'}
`; if (issueDetails.subtitle?.auto) { popupContent += `

${issueDetails.subtitle.auto}

`; } let proposedTags = ''; if (issueDetails.new_elems && issueDetails.new_elems[0] && issueDetails.new_elems[0].add) { proposedTags = ''; issueDetails.new_elems[0].add.forEach(tag => { proposedTags += ``; }); proposedTags += '
${tag.k}${tag.v}
'; popupContent += `

Tags proposés :

${proposedTags}
`; } // Construire le lien JOSM /import const josmFixUrl = `http://localhost:8111/import?url=https://osmose.openstreetmap.fr/api/0.3/issue/${storedIssueId}/fix/0`; popupContent += `
⚡ Réparer dans JOSM Voir sur Osmose
`; // Mettre à jour le contenu de la popup et s'assurer qu'elle est ouverte clickedMarker.setPopupContent(popupContent); clickedMarker.openPopup(); // Rouvrir pour ajuster la taille si nécessaire // Lier l'événement au bouton JOSM DANS la popup bindEventsOnJosmRemote(clickedMarker.getPopup().getElement()); }) .catch(error => { console.error("Erreur lors de la récupération des détails de l'issue Osmose:", error); clickedMarker.setPopupContent(`Erreur lors du chargement des détails.
Voir sur Osmose`); clickedMarker.openPopup(); }); }); osmose_markers.addLayer(osmoseMarker); }); updateCounters(); // Mettre à jour l'affichage après le traitement }) .catch(error => { console.error('Erreur détaillée lors de la recherche de la liste des issues Osmose:', error); osmoseIssuesCount = 0; updateCounters(); }); } // Modifier la gestion du clic sur les marqueurs Osmose function handleMarkerClick(marker, map) { const popup = marker.getPopup(); const markerLatLng = marker.getLatLng(); // Calculer la position relative du marqueur dans la vue const markerPoint = map.latLngToContainerPoint(markerLatLng); const mapHeight = map.getContainer().clientHeight; // Si le marqueur est dans la moitié supérieure de l'écran if (markerPoint.y < mapHeight / 2) { // Calculer le décalage nécessaire pour centrer la popup const targetLatLng = map.containerPointToLatLng([ markerPoint.x, mapHeight / 2 ]); // Déplacer la carte avec une animation map.once('moveend', () => { marker.openPopup(); }); map.panTo(targetLatLng, { animate: true, duration: 0.5 }); } else { marker.openPopup(); } } // Ajouter un écouteur d'événements pour le changement de visibilité des calques function init() { $(document).ready(function () { bindEventsOnJosmRemote(); onMapMoveEnd(); map.on('moveend', onMapMoveEnd); $('#spinning_icon').hide(); /** * boutons de changement de filtres et de rechargement des bornes à l'affichage */ $('#removeMarkers').on('click', function () { supprimerMarqueurs(); }); $('#load').on('click', function () { loadOverpassQuery(); }); $('#toggleSidePanel').on('click', function () { $('body').toggleClass('side-panel-open'); }); $('#chercherButton').on('click', function () { supprimerMarqueurs(); loadOverpassQuery(); }); $('#setRandomView').on('click', function () { setRandomView(); loadOverpassQuery(); }); $('#filterUnkown').on('click', function () { display_unknown_max_power_station = cycleVariableState(display_unknown_max_power_station, '#filterUnkown'); showActiveFilter(display_unknown_max_power_station, '#filterUnkown'); refreshDisplay(); }); /** * toggle des alertes de tags décrivant la présence de cable */ $('#cableMissing').on('click', function () { display_alert_cable_missing = !display_alert_cable_missing; showActiveFilter(display_alert_cable_missing, '#cableMissing'); refreshDisplay(); }); showActiveFilter(display_unknown_max_power_station, '#filterUnkown'); $('#shareUrl').on('click', copyCurrentUrl); // Initialisation des états des checkboxes des filtres selon les valeurs de configuration Object.keys(lcm_config.filterConfigs).forEach(filterId => { console.log("checbox ", $(`#${filterId}`) , lcm_config.filterConfigs[filterId] , lcm_config) $(`#${filterId}`).prop('checked', lcm_config.filterConfigs[filterId]); }); // Écouteurs pour les filtres Object.keys(lcm_config.filterConfigs).forEach(filterId => { $(`#${filterId}`).on('change', function() { lcm_config[lcm_config.filterConfigs[filterId]] = this.checked; refreshDisplay(); }); }); food_places_markers.addTo(map); $('#found_charging_stations').hide(); // Mettre à jour le contrôle des calques const overlayMaps = { "Stations de recharge": all_stations_markers, "Restaurants et cafés": food_places_markers, "Bornes potentielles (Osmose)": osmose_markers }; // const baseLayersControl = L.control.layers(baseLayers, null, { // collapsed: true, // className: 'leaflet-control-layers base-layers', // id: 'base-layers-control' // }).addTo(map); const overlayControl = L.control.layers(null, overlayMaps, { collapsed: true, className: 'leaflet-control-layers overlay-layers', id: 'overlay-layers-control' }).addTo(map); $('#sendToJOSM').on('click', () => { sendToJOSM(map, geojsondata) .then(() => { console.log('Données envoyées à JOSM avec succès !'); }) .catch(() => { alert('Erreur : JOSM doit être ouvert avec l\'option "Contrôle à distance" activée'); }); }); $('#josmLink').on('click', () => { sendToJOSM(map, geojsondata) .then(() => { console.log('Données envoyées à JOSM avec succès !'); }) .catch(() => { alert('Erreur : JOSM doit être ouvert avec l\'option de télécommande "Contrôle à distance" activée dans ses paramètres (accédez-y avec F12)'); }); }); $('#searchButton').on('click', searchLocation); $('#shareUrl').on('click', copyCurrentUrl); $('#filter_max_output').on('input', function () { const value = $(this).val(); console.log('filter_max_output', value, $(this)); $('#filter_max_output_display').text(value + ' kW'); refreshDisplay(); }); $('#filter_max_output_slider').on('input', function () { const value = $(this).val(); lcm_config.filter_max_output_default_value = value; $('#filter_max_output_display').text(value + ' kW'); refreshDisplay(); }); $('#searchResults').on('change', function () { const selectedIndex = $(this).eq(0).val(); if (selectedIndex !== null) { const selectedPlace = $(this).find('option:selected').data('place'); moveToLocation(selectedPlace); } }); osmose_markers.addTo(map); }); } function copyCurrentUrl() { const url = window.location.href; var dummy = document.createElement('input'), text = window.location.href; document.body.appendChild(dummy); dummy.value = text; dummy.select(); document.execCommand('copy'); document.body.removeChild(dummy); } init() // Créer un nouveau pane pour les marqueurs Osmose avec un zIndex plus élevé map.createPane('osmosePane'); map.getPane('osmosePane').style.zIndex = 1000; // Ajouter une nouvelle fonction pour mettre à jour le compteur de stations filtrées function updateFilteredStationsCount() { const totalStations = geojsondata ? geojsondata.features.length : 0; const filterStats = `
${displayedStationsCount} stations sur ${totalStations} trouvées
`; // Mettre à jour ou créer l'élément après le slider let existingStats = $('.filter-stats'); if (existingStats.length) { existingStats.replaceWith(filterStats); } else { $('#filter_max_output_display').after(filterStats); } }