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
ReferenceMatch.cc
Go to the documentation of this file.
1 // -*- lsst-c++ -*-
2 
3 /*
4  * LSST Data Management System
5  * Copyright 2008, 2009, 2010 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 
31 
32 #include <math.h>
33 #include <cstddef>
34 #include <algorithm>
35 #include <limits>
36 #include <string>
37 #include <vector>
38 
39 #include "boost/scoped_ptr.hpp"
40 
41 #include "lsst/utils/ieee.h"
42 
43 #include "lsst/pex/logging/Log.h"
44 #include "lsst/afw/coord/Coord.h"
45 #include "lsst/afw/geom/Angle.h"
47 
48 #include "lsst/ap/constants.h"
49 #include "lsst/ap/utils/Arena.h"
50 #include "lsst/ap/utils/Csv.h"
53 #include "lsst/ap/match/BBox.h"
58 
59 
60 using std::fabs;
61 using std::min;
62 using std::max;
63 using std::numeric_limits;
64 using std::vector;
65 using std::string;
66 
68 
75 
77 
80 
82 
91 
92 
93 namespace lsst { namespace ap { namespace match {
94 
95 namespace {
96 
97 // converts from milliarcsec/yr to rad/day
98 double const RADY_PER_MASD = 1.32734751843815467101961424328e-11;
99 
100 // -- Classes for match participants ----
101 
102 class MatchablePos;
103 class MatchableRef;
104 
105 
108 class RefPosMatch {
109 public:
110  RefPosMatch(MatchableRef *ref,
111  MatchablePos *pos,
112  Eigen::Vector3d const &v,
114  ) :
115  _ref(ref),
116  _pos(pos),
117  _sc(cartesianToIcrs(v)),
118  _angSep(angularSeparation),
119  _refCount(2)
120  { }
121 
122  ~RefPosMatch() {
123 #ifndef NDEBUG
124  _ref = 0;
125  _pos = 0;
126 #endif
127  }
128 
129  MatchableRef const *getRef() const {
130  return _ref;
131  }
132  MatchableRef *getRef() {
133  return _ref;
134  }
135  MatchablePos const *getPos() const {
136  return _pos;
137  }
138  MatchablePos *getPos() {
139  return _pos;
140  }
141  IcrsCoord const & getSphericalCoordinates() const {
142  return _sc;
143  }
144  Angle getAngularSeparation() const {
145  return _angSep;
146  }
147 
148  int getRefCount() const {
149  return _refCount;
150  }
151  int decrementRefCount() {
152  return --_refCount;
153  }
154 
155 private:
156  MatchableRef *_ref;
157  MatchablePos *_pos;
161 };
162 
163 
166 class Matchable : public BBox {
167 public:
169 
170  Matchable(std::string const &record) :
171  _closestId(0),
172  _closestSep(numeric_limits<double>::infinity()),
173  _nMatches(0),
174  _refCount(0),
175  _matches(),
176  _record(record)
177  { }
178 
179  virtual ~Matchable() { }
180 
181  int64_t getClosestId() const {
182  return _closestId;
183  }
184  int getNumMatches() const {
185  return _nMatches;
186  }
187  std::string const & getRecord() const {
188  return _record;
189  }
190 
191  MatchVector & getMatches() {
192  return _matches;
193  }
194  MatchVector const & getMatches() const {
195  return _matches;
196  }
197 
198  void appendMatch(int64_t id, RefPosMatch *m) {
199  _matches.push_back(m);
200  ++_refCount;
201  ++_nMatches;
202  if (m->getAngularSeparation() < _closestSep) {
203  _closestId = id;
204  _closestSep = m->getAngularSeparation();
205  }
206  }
207  int decrementRefCount() {
208  return --_refCount;
209  }
210  int getRefCount() const {
211  return _refCount;
212  }
213 
214 private:
215  int64_t _closestId;
216  double _closestSep;
218  int _refCount;
219  MatchVector _matches;
220  std::string _record;
221 };
222 
223 
234 class MatchablePos : public Matchable {
235 public:
236  MatchablePos(int64_t id,
237  Angle ra,
238  Angle decl,
239  double epoch,
240  Angle radius,
241  std::string const &record);
242 
243  virtual ~MatchablePos() { }
244 
245  int64_t getId() const {
246  return _id;
247  }
248  double getEpoch() const {
249  return _epoch;
250  }
251  IcrsCoord const & getSphericalCoordinates() const {
252  return _sc;
253  }
254  Eigen::Vector3d const & getPosition() const {
255  return _p;
256  }
257  Angle getRadius() const {
258  return _radius;
259  }
260 
261  // BBox API
262  virtual double getMinCoord0() const {
263  return (_sc.getLongitude() - _alpha).asRadians();
264  }
265  virtual double getMaxCoord0() const {
266  return (_sc.getLongitude() + _alpha).asRadians();
267  }
268  virtual double getMinCoord1() const {
269  return clampPhi(_sc.getLatitude() - _radius);
270  }
271  virtual double getMaxCoord1() const {
272  return clampPhi(_sc.getLatitude() + _radius);
273  }
274 
275 private:
276  int64_t _id;
277  double _epoch;
278  IcrsCoord _sc;
279  Eigen::Vector3d _p;
282 };
283 
284 MatchablePos::MatchablePos(
285  int64_t id,
286  Angle ra,
287  Angle decl,
288  double epoch,
289  Angle radius,
290  std::string const &record
291 ) :
292  Matchable(record),
293  _id(id),
294  _epoch(epoch),
295  _sc(ra, decl),
296  _p(_sc.getVector().asEigen()),
297  _radius(radius),
298  _alpha(maxAlpha(radius, max(fabs(getMinCoord1()), fabs(getMaxCoord1())) * radians))
299 { }
300 
301 
304 class MatchableRef : public Matchable {
305 public:
306  MatchableRef(ReferencePosition const &rp,
307  std::string const &record,
308  int);
309 
310  virtual ~MatchableRef() { }
311 
312  ReferencePosition & getReferencePosition() {
313  return _rp;
314  }
315  ReferencePosition const & getReferencePosition() const {
316  return _rp;
317  }
318 
319  // BBox API
320  virtual double getMinCoord0() const {
321  return _rp.getMinCoord0();
322  }
323  virtual double getMaxCoord0() const {
324  return _rp.getMaxCoord0();
325  }
326  virtual double getMinCoord1() const {
327  return _rp.getMinCoord1();
328  }
329  virtual double getMaxCoord1() const {
330  return _rp.getMaxCoord1();
331  }
332 
333 private:
334  ReferencePosition _rp;
335 };
336 
337 MatchableRef::MatchableRef(
338  ReferencePosition const &rp,
339  std::string const &record,
340  int
341 ) :
342  Matchable(record),
343  _rp(rp)
344 { }
345 
346 inline bool operator<(std::pair<Angle, MatchableRef *> const &r1,
347  std::pair<Angle, MatchableRef *> const &r2) {
348  return r1.first > r2.first;
349 }
350 
351 
354 class RefWithCov : public BBox {
355 public:
356  RefWithCov(ReferencePosition const &rp,
357  std::string const &record,
358  int numFilters);
359 
360  virtual ~RefWithCov() { }
361 
362  ReferencePosition & getReferencePosition() {
363  return _rp;
364  }
365  ReferencePosition const & getReferencePosition() const {
366  return _rp;
367  }
368 
369  std::string const & getRecord() const {
370  return _record;
371  }
372 
373  void writeCoverage(lsst::ap::utils::CsvWriter &writer) const {
374  for (int i = 0; i < _numFilters; ++i) {
375  writer.appendField(_filterCov[i]);
376  }
377  }
378 
379  int isCovered() const {
380  return _covered;
381  }
382 
383  void appendMatch(ExposureInfo *e) {
384  _covered = true;
385  ++_filterCov[e->getFilter().getId()];
386  }
387 
388  // BBox API
389  virtual double getMinCoord0() const {
390  return _rp.getMinCoord0();
391  }
392  virtual double getMaxCoord0() const {
393  return _rp.getMaxCoord0();
394  }
395  virtual double getMinCoord1() const {
396  return _rp.getMinCoord1();
397  }
398  virtual double getMaxCoord1() const {
399  return _rp.getMaxCoord1();
400  }
401 
402 private:
403  ReferencePosition _rp;
404  std::string _record;
405  bool _covered;
407  boost::scoped_array<int> _filterCov;
408 };
409 
410 RefWithCov::RefWithCov(
411  ReferencePosition const &rp,
412  std::string const &record,
413  int numFilters
414 ) :
415  _rp(rp),
416  _record(record),
417  _covered(false),
418  _numFilters(numFilters),
419  _filterCov(new int[numFilters])
420 {
421  for (int i = 0; i < numFilters; ++i) {
422  _filterCov[i] = 0;
423  }
424 }
425 
426 inline bool operator<(std::pair<Angle, RefWithCov *> const &r1,
427  std::pair<Angle, RefWithCov *> const &r2) {
428  return r1.first > r2.first;
429 }
430 
431 
432 // -- Classes for reading in reference objects and positions ----
433 
439 class MatchablePosReader {
440 public:
441  MatchablePosReader(std::string const &path,
442  CatalogControl const &control,
443  CsvControl const &dialect,
444  CsvControl const &outDialect,
445  Angle radius);
446 
447  ~MatchablePosReader();
448 
449  double getMinEpoch() const { return _minEpoch; }
450  double getMaxEpoch() const { return _maxEpoch; }
451  double getDefaultEpoch() const { return _defaultEpoch; }
452 
456  std::string const & getNullRecord() const {
457  return _nullRecord;
458  }
459 
462  bool haveOutputRecord() const {
463  return !_columns.empty();
464  }
465 
468  bool isDone() const {
469  return _next == 0;
470  }
471 
475  Angle peek() const {
476  return _next->getMinCoord1() * radians;
477  }
478 
481  MatchablePos *next() {
482  MatchablePos *p = _next;
483  _read();
484  return p;
485  }
486 
489  void destroy(MatchablePos *p) {
490  _arena.destroy(p);
491  }
492 
493 private:
494  void _read();
495  void _scan(lsst::ap::utils::CsvReader &reader);
496 
500  double _minEpoch;
501  double _maxEpoch;
502  Angle _radius;
503  MatchablePos *_next;
504  boost::scoped_ptr<CsvReader> _reader;
505  int _idCol;
507  int _raCol;
508  int _declCol;
509  double _raScale;
510  double _declScale;
511  std::vector<int> _columns;
512  std::string _nullRecord;
513  std::string _record;
514  std::stringstream _buf;
516 };
517 
518 MatchablePosReader::MatchablePosReader(
519  std::string const &path,
520  CatalogControl const &control,
521  CsvControl const &dialect,
522  CsvControl const &outDialect,
523  Angle radius
524 ) :
525  _arena(),
526  _decl(-HALFPI, radians),
527  _defaultEpoch(control.epoch),
530  _radius(radius),
531  _next(0),
532  _reader(),
533  _columns(),
534  _nullRecord(),
535  _record(),
536  _buf(),
537  _writer(_buf, outDialect)
538 {
539  typedef std::vector<std::string>::const_iterator Iter;
540 
541  Log log(Log::getDefaultLog(), "lsst.ap.match");
542  log.log(Log::INFO, "Opening position table " + path);
543 
544  // create CSV reader
545  _reader.reset(new CsvReader(path, dialect, control.fieldNames.empty()));
546  if (!control.fieldNames.empty()) {
547  _reader->setFieldNames(control.fieldNames);
548  }
549  // get standard column info
550  _idCol = _reader->getIndexOf(control.idColumn);
551  if (!control.epochColumn.empty()) {
552  _epochCol = _reader->getIndexOf(control.epochColumn);
553  } else {
554  _epochCol = -1;
555  }
556  _raCol = _reader->getIndexOf(control.raColumn);
557  _declCol = _reader->getIndexOf(control.declColumn);
558  _raScale = control.raScale;
559  _declScale = control.declScale;
560  if (_idCol < 0 || _raCol < 0 || _declCol < 0) {
561  throw LSST_EXCEPT(pexExcept::RuntimeError,
562  "Position table does not contain unique id, "
563  "right ascension, or declination column(s)");
564  }
565  // compute vector of output field indexes and NULL record
566  if (!control.outputFields.empty()) {
567  _writer.endRecord();
568  _buf.str("");
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) {
573  _columns.push_back(i);
575  }
576  } else {
577  for (Iter i = control.outputFields.begin(),
578  e = control.outputFields.end(); i != e; ++i) {
579  int idx = _reader->getIndexOf(*i);
580  if (idx < 0) {
581  throw LSST_EXCEPT(pexExcept::InvalidParameterError,
582  "Position table has no column named " +
583  *i);
584  }
585  _columns.push_back(idx);
587  }
588  }
589  _nullRecord = _buf.str();
590  }
591  if (_epochCol >= 0) {
592  if (!lsst::utils::isnan(control.minEpoch) &&
593  !lsst::utils::isnan(control.maxEpoch)) {
594  _minEpoch = control.minEpoch;
595  _maxEpoch = control.maxEpoch;
596  } else {
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());
600  _scan(reader);
601  log.format(Log::INFO, "Scanned %llu records",
602  static_cast<unsigned long long>(reader.getNumRecords()));
603  }
604  }
605  log.format(Log::INFO, " - time range is [%.3f, %.3f] MJD",
607  // read first record
608  _read();
609 }
610 
611 MatchablePosReader::~MatchablePosReader() { }
612 
613 void MatchablePosReader::_read() {
614  typedef std::vector<int>::const_iterator Iter;
615 
616  if (_reader->isDone()) {
617  _next = 0;
618  return;
619  }
620  // retrieve column values
621  int64_t id = _reader->get<int64_t>(_idCol);
622  double epoch = _defaultEpoch;
623  if (_epochCol >= 0) {
624  epoch = _reader->get<double>(_epochCol);
625  }
626  Angle ra = _reader->get<double>(_raCol)*_raScale * radians;
627  Angle decl = _reader->get<double>(_declCol)*_declScale * radians;
628  // check for NULLs and illegal values
629  if (_reader->isNull(_idCol)) {
630  throw LSST_EXCEPT(pexExcept::RuntimeError,
631  "NULL unique id found in position table");
632  }
633  if (lsst::utils::isnan(ra.asRadians()) ||
634  lsst::utils::isnan(decl.asRadians()) ||
635  lsst::utils::isnan(epoch)) {
636  throw LSST_EXCEPT(pexExcept::RuntimeError,
637  "Position table contains NULL or NaN right "
638  "ascension, declination, or epoch");
639  }
640  if (decl < -HALFPI || decl > HALFPI) {
641  throw LSST_EXCEPT(pexExcept::RuntimeError,
642  "Invalid declination found in position table");
643  }
644  if (epoch < J2000_MJD - 200.0*DAYS_PER_JY ||
645  epoch > J2000_MJD + 200.0*DAYS_PER_JY) {
646  throw LSST_EXCEPT(pexExcept::RuntimeError,
647  "Position table epoch is not within 200 years of "
648  "J2000. Check your units - MJD required.");
649  }
650  // check that input file is declination sorted
651  if (decl < _decl) {
652  throw LSST_EXCEPT(pexExcept::RuntimeError,
653  "Position table is not sorted by declination");
654  }
655  // Construct ancillary column output string
656  if (!_columns.empty()) {
657  _writer.endRecord();
658  _buf.str("");
659  _record.clear();
660  for (Iter i = _columns.begin(), e = _columns.end(); i != e; ++i) {
661  _writer.appendField(_reader->get<char const *>(*i));
662  }
663  _record = _buf.str();
664  }
665  _reader->nextRecord();
666  // Create Pos object.
667  _next = new (_arena) MatchablePos(id, ra, decl, epoch, _radius, _record);
668  _decl = decl;
669 }
670 
671 void MatchablePosReader::_scan(lsst::ap::utils::CsvReader &reader) {
672  if (reader.isDone()) {
673  return;
674  }
675  _minEpoch = reader.get<double>(_epochCol);
678  throw LSST_EXCEPT(pexExcept::RuntimeError,
679  "Position table contains NULL or NaN epoch");
680  }
681  reader.nextRecord();
682  while (!reader.isDone()) {
683  double epoch = reader.get<double>(_epochCol);
684  if (lsst::utils::isnan(epoch)) {
685  throw LSST_EXCEPT(pexExcept::RuntimeError,
686  "Position table contains NULL or NaN epoch");
687  }
688  if (epoch < _minEpoch) {
689  _minEpoch = epoch;
690  } else if (epoch > _maxEpoch) {
691  _maxEpoch = epoch;
692  }
693  reader.nextRecord();
694  }
695 }
696 
697 
705 class RefReaderBase {
706 public:
707  RefReaderBase(std::string const &path,
708  CatalogControl const &control,
709  CsvControl const &dialect,
710  CsvControl const &outDialect,
711  double minMatchEpoch,
712  double maxMatchEpoch,
713  Angle parallaxThresh);
714 
715  virtual ~RefReaderBase();
716 
717  double getMinEpoch() const { return _minEpoch; }
718  double getMaxEpoch() const { return _maxEpoch; }
719  double getMinMatchEpoch() const { return _minMatchEpoch; }
720  double getMaxMatchEpoch() const { return _maxMatchEpoch; }
721  Angle getMaxParallax() const { return _maxParallax; }
722  double getMaxAngularVelocity() const { return _maxAngularVelocity; }
723  Angle getParallaxThresh() const { return _parallaxThresh; }
724  Angle getReadAhead() const { return _readAhead; }
725 
729  std::string const & getNullRecord() const {
730  return _nullRecord;
731  }
732 
736  bool haveOutputRecord() const {
737  return !_columns.empty();
738  }
739 
740 protected:
741  ReferencePosition const * _readReferencePosition();
742 
743  std::string const & getRecord() const {
744  return _record;
745  }
746 
747  Angle _decl;
749  boost::scoped_ptr<CsvReader> _reader;
750  int _numFilters;
751 
752 private:
753  void _scan(lsst::ap::utils::CsvReader &reader,
754  bool needEpochStats,
755  bool needMotionStats);
756 
757  ReferencePosition _pos;
758  double _defaultEpoch;
759  double _minEpoch;
760  double _maxEpoch;
766  int _idCol;
767  int _epochCol;
768  int _raCol;
769  int _declCol;
770  int _muRaCol;
774  double _raScale;
775  double _declScale;
776  double _muRaScale;
777  double _muDeclScale;
781  std::vector<int> _columns;
782  std::string _nullRecord;
783  std::string _record;
784  std::stringstream _buf;
786 };
787 
788 RefReaderBase::RefReaderBase(
789  std::string const &path,
790  CatalogControl const &control,
791  CsvControl const &dialect,
792  CsvControl const &outDialect,
793  double minMatchEpoch,
794  double maxMatchEpoch,
795  Angle parallaxThresh
796 ) :
797  _decl(-HALFPI, radians),
798  _readAhead(0.0, radians),
799  _reader(),
800  _numFilters(static_cast<int>(Filter::getNames().size())),
801  _pos(0, 0.0 * radians, 0.0 * radians),
802  _defaultEpoch(control.epoch),
804  _maxEpoch(_defaultEpoch),
805  _minMatchEpoch(min(minMatchEpoch, maxMatchEpoch)),
806  _maxMatchEpoch(max(minMatchEpoch, maxMatchEpoch)),
807  _maxParallax(0.0 * radians),
808  _maxAngularVelocity(0.0),
809  _parallaxThresh(parallaxThresh),
810  _columns(),
811  _nullRecord(),
812  _record(),
813  _buf(),
814  _writer(_buf, outDialect)
815 {
816  typedef std::vector<std::string>::const_iterator Iter;
817 
818  Log log(Log::getDefaultLog(), "lsst.ap.match");
819  log.log(Log::INFO, "Opening reference catalog " + path);
820 
821  // create CSV reader
822  _reader.reset(new CsvReader(path, dialect, control.fieldNames.empty()));
823  if (!control.fieldNames.empty()) {
824  _reader->setFieldNames(control.fieldNames);
825  }
826  // get standard column info
827  _idCol = _reader->getIndexOf(control.idColumn);
828  if (!control.epochColumn.empty()) {
829  _epochCol = _reader->getIndexOf(control.epochColumn);
830  } else {
831  _epochCol = -1;
832  }
833  _raCol = _reader->getIndexOf(control.raColumn);
834  _declCol = _reader->getIndexOf(control.declColumn);
835  _muRaCol = _reader->getIndexOf(control.muRaColumn);
836  _muDeclCol = _reader->getIndexOf(control.muDeclColumn);
837  _vRadialCol = _reader->getIndexOf(control.vRadialColumn);
838  _parallaxCol = _reader->getIndexOf(control.parallaxColumn);
839  _raScale = control.raScale;
840  _declScale = control.declScale;
841  _muRaScale = control.muRaScale;
842  _muDeclScale = control.muDeclScale;
843  _vRadialScale = control.vRadialScale;
844  _parallaxScale = control.parallaxScale;
845  _muRaTrueAngle = control.muRaTrueAngle;
846  if (_idCol < 0 || _raCol < 0 || _declCol < 0) {
847  throw LSST_EXCEPT(pexExcept::RuntimeError,
848  "Reference catalog doesn't contain unique id, "
849  "right ascension, or declination column(s)");
850  }
851  // compute vector of output field indexes and NULL record
852  if (!control.outputFields.empty()) {
853  _writer.endRecord();
854  _buf.str("");
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) {
859  _columns.push_back(i);
861  }
862  } else {
863  for (Iter i = control.outputFields.begin(),
864  e = control.outputFields.end(); i != e; ++i) {
865  int idx = _reader->getIndexOf(*i);
866  if (idx < 0) {
867  throw LSST_EXCEPT(pexExcept::InvalidParameterError,
868  "Reference catalog has no column "
869  "named " + *i);
870  }
871  _columns.push_back(idx);
873  }
874  }
875  _nullRecord = _buf.str();
876  }
877  if (_epochCol >= 0 ||
878  (_muRaCol >= 0 && _muDeclCol >= 0 && _parallaxCol >= 0)) {
879 
880  bool needEpochStats = false;
881  bool needMotionStats = false;
882  if (_epochCol >= 0) {
883  if (!lsst::utils::isnan(control.minEpoch) &&
884  !lsst::utils::isnan(control.maxEpoch)) {
885  _minEpoch = control.minEpoch;
886  _maxEpoch = control.maxEpoch;
887  } else {
888  needEpochStats = true;
889  }
890  }
891  if (_muRaCol >= 0 && _muDeclCol >= 0 && _parallaxCol >= 0) {
892  if (!lsst::utils::isnan(control.maxParallax) &&
893  !lsst::utils::isnan(control.maxAngularVelocity)) {
894  _maxParallax = masToRad(control.maxParallax) * radians;
895  _maxAngularVelocity = control.maxAngularVelocity*RADY_PER_MASD;
896  } else {
897  needMotionStats = true;
898  }
899  }
900 
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",
907  static_cast<unsigned long long>(reader.getNumRecords()));
908 
909  }
910  }
911  // determine read-ahead amount
912  double dtMax = max(fabs(_maxEpoch - _minMatchEpoch),
913  fabs(_maxMatchEpoch - _minEpoch));
916  log.format(Log::INFO, " - time range is [%.3f, %.3f] MJD",
917  _minEpoch, _maxEpoch);
918  log.format(Log::INFO, " - max parallax is %.3f milliarcsec",
920  log.format(Log::INFO, " - max angular velocity is %.3f milliarcsec/yr",
921  _maxAngularVelocity/RADY_PER_MASD);
922  log.format(Log::INFO, " - read-ahead is %.3f milliarcsec",
924 }
925 
926 RefReaderBase::~RefReaderBase() { }
927 
928 ReferencePosition const * RefReaderBase::_readReferencePosition() {
929  typedef std::vector<int>::const_iterator Iter;
930 
931  if (_reader->isDone()) {
932  return 0;
933  }
934  // retrieve column values
935  int64_t id = _reader->get<int64_t>(_idCol);
936  double epoch = _defaultEpoch;
937  if (_epochCol >= 0) {
938  epoch = _reader->get<double>(_epochCol);
939  }
940  Angle ra = _reader->get<double>(_raCol)*_raScale * radians;
941  Angle decl = _reader->get<double>(_declCol)*_declScale * radians;
942  // check for NULLs and illegal values
943  if (_reader->isNull(_idCol)) {
944  throw LSST_EXCEPT(pexExcept::RuntimeError,
945  "NULL unique id found in reference catalog");
946  }
947  if (lsst::utils::isnan(ra.asRadians()) ||
948  lsst::utils::isnan(decl.asRadians()) ||
949  lsst::utils::isnan(epoch)) {
950  throw LSST_EXCEPT(pexExcept::RuntimeError,
951  "Reference catalog record contains NULL/NaN right "
952  "ascension, declination, or epoch");
953  }
954  if (decl < -HALFPI || decl > HALFPI) {
955  throw LSST_EXCEPT(pexExcept::RuntimeError,
956  "Invalid declination found in reference catalog");
957  }
958  if (epoch < J2000_MJD - 200.0*DAYS_PER_JY ||
959  epoch > J2000_MJD + 200.0*DAYS_PER_JY) {
960  throw LSST_EXCEPT(pexExcept::RuntimeError,
961  "Reference catalog epoch is not within 200 years "
962  "of J2000. Check your units - MJD required.");
963  }
964  // check that input file actually is declination sorted
965  if (decl < _decl) {
966  throw LSST_EXCEPT(pexExcept::RuntimeError,
967  "Reference catalog is not sorted by declination");
968  }
969  // Construct ancillary column output string
970  if (!_columns.empty()) {
971  _writer.endRecord();
972  _buf.str("");
973  for (Iter i = _columns.begin(), e = _columns.end(); i != e; ++i) {
974  _writer.appendField(_reader->get<char const *>(*i));
975  }
976  _record = _buf.str();
977  }
978  // create reference position
979  _pos = ReferencePosition(id, ra, decl, epoch);
980  if (_muRaCol >= 0 && _muDeclCol >= 0 && _parallaxCol >= 0) {
981  // have motion parameters
982  double muRa = _reader->get<double>(_muRaCol)*_muRaScale;
983  double muDecl = _reader->get<double>(_muDeclCol)*_muDeclScale;
984  Angle parallax(_reader->get<double>(_parallaxCol)*_parallaxScale,
985  radians);
986  double vRadial = (_vRadialCol < 0) ? 0.0 :
987  _reader->get<double>(_vRadialCol)*_vRadialScale;
988  if (parallax < 0.0) {
989  throw LSST_EXCEPT(pexExcept::RuntimeError,
990  "Reference catalog contains negative parallax");
991  }
992  if (!lsst::utils::isnan(muRa) &&
993  !lsst::utils::isnan(muDecl) &&
994  !lsst::utils::isnan(vRadial) &&
995  !lsst::utils::isnan(parallax)) {
996  // motion parameters are non-null
997  _pos.setMotion(muRa, muDecl, parallax, vRadial,
998  _muRaTrueAngle, parallax > _parallaxThresh);
999  }
1000  }
1001  _pos.setTimeRange(_minMatchEpoch, _maxMatchEpoch);
1002  _decl = decl;
1003  _reader->nextRecord();
1004  return &_pos;
1005 }
1006 
1007 void RefReaderBase::_scan(lsst::ap::utils::CsvReader &reader,
1008  bool needEpochStats,
1009  bool needMotionStats)
1010 {
1011  if (reader.isDone()) {
1012  return;
1013  }
1014  if (needEpochStats) {
1015  _minEpoch = reader.get<double>(_epochCol);
1016  _maxEpoch = _minEpoch;
1017  }
1018  while (!reader.isDone()) {
1019  if (needEpochStats) {
1020  double epoch = reader.get<double>(_epochCol);
1021  if (lsst::utils::isnan(epoch)) {
1022  throw LSST_EXCEPT(pexExcept::RuntimeError,
1023  "Record contains NULL or NaN epoch");
1024  }
1025  if (epoch < _minEpoch) {
1026  _minEpoch = epoch;
1027  } else if (epoch > _maxEpoch) {
1028  _maxEpoch = epoch;
1029  }
1030  }
1031  if (needMotionStats) {
1032  Angle parallax(reader.get<double>(_parallaxCol)*_parallaxScale,
1033  radians);
1034  double muRa = reader.get<double>(_muRaCol)*_muRaScale;
1035  double muDecl = reader.get<double>(_muDeclCol)*_muDeclScale;
1036  if (!lsst::utils::isnan(parallax) &&
1037  !lsst::utils::isnan(muRa) &&
1038  !lsst::utils::isnan(muDecl)) {
1039  if (!_muRaTrueAngle) {
1040  Angle decl(reader.get<double>(_declCol)*_declScale, radians);
1041  if (decl < -HALFPI || decl > HALFPI ||
1042  lsst::utils::isnan(decl.asRadians())) {
1043  throw LSST_EXCEPT(pexExcept::RuntimeError,
1044  "Invalid declination found in "
1045  "reference catalog");
1046  }
1047  muRa *= std::cos(decl.asRadians());
1048  }
1049  if (parallax > _maxParallax) {
1050  _maxParallax = parallax;
1051  }
1052  double v = sqrt(muRa*muRa + muDecl*muDecl);
1053  if (v > _maxAngularVelocity) {
1054  _maxAngularVelocity = v;
1055  }
1056  }
1057  }
1058  reader.nextRecord();
1059  }
1060 }
1061 
1062 
1066 template <typename Ref>
1067 class RefReader : public RefReaderBase {
1068 public:
1069  RefReader(std::string const &path,
1070  CatalogControl const &control,
1071  CsvControl const &dialect,
1072  CsvControl const &outDialect,
1073  double minMatchEpoch,
1074  double maxMatchEpoch,
1075  Angle parallaxThresh);
1076 
1077  ~RefReader();
1078 
1081  bool isDone() const {
1082  return _heap.empty();
1083  }
1084 
1088  Angle peek() const {
1089  return _heap.front().first;
1090  }
1091 
1094  Ref *next() {
1095  while (_decl - peek() <= _readAhead && !_reader->isDone()) {
1096  _read();
1097  }
1098  std::pop_heap(_heap.begin(), _heap.end());
1099  Ref *r = _heap.back().second;
1100  _heap.pop_back();
1101  return r;
1102  }
1103 
1106  void destroy(Ref *r) {
1107  _arena.destroy(r);
1108  }
1109 
1110 private:
1111  void _read() {
1112  ReferencePosition const *p = _readReferencePosition();
1113  if (p != 0) {
1114  Ref *r = new (_arena) Ref(*p, getRecord(), _numFilters);
1115  _heap.push_back(std::pair<Angle, Ref *>(Angle(r->getMinCoord1()), r));
1116  std::push_heap(_heap.begin(), _heap.end());
1117  }
1118  }
1119 
1121  std::vector<std::pair<Angle, Ref *> > _heap;
1122 };
1123 
1124 template <typename Ref>
1125 RefReader<Ref>::RefReader(
1126  std::string const &path,
1127  CatalogControl const &control,
1128  CsvControl const &dialect,
1129  CsvControl const &outDialect,
1130  double minMatchEpoch,
1131  double maxMatchEpoch,
1132  Angle parallaxThresh
1133 ) :
1134  RefReaderBase(path,
1135  control,
1136  dialect,
1137  outDialect,
1138  minMatchEpoch,
1139  maxMatchEpoch,
1140  parallaxThresh)
1141 {
1142  _read();
1143 }
1144 
1145 template <typename Ref>
1146 RefReader<Ref>::~RefReader() { }
1147 
1148 
1149 // -- Matchers ----
1150 
1153 class RefPosMatcher {
1154 public:
1155  RefPosMatcher(bool outputRefExtras);
1156  ~RefPosMatcher();
1157 
1158  // Sweep event call-backs
1159  void operator()(MatchablePos *p) {
1160  _finish(p);
1161  }
1162  void operator()(MatchableRef *r) {
1163  _finish(r);
1164  }
1165  void operator()(MatchableRef *r, MatchablePos *p) {
1166  _candidateMatch(r, p);
1167  }
1168  void operator()(MatchablePos *p, MatchableRef *r) {
1169  _candidateMatch(r, p);
1170  }
1171 
1172  void match(CsvWriter &writer,
1173  RefReader<MatchableRef> &refReader,
1174  MatchablePosReader &posReader);
1175 
1176 private:
1177  void _writeMatch(RefPosMatch const *m);
1178  void _finish(MatchablePos *p);
1179  void _finish(MatchableRef *r);
1180  void _candidateMatch(MatchableRef *r, MatchablePos *p);
1181 
1182  // memory allocator for matches
1184 
1185  // data structures for matching
1188 
1189  CsvWriter *_writer;
1190  RefReader<MatchableRef> *_refReader;
1191  MatchablePosReader *_posReader;
1192 
1193  // output corrected reference object position and flags?
1195 };
1196 
1197 RefPosMatcher::RefPosMatcher(bool outputRefExtras) :
1198  _arena(),
1199  _posSweep(),
1200  _refSweep(),
1201  _writer(0),
1202  _refReader(0),
1203  _posReader(0),
1204  _outputRefExtras(outputRefExtras)
1205 { }
1206 
1207 RefPosMatcher::~RefPosMatcher() {
1208  _writer = 0;
1209  _refReader = 0;
1210  _posReader = 0;
1211 }
1212 
1215 void RefPosMatcher::match(CsvWriter &writer,
1216  RefReader<MatchableRef> &refReader,
1217  MatchablePosReader &posReader)
1218 {
1219  _writer = &writer;
1220  _refReader = &refReader;
1221  _posReader = &posReader;
1222  while (true) {
1223  if (_posReader->isDone()) {
1224  _refSweep.clear(*this);
1225  while (!_refReader->isDone()) {
1226  Angle refDecl = _refReader->peek();
1227  _posSweep.advance(refDecl.asRadians(), *this);
1228  MatchableRef *r = _refReader->next();
1229  _posSweep.search(r, *this);
1230  _finish(r);
1231  }
1232  break;
1233  } else if (_refReader->isDone()) {
1234  _posSweep.clear(*this);
1235  while (!_posReader->isDone()) {
1236  Angle posDecl = _posReader->peek();
1237  _refSweep.advance(posDecl.asRadians(), *this);
1238  MatchablePos *p = _posReader->next();
1239  _refSweep.search(p, *this);
1240  _finish(p);
1241  }
1242  break;
1243  }
1244  Angle posDecl = _posReader->peek();
1245  Angle refDecl = _refReader->peek();
1246  _refSweep.advance(posDecl.asRadians(), *this);
1247  _posSweep.advance(refDecl.asRadians(), *this);
1248  if (refDecl < posDecl) {
1249  MatchableRef *r = _refReader->next();
1250  _posSweep.search(r, *this);
1251  _refSweep.insert(r);
1252  } else {
1253  MatchablePos *p = _posReader->next();
1254  _refSweep.search(p, *this);
1255  _posSweep.insert(p);
1256  }
1257  }
1258  _refSweep.clear(*this);
1259  _posSweep.clear(*this);
1260  _writer = 0;
1261  _refReader = 0;
1262  _posReader = 0;
1263 }
1264 
1267 void RefPosMatcher::_writeMatch(RefPosMatch const *m) {
1268  MatchableRef const *r = m->getRef();
1269  MatchablePos const *p = m->getPos();
1270  _writer->appendField(r->getReferencePosition().getId());
1271  _writer->appendField(p->getId());
1272  if (_outputRefExtras) {
1274  m->getSphericalCoordinates().getLongitude().asDegrees());
1276  m->getSphericalCoordinates().getLatitude().asDegrees());
1277  }
1278  _writer->appendField(m->getAngularSeparation().asArcseconds());
1279  _writer->appendField(r->getNumMatches());
1280  _writer->appendField(p->getNumMatches());
1281  _writer->appendField(r->getClosestId() == p->getId());
1282  _writer->appendField(p->getClosestId() == r->getReferencePosition().getId());
1283  if (_outputRefExtras) {
1284  _writer->appendField(r->getReferencePosition().getFlags());
1285  }
1286  if (_refReader->haveOutputRecord()) {
1288  _writer->write(r->getRecord());
1289  }
1290  if (_posReader->haveOutputRecord()) {
1292  _writer->write(p->getRecord());
1293  }
1294  _writer->endRecord();
1295 }
1296 
1300 void RefPosMatcher::_finish(MatchablePos *p) {
1301  typedef MatchablePos::MatchVector::iterator Iter;
1302 
1303  if (p->getNumMatches() == 0) {
1304  // p has no matches - write out p and delete it immediately.
1305  _writer->appendNull();
1306  _writer->appendField(p->getId());
1307  if (_outputRefExtras) {
1308  _writer->appendNull();
1309  _writer->appendNull();
1310  }
1311  _writer->appendNull();
1312  _writer->appendNull();
1313  _writer->appendField(0);
1314  _writer->appendNull();
1315  _writer->appendNull();
1316  if (_outputRefExtras) {
1317  _writer->appendNull();
1318  }
1319  if (_refReader->haveOutputRecord()) {
1321  _writer->write(_refReader->getNullRecord());
1322  }
1323  if (_posReader->haveOutputRecord()) {
1325  _writer->write(p->getRecord());
1326  }
1327  _writer->endRecord();
1328  _posReader->destroy(p);
1329  return;
1330  }
1331  // All matches for p have been found - write out all match
1332  // pairs (r, p) where all matches for r have also been found
1333  //
1334  // Note that writing (r, p) is delayed until this late stage
1335  // because the match table output includes flags that indicate
1336  // whether r is the closest match to p (and vice versa), as well
1337  // as match counts for r and p.
1338  for (Iter i = p->getMatches().begin(), e = p->getMatches().end();
1339  i != e; ++i) {
1340  RefPosMatch *m = *i;
1341  MatchableRef *r = m->getRef();
1342  if (m->decrementRefCount() == 0) {
1343  // p had the only reference to m
1344  _writeMatch(m);
1345  // delete m
1346  _arena.destroy(m);
1347  if (r->decrementRefCount() == 0) {
1348  // all matches involving r have been written
1349  _refReader->destroy(r);
1350  }
1351  p->decrementRefCount();
1352  }
1353  }
1354  if (p->getRefCount() == 0) {
1355  _posReader->destroy(p);
1356  }
1357 }
1358 
1362 void RefPosMatcher::_finish(MatchableRef *r) {
1363  typedef MatchablePos::MatchVector::iterator Iter;
1364 
1365  if (r->getNumMatches() == 0) {
1366  // r has no matches - write out r and delete it immediately.
1367  _writer->appendField(r->getReferencePosition().getId());
1368  _writer->appendNull();
1369  if (_outputRefExtras) {
1370  _writer->appendNull();
1371  _writer->appendNull();
1372  }
1373  _writer->appendNull();
1374  _writer->appendField(0);
1375  _writer->appendNull();
1376  _writer->appendNull();
1377  _writer->appendNull();
1378  if (_outputRefExtras) {
1379  _writer->appendNull();
1380  }
1381  if (_refReader->haveOutputRecord()) {
1383  _writer->write(r->getRecord());
1384  }
1385  if (_posReader->haveOutputRecord()) {
1387  _writer->write(_posReader->getNullRecord());
1388  }
1389  _writer->endRecord();
1390  _refReader->destroy(r);
1391  return;
1392  }
1393  // All matches for r have been found - write out all match
1394  // pairs (r, p) where all matches for p have also been found
1395  //
1396  // Note that writing (r, p) is delayed until this late stage
1397  // because the match table output includes flags that indicate
1398  // whether r is the closest match to p (and vice versa), as well
1399  // as match counts for r and p.
1400  for (Iter i = r->getMatches().begin(), e = r->getMatches().end();
1401  i != e; ++i) {
1402  RefPosMatch *m = *i;
1403  MatchablePos *p = m->getPos();
1404  if (m->decrementRefCount() == 0) {
1405  // r had the only reference to m
1406  _writeMatch(m);
1407  // delete m
1408  _arena.destroy(m);
1409  if (p->decrementRefCount() == 0) {
1410  // all matches involving p have been written
1411  _posReader->destroy(p);
1412  }
1413  r->decrementRefCount();
1414  }
1415  }
1416  if (r->getRefCount() == 0) {
1417  _refReader->destroy(r);
1418  }
1419 }
1420 
1423 void RefPosMatcher::_candidateMatch(MatchableRef *r, MatchablePos *p) {
1424  Eigen::Vector3d v = r->getReferencePosition().getPosition(p->getEpoch());
1425  Angle sep = angularSeparation(v, p->getPosition());
1426  if (sep <= p->getRadius()) {
1427  // got a match
1428  RefPosMatch *m = new (_arena) RefPosMatch(r, p, v, sep);
1429  r->appendMatch(p->getId(), m);
1430  p->appendMatch(r->getReferencePosition().getId(), m);
1431  }
1432 }
1433 
1434 
1437 class RefExpMatcher {
1438 public:
1439  RefExpMatcher();
1440  ~RefExpMatcher();
1441 
1442  // Sweep event call-backs
1443  void operator()(ExposureInfo *e) { }
1444 
1445  void operator()(RefWithCov *r) {
1446  _finish(r);
1447  }
1448  void operator()(RefWithCov *r, ExposureInfo *e) {
1449  _candidateMatch(r, e);
1450  }
1451  void operator()(ExposureInfo *e, RefWithCov *r) {
1452  _candidateMatch(r, e);
1453  }
1454 
1455  void match(CsvWriter &writer,
1456  RefReader<RefWithCov> &refReader,
1457  std::vector<ExposureInfo::Ptr> &exposures);
1458 
1459 private:
1460  struct ExposureInfoComparator {
1461  bool operator()(ExposureInfo::Ptr const &e1,
1462  ExposureInfo::Ptr const &e2) const
1463  {
1464  return e1->getMinCoord1() < e2->getMinCoord1();
1465  }
1466  };
1467 
1468  void _finish(RefWithCov *r);
1469  void _candidateMatch(RefWithCov *r, ExposureInfo *e);
1470 
1471  // heap that reorders to produce declination sorted output
1472  std::vector<std::pair<Angle, RefWithCov *> > _heap;
1473 
1474  // data structures for matching
1477 
1478  CsvWriter *_writer;
1479  RefReader<RefWithCov> *_refReader;
1481 };
1482 
1483 RefExpMatcher::RefExpMatcher() :
1484  _expSweep(),
1485  _refSweep(),
1486  _writer(0),
1487  _refReader(0),
1488  _maxDecl(-HALFPI, radians)
1489 { }
1490 
1491 RefExpMatcher::~RefExpMatcher() {
1492  _writer = 0;
1493  _refReader = 0;
1494 }
1495 
1498 void RefExpMatcher::match(CsvWriter &writer,
1499  RefReader<RefWithCov> &refReader,
1500  std::vector<ExposureInfo::Ptr> &exposures)
1501 {
1502  typedef std::vector<ExposureInfo::Ptr>::const_iterator Iter;
1503 
1504  _writer = &writer;
1505  _refReader = &refReader;
1506 
1507  // sort exposures by minimum bounding box declination
1508  std::sort(exposures.begin(), exposures.end(), ExposureInfoComparator());
1509  Iter i = exposures.begin();
1510  Iter const end = exposures.end();
1511  // run the standard sweep line algorithm
1512  while (true) {
1513  if (i == end) {
1514  _refSweep.clear(*this);
1515  while (!_refReader->isDone()) {
1516  Angle refDecl = _refReader->peek();
1517  _expSweep.advance(refDecl.asRadians(), *this);
1518  RefWithCov *r = _refReader->next();
1519  _expSweep.search(r, *this);
1520  _finish(r);
1521  }
1522  break;
1523  } else if (_refReader->isDone()) {
1524  _expSweep.clear(*this);
1525  for (; i != end; ++i) {
1526  Angle expDecl((*i)->getMinCoord1(), radians);
1527  _refSweep.advance(expDecl.asRadians(), *this);
1528  ExposureInfo *info = i->get();
1529  _refSweep.search(info, *this);
1530  }
1531  break;
1532  }
1533  Angle expDecl((*i)->getMinCoord1(), radians);
1534  Angle refDecl = _refReader->peek();
1535  _refSweep.advance(expDecl.asRadians(), *this);
1536  _expSweep.advance(refDecl.asRadians(), *this);
1537  if (refDecl < expDecl) {
1538  RefWithCov *r = _refReader->next();
1539  _expSweep.search(r, *this);
1540  _refSweep.insert(r);
1541  } else {
1542  ExposureInfo *info = i->get();
1543  ++i;
1544  _refSweep.search(info, *this);
1545  _expSweep.insert(info);
1546  }
1547  }
1548  _refSweep.clear(*this);
1549  _expSweep.clear(*this);
1550  while (!_heap.empty()) {
1551  // write out any reference catalog entries remaining in the
1552  // declination min-heap.
1553  std::pop_heap(_heap.begin(), _heap.end());
1554  RefWithCov *r = _heap.back().second;
1555  _heap.pop_back();
1556  if (_refReader->haveOutputRecord()) {
1557  _writer->write(r->getRecord());
1559  }
1560  r->writeCoverage(*_writer);
1561  _writer->endRecord();
1562  _refReader->destroy(r);
1563  }
1564  _writer = 0;
1565  _refReader = 0;
1566 }
1567 
1571 void RefExpMatcher::_finish(RefWithCov *r) {
1572  if (!r->isCovered()) {
1573  _refReader->destroy(r);
1574  } else {
1575  // insert r into declination heap, and update max declination
1576  // seen so far.
1577  Angle decl = r->getReferencePosition().getSphericalCoords().getLatitude();
1578  if (decl >= _maxDecl) {
1579  _maxDecl = decl;
1580  }
1581  _heap.push_back(std::pair<Angle, RefWithCov *>(decl, r));
1582  std::push_heap(_heap.begin(), _heap.end());
1583  while (!_heap.empty() &&
1584  _maxDecl - _heap.front().first > _refReader->getReadAhead()) {
1585  std::pop_heap(_heap.begin(), _heap.end());
1586  r = _heap.back().second;
1587  _heap.pop_back();
1588  if (_refReader->haveOutputRecord()) {
1589  _writer->write(r->getRecord());
1591  }
1592  r->writeCoverage(*_writer);
1593  _writer->endRecord();
1594  _refReader->destroy(r);
1595  }
1596  }
1597 }
1598 
1599 
1602 void RefExpMatcher::_candidateMatch(RefWithCov *r, ExposureInfo *e) {
1603  double epoch = e->getEpoch();
1604  Eigen::Vector3d v = r->getReferencePosition().getPosition(
1605  epoch, e->getEarthPosition());
1606  IcrsCoord sc = cartesianToIcrs(v);
1607  lsst::afw::geom::Point2D p = e->getWcs()->skyToPixel(sc);
1608  if (p.getX() >= e->getX0() + PixelZeroPos - 0.5 &&
1609  p.getX() <= e->getX0() + e->getWidth() + PixelZeroPos - 0.5 &&
1610  p.getY() >= e->getY0() + PixelZeroPos - 0.5 &&
1611  p.getY() <= e->getY0() + e->getHeight() + PixelZeroPos - 0.5) {
1612  r->appendMatch(e);
1613  }
1614 }
1615 
1616 
1619 void checkFilters() {
1620  typedef std::vector<std::string>::const_iterator Iter;
1621 
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) {
1625  Filter f = Filter(*i, false);
1626  if (f.getId() < 0 || f.getId() >= n) {
1627  throw LSST_EXCEPT(pexExcept::RuntimeError,
1628  "Filter IDs do not have contiguous IDs starting at 0");
1629  }
1630  }
1631 }
1632 
1633 
1634 } // namespace lsst::ap::match::<anonymous>
1635 
1636 
1638  std::string const &refFile,
1639  CatalogControl const &refControl,
1640  CsvControl const &refDialect,
1641  std::string const &posFile,
1642  CatalogControl const &posControl,
1643  CsvControl const &posDialect,
1644  std::string const &outFile,
1645  CsvControl const &outDialect,
1646  lsst::afw::geom::Angle const radius,
1647  lsst::afw::geom::Angle const parallaxThresh,
1648  bool outputRefExtras,
1649  bool truncateOutFile
1650 ) {
1651  Log log(Log::getDefaultLog(), "lsst.ap.match");
1652  log.log(Log::INFO, "Matching reference catalog to position table...");
1653 
1654  // Create readers, writer and matcher
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);
1662 
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()),
1667  outFile.c_str());
1668 }
1669 
1670 
1672  std::vector<ExposureInfo::Ptr> &exposures,
1673  std::string const &refFile,
1674  CatalogControl const &refControl,
1675  CsvControl const &refDialect,
1676  std::string const &outFile,
1677  CsvControl const &outDialect,
1678  lsst::afw::geom::Angle const parallaxThresh,
1679  bool truncateOutFile
1680 ) {
1681  typedef std::vector<ExposureInfo::Ptr>::const_iterator Iter;
1682  if (exposures.empty()) {
1683  throw LSST_EXCEPT(pexExcept::InvalidParameterError,
1684  "no input exposure information");
1685  }
1686  checkFilters();
1687 
1688  Log log(Log::getDefaultLog(), "lsst.ap.match");
1689  log.log(Log::INFO, "Filtering out reference catalog entries not "
1690  "observable in any exposure...");
1691 
1692  // determine min/max epoch of exposures
1693  Iter i = exposures.begin();
1694  double minEpoch = (*i)->getEpoch();
1695  double maxEpoch = minEpoch;
1696  ++i;
1697  for (Iter e = exposures.end(); i != e; ++i) {
1698  double epoch = (*i)->getEpoch();
1699  if (epoch < minEpoch) {
1700  minEpoch = epoch;
1701  } else if (epoch > maxEpoch) {
1702  maxEpoch = epoch;
1703  }
1704  }
1705  log.format(Log::INFO, "Time range of exposures is [%.3f, %.3f] MJD",
1706  minEpoch, maxEpoch);
1707 
1708  // Create reader, writer and matcher
1709  RefReader<RefWithCov> refReader(
1710  refFile, refControl, refDialect, outDialect,
1711  minEpoch, maxEpoch, parallaxThresh);
1712  CsvWriter writer(outFile, outDialect, truncateOutFile);
1713  RefExpMatcher matcher;
1714 
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",
1718  static_cast<unsigned long long>(writer.getNumRecords()),
1719  outFile.c_str());
1720 }
1721 
1722 }}} // namespace lsst::ap::match
1723 
int _nMatches
int _numFilters
An include file to include the public header files for lsst::afw::math.
double _defaultEpoch
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
Definition: CsvControl.h:155
Angle _readAhead
Edge * next
Definition: ImageUtils.cc:91
lsst::afw::geom::Angle const angularSeparation(Eigen::Vector3d const &v1, Eigen::Vector3d const &v2)
Definition: SpatialUtils.h:83
IcrsCoord _sc
double _parallaxScale
double _muRaScale
int64_t _closestId
double _muDeclScale
Image utility functions.
void appendField(std::string const &v)
Definition: Csv.cc:281
int64_t _id
Parameters that define a Character-Separated-Value dialect.
Definition: CsvControl.h:48
int _epochCol
size_t getNumRecords() const
Definition: Csv.cc:64
Angle _alpha
AngleUnit const radians
constant with units of radians
Definition: Angle.h:91
Angle _parallaxThresh
Angle _maxParallax
int _declCol
def info
Definition: log.py:102
Angle _decl
bool _muRaTrueAngle
bool _outputRefExtras
boost::scoped_array< int > _filterCov
double _vRadialScale
MatchVector _matches
lsst::afw::coord::IcrsCoord const cartesianToIcrs(Eigen::Vector3d const &v)
Definition: SpatialUtils.h:74
CsvWriter _writer
size_t getNumRecords() const
Definition: Csv.cc:271
MatchablePosReader * _posReader
Angle _radius
lsst::afw::coord::IcrsCoord IcrsCoord
Definition: misc.h:41
int isnan(T t)
Definition: ieee.h:110
a place to record messages and descriptions of the state of processing.
Definition: Log.h:154
Class that bundles together the WCS, extents, time, and calibration information from an image (typica...
double min
Definition: attributes.cc:216
double asRadians() const
Definition: Angle.h:122
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)
int _refCount
std::string const get(int i) const
Definition: Csv.cc:147
bool _covered
Matching against simulated reference catalog positions.
def log
Definition: log.py:85
int getId() const
Definition: Filter.h:136
RefReader< MatchableRef > * _refReader
MatchablePos * _pos
int _raCol
double max
Definition: attributes.cc:218
Config parameters for catalogs involved in reference matching.
int _muRaCol
void log(int importance, const std::string &message, const lsst::daf::base::PropertySet &properties)
std::stringstream _buf
double maxAlpha(double const theta, double const centerDec)
Definition: SpatialUtil.cc:279
int _muDeclCol
double _declScale
double _maxAngularVelocity
SphericalSweep< ExposureInfo > _expSweep
int _parallaxCol
double _maxMatchEpoch
void format(int importance, const char *fmt,...)
bool isDone() const
Definition: Csv.cc:90
Angle _maxDecl
int _vRadialCol
Holds an integer identifier for an LSST filter.
Definition: Filter.h:107
SphericalSweep< MatchablePos > _posSweep
int _idCol
double _minEpoch
Eigen::Vector3d _p
int INFO
Definition: log.py:37
#define LSST_EXCEPT(type,...)
Definition: Exception.h:46
Spatial utility functions.
std::string _nullRecord
double _epoch
std::vector< std::pair< Angle, Ref * > > _heap
lsst::afw::geom::Angle Angle
Definition: misc.h:42
Arena< MatchablePos > _arena
MatchableRef * _ref
const double PixelZeroPos
FITS uses 1.0, SDSS uses 0.5, LSST uses 0.0 (http://dev.lsstcorp.org/trac/wiki/BottomLeftPixelProposa...
Definition: ImageUtils.h:42
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
Single threaded arena (pool) memory allocator.
int id
Definition: CR.cc:151
2D bounding box interface.
double _minMatchEpoch
double arcsecToRad(double x)
Definition: Angle.h:41
double radToMas(double x)
Definition: Angle.h:38
double masToRad(double x)
Definition: Angle.h:44
double _closestSep
Pointer vector that avoids memory allocation for small vectors.
void write(std::string const &v)
Definition: Csv.cc:309
MatchablePos * _next
lsst::afw::geom::Angle getLatitude() const
The main access method for the latitudinal coordinate.
Definition: Coord.h:118
Classes for CSV I/O.
std::vector< int > _columns
double _maxEpoch
Angle const maxAlpha(Angle radius, Angle centerPhi)
Definition: SpatialUtils.cc:79
boost::scoped_ptr< CsvReader > _reader
A class to handle Icrs coordinates (inherits from Coord)
Definition: Coord.h:155
double _raScale
std::string _record
Angle _angSep
Sweep line data structures useful for region-region matching.
Useful astronomical constants.
CsvControl const & getControl() const
Definition: Csv.cc:252
ReferencePosition _rp
Functions to handle coordinates.
lsst::afw::geom::Angle const clampPhi(lsst::afw::geom::Angle const a)
Definition: SpatialUtils.h:45