LSST Applications g0b6bd0c080+a72a5dd7e6,g1182afd7b4+2a019aa3bb,g17e5ecfddb+2b8207f7de,g1d67935e3f+06cf436103,g38293774b4+ac198e9f13,g396055baef+6a2097e274,g3b44f30a73+6611e0205b,g480783c3b1+98f8679e14,g48ccf36440+89c08d0516,g4b93dc025c+98f8679e14,g5c4744a4d9+a302e8c7f0,g613e996a0d+e1c447f2e0,g6c8d09e9e7+25247a063c,g7271f0639c+98f8679e14,g7a9cd813b8+124095ede6,g9d27549199+a302e8c7f0,ga1cf026fa3+ac198e9f13,ga32aa97882+7403ac30ac,ga786bb30fb+7a139211af,gaa63f70f4e+9994eb9896,gabf319e997+ade567573c,gba47b54d5d+94dc90c3ea,gbec6a3398f+06cf436103,gc6308e37c7+07dd123edb,gc655b1545f+ade567573c,gcc9029db3c+ab229f5caf,gd01420fc67+06cf436103,gd877ba84e5+06cf436103,gdb4cecd868+6f279b5b48,ge2d134c3d5+cc4dbb2e3f,ge448b5faa6+86d1ceac1d,gecc7e12556+98f8679e14,gf3ee170dca+25247a063c,gf4ac96e456+ade567573c,gf9f5ea5b4d+ac198e9f13,gff490e6085+8c2580be5c,w.2022.27
LSST Data Management Base Package
catalogMatches.py
Go to the documentation of this file.
1# This file is part of afw.
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/>.
21
22__all__ = ["makeMergedSchema", "copyIntoCatalog",
23 "matchesToCatalog", "matchesFromCatalog", "copyAliasMapWithPrefix",
24 "reindexCatalog"]
25
26import os.path
27
28import numpy as np
29
30from ._schema import Schema
31from ._schemaMapper import SchemaMapper
32from ._base import BaseCatalog
33from ._table import SimpleTable
34from ._simple import SimpleCatalog
35from ._source import SourceCatalog, SourceTable
36from ._match import ReferenceMatch
37
38from lsst.utils import getPackageDir
39
40
41def makeMapper(sourceSchema, targetSchema, sourcePrefix=None, targetPrefix=None):
42 """Create a SchemaMapper between the input source and target schemas.
43
44 Parameters
45 ----------
46 sourceSchema : :py:class:`lsst.afw.table.Schema`
47 Input source schema that fields will be mapped from.
48 targetSchema : :py:class:`lsst.afw.table.Schema`
49 Target schema that fields will be mapped to.
50 sourcePrefix : `str`, optional
51 If set, only those keys with that prefix will be mapped.
52 targetPrefix : `str`, optional
53 If set, prepend it to the mapped (target) key name.
54
55 Returns
56 -------
57 SchemaMapper : :py:class:`lsst.afw.table.SchemaMapper`
58 Mapping between source and target schemas.
59 """
60 m = SchemaMapper(sourceSchema, targetSchema)
61 for key, field in sourceSchema:
62 keyName = field.getName()
63 if sourcePrefix is not None:
64 if not keyName.startswith(sourcePrefix):
65 continue
66 else:
67 keyName = field.getName().replace(sourcePrefix, "", 1)
68 m.addMapping(key, (targetPrefix or "") + keyName)
69 return m
70
71
72def makeMergedSchema(sourceSchema, targetSchema, sourcePrefix=None, targetPrefix=None):
73 """Return a schema that is a deep copy of a mapping between source and target schemas.
74
75 Parameters
76 ----------
77 sourceSchema : :py:class:`lsst.afw.table.Schema`
78 Input source schema that fields will be mapped from.
79 targetSchema : :py:class:`lsst.afw.atable.Schema`
80 Target schema that fields will be mapped to.
81 sourcePrefix : `str`, optional
82 If set, only those keys with that prefix will be mapped.
83 targetPrefix : `str`, optional
84 If set, prepend it to the mapped (target) key name.
85
86 Returns
87 -------
88 schema : :py:class:`lsst.afw.table.Schema`
89 Schema that is the result of the mapping between source and target schemas.
90 """
91 return makeMapper(sourceSchema, targetSchema, sourcePrefix, targetPrefix).getOutputSchema()
92
93
94def copyIntoCatalog(catalog, target, sourceSchema=None, sourcePrefix=None, targetPrefix=None):
95 """Copy entries from one Catalog into another.
96
97 Parameters
98 ----------
99 catalog : :py:class:`lsst.afw.table.base.Catalog`
100 Source catalog to be copied from.
101 target : :py:class:`lsst.afw.table.base.Catalog`
102 Target catalog to be copied to (edited in place).
103 sourceSchema : :py:class:`lsst.afw.table.Schema`, optional
104 Schema of source catalog.
105 sourcePrefix : `str`, optional
106 If set, only those keys with that prefix will be copied.
107 targetPrefix : `str`, optional
108 If set, prepend it to the copied (target) key name
109
110 Returns
111 -------
112 target : :py:class:`lsst.afw.table.base.Catalog`
113 Target catalog that is edited in place.
114 """
115 if sourceSchema is None:
116 sourceSchema = catalog.schema
117
118 targetSchema = target.schema
119 target.reserve(len(catalog))
120 for i in range(len(target), len(catalog)):
121 target.addNew()
122
123 if len(catalog) != len(target):
124 raise RuntimeError(f"Length mismatch: {len(catalog)} vs {len(target)}")
125
126 m = makeMapper(sourceSchema, targetSchema, sourcePrefix, targetPrefix)
127 for rFrom, rTo in zip(catalog, target):
128 rTo.assign(rFrom, m)
129
130
131def matchesToCatalog(matches, matchMeta):
132 """Denormalise matches into a Catalog of "unpacked matches".
133
134 Parameters
135 ----------
136 matches : `~lsst.afw.table.match.SimpleMatch`
137 Unpacked matches, i.e. a list of Match objects whose schema
138 has "first" and "second" attributes which, resepectively,
139 contain the reference and source catalog entries, and a
140 "distance" field (the measured distance between the reference
141 and source objects).
142 matchMeta : `~lsst.daf.base.PropertySet`
143 Metadata for matches (must have .add attribute).
144
145 Returns
146 -------
147 mergedCatalog : :py:class:`lsst.afw.table.BaseCatalog`
148 Catalog of matches (with ``ref_`` and ``src_`` prefix identifiers for
149 referece and source entries, respectively, including alias
150 maps from reference and source catalogs)
151 """
152 if len(matches) == 0:
153 raise RuntimeError("No matches provided.")
154
155 refSchema = matches[0].first.getSchema()
156 srcSchema = matches[0].second.getSchema()
157
158 mergedSchema = makeMergedSchema(refSchema, Schema(), targetPrefix="ref_")
159 mergedSchema = makeMergedSchema(
160 srcSchema, mergedSchema, targetPrefix="src_")
161
162 mergedSchema = copyAliasMapWithPrefix(refSchema, mergedSchema, prefix="ref_")
163 mergedSchema = copyAliasMapWithPrefix(srcSchema, mergedSchema, prefix="src_")
164
165 distKey = mergedSchema.addField(
166 "distance", type=np.float64, doc="Distance between ref and src")
167
168 mergedCatalog = BaseCatalog(mergedSchema)
169 copyIntoCatalog([m.first for m in matches], mergedCatalog,
170 sourceSchema=refSchema, targetPrefix="ref_")
171 copyIntoCatalog([m.second for m in matches], mergedCatalog,
172 sourceSchema=srcSchema, targetPrefix="src_")
173 for m, r in zip(matches, mergedCatalog):
174 r.set(distKey, m.distance)
175
176 # obtain reference catalog name if one is setup
177 try:
178 catalogName = os.path.basename(getPackageDir("astrometry_net_data"))
179 except LookupError:
180 catalogName = "NOT_SET"
181 matchMeta.add("REFCAT", catalogName)
182 mergedCatalog.getTable().setMetadata(matchMeta)
183
184 return mergedCatalog
185
186
187def matchesFromCatalog(catalog, sourceSlotConfig=None):
188 """Generate a list of ReferenceMatches from a Catalog of "unpacked matches".
189
190 Parameters
191 ----------
192 catalog : :py:class:`lsst.afw.table.BaseCatalog`
193 Catalog of matches. Must have schema where reference entries
194 are prefixed with ``ref_`` and source entries are prefixed with
195 ``src_``.
196 sourceSlotConfig : `lsst.meas.base.baseMeasurement.SourceSlotConfig`, optional
197 Configuration for source slots.
198
199 Returns
200 -------
201 matches : :py:class:`lsst.afw.table.ReferenceMatch`
202 List of matches.
203 """
204 refSchema = makeMergedSchema(
205 catalog.schema, SimpleTable.makeMinimalSchema(), sourcePrefix="ref_")
206 refCatalog = SimpleCatalog(refSchema)
207 copyIntoCatalog(catalog, refCatalog, sourcePrefix="ref_")
208
209 srcSchema = makeMergedSchema(
210 catalog.schema, SourceTable.makeMinimalSchema(), sourcePrefix="src_")
211 srcCatalog = SourceCatalog(srcSchema)
212 copyIntoCatalog(catalog, srcCatalog, sourcePrefix="src_")
213
214 if sourceSlotConfig is not None:
215 sourceSlotConfig.setupSchema(srcCatalog.schema)
216
217 matches = []
218 distKey = catalog.schema.find("distance").key
219 for ref, src, cat in zip(refCatalog, srcCatalog, catalog):
220 matches.append(ReferenceMatch(ref, src, cat[distKey]))
221
222 return matches
223
224
225def copyAliasMapWithPrefix(inSchema, outSchema, prefix=""):
226 """Copy an alias map from one schema into another.
227
228 This copies the alias map of one schema into another, optionally
229 prepending a prefix to both the "from" and "to" names of the alias
230 (the example use case here is for the "match" catalog created by
231 `lsst.meas.astrom.denormalizeMatches` where prefixes ``src_`` and
232 ``ref_`` are added to the source and reference field entries,
233 respectively).
234
235 Parameters
236 ----------
237 inSchema : `lsst.afw.table.Schema`
238 The input schema whose `lsst.afw.table.AliasMap` is to be
239 copied to ``outSchema``.
240 outSchema : `lsst.afw.table.Schema`
241 The output schema into which the `lsst.afw.table.AliasMap`
242 from ``inSchema`` is to be copied (modified in place).
243 prefix : `str`, optional
244 An optional prefix to add to both the "from" and "to" names
245 of the alias (default is an empty string).
246
247 Returns
248 -------
249 outSchema : `lsst.afw.table.Schema`
250 The output schema with the alias mappings from `inSchema`
251 added.
252 """
253 for k, v in inSchema.getAliasMap().items():
254 outSchema.getAliasMap().set(prefix + k, prefix + v)
255
256 return outSchema
257
258
259def reindexCatalog(catalog, indices, deep=True):
260 """Apply a numpy index array to an afw Catalog
261
262 Parameters
263 ----------
265 Catalog to reindex.
266 indices : `numpy.ndarray` of `int`
267 Index array.
268 deep : `bool`
269 Whether or not to make a deep copy of the original catalog.
270
271 Returns
272 -------
273 new : subclass of `lsst.afw.table.BaseCatalog`
274 Reindexed catalog. Records are shallow copies of those in ``catalog``.
275 """
276 new = SourceCatalog(catalog.table.clone() if deep else catalog.table)
277 records = [catalog[int(ii)] for ii in indices]
278 new.extend(records, deep=deep)
279 return new
std::vector< SchemaItem< Flag > > * items
Mapping class that holds aliases for a Schema.
Definition: AliasMap.h:36
Defines the fields and offsets for a table.
Definition: Schema.h:51
A mapping between the keys of two Schemas, used to copy data between them.
Definition: SchemaMapper.h:21
Class for storing generic metadata.
Definition: PropertySet.h:66
daf::base::PropertySet * set
Definition: fits.cc:912
def makeMergedSchema(sourceSchema, targetSchema, sourcePrefix=None, targetPrefix=None)
def makeMapper(sourceSchema, targetSchema, sourcePrefix=None, targetPrefix=None)
def matchesToCatalog(matches, matchMeta)
def reindexCatalog(catalog, indices, deep=True)
def copyAliasMapWithPrefix(inSchema, outSchema, prefix="")
def matchesFromCatalog(catalog, sourceSlotConfig=None)
def copyIntoCatalog(catalog, target, sourceSchema=None, sourcePrefix=None, targetPrefix=None)
Match< SimpleRecord, SourceRecord > ReferenceMatch
Definition: fwd.h:104
SortedCatalogT< SimpleRecord > SimpleCatalog
Definition: fwd.h:79
CatalogT< BaseRecord > BaseCatalog
Definition: fwd.h:72
std::string getPackageDir(std::string const &packageName)
return the root directory of a setup package
Definition: packaging.cc:33
Lightweight representation of a geometric match between two records.
Definition: Match.h:67