Coverage for tests / test_transforms.py: 21%
103 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-17 08:50 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-17 08:50 +0000
1#
2# Developed for the LSST Data Management System.
3# This product includes software developed by the LSST Project
4# (https://www.lsst.org).
5# See the COPYRIGHT file at the top-level directory of this distribution
6# for details of code ownership.
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 GNU General Public License
19# along with this program. If not, see <https://www.gnu.org/licenses/>.
20#
22import unittest
24import numpy as np
26import lsst.utils.tests
27from lsst.geom import (
28 LinearTransform, AffineTransform, Point2D, Extent2D, SpherePoint, SphereTransform, degrees
29)
30from lsst.sphgeom import UnitVector3d
33class TransformTests:
35 def testVectorization(self):
36 x1 = np.random.randn(4, 3)
37 y1 = np.random.randn(4, 3)
38 x2, y2 = self.transform(x1, y1)
39 self.assertEqual(x1.shape, x2.shape)
40 self.assertEqual(x2.shape, y2.shape)
41 for i in range(4):
42 for j in range(3):
43 x3, y3 = self.transform(Point2D(x1[i, j], y1[i, j]))
44 self.assertAlmostEqual(x3, x2[i, j], 15)
45 self.assertAlmostEqual(y3, y2[i, j], 15)
48class LinearTransformTestCase(lsst.utils.tests.TestCase, TransformTests):
50 def setUp(self):
51 np.random.seed(5)
52 matrix = np.random.randn(2, 2)
53 self.transform = LinearTransform(matrix)
56class AffineTransformTestCase(lsst.utils.tests.TestCase, TransformTests):
58 def setUp(self):
59 np.random.seed(6)
60 matrix = np.random.randn(2, 2)
61 vector = np.random.randn(2)
62 self.transform = AffineTransform(LinearTransform(matrix), Extent2D(vector))
65class SphereTransformTestCase(lsst.utils.tests.TestCase):
67 def setUp(self):
68 self.x = UnitVector3d(1.0, 0.0, 0.0)
69 self.y = UnitVector3d(0.0, 1.0, 0.0)
70 self.z = UnitVector3d(0.0, 0.0, 1.0)
71 self.rng = np.random.default_rng(500)
73 def make_rotation_in_plane(self, angle, i1, i2):
74 """Make a spherical rotation transform in one plane.
76 Parameters
77 ----------
78 angle : `lsst.geom.Angle`
79 Angle to rotate by.
80 i1 : `int`
81 Index of the first dimension rotated (0, 1, or 2).
82 i2 : `int`
83 Index of the second dimension rotated (0, 1, or 2).
85 Returns
86 -------
87 transform : `lsst.geom.SphereTransform`
88 Rotation in the given plane.
89 """
90 rot2d = LinearTransform.makeRotation(angle)
91 matrix2d = rot2d.getMatrix()
92 matrix3d = np.identity(3)
93 matrix3d[i1, i1] = matrix2d[0, 0]
94 matrix3d[i1, i2] = matrix2d[0, 1]
95 matrix3d[i2, i1] = matrix2d[1, 0]
96 matrix3d[i2, i2] = matrix2d[1, 1]
97 return SphereTransform(matrix3d)
99 def make_random_rotation(self):
100 """Make a random SphereTransform."""
101 t1 = self.make_rotation_in_plane(self.rng.uniform(0, 360)*degrees, 0, 1)
102 t2 = self.make_rotation_in_plane(self.rng.uniform(0, 360)*degrees, 1, 2)
103 t3 = self.make_rotation_in_plane(self.rng.uniform(0, 360)*degrees, 2, 0)
104 return t3 * t2 * t1
106 def test_identity(self):
107 """Test the identity transform."""
108 identity = SphereTransform()
109 self.assertEqual(self.x, identity(self.x))
110 self.assertEqual(self.y, identity(self.y))
111 self.assertEqual(self.z, identity(self.z))
113 def test_matrix(self):
114 """Test constructing from a matrix, accessing the matrix, computing
115 the inverse transform, and composing transforms.
116 """
117 transform = self.make_rotation_in_plane(90*degrees, 0, 1)
118 self.assertFloatsAlmostEqual(
119 transform.getMatrix(),
120 np.array([[0.0, -1.0, 0.0], [1.0, 0.0, 0.0], [0.0, 0.0, 1.0]]),
121 )
122 self.assertFloatsAlmostEqual(np.array(transform(self.x)), np.array(self.y))
123 self.assertFloatsAlmostEqual(np.array(transform(self.z)), np.array(self.z))
124 # Test inversion and composition on the simple transform and a random
125 # one.
126 inv = transform.inverted()
127 self.assertFloatsAlmostEqual(np.array(inv(self.y)), np.array(self.x))
128 self.assertFloatsAlmostEqual(np.array(inv(self.z)), np.array(self.z))
129 identity = transform.inverted() * transform
130 self.assertFloatsAlmostEqual(identity.getMatrix(), SphereTransform().getMatrix())
131 random_transform = self.make_random_rotation()
132 inv_random_transform = random_transform.inverted()
133 self.assertFloatsAlmostEqual(
134 (random_transform * inv_random_transform).getMatrix(), SphereTransform().getMatrix()
135 )
136 self.assertFloatsAlmostEqual(
137 (inv_random_transform * random_transform).getMatrix(), SphereTransform().getMatrix())
138 # Test transforming SpherePoints via the random one; they should remain
139 # 90 degrees away from each other.
140 spx = SpherePoint(self.x)
141 spy = SpherePoint(self.y)
142 spz = SpherePoint(self.z)
143 rspx = random_transform(spx)
144 rspy = random_transform(spy)
145 rspz = random_transform(spz)
146 self.assertFloatsAlmostEqual(rspx.separation(rspy).asDegrees(), 90.0, rtol=1E-15)
147 self.assertFloatsAlmostEqual(rspy.separation(rspz).asDegrees(), 90.0, rtol=1E-15)
148 self.assertFloatsAlmostEqual(rspz.separation(rspx).asDegrees(), 90.0, rtol=1E-15)
150 def test_fit_unit_vectors(self):
151 """Test SphereTransform.fit_unit_vectors."""
152 z = np.linspace(0.0, 1.0, 50)
153 self.rng.shuffle(z)
154 from_array = np.zeros((50, 3), dtype=float)
155 from_array[:, 0] = (1.0 - z**2)**0.5
156 from_array[:, 2] = z
157 to_array = np.zeros((50, 3), dtype=float)
158 to_array[:, 1] = (1.0 - z**2)**0.5
159 to_array[:, 2] = z
160 weights = np.ones(50, dtype=float)
161 transform = SphereTransform.fit_unit_vectors(from_array, to_array, weights)
162 self.assertFloatsAlmostEqual(np.array(transform(self.x)), np.array(self.y))
163 self.assertFloatsAlmostEqual(np.array(transform(self.z)), np.array(self.z))
164 # Rotate all of the to_ points by an arbitrary transform and fit again.
165 random_transform = self.make_random_rotation()
166 to_array_2 = np.zeros((50, 3), dtype=float)
167 to_array_2[:, 0] = random_transform.applyX(to_array[:, 0], to_array[:, 1], to_array[:, 2])
168 to_array_2[:, 1] = random_transform.applyY(to_array[:, 0], to_array[:, 1], to_array[:, 2])
169 to_array_2[:, 2] = random_transform.applyZ(to_array[:, 0], to_array[:, 1], to_array[:, 2])
170 transform_2 = SphereTransform.fit_unit_vectors(from_array, to_array_2, weights)
171 # The fitted rotation should be the composition:
172 self.assertFloatsAlmostEqual(
173 transform_2.getMatrix(), (random_transform * transform).getMatrix(), rtol=1E-15, atol=1E-15
174 )
177class MemoryTester(lsst.utils.tests.MemoryTestCase):
178 pass
181def setup_module(module):
182 lsst.utils.tests.init()
185if __name__ == "__main__": 185 ↛ 186line 185 didn't jump to line 186 because the condition on line 185 was never true
186 lsst.utils.tests.init()
187 unittest.main()