Coverage for python/lsst/obs/base/exposureIdInfo.py: 35%
36 statements
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-22 09:58 +0000
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-22 09:58 +0000
1# This file is part of obs_base.
2#
3# Developed for the LSST Data Management System.
4# This product includes software developed by the LSST Project
5# (https://www.lsst.org).
6# See the COPYRIGHT file at the top-level directory of this distribution
7# for details of code ownership.
8#
9# This program is free software: you can redistribute it and/or modify
10# it under the terms of the GNU General Public License as published by
11# the Free Software Foundation, either version 3 of the License, or
12# (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License
20# along with this program. If not, see <https://www.gnu.org/licenses/>.
22from __future__ import annotations
24__all__ = ["ExposureIdInfo"]
26from typing import Optional
28from deprecated.sphinx import deprecated
29from lsst.afw.table import IdFactory
30from lsst.daf.butler import DataCoordinate
33@deprecated(
34 "Deprecated in favor of `lsst.meas.base.IdGenerator`; will be removed after v27.",
35 version="v26",
36 category=FutureWarning,
37)
38class ExposureIdInfo:
39 """Struct representing an exposure ID and the number of bits it uses.
41 Parameters
42 ----------
43 expId : `int`
44 Exposure ID. Note that this is typically the ID of an
45 `afw.image.Exposure`, not the ID of an actual observation, and hence it
46 usually either includes a detector component or is derived from SkyMap
47 IDs, and the observation ID component usually represents a ``visit``
48 rather than ``exposure``. For code using the Gen3 butler, this will
49 usually be obtained via a `~lsst.daf.butler.DimensionPacker` (see
50 example below).
51 expBits : `int`
52 Maximum number of bits allowed for exposure IDs of this type.
53 maxBits : `int`, optional
54 Maximum number of bits available for values that combine exposure ID
55 with other information, such as source ID. If not provided
56 (recommended when possible), `unusedBits` will be computed by assuming
57 the full ID must fit an an `lsst.afw.table` RecordId field.
59 Examples
60 --------
61 One common use is creating an ID factory for making a source table.
62 For example, given a `ExposureIdInfo` instance ``info``,
64 .. code-block:: python
66 from lsst.afw.table import SourceTable
67 schema = SourceTable.makeMinimalSchema()
68 #...add fields to schema as desired, then...
69 sourceTable = SourceTable.make(self.schema, info.makeSourceIdFactory())
71 An `ExposureIdInfo` instance can be obtained from a
72 `~lsst.daf.butler.DataCoordinate` with:
74 .. code-block:: python
76 expandedDataId = butler.registry.expandDataId(dataId)
77 info = ExposureIdInfo.fromDataId(expandedDataId, "visit_detector")
79 The first line should be unnecessary for the data IDs passed to
80 `~lsst.pipe.base.PipelineTask` methods, as those are already expanded, and
81 ``"visit_detector"`` can be replaced by other strings to pack data IDs with
82 different dimensions (e.g. ``"tract_patch"`` or ``"tract_patch_band"``);
83 see the data repository's dimensions configuration for other options.
85 At least one bit must be reserved for the exposure ID, even if there is no
86 exposure ID, for reasons that are not entirely clear (this is DM-6664).
87 """
89 def __init__(self, expId: int = 0, expBits: int = 1, maxBits: Optional[int] = None):
90 """Construct an ExposureIdInfo
92 See the class doc string for an explanation of the arguments.
93 """
94 expId = int(expId)
95 expBits = int(expBits)
97 if expId.bit_length() > expBits:
98 raise RuntimeError("expId=%s uses %s bits > expBits=%s" % (expId, expId.bit_length(), expBits))
100 self.expId = expId
101 self.expBits = expBits
103 if maxBits is not None:
104 maxBits = int(maxBits)
105 if maxBits < expBits:
106 raise RuntimeError("expBits=%s > maxBits=%s" % (expBits, maxBits))
107 self.maxBits = maxBits
109 def __repr__(self) -> str:
110 return (
111 f"{self.__class__.__name__}(expId={self.expId}, expBits={self.expBits}, maxBits={self.maxBits})"
112 )
114 @classmethod
115 def fromDataId(
116 cls, dataId: DataCoordinate, name: str = "visit_detector", maxBits: Optional[int] = None
117 ) -> ExposureIdInfo:
118 """Construct an instance from a fully-expanded data ID.
120 Parameters
121 ----------
122 dataId : `lsst.daf.butler.DataCoordinate`
123 An expanded data ID that identifies the dimensions to be packed and
124 contains extra information about the maximum values for those
125 dimensions. An expanded data ID can be obtained from
126 `Registry.expandDataId`, but all data IDs passed to `PipelineTask`
127 methods should already be expanded.
128 name : `str`, optional
129 Name of the packer to use. The set of available packers can be
130 found in the data repository's dimension configuration (see the
131 "packers" section of ``dimensions.yaml`` in ``daf_butler`` for the
132 defaults).
133 maxBits : `int`, optional
134 Forwarded as the ``__init__`` parameter of the same name. Should
135 usually be unnecessary.
137 Returns
138 -------
139 info : `ExposureIdInfo`
140 An `ExposureIdInfo` instance.
141 """
142 if not isinstance(dataId, DataCoordinate) or not dataId.hasRecords():
143 raise RuntimeError(
144 "A fully-expanded data ID is required; use Registry.expandDataId to obtain one."
145 )
146 expId, expBits = dataId.pack(name, returnMaxBits=True)
147 return cls(expId=expId, expBits=expBits, maxBits=maxBits)
149 @property
150 def unusedBits(self) -> int:
151 """Maximum number of bits available for non-exposure info `(int)`."""
152 if self.maxBits is None:
153 from lsst.afw.table import IdFactory
155 return IdFactory.computeReservedFromMaxBits(self.expBits)
156 else:
157 return self.maxBits - self.expBits
159 def makeSourceIdFactory(self) -> IdFactory:
160 """Make a `lsst.afw.table.SourceTable.IdFactory` instance from this
161 exposure information.
163 Returns
164 -------
165 idFactory : `lsst.afw.table.SourceTable.IdFactory`
166 An ID factory that generates new IDs that fold in the image IDs
167 managed by this object.
168 """
169 return IdFactory.makeSource(self.expId, self.unusedBits)