LSSTApplications  8.0.0.0+107,8.0.0.1+13,9.1+18,9.2,master-g084aeec0a4,master-g0aced2eed8+6,master-g15627eb03c,master-g28afc54ef9,master-g3391ba5ea0,master-g3d0fb8ae5f,master-g4432ae2e89+36,master-g5c3c32f3ec+17,master-g60f1e072bb+1,master-g6a3ac32d1b,master-g76a88a4307+1,master-g7bce1f4e06+57,master-g8ff4092549+31,master-g98e65bf68e,master-ga6b77976b1+53,master-gae20e2b580+3,master-gb584cd3397+53,master-gc5448b162b+1,master-gc54cf9771d,master-gc69578ece6+1,master-gcbf758c456+22,master-gcec1da163f+63,master-gcf15f11bcc,master-gd167108223,master-gf44c96c709
LSSTDataManagementBasePackage
ExposureInfo.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 
30 
31 #include <algorithm>
32 #include <string>
33 
34 #include "boost/algorithm/string/trim.hpp"
35 #include "boost/regex.hpp"
36 
37 #include "lsst/pex/exceptions.h"
38 #include "lsst/pex/logging/Log.h"
39 #include "lsst/daf/base/DateTime.h"
40 #include "lsst/afw/image/Filter.h"
42 
43 #include "lsst/ap/utils/Csv.h"
45 
46 using std::max;
47 
48 using boost::algorithm::trim_copy;
49 
50 using lsst::pex::exceptions::InvalidParameterError;
51 using lsst::pex::exceptions::RuntimeError;
57 
63 
64 
65 namespace lsst { namespace ap { namespace match {
66 
67 // -- ExposureInfo implementation ----
68 
69 std::string const ExposureInfo::DEF_ID_KEY("Computed_ccdExposureId");
70 
72  if (metadata->exists("FILTER")) {
73  _filter = Filter(trim_copy(metadata->getAsString("FILTER")), false);
74  }
75  // get image extents
76  _extent.setX(metadata->getAsInt("NAXIS1"));
77  _extent.setY(metadata->getAsInt("NAXIS2"));
78  if (metadata->exists("LTV1")) {
79  _xy0.setX(-metadata->getAsInt("LTV1"));
80  }
81  if (metadata->exists("LTV2")) {
82  _xy0.setY(-metadata->getAsInt("LTV2"));
83  }
84  // get image exposure time and mid-point
85  if (metadata->exists("TIME-MID")) {
86  DateTime mid(metadata->getAsString("TIME-MID"));
87  _epoch = mid.get(DateTime::MJD, DateTime::TAI);
88  }
89  if (metadata->exists("EXPTIME")) {
90  _expTime = metadata->getAsDouble("EXPTIME");
91  }
92  // get image flux
93  if (metadata->exists("FLUXMAG0")) {
94  if (metadata->exists("FLUXMAG0ERR")) {
95  _fluxMag0Sigma = metadata->getAsDouble("FLUXMAG0ERR");
96  if (_fluxMag0Sigma < 0.0) {
97  throw LSST_EXCEPT(InvalidParameterError,
98  "negative magnitude zero point error");
99  }
100  } else {
101  _fluxMag0Sigma = 0.0;
102  }
103  _fluxMag0 = metadata->getAsDouble("FLUXMAG0");
104  if (_fluxMag0 <= 0.0) {
105  throw LSST_EXCEPT(InvalidParameterError,
106  "magnitude zero point is negative or zero");
107  }
108  _canCalibrateFlux = true;
109  }
110  // compute image center and corners
111  Eigen::Vector3d c = _pixToSky(_xy0.getX() + 0.5*_extent.getX() + PixelZeroPos,
112  _xy0.getY() + 0.5*_extent.getY() + PixelZeroPos);
113  Eigen::Vector3d llc = _pixToSky(_xy0.getX() + PixelZeroPos - 0.5,
114  _xy0.getY() + PixelZeroPos - 0.5);
115  Eigen::Vector3d ulc = _pixToSky(_xy0.getX() + PixelZeroPos - 0.5,
116  _xy0.getY() + _extent.getY() - 0.5 + PixelZeroPos);
117  Eigen::Vector3d lrc = _pixToSky(_xy0.getX() + _extent.getX() - 0.5 + PixelZeroPos,
118  _xy0.getY() + PixelZeroPos - 0.5);
119  Eigen::Vector3d urc = _pixToSky(_xy0.getX() + _extent.getX() - 0.5 + PixelZeroPos,
120  _xy0.getY() + _extent.getY() - 0.5 + PixelZeroPos);
121  // compute bounding box from bounding circle
123  _radius = angularSeparation(c, llc);
124  _radius = max(_radius, angularSeparation(c, ulc));
125  _radius = max(_radius, angularSeparation(c, lrc));
126  _radius = max(_radius, angularSeparation(c, urc));
127  _alpha = maxAlpha(_radius, _center.getLatitude());
128 }
129 
132  std::string const &idKey
133 ) :
134  _center(),
135  _earthPos(),
136  _id(metadata->getAsInt64(idKey)),
137  _epoch(std::numeric_limits<double>::quiet_NaN()),
138  _expTime(std::numeric_limits<double>::quiet_NaN()),
139  _fluxMag0(std::numeric_limits<double>::quiet_NaN()),
140  _fluxMag0Sigma(std::numeric_limits<double>::quiet_NaN()),
141  _extent(),
142  _xy0(0, 0),
143  _wcs(lsst::afw::image::makeWcs(metadata, false)),
144  _filter(),
145  _canCalibrateFlux(false),
146  _epValid(false)
147 {
148  _init(metadata);
149 }
150 
153  int64_t id
154 ) :
155  _center(),
156  _earthPos(),
157  _id(id),
158  _epoch(std::numeric_limits<double>::quiet_NaN()),
159  _expTime(std::numeric_limits<double>::quiet_NaN()),
160  _fluxMag0(std::numeric_limits<double>::quiet_NaN()),
161  _fluxMag0Sigma(std::numeric_limits<double>::quiet_NaN()),
162  _extent(),
163  _xy0(0, 0),
164  _wcs(lsst::afw::image::makeWcs(metadata, false)),
165  _filter(),
166  _canCalibrateFlux(false),
167  _epValid(false)
168 {
169  _init(metadata);
170 }
171 
173 
174 double ExposureInfo::calibrateFlux(double flux, double fluxScale) const {
175  if (fluxScale <= 0.0) {
176  throw LSST_EXCEPT(InvalidParameterError, "flux "
177  "scaling factor is <= 0");
178  }
179  if (!canCalibrateFlux()) {
180  throw LSST_EXCEPT(RuntimeError, "Cannot calibrate "
181  "flux without fluxMag0");
182  }
183  return fluxScale*flux/_fluxMag0;
184 }
185 
186 std::pair<double, double> const ExposureInfo::calibrateFlux(
187  double flux,
188  double fluxSigma,
189  double fluxScale
190 ) const {
191  if (fluxScale <= 0.0) {
192  throw LSST_EXCEPT(InvalidParameterError, "flux "
193  "scaling factor is <= 0");
194  }
195  if (fluxSigma <= 0.0) {
196  throw LSST_EXCEPT(InvalidParameterError, "flux error is <= 0");
197  }
198  if (!canCalibrateFlux()) {
199  throw LSST_EXCEPT(RuntimeError, "Cannot calibrate "
200  "flux without fluxMag0");
201  }
202  // Use delta method to estimate the variance oF flux/fluxMag0.
203  double f02 = _fluxMag0*_fluxMag0;
204  double f0Var = _fluxMag0Sigma*_fluxMag0Sigma;
205  double f2 = flux*flux;
206  double fVar = fluxSigma*fluxSigma;
207  double var = (fVar*f02 + f0Var*f2)/(f02*f02);
208  return std::make_pair(fluxScale*flux/_fluxMag0, fluxScale*fluxScale*var);
209 }
210 
212  return static_cast<double>(_center.getLongitude() - _alpha);
213 }
214 
216  return static_cast<double>(_center.getLongitude() + _alpha);
217 }
218 
220  return static_cast<double>(_center.getLatitude() - _radius);
221 }
222 
224  return static_cast<double>(_center.getLatitude() + _radius);
225 }
226 
227 Eigen::Vector3d const ExposureInfo::_pixToSky(double x, double y) const {
228  return _wcs->pixelToSky(x, y)->toIcrs().getVector().asEigen();
229 }
230 
231 
232 // -- ExposureInfoMap implementation ----
233 
235 
237 
239  if (!info) {
240  throw LSST_EXCEPT(InvalidParameterError, "Cannot insert a "
241  "null ExposureInfo into an ExposureInfoMap");
242  }
243  int64_t id = info->getId();
244  if (contains(id)) {
245  throw LSST_EXCEPT(InvalidParameterError, "ExposureInfoMap "
246  "already contains an exposure with the "
247  "specified id");
248  }
249  _map.insert(std::pair<int64_t, ExposureInfo::Ptr>(id, info));
250 }
251 
253  _map.clear();
254 }
255 
256 bool ExposureInfoMap::erase(int64_t id) {
257  return _map.erase(id) != 0u;
258 }
259 
260 
261 namespace {
262 
263 int const INT_T = 0;
264 int const DOUBLE_T = 1;
265 int const STRING_T = 2;
266 
267 std::tr1::unordered_map<std::string, int> const & fitsKeyMap() {
268  static char const * const I_KEYS[] = {
269  "NAXIS1", "NAXIS2",
270  "LTV1", "LTV2",
271  "A_ORDER", "B_ORDER",
272  "AP_ORDER", "BP_ORDER"
273  };
274  static char const * const D_KEYS[] = {
275  "EQUINOX", "EXPTIME",
276  "FLUXMAG0", "FLUXMAG0ERR",
277  "CRPIX1", "CRPIX2",
278  "CRVAL1", "CRVAL2",
279  "CDELT1", "CDELT2",
280  "CD1_1", "CD1_2", "CD2_1", "CD2_2",
281  "PC1_1", "PC1_2", "PC2_1", "PC2_2"
282  };
283  static char const * const S_KEYS[] = {
284  "RADESYS", "TIME-MID", "FILTER",
285  "CUNIT1", "CUNIT2",
286  "CTYPE1", "CTYPE2"
287  };
288  static std::tr1::unordered_map<std::string, int> map;
289  if (map.size() == 0) {
290  // build key to type mapping for the FITS cards we care about
291  for (size_t i = 0; i < sizeof(I_KEYS)/sizeof(char const *); ++i) {
292  map.insert(std::make_pair(std::string(I_KEYS[i]), INT_T));
293  }
294  for (size_t i = 0; i < sizeof(D_KEYS)/sizeof(char const *); ++i) {
295  map.insert(std::make_pair(std::string(D_KEYS[i]), DOUBLE_T));
296  }
297  for (size_t i = 0; i < sizeof(S_KEYS)/sizeof(char const *); ++i) {
298  map.insert(std::make_pair(std::string(S_KEYS[i]), STRING_T));
299  }
300  }
301  return map;
302 }
303 
304 } // namespace lsst::ap::match::<anonymous>
305 
306 
308  std::vector<ExposureInfo::Ptr> & exposures,
309  std::string const & csvFile,
310  CsvControl const & control,
311  std::string const & idColumn
312 ) {
313  typedef std::tr1::unordered_map<std::string, int> FkMap;
314  typedef FkMap::const_iterator FkIter;
315 
316  static boost::regex const sipRegex("^[AB]P?_[0-9]+_[0-9]+$");
317 
318  Log log(Log::getDefaultLog(), "lsst.ap.match");
319  log.log(Log::INFO, "Reading exposure metadata from " + csvFile);
320 
321  FkMap const & fkMap = fitsKeyMap();
322  // open CSV file and get column indexes
323  CsvReader reader(csvFile, control, true);
324  int const idCol = reader.getIndexOf(idColumn);
325  int const keyCol = reader.getIndexOf("metadataKey");
326  int const intCol = reader.getIndexOf("intValue");
327  int const doubleCol = reader.getIndexOf("doubleValue");
328  int const stringCol = reader.getIndexOf("stringValue");
329  if (idCol == -1) {
330  throw LSST_EXCEPT(RuntimeError, "Exposure metadata table "
331  "has no column named " + idColumn);
332  }
333  if (keyCol == -1) {
334  throw LSST_EXCEPT(RuntimeError, "Exposure metadata table "
335  "has no column named metadataKey");
336  }
337  if (intCol == -1) {
338  throw LSST_EXCEPT(RuntimeError, "Exposure metadata table "
339  "has no column named intValue");
340  }
341  if (doubleCol == -1) {
342  throw LSST_EXCEPT(RuntimeError, "Exposure metadata table "
343  "has no column named doubleValue");
344  }
345  if (stringCol == -1) {
346  throw LSST_EXCEPT(RuntimeError, "Exposure metadata table "
347  "has no column named stringValue");
348  }
349  PropertySet::Ptr ps;
350  int64_t lastId = reader.get<int64_t>(idCol) - 1;
351  for (; !reader.isDone(); reader.nextRecord()) {
352  int64_t id = reader.get<int64_t>(idCol);
353  if (id != lastId) {
354  if (ps) {
355  exposures.push_back(ExposureInfo::Ptr(
356  new ExposureInfo(ps, lastId)));
357  }
358  ps.reset(new PropertySet());
359  lastId = id;
360  }
361  std::string const key = reader.get(keyCol);
362  FkIter k = fkMap.find(key);
363  if (k != fkMap.end()) {
364  if (k->second == INT_T) { // integer-valued key
365  ps->set(key, reader.get<int>(intCol));
366  } else if (k->second == DOUBLE_T) { // double-valued key
367  ps->set(key, reader.get<double>(doubleCol));
368  } else { // string-valued key
369  ps->set(key, reader.get(stringCol));
370  }
371  } else if (boost::regex_search(key.begin(), key.end(), sipRegex)) {
372  // key is a SIP coefficient
373  ps->set(key, reader.get<double>(doubleCol));
374  }
375  }
376  if (ps) {
377  exposures.push_back(ExposureInfo::Ptr(
378  new ExposureInfo(ps, lastId)));
379  }
380 }
381 
382 }}} // namespace lsst::ap::match
383 
IcrsCoord _center
Definition: attributes.cc:151
virtual double getMaxCoord1() const
ExposureInfo(lsst::daf::base::PropertySet::Ptr props, std::string const &idKey=DEF_ID_KEY)
static std::string const DEF_ID_KEY
Definition: ExposureInfo.h:66
lsst::afw::geom::Angle const angularSeparation(Eigen::Vector3d const &v1, Eigen::Vector3d const &v2)
Definition: SpatialUtils.h:83
Class for handling dates/times, including MJD, UTC, and TAI.
Definition: DateTime.h:58
int64_t _id
lsst::afw::geom::Angle _radius
Definition: ExposureInfo.h:160
lsst::afw::image::Wcs::Ptr _wcs
Definition: ExposureInfo.h:170
Parameters that define a Character-Separated-Value dialect.
Definition: CsvControl.h:48
int y
lsst::afw::geom::Angle getLongitude() const
The main access method for the longitudinal coordinate.
Definition: Coord.h:112
boost::shared_ptr< PropertySet > Ptr
Definition: PropertySet.h:90
Image utility functions.
lsst::afw::coord::IcrsCoord const cartesianToIcrs(Eigen::Vector3d const &v)
Definition: SpatialUtils.h:74
virtual double getMinCoord0() const
lsst::afw::image::Filter _filter
Definition: ExposureInfo.h:171
void log(int importance, const std::string &message, const lsst::daf::base::PropertySet &properties)
lsst::afw::geom::Point2I _xy0
Definition: ExposureInfo.h:169
void insert(ExposureInfo::Ptr info)
boost::shared_ptr< ExposureInfo > Ptr
Definition: ExposureInfo.h:63
a place to record messages and descriptions of the state of processing.
Definition: Log.h:154
table::Key< table::Array< Kernel::Pixel > > image
Definition: FixedKernel.cc:117
definition of the DualLog class
bool contains(int64_t id) const
Definition: ExposureInfo.h:190
lsst::daf::base::PropertySet PropertySet
Definition: Wcs.cc:57
std::string const get(int i) const
Definition: Csv.cc:147
Wcs::Ptr makeWcs(boost::shared_ptr< lsst::daf::base::PropertySet > const &fitsMetadata, bool stripMetadata=false)
Definition: makeWcs.cc:39
double max
Definition: attributes.cc:218
Class that bundles together the WCS, extents, time, and calibration information from an image (typica...
Include files required for standard LSST Exception handling.
lsst::afw::geom::Angle _alpha
Definition: ExposureInfo.h:161
int x
double maxAlpha(double const theta, double const centerDec)
Definition: SpatialUtil.cc:279
void readExposureInfos(std::vector< ExposureInfo::Ptr > &exposures, std::string const &csvFile, CsvControl const &control, std::string const &idColumn)
bool isDone() const
Definition: Csv.cc:90
virtual double getMaxCoord0() const
Holds an integer identifier for an LSST filter.
Definition: Filter.h:107
lsst::afw::coord::IcrsCoord _center
Definition: ExposureInfo.h:159
Interface for DateTime class.
#define LSST_EXCEPT(type,...)
Definition: Exception.h:46
Spatial utility functions.
double _epoch
int id
Definition: CR.cc:151
lsst::afw::geom::Extent2I _extent
Definition: ExposureInfo.h:168
double calibrateFlux(double flux, double fluxScale) const
virtual double getMinCoord1() const
lsst::afw::geom::Angle getLatitude() const
The main access method for the latitudinal coordinate.
Definition: Coord.h:119
geom::Point2I & _xy0
Definition: fits_io_mpl.h:78
Eigen::Vector3d const _pixToSky(double x, double y) const
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
int getIndexOf(std::string const &name) const
Definition: Csv.cc:79
bool canCalibrateFlux() const
Is there enough information to calibrate fluxes?
Definition: ExposureInfo.h:116
Classes for CSV I/O.
void _init(lsst::daf::base::PropertySet::Ptr props)
Definition: ExposureInfo.cc:71
Angle const maxAlpha(Angle radius, Angle centerPhi)
Definition: SpatialUtils.cc:79
Class encapsulating an identifier for an LSST filter.