func nextBusHandler(w http.ResponseWriter, r *http.Request) { /* Extract fields in GET request */ stopsStrCommaDelimited := r.URL.Query().Get("stops") numRouteOptionsPerStopCommaDelimited := r.URL.Query().Get("numRouteOptionsPerStop") lineBoundCombinationsCommaDelimited := r.URL.Query().Get("lineBoundCombinations") /* Map of target buses => target stop map (vehicleId => naptanId) */ counter := 0 numRouteOptionsPerStopArr := strings.Split(numRouteOptionsPerStopCommaDelimited, ",") lineBoundCombinationsArr := strings.Split(lineBoundCombinationsCommaDelimited, ",") stops := strings.Split(stopsStrCommaDelimited, ",") stopNextBusesMap := make(map[string][]TflNextBus) for i, stop := range stops { lineBoundCombinations := make(map[string]string) numRouteOptions, err := strconv.Atoi(numRouteOptionsPerStopArr[i]) if err != nil { writeError(w, err) } for i := 0; i < numRouteOptions; i++ { /* Extract line bound combination for stop */ line := lineBoundCombinationsArr[counter] counter++ bound := lineBoundCombinationsArr[counter] counter++ lineBoundCombinations[line] = bound } /* Find next buses */ nextBuses := selectdb.SelectNextBuses(lineBoundCombinations, stop, time.Now().In(timezone.Get())) tflNextBuses := make([]TflNextBus, len(nextBuses)) for i, nextBus := range nextBuses { tflNextBuses[i] = TflNextBus{ Line: nextBus.Line, Bound: nextBus.Bound, VehicleId: nextBus.VehicleId, ExpectedArrivalTime: formatISOTime(nextBus.ExpectedArrivalTime), AlreadyDeparted: nextBus.AlreadyDeparted, } } addToNextBusesCurrentLocations(&tflNextBuses) stopNextBusesMap[stop] = tflNextBuses } /* Write HTML output */ writeNextBusesDetails(w, stopNextBusesMap) }
/* Change the predictions inside directions to use our own predictions */ func (directions *TflDirections) UpdatePredictions(isDeparting bool, departureOrArrivalTime time.Time) error { journeys := &directions.Journeys for i := 0; i < len(*journeys); i++ { /* Counter for keeping track of times for prediction */ cumulativeTime := departureOrArrivalTime if !isDeparting { /* Approximate departure time for arriving case by subtracting tfl estimate */ cumulativeTime = departureOrArrivalTime.Add(-time.Duration((*journeys)[i].TflDuration) * time.Minute) } initialTime := cumulativeTime legs := &(*journeys)[i].Legs for j := 0; j < len(*legs); j++ { leg := &(*legs)[j] leg.DepartureTime = formatISOTime(cumulativeTime) if leg.Mode.Id == "bus" { var line string var bound string var stopPoints []string lineBoundCombination := make(map[string]string) /* Update naptan ids and coordinates of intermediate stops */ for i, routeOption := range leg.RouteOptions { var err error line = routeOption.LineIdentifier.Id stopPoints, bound, err = searchIntermediateStops(line, leg.DeparturePoint.IcsCode, leg.ArrivalPoint.IcsCode) if err != nil { /* Bus may not be Tfl operated (e.g. bus 555 to Heathrow). In such case skip. */ continue } /* Add bound to struct */ leg.RouteOptions[i].LineIdentifier.Bound = bound lineBoundCombination[line] = bound } /* If no stop points found, invalidate journey for removal afterwards*/ if len(stopPoints) == 0 { (*journeys)[i].IsInvalid = true continue } leg.IntermediateStops = make([]TflStopPoint, len(stopPoints)) details := selectdb.SelectMultipleStops(stopPoints) for i, naptanId := range stopPoints { latitude, _ := strconv.ParseFloat(details[naptanId]["latitude"], 64) longitude, _ := strconv.ParseFloat(details[naptanId]["longitude"], 64) leg.IntermediateStops[i] = TflStopPoint{ Name: details[naptanId]["name"], NaptanId: naptanId, Latitude: latitude, Longitude: longitude, } } /* Update bus prediction */ newPrediction, historicBasedPrediction, realTimeBasedPrediction, newPredictionAdjacentStops, err := updateBusPrediction(line, bound, stopPoints, cumulativeTime) if err == nil { leg.DurationInMinutes = int(newPrediction.Minutes()) leg.HistoricDurationInMinutes = int(historicBasedPrediction.Minutes()) leg.RealTimeDurationInMinutes = int(realTimeBasedPrediction.Minutes()) } /* Update next bus arrivals */ fromStop := leg.IntermediateStops[0].NaptanId nextBuses := selectdb.SelectNextBuses(lineBoundCombination, fromStop, time.Now().In(timezone.Get())) leg.NextBuses = make([]TflNextBus, len(nextBuses)) for i, nextBus := range nextBuses { leg.NextBuses[i] = TflNextBus{ Line: nextBus.Line, Bound: nextBus.Bound, VehicleId: nextBus.VehicleId, ExpectedArrivalTime: formatISOTime(nextBus.ExpectedArrivalTime), AlreadyDeparted: nextBus.AlreadyDeparted, } } addToNextBusesCurrentLocations(&leg.NextBuses) /* Find out next bus frequency */ averageWaitTime, minWaitTime, maxWaitTime, err := selectdb.SelectAverageWaitTimes(lineBoundCombination, initialTime) if err != nil { logger.GetLogger().Error(err.Error()) } leg.FrequencyLow = int(minWaitTime.Minutes()) leg.FrequencyHigh = int(maxWaitTime.Minutes()) nextBusFlag := false if len(nextBuses) > 0 { /* Find first bus that is going to arrive and the user is able to catch */ for _, nextBus := range nextBuses { nextBusTime := nextBus.ExpectedArrivalTime if nextBusTime.After(cumulativeTime) { leg.TimeToNextBus = int(nextBusTime.Sub(cumulativeTime).Minutes()) cumulativeTime = nextBusTime nextBusFlag = true break } } } if len(nextBuses) == 0 || (len(nextBuses) > 0 && !nextBusFlag) { /* Handle special case if lack of next buses, use average wait times instead */ leg.TimeToNextBus = int(averageWaitTime.Minutes()) cumulativeTime = cumulativeTime.Add(averageWaitTime) } /* Update cumulative time */ leg.IntermediateStops[0].ExpectedArrival = int(cumulativeTime.Sub(initialTime).Minutes()) for i := 0; i < len(stopPoints)-1; i++ { fromStop := stopPoints[i] toStop := stopPoints[i+1] cumulativeTime = cumulativeTime.Add(newPredictionAdjacentStops[fromStop][toStop]) leg.IntermediateStops[i+1].ExpectedArrival = int(cumulativeTime.Sub(initialTime).Minutes()) } } else { cumulativeTime = cumulativeTime.Add(time.Duration(leg.DurationInMinutes) * time.Minute) } } } /* Remove invalid journeys */ for i := len(*journeys) - 1; i >= 0; i-- { if (*journeys)[i].IsInvalid { *journeys = append((*journeys)[:i], (*journeys)[i+1:]...) } } return nil }