Coverage for tests/test_ticket2707.py: 13%

83 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2023-06-15 02:47 -0700

1# 

2# LSST Data Management System 

3# Copyright 2008, 2009, 2010 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# 

22 

23import unittest 

24 

25import lsst.geom 

26import lsst.afw.table as afwTable 

27import lsst.utils.tests 

28 

29 

30class MatchXyTest(unittest.TestCase): 

31 """Test that matching sources by centroid works as expected, 

32 even when some of the centroids contain NaN. 

33 """ 

34 

35 def setUp(self): 

36 nan = float('nan') 

37 self.schema = afwTable.SourceTable.makeMinimalSchema() 

38 centroidKey = afwTable.Point2DKey.addFields( 

39 self.schema, "cen", "center", "pixels") 

40 self.table = afwTable.SourceTable.make(self.schema) 

41 self.table.defineCentroid("cen") 

42 idKey = self.table.getIdKey() 

43 self.cat1 = afwTable.SourceCatalog(self.table) 

44 self.cat2 = afwTable.SourceCatalog(self.table) 

45 self.nobj = 10 

46 self.nUniqueMatch = 0 

47 self.matchRadius = 0.1 # Matching radius to use in tests (pixels) 

48 for i in range(self.nobj): 

49 j = self.nobj - i - 1 

50 r1, r2 = self.cat1.addNew(), self.cat2.addNew() 

51 r1.set(idKey, i) 

52 r2.set(idKey, self.nobj + j) 

53 if i % 3 != 0: 

54 # These will provide the exact matches, though the two records we're setting right now won't 

55 # match each other (because cat2 counts down in reverse). 

56 r1.set(centroidKey, lsst.geom.Point2D(i, i)) 

57 r2.set(centroidKey, lsst.geom.Point2D(j, j)) 

58 self.nUniqueMatch += 1 

59 elif i == 3: 

60 # Deliberately offset position in cat2 by 2 pixels and a bit so that it will match a 

61 # different source. This gives us an extra match when we're not just getting the closest. 

62 # The "2 pixels" makes it line up with a different source. 

63 # The "a bit" is half a match radius, so that it's still within the matching radius, but it 

64 # doesn't match another source exactly. If it matches another source exactly, then it's not 

65 # clear which one will be taken as the match (in fact, it 

66 # appears to depend on the compiler). 

67 offset = 2 + 0.5*self.matchRadius 

68 r1.set(centroidKey, lsst.geom.Point2D(i, i)) 

69 r2.set(centroidKey, lsst.geom.Point2D(j + offset, j + offset)) 

70 else: 

71 # Test that we can handle NANs 

72 r1.set(centroidKey, lsst.geom.Point2D(nan, nan)) 

73 r2.set(centroidKey, lsst.geom.Point2D(nan, nan)) 

74 

75 def tearDown(self): 

76 del self.cat2 

77 del self.cat1 

78 del self.table 

79 del self.schema 

80 

81 def testMatchXy(self): 

82 matches = afwTable.matchXy(self.cat1, self.cat2, self.matchRadius) 

83 self.assertEqual(len(matches), self.nUniqueMatch) 

84 

85 for m in matches: 

86 self.assertEqual(m.first.getId() + self.nobj, m.second.getId()) 

87 self.assertEqual(m.distance, 0.0) 

88 

89 def testMatchXyMatchControl(self): 

90 """Test using MatchControl to return all matches 

91 

92 Also tests closest==False at the same time 

93 """ 

94 for closest in (True, False): 

95 for includeMismatches in (True, False): 

96 mc = afwTable.MatchControl() 

97 mc.findOnlyClosest = closest 

98 mc.includeMismatches = includeMismatches 

99 matches = afwTable.matchXy( 

100 self.cat1, self.cat2, self.matchRadius, mc) 

101 

102 if False: 

103 for m in matches: 

104 print(closest, m.first.getId(), 

105 m.second.getId(), m.distance) 

106 

107 if includeMismatches: 

108 catMatches = afwTable.SourceCatalog(self.table) 

109 catMismatches = afwTable.SourceCatalog(self.table) 

110 for m in matches: 

111 if m[1] is not None: 

112 if not any(x == m[0] for x in catMatches): 

113 catMatches.append(m[0]) 

114 else: 

115 catMismatches.append(m[0]) 

116 matches = afwTable.matchXy( 

117 catMatches, self.cat2, self.matchRadius, mc) 

118 mc.includeMismatches = False 

119 noMatches = afwTable.matchXy( 

120 catMismatches, self.cat2, self.matchRadius, mc) 

121 self.assertEqual(len(noMatches), 0) 

122 

123 # If we're not getting only the closest match, then we get an extra match due to the 

124 # source we offset by 2 pixels and a bit. Everything else 

125 # should match exactly. 

126 self.assertEqual( 

127 len(matches), 

128 self.nUniqueMatch if closest else self.nUniqueMatch + 1) 

129 self.assertEqual( 

130 sum(1 for m in matches if m.distance == 0.0), 

131 self.nUniqueMatch) 

132 for m in matches: 

133 if closest: 

134 self.assertEqual(m.first.getId() + self.nobj, m.second.getId()) 

135 else: 

136 self.assertLessEqual(m.distance, self.matchRadius) 

137 

138 def testSelfMatchXy(self): 

139 """Test doing a self-matches""" 

140 for symmetric in (True, False): 

141 mc = afwTable.MatchControl() 

142 mc.symmetricMatch = symmetric 

143 matches = afwTable.matchXy(self.cat2, self.matchRadius, mc) 

144 

145 if False: 

146 for m in matches: 

147 print(m.first.getId(), m.second.getId(), m.distance) 

148 

149 # There is only one source that matches another source when we do a self-match: the one 

150 # offset by 2 pixels and a bit. 

151 # If we're getting symmetric matches, that multiplies the expected number by 2 because it 

152 # produces s1,s2 and s2,s1. 

153 self.assertEqual(len(matches), 2 if symmetric else 1) 

154 

155 

156class MemoryTester(lsst.utils.tests.MemoryTestCase): 

157 pass 

158 

159 

160def setup_module(module): 

161 lsst.utils.tests.init() 

162 

163 

164if __name__ == "__main__": 164 ↛ 165line 164 didn't jump to line 165, because the condition on line 164 was never true

165 lsst.utils.tests.init() 

166 unittest.main()