def getExposure(self): """Construct a test exposure. The test exposure has a simple WCS set, as well as a list of unlikely header keywords that can be removed during ISR processing to exercise that code. Returns ------- exposure : `lsst.afw.exposure.Exposure` Construct exposure containing masked image of the appropriate size. """ camera = self.getCamera() detector = camera[self.config.detectorIndex] image = afwUtils.makeImageFromCcd(detector, isTrimmed=self.config.isTrimmed, showAmpGain=False, rcMarkSize=0, binSize=1, imageFactory=afwImage.ImageF) var = afwImage.ImageF(image.getDimensions()) mask = afwImage.Mask(image.getDimensions()) image.assign(0.0) maskedImage = afwImage.makeMaskedImage(image, mask, var) exposure = afwImage.makeExposure(maskedImage) exposure.setDetector(detector) exposure.setWcs(self.getWcs()) visitInfo = afwImage.VisitInfo(exposureTime=self.config.expTime, darkTime=self.config.darkTime) exposure.getInfo().setVisitInfo(visitInfo) metadata = exposure.getMetadata() metadata.add("SHEEP", 7.3, "number of sheep on farm") metadata.add("MONKEYS", 155, "monkeys per tree") metadata.add("VAMPIRES", 4, "How scary are vampires.") ccd = exposure.getDetector() newCcd = ccd.rebuild() newCcd.clear() for amp in ccd: newAmp = amp.rebuild() newAmp.setLinearityCoeffs((0., 1., 0., 0.)) newAmp.setLinearityType("Polynomial") newAmp.setGain(self.config.gain) newAmp.setSuspectLevel(25000.0) newAmp.setSaturation(32000.0) newCcd.append(newAmp) exposure.setDetector(newCcd.finish()) exposure.image.array[:] = np.zeros(exposure.getImage().getDimensions()).transpose() exposure.mask.array[:] = np.zeros(exposure.getMask().getDimensions()).transpose() exposure.variance.array[:] = np.zeros(exposure.getVariance().getDimensions()).transpose() return exposure
def testParallacticAngle(self): """Check that we get the same precomputed values for parallactic angle.""" parallacticAngle = [141.39684140703142*degrees, 76.99982166973487*degrees] for item, parAngle in zip((self.data1, self.data2), parallacticAngle): visitInfo = afwImage.VisitInfo(era=item.era, boresightRaDec=item.boresightRaDec, observatory=item.observatory, ) self.assertAnglesAlmostEqual(visitInfo.getBoresightParAngle(), parAngle)
def testTablePersistence(self): for item in (self.data1, self.data2): tablePath = os.path.join( self.testDir, "testVisitInfo_testTablePersistence.fits") v1 = afwImage.VisitInfo(*item) v1.writeFits(tablePath) v2 = afwImage.VisitInfo.readFits(tablePath) self.assertEqual(v1, v2) os.unlink(tablePath)
def testParallacticAngleSouthMeridian(self): """An observation on the Meridian that is South of zenith has a parallactic angle of zero.""" meridianBoresightRA = self.data1.era + self.data1.observatory.getLongitude() southBoresightDec = self.data1.observatory.getLatitude() - 10.*degrees visitInfo = afwImage.VisitInfo(era=self.data1.era, boresightRaDec=SpherePoint(meridianBoresightRA, southBoresightDec), observatory=self.data1.observatory, ) self.assertAnglesAlmostEqual(visitInfo.getBoresightParAngle(), Angle(0.))
def testStripVisitInfoKeywords(self): for argList in (self.data1, self.data2): visitInfo = afwImage.VisitInfo(*argList) metadata = PropertyList() afwImage.setVisitInfoMetadata(metadata, visitInfo) # add an extra keyword that will not be stripped metadata.set("EXTRA", 5) self.assertEqual(metadata.nameCount(), 21) afwImage.stripVisitInfoKeywords(metadata) self.assertEqual(metadata.nameCount(), 1)
def setUp(self): np.random.seed(1234) self.cutoutSize = 35 self.center = lsst.geom.Point2D(50.1, 49.8) self.bbox = lsst.geom.Box2I(lsst.geom.Point2I(-20, -30), lsst.geom.Extent2I(140, 160)) self.dataset = lsst.meas.base.tests.TestDataset(self.bbox) self.dataset.addSource(100000.0, self.center) exposure, catalog = self.dataset.realize( 10.0, self.dataset.makeMinimalSchema(), randomSeed=0) self.exposure = exposure detector = DetectorWrapper(id=23, bbox=exposure.getBBox()).detector self.exposure.setDetector(detector) visit = afwImage.VisitInfo(exposureId=1234, exposureTime=200., date=dafBase.DateTime( "2014-05-13T17:00:00.000000000", dafBase.DateTime.Timescale.TAI)) self.exposure.getInfo().setVisitInfo(visit) self.filter_names = ["g"] afwImageUtils.resetFilters() afwImageUtils.defineFilter('g', lambdaEff=487, alias="g.MP9401") self.exposure.setFilter(afwImage.Filter('g')) diaObjects = makeDiaObjects(2, self.exposure) diaSourceHistory = makeDiaSources(10, diaObjects["diaObjectId"], self.exposure) diaForcedSources = makeDiaForcedSources(10, diaObjects["diaObjectId"], self.exposure) self.diaObjects, diaSourceHistory, self.diaForcedSources = _roundTripThroughApdb( diaObjects, diaSourceHistory, diaForcedSources, self.exposure.getInfo().getVisitInfo().getDate().toPython()) self.diaObjects.replace(to_replace=[None], value=np.nan, inplace=True) diaSourceHistory.replace(to_replace=[None], value=np.nan, inplace=True) self.diaForcedSources.replace(to_replace=[None], value=np.nan, inplace=True) diaSourceHistory["programId"] = 0 self.diaSources = diaSourceHistory.loc[[(0, "g", 8), (1, "g", 9)], :] self.diaSources["bboxSize"] = self.cutoutSize self.diaSourceHistory = diaSourceHistory.drop(labels=[(0, "g", 8), (1, "g", 9)]) self.cutoutWcs = wcs.WCS(naxis=2) self.cutoutWcs.wcs.crpix = [self.center[0], self.center[1]] self.cutoutWcs.wcs.crval = [ self.exposure.getWcs().getSkyOrigin().getRa().asDegrees(), self.exposure.getWcs().getSkyOrigin().getDec().asDegrees() ] self.cutoutWcs.wcs.cd = self.exposure.getWcs().getCdMatrix() self.cutoutWcs.wcs.ctype = ["RA---TAN", "DEC--TAN"]
def testSetVisitInfoMetadata(self): for item in (self.data1, self.data2): visitInfo = afwImage.VisitInfo( item.exposureId, item.exposureTime, item.darkTime, item.date, item.ut1, item.era, item.boresightRaDec, item.boresightAzAlt, item.boresightAirmass, item.boresightRotAngle, item.rotType, item.observatory, item.weather, ) metadata = PropertyList() afwImage.setVisitInfoMetadata(metadata, visitInfo) self.assertEqual(metadata.nameCount(), 20) self.assertEqual(metadata.getScalar("EXPID"), item.exposureId) self.assertEqual(metadata.getScalar("EXPTIME"), item.exposureTime) self.assertEqual(metadata.getScalar("DARKTIME"), item.darkTime) self.assertEqual(metadata.getScalar("DATE-AVG"), item.date.toString(DateTime.TAI)) self.assertEqual(metadata.getScalar("TIMESYS"), "TAI") self.assertEqual(metadata.getScalar("MJD-AVG-UT1"), item.ut1) self.assertEqual(metadata.getScalar("AVG-ERA"), item.era.asDegrees()) self.assertEqual(metadata.getScalar("BORE-RA"), item.boresightRaDec[0].asDegrees()) self.assertEqual(metadata.getScalar("BORE-DEC"), item.boresightRaDec[1].asDegrees()) self.assertEqual(metadata.getScalar("BORE-AZ"), item.boresightAzAlt[0].asDegrees()) self.assertEqual(metadata.getScalar("BORE-ALT"), item.boresightAzAlt[1].asDegrees()) self.assertEqual(metadata.getScalar("BORE-AIRMASS"), item.boresightAirmass) self.assertEqual(metadata.getScalar("BORE-ROTANG"), item.boresightRotAngle.asDegrees()) self.assertEqual(metadata.getScalar("ROTTYPE"), RotTypeEnumNameDict[item.rotType]) self.assertEqual(metadata.getScalar("OBS-LONG"), item.observatory.getLongitude().asDegrees()) self.assertEqual(metadata.getScalar("OBS-LAT"), item.observatory.getLatitude().asDegrees()) self.assertEqual(metadata.getScalar("OBS-ELEV"), item.observatory.getElevation()) self.assertEqual(metadata.getScalar("AIRTEMP"), item.weather.getAirTemperature()) self.assertEqual(metadata.getScalar("AIRPRESS"), item.weather.getAirPressure()) self.assertEqual(metadata.getScalar("HUMIDITY"), item.weather.getHumidity())
def testTablePersistence(self): """Test that VisitInfo can be round-tripped with current code. """ for item in (self.data1, self.data2): tablePath = os.path.join( self.testDir, "testVisitInfo_testTablePersistence.fits") v1 = afwImage.VisitInfo(*item) v1.writeFits(tablePath) v2 = afwImage.VisitInfo.readFits(tablePath) self.assertEqual(v1, v2) os.unlink(tablePath)
def std_dark(self, item, dataId): """Standardiation of master dark frame. Must only be called on master darks. @param[in,out] item the master dark, as an image-like object @param[in] dataId unused """ exp = exposureFromImage(item) if not exp.getInfo().hasVisitInfo(): # hard-coded, but pipe_drivers always(?) normalises darks to a darktime of 1s so this is OK? exp.getInfo().setVisitInfo(afwImage.VisitInfo(darkTime=1.0)) return exp
def test_str(self): """Check that we get something reasonable for str()""" visitInfo = makeVisitInfo(self.data1) string = str(visitInfo) self.assertIn("exposureId=10313423", string) self.assertIn("exposureTime=10.01", string) self.assertIn("darkTime=11.02", string) self.assertIn("rotType=1", string) # Check that it at least doesn't throw str(afwImage.VisitInfo())
def testBadRotTypes(self): """Test that invalid rot type names cannot be used to construct a VisitInfo""" for badRotTypeName in ( "unknown", # must be all uppercase "sky", # must be all uppercase "Sky", # must be all uppercase "SKY1", # extra chars "HORIZONTAL", # extra chars ): metadata = propertySetFromDict({"ROTTYPE": badRotTypeName}) with self.assertRaises(lsst.pex.exceptions.RuntimeError): afwImage.VisitInfo(metadata)
def loadExposure(self, sensorRef): """Load SDSS data as a post-ISR exposure - Image is from fpC - Mask is from fpM - Wcs is from asTrans - PhotoCalib is from tsField - Psf is from psField """ originalExp = sensorRef.get("fpC").convertF() image = originalExp.getMaskedImage().getImage() if self.config.removePedestal: image -= self.config.pedestalVal mask = sensorRef.get("fpM") wcs = sensorRef.get("asTrans") tsField = sensorRef.get("tsField") photoCalib = tsField.photoCalib gain = tsField.gain var = afwImage.ImageF(image, True) var /= gain mi = afwImage.MaskedImageF(image, mask, var) if self.config.removeOverlap: bbox = mi.getBBox() begin = bbox.getBegin() extent = bbox.getDimensions() extent -= geom.Extent2I(0, self.config.overlapSize) tbbox = geom.BoxI(begin, extent) mi = afwImage.MaskedImageF(mi, tbbox) exposure = afwImage.ExposureF(mi, wcs) expInfo = exposure.getInfo() expInfo.setPhotoCalib(photoCalib) camera = sensorRef.get('camera') detector = camera["%(filter)s%(camcol)d" % sensorRef.dataId] expInfo.setDetector(detector) expInfo.setFilter(afwImage.Filter(sensorRef.dataId['filter'])) visitInfo = afwImage.VisitInfo( exposureTime=tsField.exptime, date=tsField.dateAvg, boresightAirmass=tsField.airmass, ) expInfo.setVisitInfo(visitInfo) # Install the SDSS PSF here; if we want to overwrite it later, we can. psf = sensorRef.get('psField') exposure.setPsf(psf) return exposure
def setUp(self): # CFHT Filters from the camera mapper. afwImageUtils.resetFilters() afwImageUtils.defineFilter('u', lambdaEff=374, alias="u.MP9301") afwImageUtils.defineFilter('g', lambdaEff=487, alias="g.MP9401") afwImageUtils.defineFilter('r', lambdaEff=628, alias="r.MP9601") afwImageUtils.defineFilter('i', lambdaEff=778, alias="i.MP9701") afwImageUtils.defineFilter('z', lambdaEff=1170, alias="z.MP9801") self.metadata = dafBase.PropertySet() self.metadata.set("SIMPLE", "T") self.metadata.set("BITPIX", -32) self.metadata.set("NAXIS", 2) self.metadata.set("NAXIS1", 1024) self.metadata.set("NAXIS2", 1153) self.metadata.set("RADECSYS", 'FK5') self.metadata.set("EQUINOX", 2000.) self.metadata.setDouble("CRVAL1", 215.604025685476) self.metadata.setDouble("CRVAL2", 53.1595451514076) self.metadata.setDouble("CRPIX1", 1109.99981456774) self.metadata.setDouble("CRPIX2", 560.018167811613) self.metadata.set("CTYPE1", 'RA---SIN') self.metadata.set("CTYPE2", 'DEC--SIN') self.metadata.setDouble("CD1_1", 5.10808596133527E-05) self.metadata.setDouble("CD1_2", 1.85579539217196E-07) self.metadata.setDouble("CD2_2", -5.10281493481982E-05) self.metadata.setDouble("CD2_1", -8.27440751733828E-07) self.wcs = afwGeom.makeSkyWcs(self.metadata) self.exposure = afwImage.makeExposure( afwImage.makeMaskedImageFromArrays(np.ones((1024, 1153))), self.wcs) detector = DetectorWrapper(id=23, bbox=self.exposure.getBBox()).detector visit = afwImage.VisitInfo(exposureId=4321, exposureTime=200., date=dafBase.DateTime(nsecs=1400000000 * 10**9)) self.exposure.setDetector(detector) self.exposure.getInfo().setVisitInfo(visit) self.exposure.setFilter(afwImage.Filter('g')) scale = 2 scaleErr = 1 self.photoCalib = afwImage.PhotoCalib(scale, scaleErr) self.exposure.setPhotoCalib(self.photoCalib) self.inputCatalogNoFlags = make_input_source_catalog(10, False) self.inputCatalog = make_input_source_catalog(10, True)
def makeDark(darkval, exptime): '''!Make a dark exposure in DN @param[in] darkval -- dark current in e-/sec @param[in] exptime -- exposure time of the dark frame @return an assembled dark exposure ''' darkExposure = makeAssemblyInput(False, doTrim=True) detector = darkExposure.getDetector() visitInfo = afwImage.VisitInfo(exposureTime=exptime) darkExposure.getInfo().setVisitInfo(visitInfo) im = darkExposure.getMaskedImage().getImage() for amp in detector: subim = im.Factory(im, amp.getBBox()) subim.set(darkval*exptime/amp.getGain()) return darkExposure
def testDarkWithDarktime(self): darkTime = 128.0 nan = float("NAN") exp = afwImage.ExposureF(1, 1) exp.getMaskedImage().getImage().set(1.0) exp.getMaskedImage().getMask().set(0) exp.getMaskedImage().getVariance().set(1.0) dark = afwImage.ExposureF(1, 1) dark.getMaskedImage().getImage().set(1.0 / darkTime) dark.getMaskedImage().getMask().set(0) dark.getMaskedImage().getVariance().set(0.0) dark.getInfo().setVisitInfo(afwImage.VisitInfo()) task = ipIsr.IsrTask() # No darktime set in at least one of the inputs exp.getInfo().setVisitInfo(afwImage.VisitInfo(darkTime=nan)) dark.getInfo().setVisitInfo(afwImage.VisitInfo(darkTime=nan)) with self.assertRaises(RuntimeError): task.darkCorrection(exp, dark) exp.getInfo().setVisitInfo(afwImage.VisitInfo(darkTime=nan)) dark.getInfo().setVisitInfo(afwImage.VisitInfo(darkTime=1.0)) with self.assertRaises(RuntimeError): task.darkCorrection(exp, dark) exp.getInfo().setVisitInfo(afwImage.VisitInfo(darkTime=1.0)) dark.getInfo().setVisitInfo(afwImage.VisitInfo(darkTime=nan)) with self.assertRaises(RuntimeError): task.darkCorrection(exp, dark) # With darktime set exp.getInfo().setVisitInfo(afwImage.VisitInfo(darkTime=darkTime)) dark.getInfo().setVisitInfo(afwImage.VisitInfo(darkTime=1.0)) task.darkCorrection(exp, dark) self.assertEqual(exp.getMaskedImage().getImage().get(0, 0), 0.0) self.assertEqual(exp.getMaskedImage().getMask().get(0, 0), 0) self.assertEqual(exp.getMaskedImage().getVariance().get(0, 0), 1.0) self.assertEqual(exp.getInfo().getVisitInfo().getDarkTime(), darkTime) # Hasn't been modified
def makeVisitInfo(): """Return a non-NaN visitInfo.""" return afwImage.VisitInfo(exposureId=10313423, exposureTime=10.01, darkTime=11.02, date=dafBase.DateTime(65321.1, dafBase.DateTime.MJD, dafBase.DateTime.TAI), ut1=12345.1, era=45.1*afwGeom.degrees, boresightRaDec=afwGeom.SpherePoint(23.1, 73.2, afwGeom.degrees), boresightAzAlt=afwGeom.SpherePoint(134.5, 33.3, afwGeom.degrees), boresightAirmass=1.73, boresightRotAngle=73.2*afwGeom.degrees, rotType=afwImage.RotType.SKY, observatory=Observatory(11.1*afwGeom.degrees, 22.2*afwGeom.degrees, 0.333), weather=Weather(1.1, 2.2, 34.5), )
def setUp(self): super().setUp() # Explicit date calculation to avoid errors from misuse of time libraries. mjd = 57071.0 self.utc_jd = mjd + 2_400_000.5 - 35.0 / (24.0 * 60.0 * 60.0) self.visitId = 42 self.visitInfo = afwImage.VisitInfo( # Incomplete VisitInfo; Python constructor allows any value to # be defaulted. exposureTime=30.0, darkTime=3.0, date=dafBase.DateTime(mjd, system=dafBase.DateTime.MJD), boresightRaDec=geom.SpherePoint(0.0, 0.0, geom.degrees), )
def makeVisitInfo(data): """Return a VisitInfo constructed from a VisitInfoData namedtuple.""" return afwImage.VisitInfo(data.exposureId, data.exposureTime, data.darkTime, data.date, data.ut1, data.era, data.boresightRaDec, data.boresightAzAlt, data.boresightAirmass, data.boresightRotAngle, data.rotType, data.observatory, data.weather, data.instrumentLabel )
def computeApproxPixelAreaFields(camera): """ Compute the approximate pixel area bounded fields from the camera geometry. Parameters ---------- camera: `lsst.afw.cameraGeom.Camera` Returns ------- approxPixelAreaFields: `dict` Dictionary of approximate area fields, keyed with detector ID """ areaScaling = 1. / computeReferencePixelScale(camera)**2. # Generate fake WCSs centered at 180/0 to avoid the RA=0/360 problem, # since we are looking for relative scales boresight = geom.SpherePoint(180.0*geom.degrees, 0.0*geom.degrees) flipX = False # Create a temporary visitInfo for input to createInitialSkyWcs # The orientation does not matter for the area computation visitInfo = afwImage.VisitInfo(boresightRaDec=boresight, boresightRotAngle=0.0*geom.degrees, rotType=afwImage.visitInfo.RotType.SKY) approxPixelAreaFields = {} for i, detector in enumerate(camera): key = detector.getId() wcs = createInitialSkyWcs(visitInfo, detector, flipX) bbox = detector.getBBox() areaField = afwMath.PixelAreaBoundedField(bbox, wcs, unit=geom.arcseconds, scaling=areaScaling) approxAreaField = afwMath.ChebyshevBoundedField.approximate(areaField) approxPixelAreaFields[key] = approxAreaField return approxPixelAreaFields
def test_str(self): """Check that we get something reasonable for str()""" visitInfo = afwImage.VisitInfo(self.data1.exposureId, self.data1.exposureTime, self.data1.darkTime, self.data1.date, self.data1.ut1, self.data1.era, self.data1.boresightRaDec, self.data1.boresightAzAlt, self.data1.boresightAirmass, self.data1.boresightRotAngle, self.data1.rotType, self.data1.observatory, self.data1.weather, ) string = str(visitInfo) self.assertIn("exposureId=10313423", string) self.assertIn("exposureTime=10.01", string) self.assertIn("darkTime=11.02", string) self.assertIn("rotType=1", string)
def bypass_raw_visitInfo(self, datasetType, pythonType, location, dataId): if False: # afwImage.readMetadata() doesn't honour [hdu] suffixes in filenames # # We could workaround this by moving the "else" block into obs_base, # or by changing afw # return self.bypass__raw_visitInfo(datasetType, pythonType, location, dataId) else: import re import lsst.afw.image as afwImage fileName = location.getLocationsWithRoot()[0] mat = re.search(r"\[(\d+)\]$", fileName) if mat: hdu = int(mat.group(1)) md = afwImage.readMetadata(fileName, hdu=hdu) else: md = afwImage.readMetadata(fileName) # or hdu = INT_MIN; -(1 << 31) return afwImage.VisitInfo(md)
def _initMappings(self, policy, rootStorage=None, calibStorage=None, provided=None, use_default=True): """Initialize mappings For each of the dataset types that we want to be able to read, there are methods that can be created to support them: * map_<dataset> : determine the path for dataset * std_<dataset> : standardize the retrieved dataset * bypass_<dataset> : retrieve the dataset (bypassing the usual retrieval machinery) * query_<dataset> : query the registry Besides the dataset types explicitly listed in the policy, we create additional, derived datasets for additional conveniences, e.g., reading the header of an image, retrieving only the size of a catalog. Parameters ---------- policy : `lsst.daf.persistence.Policy` Policy with per-camera defaults already merged rootStorage : `Storage subclass instance` Interface to persisted repository data. calibRoot : `Storage subclass instance` Interface to persisted calib repository data provided : `list` of `str` Keys provided by the mapper use_default : `bool` Load default camera mappings """ # Sub-dictionaries (for exposure/calibration/dataset types) imgMappingPolicy = dafPersist.Policy(dafPersist.Policy.defaultPolicyFile( "obs_base", "ImageMappingDictionary.paf", "policy")) expMappingPolicy = dafPersist.Policy(dafPersist.Policy.defaultPolicyFile( "obs_base", "ExposureMappingDictionary.paf", "policy")) calMappingPolicy = dafPersist.Policy(dafPersist.Policy.defaultPolicyFile( "obs_base", "CalibrationMappingDictionary.paf", "policy")) dsMappingPolicy = dafPersist.Policy(dafPersist.Policy.defaultPolicyFile( "obs_base", "DatasetMappingDictionary.paf", "policy")) # Mappings mappingList = ( ("images", imgMappingPolicy, ImageMapping), ("exposures", expMappingPolicy, ExposureMapping), ("calibrations", calMappingPolicy, CalibrationMapping), ("datasets", dsMappingPolicy, DatasetMapping) ) self.mappings = dict() for name, defPolicy, cls in mappingList: if name in policy: datasets = policy[name] # Centrally-defined datasets defaultsPath = os.path.join(getPackageDir("obs_base"), "policy", name + ".yaml") if os.path.exists(defaultsPath) and use_default: datasets.merge(dafPersist.Policy(defaultsPath)) mappings = dict() setattr(self, name, mappings) for datasetType in datasets.names(True): subPolicy = datasets[datasetType] subPolicy.merge(defPolicy) if not hasattr(self, "map_" + datasetType) and 'composite' in subPolicy: def compositeClosure(dataId, write=False, mapper=None, mapping=None, subPolicy=subPolicy): components = subPolicy.get('composite') assembler = subPolicy['assembler'] if 'assembler' in subPolicy else None disassembler = subPolicy['disassembler'] if 'disassembler' in subPolicy else None python = subPolicy['python'] butlerComposite = dafPersist.ButlerComposite(assembler=assembler, disassembler=disassembler, python=python, dataId=dataId, mapper=self) for name, component in components.items(): butlerComposite.add(id=name, datasetType=component.get('datasetType'), setter=component.get('setter', None), getter=component.get('getter', None), subset=component.get('subset', False), inputOnly=component.get('inputOnly', False)) return butlerComposite setattr(self, "map_" + datasetType, compositeClosure) # for now at least, don't set up any other handling for this dataset type. continue if name == "calibrations": mapping = cls(datasetType, subPolicy, self.registry, self.calibRegistry, calibStorage, provided=provided, dataRoot=rootStorage) else: mapping = cls(datasetType, subPolicy, self.registry, rootStorage, provided=provided) self.keyDict.update(mapping.keys()) mappings[datasetType] = mapping self.mappings[datasetType] = mapping if not hasattr(self, "map_" + datasetType): def mapClosure(dataId, write=False, mapper=weakref.proxy(self), mapping=mapping): return mapping.map(mapper, dataId, write) setattr(self, "map_" + datasetType, mapClosure) if not hasattr(self, "query_" + datasetType): def queryClosure(format, dataId, mapping=mapping): return mapping.lookup(format, dataId) setattr(self, "query_" + datasetType, queryClosure) if hasattr(mapping, "standardize") and not hasattr(self, "std_" + datasetType): def stdClosure(item, dataId, mapper=weakref.proxy(self), mapping=mapping): return mapping.standardize(mapper, item, dataId) setattr(self, "std_" + datasetType, stdClosure) def setMethods(suffix, mapImpl=None, bypassImpl=None, queryImpl=None): """Set convenience methods on CameraMapper""" mapName = "map_" + datasetType + "_" + suffix bypassName = "bypass_" + datasetType + "_" + suffix queryName = "query_" + datasetType + "_" + suffix if not hasattr(self, mapName): setattr(self, mapName, mapImpl or getattr(self, "map_" + datasetType)) if not hasattr(self, bypassName): if bypassImpl is None and hasattr(self, "bypass_" + datasetType): bypassImpl = getattr(self, "bypass_" + datasetType) if bypassImpl is not None: setattr(self, bypassName, bypassImpl) if not hasattr(self, queryName): setattr(self, queryName, queryImpl or getattr(self, "query_" + datasetType)) # Filename of dataset setMethods("filename", bypassImpl=lambda datasetType, pythonType, location, dataId: [os.path.join(location.getStorage().root, p) for p in location.getLocations()]) # Metadata from FITS file if subPolicy["storage"] == "FitsStorage": # a FITS image setMethods("md", bypassImpl=lambda datasetType, pythonType, location, dataId: readMetadata(location.getLocationsWithRoot()[0])) # Add support for configuring FITS compression addName = "add_" + datasetType if not hasattr(self, addName): setattr(self, addName, self.getImageCompressionSettings) if name == "exposures": setMethods("wcs", bypassImpl=lambda datasetType, pythonType, location, dataId: afwGeom.makeSkyWcs(readMetadata(location.getLocationsWithRoot()[0]))) setMethods("calib", bypassImpl=lambda datasetType, pythonType, location, dataId: afwImage.Calib(readMetadata(location.getLocationsWithRoot()[0]))) setMethods("visitInfo", bypassImpl=lambda datasetType, pythonType, location, dataId: afwImage.VisitInfo(readMetadata(location.getLocationsWithRoot()[0]))) setMethods("filter", bypassImpl=lambda datasetType, pythonType, location, dataId: afwImage.Filter(readMetadata(location.getLocationsWithRoot()[0]))) setMethods("detector", mapImpl=lambda dataId, write=False: dafPersist.ButlerLocation( pythonType="lsst.afw.cameraGeom.CameraConfig", cppType="Config", storageName="Internal", locationList="ignored", dataId=dataId, mapper=self, storage=None, ), bypassImpl=lambda datasetType, pythonType, location, dataId: self.camera[self._extractDetectorName(dataId)] ) setMethods("bbox", bypassImpl=lambda dsType, pyType, location, dataId: afwImage.bboxFromMetadata( readMetadata(location.getLocationsWithRoot()[0], hdu=1))) elif name == "images": setMethods("bbox", bypassImpl=lambda dsType, pyType, location, dataId: afwImage.bboxFromMetadata( readMetadata(location.getLocationsWithRoot()[0]))) if subPolicy["storage"] == "FitsCatalogStorage": # a FITS catalog setMethods("md", bypassImpl=lambda datasetType, pythonType, location, dataId: readMetadata(os.path.join(location.getStorage().root, location.getLocations()[0]), hdu=1)) # Sub-images if subPolicy["storage"] == "FitsStorage": def mapSubClosure(dataId, write=False, mapper=weakref.proxy(self), mapping=mapping): subId = dataId.copy() del subId['bbox'] loc = mapping.map(mapper, subId, write) bbox = dataId['bbox'] llcX = bbox.getMinX() llcY = bbox.getMinY() width = bbox.getWidth() height = bbox.getHeight() loc.additionalData.set('llcX', llcX) loc.additionalData.set('llcY', llcY) loc.additionalData.set('width', width) loc.additionalData.set('height', height) if 'imageOrigin' in dataId: loc.additionalData.set('imageOrigin', dataId['imageOrigin']) return loc def querySubClosure(key, format, dataId, mapping=mapping): subId = dataId.copy() del subId['bbox'] return mapping.lookup(format, subId) setMethods("sub", mapImpl=mapSubClosure, queryImpl=querySubClosure) if subPolicy["storage"] == "FitsCatalogStorage": # Length of catalog setMethods("len", bypassImpl=lambda datasetType, pythonType, location, dataId: readMetadata(os.path.join(location.getStorage().root, location.getLocations()[0]), hdu=1).get("NAXIS2")) # Schema of catalog if not datasetType.endswith("_schema") and datasetType + "_schema" not in datasets: setMethods("schema", bypassImpl=lambda datasetType, pythonType, location, dataId: afwTable.Schema.readFits(os.path.join(location.getStorage().root, location.getLocations()[0])))
def testConstructorKeywordArguments(self): """Test VisitInfo with named arguments""" data = self.data1 visitInfo = afwImage.VisitInfo() self._testIsEmpty(visitInfo) visitInfo = afwImage.VisitInfo(exposureId=data.exposureId) self.assertEqual(visitInfo.getExposureId(), data.exposureId) self.assertTrue(math.isnan(visitInfo.getExposureTime())) visitInfo = afwImage.VisitInfo(exposureTime=data.exposureTime) self.assertEqual(visitInfo.getExposureTime(), data.exposureTime) visitInfo = afwImage.VisitInfo(darkTime=data.darkTime) self.assertEqual(visitInfo.getDarkTime(), data.darkTime) visitInfo = afwImage.VisitInfo(date=data.date) self.assertEqual(visitInfo.getDate(), data.date) visitInfo = afwImage.VisitInfo(ut1=data.ut1) self.assertEqual(visitInfo.getUt1(), data.ut1) visitInfo = afwImage.VisitInfo(era=data.era) self.assertEqual(visitInfo.getEra(), data.era) visitInfo = afwImage.VisitInfo(boresightRaDec=data.boresightRaDec) self.assertEqual(visitInfo.getBoresightRaDec(), data.boresightRaDec) visitInfo = afwImage.VisitInfo(boresightAzAlt=data.boresightAzAlt) self.assertEqual(visitInfo.getBoresightAzAlt(), data.boresightAzAlt) visitInfo = afwImage.VisitInfo(boresightAirmass=data.boresightAirmass) self.assertEqual(visitInfo.getBoresightAirmass(), data.boresightAirmass) visitInfo = afwImage.VisitInfo( boresightRotAngle=data.boresightRotAngle) self.assertEqual(visitInfo.getBoresightRotAngle(), data.boresightRotAngle) visitInfo = afwImage.VisitInfo(rotType=data.rotType) self.assertEqual(visitInfo.getRotType(), data.rotType) visitInfo = afwImage.VisitInfo(observatory=data.observatory) self.assertEqual(visitInfo.getObservatory(), data.observatory) visitInfo = afwImage.VisitInfo(weather=data.weather) self.assertEqual(visitInfo.getWeather(), data.weather)
def testMetadataConstructor(self): """Test the metadata constructor This constructor allows missing values """ data = self.data1 metadata = propertySetFromDict({}) visitInfo = afwImage.VisitInfo(metadata) self._testIsEmpty(visitInfo) metadata = propertySetFromDict({"EXPID": data.exposureId}) visitInfo = afwImage.VisitInfo(metadata) self.assertEqual(visitInfo.getExposureId(), data.exposureId) self.assertTrue(math.isnan(visitInfo.getExposureTime())) metadata = propertySetFromDict({"EXPTIME": data.exposureTime}) visitInfo = afwImage.VisitInfo(metadata) self.assertEqual(visitInfo.getExposureTime(), data.exposureTime) metadata = propertySetFromDict({"DARKTIME": data.darkTime}) visitInfo = afwImage.VisitInfo(metadata) self.assertEqual(visitInfo.getDarkTime(), data.darkTime) metadata = propertySetFromDict({ "DATE-AVG": data.date.toString(DateTime.TAI), "TIMESYS": "TAI" }) visitInfo = afwImage.VisitInfo(metadata) self.assertEqual(visitInfo.getDate(), data.date) # TIME-MID in UTC is an acceptable alternative to DATE-AVG metadata = propertySetFromDict( {"TIME-MID": data.date.toString(DateTime.UTC)}) visitInfo = afwImage.VisitInfo(metadata) self.assertEqual(visitInfo.getDate(), data.date) # TIME-MID must be in UTC and TIMESYS is ignored metadata = propertySetFromDict({ "TIME-MID": data.date.toString(DateTime.TAI) + "Z", "TIMESYS": "TAI", }) visitInfo = afwImage.VisitInfo(metadata) self.assertNotEqual(visitInfo.getDate(), data.date) # if both DATE-AVG and TIME-MID provided then use DATE-AVG # use the wrong time system for TIME-MID so if it is used, an error # will result metadata = propertySetFromDict({ "DATE-AVG": data.date.toString(DateTime.TAI), "TIMESYS": "TAI", "TIME-MID": data.date.toString(DateTime.TAI) + "Z", }) visitInfo = afwImage.VisitInfo(metadata) self.assertEqual(visitInfo.getDate(), data.date) metadata = propertySetFromDict({"MJD-AVG-UT1": data.ut1}) visitInfo = afwImage.VisitInfo(metadata) self.assertEqual(visitInfo.getUt1(), data.ut1) metadata = propertySetFromDict({"AVG-ERA": data.era.asDegrees()}) visitInfo = afwImage.VisitInfo(metadata) self.assertEqual(visitInfo.getEra(), data.era) for i, key in enumerate(("BORE-RA", "BORE-DEC")): metadata = propertySetFromDict( {key: data.boresightRaDec[i].asDegrees()}) visitInfo = afwImage.VisitInfo(metadata) self.assertEqual(visitInfo.getBoresightRaDec()[i], data.boresightRaDec[i]) for i, key in enumerate(("BORE-AZ", "BORE-ALT")): metadata = propertySetFromDict( {key: data.boresightAzAlt[i].asDegrees()}) visitInfo = afwImage.VisitInfo(metadata) self.assertEqual(visitInfo.getBoresightAzAlt()[i], data.boresightAzAlt[i]) metadata = propertySetFromDict({"BORE-AIRMASS": data.boresightAirmass}) visitInfo = afwImage.VisitInfo(metadata) self.assertEqual(visitInfo.getBoresightAirmass(), data.boresightAirmass) metadata = propertySetFromDict( {"BORE-ROTANG": data.boresightRotAngle.asDegrees()}) visitInfo = afwImage.VisitInfo(metadata) self.assertEqual(visitInfo.getBoresightRotAngle(), data.boresightRotAngle) metadata = propertySetFromDict( {"ROTTYPE": RotTypeEnumNameDict[data.rotType]}) visitInfo = afwImage.VisitInfo(metadata) self.assertEqual(visitInfo.getRotType(), data.rotType) metadata = propertySetFromDict( {"OBS-LONG": data.observatory.getLongitude().asDegrees()}) visitInfo = afwImage.VisitInfo(metadata) self.assertEqual(visitInfo.getObservatory().getLongitude(), data.observatory.getLongitude()) metadata = propertySetFromDict( {"OBS-LAT": data.observatory.getLatitude().asDegrees()}) visitInfo = afwImage.VisitInfo(metadata) self.assertEqual(visitInfo.getObservatory().getLatitude(), data.observatory.getLatitude()) metadata = propertySetFromDict( {"OBS-ELEV": data.observatory.getElevation()}) visitInfo = afwImage.VisitInfo(metadata) self.assertEqual(visitInfo.getObservatory().getElevation(), data.observatory.getElevation()) metadata = propertySetFromDict( {"AIRTEMP": data.weather.getAirTemperature()}) visitInfo = afwImage.VisitInfo(metadata) self.assertEqual(visitInfo.getWeather().getAirTemperature(), data.weather.getAirTemperature()) metadata = propertySetFromDict( {"AIRPRESS": data.weather.getAirPressure()}) visitInfo = afwImage.VisitInfo(metadata) self.assertEqual(visitInfo.getWeather().getAirPressure(), data.weather.getAirPressure()) metadata = propertySetFromDict( {"HUMIDITY": data.weather.getHumidity()}) visitInfo = afwImage.VisitInfo(metadata) self.assertEqual(visitInfo.getWeather().getHumidity(), data.weather.getHumidity())
def computeCcdOffsets(camera, defaultOrientation): """ Compute the CCD offsets in ra/dec and x/y space Parameters ---------- camera: `lsst.afw.cameraGeom.Camera` defaultOrientation: `float` Default camera orientation (degrees) Returns ------- ccdOffsets: `numpy.ndarray` Numpy array with ccd offset information for input to FGCM. Angular units are degrees, and x/y units are pixels. """ # TODO: DM-21215 will fully generalize to arbitrary camera orientations # and we need to know the ccd offsets from the camera geometry ccdOffsets = np.zeros(len(camera), dtype=[('CCDNUM', 'i4'), ('DELTA_RA', 'f8'), ('DELTA_DEC', 'f8'), ('RA_SIZE', 'f8'), ('DEC_SIZE', 'f8'), ('X_SIZE', 'i4'), ('Y_SIZE', 'i4')]) # Generate fake WCSs centered at 180/0 to avoid the RA=0/360 problem, # since we are looking for relative positions boresight = geom.SpherePoint(180.0*geom.degrees, 0.0*geom.degrees) # TODO: DM-17597 will update testdata_jointcal so that the test data # does not have nan as the boresight angle for HSC data. For the # time being, there is this ungainly hack. if camera.getName() == 'HSC' and np.isnan(defaultOrientation): orientation = 270*geom.degrees else: orientation = defaultOrientation*geom.degrees flipX = False # Create a temporary visitInfo for input to createInitialSkyWcs visitInfo = afwImage.VisitInfo(boresightRaDec=boresight, boresightRotAngle=orientation, rotType=afwImage.visitInfo.RotType.SKY) for i, detector in enumerate(camera): ccdOffsets['CCDNUM'][i] = detector.getId() wcs = createInitialSkyWcs(visitInfo, detector, flipX) detCenter = wcs.pixelToSky(detector.getCenter(afwCameraGeom.PIXELS)) ccdOffsets['DELTA_RA'][i] = (detCenter.getRa() - boresight.getRa()).asDegrees() ccdOffsets['DELTA_DEC'][i] = (detCenter.getDec() - boresight.getDec()).asDegrees() bbox = detector.getBBox() detCorner1 = wcs.pixelToSky(geom.Point2D(bbox.getMin())) detCorner2 = wcs.pixelToSky(geom.Point2D(bbox.getMax())) ccdOffsets['RA_SIZE'][i] = np.abs((detCorner2.getRa() - detCorner1.getRa()).asDegrees()) ccdOffsets['DEC_SIZE'][i] = np.abs((detCorner2.getDec() - detCorner1.getDec()).asDegrees()) ccdOffsets['X_SIZE'][i] = bbox.getMaxX() ccdOffsets['Y_SIZE'][i] = bbox.getMaxY() return ccdOffsets
def std_dark(self, item, dataId): exposure = self._standardizeExposure(self.calibrations['dark'], item, dataId, trimmed=False) visitInfo = afwImage.VisitInfo(exposureTime=1.0, darkTime=1.0) exposure.getInfo().setVisitInfo(visitInfo) return exposure
def setUp(self): # CFHT Filters from the camera mapper. self.filter_names = ["u", "g", "r", "i", "z"] afwImageUtils.resetFilters() afwImageUtils.defineFilter('u', lambdaEff=374, alias="u.MP9301") afwImageUtils.defineFilter('g', lambdaEff=487, alias="g.MP9401") afwImageUtils.defineFilter('r', lambdaEff=628, alias="r.MP9601") afwImageUtils.defineFilter('i', lambdaEff=778, alias="i.MP9701") afwImageUtils.defineFilter('z', lambdaEff=1170, alias="z.MP9801") # metadata taken from CFHT data # v695856-e0/v695856-e0-c000-a00.sci_img.fits self.metadata = dafBase.PropertySet() self.metadata.set("SIMPLE", "T") self.metadata.set("BITPIX", -32) self.metadata.set("NAXIS", 2) self.metadata.set("NAXIS1", 1024) self.metadata.set("NAXIS2", 1153) self.metadata.set("RADECSYS", 'FK5') self.metadata.set("EQUINOX", 2000.) self.metadata.setDouble("CRVAL1", 215.604025685476) self.metadata.setDouble("CRVAL2", 53.1595451514076) self.metadata.setDouble("CRPIX1", 1109.99981456774) self.metadata.setDouble("CRPIX2", 560.018167811613) self.metadata.set("CTYPE1", 'RA---SIN') self.metadata.set("CTYPE2", 'DEC--SIN') self.metadata.setDouble("CD1_1", 5.10808596133527E-05) self.metadata.setDouble("CD1_2", 1.85579539217196E-07) self.metadata.setDouble("CD2_2", -5.10281493481982E-05) self.metadata.setDouble("CD2_1", -8.27440751733828E-07) self.wcs = afwGeom.makeSkyWcs(self.metadata) self.calibration = 10000 self.calibrationErr = 100 self.exposureId = 1234 self.exposureTime = 200. self.imageSize = [1024, 1153] self.dateTime = "2014-05-13T17:00:00.000000000" # Make images with one source in them and distinct values and # variance for each image. # Direct Image source_image = afwImage.MaskedImageF( lsst.geom.ExtentI(self.imageSize[0] + 1, self.imageSize[1] + 1)) source_image.image[100, 100, afwImage.LOCAL] = 10 source_image.getVariance().set(1) bbox = lsst.geom.BoxI( lsst.geom.PointI(1, 1), lsst.geom.ExtentI(self.imageSize[0], self.imageSize[1])) masked_image = afwImage.MaskedImageF(source_image, bbox, afwImage.LOCAL) self.exposure = afwImage.makeExposure(masked_image, self.wcs) detector = DetectorWrapper( id=23, bbox=self.exposure.getBBox()).detector visit = afwImage.VisitInfo( exposureId=self.exposureId, exposureTime=self.exposureTime, date=dafBase.DateTime(self.dateTime, dafBase.DateTime.Timescale.TAI)) self.exposure.setDetector(detector) self.exposure.getInfo().setVisitInfo(visit) self.exposure.setFilter(afwImage.Filter('g')) self.exposure.setPhotoCalib(afwImage.PhotoCalib(self.calibration, self.calibrationErr)) # Difference Image source_image = afwImage.MaskedImageF( lsst.geom.ExtentI(self.imageSize[0] + 1, self.imageSize[1] + 1)) source_image.image[100, 100, afwImage.LOCAL] = 20 source_image.getVariance().set(2) bbox = lsst.geom.BoxI( lsst.geom.PointI(1, 1), lsst.geom.ExtentI(self.imageSize[0], self.imageSize[1])) masked_image = afwImage.MaskedImageF(source_image, bbox, afwImage.LOCAL) self.diffim = afwImage.makeExposure(masked_image, self.wcs) self.diffim.setDetector(detector) self.diffim.getInfo().setVisitInfo(visit) self.diffim.setFilter(afwImage.Filter('g')) self.diffim.setPhotoCalib(afwImage.PhotoCalib(self.calibration, self.calibrationErr)) self.expIdBits = 16 FWHM = 5 psf = measAlg.DoubleGaussianPsf(15, 15, FWHM/(2*np.sqrt(2*np.log(2)))) self.exposure.setPsf(psf) self.diffim.setPsf(psf) self.testDiaObjects = create_test_dia_objects(5, self.wcs)
def setUp(self): """Create a sqlite3 database with default tables and schemas. """ # CFHT Filters from the camera mapper. self.filter_names = ["u", "g", "r", "i", "z"] afwImageUtils.resetFilters() afwImageUtils.defineFilter('u', lambdaEff=374, alias="u.MP9301") afwImageUtils.defineFilter('g', lambdaEff=487, alias="g.MP9401") afwImageUtils.defineFilter('r', lambdaEff=628, alias="r.MP9601") afwImageUtils.defineFilter('i', lambdaEff=778, alias="i.MP9701") afwImageUtils.defineFilter('z', lambdaEff=1170, alias="z.MP9801") self.dia_object_schema = make_dia_object_schema() # metadata taken from CFHT data # v695856-e0/v695856-e0-c000-a00.sci_img.fits self.metadata = dafBase.PropertySet() self.metadata.set("SIMPLE", "T") self.metadata.set("BITPIX", -32) self.metadata.set("NAXIS", 2) self.metadata.set("NAXIS1", 1024) self.metadata.set("NAXIS2", 1153) self.metadata.set("RADECSYS", 'FK5') self.metadata.set("EQUINOX", 2000.) self.metadata.setDouble("CRVAL1", 215.604025685476) self.metadata.setDouble("CRVAL2", 53.1595451514076) self.metadata.setDouble("CRPIX1", 1109.99981456774) self.metadata.setDouble("CRPIX2", 560.018167811613) self.metadata.set("CTYPE1", 'RA---SIN') self.metadata.set("CTYPE2", 'DEC--SIN') self.metadata.setDouble("CD1_1", 5.10808596133527E-05) self.metadata.setDouble("CD1_2", 1.85579539217196E-07) self.metadata.setDouble("CD2_2", -5.10281493481982E-05) self.metadata.setDouble("CD2_1", -8.27440751733828E-07) self.wcs = afwGeom.makeSkyWcs(self.metadata) self.exposure = afwImage.makeExposure( afwImage.makeMaskedImageFromArrays(np.ones((1024, 1153))), self.wcs) detector = DetectorWrapper(id=23, bbox=self.exposure.getBBox()).detector visit = afwImage.VisitInfo( exposureId=1234, exposureTime=200., date=dafBase.DateTime("2014-05-13T17:00:00.000000000", dafBase.DateTime.Timescale.TAI)) self.exposure.setDetector(detector) self.exposure.getInfo().setVisitInfo(visit) self.exposure.setFilter(afwImage.Filter('g')) self.flux0 = 10000 self.flux0_err = 100 self.exposure.setPhotoCalib( afwImage.PhotoCalib(self.flux0, self.flux0_err)) bbox = geom.Box2D(self.exposure.getBBox()) wcs = self.exposure.getWcs() self.pixelator = sphgeom.HtmPixelization(20) region = sphgeom.ConvexPolygon([wcs.pixelToSky(pp).getVector() for pp in bbox.getCorners()]) indices = self.pixelator.envelope(region, 64) # Index types must be cast to int to work with dax_apdb. self.index_ranges = indices.ranges()
def setUp(self): self.visitInfo = afwImage.VisitInfo() self.size = 16.8 self.npix = 1024 self.scale = self.size / self.npix
def makeExposure(flipX=False, flipY=False): """Create an exposure and flip the x or y (or both) coordinates. Returns bounding boxes that are right or left handed around the bounding polygon. Parameters ---------- flipX : `bool` Flip the x coordinate in the WCS. flipY : `bool` Flip the y coordinate in the WCS. Returns ------- exposure : `lsst.afw.image.Exposure` Exposure with a valid bounding box and wcs. """ metadata = dafBase.PropertySet() metadata.set("SIMPLE", "T") metadata.set("BITPIX", -32) metadata.set("NAXIS", 2) metadata.set("NAXIS1", 1024) metadata.set("NAXIS2", 1153) metadata.set("RADECSYS", 'FK5') metadata.set("EQUINOX", 2000.) metadata.setDouble("CRVAL1", 215.604025685476) metadata.setDouble("CRVAL2", 53.1595451514076) metadata.setDouble("CRPIX1", 1109.99981456774) metadata.setDouble("CRPIX2", 560.018167811613) metadata.set("CTYPE1", 'RA---SIN') metadata.set("CTYPE2", 'DEC--SIN') xFlip = 1 if flipX: xFlip = -1 yFlip = 1 if flipY: yFlip = -1 metadata.setDouble("CD1_1", xFlip * 5.10808596133527E-05) metadata.setDouble("CD1_2", yFlip * 1.85579539217196E-07) metadata.setDouble("CD2_2", yFlip * -5.10281493481982E-05) metadata.setDouble("CD2_1", xFlip * -8.27440751733828E-07) wcs = afwGeom.makeSkyWcs(metadata) exposure = afwImage.makeExposure( afwImage.makeMaskedImageFromArrays(np.ones((1024, 1153))), wcs) detector = DetectorWrapper(id=23, bbox=exposure.getBBox()).detector visit = afwImage.VisitInfo(exposureId=1234, exposureTime=200., date=dafBase.DateTime( "2014-05-13T17:00:00.000000000", dafBase.DateTime.Timescale.TAI)) exposure.info.id = 1234 exposure.setDetector(detector) exposure.getInfo().setVisitInfo(visit) exposure.setFilter(afwImage.FilterLabel(band='g')) return exposure