27 from lsst.daf.butler.formatters.fileFormatter 
import FileFormatter
 
   32     """Class to support bright object masks 
   34     N.b. I/O is done by providing a readFits method which fools the butler. 
   38         schema = afwTable.SimpleTable.makeMinimalSchema()
 
   39         schema.addField(
"type", str, 
"type of region (e.g. box, circle)", size=10)
 
   40         schema.addField(
"radius", 
"Angle", 
"radius of mask (if type == circle")
 
   41         schema.addField(
"height", 
"Angle", 
"height of mask (if type == box)")
 
   42         schema.addField(
"width", 
"Angle", 
"width of mask (if type == box)")
 
   43         schema.addField(
"angle", 
"Angle", 
"rotation of mask (if type == box)")
 
   44         schema.addField(
"mag", float, 
"object's magnitude")
 
   66         """FitsCatalogStorage facade for `read`. 
   68         This method is intended for use by the Gen2 Butler only. 
   73             Name of the file to read. 
   75             Provided for compatibility with the "FitsCatalogStorage" read API 
   76             defined in `lsst.daf.persistence`, and ignored here. 
   78             Provided for compatibility with the "FitsCatalogStorage" read API 
   79             defined in `lsst.daf.persistence`, and ignored here. 
   83         Having a `readFits` method makes the `ObjectCatalogMask` class 
   84         duck-type compatible with `lsst.afw.table` catalogs, to the extent 
   85         needed to support reading by the Gen2 Butler with no specialized code 
   86         in `lsst.daf.persistence`. The on-disk type should actually be an 
   87         ASCII ds9 region file, typically with a ".reg" suffix. 
   89         return cls.
read(fileName)
 
   93         """Read a ds9 region file, returning a ObjectMaskCatalog object 
   95         The files should be structured as follows: 
   97         # Description of catalogue as a comment 
   98         # CATALOG: catalog-id-string 
  105         circle(RA, DEC, RADIUS)           # ID: 1, mag: 12.34 
  106         box(RA, DEC, XSIZE, YSIZE, THETA) # ID: 2, mag: 23.45 
  109         The ", mag: XX.YY" is optional 
  111         The commented lines must be present, with the relevant fields such as 
  112         tract patch and filter filled in. The coordinate system must be listed 
  113         as above. Each patch is specified as a box or circle, with RA, DEC, 
  114         and dimensions specified in decimal degrees (with or without an 
  117         Only (axis-aligned) boxes and circles are currently supported as 
  121         log = Log.getLogger(
"ObjectMaskCatalog")
 
  124         checkedWcsIsFk5 = 
False 
  125         NaN = float(
"NaN")*geom.degrees
 
  128         with open(fileName) 
as fd:
 
  129             for lineNo, line 
in enumerate(fd.readlines(), 1):
 
  132                 if re.search(
r"^\s*#", line):
 
  141                     mat = re.search(
r"^\s*#\s*([a-zA-Z][a-zA-Z0-9_]+)\s*:\s*(.*)", line)
 
  143                         key, value = mat.group(1).lower(), mat.group(2)
 
  147                         brightObjects.table.getMetadata().
set(key, value)
 
  149                 line = re.sub(
r"^\s*#.*", 
"", line)
 
  153                 if re.search(
r"^\s*wcs\s*;\s*fk5\s*$", line, re.IGNORECASE):
 
  154                     checkedWcsIsFk5 = 
True 
  159                 mat = re.search(
r"^\s*(box|circle)" 
  161                                 r"([+\-]?(?:0|[1-9]\d*)(?:\.\d*)?(?:[eE][+\-]?\d+)?)([d]*)"   
  163                                 r"([+\-]?(?:0|[1-9]\d*)(?:\.\d*)?(?:[eE][+\-]?\d+)?)([d]*)"   
  165                                 r"([+\-]?(?:0|[1-9]\d*)(?:\.\d*)?(?:[eE][+\-]?\d+)?)([d]*)"   
  168                                 r"([+\-]?(?:0|[1-9]\d*)(?:\.\d*)?(?:[eE][+\-]?\d+)?)([d]*)"   
  171                                 r"([+\-]?(?:0|[1-9]\d*)(?:\.\d*)?(?:[eE][+\-]?\d+)?)([d]*)"   
  175                                 r"#\s*ID:[\w\s]*(\d+)"   
  176                                 r"(?:\s*,?\s*mag:\s*([+\-]?(?:0|[1-9]\d*)(?:\.\d*)?(?:[eE][+\-]?\d+)?))?" 
  179                     _type, ra, raUnit, dec, decUnit, \
 
  180                         param1, param1Unit, param2, param2Unit, param3, param3Unit, \
 
  181                         _id, mag = mat.groups()
 
  195                     angle = 0.0*geom.degrees
 
  198                         width = 
convertToAngle(param1, param1Unit, 
"width", fileName, lineNo)
 
  199                         height = 
convertToAngle(param2, param2Unit, 
"height", fileName, lineNo)
 
  200                         if param3 
is not None:
 
  201                             angle = 
convertToAngle(param3, param3Unit, 
"angle", fileName, lineNo)
 
  204                             log.warn(
"Rotated boxes are not supported: \"%s\" at %s:%d" % (
 
  205                                 line, fileName, lineNo))
 
  207                     elif _type == 
"circle":
 
  208                         radius = 
convertToAngle(param1, param1Unit, 
"radius", fileName, lineNo)
 
  210                         if not (param2 
is None and param3 
is None):
 
  211                             log.warn(
"Extra parameters for circle: \"%s\" at %s:%d" % (
 
  212                                 line, fileName, lineNo))
 
  215                     rec = brightObjects.addNew()
 
  223                     rec[
"height"] = height
 
  225                     rec[
"radius"] = radius
 
  227                     log.warn(
"Unexpected line \"%s\" at %s:%d" % (line, fileName, lineNo))
 
  231             raise RuntimeError(
"Saw %d formatting errors in %s" % (nFormatError, fileName))
 
  233         if not checkedWcsIsFk5:
 
  234             raise RuntimeError(
"Expected to see a line specifying an fk5 wcs in %s" % fileName)
 
  237         brightObjects._catalog = brightObjects._catalog.copy(
True)
 
  243     """Given a variable and its units, return an geom.Angle 
  245     what, fileName, and lineNo are used to generate helpful error messages 
  249     if varUnit 
in (
"d", 
"", 
None):
 
  256         raise RuntimeError(
"unsupported unit \"%s\" for %s at %s:%d" %
 
  257                            (varUnit, what, fileName, lineNo))
 
  259     return var*geom.degrees
 
  263     """Plugin for reading DS9 region file catalogs with Gen3 Butler. 
  267     def _readFile(self, path, pytype):
 
  269         if not os.path.exists(path):
 
  272         return pytype.read(path)
 
  274     def _writeFile(self, inMemoryDataset, fileDescriptor):
 
  276         raise NotImplementedError(
"Write not implemented.")