Skip to content

Commit

Permalink
more general change refdata method
Browse files Browse the repository at this point in the history
  • Loading branch information
bmorris3 committed May 26, 2023
1 parent 2e855d2 commit 4afdace
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 43 deletions.
21 changes: 8 additions & 13 deletions jdaviz/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -439,13 +439,8 @@ def _on_layers_changed(self, msg):
}

def _on_refdata_changed(self, msg):
is_wcs_only = (
msg.old.meta.get('WCS-ONLY', False) or
msg.new.meta.get('WCS-ONLY', False)
)

if not is_wcs_only:
return
old_is_wcs_only = msg.old.meta.get('WCS-ONLY', False)
new_is_wcs_only = msg.new.meta.get('WCS-ONLY', False)

wcs_only_refdata_icon = 'mdi-compass-outline'
wcs_only_not_refdata_icon = 'mdi-compass-off-outline'
Expand All @@ -457,11 +452,11 @@ def switch_icon(old_icon, new_icon):

new_layer_icons = {}
for i, (layer_name, layer_icon) in enumerate(self.state.layer_icons.items()):
if layer_name == msg.old.label:
if layer_name == msg.old.label and old_is_wcs_only:
new_layer_icons[layer_name] = switch_icon(
layer_icon, wcs_only_not_refdata_icon
)
elif layer_name == msg.new.label:
elif layer_name == msg.new.label and new_is_wcs_only:
new_layer_icons[layer_name] = switch_icon(
layer_icon, wcs_only_refdata_icon
)
Expand All @@ -480,7 +475,7 @@ def _change_reference_data(self, new_refdata_label):

viewer_reference = self._get_first_viewer_reference_name()
viewer = self.get_viewer(viewer_reference)
old_refdata = viewer.state.reference_data
old_refdata = self._viewer_store[viewer_reference].state.reference_data

if new_refdata_label == old_refdata.label:
# if there's no refdata change, don't do anything:
Expand All @@ -491,8 +486,6 @@ def _change_reference_data(self, new_refdata_label):
if data.label == new_refdata_label
]

self._viewer_store[viewer_reference].state.reference_data = new_refdata

change_refdata_message = ChangeRefDataMessage(
new_refdata,
viewer,
Expand All @@ -501,9 +494,11 @@ def _change_reference_data(self, new_refdata_label):
old=old_refdata,
new=new_refdata
)

self._viewer_store[viewer_reference].state.reference_data = new_refdata
self.hub.broadcast(change_refdata_message)

viewer.state.reset_limits()
self._viewer_store[viewer_reference].state.reset_limits()

def _link_new_data(self, reference_data=None, data_to_be_linked=None):
"""
Expand Down
107 changes: 77 additions & 30 deletions jdaviz/configs/imviz/wcs_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,7 @@ def data_outside_gwcs_bounding_box(data, x, y):
return outside_bounding_box


def get_fits_wcs_from_file(filename):
def _get_fits_wcs_from_file(filename):
header = fits.getheader(filename)
with warnings.catch_warnings():
# Ignore a warning on using DATE-OBS in place of MJD-OBS
Expand All @@ -303,6 +303,7 @@ def rotated_gwcs(
center_world_coord,
rotation_angle,
pixel_scales,
cdelt_signs,
refdata_shape=(2, 2)
):
# based on ``gwcs_simple_imaging_units`` in gwcs:
Expand All @@ -319,11 +320,12 @@ def rotated_gwcs(
rescale_pixel_scale = np.array(refdata_shape) / 1000

shift_by_crpix = (
models.Shift(-refdata_shape[1] / 2 * u.pixel) &
models.Shift(-refdata_shape[0] / 2 * u.pixel)
models.Shift(-refdata_shape[0] / 2 * u.pixel) &
models.Shift(-refdata_shape[1] / 2 * u.pixel)
)
# multiplying by +/-1 can flip east/west
flip_east_west = models.Multiply(-1) & models.Multiply(1)

# multiplying by +/-1 can flip north/south or east/west
flip_axes = models.Multiply(cdelt_signs[0]) & models.Multiply(cdelt_signs[1])
rotation = models.AffineTransformation2D(
rotation_matrix * u.deg, translation=[0, 0] * u.deg
)
Expand All @@ -344,7 +346,7 @@ def rotated_gwcs(
)

det2sky = (
shift_by_crpix | flip_east_west | rotation |
flip_axes | shift_by_crpix | rotation |
tan | celestial_rotation
)
det2sky.name = "linear_transform"
Expand All @@ -367,11 +369,46 @@ def rotated_gwcs(
return GWCS(pipeline)


def get_rotated_nddata_from_fits(filename, rotation_angle, refdata_shape=(2, 2)):
def _prepare_rotated_nddata(wcs, rotation_angle, refdata_shape):
# get the world coordinates of the central pixel
real_image_shape = np.array(wcs.array_shape)
central_pixel_coord = real_image_shape / 2 * u.pix
central_world_coord = wcs.pixel_to_world(*central_pixel_coord)
rotation_angle = coord.Angle(rotation_angle).wrap_at(360 * u.deg)

# compute the x/y plate scales from the WCS:
pixel_scales = [
value * unit / u.pix
for value, unit in zip(
proj_plane_pixel_scales(wcs), wcs.wcs.cunit
)
]

# flip e.g. RA or Dec axes?
cdelt_signs = np.sign(wcs.wcs.cdelt)

# create a GWCS centered on ``filename``,
# and rotated by ``rotation_angle``:
new_rotated_gwcs = rotated_gwcs(
central_world_coord, rotation_angle, pixel_scales, cdelt_signs
)

# create an all-nan NDDataArray with the rotated GWCS:
ndd = NDDataArray(
data=np.nan * np.ones(refdata_shape),
wcs=new_rotated_gwcs,
)
return ndd


def _get_rotated_nddata_from_fits(filename, rotation_angle, refdata_shape=(2, 2)):
"""
Create a synthetic NDDataArray which stores GWCS that approximate
the FITS WCS in ``filename`` rotated by ``rotation_angle``.
This method is useful for ensuring that future datasets are loaded
in the correct orientation.
Parameters
----------
filename : path-like, str
Expand All @@ -388,31 +425,41 @@ def get_rotated_nddata_from_fits(filename, rotation_angle, refdata_shape=(2, 2))
Data are all NaNs, wcs are rotated.
"""
# get the FITS WCS from the file:
wcs = get_fits_wcs_from_file(filename)
wcs = _get_fits_wcs_from_file(filename)

# get the world coordinates of the central pixel
real_image_shape = np.array(wcs.array_shape)
central_pixel_coord = real_image_shape / 2 * u.pix
central_world_coord = wcs.pixel_to_world(*central_pixel_coord)
return _prepare_rotated_nddata(wcs, rotation_angle, refdata_shape)

# compute the x/y plate scales from the WCS:
pixel_scales = [
value * unit / u.pix
for value, unit in zip(
proj_plane_pixel_scales(wcs), wcs.wcs.cunit
)
]

# create a GWCS centered on ``filename``,
# and rotated by ``rotation_angle``:
new_rotated_gwcs = rotated_gwcs(
central_world_coord, rotation_angle, pixel_scales
)
def _get_rotated_nddata_from_label(app, data_label, rotation_angle, refdata_shape=(2, 2)):
"""
Create a synthetic NDDataArray which stores GWCS that approximate
the WCS in the coords attr of the Data object with label ``data_label``
loaded into ``app``.
# create an all-nan NDDataArray with the rotated GWCS:
ndd = NDDataArray(
data=np.nan * np.ones(refdata_shape),
wcs=new_rotated_gwcs,
)
This method is useful for rotating pre-loaded datasets when
combined with ``app._change_reference_data(data_label)``.
return ndd
Parameters
----------
app : `~jdaviz.Application`
App instance containing ``data_label``
data_label : str
Data label for the Data to rotate
rotation_angle : `~astropy.units.Quantity`
Angle to rotate the image counter-clockwise from its
original orientation
refdata_shape : tuple
Shape of the reference data array
Returns
-------
ndd : `~astropy.nddata.NDDataArray`
Data are all NaNs, wcs are rotated.
"""
# get the WCS from the Data object's coords attribute:
[wcs] = [
data.coords for data in app.data_collection
if data.label == data_label
]

return _prepare_rotated_nddata(wcs, rotation_angle, refdata_shape)

0 comments on commit 4afdace

Please sign in to comment.