23 from collections
import OrderedDict
29 __all__ = [
"Mapping",
"ImageMapping",
"ExposureMapping",
"CalibrationMapping",
"DatasetMapping"]
34 """Mapping is a base class for all mappings. Mappings are used by 35 the Mapper to map (determine a path to some data given some 36 identifiers) and standardize (convert data into some standard 37 format or type) data, and to query the associated registry to see 38 what data is available. 40 Subclasses must specify self.storage or else override self.map(). 42 Public methods: lookup, have, need, getKeys, map 44 Mappings are specified mainly by policy. A Mapping policy should 47 template (string): a Python string providing the filename for that 48 particular dataset type based on some data identifiers. In the 49 case of redundancy in the path (e.g., file uniquely specified by 50 the exposure number, but filter in the path), the 51 redundant/dependent identifiers can be looked up in the registry. 53 python (string): the Python type for the retrieved data (e.g. 54 lsst.afw.image.ExposureF) 56 persistable (string): the Persistable registration for the on-disk data 59 storage (string, optional): Storage type for this dataset type (e.g. 62 level (string, optional): the level in the camera hierarchy at which the 63 data is stored (Amp, Ccd or skyTile), if relevant 65 tables (string, optional): a whitespace-delimited list of tables in the 66 registry that can be NATURAL JOIN-ed to look up additional 72 Butler dataset type to be mapped. 73 policy : `daf_persistence.Policy` 75 registry : `lsst.obs.base.Registry` 76 Registry for metadata lookups. 77 rootStorage : Storage subclass instance 78 Interface to persisted repository data. 79 provided : `list` of `str` 80 Keys provided by the mapper. 83 def __init__(self, datasetType, policy, registry, rootStorage, provided=None):
86 raise RuntimeError(
"No policy provided for mapping")
100 (k, _formatMap(v, k, datasetType))
102 re.findall(
r'\%\((\w+)\).*?([diouxXeEfFgGcrs])', self.
template)
106 if provided
is not None:
113 if 'level' in policy:
115 if 'tables' in policy:
121 self.
obsTimeName = policy[
'obsTimeName']
if 'obsTimeName' in policy
else None 122 self.
recipe = policy[
'recipe']
if 'recipe' in policy
else 'default' 129 raise RuntimeError(
"Template is not defined for the {} dataset type, ".
format(self.
datasetType) +
130 "it must be set before it can be used.")
133 """Return the dict of keys and value types required for this mapping.""" 136 def map(self, mapper, dataId, write=False):
137 """Standard implementation of map function. 141 mapper: `lsst.daf.persistence.Mapper` 148 lsst.daf.persistence.ButlerLocation 149 Location of object that was mapped. 152 usedDataId = {key: actualId[key]
for key
in self.
keyDict.
keys()}
153 path = mapper._mapActualToPath(self.
template, actualId)
154 if os.path.isabs(path):
155 raise RuntimeError(
"Mapped path should not be absolute.")
162 for ext
in (
None,
'.gz',
'.fz'):
163 if ext
and path.endswith(ext):
165 extPath = path + ext
if ext
else path
170 assert path,
"Fully-qualified filename is empty." 173 if hasattr(mapper, addFunc):
174 addFunc = getattr(mapper, addFunc)
175 additionalData = addFunc(self.
datasetType, actualId)
176 assert isinstance(additionalData, PropertySet), \
177 "Bad type for returned data: %s" (
type(additionalData),)
179 additionalData =
None 182 locationList=path, dataId=actualId.copy(), mapper=mapper,
184 additionalData=additionalData)
187 """Look up properties for in a metadata registry given a partial 192 properties : `list` of `str` 200 Values of properties. 203 raise RuntimeError(
"No registry for lookup")
205 skyMapKeys = (
"tract",
"patch")
217 substitutions = OrderedDict()
219 properties =
list(properties)
223 substitutions[p] = dataId[p]
227 "Cannot look up skymap key '%s'; it must be explicitly included in the data ID" % p
230 substitutions[p] = index
238 if p
not in (
'filter',
'expTime',
'taiObs'):
241 if fastPath
and 'visit' in dataId
and "raw" in self.
tables:
242 lookupDataId = {
'visit': dataId[
'visit']}
245 if dataId
is not None:
246 for k, v
in dataId.items():
253 where.append((k,
'?'))
255 lookupDataId = {k[0]: v
for k, v
in zip(where, values)}
264 result = [tuple(v
if k
in removed
else item[v]
for k, v
in substitutions.items())
268 def have(self, properties, dataId):
269 """Returns whether the provided data identifier has all 270 the properties in the provided list. 274 properties : `list of `str` 282 True if all properties are present. 284 for prop
in properties:
285 if prop
not in dataId:
289 def need(self, properties, dataId):
290 """Ensures all properties in the provided list are present in 291 the data identifier, looking them up as needed. This is only 292 possible for the case where the data identifies a single 297 properties : `list` of `str` 300 Partial dataset identifier 305 Copy of dataset identifier with enhanced values. 307 newId = dataId.copy()
309 for prop
in properties:
310 if prop
not in newId:
311 newProps.append(prop)
312 if len(newProps) == 0:
315 lookups = self.
lookup(newProps, newId)
316 if len(lookups) != 1:
317 raise NoResults(
"No unique lookup for %s from %s: %d matches" %
318 (newProps, newId, len(lookups)),
320 for i, prop
in enumerate(newProps):
321 newId[prop] = lookups[0][i]
325 def _formatMap(ch, k, datasetType):
326 """Convert a format character into a Python type.""" 334 raise RuntimeError(
"Unexpected format specifier %s" 335 " for field %s in template for dataset %s" %
336 (ch, k, datasetType))
340 """ImageMapping is a Mapping subclass for non-camera images. 345 Butler dataset type to be mapped. 346 policy : `daf_persistence.Policy` 348 registry : `lsst.obs.base.Registry` 349 Registry for metadata lookups 351 Path of root directory 354 def __init__(self, datasetType, policy, registry, root, **kwargs):
355 Mapping.__init__(self, datasetType, policy, registry, root, **kwargs)
356 self.
columns = policy.asArray(
'columns')
if 'columns' in policy
else None 360 """ExposureMapping is a Mapping subclass for normal exposures. 365 Butler dataset type to be mapped. 366 policy : `daf_persistence.Policy` 368 registry : `lsst.obs.base.Registry` 369 Registry for metadata lookups 371 Path of root directory 374 def __init__(self, datasetType, policy, registry, root, **kwargs):
375 Mapping.__init__(self, datasetType, policy, registry, root, **kwargs)
376 self.
columns = policy.asArray(
'columns')
if 'columns' in policy
else None 379 return mapper._standardizeExposure(self, item, dataId)
383 """CalibrationMapping is a Mapping subclass for calibration-type products. 385 The difference is that data properties in the query or template 386 can be looked up using a reference Mapping in addition to this one. 388 CalibrationMapping Policies can contain the following: 390 reference (string, optional) 391 a list of tables for finding missing dataset 392 identifier components (including the observation time, if a validity range 393 is required) in the exposure registry; note that the "tables" entry refers 394 to the calibration registry 396 refCols (string, optional) 397 a list of dataset properties required from the 398 reference tables for lookups in the calibration registry 401 true if the calibration dataset has a validity range 402 specified by a column in the tables of the reference dataset in the 403 exposure registry) and two columns in the tables of this calibration 404 dataset in the calibration registry) 406 obsTimeName (string, optional) 407 the name of the column in the reference 408 dataset tables containing the observation time (default "taiObs") 410 validStartName (string, optional) 411 the name of the column in the 412 calibration dataset tables containing the start of the validity range 413 (default "validStart") 415 validEndName (string, optional) 416 the name of the column in the 417 calibration dataset tables containing the end of the validity range 423 Butler dataset type to be mapped. 424 policy : `daf_persistence.Policy` 426 registry : `lsst.obs.base.Registry` 427 Registry for metadata lookups 428 calibRegistry : `lsst.obs.base.Registry` 429 Registry for calibration metadata lookups. 431 Path of calibration root directory. 433 Path of data root directory; used for outputs only. 436 def __init__(self, datasetType, policy, registry, calibRegistry, calibRoot, dataRoot=None, **kwargs):
437 Mapping.__init__(self, datasetType, policy, calibRegistry, calibRoot, **kwargs)
438 self.
reference = policy.asArray(
"reference")
if "reference" in policy
else None 439 self.
refCols = policy.asArray(
"refCols")
if "refCols" in policy
else None 442 if "validRange" in policy
and policy[
"validRange"]:
443 self.
range = (
"?", policy[
"validStartName"], policy[
"validEndName"])
444 if "columns" in policy:
446 if "filter" in policy:
449 if "metadataKey" in policy:
452 def map(self, mapper, dataId, write=False):
453 location = Mapping.map(self, mapper, dataId, write=write)
460 """Look up properties for in a metadata registry given a partial 465 properties : `list` of `str` 466 Properties to look up. 473 Values of properties. 479 newId = dataId.copy()
483 for k, v
in dataId.items():
492 for k
in dataId.keys():
495 columns =
set(properties)
499 return Mapping.lookup(self, properties, newId)
501 lookupDataId = dict(zip(where, values))
503 if len(lookups) != 1:
504 raise RuntimeError(
"No unique lookup for %s from %s: %d matches" %
505 (columns, dataId, len(lookups)))
506 if columns ==
set(properties):
509 for i, prop
in enumerate(columns):
510 newId[prop] = lookups[0][i]
511 return Mapping.lookup(self, properties, newId)
514 return mapper._standardizeExposure(self, item, dataId, filter=self.
setFilter)
518 """DatasetMapping is a Mapping subclass for non-Exposure datasets that can 519 be retrieved by the standard daf_persistence mechanism. 521 The differences are that the Storage type must be specified and no 522 Exposure standardization is performed. 524 The "storage" entry in the Policy is mandatory; the "tables" entry is 525 optional; no "level" entry is allowed. 530 Butler dataset type to be mapped. 531 policy : `daf_persistence.Policy` 533 registry : `lsst.obs.base.Registry` 534 Registry for metadata lookups 536 Path of root directory 539 def __init__(self, datasetType, policy, registry, root, **kwargs):
540 Mapping.__init__(self, datasetType, policy, registry, root, **kwargs)
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 format(config, name=None, writeSourceLine=True, prefix="", verbose=False)
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)
daf::base::PropertyList * list
def map(self, mapper, dataId, write=False)