Coverage for tests/test_hpxUtils.py: 18%
68 statements
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-22 02:38 -0700
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-22 02:38 -0700
1#
2# LSST Data Management System
3# Copyright 2021 LSST Corporation.
4#
5# This product includes software developed by the
6# LSST Project (http://www.lsst.org/).
7#
8# This program is free software: you can redistribute it and/or modify
9# it under the terms of the GNU General Public License as published by
10# the Free Software Foundation, either version 3 of the License, or
11# (at your option) any later version.
12#
13# This program is distributed in the hope that it will be useful,
14# but WITHOUT ANY WARRANTY; without even the implied warranty of
15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16# GNU General Public License for more details.
17#
18# You should have received a copy of the LSST License Statement and
19# the GNU General Public License along with this program. If not,
20# see <http://www.lsstcorp.org/LegalNotices/>.
21#
22import unittest
23import hpgeom as hpg
24import numpy as np
25from astropy.wcs import WCS
27import lsst.utils.tests
28import lsst.afw.geom as afwGeom
31class HpxUtilsTestCase(lsst.utils.tests.TestCase):
32 def test_hpx_wcs(self):
33 """Test various computations of HPX wcs.
34 """
35 # The default is 2**9=512 pixels on a side. We also
36 # test smaller and larger tiles.
37 shift_order_tests = [8, 9, 10]
38 # Choose arbitrary positions that are north, south,
39 # equatorial, east, and west. 41.8 degrees is special.
40 pos_tests = [(0.0, 0.0),
41 (43.0, 3.0),
42 (127.0, -23.0),
43 (194.0, 36.0),
44 (207.0, -41.5),
45 (256.0, 42.0),
46 (302.0, -67.0),
47 (332.0, 75.0),
48 (348.0, -83.0),
49 (358.0, 89.0)]
51 # Default order of 11 (nside=2048) gives 0.2" pixels for 512
52 # subpixels
53 hips_order_tests = [12, 11, 10]
55 for shift_order, hips_order in zip(shift_order_tests, hips_order_tests):
56 nside = 2**hips_order
57 nsubpix = 2**shift_order
59 for pos in pos_tests:
60 pixel = hpg.angle_to_pixel(nside, pos[0], pos[1])
62 wcs = afwGeom.makeHpxWcs(hips_order, pixel, shift_order=shift_order)
64 y, x = np.meshgrid(np.arange(nsubpix), np.arange(nsubpix))
65 x = x.ravel().astype(np.float64)
66 y = y.ravel().astype(np.float64)
67 ra, dec = wcs.pixelToSkyArray(x, y, degrees=True)
68 ra[ra == 360.0] = 0.0
70 # Check that all these positions are truly inside the pixel
71 radec_pixels = hpg.angle_to_pixel(nside, ra, dec)
72 np.testing.assert_array_equal(radec_pixels, pixel)
74 # Check that the orientation is correct.
75 # pixel (0, 0) should be E
76 # pixel (nsubpix - 1, 0) should be N
77 # pixel (nsubpix - 1, nsubpix - 1) should be W
78 # pixel (0, nsubpix - 1) should be S
79 xx = np.array([0, nsubpix - 1, nsubpix - 1, 0], dtype=np.float64)
80 yy = np.array([0, 0, nsubpix - 1, nsubpix - 1], dtype=np.float64)
81 ra_cornerpix, dec_cornerpix = wcs.pixelToSkyArray(xx, yy, degrees=True)
83 # Generate all the sub-pixels
84 bit_shift = 2*int(np.round(np.log2(nsubpix)))
85 sub_pixels = np.left_shift(pixel, bit_shift) + np.arange(nsubpix*nsubpix)
86 ra_sub, dec_sub = hpg.pixel_to_angle(nside*nsubpix, sub_pixels)
87 # Deal with RA = 0 for testing...
88 if ra_sub.max() > 350.0 and ra_sub.min() < 10.0:
89 hi, = np.where(ra_sub > 180.0)
90 ra_sub[hi] -= 360.0
91 hi_corner, = np.where(ra_cornerpix > 180.0)
92 ra_cornerpix[hi_corner] -= 360.0
94 cos_dec = np.cos(np.deg2rad(np.median(dec)))
96 easternmost = np.argmax(ra_sub)
97 self.assertFloatsAlmostEqual(ra_cornerpix[0], ra_sub[easternmost], atol=1e-13/cos_dec)
98 self.assertFloatsAlmostEqual(dec_cornerpix[0], dec_sub[easternmost], atol=5e-13)
99 northernmost = np.argmax(dec_sub)
100 self.assertFloatsAlmostEqual(ra_cornerpix[1], ra_sub[northernmost], atol=1e-13/cos_dec)
101 self.assertFloatsAlmostEqual(dec_cornerpix[1], dec_sub[northernmost], atol=5e-13)
102 westernmost = np.argmin(ra_sub)
103 self.assertFloatsAlmostEqual(ra_cornerpix[2], ra_sub[westernmost], atol=1e-13/cos_dec)
104 self.assertFloatsAlmostEqual(dec_cornerpix[2], dec_sub[westernmost], atol=5e-13)
105 southernmost = np.argmin(dec_sub)
106 self.assertFloatsAlmostEqual(ra_cornerpix[3], ra_sub[southernmost], atol=1e-13/cos_dec)
107 self.assertFloatsAlmostEqual(dec_cornerpix[3], dec_sub[southernmost], atol=5e-13)
109 # Confirm that the transformation also works with astropy WCS
110 astropy_wcs = WCS(header=wcs.getFitsMetadata().toDict())
111 astropy_coords = astropy_wcs.pixel_to_world(x, y)
113 astropy_ra = astropy_coords.ra.degree
114 astropy_dec = astropy_coords.dec.degree
116 # The astropy warping is only consistent at the 5e-11 level.
117 self.assertFloatsAlmostEqual(astropy_ra, ra, atol=5e-11/cos_dec)
118 self.assertFloatsAlmostEqual(astropy_dec, dec, atol=5e-11)
120 def test_hpx_wcs_bad_inputs(self):
121 """Test assertions for bad inputs to makeHpxWcs()
122 """
123 # order must be positive.
124 self.assertRaises(ValueError, afwGeom.makeHpxWcs, 0, 100)
125 self.assertRaises(ValueError, afwGeom.makeHpxWcs, -10, 100)
127 # pixel number must be in range.
128 self.assertRaises(ValueError, afwGeom.makeHpxWcs, 5, -1)
129 self.assertRaises(ValueError, afwGeom.makeHpxWcs, 5, 12*2**10 + 1)
131 # tilepix must be a positive power of 2.
132 # shift_order must be positive
133 self.assertRaises(ValueError, afwGeom.makeHpxWcs, 5, 0, shift_order=0)
134 self.assertRaises(ValueError, afwGeom.makeHpxWcs, 5, 0, shift_order=-10)
137class MemoryTester(lsst.utils.tests.MemoryTestCase):
138 pass
141def setup_module(module):
142 lsst.utils.tests.init()
145if __name__ == "__main__": 145 ↛ 146line 145 didn't jump to line 146, because the condition on line 145 was never true
146 lsst.utils.tests.init()
147 unittest.main()