# Project export: UrbanResponse AI

This document was generated by HackStack to give an AI agent context about a hackathon project. Sections are labeled with their provenance; content marked as truncated was cut to keep this document small.

## Project metadata

- Hackathon: UC Berkeley AI Hackathon 2025
- Tagline: UrbanResponse AI simulates first-responder actions in real-time to help cities plan, prepare, and protect!
- Devpost: https://devpost.com/software/urbanresponse-ai
- GitHub: https://github.com/tpradosh/UrbanResponseAi
- Team: 4 GitHub contributor(s) — MarcusHsieh (16 commits), Pradosh Thirunavukkarasu (10 commits), Arjun Supramaniam (1 commits), Copilot (1 commits)

## Devpost submission (written by the team)

### Inspiration

Emergency response coordination is a critical challenge that affects every community. During the Southern California wildfires earlier this year and recent campus emergencies at UC Berkeley, we witnessed firsthand how fragmented communication and slow decision-making can cost precious time when lives are at stake. We were inspired by the potential to combine cutting-edge AI with immersive 3D visualization to transform how cities respond to emergencies. Our team envisioned a future where AI agents could instantly analyze disaster scenarios, optimize resource allocation, and coordinate multi-agency responses; all while emergency managers watch it unfold in stunning photorealistic 3D.

### What it does

AI-Powered Decision Making: Google Gemini LLM analyzes emergency events in real-time, considering severity, location, resource availability, and Berkeley-specific factors to make intelligent dispatch decisions. Immersive 3D Visualization: Using Google to render Berkeley's buildings and terrain with stunning detail, allowing emergency managers to see exactly where events occur and track vehicle movements in real-time. Multi-Agent Coordination: CrewAI agents work as a specialized team - a Triage Agent assesses threats, Resource Allocation Agent optimizes vehicle deployment, and Dispatch Agent coordinates responses. Hardware Integration: ESP32 touch-screen interfaces deployed throughout Berkeley allow field personnel to trigger emergencies instantly, bridging the physical-digital gap. Smart Vehicle Dispatch: The system automatically selects optimal emergency vehicles (ambulances, fire trucks, police) and calculates realistic routes using Google Maps' emergency routing.

### How we built it

Frontend Architecture: JavaScript ES6+ with Google Maps JavaScript API v3.55 Google 3D Maps API (maps3d) for photorealistic visualization AI Backend: Python FastAPI serving the core intelligence system Google Gemini 2.0 Flash as the primary LLM for emergency analysis CrewAI v0.28.8 for multi-agent coordination and workflow Fetch.ai uAgents for decentralized agent communication Hardware Layer: ESP32 microcontrollers with 4.0" TFT touch displays

### Challenges we ran into

One issue we faced early on was synchronizing Google Maps routing with vehicle movement animations while maintaining realistic speeds. Handling route calculation failures also proved technically challenging. This was a core functionality for simulating emergency responses in real time. Similarly, ensuring that the Gemini LLM provided consistent and logical emergency responses across different event types, severities, and sources required extensive prompt engineering and testing. Finally, balancing CrewAI's powerful capabilities with real-time performance requirements meant developing both full and simplified agent versions that could adapt dynamically to system load.

### Accomplishments we're proud of

Given it was our first time using Agentic AI, we were able to adapt quickly while exposing ourselves to new technologies. We persevered through numerous challenges and created a system that has a real-world impact on a lot of people. In addition, we achieved sub-5-second emergency event processing from hardware trigger to AI analysis to vehicle dispatch visualization, which simulates the real-life effectiveness.

### What we learned

Through this project, we learned how to bring together diverse technologies, AI, real-time simulation, embedded hardware, and geospatial data, into a unified, mission-critical system. We developed a deeper appreciation for designing user-centric tools that are both intelligent and responsive under pressure. Working with large language models and agent-based systems taught us the importance of adaptability, prompt design, and clear coordination. Above all, we learned how to turn a bold idea into a tangible, impactful solution through collaboration, iteration, and resilience.

### What's next

Multi-City Expansion: Extend beyond Berkeley to San Francisco, Oakland, and eventually any city with emergency response needs. Hospital Integration: Connect with Berkeley's hospital systems for real-time bed availability and specialized care routing. Public Alert System: Add civilian notification capabilities with evacuation routing and safety instructions. Multi-Agent Decision Making Process

## README (from the GitHub repository)

# UrbanResponse AI - Emergency Response Simulation

> 3D Emergency Response Simulation for Berkeley, CA using Google Maps JavaScript API

## 🌟 Overview

UrbanResponse AI is an interactive 3D emergency response simulation that demonstrates intelligent emergency vehicle dispatching and routing in Berkeley, California. This implementation focuses on the visualization and simulation components from the larger UrbanResponse AI project.

### Key Features

- **🗺️ 3D Interactive Map**: Powered by Google Maps JavaScript API with photorealistic 3D buildings
- **🔥 Drag & Drop Events**: Create emergency events by dragging icons onto the map
- **🚑 Smart Vehicle Dispatching**: Automatic selection of nearest appropriate emergency vehicles
- **🛣️ Intelligent Routing**: Real-time route calculation with Google Maps Directions API
- **⚡ Smooth Animations**: Realistic vehicle movement animations along calculated routes
- **📍 Real Berkeley Data**: Actual emergency station locations and service areas
- **🎛️ Live Control Panel**: Real-time monitoring and control of emergency responses

## 🚀 Quick Start

### Prerequisites

1. **Google Maps API Key** - Get one from [Google Cloud Console](https://console.cloud.google.com/)
2. **Modern Web Browser** - Chrome, Firefox, Safari, or Edge
3. **Local Web Server** - For CORS compliance (see setup options below)

### Setup Instructions

1. **Clone/Download the Project**
   ```bash
   git clone <repository-url>
   cd Berk2025
   ```

2. **Configure Google Maps API Key**
   
   Open `index.html` and replace `YOUR_API_KEY` with your actual API key:
   ```html
   <script src="https://maps.googleapis.com/maps/api/js?key=YOUR_ACTUAL_API_KEY&libraries=geometry&callback=initApp&v=3.55" async defer></script>
   ```

   Also update `config.js`:
   ```javascript
   GOOGLE_MAPS_API_KEY: "YOUR_ACTUAL_API_KEY",
   ```

3. **Set Up Local Web Server**

   Choose one of these options:

   **Option A: Python (if installed)**
   ```bash
   # Python 3
   python -m http.server 8000
   
   # Python 2
   python -m SimpleHTTPServer 8000
   ```

   **Option B: Node.js (if installed)**
   ```bash
   npx http-server -p 8000
   ```

   **Option C: PHP (if installed)**
   ```bash
   php -S localhost:8000
   ```

   **Option D: Live Server Extension (VS Code)**
   - Install "Live Server" extension
   - Right-click `index.html` → "Open with Live Server"

4. **Open Application**
   
   Navigate to: `http://localhost:8000`

## 🎮 How to Use

### Creating Emergency Events

1. **Drag & Drop Method**:
   - Select an emergency type from the sidebar (🔥 Fire, 🚑 Medical, 🚓 Police, 🚗 Accident)
   - Drag the icon onto the map where you want the emergency
   - Drop to create the event

2. **Event Information**:
   - Click on any event marker to see details
   - Use "Dispatch Emergency Vehicles" to send response teams

### Managing Vehicles

- **View Status**: Check vehicle availability in the sidebar
- **Manual Reset**: Click "Reset Vehicles" to return all vehicles to stations
- **Monitor Movement**: Watch real-time vehicle animations on the map

### Keyboard Shortcuts

- `Ctrl + C`: Clear all events
- `Ctrl + R`: Reset all vehicles
- `Escape`: Close event information panel

### Map Controls

- **Pan**: Click and drag
- **Zoom**: Mouse wheel or zoom controls
- **3D View**: Automatic 3D buildings in supported areas
- **Station Info**: Click on station markers for details

## 🏗️ Architecture

### Core Components

1. **MapManager** (`js/mapManager.js`)
   - Google Maps 3D integration
   - Marker management
   - Coordinate conversion

2. **EventManager** (`js/eventManager.js`)
   - Emergency event creation and tracking
   - Drag & drop functionality
   - Event lifecycle management

3. **VehicleManager** (`js/vehicleManager.js`)
   - Emergency vehicle dispatching
   - Animation and movement
   - Status tracking

4. **RoutingEngine** (`js/routingEngine.js`)
   - Google Maps Directions API integration
   - Route calculation and caching
   - Fallback routing

### Data Structure

- **Emergency Stations**: Real Berkeley fire stations, hospitals, and police stations
- **Vehicle Fleet**: Ambulances, fire trucks, and police vehicles with realistic capabilities
- **Event Types**: Fire, medical, police, and traffic accidents with appropriate response protocols

## 🎯 Berkeley-Specific Features

### Real Emergency Stations

- **Fire Stations**: 5 actual Berkeley Fire Department locations
- **Hospitals**: Alta Bates Summit, UC Health Services, Paramedic stations
- **Police**: Berkeley PD headquarters, UC Police, patrol stations

### Coverage Areas

- **Downtown Berkeley**: High-density urban response
- **UC Berkeley Campus**: Specialized campus emergency services
- **Berkeley Hills**: Wildfire and residential emergency response
- **Berkeley Marina**: Marine and waterfront emergencies

### Realistic Response Times

- **Ambulance**: 4-8 minute average response
- **Fire Trucks**: 5-10 minute average response  
- **Police**: 3-7 minute average response

## ⚙️ Configuration

### Map Settings (`config.js`)

```javascript
MAP_CONFIG: {
    center: { lat: 37.8715, lng: -122.2730 }, // Berkeley center
    zoom: 16,
    mapId: "YOUR_MAP_ID", // For 3D features
    tilt: 45,
    heading: 0
}
```

### Event Types

```javascript
EVENT_CONFIG: {
    eventTypes: {
        fire: { priority: 1, requiredVehicles: ["fire_truck", "ambulance"] },
        medical: { priority: 2, requiredVehicles: ["ambulance"] },
        police: { priority: 3, requiredVehicles: ["police"] },
        accident: { priority: 2, requiredVehicles: ["police", "ambulance"] }
    }
}
```

### Vehicle Settings

```javascript
VEHICLE_CONFIG: {
    animationSpeed: 50, // km/h
    animationSteps: 100,
    vehicleTypes: {
        ambulance: { icon: "🚑", color: "#e67e22" },
        fire_truck: { icon: "🚒", color: "#e74c3c" },
        police: { icon: "🚓", color: "#3498db" }
    }
}
```

## Agents

- **System**: Fetch.ai + CrewAI intelligent agent system
- **Hardware**: ESP32 field sensors and triggers
- **Analytics**: Response time optimization and predictive modeling

### Integration Points

- WebSocket endpoints for agent communication
- Hardware event ingestion via serial/network
- RESTful APIs for external system integration

## 📝 Development Notes

### File Structure
```
├── index.html              # Main application entry
├── styles.css              # UI styling and responsive design
├── config.js               # System configuration
├── data/
│   └── stations.js         # Berkeley emergency stations data
└── js/
    ├── app.js              # Main application controller
    ├── mapManager.js       # Google Maps integration
    ├── eventManager.js     # Emergency event handling
    ├── vehicleManager.js   # Vehicle dispatch and animation
    └── routingEngine.js    # Route calculation
```


## Detected evidence (automated analysis)

Indexed codebase: 27 recognized source files, 565 KB.
- Anthropic (technology) — detected in the code
- C++ (language) — detected in the code
- CrewAI (technology) — detected in the code
- CSS (language) — detected in the code
- FastAPI (technology) — detected in the code
- HTML (language) — detected in the code
- JavaScript (language) — detected in the code
- Python (language) — detected in the code
- Google Gemini (technology) — claimed on Devpost, not found in the code
- AI coding agent: GitHub Copilot — evidence: commit authorship or trailers

## Codebase structure (from repository index)

### Files (40 of 40)

```
.DS_Store
.gitignore
3DMap/config.js
3DMap/esp32_events.json
3DMap/index.html
3DMap/js/app.js
3DMap/js/eventManager.js
3DMap/js/mapManager.js
3DMap/js/routingEngine.js
3DMap/js/unifiedDataAdapter.js
3DMap/js/vehicleManager.js
3DMap/styles.css
Agents/agentic_core/.gitignore
Agents/agentic_core/coordinator.py
Agents/agentic_core/disaster_response_crew.py
Agents/agentic_core/env_config.template
Agents/agentic_core/llm_integration.py
Agents/agentic_core/README.md
Agents/agentic_core/resource_manager.py
Agents/agentic_core/setup_env.py
Agents/agentic_core/test_all_components.py
Agents/agentic_core/test_berkeley_complete.py
Agents/env_template.txt
Agents/README.md
Agents/requirements.txt
backend_ai_integration.py
esp32/esp32_disaster_interface/.gitignore
esp32/esp32_disaster_interface/platformio.ini
esp32/esp32_disaster_interface/src/main.cpp
esp32/ESP32_INTEGRATION_README.md
esp32/esp32_serial_bridge.py
esp32/ESP32_TFT_Wiring_Guide.md
esp32/requirements_esp32.txt
gemini_analysis_log.txt
README.md
requirements_ai_backend.txt
run.sh
shared_routing_utils.py
start_system.sh
unified_emergency_data.json
```

### Dependencies

- Agents/requirements.txt: anthropic@==0.7.8, crewai@==0.28.8, crewai[tools]@==0.28.8, fastapi@==0.104.1, google-generativeai@==0.3.2, loguru@==0.7.2, numpy@==1.24.3, pandas@==2.1.4, pydantic@==2.5.0, python-dotenv@==1.0.0, uagents@==0.12.0, uagents[bureau]@==0.12.0, uvicorn@==0.24.0

### Recent commits (newest first)

- Final
- Working
- Draft 1 working
- Merge pull request #6 from tpradosh/MarcusHsieh/esp32
- Merge branch 'MarcusHsieh/esp32' of https://github.com/tpradosh/Berk2025 into MarcusHsieh/esp32
- Merge branch 'agent_comms' into MarcusHsieh/esp32
- Merge branch 'agent_comms' of https://github.com/tpradosh/Berk2025 into agent_comms
- Integrate ESP32
- fixed so the frontend reacts to the LLM's analysis of the situation
- Update requirements_esp32.txt
- Merge branch 'agent_comms' of https://github.com/tpradosh/Berk2025 into agent_comms
- Real-time disaster initiation on ESP-32
- added cleaner logging
- working point
- Resolve merge conflicts between agent_comms and MarcusHsieh/3Dmap branches
- moved it into 3dmap folder for merging
- cleanup
- integrated LLM ai to the 3d munger maps
- Fix
- Working base

## Key source files (fetched from GitHub, selected and truncated for size)

### esp32/ESP32_TFT_Wiring_Guide.md

```markdown
# ESP32 TFT Touch Screen Wiring Guide

## Pin Connections

### TFT Display Connections

| TFT Pin | Pin Name | ESP32 Pin (GPIO) | Description |
|---------|----------|------------------|-------------|
| 1       | VCC      | 3.3V or 5V      | Power supply for the display module |
| 2       | GND      | GND              | Ground connection |
| 3       | CS       | GPIO 5           | LCD Chip Select (can be another GPIO) |
| 4       | RESET    | GPIO 4           | LCD Reset (can be another GPIO) |
| 5       | DC/RS    | GPIO 2           | Data/Command select (can be another GPIO) |
| 6       | SDI (MOSI)| GPIO 23         | SPI Master Out Slave In (VSPI MOSI) |
| 7       | SCK      | GPIO 18          | SPI Clock (VSPI SCK) |
| 8       | LED      | 3.3V             | Backlight control (connect to 3.3V for always on) |
| 9       | SDO (MISO)| GPIO 19         | SPI Master In Slave Out (VSPI MISO) |

### Touch Screen Connections

| Touch Pin | Pin Name | ESP32 Pin (GPIO) | Description |
|-----------|----------|------------------|-------------|
| 11        | T_CLK    | GPIO 25          | Touch Clock (can be another GPIO) |
| 12        | T_CS     | GPIO 33          | Touch Chip Select (can be another GPIO) |
| 13        | T_DIN    | GPIO 32          | Touch Data In (MOSI) (can be another GPIO) |
| 14        | T_DO     | GPIO 35          | Touch Data Out (MISO) (can be another GPIO) |
| 15        | T_IRQ    | GPIO 34          | Touch Interrupt Request (can be another GPIO) |

### Serial Data Format
The ESP32 sends disaster events in this JSON format:
```json
{
  "event_id": "ESP32_12345_1640995200",
  "event_type": "FIRE",
  "severity": "HIGH",
  "timestamp": 1640995200,
  "source": "esp32_hardware",
  "injured_count": 3,
  "details": "3 injured"
}
```
```

### esp32/ESP32_INTEGRATION_README.md

```markdown
# ESP32 Disaster Interface Integration

## Quick Start

### 1. Hardware Setup
- Connect ESP32 with 4.0" TFT touch screen according to `ESP32_TFT_Wiring_Guide.md`
- Upload code from `esp32_disaster_interface/` using PlatformIO

### 2. Software Setup
```bash
# Install Python dependencies
cd esp32
pip install -r requirements_esp32.txt

# Start ESP32 serial bridge (adjust COM port as needed)
python esp32_serial_bridge.py COM8

# In a separate terminal, start web application
cd ../3DMap
python -m http.server 8000
```

### 3. Usage
- Open browser to http://localhost:8000
- Use ESP32 touch screen to trigger disasters:
  - FIRE - Red markers
  - MEDICAL - Orange markers (with injured count)
  - POLICE - Blue markers  
  - TRAFFIC - Yellow markers
- Events appear on map in real-time at a random Berkeley location

## File Organization

```
📁 Project Root/
├── 📁 esp32/                        # All ESP32 files organized here
│   ├── esp32_serial_bridge.py     # ESP32 communication bridge
│   ├── requirements_esp32.txt     # Python dependencies
│   ├── ESP32_TFT_Wiring_Guide.md  # Hardware connection guide
│   └── 📁 esp32_disaster_interface/ # Complete ESP32 PlatformIO project
│
└── 📁 3DMap/                      # Main web application (served on localhost:8000)
    ├── index.html                 # Main web application
    ├── config.js                  # Application configuration
    ├── 📁 js/                     # JavaScript modules
    │   ├── eventManager.js        # Event handling with ESP32 integration
    │   └── mapManager.js          # Map management
    └── styles.css                 # Application styles
    └── esp32_events.json          # ESP32 events file (auto-created here)
```

## ESP32 Integration Details

**Communication Flow:**
1. ESP32 → Serial → `esp32_serial_bridge.py` (in esp32/ directory)
2. Bridge → `../3DMap/esp32_events.json` (writes to web server directory)
3. Web app → fetches `/esp32_events.json` (from localhost:8000/esp32_events.json)
4. Events displayed on map in real-time

**Recent Improvements:**
- **File Organization**: All ESP32 files now in dedicated `esp32_integration/` folder
- **Path Fix**: Updated web app to fetch ESP32 events from correct location
- **Severity Normalization**: ESP32 severity values (HIGH, MED, LOW) automatically normalized to lowercase
- **Event Type Consistency**: All event types normalized for proper UI mapping
- **Duplicate Prevention**: Built-in protection against processing the same ESP32 event multiple times

## ESP32 Hardware Interface

The ESP32 touch screen interface provides:
- 4 disaster types: FIRE, MEDICAL, POLICE, TRAFFIC
- 3 severity levels: HIGH, MED, LOW (automatically normalized to lowercase)
- Injured count selection for medical emergencies (1-99)
- Real-time event transmission to web application
- JSON format: `DISASTER_EVENT:{"event_id":"ESP32_xxx","event_type":"FIRE","severity":"HIGH",...}`

```

### Agents/requirements.txt

```
# Agentic Trinity Core Dependencies

# Fetch.ai framework
uagents==0.12.0
uagents[bureau]==0.12.0

# CrewAI multi-agent framework
crewai==0.28.8
crewai[tools]==0.28.8

# LLM integrations
google-generativeai==0.3.2
anthropic==0.7.8

# Utility libraries
pydantic==2.5.0
python-dotenv==1.0.0
loguru==0.7.2
fastapi==0.104.1
uvicorn==0.24.0

# Data handling
pandas==2.1.4
numpy==1.24.3 
```

### 3DMap/js/app.js

```javascript
// UrbanResponse AI - Main Application
// Initializes and coordinates all system components

class UrbanResponseApp {
    constructor() {
        this.initialized = false;
        this.components = {};
        this.agentSystem = null;
        
        // Performance monitoring
        this.performanceMetrics = {
            startTime: new Date(),
            eventsCreated: 0,
            vehiclesDispatched: 0,
            eventsResolved: 0
        };
        
        // Bind the global callback for Google Maps API
        window.initApp = this.init.bind(this);
    }

    // Initialize the application
    async init() {
        try {
            console.log('🚀 Initializing UrbanResponse AI System...');
            
                    // Initialize unified data first
        await this.initializeUnifiedData();
        
        // Initialize components in order
        await this.initializeMapManager();
        this.initializeEventManager();
        this.initializeVehicleManager();
        this.initializeRoutingEngine();

        // Initialize settings panel
        this.initializeSettingsPanel();
            
            // Setup integration hooks
            this.setupAgentIntegration();
            
            // Add vehicle markers to map
            this.components.vehicleManager.addVehicleMarkersToMap();
            
            // Setup keyboard shortcuts
            this.setupKeyboardShortcuts();
            
            // Setup UI event listeners
            this.setupUIEventListeners();
            
            // Start health monitoring
            this.startHealthMonitoring();
            
            // Mark as initialized
            this.initialized = true;
            
            console.log('✅ UrbanResponse AI System initialized successfully');
            console.log('📊 System Status:', this.getSystemStatus());
            
            // Run initial system test - COMMENTED OUT TO REMOVE AUTOMATIC INITIAL EVENT
            // setTimeout(() => {
            //     this.runSystemTest();
            // }, 2000);
            
        } catch (error) {
            console.error('❌ System initialization failed:', error);
            throw error;
        }
    }

    // Initialize Unified Data
    async initializeUnifiedData() {
        console.log('📊 Initializing Unified Emergency Data...');
        
        // Load unified data and set up legacy compatibility
        const success = await window.loadUnifiedData();
        if (!success) {
            throw new Error('Failed to load unified emergency data');
        }
        
        console.log('✅ Unified Emergency Data initialized');
    }

    // Initialize Map Manager
    async initializeMapManager() {
        console.log('🗺️ Initializing Map Manager...');
        this.components.mapManager = new MapManager();
        window.mapManager = this.components.mapManager;
        
        await this.components.mapManager.initializeMap();
        console.log('✅ Map Manager initialized');
    }

    // Initialize Event Manager
    initializeEventManager() {
        console.log('📋 Initializing Event Manager...');
        this.components.eventManager = new EventManager();
        window.eventManager = this.components.eventManager;
        console.log('✅ Event Manager initialized');
    }

    // Initialize Vehicle Manager
    initializeVehicleManager() {
        console.log('🚗 Initializing Vehicle Manager...');
        this.components.vehicleManager = new VehicleManager();
        window.vehicleManager = this.components.vehicleManager;
        console.log('✅ Vehicle Manager initialized');
    }

    // Initialize Routing Engine
    initializeRoutingEngine() {
        console.log('🛣️ Initializing Routing Engine...');
        this.components.routingEngine = new RoutingEngine();
        window.routingEngine = this.components.routingEngine;
        console.log('✅ Routing Engine initialized');
    }

    // Initialize settings panel and controls
    initializeSettingsPanel() {
        console.log('🔧 Initializing settings panel...');
        
        // Settings button click handler
        const settingsBtn = document.getElementById('settingsBtn');
        if (settingsBtn) {
            settingsBtn.addEventListener('click', () => {
                this.toggleSettingsPanel();
            });
        }

        // Close settings panel handlers
        const closeSettings = document.getElementById('closeSettings');
        if (closeSettings) {
            closeSettings.addEventListener('click', () => {
                this.hideSettingsPanel();
            });
        }

        // Click outside panel to close
        const settingsPanel = document.getElementById('settingsPanel');
        if (settingsPanel) {
            settingsPanel.addEventListener('click', (e) => {
                if (e.target === settingsPanel) {
                    this.hideSettingsPanel();
                }
            });
        }

        // Tickrate slider functionality
        const tickrateSlider = document.getElementById('tickrateSlider');
        const tickrateValue = document.getElementById('tickrateValue');
        
        if (tickrateSlider && tickrateValue) {
            tickrateSlider.addEventListener('input', (e) => {
                const value = parseFloat(e.target.value);
                tickrateValue.textContent = `${value.toFixed(1)}x`;
                
                // Update global tickrate multiplier
                if (window.CONFIG && CONFIG.VEHICLE_CONFIG) {
                    CONFIG.VEHICLE_CONFIG.tickrateMultiplier = value;
                    console.log(`⚡ Tickrate multiplier updated to ${value}x`);
                }
            });
            
            // Initialize display
            tickrateValue.textContent = `${tickrateSlider.value}x`;
        }

        // Move system control event handlers to settings panel
        this.initializeSystemControls();
    }

    // Initialize system controls (moved from sidebar)
    in
[truncated — 18758 more characters]
```

### run.sh

```shell
#!/bin/bash

# Simple wrapper to start the full UrbanResponse AI system
echo "🚀 Starting UrbanResponse AI System..."
exec ./start_system.sh 
```

### start_system.sh

```shell
#!/bin/bash

echo "🚀 Starting UrbanResponse AI System..."

# Kill any existing processes on port 8000
echo "🔫 Cleaning up existing processes..."
lsof -ti:8000 | xargs kill -9 2>/dev/null || echo "   No existing processes found on port 8000"
pkill -f "esp32_serial_bridge.py" 2>/dev/null || echo "   No existing ESP32 bridge processes found"

# Wait for cleanup
sleep 2

# Clear ESP32 events file
echo "🧹 Clearing ESP32 events..."
echo "[]" > 3DMap/esp32_events.json
echo "   ESP32 events cleared"

# Check if ESP32 bridge exists and start it
if [ -f "esp32/esp32_serial_bridge.py" ]; then
    echo "🔌 Starting ESP32 Serial Bridge..."
    cd esp32
    nohup python esp32_serial_bridge.py COM8 > ../esp32_bridge.log 2>&1 &
    ESP32_PID=$!
    cd ..
    sleep 2
    
    if ps -p $ESP32_PID > /dev/null; then
        echo "✅ ESP32 Bridge started (PID: $ESP32_PID)"
    else
        echo "⚠️ ESP32 Bridge failed to start - check esp32_bridge.log"
    fi
else
    echo "⚠️ ESP32 bridge not found - web interface only"
fi

echo ""
echo "🤖 Starting AI Backend Integration..."
echo "📊 Access: http://localhost:8000"
echo "🎛️ Press Ctrl+C to stop"
echo ""

# Cleanup function
cleanup() {
    echo ""
    echo "🛑 Shutting down system..."
    if [ ! -z "$ESP32_PID" ] && ps -p $ESP32_PID > /dev/null; then
        kill $ESP32_PID 2>/dev/null
    fi
    echo "✅ System stopped"
    exit 0
}

trap cleanup SIGINT SIGTERM

# Run the AI backend integration (this serves the web app and handles all AI)
python backend_ai_integration.py 
```

### shared_routing_utils.py

```python
"""
Shared Routing and Distance Utilities
Used by both frontend (via API) and backend for consistent calculations
"""

import math
from typing import Dict, Tuple, Optional, List
from dataclasses import dataclass

@dataclass
class RouteResult:
    """Result of a route calculation"""
    distance_km: float
    duration_minutes: float
    eta_minutes: float
    path_points: Optional[List[Dict]] = None
    method: str = "haversine"

class SharedRoutingUtils:
    """Shared utilities for routing and distance calculations"""
    
    # Emergency vehicle speeds (km/h) - consistent with frontend CONFIG
    EMERGENCY_SPEEDS = {
        'ambulance': 60,      # Ambulances in urban areas
        'fire_truck': 55,     # Fire trucks (heavier, slower acceleration)  
        'police': 65,         # Police vehicles (faster)
        'default': 60
    }
    
    # Speed factors for different scenarios
    SPEED_FACTORS = {
        'urban': 0.8,         # Urban area factor (traffic, lights)
        'emergency': 0.9,     # Emergency response factor
        'short_distance': 0.7,    # < 1km (more intersections)
        'medium_distance': 0.85,  # 1-3km (some delays)
        'long_distance': 0.9      # > 3km (highway access)
    }
    
    @staticmethod
    def calculate_haversine_distance(loc1: Dict, loc2: Dict) -> float:
        """
        Calculate distance between two locations using Haversine formula
        Compatible with both frontend and backend location formats
        """
        lat1 = loc1['lat']
        lon1 = loc1.get('lon') or loc1.get('lng')  # Handle both formats
        lat2 = loc2['lat'] 
        lon2 = loc2.get('lon') or loc2.get('lng')  # Handle both formats
        
        if lon1 is None or lon2 is None:
            raise ValueError("Location must have 'lon' or 'lng' coordinate")
        
        # Convert to radians
        lat1, lon1, lat2, lon2 = map(math.radians, [lat1, lon1, lat2, lon2])
        
        # Haversine formula
        dlat = lat2 - lat1
        dlon = lon2 - lon1
        a = math.sin(dlat/2)**2 + math.cos(lat1) * math.cos(lat2) * math.sin(dlon/2)**2
        c = 2 * math.asin(math.sqrt(a))
        
        # Earth's radius in kilometers
        r = 6371
        return r * c
    
    @staticmethod
    def calculate_emergency_eta(origin: Dict, destination: Dict, 
                              vehicle_type: str = 'ambulance',
                              include_preparation_time: bool = True) -> RouteResult:
        """
        Calculate ETA for emergency vehicles using enhanced logic
        Consistent with frontend routing calculations
        """
        distance_km = SharedRoutingUtils.calculate_haversine_distance(origin, destination)
        
        # Get base speed for vehicle type
        base_speed = SharedRoutingUtils.EMERGENCY_SPEEDS.get(
            vehicle_type, SharedRoutingUtils.EMERGENCY_SPEEDS['default']
        )
        
        # Apply distance-based speed factors
        if distance_km < 1.0:
            distance_factor = SharedRoutingUtils.SPEED_FACTORS['short_distance']
        elif distance_km < 3.0:
            distance_factor = SharedRoutingUtils.SPEED_FACTORS['medium_distance']
        else:
            distance_factor = SharedRoutingUtils.SPEED_FACTORS['long_distance']
        
        # Apply area and emergency factors
        urban_factor = SharedRoutingUtils.SPEED_FACTORS['urban']
        emergency_factor = SharedRoutingUtils.SPEED_FACTORS['emergency']
        
        # Calculate effective speed
        effective_speed = base_speed * distance_factor * urban_factor * emergency_factor
        
        # Calculate travel time
        travel_time_hours = distance_km / effective_speed
        travel_time_minutes = travel_time_hours * 60
        
        # Add preparation time if requested
        preparation_time = 1.5 if include_preparation_time else 0  # 1.5 minutes
        
        # Calculate total ETA
        total_eta = max(1.0, travel_time_minutes + preparation_time)  # Minimum 1 minute
        
        return RouteResult(
            distance_km=round(distance_km, 2),
            duration_minutes=round(travel_time_minutes, 1),
            eta_minutes=round(total_eta, 1),
            method="haversine_enhanced"
        )
    
    @staticmethod
    def find_nearest_units(target_location: Dict, available_units: List[Dict], 
                          max_count: int = 3) -> List[Tuple[Dict, float]]:
        """
        Find nearest available units to a target location
        Returns list of (unit, distance) tuples sorted by distance
        """
        units_with_distance = []
        
        for unit in available_units:
            if unit.get('status') == 'available':
                distance = SharedRoutingUtils.calculate_haversine_distance(
                    target_location, unit['location']
                )
                units_with_distance.append((unit, distance))
        
        # Sort by distance and return top N
        units_with_distance.sort(key=lambda x: x[1])
        return units_with_distance[:max_count]
    
    @staticmethod
    def calculate_response_score(unit: Dict, target_location: Dict, 
                               required_capabilities: List[str] = None) -> float:
        """
        Calculate a response score for a unit based on distance, capabilities, and availability
        Higher score = better choice
        """
        # Base score starts at 100
        score = 100.0
        
        # Distance penalty (closer is better)
        distance = SharedRoutingUtils.calculate_haversine_distance(
            target_location, unit['location']
        )
        distance_penalty = distance * 10  # 10 points per km
        score -= distance_penalty
        
        # Availability bonus
        if unit.get('status') == 'available':
            score += 50
        elif unit.get('status') == 'dispatched':
            score -= 30
        else:
            score -= 50
        
        # Capability matching bonus
     
[truncated — 5754 more characters]
```

### 3DMap/config.js

```javascript
// UrbanResponse AI - Configuration File
// 
// MAP TILES API USAGE GUIDE:
// 
// 1. PHOTOREALISTIC 3D TILES ✅ REQUIRED
//    - Automatically included with 'maps3d' library
//    - Provides 3D buildings, terrain, and photorealistic rendering
//    - Essential for true 3D emergency response visualization
//    - Billing: Charged per 1000 map loads with 3D enabled
//
// 2. STREET VIEW TILES 🔧 OPTIONAL
//    - Only needed if you want street-level views of emergency scenes
//    - Useful for detailed location assessment
//    - Requires separate Street View API integration
//    - Billing: Charged per Street View panorama request
//
// 3. VECTOR TILES 📊 INCLUDED
//    - Standard map tiles for 2D rendering
//    - Included with basic Maps JavaScript API
//    - Used for fallback when 3D is not available
//
// CURRENT SETUP: Using Photorealistic 3D Tiles via maps3d library
// STREET VIEW: Disabled (can be enabled by setting enableStreetView: true)

const CONFIG = {
    // Berkeley, CA coordinates and bounds
    BERKELEY_CENTER: {
        lat: 37.8715,
        lng: -122.2730
    },
    
    BERKELEY_BOUNDS: {
        north: 37.8965,
        south: 37.8465,
        east: -122.2430,
        west: -122.3030
    },

    // Google Maps Configuration with 3D Photorealistic Tiles
    GOOGLE_MAPS: {
        apiKey: "AIzaSyCrCLL6KjtNtUIuMQSx0yClrcEqK_sAUyg",
        mapId: "DEMO_MAP_ID", // Default map ID for 3D maps - replace with your actual Map ID
        libraries: ["geometry", "maps3d"], // maps3d includes Photorealistic 3D Tiles
        version: "3.55",
        loading: "async"
    },

    MAP_CONFIG: {
        center: { lat: 37.8715, lng: -122.2730 },
        zoom: 16,
        heading: 0,
        tilt: 0,
        mapTypeId: "roadmap",
        gestureHandling: "greedy",
        disableDefaultUI: false,
        zoomControl: true,
        streetViewControl: false, // Set to true if you want Street View integration
        fullscreenControl: true,
        styles: [] // Custom map styling
    },

    // 3D Map specific settings with Photorealistic Tiles
    MAP_3D_CONFIG: {
        center: { lat: 37.8715, lng: -122.2730, altitude: 200 },
        range: 1000,
        heading: 0,
        tilt: 60,
        roll: 0,
        // Photorealistic 3D Tiles are automatically enabled with maps3d library
        enablePhotorealistic3D: true,
        // Street View integration (optional)
        enableStreetView: false
    },

    // Event Configuration
    EVENT_CONFIG: {
        maxActiveEvents: 10,
        eventTimeout: 300000, // 5 minutes in milliseconds
        eventTypes: {
            fire: {
                name: "Fire Emergency",
                icon: "🔥",
                markerIcon: "🏠🔥", // Distinct marker for map
                color: "#e74c3c",
                priority: 1,
                requiredVehicles: ["fire_truck", "ambulance"],
                resolutionTime: 15000 // 15 seconds after both vehicles arrive
            },
            medical: {
                name: "Medical Emergency", 
                icon: "🚑",
                markerIcon: "⚕️", // Distinct marker for map
                color: "#e67e22",
                priority: 2,
                requiredVehicles: ["ambulance"],
                resolutionTime: 10000 // 10 seconds after vehicle arrives
            },
            police: {
                name: "Police Emergency",
                icon: "🚓", 
                markerIcon: "⚠️", // Distinct marker for map
                color: "#3498db",
                priority: 3,
                requiredVehicles: ["police"],
                resolutionTime: 12000 // 12 seconds after vehicle arrives
            },
            accident: {
                name: "Traffic Accident",
                icon: "🚗",
                markerIcon: "💥", // Distinct marker for map
                color: "#f39c12",
                priority: 2,
                requiredVehicles: ["police", "ambulance"],
                resolutionTime: 18000 // 18 seconds after both vehicles arrive
            },
            traffic: {
                name: "Traffic Incident",
                icon: "🚦",
                markerIcon: "🚧", // Distinct marker for map
                color: "#f1c40f",
                priority: 2,
                requiredVehicles: ["police"],
                resolutionTime: 12000 // 12 seconds after vehicle arrives
            }
        }
    },

    // Vehicle Configuration
    VEHICLE_CONFIG: {
        animationSpeed: 45, // km/h - Realistic emergency vehicle average speed in urban areas
        realWorldSpeedKmh: 45, // Real average emergency vehicle speed (considering traffic, stops, etc)
        animationSteps: 1000,
        tickrateMultiplier: 1.0, // Global simulation speed multiplier
        vehicleTypes: {
            ambulance: {
                icon: "🚑",
                markerIcon: "🚑", // Distinct marker for map
                color: "#e67e22",
                size: 20, // Reduced from 24
                stationIcon: "🏥",
                averageSpeedKmh: 42 // Ambulances slightly slower due to patient care
            },
            fire_truck: {
                icon: "🚒",
                markerIcon: "🚒", // Distinct marker for map
                color: "#e74c3c", 
                size: 22, // Reduced from 26
                stationIcon: "🚒",
                averageSpeedKmh: 48 // Fire trucks can move faster with larger equipment
            },
            police: {
                icon: "🚓",
                markerIcon: "🚔", // Distinct marker for map (different from draggable)
                color: "#3498db",
                size: 18, // Reduced from 22
                stationIcon: "🚓",
                averageSpeedKmh: 50 // Police vehicles typically fastest response
            }
        }
    },

    // Routing Configuration
    ROUTING_CONFIG: {
        routingService: "google", // or "manual" for simple straight-line routing
        avoidTolls: true,
        avoidH
[truncated — 2381 more characters]
```

### esp32/esp32_serial_bridge.py

```python
#!/usr/bin/env python3
"""
ESP32 Serial Bridge for UrbanResponse AI
Listens for disaster events from ESP32 via serial and forwards them to the web application
"""

import serial
import json
import time
import threading
import websocket
import random
from datetime import datetime
import sys
import os

# Configuration
SERIAL_PORT = 'COM8'  # Adjust based on your ESP32 port (COM8 on Windows, /dev/ttyUSB0 on Linux)
SERIAL_BAUD = 115200
WS_SERVER_URL = 'ws://localhost:8080'  # WebSocket server for real-time communication
BERKELEY_CENTER = {'lat': 37.8715, 'lng': -122.2730}  # UC Berkeley coordinates

class ESP32SerialBridge:
    def __init__(self, serial_port=SERIAL_PORT, baud_rate=SERIAL_BAUD):
        self.serial_port = serial_port
        self.baud_rate = baud_rate
        self.serial_conn = None
        self.websocket_conn = None
        self.running = False
        self.event_counter = 0
        
        # Berkeley area coordinates for random placement
        self.berkeley_bounds = {
            'north': 37.8815,
            'south': 37.8615,
            'east': -122.2530,
            'west': -122.2930
        }
        
    def connect_serial(self):
        """Connect to ESP32 via serial"""
        try:
            self.serial_conn = serial.Serial(
                port=self.serial_port,
                baudrate=self.baud_rate,
                timeout=1,
                write_timeout=1
            )
            print(f"SUCCESS: Connected to ESP32 on {self.serial_port}")
            return True
        except Exception as e:
            print(f"ERROR: Failed to connect to ESP32: {e}")
            return False
    
    def connect_websocket(self):
        """Connect to WebSocket server for real-time communication"""
        try:
            self.websocket_conn = websocket.create_connection(WS_SERVER_URL)
            print(f"SUCCESS: Connected to WebSocket server at {WS_SERVER_URL}")
            return True
        except Exception as e:
            print(f"ERROR: Failed to connect to WebSocket: {e}")
            return False
    
    def generate_random_location(self):
        """Generate a random location within Berkeley bounds"""
        lat = random.uniform(self.berkeley_bounds['south'], self.berkeley_bounds['north'])
        lng = random.uniform(self.berkeley_bounds['west'], self.berkeley_bounds['east'])
        return {'lat': lat, 'lng': lng}
    
    def process_esp32_event(self, raw_data):
        """Process disaster event data from ESP32"""
        try:
            # Parse JSON from ESP32
            if not raw_data.startswith('DISASTER_EVENT:'):
                return None
                
            json_data = raw_data.replace('DISASTER_EVENT:', '').strip()
            esp32_event = json.loads(json_data)
            
            # Generate event ID and location
            self.event_counter += 1
            event_id = f"ESP32_{self.event_counter}_{int(time.time())}"
            location = self.generate_random_location()
            
            # Convert ESP32 event to UrbanResponse format
            urban_event = {
                'event_id': event_id,
                'event_type': esp32_event.get('event_type', 'fire').lower(),
                'location': location,
                'severity': esp32_event.get('severity', 'medium').lower(),
                'timestamp': datetime.now().isoformat(),
                'source': 'esp32_hardware',
                **esp32_event  # Include original ESP32 data
            }
            
            # Map event types to expected format
            event_type_mapping = {
                'FIRE': 'fire',
                'MEDICAL': 'medical',
                'POLICE': 'police',
                'TRAFFIC': 'traffic'
            }
            
            if urban_event['event_type'].upper() in event_type_mapping:
                urban_event['event_type'] = event_type_mapping[urban_event['event_type'].upper()]
            
            print(f"EVENT: Processed ESP32 disaster event: {urban_event['event_type']} at {location}")
            return urban_event
            
        except Exception as e:
            print(f"ERROR: Error processing ESP32 event: {e}")
            return None
    
    def send_to_webapp(self, event_data):
        """Send event data to web application"""
        try:
            # Try WebSocket first
            if self.websocket_conn:
                message = {
                    'type': 'disaster_event',
                    'data': event_data
                }
                self.websocket_conn.send(json.dumps(message))
                print(f"WEBSOCKET: Sent event via WebSocket: {event_data['event_id']}")
                return True
                
        except Exception as e:
            print(f"ERROR: Failed to send via WebSocket: {e}")
        
        # Fallback: Write to file for polling
        try:
            # Get absolute path to 3DMap directory
            script_dir = os.path.dirname(os.path.abspath(__file__))
            project_root = os.path.dirname(script_dir)
            events_file = os.path.join(project_root, '3DMap', 'esp32_events.json')
            
            # Ensure 3DMap directory exists
            map_dir = os.path.join(project_root, '3DMap')
            if not os.path.exists(map_dir):
                print(f"ERROR: 3DMap directory not found at: {map_dir}")
                return False
            
            events = []
            
            # Read existing events
            if os.path.exists(events_file):
                with open(events_file, 'r') as f:
                    try:
                        events = json.load(f)
                    except json.JSONDecodeError:
                        events = []
            
            # Add new event
            events.append(event_data)
            
            # Keep only last 10 events
            events = events[-10:]
            
            # Write back to file
            with open(events_file, 'w') as f:
                jso
[truncated — 4295 more characters]
```

[14 more indexed source files omitted to keep this export small. The full file list is in the Codebase structure section above.]