Coverage for python/lsst/daf/butler/core/_topology.py: 75%
49 statements
« prev ^ index » next coverage.py v6.5.0, created at 2023-02-22 03:05 -0800
« prev ^ index » next coverage.py v6.5.0, created at 2023-02-22 03:05 -0800
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 typing import Any, Mapping, Optional
34from lsst.utils.classes import immutable
36from .named import NamedValueAbstractSet
39@enum.unique
40class TopologicalSpace(enum.Enum):
41 """Enumeration of continuous-variable relationships for dimensions.
43 Most dimension relationships are discrete, in that they are regular foreign
44 key relationships between tables. Those connected to a
45 `TopologicalSpace` are not - a row in a table instead occupies some
46 region in a continuous-variable space, and topological operators like
47 "overlaps" between regions in that space define the relationships between
48 rows.
49 """
51 SPATIAL = enum.auto()
52 """The (spherical) sky, using `lsst.sphgeom.Region` objects to represent
53 those regions in memory.
54 """
56 TEMPORAL = enum.auto()
57 """Time, using `Timespan` instances (with TAI endpoints) to represent
58 intervals in memory.
59 """
62@immutable
63class TopologicalFamily(ABC):
64 """A grouping of `TopologicalRelationshipEndpoint` objects.
66 These regions form a hierarchy in which one endpoint's rows always contain
67 another's in a predefined way.
69 This hierarchy means that endpoints in the same family do not generally
70 have to be have to be related using (e.g.) overlaps; instead, the regions
71 from one "best" endpoint from each family are related to the best endpoint
72 from each other family in a query.
74 Parameters
75 ----------
76 name : `str`
77 Unique string identifier for this family.
78 category : `TopologicalSpace`
79 Space in which the regions of this family live.
80 """
82 def __init__(
83 self,
84 name: str,
85 space: TopologicalSpace,
86 ):
87 self.name = name
88 self.space = space
90 def __eq__(self, other: Any) -> bool:
91 if isinstance(other, TopologicalFamily):
92 return self.space == other.space and self.name == other.name
93 return False
95 def __hash__(self) -> int:
96 return hash(self.name)
98 def __contains__(self, other: TopologicalRelationshipEndpoint) -> bool:
99 return other.topology.get(self.space) == self
101 @abstractmethod
102 def choose(
103 self, endpoints: NamedValueAbstractSet[TopologicalRelationshipEndpoint]
104 ) -> TopologicalRelationshipEndpoint:
105 """Select the best member of this family to use.
107 These are to be used in a query join or data ID when more than one
108 is present.
110 Usually this should correspond to the most fine-grained region.
112 Parameters
113 ----------
114 endpoints : `NamedValueAbstractSet` [`TopologicalRelationshipEndpoint`]
115 Endpoints to choose from. May include endpoints that are not
116 members of this family (which should be ignored).
118 Returns
119 -------
120 best : `TopologicalRelationshipEndpoint`
121 The best endpoint that is both a member of ``self`` and in
122 ``endpoints``.
123 """
124 raise NotImplementedError()
126 name: str
127 """Unique string identifier for this family (`str`).
128 """
130 space: TopologicalSpace
131 """Space in which the regions of this family live (`TopologicalSpace`).
132 """
135@immutable
136class TopologicalRelationshipEndpoint(ABC):
137 """Representation of a logical table that can participate in overlap joins.
139 An abstract base class whose instances represent a logical table that
140 may participate in overlap joins defined by a `TopologicalSpace`.
141 """
143 @property
144 @abstractmethod
145 def name(self) -> str:
146 """Return unique string identifier for this endpoint (`str`)."""
147 raise NotImplementedError()
149 @property
150 @abstractmethod
151 def topology(self) -> Mapping[TopologicalSpace, TopologicalFamily]:
152 """Return the relationship families to which this endpoint belongs.
154 It is keyed by the category for that family.
155 """
156 raise NotImplementedError()
158 @property
159 def spatial(self) -> Optional[TopologicalFamily]:
160 """Return this endpoint's `~TopologicalSpace.SPATIAL` family."""
161 return self.topology.get(TopologicalSpace.SPATIAL)
163 @property
164 def temporal(self) -> Optional[TopologicalFamily]:
165 """Return this endpoint's `~TopologicalSpace.TEMPORAL` family."""
166 return self.topology.get(TopologicalSpace.TEMPORAL)