/** * 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; // 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 function setRandomView() { let randomCity = lcm_utils.cities[Math.floor(Math.random() * lcm_utils.cities.length)]; map = map.setView(randomCity.coords, lcm_config.initialZoom); } function setCoordinatesOfLeafletMapFromQueryParameters() { const urlParams = new URLSearchParams(window.location.href); const lat = urlParams.get('lat'); const lng = urlParams.get('lng'); const zoom = urlParams.get('zoom'); if (lat && lng && zoom) { map = map.setView([lat, lng], zoom); } else { console.error('Les paramètres de coordonnées et de zoom doivent être présents dans l\'URL.'); setRandomView(); } } function updateURLWithMapCoordinatesAndZoom() { // Récupère les coordonnées et le niveau de zoom de la carte const center = map.getCenter() const zoom = map.getZoom() // Construit l'URL avec les paramètres de coordonnées et de zoom const url = `#coords=1&lat=${center.lat}&lng=${center.lng}&zoom=${zoom}` // Met à jour l'URL de la page history.replaceState(null, null, url) updateExternalEditorsLinks() } let all_stations_markers = L.layerGroup().addTo(map) // layer group pour tous les marqueurs // let stations_much_speed_wow = L.layerGroup().addTo(map) // layer group des stations rapides var osm = L.tileLayer(lcm_config.tileServers.osm, { attribution: lcm_config.osmMention + '© OpenStreetMap contributors' }) var cycle = L.tileLayer(lcm_config.tileServers.cycle, { attribution: lcm_config.osmMention + '© OpenCycleMap contributors' }) var transport = L.tileLayer(lcm_config.tileServers.transport, { attribution: lcm_config.osmMention }) let tileGrey = L.tileLayer(lcm_config.tileServers.cartodb, { attribution: lcm_config.osmMention }) let stamen = L.tileLayer(lcm_config.tileServers.stamen, { attribution: lcm_config.osmMention }) var baseLayers = { 'Grey': tileGrey, 'Stamen': stamen, 'OpenStreetMap': osm, // 'OpenCycleMap': cycle, 'Transport': transport } let overlays = { stations: all_stations_markers } const layerControl = L.control.layers(baseLayers, overlays, { collapsed: true }).addTo(map) tileGrey.addTo(map) function buildOverpassApiUrl(map, overpassQuery) { let baseUrl = 'https://overpass-api.de/api/interpreter'; const kilometersMarginForLoading = 2; const marginInDegrees = kilometersMarginForLoading / 111; const south = map.getBounds().getSouth() - marginInDegrees; const west = map.getBounds().getWest() - marginInDegrees; const north = map.getBounds().getNorth() + marginInDegrees; const east = map.getBounds().getEast() + marginInDegrees; let bounds = south + ',' + west + ',' + north + ',' + east; let resultUrl, query = ''; if (lcm_config.overrideQuery) { query = `?data=[out:json][timeout:15];( nwr[amenity=charging_station](${bounds}); );out body geom;`; } else { let nodeQuery = 'node[' + overpassQuery + '](' + bounds + ');'; let wayQuery = 'way[' + overpassQuery + '](' + bounds + ');'; let relationQuery = 'relation[' + overpassQuery + '](' + bounds + ');'; query = '?data=[out:json][timeout:15];(' + nodeQuery + wayQuery + relationQuery + ');out body geom;'; } resultUrl = baseUrl + query; return resultUrl; } function supprimerMarqueurs() { all_stations_markers.clearLayers(); map.eachLayer((layer) => { if (layer instanceof L.Marker) { layer.remove(); } }); } let coef_reduction_bars = 0.8 function calculerPourcentage(partie, total, reduc) { if (total === 0) { return 'Division par zéro impossible' } let coef_reduction = 1 if (reduc) { coef_reduction = coef_reduction_bars } return ((partie / total) * 100 * coef_reduction).toFixed(1) } function displayStatsFromGeoJson(resultAsGeojson) { let count = resultAsGeojson.features.length; let count_station_output = 0; let count_ref_eu = 0; let output_more_than_300 = 0; let output_more_than_200 = 0; let output_more_than_100 = 0; let output_more_than_50 = 0; let count_station_outputoutput_between_1_and_50 = 0; let count_output_unknown = 0; let count_estimated_type2combo = 0; let count_found_type2combo = 0; let count_found_type2 = 0; $('#count_features_fond').html('⚡' + count + ' stations'); resultAsGeojson.features.map(feature => { let found_type2_combo = false; let found_type2 = false; let keys_of_object = Object.keys(feature.properties.tags); keys_of_object.map(tagKey => { if (tagKey.indexOf('type2_combo') !== -1) { found_type2_combo = true; } if (tagKey.indexOf('type2') !== -1) { found_type2 = true; } }); let outputPower = lcm_utils.guessOutputPowerFromFeature(feature); if (found_type2_combo) { count_found_type2combo++; } if (found_type2) { count_found_type2++; } if (outputPower == 0) { count_output_unknown++; } // filtres // filtrer les valeurs inconnues if (outputPower >= 200 && !found_type2_combo) { count_estimated_type2combo++; } if (outputPower > 0 && outputPower < 50) { count_station_outputoutput_between_1_and_50++; } if (outputPower >= 50 && outputPower < 100) { output_more_than_50++; } else if (outputPower >= 100 && outputPower < 200) { output_more_than_100++; } else if (outputPower >= 200 && outputPower < 300) { output_more_than_200++; } else if (outputPower >= 300) { feature.properties.puissance_haute = true; output_more_than_300++; } if (feature.properties.tags['charging_station:output']) { count_station_output++; } if (feature.properties.tags['ref:EU:EVSE']) { count_ref_eu++; } }); let bar_powers = `
${count_output_unknown}
${count_station_outputoutput_between_1_and_50 ? count_station_outputoutput_between_1_and_50 : ''}
${output_more_than_50 ? output_more_than_50 : ''}
${output_more_than_100 ? output_more_than_100 : ''}
${output_more_than_200 ? output_more_than_200 : '' | ''}
${output_more_than_300 ? output_more_than_300 : ''}
`; let stats_content = `
Type Nombre Pourcentage
Puissance inconnue ${count_output_unknown} ${calculerPourcentage(count_output_unknown, count)}%
1-50 kW ${count_station_outputoutput_between_1_and_50} ${calculerPourcentage(count_station_outputoutput_between_1_and_50, count)}%
50-100 kW ${output_more_than_50} ${calculerPourcentage(output_more_than_50, count)}%
100-200 kW ${output_more_than_100} ${calculerPourcentage(output_more_than_100, count)}%
200-300 kW ${output_more_than_200} ${calculerPourcentage(output_more_than_200, count)}%
300+ kW ${output_more_than_300} ${calculerPourcentage(output_more_than_300, count)}%
`; $('#found_charging_stations').html(stats_content); $('#bars_power').html(bar_powers); } // Modifier bindEventsOnJosmRemote pour cibler les boutons dans un contexte (la popup) function bindEventsOnJosmRemote(popupElement) { // Cible tous les liens JOSM à l'intérieur de l'élément popup fourni $(popupElement).find('.edit-button.josm').each(function () { // Utiliser .off().on() pour éviter les liaisons multiples si la popup est rouverte $(this).off('click').on('click', (event) => { event.preventDefault(); // Empêcher le comportement par défaut du lien let josm_link = $(this).attr('data-href'); console.log('Sending command to JOSM:', josm_link); // Utiliser fetch pour la requête GET vers JOSM (plus moderne que $.get) fetch(josm_link) .then(response => { if (!response.ok) { // Gérer les erreurs de communication avec JOSM console.error('JOSM remote control error:', response.status, response.statusText); alert('Erreur de communication avec JOSM. Assurez-vous que JOSM est lancé et que le contrôle à distance est activé.'); } else { console.log('JOSM command sent successfully.'); // Optionnel: Afficher une notification de succès } }) .catch(error => { console.error('Failed to send command to JOSM:', error); alert('Impossible d\'envoyer la commande à JOSM. Est-il lancé et le contrôle à distance activé ?'); }); }); }); } function displayPointsFromApi(points, convert_to_osm_json) { if (points && convert_to_osm_json) { geojsondata = osmtogeojson(points); } displayStatsFromGeoJson(geojsondata); let resultLayer = L.geoJson(geojsondata, { style: function (feature) { return { color: '#f00' }; }, filter: function (feature, layer) { let isPolygon = (feature.geometry) && (feature.geometry.type !== undefined) && (feature.geometry.type === 'Polygon'); if (isPolygon) { feature.geometry.type = 'Point'; let polygonCenter = L.latLngBounds(feature.geometry.coordinates[0]).getCenter(); feature.geometry.coordinates = [polygonCenter.lat, polygonCenter.lng]; } return true; }, onmoveend: function (event) { // console.log('déplacement terminé'); }, onzoomend: function (event) { supprimerMarqueurs(); displayPointsFromApi(); }, onEachFeature: eachFeature, }); } function makePopupOfFeature(feature) { let popupContent = '' popupContent += '
' let type2 = feature.properties.tags['socket:type2'] let type2_combo = feature.properties.tags['socket:type2_combo'] if (type2) { popupContent += ' prise de type 2' if (type2 !== 'yes') { popupContent += 'x ' + type2 + '' } } if (feature.properties.tags['socket:type2_combo']) { popupContent += ' prise de type 2 combo CCS' if (type2_combo !== 'yes') { popupContent += 'x ' + type2_combo + '' } } popupContent += '
' popupContent += '
' // ne montrer que certains champs dans la popup lcm_config.tags_to_display_in_popup.forEach(function (key) { if (lcm_config.tags_to_display_in_popup.indexOf(key)) { let value = feature.properties.tags[key] if (value) { if (value.indexOf('http') !== -1) { value = '' + value + '' } popupContent = popupContent + '
' + key + ' :' + value + '' } } }) popupContent += '
' return popupContent; } /** * application des filtres dans la sélection des bornes à afficher * @param feature * @param layer */ function eachFeature(feature, layer) { let link_josm = createJOSMEditLink(feature); let popupContent = makePopupOfFeature(feature); layer.bindPopup(popupContent); let outPowerGuessed = lcm_utils.guessOutputPowerFromFeature(feature); let color = lcm_color_utils.getColor(feature); let displayOutPowerGuessed = '? kW'; if (outPowerGuessed) { displayOutPowerGuessed = outPowerGuessed + ' kW max'; if (display_unknown_max_power_station === 'show_only') { return; } } else { // on cache cette station si on ne veut pas voir les stations à la puissance inconnue if (display_unknown_max_power_station === 'hide') { return; } } /** * bornes sans informations, suggérer d'ajouter des tags dans OSM */ if (!popupContent) { popupContent = ` Aucune information renseignée, ajoutez la dans OpenStreetMap!`; } // Ajout du lien vers Panoramax const panoramaxLink = `https://api.panoramax.xyz/#focus=map&map=16.7/${feature.geometry.coordinates[1]}/${feature.geometry.coordinates[0]}&speed=250`; let html = ` 🚗 🚴‍♀️ 👠 ✏️ JOSM icone ${displayOutPowerGuessed} ${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: color, fillColor: color, fillOpacity: opacity, colorOpacity: opacity, radius: radius }).addTo(all_stations_markers); if (zoom > 15) { let circle_center = L.circle(layer._latlng, { color: 'black', fillColor: color, fillOpacity: 1, radius: 0.1 }).addTo(all_stations_markers); } circle.bindPopup(html); 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}` }) } function makeCssClassFromTags(tags) { let tagKeys = Object.keys(tags) if (!tags) { return '' } let listOfClasses = [] tagKeys.forEach((element) => { listOfClasses.push('tag-' + element + '_' + tags[element].replace(':', '--').replace(' ', '-')) }) return listOfClasses.join(' ') } function getIconFromTags(tags) { let iconFileName = '' // let iconFileName = 'icon_restaurant.png'; if (tags['man_made']) { iconFileName = 'fountain.png' } return iconFileName } function toggleMinPower(showHighPower) { showHighPower = !showHighPower; addFilteredMarkers(showHighPower); this.textContent = showHighPower ? 'Montrer puissance haute' : 'Montrer puissance normale'; } function addFilteredMarkers(showHighPower) { allMarkers.clearLayers(); let counter = 0; geojsondata.features.forEach(function (feature) { if (feature.properties.puissance_haute === showHighPower) { counter++; let marker = L.marker(feature.geometry.coordinates).bindPopup(feature.properties.puissance_haute ? 'Puissance haute' : 'Puissance normale'); allMarkers.addLayer(marker); } }); } 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(); displayPointsFromApi(geojsondata, convert_points_to_osm); } 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; // 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 () { 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'); }); 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 nouvelle fonction function searchOsmoseIssues(map) { const bounds = map.getBounds(); const zoom = map.getZoom(); // Construction de la bbox avec l'ordre correct : ouest,sud,est,nord const bbox = `${bounds.getWest()}%2C${bounds.getSouth()}%2C${bounds.getEast()}%2C${bounds.getNorth()}`; // URL formatée selon l'exemple fourni const url = `https://osmose.openstreetmap.fr/api/0.3/issues?zoom=${zoom}&item=8410%2C8411&level=1%2C2%2C3&limit=500&bbox=${bbox}`; console.log("Recherche des issues Osmose (liste) avec l'URL :", url); osmose_markers.clearLayers(); 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); return; } const issuesList = data.issues; console.log(`Nombre d'issues Osmose (liste) trouvées: ${issuesList.length}`); issuesList.forEach(issueInfo => { // Vérifier si les coordonnées sont valides if (issueInfo.lat == null || issueInfo.lon == null) { console.warn("Issue Osmose sans coordonnées ignorée:", issueInfo); return; // Ignorer cette issue } const lat = parseFloat(issueInfo.lat); const lon = parseFloat(issueInfo.lon); const issueId = issueInfo.id; // C'est l'UUID // Vérifier si l'UUID est valide if (!issueId) { console.warn("Issue Osmose sans ID (UUID) ignorée:", issueInfo); return; } const osmoseMarker = L.circle([lat, lon], { color: "purple", fillColor: "purple", fillOpacity: 0.8, radius: 10, className: 'leaflet-osmose-layer', pane: 'markerPane', issueId: issueId }); // 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; // Afficher immédiatement la popup de chargement clickedMarker.openPopup(); 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); }); }) .catch(error => { console.error('Erreur détaillée lors de la recherche de la liste des issues Osmose:', error); }); } // Modifier la fonction init pour ajouter le nouveau calque function init() { 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 }; L.control.layers(null, overlayMaps).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); $('#searchResults').on('change', function () { const selectedIndex = $(this).val(); if (selectedIndex !== null) { const selectedPlace = $(this).find('option:selected').data('place'); moveToLocation(selectedPlace); } }); document.getElementById('searchButton').addEventListener('click', searchLocation); 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); } function searchLocation() { const location = $('#searchLocation').val(); if (!location) { alert('Veuillez entrer un lieu à rechercher.'); return; } const url = `https://nominatim.openstreetmap.org/search?format=json&q=${encodeURIComponent(location)}`; fetch(url) .then(response => response.json()) .then(data => { const resultsDropdown = $('#searchResults'); resultsDropdown.empty(); // Clear previous results if (data.length === 0) { alert('Lieu non trouvé. Veuillez essayer un autre terme de recherche.'); resultsDropdown.hide(); } else if (data.length === 1) { const place = data[0]; moveToLocation(place); resultsDropdown.hide(); } else { data.forEach((place, index) => { const option = $('') .val(index) .text(`${place.display_name} (${place.lat}, ${place.lon})`); resultsDropdown.append(option); }); resultsDropdown.show(); } }) .catch(error => { console.error('Erreur lors de la recherche du lieu :', error); alert('Erreur lors de la recherche du lieu.'); }); } function moveToLocation(place) { const lat = parseFloat(place.lat); const lon = parseFloat(place.lon); map.setView([lat, lon], 13); // Ajustez le niveau de zoom selon vos besoins } init() // Créer un nouveau pane pour les marqueurs Osmose avec un zIndex plus élevé map.createPane('osmosePane'); map.getPane('osmosePane').style.zIndex = 1000;