Coverage for python/lsst/daf/butler/core/_topology.py: 80%
47 statements
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-14 09:11 +0000
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-14 09:11 +0000
1# This file is part of daf_butler.
2#
3# Developed for the LSST Data Management System.
4# This product includes software developed by the LSST Project
5# (http://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 <http://www.gnu.org/licenses/>.
22from __future__ import annotations
24__all__ = (
25 "TopologicalSpace",
26 "TopologicalFamily",
27 "TopologicalRelationshipEndpoint",
28)
30import enum
31from abc import ABC, abstractmethod
32from collections.abc import Mapping
33from typing import Any
35from lsst.utils.classes import immutable
37from .named import NamedValueAbstractSet
40@enum.unique
41class TopologicalSpace(enum.Enum):
42 """Enumeration of continuous-variable relationships for dimensions.
44 Most dimension relationships are discrete, in that they are regular foreign
45 key relationships between tables. Those connected to a
46 `TopologicalSpace` are not - a row in a table instead occupies some
47 region in a continuous-variable space, and topological operators like
48 "overlaps" between regions in that space define the relationships between
49 rows.
50 """
52 SPATIAL = enum.auto()
53 """The (spherical) sky, using `lsst.sphgeom.Region` objects to represent
54 those regions in memory.
55 """
57 TEMPORAL = enum.auto()
58 """Time, using `Timespan` instances (with TAI endpoints) to represent
59 intervals in memory.
60 """
63@immutable
64class TopologicalFamily(ABC):
65 """A grouping of `TopologicalRelationshipEndpoint` objects.
67 These regions form a hierarchy in which one endpoint's rows always contain
68 another's in a predefined way.
70 This hierarchy means that endpoints in the same family do not generally
71 have to be have to be related using (e.g.) overlaps; instead, the regions
72 from one "best" endpoint from each family are related to the best endpoint
73 from each other family in a query.
75 Parameters
76 ----------
77 name : `str`
78 Unique string identifier for this family.
79 category : `TopologicalSpace`
80 Space in which the regions of this family live.
81 """
83 def __init__(
84 self,
85 name: str,
86 space: TopologicalSpace,
87 ):
88 self.name = name
89 self.space = space
91 def __eq__(self, other: Any) -> bool:
92 if isinstance(other, TopologicalFamily):
93 return self.space == other.space and self.name == other.name
94 return False
96 def __hash__(self) -> int:
97 return hash(self.name)
99 def __contains__(self, other: TopologicalRelationshipEndpoint) -> bool:
100 return other.topology.get(self.space) == self
102 @abstractmethod
103 def choose(
104 self, endpoints: NamedValueAbstractSet[TopologicalRelationshipEndpoint]
105 ) -> TopologicalRelationshipEndpoint:
106 """Select the best member of this family to use.
108 These are to be used in a query join or data ID when more than one
109 is present.
111 Usually this should correspond to the most fine-grained region.
113 Parameters
114 ----------
115 endpoints : `NamedValueAbstractSet` [`TopologicalRelationshipEndpoint`]
116 Endpoints to choose from. May include endpoints that are not
117 members of this family (which should be ignored).
119 Returns
120 -------
121 best : `TopologicalRelationshipEndpoint`
122 The best endpoint that is both a member of ``self`` and in
123 ``endpoints``.
124 """
125 raise NotImplementedError()
127 name: str
128 """Unique string identifier for this family (`str`).
129 """
131 space: TopologicalSpace
132 """Space in which the regions of this family live (`TopologicalSpace`).
133 """
136@immutable
137class TopologicalRelationshipEndpoint(ABC):
138 """Representation of a logical table that can participate in overlap joins.
140 An abstract base class whose instances represent a logical table that
141 may participate in overlap joins defined by a `TopologicalSpace`.
142 """
144 @property
145 @abstractmethod
146 def name(self) -> str:
147 """Return unique string identifier for this endpoint (`str`)."""
148 raise NotImplementedError()
150 @property
151 @abstractmethod
152 def topology(self) -> Mapping[TopologicalSpace, TopologicalFamily]:
153 """Return the relationship families to which this endpoint belongs.
155 It is keyed by the category for that family.
156 """
157 raise NotImplementedError()
159 @property
160 def spatial(self) -> TopologicalFamily | None:
161 """Return this endpoint's `~TopologicalSpace.SPATIAL` family."""
162 return self.topology.get(TopologicalSpace.SPATIAL)
164 @property
165 def temporal(self) -> TopologicalFamily | None:
166 """Return this endpoint's `~TopologicalSpace.TEMPORAL` family."""
167 return self.topology.get(TopologicalSpace.TEMPORAL)