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

Implement Superpixel on NDCube #450

Merged
merged 120 commits into from
Mar 1, 2023
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
120 commits
Select commit Hold shift + click to select a range
57e9a02
First version of NDCube.superpixel.
DanRyanIrish Jul 29, 2021
899f740
Merge branch 'resampled_wcs' into superpixel
DanRyanIrish Jul 29, 2021
820c670
Add #450 changelog.
DanRyanIrish Jul 29, 2021
5ff666f
Minor fixes.
DanRyanIrish Jul 29, 2021
0b0230d
Remove additive/subtractive factors from ResampledLowLevelWCS.
DanRyanIrish Jul 29, 2021
511f251
Merge branch 'resampled_wcs' into superpixel
DanRyanIrish Jul 29, 2021
90a1725
First version of superpixel that handles extra coords.
DanRyanIrish Jul 29, 2021
db9c504
Implement new interpolate method on ExtraCoords.
DanRyanIrish Jul 29, 2021
384cd85
Merge branch 'resampled_wcs' into superpixel
DanRyanIrish Jul 29, 2021
7634286
Codestyle fixes.
DanRyanIrish Jul 29, 2021
5095e5e
Ensure interpolated extra_coord mapping is an int.
DanRyanIrish Jul 29, 2021
32b66c5
Merge branch 'resampled_wcs' into superpixel
DanRyanIrish Jul 30, 2021
e6b91dc
Merge branch 'resampled_wcs' into superpixel
DanRyanIrish Jul 30, 2021
fbe2cb4
Test NDCube.superpixel.
DanRyanIrish Jul 30, 2021
4154001
Minor bug fixes.
DanRyanIrish Jul 30, 2021
e978354
Merge branch 'resampled_wcs' into superpixel
DanRyanIrish Jul 30, 2021
f986fec
Merge branch 'resampled_wcs' into superpixel
DanRyanIrish Jul 30, 2021
20c3684
Codestyle fixes.
DanRyanIrish Jul 30, 2021
bf0a2a0
Merge branch 'resampled_wcs' into superpixel
DanRyanIrish Aug 4, 2021
ebc22ae
minor changes
DanRyanIrish Aug 4, 2021
6472b90
Merge branch 'main' of https://github.com/sunpy/ndcube into superpixel
DanRyanIrish Aug 6, 2021
34ff5ca
Merge branch 'main' of https://github.com/sunpy/ndcube into superpixel
DanRyanIrish Aug 14, 2021
c12bdab
Update ndcube/extra_coords/extra_coords.py
DanRyanIrish Aug 14, 2021
8660936
Add support for SkyCoords to ExtraCoords.interpolate.
DanRyanIrish Aug 21, 2021
4096adf
Merge branch 'main' of https://github.com/sunpy/ndcube into superpixel
DanRyanIrish Aug 21, 2021
b6b2f05
Merge branch 'main' of https://github.com/sunpy/ndcube into superpixel
DanRyanIrish Aug 21, 2021
7a367f7
Fix bug in ExtraCoords.interpolate when no limit given.
DanRyanIrish Sep 1, 2021
4ebdc1a
Merge branch 'main' of https://github.com/sunpy/ndcube into superpixel
DanRyanIrish Sep 8, 2021
f39dc02
Stop NDCube.superpixel crashing when there are no extra_coords.
DanRyanIrish Sep 8, 2021
987c275
Add changelog for new ExtraCoords.is_empty property.
DanRyanIrish Sep 8, 2021
8ff26a7
Merge branch 'main' of https://github.com/sunpy/ndcube into superpixel
DanRyanIrish Nov 3, 2021
4d2f995
Minor comment improvements
DanRyanIrish Nov 4, 2021
cd0b0cd
Merge branch 'main' of https://github.com/sunpy/ndcube into superpixel
DanRyanIrish Nov 4, 2021
730fbb3
Merge branch 'superpixel' of https://github.com/DanRyanIrish/ndcube i…
DanRyanIrish Nov 4, 2021
4d0346c
Add changelog for ExtraCoords.interpolate.
DanRyanIrish Nov 4, 2021
74ea1ff
Merge branch 'main' of https://github.com/sunpy/ndcube into superpixel
DanRyanIrish Nov 10, 2021
e9af6d1
Merge branch 'main' of https://github.com/sunpy/ndcube into superpixel
DanRyanIrish Nov 18, 2021
d7d8ed4
Merge branch 'main' of https://github.com/sunpy/ndcube into superpixel
DanRyanIrish Aug 26, 2022
a03c901
Merge branch 'main' of https://github.com/sunpy/ndcube into superpixel
DanRyanIrish Sep 16, 2022
ec93b15
Made superpixel only support certain aggregation functions.
DanRyanIrish Sep 16, 2022
79cd54b
First draft implementation of error propagation in NDCube.superpixel.
DanRyanIrish Sep 16, 2022
a9f956d
Generalize interpolation of SkyCoord extra coords.
DanRyanIrish Sep 16, 2022
e80d767
Changed func_name kwarg to method in NDCube.superpixel.
DanRyanIrish Sep 18, 2022
aa6deb0
Merge branch 'main' of https://github.com/sunpy/ndcube into superpixel
DanRyanIrish Sep 18, 2022
6ea3a44
Merge branch 'main' of https://github.com/sunpy/ndcube into superpixel
DanRyanIrish Sep 21, 2022
1557941
Add missing frame kwarg to a SkyCoord definition
DanRyanIrish Sep 21, 2022
56e313d
Set initial masked uncertainty to 0 and indexing correction.
DanRyanIrish Sep 24, 2022
fc189cf
Update calling NDCube.superpixel in test.
DanRyanIrish Sep 24, 2022
488fc6b
Correct NDCube.superpixel type check for uncert propagation. Also add…
DanRyanIrish Sep 30, 2022
cf0f3b2
Merge branch 'main' of https://github.com/sunpy/ndcube into superpixel
DanRyanIrish Dec 19, 2022
a49737d
Remove unneeded for loop.
DanRyanIrish Dec 19, 2022
b8414ff
Start writing tests for superpixel error propagation.
DanRyanIrish Dec 19, 2022
c23b4a7
Merge branch 'main' of https://github.com/sunpy/ndcube into superpixel
DanRyanIrish Dec 19, 2022
0cb4c24
Test error propagation in NDCube.superpixel.
DanRyanIrish Dec 19, 2022
90bce97
Switch ExtraCoords.interpolate to use numpy.interp.
DanRyanIrish Dec 19, 2022
74fa8c8
Fix codestyle
DanRyanIrish Dec 19, 2022
af9ca37
Rename NDCube.superpixel to rebin.
DanRyanIrish Dec 19, 2022
75a55a6
Propagate uncerts in min/max methods of NDCube.rebin.
DanRyanIrish Dec 19, 2022
e16cfd9
Add std dev support to NDCube.rebin.
DanRyanIrish Dec 19, 2022
27e4131
Add test for max version of NDCube.rebin.
DanRyanIrish Dec 20, 2022
e327cdf
Remove another scipy interpolate usage previously missed.
DanRyanIrish Dec 20, 2022
aab6650
Change how NDCube.rebin gets uncerts for min/max versions.
DanRyanIrish Dec 22, 2022
c4df4ca
Refactors uncertainty calculation for NDCube.rebin.
DanRyanIrish Jan 19, 2023
8062ee2
Add option to include/exclude masked values in NDCube.rebin.
DanRyanIrish Jan 20, 2023
9349fcd
Make np.mean default op for NDCube.rebin and general code improvements
DanRyanIrish Jan 24, 2023
db39ea7
Add test for NDCube.rebin with exclude_masked_values=False and update…
DanRyanIrish Jan 24, 2023
10a74d9
Fixes codestyle.
DanRyanIrish Jan 24, 2023
3bf58f0
Updates NDCube.rebin docstring.
DanRyanIrish Jan 24, 2023
ab89e27
Minor code tidying
DanRyanIrish Jan 25, 2023
f374b28
Move rebin error propagation function to utils.
DanRyanIrish Jan 25, 2023
86648b4
Update ndcube/ndcube.py
DanRyanIrish Jan 26, 2023
6b985b0
Pass kwargs to ExtraCoords.interpolate to numpy.interp.
DanRyanIrish Jan 26, 2023
0795d9a
Merge branch 'main' of https://github.com/sunpy/ndcube into superpixel
DanRyanIrish Jan 31, 2023
2f7275b
Fix codestyle.
DanRyanIrish Jan 31, 2023
e46017b
Merge branch 'main' of https://github.com/sunpy/ndcube into superpixel
DanRyanIrish Feb 17, 2023
4770ad4
Merge branch 'main' of https://github.com/sunpy/ndcube into superpixel
DanRyanIrish Feb 18, 2023
719cbe2
Rename propagate_uncertainty kwarg to propagate_uncertainties
DanRyanIrish Feb 18, 2023
a6d7380
Move rebin() from NDCubeBase to NDCube.
DanRyanIrish Feb 18, 2023
8f4564b
Add handle_mask kwarg to NDCube.rebin API.
DanRyanIrish Feb 20, 2023
8b5fb14
Merge branch 'superpixel' of https://github.com/DanRyanIrish/ndcube i…
DanRyanIrish Feb 20, 2023
2c0be40
Add dask support ot NDCube.rebin
DanRyanIrish Feb 21, 2023
9cf76c9
Make default NDCube.rebin propagation dask-friendly.
DanRyanIrish Feb 21, 2023
b8a5abe
Update ndcube/ndcube.py from code review
DanRyanIrish Feb 21, 2023
7901488
Fix codestyle
DanRyanIrish Feb 21, 2023
35e8f06
Move try import dask out of NDCube.rebin to module level dict.
DanRyanIrish Feb 21, 2023
178d4ae
Merge branch 'main' of https://github.com/sunpy/ndcube into superpixel
DanRyanIrish Feb 21, 2023
62ca28f
Fix codestyle
DanRyanIrish Feb 21, 2023
674d978
Apply suggestions from code review
DanRyanIrish Feb 22, 2023
d708d74
Add interpolate methods to TableCoordinate classes.
DanRyanIrish Feb 22, 2023
e670928
Add resample method to ExtraCoords.
DanRyanIrish Feb 22, 2023
91868cb
Add resample method to ExtraCoords.
DanRyanIrish Feb 23, 2023
0e8d0c7
Update changelog for PR 450.
DanRyanIrish Feb 23, 2023
482ee26
Remove ExtraCoords.interpolate as superceded by ExtraCoord.resample.
DanRyanIrish Feb 23, 2023
aee99b6
Fix bug in ExtraCoord.resample so upper edge of lookup table coords c…
DanRyanIrish Feb 23, 2023
eff2f7d
Refactor SkyCoordTableCoordinate.interpolate.
DanRyanIrish Feb 25, 2023
8c444a6
Update QuantityTableCoord interpolate and MultiTabCoord init.
DanRyanIrish Feb 26, 2023
03ba1c6
Apply changes from code review.
DanRyanIrish Feb 26, 2023
7ff8482
Fixes bugs in TableCoord interpolation.
DanRyanIrish Feb 27, 2023
b1a0473
Remove mesh support from QuantityTableCoordinate.
DanRyanIrish Feb 27, 2023
87d6cb8
Add back mesh support for SkyCoordTableCoord including in interpolate…
DanRyanIrish Feb 27, 2023
ae6bc31
Update minimum version test dependencies.
DanRyanIrish Feb 27, 2023
01b809a
Ignore warnings for oldestdeps tests.
DanRyanIrish Feb 27, 2023
4cbb4e3
Undo warning ignoring as it didnt work.
DanRyanIrish Feb 27, 2023
77d293f
Should trigger no warning
nabobalis Feb 27, 2023
577b539
actual fix
nabobalis Feb 27, 2023
c783738
Merge branch 'main' of https://github.com/sunpy/ndcube into superpixel
DanRyanIrish Feb 28, 2023
8f550e5
Merge branch 'superpixel' of https://github.com/DanRyanIrish/ndcube i…
DanRyanIrish Feb 28, 2023
d7f0007
Merge branch 'main' of https://github.com/sunpy/ndcube into superpixel
DanRyanIrish Feb 28, 2023
cc4b8f7
Add tests for NDCube.rebin errors.
DanRyanIrish Feb 28, 2023
ce020b6
Add tests for NDCube.rebin warnings.
DanRyanIrish Feb 28, 2023
d8ce29e
Add test for scalar inputs to ExtraCoords.resample.
DanRyanIrish Feb 28, 2023
26a7812
Add tests for correct errors raised by ExtraCoords.resample
DanRyanIrish Feb 28, 2023
3a7533f
Add test for resampling wcs-based ExtraCoords.
DanRyanIrish Feb 28, 2023
6d39cdd
Apply suggestions from code review
DanRyanIrish Feb 28, 2023
b9bbf10
Add tests TableCoord interpolate raises correct errors.
DanRyanIrish Feb 28, 2023
d59b208
Add tests for propagate_rebin_uncertainties().
DanRyanIrish Feb 28, 2023
e906890
Fix codestyle.
DanRyanIrish Feb 28, 2023
9927ad9
Update handling of mesh in SkyCoordTableCoordinate.interpolate.
DanRyanIrish Feb 28, 2023
8b82817
Changes suggested by code review.
DanRyanIrish Feb 28, 2023
5605aef
Rename use_masked_values kwarg to operation_ignores_mask.
DanRyanIrish Mar 1, 2023
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
1 change: 1 addition & 0 deletions changelog/450.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Implement a new `ndcube.NDCube.superpixel` method to combine integer numbers of pixels along each axis into larger pixels.
7 changes: 6 additions & 1 deletion ndcube/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,15 @@ def gen_ndcube_3d_l_ln_lt_ectime(wcs_3d_lt_ln_l, time_axis, time_base, global_co
wcs_3d_lt_ln_l.array_shape = shape
data_cube = data_nd(shape)
mask = data_cube < 0
meta = {"message": "hello world"}
unit = u.ph
extra_coords = time_extra_coords(shape, time_axis, time_base)
cube = NDCube(data_cube,
wcs_3d_lt_ln_l,
mask=mask,
uncertainty=data_cube)
uncertainty=data_cube,
meta=meta,
unit=unit)
cube._extra_coords = extra_coords

if global_coords:
Expand Down Expand Up @@ -375,6 +379,7 @@ def ndcube_3d_l_ln_lt_ectime(wcs_3d_lt_ln_l):
1,
Time('2000-01-01', format='fits', scale='utc'))


@pytest.fixture
def ndcube_2d_ln_lt(wcs_2d_lt_ln):
shape = (10, 12)
Expand Down
61 changes: 60 additions & 1 deletion ndcube/extra_coords/extra_coords.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import astropy.units as u
import numpy as np
import scipy.interpolate
from astropy.coordinates import SkyCoord
from astropy.time import Time
from astropy.wcs.wcsapi import BaseHighLevelWCS
Expand Down Expand Up @@ -215,7 +216,7 @@ def add(self, name, array_dimension, lookup_table, physical_types=None, **kwargs

# Sort the LUTs so that the mapping and the wcs are ordered in pixel dim order
self._lookup_tables = list(sorted(self._lookup_tables,
key=lambda x: x[0] if isinstance(x[0], int) else x[0][0]))
key=lambda x: x[0] if isinstance(x[0], Integral) else x[0][0]))

@property
def _name_lut_map(self):
Expand Down Expand Up @@ -391,6 +392,64 @@ def dropped_world_dimensions(self):

return dict()

def interpolate(self, new_array_grids, ndcube=None, **kwargs):
DanRyanIrish marked this conversation as resolved.
Show resolved Hide resolved
"""Interpolate all extra coords to new array index grids.

One new array index grid must be supplied for each array axis in array-axis order.
DanRyanIrish marked this conversation as resolved.
Show resolved Hide resolved

Parameters
----------
new_array_grids: iterable of array-likes or `None`
The array index values at which the the new values of the coords are desired.
An array for each array axis must be given.
If None given for an axis, no interpolation is performed for coords
corresponding to that axis.

ndcube: `~ndcube.NDCube `
DanRyanIrish marked this conversation as resolved.
Show resolved Hide resolved
The NDCube instance with which the output ExtraCoords object is associated.
DanRyanIrish marked this conversation as resolved.
Show resolved Hide resolved

Returns
-------
new_ec: `~ndcube.extra_coords.ExtraCoords`
A new ExtraCoords object holding the interpolated coords.

"""
cube_dims = self._ndcube.dimensions.value.astype(int)
DanRyanIrish marked this conversation as resolved.
Show resolved Hide resolved
naxes = len(cube_dims)
if len(new_array_grids) != naxes:
raise ValueError("new_array_grids must have an entry for each array axis."
f"num. array axes: {naxes}; "
f"num. new array grids: {len(new_array_grids)}")
array_order_mapping = convert_between_array_and_pixel_axes(np.asarray(self.mapping), naxes)
old_array_grids = [None if new_grid is None else np.arange(dim)
for new_grid, dim in zip(new_array_grids, cube_dims)]
new_ec = type(self)(ndcube)
for key, aom, lut in zip(self.keys(), array_order_mapping, self._lookup_tables):
if new_array_grids[aom] is None:
new_coord = lut[1]
else:
table = lut[1].table
if isinstance(table, tuple):
table = table[0]
if isinstance(table, Time):
table_values = table.mjd
elif isinstance(table, SkyCoord):
pass
elif isinstance(table, u.Quantity):
table_values = table.value
else:
raise TypeError(f"Unrecognized lookup table type: {table}")
lut_interp = scipy.interpolate.interp1d(old_array_grids[aom], table_values)
DanRyanIrish marked this conversation as resolved.
Show resolved Hide resolved
new_table_values = lut_interp(new_array_grids[aom])
if isinstance(table, Time):
new_coord = Time(new_table_values, scale=table.scale, format="mjd")
elif isinstance(table, SkyCoord):
pass
else:
new_coord = u.Quantity(new_table_values, unit=table.unit)
new_ec.add(key, int(aom), new_coord, physical_types=lut[1].physical_types)
return new_ec

def __str__(self):
elements = [f"{', '.join(table.names)} ({axes}): {table}" for axes, table in self._lookup_tables]
return f"ExtraCoords({', '.join(elements)})"
Expand Down
123 changes: 122 additions & 1 deletion ndcube/ndcube.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
from ndcube.ndcube_sequence import NDCubeSequence
from ndcube.utils.wcs_high_level_conversion import high_level_objects_to_values, values_to_high_level_objects
from ndcube.visualization import PlotterDescriptor
from ndcube.wcs.wrappers import CompoundLowLevelWCS
from ndcube.wcs.wrappers import CompoundLowLevelWCS, ResampledLowLevelWCS

__all__ = ['NDCubeABC', 'NDCubeBase', 'NDCube']

Expand Down Expand Up @@ -782,6 +782,127 @@ def reproject_to(self, target_wcs, shape_out=None, order='bilinear', output_arra

return resampled_cube

def superpixel(self, superpixel_shape, func=np.sum, new_unit=False):
"""Downsample array by creating non-overlapping superpixels.

Values in superpixels are determined applying a function to the pixel
values within it. The number of pixels in each superpixel in each
dimension is given is given by the superpixel_shape input.
DanRyanIrish marked this conversation as resolved.
Show resolved Hide resolved
This must be an integer fraction of the cube's array size in each dimension.

This method currently does not handle uncertainty or extra_coords.
DanRyanIrish marked this conversation as resolved.
Show resolved Hide resolved
They are dropped from the output.

Parameters
----------
data : array-like
The data array to be downsampled

superpixel_shape : array-like
The number of pixels in a superpixel in each dimension.
Must be the same length as number of dimensions in data.
Each element must be in int. If they are not they will be rounded
to the nearest int.

func : (optional)
Function applied to the data to derive values of the superpixels.
The function must take an array as its first argument and
support the axis kwarg with the same meaning as a numpy axis
kward (see the description of `~numpy.sum` for an example.)
Default: `~numpy.sum`

new_unit : `str` or `astropy.units.Unit` (optional)
The unit of the new data. This might need to change based on how
func alters the data.
Default: False, i.e. self.unit is used. (False is used as default
flag because None is a valid new_unit option.)

Returns
-------
new_cube: `NDCube`
The resolution-degraded cube.

References
----------
https://mail.scipy.org/pipermail/numpy-discussion/2010-July/051760.html

Notes
-----
Superpixels are created by reshaping the N-D array to a 2N-D array and
applying the function over the odd-numbered axes. An example of this
applied the data only is the following. Let's say you have an array::

x = np.array([[0, 0, 0, 1, 1, 1],
[0, 0, 1, 1, 0, 0],
[1, 1, 0, 0, 1, 1],
[0, 0, 0, 0, 1, 1],
[1, 0, 1, 0, 1, 1],
[0, 0, 1, 0, 0, 0]])

and you want to sum over 2x2 non-overlapping sub-arrays. This summing can
be done by reshaping the array::

y = x.reshape(3,2,3,2)

and then summing over the 1st and third directions::

y2 = y.sum(axis=3).sum(axis=1)

which gives the expected array::

array([[0, 3, 2],
[2, 0, 4],
[1, 2, 2]])
"""
# Sanitize input.
# Make sure the input superpixel dimensions are integers.
superpixel_shape = np.rint(superpixel_shape).astype(int)
offsets = (superpixel_shape - 1) / 2
if all(superpixel_shape == 1):
return deepcopy(self)
# Ensure superpixel_size has right number of entries and each entry is an
# integer fraction of the array shape in each dimension.
data_shape = self.dimensions.value.astype(int)
naxes = len(data_shape)
if len(superpixel_shape) != naxes:
raise ValueError("superpixel_shape must have an entry for each array axis.")
if (np.mod(data_shape, superpixel_shape) != 0).any():
raise ValueError(
"superpixel shape must be an integer fraction of the data shape in each dimension. "
f"data shape: {data_shape}; superpixel shape: {superpixel_shape}")
if new_unit is False:
new_unit = self.unit

# Reshape array and apply function over odd axes to generate array of superpixels.
if self.mask is None:
data = self.data
new_mask = None
else:
data = np.ma.masked_array(self.data, self.mask)
reshape = np.empty(data_shape.size + superpixel_shape.size, dtype=int)
reshape[0::2] = data_shape / superpixel_shape
reshape[1::2] = superpixel_shape
new_data = data.reshape(tuple(reshape))
for i in range(len(reshape) - 1, 0, -2):
new_data = func(new_data, axis=i)
if self.mask is not None:
new_mask = new_data.mask
new_data = new_data.data

# Resample WCS
new_wcs = ResampledLowLevelWCS(self.wcs.low_level_wcs, superpixel_shape[::-1])

# Reform NDCube.
new_cube = type(self)(new_data, new_wcs, mask=new_mask, meta=self.meta, unit=new_unit)
DanRyanIrish marked this conversation as resolved.
Show resolved Hide resolved

# Reconstitute extra coords
new_array_grids = [None if superpixel_shape[i] == 1 else
np.arange(offsets[i], data_shape[i] + offsets[i], superpixel_shape[i])
for i in range(naxes)]
new_cube._extra_coords = self.extra_coords.interpolate(new_array_grids, new_cube)

return new_cube


class NDCube(NDCubeBase, astropy.nddata.NDArithmeticMixin):
"""
Expand Down
41 changes: 41 additions & 0 deletions ndcube/tests/test_ndcube.py
Original file line number Diff line number Diff line change
Expand Up @@ -630,3 +630,44 @@ def test_wcs_type_after_init(ndcube_3d_ln_lt_l, wcs_3d_l_lt_ln):
cube = NDCube(ndcube_3d_ln_lt_l.data[slices], low_level_wcs)
# Check the WCS has been converted to high level but NDCube init.
assert isinstance(cube.wcs, BaseHighLevelWCS)


def test_superpixel(ndcube_3d_l_ln_lt_ectime):
# Execute superpixel.
cube = ndcube_3d_l_ln_lt_ectime[:, 1:]
superpixel_shape = (10, 2, 1)
output = cube.superpixel(superpixel_shape, func=np.sum)
output_sc, output_spec = output.axis_world_coords(wcs=output.wcs)
output_time, = output.axis_world_coords(wcs=output.extra_coords)

# Build expected cube contents.
expected_data = np.array([[3840, 3860, 3880, 3900, 3920, 3940, 3960, 3980],
[4160, 4180, 4200, 4220, 4240, 4260, 4280, 4300]])
expected_mask = np.zeros(expected_data.shape, dtype=bool)
expected_uncertainty = None
expected_unit = cube.unit
expected_meta = cube.meta
expected_Tx = np.array([[9.99999999, 19.99999994, 29.99999979, 39.9999995,
49.99999902, 59.99999831, 69.99999731, 79.99999599],
[9.99999999, 19.99999994, 29.99999979, 39.9999995,
49.99999902, 59.99999831, 69.99999731, 79.99999599]]) * u.arcsec
expected_Ty = np.array([[-14.99999996, -14.9999999, -14.99999981, -14.99999969,
-14.99999953, -14.99999934, -14.99999911, -14.99999885],
[-4.99999999, -4.99999998, -4.99999995, -4.9999999,
-4.99999985, -4.99999979, -4.99999971, -4.99999962]]) * u.arcsec
expected_spec = SpectralCoord([1.02e-09], unit=u.m)
expected_time = Time([51544.00104167, 51544.00243056], format="mjd", scale="utc")

# Confirm output is as expected.
assert (output.dimensions.value == np.array([1, 2, 8])).all()
assert (output.data == expected_data).all()
assert (output.mask == expected_mask).all()
assert output.uncertainty == expected_uncertainty
assert output.unit == expected_unit
assert output.meta == expected_meta
assert u.allclose(output_sc.Tx, expected_Tx)
assert u.allclose(output_sc.Ty, expected_Ty)
assert u.allclose(output_spec, expected_spec)
assert output_time.scale == expected_time.scale
assert output_time.format == expected_time.format
assert np.allclose(output_time.value, expected_time.value)