From dbca8f1ea7eb299ae8f1c93a7bd39aa2e247955a Mon Sep 17 00:00:00 2001 From: Benoit Frisque Date: Wed, 23 Oct 2024 18:27:24 +0200 Subject: [PATCH 01/11] added species card --- Dashboards/constants.py | 14 +++++++++ Dashboards/dashboard.py | 65 ++++++++++++++++++++++++++++++++--------- 2 files changed, 66 insertions(+), 13 deletions(-) diff --git a/Dashboards/constants.py b/Dashboards/constants.py index ecbae77..0f4c18d 100644 --- a/Dashboards/constants.py +++ b/Dashboards/constants.py @@ -101,3 +101,17 @@ "plot_bgcolor": "rgba(0, 0, 0, 0)", # Transparent plot background "paper_bgcolor": "rgba(0, 0, 0, 0)", # Transparent paper background } + +TRANSLATIONS_EN_FR = { + "unknown": "inconnu", + "acidophilous": "acidophile", + "neutrophilous": "neutrophile", + "basophilous": "basophile", + "oligotrophic": "oligotrophe", + "mesotrophic": "mésotrophe", + "eutrophic": "eutrophe", + "sensitive": "sensible", + "intermediate": "intermédiaire", + "tolerant": "tolérant", + "resistant": "résistant", +} diff --git a/Dashboards/dashboard.py b/Dashboards/dashboard.py index ba82fa3..9dd1700 100644 --- a/Dashboards/dashboard.py +++ b/Dashboards/dashboard.py @@ -11,7 +11,7 @@ from Dashboards.my_data.datasets import get_useful_data from Dashboards.my_data.computed_datasets import merge_tables, calc_degrees_pollution, calc_vdl, count_lichen, count_lichen_per_species, count_species_per_observation, count_lichen_per_lichen_id, group_lichen_by_observation_and_thallus from Dashboards.charts import blank_figure, create_map, create_hist1_nb_species, create_hist2_vdl, create_hist3, create_pie_thallus, create_hist4, create_gauge_chart -from Dashboards.constants import MAP_SETTINGS, BASE_COLOR_PALETTE, BODY_FONT_FAMILY, POSITIVE_GAUGE_COLOR_PALETTE, NEGATIVE_GAUGE_COLOR_PALETTE +from Dashboards.constants import MAP_SETTINGS, BASE_COLOR_PALETTE, BODY_FONT_FAMILY, POSITIVE_GAUGE_COLOR_PALETTE, NEGATIVE_GAUGE_COLOR_PALETTE, TRANSLATIONS_EN_FR _dash_renderer._set_react_version("18.2.0") @@ -159,6 +159,10 @@ def update_dashboard_observation(clickData, date_range): Output(component_id='map-species_present', component_property='figure'), Output(component_id='hist4-species', component_property='figure'), Output(component_id='lichen-image', component_property='src'), + Output(component_id='acid-badge', component_property='children'), + Output(component_id='eutro-badge', component_property='children'), + Output(component_id='poleo-badge', component_property='children'), + Output(component_id='thallus-badge', component_property='children'), Input(component_id='species-dropdown', component_property='value'), State('map-species_present', 'relayoutData') ) @@ -182,10 +186,24 @@ def update_dashboard2(species_id_selected, relayoutData): fig_map = create_map(observation_with_selected_species_col_df, 'selected_species_present', current_zoom, current_center) - lichen_img = merged_lichen_species_df[merged_lichen_species_df['species_id'] == species_id_selected]['picture'].iloc[0] - lichen_img_path = os.path.join(lichen_img_dir, lichen_img) + # Filter on the selected species + species_selected = merged_lichen_species_df[merged_lichen_species_df['species_id'] == species_id_selected].iloc[0] - return fig_map, hist4_species, lichen_img_path + species_img = species_selected['picture'] + species_img_path = os.path.join(lichen_img_dir, species_img) + + species_acid = species_selected['pH'] + species_eutro = species_selected['eutrophication'] + species_poleo = species_selected['poleotolerance'] + species_thallus = species_selected['thallus'] + + # Translate with the dictionary + species_acid = TRANSLATIONS_EN_FR.get(species_acid, species_acid) + species_eutro = TRANSLATIONS_EN_FR.get(species_eutro, species_eutro) + species_poleo = TRANSLATIONS_EN_FR.get(species_poleo, species_poleo) + species_thallus = TRANSLATIONS_EN_FR.get(species_thallus, species_thallus) + + return fig_map, hist4_species, species_img_path, species_acid, species_eutro, species_poleo, species_thallus def title_and_tooltip(title, tooltip_text): @@ -521,17 +539,38 @@ def title_and_tooltip(title, tooltip_text): align="stretch", children=[ dmc.GridCol( - span=8, + span=6, children=[ dmc.Card([ - dmc.Title("Carte d'identité de l'espèce sélectionnée", order=4, className="graph-title"), - dmc.Image( - id="lichen-image", - radius="md", - src=None, - h=150, - fallbackSrc="https://placehold.co/600x400?text=No%20image%20found", - ), + dmc.Title( + "Carte d'identité de l'espèce sélectionnée", order=4, className="graph-title"), + dmc.Grid( + children=[ + dmc.GridCol( + dmc.Image( + id="lichen-image", + radius="md", + src=None, + h=150, + fallbackSrc="https://placehold.co/600x400?text=No%20image%20found", + ), + span=8), + dmc.GridCol( + dmc.Stack( + [ + dmc.Badge(id="acid-badge"), + dmc.Badge(id="eutro-badge", variant="light"), + dmc.Badge(id="poleo-badge", variant="outline"), + dmc.Badge(id="thallus-badge", variant="light"), + ], + align="center", + gap="md", + ), + span=4) + ], + gutter="md", + grow=False, + ) ], withBorder=True, ) From 2677adbcd5fcfc61cdd1d7512fcd300341c9f98c Mon Sep 17 00:00:00 2001 From: Benoit Frisque Date: Wed, 23 Oct 2024 18:46:43 +0200 Subject: [PATCH 02/11] added calendar icon and reset button --- Dashboards/dashboard.py | 62 ++++++++++++++++++++++++++++++----------- 1 file changed, 46 insertions(+), 16 deletions(-) diff --git a/Dashboards/dashboard.py b/Dashboards/dashboard.py index 9dd1700..2024e2c 100644 --- a/Dashboards/dashboard.py +++ b/Dashboards/dashboard.py @@ -52,6 +52,18 @@ date_range = [merged_observation_df["date_obs"].min(), datetime.now().date()] map_column_selected = list(MAP_SETTINGS.keys())[0] +# Callback to reset the date range +@callback( + Output('date-picker-range', 'value'), + Input('reset-date-button', 'n_clicks'), + State('date-picker-range', 'minDate'), + State('date-picker-range', 'maxDate') +) +def reset_date_range(n_clicks, min_date, max_date): + if n_clicks is None: + raise PreventUpdate + + return [min_date, max_date] # First callback to update the dashboard based on date and map selection @callback( @@ -242,10 +254,41 @@ def title_and_tooltip(title, tooltip_text): html.Div( style={"flex": "5"}, children=[ + dmc.Group( + [ + DashIconify( + icon="mdi:calendar", + width=26, + height=26, + ), + dmc.DatePicker( + id="date-picker-range", + # description="Sélectionner une plage de dates", + minDate=merged_observation_df["date_obs"].min(), + maxDate=datetime.now().date(), + type="range", + value=[ + merged_observation_df["date_obs"].min(), + datetime.now().date(), + ], + valueFormat="DD/MM/YYYY", + w=170 # width + ), + dmc.Button( + id="reset-date-button", + children="✖", + variant="outline", + color="red", + size="xs", + )], + align="center", + gap="xs", + ), # Divider for the map title and selector - html.Div( - style={"display": "flex", - "align-items": "center", "gap": "10px", }, + dmc.Group( + style={ + #"display": "flex", + "align-items": "center", "gap": "10px" }, children=[ dmc.Title( "Carte des observations", @@ -650,19 +693,6 @@ def switch_theme(_, theme): sidebar = dmc.Box( children=[ theme_toggle, - dmc.DatePicker( - id="date-picker-range", - label="Sélectionner une plage de dates", - minDate=merged_observation_df["date_obs"].min(), - maxDate=datetime.now().date(), - type="range", - value=[ - merged_observation_df["date_obs"].min(), - datetime.now().date(), - ], - valueFormat="DD/MM/YYYY", - w=110, # width - ), ], style={"width": "120px", "padding": "10px"}, ) From a33ce92a804768a7764bce8c7a55eb893d020ca0 Mon Sep 17 00:00:00 2001 From: Benoit Frisque Date: Wed, 23 Oct 2024 19:02:51 +0200 Subject: [PATCH 03/11] moved dark mode button to top right --- Dashboards/dashboard.py | 62 ++++++++++++++++++++--------------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/Dashboards/dashboard.py b/Dashboards/dashboard.py index 2024e2c..9aced95 100644 --- a/Dashboards/dashboard.py +++ b/Dashboards/dashboard.py @@ -57,10 +57,11 @@ Output('date-picker-range', 'value'), Input('reset-date-button', 'n_clicks'), State('date-picker-range', 'minDate'), - State('date-picker-range', 'maxDate') + State('date-picker-range', 'maxDate'), + State('date-picker-range', 'value') ) -def reset_date_range(n_clicks, min_date, max_date): - if n_clicks is None: +def reset_date_range(n_clicks, min_date, max_date, date_range): + if n_clicks is None or date_range == [min_date, max_date]: raise PreventUpdate return [min_date, max_date] @@ -262,25 +263,26 @@ def title_and_tooltip(title, tooltip_text): height=26, ), dmc.DatePicker( - id="date-picker-range", - # description="Sélectionner une plage de dates", - minDate=merged_observation_df["date_obs"].min(), - maxDate=datetime.now().date(), - type="range", - value=[ - merged_observation_df["date_obs"].min(), - datetime.now().date(), - ], - valueFormat="DD/MM/YYYY", - w=170 # width - ), + id="date-picker-range", + # description="Sélectionner une plage de dates", + minDate=merged_observation_df["date_obs"].min(), + maxDate=datetime.now().date(), + type="range", + value=[ + merged_observation_df["date_obs"].min(), + datetime.now().date(), + ], + valueFormat="DD/MM/YYYY", + w=170 # width + ), dmc.Button( - id="reset-date-button", - children="✖", - variant="outline", - color="red", - size="xs", - )], + id="reset-date-button", + children="✖", + variant="outline", + color="red", + size="xs", + ) + ], align="center", gap="xs", ), @@ -653,7 +655,12 @@ def title_and_tooltip(title, tooltip_text): variant="transparent", id="color-scheme-toggle", size="lg", - ms="auto", + style={ + "position": "fixed", + "top": "21px", + "right": "21px", + "zIndex": 1000 + } ) @@ -690,13 +697,6 @@ def switch_theme(_, theme): ) -sidebar = dmc.Box( - children=[ - theme_toggle, - ], - style={"width": "120px", "padding": "10px"}, -) - dashboards_layout = dmc.Box( children=[ dmc.Accordion( @@ -768,9 +768,9 @@ def switch_theme(_, theme): children=[ dmc.Group( children=[ - sidebar, - dmc.Divider(orientation="vertical"), + # dmc.Divider(orientation="vertical"), dashboards_layout, + theme_toggle, ], align="start", ) From 447f5d2274464549490c95121200c0759b6b98ff Mon Sep 17 00:00:00 2001 From: Benoit Frisque Date: Wed, 23 Oct 2024 21:48:34 +0200 Subject: [PATCH 04/11] adde variables for map and updated plotly version --- Dashboards/charts.py | 8 +- Dashboards/constants.py | 13 + Dashboards/dashboard.py | 544 ++++++++++++------------ Dashboards/my_data/computed_datasets.py | 7 +- poetry.lock | 8 +- 5 files changed, 300 insertions(+), 280 deletions(-) diff --git a/Dashboards/charts.py b/Dashboards/charts.py index 3cf8458..7a15018 100644 --- a/Dashboards/charts.py +++ b/Dashboards/charts.py @@ -20,22 +20,22 @@ def blank_figure(): return fig def create_map(filtered_df, selected_map_column, zoom, center): - fig_map = px.scatter_mapbox( + fig_map = px.scatter_map( filtered_df, lat="localisation_lat", lon="localisation_long", color=selected_map_column, hover_name="date_obs", hover_data=["localisation_lat", "localisation_long"], - mapbox_style="open-street-map", + map_style="open-street-map", color_discrete_map=MAP_SETTINGS[selected_map_column]["color_map"], ) fig_map.update_layout( PLOTLY_LAYOUT, margin=dict(l=0, r=0, t=0, b=0), - mapbox_zoom=zoom, - mapbox_center=center, + map_zoom=zoom, + map_center=center, legend=dict( x=0.02, # Position the legend on the map y=0.02, diff --git a/Dashboards/constants.py b/Dashboards/constants.py index 0f4c18d..3b259d6 100644 --- a/Dashboards/constants.py +++ b/Dashboards/constants.py @@ -76,6 +76,19 @@ "title": "VDL", "color_map": {'<25': 'red', '25-50': 'orange', '50-75': 'yellow', '>75': 'green'} }, + "deg_pollution_acid_cat": { + "title": "Dégré pollution acide", + "color_map": {'0-25%': 'red', '25-50%': 'orange', '50-75%': 'yellow', '75-100%': 'green'} + }, + "deg_pollution_azote_cat": { + "title": "Dégré pollution azote", + "color_map": {'0-25%': 'green', '25-50%': 'yellow', '50-75%': 'orange', '75-100%': 'red'} + }, + "deg_artif_cat": + { + "title": "Dégré artificialisation", + "color_map": {'0-25%': 'green', '25-50%': 'yellow', '50-75%': 'orange', '75-100%': 'red'} + }, "selected_species_present": { "title": "Espèce sélectionnée présente", diff --git a/Dashboards/dashboard.py b/Dashboards/dashboard.py index 9aced95..63a7cd6 100644 --- a/Dashboards/dashboard.py +++ b/Dashboards/dashboard.py @@ -84,9 +84,9 @@ def update_dashboard_map(date_range, map_column_selected, relayoutData): filtered_observation_df = merged_observation_df[(merged_observation_df['date_obs'] >= start_date) & (merged_observation_df['date_obs'] <= end_date)] - if relayoutData and "mapbox.zoom" in relayoutData and "mapbox.center" in relayoutData: - current_zoom = relayoutData["mapbox.zoom"] - current_center = relayoutData["mapbox.center"] + if relayoutData and "map.zoom" in relayoutData and "map.center" in relayoutData: + current_zoom = relayoutData["map.zoom"] + current_center = relayoutData["map.center"] else: current_zoom = 4.8 current_center = {"lat": filtered_observation_df['localisation_lat'].mean() + 0.5, "lon": filtered_observation_df['localisation_long'].mean()} @@ -246,196 +246,182 @@ def title_and_tooltip(title, tooltip_text): # Layout for the sites (observations) -sites_layout = [ - # Divider for the 2 columns - html.Div( - style={"display": "flex", "gap": "10px"}, - children=[ - # Divider for the first column with map and gauge - html.Div( - style={"flex": "5"}, - children=[ - dmc.Group( - [ - DashIconify( - icon="mdi:calendar", - width=26, - height=26, - ), - dmc.DatePicker( - id="date-picker-range", - # description="Sélectionner une plage de dates", - minDate=merged_observation_df["date_obs"].min(), - maxDate=datetime.now().date(), - type="range", - value=[ - merged_observation_df["date_obs"].min(), - datetime.now().date(), - ], - valueFormat="DD/MM/YYYY", - w=170 # width - ), - dmc.Button( - id="reset-date-button", - children="✖", - variant="outline", - color="red", - size="xs", - ) - ], - align="center", - gap="xs", - ), - # Divider for the map title and selector - dmc.Group( - style={ - #"display": "flex", - "align-items": "center", "gap": "10px" }, - children=[ - dmc.Title( - "Carte des observations", - order=4, - className="graph-title", - style={"padding": "0px"}, - ), - # Selector for the map column - dmc.SegmentedControl( - id="map-column-select", - value=list(MAP_SETTINGS.keys())[0], - data=[ - {"label": MAP_SETTINGS[col] - ["title"], "value": col} - for col in ["nb_species_cat", "VDL_cat"] - ], - transitionDuration=500, - ), - ], - ), - html.Div( - children=[ - dmc.Card( - children=[ - dcc.Graph( - id="map-nb_species-vdl", - figure=blank_fig, - config={ - "displaylogo": False, # Remove plotly logo - }, - ), - ], - withBorder=True, - shadow="sm", - # Remove padding between the card and the map - style={"padding": "0"}, - ), - ], - ), - # Divider for the gauge charts, with 3 columns each - html.Div( - style={"display": "flex", "gap": "10px", - "padding-top": "10px"}, - children=[ - html.Div( - style={"flex": "1"}, - children=[ - dmc.Card( - children=[ - title_and_tooltip( - title="% Espèces toxitolérantes", - tooltip_text="Pourcentage d'espèces toxitolérantes sur le site sélectionné", - ), - dcc.Graph( - id="gauge-chart1-artif", - figure=blank_fig, - style={"height": "100px"}, - config={ - "displayModeBar": False, # Remove plotly tool bar - }, - ), - ], - withBorder=True, - shadow="sm", - # Reduce padding between the card and the gauge - style={ - "padding-top": "5px", "padding-left": "5px", "padding-right": "5px"}, - ), - ], - ), - html.Div( - style={"flex": "1"}, - children=[ - dmc.Card( - children=[ - title_and_tooltip( - title="% Espèces eutophes", - tooltip_text="Pourcentage d'espèces eutrophes sur le site sélectionné" - ), - dcc.Graph( - id="gauge-chart3-azote", - figure=blank_fig, - style={"height": "100px"}, - config={ - "displayModeBar": False, # Remove plotly tool bar - }, - ), - ], - withBorder=True, - shadow="sm", - # Reduce padding between the card and the gauge - style={ - "padding-top": "5px", "padding-left": "5px", "padding-right": "5px"}, - ), - ], - ), - html.Div( - style={"flex": "1"}, - children=[ - dmc.Card( - children=[ - title_and_tooltip( - title="% Espèces acidophiles", - tooltip_text="Pourcentage d'espèces acidophiles sur le site sélectionné" - ), - dcc.Graph( - id="gauge-chart2-acide", - figure=blank_fig, - style={"height": "100px"}, - config={ - "displayModeBar": False, # Remove plotly tool bar - }, - ), - ], - withBorder=True, - shadow="sm", - # Reduce padding between the card and the gauge - style={ - "padding-top": "5px", "padding-left": "5px", "padding-right": "5px"}, - ), - ], - ), - ], - ), - ], - ), - # Divider for the second column with histograms - html.Div( - style={"flex": "5", - "padding": "5px", - }, - children=[ - dmc.Grid( - gutter="md", - align="stretch", - children=[ - # Column for hist1 - dmc.GridCol( - span=6, - children=[ - title_and_tooltip( - title="Distribution du nombre d'espèces", - tooltip_text="Distribution du nombre d'espèces par site. Si vous cliquez sur un site sur la carte, son nombre d'espèce sera affiché en trait pointillé rouge." - ), - dmc.Card( +sites_layout = html.Div( # Divider for the 2 columns + style={"display": "flex", "gap": "10px"}, + children=[ + # Divider for the first column with map and gauge + html.Div( + style={"flex-grow": "1", "flex-basis": "auto"}, + children=[ + dmc.Group( + [ + DashIconify( + icon="mdi:calendar", + width=26, + height=26, + ), + dmc.DatePicker( + id="date-picker-range", + # description="Sélectionner une plage de dates", + minDate=merged_observation_df["date_obs"].min(), + maxDate=datetime.now().date(), + type="range", + value=[ + merged_observation_df["date_obs"].min(), + datetime.now().date(), + ], + valueFormat="DD/MM/YYYY", + w=170 # width + ), + dmc.Button( + id="reset-date-button", + children="✖", + variant="outline", + color="red", + size="xs", + ) + ], + align="center", + gap="xs", + ), + dmc.Title( + "Carte des observations", + order=4, + className="graph-title", + ), + # Selector for the map column + dmc.SegmentedControl( + id="map-column-select", + value=list(MAP_SETTINGS.keys())[0], + data=[ + {"label": MAP_SETTINGS[col] + ["title"], "value": col} + for col in ["nb_species_cat", "VDL_cat", "deg_pollution_acid_cat", "deg_pollution_azote_cat", "deg_artif_cat"] + ], + transitionDuration=600, + transitionTimingFunction="ease-in-out", + ), + html.Div( + children=[ + dmc.Card( + children=[ + dcc.Graph( + id="map-nb_species-vdl", + figure=blank_fig, + style={"height": "469px"}, # play on that parameter to align all graphs + config={ + "displaylogo": False, # Remove plotly logo + }, + ), + ], + withBorder=True, + shadow="sm", + # Remove padding between the card and the map + style={"padding": "0"}, + ), + ], + ), + # Divider for the gauge charts, with 3 columns each + html.Div( + style={"display": "flex", "gap": "10px", "padding-top": "10px"}, + children=[ + html.Div( + style={"flex": "1"}, + children=[ + dmc.Card( + children=[ + title_and_tooltip( + title="% Espèces toxitolérantes", + tooltip_text="Pourcentage d'espèces toxitolérantes sur le site sélectionné", + ), + dcc.Graph( + id="gauge-chart1-artif", + figure=blank_fig, + style={"height": "100px"}, + config={ + "displayModeBar": False, # Remove plotly tool bar + }, + ), + ], + withBorder=True, + shadow="sm", + # Reduce padding between the card and the gauge + style={"padding-top": "5px", "padding-left": "5px", "padding-right": "5px"}, + ), + ], + ), + html.Div( + style={"flex": "1"}, + children=[ + dmc.Card( + children=[ + title_and_tooltip( + title="% Espèces eutrophes", + tooltip_text="Pourcentage d'espèces eutrophes sur le site sélectionné" + ), + dcc.Graph( + id="gauge-chart3-azote", + figure=blank_fig, + style={"height": "100px"}, + config={ + "displayModeBar": False, # Remove plotly tool bar + }, + ), + ], + withBorder=True, + shadow="sm", + # Reduce padding between the card and the gauge + style={"padding-top": "5px", "padding-left": "5px", "padding-right": "5px"}, + ), + ], + ), + html.Div( + style={"flex": "1"}, + children=[ + dmc.Card( + children=[ + title_and_tooltip( + title="% Espèces acidophiles", + tooltip_text="Pourcentage d'espèces acidophiles sur le site sélectionné" + ), + dcc.Graph( + id="gauge-chart2-acide", + figure=blank_fig, + style={"height": "100px"}, + config={ + "displayModeBar": False, # Remove plotly tool bar + }, + ), + ], + withBorder=True, + shadow="sm", + # Reduce padding between the card and the gauge + style={"padding-top": "5px", "padding-left": "5px", "padding-right": "5px"}, + ), + ], + ), + ], + ), + ], + ), + # Divider for the second column with histograms + html.Div( + style={"flex-grow": "1", "flex-basis": "auto"}, + children=[ + dmc.Grid( + gutter="md", + align="stretch", + children=[ + # Column for hist1 + dmc.GridCol( + span=6, + children=[ + dmc.Card( + children=[ + title_and_tooltip( + title="Distribution du nombre d'espèces", + tooltip_text="Distribution du nombre d'espèces par site. Si vous cliquez sur un site sur la carte, son nombre d'espèce sera affiché en trait pointillé rouge." + ), dcc.Graph( id="hist1-nb_species", figure=blank_fig, @@ -444,78 +430,98 @@ def title_and_tooltip(title, tooltip_text): "displaylogo": False, # Remove plotly logo }, ), - withBorder=True, - shadow="sm", - ) - ], - ), - # Column for hist2 - dmc.GridCol( - span=6, - children=[ - title_and_tooltip( - title="Distribution de VDL", - tooltip_text="Distribution des valeurs de Diversité Lichénique (VDL) sur l'ensemble des sites. Si vous cliquez sur un site sur la carte, sa VDL sera affichée en trait pointillé rouge." - ), - dcc.Graph( - id="hist2-vdl", - figure=blank_fig, - style={"height": "300px"}, - config={ - "displaylogo": False, # Remove plotly logo - }, - ), - ], - ), - # Column for hist3 - dmc.GridCol( - span=8, - children=[ - dmc.Card( - children=[ - title_and_tooltip( - title="Espèces observées sur le site sélectionné", - tooltip_text="Distribution des espèces observées sur le site sélectionné" - ), - dcc.Graph( - id="hist3-species", - figure=blank_fig, - style={"height": "300px"}, - config={ - "displaylogo": False, # Remove plotly logo - }, - ), - ], - withBorder=True, - shadow="sm", - ), - ], - ), - # Column for pie chart - dmc.GridCol( - span=4, - children=[ - title_and_tooltip( - title="Morphologie du site sélectionné", - tooltip_text="Distribution des thalles sur le site sélectionné" - ), - dcc.Graph( - id="pie-thallus", - figure=blank_fig, - style={"height": "250px"}, - config={ - "displaylogo": False, # Remove plotly logo - }, - ), - ], - ), - ], - ) - ], - ), - ], - ), -] + ], + withBorder=True, + shadow="sm", + pt="xs", + pb="xs", + ), + ], + ), + # Column for hist2 + dmc.GridCol( + span=6, + children=[ + dmc.Card( + children=[ + title_and_tooltip( + title="Distribution de VDL", + tooltip_text="Distribution des valeurs de Diversité Lichénique (VDL) sur l'ensemble des sites. Si vous cliquez sur un site sur la carte, sa VDL sera affichée en trait pointillé rouge." + ), + dcc.Graph( + id="hist2-vdl", + figure=blank_fig, + style={"height": "300px"}, + config={ + "displaylogo": False, # Remove plotly logo + }, + ), + ], + withBorder=True, + shadow="sm", + pt="xs", + pb="xs", + ), + ], + ), + # Column for hist3 + dmc.GridCol( + span=7, + children=[ + dmc.Card( + children=[ + title_and_tooltip( + title="Espèces observées sur le site sélectionné", + tooltip_text="Distribution des espèces observées sur le site sélectionné" + ), + dcc.Graph( + id="hist3-species", + figure=blank_fig, + style={"height": "300px"}, + config={ + "displaylogo": False, # Remove plotly logo + }, + ), + ], + withBorder=True, + shadow="sm", + pt="xs", + pb="xs", + ), + ], + ), + # Column for pie chart + dmc.GridCol( + span=5, + children=[ + dmc.Card( + children=[ + title_and_tooltip( + title="Morphologie du site sélectionné", + tooltip_text="Distribution des thalles sur le site sélectionné" + ), + dcc.Graph( + id="pie-thallus", + figure=blank_fig, + style={"height": "300px"}, + config={ + "displaylogo": False, # Remove plotly logo + }, + ), + ], + withBorder=True, + shadow="sm", + pt="xs", + pb="xs", + ), + ], + ), + ], + ) + ], + ), + ], +) # Layout for the "Espèces" tab species_layout = html.Div( # Divider for 2 columns @@ -712,18 +718,14 @@ def switch_theme(_, theme): children=[ DashIconify( icon="tabler:map-pin", - height=20, + height=25, color=BASE_COLOR_PALETTE[0] ), - dmc.Title("Sites", order=3), dmc.Tooltip( label="Cliquez sur un site pour découvrir ce que les lichens peuvent nous apprendre", position="right", withArrow=True, - children=DashIconify( - icon="material-symbols:info-outline", - className="info-icon", - ) + children=dmc.Title("Sites", order=3) ), ], align="center", diff --git a/Dashboards/my_data/computed_datasets.py b/Dashboards/my_data/computed_datasets.py index 594e142..d6f93f2 100644 --- a/Dashboards/my_data/computed_datasets.py +++ b/Dashboards/my_data/computed_datasets.py @@ -109,7 +109,7 @@ def unique_lichen_name(nb_lichen_per_lichen_id_df): -# Count the number of lichen (species) for each observation +# Count the number of lichen species for each observation def count_species_per_observation(lichen_df, observation_df): # Count the number of different lichen (=lines in the df) per observation count_species_per_observation_df = lichen_df['observation_id'].value_counts().to_frame().rename(columns={"count":"nb_species"}) @@ -200,6 +200,7 @@ def calc_degrees_pollution(merged_table_with_nb_lichen_df, lichen_df, merged_lic # Calculate the ratio of resistant nb_lichen to total nb_lichen merged_df['deg_artif'] = merged_df['nb_lichen_resistant'] / merged_df['nb_lichen'] + # Add a categorical column based on the number of lichen # Calculate the degree of acid pollution merged_df['deg_pollution_acid'] = merged_df['nb_lichen_acid'] / merged_df['nb_lichen'] @@ -207,4 +208,8 @@ def calc_degrees_pollution(merged_table_with_nb_lichen_df, lichen_df, merged_lic # Calculate the degree of nitrogen (azote in french) pollution merged_df['deg_pollution_azote'] = merged_df['nb_lichen_eutrophic'] / merged_df['nb_lichen'] + # Add categorical columns + for col in ['deg_artif', 'deg_pollution_acid', 'deg_pollution_azote']: + merged_df[col + '_cat'] = pd.cut(merged_df[col], bins=[-0.1, 0.25, 0.5, 0.75, np.inf], labels=["0-25%", "25-50%", "50-75%", "75-100%"]) + return merged_df diff --git a/poetry.lock b/poetry.lock index b819e05..92823a5 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand. [[package]] name = "anyio" @@ -2164,13 +2164,13 @@ type = ["mypy (>=1.8)"] [[package]] name = "plotly" -version = "5.22.0" +version = "5.24.1" description = "An open-source, interactive data visualization library for Python" optional = false python-versions = ">=3.8" files = [ - {file = "plotly-5.22.0-py3-none-any.whl", hash = "sha256:68fc1901f098daeb233cc3dd44ec9dc31fb3ca4f4e53189344199c43496ed006"}, - {file = "plotly-5.22.0.tar.gz", hash = "sha256:859fdadbd86b5770ae2466e542b761b247d1c6b49daed765b95bb8c7063e7469"}, + {file = "plotly-5.24.1-py3-none-any.whl", hash = "sha256:f67073a1e637eb0dc3e46324d9d51e2fe76e9727c892dde64ddf1e1b51f29089"}, + {file = "plotly-5.24.1.tar.gz", hash = "sha256:dbc8ac8339d248a4bcc36e08a5659bacfe1b079390b8953533f4eb22169b4bae"}, ] [package.dependencies] From f19d5df9c5a053eb1f63135249e1657b95300099 Mon Sep 17 00:00:00 2001 From: Benoit Frisque Date: Thu, 24 Oct 2024 10:25:23 +0200 Subject: [PATCH 05/11] refactored layout --- Dashboards/dashboard.py | 314 ++++++++++++++-------------------------- 1 file changed, 105 insertions(+), 209 deletions(-) diff --git a/Dashboards/dashboard.py b/Dashboards/dashboard.py index 63a7cd6..04c067d 100644 --- a/Dashboards/dashboard.py +++ b/Dashboards/dashboard.py @@ -48,9 +48,14 @@ # Initialize a blank figure to show during loading blank_fig = blank_figure() -# Initialize the selections +# Initialize the options and selections date_range = [merged_observation_df["date_obs"].min(), datetime.now().date()] map_column_selected = list(MAP_SETTINGS.keys())[0] +species_options = [ + {"label": row["name"], "value": row["species_id"]} + for _, row in nb_lichen_per_species_df.sort_values(by="name").iterrows() +] +species_id_selected = species_options[0]['value'] # Default to the first species ID # Callback to reset the date range @callback( @@ -202,7 +207,7 @@ def update_dashboard2(species_id_selected, relayoutData): # Filter on the selected species species_selected = merged_lichen_species_df[merged_lichen_species_df['species_id'] == species_id_selected].iloc[0] - species_img = species_selected['picture'] + species_img = species_selected['picture'] species_img_path = os.path.join(lichen_img_dir, species_img) species_acid = species_selected['pH'] @@ -219,50 +224,85 @@ def update_dashboard2(species_id_selected, relayoutData): return fig_map, hist4_species, species_img_path, species_acid, species_eutro, species_poleo, species_thallus +# Constants for common styles +FLEX_COLUMNS_CONTAINER_STYLE = {"display": "flex", "gap": "16px"} +GRID_STYLE = {"gutter": "md", "align": "stretch"} +CARD_STYLE = {"p": "5px", "shadow": "sm", "withBorder": True} +GAUGE_GRAPH_STYLE = {"height": "100px"} +HIST_GRAPH_STYLE = {"height": "300px"} +MAP_STYLE = { + "withBorder": True, + "shadow": "sm", + "p": 0, + "mt": 8, + "mb": 16 +} + +# Reusable component for title and tooltip def title_and_tooltip(title, tooltip_text): - return html.Div( - style={"display": "flex", "align-items": "center"}, + return dmc.Group( children=[ - dmc.Title(title, order=4, className="graph-title"), + dmc.Title(title, order=4), dmc.Tooltip( - label=tooltip_text, - position="top", - withArrow=True, children=DashIconify( icon="material-symbols:info-outline", className="info-icon", ), + label=tooltip_text, + withArrow=True, + position="top", ), ], + align="center", + gap="xs", ) +# Reusable component for gauge cards +def gauge_card(title, tooltip_text, graph_id): + return dmc.Card( + children=[ + title_and_tooltip(title, tooltip_text), + dcc.Graph( + id=graph_id, + figure=blank_fig, + style=GAUGE_GRAPH_STYLE, + config={"displayModeBar": False}, + ), + ], + **CARD_STYLE + ) -# Create options for the user species dropdown, sorted by name -species_options = [ - {"label": row["name"], "value": row["species_id"]} - for _, row in nb_lichen_per_species_df.sort_values(by="name").iterrows() -] -species_id_selected = species_options[0]['value'] # Default to the first species ID - +# Reusable component for histogram cards +def histogram_card(title, tooltip_text, graph_id, height="300px"): + return dmc.Card( + children=[ + title_and_tooltip(title, tooltip_text), + dcc.Graph( + id=graph_id, + figure=blank_fig, + style={"height": height}, + config={"displaylogo": False}, + ), + ], + withBorder=True, + shadow="sm", + pt="xs", + pb="xs", + ) # Layout for the sites (observations) -sites_layout = html.Div( # Divider for the 2 columns - style={"display": "flex", "gap": "10px"}, +sites_layout = html.Div( + style=FLEX_COLUMNS_CONTAINER_STYLE, children=[ - # Divider for the first column with map and gauge + # First column with map and gauge html.Div( - style={"flex-grow": "1", "flex-basis": "auto"}, + style={"flex-grow": "1", "flex-basis": "auto", "flex-shrink": "1"}, children=[ dmc.Group( [ - DashIconify( - icon="mdi:calendar", - width=26, - height=26, - ), + DashIconify(icon="mdi:calendar", width=26, height=26), dmc.DatePicker( id="date-picker-range", - # description="Sélectionner une plage de dates", minDate=merged_observation_df["date_obs"].min(), maxDate=datetime.now().date(), type="range", @@ -271,7 +311,7 @@ def title_and_tooltip(title, tooltip_text): datetime.now().date(), ], valueFormat="DD/MM/YYYY", - w=170 # width + w=170, ), dmc.Button( id="reset-date-button", @@ -279,23 +319,17 @@ def title_and_tooltip(title, tooltip_text): variant="outline", color="red", size="xs", - ) + ), ], align="center", gap="xs", ), - dmc.Title( - "Carte des observations", - order=4, - className="graph-title", - ), - # Selector for the map column + dmc.Title("Carte des observations", order=4, className="graph-title"), dmc.SegmentedControl( id="map-column-select", value=list(MAP_SETTINGS.keys())[0], data=[ - {"label": MAP_SETTINGS[col] - ["title"], "value": col} + {"label": MAP_SETTINGS[col]["title"], "value": col} for col in ["nb_species_cat", "VDL_cat", "deg_pollution_acid_cat", "deg_pollution_azote_cat", "deg_artif_cat"] ], transitionDuration=600, @@ -308,224 +342,89 @@ def title_and_tooltip(title, tooltip_text): dcc.Graph( id="map-nb_species-vdl", figure=blank_fig, - style={"height": "469px"}, # play on that parameter to align all graphs - config={ - "displaylogo": False, # Remove plotly logo - }, + style={"height": "469px"}, + config={"displaylogo": False}, ), ], - withBorder=True, - shadow="sm", - # Remove padding between the card and the map - style={"padding": "0"}, + **MAP_STYLE ), ], ), - # Divider for the gauge charts, with 3 columns each - html.Div( - style={"display": "flex", "gap": "10px", "padding-top": "10px"}, + dmc.Grid( + **GRID_STYLE, children=[ - html.Div( - style={"flex": "1"}, - children=[ - dmc.Card( - children=[ - title_and_tooltip( - title="% Espèces toxitolérantes", - tooltip_text="Pourcentage d'espèces toxitolérantes sur le site sélectionné", - ), - dcc.Graph( - id="gauge-chart1-artif", - figure=blank_fig, - style={"height": "100px"}, - config={ - "displayModeBar": False, # Remove plotly tool bar - }, - ), - ], - withBorder=True, - shadow="sm", - # Reduce padding between the card and the gauge - style={"padding-top": "5px", "padding-left": "5px", "padding-right": "5px"}, - ), - ], + dmc.GridCol(gauge_card("% Espèces toxitolérantes", "Pourcentage d'espèces toxitolérantes sur le site sélectionné", "gauge-chart1-artif"), + span=4.2, ), - html.Div( - style={"flex": "1"}, - children=[ - dmc.Card( - children=[ - title_and_tooltip( - title="% Espèces eutrophes", - tooltip_text="Pourcentage d'espèces eutrophes sur le site sélectionné" - ), - dcc.Graph( - id="gauge-chart3-azote", - figure=blank_fig, - style={"height": "100px"}, - config={ - "displayModeBar": False, # Remove plotly tool bar - }, - ), - ], - withBorder=True, - shadow="sm", - # Reduce padding between the card and the gauge - style={"padding-top": "5px", "padding-left": "5px", "padding-right": "5px"}, - ), - ], + dmc.GridCol( + gauge_card("% Espèces eutrophes", "Pourcentage d'espèces eutrophes sur le site sélectionné", "gauge-chart3-azote"), + span=3.9, ), - html.Div( - style={"flex": "1"}, - children=[ - dmc.Card( - children=[ - title_and_tooltip( - title="% Espèces acidophiles", - tooltip_text="Pourcentage d'espèces acidophiles sur le site sélectionné" - ), - dcc.Graph( - id="gauge-chart2-acide", - figure=blank_fig, - style={"height": "100px"}, - config={ - "displayModeBar": False, # Remove plotly tool bar - }, - ), - ], - withBorder=True, - shadow="sm", - # Reduce padding between the card and the gauge - style={"padding-top": "5px", "padding-left": "5px", "padding-right": "5px"}, - ), - ], + dmc.GridCol( + gauge_card("% Espèces acidophiles", "Pourcentage d'espèces acidophiles sur le site sélectionné", "gauge-chart2-acide"), + span=3.9, ), ], ), ], ), - # Divider for the second column with histograms + # Second column with histograms html.Div( - style={"flex-grow": "1", "flex-basis": "auto"}, + style={"flex-grow": "1", "flex-basis": "auto", "flex-shrink": "1"}, children=[ dmc.Grid( - gutter="md", - align="stretch", + **GRID_STYLE, children=[ - # Column for hist1 dmc.GridCol( span=6, children=[ - dmc.Card( - children=[ - title_and_tooltip( - title="Distribution du nombre d'espèces", - tooltip_text="Distribution du nombre d'espèces par site. Si vous cliquez sur un site sur la carte, son nombre d'espèce sera affiché en trait pointillé rouge." - ), - dcc.Graph( - id="hist1-nb_species", - figure=blank_fig, - style={"height": "300px"}, - config={ - "displaylogo": False, # Remove plotly logo - }, - ), - ], - withBorder=True, - shadow="sm", - pt="xs", - pb="xs", + histogram_card( + "Distribution du nombre d'espèces", + "Distribution du nombre d'espèces par site. Si vous cliquez sur un site sur la carte, son nombre d'espèce sera affiché en trait pointillé rouge.", + "hist1-nb_species", ), ], ), - # Column for hist2 dmc.GridCol( span=6, children=[ - dmc.Card( - children=[ - title_and_tooltip( - title="Distribution de VDL", - tooltip_text="Distribution des valeurs de Diversité Lichénique (VDL) sur l'ensemble des sites. Si vous cliquez sur un site sur la carte, sa VDL sera affichée en trait pointillé rouge." - ), - dcc.Graph( - id="hist2-vdl", - figure=blank_fig, - style={"height": "300px"}, - config={ - "displaylogo": False, # Remove plotly logo - }, - ), - ], - withBorder=True, - shadow="sm", - pt="xs", - pb="xs", + histogram_card( + "Distribution de VDL", + "Distribution des valeurs de Diversité Lichénique (VDL) sur l'ensemble des sites. Si vous cliquez sur un site sur la carte, sa VDL sera affichée en trait pointillé rouge.", + "hist2-vdl", ), ], ), - # Column for hist3 dmc.GridCol( span=7, children=[ - dmc.Card( - children=[ - title_and_tooltip( - title="Espèces observées sur le site sélectionné", - tooltip_text="Distribution des espèces observées sur le site sélectionné" - ), - dcc.Graph( - id="hist3-species", - figure=blank_fig, - style={"height": "300px"}, - config={ - "displaylogo": False, # Remove plotly logo - }, - ), - ], - withBorder=True, - shadow="sm", - pt="xs", - pb="xs", + histogram_card( + "Espèces observées sur le site sélectionné", + "Distribution des espèces observées sur le site sélectionné", + "hist3-species", ), ], ), - # Column for pie chart dmc.GridCol( span=5, children=[ - dmc.Card( - children=[ - title_and_tooltip( - title="Morphologie du site sélectionné", - tooltip_text="Distribution des thalles sur le site sélectionné" - ), - dcc.Graph( - id="pie-thallus", - figure=blank_fig, - style={"height": "300px"}, - config={ - "displaylogo": False, # Remove plotly logo - }, - ), - ], - withBorder=True, - shadow="sm", - pt="xs", - pb="xs", + histogram_card( + "Morphologie du site sélectionné", + "Distribution des thalles sur le site sélectionné", + "pie-thallus", ), ], ), ], - ) + ), ], ), ], ) # Layout for the "Espèces" tab -species_layout = html.Div( # Divider for 2 columns - style={"display": "flex", "gap": "20px"}, +species_layout = html.Div( + style=FLEX_COLUMNS_CONTAINER_STYLE, children=[ # Divider for the first column with selector and map html.Div( @@ -572,10 +471,7 @@ def title_and_tooltip(title, tooltip_text): }, ), ], - withBorder=True, - shadow="sm", - # Remove padding between the card and the map - style={"padding": "0"}, + **MAP_STYLE ), ], ), From eca15bc2b39e8222f0456b4bf95ac7600d31ad80 Mon Sep 17 00:00:00 2001 From: Benoit Frisque Date: Thu, 24 Oct 2024 10:44:42 +0200 Subject: [PATCH 06/11] updated species dropdown with dmc library --- Dashboards/dashboard.py | 49 +++++++++++++++++------------------------ 1 file changed, 20 insertions(+), 29 deletions(-) diff --git a/Dashboards/dashboard.py b/Dashboards/dashboard.py index 04c067d..113ddc4 100644 --- a/Dashboards/dashboard.py +++ b/Dashboards/dashboard.py @@ -51,11 +51,11 @@ # Initialize the options and selections date_range = [merged_observation_df["date_obs"].min(), datetime.now().date()] map_column_selected = list(MAP_SETTINGS.keys())[0] -species_options = [ - {"label": row["name"], "value": row["species_id"]} - for _, row in nb_lichen_per_species_df.sort_values(by="name").iterrows() -] -species_id_selected = species_options[0]['value'] # Default to the first species ID + + +# Convert DataFrame to list of dictionaries +species_options = [{"value": str(row["species_id"]), "label": row["name"]} for _, row in merged_lichen_species_df.sort_values(by="name").iterrows()] +species_id_selected = species_options[0]["value"] # Default to the first species ID # Callback to reset the date range @callback( @@ -185,6 +185,8 @@ def update_dashboard_observation(clickData, date_range): State('map-species_present', 'relayoutData') ) def update_dashboard2(species_id_selected, relayoutData): + if isinstance(species_id_selected, str): + species_id_selected = int(species_id_selected) hist4_species = create_hist4(nb_lichen_per_species_df, species_id_selected) @@ -296,7 +298,7 @@ def histogram_card(title, tooltip_text, graph_id, height="300px"): children=[ # First column with map and gauge html.Div( - style={"flex-grow": "1", "flex-basis": "auto", "flex-shrink": "1"}, + style={"flex-grow": "1", "flex-basis": "auto"}, children=[ dmc.Group( [ @@ -370,7 +372,7 @@ def histogram_card(title, tooltip_text, graph_id, height="300px"): ), # Second column with histograms html.Div( - style={"flex-grow": "1", "flex-basis": "auto", "flex-shrink": "1"}, + style={"flex-grow": "1", "flex-basis": "auto"}, children=[ dmc.Grid( **GRID_STYLE, @@ -430,28 +432,17 @@ def histogram_card(title, tooltip_text, graph_id, height="300px"): html.Div( style={"flex": "5"}, children=[ - html.Div( - [ - html.Label( - "Sélectionner une espèce:", - style={ - "margin-right": "10px", - }, - ), - dcc.Dropdown( - id="species-dropdown", - options=species_options, - value=species_id_selected, - clearable=False, - style={"width": "400px"}, - ), - ], - style={ - "display": "flex", - "align-items": "center", - "justify-content": "left", - "margin-left": "20px", - }, + dmc.Select( + id="species-dropdown", + label="Espèce", + description="Sélectionnez une espèce pour afficher les informations", + value=species_id_selected, + data=species_options, + # withCheckIcon=True, + clearable=False, + allowDeselect=False, + searchable=True, + w=400, ), dmc.Title( "Carte de présence de l'espèce sélectionnée", From e66db28b9bbccccd61fd54309d22bb286466090d Mon Sep 17 00:00:00 2001 From: Benoit Frisque Date: Thu, 24 Oct 2024 10:45:01 +0200 Subject: [PATCH 07/11] renamed image --- ...ia_glabratula.JPG => melanelixia_glabratula.jpg} | Bin 1 file changed, 0 insertions(+), 0 deletions(-) rename Dashboards/assets/img/{melanelixia_glabratula.JPG => melanelixia_glabratula.jpg} (100%) diff --git a/Dashboards/assets/img/melanelixia_glabratula.JPG b/Dashboards/assets/img/melanelixia_glabratula.jpg similarity index 100% rename from Dashboards/assets/img/melanelixia_glabratula.JPG rename to Dashboards/assets/img/melanelixia_glabratula.jpg From c8fd0aae52e4538c30461d49e93558612b138712 Mon Sep 17 00:00:00 2001 From: Benoit Frisque Date: Thu, 24 Oct 2024 10:45:37 +0200 Subject: [PATCH 08/11] renamed image --- ...loicia_canescens.JPG => diploicia_canescens.jpg} | Bin 1 file changed, 0 insertions(+), 0 deletions(-) rename Dashboards/assets/img/{diploicia_canescens.JPG => diploicia_canescens.jpg} (100%) diff --git a/Dashboards/assets/img/diploicia_canescens.JPG b/Dashboards/assets/img/diploicia_canescens.jpg similarity index 100% rename from Dashboards/assets/img/diploicia_canescens.JPG rename to Dashboards/assets/img/diploicia_canescens.jpg From a37f0901581724388ae635c95320729802b08370 Mon Sep 17 00:00:00 2001 From: Benoit Frisque Date: Thu, 24 Oct 2024 12:59:58 +0200 Subject: [PATCH 09/11] improved species dashboard with cards --- Dashboards/assets/styles.css | 9 - Dashboards/constants.py | 45 +++-- Dashboards/dashboard.py | 311 +++++++++++++++++++---------------- 3 files changed, 192 insertions(+), 173 deletions(-) delete mode 100644 Dashboards/assets/styles.css diff --git a/Dashboards/assets/styles.css b/Dashboards/assets/styles.css deleted file mode 100644 index 2dda30a..0000000 --- a/Dashboards/assets/styles.css +++ /dev/null @@ -1,9 +0,0 @@ -.graph-title { - text-align: left; - margin: 0; - padding: 5px; -} - -.info-icon { - font-size: 15px; -} diff --git a/Dashboards/constants.py b/Dashboards/constants.py index 3b259d6..99019ca 100644 --- a/Dashboards/constants.py +++ b/Dashboards/constants.py @@ -1,25 +1,9 @@ BODY_FONT_FAMILY = '"Source Sans Pro", sans-serif' -PLOTLY_FONT_COLOR = "#495057" # Grey - -# Constants for color palettes, font families, etc. -# BASE_COLOR_PALETTE = [ -# "#387CA6", -# "#1C6C8C", -# "#3887A6", -# "#ADCCD9", -# "#F2F2F2" -# ] +PLOTLY_FONT_COLOR = "#868e96" # Grey # Generated with https://omatsuri.app/color-shades-generator - BASE_COLOR_PALETTE = [ - # "#333D43", - # "#37444C", - # "#3A4C58", - # "#3C5665", - # "#3D6176", - # "#3C6D8C", "#387CA6", "#4A86AB", "#608FAD", @@ -30,12 +14,6 @@ ] PASTEL_COLOR_PALETTE = [ - # "#c1c4c6", - # "#c3c6c9", - # "#c3c9cc", - # "#c4ccd0", - # "#c4cfd5", - # "#c4d3dc", "#c3d7e4", "#c8dae5", "#cfdde6", @@ -97,6 +75,22 @@ } +# Constants for common styles +FLEX_COLUMNS_CONTAINER_STYLE = {"display": "flex", "gap": "16px"} +GRID_STYLE = {"gutter": "md", "align": "stretch"} +CARD_STYLE = { + "pt":"xs", + "pb": "cs", + "shadow": "sm", + "withBorder": True + } + +MAP_STYLE = { + "withBorder": True, + "p": 0, +} + + # Define the plotly style for hover labels PLOTLY_HOVER_STYLE = { "font": dict( @@ -106,7 +100,10 @@ # Define the plotly layout for all plots PLOTLY_LAYOUT = { - "font": dict(family=BODY_FONT_FAMILY, color=PLOTLY_FONT_COLOR), + "font": dict(family=BODY_FONT_FAMILY, + color="grey", + #color=PLOTLY_FONT_COLOR + ), "template": "plotly_white", "margin": dict(l=0, r=0, t=10, b=10), "barcornerradius": "30%", diff --git a/Dashboards/dashboard.py b/Dashboards/dashboard.py index 113ddc4..2bbac60 100644 --- a/Dashboards/dashboard.py +++ b/Dashboards/dashboard.py @@ -11,7 +11,7 @@ from Dashboards.my_data.datasets import get_useful_data from Dashboards.my_data.computed_datasets import merge_tables, calc_degrees_pollution, calc_vdl, count_lichen, count_lichen_per_species, count_species_per_observation, count_lichen_per_lichen_id, group_lichen_by_observation_and_thallus from Dashboards.charts import blank_figure, create_map, create_hist1_nb_species, create_hist2_vdl, create_hist3, create_pie_thallus, create_hist4, create_gauge_chart -from Dashboards.constants import MAP_SETTINGS, BASE_COLOR_PALETTE, BODY_FONT_FAMILY, POSITIVE_GAUGE_COLOR_PALETTE, NEGATIVE_GAUGE_COLOR_PALETTE, TRANSLATIONS_EN_FR +from Dashboards.constants import MAP_SETTINGS, BASE_COLOR_PALETTE, BODY_FONT_FAMILY, POSITIVE_GAUGE_COLOR_PALETTE, NEGATIVE_GAUGE_COLOR_PALETTE, TRANSLATIONS_EN_FR, GRID_STYLE, CARD_STYLE, MAP_STYLE, FLEX_COLUMNS_CONTAINER_STYLE _dash_renderer._set_react_version("18.2.0") @@ -176,11 +176,13 @@ def update_dashboard_observation(clickData, date_range): @callback( Output(component_id='map-species_present', component_property='figure'), Output(component_id='hist4-species', component_property='figure'), - Output(component_id='lichen-image', component_property='src'), + Output(component_id='species-name', component_property='children'), + Output(component_id='species-image', component_property='src'), Output(component_id='acid-badge', component_property='children'), Output(component_id='eutro-badge', component_property='children'), Output(component_id='poleo-badge', component_property='children'), - Output(component_id='thallus-badge', component_property='children'), + Output(component_id='species-thallus', component_property='children'), + Output(component_id='species-rarity', component_property='children'), Input(component_id='species-dropdown', component_property='value'), State('map-species_present', 'relayoutData') ) @@ -203,12 +205,12 @@ def update_dashboard2(species_id_selected, relayoutData): current_zoom = 4.8 current_center = {"lat": observation_with_selected_species_col_df['localisation_lat'].mean() + 0.5, "lon": observation_with_selected_species_col_df['localisation_long'].mean()} - fig_map = create_map(observation_with_selected_species_col_df, 'selected_species_present', current_zoom, current_center) # Filter on the selected species species_selected = merged_lichen_species_df[merged_lichen_species_df['species_id'] == species_id_selected].iloc[0] + species_name = species_selected['name'] species_img = species_selected['picture'] species_img_path = os.path.join(lichen_img_dir, species_img) @@ -216,6 +218,7 @@ def update_dashboard2(species_id_selected, relayoutData): species_eutro = species_selected['eutrophication'] species_poleo = species_selected['poleotolerance'] species_thallus = species_selected['thallus'] + species_rarity = species_selected['rarity'] # Translate with the dictionary species_acid = TRANSLATIONS_EN_FR.get(species_acid, species_acid) @@ -223,22 +226,13 @@ def update_dashboard2(species_id_selected, relayoutData): species_poleo = TRANSLATIONS_EN_FR.get(species_poleo, species_poleo) species_thallus = TRANSLATIONS_EN_FR.get(species_thallus, species_thallus) - return fig_map, hist4_species, species_img_path, species_acid, species_eutro, species_poleo, species_thallus - - -# Constants for common styles -FLEX_COLUMNS_CONTAINER_STYLE = {"display": "flex", "gap": "16px"} -GRID_STYLE = {"gutter": "md", "align": "stretch"} -CARD_STYLE = {"p": "5px", "shadow": "sm", "withBorder": True} -GAUGE_GRAPH_STYLE = {"height": "100px"} -HIST_GRAPH_STYLE = {"height": "300px"} -MAP_STYLE = { - "withBorder": True, - "shadow": "sm", - "p": 0, - "mt": 8, - "mb": 16 -} + # Pluralize the thallus + if not species_thallus.endswith("x") or species_thallus.endswith("s"): + species_thallus += "s" + + return fig_map, hist4_species, species_name, species_img_path, species_acid, species_eutro, species_poleo, species_thallus, species_rarity + + # Reusable component for title and tooltip def title_and_tooltip(title, tooltip_text): @@ -248,7 +242,7 @@ def title_and_tooltip(title, tooltip_text): dmc.Tooltip( children=DashIconify( icon="material-symbols:info-outline", - className="info-icon", + height=15, ), label=tooltip_text, withArrow=True, @@ -260,14 +254,14 @@ def title_and_tooltip(title, tooltip_text): ) # Reusable component for gauge cards -def gauge_card(title, tooltip_text, graph_id): +def gauge_card(title, tooltip_text, graph_id, height="100px"): return dmc.Card( children=[ title_and_tooltip(title, tooltip_text), dcc.Graph( id=graph_id, figure=blank_fig, - style=GAUGE_GRAPH_STYLE, + style={"height": height}, config={"displayModeBar": False}, ), ], @@ -286,10 +280,7 @@ def histogram_card(title, tooltip_text, graph_id, height="300px"): config={"displaylogo": False}, ), ], - withBorder=True, - shadow="sm", - pt="xs", - pb="xs", + **CARD_STYLE ) # Layout for the sites (observations) @@ -325,32 +316,40 @@ def histogram_card(title, tooltip_text, graph_id, height="300px"): ], align="center", gap="xs", + mb="xs", ), - dmc.Title("Carte des observations", order=4, className="graph-title"), - dmc.SegmentedControl( - id="map-column-select", - value=list(MAP_SETTINGS.keys())[0], - data=[ - {"label": MAP_SETTINGS[col]["title"], "value": col} - for col in ["nb_species_cat", "VDL_cat", "deg_pollution_acid_cat", "deg_pollution_azote_cat", "deg_artif_cat"] - ], - transitionDuration=600, - transitionTimingFunction="ease-in-out", - ), - html.Div( + dmc.Card( children=[ - dmc.Card( + dmc.Title("Carte des observations", order=4), + dmc.SegmentedControl( + id="map-column-select", + value=list(MAP_SETTINGS.keys())[0], + data=[ + {"label": MAP_SETTINGS[col]["title"], "value": col} + for col in ["nb_species_cat", "VDL_cat", "deg_pollution_acid_cat", "deg_pollution_azote_cat", "deg_artif_cat"] + ], + transitionDuration=600, + transitionTimingFunction="ease-in-out", + mb="xs" + ), + html.Div( children=[ - dcc.Graph( - id="map-nb_species-vdl", - figure=blank_fig, - style={"height": "469px"}, - config={"displaylogo": False}, + dmc.Card( + children=[ + dcc.Graph( + id="map-nb_species-vdl", + figure=blank_fig, + style={"height": "469px"}, + config={"displaylogo": False}, + ), + ], + **MAP_STYLE ), ], - **MAP_STYLE ), ], + **CARD_STYLE, + mb="xs", ), dmc.Grid( **GRID_STYLE, @@ -424,113 +423,146 @@ def histogram_card(title, tooltip_text, graph_id, height="300px"): ], ) -# Layout for the "Espèces" tab -species_layout = html.Div( - style=FLEX_COLUMNS_CONTAINER_STYLE, +urls = [ + "https://raw.githubusercontent.com/mantinedev/mantine/master/.demo/images/bg-1.png", + "https://raw.githubusercontent.com/mantinedev/mantine/master/.demo/images/bg-2.png", + "https://raw.githubusercontent.com/mantinedev/mantine/master/.demo/images/bg-3.png", +] + +images = [dmc.Image(radius="sm", src=url) for url in urls] + +species_card = dmc.Card( children=[ - # Divider for the first column with selector and map - html.Div( - style={"flex": "5"}, + dmc.CardSection( children=[ - dmc.Select( - id="species-dropdown", - label="Espèce", - description="Sélectionnez une espèce pour afficher les informations", - value=species_id_selected, - data=species_options, - # withCheckIcon=True, - clearable=False, - allowDeselect=False, - searchable=True, - w=400, + dmc.Text(id="species-name", size="lg"), + ], + withBorder=True, + inheritPadding=True, + py="xs", + ), + dmc.Text( + children=[ + "Ce lichen fait partie de la famille des ", + dmc.Text( + id="species-thallus", + c="blue", + style={"display": "inline"}, ), - dmc.Title( - "Carte de présence de l'espèce sélectionnée", - order=4, - className="graph-title", - style={"padding": "0px"}, + " et est classé comme ", + dmc.Text( + id="species-rarity", + c="blue", + style={"display": "inline"}, ), - html.Div( - children=[ - dmc.Card( - children=[ - dcc.Graph( - id="map-species_present", - figure=blank_fig, - config={ - "displaylogo": False, # Remove plotly logo - }, - ), - ], - **MAP_STYLE + ".", + ], + mt="sm", + c="dimmed", + size="sm", + ), + dmc.CardSection( + dmc.Image( + id="species-image", + mt="sm", + src=None, + fallbackSrc="https://placehold.co/600x400?text=No%20image%20found", + ), + ), + dmc.CardSection( + children=[ + dmc.Stack( + [ + dmc.Group( + [ + "Acidité", + dmc.Badge(id="acid-badge", variant="light"), + ] + ), + dmc.Group( + [ + "Eutrophisation", + dmc.Badge(id="eutro-badge", variant="light"), + ] + ), + dmc.Group( + [ + "Poléotolérance", + dmc.Badge(id="poleo-badge", variant="light"), + ] ), ], + align="left", + gap="md", ), ], + inheritPadding=True, + mt="sm", + pb="md", ), - # Divider for the second column + ], + withBorder=True, + shadow="sm", + radius="md", + maw=300, + miw=250, +) + +# Layout for the "Espèces" tab +species_layout = html.Div( + children=[ + dmc.Select( + id="species-dropdown", + label="Espèce", + description="Sélectionnez une espèce pour afficher les informations", + value=species_id_selected, + data=species_options, + clearable=False, + allowDeselect=False, + searchable=True, + w=400, + ), + dmc.Space(h=10), html.Div( - style={"flex": "5"}, + style=FLEX_COLUMNS_CONTAINER_STYLE, children=[ - dmc.Grid( - gutter="md", - align="stretch", + html.Div(species_card), + html.Div( + style={"flex-basis": "40%", "flex-grow": "1"}, children=[ - dmc.GridCol( - span=6, - children=[ - dmc.Card([ - dmc.Title( - "Carte d'identité de l'espèce sélectionnée", order=4, className="graph-title"), - dmc.Grid( - children=[ - dmc.GridCol( - dmc.Image( - id="lichen-image", - radius="md", - src=None, - h=150, - fallbackSrc="https://placehold.co/600x400?text=No%20image%20found", - ), - span=8), - dmc.GridCol( - dmc.Stack( - [ - dmc.Badge(id="acid-badge"), - dmc.Badge(id="eutro-badge", variant="light"), - dmc.Badge(id="poleo-badge", variant="outline"), - dmc.Badge(id="thallus-badge", variant="light"), - ], - align="center", - gap="md", - ), - span=4) - ], - gutter="md", - grow=False, - ) - ], - withBorder=True, - ) - ], - ), - dmc.GridCol( - span=10, + dmc.Card( children=[ title_and_tooltip( - title="Espèces les plus observées", - tooltip_text="Distribution des espèces observées sur l'ensemble des sites" + title="Carte de présence de l'espèce sélectionnée", + tooltip_text="Carte de présence de l'espèce sélectionnée" ), - dcc.Graph( - id="hist4-species", - figure=blank_fig, - config={ - "displaylogo": False, # Remove plotly logo - }, + dmc.Card( + children=[ + dcc.Graph( + id="map-species_present", + figure=blank_fig, + config={ + "displaylogo": False, # Remove plotly logo + }, + style={"height": "588px"}, + ), + ], + **MAP_STYLE, ), ], + **CARD_STYLE, + ), + ], + ), + html.Div( + style={"flex-basis": "40%", "flex-grow": "1"}, + children=[ + histogram_card( + "Espèces les plus observées", + "Distribution des espèces observées sur l'ensemble des sites", + "hist4-species", + height="590px", ), - ], ), ], @@ -542,7 +574,7 @@ def histogram_card(title, tooltip_text, graph_id, height="300px"): # Toggle to switch between light and dark theme theme_toggle = dmc.ActionIcon( [ - dmc.Paper(DashIconify(icon="radix-icons:sun", width=25), darkHidden=True), + dmc.Paper(DashIconify(icon="radix-icons:sun", width=25), darkHidden=True), dmc.Paper(DashIconify(icon="radix-icons:moon", width=25), lightHidden=True), ], variant="transparent", @@ -550,9 +582,9 @@ def histogram_card(title, tooltip_text, graph_id, height="300px"): size="lg", style={ "position": "fixed", - "top": "21px", - "right": "21px", - "zIndex": 1000 + "top": "20px", + "right": "26px", + "zIndex": 1000, } ) @@ -657,7 +689,6 @@ def switch_theme(_, theme): children=[ dmc.Group( children=[ - # dmc.Divider(orientation="vertical"), dashboards_layout, theme_toggle, ], From 6583ffc40b5a87fae2efc1c55930103016bbb024 Mon Sep 17 00:00:00 2001 From: Benoit Frisque Date: Thu, 24 Oct 2024 13:00:14 +0200 Subject: [PATCH 10/11] added rarity column --- Dashboards/assets/lichen_species_ecology.csv | 78 ++++++++++---------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/Dashboards/assets/lichen_species_ecology.csv b/Dashboards/assets/lichen_species_ecology.csv index 979eff2..669c61d 100644 --- a/Dashboards/assets/lichen_species_ecology.csv +++ b/Dashboards/assets/lichen_species_ecology.csv @@ -1,39 +1,39 @@ -id;Taxon;pH;aridity;eutrophication;poleotolerance;thallus;picture -7;Amandinea punctata/Lecidella elaeochroma;neutrophilous;xerophilous;mesotrophic;resistant;crustose;amandinea_punctata.jpg -28;Anaptychia ciliaris;neutrophilous;mesophilous;mesotrophic;intermediate;fruticose;anaptychia_ciliaris.jpg -30;Autre lichen crustacé;unknown;unknown;unknown;intermediate;crustose;lichen_crustace.jpg -20;Autre lichen foliacé;unknown;unknown;unknown;intermediate;foliose;lichen_foliace.jpg -36;Autre lichen fruticuleux;unknown;unknown;unknown;intermediate;fruticose;lichen_crustace.jpg -2;Candelaria concolor;neutrophilous;xerophilous;eutrophic;resistant;foliose;candelaria_concolor.jpg -21;Candelariella sp.;neutrophilous;mesophilous;eutrophic;resistant;crustose;candelariella.jpg -35;Diploicia canescens;basophilous;mesophilous;mesotrophic;intermediate;crustose;diploicia_canescens.jpg -8;Evernia prunastri;acidophilous;mesophilous;oligotrophic;intermediate;fruticose;evernia_prunastri.jpg -3;Flavoparmelia caperata/soredians;neutrophilous;mesophilous;oligotrophic;intermediate;foliose;flavoparmelia_caperata.jpg -24;Hyperphyscia adglutinata;basophilous;xerophilous;eutrophic;resistant;foliose;hyperphyscia_adglutinata.jpg -22;Hypogymnia physodes/tubulosa;acidophilous;mesophilous;oligotrophic;intermediate;foliose;hypogymnia_physodes.jpg -4;Hypotrachyna afrorevoluta/revoluta;acidophilous;mesophilous;oligotrophic;intermediate;foliose;hypotrachyna_afrevoluta.jpg -9;Lecanora sp.;unknown;unknown;unknown;intermediate;crustose;lecanora.jpg -5;Lichen crustacé à aspect poudreux;acidophilous;unknown;unknown;intermediate;crustose;crustaces_poudreux.jpg -38;Lichen crustacé à lirelles;unknown;unknown;unknown;intermediate;crustose;crustaces_lirelles.jpg -10;Melanelixia glabratula/Melanohalea exasperatula;neutrophilous;mesophilous;mesotrophic;resistant;foliose;melanelixia_glabratula.jpg -25;Melanohalea exasperata;neutrophilous;xerophilous;mesotrophic;intermediate;foliose;melanohalea_exasperata.jpg -33;Parmelia saxatilis;acidophilous;mesophilous;oligotrophic;intermediate;foliose;parmelia_saxatilis.jpg -11;Parmelia sulcata;acidophilous;mesophilous;oligotrophic;resistant;foliose;parmelia_sulcata.jpg -23;Parmelina tiliacea/pastillifera;neutrophilous;hygrophilous;mesotrophic;intermediate;foliose;parmelina_tiliacea.jpg -31;Parmotrema perlatum/reticulatum;neutrophilous;mesophilous;oligotrophic;intermediate;foliose;parmotrema_perlatum.jpg -29;Pertusaria pertusa;acidophilous;mesophilous;oligotrophic;intermediate;crustose;pertusaria_pertusa.jpg -12;Phaeophyscia orbicularis;neutrophilous;xerophilous;eutrophic;resistant;foliose;phaeophyscia_orbicularis.jpg -13;Physcia adscendens/tenella;basophilous;xerophilous;eutrophic;resistant;foliose;physcia_adscendens.jpg -19;Physcia aipolia/stellaris;neutrophilous;mesophilous;eutrophic;resistant;foliose;physcia_aipolia.jpg -16;Physcia leptalea;neutrophilous;xerophilous;mesotrophic;intermediate;foliose;physcia_leptalea.jpg -18;Physconia distorta;neutrophilous;xerophilous;eutrophic;resistant;foliose;physconia_distorta.jpg -14;Physconia grisea;neutrophilous;mesophilous;eutrophic;resistant;foliose;physconia_grisea.jpg -15;Pleurosticta acetabulum;neutrophilous;xerophilous;mesotrophic;intermediate;foliose;pleurosticta_acetabulum.jpg -37;Polycauliona polycarpa;neutrophilous;xerophilous;mesotrophic;intermediate;foliose;polycauliona_polycarpa.jpg -27;Pseudevernia furfuracea;acidophilous;xerophilous;oligotrophic;intermediate;fruticose;pseudevernia_furfuracea.jpg -17;Punctelia sp.;neutrophilous;mesophilous;oligotrophic;resistant;foliose;punctelia_sp_.jpg -6;Ramalina farinacea;neutrophilous;hygrophilous;oligotrophic;intermediate;fruticose;ramalina_farinacea.jpg -32;Ramalina fastigiata;neutrophilous;mesophilous;oligotrophic;intermediate;fruticose;ramalina_fastigiata.jpg -26;Ramalina fraxinea;neutrophilous;mesophilous;mesotrophic;sensitive;fruticose;ramalina_fraxinea.jpg -34;Usnea sp.;acidophilous;hygrophilous;oligotrophic;sensitive;fruticose;usnea.jpg -1;Xanthoria parietina;neutrophilous;xerophilous;eutrophic;resistant;foliose;xanthoria_parietina.jpg +id;Taxon;pH;aridity;eutrophication;poleotolerance;thallus;rarity;picture +8;Evernia prunastri;acidophilous;mesophilous;oligotrophic;intermediate;fruticuleux;commun;evernia_prunastri.jpg +3;Flavoparmelia caperata/soredians;neutrophilous;mesophilous;oligotrophic;intermediate;foliacé;commun;flavoparmelia_caperata.jpg +9;Lecanora sp.;unknown;unknown;unknown;intermediate;crustacé;commun;lecanora.jpg +5;Lichen crustacé à aspect poudreux;acidophilous;unknown;unknown;intermediate;crustacé;commun;crustaces_poudreux.jpg +10;Melanelixia glabratula/Melanohalea exasperatula;neutrophilous;mesophilous;mesotrophic;resistant;foliacé;commun;melanelixia_glabratula.jpg +11;Parmelia sulcata;acidophilous;mesophilous;oligotrophic;resistant;foliacé;commun;parmelia_sulcata.jpg +14;Physconia grisea;neutrophilous;mesophilous;eutrophic;resistant;foliacé;commun;physconia_grisea.jpg +17;Punctelia sp.;neutrophilous;mesophilous;oligotrophic;resistant;foliacé;commun;punctelia_sp_.jpg +35;Diploicia canescens;basophilous;mesophilous;mesotrophic;intermediate;crustacé;commun près des côtes;diploicia_canescens.jpg +30;Autre lichen crustacé;unknown;unknown;unknown;intermediate;crustacé;indéterminé;lichen_crustace.jpg +20;Autre lichen foliacé;unknown;unknown;unknown;intermediate;foliacé;indéterminé;lichen_foliace.jpg +36;Autre lichen fruticuleux;unknown;unknown;unknown;intermediate;fruticuleux;indéterminé;lichen_crustace.jpg +22;Hypogymnia physodes/tubulosa;acidophilous;mesophilous;oligotrophic;intermediate;foliacé;peu commun;hypogymnia_physodes.jpg +4;Hypotrachyna afrorevoluta/revoluta;acidophilous;mesophilous;oligotrophic;intermediate;foliacé;peu commun;hypotrachyna_afrevoluta.jpg +38;Lichen crustacé à lirelles;unknown;unknown;unknown;intermediate;crustacé;peu commun;crustaces_lirelles.jpg +33;Parmelia saxatilis;acidophilous;mesophilous;oligotrophic;intermediate;foliacé;peu commun;parmelia_saxatilis.jpg +23;Parmelina tiliacea/pastillifera;neutrophilous;hygrophilous;mesotrophic;intermediate;foliacé;peu commun;parmelina_tiliacea.jpg +31;Parmotrema perlatum/reticulatum;neutrophilous;mesophilous;oligotrophic;intermediate;foliacé;peu commun;parmotrema_perlatum.jpg +19;Physcia aipolia/stellaris;neutrophilous;mesophilous;eutrophic;resistant;foliacé;peu commun;physcia_aipolia.jpg +18;Physconia distorta;neutrophilous;xerophilous;eutrophic;resistant;foliacé;peu commun;physconia_distorta.jpg +15;Pleurosticta acetabulum;neutrophilous;xerophilous;mesotrophic;intermediate;foliacé;peu commun;pleurosticta_acetabulum.jpg +37;Polycauliona polycarpa;neutrophilous;xerophilous;mesotrophic;intermediate;foliacé;peu commun;polycauliona_polycarpa.jpg +6;Ramalina farinacea;neutrophilous;hygrophilous;oligotrophic;intermediate;fruticuleux;peu commun;ramalina_farinacea.jpg +32;Ramalina fastigiata;neutrophilous;mesophilous;oligotrophic;intermediate;fruticuleux;peu commun;ramalina_fastigiata.jpg +16;Physcia leptalea;neutrophilous;xerophilous;mesotrophic;intermediate;foliacé;rare;physcia_leptalea.jpg +28;Anaptychia ciliaris;neutrophilous;mesophilous;mesotrophic;intermediate;fruticuleux;rare;anaptychia_ciliaris.jpg +25;Melanohalea exasperata;neutrophilous;xerophilous;mesotrophic;intermediate;foliacé;rare;melanohalea_exasperata.jpg +29;Pertusaria pertusa;acidophilous;mesophilous;oligotrophic;intermediate;crustacé;rare;pertusaria_pertusa.jpg +27;Pseudevernia furfuracea;acidophilous;xerophilous;oligotrophic;intermediate;fruticuleux;rare;pseudevernia_furfuracea.jpg +26;Ramalina fraxinea;neutrophilous;mesophilous;mesotrophic;sensitive;fruticuleux;rare;ramalina_fraxinea.jpg +34;Usnea sp.;acidophilous;hygrophilous;oligotrophic;sensitive;fruticuleux;rare;usnea.jpg +7;Amandinea punctata/Lecidella elaeochroma;neutrophilous;xerophilous;mesotrophic;resistant;crustacé;très commun;amandinea_punctata.jpg +2;Candelaria concolor;neutrophilous;xerophilous;eutrophic;resistant;foliacé;très commun;candelaria_concolor.jpg +21;Candelariella sp.;neutrophilous;mesophilous;eutrophic;resistant;crustacé;très commun;candelariella.jpg +24;Hyperphyscia adglutinata;basophilous;xerophilous;eutrophic;resistant;foliacé;très commun;hyperphyscia_adglutinata.jpg +12;Phaeophyscia orbicularis;neutrophilous;xerophilous;eutrophic;resistant;foliacé;très commun;phaeophyscia_orbicularis.jpg +13;Physcia adscendens/tenella;basophilous;xerophilous;eutrophic;resistant;foliacé;très commun;physcia_adscendens.jpg +1;Xanthoria parietina;neutrophilous;xerophilous;eutrophic;resistant;foliacé;très commun;xanthoria_parietina.jpg From e128c5647ad12f225383c622200fb12dddf60166 Mon Sep 17 00:00:00 2001 From: Benoit Frisque Date: Thu, 24 Oct 2024 14:33:16 +0200 Subject: [PATCH 11/11] improved flex layout --- Dashboards/dashboard.py | 112 ++++++++++++++++++++++++++-------------- 1 file changed, 73 insertions(+), 39 deletions(-) diff --git a/Dashboards/dashboard.py b/Dashboards/dashboard.py index 2bbac60..1fed9a3 100644 --- a/Dashboards/dashboard.py +++ b/Dashboards/dashboard.py @@ -2,7 +2,7 @@ import os import dash_mantine_components as dmc -from dash import Dash, _dash_renderer, html, dcc, Output, Input, callback +from dash import Dash, _dash_renderer, html, dcc, Output, Input, callback, no_update from dash.dependencies import State from dash.exceptions import PreventUpdate from dash_iconify import DashIconify @@ -250,26 +250,36 @@ def title_and_tooltip(title, tooltip_text): ), ], align="center", - gap="xs", + gap=2, ) # Reusable component for gauge cards -def gauge_card(title, tooltip_text, graph_id, height="100px"): +def gauge_card(title, tooltip_text, graph_id, max_height="200px"): return dmc.Card( children=[ title_and_tooltip(title, tooltip_text), dcc.Graph( id=graph_id, figure=blank_fig, - style={"height": height}, + style={ + "height": "100px", + "width": "100%", + }, config={"displayModeBar": False}, ), ], - **CARD_STYLE + style={ + "display": "flex", + "flexDirection": "column", + "justifyContent": "space-between", + "flexGrow": 1, + "maxHeight": max_height + }, + **CARD_STYLE ) # Reusable component for histogram cards -def histogram_card(title, tooltip_text, graph_id, height="300px"): +def histogram_card(title, tooltip_text, graph_id, height="330px"): return dmc.Card( children=[ title_and_tooltip(title, tooltip_text), @@ -289,7 +299,7 @@ def histogram_card(title, tooltip_text, graph_id, height="300px"): children=[ # First column with map and gauge html.Div( - style={"flex-grow": "1", "flex-basis": "auto"}, + style={"flex-grow": "1", "flex-basis": "50%"}, children=[ dmc.Group( [ @@ -349,29 +359,48 @@ def histogram_card(title, tooltip_text, graph_id, height="300px"): ), ], **CARD_STYLE, - mb="xs", + mb="md", ), dmc.Grid( **GRID_STYLE, children=[ - dmc.GridCol(gauge_card("% Espèces toxitolérantes", "Pourcentage d'espèces toxitolérantes sur le site sélectionné", "gauge-chart1-artif"), - span=4.2, + dmc.GridCol( + gauge_card( + "% Espèces toxitolérantes", + "Pourcentage d'espèces toxitolérantes sur le site sélectionné", + "gauge-chart1-artif", + ), + span=4, + style={"display": "flex", + "flexDirection": "column"} ), dmc.GridCol( - gauge_card("% Espèces eutrophes", "Pourcentage d'espèces eutrophes sur le site sélectionné", "gauge-chart3-azote"), - span=3.9, + gauge_card( + "% Espèces eutrophes", + "Pourcentage d'espèces eutrophes sur le site sélectionné", + "gauge-chart3-azote", + ), + span=4, + style={"display": "flex", + "flexDirection": "column"} ), dmc.GridCol( - gauge_card("% Espèces acidophiles", "Pourcentage d'espèces acidophiles sur le site sélectionné", "gauge-chart2-acide"), - span=3.9, + gauge_card( + "% Espèces acidophiles", + "Pourcentage d'espèces acidophiles sur le site sélectionné", + "gauge-chart2-acide", + ), + span=4, + style={"display": "flex", + "flexDirection": "column"} ), ], - ), + ) ], ), # Second column with histograms html.Div( - style={"flex-grow": "1", "flex-basis": "auto"}, + style={"flex-grow": "1", "flex-basis": "50%"}, children=[ dmc.Grid( **GRID_STYLE, @@ -544,10 +573,11 @@ def histogram_card(title, tooltip_text, graph_id, height="300px"): config={ "displaylogo": False, # Remove plotly logo }, - style={"height": "588px"}, + style={"height": "578px"}, ), ], **MAP_STYLE, + mt="xs", ), ], **CARD_STYLE, @@ -625,6 +655,7 @@ def switch_theme(_, theme): dashboards_layout = dmc.Box( children=[ dmc.Accordion( + id="accordion", disableChevronRotation=True, chevronPosition="left", variant="contained", @@ -633,28 +664,31 @@ def switch_theme(_, theme): dmc.AccordionItem( children=[ dmc.AccordionControl( - dmc.Group( - children=[ - DashIconify( - icon="tabler:map-pin", - height=25, - color=BASE_COLOR_PALETTE[0] - ), - dmc.Tooltip( - label="Cliquez sur un site pour découvrir ce que les lichens peuvent nous apprendre", - position="right", - withArrow=True, - children=dmc.Title("Sites", order=3) - ), - ], - align="center", - ), - ), - dmc.AccordionPanel(sites_layout), - ], - value="sites", - ), - dmc.AccordionItem( + [ + dmc.Group( + children=[ + DashIconify( + icon="tabler:map-pin", + height=25, + color=BASE_COLOR_PALETTE[0] + ), + dmc.Tooltip( + label="Cliquez sur un site pour découvrir ce que les lichens peuvent nous apprendre", + position="right", + withArrow=True, + children=dmc.Title( + "Sites", order=3) + ), + ], + align="center", + ), + ], + ), + dmc.AccordionPanel(sites_layout), + ], + value="sites", + ), + dmc.AccordionItem( children=[ dmc.AccordionControl( dmc.Group(