LSSTApplications  1.1.2+25,10.0+13,10.0+132,10.0+133,10.0+224,10.0+41,10.0+8,10.0-1-g0f53050+14,10.0-1-g4b7b172+19,10.0-1-g61a5bae+98,10.0-1-g7408a83+3,10.0-1-gc1e0f5a+19,10.0-1-gdb4482e+14,10.0-11-g3947115+2,10.0-12-g8719d8b+2,10.0-15-ga3f480f+1,10.0-2-g4f67435,10.0-2-gcb4bc6c+26,10.0-28-gf7f57a9+1,10.0-3-g1bbe32c+14,10.0-3-g5b46d21,10.0-4-g027f45f+5,10.0-4-g86f66b5+2,10.0-4-gc4fccf3+24,10.0-40-g4349866+2,10.0-5-g766159b,10.0-5-gca2295e+25,10.0-6-g462a451+1
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 
41 __all__ = ("SimpleMapper", "makeSimpleCamera", "makeDataRepo")
42 
43 class PersistenceType(object):
44  """Base class of a hierarchy used by SimpleMapper to defined different kinds of types of objects
45  to persist.
46 
47  PersistenceType objects are never instantiated; only the type objects are used (we needed a
48  simple singleton struct that could be inherited, which is exactly what a Python type is).
49  """
50  python = None
51  cpp = "ignored"
52  storage = None
53  ext = ""
54  suffixes = ()
55 
56  @classmethod
57  def makeButlerLocation(cls, path, dataId, suffix=None):
58  """Method called by SimpleMapping to implement a map_ method."""
59  return lsst.daf.persistence.ButlerLocation(cls.python, cls.cpp, cls.storage, [path], dataId)
60 
62  """Persistence type for things that don't actually use daf_persistence.
63  """
64 
65  python = "lsst.daf.base.PropertySet" # something to import even when we don't need to
66 
67  @classmethod
68  def makeButlerLocation(cls, path, dataId, suffix=None):
69  """Method called by SimpleMapping to implement a map_ method; overridden to not use the path."""
70  return lsst.daf.persistence.ButlerLocation(cls.python, cls.cpp, cls.storage, [], dataId)
71 
73  """Persistence type of Exposure images.
74  """
75 
76  python = "lsst.afw.image.ExposureF"
77  cpp = "ExposureF"
78  storage = "FitsStorage"
79  ext = ".fits"
80  suffixes = ("_sub",)
81 
82  @classmethod
83  def makeButlerLocation(cls, path, dataId, suffix=None):
84  """Method called by SimpleMapping to implement a map_ method; overridden to support subimages."""
85  if suffix is None:
86  loc = super(ExposurePersistenceType, cls).makeButlerLocation(path, dataId, suffix=None)
87  elif suffix == "_sub":
88  subId = dataId.copy()
89  bbox = subId.pop('bbox')
90  loc = super(ExposurePersistenceType, cls).makeButlerLocation(path, subId, suffix=None)
91  loc.additionalData.set('llcX', bbox.getMinX())
92  loc.additionalData.set('llcY', bbox.getMinY())
93  loc.additionalData.set('width', bbox.getWidth())
94  loc.additionalData.set('height', bbox.getHeight())
95  if 'imageOrigin' in dataId:
96  loc.additionalData.set('imageOrigin',
97  dataId['imageOrigin'])
98  return loc
99 
101  python = "lsst.skymap.BaseSkyMap"
102  storage = "PickleStorage"
103  ext = ".pickle"
104 
106  python = "lsst.afw.table.BaseCatalog"
107  cpp = "BaseCatalog"
108  storage = "FitsCatalogStorage"
109  ext = ".fits"
110 
112  python = "lsst.afw.table.SimpleCatalog"
113  cpp = "SimpleCatalog"
114 
116  python = "lsst.afw.table.SourceCatalog"
117  cpp = "SourceCatalog"
118 
120  python = "lsst.afw.table.ExposureCatalog"
121  cpp = "ExposureCatalog"
122 
123 class SimpleMapping(object):
124  """Mapping object used to implement SimpleMapper, similar in intent to lsst.daf.peristence.Mapping.
125  """
126 
127  template = None
128  keys = {}
129 
130  def __init__(self, persistence, template=None, keys=None):
131  self.persistence = persistence
132  if template is not None:
133  self.template = template
134  if keys is not None:
135  self.keys = keys
136 
137  def map(self, dataset, root, dataId, suffix=None):
138  if self.template is not None:
139  path = os.path.join(root, self.template.format(dataset=dataset, ext=self.persistence.ext,
140  **dataId))
141  else:
142  path = None
143  return self.persistence.makeButlerLocation(path, dataId, suffix=suffix)
144 
146  """Mapping for dataset types that are organized the same way as raw data (i.e. by CCD)."""
147 
148  template = "{dataset}-{visit:04d}-{ccd:01d}{ext}"
149  keys = dict(visit=int, ccd=int)
150 
151  def query(self, index, level, format, dataId):
152  results = []
153  visit = dataId.get('visit', None)
154  dictList = index[level][visit]
155  ccd = dataId.get('ccd', None)
156  for d in dictList.values():
157  if ccd is not None and d['ccd'] != ccd:
158  continue
159  results.append(tuple(d[f] for f in format))
160  return results
161 
163  """Mapping for dataset types that are organized according to a SkyMap subdivision of the sky."""
164 
165  template = "{dataset}-{filter}-{tract:02d}-{patch}{ext}"
166  keys = dict(filter=str, tract=int, patch=str)
167 
169  """Mapping for CoaddTempExp datasets."""
170 
171  template = "{dataset}-{tract:02d}-{patch}-{visit:04d}{ext}"
172  keys = dict(tract=int, patch=str, visit=int)
173 
175  """Mapping for forced_src datasets."""
176 
177  template = "{dataset}-{tract:02d}-{visit:04d}-{ccd:01d}{ext}"
178  keys = dict(tract=int, ccd=int, visit=int)
179 
180 class MapperMeta(type):
181  """Metaclass for SimpleMapper that creates map_ and query_ methods for everything found in the
182  'mappings' class variable.
183  """
184 
185  @staticmethod
186  def _makeMapClosure(dataset, mapping, suffix=None):
187  def mapClosure(self, dataId, write=False):
188  return mapping.map(dataset, self.root, dataId, suffix=suffix)
189  return mapClosure
190 
191  @staticmethod
192  def _makeQueryClosure(dataset, mapping):
193  def queryClosure(self, level, format, dataId):
194  return mapping.query(self.index, level, format, dataId)
195  return queryClosure
196 
197  def __init__(cls, name, bases, dict_):
198  type.__init__(cls, name, bases, dict_)
199  cls.keyDict = dict()
200  for dataset, mapping in cls.mappings.iteritems():
201  setattr(cls, "map_" + dataset, MapperMeta._makeMapClosure(dataset, mapping, suffix=None))
202  for suffix in mapping.persistence.suffixes:
203  setattr(cls, "map_" + dataset + suffix,
204  MapperMeta._makeMapClosure(dataset, mapping, suffix=suffix))
205  if hasattr(mapping, "query"):
206  setattr(cls, "query_" + dataset, MapperMeta._makeQueryClosure(dataset, mapping))
207  cls.keyDict.update(mapping.keys)
208 
210  """
211  An extremely simple mapper for an imaginary camera for use in integration tests.
212 
213  As SimpleMapper does not inherit from daf.butlerUtils.CameraMapper, it does not
214  use a policy file to set mappings or a registry; all the information is here
215  (in the map_* and query_* methods).
216 
217  The imaginary camera's raw data format has only 'visit' and 'ccd' keys, with
218  two CCDs per visit (by default).
219  """
220 
221  __metaclass__ = MapperMeta
222 
223  mappings = dict(
224  calexp = RawMapping(ExposurePersistenceType),
225  forced_src = ForcedSrcMapping(SourceCatalogPersistenceType),
226  forced_src_schema = SimpleMapping(SourceCatalogPersistenceType,
227  template="{dataset}{ext}", keys={}),
228  truth = SimpleMapping(SimpleCatalogPersistenceType, template="{dataset}-{tract:02d}{ext}",
229  keys={"tract":int}),
230  simsrc = RawMapping(SimpleCatalogPersistenceType, template="{dataset}-{tract:02d}{ext}",
231  keys={"tract":int}),
232  observations = SimpleMapping(ExposureCatalogPersistenceType, template="{dataset}-{tract:02d}{ext}",
233  keys={"tract":int}),
234  ccdExposureId = RawMapping(BypassPersistenceType),
235  ccdExposureId_bits = SimpleMapping(BypassPersistenceType),
236  deepCoaddId = SkyMapping(BypassPersistenceType),
237  deepCoaddId_bits = SimpleMapping(BypassPersistenceType),
238  deepCoadd_skyMap = SimpleMapping(SkyMapPersistenceType, template="{dataset}{ext}", keys={}),
239  deepCoadd = SkyMapping(ExposurePersistenceType),
240  deepCoadd_calexp = SkyMapping(ExposurePersistenceType),
241  deepCoadd_calexpBackground = SkyMapping(CatalogPersistenceType),
242  deepCoadd_icSrc = SkyMapping(SourceCatalogPersistenceType),
243  deepCoadd_icSrc_schema = SimpleMapping(SourceCatalogPersistenceType,
244  template="{dataset}{ext}", keys={}),
245  deepCoadd_src = SkyMapping(SourceCatalogPersistenceType),
246  deepCoadd_src_schema = SimpleMapping(SourceCatalogPersistenceType,
247  template="{dataset}{ext}", keys={}),
248  deepCoadd_forced_src = SkyMapping(SourceCatalogPersistenceType),
249  deepCoadd_forced_src_schema = SimpleMapping(SourceCatalogPersistenceType,
250  template="{dataset}{ext}", keys={}),
251  deepCoadd_mock = SkyMapping(ExposurePersistenceType),
252  deepCoadd_tempExp = TempExpMapping(ExposurePersistenceType),
253  deepCoadd_tempExp_mock = TempExpMapping(ExposurePersistenceType),
254  )
255 
256  levels = dict(
257  visit = ['ccd'],
258  ccd = [],
259  )
260 
261  def __init__(self, root):
262  super(SimpleMapper, self).__init__()
263  self.root = root
264  self.camera = makeSimpleCamera(nX=1, nY=2, sizeX=400, sizeY=200, gapX=2, gapY=2)
265  self.update()
266 
267  def getDefaultLevel(self): return "ccd"
268 
269  def getKeys(self, datasetType, level):
270  if datasetType is None:
271  keyDict = self.keyDict
272  else:
273  keyDict = self.mappings[datasetType].keys
274  if level is not None and level in self.levels:
275  keyDict = dict(keyDict)
276  for l in self.levels[level]:
277  if l in keyDict:
278  del keyDict[l]
279  return keyDict
280 
281  def update(self):
282  filenames = os.listdir(self.root)
283  rawRegex = re.compile(r"(?P<dataset>\w+)-(?P<visit>\d+)-(?P<ccd>\d).*")
284  self.index = {}
285  for filename in filenames:
286  m = rawRegex.match(filename)
287  if not m:
288  continue
289  index = self.index.setdefault(m.group('dataset'), dict(ccd={None:[]}, visit={None:[]}))
290  visit = int(m.group('visit'))
291  ccd = int(m.group('ccd'))
292  d1 = dict(visit=visit, ccd=ccd)
293  d2 = dict(visit=visit)
294  index['ccd'].setdefault(visit, []).append(d1)
295  index['ccd'][None].append(d1)
296  index['visit'][visit] = [d2]
297  index['visit'][None].append(d1)
298 
299  def keys(self):
300  return self.keyDict
301 
302  def bypass_camera(self, datasetType, pythonType, location, dataId):
303  return self.camera
304 
305  def map_camera(self, dataId, write=False):
307  "lsst.afw.cameraGeom.Camera", "Camera", None, [], dataId
308  )
309 
310  def std_calexp(self, item, dataId):
311  detectorId = dataId["ccd"]
312  detector = self.camera[detectorId]
313  item.setDetector(detector)
314  return item
315 
316  def _computeCcdExposureId(self, dataId):
317  return long(dataId["visit"]) * 10 + long(dataId["ccd"])
318 
319  def _computeCoaddId(self, dataId):
320  # Note: for real IDs, we'd want to include filter here, but it doesn't actually matter
321  # for any of the tests we've done so far, which all assume filter='r'
322  tract = long(dataId['tract'])
323  if tract < 0 or tract >= 128:
324  raise RuntimeError('tract not in range [0,128)')
325  patchX, patchY = map(int, dataId['patch'].split(','))
326  for p in (patchX, patchY):
327  if p < 0 or p >= 2**13:
328  raise RuntimeError('patch component not in range [0, 8192)')
329  return (tract * 2**13 + patchX) * 2**13 + patchY
330 
331  def splitCcdExposureId(ccdExposureId):
332  return dict(visit=(long(ccdExposureId) // 10), ccd=(long(ccdExposureId) % 10))
333 
334  def bypass_ccdExposureId(self, datasetType, pythonType, location, dataId):
335  return self._computeCcdExposureId(dataId)
336 
337  def bypass_ccdExposureId_bits(self, datasetType, pythonType, location, dataId):
338  return 32
339 
340  def bypass_deepCoaddId(self, datasetType, pythonType, location, dataId):
341  return self._computeCoaddId(dataId)
342 
343  def bypass_deepCoaddId_bits(self, datasetType, pythonType, location, dataId):
344  return 1 + 7 + 13*2 + 3
345 
346 def makeSimpleCamera(
347  nX, nY,
348  sizeX, sizeY,
349  gapX, gapY,
350  pixelSize=1.0,
351  plateScale=20.0,
352  radialDistortion=0.925,
353 ):
354  """Create a camera
355 
356  @param[in] nx: number of detectors in x
357  @param[in] ny: number of detectors in y
358  @param[in] sizeX: detector size in x (pixels)
359  @param[in] sizeY: detector size in y (pixels)
360  @param[in] gapX: gap between detectors in x (mm)
361  @param[in] gapY: gap between detectors in y (mm)
362  @param[in] pixelSize: pixel size (mm) (a float)
363  @param[in] plateScale: plate scale in arcsec/mm; 20.0 is for LSST
364  @param[in] radialDistortion: radial distortion, in mm/rad^2
365  (the r^3 coefficient of the radial distortion polynomial
366  that converts PUPIL in radians to FOCAL_PLANE in mm);
367  0.925 is the value Dave Monet measured for lsstSim data
368 
369  Each detector will have one amplifier (with no raw information).
370  """
371  pScaleRad = lsst.afw.geom.arcsecToRad(plateScale)
372  radialDistortCoeffs = [0.0, 1.0/pScaleRad, 0.0, radialDistortion/pScaleRad]
373  focalPlaneToPupil = lsst.afw.geom.RadialXYTransform(radialDistortCoeffs)
374  nativeSys = lsst.afw.cameraGeom.FOCAL_PLANE
375  transforms = {
376  lsst.afw.cameraGeom.PUPIL: focalPlaneToPupil,
377  }
378  transformMap = lsst.afw.cameraGeom.CameraTransformMap(nativeSys, transforms)
379 
380  detectorList = []
382  for iY in range(nY):
383  cY = (iY - 0.5 * (nY - 1)) * (pixelSize * sizeY + gapY)
384  for iX in range(nX):
385  cX = (iX - 0.5 * (nX - 1)) * (pixelSize * sizeY + gapX)
386  fpPos = lsst.afw.geom.Point2D(cX, cY)
387  detectorName = "detector %d,%d" % (iX, iY)
388  detectorId = len(detectorList) + 1
389  detectorList.append(DetectorWrapper(
390  name = detectorName,
391  id = detectorId,
392  serial = detectorName + " serial",
393  bbox = ccdBBox,
394  ampExtent = ccdBBox.getDimensions(),
395  numAmps = 1,
396  pixelSize = lsst.afw.geom.Extent2D(pixelSize, pixelSize),
397  orientation = lsst.afw.cameraGeom.Orientation(fpPos),
398  plateScale = plateScale,
399  radialDistortion = radialDistortion,
400  ).detector)
401 
402  return lsst.afw.cameraGeom.Camera(
403  name = "Simple Camera",
404  detectorList = detectorList,
405  transformMap = transformMap,
406  )
407 
408 def makeDataRepo(root):
409  """
410  Create a data repository for SimpleMapper and return a butler for it.
411 
412  Clobbers anything already in the given path.
413  """
414  if os.path.exists(root):
415  shutil.rmtree(root)
416  os.makedirs(root)
417  with open(os.path.join(root, "_mapper"), "w") as f:
418  f.write("lsst.pipe.tasks.mocks.SimpleMapper\n")
419  return lsst.daf.persistence.Butler(root=root)
A Detector and the data used to construct it.
Definition: testUtils.py:15
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