Oui, la base de données est maintenant capable de gérer les correspondances entre gares !
Le script createGTFSDatabase.js crée :
direct_connections : Connexions au sein d’un même trajettransfer_opportunities : Toutes les correspondances possibles entre trajetsdirect_connectionsToutes les connexions possibles au sein d’un même trajet.
-- Exemple: Paris → Lyon (sans changer de train)
SELECT * FROM direct_connections
WHERE from_stop_name LIKE '%Paris%'
AND to_stop_name LIKE '%Lyon%'
LIMIT 5;
transfer_opportunities ⭐ NOUVEAUToutes les correspondances possibles dans chaque gare.
Filtre automatique :
-- Exemple: Toutes les correspondances à Lyon Part-Dieu
SELECT * FROM transfer_opportunities
WHERE transfer_stop_name LIKE '%Lyon Part%'
LIMIT 10;
gtfsDatabaseService.tsLe service de base inclut une méthode simple pour trouver des correspondances :
import { gtfsDb } from './src/services/gtfsDatabaseService';
// Recherche avec correspondances (méthode basique)
const journeys = await gtfsDb.findJourney(
'StopID:...', // Départ
'StopID:...', // Arrivée
'08:00:00', // Heure de départ
new Date(), // Date
2 // Max 2 correspondances
);
// journeys = [
// [connection1, connection2], // Trajet avec 1 correspondance
// [connection3], // Trajet direct
// ]
gtfsDatabaseServiceEnhanced.ts ⭐ RECOMMANDÉLe service amélioré utilise des requêtes SQL optimisées pour des performances maximales :
import { gtfsDbEnhanced } from './src/services/gtfsDatabaseServiceEnhanced';
await gtfsDbEnhanced.initialize();
// Recherche COMPLÈTE avec toutes les options
const journeys = await gtfsDbEnhanced.findAllJourneys(
'StopID:DUA8711300:LOC', // Paris Gare de Lyon
'StopID:DUA8727100:LOC', // Lyon Part Dieu
'08:00:00',
2 // Max 2 correspondances
);
// Résultat trié par durée totale
journeys.forEach(journey => {
console.log(`Durée: ${journey.totalDuration} min`);
console.log(`Correspondances: ${journey.legs.length - 1}`);
if (journey.transferStation) {
console.log(`Via: ${journey.transferStation}`);
console.log(`Temps d'attente: ${journey.transferTime} min`);
}
journey.legs.forEach((leg, i) => {
console.log(` ${i+1}. ${leg.from_stop_name} → ${leg.to_stop_name}`);
console.log(` ${leg.departure_time} → ${leg.arrival_time}`);
console.log(` ${leg.route_short_name}`);
});
});
SELECT
leg1.from_stop_name as depart,
leg1.departure_time,
leg1.to_stop_name as correspondance,
leg1.arrival_time as arrivee_correspondance,
leg2.departure_time as depart_correspondance,
leg2.to_stop_name as arrivee,
leg2.arrival_time,
-- Temps de correspondance en minutes
(CAST(substr(leg2.departure_time, 1, 2) AS INTEGER) * 60 +
CAST(substr(leg2.departure_time, 4, 2) AS INTEGER)) -
(CAST(substr(leg1.arrival_time, 1, 2) AS INTEGER) * 60 +
CAST(substr(leg1.arrival_time, 4, 2) AS INTEGER)) as temps_correspondance_min,
-- Durée totale
(CAST(substr(leg2.arrival_time, 1, 2) AS INTEGER) * 60 +
CAST(substr(leg2.arrival_time, 4, 2) AS INTEGER)) -
(CAST(substr(leg1.departure_time, 1, 2) AS INTEGER) * 60 +
CAST(substr(leg1.departure_time, 4, 2) AS INTEGER)) as duree_totale_min
FROM direct_connections leg1
JOIN direct_connections leg2
ON leg1.to_stop_id = leg2.from_stop_id
AND leg1.trip_id != leg2.trip_id
AND leg2.departure_time > leg1.arrival_time
WHERE leg1.from_stop_name LIKE '%Paris%'
AND leg2.to_stop_name LIKE '%Marseille%'
AND leg1.departure_time >= '08:00:00'
AND temps_correspondance_min >= 5
AND temps_correspondance_min <= 60
ORDER BY duree_totale_min
LIMIT 10;
SELECT
transfer_stop_name,
COUNT(DISTINCT departure_trip_id) as nb_trains_au_depart,
AVG(transfer_time_minutes) as temps_attente_moyen,
MIN(transfer_time_minutes) as temps_attente_min
FROM transfer_opportunities
GROUP BY transfer_stop_name
ORDER BY nb_trains_au_depart DESC
LIMIT 20;
SELECT
transfer_stop_name,
arrival_time,
COUNT(*) as nb_connexions_disponibles,
MIN(departure_time) as prochain_depart,
MIN(transfer_time_minutes) as temps_attente_min
FROM transfer_opportunities
WHERE arrival_trip_id = 'TRIP_ID_HERE'
GROUP BY transfer_stop_name, arrival_time
ORDER BY transfer_time_minutes;
Le script crée automatiquement ces index pour accélérer les recherches :
-- Index pour les correspondances
CREATE INDEX idx_stop_times_stop_arrival ON stop_times(stop_id, arrival_time);
CREATE INDEX idx_stop_times_stop_departure ON stop_times(stop_id, departure_time);
CREATE INDEX idx_stop_times_compound ON stop_times(stop_id, trip_id, departure_time, arrival_time);
Pour des performances maximales, vous pouvez activer la table precalculated_transfers :
// Dans createGTFSDatabase.js, ligne 700
createTransferTable(db, true); // ⚠️ Peut être long et volumineux
Cette table pré-calcule toutes les correspondances possibles. C’est très rapide à interroger mais :
Recommandation : Commencez sans cette table. Les vues SQL sont déjà très rapides. Activez-la uniquement si vous avez des problèmes de performance.
const JourneyDisplay = ({ journey }: { journey: JourneyWithTransfer }) => {
return (
<View style={styles.journey}>
{/* Résumé */}
<Text style={styles.duration}>
Durée totale: {Math.floor(journey.totalDuration / 60)}h
{journey.totalDuration % 60}min
</Text>
{journey.legs.length > 1 && (
<Text style={styles.transfers}>
{journey.legs.length - 1} correspondance(s)
</Text>
)}
{/* Chaque tronçon */}
{journey.legs.map((leg, index) => (
<View key={index}>
<View style={styles.leg}>
<Text style={styles.station}>{leg.from_stop_name}</Text>
<Text style={styles.time}>{leg.departure_time}</Text>
<Text style={styles.route}>{leg.route_short_name}</Text>
<Text style={styles.arrow}>↓</Text>
<Text style={styles.station}>{leg.to_stop_name}</Text>
<Text style={styles.time}>{leg.arrival_time}</Text>
</View>
{/* Correspondance */}
{index < journey.legs.length - 1 && (
<View style={styles.transfer}>
<Icon name="swap-horizontal" />
<Text>Correspondance ({journey.transferTime} min d'attente)</Text>
</View>
)}
</View>
))}
</View>
);
};
L’algorithme privilégie automatiquement :
// La fonction findAllJourneys gère tout ça automatiquement !
const journeys = await gtfsDbEnhanced.findAllJourneys(
fromStopId,
toStopId,
departureTime,
2 // maxTransfers
);
// Obtenir les trajets valides pour une date spécifique
const allJourneys = await gtfsDbEnhanced.findAllJourneys(/*...*/);
const validJourneys = await gtfsDbEnhanced.filterJourneysByDate(
allJourneys,
new Date('2025-12-25') // Jour de Noël
);
// Trouver les meilleures gares pour une correspondance
const query = `
SELECT
leg1.to_stop_name as gare_correspondance,
COUNT(*) as nb_options,
AVG(
(CAST(substr(leg2.departure_time, 1, 2) AS INTEGER) * 60 +
CAST(substr(leg2.departure_time, 4, 2) AS INTEGER)) -
(CAST(substr(leg1.arrival_time, 1, 2) AS INTEGER) * 60 +
CAST(substr(leg1.arrival_time, 4, 2) AS INTEGER))
) as temps_attente_moyen
FROM direct_connections leg1
JOIN direct_connections leg2
ON leg1.to_stop_id = leg2.from_stop_id
WHERE leg1.from_stop_id = ?
AND leg2.to_stop_id = ?
GROUP BY leg1.to_stop_name
ORDER BY nb_options DESC, temps_attente_moyen ASC
LIMIT 5;
`;
Vous pouvez ajuster les limites dans la vue transfer_opportunities :
-- Dans createGTFSDatabase.js, fonction createViews()
WHERE transfer_time_minutes >= 5 -- Minimum: 5 minutes
AND transfer_time_minutes <= 120; -- Maximum: 2 heures
Modifiez ces valeurs selon vos besoins :
| Méthode | Vitesse | Correspondances | Complexité |
|---|---|---|---|
| Fichiers JSON | 🐌 Lent | ❌ Complexe | 😰 Élevée |
Vue direct_connections |
⚡ Rapide | ⚠️ Même trajet uniquement | 😊 Simple |
Vue transfer_opportunities |
⚡ Rapide | ✅ Oui | 😊 Simple |
| Service Enhanced | 🚀 Ultra-rapide | ✅ Oui (SQL optimisé) | 😎 Très simple |
| Table pré-calculée | 💨 Instantané | ✅ Oui | 😌 Moyen |
Oui, la base de données gère complètement les correspondances !
gtfsDb.findJourney() - boucles TypeScriptgtfsDbEnhanced.findAllJourneys() - SQL pur ⭐Tout est prêt pour gérer les correspondances de manière optimale ! 🚀