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