File indexing completed on 2024-12-22 04:17:14

0001 /***************************************************************************
0002  *                                                                         *
0003  *   copyright : (C) 2007 The University of Toronto                        *
0004  *                   netterfield@astro.utoronto.ca                         *
0005  *                                                                         *
0006  *   This program is free software; you can redistribute it and/or modify  *
0007  *   it under the terms of the GNU General Public License as published by  *
0008  *   the Free Software Foundation; either version 2 of the License, or     *
0009  *   (at your option) any later version.                                   *
0010  *                                                                         *
0011  ***************************************************************************/
0012 
0013 #include "fitsimage.h"
0014 
0015 #include <QXmlStreamWriter>
0016 
0017 #include <math.h>
0018 #include <QHash>
0019 
0020 
0021 
0022 using namespace Kst;
0023 
0024 static const QString fitsTypeString = "FITS image";
0025 static const QString DefaultMatrixName = "1";
0026 
0027 class FitsImageSource::Config {
0028   public:
0029     Config() {
0030     }
0031 
0032     void read(QSettings *cfg, const QString& fileName = QString()) {
0033       Q_UNUSED(fileName);
0034       cfg->beginGroup(fitsTypeString);
0035       cfg->endGroup();
0036     }
0037 
0038     void save(QXmlStreamWriter& s) {
0039       Q_UNUSED(s);
0040     }
0041 
0042     void load(const QDomElement& e) {
0043       Q_UNUSED(e);
0044     }
0045 };
0046 
0047 
0048 
0049 //
0050 // String interface
0051 //
0052 
0053 class DataInterfaceFitsImageString : public DataSource::DataInterface<DataString>
0054 {
0055 public:
0056   DataInterfaceFitsImageString(FitsImageSource& s) : source(s) {}
0057 
0058   // read one element
0059   int read(const QString&, DataString::ReadInfo&);
0060 
0061   // named elements
0062   QStringList list() const { return source._strings.keys(); }
0063   bool isListComplete() const { return true; }
0064   bool isValid(const QString&) const;
0065 
0066   // T specific
0067   const DataString::DataInfo dataInfo(const QString&, int frame=0) const { Q_UNUSED(frame) return DataString::DataInfo(); }
0068   void setDataInfo(const QString&, const DataString::DataInfo&) {}
0069 
0070   // meta data
0071   QMap<QString, double> metaScalars(const QString&) { return QMap<QString, double>(); }
0072   QMap<QString, QString> metaStrings(const QString&) { return QMap<QString, QString>(); }
0073 
0074 
0075 private:
0076   FitsImageSource& source;
0077 };
0078 
0079 
0080 //-------------------------------------------------------------------------------------------
0081 int DataInterfaceFitsImageString::read(const QString& string, DataString::ReadInfo& p)
0082 {
0083   // TODO read strings from file
0084   if (isValid(string) && p.value) {
0085     *p.value = source._strings[string];
0086     return 1;
0087   }
0088   return 0;
0089 }
0090 
0091 
0092 //-------------------------------------------------------------------------------------------
0093 bool DataInterfaceFitsImageString::isValid(const QString& string) const
0094 {
0095   // TODO read strings from file
0096   return  source._strings.contains( string );
0097 }
0098 
0099 
0100 
0101 //
0102 // Matrix interface
0103 //
0104 
0105 
0106 class DataInterfaceFitsImageMatrix : public DataSource::DataInterface<DataMatrix> {
0107 public:
0108 
0109   DataInterfaceFitsImageMatrix(fitsfile **fitsfileptr) : _fitsfileptr(fitsfileptr) {}
0110 
0111   // read one element
0112   int read(const QString&, DataMatrix::ReadInfo&);
0113 
0114   // named elements
0115   QStringList list() const { return _matrixHash.keys(); }
0116   bool isListComplete() const { return true; }
0117   bool isValid(const QString&) const;
0118 
0119   // T specific
0120   const DataMatrix::DataInfo dataInfo(const QString&, int frame=0) const;
0121   void setDataInfo(const QString&, const DataMatrix::DataInfo&) {}
0122 
0123   // meta data
0124   QMap<QString, double> metaScalars(const QString &m);
0125   QMap<QString, QString> metaStrings(const QString &m);
0126 
0127 
0128   // no interface
0129   fitsfile **_fitsfileptr;
0130   QHash<QString,int> _matrixHash;
0131 
0132   void init();
0133   void clear();
0134 };
0135 
0136 void DataInterfaceFitsImageMatrix::clear()
0137 {
0138   _matrixHash.clear();
0139 }
0140 
0141 void DataInterfaceFitsImageMatrix::init()
0142 {
0143   int hdu;
0144   int nhdu;
0145   int status=0;
0146   int type;
0147   QString name;
0148   char instr[32];
0149   char tmpstr[1024];
0150 
0151   fits_get_hdu_num(*_fitsfileptr, &hdu);
0152 
0153   _matrixHash.insert(DefaultMatrixName, hdu);
0154 
0155   fits_get_num_hdus(*_fitsfileptr, &nhdu, &status);
0156   for (hdu = 1; hdu <= nhdu; ++hdu) {
0157     fits_movabs_hdu(*_fitsfileptr, hdu, &type, &status);
0158     fits_get_hdu_type(*_fitsfileptr, &type, &status);
0159     if (type == IMAGE_HDU) {
0160       fits_read_key_str(*_fitsfileptr, "EXTNAME", instr, tmpstr, &status);
0161       if (status) {
0162         name = QString("HDU%1").arg(hdu);
0163       } else {
0164         name = QString(instr).trimmed();
0165       }
0166       _matrixHash.insert(name, hdu);
0167     }
0168   }
0169 }
0170 
0171 const DataMatrix::DataInfo DataInterfaceFitsImageMatrix::dataInfo(const QString& matrix, int frame) const
0172 {
0173   Q_UNUSED(frame)
0174   long n_axes[3];
0175   int status = 0;
0176   int type;
0177 
0178   if ( !*_fitsfileptr || !_matrixHash.contains( matrix ) ) {
0179     return DataMatrix::DataInfo();
0180   }
0181 
0182   fits_movabs_hdu(*_fitsfileptr, _matrixHash[matrix], &type, &status);
0183 
0184   fits_get_img_size( *_fitsfileptr,  2,  n_axes,  &status );
0185 
0186   if (status) {
0187     return DataMatrix::DataInfo();
0188   }
0189 
0190   DataMatrix::DataInfo info;
0191   info.xSize = n_axes[0];
0192   info.ySize = n_axes[1];
0193 
0194   char charCDelt1[] = "CDELT1";
0195   char charCDelt2[] = "CDELT2";
0196   double dx,dy;
0197   fits_read_key(*_fitsfileptr, TDOUBLE, charCDelt1, &dx, NULL, &status);
0198   fits_read_key(*_fitsfileptr, TDOUBLE, charCDelt2, &dy, NULL, &status);
0199 
0200   if (!status) {
0201     info.invertXHint = (dx<0);
0202     info.invertYHint = (dy<0);
0203   }
0204 
0205   return info;
0206 }
0207 
0208 QMap<QString, double> DataInterfaceFitsImageMatrix::metaScalars(const QString &matrix) {
0209   qDebug() << "metascalars for " << matrix;
0210   QMap<QString, double> M;
0211 
0212   return M;
0213 }
0214 
0215 QMap<QString, QString> DataInterfaceFitsImageMatrix::metaStrings(const QString &matrix) {
0216 
0217   QMap<QString, QString> M;
0218   int status = 0;
0219   int type;
0220   char instr[128];
0221 
0222   M.clear();
0223 
0224   if ( !*_fitsfileptr || !_matrixHash.contains( matrix ) ) {
0225     return M;
0226   }
0227 
0228   fits_movabs_hdu(*_fitsfileptr, _matrixHash[matrix], &type, &status);
0229 
0230   fits_read_key(*_fitsfileptr, TSTRING, "CTYPE1", instr, NULL, &status);
0231   if (!status) {
0232     M.insert("x_quantity", QString(instr).trimmed());
0233   }
0234   status = 0;
0235   fits_read_key(*_fitsfileptr, TSTRING, "CTYPE2", instr, NULL, &status);
0236   if (!status) {
0237     M.insert("y_quantity", QString(instr).trimmed());
0238   }
0239   status = 0;
0240   fits_read_key(*_fitsfileptr, TSTRING, "CRUNIT1", instr, NULL, &status);
0241   if (!status) {
0242     M.insert("x_units", QString(instr).trimmed());
0243   }
0244   status = 0;
0245   fits_read_key(*_fitsfileptr, TSTRING, "CRUNIT2", instr, NULL, &status);
0246   if (!status) {
0247     M.insert("y_units", QString(instr).trimmed());
0248   }
0249   status = 0;
0250   fits_read_key(*_fitsfileptr, TSTRING, "BUNIT", instr, NULL, &status);
0251   if (!status) {
0252     M.insert("z_units", QString(instr).trimmed());
0253   }
0254   status = 0;
0255 
0256   return M;
0257 }
0258 
0259 int DataInterfaceFitsImageMatrix::read(const QString& field, DataMatrix::ReadInfo& p) {
0260   long n_axes[2],  fpixel[2] = {1, 1};
0261   double nullval = NAN;
0262   double blank = 0.0;
0263   long n_elements;
0264   int px, py,  anynull;
0265   int status = 0, type;
0266   double *buffer;
0267 
0268   if ((!*_fitsfileptr) || (!_matrixHash.contains(field))) {
0269     return 0;
0270   }
0271 
0272   fits_movabs_hdu(*_fitsfileptr, _matrixHash[field], &type, &status);
0273 
0274   fits_get_img_size( *_fitsfileptr,  2,  n_axes,  &status );
0275 
0276   if (status) {
0277     return 0;
0278   }
0279 
0280   n_elements = n_axes[0]*n_axes[1];
0281   buffer = (double*)malloc(n_elements*sizeof(double));
0282 
0283   fits_read_pix( *_fitsfileptr,  TDOUBLE, fpixel, n_elements, &nullval, buffer, &anynull,  &status );
0284 
0285   // Check to see if the file is using the BLANK keyword
0286   // to indicate the NULL value for the image.  This is
0287   // not correct useage for floating point images, but
0288   // it is used frequently nonetheless...
0289   char charBlank[] = "BLANK";
0290   fits_read_key(*_fitsfileptr, TDOUBLE, charBlank, &blank, NULL, &status);
0291   if (status) { //keyword does not exist, ignore it
0292     status = 0;
0293   } else { //keyword is used, replace pixels with this value
0294     double epsilon = fabs(1e-4 * blank);
0295     for (long j = 0; j < n_elements; j++) {
0296       if (fabs(buffer[j]-blank) < epsilon) {
0297         buffer[j] = NAN;
0298       }
0299     }
0300   }
0301 
0302   int y0 = p.yStart;
0303   int y1 = p.yStart + p.yNumSteps;
0304   int x0 = p.xStart;
0305   int x1 = p.xStart + p.xNumSteps;
0306   double* z = p.data->z;
0307 
0308   int ni = p.xNumSteps * p.yNumSteps - 1;
0309 
0310   // set the suggested matrix transform params: pixel index....
0311   double x, y, dx, dy, cx, cy;
0312   char charCRVal1[] = "CRVAL1";
0313   char charCRVal2[] = "CRVAL2";
0314   char charCDelt1[] = "CDELT1";
0315   char charCDelt2[] = "CDELT2";
0316   char charCRPix1[] = "CRPIX1";
0317   char charCRPix2[] = "CRPIX2";
0318   fits_read_key(*_fitsfileptr, TDOUBLE, charCRVal1, &x, NULL, &status);
0319   fits_read_key(*_fitsfileptr, TDOUBLE, charCRVal2, &y, NULL, &status);
0320   fits_read_key(*_fitsfileptr, TDOUBLE, charCDelt1, &dx, NULL, &status);
0321   fits_read_key(*_fitsfileptr, TDOUBLE, charCDelt2, &dy, NULL, &status);
0322   fits_read_key(*_fitsfileptr, TDOUBLE, charCRPix1, &cx, NULL, &status);
0323   fits_read_key(*_fitsfileptr, TDOUBLE, charCRPix2, &cy, NULL, &status);
0324 
0325   int i = 0;
0326 
0327   if ((dx<0) && (dy>0)) {
0328     for (px = p.xStart; px < x1; ++px) {
0329       for (py = y1-1; py >= p.yStart; --py) {
0330         z[ni - i] = buffer[px + py*n_axes[0]];
0331         i++;
0332       }
0333     }
0334   } else if ((dx>0) && (dy>0)) {
0335     for (px = x1-1; px >= p.xStart; --px) {
0336       for (py = y1-1; py >= p.yStart; --py) {
0337         z[ni - i] = buffer[px + py*n_axes[0]];
0338         i++;
0339       }
0340     }
0341   } else if ((dx>0) && (dy<0)) {
0342     for (px = x1-1; px >= p.xStart; --px) {
0343       for (py = p.yStart; py < y1; ++py) {
0344         z[ni - i] = buffer[px + py*n_axes[0]];
0345         i++;
0346       }
0347     }
0348   } else if ((dx<0) && (dy<0)) {
0349     for (px = p.xStart; px < x1; ++px) {
0350       for (py = p.yStart; py < y1; ++py) {
0351         z[ni - i] = buffer[px + py*n_axes[0]];
0352         i++;
0353       }
0354     }
0355   }
0356   free(buffer);
0357 
0358   if (status) {
0359     p.data->xMin = x0;
0360     p.data->yMin = y0;
0361     p.data->xStepSize = 1;
0362     p.data->yStepSize = 1;
0363   } else {
0364     dx = fabs(dx);
0365     dy = fabs(dy);
0366     p.data->xStepSize = dx;
0367     p.data->yStepSize = dy;
0368     p.data->xMin = x - cx*dx;
0369     p.data->yMin = y - cy*dy;
0370   }
0371 
0372   return(i);
0373 }
0374 
0375 bool DataInterfaceFitsImageMatrix::isValid(const QString& field) const {
0376   return  _matrixHash.contains( field );
0377 }
0378 
0379 
0380 FitsImageSource::FitsImageSource(Kst::ObjectStore *store, QSettings *cfg, const QString& filename, const QString& type, const QDomElement& e)
0381 : Kst::DataSource(store, cfg, filename, type),
0382   _config(0L),
0383   is(new DataInterfaceFitsImageString(*this)),
0384   im(new DataInterfaceFitsImageMatrix(&_fptr))
0385 {
0386   setInterface(is);
0387   setInterface(im);
0388 
0389   setUpdateType(None);
0390 
0391   _fptr = 0L;
0392   _valid = false;
0393 
0394   if (!type.isEmpty() && type != fitsTypeString) {
0395     return;
0396   }
0397 
0398   _config = new FitsImageSource::Config;
0399   _config->read(cfg, filename);
0400   if (!e.isNull()) {
0401     _config->load(e);
0402   }
0403 
0404   if (init()) {
0405     _valid = true;
0406   }
0407 
0408   registerChange();
0409 }
0410 
0411 
0412 
0413 FitsImageSource::~FitsImageSource() {
0414   int status = 0;
0415   if (_fptr) {
0416     fits_close_file( _fptr, &status );
0417     _fptr = 0L;
0418   }
0419   delete _config;
0420   _config = 0L;
0421 }
0422 
0423 const QString& FitsImageSource::typeString() const {
0424   return fitsTypeString;
0425 }
0426 
0427 
0428 
0429 void FitsImageSource::reset() {
0430   init();
0431   Object::reset();
0432 }
0433 
0434 bool FitsImageSource::init() {
0435   int status = 0;
0436   fits_open_image( &_fptr, _filename.toAscii(), READONLY, &status );
0437 
0438   im->clear();
0439   _strings = fileMetas();
0440   if (status == 0) {
0441     im->init();
0442 
0443     registerChange();
0444     return true;
0445   } else {
0446     fits_close_file( _fptr, &status );
0447     _fptr = 0L;
0448     return false;
0449   }
0450 }
0451 
0452 
0453 Kst::Object::UpdateType FitsImageSource::internalDataSourceUpdate() {
0454   return (Kst::Object::NoChange);
0455 }
0456 
0457 
0458 bool FitsImageSource::isEmpty() const {
0459   return im->dataInfo(DefaultMatrixName,0).xSize < 1;
0460 }
0461 
0462 
0463 QString FitsImageSource::fileType() const {
0464   return fitsTypeString;
0465 }
0466 
0467 
0468 void FitsImageSource::save(QXmlStreamWriter &streamWriter) {
0469   Kst::DataSource::save(streamWriter);
0470 }
0471 
0472 
0473 QString FitsImagePlugin::pluginName() const { return tr("FITS Image Source Reader"); }
0474 QString FitsImagePlugin::pluginDescription() const { return tr("FITS Image Source Reader"); }
0475 
0476 
0477 Kst::DataSource *FitsImagePlugin::create(Kst::ObjectStore *store,
0478                                             QSettings *cfg,
0479                                             const QString &filename,
0480                                             const QString &type,
0481                                             const QDomElement &element) const {
0482 
0483   return new FitsImageSource(store, cfg, filename, type, element);
0484 }
0485 
0486 
0487 
0488 QStringList FitsImagePlugin::matrixList(QSettings *cfg,
0489                                              const QString& filename,
0490                                              const QString& type,
0491                                              QString *typeSuggestion,
0492                                              bool *complete) const {
0493   Q_UNUSED(type)
0494   QStringList matrixList;
0495 
0496   if (complete) {
0497     *complete = true;
0498   }
0499 
0500   if (typeSuggestion) {
0501     *typeSuggestion = fitsTypeString;
0502   }
0503 
0504   if ( understands(cfg, filename) ) {
0505     fitsfile* ffits;
0506     int status = 0;
0507     int hdu;
0508     int nhdu;
0509     int type;
0510     QString name;
0511     char instr[32];
0512     char tmpstr[1024];
0513 
0514 
0515     fits_open_image( &ffits, filename.toAscii(), READONLY, &status );
0516     matrixList.append( DefaultMatrixName );
0517 
0518     fits_get_num_hdus(ffits, &nhdu, &status);
0519     for (hdu = 1; hdu <= nhdu; ++hdu) {
0520       fits_movabs_hdu(ffits, hdu, &type, &status);
0521       fits_get_hdu_type(ffits, &type, &status);
0522       if (type == IMAGE_HDU) {
0523         fits_read_key_str(ffits, "EXTNAME", instr, tmpstr, &status);
0524         if (status) {
0525           name = QString("HDU%1").arg(hdu);
0526         } else {
0527           name = QString(instr).trimmed();
0528         }
0529         matrixList.append(name);
0530       }
0531     }
0532 
0533     fits_close_file( ffits ,  &status );
0534   }
0535   return matrixList;
0536 
0537 }
0538 
0539 
0540 QStringList FitsImagePlugin::scalarList(QSettings *cfg,
0541                                             const QString& filename,
0542                                             const QString& type,
0543                                             QString *typeSuggestion,
0544                                             bool *complete) const {
0545 
0546   QStringList scalarList;
0547 
0548   if ((!type.isEmpty() && !provides().contains(type)) || 0 == understands(cfg, filename)) {
0549     if (complete) {
0550       *complete = false;
0551     }
0552     return QStringList();
0553   }
0554 
0555   if (typeSuggestion) {
0556     *typeSuggestion = fitsTypeString;
0557   }
0558 
0559   scalarList.append("FRAMES");
0560   return scalarList;
0561 
0562 }
0563 
0564 
0565 QStringList FitsImagePlugin::stringList(QSettings *cfg,
0566                                       const QString& filename,
0567                                       const QString& type,
0568                                       QString *typeSuggestion,
0569                                       bool *complete) const {
0570 
0571   QStringList stringList;
0572 
0573   if ((!type.isEmpty() && !provides().contains(type)) || 0 == understands(cfg, filename)) {
0574     if (complete) {
0575       *complete = false;
0576     }
0577     return QStringList();
0578   }
0579 
0580   if (typeSuggestion) {
0581     *typeSuggestion = fitsTypeString;
0582   }
0583 
0584   stringList.append("FILENAME");
0585   return stringList;
0586 
0587 }
0588 
0589 QStringList FitsImagePlugin::fieldList(QSettings *cfg,
0590                                             const QString& filename,
0591                                             const QString& type,
0592                                             QString *typeSuggestion,
0593                                             bool *complete) const {
0594   Q_UNUSED(type)
0595   Q_UNUSED(cfg)
0596   Q_UNUSED(filename)
0597   QStringList fieldList;
0598 
0599   if (complete) {
0600     *complete = true;
0601   }
0602 
0603   if (typeSuggestion) {
0604     *typeSuggestion = fitsTypeString;
0605   }
0606 
0607   return fieldList;
0608 }
0609 
0610 
0611 int FitsImagePlugin::understands(QSettings *cfg, const QString& filename) const {
0612   Q_UNUSED(cfg)
0613   fitsfile* ffits;
0614   int status = 0;
0615   int ret_val = 0;
0616   int naxis;
0617 
0618   fits_open_image( &ffits, filename.toAscii(), READONLY, &status );
0619   fits_get_img_dim( ffits, &naxis,  &status);
0620 
0621   if ((status == 0) && (naxis > 1)) {
0622     ret_val = 95;
0623   } else {
0624     ret_val = 0;
0625   }
0626 
0627   // status !=0 should prevent close from having trouble...
0628   fits_close_file( ffits ,  &status );
0629 
0630   return ret_val;
0631 }
0632 
0633 
0634 
0635 bool FitsImagePlugin::supportsTime(QSettings *cfg, const QString& filename) const {
0636   //FIXME
0637   Q_UNUSED(cfg)
0638   Q_UNUSED(filename)
0639   return false;
0640 }
0641 
0642 
0643 QStringList FitsImagePlugin::provides() const {
0644   QStringList rc;
0645   rc += fitsTypeString;
0646   return rc;
0647 }
0648 
0649 
0650 Kst::DataSourceConfigWidget *FitsImagePlugin::configWidget(QSettings *cfg, const QString& filename) const {
0651 
0652   Q_UNUSED(cfg)
0653   Q_UNUSED(filename)
0654   return 0;;
0655 
0656 }
0657 
0658 #ifndef QT5
0659 Q_EXPORT_PLUGIN2(kstdata_qimagesource, FitsImagePlugin)
0660 #endif
0661 
0662 // vim: ts=2 sw=2 et