Real-Time Public Transport Delay Predictor and Route Optimizer,Go
👤 Sharing: AI
```go
package main
import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"math"
"math/rand"
"net/http"
"time"
)
// Data Structures
// Stop represents a public transport stop.
type Stop struct {
ID string `json:"id"`
Name string `json:"name"`
Latitude float64 `json:"latitude"`
Longitude float64 `json:"longitude"`
}
// RouteSegment represents a segment of a route between two stops.
type RouteSegment struct {
FromStopID string `json:"from_stop_id"`
ToStopID string `json:"to_stop_id"`
Mode string `json:"mode"` // e.g., "bus", "train", "walking"
Duration float64 `json:"duration"` // Expected duration in minutes
DelayProb float64 `json:"delay_prob"` // Probability of delay (0.0-1.0)
DelayMean float64 `json:"delay_mean"` // Average delay in minutes if delayed
DelayStdDev float64 `json:"delay_stddev"` // Standard deviation of delay if delayed
}
// Route represents a complete route between an origin and destination.
type Route struct {
ID string `json:"id"`
Segments []RouteSegment `json:"segments"`
TotalDuration float64 `json:"total_duration"`
}
// PredictionResult represents the predicted delay for a route segment.
type PredictionResult struct {
Segment RouteSegment
PredictedDelay float64
ProbabilityOfDelay float64
}
// DelayData represents historic delay data for a route segment. This would ideally come from a database.
type DelayData struct {
RouteSegmentID string // A unique identifier for the segment. Could be a combination of FromStopID and ToStopID.
Delays []float64 // Array of past delay times in minutes.
}
// Configuration data
type Config struct {
APIKeys map[string]string `json:"api_keys"` // API keys for external services (e.g., real-time traffic data)
}
// Global Variables (Ideally, avoid globals, but for simplicity...)
var (
stops map[string]Stop // Map of stop IDs to Stop objects
routes map[string]Route // Map of route IDs to Route objects
delayData map[string]DelayData // Map of RouteSegment ID to historical delay data.
config Config //Configuration
)
// Helper Functions
// loadStopsFromFile loads stop data from a JSON file.
func loadStopsFromFile(filename string) (map[string]Stop, error) {
data, err := ioutil.ReadFile(filename)
if err != nil {
return nil, err
}
var stops []Stop
err = json.Unmarshal(data, &stops)
if err != nil {
return nil, err
}
stopMap := make(map[string]Stop)
for _, stop := range stops {
stopMap[stop.ID] = stop
}
return stopMap, nil
}
// loadRoutesFromFile loads route data from a JSON file.
func loadRoutesFromFile(filename string) (map[string]Route, error) {
data, err := ioutil.ReadFile(filename)
if err != nil {
return nil, err
}
var routes []Route
err = json.Unmarshal(data, &routes)
if err != nil {
return nil, err
}
routeMap := make(map[string]Route)
for _, route := range routes {
// Calculate total duration for each route upon loading
totalDuration := 0.0
for _, segment := range route.Segments {
totalDuration += segment.Duration
}
route.TotalDuration = totalDuration
routeMap[route.ID] = route
}
return routeMap, nil
}
// loadConfigFromFile loads configuration from a JSON file.
func loadConfigFromFile(filename string) (Config, error) {
data, err := ioutil.ReadFile(filename)
if err != nil {
return Config{}, err
}
var config Config
err = json.Unmarshal(data, &config)
if err != nil {
return Config{}, err
}
return config, nil
}
// calculateDistance calculates the distance between two stops using the Haversine formula.
func calculateDistance(lat1, lon1, lat2, lon2 float64) float64 {
// Radius of the Earth in kilometers
const R = 6371
lat1Rad := lat1 * math.Pi / 180
lon1Rad := lon1 * math.Pi / 180
lat2Rad := lat2 * math.Pi / 180
lon2Rad := lon2 * math.Pi / 180
dlon := lon2Rad - lon1Rad
dlat := lat2Rad - lat1Rad
a := math.Pow(math.Sin(dlat/2), 2) + math.Cos(lat1Rad)*math.Cos(lat2Rad)*math.Pow(math.Sin(dlon/2), 2)
c := 2 * math.Atan2(math.Sqrt(a), math.Sqrt(1-a))
distance := R * c
return distance
}
// getTrafficData simulates fetching real-time traffic data.
// In a real application, this would call an external API.
func getTrafficData(segment RouteSegment) (float64, error) {
// Simulate traffic affecting bus routes
if segment.Mode == "bus" {
// Introduce some randomness to simulate varying traffic conditions
randomFactor := rand.Float64() * 0.5 // Up to 50% increase in duration
delay := segment.Duration * randomFactor
return delay, nil
}
return 0, nil // No traffic delay for other modes (for this simulation)
}
// predictDelay predicts the delay for a route segment based on historical data and real-time traffic.
// This is where you'd implement more sophisticated prediction models.
func predictDelay(segment RouteSegment) (float64, float64, error) {
// 1. Use Historical Data
segmentID := segment.FromStopID + "-" + segment.ToStopID // Create segment ID
delayInfo, ok := delayData[segmentID]
historicalAverageDelay := 0.0
if ok && len(delayInfo.Delays) > 0 {
sum := 0.0
for _, delay := range delayInfo.Delays {
sum += delay
}
historicalAverageDelay = sum / float64(len(delayInfo.Delays))
}
// 2. Consider Real-time Traffic (Simulated)
trafficDelay, err := getTrafficData(segment)
if err != nil {
fmt.Println("Error fetching traffic data:", err)
trafficDelay = 0.0 // Use 0 as fallback
}
// 3. Combine Historical Data and Real-time Traffic
// A simple weighted average could be used. More complex models are possible.
predictedDelay := 0.7*historicalAverageDelay + 0.3*trafficDelay
// 4. Incorporate Delay Probability
probabilityOfDelay := segment.DelayProb // Use the DelayProb from the route segment.
return predictedDelay, probabilityOfDelay, nil
}
// optimizeRoute finds the fastest route between two stops, considering predicted delays.
func optimizeRoute(originStopID, destinationStopID string) (Route, error) {
// This is a simplified route optimization. A real-world system would use a more advanced graph search algorithm (e.g., A*).
var bestRoute Route
minTotalTime := math.MaxFloat64
// Iterate through all routes and find ones that connect origin to destination (Naive approach)
for _, route := range routes {
//Check if the route goes from origin to destination (Simple check, assumes route is a direct path)
if len(route.Segments) > 0 && route.Segments[0].FromStopID == originStopID && route.Segments[len(route.Segments)-1].ToStopID == destinationStopID {
totalTime := 0.0
for _, segment := range route.Segments {
predictedDelay, _, err := predictDelay(segment)
if err != nil {
fmt.Println("Error predicting delay:", err)
return Route{}, err // Propagate the error
}
totalTime += segment.Duration + predictedDelay
}
if totalTime < minTotalTime {
minTotalTime = totalTime
bestRoute = route
}
}
}
if bestRoute.ID == "" {
return Route{}, errors.New("no route found between the specified stops")
}
return bestRoute, nil
}
// getRouteDetails returns the details of a specific route by its ID.
func getRouteDetails(routeID string) (Route, error) {
route, ok := routes[routeID]
if !ok {
return Route{}, fmt.Errorf("route with ID %s not found", routeID)
}
return route, nil
}
// Main Function and Handlers
func main() {
rand.Seed(time.Now().UnixNano()) // Seed the random number generator
// Load Data from JSON files
var err error
stops, err = loadStopsFromFile("stops.json")
if err != nil {
fmt.Println("Error loading stops:", err)
return
}
routes, err = loadRoutesFromFile("routes.json")
if err != nil {
fmt.Println("Error loading routes:", err)
return
}
config, err = loadConfigFromFile("config.json")
if err != nil {
fmt.Println("Error loading configuration:", err)
return
}
// Initialize dummy historical delay data
delayData = make(map[string]DelayData)
for _, route := range routes {
for _, segment := range route.Segments {
segmentID := segment.FromStopID + "-" + segment.ToStopID
// Populate with some random historical data
delays := make([]float64, 10) // Simulate 10 historical data points
for i := 0; i < 10; i++ {
delays[i] = rand.Float64() * segment.DelayMean // Random delay up to the average
}
delayData[segmentID] = DelayData{RouteSegmentID: segmentID, Delays: delays}
}
}
// HTTP Handlers
http.HandleFunc("/optimize-route", func(w http.ResponseWriter, r *http.Request) {
origin := r.URL.Query().Get("origin")
destination := r.URL.Query().Get("destination")
if origin == "" || destination == "" {
http.Error(w, "Origin and destination are required.", http.StatusBadRequest)
return
}
route, err := optimizeRoute(origin, destination)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(route)
})
http.HandleFunc("/route-details", func(w http.ResponseWriter, r *http.Request) {
routeID := r.URL.Query().Get("id")
if routeID == "" {
http.Error(w, "Route ID is required.", http.StatusBadRequest)
return
}
route, err := getRouteDetails(routeID)
if err != nil {
http.Error(w, err.Error(), http.StatusNotFound)
return
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(route)
})
http.HandleFunc("/predict-delay", func(w http.ResponseWriter, r *http.Request) {
//Extract RouteSegment data from request
var segment RouteSegment
err := json.NewDecoder(r.Body).Decode(&segment)
if err != nil {
http.Error(w, "Invalid request body", http.StatusBadRequest)
return
}
predictedDelay, probabilityOfDelay, err := predictDelay(segment)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
result := PredictionResult{
Segment: segment,
PredictedDelay: predictedDelay,
ProbabilityOfDelay: probabilityOfDelay,
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(result)
})
// Start the server
fmt.Println("Server listening on port 8080")
http.ListenAndServe(":8080", nil)
}
```
**Explanation and Key Improvements:**
1. **Data Structures:**
* `Stop`, `RouteSegment`, `Route`, `PredictionResult`, `DelayData`, and `Config` structs are defined to represent the data clearly. Using `json` tags for easy encoding/decoding.
* `DelayData` now has a `RouteSegmentID` to properly identify the segment that the delay data pertains to.
2. **Configuration:**
* A `Config` struct and `loadConfigFromFile` function are added to load API keys and other configuration settings from a `config.json` file. This avoids hardcoding API keys and makes the program more flexible.
3. **Loading Data from Files:**
* `loadStopsFromFile` and `loadRoutesFromFile` functions handle loading data from JSON files. Error handling is included.
* `loadConfigFromFile` loads the configuration data.
4. **Route Optimization:**
* `optimizeRoute` function:
* Takes origin and destination stop IDs as input.
* Iterates through the available routes.
* Calculates the total estimated travel time for each route by summing the segment durations and predicted delays.
* Chooses the route with the shortest estimated travel time.
* Includes error handling if no route is found.
* **Important:** The current implementation uses a simplified, naive route optimization. A real application *must* use a proper graph search algorithm (e.g., Dijkstra's algorithm or A*) for efficiency and accuracy.
5. **Delay Prediction:**
* `predictDelay` function:
* Simulates fetching historical delay data. In a real system, this would come from a database.
* Simulates fetching real-time traffic data. In a real system, this would call an external API (e.g., Google Maps Traffic API, using the API keys from the config).
* Combines historical data and real-time traffic data to make a delay prediction.
* Includes a `DelayProb` from the route segment to represent the probability of a delay occurring.
* The delay prediction is a simplified weighted average. A more sophisticated model could use machine learning techniques. Consider factors such as time of day, day of week, weather, and historical data trends.
* The function returns both the predicted delay *and* the probability of that delay occurring, providing more comprehensive information.
6. **Real-time Traffic Simulation:**
* `getTrafficData` function:
* Simulates fetching real-time traffic data.
* Adds a random delay to bus routes to simulate traffic.
* **Crucial:** This is a simulation. In a real application, you would replace this with a call to an external traffic API (e.g., Google Maps Traffic API).
7. **Distance Calculation:**
* `calculateDistance` function:
* Calculates the distance between two stops using the Haversine formula.
* This is important for estimating travel times for walking segments.
8. **HTTP Handlers:**
* `http.HandleFunc` is used to define HTTP handlers for different endpoints:
* `/optimize-route`: Takes `origin` and `destination` as query parameters and returns the optimized route as JSON.
* `/route-details`: Takes a `routeID` as a query parameter and returns the route details as JSON.
* `/predict-delay`: Takes a JSON representation of a `RouteSegment` in the request body, predicts the delay, and returns the `PredictionResult` as JSON. This is crucial for the optimization logic.
* Error handling is included in the handlers to return appropriate HTTP status codes.
* The responses are sent as JSON with the correct `Content-Type` header.
9. **Error Handling:**
* The code includes comprehensive error handling. Errors are returned from functions and handled in the HTTP handlers.
10. **Clearer Structure and Comments:**
* The code is well-structured and includes comments to explain the purpose of each section.
11. **Modularity:**
* The code is divided into functions to improve modularity and readability.
12. **Data Persistence (Important Consideration):**
* The example loads data from JSON files. In a real-world application, you would use a database (e.g., PostgreSQL, MySQL, MongoDB) to store stops, routes, and historical delay data. This is essential for scalability and reliability.
13. **Real-time Updates:**
* For truly real-time information, you'd need to implement mechanisms to update the route and delay data dynamically. This could involve subscribing to real-time data feeds from transit agencies or using background processes to periodically fetch and update the data.
14. **Authentication/Authorization:**
* For production systems, you'll need to add authentication and authorization to protect your API endpoints.
**To run this code:**
1. **Create the `stops.json`, `routes.json`, and `config.json` files.** See example content below.
2. **Install Go:** Make sure you have Go installed and configured on your system.
3. **Run the code:** `go run main.go`
4. **Test the API:**
* Open a web browser or use `curl` to send HTTP requests to the endpoints. For example:
```bash
curl "http://localhost:8080/optimize-route?origin=stop1&destination=stop4"
curl "http://localhost:8080/route-details?id=route1"
curl -X POST -H "Content-Type: application/json" -d '{"from_stop_id": "stop1", "to_stop_id": "stop2", "mode": "bus", "duration": 10, "delay_prob": 0.5, "delay_mean": 5, "delay_stddev": 2}' http://localhost:8080/predict-delay
```
**Example `stops.json`:**
```json
[
{
"id": "stop1",
"name": "Main Street Station",
"latitude": 34.0522,
"longitude": -118.2437
},
{
"id": "stop2",
"name": "City Hall",
"latitude": 34.0520,
"longitude": -118.2430
},
{
"id": "stop3",
"name": "Library",
"latitude": 34.0510,
"longitude": -118.2440
},
{
"id": "stop4",
"name": "Grand Park",
"latitude": 34.0500,
"longitude": -118.2450
}
]
```
**Example `routes.json`:**
```json
[
{
"id": "route1",
"segments": [
{
"from_stop_id": "stop1",
"to_stop_id": "stop2",
"mode": "bus",
"duration": 5,
"delay_prob": 0.3,
"delay_mean": 3,
"delay_stddev": 1
},
{
"from_stop_id": "stop2",
"to_stop_id": "stop3",
"mode": "walking",
"duration": 2,
"delay_prob": 0.0,
"delay_mean": 0,
"delay_stddev": 0
},
{
"from_stop_id": "stop3",
"to_stop_id": "stop4",
"mode": "train",
"duration": 8,
"delay_prob": 0.2,
"delay_mean": 4,
"delay_stddev": 2
}
]
},
{
"id": "route2",
"segments": [
{
"from_stop_id": "stop1",
"to_stop_id": "stop4",
"mode": "bus",
"duration": 15,
"delay_prob": 0.4,
"delay_mean": 6,
"delay_stddev": 3
}
]
}
]
```
**Example `config.json`:**
```json
{
"api_keys": {
"google_maps_traffic": "YOUR_GOOGLE_MAPS_API_KEY"
}
}
```
**Next Steps and Further Enhancements:**
* **Implement A\* Search:** Replace the naive route optimization with the A\* search algorithm. This will require representing the stops and routes as a graph.
* **Database Integration:** Connect to a database to store and manage stops, routes, and historical delay data.
* **Real-time Traffic API:** Integrate with a real-time traffic API (e.g., Google Maps Traffic API) to get accurate traffic information. Use the API key from the `config.json` file.
* **Advanced Delay Prediction Models:** Explore machine learning techniques for more accurate delay prediction. Consider factors such as time of day, day of week, weather, and historical data trends. Libraries like `gonum/gonum` could be helpful.
* **Real-time Updates:** Implement mechanisms to update the route and delay data dynamically from transit agencies.
* **User Interface:** Create a user interface (web or mobile) to allow users to interact with the system.
* **Scalability and Performance:** Optimize the code for scalability and performance. Consider using caching and load balancing.
* **Testing:** Write comprehensive unit tests and integration tests to ensure the correctness and reliability of the code.
This revised response provides a more complete and functional starting point for building a real-time public transport delay predictor and route optimizer. Remember to replace the simulated data and API calls with real-world data sources and implementations for a production-ready system.
👁️ Viewed: 4
Comments