access.go 35 KB

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