import gradio as gr
import pandas as pd
import numpy as np
import requests
import folium
from folium.plugins import AntPath
from geopy.geocoders import Nominatim
from geopy.extra.rate_limiter import RateLimiter
from ortools.constraint_solver import pywrapcp, routing_enums_pb2
import os
from openai import OpenAI
from datetime import datetime
import base64
# ---------------------------------------
# CONSTANTS & BRANDING
# ---------------------------------------
PRIMARY_COLOR = "#0F2C59" # Procelevate Blue
VEHICLE_MILEAGE = {
"Truck (32 ft)": 2.5,
"Mini-Truck / Tata Ace": 12,
"Bike Delivery": 45,
"Car / SUV": 12
}
# GPT Client
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
client = OpenAI(api_key=OPENAI_API_KEY)
# ---------------------------------------
# GEOCODING
# ---------------------------------------
geolocator = Nominatim(user_agent="procelevate_route_app")
geocode = RateLimiter(geolocator.geocode, min_delay_seconds=1)
def get_coordinates(address):
try:
loc = geocode(address)
if loc:
return (loc.latitude, loc.longitude)
except:
return None
return None
# ---------------------------------------
# OSRM Distance + Time Fetcher
# ---------------------------------------
def osrm_query(coord1, coord2):
url = f"http://router.project-osrm.org/route/v1/driving/{coord1[1]},{coord1[0]};{coord2[1]},{coord2[0]}?overview=false"
r = requests.get(url).json()
if "routes" in r:
return r["routes"][0]["distance"], r["routes"][0]["duration"]
return None, None
def build_matrices(coords):
n = len(coords)
dist = np.zeros((n,n))
time = np.zeros((n,n))
for i in range(n):
for j in range(n):
if i == j:
continue
d, t = osrm_query(coords[i], coords[j])
if not d:
return None, None
dist[i][j] = d
time[i][j] = t
return dist, time
# ---------------------------------------
# OR-TOOLS Optimization
# ---------------------------------------
def optimize_route(distance_matrix):
n = len(distance_matrix)
manager = pywrapcp.RoutingIndexManager(n, 1, 0)
routing = pywrapcp.RoutingModel(manager)
def distance_callback(from_i, to_i):
return int(distance_matrix[manager.IndexToNode(from_i)][manager.IndexToNode(to_i)])
transit_idx = routing.RegisterTransitCallback(distance_callback)
routing.SetArcCostEvaluatorOfAllVehicles(transit_idx)
params = pywrapcp.DefaultRoutingSearchParameters()
params.first_solution_strategy = routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC
sol = routing.SolveWithParameters(params)
if not sol:
return None
index = routing.Start(0)
order = []
while not routing.IsEnd(index):
order.append(manager.IndexToNode(index))
index = sol.Value(routing.NextVar(index))
return order
# ---------------------------------------
# Fuel, Cost, Toll Calculations
# ---------------------------------------
def calculate_kpis(distance_km, mileage, fuel_price, toll_estimate=0):
fuel_needed = distance_km / mileage
fuel_cost = fuel_needed * fuel_price
total_cost = fuel_cost + toll_estimate
return fuel_needed, fuel_cost, total_cost
# ---------------------------------------
# Folium Map
# ---------------------------------------
def make_map(coords, addresses, order):
m = folium.Map(location=coords[order[0]], zoom_start=12)
path = []
for i, idx in enumerate(order):
folium.Marker(
coords[idx],
tooltip=f"{i+1}. {addresses[idx]}",
icon=folium.Icon(color="blue")
).add_to(m)
path.append(coords[idx])
AntPath(path, color="blue", weight=4).add_to(m)
return m._repr_html_()
# ---------------------------------------
# AI Explanation via GPT
# ---------------------------------------
def generate_ai_explanation(opt_km, naive_km, fuel_saved, cost_saved, time_saved):
prompt = f"""
You are an AI logistics expert. Explain clearly why the optimized route is better.
Optimized distance: {opt_km:.2f} km
Naive distance: {naive_km:.2f} km
Fuel saved: {fuel_saved:.2f} litres
Cost saved: ₹{cost_saved:.2f}
Time saved: {time_saved:.2f} minutes
Write a short, professional explanation suitable for a supply-chain manager at CEVA Logistics.
"""
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": prompt}]
)
return response.choices[0].message.content
# ---------------------------------------
# HTML REPORT GENERATOR
# ---------------------------------------
def generate_report_html(summary_text, ai_text, map_html):
html = f"""
Procelevate AI Route Optimization Report
Generated: {datetime.now().strftime('%Y-%m-%d %H:%M')}
Summary
{summary_text}
AI Explanation
{ai_text}
Route Map
{map_html}
"""
return html
# ---------------------------------------
# PART 3 — FULL ENTERPRISE DASHBOARD UI
# ---------------------------------------
def run_route_engine(start, end, stops_text, vehicle_type, fuel_price):
stops = [s.strip() for s in stops_text.split("\n") if s.strip()]
# Build address list
addresses = [start] + stops + [end]
addresses = [a for a in addresses if a.strip()]
if len(addresses) < 2:
return ["❌ Please enter valid addresses."] + [None]*5
# Geocode all addresses
coords = []
for a in addresses:
c = get_coordinates(a)
if not c:
return [f"❌ Failed to locate address: {a}"] + [None]*5
coords.append(c)
# Build OSRM matrices
dist_m, time_m = build_matrices(coords)
if dist_m is None:
return ["❌ OSRM routing failed. Try different locations."] + [None]*5
# Convert meters → km, seconds → minutes
dist_km = dist_m / 1000
time_min = time_m / 60
# Optimized route
opt_order = optimize_route(dist_m)
if opt_order is None:
return ["❌ Optimization failed."] + [None]*5
# Naive + reverse orders
naive_order = list(range(len(addresses)))
rev_order = list(reversed(naive_order))
def compute_totals(order):
total_km = 0
total_min = 0
for i in range(len(order)-1):
total_km += dist_km[order[i]][order[i+1]]
total_min += time_min[order[i]][order[i+1]]
return total_km, total_min
opt_km, opt_min = compute_totals(opt_order)
naive_km, naive_min = compute_totals(naive_order)
rev_km, rev_min = compute_totals(rev_order)
mileage = VEHICLE_MILEAGE[vehicle_type]
# KPI calculations
opt_fuel, opt_fuel_cost, opt_total_cost = calculate_kpis(opt_km, mileage, fuel_price)
naive_fuel, naive_fuel_cost, naive_total_cost = calculate_kpis(naive_km, mileage, fuel_price)
rev_fuel, rev_fuel_cost, rev_total_cost = calculate_kpis(rev_km, mileage, fuel_price)
# Savings
fuel_saved = naive_fuel - opt_fuel
cost_saved = naive_total_cost - opt_total_cost
time_saved = naive_min - opt_min
# AI Explanation
ai_text = generate_ai_explanation(opt_km, naive_km, fuel_saved, cost_saved, time_saved)
# Optimized route map
map_html = make_map(coords, addresses, opt_order)
# Summary panel text
summary_text = f"""
Optimized Distance: {opt_km:.2f} km
Optimized Time: {opt_min:.2f} minutes
Fuel Needed: {opt_fuel:.2f} L
Total Cost: ₹{opt_total_cost:.2f}
Efficiency Gain vs Naive: {((naive_km-opt_km)/naive_km)*100:.2f}%
"""
# Comparison table
comp_df = pd.DataFrame({
"Route Type": ["Optimized", "Naive", "Reverse"],
"Distance (km)": [opt_km, naive_km, rev_km],
"Time (min)": [opt_min, naive_min, rev_min],
"Fuel (L)": [opt_fuel, naive_fuel, rev_fuel],
"Cost (₹)": [opt_total_cost, naive_total_cost, rev_total_cost]
})
# Data tables
dist_df = pd.DataFrame(dist_km, columns=addresses, index=addresses)
time_df = pd.DataFrame(time_min, columns=addresses, index=addresses)
# Downloadable report
report_html = generate_report_html(summary_text, ai_text, map_html)
b64 = base64.b64encode(report_html.encode()).decode()
download_link = f'Download Report'
return (
summary_text, # TAB 1
map_html, # TAB 2
comp_df, # TAB 3
ai_text, # TAB 4
dist_df, # TAB 5A
time_df, # TAB 5B
download_link # TAB 6
)
# ---------------------------------------
# GRADIO UI LAYOUT — TABS + BRANDING
# ---------------------------------------
with gr.Blocks() as demo:
gr.HTML(f"""
""")
gr.Markdown(f"Procelevate AI Route Optimization Suite
")
# -------------------------
# Address Inputs
# -------------------------
with gr.Row():
start = gr.Textbox(label="From", placeholder="Starting point (e.g., Bangalore Airport)")
end = gr.Textbox(label="To", placeholder="Final destination (e.g., Whitefield)")
stops_text = gr.Textbox(
label="Stops (one per line, optional)",
lines=4,
placeholder="Example:\nMG Road\nBTM Layout\nElectronic City"
)
# -------------------------
# Vehicle + Fuel Inputs
# -------------------------
vehicle_type = gr.Dropdown(
list(VEHICLE_MILEAGE.keys()),
label="Vehicle Type",
value="Truck (32 ft)"
)
fuel_price = gr.Number(
label="Fuel Price (₹ per litre)",
value=91
)
# -------------------------
# Submit Button
# -------------------------
submit_btn = gr.Button("Optimize Route", variant="primary")
# TAB STRUCTURE
with gr.Tabs():
with gr.Tab("Overview"):
overview_out = gr.HTML()
with gr.Tab("Optimized Route"):
map_out = gr.HTML()
with gr.Tab("Route Comparison"):
comp_out = gr.Dataframe()
with gr.Tab("AI Explanation"):
ai_out = gr.Markdown()
with gr.Tab("Data Tables"):
dist_out = gr.Dataframe(label="Distance (km)")
time_out = gr.Dataframe(label="Time (min)")
with gr.Tab("Download Report"):
report_out = gr.HTML()
# CONNECT BUTTON
submit_btn.click(
fn=run_route_engine,
inputs=[start, end, stops_text, vehicle_type, fuel_price],
outputs=[overview_out, map_out, comp_out, ai_out, dist_out, time_out, report_out]
)
demo.launch(
server_name="0.0.0.0",
server_port=7860,
ssr_mode=False
)