9#include "QtNetwork/qnetworkreply.h"
10#include "auxiliary/kspaths.h"
12#include "fitshistogramcommand.h"
14#include "fitsviewer.h"
15#include "ksnotification.h"
18#include "ui_fitsheaderdialog.h"
19#include "ui_statform.h"
20#include "fitsstretchui.h"
26#include "ekos/auxiliary/stellarsolverprofile.h"
27#include "ekos/auxiliary/stellarsolverprofileeditor.h"
29#include <fits_debug.h>
30#include "fitscommon.h"
31#include <QDesktopServices>
45 undoStack->setUndoLimit(10);
47 connect(undoStack, SIGNAL(cleanChanged(
bool)),
this, SLOT(modifyFITSState(
bool)));
49 m_PlateSolveWidget =
new QDialog(
this);
50 m_CatalogObjectWidget =
new QDialog(
this);
52 fitsHeaderDialog =
new QDialog(
this);
53 m_HistogramEditor =
new FITSHistogramEditor(
this);
54 connect(m_HistogramEditor, &FITSHistogramEditor::newHistogramCommand,
this, [
this](FITSHistogramCommand * command)
56 undoStack->push(command);
64void FITSTab::saveUnsaved()
66 if (undoStack->
isClean() || m_View->getMode() != FITS_NORMAL)
70 QString message =
i18n(
"The current FITS file has unsaved changes. Would you like to save before closing it?");
92QString FITSTab::getPreviewText()
const
97void FITSTab::setPreviewText(
const QString &value)
102void FITSTab::selectRecentFITS(
int i)
107void FITSTab::clearRecentFITS()
110 recentImages->clear();
114bool FITSTab::setupView(FITSMode mode, FITSScale filter)
118 m_View.
reset(
new FITSView(
this, mode, filter));
122 connect(m_View.
get(), &FITSView::rectangleUpdated,
this, [
this](
QRect roi)
124 displayStats(roi.isValid());
129 stat.setupUi(statWidget);
130 m_PlateSolveUI.setupUi(m_PlateSolveWidget);
135 const QString EditorID =
"FITSSolverProfileEditor";
139 m_EditorDialog =
new KConfigDialog(
nullptr, EditorID, Options::self());
140 m_ProfileEditor =
new Ekos::StellarSolverProfileEditor(
nullptr, Ekos::AlignProfiles, m_EditorDialog.
data());
141 m_ProfileEditorPage = m_EditorDialog->addPage(m_ProfileEditor.
data(),
142 i18n(
"FITS Viewer Solver Profiles Editor"));
147 m_ProfileEditor->loadProfile(m_PlateSolveUI.kcfg_FitsSolverProfile->currentText());
148 KConfigDialog * d = KConfigDialog::exists(EditorID);
151 d->setCurrentPage(m_ProfileEditorPage);
158 for (
int i = 0; i <= STAT_STDDEV; i++)
160 for (
int j = 0; j < 3; j++)
168 stat.statsTable->setSpan(i, 0, 1, 3);
171 fitsTools->addItem(statWidget,
i18n(
"Statistics"));
173 fitsTools->addItem(m_PlateSolveWidget,
i18n(
"Plate Solving"));
177 m_CatalogObjectUI.setupUi(m_CatalogObjectWidget);
178 m_CatalogObjectItem = fitsTools->addItem(m_CatalogObjectWidget,
i18n(
"Catalog Objects"));
181 fitsTools->addItem(m_HistogramEditor,
i18n(
"Histogram"));
183 header.setupUi(fitsHeaderDialog);
184 fitsTools->addItem(fitsHeaderDialog,
i18n(
"FITS Header"));
185 connect(m_View.
get(), &FITSView::headerChanged,
this, &FITSTab::loadFITSHeader);
189 recentPanel->
setLayout(recentPanelLayout);
190 fitsTools->addItem(recentPanel,
i18n(
"Recent Images"));
192 recentPanelLayout->
addWidget(recentImages);
194 recentPanelLayout->
addWidget(clearRecent);
202 fitsSplitter->addWidget(scrollFitsPanel);
203 fitsSplitter->addWidget(m_View.
get());
207 fitsSplitter->setSizes(
QList<int>() << 0 << m_View->width() );
211 stretchUI.reset(
new FITSStretchUI(m_View,
nullptr));
217 connect(m_View.
get(), &FITSView::newStatus,
this, &FITSTab::newStatus);
218 connect(m_View.
get(), &FITSView::debayerToggled,
this, &FITSTab::debayerToggled);
219 connect(m_View.
get(), &FITSView::updated,
this, &FITSTab::updated);
222 connect(m_View.
get(), &FITSView::failed,
this, &FITSTab::failed);
231void FITSTab::loadFile(
const QUrl &imageURL, FITSMode mode, FITSScale filter)
240 if (setupView(mode, filter))
244 connect(m_View.
get(), &FITSView::loaded,
this, [&]()
250 connect(m_View.
get(), &FITSView::updated,
this, &FITSTab::updated);
254 modifyFITSState(
true, imageURL);
256 currentURL = imageURL;
258 m_View->setFilter(filter);
263bool FITSTab::shouldComputeHFR()
const
265 if (viewer->isStarsMarked())
267 if (!Options::autoHFR())
269 return ((!m_View.
isNull()) && (m_View->getMode() == FITS_NORMAL));
272void FITSTab::processData()
276 m_HistogramEditor->setImageData(imageData);
286 if (shouldComputeHFR())
288 m_View->searchStars();
289 qCDebug(KSTARS_FITS) <<
"FITS HFR:" << imageData->getHFR();
303 &FITSTab::selectRecentFITS);
304 recentImages->addItem(imageData->filename());
305 recentImages->setCurrentRow(recentImages->count() - 1);
307 &FITSTab::selectRecentFITS);
315 if (viewer->isStarsMarked())
317 m_View->toggleStars(
true);
318 m_View->updateFrame();
324 stretchUI->generateHistogram();
329 setupView(mode, filter);
334 if (viewer->isStarsMarked())
336 m_View->toggleStars(
true);
340 m_View->setFilter(filter);
342 if (!m_View->loadData(data))
353void FITSTab::modifyFITSState(
bool clean,
const QUrl &imageURL)
357 if (undoStack->
isClean() ==
false)
365 emit changeStatus(clean, imageURL);
368bool FITSTab::saveImage(
const QString &filename)
370 return m_View->saveImage(filename);
373void FITSTab::copyFITS()
378void FITSTab::histoFITS()
380 fitsTools->setCurrentIndex(1);
381 if(m_View->width() > 200)
382 fitsSplitter->setSizes(
QList<int>() << 200 << m_View->width() - 200);
384 fitsSplitter->setSizes(
QList<int>() << 50 << 50);
387void FITSTab::displayStats(
bool roi)
391 stat.statsTable->item(STAT_WIDTH, 0)->setText(
QString::number(imageData->width(roi)));
392 stat.statsTable->item(STAT_HEIGHT, 0)->setText(
QString::number(imageData->height(roi)));
393 stat.statsTable->item(STAT_BITPIX, 0)->setText(
QString::number(imageData->bpp()));
396 stat.statsTable->item(STAT_HFR, 0)->setText(
QString::number(imageData->getHFR(),
'f', 3));
398 stat.statsTable->item(STAT_HFR, 0)->setText(
"---");
400 if (imageData->channels() == 1)
402 for (
int i = STAT_MIN; i <= STAT_STDDEV; i++)
404 if (stat.statsTable->columnSpan(i, 0) != 3)
405 stat.statsTable->setSpan(i, 0, 1, 3);
408 stat.statsTable->horizontalHeaderItem(0)->setText(
i18n(
"Value"));
409 stat.statsTable->hideColumn(1);
410 stat.statsTable->hideColumn(2);
414 for (
int i = STAT_MIN; i <= STAT_STDDEV; i++)
416 if (stat.statsTable->columnSpan(i, 0) != 1)
417 stat.statsTable->setSpan(i, 0, 1, 1);
420 stat.statsTable->horizontalHeaderItem(0)->setText(
i18nc(
"Red",
"R"));
421 stat.statsTable->showColumn(1);
422 stat.statsTable->showColumn(2);
425 if (!Options::nonLinearHistogram() && !imageData->isHistogramConstructed())
426 imageData->constructHistogram();
428 for (
int i = 0; i < imageData->channels(); i++)
430 stat.statsTable->item(STAT_MIN, i)->setText(
QString::number(imageData->getMin(i, roi),
'f', 3));
431 stat.statsTable->item(STAT_MAX, i)->setText(
QString::number(imageData->getMax(i, roi),
'f', 3));
432 stat.statsTable->item(STAT_MEAN, i)->setText(
QString::number(imageData->getMean(i, roi),
'f', 3));
433 stat.statsTable->item(STAT_MEDIAN, i)->setText(
QString::number(imageData->getMedian(i, roi),
'f', 3));
434 stat.statsTable->item(STAT_STDDEV, i)->setText(
QString::number(imageData->getStdDev(i, roi),
'f', 3));
438void FITSTab::statFITS()
440 fitsTools->setCurrentIndex(0);
441 if(m_View->width() > 200)
442 fitsSplitter->setSizes(
QList<int>() << 200 << m_View->width() - 200);
444 fitsSplitter->setSizes(
QList<int>() << 50 << 50);
448void FITSTab::loadFITSHeader()
454 int nkeys = imageData->getRecords().size();
456 header.tableWidget->setRowCount(nkeys);
457 for (
const auto &oneRecord : imageData->getRecords())
461 header.tableWidget->setItem(counter, 0, tempItem);
464 header.tableWidget->setItem(counter, 1, tempItem);
467 header.tableWidget->setItem(counter, 2, tempItem);
471 header.tableWidget->setColumnWidth(0, 100);
472 header.tableWidget->setColumnWidth(1, 100);
473 header.tableWidget->setColumnWidth(2, 250);
476void FITSTab::loadCatalogObjects()
487 m_CatalogObjectUI.tableView->setSortingEnabled(
false);
491 int counter = 0, total = 0, highlightRow = -1;
495 for (
const CatObject &catObject : catObjects)
504 tempItem->
setData(catObject.num, CAT_OBJ_SORT_ROLE);
506 m_CatObjModel.
setItem(counter, CAT_NUM, tempItem);
511 m_CatObjModel.
setItem(counter, CAT_CDSPORTAL, tempItem);
516 m_CatObjModel.
setItem(counter, CAT_SIMBAD, tempItem);
521 m_CatObjModel.
setItem(counter, CAT_NED, tempItem);
525 tempItem->
setData(catObject.name, CAT_OBJ_SORT_ROLE);
526 m_CatObjModel.
setItem(counter, CAT_OBJECT, tempItem);
530 tempItem->
setData(catObject.typeCode, CAT_OBJ_SORT_ROLE);
531 m_CatObjModel.
setItem(counter, CAT_TYPECODE, tempItem);
535 tempItem->
setData(catObject.typeLabel, CAT_OBJ_SORT_ROLE);
536 m_CatObjModel.
setItem(counter, CAT_TYPELABEL, tempItem);
540 .
arg(catObject.d.toDMSString(
true,
false,
true));
542 tempItem->
setData(coordStr, CAT_OBJ_SORT_ROLE);
543 m_CatObjModel.
setItem(counter, CAT_COORDS, tempItem);
547 tempItem->
setData(catObject.dist, CAT_OBJ_SORT_ROLE);
549 m_CatObjModel.
setItem(counter, CAT_DISTANCE, tempItem);
552 QString magStr = (catObject.magnitude <= 0.0) ?
"" :
QString(
"%1").arg(catObject.magnitude, 0,
'f', 1);
554 tempItem->
setData(catObject.magnitude, CAT_OBJ_SORT_ROLE);
556 m_CatObjModel.
setItem(counter, CAT_MAGNITUDE, tempItem);
559 QString sizeStr = (catObject.size <= 0.0) ?
"" :
QString(
"%1").arg(catObject.
size, 0,
'f', 0);
561 tempItem->
setData(catObject.size, CAT_OBJ_SORT_ROLE);
563 m_CatObjModel.
setItem(counter, CAT_SIZE, tempItem);
565 if (catObject.highlight)
566 highlightRow = counter;
574 if (highlightRow >= 0)
575 catHighlightRow(highlightRow);
579 m_CatalogObjectUI.tableView->setSortingEnabled(
true);
582 if (!imageData->getCatQueryInProgress())
583 m_CatalogObjectUI.status->setText(
i18n(
"%1 / %2 Simbad objects loaded", counter, total));
586void FITSTab::queriedCatalogObjects()
589 if (fitsTools->currentIndex() != m_CatalogObjectItem)
591 fitsTools->setCurrentIndex(m_CatalogObjectItem);
592 if(m_View->width() > 200)
593 fitsSplitter->setSizes(
QList<int>() << 200 << m_View->width() - 200);
595 fitsSplitter->setSizes(
QList<int>() << 50 << 50);
598 m_CatalogObjectUI.status->setText(
i18n(
"Simbad query in progress..."));
601void FITSTab::catQueryFailed(
const QString text)
603 m_CatalogObjectUI.status->setText(text);
606void FITSTab::catReset()
608 m_CatalogObjectUI.status->setText(
"");
613void FITSTab::catHighlightRow(
const int row)
615 m_CatalogObjectUI.tableView->selectRow(row);
621void FITSTab::catHighlightChanged(
const int highlight)
630 if (highlight < 0 || highlight >= catObjects.
size())
633 int num = catObjects[highlight].num;
634 for (
int i = 0; i < m_CatObjModel.
rowCount(); i++)
640 int itmRow = itm->
row();
641 QModelIndex currentIndex = m_CatalogObjectUI.tableView->currentIndex();
645 if (currentRow == itmRow)
650 catHighlightRow(itmRow);
664 int selNum = -1, deselNum = -1;
673 deselNum = m_CatObjModel.
item(deselItem->
row(), CAT_NUM)->
text().
toInt(&ok);
677 imageData->highlightCatObject(selNum - 1, deselNum - 1);
678 m_View->updateFrame();
682void FITSTab::catCellDoubleClicked(
const QModelIndex &index)
685 int row = item->
row();
690 if (col == CAT_CDSPORTAL)
691 launchCDS(catObjectName);
692 else if (col == CAT_SIMBAD)
693 launchSimbad(catObjectName);
694 else if (col == CAT_NED)
695 launchNED(catObjectName);
696 else if (col == CAT_TYPECODE || col == CAT_TYPELABEL)
697 launchCatTypeFilterDialog();
700void FITSTab::launchCatTypeFilterDialog()
702 m_CatObjTypeFilterDialog->show();
703 m_CatObjTypeFilterDialog->raise();
706void FITSTab::showCatObjNames(
bool enabled)
714 Options::setFitsCatObjShowNames(
enabled);
715 m_View->updateFrame();
718void FITSTab::launchSimbad(
QString name)
720 QUrl url =
QUrl(
"https://simbad.u-strasbg.fr/simbad/sim-id");
727 qCDebug(KSTARS_FITS) <<
"Unable to open Simbad url:" << url;
730void FITSTab::launchCDS(
QString name)
732 QUrl url =
QUrl(
"https://cdsportal.u-strasbg.fr/");
739 qCDebug(KSTARS_FITS) <<
"Unable to open CDS Portal url:" << url;
742void FITSTab::launchNED(
QString name)
744 QUrl url =
QUrl(
"https://ned.ipac.caltech.edu/cgi-bin/objsearch");
746 query.replace(
"+",
"%2B");
751 qCDebug(KSTARS_FITS) <<
"Unable to open NED url" << url;
754void FITSTab::initCatalogObject()
757 m_CatalogObjectUI.tableView->setModel(&m_CatObjModel);
782 i18n(
"Object Type Code. Double click to launch Object Type Filter.\n\nSee https://simbad.cds.unistra.fr/guide/otypes.htx for more details"),
785 i18n(
"Object Type Label. Double click to launch Object Type Filter.\n\nSee https://simbad.cds.unistra.fr/guide/otypes.htx for more details"),
795 m_CatalogObjectUI.tableView->setItemDelegateForColumn(CAT_NUM, delegate);
796 m_CatalogObjectUI.tableView->setItemDelegateForColumn(CAT_CDSPORTAL, delegate);
797 m_CatalogObjectUI.tableView->setItemDelegateForColumn(CAT_SIMBAD, delegate);
798 m_CatalogObjectUI.tableView->setItemDelegateForColumn(CAT_NED, delegate);
799 m_CatalogObjectUI.tableView->setItemDelegateForColumn(CAT_OBJECT, delegate);
800 m_CatalogObjectUI.tableView->setItemDelegateForColumn(CAT_TYPECODE, delegate);
801 m_CatalogObjectUI.tableView->setItemDelegateForColumn(CAT_TYPELABEL, delegate);
802 m_CatalogObjectUI.tableView->setItemDelegateForColumn(CAT_COORDS, delegate);
803 m_CatalogObjectUI.tableView->setItemDelegateForColumn(CAT_DISTANCE, delegate);
804 m_CatalogObjectUI.tableView->setItemDelegateForColumn(CAT_MAGNITUDE, delegate);
805 m_CatalogObjectUI.tableView->setItemDelegateForColumn(CAT_SIZE, delegate);
807 m_CatalogObjectUI.tableView->setAutoScroll(
true);
809 connect(m_View.
get(), &FITSView::catLoaded,
this, &FITSTab::loadCatalogObjects);
810 connect(m_View.
get(), &FITSView::catQueried,
this, &FITSTab::queriedCatalogObjects);
811 connect(m_View.
get(), &FITSView::catQueryFailed,
this, &FITSTab::catQueryFailed);
812 connect(m_View.
get(), &FITSView::catReset,
this, &FITSTab::catReset);
813 connect(m_View.
get(), &FITSView::catHighlightChanged,
this, &FITSTab::catHighlightChanged);
815 &FITSTab::catRowChanged);
820 setupCatObjTypeFilter();
823 m_CatalogObjectUI.kcfg_FitsCatObjShowNames->setChecked(Options::fitsCatObjShowNames());
827void FITSTab::setupCatObjTypeFilter()
830 m_CatObjTypeFilterDialog =
new QDialog(
this);
831 m_CatObjTypeFilterUI.setupUi(m_CatObjTypeFilterDialog);
846 for (
int i = 0; i < MAX_CAT_OBJ_TYPES; i++)
848 const int depth = catObjTypes[i].depth;
860 treeItem[
depth]->
setText(CATTYPE_CODE, catObjTypes[i].code);
861 treeItem[
depth]->
setText(CATTYPE_CANDCODE, catObjTypes[i].candidateCode);
862 treeItem[
depth]->
setText(CATTYPE_LABEL, catObjTypes[i].label);
863 treeItem[
depth]->
setText(CATTYPE_DESCRIPTION, catObjTypes[i].description);
864 treeItem[
depth]->
setText(CATTYPE_COMMENTS, catObjTypes[i].comments);
866 m_CatObjTypeFilterUI.tree->expandAll();
867 for (
int i = 0; i < CAT_OBJ_MAX_DEPTH; i++)
869 m_CatObjTypeFilterUI.tree->resizeColumnToContents(i);
875void FITSTab::applyTypeFilter()
886 for (
auto item : items)
898 m_View->imageData()->setCatObjectsFilters(filters);
900 m_View->imageData()->filterCatObjects();
901 m_View->updateFrame();
903 loadCatalogObjects();
906void FITSTab::checkAllTypeFilter()
910 for (
auto item : items)
916void FITSTab::uncheckAllTypeFilter()
920 for (
auto item : items)
939void FITSTab::headerFITS()
941 fitsTools->setCurrentIndex(2);
942 if(m_View->width() > 200)
943 fitsSplitter->setSizes(
QList<int>() << 200 << m_View->width() - 200);
945 fitsSplitter->setSizes(
QList<int>() << 50 << 50);
948bool FITSTab::saveFile()
950 QUrl backupCurrent = currentURL;
951 QUrl currentDir(Options::fitsDir());
952 currentDir.setScheme(
"file");
958 if (mDirty ==
false && !currentURL.
isEmpty())
967 "Images (*.fits *.fits.gz *.fit *.xisf *.jpg *.jpeg *.png)");
971 "FITS (*.fits *.fits.gz *.fit);;XISF (*.xisf);;JPEG (*.jpg *.jpeg);;PNG (*.png)", &selectedFilter);
976 currentURL = backupCurrent;
983 if (selectedFilter.
contains(
"XISF"))
985 else if (selectedFilter.
contains(
"JPEG"))
987 else if (selectedFilter.
contains(
"PNG"))
1000 if (!saveImage(localFile))
1002 KSNotification::error(
i18n(
"Image save error: %1", m_View->imageData()->getLastError()),
i18n(
"Image Save"));
1006 emit newStatus(
i18n(
"File saved to %1", currentURL.
url()), FITS_MESSAGE);
1013 KSNotification::sorry(message,
i18n(
"Invalid URL"));
1018bool FITSTab::saveFileAs()
1024void FITSTab::ZoomIn()
1026 QPoint oldCenter = m_View->getImagePoint(m_View->viewport()->rect().center());
1028 m_View->cleanUpZoom(oldCenter);
1031void FITSTab::ZoomOut()
1033 QPoint oldCenter = m_View->getImagePoint(m_View->viewport()->rect().center());
1035 m_View->cleanUpZoom(oldCenter);
1038void FITSTab::ZoomDefault()
1040 QPoint oldCenter = m_View->getImagePoint(m_View->viewport()->rect().center());
1041 m_View->ZoomDefault();
1042 m_View->cleanUpZoom(oldCenter);
1045void FITSTab::tabPositionUpdated()
1049 emit newStatus(
QString(
"%1x%2").arg(m_View->imageData()->width()).
arg(m_View->imageData()->height()),
1053void FITSTab::setStretchValues(
double shadows,
double midtones,
double highlights)
1056 stretchUI->setStretchValues(shadows, midtones, highlights);
1059void FITSTab::setAutoStretch()
1061 if (!m_View->getAutoStretch())
1062 m_View->setAutoStretchParams();
1072 case Ekos::AlignProfiles:
1074 savedProfiles =
QDir(KSPaths::writableLocation(
1077 StellarSolver::loadSavedOptionsProfiles(savedProfiles) :
1078 Ekos::getDefaultAlignOptionsProfiles();
1080 case Ekos::FocusProfiles:
1081 savedProfiles =
QDir(KSPaths::writableLocation(
1084 StellarSolver::loadSavedOptionsProfiles(savedProfiles) :
1085 Ekos::getDefaultFocusOptionsProfiles();
1087 case Ekos::GuideProfiles:
1088 savedProfiles =
QDir(KSPaths::writableLocation(
1091 StellarSolver::loadSavedOptionsProfiles(savedProfiles) :
1092 Ekos::getDefaultGuideOptionsProfiles();
1094 case Ekos::HFRProfiles:
1095 savedProfiles =
QDir(KSPaths::writableLocation(
1098 StellarSolver::loadSavedOptionsProfiles(savedProfiles) :
1099 Ekos::getDefaultHFROptionsProfiles();
1105void FITSTab::setupSolver(
bool extractOnly)
1107 auto parameters = getSSolverParametersList(
static_cast<Ekos::ProfileGroup
>(Options::fitsSolverModule())).at(
1108 m_PlateSolveUI.kcfg_FitsSolverProfile->currentIndex());
1109 parameters.search_radius = m_PlateSolveUI.kcfg_FitsSolverRadius->value();
1112 if (!m_PlateSolveUI.kcfg_FitsSolverLinear->isChecked())
1117 double offset = m_View->imageData()->getAverageMedian();
1119 offset = m_View->imageData()->getAverageMean();
1120 parameters.threshold_offset = offset;
1130 if (m_Solver && !m_PlateSolveUI.kcfg_FitsSolverLinear->isChecked())
1131 parameters.threshold_offset = m_Solver->getBackground().global;
1137 const int imageWidth = m_View->imageData()->width();
1138 const int imageHeight = m_View->imageData()->height();
1139 if (m_PlateSolveUI.kcfg_FitsSolverUseScale->isChecked() && imageWidth != 0 && imageHeight != 0)
1141 const double scale = m_PlateSolveUI.kcfg_FitsSolverScale->value();
1142 double lowScale = scale * 0.8;
1143 double highScale = scale * 1.2;
1146 const int units = m_PlateSolveUI.kcfg_FitsSolverImageScaleUnits->currentIndex();
1147 if (units == SSolver::DEG_WIDTH)
1149 lowScale = (lowScale * 3600) / std::max(imageWidth, imageHeight);
1150 highScale = (highScale * 3600) / std::min(imageWidth, imageHeight);
1152 else if (units == SSolver::ARCMIN_WIDTH)
1154 lowScale = (lowScale * 60) / std::max(imageWidth, imageHeight);
1155 highScale = (highScale * 60) / std::min(imageWidth, imageHeight);
1158 m_Solver->useScale(m_PlateSolveUI.kcfg_FitsSolverUseScale->isChecked(), lowScale, highScale);
1160 else m_Solver->useScale(
false, 0, 0);
1162 if (m_PlateSolveUI.kcfg_FitsSolverUsePosition->isChecked())
1165 const dms ra = m_PlateSolveUI.FitsSolverEstRA->createDms(&ok);
1167 const dms dec = m_PlateSolveUI.FitsSolverEstDec->createDms(&ok2);
1169 m_Solver->usePosition(
true, ra.
Degrees(),
dec.Degrees());
1171 m_Solver->usePosition(
false, 0, 0);
1173 else m_Solver->usePosition(
false, 0, 0);
1178void FITSTab::extractImage()
1180 if (m_Solver.
get() && m_Solver->isRunning())
1182 m_PlateSolveUI.SolveButton->setText(
i18n(
"Aborting..."));
1186 m_PlateSolveUI.SolveButton->setText(
i18n(
"Cancel"));
1190 m_PlateSolveUI.FitsSolverAngle->setText(
"");
1191 m_PlateSolveUI.FitsSolverIndexfile->setText(
"");
1192 m_PlateSolveUI.Solution1->setText(
i18n(
"Extracting..."));
1193 m_PlateSolveUI.Solution2->setText(
"");
1195 m_Solver->runSolver(m_View->imageData());
1198void FITSTab::solveImage()
1200 if (m_Solver.
get() && m_Solver->isRunning())
1202 m_PlateSolveUI.SolveButton->setText(
i18n(
"Aborting..."));
1206 m_PlateSolveUI.SolveButton->setText(
i18n(
"Cancel"));
1210 m_PlateSolveUI.Solution2->setText(
i18n(
"Solving..."));
1212 m_Solver->runSolver(m_View->imageData());
1215void FITSTab::extractorDone(
bool timedOut,
bool success,
const FITSImage::Solution &solution,
double elapsedSeconds)
1218 disconnect(m_Solver.
get(), &SolverUtils::done,
this, &FITSTab::extractorDone);
1219 m_PlateSolveUI.Solution2->setText(
"");
1223 const QString result =
i18n(
"Extractor timed out: %1s",
QString(
"%L1").arg(elapsedSeconds, 0,
'f', 1));
1224 m_PlateSolveUI.Solution1->setText(result);
1227 m_PlateSolveUI.SolveButton->setText(
"Solve");
1232 const QString result =
i18n(
"Extractor failed: %1s",
QString(
"%L1").arg(elapsedSeconds, 0,
'f', 1));
1233 m_PlateSolveUI.Solution1->setText(result);
1236 m_PlateSolveUI.SolveButton->setText(
i18n(
"Solve"));
1241 const QString starStr =
i18n(
"Extracted %1 stars (%2 unfiltered) in %3s",
1242 m_Solver->getNumStarsFound(),
1243 m_Solver->getBackground().num_stars_detected,
1244 QString(
"%1").arg(elapsedSeconds, 0,
'f', 1));
1245 m_PlateSolveUI.Solution1->setText(starStr);
1251 for (
int i = 0; i < starList.
size(); i++)
1253 const auto &star = starList[i];
1254 Edge *oneEdge =
new Edge();
1255 oneEdge->x = star.x;
1256 oneEdge->y = star.y;
1257 oneEdge->val = star.peak;
1258 oneEdge->sum = star.flux;
1259 oneEdge->HFR = star.HFR;
1260 oneEdge->width = star.a;
1261 oneEdge->numPixels = star.numPixels;
1265 oneEdge->ellipticity = 1 - star.b / star.a;
1267 oneEdge->ellipticity = 0;
1269 starCenters.
append(oneEdge);
1271 m_View->imageData()->setStarCenters(starCenters);
1272 m_View->updateFrame();
1279void FITSTab::solverDone(
bool timedOut,
bool success,
const FITSImage::Solution &solution,
double elapsedSeconds)
1281 disconnect(m_Solver.
get(), &SolverUtils::done,
this, &FITSTab::solverDone);
1282 m_PlateSolveUI.SolveButton->setText(
"Solve");
1284 if (m_Solver->isRunning())
1285 qCDebug(KSTARS_FITS) <<
"solverDone called, but it is still running.";
1289 const QString result =
i18n(
"Solver timed out: %1s",
QString(
"%L1").arg(elapsedSeconds, 0,
'f', 1));
1290 m_PlateSolveUI.Solution2->setText(result);
1294 const QString result =
i18n(
"Solver failed: %1s",
QString(
"%L1").arg(elapsedSeconds, 0,
'f', 1));
1295 m_PlateSolveUI.Solution2->setText(result);
1299 const bool eastToTheRight = solution.parity == FITSImage::POSITIVE ? false :
true;
1300 m_View->imageData()->injectWCS(solution.orientation, solution.ra, solution.dec, solution.pixscale, eastToTheRight);
1301 m_View->imageData()->loadWCS();
1302 m_View->syncWCSState();
1303 if (m_View->areObjectsShown())
1305 m_View->imageData()->searchObjects();
1308 const double solverPA = KSUtils::rotationToPositionAngle(solution.orientation);
1309 m_PlateSolveUI.FitsSolverAngle->setText(
QString(
"%1º").arg(solverPA, 0,
'f', 2));
1311 int indexUsed = -1, healpixUsed = -1;
1312 m_Solver->getSolutionHealpix(&indexUsed, &healpixUsed);
1314 m_PlateSolveUI.FitsSolverIndexfile->setText(
"");
1316 m_PlateSolveUI.FitsSolverIndexfile->setText(
1319 .arg(healpixUsed >= 0 ?
QString(
"-%1").arg(healpixUsed) :
QString(
"")));;
1322 const int imageWidth = m_View->imageData()->width();
1323 const int units = m_PlateSolveUI.kcfg_FitsSolverImageScaleUnits->currentIndex();
1324 if (units == SSolver::DEG_WIDTH)
1325 m_PlateSolveUI.kcfg_FitsSolverScale->setValue(solution.pixscale * imageWidth / 3600.0);
1326 else if (units == SSolver::ARCMIN_WIDTH)
1327 m_PlateSolveUI.kcfg_FitsSolverScale->setValue(solution.pixscale * imageWidth / 60.0);
1329 m_PlateSolveUI.kcfg_FitsSolverScale->setValue(solution.pixscale);
1332 m_PlateSolveUI.FitsSolverEstRA->show(
dms(solution.ra));
1333 m_PlateSolveUI.FitsSolverEstDec->show(
dms(solution.dec));
1335 m_PlateSolveUI.Solution2->setText(result);
1341int FITSTab::getProfileIndex(
int moduleIndex)
1343 if (moduleIndex < 0 || moduleIndex >= Ekos::ProfileGroupNames.
size())
1345 const QString moduleName = Ekos::ProfileGroupNames[moduleIndex].toString();
1346 const QString str = Options::fitsSolverProfileIndeces();
1351 return indeces[moduleName].toString().toInt();
1354void FITSTab::setProfileIndex(
int moduleIndex,
int profileIndex)
1356 if (moduleIndex < 0 || moduleIndex >= Ekos::ProfileGroupNames.
size())
1358 QString str = Options::fitsSolverProfileIndeces();
1363 for (
int i = 0; i < Ekos::ProfileGroupNames.size(); i++)
1365 QString name = Ekos::ProfileGroupNames[i].toString();
1366 if (name ==
"Align")
1368 else if (name ==
"Guide")
1370 else if (name ==
"HFR")
1373 initialIndeces[
name] =
"0";
1379 indeces[Ekos::ProfileGroupNames[moduleIndex].toString()] =
QString::number(profileIndex);
1381 Options::setFitsSolverProfileIndeces(
QString(doc.
toJson()));
1384void FITSTab::setupProfiles(
int moduleIndex)
1386 if (moduleIndex < 0 || moduleIndex >= Ekos::ProfileGroupNames.
size())
1388 Ekos::ProfileGroup profileGroup =
static_cast<Ekos::ProfileGroup
>(moduleIndex);
1389 Options::setFitsSolverModule(moduleIndex);
1393 m_PlateSolveUI.kcfg_FitsSolverProfile->clear();
1394 for(
auto ¶m : optionsList)
1395 m_PlateSolveUI.kcfg_FitsSolverProfile->addItem(param.listName);
1397 m_ProfileEditor->setProfileGroup(profileGroup,
false);
1400 m_PlateSolveUI.kcfg_FitsSolverProfile->setCurrentIndex(getProfileIndex(Options::fitsSolverModule()));
1402 m_ProfileEditorPage->setHeader(
QString(
"FITS Viewer Solver %1 Profiles Editor")
1403 .arg(Ekos::ProfileGroupNames[moduleIndex].
toString()));
1406void FITSTab::initSolverUI()
1409 m_PlateSolveUI.kcfg_FitsSolverModule->clear();
1410 for (
int i = 0; i < Ekos::ProfileGroupNames.size(); i++)
1411 m_PlateSolveUI.kcfg_FitsSolverModule->addItem(Ekos::ProfileGroupNames[i].toString());
1412 m_PlateSolveUI.kcfg_FitsSolverModule->setCurrentIndex(Options::fitsSolverModule());
1414 setupProfiles(Options::fitsSolverModule());
1419 m_PlateSolveUI.kcfg_FitsSolverUseScale->setChecked(Options::fitsSolverUseScale());
1420 m_PlateSolveUI.kcfg_FitsSolverScale->setValue(Options::fitsSolverScale());
1421 m_PlateSolveUI.kcfg_FitsSolverImageScaleUnits->setCurrentIndex(Options::fitsSolverImageScaleUnits());
1423 m_PlateSolveUI.kcfg_FitsSolverUsePosition->setChecked(Options::fitsSolverUsePosition());
1424 m_PlateSolveUI.kcfg_FitsSolverRadius->setValue(Options::fitsSolverRadius());
1426 m_PlateSolveUI.FitsSolverEstRA->setUnits(dmsBox::HOURS);
1427 m_PlateSolveUI.FitsSolverEstDec->setUnits(dmsBox::DEGREES);
1432 setProfileIndex(m_PlateSolveUI.kcfg_FitsSolverModule->currentIndex(), index);
1437 Options::setFitsSolverUseScale(state);
1441 Options::setFitsSolverScale(value);
1445 Options::setFitsSolverImageScaleUnits(index);
1450 Options::setFitsSolverUsePosition(state);
1455 Options::setFitsSolverRadius(value);
1459 const auto center = SkyMap::Instance()->getCenterPoint();
1460 m_PlateSolveUI.FitsSolverEstRA->show(center.ra());
1461 m_PlateSolveUI.FitsSolverEstDec->show(center.dec());
1465 const SSolver::SolverType
type =
static_cast<SSolver::SolverType
>(Options::solverType());
1466 if(type != SSolver::SOLVER_STELLARSOLVER)
1468 m_PlateSolveUI.Solution2->setText(
i18n(
"Warning! This tool only supports the internal StellarSolver solver."));
1469 m_PlateSolveUI.Solution1->setText(
i18n(
"Change to that in the Ekos Align options menu."));
Primary window to view monochrome and color FITS images.
static KStars * Instance()
An angle, stored as degrees, but expressible in many ways.
const double & Degrees() const
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
Type type(const QSqlDatabase &db)
std::optional< QSqlQuery > query(const QString &queryStatement)
char * toString(const EngineQuery &query)
Ekos is an advanced Astrophotography tool for Linux.
ButtonCode warningContinueCancel(QWidget *parent, const QString &text, const QString &title=QString(), const KGuiItem &buttonContinue=KStandardGuiItem::cont(), const KGuiItem &buttonCancel=KStandardGuiItem::cancel(), const QString &dontAskAgainName=QString(), Options options=Notify)
QString name(StandardAction id)
void doubleClicked(const QModelIndex &index)
void stateChanged(int state)
void setImage(const QImage &image, Mode mode)
void activated(int index)
bool openUrl(const QUrl &url)
QString filePath(const QString &fileName) const const
void valueChanged(double d)
bool exists(const QString &fileName)
QUrl getSaveFileUrl(QWidget *parent, const QString &caption, const QUrl &dir, const QString &filter, QString *selectedFilter, Options options, const QStringList &supportedSchemes)
bool exists() const const
QIcon fromTheme(const QString &name)
void currentRowChanged(const QModelIndex ¤t, const QModelIndex &previous)
QJsonDocument fromJson(const QByteArray &json, QJsonParseError *error)
bool isNull() const const
bool isObject() const const
QJsonObject object() const const
QByteArray toJson(JsonFormat format) const const
void append(QList< T > &&value)
void push_back(parameter_type value)
void reserve(qsizetype size)
qsizetype size() const const
bool isValid() const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
bool disconnect(const QMetaObject::Connection &connection)
QPixmap scaled(const QSize &size, Qt::AspectRatioMode aspectRatioMode, Qt::TransformationMode transformMode) const const
bool isNull() const const
void splitterMoved(int pos, int index)
Qt::CheckState checkState() const const
void setCheckState(Qt::CheckState state)
virtual void setData(const QVariant &value, int role)
void setTextAlignment(Qt::Alignment alignment)
QString text() const const
QStandardItem * item(int row, int column) const const
QStandardItem * itemFromIndex(const QModelIndex &index) const const
virtual bool removeRows(int row, int count, const QModelIndex &parent) override
virtual int rowCount(const QModelIndex &parent) const const override
void setColumnCount(int columns)
virtual bool setHeaderData(int section, Qt::Orientation orientation, const QVariant &value, int role) override
void setHorizontalHeaderLabels(const QStringList &labels)
void setItem(int row, QStandardItem *item)
void setRowCount(int rows)
void setSortRole(int role)
QString arg(Args &&... args) const const
bool contains(QChar ch, Qt::CaseSensitivity cs) const const
bool isEmpty() const const
QString number(double n, char format, int precision)
QString & remove(QChar ch, Qt::CaseSensitivity cs)
QString & replace(QChar before, QChar after, Qt::CaseSensitivity cs)
bool startsWith(QChar c, Qt::CaseSensitivity cs) const const
int toInt(bool *ok, int base) const const
QByteArray toLatin1() const const
QByteArray toUtf8() const const
QTextStream & dec(QTextStream &stream)
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
void setText(int column, const QString &text)
void setActive(bool active)
bool isClean() const const
QUrl fromLocalFile(const QString &localFile)
bool isEmpty() const const
bool isValid() const const
void setPath(const QString &path, ParsingMode mode)
void setQuery(const QString &query, ParsingMode mode)
QString toLocalFile() const const
QString toString(FormattingOptions options) const const
QString url(FormattingOptions options) const const