/** * 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 = `
`; 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)}% |
'+feature.properties+'') })) } 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(); } } } 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]; ( node["amenity"="restaurant"](${bbox}); node["amenity"="cafe"](${bbox}); ); out body; >; out skel qt;`; const url = `https://overpass-api.de/api/interpreter?data=${encodeURIComponent(query)}`; food_places_markers.clearLayers(); fetch(url) .then(response => response.json()) .then(data => { const geojson = osmtogeojson(data); geojson.features.forEach(feature => { const coords = feature.geometry.coordinates; const properties = feature.properties; const name = properties.tags.name || 'Sans nom'; const type = properties.tags.amenity; const marker = L.marker([coords[1], coords[0]], { icon: foodIcon }); marker.bindPopup(` ${name}