2A.eco - Exercice API SNCF corrigé

Links: notebook, html, PDF, python, slides, GitHub

Manipulation d’une API REST, celle de la SNCF est prise comme exemple. Correction d’exercices.

from jyquickhelper import add_notebook_menu
add_notebook_menu()

Partie 0 - modules recommandés et connexion à l’API

Il vous faudra sûrement les modules suivant :

  • requests

  • datetime

  • pandas

  • matplotlib

Créer un login pour vous connecter à l’API de la SNCF https://data.sncf.com/api

Vous pouvez maintenant commencer. Ce notebook peut prendre du temps à s’éxécuter, surout à partir de la partie 3

# !!!!! Attention à bien mettre votre token ici  !!!!!

token_auth = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
import keyring, os
if "XXXXXX" in token_auth:
    token_auth = keyring.get_password("sncf", "ensae_teaching_cs,key")

Partie 1 - Trouver les gares accessibles via la SNCF

  • Trouver l’ensemble des gares disponibles sur l’API et créer un fichier csv avec les codes de la gare, son nom et ses coordonnées latitude et longitude, ainsi que les informations administratives de la région quand elles sont disponibles

  • Représentez les sur un graphique

import pandas as pd
import requests
def page_gares(numero_page) :
    return requests.get(
        ('https://api.sncf.com/v1/coverage/sncf/stop_areas?start_page={}').format(numero_page),
        auth=(token_auth, ''))

######################################
# on commence par la première page qui nous donne le nombre de résultats par page ainsi que le nombre total de résultats

page_initiale = page_gares(0)
item_per_page = page_initiale.json()['pagination']['items_per_page']
total_items = page_initiale.json()['pagination']['total_result']
dfs = []

# on fait une boucle sur toutes les pages suivantes
print_done = {}

for page in range(int(total_items/item_per_page)+1) :
    stations_page = page_gares(page)

    ensemble_stations = stations_page.json()

    if 'stop_areas' not in ensemble_stations:
        # pas d'arrêt
        continue

    # on ne retient que les informations qui nous intéressent
    for station in ensemble_stations['stop_areas']:

        station['lat'] = station['coord']['lat']
        station["lon"]  = station['coord']['lon']

        if 'administrative_regions' in station.keys() :
            for var_api, var_df in zip(['insee','name','label','id','zip_code'],
                                       ['insee','region','label_region','id_region','zip_code']):
                try:
                    station[var_df] = station['administrative_regions'][0][var_api]
                except KeyError:
                    if var_api not in print_done:
                        print("key '{0}' not here but {1}".format(var_api,
                                ",".join(station['administrative_regions'][0].keys())))
                        print_done[var_api] = var_api

        [station.pop(k,None) for k in ['coord','links','administrative_regions', 'type', 'codes']]

    stations = ensemble_stations['stop_areas']
    try:
        dp = pd.DataFrame(stations)
    except Exception as e:
        # La SNCF modifie parfois le schéma de ses données.
        # On affiche station pour avoir une meilleure idée que l'erreur retournée par pandas
        raise Exception("Problème de données\n{0}".format(stations)) from e

    dfs.append(dp)
    if page % 10 == 0:
        print("je suis à la page", page, "---", dp.shape)

import pandas
df = pandas.concat(dfs)
df.to_csv("./ensemble_gares.csv")
print(df.shape)
df.head()
je suis à la page 0 --- (25, 11)
je suis à la page 10 --- (25, 11)
je suis à la page 20 --- (25, 11)
je suis à la page 30 --- (25, 11)
je suis à la page 40 --- (25, 11)
je suis à la page 50 --- (25, 11)
je suis à la page 60 --- (25, 11)
je suis à la page 70 --- (25, 11)
je suis à la page 80 --- (25, 11)
je suis à la page 90 --- (25, 11)
je suis à la page 100 --- (25, 11)
je suis à la page 110 --- (25, 11)
je suis à la page 120 --- (25, 11)
je suis à la page 130 --- (25, 11)
je suis à la page 140 --- (25, 11)
je suis à la page 150 --- (25, 11)
je suis à la page 160 --- (25, 11)
je suis à la page 170 --- (25, 11)
(4314, 11)
name label timezone id lat lon insee region label_region id_region zip_code
0 Europe/Paris stop_area:OCE:SA:87271387 0 0 NaN NaN NaN NaN NaN
1 . . Europe/Paris stop_area:OCE:SA:00 27.141247 -3.404382 NaN NaN NaN NaN NaN
2 _ _ Europe/Paris stop_area:OCE:SA:87757005 0 0 NaN NaN NaN NaN NaN
3 Aachen/Aix la Chapelle Aachen/Aix la Chapelle (Aachen) Europe/Paris stop_area:OCE:SA:80153452 50.767759 6.091215 Aachen Aachen admin:osm:relation:62564
4 Abancourt Abancourt (Abancourt) Europe/Paris stop_area:OCE:SA:87313759 49.685665 1.77418 60001 Abancourt Abancourt (60220) admin:fr:60001 60220
df[df.name.str.contains('Lyon')]
name label timezone id lat lon insee region label_region id_region zip_code
12 Lyon-Gorge-de-Loup Lyon-Gorge-de-Loup (Lyon) Europe/Paris stop_area:OCE:SA:87721175 45.766077 4.804731 69123 Lyon Lyon admin:fr:69123
13 Lyon-Jean-Macé Lyon-Jean-Macé (Lyon) Europe/Paris stop_area:OCE:SA:87282624 45.745198 4.841515 69123 Lyon Lyon admin:fr:69123
14 Lyon-Part-Dieu Lyon-Part-Dieu (Lyon) Europe/Paris stop_area:OCE:SA:87723197 45.760585 4.859435 69123 Lyon Lyon admin:fr:69123
15 Lyon-Part-Dieu-Gare-Rou Lyon-Part-Dieu-Gare-Rou (Lyon) Europe/Paris stop_area:OCE:SA:87697128 45.760585 4.859435 69123 Lyon Lyon admin:fr:69123
16 Lyon-Perrache Lyon-Perrache (Lyon) Europe/Paris stop_area:OCE:SA:87722025 45.748785 4.825941 69123 Lyon Lyon admin:fr:69123
17 Lyon-Perrache-Gare-Rout Lyon-Perrache-Gare-Rout (Lyon) Europe/Paris stop_area:OCE:SA:87697136 45.749712 4.82678 69123 Lyon Lyon admin:fr:69123
18 Lyon-Saint-Exupery-Tgv Lyon-Saint-Exupery-Tgv (Colombier-Saugnieu) Europe/Paris stop_area:OCE:SA:87762906 45.720928 5.075833 69299 Colombier-Saugnieu Colombier-Saugnieu (69124) admin:fr:69299 69124
19 Lyon-St-Paul Lyon-St-Paul (Lyon) Europe/Paris stop_area:OCE:SA:87721159 45.766082 4.826904 69123 Lyon Lyon admin:fr:69123
20 Lyon-St-Paul-la-Feuilée Lyon-St-Paul-la-Feuilée (Lyon) Europe/Paris stop_area:OCE:SA:87698340 45.766342 4.829385 69123 Lyon Lyon admin:fr:69123
21 Lyon-St-Paul-Quai-Bondy Lyon-St-Paul-Quai-Bondy (Lyon) Europe/Paris stop_area:OCE:SA:87698332 45.766488 4.828105 69123 Lyon Lyon admin:fr:69123
22 Lyon-Vaise Lyon-Vaise (Lyon) Europe/Paris stop_area:OCE:SA:87721001 45.780142 4.80405 69123 Lyon Lyon admin:fr:69123
23 Lyon-Vaise-Gare-Routièr Lyon-Vaise-Gare-Routièr (Lyon) Europe/Paris stop_area:OCE:SA:87697045 45.781597 4.805428 69123 Lyon Lyon admin:fr:69123
20 Paris-Gare-de-Lyon Paris-Gare-de-Lyon (Paris) Europe/Paris stop_area:OCE:SA:87686006 48.844922 2.373462 75056 Paris Paris (75000-75116) admin:fr:75056 75000;75116
18 Urcay-Hotel-du-Lyon-d'O Urcay-Hotel-du-Lyon-d'O (Urçay) Europe/Paris stop_area:OCE:SA:87409490 46.626818 2.589411 3293 Urçay Urçay (03360) admin:fr:3293 03360
df = pd.read_csv("./ensemble_gares.csv", encoding = "ISO-8859-1")
print(df.columns)
print(df.shape)
# Exemple des informations sur une gare
df.iloc[317]
Index(['Unnamed: 0', 'name', 'label', 'timezone', 'id', 'lat', 'lon', 'insee',
       'region', 'label_region', 'id_region', 'zip_code'],
      dtype='object')
(4314, 12)
Unnamed: 0                                     17
name                              Baume-les-Dames
label           Baume-les-Dames (Baume-les-Dames)
timezone                             Europe/Paris
id                      stop_area:OCE:SA:87718460
lat                                        47.354
lon                                       6.35967
insee                                       25047
region                            Baume-les-Dames
label_region              Baume-les-Dames (25110)
id_region                          admin:fr:25047
zip_code                                    25110
Name: 317, dtype: object
# on crée un dictionnaire des correspondances entre les noms et les codes des gares
dict_label_gare_code = df[['label','id']].set_index('label').to_dict()['id']
dict_nom_gare_code = df[['name','id']].set_index('name').to_dict()['id']
print(df.columns)

# graphique dans le plan des gares
%matplotlib inline
import matplotlib.pyplot as plt
lng_var = df[(df['lat']>35) & (df['lat']<60)]["lon"].tolist()
lat_var = df[(df['lat']>35) & (df['lat']<60)]["lat"].tolist()
plt.scatter(x = lng_var , y = lat_var,marker = "o");
Index(['Unnamed: 0', 'name', 'label', 'timezone', 'id', 'lat', 'lon', 'insee',
       'region', 'label_region', 'id_region', 'zip_code'],
      dtype='object')
../_images/TD2A_eco_API_SNCF_corrige_11_1.png

Les trajets depuis la Gare de Lyon

Partons à Lyon

Imaginez que vous vouliez un peu voyager hors de Paris, et il se trouve que justement on vous propose de passer quelques jours à Lyon. Vous partez le 17 novembre vers 19h50 pour ne pas trop écourter votre journée de travail (documentation de l’API).

Question 1

  • Commencez par récupérer les informations sur le trajet entre Paris Gare de Lyon et Lyon Perrache le 17 novembre à 19h57

    • Paris - Gare de Lyon (code de la gare : stop_area:OCE:SA:87686006)

    • Lyon - Gare Lyon Perrache (code de la gare : stop_area:OCE:SA:87722025)

    • Indice : utiliser la requête “journeys”

    • Autre indice : le format de la date est AAAAMMJJTHHMMSS (Année, mois, jour, heure, minutes, secondes)

  • Répondez aux questions suivantes

    • combien y a-t-il d’arrêts entre ces deux gares ? (utilisez la clé ‘journeys’)

    • combien de temps d’arrêt à chacune d’elles ?

Tout d’abord une fonction qui sera utile pour calculer des temps :

from datetime import datetime, timedelta

def convertir_en_temps(chaine) :
    ''' on convertit en date la chaine de caractères de l API'''
    return datetime.strptime(chaine.replace('T',''),'%Y%m%d%H%M%S')

Et l’inverse :

def convertir_en_chaine(dt) :
    ''' on convertit en chaîne de caractères un datetime'''
    return datetime.strftime(dt, '%Y%m%dT%H%M%S')

now = datetime.now()
convertir_en_chaine(now)
'20200418T165041'
convertir_en_temps(convertir_en_chaine(now))
datetime.datetime(2020, 4, 18, 16, 50, 41)
# informations sur le trajet qu'on choisit dans le futur
# l'API ne retourne pas de résultatq très loin dans le passé
now = datetime.now()
dt = now + timedelta(14)  # dans deux semaines

date_depart = convertir_en_chaine(dt)
gare_depart = 'stop_area:OCE:SA:87686006'
gare_arrivee = 'stop_area:OCE:SA:87722025'

date_depart
'20200502T165041'

Ensemble des départs :

paris_lyon = requests.get(
    ('https://api.sncf.com/v1/coverage/sncf/journeys?'
     'from={}&to={}&datetime={}').format(gare_depart, gare_arrivee, date_depart),
    auth=(token_auth, '')).json()

Les gares du chemin entre Paris et Lyon sur ce trajet ainsi que le temps d’arrêt :

session = paris_lyon['journeys'][0]['sections'][1]
rows = []
if "stop_date_times" in session:
    for i in session['stop_date_times']:
        rows.append(dict(name=i['stop_point']['name'],
                         depart=convertir_en_temps(i['departure_date_time']),
                         arrivee=convertir_en_temps(i['arrival_date_time'])))
pandas.DataFrame(rows)
name depart arrivee
0 Paris-Gare-de-Lyon 2020-05-02 16:58:00 2020-05-02 16:58:00
1 Lyon-Part-Dieu 2020-05-02 19:01:00 2020-05-02 18:56:00
2 Lyon-Perrache 2020-05-02 19:09:00 2020-05-02 19:09:00

Question 2

Vous êtes un peu pressé et vous avez peur de vous tromper en arrivant à la gare car d’autres TGV partent à peu près en même temps (à partir de 19h00) de la gare de Lyon. Si vous demandez à l’API, combien de résultats vous donne-t-elle ?

Soit l’API a changé, soit…

requests.get(
    ('https://api.sncf.com/v1/coverage/sncf/stop_points/'
     'stop_area:OCE:SA:87686006/departures'), auth=(token_auth, '')).json()
{'pagination': {'start_page': 0,
  'items_on_page': 0,
  'items_per_page': 0,
  'total_result': 0},
 'links': [],
 'disruptions': [],
 'notes': [],
 'feed_publishers': [],
 'departures': [],
 'context': {'timezone': 'Europe/Paris',
  'current_datetime': '20200418T165041'},
 'error': {'message': 'ptref : Filters: Unable to find object',
  'id': 'unknown_object'},
 'exceptions': []}
### les trains qui partent autour de 19h00
departs_paris = requests.get(
    ('https://api.sncf.com/v1/coverage/sncf/stop_points/stop_point:OCE:SP:'
     'TGV-87686006/departures?from_datetime={}').format(
        date_depart), auth=(token_auth, '')).json()

# Nombre de trains que l'API renvoie à partir de cet horaire-là
#print(len(departs_paris['departures']))
departs_paris
{'pagination': {'start_page': 0,
  'items_on_page': 10,
  'items_per_page': 10,
  'total_result': 10},
 'links': [{'href': 'https://api.sncf.com/v1/coverage/sncf/stop_points/{stop_point.id}',
   'type': 'stop_point',
   'rel': 'stop_points',
   'templated': True},
  {'href': 'https://api.sncf.com/v1/coverage/sncf/commercial_modes/{commercial_modes.id}',
   'type': 'commercial_modes',
   'rel': 'commercial_modes',
   'templated': True},
  {'href': 'https://api.sncf.com/v1/coverage/sncf/stop_areas/{stop_area.id}',
   'type': 'stop_area',
   'rel': 'stop_areas',
   'templated': True},
  {'href': 'https://api.sncf.com/v1/coverage/sncf/physical_modes/{physical_modes.id}',
   'type': 'physical_modes',
   'rel': 'physical_modes',
   'templated': True},
  {'href': 'https://api.sncf.com/v1/coverage/sncf/routes/{route.id}',
   'type': 'route',
   'rel': 'routes',
   'templated': True},
  {'href': 'https://api.sncf.com/v1/coverage/sncf/commercial_modes/{commercial_mode.id}',
   'type': 'commercial_mode',
   'rel': 'commercial_modes',
   'templated': True},
  {'href': 'https://api.sncf.com/v1/coverage/sncf/vehicle_journeys/{vehicle_journey.id}',
   'type': 'vehicle_journey',
   'rel': 'vehicle_journeys',
   'templated': True},
  {'href': 'https://api.sncf.com/v1/coverage/sncf/lines/{line.id}',
   'type': 'line',
   'rel': 'lines',
   'templated': True},
  {'href': 'https://api.sncf.com/v1/coverage/sncf/physical_modes/{physical_mode.id}',
   'type': 'physical_mode',
   'rel': 'physical_modes',
   'templated': True},
  {'href': 'https://api.sncf.com/v1/coverage/sncf/networks/{network.id}',
   'type': 'network',
   'rel': 'networks',
   'templated': True},
  {'href': 'https://api.sncf.com/v1/coverage/sncf/stop_points/stop_point:OCE:SP:TGV-87686006/departures?from_datetime=20200502T165041',
   'type': 'first',
   'templated': False}],
 'disruptions': [],
 'notes': [],
 'feed_publishers': [],
 'departures': [{'display_informations': {'direction': 'Mulhouse (Mulhouse)',
    'code': '',
    'network': 'SNCF',
    'links': [],
    'color': '000000',
    'name': 'Paris <> Bourgogne Franche Comté <> Mulhouse',
    'physical_mode': 'Train grande vitesse',
    'headsign': '6713',
    'label': 'Paris <> Bourgogne Franche Comté <> Mulhouse',
    'equipments': [],
    'text_color': 'FFFFFF',
    'commercial_mode': 'TGV',
    'description': ''},
   'stop_point': {'commercial_modes': [{'id': 'commercial_mode:INOUI',
      'name': 'TGV INOUI'},
     {'id': 'commercial_mode:tgv', 'name': 'TGV'}],
    'name': 'Paris-Gare-de-Lyon',
    'links': [],
    'physical_modes': [{'id': 'physical_mode:LongDistanceTrain',
      'name': 'Train grande vitesse'}],
    'coord': {'lat': '48.844922', 'lon': '2.373462'},
    'label': 'Paris-Gare-de-Lyon (Paris)',
    'equipments': [],
    'administrative_regions': [{'insee': '75056',
      'name': 'Paris',
      'level': 8,
      'coord': {'lat': '48.8566969', 'lon': '2.3514616'},
      'label': 'Paris (75000-75116)',
      'id': 'admin:fr:75056',
      'zip_code': '75000;75116'}],
    'fare_zone': {'name': '0'},
    'id': 'stop_point:OCE:SP:TGV-87686006',
    'stop_area': {'codes': [{'type': 'CR-CI-CH', 'value': '0087-686006-BV'},
      {'type': 'CR-CI-CH', 'value': '0087-686006-BV'},
      {'type': 'UIC8', 'value': '87686006'},
      {'type': 'external_code', 'value': 'OCE87686006'}],
     'name': 'Paris-Gare-de-Lyon',
     'links': [],
     'coord': {'lat': '48.844922', 'lon': '2.373462'},
     'label': 'Paris-Gare-de-Lyon (Paris)',
     'administrative_regions': [{'insee': '75056',
       'name': 'Paris',
       'level': 8,
       'coord': {'lat': '48.8566969', 'lon': '2.3514616'},
       'label': 'Paris (75000-75116)',
       'id': 'admin:fr:75056',
       'zip_code': '75000;75116'}],
     'timezone': 'Europe/Paris',
     'id': 'stop_area:OCE:SA:87686006'}},
   'route': {'direction': {'embedded_type': 'stop_area',
     'stop_area': {'codes': [{'type': 'CR-CI-CH', 'value': '0087-182063-BV'},
       {'type': 'CR-CI-CH', 'value': '0087-182063-BV'},
       {'type': 'UIC8', 'value': '87182063'},
       {'type': 'external_code', 'value': 'OCE87182063'}],
      'name': 'Mulhouse',
      'links': [],
      'coord': {'lat': '47.741798', 'lon': '7.34285'},
      'label': 'Mulhouse (Mulhouse)',
      'timezone': 'Europe/Paris',
      'id': 'stop_area:OCE:SA:87182063'},
     'quality': 0,
     'name': 'Mulhouse (Mulhouse)',
     'id': 'stop_area:OCE:SA:87182063'},
    'name': 'Paris-Gare-de-Lyon vers Mulhouse (TGV)',
    'links': [],
    'physical_modes': [{'id': 'physical_mode:LongDistanceTrain',
      'name': 'Train grande vitesse'}],
    'is_frequence': 'False',
    'geojson': {'type': 'MultiLineString', 'coordinates': []},
    'direction_type': 'forward',
    'line': {'code': '',
     'name': 'Paris <> Bourgogne Franche Comté <> Mulhouse',
     'links': [],
     'color': '000000',
     'geojson': {'type': 'MultiLineString', 'coordinates': []},
     'text_color': 'FFFFFF',
     'codes': [],
     'closing_time': '235600',
     'opening_time': '053300',
     'id': 'line:OCE:139'},
    'id': 'route:OCE:139-TGV-87686006-87182063'},
   'links': [{'type': 'line', 'id': 'line:OCE:139'},
    {'type': 'vehicle_journey', 'id': 'vehicle_journey:OCE:SN006713F03003'},
    {'type': 'route', 'id': 'route:OCE:139-TGV-87686006-87182063'},
    {'type': 'commercial_mode', 'id': 'commercial_mode:tgv'},
    {'type': 'physical_mode', 'id': 'physical_mode:LongDistanceTrain'},
    {'type': 'network', 'id': 'network:sncf'}],
   'stop_date_time': {'links': [],
    'arrival_date_time': '20200502T191900',
    'additional_informations': [],
    'departure_date_time': '20200502T191900',
    'base_arrival_date_time': '20200502T191900',
    'base_departure_date_time': '20200502T191900',
    'data_freshness': 'base_schedule'}},
  {'display_informations': {'direction': 'Grenoble (Grenoble)',
    'code': '',
    'network': 'SNCF',
    'links': [],
    'color': '000000',
    'name': 'Paris <> Grenoble',
    'physical_mode': 'Train grande vitesse',
    'headsign': '6927',
    'label': 'Paris <> Grenoble',
    'equipments': [],
    'text_color': 'FFFFFF',
    'commercial_mode': 'TGV',
    'description': ''},
   'stop_point': {'commercial_modes': [{'id': 'commercial_mode:INOUI',
      'name': 'TGV INOUI'},
     {'id': 'commercial_mode:tgv', 'name': 'TGV'}],
    'name': 'Paris-Gare-de-Lyon',
    'links': [],
    'physical_modes': [{'id': 'physical_mode:LongDistanceTrain',
      'name': 'Train grande vitesse'}],
    'coord': {'lat': '48.844922', 'lon': '2.373462'},
    'label': 'Paris-Gare-de-Lyon (Paris)',
    'equipments': [],
    'administrative_regions': [{'insee': '75056',
      'name': 'Paris',
      'level': 8,
      'coord': {'lat': '48.8566969', 'lon': '2.3514616'},
      'label': 'Paris (75000-75116)',
      'id': 'admin:fr:75056',
      'zip_code': '75000;75116'}],
    'fare_zone': {'name': '0'},
    'id': 'stop_point:OCE:SP:TGV-87686006',
    'stop_area': {'codes': [{'type': 'CR-CI-CH', 'value': '0087-686006-BV'},
      {'type': 'CR-CI-CH', 'value': '0087-686006-BV'},
      {'type': 'UIC8', 'value': '87686006'},
      {'type': 'external_code', 'value': 'OCE87686006'}],
     'name': 'Paris-Gare-de-Lyon',
     'links': [],
     'coord': {'lat': '48.844922', 'lon': '2.373462'},
     'label': 'Paris-Gare-de-Lyon (Paris)',
     'administrative_regions': [{'insee': '75056',
       'name': 'Paris',
       'level': 8,
       'coord': {'lat': '48.8566969', 'lon': '2.3514616'},
       'label': 'Paris (75000-75116)',
       'id': 'admin:fr:75056',
       'zip_code': '75000;75116'}],
     'timezone': 'Europe/Paris',
     'id': 'stop_area:OCE:SA:87686006'}},
   'route': {'direction': {'embedded_type': 'stop_area',
     'stop_area': {'codes': [{'type': 'CR-CI-CH', 'value': '0087-747006-BV'},
       {'type': 'CR-CI-CH', 'value': '0087-747006-BV'},
       {'type': 'UIC8', 'value': '87747006'},
       {'type': 'external_code', 'value': 'OCE87747006'}],
      'name': 'Grenoble',
      'links': [],
      'coord': {'lat': '45.191463', 'lon': '5.71453'},
      'label': 'Grenoble (Grenoble)',
      'timezone': 'Europe/Paris',
      'id': 'stop_area:OCE:SA:87747006'},
     'quality': 0,
     'name': 'Grenoble (Grenoble)',
     'id': 'stop_area:OCE:SA:87747006'},
    'name': 'Paris-Gare-de-Lyon vers Grenoble (TGV)',
    'links': [],
    'physical_modes': [{'id': 'physical_mode:LongDistanceTrain',
      'name': 'Train grande vitesse'}],
    'is_frequence': 'False',
    'geojson': {'type': 'MultiLineString', 'coordinates': []},
    'direction_type': 'forward',
    'line': {'code': '',
     'name': 'Paris <> Grenoble',
     'links': [],
     'color': '000000',
     'geojson': {'type': 'MultiLineString', 'coordinates': []},
     'text_color': 'FFFFFF',
     'codes': [],
     'closing_time': '002100',
     'opening_time': '051900',
     'id': 'line:OCE:146'},
    'id': 'route:OCE:146-TGV-87686006-87747006'},
   'links': [{'type': 'line', 'id': 'line:OCE:146'},
    {'type': 'vehicle_journey', 'id': 'vehicle_journey:OCE:SN006927F12012'},
    {'type': 'route', 'id': 'route:OCE:146-TGV-87686006-87747006'},
    {'type': 'commercial_mode', 'id': 'commercial_mode:tgv'},
    {'type': 'physical_mode', 'id': 'physical_mode:LongDistanceTrain'},
    {'type': 'network', 'id': 'network:sncf'}],
   'stop_date_time': {'links': [],
    'arrival_date_time': '20200502T194300',
    'additional_informations': [],
    'departure_date_time': '20200502T194300',
    'base_arrival_date_time': '20200502T194300',
    'base_departure_date_time': '20200502T194300',
    'data_freshness': 'base_schedule'}},
  {'display_informations': {'direction': 'Milano-Porta-Garibaldi (Milano)',
    'code': '',
    'network': 'SNCF',
    'links': [],
    'color': '000000',
    'name': 'Paris<>Milano PG',
    'physical_mode': 'Train grande vitesse',
    'headsign': '9241',
    'label': 'Paris<>Milano PG',
    'equipments': [],
    'text_color': 'FFFFFF',
    'commercial_mode': 'TGV',
    'description': ''},
   'stop_point': {'commercial_modes': [{'id': 'commercial_mode:INOUI',
      'name': 'TGV INOUI'},
     {'id': 'commercial_mode:tgv', 'name': 'TGV'}],
    'name': 'Paris-Gare-de-Lyon',
    'links': [],
    'physical_modes': [{'id': 'physical_mode:LongDistanceTrain',
      'name': 'Train grande vitesse'}],
    'coord': {'lat': '48.844922', 'lon': '2.373462'},
    'label': 'Paris-Gare-de-Lyon (Paris)',
    'equipments': [],
    'administrative_regions': [{'insee': '75056',
      'name': 'Paris',
      'level': 8,
      'coord': {'lat': '48.8566969', 'lon': '2.3514616'},
      'label': 'Paris (75000-75116)',
      'id': 'admin:fr:75056',
      'zip_code': '75000;75116'}],
    'fare_zone': {'name': '0'},
    'id': 'stop_point:OCE:SP:TGV-87686006',
    'stop_area': {'codes': [{'type': 'CR-CI-CH', 'value': '0087-686006-BV'},
      {'type': 'CR-CI-CH', 'value': '0087-686006-BV'},
      {'type': 'UIC8', 'value': '87686006'},
      {'type': 'external_code', 'value': 'OCE87686006'}],
     'name': 'Paris-Gare-de-Lyon',
     'links': [],
     'coord': {'lat': '48.844922', 'lon': '2.373462'},
     'label': 'Paris-Gare-de-Lyon (Paris)',
     'administrative_regions': [{'insee': '75056',
       'name': 'Paris',
       'level': 8,
       'coord': {'lat': '48.8566969', 'lon': '2.3514616'},
       'label': 'Paris (75000-75116)',
       'id': 'admin:fr:75056',
       'zip_code': '75000;75116'}],
     'timezone': 'Europe/Paris',
     'id': 'stop_area:OCE:SA:87686006'}},
   'route': {'direction': {'embedded_type': 'stop_area',
     'stop_area': {'codes': [{'type': 'CR-CI-CH', 'value': '0083-016451-00'},
       {'type': 'CR-CI-CH', 'value': '0083-016451-00'},
       {'type': 'UIC8', 'value': '83016451'},
       {'type': 'external_code', 'value': 'OCE83016451'}],
      'name': 'Milano-Porta-Garibaldi',
      'links': [],
      'coord': {'lat': '45.48496', 'lon': '9.18759'},
      'label': 'Milano-Porta-Garibaldi (Milano)',
      'timezone': 'Europe/Paris',
      'id': 'stop_area:OCE:SA:83016451'},
     'quality': 0,
     'name': 'Milano-Porta-Garibaldi (Milano)',
     'id': 'stop_area:OCE:SA:83016451'},
    'name': 'Paris-Gare-de-Lyon vers Milano-Porta-Garibaldi (TGV)',
    'links': [],
    'physical_modes': [{'id': 'physical_mode:LongDistanceTrain',
      'name': 'Train grande vitesse'}],
    'is_frequence': 'False',
    'geojson': {'type': 'MultiLineString', 'coordinates': []},
    'direction_type': 'forward',
    'line': {'code': '',
     'name': 'Paris<>Milano PG',
     'links': [],
     'color': '000000',
     'geojson': {'type': 'MultiLineString', 'coordinates': []},
     'text_color': 'FFFFFF',
     'codes': [],
     'closing_time': '233000',
     'opening_time': '054500',
     'id': 'line:OCE:645702'},
    'id': 'route:OCE:645702-TGV-87686006-83016451'},
   'links': [{'type': 'line', 'id': 'line:OCE:645702'},
    {'type': 'vehicle_journey', 'id': 'vehicle_journey:OCE:SN009241F14014'},
    {'type': 'route', 'id': 'route:OCE:645702-TGV-87686006-83016451'},
    {'type': 'commercial_mode', 'id': 'commercial_mode:tgv'},
    {'type': 'physical_mode', 'id': 'physical_mode:LongDistanceTrain'},
    {'type': 'network', 'id': 'network:sncf'}],
   'stop_date_time': {'links': [],
    'arrival_date_time': '20200503T062900',
    'additional_informations': [],
    'departure_date_time': '20200503T062900',
    'base_arrival_date_time': '20200503T062900',
    'base_departure_date_time': '20200503T062900',
    'data_freshness': 'base_schedule'}},
  {'display_informations': {'direction': 'Grenoble (Grenoble)',
    'code': '',
    'network': 'SNCF',
    'links': [],
    'color': '000000',
    'name': 'Paris <> Grenoble',
    'physical_mode': 'Train grande vitesse',
    'headsign': '6905',
    'label': 'Paris <> Grenoble',
    'equipments': [],
    'text_color': 'FFFFFF',
    'commercial_mode': 'TGV',
    'description': ''},
   'stop_point': {'commercial_modes': [{'id': 'commercial_mode:INOUI',
      'name': 'TGV INOUI'},
     {'id': 'commercial_mode:tgv', 'name': 'TGV'}],
    'name': 'Paris-Gare-de-Lyon',
    'links': [],
    'physical_modes': [{'id': 'physical_mode:LongDistanceTrain',
      'name': 'Train grande vitesse'}],
    'coord': {'lat': '48.844922', 'lon': '2.373462'},
    'label': 'Paris-Gare-de-Lyon (Paris)',
    'equipments': [],
    'administrative_regions': [{'insee': '75056',
      'name': 'Paris',
      'level': 8,
      'coord': {'lat': '48.8566969', 'lon': '2.3514616'},
      'label': 'Paris (75000-75116)',
      'id': 'admin:fr:75056',
      'zip_code': '75000;75116'}],
    'fare_zone': {'name': '0'},
    'id': 'stop_point:OCE:SP:TGV-87686006',
    'stop_area': {'codes': [{'type': 'CR-CI-CH', 'value': '0087-686006-BV'},
      {'type': 'CR-CI-CH', 'value': '0087-686006-BV'},
      {'type': 'UIC8', 'value': '87686006'},
      {'type': 'external_code', 'value': 'OCE87686006'}],
     'name': 'Paris-Gare-de-Lyon',
     'links': [],
     'coord': {'lat': '48.844922', 'lon': '2.373462'},
     'label': 'Paris-Gare-de-Lyon (Paris)',
     'administrative_regions': [{'insee': '75056',
       'name': 'Paris',
       'level': 8,
       'coord': {'lat': '48.8566969', 'lon': '2.3514616'},
       'label': 'Paris (75000-75116)',
       'id': 'admin:fr:75056',
       'zip_code': '75000;75116'}],
     'timezone': 'Europe/Paris',
     'id': 'stop_area:OCE:SA:87686006'}},
   'route': {'direction': {'embedded_type': 'stop_area',
     'stop_area': {'codes': [{'type': 'CR-CI-CH', 'value': '0087-747006-BV'},
       {'type': 'CR-CI-CH', 'value': '0087-747006-BV'},
       {'type': 'UIC8', 'value': '87747006'},
       {'type': 'external_code', 'value': 'OCE87747006'}],
      'name': 'Grenoble',
      'links': [],
      'coord': {'lat': '45.191463', 'lon': '5.71453'},
      'label': 'Grenoble (Grenoble)',
      'timezone': 'Europe/Paris',
      'id': 'stop_area:OCE:SA:87747006'},
     'quality': 0,
     'name': 'Grenoble (Grenoble)',
     'id': 'stop_area:OCE:SA:87747006'},
    'name': 'Paris-Gare-de-Lyon vers Grenoble (TGV)',
    'links': [],
    'physical_modes': [{'id': 'physical_mode:LongDistanceTrain',
      'name': 'Train grande vitesse'}],
    'is_frequence': 'False',
    'geojson': {'type': 'MultiLineString', 'coordinates': []},
    'direction_type': 'forward',
    'line': {'code': '',
     'name': 'Paris <> Grenoble',
     'links': [],
     'color': '000000',
     'geojson': {'type': 'MultiLineString', 'coordinates': []},
     'text_color': 'FFFFFF',
     'codes': [],
     'closing_time': '002100',
     'opening_time': '051900',
     'id': 'line:OCE:146'},
    'id': 'route:OCE:146-TGV-87686006-87747006'},
   'links': [{'type': 'line', 'id': 'line:OCE:146'},
    {'type': 'vehicle_journey', 'id': 'vehicle_journey:OCE:SN006905F12012'},
    {'type': 'route', 'id': 'route:OCE:146-TGV-87686006-87747006'},
    {'type': 'commercial_mode', 'id': 'commercial_mode:tgv'},
    {'type': 'physical_mode', 'id': 'physical_mode:LongDistanceTrain'},
    {'type': 'network', 'id': 'network:sncf'}],
   'stop_date_time': {'links': [],
    'arrival_date_time': '20200503T073900',
    'additional_informations': [],
    'departure_date_time': '20200503T073900',
    'base_arrival_date_time': '20200503T073900',
    'base_departure_date_time': '20200503T073900',
    'data_freshness': 'base_schedule'}},
  {'display_informations': {'direction': 'Mulhouse (Mulhouse)',
    'code': '',
    'network': 'SNCF',
    'links': [],
    'color': '000000',
    'name': 'Paris <> Bourgogne Franche Comté <> Mulhouse',
    'physical_mode': 'Train grande vitesse',
    'headsign': '6703',
    'label': 'Paris <> Bourgogne Franche Comté <> Mulhouse',
    'equipments': [],
    'text_color': 'FFFFFF',
    'commercial_mode': 'TGV',
    'description': ''},
   'stop_point': {'commercial_modes': [{'id': 'commercial_mode:INOUI',
      'name': 'TGV INOUI'},
     {'id': 'commercial_mode:tgv', 'name': 'TGV'}],
    'name': 'Paris-Gare-de-Lyon',
    'links': [],
    'physical_modes': [{'id': 'physical_mode:LongDistanceTrain',
      'name': 'Train grande vitesse'}],
    'coord': {'lat': '48.844922', 'lon': '2.373462'},
    'label': 'Paris-Gare-de-Lyon (Paris)',
    'equipments': [],
    'administrative_regions': [{'insee': '75056',
      'name': 'Paris',
      'level': 8,
      'coord': {'lat': '48.8566969', 'lon': '2.3514616'},
      'label': 'Paris (75000-75116)',
      'id': 'admin:fr:75056',
      'zip_code': '75000;75116'}],
    'fare_zone': {'name': '0'},
    'id': 'stop_point:OCE:SP:TGV-87686006',
    'stop_area': {'codes': [{'type': 'CR-CI-CH', 'value': '0087-686006-BV'},
      {'type': 'CR-CI-CH', 'value': '0087-686006-BV'},
      {'type': 'UIC8', 'value': '87686006'},
      {'type': 'external_code', 'value': 'OCE87686006'}],
     'name': 'Paris-Gare-de-Lyon',
     'links': [],
     'coord': {'lat': '48.844922', 'lon': '2.373462'},
     'label': 'Paris-Gare-de-Lyon (Paris)',
     'administrative_regions': [{'insee': '75056',
       'name': 'Paris',
       'level': 8,
       'coord': {'lat': '48.8566969', 'lon': '2.3514616'},
       'label': 'Paris (75000-75116)',
       'id': 'admin:fr:75056',
       'zip_code': '75000;75116'}],
     'timezone': 'Europe/Paris',
     'id': 'stop_area:OCE:SA:87686006'}},
   'route': {'direction': {'embedded_type': 'stop_area',
     'stop_area': {'codes': [{'type': 'CR-CI-CH', 'value': '0087-182063-BV'},
       {'type': 'CR-CI-CH', 'value': '0087-182063-BV'},
       {'type': 'UIC8', 'value': '87182063'},
       {'type': 'external_code', 'value': 'OCE87182063'}],
      'name': 'Mulhouse',
      'links': [],
      'coord': {'lat': '47.741798', 'lon': '7.34285'},
      'label': 'Mulhouse (Mulhouse)',
      'timezone': 'Europe/Paris',
      'id': 'stop_area:OCE:SA:87182063'},
     'quality': 0,
     'name': 'Mulhouse (Mulhouse)',
     'id': 'stop_area:OCE:SA:87182063'},
    'name': 'Paris-Gare-de-Lyon vers Mulhouse (TGV)',
    'links': [],
    'physical_modes': [{'id': 'physical_mode:LongDistanceTrain',
      'name': 'Train grande vitesse'}],
    'is_frequence': 'False',
    'geojson': {'type': 'MultiLineString', 'coordinates': []},
    'direction_type': 'forward',
    'line': {'code': '',
     'name': 'Paris <> Bourgogne Franche Comté <> Mulhouse',
     'links': [],
     'color': '000000',
     'geojson': {'type': 'MultiLineString', 'coordinates': []},
     'text_color': 'FFFFFF',
     'codes': [],
     'closing_time': '235600',
     'opening_time': '053300',
     'id': 'line:OCE:139'},
    'id': 'route:OCE:139-TGV-87686006-87182063'},
   'links': [{'type': 'line', 'id': 'line:OCE:139'},
    {'type': 'vehicle_journey', 'id': 'vehicle_journey:OCE:SN006703F07007'},
    {'type': 'route', 'id': 'route:OCE:139-TGV-87686006-87182063'},
    {'type': 'commercial_mode', 'id': 'commercial_mode:tgv'},
    {'type': 'physical_mode', 'id': 'physical_mode:LongDistanceTrain'},
    {'type': 'network', 'id': 'network:sncf'}],
   'stop_date_time': {'links': [],
    'arrival_date_time': '20200503T091900',
    'additional_informations': [],
    'departure_date_time': '20200503T091900',
    'base_arrival_date_time': '20200503T091900',
    'base_departure_date_time': '20200503T091900',
    'data_freshness': 'base_schedule'}},
  {'display_informations': {'direction': 'Grenoble (Grenoble)',
    'code': '',
    'network': 'SNCF',
    'links': [],
    'color': '000000',
    'name': 'Paris <> Grenoble',
    'physical_mode': 'Train grande vitesse',
    'headsign': '6911',
    'label': 'Paris <> Grenoble',
    'equipments': [],
    'text_color': 'FFFFFF',
    'commercial_mode': 'TGV',
    'description': ''},
   'stop_point': {'commercial_modes': [{'id': 'commercial_mode:INOUI',
      'name': 'TGV INOUI'},
     {'id': 'commercial_mode:tgv', 'name': 'TGV'}],
    'name': 'Paris-Gare-de-Lyon',
    'links': [],
    'physical_modes': [{'id': 'physical_mode:LongDistanceTrain',
      'name': 'Train grande vitesse'}],
    'coord': {'lat': '48.844922', 'lon': '2.373462'},
    'label': 'Paris-Gare-de-Lyon (Paris)',
    'equipments': [],
    'administrative_regions': [{'insee': '75056',
      'name': 'Paris',
      'level': 8,
      'coord': {'lat': '48.8566969', 'lon': '2.3514616'},
      'label': 'Paris (75000-75116)',
      'id': 'admin:fr:75056',
      'zip_code': '75000;75116'}],
    'fare_zone': {'name': '0'},
    'id': 'stop_point:OCE:SP:TGV-87686006',
    'stop_area': {'codes': [{'type': 'CR-CI-CH', 'value': '0087-686006-BV'},
      {'type': 'CR-CI-CH', 'value': '0087-686006-BV'},
      {'type': 'UIC8', 'value': '87686006'},
      {'type': 'external_code', 'value': 'OCE87686006'}],
     'name': 'Paris-Gare-de-Lyon',
     'links': [],
     'coord': {'lat': '48.844922', 'lon': '2.373462'},
     'label': 'Paris-Gare-de-Lyon (Paris)',
     'administrative_regions': [{'insee': '75056',
       'name': 'Paris',
       'level': 8,
       'coord': {'lat': '48.8566969', 'lon': '2.3514616'},
       'label': 'Paris (75000-75116)',
       'id': 'admin:fr:75056',
       'zip_code': '75000;75116'}],
     'timezone': 'Europe/Paris',
     'id': 'stop_area:OCE:SA:87686006'}},
   'route': {'direction': {'embedded_type': 'stop_area',
     'stop_area': {'codes': [{'type': 'CR-CI-CH', 'value': '0087-747006-BV'},
       {'type': 'CR-CI-CH', 'value': '0087-747006-BV'},
       {'type': 'UIC8', 'value': '87747006'},
       {'type': 'external_code', 'value': 'OCE87747006'}],
      'name': 'Grenoble',
      'links': [],
      'coord': {'lat': '45.191463', 'lon': '5.71453'},
      'label': 'Grenoble (Grenoble)',
      'timezone': 'Europe/Paris',
      'id': 'stop_area:OCE:SA:87747006'},
     'quality': 0,
     'name': 'Grenoble (Grenoble)',
     'id': 'stop_area:OCE:SA:87747006'},
    'name': 'Paris-Gare-de-Lyon vers Grenoble (TGV)',
    'links': [],
    'physical_modes': [{'id': 'physical_mode:LongDistanceTrain',
      'name': 'Train grande vitesse'}],
    'is_frequence': 'False',
    'geojson': {'type': 'MultiLineString', 'coordinates': []},
    'direction_type': 'forward',
    'line': {'code': '',
     'name': 'Paris <> Grenoble',
     'links': [],
     'color': '000000',
     'geojson': {'type': 'MultiLineString', 'coordinates': []},
     'text_color': 'FFFFFF',
     'codes': [],
     'closing_time': '002100',
     'opening_time': '051900',
     'id': 'line:OCE:146'},
    'id': 'route:OCE:146-TGV-87686006-87747006'},
   'links': [{'type': 'line', 'id': 'line:OCE:146'},
    {'type': 'vehicle_journey', 'id': 'vehicle_journey:OCE:SN006911F06006'},
    {'type': 'route', 'id': 'route:OCE:146-TGV-87686006-87747006'},
    {'type': 'commercial_mode', 'id': 'commercial_mode:tgv'},
    {'type': 'physical_mode', 'id': 'physical_mode:LongDistanceTrain'},
    {'type': 'network', 'id': 'network:sncf'}],
   'stop_date_time': {'links': [],
    'arrival_date_time': '20200503T094300',
    'additional_informations': [],
    'departure_date_time': '20200503T094300',
    'base_arrival_date_time': '20200503T094300',
    'base_departure_date_time': '20200503T094300',
    'data_freshness': 'base_schedule'}},
  {'display_informations': {'direction': 'Annecy (Annecy)',
    'code': '',
    'network': 'SNCF',
    'links': [],
    'color': '000000',
    'name': 'Paris <> Chambéry / Annecy',
    'physical_mode': 'Train grande vitesse',
    'headsign': '6937',
    'label': 'Paris <> Chambéry / Annecy',
    'equipments': [],
    'text_color': 'FFFFFF',
    'commercial_mode': 'TGV',
    'description': ''},
   'stop_point': {'commercial_modes': [{'id': 'commercial_mode:INOUI',
      'name': 'TGV INOUI'},
     {'id': 'commercial_mode:tgv', 'name': 'TGV'}],
    'name': 'Paris-Gare-de-Lyon',
    'links': [],
    'physical_modes': [{'id': 'physical_mode:LongDistanceTrain',
      'name': 'Train grande vitesse'}],
    'coord': {'lat': '48.844922', 'lon': '2.373462'},
    'label': 'Paris-Gare-de-Lyon (Paris)',
    'equipments': [],
    'administrative_regions': [{'insee': '75056',
      'name': 'Paris',
      'level': 8,
      'coord': {'lat': '48.8566969', 'lon': '2.3514616'},
      'label': 'Paris (75000-75116)',
      'id': 'admin:fr:75056',
      'zip_code': '75000;75116'}],
    'fare_zone': {'name': '0'},
    'id': 'stop_point:OCE:SP:TGV-87686006',
    'stop_area': {'codes': [{'type': 'CR-CI-CH', 'value': '0087-686006-BV'},
      {'type': 'CR-CI-CH', 'value': '0087-686006-BV'},
      {'type': 'UIC8', 'value': '87686006'},
      {'type': 'external_code', 'value': 'OCE87686006'}],
     'name': 'Paris-Gare-de-Lyon',
     'links': [],
     'coord': {'lat': '48.844922', 'lon': '2.373462'},
     'label': 'Paris-Gare-de-Lyon (Paris)',
     'administrative_regions': [{'insee': '75056',
       'name': 'Paris',
       'level': 8,
       'coord': {'lat': '48.8566969', 'lon': '2.3514616'},
       'label': 'Paris (75000-75116)',
       'id': 'admin:fr:75056',
       'zip_code': '75000;75116'}],
     'timezone': 'Europe/Paris',
     'id': 'stop_area:OCE:SA:87686006'}},
   'route': {'direction': {'embedded_type': 'stop_area',
     'stop_area': {'codes': [{'type': 'CR-CI-CH', 'value': '0087-746008-BV'},
       {'type': 'CR-CI-CH', 'value': '0087-746008-BV'},
       {'type': 'UIC8', 'value': '87746008'},
       {'type': 'external_code', 'value': 'OCE87746008'}],
      'name': 'Annecy',
      'links': [],
      'coord': {'lat': '45.902059', 'lon': '6.121819'},
      'label': 'Annecy (Annecy)',
      'timezone': 'Europe/Paris',
      'id': 'stop_area:OCE:SA:87746008'},
     'quality': 0,
     'name': 'Annecy (Annecy)',
     'id': 'stop_area:OCE:SA:87746008'},
    'name': 'Paris-Gare-de-Lyon vers Annecy (TGV)',
    'links': [],
    'physical_modes': [{'id': 'physical_mode:LongDistanceTrain',
      'name': 'Train grande vitesse'}],
    'is_frequence': 'False',
    'geojson': {'type': 'MultiLineString', 'coordinates': []},
    'direction_type': 'forward',
    'line': {'code': '',
     'name': 'Paris <> Chambéry / Annecy',
     'links': [],
     'color': '000000',
     'geojson': {'type': 'MultiLineString', 'coordinates': []},
     'text_color': 'FFFFFF',
     'codes': [],
     'closing_time': '233800',
     'opening_time': '053200',
     'id': 'line:OCE:147'},
    'id': 'route:OCE:147-TGV-87686006-87746008'},
   'links': [{'type': 'line', 'id': 'line:OCE:147'},
    {'type': 'vehicle_journey', 'id': 'vehicle_journey:OCE:SN006937F07007'},
    {'type': 'route', 'id': 'route:OCE:147-TGV-87686006-87746008'},
    {'type': 'commercial_mode', 'id': 'commercial_mode:tgv'},
    {'type': 'physical_mode', 'id': 'physical_mode:LongDistanceTrain'},
    {'type': 'network', 'id': 'network:sncf'}],
   'stop_date_time': {'links': [],
    'arrival_date_time': '20200503T094700',
    'additional_informations': [],
    'departure_date_time': '20200503T094700',
    'base_arrival_date_time': '20200503T094700',
    'base_departure_date_time': '20200503T094700',
    'data_freshness': 'base_schedule'}},
  {'display_informations': {'direction': 'Milano-Porta-Garibaldi (Milano)',
    'code': '',
    'network': 'SNCF',
    'links': [],
    'color': '000000',
    'name': 'Paris<>Milano PG',
    'physical_mode': 'Train grande vitesse',
    'headsign': '9245',
    'label': 'Paris<>Milano PG',
    'equipments': [],
    'text_color': 'FFFFFF',
    'commercial_mode': 'TGV',
    'description': ''},
   'stop_point': {'commercial_modes': [{'id': 'commercial_mode:INOUI',
      'name': 'TGV INOUI'},
     {'id': 'commercial_mode:tgv', 'name': 'TGV'}],
    'name': 'Paris-Gare-de-Lyon',
    'links': [],
    'physical_modes': [{'id': 'physical_mode:LongDistanceTrain',
      'name': 'Train grande vitesse'}],
    'coord': {'lat': '48.844922', 'lon': '2.373462'},
    'label': 'Paris-Gare-de-Lyon (Paris)',
    'equipments': [],
    'administrative_regions': [{'insee': '75056',
      'name': 'Paris',
      'level': 8,
      'coord': {'lat': '48.8566969', 'lon': '2.3514616'},
      'label': 'Paris (75000-75116)',
      'id': 'admin:fr:75056',
      'zip_code': '75000;75116'}],
    'fare_zone': {'name': '0'},
    'id': 'stop_point:OCE:SP:TGV-87686006',
    'stop_area': {'codes': [{'type': 'CR-CI-CH', 'value': '0087-686006-BV'},
      {'type': 'CR-CI-CH', 'value': '0087-686006-BV'},
      {'type': 'UIC8', 'value': '87686006'},
      {'type': 'external_code', 'value': 'OCE87686006'}],
     'name': 'Paris-Gare-de-Lyon',
     'links': [],
     'coord': {'lat': '48.844922', 'lon': '2.373462'},
     'label': 'Paris-Gare-de-Lyon (Paris)',
     'administrative_regions': [{'insee': '75056',
       'name': 'Paris',
       'level': 8,
       'coord': {'lat': '48.8566969', 'lon': '2.3514616'},
       'label': 'Paris (75000-75116)',
       'id': 'admin:fr:75056',
       'zip_code': '75000;75116'}],
     'timezone': 'Europe/Paris',
     'id': 'stop_area:OCE:SA:87686006'}},
   'route': {'direction': {'embedded_type': 'stop_area',
     'stop_area': {'codes': [{'type': 'CR-CI-CH', 'value': '0083-016451-00'},
       {'type': 'CR-CI-CH', 'value': '0083-016451-00'},
       {'type': 'UIC8', 'value': '83016451'},
       {'type': 'external_code', 'value': 'OCE83016451'}],
      'name': 'Milano-Porta-Garibaldi',
      'links': [],
      'coord': {'lat': '45.48496', 'lon': '9.18759'},
      'label': 'Milano-Porta-Garibaldi (Milano)',
      'timezone': 'Europe/Paris',
      'id': 'stop_area:OCE:SA:83016451'},
     'quality': 0,
     'name': 'Milano-Porta-Garibaldi (Milano)',
     'id': 'stop_area:OCE:SA:83016451'},
    'name': 'Paris-Gare-de-Lyon vers Milano-Porta-Garibaldi (TGV)',
    'links': [],
    'physical_modes': [{'id': 'physical_mode:LongDistanceTrain',
      'name': 'Train grande vitesse'}],
    'is_frequence': 'False',
    'geojson': {'type': 'MultiLineString', 'coordinates': []},
    'direction_type': 'forward',
    'line': {'code': '',
     'name': 'Paris<>Milano PG',
     'links': [],
     'color': '000000',
     'geojson': {'type': 'MultiLineString', 'coordinates': []},
     'text_color': 'FFFFFF',
     'codes': [],
     'closing_time': '233000',
     'opening_time': '054500',
     'id': 'line:OCE:645702'},
    'id': 'route:OCE:645702-TGV-87686006-83016451'},
   'links': [{'type': 'line', 'id': 'line:OCE:645702'},
    {'type': 'vehicle_journey', 'id': 'vehicle_journey:OCE:SN009245F35035'},
    {'type': 'route', 'id': 'route:OCE:645702-TGV-87686006-83016451'},
    {'type': 'commercial_mode', 'id': 'commercial_mode:tgv'},
    {'type': 'physical_mode', 'id': 'physical_mode:LongDistanceTrain'},
    {'type': 'network', 'id': 'network:sncf'}],
   'stop_date_time': {'links': [],
    'arrival_date_time': '20200503T104300',
    'additional_informations': [],
    'departure_date_time': '20200503T104300',
    'base_arrival_date_time': '20200503T104300',
    'base_departure_date_time': '20200503T104300',
    'data_freshness': 'base_schedule'}},
  {'display_informations': {'direction': 'Mulhouse (Mulhouse)',
    'code': '',
    'network': 'SNCF',
    'links': [],
    'color': '000000',
    'name': 'Paris <> Bourgogne Franche Comté <> Mulhouse',
    'physical_mode': 'Train grande vitesse',
    'headsign': '6705',
    'label': 'Paris <> Bourgogne Franche Comté <> Mulhouse',
    'equipments': [],
    'text_color': 'FFFFFF',
    'commercial_mode': 'TGV',
    'description': ''},
   'stop_point': {'commercial_modes': [{'id': 'commercial_mode:INOUI',
      'name': 'TGV INOUI'},
     {'id': 'commercial_mode:tgv', 'name': 'TGV'}],
    'name': 'Paris-Gare-de-Lyon',
    'links': [],
    'physical_modes': [{'id': 'physical_mode:LongDistanceTrain',
      'name': 'Train grande vitesse'}],
    'coord': {'lat': '48.844922', 'lon': '2.373462'},
    'label': 'Paris-Gare-de-Lyon (Paris)',
    'equipments': [],
    'administrative_regions': [{'insee': '75056',
      'name': 'Paris',
      'level': 8,
      'coord': {'lat': '48.8566969', 'lon': '2.3514616'},
      'label': 'Paris (75000-75116)',
      'id': 'admin:fr:75056',
      'zip_code': '75000;75116'}],
    'fare_zone': {'name': '0'},
    'id': 'stop_point:OCE:SP:TGV-87686006',
    'stop_area': {'codes': [{'type': 'CR-CI-CH', 'value': '0087-686006-BV'},
      {'type': 'CR-CI-CH', 'value': '0087-686006-BV'},
      {'type': 'UIC8', 'value': '87686006'},
      {'type': 'external_code', 'value': 'OCE87686006'}],
     'name': 'Paris-Gare-de-Lyon',
     'links': [],
     'coord': {'lat': '48.844922', 'lon': '2.373462'},
     'label': 'Paris-Gare-de-Lyon (Paris)',
     'administrative_regions': [{'insee': '75056',
       'name': 'Paris',
       'level': 8,
       'coord': {'lat': '48.8566969', 'lon': '2.3514616'},
       'label': 'Paris (75000-75116)',
       'id': 'admin:fr:75056',
       'zip_code': '75000;75116'}],
     'timezone': 'Europe/Paris',
     'id': 'stop_area:OCE:SA:87686006'}},
   'route': {'direction': {'embedded_type': 'stop_area',
     'stop_area': {'codes': [{'type': 'CR-CI-CH', 'value': '0087-182063-BV'},
       {'type': 'CR-CI-CH', 'value': '0087-182063-BV'},
       {'type': 'UIC8', 'value': '87182063'},
       {'type': 'external_code', 'value': 'OCE87182063'}],
      'name': 'Mulhouse',
      'links': [],
      'coord': {'lat': '47.741798', 'lon': '7.34285'},
      'label': 'Mulhouse (Mulhouse)',
      'timezone': 'Europe/Paris',
      'id': 'stop_area:OCE:SA:87182063'},
     'quality': 0,
     'name': 'Mulhouse (Mulhouse)',
     'id': 'stop_area:OCE:SA:87182063'},
    'name': 'Paris-Gare-de-Lyon vers Mulhouse (TGV)',
    'links': [],
    'physical_modes': [{'id': 'physical_mode:LongDistanceTrain',
      'name': 'Train grande vitesse'}],
    'is_frequence': 'False',
    'geojson': {'type': 'MultiLineString', 'coordinates': []},
    'direction_type': 'forward',
    'line': {'code': '',
     'name': 'Paris <> Bourgogne Franche Comté <> Mulhouse',
     'links': [],
     'color': '000000',
     'geojson': {'type': 'MultiLineString', 'coordinates': []},
     'text_color': 'FFFFFF',
     'codes': [],
     'closing_time': '235600',
     'opening_time': '053300',
     'id': 'line:OCE:139'},
    'id': 'route:OCE:139-TGV-87686006-87182063'},
   'links': [{'type': 'line', 'id': 'line:OCE:139'},
    {'type': 'vehicle_journey', 'id': 'vehicle_journey:OCE:SN006705F09009'},
    {'type': 'route', 'id': 'route:OCE:139-TGV-87686006-87182063'},
    {'type': 'commercial_mode', 'id': 'commercial_mode:tgv'},
    {'type': 'physical_mode', 'id': 'physical_mode:LongDistanceTrain'},
    {'type': 'network', 'id': 'network:sncf'}],
   'stop_date_time': {'links': [],
    'arrival_date_time': '20200503T112100',
    'additional_informations': [],
    'departure_date_time': '20200503T112100',
    'base_arrival_date_time': '20200503T112100',
    'base_departure_date_time': '20200503T112100',
    'data_freshness': 'base_schedule'}},
  {'display_informations': {'direction': 'Grenoble (Grenoble)',
    'code': '',
    'network': 'SNCF',
    'links': [],
    'color': '000000',
    'name': 'Paris <> Grenoble',
    'physical_mode': 'Train grande vitesse',
    'headsign': '6917',
    'label': 'Paris <> Grenoble',
    'equipments': [],
    'text_color': 'FFFFFF',
    'commercial_mode': 'TGV',
    'description': ''},
   'stop_point': {'commercial_modes': [{'id': 'commercial_mode:INOUI',
      'name': 'TGV INOUI'},
     {'id': 'commercial_mode:tgv', 'name': 'TGV'}],
    'name': 'Paris-Gare-de-Lyon',
    'links': [],
    'physical_modes': [{'id': 'physical_mode:LongDistanceTrain',
      'name': 'Train grande vitesse'}],
    'coord': {'lat': '48.844922', 'lon': '2.373462'},
    'label': 'Paris-Gare-de-Lyon (Paris)',
    'equipments': [],
    'administrative_regions': [{'insee': '75056',
      'name': 'Paris',
      'level': 8,
      'coord': {'lat': '48.8566969', 'lon': '2.3514616'},
      'label': 'Paris (75000-75116)',
      'id': 'admin:fr:75056',
      'zip_code': '75000;75116'}],
    'fare_zone': {'name': '0'},
    'id': 'stop_point:OCE:SP:TGV-87686006',
    'stop_area': {'codes': [{'type': 'CR-CI-CH', 'value': '0087-686006-BV'},
      {'type': 'CR-CI-CH', 'value': '0087-686006-BV'},
      {'type': 'UIC8', 'value': '87686006'},
      {'type': 'external_code', 'value': 'OCE87686006'}],
     'name': 'Paris-Gare-de-Lyon',
     'links': [],
     'coord': {'lat': '48.844922', 'lon': '2.373462'},
     'label': 'Paris-Gare-de-Lyon (Paris)',
     'administrative_regions': [{'insee': '75056',
       'name': 'Paris',
       'level': 8,
       'coord': {'lat': '48.8566969', 'lon': '2.3514616'},
       'label': 'Paris (75000-75116)',
       'id': 'admin:fr:75056',
       'zip_code': '75000;75116'}],
     'timezone': 'Europe/Paris',
     'id': 'stop_area:OCE:SA:87686006'}},
   'route': {'direction': {'embedded_type': 'stop_area',
     'stop_area': {'codes': [{'type': 'CR-CI-CH', 'value': '0087-747006-BV'},
       {'type': 'CR-CI-CH', 'value': '0087-747006-BV'},
       {'type': 'UIC8', 'value': '87747006'},
       {'type': 'external_code', 'value': 'OCE87747006'}],
      'name': 'Grenoble',
      'links': [],
      'coord': {'lat': '45.191463', 'lon': '5.71453'},
      'label': 'Grenoble (Grenoble)',
      'timezone': 'Europe/Paris',
      'id': 'stop_area:OCE:SA:87747006'},
     'quality': 0,
     'name': 'Grenoble (Grenoble)',
     'id': 'stop_area:OCE:SA:87747006'},
    'name': 'Paris-Gare-de-Lyon vers Grenoble (TGV)',
    'links': [],
    'physical_modes': [{'id': 'physical_mode:LongDistanceTrain',
      'name': 'Train grande vitesse'}],
    'is_frequence': 'False',
    'geojson': {'type': 'MultiLineString', 'coordinates': []},
    'direction_type': 'forward',
    'line': {'code': '',
     'name': 'Paris <> Grenoble',
     'links': [],
     'color': '000000',
     'geojson': {'type': 'MultiLineString', 'coordinates': []},
     'text_color': 'FFFFFF',
     'codes': [],
     'closing_time': '002100',
     'opening_time': '051900',
     'id': 'line:OCE:146'},
    'id': 'route:OCE:146-TGV-87686006-87747006'},
   'links': [{'type': 'line', 'id': 'line:OCE:146'},
    {'type': 'vehicle_journey', 'id': 'vehicle_journey:OCE:SN006917F54054'},
    {'type': 'route', 'id': 'route:OCE:146-TGV-87686006-87747006'},
    {'type': 'commercial_mode', 'id': 'commercial_mode:tgv'},
    {'type': 'physical_mode', 'id': 'physical_mode:LongDistanceTrain'},
    {'type': 'network', 'id': 'network:sncf'}],
   'stop_date_time': {'links': [],
    'arrival_date_time': '20200503T114100',
    'additional_informations': [],
    'departure_date_time': '20200503T114100',
    'base_arrival_date_time': '20200503T114100',
    'base_departure_date_time': '20200503T114100',
    'data_freshness': 'base_schedule'}}],
 'context': {'timezone': 'Europe/Paris',
  'current_datetime': '20200418T165041'},
 'exceptions': []}
from jyquickhelper import JSONJS
JSONJS(departs_paris)
  • Quels sont les horaires de départ de ces trains ?

for i in range(len(departs_paris['departures'])) :
    print(departs_paris['departures'][i]['stop_date_time']['departure_date_time'])
20200502T191900
20200502T194300
20200503T062900
20200503T073900
20200503T091900
20200503T094300
20200503T094700
20200503T104300
20200503T112100
20200503T114100
  • Parmi ces trains, combien de trains ont pour destination finale Lyon et qui partent le 17 novembre ?

nombre_trains_pour_lyon = 0

for depart in departs_paris['departures'] :
    if "Lyon" in depart['display_informations']['direction'] :
        if (convertir_en_temps(depart['stop_date_time']['arrival_date_time']) >
                convertir_en_temps(date_depart) and
                convertir_en_temps(depart['stop_date_time']['arrival_date_time']) <
                datetime(2016,11,18,0,0,0)):
            nombre_trains_pour_lyon += 1
            print("le prochain départ pour Lyon sera le",
                  convertir_en_temps(depart['stop_date_time']['arrival_date_time']))

print("Il y a" , nombre_trains_pour_lyon, "train(s) pour Lyon dans les trains proposés",
      "par l'API qui partent encore le 17 novembre")
Il y a 0 train(s) pour Lyon dans les trains proposés par l'API qui partent encore le 17 novembre

C’est quand qu’on va où ?

En fait, vous n’êtes plus très sûr de vouloir aller à Lyon. Mais bon maintenant vous êtes Gare de Lyon et il est 18h00.

Question 3

  • Combien de tgv partent entre 18h00 et 20h00 ?

  • Lequel arrive le plus tôt à sa destination finale ?

# on crée deux fonctions :

def trouver_destination_tgv(origine, datetime) :
    '''Permet d avoir les 10 prochains départs d une gare donnée '''
    return requests.get('https://api.sncf.com/v1/coverage/sncf/stop_points/{}/' \
                        'departures?from_datetime={}'.format(origine, datetime) ,
                        auth=(token_auth, '')).json()

def trouver_trajet_dispo_max_heure(gare_depart, date_heure_depart, date_heure_max) :
    '''Permet d avoir toutes les informations sur des trajets partant
    d une gare entre une date X et une date Y'''

    destinations = []

    # on interroge l'API tant qu'il renvoie des information
    # sur les trains partant de Gare de lyon

    while convertir_en_temps(date_heure_depart) < convertir_en_temps(date_heure_max) :
    # on prend toutes les destinations qui partent à partir d'une certaine heure
        destinations = destinations + trouver_destination_tgv(
            gare_depart, date_heure_depart)['departures']

        nombre_resultats = trouver_destination_tgv(
            gare_depart, date_heure_depart)['pagination']['items_on_page']

        if nombre_resultats <= 0:
            break

        # on trouve l'heure max de la première série de 10 solutions que l'application renvoie
        # on remplace l'heure qu'on cherche par celle là
        resultats = trouver_destination_tgv(gare_depart, date_heure_depart)
        date_heure_depart = resultats['departures'][nombre_resultats-1][
            'stop_date_time']['departure_date_time']

    return destinations
# on trouve l'ensemble des trajets dont le départ est compris entre deux horaires
# informations sur le trajet qu'on choisit dans le futur
# l'API ne retourne pas de résultatq très loin dans le passé
now = datetime.now()
if now.hour < 6:
    # pas trop tôt
    now += timedelta(hours=4)
dt = now + timedelta(14)  # dans deux semaines

date_heure = convertir_en_chaine(dt)
max_date_heure = convertir_en_chaine(dt + timedelta(hours=24))
print("entre", date_heure, "et", max_date_heure)

gare_initiale = 'stop_point:OCE:SP:TGV-87686006'

# on demande à avoir tous les trajets partant de gare de lyon
# entre deux heures précises

destinations_depuis_paris_max_20h = trouver_trajet_dispo_max_heure(
    gare_initiale, date_heure, max_date_heure)

# on veut supprimer ceux pour lesquels le départ est après 20h00

dictionnaire_destinations = {}

i = 0

for depart in destinations_depuis_paris_max_20h :
    print(depart['display_informations']['direction'],
          depart['stop_date_time']['departure_date_time'])
    if (convertir_en_temps(depart['stop_date_time']['departure_date_time']) <
            convertir_en_temps(max_date_heure)):
        i += 1
        dictionnaire_destinations[i] = depart

print("Je peux prendre", len(dictionnaire_destinations.keys()),
      "trains qui partent entre 18h et 20h de Gare de Lyon le 17 novembre 2016")
entre 20200502T165041 et 20200503T165041
Mulhouse (Mulhouse) 20200502T191900
Grenoble (Grenoble) 20200502T194300
Milano-Porta-Garibaldi (Milano) 20200503T062900
Grenoble (Grenoble) 20200503T073900
Mulhouse (Mulhouse) 20200503T091900
Grenoble (Grenoble) 20200503T094300
Annecy (Annecy) 20200503T094700
Milano-Porta-Garibaldi (Milano) 20200503T104300
Mulhouse (Mulhouse) 20200503T112100
Grenoble (Grenoble) 20200503T114100
Grenoble (Grenoble) 20200503T114100
Annecy (Annecy) 20200503T124500
Milano-Porta-Garibaldi (Milano) 20200503T144200
Grenoble (Grenoble) 20200503T144200
Besançon-Viotte (Besançon) 20200503T145000
Annecy (Annecy) 20200503T164400
Besançon-Viotte (Besançon) 20200503T164800
Mulhouse (Mulhouse) 20200503T171700
Grenoble (Grenoble) 20200503T174100
Annecy (Annecy) 20200503T174600
Je peux prendre 17 trains qui partent entre 18h et 20h de Gare de Lyon le 17 novembre 2016
# on cherche celui qui arrive le plus tôt à sa destination

def trouver_info_trajet(dep, arr, heure) :
    res = requests.get('https://api.sncf.com/v1/coverage/sncf/journeys?from={}&to={}&datetime={}'.format(dep, arr, heure), \
                         auth=(token_auth, '')).json()
    if 'journeys' not in res:
        if 'error' in res and "no solution" in res["error"]['message']:
            print("Pas de solution pour '{0} --> '{1}' h: {2}.".format(dep, arr, heure))
            return None
    return res['journeys'][0]

# on initiale l'heure à la fin de la journée : on veut réduire cette variable au maximum
# on veut 6h après le départ
heure_minimale = dt + timedelta(hours=8)
destination_la_plus_rapide = None
print("heure_minimale", heure_minimale, " len ", len(dictionnaire_destinations))

# parmi toutes les destinations possibles, on recherche le train qui arrive le plus tôt à sa destination finale
for code, valeurs in dictionnaire_destinations.items() :
    ''' on prend le code de la gare'''
    code_destination = dictionnaire_destinations[code]['route']['direction']['id']
    ''' on regarde à quelle heure arrive le train'''
    trajet = trouver_info_trajet('stop_area:OCE:SA:87686006',code_destination,
                                 dictionnaire_destinations[code]['stop_date_time']['arrival_date_time'])
    if trajet is None:
        continue
    if heure_minimale > convertir_en_temps(trajet['arrival_date_time']) :
        heure_minimale = convertir_en_temps(trajet['arrival_date_time'])
        destination_la_plus_rapide = dictionnaire_destinations[code]
heure_minimale 2020-05-03 00:50:41.995641  len  17
if destination_la_plus_rapide is not None:
    print(destination_la_plus_rapide['display_informations']['direction'], heure_minimale)
else:
    print("pas de résultat")
Mulhouse (Mulhouse) 2020-05-02 22:17:00

Et les correspondances ?

Question 4

  • On va essayer de voir jusqu’où on peut aller, en prenant des trains au départ de la Gare de Lyon :

    • Quelles sont toutes les gares atteignables en partant le 17 novembre, sans faire de changement et sans partir après minuit ?

    • Si on prend un de ces trains, jusqu’où peut-on aller, avec une correspondance, sans partir après 8h le lendemain matin ?

# on va trouver toutes les gares qui sont sur les trajets des
# trains retenus donc atteignables sans correspondance

def trouver_toutes_les_gares_du_trajet(gare_depart, gare_arrivee_finale, horaire_depart) :
    return requests.get('https://api.sncf.com/v1/coverage/sncf/journeys?from={}&to={}' \
                        '&datetime={}'.format(gare_depart,gare_arrivee_finale,horaire_depart), \
                        auth=(token_auth, '')).json()
# Exemple pour la première gare de la liste

if len(dictionnaire_destinations) > 1:
    gare_depart = dictionnaire_destinations[1]['stop_point']['id']
    gare_arrivee = dictionnaire_destinations[1]['route']['direction']['id']
    horaire_train = dictionnaire_destinations[1]['stop_date_time']['arrival_date_time']

    trajet_recherche = trouver_toutes_les_gares_du_trajet(gare_depart,gare_arrivee,horaire_train)
    session = trajet_recherche['journeys'][0]['sections'][0]
    if "stop_date_times" in session:
        for i in session['stop_date_times']:
            print(i['stop_point']['name'])
Paris-Gare-de-Lyon
Dijon-Ville
Besançon-Franche-Comté
Belfort-Montbéliard-TGV
Mulhouse
# on fait la liste des gares où on
# peut aller sans correspondance

liste_gares_direct = []

for x in dictionnaire_destinations.keys():
    # on prend les deux gares départ + finale
    gare_depart = dictionnaire_destinations[x]['stop_point']['id']
    gare_arrivee = dictionnaire_destinations[x]['route']['direction']['id']
    horaire_train = dictionnaire_destinations[x]['stop_date_time']['arrival_date_time']

    # on appelle la fonction définie précédemment
    trajet_recherche = trouver_toutes_les_gares_du_trajet(gare_depart,gare_arrivee,horaire_train)
    if 'error' in trajet_recherche:
        continue
    session = trajet_recherche['journeys'][0]['sections'][0]
    if "stop_date_times" in session:
        for i in session['stop_date_times']:
            print(i['stop_point']['name'], i['arrival_date_time'])
            liste_gares_direct.append(i['stop_point']['name'])
        print("-------------")

#### là on a la liste des gares atteignables sans correspondance
liste_gares_direct = set(liste_gares_direct)
Paris-Gare-de-Lyon 20200502T191900
Dijon-Ville 20200502T205700
Besançon-Franche-Comté 20200502T212800
Belfort-Montbéliard-TGV 20200502T215200
Mulhouse 20200502T221700
-------------
Paris-Gare-de-Lyon 20200502T194300
Lyon-Saint-Exupery-Tgv 20200502T213600
Grenoble 20200502T224200
-------------
Paris-Gare-de-Lyon 20200503T062900
Lyon-Part-Dieu 20200503T083100
Chambéry-Chal.-les-Eaux 20200503T094000
Modane 20200503T104700
Bardonecchia-Stazione 20200503T111300
Oulx-Cesana-Clav-Sestri 20200503T112300
Torino PS (Turin) 20200503T122400
Milano-Porta-Garibaldi 20200503T135000
-------------
Paris-Gare-de-Lyon 20200503T073900
Lyon-Saint-Exupery-Tgv 20200503T093400
Grenoble 20200503T104200
-------------
Paris-Gare-de-Lyon 20200503T091900
Montbard 20200503T102500
Dijon-Ville 20200503T110100
Besançon-Franche-Comté 20200503T113100
Belfort-Montbéliard-TGV 20200503T115500
Mulhouse 20200503T122000
-------------
Paris-Gare-de-Lyon 20200503T094300
Lyon-Saint-Exupery-Tgv 20200503T113500
Grenoble 20200503T124200
-------------
Paris-Gare-de-Lyon 20200503T094700
Mâcon-Loché-TGV 20200503T112400
Aix-les-Bains-le-Revard 20200503T125100
Annecy 20200503T132900
-------------
Paris-Gare-de-Lyon 20200503T104300
Lyon-Saint-Exupery-Tgv 20200503T123700
Chambéry-Chal.-les-Eaux 20200503T134000
Modane 20200503T145000
Bardonecchia-Stazione 20200503T151300
Oulx-Cesana-Clav-Sestri 20200503T152300
Torino PS (Turin) 20200503T161800
Vercelli 20200503T165500
Novara 20200503T171100
Milano-Porta-Garibaldi 20200503T175000
-------------
Paris-Gare-de-Lyon 20200503T112100
Dijon-Ville 20200503T125700
Besançon-Franche-Comté 20200503T132800
Belfort-Montbéliard-TGV 20200503T135200
Mulhouse 20200503T141700
-------------
Paris-Gare-de-Lyon 20200503T114100
Lyon-Saint-Exupery-Tgv 20200503T133400
Grenoble 20200503T144200
-------------
Paris-Gare-de-Lyon 20200503T114100
Lyon-Saint-Exupery-Tgv 20200503T133400
Grenoble 20200503T144200
-------------
Paris-Gare-de-Lyon 20200503T124500
Mâcon-Loché-TGV 20200503T142200
Chambéry-Chal.-les-Eaux 20200503T153900
Aix-les-Bains-le-Revard 20200503T155900
Annecy 20200503T162900
-------------
Paris-Gare-de-Lyon 20200503T144200
Lyon-Saint-Exupery-Tgv 20200503T163500
Chambéry-Chal.-les-Eaux 20200503T173700
St-Jean-de-Maurienne-A. 20200503T182200
Modane 20200503T184700
Bardonecchia-Stazione 20200503T191300
Oulx-Cesana-Clav-Sestri 20200503T192300
Torino PS (Turin) 20200503T201800
Milano-Porta-Garibaldi 20200503T215000
-------------
Paris-Gare-de-Lyon 20200503T144200
Lyon-Saint-Exupery-Tgv 20200503T163500
Grenoble 20200503T174500
-------------
Paris-Gare-de-Lyon 20200503T145000
Montbard 20200503T155500
Dijon-Ville 20200503T163200
Besançon-Franche-Comté 20200503T170500
Besançon-Viotte 20200503T172000
-------------
Paris-Gare-de-Lyon 20200503T164400
Mâcon-Loché-TGV 20200503T182200
Chambéry-Chal.-les-Eaux 20200503T194000
Aix-les-Bains-le-Revard 20200503T195900
Annecy 20200503T202800
-------------
Paris-Gare-de-Lyon 20200503T164800
Montbard 20200503T175500
Dijon-Ville 20200503T183200
Dole-Ville 20200503T185800
Besançon-Viotte 20200503T192400
-------------

Exemple : trouver toutes les correspondances possibles depuis le trajet entre les gares de Paris et de Perpignan

# pour le premier trajet gare de la liste trouvée à l'étape précédente
# on va chercher toutes les connexions des gares possibles, entre le moment de l'arrivée
# et 8 heures le lendemain matin

if len(dictionnaire_destinations) > 1:
    gare_depart = dictionnaire_destinations[1]['stop_point']['id']
    gare_arrivee = dictionnaire_destinations[1]['route']['direction']['id']
    horaire_train = dictionnaire_destinations[1]['stop_date_time']['arrival_date_time']

    horaire_max = convertir_en_chaine(dt + timedelta(hours=8))
    print("horaire_max", horaire_max)
else:
    horaire_train = None
horaire_max 20200503T005041
# en partant de gare de lyon en direction de Perpignan

if horaire_train is not None:
    trajet_recherche = trouver_toutes_les_gares_du_trajet(gare_depart,gare_arrivee,horaire_train)

dictionnaire_correspondances = {}

if (horaire_train is not None and
        'stop_date_times' in trajet_recherche['journeys'][0]['sections'][0]):
    for i in trajet_recherche['journeys'][0]['sections'][0]['stop_date_times']:

        #print("la gare où on est descendu depuis Paris", i['stop_point']['name'])

        if i['stop_point']['id'] == "stop_point:OCE:SP:TGV-87686006" :
            #print("on ne prend pas la gare de Lyon - ce n'est pas une gare du trajet")
            pass

        else :
            # on va appliquer à nouveau la fonction des trajets disponibles mais pour l'ensemble des gares
            gare_dep_connexion = i['stop_point']['id']
            nom_gare_dep = i['stop_point']['name']
            heure_dep_connexion = i['arrival_date_time']

            trajet_recherche_connexion = trouver_trajet_dispo_max_heure(gare_dep_connexion, heure_dep_connexion, horaire_max)

            test_as_connexion_on_time = True

            # pour chaque trajet possible depuis la gare où on est arrivé depuis paris, on va vérifier qu'on part bien
            # avant 8h le lendemain
            autre_gare = None
            for vers_autre_gare in trajet_recherche_connexion :
                heure_depart_depuis_autre_gare = vers_autre_gare['stop_date_time']['departure_date_time']
                destination_trajet = vers_autre_gare['display_informations']['direction']

                if convertir_en_temps(heure_depart_depuis_autre_gare) < convertir_en_temps(horaire_max) :
                    dictionnaire_correspondances[(nom_gare_dep,heure_depart_depuis_autre_gare)] = destination_trajet
                    test_as_connexion_on_time = False
                    # print(nom_gare_dep,heure_depart_depuis_autre_gare, "gare finale du trajet", destination_trajet)
                    autre_gare = vers_autre_gare

            if autre_gare and test_as_connexion_on_time:
                dictionnaire_correspondances[(nom_gare_dep,autre_gare['stop_date_time']['departure_date_time'])] = ""
else:
    print("impossible de trouver 'stop_date_times'")

# on garde toutes les gares où on peut aller depuis une des gares de correspondance, avec un départ avant 8H
dictionnaire_correspondances
{('Dijon-Ville', '20200502T210100'): 'Mulhouse (Mulhouse)',
 ('Besançon-Franche-Comté', '20200502T213100'): 'Mulhouse (Mulhouse)',
 ('Belfort-Montbéliard-TGV', '20200502T215500'): 'Mulhouse (Mulhouse)'}
# Pour les trajets qui partent avant 8h des gares, on va chercher toutes les gares qui sont sur le trajet
gares_avec_connexion = []
for k,v in dictionnaire_correspondances.items() :
    if len(v) == 0 :
        pass
    else :
        if k[0] not in dict_nom_gare_code:
            print("'{0}' pas trouvé dans {1}".format(k[0], ", ".join(
                sorted(_ for _ in dict_nom_gare_code if isinstance(_, str) and _[:4] == k[0][:4]))))
            continue
        if v not in dict_label_gare_code:
            print("'{0}' pas trouvé dans {1}".format(v, ", ".join(
                sorted(_ for _ in dict_label_gare_code if isinstance(_, str) and _[:4] == v[:4]))))
            continue

        dep = dict_nom_gare_code[k[0]]
        arr = dict_label_gare_code[v]

        gares_entre_dep_arr = trouver_toutes_les_gares_du_trajet(dep, arr,k[1])
        for gare in gares_entre_dep_arr['journeys'][0]['sections'][1]['stop_date_times']:
            #print("gare depart:", k[0], gare['stop_point']['name'])
            gares_avec_connexion.append(gare['stop_point']['name'])

# la liste des gares atteignables avec 1 correspondance
gares_avec_connexion = set(gares_avec_connexion)
'Besançon-Franche-Comté' pas trouvé dans Besançon-Franche-Comté, Besançon-Mouillère, Besançon-Viotte
'Belfort-Montbéliard-TGV' pas trouvé dans Belfort-Montbéliard-TGV, Belfort-Ville
print(gares_avec_connexion)
{'Belfort-Montbéliard-TGV', 'Mulhouse', 'Besançon-Franche-Comté', 'Dijon-Ville'}
# on crée la liste des gares atteignables seulement avec une correspondance (pas directement atteignable)
gares_atteintes_avec_connexion = [a for a in gares_avec_connexion if (a not in liste_gares_direct)]
print(gares_atteintes_avec_connexion)
[]
Exemple : trouver toutes les correspondances possibles depuis les trains qu’on prend de la Gare de Lyon

Maintenant qu’on a fait un exemple, on le fait pour tous les trajets qui partent de la Gare de Lyon

!!! Attention cette celulle prend du temps (beaucoup beaucoup de temps)
gares_avec_connexion = []

for gare_initiale in dictionnaire_destinations:
    # pour le premier trajet gare de la liste trouvée à l'étape précédente
    # on va chercher toutes les connexions des gares possibles
    print(gare_initiale, "/", len(dictionnaire_destinations))

    gare_depart = dictionnaire_destinations[gare_initiale]['stop_point']['id']
    gare_arrivee = dictionnaire_destinations[gare_initiale]['route']['direction']['id']
    horaire_train = dictionnaire_destinations[gare_initiale]['stop_date_time']['arrival_date_time']

    # Pour les trajets qui partent avant 8h des gares, on va chercher toutes les gares qui sont sur le trajet

    trajet_recherche = trouver_toutes_les_gares_du_trajet(gare_depart, gare_arrivee, horaire_train)

    dictionnaire_correspondances = {}
    if 'journeys' not in trajet_recherche:
        print("Pas de trajet entre '{0}' et '{1}' h={2}.".format(gare_depart, gare_arrivee, horaire_train))
        continue
    session = trajet_recherche['journeys'][0]['sections'][0]

    if "stop_date_times" in session:
        for i in session['stop_date_times']:

            if i['stop_point']['id'] == "stop_point:OCE:SP:TGV-87686006" :
                #print("on ne prend pas la gare de Lyon - ce n'est pas une gare du trajet")
                pass

            else :
                # on va appliquer à nouveau la fonction des trajets disponibles mais pour l'ensemble des gares
                gare_dep_connexion = i['stop_point']['id']
                nom_gare_dep = i['stop_point']['name']
                heure_dep_connexion = i['arrival_date_time']

                trajet_recherche_connexion = trouver_trajet_dispo_max_heure(gare_dep_connexion, heure_dep_connexion, horaire_max)

                test_as_connexion_on_time = True

                # pour chaque trajet possible depuis la gare où on est arrivé depuis paris, on va vérifier qu'on part bien
                # avant 8h le lendemain
                for vers_autre_gare in trajet_recherche_connexion :
                    heure_depart_depuis_autre_gare = vers_autre_gare['stop_date_time']['departure_date_time']
                    destination_trajet = vers_autre_gare['display_informations']['direction']

                    if convertir_en_temps(heure_depart_depuis_autre_gare) < convertir_en_temps(horaire_max) :
                        dictionnaire_correspondances[(nom_gare_dep,heure_depart_depuis_autre_gare)] = destination_trajet
                        test_as_connexion_on_time = False

                if test_as_connexion_on_time == True :
                    dictionnaire_correspondances[(nom_gare_dep,vers_autre_gare['stop_date_time']['departure_date_time'])] = ""

    # on garde toutes les gares où on peut aller depuis une des gares de correspondance, avec un départ avant 8H

    for k,v in dictionnaire_correspondances.items() :
        if len(v) == 0:
            continue
        if k[0] not in dict_nom_gare_code:
            print("'{0}' pas trouvé dans {1}".format(k[0], ", ".join(
                sorted(_ for _ in dict_nom_gare_code if isinstance(_, str) and _[:4] == k[0][:4]))))
            continue
        if v not in dict_label_gare_code:
            print("'{0}' pas trouvé dans {1}".format(v, ", ".join(
                sorted(_ for _ in dict_label_gare_code if isinstance(_, str) and _[:4] == v[:4]))))
            continue
        dep = dict_nom_gare_code[k[0]]
        arr = dict_label_gare_code[v]
        gares_entre_dep_arr = trouver_toutes_les_gares_du_trajet(dep, arr, k[1])
        if 'journeys' not in gares_entre_dep_arr:
            print("Pas de trajet entre '{0}' et '{1}'.".format(k[0], v))
            continue
        session = gares_entre_dep_arr['journeys'][0]['sections'][1]
        if "stop_date_times" in session:
            for gare in session['stop_date_times'] :
                gares_avec_connexion.append(gare['stop_point']['name'])
1 / 17
'Besançon-Franche-Comté' pas trouvé dans Besançon-Franche-Comté, Besançon-Mouillère, Besançon-Viotte
'Belfort-Montbéliard-TGV' pas trouvé dans Belfort-Montbéliard-TGV, Belfort-Ville
2 / 17
3 / 17
4 / 17
5 / 17
6 / 17
7 / 17
8 / 17
9 / 17
10 / 17
11 / 17
12 / 17
13 / 17
14 / 17
15 / 17
16 / 17
17 / 17
# la liste des gares atteignables avec 1 correspondance
gares_avec_connexion = set(gares_avec_connexion)
gares_connexion = [a for a in gares_avec_connexion if a not in liste_gares_direct]
print(gares_connexion)
[]

Question 5

  • Représenter toutes les gares atteignables avec un graphique de type scatter. Distinguer les gares atteintes en un seul trajet et celles atteintes avec une correspondance.

######### Type de chaque gare pour le graphique
dict_type_gares = {}
for a in liste_gares_direct :
    dict_type_gares[a] = "direct"
for a in gares_connexion :
    dict_type_gares[a] = "correspondance"
dict_type_gares['Paris-Gare-de-Lyon'] = 'depart'
dict_type_gares
{'Modane': 'direct',
 'Bardonecchia-Stazione': 'direct',
 'Chambéry-Chal.-les-Eaux': 'direct',
 'Besançon-Viotte': 'direct',
 'Dijon-Ville': 'direct',
 'Novara': 'direct',
 'Torino PS (Turin)': 'direct',
 'Mâcon-Loché-TGV': 'direct',
 'Lyon-Part-Dieu': 'direct',
 'Annecy': 'direct',
 'Belfort-Montbéliard-TGV': 'direct',
 'Aix-les-Bains-le-Revard': 'direct',
 'Vercelli': 'direct',
 'St-Jean-de-Maurienne-A.': 'direct',
 'Lyon-Saint-Exupery-Tgv': 'direct',
 'Grenoble': 'direct',
 'Montbard': 'direct',
 'Besançon-Franche-Comté': 'direct',
 'Oulx-Cesana-Clav-Sestri': 'direct',
 'Mulhouse': 'direct',
 'Paris-Gare-de-Lyon': 'depart',
 'Milano-Porta-Garibaldi': 'direct',
 'Dole-Ville': 'direct'}

On représente tout ça sur un graphique

# on va les représenter grâce à la base des latitude / longitude

import pandas as pd
import matplotlib.pyplot as plt
import matplotlib as mpl
from matplotlib.lines import Line2D

mpl.rcParams['axes.facecolor'] = "whitesmoke"

palette = plt.cm.spring

liste_couleurs = [palette(0), palette(0.5), palette(0.8)]

data_all = pd.read_csv("./ensemble_gares.csv", encoding = "ISO-8859-1")

connexions = []
lat = []
lon = []
labels = []

dict_lat = data_all.set_index('name')['lat'].to_dict()
dict_lon = data_all.set_index('name')['lon'].to_dict()
#dict_lab = data_all.set_index('name')['name'].str.replace("gare de","").to_dict()


for gare in dict_type_gares:
    if gare not in dict_lat:
        print("'{0}' pas trouvé dans dict_lat (problème d'accents?)".format(gare))
        continue
    if gare not in dict_lon:
        print("'{0}' pas trouvé dans dict_lon (problème d'accents?)".format(gare))
        continue
    lat.append(dict_lat[gare])
    lon.append(dict_lon[gare])
    labels.append(gare)
'Chambéry-Chal.-les-Eaux' pas trouvé dans dict_lat (problème d'accents?)
'Besançon-Viotte' pas trouvé dans dict_lat (problème d'accents?)
'Mâcon-Loché-TGV' pas trouvé dans dict_lat (problème d'accents?)
'Belfort-Montbéliard-TGV' pas trouvé dans dict_lat (problème d'accents?)
'Besançon-Franche-Comté' pas trouvé dans dict_lat (problème d'accents?)
%matplotlib inline
### La carte
###################################################################################################

def liste_unique(liste) :
    unicite = []
    for x in liste :
        if x in unicite :
            pass
        else :
            unicite.append(x)
    return unicite

lab_un = liste_unique(labels)
lat_un = liste_unique(lat)
lon_un = liste_unique(lon)

fig = plt.figure(figsize=(12,10))

for label, x, y in set(zip(labels, lon, lat)) :
    if dict_type_gares[label] == "direct" :
        plt.annotate(label, xy = (x - 0.05, y - 0.05), horizontalalignment = 'right', size = 13)
    else :
        plt.annotate(label, xy = (x + 0.05, y + 0.05), horizontalalignment = 'left', size = 13)

colors = []
for x in lab_un :
    if dict_type_gares[x] == "depart" :
        colors.append(liste_couleurs[0])
    if dict_type_gares[x] == "direct" :
        colors.append(liste_couleurs[1])
    if dict_type_gares[x] == "correspondance" :
        colors.append(liste_couleurs[2])


plt.scatter(x = lon_un , y = lat_un, marker = "o", c = colors, s = 100, alpha = 0.5)

#### Legende

circ1 = Line2D([0], [0], linestyle="none", marker="o", alpha=0.5, markersize=10, markerfacecolor = liste_couleurs[0])
circ2 = Line2D([0], [0], linestyle="none", marker="o", alpha=0.5, markersize=10, markerfacecolor = liste_couleurs[1])
circ3 = Line2D([0], [0], linestyle="none", marker="o", alpha=0.5, markersize=10, markerfacecolor = liste_couleurs[2])

legende = plt.legend((circ1, circ2, circ3), ("Gare de départ", "Direct depuis Gare de Lyon le soir du 17 novembre",
                                             "Avec une correspondance depuis une gare directe"), numpoints=1, loc="best")

legende.get_frame().set_facecolor('white')

plt.title("Gares atteignables avant minuit depuis la Gare de Lyon", size = 20);
../_images/TD2A_eco_API_SNCF_corrige_59_0.png