Skip to content

Commit 161b703

Browse files
committed
Improve and add more viewport test
1 parent 2ae9e2a commit 161b703

File tree

1 file changed

+88
-16
lines changed

1 file changed

+88
-16
lines changed

src/astro_image_display_api/widget_api_test.py

Lines changed: 88 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import numpy as np
66

77
from astropy.io import fits
8-
from astropy.nddata import NDData
8+
from astropy.nddata import CCDData, NDData
99
from astropy.table import Table, vstack
1010
from astropy import units as u
1111
from astropy.wcs import WCS
@@ -31,6 +31,7 @@ def wcs(self):
3131
w = WCS(naxis=2)
3232

3333
# Set up an "Airy's zenithal" projection
34+
# Note: WCS is 1-based, not 0-based
3435
w.wcs.crpix = [-234.75, 8.3393]
3536
w.wcs.cdelt = np.array([-0.066667, 0.066667])
3637
w.wcs.crval = [0, -90]
@@ -113,12 +114,12 @@ def test_set_get_center_xy(self, data):
113114

114115
def test_set_get_center_world(self, data, wcs):
115116
self.image.load_image(NDData(data=data, wcs=wcs), image_label='test')
116-
self.image.set_viewport(center=SkyCoord(*wcs.crval, unit='deg'), image_label='test')
117+
self.image.set_viewport(center=SkyCoord(*wcs.wcs.crval, unit='deg'), image_label='test')
117118

118119
vport = self.image.get_viewport(image_label='test')
119120
assert isinstance(vport['center'], SkyCoord)
120-
assert vport['center'].ra.deg == pytest.approx(wcs.crval[0])
121-
assert vport['center'].dec.deg == pytest.approx(wcs.crval[1])
121+
assert vport['center'].ra.deg == pytest.approx(wcs.wcs.crval[0])
122+
assert vport['center'].dec.deg == pytest.approx(wcs.wcs.crval[1])
122123

123124
def test_set_get_fov_pixel(self, data):
124125
# Set data first, since that is needed to determine zoom level
@@ -136,10 +137,14 @@ def test_set_get_fov_world(self, data, wcs):
136137
# Set the FOV in world coordinates
137138
self.image.set_viewport(fov=0.1 * u.deg, image_label='test')
138139
vport = self.image.get_viewport(image_label='test')
139-
assert isinstance(vport['fov'], SkyCoord)
140-
assert vport['fov'].deg == pytest.approx(0.1)
140+
assert isinstance(vport['fov'], u.Quantity)
141+
assert len(np.atleast_1d(vport['fov'])) == 1
142+
assert vport['fov'].unit.physical_type == 'angle'
143+
fov_degree = vport['fov'].to(u.degree).value
144+
assert fov_degree == pytest.approx(0.1)
141145

142146
def test_set_get_viewport_errors(self, data, wcs):
147+
# Test several of the expected errors that can be raised
143148
self.image.load_image(NDData(data=data, wcs=wcs), image_label='test')
144149

145150
# fov can be float or an angular Qunatity
@@ -166,25 +171,64 @@ def test_set_get_viewport_errors(self, data, wcs):
166171
# If there are multiple images loaded, the image_label must be provided
167172
self.image.load_image(data, image_label='another test')
168173

169-
with pytest.raises(ValueError, match='[Ii]mage label.*not provided'):
174+
with pytest.raises(ValueError, match='Multiple catalog styles defined'):
170175
self.image.get_viewport()
171176

172-
def test_viewport_is_defined_aster_loading_image(self, data):
177+
# setting sky_or_pixel to something other than 'sky' or 'pixel' or None
178+
# should raise an error
179+
with pytest.raises(ValueError, match='[Ss]ky_or_pixel must be'):
180+
self.image.get_viewport(sky_or_pixel='not a valid value')
181+
182+
def test_set_get_viewport_errors_because_no_wcs(self, data):
183+
# Check that errors are raised when they should be when calling
184+
# get_viewport when no WCS is present.
185+
186+
# Load the data without a WCS
187+
self.image.load_image(data, image_label='test')
188+
189+
# Set the viewport with a SkyCoord center
190+
with pytest.raises(TypeError, match='Center must be a tuple'):
191+
self.image.set_viewport(center=SkyCoord(ra=10, dec=20, unit='deg'), image_label='test')
192+
193+
# Set the viewport with a Quantity fov
194+
with pytest.raises(TypeError, match='FOV must be a float'):
195+
self.image.set_viewport(fov=100 * u.arcmin, image_label='test')
196+
197+
# Try getting the viewport as sky
198+
with pytest.raises(ValueError, match='WCS is not set'):
199+
self.image.get_viewport(image_label='test', sky_or_pixel='sky')
200+
201+
@pytest.mark.parametrize("world", [True, False])
202+
def test_viewport_is_defined_after_loading_image(self, tmp_path, data, wcs, world):
173203
# Check that the viewport is set to a default value when an image
174204
# is loaded, even if no viewport is explicitly set.
175-
self.image.load_image(data)
205+
206+
# Load the image from FITS to ensure that at least one image with WCS
207+
# has been loaded from FITS.
208+
wcs = wcs if world else None
209+
ccd = CCDData(data=data, unit="adu", wcs=wcs)
210+
211+
ccd_path = tmp_path / 'test.fits'
212+
ccd.write(ccd_path)
213+
self.image.load_image(ccd_path)
176214

177215
# Getting the viewport should not fail...
178216
vport = self.image.get_viewport()
179217

180218
assert 'center' in vport
181-
# No world, so center should be a tuple
182-
assert isinstance(vport['center'], tuple)
219+
183220
assert 'fov' in vport
184-
# fov should be a float since no WCS
185-
assert isinstance(vport['fov'], numbers.Real)
186221
assert 'image_label' in vport
187222
assert vport['image_label'] is None
223+
if world:
224+
assert isinstance(vport['center'], SkyCoord)
225+
# fov should be a Quantity since WCS is present
226+
assert isinstance(vport['fov'], u.Quantity)
227+
else:
228+
# No world, so center should be a tuple
229+
assert isinstance(vport['center'], tuple)
230+
# fov should be a float since no WCS
231+
assert isinstance(vport['fov'], numbers.Real)
188232

189233
def test_set_get_view_port_no_image_label(self, data):
190234
# If there is only one image, the viewport should be able to be set
@@ -232,13 +276,15 @@ def test_get_viewport_sky_or_pixel(self, data, wcs):
232276
# Load the data with a WCS
233277
self.image.load_image(NDData(data=data, wcs=wcs), image_label='test')
234278

235-
input_center = SkyCoord(*wcs.val, unit='deg')
279+
input_center = SkyCoord(*wcs.wcs.crval, unit='deg')
236280
input_fov = 2 * u.arcmin
237281
self.image.set_viewport(center=input_center, fov=input_fov, image_label='test')
238282

239283
# Get the viewport in pixel coordinates
240284
vport_pixel = self.image.get_viewport(image_label='test', sky_or_pixel='pixel')
241-
assert vport_pixel['center'] == wcs.crpix
285+
# The WCS set up for the tests is 1-based, rather than the usual 0-based,
286+
# so we need to subtract 1 from the pixel coordinates.
287+
assert all(vport_pixel['center'] == (wcs.wcs.crpix - 1))
242288
# tbh, not at all sure what the fov should be in pixel coordinates,
243289
# so just check that it is a float.
244290
assert isinstance(vport_pixel['fov'], numbers.Real)
@@ -266,7 +312,33 @@ def test_get_viewport_no_sky_or_pixel(self, data, wcs, sky_or_pixel):
266312
assert vport['fov'].unit.physical_type == "angle"
267313
case 'pixel':
268314
assert isinstance(vport['center'], tuple)
269-
assert isinstance(vport['fov'], float)
315+
assert isinstance(vport['fov'], numbers.Real)
316+
317+
def test_get_viewport_with_wcs_set_pixel_or_world(self, data, wcs):
318+
# Check that the viewport can be retrieved in both pixel and world
319+
# after setting with the opposite if the WCS is set.
320+
# Load the data with a WCS
321+
self.image.load_image(NDData(data=data, wcs=wcs), image_label='test')
322+
323+
# Set the viewport in world coordinates
324+
input_center = SkyCoord(*wcs.wcs.crval, unit='deg')
325+
input_fov = 2 * u.arcmin
326+
self.image.set_viewport(center=input_center, fov=input_fov, image_label='test')
327+
328+
# Get the viewport in pixel coordinates
329+
vport_pixel = self.image.get_viewport(image_label='test', sky_or_pixel='pixel')
330+
assert all(vport_pixel['center'] == (wcs.wcs.crpix - 1))
331+
assert isinstance(vport_pixel['fov'], numbers.Real)
332+
333+
# Set the viewport in pixel coordinates
334+
input_center_pixel = (wcs.wcs.crpix[0], wcs.wcs.crpix[1])
335+
input_fov_pixel = 100 # in pixels
336+
self.image.set_viewport(center=input_center_pixel, fov=input_fov_pixel, image_label='test')
337+
338+
# Get the viewport in world coordinates
339+
vport_world = self.image.get_viewport(image_label='test', sky_or_pixel='sky')
340+
assert vport_world['center'] == wcs.pixel_to_world(*input_center_pixel)
341+
assert isinstance(vport_world['fov'], u.Quantity)
270342

271343
def test_viewport_round_trips(self, data, wcs):
272344
# Check that the viewport retrieved with get can be used to set

0 commit comments

Comments
 (0)