Coverage for tests/test_exposure.py: 12%

680 statements  

« prev     ^ index     » next       coverage.py v6.4, created at 2022-06-02 03:42 -0700

1# This file is part of afw. 

2# 

3# Developed for the LSST Data Management System. 

4# This product includes software developed by the LSST Project 

5# (https://www.lsst.org). 

6# See the COPYRIGHT file at the top-level directory of this distribution 

7# for details of code ownership. 

8# 

9# This program is free software: you can redistribute it and/or modify 

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

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

12# (at your option) any later version. 

13# 

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

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

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

17# GNU General Public License for more details. 

18# 

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

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

21 

22""" 

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.image.utils import defineFilter 

39from lsst.afw.coord import Weather 

40import lsst.afw.geom as afwGeom 

41import lsst.afw.table as afwTable 

42import lsst.pex.exceptions as pexExcept 

43from lsst.afw.fits import readMetadata, FitsError 

44from lsst.afw.cameraGeom.testUtils import DetectorWrapper 

45from lsst.log import Log 

46from testTableArchivesLib import DummyPsf 

47 

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

49 

50try: 

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

52except LookupError: 

53 dataDir = None 

54else: 

55 InputMaskedImageName = "871034p_1_MI.fits" 

56 InputMaskedImageNameSmall = "small_MI.fits" 

57 InputImageNameSmall = "small" 

58 OutputMaskedImageName = "871034p_1_MInew.fits" 

59 

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

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

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

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

64 

65 

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

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

68 """ 

69 A test case for the Exposure Class 

70 """ 

71 

72 def setUp(self): 

73 maskedImage = afwImage.MaskedImageF(inFilePathSmall) 

74 maskedImageMD = readMetadata(inFilePathSmall) 

75 

76 self.smallExposure = afwImage.ExposureF(inFilePathSmall) 

77 self.width = maskedImage.getWidth() 

78 self.height = maskedImage.getHeight() 

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

80 self.md = maskedImageMD 

81 self.psf = DummyPsf(2.0) 

82 self.detector = DetectorWrapper().detector 

83 self.id = 42 

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

85 

86 self.exposureBlank = afwImage.ExposureF() 

87 self.exposureMiOnly = afwImage.makeExposure(maskedImage) 

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

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

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

91 # test with ExtentI(100, 100) too 

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

93 

94 afwImage.Filter.reset() 

95 afwImage.FilterProperty.reset() 

96 

97 defineFilter("g", 470.0) 

98 

99 def tearDown(self): 

100 del self.smallExposure 

101 del self.wcs 

102 del self.psf 

103 del self.detector 

104 del self.extras 

105 

106 del self.exposureBlank 

107 del self.exposureMiOnly 

108 del self.exposureMiWcs 

109 del self.exposureCrWcs 

110 del self.exposureCrOnly 

111 

112 def testGetMaskedImage(self): 

113 """ 

114 Test to ensure a MaskedImage can be obtained from each 

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

116 therefore each of the Exposures should return a MaskedImage. 

117 

118 MaskedImage class should throw appropriate 

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

120 obtained. 

121 """ 

122 maskedImageBlank = self.exposureBlank.getMaskedImage() 

123 blankWidth = maskedImageBlank.getWidth() 

124 blankHeight = maskedImageBlank.getHeight() 

125 if blankWidth != blankHeight != 0: 

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

127 

128 maskedImageMiOnly = self.exposureMiOnly.getMaskedImage() 

129 miOnlyWidth = maskedImageMiOnly.getWidth() 

130 miOnlyHeight = maskedImageMiOnly.getHeight() 

131 self.assertAlmostEqual(miOnlyWidth, self.width) 

132 self.assertAlmostEqual(miOnlyHeight, self.height) 

133 

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

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

136 # the WCS being copied/created. 

137 

138 maskedImageMiWcs = self.exposureMiWcs.getMaskedImage() 

139 miWcsWidth = maskedImageMiWcs.getWidth() 

140 miWcsHeight = maskedImageMiWcs.getHeight() 

141 self.assertAlmostEqual(miWcsWidth, self.width) 

142 self.assertAlmostEqual(miWcsHeight, self.height) 

143 

144 maskedImageCrWcs = self.exposureCrWcs.getMaskedImage() 

145 crWcsWidth = maskedImageCrWcs.getWidth() 

146 crWcsHeight = maskedImageCrWcs.getHeight() 

147 if crWcsWidth != crWcsHeight != 0: 

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

149 

150 maskedImageCrOnly = self.exposureCrOnly.getMaskedImage() 

151 crOnlyWidth = maskedImageCrOnly.getWidth() 

152 crOnlyHeight = maskedImageCrOnly.getHeight() 

153 if crOnlyWidth != crOnlyHeight != 0: 

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

155 

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

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

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

159 # check width/height properties 

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

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

162 

163 def testProperties(self): 

164 self.assertMaskedImagesEqual(self.exposureMiOnly.maskedImage, 

165 self.exposureMiOnly.getMaskedImage()) 

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

167 mi2.image.array[:] = 5.0 

168 mi2.variance.array[:] = 3.0 

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

170 self.exposureMiOnly.maskedImage = mi2 

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

172 self.assertImagesEqual(self.exposureMiOnly.image, 

173 self.exposureMiOnly.maskedImage.image) 

174 

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

176 image3.array[:] = 3.0 

177 self.exposureMiOnly.image = image3 

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

179 

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

181 mask3.array[:] = 0x2 

182 self.exposureMiOnly.mask = mask3 

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

184 

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

186 var3.array[:] = 2.0 

187 self.exposureMiOnly.variance = var3 

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

189 

190 # Test the property getter for a null VisitInfo. 

191 self.assertIsNone(self.exposureMiOnly.visitInfo) 

192 

193 def testGetWcs(self): 

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

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

196 """ 

197 # These exposures don't contain a WCS 

198 self.assertIsNone(self.exposureBlank.getWcs()) 

199 self.assertIsNone(self.exposureMiOnly.getWcs()) 

200 self.assertIsNone(self.exposureCrOnly.getWcs()) 

201 

202 # These exposures should contain a WCS 

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

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

205 

206 def testExposureInfoConstructor(self): 

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

208 exposureInfo = afwImage.ExposureInfo() 

209 exposureInfo.setWcs(self.wcs) 

210 exposureInfo.setDetector(self.detector) 

211 gFilter = afwImage.Filter("g") 

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

213 exposureInfo.setFilter(gFilter) 

214 exposureInfo.setFilterLabel(gFilterLabel) 

215 maskedImage = afwImage.MaskedImageF(inFilePathSmall) 

216 exposure = afwImage.ExposureF(maskedImage, exposureInfo) 

217 

218 self.assertTrue(exposure.hasWcs()) 

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

220 self.wcs.getPixelOrigin()) 

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

222 self.detector.getName()) 

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

224 self.detector.getSerial()) 

225 self.assertEqual(exposure.getFilter(), gFilter) 

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

227 

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

229 # check the ExposureInfo property 

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

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

232 self.wcs.getPixelOrigin()) 

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

234 self.detector.getName()) 

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

236 self.detector.getSerial()) 

237 self.assertEqual(exposure.getInfo().getFilter(), gFilter) 

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

239 

240 def testNullWcs(self): 

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

242 

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

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

245 """ 

246 maskedImage = self.exposureMiOnly.getMaskedImage() 

247 exposure = afwImage.ExposureF(maskedImage, None) 

248 self.assertFalse(exposure.hasWcs()) 

249 self.assertFalse(exposure.hasPsf()) 

250 

251 def testExposureInfoSetNone(self): 

252 exposureInfo = afwImage.ExposureInfo() 

253 exposureInfo.setDetector(None) 

254 exposureInfo.setValidPolygon(None) 

255 exposureInfo.setPsf(None) 

256 exposureInfo.setWcs(None) 

257 exposureInfo.setPhotoCalib(None) 

258 exposureInfo.setCoaddInputs(None) 

259 exposureInfo.setVisitInfo(None) 

260 exposureInfo.setApCorrMap(None) 

261 for key in self.extras: 

262 exposureInfo.setComponent(key, None) 

263 

264 def testSetExposureInfo(self): 

265 exposureInfo = afwImage.ExposureInfo() 

266 exposureInfo.setWcs(self.wcs) 

267 exposureInfo.setDetector(self.detector) 

268 gFilter = afwImage.Filter("g") 

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

270 exposureInfo.setFilter(gFilter) 

271 exposureInfo.setFilterLabel(gFilterLabel) 

272 exposureInfo.setId(self.id) 

273 maskedImage = afwImage.MaskedImageF(inFilePathSmall) 

274 exposure = afwImage.ExposureF(maskedImage) 

275 self.assertFalse(exposure.hasWcs()) 

276 

277 exposure.setInfo(exposureInfo) 

278 

279 self.assertTrue(exposure.hasWcs()) 

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

281 self.wcs.getPixelOrigin()) 

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

283 self.detector.getName()) 

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

285 self.detector.getSerial()) 

286 self.assertEqual(exposure.getFilter(), gFilter) 

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

288 

289 # test properties 

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

291 self.assertEqual(exposure.filterLabel, gFilterLabel) 

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

293 

294 def testDefaultFilter(self): 

295 """Test that old convention of having a "default" filter replaced with `None`. 

296 """ 

297 exposureInfo = afwImage.ExposureInfo() 

298 noFilter = afwImage.Filter() 

299 exposureInfo.setFilter(noFilter) 

300 self.assertFalse(exposureInfo.hasFilterLabel()) 

301 self.assertIsNone(exposureInfo.getFilterLabel()) 

302 

303 def testVisitInfoFitsPersistence(self): 

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

305 exposureId = 5 

306 exposureTime = 12.3 

307 boresightRotAngle = 45.6 * lsst.geom.degrees 

308 weather = Weather(1.1, 2.2, 0.3) 

309 visitInfo = afwImage.VisitInfo( 

310 exposureId=exposureId, 

311 exposureTime=exposureTime, 

312 boresightRotAngle=boresightRotAngle, 

313 weather=weather, 

314 ) 

315 photoCalib = afwImage.PhotoCalib(3.4, 5.6) 

316 exposureInfo = afwImage.ExposureInfo() 

317 exposureInfo.setVisitInfo(visitInfo) 

318 exposureInfo.setPhotoCalib(photoCalib) 

319 exposureInfo.setDetector(self.detector) 

320 gFilter = afwImage.Filter("g") 

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

322 exposureInfo.setFilter(gFilter) 

323 exposureInfo.setFilterLabel(gFilterLabel) 

324 maskedImage = afwImage.MaskedImageF(inFilePathSmall) 

325 exposure = afwImage.ExposureF(maskedImage, exposureInfo) 

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

327 exposure.writeFits(tmpFile) 

328 rtExposure = afwImage.ExposureF(tmpFile) 

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

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

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

332 self.assertEqual(rtExposure.getFilter(), gFilter) 

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

334 

335 # Test property getters. 

336 self.assertEqual(rtExposure.photoCalib, photoCalib) 

337 self.assertEqual(rtExposure.filterLabel, gFilterLabel) 

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

339 self.assertIsNotNone(rtExposure.visitInfo) 

340 

341 def testSetMembers(self): 

342 """ 

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

344 """ 

345 exposure = afwImage.ExposureF() 

346 

347 maskedImage = afwImage.MaskedImageF(inFilePathSmall) 

348 exposure.setMaskedImage(maskedImage) 

349 exposure.setWcs(self.wcs) 

350 exposure.setDetector(self.detector) 

351 exposure.setFilter(afwImage.Filter("g")) 

352 exposure.setFilterLabel(afwImage.FilterLabel(band="g")) 

353 

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

355 self.detector.getName()) 

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

357 self.detector.getSerial()) 

358 self.assertEqual(exposure.getFilter().getName(), "g") 

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

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

361 

362 # The PhotoCalib tests are in test_photoCalib.py; 

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

364 self.assertIsNone(exposure.getPhotoCalib()) 

365 

366 photoCalib = afwImage.PhotoCalib(511.1, 44.4) 

367 exposure.setPhotoCalib(photoCalib) 

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

369 

370 # Psfs next 

371 self.assertFalse(exposure.hasPsf()) 

372 exposure.setPsf(self.psf) 

373 self.assertTrue(exposure.hasPsf()) 

374 

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

376 

377 # extras next 

378 info = exposure.getInfo() 

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

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

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

382 info.setComponent(key, value) 

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

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

385 info.removeComponent(key) 

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

387 

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

389 # that already has both 

390 self.exposureMiWcs.setMaskedImage(maskedImage) 

391 exposure.setWcs(self.wcs) 

392 

393 def testHasWcs(self): 

394 """ 

395 Test if an Exposure has a WCS or not. 

396 """ 

397 self.assertFalse(self.exposureBlank.hasWcs()) 

398 

399 self.assertFalse(self.exposureMiOnly.hasWcs()) 

400 self.assertTrue(self.exposureMiWcs.hasWcs()) 

401 self.assertTrue(self.exposureCrWcs.hasWcs()) 

402 self.assertFalse(self.exposureCrOnly.hasWcs()) 

403 

404 def testGetSubExposure(self): 

405 """ 

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

407 

408 The MaskedImage class should throw a 

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

410 subRegion is not fully contained within the original 

411 MaskedImage. 

412 

413 """ 

414 # 

415 # This subExposure is valid 

416 # 

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

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

419 subExposure = self.exposureCrWcs.Factory( 

420 self.exposureCrWcs, subBBox, afwImage.LOCAL) 

421 

422 self.checkWcs(self.exposureCrWcs, subExposure) 

423 

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

425 # from the MaskedImage class and should trigger an exception 

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

427 

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

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

430 

431 def getSubRegion(): 

432 self.exposureCrWcs.Factory( 

433 self.exposureCrWcs, subRegion3, afwImage.LOCAL) 

434 

435 self.assertRaises(pexExcept.LengthError, getSubRegion) 

436 

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

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

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

440 

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

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

443 

444 def getSubRegion(): 

445 self.exposureCrWcs.Factory( 

446 self.exposureCrWcs, subRegion4, afwImage.LOCAL) 

447 

448 self.assertRaises(pexExcept.LengthError, getSubRegion) 

449 

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

451 # transformation 

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

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

454 subExposure = self.exposureCrWcs.Factory( 

455 self.exposureCrWcs, subBBox, afwImage.LOCAL) 

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

457 

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

459 

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

461 

462 def testReadWriteFits(self): 

463 """Test readFits and writeFits. 

464 """ 

465 # This should pass without an exception 

466 mainExposure = afwImage.ExposureF(inFilePathSmall) 

467 mainExposure.info.setId(self.id) 

468 mainExposure.setDetector(self.detector) 

469 

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

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

472 subExposure = mainExposure.Factory( 

473 mainExposure, subBBox, afwImage.LOCAL) 

474 self.checkWcs(mainExposure, subExposure) 

475 det = subExposure.getDetector() 

476 self.assertTrue(det) 

477 

478 subExposure = afwImage.ExposureF( 

479 inFilePathSmall, subBBox, afwImage.LOCAL) 

480 

481 self.checkWcs(mainExposure, subExposure) 

482 

483 # This should throw an exception 

484 def getExposure(): 

485 afwImage.ExposureF(inFilePathSmallImage) 

486 

487 self.assertRaises(FitsError, getExposure) 

488 

489 mainExposure.setPsf(self.psf) 

490 

491 # Make sure we can write without an exception 

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

493 mainExposure.setPhotoCalib(photoCalib) 

494 

495 mainInfo = mainExposure.getInfo() 

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

497 mainInfo.setComponent(key, value) 

498 

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

500 mainExposure.writeFits(tmpFile) 

501 

502 readExposure = type(mainExposure)(tmpFile) 

503 

504 # 

505 # Check the round-tripping 

506 # 

507 self.assertEqual(mainExposure.getFilter().getName(), 

508 readExposure.getFilter().getName()) 

509 self.assertIsNotNone(mainExposure.getFilterLabel()) 

510 self.assertEqual(mainExposure.getFilterLabel(), 

511 readExposure.getFilterLabel()) 

512 

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

514 

515 readInfo = readExposure.getInfo() 

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

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

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

519 

520 psf = readExposure.getPsf() 

521 self.assertIsNotNone(psf) 

522 self.assertEqual(psf, self.psf) 

523 # check psf property getter 

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

525 

526 def checkWcs(self, parentExposure, subExposure): 

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

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

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

530 to get the same sky coordinates for each. 

531 """ 

532 subMI = subExposure.getMaskedImage() 

533 subDim = subMI.getDimensions() 

534 

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

536 # with WCS 

537 mainWcs = parentExposure.getWcs() 

538 subWcs = subExposure.getWcs() 

539 

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

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

542 self.assertSpherePointsAlmostEqual( 

543 mainWcs.pixelToSky( 

544 afwImage.indexToPosition(xSubInd), 

545 afwImage.indexToPosition(ySubInd), 

546 ), 

547 subWcs.pixelToSky( 

548 afwImage.indexToPosition(xSubInd), 

549 afwImage.indexToPosition(ySubInd), 

550 )) 

551 

552 def cmpExposure(self, e1, e2): 

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

554 e2.getDetector().getName()) 

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

556 e2.getDetector().getSerial()) 

557 self.assertEqual(e1.getFilter().getName(), e2.getFilter().getName()) 

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

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

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

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

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

563 # check PSF identity 

564 if not e1.getPsf(): 

565 self.assertFalse(e2.getPsf()) 

566 else: 

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

568 # Check extra components 

569 i1 = e1.getInfo() 

570 i2 = e2.getInfo() 

571 for key in self.extras: 

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

573 if i1.hasComponent(key): 

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

575 

576 def testCopyExposure(self): 

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

578 

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

580 exposureU.setWcs(self.wcs) 

581 exposureU.setDetector(self.detector) 

582 exposureU.setFilter(afwImage.Filter("g")) 

583 exposureU.setFilterLabel(afwImage.FilterLabel(band="g")) 

584 exposureU.setPsf(DummyPsf(4.0)) 

585 infoU = exposureU.getInfo() 

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

587 infoU.setComponent(key, value) 

588 

589 exposureF = exposureU.convertF() 

590 self.cmpExposure(exposureF, exposureU) 

591 

592 nexp = exposureF.Factory(exposureF, False) 

593 self.cmpExposure(exposureF, nexp) 

594 

595 # Ensure that the copy was deep. 

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

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

598 # x0,y0 = cen0 

599 # det = exposureF.getDetector() 

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

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

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

603 

604 def testDeepCopyData(self): 

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

606 """ 

607 exp = afwImage.ExposureF(6, 7) 

608 mi = exp.getMaskedImage() 

609 mi.getImage().set(100) 

610 mi.getMask().set(5) 

611 mi.getVariance().set(200) 

612 

613 expCopy = exp.clone() 

614 miCopy = expCopy.getMaskedImage() 

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

616 miCopy.getMask().set(2) 

617 miCopy.getVariance().set(175) 

618 

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

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

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

622 

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

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

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

626 

627 def testDeepCopySubData(self): 

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

629 """ 

630 exp = afwImage.ExposureF(6, 7) 

631 mi = exp.getMaskedImage() 

632 mi.getImage().set(100) 

633 mi.getMask().set(5) 

634 mi.getVariance().set(200) 

635 

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

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

638 miCopy = expCopy.getMaskedImage() 

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

640 miCopy.getMask().set(2) 

641 miCopy.getVariance().set(175) 

642 

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

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

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

646 

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

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

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

650 

651 def testDeepCopyMetadata(self): 

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

653 """ 

654 exp = afwImage.ExposureF(10, 10) 

655 expMeta = exp.getMetadata() 

656 expMeta.set("foo", 5) 

657 expCopy = exp.clone() 

658 expCopyMeta = expCopy.getMetadata() 

659 expCopyMeta.set("foo", 6) 

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

661 # this will fail if the bug is present 

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

663 

664 def testDeepCopySubMetadata(self): 

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

666 """ 

667 exp = afwImage.ExposureF(10, 10) 

668 expMeta = exp.getMetadata() 

669 expMeta.set("foo", 5) 

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

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

672 expCopyMeta = expCopy.getMetadata() 

673 expCopyMeta.set("foo", 6) 

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

675 # this will fail if the bug is present 

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

677 

678 def testMakeExposureLeaks(self): 

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

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

681 afwImage.makeExposure(afwImage.makeMaskedImage( 

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

683 

684 def testImageSlices(self): 

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

686 exp = afwImage.ExposureF(10, 20) 

687 mi = exp.getMaskedImage() 

688 mi.image[9, 19] = 10 

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

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

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

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

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

694 self.assertEqual(sexp.getDimensions(), 

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

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

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

698 

699 def testConversionToScalar(self): 

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

701 im = afwImage.ExposureF(10, 20) 

702 

703 # only single pixel images may be converted 

704 self.assertRaises(TypeError, float, im) 

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

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

707 

708 def testReadMetadata(self): 

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

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

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

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

713 self.exposureCrWcs.writeFits(tmpFile) 

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

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

716 # frazzle and the Wcs from the PropertySet returned by 

717 # testReadMetadata. 

718 md = readMetadata(tmpFile) 

719 wcs = afwGeom.makeSkyWcs(md, False) 

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

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

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

723 frazzle = md.getScalar("FRAZZLE") 

724 self.assertTrue(frazzle) 

725 

726 def testArchiveKeys(self): 

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

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

729 exposure1.setPsf(self.psf) 

730 exposure1.writeFits(tmpFile) 

731 exposure2 = afwImage.ExposureF(tmpFile) 

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

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

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

735 

736 def testTicket2861(self): 

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

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

739 exposure1.setPsf(self.psf) 

740 schema = afwTable.ExposureTable.makeMinimalSchema() 

741 coaddInputs = afwImage.CoaddInputs(schema, schema) 

742 exposure1.getInfo().setCoaddInputs(coaddInputs) 

743 exposure2 = afwImage.ExposureF(exposure1, True) 

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

745 exposure2.writeFits(tmpFile) 

746 exposure3 = afwImage.ExposureF(tmpFile) 

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

748 

749 def testGetCutout(self): 

750 wcs = self.smallExposure.getWcs() 

751 

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

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

754 2*self.smallExposure.getDimensions()] 

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

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

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

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

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

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

761 for cutoutSize in dimensions: 

762 for label, cutoutCenter in locations: 

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

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

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

766 centerInPixels = wcs.skyToPixel(cutoutCenter) 

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

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

769 self._checkCutoutPixels( 

770 cutout, 

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

772 msg) 

773 

774 # Need a valid WCS 

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

776 self.exposureMiOnly.getCutout(cutoutCenter, cutoutSize) 

777 else: 

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

779 self.smallExposure.getCutout(cutoutCenter, cutoutSize) 

780 

781 def testGetConvexPolygon(self): 

782 """Test the convex polygon.""" 

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

784 self.assertIsNone(self.exposureMiOnly.convex_polygon) 

785 

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

787 bbox = self.exposureMiWcs.getBBox() 

788 # Grow by the default padding. 

789 bbox.grow(10) 

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

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

792 wcs = self.exposureMiWcs.wcs 

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

794 y.ravel()) 

795 

796 poly = self.exposureMiWcs.convex_polygon 

797 contains = poly.contains(ra, dec) 

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

799 

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

801 bbox.grow(1) 

802 

803 ra, dec = wcs.pixelToSkyArray( 

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

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

806 contains = poly.contains(ra, dec) 

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

808 

809 ra, dec = wcs.pixelToSkyArray( 

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

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

812 contains = poly.contains(ra, dec) 

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

814 

815 ra, dec = wcs.pixelToSkyArray( 

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

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

818 contains = poly.contains(ra, dec) 

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

820 

821 ra, dec = wcs.pixelToSkyArray( 

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

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

824 contains = poly.contains(ra, dec) 

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

826 

827 def testContainsSkyCoords(self): 

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

829 self.assertRaisesRegex(ValueError, 

830 "Exposure does not have a valid WCS", 

831 self.exposureMiOnly.containsSkyCoords, 

832 0.0, 

833 0.0) 

834 

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

836 bbox = self.exposureMiWcs.getBBox() 

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

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

839 wcs = self.exposureMiWcs.wcs 

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

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

842 

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

844 dec*units.radian) 

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

846 

847 # Same test, everything in degrees. 

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

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

850 degrees=True) 

851 

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

853 dec*units.degree) 

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

855 

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

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

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

859 

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

861 dec*units.degree) 

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

863 compare[0] = False 

864 compare[-1] = False 

865 np.testing.assert_array_equal(contains, compare) 

866 

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

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

869 

870 Parameters 

871 ---------- 

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

873 The cutout to test. 

874 size : `lsst.geom.Extent2I` 

875 The expected dimensions of ``cutout``. 

876 center : `lsst.geom.SpherePoint` 

877 The expected center of ``cutout``. 

878 precision : `lsst.geom.Angle` 

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

880 msg : `str` 

881 An error message suffix describing test parameters. 

882 """ 

883 newCenter = self._getExposureCenter(cutout) 

884 self.assertIsNotNone(cutout, msg=msg) 

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

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

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

888 

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

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

891 

892 Parameters 

893 ---------- 

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

895 The cutout to test. 

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

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

898 msg : `str` 

899 An error message suffix describing test parameters. 

900 """ 

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

902 edgeMask = mask.getPlaneBitMask("NO_DATA") 

903 

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

905 maskBitsSet = mask[corner] & edgeMask 

906 if corner in validCorners: 

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

908 else: 

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

910 

911 def _getExposureCenter(self, exposure): 

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

913 

914 Parameters 

915 ---------- 

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

917 The image whose center is desired. 

918 

919 Returns 

920 ------- 

921 center : `lsst.geom.SpherePoint` 

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

923 """ 

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

925 

926 def _getValidCorners(self, imageBox, cutoutBox): 

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

928 

929 Parameters 

930 ---------- 

931 imageBox: `lsst.geom.Extent2I` 

932 The bounding box of the original image. 

933 cutoutBox : `lsst.geom.Box2I` 

934 The bounding box of the cutout. 

935 

936 Returns 

937 ------- 

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

939 The corners that are drawn from the original image. 

940 """ 

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

942 

943 

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

945 def setUp(self): 

946 super().setUp() 

947 

948 afwImage.Filter.reset() 

949 afwImage.FilterProperty.reset() 

950 defineFilter("g", 470.0) 

951 

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

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

954 np.identity(2), 

955 ) 

956 self.photoCalib = afwImage.PhotoCalib(1.5) 

957 self.psf = DummyPsf(2.0) 

958 self.detector = DetectorWrapper().detector 

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

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

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

962 self.coaddInputs = afwImage.CoaddInputs() 

963 self.apCorrMap = afwImage.ApCorrMap() 

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

965 

966 self.exposureInfo = afwImage.ExposureInfo() 

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

968 self.exposureId = 42 

969 

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

971 self.assertFalse(has()) 

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

973 self.assertIsNone(get()) 

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

975 

976 self.exposureInfo.setComponent(key, value) 

977 self.assertTrue(has()) 

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

979 self.assertIsNotNone(get()) 

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

981 self.assertEqual(get(), value) 

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

983 

984 self.exposureInfo.removeComponent(key) 

985 self.assertFalse(has()) 

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

987 self.assertIsNone(get()) 

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

989 

990 def testAliases(self): 

991 cls = type(self.exposureInfo) 

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

993 self.exposureInfo.hasWcs, self.exposureInfo.getWcs) 

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

995 self.exposureInfo.hasPsf, self.exposureInfo.getPsf) 

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

997 self.exposureInfo.hasPhotoCalib, self.exposureInfo.getPhotoCalib) 

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

999 self.exposureInfo.hasDetector, self.exposureInfo.getDetector) 

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

1001 self.exposureInfo.hasValidPolygon, self.exposureInfo.getValidPolygon) 

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

1003 self.exposureInfo.hasCoaddInputs, self.exposureInfo.getCoaddInputs) 

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

1005 self.exposureInfo.hasApCorrMap, self.exposureInfo.getApCorrMap) 

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

1007 self.exposureInfo.hasTransmissionCurve, self.exposureInfo.getTransmissionCurve) 

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

1009 self.exposureInfo.hasSummaryStats, self.exposureInfo.getSummaryStats) 

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

1011 self.exposureInfo.hasFilterLabel, self.exposureInfo.getFilterLabel) 

1012 

1013 def testId(self): 

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

1015 

1016 self.assertFalse(self.exposureInfo.hasId()) 

1017 self.assertIsNone(self.exposureInfo.getId()) 

1018 with self.assertWarns(FutureWarning): 

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

1020 self.assertIsNone(self.exposureInfo.id) 

1021 

1022 self.exposureInfo.setId(self.exposureId) 

1023 self.assertTrue(self.exposureInfo.hasId()) 

1024 self.assertIsNotNone(self.exposureInfo.getId()) 

1025 self.assertIsNotNone(self.exposureInfo.id) 

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

1027 with self.assertWarns(FutureWarning): 

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

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

1030 

1031 self.exposureInfo.id = 99899 

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

1033 

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

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

1036 

1037 self.exposureInfo.id = None 

1038 self.assertFalse(self.exposureInfo.hasId()) 

1039 self.assertIsNone(self.exposureInfo.getId()) 

1040 self.assertIsNone(self.exposureInfo.id) 

1041 

1042 def testCopy(self): 

1043 # Test that ExposureInfos have independently settable state 

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

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

1046 

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

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

1049 np.identity(2), 

1050 ) 

1051 copy.setWcs(newWcs) 

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

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

1054 

1055 def testMissingProperties(self): 

1056 # Test that invalid properties return None instead of raising 

1057 exposureInfo = afwImage.ExposureInfo() 

1058 

1059 self.assertIsNone(exposureInfo.id) 

1060 

1061 

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

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

1064 

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

1066 """ 

1067 def setUp(self): 

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

1069 

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

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

1072 nx = ny = 10 

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

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

1075 mask = afwImage.MaskX(nx, ny) 

1076 mask.array[5, 5] = 5 

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

1078 self.exposureId = 12345 

1079 

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

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

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

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

1084 

1085 def testReadUnversioned(self): 

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

1087 """ 

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

1089 exposure = afwImage.ExposureF.readFits(filename) 

1090 

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

1092 

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

1094 with self.assertWarns(FutureWarning): 

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

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

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

1098 

1099 def testReadVersion0(self): 

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

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

1102 is marked as ExposureInfo version 0 in the header. 

1103 """ 

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

1105 exposure = afwImage.ExposureF.readFits(filename) 

1106 

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

1108 

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

1110 with self.assertWarns(FutureWarning): 

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

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

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

1114 

1115 # Check that the metadata reader parses the file correctly 

1116 reader = afwImage.ExposureFitsReader(filename) 

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

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

1119 

1120 def testReadVersion1(self): 

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

1122 Version 1 replaced Calib with PhotoCalib. 

1123 """ 

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

1125 exposure = afwImage.ExposureF.readFits(filename) 

1126 

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

1128 

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

1130 with self.assertWarns(FutureWarning): 

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

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

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

1134 

1135 # Check that the metadata reader parses the file correctly 

1136 reader = afwImage.ExposureFitsReader(filename) 

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

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

1139 

1140 def testReadVersion2(self): 

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

1142 Version 2 replaced Filter with FilterLabel. 

1143 """ 

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

1145 exposure = afwImage.ExposureF.readFits(filename) 

1146 

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

1148 

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

1150 with self.assertWarns(FutureWarning): 

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

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

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

1154 

1155 # Check that the metadata reader parses the file correctly 

1156 reader = afwImage.ExposureFitsReader(filename) 

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

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

1159 

1160 def testExposureSummaryExtraComponents(self): 

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

1162 """ 

1163 testDict = {'ra': 0.0, 

1164 'decl': 0.0, 

1165 'nonsense': 1.0} 

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

1167 with self.assertWarns(FutureWarning): 

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

1169 

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

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

1172 

1173 

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

1175 pass 

1176 

1177 

1178def setup_module(module): 

1179 lsst.utils.tests.init() 

1180 

1181 

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

1183 lsst.utils.tests.init() 

1184 unittest.main()