func lookupJourneyRecords(line, bound, toStopSeq string, departureTime time.Time, method, mixedType string) []selectdb.JourneyRecord { toStopSeqNum, err := strconv.Atoi(toStopSeq) if err != nil { logger.GetLogger().Panic(err) } /* To stop seq should be greater than 0, since all valid journeys will have toStopSeq >= 1 */ if toStopSeqNum == 0 { logger.GetLogger().Panic("Unexpected stop sequence 0 in toStopSeq") } fromStopSeqNum := toStopSeqNum - 1 fromStop := selectdb.SelectStopWithStopSeq(line, bound, fromStopSeqNum) toStop := selectdb.SelectStopWithStopSeq(line, bound, toStopSeqNum) timeRanges, options := getTimeRangesAndOptions(method, line, bound, departureTime, mixedType) journeyRecords := selectdb.SelectJourneyHistory(fromStop, toStop, timeRanges, options) return journeyRecords }
/* Returns the predictios given by the mixed method, also historic and and real time methods */ func PredictJourneyWithMixedData(mixedMethod Method, line, bound string, intermediateStops map[string]map[string]bool, intermediateStopsOrdered []string, departureTime time.Time) (map[string]map[string]map[string]int64, map[string]map[string]map[string]int64, map[string]map[string]map[string]int64, error) { historicMethod, realTimeMethod, _ := mixedMethod.Decompose() historicDateRange, historicOptions := setDateRangeAndOptions(historicMethod, line, bound, departureTime) historicData, _ := predictJourneyWithHistoricData(intermediateStops, intermediateStopsOrdered, departureTime, historicDateRange, historicOptions) _, realTimeOptions := setDateRangeAndOptions(realTimeMethod, line, bound, departureTime) realTimeData, _ := predictJourneyWithRealTimeData(intermediateStops, departureTime, realTimeOptions, nil) /* Get complete journey history entries for real time */ if mixedMethod.IsDelayReverseLookupBased() { realTimeOptions["realtimemethod"] = "last" realTimeOptions["realtimecount"] = strconv.Itoa(maxDelayBusCountThreshold()) realTimeOptions["slidingwindow"] = "120" realTimeOptions["norank"] = "true" } slidingWindow := SlidingWindow if realTimeOptions["slidingwindow"] != "" { slidingWindowInMinutes, _ := strconv.Atoi(realTimeOptions["slidingwindow"]) slidingWindow = time.Duration(slidingWindowInMinutes) * time.Minute } timeRanges := map[time.Time]time.Time{ departureTime.Add(-slidingWindow): departureTime, } /* Combine historic and real time data */ predictionData := make(map[string]map[string]map[string]int64) /* Accumulator keeping track of time to each stop - to decide whether to use real time data */ timeToStop := time.Duration(0) ChooseHistoricOrRealTime: for fromStop, details := range intermediateStops { for toStop, _ := range details { historicFromStopDetails, historicExists := historicData[fromStop] realTimeFromStopDetails, realTimeExists := realTimeData[fromStop] realTimeJourneyHistory := selectdb.SelectJourneyHistory(fromStop, toStop, timeRanges, realTimeOptions) if historicExists && realTimeExists { historicEstimate := time.Duration(historicFromStopDetails[toStop]["estimate"]) * time.Second realTimeEstimate := time.Duration(realTimeFromStopDetails[toStop]["estimate"]) * time.Second /* Case 1: Delay lookup */ if mixedMethod.IsDelayReverseLookupBased() { realTimeEstimates := journeyHistoryToRealTimeEstimates(realTimeJourneyHistory) shouldUseHistoric, delayBusCount, _ := HandleDelayReverseLookup(fromStop, toStop, departureTime, timeToStop, historicEstimate, realTimeEstimates) if shouldUseHistoric { historicFromStopDetails[toStop]["mixedType"] = 1 predictionData[fromStop] = historicFromStopDetails timeToStop = timeToStop + historicEstimate } else { realTimeFromStopDetails[toStop]["mixedType"] = 2 predictionData[fromStop] = realTimeFromStopDetails timeToStop = timeToStop + realTimeEstimate } predictionData[fromStop][toStop]["delayBusCount"] = int64(delayBusCount) continue } /* Case 2: Normal */ /* Use real time estimate only if all of real time journey history lie above / below threshold */ for i := len(realTimeJourneyHistory) - 1; i >= 0; i-- { realTimeEstimate := time.Duration(realTimeJourneyHistory[i].TimeTaken) * time.Second if historicEstimate-DelayThreshold < realTimeEstimate && realTimeEstimate < historicEstimate+DelayThreshold { /* Use historic estimate if lying within range */ historicFromStopDetails[toStop]["mixedType"] = 1 predictionData[fromStop] = historicFromStopDetails timeToStop = timeToStop + historicEstimate continue ChooseHistoricOrRealTime } } /* Otherwise use real time estimate */ realTimeFromStopDetails[toStop]["mixedType"] = 2 predictionData[fromStop] = realTimeFromStopDetails timeToStop = timeToStop + realTimeEstimate } } } return predictionData, historicData, realTimeData, nil }
/* Delay characteristics represented by map fromId => (toId => (day of week => (time of day => (delayThreshold => (numBuses => expected lasting time))))) */ func computeDelayCharacteristics(adjacentStops map[string]map[string]bool) map[string]map[string]map[int]map[int]map[time.Duration]map[int]time.Duration { delayCharacteristics := make(map[string]map[string]map[int]map[int]map[time.Duration]map[int]time.Duration) /* Channel for delay characteristics */ ch := make(chan map[string]map[string]map[int]map[int]map[time.Duration]map[int]time.Duration) lineBounds := selectdb.SelectLineBoundsServingMultipleAdjacentStops(adjacentStops) for fromStop, details := range adjacentStops { fromStopDelayCharacteristics := make(map[string]map[int]map[int]map[time.Duration]map[int]time.Duration) for toStop, _ := range details { go func(fromStop, toStop string) { /* Form time ranges - Compute for the weekday/weekend of yesterday */ timeRanges := make(map[time.Time]time.Time) endOfYesterday := time.Now().In(timezone.Get()).Truncate(24 * time.Hour) startOfYesterday := endOfYesterday.AddDate(0, 0, -1) weekday := startOfYesterday.Weekday() for _, day := range daysToLookBack { timeRanges[startOfYesterday.AddDate(0, 0, -day)] = endOfYesterday.AddDate(0, 0, -day) } /* Select journey history */ journeyHistory := selectdb.SelectJourneyHistory(fromStop, toStop, timeRanges, nil) times := make([]time.Time, len(journeyHistory)) actualDurations := make([]time.Duration, len(journeyHistory)) for i, record := range journeyHistory { times[i] = record.ToTimestamp actualDurations[i] = time.Duration(record.TimeTaken) * time.Second } /* Select historic journey times */ var line, bound string for k, v := range lineBounds[fromStop][toStop] { line = k bound = v } /* Generate times at intervals for prediction efficiency */ timesAtIntervals := make([]time.Time, 0) for startTime, endTime := range timeRanges { for t := startTime.Round(predictionInterval); t.Before(endTime.Add(predictionInterval)); t = t.Add(predictionInterval) { timesAtIntervals = append(timesAtIntervals, t) } } historicTimes := PredictJourneyTimes(line, bound, fromStop, toStop, timesAtIntervals, DefaultHistoric(), nil) historicDurations := make([]time.Duration, len(journeyHistory)) for i, t := range times { historicDurations[i] = historicTimes[t.Round(predictionInterval)] } delayCharacteristics := map[string]map[string]map[int]map[int]map[time.Duration]map[int]time.Duration{ fromStop: map[string]map[int]map[int]map[time.Duration]map[int]time.Duration{ toStop: computeDelayCharacteristicsForStopPair(fromStop, toStop, weekday, times, actualDurations, historicDurations), }, } updateDelayCharacteristics(delayCharacteristics) ch <- delayCharacteristics }(fromStop, toStop) fromStopDelayCharacteristics[toStop] = nil // fromStopDelayCharacteristics[toStop] = computeDelayCharacteristicsForStopPair(fromStop, toStop, times, actualDurations, historicDurations) } // delayCharacteristics[fromStop] = fromStopDelayCharacteristics delayCharacteristics[fromStop] = fromStopDelayCharacteristics } for _, v := range adjacentStops { for range v { <-ch /*entry := <-ch delayCharacteristics[entry.fromStop][entry.toStop] = entry.entry updateDelayCharacteristics(delayCharacteristics)*/ } } return delayCharacteristics }