Spaces:
Runtime error
Runtime error
| import numpy as np | |
| import pandas as pd | |
| import panel as pn | |
| import xarray as xr | |
| import param | |
| import hvplot.xarray | |
| import matplotlib.pyplot as plt | |
| import cartopy.crs as ccrs | |
| import cartopy.feature as cfeature | |
| from datatree import open_datatree | |
| def get_range(da): | |
| return ( int(da.min().round() - 1), int(da.max().round() + 1),) | |
| def load_csv(path='./data/zarr_table.csv'): | |
| df = pd.read_csv(path,index_col=None) | |
| return df.sort_values(by="year") #inplace=True) | |
| def load_bathymetry(path='./data/bathy6min.nc'): | |
| return xr.open_dataset(path, decode_times=False, use_cftime=True) | |
| def load_zarr(path='./data/1H_file.zarr'): | |
| return open_datatree(path, engine='zarr') | |
| def load_file(tree,selected_file): | |
| return tree[selected_file+"/"].ds | |
| def filter_df(sorted_df,selected_file): | |
| # include user_interface_url here | |
| 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", | |
| ] | |
| ) | |
| # include LOCAL_CDI_ID here | |
| dataframe2 = sorted_df[sorted_df["file_name"] == selected_file].drop( | |
| columns=[ | |
| "file_name", | |
| "date_start", | |
| "date_end", | |
| "ADCP_frequency(kHz)", | |
| "bin_length(meter)", | |
| "year", | |
| ] | |
| ) | |
| return dataframe.transpose(), dataframe2.transpose() | |
| def filter_data(ds,longitude_range,latitude_range): | |
| return ds.where( | |
| (ds.LONGITUDE >= longitude_range[0]) | |
| & (ds.LONGITUDE <= longitude_range[1]) | |
| & (ds.LATITUDE >= latitude_range[0]) | |
| & (ds.LATITUDE <= latitude_range[1]), | |
| drop=True, | |
| ) | |
| def quiver_depth_filtered(ax, ds, depth_range, scale_factor, color="blue"): | |
| """ | |
| Plot quiver plot of mean current vectors filtered by depth. | |
| Parameters: | |
| ax (matplotlib.axes.Axes): The matplotlib axes object to plot on. | |
| ds (xarray.Dataset): The dataset containing the current data. | |
| depth_range (tuple): Tuple containing the minimum and maximum depth values for filtering. | |
| scale_factor (float): Scaling factor for the magnitude of the current vectors. | |
| color (str, optional): Color of the quiver arrows. Defaults to "blue". | |
| Returns: | |
| matplotlib.quiver.Quiver: The quiver plot object. | |
| """ | |
| # Filter data based on depth range | |
| ds = ds.sel( PROFZ=slice(depth_range[1],depth_range[0])) | |
| # Calculate mean current vectors within the selected depth range | |
| u_mean = ds.UCUR.mean(dim="PROFZ", skipna=True) | |
| v_mean = ds.VCUR.mean(dim="PROFZ", skipna=True) | |
| # Extract longitude and latitude coordinates | |
| lon = ds.coords["LONGITUDE"].values | |
| lat = ds.coords["LATITUDE"].values | |
| # Plot quiver plot | |
| return ax.quiver( | |
| lon, | |
| lat, | |
| u_mean * scale_factor, | |
| v_mean * scale_factor, | |
| color=color, | |
| scale=2, | |
| width=0.001, | |
| headwidth=3, | |
| transform=ccrs.PlateCarree(), | |
| ) | |
| def bathy_uship_vship_bottom_depth(ds): | |
| """ | |
| Plot maximum values of bathymetry, USHIP, VSHIP, and bottom depth over time. | |
| Parameters: | |
| ds (xarray.Dataset): Dataset containing the required variables. | |
| Returns: | |
| list: List of hvplot objects representing the plots of maximum values of bathymetry, | |
| USHIP, VSHIP, and bottom depth over time. | |
| """ | |
| return [ | |
| ds["BATHY"].max(dim="PROFZ").hvplot(x="TIME", width=400, height=200), | |
| ds["USHIP"].max(dim="PROFZ").hvplot(x="TIME", width=400, height=200), | |
| ds["VSHIP"].max(dim="PROFZ").hvplot(x="TIME", width=400, height=200), | |
| ds["BOTTOM_DEPTH"].max(dim="PROFZ").hvplot(x="TIME", width=400, height=200), | |
| ] | |
| def corsen_data(ds, sample): | |
| """ | |
| Downsample the dataset `ds` based on the number of vectors specified by `sample`. | |
| Parameters: | |
| ds (xarray.Dataset): Dataset to be downsampled. | |
| sample (int): Number of vectors used for downsampling. | |
| Returns: | |
| xarray.Dataset: Downsampled dataset. | |
| """ | |
| coords = ["LATITUDE", "LONGITUDE"] | |
| corsen = max(1, ds.TIME.size // sample) | |
| return ( | |
| ds.reset_coords(coords) | |
| .coarsen({"TIME": corsen}, boundary="trim") | |
| .mean() | |
| .set_coords(coords) | |
| ) | |
| def vectors_plot(ds, bathy, longitude_range, latitude_range , | |
| depth_range, depth_2_range, depth_3_range, | |
| scale_factor, sample, | |
| depth_2_checkbox=False, depth_3_checkbox=False, bathy_checkbox=False): | |
| """ | |
| Plot vectors filtered by depth on a map with specified features. | |
| Parameters: | |
| ds (xarray.Dataset): Dataset containing current data. | |
| bathy (xarray.Dataset): Dataset containing bathymetry data. | |
| longitude_range (tuple): Tuple containing the minimum and maximum longitude values. | |
| latitude_range (tuple): Tuple containing the minimum and maximum latitude values. | |
| depth_range (tuple): Tuple containing the minimum and maximum depth values for filtering. | |
| depth_2_range (tuple): Tuple containing the minimum and maximum depth values for filtering depth 2. | |
| depth_3_range (tuple): Tuple containing the minimum and maximum depth values for filtering depth 3. | |
| scale_factor (float): Scaling factor for the magnitude of the current vectors. | |
| sample (int): Number of vectors used for downsampling. | |
| depth_2_checkbox (bool, optional): Whether to plot vectors for depth 2. Defaults to False. | |
| depth_3_checkbox (bool, optional): Whether to plot vectors for depth 3. Defaults to False. | |
| bathy_checkbox (bool, optional): Whether to plot bathymetry. Defaults to False. | |
| Returns: | |
| matplotlib.figure.Figure: The generated plot. | |
| """ | |
| # Create subplot with Mercator projection | |
| fig, ax = plt.subplots(figsize=(5, 4), subplot_kw={"projection": ccrs.Mercator()}) | |
| # Apply data downsampling | |
| ds = corsen_data(ds, sample) | |
| # Plot vectors filtered by depth | |
| quiver_depth_filtered(ax, ds, depth_range, scale_factor, color="blue") | |
| if depth_2_checkbox: | |
| quiver_depth_filtered(ax, ds, depth_2_range, scale_factor, color="green") | |
| if depth_3_checkbox: | |
| quiver_depth_filtered(ax, ds, depth_3_range, scale_factor, color="red") | |
| # Add map features | |
| ax.add_feature(cfeature.COASTLINE) | |
| ax.add_feature(cfeature.BORDERS, linestyle=":") | |
| ax.add_feature(cfeature.LAND, color="lightgray") | |
| # Plot bathymetry if provided | |
| if bathy_checkbox: | |
| contour_levels = [-1000] | |
| ax.contour(bathy.longitude, bathy.latitude, bathy.z, | |
| levels=contour_levels, colors="black", transform=ccrs.PlateCarree()) | |
| # Set extent and add gridlines | |
| ax.set_extent([longitude_range[0], longitude_range[1], | |
| latitude_range[0], latitude_range[1]]) | |
| ax.gridlines(draw_labels=True) | |
| # Set labels and close plot | |
| plt.ylabel("Latitude", fontsize=15, labelpad=35) | |
| plt.xlabel("Longitude", fontsize=15, labelpad=20) | |
| #https://panel.holoviz.org/reference/panes/Matplotlib.html#using-the-matplotlib-pyplot-interface | |
| plt.close(fig) | |
| return fig | |
| class SADCP_Viewer(param.Parameterized): | |
| """ | |
| A parameterized class for viewing SADCP data. | |
| This class provides widgets for selecting data parameters, updating data based on selections, | |
| and generating plots to visualize the SADCP data. | |
| Available functions: | |
| - update_name_options: Update dropdown options and slider ranges based on selected years and file. | |
| - update_plots: Update plots based on selected data and parameters. | |
| """ | |
| # Load data and initialize widgets | |
| df = load_csv() | |
| bathy = load_bathymetry() | |
| tree=load_zarr() | |
| file_names = df["file_name"].tolist() | |
| years = sorted(df["year"].unique()) | |
| # Widgets for selecting data parameters | |
| year_slider = pn.widgets.IntRangeSlider(name="Year Range", start=df["year"].min(), end=df["year"].max()) | |
| file_dropdown = pn.widgets.Select(name="File Selector") | |
| 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") | |
| depth_2_range_slider = pn.widgets.IntRangeSlider(start=100, end=300, value=(100, 300), step=1, name="Depth 2 Range") | |
| depth_3_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") | |
| data_table = pn.widgets.Tabulator(width=400, height=200) | |
| metadata_table = pn.widgets.Tabulator(width=600, height=800) | |
| # Download button is not working : TODO | |
| download_button = pn.widgets.Button(name="Download", button_type="primary") | |
| def __init__(self, **params): | |
| """ | |
| Initialize the SADCP_Viewer class. | |
| Parameters: | |
| **params: Additional parameters to be passed to the superclass. | |
| """ | |
| super(SADCP_Viewer, self).__init__(**params) | |
| self.file_dropdown.objects = self.file_names | |
| 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): | |
| """ | |
| Update dropdown options and slider ranges based on selected years and file. | |
| This function updates the dropdown options and slider ranges based on the selected years | |
| and file. It also loads the selected file's data and adjusts slider ranges accordingly. | |
| """ | |
| # Extract selected start and end years | |
| start_year, end_year = self.year_slider.value | |
| # Filter DataFrame based on selected years and sort by year | |
| mask = (self.df["year"] >= start_year) & (self.df["year"] <= end_year) | |
| sorted_df = self.df[mask].sort_values(by="year") | |
| # Get unique file names | |
| files = sorted_df["file_name"].unique().tolist() | |
| # Update file dropdown options | |
| self.file_dropdown.options = files | |
| if files: | |
| selected_file = self.file_dropdown.value | |
| # Set default selected file if not selected or not in options | |
| if not selected_file or selected_file not in files: | |
| selected_file = files[0] | |
| self.file_dropdown.value = selected_file | |
| # Update data table and metadata table based on selected file | |
| self.data_table.value, self.metadata_table.value = filter_df(sorted_df, selected_file) | |
| # Load selected file's data | |
| self.ds = load_file(self.tree,selected_file) | |
| # Update slider ranges for longitude, latitude, and depth | |
| for slider, coord in zip([self.longitude_slider, self.latitude_slider, self.depth_range_slider, | |
| self.depth_2_range_slider, self.depth_3_range_slider], | |
| [self.ds.LONGITUDE, self.ds.LATITUDE, self.ds.PROFZ,self.ds.PROFZ,self.ds.PROFZ]): | |
| coord_range = get_range(coord) | |
| slider.start, slider.end, slider.value = coord_range[0], coord_range[1], coord_range | |
| # Close dataset to free up resources | |
| # self.ds.close() | |
| def update_plots(self): | |
| """ | |
| This function updates the plots based on the selected data and parameters. | |
| The function filters the data, generates additional plots, and updates the main vector plot based on the selected parameters. | |
| Returns: | |
| pn.Row: A Panel row containing the updated map plot and additional plots. | |
| """ | |
| # Filter the data | |
| self.ds_filtered = filter_data(self.ds,self.longitude_slider.value,self.latitude_slider.value) | |
| # Prepare the plots shown in left | |
| # Update vector plots | |
| vector_plot = vectors_plot(self.ds_filtered, self.bathy, | |
| self.longitude_slider.value, self.latitude_slider.value, | |
| self.depth_range_slider.value, self.depth_2_range_slider.value, self.depth_3_range_slider.value, | |
| self.scale_factor_slider.value,self.num_vectors_slider.value, | |
| depth_2_checkbox= self.depth_2_checkbox.value, | |
| depth_3_checkbox= self.depth_3_checkbox.value, | |
| bathy_checkbox=self.bathy_checkbox.value, | |
| ) | |
| # Generate plots which will be plotted on the left row. | |
| self.plot_left = pn.Column( | |
| # Here adjust the style option later TODO | |
| # https://panel.holoviz.org/how_to/styling/matplotlib.html | |
| pn.pane.Matplotlib(vector_plot, dpi=144), | |
| # Add here the hvplot block of contour TODO | |
| sizing_mode="stretch_both") | |
| # Generate additional plots which will be plotted on the right row. | |
| other_plots = bathy_uship_vship_bottom_depth(self.ds_filtered) | |
| self.plot_right = pn.Column( | |
| *(pn.pane.HoloViews(plot, width=400, height=200) for plot in other_plots), | |
| sizing_mode="stretch_width" | |
| ) | |
| # Return a Panel row containing the updated map plot and additional plots | |
| return pn.Row(self.plot_left, self.plot_right, sizing_mode="stretch_both") | |
| pn.extension("tabulator") | |
| pn.config.theme = 'dark' | |
| explorer = SADCP_Viewer() | |
| # Instantiate the SADCP_Viewer class and create a template | |
| tabs = pn.Tabs( | |
| ("Plots", pn.Column(explorer.update_plots)), | |
| ( | |
| "Metadata", | |
| pn.Column( | |
| explorer.metadata_table, explorer.download_button, height=500, margin=10 | |
| ), | |
| ), | |
| ) | |
| sidebar = [ | |
| pn.panel('./EuroGO-SHIP_logo_wide_tagline_1.2.png',width=300 ), | |
| """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.depth_2_range_slider, | |
| explorer.depth_3_range_slider, | |
| explorer.num_vectors_slider, | |
| explorer.scale_factor_slider, | |
| explorer.data_table, | |
| """You can consult detailed information on this data in the metadata tab shown on the right. | |
| To download full dataset, please go to https://cdi.seadatanet.org/search | |
| and search with LOCAL_CDI_ID indicated above.""", | |
| #pn.panel('https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png', ), | |
| #width=10) | |
| ] | |
| template = pn.template.FastListTemplate( | |
| title="SADCP data Viewer", logo='https://avatars.githubusercontent.com/u/123177533?s=200&v=4', | |
| sidebar=sidebar, main=[tabs] | |
| ) | |
| template.servable() | |