def test_link_editor(): # Make sure that the WCSLink works property in the link editor and is # returned unmodified. The main way to check that is just to make sure that # the link round-trips when going through EditableLinkFunctionState. wcs1 = WCS(naxis=2) wcs1.wcs.ctype = 'DEC--TAN', 'RA---TAN' wcs1.wcs.set() data1 = Data(label='Data 1') data1.coords = WCSCoordinates(wcs=wcs1) data1['x'] = np.ones((2, 3)) wcs2 = WCS(naxis=3) wcs2.wcs.ctype = 'GLON-CAR', 'FREQ', 'GLAT-CAR' wcs2.wcs.set() data2 = Data(label='Data 2') data2.coords = WCSCoordinates(wcs=wcs2) data2['x'] = np.ones((2, 3, 4)) link1 = WCSLink(data1, data2) link2 = EditableLinkFunctionState(link1).link assert isinstance(link2, WCSLink) assert link2.data1.label == 'Data 1' assert link2.data2.label == 'Data 2'
def test_clone_wcs_link(): # Make sure that WCSLink can be serialized/deserialized wcs1 = WCS(naxis=2) wcs1.wcs.ctype = 'DEC--TAN', 'RA---TAN' wcs1.wcs.set() data1 = Data(label='Data 1') data1.coords = WCSCoordinates(wcs=wcs1) data1['x'] = np.ones((2, 3)) wcs2 = WCS(naxis=3) wcs2.wcs.ctype = 'GLON-CAR', 'FREQ', 'GLAT-CAR' wcs2.wcs.set() data2 = Data(label='Data 2') data2.coords = WCSCoordinates(wcs=wcs2) data2['x'] = np.ones((2, 3, 4)) link1 = WCSLink(data1, data2) link2 = clone(link1) assert isinstance(link2, WCSLink) assert link2.data1.label == 'Data 1' assert link2.data2.label == 'Data 2'
def test_wcs_affine_approximation(): wcs1 = WCS(naxis=2) wcs1.wcs.ctype = 'DEC--TAN', 'RA---TAN' wcs1.wcs.set() data1 = Data(label='Data 1') data1.coords = wcs1 data1['x'] = np.ones((2, 3)) wcs2 = WCS(naxis=2) wcs2.wcs.ctype = 'DEC--TAN', 'RA---TAN' wcs2.wcs.crpix = -3, 5 wcs2.wcs.cd = [[2, -1], [1, 2]] wcs2.wcs.set() data2 = Data(label='Data 2') data2.coords = wcs2 data2['x'] = np.ones((2, 3)) link = WCSLink(data1, data2) affine_link = link.as_affine_link(tolerance=0.1) assert isinstance(affine_link, AffineLink) assert_allclose(affine_link.matrix, [[0.4, 0.2, -3.4], [-0.2, 0.4, 4.2], [0, 0, 1]], atol=1e-5) x1 = np.array([1.4, 3.2, 2.5]) y1 = np.array([0.2, 4.3, 2.2]) x2, y2 = link.forwards(x1, y1) x3, y3 = affine_link.forwards(x1, y1) assert_allclose(x2, x3, atol=1e-5) assert_allclose(y2, y3, atol=1e-5) x4, y4 = link.backwards(x1, y1) x5, y5 = affine_link.backwards(x1, y1) assert_allclose(x4, x5, atol=1e-5) assert_allclose(y4, y4, atol=1e-5)
def test_wcs_offset_approximation(): wcs1 = WCS(naxis=2) wcs1.wcs.ctype = 'DEC--TAN', 'RA---TAN' wcs1.wcs.set() data1 = Data(label='Data 1') data1.coords = wcs1 data1['x'] = np.ones((2, 3)) wcs2 = WCS(naxis=2) wcs2.wcs.ctype = 'DEC--TAN', 'RA---TAN' wcs2.wcs.crpix = -3, 5 wcs2.wcs.set() data2 = Data(label='Data 2') data2.coords = wcs2 data2['x'] = np.ones((2, 3)) link = WCSLink(data1, data2) offset_link = link.as_affine_link(tolerance=0.1) assert isinstance(offset_link, OffsetLink) assert_allclose(offset_link.offsets, [3, -5]) x1 = np.array([1.4, 3.2, 2.5]) y1 = np.array([0.2, 4.3, 2.2]) x2, y2 = link.forwards(x1, y1) x3, y3 = offset_link.forwards(x1, y1) assert_allclose(x2, x3, atol=1e-5) assert_allclose(y2, y3, atol=1e-5) x4, y4 = link.backwards(x1, y1) x5, y5 = offset_link.backwards(x1, y1) assert_allclose(x4, x5, atol=1e-5) assert_allclose(y4, y4, atol=1e-5)
def test_wcs_no_approximation(): wcs1 = WCS(naxis=2) wcs1.wcs.ctype = 'DEC--TAN', 'RA---TAN' wcs1.wcs.set() data1 = Data(label='Data 1') data1.coords = wcs1 data1['x'] = np.ones((2, 3)) wcs2 = WCS(naxis=2) wcs2.wcs.ctype = 'DEC--TAN', 'RA---TAN' wcs2.wcs.crval = 30, 50 wcs2.wcs.set() data2 = Data(label='Data 2') data2.coords = wcs2 data2['x'] = np.ones((2, 3)) link = WCSLink(data1, data2) with pytest.raises(NoAffineApproximation): link.as_affine_link(tolerance=0.1)
def link_data(self, link_type='pixels', wcs_fallback_scheme='pixels', wcs_use_affine=True, error_on_fail=False): """(Re)link loaded data with the desired link type. All existing links will be replaced. .. warning:: Any markers added would be removed. You can add back the markers manually using :meth:`add_markers`. During the markers removal, pan/zoom will also reset. Parameters ---------- link_type : {'pixels', 'wcs'} Choose to link by pixels or WCS. wcs_fallback_scheme : {None, 'pixels'} If WCS linking failed, choose to fall back to linking by pixels or not at all. This is only used when ``link_type='wcs'``. Choosing `None` may result in some Imviz functionality not working properly. wcs_use_affine : bool Use an affine transform to represent the offset between images if possible (requires that the approximation is accurate to within 1 pixel with the full WCS transformations). If approximation fails, it will automatically fall back to full WCS transformation. This is only used when ``link_type='wcs'``. Affine approximation is much more performant at the cost of accuracy. error_on_fail : bool If `True`, any failure in linking will raise an exception. If `False`, warnings will be emitted as snackbar messages. When only warnings are emitted and no links are assigned, some Imviz functionality may not work properly. Raises ------ ValueError Invalid inputs or reference data. """ if len(self.app.data_collection) <= 1: # No need to link, we are done. return if link_type not in ('pixels', 'wcs'): raise ValueError( f"link_type must be 'pixels' or 'wcs', got {link_type}") if link_type == 'wcs' and wcs_fallback_scheme not in (None, 'pixels'): raise ValueError("wcs_fallback_scheme must be None or 'pixels', " f"got {wcs_fallback_scheme}") # Clear any existing markers. Otherwise, re-linking will crash. self.reset_markers() refdata, iref = get_reference_image_data(self.app) links_list = [] ids0 = refdata.pixel_component_ids ndim_range = range(refdata.ndim) for i, data in enumerate(self.app.data_collection): # Do not link with self if i == iref: continue # We are not touching any existing Subsets. They keep their own links. if not layer_is_image_data(data): continue ids1 = data.pixel_component_ids try: if link_type == 'pixels': new_links = [ LinkSame(ids0[i], ids1[i]) for i in ndim_range ] else: # 'wcs' wcslink = WCSLink(data1=refdata, data2=data, cids1=ids0, cids2=ids1) if wcs_use_affine: try: new_links = [wcslink.as_affine_link()] except NoAffineApproximation: # pragma: no cover new_links = [wcslink] else: new_links = [wcslink] except Exception as e: if link_type == 'wcs' and wcs_fallback_scheme == 'pixels': try: new_links = [ LinkSame(ids0[i], ids1[i]) for i in ndim_range ] except Exception as e: # pragma: no cover if error_on_fail: raise else: self.app.hub.broadcast( SnackbarMessage( f"Error linking '{data.label}' to '{refdata.label}': " f"{repr(e)}", color="warning", timeout=8000, sender=self.app)) continue else: if error_on_fail: raise else: self.app.hub.broadcast( SnackbarMessage( f"Error linking '{data.label}' to '{refdata.label}': " f"{repr(e)}", color="warning", timeout=8000, sender=self.app)) continue links_list += new_links if len(links_list) > 0: with self.app.data_collection.delay_link_manager_update(): self.app.data_collection.set_links(links_list) self.app.hub.broadcast( SnackbarMessage('Images successfully relinked', color='success', timeout=8000, sender=self.app))