mirror of
				https://forge.chapril.org/tykayn/libre-charge-map
				synced 2025-10-09 17:02:46 +02:00 
			
		
		
		
	add calcul itineraire et décharge
This commit is contained in:
		
							parent
							
								
									872d14e17c
								
							
						
					
					
						commit
						da7292e746
					
				
					 8 changed files with 502 additions and 53 deletions
				
			
		
							
								
								
									
										38
									
								
								index.html
									
										
									
									
									
								
							
							
						
						
									
										38
									
								
								index.html
									
										
									
									
									
								
							|  | @ -141,6 +141,44 @@ | ||||||
|                         la nuit, ou bien où l'on séjourne plus longtemps. |                         la nuit, ou bien où l'on séjourne plus longtemps. | ||||||
|                     </span> |                     </span> | ||||||
|                 </p> |                 </p> | ||||||
|  |                 <div class="filter-battery" id="filterBattery"> | ||||||
|  |                     <h3>Paramètres de la batterie</h3> | ||||||
|  |                     <p> | ||||||
|  |                         <label for="battery_capacity"> | ||||||
|  |                             Capacité de la batterie (kWh) : | ||||||
|  |                         </label> | ||||||
|  |                         <input type="number" id="battery_capacity" min="20" max="200" value="40" step="1" | ||||||
|  |                             style="width: 4em;"> | ||||||
|  |                     </p> | ||||||
|  |                     <p> | ||||||
|  |                         <label for="battery_start_level"> | ||||||
|  |                             Niveau de départ (%) : | ||||||
|  |                         </label> | ||||||
|  |                         <input type="number" id="battery_start_level" min="0" max="100" value="90" step="1" | ||||||
|  |                             style="width: 4em;"> | ||||||
|  |                     </p> | ||||||
|  |                     <p> | ||||||
|  |                         <label for="consumption_per_km"> | ||||||
|  |                             Consommation (Wh/km) : | ||||||
|  |                         </label> | ||||||
|  |                         <input type="number" id="consumption_per_km" min="100" max="300" value="160" step="1" | ||||||
|  |                             style="width: 4em;"> | ||||||
|  |                     </p> | ||||||
|  |                     <p> | ||||||
|  |                         <label for="min_battery_level"> | ||||||
|  |                             Niveau minimum recommandé (%) : | ||||||
|  |                         </label> | ||||||
|  |                         <input type="number" id="min_battery_level" min="0" max="100" value="15" step="1" | ||||||
|  |                             style="width: 4em;"> | ||||||
|  |                     </p> | ||||||
|  |                     <p> | ||||||
|  |                         <label for="charge_to_level"> | ||||||
|  |                             Niveau de charge visé (%) : | ||||||
|  |                         </label> | ||||||
|  |                         <input type="number" id="charge_to_level" min="0" max="100" value="80" step="1" | ||||||
|  |                             style="width: 4em;"> | ||||||
|  |                     </p> | ||||||
|  |                 </div> | ||||||
|                 <div class="filter-sockets" id="filterSockets"> |                 <div class="filter-sockets" id="filterSockets"> | ||||||
| 
 | 
 | ||||||
|                     <h3>Types de prises</h3> |                     <h3>Types de prises</h3> | ||||||
|  |  | ||||||
							
								
								
									
										466
									
								
								js/lcm_main.js
									
										
									
									
									
								
							
							
						
						
									
										466
									
								
								js/lcm_main.js
									
										
									
									
									
								
							|  | @ -4,10 +4,18 @@ | ||||||
|  * lister les bornes trouvées dans la page |  * lister les bornes trouvées dans la page | ||||||
|  * @type {boolean} |  * @type {boolean} | ||||||
|  */ |  */ | ||||||
|  | 
 | ||||||
|  | import {lcm_i18n} from './lcm_i18n.js'; | ||||||
|  |   | ||||||
|  |     // Détecter la langue du navigateur
 | ||||||
|  |     const currentLanguage = lcm_i18n.detectLanguage(); | ||||||
|  |     console.log('Langue détectée:', currentLanguage); | ||||||
|  | 
 | ||||||
| import lcm_config from './lcm_config.js' | import lcm_config from './lcm_config.js' | ||||||
| import lcm_utils, { valid_qa_message } from './lcm_utils.js' | import lcm_utils, { valid_qa_message } from './lcm_utils.js' | ||||||
| import lcm_color_utils from './lcm_color_utils.js' | import lcm_color_utils from './lcm_color_utils.js' | ||||||
| import { sendToJOSM, createJOSMEditLink } from './lcm_editor.js' | import { sendToJOSM, createJOSMEditLink } from './lcm_editor.js' | ||||||
|  | import routing from './lcm_routing.js' | ||||||
| 
 | 
 | ||||||
| let geojsondata; | let geojsondata; | ||||||
| let lastLatLng; | let lastLatLng; | ||||||
|  | @ -15,7 +23,16 @@ let searchLocationMarker = null; | ||||||
| let count_hidden_by_filters = 0; | let count_hidden_by_filters = 0; | ||||||
| let averageChargeKwh = 26; | let averageChargeKwh = 26; | ||||||
| 
 | 
 | ||||||
|  | let routePolyline = null; | ||||||
|  | let routeMarkers = []; | ||||||
|  | let startMarker = null; | ||||||
|  | let endMarker = null; | ||||||
|  | let startCoords = null; | ||||||
|  | let endCoords = null; | ||||||
| 
 | 
 | ||||||
|  | // Déclarer les variables d'itinéraire au début
 | ||||||
|  | let startItinerary = [0, 0]; | ||||||
|  | let endItinerary = [0, 0]; | ||||||
| 
 | 
 | ||||||
| // serveurs de tuiles: https://wiki.openstreetmap.org/wiki/Tile_servers
 | // serveurs de tuiles: https://wiki.openstreetmap.org/wiki/Tile_servers
 | ||||||
| // https://stamen-tiles.a.ssl.fastly.net/toner/{z}/{x}/{y}.png
 | // https://stamen-tiles.a.ssl.fastly.net/toner/{z}/{x}/{y}.png
 | ||||||
|  | @ -31,6 +48,8 @@ let averageChargeKwh = 26; | ||||||
| let map = L.map('map') | let map = L.map('map') | ||||||
| L.control.scale().addTo(map) | L.control.scale().addTo(map) | ||||||
| 
 | 
 | ||||||
|  | setCoordinatesOfLeafletMapFromQueryParameters() | ||||||
|  | 
 | ||||||
| /** | /** | ||||||
|  * filtres à toggle par des boutons dans la page |  * filtres à toggle par des boutons dans la page | ||||||
|  * à appliquer à chaque rafraîchissement des points geojson |  * à appliquer à chaque rafraîchissement des points geojson | ||||||
|  | @ -38,21 +57,13 @@ L.control.scale().addTo(map) | ||||||
|  */ |  */ | ||||||
| let filterStatesAvailable = ['hide', 'show', 'showOnly'] | 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_unknown_max_power_station = 'show'; | ||||||
| let display_alert_cable_missing = '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
 |  | ||||||
| 
 | 
 | ||||||
|  | const start = map.getCenter(); | ||||||
|  | const startLat = start.lat; | ||||||
|  | const startLon = start.lng; | ||||||
| 
 | 
 | ||||||
| // Ajouter cette fonction avant searchLocation
 | // Ajouter cette fonction avant searchLocation
 | ||||||
| function moveToLocation(place) { | function moveToLocation(place) { | ||||||
|  | @ -198,7 +209,19 @@ function searchLocation() { | ||||||
|                         .val(index) |                         .val(index) | ||||||
|                         .text(displayText) |                         .text(displayText) | ||||||
|                         .data('place', place); |                         .data('place', place); | ||||||
|  | 
 | ||||||
|  |                     // Création du bouton itinéraire
 | ||||||
|  |                     const routeBtn = $('<button>') | ||||||
|  |                         .addClass('route-to-place') | ||||||
|  |                         .text('Itinéraire '+index) | ||||||
|  |                         .on('click', function(e) { | ||||||
|  |                             e.preventDefault(); | ||||||
|  |                             calculerEtAfficherItineraire(place, 'car'); | ||||||
|  |                         }); | ||||||
|  | 
 | ||||||
|  |                     // Ajoute le bouton à côté de l'option (ou dans une div, selon ton HTML)
 | ||||||
|                     resultsDropdown.append(option); |                     resultsDropdown.append(option); | ||||||
|  |                     resultsDropdown.after(routeBtn); | ||||||
|                 }); |                 }); | ||||||
|                 resultsDropdown.show(); |                 resultsDropdown.show(); | ||||||
|                 $('.close-results-button').show(); |                 $('.close-results-button').show(); | ||||||
|  | @ -226,25 +249,62 @@ function setCoordinatesOfLeafletMapFromQueryParameters() { | ||||||
|     const lat = urlParams.get('lat'); |     const lat = urlParams.get('lat'); | ||||||
|     const lng = urlParams.get('lng'); |     const lng = urlParams.get('lng'); | ||||||
|     const zoom = urlParams.get('zoom'); |     const zoom = urlParams.get('zoom'); | ||||||
|  |     const startLat = urlParams.get('startLat'); | ||||||
|  |     const startLng = urlParams.get('startLng'); | ||||||
|  |     const endLat = urlParams.get('endLat'); | ||||||
|  |     const endLng = urlParams.get('endLng'); | ||||||
|  |     const batteryCapacity = urlParams.get('batteryCapacity'); | ||||||
|  |     const consumptionPerKm = urlParams.get('consumptionPerKm'); | ||||||
| 
 | 
 | ||||||
|  |     // Mettre à jour les champs de batterie si présents dans l'URL
 | ||||||
|  |     if (batteryCapacity) { | ||||||
|  |         $('#battery_capacity').val(batteryCapacity); | ||||||
|  |     } | ||||||
|  |     if (consumptionPerKm) { | ||||||
|  |         $('#consumption_per_km').val(consumptionPerKm); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Mettre à jour les coordonnées de départ et d'arrivée si présentes
 | ||||||
|  |     if (startLat && startLng) { | ||||||
|  |         startItinerary = [parseFloat(startLat), parseFloat(startLng)]; | ||||||
|  |     } | ||||||
|  |     if (endLat && endLng) { | ||||||
|  |         endItinerary = [parseFloat(endLat), parseFloat(endLng)]; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Initialiser la carte avec les coordonnées par défaut
 | ||||||
|  |     if (!map) { | ||||||
|  |         map = L.map('map'); | ||||||
|  |         L.control.scale().addTo(map); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Si des coordonnées sont fournies dans l'URL, les utiliser
 | ||||||
|     if (lat && lng && zoom) { |     if (lat && lng && zoom) { | ||||||
|         map = map.setView([lat, lng], zoom); |         map.setView([lat, lng], zoom); | ||||||
|     } else { |     } else { | ||||||
|         console.error('Les paramètres de coordonnées et de zoom doivent être présents dans l\'URL.'); |         console.error('Les paramètres de coordonnées et de zoom doivent être présents dans l\'URL.'); | ||||||
|         setRandomView(); |         setRandomView(); | ||||||
|     } |     } | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
|  |     calculerEtAfficherItineraire(endItinerary) | ||||||
|  | } | ||||||
|  | // mettre à jour les infos queryparam dans l'url pour passer le lien avec l'état inclus
 | ||||||
| function updateURLWithMapCoordinatesAndZoom() { | function updateURLWithMapCoordinatesAndZoom() { | ||||||
|     // Récupère les coordonnées et le niveau de zoom de la carte
 |     // Récupère les coordonnées et le niveau de zoom de la carte
 | ||||||
|     const center = map.getCenter() |     const center = map.getCenter(); | ||||||
|     const zoom = map.getZoom() |     const zoom = map.getZoom(); | ||||||
| 
 | 
 | ||||||
|     // Construit l'URL avec les paramètres de coordonnées et de zoom
 |     const batteryCapacity = $('#battery_capacity').val(); | ||||||
|     const url = `#coords=1&lat=${center.lat}&lng=${center.lng}&zoom=${zoom}` |     const consumptionPerKm = $('#consumption_per_km').val(); | ||||||
|     // Met à jour l'URL de la page
 | 
 | ||||||
|     history.replaceState(null, null, url) |     // Construit l'URL avec tous les paramètres
 | ||||||
|     updateExternalEditorsLinks() |     const url = `#coords=1&lat=${center.lat}&lng=${center.lng}&zoom=${zoom}` + | ||||||
|  |         `&startLat=${startItinerary[0]}&startLng=${startItinerary[1]}` + | ||||||
|  |         `&endLat=${endItinerary[0]}&endLng=${endItinerary[1]}` + | ||||||
|  |         `&batteryCapacity=${batteryCapacity}&consumptionPerKm=${consumptionPerKm}`; | ||||||
|  | 
 | ||||||
|  |     history.replaceState(null, null, url); | ||||||
|  |     updateExternalEditorsLinks(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -1034,7 +1094,7 @@ function onMapMoveEnd() { | ||||||
|         // Calculer la distance en km entre l'ancienne et la nouvelle position
 |         // Calculer la distance en km entre l'ancienne et la nouvelle position
 | ||||||
|         const distanceKm = map.distance(center, window.lastKnownPosition) / 1000; |         const distanceKm = map.distance(center, window.lastKnownPosition) / 1000; | ||||||
| 
 | 
 | ||||||
|         console.log('déplacement de ', distanceKm, 'km') |         // console.log('déplacement de ', distanceKm, 'km')
 | ||||||
|         // Ne mettre à jour que si on s'est déplacé de plus de 2km
 |         // Ne mettre à jour que si on s'est déplacé de plus de 2km
 | ||||||
|         if (distanceKm > 2) { |         if (distanceKm > 2) { | ||||||
|             window.lastKnownPosition = center; |             window.lastKnownPosition = center; | ||||||
|  | @ -1049,21 +1109,16 @@ function onMapMoveEnd() { | ||||||
|         // console.log("Recherche des issues Osmose...");
 |         // console.log("Recherche des issues Osmose...");
 | ||||||
|         searchOsmoseIssues(map); |         searchOsmoseIssues(map); | ||||||
|     } else { |     } else { | ||||||
|         console.log("Zoom trop faible pour les issues Osmose"); |         // console.log("Zoom trop faible pour les issues Osmose");
 | ||||||
|         osmose_markers.clearLayers(); |         osmose_markers.clearLayers(); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| setCoordinatesOfLeafletMapFromQueryParameters() |  | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| import { lcm_i18n } from './lcm_i18n.js'; |  | ||||||
| $(document).ready(function () { | $(document).ready(function () { | ||||||
|     // Charger le service de traduction
 |     // Charger le service de traduction
 | ||||||
| 
 | 
 | ||||||
|     // Détecter la langue du navigateur
 |  | ||||||
|     const currentLanguage = lcm_i18n.detectLanguage(); |  | ||||||
|     console.log('Langue détectée:', currentLanguage); |  | ||||||
|     init() |     init() | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
|  | @ -1080,7 +1135,7 @@ function updateExternalEditorsLinks() { | ||||||
|     const zoom = map.getZoom() |     const zoom = map.getZoom() | ||||||
| 
 | 
 | ||||||
|     mapCompleteLink(center.lat, center.lng, zoom) |     mapCompleteLink(center.lat, center.lng, zoom) | ||||||
|     idLink(center.lat, center.lng, zoom) |     idEditorLink(center.lat, center.lng, zoom) | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -1088,10 +1143,10 @@ function mapCompleteLink(lat, lon, zoom) { | ||||||
|     $("mapCompleteLink").attr('href', `https://mapcomplete.org/charging_stations?z=${zoom}&lat=${lat}&lon=${lon}`) |     $("mapCompleteLink").attr('href', `https://mapcomplete.org/charging_stations?z=${zoom}&lat=${lat}&lon=${lon}`) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function idLink(lat, lon, zoom) { | function idEditorLink(lat, lon, zoom) { | ||||||
|     let href = `https://www.openstreetmap.org/edit?editor=id#map=${zoom}/${lat}/${lon}` |     let href = `https://www.openstreetmap.org/edit?editor=id#map=${zoom}/${lat}/${lon}` | ||||||
|     console.log('idlink', href) |   | ||||||
|     $("idLink").attr('href', href) |     $("idEditorLink").attr('href', href) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -1211,7 +1266,7 @@ function searchOsmoseIssues(map) { | ||||||
|     // Modifier la vérification des stations existantes
 |     // Modifier la vérification des stations existantes
 | ||||||
|     const existingStations = []; |     const existingStations = []; | ||||||
|     if (lcm_config.hide_osmose_markers_if_close_to_existing_charging_stations && |     if (lcm_config.hide_osmose_markers_if_close_to_existing_charging_stations && | ||||||
|         isChargingStationLayerActive() && // Nouvelle condition
 |         isChargingStationLayerActive() && | ||||||
|         geojsondata && |         geojsondata && | ||||||
|         geojsondata.features) { |         geojsondata.features) { | ||||||
|         geojsondata.features.forEach(feature => { |         geojsondata.features.forEach(feature => { | ||||||
|  | @ -1243,13 +1298,21 @@ function searchOsmoseIssues(map) { | ||||||
|             osmoseIssuesCount = issuesList.length; |             osmoseIssuesCount = issuesList.length; | ||||||
| 
 | 
 | ||||||
|             issuesList.forEach(issueInfo => { |             issuesList.forEach(issueInfo => { | ||||||
|                 if (issueInfo.lat == null || issueInfo.lon == null || !issueInfo.id) { |                 // Vérifier que les coordonnées existent et sont valides
 | ||||||
|  |                 if (!issueInfo || issueInfo.lat == null || issueInfo.lon == null || !issueInfo.id) { | ||||||
|  |                     console.warn("Issue Osmose invalide:", issueInfo); | ||||||
|                     return; |                     return; | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|                 const lat = parseFloat(issueInfo.lat); |                 const lat = parseFloat(issueInfo.lat); | ||||||
|                 const lon = parseFloat(issueInfo.lon); |                 const lon = parseFloat(issueInfo.lon); | ||||||
| 
 | 
 | ||||||
|  |                 // Vérifier que les coordonnées sont des nombres valides
 | ||||||
|  |                 if (isNaN(lat) || isNaN(lon)) { | ||||||
|  |                     console.warn("Coordonnées invalides pour l'issue Osmose:", issueInfo); | ||||||
|  |                     return; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|                 // Vérifier la distance avec les stations existantes
 |                 // Vérifier la distance avec les stations existantes
 | ||||||
|                 if (lcm_config.hide_osmose_markers_if_close_to_existing_charging_stations) { |                 if (lcm_config.hide_osmose_markers_if_close_to_existing_charging_stations) { | ||||||
|                     const tooClose = existingStations.some(station => { |                     const tooClose = existingStations.some(station => { | ||||||
|  | @ -1258,14 +1321,13 @@ function searchOsmoseIssues(map) { | ||||||
|                     }); |                     }); | ||||||
| 
 | 
 | ||||||
|                     if (tooClose) { |                     if (tooClose) { | ||||||
|                         // console.log(`Marqueur Osmose ignoré car trop proche d'une station existante: ${lat},${lon}`);
 |  | ||||||
|                         return; |                         return; | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|                 lcm_config.osmoseIssuesList.push(issueInfo); |                 lcm_config.osmoseIssuesList.push(issueInfo); | ||||||
| 
 | 
 | ||||||
|                 // Créer le marqueur Osmose si il n'est pas trop proche d'une station existante
 |                 // Créer le marqueur Osmose
 | ||||||
|                 const osmoseMarker = L.circle([lat, lon], { |                 const osmoseMarker = L.circle([lat, lon], { | ||||||
|                     color: "purple", |                     color: "purple", | ||||||
|                     fillColor: "purple", |                     fillColor: "purple", | ||||||
|  | @ -1283,6 +1345,10 @@ function searchOsmoseIssues(map) { | ||||||
|                     const clickedMarker = e.target; |                     const clickedMarker = e.target; | ||||||
|                     const storedIssueId = clickedMarker.options.issueId; |                     const storedIssueId = clickedMarker.options.issueId; | ||||||
| 
 | 
 | ||||||
|  |                     if (!storedIssueId) { | ||||||
|  |                         console.warn("ID d'issue manquant pour le marqueur:", clickedMarker); | ||||||
|  |                         return; | ||||||
|  |                     } | ||||||
| 
 | 
 | ||||||
|                     const detailUrl = `https://osmose.openstreetmap.fr/api/0.3/issue/${storedIssueId}?langs=auto`; |                     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); |                     console.log("Récupération des détails pour l'issue:", detailUrl); | ||||||
|  | @ -1295,6 +1361,10 @@ function searchOsmoseIssues(map) { | ||||||
|                             return response.json(); |                             return response.json(); | ||||||
|                         }) |                         }) | ||||||
|                         .then(issueDetails => { |                         .then(issueDetails => { | ||||||
|  |                             if (!issueDetails) { | ||||||
|  |                                 throw new Error("Détails de l'issue non reçus"); | ||||||
|  |                             } | ||||||
|  | 
 | ||||||
|                             // Construire le contenu de la popup avec les détails
 |                             // Construire le contenu de la popup avec les détails
 | ||||||
|                             let popupContent = `<strong>${issueDetails.title?.auto || 'Titre non disponible'}</strong><br>`; |                             let popupContent = `<strong>${issueDetails.title?.auto || 'Titre non disponible'}</strong><br>`; | ||||||
|                             if (issueDetails.subtitle?.auto) { |                             if (issueDetails.subtitle?.auto) { | ||||||
|  | @ -1308,10 +1378,8 @@ function searchOsmoseIssues(map) { | ||||||
|                                     proposedTags += `<tr><td>${tag.k}</td><td>${tag.v}</td></tr>`; |                                     proposedTags += `<tr><td>${tag.k}</td><td>${tag.v}</td></tr>`; | ||||||
|                                 }); |                                 }); | ||||||
|                                 proposedTags += '</table>'; |                                 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`; |                             const josmFixUrl = `http://localhost:8111/import?url=https://osmose.openstreetmap.fr/api/0.3/issue/${storedIssueId}/fix/0`; | ||||||
| 
 | 
 | ||||||
|                             let josm_buttons = ` |                             let josm_buttons = ` | ||||||
|  | @ -1320,30 +1388,26 @@ function searchOsmoseIssues(map) { | ||||||
|                                     <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> |                                     <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>`; |                                 </div>`; | ||||||
| 
 | 
 | ||||||
|                             popupContent += josm_buttons |                             popupContent += josm_buttons; | ||||||
|                             // Mettre à jour le contenu de la popup et s'assurer qu'elle est ouverte
 |  | ||||||
|                             clickedMarker.setPopupContent(popupContent); |                             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} |                             $('#current_station_infos').html(`<div class='island osmose-details'><h2>Analyse Osmose</h2>${josm_buttons} | ||||||
|                                 ${proposedTags} |                                 ${proposedTags} | ||||||
|                                 </div>`); |                                 </div>`); | ||||||
| 
 | 
 | ||||||
|                             // Lier l'événement au bouton JOSM DANS la popup
 |  | ||||||
|                             bindEventsOnJosmRemote(clickedMarker.getPopup().getElement()); |                             bindEventsOnJosmRemote(clickedMarker.getPopup().getElement()); | ||||||
| 
 | 
 | ||||||
|                         }) |                         }) | ||||||
|                         .catch(error => { |                         .catch(error => { | ||||||
|                             console.error("Erreur lors de la récupération des détails de l'issue Osmose:", 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.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); |                 osmose_markers.addLayer(osmoseMarker); | ||||||
|             }); |             }); | ||||||
| 
 | 
 | ||||||
|             updateCounters(); // Mettre à jour l'affichage après le traitement
 |             updateCounters(); | ||||||
|         }) |         }) | ||||||
|         .catch(error => { |         .catch(error => { | ||||||
|             console.error('Erreur détaillée lors de la recherche de la liste des issues Osmose:', error); |             console.error('Erreur détaillée lors de la recherche de la liste des issues Osmose:', error); | ||||||
|  | @ -1355,11 +1419,19 @@ function searchOsmoseIssues(map) { | ||||||
| 
 | 
 | ||||||
| // Ajouter un écouteur d'événements pour le changement de visibilité des calques
 | // Ajouter un écouteur d'événements pour le changement de visibilité des calques
 | ||||||
| function init() { | function init() { | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|     bindEventsOnJosmRemote(); |     bindEventsOnJosmRemote(); | ||||||
|     onMapMoveEnd(); |     onMapMoveEnd(); | ||||||
|     map.on('moveend', onMapMoveEnd); |     map.on('moveend', onMapMoveEnd); | ||||||
|     $('#spinning_icon').hide(); |     $('#spinning_icon').hide(); | ||||||
| 
 | 
 | ||||||
|  |         // ... dans la gestion du clic sur un résultat Addok ...
 | ||||||
|  |     $('.route-to-place').on('click', function() { | ||||||
|  |         const place = $(this).data('place'); | ||||||
|  |         calculerEtAfficherItineraire(place, 'car'); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|     /** |     /** | ||||||
|      * boutons de changement de filtres et de rechargement des bornes à l'affichage |      * boutons de changement de filtres et de rechargement des bornes à l'affichage | ||||||
|      */ |      */ | ||||||
|  | @ -1548,3 +1620,309 @@ function updateFilteredStationsCount() { | ||||||
|         $('#filter_max_output_display').after(filterStats); |         $('#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(` | ||||||
|  |             <button onclick="window.setStartMarkerFromPopup([${e.latlng.lat},${e.latlng.lng}])">Choisir comme départ</button><br> | ||||||
|  |             <button onclick="window.setEndMarkerFromPopup([${e.latlng.lat},${e.latlng.lng}])">Choisir comme arrivée</button> | ||||||
|  |         `)
 | ||||||
|  |         .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; | ||||||
|  | 
 | ||||||
|  |     // 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 = `<div class="battery-info">
 | ||||||
|  |                         <h3>Niveau de batterie : ${level}%</h3>`; | ||||||
|  | 
 | ||||||
|  |                     if (level <= minBatteryLevel) { | ||||||
|  |                         popupContent += `<p class="warning">⚠️ Recharge recommandée !</p>`; | ||||||
|  |                     } | ||||||
|  | 
 | ||||||
|  |                     popupContent += `<p>Distance parcourue : ${(accumulatedDistance + segmentDistance * ratio).toFixed(1)} km</p>
 | ||||||
|  |                         <p>Consommation : ${((accumulatedDistance + segmentDistance * ratio) * consumptionPerKm / 1000).toFixed(1)} kWh</p> | ||||||
|  |                     </div>`; | ||||||
|  | 
 | ||||||
|  |                     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; | ||||||
|  | 
 | ||||||
|  |         // Ajouter les marqueurs de pause
 | ||||||
|  |         window.pauseMarkers = []; | ||||||
|  |         if (numberOfPauses > 0) { | ||||||
|  |             const totalDistance = route.distance; | ||||||
|  |             const pauseInterval = totalDistance / (numberOfPauses + 1); | ||||||
|  | 
 | ||||||
|  |             for (let i = 1; i <= numberOfPauses; i++) { | ||||||
|  |                 const pauseDistance = pauseInterval * i; | ||||||
|  |                 let accumulatedDistance = 0; | ||||||
|  |                 let pauseCoords; | ||||||
|  | 
 | ||||||
|  |                 // Trouver les coordonnées pour cette pause
 | ||||||
|  |                 for (let j = 0; j < coords.length - 1; j++) { | ||||||
|  |                     const segmentDistance = L.latLng(coords[j]).distanceTo(coords[j + 1]); | ||||||
|  |                     if (accumulatedDistance + segmentDistance >= pauseDistance) { | ||||||
|  |                         const ratio = (pauseDistance - accumulatedDistance) / segmentDistance; | ||||||
|  |                         pauseCoords = [ | ||||||
|  |                             coords[j][0] + (coords[j + 1][0] - coords[j][0]) * ratio, | ||||||
|  |                             coords[j][1] + (coords[j + 1][1] - coords[j][1]) * ratio | ||||||
|  |                         ]; | ||||||
|  |                         break; | ||||||
|  |                     } | ||||||
|  |                     accumulatedDistance += segmentDistance; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 if (pauseCoords) { | ||||||
|  |                     // Calculer la distance restante jusqu'à la prochaine pause ou la fin
 | ||||||
|  |                     const nextPauseDistance = (i < numberOfPauses) ? pauseInterval : (totalDistance - pauseDistance); | ||||||
|  |                     const consumptionToNextPause = (nextPauseDistance / 1000) * consumptionPerKm / 1000; // en kWh
 | ||||||
|  |                     const batteryLevelNeeded = (consumptionToNextPause / batteryCapacity) * 100 + minBatteryLevel; | ||||||
|  |                      | ||||||
|  |                     // Calculer le niveau de batterie actuel à ce point
 | ||||||
|  |                     const currentBatteryLevel = ((batteryStartKwh - (pauseDistance / 1000 * consumptionPerKm / 1000)) / batteryCapacity) * 100; | ||||||
|  |                      | ||||||
|  |                     // Rechercher les stations de recharge autour du point de pause
 | ||||||
|  |                     const chargingStations = await searchChargingStationsAroundPoint(pauseCoords[0], pauseCoords[1]); | ||||||
|  |                      | ||||||
|  |                     // Créer le contenu du popup avec les stations trouvées
 | ||||||
|  |                     let popupContent = `<div class="pause-info">
 | ||||||
|  |                         <h3>Pause recommandée n°${i}</h3> | ||||||
|  |                         <p>Durée : 10 minutes</p> | ||||||
|  |                         <p>Niveau de batterie actuel : ${currentBatteryLevel.toFixed(1)}%</p> | ||||||
|  |                         <p>Niveau de batterie nécessaire : ${batteryLevelNeeded.toFixed(1)}%</p> | ||||||
|  |                         <p>Distance jusqu'à la prochaine pause : ${(nextPauseDistance / 1000).toFixed(1)} km</p>`; | ||||||
|  | 
 | ||||||
|  |                     if (chargingStations.length > 0) { | ||||||
|  |                         popupContent += `<div class="charging-stations">
 | ||||||
|  |                             <h4>Stations de recharge à proximité (${chargingStations.length}) :</h4> | ||||||
|  |                             <ul>`;
 | ||||||
|  |                          | ||||||
|  |                         chargingStations.forEach(station => { | ||||||
|  |                             const name = station.tags?.name || 'Sans nom'; | ||||||
|  |                             const operator = station.tags?.operator || 'Opérateur inconnu'; | ||||||
|  |                             const distance = L.latLng(pauseCoords).distanceTo([station.lat, station.lon]).toFixed(0); | ||||||
|  |                              | ||||||
|  |                             popupContent += `<li>
 | ||||||
|  |                                 <strong>${name}</strong><br> | ||||||
|  |                                 Opérateur : ${operator}<br> | ||||||
|  |                                 Distance : ${distance} m | ||||||
|  |                             </li>`; | ||||||
|  |                         }); | ||||||
|  |                          | ||||||
|  |                         popupContent += `</ul></div>`; | ||||||
|  |                     } else { | ||||||
|  |                         popupContent += `<p>Aucune station de recharge trouvée dans un rayon de 1.5 km</p>`; | ||||||
|  |                     } | ||||||
|  |                      | ||||||
|  |                     popupContent += `</div>`; | ||||||
|  | 
 | ||||||
|  |                     const pauseMarker = L.marker(pauseCoords, { | ||||||
|  |                         icon: L.divIcon({ | ||||||
|  |                             className: 'pause-marker', | ||||||
|  |                             html: '⏸️', | ||||||
|  |                             iconSize: [30, 30] | ||||||
|  |                         }) | ||||||
|  |                     }).addTo(map); | ||||||
|  |                      | ||||||
|  |                     pauseMarker.bindPopup(popupContent); | ||||||
|  |                     window.pauseMarkers.push(pauseMarker); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // Afficher distance, durée, et boutons de mode de transport
 | ||||||
|  |         const distanceKm = (route.distance / 1000).toFixed(1); | ||||||
|  |         const durationMin = Math.round(route.duration / 60); | ||||||
|  |         const totalDurationMin = durationMin + totalPauseTimeMinutes; | ||||||
|  | 
 | ||||||
|  |         // Convertir la durée totale en heures et minutes si nécessaire
 | ||||||
|  |         let durationDisplay = ''; | ||||||
|  |         if (totalDurationMin >= 60) { | ||||||
|  |             const hours = Math.floor(totalDurationMin / 60); | ||||||
|  |             const minutes = totalDurationMin % 60; | ||||||
|  |             durationDisplay = `${hours}h${minutes > 0 ? ` ${minutes}min` : ''}`; | ||||||
|  |         } else { | ||||||
|  |             durationDisplay = `${totalDurationMin} min`; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         $('#current_station_infos').html(` | ||||||
|  |             <section class="routing"> | ||||||
|  |             <h2>Itinéraire</h2> | ||||||
|  |             <div>Distance : <strong>${distanceKm} km</strong></div> | ||||||
|  |             <div>Durée estimée : <strong>${durationDisplay}</strong> (dont ${totalPauseTimeMinutes} min de pause)</div> | ||||||
|  |             <div>Nombre de pauses recommandées : <strong>${numberOfPauses}</strong> (10 min toutes les 2h)</div> | ||||||
|  |             <div>Niveau de batterie final estimé : <strong>${batteryEndLevel.toFixed(1)}%</strong></div> | ||||||
|  |             <div>Consommation totale estimée : <strong>${totalConsumptionKwh.toFixed(1)} kWh</strong></div> | ||||||
|  |             <div> | ||||||
|  |                 <button onclick="calculerEtAfficherItineraire(window.lastRouteDestination, 'car')">🚗 Voiture</button> | ||||||
|  |                 <button onclick="calculerEtAfficherItineraire(window.lastRouteDestination, 'bike')">🚴 Vélo</button> | ||||||
|  |                 <button onclick="calculerEtAfficherItineraire(window.lastRouteDestination, 'foot')">🚶 Piéton</button> | ||||||
|  |             </div> | ||||||
|  |             </section> | ||||||
|  |         `);
 | ||||||
|  | 
 | ||||||
|  |         // 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: '🟢', 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: '🔴', 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(); | ||||||
|  |     }); | ||||||
|  | } | ||||||
							
								
								
									
										8
									
								
								js/lcm_routing.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								js/lcm_routing.js
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,8 @@ | ||||||
|  | /** | ||||||
|  |  * gestion des itinéraires pour libre charge map | ||||||
|  |  */ | ||||||
|  | class routing{ | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export default routing | ||||||
|  | @ -159,6 +159,7 @@ function compterFeaturesInOpenStreetMapFromFile() { | ||||||
|     const nombreFeaturesInOSM = data.elements.length; |     const nombreFeaturesInOSM = data.elements.length; | ||||||
| 
 | 
 | ||||||
|     let countPointsDeChargeOSM = 0; |     let countPointsDeChargeOSM = 0; | ||||||
|  |     let validationErrors = {} | ||||||
| 
 | 
 | ||||||
|     // Initialiser le compteur pour chaque tag
 |     // Initialiser le compteur pour chaque tag
 | ||||||
|     let tagsCountOSM = {}; |     let tagsCountOSM = {}; | ||||||
|  | @ -176,11 +177,15 @@ function compterFeaturesInOpenStreetMapFromFile() { | ||||||
|       } |       } | ||||||
|       // Compter les tags présents
 |       // Compter les tags présents
 | ||||||
|       if (element.tags) { |       if (element.tags) { | ||||||
|  | 
 | ||||||
|  |         let tags = Object.keys(element.tags) | ||||||
|  | 
 | ||||||
|         lcm_config.tags_to_display_in_popup.forEach(tag => { |         lcm_config.tags_to_display_in_popup.forEach(tag => { | ||||||
|           if (element.tags[tag] !== undefined && element.tags[tag] !== null && element.tags[tag] !== "") { |           if (element.tags[tag] !== undefined && element.tags[tag] !== null && element.tags[tag] !== "") { | ||||||
|             tagsCountOSM[tag]++; |             tagsCountOSM[tag]++; | ||||||
|           } |           } | ||||||
|         }); |         }); | ||||||
|  | 
 | ||||||
|       } |       } | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|  | @ -188,7 +193,8 @@ function compterFeaturesInOpenStreetMapFromFile() { | ||||||
|       ...stats, |       ...stats, | ||||||
|       countStationsOSM: nombreFeaturesInOSM, |       countStationsOSM: nombreFeaturesInOSM, | ||||||
|       countPointsDeChargeOSM: countPointsDeChargeOSM, |       countPointsDeChargeOSM: countPointsDeChargeOSM, | ||||||
|       tagsCountOSM |       tagsCountOSM, | ||||||
|  |       validationErrors | ||||||
|     }; |     }; | ||||||
|   } catch (erreur) { |   } catch (erreur) { | ||||||
|     console.error('Erreur lors du traitement du fichier OSM:', erreur.message); |     console.error('Erreur lors du traitement du fichier OSM:', erreur.message); | ||||||
|  |  | ||||||
|  | @ -3,9 +3,9 @@ overrides leaflet | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| .leaflet-interactive { | // .leaflet-interactive { | ||||||
|   border: solid 3px white; | //   border: solid 3px white; | ||||||
| } | // } | ||||||
| 
 | 
 | ||||||
| .leaflet-control-layers { | .leaflet-control-layers { | ||||||
|   &.leaflet-control-layers-toggle { |   &.leaflet-control-layers-toggle { | ||||||
|  |  | ||||||
|  | @ -700,6 +700,16 @@ header { | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | .routing{ | ||||||
|  | 
 | ||||||
|  |   .start-marker { font-size: 2em; } | ||||||
|  |   .end-marker { font-size: 2em; } | ||||||
|  |   .pause-marker { font-size: 2em; } | ||||||
|  |    | ||||||
|  |   .route-color{ | ||||||
|  |     fill: #0059ce; | ||||||
|  |   } | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| @include meta.load-css('_osmose.scss'); | @include meta.load-css('_osmose.scss'); | ||||||
| @include meta.load-css('_recherche.scss'); | @include meta.load-css('_recherche.scss'); | ||||||
|  |  | ||||||
|  | @ -624,6 +624,19 @@ header img { | ||||||
|   height: 1rem; |   height: 1rem; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | .routing .start-marker { | ||||||
|  |   font-size: 2em; | ||||||
|  | } | ||||||
|  | .routing .end-marker { | ||||||
|  |   font-size: 2em; | ||||||
|  | } | ||||||
|  | .routing .pause-marker { | ||||||
|  |   font-size: 2em; | ||||||
|  | } | ||||||
|  | .routing .route-color { | ||||||
|  |   fill: #0059ce; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| .osmose-marker-drop:hover .osmose-marker-inner { | .osmose-marker-drop:hover .osmose-marker-inner { | ||||||
|   animation: bounce 0.5s ease infinite; |   animation: bounce 0.5s ease infinite; | ||||||
|   background-color: #9F2BFF; |   background-color: #9F2BFF; | ||||||
|  | @ -997,10 +1010,6 @@ label:hover .island .checkbox-custom { | ||||||
| /** | /** | ||||||
| overrides leaflet | overrides leaflet | ||||||
|  */ |  */ | ||||||
| .leaflet-interactive { |  | ||||||
|   border: solid 3px white; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| .leaflet-control-layers.leaflet-control-layers-toggle { | .leaflet-control-layers.leaflet-control-layers-toggle { | ||||||
|   background: url("img/burger.png"); |   background: url("img/burger.png"); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1 +1 @@ | ||||||
| {"version":3,"sourceRoot":"","sources":["main.scss","_osmose.scss","_recherche.scss","_filters.scss","_mobile.scss","_overrides.scss","_animations.scss","_stats.scss"],"names":[],"mappings":";AAIA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;;;AAGF;EACE;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;EACA;EACA;;;AAKA;EACE;;AAGE;EACE;EACA;;AAKJ;EACE;;;AAMN;EACE;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;;AAIJ;EACE;;;AAGF;EACE;EACA;EACA;;;AAGF;AAAA;AAAA;AAAA;EAIE;EACA;EACA;EACA;EACA;;;AAKA;EACE;EACA;;;AAIJ;EACE;EACA;;;AAGF;EACE;EACA;;AAGA;EACE;EACA;;;AAIJ;EACE;;;AAGF;EACE;EACA;EACA;;;AAGF;EAOE;;AANA;EACE;EACA;EACA;;;AAMJ;EACE;;;AAGF;EACE;IACE;;EAGF;IACE;;;AAIJ;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAIJ;EACE;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;EACA;EACA;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAIF;EACE;EACA;EACA;;AAEA;EACE;;;AAIJ;AAAA;AAAA;AAIA;EACE;;;AAGF;EACE;EACA;EACA;EACA;EACA;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;AAAA;EAEE;;;AAGF;AAAA;EAEE;;;AAGF;AAAA;EAEE;;;AAGF;AAAA;EAEE;;;AAGF;AAAA;EAEE;;;AAGF;EACE;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;;AAEA;EACE;;;AAIJ;EACE;EACA;EACA;;;AAGF;EACE;;;AAGF;EACE;EACA;EACA;EACA;EACA;;;AAIF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;;;AAIA;EACE;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;;;AAIJ;EACE;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;;AAEA;EACE;EACA;EACA;;AAGF;EACE;EACA;;;AAIJ;EACE;EACA;;;AAGF;EACE;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;;AAIJ;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;;;AAOF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;;AAIA;EACE;EACA;;;AAKN;EACE;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAMA;EACE;EACA;EACA;EACA;EACA;EACA;;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;;;AAGF;EACE;;;AAIA;EACE;;;AAIJ;EACE;;AAUE;EACE;EACA;EACA;;AAcJ;EACE;;AAGF;EACE;;AAGF;EACE;EACA;;AAEA;EACE;EACA;EACA;;AAIJ;EACE;;AAGF;EACE;EACA;EACA;EACA;EACA;;AAIA;EACE;EACA;;;AC1rBN;EACE;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;;;AAEA;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;EACA;;;AAKJ;EACE;EACA;EACA;EACA;;;AAEA;EACE;EACA;EACA;EACA;;;AAEA;EACE;;;AAIJ;EACE;EACA;;;AC3DJ;EACE;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;;;AAEA;EACE;EACA;;;AAEA;EACE;;;AAKN;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;;;AAEA;EACE;EACA;EACA;;;AAGF;EACE;;;AASJ;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;;ACpEF;EACE;;;AAEA;EACE;EACA;EACA;;;AAGF;EACE;;;AAIJ;EACE;EACA;EACA;EACA;;;AAGF;EACE;EACA;;;AAcF;AAAA;EAEE;EACA;EACA;EACA;EACA;;;AAEA;AAAA;EACE;EACA,OApDS;EAqDT;;;AAGF;AAAA;EAvBA;EACA;EAwBE;EACA;EArBF;EAuBE;EACA;;;AAEA;AAAA;EACE,YA/DK;;;AAmET;AAAA;EACE;EACA;EACA;EACA;EACA;;;AAGF;AAAA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EA/CF;;;AAkDE;AAAA;EACE;;;AAIJ;AAAA;EACE,YA/FY;EAgGZ,cAhGY;EAiGZ;;;AAEA;AAAA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAIJ;AAAA;EACE;EACA;EACA;EACA;;;AAGF;AAAA;EACE;EACA,OAxHS;EAsCX;;;AAsFA;AAAA;EACE;;;AAIA;AAAA;EACE,YAhIK;EAiIL,cAhIW;EAiIX;;;AAGF;AAAA;EACE,OArIW;EAsIX;;;AAMN;EACE;IACE;;EAGF;IACE;;EAGF;IACE;;;AC9JJ;AACA;EACE;IACE;IACA;;EAKA;AAAA;IAEE;IACA;;EAIJ;IACE;;EAGF;IAEE;IACA;IACA;IACA;IACA;IACA;IACA;;EAGF;IACE;IACA;IACA;;EAGF;IACE;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;;EAGF;AACE;;EAGF;IACE;IACA;IACA;IACA;IACA;;EAGF;IACE;;EAGF;AAAA;IAEE;IACA;IACA;IACA;IACA;IACA;;EAIF;IACE;;EAGF;IACE;;EAGF;IACE;IACA;IACA;IACA;IAEA;IACA;IACA;IACA;IACA;;EAIF;IAEE;;;ACrGJ;AAAA;AAAA;AAKA;EACE;;;AAIA;EACE;;;AAGF;EACE;;;AAKF;EACE;;;AAEA;EACE;EACA;;;AAKN;EACE;EACA;;;AAGF;AAEE;EACE;EACA;EACA;EACA;EACA;;;AAEA;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;;;AAIJ;EACE;EACA;EACA;EACA;EACA;;;AAEA;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;;;AAMN;EAyBE;EACA;EACA;EACA;;;AA1BE;EACE;EACA;EACA;EACA;EACA;EACA;;;AAKF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;;ACnGN;EACE;IACE;;EAGF;IACE;;EAGF;IACE;;EAGF;IACE;;EAGF;IACE;;EAGF;IACE;;EAGF;IACE;;;AAIJ;EAEE;IAEE;;EAGF;IACE;;;ACtCJ;EACE;EACA;EACA;;;AAEA;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;;;AAGF;EACE;EACA;EACA;;;AAKA;EACE;;;AAIJ;EACE;;;AAGF;EACE;EACA;;;AAEA;EACE;EACA;EACA;;;AP6pBN;EACE;EACA","file":"style.css"} | {"version":3,"sourceRoot":"","sources":["main.scss","_osmose.scss","_recherche.scss","_filters.scss","_mobile.scss","_overrides.scss","_animations.scss","_stats.scss"],"names":[],"mappings":";AAIA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;;;AAGF;EACE;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;EACA;EACA;;;AAKA;EACE;;AAGE;EACE;EACA;;AAKJ;EACE;;;AAMN;EACE;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;;AAIJ;EACE;;;AAGF;EACE;EACA;EACA;;;AAGF;AAAA;AAAA;AAAA;EAIE;EACA;EACA;EACA;EACA;;;AAKA;EACE;EACA;;;AAIJ;EACE;EACA;;;AAGF;EACE;EACA;;AAGA;EACE;EACA;;;AAIJ;EACE;;;AAGF;EACE;EACA;EACA;;;AAGF;EAOE;;AANA;EACE;EACA;EACA;;;AAMJ;EACE;;;AAGF;EACE;IACE;;EAGF;IACE;;;AAIJ;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAIJ;EACE;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;EACA;EACA;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAIF;EACE;EACA;EACA;;AAEA;EACE;;;AAIJ;AAAA;AAAA;AAIA;EACE;;;AAGF;EACE;EACA;EACA;EACA;EACA;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;AAAA;EAEE;;;AAGF;AAAA;EAEE;;;AAGF;AAAA;EAEE;;;AAGF;AAAA;EAEE;;;AAGF;AAAA;EAEE;;;AAGF;EACE;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;;AAEA;EACE;;;AAIJ;EACE;EACA;EACA;;;AAGF;EACE;;;AAGF;EACE;EACA;EACA;EACA;EACA;;;AAIF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;;;AAIA;EACE;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;;;AAIJ;EACE;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;;AAEA;EACE;EACA;EACA;;AAGF;EACE;EACA;;;AAIJ;EACE;EACA;;;AAGF;EACE;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;;AAIJ;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;;;AAOF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;;AAIA;EACE;EACA;;;AAKN;EACE;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAMA;EACE;EACA;EACA;EACA;EACA;EACA;;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;;;AAGF;EACE;;;AAIA;EACE;;;AAIJ;EACE;;AAUE;EACE;EACA;EACA;;AAcJ;EACE;;AAGF;EACE;;AAGF;EACE;EACA;;AAEA;EACE;EACA;EACA;;AAIJ;EACE;;AAGF;EACE;EACA;EACA;EACA;EACA;;AAIA;EACE;EACA;;;AAMJ;EAAgB;;AAChB;EAAc;;AACd;EAAgB;;AAEhB;EACE;;;ACrsBJ;EACE;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;;;AAEA;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;EACA;;;AAKJ;EACE;EACA;EACA;EACA;;;AAEA;EACE;EACA;EACA;EACA;;;AAEA;EACE;;;AAIJ;EACE;EACA;;;AC3DJ;EACE;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;;;AAEA;EACE;EACA;;;AAEA;EACE;;;AAKN;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;;;AAEA;EACE;EACA;EACA;;;AAGF;EACE;;;AASJ;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;;ACpEF;EACE;;;AAEA;EACE;EACA;EACA;;;AAGF;EACE;;;AAIJ;EACE;EACA;EACA;EACA;;;AAGF;EACE;EACA;;;AAcF;AAAA;EAEE;EACA;EACA;EACA;EACA;;;AAEA;AAAA;EACE;EACA,OApDS;EAqDT;;;AAGF;AAAA;EAvBA;EACA;EAwBE;EACA;EArBF;EAuBE;EACA;;;AAEA;AAAA;EACE,YA/DK;;;AAmET;AAAA;EACE;EACA;EACA;EACA;EACA;;;AAGF;AAAA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EA/CF;;;AAkDE;AAAA;EACE;;;AAIJ;AAAA;EACE,YA/FY;EAgGZ,cAhGY;EAiGZ;;;AAEA;AAAA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAIJ;AAAA;EACE;EACA;EACA;EACA;;;AAGF;AAAA;EACE;EACA,OAxHS;EAsCX;;;AAsFA;AAAA;EACE;;;AAIA;AAAA;EACE,YAhIK;EAiIL,cAhIW;EAiIX;;;AAGF;AAAA;EACE,OArIW;EAsIX;;;AAMN;EACE;IACE;;EAGF;IACE;;EAGF;IACE;;;AC9JJ;AACA;EACE;IACE;IACA;;EAKA;AAAA;IAEE;IACA;;EAIJ;IACE;;EAGF;IAEE;IACA;IACA;IACA;IACA;IACA;IACA;;EAGF;IACE;IACA;IACA;;EAGF;IACE;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;;EAGF;AACE;;EAGF;IACE;IACA;IACA;IACA;IACA;;EAGF;IACE;;EAGF;AAAA;IAEE;IACA;IACA;IACA;IACA;IACA;;EAIF;IACE;;EAGF;IACE;;EAGF;IACE;IACA;IACA;IACA;IAEA;IACA;IACA;IACA;IACA;;EAIF;IAEE;;;ACrGJ;AAAA;AAAA;AAUE;EACE;;;AAGF;EACE;;;AAKF;EACE;;;AAEA;EACE;EACA;;;AAKN;EACE;EACA;;;AAGF;AAEE;EACE;EACA;EACA;EACA;EACA;;;AAEA;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;;;AAIJ;EACE;EACA;EACA;EACA;EACA;;;AAEA;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;;;AAMN;EAyBE;EACA;EACA;EACA;;;AA1BE;EACE;EACA;EACA;EACA;EACA;EACA;;;AAKF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;;ACnGN;EACE;IACE;;EAGF;IACE;;EAGF;IACE;;EAGF;IACE;;EAGF;IACE;;EAGF;IACE;;EAGF;IACE;;;AAIJ;EAEE;IAEE;;EAGF;IACE;;;ACtCJ;EACE;EACA;EACA;;;AAEA;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;;;AAGF;EACE;EACA;EACA;;;AAKA;EACE;;;AAIJ;EACE;;;AAGF;EACE;EACA;;;AAEA;EACE;EACA;EACA;;;APuqBN;EACE;EACA","file":"style.css"} | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Tykayn
						Tykayn