SourceXtractorPlusPlus  0.19
SourceXtractor++, the next generation SExtractor
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
AssocModeConfig.cpp
Go to the documentation of this file.
1 
18 #include <map>
19 #include <boost/algorithm/string.hpp>
20 
21 #include <CCfits/CCfits>
22 
23 #include "ElementsKernel/Logging.h"
24 
25 #include "Table/AsciiReader.h"
26 #include "Table/FitsReader.h"
27 #include "Table/CastVisitor.h"
28 
32 
34 
36 
37 using namespace Euclid::Configuration;
38 namespace po = boost::program_options;
39 
40 namespace SourceXtractor {
41 
43 
44 enum class AssocCoordType {
45  PIXEL,
46  WORLD
47 };
48 
49 static const std::string ASSOC_CATALOG { "assoc-catalog" };
50 static const std::string ASSOC_MODE { "assoc-mode" };
51 static const std::string ASSOC_RADIUS { "assoc-radius" };
52 static const std::string ASSOC_FILTER { "assoc-filter" };
53 static const std::string ASSOC_COPY { "assoc-copy" };
54 static const std::string ASSOC_COLUMNS { "assoc-columns" };
55 static const std::string ASSOC_COORD_TYPE { "assoc-coord-type" };
56 
57 namespace {
58 
60  std::make_pair("", AssocModeConfig::AssocMode::UNKNOWN),
61  std::make_pair("FIRST", AssocModeConfig::AssocMode::FIRST),
62  std::make_pair("NEAREST", AssocModeConfig::AssocMode::NEAREST),
63  std::make_pair("MEAN", AssocModeConfig::AssocMode::MEAN),
64  std::make_pair("MAG_MEAN", AssocModeConfig::AssocMode::MAG_MEAN),
65  std::make_pair("SUM", AssocModeConfig::AssocMode::SUM),
66  std::make_pair("MAG_SUM", AssocModeConfig::AssocMode::MAG_SUM),
67  std::make_pair("MIN", AssocModeConfig::AssocMode::MIN),
68  std::make_pair("MAX", AssocModeConfig::AssocMode::MAX)
69 };
70 
72  std::make_pair("ALL", AssocModeConfig::AssocFilter::ALL),
73  std::make_pair("MATCHED", AssocModeConfig::AssocFilter::MATCHED),
74  std::make_pair("UNMATCHED", AssocModeConfig::AssocFilter::UNMATCHED)
75 };
76 
77 const std::map<std::string, AssocCoordType> assoc_coord_type_table {
78  std::make_pair("PIXEL", AssocCoordType::PIXEL),
79  std::make_pair("WORLD", AssocCoordType::WORLD)
80 };
81 
82 std::vector<int> parseColumnList(const std::string& arg) {
83  if (arg.size() > 0) {
84  try {
86  boost::split(parts, arg, boost::is_any_of(","));
87 
88  std::vector<int> column_list;
89  for (auto& part : parts) {
90  // the input is a 1-based index, the internal index is 0-based
91  column_list.emplace_back(boost::lexical_cast<int>(part)-1);
92  }
93  return column_list;
94  } catch(...) {
95  throw Elements::Exception() << "Can't parse column list to int: " << arg;
96  }
97  } else {
98  return {};
99  }
100 }
101 
102 }
103 
104 AssocModeConfig::AssocModeConfig(long manager_id) : Configuration(manager_id), m_assoc_mode(AssocMode::UNKNOWN), m_assoc_radius(0.) {
105  declareDependency<DetectionImageConfig>();
106  declareDependency<PartitionStepConfig>();
108 }
109 
111  return { {"Assoc config", {
112  {ASSOC_CATALOG.c_str(), po::value<std::string>(),
113  "Assoc catalog file"},
114  {ASSOC_COLUMNS.c_str(), po::value<std::string>()->default_value("1,2"),
115  "Assoc columns to specify x/ra,y/dec[,weight] (the index of the first column is 1)"},
116  {ASSOC_MODE.c_str(), po::value<std::string>()->default_value("NEAREST"),
117  "Assoc mode [FIRST, NEAREST, MEAN, MAG_MEAN, SUM, MAG_SUM, MIN, MAX]"},
118  {ASSOC_RADIUS.c_str(), po::value<double>()->default_value(2.0),
119  "Assoc radius (Always in pixels of the detection image)"},
120  {ASSOC_FILTER.c_str(), po::value<std::string>()->default_value("ALL"),
121  "Assoc catalog filter setting: ALL, MATCHED, UNMATCHED"},
122  {ASSOC_COPY.c_str(), po::value<std::string>()->default_value(""),
123  "List of columns indices in the assoc catalog to copy on match (the index of the first column is 1). "},
124  {ASSOC_COORD_TYPE.c_str(), po::value<std::string>()->default_value("PIXEL"),
125  "Assoc coordinates type: PIXEL, WORLD"},
126  }}};
127 }
128 
129 void AssocModeConfig::initialize(const UserValues& args) {
130  readConfig(args);
131  readCatalogs(args);
132 }
133 
134 void AssocModeConfig::readConfig(const UserValues& args) {
135  auto filter = boost::to_upper_copy(args.at(ASSOC_FILTER).as<std::string>());
136  if (assoc_filter_table.find(filter) != assoc_filter_table.end()) {
137  auto assoc_filter = assoc_filter_table.at(filter);
138  if (assoc_filter == AssocFilter::MATCHED) {
139  getDependency<PartitionStepConfig>().addPartitionStepCreator(
141  return std::make_shared<AssocModePartitionStep>(true);
142  }
143  );
144  } else if (assoc_filter == AssocFilter::UNMATCHED) {
145  getDependency<PartitionStepConfig>().addPartitionStepCreator(
147  return std::make_shared<AssocModePartitionStep>(false);
148  }
149  );
150  }
151  } else {
152  throw Elements::Exception() << "Invalid assoc filter: " << filter;
153  }
154 
155  if (args.find(ASSOC_MODE) != args.end()) {
156  auto assoc_mode = boost::to_upper_copy(args.at(ASSOC_MODE).as<std::string>());
157  if (assoc_mode_table.find(assoc_mode) != assoc_mode_table.end()) {
158  m_assoc_mode = assoc_mode_table.at(assoc_mode);
159  } else {
160  throw Elements::Exception() << "Invalid association mode: " << assoc_mode;
161  }
162  }
163 
164  m_assoc_radius = args.at(ASSOC_RADIUS).as<double>();
165 }
166 
167 void AssocModeConfig::readCatalogs(const UserValues& args) {
168  auto columns = parseColumnList(args.at(ASSOC_COLUMNS).as<std::string>());
169  if (columns.size() < 2) {
170  throw Elements::Exception() << "At least 2 columns must be specified for x,y coordinates in the assoc catalog";
171  }
172  if (columns.size() > 3) {
173  throw Elements::Exception() << "Maximum 3 columns for x, y and weight must be specified in the assoc catalog";
174  }
175 
176  m_columns_idx = parseColumnList(args.at(ASSOC_COPY).as<std::string>());
177 
178  AssocCoordType assoc_coord_type = AssocCoordType::PIXEL;
179  if (args.find(ASSOC_COORD_TYPE) != args.end()) {
180  auto assoc_coord_type_str = boost::to_upper_copy(args.at(ASSOC_COORD_TYPE).as<std::string>());
181  if (assoc_coord_type_table.find(assoc_coord_type_str) != assoc_coord_type_table.end()) {
182  assoc_coord_type = assoc_coord_type_table.at(assoc_coord_type_str);
183  } else {
184  throw Elements::Exception() << "Invalid association coordinate type: " << assoc_coord_type_str;
185  }
186  }
187 
188  if (args.find(ASSOC_CATALOG) != args.end()) {
189  auto filename = args.at(ASSOC_CATALOG).as<std::string>();
190  try {
192  try {
193  reader = std::make_shared<Euclid::Table::FitsReader>(filename);
194  } catch (...) {
195  // If FITS not successful try reading as ascii
196  reader = std::make_shared<Euclid::Table::AsciiReader>(filename);
197  }
198  auto table = reader->read();
199 
200  for (size_t i = 0; i < getDependency<DetectionImageConfig>().getExtensionsNb(); i++) {
201  if (assoc_coord_type == AssocCoordType::WORLD) {
202  auto coordinate_system = getDependency<DetectionImageConfig>().getCoordinateSystem(i);
203  m_catalogs.emplace_back(readTable(table, columns, m_columns_idx, coordinate_system));
204  } else {
205  m_catalogs.emplace_back(readTable(table, columns, m_columns_idx, nullptr));
206  }
207  }
208  } catch (const std::exception& e) {
209  throw Elements::Exception() << "Can't either open or read assoc catalog: " << filename << " (" << e.what() << ")";
210  } catch(...) {
211  throw Elements::Exception() << "Can't either open or read assoc catalog: " << filename;
212  }
213  }
214 
215  if (assoc_coord_type == AssocCoordType::PIXEL && getDependency<DetectionImageConfig>().getExtensionsNb() > 1) {
216  logger.warn() <<
217  "Using Assoc catalog matching in pixel coordinates with multiple detection images";
218  }
219 }
220 
222  const Euclid::Table::Table& table, const std::vector<int>& columns,
223  const std::vector<int>& copy_columns, std::shared_ptr<CoordinateSystem> coordinate_system) {
225 
227  for (auto& row : table) {
228  // our internal pixel coordinates are zero-based
229 
230  ImageCoordinate coord;
231  if (coordinate_system != nullptr) {
232  auto world_coord = WorldCoordinate{
233  boost::apply_visitor(CastVisitor<double>{}, row[columns.at(0)]),
234  boost::apply_visitor(CastVisitor<double>{}, row[columns.at(1)]),
235  };
236 
237  coord = coordinate_system->worldToImage(world_coord);
238  } else {
239  coord = ImageCoordinate{
240  boost::apply_visitor(CastVisitor<double>{}, row[columns.at(0)]) - 1.0,
241  boost::apply_visitor(CastVisitor<double>{}, row[columns.at(1)]) - 1.0,
242  };
243  }
244  catalog.emplace_back(CatalogEntry { coord, 1.0, {} });
245  if (columns.size() == 3 && columns.at(2) >= 0) {
246  catalog.back().weight = boost::get<double>(row[columns.at(2)]);
247  }
248  for (auto column : copy_columns) {
249  if (column >= static_cast<int>(row.size())) {
250  throw Elements::Exception() << "Column index " << column << " is out of bounds";
251  }
252  if (row[column].type() == typeid(int)) {
253  catalog.back().assoc_columns.emplace_back(boost::get<int>(row[column]));
254  } else if (row[column].type() == typeid(double)) {
255  catalog.back().assoc_columns.emplace_back(boost::get<double>(row[column]));
256  } else if (row[column].type() == typeid(int64_t)) {
257  catalog.back().assoc_columns.emplace_back(boost::get<int64_t>(row[column]));
258  } else if (row[column].type() == typeid(SeFloat)) {
259  catalog.back().assoc_columns.emplace_back(boost::get<SeFloat>(row[column]));
260  } else{
261  throw Elements::Exception() << "Wrong type in assoc column (must be a numeric type)";
262  }
263  }
264  }
265  return catalog;
266 }
267 
268 
269 }
270 
std::vector< CatalogEntry > readTable(const Euclid::Table::Table &table, const std::vector< int > &columns, const std::vector< int > &copy_columns, std::shared_ptr< CoordinateSystem > coordinate_system)
static const std::string ASSOC_CATALOG
static auto logger
Definition: WCS.cpp:41
void readConfig(const UserValues &args)
static const std::string ASSOC_MODE
ELEMENTS_API auto split(Args &&...args) -> decltype(splitPath(std::forward< Args >(args)...))
static ConfigManager & getInstance(long id)
SeFloat32 SeFloat
Definition: Types.h:32
STL class.
STL class.
static const std::string ASSOC_RADIUS
T at(T...args)
static const std::string ASSOC_FILTER
constexpr double e
T what(T...args)
std::map< std::string, OptionDescriptionList > getProgramOptions() override
std::vector< std::vector< CatalogEntry > > m_catalogs
string filename
Definition: conf.py:65
static Elements::Logging logger
T make_pair(T...args)
STL class.
void initialize(const UserValues &args) override
T size(T...args)
static const std::string ASSOC_COLUMNS
void readCatalogs(const UserValues &args)
T c_str(T...args)
T back(T...args)
std::vector< int > m_columns_idx
static Logging getLogger(const std::string &name="")
static const std::string ASSOC_COORD_TYPE
static const std::string ASSOC_COPY
T emplace_back(T...args)