39 #include "boost/scoped_ptr.hpp"
63 using std::numeric_limits;
93 namespace lsst {
namespace ap {
namespace match {
98 double const RADY_PER_MASD = 1.32734751843815467101961424328e-11;
110 RefPosMatch(MatchableRef *ref,
112 Eigen::Vector3d
const &v,
129 MatchableRef
const *getRef()
const {
132 MatchableRef *getRef() {
135 MatchablePos
const *getPos()
const {
138 MatchablePos *getPos() {
141 IcrsCoord const & getSphericalCoordinates()
const {
144 Angle getAngularSeparation()
const {
148 int getRefCount()
const {
151 int decrementRefCount() {
166 class Matchable :
public BBox {
170 Matchable(std::string
const &
record) :
179 virtual ~Matchable() { }
181 int64_t getClosestId()
const {
184 int getNumMatches()
const {
187 std::string
const & getRecord()
const {
191 MatchVector & getMatches() {
194 MatchVector
const & getMatches()
const {
198 void appendMatch(int64_t
id, RefPosMatch *m) {
207 int decrementRefCount() {
210 int getRefCount()
const {
234 class MatchablePos :
public Matchable {
236 MatchablePos(int64_t
id,
241 std::string
const &record);
243 virtual ~MatchablePos() { }
245 int64_t getId()
const {
248 double getEpoch()
const {
251 IcrsCoord const & getSphericalCoordinates()
const {
254 Eigen::Vector3d
const & getPosition()
const {
257 Angle getRadius()
const {
262 virtual double getMinCoord0()
const {
265 virtual double getMaxCoord0()
const {
268 virtual double getMinCoord1()
const {
271 virtual double getMaxCoord1()
const {
284 MatchablePos::MatchablePos(
290 std::string
const &record
296 _p(
_sc.getVector().asEigen()),
304 class MatchableRef :
public Matchable {
306 MatchableRef(ReferencePosition
const &rp,
307 std::string
const &record,
310 virtual ~MatchableRef() { }
312 ReferencePosition & getReferencePosition() {
315 ReferencePosition
const & getReferencePosition()
const {
320 virtual double getMinCoord0()
const {
321 return _rp.getMinCoord0();
323 virtual double getMaxCoord0()
const {
324 return _rp.getMaxCoord0();
326 virtual double getMinCoord1()
const {
327 return _rp.getMinCoord1();
329 virtual double getMaxCoord1()
const {
330 return _rp.getMaxCoord1();
337 MatchableRef::MatchableRef(
338 ReferencePosition
const &rp,
339 std::string
const &record,
346 inline bool operator<(std::pair<Angle, MatchableRef *>
const &r1,
347 std::pair<Angle, MatchableRef *>
const &r2) {
348 return r1.first > r2.first;
354 class RefWithCov :
public BBox {
356 RefWithCov(ReferencePosition
const &rp,
357 std::string
const &record,
360 virtual ~RefWithCov() { }
362 ReferencePosition & getReferencePosition() {
365 ReferencePosition
const & getReferencePosition()
const {
369 std::string
const & getRecord()
const {
379 int isCovered()
const {
383 void appendMatch(ExposureInfo *e) {
389 virtual double getMinCoord0()
const {
390 return _rp.getMinCoord0();
392 virtual double getMaxCoord0()
const {
393 return _rp.getMaxCoord0();
395 virtual double getMinCoord1()
const {
396 return _rp.getMinCoord1();
398 virtual double getMaxCoord1()
const {
399 return _rp.getMaxCoord1();
403 ReferencePosition
_rp;
410 RefWithCov::RefWithCov(
411 ReferencePosition
const &rp,
412 std::string
const &record,
418 _numFilters(numFilters),
421 for (
int i = 0; i < numFilters; ++i) {
426 inline bool operator<(std::pair<Angle, RefWithCov *>
const &r1,
427 std::pair<Angle, RefWithCov *>
const &r2) {
428 return r1.first > r2.first;
439 class MatchablePosReader {
441 MatchablePosReader(std::string
const &path,
442 CatalogControl
const &control,
447 ~MatchablePosReader();
449 double getMinEpoch()
const {
return _minEpoch; }
450 double getMaxEpoch()
const {
return _maxEpoch; }
456 std::string
const & getNullRecord()
const {
462 bool haveOutputRecord()
const {
468 bool isDone()
const {
481 MatchablePos *
next() {
482 MatchablePos *p =
_next;
489 void destroy(MatchablePos *p) {
518 MatchablePosReader::MatchablePosReader(
519 std::string
const &path,
520 CatalogControl
const &control,
539 typedef std::vector<std::string>::const_iterator Iter;
541 Log log(Log::getDefaultLog(),
"lsst.ap.match");
542 log.
log(Log::INFO,
"Opening position table " + path);
546 if (!control.fieldNames.empty()) {
547 _reader->setFieldNames(control.fieldNames);
551 if (!control.epochColumn.empty()) {
562 "Position table does not contain unique id, "
563 "right ascension, or declination column(s)");
566 if (!control.outputFields.empty()) {
569 if (control.outputFields.size() == 1 &&
570 control.outputFields[0] ==
"*") {
571 int nFields =
static_cast<int>(
_reader->getFieldNames().size());
572 for (
int i = 0; i < nFields; ++i) {
577 for (Iter i = control.outputFields.begin(),
578 e = control.outputFields.end(); i != e; ++i) {
579 int idx =
_reader->getIndexOf(*i);
581 throw LSST_EXCEPT(pexExcept::InvalidParameterError,
582 "Position table has no column named " +
597 log.
log(Log::INFO,
"Scanning position table to determine min/max "
598 "epoch of input positions");
599 CsvReader reader(path, dialect, control.fieldNames.empty());
601 log.
format(Log::INFO,
"Scanned %llu records",
602 static_cast<unsigned long long>(reader.getNumRecords()));
605 log.
format(Log::INFO,
" - time range is [%.3f, %.3f] MJD",
611 MatchablePosReader::~MatchablePosReader() { }
614 typedef std::vector<int>::const_iterator Iter;
631 "NULL unique id found in position table");
637 "Position table contains NULL or NaN right "
638 "ascension, declination, or epoch");
640 if (decl < -HALFPI || decl >
HALFPI) {
642 "Invalid declination found in position table");
644 if (epoch < J2000_MJD - 200.0*DAYS_PER_JY ||
645 epoch > J2000_MJD + 200.0*DAYS_PER_JY) {
647 "Position table epoch is not within 200 years of "
648 "J2000. Check your units - MJD required.");
653 "Position table is not sorted by declination");
679 "Position table contains NULL or NaN epoch");
682 while (!reader.
isDone()) {
686 "Position table contains NULL or NaN epoch");
690 }
else if (epoch > _maxEpoch) {
705 class RefReaderBase {
707 RefReaderBase(std::string
const &path,
708 CatalogControl
const &control,
711 double minMatchEpoch,
712 double maxMatchEpoch,
713 Angle parallaxThresh);
715 virtual ~RefReaderBase();
717 double getMinEpoch()
const {
return _minEpoch; }
718 double getMaxEpoch()
const {
return _maxEpoch; }
729 std::string
const & getNullRecord()
const {
736 bool haveOutputRecord()
const {
741 ReferencePosition
const * _readReferencePosition();
743 std::string
const & getRecord()
const {
749 boost::scoped_ptr<CsvReader>
_reader;
755 bool needMotionStats);
757 ReferencePosition
_pos;
784 std::stringstream
_buf;
788 RefReaderBase::RefReaderBase(
789 std::string
const &path,
790 CatalogControl
const &control,
793 double minMatchEpoch,
794 double maxMatchEpoch,
800 _numFilters(static_cast<int>(
Filter::getNames().size())),
816 typedef std::vector<std::string>::const_iterator Iter;
818 Log log(Log::getDefaultLog(),
"lsst.ap.match");
819 log.
log(Log::INFO,
"Opening reference catalog " + path);
823 if (!control.fieldNames.empty()) {
824 _reader->setFieldNames(control.fieldNames);
828 if (!control.epochColumn.empty()) {
848 "Reference catalog doesn't contain unique id, "
849 "right ascension, or declination column(s)");
852 if (!control.outputFields.empty()) {
855 if (control.outputFields.size() == 1 &&
856 control.outputFields[0] ==
"*") {
857 int nFields =
static_cast<int>(
_reader->getFieldNames().size());
858 for (
int i = 0; i < nFields; ++i) {
863 for (Iter i = control.outputFields.begin(),
864 e = control.outputFields.end(); i != e; ++i) {
865 int idx =
_reader->getIndexOf(*i);
867 throw LSST_EXCEPT(pexExcept::InvalidParameterError,
868 "Reference catalog has no column "
880 bool needEpochStats =
false;
881 bool needMotionStats =
false;
886 _maxEpoch = control.maxEpoch;
888 needEpochStats =
true;
897 needMotionStats =
true;
901 if (needEpochStats || needMotionStats) {
902 log.
log(Log::INFO,
"Scanning reference catalog to determine "
903 "time-range and/or maximum velocity/parallax");
904 CsvReader reader(path, dialect, control.fieldNames.empty());
905 _scan(reader, needEpochStats, needMotionStats);
906 log.
format(Log::INFO,
"Scanned %llu records",
916 log.
format(Log::INFO,
" - time range is [%.3f, %.3f] MJD",
918 log.
format(Log::INFO,
" - max parallax is %.3f milliarcsec",
920 log.
format(Log::INFO,
" - max angular velocity is %.3f milliarcsec/yr",
922 log.
format(Log::INFO,
" - read-ahead is %.3f milliarcsec",
926 RefReaderBase::~RefReaderBase() { }
928 ReferencePosition
const * RefReaderBase::_readReferencePosition() {
929 typedef std::vector<int>::const_iterator Iter;
945 "NULL unique id found in reference catalog");
951 "Reference catalog record contains NULL/NaN right "
952 "ascension, declination, or epoch");
954 if (decl < -HALFPI || decl > HALFPI) {
956 "Invalid declination found in reference catalog");
958 if (epoch < J2000_MJD - 200.0*DAYS_PER_JY ||
959 epoch > J2000_MJD + 200.0*DAYS_PER_JY) {
961 "Reference catalog epoch is not within 200 years "
962 "of J2000. Check your units - MJD required.");
967 "Reference catalog is not sorted by declination");
979 _pos = ReferencePosition(
id, ra, decl, epoch);
988 if (parallax < 0.0) {
990 "Reference catalog contains negative parallax");
997 _pos.setMotion(muRa, muDecl, parallax, vRadial,
1008 bool needEpochStats,
1009 bool needMotionStats)
1014 if (needEpochStats) {
1018 while (!reader.
isDone()) {
1019 if (needEpochStats) {
1023 "Record contains NULL or NaN epoch");
1027 }
else if (epoch > _maxEpoch) {
1031 if (needMotionStats) {
1041 if (decl < -HALFPI || decl > HALFPI ||
1044 "Invalid declination found in "
1045 "reference catalog");
1047 muRa *= std::cos(decl.asRadians());
1052 double v = sqrt(muRa*muRa + muDecl*muDecl);
1066 template <
typename Ref>
1067 class RefReader :
public RefReaderBase {
1069 RefReader(std::string
const &path,
1070 CatalogControl
const &control,
1073 double minMatchEpoch,
1074 double maxMatchEpoch,
1075 Angle parallaxThresh);
1081 bool isDone()
const {
1082 return _heap.empty();
1088 Angle peek()
const {
1089 return _heap.front().first;
1099 Ref *r =
_heap.back().second;
1106 void destroy(Ref *r) {
1112 ReferencePosition
const *p = _readReferencePosition();
1115 _heap.push_back(std::pair<Angle, Ref *>(
Angle(r->getMinCoord1()), r));
1121 std::vector<std::pair<Angle, Ref *> >
_heap;
1124 template <
typename Ref>
1125 RefReader<Ref>::RefReader(
1126 std::string
const &path,
1127 CatalogControl
const &control,
1130 double minMatchEpoch,
1131 double maxMatchEpoch,
1132 Angle parallaxThresh
1145 template <
typename Ref>
1146 RefReader<Ref>::~RefReader() { }
1153 class RefPosMatcher {
1155 RefPosMatcher(
bool outputRefExtras);
1159 void operator()(MatchablePos *p) {
1162 void operator()(MatchableRef *r) {
1165 void operator()(MatchableRef *r, MatchablePos *p) {
1166 _candidateMatch(r, p);
1168 void operator()(MatchablePos *p, MatchableRef *r) {
1169 _candidateMatch(r, p);
1173 RefReader<MatchableRef> &refReader,
1174 MatchablePosReader &posReader);
1177 void _writeMatch(RefPosMatch
const *m);
1178 void _finish(MatchablePos *p);
1179 void _finish(MatchableRef *r);
1180 void _candidateMatch(MatchableRef *r, MatchablePos *p);
1197 RefPosMatcher::RefPosMatcher(
bool outputRefExtras) :
1207 RefPosMatcher::~RefPosMatcher() {
1215 void RefPosMatcher::match(
CsvWriter &writer,
1216 RefReader<MatchableRef> &refReader,
1217 MatchablePosReader &posReader)
1248 if (refDecl < posDecl) {
1267 void RefPosMatcher::_writeMatch(RefPosMatch
const *m) {
1268 MatchableRef
const *r = m->getRef();
1269 MatchablePos
const *p = m->getPos();
1274 m->getSphericalCoordinates().getLongitude().asDegrees());
1276 m->getSphericalCoordinates().getLatitude().asDegrees());
1300 void RefPosMatcher::_finish(MatchablePos *p) {
1301 typedef MatchablePos::MatchVector::iterator Iter;
1303 if (p->getNumMatches() == 0) {
1338 for (Iter i = p->getMatches().begin(), e = p->getMatches().end();
1340 RefPosMatch *m = *i;
1341 MatchableRef *r = m->getRef();
1342 if (m->decrementRefCount() == 0) {
1347 if (r->decrementRefCount() == 0) {
1351 p->decrementRefCount();
1354 if (p->getRefCount() == 0) {
1362 void RefPosMatcher::_finish(MatchableRef *r) {
1363 typedef MatchablePos::MatchVector::iterator Iter;
1365 if (r->getNumMatches() == 0) {
1400 for (Iter i = r->getMatches().begin(), e = r->getMatches().end();
1402 RefPosMatch *m = *i;
1403 MatchablePos *p = m->getPos();
1404 if (m->decrementRefCount() == 0) {
1409 if (p->decrementRefCount() == 0) {
1413 r->decrementRefCount();
1416 if (r->getRefCount() == 0) {
1423 void RefPosMatcher::_candidateMatch(MatchableRef *r, MatchablePos *p) {
1424 Eigen::Vector3d v = r->getReferencePosition().getPosition(p->getEpoch());
1426 if (sep <= p->getRadius()) {
1428 RefPosMatch *m =
new (
_arena) RefPosMatch(r, p, v, sep);
1429 r->appendMatch(p->getId(), m);
1430 p->appendMatch(r->getReferencePosition().getId(), m);
1437 class RefExpMatcher {
1443 void operator()(ExposureInfo *e) { }
1445 void operator()(RefWithCov *r) {
1448 void operator()(RefWithCov *r, ExposureInfo *e) {
1449 _candidateMatch(r, e);
1451 void operator()(ExposureInfo *e, RefWithCov *r) {
1452 _candidateMatch(r, e);
1456 RefReader<RefWithCov> &refReader,
1457 std::vector<ExposureInfo::Ptr> &exposures);
1460 struct ExposureInfoComparator {
1461 bool operator()(ExposureInfo::Ptr
const &e1,
1462 ExposureInfo::Ptr
const &e2)
const
1464 return e1->getMinCoord1() < e2->getMinCoord1();
1468 void _finish(RefWithCov *r);
1469 void _candidateMatch(RefWithCov *r, ExposureInfo *e);
1472 std::vector<std::pair<Angle, RefWithCov *> >
_heap;
1483 RefExpMatcher::RefExpMatcher() :
1491 RefExpMatcher::~RefExpMatcher() {
1498 void RefExpMatcher::match(
CsvWriter &writer,
1499 RefReader<RefWithCov> &refReader,
1500 std::vector<ExposureInfo::Ptr> &exposures)
1502 typedef std::vector<ExposureInfo::Ptr>::const_iterator Iter;
1508 std::sort(exposures.begin(), exposures.end(), ExposureInfoComparator());
1509 Iter i = exposures.begin();
1510 Iter
const end = exposures.end();
1525 for (; i != end; ++i) {
1527 _refSweep.advance(expDecl.asRadians(), *
this);
1528 ExposureInfo *info = i->get();
1535 _refSweep.advance(expDecl.asRadians(), *
this);
1536 _expSweep.advance(refDecl.asRadians(), *
this);
1537 if (refDecl < expDecl) {
1542 ExposureInfo *info = i->get();
1550 while (!
_heap.empty()) {
1554 RefWithCov *r =
_heap.back().second;
1571 void RefExpMatcher::_finish(RefWithCov *r) {
1572 if (!r->isCovered()) {
1577 Angle decl = r->getReferencePosition().getSphericalCoords().getLatitude();
1581 _heap.push_back(std::pair<Angle, RefWithCov *>(decl, r));
1583 while (!
_heap.empty() &&
1586 r =
_heap.back().second;
1602 void RefExpMatcher::_candidateMatch(RefWithCov *r, ExposureInfo *e) {
1603 double epoch = e->getEpoch();
1604 Eigen::Vector3d v = r->getReferencePosition().getPosition(
1605 epoch, e->getEarthPosition());
1609 p.getX() <= e->getX0() + e->getWidth() +
PixelZeroPos - 0.5 &&
1611 p.getY() <= e->getY0() + e->getHeight() +
PixelZeroPos - 0.5) {
1619 void checkFilters() {
1620 typedef std::vector<std::string>::const_iterator Iter;
1622 std::vector<std::string>
const names = Filter::getNames();
1623 int n =
static_cast<int>(names.size());
1624 for (Iter i = names.begin(), e = names.end(); i != e; ++i) {
1628 "Filter IDs do not have contiguous IDs starting at 0");
1638 std::string
const &refFile,
1641 std::string
const &posFile,
1644 std::string
const &outFile,
1648 bool outputRefExtras,
1649 bool truncateOutFile
1651 Log log(Log::getDefaultLog(),
"lsst.ap.match");
1652 log.
log(Log::INFO,
"Matching reference catalog to position table...");
1655 MatchablePosReader posReader(
1656 posFile, posControl, posDialect, outDialect, radius);
1657 RefReader<MatchableRef> refReader(
1658 refFile, refControl, refDialect, outDialect,
1659 posReader.getMinEpoch(), posReader.getMaxEpoch(), parallaxThresh);
1660 CsvWriter writer(outFile, outDialect, truncateOutFile);
1661 RefPosMatcher matcher(outputRefExtras);
1663 log.
log(Log::INFO,
"Starting reference catalog to position table match");
1664 matcher.match(writer, refReader, posReader);
1665 log.
format(Log::INFO,
"Wrote %llu records to output match table %s",
1666 static_cast<unsigned long long>(writer.getNumRecords()),
1672 std::vector<ExposureInfo::Ptr> &exposures,
1673 std::string
const &refFile,
1676 std::string
const &outFile,
1679 bool truncateOutFile
1681 typedef std::vector<ExposureInfo::Ptr>::const_iterator Iter;
1682 if (exposures.empty()) {
1683 throw LSST_EXCEPT(pexExcept::InvalidParameterError,
1684 "no input exposure information");
1688 Log log(Log::getDefaultLog(),
"lsst.ap.match");
1689 log.
log(Log::INFO,
"Filtering out reference catalog entries not "
1690 "observable in any exposure...");
1693 Iter i = exposures.begin();
1694 double minEpoch = (*i)->getEpoch();
1695 double maxEpoch = minEpoch;
1697 for (Iter e = exposures.end(); i != e; ++i) {
1698 double epoch = (*i)->getEpoch();
1699 if (epoch < minEpoch) {
1701 }
else if (epoch > maxEpoch) {
1705 log.
format(Log::INFO,
"Time range of exposures is [%.3f, %.3f] MJD",
1706 minEpoch, maxEpoch);
1709 RefReader<RefWithCov> refReader(
1710 refFile, refControl, refDialect, outDialect,
1711 minEpoch, maxEpoch, parallaxThresh);
1712 CsvWriter writer(outFile, outDialect, truncateOutFile);
1713 RefExpMatcher matcher;
1715 log.
log(Log::INFO,
"Starting reference catalog to exposure match");
1716 matcher.match(writer, refReader, exposures);
1717 log.
format(Log::INFO,
"Wrote %llu records to output match table %s",
An include file to include the public header files for lsst::afw::math.
Class for simulated reference catalog positions.
SphericalSweep< MatchableRef > _refSweep
void referenceMatch(std::string const &refFile, CatalogControl const &refControl, CsvControl const &refDialect, std::string const &posFile, CatalogControl const &posControl, CsvControl const &posDialect, std::string const &outFile, CsvControl const &outDialect, lsst::afw::geom::Angle const radius, lsst::afw::geom::Angle const parallaxThresh, bool outputRefExtras, bool truncateOutFile)
char getDelimiter() const
lsst::afw::geom::Angle const angularSeparation(Eigen::Vector3d const &v1, Eigen::Vector3d const &v2)
lsst::afw::geom::Angle Angle
void appendField(std::string const &v)
Parameters that define a Character-Separated-Value dialect.
size_t getNumRecords() const
lsst::afw::geom::Angle getLongitude() const
The main access method for the longitudinal coordinate.
void format(int importance, const char *fmt,...)
boost::scoped_array< int > _filterCov
lsst::afw::coord::IcrsCoord const cartesianToIcrs(Eigen::Vector3d const &v)
void log(int importance, const std::string &message, const lsst::daf::base::PropertySet &properties)
size_t getNumRecords() const
MatchablePosReader * _posReader
a place to record messages and descriptions of the state of processing.
definition of the DualLog class
void referenceFilter(std::vector< ExposureInfo::Ptr > &exposures, std::string const &refFile, CatalogControl const &refControl, CsvControl const &refDialect, std::string const &outFile, CsvControl const &outDialect, lsst::afw::geom::Angle const parallaxThresh, bool truncateOutFile)
std::string const get(int i) const
Matching against simulated reference catalog positions.
RefReader< MatchableRef > * _refReader
Class that bundles together the WCS, extents, time, and calibration information from an image (typica...
Config parameters for catalogs involved in reference matching.
lsst::afw::coord::IcrsCoord IcrsCoord
AngleUnit const radians
constant with units of radians
double maxAlpha(double const theta, double const centerDec)
double _maxAngularVelocity
SphericalSweep< ExposureInfo > _expSweep
Holds an integer identifier for an LSST filter.
SphericalSweep< MatchablePos > _posSweep
#define LSST_EXCEPT(type,...)
Spatial utility functions.
std::vector< std::pair< Angle, Ref * > > _heap
Arena< MatchablePos > _arena
Single threaded arena (pool) memory allocator.
2D bounding box interface.
double arcsecToRad(double x)
double radToMas(double x)
double masToRad(double x)
Useful astronomical constants.
lsst::afw::geom::Angle getLatitude() const
The main access method for the latitudinal coordinate.
Pointer vector that avoids memory allocation for small vectors.
void write(std::string const &v)
const double PixelZeroPos
FITS uses 1.0, SDSS uses 0.5, LSST uses 0.0 (http://dev.lsstcorp.org/trac/wiki/BottomLeftPixelProposa...
std::vector< int > _columns
Angle const maxAlpha(Angle radius, Angle centerPhi)
boost::scoped_ptr< CsvReader > _reader
A class to handle Icrs coordinates (inherits from Coord)
void push_back(value_type v)
Sweep line data structures useful for region-region matching.
afw::table::SourceRecord * record
CsvControl const & getControl() const
Functions to handle coordinates.
lsst::afw::geom::Angle const clampPhi(lsst::afw::geom::Angle const a)