//**************************************************************** /** @class FitsDumper @brief Dumps contents of fits tables to stdout or a file */ //**************************************************************** #include "Configuration.h" #include #include #include "externals/fits.h" //#define PLOTTING_PLEASE #ifdef PLOTTING_PLEASE #include #include #include #include #include #include #include #include #include #endif #include "Time.h" using namespace std; #ifdef PLOTTING_PLEASE class TimeScaleDraw: public QwtScaleDraw { public: virtual QwtText label(double v) const { Time t(v); string time = t.GetAsStr("%H:%M:%S%F"); while (time[time.size()-1] == '0' && time.size() > 2) { time = time.substr(0, time.size()-1); } return QwtText(time.c_str()); } }; #endif class FitsDumper { public: FitsDumper(); ~FitsDumper(); private: fits* fFile; bool fDotsPlease; bool fNoZeroPlease; string fFilename; fits::Table::Columns fColMap; fits::Table::Keys fKeyMap; // Convert CCfits::ValueType into a human readable string string ValueTypeToStr(char type) const; // Convert CCfits::ValueType into a number of associated bytes int ValueTypeToSize(char type) const; /// Calculate the buffer size required to read a row of the fits table, as well as the offsets to each column // vector CalculateOffsets() const; template T PtrToValue(const unsigned char* &ptr) const; // template // double PtrToDouble(const unsigned char *ptr) const; // double PtrToDouble(const unsigned char *ptr, CCfits::ValueType type) const; /// Write a single row of the selected data // int WriteRow(ostream &, const vector &, const vector &, unsigned char *, const vector >&) const; bool OpenFile(const string &); /// Open a file bool OpenTable(const string &); /// Open a table /// Lists all columns of an open file void List(); void ListHeader(const string& filename); void ListKeywords(ostream &); bool separateColumnsFromRanges(const vector& list, vector >& ranges, vector& listNamesOnly); /// Perform the dumping, based on the current dump list bool Dump(const string &, const vector &list, int); ///Display the selected columns values VS time #ifdef PLOTTING_PLEASE int doCurvesDisplay( const vector &list, const string& tableName); #endif int doMinMaxPlease(const string& filename, const vector& list, int precision); int doStatsPlease(const string &filename, const vector& list, int precision); // void doTBoundary(conf.Get("outfile"), conf.Get("precision"), true); // bool Plot(const vector &list); public: ///Configures the fitsLoader from the config file and/or command arguments. int ExecConfig(Configuration& conf); }; // -------------------------------------------------------------------------- // //! Constructor //! @param out //! the ostream where to redirect the outputs // FitsDumper::FitsDumper() : fFile(0), fDotsPlease(false), fNoZeroPlease(false) { } // -------------------------------------------------------------------------- // //! Destructor // FitsDumper::~FitsDumper() { if (fFile) delete fFile; } string FitsDumper::ValueTypeToStr(char type) const { switch (type) { case 'L': return "bool(8)"; case 'B': return "byte(8)"; case 'I': return "short(16)"; case 'J': return "int(32)"; case 'K': return "int(64)"; case 'E': return "float(32)"; case 'D': return "double(64)"; default: return "unknwown"; } } int FitsDumper::ValueTypeToSize(char type) const { switch (type) { case 'L': return sizeof(uint8_t); case 'B': return sizeof(int8_t); case 'I': return sizeof(int16_t); case 'J': return sizeof(int32_t); case 'K': return sizeof(int64_t); case 'E': return sizeof(float); case 'D': return sizeof(double); default: return 0; } } template T FitsDumper::PtrToValue(const unsigned char* &ptr) const { T t; reverse_copy(ptr, ptr+sizeof(T), reinterpret_cast(&t)); ptr += sizeof(T); return t; } // -------------------------------------------------------------------------- // //! Loads the fits file based on the current parameters // bool FitsDumper::OpenFile(const string &filename) { if (fFile) { fFile->close(); delete fFile; } try { fFile = new fits(filename); } catch (std::runtime_error e) { cout << "Something went wrong while trying to open " << filename; cout << ": " << e.what() << " Aborting dump." << endl; return false; } fFilename = filename; const fits::Table::Columns& tCols = fFile->GetColumns(); for (auto it=tCols.begin(); it != tCols.end(); it++) fColMap.insert(*it); const fits::Table::Keys& tkeys = fFile->GetKeys(); for (auto it=tkeys.begin(); it != tkeys.end(); it++) fKeyMap.insert(*it); return true; } bool FitsDumper::OpenTable(const string &) { if (!fFile) { cerr << "No file open." << endl; return false; } return true; } void FitsDumper::List() { if (!fFile) { cerr << "No file open." << endl; return; } cout << "\nFile: " << fFilename << "\n"; cout << " " << fKeyMap.find("EXTNAME")->second.value << " ["; cout << fKeyMap.find("NAXIS2")->second.value << "]\n"; for (auto it = fColMap.begin(); it != fColMap.end(); it++) { cout << " " << it->first << "[" << it->second.num << "] (" << it->second.unit << ":" << ValueTypeToStr(it->second.type) << ") "; for (auto jt = fKeyMap.begin(); jt != fKeyMap.end(); jt++) if (jt->second.value == it->first) cout << jt->second.comment << endl; } cout << endl; cout << flush; } void FitsDumper::ListKeywords(ostream &out) { for (auto it=fKeyMap.begin(); it != fKeyMap.end(); it++) { out << "## " << setw(8) << it->first << " = " << setw(10); out << "'" << it->second.value << "'" << " / " << it->second.comment << endl; } } void FitsDumper::ListHeader(const string& filename) { if (!fFile) { cerr << "No table open." << endl; return; } ofstream out(filename=="-"?"/dev/stdout":filename); if (!out) { cerr << "Cannot open file " << filename << ": " << strerror(errno) << endl; return; } out << "\nTable: " << fKeyMap.find("EXTNAME")->second.value << " (rows=" << fKeyMap.find("NAXIS2")->second.value << ")\n"; if (fKeyMap.find("COMMENT") != fKeyMap.end()) out << "Comment: \t" << fKeyMap.find("COMMENT")->second.value << "\n"; ListKeywords(out); out << endl; } bool FitsDumper::separateColumnsFromRanges(const vector& list, vector >& ranges, vector& listNamesOnly) { for (vector::const_iterator it=list.begin(); it!=list.end(); it++) { string columnNameOnly = *it; unsigned long bracketIndex0 = columnNameOnly.find_first_of('['); unsigned long bracketIndex1 = columnNameOnly.find_first_of(']'); unsigned long colonIndex = columnNameOnly.find_first_of(':'); int columnStart = -1; int columnEnd = -1; if (bracketIndex0 != string::npos) {//there is a range given. Extract the range if (colonIndex != string::npos) {//we have a range here columnStart = atoi(columnNameOnly.substr(bracketIndex0+1, colonIndex-(bracketIndex0+1)).c_str()); columnEnd = atoi(columnNameOnly.substr(colonIndex+1, bracketIndex1-(colonIndex+1)).c_str()); columnEnd++; } else {//only a single index there columnStart = atoi(columnNameOnly.substr(bracketIndex0+1, bracketIndex1 - (bracketIndex0+1)).c_str()); columnEnd = columnStart+1; } columnNameOnly = columnNameOnly.substr(0, bracketIndex0); } if (fColMap.find(columnNameOnly) == fColMap.end()) { cerr << "ERROR - Column '" << columnNameOnly << "' not found in table." << endl; return false; } fits::Table::Column& cCol = fColMap.find(columnNameOnly)->second; if (bracketIndex0 == string::npos) {//no range given: use the full range ranges.push_back(make_pair(0, cCol.num)); columnStart = 0; columnEnd = 1; } else {//use the range extracted earlier if (columnStart < 0) { cerr << "ERROR - Start range for column " << columnNameOnly << " is less than zero (" << columnStart << "). Aborting" << endl; return false; } if (columnEnd>1 && columnEnd > (int)(cCol.num)) { cerr << "ERROR - End range for column " << columnNameOnly << " is greater than the last element (" << cCol.num << " vs " << columnEnd << "). Aborting" << endl; return false; } ranges.push_back(make_pair(columnStart, columnEnd)); } // cout << "Will be exporting from " << columnStart << " to " << columnEnd-1 << " for column " << columnNameOnly << endl; listNamesOnly.push_back(columnNameOnly); } return true; } // -------------------------------------------------------------------------- // //! Perform the actual dump, based on the current parameters // bool FitsDumper::Dump(const string &filename, const vector &list, int precision) { //first of all, let's separate the columns from their ranges and check that the requested columns are indeed part of the file vector > ranges; vector listNamesOnly; if (!separateColumnsFromRanges(list, ranges, listNamesOnly)) { cerr << "Something went wrong while extracting the columns names from parameters. Aborting" << endl; return false; } ofstream out(filename=="-"?"/dev/stdout":filename); if (!out) { cerr << "Cannot open file " << filename << ": " << strerror(errno) << endl; return false; } out.precision(precision); out << "## --------------------------------------------------------------------------\n"; if (filename!="-") out << "## File: \t" << filename << '\n'; out << "## Table: \t" << fKeyMap.find("EXTNAME")->second.value << '\n'; out << "## Comment: \t" << ((fKeyMap.find("COMMENT") != fKeyMap.end()) ? fKeyMap.find("COMMENT")->second.value : "") << '\n'; out << "## NumRows: \t" << fFile->GetInt("NAXIS2") << '\n'; out << "## --------------------------------------------------------------------------\n"; ListKeywords(out); out << "## --------------------------------------------------------------------------\n"; out << "#\n"; auto rangesIt = ranges.begin(); for (vector::const_iterator it=listNamesOnly.begin(); it!=listNamesOnly.end(); it++, rangesIt++) { const fits::Table::Column& col = fColMap[*it]; // const MyColumn *col = static_cast(fTable->column()[*it]); if (rangesIt->first != 0 || rangesIt->second != (int)(col.num)) { out << "#"; for (int i=rangesIt->first; isecond; i++) out << " " << *it << "[" << i << "]"; out << ": " << col.unit; } else out << "# " << *it << "[" << col.num << "]: " << col.unit; // FIXME: retrive the column comment // if (!col->comment().empty()) // out << " (" <comment() << ")"; out << '\n'; } out << "#" << endl; // Loop over all columns in our list of requested columns vector > columnsData; for (vector::const_iterator it=listNamesOnly.begin(); it!=listNamesOnly.end(); it++) { fits::Table::Column& cCol = fColMap.find(*it)->second; columnsData.push_back(make_pair(cCol.type, new char[cCol.num*cCol.size])); fFile->SetPtrAddress(*it, columnsData[columnsData.size()-1].second); } int numRows = fFile->GetInt("NAXIS2"); int row = 0; while (fFile->GetNextRow() && row < numRows) { row++; rangesIt = ranges.begin(); for (auto it=columnsData.begin(); it != columnsData.end(); it++, rangesIt++) { for (int i=rangesIt->first; isecond; i++) { switch (it->first) { case 'L': out << reinterpret_cast(it->second)[i] << " "; break; case 'B': out << reinterpret_cast(it->second)[i] << " "; break; case 'I': out << reinterpret_cast(it->second)[i] << " "; break; case 'J': out << reinterpret_cast(it->second)[i] << " "; break; case 'K': out << reinterpret_cast(it->second)[i] << " "; break; case 'E': out << reinterpret_cast(it->second)[i] << " "; break; case 'D': out << reinterpret_cast(it->second)[i] << " "; break; default: ; } } } } for (auto it = columnsData.begin(); it != columnsData.end(); it++) delete[] it->second; return true; } // -------------------------------------------------------------------------- // //! Retrieves the configuration parameters //! @param conf //! the configuration object // int FitsDumper::ExecConfig(Configuration& conf) { if (conf.Has("fitsfile")) { if (!OpenFile(conf.Get("fitsfile"))) return -1; } #ifdef PLOTTING_PLEASE if (conf.Get("stat") && conf.Get("graph")) { cout << "Invalid conbination of options: cannot graph stats. Aborting" << endl; return -1; } #endif if (conf.Get("minmax") && conf.Get("stat")) { cout << "Invalid combination of options: cannot do stats and minmax. Aborting" << endl; return -1; } #ifdef PLOTTING_PLEASE if (conf.Get("minmax") && conf.Get("graph")) { cout << "Invalid combination of options: cannot graph minmax. Aborting" << endl; return -1; } #endif if (conf.Get("stat") && conf.Get("nozero")) { cout << "Invalid combination of options: nozero only works with minmax. Aborting" << endl; return -1; } if (conf.Get("nozero")) { fNoZeroPlease = true; } if (conf.Has("tablename")) { if (!OpenTable(conf.Get("tablename"))) return -1; } if (conf.Get("list")) List(); /* if (conf.Get("tstart")) { doTBoundary(conf.Get("outfile"), conf.Get("precision"), true); } if (conf.Get("tstop")) { doTBoundary(conf.Get("outfile"), conf.Get("precision"), false); } */ if (conf.Get("minmax")) { if (!conf.Has("col")) { cout << "Please specify the columns that should be dumped as arguments. Aborting" << endl; return 0; } doMinMaxPlease(conf.Get("outfile"), conf.Get>("col"), conf.Get("precision")); return 0; } if (conf.Get("stat")) { if (!conf.Has("col")) { cout << "Please specify the columns that should be dumped as arguments. Aborting" << endl; return 0; } doStatsPlease(conf.Get("outfile"), conf.Get>("col"), conf.Get("precision")); return 0; } #ifdef PLOTTING_PLEASE if (conf.Get("graph")) { if (!conf.Has("col")) { cout << "Please specify the columns that should be dumped as arguments. Aborting" << endl; return 0; } if (conf.Get("dots")) fDotsPlease = true; doCurvesDisplay(conf.Get>("col"), conf.Get("tablename")); return 1; } #endif if (conf.Get("header")) ListHeader(conf.Get("outfile")); if (conf.Get("header") || conf.Get("list")) return 1; if (conf.Has("outfile")) { if (!conf.Has("col")) { cout << "Please specify the columns that should be dumped as arguments. Aborting" << endl; return 0; } if (!Dump(conf.Get("outfile"), conf.Get>("col"), conf.Get("precision"))) return -1; } return 0; } void PrintUsage() { cout << "fitsdump is a tool to dump data from a FITS table as ascii.\n" "\n" "Usage: fitsdump [OPTIONS] fitsfile col col ... \n" " or: fitsdump [OPTIONS]\n"; cout << endl; } void PrintHelp() { // } struct minMaxStruct { double min; double max; double average; long numValues; minMaxStruct() : min(1e10), max(-1e10), average(0), numValues(0) {} }; int FitsDumper::doMinMaxPlease(const string& filename, const vector& list, int precision) { //first of all, let's separate the columns from their ranges and check that the requested columns are indeed part of the file vector > ranges; vector listNamesOnly; if (!separateColumnsFromRanges(list, ranges, listNamesOnly)) { cerr << "Something went wrong while extracting the columns names from parameters. Aborting" << endl; return false; } ofstream out(filename=="-"?"/dev/stdout":filename); if (!out) { cerr << "Cannot open file " << filename << ": " << strerror(errno) << endl; return false; } out.precision(precision); // Loop over all columns in our list of requested columns vector > columnsData; vector statData; int numRows = fFile->GetInt("NAXIS2"); auto rangesIt = ranges.begin(); for (vector::const_iterator it=listNamesOnly.begin(); it!=listNamesOnly.end(); it++, rangesIt++) { fits::Table::Column& cCol = fColMap.find(*it)->second; columnsData.push_back(make_pair(cCol.type, new char[cCol.num*cCol.size])); // minMaxStuct initData; statData.push_back(minMaxStruct()); fFile->SetPtrAddress(*it, columnsData[columnsData.size()-1].second); } int row = 0; long UTCvalue0=0; long UTCvalue1=0; while (fFile->GetNextRow() && row < numRows) { rangesIt = ranges.begin(); auto statsIt = statData.begin(); for (auto it=columnsData.begin(); it != columnsData.end(); it++, rangesIt++, statsIt++) { double cValue = 0; for (int i=rangesIt->first; isecond; i++) { switch (it->first) { case 'L': cValue = reinterpret_cast(it->second)[i]; break; case 'B': cValue = reinterpret_cast(it->second)[i]; break; case 'I': cValue = reinterpret_cast(it->second)[i]; break; case 'J': cValue = reinterpret_cast(it->second)[i]; break; case 'K': cValue = reinterpret_cast(it->second)[i]; break; case 'E': cValue = reinterpret_cast(it->second)[i]; break; case 'D': cValue = reinterpret_cast(it->second)[i]; break; default: ; } if (list.size() == 1 && (list[0] == "UnixTimeUTC" || list[0] == "PCTime")) { if (i==0) { UTCvalue0 = cValue; } else { UTCvalue1 = cValue; boost::posix_time::ptime unixTimeT( boost::gregorian::date(1970, boost::gregorian::Jan, 1), boost::posix_time::seconds(UTCvalue0) + boost::posix_time::microsec(UTCvalue1)); Time mjdTime(unixTimeT); cValue = mjdTime.Mjd(); if (!fNoZeroPlease || cValue != 0) { statsIt->average += cValue; if (cValue < statsIt->min) statsIt->min = cValue; if (cValue > statsIt->max) statsIt->max = cValue; statsIt->numValues++; } } } else { if (!fNoZeroPlease || cValue != 0) { statsIt->average += cValue; if (cValue < statsIt->min) statsIt->min = cValue; if (cValue > statsIt->max) statsIt->max = cValue; statsIt->numValues++; } } } } row++; } for (auto it = columnsData.begin(); it != columnsData.end(); it++) delete[] it->second; //okay. So now I've got ALL the data, loaded. //let's do the summing and averaging in a safe way (i.e. avoid overflow of variables as much as possible) rangesIt = ranges.begin(); auto statsIt = statData.begin(); auto nameIt = listNamesOnly.begin(); for (auto it=columnsData.begin(); it != columnsData.end(); it++, rangesIt++, statsIt++, nameIt++) { // int span = rangesIt->second - rangesIt->first; cout << *nameIt << ": " << endl; if (statsIt->numValues != 0) { statsIt->average /= statsIt->numValues; out << "min: " << statsIt->min << endl; out << "max: " << statsIt->max << endl; out << "mea: " << statsIt->average << endl; } else { out << "min: 0" << endl << "max: 0" << endl << "mea: " << endl; } } return true; } /* void FitsDumper::doTBoundary(const string& filename, int precision, bool tStop) { //first of all, let's separate the columns from their ranges and check that the requested columns are indeed part of the file vector > ranges; vector listNamesOnly; if (!separateColumnsFromRanges(list, ranges, listNamesOnly)) { cerr << "Something went wrong while extracting the columns names from parameters. Aborting" << endl; return false; } ofstream out(filename=="-"?"/dev/stdout":filename); if (!out) { cerr << "Cannot open file " << filename << ": " << strerror(errno) << endl; return false; } out.precision(precision); // Loop over all columns in our list of requested columns vector > columnsData; vector statData; int numRows = fFile->GetInt("NAXIS2"); auto rangesIt = ranges.begin(); for (vector::const_iterator it=listNamesOnly.begin(); it!=listNamesOnly.end(); it++, rangesIt++) { fits::Table::Column& cCol = fColMap.find(*it)->second; columnsData.push_back(make_pair(cCol.type, new char[cCol.num*cCol.size])); // minMaxStuct initData; statData.push_back(minMaxStruct()); fFile->SetPtrAddress(*it, columnsData[columnsData.size()-1].second); } int row = 0; while (fFile->GetNextRow() && row < numRows) { rangesIt = ranges.begin(); auto statsIt = statData.begin(); for (auto it=columnsData.begin(); it != columnsData.end(); it++, rangesIt++, statsIt++) { double cValue = 0; for (int i=rangesIt->first; isecond; i++) { switch (it->first) { case 'L': cValue = reinterpret_cast(it->second)[i]; break; case 'B': cValue = reinterpret_cast(it->second)[i]; break; case 'I': cValue = reinterpret_cast(it->second)[i]; break; case 'J': cValue = reinterpret_cast(it->second)[i]; break; case 'K': cValue = reinterpret_cast(it->second)[i]; break; case 'E': cValue = reinterpret_cast(it->second)[i]; break; case 'D': cValue = reinterpret_cast(it->second)[i]; break; default: ; } if (!fNoZeroPlease || cValue != 0) { statsIt->average += cValue; if (cValue < statsIt->min) statsIt->min = cValue; if (cValue > statsIt->max) statsIt->max = cValue; statsIt->numValues++; } } } row++; } for (auto it = columnsData.begin(); it != columnsData.end(); it++) delete[] it->second; //okay. So now I've got ALL the data, loaded. //let's do the summing and averaging in a safe way (i.e. avoid overflow of variables as much as possible) rangesIt = ranges.begin(); auto statsIt = statData.begin(); auto nameIt = listNamesOnly.begin(); for (auto it=columnsData.begin(); it != columnsData.end(); it++, rangesIt++, statsIt++, nameIt++) { int span = rangesIt->second - rangesIt->first; cout << *nameIt << ": " << endl; if (statsIt->numValues != 0) { statsIt->average /= statsIt->numValues; out << "min: " << statsIt->min << endl; out << "max: " << statsIt->max << endl; out << "mea: " << statsIt->average << endl; } else { out << "min: 0" << endl << "max: 0" << endl << "mea: " << endl; } } return true; } */ template void displayStats(char* array, int numElems, ofstream& out) { if (numElems == 0) { out << "Min: 0" << endl; out << "Max: 0" << endl; out << "Med: 0" << endl; out << "Mea: 0" << endl; return; } sort(reinterpret_cast(array), &reinterpret_cast(array)[numElems]); out << "Min: " << reinterpret_cast(array)[0] << endl; out << "Max: " << reinterpret_cast(array)[numElems-1] << endl; if (numElems%2 == 0) out << "Med: " << (reinterpret_cast(array)[numElems/2] + reinterpret_cast(array)[numElems/2+1])/2.f << endl; else out << "Med: " << reinterpret_cast(array)[numElems/2+1] << endl; double average = 0; for (int i=0;i(array)[i]; out << "Mea: " << average/(double)numElems << endl; } int FitsDumper::doStatsPlease(const string &filename, const vector& list, int precision) { //first of all, let's separate the columns from their ranges and check that the requested columns are indeed part of the file vector > ranges; vector listNamesOnly; if (!separateColumnsFromRanges(list, ranges, listNamesOnly)) { cerr << "Something went wrong while extracting the columns names from parameters. Aborting" << endl; return false; } ofstream out(filename=="-"?"/dev/stdout":filename); if (!out) { cerr << "Cannot open file " << filename << ": " << strerror(errno) << endl; return false; } out.precision(precision); // Loop over all columns in our list of requested columns vector > columnsData; vector statData; int numRows = fFile->GetInt("NAXIS2"); auto rangesIt = ranges.begin(); for (vector::const_iterator it=listNamesOnly.begin(); it!=listNamesOnly.end(); it++, rangesIt++) { fits::Table::Column& cCol = fColMap.find(*it)->second; columnsData.push_back(make_pair(cCol.type, new char[cCol.num*cCol.size])); statData.push_back(new char[(rangesIt->second - rangesIt->first)*cCol.size*numRows]); fFile->SetPtrAddress(*it, columnsData[columnsData.size()-1].second); } int row = 0; while (fFile->GetNextRow() && row < numRows) { rangesIt = ranges.begin(); auto statsIt = statData.begin(); for (auto it=columnsData.begin(); it != columnsData.end(); it++, rangesIt++, statsIt++) { int span = rangesIt->second - rangesIt->first; for (int i=rangesIt->first; isecond; i++) { switch (it->first) { case 'L': reinterpret_cast(*statsIt)[i - rangesIt->first + row*span] = reinterpret_cast(it->second)[i]; break; case 'B': reinterpret_cast(*statsIt)[i - rangesIt->first + row*span] = reinterpret_cast(it->second)[i]; break; case 'I': reinterpret_cast(*statsIt)[i - rangesIt->first + row*span] = reinterpret_cast(it->second)[i]; break; case 'J': reinterpret_cast(*statsIt)[i - rangesIt->first + row*span] = reinterpret_cast(it->second)[i]; break; case 'K': reinterpret_cast(*statsIt)[i - rangesIt->first + row*span] = reinterpret_cast(it->second)[i]; break; case 'E': reinterpret_cast(*statsIt)[i - rangesIt->first + row*span] = reinterpret_cast(it->second)[i]; break; case 'D': reinterpret_cast(*statsIt)[i - rangesIt->first + row*span] = reinterpret_cast(it->second)[i]; break; default: ; } } } row++; } for (auto it = columnsData.begin(); it != columnsData.end(); it++) delete[] it->second; //okay. So now I've got ALL the data, loaded. //let's do the summing and averaging in a safe way (i.e. avoid overflow of variables as much as possible) rangesIt = ranges.begin(); auto statsIt = statData.begin(); auto nameIt = list.begin(); for (auto it=columnsData.begin(); it != columnsData.end(); it++, rangesIt++, statsIt++, nameIt++) { int span = rangesIt->second - rangesIt->first; out << *nameIt << ": " << endl; switch (it->first) { case 'L': displayStats(*statsIt, numRows*span, out); break; case 'B': displayStats(*statsIt, numRows*span, out); break; case 'I': displayStats(*statsIt, numRows*span, out); break; case 'J': displayStats(*statsIt, numRows*span, out); break; case 'K': displayStats(*statsIt, numRows*span, out); break; case 'E': displayStats(*statsIt, numRows*span, out); break; case 'D': displayStats(*statsIt, numRows*span, out); break; default: ; } } return true; } #ifdef PLOTTING_PLEASE int FitsDumper::doCurvesDisplay( const vector &list, const string& tableName) { //first of all, let's separate the columns from their ranges and check that the requested columns are indeed part of the file vector > ranges; vector listNamesOnly; if (!separateColumnsFromRanges(list, ranges, listNamesOnly)) { cerr << "Something went wrong while extracting the columns names from parameters. Aborting" << endl; return false; } vector curvesNames; stringstream str; for (auto it=ranges.begin(), jt=listNamesOnly.begin(); it != ranges.end(); it++, jt++) { for (int i=it->first; isecond;i++) { str.str(""); str << *jt << "[" << i << "]"; curvesNames.push_back(str.str()); } } char* handle = new char[17]; sprintf(handle,"FitsDump Display"); // Qt::HANDLE h = *handle;//NULL int argc = 1; char ** argv = &handle; QApplication a(argc, argv); QwtPlot* plot = new QwtPlot(); QwtPlotGrid* grid = new QwtPlotGrid; grid->enableX(false); grid->enableY(true); grid->enableXMin(false); grid->enableYMin(false); grid->setMajPen(QPen(Qt::black, 0, Qt::DotLine)); grid->attach(plot); plot->setAutoReplot(true); string title = tableName; plot->setAxisScaleDraw( QwtPlot::xBottom, new TimeScaleDraw()); QWidget window; QHBoxLayout* layout = new QHBoxLayout(&window); layout->setContentsMargins(0,0,0,0); layout->addWidget(plot); QwtPlotZoomer zoom(plot->canvas()); zoom.setRubberBandPen(QPen(Qt::gray, 2, Qt::DotLine)); zoom.setTrackerPen(QPen(Qt::gray)); int totalSize = 0; for (unsigned int i=0;i curves(totalSize); int ii=0; for (auto it = curves.begin(), jt=curvesNames.begin(); it != curves.end(); it++, jt++) { *it = new QwtPlotCurve(jt->c_str()); switch (ii%6) { case 0: (*it)->setPen(QColor(255,0,0)); break; case 1: (*it)->setPen(QColor(0,255,0)); break; case 2: (*it)->setPen(QColor(0,0,255)); break; case 3: (*it)->setPen(QColor(255,255,0)); break; case 4: (*it)->setPen(QColor(0,255,255)); break; case 5: (*it)->setPen(QColor(255,0,255)); break; default: (*it)->setPen(QColor(0,0,0)); }; ii++; if (fDotsPlease) (*it)->setStyle(QwtPlotCurve::Dots); else (*it)->setStyle(QwtPlotCurve::Lines); (*it)->attach(plot); } plot->insertLegend(new QwtLegend(), QwtPlot::RightLegend); vector > columnsData; for (vector::const_iterator it=listNamesOnly.begin(); it!=listNamesOnly.end(); it++) { fits::Table::Column& cCol = fColMap.find(*it)->second; columnsData.push_back(make_pair(cCol.type, new char[cCol.num*cCol.size])); fFile->SetPtrAddress(*it, columnsData[columnsData.size()-1].second); } //add the time column to the given columns if (fColMap.find("Time") == fColMap.end() && fColMap.find("UnixTimeUTC") == fColMap.end()) { cerr << "Error: time column could not be found in given table. Aborting" << endl; return false; } const fits::Table::Column& timeCol = (fColMap.find("Time") != fColMap.end()) ? fColMap.find("Time")->second : fColMap.find("UnixTimeUTC")->second; bool unixTime = (fColMap.find("Time") == fColMap.end()); if (unixTime) ranges.push_back(make_pair(0,2)); else ranges.push_back(make_pair(0,1)); columnsData.push_back(make_pair(timeCol.type, new char[timeCol.num*timeCol.size])); fFile->SetPtrAddress(unixTime ? "UnixTimeUTC" : "Time", columnsData[columnsData.size()-1].second); // stringstream str; str.str(""); vector xValues(totalSize); double* yValues; cout.precision(10); str.precision(20); for (auto it=xValues.begin(); it!=xValues.end(); it++) *it = new double[fFile->GetInt("NAXIS2")]; yValues = new double[fFile->GetInt("NAXIS2")]; cout.precision(3); int endIndex = 0; int numRows = fFile->GetInt("NAXIS2"); for (int i=1;iGetNextRow(); cout << "\r" << "Constructing graph " << ((float)(endIndex)/(float)(fFile->GetInt("NAXIS2")))*100.0 << "%"; endIndex++; auto rangesIt = ranges.begin(); for (auto it=columnsData.begin(); it != columnsData.end(); it++, rangesIt++) { for (int j=rangesIt->first; jsecond; j++) { switch (it->first) { case 'L': str << reinterpret_cast(it->second)[j] << " "; break; case 'B': str << reinterpret_cast(it->second)[j] << " "; break; case 'I': str << reinterpret_cast(it->second)[j] << " "; break; case 'J': str << reinterpret_cast(it->second)[j] << " "; break; case 'K': str << reinterpret_cast(it->second)[j] << " "; break; case 'E': str << reinterpret_cast(it->second)[j] << " "; break; case 'D': str << reinterpret_cast(it->second)[j] << " "; break; default: ; } } } for (auto it=xValues.begin(); it!= xValues.end(); it++) { str >> (*it)[i-1]; } if (unixTime) { long u1, u2; str >> u1 >> u2; boost::posix_time::ptime unixTimeT( boost::gregorian::date(1970, boost::gregorian::Jan, 1), boost::posix_time::seconds(u1) + boost::posix_time::microsec(u2)); Time mjdTime(unixTimeT); yValues[i-1] = mjdTime.Mjd(); if (yValues[i-1] < 40587) yValues[i-1] += 40587; } else { str >> yValues[i-1]; if (yValues[i-1] < 40587) yValues[i-1] += 40587; Time t(yValues[i-1]); string time = t.GetAsStr("%H:%M:%S%F"); while (time[time.size()-1] == '0' && time.size() > 2) { time = time.substr(0, time.size()-1); } } if (i==1) { Time t(yValues[0]); title += " - " + t.GetAsStr("%Y-%m-%d"); plot->setTitle(title.c_str()); } } //set the actual data. auto jt = xValues.begin(); for (auto it=curves.begin(); it != curves.end(); it++, jt++) (*it)->setRawData(yValues, *jt, endIndex-1); QStack stack; double minX, minY, maxX, maxY; minX = minY = 1e10; maxX = maxY = -1e10; QRectF rect; QPointF point; for (auto it=curves.begin(); it!= curves.end(); it++) { rect = (*it)->boundingRect(); point = rect.bottomRight(); if (point.x() < minX) minX = point.x(); if (point.y() < minY) minY = point.y(); if (point.x() > maxX) maxX = point.x(); if (point.y() > maxY) maxY = point.y(); point = rect.topLeft(); if (point.x() < minX) minX = point.x(); if (point.y() < minY) minY = point.y(); if (point.x() > maxX) maxX = point.x(); if (point.y() > maxY) maxY = point.y(); } QPointF bottomRight(maxX, minY); QPointF topLeft(minX, maxY); QPointF center((bottomRight+topLeft)/2.f); stack.push(QRectF(topLeft + (topLeft-center)*(.5f),bottomRight + (bottomRight-center)*(.5f))); zoom.setZoomStack(stack); // delete[] fitsBuffer; for (auto it = columnsData.begin(); it != columnsData.end(); it++) delete[] it->second; window.resize(600, 400); window.show(); a.exec(); for (auto it = curves.begin(); it != curves.end(); it++) { (*it)->detach(); delete *it; } grid->detach(); for (auto it = xValues.begin(); it != xValues.end(); it++) delete[] *it; delete[] yValues; delete[] handle; return 0; } #endif void SetupConfiguration(Configuration& conf) { po::options_description configs("Fitsdump options"); configs.add_options() ("fitsfile,f", var() #if BOOST_VERSION >= 104200 ->required() #endif , "Name of FITS file") ("tablename,t", var("DATA") #if BOOST_VERSION >= 104200 ->required() #endif , "Name of input table") ("col,c", vars(), "List of columns to dump\narg is a list of columns, separated by a space.\nAdditionnally, a list of sub-columns can be added\ne.g. Data[3] will dump sub-column 3 of column Data\nData[3:4] will dump sub-columns 3 and 4\nOmitting this argument dump the entire column\nnota: all indices start at zero") ("outfile,o", var("/dev/stdout"), "Name of output file (-:/dev/stdout)") ("precision,p", var(20), "Precision of ofstream") ("list,l", po_switch(), "List all tables and columns in file") ("header,h", po_switch(), "Dump header of given table") ("stat,s", po_switch(), "Perform statistics instead of dump") ("minmax,m", po_switch(), "Calculates min and max of data") ("nozero,z", po_switch(), "skip 0 values for stats") ("tstart,a", po_switch(), "Give the mjdStart from reading the file data") ("tstop,b", po_switch(), "Give the mjdStop from reading the file data") #ifdef PLOTTING_PLEASE ("graph,g", po_switch(), "Plot the columns instead of dumping them") ("dots,d", po_switch(), "Plot using dots instead of lines") #endif ; po::positional_options_description p; p.add("fitsfile", 1); // The first positional options p.add("col", -1); // All others conf.AddOptions(configs); conf.SetArgumentPositions(p); } int main(int argc, const char** argv) { Configuration conf(argv[0]); conf.SetPrintUsage(PrintUsage); SetupConfiguration(conf); if (!conf.DoParse(argc, argv, PrintHelp)) return -1; FitsDumper loader; return loader.ExecConfig(conf); }