Commit 317b775c authored by cponcele's avatar cponcele
Browse files

First release of a jupyter widget for diplay of xsf datasets

parent 3e5890d6
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"id": "cdbf91dd",
"metadata": {},
"outputs": [],
"source": [
"import os\n",
"from ipywidgets import HBox, VBox, Layout, Output, Textarea, Button\n",
"from ipytree import Tree, Node\n",
"from ipyfilechooser import FileChooser\n",
"from netCDF4 import Dataset\n",
"import numpy as np\n",
"import matplotlib.pyplot as plt\n",
"from mpl_toolkits.axes_grid1 import make_axes_locatable\n",
"#import mpld3\n",
"#mpld3.enable_notebook()"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "0e3914ae",
"metadata": {},
"outputs": [],
"source": [
"def info_panel(file, layer, plot_output):\n",
" \n",
" panel = VBox()\n",
" children = []\n",
" \n",
" f = Dataset(file, 'r')\n",
" \n",
" # Variables for Textarea and Button widgets\n",
" if layer == os.path.basename(file):\n",
" value = str(f)\n",
" disabled = True\n",
" else:\n",
" value = str(f[layer])\n",
" disabled = False if len(f[layer][:].shape) == 2 else True\n",
" \n",
" # Textarea widget with infos about selected variable \n",
" text_area = Textarea(value=value, layout=Layout(width=\"500px\", height=\"100px\"), disabled=True)\n",
" children.append(text_area)\n",
" \n",
" # Button widgets used to plot georeferenced variables\n",
" button = Button(description=\"Create Plot\", button_style=\"danger\", icon=\"map\", disabled=disabled)\n",
" children.append(button)\n",
" \n",
" # Output widget where the variable will be plotted using matplotlib\n",
"# output = Output()\n",
"# children.append(output)\n",
" \n",
" # Plot variable in the Output widget using matplotlib\n",
" def plot(button):\n",
" plot_output.clear_output()\n",
" \n",
" ds = Dataset(file, 'r')\n",
" data = ds[layer][:]\n",
" data = np.flip(data,0)\n",
" \n",
" lat = ds.variables['lat'][:]\n",
" lon = ds.variables['lon'][:]\n",
" \n",
" with plot_output:\n",
" print(\"Plotting, please wait...\")\n",
" \n",
"# fig = plt.figure(figsize=(10, 20))\n",
"# a = plt.imshow(data, cmap=\"jet\", extent=[lon.min(), lon.max(), lat.min(), lat.max()])\n",
"# fig.colorbar(a, orientation='vertical')\n",
"# plt.show()\n",
" \n",
" \n",
" plt.figure(figsize=(5,10))\n",
" ax = plt.gca()\n",
" im = ax.imshow(data, cmap=\"jet\", extent=[lon.min(), lon.max(), lat.min(), lat.max()])\n",
" \n",
" start, end = ax.get_xlim()\n",
" ax.xaxis.set_ticks(np.arange(start, end, 1.0))\n",
"\n",
" # create an axes on the right side of ax. The width of cax will be 5%\n",
" # of ax and the padding between cax and ax will be fixed at 0.05 inch.\n",
" divider = make_axes_locatable(ax)\n",
" cax = divider.append_axes(\"right\", size=\"5%\", pad=0.05)\n",
"\n",
" plt.colorbar(im, cax=cax)\n",
" \n",
" ds.close()\n",
" \n",
" button.on_click(plot)\n",
" \n",
" panel.children = children\n",
" \n",
" f.close()\n",
" \n",
" return panel"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "7ab88f1b",
"metadata": {},
"outputs": [],
"source": [
"def explorer(file):\n",
" \n",
" # Left part of the explorer (name of the file + groups/variables)\n",
" tree = Tree(multiple_selection=False)\n",
" root_name = os.path.basename(file)\n",
" root_node = Node(root_name, icon=\"file\", icon_style=\"info\", opened=False)\n",
" tree.add_node(root_node)\n",
" tree_widget = VBox([tree], layout=Layout(width='40%'))\n",
" \n",
" # Right part of the explorer\n",
" info_output = Output(layout=Layout(max_width=\"760px\"))\n",
" info_panel_widget = VBox([info_output])\n",
" \n",
" # Treeview is Left + Right part of the explorer\n",
" treeview = HBox([tree_widget, info_panel_widget])\n",
" \n",
" # Bottom part of the explorer (here, georeferenced variables will be plotted)\n",
" plot_output = Output()\n",
" full_widget = VBox([treeview, plot_output])\n",
" \n",
" def on_click(event):\n",
" if event[\"new\"]:\n",
" print(event[\"new\"])\n",
" cur_node = event[\"owner\"]\n",
" node_name = cur_node.name\n",
" \n",
"# if cur_node == root_node:\n",
"# output.clear_output()\n",
"# return\n",
" \n",
" with info_output:\n",
" info_output.clear_output()\n",
" gui = info_panel(file,node_name,plot_output)\n",
" display(gui)\n",
" \n",
" root_node.observe(on_click, \"selected\")\n",
" \n",
" \n",
" if file.endswith('.nc'):\n",
" f = Dataset(file, 'r')\n",
" \n",
" for layer in f.variables.keys():\n",
" node_name = layer\n",
" node = Node(node_name, icon_style=\"success\", icon=\"leaf\")\n",
" root_node.add_node(node)\n",
" node.observe(on_click, \"selected\")\n",
" \n",
" f.close()\n",
" \n",
" return full_widget"
]
},
{
"cell_type": "code",
"execution_count": 12,
"id": "7f4de58c",
"metadata": {},
"outputs": [
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "075aee1f8f5d4007b48cdc42b88281a7",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"VBox(children=(FileChooser(path='C:\\Users\\cponcele\\Desktop', filename='', title='HTML(value='', layout=Layout(…"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"# File Chooser widget\n",
"fc = FileChooser()\n",
"fc.default_path = os.path.join(os.path.join(os.environ['USERPROFILE']), 'Desktop')\n",
"fc.filter_pattern = '*.nc'\n",
"fc.use_dir_icons = True\n",
"\n",
"# gui = VBox([fc, Output()])\n",
"gui = VBox([fc])\n",
"\n",
"# Callback function that add a treeview below the FileChooser widget\n",
"def show_explorer(chooser):\n",
" # Single treeview (you can read only one file)\n",
"# lst = list(gui.children)\n",
"# lst[1] = treeview(chooser.selected)\n",
"# gui.children = lst\n",
"\n",
" # Multiple treeview (you can read multiple file by creating a new treeview)\n",
" gui.children += (explorer(chooser.selected),)\n",
"\n",
"# Register callback function\n",
"fc.register_callback(show_explorer)\n",
"\n",
"gui"
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "26ba5ad5",
"metadata": {},
"outputs": [],
"source": [
"from netCDF4 import Dataset\n",
"\n",
"file = r\"C:\\data\\datasets\\0005_20130131_173953_Pingeline.xsf.nc\"\n",
"ds = Dataset(file, 'r')\n",
"\n",
"# https://unidata.github.io/netcdf4-python/#groups-in-a-netcdf-file\n",
"\n",
"#print(rootgrp.variables)\n",
"\n",
"def walktree(top):\n",
" yield top.groups.values()\n",
" for value in top.groups.values():\n",
" yield from walktree(value)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "4c9d6aee",
"metadata": {},
"outputs": [],
"source": [
"from netCDF4 import Dataset\n",
"\n",
"file = r\"C:\\Users\\mamoev\\Desktop\\Stage\\EMODNet_Exemples\\all_xsf\\0139_20120607_092822_ShipName.xsf.nc\"\n",
"ds = Dataset(file, 'r')\n",
"\n",
"groups = ds.groups\n",
" \n",
"for group in ds.groups:\n",
" "
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "ec09ab95-e226-4ad3-8611-80088c2682d9",
"metadata": {},
"outputs": [
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "b08867436ee64de2af4891ee51fd9f50",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"FileChooser(path='C:\\\\data\\\\datasets', filename='', title='HTML(value='<b>FileChooser title</b>')', show_hidde…"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"# Create and display a FileChooser widget\n",
"fc = FileChooser(r\"C:\\\\data\\\\datasets\\\\\")\n",
"# Sample callback function\n",
"def change_title(chooser):\n",
" print(chooser.selected)\n",
"# Change the title (use '' to hide)\n",
"fc.title = '<b>FileChooser title</b>'\n",
"\n",
"# Sample callback function\n",
"def change_title(chooser):\n",
" chooser.title = '<b>Callback function executed</b>'\n",
" print(\"coucou\")\n",
"fc.register_callback(change_title)\n",
"display(fc)"
]
},
{
"cell_type": "code",
"execution_count": 10,
"id": "1cae454e-0ee1-49fb-996e-136f884a20cc",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"coucou\n"
]
}
],
"source": [
" print(\"coucou\")\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "096bd97d-c241-458d-a3b5-4842673d0801",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"hide_input": false,
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.10"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"id": "4f215451-f873-47a3-b0cf-efe39f21f1ea",
"metadata": {
"tags": []
},
"outputs": [],
"source": [
"import SonarWidget as w"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "30ae36d9-cfe8-4245-80b4-fda078680de5",
"metadata": {
"tags": []
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"C:\\data\\datasets\\XSF\\data\\0006_20200504_111056_FG_EM122.xsf.nc was selected\n"
]
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "82a6b41e744143e5af600da3ee3ed3f3",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"VBox(children=(FileChooser(path='C:\\dev\\workspaces\\pySonar-netcdf\\notebooks', filename='C:\\data\\datasets\\XSF\\d…"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"f=w.NCExplorer(starting_path=r'C:\\data\\datasets\\XSF\\data\\0006_20200504_111056_FG_EM122.xsf.nc')\n",
"f.show()"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "2a5dc0e1-bea5-4ada-a2b7-005169b05e9a",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.10"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
import os
from IPython.core.display import display
from ipyfilechooser import FileChooser
from ipywidgets import HBox, VBox, Output, Textarea, Button
from ipytree import Tree, Node
import importer # allow to solve relative imports
import sonar_netcdf.utils.nc_reader as reader
import netCDF4 as nc
class AppLayout2:
def clear_output(self, event):
self.content_plt.clear_output()
def clear_all(self):
self.plt_button.disabled=True
self.plt_button.description="plot selection"
self.clear_output(None)
self.content_text.value="Empty content"
def __init__(self, top=None):
self.top = top
self.content_text = Textarea(
"Empty content", layout={"width": "100%", "height": "100%"}
)
self.content_plt = Output(layout={"border": "1px solid black"})
self.tree = Tree(multiple_selection=False, layout={"width": "50%"})
center = HBox([self.tree, self.content_text], layout={"width": "100%"})
self.plt_button = Button(description="plot selection")
self.plt_button.disabled = True
clear_button = Button(description="clear")
clear_button.on_click(self.clear_output)
plt_toolbox = HBox((self.plt_button, clear_button))
plt_widget = VBox([plt_toolbox, self.content_plt])
self.widget = VBox([top, center, plt_widget], layout={"width": "100%"})
def show(self):
display(self.widget)
class NCExplorer:
def __init__(self, starting_path=r"C:\\"):
self.fc = self._createFileChooser(starting_path, self.on_file_selected)
self.widget = AppLayout2(top=self.fc)
self.current_selection = None
self.widget.plt_button.on_click(self.plot_current_variable)
if os.path.isfile(starting_path) and os.path.exists(starting_path):
self.on_file_selected(starting_path)
def on_file_selected(self, filename):
self.current_file = filename
self.current_selection = None
# clear previous selection
for n in self.widget.tree.nodes:
self.widget.tree.remove_node(n)
self.node_dataset_dict={}
self.widget.clear_all()
root_name = os.path.basename(filename)
root_node = Node(root_name, icon="file", icon_style="info", opened=False)
self.widget.tree.add_node(root_node)
print(f"{filename} was selected")
self.current_reader = reader.NcReader(filename)
root = self.current_reader.dataset
# create a dictonnary to retains informations between dataset and tree nodes
self.node_dataset_dict = dict()
self.node_dataset_dict[root_node] = root
root_node.observe(self.handle_tree_click, "selected")
self._recurseTree(current_group=root, current_node=root_node)
def show(self):
return self.widget.show()
def _recurseTree(self, current_group: nc.Dataset, current_node: Node):
for g in current_group.groups:
n = Node(g, icon="folder", icon_style="info", opened=False)
current_node.add_node(n)
self.node_dataset_dict[n] = current_group[g]
n.observe(self.handle_tree_click, "selected")
self._recurseTree(current_group.groups[g], n)
for v in current_group.variables:
n = Node(v, icon="leaf", icon_style="info")
variable = current_group.variables[v]
n.observe(self.handle_tree_click, "selected")
self.node_dataset_dict[n] = variable
current_node.add_node(n)
def _get_variable_path_and_name(self, dataset: nc.Dataset):
if isinstance(dataset, nc.Variable):
vpath = "/"
parent = dataset._grp
if hasattr(parent, "path"):
vpath = parent.path
return (f"{vpath}/{dataset.name}", dataset.name)
return None
def print_node_overview(self, dataset):
marker = "-> "
if isinstance(dataset, nc.Variable):
(full_path, name) = self._get_variable_path_and_name(dataset)
if self.current_reader._is_xsfvlen(dataset):
desc = f"Dataset : {full_path} vlen({dataset.dtype})\n\n"
else:
desc = f"Dataset : {full_path} ({dataset.dtype})\n\n"
v = ",".join((f"{dim}" for dim in dataset.dimensions))
dimensions = f"Dimensions: ({v}) {dataset.shape}"
desc = "".join([desc, dimensions])
elif isinstance(dataset, nc.Group): # NCGroup
if hasattr(dataset, "path"):
vpath = dataset.path
desc = f"Dataset : {vpath}\n\n"
dimensions = f"Dimensions:\n"
v = "".join(
(
f"\t{marker}{dim}:{dataset.dimensions[dim].size}\n"
for dim in dataset.dimensions
)
)
desc = "".join([desc, dimensions, v])
cmptypes = f"\nCompound types:\n"
v = "".join(
(f"\t{marker}{t}:{dataset.cmptypes[t]}\n" for t in dataset.cmptypes)
)
desc = "".join([desc, cmptypes, v])
enum_t = f"\nEnum types:\n"
v = "".join(
(
f"\t{marker}{t}: type={dataset.enumtypes[t].dtype}, values={dataset.enumtypes[t].enum_dict}\n"
for t in dataset.enumtypes
)
)
desc = "".join([desc, enum_t, v])
vl_type = f"\nVlen types:\n"
v = "".join(
(
f"\t{marker}{t}: type={dataset.vltypes[t].dtype}\n"
for t in dataset.vltypes
)
)
desc = "".join([desc, vl_type, v])
else:
# ROOT GRoup
desc = f"Dataset : / \n\n"
attributes = f"\nAttributes:\n"
v = "".join(
(f"\t{marker}{att}:{dataset.getncattr(att)}\n" for att in dataset.ncattrs())
)
desc = "".join([desc, attributes, v])
self.widget.content_text.value = str(desc)
def handle_tree_click(self, event):
if event["new"] and event["owner"] is not None:
node = event["owner"]
self.print_node_overview(self.node_dataset_dict[node])
self.current_selection = self.node_dataset_dict[node]
if self.current_selection is not None and isinstance(
self.current_selection, nc.Variable
):
self.widget.plt_button.description = (
f"Plot {self.current_selection.name}"
)
self.widget.plt_button.disabled = False
else:
self.widget.plt_button.disabled = True
def plot_ncvariable(self, dataset):
with self.widget.content_plt:
ret = self._get_variable_path_and_name(dataset)
if ret is not None:
path = ret[0]
name = ret[1]
self.current_reader._display_variable(
variable_name=name, variable_path=path, slice_index={}
)
def plot_current_variable(self, event):
if self.current_selection is not None and isinstance(
self.current_selection, nc.Variable
):
self.plot_ncvariable(self.current_selection)
# create a file Chooser Part
def _createFileChooser(self, path: str, callback):
if not os.path.isfile(path):
fc = FileChooser(path)
else:
fc = FileChooser(filename=path)
# Create and display a FileChooser widget
# create our own callback that just call by passing the selected file
def on_selection(chooser):
callback(chooser.selected)
fc.register_callback(on_selection)
return fc
if __name__ == "__main__":
import matplotlib.pyplot as plt