LSST Applications g0f08755f38+82efc23009,g12f32b3c4e+e7bdf1200e,g1653933729+a8ce1bb630,g1a0ca8cf93+50eff2b06f,g28da252d5a+52db39f6a5,g2bbee38e9b+37c5a29d61,g2bc492864f+37c5a29d61,g2cdde0e794+c05ff076ad,g3156d2b45e+41e33cbcdc,g347aa1857d+37c5a29d61,g35bb328faa+a8ce1bb630,g3a166c0a6a+37c5a29d61,g3e281a1b8c+fb992f5633,g414038480c+7f03dfc1b0,g41af890bb2+11b950c980,g5fbc88fb19+17cd334064,g6b1c1869cb+12dd639c9a,g781aacb6e4+a8ce1bb630,g80478fca09+72e9651da0,g82479be7b0+04c31367b4,g858d7b2824+82efc23009,g9125e01d80+a8ce1bb630,g9726552aa6+8047e3811d,ga5288a1d22+e532dc0a0b,gae0086650b+a8ce1bb630,gb58c049af0+d64f4d3760,gc28159a63d+37c5a29d61,gcf0d15dbbd+2acd6d4d48,gd7358e8bfb+778a810b6e,gda3e153d99+82efc23009,gda6a2b7d83+2acd6d4d48,gdaeeff99f8+1711a396fd,ge2409df99d+6b12de1076,ge79ae78c31+37c5a29d61,gf0baf85859+d0a5978c5a,gf3967379c6+4954f8c433,gfb92a5be7c+82efc23009,gfec2e1e490+2aaed99252,w.2024.46
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
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)