def testValid(self): test_data = { "[1:1084,1:1024]": geom.BoxI(geom.PointI(0, 0), geom.PointI(1083, 1023)), "[0:0,0:0]": geom.BoxI(geom.PointI(-1, -1), geom.PointI(-1, -1)) } for val, result in test_data.items(): self.assertEqual(obsBase.bboxFromIraf(val), result)
def assemble_raw(dataId, componentInfo, cls): """Called by the butler to construct the composite type "raw". Note that we still need to define "_raw" and copy various fields over. Parameters ---------- dataId : `lsst.daf.persistence.dataId.DataId` The data ID. componentInfo : `dict` dict containing the components, as defined by the composite definition in the mapper policy. cls : 'object' Unused. Returns ------- exposure : `lsst.afw.image.Exposure` The assembled exposure. """ from lsst.ip.isr import AssembleCcdTask config = AssembleCcdTask.ConfigClass() config.doTrim = False assembleTask = AssembleCcdTask(config=config) ampExps = componentInfo['raw_amp'].obj if len(ampExps) == 0: raise RuntimeError("Unable to read raw_amps for %s" % dataId) ccd = ampExps[0].getDetector() # the same (full, CCD-level) Detector is attached to all ampExps # # Check that the geometry in the metadata matches cameraGeom # logger = lsst.log.Log.getLogger("LsstCamMapper") warned = False for i, (amp, ampExp) in enumerate(zip(ccd, ampExps)): ampMd = ampExp.getMetadata().toDict() if amp.getRawBBox() != ampExp.getBBox(): # Oh dear. cameraGeom is wrong -- probably overscan if amp.getRawDataBBox().getDimensions() != amp.getBBox().getDimensions(): raise RuntimeError("Active area is the wrong size: %s v. %s" % (amp.getRawDataBBox().getDimensions(), amp.getBBox().getDimensions())) if not warned: logger.warn("amp.getRawBBox() != data.getBBox(); patching. (%s v. %s)", amp.getRawBBox(), ampExp.getBBox()) warned = True w, h = ampExp.getBBox().getDimensions() ow, oh = amp.getRawBBox().getDimensions() # "old" (cameraGeom) dimensions # # We could trust the BIASSEC keyword, or we can just assume that # they've changed the number of overscan pixels (serial and/or # parallel). As Jim Chiang points out, the latter is safer # bbox = amp.getRawHorizontalOverscanBBox() hOverscanBBox = geom.BoxI(bbox.getBegin(), geom.ExtentI(w - bbox.getBeginX(), bbox.getHeight())) bbox = amp.getRawVerticalOverscanBBox() vOverscanBBox = geom.BoxI(bbox.getBegin(), geom.ExtentI(bbox.getWidth(), h - bbox.getBeginY())) amp.setRawBBox(ampExp.getBBox()) amp.setRawHorizontalOverscanBBox(hOverscanBBox) amp.setRawVerticalOverscanBBox(vOverscanBBox) # # This gets all the geometry right for the amplifier, but the size # of the untrimmed image will be wrong and we'll put the amp # sections in the wrong places, i.e. # amp.getRawXYOffset() # will be wrong. So we need to recalculate the offsets. # xRawExtent, yRawExtent = amp.getRawBBox().getDimensions() x0, y0 = amp.getRawXYOffset() ix, iy = x0//ow, y0/oh x0, y0 = ix*xRawExtent, iy*yRawExtent amp.setRawXYOffset(geom.ExtentI(ix*xRawExtent, iy*yRawExtent)) # # Check the "IRAF" keywords, but don't abort if they're wrong # # Only warn about the first amp, use debug for the others # detsec = bboxFromIraf(ampMd["DETSEC"]) if "DETSEC" in ampMd else None datasec = bboxFromIraf(ampMd["DATASEC"]) if "DATASEC" in ampMd else None biassec = bboxFromIraf(ampMd["BIASSEC"]) if "BIASSEC" in ampMd else None logCmd = logger.warn if i == 0 else logger.debug if detsec and amp.getBBox() != detsec: logCmd("DETSEC doesn't match for %s (%s != %s)", dataId, amp.getBBox(), detsec) if datasec and amp.getRawDataBBox() != datasec: logCmd("DATASEC doesn't match for %s (%s != %s)", dataId, amp.getRawDataBBox(), detsec) if biassec and amp.getRawHorizontalOverscanBBox() != biassec: logCmd("BIASSEC doesn't match for %s (%s != %s)", dataId, amp.getRawHorizontalOverscanBBox(), detsec) ampDict = {} for amp, ampExp in zip(ccd, ampExps): ampDict[amp.getName()] = ampExp exposure = assembleTask.assembleCcd(ampDict) md = componentInfo['raw_hdu'].obj fix_header(md) # No mapper so cannot specify the translator class exposure.setMetadata(md) visitInfo = LsstCamMakeRawVisitInfo(logger)(md) exposure.getInfo().setVisitInfo(visitInfo) boresight = visitInfo.getBoresightRaDec() rotangle = visitInfo.getBoresightRotAngle() if boresight.isFinite(): exposure.setWcs(getWcsFromDetector(exposure.getDetector(), boresight, 90*geom.degrees - rotangle)) else: # Should only warn for science observations but VisitInfo does not know logger.warn("Unable to set WCS for %s from header as RA/Dec/Angle are unavailable" % (dataId,)) return exposure
def fixAmpGeometry(inAmp, bbox, metadata, logCmd=None): """Make sure a camera geometry amplifier matches an on-disk bounding box. Bounding box differences that are consistent with differences in overscan regions are assumed to be overscan regions, which gives us enough information to correct the camera geometry. Parameters ---------- inAmp : `lsst.afw.cameraGeom.Amplifier` Amplifier description from camera geometry. bbox : `lsst.geom.Box2I` The on-disk bounding box of the amplifer image. metadata : `lsst.daf.base.PropertyList` FITS header metadata from the amplifier HDU. logCmd : `function`, optional Call back to use to issue log messages. Arguments to this function should match arguments to be accepted by normal logging functions. Return ------ outAmp : `~lsst.afw.cameraGeom.Amplifier.Builder` modified : `bool` `True` if ``amp`` was modified; `False` otherwise. Raises ------ RuntimeError Raised if the bounding boxes differ in a way that is not consistent with just a change in overscan. """ if logCmd is None: # Define a null log command def logCmd(*args): return modified = False outAmp = inAmp.rebuild() if outAmp.getRawBBox( ) != bbox: # Oh dear. cameraGeom is wrong -- probably overscan if outAmp.getRawDataBBox().getDimensions() != outAmp.getBBox( ).getDimensions(): raise RuntimeError("Active area is the wrong size: %s v. %s" % (outAmp.getRawDataBBox().getDimensions(), outAmp.getBBox().getDimensions())) logCmd("outAmp.getRawBBox() != data.getBBox(); patching. (%s v. %s)", outAmp.getRawBBox(), bbox) w, h = bbox.getDimensions() ow, oh = outAmp.getRawBBox().getDimensions( ) # "old" (cameraGeom) dimensions # # We could trust the BIASSEC keyword, or we can just assume that # they've changed the number of overscan pixels (serial and/or # parallel). As Jim Chiang points out, the latter is safer # fromCamGeom = outAmp.getRawHorizontalOverscanBBox() hOverscanBBox = Box2I( fromCamGeom.getBegin(), Extent2I(w - fromCamGeom.getBeginX(), fromCamGeom.getHeight())) fromCamGeom = outAmp.getRawVerticalOverscanBBox() vOverscanBBox = Box2I( fromCamGeom.getBegin(), Extent2I(fromCamGeom.getWidth(), h - fromCamGeom.getBeginY())) outAmp.setRawBBox(bbox) outAmp.setRawHorizontalOverscanBBox(hOverscanBBox) outAmp.setRawVerticalOverscanBBox(vOverscanBBox) # # This gets all the geometry right for the amplifier, but the size # of the untrimmed image will be wrong and we'll put the amp sections # in the wrong places, i.e. # outAmp.getRawXYOffset() # will be wrong. So we need to recalculate the offsets. # xRawExtent, yRawExtent = outAmp.getRawBBox().getDimensions() x0, y0 = outAmp.getRawXYOffset() ix, iy = x0 // ow, y0 / oh x0, y0 = ix * xRawExtent, iy * yRawExtent outAmp.setRawXYOffset(Extent2I(ix * xRawExtent, iy * yRawExtent)) modified = True # # Check the "IRAF" keywords, but don't abort if they're wrong # # Only warn about the first amp, use debug for the others # d = metadata.toDict() detsec = bboxFromIraf(d["DETSEC"]) if "DETSEC" in d else None datasec = bboxFromIraf(d["DATASEC"]) if "DATASEC" in d else None biassec = bboxFromIraf(d["BIASSEC"]) if "BIASSEC" in d else None if detsec and outAmp.getBBox() != detsec: logCmd("DETSEC doesn't match (%s != %s)", outAmp.getBBox(), detsec) if datasec and outAmp.getRawDataBBox() != datasec: logCmd("DATASEC doesn't match for (%s != %s)", outAmp.getRawDataBBox(), detsec) if biassec and outAmp.getRawHorizontalOverscanBBox() != biassec: logCmd("BIASSEC doesn't match for (%s != %s)", outAmp.getRawHorizontalOverscanBBox(), detsec) return outAmp, modified
def std_raw(self, item, dataId): """Method for performing any necessary manipulation of the raw files. @param[in,out] item afwImage exposure object with associated metadata and detector info @param[in] dataId """ md = item.getMetadata() # Note that setting these must be done before the call to super below md.set('CTYPE1', 'RA---TAN') # add missing keywords md.set('CTYPE2', 'DEC--TAN') # add missing keywords md.set('CRVAL2', decStrToDeg(md.get('DEC'))) # translate RA/DEC from header md.set('CRVAL1', raStrToDeg(md.get('RA'))) md.set('CRPIX1', 210.216) # set reference pixels md.set('CRPIX2', 344.751) md.set('CD1_1', -0.000111557869436) # set nominal CD matrix md.set('CD1_2', 1.09444409144E-07) md.set('CD2_1', 6.26180926869E-09) md.set('CD2_2', -0.000111259259893) item = super(Ctio0m9Mapper, self).std_raw(item, dataId) # # We may need to hack up the cameraGeom # # There doesn't seem to be a way to get the extended register, so I don't update it. # We could do this by detecting extra overscan and adjusting things cleverly; probably # we need to so so. # ccd = item.getDetector() rawBBoxFromMetadata = bboxFromIraf(md.get("ASEC11")) rawBBox = ccd[0].getRawBBox() if rawBBoxFromMetadata != rawBBox: extraSerialOverscan = rawBBoxFromMetadata.getWidth( ) - rawBBox.getWidth() # extra overscan pixels extraParallelOverscan = rawBBoxFromMetadata.getHeight( ) - rawBBox.getHeight() # vertical ccd = cameraGeom.copyDetector( ccd, ampInfoCatalog=ccd.getAmpInfoCatalog().copy(deep=True)) item.setDetector(ccd) for a in ccd: ix, iy = [int(_) for _ in a.getName()] irafName = "%d%d" % (iy, ix) a.setRawBBox(bboxFromIraf(md.get("ASEC%s" % irafName))) a.setRawDataBBox(bboxFromIraf(md.get("TSEC%s" % irafName))) if extraSerialOverscan != 0 or extraParallelOverscan != 0: # # the number of overscan pixels has been changed from camera.yaml # # First adjust the overscan # rawHorizontalOverscanBBox = a.getRawHorizontalOverscanBBox( ) rawHorizontalOverscanBBox.shift( afwGeom.ExtentI((ix - 1) * extraSerialOverscan, (iy - 1) * extraParallelOverscan)) xy0 = rawHorizontalOverscanBBox.getMin() xy1 = rawHorizontalOverscanBBox.getMax() xy1.shift( afwGeom.ExtentI(extraSerialOverscan, extraParallelOverscan)) a.setRawHorizontalOverscanBBox(afwGeom.BoxI(xy0, xy1)) # # And now move the extended register to allow for the extra overscan pixels # rawPrescanBBox = a.getRawPrescanBBox() rawPrescanBBox.shift( afwGeom.ExtentI(2 * (ix - 1) * extraSerialOverscan, (iy - 1) * extraParallelOverscan)) xy0 = rawPrescanBBox.getMin() xy1 = rawPrescanBBox.getMax() xy1.shift(afwGeom.ExtentI(0, extraParallelOverscan)) a.setRawPrescanBBox(afwGeom.BoxI(xy0, xy1)) return item