LSST Applications  21.0.0-147-g0e635eb1+1acddb5be5,22.0.0+052faf71bd,22.0.0+1ea9a8b2b2,22.0.0+6312710a6c,22.0.0+729191ecac,22.0.0+7589c3a021,22.0.0+9f079a9461,22.0.1-1-g7d6de66+b8044ec9de,22.0.1-1-g87000a6+536b1ee016,22.0.1-1-g8e32f31+6312710a6c,22.0.1-10-gd060f87+016f7cdc03,22.0.1-12-g9c3108e+df145f6f68,22.0.1-16-g314fa6d+c825727ab8,22.0.1-19-g93a5c75+d23f2fb6d8,22.0.1-19-gb93eaa13+aab3ef7709,22.0.1-2-g8ef0a89+b8044ec9de,22.0.1-2-g92698f7+9f079a9461,22.0.1-2-ga9b0f51+052faf71bd,22.0.1-2-gac51dbf+052faf71bd,22.0.1-2-gb66926d+6312710a6c,22.0.1-2-gcb770ba+09e3807989,22.0.1-20-g32debb5+b8044ec9de,22.0.1-23-gc2439a9a+fb0756638e,22.0.1-3-g496fd5d+09117f784f,22.0.1-3-g59f966b+1e6ba2c031,22.0.1-3-g849a1b8+f8b568069f,22.0.1-3-gaaec9c0+c5c846a8b1,22.0.1-32-g5ddfab5d3+60ce4897b0,22.0.1-4-g037fbe1+64e601228d,22.0.1-4-g8623105+b8044ec9de,22.0.1-5-g096abc9+d18c45d440,22.0.1-5-g15c806e+57f5c03693,22.0.1-7-gba73697+57f5c03693,master-g6e05de7fdc+c1283a92b8,master-g72cdda8301+729191ecac,w.2021.39
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 
26 import os.path
27 
28 import numpy as np
29 
30 from ._schema import Schema
31 from ._schemaMapper import SchemaMapper
32 from ._base import BaseCatalog
33 from ._table import SimpleTable
34 from ._simple import SimpleCatalog
35 from ._source import SourceCatalog, SourceTable
36 from ._match import ReferenceMatch
37 
38 from lsst.utils import getPackageDir
39 
40 
41 def 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 
72 def 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 
94 def 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 
131 def 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 
187 def 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 
225 def 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 
259 def 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
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