SourceXtractorPlusPlus 1.0.1
SourceXtractor++, the next generation SExtractor
Loading...
Searching...
No Matches
AssocModeConfig.cpp
Go to the documentation of this file.
1
17
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_SOURCE_WIDTHS { "assoc-source-widths" };
54static const std::string ASSOC_SOURCE_HEIGHTS { "assoc-source-heights" };
55static const std::string ASSOC_DEFAULT_PIXEL_SIZE { "assoc-default-pixel-size" };
56static const std::string ASSOC_GROUP_ID { "assoc-group-id" };
57static const std::string ASSOC_CONFIG { "assoc-config" };
58static const std::string ASSOC_TEST { "assoc-test" };
59
60namespace {
61
72};
73
78};
79
83};
84
85std::vector<int> parseColumnList(const std::string& arg) {
86 if (arg.size() > 0) {
87 try {
89 boost::split(parts, arg, boost::is_any_of(","));
90
91 std::vector<int> column_list;
92 for (auto& part : parts) {
93 // the input is a 1-based index, the internal index is 0-based
94 column_list.emplace_back(boost::lexical_cast<int>(part)-1);
95 }
96 return column_list;
97 } catch(...) {
98 throw Elements::Exception() << "Can't parse column list to int: " << arg;
99 }
100 } else {
101 return {};
102 }
103}
104
105}
106
116
118 return { {"Assoc config", {
119 {ASSOC_CATALOG.c_str(), po::value<std::string>(),
120 "Assoc catalog file"},
121 {ASSOC_COLUMNS.c_str(), po::value<std::string>()->default_value("1,2"),
122 "Assoc columns to specify x/ra,y/dec[,weight] (the index of the first column is 1)"},
123 {ASSOC_MODE.c_str(), po::value<std::string>()->default_value("NEAREST"),
124 "Assoc mode [FIRST, NEAREST, MEAN, MAG_MEAN, SUM, MAG_SUM, MIN, MAX]"},
125 {ASSOC_RADIUS.c_str(), po::value<double>()->default_value(2.0),
126 "Assoc radius (Always in pixels of the detection image)"},
127 {ASSOC_FILTER.c_str(), po::value<std::string>()->default_value("ALL"),
128 "Assoc catalog filter setting: ALL, MATCHED, UNMATCHED"},
129 {ASSOC_COPY.c_str(), po::value<std::string>()->default_value(""),
130 "List of columns indices in the assoc catalog to copy on match (the index of the first column is 1). "},
131 {ASSOC_COORD_TYPE.c_str(), po::value<std::string>()->default_value("PIXEL"),
132 "Assoc coordinates type: PIXEL, WORLD"},
133 {ASSOC_SOURCE_SIZES.c_str(), po::value<int>()->default_value(-1),
134 "Column containing the source sizes (in reference frame pixels)"},
135 {ASSOC_SOURCE_WIDTHS.c_str(), po::value<int>()->default_value(-1),
136 "Column containing the source widths (in reference frame pixels)"},
137 {ASSOC_SOURCE_HEIGHTS.c_str(), po::value<int>()->default_value(-1),
138 "Column containing the source heights (in reference frame pixels)"},
139 {ASSOC_DEFAULT_PIXEL_SIZE.c_str(), po::value<double>()->default_value(5.0),
140 "Default source size (in reference frame pixels)"},
141 {ASSOC_GROUP_ID.c_str(), po::value<int>()->default_value(-1),
142 "Column containing the group id"},
143 {ASSOC_CONFIG.c_str(), po::value<std::string>(),
144 "Text file containing the assoc columns configuration"},
145 {ASSOC_TEST.c_str(), po::bool_switch(),
146 "Prints the assoc configuration and quits"},
147 }}};
148}
149
151 // read configuration from command line arguments
152 readCommonConfig(args);
153
154 // read columns from columns config file
155 if (args.find(ASSOC_CONFIG) != args.end()) {
156 auto assoc_config_filename = args.at(ASSOC_CONFIG).as<std::string>();
157 readConfigFromFile(assoc_config_filename);
158 } else {
160 }
161
162 // sanity check that the configuration is coherent
163
164 // read the catalogs
165 if (m_filename != "") {
166 checkConfig();
168
169 if (args.at(ASSOC_TEST).as<bool>()) {
170 printConfig();
171 throw Elements::Exception() << "Exiting by user request";
172 }
173 }
174}
175
177 auto filter = boost::to_upper_copy(args.at(ASSOC_FILTER).as<std::string>());
178 if (assoc_filter_table.find(filter) != assoc_filter_table.end()) {
179 auto assoc_filter = assoc_filter_table.at(filter);
180 if (assoc_filter == AssocFilter::MATCHED) {
181 getDependency<PartitionStepConfig>().addPartitionStepCreator(
184 }
185 );
186 } else if (assoc_filter == AssocFilter::UNMATCHED) {
187 getDependency<PartitionStepConfig>().addPartitionStepCreator(
190 }
191 );
192 }
193 } else {
194 throw Elements::Exception() << "Invalid assoc filter: " << filter;
195 }
196
197 if (args.find(ASSOC_MODE) != args.end()) {
198 auto assoc_mode = boost::to_upper_copy(args.at(ASSOC_MODE).as<std::string>());
199 if (assoc_mode_table.find(assoc_mode) != assoc_mode_table.end()) {
200 m_assoc_mode = assoc_mode_table.at(assoc_mode);
201 } else {
202 throw Elements::Exception() << "Invalid association mode: " << assoc_mode;
203 }
204 }
205
206 m_assoc_radius = args.at(ASSOC_RADIUS).as<double>();
208
209 if (args.find(ASSOC_CATALOG) != args.end()) {
211 }
212}
213
215 m_columns = parseColumnList(args.at(ASSOC_COLUMNS).as<std::string>());
216 m_columns_idx = parseColumnList(args.at(ASSOC_COPY).as<std::string>());
217
218 m_pixel_width_column = args.at(ASSOC_SOURCE_SIZES).as<int>() - 1; // config uses 1 as first column
219 m_pixel_height_column = args.at(ASSOC_SOURCE_SIZES).as<int>() - 1; // config uses 1 as first column
220
221 if (args.find(ASSOC_SOURCE_WIDTHS) != args.end()) {
222 m_pixel_width_column = args.at(ASSOC_SOURCE_WIDTHS).as<int>() - 1; // config uses 1 as first column
223 }
224 if (args.find(ASSOC_SOURCE_HEIGHTS) != args.end()) {
225 m_pixel_height_column = args.at(ASSOC_SOURCE_HEIGHTS).as<int>() - 1; // config uses 1 as first column
226 }
227
228 m_group_id_column = args.at(ASSOC_GROUP_ID).as<int>() - 1; // config uses 1 as first column
229
231}
232
235
236 if (m_assoc_columns.find("x") != m_assoc_columns.end() && m_assoc_columns.find("y") != m_assoc_columns.end()) {
238
239 m_columns.push_back(m_assoc_columns.at("x"));
240 m_assoc_columns.erase("x");
241 m_columns.push_back(m_assoc_columns.at("y"));
242 m_assoc_columns.erase("y");
243
244 if (m_assoc_columns.find("ra") != m_assoc_columns.end() ||
245 m_assoc_columns.find("dec") != m_assoc_columns.end()) {
246 throw Elements::Exception() << "Use either X/Y or RA/DEC coordinates in assoc config file but not both";
247 }
248 } else if (m_assoc_columns.find("ra") != m_assoc_columns.end() &&
249 m_assoc_columns.find("dec") != m_assoc_columns.end()) {
250
252
253 m_columns.push_back(m_assoc_columns.at("ra"));
254 m_assoc_columns.erase("ra");
255 m_columns.push_back(m_assoc_columns.at("dec"));
256 m_assoc_columns.erase("dec");
257
258 if (m_assoc_columns.find("x") != m_assoc_columns.end() ||
259 m_assoc_columns.find("y") != m_assoc_columns.end()) {
260 throw Elements::Exception() << "Use either X/Y or RA/DEC coordinates in assoc config file but not both";
261 }
262 } else {
263 throw Elements::Exception() << "Missing X/Y or RA/DEC coordinates in assoc config file";
264 }
265
266 if (m_assoc_columns.find("weight") != m_assoc_columns.end()) {
267 m_columns.push_back(m_assoc_columns.at("weight"));
268 m_assoc_columns.erase("weight");
269 }
270
271 if (m_assoc_columns.find("pixel_size") != m_assoc_columns.end()) {
272 m_pixel_width_column = m_assoc_columns.at("pixel_size");
273 m_pixel_height_column = m_assoc_columns.at("pixel_size");
274 m_assoc_columns.erase("pixel_size");
275 }
276
277 if (m_assoc_columns.find("pixel_width") != m_assoc_columns.end()) {
278 m_pixel_width_column = m_assoc_columns.at("pixel_width");
279 m_assoc_columns.erase("pixel_width");
280 }
281
282 if (m_assoc_columns.find("pixel_height") != m_assoc_columns.end()) {
283 m_pixel_width_column = m_assoc_columns.at("pixel_height");
284 m_assoc_columns.erase("pixel_height");
285 }
286
287 if (m_assoc_columns.find("group_id") != m_assoc_columns.end()) {
288 m_group_id_column = m_assoc_columns.at("group_id");
289 m_assoc_columns.erase("group_id");
290 }
291
292 for (auto& column_info : m_assoc_columns) {
293 m_custom_column_names.push_back(column_info.first);
294 m_columns_idx.push_back(column_info.second);
295 }
296}
297
299 if (m_columns.size() < 2) {
300 throw Elements::Exception() << "At least 2 columns must be specified for x,y coordinates in the assoc catalog";
301 }
302 if (m_columns.size() > 3) {
303 throw Elements::Exception() << "Maximum 3 columns for x, y and weight must be specified in the assoc catalog";
304 }
305
307 logger.warn() <<
308 "Using Assoc catalog matching in pixel coordinates with multiple detection images";
309 }
310
312 throw Elements::Exception() << "Using Assoc catalog matching in pixel coordinates without a detection images";
313 }
314
315}
316
318 AssocCoordType assoc_coord_type = AssocCoordType::PIXEL;
319 if (args.find(ASSOC_COORD_TYPE) != args.end()) {
320 auto assoc_coord_type_str = boost::to_upper_copy(args.at(ASSOC_COORD_TYPE).as<std::string>());
321 if (assoc_coord_type_table.find(assoc_coord_type_str) != assoc_coord_type_table.end()) {
322 assoc_coord_type = assoc_coord_type_table.at(assoc_coord_type_str);
323 } else {
324 throw Elements::Exception() << "Invalid association coordinate type: " << assoc_coord_type_str;
325 }
326 }
327 return assoc_coord_type;
328}
329
331 const std::vector<int>& columns, AssocCoordType assoc_coord_type) {
332 try {
334 try {
336 } catch (...) {
337 // If FITS not successful try reading as ascii
339 }
340 auto table = reader->read();
341
342 size_t exts_nb = getDependency<DetectionImageConfig>().getExtensionsNb();
343 if (exts_nb == 0) {
344 // No detection image
345 m_catalogs.emplace_back(readTable(table, columns, m_columns_idx, true));
346 } else {
347 for (size_t i = 0; i < exts_nb; i++) {
348 auto coordinate_system = getDependency<DetectionImageConfig>().getCoordinateSystem(i);
349 if (assoc_coord_type == AssocCoordType::WORLD) {
350 m_catalogs.emplace_back(readTable(table, columns, m_columns_idx, true, coordinate_system));
351 } else {
352 m_catalogs.emplace_back(readTable(table, columns, m_columns_idx, false, coordinate_system));
353 }
354 }
355 }
356 } catch (const std::exception& e) {
357 throw Elements::Exception() << "Can't either open or read assoc catalog: " << filename << " (" << e.what() << ")";
358 } catch(...) {
359 throw Elements::Exception() << "Can't either open or read assoc catalog: " << filename;
360 }
361}
362
364 const Euclid::Table::Table& table, const std::vector<int>& columns,
365 const std::vector<int>& copy_columns, bool use_world, std::shared_ptr<CoordinateSystem> coordinate_system) {
367
369 for (auto& row : table) {
370
371 ImageCoordinate coord;
372 WorldCoordinate world_coord;
373 if (use_world) {
374 world_coord = WorldCoordinate {
375 boost::apply_visitor(CastVisitor<double>{}, row[columns.at(0)]),
376 boost::apply_visitor(CastVisitor<double>{}, row[columns.at(1)]),
377 };
378 if (coordinate_system != nullptr) {
379 coord = coordinate_system->worldToImage(world_coord);
380 }
381 } else {
382 coord = ImageCoordinate {
383 // our internal pixel coordinates are zero-based
384 boost::apply_visitor(CastVisitor<double>{}, row[columns.at(0)]) - 1.0,
385 boost::apply_visitor(CastVisitor<double>{}, row[columns.at(1)]) - 1.0,
386 };
387 if (coordinate_system != nullptr) {
388 world_coord = coordinate_system->imageToWorld(coord);
389 }
390 }
391 catalog.emplace_back(CatalogEntry { coord, world_coord, 1.0, {}, 1.0, 1.0, 0 });
392 if (columns.size() == 3 && columns.at(2) >= 0) {
393 catalog.back().weight = boost::apply_visitor(CastVisitor<double>{}, row[columns.at(2)]);
394 }
395 for (auto column : copy_columns) {
396 if (column >= static_cast<int>(row.size())) {
397 throw Elements::Exception() << "Column index " << column << " is out of bounds";
398 }
399 if (row[column].type() == typeid(int)) {
400 catalog.back().assoc_columns.emplace_back(boost::get<int>(row[column]));
401 } else if (row[column].type() == typeid(double)) {
402 catalog.back().assoc_columns.emplace_back(boost::get<double>(row[column]));
403 } else if (row[column].type() == typeid(int64_t)) {
404 catalog.back().assoc_columns.emplace_back(boost::get<int64_t>(row[column]));
405 } else if (row[column].type() == typeid(SeFloat)) {
406 catalog.back().assoc_columns.emplace_back(boost::get<SeFloat>(row[column]));
407 } else {
408 throw Elements::Exception() << "Wrong type in assoc column (must be a numeric type)";
409 }
410 }
411
412 if (m_group_id_column >= 0) {
413 catalog.back().group_id = boost::apply_visitor(CastVisitor<int64_t>{}, row[m_group_id_column]);
414 }
415
417 //catalog.back().source_radius_pixels = boost::apply_visitor(CastVisitor<double>{}, row[m_pixel_size_column]);
418 catalog.back().source_pixel_width = boost::apply_visitor(CastVisitor<double>{}, row[m_pixel_width_column]);
419 catalog.back().source_pixel_height = boost::apply_visitor(CastVisitor<double>{}, row[m_pixel_height_column]);
420 } else {
421 catalog.back().source_pixel_width = m_default_pixel_size;
422 catalog.back().source_pixel_height = m_default_pixel_size;
423 }
424 }
425 return catalog;
426}
427
430
431 const std::vector<std::string> reserved_names {
432 "x", "y", "ra", "dec", "weight", "group_id", "pixel_size", "pixel_width", "pixel_height"
433 };
434
435 std::ifstream config_file(filename);
436 if (!config_file.is_open()) {
437 throw Elements::Exception() << "Can't either open or read assoc config file: " << filename;
438 }
439
440 std::string line;
441 int current_column_nb = 1; // column indices start at 1
442 while (std::getline(config_file, line)) {
443 boost::trim(line);
444 // Skip lines starting with '#'
445 if (line.empty() || line[0] == '#') {
446 continue;
447 }
448
449 std::string name;
450 int number;
451 // Find the position of the '=' character
452 auto equal_sign_pos = line.find('=');
453 if (equal_sign_pos != std::string::npos) {
454 name = line.substr(0, equal_sign_pos);
455 std::string number_string = line.substr(equal_sign_pos + 1);
456 std::istringstream iss(number_string);
457 iss >> number;
458 number--;
459 current_column_nb = number + 1;
460 } else {
461 name = line;
462 number = current_column_nb++;
463 }
464 boost::trim(name);
465
466 if (std::find(reserved_names.begin(), reserved_names.end(), boost::to_lower_copy(name)) != reserved_names.end()) {
467 boost::to_lower(name);
468 }
469
470
471 // Store the parsed information into the vector
472 columns[name] = number;
473 }
474
475 config_file.close();
476 return columns;
477}
478
480 std::cout << "Assoc catalog configuration" << std::endl;
481
482 auto& catalog = m_catalogs.at(0);
483
485 std::cout << "X" << "\t";
486 std::cout << "Y" << "\t";
487 } else {
488 std::cout << "RA" << "\t";
489 std::cout << "DEC" << "\t";
490 }
491 if (m_columns.size() >= 3) {
492 std::cout << "WEIGHT" << "\t";
493 }
494 if (m_pixel_width_column >= 0) {
495 std::cout << "PIXEL_WIDTH" << "\t";
496 }
497 if (m_pixel_height_column >= 0) {
498 std::cout << "PIXEL_HEIGHT" << "\t";
499 }
500 if (m_group_id_column >= 0) {
501 std::cout << "GROUP_ID" << "\t";
502 }
503
504 for (const auto& name : m_custom_column_names) {
505 std::cout << name << "\t";
506 }
508
509 int lines = 0;
510 for (const auto& entry : catalog) {
512 std::cout << entry.coord.m_x << "\t";
513 std::cout << entry.coord.m_y << "\t";
514 } else {
515 std::cout << entry.world_coord.m_alpha << "\t";
516 std::cout << entry.world_coord.m_delta << "\t";
517 }
518 if (m_columns.size() >= 3) {
519 std::cout << entry.weight << "\t";
520 }
521 if (m_pixel_width_column >= 0) {
522 std::cout << entry.source_pixel_width << "\t";
523 }
524 if (m_pixel_height_column >= 0) {
525 std::cout << entry.source_pixel_height << "\t";
526 }
527 if (m_group_id_column >= 0) {
528 std::cout << entry.group_id << "\t";
529 }
530 for (auto& column : entry.assoc_columns) {
531 std::cout << column << "\t";
532 }
534
535 if (++lines >= 10) {
536 break;
537 }
538 }
539}
540
541
542}
T at(T... args)
T back(T... args)
static Logging getLogger(const std::string &name="")
static ConfigManager & getInstance(long id)
std::map< std::string, boost::program_options::variable_value > UserValues
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 close(T... args)
T emplace_back(T... args)
T empty(T... args)
T end(T... args)
T endl(T... args)
T find(T... args)
T getline(T... args)
T is_open(T... args)
T make_pair(T... args)
T make_shared(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_WIDTHS
static const std::string ASSOC_SOURCE_SIZES
static const std::string ASSOC_FILTER
static const std::string ASSOC_CATALOG
SeFloat32 SeFloat
Definition Types.h:32
static const std::string ASSOC_SOURCE_HEIGHTS
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 size(T... args)
T substr(T... args)