access.go 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880
  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. "slices"
  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 _Result struct {
  29. Filename string
  30. Offset uint
  31. Date time.Time
  32. LineID string
  33. TimetableHome string
  34. Calendar []Schedule
  35. DeparturesType DeparturesType
  36. Vehicles Vehicles
  37. Feed Feed
  38. Ctx Context
  39. Traffic *Traffic
  40. Languages []language.Tag
  41. Location *time.Location
  42. Datetime time.Time
  43. MinuteB4Datetime time.Time
  44. TodaySchedule map[string]struct{}
  45. YesterdaySchedule map[string]struct{}
  46. file *os.File
  47. TripsFile *os.File
  48. Trips map[string]Trip
  49. Departures []DepartureRealtime
  50. DeparturesSchedule []Departure
  51. Stop Stop
  52. Line Line
  53. Trip Trip
  54. FeedInfo FeedInfo
  55. }
  56. func isTimeout(err error) bool {
  57. var e net.Error
  58. return errors.As(err, &e) && e.Timeout()
  59. }
  60. func CleanQuery(query string, feed Feed) (string, error) {
  61. t := transform.Chain(runes.Remove(runes.Predicate(transformers.IsNonAlphanum)), feed.Transformer())
  62. queryCleaned, _, err := transform.String(t, query)
  63. return strings.ToLower(queryCleaned), err
  64. }
  65. func findSchedule(home string, time time.Time, calendar []Schedule) (map[string]struct{},
  66. error) {
  67. schedules := map[string]struct{}{}
  68. weekday := uint8(1 << time.Weekday())
  69. date := time.Format(DateFormat)
  70. for _, schedule := range calendar {
  71. for _, dateRange := range schedule.DateRanges {
  72. if dateRange.Start <= date && date <= dateRange.End &&
  73. (dateRange.Weekdays&weekday != 0) {
  74. schedules[schedule.Id] = struct{}{}
  75. break
  76. }
  77. }
  78. }
  79. var err error
  80. if len(schedules) == 0 {
  81. err = traffic_errors.NoSchedule{Date: date}
  82. }
  83. return schedules, err
  84. }
  85. func openFile(input ...interface{}) (interface{}, error) {
  86. result := input[0].(_Result)
  87. file, err := os.Open(filepath.Join(result.TimetableHome, result.Filename))
  88. result.file = file
  89. return result, err
  90. }
  91. func seek(input ...interface{}) (interface{}, error) {
  92. result := input[0].(_Result)
  93. _, err := result.file.Seek(int64(result.Offset), 0)
  94. return result, err
  95. }
  96. func unmarshalStop(input ...interface{}) (interface{}, error) {
  97. result := input[0].(_Result)
  98. result.Stop = Stop{}
  99. err := bare.UnmarshalReader(result.file, &result.Stop)
  100. result.file.Close()
  101. return result, err
  102. }
  103. func unmarshalFeedInfo(input ...interface{}) (interface{}, error) {
  104. result := input[0].(_Result)
  105. result.FeedInfo = FeedInfo{}
  106. err := bare.UnmarshalReader(result.file, &result.FeedInfo)
  107. result.file.Close()
  108. return result, err
  109. }
  110. func unmarshalLine(input ...interface{}) (interface{}, error) {
  111. result := input[0].(_Result)
  112. result.Line = Line{}
  113. err := bare.UnmarshalReader(result.file, &result.Line)
  114. result.file.Close()
  115. return result, err
  116. }
  117. func unmarshalTrip(input ...interface{}) (interface{}, error) {
  118. result := input[0].(_Result)
  119. result.Trip = Trip{}
  120. err := bare.UnmarshalReader(result.file, &result.Trip)
  121. return result, err
  122. }
  123. func GetTimeWithDelay(departure DepartureRealtime) time.Time {
  124. if departure.Update.TimeUTC != "" {
  125. updateTimeUTC, err := time.Parse("150405", departure.Update.Time)
  126. if err != nil {
  127. panic("departure update time ‘" + departure.Update.Time + "’ not in format 150405")
  128. }
  129. updateTime := time.Date(departure.Time.Year(), departure.Time.Month(), departure.Time.Day(), updateTimeUTC.Hour(), updateTimeUTC.Minute(), updateTimeUTC.Second(), 0, time.UTC)
  130. return updateTime.In(departure.Time.Location())
  131. } else if departure.Update.Time != "" {
  132. updateTime, err := time.Parse("150405", departure.Update.Time)
  133. if err != nil {
  134. panic("departure update time ‘" + departure.Update.Time + "’ not in format 150405")
  135. }
  136. updateDateTime := time.Date(departure.Time.Year(), departure.Time.Month(), departure.Time.Day(), updateTime.Hour(), updateTime.Minute(), updateTime.Second(), 0, departure.Time.Location())
  137. return updateDateTime
  138. } else {
  139. delay := int(departure.Update.Delay)
  140. return departure.Time.Add(time.Duration(delay) * time.Second)
  141. }
  142. }
  143. func unmarshalCodeIndex(timetableHome, filename string) (CodeIndex, error) {
  144. ix := CodeIndex{}
  145. ixFile, err := os.Open(filepath.Join(timetableHome, filename))
  146. if err != nil {
  147. return ix, fmt.Errorf("while opening file: %w", err)
  148. }
  149. defer ixFile.Close()
  150. r := bare.NewReader(ixFile)
  151. num, err := r.ReadUint()
  152. if err != nil {
  153. return ix, fmt.Errorf("while reading length: %w", err)
  154. }
  155. for i := uint64(0); i < num; i++ {
  156. k, err := r.ReadString()
  157. if err != nil {
  158. return ix, fmt.Errorf("while reading key at %d: %w", i, err)
  159. }
  160. v, err := r.ReadUint()
  161. if err != nil {
  162. return ix, fmt.Errorf("while reading value at %d: %w", i, err)
  163. }
  164. ix[k] = uint(v)
  165. }
  166. return ix, nil
  167. }
  168. func unmarshalNameIndex(timetableHome, filename string) (NameIndex, error) {
  169. ix := NameIndex{}
  170. ixFile, err := os.Open(filepath.Join(timetableHome, filename))
  171. if err != nil {
  172. return ix, fmt.Errorf("while opening file: %w", err)
  173. }
  174. defer ixFile.Close()
  175. for err == nil {
  176. nameOffset := NameOffset{}
  177. err = bare.UnmarshalReader(ixFile, &nameOffset)
  178. if err != nil {
  179. if err == io.EOF {
  180. break
  181. } else {
  182. return ix, fmt.Errorf("while unmarshaling: %w", err)
  183. }
  184. }
  185. ix = append(ix, nameOffset)
  186. }
  187. return ix, nil
  188. }
  189. func unmarshalStopCodeIndex(timetableHome string) (CodeIndex, error) {
  190. return unmarshalCodeIndex(timetableHome, "ix_stop_codes.bare")
  191. }
  192. func unmarshalLineCodeIndex(timetableHome string) (CodeIndex, error) {
  193. return unmarshalCodeIndex(timetableHome, "ix_line_codes.bare")
  194. }
  195. func unmarshalLineIndex(timetableHome string) (NameIndex, error) {
  196. return unmarshalNameIndex(timetableHome, "ix_lines.bare")
  197. }
  198. func unmarshalStopNameIndex(timetableHome string) (NameIndex, error) {
  199. return unmarshalNameIndex(timetableHome, "ix_stop_names.bare")
  200. }
  201. func unmarshalTripIndex(timetableHome string) (NameIndex, error) {
  202. return unmarshalNameIndex(timetableHome, "ix_trips.bare")
  203. }
  204. func readIndexes(feedHome string, versions []Version) (FeedCodeIndex,
  205. FeedNameIndex, FeedNameIndex, FeedCodeIndex, FeedNameIndex, error) {
  206. codeIndex := FeedCodeIndex{}
  207. nameIndex := FeedNameIndex{}
  208. lineIndex := FeedNameIndex{}
  209. tripIndex := FeedNameIndex{}
  210. lineIdIndex := FeedCodeIndex{}
  211. for _, v := range versions {
  212. validity := Validity(v.String())
  213. timetableHome := filepath.Join(feedHome, string(validity))
  214. cIx, err := unmarshalStopCodeIndex(timetableHome)
  215. if err != nil {
  216. return codeIndex, nameIndex, lineIndex, lineIdIndex, tripIndex,
  217. fmt.Errorf("while unmarshalling code index: %w", err)
  218. }
  219. liIx, err := unmarshalLineCodeIndex(timetableHome)
  220. if err != nil {
  221. return codeIndex, nameIndex, lineIndex, lineIdIndex, tripIndex,
  222. fmt.Errorf("while unmarshalling code index: %w", err)
  223. }
  224. nIx, err := unmarshalStopNameIndex(timetableHome)
  225. if err != nil {
  226. return codeIndex, nameIndex, lineIndex, lineIdIndex, tripIndex,
  227. fmt.Errorf("while unmarshalling name index: %w", err)
  228. }
  229. lIx, err := unmarshalLineIndex(timetableHome)
  230. if err != nil {
  231. return codeIndex, nameIndex, lineIndex, lineIdIndex, tripIndex,
  232. fmt.Errorf("while unmarshalling line index: %w", err)
  233. }
  234. tIx, err := unmarshalTripIndex(timetableHome)
  235. if err != nil {
  236. return codeIndex, nameIndex, lineIndex, lineIdIndex, tripIndex,
  237. fmt.Errorf("while unmarshalling trip index: %w", err)
  238. }
  239. codeIndex[validity] = cIx
  240. nameIndex[validity] = nIx
  241. lineIndex[validity] = lIx
  242. lineIdIndex[validity] = liIx
  243. tripIndex[validity] = tIx
  244. }
  245. return codeIndex, nameIndex, lineIndex, lineIdIndex, tripIndex, nil
  246. }
  247. func unmarshalCalendar(timetableHome string) ([]Schedule, error) {
  248. calendar := []Schedule{}
  249. calendarFile, err := os.Open(filepath.Join(timetableHome, "calendar.bare"))
  250. if err != nil {
  251. return calendar, fmt.Errorf("while opening file: %w", err)
  252. }
  253. defer calendarFile.Close()
  254. for err == nil {
  255. schedule := Schedule{}
  256. err = bare.UnmarshalReader(calendarFile, &schedule)
  257. if err != nil {
  258. if err == io.EOF {
  259. break
  260. } else {
  261. return calendar, fmt.Errorf("while unmarshaling: %w", err)
  262. }
  263. }
  264. calendar = append(calendar, schedule)
  265. }
  266. return calendar, nil
  267. }
  268. func readCalendar(feedHome string, versions []Version) (FeedCalendar, error) {
  269. calendars := FeedCalendar{}
  270. for _, v := range versions {
  271. validity := Validity(v.String())
  272. timetableHome := filepath.Join(feedHome, string(validity))
  273. schedule, err := unmarshalCalendar(timetableHome)
  274. if err != nil {
  275. return calendars, fmt.Errorf("while unmarshaling for %s: %w", v, err)
  276. }
  277. calendars[validity] = schedule
  278. }
  279. return calendars, nil
  280. }
  281. func unmarshalVehicles(timetableHome string) (Vehicles, error) {
  282. vehicles := Vehicles{}
  283. vehiclesFile, err := os.Open(filepath.Join(timetableHome, "vehicles.bare"))
  284. if err != nil {
  285. return vehicles, fmt.Errorf("while opening file: %w", err)
  286. }
  287. defer vehiclesFile.Close()
  288. for err == nil {
  289. vehicle := Vehicle{}
  290. err = bare.UnmarshalReader(vehiclesFile, &vehicle)
  291. if err != nil {
  292. if err == io.EOF {
  293. break
  294. } else {
  295. return vehicles, fmt.Errorf("while unmarshaling: %w", err)
  296. }
  297. }
  298. vehicles[vehicle.Id] = vehicle
  299. }
  300. return vehicles, nil
  301. }
  302. func readVehicles(feedHome string, versions []Version) (FeedVehicles, error) {
  303. vehicles := FeedVehicles{}
  304. for _, v := range versions {
  305. validity := Validity(v.String())
  306. timetableHome := filepath.Join(feedHome, string(validity))
  307. versionVehicles, err := unmarshalVehicles(timetableHome)
  308. if err != nil {
  309. return vehicles, fmt.Errorf("while unmarshaling for %s: %w", v, err)
  310. }
  311. vehicles[validity] = versionVehicles
  312. }
  313. return vehicles, nil
  314. }
  315. func createPositionIndex(feedHome string, versions []Version) (FeedPositionIndex, error) {
  316. feedPositionIndex := FeedPositionIndex{}
  317. for _, v := range versions {
  318. positionIndex := rtreego.NewTree(2, 25, 50)
  319. validity := Validity(v.String())
  320. timetableHome := filepath.Join(feedHome, string(validity))
  321. stopsFile, err := os.Open(filepath.Join(timetableHome, "stops.bare"))
  322. if err != nil {
  323. return feedPositionIndex, fmt.Errorf("while opening stops file: %w", err)
  324. }
  325. defer stopsFile.Close()
  326. for err == nil {
  327. stop := Stop{}
  328. err = bare.UnmarshalReader(stopsFile, &stop)
  329. if err != nil {
  330. if err == io.EOF {
  331. break
  332. } else {
  333. return feedPositionIndex, fmt.Errorf("while unmarshaling: %w", err)
  334. }
  335. }
  336. stop.Name = ""
  337. stop.NodeName = ""
  338. stop.ChangeOptions = nil
  339. stop.Zone = ""
  340. stop.Order = nil
  341. positionIndex.Insert(stop)
  342. feedPositionIndex[validity] = positionIndex
  343. }
  344. }
  345. return feedPositionIndex, nil
  346. }
  347. func EnableFeeds(cfg config.Config, traffic *Traffic) {
  348. feedsMap := RegisterFeeds()
  349. feeds := map[string]Feed{}
  350. for _, enabledFeed := range cfg.EnabledFeeds {
  351. if _, ok := feedsMap[enabledFeed]; !ok {
  352. log.Printf("feed %s not registered, ignoring\n", enabledFeed)
  353. continue
  354. }
  355. feeds[enabledFeed] = feedsMap[enabledFeed]
  356. }
  357. traffic.Feeds = feeds
  358. }
  359. func Initialise(sigChan chan os.Signal, doneChan chan bool, initedChan chan bool, cfg config.Config,
  360. traffic *Traffic) {
  361. bare.MaxMapSize(8192)
  362. alreadyInitialised := false
  363. for {
  364. sig := <-sigChan
  365. if sig == os.Interrupt {
  366. break
  367. } // else it's SIGUSR1, reloading config
  368. allVersions := GlobalVersions{}
  369. codeIndexes := GlobalCodeIndex{}
  370. nameIndexes := GlobalNameIndex{}
  371. lineIndexes := GlobalNameIndex{}
  372. lineIdIndexes := GlobalCodeIndex{}
  373. tripIndexes := GlobalNameIndex{}
  374. calendars := GlobalCalendar{}
  375. vehicles := GlobalVehicles{}
  376. positionIndexes := GlobalPositionIndex{}
  377. feedInfos := map[Validity]map[string]FeedInfo{}
  378. for _, feed := range traffic.Feeds {
  379. feedID := feed.String()
  380. feedHome := filepath.Join(cfg.FeedsPath, feedID)
  381. err := file.UnpackTraffic(cfg.FeedsPath, feedID)
  382. if err != nil {
  383. log.Printf("while unpacking TRAFFIC in feed %s: %v\n", feed, err)
  384. continue
  385. }
  386. feedValidities, err := ListVersions(cfg, feed)
  387. if err != nil {
  388. log.Printf("while listing TRAFFIC versions in feed %s: %v\n", feed, err)
  389. continue
  390. }
  391. for _, version := range feedValidities {
  392. feedInfo, err := getFeedInfo(cfg.FeedsPath, feedID, Validity(version))
  393. if err != nil {
  394. log.Printf("while getting feed info for feed %s, version %s: %v\n", feed, version, err)
  395. continue
  396. }
  397. if feedInfos[Validity(feedInfo.ValidSince+"_"+feedInfo.ValidTill)] == nil {
  398. feedInfos[Validity(feedInfo.ValidSince+"_"+feedInfo.ValidTill)] = map[string]FeedInfo{}
  399. }
  400. feedInfos[Validity(feedInfo.ValidSince+"_"+feedInfo.ValidTill)][feedID] = feedInfo
  401. }
  402. traffic.FeedInfos = feedInfos
  403. feedVersions, deletedValidities, err := CleanOldVersions(cfg, feed, traffic, feedID, feedValidities)
  404. if err != nil {
  405. log.Printf("while cleaning old TRAFFIC versions in feed %s: %v\n",
  406. feed, err)
  407. continue
  408. }
  409. for _, deletedVersion := range deletedValidities {
  410. delete(feedInfos, Validity(deletedVersion))
  411. }
  412. allVersions[feedID] = feedVersions
  413. codeIndexes[feedID], nameIndexes[feedID], lineIndexes[feedID], lineIdIndexes[feedID], tripIndexes[feedID],
  414. err = readIndexes(feedHome, feedVersions)
  415. if err != nil {
  416. log.Printf("while reading indexes in feed %s: %v\n", feed, err)
  417. continue
  418. }
  419. calendars[feedID], err = readCalendar(feedHome, feedVersions)
  420. if err != nil {
  421. log.Printf("while reading calendars in feed %s: %v\n", feed, err)
  422. continue
  423. }
  424. vehicles[feedID], err = readVehicles(feedHome, feedVersions)
  425. if err != nil {
  426. log.Printf("while reading vehicles in feed %s: %v\n", feed, err)
  427. continue
  428. }
  429. positionIndexes[feedID], err = createPositionIndex(feedHome, feedVersions)
  430. if err != nil {
  431. log.Printf("while creating position index in feed %s: %v\n", feed, err)
  432. continue
  433. }
  434. }
  435. traffic.CodeIndexes = codeIndexes
  436. traffic.NameIndexes = nameIndexes
  437. traffic.LineIndexes = lineIndexes
  438. traffic.LineIdIndexes = lineIdIndexes
  439. traffic.TripIndexes = tripIndexes
  440. traffic.Versions = allVersions
  441. traffic.Calendars = calendars
  442. traffic.Vehicles = vehicles
  443. traffic.PositionIndexes = positionIndexes
  444. traffic.FeedInfos = feedInfos
  445. log.Println("Initialised")
  446. if !alreadyInitialised {
  447. initedChan <- true
  448. }
  449. }
  450. doneChan <- true
  451. }
  452. func GetTripFromStop(tripID string, stopCode string, context Context, traffic *Traffic) ([]TimedStopStub, error) {
  453. stubs := []TimedStopStub{}
  454. var (
  455. order = -1
  456. trip Trip
  457. err error
  458. baseTime uint = 0
  459. time uint = 0
  460. )
  461. file, err := openTrips(context)
  462. if err != nil {
  463. return stubs, fmt.Errorf("while opening trips: %w", err)
  464. }
  465. defer file.Close()
  466. if stopCode != "" {
  467. startingStop, err := GetStop(stopCode, context, traffic)
  468. if err != nil {
  469. return stubs, fmt.Errorf("while getting starting stop: %w", err)
  470. }
  471. tripOffset := -1
  472. order = -1
  473. o := startingStop.Order[tripID]
  474. tripOffset = int(o.TripOffset)
  475. order = o.Sequence
  476. if tripOffset == -1 {
  477. return stubs, fmt.Errorf("trip for starting stop not found")
  478. }
  479. trip, err = GetTripByOffset(file, uint(tripOffset), context)
  480. if err != nil {
  481. return stubs, fmt.Errorf("while getting trip: %w", err)
  482. }
  483. } else {
  484. trip, err = GetTrip(file, tripID, context, traffic)
  485. if err != nil {
  486. return stubs, fmt.Errorf("while getting trip: %w", err)
  487. }
  488. }
  489. for _, departure := range trip.Departures {
  490. if departure.StopSequence >= order {
  491. stop, err := getStopByOffset(uint(departure.StopOffset), context, traffic)
  492. if err != nil {
  493. return stubs, fmt.Errorf("while getting stop: %w", err)
  494. }
  495. if baseTime != 0 {
  496. time = departure.Time - baseTime
  497. }
  498. stubs = append(stubs, TimedStopStub{
  499. StopStub: StopStub{
  500. Code: stop.Code,
  501. Name: stop.Name,
  502. NodeName: stop.NodeName,
  503. Zone: stop.Zone,
  504. OnDemand: departure.Pickup == BY_DRIVER || departure.Dropoff == BY_DRIVER,
  505. },
  506. Time: time,
  507. })
  508. }
  509. }
  510. return stubs, nil
  511. }
  512. func getStopByOffset(offset uint, context Context, traffic *Traffic) (Stop, error) { // todo offset should be uint64 everywhere
  513. result := _Result{
  514. Filename: "stops.bare",
  515. Offset: offset,
  516. TimetableHome: filepath.Join(context.DataHome, context.FeedID, string(context.Version)),
  517. }
  518. r, e := gott.NewResult(result).
  519. Bind(openFile).
  520. Bind(seek).
  521. Bind(unmarshalStop).
  522. Finish()
  523. if e != nil {
  524. return Stop{}, e
  525. } else {
  526. return r.(_Result).Stop, nil
  527. }
  528. }
  529. func getLineByOffset(offset uint, dataHome string, feedName string,
  530. versionCode Validity) (Line, error) {
  531. result := _Result{
  532. Filename: "lines.bare",
  533. Offset: offset,
  534. TimetableHome: filepath.Join(dataHome, feedName, string(versionCode)),
  535. }
  536. r, e := gott.NewResult(result).
  537. Bind(openFile).
  538. Bind(seek).
  539. Bind(unmarshalLine).
  540. Finish()
  541. if e != nil {
  542. return Line{}, e
  543. } else {
  544. return r.(_Result).Line, nil
  545. }
  546. }
  547. func getFeedInfo(dataHome string, feedName string, versionCode Validity) (FeedInfo, error) {
  548. result := _Result{
  549. Filename: "feed_info.bare",
  550. TimetableHome: filepath.Join(dataHome, feedName, string(versionCode)),
  551. }
  552. r, e := gott.NewResult(result).
  553. Bind(openFile).
  554. Bind(unmarshalFeedInfo).
  555. Finish()
  556. if e != nil {
  557. return FeedInfo{}, e
  558. } else {
  559. return r.(_Result).FeedInfo, nil
  560. }
  561. }
  562. func GetStop(stopCode string, context Context, traffic *Traffic) (Stop, error) {
  563. codeIndex := traffic.CodeIndexes[context.FeedID][context.Version]
  564. return getStopByOffset(codeIndex[stopCode], context, traffic)
  565. }
  566. func GetStopStub(stopCode string, lineID string, context Context, traffic *Traffic) (StopStub, error) {
  567. stop, err := GetStop(stopCode, context, traffic)
  568. if err != nil {
  569. return StopStub{}, err
  570. }
  571. var trip Trip
  572. var stopOrder = -1
  573. file, err := openTrips(context)
  574. if err != nil {
  575. return StopStub{}, fmt.Errorf("while opening trips: %w", err)
  576. }
  577. defer file.Close()
  578. for _, order := range stop.Order {
  579. offset := order.TripOffset
  580. trip, _ = GetTripByOffset(file, offset, context)
  581. if trip.LineID == lineID {
  582. stopOrder = order.Sequence
  583. break
  584. }
  585. }
  586. if stopOrder == -1 {
  587. return StopStub{}, fmt.Errorf("cannot the stop on given line")
  588. }
  589. ix, ok := slices.BinarySearchFunc(trip.Departures, stopOrder, func(d Departure, stopOrder int) int {
  590. return d.StopSequence - stopOrder
  591. })
  592. if !ok {
  593. return StopStub{}, fmt.Errorf("cannot find departure at sequence %d", stopOrder)
  594. }
  595. stopStub := StopStub{
  596. Code: stop.Code,
  597. Name: stop.Name,
  598. NodeName: stop.NodeName,
  599. Zone: stop.Zone,
  600. OnDemand: trip.Departures[ix].Pickup == BY_DRIVER || trip.Departures[ix].Dropoff == BY_DRIVER,
  601. }
  602. return stopStub, nil
  603. }
  604. func GetLine(id string, context Context, traffic *Traffic) (Line, error) {
  605. index := traffic.LineIdIndexes[context.FeedID][context.Version]
  606. return getLineByOffset(index[id], context.DataHome, context.FeedID, context.Version)
  607. }
  608. func GetLineOld(name string, context Context, traffic *Traffic) (Line, error) {
  609. index := traffic.LineIndexes[context.FeedID][context.Version]
  610. for _, o := range index {
  611. cleanedName, err := CleanQuery(name, traffic.Feeds[context.FeedID])
  612. if err != nil {
  613. return Line{}, err
  614. }
  615. if o.Name == cleanedName {
  616. return getLineByOffset(o.Offsets[0], context.DataHome, context.FeedID, context.Version)
  617. }
  618. }
  619. return Line{}, nil
  620. }
  621. func QueryLines(query string, dataHome string, feedName string,
  622. versionCode Validity, traffic *Traffic) ([]Line, error) {
  623. linesSet := map[string]Line{}
  624. index := traffic.LineIndexes[feedName][versionCode]
  625. cleanQuery, err := CleanQuery(query, traffic.Feeds[feedName])
  626. if err != nil {
  627. return []Line{}, fmt.Errorf("while cleaning query: %w", err)
  628. }
  629. results := fuzzy.FindFrom(cleanQuery, index)
  630. for _, result := range results {
  631. for _, offset := range index[result.Index].Offsets {
  632. line, err := getLineByOffset(offset, dataHome, feedName, versionCode)
  633. if err != nil {
  634. return []Line{}, fmt.Errorf("while getting line for %s: %w", result.Str, err)
  635. }
  636. linesSet[line.Id] = line
  637. }
  638. }
  639. lines := make([]Line, len(linesSet))
  640. i := 0
  641. for _, line := range linesSet {
  642. lines[i] = line
  643. i++
  644. }
  645. return lines, nil
  646. }
  647. func QueryStops(query string, context Context, traffic *Traffic) ([]Stop, error) {
  648. stopsSet := map[string]Stop{}
  649. nameIndex := traffic.NameIndexes[context.FeedID][context.Version]
  650. results := fuzzy.FindFrom(query, nameIndex)
  651. for _, result := range results {
  652. for _, offset := range nameIndex[result.Index].Offsets {
  653. stop, err := getStopByOffset(offset, context, traffic)
  654. if err != nil {
  655. return []Stop{}, err
  656. }
  657. stopsSet[stop.Id] = stop
  658. }
  659. }
  660. stops := make([]Stop, len(stopsSet))
  661. i := 0
  662. for _, stop := range stopsSet {
  663. stops[i] = stop
  664. i++
  665. }
  666. return stops, nil
  667. }
  668. func GetStopsNear(location Position, context Context, traffic *Traffic) ([]Stop, error) {
  669. stops := []Stop{}
  670. positionIndex := traffic.PositionIndexes[context.FeedID][context.Version]
  671. codeIndex := traffic.CodeIndexes[context.FeedID][context.Version]
  672. spatials := positionIndex.NearestNeighbors(12, rtreego.Point{location.Lat, location.Lon})
  673. for _, spatial := range spatials {
  674. stop, err := getStopByOffset(codeIndex[spatial.(Stop).Code], context, traffic)
  675. if err != nil {
  676. return stops, fmt.Errorf("while getting stop by offset for %s: %w", spatial.(Stop).Code, err)
  677. }
  678. stops = append(stops, stop)
  679. }
  680. return stops, nil
  681. }
  682. func GetLanguage(ctx Context) (string, error) {
  683. feedInfo, err := getFeedInfo(ctx.DataHome, ctx.FeedID, ctx.Version)
  684. return feedInfo.Language, err
  685. }
  686. func CleanOldVersions(cfg config.Config, feed Feed, t *Traffic, feedID string, allValidities []string) ([]Version, []string, error) {
  687. feedVersions := []Version{}
  688. deletedValidities := []string{}
  689. timezone, err := GetTimezone(Stop{}, t, feedID)
  690. if err != nil {
  691. return feedVersions, deletedValidities, fmt.Errorf("while getting timezone: %w", err)
  692. }
  693. now := time.Now().In(timezone)
  694. versionsMap := map[string]Version{}
  695. allVersions := []Version{}
  696. for _, validity := range allValidities {
  697. version, err := MakeVersionTimezone(validity, timezone)
  698. if err != nil {
  699. return feedVersions, deletedValidities, fmt.Errorf("while making version of %s: %w", version, err)
  700. }
  701. allVersions = append(allVersions, version)
  702. versionsMap[validity] = version
  703. }
  704. validVersions := FindValidVersions(allVersions, now)
  705. validVersionsMap := map[string]bool{}
  706. for _, version := range validVersions {
  707. validVersionsMap[version.String()] = true
  708. }
  709. err = file.CleanOldVersions(FeedPath(cfg, feed), validVersionsMap)
  710. if err != nil {
  711. return feedVersions, deletedValidities, fmt.Errorf("while removing files: %w", err)
  712. }
  713. for _, version := range validVersions {
  714. feedVersions = append(feedVersions, version)
  715. }
  716. for _, version := range allVersions {
  717. if _, ok := validVersionsMap[version.String()]; !ok {
  718. deletedValidities = append(deletedValidities, version.String())
  719. }
  720. }
  721. return feedVersions, deletedValidities, nil
  722. }
  723. func createSmallerRect(side float64, rect *rtreego.Rect) (*rtreego.Rect, Position, Position, error) {
  724. halfSide := side / 2
  725. latMid := rect.PointCoord(0) + (rect.LengthsCoord(0) / 2)
  726. lonMid := rect.PointCoord(1) + (rect.LengthsCoord(1) / 2)
  727. lb := Position{Lat: latMid - halfSide, Lon: lonMid - halfSide}
  728. rt := Position{Lat: latMid + halfSide, Lon: lonMid + halfSide}
  729. rect, err := rtreego.NewRectFromPoints(rtreego.Point{lb.Lat, lb.Lon}, rtreego.Point{rt.Lat, rt.Lon})
  730. return rect, lb, rt, err
  731. }
  732. func GetStopsIn(lb, rt Position, context Context, traffic *Traffic) ([]Stop, error) {
  733. limit := 0.0005
  734. side := 0.0224 // sqrt(0.0005)
  735. stops := []Stop{}
  736. // TODO does it take into account rect 179 -> -179 latitude?
  737. rect, err := rtreego.NewRectFromPoints(rtreego.Point{lb.Lat, lb.Lon}, rtreego.Point{rt.Lat, rt.Lon})
  738. if err != nil {
  739. return stops, fmt.Errorf("while creating a rect: %w", err)
  740. }
  741. if rect.Size() > limit {
  742. rect, _, _, err = createSmallerRect(side, rect)
  743. if err != nil {
  744. return stops, fmt.Errorf("while creating the smaller rect: %w", err)
  745. }
  746. }
  747. positionIndex := traffic.PositionIndexes[context.FeedID][context.Version]
  748. codeIndex := traffic.CodeIndexes[context.FeedID][context.Version]
  749. spatials := positionIndex.SearchIntersect(rect)
  750. for _, spatial := range spatials {
  751. stop, err := getStopByOffset(codeIndex[spatial.(Stop).Code], context, traffic)
  752. if err != nil {
  753. return stops, fmt.Errorf("while getting stop by offset for %s: %w", spatial.(Stop).Code, err)
  754. }
  755. stops = append(stops, stop)
  756. }
  757. return stops, nil
  758. }
  759. func GetVehiclesIn(lb, rt Position, context Context, t *Traffic) ([]VehicleStatus, error) {
  760. limit := 0.0005
  761. side := 0.0224 // sqrt(0.0005)
  762. vehicles := []VehicleStatus{}
  763. rect, err := rtreego.NewRectFromPoints(rtreego.Point{lb.Lat, lb.Lon}, rtreego.Point{rt.Lat, rt.Lon})
  764. if err != nil {
  765. return vehicles, fmt.Errorf("while creating a rect: %w", err)
  766. }
  767. if rect.Size() > limit {
  768. rect, lb, rt, err = createSmallerRect(side, rect)
  769. if err != nil {
  770. return vehicles, fmt.Errorf("while creating the smaller rect: %w", err)
  771. }
  772. }
  773. vehiclesRt := getVehiclePositions(context, t, lb, rt)
  774. for _, vehicleRt := range vehiclesRt {
  775. if rt.Lon < float64(vehicleRt.Longitude) || lb.Lon > float64(vehicleRt.Longitude) {
  776. continue
  777. }
  778. lat := float64(vehicleRt.Latitude)
  779. if lb.Lat < rt.Lat {
  780. if lb.Lat < lat && lat < rt.Lat {
  781. vehicles = append(vehicles, vehicleRt)
  782. }
  783. } else {
  784. if lat > lb.Lat || lat < rt.Lat {
  785. vehicles = append(vehicles, vehicleRt)
  786. }
  787. }
  788. }
  789. return vehicles, nil
  790. }