summaryrefslogtreecommitdiff
path: root/silx/io/nxdata/parse.py
diff options
context:
space:
mode:
Diffstat (limited to 'silx/io/nxdata/parse.py')
-rw-r--r--silx/io/nxdata/parse.py111
1 files changed, 106 insertions, 5 deletions
diff --git a/silx/io/nxdata/parse.py b/silx/io/nxdata/parse.py
index cce47ab..6bd18d6 100644
--- a/silx/io/nxdata/parse.py
+++ b/silx/io/nxdata/parse.py
@@ -1,7 +1,7 @@
# coding: utf-8
# /*##########################################################################
#
-# Copyright (c) 2017-2019 European Synchrotron Radiation Facility
+# Copyright (c) 2017-2020 European Synchrotron Radiation Facility
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
@@ -41,6 +41,7 @@ Other public functions:
"""
+import json
import numpy
import six
@@ -53,13 +54,83 @@ from ._utils import get_attr_as_unicode, INTERPDIM, nxdata_logger, \
__authors__ = ["P. Knobel"]
__license__ = "MIT"
-__date__ = "15/02/2019"
+__date__ = "24/03/2020"
class InvalidNXdataError(Exception):
pass
+class _SilxStyle(object):
+ """NXdata@SILX_style parser.
+
+ :param NXdata nxdata:
+ NXdata description for which to extract silx_style information.
+ """
+
+ def __init__(self, nxdata):
+ naxes = len(nxdata.axes)
+ self._axes_scale_types = [None] * naxes
+ self._signal_scale_type = None
+
+ stylestr = get_attr_as_unicode(nxdata.group, "SILX_style")
+ if stylestr is None:
+ return
+
+ try:
+ style = json.loads(stylestr)
+ except json.JSONDecodeError:
+ nxdata_logger.error(
+ "Ignoring SILX_style, cannot parse: %s", stylestr)
+ return
+
+ if not isinstance(style, dict):
+ nxdata_logger.error(
+ "Ignoring SILX_style, cannot parse: %s", stylestr)
+
+ if 'axes_scale_types' in style:
+ axes_scale_types = style['axes_scale_types']
+
+ if isinstance(axes_scale_types, str):
+ # Convert single argument to list
+ axes_scale_types = [axes_scale_types]
+
+ if not isinstance(axes_scale_types, list):
+ nxdata_logger.error(
+ "Ignoring SILX_style:axes_scale_types, not a list")
+ else:
+ for scale_type in axes_scale_types:
+ if scale_type not in ('linear', 'log'):
+ nxdata_logger.error(
+ "Ignoring SILX_style:axes_scale_types, invalid value: %s", str(scale_type))
+ break
+ else: # All values are valid
+ if len(axes_scale_types) > naxes:
+ nxdata_logger.error(
+ "Clipping SILX_style:axes_scale_types, too many values")
+ axes_scale_types = axes_scale_types[:naxes]
+ elif len(axes_scale_types) < naxes:
+ # Extend axes_scale_types with None to match number of axes
+ axes_scale_types = [None] * (naxes - len(axes_scale_types)) + axes_scale_types
+ self._axes_scale_types = tuple(axes_scale_types)
+
+ if 'signal_scale_type' in style:
+ scale_type = style['signal_scale_type']
+ if scale_type not in ('linear', 'log'):
+ nxdata_logger.error(
+ "Ignoring SILX_style:signal_scale_type, invalid value: %s", str(scale_type))
+ else:
+ self._signal_scale_type = scale_type
+
+ axes_scale_types = property(
+ lambda self: self._axes_scale_types,
+ doc="Tuple of NXdata axes scale types (None, 'linear' or 'log'). List[str]")
+
+ signal_scale_type = property(
+ lambda self: self._signal_scale_type,
+ doc="NXdata signal scale type (None, 'linear' or 'log'). str")
+
+
class NXdata(object):
"""NXdata parser.
@@ -76,6 +147,7 @@ class NXdata(object):
"""
def __init__(self, group, validate=True):
super(NXdata, self).__init__()
+ self._plot_style = None
self.group = group
"""h5py-like group object with @NX_class=NXdata.
@@ -147,6 +219,8 @@ class NXdata(object):
# excludes scatters
self.signal_is_1d = self.signal_is_1d and len(self.axes) <= 1 # excludes n-D scatters
+ self._plot_style = _SilxStyle(self)
+
def _validate(self):
"""Fill :attr:`issues` with error messages for each error found."""
if not is_group(self.group):
@@ -250,8 +324,18 @@ class NXdata(object):
"dimensions as axis '%s'." % axis_name)
# test dimensions of errors associated with signal
+
+ signal_errors = signal_name + "_errors"
if "errors" in self.group and is_dataset(self.group["errors"]):
- if self.group["errors"].shape != self.group[signal_name].shape:
+ errors = "errors"
+ elif signal_errors in self.group and is_dataset(self.group[signal_errors]):
+ errors = signal_errors
+ else:
+ errors = None
+ if errors:
+ if self.group[errors].shape != self.group[signal_name].shape:
+ # In principle just the same size should be enough but
+ # NeXus documentation imposes to have the same shape
self.issues.append(
"Dataset containing standard deviations must " +
"have the same dimensions as the signal.")
@@ -629,9 +713,26 @@ class NXdata(object):
if not self.is_valid:
raise InvalidNXdataError("Unable to parse invalid NXdata")
- if "errors" not in self.group:
+ # case of signal
+ signal_errors = self.signal_dataset_name + "_errors"
+ if "errors" in self.group and is_dataset(self.group["errors"]):
+ errors = "errors"
+ elif signal_errors in self.group and is_dataset(self.group[signal_errors]):
+ errors = signal_errors
+ else:
return None
- return self.group["errors"]
+ return self.group[errors]
+
+ @property
+ def plot_style(self):
+ """Information extracted from the optional SILX_style attribute
+
+ :raises: InvalidNXdataError
+ """
+ if not self.is_valid:
+ raise InvalidNXdataError("Unable to parse invalid NXdata")
+
+ return self._plot_style
@property
def is_scatter(self):