SourceXtractorPlusPlus 0.21
SourceXtractor++, the next generation SExtractor
Loading...
Searching...
No Matches
AssocModeConfig.cpp
Go to the documentation of this file.
1
18#include <map>
19#include <boost/algorithm/string.hpp>
20#include <fstream>
21
22#include <CCfits/CCfits>
23
24#include "ElementsKernel/Logging.h"
25
26#include "Table/AsciiReader.h"
27#include "Table/FitsReader.h"
28#include "Table/CastVisitor.h"
29
33
35
37
38using namespace Euclid::Configuration;
39namespace po = boost::program_options;
40
41namespace SourceXtractor {
42
44
45static const std::string ASSOC_CATALOG { "assoc-catalog" };
46static const std::string ASSOC_MODE { "assoc-mode" };
47static const std::string ASSOC_RADIUS { "assoc-radius" };
48static const std::string ASSOC_FILTER { "assoc-filter" };
49static const std::string ASSOC_COPY { "assoc-copy" };
50static const std::string ASSOC_COLUMNS { "assoc-columns" };
51static const std::string ASSOC_COORD_TYPE { "assoc-coord-type" };
52static const std::string ASSOC_SOURCE_SIZES { "assoc-source-sizes" };
53static const std::string ASSOC_DEFAULT_PIXEL_SIZE { "assoc-default-pixel-size" };
54static const std::string ASSOC_GROUP_ID { "assoc-group-id" };
55static const std::string ASSOC_CONFIG { "assoc-config" };
56static const std::string ASSOC_TEST { "assoc-test" };
57
58namespace {
59
70};
71
76};
77
81};
82
83std::vector<int> parseColumnList(const std::string& arg) {
84 if (arg.size() > 0) {
85 try {
87 boost::split(parts, arg, boost::is_any_of(","));
88
89 std::vector<int> column_list;
90 for (auto& part : parts) {
91 // the input is a 1-based index, the internal index is 0-based
92 column_list.emplace_back(boost::lexical_cast<int>(part)-1);
93 }
94 return column_list;
95 } catch(...) {
96 throw Elements::Exception() << "Can't parse column list to int: " << arg;
97 }
98 } else {
99 return {};
100 }
101}
102
103}
104
106 m_assoc_radius(0.), m_default_pixel_size(10), m_pixel_size_column(-1), m_group_id_column(-1) {
110}
111
113 return { {"Assoc config", {
114 {ASSOC_CATALOG.c_str(), po::value<std::string>(),
115 "Assoc catalog file"},
116 {ASSOC_COLUMNS.c_str(), po::value<std::string>()->default_value("1,2"),
117 "Assoc columns to specify x/ra,y/dec[,weight] (the index of the first column is 1)"},
118 {ASSOC_MODE.c_str(), po::value<std::string>()->default_value("NEAREST"),
119 "Assoc mode [FIRST, NEAREST, MEAN, MAG_MEAN, SUM, MAG_SUM, MIN, MAX]"},
120 {ASSOC_RADIUS.c_str(), po::value<double>()->default_value(2.0),
121 "Assoc radius (Always in pixels of the detection image)"},
122 {ASSOC_FILTER.c_str(), po::value<std::string>()->default_value("ALL"),
123 "Assoc catalog filter setting: ALL, MATCHED, UNMATCHED"},
124 {ASSOC_COPY.c_str(), po::value<std::string>()->default_value(""),
125 "List of columns indices in the assoc catalog to copy on match (the index of the first column is 1). "},
126 {ASSOC_COORD_TYPE.c_str(), po::value<std::string>()->default_value("PIXEL"),
127 "Assoc coordinates type: PIXEL, WORLD"},
128 {ASSOC_SOURCE_SIZES.c_str(), po::value<int>()->default_value(-1),
129 "Column containing the source sizes (in reference frame pixels)"},
130 {ASSOC_DEFAULT_PIXEL_SIZE.c_str(), po::value<double>()->default_value(5.0),
131 "Default source size (in reference frame pixels)"},
132 {ASSOC_GROUP_ID.c_str(), po::value<int>()->default_value(-1),
133 "Column containing the group id"},
134 {ASSOC_CONFIG.c_str(), po::value<std::string>(),
135 "Text file containing the assoc columns configuration"},
136 {ASSOC_TEST.c_str(), po::bool_switch(),
137 "Prints the assoc configuration and quits"},
138 }}};
139}
140
142 // read configuration from command line arguments
144
145 // read columns from columns config file
146 if (args.find(ASSOC_CONFIG) != args.end()) {
149 } else {
151 }
152
153 // sanity check that the configuration is coherent
154 checkConfig();
155
156 // read the catalogs
157 if (m_filename != "") {
159 }
160
161 if (args.at(ASSOC_TEST).as<bool>()) {
162 printConfig();
163 throw Elements::Exception() << "Exiting by user request";
164 }
165}
166
168 auto filter = boost::to_upper_copy(args.at(ASSOC_FILTER).as<std::string>());
169 if (assoc_filter_table.find(filter) != assoc_filter_table.end()) {
172 getDependency<PartitionStepConfig>().addPartitionStepCreator(
175 }
176 );
177 } else if (assoc_filter == AssocFilter::UNMATCHED) {
178 getDependency<PartitionStepConfig>().addPartitionStepCreator(
181 }
182 );
183 }
184 } else {
185 throw Elements::Exception() << "Invalid assoc filter: " << filter;
186 }
187
188 if (args.find(ASSOC_MODE) != args.end()) {
189 auto assoc_mode = boost::to_upper_copy(args.at(ASSOC_MODE).as<std::string>());
190 if (assoc_mode_table.find(assoc_mode) != assoc_mode_table.end()) {
192 } else {
193 throw Elements::Exception() << "Invalid association mode: " << assoc_mode;
194 }
195 }
196
197 m_assoc_radius = args.at(ASSOC_RADIUS).as<double>();
199
200 if (args.find(ASSOC_CATALOG) != args.end()) {
202 }
203}
204
208
209 m_pixel_size_column = args.at(ASSOC_SOURCE_SIZES).as<int>() - 1; // config uses 1 as first column
210 m_group_id_column = args.at(ASSOC_GROUP_ID).as<int>() - 1; // config uses 1 as first column
211
213}
214
217
220
225
226 if (m_assoc_columns.find("ra") != m_assoc_columns.end() ||
228 throw Elements::Exception() << "Use either X/Y or RA/DEC coordinates in assoc config file but not both";
229 }
230 } else if (m_assoc_columns.find("ra") != m_assoc_columns.end() &&
232
234
238 m_assoc_columns.erase("dec");
239
240 if (m_assoc_columns.find("x") != m_assoc_columns.end() ||
242 throw Elements::Exception() << "Use either X/Y or RA/DEC coordinates in assoc config file but not both";
243 }
244 } else {
245 throw Elements::Exception() << "Missing X/Y or RA/DEC coordinates in assoc config file";
246 }
247
248 if (m_assoc_columns.find("weight") != m_assoc_columns.end()) {
250 m_assoc_columns.erase("weight");
251 }
252
253 if (m_assoc_columns.find("pixel_size") != m_assoc_columns.end()) {
254 m_pixel_size_column = m_assoc_columns.at("pixel_size");
255 m_assoc_columns.erase("pixel_size");
256 }
257
258 if (m_assoc_columns.find("group_id") != m_assoc_columns.end()) {
260 m_assoc_columns.erase("group_id");
261 }
262
263 for (auto& column_info : m_assoc_columns) {
266 }
267}
268
270 if (m_columns.size() < 2) {
271 throw Elements::Exception() << "At least 2 columns must be specified for x,y coordinates in the assoc catalog";
272 }
273 if (m_columns.size() > 3) {
274 throw Elements::Exception() << "Maximum 3 columns for x, y and weight must be specified in the assoc catalog";
275 }
276
278 logger.warn() <<
279 "Using Assoc catalog matching in pixel coordinates with multiple detection images";
280 }
281
283 throw Elements::Exception() << "Using Assoc catalog matching in pixel coordinates without a detection images";
284 }
285
286}
287
300
303 try {
305 try {
307 } catch (...) {
308 // If FITS not successful try reading as ascii
310 }
311 auto table = reader->read();
312
313 size_t exts_nb = getDependency<DetectionImageConfig>().getExtensionsNb();
314 if (exts_nb == 0) {
315 // No detection image
316 m_catalogs.emplace_back(readTable(table, columns, m_columns_idx, true));
317 } else {
318 for (size_t i = 0; i < exts_nb; i++) {
319 auto coordinate_system = getDependency<DetectionImageConfig>().getCoordinateSystem(i);
321 m_catalogs.emplace_back(readTable(table, columns, m_columns_idx, true, coordinate_system));
322 } else {
323 m_catalogs.emplace_back(readTable(table, columns, m_columns_idx, false, coordinate_system));
324 }
325 }
326 }
327 } catch (const std::exception& e) {
328 throw Elements::Exception() << "Can't either open or read assoc catalog: " << filename << " (" << e.what() << ")";
329 } catch(...) {
330 throw Elements::Exception() << "Can't either open or read assoc catalog: " << filename;
331 }
332}
333
335 const Euclid::Table::Table& table, const std::vector<int>& columns,
338
340 for (auto& row : table) {
341
342 ImageCoordinate coord;
343 WorldCoordinate world_coord;
344 if (use_world) {
345 world_coord = WorldCoordinate {
346 boost::apply_visitor(CastVisitor<double>{}, row[columns.at(0)]),
347 boost::apply_visitor(CastVisitor<double>{}, row[columns.at(1)]),
348 };
349 if (coordinate_system != nullptr) {
350 coord = coordinate_system->worldToImage(world_coord);
351 }
352 } else {
353 coord = ImageCoordinate {
354 // our internal pixel coordinates are zero-based
355 boost::apply_visitor(CastVisitor<double>{}, row[columns.at(0)]) - 1.0,
356 boost::apply_visitor(CastVisitor<double>{}, row[columns.at(1)]) - 1.0,
357 };
358 if (coordinate_system != nullptr) {
359 world_coord = coordinate_system->imageToWorld(coord);
360 }
361 }
362 catalog.emplace_back(CatalogEntry { coord, world_coord, 1.0, {}, 1.0, 0 });
363 if (columns.size() == 3 && columns.at(2) >= 0) {
364 catalog.back().weight = boost::apply_visitor(CastVisitor<double>{}, row[columns.at(2)]);
365 }
366 for (auto column : copy_columns) {
367 if (column >= static_cast<int>(row.size())) {
368 throw Elements::Exception() << "Column index " << column << " is out of bounds";
369 }
370 if (row[column].type() == typeid(int)) {
371 catalog.back().assoc_columns.emplace_back(boost::get<int>(row[column]));
372 } else if (row[column].type() == typeid(double)) {
373 catalog.back().assoc_columns.emplace_back(boost::get<double>(row[column]));
374 } else if (row[column].type() == typeid(int64_t)) {
375 catalog.back().assoc_columns.emplace_back(boost::get<int64_t>(row[column]));
376 } else if (row[column].type() == typeid(SeFloat)) {
377 catalog.back().assoc_columns.emplace_back(boost::get<SeFloat>(row[column]));
378 } else {
379 throw Elements::Exception() << "Wrong type in assoc column (must be a numeric type)";
380 }
381 }
382
383 if (m_group_id_column >= 0) {
384 catalog.back().group_id = boost::apply_visitor(CastVisitor<int64_t>{}, row[m_group_id_column]);
385 }
386
387 if (m_pixel_size_column >= 0) {
388 catalog.back().source_radius_pixels = boost::apply_visitor(CastVisitor<double>{}, row[m_pixel_size_column]);
389 } else {
390 catalog.back().source_radius_pixels = m_default_pixel_size;
391 }
392 }
393 return catalog;
394}
395
398
400 "x", "y", "ra", "dec", "weight", "group_id", "source_radius_pixel"
401 };
402
403 std::ifstream config_file(filename);
404 if (!config_file.is_open()) {
405 throw Elements::Exception() << "Can't either open or read assoc config file: " << filename;
406 }
407
409 int current_column_nb = 1; // column indices start at 1
410 while (std::getline(config_file, line)) {
411 boost::trim(line);
412 // Skip lines starting with '#'
413 if (line.empty() || line[0] == '#') {
414 continue;
415 }
416
417 std::string name;
418 int number;
419 // Find the position of the '=' character
420 auto equal_sign_pos = line.find('=');
421 if (equal_sign_pos != std::string::npos) {
422 name = line.substr(0, equal_sign_pos);
425 iss >> number;
426 number--;
428 } else {
429 name = line;
431 }
432 boost::trim(name);
433
434 if (std::find(reserved_names.begin(), reserved_names.end(), boost::to_lower_copy(name)) != reserved_names.end()) {
435 boost::to_lower(name);
436 }
437
438
439 // Store the parsed information into the vector
440 columns[name] = number;
441 }
442
443 config_file.close();
444 return columns;
445}
446
448 std::cout << "Assoc catalog configuration" << std::endl;
449
450 auto& catalog = m_catalogs.at(0);
451
453 std::cout << "X" << "\t";
454 std::cout << "Y" << "\t";
455 } else {
456 std::cout << "RA" << "\t";
457 std::cout << "DEC" << "\t";
458 }
459 if (m_columns.size() >= 3) {
460 std::cout << "WEIGHT" << "\t";
461 }
462 if (m_pixel_size_column >= 0) {
463 std::cout << "PIXEL_SIZE" << "\t";
464 }
465 if (m_group_id_column >= 0) {
466 std::cout << "GROUP_ID" << "\t";
467 }
468
469 for (const auto& name : m_custom_column_names) {
470 std::cout << name << "\t";
471 }
473
474 int lines = 0;
475 for (const auto& entry : catalog) {
477 std::cout << entry.coord.m_x << "\t";
478 std::cout << entry.coord.m_y << "\t";
479 } else {
480 std::cout << entry.world_coord.m_alpha << "\t";
481 std::cout << entry.world_coord.m_delta << "\t";
482 }
483 if (m_columns.size() >= 3) {
484 std::cout << entry.weight << "\t";
485 }
486 if (m_pixel_size_column >= 0) {
487 std::cout << entry.source_radius_pixels << "\t";
488 }
489 if (m_group_id_column >= 0) {
490 std::cout << entry.group_id << "\t";
491 }
492 for (auto& column : entry.assoc_columns) {
493 std::cout << column << "\t";
494 }
496
497 if (++lines >= 10) {
498 break;
499 }
500 }
501}
502
503
504}
T at(T... args)
static Logging getLogger(const std::string &name="")
void warn(const std::string &logMessage)
static ConfigManager & getInstance(long id)
std::map< std::string, OptionDescriptionList > getProgramOptions() override
std::vector< CatalogEntry > readTable(const Euclid::Table::Table &table, const std::vector< int > &columns, const std::vector< int > &copy_columns, bool use_world, std::shared_ptr< CoordinateSystem > coordinate_system=nullptr)
void readConfigFromFile(const std::string &filename)
void readConfigFromParams(const UserValues &args)
void initialize(const UserValues &args) override
std::map< std::string, unsigned int > parseConfigFile(const std::string &filename)
void readCommonConfig(const UserValues &args)
std::vector< std::vector< CatalogEntry > > m_catalogs
std::map< std::string, unsigned int > m_assoc_columns
void readCatalogs(const std::string &filename, const std::vector< int > &columns, AssocCoordType assoc_coord_type)
AssocCoordType getCoordinateType(const UserValues &args) const
std::vector< std::string > m_custom_column_names
T end(T... args)
T endl(T... args)
T erase(T... args)
T find(T... args)
T getline(T... args)
T make_pair(T... args)
static Elements::Logging logger
static const std::string ASSOC_COORD_TYPE
static const std::string ASSOC_GROUP_ID
static const std::string ASSOC_COPY
static const std::string ASSOC_CONFIG
static const std::string ASSOC_SOURCE_SIZES
static const std::string ASSOC_FILTER
static const std::string ASSOC_CATALOG
static const std::string ASSOC_MODE
static const std::string ASSOC_RADIUS
static const std::string ASSOC_TEST
static const std::string ASSOC_DEFAULT_PIXEL_SIZE
static const std::string ASSOC_COLUMNS
T push_back(T... args)
T size(T... args)