RELEASE V5.5.0 : Voici les modifications apportées :
-> Implémentation des afficheurs "Sécurité"; "Informations" et "Correspondances" pour les afficheurs AFL FONCTIONNALITÉS EN COURS DE DÉVELOPPEMENT : -> REFONTE DU DESIGN DES AFFICHEURS CLASSIQUES
This commit is contained in:
1 parent
1c83953552
commit
4f3a34b101
24 files changed
+3517
-412
No files matched your search
+106
-54
@@ -341,49 +341,65 @@ async function fetchGtfsStationTimetable(uic, mode = 'departs') {
|
||||
const currentDay = now.getDay(); // 0 = Dimanche, 1 = Lundi, ..., 6 = Samedi
|
||||
const currentYYYYMMDD = `${now.getFullYear()}${String(now.getMonth() + 1).padStart(2, '0')}${String(now.getDate()).padStart(2, '0')}`;
|
||||
|
||||
// 1. Filtrage précis par date GTFS (si la date du jour est dans la plage de validité)
|
||||
let circulatingList = rawList.filter(t => {
|
||||
// Si la date courante figure dans les dates explicites
|
||||
if (Array.isArray(t.dates) && t.dates.length > 0 && t.dates.includes(currentYYYYMMDD)) {
|
||||
return true;
|
||||
const tomorrow = new Date(now.getTime() + 24 * 60 * 60 * 1000);
|
||||
const tomorrowDay = tomorrow.getDay();
|
||||
const tomorrowYYYYMMDD = `${tomorrow.getFullYear()}${String(tomorrow.getMonth() + 1).padStart(2, '0')}${String(tomorrow.getDate()).padStart(2, '0')}`;
|
||||
|
||||
// Helper de vérification des règles de circulation calendaires
|
||||
const circulatesOnDate = (t, targetDay, targetYYYYMMDD, targetDate) => {
|
||||
const pad = (n) => String(n).padStart(2, '0');
|
||||
const targetDateHyphen = `${targetDate.getFullYear()}-${pad(targetDate.getMonth() + 1)}-${pad(targetDate.getDate())}`;
|
||||
|
||||
// 1. Base GTFS : si t.dates existe (tableau précis de dates YYYYMMDD), c'est la source de vérité absolue
|
||||
if (Array.isArray(t.dates) && t.dates.length > 0) {
|
||||
return t.dates.includes(targetYYYYMMDD) || t.dates.includes(targetDateHyphen);
|
||||
}
|
||||
// Si le jour de la semaine est actif dans la période de validité
|
||||
if (Array.isArray(t.days) && t.days.length > 0 && t.days.includes(currentDay)) {
|
||||
if ((!t.start || currentYYYYMMDD >= t.start) && (!t.end || currentYYYYMMDD <= t.end)) {
|
||||
return true;
|
||||
|
||||
// 2. Base Supabase : jours personnalisés
|
||||
const customDaysRaw = t.jours_personnalises;
|
||||
if (customDaysRaw) {
|
||||
let customDays = [];
|
||||
if (Array.isArray(customDaysRaw)) {
|
||||
customDays = customDaysRaw;
|
||||
} else if (typeof customDaysRaw === 'string') {
|
||||
try { customDays = JSON.parse(customDaysRaw); } catch (e) {}
|
||||
}
|
||||
if (Array.isArray(customDays) && customDays.length > 0) {
|
||||
const match = customDays.find(item => item.date === targetDateHyphen || item.date === targetYYYYMMDD);
|
||||
if (match) return Boolean(match.circule);
|
||||
}
|
||||
}
|
||||
// Train de nuit parti la veille et arrivant aujourd'hui (heures >= 24h)
|
||||
|
||||
// 3. Colonnes circule_lundi, circule_mardi...
|
||||
const dayNames = ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi', 'samedi'];
|
||||
const colName = `circule_${dayNames[targetDay]}`;
|
||||
if (typeof t[colName] !== 'undefined' && t[colName] !== null) {
|
||||
const db = t.date_debut ? String(t.date_debut).slice(0, 10) : null;
|
||||
const df = t.date_fin ? String(t.date_fin).slice(0, 10) : null;
|
||||
if (db && targetDateHyphen < db) return false;
|
||||
if (df && targetDateHyphen > df) return false;
|
||||
return Boolean(t[colName]);
|
||||
}
|
||||
|
||||
// 4. Jours GTFS (fallback si t.dates n'était pas fourni)
|
||||
if (Array.isArray(t.days) && t.days.length > 0) {
|
||||
const prevDay = (currentDay + 6) % 7;
|
||||
const rawTime = mode === 'arrivees' ? (t.arr || t.dep) : (t.dep || t.arr);
|
||||
const m = String(rawTime).match(/^(\d{1,2})/);
|
||||
const rawHour = m ? parseInt(m[1], 10) : 0;
|
||||
if (rawHour >= 24 && t.days.includes(prevDay)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// Si aucune règle n'est renseignée
|
||||
if (!t.dates?.length && !t.days?.length) return true;
|
||||
return false;
|
||||
});
|
||||
|
||||
// 2. Fallback robuste : si la date actuelle est hors calendrier GTFS, filtrer par jour de la semaine
|
||||
if (circulatingList.length === 0) {
|
||||
circulatingList = rawList.filter(t => {
|
||||
if (Array.isArray(t.days) && t.days.length > 0) {
|
||||
return t.days.includes(currentDay);
|
||||
}
|
||||
if (!t.days.includes(targetDay)) return false;
|
||||
if (t.start && targetYYYYMMDD < String(t.start).replace(/-/g, '')) return false;
|
||||
if (t.end && targetYYYYMMDD > String(t.end).replace(/-/g, '')) return false;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Fallback complet : si toujours vide, renvoyer la grille quotidienne complète
|
||||
if (circulatingList.length === 0) {
|
||||
circulatingList = rawList;
|
||||
}
|
||||
// 5. Date unique fixée (date_depart, date, etc.)
|
||||
const singleDate = t.date_depart || t.date || t.service_date;
|
||||
if (singleDate) {
|
||||
const sStr = String(singleDate).slice(0, 10);
|
||||
return sStr === targetDateHyphen || sStr.replace(/-/g, '') === targetYYYYMMDD;
|
||||
}
|
||||
|
||||
// Helper de résolution ultra-robuste de quai par couple (train, gare)
|
||||
return true;
|
||||
};
|
||||
|
||||
// Helper de résolution de quai
|
||||
const lookupPlatform = (trainNum, tripId, stationUic, stationName) => {
|
||||
const cNum = cleanNum(trainNum);
|
||||
const cName = stationName ? formatStationName(stationName).toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g, '').replace(/[-_/.,()'&]/g, ' ').replace(/\s+/g, ' ').trim() : '';
|
||||
@@ -402,19 +418,18 @@ async function fetchGtfsStationTimetable(uic, mode = 'departs') {
|
||||
);
|
||||
};
|
||||
|
||||
// Enrichir chaque train circulant avec le GTFS-RT et le SIRI Lite
|
||||
const enrichedTrains = circulatingList.map((t, idx) => {
|
||||
// Helper d'enrichissement d'un train
|
||||
const mapTrain = (t, idx, isTomorrow = false) => {
|
||||
const rawTime = mode === 'arrivees' ? (t.arr || t.dep) : (t.dep || t.arr);
|
||||
const timeHM = parseTimeHM(rawTime) || 0;
|
||||
|
||||
// Recherche de mise à jour GTFS-RT ou SIRI-Lite (par trip_id ou numéro de train)
|
||||
const rt = rtData.updates.get(t.trip_id) || (t.num ? rtData.updates.get(t.num) : null) || (cleanNum(t.num) ? rtData.updates.get(cleanNum(t.num)) : null);
|
||||
const siri = siriData.updates.get(t.num) || (cleanNum(t.num) ? siriData.updates.get(cleanNum(t.num)) : null) || (t.trip_id ? siriData.updates.get(t.trip_id) : null);
|
||||
// Recherche de mise à jour GTFS-RT ou SIRI-Lite (valide pour aujourd'hui)
|
||||
const rt = !isTomorrow ? (rtData.updates.get(t.trip_id) || (t.num ? rtData.updates.get(t.num) : null) || (cleanNum(t.num) ? rtData.updates.get(cleanNum(t.num)) : null)) : null;
|
||||
const siri = !isTomorrow ? (siriData.updates.get(t.num) || (cleanNum(t.num) ? siriData.updates.get(cleanNum(t.num)) : null) || (t.trip_id ? siriData.updates.get(t.trip_id) : null)) : null;
|
||||
|
||||
const isCanceled = (rt && rt.isCanceled) || (siri && siri.isCanceled) || false;
|
||||
const delayMinutes = (rt && rt.delayMinutes > 0) ? rt.delayMinutes : (siri && siri.delayMinutes > 0 ? siri.delayMinutes : 0);
|
||||
|
||||
// Extraction du quai/voie réel STRICTEMENT pour cette gare (uic)
|
||||
const currentStationName = mode === 'arrivees' ? t.dest : t.orig;
|
||||
let realVoie = lookupPlatform(t.num, t.trip_id, uic, currentStationName);
|
||||
if (!realVoie && t.quai && typeof t.quai === 'string') {
|
||||
@@ -431,13 +446,11 @@ async function fetchGtfsStationTimetable(uic, mode = 'departs') {
|
||||
etat = 'retarde';
|
||||
}
|
||||
|
||||
// Causes de retard issues des alertes temps réel
|
||||
let cause = '';
|
||||
if (delayMinutes > 0 && rtData.alerts.length > 0) {
|
||||
if (!isTomorrow && delayMinutes > 0 && rtData.alerts.length > 0) {
|
||||
cause = rtData.alerts[idx % rtData.alerts.length].text;
|
||||
}
|
||||
|
||||
// Formatage des arrêts intermédiaires avec leurs voies respectives (propres à chaque gare)
|
||||
const formattedStops = (t.stops || []).map((st) => {
|
||||
const stopUic = st.uic || '';
|
||||
const stopVoie = lookupPlatform(t.num, t.trip_id, stopUic, st.nom);
|
||||
@@ -454,7 +467,7 @@ async function fetchGtfsStationTimetable(uic, mode = 'departs') {
|
||||
});
|
||||
|
||||
return {
|
||||
id: `gtfs-${t.num || idx}-${rawTime}-${idx}`,
|
||||
id: `gtfs-${isTomorrow ? 'demain-' : ''}${t.num || idx}-${rawTime}-${idx}`,
|
||||
trip_id: t.trip_id,
|
||||
numero_train: t.num || String(1000 + idx),
|
||||
type_train: t.type || 'TER',
|
||||
@@ -477,6 +490,7 @@ async function fetchGtfsStationTimetable(uic, mode = 'departs') {
|
||||
stops: formattedStops,
|
||||
composition: (t.type || '').includes('TGV') ? 'Rame 1 (Voitures 1 à 8) + Rame 2 (Voitures 11 à 18)' : 'Train standard',
|
||||
is_substitution: (t.type || '').includes('AUTOCAR'),
|
||||
_isTomorrow: isTomorrow,
|
||||
_displayTime: formatTimeHM(rawTime),
|
||||
_displayPlatform: realVoie,
|
||||
_depHM: timeHM,
|
||||
@@ -489,16 +503,54 @@ async function fetchGtfsStationTimetable(uic, mode = 'departs') {
|
||||
causeDetails: cause || null
|
||||
}
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
// Tri chronologique : afficher les trains circulant à partir de l'heure courante
|
||||
enrichedTrains.sort((a, b) => {
|
||||
const aDiff = ((a._depHM || 0) - currentHM + 1440) % 1440;
|
||||
const bDiff = ((b._depHM || 0) - currentHM + 1440) % 1440;
|
||||
return aDiff - bDiff;
|
||||
});
|
||||
// 1. Filtrer les trains circulant aujourd'hui
|
||||
let rawToday = rawList.filter(t => circulatesOnDate(t, currentDay, currentYYYYMMDD, now));
|
||||
if (rawToday.length === 0) {
|
||||
// Fallback si pas de calendrier GTFS explicite
|
||||
rawToday = rawList.filter(t => Array.isArray(t.days) && t.days.includes(currentDay));
|
||||
if (rawToday.length === 0) rawToday = rawList;
|
||||
}
|
||||
|
||||
return enrichedTrains;
|
||||
const enrichedToday = rawToday.map((t, idx) => mapTrain(t, idx, false));
|
||||
|
||||
// Filtrer les trains passés : "Une fois que l'heure de départ / Arrivée est dépassée, alors ne plus l'afficher"
|
||||
const remainingToday = enrichedToday
|
||||
.filter(t => {
|
||||
const trainHM = (mode === 'arrivees' ? t._arrHM : t._depHM) || 0;
|
||||
const delay = t.retard_minutes || 0;
|
||||
return (trainHM + delay) >= currentHM;
|
||||
})
|
||||
.sort((a, b) => {
|
||||
const aHM = (mode === 'arrivees' ? a._arrHM : a._depHM) || 0;
|
||||
const bHM = (mode === 'arrivees' ? b._arrHM : b._depHM) || 0;
|
||||
return aHM - bHM;
|
||||
});
|
||||
|
||||
// 2. Filtrer les trains circulant demain
|
||||
let rawTomorrow = rawList.filter(t => circulatesOnDate(t, tomorrowDay, tomorrowYYYYMMDD, tomorrow));
|
||||
if (rawTomorrow.length === 0) {
|
||||
rawTomorrow = rawList.filter(t => Array.isArray(t.days) && t.days.includes(tomorrowDay));
|
||||
}
|
||||
|
||||
const enrichedTomorrow = rawTomorrow
|
||||
.map((t, idx) => mapTrain(t, idx, true))
|
||||
.sort((a, b) => {
|
||||
const aHM = (mode === 'arrivees' ? a._arrHM : a._depHM) || 0;
|
||||
const bHM = (mode === 'arrivees' ? b._arrHM : b._depHM) || 0;
|
||||
return aHM - bHM;
|
||||
});
|
||||
|
||||
// 3. Logique : s'il reste <= 5 horaires aujourd'hui, afficher ceux d'aujourd'hui + ceux du lendemain
|
||||
let resultList = [];
|
||||
if (remainingToday.length <= 5) {
|
||||
resultList = [...remainingToday, ...enrichedTomorrow];
|
||||
} else {
|
||||
resultList = remainingToday;
|
||||
}
|
||||
|
||||
return resultList;
|
||||
} catch (err) {
|
||||
console.warn('[sncfApi] Erreur de chargement GTFS station:', err);
|
||||
return null;
|
||||
|
||||
Reference in new issue
Block a user