Coverage for tests/test_exposure.py: 11%

677 statements  

« prev     ^ index     » next       coverage.py v6.5.0, created at 2022-11-06 12:52 -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 with self.assertWarns(FutureWarning): 

219 self.assertEqual(exposure.getFilterLabel(), gFilterLabel) 

220 

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

222 # check the ExposureInfo property 

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

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

225 self.wcs.getPixelOrigin()) 

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

227 self.detector.getName()) 

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

229 self.detector.getSerial()) 

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

231 with self.assertWarns(FutureWarning): 

232 self.assertEqual(exposure.getInfo().getFilterLabel(), gFilterLabel) 

233 

234 def testNullWcs(self): 

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

236 

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

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

239 """ 

240 maskedImage = self.exposureMiOnly.getMaskedImage() 

241 exposure = afwImage.ExposureF(maskedImage, None) 

242 self.assertFalse(exposure.hasWcs()) 

243 self.assertFalse(exposure.hasPsf()) 

244 

245 def testExposureInfoSetNone(self): 

246 exposureInfo = afwImage.ExposureInfo() 

247 exposureInfo.setDetector(None) 

248 exposureInfo.setValidPolygon(None) 

249 exposureInfo.setPsf(None) 

250 exposureInfo.setWcs(None) 

251 exposureInfo.setPhotoCalib(None) 

252 exposureInfo.setCoaddInputs(None) 

253 exposureInfo.setVisitInfo(None) 

254 exposureInfo.setApCorrMap(None) 

255 for key in self.extras: 

256 exposureInfo.setComponent(key, None) 

257 

258 def testSetExposureInfo(self): 

259 exposureInfo = afwImage.ExposureInfo() 

260 exposureInfo.setWcs(self.wcs) 

261 exposureInfo.setDetector(self.detector) 

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

263 exposureInfo.setFilter(gFilterLabel) 

264 exposureInfo.setId(self.id) 

265 maskedImage = afwImage.MaskedImageF(inFilePathSmall) 

266 exposure = afwImage.ExposureF(maskedImage) 

267 self.assertFalse(exposure.hasWcs()) 

268 

269 exposure.setInfo(exposureInfo) 

270 

271 self.assertTrue(exposure.hasWcs()) 

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

273 self.wcs.getPixelOrigin()) 

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

275 self.detector.getName()) 

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

277 self.detector.getSerial()) 

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

279 with self.assertWarns(FutureWarning): 

280 self.assertEqual(exposure.getFilterLabel(), gFilterLabel) 

281 

282 # test properties 

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

284 self.assertEqual(exposure.filter, gFilterLabel) 

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

286 

287 def testVisitInfoFitsPersistence(self): 

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

289 exposureId = 5 

290 exposureTime = 12.3 

291 boresightRotAngle = 45.6 * lsst.geom.degrees 

292 weather = Weather(1.1, 2.2, 0.3) 

293 visitInfo = afwImage.VisitInfo( 

294 exposureId=exposureId, 

295 exposureTime=exposureTime, 

296 boresightRotAngle=boresightRotAngle, 

297 weather=weather, 

298 ) 

299 photoCalib = afwImage.PhotoCalib(3.4, 5.6) 

300 exposureInfo = afwImage.ExposureInfo() 

301 exposureInfo.setVisitInfo(visitInfo) 

302 exposureInfo.setPhotoCalib(photoCalib) 

303 exposureInfo.setDetector(self.detector) 

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

305 exposureInfo.setFilter(gFilterLabel) 

306 maskedImage = afwImage.MaskedImageF(inFilePathSmall) 

307 exposure = afwImage.ExposureF(maskedImage, exposureInfo) 

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

309 exposure.writeFits(tmpFile) 

310 rtExposure = afwImage.ExposureF(tmpFile) 

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

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

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

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

315 with self.assertWarns(FutureWarning): 

316 self.assertEqual(rtExposure.getFilterLabel(), gFilterLabel) 

317 

318 # Test property getters. 

319 self.assertEqual(rtExposure.photoCalib, photoCalib) 

320 self.assertEqual(rtExposure.filter, gFilterLabel) 

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

322 self.assertIsNotNone(rtExposure.visitInfo) 

323 

324 def testSetMembers(self): 

325 """ 

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

327 """ 

328 exposure = afwImage.ExposureF() 

329 

330 maskedImage = afwImage.MaskedImageF(inFilePathSmall) 

331 exposure.setMaskedImage(maskedImage) 

332 exposure.setWcs(self.wcs) 

333 exposure.setDetector(self.detector) 

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

335 

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

337 self.detector.getName()) 

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

339 self.detector.getSerial()) 

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

341 with self.assertWarns(FutureWarning): 

342 self.assertEqual(exposure.getFilterLabel().bandLabel, "g") 

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

344 

345 # The PhotoCalib tests are in test_photoCalib.py; 

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

347 self.assertIsNone(exposure.getPhotoCalib()) 

348 

349 photoCalib = afwImage.PhotoCalib(511.1, 44.4) 

350 exposure.setPhotoCalib(photoCalib) 

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

352 

353 # Psfs next 

354 self.assertFalse(exposure.hasPsf()) 

355 exposure.setPsf(self.psf) 

356 self.assertTrue(exposure.hasPsf()) 

357 

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

359 

360 # extras next 

361 info = exposure.getInfo() 

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

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

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

365 info.setComponent(key, value) 

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

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

368 info.removeComponent(key) 

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

370 

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

372 # that already has both 

373 self.exposureMiWcs.setMaskedImage(maskedImage) 

374 exposure.setWcs(self.wcs) 

375 

376 def testHasWcs(self): 

377 """ 

378 Test if an Exposure has a WCS or not. 

379 """ 

380 self.assertFalse(self.exposureBlank.hasWcs()) 

381 

382 self.assertFalse(self.exposureMiOnly.hasWcs()) 

383 self.assertTrue(self.exposureMiWcs.hasWcs()) 

384 self.assertTrue(self.exposureCrWcs.hasWcs()) 

385 self.assertFalse(self.exposureCrOnly.hasWcs()) 

386 

387 def testGetSubExposure(self): 

388 """ 

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

390 

391 The MaskedImage class should throw a 

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

393 subRegion is not fully contained within the original 

394 MaskedImage. 

395 

396 """ 

397 # 

398 # This subExposure is valid 

399 # 

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

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

402 subExposure = self.exposureCrWcs.Factory( 

403 self.exposureCrWcs, subBBox, afwImage.LOCAL) 

404 

405 self.checkWcs(self.exposureCrWcs, subExposure) 

406 

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

408 # from the MaskedImage class and should trigger an exception 

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

410 

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

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

413 

414 def getSubRegion(): 

415 self.exposureCrWcs.Factory( 

416 self.exposureCrWcs, subRegion3, afwImage.LOCAL) 

417 

418 self.assertRaises(pexExcept.LengthError, getSubRegion) 

419 

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

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

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

423 

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

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

426 

427 def getSubRegion(): 

428 self.exposureCrWcs.Factory( 

429 self.exposureCrWcs, subRegion4, afwImage.LOCAL) 

430 

431 self.assertRaises(pexExcept.LengthError, getSubRegion) 

432 

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

434 # transformation 

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

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

437 subExposure = self.exposureCrWcs.Factory( 

438 self.exposureCrWcs, subBBox, afwImage.LOCAL) 

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

440 

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

442 

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

444 

445 def testReadWriteFits(self): 

446 """Test readFits and writeFits. 

447 """ 

448 # This should pass without an exception 

449 mainExposure = afwImage.ExposureF(inFilePathSmall) 

450 mainExposure.info.setId(self.id) 

451 mainExposure.setDetector(self.detector) 

452 

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

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

455 subExposure = mainExposure.Factory( 

456 mainExposure, subBBox, afwImage.LOCAL) 

457 self.checkWcs(mainExposure, subExposure) 

458 det = subExposure.getDetector() 

459 self.assertTrue(det) 

460 

461 subExposure = afwImage.ExposureF( 

462 inFilePathSmall, subBBox, afwImage.LOCAL) 

463 

464 self.checkWcs(mainExposure, subExposure) 

465 

466 # This should throw an exception 

467 def getExposure(): 

468 afwImage.ExposureF(inFilePathSmallImage) 

469 

470 self.assertRaises(FitsError, getExposure) 

471 

472 mainExposure.setPsf(self.psf) 

473 

474 # Make sure we can write without an exception 

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

476 mainExposure.setPhotoCalib(photoCalib) 

477 

478 mainInfo = mainExposure.getInfo() 

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

480 mainInfo.setComponent(key, value) 

481 

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

483 mainExposure.writeFits(tmpFile) 

484 

485 readExposure = type(mainExposure)(tmpFile) 

486 

487 # 

488 # Check the round-tripping 

489 # 

490 self.assertIsNotNone(mainExposure.getFilter()) 

491 self.assertEqual(mainExposure.getFilter(), 

492 readExposure.getFilter()) 

493 with self.assertWarns(FutureWarning): 

494 self.assertIsNotNone(mainExposure.getFilterLabel()) 

495 self.assertEqual(mainExposure.getFilterLabel(), 

496 readExposure.getFilterLabel()) 

497 

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

499 

500 readInfo = readExposure.getInfo() 

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

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

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

504 

505 psf = readExposure.getPsf() 

506 self.assertIsNotNone(psf) 

507 self.assertEqual(psf, self.psf) 

508 # check psf property getter 

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

510 

511 def checkWcs(self, parentExposure, subExposure): 

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

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

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

515 to get the same sky coordinates for each. 

516 """ 

517 subMI = subExposure.getMaskedImage() 

518 subDim = subMI.getDimensions() 

519 

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

521 # with WCS 

522 mainWcs = parentExposure.getWcs() 

523 subWcs = subExposure.getWcs() 

524 

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

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

527 self.assertSpherePointsAlmostEqual( 

528 mainWcs.pixelToSky( 

529 afwImage.indexToPosition(xSubInd), 

530 afwImage.indexToPosition(ySubInd), 

531 ), 

532 subWcs.pixelToSky( 

533 afwImage.indexToPosition(xSubInd), 

534 afwImage.indexToPosition(ySubInd), 

535 )) 

536 

537 def cmpExposure(self, e1, e2): 

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

539 e2.getDetector().getName()) 

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

541 e2.getDetector().getSerial()) 

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

543 with self.assertWarns(FutureWarning): 

544 self.assertEqual(e1.getFilterLabel(), e2.getFilterLabel()) 

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

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

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

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

549 # check PSF identity 

550 if not e1.getPsf(): 

551 self.assertFalse(e2.getPsf()) 

552 else: 

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

554 # Check extra components 

555 i1 = e1.getInfo() 

556 i2 = e2.getInfo() 

557 for key in self.extras: 

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

559 if i1.hasComponent(key): 

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

561 

562 def testCopyExposure(self): 

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

564 

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

566 exposureU.setWcs(self.wcs) 

567 exposureU.setDetector(self.detector) 

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

569 exposureU.setPsf(DummyPsf(4.0)) 

570 infoU = exposureU.getInfo() 

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

572 infoU.setComponent(key, value) 

573 

574 exposureF = exposureU.convertF() 

575 self.cmpExposure(exposureF, exposureU) 

576 

577 nexp = exposureF.Factory(exposureF, False) 

578 self.cmpExposure(exposureF, nexp) 

579 

580 # Ensure that the copy was deep. 

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

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

583 # x0,y0 = cen0 

584 # det = exposureF.getDetector() 

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

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

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

588 

589 def testDeepCopyData(self): 

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

591 """ 

592 exp = afwImage.ExposureF(6, 7) 

593 mi = exp.getMaskedImage() 

594 mi.getImage().set(100) 

595 mi.getMask().set(5) 

596 mi.getVariance().set(200) 

597 

598 expCopy = exp.clone() 

599 miCopy = expCopy.getMaskedImage() 

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

601 miCopy.getMask().set(2) 

602 miCopy.getVariance().set(175) 

603 

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

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

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

607 

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

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

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

611 

612 def testDeepCopySubData(self): 

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

614 """ 

615 exp = afwImage.ExposureF(6, 7) 

616 mi = exp.getMaskedImage() 

617 mi.getImage().set(100) 

618 mi.getMask().set(5) 

619 mi.getVariance().set(200) 

620 

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

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

623 miCopy = expCopy.getMaskedImage() 

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

625 miCopy.getMask().set(2) 

626 miCopy.getVariance().set(175) 

627 

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

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

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

631 

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

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

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

635 

636 def testDeepCopyMetadata(self): 

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

638 """ 

639 exp = afwImage.ExposureF(10, 10) 

640 expMeta = exp.getMetadata() 

641 expMeta.set("foo", 5) 

642 expCopy = exp.clone() 

643 expCopyMeta = expCopy.getMetadata() 

644 expCopyMeta.set("foo", 6) 

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

646 # this will fail if the bug is present 

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

648 

649 def testDeepCopySubMetadata(self): 

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

651 """ 

652 exp = afwImage.ExposureF(10, 10) 

653 expMeta = exp.getMetadata() 

654 expMeta.set("foo", 5) 

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

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

657 expCopyMeta = expCopy.getMetadata() 

658 expCopyMeta.set("foo", 6) 

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

660 # this will fail if the bug is present 

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

662 

663 def testMakeExposureLeaks(self): 

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

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

666 afwImage.makeExposure(afwImage.makeMaskedImage( 

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

668 

669 def testImageSlices(self): 

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

671 exp = afwImage.ExposureF(10, 20) 

672 mi = exp.getMaskedImage() 

673 mi.image[9, 19] = 10 

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

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

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

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

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

679 self.assertEqual(sexp.getDimensions(), 

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

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

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

683 

684 def testConversionToScalar(self): 

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

686 im = afwImage.ExposureF(10, 20) 

687 

688 # only single pixel images may be converted 

689 self.assertRaises(TypeError, float, im) 

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

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

692 

693 def testReadMetadata(self): 

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

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

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

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

698 self.exposureCrWcs.writeFits(tmpFile) 

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

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

701 # frazzle and the Wcs from the PropertySet returned by 

702 # testReadMetadata. 

703 md = readMetadata(tmpFile) 

704 wcs = afwGeom.makeSkyWcs(md, False) 

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

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

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

708 frazzle = md.getScalar("FRAZZLE") 

709 self.assertTrue(frazzle) 

710 

711 def testArchiveKeys(self): 

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

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

714 exposure1.setPsf(self.psf) 

715 exposure1.writeFits(tmpFile) 

716 exposure2 = afwImage.ExposureF(tmpFile) 

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

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

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

720 

721 def testTicket2861(self): 

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

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

724 exposure1.setPsf(self.psf) 

725 schema = afwTable.ExposureTable.makeMinimalSchema() 

726 coaddInputs = afwImage.CoaddInputs(schema, schema) 

727 exposure1.getInfo().setCoaddInputs(coaddInputs) 

728 exposure2 = afwImage.ExposureF(exposure1, True) 

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

730 exposure2.writeFits(tmpFile) 

731 exposure3 = afwImage.ExposureF(tmpFile) 

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

733 

734 def testGetCutout(self): 

735 wcs = self.smallExposure.getWcs() 

736 

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

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

739 2*self.smallExposure.getDimensions()] 

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

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

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

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

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

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

746 for cutoutSize in dimensions: 

747 for label, cutoutCenter in locations: 

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

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

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

751 centerInPixels = wcs.skyToPixel(cutoutCenter) 

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

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

754 self._checkCutoutPixels( 

755 cutout, 

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

757 msg) 

758 

759 # Need a valid WCS 

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

761 self.exposureMiOnly.getCutout(cutoutCenter, cutoutSize) 

762 else: 

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

764 self.smallExposure.getCutout(cutoutCenter, cutoutSize) 

765 

766 def testGetConvexPolygon(self): 

767 """Test the convex polygon.""" 

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

769 self.assertIsNone(self.exposureMiOnly.convex_polygon) 

770 

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

772 bbox = self.exposureMiWcs.getBBox() 

773 # Grow by the default padding. 

774 bbox.grow(10) 

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

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

777 wcs = self.exposureMiWcs.wcs 

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

779 y.ravel()) 

780 

781 poly = self.exposureMiWcs.convex_polygon 

782 contains = poly.contains(ra, dec) 

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

784 

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

786 bbox.grow(1) 

787 

788 ra, dec = wcs.pixelToSkyArray( 

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

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

791 contains = poly.contains(ra, dec) 

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

793 

794 ra, dec = wcs.pixelToSkyArray( 

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

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

797 contains = poly.contains(ra, dec) 

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

799 

800 ra, dec = wcs.pixelToSkyArray( 

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

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

803 contains = poly.contains(ra, dec) 

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

805 

806 ra, dec = wcs.pixelToSkyArray( 

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

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

809 contains = poly.contains(ra, dec) 

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

811 

812 def testContainsSkyCoords(self): 

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

814 self.assertRaisesRegex(ValueError, 

815 "Exposure does not have a valid WCS", 

816 self.exposureMiOnly.containsSkyCoords, 

817 0.0, 

818 0.0) 

819 

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

821 bbox = self.exposureMiWcs.getBBox() 

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

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

824 wcs = self.exposureMiWcs.wcs 

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

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

827 

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

829 dec*units.radian) 

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

831 

832 # Same test, everything in degrees. 

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

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

835 degrees=True) 

836 

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

838 dec*units.degree) 

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

840 

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

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

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

844 

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

846 dec*units.degree) 

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

848 compare[0] = False 

849 compare[-1] = False 

850 np.testing.assert_array_equal(contains, compare) 

851 

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

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

854 

855 Parameters 

856 ---------- 

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

858 The cutout to test. 

859 size : `lsst.geom.Extent2I` 

860 The expected dimensions of ``cutout``. 

861 center : `lsst.geom.SpherePoint` 

862 The expected center of ``cutout``. 

863 precision : `lsst.geom.Angle` 

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

865 msg : `str` 

866 An error message suffix describing test parameters. 

867 """ 

868 newCenter = self._getExposureCenter(cutout) 

869 self.assertIsNotNone(cutout, msg=msg) 

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

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

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

873 

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

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

876 

877 Parameters 

878 ---------- 

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

880 The cutout to test. 

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

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

883 msg : `str` 

884 An error message suffix describing test parameters. 

885 """ 

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

887 edgeMask = mask.getPlaneBitMask("NO_DATA") 

888 

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

890 maskBitsSet = mask[corner] & edgeMask 

891 if corner in validCorners: 

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

893 else: 

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

895 

896 def _getExposureCenter(self, exposure): 

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

898 

899 Parameters 

900 ---------- 

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

902 The image whose center is desired. 

903 

904 Returns 

905 ------- 

906 center : `lsst.geom.SpherePoint` 

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

908 """ 

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

910 

911 def _getValidCorners(self, imageBox, cutoutBox): 

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

913 

914 Parameters 

915 ---------- 

916 imageBox: `lsst.geom.Extent2I` 

917 The bounding box of the original image. 

918 cutoutBox : `lsst.geom.Box2I` 

919 The bounding box of the cutout. 

920 

921 Returns 

922 ------- 

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

924 The corners that are drawn from the original image. 

925 """ 

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

927 

928 

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

930 def setUp(self): 

931 super().setUp() 

932 

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

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

935 np.identity(2), 

936 ) 

937 self.photoCalib = afwImage.PhotoCalib(1.5) 

938 self.psf = DummyPsf(2.0) 

939 self.detector = DetectorWrapper().detector 

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

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

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

943 self.coaddInputs = afwImage.CoaddInputs() 

944 self.apCorrMap = afwImage.ApCorrMap() 

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

946 

947 self.exposureInfo = afwImage.ExposureInfo() 

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

949 self.exposureId = 42 

950 

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

952 self.assertFalse(has()) 

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

954 self.assertIsNone(get()) 

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

956 

957 self.exposureInfo.setComponent(key, value) 

958 self.assertTrue(has()) 

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

960 self.assertIsNotNone(get()) 

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

962 self.assertEqual(get(), value) 

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

964 

965 self.exposureInfo.removeComponent(key) 

966 self.assertFalse(has()) 

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

968 self.assertIsNone(get()) 

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

970 

971 def testAliases(self): 

972 cls = type(self.exposureInfo) 

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

974 self.exposureInfo.hasWcs, self.exposureInfo.getWcs) 

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

976 self.exposureInfo.hasPsf, self.exposureInfo.getPsf) 

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

978 self.exposureInfo.hasPhotoCalib, self.exposureInfo.getPhotoCalib) 

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

980 self.exposureInfo.hasDetector, self.exposureInfo.getDetector) 

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

982 self.exposureInfo.hasValidPolygon, self.exposureInfo.getValidPolygon) 

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

984 self.exposureInfo.hasCoaddInputs, self.exposureInfo.getCoaddInputs) 

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

986 self.exposureInfo.hasApCorrMap, self.exposureInfo.getApCorrMap) 

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

988 self.exposureInfo.hasTransmissionCurve, self.exposureInfo.getTransmissionCurve) 

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

990 self.exposureInfo.hasSummaryStats, self.exposureInfo.getSummaryStats) 

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

992 self.exposureInfo.hasFilter, self.exposureInfo.getFilter) 

993 with self.assertWarns(FutureWarning): 

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

995 self.exposureInfo.hasFilterLabel, self.exposureInfo.getFilterLabel) 

996 

997 def testId(self): 

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

999 

1000 self.assertFalse(self.exposureInfo.hasId()) 

1001 self.assertIsNone(self.exposureInfo.getId()) 

1002 with self.assertWarns(FutureWarning): 

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

1004 self.assertIsNone(self.exposureInfo.id) 

1005 

1006 self.exposureInfo.setId(self.exposureId) 

1007 self.assertTrue(self.exposureInfo.hasId()) 

1008 self.assertIsNotNone(self.exposureInfo.getId()) 

1009 self.assertIsNotNone(self.exposureInfo.id) 

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

1011 with self.assertWarns(FutureWarning): 

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

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

1014 

1015 self.exposureInfo.id = 99899 

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

1017 

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

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

1020 

1021 self.exposureInfo.id = None 

1022 self.assertFalse(self.exposureInfo.hasId()) 

1023 self.assertIsNone(self.exposureInfo.getId()) 

1024 self.assertIsNone(self.exposureInfo.id) 

1025 

1026 def testCopy(self): 

1027 # Test that ExposureInfos have independently settable state 

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

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

1030 

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

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

1033 np.identity(2), 

1034 ) 

1035 copy.setWcs(newWcs) 

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

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

1038 

1039 def testMissingProperties(self): 

1040 # Test that invalid properties return None instead of raising 

1041 exposureInfo = afwImage.ExposureInfo() 

1042 

1043 self.assertIsNone(exposureInfo.id) 

1044 

1045 

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

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

1048 

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

1050 """ 

1051 def setUp(self): 

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

1053 

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

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

1056 nx = ny = 10 

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

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

1059 mask = afwImage.MaskX(nx, ny) 

1060 mask.array[5, 5] = 5 

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

1062 self.exposureId = 12345 

1063 

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

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

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

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

1068 

1069 def testReadUnversioned(self): 

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

1071 """ 

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

1073 exposure = afwImage.ExposureF.readFits(filename) 

1074 

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

1076 

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

1078 with self.assertWarns(FutureWarning): 

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

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

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

1082 with self.assertWarns(FutureWarning): 

1083 self.assertEqual(exposure.getFilterLabel(), self.v1FilterLabel) 

1084 

1085 def testReadVersion0(self): 

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

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

1088 is marked as ExposureInfo version 0 in the header. 

1089 """ 

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

1091 exposure = afwImage.ExposureF.readFits(filename) 

1092 

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

1094 

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

1096 with self.assertWarns(FutureWarning): 

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

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

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

1100 with self.assertWarns(FutureWarning): 

1101 self.assertEqual(exposure.getFilterLabel(), self.v1FilterLabel) 

1102 

1103 # Check that the metadata reader parses the file correctly 

1104 reader = afwImage.ExposureFitsReader(filename) 

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

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

1107 

1108 def testReadVersion1(self): 

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

1110 Version 1 replaced Calib with PhotoCalib. 

1111 """ 

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

1113 exposure = afwImage.ExposureF.readFits(filename) 

1114 

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

1116 

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

1118 with self.assertWarns(FutureWarning): 

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

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

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

1122 with self.assertWarns(FutureWarning): 

1123 self.assertEqual(exposure.getFilterLabel(), self.v1FilterLabel) 

1124 

1125 # Check that the metadata reader parses the file correctly 

1126 reader = afwImage.ExposureFitsReader(filename) 

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

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

1129 

1130 def testReadVersion2(self): 

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

1132 Version 2 replaced Filter with FilterLabel. 

1133 """ 

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

1135 exposure = afwImage.ExposureF.readFits(filename) 

1136 

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

1138 

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

1140 with self.assertWarns(FutureWarning): 

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

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

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

1144 with self.assertWarns(FutureWarning): 

1145 self.assertEqual(exposure.getFilterLabel(), self.v2FilterLabel) 

1146 

1147 # Check that the metadata reader parses the file correctly 

1148 reader = afwImage.ExposureFitsReader(filename) 

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

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

1151 

1152 def testExposureSummaryExtraComponents(self): 

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

1154 """ 

1155 testDict = {'ra': 0.0, 

1156 'decl': 0.0, 

1157 'nonsense': 1.0} 

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

1159 with self.assertWarns(FutureWarning): 

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

1161 

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

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

1164 

1165 

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

1167 pass 

1168 

1169 

1170def setup_module(module): 

1171 lsst.utils.tests.init() 

1172 

1173 

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

1175 lsst.utils.tests.init() 

1176 unittest.main()