Coverage for tests/test_exposure.py: 11%

652 statements  

« prev     ^ index     » next       coverage.py v6.5.0, created at 2022-12-14 03:43 -0800

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""" 

23Test lsst.afw.image.Exposure 

24""" 

25 

26import os.path 

27import unittest 

28 

29import numpy as np 

30from numpy.testing import assert_allclose 

31import yaml 

32import astropy.units as units 

33 

34import lsst.utils 

35import lsst.utils.tests 

36import lsst.geom 

37import lsst.afw.image as afwImage 

38from lsst.afw.coord import Weather 

39import lsst.afw.geom as afwGeom 

40import lsst.afw.table as afwTable 

41import lsst.pex.exceptions as pexExcept 

42from lsst.afw.fits import readMetadata, FitsError 

43from lsst.afw.cameraGeom.testUtils import DetectorWrapper 

44from lsst.log import Log 

45from testTableArchivesLib import DummyPsf 

46 

47Log.getLogger("lsst.afw.image.Mask").setLevel(Log.INFO) 

48 

49try: 

50 dataDir = os.path.join(lsst.utils.getPackageDir("afwdata"), "data") 

51except LookupError: 

52 dataDir = None 

53else: 

54 InputMaskedImageName = "871034p_1_MI.fits" 

55 InputMaskedImageNameSmall = "small_MI.fits" 

56 InputImageNameSmall = "small" 

57 OutputMaskedImageName = "871034p_1_MInew.fits" 

58 

59 currDir = os.path.abspath(os.path.dirname(__file__)) 

60 inFilePath = os.path.join(dataDir, InputMaskedImageName) 

61 inFilePathSmall = os.path.join(dataDir, InputMaskedImageNameSmall) 

62 inFilePathSmallImage = os.path.join(dataDir, InputImageNameSmall) 

63 

64 

65@unittest.skipIf(dataDir is None, "afwdata not setup") 

66class ExposureTestCase(lsst.utils.tests.TestCase): 

67 """ 

68 A test case for the Exposure Class 

69 """ 

70 

71 def setUp(self): 

72 maskedImage = afwImage.MaskedImageF(inFilePathSmall) 

73 maskedImageMD = readMetadata(inFilePathSmall) 

74 

75 self.smallExposure = afwImage.ExposureF(inFilePathSmall) 

76 self.width = maskedImage.getWidth() 

77 self.height = maskedImage.getHeight() 

78 self.wcs = afwGeom.makeSkyWcs(maskedImageMD, False) 

79 self.md = maskedImageMD 

80 self.psf = DummyPsf(2.0) 

81 self.detector = DetectorWrapper().detector 

82 self.id = 42 

83 self.extras = {"MISC": DummyPsf(3.5)} 

84 

85 self.exposureBlank = afwImage.ExposureF() 

86 self.exposureMiOnly = afwImage.makeExposure(maskedImage) 

87 self.exposureMiWcs = afwImage.makeExposure(maskedImage, self.wcs) 

88 # n.b. the (100, 100, ...) form 

89 self.exposureCrWcs = afwImage.ExposureF(100, 100, self.wcs) 

90 # test with ExtentI(100, 100) too 

91 self.exposureCrOnly = afwImage.ExposureF(lsst.geom.ExtentI(100, 100)) 

92 

93 def tearDown(self): 

94 del self.smallExposure 

95 del self.wcs 

96 del self.psf 

97 del self.detector 

98 del self.extras 

99 

100 del self.exposureBlank 

101 del self.exposureMiOnly 

102 del self.exposureMiWcs 

103 del self.exposureCrWcs 

104 del self.exposureCrOnly 

105 

106 def testGetMaskedImage(self): 

107 """ 

108 Test to ensure a MaskedImage can be obtained from each 

109 Exposure. An Exposure is required to have a MaskedImage, 

110 therefore each of the Exposures should return a MaskedImage. 

111 

112 MaskedImage class should throw appropriate 

113 lsst::pex::exceptions::NotFound if the MaskedImage can not be 

114 obtained. 

115 """ 

116 maskedImageBlank = self.exposureBlank.getMaskedImage() 

117 blankWidth = maskedImageBlank.getWidth() 

118 blankHeight = maskedImageBlank.getHeight() 

119 if blankWidth != blankHeight != 0: 

120 self.fail(f"{blankWidth} = {blankHeight} != 0") 

121 

122 maskedImageMiOnly = self.exposureMiOnly.getMaskedImage() 

123 miOnlyWidth = maskedImageMiOnly.getWidth() 

124 miOnlyHeight = maskedImageMiOnly.getHeight() 

125 self.assertAlmostEqual(miOnlyWidth, self.width) 

126 self.assertAlmostEqual(miOnlyHeight, self.height) 

127 

128 # NOTE: Unittests for Exposures created from a MaskedImage and 

129 # a WCS object are incomplete. No way to test the validity of 

130 # the WCS being copied/created. 

131 

132 maskedImageMiWcs = self.exposureMiWcs.getMaskedImage() 

133 miWcsWidth = maskedImageMiWcs.getWidth() 

134 miWcsHeight = maskedImageMiWcs.getHeight() 

135 self.assertAlmostEqual(miWcsWidth, self.width) 

136 self.assertAlmostEqual(miWcsHeight, self.height) 

137 

138 maskedImageCrWcs = self.exposureCrWcs.getMaskedImage() 

139 crWcsWidth = maskedImageCrWcs.getWidth() 

140 crWcsHeight = maskedImageCrWcs.getHeight() 

141 if crWcsWidth != crWcsHeight != 0: 

142 self.fail(f"{crWcsWidth} != {crWcsHeight} != 0") 

143 

144 maskedImageCrOnly = self.exposureCrOnly.getMaskedImage() 

145 crOnlyWidth = maskedImageCrOnly.getWidth() 

146 crOnlyHeight = maskedImageCrOnly.getHeight() 

147 if crOnlyWidth != crOnlyHeight != 0: 

148 self.fail(f"{crOnlyWidth} != {crOnlyHeight} != 0") 

149 

150 # Check Exposure.getWidth() returns the MaskedImage's width 

151 self.assertEqual(crOnlyWidth, self.exposureCrOnly.getWidth()) 

152 self.assertEqual(crOnlyHeight, self.exposureCrOnly.getHeight()) 

153 # check width/height properties 

154 self.assertEqual(crOnlyWidth, self.exposureCrOnly.width) 

155 self.assertEqual(crOnlyHeight, self.exposureCrOnly.height) 

156 

157 def testProperties(self): 

158 self.assertMaskedImagesEqual(self.exposureMiOnly.maskedImage, 

159 self.exposureMiOnly.getMaskedImage()) 

160 mi2 = afwImage.MaskedImageF(self.exposureMiOnly.getDimensions()) 

161 mi2.image.array[:] = 5.0 

162 mi2.variance.array[:] = 3.0 

163 mi2.mask.array[:] = 0x1 

164 self.exposureMiOnly.maskedImage = mi2 

165 self.assertMaskedImagesEqual(self.exposureMiOnly.maskedImage, mi2) 

166 self.assertImagesEqual(self.exposureMiOnly.image, 

167 self.exposureMiOnly.maskedImage.image) 

168 

169 image3 = afwImage.ImageF(self.exposureMiOnly.getDimensions()) 

170 image3.array[:] = 3.0 

171 self.exposureMiOnly.image = image3 

172 self.assertImagesEqual(self.exposureMiOnly.image, image3) 

173 

174 mask3 = afwImage.MaskX(self.exposureMiOnly.getDimensions()) 

175 mask3.array[:] = 0x2 

176 self.exposureMiOnly.mask = mask3 

177 self.assertMasksEqual(self.exposureMiOnly.mask, mask3) 

178 

179 var3 = afwImage.ImageF(self.exposureMiOnly.getDimensions()) 

180 var3.array[:] = 2.0 

181 self.exposureMiOnly.variance = var3 

182 self.assertImagesEqual(self.exposureMiOnly.variance, var3) 

183 

184 # Test the property getter for a null VisitInfo. 

185 self.assertIsNone(self.exposureMiOnly.visitInfo) 

186 

187 def testGetWcs(self): 

188 """Test that a WCS can be obtained from each Exposure created with 

189 a WCS, and that an Exposure lacking a WCS returns None. 

190 """ 

191 # These exposures don't contain a WCS 

192 self.assertIsNone(self.exposureBlank.getWcs()) 

193 self.assertIsNone(self.exposureMiOnly.getWcs()) 

194 self.assertIsNone(self.exposureCrOnly.getWcs()) 

195 

196 # These exposures should contain a WCS 

197 self.assertEqual(self.wcs, self.exposureMiWcs.getWcs()) 

198 self.assertEqual(self.wcs, self.exposureCrWcs.getWcs()) 

199 

200 def testExposureInfoConstructor(self): 

201 """Test the Exposure(maskedImage, exposureInfo) constructor""" 

202 exposureInfo = afwImage.ExposureInfo() 

203 exposureInfo.setWcs(self.wcs) 

204 exposureInfo.setDetector(self.detector) 

205 gFilterLabel = afwImage.FilterLabel(band="g") 

206 exposureInfo.setFilter(gFilterLabel) 

207 maskedImage = afwImage.MaskedImageF(inFilePathSmall) 

208 exposure = afwImage.ExposureF(maskedImage, exposureInfo) 

209 

210 self.assertTrue(exposure.hasWcs()) 

211 self.assertEqual(exposure.getWcs().getPixelOrigin(), 

212 self.wcs.getPixelOrigin()) 

213 self.assertEqual(exposure.getDetector().getName(), 

214 self.detector.getName()) 

215 self.assertEqual(exposure.getDetector().getSerial(), 

216 self.detector.getSerial()) 

217 self.assertEqual(exposure.getFilter(), gFilterLabel) 

218 

219 self.assertTrue(exposure.getInfo().hasWcs()) 

220 # check the ExposureInfo property 

221 self.assertTrue(exposure.info.hasWcs()) 

222 self.assertEqual(exposure.getInfo().getWcs().getPixelOrigin(), 

223 self.wcs.getPixelOrigin()) 

224 self.assertEqual(exposure.getInfo().getDetector().getName(), 

225 self.detector.getName()) 

226 self.assertEqual(exposure.getInfo().getDetector().getSerial(), 

227 self.detector.getSerial()) 

228 self.assertEqual(exposure.getInfo().getFilter(), gFilterLabel) 

229 

230 def testNullWcs(self): 

231 """Test that an Exposure constructed with second argument None is usable 

232 

233 When the exposureInfo constructor was first added, trying to get a WCS 

234 or other info caused a segfault because the ExposureInfo did not exist. 

235 """ 

236 maskedImage = self.exposureMiOnly.getMaskedImage() 

237 exposure = afwImage.ExposureF(maskedImage, None) 

238 self.assertFalse(exposure.hasWcs()) 

239 self.assertFalse(exposure.hasPsf()) 

240 

241 def testExposureInfoSetNone(self): 

242 exposureInfo = afwImage.ExposureInfo() 

243 exposureInfo.setDetector(None) 

244 exposureInfo.setValidPolygon(None) 

245 exposureInfo.setPsf(None) 

246 exposureInfo.setWcs(None) 

247 exposureInfo.setPhotoCalib(None) 

248 exposureInfo.setCoaddInputs(None) 

249 exposureInfo.setVisitInfo(None) 

250 exposureInfo.setApCorrMap(None) 

251 for key in self.extras: 

252 exposureInfo.setComponent(key, None) 

253 

254 def testSetExposureInfo(self): 

255 exposureInfo = afwImage.ExposureInfo() 

256 exposureInfo.setWcs(self.wcs) 

257 exposureInfo.setDetector(self.detector) 

258 gFilterLabel = afwImage.FilterLabel(band="g") 

259 exposureInfo.setFilter(gFilterLabel) 

260 exposureInfo.setId(self.id) 

261 maskedImage = afwImage.MaskedImageF(inFilePathSmall) 

262 exposure = afwImage.ExposureF(maskedImage) 

263 self.assertFalse(exposure.hasWcs()) 

264 

265 exposure.setInfo(exposureInfo) 

266 

267 self.assertTrue(exposure.hasWcs()) 

268 self.assertEqual(exposure.getWcs().getPixelOrigin(), 

269 self.wcs.getPixelOrigin()) 

270 self.assertEqual(exposure.getDetector().getName(), 

271 self.detector.getName()) 

272 self.assertEqual(exposure.getDetector().getSerial(), 

273 self.detector.getSerial()) 

274 self.assertEqual(exposure.getFilter(), gFilterLabel) 

275 

276 # test properties 

277 self.assertEqual(exposure.detector.getName(), self.detector.getName()) 

278 self.assertEqual(exposure.filter, gFilterLabel) 

279 self.assertEqual(exposure.wcs, self.wcs) 

280 

281 def testVisitInfoFitsPersistence(self): 

282 """Test saving an exposure to FITS and reading it back in preserves (some) VisitInfo fields""" 

283 exposureId = 5 

284 exposureTime = 12.3 

285 boresightRotAngle = 45.6 * lsst.geom.degrees 

286 weather = Weather(1.1, 2.2, 0.3) 

287 visitInfo = afwImage.VisitInfo( 

288 exposureId=exposureId, 

289 exposureTime=exposureTime, 

290 boresightRotAngle=boresightRotAngle, 

291 weather=weather, 

292 ) 

293 photoCalib = afwImage.PhotoCalib(3.4, 5.6) 

294 exposureInfo = afwImage.ExposureInfo() 

295 exposureInfo.setVisitInfo(visitInfo) 

296 exposureInfo.setPhotoCalib(photoCalib) 

297 exposureInfo.setDetector(self.detector) 

298 gFilterLabel = afwImage.FilterLabel(band="g") 

299 exposureInfo.setFilter(gFilterLabel) 

300 maskedImage = afwImage.MaskedImageF(inFilePathSmall) 

301 exposure = afwImage.ExposureF(maskedImage, exposureInfo) 

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

303 exposure.writeFits(tmpFile) 

304 rtExposure = afwImage.ExposureF(tmpFile) 

305 rtVisitInfo = rtExposure.getInfo().getVisitInfo() 

306 self.assertEqual(rtVisitInfo.getWeather(), weather) 

307 self.assertEqual(rtExposure.getPhotoCalib(), photoCalib) 

308 self.assertEqual(rtExposure.getFilter(), gFilterLabel) 

309 

310 # Test property getters. 

311 self.assertEqual(rtExposure.photoCalib, photoCalib) 

312 self.assertEqual(rtExposure.filter, gFilterLabel) 

313 # NOTE: we can't test visitInfo equality, because most fields are NaN. 

314 self.assertIsNotNone(rtExposure.visitInfo) 

315 

316 def testSetMembers(self): 

317 """ 

318 Test that the MaskedImage and the WCS of an Exposure can be set. 

319 """ 

320 exposure = afwImage.ExposureF() 

321 

322 maskedImage = afwImage.MaskedImageF(inFilePathSmall) 

323 exposure.setMaskedImage(maskedImage) 

324 exposure.setWcs(self.wcs) 

325 exposure.setDetector(self.detector) 

326 exposure.setFilter(afwImage.FilterLabel(band="g")) 

327 

328 self.assertEqual(exposure.getDetector().getName(), 

329 self.detector.getName()) 

330 self.assertEqual(exposure.getDetector().getSerial(), 

331 self.detector.getSerial()) 

332 self.assertEqual(exposure.getFilter().bandLabel, "g") 

333 self.assertEqual(exposure.getWcs(), self.wcs) 

334 

335 # The PhotoCalib tests are in test_photoCalib.py; 

336 # here we just check that it's gettable and settable. 

337 self.assertIsNone(exposure.getPhotoCalib()) 

338 

339 photoCalib = afwImage.PhotoCalib(511.1, 44.4) 

340 exposure.setPhotoCalib(photoCalib) 

341 self.assertEqual(exposure.getPhotoCalib(), photoCalib) 

342 

343 # Psfs next 

344 self.assertFalse(exposure.hasPsf()) 

345 exposure.setPsf(self.psf) 

346 self.assertTrue(exposure.hasPsf()) 

347 

348 exposure.setPsf(DummyPsf(1.0)) # we can reset the Psf 

349 

350 # extras next 

351 info = exposure.getInfo() 

352 for key, value in self.extras.items(): 

353 self.assertFalse(info.hasComponent(key)) 

354 self.assertIsNone(info.getComponent(key)) 

355 info.setComponent(key, value) 

356 self.assertTrue(info.hasComponent(key)) 

357 self.assertEqual(info.getComponent(key), value) 

358 info.removeComponent(key) 

359 self.assertFalse(info.hasComponent(key)) 

360 

361 # Test that we can set the MaskedImage and WCS of an Exposure 

362 # that already has both 

363 self.exposureMiWcs.setMaskedImage(maskedImage) 

364 exposure.setWcs(self.wcs) 

365 

366 def testHasWcs(self): 

367 """ 

368 Test if an Exposure has a WCS or not. 

369 """ 

370 self.assertFalse(self.exposureBlank.hasWcs()) 

371 

372 self.assertFalse(self.exposureMiOnly.hasWcs()) 

373 self.assertTrue(self.exposureMiWcs.hasWcs()) 

374 self.assertTrue(self.exposureCrWcs.hasWcs()) 

375 self.assertFalse(self.exposureCrOnly.hasWcs()) 

376 

377 def testGetSubExposure(self): 

378 """ 

379 Test that a subExposure of the original Exposure can be obtained. 

380 

381 The MaskedImage class should throw a 

382 lsst::pex::exceptions::InvalidParameter if the requested 

383 subRegion is not fully contained within the original 

384 MaskedImage. 

385 

386 """ 

387 # 

388 # This subExposure is valid 

389 # 

390 subBBox = lsst.geom.Box2I(lsst.geom.Point2I(40, 50), 

391 lsst.geom.Extent2I(10, 10)) 

392 subExposure = self.exposureCrWcs.Factory( 

393 self.exposureCrWcs, subBBox, afwImage.LOCAL) 

394 

395 self.checkWcs(self.exposureCrWcs, subExposure) 

396 

397 # this subRegion is not valid and should trigger an exception 

398 # from the MaskedImage class and should trigger an exception 

399 # from the WCS class for the MaskedImage 871034p_1_MI. 

400 

401 subRegion3 = lsst.geom.Box2I(lsst.geom.Point2I(100, 100), 

402 lsst.geom.Extent2I(10, 10)) 

403 

404 def getSubRegion(): 

405 self.exposureCrWcs.Factory( 

406 self.exposureCrWcs, subRegion3, afwImage.LOCAL) 

407 

408 self.assertRaises(pexExcept.LengthError, getSubRegion) 

409 

410 # this subRegion is not valid and should trigger an exception 

411 # from the MaskedImage class only for the MaskedImage small_MI. 

412 # small_MI (cols, rows) = (256, 256) 

413 

414 subRegion4 = lsst.geom.Box2I(lsst.geom.Point2I(250, 250), 

415 lsst.geom.Extent2I(10, 10)) 

416 

417 def getSubRegion(): 

418 self.exposureCrWcs.Factory( 

419 self.exposureCrWcs, subRegion4, afwImage.LOCAL) 

420 

421 self.assertRaises(pexExcept.LengthError, getSubRegion) 

422 

423 # check the sub- and parent- exposures are using the same Wcs 

424 # transformation 

425 subBBox = lsst.geom.Box2I(lsst.geom.Point2I(40, 50), 

426 lsst.geom.Extent2I(10, 10)) 

427 subExposure = self.exposureCrWcs.Factory( 

428 self.exposureCrWcs, subBBox, afwImage.LOCAL) 

429 parentSkyPos = self.exposureCrWcs.getWcs().pixelToSky(0, 0) 

430 

431 subExpSkyPos = subExposure.getWcs().pixelToSky(0, 0) 

432 

433 self.assertSpherePointsAlmostEqual(parentSkyPos, subExpSkyPos, msg="Wcs in sub image has changed") 

434 

435 def testReadWriteFits(self): 

436 """Test readFits and writeFits. 

437 """ 

438 # This should pass without an exception 

439 mainExposure = afwImage.ExposureF(inFilePathSmall) 

440 mainExposure.info.setId(self.id) 

441 mainExposure.setDetector(self.detector) 

442 

443 subBBox = lsst.geom.Box2I(lsst.geom.Point2I(10, 10), 

444 lsst.geom.Extent2I(40, 50)) 

445 subExposure = mainExposure.Factory( 

446 mainExposure, subBBox, afwImage.LOCAL) 

447 self.checkWcs(mainExposure, subExposure) 

448 det = subExposure.getDetector() 

449 self.assertTrue(det) 

450 

451 subExposure = afwImage.ExposureF( 

452 inFilePathSmall, subBBox, afwImage.LOCAL) 

453 

454 self.checkWcs(mainExposure, subExposure) 

455 

456 # This should throw an exception 

457 def getExposure(): 

458 afwImage.ExposureF(inFilePathSmallImage) 

459 

460 self.assertRaises(FitsError, getExposure) 

461 

462 mainExposure.setPsf(self.psf) 

463 

464 # Make sure we can write without an exception 

465 photoCalib = afwImage.PhotoCalib(1e-10, 1e-12) 

466 mainExposure.setPhotoCalib(photoCalib) 

467 

468 mainInfo = mainExposure.getInfo() 

469 for key, value in self.extras.items(): 

470 mainInfo.setComponent(key, value) 

471 

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

473 mainExposure.writeFits(tmpFile) 

474 

475 readExposure = type(mainExposure)(tmpFile) 

476 

477 # 

478 # Check the round-tripping 

479 # 

480 self.assertIsNotNone(mainExposure.getFilter()) 

481 self.assertEqual(mainExposure.getFilter(), 

482 readExposure.getFilter()) 

483 

484 self.assertEqual(photoCalib, readExposure.getPhotoCalib()) 

485 

486 readInfo = readExposure.getInfo() 

487 self.assertEqual(mainExposure.info.getId(), readInfo.id) 

488 for key, value in self.extras.items(): 

489 self.assertEqual(value, readInfo.getComponent(key)) 

490 

491 psf = readExposure.getPsf() 

492 self.assertIsNotNone(psf) 

493 self.assertEqual(psf, self.psf) 

494 # check psf property getter 

495 self.assertEqual(readExposure.psf, self.psf) 

496 

497 def checkWcs(self, parentExposure, subExposure): 

498 """Compare WCS at corner points of a sub-exposure and its parent exposure 

499 By using the function indexToPosition, we should be able to convert the indices 

500 (of the four corners (of the sub-exposure)) to positions and use the wcs 

501 to get the same sky coordinates for each. 

502 """ 

503 subMI = subExposure.getMaskedImage() 

504 subDim = subMI.getDimensions() 

505 

506 # Note: pixel positions must be computed relative to XY0 when working 

507 # with WCS 

508 mainWcs = parentExposure.getWcs() 

509 subWcs = subExposure.getWcs() 

510 

511 for xSubInd in (0, subDim.getX()-1): 

512 for ySubInd in (0, subDim.getY()-1): 

513 self.assertSpherePointsAlmostEqual( 

514 mainWcs.pixelToSky( 

515 afwImage.indexToPosition(xSubInd), 

516 afwImage.indexToPosition(ySubInd), 

517 ), 

518 subWcs.pixelToSky( 

519 afwImage.indexToPosition(xSubInd), 

520 afwImage.indexToPosition(ySubInd), 

521 )) 

522 

523 def cmpExposure(self, e1, e2): 

524 self.assertEqual(e1.getDetector().getName(), 

525 e2.getDetector().getName()) 

526 self.assertEqual(e1.getDetector().getSerial(), 

527 e2.getDetector().getSerial()) 

528 self.assertEqual(e1.getFilter(), e2.getFilter()) 

529 xy = lsst.geom.Point2D(0, 0) 

530 self.assertEqual(e1.getWcs().pixelToSky(xy)[0], 

531 e2.getWcs().pixelToSky(xy)[0]) 

532 self.assertEqual(e1.getPhotoCalib(), e2.getPhotoCalib()) 

533 # check PSF identity 

534 if not e1.getPsf(): 

535 self.assertFalse(e2.getPsf()) 

536 else: 

537 self.assertEqual(e1.getPsf(), e2.getPsf()) 

538 # Check extra components 

539 i1 = e1.getInfo() 

540 i2 = e2.getInfo() 

541 for key in self.extras: 

542 self.assertEqual(i1.hasComponent(key), i2.hasComponent(key)) 

543 if i1.hasComponent(key): 

544 self.assertEqual(i1.getComponent(key), i2.getComponent(key)) 

545 

546 def testCopyExposure(self): 

547 """Copy an Exposure (maybe changing type)""" 

548 

549 exposureU = afwImage.ExposureU(inFilePathSmall, allowUnsafe=True) 

550 exposureU.setWcs(self.wcs) 

551 exposureU.setDetector(self.detector) 

552 exposureU.setFilter(afwImage.FilterLabel(band="g")) 

553 exposureU.setPsf(DummyPsf(4.0)) 

554 infoU = exposureU.getInfo() 

555 for key, value in self.extras.items(): 

556 infoU.setComponent(key, value) 

557 

558 exposureF = exposureU.convertF() 

559 self.cmpExposure(exposureF, exposureU) 

560 

561 nexp = exposureF.Factory(exposureF, False) 

562 self.cmpExposure(exposureF, nexp) 

563 

564 # Ensure that the copy was deep. 

565 # (actually this test is invalid since getDetector() returns a shared_ptr) 

566 # cen0 = exposureU.getDetector().getCenterPixel() 

567 # x0,y0 = cen0 

568 # det = exposureF.getDetector() 

569 # det.setCenterPixel(lsst.geom.Point2D(999.0, 437.8)) 

570 # self.assertEqual(exposureU.getDetector().getCenterPixel()[0], x0) 

571 # self.assertEqual(exposureU.getDetector().getCenterPixel()[1], y0) 

572 

573 def testDeepCopyData(self): 

574 """Make sure a deep copy of an Exposure has its own data (ticket #2625) 

575 """ 

576 exp = afwImage.ExposureF(6, 7) 

577 mi = exp.getMaskedImage() 

578 mi.getImage().set(100) 

579 mi.getMask().set(5) 

580 mi.getVariance().set(200) 

581 

582 expCopy = exp.clone() 

583 miCopy = expCopy.getMaskedImage() 

584 miCopy.getImage().set(-50) 

585 miCopy.getMask().set(2) 

586 miCopy.getVariance().set(175) 

587 

588 self.assertFloatsAlmostEqual(miCopy.getImage().getArray(), -50) 

589 self.assertTrue(np.all(miCopy.getMask().getArray() == 2)) 

590 self.assertFloatsAlmostEqual(miCopy.getVariance().getArray(), 175) 

591 

592 self.assertFloatsAlmostEqual(mi.getImage().getArray(), 100) 

593 self.assertTrue(np.all(mi.getMask().getArray() == 5)) 

594 self.assertFloatsAlmostEqual(mi.getVariance().getArray(), 200) 

595 

596 def testDeepCopySubData(self): 

597 """Make sure a deep copy of a subregion of an Exposure has its own data (ticket #2625) 

598 """ 

599 exp = afwImage.ExposureF(6, 7) 

600 mi = exp.getMaskedImage() 

601 mi.getImage().set(100) 

602 mi.getMask().set(5) 

603 mi.getVariance().set(200) 

604 

605 bbox = lsst.geom.Box2I(lsst.geom.Point2I(1, 0), lsst.geom.Extent2I(5, 4)) 

606 expCopy = exp.Factory(exp, bbox, afwImage.PARENT, True) 

607 miCopy = expCopy.getMaskedImage() 

608 miCopy.getImage().set(-50) 

609 miCopy.getMask().set(2) 

610 miCopy.getVariance().set(175) 

611 

612 self.assertFloatsAlmostEqual(miCopy.getImage().getArray(), -50) 

613 self.assertTrue(np.all(miCopy.getMask().getArray() == 2)) 

614 self.assertFloatsAlmostEqual(miCopy.getVariance().getArray(), 175) 

615 

616 self.assertFloatsAlmostEqual(mi.getImage().getArray(), 100) 

617 self.assertTrue(np.all(mi.getMask().getArray() == 5)) 

618 self.assertFloatsAlmostEqual(mi.getVariance().getArray(), 200) 

619 

620 def testDeepCopyMetadata(self): 

621 """Make sure a deep copy of an Exposure has a deep copy of metadata (ticket #2568) 

622 """ 

623 exp = afwImage.ExposureF(10, 10) 

624 expMeta = exp.getMetadata() 

625 expMeta.set("foo", 5) 

626 expCopy = exp.clone() 

627 expCopyMeta = expCopy.getMetadata() 

628 expCopyMeta.set("foo", 6) 

629 self.assertEqual(expCopyMeta.getScalar("foo"), 6) 

630 # this will fail if the bug is present 

631 self.assertEqual(expMeta.getScalar("foo"), 5) 

632 

633 def testDeepCopySubMetadata(self): 

634 """Make sure a deep copy of a subregion of an Exposure has a deep copy of metadata (ticket #2568) 

635 """ 

636 exp = afwImage.ExposureF(10, 10) 

637 expMeta = exp.getMetadata() 

638 expMeta.set("foo", 5) 

639 bbox = lsst.geom.Box2I(lsst.geom.Point2I(1, 0), lsst.geom.Extent2I(5, 5)) 

640 expCopy = exp.Factory(exp, bbox, afwImage.PARENT, True) 

641 expCopyMeta = expCopy.getMetadata() 

642 expCopyMeta.set("foo", 6) 

643 self.assertEqual(expCopyMeta.getScalar("foo"), 6) 

644 # this will fail if the bug is present 

645 self.assertEqual(expMeta.getScalar("foo"), 5) 

646 

647 def testMakeExposureLeaks(self): 

648 """Test for memory leaks in makeExposure (the test is in lsst.utils.tests.MemoryTestCase)""" 

649 afwImage.makeMaskedImage(afwImage.ImageU(lsst.geom.Extent2I(10, 20))) 

650 afwImage.makeExposure(afwImage.makeMaskedImage( 

651 afwImage.ImageU(lsst.geom.Extent2I(10, 20)))) 

652 

653 def testImageSlices(self): 

654 """Test image slicing, which generate sub-images using Box2I under the covers""" 

655 exp = afwImage.ExposureF(10, 20) 

656 mi = exp.getMaskedImage() 

657 mi.image[9, 19] = 10 

658 # N.b. Exposures don't support setting/getting the pixels so can't 

659 # replicate e.g. Image's slice tests 

660 sexp = exp[1:4, 6:10] 

661 self.assertEqual(sexp.getDimensions(), lsst.geom.ExtentI(3, 4)) 

662 sexp = exp[:, -3:, afwImage.LOCAL] 

663 self.assertEqual(sexp.getDimensions(), 

664 lsst.geom.ExtentI(exp.getWidth(), 3)) 

665 self.assertEqual(sexp.maskedImage[-1, -1, afwImage.LOCAL], 

666 exp.maskedImage[-1, -1, afwImage.LOCAL]) 

667 

668 def testConversionToScalar(self): 

669 """Test that even 1-pixel Exposures can't be converted to scalars""" 

670 im = afwImage.ExposureF(10, 20) 

671 

672 # only single pixel images may be converted 

673 self.assertRaises(TypeError, float, im) 

674 # actually, can't convert (img, msk, var) to scalar 

675 self.assertRaises(TypeError, float, im[0, 0]) 

676 

677 def testReadMetadata(self): 

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

679 self.exposureCrWcs.getMetadata().set("FRAZZLE", True) 

680 # This will write the main metadata (inc. FRAZZLE) to the primary HDU, and the 

681 # WCS to subsequent HDUs, along with INHERIT=T. 

682 self.exposureCrWcs.writeFits(tmpFile) 

683 # This should read the first non-empty HDU (i.e. it skips the primary), but 

684 # goes back and reads it if it finds INHERIT=T. That should let us read 

685 # frazzle and the Wcs from the PropertySet returned by 

686 # testReadMetadata. 

687 md = readMetadata(tmpFile) 

688 wcs = afwGeom.makeSkyWcs(md, False) 

689 self.assertPairsAlmostEqual(wcs.getPixelOrigin(), self.wcs.getPixelOrigin()) 

690 self.assertSpherePointsAlmostEqual(wcs.getSkyOrigin(), self.wcs.getSkyOrigin()) 

691 assert_allclose(wcs.getCdMatrix(), self.wcs.getCdMatrix(), atol=1e-10) 

692 frazzle = md.getScalar("FRAZZLE") 

693 self.assertTrue(frazzle) 

694 

695 def testArchiveKeys(self): 

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

697 exposure1 = afwImage.ExposureF(100, 100, self.wcs) 

698 exposure1.setPsf(self.psf) 

699 exposure1.writeFits(tmpFile) 

700 exposure2 = afwImage.ExposureF(tmpFile) 

701 self.assertFalse(exposure2.getMetadata().exists("AR_ID")) 

702 self.assertFalse(exposure2.getMetadata().exists("PSF_ID")) 

703 self.assertFalse(exposure2.getMetadata().exists("WCS_ID")) 

704 

705 def testTicket2861(self): 

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

707 exposure1 = afwImage.ExposureF(100, 100, self.wcs) 

708 exposure1.setPsf(self.psf) 

709 schema = afwTable.ExposureTable.makeMinimalSchema() 

710 coaddInputs = afwImage.CoaddInputs(schema, schema) 

711 exposure1.getInfo().setCoaddInputs(coaddInputs) 

712 exposure2 = afwImage.ExposureF(exposure1, True) 

713 self.assertIsNotNone(exposure2.getInfo().getCoaddInputs()) 

714 exposure2.writeFits(tmpFile) 

715 exposure3 = afwImage.ExposureF(tmpFile) 

716 self.assertIsNotNone(exposure3.getInfo().getCoaddInputs()) 

717 

718 def testGetCutout(self): 

719 wcs = self.smallExposure.getWcs() 

720 

721 dimensions = [lsst.geom.Extent2I(100, 50), lsst.geom.Extent2I(15, 15), lsst.geom.Extent2I(0, 10), 

722 lsst.geom.Extent2I(25, 30), lsst.geom.Extent2I(15, -5), 

723 2*self.smallExposure.getDimensions()] 

724 locations = [("center", self._getExposureCenter(self.smallExposure)), 

725 ("edge", wcs.pixelToSky(lsst.geom.Point2D(0, 0))), 

726 ("rounding test", wcs.pixelToSky(lsst.geom.Point2D(0.2, 0.7))), 

727 ("just inside", wcs.pixelToSky(lsst.geom.Point2D(-0.5 + 1e-4, -0.5 + 1e-4))), 

728 ("just outside", wcs.pixelToSky(lsst.geom.Point2D(-0.5 - 1e-4, -0.5 - 1e-4))), 

729 ("outside", wcs.pixelToSky(lsst.geom.Point2D(-1000, -1000)))] 

730 for cutoutSize in dimensions: 

731 for label, cutoutCenter in locations: 

732 msg = 'Cutout size = %s, location = %s' % (cutoutSize, label) 

733 if "outside" not in label and all(cutoutSize.gt(0)): 

734 cutout = self.smallExposure.getCutout(cutoutCenter, cutoutSize) 

735 centerInPixels = wcs.skyToPixel(cutoutCenter) 

736 precision = (1 + 1e-4)*np.sqrt(0.5)*wcs.getPixelScale(centerInPixels) 

737 self._checkCutoutProperties(cutout, cutoutSize, cutoutCenter, precision, msg) 

738 self._checkCutoutPixels( 

739 cutout, 

740 self._getValidCorners(self.smallExposure.getBBox(), cutout.getBBox()), 

741 msg) 

742 

743 # Need a valid WCS 

744 with self.assertRaises(pexExcept.LogicError, msg=msg): 

745 self.exposureMiOnly.getCutout(cutoutCenter, cutoutSize) 

746 else: 

747 with self.assertRaises(pexExcept.InvalidParameterError, msg=msg): 

748 self.smallExposure.getCutout(cutoutCenter, cutoutSize) 

749 

750 def testGetConvexPolygon(self): 

751 """Test the convex polygon.""" 

752 # Check that we do not have a convex polygon for the plain exposure. 

753 self.assertIsNone(self.exposureMiOnly.convex_polygon) 

754 

755 # Check that all the points in the padded bounding box are in the polygon 

756 bbox = self.exposureMiWcs.getBBox() 

757 # Grow by the default padding. 

758 bbox.grow(10) 

759 x, y = np.meshgrid(np.arange(bbox.getBeginX(), bbox.getEndX(), dtype=np.float64), 

760 np.arange(bbox.getBeginY(), bbox.getEndY(), dtype=np.float64)) 

761 wcs = self.exposureMiWcs.wcs 

762 ra, dec = wcs.pixelToSkyArray(x.ravel(), 

763 y.ravel()) 

764 

765 poly = self.exposureMiWcs.convex_polygon 

766 contains = poly.contains(ra, dec) 

767 np.testing.assert_array_equal(contains, np.ones(len(contains), dtype=bool)) 

768 

769 # Check that points one pixel outside of the bounding box are not in the polygon 

770 bbox.grow(1) 

771 

772 ra, dec = wcs.pixelToSkyArray( 

773 np.linspace(bbox.getBeginX(), bbox.getEndX(), 100), 

774 np.full(100, bbox.getBeginY())) 

775 contains = poly.contains(ra, dec) 

776 np.testing.assert_array_equal(contains, np.zeros(len(contains), dtype=bool)) 

777 

778 ra, dec = wcs.pixelToSkyArray( 

779 np.linspace(bbox.getBeginX(), bbox.getEndX(), 100), 

780 np.full(100, bbox.getEndY())) 

781 contains = poly.contains(ra, dec) 

782 np.testing.assert_array_equal(contains, np.zeros(len(contains), dtype=bool)) 

783 

784 ra, dec = wcs.pixelToSkyArray( 

785 np.full(100, bbox.getBeginX()), 

786 np.linspace(bbox.getBeginY(), bbox.getEndY(), 100)) 

787 contains = poly.contains(ra, dec) 

788 np.testing.assert_array_equal(contains, np.zeros(len(contains), dtype=bool)) 

789 

790 ra, dec = wcs.pixelToSkyArray( 

791 np.full(100, bbox.getEndX()), 

792 np.linspace(bbox.getBeginY(), bbox.getEndY(), 100)) 

793 contains = poly.contains(ra, dec) 

794 np.testing.assert_array_equal(contains, np.zeros(len(contains), dtype=bool)) 

795 

796 def testContainsSkyCoords(self): 

797 """Test the sky coord containment code.""" 

798 self.assertRaisesRegex(ValueError, 

799 "Exposure does not have a valid WCS", 

800 self.exposureMiOnly.containsSkyCoords, 

801 0.0, 

802 0.0) 

803 

804 # Check that all the points within the bounding box are contained 

805 bbox = self.exposureMiWcs.getBBox() 

806 x, y = np.meshgrid(np.arange(bbox.getBeginX() + 1, bbox.getEndX() - 1), 

807 np.arange(bbox.getBeginY() + 1, bbox.getEndY() - 1)) 

808 wcs = self.exposureMiWcs.wcs 

809 ra, dec = wcs.pixelToSkyArray(x.ravel().astype(np.float64), 

810 y.ravel().astype(np.float64)) 

811 

812 contains = self.exposureMiWcs.containsSkyCoords(ra*units.radian, 

813 dec*units.radian) 

814 np.testing.assert_array_equal(contains, np.ones(len(contains), dtype=bool)) 

815 

816 # Same test, everything in degrees. 

817 ra, dec = wcs.pixelToSkyArray(x.ravel().astype(np.float64), 

818 y.ravel().astype(np.float64), 

819 degrees=True) 

820 

821 contains = self.exposureMiWcs.containsSkyCoords(ra*units.degree, 

822 dec*units.degree) 

823 np.testing.assert_array_equal(contains, np.ones(len(contains), dtype=bool)) 

824 

825 # Prepend and append some positions out of the box. 

826 ra = np.concatenate(([300.0], ra, [180.])) 

827 dec = np.concatenate(([50.0], dec, [50.0])) 

828 

829 contains = self.exposureMiWcs.containsSkyCoords(ra*units.degree, 

830 dec*units.degree) 

831 compare = np.ones(len(contains), dtype=bool) 

832 compare[0] = False 

833 compare[-1] = False 

834 np.testing.assert_array_equal(contains, compare) 

835 

836 def _checkCutoutProperties(self, cutout, size, center, precision, msg): 

837 """Test whether a cutout has the desired size and position. 

838 

839 Parameters 

840 ---------- 

841 cutout : `lsst.afw.image.Exposure` 

842 The cutout to test. 

843 size : `lsst.geom.Extent2I` 

844 The expected dimensions of ``cutout``. 

845 center : `lsst.geom.SpherePoint` 

846 The expected center of ``cutout``. 

847 precision : `lsst.geom.Angle` 

848 The precision to which ``center`` must match. 

849 msg : `str` 

850 An error message suffix describing test parameters. 

851 """ 

852 newCenter = self._getExposureCenter(cutout) 

853 self.assertIsNotNone(cutout, msg=msg) 

854 self.assertSpherePointsAlmostEqual(newCenter, center, maxSep=precision, msg=msg) 

855 self.assertEqual(cutout.getWidth(), size[0], msg=msg) 

856 self.assertEqual(cutout.getHeight(), size[1], msg=msg) 

857 

858 def _checkCutoutPixels(self, cutout, validCorners, msg): 

859 """Test whether a cutout has valid/empty pixels where expected. 

860 

861 Parameters 

862 ---------- 

863 cutout : `lsst.afw.image.Exposure` 

864 The cutout to test. 

865 validCorners : iterable of `lsst.geom.Point2I` 

866 The corners of ``cutout`` that should be drawn from the original image. 

867 msg : `str` 

868 An error message suffix describing test parameters. 

869 """ 

870 mask = cutout.getMaskedImage().getMask() 

871 edgeMask = mask.getPlaneBitMask("NO_DATA") 

872 

873 for corner in cutout.getBBox().getCorners(): 

874 maskBitsSet = mask[corner] & edgeMask 

875 if corner in validCorners: 

876 self.assertEqual(maskBitsSet, 0, msg=msg) 

877 else: 

878 self.assertEqual(maskBitsSet, edgeMask, msg=msg) 

879 

880 def _getExposureCenter(self, exposure): 

881 """Return the sky coordinates of an Exposure's center. 

882 

883 Parameters 

884 ---------- 

885 exposure : `lsst.afw.image.Exposure` 

886 The image whose center is desired. 

887 

888 Returns 

889 ------- 

890 center : `lsst.geom.SpherePoint` 

891 The position at the center of ``exposure``. 

892 """ 

893 return exposure.getWcs().pixelToSky(lsst.geom.Box2D(exposure.getBBox()).getCenter()) 

894 

895 def _getValidCorners(self, imageBox, cutoutBox): 

896 """Return the corners of a cutout that are constrained by the original image. 

897 

898 Parameters 

899 ---------- 

900 imageBox: `lsst.geom.Extent2I` 

901 The bounding box of the original image. 

902 cutoutBox : `lsst.geom.Box2I` 

903 The bounding box of the cutout. 

904 

905 Returns 

906 ------- 

907 corners : iterable of `lsst.geom.Point2I` 

908 The corners that are drawn from the original image. 

909 """ 

910 return [corner for corner in cutoutBox.getCorners() if corner in imageBox] 

911 

912 

913class ExposureInfoTestCase(lsst.utils.tests.TestCase): 

914 def setUp(self): 

915 super().setUp() 

916 

917 self.wcs = afwGeom.makeSkyWcs(lsst.geom.Point2D(0.0, 0.0), 

918 lsst.geom.SpherePoint(2.0, 34.0, lsst.geom.degrees), 

919 np.identity(2), 

920 ) 

921 self.photoCalib = afwImage.PhotoCalib(1.5) 

922 self.psf = DummyPsf(2.0) 

923 self.detector = DetectorWrapper().detector 

924 self.summaryStats = afwImage.ExposureSummaryStats(ra=100.0) 

925 self.polygon = afwGeom.Polygon(lsst.geom.Box2D(lsst.geom.Point2D(0.0, 0.0), 

926 lsst.geom.Point2D(25.0, 20.0))) 

927 self.coaddInputs = afwImage.CoaddInputs() 

928 self.apCorrMap = afwImage.ApCorrMap() 

929 self.transmissionCurve = afwImage.TransmissionCurve.makeIdentity() 

930 

931 self.exposureInfo = afwImage.ExposureInfo() 

932 self.gFilterLabel = afwImage.FilterLabel(band="g") 

933 self.exposureId = 42 

934 

935 def _checkAlias(self, exposureInfo, key, value, has, get): 

936 self.assertFalse(has()) 

937 self.assertFalse(exposureInfo.hasComponent(key)) 

938 self.assertIsNone(get()) 

939 self.assertIsNone(exposureInfo.getComponent(key)) 

940 

941 self.exposureInfo.setComponent(key, value) 

942 self.assertTrue(has()) 

943 self.assertTrue(exposureInfo.hasComponent(key)) 

944 self.assertIsNotNone(get()) 

945 self.assertIsNotNone(exposureInfo.getComponent(key)) 

946 self.assertEqual(get(), value) 

947 self.assertEqual(exposureInfo.getComponent(key), value) 

948 

949 self.exposureInfo.removeComponent(key) 

950 self.assertFalse(has()) 

951 self.assertFalse(exposureInfo.hasComponent(key)) 

952 self.assertIsNone(get()) 

953 self.assertIsNone(exposureInfo.getComponent(key)) 

954 

955 def testAliases(self): 

956 cls = type(self.exposureInfo) 

957 self._checkAlias(self.exposureInfo, cls.KEY_WCS, self.wcs, 

958 self.exposureInfo.hasWcs, self.exposureInfo.getWcs) 

959 self._checkAlias(self.exposureInfo, cls.KEY_PSF, self.psf, 

960 self.exposureInfo.hasPsf, self.exposureInfo.getPsf) 

961 self._checkAlias(self.exposureInfo, cls.KEY_PHOTO_CALIB, self.photoCalib, 

962 self.exposureInfo.hasPhotoCalib, self.exposureInfo.getPhotoCalib) 

963 self._checkAlias(self.exposureInfo, cls.KEY_DETECTOR, self.detector, 

964 self.exposureInfo.hasDetector, self.exposureInfo.getDetector) 

965 self._checkAlias(self.exposureInfo, cls.KEY_VALID_POLYGON, self.polygon, 

966 self.exposureInfo.hasValidPolygon, self.exposureInfo.getValidPolygon) 

967 self._checkAlias(self.exposureInfo, cls.KEY_COADD_INPUTS, self.coaddInputs, 

968 self.exposureInfo.hasCoaddInputs, self.exposureInfo.getCoaddInputs) 

969 self._checkAlias(self.exposureInfo, cls.KEY_AP_CORR_MAP, self.apCorrMap, 

970 self.exposureInfo.hasApCorrMap, self.exposureInfo.getApCorrMap) 

971 self._checkAlias(self.exposureInfo, cls.KEY_TRANSMISSION_CURVE, self.transmissionCurve, 

972 self.exposureInfo.hasTransmissionCurve, self.exposureInfo.getTransmissionCurve) 

973 self._checkAlias(self.exposureInfo, cls.KEY_SUMMARY_STATS, self.summaryStats, 

974 self.exposureInfo.hasSummaryStats, self.exposureInfo.getSummaryStats) 

975 self._checkAlias(self.exposureInfo, cls.KEY_FILTER, self.gFilterLabel, 

976 self.exposureInfo.hasFilter, self.exposureInfo.getFilter) 

977 

978 def testId(self): 

979 self.exposureInfo.setVisitInfo(afwImage.VisitInfo()) 

980 

981 self.assertFalse(self.exposureInfo.hasId()) 

982 self.assertIsNone(self.exposureInfo.getId()) 

983 with self.assertWarns(FutureWarning): 

984 self.assertEqual(self.exposureInfo.getVisitInfo().getExposureId(), 0) 

985 self.assertIsNone(self.exposureInfo.id) 

986 

987 self.exposureInfo.setId(self.exposureId) 

988 self.assertTrue(self.exposureInfo.hasId()) 

989 self.assertIsNotNone(self.exposureInfo.getId()) 

990 self.assertIsNotNone(self.exposureInfo.id) 

991 self.assertEqual(self.exposureInfo.getId(), self.exposureId) 

992 with self.assertWarns(FutureWarning): 

993 self.assertEqual(self.exposureInfo.getVisitInfo().getExposureId(), self.exposureId) 

994 self.assertEqual(self.exposureInfo.id, self.exposureId) 

995 

996 self.exposureInfo.id = 99899 

997 self.assertEqual(self.exposureInfo.getId(), 99899) 

998 

999 self.exposureInfo.setVisitInfo(afwImage.VisitInfo(exposureId=12321)) 

1000 self.assertEqual(self.exposureInfo.id, 12321) 

1001 

1002 self.exposureInfo.id = None 

1003 self.assertFalse(self.exposureInfo.hasId()) 

1004 self.assertIsNone(self.exposureInfo.getId()) 

1005 self.assertIsNone(self.exposureInfo.id) 

1006 

1007 def testCopy(self): 

1008 # Test that ExposureInfos have independently settable state 

1009 copy = afwImage.ExposureInfo(self.exposureInfo, True) 

1010 self.assertEqual(self.exposureInfo.getWcs(), copy.getWcs()) 

1011 

1012 newWcs = afwGeom.makeSkyWcs(lsst.geom.Point2D(-23.0, 8.0), 

1013 lsst.geom.SpherePoint(0.0, 0.0, lsst.geom.degrees), 

1014 np.identity(2), 

1015 ) 

1016 copy.setWcs(newWcs) 

1017 self.assertEqual(copy.getWcs(), newWcs) 

1018 self.assertNotEqual(self.exposureInfo.getWcs(), copy.getWcs()) 

1019 

1020 def testMissingProperties(self): 

1021 # Test that invalid properties return None instead of raising 

1022 exposureInfo = afwImage.ExposureInfo() 

1023 

1024 self.assertIsNone(exposureInfo.id) 

1025 

1026 

1027class ExposureNoAfwdataTestCase(lsst.utils.tests.TestCase): 

1028 """Tests of Exposure that don't require afwdata. 

1029 

1030 These tests use the trivial exposures written to ``afw/tests/data``. 

1031 """ 

1032 def setUp(self): 

1033 self.dataDir = os.path.join(os.path.split(__file__)[0], "data") 

1034 

1035 # Check the values below against what was written by comparing with 

1036 # the code in `afw/tests/data/makeTestExposure.py` 

1037 nx = ny = 10 

1038 image = afwImage.ImageF(np.arange(nx*ny, dtype='f').reshape(nx, ny)) 

1039 variance = afwImage.ImageF(np.ones((nx, ny), dtype='f')) 

1040 mask = afwImage.MaskX(nx, ny) 

1041 mask.array[5, 5] = 5 

1042 self.maskedImage = afwImage.MaskedImageF(image, mask, variance) 

1043 self.exposureId = 12345 

1044 

1045 self.v0PhotoCalib = afwImage.makePhotoCalibFromCalibZeroPoint(1e6, 2e4) 

1046 self.v1PhotoCalib = afwImage.PhotoCalib(1e6, 2e4) 

1047 self.v1FilterLabel = afwImage.FilterLabel(physical="ha") 

1048 self.v2FilterLabel = afwImage.FilterLabel(band="N656", physical="ha") 

1049 

1050 def testReadUnversioned(self): 

1051 """Test that we can read an unversioned (implicit verison 0) file. 

1052 """ 

1053 filename = os.path.join(self.dataDir, "exposure-noversion.fits") 

1054 exposure = afwImage.ExposureF.readFits(filename) 

1055 

1056 self.assertMaskedImagesEqual(exposure.maskedImage, self.maskedImage) 

1057 

1058 self.assertEqual(exposure.info.id, self.exposureId) 

1059 with self.assertWarns(FutureWarning): 

1060 self.assertEqual(exposure.info.getVisitInfo().getExposureId(), self.exposureId) 

1061 self.assertEqual(exposure.getPhotoCalib(), self.v0PhotoCalib) 

1062 self.assertEqual(exposure.getFilter(), self.v1FilterLabel) 

1063 

1064 def testReadVersion0(self): 

1065 """Test that we can read a version 0 file. 

1066 This file should be identical to the unversioned one, except that it 

1067 is marked as ExposureInfo version 0 in the header. 

1068 """ 

1069 filename = os.path.join(self.dataDir, "exposure-version-0.fits") 

1070 exposure = afwImage.ExposureF.readFits(filename) 

1071 

1072 self.assertMaskedImagesEqual(exposure.maskedImage, self.maskedImage) 

1073 

1074 self.assertEqual(exposure.info.id, self.exposureId) 

1075 with self.assertWarns(FutureWarning): 

1076 self.assertEqual(exposure.info.getVisitInfo().getExposureId(), self.exposureId) 

1077 self.assertEqual(exposure.getPhotoCalib(), self.v0PhotoCalib) 

1078 self.assertEqual(exposure.getFilter(), self.v1FilterLabel) 

1079 

1080 # Check that the metadata reader parses the file correctly 

1081 reader = afwImage.ExposureFitsReader(filename) 

1082 self.assertEqual(reader.readExposureInfo().getPhotoCalib(), self.v0PhotoCalib) 

1083 self.assertEqual(reader.readPhotoCalib(), self.v0PhotoCalib) 

1084 

1085 def testReadVersion1(self): 

1086 """Test that we can read a version 1 file. 

1087 Version 1 replaced Calib with PhotoCalib. 

1088 """ 

1089 filename = os.path.join(self.dataDir, "exposure-version-1.fits") 

1090 exposure = afwImage.ExposureF.readFits(filename) 

1091 

1092 self.assertMaskedImagesEqual(exposure.maskedImage, self.maskedImage) 

1093 

1094 self.assertEqual(exposure.info.id, self.exposureId) 

1095 with self.assertWarns(FutureWarning): 

1096 self.assertEqual(exposure.info.getVisitInfo().getExposureId(), self.exposureId) 

1097 self.assertEqual(exposure.getPhotoCalib(), self.v1PhotoCalib) 

1098 self.assertEqual(exposure.getFilter(), self.v1FilterLabel) 

1099 

1100 # Check that the metadata reader parses the file correctly 

1101 reader = afwImage.ExposureFitsReader(filename) 

1102 self.assertEqual(reader.readExposureInfo().getPhotoCalib(), self.v1PhotoCalib) 

1103 self.assertEqual(reader.readPhotoCalib(), self.v1PhotoCalib) 

1104 

1105 def testReadVersion2(self): 

1106 """Test that we can read a version 2 file. 

1107 Version 2 replaced Filter with FilterLabel. 

1108 """ 

1109 filename = os.path.join(self.dataDir, "exposure-version-2.fits") 

1110 exposure = afwImage.ExposureF.readFits(filename) 

1111 

1112 self.assertMaskedImagesEqual(exposure.maskedImage, self.maskedImage) 

1113 

1114 self.assertEqual(exposure.info.id, self.exposureId) 

1115 with self.assertWarns(FutureWarning): 

1116 self.assertEqual(exposure.info.getVisitInfo().getExposureId(), self.exposureId) 

1117 self.assertEqual(exposure.getPhotoCalib(), self.v1PhotoCalib) 

1118 self.assertEqual(exposure.getFilter(), self.v2FilterLabel) 

1119 

1120 # Check that the metadata reader parses the file correctly 

1121 reader = afwImage.ExposureFitsReader(filename) 

1122 self.assertEqual(reader.readExposureInfo().getPhotoCalib(), self.v1PhotoCalib) 

1123 self.assertEqual(reader.readPhotoCalib(), self.v1PhotoCalib) 

1124 

1125 def testExposureSummaryExtraComponents(self): 

1126 """Test that we can read an exposure summary with extra components. 

1127 """ 

1128 testDict = {'ra': 0.0, 

1129 'decl': 0.0, 

1130 'nonsense': 1.0} 

1131 bytes = yaml.dump(testDict, encoding='utf-8') 

1132 with self.assertWarns(FutureWarning): 

1133 summaryStats = lsst.afw.image.ExposureSummaryStats._read(bytes) 

1134 

1135 self.assertEqual(summaryStats.ra, testDict['ra']) 

1136 self.assertEqual(summaryStats.decl, testDict['decl']) 

1137 

1138 

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

1140 pass 

1141 

1142 

1143def setup_module(module): 

1144 lsst.utils.tests.init() 

1145 

1146 

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

1148 lsst.utils.tests.init() 

1149 unittest.main()