diff --git a/glue_solar/__init__.py b/glue_solar/__init__.py index 32c565f..3d4284a 100644 --- a/glue_solar/__init__.py +++ b/glue_solar/__init__.py @@ -11,4 +11,6 @@ def setup(): from glue.viewers.image.qt import ImageViewer from glue_solar.pixel_extraction import PixelExtractionTool # noqa + from glue_solar.pixel_hover import HoverExtractionTool ImageViewer.tools.append('solar:pixel_extraction') + ImageViewer.tools.append('solar:hover_extraction') diff --git a/glue_solar/instruments/__init__.py b/glue_solar/instruments/__init__.py index 4ed2abd..a2f6261 100644 --- a/glue_solar/instruments/__init__.py +++ b/glue_solar/instruments/__init__.py @@ -1 +1,2 @@ from .iris import _parse_iris_raster +from .sst import read_lapalma diff --git a/glue_solar/instruments/sst.py b/glue_solar/instruments/sst.py new file mode 100644 index 0000000..475cf3b --- /dev/null +++ b/glue_solar/instruments/sst.py @@ -0,0 +1,120 @@ +""" +A reader for 'La Palma' datacubes from the Swedish 1-m Solar Telescope +""" +import numpy as np +from glue.config import data_factory +from glue.core.data_factories import has_extension +from glue.core import Data + + +def lp_getheader(filename): + """ + Reads header from La Palma format cube. + + Parameters + ---------- + filename : str + File to read header from. + + Returns + ------- + result : list + List with shape tuple (nx, ny [, nt]), + datatype (with endianness), header string. + """ + # read header and convert to string + h = np.fromfile(filename, dtype='uint8', count=512) + header = '' + for s in h[h > 0]: + header += chr(s) + # start reading at 'datatype' + hd = header[header.lower().find('datatype'):] + hd = hd.split(':')[0].replace(',', ' ').split() + # Types: uint8 int16 int32 float32 + typelist = ['u1', 'i2', 'i4', 'f4'] + # extract datatype + try: + dtype = typelist[int(hd[0].split('=')[1]) - 1] + except: + print(header) + raise IOError('lp_getheader: datatype invalid or missing') + # extract endianness + try: + if hd[-1].split('=')[0].lower() != 'endian': + raise IndexError() + endian = hd[-1].split('=')[1] + except IndexError: + print(header) + raise IOError('lp_getheader: endianess missing.') + if endian.lower() == 'l': + dtype = '<' + dtype + else: + dtype = '>' + dtype + # extract dims + try: + if hd[2].split('=')[0].lower() != 'dims': + raise IndexError() + dims = int(hd[2].split('=')[1]) + if dims not in [2, 3]: + raise ValueError('Invalid dims=%i (must be 2 or 3)' % dims) + except IndexError: + print(header) + raise IOError('lp_getheader: dims invalid or missing.') + try: + if hd[3].split('=')[0].lower() != 'nx': + raise IndexError() + nx = int(hd[3].split('=')[1]) + except: + print(header) + raise IOError('lp_getheader: nx invalid or missing.') + try: + if hd[4].split('=')[0].lower() != 'ny': + raise IndexError() + ny = int(hd[4].split('=')[1]) + except: + print(header) + raise IOError('lp_getheader: ny invalid or missing.') + if dims == 3: + try: + if hd[5].split('=')[0].lower() != 'nt': + raise IndexError() + nt = int(hd[5].split('=')[1]) + except: + print(header) + raise IOError('lp_getheader: nt invalid or missing.') + shape = (nx, ny, nt) + else: + shape = (nx, ny) + return [shape, dtype, header] + + +def lp_getdata(filename, rw=False, verbose=False): + """ + Reads La Palma format cube into a memmap object. + + Parameters + ---------- + filename : str + File to read. + rw : bool, optional + If True, then any change to the data will be written to file. + verbose : bool, optional + If True, will print out additional information. + + Returns + ------- + data : ndarray + Numpy memmap object with data. + """ + sh, dt, header = lp_getheader(filename) + if verbose: + print(('Reading %s...\n%s' % (filename, header))) + mode = ['c', 'r+'] + return np.memmap(filename, mode=mode[rw], shape=sh, dtype=dt, order='F', + offset=512) + + +@data_factory('La Palma cube', has_extension('fcube icube'), default='fcube icube') +def read_lapalma(file_name): + data = lp_getdata(file_name) + return Data(cube=data) diff --git a/glue_solar/pixel_hover.py b/glue_solar/pixel_hover.py new file mode 100644 index 0000000..3d224d0 --- /dev/null +++ b/glue_solar/pixel_hover.py @@ -0,0 +1,75 @@ +from __future__ import absolute_import, division, print_function + +from glue.config import viewer_tool + +from glue.core.data_derived import IndexedData + +from glue.viewers.matplotlib.toolbar_mode import ToolbarModeBase + +__all__ = ['HoverExtractionTool'] + + +@viewer_tool +class HoverExtractionTool(ToolbarModeBase): + """ + Create a derived dataset corresponding to the selected pixel. + """ + + icon = "glue_cross" + tool_id = 'solar:hover_extraction' + action_text = 'Pixel' + tool_tip = 'Show data for a single pixel based on mouse location' + status_tip = 'Mouse over to update the extracted dataset in real time' + + _pressed = False + + def __init__(self, *args, **kwargs): + super(HoverExtractionTool, self).__init__(*args, **kwargs) + self._move_callback = self._extract_pixel + self._press_callback = self._on_press + self._release_callback = self._on_release + self._derived = None + + self._line_x = self.viewer.axes.axvline(0, color='blue') + self._line_x.set_visible(False) + + self._line_y = self.viewer.axes.axhline(0, color='blue') + self._line_y.set_visible(False) + + def _on_press(self, mode): + self._pressed = True + self._extract_pixel(mode) + + def _on_release(self, mode): + self._pressed = False + + def _extract_pixel(self, mode): + + x, y = self._event_xdata, self._event_ydata + + if x is None or y is None: + return None + + xi = int(round(x)) + yi = int(round(y)) + + indices = [None] * self.viewer.state.reference_data.ndim + indices[self.viewer.state.x_att.axis] = xi + indices[self.viewer.state.y_att.axis] = yi + + self._line_x.set_data([x, x], [0, 1]) + self._line_x.set_visible(True) + self._line_y.set_data([0, 1], [y, y]) + self._line_y.set_visible(True) + self.viewer.axes.figure.canvas.draw() + + if self._derived is None: + self._derived = IndexedData(self.viewer.state.reference_data, indices) + self.viewer.session.data_collection.append(self._derived) + else: + try: + self._derived.indices = indices + except TypeError: + self.viewer.session.data_collection.remove(self._derived) + self._derived = IndexedData(self.viewer.state.reference_data, indices) + self.viewer.session.data_collection.append(self._derived)