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 jdcal import matplotlib.pyplot as plt import numpy as np import pandas as pd import panel as pn import xarray as xr from astropy.time import Time 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): 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() @param.depends("year_slider.value", "file_dropdown.value", watch=True) 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 @param.depends( "year_slider.value", "file_dropdown.value", "depth_range_slider.value", "depth_2_checkbox.value", "depth_3_checkbox.value", "depth2_range_slider.value", "depth3_range_slider.value", "longitude_slider.value", "latitude_slider.value", "num_vectors_slider.value", "scale_factor_slider.value", "bathy_checkbox.value", watch=True, ) 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, explorer.download_button, 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()