LSSTApplications  10.0-2-g4f67435,11.0.rc2+1,11.0.rc2+12,11.0.rc2+3,11.0.rc2+4,11.0.rc2+5,11.0.rc2+6,11.0.rc2+7,11.0.rc2+8
LSSTDataManagementBasePackage
simpleMapper.py
Go to the documentation of this file.
1 #
2 # LSST Data Management System
3 # Copyright 2008, 2009, 2010, 2011, 2012 LSST Corporation.
4 #
5 # This product includes software developed by the
6 # LSST Project (http://www.lsst.org/).
7 #
8 # This program is free software: you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation, either version 3 of the License, or
11 # (at your option) any later version.
12 #
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
17 #
18 # You should have received a copy of the LSST License Statement and
19 # the GNU General Public License along with this program. If not,
20 # see <http://www.lsstcorp.org/LegalNotices/>.
21 #
22 
23 """Mapper and cameraGeom definition for extremely simple mock data.
24 
25 SimpleMapper inherits directly from Mapper, not CameraMapper. This means
26 we can avoid any problems with paf files at the expense of reimplementing
27 some parts of CameraMapper here. Jim is not sure this was the best
28 possible approach, but it gave him an opportunity to play around with
29 prototyping a future paf-free mapper class, and it does everything it
30 needs to do right now.
31 """
32 
33 import os
34 import shutil
35 import re
36 
39 from lsst.afw.cameraGeom.testUtils import DetectorWrapper
40 import lsst.afw.image.utils as afwImageUtils
41 
42 __all__ = ("SimpleMapper", "makeSimpleCamera", "makeDataRepo")
43 
44 class PersistenceType(object):
45  """Base class of a hierarchy used by SimpleMapper to defined different kinds of types of objects
46  to persist.
47 
48  PersistenceType objects are never instantiated; only the type objects are used (we needed a
49  simple singleton struct that could be inherited, which is exactly what a Python type is).
50  """
51  python = None
52  cpp = "ignored"
53  storage = None
54  ext = ""
55  suffixes = ()
56 
57  @classmethod
58  def makeButlerLocation(cls, path, dataId, suffix=None):
59  """Method called by SimpleMapping to implement a map_ method."""
60  return lsst.daf.persistence.ButlerLocation(cls.python, cls.cpp, cls.storage, [path], dataId)
61 
63  """Persistence type for things that don't actually use daf_persistence.
64  """
65 
66  python = "lsst.daf.base.PropertySet" # something to import even when we don't need to
67 
68  @classmethod
69  def makeButlerLocation(cls, path, dataId, suffix=None):
70  """Method called by SimpleMapping to implement a map_ method; overridden to not use the path."""
71  return lsst.daf.persistence.ButlerLocation(cls.python, cls.cpp, cls.storage, [], dataId)
72 
74  """Persistence type of Exposure images.
75  """
76 
77  python = "lsst.afw.image.ExposureF"
78  cpp = "ExposureF"
79  storage = "FitsStorage"
80  ext = ".fits"
81  suffixes = ("_sub",)
82 
83  @classmethod
84  def makeButlerLocation(cls, path, dataId, suffix=None):
85  """Method called by SimpleMapping to implement a map_ method; overridden to support subimages."""
86  if suffix is None:
87  loc = super(ExposurePersistenceType, cls).makeButlerLocation(path, dataId, suffix=None)
88  elif suffix == "_sub":
89  subId = dataId.copy()
90  bbox = subId.pop('bbox')
91  loc = super(ExposurePersistenceType, cls).makeButlerLocation(path, subId, suffix=None)
92  loc.additionalData.set('llcX', bbox.getMinX())
93  loc.additionalData.set('llcY', bbox.getMinY())
94  loc.additionalData.set('width', bbox.getWidth())
95  loc.additionalData.set('height', bbox.getHeight())
96  if 'imageOrigin' in dataId:
97  loc.additionalData.set('imageOrigin',
98  dataId['imageOrigin'])
99  return loc
100 
102  python = "lsst.skymap.BaseSkyMap"
103  storage = "PickleStorage"
104  ext = ".pickle"
105 
107  python = "lsst.afw.table.BaseCatalog"
108  cpp = "BaseCatalog"
109  storage = "FitsCatalogStorage"
110  ext = ".fits"
111 
113  python = "lsst.afw.table.SimpleCatalog"
114  cpp = "SimpleCatalog"
115 
117  python = "lsst.afw.table.SourceCatalog"
118  cpp = "SourceCatalog"
119 
121  python = "lsst.afw.table.ExposureCatalog"
122  cpp = "ExposureCatalog"
123 
125  python = "lsst.afw.detection.PeakCatalog"
126  cpp = "PeakCatalog"
127 
128 class SimpleMapping(object):
129  """Mapping object used to implement SimpleMapper, similar in intent to lsst.daf.peristence.Mapping.
130  """
131 
132  template = None
133  keys = {}
134 
135  def __init__(self, persistence, template=None, keys=None):
136  self.persistence = persistence
137  if template is not None:
138  self.template = template
139  if keys is not None:
140  self.keys = keys
141 
142  def map(self, dataset, root, dataId, suffix=None):
143  if self.template is not None:
144  path = os.path.join(root, self.template.format(dataset=dataset, ext=self.persistence.ext,
145  **dataId))
146  else:
147  path = None
148  return self.persistence.makeButlerLocation(path, dataId, suffix=suffix)
149 
151  """Mapping for dataset types that are organized the same way as raw data (i.e. by CCD)."""
152 
153  template = "{dataset}-{visit:04d}-{ccd:01d}{ext}"
154  keys = dict(visit=int, ccd=int)
155 
156  def query(self, index, level, format, dataId):
157  results = []
158  visit = dataId.get('visit', None)
159  dictList = index[level][visit]
160  ccd = dataId.get('ccd', None)
161  for d in dictList.values():
162  if ccd is not None and d['ccd'] != ccd:
163  continue
164  results.append(tuple(d[f] for f in format))
165  return results
166 
168  """Mapping for dataset types that are organized according to a SkyMap subdivision of the sky."""
169 
170  template = "{dataset}-{filter}-{tract:02d}-{patch}{ext}"
171  keys = dict(filter=str, tract=int, patch=str)
172 
174  """Mapping for CoaddTempExp datasets."""
175 
176  template = "{dataset}-{tract:02d}-{patch}-{visit:04d}{ext}"
177  keys = dict(tract=int, patch=str, visit=int)
178 
180  """Mapping for forced_src datasets."""
181 
182  template = "{dataset}-{tract:02d}-{visit:04d}-{ccd:01d}{ext}"
183  keys = dict(tract=int, ccd=int, visit=int)
184 
185 class MapperMeta(type):
186  """Metaclass for SimpleMapper that creates map_ and query_ methods for everything found in the
187  'mappings' class variable.
188  """
189 
190  @staticmethod
191  def _makeMapClosure(dataset, mapping, suffix=None):
192  def mapClosure(self, dataId, write=False):
193  return mapping.map(dataset, self.root, dataId, suffix=suffix)
194  return mapClosure
195 
196  @staticmethod
197  def _makeQueryClosure(dataset, mapping):
198  def queryClosure(self, level, format, dataId):
199  return mapping.query(self.index, level, format, dataId)
200  return queryClosure
201 
202  def __init__(cls, name, bases, dict_):
203  type.__init__(cls, name, bases, dict_)
204  cls.keyDict = dict()
205  for dataset, mapping in cls.mappings.iteritems():
206  setattr(cls, "map_" + dataset, MapperMeta._makeMapClosure(dataset, mapping, suffix=None))
207  for suffix in mapping.persistence.suffixes:
208  setattr(cls, "map_" + dataset + suffix,
209  MapperMeta._makeMapClosure(dataset, mapping, suffix=suffix))
210  if hasattr(mapping, "query"):
211  setattr(cls, "query_" + dataset, MapperMeta._makeQueryClosure(dataset, mapping))
212  cls.keyDict.update(mapping.keys)
213 
215  """
216  An extremely simple mapper for an imaginary camera for use in integration tests.
217 
218  As SimpleMapper does not inherit from daf.butlerUtils.CameraMapper, it does not
219  use a policy file to set mappings or a registry; all the information is here
220  (in the map_* and query_* methods).
221 
222  The imaginary camera's raw data format has only 'visit' and 'ccd' keys, with
223  two CCDs per visit (by default).
224  """
225 
226  __metaclass__ = MapperMeta
227 
228  mappings = dict(
229  calexp = RawMapping(ExposurePersistenceType),
230  forced_src = ForcedSrcMapping(SourceCatalogPersistenceType),
231  forced_src_schema = SimpleMapping(SourceCatalogPersistenceType,
232  template="{dataset}{ext}", keys={}),
233  truth = SimpleMapping(SimpleCatalogPersistenceType, template="{dataset}-{tract:02d}{ext}",
234  keys={"tract":int}),
235  simsrc = RawMapping(SimpleCatalogPersistenceType, template="{dataset}-{tract:02d}{ext}",
236  keys={"tract":int}),
237  observations = SimpleMapping(ExposureCatalogPersistenceType, template="{dataset}-{tract:02d}{ext}",
238  keys={"tract":int}),
239  ccdExposureId = RawMapping(BypassPersistenceType),
240  ccdExposureId_bits = SimpleMapping(BypassPersistenceType),
241  deepCoaddId = SkyMapping(BypassPersistenceType),
242  deepCoaddId_bits = SimpleMapping(BypassPersistenceType),
243  deepMergedCoaddId = SkyMapping(BypassPersistenceType),
244  deepMergedCoaddId_bits = SimpleMapping(BypassPersistenceType),
245  deepCoadd_skyMap = SimpleMapping(SkyMapPersistenceType, template="{dataset}{ext}", keys={}),
246  deepCoadd = SkyMapping(ExposurePersistenceType),
247  deepCoadd_calexp_det = SkyMapping(ExposurePersistenceType),
248  deepCoadd_calexp_detBackground = SkyMapping(CatalogPersistenceType),
249  deepCoadd_icSrc = SkyMapping(SourceCatalogPersistenceType),
250  deepCoadd_icSrc_schema = SimpleMapping(SourceCatalogPersistenceType,
251  template="{dataset}{ext}", keys={}),
252  deepCoadd_src = SkyMapping(SourceCatalogPersistenceType),
253  deepCoadd_src_schema = SimpleMapping(SourceCatalogPersistenceType,
254  template="{dataset}{ext}", keys={}),
255  deepCoadd_peak_schema = SimpleMapping(PeakCatalogPersistenceType,
256  template="{dataset}{ext}", keys={}),
257  deepCoadd_ref = SkyMapping(SourceCatalogPersistenceType),
258  deepCoadd_ref_schema = SimpleMapping(SourceCatalogPersistenceType,
259  template="{dataset}{ext}", keys={}),
260  deepCoadd_det = SkyMapping(SourceCatalogPersistenceType),
261  deepCoadd_det_schema = SimpleMapping(SourceCatalogPersistenceType,
262  template="{dataset}{ext}", keys={}),
263  deepCoadd_mergeDet = SkyMapping(SourceCatalogPersistenceType),
264  deepCoadd_mergeDet_schema = SimpleMapping(SourceCatalogPersistenceType,
265  template="{dataset}{ext}", keys={}),
266  deepCoadd_meas = SkyMapping(SourceCatalogPersistenceType),
267  deepCoadd_meas_schema = SimpleMapping(SourceCatalogPersistenceType,
268  template="{dataset}{ext}", keys={}),
269  deepCoadd_forced_src = SkyMapping(SourceCatalogPersistenceType),
270  deepCoadd_forced_src_schema = SimpleMapping(SourceCatalogPersistenceType,
271  template="{dataset}{ext}", keys={}),
272  deepCoadd_mock = SkyMapping(ExposurePersistenceType),
273  deepCoadd_tempExp = TempExpMapping(ExposurePersistenceType),
274  deepCoadd_tempExp_mock = TempExpMapping(ExposurePersistenceType),
275  )
276 
277  levels = dict(
278  visit = ['ccd'],
279  ccd = [],
280  )
281 
282  def __init__(self, root):
283  super(SimpleMapper, self).__init__()
284  self.root = root
285  self.camera = makeSimpleCamera(nX=1, nY=2, sizeX=400, sizeY=200, gapX=2, gapY=2)
286  afwImageUtils.defineFilter('r', 619.42)
287  self.update()
288 
289  def getDefaultLevel(self): return "ccd"
290 
291  def getKeys(self, datasetType, level):
292  if datasetType is None:
293  keyDict = self.keyDict
294  else:
295  keyDict = self.mappings[datasetType].keys
296  if level is not None and level in self.levels:
297  keyDict = dict(keyDict)
298  for l in self.levels[level]:
299  if l in keyDict:
300  del keyDict[l]
301  return keyDict
302 
303  def update(self):
304  filenames = os.listdir(self.root)
305  rawRegex = re.compile(r"(?P<dataset>\w+)-(?P<visit>\d+)-(?P<ccd>\d).*")
306  self.index = {}
307  for filename in filenames:
308  m = rawRegex.match(filename)
309  if not m:
310  continue
311  index = self.index.setdefault(m.group('dataset'), dict(ccd={None:[]}, visit={None:[]}))
312  visit = int(m.group('visit'))
313  ccd = int(m.group('ccd'))
314  d1 = dict(visit=visit, ccd=ccd)
315  d2 = dict(visit=visit)
316  index['ccd'].setdefault(visit, []).append(d1)
317  index['ccd'][None].append(d1)
318  index['visit'][visit] = [d2]
319  index['visit'][None].append(d1)
320 
321  def keys(self):
322  return self.keyDict
323 
324  def bypass_camera(self, datasetType, pythonType, location, dataId):
325  return self.camera
326 
327  def map_camera(self, dataId, write=False):
329  "lsst.afw.cameraGeom.Camera", "Camera", None, [], dataId
330  )
331 
332  def std_calexp(self, item, dataId):
333  detectorId = dataId["ccd"]
334  detector = self.camera[detectorId]
335  item.setDetector(detector)
336  return item
337 
338  def _computeCcdExposureId(self, dataId):
339  return long(dataId["visit"]) * 10 + long(dataId["ccd"])
340 
341  def _computeCoaddId(self, dataId):
342  # Note: for real IDs, we'd want to include filter here, but it doesn't actually matter
343  # for any of the tests we've done so far, which all assume filter='r'
344  tract = long(dataId['tract'])
345  if tract < 0 or tract >= 128:
346  raise RuntimeError('tract not in range [0,128)')
347  patchX, patchY = map(int, dataId['patch'].split(','))
348  for p in (patchX, patchY):
349  if p < 0 or p >= 2**13:
350  raise RuntimeError('patch component not in range [0, 8192)')
351  return (tract * 2**13 + patchX) * 2**13 + patchY
352 
353  def splitCcdExposureId(ccdExposureId):
354  return dict(visit=(long(ccdExposureId) // 10), ccd=(long(ccdExposureId) % 10))
355 
356  def bypass_ccdExposureId(self, datasetType, pythonType, location, dataId):
357  return self._computeCcdExposureId(dataId)
358 
359  def bypass_ccdExposureId_bits(self, datasetType, pythonType, location, dataId):
360  return 32
361 
362  def bypass_deepCoaddId(self, datasetType, pythonType, location, dataId):
363  return self._computeCoaddId(dataId)
364 
365  def bypass_deepCoaddId_bits(self, datasetType, pythonType, location, dataId):
366  return 1 + 7 + 13*2 + 3
367 
368  def bypass_deepMergedCoaddId(self, datasetType, pythonType, location, dataId):
369  return self._computeCoaddId(dataId)
370 
371  def bypass_deepMergedCoaddId_bits(self, datasetType, pythonType, location, dataId):
372  return 1 + 7 + 13*2 + 3
373 
374 def makeSimpleCamera(
375  nX, nY,
376  sizeX, sizeY,
377  gapX, gapY,
378  pixelSize=1.0,
379  plateScale=20.0,
380  radialDistortion=0.925,
381 ):
382  """Create a camera
383 
384  @param[in] nx: number of detectors in x
385  @param[in] ny: number of detectors in y
386  @param[in] sizeX: detector size in x (pixels)
387  @param[in] sizeY: detector size in y (pixels)
388  @param[in] gapX: gap between detectors in x (mm)
389  @param[in] gapY: gap between detectors in y (mm)
390  @param[in] pixelSize: pixel size (mm) (a float)
391  @param[in] plateScale: plate scale in arcsec/mm; 20.0 is for LSST
392  @param[in] radialDistortion: radial distortion, in mm/rad^2
393  (the r^3 coefficient of the radial distortion polynomial
394  that converts PUPIL in radians to FOCAL_PLANE in mm);
395  0.925 is the value Dave Monet measured for lsstSim data
396 
397  Each detector will have one amplifier (with no raw information).
398  """
399  pScaleRad = lsst.afw.geom.arcsecToRad(plateScale)
400  radialDistortCoeffs = [0.0, 1.0/pScaleRad, 0.0, radialDistortion/pScaleRad]
401  focalPlaneToPupil = lsst.afw.geom.RadialXYTransform(radialDistortCoeffs)
402  nativeSys = lsst.afw.cameraGeom.FOCAL_PLANE
403  transforms = {
404  lsst.afw.cameraGeom.PUPIL: focalPlaneToPupil,
405  }
406  transformMap = lsst.afw.cameraGeom.CameraTransformMap(nativeSys, transforms)
407 
408  detectorList = []
410  for iY in range(nY):
411  cY = (iY - 0.5 * (nY - 1)) * (pixelSize * sizeY + gapY)
412  for iX in range(nX):
413  cX = (iX - 0.5 * (nX - 1)) * (pixelSize * sizeY + gapX)
414  fpPos = lsst.afw.geom.Point2D(cX, cY)
415  detectorName = "detector %d,%d" % (iX, iY)
416  detectorId = len(detectorList) + 1
417  detectorList.append(DetectorWrapper(
418  name = detectorName,
419  id = detectorId,
420  serial = detectorName + " serial",
421  bbox = ccdBBox,
422  ampExtent = ccdBBox.getDimensions(),
423  numAmps = 1,
424  pixelSize = lsst.afw.geom.Extent2D(pixelSize, pixelSize),
425  orientation = lsst.afw.cameraGeom.Orientation(fpPos),
426  plateScale = plateScale,
427  radialDistortion = radialDistortion,
428  ).detector)
429 
430  return lsst.afw.cameraGeom.Camera(
431  name = "Simple Camera",
432  detectorList = detectorList,
433  transformMap = transformMap,
434  )
435 
436 def makeDataRepo(root):
437  """
438  Create a data repository for SimpleMapper and return a butler for it.
439 
440  Clobbers anything already in the given path.
441  """
442  if os.path.exists(root):
443  shutil.rmtree(root)
444  os.makedirs(root)
445  with open(os.path.join(root, "_mapper"), "w") as f:
446  f.write("lsst.pipe.tasks.mocks.SimpleMapper\n")
447  return lsst.daf.persistence.Butler(root=root)
A Detector and the data used to construct it.
Definition: testUtils.py:17
An integer coordinate rectangle.
Definition: Box.h:53
A purely radial polynomial distortion, up to 6th order.
Definition: XYTransform.h:186
double arcsecToRad(double x)
Definition: Angle.h:41