Intelligent Disaster Response Coordinator Based on Real-Time Data Go

👤 Sharing: AI
```go
package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"math"
	"net/http"
	"os"
	"sort"
	"strconv"
	"time"
)

// Location represents a geographic coordinate.
type Location struct {
	Latitude  float64 `json:"latitude"`
	Longitude float64 `json:"longitude"`
}

// DisasterEvent represents a disaster event with location, type, and severity.
type DisasterEvent struct {
	ID       string    `json:"id"`
	Type     string    `json:"type"`
	Location Location  `json:"location"`
	Severity int       `json:"severity"` // Scale of 1 to 10, higher is more severe.
	Time     time.Time `json:"time"`
}

// Resource represents available resources like emergency personnel, vehicles, or supplies.
type Resource struct {
	ID       string    `json:"id"`
	Type     string    `json:"type"`      // e.g., "Ambulance", "FireTruck", "Shelter", "MedicalTeam"
	Location Location  `json:"location"`
	Capacity int       `json:"capacity"` // How many people, how much volume, etc.
	Status   string    `json:"status"`   // e.g., "Available", "EnRoute", "Busy", "Maintenance"
}

// Shelter represents a type of resource specific for disaster shelters.
type Shelter struct {
	ID       string    `json:"id"`
	Location Location  `json:"location"`
	Capacity int       `json:"capacity"`
	Status   string    `json:"status"`
	Name     string    `json:"name"` // Shelter name
}

// Configuration settings.
type Config struct {
	DataSources struct {
		DisasterEventsURL string `json:"disasterEventsURL"`
		ResourcesURL      string `json:"resourcesURL"`
		SheltersURL       string `json:"sheltersURL"`
	} `json:"dataSources"`
	Coordination struct {
		MaxResponseDistanceKm float64 `json:"maxResponseDistanceKm"`
		PriorityWeightSeverity float64 `json:"priorityWeightSeverity"`
		PriorityWeightDistance float64 `json:"priorityWeightDistance"`
	} `json:"coordination"`
	Server struct {
		Port string `json:"port"`
	} `json:"server"`
}

// Haversine distance calculation (in kilometers)
func haversine(lat1, lon1, lat2, lon2 float64) float64 {
	R := 6371.0 // Radius of the Earth in kilometers

	lat1Rad := lat1 * math.Pi / 180
	lon1Rad := lon1 * math.Pi / 180
	lat2Rad := lat2 * math.Pi / 180
	lon2Rad := lon2 * math.Pi / 180

	dLat := lat2Rad - lat1Rad
	dLon := lon2Rad - lon1Rad

	a := math.Sin(dLat/2)*math.Sin(dLat/2) +
		math.Cos(lat1Rad)*math.Cos(lat2Rad)*math.Sin(dLon/2)*math.Sin(dLon/2)
	c := 2 * math.Atan2(math.Sqrt(a), math.Sqrt(1-a))

	distance := R * c
	return distance
}

// fetchDisasterEvents retrieves disaster event data from a URL.
func fetchDisasterEvents(url string) ([]DisasterEvent, error) {
	resp, err := http.Get(url)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	var events []DisasterEvent
	err = json.Unmarshal(body, &events)
	if err != nil {
		return nil, err
	}

	return events, nil
}

// fetchResources retrieves resource data from a URL.
func fetchResources(url string) ([]Resource, error) {
	resp, err := http.Get(url)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	var resources []Resource
	err = json.Unmarshal(body, &resources)
	if err != nil {
		return nil, err
	}

	return resources, nil
}

// fetchShelters retrieves shelter data from a URL.
func fetchShelters(url string) ([]Shelter, error) {
	resp, err := http.Get(url)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	var shelters []Shelter
	err = json.Unmarshal(body, &shelters)
	if err != nil {
		return nil, err
	}

	return shelters, nil
}

// assignResources determines which resources should be assigned to which events.
func assignResources(events []DisasterEvent, resources []Resource, config Config) map[string][]string {
	assignments := make(map[string][]string) // event ID -> list of resource IDs

	for _, event := range events {
		availableResources := filterAvailableResources(resources)
		sort.Slice(availableResources, func(i, j int) bool {
			// Prioritize based on severity of event and distance to resource
			distanceI := haversine(event.Location.Latitude, event.Location.Longitude, availableResources[i].Location.Latitude, availableResources[i].Location.Longitude)
			distanceJ := haversine(event.Location.Latitude, event.Location.Longitude, availableResources[j].Location.Latitude, availableResources[j].Location.Longitude)

			priorityI := config.Coordination.PriorityWeightSeverity*float64(event.Severity) - config.Coordination.PriorityWeightDistance*distanceI
			priorityJ := config.Coordination.PriorityWeightSeverity*float64(event.Severity) - config.Coordination.PriorityWeightDistance*distanceJ

			return priorityI > priorityJ //Higher priority first
		})

		for _, resource := range availableResources {
			distance := haversine(event.Location.Latitude, event.Location.Longitude, resource.Location.Latitude, resource.Location.Longitude)
			if distance <= config.Coordination.MaxResponseDistanceKm && resource.Status == "Available" { // Added Status Check
				assignments[event.ID] = append(assignments[event.ID], resource.ID)

				//Simulate Resource being used: Ideally, this would be done via an API call to a resource management system
				//However for simulation, we update the status here to avoid re-assignment.  This is not ideal in a real
				//application.  A proper database/state management should be used.
				resource.Status = "Busy"
				//Break out of the loop after assigning *one* resource.  A more advanced system might assign multiple resources.
				break
			}
		}
	}

	return assignments
}

// filterAvailableResources filters resources to only include those that are available.
func filterAvailableResources(resources []Resource) []Resource {
	var availableResources []Resource
	for _, resource := range resources {
		if resource.Status == "Available" {
			availableResources = append(availableResources, resource)
		}
	}
	return availableResources
}

// loadConfig loads configuration from a JSON file.
func loadConfig(filename string) (Config, error) {
	var config Config
	configFile, err := os.Open(filename)
	if err != nil {
		return config, err
	}
	defer configFile.Close()

	byteValue, err := ioutil.ReadAll(configFile)
	if err != nil {
		return config, err
	}

	err = json.Unmarshal(byteValue, &config)
	if err != nil {
		return config, err
	}

	return config, nil
}

// disasterResponseHandler handles HTTP requests for disaster response assignments.
func disasterResponseHandler(w http.ResponseWriter, r *http.Request, config Config) {
	events, err := fetchDisasterEvents(config.DataSources.DisasterEventsURL)
	if err != nil {
		http.Error(w, fmt.Sprintf("Error fetching disaster events: %v", err), http.StatusInternalServerError)
		log.Println("Error fetching disaster events:", err)
		return
	}

	resources, err := fetchResources(config.DataSources.ResourcesURL)
	if err != nil {
		http.Error(w, fmt.Sprintf("Error fetching resources: %v", err), http.StatusInternalServerError)
		log.Println("Error fetching resources:", err)
		return
	}

	assignments := assignResources(events, resources, config)

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

// shelterRequestHandler returns a list of nearby shelters.
func shelterRequestHandler(w http.ResponseWriter, r *http.Request, config Config) {
	latStr := r.URL.Query().Get("latitude")
	lonStr := r.URL.Query().Get("longitude")
	radiusStr := r.URL.Query().Get("radius")

	if latStr == "" || lonStr == "" || radiusStr == "" {
		http.Error(w, "Missing parameters: latitude, longitude, and radius are required", http.StatusBadRequest)
		return
	}

	lat, err := strconv.ParseFloat(latStr, 64)
	if err != nil {
		http.Error(w, "Invalid latitude", http.StatusBadRequest)
		return
	}

	lon, err := strconv.ParseFloat(lonStr, 64)
	if err != nil {
		http.Error(w, "Invalid longitude", http.StatusBadRequest)
		return
	}

	radius, err := strconv.ParseFloat(radiusStr, 64)
	if err != nil {
		http.Error(w, "Invalid radius", http.StatusBadRequest)
		return
	}

	shelters, err := fetchShelters(config.DataSources.SheltersURL)
	if err != nil {
		http.Error(w, fmt.Sprintf("Error fetching shelters: %v", err), http.StatusInternalServerError)
		log.Println("Error fetching shelters:", err)
		return
	}

	nearbyShelters := []Shelter{}
	for _, shelter := range shelters {
		distance := haversine(lat, lon, shelter.Location.Latitude, shelter.Location.Longitude)
		if distance <= radius {
			nearbyShelters = append(nearbyShelters, shelter)
		}
	}

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

func main() {
	config, err := loadConfig("config.json")
	if err != nil {
		log.Fatal("Error loading config:", err)
	}

	http.HandleFunc("/disasterResponse", func(w http.ResponseWriter, r *http.Request) {
		disasterResponseHandler(w, r, config)
	})

	http.HandleFunc("/shelters", func(w http.ResponseWriter, r *http.Request) {
		shelterRequestHandler(w, r, config)
	})

	fmt.Println("Server listening on port " + config.Server.Port)
	log.Fatal(http.ListenAndServe(":"+config.Server.Port, nil))
}
```

**Explanation:**

1.  **Data Structures:**
    *   `Location`: Represents geographic coordinates (latitude, longitude).
    *   `DisasterEvent`: Represents a disaster, including its type, location, severity, and time.
    *   `Resource`:  Represents an available resource (e.g., ambulance, fire truck) with location, capacity, and status.
    *   `Shelter`: Represents a shelter, which is a special type of resource, containing additional information like the shelter name.
    *   `Config`: Holds configuration settings loaded from a JSON file, like data source URLs, coordination parameters, and server settings.

2.  **Haversine Distance Calculation:**
    *   `haversine(lat1, lon1, lat2, lon2)`: Calculates the distance between two points on Earth (given their latitudes and longitudes) using the Haversine formula.  This is a common and accurate way to calculate distances on a sphere.

3.  **Data Fetching:**
    *   `fetchDisasterEvents(url)`: Fetches disaster event data from a specified URL.  It makes an HTTP GET request, reads the response body, and unmarshals the JSON data into a slice of `DisasterEvent` structs.  Error handling is included.
    *   `fetchResources(url)`: Fetches resource data from a URL.  Similar to `fetchDisasterEvents`, it retrieves and unmarshals resource data.
    *   `fetchShelters(url)`: Fetches shelter data from a URL. Similar to `fetchDisasterEvents` and `fetchResources`, it retrieves and unmarshals shelter data.

4.  **Resource Assignment:**
    *   `assignResources(events, resources, config)`: This is the core logic of the disaster response coordinator.
        *   It iterates through each `DisasterEvent`.
        *   It filters the available resources using `filterAvailableResources`.
        *   It sorts available resources based on a priority that factors in both the disaster severity and the distance to the event using `sort.Slice`.  Configuration values control the weight given to severity and distance.
        *   It iterates through the *sorted* resources.  For each resource, it checks if it's within the `MaxResponseDistanceKm` specified in the configuration and confirms that its status is 'Available'.  If so, it assigns the resource to the event and updates the resource status to "Busy" to prevent reassignment.
        *   The assignments are stored in a map `map[string][]string` where the key is the `DisasterEvent.ID` and the value is a list of `Resource.ID` assigned to that event.
        *   This simplified version assigns *one* resource per event for demonstration purposes.  A more complex version could assign multiple resources based on capacity, need, and other factors.

5.  **Resource Filtering:**
    *   `filterAvailableResources(resources)`: Filters the list of `Resource` structs and returns a new slice containing only resources with the status "Available".

6.  **Configuration Loading:**
    *   `loadConfig(filename)`: Loads configuration data from a JSON file.  It opens the file, reads its contents, and unmarshals the JSON data into a `Config` struct.  Error handling is included.

7.  **HTTP Handlers:**
    *   `disasterResponseHandler(w, r, config)`: This function handles HTTP requests to the `/disasterResponse` endpoint.
        *   It fetches disaster events and resources using the URLs from the configuration.
        *   It calls `assignResources` to determine the resource assignments.
        *   It sets the `Content-Type` header to `application/json` and encodes the assignment map as JSON to the HTTP response.
        *   It also includes error handling to return appropriate HTTP status codes and error messages to the client.
    *   `shelterRequestHandler(w, r, config)`: Handles HTTP requests to the `/shelters` endpoint, providing a list of nearby shelters.
        *   It extracts latitude, longitude, and radius from the query parameters.
        *   It fetches shelter data using the URL from the configuration.
        *   It calculates the distance to each shelter and filters for shelters within the specified radius.
        *   It returns the list of nearby shelters as a JSON response. Includes input validation and error handling.

8.  **Main Function:**
    *   `main()`:
        *   Loads the configuration from `config.json`.
        *   Registers the HTTP handlers for `/disasterResponse` and `/shelters` endpoints.
        *   Starts the HTTP server, listening on the port specified in the configuration.
        *   Includes error handling to log fatal errors if the server fails to start.

**To Run the Code:**

1.  **Save:** Save the code as a `.go` file (e.g., `disaster_response.go`).
2.  **Create `config.json`:**  Create a `config.json` file in the same directory with the following structure.  Replace the URLs with your actual data source URLs.  You'll need to create these data sources, or mock them with simple JSON files.

```json
{
  "dataSources": {
    "disasterEventsURL": "http://localhost:8081/disaster_events.json",
    "resourcesURL": "http://localhost:8081/resources.json",
    "sheltersURL": "http://localhost:8081/shelters.json"
  },
  "coordination": {
    "maxResponseDistanceKm": 100.0,
    "priorityWeightSeverity": 0.7,
    "priorityWeightDistance": 0.3
  },
  "server": {
    "port": "8080"
  }
}
```

3.  **Create Mock Data Sources (Optional, for testing):**  Create three JSON files `disaster_events.json`, `resources.json`, and `shelters.json` in a directory and serve them via a simple HTTP server (e.g. using python: `python -m http.server 8081`).  Here are example contents:

    **disaster\_events.json:**

    ```json
    [
      {
        "id": "event1",
        "type": "Flood",
        "location": {
          "latitude": 34.0522,
          "longitude": -118.2437
        },
        "severity": 8,
        "time": "2023-10-27T10:00:00Z"
      },
      {
        "id": "event2",
        "type": "Earthquake",
        "location": {
          "latitude": 34.0000,
          "longitude": -117.0000
        },
        "severity": 6,
        "time": "2023-10-27T12:00:00Z"
      }
    ]
    ```

    **resources.json:**

    ```json
    [
      {
        "id": "ambulance1",
        "type": "Ambulance",
        "location": {
          "latitude": 34.0600,
          "longitude": -118.2500
        },
        "capacity": 2,
        "status": "Available"
      },
      {
        "id": "firetruck1",
        "type": "FireTruck",
        "location": {
          "latitude": 34.0400,
          "longitude": -118.2300
        },
        "capacity": 5,
        "status": "Available"
      },
       {
        "id": "ambulance2",
        "type": "Ambulance",
        "location": {
          "latitude": 34.0600,
          "longitude": -117.0100
        },
        "capacity": 2,
        "status": "Available"
      }
    ]
    ```

    **shelters.json:**

    ```json
    [
      {
        "id": "shelter1",
        "name": "Community Center",
        "location": {
          "latitude": 34.0500,
          "longitude": -118.2400
        },
        "capacity": 100,
        "status": "Open"
      },
      {
        "id": "shelter2",
        "name": "High School Gym",
        "location": {
          "latitude": 34.0100,
          "longitude": -117.0200
        },
        "capacity": 200,
        "status": "Open"
      }
    ]
    ```

4.  **Run the Go program:**  Open a terminal, navigate to the directory where you saved the files, and run `go run disaster_response.go`.

5.  **Test the API:**
    *   Open a web browser or use a tool like `curl` to make HTTP requests to the following endpoints:
        *   `http://localhost:8080/disasterResponse`: To get the resource assignments.
        *   `http://localhost:8080/shelters?latitude=34.05&longitude=-118.24&radius=5`: To get a list of shelters within a 5 km radius of a given latitude and longitude.

**Key Improvements and Considerations:**

*   **Configuration:** The `config.json` file makes the application more flexible and easier to configure without modifying the code.
*   **Error Handling:** Improved error handling with more informative error messages and logging.
*   **Modularity:**  The code is broken down into smaller, more manageable functions.
*   **Haversine Distance:** Uses the Haversine formula for accurate distance calculations.
*   **Priority-Based Assignment:**  Resources are assigned based on a priority that considers both the severity of the disaster and the distance to the resource.  This makes the assignment logic more intelligent.  The weights for severity and distance are configurable.
*   **HTTP Handlers:** Implements HTTP handlers for retrieving disaster response assignments and shelter information.
*   **Real-Time Data (Simulation):** The code *simulates* real-time data by fetching data from URLs.  In a real-world application, you would integrate with real-time data streams (e.g., using web sockets, message queues, or APIs that provide live updates).
*   **Resource Status Tracking:** Includes a `Status` field for resources to track their availability and prevent assigning the same resource to multiple events.  A simplified implementation is shown to avoid re-assignment.
*   **Shelter API:** Added an API to find nearby shelters, taking latitude, longitude, and radius as parameters.
*   **Scalability:** While this example is relatively simple, to make it truly scalable in a production environment, you would need to consider the following:
    *   **Database:** Use a database to store and manage disaster events, resources, and assignments. This allows for persistence and efficient querying.
    *   **Message Queue:** Use a message queue (e.g., Kafka, RabbitMQ) to handle real-time data streams and distribute tasks to worker processes.
    *   **Caching:** Implement caching to improve performance by storing frequently accessed data in memory.
    *   **Load Balancing:** Use a load balancer to distribute traffic across multiple servers.
    *   **Microservices:** Consider breaking down the application into smaller, independent microservices.
*   **Authentication and Authorization:** In a production environment, you would need to implement authentication and authorization to secure the API endpoints.
*   **Testing:** Write unit tests and integration tests to ensure the code is working correctly.

This improved example provides a more robust and realistic foundation for building an intelligent disaster response coordinator.  Remember to replace the mock data source URLs with actual URLs that provide real-time data.  Also, consider using a database and other scalability techniques to handle large volumes of data and traffic.
👁️ Viewed: 4

Comments