Alexandria  2.25.0
SDC-CH common library for the Euclid project
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
CatalogConfig.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2012-2021 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 
25 #include "ElementsKernel/Logging.h"
27 #include "Table/AsciiReader.h"
28 #include "Table/FitsReader.h"
29 #include <CCfits/CCfits>
30 #include <array>
31 #include <fstream>
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 {
50  {"Input catalog options",
51  {{INPUT_CATALOG_FILE.c_str(), po::value<std::string>()->required(), "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>(), "The name of the column representing the source ID"},
55  {SOURCE_ID_COLUMN_INDEX.c_str(), po::value<int>(), "The index of the column representing the source ID"}}}};
56 }
57 
59 
60  if (args.find(SOURCE_ID_COLUMN_NAME) != args.end() && args.find(SOURCE_ID_COLUMN_INDEX) != args.end()) {
61  throw Elements::Exception() << "Options " << SOURCE_ID_COLUMN_NAME << " and " << SOURCE_ID_COLUMN_INDEX
62  << " are mutually exclusive";
63  }
64 
65  if (args.find(SOURCE_ID_COLUMN_INDEX) != args.end() && args.at(SOURCE_ID_COLUMN_INDEX).as<int>() < 1) {
66  throw Elements::Exception() << SOURCE_ID_COLUMN_INDEX << " must be a one-based "
67  << "index but was " << args.at(SOURCE_ID_COLUMN_INDEX).as<int>();
68  }
69 
70  if (args.find(INPUT_CATALOG_FORMAT) != args.end() && args.at(INPUT_CATALOG_FORMAT).as<std::string>() != "AUTO" &&
71  args.at(INPUT_CATALOG_FORMAT).as<std::string>() != "FITS" &&
72  args.at(INPUT_CATALOG_FORMAT).as<std::string>() != "ASCII") {
73  throw Elements::Exception() << INPUT_CATALOG_FORMAT << "must be one of "
74  << "AUTO, FITS or ASCII, but was " << args.at(INPUT_CATALOG_FORMAT).as<std::string>();
75  }
76 }
77 
78 namespace {
79 
80 fs::path getCatalogFileFromOptions(const Configuration::UserValues& args, const fs::path& base_dir) {
81  fs::path catalog_file{args.at(INPUT_CATALOG_FILE).as<std::string>()};
82  if (catalog_file.is_relative()) {
83  catalog_file = base_dir / catalog_file;
84  }
85  if (!fs::exists(catalog_file)) {
86  throw Elements::Exception() << "Input catalog file " << catalog_file << " does not exist";
87  }
88  if (fs::is_directory(catalog_file)) {
89  throw Elements::Exception() << "Input catalog file " << catalog_file << " is not a file";
90  }
91  return catalog_file;
92 }
93 
94 enum class FormatType { FITS, ASCII };
95 
96 FormatType autoDetectFormatType(fs::path file) {
97  logger.info() << "Auto-detecting format of file " << file;
98  FormatType result = FormatType::ASCII;
99  {
100  std::ifstream in{file.string()};
101  std::array<char, 81> first_header_array;
102  in.read(first_header_array.data(), 80);
103  first_header_array.back() = '\0';
104  in.close();
105  std::string first_header_str{first_header_array.data()};
106  if (first_header_str.compare(0, 9, "SIMPLE =") == 0) {
107  result = FormatType::FITS;
108  }
109  }
110  logger.info() << "Detected " << (result == FormatType::FITS ? "FITS" : "ASCII") << " format";
111  return result;
112 }
113 
114 FormatType getFormatTypeFromOptions(const Configuration::UserValues& args, const fs::path& file) {
115  FormatType format;
116  if (args.at(INPUT_CATALOG_FORMAT).as<std::string>().compare("AUTO") == 0) {
117  format = autoDetectFormatType(file);
118  } else if (args.at(INPUT_CATALOG_FORMAT).as<std::string>().compare("FITS") == 0) {
119  format = FormatType::FITS;
120  } else {
121  format = FormatType::ASCII;
122  }
123  return format;
124 }
125 
126 std::unique_ptr<Table::TableReader> getTableReaderImpl(bool fits_format, const boost::filesystem::path& filename) {
127  if (fits_format) {
128  return Euclid::make_unique<Table::FitsReader>(filename.native(), 1);
129  } else {
130  return Euclid::make_unique<Table::AsciiReader>(filename.native());
131  }
132 }
133 
134 std::string getIdColumnFromOptions(const Configuration::UserValues& args, const Table::ColumnInfo& column_info) {
135  std::string id_column_name = "ID";
136  if (args.find(SOURCE_ID_COLUMN_NAME) != args.end()) {
137  id_column_name = args.at(SOURCE_ID_COLUMN_NAME).as<std::string>();
138  if (column_info.find(id_column_name) == nullptr) {
139  throw Elements::Exception() << "Input catalog file does not contain the "
140  << "ID column with name " << id_column_name;
141  }
142  }
143  if (args.find(SOURCE_ID_COLUMN_INDEX) != args.end()) {
144  std::size_t index = args.at(SOURCE_ID_COLUMN_INDEX).as<int>();
145  if (index > column_info.size()) {
146  throw Elements::Exception() << SOURCE_ID_COLUMN_INDEX << " (" << index << ") is out of range ("
147  << column_info.size() << ")";
148  }
149  id_column_name = column_info.getDescription(index - 1).name;
150  }
151  logger.info() << "Using ID column \"" << id_column_name << '"';
152  return id_column_name;
153 }
154 
155 } // Anonymous namespace
156 
158  m_filename = getCatalogFileFromOptions(args, m_base_dir);
159  m_fits_format = getFormatTypeFromOptions(args, m_filename) == FormatType::FITS;
160  m_column_info = std::make_shared<Table::ColumnInfo>(getTableReaderImpl(m_fits_format, m_filename)->getInfo());
161  m_id_column_name = getIdColumnFromOptions(args, *m_column_info);
162 }
163 
164 void CatalogConfig::setBaseDir(const fs::path& base_dir) {
166  throw Elements::Exception() << "setBaseDir() call to initialized CatalogConfig";
167  }
168  m_base_dir = base_dir;
169 }
170 
172  if (getCurrentState() >= State::FINAL) {
173  throw Elements::Exception() << "addAttributeHandler() call to finalized CatalogConfig";
174  }
176 }
177 
179  if (getCurrentState() < State::FINAL) {
180  throw Elements::Exception() << "getTableReader() call to not finalized CatalogConfig";
181  }
182  return getTableReaderImpl(m_fits_format, m_filename);
183 }
184 
187  throw Elements::Exception() << "getColumnInfo() call to uninitialized CatalogConfig";
188  }
189  return m_column_info;
190 }
191 
193  return m_id_column_name;
194 }
195 
196 namespace {
197 
198 class ConverterImpl {
199 
200 public:
201  ConverterImpl(std::shared_ptr<Table::ColumnInfo> column_info, const std::string& id_column_name,
203  : m_converter(column_info, id_column_name, std::move(attribute_handlers)) {}
204 
205  SourceCatalog::Catalog operator()(const Table::Table& table) {
206  return m_converter.createCatalog(table);
207  }
208 
209 private:
210  SourceCatalog::CatalogFromTable m_converter;
211 };
212 
213 } // Anonymous namespace
214 
216  if (getCurrentState() < State::FINAL) {
217  throw Elements::Exception() << "getTableToCatalogConverter() call to not finalized CatalogConfig";
218  }
219  return ConverterImpl{m_column_info, m_id_column_name, m_attribute_handlers};
220 }
221 
223  if (getCurrentState() < State::FINAL) {
224  throw Elements::Exception() << "getAsTable() call to not finalized CatalogConfig";
225  }
226  logger.info() << "Reading table from file " << m_filename;
227  return getTableReader()->read();
228 }
229 
231  if (getCurrentState() < State::FINAL) {
232  throw Elements::Exception() << "getCatalog() call to not finalized CatalogConfig";
233  }
234  auto table = readAsTable();
235  auto converter = getTableToCatalogConverter();
236  return converter(table);
237 }
238 
239 const boost::filesystem::path& CatalogConfig::getFilename() const {
241  throw Elements::Exception() << "getFilename() call to not finalized CatalogConfig";
242  }
243  return m_filename;
244 }
245 
246 } // namespace Configuration
247 } // namespace Euclid
static const std::string SOURCE_ID_COLUMN_NAME
SourceCatalog::Catalog readAsCatalog() const
Returns the Catalog object.
TableToCatalogConverter getTableToCatalogConverter() 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...
std::shared_ptr< Table::ColumnInfo > getColumnInfo() const
static const std::string SOURCE_ID_COLUMN_INDEX
static const std::string INPUT_CATALOG_FILE
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
T at(T...args)
void initialize(const UserValues &args) override
Initializes the CatalogConfig instance.
T push_back(T...args)
T data(T...args)
std::vector< std::shared_ptr< SourceCatalog::AttributeFromRow > > m_attribute_handlers
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
void setBaseDir(const boost::filesystem::path &base_dir)
Sets the directory used when resolving relative paths.
Represents a table.
Definition: Table.h:49
T find(T...args)
Table::Table readAsTable() const
Returns the catalog as a Table::Table object.
STL class.
STL class.
const boost::filesystem::path & getFilename() const
Returns the filename of the input catalog.
std::unique_ptr< Table::TableReader > getTableReader() const
boost::filesystem::path m_filename
T c_str(T...args)
CatalogConfig(long manager_id)
Constructs a new CatalogConfig object.
T back(T...args)
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.