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
}