harshini9942 commited on
Commit
ab781ed
Β·
1 Parent(s): 231c749

Logo added

Browse files
Files changed (1) hide show
  1. streamlit/dashboard.py +153 -35
streamlit/dashboard.py CHANGED
@@ -16,6 +16,7 @@ Components
16
 
17
  # ==================== IMPORTS ====================
18
  import streamlit as st
 
19
  import os
20
  import pandas as pd
21
  import numpy as np
@@ -468,20 +469,19 @@ def _load_launch_date_csv(launch_csv):
468
 
469
  def _scan_existing_nc_for_launch_dates(incois_wmo_set, launch_csv):
470
  """
471
- Scan already-downloaded more_components/{wmo}_meta.nc files and extract
472
- LAUNCH_DATE for any INCOIS float not yet in the CSV.
473
  Returns count of NEW entries added.
474
  """
475
- target_dir = BASE_DIR / "more_components"
476
- if not target_dir.exists():
477
- return 0
478
  existing = _load_launch_date_csv(launch_csv)
479
  already_have = set(existing["wmo_id"].tolist())
480
  new_rows = []
481
  for wmo in incois_wmo_set:
482
  if wmo in already_have:
483
  continue
484
- meta_path = target_dir / f"{wmo}_meta.nc"
 
 
485
  if not meta_path.exists():
486
  continue
487
  ld = _read_launch_date_from_nc(meta_path)
@@ -516,8 +516,17 @@ df_prof["profiler_name"] = df_prof["wmo_id"].map(_meta_pname).fillna("Unknown")
516
 
517
  @st.dialog("Float Information", width="large")
518
  def show_float_details(wmo):
519
- meta_path = BASE_DIR / f"more_components/{wmo}_meta.nc"
520
- prof_path = BASE_DIR / f"more_components/{wmo}_prof.nc"
 
 
 
 
 
 
 
 
 
521
 
522
  # Auto-download from IFREMER GDAC if files do not exist
523
  if not meta_path.exists() or not prof_path.exists():
@@ -529,13 +538,13 @@ def show_float_details(wmo):
529
  else:
530
  dac = "incois" # fallback
531
 
532
- meta_url = f"ftp://ftp.ifremer.fr/ifremer/argo/dac/{dac}/{wmo}/{wmo}_meta.nc"
533
- prof_url = f"ftp://ftp.ifremer.fr/ifremer/argo/dac/{dac}/{wmo}/{wmo}_prof.nc"
534
 
535
- target_dir = BASE_DIR / "more_components"
536
  target_dir.mkdir(exist_ok=True)
537
 
538
- with st.spinner(f"Downloading GDAC NetCDF files for {wmo} ({dac})..."):
539
  try:
540
  if not meta_path.exists():
541
  urllib.request.urlretrieve(meta_url, meta_path)
@@ -830,7 +839,97 @@ st.markdown(
830
  # Read URL query params for shareable filter state
831
  qp = st.query_params
832
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
833
  with st.sidebar:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
834
  st.markdown("## πŸ” Filters")
835
 
836
  # ── Refresh ──
@@ -848,6 +947,7 @@ with st.sidebar:
848
  value=qp.get("wmo", ""),
849
  placeholder="e.g. 2902115, 2902116",
850
  help="Comma-separated WMO numbers",
 
851
  )
852
 
853
  # ── Parameter Filter ──
@@ -1101,14 +1201,7 @@ col_left, col_right = st.columns([55, 45], gap="medium")
1101
  with col_left:
1102
  # ── Component 1: Geospatial Float Position Map (PRD Β§7.1) ──
1103
  st.markdown('<div class="stPlotlyChart">', unsafe_allow_html=True)
1104
- c_title, c_btn = st.columns([7, 3])
1105
- with c_title:
1106
- st.markdown("### πŸ“ Geographic Float Positions")
1107
- with c_btn:
1108
- if st.button("🏠 Reset Map Data", use_container_width=True):
1109
- if "main_map" in st.session_state:
1110
- del st.session_state["main_map"]
1111
- st.rerun()
1112
 
1113
  if len(filt_prof) > 0:
1114
  # --- Check map selection from session state ---
@@ -1173,7 +1266,6 @@ with col_left:
1173
  lon=group["longitude"].tolist(),
1174
  mode="lines+markers+text",
1175
  text=group["profile_seq"].astype(str).tolist(),
1176
- customdata=[[wmo]] * len(group),
1177
  textposition="top right",
1178
  textfont=dict(size=11, color="white"),
1179
  marker=dict(size=7, color=color, opacity=0.9),
@@ -1231,6 +1323,9 @@ with col_left:
1231
  if len(map_df) > 12_000:
1232
  map_df = map_df.sample(12_000, random_state=42)
1233
 
 
 
 
1234
  fig_map = px.scatter_mapbox(
1235
  map_df,
1236
  lat="latitude",
@@ -1246,10 +1341,11 @@ with col_left:
1246
  "longitude": ":.2f",
1247
  },
1248
  zoom=2,
1249
- center={"lat": -10, "lon": 80},
1250
  category_orders={"institution": list(REGION_COLORS.keys())},
1251
  )
1252
  fig_map.update_traces(marker=dict(size=8, opacity=0.9))
 
1253
  fig_map.update_layout(
1254
  mapbox_style="white-bg",
1255
  mapbox_layers=[
@@ -1302,14 +1398,7 @@ with col_left:
1302
  with col_right:
1303
  st.markdown('<div class="stPlotlyChart">', unsafe_allow_html=True)
1304
  # ── Bar chart (PRD Β§7.2) ──
1305
- c_bar_title, c_bar_btn = st.columns([7, 3])
1306
- with c_bar_title:
1307
- st.markdown("### πŸ“ˆ Number of Floats per DAC")
1308
- with c_bar_btn:
1309
- if st.button("🏠 Reset Chart Data", use_container_width=True):
1310
- if "bar_chart" in st.session_state:
1311
- del st.session_state["bar_chart"]
1312
- st.rerun()
1313
 
1314
  if len(filt_prof) > 0 and "dac" in filt_prof.columns:
1315
  # Active floats in the last 90 days of each year
@@ -1881,9 +1970,6 @@ if len(df_meta) > 0:
1881
  if st.button("🌐 Fetch Missing Launch Dates from GDAC", key="fetch_launch_dates"):
1882
  import urllib.request as _urlreq
1883
 
1884
- target_dir = BASE_DIR / "more_components"
1885
- target_dir.mkdir(exist_ok=True)
1886
-
1887
  existing_csv = _load_launch_date_csv(launch_csv)
1888
  new_rows = []
1889
  failed = []
@@ -1895,6 +1981,18 @@ if len(df_meta) > 0:
1895
  status_ph.markdown(f"Fetching **{wmo}** &nbsp;({idx}/{total})…")
1896
  prog.progress(idx / total)
1897
 
 
 
 
 
 
 
 
 
 
 
 
 
1898
  meta_path = target_dir / f"{wmo}_meta.nc"
1899
  dac = _dac_lookup.get(wmo, "incois")
1900
 
@@ -2001,6 +2099,14 @@ if len(df_meta) > 0:
2001
  )
2002
 
2003
  body_html = ""
 
 
 
 
 
 
 
 
2004
  for year in pivot.index:
2005
  is_total_row = year == "Total"
2006
  row_bg = "background:rgba(0,188,212,0.06);" if is_total_row else ""
@@ -2014,12 +2120,24 @@ if len(df_meta) > 0:
2014
  for col in pivot.columns:
2015
  val = pivot.loc[year, col]
2016
  is_tot = is_total_row or col == "Total"
2017
- style = "font-weight:bold;color:#FFB74D;" if is_tot else ""
 
 
 
 
 
 
 
 
 
 
 
 
2018
  cell = (
2019
  f"{int(val):,}" if val > 0
2020
  else "<span style='color:rgba(255,255,255,0.18)'>-</span>"
2021
  )
2022
- row_html += f"<td style='{style}'>{cell}</td>"
2023
  body_html += f"<tr style='{row_bg}'>{row_html}</tr>"
2024
 
2025
  st.markdown(
 
16
 
17
  # ==================== IMPORTS ====================
18
  import streamlit as st
19
+ import streamlit.components.v1 as components
20
  import os
21
  import pandas as pd
22
  import numpy as np
 
469
 
470
  def _scan_existing_nc_for_launch_dates(incois_wmo_set, launch_csv):
471
  """
472
+ Scan already-downloaded more_components/{wmo}_meta.nc or inactive_floats/{wmo}_meta.nc files
473
+ and extract LAUNCH_DATE for any INCOIS float not yet in the CSV.
474
  Returns count of NEW entries added.
475
  """
 
 
 
476
  existing = _load_launch_date_csv(launch_csv)
477
  already_have = set(existing["wmo_id"].tolist())
478
  new_rows = []
479
  for wmo in incois_wmo_set:
480
  if wmo in already_have:
481
  continue
482
+ meta_path = BASE_DIR / f"more_components/{wmo}_meta.nc"
483
+ if not meta_path.exists():
484
+ meta_path = BASE_DIR / f"inactive_floats/{wmo}_meta.nc"
485
  if not meta_path.exists():
486
  continue
487
  ld = _read_launch_date_from_nc(meta_path)
 
516
 
517
  @st.dialog("Float Information", width="large")
518
  def show_float_details(wmo):
519
+ # Determine if active/inactive to set target folder
520
+ float_profiles = df_prof[df_prof["wmo_id"] == str(wmo)]
521
+ is_active = False
522
+ if not float_profiles.empty:
523
+ latest_prof_date = float_profiles["date"].max()
524
+ if latest_prof_date is not pd.NaT and latest_prof_date.year >= 2026:
525
+ is_active = True
526
+
527
+ folder_name = "more_components" if is_active else "inactive_floats"
528
+ meta_path = BASE_DIR / f"{folder_name}/{wmo}_meta.nc"
529
+ prof_path = BASE_DIR / f"{folder_name}/{wmo}_prof.nc"
530
 
531
  # Auto-download from IFREMER GDAC if files do not exist
532
  if not meta_path.exists() or not prof_path.exists():
 
538
  else:
539
  dac = "incois" # fallback
540
 
541
+ meta_url = f"https://data-argo.ifremer.fr/dac/{dac}/{wmo}/{wmo}_meta.nc"
542
+ prof_url = f"https://data-argo.ifremer.fr/dac/{dac}/{wmo}/{wmo}_prof.nc"
543
 
544
+ target_dir = BASE_DIR / folder_name
545
  target_dir.mkdir(exist_ok=True)
546
 
547
+ with st.spinner(f"Downloading GDAC NetCDF files for {wmo} ({dac}) to local {folder_name} folder..."):
548
  try:
549
  if not meta_path.exists():
550
  urllib.request.urlretrieve(meta_url, meta_path)
 
839
  # Read URL query params for shareable filter state
840
  qp = st.query_params
841
 
842
+ # ── Reset Trigger for Map Home Button ──
843
+ st.markdown(
844
+ """
845
+ <style>
846
+ /* Hide the reset trigger input container completely */
847
+ div.element-container:has(input[placeholder="reset_trigger_placeholder"]) {
848
+ display: none !important;
849
+ }
850
+ </style>
851
+ """,
852
+ unsafe_allow_html=True
853
+ )
854
+ reset_trigger = st.text_input("", placeholder="reset_trigger_placeholder", key="reset_trigger")
855
+
856
+ if reset_trigger == "true":
857
+ if "main_map" in st.session_state:
858
+ st.session_state.main_map = None
859
+ if "bar_chart" in st.session_state:
860
+ st.session_state.bar_chart = None
861
+ if "last_viewed_wmo" in st.session_state:
862
+ st.session_state.last_viewed_wmo = None
863
+ if "search_wmo_input" in st.session_state:
864
+ st.session_state.search_wmo_input = ""
865
+ st.query_params.update({"wmo": ""})
866
+ st.session_state.reset_trigger = ""
867
+ st.rerun()
868
+
869
+ components.html(
870
+ """
871
+ <script>
872
+ (function() {
873
+ const parentDoc = window.parent.document;
874
+ const interval = setInterval(() => {
875
+ const resetButtons = parentDoc.querySelectorAll('.modebar-btn[data-val="reset"]');
876
+ if (resetButtons.length > 0) {
877
+ resetButtons.forEach(btn => {
878
+ if (!btn.dataset.hasResetListener) {
879
+ btn.dataset.hasResetListener = "true";
880
+ btn.addEventListener('click', function(e) {
881
+ const input = parentDoc.querySelector('input[placeholder="reset_trigger_placeholder"]');
882
+ if (input) {
883
+ const nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, "value").set;
884
+ nativeInputValueSetter.call(input, "true");
885
+ input.dispatchEvent(new Event('input', { bubbles: true }));
886
+ input.dispatchEvent(new Event('change', { bubbles: true }));
887
+ input.blur();
888
+ }
889
+ });
890
+ }
891
+ });
892
+ }
893
+ }, 500);
894
+ })();
895
+ </script>
896
+ """,
897
+ height=0,
898
+ width=0
899
+ )
900
+
901
  with st.sidebar:
902
+ # ── circular logo ──
903
+ try:
904
+ import base64
905
+ logo_path = BASE_DIR / "incois_logo.jpg"
906
+ if logo_path.exists():
907
+ with open(logo_path, "rb") as f:
908
+ logo_data = base64.b64encode(f.read()).decode("utf-8")
909
+ st.markdown(
910
+ f"""
911
+ <div style="display: flex; justify-content: center; margin-top: 10px; margin-bottom: 20px;">
912
+ <div style="
913
+ width: 140px;
914
+ height: 140px;
915
+ border-radius: 50%;
916
+ overflow: hidden;
917
+ border: 4px solid #1f6feb;
918
+ box-shadow: 0 4px 15px rgba(0, 0, 0, 0.4);
919
+ background-color: white;
920
+ display: flex;
921
+ justify-content: center;
922
+ align-items: center;
923
+ ">
924
+ <img src="data:image/jpeg;base64,{logo_data}" style="width: 100%; height: 100%; object-fit: cover;" />
925
+ </div>
926
+ </div>
927
+ """,
928
+ unsafe_allow_html=True
929
+ )
930
+ except Exception as e:
931
+ pass
932
+
933
  st.markdown("## πŸ” Filters")
934
 
935
  # ── Refresh ──
 
947
  value=qp.get("wmo", ""),
948
  placeholder="e.g. 2902115, 2902116",
949
  help="Comma-separated WMO numbers",
950
+ key="search_wmo_input",
951
  )
952
 
953
  # ── Parameter Filter ──
 
1201
  with col_left:
1202
  # ── Component 1: Geospatial Float Position Map (PRD Β§7.1) ──
1203
  st.markdown('<div class="stPlotlyChart">', unsafe_allow_html=True)
1204
+ st.markdown("### πŸ“ Geographic Float Positions")
 
 
 
 
 
 
 
1205
 
1206
  if len(filt_prof) > 0:
1207
  # --- Check map selection from session state ---
 
1266
  lon=group["longitude"].tolist(),
1267
  mode="lines+markers+text",
1268
  text=group["profile_seq"].astype(str).tolist(),
 
1269
  textposition="top right",
1270
  textfont=dict(size=11, color="white"),
1271
  marker=dict(size=7, color=color, opacity=0.9),
 
1323
  if len(map_df) > 12_000:
1324
  map_df = map_df.sample(12_000, random_state=42)
1325
 
1326
+ center_lat = float(map_df["latitude"].mean()) if len(map_df) > 0 else -10.0
1327
+ center_lon = float(map_df["longitude"].mean()) if len(map_df) > 0 else 80.0
1328
+
1329
  fig_map = px.scatter_mapbox(
1330
  map_df,
1331
  lat="latitude",
 
1341
  "longitude": ":.2f",
1342
  },
1343
  zoom=2,
1344
+ center={"lat": center_lat, "lon": center_lon},
1345
  category_orders={"institution": list(REGION_COLORS.keys())},
1346
  )
1347
  fig_map.update_traces(marker=dict(size=8, opacity=0.9))
1348
+
1349
  fig_map.update_layout(
1350
  mapbox_style="white-bg",
1351
  mapbox_layers=[
 
1398
  with col_right:
1399
  st.markdown('<div class="stPlotlyChart">', unsafe_allow_html=True)
1400
  # ── Bar chart (PRD Β§7.2) ──
1401
+ st.markdown("### πŸ“ˆ Number of Floats per DAC")
 
 
 
 
 
 
 
1402
 
1403
  if len(filt_prof) > 0 and "dac" in filt_prof.columns:
1404
  # Active floats in the last 90 days of each year
 
1970
  if st.button("🌐 Fetch Missing Launch Dates from GDAC", key="fetch_launch_dates"):
1971
  import urllib.request as _urlreq
1972
 
 
 
 
1973
  existing_csv = _load_launch_date_csv(launch_csv)
1974
  new_rows = []
1975
  failed = []
 
1981
  status_ph.markdown(f"Fetching **{wmo}** &nbsp;({idx}/{total})…")
1982
  prog.progress(idx / total)
1983
 
1984
+ # Determine if active/inactive to set target folder
1985
+ float_profiles = df_prof[df_prof["wmo_id"] == str(wmo)]
1986
+ is_active = False
1987
+ if not float_profiles.empty:
1988
+ latest_prof_date = float_profiles["date"].max()
1989
+ if latest_prof_date is not pd.NaT and latest_prof_date.year >= 2026:
1990
+ is_active = True
1991
+
1992
+ folder_name = "more_components" if is_active else "inactive_floats"
1993
+ target_dir = BASE_DIR / folder_name
1994
+ target_dir.mkdir(exist_ok=True)
1995
+
1996
  meta_path = target_dir / f"{wmo}_meta.nc"
1997
  dac = _dac_lookup.get(wmo, "incois")
1998
 
 
2099
  )
2100
 
2101
  body_html = ""
2102
+
2103
+ # Calculate max value for heatmap scaling (excluding Totals)
2104
+ try:
2105
+ heatmap_max = pivot.drop("Total", axis=0).drop("Total", axis=1).max().max()
2106
+ except:
2107
+ heatmap_max = 1
2108
+ if heatmap_max <= 0: heatmap_max = 1
2109
+
2110
  for year in pivot.index:
2111
  is_total_row = year == "Total"
2112
  row_bg = "background:rgba(0,188,212,0.06);" if is_total_row else ""
 
2120
  for col in pivot.columns:
2121
  val = pivot.loc[year, col]
2122
  is_tot = is_total_row or col == "Total"
2123
+
2124
+ if is_tot:
2125
+ style = "font-weight:bold;color:#FFB74D;"
2126
+ cell_bg = ""
2127
+ else:
2128
+ style = ""
2129
+ if val > 0:
2130
+ intensity = min(val / heatmap_max, 1.0)
2131
+ # Cyan color (#00BCD4) with dynamic opacity based on value
2132
+ cell_bg = f"background:rgba(0, 188, 212, {max(0.1, intensity * 0.9)});"
2133
+ else:
2134
+ cell_bg = ""
2135
+
2136
  cell = (
2137
  f"{int(val):,}" if val > 0
2138
  else "<span style='color:rgba(255,255,255,0.18)'>-</span>"
2139
  )
2140
+ row_html += f"<td style='{style}{cell_bg}'>{cell}</td>"
2141
  body_html += f"<tr style='{row_bg}'>{row_html}</tr>"
2142
 
2143
  st.markdown(