From 92eabc8c2a6186a596f26e0df0bcf9e0cd8c936c Mon Sep 17 00:00:00 2001 From: Shiva Pochampally <146849983+PieLord757@users.noreply.github.com> Date: Sun, 28 Sep 2025 00:49:47 -0400 Subject: [PATCH] Added meteo API functionality to the MongoDB agent and LLM summary --- .../gemini_mongo_mateo.cpython-312.pyc | Bin 0 -> 16294 bytes .../gemini_reroute_mateo.cpython-312.pyc | Bin 0 -> 26908 bytes llm/gemini_mongo_mateo.py | 377 +++ llm/gemini_reroute_mateo.py | 588 +++++ llm/safe_route_coordinates.json | 2349 +++++++++++++++++ 5 files changed, 3314 insertions(+) create mode 100644 llm/__pycache__/gemini_mongo_mateo.cpython-312.pyc create mode 100644 llm/__pycache__/gemini_reroute_mateo.cpython-312.pyc create mode 100644 llm/gemini_mongo_mateo.py create mode 100644 llm/gemini_reroute_mateo.py create mode 100644 llm/safe_route_coordinates.json diff --git a/llm/__pycache__/gemini_mongo_mateo.cpython-312.pyc b/llm/__pycache__/gemini_mongo_mateo.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8bcd0af3dddd622544b3f5fcb75986731bb1700d GIT binary patch literal 16294 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 literal 0 HcmV?d00001 diff --git a/llm/gemini_mongo_mateo.py b/llm/gemini_mongo_mateo.py new file mode 100644 index 0000000..32d5709 --- /dev/null +++ b/llm/gemini_mongo_mateo.py @@ -0,0 +1,377 @@ +#MONGO_URI=mongodb+srv://Admin:HelloKitty420@geobase.tyxsoir.mongodb.net/crashes + +import os +import requests +import time +from datetime import datetime +from pymongo import MongoClient +from langchain_google_genai import ChatGoogleGenerativeAI +from math import radians, sin, cos, sqrt, atan2 + +# Configuration +GEMINI_API_KEY = "AIzaSyBCbEOo4aK72507hqvpYkE9zXUe-z5aSXA" +MONGO_URI = "mongodb+srv://Admin:HelloKitty420@geobase.tyxsoir.mongodb.net/crashes" + +llm = ChatGoogleGenerativeAI(model="gemini-2.5-flash-lite", api_key=GEMINI_API_KEY) + +def connect_to_mongodb(): + """ + Connect to MongoDB database and return the collection. + """ + try: + print("Connecting to MongoDB...") + client = MongoClient(MONGO_URI) + # Test the connection + client.admin.command('ping') + print("βœ… Successfully connected to MongoDB!") + + db = client.crashes # Database name + collection = db.crashes # Collection name - corrected to 'crashes' + + # Get collection stats + total_count = collection.estimated_document_count() + print(f"πŸ“Š Found {total_count:,} total crash records in database") + + # Check specifically for 2020+ data + filter_2020_plus = {"reportDate": {"$gte": datetime(2020, 1, 1)}} + count_2020_plus = collection.count_documents(filter_2020_plus) + print(f"πŸ“… Found {count_2020_plus:,} crash records from 2020 onward") + + return collection + + except Exception as e: + print(f"❌ Failed to connect to MongoDB: {e}") + return None + +def get_crashes_within_radius_mongodb(collection, center_lat, center_lon, radius_km): + """ + Query MongoDB for crashes within specified radius using geospatial query. + Filters for crashes from 2020 onward only. + + Args: + collection: MongoDB collection object + center_lat: Latitude of center point + center_lon: Longitude of center point + radius_km: Radius in kilometers + + Returns: + List of crash documents within radius from 2020 onward + """ + try: + print(f"πŸ” Querying crashes within {radius_km}km of ({center_lat:.6f}, {center_lon:.6f}) from 2020 onward...") + + # MongoDB geospatial query using $geoWithin and $centerSphere + # $centerSphere uses radians, so convert km to radians (divide by Earth's radius in km) + radius_radians = radius_km / 6371 # Earth's radius in km + + # Combined query: geospatial AND date filter for 2020+ + query = { + "location": { + "$geoWithin": { + "$centerSphere": [[center_lon, center_lat], radius_radians] + } + }, + "reportDate": { + "$gte": datetime(2020, 1, 1) # Only crashes from 2020 onward + } + } + + # Execute the query + cursor = collection.find(query) + crashes = list(cursor) + + print(f"πŸ“ Found {len(crashes)} crashes within {radius_km}km radius (from 2020 onward)") + + # Add distance calculation to each crash for sorting + for crash in crashes: + if crash.get('location', {}).get('coordinates'): + crash_lon, crash_lat = crash['location']['coordinates'] + distance = haversine_distance(center_lat, center_lon, crash_lat, crash_lon) + crash['distance_km'] = distance + + # Sort by distance + crashes.sort(key=lambda x: x.get('distance_km', float('inf'))) + + return crashes + + except Exception as e: + print(f"❌ Error querying MongoDB: {e}") + return [] + +def haversine_distance(lat1, lon1, lat2, lon2): + """ + Calculate the great circle distance between two points + on the earth (specified in decimal degrees) + Returns distance in kilometers + """ + # Convert decimal degrees to radians + lat1, lon1, lat2, lon2 = map(radians, [lat1, lon1, lat2, lon2]) + + # Haversine formula + dlat = lat2 - lat1 + dlon = lon2 - lon1 + a = sin(dlat/2)**2 + cos(lat1) * cos(lat2) * sin(dlon/2)**2 + c = 2 * atan2(sqrt(a), sqrt(1-a)) + + # Radius of earth in kilometers + r = 6371 + + return c * r + +def get_current_weather(lat, lon): + """ + Get current weather data from Open-Meteo API. + """ + try: + url = "https://api.open-meteo.com/v1/forecast" + response = requests.get( + url, + params={ + "latitude": lat, + "longitude": lon, + "current": "precipitation,wind_speed_10m,is_day,weather_code" + }, + timeout=10 + ) + response.raise_for_status() + data = response.json() + + current = data.get("current", {}) + + # Map weather codes to descriptions (WMO Weather interpretation codes) + weather_code_map = { + 0: "Clear sky", + 1: "Mainly clear", + 2: "Partly cloudy", + 3: "Overcast", + 45: "Fog", + 48: "Depositing rime fog", + 51: "Light drizzle", + 53: "Moderate drizzle", + 55: "Dense drizzle", + 56: "Light freezing drizzle", + 57: "Dense freezing drizzle", + 61: "Slight rain", + 63: "Moderate rain", + 65: "Heavy rain", + 66: "Light freezing rain", + 67: "Heavy freezing rain", + 71: "Slight snow fall", + 73: "Moderate snow fall", + 75: "Heavy snow fall", + 77: "Snow grains", + 80: "Slight rain showers", + 81: "Moderate rain showers", + 82: "Violent rain showers", + 85: "Slight snow showers", + 86: "Heavy snow showers", + 95: "Thunderstorm", + 96: "Thunderstorm with slight hail", + 99: "Thunderstorm with heavy hail" + } + + weather_code = current.get("weather_code", 0) + weather_desc = weather_code_map.get(weather_code, "Unknown weather") + precipitation = current.get("precipitation", 0) + wind_speed = current.get("wind_speed_10m", 0) + is_day = current.get("is_day", 1) + + day_night = "day" if is_day else "night" + + summary_parts = [] + summary_parts.append(f"Conditions: {weather_desc}") + summary_parts.append(f"Precipitation: {precipitation}mm/h") + summary_parts.append(f"Wind: {wind_speed} km/h") + summary_parts.append(f"Time: {day_night}") + + summary = " | ".join(summary_parts) + + return data, summary + + except Exception as e: + return None, f"Weather API failed: {str(e)}" + +def analyze_mongodb_crash_patterns(crashes, center_lat, center_lon, radius_km, weather_summary=None): + """ + Analyze crash patterns from MongoDB data and generate safety assessment. + """ + if not crashes: + return "No crash data available for the specified location and radius." + + total_crashes = len(crashes) + avg_distance = sum(crash.get('distance_km', 0) for crash in crashes) / total_crashes if crashes else 0 + + # Analyze crash patterns from MongoDB structure + crash_analysis = { + 'severity_counts': {}, + 'total_fatalities': 0, + 'total_major_injuries': 0, + 'total_minor_injuries': 0, + 'speeding_involved': 0, + 'impaired_involved': 0, + 'pedestrian_crashes': 0, + 'bicyclist_crashes': 0, + 'vehicle_counts': {} + } + + # Analyze each crash + for crash in crashes: + # Severity analysis + severity = crash.get('severity', 'Unknown') + crash_analysis['severity_counts'][severity] = crash_analysis['severity_counts'].get(severity, 0) + 1 + + # Casualty analysis + casualties = crash.get('casualties', {}) + + # Count fatalities and injuries across all categories + for category in ['bicyclists', 'drivers', 'pedestrians', 'passengers']: + if category in casualties: + crash_analysis['total_fatalities'] += casualties[category].get('fatal', 0) + crash_analysis['total_major_injuries'] += casualties[category].get('major_injuries', 0) + crash_analysis['total_minor_injuries'] += casualties[category].get('minor_injuries', 0) + + # Count vulnerable road user involvement + if casualties.get('pedestrians', {}).get('total', 0) > 0: + crash_analysis['pedestrian_crashes'] += 1 + if casualties.get('bicyclists', {}).get('total', 0) > 0: + crash_analysis['bicyclist_crashes'] += 1 + + # Circumstances analysis + circumstances = crash.get('circumstances', {}) + if circumstances.get('speeding_involved', False): + crash_analysis['speeding_involved'] += 1 + + # Check for impairment + if (circumstances.get('pedestrians_impaired', False) or + circumstances.get('bicyclists_impaired', False) or + circumstances.get('drivers_impaired', False)): + crash_analysis['impaired_involved'] += 1 + + # Vehicle analysis + vehicles = crash.get('vehicles', {}) + total_vehicles = vehicles.get('total', 0) + crash_analysis['vehicle_counts'][str(total_vehicles)] = crash_analysis['vehicle_counts'].get(str(total_vehicles), 0) + 1 + + # Create comprehensive summary for LLM + crash_summary = f""" +SEVERITY BREAKDOWN: {dict(crash_analysis['severity_counts'])} +CASUALTIES: +- Fatal injuries: {crash_analysis['total_fatalities']} +- Major injuries: {crash_analysis['total_major_injuries']} +- Minor injuries: {crash_analysis['total_minor_injuries']} +VULNERABLE ROAD USERS: +- Crashes involving pedestrians: {crash_analysis['pedestrian_crashes']} +- Crashes involving bicyclists: {crash_analysis['bicyclist_crashes']} +RISK FACTORS: +- Crashes involving speeding: {crash_analysis['speeding_involved']} +- Crashes with impairment: {crash_analysis['impaired_involved']} +VEHICLE INVOLVEMENT: {dict(crash_analysis['vehicle_counts'])}""" + + # Add current weather information if available + weather_info = "" + if weather_summary: + weather_info = f""" + +CURRENT WEATHER CONDITIONS: +{weather_summary}""" + + # Create prompt for LLM + prompt = f"""You are a traffic safety expert analyzing recent crash data (2020 onward) and current conditions for location ({center_lat:.6f}, {center_lon:.6f}) within a {radius_km}km radius. + + CRASH STATISTICS (2020-Present): + - Total crashes in area: {total_crashes} + - Average distance from center: {avg_distance:.2f} km + - Search area: {radius_km}km radius (approximately {3.14159 * radius_km**2:.1f} kmΒ²) + + DETAILED CRASH ANALYSIS:{crash_summary}{weather_info} + + Based on this comprehensive recent MongoDB crash data (2020 onward), provide: + 1. A danger level assessment (Low, Moderate, High, Very High) + 2. Key safety concerns based on recent crash patterns AND current weather conditions + 3. Specific recommendations for someone traveling to this location RIGHT NOW + 4. Notable patterns in recent crash data (severity, vulnerable users, risk factors) + 5. How current weather conditions may affect driving safety + + Focus on practical, actionable safety advice based on recent trends. Be specific about identified risks and provide clear recommendations.""" + + try: + response = llm.invoke(prompt) + return response.content + except Exception as e: + return f"Error analyzing crash data with LLM: {e}" + +def main(): + """ + Main function to analyze crash danger using MongoDB geospatial queries. + """ + print("πŸš— MongoDB Traffic Crash Danger Analysis Tool (2020+ Data)") + print("=" * 65) + + # Connect to MongoDB + collection = connect_to_mongodb() + if collection is None: + print("❌ Could not connect to MongoDB. Exiting...") + return + + # Get user input for location and radius + try: + center_lat = float(input("Enter latitude: ")) + center_lon = float(input("Enter longitude: ")) + radius_km = float(input("Enter search radius in kilometers (default: 1.0): ") or "1.0") + + print(f"\nπŸ” Analyzing recent crashes (2020+) within {radius_km}km of ({center_lat:.6f}, {center_lon:.6f})...") + + # Query MongoDB for nearby crashes using geospatial indexing + nearby_crashes = get_crashes_within_radius_mongodb(collection, center_lat, center_lon, radius_km) + + if len(nearby_crashes) > 0: + print(f"πŸ”΄ Closest crash: {nearby_crashes[0]['distance_km']:.3f}km away") + print(f"πŸ”΄ Furthest crash: {nearby_crashes[-1]['distance_km']:.3f}km away") + + # Display sample crash details from MongoDB structure + print("πŸ“Š Sample crash details from MongoDB:") + sample = nearby_crashes[0] + print(f" - ID: {sample.get('crashId', 'N/A')}") + print(f" - Severity: {sample.get('severity', 'N/A')}") + print(f" - Address: {sample.get('address', 'N/A')}") + print(f" - Ward: {sample.get('ward', 'N/A')}") + + casualties = sample.get('casualties', {}) + total_casualties = 0 + for cat in ['bicyclists', 'drivers', 'pedestrians', 'passengers']: + cat_data = casualties.get(cat, {}) + total_casualties += (cat_data.get('fatal', 0) + + cat_data.get('major_injuries', 0) + + cat_data.get('minor_injuries', 0)) + print(f" - Total casualties: {total_casualties}") + else: + print("ℹ️ No crashes found within the specified radius.") + + # Get current weather conditions + print("\n🌀️ Fetching current weather conditions...") + weather_data, weather_summary = get_current_weather(center_lat, center_lon) + + if weather_data is None: + print(f"⚠️ Weather data unavailable: {weather_summary}") + weather_summary = None + else: + print(f"🌀️ Current conditions: {weather_summary}") + + # Generate comprehensive safety analysis using LLM + print("\nπŸ€– Generating comprehensive safety assessment...") + analysis = analyze_mongodb_crash_patterns(nearby_crashes, center_lat, center_lon, radius_km, weather_summary) + + print("\n" + "="*65) + print("🚨 RECENT CRASH SAFETY ASSESSMENT REPORT (2020-Present)") + print("="*65) + print(analysis) + + except ValueError: + print("❌ Please enter valid numerical values for coordinates and radius.") + except KeyboardInterrupt: + print("\n⚠️ Analysis cancelled by user.") + except Exception as e: + print(f"❌ An error occurred: {e}") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/llm/gemini_reroute_mateo.py b/llm/gemini_reroute_mateo.py new file mode 100644 index 0000000..12ad10b --- /dev/null +++ b/llm/gemini_reroute_mateo.py @@ -0,0 +1,588 @@ +import os +import requests +import json +from datetime import datetime +from pymongo import MongoClient +from langchain_google_genai import ChatGoogleGenerativeAI +from math import radians, sin, cos, sqrt, atan2, degrees, atan2 +from typing import List, Tuple, Dict, Optional + + + + +# Configuration +GEMINI_API_KEY = "AIzaSyBCbEOo4aK72507hqvpYkE9zXUe-z5aSXA" +MONGO_URI = "mongodb+srv://Admin:HelloKitty420@geobase.tyxsoir.mongodb.net/crashes" +MAPBOX_API_KEY = "pk.eyJ1IjoicGllbG9yZDc1NyIsImEiOiJjbWcxdTd6c3AwMXU1MmtxMDh6b2l5amVrIn0.5Es0azrah23GX1e9tmbjGw" + +llm = ChatGoogleGenerativeAI(model="gemini-2.5-flash-lite", api_key=GEMINI_API_KEY) + +class SafeRouteAnalyzer: + def __init__(self, mongo_uri: str): + """Initialize the safe route analyzer with MongoDB connection.""" + try: + self.client = MongoClient(mongo_uri) + self.client.admin.command('ping') + self.db = self.client.crashes + self.collection = self.db.crashes + print("βœ… Connected to MongoDB for route safety analysis") + except Exception as e: + print(f"❌ Failed to connect to MongoDB: {e}") + self.collection = None + + def haversine_distance(self, lat1: float, lon1: float, lat2: float, lon2: float) -> float: + """Calculate distance between two points in kilometers.""" + lat1, lon1, lat2, lon2 = map(radians, [lat1, lon1, lat2, lon2]) + dlat = lat2 - lat1 + dlon = lon2 - lon1 + a = sin(dlat/2)**2 + cos(lat1) * cos(lat2) * sin(dlon/2)**2 + c = 2 * atan2(sqrt(a), sqrt(1-a)) + return 6371 * c # Earth's radius in km + + def get_route_from_mapbox(self, start_lat: float, start_lon: float, + end_lat: float, end_lon: float, profile: str = "driving") -> Dict: + """ + Get route from Mapbox Directions API. + + Args: + start_lat, start_lon: Starting coordinates + end_lat, end_lon: Destination coordinates + profile: 'driving', 'walking', or 'cycling' + + Returns: + Route data with coordinates, distance, duration + """ + try: + url = f"https://api.mapbox.com/directions/v5/mapbox/{profile}/{start_lon},{start_lat};{end_lon},{end_lat}" + params = { + 'access_token': MAPBOX_API_KEY, + 'overview': 'full', + 'geometries': 'geojson', + 'steps': 'true' + } + + response = requests.get(url, params=params, timeout=15) + response.raise_for_status() + + data = response.json() + + if data.get('code') == 'Ok' and data.get('routes'): + route = data['routes'][0] + geometry = route['geometry'] + + # Extract coordinates from GeoJSON format + coordinates = [[coord[1], coord[0]] for coord in geometry['coordinates']] # Convert [lon,lat] to [lat,lon] + + return { + 'success': True, + 'coordinates': coordinates, # List of [lat, lon] pairs + 'distance_km': route['distance'] / 1000, + 'duration_min': route['duration'] / 60, + 'geometry': geometry + } + else: + error_msg = data.get('message', 'No route found') + return {'success': False, 'error': error_msg} + + except Exception as e: + return {'success': False, 'error': str(e)} + + def get_alternative_routes_mapbox(self, start_lat: float, start_lon: float, + end_lat: float, end_lon: float, num_alternatives: int = 3) -> List[Dict]: + """ + Get multiple alternative routes using Mapbox Directions API. + """ + try: + url = f"https://api.mapbox.com/directions/v5/mapbox/driving/{start_lon},{start_lat};{end_lon},{end_lat}" + params = { + 'access_token': MAPBOX_API_KEY, + 'alternatives': 'true', # Request alternatives + 'overview': 'full', + 'geometries': 'geojson', + 'steps': 'false' + } + + response = requests.get(url, params=params, timeout=15) + response.raise_for_status() + data = response.json() + + routes = [] + if data.get('code') == 'Ok' and data.get('routes'): + for i, route in enumerate(data['routes'][:num_alternatives]): + geometry = route['geometry'] + coordinates = [[coord[1], coord[0]] for coord in geometry['coordinates']] # Convert [lon,lat] to [lat,lon] + + routes.append({ + 'route_id': i, + 'coordinates': coordinates, + 'distance_km': route['distance'] / 1000, + 'duration_min': route['duration'] / 60, + 'geometry': geometry + }) + + return routes + + except Exception as e: + print(f"Error getting alternative routes: {e}") + return [] + + def analyze_route_safety(self, route_coordinates: List[Tuple[float, float]], + buffer_km: float = 0.2) -> Dict: + """ + Analyze safety along a route by checking for crashes near route points. + + Args: + route_coordinates: List of (lat, lon) tuples along the route + buffer_km: How far to look for crashes around each route point + + Returns: + Safety analysis data + """ + if self.collection is None: + return {'error': 'No database connection'} + + try: + all_nearby_crashes = [] + safety_scores = [] + + # Sample every Nth point to avoid too many queries (adjust based on route length) + sample_interval = max(1, len(route_coordinates) // 20) # Max 20 sample points + sample_points = route_coordinates[::sample_interval] + + print(f"πŸ” Analyzing safety at {len(sample_points)} points along route...") + + for i, (lat, lon) in enumerate(sample_points): + # Query crashes within buffer distance of this route point + radius_radians = buffer_km / 6371 + + query = { + "location": { + "$geoWithin": { + "$centerSphere": [[lon, lat], radius_radians] + } + }, + "reportDate": { + "$gte": datetime(2020, 1, 1) + } + } + + crashes_near_point = list(self.collection.find(query)) + + # Calculate safety score for this point (lower = safer) + point_safety_score = self.calculate_point_safety_score(crashes_near_point) + safety_scores.append({ + 'point_index': i * sample_interval, + 'coordinates': [lat, lon], + 'crashes_count': len(crashes_near_point), + 'safety_score': point_safety_score + }) + + all_nearby_crashes.extend(crashes_near_point) + + # Remove duplicate crashes + unique_crashes = {} + for crash in all_nearby_crashes: + crash_id = crash.get('crashId', str(crash.get('_id'))) + if crash_id not in unique_crashes: + unique_crashes[crash_id] = crash + + unique_crashes_list = list(unique_crashes.values()) + + # Calculate overall route safety metrics + total_crashes = len(unique_crashes_list) + avg_safety_score = sum(point['safety_score'] for point in safety_scores) / len(safety_scores) if safety_scores else 0 + max_danger_score = max((point['safety_score'] for point in safety_scores), default=0) + + return { + 'total_crashes_near_route': total_crashes, + 'average_safety_score': avg_safety_score, + 'max_danger_score': max_danger_score, + 'safety_points': safety_scores, + 'crashes_data': unique_crashes_list, + 'route_length_points': len(route_coordinates) + } + + except Exception as e: + return {'error': str(e)} + + def calculate_point_safety_score(self, crashes: List[Dict]) -> float: + """ + Calculate a safety score for a point based on nearby crashes. + Higher score = more dangerous + """ + if not crashes: + return 0.0 + + score = 0.0 + + for crash in crashes: + # Base score for any crash + base_score = 1.0 + + # Weight by severity + severity = crash.get('severity', '').lower() + if 'fatal' in severity or 'major' in severity: + base_score *= 3.0 + elif 'minor' in severity: + base_score *= 1.5 + + # Weight by casualty count + casualties = crash.get('casualties', {}) + total_casualties = 0 + for category in ['bicyclists', 'drivers', 'pedestrians', 'passengers']: + if category in casualties: + cat_data = casualties[category] + total_casualties += (cat_data.get('fatal', 0) * 5 + + cat_data.get('major_injuries', 0) * 2 + + cat_data.get('minor_injuries', 0) * 1) + + base_score += total_casualties * 0.5 + + # Weight by circumstances + circumstances = crash.get('circumstances', {}) + if circumstances.get('speeding_involved', False): + base_score *= 1.3 + if any([circumstances.get('pedestrians_impaired', False), + circumstances.get('bicyclists_impaired', False), + circumstances.get('drivers_impaired', False)]): + base_score *= 1.4 + + score += base_score + + return score + + def generate_safety_report_with_llm(self, route_safety_data: Dict, + route_info: Dict, weather_summary: str = None) -> str: + """ + Use LLM to generate comprehensive safety report and route recommendations. + """ + if 'error' in route_safety_data: + return f"Error analyzing route safety: {route_safety_data['error']}" + + crashes = route_safety_data.get('crashes_data', []) + safety_points = route_safety_data.get('safety_points', []) + + # Find most dangerous sections + dangerous_points = sorted(safety_points, key=lambda x: x['safety_score'], reverse=True)[:3] + + # Analyze crash patterns + severity_counts = {} + casualty_summary = {'fatal': 0, 'major': 0, 'minor': 0} + risk_factors = {'speeding': 0, 'impairment': 0, 'pedestrian': 0, 'bicyclist': 0} + + for crash in crashes: + severity = crash.get('severity', 'Unknown') + severity_counts[severity] = severity_counts.get(severity, 0) + 1 + + # Count casualties + casualties = crash.get('casualties', {}) + for category in ['bicyclists', 'drivers', 'pedestrians', 'passengers']: + if category in casualties: + cat_data = casualties[category] + casualty_summary['fatal'] += cat_data.get('fatal', 0) + casualty_summary['major'] += cat_data.get('major_injuries', 0) + casualty_summary['minor'] += cat_data.get('minor_injuries', 0) + + # Count risk factors + circumstances = crash.get('circumstances', {}) + if circumstances.get('speeding_involved', False): + risk_factors['speeding'] += 1 + if any([circumstances.get(f'{cat}_impaired', False) for cat in ['pedestrians', 'bicyclists', 'drivers']]): + risk_factors['impairment'] += 1 + if casualties.get('pedestrians', {}).get('total', 0) > 0: + risk_factors['pedestrian'] += 1 + if casualties.get('bicyclists', {}).get('total', 0) > 0: + risk_factors['bicyclist'] += 1 + + weather_info = f"\n\nCURRENT WEATHER CONDITIONS:\n{weather_summary}" if weather_summary else "" + + prompt = f"""You are an expert traffic safety analyst and route planning specialist. Analyze this route's safety profile and provide recommendations. + +ROUTE INFORMATION: +- Distance: {route_info.get('distance_km', 0):.1f} km +- Estimated duration: {route_info.get('duration_min', 0):.0f} minutes +- Analysis points along route: {len(safety_points)} + +SAFETY ANALYSIS (2020+ crash data): +- Total crashes near route: {route_safety_data.get('total_crashes_near_route', 0)} +- Average safety score: {route_safety_data.get('average_safety_score', 0):.2f} +- Maximum danger score: {route_safety_data.get('max_danger_score', 0):.2f} + +CRASH BREAKDOWN: +- Severity distribution: {severity_counts} +- Casualties: {casualty_summary['fatal']} fatal, {casualty_summary['major']} major injuries, {casualty_summary['minor']} minor injuries +- Risk factors: {risk_factors['speeding']} speeding-related, {risk_factors['impairment']} impairment-related +- Vulnerable users: {risk_factors['pedestrian']} pedestrian crashes, {risk_factors['bicyclist']} bicyclist crashes + +MOST DANGEROUS SECTIONS: +{chr(10).join([f"Point {p['point_index']}: {p['crashes_count']} crashes nearby, safety score {p['safety_score']:.1f}" for p in dangerous_points[:3]])} +{weather_info} + +Please provide: +1. Overall route safety assessment (SAFE/MODERATE RISK/HIGH RISK/DANGEROUS) +2. Specific dangerous sections to watch out for +3. Driving recommendations for this route considering current conditions +4. Whether an alternative route should be recommended +5. Time-of-day considerations if applicable +6. Weather-specific precautions based on crash patterns + +Be specific and actionable in your recommendations.""" + + try: + response = llm.invoke(prompt) + return response.content + except Exception as e: + return f"Error generating safety analysis: {e}" + + def find_safer_route(self, start_lat: float, start_lon: float, + end_lat: float, end_lon: float) -> Dict: + """ + Find the safest route among alternatives by analyzing crash data. + """ + print("πŸ—ΊοΈ Getting alternative routes...") + + # Get multiple route options + alternative_routes = self.get_alternative_routes_mapbox(start_lat, start_lon, end_lat, end_lon) + + if not alternative_routes: + print("❌ No routes found") + return {'error': 'No routes available'} + + print(f"πŸ“ Analyzing {len(alternative_routes)} route options for safety...") + + # Analyze safety for each route + route_analyses = [] + for i, route in enumerate(alternative_routes): + print(f"πŸ” Analyzing route {i+1}/{len(alternative_routes)}...") + + safety_analysis = self.analyze_route_safety(route['coordinates']) + + if 'error' not in safety_analysis: + route_analyses.append({ + 'route_id': i, + 'route_data': route, + 'safety_analysis': safety_analysis, + 'safety_score': safety_analysis.get('average_safety_score', float('inf')) + }) + + if not route_analyses: + return {'error': 'Could not analyze any routes for safety'} + + # Sort routes by safety (lower score = safer) + route_analyses.sort(key=lambda x: x['safety_score']) + + # Get weather for additional context + weather_data, weather_summary = self.get_current_weather(start_lat, start_lon) + + # Generate safety reports for top routes + results = { + 'recommended_route': route_analyses[0], + 'alternative_routes': route_analyses[1:], + 'weather_summary': weather_summary + } + + # Generate LLM analysis for the safest route + safest_route = route_analyses[0] + safety_report = self.generate_safety_report_with_llm( + safest_route['safety_analysis'], + safest_route['route_data'], + weather_summary + ) + + results['safety_report'] = safety_report + results['route_comparison'] = self.compare_routes_with_llm(route_analyses, weather_summary) + + return results + + def compare_routes_with_llm(self, route_analyses: List[Dict], weather_summary: str = None) -> str: + """ + Use LLM to compare multiple routes and explain why one is safer. + """ + if len(route_analyses) < 2: + return "Only one route available for analysis." + + comparison_data = [] + for i, analysis in enumerate(route_analyses): + route_data = analysis['route_data'] + safety_data = analysis['safety_analysis'] + + comparison_data.append({ + 'route_num': i + 1, + 'distance_km': route_data.get('distance_km', 0), + 'duration_min': route_data.get('duration_min', 0), + 'crashes_near_route': safety_data.get('total_crashes_near_route', 0), + 'safety_score': safety_data.get('average_safety_score', 0), + 'max_danger_score': safety_data.get('max_danger_score', 0) + }) + + weather_info = f"\nCurrent weather: {weather_summary}" if weather_summary else "" + + prompt = f"""Compare these route options for safety and provide a recommendation: + +ROUTE OPTIONS: +{chr(10).join([f"Route {r['route_num']}: {r['distance_km']:.1f}km, {r['duration_min']:.0f}min, {r['crashes_near_route']} nearby crashes, safety score {r['safety_score']:.2f}" for r in comparison_data])}{weather_info} + +Provide: +1. Which route is safest and why +2. Trade-offs between routes (safety vs. time/distance) +3. Clear recommendation with reasoning +4. Any weather-related considerations + +Keep it concise and actionable.""" + + try: + response = llm.invoke(prompt) + return response.content + except Exception as e: + return f"Error comparing routes: {e}" + + def get_current_weather(self, lat: float, lon: float) -> Tuple[Optional[Dict], str]: + """Get current weather conditions using Open-Meteo API.""" + try: + url = "https://api.open-meteo.com/v1/forecast" + response = requests.get( + url, + params={ + "latitude": lat, + "longitude": lon, + "current": "precipitation,wind_speed_10m,is_day,weather_code" + }, + timeout=10 + ) + response.raise_for_status() + data = response.json() + + current = data.get("current", {}) + + # Map weather codes to descriptions (WMO Weather interpretation codes) + weather_code_map = { + 0: "Clear sky", + 1: "Mainly clear", + 2: "Partly cloudy", + 3: "Overcast", + 45: "Fog", + 48: "Depositing rime fog", + 51: "Light drizzle", + 53: "Moderate drizzle", + 55: "Dense drizzle", + 56: "Light freezing drizzle", + 57: "Dense freezing drizzle", + 61: "Slight rain", + 63: "Moderate rain", + 65: "Heavy rain", + 66: "Light freezing rain", + 67: "Heavy freezing rain", + 71: "Slight snow fall", + 73: "Moderate snow fall", + 75: "Heavy snow fall", + 77: "Snow grains", + 80: "Slight rain showers", + 81: "Moderate rain showers", + 82: "Violent rain showers", + 85: "Slight snow showers", + 86: "Heavy snow showers", + 95: "Thunderstorm", + 96: "Thunderstorm with slight hail", + 99: "Thunderstorm with heavy hail" + } + + weather_code = current.get("weather_code", 0) + weather_desc = weather_code_map.get(weather_code, "Unknown weather") + precipitation = current.get("precipitation", 0) + wind_speed = current.get("wind_speed_10m", 0) + is_day = current.get("is_day", 1) + + day_night = "day" if is_day else "night" + + summary = f"{weather_desc}, precipitation {precipitation}mm/h, wind {wind_speed} km/h, {day_night}" + return data, summary + + except Exception as e: + return None, f"Weather unavailable: {e}" + + +def main(): + """ + Demo function showing how to use the SafeRouteAnalyzer. + """ + print("πŸ›£οΈ Safe Route Planning System") + print("=" * 50) + + analyzer = SafeRouteAnalyzer(MONGO_URI) + + if analyzer.collection is None: + print("❌ Cannot proceed without database connection") + return + + # Get input + try: + print("\nπŸ“ Enter route details:") + start_lat = float(input("Starting latitude: ")) + start_lon = float(input("Starting longitude: ")) + end_lat = float(input("Destination latitude: ")) + end_lon = float(input("Destination longitude: ")) + + print(f"\nπŸš— Planning safe route from ({start_lat:.4f}, {start_lon:.4f}) to ({end_lat:.4f}, {end_lon:.4f})") + + # Find the safest route + results = analyzer.find_safer_route(start_lat, start_lon, end_lat, end_lon) + + if 'error' in results: + print(f"❌ Error: {results['error']}") + return + + # Display results + recommended = results['recommended_route'] + route_data = recommended['route_data'] + safety_data = recommended['safety_analysis'] + + print("\n" + "="*50) + print("πŸ† RECOMMENDED SAFE ROUTE") + print("="*50) + print(f"πŸ“ Distance: {route_data['distance_km']:.1f} km") + print(f"⏱️ Duration: {route_data['duration_min']:.0f} minutes") + print(f"🚨 Crashes nearby: {safety_data['total_crashes_near_route']}") + print(f"πŸ“Š Safety score: {safety_data['average_safety_score']:.2f} (lower is safer)") + + print(f"\n🌀️ Weather: {results.get('weather_summary', 'N/A')}") + + print("\nπŸ“‹ SAFETY ANALYSIS:") + print("-" * 30) + print(results['safety_report']) + + if len(results['alternative_routes']) > 0: + print("\nπŸ”„ ROUTE COMPARISON:") + print("-" * 30) + print(results['route_comparison']) + + # Output for Mapbox visualization + coordinates = recommended['route_data']['coordinates'] + print(f"\nπŸ—ΊοΈ Route coordinates for Mapbox ({len(coordinates)} points):") + print("First 5 points:", coordinates[:5]) + print("Last 5 points:", coordinates[-5:]) + + # You can save these coordinates to pass to your Mapbox visualization + route_data_to_save = { + 'recommended_route': coordinates, + 'route_info': route_data, + 'safety_summary': { + 'total_crashes': safety_data['total_crashes_near_route'], + 'average_safety_score': safety_data['average_safety_score'], + 'max_danger_score': safety_data['max_danger_score'] + } + } + + with open('safe_route_coordinates.json', 'w') as f: + json.dump(route_data_to_save, f, indent=2) + print("πŸ“ Route data saved to 'safe_route_coordinates.json'") + + except ValueError: + print("❌ Please enter valid numerical coordinates") + except KeyboardInterrupt: + print("\n⚠️ Route planning cancelled") + except Exception as e: + print(f"❌ Error: {e}") + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/llm/safe_route_coordinates.json b/llm/safe_route_coordinates.json new file mode 100644 index 0000000..8063b1d --- /dev/null +++ b/llm/safe_route_coordinates.json @@ -0,0 +1,2349 @@ +{ + "recommended_route": [ + [ + 38.909652, + -77.037501 + ], + [ + 38.909653, + -77.037271 + ], + [ + 38.909653, + -77.037209 + ], + [ + 38.909654, + -77.036649 + ], + [ + 38.909653, + -77.036523 + ], + [ + 38.909654, + -77.036407 + ], + [ + 38.909652, + -77.035496 + ], + [ + 38.909651, + -77.035035 + ], + [ + 38.909651, + -77.03493 + ], + [ + 38.909653, + -77.034687 + ], + [ + 38.909654, + -77.034643 + ], + [ + 38.909657, + -77.034545 + ], + [ + 38.90966, + -77.034429 + ], + [ + 38.909659, + -77.033997 + ], + [ + 38.909659, + -77.033855 + ], + [ + 38.909659, + -77.032986 + ], + [ + 38.909659, + -77.032616 + ], + [ + 38.909661, + -77.032123 + ], + [ + 38.909663, + -77.032052 + ], + [ + 38.909664, + -77.031952 + ], + [ + 38.909665, + -77.031861 + ], + [ + 38.909667, + -77.031796 + ], + [ + 38.909668, + -77.031375 + ], + [ + 38.909667, + -77.030905 + ], + [ + 38.909668, + -77.030738 + ], + [ + 38.909654, + -77.030605 + ], + [ + 38.90965, + -77.030566 + ], + [ + 38.909643, + -77.030528 + ], + [ + 38.90963, + -77.030492 + ], + [ + 38.909607, + -77.030443 + ], + [ + 38.909582, + -77.030403 + ], + [ + 38.90953, + -77.030321 + ], + [ + 38.909488, + -77.030313 + ], + [ + 38.909453, + -77.030302 + ], + [ + 38.909428, + -77.03029 + ], + [ + 38.909392, + -77.03027 + ], + [ + 38.909361, + -77.030248 + ], + [ + 38.90932, + -77.030212 + ], + [ + 38.90929, + -77.030186 + ], + [ + 38.909216, + -77.029998 + ], + [ + 38.909183, + -77.029917 + ], + [ + 38.909164, + -77.029863 + ], + [ + 38.909148, + -77.029802 + ], + [ + 38.909138, + -77.029748 + ], + [ + 38.909136, + -77.029736 + ], + [ + 38.909128, + -77.029625 + ], + [ + 38.909129, + -77.029559 + ], + [ + 38.909135, + -77.029477 + ], + [ + 38.909145, + -77.029391 + ], + [ + 38.909156, + -77.029326 + ], + [ + 38.909169, + -77.029265 + ], + [ + 38.909205, + -77.029203 + ], + [ + 38.909244, + -77.029145 + ], + [ + 38.909284, + -77.029092 + ], + [ + 38.909328, + -77.029047 + ], + [ + 38.909378, + -77.029003 + ], + [ + 38.909425, + -77.028972 + ], + [ + 38.909538, + -77.028946 + ], + [ + 38.909582, + -77.028939 + ], + [ + 38.909608, + -77.028934 + ], + [ + 38.909634, + -77.028933 + ], + [ + 38.909671, + -77.028934 + ], + [ + 38.9097, + -77.028938 + ], + [ + 38.909713, + -77.02894 + ], + [ + 38.909729, + -77.028943 + ], + [ + 38.909789, + -77.028885 + ], + [ + 38.909813, + -77.028862 + ], + [ + 38.909837, + -77.028836 + ], + [ + 38.909857, + -77.028811 + ], + [ + 38.909871, + -77.028784 + ], + [ + 38.909888, + -77.028745 + ], + [ + 38.910077, + -77.028196 + ], + [ + 38.910111, + -77.028091 + ], + [ + 38.910161, + -77.027955 + ], + [ + 38.910302, + -77.027556 + ], + [ + 38.91043, + -77.02719 + ], + [ + 38.910482, + -77.02704 + ], + [ + 38.910533, + -77.026889 + ], + [ + 38.910615, + -77.026658 + ], + [ + 38.910688, + -77.02645 + ], + [ + 38.910809, + -77.026096 + ], + [ + 38.91085, + -77.025982 + ], + [ + 38.910851, + -77.02598 + ], + [ + 38.91088, + -77.025896 + ], + [ + 38.91114, + -77.02515 + ], + [ + 38.91125, + -77.024834 + ], + [ + 38.9115, + -77.024115 + ], + [ + 38.911553, + -77.023968 + ], + [ + 38.911583, + -77.023884 + ], + [ + 38.911603, + -77.023821 + ], + [ + 38.91172, + -77.023486 + ], + [ + 38.911907, + -77.022948 + ], + [ + 38.911908, + -77.022946 + ], + [ + 38.911945, + -77.022846 + ], + [ + 38.911958, + -77.022807 + ], + [ + 38.911959, + -77.022805 + ], + [ + 38.91209, + -77.022421 + ], + [ + 38.912114, + -77.02235 + ], + [ + 38.912174, + -77.022174 + ], + [ + 38.912217, + -77.022046 + ], + [ + 38.91226, + -77.021921 + ], + [ + 38.91231, + -77.021786 + ], + [ + 38.912602, + -77.020949 + ], + [ + 38.912607, + -77.020936 + ], + [ + 38.912806, + -77.02036 + ], + [ + 38.912817, + -77.020329 + ], + [ + 38.912917, + -77.020041 + ], + [ + 38.912972, + -77.019901 + ], + [ + 38.912985, + -77.019867 + ], + [ + 38.913026, + -77.019727 + ], + [ + 38.913304, + -77.018938 + ], + [ + 38.913336, + -77.018849 + ], + [ + 38.913478, + -77.018444 + ], + [ + 38.913517, + -77.018335 + ], + [ + 38.913555, + -77.01823 + ], + [ + 38.913623, + -77.018041 + ], + [ + 38.913677, + -77.017891 + ], + [ + 38.913721, + -77.017765 + ], + [ + 38.913759, + -77.017654 + ], + [ + 38.91379, + -77.017557 + ], + [ + 38.914032, + -77.016875 + ], + [ + 38.914075, + -77.016733 + ], + [ + 38.914051, + -77.016672 + ], + [ + 38.914027, + -77.016613 + ], + [ + 38.913851, + -77.016215 + ], + [ + 38.913798, + -77.016097 + ], + [ + 38.913695, + -77.015858 + ], + [ + 38.913665, + -77.015789 + ], + [ + 38.913541, + -77.015495 + ], + [ + 38.913408, + -77.015177 + ], + [ + 38.913342, + -77.015018 + ], + [ + 38.913011, + -77.014248 + ], + [ + 38.91301, + -77.014245 + ], + [ + 38.913001, + -77.014224 + ], + [ + 38.912942, + -77.014084 + ], + [ + 38.912758, + -77.013651 + ], + [ + 38.912732, + -77.013591 + ], + [ + 38.912732, + -77.01359 + ], + [ + 38.912731, + -77.013587 + ], + [ + 38.912684, + -77.013477 + ], + [ + 38.912617, + -77.013329 + ], + [ + 38.91261, + -77.013314 + ], + [ + 38.912475, + -77.012994 + ], + [ + 38.912195, + -77.012328 + ], + [ + 38.912164, + -77.012255 + ], + [ + 38.912122, + -77.012159 + ], + [ + 38.912081, + -77.012074 + ], + [ + 38.911936, + -77.011724 + ], + [ + 38.911919, + -77.011682 + ], + [ + 38.911907, + -77.011655 + ], + [ + 38.911906, + -77.011652 + ], + [ + 38.911793, + -77.011387 + ], + [ + 38.91174, + -77.011262 + ], + [ + 38.911736, + -77.011253 + ], + [ + 38.911735, + -77.011251 + ], + [ + 38.911697, + -77.011162 + ], + [ + 38.911613, + -77.010964 + ], + [ + 38.911575, + -77.010874 + ], + [ + 38.911574, + -77.010871 + ], + [ + 38.911503, + -77.010705 + ], + [ + 38.911457, + -77.010597 + ], + [ + 38.911427, + -77.010527 + ], + [ + 38.911416, + -77.010502 + ], + [ + 38.911287, + -77.010201 + ], + [ + 38.911249, + -77.010111 + ], + [ + 38.911239, + -77.010086 + ], + [ + 38.911141, + -77.00985 + ], + [ + 38.91113, + -77.009823 + ], + [ + 38.911059, + -77.009662 + ], + [ + 38.910879, + -77.00925 + ], + [ + 38.910872, + -77.009233 + ], + [ + 38.910806, + -77.009099 + ], + [ + 38.910704, + -77.009099 + ], + [ + 38.910679, + -77.009099 + ], + [ + 38.91038, + -77.009099 + ], + [ + 38.91, + -77.009095 + ], + [ + 38.909725, + -77.009093 + ], + [ + 38.909637, + -77.009092 + ], + [ + 38.90957, + -77.009092 + ], + [ + 38.909237, + -77.009095 + ], + [ + 38.909225, + -77.009095 + ], + [ + 38.908812, + -77.009189 + ], + [ + 38.908571, + -77.009189 + ], + [ + 38.907897, + -77.009191 + ], + [ + 38.907713, + -77.009191 + ], + [ + 38.907438, + -77.009191 + ], + [ + 38.907358, + -77.009191 + ], + [ + 38.907335, + -77.00927 + ], + [ + 38.90732, + -77.00932 + ], + [ + 38.907262, + -77.009509 + ], + [ + 38.90723, + -77.009604 + ], + [ + 38.907046, + -77.01015 + ], + [ + 38.906811, + -77.01082 + ], + [ + 38.906508, + -77.011689 + ] + ], + "route_info": { + "route_id": 0, + "coordinates": [ + [ + 38.909652, + -77.037501 + ], + [ + 38.909653, + -77.037271 + ], + [ + 38.909653, + -77.037209 + ], + [ + 38.909654, + -77.036649 + ], + [ + 38.909653, + -77.036523 + ], + [ + 38.909654, + -77.036407 + ], + [ + 38.909652, + -77.035496 + ], + [ + 38.909651, + -77.035035 + ], + [ + 38.909651, + -77.03493 + ], + [ + 38.909653, + -77.034687 + ], + [ + 38.909654, + -77.034643 + ], + [ + 38.909657, + -77.034545 + ], + [ + 38.90966, + -77.034429 + ], + [ + 38.909659, + -77.033997 + ], + [ + 38.909659, + -77.033855 + ], + [ + 38.909659, + -77.032986 + ], + [ + 38.909659, + -77.032616 + ], + [ + 38.909661, + -77.032123 + ], + [ + 38.909663, + -77.032052 + ], + [ + 38.909664, + -77.031952 + ], + [ + 38.909665, + -77.031861 + ], + [ + 38.909667, + -77.031796 + ], + [ + 38.909668, + -77.031375 + ], + [ + 38.909667, + -77.030905 + ], + [ + 38.909668, + -77.030738 + ], + [ + 38.909654, + -77.030605 + ], + [ + 38.90965, + -77.030566 + ], + [ + 38.909643, + -77.030528 + ], + [ + 38.90963, + -77.030492 + ], + [ + 38.909607, + -77.030443 + ], + [ + 38.909582, + -77.030403 + ], + [ + 38.90953, + -77.030321 + ], + [ + 38.909488, + -77.030313 + ], + [ + 38.909453, + -77.030302 + ], + [ + 38.909428, + -77.03029 + ], + [ + 38.909392, + -77.03027 + ], + [ + 38.909361, + -77.030248 + ], + [ + 38.90932, + -77.030212 + ], + [ + 38.90929, + -77.030186 + ], + [ + 38.909216, + -77.029998 + ], + [ + 38.909183, + -77.029917 + ], + [ + 38.909164, + -77.029863 + ], + [ + 38.909148, + -77.029802 + ], + [ + 38.909138, + -77.029748 + ], + [ + 38.909136, + -77.029736 + ], + [ + 38.909128, + -77.029625 + ], + [ + 38.909129, + -77.029559 + ], + [ + 38.909135, + -77.029477 + ], + [ + 38.909145, + -77.029391 + ], + [ + 38.909156, + -77.029326 + ], + [ + 38.909169, + -77.029265 + ], + [ + 38.909205, + -77.029203 + ], + [ + 38.909244, + -77.029145 + ], + [ + 38.909284, + -77.029092 + ], + [ + 38.909328, + -77.029047 + ], + [ + 38.909378, + -77.029003 + ], + [ + 38.909425, + -77.028972 + ], + [ + 38.909538, + -77.028946 + ], + [ + 38.909582, + -77.028939 + ], + [ + 38.909608, + -77.028934 + ], + [ + 38.909634, + -77.028933 + ], + [ + 38.909671, + -77.028934 + ], + [ + 38.9097, + -77.028938 + ], + [ + 38.909713, + -77.02894 + ], + [ + 38.909729, + -77.028943 + ], + [ + 38.909789, + -77.028885 + ], + [ + 38.909813, + -77.028862 + ], + [ + 38.909837, + -77.028836 + ], + [ + 38.909857, + -77.028811 + ], + [ + 38.909871, + -77.028784 + ], + [ + 38.909888, + -77.028745 + ], + [ + 38.910077, + -77.028196 + ], + [ + 38.910111, + -77.028091 + ], + [ + 38.910161, + -77.027955 + ], + [ + 38.910302, + -77.027556 + ], + [ + 38.91043, + -77.02719 + ], + [ + 38.910482, + -77.02704 + ], + [ + 38.910533, + -77.026889 + ], + [ + 38.910615, + -77.026658 + ], + [ + 38.910688, + -77.02645 + ], + [ + 38.910809, + -77.026096 + ], + [ + 38.91085, + -77.025982 + ], + [ + 38.910851, + -77.02598 + ], + [ + 38.91088, + -77.025896 + ], + [ + 38.91114, + -77.02515 + ], + [ + 38.91125, + -77.024834 + ], + [ + 38.9115, + -77.024115 + ], + [ + 38.911553, + -77.023968 + ], + [ + 38.911583, + -77.023884 + ], + [ + 38.911603, + -77.023821 + ], + [ + 38.91172, + -77.023486 + ], + [ + 38.911907, + -77.022948 + ], + [ + 38.911908, + -77.022946 + ], + [ + 38.911945, + -77.022846 + ], + [ + 38.911958, + -77.022807 + ], + [ + 38.911959, + -77.022805 + ], + [ + 38.91209, + -77.022421 + ], + [ + 38.912114, + -77.02235 + ], + [ + 38.912174, + -77.022174 + ], + [ + 38.912217, + -77.022046 + ], + [ + 38.91226, + -77.021921 + ], + [ + 38.91231, + -77.021786 + ], + [ + 38.912602, + -77.020949 + ], + [ + 38.912607, + -77.020936 + ], + [ + 38.912806, + -77.02036 + ], + [ + 38.912817, + -77.020329 + ], + [ + 38.912917, + -77.020041 + ], + [ + 38.912972, + -77.019901 + ], + [ + 38.912985, + -77.019867 + ], + [ + 38.913026, + -77.019727 + ], + [ + 38.913304, + -77.018938 + ], + [ + 38.913336, + -77.018849 + ], + [ + 38.913478, + -77.018444 + ], + [ + 38.913517, + -77.018335 + ], + [ + 38.913555, + -77.01823 + ], + [ + 38.913623, + -77.018041 + ], + [ + 38.913677, + -77.017891 + ], + [ + 38.913721, + -77.017765 + ], + [ + 38.913759, + -77.017654 + ], + [ + 38.91379, + -77.017557 + ], + [ + 38.914032, + -77.016875 + ], + [ + 38.914075, + -77.016733 + ], + [ + 38.914051, + -77.016672 + ], + [ + 38.914027, + -77.016613 + ], + [ + 38.913851, + -77.016215 + ], + [ + 38.913798, + -77.016097 + ], + [ + 38.913695, + -77.015858 + ], + [ + 38.913665, + -77.015789 + ], + [ + 38.913541, + -77.015495 + ], + [ + 38.913408, + -77.015177 + ], + [ + 38.913342, + -77.015018 + ], + [ + 38.913011, + -77.014248 + ], + [ + 38.91301, + -77.014245 + ], + [ + 38.913001, + -77.014224 + ], + [ + 38.912942, + -77.014084 + ], + [ + 38.912758, + -77.013651 + ], + [ + 38.912732, + -77.013591 + ], + [ + 38.912732, + -77.01359 + ], + [ + 38.912731, + -77.013587 + ], + [ + 38.912684, + -77.013477 + ], + [ + 38.912617, + -77.013329 + ], + [ + 38.91261, + -77.013314 + ], + [ + 38.912475, + -77.012994 + ], + [ + 38.912195, + -77.012328 + ], + [ + 38.912164, + -77.012255 + ], + [ + 38.912122, + -77.012159 + ], + [ + 38.912081, + -77.012074 + ], + [ + 38.911936, + -77.011724 + ], + [ + 38.911919, + -77.011682 + ], + [ + 38.911907, + -77.011655 + ], + [ + 38.911906, + -77.011652 + ], + [ + 38.911793, + -77.011387 + ], + [ + 38.91174, + -77.011262 + ], + [ + 38.911736, + -77.011253 + ], + [ + 38.911735, + -77.011251 + ], + [ + 38.911697, + -77.011162 + ], + [ + 38.911613, + -77.010964 + ], + [ + 38.911575, + -77.010874 + ], + [ + 38.911574, + -77.010871 + ], + [ + 38.911503, + -77.010705 + ], + [ + 38.911457, + -77.010597 + ], + [ + 38.911427, + -77.010527 + ], + [ + 38.911416, + -77.010502 + ], + [ + 38.911287, + -77.010201 + ], + [ + 38.911249, + -77.010111 + ], + [ + 38.911239, + -77.010086 + ], + [ + 38.911141, + -77.00985 + ], + [ + 38.91113, + -77.009823 + ], + [ + 38.911059, + -77.009662 + ], + [ + 38.910879, + -77.00925 + ], + [ + 38.910872, + -77.009233 + ], + [ + 38.910806, + -77.009099 + ], + [ + 38.910704, + -77.009099 + ], + [ + 38.910679, + -77.009099 + ], + [ + 38.91038, + -77.009099 + ], + [ + 38.91, + -77.009095 + ], + [ + 38.909725, + -77.009093 + ], + [ + 38.909637, + -77.009092 + ], + [ + 38.90957, + -77.009092 + ], + [ + 38.909237, + -77.009095 + ], + [ + 38.909225, + -77.009095 + ], + [ + 38.908812, + -77.009189 + ], + [ + 38.908571, + -77.009189 + ], + [ + 38.907897, + -77.009191 + ], + [ + 38.907713, + -77.009191 + ], + [ + 38.907438, + -77.009191 + ], + [ + 38.907358, + -77.009191 + ], + [ + 38.907335, + -77.00927 + ], + [ + 38.90732, + -77.00932 + ], + [ + 38.907262, + -77.009509 + ], + [ + 38.90723, + -77.009604 + ], + [ + 38.907046, + -77.01015 + ], + [ + 38.906811, + -77.01082 + ], + [ + 38.906508, + -77.011689 + ] + ], + "distance_km": 3.353476, + "duration_min": 11.193766666666667, + "geometry": { + "coordinates": [ + [ + -77.037501, + 38.909652 + ], + [ + -77.037271, + 38.909653 + ], + [ + -77.037209, + 38.909653 + ], + [ + -77.036649, + 38.909654 + ], + [ + -77.036523, + 38.909653 + ], + [ + -77.036407, + 38.909654 + ], + [ + -77.035496, + 38.909652 + ], + [ + -77.035035, + 38.909651 + ], + [ + -77.03493, + 38.909651 + ], + [ + -77.034687, + 38.909653 + ], + [ + -77.034643, + 38.909654 + ], + [ + -77.034545, + 38.909657 + ], + [ + -77.034429, + 38.90966 + ], + [ + -77.033997, + 38.909659 + ], + [ + -77.033855, + 38.909659 + ], + [ + -77.032986, + 38.909659 + ], + [ + -77.032616, + 38.909659 + ], + [ + -77.032123, + 38.909661 + ], + [ + -77.032052, + 38.909663 + ], + [ + -77.031952, + 38.909664 + ], + [ + -77.031861, + 38.909665 + ], + [ + -77.031796, + 38.909667 + ], + [ + -77.031375, + 38.909668 + ], + [ + -77.030905, + 38.909667 + ], + [ + -77.030738, + 38.909668 + ], + [ + -77.030605, + 38.909654 + ], + [ + -77.030566, + 38.90965 + ], + [ + -77.030528, + 38.909643 + ], + [ + -77.030492, + 38.90963 + ], + [ + -77.030443, + 38.909607 + ], + [ + -77.030403, + 38.909582 + ], + [ + -77.030321, + 38.90953 + ], + [ + -77.030313, + 38.909488 + ], + [ + -77.030302, + 38.909453 + ], + [ + -77.03029, + 38.909428 + ], + [ + -77.03027, + 38.909392 + ], + [ + -77.030248, + 38.909361 + ], + [ + -77.030212, + 38.90932 + ], + [ + -77.030186, + 38.90929 + ], + [ + -77.029998, + 38.909216 + ], + [ + -77.029917, + 38.909183 + ], + [ + -77.029863, + 38.909164 + ], + [ + -77.029802, + 38.909148 + ], + [ + -77.029748, + 38.909138 + ], + [ + -77.029736, + 38.909136 + ], + [ + -77.029625, + 38.909128 + ], + [ + -77.029559, + 38.909129 + ], + [ + -77.029477, + 38.909135 + ], + [ + -77.029391, + 38.909145 + ], + [ + -77.029326, + 38.909156 + ], + [ + -77.029265, + 38.909169 + ], + [ + -77.029203, + 38.909205 + ], + [ + -77.029145, + 38.909244 + ], + [ + -77.029092, + 38.909284 + ], + [ + -77.029047, + 38.909328 + ], + [ + -77.029003, + 38.909378 + ], + [ + -77.028972, + 38.909425 + ], + [ + -77.028946, + 38.909538 + ], + [ + -77.028939, + 38.909582 + ], + [ + -77.028934, + 38.909608 + ], + [ + -77.028933, + 38.909634 + ], + [ + -77.028934, + 38.909671 + ], + [ + -77.028938, + 38.9097 + ], + [ + -77.02894, + 38.909713 + ], + [ + -77.028943, + 38.909729 + ], + [ + -77.028885, + 38.909789 + ], + [ + -77.028862, + 38.909813 + ], + [ + -77.028836, + 38.909837 + ], + [ + -77.028811, + 38.909857 + ], + [ + -77.028784, + 38.909871 + ], + [ + -77.028745, + 38.909888 + ], + [ + -77.028196, + 38.910077 + ], + [ + -77.028091, + 38.910111 + ], + [ + -77.027955, + 38.910161 + ], + [ + -77.027556, + 38.910302 + ], + [ + -77.02719, + 38.91043 + ], + [ + -77.02704, + 38.910482 + ], + [ + -77.026889, + 38.910533 + ], + [ + -77.026658, + 38.910615 + ], + [ + -77.02645, + 38.910688 + ], + [ + -77.026096, + 38.910809 + ], + [ + -77.025982, + 38.91085 + ], + [ + -77.02598, + 38.910851 + ], + [ + -77.025896, + 38.91088 + ], + [ + -77.02515, + 38.91114 + ], + [ + -77.024834, + 38.91125 + ], + [ + -77.024115, + 38.9115 + ], + [ + -77.023968, + 38.911553 + ], + [ + -77.023884, + 38.911583 + ], + [ + -77.023821, + 38.911603 + ], + [ + -77.023486, + 38.91172 + ], + [ + -77.022948, + 38.911907 + ], + [ + -77.022946, + 38.911908 + ], + [ + -77.022846, + 38.911945 + ], + [ + -77.022807, + 38.911958 + ], + [ + -77.022805, + 38.911959 + ], + [ + -77.022421, + 38.91209 + ], + [ + -77.02235, + 38.912114 + ], + [ + -77.022174, + 38.912174 + ], + [ + -77.022046, + 38.912217 + ], + [ + -77.021921, + 38.91226 + ], + [ + -77.021786, + 38.91231 + ], + [ + -77.020949, + 38.912602 + ], + [ + -77.020936, + 38.912607 + ], + [ + -77.02036, + 38.912806 + ], + [ + -77.020329, + 38.912817 + ], + [ + -77.020041, + 38.912917 + ], + [ + -77.019901, + 38.912972 + ], + [ + -77.019867, + 38.912985 + ], + [ + -77.019727, + 38.913026 + ], + [ + -77.018938, + 38.913304 + ], + [ + -77.018849, + 38.913336 + ], + [ + -77.018444, + 38.913478 + ], + [ + -77.018335, + 38.913517 + ], + [ + -77.01823, + 38.913555 + ], + [ + -77.018041, + 38.913623 + ], + [ + -77.017891, + 38.913677 + ], + [ + -77.017765, + 38.913721 + ], + [ + -77.017654, + 38.913759 + ], + [ + -77.017557, + 38.91379 + ], + [ + -77.016875, + 38.914032 + ], + [ + -77.016733, + 38.914075 + ], + [ + -77.016672, + 38.914051 + ], + [ + -77.016613, + 38.914027 + ], + [ + -77.016215, + 38.913851 + ], + [ + -77.016097, + 38.913798 + ], + [ + -77.015858, + 38.913695 + ], + [ + -77.015789, + 38.913665 + ], + [ + -77.015495, + 38.913541 + ], + [ + -77.015177, + 38.913408 + ], + [ + -77.015018, + 38.913342 + ], + [ + -77.014248, + 38.913011 + ], + [ + -77.014245, + 38.91301 + ], + [ + -77.014224, + 38.913001 + ], + [ + -77.014084, + 38.912942 + ], + [ + -77.013651, + 38.912758 + ], + [ + -77.013591, + 38.912732 + ], + [ + -77.01359, + 38.912732 + ], + [ + -77.013587, + 38.912731 + ], + [ + -77.013477, + 38.912684 + ], + [ + -77.013329, + 38.912617 + ], + [ + -77.013314, + 38.91261 + ], + [ + -77.012994, + 38.912475 + ], + [ + -77.012328, + 38.912195 + ], + [ + -77.012255, + 38.912164 + ], + [ + -77.012159, + 38.912122 + ], + [ + -77.012074, + 38.912081 + ], + [ + -77.011724, + 38.911936 + ], + [ + -77.011682, + 38.911919 + ], + [ + -77.011655, + 38.911907 + ], + [ + -77.011652, + 38.911906 + ], + [ + -77.011387, + 38.911793 + ], + [ + -77.011262, + 38.91174 + ], + [ + -77.011253, + 38.911736 + ], + [ + -77.011251, + 38.911735 + ], + [ + -77.011162, + 38.911697 + ], + [ + -77.010964, + 38.911613 + ], + [ + -77.010874, + 38.911575 + ], + [ + -77.010871, + 38.911574 + ], + [ + -77.010705, + 38.911503 + ], + [ + -77.010597, + 38.911457 + ], + [ + -77.010527, + 38.911427 + ], + [ + -77.010502, + 38.911416 + ], + [ + -77.010201, + 38.911287 + ], + [ + -77.010111, + 38.911249 + ], + [ + -77.010086, + 38.911239 + ], + [ + -77.00985, + 38.911141 + ], + [ + -77.009823, + 38.91113 + ], + [ + -77.009662, + 38.911059 + ], + [ + -77.00925, + 38.910879 + ], + [ + -77.009233, + 38.910872 + ], + [ + -77.009099, + 38.910806 + ], + [ + -77.009099, + 38.910704 + ], + [ + -77.009099, + 38.910679 + ], + [ + -77.009099, + 38.91038 + ], + [ + -77.009095, + 38.91 + ], + [ + -77.009093, + 38.909725 + ], + [ + -77.009092, + 38.909637 + ], + [ + -77.009092, + 38.90957 + ], + [ + -77.009095, + 38.909237 + ], + [ + -77.009095, + 38.909225 + ], + [ + -77.009189, + 38.908812 + ], + [ + -77.009189, + 38.908571 + ], + [ + -77.009191, + 38.907897 + ], + [ + -77.009191, + 38.907713 + ], + [ + -77.009191, + 38.907438 + ], + [ + -77.009191, + 38.907358 + ], + [ + -77.00927, + 38.907335 + ], + [ + -77.00932, + 38.90732 + ], + [ + -77.009509, + 38.907262 + ], + [ + -77.009604, + 38.90723 + ], + [ + -77.01015, + 38.907046 + ], + [ + -77.01082, + 38.906811 + ], + [ + -77.011689, + 38.906508 + ] + ], + "type": "LineString" + } + }, + "safety_summary": { + "total_crashes": 3685, + "average_safety_score": 473.48454545454547, + "max_danger_score": 1096.7499999999998 + } +} \ No newline at end of file