From 32cb98446e9ed36413bee2ba1a6d4e8e320c4905 Mon Sep 17 00:00:00 2001 From: Alexandre <44178713+alexbelgium@users.noreply.github.com> Date: Wed, 30 Oct 2024 09:04:22 +0100 Subject: [PATCH 01/28] Add interactive chart if screen >800px and existing --- scripts/overview.php | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/scripts/overview.php b/scripts/overview.php index 29bfee391..23c2f41b7 100644 --- a/scripts/overview.php +++ b/scripts/overview.php @@ -12,6 +12,7 @@ set_timezone(); $myDate = date('Y-m-d'); $chart = "Combo-$myDate.png"; +$interactivechart = "interactive_daily_plot.html"; $db = new SQLite3('./scripts/birds.db', SQLITE3_OPEN_READONLY); $db->busyTimeout(1000); @@ -320,10 +321,20 @@ function setModalText(iter, title, text, authorlink, photolink, licenseurl) { $dividedrefresh = 1; } $time = time(); -if (file_exists('./Charts/'.$chart)) { - echo ""; -} +$interactivechart_path = './Charts/' . $interactivechart; +$chart_path = './Charts/' . $chart; +if (file_exists($interactivechart_path)) { + $html_content = file_get_contents($interactivechart_path); + echo $html_content; +} elseif (file_exists($chart_path)) { + echo ""; +} ?> +
From e0908f62ea1307f8e0873927b8bab2e57725a403 Mon Sep 17 00:00:00 2001 From: Alexandre <44178713+alexbelgium@users.noreply.github.com> Date: Wed, 30 Oct 2024 09:04:47 +0100 Subject: [PATCH 02/28] Create dynamic_plot.py --- scripts/dynamic_plot.py | 202 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 202 insertions(+) create mode 100644 scripts/dynamic_plot.py diff --git a/scripts/dynamic_plot.py b/scripts/dynamic_plot.py new file mode 100644 index 000000000..dd256aadd --- /dev/null +++ b/scripts/dynamic_plot.py @@ -0,0 +1,202 @@ +import pandas as pd +import plotly.graph_objects as go +from plotly.subplots import make_subplots +import numpy as np +from datetime import datetime +import os +from utils.helpers import get_settings # Import to access COLOR_SCHEME + +# Define custom colorscales for light and dark modes +COLOR_SCALES = { + "light": [ + [0.0, '#E0F2E9'], [0.2, '#A3D8A1'], [0.4, '#70BD70'], + [0.6, '#46A846'], [0.8, '#2E7D2E'], [1.0, '#004D00'] + ], + "dark": [ + [0.0, '#F0F0F0'], [0.2, '#BDBDBD'], [0.4, '#969696'], + [0.6, '#737373'], [0.8, '#525252'], [1.0, '#252525'] + ] +} +ALL_HOURS = list(range(24)) + + +def normalize_logarithmic(arr): + """Applies a logarithmic normalization to the array, mapping values between 0.5 and max(arr) to a normalized scale between 0 and 1.""" + arr = arr.astype(float) + min_val = 0.5 + arr = np.clip(arr, min_val, None) + return np.log(arr / min_val) / np.log(np.max(arr) / min_val) if np.max(arr) > min_val else arr - min_val + + +def determine_text_color(z, threshold=0.5): + """Determines text color (black or white) based on normalized value of z.""" + return np.where(z > threshold, 'white', 'black') + + +def add_annotations(fig, text_array, text_colors, col, row, species_list, all_hours, annotations): + """Collects annotations for the heatmap without adding them individually, appending them to the provided annotations list.""" + if col in [1, 2]: # Single-column heatmaps + for i, species in enumerate(species_list): + current_text, current_color = text_array[i, 0], text_colors[i, 0] + if current_text: + annotations.append(dict( + x=0, y=species, text=current_text, showarrow=False, + font=dict(color=current_color, size=10), + xref=f'x{col}', yref=f'y{col}', xanchor='center', yanchor='middle' + )) + elif col == 3: # Multi-column heatmap + for i, species in enumerate(species_list): + for j, hour in enumerate(all_hours): + current_text, current_color = text_array[i, j], text_colors[i, j] + if current_text: + annotations.append(dict( + x=hour, y=species, text=current_text, showarrow=False, + font=dict(color=current_color, size=10), + xref='x3', yref='y3', xanchor='center', yanchor='middle' + )) + + +def create_plotly_heatmap(df_birds, now): + """Creates a Plotly heatmap with annotations based on bird detection data.""" + # Titles and Subtitle + main_title = f"Hourly Overview Updated at {now.strftime('%Y-%m-%d %H:%M:%S')}" + subtitle = f"({df_birds['Com_Name'].nunique()} species today; {len(df_birds)} detections today)" + + # Ensure 'Time' is datetime + if not pd.api.types.is_datetime64_any_dtype(df_birds['Time']): + df_birds['Time'] = pd.to_datetime(df_birds['Time'], unit='ns') + + df_birds['Hour'] = df_birds['Time'].dt.hour + + # Group data and fill missing values + plot_dataframe = df_birds.groupby(['Hour', 'Com_Name']).agg( + Count=('Com_Name', 'count'), + Conf=('Confidence', 'max') + ).reset_index().fillna({'Conf': 0, 'Count': 0}) + + # Fetch color scheme setting and choose corresponding colorscale and text color + conf = get_settings() + color_scheme = conf.get('COLOR_SCHEME', 'light') + color_scale = COLOR_SCALES.get(color_scheme, COLOR_SCALES["light"]) + + # Summarize data for heatmap axes + df_birds_summary = plot_dataframe.groupby('Com_Name').agg( + Count=('Count', 'sum'), + Conf=('Conf', 'max') + ).reset_index() + df_birds_summary = df_birds_summary[df_birds_summary['Count'] > 0] + df_birds_summary.sort_values(by=['Count', 'Conf'], ascending=[False, False], inplace=True) + species_list = df_birds_summary['Com_Name'].tolist() + + # Normalize values and prepare text annotations + z_confidence = normalize_logarithmic(df_birds_summary['Conf'].values.reshape(-1, 1) * 100) + text_confidence = np.char.add(np.round(df_birds_summary['Conf'].values).astype(int).astype(str), ' %') + + z_detections = normalize_logarithmic(df_birds_summary['Count'].values.reshape(-1, 1)) + text_detections = df_birds_summary['Count'].astype(str).values # Use actual counts for annotations + text_color_detections = determine_text_color(z_detections, threshold=0.5, color_scheme=color_scheme) + + df_hourly = plot_dataframe.pivot_table(index='Com_Name', columns='Hour', values='Count', aggfunc='sum').fillna(0) + df_hourly = df_hourly.reindex(species_list).fillna(0).reindex(columns=ALL_HOURS, fill_value=0) + z_hourly = normalize_logarithmic(df_hourly.values) + text_hourly = df_hourly.astype(int).astype(str).values # Use actual counts for hourly annotations + text_color_hourly = determine_text_color(z_hourly, threshold=0.5, color_scheme=color_scheme) + + # Create subplots + fig = make_subplots(rows=1, cols=3, shared_yaxes=True, column_widths=[0.1, 0.1, 0.7], horizontal_spacing=0.02) + + # Heatmap Traces + fig.add_trace(go.Heatmap( + z=z_confidence, customdata=df_birds_summary['Conf'].values, x=['Confidence'], y=species_list, + colorscale=color_scale, showscale=False, hovertemplate='Species: %{y}
Max Confidence: %{customdata:.0f}%', + xgap=1, ygap=1, zmin=0, zmax=1 + ), row=1, col=1) + + fig.add_trace(go.Heatmap( + z=z_detections, customdata=df_birds_summary['Count'].values, x=['Count'], y=species_list, + colorscale=color_scale, showscale=False, hovertemplate='Species: %{y}
Total Counts: %{customdata}', + xgap=1, ygap=1, zmin=0, zmax=1 + ), row=1, col=2) + + fig.add_trace(go.Heatmap( + z=z_hourly, customdata=df_hourly.values, x=ALL_HOURS, y=species_list, + colorscale=color_scale, showscale=False, hovertemplate='Species: %{y}
Hour: %{x}
Detections: %{customdata}', + xgap=1, ygap=1, zmin=0, zmax=1 + ), row=1, col=3) + + # Annotations + annotations = [] + add_annotations(fig, text_confidence.reshape(-1, 1), determine_text_color(z_confidence, threshold=0.5, color_scheme=color_scheme), col=1, row=1, species_list=species_list, all_hours=ALL_HOURS, annotations=annotations) + add_annotations(fig, text_detections.reshape(-1, 1), text_color_detections, col=2, row=1, species_list=species_list, all_hours=ALL_HOURS, annotations=annotations) + add_annotations(fig, text_hourly, text_color_hourly, col=3, row=1, species_list=species_list, all_hours=ALL_HOURS, annotations=annotations) + fig.update_layout(annotations=annotations) + + # Layout configuration + fig.update_layout( + title=dict(text=f"{main_title}
{subtitle}", x=0.5, y=0.97, xanchor='center', yanchor='top', font=dict(size=24)), + autosize=True, height=max(600, len(species_list) * 25 + 100), + yaxis=dict(autorange='reversed', tickfont=dict(size=10), showticklabels=True, ticklabelstandoff=15, fixedrange=True), + xaxis1=dict(title='Max confidence', showticklabels=False, title_font=dict(size=10), fixedrange=True), + xaxis2=dict(title='Total counts', showticklabels=False, title_font=dict(size=10), fixedrange=True), + xaxis3=dict(title='Hour', tickfont=dict(size=10), tickmode='linear', dtick=1, fixedrange=True), + margin=dict(l=20, r=20, t=80, b=80), clickmode='event+select', + plot_bgcolor='#CCCCCC', paper_bgcolor='#7F7F7F', font=dict(size=10), dragmode=False + ) + fig.update_xaxes(showgrid=False, zeroline=False) + fig.update_yaxes(showgrid=False, zeroline=False) + + # Export the figure as an HTML string + html_str = ( + f"
" + + fig.to_html( + include_plotlyjs='cdn', + full_html=False, + default_height='80%', + default_width='100%', + config=dict( + scrollZoom=False, + doubleClick=False, + displaylogo=False, + displayModeBar=False, + modeBarButtonsToRemove=[ + 'zoom2d', 'pan2d', 'select2d', 'lasso2d', 'zoomIn2d', 'zoomOut2d', 'resetScale2d' + ] + ) + ) + + "
" + ) + + # Add CSS and JavaScript + html_str = ( + "" + + html_str + + "" + ) + + # Save the HTML file + output_dir = os.path.expanduser('~/BirdSongs/Extracted/Charts/') + os.makedirs(output_dir, exist_ok=True) + with open(os.path.join(output_dir, 'interactive_daily_plot.html'), 'w') as f: + f.write(html_str) + + # Clear figure to reset layout and prevent issues in future runs + fig.clear() From 2e9bee84a51c0e4f71fb89e820f649dee3e561af Mon Sep 17 00:00:00 2001 From: Alexandre <44178713+alexbelgium@users.noreply.github.com> Date: Wed, 30 Oct 2024 09:05:20 +0100 Subject: [PATCH 03/28] Call dynamic plot --- scripts/daily_plot.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/scripts/daily_plot.py b/scripts/daily_plot.py index cce51b16f..798df847b 100755 --- a/scripts/daily_plot.py +++ b/scripts/daily_plot.py @@ -14,6 +14,7 @@ from matplotlib.colors import LogNorm from utils.helpers import DB_PATH, get_settings +from dynamic_plot import create_plotly_heatmap def get_data(now=None): @@ -214,6 +215,10 @@ def main(daemon, sleep_m): data, time = get_data(now) if not data.empty: create_plot(data, time) + try: + create_plotly_heatmap(data, time) + except Exception as e: + print(f"Failed to create interactive heatmap: {e}") else: print('empty dataset') if daemon: From e3591e0823bbd8ff2e1447eceafbe745d91b23ad Mon Sep 17 00:00:00 2001 From: Alexandre <44178713+alexbelgium@users.noreply.github.com> Date: Wed, 30 Oct 2024 13:25:54 +0100 Subject: [PATCH 04/28] Full annotated code --- scripts/dynamic_plot.py | 115 ++++++++++++++++++++++++---------------- 1 file changed, 68 insertions(+), 47 deletions(-) diff --git a/scripts/dynamic_plot.py b/scripts/dynamic_plot.py index dd256aadd..9f3fd9aa6 100644 --- a/scripts/dynamic_plot.py +++ b/scripts/dynamic_plot.py @@ -4,19 +4,36 @@ import numpy as np from datetime import datetime import os -from utils.helpers import get_settings # Import to access COLOR_SCHEME - -# Define custom colorscales for light and dark modes -COLOR_SCALES = { - "light": [ - [0.0, '#E0F2E9'], [0.2, '#A3D8A1'], [0.4, '#70BD70'], - [0.6, '#46A846'], [0.8, '#2E7D2E'], [1.0, '#004D00'] - ], - "dark": [ - [0.0, '#F0F0F0'], [0.2, '#BDBDBD'], [0.4, '#969696'], - [0.6, '#737373'], [0.8, '#525252'], [1.0, '#252525'] - ] -} +from utils.helpers import get_settings # Import if needed for other settings + +# Fetch color scheme setting and choose corresponding colorscale and text color +conf = get_settings() +color_scheme = conf.get('COLOR_SCHEME', 'light') + +if color_scheme == 'dark': + PLOT_BGCOLOR='#F0F0F0' + PAPER_BGCOLOR='#7F7F7F' + CUSTOM_COLOR_SCALE = [ + [0.0, PLOT_BGCOLOR], + [0.2, '#BDBDBD'], + [0.4, '#969696'], + [0.6, '#737373'], + [0.8, '#525252'], + [1.0, '#252525'] + ] +else: + PLOT_BGCOLOR='#FFFFFF' + PAPER_BGCOLOR='#7BC58A' + CUSTOM_COLOR_SCALE = [ + [0.0, PLOT_BGCOLOR], + [0.1, '#E0F2E9'], + [0.2, '#A3D8A1'], + [0.4, '#70BD70'], + [0.6, '#46A846'], + [0.8, '#2E7D2E'], + [1.0, '#004D00'] + ] + ALL_HOURS = list(range(24)) @@ -28,9 +45,9 @@ def normalize_logarithmic(arr): return np.log(arr / min_val) / np.log(np.max(arr) / min_val) if np.max(arr) > min_val else arr - min_val -def determine_text_color(z, threshold=0.5): - """Determines text color (black or white) based on normalized value of z.""" - return np.where(z > threshold, 'white', 'black') +def determine_text_color(z, threshold=0.8): + """Determines text color (darkgrey or white) based on normalized value of z.""" + return np.where(z == 0, PLOT_BGCOLOR, np.where(z > threshold, PLOT_BGCOLOR, '#1A1A1A')) def add_annotations(fig, text_array, text_colors, col, row, species_list, all_hours, annotations): @@ -41,7 +58,7 @@ def add_annotations(fig, text_array, text_colors, col, row, species_list, all_ho if current_text: annotations.append(dict( x=0, y=species, text=current_text, showarrow=False, - font=dict(color=current_color, size=10), + font=dict(color=current_color, size=12), xref=f'x{col}', yref=f'y{col}', xanchor='center', yanchor='middle' )) elif col == 3: # Multi-column heatmap @@ -51,7 +68,7 @@ def add_annotations(fig, text_array, text_colors, col, row, species_list, all_ho if current_text: annotations.append(dict( x=hour, y=species, text=current_text, showarrow=False, - font=dict(color=current_color, size=10), + font=dict(color=current_color, size=12), xref='x3', yref='y3', xanchor='center', yanchor='middle' )) @@ -74,11 +91,6 @@ def create_plotly_heatmap(df_birds, now): Conf=('Confidence', 'max') ).reset_index().fillna({'Conf': 0, 'Count': 0}) - # Fetch color scheme setting and choose corresponding colorscale and text color - conf = get_settings() - color_scheme = conf.get('COLOR_SCHEME', 'light') - color_scale = COLOR_SCALES.get(color_scheme, COLOR_SCALES["light"]) - # Summarize data for heatmap axes df_birds_summary = plot_dataframe.groupby('Com_Name').agg( Count=('Count', 'sum'), @@ -89,58 +101,70 @@ def create_plotly_heatmap(df_birds, now): species_list = df_birds_summary['Com_Name'].tolist() # Normalize values and prepare text annotations - z_confidence = normalize_logarithmic(df_birds_summary['Conf'].values.reshape(-1, 1) * 100) - text_confidence = np.char.add(np.round(df_birds_summary['Conf'].values).astype(int).astype(str), ' %') + z_confidence = normalize_logarithmic(df_birds_summary['Conf'].values.reshape(-1, 1)) * 100 + text_confidence = np.char.add((df_birds_summary['Conf'].values * 100).round().astype(int).astype(str), ' %') z_detections = normalize_logarithmic(df_birds_summary['Count'].values.reshape(-1, 1)) text_detections = df_birds_summary['Count'].astype(str).values # Use actual counts for annotations - text_color_detections = determine_text_color(z_detections, threshold=0.5, color_scheme=color_scheme) + text_color_detections = determine_text_color(z_detections, threshold=0.5) # Removed color_scheme df_hourly = plot_dataframe.pivot_table(index='Com_Name', columns='Hour', values='Count', aggfunc='sum').fillna(0) df_hourly = df_hourly.reindex(species_list).fillna(0).reindex(columns=ALL_HOURS, fill_value=0) z_hourly = normalize_logarithmic(df_hourly.values) text_hourly = df_hourly.astype(int).astype(str).values # Use actual counts for hourly annotations - text_color_hourly = determine_text_color(z_hourly, threshold=0.5, color_scheme=color_scheme) + text_color_hourly = determine_text_color(z_hourly, threshold=0.5) # Removed color_scheme # Create subplots fig = make_subplots(rows=1, cols=3, shared_yaxes=True, column_widths=[0.1, 0.1, 0.7], horizontal_spacing=0.02) - # Heatmap Traces + # Prepare structured customdata arrays for each heatmap trace + custom_data_confidence = np.array([{'confidence': conf * 100} for conf in df_birds_summary['Conf'].values]).reshape(-1, 1) + custom_data_count = np.array([{'count': count} for count in df_birds_summary['Count'].values]).reshape(-1, 1) + + # Add traces with updated customdata structure and hovertemplate fig.add_trace(go.Heatmap( - z=z_confidence, customdata=df_birds_summary['Conf'].values, x=['Confidence'], y=species_list, - colorscale=color_scale, showscale=False, hovertemplate='Species: %{y}
Max Confidence: %{customdata:.0f}%', + z=z_confidence, customdata=custom_data_confidence, x=['Confidence'], y=species_list, + colorscale=CUSTOM_COLOR_SCALE, showscale=False, + hovertemplate='Species: %{y}
Max Confidence: %{customdata.confidence:.0f}%', xgap=1, ygap=1, zmin=0, zmax=1 ), row=1, col=1) - + fig.add_trace(go.Heatmap( - z=z_detections, customdata=df_birds_summary['Count'].values, x=['Count'], y=species_list, - colorscale=color_scale, showscale=False, hovertemplate='Species: %{y}
Total Counts: %{customdata}', + z=z_detections, customdata=custom_data_count, x=['Count'], y=species_list, + colorscale=CUSTOM_COLOR_SCALE, showscale=False, + hovertemplate='Species: %{y}
Total Counts: %{customdata.count}', xgap=1, ygap=1, zmin=0, zmax=1 ), row=1, col=2) fig.add_trace(go.Heatmap( z=z_hourly, customdata=df_hourly.values, x=ALL_HOURS, y=species_list, - colorscale=color_scale, showscale=False, hovertemplate='Species: %{y}
Hour: %{x}
Detections: %{customdata}', + colorscale=CUSTOM_COLOR_SCALE, showscale=False, hovertemplate='Species: %{y}
Hour: %{x}
Detections: %{customdata}', xgap=1, ygap=1, zmin=0, zmax=1 ), row=1, col=3) # Annotations annotations = [] - add_annotations(fig, text_confidence.reshape(-1, 1), determine_text_color(z_confidence, threshold=0.5, color_scheme=color_scheme), col=1, row=1, species_list=species_list, all_hours=ALL_HOURS, annotations=annotations) - add_annotations(fig, text_detections.reshape(-1, 1), text_color_detections, col=2, row=1, species_list=species_list, all_hours=ALL_HOURS, annotations=annotations) - add_annotations(fig, text_hourly, text_color_hourly, col=3, row=1, species_list=species_list, all_hours=ALL_HOURS, annotations=annotations) + add_annotations(fig, text_confidence.reshape(-1, 1), determine_text_color(z_confidence, threshold=0.5), + col=1, row=1, species_list=species_list, all_hours=ALL_HOURS, annotations=annotations) + add_annotations(fig, text_detections.reshape(-1, 1), text_color_detections, + col=2, row=1, species_list=species_list, all_hours=ALL_HOURS, annotations=annotations) + add_annotations(fig, text_hourly, text_color_hourly, + col=3, row=1, species_list=species_list, all_hours=ALL_HOURS, annotations=annotations) fig.update_layout(annotations=annotations) # Layout configuration fig.update_layout( - title=dict(text=f"{main_title}
{subtitle}", x=0.5, y=0.97, xanchor='center', yanchor='top', font=dict(size=24)), + title=dict(text=f"{main_title}
{subtitle}", + x=0.5, y=0.97, xanchor='center', yanchor='top', font=dict(size=24)), autosize=True, height=max(600, len(species_list) * 25 + 100), - yaxis=dict(autorange='reversed', tickfont=dict(size=10), showticklabels=True, ticklabelstandoff=15, fixedrange=True), - xaxis1=dict(title='Max confidence', showticklabels=False, title_font=dict(size=10), fixedrange=True), - xaxis2=dict(title='Total counts', showticklabels=False, title_font=dict(size=10), fixedrange=True), - xaxis3=dict(title='Hour', tickfont=dict(size=10), tickmode='linear', dtick=1, fixedrange=True), - margin=dict(l=20, r=20, t=80, b=80), clickmode='event+select', - plot_bgcolor='#CCCCCC', paper_bgcolor='#7F7F7F', font=dict(size=10), dragmode=False + yaxis=dict(autorange='reversed', tickfont=dict(size=12), showticklabels=True, ticklabelstandoff=15, fixedrange=True), + xaxis1=dict(title='Max Confidence', showticklabels=False, title_font=dict(size=12), fixedrange=True), + xaxis2=dict(title='Total Counts', showticklabels=False, title_font=dict(size=12), fixedrange=True), + xaxis3=dict(title='Hour', tickfont=dict(size=12), tickmode='linear', dtick=1, fixedrange=True), + margin=dict(l=20, r=20, t=80, b=80), clickmode='event+select', + plot_bgcolor=PAPER_BGCOLOR, + paper_bgcolor=PAPER_BGCOLOR, + font=dict(size=12, color='#000000'), dragmode=False ) fig.update_xaxes(showgrid=False, zeroline=False) fig.update_yaxes(showgrid=False, zeroline=False) @@ -197,6 +221,3 @@ def create_plotly_heatmap(df_birds, now): os.makedirs(output_dir, exist_ok=True) with open(os.path.join(output_dir, 'interactive_daily_plot.html'), 'w') as f: f.write(html_str) - - # Clear figure to reset layout and prevent issues in future runs - fig.clear() From 769827cdef2ddf1ba8cdfa98756bb59229b3dd40 Mon Sep 17 00:00:00 2001 From: Alexandre <44178713+alexbelgium@users.noreply.github.com> Date: Wed, 30 Oct 2024 13:46:26 +0100 Subject: [PATCH 05/28] Lint --- scripts/dynamic_plot.py | 66 ++++++++++++++++++++--------------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/scripts/dynamic_plot.py b/scripts/dynamic_plot.py index 9f3fd9aa6..dfbdecfb8 100644 --- a/scripts/dynamic_plot.py +++ b/scripts/dynamic_plot.py @@ -2,7 +2,6 @@ import plotly.graph_objects as go from plotly.subplots import make_subplots import numpy as np -from datetime import datetime import os from utils.helpers import get_settings # Import if needed for other settings @@ -11,28 +10,28 @@ color_scheme = conf.get('COLOR_SCHEME', 'light') if color_scheme == 'dark': - PLOT_BGCOLOR='#F0F0F0' - PAPER_BGCOLOR='#7F7F7F' - CUSTOM_COLOR_SCALE = [ - [0.0, PLOT_BGCOLOR], - [0.2, '#BDBDBD'], - [0.4, '#969696'], - [0.6, '#737373'], - [0.8, '#525252'], - [1.0, '#252525'] - ] + PLOT_BGCOLOR = '#F0F0F0' + PAPER_BGCOLOR = '#7F7F7F' + CUSTOM_COLOR_SCALE = [ + [0.0, PLOT_BGCOLOR], + [0.2, '#BDBDBD'], + [0.4, '#969696'], + [0.6, '#737373'], + [0.8, '#525252'], + [1.0, '#252525'] + ] else: - PLOT_BGCOLOR='#FFFFFF' - PAPER_BGCOLOR='#7BC58A' - CUSTOM_COLOR_SCALE = [ - [0.0, PLOT_BGCOLOR], - [0.1, '#E0F2E9'], - [0.2, '#A3D8A1'], - [0.4, '#70BD70'], - [0.6, '#46A846'], - [0.8, '#2E7D2E'], - [1.0, '#004D00'] - ] + PLOT_BGCOLOR = '#FFFFFF' + PAPER_BGCOLOR = '#7BC58A' + CUSTOM_COLOR_SCALE = [ + [0.0, PLOT_BGCOLOR], + [0.1, '#E0F2E9'], + [0.2, '#A3D8A1'], + [0.4, '#70BD70'], + [0.6, '#46A846'], + [0.8, '#2E7D2E'], + [1.0, '#004D00'] + ] ALL_HOURS = list(range(24)) @@ -124,44 +123,45 @@ def create_plotly_heatmap(df_birds, now): # Add traces with updated customdata structure and hovertemplate fig.add_trace(go.Heatmap( z=z_confidence, customdata=custom_data_confidence, x=['Confidence'], y=species_list, - colorscale=CUSTOM_COLOR_SCALE, showscale=False, + colorscale=CUSTOM_COLOR_SCALE, showscale=False, hovertemplate='Species: %{y}
Max Confidence: %{customdata.confidence:.0f}%', xgap=1, ygap=1, zmin=0, zmax=1 ), row=1, col=1) fig.add_trace(go.Heatmap( z=z_detections, customdata=custom_data_count, x=['Count'], y=species_list, - colorscale=CUSTOM_COLOR_SCALE, showscale=False, + colorscale=CUSTOM_COLOR_SCALE, showscale=False, hovertemplate='Species: %{y}
Total Counts: %{customdata.count}', xgap=1, ygap=1, zmin=0, zmax=1 ), row=1, col=2) fig.add_trace(go.Heatmap( z=z_hourly, customdata=df_hourly.values, x=ALL_HOURS, y=species_list, - colorscale=CUSTOM_COLOR_SCALE, showscale=False, hovertemplate='Species: %{y}
Hour: %{x}
Detections: %{customdata}', + colorscale=CUSTOM_COLOR_SCALE, showscale=False, + hovertemplate='Species: %{y}
Hour: %{x}
Detections: %{customdata}', xgap=1, ygap=1, zmin=0, zmax=1 ), row=1, col=3) # Annotations annotations = [] - add_annotations(fig, text_confidence.reshape(-1, 1), determine_text_color(z_confidence, threshold=0.5), - col=1, row=1, species_list=species_list, all_hours=ALL_HOURS, annotations=annotations) - add_annotations(fig, text_detections.reshape(-1, 1), text_color_detections, - col=2, row=1, species_list=species_list, all_hours=ALL_HOURS, annotations=annotations) - add_annotations(fig, text_hourly, text_color_hourly, - col=3, row=1, species_list=species_list, all_hours=ALL_HOURS, annotations=annotations) + add_annotations(fig, text_confidence.reshape(-1, 1), determine_text_color(z_confidence, threshold=0.5), + col=1, row=1, species_list=species_list, all_hours=ALL_HOURS, annotations=annotations) + add_annotations(fig, text_detections.reshape(-1, 1), text_color_detections, + col=2, row=1, species_list=species_list, all_hours=ALL_HOURS, annotations=annotations) + add_annotations(fig, text_hourly, text_color_hourly, + col=3, row=1, species_list=species_list, all_hours=ALL_HOURS, annotations=annotations) fig.update_layout(annotations=annotations) # Layout configuration fig.update_layout( - title=dict(text=f"{main_title}
{subtitle}", + title=dict(text=f"{main_title}
{subtitle}", x=0.5, y=0.97, xanchor='center', yanchor='top', font=dict(size=24)), autosize=True, height=max(600, len(species_list) * 25 + 100), yaxis=dict(autorange='reversed', tickfont=dict(size=12), showticklabels=True, ticklabelstandoff=15, fixedrange=True), xaxis1=dict(title='Max Confidence', showticklabels=False, title_font=dict(size=12), fixedrange=True), xaxis2=dict(title='Total Counts', showticklabels=False, title_font=dict(size=12), fixedrange=True), xaxis3=dict(title='Hour', tickfont=dict(size=12), tickmode='linear', dtick=1, fixedrange=True), - margin=dict(l=20, r=20, t=80, b=80), clickmode='event+select', + margin=dict(l=20, r=20, t=80, b=80), clickmode='event+select', plot_bgcolor=PAPER_BGCOLOR, paper_bgcolor=PAPER_BGCOLOR, font=dict(size=12, color='#000000'), dragmode=False From 0403b31738b6cdb2f553d726480116021a4f6c3b Mon Sep 17 00:00:00 2001 From: Alexandre Date: Wed, 30 Oct 2024 13:57:52 +0100 Subject: [PATCH 06/28] Add hourly confidence --- scripts/dynamic_plot.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/scripts/dynamic_plot.py b/scripts/dynamic_plot.py index dfbdecfb8..7535a7fc0 100644 --- a/scripts/dynamic_plot.py +++ b/scripts/dynamic_plot.py @@ -1,9 +1,9 @@ +import os import pandas as pd import plotly.graph_objects as go from plotly.subplots import make_subplots import numpy as np -import os -from utils.helpers import get_settings # Import if needed for other settings +from utils.helpers import get_settings # Fetch color scheme setting and choose corresponding colorscale and text color conf = get_settings() @@ -77,13 +77,13 @@ def create_plotly_heatmap(df_birds, now): # Titles and Subtitle main_title = f"Hourly Overview Updated at {now.strftime('%Y-%m-%d %H:%M:%S')}" subtitle = f"({df_birds['Com_Name'].nunique()} species today; {len(df_birds)} detections today)" - + # Ensure 'Time' is datetime if not pd.api.types.is_datetime64_any_dtype(df_birds['Time']): df_birds['Time'] = pd.to_datetime(df_birds['Time'], unit='ns') df_birds['Hour'] = df_birds['Time'].dt.hour - + # Group data and fill missing values plot_dataframe = df_birds.groupby(['Hour', 'Com_Name']).agg( Count=('Com_Name', 'count'), @@ -119,7 +119,7 @@ def create_plotly_heatmap(df_birds, now): # Prepare structured customdata arrays for each heatmap trace custom_data_confidence = np.array([{'confidence': conf * 100} for conf in df_birds_summary['Conf'].values]).reshape(-1, 1) custom_data_count = np.array([{'count': count} for count in df_birds_summary['Count'].values]).reshape(-1, 1) - + # Add traces with updated customdata structure and hovertemplate fig.add_trace(go.Heatmap( z=z_confidence, customdata=custom_data_confidence, x=['Confidence'], y=species_list, @@ -127,7 +127,7 @@ def create_plotly_heatmap(df_birds, now): hovertemplate='Species: %{y}
Max Confidence: %{customdata.confidence:.0f}%', xgap=1, ygap=1, zmin=0, zmax=1 ), row=1, col=1) - + fig.add_trace(go.Heatmap( z=z_detections, customdata=custom_data_count, x=['Count'], y=species_list, colorscale=CUSTOM_COLOR_SCALE, showscale=False, @@ -189,7 +189,7 @@ def create_plotly_heatmap(df_birds, now): ) + "" ) - + # Add CSS and JavaScript html_str = ( "" From a26a4e45e8df0cf4c6c0b008bb4d9e6b619e0f52 Mon Sep 17 00:00:00 2001 From: Alexandre Date: Wed, 30 Oct 2024 14:09:53 +0100 Subject: [PATCH 07/28] Lint --- scripts/dynamic_plot.py | 115 +++++++++++++++++++++++++--------------- 1 file changed, 72 insertions(+), 43 deletions(-) diff --git a/scripts/dynamic_plot.py b/scripts/dynamic_plot.py index 7535a7fc0..9c043e616 100644 --- a/scripts/dynamic_plot.py +++ b/scripts/dynamic_plot.py @@ -1,3 +1,4 @@ +"""This module generates a Plotly heatmap visualizing bird detection data, with hourly counts, confidence levels""" import os import pandas as pd import plotly.graph_objects as go @@ -5,7 +6,7 @@ import numpy as np from utils.helpers import get_settings -# Fetch color scheme setting and choose corresponding colorscale and text color + conf = get_settings() color_scheme = conf.get('COLOR_SCHEME', 'light') @@ -25,7 +26,6 @@ PAPER_BGCOLOR = '#7BC58A' CUSTOM_COLOR_SCALE = [ [0.0, PLOT_BGCOLOR], - [0.1, '#E0F2E9'], [0.2, '#A3D8A1'], [0.4, '#70BD70'], [0.6, '#46A846'], @@ -49,7 +49,7 @@ def determine_text_color(z, threshold=0.8): return np.where(z == 0, PLOT_BGCOLOR, np.where(z > threshold, PLOT_BGCOLOR, '#1A1A1A')) -def add_annotations(fig, text_array, text_colors, col, row, species_list, all_hours, annotations): +def add_annotations(text_array, text_colors, col, species_list, all_hours, annotations): """Collects annotations for the heatmap without adding them individually, appending them to the provided annotations list.""" if col in [1, 2]: # Single-column heatmaps for i, species in enumerate(species_list): @@ -74,7 +74,6 @@ def add_annotations(fig, text_array, text_colors, col, row, species_list, all_ho def create_plotly_heatmap(df_birds, now): """Creates a Plotly heatmap with annotations based on bird detection data.""" - # Titles and Subtitle main_title = f"Hourly Overview Updated at {now.strftime('%Y-%m-%d %H:%M:%S')}" subtitle = f"({df_birds['Com_Name'].nunique()} species today; {len(df_birds)} detections today)" @@ -84,13 +83,11 @@ def create_plotly_heatmap(df_birds, now): df_birds['Hour'] = df_birds['Time'].dt.hour - # Group data and fill missing values plot_dataframe = df_birds.groupby(['Hour', 'Com_Name']).agg( Count=('Com_Name', 'count'), Conf=('Confidence', 'max') ).reset_index().fillna({'Conf': 0, 'Count': 0}) - # Summarize data for heatmap axes df_birds_summary = plot_dataframe.groupby('Com_Name').agg( Count=('Count', 'sum'), Conf=('Conf', 'max') @@ -99,28 +96,29 @@ def create_plotly_heatmap(df_birds, now): df_birds_summary.sort_values(by=['Count', 'Conf'], ascending=[False, False], inplace=True) species_list = df_birds_summary['Com_Name'].tolist() - # Normalize values and prepare text annotations z_confidence = normalize_logarithmic(df_birds_summary['Conf'].values.reshape(-1, 1)) * 100 text_confidence = np.char.add((df_birds_summary['Conf'].values * 100).round().astype(int).astype(str), ' %') z_detections = normalize_logarithmic(df_birds_summary['Count'].values.reshape(-1, 1)) - text_detections = df_birds_summary['Count'].astype(str).values # Use actual counts for annotations - text_color_detections = determine_text_color(z_detections, threshold=0.5) # Removed color_scheme + text_detections = df_birds_summary['Count'].astype(str).values + text_color_detections = determine_text_color(z_detections, threshold=0.5) + + df_hourly_counts = plot_dataframe.pivot_table(index='Com_Name', columns='Hour', values='Count', aggfunc='sum').fillna(0) + df_hourly_conf = plot_dataframe.pivot_table(index='Com_Name', columns='Hour', values='Conf', aggfunc='max').fillna(0) + df_hourly_counts = df_hourly_counts.reindex(species_list).fillna(0).reindex(columns=ALL_HOURS, fill_value=0) + df_hourly_conf = df_hourly_conf.reindex(species_list).fillna(0).reindex(columns=ALL_HOURS, fill_value=0) + + z_hourly = normalize_logarithmic(df_hourly_counts.values) + text_hourly = df_hourly_counts.astype(int).astype(str).values + text_color_hourly = determine_text_color(z_hourly, threshold=0.5) - df_hourly = plot_dataframe.pivot_table(index='Com_Name', columns='Hour', values='Count', aggfunc='sum').fillna(0) - df_hourly = df_hourly.reindex(species_list).fillna(0).reindex(columns=ALL_HOURS, fill_value=0) - z_hourly = normalize_logarithmic(df_hourly.values) - text_hourly = df_hourly.astype(int).astype(str).values # Use actual counts for hourly annotations - text_color_hourly = determine_text_color(z_hourly, threshold=0.5) # Removed color_scheme + custom_data_hourly = np.dstack((df_hourly_counts.values, (df_hourly_conf.values * 100).astype(int))) - # Create subplots fig = make_subplots(rows=1, cols=3, shared_yaxes=True, column_widths=[0.1, 0.1, 0.7], horizontal_spacing=0.02) - # Prepare structured customdata arrays for each heatmap trace custom_data_confidence = np.array([{'confidence': conf * 100} for conf in df_birds_summary['Conf'].values]).reshape(-1, 1) custom_data_count = np.array([{'count': count} for count in df_birds_summary['Count'].values]).reshape(-1, 1) - # Add traces with updated customdata structure and hovertemplate fig.add_trace(go.Heatmap( z=z_confidence, customdata=custom_data_confidence, x=['Confidence'], y=species_list, colorscale=CUSTOM_COLOR_SCALE, showscale=False, @@ -136,42 +134,75 @@ def create_plotly_heatmap(df_birds, now): ), row=1, col=2) fig.add_trace(go.Heatmap( - z=z_hourly, customdata=df_hourly.values, x=ALL_HOURS, y=species_list, - colorscale=CUSTOM_COLOR_SCALE, showscale=False, - hovertemplate='Species: %{y}
Hour: %{x}
Detections: %{customdata}', - xgap=1, ygap=1, zmin=0, zmax=1 + z=z_hourly, + customdata=custom_data_hourly, + x=ALL_HOURS, + y=species_list, + colorscale=CUSTOM_COLOR_SCALE, + showscale=False, + text=text_hourly, + hovertemplate='Species: %{y}
Hour: %{x}
Detections: %{customdata[0]}
Max Confidence: %{customdata[1]}%', + xgap=1, + ygap=1, + zmin=0, + zmax=1 ), row=1, col=3) - # Annotations annotations = [] - add_annotations(fig, text_confidence.reshape(-1, 1), determine_text_color(z_confidence, threshold=0.5), - col=1, row=1, species_list=species_list, all_hours=ALL_HOURS, annotations=annotations) - add_annotations(fig, text_detections.reshape(-1, 1), text_color_detections, - col=2, row=1, species_list=species_list, all_hours=ALL_HOURS, annotations=annotations) - add_annotations(fig, text_hourly, text_color_hourly, - col=3, row=1, species_list=species_list, all_hours=ALL_HOURS, annotations=annotations) + add_annotations(text_confidence.reshape(-1, 1), determine_text_color(z_confidence, threshold=0.5), + col=1, species_list=species_list, all_hours=ALL_HOURS, annotations=annotations) + add_annotations(text_detections.reshape(-1, 1), text_color_detections, + col=2, species_list=species_list, all_hours=ALL_HOURS, annotations=annotations) + add_annotations(text_hourly, text_color_hourly, + col=3, species_list=species_list, all_hours=ALL_HOURS, annotations=annotations) fig.update_layout(annotations=annotations) - # Layout configuration fig.update_layout( - title=dict(text=f"{main_title}
{subtitle}", - x=0.5, y=0.97, xanchor='center', yanchor='top', font=dict(size=24)), - autosize=True, height=max(600, len(species_list) * 25 + 100), - yaxis=dict(autorange='reversed', tickfont=dict(size=12), showticklabels=True, ticklabelstandoff=15, fixedrange=True), - xaxis1=dict(title='Max Confidence', showticklabels=False, title_font=dict(size=12), fixedrange=True), - xaxis2=dict(title='Total Counts', showticklabels=False, title_font=dict(size=12), fixedrange=True), - xaxis3=dict(title='Hour', tickfont=dict(size=12), tickmode='linear', dtick=1, fixedrange=True), - margin=dict(l=20, r=20, t=80, b=80), clickmode='event+select', + title=dict( + text=f"{main_title}
{subtitle}", + x=0.5, y=0.97, xanchor='center', yanchor='top', + font=dict(size=24) + ), + autosize=True, + height=max(600, len(species_list) * 25 + 100), + yaxis=dict( + autorange='reversed', + tickfont=dict(size=12), + showticklabels=True, + ticklabelstandoff=15, + fixedrange=True + ), + xaxis1=dict( + title='Max Confidence', + showticklabels=False, + title_font=dict(size=12), + fixedrange=True + ), + xaxis2=dict( + title='Total Counts', + showticklabels=False, + title_font=dict(size=12), + fixedrange=True + ), + xaxis3=dict( + title='Hour', + tickfont=dict(size=12), + tickmode='linear', + dtick=1, + fixedrange=True + ), + margin=dict(l=20, r=20, t=80, b=80), + clickmode='event+select', plot_bgcolor=PAPER_BGCOLOR, paper_bgcolor=PAPER_BGCOLOR, - font=dict(size=12, color='#000000'), dragmode=False + font=dict(size=12, color='#000000'), # Global font color set to black + dragmode=False ) fig.update_xaxes(showgrid=False, zeroline=False) fig.update_yaxes(showgrid=False, zeroline=False) - # Export the figure as an HTML string html_str = ( - f"
" + "
" + fig.to_html( include_plotlyjs='cdn', full_html=False, @@ -190,7 +221,6 @@ def create_plotly_heatmap(df_birds, now): + "
" ) - # Add CSS and JavaScript html_str = ( "" + html_str + @@ -216,8 +246,7 @@ def create_plotly_heatmap(df_birds, now): "" ) - # Save the HTML file output_dir = os.path.expanduser('~/BirdSongs/Extracted/Charts/') os.makedirs(output_dir, exist_ok=True) - with open(os.path.join(output_dir, 'interactive_daily_plot.html'), 'w') as f: + with open(os.path.join(output_dir, 'interactive_daily_plot.html'), 'w', encoding='utf-8') as f: f.write(html_str) From 887ed86a5bdc460d4e9322ca9ae4839fe9133bf5 Mon Sep 17 00:00:00 2001 From: Alexandre Date: Wed, 30 Oct 2024 14:15:48 +0100 Subject: [PATCH 08/28] Interactive instead dynamic --- scripts/daily_plot.py | 2 +- scripts/{dynamic_plot.py => utils/interactive_plot.py} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename scripts/{dynamic_plot.py => utils/interactive_plot.py} (100%) diff --git a/scripts/daily_plot.py b/scripts/daily_plot.py index 798df847b..55c29159e 100755 --- a/scripts/daily_plot.py +++ b/scripts/daily_plot.py @@ -14,7 +14,7 @@ from matplotlib.colors import LogNorm from utils.helpers import DB_PATH, get_settings -from dynamic_plot import create_plotly_heatmap +from utils.interactive_plot import create_plotly_heatmap def get_data(now=None): diff --git a/scripts/dynamic_plot.py b/scripts/utils/interactive_plot.py similarity index 100% rename from scripts/dynamic_plot.py rename to scripts/utils/interactive_plot.py From 7921c89832be785d0c16596f0e65d6bb474b14eb Mon Sep 17 00:00:00 2001 From: Alexandre Date: Wed, 30 Oct 2024 14:18:17 +0100 Subject: [PATCH 09/28] Fix confidence color --- scripts/utils/interactive_plot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/utils/interactive_plot.py b/scripts/utils/interactive_plot.py index 9c043e616..6ace08e5f 100644 --- a/scripts/utils/interactive_plot.py +++ b/scripts/utils/interactive_plot.py @@ -123,7 +123,7 @@ def create_plotly_heatmap(df_birds, now): z=z_confidence, customdata=custom_data_confidence, x=['Confidence'], y=species_list, colorscale=CUSTOM_COLOR_SCALE, showscale=False, hovertemplate='Species: %{y}
Max Confidence: %{customdata.confidence:.0f}%', - xgap=1, ygap=1, zmin=0, zmax=1 + xgap=1, ygap=1, zmin=0, zmax=100 ), row=1, col=1) fig.add_trace(go.Heatmap( From 53795b15c682a50fe3e9a092bba85cf27f0f2f34 Mon Sep 17 00:00:00 2001 From: Alexandre <44178713+alexbelgium@users.noreply.github.com> Date: Wed, 30 Oct 2024 15:35:22 +0100 Subject: [PATCH 10/28] Check for screen size first --- scripts/overview.php | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/scripts/overview.php b/scripts/overview.php index 23c2f41b7..19b2d3bef 100644 --- a/scripts/overview.php +++ b/scripts/overview.php @@ -323,16 +323,27 @@ function setModalText(iter, title, text, authorlink, photolink, licenseurl) { $time = time(); $interactivechart_path = './Charts/' . $interactivechart; $chart_path = './Charts/' . $chart; -if (file_exists($interactivechart_path)) { - $html_content = file_get_contents($interactivechart_path); - echo $html_content; -} elseif (file_exists($chart_path)) { - echo ""; -} ?> +
From 5a2e390049541f1be6bfec44ba8393876470d3d2 Mon Sep 17 00:00:00 2001 From: Alexandre <44178713+alexbelgium@users.noreply.github.com> Date: Wed, 30 Oct 2024 17:29:47 +0100 Subject: [PATCH 11/28] Leaner code --- scripts/overview.php | 27 ++++++++------------------- 1 file changed, 8 insertions(+), 19 deletions(-) diff --git a/scripts/overview.php b/scripts/overview.php index 19b2d3bef..23c2f41b7 100644 --- a/scripts/overview.php +++ b/scripts/overview.php @@ -323,27 +323,16 @@ function setModalText(iter, title, text, authorlink, photolink, licenseurl) { $time = time(); $interactivechart_path = './Charts/' . $interactivechart; $chart_path = './Charts/' . $chart; +if (file_exists($interactivechart_path)) { + $html_content = file_get_contents($interactivechart_path); + echo $html_content; +} elseif (file_exists($chart_path)) { + echo ""; +} ?> - From f6cd0e6d72f31e0f320ec52dd566d85c4dc949f7 Mon Sep 17 00:00:00 2001 From: Alexandre <44178713+alexbelgium@users.noreply.github.com> Date: Wed, 30 Oct 2024 17:32:42 +0100 Subject: [PATCH 12/28] Avoid interactive chart flashing first on mobile --- scripts/overview.php | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/scripts/overview.php b/scripts/overview.php index 23c2f41b7..ce5a6ba16 100644 --- a/scripts/overview.php +++ b/scripts/overview.php @@ -313,7 +313,7 @@ function setModalText(iter, title, text, authorlink, photolink, licenseurl) {
-
+ From 13d64dcdf48f18fe7fb5f18fcec395f48001ae9a Mon Sep 17 00:00:00 2001 From: Alexandre <44178713+alexbelgium@users.noreply.github.com> Date: Thu, 31 Oct 2024 08:29:19 +0100 Subject: [PATCH 13/28] Add filtering --- scripts/utils/interactive_plot.py | 111 +++++++++++++++++++++++------- 1 file changed, 87 insertions(+), 24 deletions(-) diff --git a/scripts/utils/interactive_plot.py b/scripts/utils/interactive_plot.py index 6ace08e5f..406b6c5c3 100644 --- a/scripts/utils/interactive_plot.py +++ b/scripts/utils/interactive_plot.py @@ -2,6 +2,7 @@ import os import pandas as pd import plotly.graph_objects as go +import json from plotly.subplots import make_subplots import numpy as np from utils.helpers import get_settings @@ -156,6 +157,7 @@ def create_plotly_heatmap(df_birds, now): add_annotations(text_hourly, text_color_hourly, col=3, species_list=species_list, all_hours=ALL_HOURS, annotations=annotations) fig.update_layout(annotations=annotations) + annotations_json = json.dumps(annotations) fig.update_layout( title=dict( @@ -221,30 +223,91 @@ def create_plotly_heatmap(df_birds, now): + "
" ) - html_str = ( - "" - + html_str + - "" - ) + html_str = f""" + +
+
+ + +
+ {fig.to_html( + include_plotlyjs='cdn', + full_html=False, + default_height='80%', + default_width='100%', + config=dict( + scrollZoom=False, + doubleClick=False, + displaylogo=False, + displayModeBar=False, + modeBarButtonsToRemove=[ + 'zoom2d', 'pan2d', 'select2d', 'lasso2d', 'zoomIn2d', 'zoomOut2d', 'resetScale2d' + ] + ) + )} +
+ + + """ + output_dir = os.path.expanduser('~/BirdSongs/Extracted/Charts/') os.makedirs(output_dir, exist_ok=True) From be924885d3fecfa037047acb555fe0a6ba048b2d Mon Sep 17 00:00:00 2001 From: Alexandre <44178713+alexbelgium@users.noreply.github.com> Date: Thu, 31 Oct 2024 08:31:31 +0100 Subject: [PATCH 14/28] Lint --- scripts/utils/interactive_plot.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/scripts/utils/interactive_plot.py b/scripts/utils/interactive_plot.py index 406b6c5c3..c78d7260a 100644 --- a/scripts/utils/interactive_plot.py +++ b/scripts/utils/interactive_plot.py @@ -227,10 +227,10 @@ def create_plotly_heatmap(df_birds, now):
- -
{fig.to_html( @@ -249,14 +249,14 @@ def create_plotly_heatmap(df_birds, now): ) )}
- + """ - output_dir = os.path.expanduser('~/BirdSongs/Extracted/Charts/') os.makedirs(output_dir, exist_ok=True) with open(os.path.join(output_dir, 'interactive_daily_plot.html'), 'w', encoding='utf-8') as f: From 615ee3c0036830d09b8f1e66fc342b47bb29b88c Mon Sep 17 00:00:00 2001 From: Alexandre <44178713+alexbelgium@users.noreply.github.com> Date: Thu, 31 Oct 2024 08:40:23 +0100 Subject: [PATCH 15/28] Improve filtering appearance --- scripts/utils/interactive_plot.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/utils/interactive_plot.py b/scripts/utils/interactive_plot.py index c78d7260a..8c25863c9 100644 --- a/scripts/utils/interactive_plot.py +++ b/scripts/utils/interactive_plot.py @@ -228,10 +228,10 @@ def create_plotly_heatmap(df_birds, now):
- + style='padding: 5px; font-size: 14px; background-color: rgba(255, 255, 255, 0.5); color: #333; border: none; + border-radius: 3px; width: 150px;' /> +
{fig.to_html( include_plotlyjs='cdn', From 8e19876910e201d6d7752d4bad16a4cc02d27c8b Mon Sep 17 00:00:00 2001 From: Alexandre <44178713+alexbelgium@users.noreply.github.com> Date: Thu, 31 Oct 2024 08:51:04 +0100 Subject: [PATCH 16/28] Allow sorting by Count (default), Species, or Max confidence --- scripts/utils/interactive_plot.py | 45 ++++++++++++++++++++++++++----- 1 file changed, 39 insertions(+), 6 deletions(-) diff --git a/scripts/utils/interactive_plot.py b/scripts/utils/interactive_plot.py index 8c25863c9..7e572d0eb 100644 --- a/scripts/utils/interactive_plot.py +++ b/scripts/utils/interactive_plot.py @@ -223,15 +223,25 @@ def create_plotly_heatmap(df_birds, now): + "
" ) + # Correct `custom_data_confidence` in the create_plotly_heatmap function: + custom_data_confidence = np.array([{'confidence': conf * 100, 'count': count} for conf, count in zip(df_birds_summary['Conf'].values, df_birds_summary['Count'].values)]).reshape(-1, 1) + + # In HTML string: html_str = f"""
-
+
+
{fig.to_html( include_plotlyjs='cdn', @@ -249,23 +259,43 @@ def create_plotly_heatmap(df_birds, now): ) )}
- +