SourceXtractorPlusPlus 0.22
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_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
113
115 return { {"Assoc config", {
116 {ASSOC_CATALOG.c_str(), po::value<std::string>(),
117 "Assoc catalog file"},
118 {ASSOC_COLUMNS.c_str(), po::value<std::string>()->default_value("1,2"),
119 "Assoc columns to specify x/ra,y/dec[,weight] (the index of the first column is 1)"},
120 {ASSOC_MODE.c_str(), po::value<std::string>()->default_value("NEAREST"),
121 "Assoc mode [FIRST, NEAREST, MEAN, MAG_MEAN, SUM, MAG_SUM, MIN, MAX]"},
122 {ASSOC_RADIUS.c_str(), po::value<double>()->default_value(2.0),
123 "Assoc radius (Always in pixels of the detection image)"},
124 {ASSOC_FILTER.c_str(), po::value<std::string>()->default_value("ALL"),
125 "Assoc catalog filter setting: ALL, MATCHED, UNMATCHED"},
126 {ASSOC_COPY.c_str(), po::value<std::string>()->default_value(""),
127 "List of columns indices in the assoc catalog to copy on match (the index of the first column is 1). "},
128 {ASSOC_COORD_TYPE.c_str(), po::value<std::string>()->default_value("PIXEL"),
129 "Assoc coordinates type: PIXEL, WORLD"},
130 {ASSOC_SOURCE_SIZES.c_str(), po::value<int>()->default_value(-1),
131 "Column containing the source sizes (in reference frame pixels)"},
132 {ASSOC_DEFAULT_PIXEL_SIZE.c_str(), po::value<double>()->default_value(5.0),
133 "Default source size (in reference frame pixels)"},
134 {ASSOC_GROUP_ID.c_str(), po::value<int>()->default_value(-1),
135 "Column containing the group id"},
136 {ASSOC_CONFIG.c_str(), po::value<std::string>(),
137 "Text file containing the assoc columns configuration"},
138 {ASSOC_TEST.c_str(), po::bool_switch(),
139 "Prints the assoc configuration and quits"},
140 }}};
141}
142
144 // read configuration from command line arguments
145 readCommonConfig(args);
146
147 // read columns from columns config file
148 if (args.find(ASSOC_CONFIG) != args.end()) {
149 auto assoc_config_filename = args.at(ASSOC_CONFIG).as<std::string>();
150 readConfigFromFile(assoc_config_filename);
151 } else {
153 }
154
155 // sanity check that the configuration is coherent
156
157 // read the catalogs
158 if (m_filename != "") {
159 checkConfig();
161
162 if (args.at(ASSOC_TEST).as<bool>()) {
163 printConfig();
164 throw Elements::Exception() << "Exiting by user request";
165 }
166 }
167}
168
170 auto filter = boost::to_upper_copy(args.at(ASSOC_FILTER).as<std::string>());
171 if (assoc_filter_table.find(filter) != assoc_filter_table.end()) {
172 auto assoc_filter = assoc_filter_table.at(filter);
173 if (assoc_filter == AssocFilter::MATCHED) {
174 getDependency<PartitionStepConfig>().addPartitionStepCreator(
177 }
178 );
179 } else if (assoc_filter == AssocFilter::UNMATCHED) {
180 getDependency<PartitionStepConfig>().addPartitionStepCreator(
183 }
184 );
185 }
186 } else {
187 throw Elements::Exception() << "Invalid assoc filter: " << filter;
188 }
189
190 if (args.find(ASSOC_MODE) != args.end()) {
191 auto assoc_mode = boost::to_upper_copy(args.at(ASSOC_MODE).as<std::string>());
192 if (assoc_mode_table.find(assoc_mode) != assoc_mode_table.end()) {
193 m_assoc_mode = assoc_mode_table.at(assoc_mode);
194 } else {
195 throw Elements::Exception() << "Invalid association mode: " << assoc_mode;
196 }
197 }
198
199 m_assoc_radius = args.at(ASSOC_RADIUS).as<double>();
201
202 if (args.find(ASSOC_CATALOG) != args.end()) {
204 }
205}
206
208 m_columns = parseColumnList(args.at(ASSOC_COLUMNS).as<std::string>());
209 m_columns_idx = parseColumnList(args.at(ASSOC_COPY).as<std::string>());
210
211 m_pixel_size_column = args.at(ASSOC_SOURCE_SIZES).as<int>() - 1; // config uses 1 as first column
212 m_group_id_column = args.at(ASSOC_GROUP_ID).as<int>() - 1; // config uses 1 as first column
213
215}
216
219
220 if (m_assoc_columns.find("x") != m_assoc_columns.end() && m_assoc_columns.find("y") != m_assoc_columns.end()) {
222
223 m_columns.push_back(m_assoc_columns.at("x"));
224 m_assoc_columns.erase("x");
225 m_columns.push_back(m_assoc_columns.at("y"));
226 m_assoc_columns.erase("y");
227
228 if (m_assoc_columns.find("ra") != m_assoc_columns.end() ||
229 m_assoc_columns.find("dec") != m_assoc_columns.end()) {
230 throw Elements::Exception() << "Use either X/Y or RA/DEC coordinates in assoc config file but not both";
231 }
232 } else if (m_assoc_columns.find("ra") != m_assoc_columns.end() &&
233 m_assoc_columns.find("dec") != m_assoc_columns.end()) {
234
236
237 m_columns.push_back(m_assoc_columns.at("ra"));
238 m_assoc_columns.erase("ra");
239 m_columns.push_back(m_assoc_columns.at("dec"));
240 m_assoc_columns.erase("dec");
241
242 if (m_assoc_columns.find("x") != m_assoc_columns.end() ||
243 m_assoc_columns.find("y") != m_assoc_columns.end()) {
244 throw Elements::Exception() << "Use either X/Y or RA/DEC coordinates in assoc config file but not both";
245 }
246 } else {
247 throw Elements::Exception() << "Missing X/Y or RA/DEC coordinates in assoc config file";
248 }
249
250 if (m_assoc_columns.find("weight") != m_assoc_columns.end()) {
251 m_columns.push_back(m_assoc_columns.at("weight"));
252 m_assoc_columns.erase("weight");
253 }
254
255 if (m_assoc_columns.find("pixel_size") != m_assoc_columns.end()) {
256 m_pixel_size_column = m_assoc_columns.at("pixel_size");
257 m_assoc_columns.erase("pixel_size");
258 }
259
260 if (m_assoc_columns.find("group_id") != m_assoc_columns.end()) {
261 m_group_id_column = m_assoc_columns.at("group_id");
262 m_assoc_columns.erase("group_id");
263 }
264
265 for (auto& column_info : m_assoc_columns) {
266 m_custom_column_names.push_back(column_info.first);
267 m_columns_idx.push_back(column_info.second);
268 }
269}
270
272 if (m_columns.size() < 2) {
273 throw Elements::Exception() << "At least 2 columns must be specified for x,y coordinates in the assoc catalog";
274 }
275 if (m_columns.size() > 3) {
276 throw Elements::Exception() << "Maximum 3 columns for x, y and weight must be specified in the assoc catalog";
277 }
278
280 logger.warn() <<
281 "Using Assoc catalog matching in pixel coordinates with multiple detection images";
282 }
283
285 throw Elements::Exception() << "Using Assoc catalog matching in pixel coordinates without a detection images";
286 }
287
288}
289
291 AssocCoordType assoc_coord_type = AssocCoordType::PIXEL;
292 if (args.find(ASSOC_COORD_TYPE) != args.end()) {
293 auto assoc_coord_type_str = boost::to_upper_copy(args.at(ASSOC_COORD_TYPE).as<std::string>());
294 if (assoc_coord_type_table.find(assoc_coord_type_str) != assoc_coord_type_table.end()) {
295 assoc_coord_type = assoc_coord_type_table.at(assoc_coord_type_str);
296 } else {
297 throw Elements::Exception() << "Invalid association coordinate type: " << assoc_coord_type_str;
298 }
299 }
300 return assoc_coord_type;
301}
302
304 const std::vector<int>& columns, AssocCoordType assoc_coord_type) {
305 try {
307 try {
309 } catch (...) {
310 // If FITS not successful try reading as ascii
312 }
313 auto table = reader->read();
314
315 size_t exts_nb = getDependency<DetectionImageConfig>().getExtensionsNb();
316 if (exts_nb == 0) {
317 // No detection image
318 m_catalogs.emplace_back(readTable(table, columns, m_columns_idx, true));
319 } else {
320 for (size_t i = 0; i < exts_nb; i++) {
321 auto coordinate_system = getDependency<DetectionImageConfig>().getCoordinateSystem(i);
322 if (assoc_coord_type == AssocCoordType::WORLD) {
323 m_catalogs.emplace_back(readTable(table, columns, m_columns_idx, true, coordinate_system));
324 } else {
325 m_catalogs.emplace_back(readTable(table, columns, m_columns_idx, false, coordinate_system));
326 }
327 }
328 }
329 } catch (const std::exception& e) {
330 throw Elements::Exception() << "Can't either open or read assoc catalog: " << filename << " (" << e.what() << ")";
331 } catch(...) {
332 throw Elements::Exception() << "Can't either open or read assoc catalog: " << filename;
333 }
334}
335
337 const Euclid::Table::Table& table, const std::vector<int>& columns,
338 const std::vector<int>& copy_columns, bool use_world, std::shared_ptr<CoordinateSystem> coordinate_system) {
340
342 for (auto& row : table) {
343
344 ImageCoordinate coord;
345 WorldCoordinate world_coord;
346 if (use_world) {
347 world_coord = WorldCoordinate {
348 boost::apply_visitor(CastVisitor<double>{}, row[columns.at(0)]),
349 boost::apply_visitor(CastVisitor<double>{}, row[columns.at(1)]),
350 };
351 if (coordinate_system != nullptr) {
352 coord = coordinate_system->worldToImage(world_coord);
353 }
354 } else {
355 coord = ImageCoordinate {
356 // our internal pixel coordinates are zero-based
357 boost::apply_visitor(CastVisitor<double>{}, row[columns.at(0)]) - 1.0,
358 boost::apply_visitor(CastVisitor<double>{}, row[columns.at(1)]) - 1.0,
359 };
360 if (coordinate_system != nullptr) {
361 world_coord = coordinate_system->imageToWorld(coord);
362 }
363 }
364 catalog.emplace_back(CatalogEntry { coord, world_coord, 1.0, {}, 1.0, 0 });
365 if (columns.size() == 3 && columns.at(2) >= 0) {
366 catalog.back().weight = boost::apply_visitor(CastVisitor<double>{}, row[columns.at(2)]);
367 }
368 for (auto column : copy_columns) {
369 if (column >= static_cast<int>(row.size())) {
370 throw Elements::Exception() << "Column index " << column << " is out of bounds";
371 }
372 if (row[column].type() == typeid(int)) {
373 catalog.back().assoc_columns.emplace_back(boost::get<int>(row[column]));
374 } else if (row[column].type() == typeid(double)) {
375 catalog.back().assoc_columns.emplace_back(boost::get<double>(row[column]));
376 } else if (row[column].type() == typeid(int64_t)) {
377 catalog.back().assoc_columns.emplace_back(boost::get<int64_t>(row[column]));
378 } else if (row[column].type() == typeid(SeFloat)) {
379 catalog.back().assoc_columns.emplace_back(boost::get<SeFloat>(row[column]));
380 } else {
381 throw Elements::Exception() << "Wrong type in assoc column (must be a numeric type)";
382 }
383 }
384
385 if (m_group_id_column >= 0) {
386 catalog.back().group_id = boost::apply_visitor(CastVisitor<int64_t>{}, row[m_group_id_column]);
387 }
388
389 if (m_pixel_size_column >= 0) {
390 catalog.back().source_radius_pixels = boost::apply_visitor(CastVisitor<double>{}, row[m_pixel_size_column]);
391 } else {
392 catalog.back().source_radius_pixels = m_default_pixel_size;
393 }
394 }
395 return catalog;
396}
397
400
401 const std::vector<std::string> reserved_names {
402 "x", "y", "ra", "dec", "weight", "group_id", "source_radius_pixel"
403 };
404
405 std::ifstream config_file(filename);
406 if (!config_file.is_open()) {
407 throw Elements::Exception() << "Can't either open or read assoc config file: " << filename;
408 }
409
410 std::string line;
411 int current_column_nb = 1; // column indices start at 1
412 while (std::getline(config_file, line)) {
413 boost::trim(line);
414 // Skip lines starting with '#'
415 if (line.empty() || line[0] == '#') {
416 continue;
417 }
418
419 std::string name;
420 int number;
421 // Find the position of the '=' character
422 auto equal_sign_pos = line.find('=');
423 if (equal_sign_pos != std::string::npos) {
424 name = line.substr(0, equal_sign_pos);
425 std::string number_string = line.substr(equal_sign_pos + 1);
426 std::istringstream iss(number_string);
427 iss >> number;
428 number--;
429 current_column_nb = number + 1;
430 } else {
431 name = line;
432 number = current_column_nb++;
433 }
434 boost::trim(name);
435
436 if (std::find(reserved_names.begin(), reserved_names.end(), boost::to_lower_copy(name)) != reserved_names.end()) {
437 boost::to_lower(name);
438 }
439
440
441 // Store the parsed information into the vector
442 columns[name] = number;
443 }
444
445 config_file.close();
446 return columns;
447}
448
450 std::cout << "Assoc catalog configuration" << std::endl;
451
452 auto& catalog = m_catalogs.at(0);
453
455 std::cout << "X" << "\t";
456 std::cout << "Y" << "\t";
457 } else {
458 std::cout << "RA" << "\t";
459 std::cout << "DEC" << "\t";
460 }
461 if (m_columns.size() >= 3) {
462 std::cout << "WEIGHT" << "\t";
463 }
464 if (m_pixel_size_column >= 0) {
465 std::cout << "PIXEL_SIZE" << "\t";
466 }
467 if (m_group_id_column >= 0) {
468 std::cout << "GROUP_ID" << "\t";
469 }
470
471 for (const auto& name : m_custom_column_names) {
472 std::cout << name << "\t";
473 }
475
476 int lines = 0;
477 for (const auto& entry : catalog) {
479 std::cout << entry.coord.m_x << "\t";
480 std::cout << entry.coord.m_y << "\t";
481 } else {
482 std::cout << entry.world_coord.m_alpha << "\t";
483 std::cout << entry.world_coord.m_delta << "\t";
484 }
485 if (m_columns.size() >= 3) {
486 std::cout << entry.weight << "\t";
487 }
488 if (m_pixel_size_column >= 0) {
489 std::cout << entry.source_radius_pixels << "\t";
490 }
491 if (m_group_id_column >= 0) {
492 std::cout << entry.group_id << "\t";
493 }
494 for (auto& column : entry.assoc_columns) {
495 std::cout << column << "\t";
496 }
498
499 if (++lines >= 10) {
500 break;
501 }
502 }
503}
504
505
506}
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_SIZES
static const std::string ASSOC_FILTER
static const std::string ASSOC_CATALOG
SeFloat32 SeFloat
Definition Types.h:32
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)