idfm-api/types/section.go

236 lines
8.5 KiB
Go

package types
import (
"encoding/json"
"fmt"
"time"
"github.com/twpayne/go-geom"
"github.com/twpayne/go-geom/encoding/geojson"
)
// A Section holds information about a specific section
type Section struct {
Type SectionType
ID ID
Mode string
From Container
To Container
Departure time.Time // Departure time
Arrival time.Time // Arrival time
Duration time.Duration // Duration of travel
Path []PathSegment // The path taken by this section
Geo *geom.LineString // The path in geojson format
StopTimes []StopTime // List of the stop times of this section
Display Display // Information to display
Additional []PTMethod // Additional informations, from what I can see this is always a PTMethod
}
// jsonSection define the JSON implementation of Section types
// We define some of the value as pointers to the real values,
// allowing us to bypass copying in cases where we don't need to process the data.
type jsonSection struct {
// Pointers to the corresponding real values
Type *SectionType `json:"type"`
ID *ID `json:"id"`
From *Container `json:"from"`
To *Container `json:"to"`
Mode *string `json:"mode"`
StopTimes *[]StopTime `json:"stop_date_times"`
Display *Display `json:"display_informations"`
Additional *[]PTMethod `json:"additional_informations"`
Path *[]PathSegment `json:"path"`
// Values to process
Departure string `json:"departure_date_time"`
Arrival string `json:"arrival_date_time"`
Duration int64 `json:"duration"`
Geo *geojson.Geometry `json:"geojson"`
}
// A SectionType codifies the type of section that can be encountered
type SectionType string
// These are the types of sections that can be returned from the API
const (
// Public transport section
SectionPublicTransport SectionType = "public_transport"
// Street section
SectionStreetNetwork SectionType = "street_network"
// Waiting section between transport
SectionWaiting SectionType = "waiting"
// This “stay in the vehicle” section occurs when the traveller has to stay in the vehicle when the bus change its routing.
SectionStayIn SectionType = "stay_in"
// Transfer section
SectionTransfer SectionType = "transfer"
// Teleportation section. Used when starting or arriving to a city or a stoparea (“potato shaped” objects) Useful to make navitia idempotent.
// Warning: Be careful: no Path nor Geo items in this case
SectionCrowFly SectionType = "crow_fly"
// Vehicle may not drive along: traveler will have to call agency to confirm journey
// Also sometimes called ODT
SectionOnDemandTransport SectionType = "on_demand_transport"
// Taking a bike from a bike sharing system (bss)
SectionBikeShareRent SectionType = "bss_rent"
// Putting back a bike from a bike sharing system (bss)
SectionBikeSharePutBack SectionType = "bss_put_back"
// Boarding on plane
SectionBoarding SectionType = "boarding"
// Landing off the plane
SectionLanding SectionType = "landing"
)
// SectionTypes is the type of a section
var SectionTypes = map[SectionType]string{
SectionPublicTransport: "Public transport section",
SectionStreetNetwork: "Street section",
SectionWaiting: "Waiting section between transport",
SectionStayIn: "This “stay in the vehicle” section occurs when the traveller has to stay in the vehicle when the bus change its routing.",
SectionTransfer: "Transfer section",
SectionCrowFly: "Teleportation section. Used when starting or arriving to a city or a stoparea (“potato shaped” objects) Useful to make navitia idempotent",
SectionOnDemandTransport: "Vehicle may not drive along: traveler will have to call agency to confirm journey",
SectionBikeShareRent: "Taking a bike from a bike sharing system (bss)",
SectionBikeSharePutBack: "Putting back a bike from a bike sharing system (bss)",
SectionBoarding: "Boarding on plane",
SectionLanding: "Landing off the plane",
}
// A StopTime stores info about a stop in a route: when the vehicle comes in, when it comes out, and what stop it is.
type StopTime struct {
// The PTDateTime of the stop, this stores the info about the arrival & departure
PTDateTime PTDateTime
StopPoint StopPoint `json:"stop_point"` // The stop point in question
DropOffAllowed bool `json:"drop_off_allowed"`
UTCDepartureTime string `json:"utc_departure_time"`
Headsign string `json:"headsign"`
UTCArrivalTime string `json:"utc_arrival_time"`
PickupAllowed bool `json:"pickup_allowed"`
DepartureTime string `json:"departure_time"`
}
// A PTMethod is a Public Transportation method: it can be regular, estimated times or ODT (on-demand transport)
type PTMethod string
// PTMethodXXX codes for known PTMethod
const (
// PTMethodRegular: No on-demand transport. Line does not contain any estimated stop times, nor zonal stop point location. No need to call too.
PTMethodRegular PTMethod = "regular"
// PTMethodDateTimeEstimated: No on-demand transport. However, line has at least one estimated date time.
PTMethodDateTimeEstimated PTMethod = "had_date_time_estimated"
// PTMethodODTStopTime: Line does not contain any estimated stop times, nor zonal stop point location. But you will have to call to take it.
PTMethodODTStopTime PTMethod = "odt_with_stop_time"
// PTMethodODTStopPoint: Line can contain some estimated stop times, but no zonal stop point location. And you will have to call to take it.
PTMethodODTStopPoint PTMethod = "odt_with_stop_point"
// PTMethodODTZone: Line can contain some estimated stop times, and zonal stop point location. And you will have to call to take it. Well, not really a public transport line, more a cab…
PTMethodODTZone PTMethod = "odt_with_zone"
)
/*
UnmarshalJSON implements json.Unmarshaller for a Section
Behaviour:
- If "from" is empty, then don't populate the From field.
- Same for "to"
*/
func (s *Section) UnmarshalJSON(b []byte) error {
data := &jsonSection{
Type: &s.Type,
ID: &s.ID,
From: &s.From,
To: &s.To,
Mode: &s.Mode,
Display: &s.Display,
Additional: &s.Additional,
StopTimes: &s.StopTimes,
Path: &s.Path,
}
// Now unmarshall the raw data into the analogous structure
err := json.Unmarshal(b, data)
if err != nil {
return fmt.Errorf("error while unmarshalling Section: %w", err)
}
// Create the error generator
gen := unmarshalErrorMaker{"Section", b}
// For departure and arrival, we use parseDateTime
s.Departure, err = parseDateTime(data.Departure)
if err != nil {
return gen.err(err, "Departure", "departure_date_time", data.Departure, "parseDateTime failed")
}
s.Arrival, err = parseDateTime(data.Arrival)
if err != nil {
return gen.err(err, "Arrival", "arrival_date_time", data.Arrival, "parseDateTime failed")
}
// As the given duration is in second, let's multiply it by one second to have the correct value
s.Duration = time.Duration(data.Duration) * time.Second
// Now let's deal with the geom
if data.Geo != nil {
// Catch an error !
if data.Geo.Coordinates == nil {
return gen.err(nil, "Geo", "geojson", data.Geo, "Geo.Coordinates is nil, can't continue as that will cause a panic")
}
// Let's decode it
geot, err := data.Geo.Decode()
if err != nil {
return gen.err(err, "Geo", "geojson", data.Geo, "Geo.Decode() failed")
}
// And let's assert the type
geo, ok := geot.(*geom.LineString)
if !ok {
return gen.err(err, "Geo", "geojson", data.Geo, "Geo type assertion failed!")
}
// Now let's assign it
s.Geo = geo
}
return nil
}
// UnmarshalJSON implements json.Unmarshaller for a PTDateTime
func (ptdt *PTDateTime) UnmarshalJSON(b []byte) error {
// First let's create the analogous structure
data := &struct {
Departure string `json:"departure_date_time"`
Arrival string `json:"arrival_date_time"`
}{}
// Now unmarshall the raw data into the analogous structure
err := json.Unmarshal(b, data)
if err != nil {
return fmt.Errorf("error while unmarshalling PTDateTime: %w", err)
}
// Create the error generator
gen := unmarshalErrorMaker{"PTDateTime", b}
// Now we use parseDateTime
ptdt.Departure, err = parseDateTime(data.Departure)
if err != nil {
return gen.err(err, "Departure", "departure_date_time", data.Departure, "parseDateTime failed")
}
ptdt.Arrival, err = parseDateTime(data.Arrival)
if err != nil {
return gen.err(err, "Arrival", "arrival_date_time", data.Arrival, "parseDateTime failed")
}
return nil
}