import gradio as gr import folium import networkx as nx from folium import Map, Marker, PolyLine, Popup, IFrame from math import radians, cos, sin, sqrt, atan2 from branca.element import MacroElement from jinja2 import Template # 1. Districts and Coordinates (Harimo Kigali) places = { "Kigali": (-1.9441, 30.0619), "Nyarugenge": (-1.9577, 30.0619), "Gasabo": (-1.9400, 30.0861), "Kicukiro": (-1.9781, 30.0597), "Burera": (-1.4800, 29.7300), "Gakenke": (-1.5700, 29.7561), "Rulindo": (-1.8333, 30.0833), "Musanze": (-1.5014, 29.6344), "Gicumbi": (-1.5794, 30.0542), "Nyagatare": (-1.3100, 30.3000), "Gatsibo": (-1.6800, 30.3900), "Kayonza": (-2.0000, 30.5667), "Kirehe": (-2.3553, 30.7767), "Ngoma": (-2.1600, 30.4700), "Rwamagana": (-1.9491, 30.4349), "Bugesera": (-2.2083, 30.2576), "Kamonyi": (-2.0833, 29.9000), "Muhanga": (-2.1200, 29.7561), "Ruhango": (-2.2136, 29.7628), "Nyamagabe": (-2.4978, 29.4897), "Nyaruguru": (-2.5806, 29.4306), "Huye": (-2.5921, 29.7408), "Gisagara": (-2.6283, 29.6820), "Nyanza": (-2.3566, 29.7507), "Rutsiro": (-2.0986, 29.3269), "Karongi": (-2.0667, 29.4677), "Rubavu": (-1.7481, 29.2730), "Rusizi": (-2.5406, 29.3737), "Nyamasheke": (-2.4700, 29.3222), "Ngororero": (-1.8733, 29.5811) } # 2. Place images URLs place_images = { "Kigali": "https://upload.wikimedia.org/wikipedia/commons/8/89/Kigali_city_view.jpg", "Nyarugenge": "https://upload.wikimedia.org/wikipedia/commons/f/f8/Nyarugenge_district.jpg", "Gasabo": "https://upload.wikimedia.org/wikipedia/commons/a/a5/Gasabo_district.jpg", "Kicukiro": "https://upload.wikimedia.org/wikipedia/commons/1/10/Kicukiro_District_-_Rwanda.jpg", "Burera": "https://upload.wikimedia.org/wikipedia/commons/3/33/Burera_Lake_Muhazi.jpg", "Gakenke": "https://upload.wikimedia.org/wikipedia/commons/c/c4/Gakenke_landscape.jpg", "Rulindo": "https://upload.wikimedia.org/wikipedia/commons/0/01/Rulindo_landscape.jpg", "Musanze": "https://upload.wikimedia.org/wikipedia/commons/6/6c/Musanze_scenery.jpg", "Gicumbi": "https://upload.wikimedia.org/wikipedia/commons/7/7e/Gicumbi_landscape.jpg", "Nyagatare": "https://upload.wikimedia.org/wikipedia/commons/9/98/Nyagatare_landscape.jpg", "Gatsibo": "https://upload.wikimedia.org/wikipedia/commons/f/f0/Gatsibo_landscape.jpg", "Kayonza": "https://upload.wikimedia.org/wikipedia/commons/b/b6/Kayonza_landscape.jpg", "Kirehe": "https://upload.wikimedia.org/wikipedia/commons/3/3f/Kirehe_landscape.jpg", "Ngoma": "https://upload.wikimedia.org/wikipedia/commons/4/49/Ngoma_landscape.jpg", "Rwamagana": "https://upload.wikimedia.org/wikipedia/commons/0/00/Rwamagana_landscape.jpg", "Bugesera": "https://upload.wikimedia.org/wikipedia/commons/1/12/Bugesera_landscape.jpg", "Kamonyi": "https://upload.wikimedia.org/wikipedia/commons/d/d4/Kamonyi_landscape.jpg", "Muhanga": "https://upload.wikimedia.org/wikipedia/commons/2/2a/Muhanga_landscape.jpg", "Ruhango": "https://upload.wikimedia.org/wikipedia/commons/9/96/Ruhango_landscape.jpg", "Nyamagabe": "https://upload.wikimedia.org/wikipedia/commons/8/80/Nyamagabe_landscape.jpg", "Nyaruguru": "https://upload.wikimedia.org/wikipedia/commons/6/63/Nyaruguru_landscape.jpg", "Huye": "https://upload.wikimedia.org/wikipedia/commons/7/7a/Huye_landscape.jpg", "Gisagara": "https://upload.wikimedia.org/wikipedia/commons/8/8c/Gisagara_landscape.jpg", "Nyanza": "https://upload.wikimedia.org/wikipedia/commons/3/3a/Nyanza_landscape.jpg", "Rutsiro": "https://upload.wikimedia.org/wikipedia/commons/5/5f/Rutsiro_landscape.jpg", "Karongi": "https://upload.wikimedia.org/wikipedia/commons/f/f5/Karongi_landscape.jpg", "Rubavu": "https://upload.wikimedia.org/wikipedia/commons/6/68/Rubavu_landscape.jpg", "Rusizi": "https://upload.wikimedia.org/wikipedia/commons/3/30/Rusizi_landscape.jpg", "Nyamasheke": "https://upload.wikimedia.org/wikipedia/commons/1/17/Nyamasheke_landscape.jpg", "Ngororero": "https://upload.wikimedia.org/wikipedia/commons/7/7d/Ngororero_landscape.jpg" } # 3. Distance calculator (Haversine Formula) def haversine(coord1, coord2): R = 6371 # Earth radius in km lat1, lon1 = coord1 lat2, lon2 = coord2 dlat = radians(lat2 - lat1) dlon = radians(lon2 - lon1) a = sin(dlat / 2)**2 + cos(radians(lat1)) * cos(radians(lat2)) * sin(dlon / 2)**2 c = 2 * atan2(sqrt(a), sqrt(1 - a)) return R * c # 4. Edges (connect Kigali and other districts) edges = [ ("Nyarugenge", "Gasabo"), ("Gasabo", "Kicukiro"), ("Kicukiro", "Bugesera"), ("Bugesera", "Rwamagana"), ("Rwamagana", "Kayonza"), ("Kayonza", "Kirehe"), ("Kirehe", "Ngoma"), ("Ngoma", "Gatsibo"), ("Gatsibo", "Nyagatare"), ("Gatsibo", "Gicumbi"), ("Gicumbi", "Rulindo"), ("Rulindo", "Gakenke"), ("Gakenke", "Burera"), ("Burera", "Musanze"), ("Musanze", "Rubavu"), ("Rubavu", "Rutsiro"), ("Rutsiro", "Karongi"), ("Karongi", "Nyamasheke"), ("Nyamasheke", "Rusizi"), ("Rutsiro", "Ngororero"), ("Ngororero", "Muhanga"), ("Muhanga", "Kamonyi"), ("Kamonyi", "Nyarugenge"), ("Muhanga", "Ruhango"), ("Ruhango", "Nyanza"), ("Nyanza", "Huye"), ("Huye", "Gisagara"), ("Gisagara", "Nyaruguru"), ("Nyaruguru", "Nyamagabe"), ("Nyamagabe", "Karongi"), ("Ngororero", "Ruhango"), ("Gicumbi", "Gasabo"), ("Bugesera", "Ngoma"), ("Kigali", "Nyarugenge"), ("Kigali", "Gasabo"), ("Kigali", "Kicukiro") ] # 5. Create Graph G = nx.Graph() for u, v in edges: G.add_edge(u, v, weight=haversine(places[u], places[v])) # 6. Animation class for marker movement class AnimateMarker(MacroElement): _template = Template(""" {% macro script(this, kwargs) %} var marker = L.marker({{this.locations[0]}}).addTo({{this._parent.get_name()}}); var latlngs = {{this.locations}}; var index = 0; function moveMarker(){ index++; if(index >= latlngs.length){ return; } marker.setLatLng(latlngs[index]); setTimeout(moveMarker, 700); } moveMarker(); {% endmacro %} """) def __init__(self, locations): super().__init__() self._name = "AnimateMarker" self.locations = locations # 7. Routing function with popup images on map markers def generate_map(start, end): if start == end: return "Hitamo aho utangiriye n’aho ugiye bitandukanye.", "", "" if not nx.has_path(G, start, end): return f"Nta nzira ibaho hagati ya {start} na {end}.", "", "" try: path = nx.astar_path(G, start, end, heuristic=lambda u, v: haversine(places[u], places[v]), weight='weight') coords = [places[p] for p in path] m = Map(location=[-1.9441, 30.0619], zoom_start=9) # Add markers for all places (normal popup with name) for name, coord in places.items(): if name == start or name == end: # For start and end: popup with image + name img_url = place_images.get(name, "https://via.placeholder.com/300?text=No+Image") html = f"""