func performancePlotHandler(w http.ResponseWriter, r *http.Request) {
	/* Extract fields in GET request */
	line := r.URL.Query().Get("line")
	bound := r.URL.Query().Get("bound")
	origin := r.URL.Query().Get("origin")
	departureTime := parseISOTime(r.URL.Query().Get("departureTime"))

	/* Find out which vehicle produced the data and list its journey history */
	vehicleId, vehicleHistory := lookupVehicleHistory(line, bound, origin, departureTime)

	/* Some additional info for nice display*/
	originName := selectdb.SelectStop(origin)
	destinationName := selectdb.SelectStop(selectdb.SelectTerminus(line, bound))

	/* Write HTML output */
	writePerformancePlotDetails(w, PerformancePlotDetails{
		Line:            line,
		Bound:           bound,
		Origin:          origin,
		DepartureTime:   formatTime(departureTime),
		OriginName:      originName,
		DestinationName: destinationName,
		VehicleId:       vehicleId,
		VehicleHistory:  vehicleHistory,
	})
}
func SimulateJourney2(line, bound string, details map[string]string) {

	/* Extract origin and destination from details (if any), if not assume entire journey */
	origin, exists := details["fromStop"]
	if !exists {
		origin = selectdb.SelectOrigin(line, bound)
	}
	destination, exists := details["toStop"]
	if !exists {
		destination = selectdb.SelectTerminus(line, bound)
	}
	stopPoints := selectdb.SelectIntermediateStops(line, bound, origin, destination)
	if len(stopPoints) == 0 {
		logger.GetLogger().Warning("Direction %v doesn't exist for line %v. Exiting...", bound, line)
		return
	}

	logger.GetLogger().Info("Blocking until line %v (%v) departs origin: %v", line, bound, origin)
	vehicleId, departureTime, err := blockUntilBusDeparts(line, bound, origin)

	/* May happen that night buses are not running during the day, so journey should not be simulated.
	   Wait for 30 minutes and check again. */
	if err != nil {
		logger.GetLogger().Error(err.Error())

		time.Sleep(sleepDuration)
		SimulateJourney2(line, bound, details)
		return
	}

	/* Spawn a new thread and simulate next journey now that bus has departed */
	go SimulateJourney2(line, bound, details)

	tflExpectedTime, err := getTflExpectedJourneyTime(line, bound, origin, destination, vehicleId)
	if err != nil {
		logger.GetLogger().Error("Failed to get tfl expected time for line %v (%v). Cause: %v. Aborting...",
			line, bound, err.Error())
		return
	}
	logger.GetLogger().Info("Tfl expected journey time for line %v (%v) is: %v", line, bound, tflExpectedTime)

	predictedTime, err := getPredictedJourneyTime2(line, bound, stopPoints)
	if err != nil {
		logger.GetLogger().Error("Failed to generate predicted time for line %v (%v). Cause: %v. Aborting",
			line, bound, err.Error())
		return
	}
	logger.GetLogger().Info("Predicted journey time for line %v (%v) is: %v", line, bound, predictedTime)

	actualTime, arrivalTime := getActualJourneyTime(line, bound, destination, vehicleId, departureTime)

	insertIntoPerformanceMeasureDB2(line, bound, vehicleId, origin, destination,
		tflExpectedTime, predictedTime, actualTime, departureTime, arrivalTime)
}
/* Attempts to interpolate until terminus, then back to its reported location (toStop) if bus is found to have
   changed direction near the terminus. */
func interpolateIfChangedDirection(line, bound, vehicleId, fromStop, toStop,
	oldTimeStamp, newTimeStamp, oldTflTimeStamp, newTflTimeStamp string,
	values, skippedStopsValues, rawArrivalTimes *[]string, cachedEntry map[string]string) {
	terminus := selectdb.SelectTerminus(line, bound)

	// Nothing to extrapolate if we already at terminus
	if fromStop == terminus {
		return
	}

	/* Check that we have sufficient data to complete interpolation.
	   Interpolation not possible if predictions up till terminus do not exist, or that they are
	   in the future.  In such case return.*/
	intermediateStopsToTerminus := selectdb.SelectIntermediateStops(line, bound, fromStop, terminus)

	for _, stop := range intermediateStopsToTerminus {
		timeStamp, timeStampPresentInCachedEntry := cachedEntry[stop]
		if !timeStampPresentInCachedEntry || !timeStampHasPassed(timeStamp) {
			return
		}
	}
	timeAtTerminus := cachedEntry[terminus]

	/* Specially handle case where terminus and fromStop are only 1 stop apart since interpolateStops
	   won't work  - it assumes stops are at least 2 apart */
	if len(intermediateStopsToTerminus) == 2 {
		*values = append(*values, fmt.Sprintf("('%v', '%v', '%v', '%v', TIMESTAMP '%v', TIMESTAMP '%v', "+
			"'%v', TIMESTAMP '%v', TIMESTAMP '%v', INTERVAL '%v seconds', FALSE)",
			line, bound, vehicleId,
			fromStop, toPsqlTimeStamp(oldTimeStamp), toPsqlTimeStamp(oldTflTimeStamp),
			terminus, toPsqlTimeStamp(timeAtTerminus), toPsqlTimeStamp(newTflTimeStamp),
			timeDifference(oldTimeStamp, timeAtTerminus)))
	} else {
		interpolateStops(line, bound, vehicleId, fromStop, terminus, oldTimeStamp, timeAtTerminus,
			oldTflTimeStamp, newTflTimeStamp, values, skippedStopsValues, rawArrivalTimes, cachedEntry)
	}

	/* Similar to above -- Further interpolation to current reported location if possible */
	reverseBound := getReverseBound(line, bound)

	// Check that to stop is actually on reverse bound, if not return
	_, err := selectdb.SelectStopSeq(line, reverseBound, toStop)
	if err != nil {
		return
	}

	// Continue with interpolation on reverse bound
	reverseOrigin := selectdb.SelectOrigin(line, reverseBound)
	if toStop == reverseOrigin {
		return
	}

	intermediateStopsReverseBound := selectdb.SelectIntermediateStops(line, reverseBound, reverseOrigin, toStop)
	for _, stop := range intermediateStopsReverseBound {
		timeStamp, timeStampPresentInCachedEntry := cachedEntry[stop]
		if !timeStampPresentInCachedEntry || !timeStampHasPassed(timeStamp) {
			return
		}
	}
	timeAtReverseOrigin := cachedEntry[reverseOrigin]

	if len(intermediateStopsReverseBound) == 2 {
		*values = append(*values, fmt.Sprintf("('%v', '%v', '%v', '%v', TIMESTAMP '%v', TIMESTAMP '%v', "+
			"'%v', TIMESTAMP '%v', TIMESTAMP '%v', INTERVAL '%v seconds', FALSE)",
			line, reverseBound, vehicleId,
			reverseOrigin, toPsqlTimeStamp(timeAtReverseOrigin), toPsqlTimeStamp(oldTflTimeStamp),
			toStop, toPsqlTimeStamp(newTimeStamp), toPsqlTimeStamp(newTflTimeStamp),
			timeDifference(timeAtReverseOrigin, newTimeStamp)))
	} else {
		interpolateStops(line, reverseBound, vehicleId, reverseOrigin, toStop, timeAtReverseOrigin, newTimeStamp,
			oldTflTimeStamp, newTflTimeStamp, values, skippedStopsValues, rawArrivalTimes, cachedEntry)
	}
}