37 using lsst::pex::exceptions::InvalidParameterError;
38 using lsst::pex::exceptions::NotFoundError;
54 using lsst::afw::table::Flag;
66 namespace lsst {
namespace ap {
namespace cluster {
73 boost::shared_ptr<SourceRecord>
const & source,
74 boost::shared_ptr<ExposureInfo>
const & exposure,
76 ) : _source(source), _exposure(exposure), _transform(transform) { }
87 typedef SourceCatalog::const_iterator Iter;
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();
97 if (sources.size() == 1) {
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());
108 cov = Eigen::Matrix2d::Zero();
109 for (Iter i = sources.begin(), e = sources.end(); i != e; ++i) {
113 }
else if (dra < -
PI) {
117 cov(0,0) += dra * dra;
118 cov(0,1) += dra * dde;
119 cov(1,1) += dde * dde;
124 cov /=
static_cast<double>(sources.size() - 1);
136 Eigen::Vector2d
const neToSky(Eigen::Vector2d
const & ne)
const {
142 Eigen::Matrix2d
const neToSky(Eigen::Matrix2d
const & cov)
const {
148 Eigen::Vector2d
const &point,
Wcs const &wcs)
const;
163 _origin = Eigen::Vector3d(cosLat * cosLon, cosLat * sinLon, sinLat);
165 _north = Eigen::Vector3d(-1.0, 0.0, 0.0);
166 _east = Eigen::Vector3d(0.0, 1.0, 0.0);
168 _north = Eigen::Vector3d(-sinLat * cosLon, -sinLat * sinLon, cosLat);
169 _east = Eigen::Vector3d(-sinLon, cosLon, 0.0);
177 _jDiag = Eigen::Vector2d(1.0 / cosLat, 1.0);
181 Eigen::Vector2d
const &point,
Wcs const &wcs)
const
183 double const pix = 8.0;
184 double const invPix = 1.0 / pix;
187 point.x(), point.y())->toIcrs().getVector().asEigen();
189 point.x() + pix, point.y())->toIcrs().getVector().asEigen();
191 point.x(), point.y() + pix)->toIcrs().getVector().asEigen();
194 Eigen::Vector2d pxne(px.dot(
_east), px.dot(
_north));
195 Eigen::Vector2d pyne(py.dot(
_east), py.dot(
_north));
201 m.col(0) = (pxne - pne)*invPix;
202 m.col(1) = (pyne - pne)*invPix;
212 std::vector<SourceAndExposure>::const_iterator first,
213 std::vector<SourceAndExposure>::const_iterator last,
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())
227 void StatTuple::compute(
228 std::vector<SourceAndExposure>::const_iterator first,
229 std::vector<SourceAndExposure>::const_iterator 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();
237 min = std::numeric_limits<double>::infinity();
240 double ns =
static_cast<double>(last - first);
241 for (; first != last; ++first) {
242 double val = first->getSource()->get(key);
262 void weightedMeanCoord(
263 SourceClusterRecord & cluster,
264 std::vector<SourceAndExposure>
const & sources,
265 NeTanProj
const & proj)
267 typedef std::vector<SourceAndExposure>::const_iterator Iter;
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) {
278 }
else if (cov(0,0) <= 0.0 || cov(1,1) <= 0.0) {
289 if (cov(0,1) != cov(1,0)) {
291 cov(0,1) = 0.0; cov(1,0) = 0.0;
297 Eigen::Matrix2d m = i->getTransform().getLinear().getMatrix();
298 Eigen::Matrix2d invCov = (m * cov * m.transpose()).inverse();
300 p = i->getTransform()(p);
305 Eigen::Matrix2d cov = invCovSum.inverse();
307 wmean = proj.neToSky(wmean);
310 cluster.setWeightedMeanCoordErr(proj.neToSky(cov).cast<
float>());
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()));
319 cluster.setWeightedMeanCoordCount(n);
324 struct CmpSourceAndExposure {
325 bool operator()(SourceAndExposure
const & a, SourceAndExposure
const &
b)
const {
326 return a.getExposureInfo()->getFilter().getId() <
327 b.getExposureInfo()->getFilter().getId();
338 std::string
const & exposurePrefix)
340 typedef SourceCatalog::const_iterator SourceIter;
341 typedef std::vector<SourceAndExposure>::const_iterator SeIter;
343 if (sources.empty()) {
344 throw LSST_EXCEPT(InvalidParameterError,
"No sources in cluster");
347 meanCoord(cluster, sources);
349 boost::shared_ptr<std::vector<SourceAndExposure> > se(
350 new std::vector<SourceAndExposure>);
351 NeTanProj
const proj(cluster.
getCoord());
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;
356 se->reserve(sources.size());
357 for (SourceIter i = sources.begin(), e = sources.end(); i != e; ++i) {
358 boost::shared_ptr<ExposureInfo> exp =
361 throw LSST_EXCEPT(NotFoundError,
"No ExposureInfo for source");
364 i, exp, proj.pixelToNeTransform(i->getCentroid().asEigen(), *exp->getWcs())));
368 t.compute(se->begin(), se->end(), expTimeMidKey);
374 if (sources.getTable()->getCentroidKey().isValid() &&
375 sources.getTable()->getCentroidErrKey().isValid()) {
376 weightedMeanCoord(cluster, *se, proj);
379 std::sort(se->begin(), se->end(), CmpSourceAndExposure());
381 SeIter i = se->begin();
382 SeIter
const e = se->end();
384 int const filterId = i->getExposureInfo()->getFilter().getId();
386 for (++j; j != e && filterId == j->getExposureInfo()->getFilter().getId(); ++j) { }
387 std::string
const filterName = i->getExposureInfo()->getFilter().getName();
389 t.compute(i, j, expTimeMidKey);
402 typedef std::pair<double, double> FluxAndErr;
403 typedef std::pair<double, double> FluxAndVariance;
405 FluxAndErr
const weightedMeanFlux(std::vector<FluxAndVariance>
const & samples)
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));
418 for (Iter i = samples.begin(), e = samples.end(); i != e; ++i) {
419 double w = 1.0/i->second;
429 for (Iter i = samples.begin(), e = samples.end(); i != e; ++i) {
430 double d = i->first - wmean;
431 vwm += (d*
d)/i->second;
433 return FluxAndErr(wmean, std::sqrt(vwm/(wsum*(samples.size() - 1))));
441 std::vector<SourceAndExposure>
const & sources,
442 std::string
const & fluxDef,
446 typedef std::vector<SourceAndExposure>::const_iterator Iter;
447 typedef std::vector<Key<Flag > >::const_iterator FlagIter;
449 if (sources.empty()) {
450 throw LSST_EXCEPT(InvalidParameterError,
"No sources in cluster");
452 Schema const sourceSchema = sources[0].getSource()->getSchema();
454 Flux::MeasKey
const sourceFluxKey = sourceSchema[fluxDef];
455 Flux::ErrKey
const sourceFluxErrKey = sourceSchema[fluxDef +
".err"];
457 Iter i = sources.begin();
458 Iter
const e = sources.end();
461 int const filterId = i->getExposureInfo()->getFilter().getId();
463 for (++j; j != e && filterId == j->getExposureInfo()->getFilter().getId(); ++j) { }
466 std::vector<FluxAndVariance> samples;
467 samples.reserve(j - i);
468 std::string
const filter = i->getExposureInfo()->getFilter().getName();
471 double flux = r->
get(sourceFluxKey);
472 double fluxErr = r->
get(sourceFluxErrKey);
477 for (FlagIter f = skipFlags.begin(), fe = skipFlags.end(); f != fe; ++f) {
486 samples.push_back(i->getExposureInfo()->calibrateFlux(flux, fluxErr, fluxScale));
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()));
503 typedef std::pair<Quadrupole, Eigen::Matrix<double, 3, 3, Eigen::DontAlign> > ShapeAndErr;
505 void weightedMeanShape(ShapeAndErr & result, std::vector<ShapeAndErr>
const & samples)
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());
513 }
else if (samples.size() == 1) {
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();
523 wmean += m * (i->first.getParameterVector());
525 Eigen::Matrix3d cov = invCovSum.inverse();
537 std::vector<SourceAndExposure>
const & sources,
538 std::string
const & shapeDef,
541 typedef std::vector<SourceAndExposure>::const_iterator Iter;
542 typedef std::vector<Key<Flag > >::const_iterator FlagIter;
544 if (sources.empty()) {
545 throw LSST_EXCEPT(InvalidParameterError,
"No sources in cluster");
547 Schema const sourceSchema = sources[0].getSource()->getSchema();
549 Shape::MeasKey
const sourceShapeKey = sourceSchema[shapeDef];
550 Shape::ErrKey
const sourceShapeErrKey = sourceSchema[shapeDef +
".err"];
552 Iter i = sources.begin();
553 Iter
const e = sources.end();
556 int const filterId = i->getExposureInfo()->getFilter().getId();
558 for (++j; j != e && filterId == j->getExposureInfo()->getFilter().getId(); ++j) { }
560 std::vector<ShapeAndErr> samples;
561 samples.reserve(j - i);
562 std::string
const filter = i->getExposureInfo()->getFilter().getName();
566 for (FlagIter f = skipFlags.begin(), fe = skipFlags.end(); f != fe; ++f) {
581 Eigen::Matrix3d cov = r->
get(sourceShapeErrKey).cast<
double>();
586 }
else if (cov(0,0) <= 0.0 ||
599 if (cov(0,1) != cov(1,0) || cov(0,2) != cov(2,0) || cov(1,2) != 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;
614 cov = j * cov * j.transpose();
615 samples.push_back(ShapeAndErr(q, cov));
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()));
An ellipse core with quadrupole moments as parameters.
Defines the fields and offsets for a table.
CentroidSlotDefinition::MeasValue getCentroid() const
Get the value of the Centroid slot measurement.
Schema getSchema() const
Return the Schema that holds this record's fields and keys.
lsst::afw::geom::Angle getDec() const
boost::shared_ptr< std::vector< SourceAndExposure > > const computeBasicAttributes(SourceClusterRecord &cluster, SourceCatalog const &sources, ExposureInfoMap const &exposures, std::string const &exposurePrefix)
AngleUnit const radians
constant with units of radians
std::vector< SourceCatalog > const cluster(SourceCatalog const &sources, ClusteringControl const &control)
Functions for computing cluster attributes.
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)
Record class that contains measurement averages on clusters of single exposure sources.
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)
void computeFluxMean(SourceClusterRecord &cluster, std::vector< SourceAndExposure > const &sources, std::string const &fluxDef, std::vector< Key< Flag > > const &skipFlags, double fluxScale)
Implementation of the WCS standard for a any projection.
double const getIyy() const
CentroidSlotDefinition::ErrValue getCentroidErr() const
Get the uncertainty on the Centroid slot measurement.
boost::shared_ptr< coord::Coord > pixelToSky(double pix1, double pix2) const
Convert from pixel position to sky coordinates (e.g. RA/dec)
lsst::afw::coord::IcrsCoord IcrsCoord
Eigen::Vector2d const cartesianToSpherical(Eigen::Vector3d const &v)
IcrsCoord getCoord() const
Convenience accessors for the keys in the minimal reference schema.
Field< T >::Value get(Key< T > const &key) const
Return the value of a field for the given key.
afw::geom::ellipses::Quadrupole Shape
void setNumSources(int const &value)
Set the number of sources in the cluster.
void setCoordErr(Eigen::Matrix< float, 2, 2 > const &value)
Set the uncertainty of the sky-coordinates of the cluster.
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
lsst::afw::image::Wcs Wcs
void setCoord(IcrsCoord const &coord)
Convenience accessors for the keys in the minimal reference schema.
#define LSST_EXCEPT(type,...)
Spatial utility functions.
A class used as a handle to a particular field in a table.
Transformer transform(LinearTransform const &transform)
lsst::afw::geom::Angle getLongitude() const
The main access method for the longitudinal coordinate.
void set(Key< T > const &key, U const &value)
Set value of a field for the given key.
afw::table::Key< double > b
Point< double, 2 > Point2D
double const getIxy() const
Record class that contains measurements made on a single exposure.
double const PI
The ratio of a circle's circumference to diameter.
lsst::afw::geom::Angle getLatitude() const
The main access method for the latitudinal coordinate.
lsst::afw::geom::Angle getRa() const
A class to handle Icrs coordinates (inherits from Coord)
SortedCatalogT< SourceRecord > SourceCatalog