Spaces:
Build error
Build error
import cftime | |
import fsspec | |
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, | |
# FloatRangeSlider, | |
FloatSlider, | |
IntRangeSlider, | |
IntSlider, | |
# interactive, | |
) | |
import param | |
pn.extension("tabulator") | |
def greg_0h(jourjul): | |
import math | |
# 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(ds): | |
from datetime import datetime, timedelta | |
time = ds["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 | |
] | |
date = xr.DataArray(date, dims="MAXT") | |
return ds.assign(TIME=date) | |
def load_csv(): | |
url="https://data-eurogoship.ifremer.fr/data/zarr_table.csv" | |
fs = fsspec.filesystem("https") | |
with fs.open(url) as f: | |
df = pd.read_csv(f,index_col=None) | |
df.sort_values(by="year") #inplace=True) | |
return df | |
def load_bathymetry(): | |
#url="https://data-eurogoship.ifremer.fr/bathymetrie/bathy6min.nc" | |
#fs = fsspec.filesystem("https") | |
return xr.open_dataset('/data/bathy6min.nc', decode_times=False, use_cftime=True) | |
def load_zarr(selected_file): | |
from datatree import open_datatree | |
tree= open_datatree('/data/1H_file.zarr', engine='zarr') | |
return tree[selected_file+"/"].ds | |
def filter_df(sorted_df,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", | |
] | |
) | |
return dataframe.transpose(), dataframe2.transpose() | |
def quiver_depth_filterd(ax,ds_filtered, depth1, depth_range_slider, scale_factor_slider, color="blue"): | |
import cartopy.crs as ccrs | |
depth_filtered = ds_filtered.where( | |
(depth1 > depth_range_slider.value[0]) | |
& (depth1 <= depth_range_slider.value[1]) | |
) | |
lon = ds_filtered.coords["LONGITUDE"].values | |
lat = ds_filtered.coords["LATITUDE"].values | |
# 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) | |
return ax.quiver( | |
lon, | |
lat, | |
u_mean * scale_factor_slider.value, | |
v_mean * scale_factor_slider.value, | |
color=color, | |
scale=2, | |
width=0.001, | |
headwidth=3, | |
transform=ccrs.PlateCarree(), | |
) | |
class SADCP_Viewer(param.Parameterized): | |
df = load_csv() | |
bathy = load_bathymetry() | |
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 | |
self.data_table.value, self.metadata_table.value = filter_df(sorted_df,selected_file) | |
self.ds=load_zarr(selected_file) | |
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"]) | |
deph_range = (int(self.depth1.min()), int(self.depth1.max())) | |
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() | |
def update_plots(self): | |
self.ds_filtered = self.filter_data() | |
# vector_plot = self.vectors_plot() | |
other_plots = self.hvplot_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 cartopy.crs as ccrs | |
import cartopy.feature as cfeature | |
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) | |
quiver_depth_filterd(ax,self.ds_filtered, self.depth1, self.depth_range_slider,self.scale_factor_slider,color="blue") | |
if self.depth_2_checkbox.value: | |
quiver_depth_filterd(ax,self.ds_filtered, self.depth1, self.depth2_range_slider,self.scale_factor_slider,color="green") | |
if self.depth_3_checkbox.value: | |
quiver_depth_filterd(ax,self.ds_filtered, self.depth1, self.depth3_range_slider,self.scale_factor_slider,color="red") | |
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 = [-1000] | |
ax.contour( | |
self.bathy.longitude, | |
self.bathy.latitude, | |
self.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) | |
plt.close() | |
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 hvplot_plots(self): | |
import hvplot.xarray | |
return [ | |
self.ds_filtered["BATHY"].max(dim="MAXZ").hvplot(x="TIME", width=400, height=200), | |
self.ds_filtered["USHIP"].max(dim="MAXZ").hvplot(x="TIME", width=400, height=200), | |
self.ds_filtered["VSHIP"].max(dim="MAXZ").hvplot(x="TIME", width=400, height=200), | |
self.ds_filtered["BOTTOM_DEPTH"].max(dim="MAXZ").hvplot(x="TIME", width=400, height=200), | |
] | |
def get_file_list(self): | |
return self.file_names | |
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 = [ | |
"""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() | |