access.go 33 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207
  1. package traffic
  2. import (
  3. "apiote.xyz/p/szczanieckiej/config"
  4. "apiote.xyz/p/szczanieckiej/file"
  5. "apiote.xyz/p/szczanieckiej/gtfs_rt"
  6. traffic_errors "apiote.xyz/p/szczanieckiej/traffic/errors"
  7. "apiote.xyz/p/szczanieckiej/transformers"
  8. "errors"
  9. "fmt"
  10. "io"
  11. "log"
  12. "net"
  13. "os"
  14. "path/filepath"
  15. "sort"
  16. "strconv"
  17. "strings"
  18. "time"
  19. "golang.org/x/text/language"
  20. "golang.org/x/text/runes"
  21. "golang.org/x/text/transform"
  22. "git.sr.ht/~sircmpwn/go-bare"
  23. "github.com/dhconnelly/rtreego"
  24. "github.com/sahilm/fuzzy"
  25. "notabug.org/apiote/gott"
  26. )
  27. type OlcError struct {
  28. Value string
  29. Err error
  30. }
  31. func (e OlcError) Error() string {
  32. return e.Err.Error()
  33. }
  34. type _Result struct {
  35. Filename string
  36. Offset uint
  37. Date time.Time
  38. LineName string
  39. TimetableHome string
  40. Calendar []Schedule
  41. DeparturesType DeparturesType
  42. Vehicles Vehicles
  43. Feed Feed
  44. Location *time.Location
  45. Datetime time.Time
  46. MinuteB4Datetime time.Time
  47. Midnight time.Time
  48. TodaySchedule string
  49. YesterdaySchedule string
  50. file *os.File
  51. TripsFile *os.File
  52. Trips map[string]Trip
  53. Departures []DepartureRealtime
  54. Stop Stop
  55. Line Line
  56. Trip Trip
  57. FeedInfo FeedInfo
  58. }
  59. var lastUpdatedGtfsRt = map[string]uint64{}
  60. func isTimeout(err error) bool {
  61. var e net.Error
  62. return errors.As(err, &e) && e.Timeout()
  63. }
  64. func CleanQuery(query string, feed Feed) (string, error) {
  65. t := transform.Chain(runes.Remove(runes.Predicate(transformers.IsNonAlphanum)), feed.Transformer())
  66. queryCleaned, _, err := transform.String(t, query)
  67. return strings.ToLower(queryCleaned), err
  68. }
  69. func findSchedule(home string, time time.Time, calendar []Schedule) (string,
  70. error) {
  71. weekday := uint8(1 << time.Weekday())
  72. date := time.Format(DateFormat)
  73. for _, schedule := range calendar {
  74. if schedule.StartDate <= date && date <= schedule.EndDate &&
  75. (schedule.Weekdays&weekday != 0) {
  76. return schedule.ScheduleID, nil
  77. }
  78. }
  79. return "", traffic_errors.NoSchedule{Date: date}
  80. }
  81. func getRealtimeOffset(tripID string, stopSequence int,
  82. feed Feed) (gtfs_rt.Update, error) {
  83. updates, lastUpdated, err := gtfs_rt.GetRt(lastUpdatedGtfsRt[feed.String()],
  84. feed.RealtimeFeeds(), feed.String())
  85. if err != nil {
  86. return gtfs_rt.Update{}, err
  87. }
  88. lastUpdatedGtfsRt[feed.String()] = lastUpdated
  89. update := updates[tripID]
  90. return update, nil
  91. }
  92. func getRealtimeUpdates(feed Feed) (map[string]gtfs_rt.Update, error) {
  93. updates, lastUpdated, err := gtfs_rt.GetRt(lastUpdatedGtfsRt[feed.String()],
  94. feed.RealtimeFeeds(), feed.String())
  95. if err != nil {
  96. return map[string]gtfs_rt.Update{}, err
  97. }
  98. lastUpdatedGtfsRt[feed.String()] = lastUpdated
  99. return updates, nil
  100. }
  101. func calculateGtfsTime(gtfsTime uint, delay int32, date time.Time,
  102. timezone *time.Location) (time.Time, error) {
  103. noon := time.Date(date.Year(), date.Month(), date.Day(), 12, 0, 0, 0,
  104. timezone)
  105. twelve, _ := time.ParseDuration("-12h")
  106. midnight := noon.Add(twelve)
  107. departureDuration, err := time.ParseDuration(
  108. strconv.FormatInt(int64(gtfsTime), 10) + "m")
  109. if err != nil {
  110. return midnight, err
  111. }
  112. delayDuration, err := time.ParseDuration(strconv.FormatInt(int64(delay),
  113. 10) + "s")
  114. if err != nil {
  115. return midnight, err
  116. }
  117. t := midnight.Add(departureDuration).Add(delayDuration)
  118. return t, nil
  119. }
  120. func loadLocation(input ...interface{}) (interface{}, error) {
  121. result := input[0].(_Result)
  122. var err error = nil
  123. result.Location, err = GetTimezone(result.Stop, result.Feed)
  124. return result, err
  125. }
  126. func loadTime(input ...interface{}) interface{} {
  127. result := input[0].(_Result)
  128. deadzone, _ := time.ParseDuration("-1m")
  129. now := time.Now()
  130. datetime := time.Date(result.Date.Year(), result.Date.Month(),
  131. result.Date.Day(), now.Hour(), now.Minute(), now.Second(), 0,
  132. result.Location)
  133. result.Datetime = datetime
  134. result.MinuteB4Datetime = datetime.Add(deadzone)
  135. result.Midnight = time.Date(datetime.Year(), datetime.Month(),
  136. datetime.Day(), 0, 0, 0, 0, result.Location)
  137. return result
  138. }
  139. func loadTodaySchedule(input ...interface{}) (interface{}, error) {
  140. result := input[0].(_Result)
  141. todaySchedule, err := findSchedule(result.TimetableHome, result.Date,
  142. result.Calendar)
  143. result.TodaySchedule = todaySchedule
  144. return result, err
  145. }
  146. func loadYesterdaySchedule(input ...interface{}) (interface{}, error) {
  147. result := input[0].(_Result)
  148. yesterday := result.Date.AddDate(0, 0, -1)
  149. yesterdaySchedule, err := findSchedule(result.TimetableHome, yesterday,
  150. result.Calendar)
  151. result.YesterdaySchedule = yesterdaySchedule
  152. return result, err
  153. }
  154. func recoverYesterdaySchedule(input ...interface{}) (interface{}, error) {
  155. result := input[0].(_Result)
  156. err := input[1].(error)
  157. dayBefore := result.Date.AddDate(0, 0, -1).Format(DateFormat)
  158. if err, ok := err.(traffic_errors.NoSchedule); ok && err.Date == dayBefore {
  159. result.YesterdaySchedule = ""
  160. return gott.Tuple{result}, nil
  161. }
  162. return gott.Tuple{result}, err
  163. }
  164. func openFile(input ...interface{}) (interface{}, error) {
  165. result := input[0].(_Result)
  166. file, err := os.Open(filepath.Join(result.TimetableHome, result.Filename))
  167. result.file = file
  168. return result, err
  169. }
  170. func seek(input ...interface{}) (interface{}, error) {
  171. result := input[0].(_Result)
  172. _, err := result.file.Seek(int64(result.Offset), 0)
  173. return result, err
  174. }
  175. func unmarshalStop(input ...interface{}) (interface{}, error) {
  176. result := input[0].(_Result)
  177. result.Stop = Stop{}
  178. err := bare.UnmarshalReader(result.file, &result.Stop)
  179. result.file.Close()
  180. return result, err
  181. }
  182. func unmarshalFeedInfo(input ...interface{}) (interface{}, error) {
  183. result := input[0].(_Result)
  184. result.FeedInfo = FeedInfo{}
  185. err := bare.UnmarshalReader(result.file, &result.FeedInfo)
  186. result.file.Close()
  187. return result, err
  188. }
  189. func unmarshalLine(input ...interface{}) (interface{}, error) {
  190. result := input[0].(_Result)
  191. result.Line = Line{}
  192. err := bare.UnmarshalReader(result.file, &result.Line)
  193. result.file.Close()
  194. return result, err
  195. }
  196. func unmarshalTrip(input ...interface{}) (interface{}, error) {
  197. result := input[0].(_Result)
  198. result.Trip = Trip{}
  199. err := bare.UnmarshalReader(result.file, &result.Trip)
  200. result.file.Close()
  201. return result, err
  202. }
  203. func openTripsFile(input ...interface{}) (interface{}, error) {
  204. result := input[0].(_Result)
  205. tripsFile, err := os.Open(filepath.Join(result.TimetableHome, "trips.bare"))
  206. result.TripsFile = tripsFile
  207. return result, err
  208. }
  209. func readTrips(input ...interface{}) (interface{}, error) {
  210. result := input[0].(_Result)
  211. trips := map[string]Trip{}
  212. orders := []StopOrder{}
  213. for _, order := range result.Stop.Order {
  214. _, err := result.TripsFile.Seek(int64(order.TripOffset), 0)
  215. if err != nil {
  216. return result, err
  217. }
  218. trip := Trip{}
  219. err = bare.UnmarshalReader(result.TripsFile, &trip)
  220. if err != nil {
  221. return result, err
  222. }
  223. if trip.ScheduleID == result.TodaySchedule ||
  224. trip.ScheduleID == result.YesterdaySchedule {
  225. trips[trip.ID] = trip
  226. order.TripID = trip.ID
  227. orders = append(orders, order)
  228. }
  229. }
  230. result.Stop.Order = orders
  231. result.Trips = trips
  232. return result, nil
  233. }
  234. func getUpdates(input ...interface{}) (interface{}, error) {
  235. result := input[0].(_Result)
  236. departures := []DepartureRealtime{}
  237. timedOut := false
  238. for _, order := range result.Stop.Order {
  239. trip := result.Trips[order.TripID]
  240. var date time.Time
  241. if trip.ScheduleID == result.TodaySchedule {
  242. date = result.Date
  243. } else if trip.ScheduleID == result.YesterdaySchedule {
  244. date = result.Date.AddDate(0, 0, -1)
  245. } else {
  246. continue
  247. }
  248. departure, err := getDeparture(date, result, order, trip, result.Feed, timedOut)
  249. if err != nil {
  250. if isTimeout(err) {
  251. timedOut = true
  252. err = nil
  253. } else {
  254. return result, err
  255. }
  256. }
  257. departures = append(departures, departure)
  258. }
  259. result.Departures = departures
  260. result.TripsFile.Close()
  261. return result, nil
  262. }
  263. func getDeparture(date time.Time, result _Result, order StopOrder,
  264. trip Trip, feed Feed, timedOut bool) (DepartureRealtime, error) {
  265. found := false
  266. departureRt := DepartureRealtime{}
  267. var finalErr error
  268. for _, departure := range trip.Departures {
  269. if departure.StopSeq == order.Order {
  270. departureRt.Departure = departure
  271. departureRt.Headsign = trip.Headsign
  272. departureRt.LineName = trip.LineName
  273. departureRt.Order = order
  274. departureRt.Update = gtfs_rt.Update{}
  275. departureTime, err := calculateGtfsTime(departure.Time, 0, date,
  276. result.Location)
  277. if err != nil {
  278. return departureRt, err
  279. }
  280. departureRt.Update.Time = departureTime
  281. if departureTime.After(result.Midnight) {
  282. if result.DeparturesType == DEPARTURES_HYBRID && !timedOut {
  283. departureRt.Update, err = getRealtimeOffset(order.TripID,
  284. order.Order, feed)
  285. if err != nil {
  286. if isTimeout(err) {
  287. timedOut = true
  288. finalErr = err
  289. } else {
  290. log.Printf("while getting realtime departures: %v\n", err)
  291. }
  292. }
  293. }
  294. departureTime, err := calculateGtfsTime(departure.Time,
  295. departureRt.Update.Delay, date, result.Location)
  296. if err != nil {
  297. return departureRt, err
  298. }
  299. departureRt.Update.Time = departureTime
  300. }
  301. found = true
  302. break
  303. }
  304. }
  305. if !found {
  306. return departureRt, traffic_errors.NoStopOrder{
  307. TripID: trip.ID,
  308. Order: order.Order,
  309. }
  310. }
  311. return departureRt, finalErr
  312. }
  313. func filterDepartures(input ...interface{}) interface{} {
  314. result := input[0].(_Result)
  315. departures := []DepartureRealtime{}
  316. for _, departure := range result.Departures {
  317. if result.DeparturesType == DEPARTURES_FULL ||
  318. departure.Update.Time.After(result.MinuteB4Datetime) {
  319. departures = append(departures, departure)
  320. }
  321. }
  322. result.Departures = departures
  323. return result
  324. }
  325. func filterDeparturesByLine(input ...interface{}) interface{} {
  326. result := input[0].(_Result)
  327. departures := []DepartureRealtime{}
  328. if result.LineName != "" {
  329. for _, departure := range result.Departures {
  330. if departure.LineName == result.LineName {
  331. departures = append(departures, departure)
  332. }
  333. }
  334. result.Departures = departures
  335. }
  336. return result
  337. }
  338. func sortDepartures(input ...interface{}) interface{} {
  339. result := input[0].(_Result)
  340. sort.Slice(result.Departures, func(i, j int) bool {
  341. return result.Departures[i].Update.Time.Before(
  342. result.Departures[j].Update.Time)
  343. })
  344. return result
  345. }
  346. func closeFiles(input ...interface{}) (interface{}, error) {
  347. result := input[0].(_Result)
  348. err := input[1].(error)
  349. if result.file != nil {
  350. result.file.Close()
  351. }
  352. if result.TripsFile != nil {
  353. result.TripsFile.Close()
  354. }
  355. return result, err
  356. }
  357. func unmarshalCodeIndex(timetableHome string) (CodeIndex, error) {
  358. ix := CodeIndex{}
  359. ixFile, err := os.Open(filepath.Join(timetableHome, "ix_stop_codes.bare"))
  360. if err != nil {
  361. return ix, fmt.Errorf("while opening file: %w\n", err)
  362. }
  363. defer ixFile.Close()
  364. r := bare.NewReader(ixFile)
  365. num, err := r.ReadUint()
  366. if err != nil {
  367. return ix, fmt.Errorf("while reading length: %w\n", err)
  368. }
  369. for i := uint64(0); i < num; i++ {
  370. k, err := r.ReadString()
  371. if err != nil {
  372. return ix, fmt.Errorf("while reading key at %d: %w\n", i, err)
  373. }
  374. v, err := r.ReadUint()
  375. if err != nil {
  376. return ix, fmt.Errorf("while reading value at %d: %w\n", i, err)
  377. }
  378. ix[ID(k)] = uint(v)
  379. }
  380. return ix, nil
  381. }
  382. func unmarshalNameIndex(timetableHome, filename string) (NameIndex, error) {
  383. ix := NameIndex{}
  384. ixFile, err := os.Open(filepath.Join(timetableHome, filename))
  385. if err != nil {
  386. return ix, fmt.Errorf("while opening file: %w", err)
  387. }
  388. defer ixFile.Close()
  389. for err == nil {
  390. nameOffset := NameOffset{}
  391. err = bare.UnmarshalReader(ixFile, &nameOffset)
  392. if err != nil {
  393. if err == io.EOF {
  394. break
  395. } else {
  396. return ix, fmt.Errorf("while unmarshaling: %w", err)
  397. }
  398. }
  399. ix = append(ix, nameOffset)
  400. }
  401. return ix, nil
  402. }
  403. func unmarshalLineIndex(timetableHome string) (NameIndex, error) {
  404. return unmarshalNameIndex(timetableHome, "ix_lines.bare")
  405. }
  406. func unmarshalStopNameIndex(timetableHome string) (NameIndex, error) {
  407. return unmarshalNameIndex(timetableHome, "ix_stop_names.bare")
  408. }
  409. func unmarshalTripIndex(timetableHome string) (NameIndex, error) {
  410. return unmarshalNameIndex(timetableHome, "ix_trips.bare")
  411. }
  412. func readIndexes(feedHome string, versions []Version) (FeedCodeIndex,
  413. FeedNameIndex, FeedNameIndex, FeedNameIndex, error) {
  414. codeIndex := FeedCodeIndex{}
  415. nameIndex := FeedNameIndex{}
  416. lineIndex := FeedNameIndex{}
  417. tripIndex := FeedNameIndex{}
  418. for _, v := range versions {
  419. validity := Validity(v.String())
  420. timetableHome := filepath.Join(feedHome, string(validity))
  421. cIx, err := unmarshalCodeIndex(timetableHome)
  422. if err != nil {
  423. return codeIndex, nameIndex, lineIndex, tripIndex,
  424. fmt.Errorf("while unmarshalling code index: %w\n", err)
  425. }
  426. nIx, err := unmarshalStopNameIndex(timetableHome)
  427. if err != nil {
  428. return codeIndex, nameIndex, lineIndex, tripIndex,
  429. fmt.Errorf("while unmarshalling name index: %w\n", err)
  430. }
  431. lIx, err := unmarshalLineIndex(timetableHome)
  432. if err != nil {
  433. return codeIndex, nameIndex, lineIndex, tripIndex,
  434. fmt.Errorf("while unmarshalling line index: %w\n", err)
  435. }
  436. tIx, err := unmarshalTripIndex(timetableHome)
  437. if err != nil {
  438. return codeIndex, nameIndex, lineIndex, tripIndex,
  439. fmt.Errorf("while unmarshalling trip index: %w\n", err)
  440. }
  441. codeIndex[validity] = cIx
  442. nameIndex[validity] = nIx
  443. lineIndex[validity] = lIx
  444. tripIndex[validity] = tIx
  445. }
  446. return codeIndex, nameIndex, lineIndex, tripIndex, nil
  447. }
  448. func unmarshalCalendar(timetableHome string) ([]Schedule, error) {
  449. calendar := []Schedule{}
  450. calendarFile, err := os.Open(filepath.Join(timetableHome, "calendar.bare"))
  451. if err != nil {
  452. return calendar, fmt.Errorf("while opening file: %w", err)
  453. }
  454. defer calendarFile.Close()
  455. for err == nil {
  456. schedule := Schedule{}
  457. err = bare.UnmarshalReader(calendarFile, &schedule)
  458. if err != nil {
  459. if err == io.EOF {
  460. break
  461. } else {
  462. return calendar, fmt.Errorf("while unmarshaling: %w", err)
  463. }
  464. }
  465. calendar = append(calendar, schedule)
  466. }
  467. return calendar, nil
  468. }
  469. func readCalendar(feedHome string, versions []Version) (FeedCalendar, error) {
  470. calendars := FeedCalendar{}
  471. for _, v := range versions {
  472. validity := Validity(v.String())
  473. timetableHome := filepath.Join(feedHome, string(validity))
  474. schedule, err := unmarshalCalendar(timetableHome)
  475. if err != nil {
  476. return calendars, fmt.Errorf("while unmarshaling for %s: %w", v, err)
  477. }
  478. calendars[validity] = schedule
  479. }
  480. return calendars, nil
  481. }
  482. func unmarshalVehicles(timetableHome string) (Vehicles, error) {
  483. vehicles := Vehicles{}
  484. vehiclesFile, err := os.Open(filepath.Join(timetableHome, "vehicles.bare"))
  485. if err != nil {
  486. return vehicles, fmt.Errorf("while opening file: %w", err)
  487. }
  488. defer vehiclesFile.Close()
  489. for err == nil {
  490. vehicle := Vehicle{}
  491. err = bare.UnmarshalReader(vehiclesFile, &vehicle)
  492. if err != nil {
  493. if err == io.EOF {
  494. break
  495. } else {
  496. return vehicles, fmt.Errorf("while unmarshaling: %w", err)
  497. }
  498. }
  499. vehicles[vehicle.Id] = vehicle
  500. }
  501. return vehicles, nil
  502. }
  503. func readVehicles(feedHome string, versions []Version) (FeedVehicles, error) {
  504. vehicles := FeedVehicles{}
  505. for _, v := range versions {
  506. validity := Validity(v.String())
  507. timetableHome := filepath.Join(feedHome, string(validity))
  508. versionVehicles, err := unmarshalVehicles(timetableHome)
  509. if err != nil {
  510. return vehicles, fmt.Errorf("while unmarshaling for %s: %w", v, err)
  511. }
  512. vehicles[validity] = versionVehicles
  513. }
  514. return vehicles, nil
  515. }
  516. func createPositionIndex(feedHome string, versions []Version) (FeedPositionIndex, error) {
  517. feedPositionIndex := FeedPositionIndex{}
  518. for _, v := range versions {
  519. positionIndex := rtreego.NewTree(2, 25, 50)
  520. validity := Validity(v.String())
  521. timetableHome := filepath.Join(feedHome, string(validity))
  522. stopsFile, err := os.Open(filepath.Join(timetableHome, "stops.bare"))
  523. if err != nil {
  524. return feedPositionIndex, fmt.Errorf("while opening stops file: %w", err)
  525. }
  526. defer stopsFile.Close()
  527. for err == nil {
  528. stop := Stop{}
  529. err = bare.UnmarshalReader(stopsFile, &stop)
  530. if err != nil {
  531. if err == io.EOF {
  532. break
  533. } else {
  534. return feedPositionIndex, fmt.Errorf("while unmarshaling: %w", err)
  535. }
  536. }
  537. stop.Name = ""
  538. stop.ChangeOptions = nil
  539. stop.Zone = ""
  540. stop.Order = nil
  541. positionIndex.Insert(stop)
  542. feedPositionIndex[validity] = positionIndex
  543. }
  544. }
  545. return feedPositionIndex, nil
  546. }
  547. func unmarshalTripFromFile(tripsFile *os.File) Trip {
  548. trip := Trip{}
  549. _ = bare.UnmarshalReader(tripsFile, &trip)
  550. return trip
  551. }
  552. func EnableFeeds(cfg config.Config, traffic *Traffic) {
  553. feedsMap := RegisterFeeds()
  554. feeds := map[string]Feed{}
  555. for _, enabledFeed := range cfg.EnabledFeeds {
  556. feeds[enabledFeed] = feedsMap[enabledFeed]
  557. }
  558. traffic.Feeds = feeds
  559. }
  560. func Initialise(sigChan chan os.Signal, doneChan chan bool, cfg config.Config,
  561. traffic *Traffic) {
  562. for {
  563. sig := <-sigChan
  564. if sig == os.Interrupt {
  565. break
  566. }
  567. allVersions := GlobalVersions{}
  568. codeIndexes := GlobalCodeIndex{}
  569. nameIndexes := GlobalNameIndex{}
  570. lineIndexes := GlobalNameIndex{}
  571. tripIndexes := GlobalNameIndex{}
  572. calendars := GlobalCalendar{}
  573. vehicles := GlobalVehicles{}
  574. positionIndexes := GlobalPositionIndex{}
  575. for _, feed := range traffic.Feeds {
  576. feedHome := filepath.Join(cfg.FeedsPath, feed.String())
  577. err := file.UnpackTraffic(cfg.FeedsPath, feed.String())
  578. if err != nil {
  579. log.Printf("while unpacking TRAFFIC in feed %s: %v\n", feed, err)
  580. continue
  581. }
  582. err = CleanOldVersions(cfg, feed)
  583. if err != nil {
  584. log.Printf("while cleaning old TRAFFIC versions in feed %s: %v\n",
  585. feed, err)
  586. continue
  587. }
  588. feedVersions, err := ListVersions(cfg, feed)
  589. if err != nil {
  590. log.Printf("while listing TRAFFIC versions in feed %s: %v\n", feed,
  591. err)
  592. continue
  593. }
  594. feedName := feed.String()
  595. allVersions[feedName] = feedVersions
  596. codeIndexes[feedName], nameIndexes[feedName], lineIndexes[feedName], tripIndexes[feedName],
  597. err = readIndexes(feedHome, feedVersions)
  598. if err != nil {
  599. log.Printf("while reading indexes in feed %s: %v\n", feed, err)
  600. continue
  601. }
  602. calendars[feedName], err = readCalendar(feedHome, feedVersions)
  603. if err != nil {
  604. log.Printf("while reading calendars in feed %s: %v\n", feed, err)
  605. continue
  606. }
  607. vehicles[feedName], err = readVehicles(feedHome, feedVersions)
  608. if err != nil {
  609. log.Printf("while reading vehicles in feed %s: %v\n", feed, err)
  610. continue
  611. }
  612. positionIndexes[feedName], err = createPositionIndex(feedHome, feedVersions)
  613. }
  614. traffic.CodeIndexes = codeIndexes
  615. traffic.NameIndexes = nameIndexes
  616. traffic.LineIndexes = lineIndexes
  617. traffic.TripIndexes = tripIndexes
  618. traffic.Versions = allVersions
  619. traffic.Calendars = calendars
  620. traffic.Vehicles = vehicles
  621. traffic.PositionIndexes = positionIndexes
  622. log.Println("Initialised")
  623. }
  624. doneChan <- true
  625. }
  626. func GetDepartures(stopCode ID, lineName, dataHome, feedName string,
  627. versionCode Validity, traffic *Traffic, date time.Time,
  628. departuresType DeparturesType) ([]DepartureRealtime, error) {
  629. codeIndex := traffic.CodeIndexes[feedName][versionCode]
  630. calendar := traffic.Calendars[feedName][versionCode]
  631. vehicles := traffic.Vehicles[feedName][versionCode]
  632. result := _Result{
  633. Offset: codeIndex[stopCode],
  634. Filename: "stops.bare",
  635. Date: date,
  636. LineName: lineName,
  637. TimetableHome: filepath.Join(dataHome, feedName, string(versionCode)),
  638. Calendar: calendar,
  639. DeparturesType: departuresType,
  640. Vehicles: vehicles,
  641. Feed: traffic.Feeds[feedName],
  642. }
  643. r, e := gott.NewResult(result).
  644. Bind(loadLocation).
  645. Map(loadTime).
  646. Bind(loadTodaySchedule).
  647. Bind(loadYesterdaySchedule).
  648. Recover(recoverYesterdaySchedule).
  649. Bind(openFile).
  650. Bind(seek).
  651. Bind(unmarshalStop).
  652. Bind(openTripsFile).
  653. Bind(readTrips).
  654. Bind(getUpdates).
  655. Map(filterDepartures).
  656. Map(filterDeparturesByLine).
  657. Map(sortDepartures).
  658. Recover(closeFiles).
  659. Finish()
  660. if e != nil {
  661. return []DepartureRealtime{}, e
  662. } else {
  663. return r.(_Result).Departures, nil
  664. }
  665. }
  666. func GetTripFromStop(tripID string, stopCode ID, context Context, traffic *Traffic) ([]TimedStopStub, error) {
  667. stubs := []TimedStopStub{}
  668. var (
  669. order = -1
  670. trip Trip
  671. err error
  672. baseTime uint = 0
  673. time uint = 0
  674. )
  675. if stopCode != "" {
  676. startingStop, err := GetStop(stopCode, context, traffic)
  677. if err != nil {
  678. return stubs, fmt.Errorf("while getting starting stop: %w", err)
  679. }
  680. tripOffset := -1
  681. order = -1
  682. for _, o := range startingStop.Order {
  683. if o.TripID == tripID {
  684. tripOffset = int(o.TripOffset)
  685. order = o.Order
  686. }
  687. }
  688. if tripOffset == -1 {
  689. return stubs, fmt.Errorf("trip for starting stop not found")
  690. }
  691. trip, err = GetTripByOffset(uint(tripOffset), context, traffic)
  692. if err != nil {
  693. return stubs, fmt.Errorf("while getting trip: %w", err)
  694. }
  695. } else {
  696. trip, err = GetTrip(tripID, context, traffic)
  697. if err != nil {
  698. return stubs, fmt.Errorf("while getting trip: %w", err)
  699. }
  700. }
  701. for _, departure := range trip.Departures {
  702. if departure.StopSeq >= order {
  703. stop, err := getStopByOffset(uint(departure.StopOffset), context, traffic)
  704. if err != nil {
  705. return stubs, fmt.Errorf("while getting stop: %w", err)
  706. }
  707. if baseTime != 0 {
  708. time = departure.Time - baseTime
  709. }
  710. stubs = append(stubs, TimedStopStub{
  711. StopStub: StopStub{
  712. Code: stop.Code,
  713. Name: stop.Name,
  714. Zone: stop.Zone,
  715. OnDemand: departure.Pickup == 3, // todo BAF10
  716. },
  717. Time: time,
  718. })
  719. }
  720. }
  721. return stubs, nil
  722. }
  723. func getStopByOffset(offset uint, context Context, traffic *Traffic) (Stop, error) { // todo offset should be uint64 everywhere
  724. result := _Result{
  725. Filename: "stops.bare",
  726. Offset: offset,
  727. TimetableHome: filepath.Join(context.DataHome, context.FeedName, string(context.Version)),
  728. }
  729. r, e := gott.NewResult(result).
  730. Bind(openFile).
  731. Bind(seek).
  732. Bind(unmarshalStop).
  733. Finish()
  734. if e != nil {
  735. return Stop{}, e
  736. } else {
  737. return r.(_Result).Stop, nil
  738. }
  739. }
  740. func getLineByOffset(offset uint, dataHome string, feedName string,
  741. versionCode Validity, traffic *Traffic) (Line, error) {
  742. result := _Result{
  743. Filename: "lines.bare",
  744. Offset: offset,
  745. TimetableHome: filepath.Join(dataHome, feedName, string(versionCode)),
  746. }
  747. r, e := gott.NewResult(result).
  748. Bind(openFile).
  749. Bind(seek).
  750. Bind(unmarshalLine).
  751. Finish()
  752. if e != nil {
  753. return Line{}, e
  754. } else {
  755. return r.(_Result).Line, nil
  756. }
  757. }
  758. func getFeedInfo(dataHome string, feedName string,
  759. versionCode Validity, traffic *Traffic) (FeedInfo, error) {
  760. result := _Result{
  761. Filename: "feed_info.bare",
  762. TimetableHome: filepath.Join(dataHome, feedName, string(versionCode)),
  763. }
  764. r, e := gott.NewResult(result).
  765. Bind(openFile).
  766. Bind(unmarshalFeedInfo).
  767. Finish()
  768. if e != nil {
  769. return FeedInfo{}, e
  770. } else {
  771. return r.(_Result).FeedInfo, nil
  772. }
  773. }
  774. func GetTripByOffset(offset uint, context Context, t *Traffic) (Trip, error) {
  775. result := _Result{
  776. Filename: "trips.bare",
  777. Offset: offset,
  778. TimetableHome: filepath.Join(context.DataHome, context.FeedName, string(context.Version)),
  779. }
  780. r, e := gott.NewResult(result).
  781. Bind(openFile).
  782. Bind(seek).
  783. Bind(unmarshalTrip).
  784. Finish()
  785. if e != nil {
  786. return Trip{}, e
  787. } else {
  788. return r.(_Result).Trip, nil
  789. }
  790. }
  791. func GetStop(stopCode ID, context Context, traffic *Traffic) (Stop, error) {
  792. codeIndex := traffic.CodeIndexes[context.FeedName][context.Version]
  793. return getStopByOffset(codeIndex[stopCode], context, traffic)
  794. }
  795. func GetStopStub(stopCode ID, lineName string, context Context, traffic *Traffic) (StopStub, error) {
  796. stop, err := GetStop(stopCode, context, traffic)
  797. if err != nil {
  798. return StopStub{}, err
  799. }
  800. var trip Trip
  801. var stopOrder = -1
  802. for _, order := range stop.Order {
  803. offset := order.TripOffset
  804. trip, _ = GetTripByOffset(offset, context, traffic)
  805. if trip.LineName == lineName {
  806. stopOrder = order.Order
  807. break
  808. }
  809. }
  810. stopStub := StopStub{
  811. Code: stop.Code,
  812. Name: stop.Name,
  813. Zone: stop.Zone,
  814. OnDemand: trip.Departures[stopOrder].Pickup == 3, // todo BAF10
  815. }
  816. return stopStub, nil
  817. }
  818. func GetLine(name string, context Context, traffic *Traffic) (Line, error) {
  819. index := traffic.LineIndexes[context.FeedName][context.Version]
  820. for _, o := range index {
  821. if o.Name == name {
  822. return getLineByOffset(o.Offsets[0], context.DataHome, context.FeedName, context.Version, traffic)
  823. }
  824. }
  825. return Line{}, nil
  826. }
  827. func GetTrip(id string, context Context, traffic *Traffic) (Trip, error) {
  828. tripIndex := traffic.TripIndexes[context.FeedName][context.Version]
  829. for _, o := range tripIndex {
  830. if o.Name == id {
  831. return GetTripByOffset(o.Offsets[0], context, traffic)
  832. }
  833. }
  834. return Trip{}, fmt.Errorf("trip by id %s not found", id)
  835. }
  836. func QueryLines(query string, dataHome string, feedName string,
  837. versionCode Validity, traffic *Traffic) ([]Line, error) {
  838. lines := []Line{}
  839. index := traffic.LineIndexes[feedName][versionCode]
  840. cleanQuery, err := CleanQuery(query, traffic.Feeds[feedName])
  841. if err != nil {
  842. return lines, fmt.Errorf("while cleaning query: %w", err)
  843. }
  844. results := fuzzy.FindFrom(cleanQuery, index)
  845. for _, result := range results {
  846. for _, offset := range index[result.Index].Offsets {
  847. line, err := getLineByOffset(offset, dataHome, feedName,
  848. versionCode, traffic)
  849. if err != nil {
  850. return lines, fmt.Errorf("while getting line for %s: %w", result.Str, err)
  851. }
  852. lines = append(lines, line)
  853. }
  854. }
  855. return lines, nil
  856. }
  857. func QueryStops(query string, context Context, traffic *Traffic) ([]Stop, error) {
  858. stops := []Stop{}
  859. nameIndex := traffic.NameIndexes[context.FeedName][context.Version]
  860. results := fuzzy.FindFrom(query, nameIndex)
  861. for _, result := range results {
  862. for _, offset := range nameIndex[result.Index].Offsets {
  863. stop, err := getStopByOffset(offset, context, traffic)
  864. if err != nil {
  865. return stops, err
  866. }
  867. stops = append(stops, stop)
  868. }
  869. }
  870. return stops, nil
  871. }
  872. func GetStopsNear(location Position, context Context, traffic *Traffic) ([]Stop, error) {
  873. stops := []Stop{}
  874. positionIndex := traffic.PositionIndexes[context.FeedName][context.Version]
  875. codeIndex := traffic.CodeIndexes[context.FeedName][context.Version]
  876. spatials := positionIndex.NearestNeighbors(12, rtreego.Point{location.Lat, location.Lon})
  877. for _, spatial := range spatials {
  878. stop, err := getStopByOffset(codeIndex[ID(spatial.(Stop).Code)], context, traffic)
  879. if err != nil {
  880. return stops, fmt.Errorf("while getting stop by offset for %s: %w", spatial.(Stop).Code, err)
  881. }
  882. stops = append(stops, stop)
  883. }
  884. return stops, nil
  885. }
  886. func GetAlerts(stopCode string, preferredLanguages []language.Tag, context Context, traffic *Traffic) ([]Alert, error) {
  887. feed := traffic.Feeds[context.FeedName]
  888. alertPositions := map[uint]struct{}{}
  889. gtfsAlerts, lastUpdated, err := gtfs_rt.GetAlerts(lastUpdatedGtfsRt[feed.String()], feed.RealtimeFeeds(), feed.String())
  890. if err != nil {
  891. if isTimeout(err) {
  892. return []Alert{}, nil
  893. }
  894. return []Alert{}, fmt.Errorf("while geting alerts: %w", err)
  895. }
  896. lastUpdatedGtfsRt[feed.String()] = lastUpdated
  897. stop, err := GetStop(ID(stopCode), context, traffic)
  898. for _, pos := range gtfsAlerts.ByStop[stop.ID] {
  899. alertPositions[pos] = struct{}{}
  900. }
  901. for _, option := range stop.ChangeOptions {
  902. line, err := GetLine(option.LineName, context, traffic)
  903. if err != nil {
  904. return []Alert{}, fmt.Errorf("while getting line %s: %w", option.LineName, err)
  905. }
  906. for _, pos := range gtfsAlerts.ByRoute[line.ID] {
  907. alertPositions[pos] = struct{}{}
  908. }
  909. for _, pos := range gtfsAlerts.ByType[line.Type] {
  910. alertPositions[pos] = struct{}{}
  911. }
  912. }
  913. for _, order := range stop.Order {
  914. trip, err := GetTripByOffset(order.TripOffset, context, traffic)
  915. if err != nil {
  916. return []Alert{}, fmt.Errorf("while getting trip at %d: %w", order.TripOffset, err)
  917. }
  918. for _, pos := range gtfsAlerts.ByTrip[trip.ID] {
  919. alertPositions[pos] = struct{}{}
  920. }
  921. }
  922. alerts := make([]Alert, len(alertPositions))
  923. i := 0
  924. for pos := range alertPositions {
  925. gtfsAlert := gtfsAlerts.Alerts[pos]
  926. includeAlert := false
  927. for _, timeRange := range gtfsAlert.TimeRanges {
  928. start := time.Unix(int64(timeRange.GetStart()), 0)
  929. var end time.Time
  930. if timeRange.GetEnd() == 0 {
  931. end = time.Date(9999, 12, 31, 23, 59, 59, 0, time.Local)
  932. } else {
  933. end = time.Unix(int64(timeRange.GetEnd()), 0)
  934. }
  935. now := time.Now()
  936. if !now.Before(start) && !now.After(end) {
  937. includeAlert = true
  938. }
  939. }
  940. if !includeAlert {
  941. continue
  942. }
  943. alerts[i] = Alert{
  944. Cause: int32(gtfsAlert.Cause), // todo(BAF10)
  945. Effect: int32(gtfsAlert.Effect), // todo(BAF10)
  946. }
  947. alertHeaderLangs := []language.Tag{}
  948. for lang := range gtfsAlert.Headers {
  949. alertHeaderLangs = append(alertHeaderLangs, lang)
  950. }
  951. m := language.NewMatcher(alertHeaderLangs)
  952. tag, _, _ := m.Match(preferredLanguages...)
  953. alerts[i].Header = gtfsAlert.Headers[tag]
  954. alertDescriptionLangs := []language.Tag{}
  955. for lang := range gtfsAlert.Descriptions {
  956. alertDescriptionLangs = append(alertDescriptionLangs, lang)
  957. }
  958. m = language.NewMatcher(alertDescriptionLangs)
  959. tag, _, _ = m.Match(preferredLanguages...)
  960. alerts[i].Description = gtfsAlert.Descriptions[tag]
  961. alertUrlLangs := []language.Tag{}
  962. for lang := range gtfsAlert.URLs {
  963. alertUrlLangs = append(alertUrlLangs, lang)
  964. }
  965. m = language.NewMatcher(alertUrlLangs)
  966. tag, _, _ = m.Match(preferredLanguages...)
  967. alerts[i].URL = gtfsAlert.URLs[tag]
  968. i++
  969. }
  970. return alerts, nil
  971. }
  972. func GetTimezone(stop Stop, feed Feed) (*time.Location, error) {
  973. if stop.Timezone != "" {
  974. return time.LoadLocation(stop.Timezone)
  975. }
  976. return feed.GetLocation(), nil
  977. }
  978. func GetLanguage(ctx Context, t *Traffic) (string, error) {
  979. feedInfo, err := getFeedInfo(ctx.DataHome, ctx.FeedName, ctx.Version, t)
  980. return feedInfo.Language, err
  981. }
  982. func CleanOldVersions(cfg config.Config, feed Feed) error {
  983. allVersions, err := ListVersions(cfg, feed)
  984. if err != nil {
  985. return err
  986. }
  987. now := time.Now().In(feed.GetLocation())
  988. versionsMap := map[string]Version{}
  989. for _, version := range allVersions {
  990. versionsMap[version.String()] = version
  991. }
  992. sort.Slice(allVersions, func(i, j int) bool {
  993. return allVersions[i].ValidFrom.Before(allVersions[j].ValidFrom)
  994. })
  995. validVersions := FindValidVersions(allVersions, now)
  996. validVersionsMap := map[string]bool{}
  997. for _, version := range validVersions {
  998. validVersionsMap[version.String()] = true
  999. }
  1000. err = file.CleanOldVersions(FeedPath(cfg, feed), validVersionsMap)
  1001. return err
  1002. }
  1003. func convertVehicle(update gtfs_rt.Update, context Context, t *Traffic) (Vehicle, error) {
  1004. vehicles := t.Vehicles[context.FeedName][context.Version]
  1005. tripID := update.TripUpdate.GetTrip().GetTripId()
  1006. trip, err := GetTrip(tripID, context, t)
  1007. if err != nil {
  1008. return Vehicle{}, fmt.Errorf("while converting vehicle: %w", err)
  1009. }
  1010. return Vehicle{
  1011. Id: ID(update.VehicleID),
  1012. Coordinates: Position{float64(update.Latitude), float64(update.Longitude)},
  1013. Capabilities: vehicles[ID(update.VehicleID)].Capabilities,
  1014. Speed: update.Speed,
  1015. Headsign: trip.Headsign,
  1016. LineName: trip.LineName,
  1017. }, nil
  1018. }
  1019. func GetVehicle(tripID string, context Context, t *Traffic) (Vehicle, error) {
  1020. vehicle := Vehicle{}
  1021. update, err := getRealtimeOffset(tripID, 0, t.Feeds[context.FeedName])
  1022. if err != nil {
  1023. return vehicle, fmt.Errorf("while getting realtime update: %w", err)
  1024. }
  1025. if update.TripUpdate == nil {
  1026. return vehicle, fmt.Errorf("empty realtime update")
  1027. }
  1028. return convertVehicle(update, context, t)
  1029. }
  1030. func GetStopsIn(lb, rt Position, context Context, traffic *Traffic) ([]Stop, error) {
  1031. // todo limit rect size
  1032. // todo does it take into account rect 179 -> -179 latitude?
  1033. stops := []Stop{}
  1034. positionIndex := traffic.PositionIndexes[context.FeedName][context.Version]
  1035. codeIndex := traffic.CodeIndexes[context.FeedName][context.Version]
  1036. rect, err := rtreego.NewRectFromPoints(rtreego.Point{lb.Lat, lb.Lon}, rtreego.Point{rt.Lat, rt.Lon})
  1037. if err != nil {
  1038. return stops, fmt.Errorf("while creating a rect: %w", err)
  1039. }
  1040. spatials := positionIndex.SearchIntersect(rect)
  1041. for _, spatial := range spatials {
  1042. stop, err := getStopByOffset(codeIndex[ID(spatial.(Stop).Code)], context, traffic)
  1043. if err != nil {
  1044. return stops, fmt.Errorf("while getting stop by offset for %s: %w", spatial.(Stop).Code, err)
  1045. }
  1046. stops = append(stops, stop)
  1047. }
  1048. return stops, nil
  1049. }
  1050. func GetVehiclesIn(lb, rt Position, context Context, t *Traffic) ([]Vehicle, error) {
  1051. // todo limit rect size
  1052. vehicles := []Vehicle{}
  1053. updates, err := getRealtimeUpdates(t.Feeds[context.FeedName])
  1054. if err != nil {
  1055. return vehicles, err
  1056. }
  1057. for _, update := range updates {
  1058. if rt.Lon < float64(update.Longitude) || lb.Lon > float64(update.Longitude) {
  1059. continue
  1060. }
  1061. lat := float64(update.Latitude)
  1062. if lb.Lat < rt.Lat {
  1063. if lb.Lat < lat && lat < rt.Lat {
  1064. vehicle, err := convertVehicle(update, context, t)
  1065. if err != nil {
  1066. return vehicles, fmt.Errorf("while converting vehicle: %w", err)
  1067. }
  1068. vehicles = append(vehicles, vehicle)
  1069. }
  1070. } else {
  1071. if lat > lb.Lat || lat < rt.Lat {
  1072. vehicle, err := convertVehicle(update, context, t)
  1073. if err != nil {
  1074. return vehicles, fmt.Errorf("while converting vehicle: %w", err)
  1075. }
  1076. vehicles = append(vehicles, vehicle)
  1077. }
  1078. }
  1079. }
  1080. return vehicles, nil
  1081. }