# Project export: FlowScope:The Agentic Simulator for Predictive Flow Dynamics

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: TreeHacks 2025
- Tagline: With agentic forecasting, FlowScope enables "what-if" scenario modeling, helping users anticipate and adapt via real-time visualization of human and transport flows across the globe
- Devpost: https://devpost.com/software/flowscope-global-dynamics-visualizer
- GitHub: https://github.com/HowardHuang1/TreeHacks2025
- Video: https://www.youtube.com/embed/wQdk5TIzKdo?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Team: 3 GitHub contributor(s) — SEddula (12 commits), Howard Huang (8 commits), ZichenYuan (6 commits)

## Devpost submission (written by the team)

### Inspiration

In recent months, the world has witnessed several tragic aviation incidents in the US—heartbreaking events that remind us of the fragile nature of our interconnected world. Behind every route and every flight lies a story of lives, livelihoods, and human connections. These events deeply moved us, fueling a vision to build FlowScope:The Agentic Simulator for Predictive Flow Dynamics, so that the world can be modeled and predicted at unprecedented heights. Our platform seeks to bring clarity to the invisible patterns of movement that shape our world—marine and air transportation, refugee flows, and other logistical patterns—so that we can predict, prepare for, and prevent future disruptions.

### What it does

Imagine a world where transportation disruptions, refugee movements, and logistical challenges are no longer unpredictable mysteries. Introducing FlowScope --- your command center for global flows, an AI agent powered platform that forecasts, simulates, and visualizes dynamic movement patterns in real-time. In this demo, we focus on marine transportation, showing how FlowScope empowers decision-makers to anticipate weather disruptions, geopolitical tensions, and operational bottlenecks. Our platform isn't limited to one domain; it's designed to adapt and scale across industries, from aviation to humanitarian logistics.

### How we built it

AI Agent Architecture: We use multiple AI agents powered by OpenAI's GPT models with LangChain, Perplexity Search, and real-time API calls. These agents work collaboratively to bridge the gap between static LLM task planning and the need for dynamic, real-time data. We also use existing marine traffic dataset (Automatic Identification System observation and dirways) to help with routing simulation. We use Leaflet map library for React to visualize maps. Intelligent Data Retrieval: Perplexity Search fetches relevant news based on location and keywords, providing our agents with context-aware insights. Path Planning and Prediction: We employ both LLM-based geopolitical risk assessment and traditional algorithms such as A* search to predict and simulate flows efficiently. We model ports and trade routes between them as nodes and edges in a graph dropping ports from this graph whenever critical ports or waterways are shut down like in the 2021 incident when the Suez Canal was obstructed for 6 days. Our ML model learns to assign weights to each of the edges in the graph, representing the cost of traversing that edge, based on a set of conditions: directional water ways, shallow water areas, sharp turns, vessel speeds in certain waters based on vessel class, geographic constraints, and historical route patterns. A* search is then used to traverse this graph and find the optimal route. Real-Time Updates: APIs like OpenWeatherMap feeds update our platform in real-time, enabling the AI agents to adjust predictions dynamically. We provide comprehensive data analysis across different metrics, including voyage types, fuel prices, and distances. Prompt Engineering: Crafting precise, contextual prompts allowed the agents to infer, reason, and respond accurately to new data inputs, enhancing reliability and interpretability. Key Features: Interactive Dashboard: Intuitive visualizations that resemble a Bloomberg Terminal for easy navigation and analysis. Agent Coordination: We developed a communication protocol that allowed agents to share intermediate results and coordinate decisions effectively. For example, a weather analysis agent would pass risk assessments to a route planning agent, ensuring context-aware decisions. Real-Time Data Synchronization: Integrating Perplexity Search with weather and transportation APIs ensured our models operated with the latest information. Challenges We Overcame: Our 4th teammate left us very last minute, but we were able to work in a team of three and brought the project together with almost no sleep.💪 Bridging multiple AI agents, path planning algorithms, and API calls is very challenging. Each component required different processes and data structures, demanding careful design and orchestration. To address this, we implemented a multi-agent framework with specialized roles for different tasks. What's Next: Extending the platform to simulate other flow types like air traffic, refugee movements, and global supply chains. Extending the platform to simulate other flow types like air traffic, refugee movements, and global supply chains. Enhancing the geopolitical risk assessment capabilities of our AI agents. Enhancing the geopolitical risk assessment capabilities of our AI agents. Introducing predictive maintenance features for transportation fleets. Introducing predictive maintenance features for transportation fleets.

### What we learned

FlowScope is more than a transportation simulation tool; it's a step toward a future where global flows are predictable, manageable, and resilient against disruptions. Join us as we bring this vision to life!

## README (from the GitHub repository)

# TreeHacks-2025
![UI design](UI.png)


## Detected evidence (automated analysis)

Indexed codebase: 27 recognized source files, 163 KB.
- CSS (language) — detected in the code
- FastAPI (technology) — detected in the code
- HTML (language) — detected in the code
- JavaScript (language) — detected in the code
- OpenAI (technology) — detected in the code
- Python (language) — detected in the code
- React (technology) — detected in the code
- LangChain (technology) — claimed on Devpost, not found in the code

## Codebase structure (from repository index)

### Files (48 of 48)

```
.DS_Store
README.md
treehacks2025/.gitignore
treehacks2025/app.py
treehacks2025/langchain_agent.py
treehacks2025/news_agent.py
treehacks2025/notebooks/1. Sea Modelling.ipynb
treehacks2025/notebooks/2. Shallow Water Modelling.ipynb
treehacks2025/notebooks/3.1 Training set exploration.ipynb
treehacks2025/notebooks/3.2  Validation set exploration.ipynb
treehacks2025/notebooks/4. Traffic Modelling.ipynb
treehacks2025/notebooks/5. Transition cost calculation.ipynb
treehacks2025/notebooks/6. Speed modelling.ipynb
treehacks2025/notebooks/7.1 Summer Route Prediction.ipynb
treehacks2025/notebooks/7.2. Winter Route Prediction.ipynb
treehacks2025/notebooks/8. Traffic Prediction.ipynb
treehacks2025/notebooks/9.1  Results - Route Prediction.ipynb
treehacks2025/notebooks/9.2. Results - Speed.ipynb
treehacks2025/notebooks/9.3. Results - traffic prediction.ipynb
treehacks2025/notebooks/9.4. Results - ETA.ipynb
treehacks2025/notebooks/predictRoute.ipynb
treehacks2025/notebooks/pygradu/gridify.py
treehacks2025/notebooks/pygradu/portcalls.py
treehacks2025/notebooks/pygradu/shortest_path.py
treehacks2025/package.json
treehacks2025/path_agent.py
treehacks2025/public/index.html
treehacks2025/public/manifest.json
treehacks2025/public/robots.txt
treehacks2025/README.md
treehacks2025/requirements.txt
treehacks2025/run.py
treehacks2025/src/App.css
treehacks2025/src/App.js
treehacks2025/src/App.test.js
treehacks2025/src/components/GeopoliticalPanel.js
treehacks2025/src/components/NewsPanel.js
treehacks2025/src/components/PathPlanningPanel.js
treehacks2025/src/components/StatsPanel.css
treehacks2025/src/components/StatsPanel.js
treehacks2025/src/components/WeatherPanel.js
treehacks2025/src/index.css
treehacks2025/src/index.js
treehacks2025/src/reportWebVitals.js
treehacks2025/src/setupTests.js
treehacks2025/static/css/style.css
treehacks2025/static/js/main.js
treehacks2025/weather.py
```

### Dependencies

- treehacks2025/package.json: @testing-library/dom@^10.4.0, @testing-library/jest-dom@^6.6.3, @testing-library/react@^16.2.0, @testing-library/user-event@^13.5.0, axios@^1.7.9, bootstrap@^5.3.3, chart.js@^4.4.7, httpx@^3.0.1, leaflet@^1.9.4, react@^19.0.0, react-chartjs-2@^5.3.0, react-dom@^19.0.0, react-leaflet@^5.0.0, react-scripts@5.0.1, web-vitals@^2.1.4
- treehacks2025/requirements.txt: asyncio@==3.4.3, fastapi@==0.104.1, folium@==0.14.0, geopy@==2.3.0, httpx@==0.25.2, numpy@==1.24.3, openai@==1.3.5, pandas@==2.0.3, pydantic@==2.5.2, python-dotenv@==1.0.0, scikit-learn@==1.3.0, uvicorn@==0.24.0

### Recent commits (newest first)

- Update README.md
- update readme
- does this fix it
- added analyitcs, simulaiton, dynamic playing, ships move now, map and agents look better
- export metric data as json to be visualized on frontend
- add impact on travel time + fuel usage + commodity prices
- update path planning agent
- add api endpoint for fetching predicted routes
- Merge branch 'main' of https://github.com/HowardHuang1/TreeHacks2025
- visualization for the routes
- increase radius for shut down ports
- add close port + reroute feature
- update
- add risk level agent
- added weather agent front end
- updated news agent
- updated the dashboard and news feed works
- output dataframe that contains an order of points (long/lat) on the predicted route
- visualization
- Merge branch 'main' of https://github.com/HowardHuang1/TreeHacks2025

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

### treehacks2025/requirements.txt

```
fastapi==0.104.1
uvicorn==0.24.0
httpx==0.25.2
python-dotenv==1.0.0
pydantic==2.5.2
asyncio==3.4.3
numpy==1.24.3
pandas==2.0.3
scikit-learn==1.3.0
geopy==2.3.0
folium==0.14.0
openai==1.3.5

```

### treehacks2025/package.json

```
{
  "name": "treehacks2025",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@testing-library/dom": "^10.4.0",
    "@testing-library/jest-dom": "^6.6.3",
    "@testing-library/react": "^16.2.0",
    "@testing-library/user-event": "^13.5.0",
    "axios": "^1.7.9",
    "bootstrap": "^5.3.3",
    "chart.js": "^4.4.7",
    "httpx": "^3.0.1",
    "leaflet": "^1.9.4",
    "react": "^19.0.0",
    "react-chartjs-2": "^5.3.0",
    "react-dom": "^19.0.0",
    "react-leaflet": "^5.0.0",
    "react-scripts": "5.0.1",
    "web-vitals": "^2.1.4"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": [
      "react-app",
      "react-app/jest"
    ]
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  }
}

```

### treehacks2025/app.py

```python
from fastapi import FastAPI, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
from typing import List, Optional, Dict
import pandas as pd
import numpy as np
from geopy.distance import geodesic
from datetime import datetime, timedelta
import random

app = FastAPI(title="Maritime Route Simulator",
             description="API for simulating and predicting maritime routes")

# Enable CORS
app.add_middleware(
    CORSMiddleware,
    allow_origins=["http://localhost:3000"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

# Pydantic models for request/response validation
class Point(BaseModel):
    lat: float
    lon: float

class RouteRequest(BaseModel):
    start: Point
    end: Point
    season: Optional[str] = "summer"

class SpeedPredictionRequest(BaseModel):
    location: Point
    time: datetime

class Route(BaseModel):
    route: List[List[float]]
    eta: str
    distance: float

class TrafficData(BaseModel):
    traffic_density: List[List[float]]

class SpeedPrediction(BaseModel):
    predicted_speed: float

# Load models and data here
# TODO: Import your trained models from notebooks

@app.post("/route", response_model=Route)
async def predict_route(request: RouteRequest):
    """Predict optimal maritime route"""
    try:
        start_point = [request.start.lat, request.start.lon]
        end_point = [request.end.lat, request.end.lon]
        
        # TODO: Implement route prediction using models from notebooks
        # For now, return a dummy route
        route = [
            start_point,
            [start_point[0] + (end_point[0] - start_point[0])/3, 
             start_point[1] + (end_point[1] - start_point[1])/3],
            [start_point[0] + 2*(end_point[0] - start_point[0])/3, 
             start_point[1] + 2*(end_point[1] - start_point[1])/3],
            end_point
        ]
        
        return Route(
            route=route,
            eta="48 hours",  # TODO: Implement ETA prediction
            distance=geodesic(start_point, end_point).nautical
        )
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

@app.get("/traffic", response_model=TrafficData)
async def get_traffic_data():
    """Get current maritime traffic data"""
    try:
        # TODO: Implement traffic visualization using data from notebooks
        return TrafficData(traffic_density=[])
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

@app.post("/speed", response_model=SpeedPrediction)
async def get_speed_prediction(request: SpeedPredictionRequest):
    """Get speed prediction for a location"""
    try:
        location = [request.location.lat, request.location.lon]
        
        # TODO: Implement speed prediction using models from notebooks
        return SpeedPrediction(predicted_speed=15.5)  # knots
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

@app.get("/ship-routes")
async def get_ship_routes():
    """Get current ship routes and port locations in the Suez Canal area"""
    try:
        # Define major ports
        ports = {
            "Port Said": {"lat": 31.2667, "lon": 32.3000},
            "Suez": {"lat": 29.9667, "lon": 32.5500},
            "Alexandria": {"lat": 31.2000, "lon": 29.9167},
            "Piraeus": {"lat": 37.9475, "lon": 23.6378},  # Greece
            "Istanbul": {"lat": 41.0082, "lon": 28.9784},  # Turkey
            "Jeddah": {"lat": 21.5433, "lon": 39.1728},  # Saudi Arabia
            "Dubai": {"lat": 25.2048, "lon": 55.2708},  # UAE
            "Mumbai": {"lat": 18.9750, "lon": 72.8258},  # India
            "Karachi": {"lat": 24.8607, "lon": 67.0011},  # Pakistan
            "Salalah": {"lat": 17.0151, "lon": 54.0924},  # Oman
            "Djibouti": {"lat": 11.8251, "lon": 42.5903},  # Djibouti
            "Aden": {"lat": 12.7797, "lon": 44.9647},  # Yemen
            "Haifa": {"lat": 32.8192, "lon": 34.9983},  # Israel
            "Beirut": {"lat": 33.9002, "lon": 35.5332},  # Lebanon
            "Limassol": {"lat": 34.6823, "lon": 33.0464},  # Cyprus
            "Valletta": {"lat": 35.8989, "lon": 14.5146},  # Malta
            "Algiers": {"lat": 36.7538, "lon": 3.0588},  # Algeria
            "Tunis": {"lat": 36.8065, "lon": 10.1815},  # Tunisia
        }

        # Define base routes with variations
        base_routes = {
            "suez_asia": [
                ports["Port Said"],
                ports["Suez"],
                ports["Jeddah"],
                ports["Aden"],
                ports["Salalah"],
                ports["Dubai"],
                ports["Karachi"],
                ports["Mumbai"],
            ],
            "suez_europe": [
                ports["Port Said"],
                ports["Alexandria"],
                ports["Limassol"],
                ports["Piraeus"],
                ports["Valletta"],
                ports["Tunis"],
                ports["Algiers"],
            ],
            "mediterranean_loop": [
                ports["Alexandria"],
                ports["Haifa"],
                ports["Beirut"],
                ports["Limassol"],
                ports["Istanbul"],
                ports["Piraeus"],
                ports["Valletta"],
                ports["Tunis"],
                ports["Alexandria"],
            ],
            "red_sea_loop": [
                ports["Suez"],
                ports["Jeddah"],
                ports["Aden"],
                ports["Djibouti"],
                ports["Suez"],
            ],
            "arabian_sea_loop": [
                ports["Aden"],
                ports["Salalah"],
                ports["Dubai"],
                ports["Karachi"],
                ports["Mumbai"],
                ports["Salalah"],
                ports["Aden"],
            ],
            "coastal_east": [
                ports["Suez"],
                ports["Jeddah"],
                ports["Aden"],
    
[truncated — 5794 more characters]
```

### treehacks2025/src/index.js

```javascript
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

```

### treehacks2025/static/js/main.js

```javascript
// Initialize the map
const map = L.map('map').setView([0, 0], 2);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
    attribution: '© OpenStreetMap contributors'
}).addTo(map);

// Store current route layer
let currentRoute = null;
let currentMarkers = [];

// Initialize the form
const routeForm = document.getElementById('routeForm');
routeForm.addEventListener('submit', async (e) => {
    e.preventDefault();
    
    const startLat = parseFloat(document.getElementById('startLat').value);
    const startLon = parseFloat(document.getElementById('startLon').value);
    const endLat = parseFloat(document.getElementById('endLat').value);
    const endLon = parseFloat(document.getElementById('endLon').value);
    const season = document.getElementById('season').value;

    try {
        // Clear previous route and markers
        if (currentRoute) {
            map.removeLayer(currentRoute);
        }
        currentMarkers.forEach(marker => map.removeLayer(marker));
        currentMarkers = [];

        // Add markers for start and end points
        const startMarker = L.marker([startLat, startLon]).addTo(map);
        const endMarker = L.marker([endLat, endLon]).addTo(map);
        currentMarkers.push(startMarker, endMarker);

        // Get route prediction
        const response = await fetch('/api/predict_route', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
            },
            body: JSON.stringify({
                start: { lat: startLat, lon: startLon },
                end: { lat: endLat, lon: endLon },
                season: season
            }),
        });

        const data = await response.json();
        
        // Draw the route
        currentRoute = L.polyline(data.route, {
            color: 'blue',
            weight: 3,
            opacity: 0.7
        }).addTo(map);

        // Fit map to show the entire route
        map.fitBounds(currentRoute.getBounds());

        // Update route information
        document.getElementById('distance').textContent = data.distance.toFixed(2);
        document.getElementById('eta').textContent = data.eta;

        // Get speed prediction for current position
        const speedResponse = await fetch('/api/speed_prediction', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
            },
            body: JSON.stringify({
                location: { lat: startLat, lon: startLon },
                time: new Date().toISOString()
            }),
        });

        const speedData = await speedResponse.json();
        document.getElementById('speed').textContent = speedData.predicted_speed.toFixed(1);

    } catch (error) {
        console.error('Error:', error);
        alert('Error calculating route. Please try again.');
    }
});

// Add click handlers to map for easy point selection
map.on('click', function(e) {
    const lat = e.latlng.lat.toFixed(6);
    const lng = e.latlng.lng.toFixed(6);
    
    if (!document.getElementById('startLat').value || !document.getElementById('startLon').value) {
        document.getElementById('startLat').value = lat;
        document.getElementById('startLon').value = lng;
    } else if (!document.getElementById('endLat').value || !document.getElementById('endLon').value) {
        document.getElementById('endLat').value = lat;
        document.getElementById('endLon').value = lng;
    }
});

```

### treehacks2025/src/App.js

```javascript
import React, { useState, useEffect, useCallback } from 'react';
import { MapContainer, useMap, TileLayer, Polyline, Marker, Popup } from 'react-leaflet';
import L from 'leaflet';
import 'leaflet/dist/leaflet.css';
import 'bootstrap/dist/css/bootstrap.min.css';
import './App.css';
import NewsPanel from './components/NewsPanel';
import WeatherPanel from './components/WeatherPanel';
import GeopoliticalPanel from './components/GeopoliticalPanel';
import PathPlanningPanel from './components/PathPlanningPanel';
import StatsPanel from './components/StatsPanel';
import './components/StatsPanel.css';

// Fix Leaflet default icon issue
delete L.Icon.Default.prototype._getIconUrl;
L.Icon.Default.mergeOptions({
  iconRetinaUrl: require('leaflet/dist/images/marker-icon-2x.png'),
  iconUrl: require('leaflet/dist/images/marker-icon.png'),
  shadowUrl: require('leaflet/dist/images/marker-shadow.png'),
});

// Create SVG icons for ships and ports
const shipIconSvg = `
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
  <path d="M16 2L4 16H28L16 2Z" fill="#3388ff"/>
  <circle cx="16" cy="16" r="6" fill="#3388ff"/>
</svg>`;

const portIconSvg = `
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
  <circle cx="12" cy="12" r="10" fill="#ff3333"/>
  <circle cx="12" cy="12" r="6" fill="white"/>
</svg>`;

// Convert SVG to data URL
const svgToDataUrl = (svg) => `data:image/svg+xml;base64,${btoa(svg)}`;

// Custom icons for ships and ports
const shipIcon = new L.Icon({
  iconUrl: svgToDataUrl(shipIconSvg),
  iconSize: [32, 32],
  iconAnchor: [16, 16],
  popupAnchor: [0, -16],
});

const portIcon = new L.Icon({
  iconUrl: svgToDataUrl(portIconSvg),
  iconSize: [24, 24],
  iconAnchor: [12, 12],
  popupAnchor: [0, -12],
});

function CenterCoordinates({ setCenter }) {
  const map = useMap();

  useEffect(() => {
    const updateCenter = () => {
      const center = map.getCenter();
      setCenter({ lat: center.lat, lon: center.lng });
    };
    map.on('moveend', updateCenter);
    updateCenter();
    return () => {
      map.off('moveend', updateCenter);
    };
  }, [map, setCenter]);

  return null;
}

function App() {
  const [startPoint, setStartPoint] = useState({ lat: 37.7749, lon: -122.4194 });
  const [endPoint, setEndPoint] = useState({ lat: 34.0522, lon: -118.2437 });
  const [season, setSeason] = useState('summer');
  const [route, setRoute] = useState(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);
  const [speed, setSpeed] = useState(null);
  const [trafficData, setTrafficData] = useState([]);
  const [mapCenter, setMapCenter] = useState({ lat: 27.0000, lon: 45.0000 }); // Centered between Mediterranean and Arabian Sea
  const [shipRoutes, setShipRoutes] = useState(null);
  const [selectedShip, setSelectedShip] = useState(null);
  const [showPorts, setShowPorts] = useState(true);
  const [showRoutes, setShowRoutes] = useState(true);
  const [isSimulating, setIsSimulating] = useState(false);
  const [simulationTime, setSimulationTime] = useState(0);
  const [simulationSpeed, setSimulationSpeed] = useState(1);
  const [shipTrails, setShipTrails] = useState({});
  const [showStats, setShowStats] = useState(false);
  const [statsData, setStatsData] = useState(null);

  // Fetch ship routes
  const fetchShipRoutes = useCallback(async () => {
    try {
      const response = await fetch('http://localhost:8000/api/ship-routes');
      if (!response.ok) {
        throw new Error('Failed to fetch ship routes');
      }
      const data = await response.json();
      console.log('Fetched ship routes:', data); // Add logging to debug
      setShipRoutes(data);
    } catch (error) {
      console.error('Error fetching ship routes:', error);
    }
  }, []);

  useEffect(() => {
    fetchShipRoutes();
    const interval = setInterval(fetchShipRoutes, 60000); // Update every minute
    return () => clearInterval(interval);
  }, [fetchShipRoutes]);

  useEffect(() => {
    // Sample stats data
    setStatsData({
      "voyages": {
        "3521": {
          "distances": {
            "direct": 237.66,
            "diverted": 505.05,
            "additional": 267.38
          },
          "time": {
            "direct": 15.84,
            "diverted": 33.67,
            "additional": 17.83
          },
          "fuel": {
            "additional_tons": 44.56,
            "additional_cost": 35650.9
          },
          "cost_impact": {
            "per_teu": 3.57,
            "percentage": 0.01
          }
        },
        "5801": {
          "distances": {
            "direct": 72.84,
            "diverted": 110.01,
            "additional": 37.17
          },
          "time": {
            "direct": 4.86,
            "diverted": 7.33,
            "additional": 2.48
          },
          "fuel": {
            "additional_tons": 6.19,
            "additional_cost": 4955.56
          },
          "cost_impact": {
            "per_teu": 0.5,
            "percentage": 0.0
          }
        },
        "5900": {
          "distances": {
            "direct": 682.13,
            "diverted": 818.83,
            "additional": 136.7
          },
          "time": {
            "direct": 45.48,
            "diverted": 54.59,
            "additional": 9.11
          },
          "fuel": {
            "additional_tons": 22.78,
            "additional_cost": 18226.08
          },
          "cost_impact": {
            "per_teu": 1.82,
            "percentage": 0.0
          }
        }
      },
      "commodities": [
        {
          "Commodity": "Electronics",
          "Baseline Price (USD)": 1000,
          "Impact Multiplier": 1.2,
          "Estimated Price Increase (%)": 0.0048,
          "New Price (USD)": 1000.048
        },
        {
          "Commodity": "Clothing",
          "Baseline Price (USD)": 50,
          "Impact Multiplier": 0.8,
          "Estimated Price In
[truncated — 34541 more characters]
```

### treehacks2025/path_agent.py

```python
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from typing import List

app = FastAPI()

class PortCoordinates(BaseModel):
    lat: float
    lon: float

class PortsRequest(BaseModel):
    selectedPorts: List[PortCoordinates]

@app.post("/api/ports")
async def receive_ports(request: PortsRequest):
    try:
        # Extract the list of selected ports
        selected_ports = request.selectedPorts
        for port in selected_ports:
            print(f"Received port - Latitude: {port.lat}, Longitude: {port.lon}")
        
        # Placeholder for further processing
        return {"message": "Ports received successfully", "ports": selected_ports}
    
    except Exception as e:
        raise HTTPException(status_code=400, detail=str(e))

```

### treehacks2025/run.py

```python
from dotenv import load_dotenv
import os

# Load environment variables from .env file
load_dotenv()

from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
import uvicorn
from app import app as main_app
from news_agent import app as news_app
from weather import app as weather_app
from langchain_agent import app as langchain_app
from app import app as ship_routes_app  # Import the ship routes app
from path_agent import app as path_app

# Create a combined FastAPI app
app = FastAPI()

# Add CORS middleware
app.add_middleware(
    CORSMiddleware,
    allow_origins=["http://localhost:3000"],  # Adjust this in production
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

main_app = FastAPI()

@main_app.get("/")
async def root():
    return {"message": "Hello World"}

# Move ship routes endpoints to main_app
for route in ship_routes_app.routes:
    main_app.routes.append(route)

# Mount apps
app.mount("/api", main_app)
app.mount("/news", news_app)
app.mount("/weather", weather_app)
app.mount("/langchain_agent", langchain_app)
app.mount("/ports", path_app)

if __name__ == "__main__":
    uvicorn.run("run:app", host="0.0.0.0", port=8000, reload=True)
```

### treehacks2025/weather.py

```python
import requests
import datetime
import os
from dotenv import load_dotenv
from fastapi import FastAPI, HTTPException

# OpenWeatherMap API Key (replace with your actual API key)
load_dotenv()

KEY = os.getenv("WEATHER_KEY")
app = FastAPI()

def get_weather_forecast(lat, lon):
    forecast_list = []
    url = f"https://api.openweathermap.org/data/2.5/forecast?lat={lat}&lon={lon}&appid={KEY}"
    print(url)
    response = requests.get(url)
    if response.status_code == 200:
        data = response.json()
        i = 0
        for forecast in data['list']:
            if i % 4 == 0:
                time = forecast['dt_txt']
                weather = forecast['weather'][0]['description']
                temp = forecast['main']['temp']
                humidity = forecast['main']['humidity']
                wind_speed = forecast['wind']['speed']
                visibility = forecast.get('visibility', 10000)

                forecast_data = {
                    "latitude": lat,
                    "longitude": lon,
                    "datetime": time,
                    "description": weather,
                    "temperature": temp,
                    "humidity": humidity,
                    "wind_speed": wind_speed,
                    "visibility": visibility
                }
                forecast_list.append(forecast_data)
            i += 1
    else:
        print(f"Failed to get forecast for ({lat}, {lon}). HTTP Status code: {response.status_code}")
    return forecast_list

@app.get("/api/weather")
async def get_weather(lat: float, lon: float):
    try:
        forecasts = get_weather_forecast(lat, lon)
        # print(forecasts[0:limit])
        return forecasts[0]
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))


if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=8002)


```

### treehacks2025/langchain_agent.py

```python
import os
import openai
import requests
import pandas as pd
from weather import get_weather_forecast
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
from langchain.schema import SystemMessage, HumanMessage
from fastapi import FastAPI, HTTPException

load_dotenv()
app = FastAPI()

def traverse_routes(df):
    results = []
    for i in range(0, len(df), 100):
        lat = df.loc[i, 'lat']
        lon = df.loc[i, 'lon']
        result = analyze_port_operation(lat, lon)
        results.append((i, lat, lon, result))

        # Convert results to a DataFrame for better visualization
        results_df = pd.DataFrame(results, columns=['Row', 'Latitude', 'Longitude', 'Result'])
    return results_df


def analyze_port_operation(lat, lon):
    # weather = api_data.get("weather")
    # news = api_data.get("news")
    weather = get_weather_forecast(lat, lon)
    news = ""

    # Construct a query for the agent
    llm = ChatOpenAI(model_name="gpt-3.5-turbo",
                      temperature=0,
                        max_tokens=30,
                        timeout=None,
                        max_retries=2)
    
    query = f"""
    Given the following data:
    - Weather: {weather}
    - News: {news}

    Analyze the impact on port operations. 
    Consider conditions like high winds (>35 knots), storms, poor visibilites, or strikes.
    Return a score of the risk level of this location between 0-1, where 0 is less risk and 1 is more risk.
    No additional sentences just one score.
    """
    
    # Make the LangChain API call
    response = llm.predict(query)
    print("here")
    decision = response.strip()
    return decision


# def test():
#     coordinates = [
#         (37.7749, -122.4194),  # San Francisco, CA
#         # (40.7128, -74.0060),   # New York, NY
#         # (51.5074, -0.1278)     # London, UK
#     ]

#     for lat, lon in coordinates:
#         # Example API Response
#         # api_response = {
#         #     "weather": {"wind_speed_knots": 40, "visibility": "poor", "temperature": 25},
#         #     "news": ["Local port workers announce 48-hour strike starting tomorrow."]
#         # }
#         decision = analyze_port_operation(lat, lon)
#         print(f"Port Status: {decision}")


@app.get("/api/langchain_agent")
async def get_risk_level(lat: float, lon: float):
    try:
        risk = analyze_port_operation(lat, lon)
        # print(forecasts[0:limit])
        return risk
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))


if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=8003)


```

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