# coding: utf-8 # /*########################################################################## # # Copyright (c) 2017 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 # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. # # ###########################################################################*/ """This module provides the :class:`Scatter` item of the :class:`Plot`. """ __authors__ = ["T. Vincent", "P. Knobel"] __license__ = "MIT" __date__ = "29/03/2017" import logging import numpy from .core import Points, ColormapMixIn _logger = logging.getLogger(__name__) class Scatter(Points, ColormapMixIn): """Description of a scatter""" _DEFAULT_SYMBOL = 'o' """Default symbol of the scatter plots""" def __init__(self): Points.__init__(self) ColormapMixIn.__init__(self) self._value = () def _addBackendRenderer(self, backend): """Update backend renderer""" # Filter-out values <= 0 xFiltered, yFiltered, valueFiltered, xerror, yerror = self.getData( copy=False, displayed=True) if len(xFiltered) == 0: return None # No data to display, do not add renderer to backend cmap = self.getColormap() rgbacolors = cmap.applyToData(self._value) return backend.addCurve(xFiltered, yFiltered, self.getLegend(), color=rgbacolors, symbol=self.getSymbol(), linewidth=0, linestyle="", yaxis='left', xerror=xerror, yerror=yerror, z=self.getZValue(), selectable=self.isSelectable(), fill=False, alpha=self.getAlpha(), symbolsize=self.getSymbolSize()) def _logFilterData(self, xPositive, yPositive): """Filter out values with x or y <= 0 on log axes :param bool xPositive: True to filter arrays according to X coords. :param bool yPositive: True to filter arrays according to Y coords. :return: The filtered arrays or unchanged object if not filtering needed :rtype: (x, y, value, xerror, yerror) """ # overloaded from Points to filter also value. value = self.getValueData(copy=False) if xPositive or yPositive: clipped = self._getClippingBoolArray(xPositive, yPositive) if numpy.any(clipped): # copy to keep original array and convert to float value = numpy.array(value, copy=True, dtype=numpy.float) value[clipped] = numpy.nan x, y, xerror, yerror = Points._logFilterData(self, xPositive, yPositive) return x, y, value, xerror, yerror def getValueData(self, copy=True): """Returns the value assigned to the scatter data points. :param copy: True (Default) to get a copy, False to use internal representation (do not modify!) :rtype: numpy.ndarray """ return numpy.array(self._value, copy=copy) def getData(self, copy=True, displayed=False): """Returns the x, y coordinates and the value of the data points :param copy: True (Default) to get a copy, False to use internal representation (do not modify!) :param bool displayed: True to only get curve points that are displayed in the plot. Default: False. Note: If plot has log scale, negative points are not displayed. :returns: (x, y, value, xerror, yerror) :rtype: 5-tuple of numpy.ndarray """ if displayed: data = self._getCachedData() if data is not None: assert len(data) == 5 return data return (self.getXData(copy), self.getYData(copy), self.getValueData(copy), self.getXErrorData(copy), self.getYErrorData(copy)) # reimplemented from Points to handle `value` def setData(self, x, y, value, xerror=None, yerror=None, copy=True): """Set the data of the scatter. :param numpy.ndarray x: The data corresponding to the x coordinates. :param numpy.ndarray y: The data corresponding to the y coordinates. :param numpy.ndarray value: The data corresponding to the value of the data points. :param xerror: Values with the uncertainties on the x values :type xerror: A float, or a numpy.ndarray of float32. If it is an array, it can either be a 1D array of same length as the data or a 2D array with 2 rows of same length as the data: row 0 for positive errors, row 1 for negative errors. :param yerror: Values with the uncertainties on the y values :type yerror: A float, or a numpy.ndarray of float32. See xerror. :param bool copy: True make a copy of the data (default), False to use provided arrays. """ value = numpy.array(value, copy=copy) assert value.ndim == 1 assert len(x) == len(value) self._value = value # set x, y, xerror, yerror # call self._updated + plot._invalidateDataRange() Points.setData(self, x, y, xerror, yerror, copy)