Coverage for tests/test_CompoundRegion.py: 30%
84 statements
« prev ^ index » next coverage.py v7.5.0, created at 2024-05-02 03:12 -0700
« prev ^ index » next coverage.py v7.5.0, created at 2024-05-02 03:12 -0700
1# This file is part of sphgeom.
2#
3# Developed for the LSST Data Management System.
4# This product includes software developed by the LSST Project
5# (http://www.lsst.org).
6# See the COPYRIGHT file at the top-level directory of this distribution
7# for details of code ownership.
8#
9# This software is dual licensed under the GNU General Public License and also
10# under a 3-clause BSD license. Recipients may choose which of these licenses
11# to use; please see the files gpl-3.0.txt and/or bsd_license.txt,
12# respectively. If you choose the GPL option then the following text applies
13# (but note that there is still no warranty even if you opt for BSD instead):
14#
15# This program is free software: you can redistribute it and/or modify
16# it under the terms of the GNU General Public License as published by
17# the Free Software Foundation, either version 3 of the License, or
18# (at your option) any later version.
19#
20# This program is distributed in the hope that it will be useful,
21# but WITHOUT ANY WARRANTY; without even the implied warranty of
22# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23# GNU General Public License for more details.
24#
25# You should have received a copy of the GNU General Public License
26# along with this program. If not, see <http://www.gnu.org/licenses/>.
28import pickle
29import unittest
31try:
32 import yaml
33except ImportError:
34 yaml = None
36from lsst.sphgeom import (
37 CONTAINS,
38 DISJOINT,
39 INTERSECTS,
40 WITHIN,
41 Angle,
42 AngleInterval,
43 Box,
44 Circle,
45 CompoundRegion,
46 IntersectionRegion,
47 LonLat,
48 NormalizedAngleInterval,
49 Region,
50 UnionRegion,
51 UnitVector3d,
52)
55class CompoundRegionTestMixin:
56 """Tests for both UnionRegion and IntersectionRegion.
58 Concrete TestCase subclasses are responsible for adding an `instance`
59 attribute with a non-trivial instance of the `CompoundRegion` subclass
60 being tested.
61 """
63 def setUp(self):
64 self.point_in_circle = LonLat.fromDegrees(44.0, 45.0)
65 self.point_in_box = LonLat.fromDegrees(46.0, 45.0)
66 self.point_in_both = LonLat.fromDegrees(45.0, 45.0)
67 self.point_in_neither = LonLat.fromDegrees(45.0, 48.0)
68 self.circle = Circle(UnitVector3d(self.point_in_circle), Angle.fromDegrees(1.0))
69 self.box = Box.fromDegrees(
70 self.point_in_box.getLon().asDegrees() - 1.5,
71 self.point_in_box.getLat().asDegrees() - 1.5,
72 self.point_in_box.getLon().asDegrees() + 1.5,
73 self.point_in_box.getLat().asDegrees() + 1.5,
74 )
75 self.faraway = Circle(UnitVector3d(self.point_in_neither), Angle.fromDegrees(0.1))
76 self.operands = (self.circle, self.box)
78 def assertOperandsEqual(self, region, operands):
79 """Assert that a compound regions operands are equal to the given
80 tuple of operands.
81 """
82 self.assertCountEqual((region.cloneOperand(0), region.cloneOperand(1)), operands)
84 def assertCompoundRegionsEqual(self, a, b):
85 """Assert that two compound regions are equal.
87 CompoundRegion does not implement equality comparison because
88 regions in general do not, and hence it cannot delegate that operation
89 to its operands. But the concrete operands (circle and box) we use in
90 these tests do implement equality comparison.
91 """
92 self.assertEqual(type(a), type(b))
93 self.assertOperandsEqual(a, (b.cloneOperand(0), b.cloneOperand(1)))
95 def testSetUp(self):
96 """Test that the points and operand regions being tested have the
97 relationships expected.
98 """
99 self.assertTrue(self.circle.contains(UnitVector3d(self.point_in_circle)))
100 self.assertTrue(self.circle.contains(UnitVector3d(self.point_in_both)))
101 self.assertFalse(self.circle.contains(UnitVector3d(self.point_in_box)))
102 self.assertFalse(self.circle.contains(UnitVector3d(self.point_in_neither)))
103 self.assertTrue(self.box.contains(UnitVector3d(self.point_in_box)))
104 self.assertTrue(self.box.contains(UnitVector3d(self.point_in_both)))
105 self.assertFalse(self.box.contains(UnitVector3d(self.point_in_circle)))
106 self.assertFalse(self.box.contains(UnitVector3d(self.point_in_neither)))
107 self.assertEqual(self.circle.relate(self.circle), CONTAINS | WITHIN)
108 self.assertEqual(self.circle.relate(self.box), INTERSECTS)
109 self.assertEqual(self.circle.relate(self.faraway), DISJOINT)
110 self.assertEqual(self.box.relate(self.circle), INTERSECTS)
111 self.assertEqual(self.box.relate(self.box), CONTAINS | WITHIN)
112 self.assertEqual(self.box.relate(self.faraway), DISJOINT)
114 def testOperands(self):
115 """Test the cloneOperands accessor."""
116 self.assertOperandsEqual(self.instance, self.operands)
118 def testCodec(self):
119 """Test that encode and decode round-trip."""
120 s = self.instance.encode()
121 self.assertCompoundRegionsEqual(type(self.instance).decode(s), self.instance)
122 self.assertCompoundRegionsEqual(CompoundRegion.decode(s), self.instance)
123 self.assertCompoundRegionsEqual(Region.decode(s), self.instance)
125 def testPickle(self):
126 """Test pickling round-trips."""
127 s = pickle.dumps(self.instance, pickle.HIGHEST_PROTOCOL)
128 self.assertCompoundRegionsEqual(pickle.loads(s), self.instance)
130 def testString(self):
131 """Test that repr returns a string that can be eval'd to yield an
132 equivalent instance.
133 """
134 self.assertCompoundRegionsEqual(
135 self.instance,
136 eval(
137 repr(self.instance),
138 {
139 "UnionRegion": UnionRegion,
140 "IntersectionRegion": IntersectionRegion,
141 "Box": Box,
142 "Circle": Circle,
143 "UnitVector3d": UnitVector3d,
144 "Angle": Angle,
145 "AngleInterval": AngleInterval,
146 "NormalizedAngleInterval": NormalizedAngleInterval,
147 },
148 ),
149 )
151 @unittest.skipIf(not yaml, "YAML module can not be imported")
152 def testYaml(self):
153 """Test that YAML dump and load round-trip."""
154 self.assertCompoundRegionsEqual(yaml.safe_load(yaml.dump(self.instance)), self.instance)
157class UnionRegionTestCase(CompoundRegionTestMixin, unittest.TestCase):
158 """Test UnionRegion."""
160 def setUp(self):
161 CompoundRegionTestMixin.setUp(self)
162 self.instance = UnionRegion(*self.operands)
164 def testContains(self):
165 """Test point-in-region checks."""
166 self.assertTrue(self.instance.contains(UnitVector3d(self.point_in_both)))
167 self.assertTrue(self.instance.contains(UnitVector3d(self.point_in_circle)))
168 self.assertTrue(self.instance.contains(UnitVector3d(self.point_in_box)))
169 self.assertFalse(self.instance.contains(UnitVector3d(self.point_in_neither)))
171 def testRelate(self):
172 """Test region-region relationship checks."""
173 self.assertEqual(self.instance.relate(self.circle), CONTAINS)
174 self.assertEqual(self.instance.relate(self.box), CONTAINS)
175 self.assertEqual(self.instance.relate(self.faraway), DISJOINT)
176 self.assertEqual(self.circle.relate(self.instance), WITHIN)
177 self.assertEqual(self.box.relate(self.instance), WITHIN)
178 self.assertEqual(self.faraway.relate(self.instance), DISJOINT)
181class IntersectionRegionTestCase(CompoundRegionTestMixin, unittest.TestCase):
182 """Test intersection region."""
184 def setUp(self):
185 CompoundRegionTestMixin.setUp(self)
186 self.instance = IntersectionRegion(*self.operands)
188 def testContains(self):
189 """Test point-in-region checks."""
190 self.assertTrue(self.instance.contains(UnitVector3d(self.point_in_both)))
191 self.assertFalse(self.instance.contains(UnitVector3d(self.point_in_circle)))
192 self.assertFalse(self.instance.contains(UnitVector3d(self.point_in_box)))
193 self.assertFalse(self.instance.contains(UnitVector3d(self.point_in_neither)))
195 def testRelate(self):
196 """Test region-region relationship checks."""
197 self.assertEqual(self.instance.relate(self.box), WITHIN)
198 self.assertEqual(self.instance.relate(self.circle), WITHIN)
199 self.assertEqual(self.instance.relate(self.faraway), DISJOINT)
200 self.assertEqual(self.circle.relate(self.instance), CONTAINS)
201 self.assertEqual(self.box.relate(self.instance), CONTAINS)
202 self.assertEqual(self.faraway.relate(self.instance), DISJOINT)
205if __name__ == "__main__":
206 unittest.main()