LSSTApplications  11.0-13-gbb96280,12.1.rc1,12.1.rc1+1,12.1.rc1+2,12.1.rc1+5,12.1.rc1+8,12.1.rc1-1-g06d7636+1,12.1.rc1-1-g253890b+5,12.1.rc1-1-g3d31b68+7,12.1.rc1-1-g3db6b75+1,12.1.rc1-1-g5c1385a+3,12.1.rc1-1-g83b2247,12.1.rc1-1-g90cb4cf+6,12.1.rc1-1-g91da24b+3,12.1.rc1-2-g3521f8a,12.1.rc1-2-g39433dd+4,12.1.rc1-2-g486411b+2,12.1.rc1-2-g4c2be76,12.1.rc1-2-gc9c0491,12.1.rc1-2-gda2cd4f+6,12.1.rc1-3-g3391c73+2,12.1.rc1-3-g8c1bd6c+1,12.1.rc1-3-gcf4b6cb+2,12.1.rc1-4-g057223e+1,12.1.rc1-4-g19ed13b+2,12.1.rc1-4-g30492a7
LSSTDataManagementBasePackage
objectMasks.py
Go to the documentation of this file.
1 import re
2 import lsst.daf.base as dafBase
3 import lsst.afw.coord as afwCoord
4 import lsst.afw.geom as afwGeom
5 import lsst.afw.table as afwTable
6 from lsst.log import Log
7 
8 class ObjectMaskCatalog(object):
9  """Class to support bright object masks
10 
11  N.b. I/O is done by providing a readFits method which fools the butler.
12  """
13  def __init__(self):
14  schema = afwTable.SimpleTable.makeMinimalSchema()
15  schema.addField("radius", "Angle", "radius of mask")
16 
18  self._catalog.table.setMetadata(dafBase.PropertyList())
19 
20  self.table = self._catalog.table
21  self.addNew = self._catalog.addNew
22 
23  def __len__(self):
24  return len(self._catalog)
25 
26  def __iter__(self):
27  return iter(self._catalog)
28 
29  def __getitem__(self, i):
30  return self._catalog.__getitem__(i)
31 
32  def __setitem__(self, i, v):
33  return self._catalog.__setitem__(i, v)
34 
35  @staticmethod
36  def readFits(fileName, hdu=0, flags=0):
37  """Read a ds9 region file, returning a ObjectMaskCatalog object
38 
39  This method is called "readFits" to fool the butler. The corresponding mapper entry looks like
40  brightObjectMask: {
41  template: "deepCoadd/BrightObjectMasks/%(tract)d/BrightObjectMask-%(tract)d-%(patch)s-%(filter)s.reg"
42  python: "lsst.obs.subaru.objectMasks.ObjectMaskCatalog"
43  persistable: "PurePythonClass"
44  storage: "FitsCatalogStorage"
45  }
46  and this is the only way I know to get it to read a random file type, in this case a ds9 region file
47 
48  This method expects to find files named as BrightObjectMask-%(tract)d-%(patch)s-%(filter)s.reg
49  The files should be structured as follows:
50 
51  # Description of catalogue as a comment
52  # CATALOG: catalog-id-string
53  # TRACT: 0
54  # PATCH: 5,4
55  # FILTER: HSC-I
56 
57  wcs; fk5
58 
59  circle(RA, DEC, RADIUS) # ID: 1
60 
61  The commented lines must be present, with the relevant fields such as tract patch and filter filled
62  in. The coordinate system must be listed as above. Each patch is specified as a circle, with an RA,
63  DEC, and Radius specified in decimal degrees. Only circles are supported as region definitions
64  currently.
65  """
66 
67  log = Log.getLogger("ObjectMaskCatalog")
68 
69  brightObjects = ObjectMaskCatalog()
70  checkedWcsIsFk5 = False
71 
72  with open(fileName) as fd:
73  for lineNo, line in enumerate(fd.readlines(), 1):
74  line = line.rstrip()
75 
76  if re.search(r"^\s*#", line):
77  #
78  # Parse any line of the form "# key : value" and put them into the metadata.
79  #
80  # The medatdata values must be defined as outlined in the above docstring
81  #
82  # The value of these three keys will be checked,
83  # so get them right!
84  #
85  mat = re.search(r"^\s*#\s*([a-zA-Z][a-zA-Z0-9_]+)\s*:\s*(.*)", line)
86  if mat:
87  key, value = mat.group(1).lower(), mat.group(2)
88  if key == "tract":
89  value = int(value)
90 
91  brightObjects.table.getMetadata().set(key, value)
92 
93  line = re.sub(r"^\s*#.*", "", line)
94  if not line:
95  continue
96 
97  if re.search(r"^\s*wcs\s*;\s*fk5\s*$", line, re.IGNORECASE):
98  checkedWcsIsFk5 = True
99  continue
100 
101  # This regular expression parses the regions file for each region to be masked,
102  # with the format as specified in the above docstring.
103  mat = re.search(r"^\s*circle(?:\s+|\s*\(\s*)"
104  "(\d+(?:\.\d*)([d]*))" "(?:\s+|\s*,\s*)"
105  "([+-]?\d+(?:\.\d*)([d]*))" "(?:\s+|\s*,\s*)"
106  "(\d+(?:\.\d*))([d'\"]*)" "(?:\s*|\s*\)\s*)"
107  "\s*#\s*ID:\s*(\d+)" "\s*$"
108  , line)
109  if mat:
110  ra, raUnit, dec, decUnit, radius, radiusUnit, _id = mat.groups()
111 
112  _id = int(_id)
113  ra = convertToAngle(ra, raUnit, "ra", fileName, lineNo)
114  dec = convertToAngle(dec, decUnit, "dec", fileName, lineNo)
115  radius = convertToAngle(radius, radiusUnit, "radius", fileName, lineNo)
116 
117  rec = brightObjects.addNew()
118  # N.b. rec["coord"] = Coord is not supported, so we have to use the setter
119  rec["id"] = _id
120  rec.setCoord(afwCoord.Fk5Coord(ra, dec))
121  rec["radius"] = radius
122  else:
123  log.warn("Unexpected line \"%s\" at %s:%d" % (line, fileName, lineNo))
124 
125  if not checkedWcsIsFk5:
126  raise RuntimeError("Expected to see a line specifying an fk5 wcs")
127 
128  # This makes the deep copy contiguous in memory so that a ColumnView can be exposed to Numpy
129  brightObjects._catalog = brightObjects._catalog.copy(True)
130 
131  return brightObjects
132 
133 
134 def convertToAngle(var, varUnit, what, fileName, lineNo):
135  """Given a variable and its units, return an afwGeom.Angle
136 
137  what, fileName, and lineNo are used to generate helpful error messages
138  """
139  var = float(var)
140 
141  if varUnit in ("d", ""):
142  pass
143  elif varUnit == "'":
144  var /= 60.0
145  elif varUnit == '"':
146  var /= 3600.0
147  else:
148  raise RuntimeError("unsupported unit \"%s\" for %s at %s:%d" %
149  (varUnit, what, fileName, lineNo))
150 
151  return var*afwGeom.degrees
int iter
Class for storing ordered metadata with comments.
Definition: PropertyList.h:82
Definition: Log.h:716
Custom catalog class for record/table subclasses that are guaranteed to have an ID, and should generally be sorted by that ID.
Definition: fwd.h:55
A class to handle Fk5 coordinates (inherits from Coord)
Definition: Coord.h:191