LSSTApplications  1.1.2+25,10.0+13,10.0+132,10.0+133,10.0+224,10.0+41,10.0+8,10.0-1-g0f53050+14,10.0-1-g4b7b172+19,10.0-1-g61a5bae+98,10.0-1-g7408a83+3,10.0-1-gc1e0f5a+19,10.0-1-gdb4482e+14,10.0-11-g3947115+2,10.0-12-g8719d8b+2,10.0-15-ga3f480f+1,10.0-2-g4f67435,10.0-2-gcb4bc6c+26,10.0-28-gf7f57a9+1,10.0-3-g1bbe32c+14,10.0-3-g5b46d21,10.0-4-g027f45f+5,10.0-4-g86f66b5+2,10.0-4-gc4fccf3+24,10.0-40-g4349866+2,10.0-5-g766159b,10.0-5-gca2295e+25,10.0-6-g462a451+1
LSSTDataManagementBasePackage
attributes.cc
Go to the documentation of this file.
1 // -*- lsst-c++ -*-
2 
3 /*
4  * LSST Data Management System
5  * Copyright 2012 LSST Corporation.
6  *
7  * This product includes software developed by the
8  * LSST Project (http://www.lsst.org/).
9  *
10  * This program is free software: you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation, either version 3 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the LSST License Statement and
21  * the GNU General Public License along with this program. If not,
22  * see <http://www.lsstcorp.org/LegalNotices/>.
23  */
24 
30 
31 #include <utility>
32 
33 #include "lsst/utils/ieee.h"
35 
36 
37 using lsst::pex::exceptions::InvalidParameterError;
38 using lsst::pex::exceptions::NotFoundError;
39 
41 
50 
52 
54 using lsst::afw::table::Flag;
61 
64 
65 
66 namespace lsst { namespace ap { namespace cluster {
67 
68 // -- SourceAndExposure implementation --------
69 
70 SourceAndExposure::SourceAndExposure() : _source(), _exposure(), _transform() { }
71 
73  boost::shared_ptr<SourceRecord> const & source,
74  boost::shared_ptr<ExposureInfo> const & exposure,
75  AffineTransform const & transform
76 ) : _source(source), _exposure(exposure), _transform(transform) { }
77 
79 
80 
81 // -- Basic attributes ---------
82 
83 namespace {
84 
85  // Compute unweighted position mean for the given sources.
86  void meanCoord(SourceClusterRecord & cluster, SourceCatalog const & sources) {
87  typedef SourceCatalog::const_iterator Iter;
88  // compute point v such that the sum of the squared angular separations
89  // between v and each source position is minimized.
90  Eigen::Vector3d v = Eigen::Vector3d::Zero();
91  for (Iter i = sources.begin(), e = sources.end(); i != e; ++i) {
92  v += i->getCoord().getVector().asEigen();
93  }
95  cluster.setCoord(c);
96  Eigen::Matrix2d cov;
97  if (sources.size() == 1) {
98  // copy position error from source (if available)
99  try {
101  sources[0].getSchema()[std::string("coord.err")];
102  cov = sources[0].get(coordErrKey).cast<double>();
103  } catch (NotFoundError &) {
104  cov = Eigen::Matrix2d::Constant(std::numeric_limits<double>::quiet_NaN());
105  }
106  } else {
107  // compute sample covariance matrix
108  cov = Eigen::Matrix2d::Zero();
109  for (Iter i = sources.begin(), e = sources.end(); i != e; ++i) {
110  double dra = i->getRa().asRadians() - c.getRa().asRadians();
111  if (dra > PI) {
112  dra = dra - TWOPI;
113  } else if (dra < -PI) {
114  dra = dra + TWOPI;
115  }
116  double dde = i->getDec().asRadians() - c.getDec().asRadians();
117  cov(0,0) += dra * dra;
118  cov(0,1) += dra * dde;
119  cov(1,1) += dde * dde;
120  }
121  cov(1,0) = cov(0,1);
122  // ... and map to unbiased estimate of the covariance
123  // of the unweighted coordinate mean
124  cov /= static_cast<double>(sources.size() - 1);
125  }
126  cluster.setCoordErr(cov.cast<float>());
127  }
128 
129 
130  // A tangent plane projection with the standard N,E basis.
131  class NeTanProj {
132  public:
133  NeTanProj(IcrsCoord const & center);
134 
135  // Map N,E coordinates to sky coordinates.
136  Eigen::Vector2d const neToSky(Eigen::Vector2d const & ne) const {
138  _origin + ne.x() * _east + ne.y() * _north);
139  }
140 
141  // Map covariance matrix in the N,E tangent plane projection to sky coordinates.
142  Eigen::Matrix2d const neToSky(Eigen::Matrix2d const & cov) const {
143  return _jDiag.asDiagonal() * cov * _jDiag.asDiagonal();
144  }
145 
146  // Compute a mapping from the given pixel space to this tangent plane.
147  AffineTransform const pixelToNeTransform(
148  Eigen::Vector2d const &point, Wcs const &wcs) const;
149 
150  private:
152  Eigen::Vector3d _origin;
153  Eigen::Vector3d _north;
154  Eigen::Vector3d _east;
155  Eigen::Vector2d _jDiag;
156  };
157 
158  NeTanProj::NeTanProj(IcrsCoord const & center) : _center(center) {
159  double sinLon = std::sin(center.getLongitude().asRadians());
160  double cosLon = std::cos(center.getLongitude().asRadians());
161  double sinLat = std::sin(center.getLatitude().asRadians());
162  double cosLat = std::cos(center.getLatitude().asRadians());
163  _origin = Eigen::Vector3d(cosLat * cosLon, cosLat * sinLon, sinLat);
164  if (std::fabs(center.getLatitude().asRadians()) == HALFPI) {
165  _north = Eigen::Vector3d(-1.0, 0.0, 0.0);
166  _east = Eigen::Vector3d(0.0, 1.0, 0.0);
167  } else {
168  _north = Eigen::Vector3d(-sinLat * cosLon, -sinLat * sinLon, cosLat);
169  _east = Eigen::Vector3d(-sinLon, cosLon, 0.0);
170  }
171  // The Jacobian of the N,E to sky transform evaluated at (x,y) = (0,0) is
172  // [ 1/cos(lat), 0
173  // 0, 1 ]
174  // which is undefined when the tangent plane is centered at a pole.
175  // Maybe the LSST schema should specify position errors in the N,E basis,
176  // e.g. the usual semi major/minor axis lengths + position angle?
177  _jDiag = Eigen::Vector2d(1.0 / cosLat, 1.0);
178  }
179 
180  AffineTransform const NeTanProj::pixelToNeTransform(
181  Eigen::Vector2d const &point, Wcs const &wcs) const
182  {
183  double const pix = 8.0;
184  double const invPix = 1.0 / pix; // exact
185 
186  Eigen::Vector3d p = wcs.pixelToSky(
187  point.x(), point.y())->toIcrs().getVector().asEigen();
188  Eigen::Vector3d px = wcs.pixelToSky(
189  point.x() + pix, point.y())->toIcrs().getVector().asEigen();
190  Eigen::Vector3d py = wcs.pixelToSky(
191  point.x(), point.y() + pix)->toIcrs().getVector().asEigen();
192 
193  Eigen::Vector2d pne(p.dot(_east), p.dot(_north));
194  Eigen::Vector2d pxne(px.dot(_east), px.dot(_north));
195  Eigen::Vector2d pyne(py.dot(_east), py.dot(_north));
196  pne /= p.dot(_origin);
197  pxne /= px.dot(_origin);
198  pyne /= py.dot(_origin);
199 
200  Eigen::Matrix2d m;
201  m.col(0) = (pxne - pne)*invPix;
202  m.col(1) = (pyne - pne)*invPix;
203 
204  return AffineTransform(m, pne - m*point);
205  }
206 
207 
208  // Min, mean, and max value of a source field.
209  struct StatTuple {
210  StatTuple();
211  void compute(
212  std::vector<SourceAndExposure>::const_iterator first,
213  std::vector<SourceAndExposure>::const_iterator last,
214  Key<double> const & key);
215 
216  double min;
217  double mean;
218  double max;
219  };
220 
221  StatTuple::StatTuple() :
222  min(std::numeric_limits<double>::quiet_NaN()),
223  mean(std::numeric_limits<double>::quiet_NaN()),
224  max(std::numeric_limits<double>::quiet_NaN())
225  { }
226 
227  void StatTuple::compute(
228  std::vector<SourceAndExposure>::const_iterator first,
229  std::vector<SourceAndExposure>::const_iterator last,
230  Key<double> const & key)
231  {
232  if (first == last) {
233  min = std::numeric_limits<double>::quiet_NaN();
234  mean = std::numeric_limits<double>::quiet_NaN();
235  max = std::numeric_limits<double>::quiet_NaN();
236  } else {
237  min = std::numeric_limits<double>::infinity();
238  max = -min;
239  mean = 0.0;
240  double ns = static_cast<double>(last - first);
241  for (; first != last; ++first) {
242  double val = first->getSource()->get(key);
243  if (val < min) {
244  min = val;
245  }
246  if (val > max) {
247  max = val;
248  }
249  mean += val;
250  }
251  mean /= ns;
252  if (mean < min) {
253  mean = min;
254  } else if (mean > max) {
255  mean = max;
256  }
257  }
258  }
259 
260 
261  // Compute weighted position mean for the given sources.
262  void weightedMeanCoord(
263  SourceClusterRecord & cluster,
264  std::vector<SourceAndExposure> const & sources,
265  NeTanProj const & proj)
266  {
267  typedef std::vector<SourceAndExposure>::const_iterator Iter;
268 
269  int n = 0;
270  Eigen::Vector2d wmean = Eigen::Vector2d::Zero();
271  Eigen::Matrix2d invCovSum = Eigen::Matrix2d::Zero();
272  for (Iter i = sources.begin(), e = sources.end(); i != e; ++i) {
273  SourceRecord const & r = *(i->getSource());
274  Eigen::Matrix2d cov = r.getCentroidErr().cast<double>();
275  if (lsst::utils::isnan(cov(0,0)) ||
276  lsst::utils::isnan(cov(1,1))) {
277  continue; // variance is NaN
278  } else if (cov(0,0) <= 0.0 || cov(1,1) <= 0.0) {
279  continue; // negative variance
280  }
281  // FIXME: for now, no measurement algorithms actually
282  // compute full covariance matrixes, and the
283  // off diagonal matrix elements are always NaN.
284  // Longer term, we will need some algorithm metadata
285  // to decide whether an off-diagonal NaN means a
286  // sample should be ignored because a computation failed,
287  // or whether it should be zeroed because the algorithm
288  // never computes it.
289  if (cov(0,1) != cov(1,0)) {
290  if (lsst::utils::isnan(cov(0,1)) && lsst::utils::isnan(cov(1,0))) {
291  cov(0,1) = 0.0; cov(1,0) = 0.0;
292  } else {
293  continue; // covariance matrix not symmetric
294  }
295  }
296  Point2D p = r.getCentroid();
297  Eigen::Matrix2d m = i->getTransform().getLinear().getMatrix();
298  Eigen::Matrix2d invCov = (m * cov * m.transpose()).inverse();
299  invCovSum += invCov;
300  p = i->getTransform()(p);
301  wmean += invCov * p.asEigen();
302  ++n;
303  }
304  if (n > 0) {
305  Eigen::Matrix2d cov = invCovSum.inverse();
306  wmean = cov * wmean;
307  wmean = proj.neToSky(wmean);
308  cluster.setWeightedMeanCoord(IcrsCoord(
309  wmean.x() * radians, wmean.y() * radians));
310  cluster.setWeightedMeanCoordErr(proj.neToSky(cov).cast<float>());
311  // chi-squared correction for cov?
312  } else {
313  cluster.setWeightedMeanCoord(IcrsCoord(
314  std::numeric_limits<double>::quiet_NaN() * radians,
315  std::numeric_limits<double>::quiet_NaN() * radians));
316  cluster.setWeightedMeanCoordErr(Eigen::Matrix2f::Constant(
317  std::numeric_limits<float>::quiet_NaN()));
318  }
319  cluster.setWeightedMeanCoordCount(n);
320  }
321 
322 
323  // Comparison functor for sorting std::vector<SourceAndExposure> by filter ID.
324  struct CmpSourceAndExposure {
325  bool operator()(SourceAndExposure const & a, SourceAndExposure const & b) const {
326  return a.getExposureInfo()->getFilter().getId() <
327  b.getExposureInfo()->getFilter().getId();
328  }
329  };
330 
331 } // namespace <anonymous>
332 
333 
334 boost::shared_ptr<std::vector<SourceAndExposure> > const computeBasicAttributes(
335  SourceClusterRecord & cluster,
336  SourceCatalog const & sources,
337  ExposureInfoMap const & exposures,
338  std::string const & exposurePrefix)
339 {
340  typedef SourceCatalog::const_iterator SourceIter;
341  typedef std::vector<SourceAndExposure>::const_iterator SeIter;
342 
343  if (sources.empty()) {
344  throw LSST_EXCEPT(InvalidParameterError, "No sources in cluster");
345  }
346  // unweighted mean coordinates
347  meanCoord(cluster, sources);
348 
349  boost::shared_ptr<std::vector<SourceAndExposure> > se(
350  new std::vector<SourceAndExposure>);
351  NeTanProj const proj(cluster.getCoord());
352  Schema const schema = sources.getSchema();
353  Key<int64_t> const expIdKey = schema.find<int64_t>(exposurePrefix + ".id").key;
354  Key<double> const expTimeMidKey = schema.find<double>(exposurePrefix + ".time.mid").key;
355  // fill in vector of source,exposure pairs
356  se->reserve(sources.size());
357  for (SourceIter i = sources.begin(), e = sources.end(); i != e; ++i) {
358  boost::shared_ptr<ExposureInfo> exp =
359  const_cast<ExposureInfoMap &>(exposures).get(i->get(expIdKey));
360  if (!exp) {
361  throw LSST_EXCEPT(NotFoundError, "No ExposureInfo for source");
362  }
363  se->push_back(SourceAndExposure(
364  i, exp, proj.pixelToNeTransform(i->getCentroid().asEigen(), *exp->getWcs())));
365  }
366  // source counts and observation time range
367  StatTuple t;
368  t.compute(se->begin(), se->end(), expTimeMidKey);
369  cluster.setNumSources(static_cast<int>(sources.size()));
370  cluster.setTimeMin(t.min);
371  cluster.setTimeMean(t.mean);
372  cluster.setTimeMax(t.max);
373  // inverse variance weighted mean coordinates
374  if (sources.getTable()->getCentroidKey().isValid() &&
375  sources.getTable()->getCentroidErrKey().isValid()) {
376  weightedMeanCoord(cluster, *se, proj);
377  }
378  // sort source,exposure vector by filter
379  std::sort(se->begin(), se->end(), CmpSourceAndExposure());
380  // per-filter source counts and observation time range
381  SeIter i = se->begin();
382  SeIter const e = se->end();
383  while (i != e) {
384  int const filterId = i->getExposureInfo()->getFilter().getId();
385  SeIter j = i;
386  for (++j; j != e && filterId == j->getExposureInfo()->getFilter().getId(); ++j) { }
387  std::string const filterName = i->getExposureInfo()->getFilter().getName();
388  cluster.setNumSources(filterName, static_cast<int>(j - i));
389  t.compute(i, j, expTimeMidKey);
390  cluster.setTimeMin(filterName, t.min);
391  cluster.setTimeMax(filterName, t.max);
392  i = j;
393  }
394  return se;
395 }
396 
397 
398 // -- Flux --------
399 
400 namespace {
401 
402  typedef std::pair<double, double> FluxAndErr;
403  typedef std::pair<double, double> FluxAndVariance;
404 
405  FluxAndErr const weightedMeanFlux(std::vector<FluxAndVariance> const & samples)
406  {
407  typedef std::vector<FluxAndVariance>::const_iterator Iter;
408  if (samples.empty()) {
409  return FluxAndErr(std::numeric_limits<double>::quiet_NaN(),
410  std::numeric_limits<double>::quiet_NaN());
411  } else if (samples.size() == 1) {
412  return FluxAndErr(samples[0].first, std::sqrt(samples[0].second));
413  }
414  // compute weighted mean x_w = sum(w_i * x_i) / sum(w_i), where
415  // w_i = 1/Var(x_i)
416  double wsum = 0.0;
417  double wmean = 0.0;
418  for (Iter i = samples.begin(), e = samples.end(); i != e; ++i) {
419  double w = 1.0/i->second;
420  wmean += w*i->first;
421  wsum += w;
422  }
423  wmean /= wsum;
424  // Var(sum(w_i * x_i) / sum(w_i)) = sum(w_i)^-1
425  // Multiply by chi-squared over the number of degrees of freedom to
426  // account for errors in the variances of the individual samples,
427  // yielding Var(x_w) = sum(w_i)^-1 * sum(w_i * (x_i - x_w)) / (n - 1)
428  double vwm = 0.0;
429  for (Iter i = samples.begin(), e = samples.end(); i != e; ++i) {
430  double d = i->first - wmean;
431  vwm += (d*d)/i->second;
432  }
433  return FluxAndErr(wmean, std::sqrt(vwm/(wsum*(samples.size() - 1))));
434  }
435 
436 } // namespace <anonymous>
437 
438 
440  SourceClusterRecord & cluster,
441  std::vector<SourceAndExposure> const & sources,
442  std::string const & fluxDef,
443  std::vector<Key<Flag > > const & skipFlags,
444  double fluxScale)
445 {
446  typedef std::vector<SourceAndExposure>::const_iterator Iter;
447  typedef std::vector<Key<Flag > >::const_iterator FlagIter;
448 
449  if (sources.empty()) {
450  throw LSST_EXCEPT(InvalidParameterError, "No sources in cluster");
451  }
452  Schema const sourceSchema = sources[0].getSource()->getSchema();
453  Schema const clusterSchema = cluster.getSchema();
454  Flux::MeasKey const sourceFluxKey = sourceSchema[fluxDef];
455  Flux::ErrKey const sourceFluxErrKey = sourceSchema[fluxDef + ".err"];
456 
457  Iter i = sources.begin();
458  Iter const e = sources.end();
459  while (i != e) {
460  // Determine range [i, j) containing sources from the same filter
461  int const filterId = i->getExposureInfo()->getFilter().getId();
462  Iter j = i;
463  for (++j; j != e && filterId == j->getExposureInfo()->getFilter().getId(); ++j) { }
464  // iterate over [i, j), storing all valid flux/error pairs
465  // after calibrating them.
466  std::vector<FluxAndVariance> samples;
467  samples.reserve(j - i);
468  std::string const filter = i->getExposureInfo()->getFilter().getName();
469  for (; i < j; ++i) {
470  SourceRecord const * r = i->getSource().get();
471  double flux = r->get(sourceFluxKey);
472  double fluxErr = r->get(sourceFluxErrKey);
473  if (!lsst::utils::isfinite(flux) || lsst::utils::isnan(fluxErr) || fluxErr <= 0.0) {
474  continue;
475  }
476  bool skip = false;
477  for (FlagIter f = skipFlags.begin(), fe = skipFlags.end(); f != fe; ++f) {
478  if (r->get(*f)) {
479  skip = true;
480  break;
481  }
482  }
483  if (skip) {
484  continue;
485  }
486  samples.push_back(i->getExposureInfo()->calibrateFlux(flux, fluxErr, fluxScale));
487  }
488  FluxAndErr mean = weightedMeanFlux(samples);
489  Flux::MeasKey const measKey = clusterSchema[filter + "." + fluxDef];
490  cluster.set(measKey, mean.first);
491  Flux::ErrKey const errKey = clusterSchema[filter + "." + fluxDef + ".err"];
492  cluster.set(errKey, mean.second);
493  Key<int> const countKey = clusterSchema[filter + "." + fluxDef + ".count"];
494  cluster.set(countKey, static_cast<int>(samples.size()));
495  }
496 }
497 
498 
499 // -- Shape --------
500 
501 namespace {
502 
503  typedef std::pair<Quadrupole, Eigen::Matrix<double, 3, 3, Eigen::DontAlign> > ShapeAndErr;
504 
505  void weightedMeanShape(ShapeAndErr & result, std::vector<ShapeAndErr> const & samples)
506  {
507  typedef std::vector<ShapeAndErr>::const_iterator Iter;
508  if (samples.empty()) {
509  result.first.setParameterVector(
510  Eigen::Vector3d::Constant(std::numeric_limits<double>::quiet_NaN()));
511  result.second = Eigen::Matrix3d::Constant(std::numeric_limits<double>::quiet_NaN());
512  return;
513  } else if (samples.size() == 1) {
514  result = samples[0];
515  return;
516  }
517 
518  Eigen::Matrix3d invCovSum = Eigen::Matrix3d::Zero();
519  Eigen::Vector3d wmean = Eigen::Vector3d::Zero();
520  for (Iter i = samples.begin(), e = samples.end(); i != e; ++i) {
521  Eigen::Matrix3d m = i->second.inverse();
522  invCovSum += m;
523  wmean += m * (i->first.getParameterVector());
524  }
525  Eigen::Matrix3d cov = invCovSum.inverse();
526  wmean = cov * wmean;
527  result.first = Quadrupole(wmean);
528  result.second = cov;
529  // chi-squared correction for cov?
530  }
531 
532 } // namespace <anonymous>
533 
534 
536  SourceClusterRecord & cluster,
537  std::vector<SourceAndExposure> const & sources,
538  std::string const & shapeDef,
539  std::vector<lsst::afw::table::Key<lsst::afw::table::Flag > > const & skipFlags)
540 {
541  typedef std::vector<SourceAndExposure>::const_iterator Iter;
542  typedef std::vector<Key<Flag > >::const_iterator FlagIter;
543 
544  if (sources.empty()) {
545  throw LSST_EXCEPT(InvalidParameterError, "No sources in cluster");
546  }
547  Schema const sourceSchema = sources[0].getSource()->getSchema();
548  Schema const clusterSchema = cluster.getSchema();
549  Shape::MeasKey const sourceShapeKey = sourceSchema[shapeDef];
550  Shape::ErrKey const sourceShapeErrKey = sourceSchema[shapeDef + ".err"];
551 
552  Iter i = sources.begin();
553  Iter const e = sources.end();
554  while (i != e) {
555  // Determine range [i, j) containing sources from the same filter
556  int const filterId = i->getExposureInfo()->getFilter().getId();
557  Iter j = i;
558  for (++j; j != e && filterId == j->getExposureInfo()->getFilter().getId(); ++j) { }
559  // iterate over [i, j) storing all valid shape/error pairs in samples
560  std::vector<ShapeAndErr> samples;
561  samples.reserve(j - i);
562  std::string const filter = i->getExposureInfo()->getFilter().getName();
563  for (; i < j; ++i) {
564  SourceRecord const * r = i->getSource().get();
565  bool skip = false;
566  for (FlagIter f = skipFlags.begin(), fe = skipFlags.end(); f != fe; ++f) {
567  if (r->get(*f)) {
568  skip = true;
569  break;
570  }
571  }
572  if (skip) {
573  continue;
574  }
575  Quadrupole q = r->get(sourceShapeKey);
576  if (!lsst::utils::isfinite(q.getIxx()) ||
579  continue; // shape contains NaNs
580  }
581  Eigen::Matrix3d cov = r->get(sourceShapeErrKey).cast<double>();
582  if (lsst::utils::isnan(cov(0,0)) ||
583  lsst::utils::isnan(cov(1,1)) ||
584  lsst::utils::isnan(cov(2,2))) {
585  continue; // covariance matrix contains NaNs
586  } else if (cov(0,0) <= 0.0 ||
587  cov(1,1) <= 0.0 ||
588  cov(2,2) <= 0.0) {
589  continue; // negative variance
590  }
591  // FIXME: for now, no measurement algorithms actually
592  // compute full covariance matrixes, and the
593  // off diagonal matrix elements are always NaN.
594  // Longer term, we will need some algorithm metadata
595  // to decide whether an off-diagonal NaN means a
596  // sample should be ignored because a computation failed,
597  // or whether it should be zeroed because the algorithm
598  // never computes it.
599  if (cov(0,1) != cov(1,0) || cov(0,2) != cov(2,0) || cov(1,2) != cov(2,1)) {
600  if (lsst::utils::isnan(cov(0,1)) && lsst::utils::isnan(cov(1,0)) &&
601  lsst::utils::isnan(cov(0,2)) && lsst::utils::isnan(cov(2,0)) &&
602  lsst::utils::isnan(cov(1,2)) && lsst::utils::isnan(cov(2,1))) {
603  cov(0,1) = 0.0; cov(1,0) = 0.0;
604  cov(0,2) = 0.0; cov(2,0) = 0.0;
605  cov(1,2) = 0.0; cov(2,1) = 0.0;
606  } else {
607  continue; // covariance matrix not symmetric
608  }
609  }
610  // transform moments and covariance matrix to N,E basis
611  LinearTransform const * xform = &(i->getTransform().getLinear());
612  Eigen::Matrix3d j = q.transform(*xform).d();
613  q.transform(*xform).inPlace();
614  cov = j * cov * j.transpose();
615  samples.push_back(ShapeAndErr(q, cov));
616  }
617  ShapeAndErr mean;
618  weightedMeanShape(mean, samples);
619  Shape::MeasKey const measKey = clusterSchema[filter + "." + shapeDef];
620  cluster.set(measKey, mean.first);
621  Shape::ErrKey const errKey = clusterSchema[filter + "." + shapeDef + ".err"];
622  cluster.set(errKey, mean.second);
623  Key<int> const countKey = clusterSchema[filter + "." + shapeDef + ".count"];
624  cluster.set(countKey, static_cast<int>(samples.size()));
625  }
626 }
627 
628 }}} // namespace lsst::ap::cluster
629 
An ellipse core with quadrupole moments as parameters.
Definition: Quadrupole.h:45
Defines the fields and offsets for a table.
Definition: Schema.h:46
IcrsCoord _center
Definition: attributes.cc:151
CentroidSlotDefinition::MeasValue getCentroid() const
Get the value of the Centroid slot measurement.
Definition: Source.h:809
Schema getSchema() const
Return the Schema that holds this record&#39;s fields and keys.
Definition: BaseRecord.h:48
lsst::afw::geom::Angle getDec() const
Definition: Coord.h:171
boost::shared_ptr< std::vector< SourceAndExposure > > const computeBasicAttributes(SourceClusterRecord &cluster, SourceCatalog const &sources, ExposureInfoMap const &exposures, std::string const &exposurePrefix)
Definition: attributes.cc:334
DerivativeMatrix d() const
Return the derivative of transformed core with respect to input core.
AngleUnit const radians
constant with units of radians
Definition: Angle.h:91
std::vector< SourceCatalog > const cluster(SourceCatalog const &sources, ClusteringControl const &control)
Definition: clustering.cc:578
Functions for computing cluster attributes.
Eigen::Vector3d _east
Definition: attributes.cc:154
void computeShapeMean(SourceClusterRecord &cluster, std::vector< SourceAndExposure > const &sources, std::string const &shapeDef, std::vector< lsst::afw::table::Key< lsst::afw::table::Flag > > const &skipFlags)
Definition: attributes.cc:535
Record class that contains measurement averages on clusters of single exposure sources.
Definition: SourceCluster.h:98
void setTimeMin(double const &value)
Set the earliest observation time [MJD TAI] of sources in the cluster.
lsst::afw::coord::IcrsCoord const cartesianToIcrs(Eigen::Vector3d const &v)
Definition: SpatialUtils.h:74
void computeFluxMean(SourceClusterRecord &cluster, std::vector< SourceAndExposure > const &sources, std::string const &fluxDef, std::vector< Key< Flag > > const &skipFlags, double fluxScale)
Definition: attributes.cc:439
Implementation of the WCS standard for a any projection.
Definition: Wcs.h:107
double const getIyy() const
Definition: Quadrupole.h:59
CentroidSlotDefinition::ErrValue getCentroidErr() const
Get the uncertainty on the Centroid slot measurement.
Definition: Source.h:813
boost::shared_ptr< coord::Coord > pixelToSky(double pix1, double pix2) const
Convert from pixel position to sky coordinates (e.g. RA/dec)
Definition: Wcs.cc:864
lsst::afw::coord::IcrsCoord IcrsCoord
Definition: misc.h:41
double const TWOPI
Definition: Angle.h:19
int isnan(T t)
Definition: ieee.h:110
Eigen::Vector2d _jDiag
Definition: attributes.cc:155
Eigen::Vector3d _north
Definition: attributes.cc:153
Eigen::Vector2d const cartesianToSpherical(Eigen::Vector3d const &v)
double min
Definition: attributes.cc:216
void inPlace()
Transform the ellipse core in-place.
int d
Definition: KDTree.cc:89
double asRadians() const
Definition: Angle.h:122
IcrsCoord getCoord() const
Convenience accessors for the keys in the minimal reference schema.
Definition: Simple.h:206
Field< T >::Value get(Key< T > const &key) const
Return the value of a field for the given key.
Definition: BaseRecord.h:123
A 2D linear coordinate transformation.
afw::geom::ellipses::Quadrupole Shape
Definition: constants.h:58
double max
Definition: attributes.cc:218
void setNumSources(int const &value)
Set the number of sources in the cluster.
double mean
Definition: attributes.cc:217
void setCoordErr(Eigen::Matrix< float, 2, 2 > const &value)
Set the uncertainty of the sky-coordinates of the cluster.
double w
Definition: CoaddPsf.cc:57
tbl::Schema schema
Definition: CoaddPsf.cc:324
An affine coordinate transformation consisting of a linear transformation and an offset.
void setTimeMax(double const &value)
Set the latest observation time [MJD TAI] of sources in the cluster.
EigenVector const & asEigen() const
Return a fixed-size Eigen representation of the coordinate object.
void setTimeMean(double const &value)
Set the mean observation time [MJD TAI] of sources in the cluster.
double const getIxx() const
Definition: Quadrupole.h:56
int isfinite(T t)
Definition: ieee.h:100
lsst::afw::image::Wcs Wcs
Definition: Wcs.cc:59
void setCoord(IcrsCoord const &coord)
Convenience accessors for the keys in the minimal reference schema.
Definition: Simple.h:207
#define LSST_EXCEPT(type,...)
Definition: Exception.h:46
Spatial utility functions.
A class used as a handle to a particular field in a table.
Definition: fwd.h:44
Transformer transform(LinearTransform const &transform)
Definition: Transformer.h:117
lsst::afw::geom::Angle getLongitude() const
The main access method for the longitudinal coordinate.
Definition: Coord.h:111
double const HALFPI
Definition: Angle.h:20
void set(Key< T > const &key, U const &value)
Set value of a field for the given key.
Definition: BaseRecord.h:136
afw::table::Key< double > b
Point< double, 2 > Point2D
Definition: Point.h:286
double const getIxy() const
Definition: Quadrupole.h:62
Record class that contains measurements made on a single exposure.
Definition: Source.h:81
double const PI
The ratio of a circle&#39;s circumference to diameter.
Definition: Angle.h:18
lsst::afw::geom::Angle getLatitude() const
The main access method for the latitudinal coordinate.
Definition: Coord.h:118
ImageT val
Definition: CR.cc:154
lsst::afw::geom::Angle getRa() const
Definition: Coord.h:170
A class to handle Icrs coordinates (inherits from Coord)
Definition: Coord.h:155
Eigen::Vector3d _origin
Definition: attributes.cc:152
SortedCatalogT< SourceRecord > SourceCatalog
Definition: fwd.h:75