Ce guide explique comment créer et utiliser une base de données SQLite optimisée à partir des données GTFS pour des recherches locales ultra-rapides.
✅ Performance : Requêtes instantanées grâce aux index SQL ✅ Mémoire : Pas besoin de charger toutes les données en RAM ✅ Flexibilité : Requêtes SQL complexes possibles ✅ Taille : Base de données compressée et optimisée ✅ Recherche spatiale : Recherche par coordonnées GPS intégrée
npm install --save-dev better-sqlite3
npm install expo-sqlite
Assurez-vous que vos fichiers GTFS sont dans le dossier :
assets/sncf_data/
├── stops.txt
├── routes.txt
├── trips.txt
├── stop_times.txt
├── calendar.txt
└── calendar_dates.txt
node scripts/createGTFSDatabase.js
Le script va :
assets/gtfs.dbDurée estimée : 2-5 minutes selon la taille des données
Le fichier assets/gtfs.db doit être créé avec les statistiques affichées :
📊 Statistiques de la base de données:
Gares: X,XXX
Lignes: XXX
Trajets: XX,XXX
Horaires: XXX,XXX
...
import { gtfsDb } from './src/services/gtfsDatabaseService';
// Dans votre composant principal (App.tsx)
useEffect(() => {
const initDb = async () => {
try {
await gtfsDb.initialize();
console.log('Base de données GTFS prête !');
} catch (error) {
console.error('Erreur initialisation GTFS:', error);
}
};
initDb();
}, []);
const searchStations = async (query: string) => {
const results = await gtfsDb.searchStops(query, 20);
// results = [
// { stop_id: '...', stop_name: 'Paris Gare de Lyon', ... }
// ]
return results;
};
// Exemple d'utilisation
const stations = await searchStations('Paris');
const findNearby = async (latitude: number, longitude: number) => {
const nearbyStops = await gtfsDb.findNearbyStops(latitude, longitude, 10);
nearbyStops.forEach(stop => {
console.log(`${stop.stop_name} - ${stop.distance.toFixed(1)} km`);
});
return nearbyStops;
};
// Exemple : trouver les gares près de ma position
const location = await Location.getCurrentPositionAsync();
const nearby = await findNearby(
location.coords.latitude,
location.coords.longitude
);
const findTrains = async (
fromStopId: string,
toStopId: string,
departureTime: string
) => {
const connections = await gtfsDb.findDirectConnections(
fromStopId,
toStopId,
departureTime, // '08:00:00'
'23:59:59', // Heure max
50 // Limite de résultats
);
return connections;
};
// Exemple
const trains = await findTrains(
'StopID:DUA8711300:LOC', // Paris Gare de Lyon
'StopID:DUA8727100:LOC', // Lyon Part Dieu
'08:00:00'
);
const planJourney = async (
fromStopId: string,
toStopId: string,
departureTime: string,
date: Date
) => {
const journeys = await gtfsDb.findJourney(
fromStopId,
toStopId,
departureTime,
date,
2 // Max 2 correspondances
);
// journeys = [
// [connection1, connection2], // Trajet avec 1 correspondance
// [connection3], // Trajet direct
// ]
return journeys;
};
// Exemple
const journeys = await planJourney(
'StopID:...',
'StopID:...',
'08:00:00',
new Date()
);
const getDestinations = async (fromStopId: string) => {
const destinations = await gtfsDb.findDestinationsFrom(
fromStopId,
'06:00:00', // Heure min (optionnel)
'23:00:00' // Heure max (optionnel)
);
destinations.forEach(dest => {
console.log(`${dest.stop_name} - ${dest.nb_connections} trains`);
});
return destinations;
};
const SearchScreen = () => {
const [query, setQuery] = useState('');
const [results, setResults] = useState<Stop[]>([]);
useEffect(() => {
const search = async () => {
if (query.length >= 2) {
const stops = await gtfsDb.searchStops(query, 10);
setResults(stops);
} else {
setResults([]);
}
};
const debounce = setTimeout(search, 300);
return () => clearTimeout(debounce);
}, [query]);
return (
<View>
<TextInput
value={query}
onChangeText={setQuery}
placeholder="Rechercher une gare..."
/>
<FlatList
data={results}
keyExtractor={(item) => item.stop_id}
renderItem={({ item }) => (
<Text>{item.stop_name}</Text>
)}
/>
</View>
);
};
const NextDepartures = ({ stopId }: { stopId: string }) => {
const [departures, setDepartures] = useState<Connection[]>([]);
useEffect(() => {
const loadDepartures = async () => {
const now = new Date();
const currentTime = now.toTimeString().slice(0, 8); // 'HH:MM:SS'
const dests = await gtfsDb.findDestinationsFrom(
stopId,
currentTime
);
setDepartures(dests);
};
loadDepartures();
}, [stopId]);
return (
<FlatList
data={departures}
keyExtractor={(item, index) => `${item.stop_id}-${index}`}
renderItem={({ item }) => (
<View>
<Text>{item.stop_name}</Text>
<Text>{item.nb_connections} trains disponibles</Text>
</View>
)}
/>
);
};
const MapWithNearbyStations = () => {
const [location, setLocation] = useState<Location.LocationObject | null>(null);
const [nearbyStops, setNearbyStops] = useState<Stop[]>([]);
useEffect(() => {
const getLocation = async () => {
const loc = await Location.getCurrentPositionAsync();
setLocation(loc);
const stops = await gtfsDb.findNearbyStops(
loc.coords.latitude,
loc.coords.longitude,
20
);
setNearbyStops(stops);
};
getLocation();
}, []);
return (
<MapView
region=
>
{nearbyStops.map((stop) => (
<Marker
key={stop.stop_id}
coordinate=
title={stop.stop_name}
/>
))}
</MapView>
);
};
Pour des besoins spécifiques, vous pouvez exécuter des requêtes SQL directement :
import * as SQLite from 'expo-sqlite';
const db = await SQLite.openDatabaseAsync('gtfs.db');
// Requête personnalisée
const results = await db.getAllAsync<YourType>(
`SELECT * FROM direct_connections
WHERE from_stop_name LIKE ?
AND departure_time >= ?
ORDER BY departure_time
LIMIT ?`,
['%Paris%', '08:00:00', 10]
);
SELECT route_short_name, COUNT(DISTINCT trip_id) as nb_trains
FROM direct_connections
GROUP BY route_short_name
ORDER BY nb_trains DESC;
SELECT to_stop_name, COUNT(*) as nb_arrivees
FROM direct_connections
GROUP BY to_stop_name
ORDER BY nb_arrivees DESC
LIMIT 20;
SELECT
from_stop_name,
to_stop_name,
AVG(
(CAST(substr(arrival_time, 1, 2) AS INTEGER) * 60 +
CAST(substr(arrival_time, 4, 2) AS INTEGER)) -
(CAST(substr(departure_time, 1, 2) AS INTEGER) * 60 +
CAST(substr(departure_time, 4, 2) AS INTEGER))
) as avg_duration_minutes
FROM direct_connections
WHERE from_stop_name LIKE '%Paris%'
AND to_stop_name LIKE '%Lyon%'
GROUP BY from_stop_name, to_stop_name;
const cache = new Map<string, any>();
const getCachedStops = async (query: string) => {
const cacheKey = `stops:${query}`;
if (cache.has(cacheKey)) {
return cache.get(cacheKey);
}
const results = await gtfsDb.searchStops(query);
cache.set(cacheKey, results);
return results;
};
const MAJOR_STATIONS = [
'Paris Gare de Lyon',
'Paris Montparnasse',
'Lyon Part Dieu',
'Marseille St-Charles',
// ...
];
const preloadMajorStations = async () => {
for (const station of MAJOR_STATIONS) {
await gtfsDb.searchStops(station, 1);
}
};
import { debounce } from 'lodash';
const debouncedSearch = debounce(async (query: string) => {
return await gtfsDb.searchStops(query);
}, 300);
assets/gtfs.db existegtfsDb.getStats() pour voir le contenuPour mettre à jour les données GTFS :
assets/sncf_data/node scripts/createGTFSDatabase.jsassets/gtfs.db dans l’applicationVous avez maintenant une base de données GTFS ultra-performante pour votre application de recherche de trajets !
Pour toute question, consultez le code dans :