diff --git a/.streamlit/config.toml b/.streamlit/config.toml
index 7bea4d1..0dba8f6 100644
--- a/.streamlit/config.toml
+++ b/.streamlit/config.toml
@@ -1,7 +1,7 @@
[server]
headless = true
address = "0.0.0.0"
-port = 8501
+port = 5000
enableCORS = true
enableXsrfProtection = true
diff --git a/logo_SISSA-1.png b/logo_SISSA-1.png
new file mode 100644
index 0000000..704e1e1
Binary files /dev/null and b/logo_SISSA-1.png differ
diff --git a/logo_inest_spoke9.png b/logo_inest_spoke9.png
new file mode 100644
index 0000000..84ae0a6
Binary files /dev/null and b/logo_inest_spoke9.png differ
diff --git a/rom.py b/rom.py
index 8635e2e..a7df5ab 100644
--- a/rom.py
+++ b/rom.py
@@ -1,77 +1,243 @@
-import sys
import numpy as np
import streamlit as st
import cloudpickle
import matplotlib.pyplot as plt
-from matplotlib.colors import LogNorm
-from scipy.ndimage import gaussian_filter
from mpl_toolkits.axes_grid1 import make_axes_locatable
from pathlib import Path
+from datetime import date
+import datetime
+
+today = date.today()
+lat = np.load('required_data/latitude.npy')
+long = np.load('required_data/longitude.npy')
+
+# ---------------- CONFIG ----------------
+st.set_page_config(page_title="AdriaSeaWatch", page_icon="ogs_logo.png", layout="wide")
BASE_DIR = Path(__file__).resolve().parent
DAILY_DIR = BASE_DIR / "daily_data"
MODELS_DIR = BASE_DIR / "models"
-st.set_page_config(
- page_title="AdriaSeaWatch",
- page_icon="ogs_logo.png"
-)
+Po = np.linspace(200, 4000, 10000)
+Isonzo = np.linspace(0, 500, 10000)
+Timavo = np.linspace(8, 80, 10000)
+
+variables_list = ["N1p","N3n","S","T","Chla"]
+variables_name = {
+ "N1p":r"Phospates [$$\text{mg}/\text{m}^3$$]",
+ "N3n":r"Nitrates [$$\text{mg}/\text{m}^3$$]",
+ "S":r"Salinity",
+ "T":r"Temperature [$$^\circ C$$]",
+ "Chla":r"Chlorophyll [$$\text{mg}/\text{m}^3$$]"
+}
+depth_list = [0, 10] # metri
+days_list = [0,1,2,3,4,5]
-Po=np.linspace(200,4000,10000)
-Isonzo=np.linspace(0,500,10000)
-Timavo=np.linspace(8,80,10000)
-variables=["N1p","N3n","S","T","Chla"]
-depth={"O m":0, "10 m":1, "30 m":2, "60 m":3, "100 m":4}
+depth_dict = {0:0, 10:1}
-vmins={"N1p":0,"N3n": 0, "Chla":0, "S":30, "T":13}
-vmaxs={"N1p":0.2,"N3n": 10, "Chla":4, "S":39.5, "T":30}
+vmins = {"N1p":0,"N3n":0,"Chla":0,"S":30,"T":13}
+vmaxs = {"N1p":0.2,"N3n":10,"Chla":4,"S":39.5,"T":30}
-# --- CONFIGURE YOUR CHOICES ---
-depth_list = [0,10]
-variables_list=["N1p","N3n","S","T","Chla"]
-days_list=[0,1,2,3,4,5]
+# ---------------- STATE INIT ----------------
+def init_state():
+ if "day_idx" not in st.session_state:
+ st.session_state.day_idx = 0
+ if "var_idx" not in st.session_state:
+ st.session_state.var_idx = 0
+ if "depth_idx" not in st.session_state:
+ st.session_state.depth_idx = 0
+ if "po" not in st.session_state:
+ st.session_state.po = float(Po[-1]/2)
+ if "isonzo" not in st.session_state:
+ st.session_state.isonzo = float(Isonzo[-1]/2)
+ if "timavo" not in st.session_state:
+ st.session_state.timavo = float(Timavo[-1]/2)
-depth_dict={
- 0:0,
- 10:1,
- # 30:2,
- # 60:3,
- # 100:4
+init_state()
-}
+# ---------------- ACTIONS ----------------
+def prev_day():
+ st.session_state.day_idx = max(0, st.session_state.day_idx - 1)
-# --- WIDGETS ---
-st.sidebar.title("Controls")
-days_ctx = st.sidebar.selectbox("Days", days_list, index=0)
-variables_ctx = st.sidebar.selectbox("Variable", variables_list, index=0)
-depth_ctx = st.sidebar.selectbox("Depth", depth_list, index=0)
+def next_day():
+ st.session_state.day_idx = min(len(days_list)-1, st.session_state.day_idx + 1)
-po= st.sidebar.slider("Po", min_value=Po[0], max_value=Po[-1], value=Po[-1]/2)
-isonzo = st.sidebar.slider("Isonzo", min_value=Isonzo[0], max_value=Isonzo[-1], value=Isonzo[-1]/2)
-timavo = st.sidebar.slider("Timavo", min_value=Timavo[0], max_value=Timavo[-1], value=Timavo[-1]/2)
+def set_var(i):
+ st.session_state.var_idx = i
-# --- MODEL PREDICTION (placeholder) ---
+def set_depth(i):
+ st.session_state.depth_idx = i
+
+# ---------------- MODEL PREDICT ----------------
def model_predict(days, variable, depth, po, isonzo, timavo):
- tmp=np.load(DAILY_DIR / "{}_{}.npy".format(variable,depth))
- model_filename = "model_{}_{}.pkl".format(variable, depth_dict[depth])
- model_path = MODELS_DIR / "model_{}".format(variable) / model_filename
+ tmp = np.load(DAILY_DIR / f"{variable}_{depth}.npy")
+ model_filename = f"model_{variable}_{depth_dict[depth]}.pkl"
+ model_path = MODELS_DIR / f"model_{variable}" / model_filename
- with open(model_path, 'rb') as f:
+ with open(model_path, "rb") as f:
model = cloudpickle.load(f)
- output=model(tmp, isonzo, po, timavo, days)
+ output = model(tmp, isonzo, po, timavo, days)
+ print(f"Predicted {variable} at {depth}m for day {days} with Po={po}, Isonzo={isonzo}, Timavo={timavo}")
+ print(output.mean())
return output
-# Compute
-Z = model_predict(days_ctx, variables_ctx, depth_ctx, po, isonzo, timavo)
+# ---------------- CURRENT CONTEXT ----------------
+days_ctx = days_list[st.session_state.day_idx]
+variables_ctx = variables_list[st.session_state.var_idx]
+depth_ctx = depth_list[st.session_state.depth_idx]
+
+po = st.session_state.po
+isonzo = st.session_state.isonzo
+timavo = st.session_state.timavo
+
+# ---------------- LAYOUT ----------------
+# 3 colonne: bottoni variabile | plot | bottoni profondità
+
+main = st.container(horizontal=True)
+left, mid, right = main.columns([1.5, 6, 1.2], gap="large", )
+
+with left:
+ fdate = today.strftime('%d/%m/%Y')
+ st.markdown("### AdriaSeaWatch ROM Explorer")
+ st.markdown(f"Analysis performed on: {fdate}")
+ st.markdown("##### Model Description")
+ st.html("""
+
+The model is a data-driven reduced-order forecasting system composed of three autoregressive models, each operating on a different sub-region of the domain. Temporal evolution is learned using Gaussian Process–based autoregression. Unlike traditional forecasting models, it does not rely on physical equations or numerical solvers, but learns the system dynamics directly from historical data.
+
+
+
+The model does not require explicit boundary conditions. The initial conditions are given by the spatial field at the bulletin day and by the river discharge values selected by the user, which drive the subsequent temporal evolution.
+
+"""
+ )
+ logo1, logo2, logo3 = st.columns(3, vertical_alignment="center")
+ logo1.image("ogs_logo.png", width=60)
+ logo2.image("logo_SISSA-1.png", width=100)
+ logo3.image("logo_inest_spoke9.png", width=100)
+
+
+with right:
+ st.markdown("##### Field of Interest")
+ for i, v in enumerate(variables_list):
+ st.button(
+ variables_name[v],
+ use_container_width=True,
+ type="primary" if i == st.session_state.var_idx else "secondary",
+ on_click=set_var,
+ args=(i,),
+ key=f"var_{v}"
+ )
+ right.markdown(r"##### Depth [$$\text{m}$$]")
+ for i, d in enumerate(depth_list):
+ st.button(
+ f"{d} m",
+ use_container_width=True,
+ type="primary" if i == st.session_state.depth_idx else "secondary",
+ on_click=set_depth,
+ args=(i,),
+ key=f"depth_{d}"
+ )
+
+ st.markdown(r"##### River Discharge [$${\text{m}^3}/\text{s}$$]")
+ st.session_state.depth_idx = depth_list.index(depth_ctx)
+ # slider sotto i bottoni temporali
+ # st.session_state.po = st.slider("Po", float(Po[0]), float(Po[-1]), float(st.session_state.po))
+ # st.session_state.isonzo = st.slider("Isonzo", float(Isonzo[0]), float(Isonzo[-1]), float(st.session_state.isonzo))
+ # st.session_state.timavo = st.slider("Timavo", float(Timavo[0]), float(Timavo[-1]), float(st.session_state.timavo))
+
+ disable_sliders = (st.session_state.day_idx == 0)
+
+ po = st.slider(
+ "Po",
+ float(Po[0]), float(Po[-1]),
+ value=float(st.session_state.po),
+ key="po",
+ disabled=disable_sliders
+ )
+
+ isonzo = st.slider(
+ "Isonzo",
+ float(Isonzo[0]), float(Isonzo[-1]),
+ value=float(st.session_state.isonzo),
+ key="isonzo",
+ disabled=disable_sliders
+ )
+
+ timavo = st.slider(
+ "Timavo",
+ float(Timavo[0]), float(Timavo[-1]),
+ value=float(st.session_state.timavo),
+ key="timavo",
+ disabled=disable_sliders
+ )
+
+po = st.session_state.po
+isonzo = st.session_state.isonzo
+timavo = st.session_state.timavo
+
+with mid:
+ # calcolo e plot in mezzo
+ Z = model_predict(days_ctx, variables_ctx, depth_ctx, po, isonzo, timavo)
+ Zma = np.ma.asarray(Z) # lo tratto come masked array comunque
+ mask = np.ma.getmaskarray(Zma) # True dove c'è "--"
+ Zplot = Zma.astype(object) # fondamentale: altrimenti None non entra
+ Zplot[mask] = None # masked -> None
+
+
+ import plotly.graph_objects as go
+
+ fig = go.Figure(
+ data=go.Heatmap(
+ z=Zplot[::-1], # Inverti l'asse y per avere l'origine in basso
+ x=long,
+ y=lat,
+ colorscale="Jet",
+ zmin=vmins[variables_ctx],
+ zmax=vmaxs[variables_ctx],
+ # colorbar=dict(title=variables_name[variables_ctx]),
+ hovertemplate='Lat: %{y}
Lon: %{x}
Value: %{z}