/**
* gestion des itinéraires pour libre charge map
*/
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 = `
⚠️ Recharge recommandée !
`; } popupContent += `Distance parcourue : ${(accumulatedDistance + segmentDistance * ratio).toFixed(1)} km
Consommation : ${((accumulatedDistance + segmentDistance * ratio) * consumptionPerKm / 1000).toFixed(1)} kWh