Skip to content

Commit

Permalink
Merge pull request #182 from DanRyanIrish/ndcube2.0
Browse files Browse the repository at this point in the history
  • Loading branch information
nabobalis authored Oct 11, 2021
2 parents 613caf6 + f32591d commit ab2c574
Show file tree
Hide file tree
Showing 10 changed files with 385 additions and 196 deletions.
1 change: 1 addition & 0 deletions changelog/ndc2.1.breaking.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
In IRIS spectrograph reader, all extra coords except time have been moved to the meta object.
1 change: 1 addition & 0 deletions changelog/ndc2.1.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Create a new `~sunraster.instr.iris.IRISSGMeta` metadata object.
1 change: 1 addition & 0 deletions changelog/ndc2.2.breaking.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
In IRIS spectrograph read, move all metadata to the meta objects of the raster cubes.
1 change: 1 addition & 0 deletions changelog/ndc2.2.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Create new property `~sunraster.spectrogram.SpectrogramCube.celestial`, on `~sunraster.spectrogram.SpectrogramCube` to return a `~astropy.coordinates.SkyCoord` holding the celestial world coords of the pixels.
1 change: 1 addition & 0 deletions changelog/ndc2.3.breaking.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Remove extra_coords kwarg from `~sunraster.spectrogram.SpectrogramCube` __init__ in accordance with new ndcube 2.0 API. Extra coords can by added through the ndcube ExtraCoords.add API which is new in ndcube 2.0.
1 change: 1 addition & 0 deletions changelog/ndc2.3.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Create new property `~sunraster.spectrogram.SpectrogramSequence.celestial`, on `~sunraster.spectrogram.SpectrogramSequence` to return a `~astropy.coordinates.SkyCoord` holding the celestial world coords of the pixels.
304 changes: 198 additions & 106 deletions sunraster/instr/iris.py

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions sunraster/instr/spice.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@
from astropy.time import Time, TimeDelta
from astropy.wcs import WCS
from sunpy.coordinates import HeliographicStonyhurst
from ndcube import NDCollection
from ndcube import NDCollection, Meta

from sunraster.meta import Meta, SlitSpectrographMetaABC
from sunraster.meta import SlitSpectrographMetaABC
from sunraster import SpectrogramCube, SpectrogramSequence, RasterSequence


Expand Down
161 changes: 97 additions & 64 deletions sunraster/spectrogram.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import abc
import textwrap
import numbers
import textwrap
import warnings

import numpy as np
from astropy.time import TimeDelta
import astropy.units as u
import numpy as np
from astropy.time import Time
from ndcube.ndcube import NDCube
from ndcube.utils.cube import convert_extra_coords_dict_to_input_format

__all__ = ['SpectrogramCube']

Expand All @@ -20,7 +20,7 @@
"been undone since the unit does not include "
"inverse time. To undo exposure time correction "
"anyway, set 'force' kwarg to True.")
AXIS_NOT_FOUND_ERROR = " axis not found. If in extra_coords, axis name must be supported: "
AXIS_NOT_FOUND_ERROR = "axis not found. If in extra_coords, axis name must be supported:"

# Define supported coordinate names for coordinate properties.
SUPPORTED_LONGITUDE_NAMES = ["custom:pos.helioprojective.lon", "pos.helioprojective.lon",
Expand Down Expand Up @@ -87,6 +87,12 @@ def lat(self):
Return the latitude coordinates for each pixel.
"""

@abc.abstractproperty
def celestial(self):
"""
Return the celestial coordinates for each pixel.
"""

@abc.abstractmethod
def apply_exposure_time_correction(self, undo=False, force=False):
"""
Expand Down Expand Up @@ -163,11 +169,11 @@ class SpectrogramCube(NDCube, SpectrogramABC):
Default is False.
"""

def __init__(self, data, wcs, extra_coords=None, unit=None, uncertainty=None, meta=None,
def __init__(self, data, wcs, unit=None, uncertainty=None, meta=None,
mask=None, instrument_axes=None, copy=False, **kwargs):
# Initialize SpectrogramCube.
super().__init__(data, wcs=wcs, uncertainty=uncertainty, mask=mask, meta=meta,
unit=unit, extra_coords=extra_coords, copy=copy, **kwargs)
unit=unit, copy=copy, **kwargs)

# Determine labels and location of each key real world coordinate.
self_extra_coords = self.extra_coords
Expand All @@ -192,34 +198,45 @@ def __init__(self, data, wcs, extra_coords=None, unit=None, uncertainty=None, me
self.instrument_axes = np.asarray(instrument_axes, dtype=str)

def __str__(self):
if self._time_name:
try:
if self.time.isscalar:
time_period = self.time
else:
time_period = (str(self.time.min()), str(self.time.max()))
else:
time_period = None
if self._longitude_name:
if self.lon.isscalar:
lon_range = self.lon
times = self.time
time_period = Time([times.min(), times.max()]).iso
except ValueError as err:
if AXIS_NOT_FOUND_ERROR in err.args[0]:
time_period = None
else:
lon_range = u.Quantity([self.lon.min(), self.lon.max()])
else:
lon_range = None
if self._latitude_name:
if self.lat.isscalar:
lat_range = self.lat
raise err
try:
sc = self.celestial
component_names = dict(
[(item, key) for key, item in sc.representation_component_names.items()])
lon = getattr(sc, component_names["lon"])
lat = getattr(sc, component_names["lat"])
if sc.isscalar:
lon_range = lon
lat_range = lat
else:
lat_range = u.Quantity([self.lat.min(), self.lat.max()])
else:
lat_range = None
if self._spectral_name:
lon_range = u.Quantity([lon.min(), lon.max()])
lat_range = u.Quantity([lat.min(), lat.max()])
except ValueError as err:
if AXIS_NOT_FOUND_ERROR in err.args[0]:
lon_range = None
lat_range = None
else:
raise err
try:
if self.spectral_axis.isscalar:
spectral_range = self.spectral_axis
else:
spectral_range = u.Quantity([self.spectral_axis.min(), self.spectral_axis.max()])
else:
spectral_range = None
except ValueError as err:
if AXIS_NOT_FOUND_ERROR in err.args[0]:
spectral_range = None
else:
raise err
return (textwrap.dedent(f"""\
{self.__class__.__name__}
{"".join(["-"] * len(self.__class__.__name__))}
Expand All @@ -237,15 +254,6 @@ def __repr__(self):
def __getitem__(self, item):
# Slice SpectrogramCube using parent slicing.
result = super().__getitem__(item)

# As result is an NDCube, must put extra coord back into input format
# to create a SpectrogramCube.
if result.extra_coords is None:
extra_coords = None
else:
extra_coords = convert_extra_coords_dict_to_input_format(result.extra_coords,
result.missing_axes)

# Slice instrument_axes if it exists.
# If item is a slice, cube dimensionality is not reduced
# so instrument_axes need not be sliced.
Expand All @@ -265,14 +273,14 @@ def __getitem__(self, item):
# If slicing causes cube to be a scalar, set instrument_axes to None.
if len(instrument_axes) == 0:
instrument_axes = None

return self.__class__(result.data, wcs=result.wcs, uncertainty=result.uncertainty,
mask=result.mask, meta=result.meta, unit=result.unit,
extra_coords=extra_coords, missing_axes=result.missing_axes,
instrument_axes=instrument_axes)
result.instrument_axes = instrument_axes
return result

@property
def spectral_axis(self):
if not self._spectral_name:
self._spectral_name, self._spectral_loc = _find_axis_name(
SUPPORTED_SPECTRAL_NAMES, self.wcs.world_axis_physical_types, self.extra_coords)
if not self._spectral_name:
raise ValueError("Spectral" + AXIS_NOT_FOUND_ERROR +
f"{SUPPORTED_SPECTRAL_NAMES}")
Expand All @@ -281,37 +289,58 @@ def spectral_axis(self):
@property
def time(self):
if not self._time_name:
raise ValueError("Time" + AXIS_NOT_FOUND_ERROR +
f"{SUPPORTED_TIME_NAMES}")
times = self._get_axis_coord(self._time_name, self._time_loc)
if isinstance(times, (u.Quantity, TimeDelta)):
try:
if self.meta.date_reference:
times += self.meta.date_reference
except AttributeError:
pass
return times
self._time_name, self._time_loc = _find_axis_name(SUPPORTED_TIME_NAMES,
self.wcs.world_axis_physical_types,
self.extra_coords)
if not self._time_name:
raise ValueError(f"Time {AXIS_NOT_FOUND_ERROR} {SUPPORTED_TIME_NAMES}")
return Time(self._get_axis_coord(self._time_name, self._time_loc))

@property
def exposure_time(self):
if not self._exposure_time_name:
raise ValueError("Exposure time" + AXIS_NOT_FOUND_ERROR +
f"{SUPPORTED_EXPOSURE_NAMES}")
return self._get_axis_coord(self._exposure_time_name, self._exposure_time_loc)
exposure_times = None
i = 0
while exposure_times is None and i < len(SUPPORTED_EXPOSURE_NAMES):
exposure_times = self.meta.get(SUPPORTED_EXPOSURE_NAMES[i], None)
i += 1
return exposure_times

@property
def lon(self):
warnings.warn("'.lon' is deprecated. Please use '.celestial'.")
if not self._longitude_name:
raise ValueError("Longitude" + AXIS_NOT_FOUND_ERROR +
f"{SUPPORTED_LONGITUDE_NAMES}")
return self._get_axis_coord(self._longitude_name, self._longitude_loc)
return self._get_axis_coord_values(self._longitude_name, self._longitude_loc)

@property
def lat(self):
warnings.warn("'.lat' is deprecated. Please use '.celestial'.")
if not self._latitude_name:
raise ValueError("Latitude" + AXIS_NOT_FOUND_ERROR +
f"{SUPPORTED_LATITUDE_NAMES}")
return self._get_axis_coord(self._latitude_name, self._latitude_loc)
return self._get_axis_coord_values(self._latitude_name, self._latitude_loc)

@property
def celestial(self):
if not self._longitude_name:
self._longitude_name, self._longitude_loc = _find_axis_name(
SUPPORTED_LONGITUDE_NAMES, self.wcs.world_axis_physical_types, self.extra_coords)
if not self._latitude_name:
self._latitude_name, self._latitude_loc = _find_axis_name(
SUPPORTED_LATITUDE_NAMES, self.wcs.world_axis_physical_types, self.extra_coords)
if self._longitude_name:
celestial_name = self._longitude_name
celestial_loc = self._longitude_loc
elif self._latitude_name:
celestial_name = self._latitude_name
celestial_loc = self._latitude_loc
else:
raise ValueError(
f"Celestial {AXIS_NOT_FOUND_ERROR} "
f"{np.concatenate([SUPPORTED_LONGITUDE_NAMES, SUPPORTED_LATITUDE_NAMES])}")
return self._get_axis_coord(celestial_name, celestial_loc)


def apply_exposure_time_correction(self, undo=False, force=False):
# Get exposure time in seconds.
Expand All @@ -337,19 +366,23 @@ def apply_exposure_time_correction(self, undo=False, force=False):
new_data, new_uncertainty, new_unit = _calculate_exposure_time_correction(
self.data, self.uncertainty, self.unit, exposure_time_s, force=force)
# Return new instance of SpectrogramCube with correction applied/undone.

return self.__class__(
new_data, wcs=self.wcs,
extra_coords=convert_extra_coords_dict_to_input_format(self.extra_coords,
self.missing_axes),
unit=new_unit, uncertainty=new_uncertainty, meta=self.meta, mask=self.mask,
missing_axes=self.missing_axes)
result = copy.deepcopy(self)
result.data = new_data
result.uncertainty = new_uncertainty
result.unit = new_unit
return result

def _get_axis_coord(self, axis_name, coord_loc):
if coord_loc == "wcs":
return self.axis_world_coords(axis_name)[0]
elif coord_loc == "extra_coords":
return self.axis_world_coords(wcs=self.extra_coords[axis_name])[0]

def _get_axis_coord_values(self, axis_name, coord_loc):
if coord_loc == "wcs":
return self.axis_world_coords_values(axis_name)[0]
elif coord_loc == "extra_coords":
return self.extra_coords[axis_name]["value"]
return self.axis_world_coords_values(wcs=self.extra_coords[axis_name])[0]


def _find_axis_name(supported_names, world_axis_physical_types, extra_coords):
Expand Down
Loading

0 comments on commit ab2c574

Please sign in to comment.