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
« 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
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
35RotTypeEnumNameDict = {
36 afwImage.RotType.UNKNOWN: "UNKNOWN",
37 afwImage.RotType.SKY: "SKY",
38 afwImage.RotType.HORIZON: "HORIZON",
39 afwImage.RotType.MOUNT: "MOUNT",
40}
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
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 )
77class VisitInfoTestCase(lsst.utils.tests.TestCase):
78 """Test lsst.afw.image.VisitInfo, a simple struct-like class"""
80 def setUp(self):
81 self.testDir = os.path.dirname(__file__)
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
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)
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)
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)
223 def testValueConstructor_data1(self):
224 self._testValueConstructor(self.data1, self.localEra1, self.hourAngle1)
225 self._testProperties(self.data1, self.localEra1, self.hourAngle1)
227 def testValueConstructor_data2(self):
228 self._testValueConstructor(self.data2, self.localEra2, self.hourAngle2)
229 self._testProperties(self.data2, self.localEra2, self.hourAngle2)
231 def testCopyWith(self):
232 visitInfo1 = makeVisitInfo(self.data1)
233 visitInfo2 = makeVisitInfo(self.data2)
235 updateFields1 = [
236 "exposureTime",
237 "darkTime",
238 "date",
239 "ut1",
240 "era",
241 "boresightRaDec",
242 "boresightAzAlt",
243 "boresightAirmass",
244 "boresightRotAngle",
245 ]
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 ]
261 kwargs1 = {k: getattr(visitInfo2, k) for k in updateFields1}
262 kwargs2 = {k: getattr(visitInfo2, k) for k in updateFields2}
264 newVisit1 = visitInfo1.copyWith(**kwargs1)
265 newVisit2 = visitInfo1.copyWith(**kwargs2)
267 for field in updateFields1:
268 self.assertEqual(getattr(newVisit1, field), getattr(visitInfo2, field))
269 self.assertEqual(getattr(newVisit2, field), getattr(visitInfo1, field))
271 for field in updateFields2:
272 self.assertEqual(getattr(newVisit1, field), getattr(visitInfo1, field))
273 self.assertEqual(getattr(newVisit2, field), getattr(visitInfo2, field))
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)
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)
294 def _testFitsRead(self, data, filePath, version):
295 """Test that old VersionInfo files are read correctly.
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)
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)
351 def testPersistenceVersions(self):
352 """Test that older versions are handled appropriately.
353 """
354 dataDir = os.path.join(os.path.dirname(__file__), "data")
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)
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"))
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)
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)
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)
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)
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())
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)
502 # commutativity and transitivity difficult to test with this setup
504 def testMetadataConstructor(self):
505 """Test the metadata constructor
507 This constructor allows missing values
508 """
509 data = self.data1
511 metadata = propertySetFromDict({})
512 visitInfo = afwImage.VisitInfo(metadata)
513 self._testIsEmpty(visitInfo)
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()))
521 metadata = propertySetFromDict({"EXPTIME": data.exposureTime})
522 visitInfo = afwImage.VisitInfo(metadata)
523 self.assertEqual(visitInfo.getExposureTime(), data.exposureTime)
525 metadata = propertySetFromDict({"DARKTIME": data.darkTime})
526 visitInfo = afwImage.VisitInfo(metadata)
527 self.assertEqual(visitInfo.getDarkTime(), data.darkTime)
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)
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)
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)
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)
559 metadata = propertySetFromDict({"MJD-AVG-UT1": data.ut1})
560 visitInfo = afwImage.VisitInfo(metadata)
561 self.assertEqual(visitInfo.getUt1(), data.ut1)
563 metadata = propertySetFromDict({"AVG-ERA": data.era.asDegrees()})
564 visitInfo = afwImage.VisitInfo(metadata)
565 self.assertEqual(visitInfo.getEra(), data.era)
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])
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])
581 metadata = propertySetFromDict({"BORE-AIRMASS": data.boresightAirmass})
582 visitInfo = afwImage.VisitInfo(metadata)
583 self.assertEqual(visitInfo.getBoresightAirmass(),
584 data.boresightAirmass)
586 metadata = propertySetFromDict(
587 {"BORE-ROTANG": data.boresightRotAngle.asDegrees()})
588 visitInfo = afwImage.VisitInfo(metadata)
589 self.assertEqual(visitInfo.getBoresightRotAngle(),
590 data.boresightRotAngle)
592 metadata = propertySetFromDict(
593 {"ROTTYPE": RotTypeEnumNameDict[data.rotType]})
594 visitInfo = afwImage.VisitInfo(metadata)
595 self.assertEqual(visitInfo.getRotType(), data.rotType)
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())
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())
609 metadata = propertySetFromDict(
610 {"OBS-ELEV": data.observatory.getElevation()})
611 visitInfo = afwImage.VisitInfo(metadata)
612 self.assertEqual(visitInfo.getObservatory().getElevation(),
613 data.observatory.getElevation())
615 metadata = propertySetFromDict(
616 {"AIRTEMP": data.weather.getAirTemperature()})
617 visitInfo = afwImage.VisitInfo(metadata)
618 self.assertEqual(visitInfo.getWeather().getAirTemperature(),
619 data.weather.getAirTemperature())
621 metadata = propertySetFromDict(
622 {"AIRPRESS": data.weather.getAirPressure()})
623 visitInfo = afwImage.VisitInfo(metadata)
624 self.assertEqual(visitInfo.getWeather().getAirPressure(),
625 data.weather.getAirPressure())
627 metadata = propertySetFromDict(
628 {"HUMIDITY": data.weather.getHumidity()})
629 visitInfo = afwImage.VisitInfo(metadata)
630 self.assertEqual(visitInfo.getWeather().getHumidity(),
631 data.weather.getHumidity())
633 metadata = propertySetFromDict({"INSTRUMENT": data.instrumentLabel})
634 visitInfo = afwImage.VisitInfo(metadata)
635 self.assertEqual(visitInfo.getInstrumentLabel(), data.instrumentLabel)
637 metadata = propertySetFromDict({"IDNUM": data.id})
638 visitInfo = afwImage.VisitInfo(metadata)
639 self.assertEqual(visitInfo.getId(), data.id)
641 metadata = propertySetFromDict({"FOCUSZ": data.focusZ})
642 visitInfo = afwImage.VisitInfo(metadata)
643 self.assertEqual(visitInfo.getFocusZ(), data.focusZ)
645 metadata = propertySetFromDict({"OBSTYPE": data.observationType})
646 visitInfo = afwImage.VisitInfo(metadata)
647 self.assertEqual(visitInfo.getObservationType(), data.observationType)
649 metadata = propertySetFromDict({"PROGRAM": data.scienceProgram})
650 visitInfo = afwImage.VisitInfo(metadata)
651 self.assertEqual(visitInfo.getScienceProgram(), data.scienceProgram)
653 metadata = propertySetFromDict({"REASON": data.observationReason})
654 visitInfo = afwImage.VisitInfo(metadata)
655 self.assertEqual(visitInfo.getObservationReason(), data.observationReason)
657 metadata = propertySetFromDict({"OBJECT": data.object})
658 visitInfo = afwImage.VisitInfo(metadata)
659 self.assertEqual(visitInfo.getObject(), data.object)
661 metadata = propertySetFromDict({"HAS-SIMULATED-CONTENT": data.hasSimulatedContent})
662 visitInfo = afwImage.VisitInfo(metadata)
663 self.assertEqual(visitInfo.getHasSimulatedContent(), data.hasSimulatedContent)
665 def testMetadataConstructorUndefined(self):
666 """Test that we can create VisitInfo using None in metadata."""
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())
686 def testConstructorKeywordArguments(self):
687 """Test VisitInfo with named arguments"""
688 data = self.data1
690 visitInfo = afwImage.VisitInfo()
691 self._testIsEmpty(visitInfo)
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()))
698 visitInfo = afwImage.VisitInfo(exposureTime=data.exposureTime)
699 self.assertEqual(visitInfo.getExposureTime(), data.exposureTime)
701 visitInfo = afwImage.VisitInfo(darkTime=data.darkTime)
702 self.assertEqual(visitInfo.getDarkTime(), data.darkTime)
704 visitInfo = afwImage.VisitInfo(date=data.date)
705 self.assertEqual(visitInfo.getDate(), data.date)
707 visitInfo = afwImage.VisitInfo(ut1=data.ut1)
708 self.assertEqual(visitInfo.getUt1(), data.ut1)
710 visitInfo = afwImage.VisitInfo(era=data.era)
711 self.assertEqual(visitInfo.getEra(), data.era)
713 visitInfo = afwImage.VisitInfo(boresightRaDec=data.boresightRaDec)
714 self.assertEqual(visitInfo.getBoresightRaDec(), data.boresightRaDec)
716 visitInfo = afwImage.VisitInfo(boresightAzAlt=data.boresightAzAlt)
717 self.assertEqual(visitInfo.getBoresightAzAlt(), data.boresightAzAlt)
719 visitInfo = afwImage.VisitInfo(boresightAirmass=data.boresightAirmass)
720 self.assertEqual(visitInfo.getBoresightAirmass(),
721 data.boresightAirmass)
723 visitInfo = afwImage.VisitInfo(
724 boresightRotAngle=data.boresightRotAngle)
725 self.assertEqual(visitInfo.getBoresightRotAngle(),
726 data.boresightRotAngle)
728 visitInfo = afwImage.VisitInfo(rotType=data.rotType)
729 self.assertEqual(visitInfo.getRotType(), data.rotType)
731 visitInfo = afwImage.VisitInfo(observatory=data.observatory)
732 self.assertEqual(visitInfo.getObservatory(), data.observatory)
734 visitInfo = afwImage.VisitInfo(weather=data.weather)
735 self.assertEqual(visitInfo.getWeather(), data.weather)
737 visitInfo = afwImage.VisitInfo(instrumentLabel=data.instrumentLabel)
738 self.assertEqual(visitInfo.getInstrumentLabel(), data.instrumentLabel)
740 visitInfo = afwImage.VisitInfo(id=data.id)
741 self.assertEqual(visitInfo.getId(), data.id)
743 visitInfo = afwImage.VisitInfo(focusZ=data.focusZ)
744 self.assertEqual(visitInfo.getFocusZ(), data.focusZ)
746 visitInfo = afwImage.VisitInfo(observationType=data.observationType)
747 self.assertEqual(visitInfo.getObservationType(), data.observationType)
749 visitInfo = afwImage.VisitInfo(scienceProgram=data.scienceProgram)
750 self.assertEqual(visitInfo.getScienceProgram(), data.scienceProgram)
752 visitInfo = afwImage.VisitInfo(observationReason=data.observationReason)
753 self.assertEqual(visitInfo.getObservationReason(), data.observationReason)
755 visitInfo = afwImage.VisitInfo(object=data.object)
756 self.assertEqual(visitInfo.getObject(), data.object)
758 visitInfo = afwImage.VisitInfo(hasSimulatedContent=data.hasSimulatedContent)
759 self.assertEqual(visitInfo.getHasSimulatedContent(), data.hasSimulatedContent)
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)
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)
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)")
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)
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))
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.))
829def setup_module(module):
830 lsst.utils.tests.init()
833class MemoryTester(lsst.utils.tests.MemoryTestCase):
834 pass
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()