import folium import pandas as pd from sqlalchemy import text from database import engine from datetime import datetime # ===================================================== # COLOR PALETTE FOR DIFFERENT DATES # ===================================================== COLOR_PALETTE = [ '#FF6B6B', # Red '#4ECDC4', # Teal '#45B7D1', # Blue '#FFA07A', # Light Salmon '#98D8C8', # Mint '#F7DC6F', # Yellow '#BB8FCE', # Purple '#85C1E2', # Sky Blue '#F8B88B', # Peach '#A9CCE3' # Light Blue ] # ===================================================== # LOCATION COORDINATES - EXACT LOCATIONS # ===================================================== LOCATION_COORDINATES = { "Adyar_GandhiNagar": (13.0125, 80.2520), "Adyar_IndiraNagar": (12.9967, 80.2531), "Adyar_KasturibaiNagar": (13.0062, 80.2535), "AnnaNagar_2ndAvenue": (13.0851, 80.2198), "AnnaNagar_Roundtana": (13.0843, 80.2125), "Guindy_GSTRoad": (13.0076, 80.2132), "Kotturpuram_AnnaUniversity": (13.0131, 80.2364), "Koyambedu_Market": (13.0691, 80.1915), "Mylapore_Temple": (13.0334, 80.2694), "Nungambakkam_HighRoad": (13.0617, 80.2458), "Thiruvanmiyur_Junction": (12.9877, 80.2573), "Tnagar_PondyBazaar": (13.0410, 80.2337), "Tnagar_UsmanRoad": (13.0354, 80.2323), "Velachery_MainRoad": (12.9868, 80.2221), "default": (13.0827, 80.2707), } # ===================================================== # HELPERS # ===================================================== def normalize_plate(plate): return str(plate).replace(" ", "").replace("-", "").upper().strip() def get_coordinates(location): if not location: return LOCATION_COORDINATES["default"] location = str(location).strip() # Exact match first if location in LOCATION_COORDINATES: return LOCATION_COORDINATES[location] # Partial match as fallback location_lower = location.lower() for key in LOCATION_COORDINATES: if key.lower() in location_lower or location_lower in key.lower(): return LOCATION_COORDINATES[key] return LOCATION_COORDINATES["default"] def map_to_html(m): """Convert folium map to HTML""" return m._repr_html_() # ===================================================== # DEFAULT MAP # ===================================================== def default_vehicle_map(): m = folium.Map( location=[13.0827, 80.2707], zoom_start=11, tiles="OpenStreetMap" ) folium.Marker( [13.0827, 80.2707], tooltip="ActionSync", popup="Vehicle Intelligence", icon=folium.Icon(color="blue", icon="car") ).add_to(m) return map_to_html(m) # ===================================================== # SEARCH VEHICLE ROUTE # ===================================================== def search_vehicle_route( plate, date_from=None, date_to=None ): try: if not plate: return ( default_vehicle_map(), pd.DataFrame(), {"error": "Enter plate number"} ) clean_plate = normalize_plate(plate) # Convert date objects to strings if needed if date_from: if hasattr(date_from, 'strftime'): # datetime.date or datetime.datetime date_from = date_from.strftime('%Y-%m-%d') elif isinstance(date_from, str) and len(date_from) > 10: # datetime string date_from = date_from[:10] if date_to: if hasattr(date_to, 'strftime'): # datetime.date or datetime.datetime date_to = date_to.strftime('%Y-%m-%d') elif isinstance(date_to, str) and len(date_to) > 10: # datetime string date_to = date_to[:10] query = """ SELECT plate, state, vehicle_type, vehicle_conf, location, date, timestamp FROM vehicle_logs WHERE REPLACE(REPLACE(UPPER(plate), ' ', ''), '-', '') = :plate """ params = { "plate": clean_plate } if date_from: query += " AND date >= :date_from" params["date_from"] = date_from if date_to: query += " AND date <= :date_to" params["date_to"] = date_to query += """ ORDER BY date ASC, timestamp ASC """ with engine.connect() as conn: result = conn.execute( text(query), params ) rows = result.fetchall() df = pd.DataFrame( rows, columns=result.keys() ) # ===================================================== # NO DATA # ===================================================== if df.empty: return ( default_vehicle_map(), pd.DataFrame(), { "error": f"No detections found for {clean_plate}" } ) # ===================================================== # ADD COORDINATES # ===================================================== df["latitude"] = df["location"].apply( lambda x: get_coordinates(x)[0] ) df["longitude"] = df["location"].apply( lambda x: get_coordinates(x)[1] ) # ===================================================== # GET UNIQUE DATES AND ASSIGN COLORS # ===================================================== unique_dates = sorted(df["date"].unique()) date_colors = {date: COLOR_PALETTE[i % len(COLOR_PALETTE)] for i, date in enumerate(unique_dates)} # ===================================================== # CREATE MAP # ===================================================== center_lat = df["latitude"].mean() center_lon = df["longitude"].mean() m = folium.Map( location=[center_lat, center_lon], zoom_start=12, tiles="OpenStreetMap" ) # ===================================================== # DETERMINE VIEW MODE # ===================================================== has_date_filter = date_from is not None or date_to is not None if not has_date_filter: # ===================================================== # MODE 1: NO DATE FILTER - SHOW SINGLE MARKER PER LOCATION # ===================================================== location_groups = df.groupby("location") for location, group in location_groups: lat = group["latitude"].iloc[0] lon = group["longitude"].iloc[0] location_dates = sorted(group["date"].unique()) total_detections = len(group) # Build popup showing all dates visited popup_html = f"""
Plate: {clean_plate}
Vehicle: {str(group.iloc[0]['vehicle_type'])}
State: {str(group.iloc[0]['state'])}
Total Visits: {total_detections}
📅 Dates Visited:Plate: {clean_plate}
Vehicle: {str(group.iloc[0]['vehicle_type'])}
State: {str(group.iloc[0]['state'])}