From 33d5b98fbc4f620c491880c3a3bc466646ad2bd5 Mon Sep 17 00:00:00 2001 From: Peter Williams Date: Wed, 31 Jan 2024 16:44:29 -0500 Subject: [PATCH] wwt_data_formats/imageset.py: be less picky about WCS when we're TOAST If we're using `set_position_from_wcs()` for a TOAST imageset, the details of the WCS aren't very important, since we're just using it to obtain basic centering and sizing info. So, we can skip various checks about square pixels and such. Also improve an error message that referred to a non-positive CD determinant when it was really checking for 0 or NaN (in effect). We are OK with negative determinants if the image isn't tiled. --- wwt_data_formats/imageset.py | 67 ++++++++++++++++++++++-------------- 1 file changed, 41 insertions(+), 26 deletions(-) diff --git a/wwt_data_formats/imageset.py b/wwt_data_formats/imageset.py index 9765f08..6c64d08 100644 --- a/wwt_data_formats/imageset.py +++ b/wwt_data_formats/imageset.py @@ -368,7 +368,8 @@ class ImageSet(LockedXmlTraits, UrlContainer): ---------- headers : :class:`~astropy.io.fits.Header` or string-keyed dict-like - A set of FITS-like headers including WCS keywords such as ``CRVAL1``. + A set of FITS-like headers including WCS keywords such as + ``CRVAL1``. width : positive integer @@ -386,9 +387,9 @@ class ImageSet(LockedXmlTraits, UrlContainer): fov_factor : optional - If *place* is provided, its zoom level will be set so that the angular - height of the client viewport is this factor times the angular height of - the image. The default is 1.7. + If *place* is provided, its zoom level will be set so that the + angular height of the client viewport is this factor times the + angular height of the image. The default is 1.7. Returns ------- @@ -407,6 +408,10 @@ class ImageSet(LockedXmlTraits, UrlContainer): For the time being, the WCS must be equatorial using the gnomonic (``TAN``) projection. + This function may be used when the imageset :attr:`projection` is TOAST. + However, in this case most of the WCS parameters are irrelevant. The WCS + information is only used to apply basic centering and sizing settings. + Required keywords in *headers* are: - ``CTYPE1`` and ``CTYPE2`` @@ -432,10 +437,13 @@ class ImageSet(LockedXmlTraits, UrlContainer): will do its best to detect and reject them. """ - if headers["CTYPE1"] not in ("RA---TAN", "RA---TPV") or headers[ - "CTYPE2" - ] not in ("DEC--TAN", "DEC--TPV"): - raise ValueError("WCS coordinates must be in an equatorial/TAN projection") + if self.projection != ProjectionType.TOAST: + if headers["CTYPE1"] not in ("RA---TAN", "RA---TPV") or headers[ + "CTYPE2" + ] not in ("DEC--TAN", "DEC--TPV"): + raise ValueError( + "WCS coordinates must be in an equatorial/TAN projection" + ) # Figure out the stuff we need from the headers. @@ -484,33 +492,40 @@ class ImageSet(LockedXmlTraits, UrlContainer): rot_rad = math.atan2(-cd_sign * cd1_2, -cd2_2) - # We can only express square pixels with a rotation and a potential - # parity flip. Do some cross-checks to ensure that the input matrix can - # be well-approximated this way. There's probably a smarter - # linear-algebra way to do this. + # If we're in TOAST projection, we're only using the WCS to assign basic + # centering and sizing attributes, and the details of the projection are + # unimportant. + # + # In other cases, they do matter. We can only express square pixels with + # a rotation and a potential parity flip. Do some cross-checks to ensure + # that the input matrix can be well-approximated this way. There's + # probably a smarter linear-algebra way to do this. TOL = 0.05 - if not abs(cd_det) > 0: - raise ValueError("determinant of the CD matrix is not positive") - scale_x = math.sqrt(cd1_1**2 + cd1_2**2) scale_y = math.sqrt(cd2_1**2 + cd2_2**2) - if abs(scale_x - scale_y) / (scale_x + scale_y) > TOL: - raise ValueError("WWT cannot express non-square pixels, which this WCS has") + if self.projection != ProjectionType.TOAST: + if not abs(cd_det) > 0: + raise ValueError("determinant of the CD matrix zero or ill-defined") - det_scale = math.sqrt(abs(cd_det)) + if abs(scale_x - scale_y) / (scale_x + scale_y) > TOL: + raise ValueError( + "WWT cannot express non-square pixels, which this WCS has" + ) - if abs((cd1_1 - cd_sign * cd2_2) / det_scale) > TOL: - raise ValueError( - f"WWT cannot express this CD matrix (1; {cd1_1} {cd_sign} {cd2_2} {det_scale})" - ) + det_scale = math.sqrt(abs(cd_det)) - if abs((cd2_1 + cd_sign * cd1_2) / det_scale) > TOL: - raise ValueError( - f"WWT cannot express this CD matrix (2; {cd2_1} {cd_sign} {cd1_2} {det_scale})" - ) + if abs((cd1_1 - cd_sign * cd2_2) / det_scale) > TOL: + raise ValueError( + f"WWT cannot express this CD matrix (1; {cd1_1} {cd_sign} {cd2_2} {det_scale})" + ) + + if abs((cd2_1 + cd_sign * cd1_2) / det_scale) > TOL: + raise ValueError( + f"WWT cannot express this CD matrix (2; {cd2_1} {cd_sign} {cd1_2} {det_scale})" + ) # This is our best effort to make sure that the view centers on the # center of the image.