Spaces:
Sleeping
Sleeping
| import math | |
| from geopy.geocoders import Nominatim | |
| class GreenRouteOptimizer: | |
| """ | |
| Calculates and compares green routes for shipments, providing data on emissions, | |
| travel time, and costs, formatted for the GreenPath Streamlit app. | |
| """ | |
| def __init__(self, user_agent="green_path_app"): | |
| self.geolocator = Nominatim(user_agent=user_agent) | |
| # Constants used for calculations | |
| self.emission_factors = {"road": 0.21, "rail": 0.041, "ship": 0.014} # kg CO₂/tonne-km | |
| self.avg_speeds_kmh = {"road": 80, "rail": 100, "ship": 30} # km/h | |
| self.carbon_tax_rate_usd = 50.0 # $ per tonne of CO₂ | |
| def geocode(self, place_name): | |
| """Geocodes a place name, returning (lat, lon) or None on failure.""" | |
| try: | |
| location = self.geolocator.geocode(place_name, timeout=10) | |
| if location: | |
| return (location.latitude, location.longitude) | |
| except Exception: | |
| return None | |
| return None | |
| def calculate_route_metrics(self, distance_km, mode, weight_tonnes): | |
| """Calculates all metrics for a single route.""" | |
| emissions_kg = distance_km * self.emission_factors[mode] * weight_tonnes | |
| travel_time_hours = distance_km / self.avg_speeds_kmh[mode] | |
| carbon_tax_usd = (emissions_kg / 1000) * self.carbon_tax_rate_usd | |
| return { | |
| "transport_mode": mode, | |
| "co2_emissions_kg": emissions_kg, | |
| "estimated_travel_time_hours": travel_time_hours, | |
| "carbon_tax_cost_usd": carbon_tax_usd, | |
| } | |
| def recommend_green_routes(self, start_place, end_place, weight_tonnes): | |
| """ | |
| The main method that finds and processes route recommendations. | |
| Returns a dictionary compatible with the Streamlit frontend. | |
| """ | |
| # --- 1. Input Validation --- | |
| if not start_place or not start_place.strip(): | |
| return {"error": "Origin location cannot be empty."} | |
| if not end_place or not end_place.strip(): | |
| return {"error": "Destination location cannot be empty."} | |
| # --- 2. Geocoding --- | |
| start_coords = self.geocode(start_place) | |
| if start_coords is None: | |
| return {"error": f"Could not find location: '{start_place}'"} | |
| end_coords = self.geocode(end_place) | |
| if end_coords is None: | |
| return {"error": f"Could not find location: '{end_place}'"} | |
| # --- 3. Calculate Distance (Haversine Formula) --- | |
| R = 6371 # Radius of Earth in km | |
| lat1, lon1 = start_coords | |
| lat2, lon2 = end_coords | |
| dlat = math.radians(lat2 - lat1) | |
| dlon = math.radians(lon2 - lon1) | |
| a = math.sin(dlat / 2)**2 + math.cos(math.radians(lat1)) * math.cos(math.radians(lat2)) * math.sin(dlon / 2)**2 | |
| c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a)) | |
| distance_km = R * c | |
| # --- 4. Calculate Metrics for All Modes --- | |
| all_routes = [] | |
| for mode in self.emission_factors: | |
| route_metrics = self.calculate_route_metrics(distance_km, mode, weight_tonnes) | |
| all_routes.append(route_metrics) | |
| # --- 5. Post-Processing for Comparison Metrics --- | |
| if not all_routes: | |
| return {"error": "Could not calculate any routes."} | |
| # Find the worst emission value to calculate savings against it | |
| worst_emission = max(route['co2_emissions_kg'] for route in all_routes) | |
| for route in all_routes: | |
| if worst_emission > 0: | |
| reduction = (worst_emission - route['co2_emissions_kg']) / worst_emission * 100 | |
| route['emission_reduction_percent'] = reduction | |
| else: | |
| route['emission_reduction_percent'] = 0 | |
| # Sort routes by emissions (best first) | |
| all_routes.sort(key=lambda x: x['co2_emissions_kg']) | |
| # --- 6. Return Data in the Exact Format the App Expects --- | |
| return {"recommendations": all_routes} |