Spaces:
Sleeping
Sleeping
Commit
·
a98c212
1
Parent(s):
55f21af
Update app.py
Browse files
app.py
CHANGED
|
@@ -7,32 +7,14 @@ import matplotlib.pyplot as plt
|
|
| 7 |
import matplotlib.colors as mcolors
|
| 8 |
import matplotlib.cm as cm
|
| 9 |
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
def load_data_antenna():
|
| 13 |
-
df_antenna = pd.read_csv(r'data/df_clean_v2.csv', engine = "pyarrow", dtype_backend = "pyarrow")
|
| 14 |
-
df_antenna['DATE'] = df_antenna['DATE'].astype('datetime64[ns]')
|
| 15 |
-
df_antenna['HEURE'] = pd.to_datetime(df_antenna['HEURE'], format = '%H:%M:%S').dt.time
|
| 16 |
-
df_antenna['ANNEE'] = df_antenna['ANNEE'].astype('int')
|
| 17 |
-
df_antenna['COMMUNE'] = df_antenna['COMMUNE'].astype('str')
|
| 18 |
-
df_antenna['LIEU_DIT'] = df_antenna['LIEU_DIT'].astype('str')
|
| 19 |
-
df_antenna['PRECISION_MILIEU'] = df_antenna['PRECISION_MILIEU'].astype('str')
|
| 20 |
-
df_antenna['DEPARTEMENT'] = df_antenna['DEPARTEMENT'].astype('str')
|
| 21 |
-
df_antenna['CODE_ESP'] = df_antenna['CODE_ESP'].astype('str')
|
| 22 |
-
df_antenna['MASSE'] = df_antenna['MASSE'].astype('str')
|
| 23 |
-
df_antenna['AB'] = df_antenna['AB'].astype('str')
|
| 24 |
-
df_antenna['SEXE'] = df_antenna['SEXE'].astype('str')
|
| 25 |
-
df_antenna['ACTION'] = df_antenna['ACTION'].astype('str')
|
| 26 |
-
df_antenna['ID_PIT'] = df_antenna['ID_PIT'].astype('str')
|
| 27 |
-
df_antenna['NUM_PIT'] = df_antenna['NUM_PIT'].astype('str')
|
| 28 |
-
df_antenna['LONG_L93'] = df_antenna['LONG_L93'].astype('float')
|
| 29 |
-
df_antenna['LAT_L93'] = df_antenna['LAT_L93'].astype('float')
|
| 30 |
-
df_antenna['LONG_WGS'] = df_antenna['LONG_WGS'].astype('float')
|
| 31 |
-
df_antenna['LAT_WGS'] = df_antenna['LAT_WGS'].astype('float')
|
| 32 |
-
return df_antenna
|
| 33 |
|
| 34 |
-
|
| 35 |
-
|
|
|
|
|
|
|
|
|
|
| 36 |
|
| 37 |
# Initialisation des variables d'état
|
| 38 |
selected_dpt = []
|
|
@@ -40,9 +22,9 @@ selected_dpt_gant = []
|
|
| 40 |
selected_sp = []
|
| 41 |
selected_gender = []
|
| 42 |
selected_sites = []
|
| 43 |
-
selected_dates = [
|
| 44 |
-
|
| 45 |
-
|
| 46 |
|
| 47 |
# CONTENU
|
| 48 |
## ROOT PAGE
|
|
@@ -59,212 +41,24 @@ with open("pages/page1.md", "r", encoding = "utf-8") as file:
|
|
| 59 |
page1 = file.read()
|
| 60 |
|
| 61 |
## VISUALISATION DES DONNEES D'ANTENNES
|
| 62 |
-
departements = sorted(
|
| 63 |
-
species = sorted(
|
| 64 |
-
genders = sorted(
|
| 65 |
-
sites = sorted(
|
| 66 |
-
dates = [
|
| 67 |
-
|
| 68 |
-
def create_trajectories_map(df, width = 1200, height = 1000):
|
| 69 |
-
if df.empty:
|
| 70 |
-
fig = go.Figure(go.Scattermapbox())
|
| 71 |
-
fig.update_layout(mapbox_style = "open-street-map", mapbox_zoom = 6, mapbox_center = {"lat": 46.493889, "lon": 2.602778}, height = height, width = width)
|
| 72 |
-
return fig
|
| 73 |
-
|
| 74 |
-
center_lat, center_lon = df['LAT_WGS'].mean(), df['LONG_WGS'].mean() # Centre de la carte définie sur la moyenne des coordonnées des points
|
| 75 |
-
|
| 76 |
-
fig = go.Figure()
|
| 77 |
-
|
| 78 |
-
# Extraction des liaisons uniques entre sites
|
| 79 |
-
unique_connections = df.sort_values('DATE').drop_duplicates(subset = ['LAT_WGS', 'LONG_WGS'], keep = 'first')
|
| 80 |
-
|
| 81 |
-
# Tracer les liaisons
|
| 82 |
-
last_point = None
|
| 83 |
-
for _, row in unique_connections.iterrows():
|
| 84 |
-
if last_point is not None:
|
| 85 |
-
fig.add_trace(go.Scattermapbox(
|
| 86 |
-
lat = [last_point['LAT_WGS'], row['LAT_WGS']],
|
| 87 |
-
lon = [last_point['LONG_WGS'], row['LONG_WGS']],
|
| 88 |
-
mode = 'lines',
|
| 89 |
-
line = dict(color = 'violet', width = 2),
|
| 90 |
-
showlegend = False
|
| 91 |
-
))
|
| 92 |
-
last_point = row
|
| 93 |
-
|
| 94 |
-
# Ajout des marqueurs pour les sites
|
| 95 |
-
sites = df.drop_duplicates(subset = ['LAT_WGS', 'LONG_WGS', 'LIEU_DIT'])
|
| 96 |
-
for _, site in sites.iterrows():
|
| 97 |
-
fig.add_trace(go.Scattermapbox(
|
| 98 |
-
lat = [site['LAT_WGS']],
|
| 99 |
-
lon = [site['LONG_WGS']],
|
| 100 |
-
mode = 'markers+text',
|
| 101 |
-
marker = go.scattermapbox.Marker(size=9, color='blue'),
|
| 102 |
-
textfont = dict(color = 'black'), # Couleur du texte
|
| 103 |
-
text = site['LIEU_DIT'],
|
| 104 |
-
textposition = "bottom right",
|
| 105 |
-
name = site['LIEU_DIT'], # Utiliser la bonne étiquette comme nom de site
|
| 106 |
-
showlegend = False
|
| 107 |
-
))
|
| 108 |
-
|
| 109 |
-
fig.update_layout(
|
| 110 |
-
mapbox_style = "open-street-map", # Utilisation d'un fond de carte en noir et blanc
|
| 111 |
-
mapbox_zoom = 6,
|
| 112 |
-
mapbox_center = {"lat": center_lat, "lon": center_lon},
|
| 113 |
-
height = height,
|
| 114 |
-
width = width
|
| 115 |
-
)
|
| 116 |
-
return fig
|
| 117 |
-
|
| 118 |
-
def create_transition_matrix(df, remove_self_loops=True, reduce_self_loops=False, reduction_factor=0.5):
|
| 119 |
-
# Tri par individu et date pour suivre les transitions
|
| 120 |
-
df = df.sort_values(by=['NUM_PIT', 'DATE'])
|
| 121 |
-
|
| 122 |
-
# Créer une colonne pour le lieu précédent
|
| 123 |
-
df['LIEU_PRECEDENT'] = df.groupby('NUM_PIT')['LIEU_DIT'].shift()
|
| 124 |
-
|
| 125 |
-
# Filtrer pour obtenir seulement les transitions valides (non nulles)
|
| 126 |
-
df_transitions = df.dropna(subset=['LIEU_PRECEDENT'])
|
| 127 |
-
|
| 128 |
-
# Compter les transitions de chaque lieu vers un autre
|
| 129 |
-
transition_counts = df_transitions.groupby(['LIEU_PRECEDENT', 'LIEU_DIT']).size().reset_index(name='count')
|
| 130 |
-
|
| 131 |
-
# Retirer les transitions où source == target si demandé
|
| 132 |
-
if remove_self_loops:
|
| 133 |
-
transition_counts = transition_counts[transition_counts['LIEU_PRECEDENT'] != transition_counts['LIEU_DIT']]
|
| 134 |
-
|
| 135 |
-
# Réduire le poids des transitions de recontrôle (si demandé)
|
| 136 |
-
if reduce_self_loops:
|
| 137 |
-
transition_counts.loc[transition_counts['LIEU_PRECEDENT'] == transition_counts['LIEU_DIT'], 'count'] *= reduction_factor
|
| 138 |
-
|
| 139 |
-
# Construire une matrice de transition
|
| 140 |
-
lieux = sorted(set(df['LIEU_DIT'].unique()) | set(df['LIEU_PRECEDENT'].dropna().unique()))
|
| 141 |
-
transition_matrix = transition_counts.pivot(index='LIEU_PRECEDENT', columns='LIEU_DIT', values='count').fillna(0)
|
| 142 |
-
|
| 143 |
-
return transition_matrix, lieux
|
| 144 |
-
|
| 145 |
-
def process_transition_matrix(transition_matrix, df, threshold=0):
|
| 146 |
-
# Transformer la matrice en table de connexions
|
| 147 |
-
transition_table = transition_matrix.stack().reset_index()
|
| 148 |
-
transition_table.columns = ['source', 'target', 'count']
|
| 149 |
-
|
| 150 |
-
# Assurer l'ordre alphabétique des paires (source, target) pour un regroupement correct
|
| 151 |
-
transition_table['site_pair'] = transition_table.apply(lambda row: tuple(sorted([row['source'], row['target']])), axis=1)
|
| 152 |
-
|
| 153 |
-
# Grouper par paire de sites et sommer les counts
|
| 154 |
-
transition_table = transition_table.groupby('site_pair', as_index=False).agg(count=('count', 'sum'))
|
| 155 |
-
|
| 156 |
-
# Séparer les paires de sites dans des colonnes distinctes
|
| 157 |
-
transition_table[['source', 'target']] = pd.DataFrame(transition_table['site_pair'].tolist(), index=transition_table.index)
|
| 158 |
|
| 159 |
-
|
| 160 |
-
transition_table = transition_table[transition_table['count'] > threshold]
|
| 161 |
-
|
| 162 |
-
# Obtenir les limites pour normaliser les valeurs de count
|
| 163 |
-
mean_count = transition_table['count'].mean()
|
| 164 |
-
std_count = transition_table['count'].std()
|
| 165 |
-
|
| 166 |
-
# Normaliser les valeurs de count entre 0 et 1
|
| 167 |
-
transition_table['normalized_count'] = (transition_table['count'] - mean_count) / std_count
|
| 168 |
-
|
| 169 |
-
# Calculate quantiles for count
|
| 170 |
-
quantiles = np.quantile(transition_table['count'], [0, 0.25, 0.5, 0.75, 1])
|
| 171 |
-
|
| 172 |
-
# Create a custom colormap
|
| 173 |
-
cmap = mcolors.LinearSegmentedColormap.from_list(
|
| 174 |
-
"CustomBlackRed",
|
| 175 |
-
["#D3D3D3", "#606060", "#FFA500", "#FF7F50", "#FF0000"]
|
| 176 |
-
)
|
| 177 |
-
|
| 178 |
-
# Calculate colors based on quantiles
|
| 179 |
-
def calculate_color(count):
|
| 180 |
-
if count <= quantiles[0]:
|
| 181 |
-
return mcolors.to_hex(cmap(0))
|
| 182 |
-
elif count <= quantiles[1]:
|
| 183 |
-
return mcolors.to_hex(cmap(0.25))
|
| 184 |
-
elif count <= quantiles[2]:
|
| 185 |
-
return mcolors.to_hex(cmap(0.5))
|
| 186 |
-
elif count <= quantiles[3]:
|
| 187 |
-
return mcolors.to_hex(cmap(0.75))
|
| 188 |
-
else:
|
| 189 |
-
return mcolors.to_hex(cmap(1))
|
| 190 |
-
|
| 191 |
-
transition_table['color'] = transition_table['count'].apply(calculate_color)
|
| 192 |
-
|
| 193 |
-
# Fusionner les coordonnées du df dans transition_table pour source et target
|
| 194 |
-
coords = df[['LIEU_DIT', 'LAT_WGS', 'LONG_WGS']].drop_duplicates()
|
| 195 |
-
coords = coords.set_index('LIEU_DIT')
|
| 196 |
-
|
| 197 |
-
transition_table = transition_table.merge(coords, left_on='source', right_index=True)
|
| 198 |
-
transition_table = transition_table.merge(coords, left_on='target', right_index=True, suffixes=('_source', '_target'))
|
| 199 |
-
|
| 200 |
-
return transition_table
|
| 201 |
-
|
| 202 |
-
def create_trajectories_map_from_matrix(df, transition_table, width=1200, height=1000):
|
| 203 |
-
fig = go.Figure()
|
| 204 |
-
|
| 205 |
-
# Créer une trace distincte pour chaque segment de ligne avec la couleur appropriée
|
| 206 |
-
for index, row in transition_table.iterrows():
|
| 207 |
-
fig.add_trace(go.Scattermapbox(
|
| 208 |
-
lat=[row['LAT_WGS_source'], row['LAT_WGS_target']],
|
| 209 |
-
lon=[row['LONG_WGS_source'], row['LONG_WGS_target']],
|
| 210 |
-
mode='lines',
|
| 211 |
-
line=dict(
|
| 212 |
-
color=row['color'],
|
| 213 |
-
width=1
|
| 214 |
-
),
|
| 215 |
-
showlegend=False
|
| 216 |
-
))
|
| 217 |
-
|
| 218 |
-
# Ajouter tous les marqueurs pour les lieux d'un seul coup
|
| 219 |
-
coords = df[['LIEU_DIT', 'LAT_WGS', 'LONG_WGS']].drop_duplicates()
|
| 220 |
-
fig.add_trace(go.Scattermapbox(
|
| 221 |
-
lat=coords['LAT_WGS'],
|
| 222 |
-
lon=coords['LONG_WGS'],
|
| 223 |
-
mode='markers+text',
|
| 224 |
-
marker=go.scattermapbox.Marker(
|
| 225 |
-
size=6,
|
| 226 |
-
color='#8467D7', # Light violet
|
| 227 |
-
),
|
| 228 |
-
text=coords['LIEU_DIT'],
|
| 229 |
-
textposition="top right",
|
| 230 |
-
textfont=dict(
|
| 231 |
-
size=12,
|
| 232 |
-
color='black'
|
| 233 |
-
),
|
| 234 |
-
hoverinfo='text',
|
| 235 |
-
showlegend=False
|
| 236 |
-
))
|
| 237 |
-
|
| 238 |
-
# Définir le centre de la carte
|
| 239 |
-
center_lat, center_lon = df['LAT_WGS'].mean(), df['LONG_WGS'].mean()
|
| 240 |
-
|
| 241 |
-
fig.update_layout(
|
| 242 |
-
mapbox_style="open-street-map",
|
| 243 |
-
mapbox_zoom=6,
|
| 244 |
-
mapbox_center={"lat": center_lat, "lon": center_lon},
|
| 245 |
-
font=dict(color='black'),
|
| 246 |
-
height=height,
|
| 247 |
-
width=width
|
| 248 |
-
)
|
| 249 |
-
|
| 250 |
-
return fig
|
| 251 |
-
|
| 252 |
-
transition_matrix, labels = create_transition_matrix(df_antenna)
|
| 253 |
-
transition_table = process_transition_matrix(transition_matrix, df_antenna)
|
| 254 |
-
create_trajectories_map_from_matrix(df_antenna, transition_table)
|
| 255 |
-
|
| 256 |
-
map_section = create_trajectories_map(df_empty)
|
| 257 |
|
| 258 |
with open("pages/page2.md", "r", encoding = "utf-8") as file:
|
| 259 |
page2 = file.read()
|
| 260 |
|
| 261 |
-
|
| 262 |
-
|
|
|
|
| 263 |
|
| 264 |
# Filtrer par département d'origine
|
| 265 |
if state.selected_dpt:
|
| 266 |
-
equipped_pit = df_filtered[(df_filtered['
|
| 267 |
-
(df_filtered['DEPARTEMENT'].isin(state.selected_dpt))]['NUM_PIT'].unique()
|
| 268 |
df_filtered = df_filtered[df_filtered['NUM_PIT'].isin(equipped_pit)]
|
| 269 |
|
| 270 |
# Filtrer par espèce
|
|
@@ -277,8 +71,7 @@ def refresh_button(state):
|
|
| 277 |
|
| 278 |
# Filtrer par site d'origine
|
| 279 |
if state.selected_sites:
|
| 280 |
-
site_pit = df_filtered[(df_filtered['
|
| 281 |
-
(df_filtered['LIEU_DIT'].isin(state.selected_sites))]['NUM_PIT'].unique()
|
| 282 |
df_filtered = df_filtered[df_filtered['NUM_PIT'].isin(site_pit)]
|
| 283 |
|
| 284 |
# Filtrer par intervalle de dates
|
|
@@ -288,100 +81,17 @@ def refresh_button(state):
|
|
| 288 |
df_filtered = df_filtered[(df_filtered['DATE'] >= start_date) & (df_filtered['DATE'] <= end_date)]
|
| 289 |
|
| 290 |
# Rafraichir la carte
|
| 291 |
-
|
| 292 |
-
transition_table_filtered = process_transition_matrix(transition_matrix_filtered, df_filtered)
|
| 293 |
-
state.map_section = create_trajectories_map_from_matrix(df_filtered, transition_table_filtered)
|
| 294 |
|
| 295 |
## PHENOLOGIES DES SITES
|
| 296 |
-
|
| 297 |
-
def gant_diagram(df_original):
|
| 298 |
-
# Créer une copie du DataFrame original par sécurité
|
| 299 |
-
df_filtered = df_original[df_original['ACTION'] == "C"].copy()
|
| 300 |
-
|
| 301 |
-
# Convertir la colonne DATE en datetime et localiser en UTC
|
| 302 |
-
df_filtered['DATE'] = pd.to_datetime(df_filtered['DATE'])
|
| 303 |
-
df_filtered['DATE'] = df_filtered['DATE'].dt.tz_localize('UTC')
|
| 304 |
-
|
| 305 |
-
# Trier les valeurs par DATE
|
| 306 |
-
df_filtered = df_filtered.sort_values(by = 'DATE', ascending = True)
|
| 307 |
-
|
| 308 |
-
# Ajouter des colonnes pour les visites précédentes et les nouvelles visites
|
| 309 |
-
df_filtered['Prev_DATE'] = df_filtered.groupby(['LIEU_DIT'])['DATE'].shift(1)
|
| 310 |
-
df_filtered['New_Visit'] = (df_filtered['DATE'] - df_filtered['Prev_DATE']).dt.days > 1
|
| 311 |
-
df_filtered['New_Visit'].fillna(True, inplace = True)
|
| 312 |
-
df_filtered['Visit_ID'] = df_filtered.groupby(['LIEU_DIT'])['New_Visit'].cumsum()
|
| 313 |
-
|
| 314 |
-
# Supprimer les LIEU_DIT qui n'ont aucune visite détectée
|
| 315 |
-
df_filtered = df_filtered.dropna(subset = ['Visit_ID'])
|
| 316 |
-
|
| 317 |
-
# Calculer min/max dates pour chaque visite
|
| 318 |
-
result = df_filtered.groupby(['LIEU_DIT', 'Visit_ID']).agg(
|
| 319 |
-
Start = pd.NamedAgg(column = 'DATE', aggfunc = 'min'),
|
| 320 |
-
Finish = pd.NamedAgg(column = 'DATE', aggfunc = 'max'),
|
| 321 |
-
Departement = pd.NamedAgg(column = 'DEPARTEMENT', aggfunc = 'first')
|
| 322 |
-
).reset_index()
|
| 323 |
-
|
| 324 |
-
# Supprimer les LIEU_DIT qui n'ont pas de dates valides
|
| 325 |
-
result = result.dropna(subset = ['Start', 'Finish'])
|
| 326 |
-
|
| 327 |
-
# Filtrer les LIEU_DIT qui ont des résultats
|
| 328 |
-
result = result[result['Finish'] > result['Start']]
|
| 329 |
-
|
| 330 |
-
# Trier les sites par ordre alphabétique et les départements pour la légende
|
| 331 |
-
result = result.sort_values(by = ['Departement', 'LIEU_DIT'])
|
| 332 |
-
result['Start'] = pd.to_datetime(result['Start'])
|
| 333 |
-
result['Finish'] = pd.to_datetime(result['Finish'])
|
| 334 |
-
|
| 335 |
-
# Déterminer la hauteur de la figure en fonction du nombre de sites
|
| 336 |
-
num_sites = result['LIEU_DIT'].nunique()
|
| 337 |
-
height_per_site = 50 # Hauteur allouée par site en pixels
|
| 338 |
-
base_height = 100 # Hauteur de base pour la figure
|
| 339 |
-
max_height = 20000 # Hauteur maximale de la figure
|
| 340 |
-
fig_height = min(base_height + num_sites * height_per_site, max_height)
|
| 341 |
-
|
| 342 |
-
# Créer le diagramme de Gantt
|
| 343 |
-
fig = px.timeline(
|
| 344 |
-
result,
|
| 345 |
-
x_start = 'Start',
|
| 346 |
-
x_end = 'Finish',
|
| 347 |
-
y = 'LIEU_DIT',
|
| 348 |
-
color = 'Departement', # Changer la couleur en fonction du département
|
| 349 |
-
color_discrete_sequence = px.colors.qualitative.Plotly,
|
| 350 |
-
labels = {'LIEU_DIT': 'Site'},
|
| 351 |
-
title = 'Présence sur chaque site'
|
| 352 |
-
)
|
| 353 |
-
|
| 354 |
-
# Ajout de lignes verticales pour chaque 1er janvier
|
| 355 |
-
start_year = result['Start'].dt.year.min()
|
| 356 |
-
end_year = result['Finish'].dt.year.max()
|
| 357 |
-
for year in range(start_year, end_year + 1):
|
| 358 |
-
fig.add_vline(
|
| 359 |
-
x = pd.Timestamp(f'{year}-01-01'),
|
| 360 |
-
line = dict(color = 'grey', dash = 'dash', width = 1)
|
| 361 |
-
)
|
| 362 |
-
|
| 363 |
-
fig.update_layout(legend = dict(traceorder = "normal"), margin = dict(l = 300))
|
| 364 |
-
|
| 365 |
-
# Mise à jour des étiquettes et de l'orientation de l'axe des ordonnées
|
| 366 |
-
fig.update_yaxes(categoryorder = 'total ascending') # Tri les sites par date de début
|
| 367 |
-
fig.update_layout(
|
| 368 |
-
xaxis_title = 'Date',
|
| 369 |
-
yaxis_title = 'Site',
|
| 370 |
-
xaxis = dict(tickformat = '%Y-%m-%d'), # Format des dates sur l'axe X pour plus de clarté
|
| 371 |
-
height = fig_height,
|
| 372 |
-
legend = dict(bgcolor = 'rgba(0, 0, 0, 0)')
|
| 373 |
-
)
|
| 374 |
-
|
| 375 |
-
return fig
|
| 376 |
-
|
| 377 |
-
gant_global = gant_diagram(df_antenna)
|
| 378 |
|
| 379 |
with open("pages/page3.md", "r", encoding = "utf-8") as file:
|
| 380 |
page3 = file.read()
|
| 381 |
|
|
|
|
| 382 |
def update_gant(state):
|
| 383 |
-
df_filtered_gant =
|
| 384 |
-
df_filtered_gant = df_filtered_gant[df_filtered_gant['ACTION'] == "C"]
|
| 385 |
|
| 386 |
# Convertir les dates sélectionnées en objets Timestamp
|
| 387 |
if state.selected_dpt_gant:
|
|
@@ -397,216 +107,72 @@ def update_gant(state):
|
|
| 397 |
state.gant_global = gant_diagram(df_filtered_gant)
|
| 398 |
|
| 399 |
## STATISTIQUES ANALYTIQUES
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 400 |
|
| 401 |
-
|
| 402 |
-
|
| 403 |
-
|
| 404 |
-
|
| 405 |
-
|
| 406 |
-
|
| 407 |
-
|
| 408 |
-
|
| 409 |
-
fig.update_layout(
|
| 410 |
-
height = 400,
|
| 411 |
-
width = 400,
|
| 412 |
-
yaxis_title = 'Nombre de détections',
|
| 413 |
-
xaxis_title = None,
|
| 414 |
-
xaxis = {'type': 'category', 'tickmode': 'linear'}, # Afficher toutes les années
|
| 415 |
-
barmode = 'stack',
|
| 416 |
-
legend = dict(
|
| 417 |
-
bgcolor = 'rgba(0, 0, 0, 0)',
|
| 418 |
-
orientation = 'h', # Légende horizontale
|
| 419 |
-
x = 0.5, # Centrer la légende horizontalement
|
| 420 |
-
y = -0.2, # Placer la légende en dessous de la figure
|
| 421 |
-
xanchor = 'center', # Ancrer la légende au centre
|
| 422 |
-
yanchor = 'top' # Ancrer la légende au-dessus de l'axe x
|
| 423 |
-
),
|
| 424 |
-
margin = dict(b = 100)
|
| 425 |
-
)
|
| 426 |
-
|
| 427 |
-
return fig
|
| 428 |
-
|
| 429 |
-
def capture_by_year(df_antenna):
|
| 430 |
-
df_filtre = df_antenna[df_antenna['ACTION'] == 'T']
|
| 431 |
-
df_filtre['YEAR'] = df_filtre['DATE'].dt.year
|
| 432 |
-
df_grouped = df_filtre.groupby(['YEAR', 'CODE_ESP'])['NUM_PIT'].nunique().reset_index()
|
| 433 |
-
df_grouped.rename(columns = {'NUM_PIT': 'Nombre d\'individus uniques'}, inplace = True)
|
| 434 |
-
|
| 435 |
-
fig = px.bar(df_grouped, x = 'YEAR', y = 'Nombre d\'individus uniques', color = 'CODE_ESP',
|
| 436 |
-
labels = {'Nombre d\'individus uniques': 'Nombre d\'individus uniques', 'CODE_ESP': 'Espèce'}
|
| 437 |
-
)
|
| 438 |
-
|
| 439 |
-
fig.update_layout(
|
| 440 |
-
height = 400,
|
| 441 |
-
width = 400,
|
| 442 |
-
yaxis_title = 'Nombre d\'individus uniques',
|
| 443 |
-
xaxis_title = None,
|
| 444 |
-
xaxis = {'type': 'category', 'tickmode': 'linear'}, # Afficher toutes les années
|
| 445 |
-
legend = dict(
|
| 446 |
-
bgcolor ='rgba(0, 0, 0, 0)',
|
| 447 |
-
orientation = 'h', # Légende horizontale
|
| 448 |
-
x = 0.5, # Centrer la légende horizontalement
|
| 449 |
-
y = -0.2, # Placer la légende en dessous de la figure
|
| 450 |
-
xanchor = 'center', # Ancrer la légende au centre
|
| 451 |
-
yanchor = 'top' # Ancrer la légende au-dessus de l'axe x
|
| 452 |
-
),
|
| 453 |
-
margin = dict(b = 100)
|
| 454 |
-
)
|
| 455 |
-
|
| 456 |
-
return fig
|
| 457 |
-
|
| 458 |
-
def control_by_year(df_antenna):
|
| 459 |
-
df_filtre = df_antenna[df_antenna['ACTION'] == 'C']
|
| 460 |
-
df_filtre['YEAR'] = df_filtre['DATE'].dt.year
|
| 461 |
-
df_grouped = df_filtre.groupby(['YEAR', 'CODE_ESP'])['NUM_PIT'].nunique().reset_index()
|
| 462 |
-
df_grouped.rename(columns = {'NUM_PIT': 'Nombre d\'individus'}, inplace = True)
|
| 463 |
-
|
| 464 |
-
fig = px.bar(df_grouped, x = 'YEAR', y = 'Nombre d\'individus', color = 'CODE_ESP',
|
| 465 |
-
labels = {'Nombre d\'individus': 'Nombre d\'individus', 'CODE_ESP': 'Espèce'},
|
| 466 |
-
)
|
| 467 |
-
|
| 468 |
-
fig.update_layout(
|
| 469 |
-
height = 400,
|
| 470 |
-
width = 400,
|
| 471 |
-
yaxis_title = 'Nombre d\'individus',
|
| 472 |
-
xaxis_title = None,
|
| 473 |
-
xaxis = {'type': 'category', 'tickmode': 'linear'}, # Afficher toutes les années
|
| 474 |
-
legend = dict(
|
| 475 |
-
bgcolor = 'rgba(0, 0, 0, 0)',
|
| 476 |
-
orientation = 'h', # Légende horizontale
|
| 477 |
-
x = 0.5, # Centrer la légende horizontalement
|
| 478 |
-
y = -0.2, # Placer la légende en dessous de la figure
|
| 479 |
-
xanchor = 'center', # Ancrer la légende au centre
|
| 480 |
-
yanchor = 'top' # Ancrer la légende au-dessus de l'axe x
|
| 481 |
-
),
|
| 482 |
-
margin = dict(b = 100)
|
| 483 |
-
)
|
| 484 |
-
|
| 485 |
-
return fig
|
| 486 |
-
|
| 487 |
-
def detection_frequencies(df_antenna):
|
| 488 |
-
df_antenna['MONTH_DAY'] = df_antenna['DATE'].dt.strftime('%m-%d')
|
| 489 |
-
global_freq = df_antenna.groupby('MONTH_DAY').size().reset_index(name = 'Global Detections')
|
| 490 |
-
|
| 491 |
-
# Calculer les fréquences par site
|
| 492 |
-
site_freq = df_antenna.groupby(['MONTH_DAY', 'LIEU_DIT']).size().reset_index(name = 'Detections')
|
| 493 |
-
sites = site_freq['LIEU_DIT'].unique()
|
| 494 |
-
|
| 495 |
-
# Préparer l'ordre chronologique
|
| 496 |
-
months_days = pd.date_range('2021-01-01', '2021-12-31').strftime('%m-%d')
|
| 497 |
-
global_freq['MONTH_DAY'] = pd.Categorical(global_freq['MONTH_DAY'], categories = months_days, ordered = True)
|
| 498 |
-
global_freq = global_freq.sort_values('MONTH_DAY')
|
| 499 |
-
|
| 500 |
-
# Création du graphique
|
| 501 |
-
fig = go.Figure()
|
| 502 |
-
|
| 503 |
-
# Ajouter la courbe globale
|
| 504 |
-
fig.add_trace(go.Scatter(x = global_freq['MONTH_DAY'], y = global_freq['Global Detections'],
|
| 505 |
-
mode = 'lines', name = 'Global'))
|
| 506 |
-
|
| 507 |
-
# Ajouter une courbe pour chaque site
|
| 508 |
-
for site in sites:
|
| 509 |
-
site_data = site_freq[site_freq['LIEU_DIT'] == site]
|
| 510 |
-
site_data['MONTH_DAY'] = pd.Categorical(site_data['MONTH_DAY'], categories = months_days, ordered = True)
|
| 511 |
-
site_data = site_data.sort_values('MONTH_DAY')
|
| 512 |
-
fig.add_trace(go.Scatter(x = site_data['MONTH_DAY'], y = site_data['Detections'],
|
| 513 |
-
mode = 'lines', name = site))
|
| 514 |
-
|
| 515 |
-
# Mise à jour du layout
|
| 516 |
-
fig.update_layout(
|
| 517 |
-
xaxis_title = 'Jour de l\'année',
|
| 518 |
-
yaxis_title = 'Nombre de détections',
|
| 519 |
-
xaxis = dict(type = 'category', categoryorder = 'array', categoryarray = [md for md in months_days]),
|
| 520 |
-
legend = dict(bgcolor = 'rgba(0, 0, 0, 0)'),
|
| 521 |
-
#yaxis = dict(range = [0, global_freq['Global Detections'].max() + 10])
|
| 522 |
-
)
|
| 523 |
-
return fig
|
| 524 |
-
|
| 525 |
-
def pie_controled(df_antenna):
|
| 526 |
-
species_counts = df_antenna[df_antenna['ACTION'] == 'C']['CODE_ESP'].value_counts().reset_index()
|
| 527 |
-
species_counts.columns = ['Species', 'Count']
|
| 528 |
-
|
| 529 |
-
# Créer un diagramme circulaire
|
| 530 |
-
fig = px.pie(species_counts,
|
| 531 |
-
values = 'Count', names = 'Species',
|
| 532 |
-
color_discrete_sequence = px.colors.qualitative.Pastel,
|
| 533 |
-
hole = 0.5,)
|
| 534 |
-
|
| 535 |
-
# Personnalisation supplémentaire
|
| 536 |
-
fig.update_layout(legend_title_text = 'Espèce',
|
| 537 |
-
showlegend = True,
|
| 538 |
-
legend = dict(bgcolor = 'rgba(0, 0, 0, 0)')
|
| 539 |
-
)
|
| 540 |
-
return fig
|
| 541 |
-
|
| 542 |
-
def pie_marked(df_antenna):
|
| 543 |
-
marked_species = df_antenna[df_antenna['ACTION'] == 'T']
|
| 544 |
-
species_counts = marked_species['CODE_ESP'].value_counts().reset_index()
|
| 545 |
-
species_counts.columns = ['Species', 'Count']
|
| 546 |
-
|
| 547 |
-
# Créer un diagramme circulaire
|
| 548 |
-
fig = px.pie(species_counts,
|
| 549 |
-
values = 'Count', names = 'Species',
|
| 550 |
-
color_discrete_sequence = px.colors.qualitative.Pastel,
|
| 551 |
-
hole = 0.5)
|
| 552 |
-
|
| 553 |
-
# Personnalisation supplémentaire
|
| 554 |
-
fig.update_layout(legend_title_text = 'Espèce',
|
| 555 |
-
showlegend = True,
|
| 556 |
-
legend = dict(bgcolor = 'rgba(0, 0, 0, 0)')
|
| 557 |
-
)
|
| 558 |
-
return fig
|
| 559 |
-
|
| 560 |
-
def top_detection(df_antenna):
|
| 561 |
-
df_antenna['NUM_PIT'] = "n° " + df_antenna['NUM_PIT'].astype(str)
|
| 562 |
-
|
| 563 |
-
# Obtenir les dix catégories les plus fréquentes avec leurs occurrences
|
| 564 |
-
top_categories = df_antenna['NUM_PIT'].value_counts().head(10)
|
| 565 |
-
|
| 566 |
-
# Créer un DataFrame à partir des dix premières catégories
|
| 567 |
-
df_top_categories = pd.DataFrame({'NUM_PIT': top_categories.index, 'Occurrences': top_categories.values})
|
| 568 |
-
df_top_categories = df_top_categories.sort_values(by = 'Occurrences', ascending = False)
|
| 569 |
-
|
| 570 |
-
# Créer le diagramme en barres horizontales
|
| 571 |
-
fig = px.bar(df_top_categories, x = 'Occurrences', y = 'NUM_PIT', orientation = 'h',
|
| 572 |
-
labels = {'Occurrences': "Nombre d'occurrences", 'NUM_PIT': ''},
|
| 573 |
-
color = 'NUM_PIT', color_discrete_sequence = px.colors.qualitative.Set3)
|
| 574 |
|
| 575 |
-
|
| 576 |
-
|
| 577 |
-
min_height = 400 # Hauteur minimale du graphique
|
| 578 |
-
calculated_height = max(min_height, len(df_top_categories) * bar_height)
|
| 579 |
|
| 580 |
-
|
| 581 |
-
|
| 582 |
-
|
| 583 |
-
|
| 584 |
-
|
| 585 |
-
|
| 586 |
-
)
|
| 587 |
|
| 588 |
-
|
| 589 |
-
|
| 590 |
-
#
|
| 591 |
-
|
| 592 |
-
|
| 593 |
-
|
| 594 |
-
plot_frequencies = detection_frequencies(df_antenna) # Courbes de fréquences de détections par jour de l'année et par site
|
| 595 |
-
plot_pie_controled = pie_controled(df_antenna) # Pieplot des individus contrôlés
|
| 596 |
-
plot_pie_marked = pie_marked(df_antenna) # Pieplot des individus marqués
|
| 597 |
-
plot_top_detection = top_detection(df_antenna) # Barplot horizontal des 10 individus les plus détectés
|
| 598 |
-
table_plot_raw = process_transition_matrix(transition_matrix, df_antenna, threshold = 0)
|
| 599 |
-
transition_table_plot = table_plot_raw[['source', 'target', 'count']].sort_values(by = 'count', ascending = False) # Table des trajectoires
|
| 600 |
|
| 601 |
# Initialisation des variables à plot
|
| 602 |
-
|
| 603 |
-
|
| 604 |
-
|
| 605 |
-
|
| 606 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 607 |
|
| 608 |
-
with open("pages/
|
| 609 |
-
|
| 610 |
|
| 611 |
# DEMARRAGE DE L'APPLICATION
|
| 612 |
pages = {
|
|
@@ -614,8 +180,10 @@ pages = {
|
|
| 614 |
"presentation": page1,
|
| 615 |
"antennes": page2,
|
| 616 |
"phenologie": page3,
|
| 617 |
-
"statistiques": page4
|
|
|
|
| 618 |
}
|
| 619 |
|
|
|
|
| 620 |
gui = Gui(pages = pages, css_file = "assets/styles.css")
|
| 621 |
gui.run(host = '0.0.0.0', port = 7860, use_session = True) # use_session = True pour permettre l'usage de plusieurs users en même temps
|
|
|
|
| 7 |
import matplotlib.colors as mcolors
|
| 8 |
import matplotlib.cm as cm
|
| 9 |
|
| 10 |
+
from functions import *
|
| 11 |
+
from dashboard import *
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 12 |
|
| 13 |
+
# CHARGEMENT DES DONNEES
|
| 14 |
+
df_controls, df_individus, df_sites, df_distances, df_mapping = load_data_antenna()
|
| 15 |
+
liste_sites_antennes = ["Brelouze", "Mairie d'Annepont", "Grotte de Loubeau", "Le Plessis", "Puy-Chenin", "Cézelle", "La Bourtière", "Goizet (W)", "Château de Gagemont", "Faye-L'Abbesse - Bourg", "Guibaud", "Cave Billard", "Grotte de Boisdichon", "Les Roches", "Barrage de l'Aigle", "Gouffre de la Fage",
|
| 16 |
+
"La Brumaudière", "Château de Verteuil", "Les Dames", "Château de Hautefort", "Les Tours de Merle", "Le Petit Pin", "Maison Brousse", "Caves de Laubenheimer", "Château de Villandraut", "Tunnel ferroviaire", "Grotte de la carrière", "Centrale hydroélectrique de Claredent", "Fermette des Nobis",
|
| 17 |
+
"Beauregard", "Grotte de la Deveze", "Petexaenea", "Gouffre de Bexanka", "Mikelauenzilo"]
|
| 18 |
|
| 19 |
# Initialisation des variables d'état
|
| 20 |
selected_dpt = []
|
|
|
|
| 22 |
selected_sp = []
|
| 23 |
selected_gender = []
|
| 24 |
selected_sites = []
|
| 25 |
+
selected_dates = [df_controls['DATE'].min(), df_controls['DATE'].max()]
|
| 26 |
+
dates_gant = [df_controls['DATE'].min(), df_controls['DATE'].max()]
|
| 27 |
+
df_empty = pd.DataFrame()
|
| 28 |
|
| 29 |
# CONTENU
|
| 30 |
## ROOT PAGE
|
|
|
|
| 41 |
page1 = file.read()
|
| 42 |
|
| 43 |
## VISUALISATION DES DONNEES D'ANTENNES
|
| 44 |
+
departements = sorted(df_controls['DEPARTEMENT'].unique().tolist())
|
| 45 |
+
species = sorted(df_controls['CODE_ESP'].unique().tolist())
|
| 46 |
+
genders = sorted(df_controls['SEXE'].dropna().unique().tolist())
|
| 47 |
+
sites = sorted(df_controls['LIEU_DIT'].unique().tolist())
|
| 48 |
+
dates = [df_controls['DATE'].min(), df_controls['DATE'].max()]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 49 |
|
| 50 |
+
m = generate_map(df_empty, df_sites)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 51 |
|
| 52 |
with open("pages/page2.md", "r", encoding = "utf-8") as file:
|
| 53 |
page2 = file.read()
|
| 54 |
|
| 55 |
+
# Callback de la carte
|
| 56 |
+
def refresh_map_button(state):
|
| 57 |
+
df_filtered = df_distances.copy()
|
| 58 |
|
| 59 |
# Filtrer par département d'origine
|
| 60 |
if state.selected_dpt:
|
| 61 |
+
equipped_pit = df_filtered[(df_filtered['DPT_DEPART'].isin(state.selected_dpt)) | (df_filtered['DPT_ARRIVEE'].isin(state.selected_dpt))]['NUM_PIT'].unique()
|
|
|
|
| 62 |
df_filtered = df_filtered[df_filtered['NUM_PIT'].isin(equipped_pit)]
|
| 63 |
|
| 64 |
# Filtrer par espèce
|
|
|
|
| 71 |
|
| 72 |
# Filtrer par site d'origine
|
| 73 |
if state.selected_sites:
|
| 74 |
+
site_pit = df_filtered[(df_filtered['SITE_DEPART'].isin(state.selected_sites)) | (df_filtered['SITE_ARRIVEE'].isin(state.selected_sites))]['NUM_PIT'].unique()
|
|
|
|
| 75 |
df_filtered = df_filtered[df_filtered['NUM_PIT'].isin(site_pit)]
|
| 76 |
|
| 77 |
# Filtrer par intervalle de dates
|
|
|
|
| 81 |
df_filtered = df_filtered[(df_filtered['DATE'] >= start_date) & (df_filtered['DATE'] <= end_date)]
|
| 82 |
|
| 83 |
# Rafraichir la carte
|
| 84 |
+
state.m = generate_map(df_filtered, df_sites)
|
|
|
|
|
|
|
| 85 |
|
| 86 |
## PHENOLOGIES DES SITES
|
| 87 |
+
gant_global = gant_diagram(df_controls)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 88 |
|
| 89 |
with open("pages/page3.md", "r", encoding = "utf-8") as file:
|
| 90 |
page3 = file.read()
|
| 91 |
|
| 92 |
+
# Callbacks du diagramme de Gant
|
| 93 |
def update_gant(state):
|
| 94 |
+
df_filtered_gant = df_controls.copy()
|
|
|
|
| 95 |
|
| 96 |
# Convertir les dates sélectionnées en objets Timestamp
|
| 97 |
if state.selected_dpt_gant:
|
|
|
|
| 107 |
state.gant_global = gant_diagram(df_filtered_gant)
|
| 108 |
|
| 109 |
## STATISTIQUES ANALYTIQUES
|
| 110 |
+
# Initialisation de tous les plots
|
| 111 |
+
plot_detection_year = detection_by_year(df_controls) # Barplot du nombre de détections par an et par espèce
|
| 112 |
+
plot_capture_year = capture_by_year(df_individus) # Barplot du nombre de captures par an et par espèces
|
| 113 |
+
plot_control_year = control_by_year(df_controls) # Barplot du nombre de contrôles par an et par espèces
|
| 114 |
+
plot_frequencies = detection_frequencies(df_controls) # Courbes de fréquences de détections par jour de l'année et par site
|
| 115 |
+
plot_pie_controled = pie_controled(df_controls) # Pieplot des individus contrôlés
|
| 116 |
+
plot_pie_marked = pie_marked(df_individus) # Pieplot des individus marqués
|
| 117 |
+
plot_top_detection = top_detection(df_controls) # Barplot horizontal des 10 individus les plus détectés
|
| 118 |
|
| 119 |
+
# Initialisation des variables à plot
|
| 120 |
+
total_recaptured = df_controls['NUM_PIT'].nunique() # Individus contrôlés
|
| 121 |
+
total_marked = df_individus['NUM_PIT'].nunique() # Individus marqués
|
| 122 |
+
sites_capture = df_individus['LIEU_DIT'].nunique() # Sites capturés au moins une fois
|
| 123 |
+
sites_antennes = df_sites['LIEU_DIT'].nunique() # Sites contrôlés au moins une fois
|
| 124 |
+
transition_table_plot = df_distances[['CODE_ESP', 'DATE', 'SITE_DEPART', 'DATE_ARRIVEE', 'SITE_ARRIVEE', 'DISTANCE']].sort_values(by='DISTANCE', ascending = False)
|
| 125 |
+
transition_table_plot['DISTANCE'] = transition_table_plot['DISTANCE'].round(2)
|
| 126 |
+
transition_table_plot = transition_table_plot.rename(columns = {'DATE':'DATE_DEPART'})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 127 |
|
| 128 |
+
with open("pages/page4.md", "r", encoding = "utf-8") as file:
|
| 129 |
+
page4 = file.read()
|
|
|
|
|
|
|
| 130 |
|
| 131 |
+
## FICHE SITE
|
| 132 |
+
# Initialisation du sélecteur et des plots
|
| 133 |
+
selection_fiche = ['Brelouze']
|
| 134 |
+
df_controls_fiche = df_controls[df_controls['LIEU_DIT'].isin(selection_fiche)]
|
| 135 |
+
df_individus_fiche = df_individus[df_individus['LIEU_DIT'].isin(selection_fiche)]
|
| 136 |
+
df_distances_fiche = df_distances[(df_distances['SITE_DEPART'].isin(selection_fiche)) | (df_distances['SITE_ARRIVEE'].isin(selection_fiche))]
|
|
|
|
| 137 |
|
| 138 |
+
plot_detection_year_fiche = detection_by_year(df_controls_fiche) # Barplot du nombre de détections par an et par espèce
|
| 139 |
+
plot_capture_year_fiche = capture_by_year(df_individus_fiche) # Barplot du nombre de captures par an et par espèces
|
| 140 |
+
plot_control_year_fiche = control_by_year(df_controls_fiche) # Barplot du nombre de contrôles par an et par espèces
|
| 141 |
+
plot_frequencies_fiche = detection_frequencies(df_controls_fiche) # Courbes de fréquences de détections par jour de l'année et par site
|
| 142 |
+
plot_pie_controled_fiche = pie_controled(df_controls_fiche) # Pieplot des individus contrôlés
|
| 143 |
+
plot_pie_marked_fiche = pie_marked(df_individus_fiche) # Pieplot des individus marqués
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 144 |
|
| 145 |
# Initialisation des variables à plot
|
| 146 |
+
total_recaptured_fiche = df_controls_fiche['NUM_PIT'].nunique() # Individus contrôlés
|
| 147 |
+
total_marked_fiche = df_individus_fiche['NUM_PIT'].nunique() # Individus marqués
|
| 148 |
+
sites_antennes_fiche = df_sites['LIEU_DIT'].nunique() # Sites contrôlés au moins une fois
|
| 149 |
+
map_fiche = generate_map(df_distances_fiche, df_sites) # Map du site et des connexions
|
| 150 |
+
gant_diagram_fiche = gant_diagram_concat(df_controls_fiche)
|
| 151 |
+
|
| 152 |
+
def update_fiche(state):
|
| 153 |
+
# Filtrage en fonction de la sélection
|
| 154 |
+
selected_sites = [state.selection_fiche]
|
| 155 |
+
df_controls_fiche = df_controls[df_controls['LIEU_DIT'].isin(selected_sites)]
|
| 156 |
+
df_individus_fiche = df_individus[df_individus['LIEU_DIT'].isin(selected_sites)]
|
| 157 |
+
df_distances_fiche = df_distances[(df_distances['SITE_DEPART'].isin(selected_sites)) | (df_distances['SITE_ARRIVEE'].isin(selected_sites))]
|
| 158 |
+
|
| 159 |
+
# Mise à jour des visualisations
|
| 160 |
+
state.plot_detection_year_fiche = detection_by_year(df_controls_fiche)
|
| 161 |
+
state.plot_capture_year_fiche = capture_by_year(df_individus_fiche)
|
| 162 |
+
state.plot_control_year_fiche = control_by_year(df_controls_fiche)
|
| 163 |
+
state.plot_frequencies_fiche = detection_frequencies(df_controls_fiche)
|
| 164 |
+
state.plot_pie_controled_fiche = pie_controled(df_controls_fiche)
|
| 165 |
+
state.plot_pie_marked_fiche = pie_marked(df_individus_fiche)
|
| 166 |
+
state.map_fiche = generate_map(df_distances_fiche, df_sites)
|
| 167 |
+
state.gant_diagram_fiche = gant_diagram_concat(df_controls_fiche)
|
| 168 |
+
|
| 169 |
+
# Mise à jour des variables globales
|
| 170 |
+
state.total_recaptured_fiche = df_controls_fiche['NUM_PIT'].nunique()
|
| 171 |
+
state.total_marked_fiche = df_individus_fiche['NUM_PIT'].nunique()
|
| 172 |
+
state.sites_antennes_fiche = df_sites['LIEU_DIT'].nunique()
|
| 173 |
|
| 174 |
+
with open("pages/page5.md", "r", encoding = "utf-8") as file:
|
| 175 |
+
page5 = file.read()
|
| 176 |
|
| 177 |
# DEMARRAGE DE L'APPLICATION
|
| 178 |
pages = {
|
|
|
|
| 180 |
"presentation": page1,
|
| 181 |
"antennes": page2,
|
| 182 |
"phenologie": page3,
|
| 183 |
+
"statistiques": page4,
|
| 184 |
+
"fiche_site": page5
|
| 185 |
}
|
| 186 |
|
| 187 |
+
Gui.register_content_provider(Map, expose_folium)
|
| 188 |
gui = Gui(pages = pages, css_file = "assets/styles.css")
|
| 189 |
gui.run(host = '0.0.0.0', port = 7860, use_session = True) # use_session = True pour permettre l'usage de plusieurs users en même temps
|