Coverage for tests/test_exposureTable.py: 18%

197 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2023-07-08 03:13 -0700

1# This file is part of afw. 

2# 

3# Developed for the LSST Data Management System. 

4# This product includes software developed by the LSST Project 

5# (https://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 program is free software: you can redistribute it and/or modify 

10# it under the terms of the GNU General Public License as published by 

11# the Free Software Foundation, either version 3 of the License, or 

12# (at your option) any later version. 

13# 

14# This program is distributed in the hope that it will be useful, 

15# but WITHOUT ANY WARRANTY; without even the implied warranty of 

16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

17# GNU General Public License for more details. 

18# 

19# You should have received a copy of the GNU General Public License 

20# along with this program. If not, see <https://www.gnu.org/licenses/>. 

21 

22""" 

23Tests for lsst.afw.table.ExposureTable 

24 

25Run with: 

26 python test_exposureTable.py 

27or 

28 pytest test_exposureTable.py 

29""" 

30 

31import os.path 

32import unittest 

33 

34import numpy as np 

35 

36import lsst.utils.tests 

37import lsst.pex.exceptions 

38from lsst.daf.base import DateTime, PropertySet, PropertyList 

39import lsst.geom 

40import lsst.afw.table 

41from lsst.afw.coord import Observatory, Weather 

42from lsst.geom import arcseconds, degrees, radians, Point2D, Extent2D, Box2D, SpherePoint 

43from lsst.afw.geom import Polygon, makeSkyWcs 

44import lsst.afw.image 

45import lsst.afw.detection 

46from lsst.afw.cameraGeom.testUtils import DetectorWrapper 

47from testTableArchivesLib import DummyPsf 

48 

49 

50class ExposureTableTestCase(lsst.utils.tests.TestCase): 

51 

52 @staticmethod 

53 def createWcs(): 

54 metadata = PropertySet() 

55 metadata.set("SIMPLE", "T") 

56 metadata.set("BITPIX", -32) 

57 metadata.set("NAXIS", 2) 

58 metadata.set("NAXIS1", 1024) 

59 metadata.set("NAXIS2", 1153) 

60 metadata.set("RADESYS", 'FK5') 

61 metadata.set("EQUINOX", 2000.) 

62 metadata.setDouble("CRVAL1", 215.604025685476) 

63 metadata.setDouble("CRVAL2", 53.1595451514076) 

64 metadata.setDouble("CRPIX1", 1109.99981456774) 

65 metadata.setDouble("CRPIX2", 560.018167811613) 

66 metadata.set("CTYPE1", 'RA---SIN') 

67 metadata.set("CTYPE2", 'DEC--SIN') 

68 metadata.setDouble("CD1_1", 5.10808596133527E-05) 

69 metadata.setDouble("CD1_2", 1.85579539217196E-07) 

70 metadata.setDouble("CD2_2", -5.10281493481982E-05) 

71 metadata.setDouble("CD2_1", -8.27440751733828E-07) 

72 return makeSkyWcs(metadata) 

73 

74 @staticmethod 

75 def createVisitInfo(): 

76 return lsst.afw.image.VisitInfo( 

77 10.01, 

78 11.02, 

79 DateTime(65321.1, DateTime.MJD, DateTime.TAI), 

80 12345.1, 

81 45.1*degrees, 

82 SpherePoint(23.1*degrees, 73.2*degrees), 

83 SpherePoint(134.5*degrees, 33.3*degrees), 

84 1.73, 

85 73.2*degrees, 

86 lsst.afw.image.RotType.SKY, 

87 Observatory(11.1*degrees, 22.2*degrees, 0.333), 

88 Weather(1.1, 2.2, 34.5), 

89 "testCam" 

90 ) 

91 

92 @staticmethod 

93 def makePolygon(): 

94 return Polygon([Point2D(1, 2), Point2D(2, 1)]) 

95 

96 def comparePsfs(self, psf1, psf2): 

97 self.assertIsNotNone(psf1) 

98 self.assertIsNotNone(psf2) 

99 self.assertEqual(psf1, psf2) 

100 

101 def setUp(self): 

102 np.random.seed(1) 

103 schema = lsst.afw.table.ExposureTable.makeMinimalSchema() 

104 self.ka = schema.addField("a", type=np.float64, doc="doc for a") 

105 self.kb = schema.addField("b", type=np.int64, doc="doc for b") 

106 self.cat = lsst.afw.table.ExposureCatalog(schema) 

107 self.plist = PropertyList() 

108 self.plist['VALUE'] = 1.0 

109 self.cat.setMetadata(self.plist) 

110 self.wcs = self.createWcs() 

111 self.psf = DummyPsf(2.0) 

112 self.bbox0 = lsst.geom.Box2I( 

113 lsst.geom.Box2D( 

114 self.wcs.getPixelOrigin() - lsst.geom.Extent2D(5.0, 4.0), 

115 self.wcs.getPixelOrigin() + lsst.geom.Extent2D(20.0, 30.0) 

116 ) 

117 ) 

118 self.bbox1 = lsst.geom.Box2I( 

119 lsst.geom.Box2D( 

120 self.wcs.getPixelOrigin() - lsst.geom.Extent2D(15.0, 40.0), 

121 self.wcs.getPixelOrigin() + lsst.geom.Extent2D(3.0, 6.0) 

122 ) 

123 ) 

124 # these numbers are what were persisted as `Calib` objects in the files. 

125 self.photoCalib = lsst.afw.image.makePhotoCalibFromCalibZeroPoint(56.0, 2.2) 

126 self.visitInfo = self.createVisitInfo() 

127 self.detector = DetectorWrapper().detector 

128 record0 = self.cat.addNew() 

129 record0.setId(1) 

130 record0.set(self.ka, np.pi) 

131 record0.set(self.kb, 4) 

132 record0.setBBox(self.bbox0) 

133 record0.setPsf(self.psf) 

134 record0.setWcs(self.wcs) 

135 record0.setPhotoCalib(self.photoCalib) 

136 record0.setVisitInfo(self.visitInfo) 

137 record0.setValidPolygon(None) 

138 record0.setDetector(None) 

139 record1 = self.cat.addNew() 

140 record1.setId(2) 

141 record1.set(self.ka, 2.5) 

142 record1.set(self.kb, 2) 

143 record1.setWcs(self.wcs) 

144 record1.setBBox(self.bbox1) 

145 record1.setValidPolygon(self.makePolygon()) 

146 record1.setDetector(self.detector) 

147 

148 def tearDown(self): 

149 del self.cat 

150 del self.psf 

151 del self.wcs 

152 del self.photoCalib 

153 del self.visitInfo 

154 del self.detector 

155 

156 def testAccessors(self): 

157 record0 = self.cat[0] 

158 record1 = self.cat[1] 

159 self.assertEqual(record0.getId(), 1) 

160 self.assertEqual(record1.getId(), 2) 

161 self.assertEqual(record0.getWcs(), self.wcs) 

162 self.assertEqual(record1.getWcs(), self.wcs) 

163 self.assertEqual(record0.getBBox(), self.bbox0) 

164 self.assertEqual(record1.getBBox(), self.bbox1) 

165 self.comparePsfs(record0.getPsf(), self.psf) 

166 self.assertIsNone(record1.getPsf()) 

167 self.assertEqual(record0.getPhotoCalib(), self.photoCalib) 

168 self.assertIsNone(record1.getPhotoCalib()) 

169 self.assertEqual(record0.getVisitInfo(), self.visitInfo) 

170 self.assertIsNone(record1.getVisitInfo()) 

171 self.assertEqual(record0.getValidPolygon(), None) 

172 self.assertEqual(record1.getValidPolygon(), self.makePolygon()) 

173 self.assertIsNone(record0.getDetector()) 

174 self.assertDetectorsEqual(record1.getDetector(), self.detector) 

175 

176 def testPersistence(self): 

177 with lsst.utils.tests.getTempFilePath(".fits") as tmpFile: 

178 self.cat.writeFits(tmpFile) 

179 cat1 = lsst.afw.table.ExposureCatalog.readFits(tmpFile) 

180 self.assertEqual(self.cat[0].get(self.ka), cat1[0].get(self.ka)) 

181 self.assertEqual(self.cat[0].get(self.kb), cat1[0].get(self.kb)) 

182 self.comparePsfs(self.cat[0].getPsf(), cat1[0].getPsf()) 

183 self.assertEqual(self.cat[0].getWcs(), cat1[0].getWcs()) 

184 self.assertEqual(self.cat[1].get(self.ka), cat1[1].get(self.ka)) 

185 self.assertEqual(self.cat[1].get(self.kb), cat1[1].get(self.kb)) 

186 self.assertEqual(self.cat[1].getWcs(), cat1[1].getWcs()) 

187 self.assertIsNone(self.cat[1].getPsf()) 

188 self.assertIsNone(self.cat[1].getPhotoCalib()) 

189 self.assertEqual(self.cat[0].getPhotoCalib(), cat1[0].getPhotoCalib()) 

190 self.assertEqual(self.cat[0].getVisitInfo(), 

191 cat1[0].getVisitInfo()) 

192 self.assertIsNone(cat1[1].getVisitInfo()) 

193 self.assertIsNone(cat1[0].getDetector()) 

194 self.assertDetectorsEqual(cat1[1].getDetector(), self.detector) 

195 # We are not checking for plist equality because reading from 

196 # fits may add extra keys; for this test we care that the 

197 # keys we set are properly round-tripped. 

198 for key in self.plist: 

199 self.assertEqual(self.plist[key], cat1.getMetadata()[key]) 

200 

201 def testGeometry(self): 

202 bigBox = lsst.geom.Box2D(lsst.geom.Box2I(self.bbox0)) 

203 bigBox.include(lsst.geom.Box2D(self.bbox1)) 

204 points = (np.random.rand(100, 2)*np.array([bigBox.getWidth(), bigBox.getHeight()]) 

205 + np.array([bigBox.getMinX(), bigBox.getMinY()])) 

206 

207 # make a very slightly perturbed wcs so the celestial transform isn't a 

208 # no-op 

209 crval2 = self.wcs.getSkyOrigin() 

210 crval2 = lsst.geom.SpherePoint(crval2.getLongitude() + 5*arcseconds, 

211 crval2.getLatitude() - 5*arcseconds) 

212 wcs2 = makeSkyWcs( 

213 crval=crval2, 

214 crpix=self.wcs.getPixelOrigin() + lsst.geom.Extent2D(30.0, -50.0), 

215 cdMatrix=self.wcs.getCdMatrix()*1.1, 

216 ) 

217 for x1, y1 in points: 

218 p1 = lsst.geom.Point2D(x1, y1) 

219 c = self.wcs.pixelToSky(p1) 

220 p2 = wcs2.skyToPixel(c) 

221 subset1 = self.cat.subsetContaining(c) 

222 subset2 = self.cat.subsetContaining(p2, wcs2) 

223 for record in self.cat: 

224 inside = lsst.geom.Box2D(record.getBBox()).contains(p1) 

225 self.assertEqual(inside, record.contains(c)) 

226 self.assertEqual(inside, record.contains(p2, wcs2)) 

227 self.assertEqual(inside, record.contains(p1, self.wcs)) 

228 self.assertEqual(inside, record in subset1) 

229 self.assertEqual(inside, record in subset2) 

230 

231 crazyPoint = lsst.geom.SpherePoint(crval2.getLongitude() + np.pi*radians, 

232 crval2.getLatitude()) 

233 subset3 = self.cat.subsetContaining(crazyPoint) 

234 self.assertEqual(len(subset3), 0) 

235 

236 def testCoaddInputs(self): 

237 coaddInputs = lsst.afw.image.CoaddInputs( 

238 lsst.afw.table.ExposureTable.makeMinimalSchema(), 

239 lsst.afw.table.ExposureTable.makeMinimalSchema() 

240 ) 

241 coaddInputs.visits.addNew().setId(2) 

242 coaddInputs.ccds.addNew().setId(3) 

243 coaddInputs.ccds.addNew().setId(4) 

244 exposureIn = lsst.afw.image.ExposureF(10, 10) 

245 exposureIn.getInfo().setCoaddInputs(coaddInputs) 

246 with lsst.utils.tests.getTempFilePath(".fits") as filename: 

247 exposureIn.writeFits(filename) 

248 exposureOut = lsst.afw.image.ExposureF(filename) 

249 coaddInputsOut = exposureOut.getInfo().getCoaddInputs() 

250 self.assertEqual(len(coaddInputsOut.visits), 1) 

251 self.assertEqual(len(coaddInputsOut.ccds), 2) 

252 self.assertEqual(coaddInputsOut.visits[0].getId(), 2) 

253 self.assertEqual(coaddInputsOut.ccds[0].getId(), 3) 

254 self.assertEqual(coaddInputsOut.ccds[1].getId(), 4) 

255 

256 def testReadV1Catalog(self): 

257 testDir = os.path.dirname(__file__) 

258 v1CatalogPath = os.path.join( 

259 testDir, "data", "exposure_catalog_v1.fits") 

260 catV1 = lsst.afw.table.ExposureCatalog.readFits(v1CatalogPath) 

261 self.assertEqual(self.cat[0].get(self.ka), catV1[0].get(self.ka)) 

262 self.assertEqual(self.cat[0].get(self.kb), catV1[0].get(self.kb)) 

263 self.comparePsfs(self.cat[0].getPsf(), catV1[0].getPsf()) 

264 bbox = Box2D(Point2D(0, 0), Extent2D(2000, 2000)) 

265 self.assertWcsAlmostEqualOverBBox(self.cat[0].getWcs(), catV1[0].getWcs(), bbox) 

266 self.assertEqual(self.cat[1].get(self.ka), catV1[1].get(self.ka)) 

267 self.assertEqual(self.cat[1].get(self.kb), catV1[1].get(self.kb)) 

268 self.assertEqual(self.cat[1].getWcs(), catV1[1].getWcs()) 

269 self.assertIsNone(self.cat[1].getPsf()) 

270 self.assertIsNone(self.cat[1].getPhotoCalib()) 

271 self.assertEqual(self.cat[0].getPhotoCalib(), catV1[0].getPhotoCalib()) 

272 self.assertIsNone(catV1[0].getVisitInfo()) 

273 self.assertIsNone(catV1[1].getVisitInfo()) 

274 

275 def testBoolArraySubset(self): 

276 i = np.array([True, False], dtype=bool) 

277 subset = self.cat[i] 

278 self.assertEqual(len(subset), 1) 

279 self.assertEqual(subset[0], self.cat[0]) 

280 

281 

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

283 pass 

284 

285 

286def setup_module(module): 

287 lsst.utils.tests.init() 

288 

289 

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

291 lsst.utils.tests.init() 

292 unittest.main()