Spaces:
Sleeping
Sleeping
Commit
•
1e15fb5
1
Parent(s):
225e905
update rescue map
Browse files- .gitignore +23 -0
- .streamlit/config.toml +28 -0
- app.py +374 -150
- utils.py → src/map_utils.py +1 -0
- src/text_content.py +88 -0
- src/utils.py +153 -0
.gitignore
ADDED
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Byte-compiled / optimized / DLL files
|
2 |
+
__pycache__/
|
3 |
+
|
4 |
+
# C extensions
|
5 |
+
*.so
|
6 |
+
|
7 |
+
# Distribution / packaging
|
8 |
+
bin/
|
9 |
+
build/
|
10 |
+
develop-eggs/
|
11 |
+
dist/
|
12 |
+
eggs/
|
13 |
+
lib/
|
14 |
+
lib64/
|
15 |
+
parts/
|
16 |
+
sdist/
|
17 |
+
var/
|
18 |
+
*.egg-info/
|
19 |
+
.installed.cfg
|
20 |
+
*.egg
|
21 |
+
|
22 |
+
# vscode
|
23 |
+
.vscode
|
.streamlit/config.toml
ADDED
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
[browser]
|
2 |
+
|
3 |
+
# Internet address where users should point their browsers in order to
|
4 |
+
# connect to the app. Can be IP address or DNS name and path.
|
5 |
+
|
6 |
+
# This is used to:
|
7 |
+
# - Set the correct URL for CORS and XSRF protection purposes.
|
8 |
+
# - Show the URL on the terminal
|
9 |
+
# - Open the browser
|
10 |
+
|
11 |
+
# Default: "localhost"
|
12 |
+
serverAddress = "localhost"
|
13 |
+
|
14 |
+
# Whether to send usage statistics to Streamlit.
|
15 |
+
|
16 |
+
# Default: true
|
17 |
+
gatherUsageStats = false
|
18 |
+
|
19 |
+
# Port where users should point their browsers in order to connect to the
|
20 |
+
# app.
|
21 |
+
|
22 |
+
# This is used to:
|
23 |
+
# - Set the correct URL for CORS and XSRF protection purposes.
|
24 |
+
# - Show the URL on the terminal
|
25 |
+
# - Open the browser
|
26 |
+
|
27 |
+
# Default: whatever value is set in server.port.
|
28 |
+
serverPort = 8501
|
app.py
CHANGED
@@ -1,165 +1,389 @@
|
|
1 |
import os
|
2 |
import time
|
3 |
-
import
|
|
|
4 |
import folium
|
5 |
import pandas as pd
|
6 |
-
from datetime import datetime
|
7 |
import streamlit as st
|
8 |
-
from streamlit_folium import st_folium
|
9 |
-
from utils import legend_macro
|
10 |
from huggingface_hub import HfApi
|
|
|
11 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
12 |
|
13 |
TOKEN = os.environ.get("HF_TOKEN", None)
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
if auto_refresh:
|
26 |
-
number = st.sidebar.number_input("Refresh rate in seconds", value=st.session_state.sleep_time)
|
27 |
-
st.session_state.sleep_time = number
|
28 |
-
|
29 |
-
session = requests.Session()
|
30 |
-
@st.cache_data(persist=True)
|
31 |
-
def parse_latlng_from_link(url):
|
32 |
-
try:
|
33 |
-
# extract latitude and longitude from gmaps link
|
34 |
-
if "@" not in url:
|
35 |
-
# We first need to get the redirect URL
|
36 |
-
resp = session.head(url, allow_redirects=True)
|
37 |
-
url = resp.url
|
38 |
-
latlng = url.split('@')[1].split(',')[0:2]
|
39 |
-
return [float(latlng[0]), float(latlng[1])]
|
40 |
-
except Exception as e:
|
41 |
-
print(f"Error parsing latlng from link: {e}")
|
42 |
-
return None
|
43 |
-
|
44 |
-
def parse_gg_sheet_interventions(url):
|
45 |
-
df = pd.read_csv(url)
|
46 |
-
return df.assign(latlng=df.iloc[:, 3].apply(parse_latlng_from_link))
|
47 |
-
|
48 |
-
def parse_gg_sheet(url):
|
49 |
-
url = url.replace("edit#gid=", "export?format=csv&gid=")
|
50 |
-
df = pd.read_csv(url)
|
51 |
-
|
52 |
-
# parse latlng (column 4) to [lat, lng]
|
53 |
-
def parse_latlng(latlng):
|
54 |
-
try:
|
55 |
-
lat, lng = latlng.split(",")
|
56 |
-
return [float(lat), float(lng)]
|
57 |
-
except Exception as e:
|
58 |
-
print(f"Error parsing latlng: {e}")
|
59 |
-
return None
|
60 |
-
|
61 |
-
return df.assign(latlng=df.iloc[:, 4].apply(parse_latlng))
|
62 |
-
|
63 |
-
df = parse_gg_sheet(
|
64 |
-
"https://docs.google.com/spreadsheets/d/1gYoBBiBo1L18IVakHkf3t1fOGvHWb23loadyFZUeHJs/edit#gid=966953708"
|
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 |
selected_options = []
|
95 |
|
96 |
-
#st.markdown("👉 **Choose request type / اختر نوع الطلب**")
|
97 |
-
#col1, col2, col3, col4, col5 = st.columns([2, 3, 2, 3, 4])
|
98 |
-
#cols = [col1, col2, col3, col4, col5]
|
99 |
-
|
100 |
-
for i, option in enumerate(options):
|
101 |
-
#checked = cols[i].checkbox(headers_mapping[option], value=True)
|
102 |
-
#if checked:
|
103 |
-
selected_options.append(headers_mapping[option])
|
104 |
-
|
105 |
-
arabic_options = [e.split("/")[1] for e in selected_options]
|
106 |
-
df['id'] = df.index
|
107 |
-
filtered_df = df[df['ما هي احتياجاتك؟ (أضفها إذا لم يتم ذكرها)'].isin(arabic_options)]
|
108 |
-
selected_headers = [headers_mapping[request] for request in arabic_options]
|
109 |
-
|
110 |
-
# select interventions
|
111 |
-
#st.markdown("👇 **View past or planned interventions / عرض عمليات المساعدة السابقة أو المخطط لها**")
|
112 |
-
#show_interventions = st.checkbox("Display Interventions عرض التدخلات", value=True)
|
113 |
-
|
114 |
-
m = folium.Map(
|
115 |
-
location=[31.228674, -7.992047],
|
116 |
-
zoom_start=8.5,
|
117 |
-
min_zoom=8.5,
|
118 |
-
max_lat=35.628674,
|
119 |
-
min_lat=29.628674,
|
120 |
-
max_lon=-4.992047,
|
121 |
-
min_lon=-10.992047,
|
122 |
-
max_bounds=True,
|
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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
import os
|
2 |
import time
|
3 |
+
from datetime import datetime
|
4 |
+
|
5 |
import folium
|
6 |
import pandas as pd
|
|
|
7 |
import streamlit as st
|
|
|
|
|
8 |
from huggingface_hub import HfApi
|
9 |
+
from streamlit_folium import st_folium
|
10 |
|
11 |
+
from src.text_content import (
|
12 |
+
COLOR_MAPPING,
|
13 |
+
CREDITS_TEXT,
|
14 |
+
HEADERS_MAPPING,
|
15 |
+
ICON_MAPPING,
|
16 |
+
INTRO_TEXT_AR,
|
17 |
+
INTRO_TEXT_EN,
|
18 |
+
INTRO_TEXT_FR,
|
19 |
+
LOGO,
|
20 |
+
REVIEW_TEXT,
|
21 |
+
SLOGAN,
|
22 |
+
)
|
23 |
+
from src.utils import add_latlng_col, init_map, parse_gg_sheet
|
24 |
|
25 |
TOKEN = os.environ.get("HF_TOKEN", None)
|
26 |
+
REQUESTS_URL = "https://docs.google.com/spreadsheets/d/1gYoBBiBo1L18IVakHkf3t1fOGvHWb23loadyFZUeHJs/edit#gid=966953708"
|
27 |
+
INTERVENTIONS_URL = "https://docs.google.com/spreadsheets/d/1eXOTqunOWWP8FRdENPs4cU9ulISm4XZWYJJNR1-SrwY/edit#gid=2089222765"
|
28 |
+
api = HfApi(TOKEN)
|
29 |
+
|
30 |
+
|
31 |
+
# Initialize Streamlit Config
|
32 |
+
st.set_page_config(
|
33 |
+
layout="wide",
|
34 |
+
initial_sidebar_state="collapsed",
|
35 |
+
page_icon="🤝",
|
36 |
+
page_title="Nt3awnou Map نتعاونو",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
37 |
)
|
38 |
+
|
39 |
+
# """
|
40 |
+
# <style>
|
41 |
+
# .block-container {
|
42 |
+
# padding-top: 1rem;
|
43 |
+
# padding-bottom: 0rem;
|
44 |
+
# }
|
45 |
+
# </style>
|
46 |
+
# <div style="text-align: center;">
|
47 |
+
# <img src="https://storage.googleapis.com/storage-annarabic/Nt3awnou(1).png" width="200" height="200">
|
48 |
+
# </div>
|
49 |
+
# """
|
50 |
+
|
51 |
+
st.markdown(
|
52 |
+
"""
|
53 |
+
<style>
|
54 |
+
.block-container {
|
55 |
+
padding-top: 0rem;
|
56 |
+
padding-bottom: 0rem;
|
57 |
+
padding-left: 0rem;
|
58 |
+
padding-right: 0rem;
|
59 |
+
}
|
60 |
+
</style>
|
61 |
+
""",
|
62 |
+
unsafe_allow_html=True,
|
63 |
)
|
64 |
|
65 |
+
# # Initialize States
|
66 |
+
# if "sleep_time" not in st.session_state:
|
67 |
+
# st.session_state.sleep_time = 2
|
68 |
+
# if "auto_refresh" not in st.session_state:
|
69 |
+
# st.session_state.auto_refresh = False
|
70 |
+
|
71 |
+
# auto_refresh = st.sidebar.checkbox("Auto Refresh?", st.session_state.auto_refresh)
|
72 |
+
# if auto_refresh:
|
73 |
+
# number = st.sidebar.number_input(
|
74 |
+
# "Refresh rate in seconds", value=st.session_state.sleep_time
|
75 |
+
# )
|
76 |
+
# st.session_state.sleep_time = number
|
77 |
+
|
78 |
+
|
79 |
+
# Streamlit functions
|
80 |
+
def display_interventions(interventions_df):
|
81 |
+
"""Display NGO interventions on the map"""
|
82 |
+
for index, row in interventions_df.iterrows():
|
83 |
+
village_status = row[interventions_df.columns[7]]
|
84 |
+
if pd.isna(village_status):
|
85 |
+
continue
|
86 |
+
if (
|
87 |
+
row[interventions_df.columns[5]]
|
88 |
+
== "Intervention prévue dans le futur / Planned future intervention"
|
89 |
+
):
|
90 |
+
# future intervention
|
91 |
+
color_mk = "pink"
|
92 |
+
status = "Planned ⌛"
|
93 |
+
elif (
|
94 |
+
row[interventions_df.columns[5]]
|
95 |
+
!= "Intervention prévue dans le futur / Planned future intervention"
|
96 |
+
and village_status
|
97 |
+
!= "Critique, Besoin d'aide en urgence / Critical, in urgent need of help"
|
98 |
+
):
|
99 |
+
# past intervention and village not in a critical condition
|
100 |
+
color_mk = "green"
|
101 |
+
status = "Done ✅"
|
102 |
+
|
103 |
+
else:
|
104 |
+
color_mk = "darkgreen"
|
105 |
+
status = "Partial ⚠️"
|
106 |
+
|
107 |
+
intervention_type = row[interventions_df.columns[6]].split("/")[0].strip()
|
108 |
+
org = row[interventions_df.columns[1]]
|
109 |
+
city = row[interventions_df.columns[9]]
|
110 |
+
date = row[interventions_df.columns[4]]
|
111 |
+
population = row[interventions_df.columns[11]]
|
112 |
+
intervention_info = f"<b>Intervention Status:</b> {status}<br><b>Village Status:</b> {village_status.split('/')[0]}<br><b>Org:</b> {org}<br><b>Intervention:</b> {intervention_type}<br><b>Population:</b> {population}<br><b>📅 Date:</b> {date}"
|
113 |
+
if row["latlng"] is None:
|
114 |
+
continue
|
115 |
+
|
116 |
+
fg.add_child(folium.Marker(
|
117 |
+
location=row["latlng"],
|
118 |
+
tooltip=city,
|
119 |
+
popup=folium.Popup(intervention_info, max_width=300),
|
120 |
+
icon=folium.Icon(color=color_mk),
|
121 |
+
))
|
122 |
+
|
123 |
+
|
124 |
+
def show_requests(filtered_df):
|
125 |
+
"""Display victim requests on the map"""
|
126 |
+
for index, row in filtered_df.iterrows():
|
127 |
+
request_type = row["ما هي احتياجاتك؟ (أضفها إذا لم يتم ذكرها)"]
|
128 |
+
long_lat = row[
|
129 |
+
"هل يمكنك تقديم الإحداثيات الدقيقة للموقع؟ (ادا كنت لا توجد بعين المكان) متلاً \n31.01837503440344, -6.781405948842175"
|
130 |
+
]
|
131 |
+
maps_url = f"https://maps.google.com/?q={long_lat}"
|
132 |
+
display_text = f'<b>Request Type:</b> {request_type}<br><b>Id:</b> {row["id"]}<br><a href="{maps_url}" target="_blank" rel="noopener noreferrer"><b>Google Maps</b></a>'
|
133 |
+
icon_name = ICON_MAPPING.get(request_type, "info-sign")
|
134 |
+
if row["latlng"] is None:
|
135 |
+
continue
|
136 |
+
|
137 |
+
fg.add_child(folium.Marker(
|
138 |
+
location=row["latlng"],
|
139 |
+
tooltip=row[" لأي جماعة / قيادة / دوار تنتمون ؟"]
|
140 |
+
if not pd.isna(row[" لأي جماعة / قيادة / دوار تنتمون ؟"])
|
141 |
+
else None,
|
142 |
+
popup=folium.Popup(display_text, max_width=300),
|
143 |
+
icon=folium.Icon(
|
144 |
+
color=COLOR_MAPPING.get(request_type, "blue"), icon=icon_name
|
145 |
+
),
|
146 |
+
))
|
147 |
+
|
148 |
+
|
149 |
+
def display_google_sheet_tables(data_url):
|
150 |
+
"""Display the google sheet tables for requests and interventions"""
|
151 |
+
st.markdown(
|
152 |
+
f"""<iframe src="{data_url}" width="100%" height="600px"></iframe>""",
|
153 |
+
unsafe_allow_html=True,
|
154 |
+
)
|
155 |
+
|
156 |
+
|
157 |
+
def display_dataframe(df, drop_cols, data_url, search_id=True, status=False, for_help_requests=False):
|
158 |
+
"""Display the dataframe in a table"""
|
159 |
+
col_1, col_2 = st.columns([1, 1])
|
160 |
+
|
161 |
+
with col_1:
|
162 |
+
query = st.text_input(
|
163 |
+
"🔍 Search for information / بحث عن المعلومات",
|
164 |
+
key=f"search_requests_{int(search_id)}",
|
165 |
+
)
|
166 |
+
with col_2:
|
167 |
+
if search_id:
|
168 |
+
id_number = st.number_input(
|
169 |
+
"🔍 Search for an id / بحث عن رقم",
|
170 |
+
min_value=0,
|
171 |
+
max_value=len(filtered_df),
|
172 |
+
value=0,
|
173 |
+
step=1,
|
174 |
+
)
|
175 |
+
if status:
|
176 |
+
selected_status = st.selectbox(
|
177 |
+
"🗓️ Status / حالة",
|
178 |
+
["all / الكل", "Done / تم", "Planned / مخطط لها"],
|
179 |
+
key="status",
|
180 |
+
)
|
181 |
+
|
182 |
+
if query:
|
183 |
+
# Filtering the dataframe based on the query
|
184 |
+
mask = df.apply(lambda row: row.astype(str).str.contains(query).any(), axis=1)
|
185 |
+
display_df = df[mask]
|
186 |
+
else:
|
187 |
+
display_df = df
|
188 |
+
|
189 |
+
display_df = display_df.drop(drop_cols, axis=1)
|
190 |
+
|
191 |
+
if search_id and id_number:
|
192 |
+
display_df = display_df[display_df["id"] == id_number]
|
193 |
+
|
194 |
+
if status:
|
195 |
+
target = "Pouvez-vous nous préciser si vous êtes déjà intervenus ou si vous prévoyez de le faire | Tell us if you already made the intervention, or if you're planning to do it"
|
196 |
+
if selected_status == "Done / تم":
|
197 |
+
display_df = display_df[
|
198 |
+
display_df[target] == "Intervention déjà passée / Past intevention"
|
199 |
+
]
|
200 |
+
|
201 |
+
elif selected_status == "Planned / مخطط لها":
|
202 |
+
display_df = display_df[
|
203 |
+
display_df[target] != "Intervention déjà passée / Past intevention"
|
204 |
+
]
|
205 |
+
|
206 |
+
st.dataframe(display_df, height=500)
|
207 |
+
st.markdown(
|
208 |
+
f"To view the full Google Sheet for advanced filtering go to: {data_url} **لعرض الورقة كاملة، اذهب إلى**"
|
209 |
+
)
|
210 |
+
# if we want to check hidden contact information
|
211 |
+
if for_help_requests:
|
212 |
+
st.markdown(
|
213 |
+
"We are hiding contact information to protect the privacy of the victims. If you are an NGO and want to contact the victims, please contact us at [email protected]",
|
214 |
+
)
|
215 |
+
st.markdown(
|
216 |
+
"""
|
217 |
+
<div style="text-align: left;">
|
218 |
+
<a href="mailto:[email protected]">[email protected]</a> نحن نخفي معلومات الاتصال لحماية خصوصية الضحايا. إذا كنت جمعية وتريد الاتصال بالضحايا، يرجى الاتصال بنا على
|
219 |
+
</div>
|
220 |
+
""",
|
221 |
+
unsafe_allow_html=True,
|
222 |
+
)
|
223 |
+
|
224 |
+
|
225 |
+
def id_review_submission():
|
226 |
+
"""Id review submission form"""
|
227 |
+
st.subheader("🔍 Review of requests")
|
228 |
+
st.markdown(REVIEW_TEXT)
|
229 |
+
|
230 |
+
id_to_review = st.number_input(
|
231 |
+
"Enter id / أدخل الرقم", min_value=0, max_value=len(df), value=0, step=1
|
232 |
+
)
|
233 |
+
reason_for_review = st.text_area("Explain why / أدخل سبب المراجعة")
|
234 |
+
if st.button("Submit / أرسل"):
|
235 |
+
if reason_for_review == "":
|
236 |
+
st.error("Please enter a reason / الرجاء إدخال سبب")
|
237 |
+
else:
|
238 |
+
filename = f"review_id_{id_to_review}_{datetime.now().strftime('%Y-%m-%d_%H-%M-%S')}.txt"
|
239 |
+
with open(filename, "w") as f:
|
240 |
+
f.write(f"id: {id_to_review}, explanation: {reason_for_review}\n")
|
241 |
+
api.upload_file(
|
242 |
+
path_or_fileobj=filename,
|
243 |
+
path_in_repo=filename,
|
244 |
+
repo_id="nt3awnou/review_requests",
|
245 |
+
repo_type="dataset",
|
246 |
+
)
|
247 |
+
st.success(
|
248 |
+
"Submitted at https://huggingface.co/datasets/nt3awnou/review_requests/ تم الإرسال"
|
249 |
+
)
|
250 |
+
|
251 |
+
|
252 |
+
# # Logo and Title
|
253 |
+
# st.markdown(LOGO, unsafe_allow_html=True)
|
254 |
+
# # st.title("Nt3awnou نتعاونو")
|
255 |
+
# st.markdown(SLOGAN, unsafe_allow_html=True)
|
256 |
+
|
257 |
+
# Load data and initialize map with plugins
|
258 |
+
df = parse_gg_sheet(REQUESTS_URL)
|
259 |
+
df = add_latlng_col(df, process_column=15)
|
260 |
+
interventions_df = parse_gg_sheet(INTERVENTIONS_URL)
|
261 |
+
interventions_df = add_latlng_col(interventions_df, process_column=12)
|
262 |
+
m = init_map()
|
263 |
+
fg = folium.FeatureGroup(name="Markers")
|
264 |
+
|
265 |
+
# Selection of requests
|
266 |
+
options = [
|
267 |
+
"إغاثة",
|
268 |
+
"مساعدة طبية",
|
269 |
+
"مأوى",
|
270 |
+
"طعام وماء",
|
271 |
+
"مخاطر (تسرب الغاز، تلف في الخدمات العامة...)",
|
272 |
+
]
|
273 |
selected_options = []
|
274 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
275 |
|
276 |
+
|
277 |
+
df["id"] = df.index
|
278 |
+
filtered_df = df
|
279 |
+
|
280 |
+
display_interventions(interventions_df)
|
281 |
+
|
282 |
+
# # Show requests
|
283 |
+
show_requests(df)
|
284 |
+
|
285 |
+
st_folium(m, use_container_width=True, returned_objects=[], feature_group_to_add=fg, key="map")
|
286 |
+
# tab_ar, tab_en, tab_fr = st.tabs(["العربية", "English", "Français"])
|
287 |
+
|
288 |
+
|
289 |
+
# with tab_en:
|
290 |
+
# st.markdown(INTRO_TEXT_EN, unsafe_allow_html=True)
|
291 |
+
# with tab_ar:
|
292 |
+
# st.markdown(INTRO_TEXT_AR, unsafe_allow_html=True)
|
293 |
+
# with tab_fr:
|
294 |
+
# st.markdown(INTRO_TEXT_FR, unsafe_allow_html=True)
|
295 |
+
|
296 |
+
# # Requests table
|
297 |
+
# st.divider()
|
298 |
+
# st.subheader("📝 **Table of requests / جدول الطلبات**")
|
299 |
+
# drop_cols = [
|
300 |
+
# "(عند الامكان) رقم هاتف شخص موجود في عين المكان",
|
301 |
+
# "الرجاء الضغط على الرابط التالي لمعرفة موقعك إذا كان متاحا",
|
302 |
+
# "GeoStamp",
|
303 |
+
# "GeoCode",
|
304 |
+
# "GeoAddress",
|
305 |
+
# "Status",
|
306 |
+
# "id",
|
307 |
+
# ]
|
308 |
+
# display_dataframe(filtered_df, drop_cols, REQUESTS_URL, search_id=True, for_help_requests=True)
|
309 |
+
|
310 |
+
# # Interventions table
|
311 |
+
# st.divider()
|
312 |
+
# st.subheader("📝 **Table of interventions / جدول التدخلات**")
|
313 |
+
# display_dataframe(
|
314 |
+
# interventions_df,
|
315 |
+
# [], # We show NGOs contact information
|
316 |
+
# INTERVENTIONS_URL,
|
317 |
+
# search_id=False,
|
318 |
+
# status=True,
|
319 |
+
# for_help_requests=False,
|
320 |
+
# )
|
321 |
+
|
322 |
+
# # Submit an id for review
|
323 |
+
# st.divider()
|
324 |
+
# id_review_submission()
|
325 |
+
|
326 |
+
|
327 |
+
# # Donations can be made to the gouvernmental fund under the name
|
328 |
+
# st.divider()
|
329 |
+
# st.subheader("📝 **Donations / التبرعات / Dons**")
|
330 |
+
# tab_ar, tab_en, tab_fr = st.tabs(["العربية", "English", "Français"])
|
331 |
+
# with tab_en:
|
332 |
+
# st.markdown(
|
333 |
+
# """
|
334 |
+
# <div style="text-align: center;">
|
335 |
+
# <h4>The official bank account dedicated to tackle the consequences of the earthquake is:</h4>
|
336 |
+
# <b>Account number:</b>
|
337 |
+
# <h2>126</h2>
|
338 |
+
# <b>RIB:</b> 001-810-0078000201106203-18
|
339 |
+
# <br>
|
340 |
+
# <b>For the money transfers coming from outside Morocco</b>
|
341 |
+
# <br>
|
342 |
+
# <b>IBAN:</b> MA64001810007800020110620318
|
343 |
+
# <br>
|
344 |
+
# """,
|
345 |
+
# unsafe_allow_html=True,
|
346 |
+
# )
|
347 |
+
# with tab_ar:
|
348 |
+
# st.markdown(
|
349 |
+
# """
|
350 |
+
# <div style="text-align: center;">
|
351 |
+
# <h4>الحساب البنكي الرسمي المخصص لمواجهة عواقب الزلزال</h4>
|
352 |
+
# <b>رقم الحساب</b>
|
353 |
+
# <h2>126</h2>
|
354 |
+
# <b>RIB:</b> 001-810-0078000201106203-18
|
355 |
+
# <br>
|
356 |
+
# <b>للتحويلات القادمة من خارج المغرب</b>
|
357 |
+
# <br>
|
358 |
+
# <b>IBAN:</b> MA64001810007800020110620318
|
359 |
+
# <br>
|
360 |
+
# </div>
|
361 |
+
# """,
|
362 |
+
# unsafe_allow_html=True,
|
363 |
+
# )
|
364 |
+
# with tab_fr:
|
365 |
+
# st.markdown(
|
366 |
+
# """
|
367 |
+
# <div style="text-align: center;">
|
368 |
+
# <h4>Le compte bancaire officiel dédié à la lutte contre les conséquences du séisme est le suivant:</h4>
|
369 |
+
# <b>Numéro de compte:</b>
|
370 |
+
# <h2>126</h2>
|
371 |
+
# <b>RIB:</b> 001-810-0078000201106203-18
|
372 |
+
# <br>
|
373 |
+
# <b>Pour les transferts d'argent en provenance de l'étranger</b>
|
374 |
+
# <br>
|
375 |
+
# <b>IBAN:</b> MA64001810007800020110620318
|
376 |
+
# <br>
|
377 |
+
# """,
|
378 |
+
# unsafe_allow_html=True,
|
379 |
+
# )
|
380 |
+
|
381 |
+
|
382 |
+
# # Credits
|
383 |
+
# st.markdown(
|
384 |
+
# CREDITS_TEXT,
|
385 |
+
# unsafe_allow_html=True,
|
386 |
+
# )
|
387 |
+
# if auto_refresh:
|
388 |
+
# time.sleep(number)
|
389 |
+
# st.experimental_rerun()
|
utils.py → src/map_utils.py
RENAMED
@@ -46,6 +46,7 @@ template = """
|
|
46 |
<li><span style='background:#37A8DA;opacity:0.7;'></span>Food & Water / طعام وماء</li>
|
47 |
<li><span style='background:#575757;opacity:0.7;'></span>Danger / مخاطر (تسرب الغاز، تلف في الخدمات العامة...)</li>
|
48 |
<li><span style='background:#6EAA25;opacity:0.7;'></span>Done / تم</li>
|
|
|
49 |
<li><span style='background:#FF91E8;opacity:0.7;'></span>Planned / مخطط لها</li>
|
50 |
</ul>
|
51 |
</div>
|
|
|
46 |
<li><span style='background:#37A8DA;opacity:0.7;'></span>Food & Water / طعام وماء</li>
|
47 |
<li><span style='background:#575757;opacity:0.7;'></span>Danger / مخاطر (تسرب الغاز، تلف في الخدمات العامة...)</li>
|
48 |
<li><span style='background:#6EAA25;opacity:0.7;'></span>Done / تم</li>
|
49 |
+
<li><span style='background:#023020;opacity:0.7;'></span>Partial / تم جزئيا</li>
|
50 |
<li><span style='background:#FF91E8;opacity:0.7;'></span>Planned / مخطط لها</li>
|
51 |
</ul>
|
52 |
</div>
|
src/text_content.py
ADDED
@@ -0,0 +1,88 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
INTRO_TEXT_EN = """
|
2 |
+
<div style="text-align: left;">
|
3 |
+
Nt3awnou نتعاونو is a collaborative platform dedicated to aiding individuals impacted by the recent earthquake in Morocco. Our core mission is to streamline and coordinate timely assistance for everyone affected. How do we achieve this? We assist those in need by allowing them to communicate their location and the specific aid they require, either by completing a form or sending a voice message via WhatsApp to a designated number. Once we receive and process this information, it can be viewed in our dashboard, which allows NGOs to organize and precisely target their interventions, ensuring swift assistance reaches those in need. Any organization that has taken initiative in a particular area can notify us by completing a dedicated form. This data is also incorporated into the dashboard so that other NGOs can help affected areas that still haven't received help.
|
4 |
+
<br>⚠️ Warning : There are still rocks falling down the mountains, making the roads to the affected areas very dangerous. We advise volunteers to donate directly to specialized NGOs.<br>
|
5 |
+
<br>
|
6 |
+
<b>✉️ You can contact us via email at </b><a href="mailto:[email protected]">[email protected]</a> <br>
|
7 |
+
<b>📝 Help us report more people in need by filling this form </b><a href="https://forms.gle/nZNCUVog9ka2Vdqu6">https://forms.gle/nZNCUVog9ka2Vdqu6</a> <br>
|
8 |
+
<b>📝 NGOs can report their interventions by filling this form </b><a href="https://forms.gle/PsNSuHHjTTgwQMmVA">https://forms.gle/PsNSuHHjTTgwQMmVA</a>
|
9 |
+
</div>
|
10 |
+
<br>
|
11 |
+
"""
|
12 |
+
|
13 |
+
INTRO_TEXT_AR = """
|
14 |
+
<div style="text-align: right;">
|
15 |
+
|
16 |
+
نتعاونو هي منصة تعاونية لمساعدة الأفراد المتضررين من الزلزال الأخير في المغرب. مهمتنا هي تسهيل تقديم المساعدة في الوقت المناسب و بفاعلية و تنظيم لجميع المتضررين. كيفاش؟ كنعاونو الناس لي محتاجين للمساعدة إعلمونا بمكانهم و نوع المساعدة لي محتاجين ليها سواء عن طريق ملأ الاستمارة أو عن طريق إرسال تسجيل صوتي عبر واتساب إلى رقم مخصص. بعد معالجة هاد المعلومات، كنجمعوهم فخريطة كتمكن الجمعيات من تنظيم و استهداف تدخلاتهم بدقة باش توصل المساعدة للناس لي محتاجين في وقت أسرع. و كل جمعية قامت باللازم في منطقة معينة تقدر تعلمنا عن طريق ملأ استمارة مخصصة لهاد الأمر. هاد المعلومات كذلك كتضاف للخريطة باش باقي الجمعيات يتاجهو لمناطق أخرى مازال ماوصلاتهم مساعدة.
|
17 |
+
<br> تحذير : نظرا لخطورة الطرقان بسبب الحجر اللي كيطيح من الجبال، ننصح المتطوعين اللي بغاو يساعدو المناطق المتضررة يتبرعو عن طريق الجمعيات المختصة⚠️
|
18 |
+
<br>
|
19 |
+
[email protected] <b>المتطوعين ليبغاو يعاونوا يقدرو يتصلوا معنا عبر البريد ✉️ </b><br>
|
20 |
+
https://forms.gle/nZNCUVog9ka2Vdqu6 <b>: ساعدونا نبلغو الناس ليمحتاجين فهاد الاستمارة 📝 </b><br>
|
21 |
+
https://forms.gle/PsNSuHHjTTgwQMmVA <b>: الجمعيات لي عندهم تدخلات يقدرو يبلغونا عبر هاد الاستمار ة📝</b>
|
22 |
+
</div>
|
23 |
+
<br>
|
24 |
+
"""
|
25 |
+
|
26 |
+
INTRO_TEXT_FR = """
|
27 |
+
<div style="text-align: left;">
|
28 |
+
Nt3awnou نتعاونو est une plateforme collaborative dédiée à l'aide aux personnes touchées par le récent tremblement de terre au Maroc. Notre mission principale est de rationaliser et de coordonner une assistance rapide pour toutes les personnes touchées. Comment y parvenons-nous ? Nous aidons les personnes dans le besoin en leur permettant de communiquer leur localisation et l'aide spécifique dont elles ont besoin, soit en remplissant un formulaire, soit en envoyant un message vocal via WhatsApp à un numéro désigné. Une fois reçues et traitées, ces informations peuvent être consultées dans notre tableau de bord, qui permet aux associations d'organiser et de cibler précisément leurs interventions, afin que l'aide parvienne rapidement à ceux qui en ont besoin. Toute organisation ayant pris une initiative dans une zone particulière peut nous en informer en remplissant un formulaire prévu à cet effet. Ces données sont également intégrées au tableau de bord afin que d'autres associations puissent aider les zones touchées qui n'ont pas encore reçu d'aide.
|
29 |
+
⚠️ Avertissement : Il y a encore des chutes de pierres dans les montagnes, ce qui rend les routes vers les zones touchées très dangereuses. Nous conseillons aux volontaires de faire des dons directement aux associations spécialisées.
|
30 |
+
<br>
|
31 |
+
<b>✉️ Vous pouvez nous contacter par courrier électronique à l'adresse suivante </b><a href="mailto:[email protected]">[email protected]</a> <br>
|
32 |
+
<b>📝 Aidez-nous à signaler plus de personnes dans le besoin en remplissant ce formulaire : </b><a href="https://forms.gle/nZNCUVog9ka2Vdqu6">https://forms.gle/nZNCUVog9ka2Vdqu6</a> <br>
|
33 |
+
<b>📝 Les associations peuvent signaler leurs interventions en remplissant ce formulaire : </b><a href="https://forms.gle/PsNSuHHjTTgwQMmVA">https://forms.gle/PsNSuHHjTTgwQMmVA</a>
|
34 |
+
</div>
|
35 |
+
<br>
|
36 |
+
"""
|
37 |
+
|
38 |
+
SLOGAN = """
|
39 |
+
<div style="text-align: center;">
|
40 |
+
<h4>وَمَنْ أَحْيَاهَا فَكَأَنَّمَا أَحْيَا النَّاسَ جَمِيعاً</h4>
|
41 |
+
</div>
|
42 |
+
"""
|
43 |
+
|
44 |
+
HEADERS_MAPPING = {
|
45 |
+
"إغاثة" : "Rescue | إغاثة | Secours",
|
46 |
+
"مساعدة طبية": "Medical Assistance | مساعدة طبية | Assistance médicale",
|
47 |
+
"مأوى": "Shelter | مأوى | Abri",
|
48 |
+
"طعام وماء": "Food & Water | طعام وماء | Nourriture et eau",
|
49 |
+
"مخاطر (تسرب الغاز، تلف في الخدمات العامة...)": "Danger | مخاطر (تسرب الغاز، تلف في الخدمات العامة...) | Danger",
|
50 |
+
}
|
51 |
+
|
52 |
+
COLOR_MAPPING = {
|
53 |
+
"إغاثة": "red",
|
54 |
+
"مساعدة طبية": "orange",
|
55 |
+
"مأوى": "beige",
|
56 |
+
"طعام وماء": "blue",
|
57 |
+
"مخاطر (تسرب الغاز، تلف في الخدمات العامة...)": "gray",
|
58 |
+
}
|
59 |
+
|
60 |
+
ICON_MAPPING = {
|
61 |
+
"إغاثة": "bell", # life ring icon for rescue
|
62 |
+
"مساعدة طبية": "heart", # medical kit for medical assistance
|
63 |
+
"مأوى": "home", # home icon for shelter
|
64 |
+
"طعام وماء": "cutlery", # cutlery (fork and knife) for food & water
|
65 |
+
"مخاطر (تسرب الغاز، تلف في الخدمات العامة...)": "Warning" # warning triangle for dangers
|
66 |
+
}
|
67 |
+
|
68 |
+
CREDITS_TEXT = """
|
69 |
+
<hr>
|
70 |
+
<div style="text-align: center;">
|
71 |
+
<p>By <b>Moroccans</b> for <b>Moroccans</b> 🤝</p>
|
72 |
+
<p>Bot powered by <a href="https://www.annarabic.com/">Annarabic</a></p>
|
73 |
+
<p>Collaboration made possible thanks to <a href="https://summerschool.morocco.ai/">AI Summer School</a></p>
|
74 |
+
"""
|
75 |
+
|
76 |
+
LOGO = """
|
77 |
+
<style>
|
78 |
+
.block-container {
|
79 |
+
padding-top: 1rem;
|
80 |
+
padding-bottom: 0rem;
|
81 |
+
}
|
82 |
+
</style>
|
83 |
+
<div style="text-align: center;">
|
84 |
+
<img src="https://storage.googleapis.com/storage-annarabic/Nt3awnou(1).png" width="200" height="200">
|
85 |
+
</div>
|
86 |
+
"""
|
87 |
+
|
88 |
+
REVIEW_TEXT = """**If a request should be reviewed or dropped submit its id here/ إذا كان يجب مراجعة أو حذف طلب، أدخل رقمه هنا:**"""
|
src/utils.py
ADDED
@@ -0,0 +1,153 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import folium
|
2 |
+
import pandas as pd
|
3 |
+
from folium import plugins
|
4 |
+
from src.map_utils import legend_macro
|
5 |
+
|
6 |
+
|
7 |
+
EPICENTER_LOCATION = [31.12210171476489, -8.42945837915193]
|
8 |
+
BORDER_COLOR = "black"
|
9 |
+
|
10 |
+
def parse_gg_sheet(url):
|
11 |
+
url = url.replace("edit#gid=", "export?format=csv&gid=")
|
12 |
+
df = pd.read_csv(url, on_bad_lines="warn")
|
13 |
+
return df
|
14 |
+
|
15 |
+
# Session for Requests
|
16 |
+
# session = requests.Session()
|
17 |
+
# @st.cache_data(persist=True)
|
18 |
+
# def parse_latlng_from_link(url):
|
19 |
+
# try:
|
20 |
+
# # extract latitude and longitude from gmaps link
|
21 |
+
# if "@" not in url:
|
22 |
+
# resp = session.head(url, allow_redirects=True)
|
23 |
+
# url = resp.url
|
24 |
+
# latlng = url.split("@")[1].split(",")[0:2]
|
25 |
+
# return [float(latlng[0]), float(latlng[1])]
|
26 |
+
# except Exception as e:
|
27 |
+
# return None
|
28 |
+
|
29 |
+
def add_latlng_col(df, process_column):
|
30 |
+
"""Add a latlng column to the dataframe"""
|
31 |
+
df = df.assign(latlng=df.iloc[:, process_column].apply(parse_latlng))
|
32 |
+
return df
|
33 |
+
|
34 |
+
# parse latlng (column 4) to [lat, lng]
|
35 |
+
import re
|
36 |
+
def parse_latlng(latlng):
|
37 |
+
if pd.isna(latlng):
|
38 |
+
return None
|
39 |
+
# lat, lng = latlng.split(",")
|
40 |
+
# return [float(lat), float(lng)]
|
41 |
+
|
42 |
+
try:
|
43 |
+
# check if it matches (30.9529832, -7.1010705) or (30.9529832,-7.1010705)
|
44 |
+
if re.match(r"\(\d+\.\d+,\s?-\d+\.\d+\)", latlng):
|
45 |
+
lat, lng = latlng[1:-1].split(",")
|
46 |
+
return [float(lat), float(lng)]
|
47 |
+
# check of it matches 30.9529832, -7.1010705 or 30.9529832,-7.1010705
|
48 |
+
elif re.match(r"\d+\.\d+,\s?-\d+\.\d+", latlng):
|
49 |
+
lat, lng = latlng.split(",")
|
50 |
+
return [float(lat), float(lng)]
|
51 |
+
# check if it matches 30,9529832, -7,1010705 or 30,9529832,-7,1010705, match1=30,9529832 and match2=-7,1010705
|
52 |
+
elif re.match(r"\d+,\d+,\s?-\d+,\d+", latlng):
|
53 |
+
d1, d2, d3, d4 = latlng.split(",")
|
54 |
+
return [float(".".join([d1, d2])), float(".".join([d3, d4]))]
|
55 |
+
except Exception as e:
|
56 |
+
print(f"Error parsing latlng: {latlng} Reason: {e}")
|
57 |
+
return None
|
58 |
+
print(f"Error parsing latlng: {latlng}")
|
59 |
+
return None
|
60 |
+
|
61 |
+
def add_epicentre_to_map(fg):
|
62 |
+
# Removed the spinner to not confuse the users as the map is already loaded
|
63 |
+
icon_epicentre = folium.plugins.BeautifyIcon(
|
64 |
+
icon='star',
|
65 |
+
border_color='#b3334f',
|
66 |
+
background_color='#b3334f',
|
67 |
+
text_color='white'
|
68 |
+
)
|
69 |
+
|
70 |
+
fg.add_child(folium.Marker(location=EPICENTER_LOCATION,
|
71 |
+
# popup="Epicenter مركز الزلزال",
|
72 |
+
tooltip="Epicenter مركز الزلزال",
|
73 |
+
icon=icon_epicentre))
|
74 |
+
|
75 |
+
|
76 |
+
|
77 |
+
def add_danger_distances_to_map(map_obj):
|
78 |
+
Danger_Distances_group = folium.FeatureGroup(name='Danger distances - earthquake magnitude 7 | مسافات الخطر - قوة الزلازل 7').add_to(map_obj)
|
79 |
+
|
80 |
+
zones = [
|
81 |
+
{"radius": 100000, "fill_opacity": 0.1, "weight": 1, "fill_color": "yellow", "tooltip": "50 to 100 km - Moderate risk area | منطقة خطر معتدلة"},
|
82 |
+
{"radius": 50000, "fill_opacity": 0.1, "weight": 1, "fill_color": "orange", "tooltip": "30 to 50 km - High risk zone | منطقة عالية المخاطر"},
|
83 |
+
{"radius": 30000, "fill_opacity": 0.2, "weight": 1, "fill_color": "#FF0000", "tooltip": "10 to 30 km - Very high risk zone | منطقة شديدة الخطورة"},
|
84 |
+
{"radius": 10000, "fill_opacity": 0.2, "weight": 0.2, "fill_color": "#8B0000", "tooltip": "0 to 10km - direct impact zone | منطقة التأثير المباشر"}
|
85 |
+
]
|
86 |
+
|
87 |
+
for zone in zones:
|
88 |
+
folium.Circle(
|
89 |
+
location=EPICENTER_LOCATION,
|
90 |
+
radius=zone["radius"],
|
91 |
+
color=BORDER_COLOR,
|
92 |
+
weight=zone["weight"],
|
93 |
+
fill_opacity=zone["fill_opacity"],
|
94 |
+
opacity=zone["fill_opacity"], # Assuming border opacity should match fill_opacity
|
95 |
+
fill_color=zone["fill_color"],
|
96 |
+
# tooltip=zone["tooltip"],
|
97 |
+
).add_to(Danger_Distances_group)
|
98 |
+
|
99 |
+
|
100 |
+
def init_map():
|
101 |
+
m = folium.Map(
|
102 |
+
location=[31.228674, -7.992047],
|
103 |
+
zoom_start=8.5,
|
104 |
+
min_zoom=8.5,
|
105 |
+
max_lat=35.628674,
|
106 |
+
min_lat=29.628674,
|
107 |
+
max_lon=-4.992047,
|
108 |
+
min_lon=-10.992047,
|
109 |
+
max_bounds=True,
|
110 |
+
)
|
111 |
+
# Add a search bar to the map
|
112 |
+
plugins.Geocoder(
|
113 |
+
collapsed=False,
|
114 |
+
position="topright",
|
115 |
+
placeholder="Search | البحث",
|
116 |
+
).add_to(m)
|
117 |
+
|
118 |
+
# Add Fullscreen button to the map
|
119 |
+
plugins.Fullscreen(
|
120 |
+
position="topright",
|
121 |
+
title="Expand me | تكبير الخريطة",
|
122 |
+
title_cancel="Exit me | تصغير الخريطة",
|
123 |
+
force_separate_button=True,
|
124 |
+
).add_to(m)
|
125 |
+
|
126 |
+
# Satellite View from Mapbox
|
127 |
+
tileurl = "https://marocmap.ikiker.com/maroc/{z}/{x}/{y}.png"
|
128 |
+
folium.TileLayer(
|
129 |
+
tiles=tileurl,
|
130 |
+
attr="Maroc Map",
|
131 |
+
name="Maroc Map",
|
132 |
+
overlay=False,
|
133 |
+
control=False,
|
134 |
+
).add_to(m)
|
135 |
+
|
136 |
+
# Add danger zones
|
137 |
+
add_epicentre_to_map(m)
|
138 |
+
add_danger_distances_to_map(m)
|
139 |
+
|
140 |
+
# Add a LayerControl to the map to toggle between layers (Satellite View and Default One)
|
141 |
+
folium.LayerControl().add_to(m)
|
142 |
+
|
143 |
+
# Add detect location button
|
144 |
+
plugins.LocateControl(
|
145 |
+
position="topleft",
|
146 |
+
drawCircle=False,
|
147 |
+
flyTo=True,
|
148 |
+
strings={"title": "My location | موقعي", "popup": "My location | موقعي"},
|
149 |
+
).add_to(m)
|
150 |
+
|
151 |
+
# Macro to add legend
|
152 |
+
m.get_root().add_child(legend_macro)
|
153 |
+
return m
|