From e6e7dc787398f226564ef1b732a82dfd4affef69 Mon Sep 17 00:00:00 2001 From: "alexis.dereeper_ird.fr" <alexis.dereeper@ird.fr> Date: Tue, 11 Mar 2025 15:36:00 +0000 Subject: [PATCH] add legend into geographical map --- dash/pages/sample-tracker.py | 273 +++++++++++++++++++---------------- 1 file changed, 152 insertions(+), 121 deletions(-) diff --git a/dash/pages/sample-tracker.py b/dash/pages/sample-tracker.py index 79f2623..b83535c 100644 --- a/dash/pages/sample-tracker.py +++ b/dash/pages/sample-tracker.py @@ -1,9 +1,11 @@ from dash import Dash, html, dcc, Input, Output, State, callback, dash_table -#from dash import Dash, dcc, html, dash_table, Input, Output, State, callback + +import branca from grist_api import GristDocAPI import os import pandas as pd +import random import plotly.express as px import plotly.graph_objects as go @@ -35,6 +37,9 @@ import folium import folium.plugins import pandas.io.sql as psql +import matplotlib.pyplot as plt +from matplotlib import colors as mcolors + import dash_bio as dashbio @@ -44,6 +49,7 @@ dash.register_page(__name__,path='/sample-tracker') server_url = "https://bioinfophim-grist.ird.fr" working_dir = "/mnt/c/Users/dereeper/Documents/formation_python_scientifique_2022/dash" + with open("grist_config.yml", "r") as yaml_file: conf = yaml.safe_load(yaml_file) server_url = conf["server_url"] @@ -91,28 +97,6 @@ df=pd.merge(df_sequenced_strains,df1, left_on='source_strain_id', right_on='id_y df_diag = df[df['Species']=='Unknown'] -year_colors = { - "2014": "lightgray", - "2015": "darkgreen", - "2016": "darkpurple", - "2017": "beige", - "2018": "lightblue", - "2019": "pink", - "2020": "green", - "2021": "red", - "2022": "black", - "2023": "blue" -} - -species_colors = { - "Xanthomonas oryzae oryzae": "lightblue", - "Xanthomonas oryzae oryzicola": "blue", - "Xanthomonas hortorum": "orange", - "Xanthomonas phaseoli": "pink", - "Xanthomonas translucens": "green", - "Xanthomonas axonopodis": "red", - "Xanthomonas vasicola": "black" -} ############################################ @@ -156,7 +140,7 @@ data_summary_filtered_md_template = 'Selected strains' data_summary_filtered_md = data_summary_filtered_md_template.format(len(df)) - +session = random.randint(1, 9000000) #print(df.head()) @@ -283,23 +267,33 @@ layout = html.Div(className='app-body', children=[ # The Visuals dcc.Tabs(id='tab', children=[ dcc.Tab(label='Geographical map', children=[ - html.Div(className="four columns pretty_container", children=[ - html.Div(className="row", children=[ - html.Label('Marker Clustering'), - daq.BooleanSwitch(id="clustering", on=True, style={'width': '10vh','margin-left': '20px'},) - ]), - - ]), - html.Div(className="four columns pretty_container", children=[ - html.Label('Colorizing map by'), + html.Br(), + html.Div(className="row", children=[ + html.Label('Marker Clustering'), + daq.BooleanSwitch(id="clustering", on=True, style={'width': '10vh','margin-left': '5px'},), + + html.Label('Map tiles: '), + dcc.Dropdown(id='tiles', + style={'width': '20vh','margin-left': '5px','margin-right': '10px'}, + placeholder='OpenStreetMap', + options=['OpenStreetMap','Satellite'], + value='OpenStreetMap', + multi=False), + html.Label('Colorizing map by: '), dcc.Dropdown(id='colorizingmap', - placeholder='Species', - options=list_colorizing_map, - value='Species', - multi=False), + style={'width': '20vh','margin-left': '5px','margin-right': '10px'}, + placeholder='Species', + options=list_colorizing_map, + value='Species', + multi=False), + ]), + html.Br(), + dcc.Loading( - html.Iframe(id='map',src="https://webphim.ird.fr/CIX/testmap.html",style={"height": "600px", "width": "100%"}) + #html.Iframe(id='map',src="https://webphim.ird.fr/CIX/testmap.html",style={"height": "600px", "width": "100%"}) + html.Iframe(id='map',style={"height": "900px", "width": "100%"}) + ), #dcc.Graph(id="map",figure=fig_scattergeo), ]), @@ -320,14 +314,17 @@ layout = html.Div(className='app-body', children=[ ), ]), dcc.Tab(label='Statistics', children=[ - - html.Label('Colorizing histograms by'), - dcc.Dropdown(id='colorizing', - placeholder='Species', - options=list_colorizing, - value='Species', - multi=False), - + html.Br(), + html.Div(className="row", children=[ + html.Label('Colorizing histograms by'), + dcc.Dropdown(id='colorizing', + placeholder='Species', + style={'width': '40vh','margin-left': '5px'}, + options=list_colorizing, + value='Species', + multi=False), + + ]), dcc.Loading( @@ -384,15 +381,16 @@ layout = html.Div(className='app-body', children=[ Input('year', 'value'), Input('pathovar', 'value'), Input('colorizing', 'value'), - Input('colorizingmap', 'value'), - Input('clustering', 'on'), + State('colorizingmap', 'value'), + State('tiles', 'value'), + State('clustering', 'on'), Input('gps_infered', 'value'), Input('include_no_date', 'on'), Input('sequenced', 'on'), #Input('datatable-paging', "page_current"), #Input('datatable-paging', "page_size"), - ) -def update_graph(sp_name, cnt_name, wt_name, year_range, pathovar_name, colorizing_name,colorizingmap_name,clustering_name,gps_infered,include_no_date,sequenced): + ) +def update_graph(sp_name, cnt_name, wt_name, year_range, pathovar_name, colorizing_name,colorizingmap_name,tiles,clustering_name,gps_infered,include_no_date,sequenced): @@ -478,10 +476,54 @@ def update_graph(sp_name, cnt_name, wt_name, year_range, pathovar_name, colorizi ########################################################################### # Geographical map of CIX ########################################################################### - map_with_clusters = folium.Map(location=[11,1], zoom_start=3) - marker_cluster = map_with_clusters - if clustering_name: - marker_cluster = folium.plugins.MarkerCluster().add_to(map_with_clusters) + dff3.to_csv(working_dir + "/" + str(session) + ".matrix.tsv",sep="\t") + updateMap(clustering_name,colorizingmap_name,tiles) + + + + + + ########################################################################### + # Geographical map of diagnostics + ########################################################################### + map_diag = folium.Map(location=[11,1], zoom_start=3) + # for idx in df_diag.index: + # lat = df_diag.loc[idx]['GPS_Latitude'] + # lon = df_diag.loc[idx]['GPS_Longitude'] + # diagnostics = df_diag.loc[idx]['diagnostics'] + # host = df_diag.loc[idx]['host'] + # my_color = "blue" + # if (diagnostics=="Négatif"): + # my_color="red" + # elif (diagnostics=="Xoo"): + # my_color="green" + # elif (diagnostics=="Xoo et Xoc"): + # my_color="blue" + + fn2= working_dir+'/'+str(session)+'.map_diagnostics.html' + map_diag.save(fn2) + + html = generate_html(dff3) + open(working_dir+"/"+str(session)+".table.html", "w").write(html) + + table = dff3.to_dict('records') + + return open(working_dir+"/"+str(session)+'.testmap.html', 'r').read(),fig,fig2,fig4,open(working_dir+"/"+str(session)+'.map_diagnostics.html', 'r').read(),str(len(dff3)),'Current selection : {} strains'.format(len(dff3)),table + + + +@callback( + Output('map','srcDoc', allow_duplicate=True), + Input('clustering', 'on'), + Input('colorizingmap', 'value'), + Input('tiles', 'value'), + prevent_initial_call=True +) + +def updateMap(clustering_name,colorizingmap_name,tiles): + + map_with_clusters = folium.Map(location=[11,1], control_scale=True, zoom_start=3) + if tiles != 'OpenStreetMap': tile = folium.TileLayer( tiles = 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', attr = 'Esri', @@ -490,35 +532,60 @@ def update_graph(sp_name, cnt_name, wt_name, year_range, pathovar_name, colorizi control = True ).add_to(map_with_clusters) + + dff3 = pd.read_csv(working_dir + "/" + str(session) + ".matrix.tsv",sep="\t") + dff3['Sampling_date'] = pd.to_datetime(dff3.Sampling_date, format='%Y-%m-%d') + + marker_cluster = map_with_clusters + if clustering_name: + marker_cluster = folium.plugins.MarkerCluster().add_to(map_with_clusters) else: marker_cluster = map_with_clusters - print("d") - dff3.GPS_Latitude - - #dff3['sampling_date'] = dff3['sampling_date'].replace(['1900'], 'NA') - # remove entries with empty or NA in GPS coordinates - df_geo = dff3[abs(dff3.GPS_Latitude) > 0] - - - # fig_scattergeo = go.Figure(data=go.Scattergeo( - # lon = df_geo['GPS_Longitude'], - # lat = df_geo['GPS_Latitude'], - # text = df['strain_id'], - # mode = 'markers', - # #marker_color = df_geo['Sampling_date'].year, - # )) - # fig_scattergeo = px.scatter_geo(df_geo, - # lat = 'GPS_Latitude', - # lon = 'GPS_Longitude', - # geojson='geometry', - # scope='africa', - # center=dict(lat=51.0057, lon=13.7274)) + #predefined_colors + # remove entries with empty or NA in GPS coordinates + df_geo = dff3[abs(dff3.GPS_Latitude) > 0] + + + species_colors = {} + list_of_species = df_geo['Species'].unique().tolist() + list_of_years = df_geo['Sampling_date'].unique().tolist() + dict_year = {} + for y in list_of_years: + dict_year[y.year]=1 + + sorted_years = sorted(list(dict_year.keys())) + predefined_colors = list(mcolors.CSS4_COLORS.keys()) + + legend_html = ''' + {% macro html(this, kwargs) %} + <div style="position: fixed; + bottom: 50px; left: 0px; width: 250px; height: 700px; + border:2px solid grey; z-index:9999; font-size:14px; + background-color:white; opacity: 0.85;"> + <b>Legend</b> <br> + ''' + i = 0 + if colorizingmap_name == "Year of sampling": + for y in sorted_years: + color = predefined_colors[i] + species_colors[int(y)]=color + legend_html = legend_html + " <i class='fa fa-circle' style='color:"+str(color)+"'></i> " + str(y) + " <br>" + i+=1 + else: + for sp in list_of_species: + + color = predefined_colors[i] + species_colors[sp]=color + legend_html = legend_html + " <i class='fa fa-circle' style='color:"+str(color)+"'></i> " + str(sp) + " <br>" + i+=1 + legend_html = legend_html + "</div>" + legend_html = legend_html + "{% endmacro %}" for idx in df_geo.index: @@ -534,19 +601,13 @@ def update_graph(sp_name, cnt_name, wt_name, year_range, pathovar_name, colorizi CIX = str(df_geo.loc[idx]['strain_id']) wt = df_geo.loc[idx]['WT_DER_RES'] - #species_pathovar = species + " " + pathovar - my_color="blue" - if (colorizingmap_name == "Year of sampling" and year in year_colors.keys()): - my_color = year_colors[year] + if (colorizingmap_name == "Year of sampling" and int(year) in list(species_colors.keys())): + my_color = species_colors[int(year)] if (colorizingmap_name == "Species" and species in species_colors.keys()): my_color = species_colors[species] - - - - folium.CircleMarker( location=(lat,lon), radius=8, @@ -560,49 +621,19 @@ def update_graph(sp_name, cnt_name, wt_name, year_range, pathovar_name, colorizi ).add_to(marker_cluster) - fn= working_dir + '/testmap.html' - marker_cluster.save(fn) - - print("e") - + legend = branca.element.MacroElement() + legend._template = branca.element.Template(legend_html) - ########################################################################### - # Geographical map of diagnostics - ########################################################################### - map_diag = folium.Map(location=[11,1], zoom_start=3) - # for idx in df_diag.index: - # lat = df_diag.loc[idx]['GPS_Latitude'] - # lon = df_diag.loc[idx]['GPS_Longitude'] - # diagnostics = df_diag.loc[idx]['diagnostics'] - # host = df_diag.loc[idx]['host'] - # my_color = "blue" - # if (diagnostics=="Négatif"): - # my_color="red" - # elif (diagnostics=="Xoo"): - # my_color="green" - # elif (diagnostics=="Xoo et Xoc"): - # my_color="blue" - - fn2= working_dir+'/map_diagnostics.html' - map_diag.save(fn2) - - html = generate_html(dff3) - open(working_dir+"/table.html", "w").write(html) - - table = dff3.to_dict('records') + # Add the legend to the map + marker_cluster.get_root().add_child(legend) - - - - - return open(working_dir+'/testmap.html', 'r').read(),fig,fig2,fig4,open(working_dir+'/map_diagnostics.html', 'r').read(),str(len(dff3)),'Current selection : {} strains'.format(len(dff3)),table - - #return fig_scattergeo,fig,fig2,fig4,open(working_dir+'/map_diagnostics.html', 'r').read(),str(len(dff3)),'Current selection : {} strains'.format(len(dff3)),table - #,open('table.html', 'r').read() - #sortable.html.table(dff3, 'sample.html') + fn= working_dir + "/"+str(session)+ '.testmap.html' + marker_cluster.save(fn) + data_returned = open(working_dir+"/"+str(session)+'.testmap.html', 'r').read() + return data_returned def generate_html(dataframe: pd.DataFrame): # get the table HTML from the dataframe -- GitLab