Skip to content

Commit 51fc39a

Browse files
committed
feat: enhance stop monitoring with cancellation status and improved schedule display
1 parent ec318a0 commit 51fc39a

2 files changed

Lines changed: 135 additions & 39 deletions

File tree

src/pages/index.astro

Lines changed: 52 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,17 @@ import Layout from '../layouts/Layout.astro';
4141
// Arrêts des lignes - format GeoJSON
4242
const ARRETS_URL = 'https://data.iledefrance-mobilites.fr/api/explore/v2.1/catalog/datasets/arrets-lignes/exports/geojson?where=mode != \'Bus\'';
4343

44+
// Configuration
45+
const REFRESH_INTERVAL_MS = 10000; // Intervalle d'actualisation en millisecondes (10 secondes)
46+
const MIN_ZOOM_FOR_STOPS = 15; // Zoom minimum pour afficher les arrêts (15 = très zoomé)
47+
4448
let map;
4549
let allLines = {}; // Stocker toutes les lignes par route_id
4650
let categories = {}; // Catégories de transport
4751
let lineImages = {}; // Cache des images de lignes
4852
let allStops = {}; // Stocker tous les arrêts par ligne
49-
const MIN_ZOOM_FOR_STOPS = 15; // Zoom minimum pour afficher les arrêts (très zoomé)
53+
let currentInfoWindow = null; // InfoWindow actuellement ouverte
54+
let currentRefreshInterval = null; // Intervalle d'actualisation actuel
5055

5156
async function initMap() {
5257
// Coordonnées de Paris
@@ -477,6 +482,17 @@ import Layout from '../layouts/Layout.astro';
477482

478483
// Info-bulle au clic
479484
marker.addListener('click', async () => {
485+
// Fermer l'infoWindow précédente si elle existe
486+
if (currentInfoWindow) {
487+
currentInfoWindow.close();
488+
}
489+
490+
// Arrêter l'actualisation précédente si elle existe
491+
if (currentRefreshInterval) {
492+
clearInterval(currentRefreshInterval);
493+
currentRefreshInterval = null;
494+
}
495+
480496
const stopIds = properties.stop_ids || [properties.stop_id];
481497
const hasMultipleDirections = stopIds.length > 1;
482498

@@ -493,6 +509,9 @@ import Layout from '../layouts/Layout.astro';
493509
`,
494510
position
495511
});
512+
513+
// Stocker l'infoWindow actuelle
514+
currentInfoWindow = infoWindow;
496515
infoWindow.open(map);
497516

498517
// Fonction pour charger les horaires de toutes les directions
@@ -509,8 +528,12 @@ import Layout from '../layouts/Layout.astro';
509528
// Fusionner tous les horaires
510529
const allSchedules = allSchedulesResults.flat();
511530

512-
// Trier par ordre chronologique (plus proche en premier)
513-
allSchedules.sort((a, b) => new Date(a.time) - new Date(b.time));
531+
// Trier par ordre chronologique (utiliser expectedTime si disponible, sinon aimedTime)
532+
allSchedules.sort((a, b) => {
533+
const timeA = new Date(a.expectedTime || a.aimedTime);
534+
const timeB = new Date(b.expectedTime || b.aimedTime);
535+
return timeA - timeB;
536+
});
514537

515538
const schedulesHTML = window.generateSchedulesHTML(allSchedules, line.routeType);
516539
const container = document.getElementById('schedules-content');
@@ -530,12 +553,16 @@ import Layout from '../layouts/Layout.astro';
530553
// Charger les horaires immédiatement
531554
loadSchedules();
532555

533-
// Actualiser toutes les 10 secondes
534-
const refreshInterval = setInterval(loadSchedules, 10000);
556+
// Actualiser selon l'intervalle configuré
557+
currentRefreshInterval = setInterval(loadSchedules, REFRESH_INTERVAL_MS);
535558

536559
// Arrêter l'actualisation quand l'info-bulle est fermée
537560
google.maps.event.addListener(infoWindow, 'closeclick', () => {
538-
clearInterval(refreshInterval);
561+
if (currentRefreshInterval) {
562+
clearInterval(currentRefreshInterval);
563+
currentRefreshInterval = null;
564+
}
565+
currentInfoWindow = null;
539566
});
540567
});
541568

@@ -583,6 +610,17 @@ import Layout from '../layouts/Layout.astro';
583610

584611
// Ajouter info-bulle au clic
585612
polyline.addListener('click', (event) => {
613+
// Fermer l'infoWindow précédente si elle existe
614+
if (currentInfoWindow) {
615+
currentInfoWindow.close();
616+
}
617+
618+
// Arrêter l'actualisation précédente si elle existe
619+
if (currentRefreshInterval) {
620+
clearInterval(currentRefreshInterval);
621+
currentRefreshInterval = null;
622+
}
623+
586624
const routeType = getRouteTypeName(properties.route_type);
587625
const infoWindow = new google.maps.InfoWindow({
588626
content: `
@@ -595,7 +633,15 @@ import Layout from '../layouts/Layout.astro';
595633
`,
596634
position: event.latLng
597635
});
636+
637+
// Stocker l'infoWindow actuelle
638+
currentInfoWindow = infoWindow;
598639
infoWindow.open(map);
640+
641+
// Nettoyer la référence quand fermée manuellement
642+
google.maps.event.addListener(infoWindow, 'closeclick', () => {
643+
currentInfoWindow = null;
644+
});
599645
});
600646

601647
return polyline;

src/scripts/stopMonitoring.js

Lines changed: 83 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@
55
const API_KEY = 'SvPHVJ5fPXkfJPKsu6958pwLCh5Oidhq';
66
const API_URL = 'https://prim.iledefrance-mobilites.fr/marketplace/stop-monitoring';
77

8+
// Configuration de l'affichage des horaires
9+
const MAX_SCHEDULES_METRO_TRAM = 5; // Nombre d'horaires affichés pour Métro/Tram
10+
const MAX_SCHEDULES_RAIL = 10; // Nombre d'horaires affichés pour RER/TER/Transilien
11+
812
/**
913
* Extrait l'ID numérique d'un stop_id complexe
1014
* Exemples:
@@ -153,23 +157,34 @@ function parseSchedulesData(data) {
153157
const destinationName = journey.DestinationName?.[0]?.value ||
154158
journey.DestinationName?.value ||
155159
'Destination inconnue';
156-
const departureTime = journey.MonitoredCall?.ExpectedDepartureTime ||
157-
journey.MonitoredCall?.AimedDepartureTime;
160+
const aimedTime = journey.MonitoredCall?.AimedDepartureTime;
161+
const expectedTime = journey.MonitoredCall?.ExpectedDepartureTime;
158162
const platform = journey.MonitoredCall?.DeparturePlatformName?.value ||
159163
journey.MonitoredCall?.ArrivalPlatformName?.value ||
160164
'-';
161165

162-
if (departureTime) {
166+
// Vérifier si le train est annulé
167+
const isCancelled = journey.MonitoredCall?.DepartureStatus === 'cancelled' ||
168+
journey.MonitoredCall?.VehicleAtStop === false &&
169+
journey.MonitoredCall?.ExpectedDepartureTime === undefined;
170+
171+
if (aimedTime || expectedTime) {
163172
schedules.push({
164173
destination: destinationName,
165-
time: departureTime,
166-
platform: platform
174+
aimedTime: aimedTime,
175+
expectedTime: expectedTime,
176+
platform: platform,
177+
isCancelled: isCancelled
167178
});
168179
}
169180
});
170181

171-
// Trier par heure
172-
schedules.sort((a, b) => new Date(a.time) - new Date(b.time));
182+
// Trier par heure (utiliser expectedTime si disponible, sinon aimedTime)
183+
schedules.sort((a, b) => {
184+
const timeA = new Date(a.expectedTime || a.aimedTime);
185+
const timeB = new Date(b.expectedTime || b.aimedTime);
186+
return timeA - timeB;
187+
});
173188

174189
console.log(`Parsed ${schedules.length} schedules`);
175190

@@ -191,49 +206,84 @@ export function generateSchedulesHTML(schedules, routeType) {
191206
// Déterminer si on affiche la colonne voie (uniquement pour RER, TER, Transilien)
192207
const showPlatform = routeType === 'RER' || routeType === 'TER' || routeType === 'Transilien';
193208

194-
// Construire le tableau HTML
209+
// Déterminer le nombre d'horaires à afficher selon le type de transport
210+
const maxSchedules = showPlatform ? MAX_SCHEDULES_RAIL : MAX_SCHEDULES_METRO_TRAM;
211+
212+
// Construire le tableau HTML avec scroll si nécessaire pour RER/TER/Transilien
213+
const tableStyle = showPlatform ? 'max-height: 300px; overflow-y: auto;' : '';
214+
195215
let html = `
196216
<div style="font-size: 11px;">
197217
<h5 style="margin: 0 0 6px 0; font-size: 12px; font-weight: 600;">Prochains passages</h5>
198-
<table style="width: 100%; border-collapse: collapse; font-size: 11px;">
199-
<thead>
200-
<tr style="border-bottom: 1px solid #ddd;">
201-
<th style="text-align: left; padding: 4px; font-weight: 600;">Heure</th>
202-
<th style="text-align: left; padding: 4px; font-weight: 600;">Direction</th>
203-
${showPlatform ? '<th style="text-align: center; padding: 4px; font-weight: 600;">Voie</th>' : ''}
204-
</tr>
205-
</thead>
206-
<tbody>
218+
<div style="${tableStyle}">
219+
<table style="width: 100%; border-collapse: collapse; font-size: 11px;">
220+
<thead>
221+
<tr style="border-bottom: 1px solid #ddd;">
222+
<th style="text-align: left; padding: 4px; font-weight: 600;">Heure</th>
223+
<th style="text-align: left; padding: 4px; font-weight: 600;">Direction</th>
224+
${showPlatform ? '<th style="text-align: center; padding: 4px; font-weight: 600;">Voie</th>' : ''}
225+
</tr>
226+
</thead>
227+
<tbody>
207228
`;
208229

209-
// Limiter à 5 horaires maximum
210-
schedules.slice(0, 5).forEach(schedule => {
211-
const time = new Date(schedule.time);
230+
// Limiter selon le type de transport
231+
schedules.slice(0, maxSchedules).forEach(schedule => {
212232
const now = new Date();
213-
const diffMinutes = Math.round((time - now) / 60000);
233+
const aimedTime = schedule.aimedTime ? new Date(schedule.aimedTime) : null;
234+
const expectedTime = schedule.expectedTime ? new Date(schedule.expectedTime) : null;
235+
const isCancelled = schedule.isCancelled || false;
236+
237+
// Utiliser expectedTime si disponible, sinon aimedTime
238+
const displayTime = expectedTime || aimedTime;
239+
const diffMinutes = Math.round((displayTime - now) / 60000);
214240

215-
// Afficher "X min" si moins de 60 minutes, sinon l'heure
241+
// Style pour les trains annulés (barré en rouge)
242+
const cancelledStyle = isCancelled ? 'text-decoration: line-through; color: #dc2626;' : '';
243+
244+
// Pour RER, TER et Transilien : toujours afficher l'heure originale
245+
// Pour Métro et Tram : afficher "X min" basé sur le temps réel (avec retard)
216246
let timeStr;
217-
if (diffMinutes < 0) {
218-
timeStr = time.toLocaleTimeString('fr-FR', { hour: '2-digit', minute: '2-digit' });
219-
} else if (diffMinutes < 60) {
220-
timeStr = `${diffMinutes} min`;
247+
if (routeType === 'RER' || routeType === 'TER' || routeType === 'Transilien') {
248+
// Toujours afficher l'heure originale
249+
timeStr = aimedTime.toLocaleTimeString('fr-FR', { hour: '2-digit', minute: '2-digit' });
221250
} else {
222-
timeStr = time.toLocaleTimeString('fr-FR', { hour: '2-digit', minute: '2-digit' });
251+
// Pour métro/tram : afficher "X min" basé sur displayTime (temps réel avec retard)
252+
if (diffMinutes < 0) {
253+
timeStr = displayTime.toLocaleTimeString('fr-FR', { hour: '2-digit', minute: '2-digit' });
254+
} else if (diffMinutes < 60) {
255+
timeStr = `${diffMinutes} min`;
256+
} else {
257+
timeStr = displayTime.toLocaleTimeString('fr-FR', { hour: '2-digit', minute: '2-digit' });
258+
}
259+
}
260+
261+
// Calculer le retard si expectedTime existe et est différent
262+
// Pour RER/TER/Transilien : afficher l'heure de retard en orange
263+
// Pour Métro/Tram : ne pas afficher (déjà inclus dans "X min")
264+
let delayHTML = '';
265+
if ((routeType === 'RER' || routeType === 'TER' || routeType === 'Transilien') &&
266+
expectedTime && aimedTime && expectedTime > aimedTime) {
267+
const delayMinutes = Math.round((expectedTime - aimedTime) / 60000);
268+
if (delayMinutes > 0) {
269+
const expectedTimeStr = expectedTime.toLocaleTimeString('fr-FR', { hour: '2-digit', minute: '2-digit' });
270+
delayHTML = ` <span style="color: #ff8800; font-weight: 600;">${expectedTimeStr}</span>`;
271+
}
223272
}
224273

225274
html += `
226-
<tr style="border-bottom: 1px solid #eee;">
227-
<td style="padding: 4px; white-space: nowrap;">${timeStr}</td>
228-
<td style="padding: 4px;">${schedule.destination}</td>
229-
${showPlatform ? `<td style="padding: 4px; text-align: center;">${schedule.platform}</td>` : ''}
275+
<tr style="border-bottom: 1px solid #eee; ${cancelledStyle}">
276+
<td style="padding: 4px; white-space: nowrap; ${cancelledStyle}">${timeStr}${delayHTML}</td>
277+
<td style="padding: 4px; ${cancelledStyle}">${schedule.destination}</td>
278+
${showPlatform ? `<td style="padding: 4px; text-align: center; ${cancelledStyle}">${schedule.platform}</td>` : ''}
230279
</tr>
231280
`;
232281
});
233282

234283
html += `
235-
</tbody>
236-
</table>
284+
</tbody>
285+
</table>
286+
</div>
237287
</div>
238288
`;
239289

0 commit comments

Comments
 (0)