123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062 |
- // SPDX-FileCopyrightText: Adam Evyčędo
- //
- // SPDX-License-Identifier: AGPL-3.0-or-later
- package api
- // TODO(BAF10) direction (0|1) to const (TO|BACK)
- import (
- "apiote.xyz/p/szczanieckiej/traffic"
- "golang.org/x/text/language"
- "encoding/hex"
- "errors"
- "fmt"
- "log"
- "sort"
- "time"
- "git.sr.ht/~sircmpwn/go-bare"
- md "github.com/JohannesKaufmann/html-to-markdown"
- "github.com/adrg/strutil"
- "github.com/adrg/strutil/metrics"
- "github.com/dhconnelly/rtreego"
- )
- func convertTrafficAlerts(trafficAlerts []traffic.SpecificAlert) []AlertV1 {
- alerts := []AlertV1{}
- converter := md.NewConverter("", true, nil)
- for _, alert := range trafficAlerts {
- description, err := converter.ConvertString(alert.Description)
- if err != nil {
- log.Println("cannot convert html to markdown")
- description = alert.Description
- }
- a := AlertV1{
- Header: alert.Header,
- Description: description,
- Url: alert.URL,
- Cause: makeAlertCauseV1(alert),
- Effect: makeAlertEffectV1(alert),
- }
- alerts = append(alerts, a)
- }
- return alerts
- }
- func convertTrafficStop(stop traffic.Stop) StopV1 {
- s := StopV1{
- Code: stop.Code,
- Name: stop.NodeName,
- Zone: stop.Zone,
- Position: PositionV1{Lat: stop.Position.Lat, Lon: stop.Position.Lon},
- ChangeOptions: []ChangeOptionV1{},
- }
- for _, option := range stop.ChangeOptions {
- s.ChangeOptions = append(s.ChangeOptions, makeChangeOptionV1(option))
- }
- return s
- }
- func convertTrafficStopV2(stop traffic.Stop, feedID string) StopV2 {
- s := StopV2{
- Code: stop.Code,
- Name: stop.Name,
- NodeName: stop.NodeName,
- Zone: stop.Zone,
- FeedID: feedID,
- Position: PositionV1{Lat: stop.Position.Lat, Lon: stop.Position.Lon},
- ChangeOptions: []ChangeOptionV1{},
- }
- for _, option := range stop.ChangeOptions {
- s.ChangeOptions = append(s.ChangeOptions, makeChangeOptionV1(option))
- }
- return s
- }
- func convertTrafficStopStub(stopStub traffic.StopStub) StopStubV1 {
- s := StopStubV1{
- Code: stopStub.Code,
- Name: stopStub.Name,
- NodeName: stopStub.NodeName,
- Zone: stopStub.Zone,
- OnDemand: stopStub.OnDemand,
- }
- return s
- }
- func convertTrafficVehicle(vehicle traffic.VehicleStatus, context traffic.Context, t *traffic.Traffic) (VehicleV1, error) {
- var (
- line traffic.Line
- err error
- )
- if vehicle.LineID == "" {
- line, err = traffic.GetLineOld(vehicle.LineName, context, t)
- if err != nil {
- return VehicleV1{}, fmt.Errorf("while getting line %s: %w", vehicle.LineName, err)
- }
- } else {
- line, err = traffic.GetLine(vehicle.LineID, context, t)
- if err != nil {
- return VehicleV1{}, fmt.Errorf("while getting line %s: %w", vehicle.LineID, err)
- }
- }
- return VehicleV1{
- Id: string(vehicle.VehicleID),
- Position: PositionV1{vehicle.Latitude, vehicle.Longitude},
- Capabilities: t.Vehicles[context.FeedID][context.Version][vehicle.VehicleID].Capabilities,
- Speed: vehicle.Speed,
- Line: LineStubV1{Name: line.Name, Kind: makeLineTypeV1(line), Colour: fromColor(line.Colour)},
- Headsign: vehicle.Headsign,
- CongestionLevel: convertCongestionLevelV1(vehicle.CongestionLevel),
- OccupancyStatus: convertOccupancyStatusV1(vehicle.OccupancyStatus),
- }, nil
- }
- func convertTrafficVehicleV2(vehicle traffic.VehicleStatus, context traffic.Context, t *traffic.Traffic) (VehicleV2, error) {
- var (
- line traffic.Line
- err error
- )
- if vehicle.LineID == "" {
- line, err = traffic.GetLineOld(vehicle.LineName, context, t)
- if err != nil {
- return VehicleV2{}, fmt.Errorf("while getting line %s: %w", vehicle.LineName, err)
- }
- } else {
- line, err = traffic.GetLine(vehicle.LineID, context, t)
- if err != nil {
- return VehicleV2{}, fmt.Errorf("while getting line %s: %w", vehicle.LineID, err)
- }
- }
- return VehicleV2{
- Id: string(vehicle.VehicleID),
- Position: PositionV1{vehicle.Latitude, vehicle.Longitude},
- Capabilities: t.Vehicles[context.FeedID][context.Version][vehicle.VehicleID].Capabilities,
- Speed: vehicle.Speed,
- Line: LineStubV2{Name: line.Name, Kind: makeLineTypeV2(line), Colour: fromColor(line.Colour)},
- Headsign: vehicle.Headsign,
- CongestionLevel: convertCongestionLevelV1(vehicle.CongestionLevel),
- OccupancyStatus: convertOccupancyStatusV1(vehicle.OccupancyStatus),
- }, nil
- }
- func convertTrafficVehicleV3(vehicle traffic.VehicleStatus, context traffic.Context, t *traffic.Traffic) (VehicleV3, error) {
- var (
- line traffic.Line
- err error
- )
- if vehicle.LineID == "" {
- line, err = traffic.GetLineOld(vehicle.LineName, context, t)
- if err != nil {
- return VehicleV3{}, fmt.Errorf("while getting line %s: %w", vehicle.LineName, err)
- }
- } else {
- line, err = traffic.GetLine(vehicle.LineID, context, t)
- if err != nil {
- return VehicleV3{}, fmt.Errorf("while getting line %s: %w", vehicle.LineID, err)
- }
- }
- return VehicleV3{
- Id: string(vehicle.VehicleID),
- Position: PositionV1{vehicle.Latitude, vehicle.Longitude},
- Capabilities: t.Vehicles[context.FeedID][context.Version][vehicle.VehicleID].Capabilities,
- Speed: vehicle.Speed,
- Line: LineStubV3{Name: line.Name, Kind: makeLineTypeV3(line), Colour: fromColor(line.Colour)},
- Headsign: vehicle.Headsign,
- CongestionLevel: convertCongestionLevelV1(vehicle.CongestionLevel),
- OccupancyStatus: convertOccupancyStatusV1(vehicle.OccupancyStatus),
- }, nil
- }
- func getDayOffset(t1, t2 time.Time) int8 {
- midnightBefore := time.Date(t1.Year(), t1.Month(), t1.Day(), 0, 0, 0, 0, t1.Location())
- tomorrow := t1.AddDate(0, 0, 1)
- midnightAfter := time.Date(tomorrow.Year(), tomorrow.Month(), tomorrow.Day(), 0, 0, 0, 0, tomorrow.Location())
- if t2.Before(midnightBefore) {
- return -1
- } else if t2.Before(midnightAfter) {
- return 0
- } else {
- return 1
- }
- }
- func marshalStopOrder(tripOffset uint, stopOrder int) (string, error) {
- order := traffic.StopOrder{
- TripOffset: tripOffset,
- Sequence: stopOrder,
- }
- bytes, err := bare.Marshal(&order)
- return hex.EncodeToString(bytes), err
- }
- func CreateSuccessTrip(trafficTrip []traffic.TimedStopStub) /*TripResponse*/ {
- /*trip := []TimedStopStub{}
- for _, s := range trafficTrip {
- trip = append(trip, TimedStopStub{
- StopStub: StopStub{
- Code: s.Code,
- Name: s.Name,
- Zone: s.Zone,
- OnDemand: s.OnDemand,
- },
- Time: s.Time,
- })
- }
- return SuccessTrip{
- Trip: trip,
- }*/
- }
- func CreateSuccessVehicle(vehicles []traffic.Vehicle, context traffic.Context, t *traffic.Traffic) /*(VehicleResponse, error)*/ {
- /*v := make([]Vehicle, len(vehicles))
- var err error = nil
- for i, vehicle := range vehicles {
- v[i], err = convertTrafficVehicle(vehicle, context, t)
- if err != nil {
- return SuccessVehicle{}, fmt.Errorf("while converting Traffic Vehicle %s: %w", vehicle.Id, err)
- }
- }
- return SuccessVehicle{
- Vehicles: v,
- }, nil*/
- }
- func CreateSuccessQueryables(items []traffic.Queryable, context traffic.Context, t *traffic.Traffic, other QueryablesResponse, sortByDistance bool) (QueryablesResponse, error) {
- success := QueryablesResponseV1{
- Queryables: []QueryableV1{},
- }
- for _, item := range items {
- if stop, ok := item.(traffic.Stop); ok {
- s := convertTrafficStop(stop)
- success.Queryables = append(success.Queryables, QueryableV1(s))
- } else {
- continue
- }
- }
- if otherV1, ok := other.(QueryablesResponseV1); ok {
- success.Queryables = append(success.Queryables, otherV1.Queryables...)
- } else {
- return success, errors.New("wrong version of other")
- }
- return success, nil
- }
- func CreateSuccessQueryablesV2(query string, items []traffic.Queryable, context traffic.Context, t *traffic.Traffic, other QueryablesResponse, sortByDistance bool) (QueryablesResponse, error) {
- success := QueryablesResponseV2{
- Queryables: []QueryableV2{},
- }
- for _, item := range items {
- if stop, ok := item.(traffic.Stop); ok {
- s := convertTrafficStopV2(stop, context.FeedID)
- success.Queryables = append(success.Queryables, QueryableV2(s))
- } else if line, ok := item.(traffic.Line); ok {
- l := convertTrafficLine(line, context.FeedID)
- success.Queryables = append(success.Queryables, QueryableV2(l))
- } else {
- continue
- }
- }
- if otherV2, ok := other.(QueryablesResponseV2); ok {
- success.Queryables = append(success.Queryables, otherV2.Queryables...)
- } else {
- return success, errors.New("wrong version of other")
- }
- if sortByDistance {
- success.Queryables = sortQueryablesByDistanceV2(query, success.Queryables)
- } else {
- success.Queryables = sortQueryables(query, success.Queryables)
- }
- return success, nil
- }
- func CreateSuccessQueryablesV3(query string, items []traffic.Queryable, context traffic.Context, t *traffic.Traffic, other QueryablesResponse, sortByDistance bool) (QueryablesResponse, error) {
- success := QueryablesResponseV3{
- Queryables: []QueryableV3{},
- }
- for _, item := range items {
- if stop, ok := item.(traffic.Stop); ok {
- s := convertTrafficStopV2(stop, context.FeedID)
- success.Queryables = append(success.Queryables, QueryableV3(s))
- } else if line, ok := item.(traffic.Line); ok {
- l := convertTrafficLineV2(line, context.FeedID)
- success.Queryables = append(success.Queryables, QueryableV3(l))
- } else {
- continue
- }
- }
- if otherV3, ok := other.(QueryablesResponseV3); ok {
- success.Queryables = append(success.Queryables, otherV3.Queryables...)
- } else {
- return success, errors.New("wrong version of other")
- }
- if sortByDistance {
- success.Queryables = sortQueryablesByDistanceV3(query, success.Queryables)
- } else {
- success.Queryables = sortQueryablesV3(query, success.Queryables)
- }
- return success, nil
- }
- func CreateSuccessQueryablesV4(query string, items []traffic.Queryable, context traffic.Context, t *traffic.Traffic, other QueryablesResponse, sortByDistance bool) (QueryablesResponse, error) {
- success := QueryablesResponseV4{
- Queryables: []QueryableV4{},
- }
- for _, item := range items {
- if stop, ok := item.(traffic.Stop); ok {
- s := convertTrafficStopV2(stop, context.FeedID)
- success.Queryables = append(success.Queryables, QueryableV4(s))
- } else if line, ok := item.(traffic.Line); ok {
- if len(line.Headsigns) > 0 {
- l := convertTrafficLineV3(line, context.FeedID)
- success.Queryables = append(success.Queryables, QueryableV4(l))
- }
- } else {
- continue
- }
- }
- if otherV4, ok := other.(QueryablesResponseV4); ok {
- success.Queryables = append(success.Queryables, otherV4.Queryables...)
- } else {
- return success, errors.New("wrong version of other")
- }
- if sortByDistance {
- success.Queryables = sortQueryablesByDistanceV4(query, success.Queryables)
- } else {
- success.Queryables = sortQueryablesV4(query, success.Queryables)
- }
- return success, nil
- }
- func LimitQueryables(r QueryablesResponse, offset, limit uint64) QueryablesResponse {
- var result QueryablesResponse
- switch r.(type) {
- case QueryablesResponseV1:
- var queryables []QueryableV1
- if int(offset) > len(r.(QueryablesResponseV1).Queryables) {
- queryables = []QueryableV1{}
- } else if len(r.(QueryablesResponseV1).Queryables) < int(offset+limit) {
- queryables = r.(QueryablesResponseV1).Queryables[offset:]
- } else {
- queryables = r.(QueryablesResponseV1).Queryables[offset : offset+limit]
- }
- result = QueryablesResponseV1{queryables}
- case QueryablesResponseV2:
- var queryables []QueryableV2
- if int(offset) > len(r.(QueryablesResponseV2).Queryables) {
- queryables = []QueryableV2{}
- } else if len(r.(QueryablesResponseV2).Queryables) < int(offset+limit) {
- queryables = r.(QueryablesResponseV2).Queryables[offset:]
- } else {
- queryables = r.(QueryablesResponseV2).Queryables[offset : offset+limit]
- }
- result = QueryablesResponseV2{queryables}
- case QueryablesResponseV3:
- var queryables []QueryableV3
- if int(offset) > len(r.(QueryablesResponseV3).Queryables) {
- queryables = []QueryableV3{}
- } else if len(r.(QueryablesResponseV3).Queryables) < int(offset+limit) {
- queryables = r.(QueryablesResponseV3).Queryables[offset:]
- } else {
- queryables = r.(QueryablesResponseV3).Queryables[offset : offset+limit]
- }
- result = QueryablesResponseV3{queryables}
- case QueryablesResponseV4:
- var queryables []QueryableV4
- if int(offset) > len(r.(QueryablesResponseV4).Queryables) {
- queryables = []QueryableV4{}
- } else if len(r.(QueryablesResponseV4).Queryables) < int(offset+limit) {
- queryables = r.(QueryablesResponseV4).Queryables[offset:]
- } else {
- queryables = r.(QueryablesResponseV4).Queryables[offset : offset+limit]
- }
- result = QueryablesResponseV4{queryables}
- }
- return result
- }
- func sortQueryables(query string, queryables []QueryableV2) []QueryableV2 {
- // fixme query and names should be cleaned
- sort.Slice(queryables, func(i, j int) bool {
- var nameI, nameJ string
- switch queryables[i].(type) {
- case StopV2:
- nameI = queryables[i].(StopV2).Name
- case LineV1:
- nameI = queryables[i].(LineV1).Name
- }
- switch queryables[j].(type) {
- case StopV2:
- nameJ = queryables[j].(StopV2).Name
- case LineV1:
- nameJ = queryables[j].(LineV1).Name
- }
- levenshtein := &metrics.Levenshtein{
- CaseSensitive: false,
- InsertCost: 1,
- DeleteCost: 1,
- ReplaceCost: 1,
- }
- distance1 := strutil.Similarity(query, nameI, levenshtein)
- distance2 := strutil.Similarity(query, nameJ, levenshtein)
- return distance1 > distance2
- })
- return queryables
- }
- func sortQueryablesByDistanceV2(query string, queryables []QueryableV2) []QueryableV2 {
- queryPosition, err := traffic.ParsePosition(query)
- if err != nil {
- log.Printf("while parsing position %s: %v\n", query, err)
- return queryables
- }
- stops := []StopV2{}
- for _, q := range queryables {
- if s, ok := q.(StopV2); ok {
- stops = append(stops, s)
- }
- }
- tree := rtreego.NewTree(2, 1, 50)
- for _, stop := range stops {
- tree.Insert(stop)
- }
- spatials := tree.NearestNeighbors(12, rtreego.Point{queryPosition.Lat, queryPosition.Lon})
- queryables = make([]QueryableV2, len(spatials))
- for i, spatial := range spatials {
- queryables[i] = spatial.(StopV2)
- }
- return queryables
- }
- func sortQueryablesV3(query string, queryables []QueryableV3) []QueryableV3 {
- // fixme query and names should be cleaned
- sort.Slice(queryables, func(i, j int) bool {
- var nameI, nameJ string
- switch queryables[i].(type) {
- case StopV2:
- nameI = queryables[i].(StopV2).Name
- case LineV2:
- nameI = queryables[i].(LineV2).Name
- }
- switch queryables[j].(type) {
- case StopV2:
- nameJ = queryables[j].(StopV2).Name
- case LineV2:
- nameJ = queryables[j].(LineV2).Name
- }
- levenshtein := &metrics.Levenshtein{
- CaseSensitive: false,
- InsertCost: 1,
- DeleteCost: 1,
- ReplaceCost: 1,
- }
- distance1 := strutil.Similarity(query, nameI, levenshtein)
- distance2 := strutil.Similarity(query, nameJ, levenshtein)
- return distance1 > distance2
- })
- return queryables
- }
- func sortQueryablesByDistanceV3(query string, queryables []QueryableV3) []QueryableV3 {
- queryPosition, err := traffic.ParsePosition(query)
- if err != nil {
- log.Printf("while parsing position %s: %v\n", query, err)
- return queryables
- }
- stops := []StopV2{}
- for _, q := range queryables {
- if s, ok := q.(StopV2); ok {
- stops = append(stops, s)
- }
- }
- tree := rtreego.NewTree(2, 1, 50)
- for _, stop := range stops {
- tree.Insert(stop)
- }
- spatials := tree.NearestNeighbors(12, rtreego.Point{queryPosition.Lat, queryPosition.Lon})
- queryables = make([]QueryableV3, len(spatials))
- for i, spatial := range spatials {
- queryables[i] = spatial.(StopV2)
- }
- return queryables
- }
- func sortQueryablesV4(query string, queryables []QueryableV4) []QueryableV4 {
- // fixme query and names should be cleaned
- sort.Slice(queryables, func(i, j int) bool {
- var nameI, nameJ string
- switch queryables[i].(type) {
- case StopV2:
- nameI = queryables[i].(StopV2).Name
- case LineV3:
- nameI = queryables[i].(LineV3).Name
- }
- switch queryables[j].(type) {
- case StopV2:
- nameJ = queryables[j].(StopV2).Name
- case LineV3:
- nameJ = queryables[j].(LineV3).Name
- }
- levenshtein := &metrics.Levenshtein{
- CaseSensitive: false,
- InsertCost: 1,
- DeleteCost: 1,
- ReplaceCost: 1,
- }
- distance1 := strutil.Similarity(query, nameI, levenshtein)
- distance2 := strutil.Similarity(query, nameJ, levenshtein)
- return distance1 > distance2
- })
- return queryables
- }
- func sortQueryablesByDistanceV4(query string, queryables []QueryableV4) []QueryableV4 {
- queryPosition, err := traffic.ParsePosition(query)
- if err != nil {
- log.Printf("while parsing position %s: %v\n", query, err)
- return queryables
- }
- stops := []StopV2{}
- for _, q := range queryables {
- if s, ok := q.(StopV2); ok {
- stops = append(stops, s)
- }
- }
- tree := rtreego.NewTree(2, 1, 50)
- for _, stop := range stops {
- tree.Insert(stop)
- }
- spatials := tree.NearestNeighbors(12, rtreego.Point{queryPosition.Lat, queryPosition.Lon})
- queryables = make([]QueryableV4, len(spatials))
- for i, spatial := range spatials {
- queryables[i] = spatial.(StopV2)
- }
- return queryables
- }
- func CreateSuccessLocatables(locatables []traffic.Locatable, context traffic.Context, t *traffic.Traffic, other LocatablesResponse) (LocatablesResponse, error) {
- success := LocatablesResponseV1{
- Locatables: []LocatableV1{},
- }
- for _, locatable := range locatables {
- if stop, ok := locatable.(traffic.Stop); ok {
- s := convertTrafficStop(stop)
- success.Locatables = append(success.Locatables, LocatableV1(s))
- } else if vehicle, ok := locatable.(traffic.VehicleStatus); ok {
- v, err := convertTrafficVehicle(vehicle, context, t)
- if err != nil {
- return success, fmt.Errorf("while converting Traffic Vehicle %s: %w", vehicle.VehicleID, err)
- }
- success.Locatables = append(success.Locatables, LocatableV1(v))
- }
- }
- if otherV1, ok := other.(LocatablesResponseV1); ok {
- success.Locatables = append(success.Locatables, otherV1.Locatables...)
- } else {
- return success, errors.New("wrong version of other")
- }
- return success, nil
- }
- func CreateSuccessLocatablesV2(locatables []traffic.Locatable, context traffic.Context, t *traffic.Traffic, other LocatablesResponse) (LocatablesResponse, error) {
- success := LocatablesResponseV2{
- Locatables: []LocatableV2{},
- }
- for _, locatable := range locatables {
- if stop, ok := locatable.(traffic.Stop); ok {
- s := convertTrafficStopV2(stop, context.FeedID)
- success.Locatables = append(success.Locatables, LocatableV2(s))
- } else if vehicle, ok := locatable.(traffic.VehicleStatus); ok {
- v, err := convertTrafficVehicleV2(vehicle, context, t)
- if err != nil {
- return success, fmt.Errorf("while converting Traffic Vehicle %s: %w", vehicle.VehicleID, err)
- }
- success.Locatables = append(success.Locatables, LocatableV2(v))
- }
- }
- if otherV2, ok := other.(LocatablesResponseV2); ok {
- success.Locatables = append(success.Locatables, otherV2.Locatables...)
- } else {
- return success, errors.New("wrong version of other")
- }
- return success, nil
- }
- func CreateSuccessLocatablesV3(locatables []traffic.Locatable, context traffic.Context, t *traffic.Traffic, other LocatablesResponse) (LocatablesResponse, error) {
- success := LocatablesResponseV3{
- Locatables: []LocatableV3{},
- }
- for _, locatable := range locatables {
- if stop, ok := locatable.(traffic.Stop); ok {
- s := convertTrafficStopV2(stop, context.FeedID)
- success.Locatables = append(success.Locatables, LocatableV3(s))
- } else if vehicle, ok := locatable.(traffic.VehicleStatus); ok {
- v, err := convertTrafficVehicleV3(vehicle, context, t)
- if err != nil {
- return success, fmt.Errorf("while converting Traffic Vehicle %s: %w", vehicle.VehicleID, err)
- }
- success.Locatables = append(success.Locatables, LocatableV3(v))
- }
- }
- if otherV3, ok := other.(LocatablesResponseV3); ok {
- success.Locatables = append(success.Locatables, otherV3.Locatables...)
- } else {
- return success, errors.New("wrong version of other")
- }
- return success, nil
- }
- func convertVehicleStatusV1(status traffic.DepartureStatus, timeToArrival float64) VehicleStatusV1 {
- if status == traffic.AT_STOP && timeToArrival < 1 { // FIXME this shouldn't happen, this should be caught in gtfs-rt processing
- return STATUS_AT_STOP
- } else if timeToArrival < 0 {
- return STATUS_DEPARTED
- } else if timeToArrival < 1 {
- return STATUS_INCOMING
- }
- return STATUS_IN_TRANSIT
- }
- func convertCongestionLevelV1(level traffic.CongestionLevel) CongestionLevelV1 {
- switch level {
- case traffic.CONGESTION_UNKNOWN:
- return CONGESTION_UNKNOWN
- case traffic.CONGESTION_SMOOTH:
- return CONGESTION_SMOOTH
- case traffic.CONGESTION_STOP_AND_GO:
- return CONGESTION_STOP_AND_GO
- case traffic.CONGESTION_SIGNIFICANT:
- return CONGESTION_SIGNIFICANT
- case traffic.CONGESTION_SEVERE:
- return CONGESTION_SEVERE
- default:
- return CONGESTION_UNKNOWN
- }
- }
- func convertOccupancyStatusV1(status traffic.OccupancyStatus) OccupancyStatusV1 {
- switch status {
- case traffic.OCCUPANCY_UNKNOWN:
- return OCCUPANCY_UNKNOWN
- case traffic.OCCUPANCY_EMPTY:
- return OCCUPANCY_EMPTY
- case traffic.OCCUPANCY_MANY_AVAILABLE:
- return OCCUPANCY_MANY_AVAILABLE
- case traffic.OCCUPANCY_FEW_AVAILABLE:
- return OCCUPANCY_FEW_AVAILABLE
- case traffic.OCCUPANCY_STANDING_ONLY:
- return OCCUPANCY_STANDING_ONLY
- case traffic.OCCUPANCY_CRUSHED:
- return OCCUPANCY_CRUSHED
- case traffic.OCCUPANCY_FULL:
- return OCCUPANCY_FULL
- case traffic.OCCUPANCY_NOT_ACCEPTING:
- return OCCUPANCY_NOT_ACCEPTING
- default:
- return OCCUPANCY_UNKNOWN
- }
- }
- func CreateSuccessDeparturesV1(stop traffic.Stop, departures []traffic.DepartureRealtime, date time.Time, vehicles map[string]traffic.Vehicle, alerts []traffic.SpecificAlert, ctx traffic.Context, t *traffic.Traffic, accept map[uint]struct{}, preferredLanguages []language.Tag) (DeparturesResponse, error) {
- d := []DepartureV1{}
- var success DeparturesResponse
- now := time.Now()
- timezone, err := traffic.GetTimezone(stop, t, ctx.FeedID)
- if err != nil {
- return success, err
- }
- datetime := time.Date(date.Year(), date.Month(),
- date.Day(), now.Hour(), now.Minute(), now.Second(), 0, now.Location()).In(timezone)
- for _, trafficDeparture := range departures {
- zoneAbbr := trafficDeparture.Time.Location().String()
- stopOrder, err := marshalStopOrder(trafficDeparture.Order.TripOffset, trafficDeparture.Order.Sequence)
- if err != nil {
- return success, err
- }
- vehicle, err := convertTrafficVehicle(trafficDeparture.Update.VehicleStatus, ctx, t)
- if err != nil {
- return success, fmt.Errorf("while converting vehicle status: %w", err)
- }
- departureTime := traffic.GetTimeWithDelay(trafficDeparture)
- departure := DepartureV1{
- Id: stopOrder,
- Time: TimeV1{
- DayOffset: getDayOffset(datetime, departureTime),
- Hour: uint8(departureTime.Hour()),
- Minute: uint8(departureTime.Minute()),
- Second: uint8(departureTime.Second()),
- Zone: zoneAbbr,
- },
- Status: STATUS_IN_TRANSIT,
- IsRealtime: trafficDeparture.Update.TimetableRelationship != traffic.NO_TRIP_DATA && trafficDeparture.Update.TimetableRelationship != traffic.NOT_REALTIME,
- Vehicle: vehicle,
- Boarding: makeBoardingV1(trafficDeparture.Departure.Pickup, trafficDeparture.Departure.Dropoff),
- }
- timeToArrival := departureTime.Sub(datetime).Minutes()
- if departure.IsRealtime {
- departure.Status = convertVehicleStatusV1(trafficDeparture.Update.VehicleStatus.Status, timeToArrival)
- }
- d = append(d, departure)
- }
- success = DeparturesResponseV1{
- Stop: convertTrafficStop(stop),
- Departures: d,
- Alerts: convertTrafficAlerts(alerts),
- }
- return success, nil
- }
- func CreateSuccessDeparturesV2(stop traffic.Stop, departures []traffic.DepartureRealtime, date time.Time, vehicles map[string]traffic.Vehicle, alerts []traffic.SpecificAlert, ctx traffic.Context, t *traffic.Traffic, accept map[uint]struct{}, preferredLanguages []language.Tag) (DeparturesResponse, error) {
- d := []DepartureV2{}
- var success DeparturesResponse
- now := time.Now()
- timezone, err := traffic.GetTimezone(stop, t, ctx.FeedID)
- if err != nil {
- return success, err
- }
- datetime := time.Date(date.Year(), date.Month(),
- date.Day(), now.Hour(), now.Minute(), now.Second(), 0, now.Location()).In(timezone)
- for _, trafficDeparture := range departures {
- zoneAbbr := trafficDeparture.Time.Location().String()
- stopOrder, err := marshalStopOrder(trafficDeparture.Order.TripOffset, trafficDeparture.Order.Sequence)
- if err != nil {
- return success, err
- }
- vehicle, err := convertTrafficVehicleV2(trafficDeparture.Update.VehicleStatus, ctx, t)
- if err != nil {
- return success, fmt.Errorf("while converting vehicle status: %w", err)
- }
- departureTime := traffic.GetTimeWithDelay(trafficDeparture)
- departure := DepartureV2{
- Id: stopOrder,
- Time: TimeV1{
- DayOffset: getDayOffset(datetime, departureTime),
- Hour: uint8(departureTime.Hour()),
- Minute: uint8(departureTime.Minute()),
- Second: uint8(departureTime.Second()),
- Zone: zoneAbbr,
- },
- Status: STATUS_IN_TRANSIT,
- IsRealtime: trafficDeparture.Update.TimetableRelationship != traffic.NO_TRIP_DATA && trafficDeparture.Update.TimetableRelationship != traffic.NOT_REALTIME,
- Vehicle: vehicle,
- Boarding: makeBoardingV1(trafficDeparture.Departure.Pickup, trafficDeparture.Departure.Dropoff),
- }
- timeToArrival := departureTime.Sub(datetime).Minutes()
- if departure.IsRealtime {
- departure.Status = convertVehicleStatusV1(trafficDeparture.Update.VehicleStatus.Status, timeToArrival)
- }
- d = append(d, departure)
- }
- success = DeparturesResponseV2{
- Stop: convertTrafficStopV2(stop, ctx.FeedID),
- Departures: d,
- Alerts: convertTrafficAlerts(alerts),
- }
- return success, nil
- }
- func CreateSuccessDeparturesV3(stop traffic.Stop, departures []traffic.DepartureRealtime, date time.Time, vehicles map[string]traffic.Vehicle, alerts []traffic.SpecificAlert, ctx traffic.Context, t *traffic.Traffic, accept map[uint]struct{}, preferredLanguages []language.Tag) (DeparturesResponse, error) {
- d := []DepartureV3{}
- var success DeparturesResponse
- now := time.Now()
- timezone, err := traffic.GetTimezone(stop, t, ctx.FeedID)
- if err != nil {
- return success, err
- }
- datetime := time.Date(date.Year(), date.Month(),
- date.Day(), now.Hour(), now.Minute(), now.Second(), 0, now.Location()).In(timezone)
- for _, trafficDeparture := range departures {
- zoneAbbr := trafficDeparture.Time.Location().String()
- stopOrder, err := marshalStopOrder(trafficDeparture.Order.TripOffset, trafficDeparture.Order.Sequence)
- if err != nil {
- return success, err
- }
- vehicle, err := convertTrafficVehicleV3(trafficDeparture.Update.VehicleStatus, ctx, t)
- if err != nil {
- return success, fmt.Errorf("while converting vehicle status: %w", err)
- }
- departureTime := traffic.GetTimeWithDelay(trafficDeparture)
- departure := DepartureV3{
- Id: stopOrder,
- Time: TimeV1{
- DayOffset: getDayOffset(datetime, departureTime),
- Hour: uint8(departureTime.Hour()),
- Minute: uint8(departureTime.Minute()),
- Second: uint8(departureTime.Second()),
- Zone: zoneAbbr,
- },
- Status: STATUS_IN_TRANSIT,
- IsRealtime: trafficDeparture.Update.TimetableRelationship != traffic.NO_TRIP_DATA && trafficDeparture.Update.TimetableRelationship != traffic.NOT_REALTIME,
- Vehicle: vehicle,
- Boarding: makeBoardingV1(trafficDeparture.Departure.Pickup, trafficDeparture.Departure.Dropoff),
- }
- timeToArrival := departureTime.Sub(datetime).Minutes()
- if departure.IsRealtime {
- departure.Status = convertVehicleStatusV1(trafficDeparture.Update.VehicleStatus.Status, timeToArrival)
- }
- d = append(d, departure)
- }
- success = DeparturesResponseV3{
- Stop: convertTrafficStopV2(stop, ctx.FeedID),
- Departures: d,
- Alerts: convertTrafficAlerts(alerts),
- }
- return success, nil
- }
- func CreateSuccessDeparturesV4(stop traffic.Stop, departures []traffic.DepartureRealtime, date time.Time, vehicles map[string]traffic.Vehicle, alerts []traffic.SpecificAlert, ctx traffic.Context, t *traffic.Traffic, accept map[uint]struct{}, preferredLanguages []language.Tag) (DeparturesResponse, error) {
- d := []DepartureV4{}
- var success DeparturesResponse
- now := time.Now()
- timezone, err := traffic.GetTimezone(stop, t, ctx.FeedID)
- if err != nil {
- return success, err
- }
- datetime := time.Date(date.Year(), date.Month(),
- date.Day(), now.Hour(), now.Minute(), now.Second(), 0, now.Location()).In(timezone)
- for _, trafficDeparture := range departures {
- zoneAbbr := trafficDeparture.Time.Location().String()
- stopOrder, err := marshalStopOrder(trafficDeparture.Order.TripOffset, trafficDeparture.Order.Sequence)
- if err != nil {
- return success, err
- }
- vehicle, err := convertTrafficVehicleV3(trafficDeparture.Update.VehicleStatus, ctx, t)
- if err != nil {
- return success, fmt.Errorf("while converting vehicle status: %w", err)
- }
- departureTime := traffic.GetTimeWithDelay(trafficDeparture)
- departure := DepartureV4{
- Id: stopOrder,
- Time: TimeV1{
- DayOffset: getDayOffset(datetime, departureTime),
- Hour: uint8(departureTime.Hour()),
- Minute: uint8(departureTime.Minute()),
- Second: uint8(departureTime.Second()),
- Zone: zoneAbbr,
- },
- Status: STATUS_IN_TRANSIT,
- IsRealtime: trafficDeparture.Update.TimetableRelationship != traffic.NO_TRIP_DATA && trafficDeparture.Update.TimetableRelationship != traffic.NOT_REALTIME,
- Vehicle: vehicle,
- Boarding: makeBoardingV1(trafficDeparture.Departure.Pickup, trafficDeparture.Departure.Dropoff),
- Alerts: convertTrafficAlerts(trafficDeparture.Alerts),
- }
- timeToArrival := departureTime.Sub(datetime).Minutes()
- if departure.IsRealtime {
- departure.Status = convertVehicleStatusV1(trafficDeparture.Update.VehicleStatus.Status, timeToArrival)
- }
- d = append(d, departure)
- }
- success = DeparturesResponseV4{
- Stop: convertTrafficStopV2(stop, ctx.FeedID),
- Departures: d,
- Alerts: convertTrafficAlerts(alerts),
- }
- return success, nil
- }
- func CreateSuccessDeparturesDev(stop traffic.Stop, departures []traffic.DepartureRealtime, date time.Time, vehicles map[string]traffic.Vehicle, alerts []traffic.SpecificAlert, ctx traffic.Context, t *traffic.Traffic, accept map[uint]struct{}, preferredLanguages []language.Tag) (DeparturesResponse, error) {
- d := []DepartureV4{}
- var success DeparturesResponse
- now := time.Now()
- timezone, err := traffic.GetTimezone(stop, t, ctx.FeedID)
- if err != nil {
- return success, err
- }
- datetime := time.Date(date.Year(), date.Month(),
- date.Day(), now.Hour(), now.Minute(), now.Second(), 0, now.Location()).In(timezone)
- for _, trafficDeparture := range departures {
- zoneAbbr := trafficDeparture.Time.Location().String()
- stopOrder, err := marshalStopOrder(trafficDeparture.Order.TripOffset, trafficDeparture.Order.Sequence)
- if err != nil {
- return success, err
- }
- vehicle, err := convertTrafficVehicleV3(trafficDeparture.Update.VehicleStatus, ctx, t)
- if err != nil {
- return success, fmt.Errorf("while converting vehicle status: %w", err)
- }
- departureTime := traffic.GetTimeWithDelay(trafficDeparture)
- departure := DepartureV4{
- Id: stopOrder,
- Time: TimeV1{
- DayOffset: getDayOffset(datetime, departureTime),
- Hour: uint8(departureTime.Hour()),
- Minute: uint8(departureTime.Minute()),
- Second: uint8(departureTime.Second()),
- Zone: zoneAbbr,
- },
- Status: STATUS_IN_TRANSIT,
- IsRealtime: trafficDeparture.Update.TimetableRelationship != traffic.NO_TRIP_DATA && trafficDeparture.Update.TimetableRelationship != traffic.NOT_REALTIME,
- Vehicle: vehicle,
- Boarding: makeBoardingV1(trafficDeparture.Departure.Pickup, trafficDeparture.Departure.Dropoff),
- Alerts: convertTrafficAlerts(trafficDeparture.Alerts),
- }
- timeToArrival := departureTime.Sub(datetime).Minutes()
- if departure.IsRealtime {
- departure.Status = convertVehicleStatusV1(trafficDeparture.Update.VehicleStatus.Status, timeToArrival)
- }
- d = append(d, departure)
- }
- success = DeparturesResponseDev{
- Stop: convertTrafficStopV2(stop, ctx.FeedID),
- Departures: d,
- Alerts: convertTrafficAlerts(alerts),
- }
- return success, nil
- }
- func fromColor(c traffic.Colour) ColourV1 {
- return ColourV1{
- R: c.R,
- G: c.G,
- B: c.B,
- }
- }
- func makeLineTypeV1(line traffic.Line) LineTypeV1 {
- if line.Kind == traffic.TRAM {
- return TRAM
- } else if line.Kind == traffic.BUS {
- return BUS
- } else {
- return LINE_UNKNOWN
- }
- }
- func makeLineTypeV2(line traffic.Line) LineTypeV2 {
- if line.Kind == traffic.TRAM {
- return LINE_V2_TRAM
- } else if line.Kind == traffic.BUS {
- return LINE_V2_BUS
- } else if line.Kind == traffic.TROLLEYBUS {
- return LINE_V2_TROLLEYBUS
- } else {
- return LINE_V2_UNKNOWN
- }
- }
- func makeLineTypeV3(line traffic.Line) LineTypeV3 {
- switch line.Kind {
- case traffic.TRAM:
- return LINE_V3_TRAM
- case traffic.BUS:
- return LINE_V3_BUS
- case traffic.TROLLEYBUS:
- return LINE_V3_TROLLEYBUS
- case traffic.METRO:
- return LINE_V3_METRO
- case traffic.RAIL:
- return LINE_V3_RAIL
- case traffic.FERRY:
- return LINE_V3_FERRY
- case traffic.CABLE_TRAM:
- return LINE_V3_CABLE_TRAM
- case traffic.CABLE_CAR:
- return LINE_V3_CABLE_CAR
- case traffic.FUNICULAR:
- return LINE_V3_FUNICULAR
- case traffic.MONORAIL:
- return LINE_V3_MONORAIL
- default:
- return LINE_V3_UNKNOWN
- }
- }
- func makeChangeOptionV1(option traffic.ChangeOption) ChangeOptionV1 {
- return ChangeOptionV1{
- LineName: option.LineName,
- Headsign: option.Headsign,
- }
- }
- func makeBoardingV1(pickup, dropoff traffic.Boarding) uint8 {
- b := BOARDING_NONE
- if pickup == traffic.REGULAR {
- b |= ONBOARDING_REGULAR
- } else if pickup == traffic.BY_PHONE {
- b |= ONBOARDING_PHONE
- } else if pickup == traffic.BY_DRIVER {
- b |= ONBOARDING_DRIVER
- }
- if dropoff == traffic.REGULAR {
- b |= OFFBOARDING_REGULAR
- } else if dropoff == traffic.BY_PHONE {
- b |= OFFBOARDING_PHONE
- } else if dropoff == traffic.BY_DRIVER {
- b |= OFFBOARDING_DRIVER
- }
- return uint8(b)
- }
- func makeAlertCauseV1(alert traffic.SpecificAlert) AlertCauseV1 {
- switch alert.Cause {
- case 0:
- return CAUSE_UNKNOWN
- case 1:
- return CAUSE_OTHER
- case 2:
- return CAUSE_TECHNICAL_PROBLEM
- case 3:
- return CAUSE_STRIKE
- case 4:
- return CAUSE_DEMONSTRATION
- case 5:
- return CAUSE_ACCIDENT
- case 6:
- return CAUSE_HOLIDAY
- case 7:
- return CAUSE_WEATHER
- case 8:
- return CAUSE_MAINTENANCE
- case 9:
- return CAUSE_CONSTRUCTION
- case 10:
- return CAUSE_POLICE_ACTIVITY
- case 11:
- return CAUSE_MEDICAL_EMERGENCY
- default:
- return CAUSE_UNKNOWN
- }
- }
- func makeAlertEffectV1(alert traffic.SpecificAlert) AlertEffectV1 {
- switch alert.Effect {
- case 0:
- return EFFECT_NO_SERVICE
- case 1:
- return EFFECT_REDUCED_SERVICE
- case 2:
- return EFFECT_SIGNIFICANT_DELAYS
- case 3:
- return EFFECT_DETOUR
- case 4:
- return EFFECT_ADDITIONAL_SERVICE
- case 5:
- return EFFECT_MODIFIED_SERVICE
- case 6:
- return EFFECT_OTHER
- case 7:
- return EFFECT_UNKNOWN
- case 8:
- return EFFECT_STOP_MOVED
- case 9:
- return EFFECT_NONE
- case 10:
- return EFFECT_ACCESSIBILITY_ISSUE
- default:
- return EFFECT_UNKNOWN
- }
- }
|