From 13ad39e200e76db0877d2b1821eb41b4879fefe0 Mon Sep 17 00:00:00 2001 From: geofrizz Date: Wed, 27 Aug 2025 16:20:52 +0200 Subject: [PATCH 1/3] added icon for buoy position --- ICON/buoy_icon_2.png | Bin 0 -> 23575 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 ICON/buoy_icon_2.png diff --git a/ICON/buoy_icon_2.png b/ICON/buoy_icon_2.png new file mode 100644 index 0000000000000000000000000000000000000000..862581460ae98b7c9692d7ac13576af560acb989 GIT binary patch literal 23575 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 SFahcNz)d^n8 Date: Wed, 27 Aug 2025 16:23:57 +0200 Subject: [PATCH 2/3] new version of libraries for pip, for solve issue with copernicus marine tool --- docker/requirements_jupyter_3-13.txt | 46 +++++----------------------- 1 file changed, 7 insertions(+), 39 deletions(-) diff --git a/docker/requirements_jupyter_3-13.txt b/docker/requirements_jupyter_3-13.txt index 2dbb24f..fcc744c 100644 --- a/docker/requirements_jupyter_3-13.txt +++ b/docker/requirements_jupyter_3-13.txt @@ -1,48 +1,16 @@ jupyter==1.1.1 -copernicusmarine==2.0.1 +copernicusmarine==2.2.2 netCDF4==1.7.2 -folium==0.19.5 +folium==0.20.0 seawater==3.3.4 -scipy==1.15.2 -shapely==2.1.0 +scipy==1.16.1 +shapely==2.1.1 ftputil==5.1.0 pykml==0.2.0 -Unidecode==1.3.8 -scikit-learn==1.6.1 -matplotlib==3.10.1 +Unidecode==1.4.0 +scikit-learn==1.7.1 +matplotlib==3.10.5 altair==5.5.0 -#accessible-pygments==0.0.5 -#blosc2==2.7.1 -#docutils==0.21.2 -#greenlet==3.1.1 -#hdf5plugin==5.0.0 -#imagesize==1.4.1 -#jupyter-book==1.0.4.post1 -#jupyter-cache==1.0.1 -#jupyter-resource-usage==1.1.1 -#jupyterlab_myst==2.4.2 -#latexcodec==3.0.0 -#linkify-it-py==2.0.3 -#markdown-it-py==3.0.0 -#mdit-py-plugins==0.4.2 -#mdurl==0.1.2 -#msgpack==1.1.0 -#myst-nb==1.2.0 -#ndindex==1.9.2 -#nodejs==0.1.1 -#numexpr==2.10.2 -#ptyprocess==0.7.0 -#pure_eval==0.2.3 -#py-cpuinfo==9.0.0 -#pybtex==0.24.0 -#pybtex-docutils==1.0.3 -#pydata-sphinx-theme==0.16.1 -#snowballstemmer==2.2.0 -#SQLAlchemy==2.0.39 -#tables==3.10.1 -#tabulate==0.9.0 -#uc-micro-py==1.0.3 -#variable_inspector==1.0.1 From 4525b145c58f09f0d131ba3ea91e5d9a2977482c Mon Sep 17 00:00:00 2001 From: geofrizz Date: Thu, 28 Aug 2025 15:56:57 +0200 Subject: [PATCH 3/3] new version for fase 03 and 05 --- notebooks/03_Model_module.ipynb | 23 ++- notebooks/05_Cal_Val_module.ipynb | 262 ++++++++++++++++++++++-------- 2 files changed, 202 insertions(+), 83 deletions(-) diff --git a/notebooks/03_Model_module.ipynb b/notebooks/03_Model_module.ipynb index a55dd11..fd7823c 100644 --- a/notebooks/03_Model_module.ipynb +++ b/notebooks/03_Model_module.ipynb @@ -71,7 +71,10 @@ "import re\n", "\n", "from netCDF4 import Dataset,num2date\n", - "from shapely.geometry import box, Point, Polygon" + "from shapely.geometry import box, Point, Polygon\n", + "\n", + "print(f\" Copernicus Marine Toolbox version: {cm.__version__}\")\n", + "print(f\" Python version: {sys.version}\")" ] }, { @@ -227,6 +230,8 @@ "metadata": {}, "outputs": [], "source": [ + "cm.login()\n", + "\n", "if modelproduct == 'anfc':\n", " print(\"Check if anfc product land-sea mask is already available or download it.\")\n", " LS_mask = LS_mask_dir+'/MED-MFC_006_013_mask_bathy.nc'\n", @@ -383,7 +388,7 @@ " reader = csv.DictReader(csvfile, delimiter=',')\n", "\n", " for row in reader:\n", - " \n", + " \n", " # Read platform code\n", " platform_code = row['platform_code']\n", " print(f\"** Start processing platform {platform_code} for download\")\n", @@ -478,7 +483,7 @@ " logT.write(msg + \"\\n\") \n", " continue\n", " except Exception as e:\n", - " msg = f\"Unexpected error for platform {platform_code} at depth={depth}: {e}\"\n", + " msg = f\"Unexpected error for platform {platform_code}\"\n", " print(msg)\n", " with open(log_fileT, 'a') as logT:\n", " logT.write(msg + \"\\n\") \n", @@ -574,7 +579,7 @@ " logT.write(msg + \"\\n\") \n", " continue\n", " except Exception as e:\n", - " msg = f\"Unexpected error for platform {platform_code} at depth={depth}: {e}\"\n", + " msg = f\"Unexpected error for platform {platform_code}\"\n", " print(msg)\n", " with open(log_fileT, 'a') as logT:\n", " logT.write(msg + \"\\n\") \n", @@ -815,14 +820,6 @@ " modS_nc.setncattr('SOURCE_field_type', attr_value)\n", " modS_nc.delncattr('field_type') \n" ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "a9f22a89-5b95-404d-9de9-573cdaaf0b8d", - "metadata": {}, - "outputs": [], - "source": [] } ], "metadata": { @@ -841,7 +838,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.18" + "version": "3.13.7" } }, "nbformat": 4, diff --git a/notebooks/05_Cal_Val_module.ipynb b/notebooks/05_Cal_Val_module.ipynb index fe9711e..435061d 100644 --- a/notebooks/05_Cal_Val_module.ipynb +++ b/notebooks/05_Cal_Val_module.ipynb @@ -265,9 +265,7 @@ "cell_type": "code", "execution_count": null, "id": "5ddbeb90-24f0-4184-955c-a83c2387bc8b", - "metadata": { - "scrolled": true - }, + "metadata": {}, "outputs": [], "source": [ "# --- INITIAL SETUP ---\n", @@ -279,7 +277,9 @@ "for p in np.arange(0,data_obs.shape[0]): \n", " print(f\"** Processing Platform: {data_obs['platform_code'][p]}\")\n", " \n", - " charts_for_platform = []\n", + " charts_group_1 = [] # For first 15 loops (i <= 14)\n", + " charts_group_2 = [] # For subsequent loops (i > 14 and i <=29)\n", + " charts_group_3 = [] # For subsequent loops (i > 29)\n", " lat = data_obs['lat'][p]\n", " lon = data_obs['lon'][p]\n", " platform_code = str(data_obs['platform_code'][p])\n", @@ -412,16 +412,27 @@ " align='left', baseline='top', dx=5, dy=5, fontSize=12, lineBreak='\\n'\n", " ).encode(x='x_pos:T', y='y_pos:Q', text='text:N')\n", "\n", + " total_sum = np.sum(clim_temp_slice)\n", + " if not np.isnan(total_sum):\n", + " # Case 1: CLIM data is valid, use a 3-color scale\n", + " color_encoding = alt.Color(\"Temperature:N\",\n", + " scale=alt.Scale(\n", + " domain=['OBS', 'MODEL', 'CLIM'],\n", + " range=['#1f77b4', '#ff7f0e', '#2ca02c'] # Blue, Orange, Green\n", + " ))\n", + " else:\n", + " # Case 2: CLIM data is invalid, use a 2-color scale\n", + " color_encoding = alt.Color(\"Temperature:N\",\n", + " scale=alt.Scale(\n", + " domain=['OBS', 'MODEL'],\n", + " range=['#1f77b4', '#ff7f0e'] # Blue, Orange\n", + " )) \n", " line_plot = alt.Chart(source).mark_line().encode(\n", " x=alt.X('time:T', title='Date', axis=alt.Axis(format=\"%d-%m-%Y\", labelAngle=-45)),\n", " y=alt.Y('y:Q', title='Temperature [°C]', scale=alt.Scale(domain=y_domain)),\n", - " color=alt.Color(\"Temperature:N\",\n", - " scale=alt.Scale(\n", - " domain=['OBS', 'MODEL', 'CLIM'],\n", - " range=['#1f77b4', '#ff7f0e', '#2ca02c'] # Blue, Orange, Green\n", - " ))\n", + " color=color_encoding\n", " )\n", - "\n", + " \n", " nearest = alt.selection_point(nearest=True, on=\"pointerover\", fields=[\"time\"], empty=False)\n", " selectors = alt.Chart(source).mark_point().encode(x=\"time:T\", opacity=alt.value(0)).add_params(nearest)\n", " points = line_plot.mark_point().encode(opacity=alt.condition(nearest, alt.value(1), alt.value(0)))\n", @@ -434,44 +445,93 @@ " width=350, height=120, title=chart_title \n", " ).interactive()\n", " \n", - " charts_for_platform.append(final_layer)\n", - "\n", + " if i <= 14:\n", + " charts_group_1.append(final_layer)\n", + " elif i > 14 and i <= 29:\n", + " charts_group_2.append(final_layer)\n", + " else:\n", + " charts_group_3.append(final_layer)\n", + " \n", + " \n", " except Exception as e:\n", " print(f\" ERROR while processing depth {depth_label}: {e}\")\n", " continue\n", "\n", " \n", - " # --- ADDING MARKER AND POPUP TO FOLIUM ---\n", - " if charts_for_platform:\n", - "\n", - " # This method creates a scrollable window inside the popup\n", - " combined_chart = alt.vconcat(*charts_for_platform).resolve_scale(color='independent')\n", - " chart_html_body = combined_chart.to_html()\n", - "\n", - " # 1. Create a new HTML structure with a centered div wrapper\n", - " chart_html = f\"\"\"\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " {chart_html_body}\n", - " \n", - " \n", - " \"\"\"\n", - "\n", - " # 2. Create an IFrame with the chart HTML and set a fixed size for the viewport\n", - " iframe = folium.IFrame(chart_html, width=600, height=450)\n", - "\n", - " # 3. Add the IFrame to a Popup\n", + " # --- ADDING MARKERS TO FOLIUM ---\n", + "\n", + " # **Marker 1: For the first 15 plots**\n", + " if charts_group_1:\n", + " combined_chart_1 = alt.vconcat(*charts_group_1).resolve_scale(color='independent')\n", + " chart_html_body = combined_chart_1.to_html()\n", + " \n", + " # Simple HTML wrapper for the popup\n", + " popup_html = f\"\"\"\n", + " {chart_html_body}\n", + " \"\"\"\n", + "\n", + " iframe = folium.IFrame(popup_html, width=600, height=450)\n", " popup = folium.Popup(iframe, max_width=600)\n", + " icon = folium.features.CustomIcon(icon_url, icon_size=(28, 30))\n", + " \n", + " if len(charts_group_2)>0:\n", + " folium.Marker(\n", + " location=[lat, lon],\n", + " icon=icon,\n", + " tooltip=f\"{platform_name} (Depth levels 1-15)\", \n", + " popup=popup\n", + " ).add_to(m)\n", + " else:\n", + " folium.Marker(\n", + " location=[lat, lon],\n", + " icon=icon,\n", + " tooltip=f\"{platform_name}\", \n", + " popup=popup\n", + " ).add_to(m) \n", + "\n", + " # **Marker 2: For the following set of plots, if they exist**\n", + " if charts_group_2:\n", + " combined_chart_2 = alt.vconcat(*charts_group_2).resolve_scale(color='independent')\n", + " chart_html_body = combined_chart_2.to_html()\n", + " \n", + " popup_html = f\"\"\"\n", + " {chart_html_body}\n", + " \"\"\"\n", + "\n", + " iframe = folium.IFrame(popup_html, width=600, height=450)\n", + " popup = folium.Popup(iframe, max_width=600)\n", + " icon = folium.features.CustomIcon(icon_url, icon_size=(28, 30))\n", + " \n", + " # Add a small offset to the longitude to make the second marker clickable\n", + " lon_offset = 0.1 \n", + " \n", + " folium.Marker(\n", + " location=[lat, lon + lon_offset],\n", + " icon=icon,\n", + " tooltip=f\"{platform_name} (Depth levels 16+)\",\n", + " popup=popup\n", + " ).add_to(m)\n", "\n", + " # **Marker 3: For the following set of plots, if they exist**\n", + " if charts_group_3:\n", + " combined_chart_3 = alt.vconcat(*charts_group_3).resolve_scale(color='independent')\n", + " chart_html_body = combined_chart_3.to_html()\n", + " \n", + " popup_html = f\"\"\"\n", + " {chart_html_body}\n", + " \"\"\"\n", + "\n", + " iframe = folium.IFrame(popup_html, width=600, height=450)\n", + " popup = folium.Popup(iframe, max_width=600)\n", " icon = folium.features.CustomIcon(icon_url, icon_size=(28, 30))\n", " \n", + " # Add a small offset to the longitude to make the second marker clickable\n", + " lon_offset = 0.1 \n", + " \n", " folium.Marker(\n", - " location=[lat, lon],\n", + " location=[lat, lon + lon_offset*2],\n", " icon=icon,\n", - " tooltip=platform_name,\n", + " tooltip=f\"{platform_name} (Depth levels 31+)\",\n", " popup=popup\n", " ).add_to(m)\n", "\n", @@ -676,7 +736,9 @@ "for p in np.arange(0,data_obs.shape[0]): \n", " print(f\"** Processing Platform: {data_obs['platform_code'][p]}\")\n", " \n", - " charts_for_platform = []\n", + " charts_group_1 = [] # For first 15 loops (i <= 14)\n", + " charts_group_2 = [] # For subsequent loops (i > 14 and i <=29)\n", + " charts_group_3 = [] # For subsequent loops (i > 29)\n", " lat = data_obs['lat'][p]\n", " lon = data_obs['lon'][p]\n", " platform_code = str(data_obs['platform_code'][p])\n", @@ -806,14 +868,25 @@ " align='left', baseline='top', dx=5, dy=5, fontSize=12, lineBreak='\\n'\n", " ).encode(x='x_pos:T', y='y_pos:Q', text='text:N')\n", "\n", + " total_sum = np.sum(clim_temp_slice)\n", + " if not np.isnan(total_sum):\n", + " # Case 1: CLIM data is valid, use a 3-color scale\n", + " color_encoding = alt.Color(\"Salinity:N\",\n", + " scale=alt.Scale(\n", + " domain=['OBS', 'MODEL', 'CLIM'],\n", + " range=['#1f77b4', '#ff7f0e', '#2ca02c'] # Blue, Orange, Green\n", + " ))\n", + " else:\n", + " # Case 2: CLIM data is invalid, use a 2-color scale\n", + " color_encoding = alt.Color(\"Salinity:N\",\n", + " scale=alt.Scale(\n", + " domain=['OBS', 'MODEL'],\n", + " range=['#1f77b4', '#ff7f0e'] # Blue, Orange\n", + " )) \n", " line_plot = alt.Chart(source).mark_line().encode(\n", " x=alt.X('time:T', title='Date', axis=alt.Axis(format=\"%d-%m-%Y\", labelAngle=-45)),\n", " y=alt.Y('y:Q', title='Salinity [PSU]', scale=alt.Scale(domain=y_domain)),\n", - " color=alt.Color(\"Salinity:N\",\n", - " scale=alt.Scale(\n", - " domain=['OBS', 'MODEL', 'CLIM'],\n", - " range=['#1f77b4', '#ff7f0e', '#2ca02c'] # Blue, Orange, Green\n", - " ))\n", + " color=color_encoding\n", " )\n", "\n", " nearest = alt.selection_point(nearest=True, on=\"pointerover\", fields=[\"time\"], empty=False)\n", @@ -828,47 +901,96 @@ " width=350, height=120, title=chart_title \n", " ).interactive()\n", " \n", - " charts_for_platform.append(final_layer)\n", + " if i <= 14:\n", + " charts_group_1.append(final_layer)\n", + " elif i > 14 and i <= 29:\n", + " charts_group_2.append(final_layer)\n", + " else:\n", + " charts_group_3.append(final_layer)\n", "\n", " except Exception as e:\n", " print(f\" ERROR while processing depth {depth_label}: {e}\")\n", " continue\n", "\n", " \n", - " # --- ADDING MARKER AND POPUP TO FOLIUM ---\n", - " if charts_for_platform:\n", - "\n", - " # This method creates a scrollable window inside the popup\n", - " combined_chart = alt.vconcat(*charts_for_platform).resolve_scale(color='independent')\n", - " chart_html_body = combined_chart.to_html()\n", - "\n", - " # 1. Create a new HTML structure with a centered div wrapper\n", - " chart_html = f\"\"\"\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " {chart_html_body}\n", - " \n", - " \n", - " \"\"\"\n", - "\n", - " # 2. Create an IFrame with the chart HTML and set a fixed size for the viewport\n", - " iframe = folium.IFrame(chart_html, width=600, height=450)\n", - "\n", - " # 3. Add the IFrame to a Popup\n", + " # --- ADDING MARKERS TO FOLIUM ---\n", + "\n", + " # **Marker 1: For the first 15 plots**\n", + " if charts_group_1:\n", + " combined_chart_1 = alt.vconcat(*charts_group_1).resolve_scale(color='independent')\n", + " chart_html_body = combined_chart_1.to_html()\n", + " \n", + " # Simple HTML wrapper for the popup\n", + " popup_html = f\"\"\"\n", + " {chart_html_body}\n", + " \"\"\"\n", + "\n", + " iframe = folium.IFrame(popup_html, width=600, height=450)\n", " popup = folium.Popup(iframe, max_width=600)\n", + " icon = folium.features.CustomIcon(icon_url, icon_size=(28, 30))\n", + " \n", + " if len(charts_group_2)>0:\n", + " folium.Marker(\n", + " location=[lat, lon],\n", + " icon=icon,\n", + " tooltip=f\"{platform_name} (Depth levels 1-15)\", \n", + " popup=popup\n", + " ).add_to(m)\n", + " else:\n", + " folium.Marker(\n", + " location=[lat, lon],\n", + " icon=icon,\n", + " tooltip=f\"{platform_name}\", \n", + " popup=popup\n", + " ).add_to(m) \n", + "\n", + " # **Marker 2: For the following set of plots, if they exist**\n", + " if charts_group_2:\n", + " combined_chart_2 = alt.vconcat(*charts_group_2).resolve_scale(color='independent')\n", + " chart_html_body = combined_chart_2.to_html()\n", + " \n", + " popup_html = f\"\"\"\n", + " {chart_html_body}\n", + " \"\"\"\n", "\n", + " iframe = folium.IFrame(popup_html, width=600, height=450)\n", + " popup = folium.Popup(iframe, max_width=600)\n", " icon = folium.features.CustomIcon(icon_url, icon_size=(28, 30))\n", " \n", + " # Add a small offset to the longitude to make the second marker clickable\n", + " lon_offset = 0.1 \n", + " \n", " folium.Marker(\n", - " location=[lat, lon],\n", + " location=[lat, lon + lon_offset],\n", " icon=icon,\n", - " tooltip=platform_name,\n", + " tooltip=f\"{platform_name} (Depth levels 16+)\",\n", " popup=popup\n", " ).add_to(m)\n", "\n", + " # **Marker 3: For the following set of plots, if they exist**\n", + " if charts_group_3:\n", + " combined_chart_3 = alt.vconcat(*charts_group_3).resolve_scale(color='independent')\n", + " chart_html_body = combined_chart_3.to_html()\n", + " \n", + " popup_html = f\"\"\"\n", + " {chart_html_body}\n", + " \"\"\"\n", + "\n", + " iframe = folium.IFrame(popup_html, width=600, height=450)\n", + " popup = folium.Popup(iframe, max_width=600)\n", + " icon = folium.features.CustomIcon(icon_url, icon_size=(28, 30))\n", + " \n", + " # Add a small offset to the longitude to make the second marker clickable\n", + " lon_offset = 0.1 \n", + " \n", + " folium.Marker(\n", + " location=[lat, lon + lon_offset*2],\n", + " icon=icon,\n", + " tooltip=f\"{platform_name} (Depth levels 31+)\",\n", + " popup=popup\n", + " ).add_to(m) \n", + "\n", + "\n", "# --- MAP FINALIZATION ---\n", "folium.TileLayer(\n", " tiles='https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}',\n", @@ -907,7 +1029,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.18" + "version": "3.13.7" } }, "nbformat": 4,