Spaces:
Sleeping
Sleeping
| import io | |
| import math | |
| # date=dataset['TIME'] | |
| from datetime import date, datetime, timedelta | |
| import cartopy.crs as ccrs | |
| import cartopy.feature as cfeature | |
| import cftime | |
| import fsspec | |
| import hvplot.xarray | |
| import matplotlib.pyplot as plt | |
| import numpy as np | |
| import pandas as pd | |
| import panel as pn | |
| import xarray as xr | |
| from ipywidgets import ( | |
| Checkbox, | |
| Dropdown, | |
| FloatRangeSlider, | |
| FloatSlider, | |
| IntRangeSlider, | |
| IntSlider, | |
| interactive, | |
| ) | |
| pn.extension() | |
| #df = pd.read_csv("table.csv", index_col=None) | |
| #df = df.sort_values(by="year") | |
| #df2 = pd.read_csv("test_table.csv", index_col=None) | |
| #file_names = df["file_name"].tolist() | |
| #df = df.sort_values(by="year") | |
| #years = sorted(df["year"].unique()) | |
| def greg_0h(jourjul): | |
| # Julian days start and end at noon. | |
| # Julian day 2440000 begins at 00 hours, May 23, 1968. | |
| # Round the input Julian day number to avoid precision errors | |
| fac = 10**9 | |
| jourjul = np.round(fac * jourjul + 0.5) / fac | |
| # Calculate seconds in the day | |
| secs = (jourjul % 1) * 24 * 3600 | |
| # Round seconds to avoid precision errors | |
| secs = np.round(fac * secs + 0.5) / fac | |
| # Gregorian calendar conversion | |
| j = math.floor(jourjul) - 1721119 | |
| in_ = 4 * j - 1 | |
| y = math.floor(in_ / 146097) | |
| j = in_ - 146097 * y | |
| in_ = math.floor(j / 4) | |
| in_ = 4 * in_ + 3 | |
| j = math.floor(in_ / 1461) | |
| d = math.floor(((in_ - 1461 * j) + 4) / 4) | |
| in_ = 5 * d - 3 | |
| m = math.floor(in_ / 153) | |
| d = math.floor(((in_ - 153 * m) + 5) / 5) | |
| y = y * 100 + j | |
| if m < 10: | |
| mo = m + 3 | |
| yr = y | |
| else: | |
| mo = m - 9 | |
| yr = y + 1 | |
| hour = math.floor(secs / 3600) | |
| mins = math.floor((secs % 3600) / 60) | |
| sec = int(secs % 60) | |
| gtime = [yr, mo, d, hour, mins, sec] | |
| return gtime | |
| def fix_time(dataset): | |
| from datetime import datetime, timedelta | |
| time = dataset["TIME"] | |
| time2 = time.dropna(dim="MAXT") | |
| time2 = time2.reindex_like(time, method='nearest') | |
| jourjul = [greg_0h(jourjul) for jourjul in time2.values] | |
| date = [ | |
| datetime(jourjul[0], jourjul[1], jourjul[2], jourjul[3], jourjul[4], jourjul[5]) | |
| for jourjul in jourjul | |
| ] | |
| return date | |
| from datetime import datetime, timedelta | |
| import cartopy.crs as ccrs | |
| import cartopy.feature as cfeature | |
| import fsspec | |
| import matplotlib.pyplot as plt | |
| import numpy as np | |
| import panel as pn | |
| import xarray as xr | |
| from ipywidgets import ( | |
| Checkbox, | |
| FloatRangeSlider, | |
| FloatSlider, | |
| IntRangeSlider, | |
| IntSlider, | |
| interactive, | |
| ) | |
| # Loaddonnées bathymétry data | |
| fs = fsspec.filesystem("https") | |
| bathy = xr.open_dataset(fs.open('https://data-eurogoship.ifremer.fr/bathymetrie/bathy6min.nc')) | |
| import os | |
| import pandas as pd | |
| import panel as pn | |
| import param | |
| import xarray as xr | |
| class SADCP_Viewer(param.Parameterized): | |
| file_csv='https://data-eurogoship.ifremer.fr/data/table.csv' | |
| file=fs.open(file_csv) | |
| df = pd.read_csv(file, index_col=None).sort_values(by="year") | |
| #df = pd.read_csv("table.csv", index_col=None).sort_values(by="year") | |
| file_names = df["file_name"].tolist() | |
| years = sorted(df["year"].unique()) | |
| year_slider = pn.widgets.IntRangeSlider( | |
| name="Year Range", start=df["year"].min(), end=df["year"].max() | |
| ) | |
| file_dropdown = pn.widgets.Select(name="File Selector") | |
| data_table = pn.widgets.Tabulator(df, name="metadata", height=200, width=300) | |
| # data_table2 = pn.widgets.Tabulator(df2, name="metadata", height=900, width=400) | |
| longitude_slider = pn.widgets.RangeSlider( | |
| name="Longitude Range", start=-180, end=180, step=1 | |
| ) | |
| latitude_slider = pn.widgets.RangeSlider( | |
| name="Latitude Range", start=-90, end=90, step=1 | |
| ) | |
| depth_range_slider = pn.widgets.IntRangeSlider( | |
| start=100, end=300, value=(100, 300), step=1, name="Depth Range" | |
| ) | |
| depth_2_checkbox = pn.widgets.Checkbox(value=False, name="Depth 2 Checkbox") | |
| depth_3_checkbox = pn.widgets.Checkbox(value=False, name="Depth 3 Checkbox") | |
| depth2_range_slider = pn.widgets.IntRangeSlider( | |
| start=100, end=300, value=(100, 300), step=1, name="Depth 2 Range" | |
| ) | |
| depth3_range_slider = pn.widgets.IntRangeSlider( | |
| start=100, end=300, value=(100, 300), step=1, name="Depth 3 Range" | |
| ) | |
| num_vectors_slider = pn.widgets.IntSlider( | |
| start=40, end=800, step=1, value=100, name="Number of Vectors" | |
| ) | |
| scale_factor_slider = pn.widgets.FloatSlider( | |
| start=0.1, end=1, step=0.1, value=0.5, name="Scale Factor" | |
| ) | |
| bathy_checkbox = pn.widgets.Checkbox(value=False, name="Bathy Checkbox") | |
| plot = pn.pane.HoloViews() | |
| plot_map = pn.pane.Matplotlib(width=800, height=600, sizing_mode="fixed") | |
| data_table = pn.widgets.Tabulator( | |
| width=400, height=200 | |
| ) | |
| metadata_table = pn.widgets.Tabulator( | |
| width=600, height=800 | |
| ) | |
| download_button = pn.widgets.Button(name="Download", button_type="primary") | |
| # plot = pn.Column() | |
| def __init__(self, **params): | |
| super(SADCP_Viewer, self).__init__(**params) | |
| self.file_dropdown.objects = self.get_file_list() | |
| self.file_dropdown.value = ( | |
| self.file_dropdown.objects[0] if self.file_dropdown.objects else None | |
| ) | |
| self.update_name_options() | |
| def update_name_options(self): | |
| start_year, end_year = self.year_slider.value | |
| mask = (self.df["year"] >= start_year) & (self.df["year"] <= end_year) | |
| sorted_df = self.df[mask].sort_values(by="year") | |
| files = sorted_df["file_name"].unique().tolist() | |
| self.file_dropdown.options = files | |
| if files: | |
| selected_file = self.file_dropdown.value | |
| if not selected_file or selected_file not in files: | |
| selected_file = files[0] | |
| self.file_dropdown.value = selected_file | |
| dataframe = sorted_df[sorted_df["file_name"] == selected_file].drop( | |
| columns=["file_name", "title", "Conventions", "featureType", "date_update", "ADCP_beam_angle", "ADCP_ship_angle", "middle_bin1_depth", "heading_corr", "pitch_corr", "ampli_corr", "pitch_roll_used", "date_creation", "ADCP_type", "data_type"] | |
| ) | |
| dataframe2 = sorted_df[sorted_df["file_name"] == selected_file].drop( | |
| columns=["file_name","date_start","date_end","ADCP_frequency(kHz)","bin_length(meter)","year"]) | |
| data_dir = "https://data-eurogoship.ifremer.fr/copy_seadatanet/" | |
| file_path = os.path.join(data_dir, selected_file) | |
| file_path=fs.open(file_path) | |
| self.ds = ( | |
| xr.open_dataset( | |
| file_path, decode_cf=True, decode_times=False, engine="scipy" | |
| ) | |
| .squeeze() | |
| .set_coords(["LONGITUDE", "LATITUDE", "TIME", "PROFZ"]) | |
| .set_xindex("PROFZ") | |
| .set_xindex("TIME") | |
| ) | |
| lon_range = ( | |
| int(self.ds["LONGITUDE"].min().round() - 1), | |
| int(self.ds["LONGITUDE"].max().round() + 1), | |
| ) | |
| lat_range = ( | |
| int(self.ds["LATITUDE"].min().round() - 1), | |
| int(self.ds["LATITUDE"].max().round() + 1), | |
| ) | |
| self.depth1 = abs(self.ds.coords["PROFZ"]) | |
| date = fix_time(self.ds) | |
| date = xr.DataArray(date, dims="MAXT") | |
| # self.ds = self.ds.where( | |
| # (self.ds.VCUR_SEADATANET_QC == 49) | |
| # & (self.ds.UCUR_SEADATANET_QC == 49) | |
| # & (self.ds.WCUR_SEADATANET_QC == 49) | |
| # & (self.ds.ECUR_SEADATANET_QC == 49) | |
| # & (self.ds.PGOOD_SEADATANET_QC == 49) | |
| # & (self.ds.ECI_SEADATANET_QC == 49) | |
| # & (self.ds.BOTTOM_DEPTH_SEADATANET_QC == 49), | |
| # drop=True,) | |
| self.ds = self.ds[ | |
| [ | |
| "USHIP", | |
| "VSHIP", | |
| "ROLL", | |
| "PITCH", | |
| "TR_TEMP", | |
| "HEADING", | |
| "U_BOTTOM", | |
| "V_BOTTOM", | |
| "UCUR", | |
| "VCUR", | |
| "WCUR", | |
| "ECUR", | |
| "PGOOD", | |
| "ECI", | |
| "BATHY", | |
| "UTIDE", | |
| "VTIDE", | |
| "BOTTOM_DEPTH", | |
| "TIME", | |
| ] | |
| ] | |
| deph_range = (int(self.depth1.min()), int(self.depth1.max())) | |
| # Ku_std_dataarray = xr.DataArray(Ku_std,dims='dim1') | |
| # date=dataset.assign(TIME=date) | |
| # dataset['TIME']=dataset['TIME'].assign_coords(dim=dataset.dims['MAXT']) | |
| self.ds = self.ds.assign(TIME=date) | |
| self.longitude_slider.start = lon_range[0] | |
| self.longitude_slider.end = lon_range[1] | |
| self.longitude_slider.value = lon_range | |
| self.latitude_slider.start = lat_range[0] | |
| self.latitude_slider.end = lat_range[1] | |
| self.latitude_slider.value = lat_range | |
| self.depth_range_slider.start = deph_range[0] | |
| self.depth_range_slider.end = deph_range[1] | |
| self.depth_range_slider.value = deph_range | |
| self.depth2_range_slider.start = deph_range[0] | |
| self.depth2_range_slider.end = deph_range[1] | |
| self.depth2_range_slider.value = deph_range | |
| self.depth3_range_slider.start = deph_range[0] | |
| self.depth3_range_slider.end = deph_range[1] | |
| self.depth3_range_slider.value = deph_range | |
| # self.update_plots() | |
| self.ds.close() | |
| self.dataframe = dataframe.transpose() | |
| self.dataframe2 = dataframe2.transpose() | |
| # self.transposed_dataframe = dataframe.transpose() | |
| self.data_table.value = self.dataframe | |
| self.metadata_table.value = self.dataframe2 | |
| def update_plots(self): | |
| self.ds_filtered = self.filter_data() | |
| # vector_plot = self.vectors_plot() | |
| other_plots = self.plots() | |
| # self.plot_map = pn.pane.Matplotlib(vector_plot, width=800, height=600, sizing_mode="fixed") | |
| self.plot = pn.Column( | |
| *(pn.pane.HoloViews(plot, width=400, height=200) for plot in other_plots), | |
| sizing_mode="stretch_width" | |
| ) | |
| vector_plot = self.vectors_plot() # Update vector plot | |
| self.plot_map.object = vector_plot | |
| # other_plots = self.plots() # Update other plots | |
| # self.plot.objects = [*(pn.pane.HoloViews(p, width=400, height=200) for p in other_plots)] | |
| # self.plot.object=plot | |
| return pn.Row(self.plot_map, self.plot, sizing_mode="stretch_both") | |
| def filter_data(self): | |
| return self.ds.where( | |
| (self.ds.LONGITUDE >= self.longitude_slider.start) | |
| & (self.ds.LONGITUDE <= self.longitude_slider.end) | |
| & (self.ds.LATITUDE >= self.latitude_slider.start) | |
| & (self.ds.LATITUDE <= self.latitude_slider.end), | |
| drop=True, | |
| ) | |
| def vectors_plot(self): | |
| import hvplot.xarray | |
| self.ds_filtered = self.filter_data() | |
| # return self.ds_filtered['VSHIP'].hvplot(x='TIME',width=400, height=200) #if 'BATHY' in self.ds_filtered else hvplot.show(hvplot.text(0, 0, "No data available", fontsize=12)) | |
| fig, ax = plt.subplots( | |
| figsize=(8, 7), subplot_kw={"projection": ccrs.Mercator()} | |
| ) | |
| coords = ["LATITUDE", "LONGITUDE"] | |
| corsen = max(1, self.ds_filtered.MAXT.size // self.num_vectors_slider.value) | |
| self.ds_filtered = ( | |
| self.ds_filtered.reset_coords(coords) | |
| .coarsen({"MAXT": corsen}, boundary="trim") | |
| .mean() | |
| .set_coords(coords) | |
| ) | |
| # self.ds_filtered =self.ds_filtered.coarsen(MAXT = corsen, side = "center", boundary = "trim").mean()[["LONGITUDE", "LONGITUDE", "TIME"]].isel(MAXZ=0) | |
| depth_filtered = self.ds_filtered.where( | |
| (self.depth1 > self.depth_range_slider.value[0]) | |
| & (self.depth1 <= self.depth_range_slider.value[1]) | |
| ) | |
| lon = self.ds_filtered.coords["LONGITUDE"].values | |
| lat = self.ds_filtered.coords["LATITUDE"].values | |
| # skip = max(1, int(np.sqrt(lon.size) / self.num_vectors_slider.value)) | |
| # skip = (slice(None, None, skip), slice(None, None, skip)) | |
| # Moyenne des vecteurs de courant sur la plage de profondeur sélectionnée | |
| u_mean = depth_filtered.UCUR.mean(dim="MAXZ", skipna=True) | |
| v_mean = depth_filtered.VCUR.mean(dim="MAXZ", skipna=True) | |
| ax.quiver( | |
| lon, | |
| lat, | |
| u_mean * self.scale_factor_slider.value, | |
| v_mean * self.scale_factor_slider.value, | |
| color="blue", | |
| scale=2, | |
| width=0.001, | |
| headwidth=3, | |
| transform=ccrs.PlateCarree(), | |
| ) | |
| if self.depth_2_checkbox.value: | |
| depth_filtered = self.ds_filtered.where( | |
| (self.depth1 > self.depth2_range_slider.value[0]) | |
| & (self.depth1 <= self.depth2_range_slider.value[1]) | |
| ) | |
| u_mean = depth_filtered.UCUR.mean(dim="MAXZ", skipna=True) | |
| v_mean = depth_filtered.VCUR.mean(dim="MAXZ", skipna=True) | |
| ax.quiver( | |
| lon, | |
| lat, | |
| u_mean * self.scale_factor_slider.value, | |
| v_mean * self.scale_factor_slider.value, | |
| color="green", | |
| scale=2, | |
| width=0.001, | |
| headwidth=3, | |
| transform=ccrs.PlateCarree(), | |
| ) | |
| if self.depth_3_checkbox.value: | |
| depth_filtered = self.ds_filtered.where( | |
| (self.depth1 > self.depth3_range_slider.value[0]) | |
| & (self.depth1 <= self.depth3_range_slider.value[1]) | |
| ) | |
| u_mean = depth_filtered.UCUR.mean(dim="MAXZ", skipna=True) | |
| v_mean = depth_filtered.VCUR.mean(dim="MAXZ", skipna=True) | |
| ax.quiver( | |
| lon, | |
| lat, | |
| u_mean * self.scale_factor_slider.value, | |
| v_mean * self.scale_factor_slider.value, | |
| color="red", | |
| scale=2, | |
| width=0.001, | |
| headwidth=3, | |
| transform=ccrs.PlateCarree(), | |
| ) | |
| ax.add_feature(cfeature.COASTLINE) | |
| ax.add_feature(cfeature.BORDERS, linestyle=":") | |
| ax.add_feature(cfeature.LAND, color="lightgray") | |
| if self.bathy_checkbox.value: | |
| contour_levels = [-2000] | |
| ax.contour( | |
| bathy.longitude, | |
| bathy.latitude, | |
| bathy.z, | |
| levels=contour_levels, | |
| colors="black", | |
| transform=ccrs.PlateCarree(), | |
| ) | |
| ax.set_extent( | |
| [ | |
| self.longitude_slider.value[0], | |
| self.longitude_slider.value[1], | |
| self.latitude_slider.value[0], | |
| self.latitude_slider.value[1], | |
| ] | |
| ) | |
| ax.gridlines(draw_labels=True) | |
| plt.ylabel("Latitude", fontsize=15, labelpad=35) | |
| plt.xlabel("Longitude", fontsize=15, labelpad=20) | |
| return fig | |
| # pn.pane.Matplotlib(fig,width=800, height=600, sizing_mode="fixed", name="Plot") | |
| # @param.depends( 'file_dropdown.value', 'longitude_slider.value', 'latitude_slider.value', watch=True) | |
| def plots(self): | |
| return [ | |
| self.ds_filtered["BATHY"].hvplot(x="TIME", width=400, height=200), | |
| self.ds_filtered["USHIP"].hvplot(x="TIME", width=400, height=200), | |
| self.ds_filtered["VSHIP"].hvplot(x="TIME", width=400, height=200), | |
| self.ds_filtered["BOTTOM_DEPTH"].hvplot(x="TIME", width=400, height=200), | |
| ] | |
| def get_file_list(self): | |
| return self.file_names | |
| # Instantiate the SADCP_Viewer class and create a template | |
| explorer = SADCP_Viewer() | |
| tabs = pn.Tabs( | |
| ("Plots", pn.Column(explorer.update_plots)), | |
| ( | |
| "Metadata", | |
| pn.Column( | |
| explorer.metadata_table, height=500, margin=10 | |
| ), | |
| ), | |
| ) | |
| sidebar = [ | |
| """This application, developed in the frame of Euro Go Shop, helps to interactively visualise and download ship ADCP data.""", | |
| explorer.year_slider, | |
| explorer.file_dropdown, | |
| explorer.longitude_slider, | |
| explorer.latitude_slider, | |
| explorer.bathy_checkbox, | |
| explorer.depth_range_slider, | |
| explorer.depth_2_checkbox, | |
| explorer.depth_3_checkbox, | |
| explorer.depth2_range_slider, | |
| explorer.depth3_range_slider, | |
| explorer.num_vectors_slider, | |
| explorer.scale_factor_slider, | |
| explorer.data_table, | |
| ] | |
| template = pn.template.FastListTemplate(title="SADCP data Viewer", sidebar=sidebar, main=[tabs]) | |
| template.servable() | |