Coverage for tests/test_visitInfo.py: 9%
441 statements
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-24 02:29 -0700
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-24 02:29 -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 # These both warn because exposureId is not passed in.
265 with self.assertWarns(FutureWarning):
266 newVisit1 = visitInfo1.copyWith(**kwargs1)
267 newVisit2 = visitInfo1.copyWith(**kwargs2)
269 for field in updateFields1:
270 self.assertEqual(getattr(newVisit1, field), getattr(visitInfo2, field))
271 self.assertEqual(getattr(newVisit2, field), getattr(visitInfo1, field))
273 for field in updateFields2:
274 self.assertEqual(getattr(newVisit1, field), getattr(visitInfo1, field))
275 self.assertEqual(getattr(newVisit2, field), getattr(visitInfo2, field))
277 # Test the deprecated exposureId.
278 # This code can be removed with DM-32138.
279 deprecatedVisit = visitInfo1.copyWith(exposureId=3)
280 with self.assertWarns(FutureWarning):
281 self.assertEqual(deprecatedVisit.getExposureId(), 3)
282 with self.assertWarns(FutureWarning):
283 deprecatedCopy = deprecatedVisit.copyWith(**kwargs1)
284 self.assertEqual(deprecatedCopy.getExposureId(), 3)
286 def testTablePersistence(self):
287 """Test that VisitInfo can be round-tripped with current code.
288 """
289 for item in (self.data1, self.data2):
290 tablePath = os.path.join(
291 self.testDir, "testVisitInfo_testTablePersistence.fits")
292 v1 = afwImage.VisitInfo(*item)
293 v1.writeFits(tablePath)
294 v2 = afwImage.VisitInfo.readFits(tablePath)
295 self.assertEqual(v1, v2)
296 os.unlink(tablePath)
298 def _testFitsRead(self, data, filePath, version):
299 """Test that old VersionInfo files are read correctly.
301 Parameters
302 ----------
303 data : `VisitInfoData`
304 The values expected to be stored in the file, or a
305 superset thereof.
306 filePath : `str`
307 The file to test.
308 version : `int`
309 The VersionInfo persistence format used in ``filePath``.
310 """
311 visitInfo = afwImage.VisitInfo.readFits(filePath)
313 if version >= 0:
314 with self.assertWarns(FutureWarning):
315 self.assertEqual(visitInfo.getExposureId(), data.exposureId)
316 self.assertEqual(visitInfo.getExposureTime(), data.exposureTime)
317 self.assertEqual(visitInfo.getDarkTime(), data.darkTime)
318 self.assertEqual(visitInfo.getDate(), data.date)
319 self.assertEqual(visitInfo.getUt1(), data.ut1)
320 self.assertEqual(visitInfo.getEra(), data.era)
321 self.assertEqual(visitInfo.getBoresightRaDec(), data.boresightRaDec)
322 self.assertEqual(visitInfo.getBoresightAzAlt(), data.boresightAzAlt)
323 self.assertEqual(visitInfo.getBoresightAirmass(),
324 data.boresightAirmass)
325 self.assertEqual(visitInfo.getBoresightRotAngle(),
326 data.boresightRotAngle)
327 self.assertEqual(visitInfo.getRotType(), data.rotType)
328 self.assertEqual(visitInfo.getObservatory(), data.observatory)
329 self.assertEqual(visitInfo.getWeather(), data.weather)
330 if version >= 1:
331 self.assertEqual(visitInfo.getInstrumentLabel(), data.instrumentLabel)
332 else:
333 self.assertEqual(visitInfo.getInstrumentLabel(), "")
334 if version >= 2:
335 self.assertEqual(visitInfo.getId(), data.id)
336 else:
337 self.assertEqual(visitInfo.getId(), 0)
338 if version >= 3:
339 self.assertEqual(visitInfo.getFocusZ(), data.focusZ)
340 else:
341 self.assertTrue(math.isnan(visitInfo.getFocusZ()))
342 if version >= 4:
343 self.assertEqual(visitInfo.getObservationType(), data.observationType)
344 self.assertEqual(visitInfo.getScienceProgram(), data.scienceProgram)
345 self.assertEqual(visitInfo.getObservationReason(), data.observationReason)
346 self.assertEqual(visitInfo.getObject(), data.object)
347 self.assertEqual(visitInfo.getHasSimulatedContent(), data.hasSimulatedContent)
348 else:
349 self.assertEqual(visitInfo.getObservationType(), "")
350 self.assertEqual(visitInfo.getScienceProgram(), "")
351 self.assertEqual(visitInfo.getObservationReason(), "")
352 self.assertEqual(visitInfo.getObject(), "")
353 self.assertEqual(visitInfo.getHasSimulatedContent(), False)
355 def testPersistenceVersions(self):
356 """Test that older versions are handled appropriately.
357 """
358 dataDir = os.path.join(os.path.dirname(__file__), "data")
360 # All files created by makeVisitInfo(self.data1).writeFits()
361 self._testFitsRead(self.data1, os.path.join(dataDir, "visitInfo-noversion.fits"), 0)
362 self._testFitsRead(self.data1, os.path.join(dataDir, "visitInfo-version-1.fits"), 1)
363 self._testFitsRead(self.data1, os.path.join(dataDir, "visitInfo-version-2.fits"), 2)
364 self._testFitsRead(self.data1, os.path.join(dataDir, "visitInfo-version-3.fits"), 3)
365 self._testFitsRead(self.data1, os.path.join(dataDir, "visitInfo-version-4.fits"), 4)
367 # Check that reading a newer file raises a useful exception.
368 with self.assertRaisesRegex(TypeError,
369 r"Cannot read VisitInfo FITS version > [0-9]+, found version 999999"):
370 afwImage.VisitInfo.readFits(os.path.join(dataDir, "visitInfo-version-999999.fits"))
372 def testSetVisitInfoMetadata(self):
373 for item in (self.data1, self.data2):
374 visitInfo = makeVisitInfo(item)
375 metadata = PropertyList()
376 afwImage.setVisitInfoMetadata(metadata, visitInfo)
377 self.assertEqual(metadata.nameCount(), 28)
378 self.assertEqual(metadata.getScalar("EXPID"), item.exposureId)
379 self.assertEqual(metadata.getScalar("EXPTIME"), item.exposureTime)
380 self.assertEqual(metadata.getScalar("DARKTIME"), item.darkTime)
381 self.assertEqual(metadata.getScalar("DATE-AVG"),
382 item.date.toString(DateTime.TAI))
383 self.assertEqual(metadata.getScalar("TIMESYS"), "TAI")
384 self.assertEqual(metadata.getScalar("MJD-AVG-UT1"), item.ut1)
385 self.assertEqual(metadata.getScalar("AVG-ERA"), item.era.asDegrees())
386 self.assertEqual(metadata.getScalar("BORE-RA"),
387 item.boresightRaDec[0].asDegrees())
388 self.assertEqual(metadata.getScalar("BORE-DEC"),
389 item.boresightRaDec[1].asDegrees())
390 self.assertEqual(metadata.getScalar("BORE-AZ"),
391 item.boresightAzAlt[0].asDegrees())
392 self.assertEqual(metadata.getScalar("BORE-ALT"),
393 item.boresightAzAlt[1].asDegrees())
394 self.assertEqual(metadata.getScalar("BORE-AIRMASS"),
395 item.boresightAirmass)
396 self.assertEqual(metadata.getScalar("BORE-ROTANG"),
397 item.boresightRotAngle.asDegrees())
398 self.assertEqual(metadata.getScalar("ROTTYPE"),
399 RotTypeEnumNameDict[item.rotType])
400 self.assertEqual(metadata.getScalar("OBS-LONG"),
401 item.observatory.getLongitude().asDegrees())
402 self.assertEqual(metadata.getScalar("OBS-LAT"),
403 item.observatory.getLatitude().asDegrees())
404 self.assertEqual(metadata.getScalar("OBS-ELEV"),
405 item.observatory.getElevation())
406 self.assertEqual(metadata.getScalar("AIRTEMP"),
407 item.weather.getAirTemperature())
408 self.assertEqual(metadata.getScalar("AIRPRESS"),
409 item.weather.getAirPressure())
410 self.assertEqual(metadata.getScalar("HUMIDITY"),
411 item.weather.getHumidity())
412 self.assertEqual(metadata.getScalar("INSTRUMENT"),
413 item.instrumentLabel)
414 self.assertEqual(metadata.getScalar("IDNUM"),
415 item.id)
416 self.assertEqual(metadata.getScalar("FOCUSZ"),
417 item.focusZ)
418 self.assertEqual(metadata.getScalar("OBSTYPE"),
419 item.observationType)
420 self.assertEqual(metadata.getScalar("PROGRAM"),
421 item.scienceProgram)
422 self.assertEqual(metadata.getScalar("REASON"),
423 item.observationReason)
424 self.assertEqual(metadata.getScalar("OBJECT"),
425 item.object)
426 self.assertEqual(metadata.getScalar("HAS-SIMULATED-CONTENT"),
427 item.hasSimulatedContent)
429 def testSetVisitInfoMetadataMissingValues(self):
430 """If a value is unknown then it should not be written to the metadata"""
431 # Only rot type and hasSimulatedContent are "known" when default-
432 # constructed, by virtue of having no "null" state.
433 visitInfo = afwImage.VisitInfo()
434 metadata = PropertyList()
435 afwImage.setVisitInfoMetadata(metadata, visitInfo)
436 self.assertEqual(metadata.getScalar("ROTTYPE"),
437 RotTypeEnumNameDict[afwImage.RotType.UNKNOWN])
438 self.assertEqual(metadata.getScalar("HAS-SIMULATED-CONTENT"), False)
439 self.assertEqual(metadata.nameCount(), 2)
441 def testStripVisitInfoKeywords(self):
442 for argList in (self.data1, self.data2):
443 visitInfo = afwImage.VisitInfo(*argList)
444 metadata = PropertyList()
445 afwImage.setVisitInfoMetadata(metadata, visitInfo)
446 # add an extra keyword that will not be stripped
447 metadata.set("EXTRA", 5)
448 self.assertEqual(metadata.nameCount(), 29)
449 afwImage.stripVisitInfoKeywords(metadata)
450 self.assertEqual(metadata.nameCount(), 1)
452 def _testIsEmpty(self, visitInfo):
453 """Test that visitInfo is all NaN, 0, or empty string, as appropriate.
454 """
455 with self.assertWarns(FutureWarning):
456 self.assertEqual(visitInfo.getExposureId(), 0)
457 self.assertTrue(math.isnan(visitInfo.getExposureTime()))
458 self.assertTrue(math.isnan(visitInfo.getDarkTime()))
459 self.assertEqual(visitInfo.getDate(), DateTime())
460 self.assertTrue(math.isnan(visitInfo.getUt1()))
461 self.assertTrue(math.isnan(visitInfo.getEra().asDegrees()))
462 for i in range(2):
463 self.assertTrue(math.isnan(
464 visitInfo.getBoresightRaDec()[i].asDegrees()))
465 self.assertTrue(math.isnan(
466 visitInfo.getBoresightAzAlt()[i].asDegrees()))
467 self.assertTrue(math.isnan(visitInfo.getBoresightAirmass()))
468 self.assertTrue(math.isnan(
469 visitInfo.getBoresightRotAngle().asDegrees()))
470 self.assertEqual(visitInfo.getRotType(), afwImage.RotType.UNKNOWN)
471 self.assertTrue(math.isnan(
472 visitInfo.getObservatory().getLongitude().asDegrees()))
473 self.assertTrue(math.isnan(
474 visitInfo.getObservatory().getLatitude().asDegrees()))
475 self.assertTrue(math.isnan(visitInfo.getObservatory().getElevation()))
476 self.assertTrue(math.isnan(visitInfo.getWeather().getAirTemperature()))
477 self.assertTrue(math.isnan(visitInfo.getWeather().getAirPressure()))
478 self.assertTrue(math.isnan(visitInfo.getWeather().getHumidity()))
479 self.assertTrue(math.isnan(visitInfo.getBoresightHourAngle()))
480 self.assertEqual(visitInfo.getInstrumentLabel(), "")
481 self.assertEqual(visitInfo.getId(), 0)
482 self.assertTrue(math.isnan(visitInfo.getFocusZ()))
483 self.assertEqual(visitInfo.getObservationType(), "")
484 self.assertEqual(visitInfo.getScienceProgram(), "")
485 self.assertEqual(visitInfo.getObservationReason(), "")
486 self.assertEqual(visitInfo.getObject(), "")
487 self.assertEqual(visitInfo.getHasSimulatedContent(), False)
489 def testEquals(self):
490 """Test that identical VisitInfo objects compare equal, even if some fields are NaN.
491 """
492 # objects with "equal state" should be equal
493 self.assertEqual(makeVisitInfo(self.data1), makeVisitInfo(self.data1))
494 self.assertEqual(makeVisitInfo(self.data2), makeVisitInfo(self.data2))
495 self.assertNotEqual(makeVisitInfo(self.data1), makeVisitInfo(self.data2))
496 self.assertEqual(afwImage.VisitInfo(), afwImage.VisitInfo())
498 # equality must be reflexive
499 info = makeVisitInfo(self.data1)
500 self.assertEqual(info, info)
501 info = makeVisitInfo(self.data2)
502 self.assertEqual(info, info)
503 info = afwImage.VisitInfo()
504 self.assertEqual(info, info)
506 # commutativity and transitivity difficult to test with this setup
508 def testMetadataConstructor(self):
509 """Test the metadata constructor
511 This constructor allows missing values
512 """
513 data = self.data1
515 metadata = propertySetFromDict({})
516 visitInfo = afwImage.VisitInfo(metadata)
517 self._testIsEmpty(visitInfo)
519 metadata = propertySetFromDict({"EXPID": data.exposureId})
520 visitInfo = afwImage.VisitInfo(metadata)
521 with self.assertWarns(FutureWarning):
522 self.assertEqual(visitInfo.getExposureId(), data.exposureId)
523 self.assertTrue(math.isnan(visitInfo.getExposureTime()))
525 metadata = propertySetFromDict({"EXPTIME": data.exposureTime})
526 visitInfo = afwImage.VisitInfo(metadata)
527 self.assertEqual(visitInfo.getExposureTime(), data.exposureTime)
529 metadata = propertySetFromDict({"DARKTIME": data.darkTime})
530 visitInfo = afwImage.VisitInfo(metadata)
531 self.assertEqual(visitInfo.getDarkTime(), data.darkTime)
533 metadata = propertySetFromDict(
534 {"DATE-AVG": data.date.toString(DateTime.TAI), "TIMESYS": "TAI"})
535 visitInfo = afwImage.VisitInfo(metadata)
536 self.assertEqual(visitInfo.getDate(), data.date)
538 # TIME-MID in UTC is an acceptable alternative to DATE-AVG
539 metadata = propertySetFromDict(
540 {"TIME-MID": data.date.toString(DateTime.UTC)})
541 visitInfo = afwImage.VisitInfo(metadata)
542 self.assertEqual(visitInfo.getDate(), data.date)
544 # TIME-MID must be in UTC and TIMESYS is ignored
545 metadata = propertySetFromDict({
546 "TIME-MID": data.date.toString(DateTime.TAI) + "Z",
547 "TIMESYS": "TAI",
548 })
549 visitInfo = afwImage.VisitInfo(metadata)
550 self.assertNotEqual(visitInfo.getDate(), data.date)
552 # if both DATE-AVG and TIME-MID provided then use DATE-AVG
553 # use the wrong time system for TIME-MID so if it is used, an error
554 # will result
555 metadata = propertySetFromDict({
556 "DATE-AVG": data.date.toString(DateTime.TAI),
557 "TIMESYS": "TAI",
558 "TIME-MID": data.date.toString(DateTime.TAI) + "Z",
559 })
560 visitInfo = afwImage.VisitInfo(metadata)
561 self.assertEqual(visitInfo.getDate(), data.date)
563 metadata = propertySetFromDict({"MJD-AVG-UT1": data.ut1})
564 visitInfo = afwImage.VisitInfo(metadata)
565 self.assertEqual(visitInfo.getUt1(), data.ut1)
567 metadata = propertySetFromDict({"AVG-ERA": data.era.asDegrees()})
568 visitInfo = afwImage.VisitInfo(metadata)
569 self.assertEqual(visitInfo.getEra(), data.era)
571 for i, key in enumerate(("BORE-RA", "BORE-DEC")):
572 metadata = propertySetFromDict(
573 {key: data.boresightRaDec[i].asDegrees()})
574 visitInfo = afwImage.VisitInfo(metadata)
575 self.assertEqual(visitInfo.getBoresightRaDec()
576 [i], data.boresightRaDec[i])
578 for i, key in enumerate(("BORE-AZ", "BORE-ALT")):
579 metadata = propertySetFromDict(
580 {key: data.boresightAzAlt[i].asDegrees()})
581 visitInfo = afwImage.VisitInfo(metadata)
582 self.assertEqual(visitInfo.getBoresightAzAlt()
583 [i], data.boresightAzAlt[i])
585 metadata = propertySetFromDict({"BORE-AIRMASS": data.boresightAirmass})
586 visitInfo = afwImage.VisitInfo(metadata)
587 self.assertEqual(visitInfo.getBoresightAirmass(),
588 data.boresightAirmass)
590 metadata = propertySetFromDict(
591 {"BORE-ROTANG": data.boresightRotAngle.asDegrees()})
592 visitInfo = afwImage.VisitInfo(metadata)
593 self.assertEqual(visitInfo.getBoresightRotAngle(),
594 data.boresightRotAngle)
596 metadata = propertySetFromDict(
597 {"ROTTYPE": RotTypeEnumNameDict[data.rotType]})
598 visitInfo = afwImage.VisitInfo(metadata)
599 self.assertEqual(visitInfo.getRotType(), data.rotType)
601 metadata = propertySetFromDict(
602 {"OBS-LONG": data.observatory.getLongitude().asDegrees()})
603 visitInfo = afwImage.VisitInfo(metadata)
604 self.assertEqual(visitInfo.getObservatory().getLongitude(),
605 data.observatory.getLongitude())
607 metadata = propertySetFromDict(
608 {"OBS-LAT": data.observatory.getLatitude().asDegrees()})
609 visitInfo = afwImage.VisitInfo(metadata)
610 self.assertEqual(visitInfo.getObservatory().getLatitude(),
611 data.observatory.getLatitude())
613 metadata = propertySetFromDict(
614 {"OBS-ELEV": data.observatory.getElevation()})
615 visitInfo = afwImage.VisitInfo(metadata)
616 self.assertEqual(visitInfo.getObservatory().getElevation(),
617 data.observatory.getElevation())
619 metadata = propertySetFromDict(
620 {"AIRTEMP": data.weather.getAirTemperature()})
621 visitInfo = afwImage.VisitInfo(metadata)
622 self.assertEqual(visitInfo.getWeather().getAirTemperature(),
623 data.weather.getAirTemperature())
625 metadata = propertySetFromDict(
626 {"AIRPRESS": data.weather.getAirPressure()})
627 visitInfo = afwImage.VisitInfo(metadata)
628 self.assertEqual(visitInfo.getWeather().getAirPressure(),
629 data.weather.getAirPressure())
631 metadata = propertySetFromDict(
632 {"HUMIDITY": data.weather.getHumidity()})
633 visitInfo = afwImage.VisitInfo(metadata)
634 self.assertEqual(visitInfo.getWeather().getHumidity(),
635 data.weather.getHumidity())
637 metadata = propertySetFromDict({"INSTRUMENT": data.instrumentLabel})
638 visitInfo = afwImage.VisitInfo(metadata)
639 self.assertEqual(visitInfo.getInstrumentLabel(), data.instrumentLabel)
641 metadata = propertySetFromDict({"IDNUM": data.id})
642 visitInfo = afwImage.VisitInfo(metadata)
643 self.assertEqual(visitInfo.getId(), data.id)
645 metadata = propertySetFromDict({"FOCUSZ": data.focusZ})
646 visitInfo = afwImage.VisitInfo(metadata)
647 self.assertEqual(visitInfo.getFocusZ(), data.focusZ)
649 metadata = propertySetFromDict({"OBSTYPE": data.observationType})
650 visitInfo = afwImage.VisitInfo(metadata)
651 self.assertEqual(visitInfo.getObservationType(), data.observationType)
653 metadata = propertySetFromDict({"PROGRAM": data.scienceProgram})
654 visitInfo = afwImage.VisitInfo(metadata)
655 self.assertEqual(visitInfo.getScienceProgram(), data.scienceProgram)
657 metadata = propertySetFromDict({"REASON": data.observationReason})
658 visitInfo = afwImage.VisitInfo(metadata)
659 self.assertEqual(visitInfo.getObservationReason(), data.observationReason)
661 metadata = propertySetFromDict({"OBJECT": data.object})
662 visitInfo = afwImage.VisitInfo(metadata)
663 self.assertEqual(visitInfo.getObject(), data.object)
665 metadata = propertySetFromDict({"HAS-SIMULATED-CONTENT": data.hasSimulatedContent})
666 visitInfo = afwImage.VisitInfo(metadata)
667 self.assertEqual(visitInfo.getHasSimulatedContent(), data.hasSimulatedContent)
669 def testMetadataConstructorUndefined(self):
670 """Test that we can create VisitInfo using None in metadata."""
672 metadata = propertySetFromDict({
673 # These are examples of generic reader code.
674 "PROGRAM": None, # A string should convert to "".
675 "DARKTIME": None, # A missing float should convert to NaN.
676 # These headers have special logic in the reader.
677 "EXPTIME": None,
678 "IDNUM": None,
679 "DATE-AVG": None,
680 "ROTTYPE": None,
681 "HAS-SIMULATED-CONTENT": None,
682 })
683 visitInfo = afwImage.VisitInfo(metadata)
684 self.assertEqual(visitInfo.getScienceProgram(), "")
685 self.assertTrue(math.isnan(visitInfo.getDarkTime()))
686 self.assertTrue(math.isnan(visitInfo.getExposureTime()))
687 self.assertEqual(visitInfo.getId(), 0)
688 self.assertFalse(visitInfo.getDate().isValid())
690 def testConstructorKeywordArguments(self):
691 """Test VisitInfo with named arguments"""
692 data = self.data1
694 visitInfo = afwImage.VisitInfo()
695 self._testIsEmpty(visitInfo)
697 visitInfo = afwImage.VisitInfo(exposureId=data.exposureId)
698 with self.assertWarns(FutureWarning):
699 self.assertEqual(visitInfo.getExposureId(), data.exposureId)
700 self.assertTrue(math.isnan(visitInfo.getExposureTime()))
702 visitInfo = afwImage.VisitInfo(exposureTime=data.exposureTime)
703 self.assertEqual(visitInfo.getExposureTime(), data.exposureTime)
705 visitInfo = afwImage.VisitInfo(darkTime=data.darkTime)
706 self.assertEqual(visitInfo.getDarkTime(), data.darkTime)
708 visitInfo = afwImage.VisitInfo(date=data.date)
709 self.assertEqual(visitInfo.getDate(), data.date)
711 visitInfo = afwImage.VisitInfo(ut1=data.ut1)
712 self.assertEqual(visitInfo.getUt1(), data.ut1)
714 visitInfo = afwImage.VisitInfo(era=data.era)
715 self.assertEqual(visitInfo.getEra(), data.era)
717 visitInfo = afwImage.VisitInfo(boresightRaDec=data.boresightRaDec)
718 self.assertEqual(visitInfo.getBoresightRaDec(), data.boresightRaDec)
720 visitInfo = afwImage.VisitInfo(boresightAzAlt=data.boresightAzAlt)
721 self.assertEqual(visitInfo.getBoresightAzAlt(), data.boresightAzAlt)
723 visitInfo = afwImage.VisitInfo(boresightAirmass=data.boresightAirmass)
724 self.assertEqual(visitInfo.getBoresightAirmass(),
725 data.boresightAirmass)
727 visitInfo = afwImage.VisitInfo(
728 boresightRotAngle=data.boresightRotAngle)
729 self.assertEqual(visitInfo.getBoresightRotAngle(),
730 data.boresightRotAngle)
732 visitInfo = afwImage.VisitInfo(rotType=data.rotType)
733 self.assertEqual(visitInfo.getRotType(), data.rotType)
735 visitInfo = afwImage.VisitInfo(observatory=data.observatory)
736 self.assertEqual(visitInfo.getObservatory(), data.observatory)
738 visitInfo = afwImage.VisitInfo(weather=data.weather)
739 self.assertEqual(visitInfo.getWeather(), data.weather)
741 visitInfo = afwImage.VisitInfo(instrumentLabel=data.instrumentLabel)
742 self.assertEqual(visitInfo.getInstrumentLabel(), data.instrumentLabel)
744 visitInfo = afwImage.VisitInfo(id=data.id)
745 self.assertEqual(visitInfo.getId(), data.id)
747 visitInfo = afwImage.VisitInfo(focusZ=data.focusZ)
748 self.assertEqual(visitInfo.getFocusZ(), data.focusZ)
750 visitInfo = afwImage.VisitInfo(observationType=data.observationType)
751 self.assertEqual(visitInfo.getObservationType(), data.observationType)
753 visitInfo = afwImage.VisitInfo(scienceProgram=data.scienceProgram)
754 self.assertEqual(visitInfo.getScienceProgram(), data.scienceProgram)
756 visitInfo = afwImage.VisitInfo(observationReason=data.observationReason)
757 self.assertEqual(visitInfo.getObservationReason(), data.observationReason)
759 visitInfo = afwImage.VisitInfo(object=data.object)
760 self.assertEqual(visitInfo.getObject(), data.object)
762 visitInfo = afwImage.VisitInfo(hasSimulatedContent=data.hasSimulatedContent)
763 self.assertEqual(visitInfo.getHasSimulatedContent(), data.hasSimulatedContent)
765 def testGoodRotTypes(self):
766 """Test round trip of all valid rot types"""
767 for rotType in RotTypeEnumNameDict:
768 metadata = propertySetFromDict(
769 {"ROTTYPE": RotTypeEnumNameDict[rotType]})
770 visitInfo = afwImage.VisitInfo(metadata)
771 self.assertEqual(visitInfo.getRotType(), rotType)
773 def testBadRotTypes(self):
774 """Test that invalid rot type names cannot be used to construct a VisitInfo"""
775 for badRotTypeName in (
776 "unknown", # must be all uppercase
777 "sky", # must be all uppercase
778 "Sky", # must be all uppercase
779 "SKY1", # extra chars
780 "HORIZONTAL", # extra chars
781 ):
782 metadata = propertySetFromDict({"ROTTYPE": badRotTypeName})
783 with self.assertRaises(lsst.pex.exceptions.RuntimeError):
784 afwImage.VisitInfo(metadata)
786 def test_str(self):
787 """Check that we get something reasonable for str()"""
788 visitInfo = makeVisitInfo(self.data1)
789 string = str(visitInfo)
790 self.assertEqual(string,
791 "VisitInfo(exposureId=10313423, exposureTime=10.01, darkTime=11.02, "
792 "date=2037-09-20T02:24:00.000000000, UT1=12345.1, ERA=0.787143 rad, "
793 "boresightRaDec=(23.1000000000, +73.2000000000), "
794 "boresightAzAlt=(134.5000000000, +33.3000000000), boresightAirmass=1.73, "
795 "boresightRotAngle=1.27758 rad, rotType=1, observatory=22.2N, 11.1E 0.333, "
796 "weather=Weather(1.1, 2.2, 34.5), instrumentLabel='TestCameraOne', "
797 "id=987654, focusZ=1.5, observationType='flat', scienceProgram='test program', "
798 "observationReason='test reason', object='test object', hasSimulatedContent=false)")
800 def testParallacticAngle(self):
801 """Check that we get the same precomputed values for parallactic angle."""
802 parallacticAngle = [141.39684140703142*degrees, 76.99982166973487*degrees]
803 for item, parAngle in zip((self.data1, self.data2), parallacticAngle):
804 visitInfo = afwImage.VisitInfo(era=item.era,
805 boresightRaDec=item.boresightRaDec,
806 observatory=item.observatory,
807 )
808 self.assertAnglesAlmostEqual(visitInfo.getBoresightParAngle(), parAngle)
810 def testParallacticAngleNorthMeridian(self):
811 """An observation on the Meridian that is North of zenith has a parallactic angle of pi radians."""
812 meridianBoresightRA = self.data1.era + self.data1.observatory.getLongitude()
813 northBoresightDec = self.data1.observatory.getLatitude() + 10.*degrees
814 visitInfo = afwImage.VisitInfo(era=self.data1.era,
815 boresightRaDec=SpherePoint(meridianBoresightRA,
816 northBoresightDec),
817 observatory=self.data1.observatory,
818 )
819 self.assertAnglesAlmostEqual(visitInfo.getBoresightParAngle(), Angle(np.pi))
821 def testParallacticAngleSouthMeridian(self):
822 """An observation on the Meridian that is South of zenith has a parallactic angle of zero."""
823 meridianBoresightRA = self.data1.era + self.data1.observatory.getLongitude()
824 southBoresightDec = self.data1.observatory.getLatitude() - 10.*degrees
825 visitInfo = afwImage.VisitInfo(era=self.data1.era,
826 boresightRaDec=SpherePoint(meridianBoresightRA,
827 southBoresightDec),
828 observatory=self.data1.observatory,
829 )
830 self.assertAnglesAlmostEqual(visitInfo.getBoresightParAngle(), Angle(0.))
833def setup_module(module):
834 lsst.utils.tests.init()
837class MemoryTester(lsst.utils.tests.MemoryTestCase):
838 pass
841if __name__ == "__main__": 841 ↛ 842line 841 didn't jump to line 842, because the condition on line 841 was never true
842 lsst.utils.tests.init()
843 unittest.main()