File size: 8,494 Bytes
5ff2435
 
 
66291ac
ee77ee9
932a171
 
5ff2435
500b397
5ff2435
fa642ae
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5ff2435
 
66291ac
fa642ae
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ee77ee9
500b397
ee77ee9
 
 
 
3e21d9f
 
ee77ee9
 
fa642ae
ee77ee9
3e21d9f
 
 
 
 
 
 
 
500b397
 
ee77ee9
932a171
fa642ae
3e21d9f
 
2793622
ee77ee9
66291ac
932a171
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
66291ac
5ff2435
 
fa642ae
5ff2435
9230299
fa642ae
9230299
5ff2435
2793622
5ff2435
3fbf837
500b397
3e21d9f
66291ac
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3e21d9f
932a171
97de2b1
66291ac
fa642ae
ee77ee9
fa642ae
5ff2435
66291ac
 
 
 
 
 
 
 
 
 
 
 
fa642ae
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
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"""
                    <h4>{name}</h4>
                    <img src="{img_url}" width="300" style="border-radius:8px;"/>
                """
                iframe = IFrame(html, width=320, height=320)
                popup = Popup(iframe, max_width=320)
                Marker(location=coord, popup=popup, icon=folium.Icon(color='green' if name == start else 'red')).add_to(m)
            else:
                Marker(location=coord, popup=name).add_to(m)

        # Draw the route line
        PolyLine(coords, color="blue", weight=5).add_to(m)
        m.add_child(AnimateMarker(coords))

        return "Inzira ngufi ni: " + " ➔ ".join(path), m._repr_html_(), ""

    except Exception as e:
        return f"Ntibishoboka kubona inzira: {str(e)}", "", ""

# 8. Gradio interface
with gr.Blocks() as demo:
    gr.Markdown("## Shaka inzira ngufi hagati y’uturere twa Rwanda")
    start = gr.Dropdown(list(places.keys()), label="Aho utangirira")
    end = gr.Dropdown(list(places.keys()), label="Aho ugiye")
    btn = gr.Button("Shaka inzira")
    output_text = gr.Textbox(label="Inzira", interactive=False)
    output_map = gr.HTML()
    
    btn.click(fn=generate_map, inputs=[start, end], outputs=[output_text, output_map, gr.Textbox(visible=False)])

demo.launch()