Alexandria  2.16
Please provide a description of the project.
CatalogConfig.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2012-2020 Euclid Science Ground Segment
3  *
4  * This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General
5  * Public License as published by the Free Software Foundation; either version 3.0 of the License, or (at your option)
6  * any later version.
7  *
8  * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
9  * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
10  * details.
11  *
12  * You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to
13  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
14  */
15 
22 #include <fstream>
23 #include <array>
24 #include <CCfits/CCfits>
26 #include "ElementsKernel/Logging.h"
28 #include "Table/AsciiReader.h"
29 #include "Table/FitsReader.h"
32 
33 namespace po = boost::program_options;
34 namespace fs = boost::filesystem;
35 
36 namespace Euclid {
37 namespace Configuration {
38 
40 
41 static const std::string INPUT_CATALOG_FILE {"input-catalog-file"};
42 static const std::string INPUT_CATALOG_FORMAT {"input-catalog-format"};
43 static const std::string SOURCE_ID_COLUMN_NAME {"source-id-column-name"};
44 static const std::string SOURCE_ID_COLUMN_INDEX {"source-id-column-index"};
45 
46 CatalogConfig::CatalogConfig(long manager_id) : Configuration(manager_id) { }
47 
49  return {{"Input catalog options", {
50  {INPUT_CATALOG_FILE.c_str(), po::value<std::string>()->required(),
51  "The file containing the input catalog"},
52  {INPUT_CATALOG_FORMAT.c_str(), po::value<std::string>()->default_value("AUTO"),
53  "The format of the input catalog (AUTO, FITS or ASCII)"},
54  {SOURCE_ID_COLUMN_NAME.c_str(), po::value<std::string>(),
55  "The name of the column representing the source ID"},
56  {SOURCE_ID_COLUMN_INDEX.c_str(), po::value<int>(),
57  "The index of the column representing the source ID"}
58  }}};
59 }
60 
62 
63  if (args.find(SOURCE_ID_COLUMN_NAME) != args.end()
64  && args.find(SOURCE_ID_COLUMN_INDEX) != args.end()) {
65  throw Elements::Exception() << "Options " << SOURCE_ID_COLUMN_NAME
66  << " and " << SOURCE_ID_COLUMN_INDEX << " are mutually exclusive";
67  }
68 
69  if (args.find(SOURCE_ID_COLUMN_INDEX) != args.end()
70  && args.at(SOURCE_ID_COLUMN_INDEX).as<int>() < 1) {
71  throw Elements::Exception() << SOURCE_ID_COLUMN_INDEX<< " must be a one-based "
72  << "index but was " << args.at(SOURCE_ID_COLUMN_INDEX).as<int>();
73  }
74 
75  if (args.find(INPUT_CATALOG_FORMAT) != args.end()
76  && args.at(INPUT_CATALOG_FORMAT).as<std::string>() != "AUTO"
77  && args.at(INPUT_CATALOG_FORMAT).as<std::string>() != "FITS"
78  && args.at(INPUT_CATALOG_FORMAT).as<std::string>() != "ASCII") {
79  throw Elements::Exception() << INPUT_CATALOG_FORMAT << "must be one of "
80  << "AUTO, FITS or ASCII, but was " << args.at(INPUT_CATALOG_FORMAT).as<std::string>();
81  }
82 }
83 
84 namespace {
85 
86 fs::path getCatalogFileFromOptions(const Configuration::UserValues& args,
87  const fs::path& base_dir) {
88  fs::path catalog_file {args.at(INPUT_CATALOG_FILE).as<std::string>()};
89  if (catalog_file.is_relative()) {
90  catalog_file = base_dir / catalog_file;
91  }
92  if (!fs::exists(catalog_file)) {
93  throw Elements::Exception() << "Input catalog file " << catalog_file << " does not exist";
94  }
95  if (fs::is_directory(catalog_file)) {
96  throw Elements::Exception() << "Input catalog file " << catalog_file << " is not a file";
97  }
98  return catalog_file;
99 }
100 
101 enum class FormatType {
102  FITS, ASCII
103 };
104 
105 FormatType autoDetectFormatType(fs::path file) {
106  logger.info() << "Auto-detecting format of file " << file;
107  FormatType result = FormatType::ASCII;
108  {
109  std::ifstream in {file.string()};
110  std::array<char, 80> first_header_array;
111  in.read(first_header_array.data(), 80);
112  in.close();
113  std::string first_header_str {first_header_array.data()};
114  if (first_header_str.compare(0, 9, "SIMPLE =") == 0) {
115  result = FormatType::FITS;
116  }
117  }
118  logger.info() << "Detected " << (result == FormatType::FITS ? "FITS" : "ASCII") << " format";
119  return result;
120 }
121 
122 FormatType getFormatTypeFromOptions(const Configuration::UserValues& args,
123  const fs::path& file) {
124  FormatType format;
125  if (args.at(INPUT_CATALOG_FORMAT).as<std::string>().compare("AUTO") == 0) {
126  format = autoDetectFormatType(file);
127  } else if (args.at(INPUT_CATALOG_FORMAT).as<std::string>().compare("FITS") == 0) {
128  format = FormatType::FITS;
129  } else {
130  format = FormatType::ASCII;
131  }
132  return format;
133 }
134 
135 std::unique_ptr<Table::TableReader> getTableReaderImpl(bool fits_format, const boost::filesystem::path& filename) {
136  if (fits_format) {
137  return make_unique<Table::FitsReader>(filename.native(), 1);
138  } else {
139  return make_unique<Table::AsciiReader>(filename.native());
140  }
141 }
142 
143 std::string getIdColumnFromOptions(const Configuration::UserValues& args,
144  const Table::ColumnInfo& column_info) {
145  std::string id_column_name = "ID";
146  if (args.find(SOURCE_ID_COLUMN_NAME) != args.end()) {
147  id_column_name = args.at(SOURCE_ID_COLUMN_NAME).as<std::string>();
148  if (column_info.find(id_column_name) == nullptr) {
149  throw Elements::Exception() << "Input catalog file does not contain the "
150  << "ID column with name " << id_column_name;
151  }
152  }
153  if (args.find(SOURCE_ID_COLUMN_INDEX) != args.end()) {
154  std::size_t index = args.at(SOURCE_ID_COLUMN_INDEX).as<int>();
155  if (index > column_info.size()) {
156  throw Elements::Exception() << SOURCE_ID_COLUMN_INDEX << " (" << index
157  << ") is out of range (" << column_info.size() << ")";
158  }
159  id_column_name = column_info.getDescription(index-1).name;
160  }
161  logger.info() << "Using ID column \"" << id_column_name << '"';
162  return id_column_name;
163 }
164 
165 } // Anonymous namespace
166 
168  m_filename = getCatalogFileFromOptions(args, m_base_dir);
169  m_fits_format = getFormatTypeFromOptions(args, m_filename) == FormatType::FITS;
170  m_column_info = std::make_shared<Table::ColumnInfo>(getTableReaderImpl(m_fits_format, m_filename)->getInfo());
171  m_id_column_name = getIdColumnFromOptions(args, *m_column_info);
172 }
173 
174 void CatalogConfig::setBaseDir(const fs::path& base_dir) {
176  throw Elements::Exception() << "setBaseDir() call to initialized CatalogConfig";
177  }
178  m_base_dir = base_dir;
179 }
180 
182  if (getCurrentState() >= State::FINAL) {
183  throw Elements::Exception() << "addAttributeHandler() call to finalized CatalogConfig";
184  }
185  m_attribute_handlers.push_back(handler);
186 }
187 
189  if (getCurrentState() < State::FINAL) {
190  throw Elements::Exception() << "getTableReader() call to not finalized CatalogConfig";
191  }
192  return getTableReaderImpl(m_fits_format, m_filename);
193 }
194 
197  throw Elements::Exception() << "getColumnInfo() call to uninitialized CatalogConfig";
198  }
199  return m_column_info;
200 }
201 
203  return m_id_column_name;
204 }
205 
206 namespace {
207 
208 class ConverterImpl {
209 
210 public:
211 
212  ConverterImpl(std::shared_ptr<Table::ColumnInfo> column_info, const std::string& id_column_name,
214  : m_converter(column_info, id_column_name, std::move(attribute_handlers)) {
215  }
216 
217  SourceCatalog::Catalog operator()(const Table::Table& table) {
218  return m_converter.createCatalog(table);
219  }
220 
221 private:
222 
223  SourceCatalog::CatalogFromTable m_converter;
224 
225 };
226 
227 } // Anonymous namespace
228 
230  if (getCurrentState() < State::FINAL) {
231  throw Elements::Exception() << "getTableToCatalogConverter() call to not finalized CatalogConfig";
232  }
233  return ConverterImpl{m_column_info, m_id_column_name, m_attribute_handlers};
234 }
235 
236 
238  if (getCurrentState() < State::FINAL) {
239  throw Elements::Exception() << "getAsTable() call to not finalized CatalogConfig";
240  }
241  logger.info() << "Reading table from file " << m_filename;
242  return getTableReader()->read();
243 }
244 
246  if (getCurrentState() < State::FINAL) {
247  throw Elements::Exception() << "getCatalog() call to not finalized CatalogConfig";
248  }
249  auto table = readAsTable();
250  auto converter = getTableToCatalogConverter();
251  return converter(table);
252 }
253 
254 const boost::filesystem::path& CatalogConfig::getFilename() const {
256  throw Elements::Exception() << "getFilename() call to not finalized CatalogConfig";
257  }
258  return m_filename;
259 }
260 
261 } // Configuration namespace
262 } // Euclid namespace
263 
264 
265 
static const std::string SOURCE_ID_COLUMN_NAME
STL class.
SourceCatalog::Catalog readAsCatalog() const
Returns the Catalog object.
std::shared_ptr< Table::ColumnInfo > getColumnInfo() const
Superclass of all configuration classes.
Definition: Configuration.h:45
void addAttributeHandler(std::shared_ptr< SourceCatalog::AttributeFromRow > handler)
Adds an attribute handler which will be used for adding attributes at the catalog objects.
static const std::string SOURCE_ID_COLUMN_INDEX
static const std::string INPUT_CATALOG_FILE
STL namespace.
void info(const std::string &logMessage)
T end(T... args)
State & getCurrentState()
Returns the current state of the configuration.
STL class.
STL class.
boost::filesystem::path m_base_dir
const boost::filesystem::path & getFilename() const
Returns the filename of the input catalog.
T at(T... args)
void initialize(const UserValues &args) override
Initializes the CatalogConfig instance.
T data(T... args)
std::vector< std::shared_ptr< SourceCatalog::AttributeFromRow > > m_attribute_handlers
Table::Table readAsTable() const
Returns the catalog as a Table::Table object.
The postInitialize() method has been called.
void preInitialize(const UserValues &args) override
Checks that all the options are valid. See the exceptions thrown for a detailed list of the checks.
static Elements::Logging logger
std::unique_ptr< Table::TableReader > getTableReader() const
void setBaseDir(const boost::filesystem::path &base_dir)
Sets the directory used when resolving relative paths.
TableToCatalogConverter getTableToCatalogConverter() const
Represents a table.
Definition: Table.h:49
T find(T... args)
STL class.
STL class.
boost::filesystem::path m_filename
T c_str(T... args)
CatalogConfig(long manager_id)
Constructs a new CatalogConfig object.
STL class.
std::shared_ptr< Table::ColumnInfo > m_column_info
Catalog contains a container of sources.
Definition: Catalog.h:47
SourceCatalog::CatalogFromTable m_converter
std::map< std::string, OptionDescriptionList > getProgramOptions() override
Returns the program options defined by the CatalogConfig.
static Logging getLogger(const std::string &name="")
The initialize() method has been called.
static const std::string INPUT_CATALOG_FORMAT
STL class.