Coverage for tests/test_visitInfo.py: 9%

438 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2023-06-15 02:47 -0700

1# 

2# LSST Data Management System 

3# Copyright 2016 LSST Corporation. 

4# 

5# This product includes software developed by the 

6# LSST Project (http://www.lsst.org/). 

7# 

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

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

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

11# (at your option) any later version. 

12# 

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

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

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

16# GNU General Public License for more details. 

17# 

18# You should have received a copy of the LSST License Statement and 

19# the GNU General Public License along with this program. If not, 

20# see <http://www.lsstcorp.org/LegalNotices/>. 

21# 

22import math 

23import os 

24import unittest 

25import collections 

26import numpy as np 

27 

28import lsst.utils.tests 

29import lsst.pex.exceptions 

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

31from lsst.geom import Angle, degrees, SpherePoint 

32from lsst.afw.coord import Observatory, Weather 

33import lsst.afw.image as afwImage 

34 

35RotTypeEnumNameDict = { 

36 afwImage.RotType.UNKNOWN: "UNKNOWN", 

37 afwImage.RotType.SKY: "SKY", 

38 afwImage.RotType.HORIZON: "HORIZON", 

39 afwImage.RotType.MOUNT: "MOUNT", 

40} 

41 

42 

43def propertySetFromDict(keyValDict): 

44 """Make an lsst.daf.base.PropertySet from a dict of key: value""" 

45 metadata = PropertySet() 

46 for key, val in keyValDict.items(): 

47 metadata.set(key, val) 

48 return metadata 

49 

50 

51def makeVisitInfo(data): 

52 """Return a VisitInfo constructed from a VisitInfoData namedtuple.""" 

53 return afwImage.VisitInfo(data.exposureId, 

54 data.exposureTime, 

55 data.darkTime, 

56 data.date, 

57 data.ut1, 

58 data.era, 

59 data.boresightRaDec, 

60 data.boresightAzAlt, 

61 data.boresightAirmass, 

62 data.boresightRotAngle, 

63 data.rotType, 

64 data.observatory, 

65 data.weather, 

66 data.instrumentLabel, 

67 data.id, 

68 data.focusZ, 

69 data.observationType, 

70 data.scienceProgram, 

71 data.observationReason, 

72 data.object, 

73 data.hasSimulatedContent, 

74 ) 

75 

76 

77class VisitInfoTestCase(lsst.utils.tests.TestCase): 

78 """Test lsst.afw.image.VisitInfo, a simple struct-like class""" 

79 

80 def setUp(self): 

81 self.testDir = os.path.dirname(__file__) 

82 

83 def computeLstHA(data): 

84 """Return LST, Hour Angle, computed from VisitInfoData.""" 

85 localEra = data.era + data.observatory.getLongitude() 

86 hourAngle = localEra - data.boresightRaDec[0] 

87 return localEra, hourAngle 

88 

89 fields = ['exposureId', 

90 'exposureTime', 

91 'darkTime', 

92 'date', 

93 'ut1', 

94 'era', 

95 'boresightRaDec', 

96 'boresightAzAlt', 

97 'boresightAirmass', 

98 'boresightRotAngle', 

99 'rotType', 

100 'observatory', 

101 'weather', 

102 'instrumentLabel', 

103 'id', 

104 'focusZ', 

105 'observationType', 

106 "scienceProgram", 

107 "observationReason", 

108 "object", 

109 "hasSimulatedContent", 

110 ] 

111 VisitInfoData = collections.namedtuple("VisitInfoData", fields) 

112 data1 = VisitInfoData(exposureId=10313423, 

113 exposureTime=10.01, 

114 darkTime=11.02, 

115 date=DateTime( 

116 65321.1, DateTime.MJD, DateTime.TAI), 

117 ut1=12345.1, 

118 era=45.1*degrees, 

119 boresightRaDec=SpherePoint( 

120 23.1*degrees, 73.2*degrees), 

121 boresightAzAlt=SpherePoint( 

122 134.5*degrees, 33.3*degrees), 

123 boresightAirmass=1.73, 

124 boresightRotAngle=73.2*degrees, 

125 rotType=afwImage.RotType.SKY, 

126 observatory=Observatory( 

127 11.1*degrees, 22.2*degrees, 0.333), 

128 weather=Weather(1.1, 2.2, 34.5), 

129 instrumentLabel="TestCameraOne", 

130 id=987654, 

131 focusZ=1.5, 

132 observationType="flat", 

133 scienceProgram="test program", 

134 observationReason="test reason", 

135 object="test object", 

136 hasSimulatedContent=False, 

137 ) 

138 self.data1 = data1 

139 self.localEra1, self.hourAngle1 = computeLstHA(data1) 

140 data2 = VisitInfoData(exposureId=1, 

141 exposureTime=15.5, 

142 darkTime=17.8, 

143 date=DateTime( 

144 55321.2, DateTime.MJD, DateTime.TAI), 

145 ut1=312345.1, 

146 era=25.1*degrees, 

147 boresightRaDec=SpherePoint( 

148 2.1*degrees, 33.2*degrees), 

149 boresightAzAlt=SpherePoint(13.5*degrees, 83.3*degrees), 

150 boresightAirmass=2.05, 

151 boresightRotAngle=-53.2*degrees, 

152 rotType=afwImage.RotType.HORIZON, 

153 observatory=Observatory( 

154 22.2*degrees, 33.3*degrees, 0.444), 

155 weather=Weather(2.2, 3.3, 44.4), 

156 instrumentLabel="TestCameraTwo", 

157 id=123456, 

158 focusZ=-0.7, 

159 observationType="science", 

160 scienceProgram="test program 2", 

161 observationReason="test reason 2", 

162 object="test object 2", 

163 hasSimulatedContent=True, 

164 ) 

165 self.data2 = data2 

166 self.localEra2, self.hourAngle2 = computeLstHA(data2) 

167 

168 def _testValueConstructor(self, data, localEra, hourAngle): 

169 visitInfo = makeVisitInfo(data) 

170 with self.assertWarns(FutureWarning): 

171 self.assertEqual(visitInfo.getExposureId(), data.exposureId) 

172 self.assertEqual(visitInfo.getExposureTime(), data.exposureTime) 

173 self.assertEqual(visitInfo.getDarkTime(), data.darkTime) 

174 self.assertEqual(visitInfo.getDate(), data.date) 

175 self.assertEqual(visitInfo.getUt1(), data.ut1) 

176 self.assertEqual(visitInfo.getEra(), data.era) 

177 self.assertEqual(visitInfo.getBoresightRaDec(), data.boresightRaDec) 

178 self.assertEqual(visitInfo.getBoresightAzAlt(), data.boresightAzAlt) 

179 self.assertEqual(visitInfo.getBoresightAirmass(), 

180 data.boresightAirmass) 

181 self.assertEqual(visitInfo.getBoresightRotAngle(), 

182 data.boresightRotAngle) 

183 self.assertEqual(visitInfo.getRotType(), data.rotType) 

184 self.assertEqual(visitInfo.getObservatory(), data.observatory) 

185 self.assertEqual(visitInfo.getInstrumentLabel(), data.instrumentLabel) 

186 self.assertEqual(visitInfo.getWeather(), data.weather) 

187 self.assertEqual(visitInfo.getLocalEra(), localEra) 

188 self.assertEqual(visitInfo.getBoresightHourAngle(), hourAngle) 

189 self.assertEqual(visitInfo.getId(), data.id) 

190 self.assertEqual(visitInfo.getFocusZ(), data.focusZ) 

191 self.assertEqual(visitInfo.getObservationType(), data.observationType) 

192 self.assertEqual(visitInfo.getScienceProgram(), data.scienceProgram) 

193 self.assertEqual(visitInfo.getObservationReason(), data.observationReason) 

194 self.assertEqual(visitInfo.getObject(), data.object) 

195 self.assertEqual(visitInfo.getHasSimulatedContent(), data.hasSimulatedContent) 

196 

197 def _testProperties(self, data, localEra, hourAngle): 

198 """Test property attribute accessors.""" 

199 visitInfo = makeVisitInfo(data) 

200 self.assertEqual(visitInfo.exposureTime, data.exposureTime) 

201 self.assertEqual(visitInfo.darkTime, data.darkTime) 

202 self.assertEqual(visitInfo.date, data.date) 

203 self.assertEqual(visitInfo.ut1, data.ut1) 

204 self.assertEqual(visitInfo.era, data.era) 

205 self.assertEqual(visitInfo.boresightRaDec, data.boresightRaDec) 

206 self.assertEqual(visitInfo.boresightAzAlt, data.boresightAzAlt) 

207 self.assertEqual(visitInfo.boresightAirmass, data.boresightAirmass) 

208 self.assertEqual(visitInfo.boresightRotAngle, data.boresightRotAngle) 

209 self.assertEqual(visitInfo.rotType, data.rotType) 

210 self.assertEqual(visitInfo.observatory, data.observatory) 

211 self.assertEqual(visitInfo.instrumentLabel, data.instrumentLabel) 

212 self.assertEqual(visitInfo.weather, data.weather) 

213 self.assertEqual(visitInfo.localEra, localEra) 

214 self.assertEqual(visitInfo.boresightHourAngle, hourAngle) 

215 self.assertEqual(visitInfo.id, data.id) 

216 self.assertEqual(visitInfo.focusZ, data.focusZ) 

217 self.assertEqual(visitInfo.observationType, data.observationType) 

218 self.assertEqual(visitInfo.scienceProgram, data.scienceProgram) 

219 self.assertEqual(visitInfo.observationReason, data.observationReason) 

220 self.assertEqual(visitInfo.object, data.object) 

221 self.assertEqual(visitInfo.hasSimulatedContent, data.hasSimulatedContent) 

222 

223 def testValueConstructor_data1(self): 

224 self._testValueConstructor(self.data1, self.localEra1, self.hourAngle1) 

225 self._testProperties(self.data1, self.localEra1, self.hourAngle1) 

226 

227 def testValueConstructor_data2(self): 

228 self._testValueConstructor(self.data2, self.localEra2, self.hourAngle2) 

229 self._testProperties(self.data2, self.localEra2, self.hourAngle2) 

230 

231 def testCopyWith(self): 

232 visitInfo1 = makeVisitInfo(self.data1) 

233 visitInfo2 = makeVisitInfo(self.data2) 

234 

235 updateFields1 = [ 

236 "exposureTime", 

237 "darkTime", 

238 "date", 

239 "ut1", 

240 "era", 

241 "boresightRaDec", 

242 "boresightAzAlt", 

243 "boresightAirmass", 

244 "boresightRotAngle", 

245 ] 

246 

247 updateFields2 = [ 

248 "rotType", 

249 "observatory", 

250 "weather", 

251 "instrumentLabel", 

252 "id", 

253 "focusZ", 

254 "observationType", 

255 "scienceProgram", 

256 "observationReason", 

257 "object", 

258 "hasSimulatedContent", 

259 ] 

260 

261 kwargs1 = {k: getattr(visitInfo2, k) for k in updateFields1} 

262 kwargs2 = {k: getattr(visitInfo2, k) for k in updateFields2} 

263 

264 newVisit1 = visitInfo1.copyWith(**kwargs1) 

265 newVisit2 = visitInfo1.copyWith(**kwargs2) 

266 

267 for field in updateFields1: 

268 self.assertEqual(getattr(newVisit1, field), getattr(visitInfo2, field)) 

269 self.assertEqual(getattr(newVisit2, field), getattr(visitInfo1, field)) 

270 

271 for field in updateFields2: 

272 self.assertEqual(getattr(newVisit1, field), getattr(visitInfo1, field)) 

273 self.assertEqual(getattr(newVisit2, field), getattr(visitInfo2, field)) 

274 

275 # Test the deprecated exposureId. 

276 # This code can be removed with DM-32138. 

277 deprecatedVisit = visitInfo1.copyWith(exposureId=3) 

278 self.assertEqual(deprecatedVisit.getExposureId(), 3) 

279 deprecatedCopy = deprecatedVisit.copyWith(**kwargs1) 

280 self.assertEqual(deprecatedCopy.getExposureId(), 3) 

281 

282 def testTablePersistence(self): 

283 """Test that VisitInfo can be round-tripped with current code. 

284 """ 

285 for item in (self.data1, self.data2): 

286 tablePath = os.path.join( 

287 self.testDir, "testVisitInfo_testTablePersistence.fits") 

288 v1 = afwImage.VisitInfo(*item) 

289 v1.writeFits(tablePath) 

290 v2 = afwImage.VisitInfo.readFits(tablePath) 

291 self.assertEqual(v1, v2) 

292 os.unlink(tablePath) 

293 

294 def _testFitsRead(self, data, filePath, version): 

295 """Test that old VersionInfo files are read correctly. 

296 

297 Parameters 

298 ---------- 

299 data : `VisitInfoData` 

300 The values expected to be stored in the file, or a 

301 superset thereof. 

302 filePath : `str` 

303 The file to test. 

304 version : `int` 

305 The VersionInfo persistence format used in ``filePath``. 

306 """ 

307 visitInfo = afwImage.VisitInfo.readFits(filePath) 

308 

309 if version >= 0: 

310 with self.assertWarns(FutureWarning): 

311 self.assertEqual(visitInfo.getExposureId(), data.exposureId) 

312 self.assertEqual(visitInfo.getExposureTime(), data.exposureTime) 

313 self.assertEqual(visitInfo.getDarkTime(), data.darkTime) 

314 self.assertEqual(visitInfo.getDate(), data.date) 

315 self.assertEqual(visitInfo.getUt1(), data.ut1) 

316 self.assertEqual(visitInfo.getEra(), data.era) 

317 self.assertEqual(visitInfo.getBoresightRaDec(), data.boresightRaDec) 

318 self.assertEqual(visitInfo.getBoresightAzAlt(), data.boresightAzAlt) 

319 self.assertEqual(visitInfo.getBoresightAirmass(), 

320 data.boresightAirmass) 

321 self.assertEqual(visitInfo.getBoresightRotAngle(), 

322 data.boresightRotAngle) 

323 self.assertEqual(visitInfo.getRotType(), data.rotType) 

324 self.assertEqual(visitInfo.getObservatory(), data.observatory) 

325 self.assertEqual(visitInfo.getWeather(), data.weather) 

326 if version >= 1: 

327 self.assertEqual(visitInfo.getInstrumentLabel(), data.instrumentLabel) 

328 else: 

329 self.assertEqual(visitInfo.getInstrumentLabel(), "") 

330 if version >= 2: 

331 self.assertEqual(visitInfo.getId(), data.id) 

332 else: 

333 self.assertEqual(visitInfo.getId(), 0) 

334 if version >= 3: 

335 self.assertEqual(visitInfo.getFocusZ(), data.focusZ) 

336 else: 

337 self.assertTrue(math.isnan(visitInfo.getFocusZ())) 

338 if version >= 4: 

339 self.assertEqual(visitInfo.getObservationType(), data.observationType) 

340 self.assertEqual(visitInfo.getScienceProgram(), data.scienceProgram) 

341 self.assertEqual(visitInfo.getObservationReason(), data.observationReason) 

342 self.assertEqual(visitInfo.getObject(), data.object) 

343 self.assertEqual(visitInfo.getHasSimulatedContent(), data.hasSimulatedContent) 

344 else: 

345 self.assertEqual(visitInfo.getObservationType(), "") 

346 self.assertEqual(visitInfo.getScienceProgram(), "") 

347 self.assertEqual(visitInfo.getObservationReason(), "") 

348 self.assertEqual(visitInfo.getObject(), "") 

349 self.assertEqual(visitInfo.getHasSimulatedContent(), False) 

350 

351 def testPersistenceVersions(self): 

352 """Test that older versions are handled appropriately. 

353 """ 

354 dataDir = os.path.join(os.path.dirname(__file__), "data") 

355 

356 # All files created by makeVisitInfo(self.data1).writeFits() 

357 self._testFitsRead(self.data1, os.path.join(dataDir, "visitInfo-noversion.fits"), 0) 

358 self._testFitsRead(self.data1, os.path.join(dataDir, "visitInfo-version-1.fits"), 1) 

359 self._testFitsRead(self.data1, os.path.join(dataDir, "visitInfo-version-2.fits"), 2) 

360 self._testFitsRead(self.data1, os.path.join(dataDir, "visitInfo-version-3.fits"), 3) 

361 self._testFitsRead(self.data1, os.path.join(dataDir, "visitInfo-version-4.fits"), 4) 

362 

363 # Check that reading a newer file raises a useful exception. 

364 with self.assertRaisesRegex(TypeError, 

365 r"Cannot read VisitInfo FITS version > [0-9]+, found version 999999"): 

366 afwImage.VisitInfo.readFits(os.path.join(dataDir, "visitInfo-version-999999.fits")) 

367 

368 def testSetVisitInfoMetadata(self): 

369 for item in (self.data1, self.data2): 

370 visitInfo = makeVisitInfo(item) 

371 metadata = PropertyList() 

372 afwImage.setVisitInfoMetadata(metadata, visitInfo) 

373 self.assertEqual(metadata.nameCount(), 28) 

374 self.assertEqual(metadata.getScalar("EXPID"), item.exposureId) 

375 self.assertEqual(metadata.getScalar("EXPTIME"), item.exposureTime) 

376 self.assertEqual(metadata.getScalar("DARKTIME"), item.darkTime) 

377 self.assertEqual(metadata.getScalar("DATE-AVG"), 

378 item.date.toString(DateTime.TAI)) 

379 self.assertEqual(metadata.getScalar("TIMESYS"), "TAI") 

380 self.assertEqual(metadata.getScalar("MJD-AVG-UT1"), item.ut1) 

381 self.assertEqual(metadata.getScalar("AVG-ERA"), item.era.asDegrees()) 

382 self.assertEqual(metadata.getScalar("BORE-RA"), 

383 item.boresightRaDec[0].asDegrees()) 

384 self.assertEqual(metadata.getScalar("BORE-DEC"), 

385 item.boresightRaDec[1].asDegrees()) 

386 self.assertEqual(metadata.getScalar("BORE-AZ"), 

387 item.boresightAzAlt[0].asDegrees()) 

388 self.assertEqual(metadata.getScalar("BORE-ALT"), 

389 item.boresightAzAlt[1].asDegrees()) 

390 self.assertEqual(metadata.getScalar("BORE-AIRMASS"), 

391 item.boresightAirmass) 

392 self.assertEqual(metadata.getScalar("BORE-ROTANG"), 

393 item.boresightRotAngle.asDegrees()) 

394 self.assertEqual(metadata.getScalar("ROTTYPE"), 

395 RotTypeEnumNameDict[item.rotType]) 

396 self.assertEqual(metadata.getScalar("OBS-LONG"), 

397 item.observatory.getLongitude().asDegrees()) 

398 self.assertEqual(metadata.getScalar("OBS-LAT"), 

399 item.observatory.getLatitude().asDegrees()) 

400 self.assertEqual(metadata.getScalar("OBS-ELEV"), 

401 item.observatory.getElevation()) 

402 self.assertEqual(metadata.getScalar("AIRTEMP"), 

403 item.weather.getAirTemperature()) 

404 self.assertEqual(metadata.getScalar("AIRPRESS"), 

405 item.weather.getAirPressure()) 

406 self.assertEqual(metadata.getScalar("HUMIDITY"), 

407 item.weather.getHumidity()) 

408 self.assertEqual(metadata.getScalar("INSTRUMENT"), 

409 item.instrumentLabel) 

410 self.assertEqual(metadata.getScalar("IDNUM"), 

411 item.id) 

412 self.assertEqual(metadata.getScalar("FOCUSZ"), 

413 item.focusZ) 

414 self.assertEqual(metadata.getScalar("OBSTYPE"), 

415 item.observationType) 

416 self.assertEqual(metadata.getScalar("PROGRAM"), 

417 item.scienceProgram) 

418 self.assertEqual(metadata.getScalar("REASON"), 

419 item.observationReason) 

420 self.assertEqual(metadata.getScalar("OBJECT"), 

421 item.object) 

422 self.assertEqual(metadata.getScalar("HAS-SIMULATED-CONTENT"), 

423 item.hasSimulatedContent) 

424 

425 def testSetVisitInfoMetadataMissingValues(self): 

426 """If a value is unknown then it should not be written to the metadata""" 

427 # Only rot type and hasSimulatedContent are "known" when default- 

428 # constructed, by virtue of having no "null" state. 

429 visitInfo = afwImage.VisitInfo() 

430 metadata = PropertyList() 

431 afwImage.setVisitInfoMetadata(metadata, visitInfo) 

432 self.assertEqual(metadata.getScalar("ROTTYPE"), 

433 RotTypeEnumNameDict[afwImage.RotType.UNKNOWN]) 

434 self.assertEqual(metadata.getScalar("HAS-SIMULATED-CONTENT"), False) 

435 self.assertEqual(metadata.nameCount(), 2) 

436 

437 def testStripVisitInfoKeywords(self): 

438 for argList in (self.data1, self.data2): 

439 visitInfo = afwImage.VisitInfo(*argList) 

440 metadata = PropertyList() 

441 afwImage.setVisitInfoMetadata(metadata, visitInfo) 

442 # add an extra keyword that will not be stripped 

443 metadata.set("EXTRA", 5) 

444 self.assertEqual(metadata.nameCount(), 29) 

445 afwImage.stripVisitInfoKeywords(metadata) 

446 self.assertEqual(metadata.nameCount(), 1) 

447 

448 def _testIsEmpty(self, visitInfo): 

449 """Test that visitInfo is all NaN, 0, or empty string, as appropriate. 

450 """ 

451 with self.assertWarns(FutureWarning): 

452 self.assertEqual(visitInfo.getExposureId(), 0) 

453 self.assertTrue(math.isnan(visitInfo.getExposureTime())) 

454 self.assertTrue(math.isnan(visitInfo.getDarkTime())) 

455 self.assertEqual(visitInfo.getDate(), DateTime()) 

456 self.assertTrue(math.isnan(visitInfo.getUt1())) 

457 self.assertTrue(math.isnan(visitInfo.getEra().asDegrees())) 

458 for i in range(2): 

459 self.assertTrue(math.isnan( 

460 visitInfo.getBoresightRaDec()[i].asDegrees())) 

461 self.assertTrue(math.isnan( 

462 visitInfo.getBoresightAzAlt()[i].asDegrees())) 

463 self.assertTrue(math.isnan(visitInfo.getBoresightAirmass())) 

464 self.assertTrue(math.isnan( 

465 visitInfo.getBoresightRotAngle().asDegrees())) 

466 self.assertEqual(visitInfo.getRotType(), afwImage.RotType.UNKNOWN) 

467 self.assertTrue(math.isnan( 

468 visitInfo.getObservatory().getLongitude().asDegrees())) 

469 self.assertTrue(math.isnan( 

470 visitInfo.getObservatory().getLatitude().asDegrees())) 

471 self.assertTrue(math.isnan(visitInfo.getObservatory().getElevation())) 

472 self.assertTrue(math.isnan(visitInfo.getWeather().getAirTemperature())) 

473 self.assertTrue(math.isnan(visitInfo.getWeather().getAirPressure())) 

474 self.assertTrue(math.isnan(visitInfo.getWeather().getHumidity())) 

475 self.assertTrue(math.isnan(visitInfo.getBoresightHourAngle())) 

476 self.assertEqual(visitInfo.getInstrumentLabel(), "") 

477 self.assertEqual(visitInfo.getId(), 0) 

478 self.assertTrue(math.isnan(visitInfo.getFocusZ())) 

479 self.assertEqual(visitInfo.getObservationType(), "") 

480 self.assertEqual(visitInfo.getScienceProgram(), "") 

481 self.assertEqual(visitInfo.getObservationReason(), "") 

482 self.assertEqual(visitInfo.getObject(), "") 

483 self.assertEqual(visitInfo.getHasSimulatedContent(), False) 

484 

485 def testEquals(self): 

486 """Test that identical VisitInfo objects compare equal, even if some fields are NaN. 

487 """ 

488 # objects with "equal state" should be equal 

489 self.assertEqual(makeVisitInfo(self.data1), makeVisitInfo(self.data1)) 

490 self.assertEqual(makeVisitInfo(self.data2), makeVisitInfo(self.data2)) 

491 self.assertNotEqual(makeVisitInfo(self.data1), makeVisitInfo(self.data2)) 

492 self.assertEqual(afwImage.VisitInfo(), afwImage.VisitInfo()) 

493 

494 # equality must be reflexive 

495 info = makeVisitInfo(self.data1) 

496 self.assertEqual(info, info) 

497 info = makeVisitInfo(self.data2) 

498 self.assertEqual(info, info) 

499 info = afwImage.VisitInfo() 

500 self.assertEqual(info, info) 

501 

502 # commutativity and transitivity difficult to test with this setup 

503 

504 def testMetadataConstructor(self): 

505 """Test the metadata constructor 

506 

507 This constructor allows missing values 

508 """ 

509 data = self.data1 

510 

511 metadata = propertySetFromDict({}) 

512 visitInfo = afwImage.VisitInfo(metadata) 

513 self._testIsEmpty(visitInfo) 

514 

515 metadata = propertySetFromDict({"EXPID": data.exposureId}) 

516 visitInfo = afwImage.VisitInfo(metadata) 

517 with self.assertWarns(FutureWarning): 

518 self.assertEqual(visitInfo.getExposureId(), data.exposureId) 

519 self.assertTrue(math.isnan(visitInfo.getExposureTime())) 

520 

521 metadata = propertySetFromDict({"EXPTIME": data.exposureTime}) 

522 visitInfo = afwImage.VisitInfo(metadata) 

523 self.assertEqual(visitInfo.getExposureTime(), data.exposureTime) 

524 

525 metadata = propertySetFromDict({"DARKTIME": data.darkTime}) 

526 visitInfo = afwImage.VisitInfo(metadata) 

527 self.assertEqual(visitInfo.getDarkTime(), data.darkTime) 

528 

529 metadata = propertySetFromDict( 

530 {"DATE-AVG": data.date.toString(DateTime.TAI), "TIMESYS": "TAI"}) 

531 visitInfo = afwImage.VisitInfo(metadata) 

532 self.assertEqual(visitInfo.getDate(), data.date) 

533 

534 # TIME-MID in UTC is an acceptable alternative to DATE-AVG 

535 metadata = propertySetFromDict( 

536 {"TIME-MID": data.date.toString(DateTime.UTC)}) 

537 visitInfo = afwImage.VisitInfo(metadata) 

538 self.assertEqual(visitInfo.getDate(), data.date) 

539 

540 # TIME-MID must be in UTC and TIMESYS is ignored 

541 metadata = propertySetFromDict({ 

542 "TIME-MID": data.date.toString(DateTime.TAI) + "Z", 

543 "TIMESYS": "TAI", 

544 }) 

545 visitInfo = afwImage.VisitInfo(metadata) 

546 self.assertNotEqual(visitInfo.getDate(), data.date) 

547 

548 # if both DATE-AVG and TIME-MID provided then use DATE-AVG 

549 # use the wrong time system for TIME-MID so if it is used, an error 

550 # will result 

551 metadata = propertySetFromDict({ 

552 "DATE-AVG": data.date.toString(DateTime.TAI), 

553 "TIMESYS": "TAI", 

554 "TIME-MID": data.date.toString(DateTime.TAI) + "Z", 

555 }) 

556 visitInfo = afwImage.VisitInfo(metadata) 

557 self.assertEqual(visitInfo.getDate(), data.date) 

558 

559 metadata = propertySetFromDict({"MJD-AVG-UT1": data.ut1}) 

560 visitInfo = afwImage.VisitInfo(metadata) 

561 self.assertEqual(visitInfo.getUt1(), data.ut1) 

562 

563 metadata = propertySetFromDict({"AVG-ERA": data.era.asDegrees()}) 

564 visitInfo = afwImage.VisitInfo(metadata) 

565 self.assertEqual(visitInfo.getEra(), data.era) 

566 

567 for i, key in enumerate(("BORE-RA", "BORE-DEC")): 

568 metadata = propertySetFromDict( 

569 {key: data.boresightRaDec[i].asDegrees()}) 

570 visitInfo = afwImage.VisitInfo(metadata) 

571 self.assertEqual(visitInfo.getBoresightRaDec() 

572 [i], data.boresightRaDec[i]) 

573 

574 for i, key in enumerate(("BORE-AZ", "BORE-ALT")): 

575 metadata = propertySetFromDict( 

576 {key: data.boresightAzAlt[i].asDegrees()}) 

577 visitInfo = afwImage.VisitInfo(metadata) 

578 self.assertEqual(visitInfo.getBoresightAzAlt() 

579 [i], data.boresightAzAlt[i]) 

580 

581 metadata = propertySetFromDict({"BORE-AIRMASS": data.boresightAirmass}) 

582 visitInfo = afwImage.VisitInfo(metadata) 

583 self.assertEqual(visitInfo.getBoresightAirmass(), 

584 data.boresightAirmass) 

585 

586 metadata = propertySetFromDict( 

587 {"BORE-ROTANG": data.boresightRotAngle.asDegrees()}) 

588 visitInfo = afwImage.VisitInfo(metadata) 

589 self.assertEqual(visitInfo.getBoresightRotAngle(), 

590 data.boresightRotAngle) 

591 

592 metadata = propertySetFromDict( 

593 {"ROTTYPE": RotTypeEnumNameDict[data.rotType]}) 

594 visitInfo = afwImage.VisitInfo(metadata) 

595 self.assertEqual(visitInfo.getRotType(), data.rotType) 

596 

597 metadata = propertySetFromDict( 

598 {"OBS-LONG": data.observatory.getLongitude().asDegrees()}) 

599 visitInfo = afwImage.VisitInfo(metadata) 

600 self.assertEqual(visitInfo.getObservatory().getLongitude(), 

601 data.observatory.getLongitude()) 

602 

603 metadata = propertySetFromDict( 

604 {"OBS-LAT": data.observatory.getLatitude().asDegrees()}) 

605 visitInfo = afwImage.VisitInfo(metadata) 

606 self.assertEqual(visitInfo.getObservatory().getLatitude(), 

607 data.observatory.getLatitude()) 

608 

609 metadata = propertySetFromDict( 

610 {"OBS-ELEV": data.observatory.getElevation()}) 

611 visitInfo = afwImage.VisitInfo(metadata) 

612 self.assertEqual(visitInfo.getObservatory().getElevation(), 

613 data.observatory.getElevation()) 

614 

615 metadata = propertySetFromDict( 

616 {"AIRTEMP": data.weather.getAirTemperature()}) 

617 visitInfo = afwImage.VisitInfo(metadata) 

618 self.assertEqual(visitInfo.getWeather().getAirTemperature(), 

619 data.weather.getAirTemperature()) 

620 

621 metadata = propertySetFromDict( 

622 {"AIRPRESS": data.weather.getAirPressure()}) 

623 visitInfo = afwImage.VisitInfo(metadata) 

624 self.assertEqual(visitInfo.getWeather().getAirPressure(), 

625 data.weather.getAirPressure()) 

626 

627 metadata = propertySetFromDict( 

628 {"HUMIDITY": data.weather.getHumidity()}) 

629 visitInfo = afwImage.VisitInfo(metadata) 

630 self.assertEqual(visitInfo.getWeather().getHumidity(), 

631 data.weather.getHumidity()) 

632 

633 metadata = propertySetFromDict({"INSTRUMENT": data.instrumentLabel}) 

634 visitInfo = afwImage.VisitInfo(metadata) 

635 self.assertEqual(visitInfo.getInstrumentLabel(), data.instrumentLabel) 

636 

637 metadata = propertySetFromDict({"IDNUM": data.id}) 

638 visitInfo = afwImage.VisitInfo(metadata) 

639 self.assertEqual(visitInfo.getId(), data.id) 

640 

641 metadata = propertySetFromDict({"FOCUSZ": data.focusZ}) 

642 visitInfo = afwImage.VisitInfo(metadata) 

643 self.assertEqual(visitInfo.getFocusZ(), data.focusZ) 

644 

645 metadata = propertySetFromDict({"OBSTYPE": data.observationType}) 

646 visitInfo = afwImage.VisitInfo(metadata) 

647 self.assertEqual(visitInfo.getObservationType(), data.observationType) 

648 

649 metadata = propertySetFromDict({"PROGRAM": data.scienceProgram}) 

650 visitInfo = afwImage.VisitInfo(metadata) 

651 self.assertEqual(visitInfo.getScienceProgram(), data.scienceProgram) 

652 

653 metadata = propertySetFromDict({"REASON": data.observationReason}) 

654 visitInfo = afwImage.VisitInfo(metadata) 

655 self.assertEqual(visitInfo.getObservationReason(), data.observationReason) 

656 

657 metadata = propertySetFromDict({"OBJECT": data.object}) 

658 visitInfo = afwImage.VisitInfo(metadata) 

659 self.assertEqual(visitInfo.getObject(), data.object) 

660 

661 metadata = propertySetFromDict({"HAS-SIMULATED-CONTENT": data.hasSimulatedContent}) 

662 visitInfo = afwImage.VisitInfo(metadata) 

663 self.assertEqual(visitInfo.getHasSimulatedContent(), data.hasSimulatedContent) 

664 

665 def testMetadataConstructorUndefined(self): 

666 """Test that we can create VisitInfo using None in metadata.""" 

667 

668 metadata = propertySetFromDict({ 

669 # These are examples of generic reader code. 

670 "PROGRAM": None, # A string should convert to "". 

671 "DARKTIME": None, # A missing float should convert to NaN. 

672 # These headers have special logic in the reader. 

673 "EXPTIME": None, 

674 "IDNUM": None, 

675 "DATE-AVG": None, 

676 "ROTTYPE": None, 

677 "HAS-SIMULATED-CONTENT": None, 

678 }) 

679 visitInfo = afwImage.VisitInfo(metadata) 

680 self.assertEqual(visitInfo.getScienceProgram(), "") 

681 self.assertTrue(math.isnan(visitInfo.getDarkTime())) 

682 self.assertTrue(math.isnan(visitInfo.getExposureTime())) 

683 self.assertEqual(visitInfo.getId(), 0) 

684 self.assertFalse(visitInfo.getDate().isValid()) 

685 

686 def testConstructorKeywordArguments(self): 

687 """Test VisitInfo with named arguments""" 

688 data = self.data1 

689 

690 visitInfo = afwImage.VisitInfo() 

691 self._testIsEmpty(visitInfo) 

692 

693 visitInfo = afwImage.VisitInfo(exposureId=data.exposureId) 

694 with self.assertWarns(FutureWarning): 

695 self.assertEqual(visitInfo.getExposureId(), data.exposureId) 

696 self.assertTrue(math.isnan(visitInfo.getExposureTime())) 

697 

698 visitInfo = afwImage.VisitInfo(exposureTime=data.exposureTime) 

699 self.assertEqual(visitInfo.getExposureTime(), data.exposureTime) 

700 

701 visitInfo = afwImage.VisitInfo(darkTime=data.darkTime) 

702 self.assertEqual(visitInfo.getDarkTime(), data.darkTime) 

703 

704 visitInfo = afwImage.VisitInfo(date=data.date) 

705 self.assertEqual(visitInfo.getDate(), data.date) 

706 

707 visitInfo = afwImage.VisitInfo(ut1=data.ut1) 

708 self.assertEqual(visitInfo.getUt1(), data.ut1) 

709 

710 visitInfo = afwImage.VisitInfo(era=data.era) 

711 self.assertEqual(visitInfo.getEra(), data.era) 

712 

713 visitInfo = afwImage.VisitInfo(boresightRaDec=data.boresightRaDec) 

714 self.assertEqual(visitInfo.getBoresightRaDec(), data.boresightRaDec) 

715 

716 visitInfo = afwImage.VisitInfo(boresightAzAlt=data.boresightAzAlt) 

717 self.assertEqual(visitInfo.getBoresightAzAlt(), data.boresightAzAlt) 

718 

719 visitInfo = afwImage.VisitInfo(boresightAirmass=data.boresightAirmass) 

720 self.assertEqual(visitInfo.getBoresightAirmass(), 

721 data.boresightAirmass) 

722 

723 visitInfo = afwImage.VisitInfo( 

724 boresightRotAngle=data.boresightRotAngle) 

725 self.assertEqual(visitInfo.getBoresightRotAngle(), 

726 data.boresightRotAngle) 

727 

728 visitInfo = afwImage.VisitInfo(rotType=data.rotType) 

729 self.assertEqual(visitInfo.getRotType(), data.rotType) 

730 

731 visitInfo = afwImage.VisitInfo(observatory=data.observatory) 

732 self.assertEqual(visitInfo.getObservatory(), data.observatory) 

733 

734 visitInfo = afwImage.VisitInfo(weather=data.weather) 

735 self.assertEqual(visitInfo.getWeather(), data.weather) 

736 

737 visitInfo = afwImage.VisitInfo(instrumentLabel=data.instrumentLabel) 

738 self.assertEqual(visitInfo.getInstrumentLabel(), data.instrumentLabel) 

739 

740 visitInfo = afwImage.VisitInfo(id=data.id) 

741 self.assertEqual(visitInfo.getId(), data.id) 

742 

743 visitInfo = afwImage.VisitInfo(focusZ=data.focusZ) 

744 self.assertEqual(visitInfo.getFocusZ(), data.focusZ) 

745 

746 visitInfo = afwImage.VisitInfo(observationType=data.observationType) 

747 self.assertEqual(visitInfo.getObservationType(), data.observationType) 

748 

749 visitInfo = afwImage.VisitInfo(scienceProgram=data.scienceProgram) 

750 self.assertEqual(visitInfo.getScienceProgram(), data.scienceProgram) 

751 

752 visitInfo = afwImage.VisitInfo(observationReason=data.observationReason) 

753 self.assertEqual(visitInfo.getObservationReason(), data.observationReason) 

754 

755 visitInfo = afwImage.VisitInfo(object=data.object) 

756 self.assertEqual(visitInfo.getObject(), data.object) 

757 

758 visitInfo = afwImage.VisitInfo(hasSimulatedContent=data.hasSimulatedContent) 

759 self.assertEqual(visitInfo.getHasSimulatedContent(), data.hasSimulatedContent) 

760 

761 def testGoodRotTypes(self): 

762 """Test round trip of all valid rot types""" 

763 for rotType in RotTypeEnumNameDict: 

764 metadata = propertySetFromDict( 

765 {"ROTTYPE": RotTypeEnumNameDict[rotType]}) 

766 visitInfo = afwImage.VisitInfo(metadata) 

767 self.assertEqual(visitInfo.getRotType(), rotType) 

768 

769 def testBadRotTypes(self): 

770 """Test that invalid rot type names cannot be used to construct a VisitInfo""" 

771 for badRotTypeName in ( 

772 "unknown", # must be all uppercase 

773 "sky", # must be all uppercase 

774 "Sky", # must be all uppercase 

775 "SKY1", # extra chars 

776 "HORIZONTAL", # extra chars 

777 ): 

778 metadata = propertySetFromDict({"ROTTYPE": badRotTypeName}) 

779 with self.assertRaises(lsst.pex.exceptions.RuntimeError): 

780 afwImage.VisitInfo(metadata) 

781 

782 def test_str(self): 

783 """Check that we get something reasonable for str()""" 

784 visitInfo = makeVisitInfo(self.data1) 

785 string = str(visitInfo) 

786 self.assertEqual(string, 

787 "VisitInfo(exposureId=10313423, exposureTime=10.01, darkTime=11.02, " 

788 "date=2037-09-20T02:24:00.000000000, UT1=12345.1, ERA=0.787143 rad, " 

789 "boresightRaDec=(23.1000000000, +73.2000000000), " 

790 "boresightAzAlt=(134.5000000000, +33.3000000000), boresightAirmass=1.73, " 

791 "boresightRotAngle=1.27758 rad, rotType=1, observatory=22.2N, 11.1E 0.333, " 

792 "weather=Weather(1.1, 2.2, 34.5), instrumentLabel='TestCameraOne', " 

793 "id=987654, focusZ=1.5, observationType='flat', scienceProgram='test program', " 

794 "observationReason='test reason', object='test object', hasSimulatedContent=false)") 

795 

796 def testParallacticAngle(self): 

797 """Check that we get the same precomputed values for parallactic angle.""" 

798 parallacticAngle = [141.39684140703142*degrees, 76.99982166973487*degrees] 

799 for item, parAngle in zip((self.data1, self.data2), parallacticAngle): 

800 visitInfo = afwImage.VisitInfo(era=item.era, 

801 boresightRaDec=item.boresightRaDec, 

802 observatory=item.observatory, 

803 ) 

804 self.assertAnglesAlmostEqual(visitInfo.getBoresightParAngle(), parAngle) 

805 

806 def testParallacticAngleNorthMeridian(self): 

807 """An observation on the Meridian that is North of zenith has a parallactic angle of pi radians.""" 

808 meridianBoresightRA = self.data1.era + self.data1.observatory.getLongitude() 

809 northBoresightDec = self.data1.observatory.getLatitude() + 10.*degrees 

810 visitInfo = afwImage.VisitInfo(era=self.data1.era, 

811 boresightRaDec=SpherePoint(meridianBoresightRA, 

812 northBoresightDec), 

813 observatory=self.data1.observatory, 

814 ) 

815 self.assertAnglesAlmostEqual(visitInfo.getBoresightParAngle(), Angle(np.pi)) 

816 

817 def testParallacticAngleSouthMeridian(self): 

818 """An observation on the Meridian that is South of zenith has a parallactic angle of zero.""" 

819 meridianBoresightRA = self.data1.era + self.data1.observatory.getLongitude() 

820 southBoresightDec = self.data1.observatory.getLatitude() - 10.*degrees 

821 visitInfo = afwImage.VisitInfo(era=self.data1.era, 

822 boresightRaDec=SpherePoint(meridianBoresightRA, 

823 southBoresightDec), 

824 observatory=self.data1.observatory, 

825 ) 

826 self.assertAnglesAlmostEqual(visitInfo.getBoresightParAngle(), Angle(0.)) 

827 

828 

829def setup_module(module): 

830 lsst.utils.tests.init() 

831 

832 

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

834 pass 

835 

836 

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

838 lsst.utils.tests.init() 

839 unittest.main()