libre-charge-map/js/lcm_main.js
2025-05-01 12:39:22 +02:00

1565 lines
No EOL
57 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 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, {valid_qa_message} 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;
let averageChargeKwh = 26;
// 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 = `
<strong>${place.display_name}</strong>
${place.type ? `<br>Type: ${place.type}` : ''}
${place.context ? `<br>${place.context}` : ''}
`;
searchLocationMarker.bindPopup(popupContent);
// Ajouter le marqueur à la carte
searchLocationMarker.addTo(map);
// Désactiver temporairement l'événement moveend
map.off('moveend', onMapMoveEnd);
// Centrer la carte sur le lieu
map.setView([lat, lon], map.getZoom());
// Réactiver l'événement moveend après un court délai
setTimeout(() => {
map.on('moveend', onMapMoveEnd);
}, 500);
// Ouvrir le popup automatiquement
searchLocationMarker.openPopup();
}
// 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) {
// Vérifier si le bouton de fermeture existe déjà
if ($('.close-results-button').length === 0) {
const closeButton = $('<button>')
.addClass('close-results-button')
.html('❌')
.attr('title', 'Fermer les résultats de recherche')
.on('click', function () {
$('#searchResults').hide();
$(this).hide();
$('#searchLocation').val('').focus();
});
resultsDropdown.before(closeButton);
}
data.forEach((place, index) => {
let displayText = place.display_name;
if (useAddok && place.distance) {
displayText += ` (${Math.round(place.distance)}m)`;
}
const option = $('<option></option>')
.val(index)
.text(displayText)
.data('place', place);
resultsDropdown.append(option);
});
resultsDropdown.show();
$('.close-results-button').show();
// Sélectionner visuellement le premier résultat dans la liste
resultsDropdown.val(0);
} else {
resultsDropdown.hide();
}
})
.catch(error => {
console.error('Erreur lors de la recherche du lieu :', error);
alert('Erreur lors de la recherche du lieu : ' + error.message);
});
}
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 bad_tags_markers = L.layerGroup()// layer group pour les marqueurs avec des problèmes de qualité
// 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 + '&copy; <a href="https://www.openstreetmap.org/">OpenStreetMap</a> contributors'
})
var cycle = L.tileLayer(lcm_config.tileServers.cycle, {
attribution: lcm_config.osmMention + '&copy; <a href="https://www.opencyclemap.org/">OpenCycleMap</a> 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
})
// Ajouter après les autres déclarations de tileLayer
let bdortho = L.tileLayer('https://wxs.ign.fr/ortho/geoportail/wmts?' +
'SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=ORTHOIMAGERY.ORTHOPHOTOS' +
'&STYLE=normal&FORMAT=image/jpeg&TILEMATRIXSET=PM&' +
'TILEMATRIX={z}&TILEROW={y}&TILECOL={x}', {
attribution: '&copy; <a href="https://geoservices.ign.fr/bdortho">IGN-F/Géoportail</a>',
maxZoom: 19
});
// Modifier la définition de baseLayers pour inclure la BD ORTHO
var baseLayers = {
'Grey': tileGrey,
// 'Stamen': stamen,
'OpenStreetMap': osm,
// 'BD ORTHO IGN': bdortho,
// 'OpenCycleMap': cycle,
'Transport': transport
}
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)
}
// Ajouter une variable globale pour stocker le nombre d'issues Osmose
let osmoseIssuesCount = 0;
// Ajouter une variable globale pour stocker le nombre de stations affichées
let displayedStationsCount = 0;
function displayStatsFromGeoJson(resultAsGeojson, stats) {
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;
// Compter les filtres désactivés
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' : ''}, ${stats.count_hidden_by_filters} masqué${stats.count_hidden_by_filters > 1 ? 's' : ''})` : ''));
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 = `<div class="bars-container">
<div class="bar color-unknown" style="width: ${calculerPourcentage(count_output_unknown, count, true)}%">${count_output_unknown}</div>
<div class="bar color-power-lesser-than-50" style="width: ${calculerPourcentage(count_station_outputoutput_between_1_and_50, count, true)}%">${count_station_outputoutput_between_1_and_50 ? count_station_outputoutput_between_1_and_50 : ''}</div>
<div class="bar color-power-lesser-than-100" style="width: ${calculerPourcentage(output_more_than_50, count, true)}%">${output_more_than_50 ? output_more_than_50 : ''}</div>
<div class="bar color-power-lesser-than-200" style="width: ${calculerPourcentage(output_more_than_100, count, true)}%">${output_more_than_100 ? output_more_than_100 : ''}</div>
<div class="bar color-power-lesser-than-300" style="width: ${calculerPourcentage(output_more_than_200, count, true)}%">${output_more_than_200 ? output_more_than_200 : '' | ''}</div>
<div class="bar color-power-lesser-than-max" style="width: ${calculerPourcentage(output_more_than_300, count, true)}%">${output_more_than_300 ? output_more_than_300 : ''}</div>
</div>`;
let stats_content = `<div class="stats-table">
<table style="width: 100%;">
<tr>
<th>Type</th>
<th>Nombre</th>
<th>Pourcentage</th>
</tr>
<tr>
<td>Puissance inconnue</td>
<td>${count_output_unknown}</td>
<td>${calculerPourcentage(count_output_unknown, count)}%</td>
</tr>
<tr>
<td>1-50 kW</td>
<td>${count_station_outputoutput_between_1_and_50}</td>
<td>${calculerPourcentage(count_station_outputoutput_between_1_and_50, count)}%</td>
</tr>
<tr>
<td>50-100 kW</td>
<td>${output_more_than_50}</td>
<td>${calculerPourcentage(output_more_than_50, count)}%</td>
</tr>
<tr>
<td>100-200 kW</td>
<td>${output_more_than_100}</td>
<td>${calculerPourcentage(output_more_than_100, count)}%</td>
</tr>
<tr>
<td>200-300 kW</td>
<td>${output_more_than_200}</td>
<td>${calculerPourcentage(output_more_than_200, count)}%</td>
</tr>
<tr>
<td>300+ kW</td>
<td>${output_more_than_300}</td>
<td>${calculerPourcentage(output_more_than_300, count)}%</td>
</tr>
</table>
</div>`;
$('#found_charging_stations').html(stats_content);
$('#bars_power').html(bar_powers);
// Remplacer la ligne existante par un appel à updateCounters
updateCounters();
}
// Ajouter une fonction pour mettre à jour les compteurs
function updateCounters() {
const stationsCount = geojsondata ? geojsondata.features.length : 0;
const osmoseText = lcm_config.osmoseIssuesList.length > 0 ? ` <span class="osmose-counter">(+ ${lcm_config.osmoseIssuesList.length} ?)</span>` : '';
$('#count_features_fond').html(`${stationsCount}${osmoseText} stations`);
}
// 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
$('#current_station_infos').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();
let josm_link = $(this).attr('data-href');
console.log('Sending command to JOSM:', josm_link);
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);
}
// Réinitialiser le compteur avant d'afficher les points
displayedStationsCount = 0;
displayStatsFromGeoJson(geojsondata);
let stats = {
count_hidden_by_filters: 0
};
displayStatsFromGeoJson(geojsondata, stats);
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,
});
// Mettre à jour le compteur de stations filtrées
updateFilteredStationsCount();
}
function displaySocketsList(feature) {
let popupContent = '';
popupContent += '<div class="sockets-list" >'
let type2 = feature.properties.tags['socket:type2']
let type2_combo = feature.properties.tags['socket:type2_combo']
if (type2) {
popupContent += ' <img class="icon-img" src="img/Type2_socket.svg" alt="prise de type 2">'
if (type2 !== 'yes') {
popupContent += '<span class="socket-counter">x ' + type2 + '</span>'
}
}
if (feature.properties.tags['socket:type2_combo']) {
popupContent += ' <img class="icon-img" src="img/type2_combo.svg" alt="prise de type 2 combo CCS">'
if (type2_combo !== 'yes') {
popupContent += '<span class="socket-counter">x ' + type2_combo + '</span>'
}
}
popupContent += '</div>'
return popupContent;
}
function makePopupOfFeature(feature) {
let popupContent = ''
popupContent += '<div class="key-values" >'
// 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 = '<a href="' + value + '">' + value + '</a>'
}
popupContent = popupContent + '<br/><strong class="popup-key">' + key + ' :</strong><span class="popup-value">' + value + '</span>'
}
}
})
popupContent += '</div>'
return popupContent;
}
/**
* application des filtres dans la sélection des bornes à afficher
* @param feature
* @param layer
*/
function eachFeature(feature, layer, stats) {
const maxPowerFilter = parseInt($('#filter_max_output').val()) || lcm_config.filter_max_output_default_value;
let outPowerGuessed = lcm_utils.guessOutputPowerFromFeature(feature);
// Filtrage par puissance
if (outPowerGuessed === 0 || outPowerGuessed === null) {
if (display_unknown_max_power_station === 'hide') {
return;
}
} else {
// Filtrer les stations dont la puissance dépasse le maximum défini
if (outPowerGuessed < maxPowerFilter) {
return;
}
}
// Incrémenter le compteur de stations affichées
displayedStationsCount++;
let popupContent = makePopupOfFeature(feature);
layer.bindPopup(popupContent);
// Vérifier les filtres activés
if (lcm_config.filterCCS && !feature.properties.tags['socket:type2_combo']) {
stats.count_hidden_by_filters++;
return;
}
if (lcm_config.filterType2 && !feature.properties.tags['socket:type2']) {
stats.count_hidden_by_filters++;
return;
}
if (lcm_config.filterDomestic && !feature.properties.tags['socket:typee']) {
stats.count_hidden_by_filters++;
return;
}
if (lcm_config.filterChademo && !feature.properties.tags['socket:chademo']) {
stats.count_hidden_by_filters++;
return;
}
if (lcm_config.filterType1 && !feature.properties.tags['socket:type1']) {
stats.count_hidden_by_filters++;
return;
}
if (lcm_config.filterType3 && !feature.properties.tags['socket:type3']) {
stats.count_hidden_by_filters++;
return;
}
if (lcm_config.filterCableAttached) {
let hasCableAttached = false;
// Vérifier si une des prises a un câble attaché
['socket:type2:cable', 'socket:type2_combo:cable', 'socket:chademo:cable', 'socket:typee:cable', 'socket:type1:cable', 'socket:type3:cable'].forEach(tag => {
if (feature.properties.tags[tag] === 'yes') {
hasCableAttached = true;
}
});
if (!hasCableAttached) {
stats.count_hidden_by_filters++;
return;
}
}
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 = `<span class="no-data"> Aucune information renseignée,
<a class="edit-button" href="https://www.openstreetmap.org/edit?editor=remote&node=${feature.properties.id}">ajoutez la dans OpenStreetMap!</a></span>`;
}
// Calcul du temps de recharge
let rechargeTimeText = '';
if (outPowerGuessed && outPowerGuessed > 0) {
const hours = averageChargeKwh / outPowerGuessed;
const minutes = Math.round(hours * 60);
const h = Math.floor(minutes / 60);
const m = minutes % 60;
rechargeTimeText = `<div class="recharge-time">
⏱️ Temps moyen de recharge:
<strong>${h > 0 ? h + 'h ' : ''}${m} min</strong>
</div>`;
}
// contenu de la popup
let html = `<span class="color-indication" style="background-color: ${lcm_color_utils.getColor(feature)};">${displayOutPowerGuessed}</span>
${rechargeTimeText}
<div class="popup-content">
<div class="socket-list">
${displaySocketsList(feature)}
</div>
<div class="other-infos">
<!-- ${popupContent}-->
</div>
</div>
`
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());
},
mouseout: function () {
// setTimeout(() => this.closePopup(), 15000);
},
click: function () {
this.openPopup();
bindEventsOnJosmRemote(this.getPopup().getElement());
},
});
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);
}
let badtags = lcm_utils.displayBadTagsFromFeature(feature);
if (badtags !== valid_qa_message) {
let circle_alert = L.circle(layer._latlng, {
color: 'red',
fillColor: 'orange',
fillOpacity: 0.5,
radius: radius * 0.85
});
circle_alert.bindTooltip(badtags, {
// permanent: true,
direction: 'top'
}).addTo(bad_tags_markers);
}
circle.bindPopup(html, {
autoPan: false,
closeOnClick: false
});
circle.on({
mouseover: function () {
this.openPopup();
fillDetailsWithFeature(feature);
bindEventsOnJosmRemote(this.getPopup().getElement());
},
mouseout: function () {
// setTimeout(() => this.closePopup(), 15000);
},
click: function () {
this.openPopup();
// Remplir automatiquement #current_station_infos lors du clic
fillDetailsWithFeature(feature);
bindEventsOnJosmRemote(this.getPopup().getElement());
},
});
}
/**
* Remplit le contenu de #current_station_infos avec les informations de la station
* @param {*} feature
*/
function fillDetailsWithFeature(feature) {
// Stocker le feature courant pour pouvoir rafraîchir le détail si besoin
$('#current_station_infos').data('currentFeature', feature);
// 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 link_josm = createJOSMEditLink(feature);
let outPowerGuessed = lcm_utils.guessOutputPowerFromFeature(feature);
let displayOutPowerGuessed = '? kW';
if (outPowerGuessed) {
displayOutPowerGuessed = outPowerGuessed + ' kW max';
}
// AJOUT : Calcul du temps de recharge pour le panneau latéral
let rechargeTimeText = '';
if (outPowerGuessed && outPowerGuessed > 0) {
const hours = averageChargeKwh / outPowerGuessed;
const minutes = Math.round(hours * 60);
const h = Math.floor(minutes / 60);
const m = minutes % 60;
rechargeTimeText = `<div class="recharge-time">
⏱️ Temps estimé pour charger ${averageChargeKwh} kWh à ${outPowerGuessed} kW:
<strong>${h > 0 ? h + 'h ' : ''}${m} min</strong>
</div>`;
} else {
rechargeTimeText = `<div class="recharge-time">⏱️ Temps estimé: puissance inconnue</div>`;
}
let content = '';
let table_details = '';
let count_features_in_table = 0;
table_details += '<div class="key-values" >'
// ne montrer que certains champs dans la popup
lcm_config.tags_to_display_in_popup.forEach((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 = '<a href="' + value + '">' + value + '</a>'
}
table_details += '<br/><strong class="popup-key">' + key + ' :</strong><span class="popup-value">' + value + '</span>'
count_features_in_table++;
}
}
})
table_details += '</div>'
if (!count_features_in_table) {
table_details += '<div class="no-feature-in-table">Aucune information renseignée</div>'
}
content += `
<span class="color-indication" style="background-color: ${lcm_color_utils.getColor(feature)};">${displayOutPowerGuessed}</span>
${rechargeTimeText}
<a href="https://www.openstreetmap.org/directions?from=&to=${feature.geometry.coordinates[1]},${feature.geometry.coordinates[0]}&engine=fossgis_osrm_car#map=14/${feature.geometry.coordinates[1]}/${feature.geometry.coordinates[0]}" class="navigation-link by-car" title="itinéraire en voiture vers cette station"> 🚗</a>
<a href="https://www.openstreetmap.org/directions?from=&to=${feature.geometry.coordinates[1]},${feature.geometry.coordinates[0]}&engine=fossgis_osrm_bike#map=14/${feature.geometry.coordinates[1]}/${feature.geometry.coordinates[0]}" class="navigation-link by-car" title="itinéraire en vélo vers cette station">🚴‍♀️</a>
<a href="https://www.openstreetmap.org/directions?from=&to=${feature.geometry.coordinates[1]},${feature.geometry.coordinates[0]}&engine=fossgis_osrm_foot#map=14/${feature.geometry.coordinates[1]}/${feature.geometry.coordinates[0]}" class="navigation-link by-car" title="itinéraire à pied vers cette station">👠</a>
<a class="edit-button" href="https://www.openstreetmap.org/edit?editor=id&node=${feature.properties.id}">✏️</a>
<a class="edit-button josm" data-href="${link_josm}" href="#">JOSM</a>
<a href="${makeMapCompleteUrl(feature)}" target="_blank" class="edit-button mapcomplete-link" title="Voir sur MapComplete">
<img src="https://mapcomplete.org/assets/themes/charging_stations/plug.svg" alt="icone">
</a>
<a href="${panoramaxLink}" target="_blank" class="panoramax-link" title="Voir sur Panoramax">
<img src="styles/images/panoramax.ico" alt="icone">
</a>
<div class="socket-list">
${displaySocketsList(feature)}
</div>
<div class="table-details" >
${table_details}
</div>
<div class="bad-tags">
<h3>Problèmes de qualité</h3>
${lcm_utils.displayBadTagsFromFeature(feature)}
</div>
`
$('#current_station_infos').html(`<div class='island irve-details'><h2>Détails</h2>${content}</div>`);
}
function makeMapCompleteUrl(feature) {
// https://mapcomplete.org/charging_stations.html?z=16.3&lat=48.85770772656571&lon=2.353630884174322#node/123454656
const center = map.getCenter()
const zoom = map.getZoom()
return `https://mapcomplete.org/charging_stations.html?z=${zoom}&lat=${center.lat}&lon=${center.lng}#node/${feature.properties.id}`
}
function bindFullDetails(feature) {
$('#fullDetails').on('click', () => {
$('#current_station_infos')[0].innerHTML = `<h2>Détails</h2>
${makePopupOfFeature(feature)}
`
})
}
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}`
updateURLWithMapCoordinatesAndZoom();
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;
} 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()
import { lcm_i18n } from './lcm_i18n.js';
$(document).ready(function () {
// Charger le service de traduction
// Détecter la langue du navigateur
const currentLanguage = lcm_i18n.detectLanguage();
console.log('Langue détectée:', currentLanguage);
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(`
<strong>${name}</strong><br>
Type: ${type}<br>
${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: '<div class="osmose-marker-inner">⚡</div>',
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;
lcm_config.osmoseIssuesList = [];
osmoseIssuesCount = issuesList.length;
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;
}
}
lcm_config.osmoseIssuesList.push(issueInfo);
// 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 = `<strong>${issueDetails.title?.auto || 'Titre non disponible'}</strong><br>`;
if (issueDetails.subtitle?.auto) {
popupContent += `<p>${issueDetails.subtitle.auto}</p>`;
}
let proposedTags = '';
if (issueDetails.new_elems && issueDetails.new_elems[0] && issueDetails.new_elems[0].add) {
proposedTags = '<table class="proposed-tags">';
issueDetails.new_elems[0].add.forEach(tag => {
proposedTags += `<tr><td>${tag.k}</td><td>${tag.v}</td></tr>`;
});
proposedTags += '</table>';
// popupContent += `<div class="proposed-tags-container"><h4>Tags proposés :</h4>${proposedTags}</div>`;
}
// Construire le lien JOSM /import
const josmFixUrl = `http://localhost:8111/import?url=https://osmose.openstreetmap.fr/api/0.3/issue/${storedIssueId}/fix/0`;
let josm_buttons = `
<div class="action-buttons">
<a class="edit-button josm" data-href="${josmFixUrl}" href="#" title="Ouvre JOSM et charge la correction proposée">⚡ Réparer dans JOSM</a>
<a href="https://osmose.openstreetmap.fr/fr/issue/${storedIssueId}" target="_blank" title="Voir les détails de l'alerte sur le site Osmose">Voir sur Osmose</a>
</div>`;
popupContent += josm_buttons
// 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
$('#current_station_infos').html(`<div class='island osmose-details'><h2>Analyse Osmose</h2>${josm_buttons}
${proposedTags}
</div>`);
// 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.<br><a href="https://osmose.openstreetmap.fr/fr/issue/${storedIssueId}" target="_blank">Voir sur Osmose</a>`);
// 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.25
// });
// } else {
// // marker.openPopup();
// }
}
// Ajouter un écouteur d'événements pour le changement de visibilité des calques
function init() {
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 () {
console.log('toggleSidePanel', $(this))
$('body').toggleClass('side-panel-open');
});
$('#chercherButton').on('click', function () {
supprimerMarqueurs();
loadOverpassQuery();
geoDataPointsFromApi();
});
$('#setRandomView').on('click', function () {
setRandomView();
loadOverpassQuery();
geoDataPointsFromApi();
});
$('#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 => {
$(`#${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();
});
});
$('#filterBadTags').on('click', function () {
lcm_config.display_alert_bad_tags = !lcm_config.display_alert_bad_tags;
showActiveFilter(lcm_config.display_alert_bad_tags, '#filterBadTags');
if (lcm_config.display_alert_bad_tags) {
bad_tags_markers.clearLayers();
bad_tags_markers.addTo(map);
} else {
bad_tags_markers.remove();
}
refreshDisplay();
});
if (lcm_config.display_restaurants_and_cafes) {
food_places_markers.addTo(map);
}
// Mettre à jour le contrôle des calques
const overlayMaps = {
// ...baseLayers,
// "🗺️ Fond de carte": baseLayers,
"⚡ Stations de recharge": all_stations_markers,
"☕ Restaurants et cafés": food_places_markers,
"💡 Bornes potentielles (Osmose)": osmose_markers,
"💡 Problèmes de qualité": bad_tags_markers
};
const overlayControl = L.control.layers(baseLayers, overlayMaps, {
// collapsed: false,
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);
$('#average_charge_kwh').val(averageChargeKwh);
$('#average_charge_kwh').on('input', function () {
averageChargeKwh = parseFloat($(this).val()) || 26;
// Si un détail de station est affiché, le mettre à jour
if ($('#current_station_infos').data('currentFeature')) {
fillDetailsWithFeature($('#current_station_infos').data('currentFeature'));
}
});
}
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 = `<div class="filter-stats">${displayedStationsCount} stations sur ${totalStations} trouvées</div>`;
// 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);
}
}