Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WCS information lost in unit conversion of LowerDimensionalObject #256

Merged
merged 3 commits into from
Jan 6, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions spectral_cube/lower_dimensional_structures.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ def wcs(self):
def meta(self):
return self._meta

@property
def mask(self):
return self._mask

@property
def header(self):
header = self._header
Expand Down Expand Up @@ -68,6 +72,52 @@ def write(self, filename, format=None, overwrite=False):
raise ValueError("Unknown format '{0}' - the only available "
"format at this time is 'fits'")

def to(self, unit, equivalencies=[]):
"""
Return a new ``LowerDimensionalObject'' of the same class with the
specified unit. See `astropy.units.Quantity.to` for further details.
"""
converted_array = u.Quantity.to(self, unit,
equivalencies=equivalencies).value

# use private versions of variables, not the generated property
# versions
# Not entirely sure the use of __class__ here is kosher, but we do want
# self.__class__, not super()
new = self.__class__(value=converted_array, unit=unit, copy=True,
wcs=self._wcs, meta=self._meta, mask=self._mask,
header=self._header)

return new

def __getitem__(self, key):
"""
Return a new ``LowerDimensionalObject'' of the same class while keeping
other properties fixed.
"""
new_qty = super(LowerDimensionalObject, self).__getitem__(key)

if new_qty.ndim < 2:
# do not return a projection
return u.Quantity(new_qty)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure what should be returned here. If a Quantity is returned, then you still lose all of the WCS info. If you have a PV slice, you would want a OneDSpectrum when slicing along the spectral axis, but something else when slicing in the spatial dimension.

Could we check for the axis type when a slice reduces ndim<2? A OneDSpectrum would be returned when the remaining axis is spectral. I'm not sure how to treat 1D spatial slices. Treat it like a mock-projection with a 1 pixel second dimension or define a spatial 1D slice class? Is there a use case for such a thing?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There might be. I chose to ignore it for now because it's not trivial, but I think we could inspect the WCS and see if one of the axes is spectral; if so, we'd then return the OneDSpectrum

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But I think for single pixels, we really want to drop all the way to a Quantity with no wcs information


if self._wcs is not None:
newwcs = self._wcs[key]
else:
newwcs = None

new = self.__class__(value=new_qty.value,
unit=new_qty.unit,
copy=False,
wcs=newwcs,
meta=self._meta,
mask=self._mask,
header=self._header)

return new



class Projection(LowerDimensionalObject):

def __new__(cls, value, unit=None, dtype=None, copy=True, wcs=None,
Expand Down
21 changes: 20 additions & 1 deletion spectral_cube/tests/test_projection.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,34 @@
from .helpers import assert_allclose
from ..lower_dimensional_structures import Projection

def test_slices_of_projections_not_projections():
# slices of projections that have <2 dimensions should not be projections
image = np.ones((2, 2)) * u.Jy
p = Projection(image, copy=False)

assert not isinstance(p[0,0], Projection)
assert not isinstance(p[0], Projection)


def test_copy_false():
image = np.ones((12, 12)) * u.Jy
p = Projection(image, copy=False)
image[3,4] = 2 * u.Jy
assert_allclose(p[3,4], 2 * u.Jy)


def test_write(tmpdir):
image = np.ones((12, 12)) * u.Jy
p = Projection(image)
p.write(tmpdir.join('test.fits').strpath)

def test_preserve_wcs_to():
# regression for #256
image = np.ones((12, 12)) * u.Jy
p = Projection(image, copy=False)
image[3,4] = 2 * u.Jy

p2 = p.to(u.mJy)
assert_allclose(p[3,4], 2 * u.Jy)
assert_allclose(p[3,4], 2000 * u.mJy)

assert p2.wcs == p.wcs