aboutsummaryrefslogtreecommitdiff
path: root/pkg
diff options
context:
space:
mode:
authorJulien Dessaux2021-04-05 17:52:31 +0200
committerJulien Dessaux2021-04-05 17:52:31 +0200
commit1ffc9c42054e208a01d3e70e6b6f3e1781e798f8 (patch)
tree721f202dcf46fb5e9c181013237e4da9d27f796a /pkg
parentReworked error handling for better and simpler tests (diff)
downloadtrains-1ffc9c42054e208a01d3e70e6b6f3e1781e798f8.tar.gz
trains-1ffc9c42054e208a01d3e70e6b6f3e1781e798f8.tar.bz2
trains-1ffc9c42054e208a01d3e70e6b6f3e1781e798f8.zip
Moved code around to conform best practices
Diffstat (limited to 'pkg')
-rw-r--r--pkg/config/config.go55
-rw-r--r--pkg/config/config_test.go54
-rw-r--r--pkg/config/error.go92
-rw-r--r--pkg/config/error_test.go20
-rw-r--r--pkg/config/test_data/invalid.yaml1
-rw-r--r--pkg/config/test_data/invalid_address.yaml3
-rw-r--r--pkg/config/test_data/invalid_address_unresolvable.yaml3
-rw-r--r--pkg/config/test_data/invalid_port.yaml3
-rw-r--r--pkg/config/test_data/invalid_token.yaml3
-rw-r--r--pkg/config/test_data/minimal.yaml3
-rw-r--r--pkg/config/test_data/minimal_with_hostname.yaml3
-rw-r--r--pkg/navitia_api_client/client.go31
-rw-r--r--pkg/navitia_api_client/client_test.go38
-rw-r--r--pkg/navitia_api_client/departures.go73
-rw-r--r--pkg/navitia_api_client/departures_test.go57
-rw-r--r--pkg/navitia_api_client/test_data/invalid.json1
-rw-r--r--pkg/navitia_api_client/test_data/normal-crepieux.json2135
17 files changed, 2575 insertions, 0 deletions
diff --git a/pkg/config/config.go b/pkg/config/config.go
new file mode 100644
index 0000000..f97467a
--- /dev/null
+++ b/pkg/config/config.go
@@ -0,0 +1,55 @@
+package config
+
+import (
+ "net"
+ "os"
+ "regexp"
+
+ "gopkg.in/yaml.v3"
+)
+
+var validToken = regexp.MustCompile(`^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$`)
+
+type Config struct {
+ // Address is the hostname or ip the web server will listen to
+ Address string `yaml:"address",default:"127.0.0.1"`
+ Port string `yaml:"port",default:"8080"`
+ // Token is the sncf api token
+ Token string `yaml:"token"`
+}
+
+func (c *Config) validate() error {
+ // address
+ if ip := net.ParseIP(c.Address); ip == nil {
+ if _, err := net.LookupIP(c.Address); err != nil {
+ return newInvalidAddressError(c.Address, err)
+ }
+ }
+ // port
+ if _, err := net.LookupPort("tcp", c.Port); err != nil {
+ return newInvalidPortError(c.Port, err)
+ }
+ // token
+ if ok := validToken.MatchString(c.Token); !ok {
+ return newInvalidTokenError(c.Token)
+ }
+ return nil
+}
+
+// LoadFile loads the c from a given file
+func LoadFile(path string) (*Config, error) {
+ var c *Config
+ f, errOpen := os.Open(path)
+ if errOpen != nil {
+ return nil, newOpenError(path, errOpen)
+ }
+ defer f.Close()
+ decoder := yaml.NewDecoder(f)
+ if err := decoder.Decode(&c); err != nil {
+ return nil, newDecodeError(path, err)
+ }
+ if err := c.validate(); err != nil {
+ return nil, err
+ }
+ return c, nil
+}
diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go
new file mode 100644
index 0000000..3904b5d
--- /dev/null
+++ b/pkg/config/config_test.go
@@ -0,0 +1,54 @@
+package config
+
+import (
+ "reflect"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func TestLoadFile(t *testing.T) {
+ // Minimal yaml file
+ minimalConfig := Config{
+ Address: "127.0.0.2",
+ Port: "8082",
+ Token: "12345678-9abc-def0-1234-56789abcdef0",
+ }
+
+ // Minimal yaml file with hostname resolving
+ minimalConfigWithResolving := Config{
+ Address: "localhost",
+ Port: "8082",
+ Token: "12345678-9abc-def0-1234-56789abcdef0",
+ }
+
+ // Test cases
+ testCases := []struct {
+ name string
+ input string
+ expected *Config
+ expectedError interface{}
+ }{
+ {"Non existant file", "test_data/non-existant", nil, &OpenError{}},
+ {"Invalid file content", "test_data/invalid.yaml", nil, &DecodeError{}},
+ {"Invalid address should fail to load", "test_data/invalid_address.yaml", nil, &InvalidAddressError{}},
+ {"Unresolvable address should fail to load", "test_data/invalid_address_unresolvable.yaml", nil, &InvalidAddressError{}},
+ {"Invalid port should fail to load", "test_data/invalid_port.yaml", nil, &InvalidPortError{}},
+ {"Invalid token should fail to load", "test_data/invalid_token.yaml", nil, &InvalidTokenError{}},
+ {"Minimal config", "test_data/minimal.yaml", &minimalConfig, nil},
+ {"Minimal config with resolving", "test_data/minimal_with_hostname.yaml", &minimalConfigWithResolving, nil},
+ }
+ for _, tc := range testCases {
+ t.Run(tc.name, func(t *testing.T) {
+ valid, err := LoadFile(tc.input)
+ if tc.expectedError != nil {
+ require.Error(t, err)
+ assert.Equalf(t, reflect.TypeOf(err), reflect.TypeOf(tc.expectedError), "Invalid error type. Got %s but expected %s", reflect.TypeOf(err), reflect.TypeOf(tc.expectedError))
+ } else {
+ require.NoError(t, err)
+ }
+ assert.Equal(t, tc.expected, valid, "Invalid value")
+ })
+ }
+}
diff --git a/pkg/config/error.go b/pkg/config/error.go
new file mode 100644
index 0000000..c49b6a9
--- /dev/null
+++ b/pkg/config/error.go
@@ -0,0 +1,92 @@
+package config
+
+import "fmt"
+
+type ErrorType int
+
+// file open configuration file error
+type OpenError struct {
+ path string
+ err error
+}
+
+func (e *OpenError) Error() string {
+ return fmt.Sprintf("Failed to open configuration file : %s", e.path)
+}
+func (e *OpenError) Unwrap() error { return e.err }
+
+func newOpenError(path string, err error) error {
+ return &OpenError{
+ path: path,
+ err: err,
+ }
+}
+
+// Yaml configuration file decoding error
+type DecodeError struct {
+ path string
+ err error
+}
+
+func (e *DecodeError) Error() string {
+ return fmt.Sprintf("Failed to decode configuration file : %s", e.path)
+}
+func (e *DecodeError) Unwrap() error { return e.err }
+
+func newDecodeError(path string, err error) error {
+ return &DecodeError{
+ path: path,
+ err: err,
+ }
+}
+
+// Invalid address field error
+type InvalidAddressError struct {
+ address string
+ err error
+}
+
+func (e *InvalidAddressError) Error() string {
+ return fmt.Sprintf("Invalid address %s : it must be a valid ipv4 address, ipv6 address, or resolvable name", e.address)
+}
+func (e *InvalidAddressError) Unwrap() error { return e.err }
+
+func newInvalidAddressError(address string, err error) error {
+ return &InvalidAddressError{
+ address: address,
+ err: err,
+ }
+}
+
+// Invalid port field error
+type InvalidPortError struct {
+ port string
+ err error
+}
+
+func (e *InvalidPortError) Error() string {
+ return fmt.Sprintf("Invalid port %s : it must be a valid port number or tcp service name", e.port)
+}
+func (e *InvalidPortError) Unwrap() error { return e.err }
+
+func newInvalidPortError(port string, err error) error {
+ return &InvalidPortError{
+ port: port,
+ err: err,
+ }
+}
+
+// Invalid token field error
+type InvalidTokenError struct {
+ token string
+}
+
+func (e *InvalidTokenError) Error() string {
+ return fmt.Sprintf("Invalid token %s : it must be an hexadecimal string that lookslike 12345678-9abc-def0-1234-56789abcdef0", e.token)
+}
+
+func newInvalidTokenError(token string) error {
+ return &InvalidTokenError{
+ token: token,
+ }
+}
diff --git a/pkg/config/error_test.go b/pkg/config/error_test.go
new file mode 100644
index 0000000..f9807c1
--- /dev/null
+++ b/pkg/config/error_test.go
@@ -0,0 +1,20 @@
+package config
+
+import "testing"
+
+func TestErrorsCoverage(t *testing.T) {
+ openErr := OpenError{}
+ _ = openErr.Error()
+ _ = openErr.Unwrap()
+ decodeErr := DecodeError{}
+ _ = decodeErr.Error()
+ _ = decodeErr.Unwrap()
+ invalidAddressErr := InvalidAddressError{}
+ _ = invalidAddressErr.Error()
+ _ = invalidAddressErr.Unwrap()
+ invalidPortErr := InvalidPortError{}
+ _ = invalidPortErr.Error()
+ _ = invalidPortErr.Unwrap()
+ invalidTokenErr := InvalidTokenError{}
+ _ = invalidTokenErr.Error()
+}
diff --git a/pkg/config/test_data/invalid.yaml b/pkg/config/test_data/invalid.yaml
new file mode 100644
index 0000000..db1ddad
--- /dev/null
+++ b/pkg/config/test_data/invalid.yaml
@@ -0,0 +1 @@
+blargh(ads)
diff --git a/pkg/config/test_data/invalid_address.yaml b/pkg/config/test_data/invalid_address.yaml
new file mode 100644
index 0000000..e3682b2
--- /dev/null
+++ b/pkg/config/test_data/invalid_address.yaml
@@ -0,0 +1,3 @@
+address: "0.0.0.0.0"
+port: 8082
+token: 12345678-9abc-def0-1234-56789abcdef0
diff --git a/pkg/config/test_data/invalid_address_unresolvable.yaml b/pkg/config/test_data/invalid_address_unresolvable.yaml
new file mode 100644
index 0000000..d476093
--- /dev/null
+++ b/pkg/config/test_data/invalid_address_unresolvable.yaml
@@ -0,0 +1,3 @@
+address: "invalid"
+port: 8082
+token: 12345678-9abc-def0-1234-56789abcdef0
diff --git a/pkg/config/test_data/invalid_port.yaml b/pkg/config/test_data/invalid_port.yaml
new file mode 100644
index 0000000..2790b3f
--- /dev/null
+++ b/pkg/config/test_data/invalid_port.yaml
@@ -0,0 +1,3 @@
+address: localhost
+port: invalid
+token: 12345678-9abc-def0-1234-56789abcdef0
diff --git a/pkg/config/test_data/invalid_token.yaml b/pkg/config/test_data/invalid_token.yaml
new file mode 100644
index 0000000..6f9e297
--- /dev/null
+++ b/pkg/config/test_data/invalid_token.yaml
@@ -0,0 +1,3 @@
+address: 127.0.0.1
+port: 8080
+token: invalid
diff --git a/pkg/config/test_data/minimal.yaml b/pkg/config/test_data/minimal.yaml
new file mode 100644
index 0000000..2944645
--- /dev/null
+++ b/pkg/config/test_data/minimal.yaml
@@ -0,0 +1,3 @@
+address: 127.0.0.2
+port: 8082
+token: 12345678-9abc-def0-1234-56789abcdef0
diff --git a/pkg/config/test_data/minimal_with_hostname.yaml b/pkg/config/test_data/minimal_with_hostname.yaml
new file mode 100644
index 0000000..716b2d5
--- /dev/null
+++ b/pkg/config/test_data/minimal_with_hostname.yaml
@@ -0,0 +1,3 @@
+address: localhost
+port: 8082
+token: 12345678-9abc-def0-1234-56789abcdef0
diff --git a/pkg/navitia_api_client/client.go b/pkg/navitia_api_client/client.go
new file mode 100644
index 0000000..aef8d0e
--- /dev/null
+++ b/pkg/navitia_api_client/client.go
@@ -0,0 +1,31 @@
+package navitia_api_client
+
+import (
+ "fmt"
+ "net/http"
+ "sync"
+ "time"
+)
+
+type Client struct {
+ baseURL string
+ httpClient *http.Client
+
+ mutex sync.Mutex
+ cache map[string]cachedResult
+}
+
+type cachedResult struct {
+ ts time.Time
+ result interface{}
+}
+
+func NewClient(token string) *Client {
+ return &Client{
+ baseURL: fmt.Sprintf("https://%s@api.sncf.com/v1", token),
+ httpClient: &http.Client{
+ Timeout: time.Minute,
+ },
+ cache: make(map[string]cachedResult),
+ }
+}
diff --git a/pkg/navitia_api_client/client_test.go b/pkg/navitia_api_client/client_test.go
new file mode 100644
index 0000000..332b222
--- /dev/null
+++ b/pkg/navitia_api_client/client_test.go
@@ -0,0 +1,38 @@
+package navitia_api_client
+
+import (
+ "fmt"
+ "io/ioutil"
+ "net/http"
+ "net/http/httptest"
+ "testing"
+)
+
+// package utilities
+func NewTestClient(ts *httptest.Server) *Client {
+ return &Client{
+ baseURL: fmt.Sprintf(ts.URL),
+ httpClient: ts.Client(),
+ cache: make(map[string]cachedResult),
+ }
+}
+
+func NewTestClientFromFilename(t *testing.T, filename string) (*Client, *httptest.Server) {
+ ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ page, err := ioutil.ReadFile(filename)
+ if err != nil {
+ t.Fatalf("Could not open test file : %s", err)
+ }
+ w.Write(page)
+ }))
+ return NewTestClient(ts), ts
+}
+
+// tests
+func TestNewClient(t *testing.T) {
+ client := NewClient("test")
+ want := "https://test@api.sncf.com/v1"
+ if client.baseURL != want {
+ t.Fatal("Invalid new client")
+ }
+}
diff --git a/pkg/navitia_api_client/departures.go b/pkg/navitia_api_client/departures.go
new file mode 100644
index 0000000..7738940
--- /dev/null
+++ b/pkg/navitia_api_client/departures.go
@@ -0,0 +1,73 @@
+package navitia_api_client
+
+import (
+ "encoding/json"
+ "fmt"
+ "net/http"
+ "time"
+)
+
+type DeparturesResponse struct {
+ Disruptions []interface{} `json:"disruptions"`
+ Notes []interface{} `json:"notes"`
+ Departures []struct {
+ DisplayInformations struct {
+ Direction string `json:"direction"`
+ Code string `json:"code"`
+ Network string `json:"network"`
+ Links []interface{} `json:"links"`
+ Color string `json:"color"`
+ Name string `json:"name"`
+ PhysicalMode string `json:"physical_mode"`
+ Headsign string `json:"headsign"`
+ Label string `json:"label"`
+ Equipments []interface{} `json:"equipments"`
+ TextColor string `json:"text_color"`
+ TripShortName string `json:"trip_short_name"`
+ CommercialMode string `json:"commercial_mode"`
+ Description string `json:"description"`
+ } `json:"display_informations"`
+ StopDateTime struct {
+ Links []interface{} `json:"links"`
+ ArrivalDateTime string `json:"arrival_date_time"`
+ AdditionalInformations []interface{} `json:"additional_informations"`
+ DepartureDateTime string `json:"departure_date_time"`
+ BaseArrivalDateTime string `json:"base_arrival_date_time"`
+ BaseDepartureDateTime string `json:"base_departure_date_time"`
+ DataFreshness string `json:"data_freshness"`
+ } `json:"stop_date_time"`
+ } `json:"departures"`
+ Context struct {
+ Timezone string `json:"timezone"`
+ CurrentDatetime string `json:"current_datetime"`
+ } `json:"context"`
+}
+
+func (c *Client) GetDepartures() (departures *DeparturesResponse, err error) {
+ request := fmt.Sprintf("%s/coverage/sncf/stop_areas/stop_area:SNCF:87723502/departures", c.baseURL)
+ start := time.Now()
+ c.mutex.Lock()
+ defer c.mutex.Unlock()
+ if cachedResult, ok := c.cache[request]; ok {
+ if start.Sub(cachedResult.ts) < 60*1000*1000*1000 {
+ return cachedResult.result.(*DeparturesResponse), nil
+ }
+ }
+ req, err := http.NewRequest("GET", request, nil)
+ if err != nil {
+ return nil, err
+ }
+ resp, err := c.httpClient.Do(req)
+ if err != nil {
+ return nil, err
+ }
+ defer resp.Body.Close()
+ if err = json.NewDecoder(resp.Body).Decode(&departures); err != nil {
+ return nil, err
+ }
+ c.cache[request] = cachedResult{
+ ts: start,
+ result: departures,
+ }
+ return
+}
diff --git a/pkg/navitia_api_client/departures_test.go b/pkg/navitia_api_client/departures_test.go
new file mode 100644
index 0000000..a1658d2
--- /dev/null
+++ b/pkg/navitia_api_client/departures_test.go
@@ -0,0 +1,57 @@
+package navitia_api_client
+
+import (
+ "net/http"
+ "net/http/httptest"
+ "testing"
+)
+
+func TestGetDepartures(t *testing.T) {
+ // invalid characters in token
+ client := NewClient("}")
+ _, err := client.GetDepartures()
+ if err == nil {
+ t.Fatalf("invalid characters in token should raise an error")
+ }
+ // unreachable server
+ client = NewClient("https://")
+ _, err = client.GetDepartures()
+ if err == nil {
+ t.Fatalf("unreachable server should raise an error")
+ }
+ // invalid json
+ client, ts := NewTestClientFromFilename(t, "test_data/invalid.json")
+ defer ts.Close()
+ _, err = client.GetDepartures()
+ if err == nil {
+ t.Fatalf("invalid json should raise an error")
+ }
+ // http error
+ ts = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.WriteHeader(http.StatusNotFound)
+ }))
+ client = NewTestClient(ts)
+ _, err = client.GetDepartures()
+ if err == nil {
+ t.Fatalf("404 should raise an error")
+ }
+ // normal working request
+ client, ts = NewTestClientFromFilename(t, "test_data/normal-crepieux.json")
+ defer ts.Close()
+ departures, err := client.GetDepartures()
+ if err != nil {
+ t.Fatalf("could not get normal-crepieux departures : %s", err)
+ }
+ if len(departures.Departures) != 10 {
+ t.Fatalf("did not decode normal-crepieux departures properly, got %d departures when expected 10", len(departures.Departures))
+ }
+ // test the cache (assuming the test takes less than 60 seconds (and it really should) it will be accurate)
+ ts.Close()
+ departures, err = client.GetDepartures()
+ if err != nil {
+ t.Fatalf("could not get normal-crepieux departures : %s", err)
+ }
+ if len(departures.Departures) != 10 {
+ t.Fatalf("did not decode normal-crepieux departures properly, got %d departures when expected 10", len(departures.Departures))
+ }
+}
diff --git a/pkg/navitia_api_client/test_data/invalid.json b/pkg/navitia_api_client/test_data/invalid.json
new file mode 100644
index 0000000..98232c6
--- /dev/null
+++ b/pkg/navitia_api_client/test_data/invalid.json
@@ -0,0 +1 @@
+{
diff --git a/pkg/navitia_api_client/test_data/normal-crepieux.json b/pkg/navitia_api_client/test_data/normal-crepieux.json
new file mode 100644
index 0000000..19a635a
--- /dev/null
+++ b/pkg/navitia_api_client/test_data/normal-crepieux.json
@@ -0,0 +1,2135 @@
+{
+ "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_areas/stop_area:OCE:SA:87723502/departures",
+ "type": "first",
+ "templated": false
+ }
+ ],
+ "disruptions": [],
+ "notes": [],
+ "feed_publishers": [],
+ "departures": [
+ {
+ "display_informations": {
+ "direction": "Ambérieu-en-Bugey (Ambérieu-en-Bugey)",
+ "code": "",
+ "network": "SNCF",
+ "links": [],
+ "color": "000000",
+ "name": "St-Etienne - Lyon - Ambérieu",
+ "physical_mode": "Train régional / TER",
+ "headsign": "886823",
+ "label": "St-Etienne - Lyon - Ambérieu",
+ "equipments": [],
+ "text_color": "FFFFFF",
+ "trip_short_name": "886823",
+ "commercial_mode": "TER",
+ "description": ""
+ },
+ "stop_point": {
+ "commercial_modes": [
+ {
+ "id": "commercial_mode:ter",
+ "name": "TER"
+ }
+ ],
+ "name": "Crépieux-la-Pape",
+ "links": [],
+ "physical_modes": [
+ {
+ "id": "physical_mode:LocalTrain",
+ "name": "Train régional / TER"
+ }
+ ],
+ "coord": {
+ "lat": "45.803921",
+ "lon": "4.892737"
+ },
+ "label": "Crépieux-la-Pape (Rillieux-la-Pape)",
+ "equipments": [],
+ "administrative_regions": [
+ {
+ "insee": "69286",
+ "name": "Rillieux-la-Pape",
+ "level": 8,
+ "coord": {
+ "lat": "45.823514",
+ "lon": "4.8994366"
+ },
+ "label": "Rillieux-la-Pape (69140)",
+ "id": "admin:fr:69286",
+ "zip_code": "69140"
+ }
+ ],
+ "fare_zone": {
+ "name": "0"
+ },
+ "id": "stop_point:OCE:SP:TrainTER-87723502",
+ "stop_area": {
+ "codes": [
+ {
+ "type": "CR-CI-CH",
+ "value": "0087-723502-00"
+ },
+ {
+ "type": "UIC8",
+ "value": "87723502"
+ },
+ {
+ "type": "external_code",
+ "value": "OCE87723502"
+ }
+ ],
+ "name": "Crépieux-la-Pape",
+ "links": [],
+ "coord": {
+ "lat": "45.803921",
+ "lon": "4.892737"
+ },
+ "label": "Crépieux-la-Pape (Rillieux-la-Pape)",
+ "administrative_regions": [
+ {
+ "insee": "69286",
+ "name": "Rillieux-la-Pape",
+ "level": 8,
+ "coord": {
+ "lat": "45.823514",
+ "lon": "4.8994366"
+ },
+ "label": "Rillieux-la-Pape (69140)",
+ "id": "admin:fr:69286",
+ "zip_code": "69140"
+ }
+ ],
+ "timezone": "Europe/Paris",
+ "id": "stop_area:OCE:SA:87723502"
+ }
+ },
+ "route": {
+ "direction": {
+ "embedded_type": "stop_area",
+ "stop_area": {
+ "codes": [
+ {
+ "type": "CR-CI-CH",
+ "value": "0087-743716-BV"
+ },
+ {
+ "type": "UIC8",
+ "value": "87743716"
+ },
+ {
+ "type": "external_code",
+ "value": "OCE87743716"
+ }
+ ],
+ "name": "Ambérieu-en-Bugey",
+ "links": [],
+ "coord": {
+ "lat": "45.954008",
+ "lon": "5.342313"
+ },
+ "label": "Ambérieu-en-Bugey (Ambérieu-en-Bugey)",
+ "timezone": "Europe/Paris",
+ "id": "stop_area:OCE:SA:87743716"
+ },
+ "quality": 0,
+ "name": "Ambérieu-en-Bugey (Ambérieu-en-Bugey)",
+ "id": "stop_area:OCE:SA:87743716"
+ },
+ "name": "St-Etienne-Châteaucreux vers Ambérieu-en-Bugey (Train TER)",
+ "links": [],
+ "physical_modes": [
+ {
+ "id": "physical_mode:LocalTrain",
+ "name": "Train régional / TER"
+ }
+ ],
+ "is_frequence": "False",
+ "geojson": {
+ "type": "MultiLineString",
+ "coordinates": []
+ },
+ "direction_type": "forward",
+ "line": {
+ "code": "",
+ "name": "St-Etienne - Lyon - Ambérieu",
+ "links": [],
+ "color": "000000",
+ "geojson": {
+ "type": "MultiLineString",
+ "coordinates": []
+ },
+ "text_color": "FFFFFF",
+ "physical_modes": [
+ {
+ "id": "physical_mode:LocalTrain",
+ "name": "Train régional / TER"
+ }
+ ],
+ "codes": [],
+ "closing_time": "221200",
+ "opening_time": "053500",
+ "commercial_mode": {
+ "id": "commercial_mode:ter",
+ "name": "TER"
+ },
+ "id": "line:OCE:199"
+ },
+ "id": "route:OCE:199-TrainTER-87726000-87743716"
+ },
+ "links": [
+ {
+ "type": "line",
+ "id": "line:OCE:199"
+ },
+ {
+ "type": "vehicle_journey",
+ "id": "vehicle_journey:OCE:SN886823F29029_dst_1"
+ },
+ {
+ "type": "route",
+ "id": "route:OCE:199-TrainTER-87726000-87743716"
+ },
+ {
+ "type": "commercial_mode",
+ "id": "commercial_mode:ter"
+ },
+ {
+ "type": "physical_mode",
+ "id": "physical_mode:LocalTrain"
+ },
+ {
+ "type": "network",
+ "id": "network:sncf"
+ }
+ ],
+ "stop_date_time": {
+ "links": [],
+ "arrival_date_time": "20210218T131800",
+ "additional_informations": [],
+ "departure_date_time": "20210218T131800",
+ "base_arrival_date_time": "20210218T131800",
+ "base_departure_date_time": "20210218T131800",
+ "data_freshness": "base_schedule"
+ }
+ },
+ {
+ "display_informations": {
+ "direction": "St-Etienne-Châteaucreux (Saint-Étienne)",
+ "code": "",
+ "network": "SNCF",
+ "links": [],
+ "color": "000000",
+ "name": "St-Etienne - Lyon - Ambérieu",
+ "physical_mode": "Train régional / TER",
+ "headsign": "886726",
+ "label": "St-Etienne - Lyon - Ambérieu",
+ "equipments": [],
+ "text_color": "FFFFFF",
+ "trip_short_name": "886726",
+ "commercial_mode": "TER",
+ "description": ""
+ },
+ "stop_point": {
+ "commercial_modes": [
+ {
+ "id": "commercial_mode:ter",
+ "name": "TER"
+ }
+ ],
+ "name": "Crépieux-la-Pape",
+ "links": [],
+ "physical_modes": [
+ {
+ "id": "physical_mode:LocalTrain",
+ "name": "Train régional / TER"
+ }
+ ],
+ "coord": {
+ "lat": "45.803921",
+ "lon": "4.892737"
+ },
+ "label": "Crépieux-la-Pape (Rillieux-la-Pape)",
+ "equipments": [],
+ "administrative_regions": [
+ {
+ "insee": "69286",
+ "name": "Rillieux-la-Pape",
+ "level": 8,
+ "coord": {
+ "lat": "45.823514",
+ "lon": "4.8994366"
+ },
+ "label": "Rillieux-la-Pape (69140)",
+ "id": "admin:fr:69286",
+ "zip_code": "69140"
+ }
+ ],
+ "fare_zone": {
+ "name": "0"
+ },
+ "id": "stop_point:OCE:SP:TrainTER-87723502",
+ "stop_area": {
+ "codes": [
+ {
+ "type": "CR-CI-CH",
+ "value": "0087-723502-00"
+ },
+ {
+ "type": "UIC8",
+ "value": "87723502"
+ },
+ {
+ "type": "external_code",
+ "value": "OCE87723502"
+ }
+ ],
+ "name": "Crépieux-la-Pape",
+ "links": [],
+ "coord": {
+ "lat": "45.803921",
+ "lon": "4.892737"
+ },
+ "label": "Crépieux-la-Pape (Rillieux-la-Pape)",
+ "administrative_regions": [
+ {
+ "insee": "69286",
+ "name": "Rillieux-la-Pape",
+ "level": 8,
+ "coord": {
+ "lat": "45.823514",
+ "lon": "4.8994366"
+ },
+ "label": "Rillieux-la-Pape (69140)",
+ "id": "admin:fr:69286",
+ "zip_code": "69140"
+ }
+ ],
+ "timezone": "Europe/Paris",
+ "id": "stop_area:OCE:SA:87723502"
+ }
+ },
+ "route": {
+ "direction": {
+ "embedded_type": "stop_area",
+ "stop_area": {
+ "codes": [
+ {
+ "type": "CR-CI-CH",
+ "value": "0087-726000-BV"
+ },
+ {
+ "type": "UIC8",
+ "value": "87726000"
+ },
+ {
+ "type": "external_code",
+ "value": "OCE87726000"
+ }
+ ],
+ "name": "St-Etienne-Châteaucreux",
+ "links": [],
+ "coord": {
+ "lat": "45.443382",
+ "lon": "4.399996"
+ },
+ "label": "St-Etienne-Châteaucreux (Saint-Étienne)",
+ "timezone": "Europe/Paris",
+ "id": "stop_area:OCE:SA:87726000"
+ },
+ "quality": 0,
+ "name": "St-Etienne-Châteaucreux (Saint-Étienne)",
+ "id": "stop_area:OCE:SA:87726000"
+ },
+ "name": "Ambérieu-en-Bugey vers St-Etienne-Châteaucreux (Train TER)",
+ "links": [],
+ "physical_modes": [
+ {
+ "id": "physical_mode:LocalTrain",
+ "name": "Train régional / TER"
+ }
+ ],
+ "is_frequence": "False",
+ "geojson": {
+ "type": "MultiLineString",
+ "coordinates": []
+ },
+ "direction_type": "backward",
+ "line": {
+ "code": "",
+ "name": "St-Etienne - Lyon - Ambérieu",
+ "links": [],
+ "color": "000000",
+ "geojson": {
+ "type": "MultiLineString",
+ "coordinates": []
+ },
+ "text_color": "FFFFFF",
+ "physical_modes": [
+ {
+ "id": "physical_mode:LocalTrain",
+ "name": "Train régional / TER"
+ }
+ ],
+ "codes": [],
+ "closing_time": "221200",
+ "opening_time": "053500",
+ "commercial_mode": {
+ "id": "commercial_mode:ter",
+ "name": "TER"
+ },
+ "id": "line:OCE:199"
+ },
+ "id": "route:OCE:199-TrainTER-87743716-87726000"
+ },
+ "links": [
+ {
+ "type": "line",
+ "id": "line:OCE:199"
+ },
+ {
+ "type": "vehicle_journey",
+ "id": "vehicle_journey:OCE:SN886726F35035_dst_1"
+ },
+ {
+ "type": "route",
+ "id": "route:OCE:199-TrainTER-87743716-87726000"
+ },
+ {
+ "type": "commercial_mode",
+ "id": "commercial_mode:ter"
+ },
+ {
+ "type": "physical_mode",
+ "id": "physical_mode:LocalTrain"
+ },
+ {
+ "type": "network",
+ "id": "network:sncf"
+ }
+ ],
+ "stop_date_time": {
+ "links": [],
+ "arrival_date_time": "20210218T134100",
+ "additional_informations": [],
+ "departure_date_time": "20210218T134100",
+ "base_arrival_date_time": "20210218T134100",
+ "base_departure_date_time": "20210218T134100",
+ "data_freshness": "base_schedule"
+ }
+ },
+ {
+ "display_informations": {
+ "direction": "Ambérieu-en-Bugey (Ambérieu-en-Bugey)",
+ "code": "",
+ "network": "SNCF",
+ "links": [],
+ "color": "000000",
+ "name": "St-Etienne - Lyon - Ambérieu",
+ "physical_mode": "Train régional / TER",
+ "headsign": "886827",
+ "label": "St-Etienne - Lyon - Ambérieu",
+ "equipments": [],
+ "text_color": "FFFFFF",
+ "trip_short_name": "886827",
+ "commercial_mode": "TER",
+ "description": ""
+ },
+ "stop_point": {
+ "commercial_modes": [
+ {
+ "id": "commercial_mode:ter",
+ "name": "TER"
+ }
+ ],
+ "name": "Crépieux-la-Pape",
+ "links": [],
+ "physical_modes": [
+ {
+ "id": "physical_mode:LocalTrain",
+ "name": "Train régional / TER"
+ }
+ ],
+ "coord": {
+ "lat": "45.803921",
+ "lon": "4.892737"
+ },
+ "label": "Crépieux-la-Pape (Rillieux-la-Pape)",
+ "equipments": [],
+ "administrative_regions": [
+ {
+ "insee": "69286",
+ "name": "Rillieux-la-Pape",
+ "level": 8,
+ "coord": {
+ "lat": "45.823514",
+ "lon": "4.8994366"
+ },
+ "label": "Rillieux-la-Pape (69140)",
+ "id": "admin:fr:69286",
+ "zip_code": "69140"
+ }
+ ],
+ "fare_zone": {
+ "name": "0"
+ },
+ "id": "stop_point:OCE:SP:TrainTER-87723502",
+ "stop_area": {
+ "codes": [
+ {
+ "type": "CR-CI-CH",
+ "value": "0087-723502-00"
+ },
+ {
+ "type": "UIC8",
+ "value": "87723502"
+ },
+ {
+ "type": "external_code",
+ "value": "OCE87723502"
+ }
+ ],
+ "name": "Crépieux-la-Pape",
+ "links": [],
+ "coord": {
+ "lat": "45.803921",
+ "lon": "4.892737"
+ },
+ "label": "Crépieux-la-Pape (Rillieux-la-Pape)",
+ "administrative_regions": [
+ {
+ "insee": "69286",
+ "name": "Rillieux-la-Pape",
+ "level": 8,
+ "coord": {
+ "lat": "45.823514",
+ "lon": "4.8994366"
+ },
+ "label": "Rillieux-la-Pape (69140)",
+ "id": "admin:fr:69286",
+ "zip_code": "69140"
+ }
+ ],
+ "timezone": "Europe/Paris",
+ "id": "stop_area:OCE:SA:87723502"
+ }
+ },
+ "route": {
+ "direction": {
+ "embedded_type": "stop_area",
+ "stop_area": {
+ "codes": [
+ {
+ "type": "CR-CI-CH",
+ "value": "0087-743716-BV"
+ },
+ {
+ "type": "UIC8",
+ "value": "87743716"
+ },
+ {
+ "type": "external_code",
+ "value": "OCE87743716"
+ }
+ ],
+ "name": "Ambérieu-en-Bugey",
+ "links": [],
+ "coord": {
+ "lat": "45.954008",
+ "lon": "5.342313"
+ },
+ "label": "Ambérieu-en-Bugey (Ambérieu-en-Bugey)",
+ "timezone": "Europe/Paris",
+ "id": "stop_area:OCE:SA:87743716"
+ },
+ "quality": 0,
+ "name": "Ambérieu-en-Bugey (Ambérieu-en-Bugey)",
+ "id": "stop_area:OCE:SA:87743716"
+ },
+ "name": "St-Etienne-Châteaucreux vers Ambérieu-en-Bugey (Train TER)",
+ "links": [],
+ "physical_modes": [
+ {
+ "id": "physical_mode:LocalTrain",
+ "name": "Train régional / TER"
+ }
+ ],
+ "is_frequence": "False",
+ "geojson": {
+ "type": "MultiLineString",
+ "coordinates": []
+ },
+ "direction_type": "forward",
+ "line": {
+ "code": "",
+ "name": "St-Etienne - Lyon - Ambérieu",
+ "links": [],
+ "color": "000000",
+ "geojson": {
+ "type": "MultiLineString",
+ "coordinates": []
+ },
+ "text_color": "FFFFFF",
+ "physical_modes": [
+ {
+ "id": "physical_mode:LocalTrain",
+ "name": "Train régional / TER"
+ }
+ ],
+ "codes": [],
+ "closing_time": "221200",
+ "opening_time": "053500",
+ "commercial_mode": {
+ "id": "commercial_mode:ter",
+ "name": "TER"
+ },
+ "id": "line:OCE:199"
+ },
+ "id": "route:OCE:199-TrainTER-87726000-87743716"
+ },
+ "links": [
+ {
+ "type": "line",
+ "id": "line:OCE:199"
+ },
+ {
+ "type": "vehicle_journey",
+ "id": "vehicle_journey:OCE:SN886827F22022_dst_1"
+ },
+ {
+ "type": "route",
+ "id": "route:OCE:199-TrainTER-87726000-87743716"
+ },
+ {
+ "type": "commercial_mode",
+ "id": "commercial_mode:ter"
+ },
+ {
+ "type": "physical_mode",
+ "id": "physical_mode:LocalTrain"
+ },
+ {
+ "type": "network",
+ "id": "network:sncf"
+ }
+ ],
+ "stop_date_time": {
+ "links": [],
+ "arrival_date_time": "20210218T141800",
+ "additional_informations": [],
+ "departure_date_time": "20210218T141800",
+ "base_arrival_date_time": "20210218T141800",
+ "base_departure_date_time": "20210218T141800",
+ "data_freshness": "base_schedule"
+ }
+ },
+ {
+ "display_informations": {
+ "direction": "St-Etienne-Châteaucreux (Saint-Étienne)",
+ "code": "",
+ "network": "SNCF",
+ "links": [],
+ "color": "000000",
+ "name": "St-Etienne - Lyon - Ambérieu",
+ "physical_mode": "Train régional / TER",
+ "headsign": "886728",
+ "label": "St-Etienne - Lyon - Ambérieu",
+ "equipments": [],
+ "text_color": "FFFFFF",
+ "trip_short_name": "886728",
+ "commercial_mode": "TER",
+ "description": ""
+ },
+ "stop_point": {
+ "commercial_modes": [
+ {
+ "id": "commercial_mode:ter",
+ "name": "TER"
+ }
+ ],
+ "name": "Crépieux-la-Pape",
+ "links": [],
+ "physical_modes": [
+ {
+ "id": "physical_mode:LocalTrain",
+ "name": "Train régional / TER"
+ }
+ ],
+ "coord": {
+ "lat": "45.803921",
+ "lon": "4.892737"
+ },
+ "label": "Crépieux-la-Pape (Rillieux-la-Pape)",
+ "equipments": [],
+ "administrative_regions": [
+ {
+ "insee": "69286",
+ "name": "Rillieux-la-Pape",
+ "level": 8,
+ "coord": {
+ "lat": "45.823514",
+ "lon": "4.8994366"
+ },
+ "label": "Rillieux-la-Pape (69140)",
+ "id": "admin:fr:69286",
+ "zip_code": "69140"
+ }
+ ],
+ "fare_zone": {
+ "name": "0"
+ },
+ "id": "stop_point:OCE:SP:TrainTER-87723502",
+ "stop_area": {
+ "codes": [
+ {
+ "type": "CR-CI-CH",
+ "value": "0087-723502-00"
+ },
+ {
+ "type": "UIC8",
+ "value": "87723502"
+ },
+ {
+ "type": "external_code",
+ "value": "OCE87723502"
+ }
+ ],
+ "name": "Crépieux-la-Pape",
+ "links": [],
+ "coord": {
+ "lat": "45.803921",
+ "lon": "4.892737"
+ },
+ "label": "Crépieux-la-Pape (Rillieux-la-Pape)",
+ "administrative_regions": [
+ {
+ "insee": "69286",
+ "name": "Rillieux-la-Pape",
+ "level": 8,
+ "coord": {
+ "lat": "45.823514",
+ "lon": "4.8994366"
+ },
+ "label": "Rillieux-la-Pape (69140)",
+ "id": "admin:fr:69286",
+ "zip_code": "69140"
+ }
+ ],
+ "timezone": "Europe/Paris",
+ "id": "stop_area:OCE:SA:87723502"
+ }
+ },
+ "route": {
+ "direction": {
+ "embedded_type": "stop_area",
+ "stop_area": {
+ "codes": [
+ {
+ "type": "CR-CI-CH",
+ "value": "0087-726000-BV"
+ },
+ {
+ "type": "UIC8",
+ "value": "87726000"
+ },
+ {
+ "type": "external_code",
+ "value": "OCE87726000"
+ }
+ ],
+ "name": "St-Etienne-Châteaucreux",
+ "links": [],
+ "coord": {
+ "lat": "45.443382",
+ "lon": "4.399996"
+ },
+ "label": "St-Etienne-Châteaucreux (Saint-Étienne)",
+ "timezone": "Europe/Paris",
+ "id": "stop_area:OCE:SA:87726000"
+ },
+ "quality": 0,
+ "name": "St-Etienne-Châteaucreux (Saint-Étienne)",
+ "id": "stop_area:OCE:SA:87726000"
+ },
+ "name": "Ambérieu-en-Bugey vers St-Etienne-Châteaucreux (Train TER)",
+ "links": [],
+ "physical_modes": [
+ {
+ "id": "physical_mode:LocalTrain",
+ "name": "Train régional / TER"
+ }
+ ],
+ "is_frequence": "False",
+ "geojson": {
+ "type": "MultiLineString",
+ "coordinates": []
+ },
+ "direction_type": "backward",
+ "line": {
+ "code": "",
+ "name": "St-Etienne - Lyon - Ambérieu",
+ "links": [],
+ "color": "000000",
+ "geojson": {
+ "type": "MultiLineString",
+ "coordinates": []
+ },
+ "text_color": "FFFFFF",
+ "physical_modes": [
+ {
+ "id": "physical_mode:LocalTrain",
+ "name": "Train régional / TER"
+ }
+ ],
+ "codes": [],
+ "closing_time": "221200",
+ "opening_time": "053500",
+ "commercial_mode": {
+ "id": "commercial_mode:ter",
+ "name": "TER"
+ },
+ "id": "line:OCE:199"
+ },
+ "id": "route:OCE:199-TrainTER-87743716-87726000"
+ },
+ "links": [
+ {
+ "type": "line",
+ "id": "line:OCE:199"
+ },
+ {
+ "type": "vehicle_journey",
+ "id": "vehicle_journey:OCE:SN886728F16016_dst_1"
+ },
+ {
+ "type": "route",
+ "id": "route:OCE:199-TrainTER-87743716-87726000"
+ },
+ {
+ "type": "commercial_mode",
+ "id": "commercial_mode:ter"
+ },
+ {
+ "type": "physical_mode",
+ "id": "physical_mode:LocalTrain"
+ },
+ {
+ "type": "network",
+ "id": "network:sncf"
+ }
+ ],
+ "stop_date_time": {
+ "links": [],
+ "arrival_date_time": "20210218T144100",
+ "additional_informations": [],
+ "departure_date_time": "20210218T144100",
+ "base_arrival_date_time": "20210218T144100",
+ "base_departure_date_time": "20210218T144100",
+ "data_freshness": "base_schedule"
+ }
+ },
+ {
+ "display_informations": {
+ "direction": "Ambérieu-en-Bugey (Ambérieu-en-Bugey)",
+ "code": "",
+ "network": "SNCF",
+ "links": [],
+ "color": "000000",
+ "name": "St-Etienne - Lyon - Ambérieu",
+ "physical_mode": "Train régional / TER",
+ "headsign": "886871",
+ "label": "St-Etienne - Lyon - Ambérieu",
+ "equipments": [],
+ "text_color": "FFFFFF",
+ "trip_short_name": "886871",
+ "commercial_mode": "TER",
+ "description": ""
+ },
+ "stop_point": {
+ "commercial_modes": [
+ {
+ "id": "commercial_mode:ter",
+ "name": "TER"
+ }
+ ],
+ "name": "Crépieux-la-Pape",
+ "links": [],
+ "physical_modes": [
+ {
+ "id": "physical_mode:LocalTrain",
+ "name": "Train régional / TER"
+ }
+ ],
+ "coord": {
+ "lat": "45.803921",
+ "lon": "4.892737"
+ },
+ "label": "Crépieux-la-Pape (Rillieux-la-Pape)",
+ "equipments": [],
+ "administrative_regions": [
+ {
+ "insee": "69286",
+ "name": "Rillieux-la-Pape",
+ "level": 8,
+ "coord": {
+ "lat": "45.823514",
+ "lon": "4.8994366"
+ },
+ "label": "Rillieux-la-Pape (69140)",
+ "id": "admin:fr:69286",
+ "zip_code": "69140"
+ }
+ ],
+ "fare_zone": {
+ "name": "0"
+ },
+ "id": "stop_point:OCE:SP:TrainTER-87723502",
+ "stop_area": {
+ "codes": [
+ {
+ "type": "CR-CI-CH",
+ "value": "0087-723502-00"
+ },
+ {
+ "type": "UIC8",
+ "value": "87723502"
+ },
+ {
+ "type": "external_code",
+ "value": "OCE87723502"
+ }
+ ],
+ "name": "Crépieux-la-Pape",
+ "links": [],
+ "coord": {
+ "lat": "45.803921",
+ "lon": "4.892737"
+ },
+ "label": "Crépieux-la-Pape (Rillieux-la-Pape)",
+ "administrative_regions": [
+ {
+ "insee": "69286",
+ "name": "Rillieux-la-Pape",
+ "level": 8,
+ "coord": {
+ "lat": "45.823514",
+ "lon": "4.8994366"
+ },
+ "label": "Rillieux-la-Pape (69140)",
+ "id": "admin:fr:69286",
+ "zip_code": "69140"
+ }
+ ],
+ "timezone": "Europe/Paris",
+ "id": "stop_area:OCE:SA:87723502"
+ }
+ },
+ "route": {
+ "direction": {
+ "embedded_type": "stop_area",
+ "stop_area": {
+ "codes": [
+ {
+ "type": "CR-CI-CH",
+ "value": "0087-743716-BV"
+ },
+ {
+ "type": "UIC8",
+ "value": "87743716"
+ },
+ {
+ "type": "external_code",
+ "value": "OCE87743716"
+ }
+ ],
+ "name": "Ambérieu-en-Bugey",
+ "links": [],
+ "coord": {
+ "lat": "45.954008",
+ "lon": "5.342313"
+ },
+ "label": "Ambérieu-en-Bugey (Ambérieu-en-Bugey)",
+ "timezone": "Europe/Paris",
+ "id": "stop_area:OCE:SA:87743716"
+ },
+ "quality": 0,
+ "name": "Ambérieu-en-Bugey (Ambérieu-en-Bugey)",
+ "id": "stop_area:OCE:SA:87743716"
+ },
+ "name": "St-Etienne-Châteaucreux vers Ambérieu-en-Bugey (Train TER)",
+ "links": [],
+ "physical_modes": [
+ {
+ "id": "physical_mode:LocalTrain",
+ "name": "Train régional / TER"
+ }
+ ],
+ "is_frequence": "False",
+ "geojson": {
+ "type": "MultiLineString",
+ "coordinates": []
+ },
+ "direction_type": "forward",
+ "line": {
+ "code": "",
+ "name": "St-Etienne - Lyon - Ambérieu",
+ "links": [],
+ "color": "000000",
+ "geojson": {
+ "type": "MultiLineString",
+ "coordinates": []
+ },
+ "text_color": "FFFFFF",
+ "physical_modes": [
+ {
+ "id": "physical_mode:LocalTrain",
+ "name": "Train régional / TER"
+ }
+ ],
+ "codes": [],
+ "closing_time": "221200",
+ "opening_time": "053500",
+ "commercial_mode": {
+ "id": "commercial_mode:ter",
+ "name": "TER"
+ },
+ "id": "line:OCE:199"
+ },
+ "id": "route:OCE:199-TrainTER-87726000-87743716"
+ },
+ "links": [
+ {
+ "type": "line",
+ "id": "line:OCE:199"
+ },
+ {
+ "type": "vehicle_journey",
+ "id": "vehicle_journey:OCE:SN886871F27027_dst_1"
+ },
+ {
+ "type": "route",
+ "id": "route:OCE:199-TrainTER-87726000-87743716"
+ },
+ {
+ "type": "commercial_mode",
+ "id": "commercial_mode:ter"
+ },
+ {
+ "type": "physical_mode",
+ "id": "physical_mode:LocalTrain"
+ },
+ {
+ "type": "network",
+ "id": "network:sncf"
+ }
+ ],
+ "stop_date_time": {
+ "links": [],
+ "arrival_date_time": "20210218T151800",
+ "additional_informations": [],
+ "departure_date_time": "20210218T151800",
+ "base_arrival_date_time": "20210218T151800",
+ "base_departure_date_time": "20210218T151800",
+ "data_freshness": "base_schedule"
+ }
+ },
+ {
+ "display_informations": {
+ "direction": "Ambérieu-en-Bugey (Ambérieu-en-Bugey)",
+ "code": "",
+ "network": "SNCF",
+ "links": [],
+ "color": "000000",
+ "name": "St-Etienne - Lyon - Ambérieu",
+ "physical_mode": "Train régional / TER",
+ "headsign": "886837",
+ "label": "St-Etienne - Lyon - Ambérieu",
+ "equipments": [],
+ "text_color": "FFFFFF",
+ "trip_short_name": "886837",
+ "commercial_mode": "TER",
+ "description": ""
+ },
+ "stop_point": {
+ "commercial_modes": [
+ {
+ "id": "commercial_mode:ter",
+ "name": "TER"
+ }
+ ],
+ "name": "Crépieux-la-Pape",
+ "links": [],
+ "physical_modes": [
+ {
+ "id": "physical_mode:LocalTrain",
+ "name": "Train régional / TER"
+ }
+ ],
+ "coord": {
+ "lat": "45.803921",
+ "lon": "4.892737"
+ },
+ "label": "Crépieux-la-Pape (Rillieux-la-Pape)",
+ "equipments": [],
+ "administrative_regions": [
+ {
+ "insee": "69286",
+ "name": "Rillieux-la-Pape",
+ "level": 8,
+ "coord": {
+ "lat": "45.823514",
+ "lon": "4.8994366"
+ },
+ "label": "Rillieux-la-Pape (69140)",
+ "id": "admin:fr:69286",
+ "zip_code": "69140"
+ }
+ ],
+ "fare_zone": {
+ "name": "0"
+ },
+ "id": "stop_point:OCE:SP:TrainTER-87723502",
+ "stop_area": {
+ "codes": [
+ {
+ "type": "CR-CI-CH",
+ "value": "0087-723502-00"
+ },
+ {
+ "type": "UIC8",
+ "value": "87723502"
+ },
+ {
+ "type": "external_code",
+ "value": "OCE87723502"
+ }
+ ],
+ "name": "Crépieux-la-Pape",
+ "links": [],
+ "coord": {
+ "lat": "45.803921",
+ "lon": "4.892737"
+ },
+ "label": "Crépieux-la-Pape (Rillieux-la-Pape)",
+ "administrative_regions": [
+ {
+ "insee": "69286",
+ "name": "Rillieux-la-Pape",
+ "level": 8,
+ "coord": {
+ "lat": "45.823514",
+ "lon": "4.8994366"
+ },
+ "label": "Rillieux-la-Pape (69140)",
+ "id": "admin:fr:69286",
+ "zip_code": "69140"
+ }
+ ],
+ "timezone": "Europe/Paris",
+ "id": "stop_area:OCE:SA:87723502"
+ }
+ },
+ "route": {
+ "direction": {
+ "embedded_type": "stop_area",
+ "stop_area": {
+ "codes": [
+ {
+ "type": "CR-CI-CH",
+ "value": "0087-743716-BV"
+ },
+ {
+ "type": "UIC8",
+ "value": "87743716"
+ },
+ {
+ "type": "external_code",
+ "value": "OCE87743716"
+ }
+ ],
+ "name": "Ambérieu-en-Bugey",
+ "links": [],
+ "coord": {
+ "lat": "45.954008",
+ "lon": "5.342313"
+ },
+ "label": "Ambérieu-en-Bugey (Ambérieu-en-Bugey)",
+ "timezone": "Europe/Paris",
+ "id": "stop_area:OCE:SA:87743716"
+ },
+ "quality": 0,
+ "name": "Ambérieu-en-Bugey (Ambérieu-en-Bugey)",
+ "id": "stop_area:OCE:SA:87743716"
+ },
+ "name": "St-Etienne-Châteaucreux vers Ambérieu-en-Bugey (Train TER)",
+ "links": [],
+ "physical_modes": [
+ {
+ "id": "physical_mode:LocalTrain",
+ "name": "Train régional / TER"
+ }
+ ],
+ "is_frequence": "False",
+ "geojson": {
+ "type": "MultiLineString",
+ "coordinates": []
+ },
+ "direction_type": "forward",
+ "line": {
+ "code": "",
+ "name": "St-Etienne - Lyon - Ambérieu",
+ "links": [],
+ "color": "000000",
+ "geojson": {
+ "type": "MultiLineString",
+ "coordinates": []
+ },
+ "text_color": "FFFFFF",
+ "physical_modes": [
+ {
+ "id": "physical_mode:LocalTrain",
+ "name": "Train régional / TER"
+ }
+ ],
+ "codes": [],
+ "closing_time": "221200",
+ "opening_time": "053500",
+ "commercial_mode": {
+ "id": "commercial_mode:ter",
+ "name": "TER"
+ },
+ "id": "line:OCE:199"
+ },
+ "id": "route:OCE:199-TrainTER-87726000-87743716"
+ },
+ "links": [
+ {
+ "type": "line",
+ "id": "line:OCE:199"
+ },
+ {
+ "type": "vehicle_journey",
+ "id": "vehicle_journey:OCE:SN886837F18018_dst_1"
+ },
+ {
+ "type": "route",
+ "id": "route:OCE:199-TrainTER-87726000-87743716"
+ },
+ {
+ "type": "commercial_mode",
+ "id": "commercial_mode:ter"
+ },
+ {
+ "type": "physical_mode",
+ "id": "physical_mode:LocalTrain"
+ },
+ {
+ "type": "network",
+ "id": "network:sncf"
+ }
+ ],
+ "stop_date_time": {
+ "links": [],
+ "arrival_date_time": "20210218T161800",
+ "additional_informations": [],
+ "departure_date_time": "20210218T161800",
+ "base_arrival_date_time": "20210218T161800",
+ "base_departure_date_time": "20210218T161800",
+ "data_freshness": "base_schedule"
+ }
+ },
+ {
+ "display_informations": {
+ "direction": "St-Etienne-Châteaucreux (Saint-Étienne)",
+ "code": "",
+ "network": "SNCF",
+ "links": [],
+ "color": "000000",
+ "name": "St-Etienne - Lyon - Ambérieu",
+ "physical_mode": "Train régional / TER",
+ "headsign": "886734",
+ "label": "St-Etienne - Lyon - Ambérieu",
+ "equipments": [],
+ "text_color": "FFFFFF",
+ "trip_short_name": "886734",
+ "commercial_mode": "TER",
+ "description": ""
+ },
+ "stop_point": {
+ "commercial_modes": [
+ {
+ "id": "commercial_mode:ter",
+ "name": "TER"
+ }
+ ],
+ "name": "Crépieux-la-Pape",
+ "links": [],
+ "physical_modes": [
+ {
+ "id": "physical_mode:LocalTrain",
+ "name": "Train régional / TER"
+ }
+ ],
+ "coord": {
+ "lat": "45.803921",
+ "lon": "4.892737"
+ },
+ "label": "Crépieux-la-Pape (Rillieux-la-Pape)",
+ "equipments": [],
+ "administrative_regions": [
+ {
+ "insee": "69286",
+ "name": "Rillieux-la-Pape",
+ "level": 8,
+ "coord": {
+ "lat": "45.823514",
+ "lon": "4.8994366"
+ },
+ "label": "Rillieux-la-Pape (69140)",
+ "id": "admin:fr:69286",
+ "zip_code": "69140"
+ }
+ ],
+ "fare_zone": {
+ "name": "0"
+ },
+ "id": "stop_point:OCE:SP:TrainTER-87723502",
+ "stop_area": {
+ "codes": [
+ {
+ "type": "CR-CI-CH",
+ "value": "0087-723502-00"
+ },
+ {
+ "type": "UIC8",
+ "value": "87723502"
+ },
+ {
+ "type": "external_code",
+ "value": "OCE87723502"
+ }
+ ],
+ "name": "Crépieux-la-Pape",
+ "links": [],
+ "coord": {
+ "lat": "45.803921",
+ "lon": "4.892737"
+ },
+ "label": "Crépieux-la-Pape (Rillieux-la-Pape)",
+ "administrative_regions": [
+ {
+ "insee": "69286",
+ "name": "Rillieux-la-Pape",
+ "level": 8,
+ "coord": {
+ "lat": "45.823514",
+ "lon": "4.8994366"
+ },
+ "label": "Rillieux-la-Pape (69140)",
+ "id": "admin:fr:69286",
+ "zip_code": "69140"
+ }
+ ],
+ "timezone": "Europe/Paris",
+ "id": "stop_area:OCE:SA:87723502"
+ }
+ },
+ "route": {
+ "direction": {
+ "embedded_type": "stop_area",
+ "stop_area": {
+ "codes": [
+ {
+ "type": "CR-CI-CH",
+ "value": "0087-726000-BV"
+ },
+ {
+ "type": "UIC8",
+ "value": "87726000"
+ },
+ {
+ "type": "external_code",
+ "value": "OCE87726000"
+ }
+ ],
+ "name": "St-Etienne-Châteaucreux",
+ "links": [],
+ "coord": {
+ "lat": "45.443382",
+ "lon": "4.399996"
+ },
+ "label": "St-Etienne-Châteaucreux (Saint-Étienne)",
+ "timezone": "Europe/Paris",
+ "id": "stop_area:OCE:SA:87726000"
+ },
+ "quality": 0,
+ "name": "St-Etienne-Châteaucreux (Saint-Étienne)",
+ "id": "stop_area:OCE:SA:87726000"
+ },
+ "name": "Ambérieu-en-Bugey vers St-Etienne-Châteaucreux (Train TER)",
+ "links": [],
+ "physical_modes": [
+ {
+ "id": "physical_mode:LocalTrain",
+ "name": "Train régional / TER"
+ }
+ ],
+ "is_frequence": "False",
+ "geojson": {
+ "type": "MultiLineString",
+ "coordinates": []
+ },
+ "direction_type": "backward",
+ "line": {
+ "code": "",
+ "name": "St-Etienne - Lyon - Ambérieu",
+ "links": [],
+ "color": "000000",
+ "geojson": {
+ "type": "MultiLineString",
+ "coordinates": []
+ },
+ "text_color": "FFFFFF",
+ "physical_modes": [
+ {
+ "id": "physical_mode:LocalTrain",
+ "name": "Train régional / TER"
+ }
+ ],
+ "codes": [],
+ "closing_time": "221200",
+ "opening_time": "053500",
+ "commercial_mode": {
+ "id": "commercial_mode:ter",
+ "name": "TER"
+ },
+ "id": "line:OCE:199"
+ },
+ "id": "route:OCE:199-TrainTER-87743716-87726000"
+ },
+ "links": [
+ {
+ "type": "line",
+ "id": "line:OCE:199"
+ },
+ {
+ "type": "vehicle_journey",
+ "id": "vehicle_journey:OCE:SN886734F19019_dst_1"
+ },
+ {
+ "type": "route",
+ "id": "route:OCE:199-TrainTER-87743716-87726000"
+ },
+ {
+ "type": "commercial_mode",
+ "id": "commercial_mode:ter"
+ },
+ {
+ "type": "physical_mode",
+ "id": "physical_mode:LocalTrain"
+ },
+ {
+ "type": "network",
+ "id": "network:sncf"
+ }
+ ],
+ "stop_date_time": {
+ "links": [],
+ "arrival_date_time": "20210218T164100",
+ "additional_informations": [],
+ "departure_date_time": "20210218T164100",
+ "base_arrival_date_time": "20210218T164100",
+ "base_departure_date_time": "20210218T164100",
+ "data_freshness": "base_schedule"
+ }
+ },
+ {
+ "display_informations": {
+ "direction": "Ambérieu-en-Bugey (Ambérieu-en-Bugey)",
+ "code": "",
+ "network": "SNCF",
+ "links": [],
+ "color": "000000",
+ "name": "St-Etienne - Lyon - Ambérieu",
+ "physical_mode": "Train régional / TER",
+ "headsign": "886839",
+ "label": "St-Etienne - Lyon - Ambérieu",
+ "equipments": [],
+ "text_color": "FFFFFF",
+ "trip_short_name": "886839",
+ "commercial_mode": "TER",
+ "description": ""
+ },
+ "stop_point": {
+ "commercial_modes": [
+ {
+ "id": "commercial_mode:ter",
+ "name": "TER"
+ }
+ ],
+ "name": "Crépieux-la-Pape",
+ "links": [],
+ "physical_modes": [
+ {
+ "id": "physical_mode:LocalTrain",
+ "name": "Train régional / TER"
+ }
+ ],
+ "coord": {
+ "lat": "45.803921",
+ "lon": "4.892737"
+ },
+ "label": "Crépieux-la-Pape (Rillieux-la-Pape)",
+ "equipments": [],
+ "administrative_regions": [
+ {
+ "insee": "69286",
+ "name": "Rillieux-la-Pape",
+ "level": 8,
+ "coord": {
+ "lat": "45.823514",
+ "lon": "4.8994366"
+ },
+ "label": "Rillieux-la-Pape (69140)",
+ "id": "admin:fr:69286",
+ "zip_code": "69140"
+ }
+ ],
+ "fare_zone": {
+ "name": "0"
+ },
+ "id": "stop_point:OCE:SP:TrainTER-87723502",
+ "stop_area": {
+ "codes": [
+ {
+ "type": "CR-CI-CH",
+ "value": "0087-723502-00"
+ },
+ {
+ "type": "UIC8",
+ "value": "87723502"
+ },
+ {
+ "type": "external_code",
+ "value": "OCE87723502"
+ }
+ ],
+ "name": "Crépieux-la-Pape",
+ "links": [],
+ "coord": {
+ "lat": "45.803921",
+ "lon": "4.892737"
+ },
+ "label": "Crépieux-la-Pape (Rillieux-la-Pape)",
+ "administrative_regions": [
+ {
+ "insee": "69286",
+ "name": "Rillieux-la-Pape",
+ "level": 8,
+ "coord": {
+ "lat": "45.823514",
+ "lon": "4.8994366"
+ },
+ "label": "Rillieux-la-Pape (69140)",
+ "id": "admin:fr:69286",
+ "zip_code": "69140"
+ }
+ ],
+ "timezone": "Europe/Paris",
+ "id": "stop_area:OCE:SA:87723502"
+ }
+ },
+ "route": {
+ "direction": {
+ "embedded_type": "stop_area",
+ "stop_area": {
+ "codes": [
+ {
+ "type": "CR-CI-CH",
+ "value": "0087-743716-BV"
+ },
+ {
+ "type": "UIC8",
+ "value": "87743716"
+ },
+ {
+ "type": "external_code",
+ "value": "OCE87743716"
+ }
+ ],
+ "name": "Ambérieu-en-Bugey",
+ "links": [],
+ "coord": {
+ "lat": "45.954008",
+ "lon": "5.342313"
+ },
+ "label": "Ambérieu-en-Bugey (Ambérieu-en-Bugey)",
+ "timezone": "Europe/Paris",
+ "id": "stop_area:OCE:SA:87743716"
+ },
+ "quality": 0,
+ "name": "Ambérieu-en-Bugey (Ambérieu-en-Bugey)",
+ "id": "stop_area:OCE:SA:87743716"
+ },
+ "name": "St-Etienne-Châteaucreux vers Ambérieu-en-Bugey (Train TER)",
+ "links": [],
+ "physical_modes": [
+ {
+ "id": "physical_mode:LocalTrain",
+ "name": "Train régional / TER"
+ }
+ ],
+ "is_frequence": "False",
+ "geojson": {
+ "type": "MultiLineString",
+ "coordinates": []
+ },
+ "direction_type": "forward",
+ "line": {
+ "code": "",
+ "name": "St-Etienne - Lyon - Ambérieu",
+ "links": [],
+ "color": "000000",
+ "geojson": {
+ "type": "MultiLineString",
+ "coordinates": []
+ },
+ "text_color": "FFFFFF",
+ "physical_modes": [
+ {
+ "id": "physical_mode:LocalTrain",
+ "name": "Train régional / TER"
+ }
+ ],
+ "codes": [],
+ "closing_time": "221200",
+ "opening_time": "053500",
+ "commercial_mode": {
+ "id": "commercial_mode:ter",
+ "name": "TER"
+ },
+ "id": "line:OCE:199"
+ },
+ "id": "route:OCE:199-TrainTER-87726000-87743716"
+ },
+ "links": [
+ {
+ "type": "line",
+ "id": "line:OCE:199"
+ },
+ {
+ "type": "vehicle_journey",
+ "id": "vehicle_journey:OCE:SN886839F10010_dst_1"
+ },
+ {
+ "type": "route",
+ "id": "route:OCE:199-TrainTER-87726000-87743716"
+ },
+ {
+ "type": "commercial_mode",
+ "id": "commercial_mode:ter"
+ },
+ {
+ "type": "physical_mode",
+ "id": "physical_mode:LocalTrain"
+ },
+ {
+ "type": "network",
+ "id": "network:sncf"
+ }
+ ],
+ "stop_date_time": {
+ "links": [],
+ "arrival_date_time": "20210218T164800",
+ "additional_informations": [],
+ "departure_date_time": "20210218T164800",
+ "base_arrival_date_time": "20210218T164800",
+ "base_departure_date_time": "20210218T164800",
+ "data_freshness": "base_schedule"
+ }
+ },
+ {
+ "display_informations": {
+ "direction": "Ambérieu-en-Bugey (Ambérieu-en-Bugey)",
+ "code": "",
+ "network": "SNCF",
+ "links": [],
+ "color": "000000",
+ "name": "St-Etienne - Lyon - Ambérieu",
+ "physical_mode": "Train régional / TER",
+ "headsign": "886843",
+ "label": "St-Etienne - Lyon - Ambérieu",
+ "equipments": [],
+ "text_color": "FFFFFF",
+ "trip_short_name": "886843",
+ "commercial_mode": "TER",
+ "description": ""
+ },
+ "stop_point": {
+ "commercial_modes": [
+ {
+ "id": "commercial_mode:ter",
+ "name": "TER"
+ }
+ ],
+ "name": "Crépieux-la-Pape",
+ "links": [],
+ "physical_modes": [
+ {
+ "id": "physical_mode:LocalTrain",
+ "name": "Train régional / TER"
+ }
+ ],
+ "coord": {
+ "lat": "45.803921",
+ "lon": "4.892737"
+ },
+ "label": "Crépieux-la-Pape (Rillieux-la-Pape)",
+ "equipments": [],
+ "administrative_regions": [
+ {
+ "insee": "69286",
+ "name": "Rillieux-la-Pape",
+ "level": 8,
+ "coord": {
+ "lat": "45.823514",
+ "lon": "4.8994366"
+ },
+ "label": "Rillieux-la-Pape (69140)",
+ "id": "admin:fr:69286",
+ "zip_code": "69140"
+ }
+ ],
+ "fare_zone": {
+ "name": "0"
+ },
+ "id": "stop_point:OCE:SP:TrainTER-87723502",
+ "stop_area": {
+ "codes": [
+ {
+ "type": "CR-CI-CH",
+ "value": "0087-723502-00"
+ },
+ {
+ "type": "UIC8",
+ "value": "87723502"
+ },
+ {
+ "type": "external_code",
+ "value": "OCE87723502"
+ }
+ ],
+ "name": "Crépieux-la-Pape",
+ "links": [],
+ "coord": {
+ "lat": "45.803921",
+ "lon": "4.892737"
+ },
+ "label": "Crépieux-la-Pape (Rillieux-la-Pape)",
+ "administrative_regions": [
+ {
+ "insee": "69286",
+ "name": "Rillieux-la-Pape",
+ "level": 8,
+ "coord": {
+ "lat": "45.823514",
+ "lon": "4.8994366"
+ },
+ "label": "Rillieux-la-Pape (69140)",
+ "id": "admin:fr:69286",
+ "zip_code": "69140"
+ }
+ ],
+ "timezone": "Europe/Paris",
+ "id": "stop_area:OCE:SA:87723502"
+ }
+ },
+ "route": {
+ "direction": {
+ "embedded_type": "stop_area",
+ "stop_area": {
+ "codes": [
+ {
+ "type": "CR-CI-CH",
+ "value": "0087-743716-BV"
+ },
+ {
+ "type": "UIC8",
+ "value": "87743716"
+ },
+ {
+ "type": "external_code",
+ "value": "OCE87743716"
+ }
+ ],
+ "name": "Ambérieu-en-Bugey",
+ "links": [],
+ "coord": {
+ "lat": "45.954008",
+ "lon": "5.342313"
+ },
+ "label": "Ambérieu-en-Bugey (Ambérieu-en-Bugey)",
+ "timezone": "Europe/Paris",
+ "id": "stop_area:OCE:SA:87743716"
+ },
+ "quality": 0,
+ "name": "Ambérieu-en-Bugey (Ambérieu-en-Bugey)",
+ "id": "stop_area:OCE:SA:87743716"
+ },
+ "name": "St-Etienne-Châteaucreux vers Ambérieu-en-Bugey (Train TER)",
+ "links": [],
+ "physical_modes": [
+ {
+ "id": "physical_mode:LocalTrain",
+ "name": "Train régional / TER"
+ }
+ ],
+ "is_frequence": "False",
+ "geojson": {
+ "type": "MultiLineString",
+ "coordinates": []
+ },
+ "direction_type": "forward",
+ "line": {
+ "code": "",
+ "name": "St-Etienne - Lyon - Ambérieu",
+ "links": [],
+ "color": "000000",
+ "geojson": {
+ "type": "MultiLineString",
+ "coordinates": []
+ },
+ "text_color": "FFFFFF",
+ "physical_modes": [
+ {
+ "id": "physical_mode:LocalTrain",
+ "name": "Train régional / TER"
+ }
+ ],
+ "codes": [],
+ "closing_time": "221200",
+ "opening_time": "053500",
+ "commercial_mode": {
+ "id": "commercial_mode:ter",
+ "name": "TER"
+ },
+ "id": "line:OCE:199"
+ },
+ "id": "route:OCE:199-TrainTER-87726000-87743716"
+ },
+ "links": [
+ {
+ "type": "line",
+ "id": "line:OCE:199"
+ },
+ {
+ "type": "vehicle_journey",
+ "id": "vehicle_journey:OCE:SN886843F24024_dst_1"
+ },
+ {
+ "type": "route",
+ "id": "route:OCE:199-TrainTER-87726000-87743716"
+ },
+ {
+ "type": "commercial_mode",
+ "id": "commercial_mode:ter"
+ },
+ {
+ "type": "physical_mode",
+ "id": "physical_mode:LocalTrain"
+ },
+ {
+ "type": "network",
+ "id": "network:sncf"
+ }
+ ],
+ "stop_date_time": {
+ "links": [],
+ "arrival_date_time": "20210218T171800",
+ "additional_informations": [],
+ "departure_date_time": "20210218T171800",
+ "base_arrival_date_time": "20210218T171800",
+ "base_departure_date_time": "20210218T171800",
+ "data_freshness": "base_schedule"
+ }
+ },
+ {
+ "display_informations": {
+ "direction": "St-Etienne-Châteaucreux (Saint-Étienne)",
+ "code": "",
+ "network": "SNCF",
+ "links": [],
+ "color": "000000",
+ "name": "St-Etienne - Lyon - Ambérieu",
+ "physical_mode": "Train régional / TER",
+ "headsign": "886738",
+ "label": "St-Etienne - Lyon - Ambérieu",
+ "equipments": [],
+ "text_color": "FFFFFF",
+ "trip_short_name": "886738",
+ "commercial_mode": "TER",
+ "description": ""
+ },
+ "stop_point": {
+ "commercial_modes": [
+ {
+ "id": "commercial_mode:ter",
+ "name": "TER"
+ }
+ ],
+ "name": "Crépieux-la-Pape",
+ "links": [],
+ "physical_modes": [
+ {
+ "id": "physical_mode:LocalTrain",
+ "name": "Train régional / TER"
+ }
+ ],
+ "coord": {
+ "lat": "45.803921",
+ "lon": "4.892737"
+ },
+ "label": "Crépieux-la-Pape (Rillieux-la-Pape)",
+ "equipments": [],
+ "administrative_regions": [
+ {
+ "insee": "69286",
+ "name": "Rillieux-la-Pape",
+ "level": 8,
+ "coord": {
+ "lat": "45.823514",
+ "lon": "4.8994366"
+ },
+ "label": "Rillieux-la-Pape (69140)",
+ "id": "admin:fr:69286",
+ "zip_code": "69140"
+ }
+ ],
+ "fare_zone": {
+ "name": "0"
+ },
+ "id": "stop_point:OCE:SP:TrainTER-87723502",
+ "stop_area": {
+ "codes": [
+ {
+ "type": "CR-CI-CH",
+ "value": "0087-723502-00"
+ },
+ {
+ "type": "UIC8",
+ "value": "87723502"
+ },
+ {
+ "type": "external_code",
+ "value": "OCE87723502"
+ }
+ ],
+ "name": "Crépieux-la-Pape",
+ "links": [],
+ "coord": {
+ "lat": "45.803921",
+ "lon": "4.892737"
+ },
+ "label": "Crépieux-la-Pape (Rillieux-la-Pape)",
+ "administrative_regions": [
+ {
+ "insee": "69286",
+ "name": "Rillieux-la-Pape",
+ "level": 8,
+ "coord": {
+ "lat": "45.823514",
+ "lon": "4.8994366"
+ },
+ "label": "Rillieux-la-Pape (69140)",
+ "id": "admin:fr:69286",
+ "zip_code": "69140"
+ }
+ ],
+ "timezone": "Europe/Paris",
+ "id": "stop_area:OCE:SA:87723502"
+ }
+ },
+ "route": {
+ "direction": {
+ "embedded_type": "stop_area",
+ "stop_area": {
+ "codes": [
+ {
+ "type": "CR-CI-CH",
+ "value": "0087-726000-BV"
+ },
+ {
+ "type": "UIC8",
+ "value": "87726000"
+ },
+ {
+ "type": "external_code",
+ "value": "OCE87726000"
+ }
+ ],
+ "name": "St-Etienne-Châteaucreux",
+ "links": [],
+ "coord": {
+ "lat": "45.443382",
+ "lon": "4.399996"
+ },
+ "label": "St-Etienne-Châteaucreux (Saint-Étienne)",
+ "timezone": "Europe/Paris",
+ "id": "stop_area:OCE:SA:87726000"
+ },
+ "quality": 0,
+ "name": "St-Etienne-Châteaucreux (Saint-Étienne)",
+ "id": "stop_area:OCE:SA:87726000"
+ },
+ "name": "Ambérieu-en-Bugey vers St-Etienne-Châteaucreux (Train TER)",
+ "links": [],
+ "physical_modes": [
+ {
+ "id": "physical_mode:LocalTrain",
+ "name": "Train régional / TER"
+ }
+ ],
+ "is_frequence": "False",
+ "geojson": {
+ "type": "MultiLineString",
+ "coordinates": []
+ },
+ "direction_type": "backward",
+ "line": {
+ "code": "",
+ "name": "St-Etienne - Lyon - Ambérieu",
+ "links": [],
+ "color": "000000",
+ "geojson": {
+ "type": "MultiLineString",
+ "coordinates": []
+ },
+ "text_color": "FFFFFF",
+ "physical_modes": [
+ {
+ "id": "physical_mode:LocalTrain",
+ "name": "Train régional / TER"
+ }
+ ],
+ "codes": [],
+ "closing_time": "221200",
+ "opening_time": "053500",
+ "commercial_mode": {
+ "id": "commercial_mode:ter",
+ "name": "TER"
+ },
+ "id": "line:OCE:199"
+ },
+ "id": "route:OCE:199-TrainTER-87743716-87726000"
+ },
+ "links": [
+ {
+ "type": "line",
+ "id": "line:OCE:199"
+ },
+ {
+ "type": "vehicle_journey",
+ "id": "vehicle_journey:OCE:SN886738F27027_dst_1"
+ },
+ {
+ "type": "route",
+ "id": "route:OCE:199-TrainTER-87743716-87726000"
+ },
+ {
+ "type": "commercial_mode",
+ "id": "commercial_mode:ter"
+ },
+ {
+ "type": "physical_mode",
+ "id": "physical_mode:LocalTrain"
+ },
+ {
+ "type": "network",
+ "id": "network:sncf"
+ }
+ ],
+ "stop_date_time": {
+ "links": [],
+ "arrival_date_time": "20210218T174100",
+ "additional_informations": [],
+ "departure_date_time": "20210218T174100",
+ "base_arrival_date_time": "20210218T174100",
+ "base_departure_date_time": "20210218T174100",
+ "data_freshness": "base_schedule"
+ }
+ }
+ ],
+ "context": {
+ "timezone": "Europe/Paris",
+ "current_datetime": "20210218T125549"
+ },
+ "exceptions": []
+}