structs.go 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. // SPDX-FileCopyrightText: Adam Evyčędo
  2. //
  3. // SPDX-License-Identifier: AGPL-3.0-or-later
  4. package traffic
  5. import (
  6. "fmt"
  7. "strings"
  8. "time"
  9. "github.com/dhconnelly/rtreego"
  10. "golang.org/x/text/language"
  11. )
  12. type DeparturesType uint8
  13. const (
  14. DEPARTURES_HYBRID DeparturesType = iota
  15. DEPARTURES_FULL
  16. )
  17. const ZWJ = "\u200d"
  18. func LuaAlertToAlert(luaAlert AlertLua) (Alert, error) {
  19. resultAlert := Alert{
  20. TimeRanges: make([][2]time.Time, len(luaAlert.TimeRanges)),
  21. Headers: map[language.Tag]string{},
  22. Descriptions: map[language.Tag]string{},
  23. URLs: map[language.Tag]string{},
  24. Cause: AlertCause(luaAlert.Cause),
  25. Effect: AlertEffect(luaAlert.Effect),
  26. }
  27. for i, r := range luaAlert.TimeRanges {
  28. start, err := time.Parse("2006-01-02T15:04:05.999Z07:00", r.StartDate)
  29. if err != nil {
  30. return Alert{}, fmt.Errorf("error parsing time for range %s: %w", r.StartDate, err)
  31. }
  32. end, err := time.Parse("2006-01-02T15:04:05.999Z07:00", r.EndDate)
  33. if err != nil {
  34. return Alert{}, fmt.Errorf("error parsing time for range %s: %w", r.EndDate, err)
  35. }
  36. resultAlert.TimeRanges[i] = [2]time.Time{start, end}
  37. }
  38. for l, header := range luaAlert.Headers {
  39. tag, err := language.Parse(l)
  40. if err != nil {
  41. return Alert{}, fmt.Errorf("error parsing tag %s: %w", l, err)
  42. }
  43. resultAlert.Headers[tag] = header
  44. }
  45. for l, description := range luaAlert.Descriptions {
  46. tag, err := language.Parse(l)
  47. if err != nil {
  48. return Alert{}, fmt.Errorf("error parsing tag %s: %w", l, err)
  49. }
  50. resultAlert.Descriptions[tag] = description
  51. }
  52. for l, url := range luaAlert.Urls {
  53. tag, err := language.Parse(l)
  54. if err != nil {
  55. return Alert{}, fmt.Errorf("error parsing tag %s: %w", l, err)
  56. }
  57. resultAlert.URLs[tag] = url
  58. }
  59. return resultAlert, nil
  60. }
  61. func (v VehicleStatus) Location() Position {
  62. return Position{
  63. Lat: v.Latitude,
  64. Lon: v.Longitude,
  65. }
  66. }
  67. func (k LineType) Value() uint {
  68. return uint(k)
  69. }
  70. func (d Direction) Value() uint {
  71. return uint(d)
  72. }
  73. type DepartureRealtime struct {
  74. Time time.Time
  75. Departure Departure
  76. Headsign string
  77. LineID string
  78. Order StopOrder
  79. Update Update
  80. Alerts []SpecificAlert
  81. }
  82. func (d DepartureRealtime) WithUpdate(update Update) DepartureRealtime {
  83. d.Update = update
  84. return d
  85. }
  86. func (d DepartureRealtime) WithAlerts(alerts []Alert, languages []language.Tag) DepartureRealtime {
  87. d.Alerts = selectSpecificAlerts(alerts, languages)
  88. return d
  89. }
  90. func selectSpecificAlerts(alerts []Alert, languages []language.Tag) []SpecificAlert {
  91. now := time.Now()
  92. validAlerts := []SpecificAlert{}
  93. for _, alert := range alerts {
  94. timeValid := len(alert.TimeRanges) == 0
  95. for _, timeRange := range alert.TimeRanges {
  96. if now.After(timeRange[0]) && now.Before(timeRange[1]) {
  97. timeValid = true
  98. break
  99. }
  100. }
  101. if !timeValid {
  102. continue
  103. }
  104. headerLanguages := make([]language.Tag, len(alert.Headers))
  105. i := 0
  106. for l := range alert.Headers {
  107. headerLanguages[i] = l
  108. i++
  109. }
  110. headerMatcher := language.NewMatcher(headerLanguages)
  111. ht, _, _ := headerMatcher.Match(languages...)
  112. descriptionLanguages := make([]language.Tag, len(alert.Descriptions))
  113. i = 0
  114. for l := range alert.Descriptions {
  115. descriptionLanguages[i] = l
  116. i++
  117. }
  118. descriptionMatcher := language.NewMatcher(descriptionLanguages)
  119. dt, _, _ := descriptionMatcher.Match(languages...)
  120. URLLanguages := make([]language.Tag, len(alert.URLs))
  121. i = 0
  122. for l := range alert.URLs {
  123. URLLanguages[i] = l
  124. i++
  125. }
  126. URLMatcher := language.NewMatcher(URLLanguages)
  127. ut, _, _ := URLMatcher.Match(languages...)
  128. validAlerts = append(validAlerts, SpecificAlert{
  129. Header: alert.Headers[ht],
  130. Description: alert.Descriptions[dt],
  131. URL: alert.URLs[ut],
  132. Cause: alert.Cause,
  133. Effect: alert.Effect,
  134. })
  135. }
  136. return validAlerts
  137. }
  138. func (g LineGraph) LastNodes() []int {
  139. lastNodes := []int{}
  140. for i, nextNodes := range g.NextNodes {
  141. for _, node := range nextNodes {
  142. if node == -1 {
  143. lastNodes = append(lastNodes, i)
  144. break
  145. }
  146. }
  147. }
  148. return lastNodes
  149. }
  150. type LineStub struct {
  151. Name string
  152. Colour string
  153. Type string
  154. }
  155. func (l Line) DisplayName() string {
  156. return l.Name
  157. }
  158. func (l Line) IsItem() {}
  159. type Shape struct { // todo(BAF11)
  160. Points []Position
  161. }
  162. type Queryable interface {
  163. IsItem()
  164. DisplayName() string
  165. }
  166. type Locatable interface {
  167. Location() Position
  168. }
  169. func (s Stop) DisplayName() string {
  170. return s.Name
  171. }
  172. func (s Stop) Location() Position {
  173. return s.Position
  174. }
  175. func (s Stop) Bounds() *rtreego.Rect {
  176. rect, err := rtreego.NewRectFromPoints(
  177. rtreego.Point{s.Position.Lat, s.Position.Lon},
  178. rtreego.Point{s.Position.Lat, s.Position.Lon},
  179. )
  180. if err != nil {
  181. panic(err.Error())
  182. }
  183. return rect
  184. }
  185. func (s Stop) IsItem() {}
  186. type TimedStopStub struct {
  187. StopStub
  188. Time uint
  189. }
  190. type StopStub struct {
  191. Code string
  192. Name string
  193. NodeName string
  194. Zone string
  195. OnDemand bool
  196. }
  197. type Validity string // 20060102_20060102
  198. func (v Validity) Start() string {
  199. return strings.Split(string(v), "_")[0]
  200. }
  201. func (v Validity) End() string {
  202. return strings.Split(string(v), "_")[1]
  203. }
  204. type FeedCodeIndex map[Validity]CodeIndex
  205. type GlobalCodeIndex map[string]FeedCodeIndex
  206. type NameIndex []NameOffset
  207. type FeedNameIndex map[Validity]NameIndex
  208. type GlobalNameIndex map[string]FeedNameIndex
  209. func (ix NameIndex) String(i int) string {
  210. return ix[i].Name
  211. }
  212. func (ix NameIndex) Len() int {
  213. return len(ix)
  214. }
  215. type FeedCalendar map[Validity][]Schedule
  216. type GlobalCalendar map[string]FeedCalendar
  217. type Vehicles map[string]Vehicle
  218. type FeedVehicles map[Validity]Vehicles
  219. type GlobalVehicles map[string]FeedVehicles
  220. type FeedPositionIndex map[Validity]*rtreego.Rtree
  221. type GlobalPositionIndex map[string]FeedPositionIndex
  222. type Version struct {
  223. Link string
  224. ValidFrom time.Time
  225. ValidTill time.Time
  226. }
  227. func (v Version) String() string {
  228. return v.ValidFrom.Format(ValidityFormat) + "_" + v.ValidTill.Format(ValidityFormat)
  229. }
  230. type GlobalVersions map[string][]Version
  231. var DateFormat string = "2006-01-02"
  232. var DateTimeFormat string = "2006-01-02T15:04:05-07:00"
  233. var ValidityFormat string = "20060102"
  234. var ValidityFormatExtended string = "20060102150405"
  235. type Traffic struct {
  236. CodeIndexes GlobalCodeIndex
  237. NameIndexes GlobalNameIndex
  238. LineIdIndexes GlobalCodeIndex
  239. LineIndexes GlobalNameIndex
  240. TripIndexes GlobalNameIndex
  241. PositionIndexes GlobalPositionIndex
  242. Versions GlobalVersions
  243. Calendars GlobalCalendar
  244. Vehicles GlobalVehicles
  245. Feeds map[string]Feed
  246. FeedInfos map[Validity]map[string]FeedInfo
  247. }
  248. type Context struct {
  249. DataHome string
  250. FeedID string
  251. Version Validity
  252. }