Coverage for tests/test_exposureTable.py: 17%

236 statements  

« prev     ^ index     » next       coverage.py v7.5.1, created at 2024-05-16 03:19 -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 

22import os.path 

23import unittest 

24 

25import numpy as np 

26 

27import lsst.utils.tests 

28import lsst.pex.exceptions 

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

30import lsst.geom 

31import lsst.afw.table 

32from lsst.afw.coord import Observatory, Weather 

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

34from lsst.afw.geom import Polygon, makeSkyWcs 

35import lsst.afw.image 

36import lsst.afw.detection 

37from lsst.afw.cameraGeom.testUtils import DetectorWrapper 

38from testTableArchivesLib import DummyPsf 

39 

40 

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

42 

43 @staticmethod 

44 def createWcs(): 

45 metadata = PropertySet() 

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

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

48 metadata.set("NAXIS", 2) 

49 metadata.set("NAXIS1", 1024) 

50 metadata.set("NAXIS2", 1153) 

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

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

53 metadata.setDouble("CRVAL1", 215.604025685476) 

54 metadata.setDouble("CRVAL2", 53.1595451514076) 

55 metadata.setDouble("CRPIX1", 1109.99981456774) 

56 metadata.setDouble("CRPIX2", 560.018167811613) 

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

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

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

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

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

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

63 return makeSkyWcs(metadata) 

64 

65 @staticmethod 

66 def createVisitInfo(): 

67 return lsst.afw.image.VisitInfo( 

68 10.01, 

69 11.02, 

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

71 12345.1, 

72 45.1*degrees, 

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

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

75 1.73, 

76 73.2*degrees, 

77 lsst.afw.image.RotType.SKY, 

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

79 Weather(1.1, 2.2, 34.5), 

80 "testCam" 

81 ) 

82 

83 @staticmethod 

84 def makePolygon(): 

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

86 

87 def comparePsfs(self, psf1, psf2): 

88 self.assertIsNotNone(psf1) 

89 self.assertIsNotNone(psf2) 

90 self.assertEqual(psf1, psf2) 

91 

92 def setUp(self): 

93 np.random.seed(1) 

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

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

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

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

98 self.plist = PropertyList() 

99 self.plist['VALUE'] = 1.0 

100 self.cat.setMetadata(self.plist) 

101 self.wcs = self.createWcs() 

102 self.psf = DummyPsf(2.0) 

103 self.bbox0 = lsst.geom.Box2I( 

104 lsst.geom.Box2D( 

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

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

107 ) 

108 ) 

109 self.bbox1 = lsst.geom.Box2I( 

110 lsst.geom.Box2D( 

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

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

113 ) 

114 ) 

115 self.apCorrMap = lsst.afw.image.ApCorrMap() 

116 self.transmissionCurve = lsst.afw.image.TransmissionCurve.makeIdentity() 

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

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

119 self.visitInfo = self.createVisitInfo() 

120 self.detector = DetectorWrapper().detector 

121 record0 = self.cat.addNew() 

122 record0.setId(1) 

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

124 record0.set(self.kb, 4) 

125 record0.setBBox(self.bbox0) 

126 record0.setPsf(self.psf) 

127 record0.setWcs(self.wcs) 

128 record0.setPhotoCalib(self.photoCalib) 

129 record0.setVisitInfo(self.visitInfo) 

130 record0.setValidPolygon(None) 

131 record0.setDetector(None) 

132 record1 = self.cat.addNew() 

133 record1.setId(2) 

134 record1.set(self.ka, 2.5) 

135 record1.set(self.kb, 2) 

136 record1.setWcs(self.wcs) 

137 record1.setBBox(self.bbox1) 

138 record1.setValidPolygon(self.makePolygon()) 

139 record1.setDetector(self.detector) 

140 record1.setApCorrMap(self.apCorrMap) 

141 record1.setTransmissionCurve(self.transmissionCurve) 

142 

143 def tearDown(self): 

144 del self.cat 

145 del self.psf 

146 del self.wcs 

147 del self.photoCalib 

148 del self.visitInfo 

149 del self.detector 

150 

151 def testAccessors(self): 

152 record0 = self.cat[0] 

153 record1 = self.cat[1] 

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

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

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

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

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

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

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

161 self.assertIsNone(record1.getPsf()) 

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

163 self.assertIsNone(record1.getPhotoCalib()) 

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

165 self.assertIsNone(record1.getVisitInfo()) 

166 self.assertIsNone(record0.getValidPolygon()) 

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

168 self.assertIsNone(record0.getDetector()) 

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

170 self.assertEqual(record1.getApCorrMap(), self.apCorrMap) 

171 self.assertEqual(record1.getTransmissionCurve(), self.transmissionCurve) 

172 

173 def testProperties(self): 

174 """Test that we can get/set with the properties; some of them are 

175 readonly, so check that those raise if set. 

176 """ 

177 # getters 

178 record = self.cat[1] 

179 self.assertEqual(record.id, 2) 

180 self.assertEqual(record.wcs, self.wcs) 

181 self.assertIsNone(record.psf) 

182 self.assertIsNone(record.photoCalib) 

183 self.assertIsNone(record.visitInfo) 

184 self.assertEqual(record.validPolygon, self.makePolygon()) 

185 self.assertDetectorsEqual(record.detector, self.detector) 

186 self.assertEqual(record.apCorrMap, self.apCorrMap) 

187 self.assertEqual(record.transmissionCurve, self.transmissionCurve) 

188 self.assertEqual(record.table, record.getTable()) 

189 

190 # no property for bbox: it is returned by value, but is mutable. 

191 with self.assertRaises(AttributeError): 

192 record.bbox 

193 

194 # table has no setter 

195 with self.assertRaises(AttributeError): 

196 record.table = None 

197 

198 # read/write properties 

199 record.id = 10 

200 self.assertEqual(record.id, 10) 

201 record.wcs = None 

202 self.assertIsNone(record.wcs) 

203 record.psf = self.psf 

204 self.assertEqual(record.psf, self.psf) 

205 record.photoCalib = self.photoCalib 

206 self.assertEqual(record.photoCalib, self.photoCalib) 

207 record.visitInfo = self.visitInfo 

208 self.assertEqual(record.visitInfo, self.visitInfo) 

209 record.validPolygon = None 

210 self.assertIsNone(record.validPolygon) 

211 record.detector = None 

212 self.assertIsNone(record.detector) 

213 record.apCorrMap = None 

214 self.assertIsNone(record.apCorrMap) 

215 record.transmissionCurve = None 

216 self.assertIsNone(record.transmissionCurve) 

217 

218 def testPersistence(self): 

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

220 self.cat.writeFits(tmpFile) 

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

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

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

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

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

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

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

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

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

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

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

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

233 cat1[0].getVisitInfo()) 

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

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

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

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

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

239 # keys we set are properly round-tripped. 

240 for key in self.plist: 

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

242 

243 def testGeometry(self): 

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

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

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

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

248 

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

250 # no-op 

251 crval2 = self.wcs.getSkyOrigin() 

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

253 crval2.getLatitude() - 5*arcseconds) 

254 wcs2 = makeSkyWcs( 

255 crval=crval2, 

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

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

258 ) 

259 for x1, y1 in points: 

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

261 c = self.wcs.pixelToSky(p1) 

262 p2 = wcs2.skyToPixel(c) 

263 subset1 = self.cat.subsetContaining(c) 

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

265 for record in self.cat: 

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

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

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

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

270 self.assertEqual(inside, record in subset1) 

271 self.assertEqual(inside, record in subset2) 

272 

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

274 crval2.getLatitude()) 

275 subset3 = self.cat.subsetContaining(crazyPoint) 

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

277 

278 def testCoaddInputs(self): 

279 coaddInputs = lsst.afw.image.CoaddInputs( 

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

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

282 ) 

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

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

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

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

287 exposureIn.getInfo().setCoaddInputs(coaddInputs) 

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

289 exposureIn.writeFits(filename) 

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

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

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

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

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

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

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

297 

298 def testReadV1Catalog(self): 

299 testDir = os.path.dirname(__file__) 

300 v1CatalogPath = os.path.join( 

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

316 

317 def testBoolArraySubset(self): 

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

319 subset = self.cat[i] 

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

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

322 

323 

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

325 pass 

326 

327 

328def setup_module(module): 

329 lsst.utils.tests.init() 

330 

331 

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

333 lsst.utils.tests.init() 

334 unittest.main()