diff --git a/js/lcm_main.js b/js/lcm_main.js
index ff26d0f..5d6849d 100644
--- a/js/lcm_main.js
+++ b/js/lcm_main.js
@@ -15,7 +15,7 @@ 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'
-import routing from './lcm_routing.js'
+import Routing from './lcm_routing.js'
let geojsondata;
let lastLatLng;
@@ -48,6 +48,11 @@ let endItinerary = [0, 0];
let map = L.map('map')
L.control.scale().addTo(map)
+
+ // Initialiser le routing
+ window.routing = new Routing(map);
+
+
setCoordinatesOfLeafletMapFromQueryParameters()
/**
@@ -216,7 +221,7 @@ function searchLocation() {
.text('Itinéraire '+index)
.on('click', function(e) {
e.preventDefault();
- calculerEtAfficherItineraire(place, 'car');
+ window.routing.calculerEtAfficherItineraire(place, 'car');
});
// Ajoute le bouton à côté de l'option (ou dans une div, selon ton HTML)
@@ -286,7 +291,7 @@ function setCoordinatesOfLeafletMapFromQueryParameters() {
setRandomView();
}
- calculerEtAfficherItineraire(endItinerary)
+ window.routing.calculerEtAfficherItineraire(endItinerary)
}
// mettre à jour les infos queryparam dans l'url pour passer le lien avec l'état inclus
function updateURLWithMapCoordinatesAndZoom() {
@@ -615,7 +620,7 @@ function displayPointsFromApi(points, convert_to_osm_json) {
});
updateFilteredStationsCount();
- calculerEtAfficherItineraire(endItinerary);
+ window.routing.calculerEtAfficherItineraire(endItinerary);
}
@@ -1438,7 +1443,7 @@ function init() {
// ... dans la gestion du clic sur un résultat Addok ...
$('.route-to-place').on('click', function() {
const place = $(this).data('place');
- calculerEtAfficherItineraire(place, 'car');
+ window.routing.calculerEtAfficherItineraire(place, 'car');
});
/**
@@ -1593,6 +1598,18 @@ function init() {
fillDetailsWithFeature($('#current_station_infos').data('currentFeature'));
}
});
+
+
+ // Modifier les gestionnaires d'événements pour utiliser window.routing
+ map.on('contextmenu', function(e) {
+ let popup = L.popup()
+ .setLatLng(e.latlng)
+ .setContent(`
+ Choisir comme départ
+ Choisir comme arrivée
+ `)
+ .openOn(map);
+ });
}
@@ -1628,278 +1645,4 @@ function updateFilteredStationsCount() {
} else {
$('#filter_max_output_display').after(filterStats);
}
-}
-// au clic droit, proposer les points d'itinéraire
-map.on('contextmenu', function(e) {
- // Créer un menu contextuel simple
- let popup = L.popup()
- .setLatLng(e.latlng)
- .setContent(`
- Choisir comme départ
- Choisir comme arrivée
- `)
- .openOn(map);
-});
-
-
-// Fonctions globales pour le menu contextuel
-window.setStartMarkerFromPopup = function(latlng) {
- startItinerary = [latlng[0], latlng[1]]
-
- setStartMarker({lat: latlng[0], lng: latlng[1]});
- map.closePopup();
- if (endItinerary) calculerEtAfficherItineraire();
-};
-window.setEndMarkerFromPopup = function(latlng) {
- setEndMarker({lat: latlng[0], lng: latlng[1]});
- endItinerary = [latlng[0], latlng[1]]
- map.closePopup();
- if (startItinerary) calculerEtAfficherItineraire();
-};
-
-
-// Déclarer une variable globale pour le tracé
-window.routePolyline = null;
-window.lastRouteDestination = null;
-
-// Ajouter cette fonction avant calculerEtAfficherItineraire
-async function searchChargingStationsAroundPoint(lat, lon, radius = 1500) {
- const query = `
- [out:json][timeout:25];
- (
- node["amenity"="charging_station"](around:${radius},${lat},${lon});
- way["amenity"="charging_station"](around:${radius},${lat},${lon});
- relation["amenity"="charging_station"](around:${radius},${lat},${lon});
- );
- out body;
- >;
- out skel qt;`;
-
- const url = `https://overpass-api.de/api/interpreter?data=${encodeURIComponent(query)}`;
-
- try {
- const response = await fetch(url);
- const data = await response.json();
- return data.elements || [];
- } catch (error) {
- console.error('Erreur lors de la recherche des stations de recharge:', error);
- return [];
- }
-}
-
-async function calculerEtAfficherItineraire(destination, mode = 'car') {
- // Récupérer le point de départ (centre de la carte)
- const startLat = startItinerary[0];
- const startLon = startItinerary[1];
- const endLat = endItinerary[0];
- const endLon = endItinerary[1];
-
- // Récupérer les paramètres de la batterie
- const batteryCapacity = parseFloat($('#battery_capacity').val()) || 40;
- const batteryStartLevel = parseFloat($('#battery_start_level').val()) || 90;
- const consumptionPerKm = parseFloat($('#consumption_per_km').val()) || 160;
- const minBatteryLevel = parseFloat($('#min_battery_level').val()) || 15;
- const chargeToLevel = parseFloat($('#charge_to_level').val()) || 80;
- const maxChargePower = parseFloat($('#max_charge_power').val()) || 50;
-
- // Construire l'URL OSRM
- const url = `https://router.project-osrm.org/route/v1/${mode}/${startLon},${startLat};${endLon},${endLat}?overview=full&geometries=geojson&alternatives=false&steps=false`;
-
- $('#current_station_infos').html('Calcul de l\'itinéraire en cours…');
-
- try {
- const response = await fetch(url);
- const data = await response.json();
-
- if (!data.routes || !data.routes.length) {
- $('#current_station_infos').html('Aucun itinéraire trouvé.');
- return;
- }
-
- const route = data.routes[0];
- const coords = route.geometry.coordinates.map(c => [c[1], c[0]]); // [lat, lon]
-
- // Supprimer l'ancien tracé et les marqueurs
- if (window.routePolyline) {
- map.removeLayer(window.routePolyline);
- }
- if (window.pauseMarkers) {
- window.pauseMarkers.forEach(marker => map.removeLayer(marker));
- }
- if (window.batteryMarkers) {
- window.batteryMarkers.forEach(marker => map.removeLayer(marker));
- }
-
- // Afficher le nouveau tracé sur la carte
- window.routePolyline = L.polyline(coords, {color: '#0059ce', weight: 5}).addTo(map);
- // Supprimer le fitBounds qui déplace la vue
- // map.fitBounds(window.routePolyline.getBounds());
-
- // Calculer la consommation totale
- const totalDistanceKm = route.distance / 1000;
- const totalConsumptionKwh = (totalDistanceKm * consumptionPerKm) / 1000;
- const batteryStartKwh = (batteryCapacity * batteryStartLevel) / 100;
- const batteryEndKwh = batteryStartKwh - totalConsumptionKwh;
- const batteryEndLevel = (batteryEndKwh / batteryCapacity) * 100;
-
- // Ajouter les marqueurs de niveau de batterie
- window.batteryMarkers = [];
- const batteryLevels = [90, 80, 70, 60, 50, 40, 30, 20, 15];
- let accumulatedDistance = 0;
- let currentBatteryLevel = batteryStartLevel;
-
- for (let i = 0; i < coords.length - 1; i++) {
- const segmentDistance = L.latLng(coords[i]).distanceTo(coords[i + 1]) / 1000; // en km
- const segmentConsumption = (segmentDistance * consumptionPerKm) / 1000; // en kWh
- const nextBatteryLevel = ((batteryStartKwh - (accumulatedDistance * consumptionPerKm / 1000)) / batteryCapacity) * 100;
-
- // Vérifier si on atteint un niveau de batterie à marquer
- for (const level of batteryLevels) {
- if (currentBatteryLevel > level && nextBatteryLevel <= level) {
- const ratio = (level - currentBatteryLevel) / (nextBatteryLevel - currentBatteryLevel);
- const markerCoords = [
- coords[i][0] + (coords[i + 1][0] - coords[i][0]) * ratio,
- coords[i][1] + (coords[i + 1][1] - coords[i][1]) * ratio
- ];
-
- const batteryMarker = L.marker(markerCoords, {
- icon: L.divIcon({
- className: 'battery-marker',
- html: `🔋 ${level}%`,
- iconSize: [30, 30]
- })
- }).addTo(map);
-
- let popupContent = `
-
Niveau de batterie : ${level}% `;
-
- if (level <= minBatteryLevel) {
- popupContent += `
⚠️ Recharge recommandée !
`;
- }
-
- popupContent += `
Distance parcourue : ${(accumulatedDistance + segmentDistance * ratio).toFixed(1)} km
-
Consommation : ${((accumulatedDistance + segmentDistance * ratio) * consumptionPerKm / 1000).toFixed(1)} kWh
-
`;
-
- batteryMarker.bindPopup(popupContent);
- window.batteryMarkers.push(batteryMarker);
- }
- }
-
- accumulatedDistance += segmentDistance;
- currentBatteryLevel = nextBatteryLevel;
- }
-
- // Calculer le nombre de pauses nécessaires (1 pause toutes les 2 heures)
- const durationHours = route.duration / 3600;
- const numberOfPauses = Math.floor(durationHours / 2);
- const pauseDurationMinutes = 10;
- const totalPauseTimeMinutes = numberOfPauses * pauseDurationMinutes;
-
- // Calculer les temps de recharge nécessaires
- let totalRechargeTimeMinutes = 0;
- if (numberOfPauses > 0) {
- const pauseInterval = totalDistance / (numberOfPauses + 1);
-
- for (let i = 1; i <= numberOfPauses; i++) {
- const pauseDistance = pauseInterval * i;
- const nextPauseDistance = (i < numberOfPauses) ? pauseInterval : (totalDistance - pauseDistance);
-
- // Calculer la consommation jusqu'à la prochaine pause
- const consumptionToNextPause = (nextPauseDistance / 1000) * consumptionPerKm / 1000; // en kWh
- const batteryLevelNeeded = (consumptionToNextPause / batteryCapacity) * 100 + minBatteryLevel;
-
- // Calculer le niveau de batterie actuel
- const currentBatteryLevel = ((batteryStartKwh - (pauseDistance / 1000 * consumptionPerKm / 1000)) / batteryCapacity) * 100;
-
- // Calculer la quantité d'énergie à recharger
- const energyToRecharge = (batteryLevelNeeded - currentBatteryLevel) * batteryCapacity / 100;
-
- // Calculer le temps de recharge nécessaire
- const rechargeTimeHours = energyToRecharge / maxChargePower;
- const rechargeTimeMinutes = Math.ceil(rechargeTimeHours * 60);
-
- // Ajouter le temps de recharge au total (minimum 10 minutes)
- totalRechargeTimeMinutes += Math.max(rechargeTimeMinutes, pauseDurationMinutes);
- }
- }
-
- // Convertir les différentes durées en format lisible
- const formatDuration = (minutes) => {
- if (minutes >= 60) {
- const hours = Math.floor(minutes / 60);
- const mins = minutes % 60;
- return `${hours}h${mins > 0 ? ` ${mins}min` : ''}`;
- }
- return `${minutes} min`;
- };
-
- const durationNoPause = formatDuration(durationMin);
- const durationWithPauses = formatDuration(durationMin + totalPauseTimeMinutes);
- const durationWithRecharge = formatDuration(durationMin + totalRechargeTimeMinutes);
-
- $('#routing_infos').html(`
-
- Itinéraire
- Distance : ${distanceKm} km
-
-
Niveaux de batterie :
-
Départ : ${batteryStartLevel}%
-
Arrivée : ${batteryEndLevel.toFixed(1)}%
-
-
-
Durées estimées :
-
Sans pause : ${durationNoPause}
-
Avec pauses (10 min) : ${durationWithPauses}
-
Avec recharges : ${durationWithRecharge}
-
- Nombre de pauses recommandées : ${numberOfPauses} (10 min toutes les 2h)
- Consommation totale estimée : ${totalConsumptionKwh.toFixed(1)} kWh
-
- 🚗 Voiture
- 🚴 Vélo
- 🚶 Piéton
-
-
- `);
-
- // Stocker la destination pour recalculer facilement
- window.lastRouteDestination = destination;
- } catch (e) {
- $('#current_station_infos').html('Erreur lors du calcul de l\'itinéraire.');
- }
-}
-
-function setStartMarker(latlng) {
- if (startMarker) map.removeLayer(startMarker);
- startMarker = L.marker(latlng, {
- draggable: true,
- icon: L.divIcon({
- className: 'start-marker',
- html: '🟢 départ',
- iconSize: [30, 30]
- })
- }).addTo(map);
- startCoords = [latlng.lat, latlng.lng];
- startMarker.on('dragend', function(e) {
- startCoords = [e.target.getLatLng().lat, e.target.getLatLng().lng];
- if (endCoords) calculerEtAfficherItineraire();
- });
-}
-
-function setEndMarker(latlng) {
- if (endMarker) map.removeLayer(endMarker);
- endMarker = L.marker(latlng, {
- draggable: true,
- icon: L.divIcon({
- className: 'end-marker',
- html: '🔴 arrivée',
- iconSize: [30, 30]
- })
- }).addTo(map);
- endCoords = [latlng.lat, latlng.lng];
- endMarker.on('dragend', function(e) {
- endCoords = [e.target.getLatLng().lat, e.target.getLatLng().lng];
- if (startCoords) calculerEtAfficherItineraire();
- });
}
\ No newline at end of file
diff --git a/js/lcm_routing.js b/js/lcm_routing.js
index 5562a43..d379f4c 100644
--- a/js/lcm_routing.js
+++ b/js/lcm_routing.js
@@ -1,8 +1,260 @@
/**
* gestion des itinéraires pour libre charge map
*/
-class routing{
+class Routing {
+ constructor(map) {
+ this.map = map;
+ this.routePolyline = null;
+ this.pauseMarkers = [];
+ this.batteryMarkers = [];
+ this.startMarker = null;
+ this.endMarker = null;
+ this.startCoords = null;
+ this.endCoords = null;
+ this.startItinerary = [0, 0];
+ this.endItinerary = [0, 0];
+ this.lastRouteDestination = null;
+ }
+ setStartMarker(latlng) {
+ if (this.startMarker) this.map.removeLayer(this.startMarker);
+ this.startMarker = L.marker(latlng, {
+ draggable: true,
+ icon: L.divIcon({
+ className: 'start-marker',
+ html: '🟢 départ',
+ iconSize: [30, 30]
+ })
+ }).addTo(this.map);
+ this.startCoords = [latlng.lat, latlng.lng];
+ this.startMarker.on('dragend', (e) => {
+ this.startCoords = [e.target.getLatLng().lat, e.target.getLatLng().lng];
+ if (this.endCoords) this.calculerEtAfficherItineraire();
+ });
+ }
+
+ setEndMarker(latlng) {
+ if (this.endMarker) this.map.removeLayer(this.endMarker);
+ this.endMarker = L.marker(latlng, {
+ draggable: true,
+ icon: L.divIcon({
+ className: 'end-marker',
+ html: '🔴 arrivée',
+ iconSize: [30, 30]
+ })
+ }).addTo(this.map);
+ this.endCoords = [latlng.lat, latlng.lng];
+ this.endMarker.on('dragend', (e) => {
+ this.endCoords = [e.target.getLatLng().lat, e.target.getLatLng().lng];
+ if (this.startCoords) this.calculerEtAfficherItineraire();
+ });
+ }
+
+ setStartMarkerFromPopup(latlng) {
+ this.startItinerary = [latlng[0], latlng[1]];
+ this.setStartMarker({lat: latlng[0], lng: latlng[1]});
+ this.map.closePopup();
+ if (this.endItinerary) this.calculerEtAfficherItineraire();
+ }
+
+ setEndMarkerFromPopup(latlng) {
+ this.setEndMarker({lat: latlng[0], lng: latlng[1]});
+ this.endItinerary = [latlng[0], latlng[1]];
+ this.map.closePopup();
+ if (this.startItinerary) this.calculerEtAfficherItineraire();
+ }
+
+ async searchChargingStationsAroundPoint(lat, lon, radius = 1500) {
+ const query = `
+ [out:json][timeout:25];
+ (
+ node["amenity"="charging_station"](around:${radius},${lat},${lon});
+ way["amenity"="charging_station"](around:${radius},${lat},${lon});
+ relation["amenity"="charging_station"](around:${radius},${lat},${lon});
+ );
+ out body;
+ >;
+ out skel qt;`;
+
+ const url = `https://overpass-api.de/api/interpreter?data=${encodeURIComponent(query)}`;
+
+ try {
+ const response = await fetch(url);
+ const data = await response.json();
+ return data.elements || [];
+ } catch (error) {
+ console.error('Erreur lors de la recherche des stations de recharge:', error);
+ return [];
+ }
+ }
+
+ formatDuration(minutes) {
+ if (minutes >= 60) {
+ const hours = Math.floor(minutes / 60);
+ const mins = minutes % 60;
+ return `${hours}h${mins > 0 ? ` ${mins}min` : ''}`;
+ }
+ return `${minutes} min`;
+ }
+
+ async calculerEtAfficherItineraire(destination, mode = 'car') {
+ const startLat = this.startItinerary[0];
+ const startLon = this.startItinerary[1];
+ const endLat = this.endItinerary[0];
+ const endLon = this.endItinerary[1];
+
+ // Récupérer les paramètres de la batterie
+ const batteryCapacity = parseFloat($('#battery_capacity').val()) || 40;
+ const batteryStartLevel = parseFloat($('#battery_start_level').val()) || 90;
+ const consumptionPerKm = parseFloat($('#consumption_per_km').val()) || 160;
+ const minBatteryLevel = parseFloat($('#min_battery_level').val()) || 15;
+ const chargeToLevel = parseFloat($('#charge_to_level').val()) || 80;
+ const maxChargePower = parseFloat($('#max_charge_power').val()) || 50;
+
+ const url = `https://router.project-osrm.org/route/v1/${mode}/${startLon},${startLat};${endLon},${endLat}?overview=full&geometries=geojson&alternatives=false&steps=false`;
+
+ $('#routing_infos').html('Calcul de l\'itinéraire en cours…');
+
+ try {
+ const response = await fetch(url);
+ const data = await response.json();
+
+ if (!data.routes || !data.routes.length) {
+ $('#routing_infos').html('Aucun itinéraire trouvé.');
+ return;
+ }
+
+ const route = data.routes[0];
+ const coords = route.geometry.coordinates.map(c => [c[1], c[0]]);
+
+ // Nettoyer les anciens éléments
+ if (this.routePolyline) {
+ this.map.removeLayer(this.routePolyline);
+ }
+ this.pauseMarkers.forEach(marker => this.map.removeLayer(marker));
+ this.batteryMarkers.forEach(marker => this.map.removeLayer(marker));
+
+ // Afficher le nouveau tracé
+ this.routePolyline = L.polyline(coords, {color: '#0059ce', weight: 5}).addTo(this.map);
+
+ // Calculs de consommation
+ const totalDistanceKm = route.distance / 1000;
+ const totalConsumptionKwh = (totalDistanceKm * consumptionPerKm) / 1000;
+ const batteryStartKwh = (batteryCapacity * batteryStartLevel) / 100;
+ const batteryEndKwh = batteryStartKwh - totalConsumptionKwh;
+ const batteryEndLevel = (batteryEndKwh / batteryCapacity) * 100;
+
+ // Marqueurs de niveau de batterie
+ this.batteryMarkers = [];
+ const batteryLevels = [90, 80, 70, 60, 50, 40, 30, 20, 15];
+ let accumulatedDistance = 0;
+ let currentBatteryLevel = batteryStartLevel;
+
+ for (let i = 0; i < coords.length - 1; i++) {
+ const segmentDistance = L.latLng(coords[i]).distanceTo(coords[i + 1]) / 1000;
+ const segmentConsumption = (segmentDistance * consumptionPerKm) / 1000;
+ const nextBatteryLevel = ((batteryStartKwh - (accumulatedDistance * consumptionPerKm / 1000)) / batteryCapacity) * 100;
+
+ for (const level of batteryLevels) {
+ if (currentBatteryLevel > level && nextBatteryLevel <= level) {
+ const ratio = (level - currentBatteryLevel) / (nextBatteryLevel - currentBatteryLevel);
+ const markerCoords = [
+ coords[i][0] + (coords[i + 1][0] - coords[i][0]) * ratio,
+ coords[i][1] + (coords[i + 1][1] - coords[i][1]) * ratio
+ ];
+
+ const batteryMarker = L.marker(markerCoords, {
+ interactive: true,
+ icon: L.divIcon({
+ className: 'battery-marker',
+ html: `🔋 ${level}%`,
+ iconSize: [30, 30]
+ })
+ }).addTo(this.map);
+
+ let popupContent = `
+
Niveau de batterie : ${level}% `;
+
+ if (level <= minBatteryLevel) {
+ popupContent += `
⚠️ Recharge recommandée !
`;
+ }
+
+ popupContent += `
Distance parcourue : ${(accumulatedDistance + segmentDistance * ratio).toFixed(1)} km
+
Consommation : ${((accumulatedDistance + segmentDistance * ratio) * consumptionPerKm / 1000).toFixed(1)} kWh
+
`;
+
+ batteryMarker.bindPopup(popupContent);
+ this.batteryMarkers.push(batteryMarker);
+ }
+ }
+
+ accumulatedDistance += segmentDistance;
+ currentBatteryLevel = nextBatteryLevel;
+ }
+
+ // Calcul des pauses et temps de recharge
+ const durationHours = route.duration / 3600;
+ const numberOfPauses = Math.floor(durationHours / 2);
+ const pauseDurationMinutes = 10;
+ const totalPauseTimeMinutes = numberOfPauses * pauseDurationMinutes;
+
+ let totalRechargeTimeMinutes = 0;
+ if (numberOfPauses > 0) {
+ const pauseInterval = totalDistanceKm / (numberOfPauses + 1);
+
+ for (let i = 1; i <= numberOfPauses; i++) {
+ const pauseDistance = pauseInterval * i;
+ const nextPauseDistance = (i < numberOfPauses) ? pauseInterval : (totalDistanceKm - pauseDistance);
+
+ const consumptionToNextPause = (nextPauseDistance / 1000) * consumptionPerKm / 1000;
+ const batteryLevelNeeded = (consumptionToNextPause / batteryCapacity) * 100 + minBatteryLevel;
+
+ const currentBatteryLevel = ((batteryStartKwh - (pauseDistance / 1000 * consumptionPerKm / 1000)) / batteryCapacity) * 100;
+
+ const energyToRecharge = (batteryLevelNeeded - currentBatteryLevel) * batteryCapacity / 100;
+
+ const rechargeTimeHours = energyToRecharge / maxChargePower;
+ const rechargeTimeMinutes = Math.ceil(rechargeTimeHours * 60);
+
+ totalRechargeTimeMinutes += Math.max(rechargeTimeMinutes, pauseDurationMinutes);
+ }
+ }
+
+ const durationMin = Math.round(route.duration / 60);
+ const durationNoPause = this.formatDuration(durationMin);
+ const durationWithPauses = this.formatDuration(durationMin + totalPauseTimeMinutes);
+ const durationWithRecharge = this.formatDuration(durationMin + totalRechargeTimeMinutes);
+
+ $('#routing_infos').html(`
+
+ Itinéraire
+ Distance : ${totalDistanceKm.toFixed(1)} km
+
+
Niveaux de batterie :
+
Départ : ${batteryStartLevel}%
+
Arrivée : ${batteryEndLevel.toFixed(1)}%
+
+
+
Durées estimées :
+
Sans pause : ${durationNoPause}
+
Avec pauses (10 min) : ${durationWithPauses}
+
Avec recharges : ${durationWithRecharge}
+
+ Nombre de pauses recommandées : ${numberOfPauses} (10 min toutes les 2h)
+ Consommation totale estimée : ${totalConsumptionKwh.toFixed(1)} kWh
+
+ 🚗 Voiture
+ 🚴 Vélo
+ 🚶 Piéton
+
+
+ `);
+
+ this.lastRouteDestination = destination;
+ } catch (e) {
+ $('#routing_infos').html('Erreur lors du calcul de l\'itinéraire.');
+ }
+ }
}
-export default routing
\ No newline at end of file
+export default Routing;
\ No newline at end of file