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