22 from collections
import OrderedDict
28 from lsst.afw.image import Exposure, MaskedImage, Image, DecoratedImage
30 __all__ = [
"Mapping",
"ImageMapping",
"ExposureMapping",
"CalibrationMapping",
"DatasetMapping"]
35 """Mapping is a base class for all mappings. Mappings are used by 36 the Mapper to map (determine a path to some data given some 37 identifiers) and standardize (convert data into some standard 38 format or type) data, and to query the associated registry to see 39 what data is available. 41 Subclasses must specify self.storage or else override self.map(). 43 Public methods: lookup, have, need, getKeys, map 45 Mappings are specified mainly by policy. A Mapping policy should 48 template (string): a Python string providing the filename for that 49 particular dataset type based on some data identifiers. In the 50 case of redundancy in the path (e.g., file uniquely specified by 51 the exposure number, but filter in the path), the 52 redundant/dependent identifiers can be looked up in the registry. 54 python (string): the Python type for the retrieved data (e.g. 55 lsst.afw.image.ExposureF) 57 persistable (string): the Persistable registration for the on-disk data 60 storage (string, optional): Storage type for this dataset type (e.g. 63 level (string, optional): the level in the camera hierarchy at which the 64 data is stored (Amp, Ccd or skyTile), if relevant 66 tables (string, optional): a whitespace-delimited list of tables in the 67 registry that can be NATURAL JOIN-ed to look up additional 73 Butler dataset type to be mapped. 74 policy : `daf_persistence.Policy` 76 registry : `lsst.obs.base.Registry` 77 Registry for metadata lookups. 78 rootStorage : Storage subclass instance 79 Interface to persisted repository data. 80 provided : `list` of `str` 81 Keys provided by the mapper. 84 def __init__(self, datasetType, policy, registry, rootStorage, provided=None):
87 raise RuntimeError(
"No policy provided for mapping")
101 (k, _formatMap(v, k, datasetType))
103 re.findall(
r'\%\((\w+)\).*?([diouxXeEfFgGcrs])', self.
template)
107 if provided
is not None:
114 if 'level' in policy:
116 if 'tables' in policy:
122 self.
obsTimeName = policy[
'obsTimeName']
if 'obsTimeName' in policy
else None 123 self.
recipe = policy[
'recipe']
if 'recipe' in policy
else 'default' 130 raise RuntimeError(
"Template is not defined for the {} dataset type, ".
format(self.
datasetType) +
131 "it must be set before it can be used.")
134 """Return the dict of keys and value types required for this mapping.""" 137 def map(self, mapper, dataId, write=False):
138 """Standard implementation of map function. 142 mapper: `lsst.daf.persistence.Mapper` 149 lsst.daf.persistence.ButlerLocation 150 Location of object that was mapped. 153 usedDataId = {key: actualId[key]
for key
in self.
keyDict.
keys()}
154 path = mapper._mapActualToPath(self.
template, actualId)
155 if os.path.isabs(path):
156 raise RuntimeError(
"Mapped path should not be absolute.")
163 for ext
in (
None,
'.gz',
'.fz'):
164 if ext
and path.endswith(ext):
166 extPath = path + ext
if ext
else path
171 assert path,
"Fully-qualified filename is empty." 174 if hasattr(mapper, addFunc):
175 addFunc = getattr(mapper, addFunc)
176 additionalData = addFunc(self.
datasetType, actualId)
177 assert isinstance(additionalData, PropertySet), \
178 "Bad type for returned data: %s" (
type(additionalData),)
180 additionalData =
None 183 locationList=path, dataId=actualId.copy(), mapper=mapper,
185 additionalData=additionalData)
188 """Look up properties for in a metadata registry given a partial 193 properties : `list` of `str` 201 Values of properties. 204 raise RuntimeError(
"No registry for lookup")
206 skyMapKeys = (
"tract",
"patch")
218 substitutions = OrderedDict()
220 properties =
list(properties)
224 substitutions[p] = dataId[p]
228 "Cannot look up skymap key '%s'; it must be explicitly included in the data ID" % p
231 substitutions[p] = index
239 if p
not in (
'filter',
'expTime',
'taiObs'):
242 if fastPath
and 'visit' in dataId
and "raw" in self.
tables:
243 lookupDataId = {
'visit': dataId[
'visit']}
246 if dataId
is not None:
247 for k, v
in dataId.items():
254 where.append((k,
'?'))
256 lookupDataId = {k[0]: v
for k, v
in zip(where, values)}
265 result = [tuple(v
if k
in removed
else item[v]
for k, v
in substitutions.items())
269 def have(self, properties, dataId):
270 """Returns whether the provided data identifier has all 271 the properties in the provided list. 275 properties : `list of `str` 283 True if all properties are present. 285 for prop
in properties:
286 if prop
not in dataId:
290 def need(self, properties, dataId):
291 """Ensures all properties in the provided list are present in 292 the data identifier, looking them up as needed. This is only 293 possible for the case where the data identifies a single 298 properties : `list` of `str` 301 Partial dataset identifier 306 Copy of dataset identifier with enhanced values. 308 newId = dataId.copy()
310 for prop
in properties:
311 if prop
not in newId:
312 newProps.append(prop)
313 if len(newProps) == 0:
316 lookups = self.
lookup(newProps, newId)
317 if len(lookups) != 1:
318 raise NoResults(
"No unique lookup for %s from %s: %d matches" %
319 (newProps, newId, len(lookups)),
321 for i, prop
in enumerate(newProps):
322 newId[prop] = lookups[0][i]
326 def _formatMap(ch, k, datasetType):
327 """Convert a format character into a Python type.""" 335 raise RuntimeError(
"Unexpected format specifier %s" 336 " for field %s in template for dataset %s" %
337 (ch, k, datasetType))
341 """ImageMapping is a Mapping subclass for non-camera images. 346 Butler dataset type to be mapped. 347 policy : `daf_persistence.Policy` 349 registry : `lsst.obs.base.Registry` 350 Registry for metadata lookups 352 Path of root directory 355 def __init__(self, datasetType, policy, registry, root, **kwargs):
356 Mapping.__init__(self, datasetType, policy, registry, root, **kwargs)
357 self.
columns = policy.asArray(
'columns')
if 'columns' in policy
else None 361 """ExposureMapping is a Mapping subclass for normal exposures. 366 Butler dataset type to be mapped. 367 policy : `daf_persistence.Policy` 369 registry : `lsst.obs.base.Registry` 370 Registry for metadata lookups 372 Path of root directory 375 def __init__(self, datasetType, policy, registry, root, **kwargs):
376 Mapping.__init__(self, datasetType, policy, registry, root, **kwargs)
377 self.
columns = policy.asArray(
'columns')
if 'columns' in policy
else None 380 return mapper._standardizeExposure(self, item, dataId)
384 """CalibrationMapping is a Mapping subclass for calibration-type products. 386 The difference is that data properties in the query or template 387 can be looked up using a reference Mapping in addition to this one. 389 CalibrationMapping Policies can contain the following: 391 reference (string, optional) 392 a list of tables for finding missing dataset 393 identifier components (including the observation time, if a validity range 394 is required) in the exposure registry; note that the "tables" entry refers 395 to the calibration registry 397 refCols (string, optional) 398 a list of dataset properties required from the 399 reference tables for lookups in the calibration registry 402 true if the calibration dataset has a validity range 403 specified by a column in the tables of the reference dataset in the 404 exposure registry) and two columns in the tables of this calibration 405 dataset in the calibration registry) 407 obsTimeName (string, optional) 408 the name of the column in the reference 409 dataset tables containing the observation time (default "taiObs") 411 validStartName (string, optional) 412 the name of the column in the 413 calibration dataset tables containing the start of the validity range 414 (default "validStart") 416 validEndName (string, optional) 417 the name of the column in the 418 calibration dataset tables containing the end of the validity range 424 Butler dataset type to be mapped. 425 policy : `daf_persistence.Policy` 427 registry : `lsst.obs.base.Registry` 428 Registry for metadata lookups 429 calibRegistry : `lsst.obs.base.Registry` 430 Registry for calibration metadata lookups. 432 Path of calibration root directory. 434 Path of data root directory; used for outputs only. 437 def __init__(self, datasetType, policy, registry, calibRegistry, calibRoot, dataRoot=None, **kwargs):
438 Mapping.__init__(self, datasetType, policy, calibRegistry, calibRoot, **kwargs)
439 self.
reference = policy.asArray(
"reference")
if "reference" in policy
else None 440 self.
refCols = policy.asArray(
"refCols")
if "refCols" in policy
else None 443 if "validRange" in policy
and policy[
"validRange"]:
444 self.
range = (
"?", policy[
"validStartName"], policy[
"validEndName"])
445 if "columns" in policy:
447 if "filter" in policy:
450 if "metadataKey" in policy:
453 def map(self, mapper, dataId, write=False):
454 location = Mapping.map(self, mapper, dataId, write=write)
461 """Look up properties for in a metadata registry given a partial 466 properties : `list` of `str` 467 Properties to look up. 474 Values of properties. 480 newId = dataId.copy()
484 for k, v
in dataId.items():
493 for k
in dataId.keys():
496 columns =
set(properties)
500 return Mapping.lookup(self, properties, newId)
502 lookupDataId = dict(zip(where, values))
504 if len(lookups) != 1:
505 raise RuntimeError(
"No unique lookup for %s from %s: %d matches" %
506 (columns, dataId, len(lookups)))
507 if columns ==
set(properties):
510 for i, prop
in enumerate(columns):
511 newId[prop] = lookups[0][i]
512 return Mapping.lookup(self, properties, newId)
515 """Default standardization function for calibration datasets. 517 If the item is of a type that should be standardized, the base class 518 ``standardizeExposure`` method is called, otherwise the item is returned 523 mapping : `lsst.obs.base.Mapping` 524 Mapping object to pass through. 526 Will be standardized if of type lsst.afw.image.Exposure, 527 lsst.afw.image.DecoratedImage, lsst.afw.image.Image 528 or lsst.afw.image.MaskedImage 535 `lsst.afw.image.Exposure` or item 536 The standardized object. 538 if issubclass(
doImport(self.
python), (Exposure, MaskedImage, Image, DecoratedImage)):
539 return mapper._standardizeExposure(self, item, dataId, filter=self.
setFilter)
544 """DatasetMapping is a Mapping subclass for non-Exposure datasets that can 545 be retrieved by the standard daf_persistence mechanism. 547 The differences are that the Storage type must be specified and no 548 Exposure standardization is performed. 550 The "storage" entry in the Policy is mandatory; the "tables" entry is 551 optional; no "level" entry is allowed. 556 Butler dataset type to be mapped. 557 policy : `daf_persistence.Policy` 559 registry : `lsst.obs.base.Registry` 560 Registry for metadata lookups 562 Path of root directory 565 def __init__(self, datasetType, policy, registry, root, **kwargs):
566 Mapping.__init__(self, datasetType, policy, registry, root, **kwargs)
def format(config, name=None, writeSourceLine=True, prefix="", verbose=False)
def __init__(self, datasetType, policy, registry, root, kwargs)
def __init__(self, datasetType, policy, registry, calibRegistry, calibRoot, dataRoot=None, kwargs)
daf::base::PropertySet * set
def __init__(self, datasetType, policy, registry, root, kwargs)
def standardize(self, mapper, item, dataId)
def map(self, mapper, dataId, write=False)
def have(self, properties, dataId)
def standardize(self, mapper, item, dataId)
def need(self, properties, dataId)
def lookup(self, properties, dataId)
def __init__(self, datasetType, policy, registry, root, kwargs)
def lookup(self, properties, dataId)
def __init__(self, datasetType, policy, registry, rootStorage, provided=None)
Backwards-compatibility support for depersisting the old Calib (FluxMag0/FluxMag0Err) objects...
daf::base::PropertyList * list
def map(self, mapper, dataId, write=False)