diff --git a/ICON/buoy_icon_2.png b/ICON/buoy_icon_2.png new file mode 100644 index 0000000..8625814 Binary files /dev/null and b/ICON/buoy_icon_2.png differ 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 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,