Coverage for tests/test_visitInfo.py: 9%
420 statements
« prev ^ index » next coverage.py v7.4.4, created at 2024-04-06 04:03 -0700
« prev ^ index » next coverage.py v7.4.4, created at 2024-04-06 04:03 -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.exposureTime,
54 data.darkTime,
55 data.date,
56 data.ut1,
57 data.era,
58 data.boresightRaDec,
59 data.boresightAzAlt,
60 data.boresightAirmass,
61 data.boresightRotAngle,
62 data.rotType,
63 data.observatory,
64 data.weather,
65 data.instrumentLabel,
66 data.id,
67 data.focusZ,
68 data.observationType,
69 data.scienceProgram,
70 data.observationReason,
71 data.object,
72 data.hasSimulatedContent,
73 )
76class VisitInfoTestCase(lsst.utils.tests.TestCase):
77 """Test lsst.afw.image.VisitInfo, a simple struct-like class"""
79 def setUp(self):
80 self.testDir = os.path.dirname(__file__)
82 def computeLstHA(data):
83 """Return LST, Hour Angle, computed from VisitInfoData."""
84 localEra = data.era + data.observatory.getLongitude()
85 hourAngle = localEra - data.boresightRaDec[0]
86 return localEra, hourAngle
88 fields = ['exposureTime',
89 'darkTime',
90 'date',
91 'ut1',
92 'era',
93 'boresightRaDec',
94 'boresightAzAlt',
95 'boresightAirmass',
96 'boresightRotAngle',
97 'rotType',
98 'observatory',
99 'weather',
100 'instrumentLabel',
101 'id',
102 'focusZ',
103 'observationType',
104 "scienceProgram",
105 "observationReason",
106 "object",
107 "hasSimulatedContent",
108 ]
109 VisitInfoData = collections.namedtuple("VisitInfoData", fields)
110 data1 = VisitInfoData(exposureTime=10.01,
111 darkTime=11.02,
112 date=DateTime(
113 65321.1, DateTime.MJD, DateTime.TAI),
114 ut1=12345.1,
115 era=45.1*degrees,
116 boresightRaDec=SpherePoint(
117 23.1*degrees, 73.2*degrees),
118 boresightAzAlt=SpherePoint(
119 134.5*degrees, 33.3*degrees),
120 boresightAirmass=1.73,
121 boresightRotAngle=73.2*degrees,
122 rotType=afwImage.RotType.SKY,
123 observatory=Observatory(
124 11.1*degrees, 22.2*degrees, 0.333),
125 weather=Weather(1.1, 2.2, 34.5),
126 instrumentLabel="TestCameraOne",
127 id=987654,
128 focusZ=1.5,
129 observationType="flat",
130 scienceProgram="test program",
131 observationReason="test reason",
132 object="test object",
133 hasSimulatedContent=False,
134 )
135 self.data1 = data1
136 self.localEra1, self.hourAngle1 = computeLstHA(data1)
137 data2 = VisitInfoData(exposureTime=15.5,
138 darkTime=17.8,
139 date=DateTime(
140 55321.2, DateTime.MJD, DateTime.TAI),
141 ut1=312345.1,
142 era=25.1*degrees,
143 boresightRaDec=SpherePoint(
144 2.1*degrees, 33.2*degrees),
145 boresightAzAlt=SpherePoint(13.5*degrees, 83.3*degrees),
146 boresightAirmass=2.05,
147 boresightRotAngle=-53.2*degrees,
148 rotType=afwImage.RotType.HORIZON,
149 observatory=Observatory(
150 22.2*degrees, 33.3*degrees, 0.444),
151 weather=Weather(2.2, 3.3, 44.4),
152 instrumentLabel="TestCameraTwo",
153 id=123456,
154 focusZ=-0.7,
155 observationType="science",
156 scienceProgram="test program 2",
157 observationReason="test reason 2",
158 object="test object 2",
159 hasSimulatedContent=True,
160 )
161 self.data2 = data2
162 self.localEra2, self.hourAngle2 = computeLstHA(data2)
164 def _testValueConstructor(self, data, localEra, hourAngle):
165 visitInfo = makeVisitInfo(data)
166 self.assertEqual(visitInfo.getExposureTime(), data.exposureTime)
167 self.assertEqual(visitInfo.getDarkTime(), data.darkTime)
168 self.assertEqual(visitInfo.getDate(), data.date)
169 self.assertEqual(visitInfo.getUt1(), data.ut1)
170 self.assertEqual(visitInfo.getEra(), data.era)
171 self.assertEqual(visitInfo.getBoresightRaDec(), data.boresightRaDec)
172 self.assertEqual(visitInfo.getBoresightAzAlt(), data.boresightAzAlt)
173 self.assertEqual(visitInfo.getBoresightAirmass(),
174 data.boresightAirmass)
175 self.assertEqual(visitInfo.getBoresightRotAngle(),
176 data.boresightRotAngle)
177 self.assertEqual(visitInfo.getRotType(), data.rotType)
178 self.assertEqual(visitInfo.getObservatory(), data.observatory)
179 self.assertEqual(visitInfo.getInstrumentLabel(), data.instrumentLabel)
180 self.assertEqual(visitInfo.getWeather(), data.weather)
181 self.assertEqual(visitInfo.getLocalEra(), localEra)
182 self.assertEqual(visitInfo.getBoresightHourAngle(), hourAngle)
183 self.assertEqual(visitInfo.getId(), data.id)
184 self.assertEqual(visitInfo.getFocusZ(), data.focusZ)
185 self.assertEqual(visitInfo.getObservationType(), data.observationType)
186 self.assertEqual(visitInfo.getScienceProgram(), data.scienceProgram)
187 self.assertEqual(visitInfo.getObservationReason(), data.observationReason)
188 self.assertEqual(visitInfo.getObject(), data.object)
189 self.assertEqual(visitInfo.getHasSimulatedContent(), data.hasSimulatedContent)
191 def _testProperties(self, data, localEra, hourAngle):
192 """Test property attribute accessors."""
193 visitInfo = makeVisitInfo(data)
194 self.assertEqual(visitInfo.exposureTime, data.exposureTime)
195 self.assertEqual(visitInfo.darkTime, data.darkTime)
196 self.assertEqual(visitInfo.date, data.date)
197 self.assertEqual(visitInfo.ut1, data.ut1)
198 self.assertEqual(visitInfo.era, data.era)
199 self.assertEqual(visitInfo.boresightRaDec, data.boresightRaDec)
200 self.assertEqual(visitInfo.boresightAzAlt, data.boresightAzAlt)
201 self.assertEqual(visitInfo.boresightAirmass, data.boresightAirmass)
202 self.assertEqual(visitInfo.boresightRotAngle, data.boresightRotAngle)
203 self.assertEqual(visitInfo.rotType, data.rotType)
204 self.assertEqual(visitInfo.observatory, data.observatory)
205 self.assertEqual(visitInfo.instrumentLabel, data.instrumentLabel)
206 self.assertEqual(visitInfo.weather, data.weather)
207 self.assertEqual(visitInfo.localEra, localEra)
208 self.assertEqual(visitInfo.boresightHourAngle, hourAngle)
209 self.assertEqual(visitInfo.id, data.id)
210 self.assertEqual(visitInfo.focusZ, data.focusZ)
211 self.assertEqual(visitInfo.observationType, data.observationType)
212 self.assertEqual(visitInfo.scienceProgram, data.scienceProgram)
213 self.assertEqual(visitInfo.observationReason, data.observationReason)
214 self.assertEqual(visitInfo.object, data.object)
215 self.assertEqual(visitInfo.hasSimulatedContent, data.hasSimulatedContent)
217 def testValueConstructor_data1(self):
218 self._testValueConstructor(self.data1, self.localEra1, self.hourAngle1)
219 self._testProperties(self.data1, self.localEra1, self.hourAngle1)
221 def testValueConstructor_data2(self):
222 self._testValueConstructor(self.data2, self.localEra2, self.hourAngle2)
223 self._testProperties(self.data2, self.localEra2, self.hourAngle2)
225 def testCopyWith(self):
226 visitInfo1 = makeVisitInfo(self.data1)
227 visitInfo2 = makeVisitInfo(self.data2)
229 updateFields1 = [
230 "exposureTime",
231 "darkTime",
232 "date",
233 "ut1",
234 "era",
235 "boresightRaDec",
236 "boresightAzAlt",
237 "boresightAirmass",
238 "boresightRotAngle",
239 ]
241 updateFields2 = [
242 "rotType",
243 "observatory",
244 "weather",
245 "instrumentLabel",
246 "id",
247 "focusZ",
248 "observationType",
249 "scienceProgram",
250 "observationReason",
251 "object",
252 "hasSimulatedContent",
253 ]
255 kwargs1 = {k: getattr(visitInfo2, k) for k in updateFields1}
256 kwargs2 = {k: getattr(visitInfo2, k) for k in updateFields2}
258 newVisit1 = visitInfo1.copyWith(**kwargs1)
259 newVisit2 = visitInfo1.copyWith(**kwargs2)
261 for field in updateFields1:
262 self.assertEqual(getattr(newVisit1, field), getattr(visitInfo2, field))
263 self.assertEqual(getattr(newVisit2, field), getattr(visitInfo1, field))
265 for field in updateFields2:
266 self.assertEqual(getattr(newVisit1, field), getattr(visitInfo1, field))
267 self.assertEqual(getattr(newVisit2, field), getattr(visitInfo2, field))
269 def testTablePersistence(self):
270 """Test that VisitInfo can be round-tripped with current code.
271 """
272 for item in (self.data1, self.data2):
273 tablePath = os.path.join(
274 self.testDir, "testVisitInfo_testTablePersistence.fits")
275 v1 = afwImage.VisitInfo(*item)
276 v1.writeFits(tablePath)
277 v2 = afwImage.VisitInfo.readFits(tablePath)
278 self.assertEqual(v1, v2)
279 os.unlink(tablePath)
281 def _testFitsRead(self, data, filePath, version):
282 """Test that old VersionInfo files are read correctly.
284 Parameters
285 ----------
286 data : `VisitInfoData`
287 The values expected to be stored in the file, or a
288 superset thereof.
289 filePath : `str`
290 The file to test.
291 version : `int`
292 The VersionInfo persistence format used in ``filePath``.
293 """
294 visitInfo = afwImage.VisitInfo.readFits(filePath)
296 if version >= 0:
297 self.assertEqual(visitInfo.getExposureTime(), data.exposureTime)
298 self.assertEqual(visitInfo.getDarkTime(), data.darkTime)
299 self.assertEqual(visitInfo.getDate(), data.date)
300 self.assertEqual(visitInfo.getUt1(), data.ut1)
301 self.assertEqual(visitInfo.getEra(), data.era)
302 self.assertEqual(visitInfo.getBoresightRaDec(), data.boresightRaDec)
303 self.assertEqual(visitInfo.getBoresightAzAlt(), data.boresightAzAlt)
304 self.assertEqual(visitInfo.getBoresightAirmass(),
305 data.boresightAirmass)
306 self.assertEqual(visitInfo.getBoresightRotAngle(),
307 data.boresightRotAngle)
308 self.assertEqual(visitInfo.getRotType(), data.rotType)
309 self.assertEqual(visitInfo.getObservatory(), data.observatory)
310 self.assertEqual(visitInfo.getWeather(), data.weather)
311 if version >= 1:
312 self.assertEqual(visitInfo.getInstrumentLabel(), data.instrumentLabel)
313 else:
314 self.assertEqual(visitInfo.getInstrumentLabel(), "")
315 if version >= 2:
316 self.assertEqual(visitInfo.getId(), data.id)
317 else:
318 self.assertEqual(visitInfo.getId(), 0)
319 if version >= 3:
320 self.assertEqual(visitInfo.getFocusZ(), data.focusZ)
321 else:
322 self.assertTrue(math.isnan(visitInfo.getFocusZ()))
323 if version >= 4:
324 self.assertEqual(visitInfo.getObservationType(), data.observationType)
325 self.assertEqual(visitInfo.getScienceProgram(), data.scienceProgram)
326 self.assertEqual(visitInfo.getObservationReason(), data.observationReason)
327 self.assertEqual(visitInfo.getObject(), data.object)
328 self.assertEqual(visitInfo.getHasSimulatedContent(), data.hasSimulatedContent)
329 else:
330 self.assertEqual(visitInfo.getObservationType(), "")
331 self.assertEqual(visitInfo.getScienceProgram(), "")
332 self.assertEqual(visitInfo.getObservationReason(), "")
333 self.assertEqual(visitInfo.getObject(), "")
334 self.assertEqual(visitInfo.getHasSimulatedContent(), False)
335 # version == 5 removed the exposureId field
337 def testPersistenceVersions(self):
338 """Test that older versions are handled appropriately.
339 """
340 dataDir = os.path.join(os.path.dirname(__file__), "data")
342 # All files created by makeVisitInfo(self.data1).writeFits()
343 self._testFitsRead(self.data1, os.path.join(dataDir, "visitInfo-noversion.fits"), 0)
344 self._testFitsRead(self.data1, os.path.join(dataDir, "visitInfo-version-1.fits"), 1)
345 self._testFitsRead(self.data1, os.path.join(dataDir, "visitInfo-version-2.fits"), 2)
346 self._testFitsRead(self.data1, os.path.join(dataDir, "visitInfo-version-3.fits"), 3)
347 self._testFitsRead(self.data1, os.path.join(dataDir, "visitInfo-version-4.fits"), 4)
348 self._testFitsRead(self.data1, os.path.join(dataDir, "visitInfo-version-5.fits"), 5)
350 # Check that reading a newer file raises a useful exception.
351 with self.assertRaisesRegex(TypeError,
352 r"Cannot read VisitInfo FITS version > [0-9]+, found version 999999"):
353 afwImage.VisitInfo.readFits(os.path.join(dataDir, "visitInfo-version-999999.fits"))
355 def testSetVisitInfoMetadata(self):
356 for item in (self.data1, self.data2):
357 visitInfo = makeVisitInfo(item)
358 metadata = PropertyList()
359 afwImage.setVisitInfoMetadata(metadata, visitInfo)
360 self.assertEqual(metadata.nameCount(), 27)
361 self.assertEqual(metadata.getScalar("EXPTIME"), item.exposureTime)
362 self.assertEqual(metadata.getScalar("DARKTIME"), item.darkTime)
363 self.assertEqual(metadata.getScalar("DATE-AVG"),
364 item.date.toString(DateTime.TAI))
365 self.assertEqual(metadata.getScalar("TIMESYS"), "TAI")
366 self.assertEqual(metadata.getScalar("MJD-AVG-UT1"), item.ut1)
367 self.assertEqual(metadata.getScalar("AVG-ERA"), item.era.asDegrees())
368 self.assertEqual(metadata.getScalar("BORE-RA"),
369 item.boresightRaDec[0].asDegrees())
370 self.assertEqual(metadata.getScalar("BORE-DEC"),
371 item.boresightRaDec[1].asDegrees())
372 self.assertEqual(metadata.getScalar("BORE-AZ"),
373 item.boresightAzAlt[0].asDegrees())
374 self.assertEqual(metadata.getScalar("BORE-ALT"),
375 item.boresightAzAlt[1].asDegrees())
376 self.assertEqual(metadata.getScalar("BORE-AIRMASS"),
377 item.boresightAirmass)
378 self.assertEqual(metadata.getScalar("BORE-ROTANG"),
379 item.boresightRotAngle.asDegrees())
380 self.assertEqual(metadata.getScalar("ROTTYPE"),
381 RotTypeEnumNameDict[item.rotType])
382 self.assertEqual(metadata.getScalar("OBS-LONG"),
383 item.observatory.getLongitude().asDegrees())
384 self.assertEqual(metadata.getScalar("OBS-LAT"),
385 item.observatory.getLatitude().asDegrees())
386 self.assertEqual(metadata.getScalar("OBS-ELEV"),
387 item.observatory.getElevation())
388 self.assertEqual(metadata.getScalar("AIRTEMP"),
389 item.weather.getAirTemperature())
390 self.assertEqual(metadata.getScalar("AIRPRESS"),
391 item.weather.getAirPressure())
392 self.assertEqual(metadata.getScalar("HUMIDITY"),
393 item.weather.getHumidity())
394 self.assertEqual(metadata.getScalar("INSTRUMENT"),
395 item.instrumentLabel)
396 self.assertEqual(metadata.getScalar("IDNUM"),
397 item.id)
398 self.assertEqual(metadata.getScalar("FOCUSZ"),
399 item.focusZ)
400 self.assertEqual(metadata.getScalar("OBSTYPE"),
401 item.observationType)
402 self.assertEqual(metadata.getScalar("PROGRAM"),
403 item.scienceProgram)
404 self.assertEqual(metadata.getScalar("REASON"),
405 item.observationReason)
406 self.assertEqual(metadata.getScalar("OBJECT"),
407 item.object)
408 self.assertEqual(metadata.getScalar("HAS-SIMULATED-CONTENT"),
409 item.hasSimulatedContent)
411 def testSetVisitInfoMetadataMissingValues(self):
412 """If a value is unknown then it should not be written to the metadata"""
413 # Only rot type and hasSimulatedContent are "known" when default-
414 # constructed, by virtue of having no "null" state.
415 visitInfo = afwImage.VisitInfo()
416 metadata = PropertyList()
417 afwImage.setVisitInfoMetadata(metadata, visitInfo)
418 self.assertEqual(metadata.getScalar("ROTTYPE"),
419 RotTypeEnumNameDict[afwImage.RotType.UNKNOWN])
420 self.assertEqual(metadata.getScalar("HAS-SIMULATED-CONTENT"), False)
421 self.assertEqual(metadata.nameCount(), 2)
423 def testStripVisitInfoKeywords(self):
424 for argList in (self.data1, self.data2):
425 visitInfo = afwImage.VisitInfo(*argList)
426 metadata = PropertyList()
427 afwImage.setVisitInfoMetadata(metadata, visitInfo)
428 # add an extra keyword that will not be stripped
429 metadata.set("EXTRA", 5)
430 self.assertEqual(metadata.nameCount(), 28)
431 afwImage.stripVisitInfoKeywords(metadata)
432 self.assertEqual(metadata.nameCount(), 1)
434 def _testIsEmpty(self, visitInfo):
435 """Test that visitInfo is all NaN, 0, or empty string, as appropriate.
436 """
437 self.assertTrue(math.isnan(visitInfo.getExposureTime()))
438 self.assertTrue(math.isnan(visitInfo.getDarkTime()))
439 self.assertEqual(visitInfo.getDate(), DateTime())
440 self.assertTrue(math.isnan(visitInfo.getUt1()))
441 self.assertTrue(math.isnan(visitInfo.getEra().asDegrees()))
442 for i in range(2):
443 self.assertTrue(math.isnan(
444 visitInfo.getBoresightRaDec()[i].asDegrees()))
445 self.assertTrue(math.isnan(
446 visitInfo.getBoresightAzAlt()[i].asDegrees()))
447 self.assertTrue(math.isnan(visitInfo.getBoresightAirmass()))
448 self.assertTrue(math.isnan(
449 visitInfo.getBoresightRotAngle().asDegrees()))
450 self.assertEqual(visitInfo.getRotType(), afwImage.RotType.UNKNOWN)
451 self.assertTrue(math.isnan(
452 visitInfo.getObservatory().getLongitude().asDegrees()))
453 self.assertTrue(math.isnan(
454 visitInfo.getObservatory().getLatitude().asDegrees()))
455 self.assertTrue(math.isnan(visitInfo.getObservatory().getElevation()))
456 self.assertTrue(math.isnan(visitInfo.getWeather().getAirTemperature()))
457 self.assertTrue(math.isnan(visitInfo.getWeather().getAirPressure()))
458 self.assertTrue(math.isnan(visitInfo.getWeather().getHumidity()))
459 self.assertTrue(math.isnan(visitInfo.getBoresightHourAngle()))
460 self.assertEqual(visitInfo.getInstrumentLabel(), "")
461 self.assertEqual(visitInfo.getId(), 0)
462 self.assertTrue(math.isnan(visitInfo.getFocusZ()))
463 self.assertEqual(visitInfo.getObservationType(), "")
464 self.assertEqual(visitInfo.getScienceProgram(), "")
465 self.assertEqual(visitInfo.getObservationReason(), "")
466 self.assertEqual(visitInfo.getObject(), "")
467 self.assertEqual(visitInfo.getHasSimulatedContent(), False)
469 def testEquals(self):
470 """Test that identical VisitInfo objects compare equal, even if some fields are NaN.
471 """
472 # objects with "equal state" should be equal
473 self.assertEqual(makeVisitInfo(self.data1), makeVisitInfo(self.data1))
474 self.assertEqual(makeVisitInfo(self.data2), makeVisitInfo(self.data2))
475 self.assertNotEqual(makeVisitInfo(self.data1), makeVisitInfo(self.data2))
476 self.assertEqual(afwImage.VisitInfo(), afwImage.VisitInfo())
478 # equality must be reflexive
479 info = makeVisitInfo(self.data1)
480 self.assertEqual(info, info)
481 info = makeVisitInfo(self.data2)
482 self.assertEqual(info, info)
483 info = afwImage.VisitInfo()
484 self.assertEqual(info, info)
486 # commutativity and transitivity difficult to test with this setup
488 def testMetadataConstructor(self):
489 """Test the metadata constructor
491 This constructor allows missing values
492 """
493 data = self.data1
495 metadata = propertySetFromDict({})
496 visitInfo = afwImage.VisitInfo(metadata)
497 self._testIsEmpty(visitInfo)
499 metadata = propertySetFromDict({"EXPTIME": data.exposureTime})
500 visitInfo = afwImage.VisitInfo(metadata)
501 self.assertEqual(visitInfo.getExposureTime(), data.exposureTime)
503 metadata = propertySetFromDict({"DARKTIME": data.darkTime})
504 visitInfo = afwImage.VisitInfo(metadata)
505 self.assertEqual(visitInfo.getDarkTime(), data.darkTime)
507 metadata = propertySetFromDict(
508 {"DATE-AVG": data.date.toString(DateTime.TAI), "TIMESYS": "TAI"})
509 visitInfo = afwImage.VisitInfo(metadata)
510 self.assertEqual(visitInfo.getDate(), data.date)
512 # TIME-MID in UTC is an acceptable alternative to DATE-AVG
513 metadata = propertySetFromDict(
514 {"TIME-MID": data.date.toString(DateTime.UTC)})
515 visitInfo = afwImage.VisitInfo(metadata)
516 self.assertEqual(visitInfo.getDate(), data.date)
518 # TIME-MID must be in UTC and TIMESYS is ignored
519 metadata = propertySetFromDict({
520 "TIME-MID": data.date.toString(DateTime.TAI) + "Z",
521 "TIMESYS": "TAI",
522 })
523 visitInfo = afwImage.VisitInfo(metadata)
524 self.assertNotEqual(visitInfo.getDate(), data.date)
526 # if both DATE-AVG and TIME-MID provided then use DATE-AVG
527 # use the wrong time system for TIME-MID so if it is used, an error
528 # will result
529 metadata = propertySetFromDict({
530 "DATE-AVG": data.date.toString(DateTime.TAI),
531 "TIMESYS": "TAI",
532 "TIME-MID": data.date.toString(DateTime.TAI) + "Z",
533 })
534 visitInfo = afwImage.VisitInfo(metadata)
535 self.assertEqual(visitInfo.getDate(), data.date)
537 metadata = propertySetFromDict({"MJD-AVG-UT1": data.ut1})
538 visitInfo = afwImage.VisitInfo(metadata)
539 self.assertEqual(visitInfo.getUt1(), data.ut1)
541 metadata = propertySetFromDict({"AVG-ERA": data.era.asDegrees()})
542 visitInfo = afwImage.VisitInfo(metadata)
543 self.assertEqual(visitInfo.getEra(), data.era)
545 for i, key in enumerate(("BORE-RA", "BORE-DEC")):
546 metadata = propertySetFromDict(
547 {key: data.boresightRaDec[i].asDegrees()})
548 visitInfo = afwImage.VisitInfo(metadata)
549 self.assertEqual(visitInfo.getBoresightRaDec()
550 [i], data.boresightRaDec[i])
552 for i, key in enumerate(("BORE-AZ", "BORE-ALT")):
553 metadata = propertySetFromDict(
554 {key: data.boresightAzAlt[i].asDegrees()})
555 visitInfo = afwImage.VisitInfo(metadata)
556 self.assertEqual(visitInfo.getBoresightAzAlt()
557 [i], data.boresightAzAlt[i])
559 metadata = propertySetFromDict({"BORE-AIRMASS": data.boresightAirmass})
560 visitInfo = afwImage.VisitInfo(metadata)
561 self.assertEqual(visitInfo.getBoresightAirmass(),
562 data.boresightAirmass)
564 metadata = propertySetFromDict(
565 {"BORE-ROTANG": data.boresightRotAngle.asDegrees()})
566 visitInfo = afwImage.VisitInfo(metadata)
567 self.assertEqual(visitInfo.getBoresightRotAngle(),
568 data.boresightRotAngle)
570 metadata = propertySetFromDict(
571 {"ROTTYPE": RotTypeEnumNameDict[data.rotType]})
572 visitInfo = afwImage.VisitInfo(metadata)
573 self.assertEqual(visitInfo.getRotType(), data.rotType)
575 metadata = propertySetFromDict(
576 {"OBS-LONG": data.observatory.getLongitude().asDegrees()})
577 visitInfo = afwImage.VisitInfo(metadata)
578 self.assertEqual(visitInfo.getObservatory().getLongitude(),
579 data.observatory.getLongitude())
581 metadata = propertySetFromDict(
582 {"OBS-LAT": data.observatory.getLatitude().asDegrees()})
583 visitInfo = afwImage.VisitInfo(metadata)
584 self.assertEqual(visitInfo.getObservatory().getLatitude(),
585 data.observatory.getLatitude())
587 metadata = propertySetFromDict(
588 {"OBS-ELEV": data.observatory.getElevation()})
589 visitInfo = afwImage.VisitInfo(metadata)
590 self.assertEqual(visitInfo.getObservatory().getElevation(),
591 data.observatory.getElevation())
593 metadata = propertySetFromDict(
594 {"AIRTEMP": data.weather.getAirTemperature()})
595 visitInfo = afwImage.VisitInfo(metadata)
596 self.assertEqual(visitInfo.getWeather().getAirTemperature(),
597 data.weather.getAirTemperature())
599 metadata = propertySetFromDict(
600 {"AIRPRESS": data.weather.getAirPressure()})
601 visitInfo = afwImage.VisitInfo(metadata)
602 self.assertEqual(visitInfo.getWeather().getAirPressure(),
603 data.weather.getAirPressure())
605 metadata = propertySetFromDict(
606 {"HUMIDITY": data.weather.getHumidity()})
607 visitInfo = afwImage.VisitInfo(metadata)
608 self.assertEqual(visitInfo.getWeather().getHumidity(),
609 data.weather.getHumidity())
611 metadata = propertySetFromDict({"INSTRUMENT": data.instrumentLabel})
612 visitInfo = afwImage.VisitInfo(metadata)
613 self.assertEqual(visitInfo.getInstrumentLabel(), data.instrumentLabel)
615 metadata = propertySetFromDict({"IDNUM": data.id})
616 visitInfo = afwImage.VisitInfo(metadata)
617 self.assertEqual(visitInfo.getId(), data.id)
619 metadata = propertySetFromDict({"FOCUSZ": data.focusZ})
620 visitInfo = afwImage.VisitInfo(metadata)
621 self.assertEqual(visitInfo.getFocusZ(), data.focusZ)
623 metadata = propertySetFromDict({"OBSTYPE": data.observationType})
624 visitInfo = afwImage.VisitInfo(metadata)
625 self.assertEqual(visitInfo.getObservationType(), data.observationType)
627 metadata = propertySetFromDict({"PROGRAM": data.scienceProgram})
628 visitInfo = afwImage.VisitInfo(metadata)
629 self.assertEqual(visitInfo.getScienceProgram(), data.scienceProgram)
631 metadata = propertySetFromDict({"REASON": data.observationReason})
632 visitInfo = afwImage.VisitInfo(metadata)
633 self.assertEqual(visitInfo.getObservationReason(), data.observationReason)
635 metadata = propertySetFromDict({"OBJECT": data.object})
636 visitInfo = afwImage.VisitInfo(metadata)
637 self.assertEqual(visitInfo.getObject(), data.object)
639 metadata = propertySetFromDict({"HAS-SIMULATED-CONTENT": data.hasSimulatedContent})
640 visitInfo = afwImage.VisitInfo(metadata)
641 self.assertEqual(visitInfo.getHasSimulatedContent(), data.hasSimulatedContent)
643 def testMetadataConstructorUndefined(self):
644 """Test that we can create VisitInfo using None in metadata."""
646 metadata = propertySetFromDict({
647 # These are examples of generic reader code.
648 "PROGRAM": None, # A string should convert to "".
649 "DARKTIME": None, # A missing float should convert to NaN.
650 # These headers have special logic in the reader.
651 "EXPTIME": None,
652 "IDNUM": None,
653 "DATE-AVG": None,
654 "ROTTYPE": None,
655 "HAS-SIMULATED-CONTENT": None,
656 })
657 visitInfo = afwImage.VisitInfo(metadata)
658 self.assertEqual(visitInfo.getScienceProgram(), "")
659 self.assertTrue(math.isnan(visitInfo.getDarkTime()))
660 self.assertTrue(math.isnan(visitInfo.getExposureTime()))
661 self.assertEqual(visitInfo.getId(), 0)
662 self.assertFalse(visitInfo.getDate().isValid())
664 def testConstructorKeywordArguments(self):
665 """Test VisitInfo with named arguments"""
666 data = self.data1
668 visitInfo = afwImage.VisitInfo()
669 self._testIsEmpty(visitInfo)
671 self.assertTrue(math.isnan(visitInfo.getExposureTime()))
673 visitInfo = afwImage.VisitInfo(exposureTime=data.exposureTime)
674 self.assertEqual(visitInfo.getExposureTime(), data.exposureTime)
676 visitInfo = afwImage.VisitInfo(darkTime=data.darkTime)
677 self.assertEqual(visitInfo.getDarkTime(), data.darkTime)
679 visitInfo = afwImage.VisitInfo(date=data.date)
680 self.assertEqual(visitInfo.getDate(), data.date)
682 visitInfo = afwImage.VisitInfo(ut1=data.ut1)
683 self.assertEqual(visitInfo.getUt1(), data.ut1)
685 visitInfo = afwImage.VisitInfo(era=data.era)
686 self.assertEqual(visitInfo.getEra(), data.era)
688 visitInfo = afwImage.VisitInfo(boresightRaDec=data.boresightRaDec)
689 self.assertEqual(visitInfo.getBoresightRaDec(), data.boresightRaDec)
691 visitInfo = afwImage.VisitInfo(boresightAzAlt=data.boresightAzAlt)
692 self.assertEqual(visitInfo.getBoresightAzAlt(), data.boresightAzAlt)
694 visitInfo = afwImage.VisitInfo(boresightAirmass=data.boresightAirmass)
695 self.assertEqual(visitInfo.getBoresightAirmass(),
696 data.boresightAirmass)
698 visitInfo = afwImage.VisitInfo(
699 boresightRotAngle=data.boresightRotAngle)
700 self.assertEqual(visitInfo.getBoresightRotAngle(),
701 data.boresightRotAngle)
703 visitInfo = afwImage.VisitInfo(rotType=data.rotType)
704 self.assertEqual(visitInfo.getRotType(), data.rotType)
706 visitInfo = afwImage.VisitInfo(observatory=data.observatory)
707 self.assertEqual(visitInfo.getObservatory(), data.observatory)
709 visitInfo = afwImage.VisitInfo(weather=data.weather)
710 self.assertEqual(visitInfo.getWeather(), data.weather)
712 visitInfo = afwImage.VisitInfo(instrumentLabel=data.instrumentLabel)
713 self.assertEqual(visitInfo.getInstrumentLabel(), data.instrumentLabel)
715 visitInfo = afwImage.VisitInfo(id=data.id)
716 self.assertEqual(visitInfo.getId(), data.id)
718 visitInfo = afwImage.VisitInfo(focusZ=data.focusZ)
719 self.assertEqual(visitInfo.getFocusZ(), data.focusZ)
721 visitInfo = afwImage.VisitInfo(observationType=data.observationType)
722 self.assertEqual(visitInfo.getObservationType(), data.observationType)
724 visitInfo = afwImage.VisitInfo(scienceProgram=data.scienceProgram)
725 self.assertEqual(visitInfo.getScienceProgram(), data.scienceProgram)
727 visitInfo = afwImage.VisitInfo(observationReason=data.observationReason)
728 self.assertEqual(visitInfo.getObservationReason(), data.observationReason)
730 visitInfo = afwImage.VisitInfo(object=data.object)
731 self.assertEqual(visitInfo.getObject(), data.object)
733 visitInfo = afwImage.VisitInfo(hasSimulatedContent=data.hasSimulatedContent)
734 self.assertEqual(visitInfo.getHasSimulatedContent(), data.hasSimulatedContent)
736 def testGoodRotTypes(self):
737 """Test round trip of all valid rot types"""
738 for rotType in RotTypeEnumNameDict:
739 metadata = propertySetFromDict(
740 {"ROTTYPE": RotTypeEnumNameDict[rotType]})
741 visitInfo = afwImage.VisitInfo(metadata)
742 self.assertEqual(visitInfo.getRotType(), rotType)
744 def testBadRotTypes(self):
745 """Test that invalid rot type names cannot be used to construct a VisitInfo"""
746 for badRotTypeName in (
747 "unknown", # must be all uppercase
748 "sky", # must be all uppercase
749 "Sky", # must be all uppercase
750 "SKY1", # extra chars
751 "HORIZONTAL", # extra chars
752 ):
753 metadata = propertySetFromDict({"ROTTYPE": badRotTypeName})
754 with self.assertRaises(lsst.pex.exceptions.RuntimeError):
755 afwImage.VisitInfo(metadata)
757 def test_str(self):
758 """Check that we get something reasonable for str()"""
759 visitInfo = makeVisitInfo(self.data1)
760 string = str(visitInfo)
761 self.assertEqual(string,
762 "VisitInfo(exposureTime=10.01, darkTime=11.02, "
763 "date=2037-09-20T02:24:00.000000000, UT1=12345.1, ERA=0.787143 rad, "
764 "boresightRaDec=(23.1000000000, +73.2000000000), "
765 "boresightAzAlt=(134.5000000000, +33.3000000000), boresightAirmass=1.73, "
766 "boresightRotAngle=1.27758 rad, rotType=1, observatory=22.2N, 11.1E 0.333, "
767 "weather=Weather(1.1, 2.2, 34.5), instrumentLabel='TestCameraOne', "
768 "id=987654, focusZ=1.5, observationType='flat', scienceProgram='test program', "
769 "observationReason='test reason', object='test object', hasSimulatedContent=false)")
771 def testParallacticAngle(self):
772 """Check that we get the same precomputed values for parallactic angle."""
773 parallacticAngle = [141.39684140703142*degrees, 76.99982166973487*degrees]
774 for item, parAngle in zip((self.data1, self.data2), parallacticAngle):
775 visitInfo = afwImage.VisitInfo(era=item.era,
776 boresightRaDec=item.boresightRaDec,
777 observatory=item.observatory,
778 )
779 self.assertAnglesAlmostEqual(visitInfo.getBoresightParAngle(), parAngle)
781 def testParallacticAngleNorthMeridian(self):
782 """An observation on the Meridian that is North of zenith has a parallactic angle of pi radians."""
783 meridianBoresightRA = self.data1.era + self.data1.observatory.getLongitude()
784 northBoresightDec = self.data1.observatory.getLatitude() + 10.*degrees
785 visitInfo = afwImage.VisitInfo(era=self.data1.era,
786 boresightRaDec=SpherePoint(meridianBoresightRA,
787 northBoresightDec),
788 observatory=self.data1.observatory,
789 )
790 self.assertAnglesAlmostEqual(visitInfo.getBoresightParAngle(), Angle(np.pi))
792 def testParallacticAngleSouthMeridian(self):
793 """An observation on the Meridian that is South of zenith has a parallactic angle of zero."""
794 meridianBoresightRA = self.data1.era + self.data1.observatory.getLongitude()
795 southBoresightDec = self.data1.observatory.getLatitude() - 10.*degrees
796 visitInfo = afwImage.VisitInfo(era=self.data1.era,
797 boresightRaDec=SpherePoint(meridianBoresightRA,
798 southBoresightDec),
799 observatory=self.data1.observatory,
800 )
801 self.assertAnglesAlmostEqual(visitInfo.getBoresightParAngle(), Angle(0.))
804def setup_module(module):
805 lsst.utils.tests.init()
808class MemoryTester(lsst.utils.tests.MemoryTestCase):
809 pass
812if __name__ == "__main__": 812 ↛ 813line 812 didn't jump to line 813, because the condition on line 812 was never true
813 lsst.utils.tests.init()
814 unittest.main()