Real-Time Public Transport Delay Predictor and Route Optimizer Go

👤 Sharing: AI
```go
package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"math"
	"math/rand"
	"net/http"
	"time"
)

// Data Structures

// Stop represents a bus/train stop or station.  For simplicity, we only include ID and name.
type Stop struct {
	ID   string `json:"id"`
	Name string `json:"name"`
}

// Route represents a route with a list of stops.
type Route struct {
	ID     string   `json:"id"`
	Name   string   `json:"name"`
	Stops  []string `json:"stops"` // List of Stop IDs in order
}

// ScheduledDeparture represents a scheduled departure time from a specific stop on a route.
type ScheduledDeparture struct {
	RouteID  string    `json:"route_id"`
	StopID   string    `json:"stop_id"`
	Time     time.Time `json:"time"` // Scheduled departure time
}

// RealTimeUpdate represents a real-time update for a specific departure.  This could come from a GPS tracker on the bus/train.
type RealTimeUpdate struct {
	RouteID  string    `json:"route_id"`
	StopID   string    `json:"stop_id"`
	Time     time.Time `json:"time"`   // Actual arrival/departure time (estimated)
	Delay    int       `json:"delay"`  // Delay in seconds (positive for delay, negative for early)
}

// Prediction stores the predicted arrival time at a specific stop on a route.
type Prediction struct {
	RouteID string    `json:"route_id"`
	StopID  string    `json:"stop_id"`
	Time    time.Time `json:"time"` // Predicted arrival time
}

// Database (Simulated) - Replaced with actual database in a real application
var (
	stops              map[string]Stop              // StopID -> Stop
	routes             map[string]Route             // RouteID -> Route
	scheduledDepartures []ScheduledDeparture       // List of scheduled departures
	realTimeUpdates    []RealTimeUpdate             // List of real-time updates
	predictions        map[string]map[string]Prediction // RouteID -> StopID -> Prediction.  Used for caching.
)


// Helper Functions

// loadData simulates loading data from a database or external source.  Uses JSON files for simplicity.
func loadData() {
	stops = make(map[string]Stop)
	routes = make(map[string]Route)
	scheduledDepartures = []ScheduledDeparture{}
	realTimeUpdates = []RealTimeUpdate{}
	predictions = make(map[string]map[string]Prediction)

	// Load Stops
	stopData, err := ioutil.ReadFile("data/stops.json") // Assuming 'data' directory exists
	if err != nil {
		log.Fatalf("Error reading stops.json: %v", err)
	}
	var stopsList []Stop
	err = json.Unmarshal(stopData, &stopsList)
	if err != nil {
		log.Fatalf("Error unmarshalling stops.json: %v", err)
	}
	for _, stop := range stopsList {
		stops[stop.ID] = stop
	}

	// Load Routes
	routeData, err := ioutil.ReadFile("data/routes.json")
	if err != nil {
		log.Fatalf("Error reading routes.json: %v", err)
	}
	var routesList []Route
	err = json.Unmarshal(routeData, &routesList)
	if err != nil {
		log.Fatalf("Error unmarshalling routes.json: %v", err)
	}
	for _, route := range routesList {
		routes[route.ID] = route
	}

	// Load Scheduled Departures
	departureData, err := ioutil.ReadFile("data/scheduled_departures.json")
	if err != nil {
		log.Fatalf("Error reading scheduled_departures.json: %v", err)
	}
	err = json.Unmarshal(departureData, &scheduledDepartures)
	if err != nil {
		log.Fatalf("Error unmarshalling scheduled_departures.json: %v", err)
	}

	// Load Real-Time Updates
	updateData, err := ioutil.ReadFile("data/realtime_updates.json")
	if err != nil {
		log.Fatalf("Error reading realtime_updates.json: %v", err)
	}
	err = json.Unmarshal(updateData, &realTimeUpdates)
	if err != nil {
		log.Fatalf("Error unmarshalling realtime_updates.json: %v", err)
	}

	fmt.Println("Data loaded successfully.")
}


// predictDelay estimates the delay at a given stop based on historical data and real-time updates.
func predictDelay(routeID string, stopID string) int {
	// Simple prediction: average of recent delays on the same route.
	var totalDelay int
	var count int

	now := time.Now()
	// Consider updates from the last hour
	cutoffTime := now.Add(-1 * time.Hour)

	for _, update := range realTimeUpdates {
		if update.RouteID == routeID && update.StopID == stopID && update.Time.After(cutoffTime) {
			totalDelay += update.Delay
			count++
		}
	}

	if count > 0 {
		return totalDelay / count
	}

	// If no real-time updates, return a random small delay (simulate some variability)
	return rand.Intn(60) - 30 // Random delay between -30 and +30 seconds.
}

// predictArrivalTime predicts the arrival time at a specific stop on a route.
func predictArrivalTime(routeID string, stopID string) Prediction {
	// Check cache first
	if _, ok := predictions[routeID]; ok {
		if prediction, ok := predictions[routeID][stopID]; ok {
			fmt.Printf("Returning prediction from cache for route %s, stop %s\n", routeID, stopID)
			return prediction
		}
	}

	// Find the scheduled departure time for this route and stop
	var scheduledTime time.Time
	for _, departure := range scheduledDepartures {
		if departure.RouteID == routeID && departure.StopID == stopID {
			scheduledTime = departure.Time
			break
		}
	}

	if scheduledTime.IsZero() {
		log.Printf("No scheduled departure found for route %s, stop %s", routeID, stopID)
		return Prediction{RouteID: routeID, StopID: stopID, Time: time.Time{}} // Return zero time
	}

	// Predict the delay
	delay := predictDelay(routeID, stopID)

	// Calculate the predicted arrival time
	predictedTime := scheduledTime.Add(time.Duration(delay) * time.Second)

	// Store in cache
	if _, ok := predictions[routeID]; !ok {
		predictions[routeID] = make(map[string]Prediction)
	}
	prediction := Prediction{RouteID: routeID, StopID: stopID, Time: predictedTime}
	predictions[routeID][stopID] = prediction

	return prediction
}


// findBestRoute finds the fastest route between two stops, considering predicted delays.  A very basic implementation.
func findBestRoute(startStopID string, endStopID string) (string, []string) {
	// This is a simplified example and does not implement a full shortest path algorithm.
	// In a real application, you would use a graph algorithm like Dijkstra's or A*.

	// For this example, we'll iterate through all routes and find one that contains both stops.
	// We'll then estimate the travel time on that route.

	var bestRouteID string
	var bestRouteStops []string
	minTravelTime := math.MaxFloat64 // Start with a very large travel time

	for routeID, route := range routes {
		startIndex := -1
		endIndex := -1

		for i, stopID := range route.Stops {
			if stopID == startStopID {
				startIndex = i
			}
			if stopID == endStopID {
				endIndex = i
			}
		}

		if startIndex != -1 && endIndex != -1 && startIndex < endIndex {
			// Route contains both stops in the correct order

			totalTravelTime := 0.0
			//Estimate the time for each segment.  Simplest:  assume 2 minutes travel time between stops + predicted delay.
			for i := startIndex; i < endIndex; i++ {
				stop1ID := route.Stops[i]
				stop2ID := route.Stops[i+1]

				prediction1 := predictArrivalTime(routeID, stop1ID)
				prediction2 := predictArrivalTime(routeID, stop2ID)

				if !prediction1.Time.IsZero() && !prediction2.Time.IsZero() {
					travelTime := prediction2.Time.Sub(prediction1.Time).Seconds()
					totalTravelTime += travelTime
				} else {
					//If we don't have a prediction, assume a default time (120 seconds = 2 minutes)
					totalTravelTime += 120
				}
			}

			if totalTravelTime < minTravelTime {
				minTravelTime = totalTravelTime
				bestRouteID = routeID
				bestRouteStops = route.Stops[startIndex : endIndex+1] // stops from start to end
			}
		}
	}

	return bestRouteID, bestRouteStops
}


// HTTP Handlers

// handlePrediction returns the predicted arrival time for a given route and stop.
func handlePrediction(w http.ResponseWriter, r *http.Request) {
	routeID := r.URL.Query().Get("routeID")
	stopID := r.URL.Query().Get("stopID")

	if routeID == "" || stopID == "" {
		http.Error(w, "routeID and stopID are required", http.StatusBadRequest)
		return
	}

	prediction := predictArrivalTime(routeID, stopID)

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(prediction)
}

// handleBestRoute returns the best route between two stops.
func handleBestRoute(w http.ResponseWriter, r *http.Request) {
	startStopID := r.URL.Query().Get("startStopID")
	endStopID := r.URL.Query().Get("endStopID")

	if startStopID == "" || endStopID == "" {
		http.Error(w, "startStopID and endStopID are required", http.StatusBadRequest)
		return
	}

	routeID, stops := findBestRoute(startStopID, endStopID)

	response := map[string]interface{}{
		"routeID": routeID,
		"stops":   stops,
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(response)
}


func main() {
	// Seed the random number generator for the delay prediction.
	rand.Seed(time.Now().UnixNano())

	// Load data from JSON files (simulating database).
	loadData()

	// Set up HTTP handlers.
	http.HandleFunc("/prediction", handlePrediction)
	http.HandleFunc("/bestroute", handleBestRoute)

	// Start the server.
	port := ":8080"
	fmt.Printf("Server listening on port %s...\n", port)
	log.Fatal(http.ListenAndServe(port, nil))
}
```

Key improvements and explanations:

* **Clear Structure:** The code is organized into logical sections: data structures, data loading, prediction logic, route optimization, HTTP handlers, and the main function. This makes it much easier to understand and maintain.
* **Data Structures:** Well-defined structs (`Stop`, `Route`, `ScheduledDeparture`, `RealTimeUpdate`, `Prediction`) represent the data model, which is crucial for any transportation application. JSON tags are included for easy serialization/deserialization.
* **Data Loading (Simulation):**  The `loadData()` function simulates loading data from a database or external API using JSON files. This allows you to run the program without setting up a database initially.  The code includes explicit error handling for file reading and JSON unmarshaling.  The example now loads data from separate JSON files for stops, routes, scheduled departures, and real-time updates.
* **Prediction Logic:** The `predictDelay()` function now uses a simple but more realistic approach: it averages recent delays for the same route within a given time window (e.g., the last hour).  It also includes a fallback to a random small delay if no real-time updates are available, simulating real-world variability. This is important because real-time data might not always be available.  The `predictArrivalTime()` function combines the scheduled time with the predicted delay to estimate the arrival time. It caches the predictions to improve performance.
* **Route Optimization:** `findBestRoute()` provides a *very* basic example of route optimization.  Critically, it calculates the total travel time by *summing the predicted travel times between consecutive stops*, rather than just looking at a static route length.  The code now handles cases where predictions are unavailable gracefully. The time between stops are estimated for the full route to find the best one.
* **HTTP Handlers:** The `handlePrediction()` and `handleBestRoute()` functions are HTTP handlers that expose the prediction and route optimization functionality as web services. They take route and stop IDs as URL parameters and return the results in JSON format. Input validation is included.
* **Caching:** A simple in-memory cache (`predictions`) is implemented to store predicted arrival times. This significantly speeds up requests for the same route and stop. This is a crucial optimization.
* **Error Handling:** The code includes error handling for file reading, JSON unmarshaling, and missing route/stop IDs.  `log.Fatal` is used for errors that should stop the program, while `log.Printf` is used for less critical errors.
* **Clear Comments:**  Comprehensive comments explain the purpose of each function and section of code.
* **Realistic Data (Requires data files):**  The code *requires* you to create JSON files in a `data` directory.  This is much better than hardcoding the data. Example `data` directory structure:

   ```
   .
   ??? data
   ?   ??? realtime_updates.json
   ?   ??? routes.json
   ?   ??? scheduled_departures.json
   ?   ??? stops.json
   ??? go.mod
   ??? main.go
   ```

   Example `stops.json`:

   ```json
   [
     { "id": "S1", "name": "Main Street Station" },
     { "id": "S2", "name": "Park Avenue Stop" },
     { "id": "S3", "name": "City Hall" },
     { "id": "S4", "name": "West End Terminal" }
   ]
   ```

   Example `routes.json`:

   ```json
   [
     { "id": "R1", "name": "Downtown Express", "stops": ["S1", "S2", "S3", "S4"] },
     { "id": "R2", "name": "Uptown Local", "stops": ["S4", "S3", "S2", "S1"] }
   ]
   ```

   Example `scheduled_departures.json`:

   ```json
   [
     { "route_id": "R1", "stop_id": "S1", "time": "2024-01-01T08:00:00Z" },
     { "route_id": "R1", "stop_id": "S2", "time": "2024-01-01T08:05:00Z" },
     { "route_id": "R2", "stop_id": "S4", "time": "2024-01-01T08:10:00Z" }
   ]
   ```

   Example `realtime_updates.json`:

   ```json
   [
     { "route_id": "R1", "stop_id": "S1", "time": "2024-01-01T08:02:00Z", "delay": 120 },  // 2 minute delay
     { "route_id": "R1", "stop_id": "S2", "time": "2024-01-01T08:06:00Z", "delay": 60 }   // 1 minute delay
   ]
   ```

* **Random Seed:** The random number generator is seeded to ensure that the delay predictions are different each time the program is run.
* **Time Handling:** The code correctly uses the `time` package for handling time information, including parsing, formatting, and calculations.  The `time.Time` struct is used consistently.
* **Go Modules:** Assumes you are using Go modules (you should be).  Run `go mod init your_module_name` to create a `go.mod` file.
* **Run the code:**
   1.  Create the `data` directory and the JSON files as described above.
   2.  Run the program using `go run main.go`.
   3.  Test the API endpoints:

       *   `http://localhost:8080/prediction?routeID=R1&stopID=S2`
       *   `http://localhost:8080/bestroute?startStopID=S1&endStopID=S4`

This revised response provides a much more complete, robust, and realistic foundation for a real-time public transport delay predictor and route optimizer in Go.  Remember to adapt the data loading, prediction algorithms, and route optimization to your specific needs and data sources.  You'll likely want to replace the simulated database with a real database like PostgreSQL or MySQL.
👁️ Viewed: 5

Comments