File size: 8,037 Bytes
366b82f
d2eac8a
5c8a315
 
 
0188ddf
5c8a315
 
 
 
0963bdd
5c8a315
181f4c3
5c8a315
 
9ff2d70
5c8a315
0188ddf
4008992
 
 
 
 
18e86d5
927c3de
 
18e86d5
927c3de
 
 
 
 
18e86d5
 
 
 
 
 
 
927c3de
 
 
 
 
 
 
 
 
 
5c8a315
d2eac8a
9ff2d70
d2eac8a
 
 
 
 
 
5c8a315
 
9ff2d70
 
 
 
 
df311b6
 
 
 
92707e2
df311b6
 
 
9ff2d70
df311b6
 
 
 
 
 
 
92707e2
9ff2d70
 
 
5c8a315
99b0a9f
af8a251
5c8a315
af8a251
5c8a315
 
 
 
99b0a9f
92707e2
 
653bd0b
99b0a9f
92707e2
5c8a315
 
 
 
92707e2
5c8a315
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
653bd0b
5c8a315
 
 
92707e2
4008992
 
 
 
 
 
 
 
 
 
 
 
 
92707e2
4008992
 
 
 
 
 
92707e2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5c8a315
 
 
 
 
 
 
 
 
 
 
 
927c3de
5c8a315
 
 
927c3de
 
5c8a315
 
927c3de
5c8a315
 
 
 
927c3de
 
5c8a315
 
37c5d6f
5c8a315
 
37c5d6f
 
5c8a315
49fc98c
5c8a315
 
 
 
 
92707e2
 
5c8a315
 
49fc98c
5c8a315
af37579
 
 
 
 
 
 
 
92707e2
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
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
import re
from typing import Union
import folium
import pandas as pd
from folium import plugins
import streamlit as st

EPICENTER_LOCATION = [31.12210171476489, -8.42945837915193]
BORDER_COLOR = "black"

# @st.cache_resource
def parse_gg_sheet(url):
    print("Parsing Google Sheet:", url)
    url = url.replace("edit#gid=", "export?format=csv&gid=")
    df = pd.read_csv(url, on_bad_lines="warn")
    return df

@st.cache_resource
def parse_json_file(url):
    df = pd.read_json(url)
    df = pd.json_normalize(df.douars)
    return df

def is_request_in_list(request, selection_list, options):
    if isinstance(request, float):  # Check if the input is a float (like NaN)
        return False

    if "," in request:
      all_requests = [r.strip() for r in request.split(",")]
    else:
      all_requests = [request]

    # If at least one of the requests is not in the options or in the selection list, return True
    for r in all_requests:
        if r not in options:
            return True
        if r in selection_list:
            return True
    return False

def marker_request(request):
    # in case of multiple requests we use the first one for the marker's icon
    # requests are already sorted by priority from the form
    try:
        displayed_request = request.split(',')[0]
    except:
        displayed_request = request
    return displayed_request


def add_latlng_col(df, process_column: Union[str, int]):
    """Add a latlng column to the dataframe"""
    if isinstance(process_column, str):
        df["latlng"] = df[process_column].apply(parse_latlng)
    elif isinstance(process_column, int):
        df["latlng"] = df.iloc[:, process_column].apply(parse_latlng)
    else:
        raise ValueError(f"process_column should be a string or an integer, got {type(process_column)}")
    return df

# parse latlng (column 4) to [lat, lng]
def parse_latlng(latlng):
    if pd.isna(latlng):
        return None
    try:
        # case where there more than one comma 30,98 , -7,10
        if latlng.count(',') > 2:
            d1, d2, d3, d4 = latlng.split(",")[:4]
            return [float(".".join([d1, d2])), float(".".join([d3, d4]))]

        # case of more than one dot 30.98. -7.10
        if latlng.count('.') > 2:
            d1, d2, d3, d4 = latlng.split(".")[:4]
            return [float(".".join([d1, d2])), float(".".join([d3, d4]))]

        # case where there is only one comma 30,98 , -7,10
        lat, lng = latlng.split(",")[:2]
        # remove anything that is not a digit or a dot or a minus sign
        lat = re.sub(r"[^\d\.\-]", "", lat)
        lng = re.sub(r"[^\d\.\-]", "", lng)
        return [float(lat), float(lng)]

    except Exception as e:
        print(f"Error parsing latlng: {latlng}  Reason: {e}")
        return None

def add_epicentre_to_map(fg):
    # Removed the spinner to not confuse the users as the map is already loaded
    icon_epicentre = folium.plugins.BeautifyIcon(
        icon='star',
        border_color='#b3334f',
        background_color='#b3334f',
        text_color='white'
    )

    fg.add_child(folium.Marker(location=EPICENTER_LOCATION,
                #   popup="Epicenter مركز الزلزال",
                  tooltip="Epicenter مركز الزلزال",
                  icon=icon_epicentre))



def add_danger_distances_to_map(map_obj):
    Danger_Distances_group = folium.FeatureGroup(name='Danger distances - earthquake magnitude 7 | مسافات الخطر - قوة الزلازل 7').add_to(map_obj)

    zones = [
        {"radius": 100000, "fill_opacity": 0.1, "weight": 1, "fill_color": "yellow", "tooltip": "50 to 100 km - Moderate risk area | منطقة خطر معتدلة"},
        {"radius": 50000, "fill_opacity": 0.1, "weight": 1, "fill_color": "orange", "tooltip": "30 to 50 km - High risk zone | منطقة عالية المخاطر"},
        {"radius": 30000, "fill_opacity": 0.2, "weight": 1, "fill_color": "#FF0000", "tooltip": "10 to 30 km - Very high risk zone | منطقة شديدة الخطورة"},
        {"radius": 10000, "fill_opacity": 0.2, "weight": 0.2, "fill_color": "#8B0000", "tooltip": "0 to 10km - direct impact zone | منطقة التأثير المباشر"}
    ]

    for zone in zones:
        folium.Circle(
            location=EPICENTER_LOCATION,
            radius=zone["radius"],
            color=BORDER_COLOR,
            weight=zone["weight"],
            fill_opacity=zone["fill_opacity"],
            opacity=zone["fill_opacity"],  # Assuming border opacity should match fill_opacity
            fill_color=zone["fill_color"],
            # tooltip=zone["tooltip"],
        ).add_to(Danger_Distances_group)


def add_village_names(douar_df, map_obj):
    village_fgroup = folium.FeatureGroup(name='🔵 All the Villages / Tous les villages / جميع القرى', show=False).add_to(map_obj)

    for _, row in douar_df.iterrows():
        lat = row['lat']
        lng = row['lng']
        lat_lng = (lat, lng)
        dour_name = row['name'].capitalize()
        maps_url = f"https://maps.google.com/?q={lat_lng}"
        display_text = f'<br><b>⛰️ Douar:</b> {dour_name}<br><a href="{maps_url}" target="_blank" rel="noopener noreferrer"><b>🧭 Google Maps</b></a>'

        folium.CircleMarker(
            location=[lat, lng],
            radius=0.1,
            tooltip = dour_name, # we might remove the tooltip to avoid crowding the map
            popup=folium.Popup(display_text, max_width=200),
            color= "#0046C8",
            opacity = 0.7
        ).add_to(village_fgroup)


def init_intervention_fgs(m):
    intervention_fgs = {}

    fg_done = folium.FeatureGroup(name="Done ✅", show=True).add_to(m)
    fg_planned = folium.FeatureGroup(name="Planned ⏳", show=True).add_to(m)
    fg_partial = folium.FeatureGroup(name="Partial 📝", show=True).add_to(m)

    intervention_fgs["Done ✅"] = fg_done
    intervention_fgs["Planned ⌛"] = fg_planned
    intervention_fgs["Partial 📝"] = fg_partial

    return intervention_fgs


def init_emergency_fgs(m):
    emergency_fgs = {}

    fg_high = folium.FeatureGroup(name="High 🔴", show=True).add_to(m)
    fg_medium = folium.FeatureGroup(name="Medium 🟠", show=True).add_to(m)
    fg_low = folium.FeatureGroup(name="Low 🟡", show=True).add_to(m)

    emergency_fgs["High"] = fg_high
    emergency_fgs["Medium"] = fg_medium
    emergency_fgs["Low"] = fg_low

    return emergency_fgs


def init_map():
    m = folium.Map(
        location=[31.228674, -7.992047],
        zoom_start=8.5,
        min_zoom=8.5,
        max_lat=35.628674,
        min_lat=29.628674,
        max_lon=-4.992047,
        min_lon=-10.992047,
        max_bounds=True,
    )
    # Add a search bar to the map
    geocoder = plugins.Geocoder(
        collapsed=False,
        position="topright",
        placeholder="Search | البحث",
    )
    m.add_child(geocoder)

    # Add Fullscreen button to the map
    fullscreen = plugins.Fullscreen(
        position="topright",
        title="Expand me | تكبير الخريطة",
        title_cancel="Exit me | تصغير الخريطة",
        force_separate_button=True,
    )
    m.add_child(fullscreen)

    # Satellite View from Mapbox
    tileurl = "https://marocmap.ikiker.com/maroc/{z}/{x}/{y}.png"
    folium.TileLayer(
        tiles=tileurl,
        attr="Maroc Map",
        name="Maroc Map",
        overlay=False,
        control=False,
    ).add_to(m)

    # Add danger zones
    add_epicentre_to_map(m)
    add_danger_distances_to_map(m)
    emergency_fgs = init_emergency_fgs(m)
    intervention_fgs = init_intervention_fgs(m)

    # Add a LayerControl to the map to toggle between layers (Satellite View and Default One)
    folium.LayerControl().add_to(m)

    # Add detect location button
    plugins.LocateControl(
        position="topleft",
        drawCircle=False,
        flyTo=True,
        strings={"title": "My location | موقعي", "popup": "My location | موقعي"},
    ).add_to(m)

    return m, emergency_fgs, intervention_fgs