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