Coverage for tests/test_visitInfo.py: 9%
436 statements
« prev ^ index » next coverage.py v6.5.0, created at 2023-03-30 02:46 -0700
« prev ^ index » next coverage.py v6.5.0, created at 2023-03-30 02:46 -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 def testSetVisitInfoMetadata(self):
364 for item in (self.data1, self.data2):
365 visitInfo = makeVisitInfo(item)
366 metadata = PropertyList()
367 afwImage.setVisitInfoMetadata(metadata, visitInfo)
368 self.assertEqual(metadata.nameCount(), 28)
369 self.assertEqual(metadata.getScalar("EXPID"), item.exposureId)
370 self.assertEqual(metadata.getScalar("EXPTIME"), item.exposureTime)
371 self.assertEqual(metadata.getScalar("DARKTIME"), item.darkTime)
372 self.assertEqual(metadata.getScalar("DATE-AVG"),
373 item.date.toString(DateTime.TAI))
374 self.assertEqual(metadata.getScalar("TIMESYS"), "TAI")
375 self.assertEqual(metadata.getScalar("MJD-AVG-UT1"), item.ut1)
376 self.assertEqual(metadata.getScalar("AVG-ERA"), item.era.asDegrees())
377 self.assertEqual(metadata.getScalar("BORE-RA"),
378 item.boresightRaDec[0].asDegrees())
379 self.assertEqual(metadata.getScalar("BORE-DEC"),
380 item.boresightRaDec[1].asDegrees())
381 self.assertEqual(metadata.getScalar("BORE-AZ"),
382 item.boresightAzAlt[0].asDegrees())
383 self.assertEqual(metadata.getScalar("BORE-ALT"),
384 item.boresightAzAlt[1].asDegrees())
385 self.assertEqual(metadata.getScalar("BORE-AIRMASS"),
386 item.boresightAirmass)
387 self.assertEqual(metadata.getScalar("BORE-ROTANG"),
388 item.boresightRotAngle.asDegrees())
389 self.assertEqual(metadata.getScalar("ROTTYPE"),
390 RotTypeEnumNameDict[item.rotType])
391 self.assertEqual(metadata.getScalar("OBS-LONG"),
392 item.observatory.getLongitude().asDegrees())
393 self.assertEqual(metadata.getScalar("OBS-LAT"),
394 item.observatory.getLatitude().asDegrees())
395 self.assertEqual(metadata.getScalar("OBS-ELEV"),
396 item.observatory.getElevation())
397 self.assertEqual(metadata.getScalar("AIRTEMP"),
398 item.weather.getAirTemperature())
399 self.assertEqual(metadata.getScalar("AIRPRESS"),
400 item.weather.getAirPressure())
401 self.assertEqual(metadata.getScalar("HUMIDITY"),
402 item.weather.getHumidity())
403 self.assertEqual(metadata.getScalar("INSTRUMENT"),
404 item.instrumentLabel)
405 self.assertEqual(metadata.getScalar("IDNUM"),
406 item.id)
407 self.assertEqual(metadata.getScalar("FOCUSZ"),
408 item.focusZ)
409 self.assertEqual(metadata.getScalar("OBSTYPE"),
410 item.observationType)
411 self.assertEqual(metadata.getScalar("PROGRAM"),
412 item.scienceProgram)
413 self.assertEqual(metadata.getScalar("REASON"),
414 item.observationReason)
415 self.assertEqual(metadata.getScalar("OBJECT"),
416 item.object)
417 self.assertEqual(metadata.getScalar("HAS-SIMULATED-CONTENT"),
418 item.hasSimulatedContent)
420 def testSetVisitInfoMetadataMissingValues(self):
421 """If a value is unknown then it should not be written to the metadata"""
422 # Only rot type and hasSimulatedContent are "known" when default-
423 # constructed, by virtue of having no "null" state.
424 visitInfo = afwImage.VisitInfo()
425 metadata = PropertyList()
426 afwImage.setVisitInfoMetadata(metadata, visitInfo)
427 self.assertEqual(metadata.getScalar("ROTTYPE"),
428 RotTypeEnumNameDict[afwImage.RotType.UNKNOWN])
429 self.assertEqual(metadata.getScalar("HAS-SIMULATED-CONTENT"), False)
430 self.assertEqual(metadata.nameCount(), 2)
432 def testStripVisitInfoKeywords(self):
433 for argList in (self.data1, self.data2):
434 visitInfo = afwImage.VisitInfo(*argList)
435 metadata = PropertyList()
436 afwImage.setVisitInfoMetadata(metadata, visitInfo)
437 # add an extra keyword that will not be stripped
438 metadata.set("EXTRA", 5)
439 self.assertEqual(metadata.nameCount(), 29)
440 afwImage.stripVisitInfoKeywords(metadata)
441 self.assertEqual(metadata.nameCount(), 1)
443 def _testIsEmpty(self, visitInfo):
444 """Test that visitInfo is all NaN, 0, or empty string, as appropriate.
445 """
446 with self.assertWarns(FutureWarning):
447 self.assertEqual(visitInfo.getExposureId(), 0)
448 self.assertTrue(math.isnan(visitInfo.getExposureTime()))
449 self.assertTrue(math.isnan(visitInfo.getDarkTime()))
450 self.assertEqual(visitInfo.getDate(), DateTime())
451 self.assertTrue(math.isnan(visitInfo.getUt1()))
452 self.assertTrue(math.isnan(visitInfo.getEra().asDegrees()))
453 for i in range(2):
454 self.assertTrue(math.isnan(
455 visitInfo.getBoresightRaDec()[i].asDegrees()))
456 self.assertTrue(math.isnan(
457 visitInfo.getBoresightAzAlt()[i].asDegrees()))
458 self.assertTrue(math.isnan(visitInfo.getBoresightAirmass()))
459 self.assertTrue(math.isnan(
460 visitInfo.getBoresightRotAngle().asDegrees()))
461 self.assertEqual(visitInfo.getRotType(), afwImage.RotType.UNKNOWN)
462 self.assertTrue(math.isnan(
463 visitInfo.getObservatory().getLongitude().asDegrees()))
464 self.assertTrue(math.isnan(
465 visitInfo.getObservatory().getLatitude().asDegrees()))
466 self.assertTrue(math.isnan(visitInfo.getObservatory().getElevation()))
467 self.assertTrue(math.isnan(visitInfo.getWeather().getAirTemperature()))
468 self.assertTrue(math.isnan(visitInfo.getWeather().getAirPressure()))
469 self.assertTrue(math.isnan(visitInfo.getWeather().getHumidity()))
470 self.assertTrue(math.isnan(visitInfo.getBoresightHourAngle()))
471 self.assertEqual(visitInfo.getInstrumentLabel(), "")
472 self.assertEqual(visitInfo.getId(), 0)
473 self.assertTrue(math.isnan(visitInfo.getFocusZ()))
474 self.assertEqual(visitInfo.getObservationType(), "")
475 self.assertEqual(visitInfo.getScienceProgram(), "")
476 self.assertEqual(visitInfo.getObservationReason(), "")
477 self.assertEqual(visitInfo.getObject(), "")
478 self.assertEqual(visitInfo.getHasSimulatedContent(), False)
480 def testEquals(self):
481 """Test that identical VisitInfo objects compare equal, even if some fields are NaN.
482 """
483 # objects with "equal state" should be equal
484 self.assertEqual(makeVisitInfo(self.data1), makeVisitInfo(self.data1))
485 self.assertEqual(makeVisitInfo(self.data2), makeVisitInfo(self.data2))
486 self.assertNotEqual(makeVisitInfo(self.data1), makeVisitInfo(self.data2))
487 self.assertEqual(afwImage.VisitInfo(), afwImage.VisitInfo())
489 # equality must be reflexive
490 info = makeVisitInfo(self.data1)
491 self.assertEqual(info, info)
492 info = makeVisitInfo(self.data2)
493 self.assertEqual(info, info)
494 info = afwImage.VisitInfo()
495 self.assertEqual(info, info)
497 # commutativity and transitivity difficult to test with this setup
499 def testMetadataConstructor(self):
500 """Test the metadata constructor
502 This constructor allows missing values
503 """
504 data = self.data1
506 metadata = propertySetFromDict({})
507 visitInfo = afwImage.VisitInfo(metadata)
508 self._testIsEmpty(visitInfo)
510 metadata = propertySetFromDict({"EXPID": data.exposureId})
511 visitInfo = afwImage.VisitInfo(metadata)
512 with self.assertWarns(FutureWarning):
513 self.assertEqual(visitInfo.getExposureId(), data.exposureId)
514 self.assertTrue(math.isnan(visitInfo.getExposureTime()))
516 metadata = propertySetFromDict({"EXPTIME": data.exposureTime})
517 visitInfo = afwImage.VisitInfo(metadata)
518 self.assertEqual(visitInfo.getExposureTime(), data.exposureTime)
520 metadata = propertySetFromDict({"DARKTIME": data.darkTime})
521 visitInfo = afwImage.VisitInfo(metadata)
522 self.assertEqual(visitInfo.getDarkTime(), data.darkTime)
524 metadata = propertySetFromDict(
525 {"DATE-AVG": data.date.toString(DateTime.TAI), "TIMESYS": "TAI"})
526 visitInfo = afwImage.VisitInfo(metadata)
527 self.assertEqual(visitInfo.getDate(), data.date)
529 # TIME-MID in UTC is an acceptable alternative to DATE-AVG
530 metadata = propertySetFromDict(
531 {"TIME-MID": data.date.toString(DateTime.UTC)})
532 visitInfo = afwImage.VisitInfo(metadata)
533 self.assertEqual(visitInfo.getDate(), data.date)
535 # TIME-MID must be in UTC and TIMESYS is ignored
536 metadata = propertySetFromDict({
537 "TIME-MID": data.date.toString(DateTime.TAI) + "Z",
538 "TIMESYS": "TAI",
539 })
540 visitInfo = afwImage.VisitInfo(metadata)
541 self.assertNotEqual(visitInfo.getDate(), data.date)
543 # if both DATE-AVG and TIME-MID provided then use DATE-AVG
544 # use the wrong time system for TIME-MID so if it is used, an error
545 # will result
546 metadata = propertySetFromDict({
547 "DATE-AVG": data.date.toString(DateTime.TAI),
548 "TIMESYS": "TAI",
549 "TIME-MID": data.date.toString(DateTime.TAI) + "Z",
550 })
551 visitInfo = afwImage.VisitInfo(metadata)
552 self.assertEqual(visitInfo.getDate(), data.date)
554 metadata = propertySetFromDict({"MJD-AVG-UT1": data.ut1})
555 visitInfo = afwImage.VisitInfo(metadata)
556 self.assertEqual(visitInfo.getUt1(), data.ut1)
558 metadata = propertySetFromDict({"AVG-ERA": data.era.asDegrees()})
559 visitInfo = afwImage.VisitInfo(metadata)
560 self.assertEqual(visitInfo.getEra(), data.era)
562 for i, key in enumerate(("BORE-RA", "BORE-DEC")):
563 metadata = propertySetFromDict(
564 {key: data.boresightRaDec[i].asDegrees()})
565 visitInfo = afwImage.VisitInfo(metadata)
566 self.assertEqual(visitInfo.getBoresightRaDec()
567 [i], data.boresightRaDec[i])
569 for i, key in enumerate(("BORE-AZ", "BORE-ALT")):
570 metadata = propertySetFromDict(
571 {key: data.boresightAzAlt[i].asDegrees()})
572 visitInfo = afwImage.VisitInfo(metadata)
573 self.assertEqual(visitInfo.getBoresightAzAlt()
574 [i], data.boresightAzAlt[i])
576 metadata = propertySetFromDict({"BORE-AIRMASS": data.boresightAirmass})
577 visitInfo = afwImage.VisitInfo(metadata)
578 self.assertEqual(visitInfo.getBoresightAirmass(),
579 data.boresightAirmass)
581 metadata = propertySetFromDict(
582 {"BORE-ROTANG": data.boresightRotAngle.asDegrees()})
583 visitInfo = afwImage.VisitInfo(metadata)
584 self.assertEqual(visitInfo.getBoresightRotAngle(),
585 data.boresightRotAngle)
587 metadata = propertySetFromDict(
588 {"ROTTYPE": RotTypeEnumNameDict[data.rotType]})
589 visitInfo = afwImage.VisitInfo(metadata)
590 self.assertEqual(visitInfo.getRotType(), data.rotType)
592 metadata = propertySetFromDict(
593 {"OBS-LONG": data.observatory.getLongitude().asDegrees()})
594 visitInfo = afwImage.VisitInfo(metadata)
595 self.assertEqual(visitInfo.getObservatory().getLongitude(),
596 data.observatory.getLongitude())
598 metadata = propertySetFromDict(
599 {"OBS-LAT": data.observatory.getLatitude().asDegrees()})
600 visitInfo = afwImage.VisitInfo(metadata)
601 self.assertEqual(visitInfo.getObservatory().getLatitude(),
602 data.observatory.getLatitude())
604 metadata = propertySetFromDict(
605 {"OBS-ELEV": data.observatory.getElevation()})
606 visitInfo = afwImage.VisitInfo(metadata)
607 self.assertEqual(visitInfo.getObservatory().getElevation(),
608 data.observatory.getElevation())
610 metadata = propertySetFromDict(
611 {"AIRTEMP": data.weather.getAirTemperature()})
612 visitInfo = afwImage.VisitInfo(metadata)
613 self.assertEqual(visitInfo.getWeather().getAirTemperature(),
614 data.weather.getAirTemperature())
616 metadata = propertySetFromDict(
617 {"AIRPRESS": data.weather.getAirPressure()})
618 visitInfo = afwImage.VisitInfo(metadata)
619 self.assertEqual(visitInfo.getWeather().getAirPressure(),
620 data.weather.getAirPressure())
622 metadata = propertySetFromDict(
623 {"HUMIDITY": data.weather.getHumidity()})
624 visitInfo = afwImage.VisitInfo(metadata)
625 self.assertEqual(visitInfo.getWeather().getHumidity(),
626 data.weather.getHumidity())
628 metadata = propertySetFromDict({"INSTRUMENT": data.instrumentLabel})
629 visitInfo = afwImage.VisitInfo(metadata)
630 self.assertEqual(visitInfo.getInstrumentLabel(), data.instrumentLabel)
632 metadata = propertySetFromDict({"IDNUM": data.id})
633 visitInfo = afwImage.VisitInfo(metadata)
634 self.assertEqual(visitInfo.getId(), data.id)
636 metadata = propertySetFromDict({"FOCUSZ": data.focusZ})
637 visitInfo = afwImage.VisitInfo(metadata)
638 self.assertEqual(visitInfo.getFocusZ(), data.focusZ)
640 metadata = propertySetFromDict({"OBSTYPE": data.observationType})
641 visitInfo = afwImage.VisitInfo(metadata)
642 self.assertEqual(visitInfo.getObservationType(), data.observationType)
644 metadata = propertySetFromDict({"PROGRAM": data.scienceProgram})
645 visitInfo = afwImage.VisitInfo(metadata)
646 self.assertEqual(visitInfo.getScienceProgram(), data.scienceProgram)
648 metadata = propertySetFromDict({"REASON": data.observationReason})
649 visitInfo = afwImage.VisitInfo(metadata)
650 self.assertEqual(visitInfo.getObservationReason(), data.observationReason)
652 metadata = propertySetFromDict({"OBJECT": data.object})
653 visitInfo = afwImage.VisitInfo(metadata)
654 self.assertEqual(visitInfo.getObject(), data.object)
656 metadata = propertySetFromDict({"HAS-SIMULATED-CONTENT": data.hasSimulatedContent})
657 visitInfo = afwImage.VisitInfo(metadata)
658 self.assertEqual(visitInfo.getHasSimulatedContent(), data.hasSimulatedContent)
660 def testMetadataConstructorUndefined(self):
661 """Test that we can create VisitInfo using None in metadata."""
663 metadata = propertySetFromDict({
664 # These are examples of generic reader code.
665 "PROGRAM": None, # A string should convert to "".
666 "DARKTIME": None, # A missing float should convert to NaN.
667 # These headers have special logic in the reader.
668 "EXPTIME": None,
669 "IDNUM": None,
670 "DATE-AVG": None,
671 "ROTTYPE": None,
672 "HAS-SIMULATED-CONTENT": None,
673 })
674 visitInfo = afwImage.VisitInfo(metadata)
675 self.assertEqual(visitInfo.getScienceProgram(), "")
676 self.assertTrue(math.isnan(visitInfo.getDarkTime()))
677 self.assertTrue(math.isnan(visitInfo.getExposureTime()))
678 self.assertEqual(visitInfo.getId(), 0)
679 self.assertFalse(visitInfo.getDate().isValid())
681 def testConstructorKeywordArguments(self):
682 """Test VisitInfo with named arguments"""
683 data = self.data1
685 visitInfo = afwImage.VisitInfo()
686 self._testIsEmpty(visitInfo)
688 visitInfo = afwImage.VisitInfo(exposureId=data.exposureId)
689 with self.assertWarns(FutureWarning):
690 self.assertEqual(visitInfo.getExposureId(), data.exposureId)
691 self.assertTrue(math.isnan(visitInfo.getExposureTime()))
693 visitInfo = afwImage.VisitInfo(exposureTime=data.exposureTime)
694 self.assertEqual(visitInfo.getExposureTime(), data.exposureTime)
696 visitInfo = afwImage.VisitInfo(darkTime=data.darkTime)
697 self.assertEqual(visitInfo.getDarkTime(), data.darkTime)
699 visitInfo = afwImage.VisitInfo(date=data.date)
700 self.assertEqual(visitInfo.getDate(), data.date)
702 visitInfo = afwImage.VisitInfo(ut1=data.ut1)
703 self.assertEqual(visitInfo.getUt1(), data.ut1)
705 visitInfo = afwImage.VisitInfo(era=data.era)
706 self.assertEqual(visitInfo.getEra(), data.era)
708 visitInfo = afwImage.VisitInfo(boresightRaDec=data.boresightRaDec)
709 self.assertEqual(visitInfo.getBoresightRaDec(), data.boresightRaDec)
711 visitInfo = afwImage.VisitInfo(boresightAzAlt=data.boresightAzAlt)
712 self.assertEqual(visitInfo.getBoresightAzAlt(), data.boresightAzAlt)
714 visitInfo = afwImage.VisitInfo(boresightAirmass=data.boresightAirmass)
715 self.assertEqual(visitInfo.getBoresightAirmass(),
716 data.boresightAirmass)
718 visitInfo = afwImage.VisitInfo(
719 boresightRotAngle=data.boresightRotAngle)
720 self.assertEqual(visitInfo.getBoresightRotAngle(),
721 data.boresightRotAngle)
723 visitInfo = afwImage.VisitInfo(rotType=data.rotType)
724 self.assertEqual(visitInfo.getRotType(), data.rotType)
726 visitInfo = afwImage.VisitInfo(observatory=data.observatory)
727 self.assertEqual(visitInfo.getObservatory(), data.observatory)
729 visitInfo = afwImage.VisitInfo(weather=data.weather)
730 self.assertEqual(visitInfo.getWeather(), data.weather)
732 visitInfo = afwImage.VisitInfo(instrumentLabel=data.instrumentLabel)
733 self.assertEqual(visitInfo.getInstrumentLabel(), data.instrumentLabel)
735 visitInfo = afwImage.VisitInfo(id=data.id)
736 self.assertEqual(visitInfo.getId(), data.id)
738 visitInfo = afwImage.VisitInfo(focusZ=data.focusZ)
739 self.assertEqual(visitInfo.getFocusZ(), data.focusZ)
741 visitInfo = afwImage.VisitInfo(observationType=data.observationType)
742 self.assertEqual(visitInfo.getObservationType(), data.observationType)
744 visitInfo = afwImage.VisitInfo(scienceProgram=data.scienceProgram)
745 self.assertEqual(visitInfo.getScienceProgram(), data.scienceProgram)
747 visitInfo = afwImage.VisitInfo(observationReason=data.observationReason)
748 self.assertEqual(visitInfo.getObservationReason(), data.observationReason)
750 visitInfo = afwImage.VisitInfo(object=data.object)
751 self.assertEqual(visitInfo.getObject(), data.object)
753 visitInfo = afwImage.VisitInfo(hasSimulatedContent=data.hasSimulatedContent)
754 self.assertEqual(visitInfo.getHasSimulatedContent(), data.hasSimulatedContent)
756 def testGoodRotTypes(self):
757 """Test round trip of all valid rot types"""
758 for rotType in RotTypeEnumNameDict:
759 metadata = propertySetFromDict(
760 {"ROTTYPE": RotTypeEnumNameDict[rotType]})
761 visitInfo = afwImage.VisitInfo(metadata)
762 self.assertEqual(visitInfo.getRotType(), rotType)
764 def testBadRotTypes(self):
765 """Test that invalid rot type names cannot be used to construct a VisitInfo"""
766 for badRotTypeName in (
767 "unknown", # must be all uppercase
768 "sky", # must be all uppercase
769 "Sky", # must be all uppercase
770 "SKY1", # extra chars
771 "HORIZONTAL", # extra chars
772 ):
773 metadata = propertySetFromDict({"ROTTYPE": badRotTypeName})
774 with self.assertRaises(lsst.pex.exceptions.RuntimeError):
775 afwImage.VisitInfo(metadata)
777 def test_str(self):
778 """Check that we get something reasonable for str()"""
779 visitInfo = makeVisitInfo(self.data1)
780 string = str(visitInfo)
781 self.assertEqual(string,
782 "VisitInfo(exposureId=10313423, exposureTime=10.01, darkTime=11.02, "
783 "date=2037-09-20T02:24:00.000000000, UT1=12345.1, ERA=0.787143 rad, "
784 "boresightRaDec=(23.1000000000, +73.2000000000), "
785 "boresightAzAlt=(134.5000000000, +33.3000000000), boresightAirmass=1.73, "
786 "boresightRotAngle=1.27758 rad, rotType=1, observatory=22.2N, 11.1E 0.333, "
787 "weather=Weather(1.1, 2.2, 34.5), instrumentLabel='TestCameraOne', "
788 "id=987654, focusZ=1.5, observationType='flat', scienceProgram='test program', "
789 "observationReason='test reason', object='test object', hasSimulatedContent=false)")
791 def testParallacticAngle(self):
792 """Check that we get the same precomputed values for parallactic angle."""
793 parallacticAngle = [141.39684140703142*degrees, 76.99982166973487*degrees]
794 for item, parAngle in zip((self.data1, self.data2), parallacticAngle):
795 visitInfo = afwImage.VisitInfo(era=item.era,
796 boresightRaDec=item.boresightRaDec,
797 observatory=item.observatory,
798 )
799 self.assertAnglesAlmostEqual(visitInfo.getBoresightParAngle(), parAngle)
801 def testParallacticAngleNorthMeridian(self):
802 """An observation on the Meridian that is North of zenith has a parallactic angle of pi radians."""
803 meridianBoresightRA = self.data1.era + self.data1.observatory.getLongitude()
804 northBoresightDec = self.data1.observatory.getLatitude() + 10.*degrees
805 visitInfo = afwImage.VisitInfo(era=self.data1.era,
806 boresightRaDec=SpherePoint(meridianBoresightRA,
807 northBoresightDec),
808 observatory=self.data1.observatory,
809 )
810 self.assertAnglesAlmostEqual(visitInfo.getBoresightParAngle(), Angle(np.pi))
812 def testParallacticAngleSouthMeridian(self):
813 """An observation on the Meridian that is South of zenith has a parallactic angle of zero."""
814 meridianBoresightRA = self.data1.era + self.data1.observatory.getLongitude()
815 southBoresightDec = self.data1.observatory.getLatitude() - 10.*degrees
816 visitInfo = afwImage.VisitInfo(era=self.data1.era,
817 boresightRaDec=SpherePoint(meridianBoresightRA,
818 southBoresightDec),
819 observatory=self.data1.observatory,
820 )
821 self.assertAnglesAlmostEqual(visitInfo.getBoresightParAngle(), Angle(0.))
824def setup_module(module):
825 lsst.utils.tests.init()
828class MemoryTester(lsst.utils.tests.MemoryTestCase):
829 pass
832if __name__ == "__main__": 832 ↛ 833line 832 didn't jump to line 833, because the condition on line 832 was never true
833 lsst.utils.tests.init()
834 unittest.main()