{ "cells": [ { "cell_type": "markdown", "metadata": { "id": "NX9jAdiRtl1T" }, "source": [ "# Biologically Informed Climate Insights Through Animal-Borne Sensors" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As climate change reshapes ecosystems, more precise and ecologically relevant measurements are needed. Traditional climate data are often limited by static, coarse, and sparse sampling, with indirect links to ecological impacts.\n", "The MEOP consortium (MEOP stands for \"Marine Mammals Exploring the Oceans Pole to Pole\") brings together several national programmes to produce a comprehensive quality-controlled database of oceanographic data obtained in Polar Regions from instrumented marine mammals. Animal-borne sensors offer fine-scale, biologically tuned measurements of climatic conditions, enhancing ecological and climate forecasting. Millions of meteorological observations from over a thousand species have already been collected using these sensors.\n", "This notebok explores how this growing dataset can bridge gaps in biodiversity and climate science, particularly in terrestrial environments, positioning tagged animals as key environmental sentinels and data providers for understanding changing ecosystems." ] }, { "cell_type": "markdown", "metadata": { "id": "d_TZpFDhcv_e" }, "source": [ "The tool uses the following product:\n", "\n", "- OCEAN:ICE's ERDDAP's dataset (https://er1.s4oceanice.eu/erddap/tabledap/MEOP_Animal-borne_profiles.html)" ] }, { "cell_type": "code", "execution_count": 19, "metadata": { "cellView": "form", "id": "AJd4f6j15tLu", "tags": [ "remove-cell" ] }, "outputs": [], "source": [ "# @title\n", "%%capture\n", "!pip install cartopy\n", "\n", "from ipywidgets import Dropdown, Text, Output, Layout, interactive\n", "from IPython.display import HTML, clear_output\n", "from matplotlib.animation import FuncAnimation\n", "from matplotlib import colors as mcolors\n", "from matplotlib.cm import ScalarMappable\n", "from io import BytesIO\n", "import cartopy.crs as ccrs\n", "import cartopy.feature as cfeature\n", "import matplotlib.pyplot as plt\n", "import matplotlib.collections as mcoll\n", "import numpy as np\n", "import pandas as pd\n", "import requests\n", "import datetime\n", "import warnings\n", "import time\n", "\n", "warnings.filterwarnings('ignore')" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "cellView": "form", "colab": { "base_uri": "https://localhost:8080/", "height": 81, "referenced_widgets": [ "4616d5e1926448118f5922e2b033099e", "3ec70f2699d44ef19d7ac598c949e3a2", "802261ac79e347bc9293b18a5c29ca6a", "9e4fcfab0d844c7d963dafc4cec6d88f", "4488d779061648b4af89cbee3915442c", "5bc2e13bc5384c89956034cd302ce98d", "9da6af65105c49c78391164fba9c2b96", "3408160c6ade449b8ae3db9af5238190", "034bba4ac4864fac9ce2ef96593981cf", "a1f2b1f1dafc4837b97915ef8c096489" ] }, "id": "pG0-cFae7tfi", "outputId": "c3db0b0d-772d-47aa-f24e-f847431df530", "tags": [ "hide-input", "remove-cell" ] }, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "4616d5e1926448118f5922e2b033099e", "version_major": 2, "version_minor": 0 }, "text/plain": [ "interactive(children=(Text(value='2020-01-01', description='Start Date:', placeholder='2020-01-01'), Text(valu…" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "# # @title\n", "# start_date = datetime.datetime.strptime('2020-01-01', \"%Y-%m-%d\").strftime(\"%Y-%m-%dT00:00:00Z\")\n", "# end_date = datetime.datetime.strptime('2020-02-05', \"%Y-%m-%d\").strftime(\"%Y-%m-%dT00:00:00Z\")\n", "\n", "# start_date_input = Text(\n", "# value='2020-01-01',\n", "# placeholder='2020-01-01',\n", "# description='Start Date:',\n", "# disabled=False\n", "# )\n", "\n", "# end_date_input = Text(\n", "# value='2020-02-05',\n", "# placeholder='2020-02-05',\n", "# description='End Date:',\n", "# disabled=False\n", "# )\n", "\n", "# def display_date_range(start, end):\n", "# global start_date\n", "# global end_date\n", "# try:\n", "# start_date = datetime.datetime.strptime(start, \"%Y-%m-%d\")\n", "# end_date = datetime.datetime.strptime(end, \"%Y-%m-%d\")\n", "# if start_date <= end_date:\n", "# start_date = start_date.strftime(\"%Y-%m-%dT00:00:00Z\")\n", "# end_date = end_date.strftime(\"%Y-%m-%dT00:00:00Z\")\n", "# except ValueError:\n", "# pass\n", "\n", "# interactive_widget = interactive(display_date_range, start=start_date_input, end=end_date_input)\n", "\n", "# display(interactive_widget)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Data gathering" ] }, { "cell_type": "markdown", "metadata": { "id": "SG4AUt1bic2F" }, "source": [ "The following code cell will download a list of platforms with data satisfying the time range chosen for this example (2019-12-01 to 2020-03-10) and with latitude less or equal than -55. This list of platforms is then displayed below." ] }, { "cell_type": "code", "execution_count": 25, "metadata": { "cellView": "form", "colab": { "base_uri": "https://localhost:8080/", "height": 424 }, "id": "O4rgCk8i9xzC", "outputId": "d4b3e79f-145d-4453-9154-e9d41dfcad57", "tags": [ "hide-input" ] }, "outputs": [ { "data": { "application/vnd.google.colaboratory.intrinsic+json": { "summary": "{\n \"name\": \"df\",\n \"rows\": 67,\n \"fields\": [\n {\n \"column\": \"platform_code\",\n \"properties\": {\n \"dtype\": \"string\",\n \"num_unique_values\": 67,\n \"samples\": [\n \"ct156-234-VERT-20\",\n \"89346\",\n \"87900\"\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n }\n ]\n}", "type": "dataframe", "variable_name": "df" }, "text/html": [ "\n", "
\n", "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
platform_code
185848
286562
386580
487888
587900
......
63wd13-420BAT-16
64wd13-765-18
65wd13-880-18
66wd13-910-18
67wd13-911-18
\n", "

67 rows × 1 columns

\n", "
\n", "
\n", "\n", "
\n", " \n", "\n", " \n", "\n", " \n", "
\n", "\n", "\n", "
\n", " \n", "\n", "\n", "\n", " \n", "
\n", "\n", "
\n", " \n", " \n", " \n", "
\n", "\n", "
\n", "
\n" ], "text/plain": [ " platform_code\n", "1 85848\n", "2 86562\n", "3 86580\n", "4 87888\n", "5 87900\n", ".. ...\n", "63 wd13-420BAT-16\n", "64 wd13-765-18\n", "65 wd13-880-18\n", "66 wd13-910-18\n", "67 wd13-911-18\n", "\n", "[67 rows x 1 columns]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "# @title\n", "max_latitude = -55\n", "all_platforms_south_url = f'https://er1.s4oceanice.eu/erddap/tabledap/MEOP_Animal-borne_profiles.csv?platform_code&latitude%3C={max_latitude}&time%3E={start_date}&time%3C={end_date}&distinct()'\n", "resp = requests.get(all_platforms_south_url)\n", "df = pd.read_csv(BytesIO(resp.content), header=0, encoding='utf-8', dtype={'platform_code': str})\n", "df = df.iloc[1:]\n", "display(df)" ] }, { "cell_type": "markdown", "metadata": { "id": "jP9lIdGpi2AE" }, "source": [ "In the next code cell the CTD data for the selected platforms will be gathered and displayed." ] }, { "cell_type": "code", "execution_count": 26, "metadata": { "cellView": "form", "colab": { "base_uri": "https://localhost:8080/", "height": 424 }, "id": "0WNig5bu-pqb", "outputId": "c19594f9-a722-46fc-8600-77bcd896fc15", "tags": [ "hide-input" ] }, "outputs": [ { "data": { "application/vnd.google.colaboratory.intrinsic+json": { "summary": "{\n \"name\": \"data_df\",\n \"rows\": 2198,\n \"fields\": [\n {\n \"column\": \"platform_code\",\n \"properties\": {\n \"dtype\": \"category\",\n \"num_unique_values\": 67,\n \"samples\": [\n \"ct156-234-VERT-20\",\n \"89346\",\n \"87900\"\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"time\",\n \"properties\": {\n \"dtype\": \"date\",\n \"min\": \"2019-12-01\",\n \"max\": \"2020-03-09\",\n \"num_unique_values\": 100,\n \"samples\": [\n \"2020-01-07\",\n \"2020-02-15\",\n \"2020-03-04\"\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"TEMP\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 1.7173014379388045,\n \"min\": -2.0234273898305086,\n \"max\": 5.2624266138297875,\n \"num_unique_values\": 2194,\n \"samples\": [\n -1.176653164551282,\n 3.8493387096774194,\n -1.25540625\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"PSAL\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 0.45668274830713473,\n \"min\": 31.869,\n \"max\": 37.0395625,\n \"num_unique_values\": 2157,\n \"samples\": [\n 34.049458333333334,\n 33.950546875,\n 33.94980777777778\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"latitude\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 7.415292495521475,\n \"min\": -78.186,\n \"max\": -48.68273316593308,\n \"num_unique_values\": 2195,\n \"samples\": [\n -67.38295306856192,\n -49.79646612903226,\n -66.375275\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"longitude\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 33.6470142391187,\n \"min\": 29.99235,\n \"max\": 168.5037,\n \"num_unique_values\": 2193,\n \"samples\": [\n 79.49198571428572,\n 74.85501344537815,\n 166.76793333333333\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n }\n ]\n}", "type": "dataframe", "variable_name": "data_df" }, "text/html": [ "\n", "
\n", "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
platform_codetimeTEMPPSALlatitudelongitude
0858482019-12-011.09790034.331646-54.65168991.141638
1858482019-12-021.07839934.341045-54.53826491.280233
2858482019-12-031.18184934.219489-54.33916391.447827
3858482019-12-041.35366434.336993-54.32504891.865526
4858482019-12-051.66800634.324731-54.65016891.985747
.....................
2193wd13-911-182019-12-28-1.67300934.289086-66.050900142.225500
2194wd13-911-182019-12-29-1.59023634.284033-66.001992142.056821
2195wd13-911-182019-12-30-1.59941434.272934-66.004350141.929450
2196wd13-911-182019-12-31-1.72042134.239960-65.997523141.830023
2197wd13-911-182020-01-01-1.72247534.183049-66.084431142.192408
\n", "

2198 rows × 6 columns

\n", "
\n", "
\n", "\n", "
\n", " \n", "\n", " \n", "\n", " \n", "
\n", "\n", "\n", "
\n", " \n", "\n", "\n", "\n", " \n", "
\n", "\n", "
\n", " \n", " \n", " \n", "
\n", "\n", "
\n", "
\n" ], "text/plain": [ " platform_code time TEMP PSAL latitude longitude\n", "0 85848 2019-12-01 1.097900 34.331646 -54.651689 91.141638\n", "1 85848 2019-12-02 1.078399 34.341045 -54.538264 91.280233\n", "2 85848 2019-12-03 1.181849 34.219489 -54.339163 91.447827\n", "3 85848 2019-12-04 1.353664 34.336993 -54.325048 91.865526\n", "4 85848 2019-12-05 1.668006 34.324731 -54.650168 91.985747\n", "... ... ... ... ... ... ...\n", "2193 wd13-911-18 2019-12-28 -1.673009 34.289086 -66.050900 142.225500\n", "2194 wd13-911-18 2019-12-29 -1.590236 34.284033 -66.001992 142.056821\n", "2195 wd13-911-18 2019-12-30 -1.599414 34.272934 -66.004350 141.929450\n", "2196 wd13-911-18 2019-12-31 -1.720421 34.239960 -65.997523 141.830023\n", "2197 wd13-911-18 2020-01-01 -1.722475 34.183049 -66.084431 142.192408\n", "\n", "[2198 rows x 6 columns]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "# @title\n", "all_data_traj = []\n", "all_data_depth = []\n", "\n", "for platform in df['platform_code']:\n", " platform_data_url = f'https://er1.s4oceanice.eu/erddap/tabledap/MEOP_Animal-borne_profiles.csv?platform_code%2Ctime%2CPRES%2CPSAL%2Clatitude%2Clongitude%2CTEMP&platform_code=%22{platform}%22&time%3E={start_date}&time%3C={end_date}'\n", " resp = requests.get(platform_data_url)\n", "\n", " if \"Not Found: Your query produced no matching results. (nRows = 0)\" in resp.text:\n", " continue\n", "\n", " df_platform = pd.read_csv(BytesIO(resp.content), encoding='utf-8', low_memory=False, dtype=str)\n", " df_platform = df_platform.iloc[1:]\n", " df_platform_filtered = df_platform.dropna(subset=['TEMP'])\n", "\n", " df_platform_temp = pd.DataFrame()\n", "\n", " df_platform_temp['PSAL'] = pd.to_numeric(df_platform_filtered['PSAL'], errors='coerce')\n", " df_platform_temp['PRES'] = pd.to_numeric(df_platform_filtered['PRES'], errors='coerce')\n", " df_platform_temp['TEMP'] = pd.to_numeric(df_platform_filtered['TEMP'], errors='coerce')\n", " df_platform_temp['latitude'] = pd.to_numeric(df_platform_filtered['latitude'], errors='coerce')\n", " df_platform_temp['longitude'] = pd.to_numeric(df_platform_filtered['longitude'], errors='coerce')\n", "\n", " df_platform_temp['time'] = pd.to_datetime(df_platform_filtered['time'], format='%Y-%m-%dT%H:%M:%SZ', errors='coerce')\n", " df_platform_temp['platform_code'] = df_platform_filtered['platform_code']\n", "\n", " df_platform_filtered = df_platform_temp\n", "\n", " df_daily_avg = df_platform_filtered.groupby([df_platform_filtered['platform_code'], df_platform_filtered['time'].dt.date]).agg({\n", " 'TEMP': 'mean',\n", " 'PSAL': 'mean',\n", " 'latitude': 'mean',\n", " 'longitude': 'mean'\n", " }).reset_index()\n", "\n", " all_data_traj.append(df_daily_avg)\n", " all_data_depth.append(df_platform_filtered)\n", "\n", "if all_data_traj:\n", " data_df = pd.concat(all_data_traj, ignore_index=True)\n", " display(data_df)\n", "else:\n", " print(\"No valid data found across platforms.\")" ] }, { "cell_type": "code", "execution_count": 36, "metadata": { "cellView": "form", "colab": { "base_uri": "https://localhost:8080/", "height": 49, "referenced_widgets": [ "ab3a3f92d86d42559e7c9c43d41c3180", "b51f510f85494268a38ad99c0d1fff6f", "d631b9d9d13d41a3a0cb45458f256c7c" ] }, "id": "1mqmZtiCnXFK", "outputId": "a45e52c9-b711-4f4a-8d47-1083e5bca2da", "tags": [ "remove-cell" ] }, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "ab3a3f92d86d42559e7c9c43d41c3180", "version_major": 2, "version_minor": 0 }, "text/plain": [ "Dropdown(description='Select the parameter:', layout=Layout(width='300px'), options=('Sea Water Temperature', …" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "# @title\n", "# selected_param = 'Sea Water Temperature'\n", "\n", "# params_dropdown = Dropdown(\n", "# options=['Sea Water Temperature', 'Practical Salinity'],\n", "# value='Sea Water Temperature',\n", "# description='Select the parameter:',\n", "# layout=Layout(width='300px'),\n", "# style={'description_width': 'initial'}\n", "# )\n", "\n", "# def on_change(change):\n", "# global selected_param\n", "# if params_dropdown.value:\n", "# selected_param = params_dropdown.value\n", "\n", "# params_dropdown.observe(on_change, names='value')\n", "\n", "# display(params_dropdown)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Trajectory plot" ] }, { "cell_type": "markdown", "metadata": { "id": "MRKooe17ldXC" }, "source": [ "The next code cell will visualize the trajectories of platforms over time, plotting the Sea Water Temperature for each day. The map is centered on the Southern Hemisphere, and data points will be connected with lines to show the movement of each platform. The color of the lines represents the value for the temperature, with a color bar provided for reference. The animation updates daily, highlighting how the platforms' trajectories evolve over time.\n", "\n", "The data is first filtered to include only valid temperature readings, and then numeric fields such as temperature, salinity, pressure, latitude, and longitude are converted to appropriate types. For each platform and time, only the data from the shallowest depth is selected, ensuring that the temperature, salinity, latitude, and longitude values closest to the surface are used. This selection provides a meaningful surface-level representation. The grouped data is used to plot the daily average trajectory, improving efficiency while maintaining the accuracy of the visualized trends." ] }, { "cell_type": "code", "execution_count": 38, "metadata": { "cellView": "form", "colab": { "base_uri": "https://localhost:8080/", "height": 839 }, "id": "v2UTc8qGbuIg", "outputId": "6df775ca-e7dc-4be7-c4f1-033a2991043f", "tags": [ "hide-input" ] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " " ] }, { "data": { "text/html": [ "" ], "text/plain": [ "" ] }, "execution_count": 38, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# @title\n", "%matplotlib agg\n", "\n", "params_dict = {'Practical Salinity': 'PSAL', 'Sea Water Temperature': 'TEMP'}\n", "param = params_dict[selected_param]\n", "\n", "data_df['TEMP'] = pd.to_numeric(data_df['TEMP'], errors='coerce')\n", "data_df['PSAL'] = pd.to_numeric(data_df['PSAL'], errors='coerce')\n", "data_df['time'] = pd.to_datetime(data_df['time'], format='%Y-%m-%dT%H:%M:%SZ')\n", "data_df['latitude'] = data_df['latitude'].astype(float)\n", "data_df['longitude'] = data_df['longitude'].astype(float)\n", "\n", "\n", "norm = plt.Normalize(vmin=data_df[param].min(), vmax=data_df[param].max())\n", "cmap = plt.get_cmap('coolwarm')\n", "data_df['color'] = data_df[param].apply(lambda x: cmap(norm(x)))\n", "\n", "fig, ax = plt.subplots(figsize=(8, 6), subplot_kw={'projection': ccrs.SouthPolarStereo()})\n", "\n", "ax.add_feature(cfeature.LAND, edgecolor='black')\n", "ax.add_feature(cfeature.OCEAN)\n", "ax.add_feature(cfeature.COASTLINE)\n", "ax.add_feature(cfeature.BORDERS, linestyle=':')\n", "\n", "ax.set_extent([-180, 180, -90, -45], crs=ccrs.PlateCarree())\n", "\n", "sm = ScalarMappable(cmap=cmap, norm=norm)\n", "sm.set_array([])\n", "cbar = fig.colorbar(sm, ax=ax, orientation='vertical', fraction=0.025, pad=0.1)\n", "if param == 'TEMP':\n", " cbar.set_label('Sea Water Temperature (°C)', fontsize=12)\n", "elif param == 'PSAL':\n", " cbar.set_label('Practical Salinity (psu)', fontsize=12)\n", "\n", "unique_days = sorted(data_df['time'].dt.date.unique())\n", "\n", "status_text = ax.text(-170, -50, '', fontsize=12, color='black', transform=ccrs.PlateCarree())\n", "plt.close()\n", "\n", "def update_plot(day):\n", " day_check = day.strftime(\"%Y-%m-%dT00:00:00Z\")\n", " if day_check == end_date or day >= unique_days[-2]:\n", " print('\\r'+' '*50, end='', flush=True)\n", " else:\n", " print(f'\\rUpdating for day: {day}', end='', flush=True)\n", "\n", " ax.clear()\n", " ax.add_feature(cfeature.LAND, edgecolor='black')\n", " ax.add_feature(cfeature.OCEAN)\n", " ax.add_feature(cfeature.COASTLINE)\n", " ax.add_feature(cfeature.BORDERS, linestyle=':')\n", " ax.set_extent([-180, 180, -90, -45], crs=ccrs.PlateCarree())\n", "\n", " current_data = data_df[data_df['time'].dt.date <= day]\n", "\n", " status_text.set_text(f'Current Day: {day}, Data Points: {len(current_data)}')\n", "\n", " for platform in current_data['platform_code'].unique():\n", " platform_data = current_data[current_data['platform_code'] == platform]\n", "\n", " for i in range(len(platform_data) - 1):\n", " ax.plot([platform_data.iloc[i]['longitude'], platform_data.iloc[i + 1]['longitude']],\n", " [platform_data.iloc[i]['latitude'], platform_data.iloc[i + 1]['latitude']],\n", " color=platform_data.iloc[i]['color'], marker='.', linewidth=2.5, transform=ccrs.PlateCarree(), alpha=0.7)\n", "\n", " ax.set_title(f'Trajectories of Platforms - Day: {day}')\n", " plt.draw()\n", "\n", "ani = FuncAnimation(fig, update_plot, frames=unique_days, interval=250, repeat=True)\n", "HTML(ani.to_html5_video())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Pressure plot" ] }, { "cell_type": "markdown", "metadata": { "id": "0EcphTohs2gd" }, "source": [ "The next code cell will display the DataFrame containing the data used for the final plot, which analyzes the Sea Water Temperature based on depth." ] }, { "cell_type": "code", "execution_count": 30, "metadata": { "cellView": "form", "colab": { "base_uri": "https://localhost:8080/", "height": 424 }, "id": "ws7qscJrHYx_", "outputId": "e0a5cc7a-db32-4989-fe90-6a2a01d9b81b", "tags": [ "hide-input" ] }, "outputs": [ { "data": { "application/vnd.google.colaboratory.intrinsic+json": { "type": "dataframe", "variable_name": "data_df_pres_concat" }, "text/html": [ "\n", "
\n", "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
PSALPRESTEMPlatitudelongitudetimeplatform_code
034.1846351.01.857105-54.72131691.09632019-12-01 04:33:5785848
134.1846352.01.857105-54.72131691.09632019-12-01 04:33:5785848
234.1846353.01.857105-54.72131691.09632019-12-01 04:33:5785848
334.1814204.01.846964-54.72131691.09632019-12-01 04:33:5785848
434.1754955.01.828237-54.72131691.09632019-12-01 04:33:5785848
........................
73559934.493000126.0-1.781000-66.026900142.27412020-01-01 22:10:00wd13-911-18
73560034.503000150.0-1.731000-66.026900142.27412020-01-01 22:10:00wd13-911-18
73560134.520000170.0-1.840000-66.026900142.27412020-01-01 22:10:00wd13-911-18
73560234.528000200.0-1.829000-66.026900142.27412020-01-01 22:10:00wd13-911-18
73560334.530000210.0-1.828000-66.026900142.27412020-01-01 22:10:00wd13-911-18
\n", "

735604 rows × 7 columns

\n", "
\n", "
\n", "\n", "
\n", " \n", "\n", " \n", "\n", " \n", "
\n", "\n", "\n", "
\n", " \n", "\n", "\n", "\n", " \n", "
\n", "\n", "
\n", " \n", " \n", " \n", "
\n", "\n", "
\n", "
\n" ], "text/plain": [ " PSAL PRES TEMP latitude longitude time \\\n", "0 34.184635 1.0 1.857105 -54.721316 91.0963 2019-12-01 04:33:57 \n", "1 34.184635 2.0 1.857105 -54.721316 91.0963 2019-12-01 04:33:57 \n", "2 34.184635 3.0 1.857105 -54.721316 91.0963 2019-12-01 04:33:57 \n", "3 34.181420 4.0 1.846964 -54.721316 91.0963 2019-12-01 04:33:57 \n", "4 34.175495 5.0 1.828237 -54.721316 91.0963 2019-12-01 04:33:57 \n", "... ... ... ... ... ... ... \n", "735599 34.493000 126.0 -1.781000 -66.026900 142.2741 2020-01-01 22:10:00 \n", "735600 34.503000 150.0 -1.731000 -66.026900 142.2741 2020-01-01 22:10:00 \n", "735601 34.520000 170.0 -1.840000 -66.026900 142.2741 2020-01-01 22:10:00 \n", "735602 34.528000 200.0 -1.829000 -66.026900 142.2741 2020-01-01 22:10:00 \n", "735603 34.530000 210.0 -1.828000 -66.026900 142.2741 2020-01-01 22:10:00 \n", "\n", " platform_code \n", "0 85848 \n", "1 85848 \n", "2 85848 \n", "3 85848 \n", "4 85848 \n", "... ... \n", "735599 wd13-911-18 \n", "735600 wd13-911-18 \n", "735601 wd13-911-18 \n", "735602 wd13-911-18 \n", "735603 wd13-911-18 \n", "\n", "[735604 rows x 7 columns]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "# @title\n", "if all_data_depth:\n", " data_df_pres_concat = pd.concat(all_data_depth, ignore_index=True)\n", " display(data_df_pres_concat)\n", "else:\n", " print(\"No valid data found across platforms.\")" ] }, { "cell_type": "markdown", "metadata": { "id": "Crgqai2frwyz" }, "source": [ "The last code cell will visualize platform data at different pressure levels over time. The data points for each pressure level (binned in increments of 10 dbar) are plotted based on the Sea Water Temperature. The color of the data points represents the value of the selected parameter, with a color bar provided for reference.\n", "\n", "This plot complements the previous trajectory plot, allowing users to visualize another fundamental component of Conductivity-Temperature-Depth (CTD) profiles. Averaging is applied to the tempearture. A second depth scale on the left visualizes the pressure levels, and the animation updates the map by showing data points for different pressure bins, enabling users to observe how conditions change with depth." ] }, { "cell_type": "code", "execution_count": 39, "metadata": { "cellView": "form", "colab": { "base_uri": "https://localhost:8080/", "height": 856 }, "id": "0mXQaquYrXsn", "outputId": "5f1a6c43-32f5-4111-a59e-b376da9ce8b6", "tags": [ "hide-input" ] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " " ] }, { "data": { "text/html": [ "" ], "text/plain": [ "" ] }, "execution_count": 39, "metadata": {}, "output_type": "execute_result" }, { "name": "stdout", "output_type": "stream", "text": [ "\r " ] } ], "source": [ "# @title\n", "pres_bin_size = 10\n", "\n", "data_df_pres = data_df_pres_concat.copy()\n", "\n", "norm = plt.Normalize(vmin=data_df[param].min(), vmax=data_df[param].max())\n", "\n", "data_df_pres['PRES'] = data_df_pres['PRES'].astype(float)\n", "data_df_pres['TEMP'] = data_df_pres['TEMP'].astype(float)\n", "data_df_pres['PSAL'] = data_df_pres['PSAL'].astype(float)\n", "data_df_pres['time'] = pd.to_datetime(data_df_pres['time'])\n", "\n", "depth_bins = np.arange(0, data_df_pres['PRES'].max() + pres_bin_size, pres_bin_size)\n", "data_df_pres['PRES'] = pd.cut(data_df_pres['PRES'], bins=depth_bins, labels=depth_bins[:-1], right=False)\n", "data_df_pres['PRES'] = data_df_pres['PRES'].astype(float)\n", "\n", "numeric_columns = ['PSAL', 'TEMP']\n", "grouped_df = data_df_pres.groupby(['time', 'PRES']).agg({\n", " 'latitude': 'first',\n", " 'longitude': 'first',\n", " 'PSAL': 'mean',\n", " 'TEMP': 'mean'\n", "}).reset_index()\n", "\n", "param = params_dict[selected_param]\n", "\n", "cmap = plt.get_cmap('coolwarm')\n", "data_df_pres['color'] = data_df_pres[param].apply(lambda x: cmap(norm(x)))\n", "\n", "fig, ax = plt.subplots(figsize=(8, 6), subplot_kw={'projection': ccrs.SouthPolarStereo()})\n", "\n", "ax.add_feature(cfeature.LAND, edgecolor='black')\n", "ax.add_feature(cfeature.OCEAN)\n", "ax.add_feature(cfeature.COASTLINE)\n", "ax.add_feature(cfeature.BORDERS, linestyle=':')\n", "ax.set_extent([-180, 180, -90, -45], crs=ccrs.PlateCarree())\n", "\n", "sm = ScalarMappable(cmap=cmap, norm=norm)\n", "sm.set_array([])\n", "cbar = fig.colorbar(sm, ax=ax, orientation='vertical', fraction=0.025, pad=0.1)\n", "if param == 'TEMP':\n", " cbar.set_label('Sea Water Temperature (°C)', fontsize=12)\n", "elif param == 'PSAL':\n", " cbar.set_label('Practical Salinity (psu)', fontsize=12)\n", "\n", "unique_pres = sorted(data_df_pres['PRES'].unique())\n", "\n", "ax_depth = fig.add_axes([0.1, 0.1, 0.03, 0.8])\n", "ax_depth.set_ylim(0, data_df_pres['PRES'].max())\n", "ax_depth.invert_yaxis()\n", "ax_depth.set_xticks([])\n", "ax_depth.set_yticks(np.arange(0, data_df_pres['PRES'].max() + 100, 100))\n", "ax_depth.set_ylabel('Pressure (dbar)', fontsize=12)\n", "\n", "ax_depth.plot([0.5, 0.5], [0, data_df_pres['PRES'].max()], color='black', lw=2)\n", "\n", "highlight_bin = ax_depth.scatter([], [], color='black', s=100, zorder=5)\n", "\n", "def update_plot(pres):\n", " if pres >= sorted(list(data_df_pres['PRES']))[-1] or pres <= sorted(list(data_df_pres['PRES']))[0]:\n", " print('\\r' + ' ' * 50, end='', flush=True)\n", " else:\n", " print(f'\\rUpdating for pressure: {int(pres)}/{int(unique_pres[-1])}', end='', flush=True)\n", "\n", " ax.clear()\n", " ax.add_feature(cfeature.LAND, edgecolor='black')\n", " ax.add_feature(cfeature.OCEAN)\n", " ax.add_feature(cfeature.COASTLINE)\n", " ax.add_feature(cfeature.BORDERS, linestyle=':')\n", " ax.set_extent([-180, 180, -90, -45], crs=ccrs.PlateCarree())\n", "\n", " current_data = data_df_pres[data_df_pres['PRES'] == pres]\n", "\n", " ax.scatter(current_data['longitude'], current_data['latitude'],\n", " c=current_data['color'], transform=ccrs.PlateCarree(), alpha=0.7, s=10)\n", "\n", " ax.set_title(f'Pressure Level: {int(pres)} dbar')\n", "\n", " highlight_bin.set_offsets([[0.5, pres]])\n", " plt.draw()\n", "\n", "ani = FuncAnimation(fig, update_plot, frames=unique_pres, interval=500, repeat=True)\n", "\n", "HTML(ani.to_html5_video())" ] }, { "cell_type": "markdown", "metadata": { "id": "gJWy-J3VtYY7" }, "source": [ "### Additional resources" ] }, { "cell_type": "markdown", "metadata": { "id": "NiuVKwnetaJm" }, "source": [ "The Python libraries that have been used in this notebook are:\n", "- [cartopy](https://scitools.org.uk/cartopy/docs/latest/)\n", "- [ipywidgets](https://ipywidgets.readthedocs.io/en/stable/)\n", "- [requests](https://requests.readthedocs.io/en/latest/)\n", "- [numpy](https://numpy.org/)\n", "- [pandas](https://pandas.pydata.org/)\n", "- [matplotlib](https://matplotlib.org/)" ] } ], "metadata": { "colab": { "provenance": [] }, "kernelspec": { "display_name": "Python 3", "name": "python3" }, "language_info": { "name": "python" }, "widgets": { "application/vnd.jupyter.widget-state+json": { "034bba4ac4864fac9ce2ef96593981cf": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": { "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": "" } }, "3408160c6ade449b8ae3db9af5238190": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": { "_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null } }, "3ec70f2699d44ef19d7ac598c949e3a2": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "TextModel", "state": { "_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "TextModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "TextView", "continuous_update": true, "description": "Start Date:", "description_tooltip": null, "disabled": false, "layout": "IPY_MODEL_5bc2e13bc5384c89956034cd302ce98d", "placeholder": "2020-01-01", "style": "IPY_MODEL_9da6af65105c49c78391164fba9c2b96", "value": "2019-12-01" } }, "4488d779061648b4af89cbee3915442c": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": { "_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null } }, "4616d5e1926448118f5922e2b033099e": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "VBoxModel", "state": { "_dom_classes": [ "widget-interact" ], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "VBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "VBoxView", "box_style": "", "children": [ "IPY_MODEL_3ec70f2699d44ef19d7ac598c949e3a2", "IPY_MODEL_802261ac79e347bc9293b18a5c29ca6a", "IPY_MODEL_9e4fcfab0d844c7d963dafc4cec6d88f" ], "layout": "IPY_MODEL_4488d779061648b4af89cbee3915442c" } }, "5bc2e13bc5384c89956034cd302ce98d": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": { "_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null } }, "802261ac79e347bc9293b18a5c29ca6a": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "TextModel", "state": { "_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "TextModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "TextView", "continuous_update": true, "description": "End Date:", "description_tooltip": null, "disabled": false, "layout": "IPY_MODEL_3408160c6ade449b8ae3db9af5238190", "placeholder": "2020-02-05", "style": "IPY_MODEL_034bba4ac4864fac9ce2ef96593981cf", "value": "2020-03-10" } }, "9da6af65105c49c78391164fba9c2b96": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": { "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": "" } }, "9e4fcfab0d844c7d963dafc4cec6d88f": { "model_module": "@jupyter-widgets/output", "model_module_version": "1.0.0", "model_name": "OutputModel", "state": { "_dom_classes": [], "_model_module": "@jupyter-widgets/output", "_model_module_version": "1.0.0", "_model_name": "OutputModel", "_view_count": null, "_view_module": "@jupyter-widgets/output", "_view_module_version": "1.0.0", "_view_name": "OutputView", "layout": "IPY_MODEL_a1f2b1f1dafc4837b97915ef8c096489", "msg_id": "", "outputs": [] } }, "a1f2b1f1dafc4837b97915ef8c096489": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": { "_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null } }, "ab3a3f92d86d42559e7c9c43d41c3180": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DropdownModel", "state": { "_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DropdownModel", "_options_labels": [ "Sea Water Temperature", "Practical Salinity" ], "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "DropdownView", "description": "Select the parameter:", "description_tooltip": null, "disabled": false, "index": 0, "layout": "IPY_MODEL_b51f510f85494268a38ad99c0d1fff6f", "style": "IPY_MODEL_d631b9d9d13d41a3a0cb45458f256c7c" } }, "b51f510f85494268a38ad99c0d1fff6f": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": { "_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "300px" } }, "d631b9d9d13d41a3a0cb45458f256c7c": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": { "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": "initial" } } } } }, "nbformat": 4, "nbformat_minor": 0 }