LSST Applications g2079a07aa2+86d27d4dc4,g2305ad1205+a659bff248,g2bbee38e9b+3c60f8fe34,g337abbeb29+3c60f8fe34,g33d1c0ed96+3c60f8fe34,g3502564af9+d77d6d1350,g3a166c0a6a+3c60f8fe34,g487adcacf7+25d9892218,g4be5004598+d77d6d1350,g50ff169b8f+96c6868917,g52b1c1532d+585e252eca,g591dd9f2cf+4d81263f9a,g5cd07815a0+980d2b1c3b,g607f77f49a+d77d6d1350,g858d7b2824+d77d6d1350,g88963caddf+83e433e629,g99cad8db69+a4d3c48eeb,g9ddcbc5298+9a081db1e4,ga1e77700b3+bcf1af89ad,ga57fefb910+9a39d7b2d7,gae0086650b+585e252eca,gb065fddaf9+4f9fd82a2c,gb0e22166c9+60f28cb32d,gb363559e06+d84b1d3d07,gb3b7280ab2+4563d032e1,gb4b16eec92+babe958938,gba4ed39666+c2a2e4ac27,gbb8dafda3b+ed6854b564,gc120e1dc64+b72d212f87,gc28159a63d+3c60f8fe34,gc3e9b769f7+921dbcd359,gcf0d15dbbd+9a39d7b2d7,gdaeeff99f8+f9a426f77a,gddc38dedce+585e252eca,ge79ae78c31+3c60f8fe34,w.2024.21
LSST Data Management Base Package
Loading...
Searching...
No Matches
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 ----------
264 catalog : `lsst.afw.table.SourceCatalog`
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
Tag types used to declare specialized field types.
Definition misc.h:31
daf::base::PropertySet * set
Definition fits.cc:931
makeMapper(sourceSchema, targetSchema, sourcePrefix=None, targetPrefix=None)
reindexCatalog(catalog, indices, deep=True)
copyAliasMapWithPrefix(inSchema, outSchema, prefix="")
copyIntoCatalog(catalog, target, sourceSchema=None, sourcePrefix=None, targetPrefix=None)
matchesToCatalog(matches, matchMeta)
matchesFromCatalog(catalog, sourceSlotConfig=None)
makeMergedSchema(sourceSchema, targetSchema, sourcePrefix=None, targetPrefix=None)