7#include "imagingplanner.h"
9#include <kio_version.h>
11#include "artificialhorizoncomponent.h"
12#include "auxiliary/screencapture.h"
13#include "auxiliary/thememanager.h"
14#include "catalogscomponent.h"
15#include "constellationboundarylines.h"
16#include "dialogs/detaildialog.h"
17#include "dialogs/finddialog.h"
20#include "ekos/scheduler/schedulerjob.h"
23#include "flagmanager.h"
24#include "flagcomponent.h"
26#include "nameresolver.h"
27#include "imageoverlaycomponent.h"
28#include "imagingplanneroptions.h"
29#include "kplotwidget.h"
30#include "kplotobject.h"
34#include "ksnotification.h"
38#include "kstarsdata.h"
39#include "fitsviewer/platesolve.h"
41#include "skymapcomposite.h"
43#include <QDesktopServices>
48#include <QNetworkReply>
50#include <QRegularExpression>
51#include <QSortFilterProxyModel>
52#include <QStandardItemModel>
57#define DPRINTF if (false) fprintf
81#define TYPE_ROLE (Qt::UserRole + 1)
82#define HOURS_ROLE (Qt::UserRole + 2)
83#define SIZE_ROLE (Qt::UserRole + 3)
84#define ALTITUDE_ROLE (Qt::UserRole + 4)
85#define MOON_ROLE (Qt::UserRole + 5)
86#define FLAGS_ROLE (Qt::UserRole + 6)
87#define NOTES_ROLE (Qt::UserRole + 7)
89#define PICKED_BIT ImagingPlannerDBEntry::PickedBit
90#define IMAGED_BIT ImagingPlannerDBEntry::ImagedBit
91#define IGNORED_BIT ImagingPlannerDBEntry::IgnoredBit
121 case SkyObject::OPEN_CLUSTER:
122 return Options::imagingPlannerAcceptOpenCluster();
123 case SkyObject::GLOBULAR_CLUSTER:
124 return Options::imagingPlannerAcceptGlobularCluster();
125 case SkyObject::GASEOUS_NEBULA:
126 return Options::imagingPlannerAcceptNebula();
127 case SkyObject::PLANETARY_NEBULA:
128 return Options::imagingPlannerAcceptPlanetary();
129 case SkyObject::SUPERNOVA_REMNANT:
130 return Options::imagingPlannerAcceptSupernovaRemnant();
131 case SkyObject::GALAXY:
132 return Options::imagingPlannerAcceptGalaxy();
133 case SkyObject::GALAXY_CLUSTER:
134 return Options::imagingPlannerAcceptGalaxyCluster();
135 case SkyObject::DARK_NEBULA:
136 return Options::imagingPlannerAcceptDarkNebula();
138 return Options::imagingPlannerAcceptOther();
145 const bool hasFlags = model->
data(idx, FLAGS_ROLE).
canConvert<
int>();
148 const bool flag = model->
data(idx, FLAGS_ROLE).
toInt() & bit;
155 const bool hasFlags = model->
data(idx, FLAGS_ROLE).
canConvert<
int>();
156 int currentFlags = 0;
158 currentFlags = model->
data(idx, FLAGS_ROLE).
toInt();
160 model->
setData(idx, val, FLAGS_ROLE);
166 const bool hasFlags = model->
data(idx, FLAGS_ROLE).
canConvert<
int>();
169 const int currentFlags = model->
data(idx, FLAGS_ROLE).
toInt();
171 model->
setData(idx, val, FLAGS_ROLE);
177 if (flags & IMAGED_BIT) str.
append(
i18n(
"Imaged"));
178 if (flags & PICKED_BIT)
184 if (flags & IGNORED_BIT)
194void setupShowCallback(
bool checked,
195 void (*showOption)(
bool),
void (*showNotOption)(
bool),
196 void (*dontCareOption)(
bool),
200 Q_UNUSED(showCheckbox);
204 showNotOption(
false);
205 dontCareOption(
false);
208 Options::self()->save();
213 showNotOption(
false);
214 dontCareOption(
true);
217 Options::self()->save();
221void setupShowNotCallback(
bool checked,
222 void (*showOption)(
bool),
void (*showNotOption)(
bool),
void (*dontCareOption)(
bool),
225 Q_UNUSED(showNotCheckbox);
230 dontCareOption(
false);
233 Options::self()->save();
238 showNotOption(
false);
239 dontCareOption(
true);
242 Options::self()->save();
246void setupDontCareCallback(
bool checked,
247 void (*showOption)(
bool),
void (*showNotOption)(
bool),
void (*dontCareOption)(
bool),
253 showNotOption(
false);
254 dontCareOption(
true);
257 Options::self()->save();
264 showNotOption(
false);
265 dontCareOption(
true);
269 Options::self()->save();
278 "\\+~#=]{1,256}\\.[a-zA-Z0-9()]{1,6}\\b([-a-zA-Z0-9()@:%_\\+.~#?&//=]*)");
283 auto match = re.match(input);
284 if (!
match.hasMatch())
287 return(
match.captured(0));
293 match = re.match(inp);
294 if (!
match.hasMatch())
297 return (
match.captured(0));
323 int column,
int role)
325 const double l =
left.siblingAtColumn(column).data(role).toDouble();
326 const double r =
right.siblingAtColumn(column).data(role).toDouble();
336 const QString l =
left.siblingAtColumn(column).data(role).toString();
337 const QString r =
right.siblingAtColumn(column).data(role).toString();
341 if (lList.
size() == 0 || rList.
size() == 0)
352 if (lList.
size() >= 2 && rList.
size() >= 2)
354 int lInt = lList[1].toInt();
355 int rInt = rList[1].toInt();
358 if (lInt > 0 && rInt > 0)
359 return -(lInt - rInt);
367void SchedulerUtils_setupJob(Ekos::SchedulerJob &job,
const QString &name,
bool isLead,
const QString &group,
368 const QString &train,
const dms &ra,
const dms &dec,
double djd,
double rotation,
const QUrl &sequenceUrl,
370 const QDateTime &completionTime,
int completionRepeats,
double minimumAltitude,
double minimumMoonSeparation,
371 double maxMoonAltitude,
372 bool enforceTwilight,
bool enforceArtificialHorizon,
bool track,
bool focus,
bool align,
bool guide)
376 job.setIsLead(isLead);
377 job.setOpticalTrain(train);
378 job.setPositionAngle(rotation);
384 job.setLeadJob(
nullptr);
386 job.setTargetCoords(ra, dec, djd);
387 job.setFITSFile(fitsUrl);
390 job.setStartupCondition(startup);
391 if (startup == Ekos::START_AT)
393 job.setStartupTime(startupTime);
396 job.setFileStartupCondition(job.getStartupCondition());
397 job.setStartAtTime(job.getStartupTime());
400 job.setMinAltitude(minimumAltitude);
401 job.setMinMoonSeparation(minimumMoonSeparation);
402 job.setMaxMoonAltitude(maxMoonAltitude);
406 job.setEnforceTwilight(enforceTwilight);
407 job.setEnforceArtificialHorizon(enforceArtificialHorizon);
410 job.setStepPipeline(Ekos::SchedulerJob::USE_NONE);
412 job.setStepPipeline(
static_cast<Ekos::SchedulerJob::StepPipeline
>(job.getStepPipeline() | Ekos::SchedulerJob::USE_TRACK));
414 job.setStepPipeline(
static_cast<Ekos::SchedulerJob::StepPipeline
>(job.getStepPipeline() | Ekos::SchedulerJob::USE_FOCUS));
416 job.setStepPipeline(
static_cast<Ekos::SchedulerJob::StepPipeline
>(job.getStepPipeline() | Ekos::SchedulerJob::USE_ALIGN));
418 job.setStepPipeline(
static_cast<Ekos::SchedulerJob::StepPipeline
>(job.getStepPipeline() | Ekos::SchedulerJob::USE_GUIDE));
421 job.setFileStartupCondition(job.getStartupCondition());
422 job.setStartAtTime(job.getStartupTime());
427 job.setSequenceFile(sequenceUrl);
428 job.setCompletionCondition(completion);
429 if (completion == Ekos::FINISH_AT)
430 job.setFinishAtTime(completionTime);
431 else if (completion == Ekos::FINISH_REPEAT)
433 job.setRepeatsRequired(completionRepeats);
434 job.setRepeatsRemaining(completionRepeats);
442void setupJob(Ekos::SchedulerJob &job,
const QString name,
double minAltitude,
double minMoonSeparation,
443 double maxMoonAltitude,
dms ra,
dms dec,
444 bool useArtificialHorizon)
447 double rotation = 0.0;
453 SchedulerUtils_setupJob(job, name,
true,
"",
455 rotation, sequenceURL,
QUrl(),
458 minAltitude, minMoonSeparation, maxMoonAltitude,
459 true, useArtificialHorizon,
460 true,
true,
true,
true);
464void getRunTimes(
const QDate &date,
const GeoLocation &geo,
double minAltitude,
double minMoonSeparation,
465 double maxMoonAltitude,
const dms &ra,
const dms &dec,
bool useArtificialHorizon,
QVector<QDateTime> *jobStartTimes,
468 jobStartTimes->
clear();
469 jobEndTimes->
clear();
470 constexpr int SCHEDULE_RESOLUTION_MINUTES = 10;
471 Ekos::SchedulerJob job;
472 setupJob(job,
"temp", minAltitude, minMoonSeparation, maxMoonAltitude, ra, dec, useArtificialHorizon);
479 startTime.setTimeZone(tz);
480 stopTime.setTimeZone(tz);
484 while (--maxIters >= 0)
486 QDateTime s = job.getNextPossibleStartTime(startTime, SCHEDULE_RESOLUTION_MINUTES,
false, stopTime);
491 QDateTime e = job.getNextEndTime(s, SCHEDULE_RESOLUTION_MINUTES, &constraintReason, stopTime);
499 if (e.
secsTo(stopTime) < 600)
509 double minMoonSeparation,
double maxMoonAltitude,
bool useArtificialHorizon)
512 getRunTimes(date, geo, minAltitude, minMoonSeparation, maxMoonAltitude,
object.ra0(),
object.dec0(), useArtificialHorizon,
515 if (jobStartTimes.
size() == 0 || jobEndTimes.
size() == 0)
519 double totalHours = 0.0;
520 for (
int i = 0; i < jobStartTimes.
size(); ++i)
521 totalHours += jobStartTimes[i].secsTo(jobEndTimes[i]) * 1.0 / 3600.0;
530int packString(
const QString &input, quint8 *p,
bool reallyPack)
533 quint32 len = str_data.
length();
534 const char *str = str_data.
data();
535 constexpr bool compatibilityMode =
false;
536 const quint8 *origP = p;
539 if (reallyPack) *p = 0xa0 | len;
542 else if (len <= std::numeric_limits<quint8>::max() &&
543 compatibilityMode ==
false)
545 if (reallyPack) *p = 0xd9;
547 if (reallyPack) *p = len;
550 else if (len <= std::numeric_limits<quint16>::max())
552 if (reallyPack) *p = 0xda;
563 if (reallyPack) memcpy(p, str, len);
564 return (p - origP) + len;
571 int size = packString(input,
nullptr,
false);
575 packString(input,
reinterpret_cast<quint8*
>(arr.
data()),
true);
593 result.
replace(remainingSpacesRegex, QStringLiteral(
""));
599bool downsampleImageFiles(
const QString &baseDir,
int maxHeight)
610 const QString subDir =
"REDUCED";
611 QDir directory(baseDir);
612 if (!directory.exists())
614 fprintf(stderr,
"downsampleImageFiles: Base directory doesn't exist\n");
622 fprintf(stderr,
"downsampleImageFiles: Failed making the output directory\n");
629 foreach (
QString filename, files)
634 if (img.height() > maxHeight)
643 if (!scaledImg.
save(jpgFilename,
"JPG"))
644 fprintf(stderr,
"downsampleImageFiles: Failed saving \"%s\"\n", writeFilename.
toLatin1().
data());
648 fprintf(stderr,
"downsampleImageFiles: saved \"%s\"\n", writeFilename.
toLatin1().
data());
651 fprintf(stderr,
"downsampleImageFiles: Wrote %d files\n", numSaved);
662 const int len = bInput.
size();
664 for (
int i = 0; i < len; ++i)
674 bInput.
replace(pos, 1, substitute);
683 auto massagedName =
name;
685 massagedName = massagedName.
replace(0, 4,
"sh2-");
686 massagedName = massagedName.
replace(
' ',
"");
692 if (files.size() > 0)
693 return files[0].absoluteFilePath();
696 for (
int i = 0; i < subDirs.size(); i++)
698 QDir subDir(subDirs[i].absoluteFilePath());
700 if (files.size() > 0)
701 return files[0].absoluteFilePath();
708 if (astrobinAbbrev ==
"ACC")
710 else if (astrobinAbbrev ==
"ASACC")
712 else if (astrobinAbbrev ==
"ANCCC")
714 else if (astrobinAbbrev ==
"ANCSACC")
715 return "CC-BY-SA-NC";
719QString creativeCommonsTooltipString(
const QString &astrobinAbbrev)
721 if (astrobinAbbrev ==
"ACC")
722 return "Atribution Creative Commons";
723 else if (astrobinAbbrev ==
"ASACC")
724 return "Atribution Share-Alike Creative Commons";
725 else if (astrobinAbbrev ==
"ANCCC")
726 return "Atribution Non-Commercial Creative Commons";
727 else if (astrobinAbbrev ==
"ANCSACC")
728 return "Atribution Non-Commercial Share-Alike Creative Commons";
747 double hoursAfterDusk = 0,
double hoursBeforeDawn = 0)
753 QDateTime dawn = midnight.
addSecs(24 * 3600 * ksal.getDawnAstronomicalTwilight());
755 QDateTime dusk = midnight.
addSecs(24 * 3600 * ksal.getDuskAstronomicalTwilight());
761 auto end = dawn.
addSecs(-hoursBeforeDawn * 3600);
770 while (t.secsTo(end) > 0)
772 double alt = getAltitude(geo, coords, t);
778 t = t.addSecs(60 * 20);
787 m_SortColumn = HOURS_COLUMN;
791bool CatalogFilter::filterAcceptsRow(
int row,
const QModelIndex &parent)
const
795 if (!acceptType(type))
return false;
799 if (!hasEnoughHours)
return false;
803 const bool isImaged =
sourceModel()->data(flagsIndex, FLAGS_ROLE).toInt() & IMAGED_BIT;
804 const bool passesImagedConstraints = !m_ImagedConstraintsEnabled || (isImaged == m_ImagedRequired);
805 if (!passesImagedConstraints)
return false;
807 const bool isIgnored =
sourceModel()->data(flagsIndex, FLAGS_ROLE).toInt() & IGNORED_BIT;
808 const bool passesIgnoredConstraints = !m_IgnoredConstraintsEnabled || (isIgnored == m_IgnoredRequired);
809 if (!passesIgnoredConstraints)
return false;
811 const bool isPicked =
sourceModel()->data(flagsIndex, FLAGS_ROLE).toInt() & PICKED_BIT;
812 const bool passesPickedConstraints = !m_PickedConstraintsEnabled || (isPicked == m_PickedRequired);
813 if (!passesPickedConstraints)
return false;
816 if (m_Keyword.isEmpty() || !m_KeywordConstraintsEnabled)
return true;
818 const QString notes =
sourceModel()->data(notesIndex, NOTES_ROLE).toString();
820 const bool REMatches = m_KeywordRE.match(notes).hasMatch();
821 return (m_KeywordRequired == REMatches);
824void CatalogFilter::setMinHours(
double hours)
829void CatalogFilter::setImagedConstraints(
bool enabled,
bool required)
831 m_ImagedConstraintsEnabled = enabled;
832 m_ImagedRequired = required;
835void CatalogFilter::setPickedConstraints(
bool enabled,
bool required)
837 m_PickedConstraintsEnabled = enabled;
838 m_PickedRequired = required;
841void CatalogFilter::setIgnoredConstraints(
bool enabled,
bool required)
843 m_IgnoredConstraintsEnabled = enabled;
844 m_IgnoredRequired = required;
847void CatalogFilter::setKeywordConstraints(
bool enabled,
bool required,
const QString &keyword)
849 m_KeywordConstraintsEnabled = enabled;
850 m_KeywordRequired = required;
852 m_KeywordRE = QRegularExpression(keyword);
855void CatalogFilter::setSortColumn(
int column)
857 if (column == m_SortColumn)
858 m_ReverseSort = !m_ReverseSort;
859 m_SortColumn = column;
868 double compareVal = 0;
873 compareVal = stringCompareFcn(left, right, NAME_COLUMN,
Qt::DisplayRole);
874 if (m_ReverseSort) compareVal = -compareVal;
878 compareVal = stringCompareFcn(left, right, TYPE_COLUMN,
Qt::DisplayRole);
879 if (m_ReverseSort) compareVal = -compareVal;
880 if (compareVal != 0)
break;
881 compareVal = floatCompareFcn(left, right, HOURS_COLUMN, HOURS_ROLE);
882 if (compareVal != 0)
break;
883 compareVal = stringCompareFcn(left, right, NAME_COLUMN,
Qt::DisplayRole);
887 compareVal = floatCompareFcn(left, right, SIZE_COLUMN, SIZE_ROLE);
888 if (m_ReverseSort) compareVal = -compareVal;
889 if (compareVal != 0)
break;
890 compareVal = floatCompareFcn(left, right, HOURS_COLUMN, HOURS_ROLE);
891 if (compareVal != 0)
break;
892 compareVal = stringCompareFcn(left, right, NAME_COLUMN,
Qt::DisplayRole);
894 case ALTITUDE_COLUMN:
896 compareVal = floatCompareFcn(left, right, ALTITUDE_COLUMN, ALTITUDE_ROLE);
897 if (m_ReverseSort) compareVal = -compareVal;
898 if (compareVal != 0)
break;
899 compareVal = floatCompareFcn(left, right, HOURS_COLUMN, HOURS_ROLE);
900 if (compareVal != 0)
break;
901 compareVal = stringCompareFcn(left, right, NAME_COLUMN,
Qt::DisplayRole);
905 compareVal = floatCompareFcn(left, right, MOON_COLUMN, MOON_ROLE);
906 if (m_ReverseSort) compareVal = -compareVal;
907 if (compareVal != 0)
break;
908 compareVal = floatCompareFcn(left, right, HOURS_COLUMN, HOURS_ROLE);
909 if (compareVal != 0)
break;
910 compareVal = stringCompareFcn(left, right, NAME_COLUMN,
Qt::DisplayRole);
912 case CONSTELLATION_COLUMN:
914 compareVal = stringCompareFcn(left, right, CONSTELLATION_COLUMN,
Qt::DisplayRole);
915 if (m_ReverseSort) compareVal = -compareVal;
916 if (compareVal != 0)
break;
917 compareVal = floatCompareFcn(left, right, HOURS_COLUMN, HOURS_ROLE);
918 if (compareVal != 0)
break;
919 compareVal = stringCompareFcn(left, right, NAME_COLUMN,
Qt::DisplayRole);
923 compareVal = stringCompareFcn(left, right, COORD_COLUMN,
Qt::DisplayRole);
924 if (m_ReverseSort) compareVal = -compareVal;
925 if (compareVal != 0)
break;
926 compareVal = floatCompareFcn(left, right, HOURS_COLUMN, HOURS_ROLE);
927 if (compareVal != 0)
break;
928 compareVal = stringCompareFcn(left, right, NAME_COLUMN,
Qt::DisplayRole);
933 compareVal = floatCompareFcn(left, right, HOURS_COLUMN, HOURS_ROLE);
934 if (m_ReverseSort) compareVal = -compareVal;
935 if (compareVal != 0)
break;
936 compareVal = stringCompareFcn(left, right, NAME_COLUMN,
Qt::DisplayRole);
939 return compareVal < 0;
949void ImagingPlannerUI::setupIcons()
973 return KStarsData::Instance()->
geo();
976QDate ImagingPlanner::getDate()
const
978 return ui->DateEdit->date();
981ImagingPlanner::ImagingPlanner() :
QDialog(nullptr), m_networkManager(this), m_manager{ CatalogsDB::dso_db_path() }
983 ui =
new ImagingPlannerUI(
this);
989 setLayout(mainLayout);
991 setWindowTitle(
i18nc(
"@title:window",
"Imaging Planner"));
994 if (Options::imagingPlannerIndependentWindow())
1010void ImagingPlanner::setupHideButtons(
bool(*option)(),
void(*setOption)(
bool),
1020 Options::self()->save();
1029 Options::self()->save();
1037void ImagingPlanner::focusOnTable()
1039 ui->CatalogView->setFocus();
1042void ImagingPlanner::adjustWindowSize()
1044 const int keepWidth =
width();
1046 const int newHeight =
height();
1047 resize(keepWidth, newHeight);
1051void ImagingPlanner::setupFilterButton(
QCheckBox * checkbox,
bool(*option)(),
void(*setOption)(
bool))
1057 Options::self()->save();
1058 m_CatalogSortModel->invalidate();
1060 ui->CatalogView->resizeColumnsToContents();
1066void ImagingPlanner::setupFilter2Buttons(
1068 bool(*yesOption)(),
bool(*noOption)(),
bool(*dontCareOption)(),
1069 void(*setYesOption)(
bool),
void(*setNoOption)(
bool),
void(*setDontCareOption)(
bool))
1075 setupShowCallback(checked, setYesOption, setNoOption, setDontCareOption, yes, no, dontCare);
1076 updateSortConstraints();
1077 m_CatalogSortModel->invalidate();
1078 ui->CatalogView->resizeColumnsToContents();
1084 setupShowNotCallback(checked, setYesOption, setNoOption, setDontCareOption, yes, no, dontCare);
1085 updateSortConstraints();
1086 m_CatalogSortModel->invalidate();
1087 ui->CatalogView->resizeColumnsToContents();
1091 connect(dontCare, &
QCheckBox::clicked, [
this, setYesOption, setNoOption, setDontCareOption, yes, no, dontCare](
bool checked)
1093 setupDontCareCallback(checked, setYesOption, setNoOption, setDontCareOption, yes, no, dontCare);
1094 updateSortConstraints();
1095 m_CatalogSortModel->invalidate();
1096 ui->CatalogView->resizeColumnsToContents();
1107void ImagingPlanner::updateSortConstraints()
1109 m_CatalogSortModel->setPickedConstraints(!ui->dontCarePickedCB->isChecked(),
1110 ui->pickedCB->isChecked());
1111 m_CatalogSortModel->setImagedConstraints(!ui->dontCareImagedCB->isChecked(),
1112 ui->imagedCB->isChecked());
1113 m_CatalogSortModel->setIgnoredConstraints(!ui->dontCareIgnoredCB->isChecked(),
1114 ui->ignoredCB->isChecked());
1115 m_CatalogSortModel->setKeywordConstraints(!ui->dontCareKeywordCB->isChecked(),
1116 ui->keywordCB->isChecked(), ui->keywordEdit->toPlainText().trimmed());
1120void ImagingPlanner::initialize()
1122 if (KStarsData::Instance() ==
nullptr)
1129 connect(
this, &ImagingPlanner::popupSorry,
this, &ImagingPlanner::sorry);
1132 m_CatalogModel =
new QStandardItemModel(0, LAST_COLUMN);
1135 m_CatalogModel->setHorizontalHeaderLabels(
1138 m_CatalogModel->horizontalHeaderItem(NAME_COLUMN)->setToolTip(
1139 i18n(
"Object Name--click header to sort ascending/descending."));
1140 m_CatalogModel->horizontalHeaderItem(
1141 HOURS_COLUMN)->setToolTip(
i18n(
"Number of hours the object can be imaged--click header to sort ascending/descending."));
1142 m_CatalogModel->horizontalHeaderItem(TYPE_COLUMN)->setToolTip(
1143 i18n(
"Object Type--click header to sort ascending/descending."));
1144 m_CatalogModel->horizontalHeaderItem(
1145 SIZE_COLUMN)->setToolTip(
i18n(
"Maximum object dimension (arcmin)--click header to sort ascending/descending."));
1146 m_CatalogModel->horizontalHeaderItem(
1147 ALTITUDE_COLUMN)->setToolTip(
i18n(
"Maximum altitude--click header to sort ascending/descending."));
1148 m_CatalogModel->horizontalHeaderItem(
1149 MOON_COLUMN)->setToolTip(
i18n(
"Moon angular separation at midnight--click header to sort ascending/descending."));
1150 m_CatalogModel->horizontalHeaderItem(
1151 CONSTELLATION_COLUMN)->setToolTip(
i18n(
"Constellation--click header to sort ascending/descending."));
1152 m_CatalogModel->horizontalHeaderItem(
1153 COORD_COLUMN)->setToolTip(
i18n(
"RA/DEC coordinates--click header to sort ascending/descending."));
1155 m_CatalogSortModel =
new CatalogFilter(
this);
1157 m_CatalogSortModel->setSourceModel(m_CatalogModel.data());
1158 m_CatalogSortModel->setDynamicSortFilter(
true);
1160 ui->CatalogView->setModel(m_CatalogSortModel.data());
1161 ui->CatalogView->setSortingEnabled(
false);
1162 ui->CatalogView->horizontalHeader()->setStretchLastSection(
false);
1163 ui->CatalogView->resizeColumnsToContents();
1164 ui->CatalogView->verticalHeader()->setVisible(
false);
1165 ui->CatalogView->setColumnHidden(FLAGS_COLUMN,
true);
1168 this, &ImagingPlanner::selectionChanged);
1173 auto utc = KStarsData::Instance()->
clock()->
utc();
1174 auto localTime = getGeo()->UTtoLT(utc);
1175 ui->DateEdit->
setDate(localTime.date());
1181 setupHideButtons(&Options::imagingPlannerHideAltitudeGraph, &Options::setImagingPlannerHideAltitudeGraph,
1182 ui->hideAltitudeGraphB, ui->showAltitudeGraphB,
1183 ui->AltitudeGraphFrame, ui->HiddenAltitudeGraphFrame);
1190 QString selection = currentObjectName();
1194 scrollToName(selection);
1218 checkTargets2(true);
1222 QString dir = QFileDialog::getExistingDirectory(this, tr(
"Downsample Directory"));
1225 if (downsampleImageFiles(dir, 300))
1226 fprintf(stderr,
"downsampling succeeded\n");
1228 fprintf(stderr,
"downsampling failed\n");
1233 Options::setImagingPlannerHideAstrobinDetails(
true);
1234 setupHideButtons(&Options::imagingPlannerHideAstrobinDetails, &Options::setImagingPlannerHideAstrobinDetails,
1235 ui->hideAstrobinDetailsButton, ui->showAstrobinDetailsButton,
1236 ui->AstrobinSearchFrame, ui->HiddenAstrobinSearchFrame);
1237 ui->AstrobinAward->setChecked(Options::astrobinAward());
1240 Options::setAstrobinAward(checked);
1241 Options::self()->save();
1244 ui->AstrobinMinRadius->setValue(Options::astrobinMinRadius());
1247 Options::setAstrobinMinRadius(ui->AstrobinMinRadius->value());
1248 Options::self()->save();
1251 ui->AstrobinMaxRadius->setValue(Options::astrobinMaxRadius());
1254 Options::setAstrobinMaxRadius(ui->AstrobinMaxRadius->value());
1255 Options::self()->save();
1266 setupHideButtons(&Options::imagingPlannerHideImage, &Options::setImagingPlannerHideImage,
1267 ui->hideImageButton, ui->showImageButton,
1268 ui->ImageFrame, ui->HiddenImageFrame);
1271 Options::setImagingPlannerHideFilters(
true);
1272 setupHideButtons(&Options::imagingPlannerHideFilters, &Options::setImagingPlannerHideFilters,
1273 ui->hideFilterTypesButton, ui->showFilterTypesButton,
1274 ui->FilterTypesFrame, ui->HiddenFilterTypesFrame);
1275 setupFilterButton(ui->OpenClusterCB, &Options::imagingPlannerAcceptOpenCluster,
1276 &Options::setImagingPlannerAcceptOpenCluster);
1277 setupFilterButton(ui->NebulaCB, &Options::imagingPlannerAcceptNebula, &Options::setImagingPlannerAcceptNebula);
1278 setupFilterButton(ui->GlobularClusterCB, &Options::imagingPlannerAcceptGlobularCluster,
1279 &Options::setImagingPlannerAcceptGlobularCluster);
1280 setupFilterButton(ui->PlanetaryCB, &Options::imagingPlannerAcceptPlanetary, &Options::setImagingPlannerAcceptPlanetary);
1281 setupFilterButton(ui->SupernovaRemnantCB, &Options::imagingPlannerAcceptSupernovaRemnant,
1282 &Options::setImagingPlannerAcceptSupernovaRemnant);
1283 setupFilterButton(ui->GalaxyCB, &Options::imagingPlannerAcceptGalaxy, &Options::setImagingPlannerAcceptGalaxy);
1284 setupFilterButton(ui->GalaxyClusterCB, &Options::imagingPlannerAcceptGalaxyCluster,
1285 &Options::setImagingPlannerAcceptGalaxyCluster);
1286 setupFilterButton(ui->DarkNebulaCB, &Options::imagingPlannerAcceptDarkNebula, &Options::setImagingPlannerAcceptDarkNebula);
1287 setupFilterButton(ui->OtherCB, &Options::imagingPlannerAcceptOther, &Options::setImagingPlannerAcceptOther);
1289 setupFilter2Buttons(ui->pickedCB, ui->notPickedCB, ui->dontCarePickedCB,
1290 &Options::imagingPlannerShowPicked, &Options::imagingPlannerShowNotPicked, &Options::imagingPlannerDontCarePicked,
1291 &Options::setImagingPlannerShowPicked, &Options::setImagingPlannerShowNotPicked, &Options::setImagingPlannerDontCarePicked);
1293 setupFilter2Buttons(ui->imagedCB, ui->notImagedCB, ui->dontCareImagedCB,
1294 &Options::imagingPlannerShowImaged, &Options::imagingPlannerShowNotImaged, &Options::imagingPlannerDontCareImaged,
1295 &Options::setImagingPlannerShowImaged, &Options::setImagingPlannerShowNotImaged, &Options::setImagingPlannerDontCareImaged);
1297 setupFilter2Buttons(ui->ignoredCB, ui->notIgnoredCB, ui->dontCareIgnoredCB,
1298 &Options::imagingPlannerShowIgnored, &Options::imagingPlannerShowNotIgnored, &Options::imagingPlannerDontCareIgnored,
1299 &Options::setImagingPlannerShowIgnored, &Options::setImagingPlannerShowNotIgnored,
1300 &Options::setImagingPlannerDontCareIgnored);
1302 ui->keywordEdit->setText(Options::imagingPlannerKeyword());
1303 ui->keywordEdit->setAcceptRichText(
false);
1304 m_Keyword = Options::imagingPlannerKeyword();
1305 setupFilter2Buttons(ui->keywordCB, ui->notKeywordCB, ui->dontCareKeywordCB,
1306 &Options::imagingPlannerShowKeyword, &Options::imagingPlannerShowNotKeyword, &Options::imagingPlannerDontCareKeyword,
1307 &Options::setImagingPlannerShowKeyword, &Options::setImagingPlannerShowNotKeyword,
1308 &Options::setImagingPlannerDontCareKeyword);
1313 ui->useArtificialHorizon->setChecked(Options::imagingPlannerUseArtificialHorizon());
1314 m_UseArtificialHorizon = Options::imagingPlannerUseArtificialHorizon();
1315 ui->minMoon->setValue(Options::imagingPlannerMinMoonSeparation());
1316 m_MinMoon = Options::imagingPlannerMinMoonSeparation();
1317 ui->maxMoonAltitude->setValue(Options::imagingPlannerMaxMoonAltitude());
1318 m_MaxMoonAltitude = Options::imagingPlannerMaxMoonAltitude();
1319 ui->minAltitude->setValue(Options::imagingPlannerMinAltitude());
1320 m_MinAltitude = Options::imagingPlannerMinAltitude();
1321 ui->minHours->setValue(Options::imagingPlannerMinHours());
1322 m_MinHours = Options::imagingPlannerMinHours();
1323 m_CatalogSortModel->setMinHours(Options::imagingPlannerMinHours());
1326 if (m_UseArtificialHorizon == ui->useArtificialHorizon->isChecked())
1328 m_UseArtificialHorizon = ui->useArtificialHorizon->isChecked();
1329 Options::setImagingPlannerUseArtificialHorizon(ui->useArtificialHorizon->isChecked());
1330 Options::self()->save();
1336 if (m_MinMoon == ui->minMoon->value())
1338 m_MinMoon = ui->minMoon->value();
1339 Options::setImagingPlannerMinMoonSeparation(ui->minMoon->value());
1340 Options::self()->save();
1346 if (m_MaxMoonAltitude == ui->maxMoonAltitude->value())
1348 m_MaxMoonAltitude = ui->maxMoonAltitude->value();
1349 Options::setImagingPlannerMaxMoonAltitude(ui->maxMoonAltitude->value());
1350 Options::self()->save();
1356 if (m_MinAltitude == ui->minAltitude->value())
1358 m_MinAltitude = ui->minAltitude->value();
1359 Options::setImagingPlannerMinAltitude(ui->minAltitude->value());
1360 Options::self()->save();
1366 if (m_MinHours == ui->minHours->value())
1368 m_MinHours = ui->minHours->value();
1369 Options::setImagingPlannerMinHours(ui->minHours->value());
1370 Options::self()->save();
1371 m_CatalogSortModel->setMinHours(Options::imagingPlannerMinHours());
1372 m_CatalogSortModel->invalidate();
1373 ui->CatalogView->resizeColumnsToContents();
1377 updateSortConstraints();
1379 m_CatalogSortModel->setMinHours(ui->minHours->value());
1381 ui->CatalogView->setColumnHidden(NOTES_COLUMN,
true);
1390 ui->userNotesLabel->setVisible(true);
1391 ui->userNotesEdit->setText(ui->userNotes->text());
1392 ui->userNotesEdit->setVisible(true);
1393 ui->userNotesEditButton->setVisible(false);
1394 ui->userNotesDoneButton->setVisible(true);
1395 ui->userNotes->setVisible(false);
1396 ui->userNotesLabel->setVisible(true);
1397 ui->userNotesOpenLink->setVisible(false);
1398 ui->userNotesOpenLink2->setVisible(false);
1399 ui->userNotesOpenLink3->setVisible(false);
1405 QString urlString = findUrl(ui->userNotes->text());
1406 if (urlString.isEmpty())
1408 QDesktopServices::openUrl(QUrl(urlString));
1413 QString urlString = findUrl(ui->userNotes->text(), 2);
1414 if (urlString.isEmpty())
1416 QDesktopServices::openUrl(QUrl(urlString));
1421 QString urlString = findUrl(ui->userNotes->text(), 3);
1422 if (urlString.isEmpty())
1424 QDesktopServices::openUrl(QUrl(urlString));
1433 m_CatalogSortModel->setSortColumn(column);
1434 m_CatalogSortModel->invalidate();
1435 ui->CatalogView->resizeColumnsToContents();
1443 qRegisterMetaType<QList<QStandardItem * >> (
"QList<QStandardItem *>");
1444 connect(
this, &ImagingPlanner::addRow,
this, &ImagingPlanner::addRowSlot);
1454 installEventFilters();
1456 m_PlateSolve.reset(
new PlateSolve(
this));
1457 m_PlateSolve->enableAuxButton(
"Retake screenshot",
1458 "Retake the screenshot of the object if you're having issues solving.");
1460 connect(m_PlateSolve.get(), &PlateSolve::auxClicked,
this, [
this]()
1462 m_PlateSolve->abort();
1467void ImagingPlanner::installEventFilters()
1471 ui->SearchText->installEventFilter(
this);
1472 ui->userNotesEdit->installEventFilter(
this);
1473 ui->keywordEdit->installEventFilter(
this);
1474 ui->ImagePreviewCreditLink->installEventFilter(
this);
1475 ui->ImagePreviewCredit->installEventFilter(
this);
1476 ui->ImagePreview->installEventFilter(
this);
1477 ui->CatalogView->viewport()->installEventFilter(
this);
1478 ui->CatalogView->installEventFilter(
this);
1479 ui->helpButton->installEventFilter(
this);
1482void ImagingPlanner::removeEventFilters()
1484 ui->SearchText->removeEventFilter(
this);
1485 ui->userNotesEdit->removeEventFilter(
this);
1486 ui->keywordEdit->removeEventFilter(
this);
1487 ui->ImagePreviewCreditLink->removeEventFilter(
this);
1488 ui->ImagePreviewCredit->removeEventFilter(
this);
1489 ui->ImagePreview->removeEventFilter(
this);
1490 ui->CatalogView->viewport()->removeEventFilter(
this);
1491 ui->helpButton->removeEventFilter(
this);
1494void ImagingPlanner::openOptionsMenu()
1496 QSharedPointer<ImagingPlannerOptions> options(
new ImagingPlannerOptions(
this));
1502void ImagingPlanner::getHelp()
1505 const QUrl url(
"https://kstars-docs.kde.org/en/user_manual/tool-imaging-planner.html");
1510KSMoon *ImagingPlanner::getMoon()
1512 if (KStarsData::Instance() ==
nullptr)
1518 auto tz = QTimeZone(getGeo()->TZ() * 3600);
1519 KStarsDateTime midnight = KStarsDateTime(getDate().addDays(1), QTime(0, 1));
1521 CachingDms LST = getGeo()->GSTtoLST(getGeo()->LTtoUT(midnight).gst());
1522 KSNumbers numbers(midnight.
djd());
1523 moon->
updateCoords(&numbers,
true, getGeo()->lat(), &LST,
true);
1529void ImagingPlanner::updateMoon()
1531 KSMoon *moon = getMoon();
1536 auto tz = QTimeZone(getGeo()->TZ() * 3600);
1537 KStarsDateTime midnight = KStarsDateTime(getDate().addDays(1), QTime(0, 1));
1538 CachingDms LST = getGeo()->GSTtoLST(getGeo()->LTtoUT(midnight).gst());
1539 KSNumbers numbers(midnight.
djd());
1541 sun->
updateCoords(&numbers,
true, getGeo()->lat(), &LST,
true);
1545 ui->moonPercentLabel->setText(QString(
"%1%").arg(moon->
illum() * 100.0 + 0.5, 0,
'f', 0));
1548bool ImagingPlanner::scrollToName(
const QString &name)
1552 QModelIndexList matchList = ui->CatalogView->model()->match(ui->CatalogView->model()->index(0, 0),
Qt::EditRole,
1554 if(matchList.count() >= 1)
1557 for (
int i = 0; i < matchList.count(); i++)
1559 QString nn = ui->CatalogView->model()->data(matchList[i],
Qt::DisplayRole).toString();
1566 ui->CatalogView->scrollTo(matchList[bestIndex]);
1567 ui->CatalogView->setCurrentIndex(matchList[bestIndex]);
1573void ImagingPlanner::searchSlot()
1575 if (m_loadingCatalog)
1577 QString origName = ui->SearchText->toPlainText().trimmed();
1578 QString
name = tweakNames(origName);
1579 ui->SearchText->setPlainText(name);
1583 if (!scrollToName(name))
1584 KSNotification::sorry(
i18n(
"No match for \"%1\"", origName));
1587 ui->SearchText->clear();
1588 ui->SearchText->setPlainText(
"");
1591void ImagingPlanner::initUserNotes()
1593 ui->userNotesLabel->setVisible(
true);
1594 ui->userNotesEdit->setVisible(
false);
1595 ui->userNotesEditButton->setVisible(
true);
1596 ui->userNotesDoneButton->setVisible(
false);
1597 ui->userNotes->setVisible(
true);
1598 ui->userNotesLabel->setVisible(
true);
1599 ui->userNotesOpenLink->setVisible(
false);
1600 ui->userNotesOpenLink2->setVisible(
false);
1601 ui->userNotesOpenLink3->setVisible(
false);
1604void ImagingPlanner::disableUserNotes()
1606 ui->userNotesEdit->setVisible(
false);
1607 ui->userNotesEditButton->setVisible(
false);
1608 ui->userNotesDoneButton->setVisible(
false);
1609 ui->userNotes->setVisible(
false);
1610 ui->userNotesLabel->setVisible(
false);
1611 ui->userNotesOpenLink->setVisible(
false);
1612 ui->userNotesOpenLink2->setVisible(
false);
1613 ui->userNotesOpenLink3->setVisible(
false);
1616void ImagingPlanner::userNotesEditFinished()
1618 const QString ¬es = ui->userNotesEdit->toPlainText().
trimmed();
1619 ui->userNotes->setText(notes);
1620 ui->userNotesLabel->setVisible(notes.
isEmpty());
1621 ui->userNotesEdit->setVisible(
false);
1622 ui->userNotesEditButton->setVisible(
true);
1623 ui->userNotesDoneButton->setVisible(
false);
1624 ui->userNotes->setVisible(
true);
1625 ui->userNotesLabel->setVisible(
true);
1626 setCurrentObjectNotes(notes);
1627 setupNotesLinks(notes);
1629 auto o = currentCatalogObject();
1631 saveToDB(currentObjectName(), currentObjectFlags(), notes);
1634void ImagingPlanner::updateNotes(
const QString ¬es)
1636 ui->userNotes->setMaximumWidth(ui->RightPanel->width() - 125);
1638 ui->userNotes->setText(notes);
1639 ui->userNotesLabel->setVisible(notes.
isEmpty());
1640 setupNotesLinks(notes);
1643void ImagingPlanner::setupNotesLinks(
const QString ¬es)
1645 QString
link = findUrl(notes);
1646 ui->userNotesOpenLink->setVisible(!
link.isEmpty());
1647 if (!
link.isEmpty())
1648 ui->userNotesOpenLink->setToolTip(
i18n(
"Open a browser with the 1st link in this note: %1", link));
1650 link = findUrl(notes, 2);
1651 ui->userNotesOpenLink2->setVisible(!
link.isEmpty());
1652 if (!
link.isEmpty())
1653 ui->userNotesOpenLink2->setToolTip(
i18n(
"Open a browser with the 2nd link in this note: %1", link));
1655 link = findUrl(notes, 3);
1656 ui->userNotesOpenLink3->setVisible(!
link.isEmpty());
1657 if (!
link.isEmpty())
1658 ui->userNotesOpenLink3->setToolTip(
i18n(
"Open a browser with the 3rd link in this note: %1", link));
1661bool ImagingPlanner::internetNameSearch(
const QString &name,
bool abellPlanetary,
int abellNumber,
1665 QElapsedTimer timer;
1667 QString filteredName =
name;
1671 QString resolverName = filteredName;
1675 resolverName = QString(
"PN A66 %1").
arg(abellNumber);
1682 CatalogObject
object = cedata.second;
1685 if (
object.
name() ==
object.name2())
1686 object.setName2(filteredName);
1687 object.setName(filteredName);
1690 m_manager.add_object(CatalogsDB::user_catalog_id,
object);
1691 const auto &added_object =
1692 m_manager.get_object(
object.getId(), CatalogsDB::user_catalog_id);
1694 if (added_object.first)
1696 *catObject = KStarsData::Instance()
1698 ->catalogsComponent()
1702 DPRINTF(stderr,
"***** Found %s using name resolver (%.1fs)\n",
name.
toLatin1().
data(),
1707bool isAbellPlanetary(
const QString &name,
int *number)
1713 auto match = abellRE.match(name);
1714 if (
match.hasMatch())
1732 std::list<CatalogObject> objs =
1733 m_manager.find_objects_by_name(filteredName, 1,
true);
1737 int abellNumber = 0;
1738 bool abellPlanetary = isAbellPlanetary(name, &abellNumber);
1739 if (objs.size() > 0 && abellPlanetary && objs.front().type() == SkyObject::GALAXY_CLUSTER)
1742 if (objs.size() == 0 && filteredName.
size() > 0)
1745 const QString capitalized = capitalize(filteredName);
1746 objs = m_manager.find_objects_by_name(capitalized, 1,
true);
1747 if (objs.size() > 0 && abellPlanetary && objs.front().type() == SkyObject::GALAXY_CLUSTER)
1750 if (objs.size() == 0)
1753 const QString lowerCase = filteredName.
toLower();
1754 objs = m_manager.find_objects_by_name(lowerCase, 1,
true);
1755 if (objs.size() > 0 && abellPlanetary && objs.front().type() == SkyObject::GALAXY_CLUSTER)
1764 QString name2 = filteredName;
1766 objs = m_manager.find_objects_by_name(name2, 1,
true);
1770 QString name2 = filteredName;
1772 objs = m_manager.find_objects_by_name(name2, 1,
true);
1775 if (objs.size() == 0 && !abellPlanetary)
1776 objs = m_manager.find_objects_by_name(filteredName.
toLower(), 20,
false);
1777 if (objs.size() == 0)
1778 return internetNameSearch(filteredName, abellPlanetary, abellNumber, catObject);
1780 if (objs.size() == 0)
1784 *catObject = objs.front();
1785 if (objs.size() >= 1)
1787 bool foundIt =
false;
1788 QString addSpace = filteredName;
1790 QString addComma = filteredName;
1792 QString sh2Fix = filteredName;
1794 for (
const auto &obj : objs)
1812 if (objs.size() == 1)
1813 DPRINTF(stderr,
" ========> \"%s\" had 1 match \"%s\", but not trusting it!!!!\n",
name.
toLatin1().
data(),
1814 objs.front().name().toLatin1().data());
1816 if (internetNameSearch(filteredName, abellPlanetary, abellNumber, catObject))
1819 DPRINTF(stderr,
"Didn't find %s (%s) -- Not using name \"%s\" name2 \"%s\" longname \"%s\"\n",
1834 auto o = m_CatalogHash.find(lName);
1835 if (o == m_CatalogHash.end())
1840void ImagingPlanner::clearObjects()
1846 m_CatalogHash.clear();
1854 if (getObject(lName) !=
nullptr)
1856 DPRINTF(stderr,
"Didn't add \"%s\" because it's already there\n",
name.
toLatin1().
data());
1861 if (!getKStarsCatalogObject(lName, &o))
1863 DPRINTF(stderr,
"************* Couldn't find \"%s\"\n", lName.
toLatin1().
data());
1866 m_CatalogHash[lName] = o;
1867 return &(m_CatalogHash[lName]);
1872bool ImagingPlanner::addCatalogItem(
const KSAlmanac &ksal,
const QString &name,
int flags)
1874 CatalogObject *
object = addObject(name);
1875 if (
object ==
nullptr)
1878 auto getItemWithUserRole = [](
const QString & itemText) -> QStandardItem *
1880 QStandardItem *ret =
new QStandardItem(itemText);
1887 QList<QStandardItem *> itemList;
1888 for (
int i = 0; i < LAST_COLUMN; ++i)
1890 if (i == NAME_COLUMN)
1892 itemList.
append(getItemWithUserRole(name));
1894 else if (i == HOURS_COLUMN)
1896 double runHours = getRunHours(*
object, getDate(), *getGeo(), ui->minAltitude->value(), ui->minMoon->value(),
1897 ui->maxMoonAltitude->value(), ui->useArtificialHorizon->isChecked());
1898 auto hoursItem = getItemWithUserRole(QString(
"%1").arg(runHours, 0,
'f', 1));
1899 hoursItem->setData(runHours, HOURS_ROLE);
1900 itemList.
append(hoursItem);
1902 else if (i == TYPE_COLUMN)
1904 auto typeItem = getItemWithUserRole(QString(
"%1").arg(SkyObject::typeShortName(object->
type())));
1905 typeItem->setData(object->
type(), TYPE_ROLE);
1906 itemList.
append(typeItem);
1908 else if (i == SIZE_COLUMN)
1910 double size = std::max(object->
a(), object->
b());
1911 auto sizeItem = getItemWithUserRole(QString(
"%1'").arg(
size, 0,
'f', 1));
1912 sizeItem->setData(
size, SIZE_ROLE);
1913 itemList.
append(sizeItem);
1915 else if (i == ALTITUDE_COLUMN)
1917 const auto time = KStarsDateTime(QDateTime(getDate(), QTime(12, 0)));
1918 const double altitude = getMaxAltitude(ksal, getDate(), getGeo(), *
object, 0, 0);
1919 auto altItem = getItemWithUserRole(QString(
"%1º").arg(altitude, 0,
'f', 0));
1920 altItem->setData(altitude, ALTITUDE_ROLE);
1921 itemList.
append(altItem);
1923 else if (i == MOON_COLUMN)
1925 KSMoon *moon = getMoon();
1931 auto tz = QTimeZone(getGeo()->TZ() * 3600);
1932 KStarsDateTime midnight = KStarsDateTime(getDate().addDays(1), QTime(0, 1));
1934 KSNumbers numbers(midnight.
djd());
1938 auto moonItem = getItemWithUserRole(QString(
"%1º").arg(separation, 0,
'f', 0));
1939 moonItem->setData(separation, MOON_ROLE);
1940 itemList.
append(moonItem);
1944 auto moonItem = getItemWithUserRole(QString(
""));
1945 moonItem->setData(-1, MOON_ROLE);
1948 else if (i == CONSTELLATION_COLUMN)
1950 QString cname = KStarsData::Instance()
1952 ->constellationBoundary()
1953 ->constellationName(
object);
1955 auto constellationItem = getItemWithUserRole(cname);
1956 itemList.
append(constellationItem);
1958 else if (i == COORD_COLUMN)
1960 itemList.
append(getItemWithUserRole(shortCoordString(object->
ra0(), object->
dec0())));
1962 else if (i == FLAGS_COLUMN)
1964 QStandardItem *flag = getItemWithUserRole(
"flag");
1965 flag->
setData(flags, FLAGS_ROLE);
1968 else if (i == NOTES_COLUMN)
1970 QStandardItem *notes = getItemWithUserRole(
"notes");
1971 notes->
setData(QString(), NOTES_ROLE);
1976 DPRINTF(stderr,
"Bug in addCatalogItem() !\n");
1981 emit addRow(itemList);
1987 m_CatalogModel->appendRow(itemList);
1991void ImagingPlanner::recompute()
1993 setStatus(
i18n(
"Updating tables..."));
1996 m_CatalogSortModel->setSourceModel(
nullptr);
1998 QElapsedTimer timer;
2001 auto tz = QTimeZone(getGeo()->TZ() * 3600);
2002 KStarsDateTime midnight = KStarsDateTime(getDate().addDays(1), QTime(0, 1));
2003 KStarsDateTime ut = getGeo()->LTtoUT(KStarsDateTime(midnight));
2004 KSAlmanac ksal(ut, getGeo());
2006 for (
int i = 0; i < m_CatalogModel->rowCount(); ++i)
2008 const QString &
name = m_CatalogModel->item(i, 0)->text();
2009 const CatalogObject *catalogEntry = getObject(name);
2010 if (catalogEntry ==
nullptr)
2012 DPRINTF(stderr,
"************* Couldn't find \"%s\"\n",
name.
toLatin1().
data());
2015 double runHours = getRunHours(*catalogEntry, getDate(), *getGeo(), ui->minAltitude->value(),
2016 ui->minMoon->value(), ui->maxMoonAltitude->value(), ui->useArtificialHorizon->isChecked());
2017 QString hoursText = QString(
"%1").arg(runHours, 0,
'f', 1);
2018 QStandardItem *hItem =
new QStandardItem(hoursText);
2021 hItem->
setData(runHours, HOURS_ROLE);
2022 m_CatalogModel->setItem(i, HOURS_COLUMN, hItem);
2025 const auto time = KStarsDateTime(QDateTime(getDate(), QTime(12, 0)));
2026 const double altitude = getMaxAltitude(ksal, getDate(), getGeo(), *catalogEntry, 0, 0);
2027 QString altText = QString(
"%1º").arg(altitude, 0,
'f', 0);
2028 auto altItem =
new QStandardItem(altText);
2030 altItem->setData(altitude, ALTITUDE_ROLE);
2031 m_CatalogModel->setItem(i, ALTITUDE_COLUMN, altItem);
2033 KSMoon *moon = getMoon();
2039 auto tz = QTimeZone(getGeo()->TZ() * 3600);
2040 KStarsDateTime midnight = KStarsDateTime(getDate().addDays(1), QTime(0, 1));
2042 KSNumbers numbers(midnight.
djd());
2046 QString moonText = QString(
"%1º").arg(separation, 0,
'f', 0);
2047 auto moonItem =
new QStandardItem(moonText);
2049 moonItem->setData(separation, MOON_ROLE);
2050 m_CatalogModel->setItem(i, MOON_COLUMN, moonItem);
2054 auto moonItem =
new QStandardItem(
"");
2056 moonItem->setData(-1, MOON_ROLE);
2057 m_CatalogModel->setItem(i, MOON_COLUMN, moonItem);
2061 const bool imaged = m_CatalogModel->item(i, FLAGS_COLUMN)->data(FLAGS_ROLE).toInt() & IMAGED_BIT;
2063 highlightImagedObject(m_CatalogModel->index(i, NAME_COLUMN),
true);
2064 const bool picked = m_CatalogModel->item(i, FLAGS_COLUMN)->data(FLAGS_ROLE).toInt() & PICKED_BIT;
2066 highlightPickedObject(m_CatalogModel->index(i, NAME_COLUMN),
true);
2069 m_CatalogSortModel->setSourceModel(m_CatalogModel.data());
2071 DPRINTF(stderr,
"Recompute took %.1fs\n", timer.
elapsed() / 1000.0);
2082bool ALREADY_CHECKING =
false;
2083int ALREADY_CHECKING_INDEX = -1;
2085struct ObjectNeighbor
2087 CatalogObject object;
2090 ObjectNeighbor(CatalogObject o,
double d, QString nei) : object(o), distance(d), neighbor(nei) {}
2096void ImagingPlanner::checkTargets2(
bool backwards)
2098 if (ALREADY_CHECKING)
2101 ALREADY_CHECKING_INDEX--;
2103 ALREADY_CHECKING_INDEX++;
2105 if (sortedAddedObjects.size() == 0)
2107 fprintf(stderr,
"No TARGETS\n");
2110 if (ALREADY_CHECKING_INDEX >= sortedAddedObjects.size())
2111 ALREADY_CHECKING_INDEX = 0;
2112 else if (ALREADY_CHECKING_INDEX < 0)
2113 ALREADY_CHECKING_INDEX = sortedAddedObjects.size() - 1;
2114 KStarsDateTime time = KStarsData::Instance()->
clock()->
utc();
2115 dms lst = getGeo()->GSTtoLST(time.
gst());
2116 CatalogObject &o = sortedAddedObjects[ALREADY_CHECKING_INDEX].object;
2118 fprintf(stderr,
"%d: %s\n", ALREADY_CHECKING_INDEX, o.
name().
toLatin1().
data());
2121 bool keepGround = Options::showGround();
2122 bool keepAnimatedSlew = Options::useAnimatedSlewing();
2123 Options::setShowGround(
false);
2124 Options::setUseAnimatedSlewing(
false);
2128 Options::setShowGround(keepGround);
2129 Options::setUseAnimatedSlewing(keepAnimatedSlew);
2135void ImagingPlanner::checkTargets(
bool justCheckCurrentCatalog)
2137 if (ALREADY_CHECKING)
2139 checkTargets2(
false);
2142 ALREADY_CHECKING =
true;
2145 FlagComponent *flags = KStarsData::Instance()->
skyComposite()->flags();
2146 for (
int i = flags->
size() - 1; i >= 0; --i) flags->
remove(i);
2147 int rows = m_CatalogModel->rowCount();
2150 for (
int i = 0; i < rows; ++i)
2152 const QString &
name = m_CatalogModel->item(i, NAME_COLUMN)->text();
2153 auto object = getObject(name);
2157 flags->
add(SkyPoint(object->
ra(), object->
dec()),
"J2000.0",
"", name,
Qt::red);
2160 fprintf(stderr,
"Added %d flags\n", numFlags);
2164 QList<QString> targets;
2165 QList<QString> newObjects;
2166 if (!justCheckCurrentCatalog)
2172 QFile inputFile(fileName);
2175 QTextStream in(&inputFile);
2178 const QString line = in.readLine().trimmed();
2179 if (line.
size() > 0 && line[0] !=
'#' && newObjects.
indexOf(line) == -1)
2184 if (newObjects.
size() == 0)
2186 fprintf(stderr,
"No New Targets\n");
2191 QList<CatalogObject> addedObjects;
2192 sortedAddedObjects.clear();
2194 int count = 0, good = 0;
2195 for (
int i = 0; i < rows; ++i)
2197 const QString &
name = m_CatalogModel->item(i, NAME_COLUMN)->text();
2199 auto o = getObject(name);
2207 fprintf(stderr,
"********** %d/%d targets found. %d unique test objects\n", good, count, newObjects.
size());
2211 if (!justCheckCurrentCatalog)
2213 fprintf(stderr,
"Adding: ");
2214 for (
const auto &name : newObjects)
2216 if (getObject(name) !=
nullptr)
2221 CatalogObject object;
2222 if (!getKStarsCatalogObject(name, &
object))
2227 object.
setRA(
object.ra0());
2228 object.setDec(
object.dec0());
2231 fprintf(stderr,
"%s had primary name %s -- reverting.\n",
2233 object.setName(name);
2239 fprintf(stderr,
"\n--------------------------------------------------------\n");
2243 for (
int i = 0; i < addedObjects.
size(); ++i)
2245 auto &
object = addedObjects[i];
2246 double closest = 1e9;
2247 QString closestName;
2248 for (
int j = 0; j < targets.
size(); ++j)
2250 if (justCheckCurrentCatalog && i == j)
2252 auto name2 = targets[j];
2253 auto object2 = getObject(name2);
2254 if (object2 ==
nullptr)
2256 fprintf(stderr,
"********************************************************* O2 for targets[%d]: %s null!\n", j,
2260 object2->setRA(object2->ra0());
2261 object2->setDec(object2->dec0());
2262 const dms dist =
object.angularDistanceTo(object2);
2263 const double arcsecDist = dist.
Degrees() * 3600.0;
2264 if (closest > arcsecDist)
2266 closest = arcsecDist;
2267 closestName = name2;
2271 sortedAddedObjects.
push_back(ObjectNeighbor(addedObjects[i], closest, closestName));
2275 if (justCheckCurrentCatalog)
2277 fprintf(stderr,
"%7.1f %-10s closest %s\n", closest / 60.0,
object.
name().toLatin1().data(),
2282 double closestNew = 1e9;
2283 QString closestNewName;
2284 for (
int j = 0; j < addedObjects.
size() - 1; ++j)
2286 if (i == j)
continue;
2287 auto object2 = addedObjects[j];
2288 object2.setRA(object2.ra0());
2289 object2.setDec(object2.dec0());
2290 const dms dist =
object.angularDistanceTo(&object2);
2291 const double arcsecDist = dist.
Degrees() * 3600.0;
2292 if (closestNew > arcsecDist)
2294 closestNew = arcsecDist;
2295 closestNewName = object2.name();
2298 fprintf(stderr,
"%7.1f %-10s (closest %s) (closestNew %5.0f' %-10s)\n",
2299 closest / 60.0,
object.
name().toLatin1().data(), closestName.
toLatin1().
data(),
2301 flags->
add(SkyPoint(
object.ra(),
object.
dec()),
"J2000.0",
"", QString(
"%1").arg(
object.
name()),
Qt::yellow);
2304 std::sort(sortedAddedObjects.begin(), sortedAddedObjects.end(),
2305 [](
const ObjectNeighbor & a,
const ObjectNeighbor & b)
2307 return a.distance > b.distance;
2309 if (justCheckCurrentCatalog)
2311 fprintf(stderr,
"Sorted: ------------------------------------------\n");
2312 for (
const auto &o : sortedAddedObjects)
2313 fprintf(stderr,
"%7.1f %-10s closest %s\n",
2315 o.neighbor.toLatin1().data());
2317 fprintf(stderr,
"DONE. ------------------------------------------\n");
2321QString ImagingPlanner::defaultDirectory()
const
2335QFileInfoList findDefaultDirectories()
2338 QDir kDir(kstarsDir);
2341 nameFilters <<
"ImagingPlanner*";
2342 QFileInfoList dirs1 = kDir.entryInfoList(nameFilters);
2347 dirs1.append(dirs2);
2348 std::sort(dirs1.begin(), dirs1.end(), sortOldest);
2355QString ImagingPlanner::findDefaultCatalog()
const
2357 QFileInfoList subDirs = findDefaultDirectories();
2358 for(
const auto &dd : subDirs)
2360 QDir subDir(dd.absoluteFilePath());
2361 const QStringList csvFilter({
"*.csv"});
2363 if (files.size() > 0)
2368 for (
const auto &file : files)
2371 firstFile = file.absoluteFilePath();
2373 return file.absoluteFilePath();
2382void ImagingPlanner::loadInitialCatalog()
2384 QString catalog = Options::imagingPlannerCatalogPath();
2386 catalog = findDefaultCatalog();
2389 KSNotification::sorry(
2390 i18n(
"You need to load a catalog to start using this tool.\n"
2391 "Use the Load Catalog button if you have one.\n"
2392 "See Data -> Download New Data if not..."));
2393 setStatus(
i18n(
"No Catalog!"));
2396 loadCatalog(catalog);
2399void ImagingPlanner::setStatus(
const QString &message)
2401 ui->statusLabel->setText(message);
2404void ImagingPlanner::catalogLoaded()
2406 DPRINTF(stderr,
"All catalogs loaded: %d of %d have catalog images\n", m_numWithImage, m_numWithImage + m_numMissingImage);
2412 ui->CatalogView->setColumnHidden(FLAGS_COLUMN,
true);
2413 ui->CatalogView->setColumnHidden(NOTES_COLUMN,
true);
2415 m_CatalogSortModel->invalidate();
2417 ui->CatalogView->resizeColumnsToContents();
2420 auto index = ui->CatalogView->
model()->
index(0, 0);
2422 ui->CatalogView->selectionModel()->select(index,
2424 ui->CatalogView->setFocus();
2431void ImagingPlanner::updateStatus()
2433 if (currentObjectName().isEmpty())
2435 const int numDisplayedObjects = m_CatalogSortModel->rowCount();
2436 const int totalCatalogObjects = m_CatalogModel->rowCount();
2438 if (numDisplayedObjects > 0)
2439 setStatus(
i18n(
"Select an object."));
2440 else if (totalCatalogObjects > 0)
2441 setStatus(
i18n(
"Check Filters to unhide objects."));
2443 setStatus(
i18n(
"Load a Catalog."));
2450void ImagingPlanner::showEvent(
QShowEvent * e)
2453 if (m_initialShow ==
false)
2455 m_initialShow =
true;
2463void ImagingPlanner::slotClose()
2469QUrl ImagingPlanner::getAstrobinUrl(
const QString &target,
bool requireAwards,
bool requireSomeFilters,
double minRadius,
2472 QString myQuery = QString(
"text={\"value\":\"%1\",\"matchType\":\"ALL\"}").arg(target);
2475 auto localTime = getGeo()->UTtoLT(KStarsData::Instance()->clock()->utc());
2476 QDate today = localTime.date();
2477 myQuery.
append(QString(
"&date_acquired={\"min\":\"2018-01-01\",\"max\":\"%1\"}").arg(today.
toString(
"yyyy-MM-dd")));
2480 myQuery.
append(QString(
"&award=[\"iotd\",\"top-pick\",\"top-pick-nomination\"]"));
2482 if (requireSomeFilters)
2483 myQuery.
append(QString(
"&filter_types={\"value\":[\"H_ALPHA\",\"SII\",\"OIII\",\"R\",\"G\",\"B\"],\"matchType\":\"ANY\"}"));
2485 if ((minRadius > 0 || maxRadius > 0) && (maxRadius > minRadius))
2486 myQuery.
append(QString(
"&field_radius={\"min\":%1,\"max\":%2}").arg(minRadius).arg(maxRadius));
2491 QByteArray packed = pack(b);
2493 QByteArray compressed = qCompress(packed).remove(0, 4);
2495 QByteArray b64 = compressed.
toBase64();
2497 replaceByteArrayChars(b64,
'+', QByteArray(
"%2B"));
2498 replaceByteArrayChars(b64,
'=', QByteArray(
"%3D"));
2499 replaceByteArrayChars(b,
'"', QByteArray(
"%22"));
2500 replaceByteArrayChars(b,
':', QByteArray(
"%3A"));
2501 replaceByteArrayChars(b,
'[', QByteArray(
"%5B"));
2502 replaceByteArrayChars(b,
']', QByteArray(
"%5D"));
2503 replaceByteArrayChars(b,
',', QByteArray(
"%2C"));
2504 replaceByteArrayChars(b,
'\'', QByteArray(
"%27"));
2505 replaceByteArrayChars(b,
'{', QByteArray(
"%7B"));
2506 replaceByteArrayChars(b,
'}', QByteArray(
"%7D"));
2508 QString url = QString(
"https://app.astrobin.com/search?p=%1").arg(b64.
toStdString().c_str());
2512void ImagingPlanner::popupAstrobin(
const QString &target)
2514 QString newStr = replaceSpaceWith(target,
"-");
2517 const QUrl url = getAstrobinUrl(newStr, Options::astrobinAward(),
false, Options::astrobinMinRadius(),
2518 Options::astrobinMaxRadius());
2527bool ImagingPlanner::checkIfPageExists(
const QString &urlString)
2532 QUrl url(urlString);
2533 QNetworkRequest request(url);
2534 QNetworkReply *reply = m_networkManager.get(request);
2567void ImagingPlanner::adjustSpecialWebPageButton(
const QString &name)
2573 toolTip =
i18n(
"Search the Professor Seligman online site for NGC images.");
2578 toolTip =
i18n(
"Search the Professor Seligman online site for information about IC objects..");
2583 label =
"Sharpless";
2584 toolTip =
i18n(
"Search the galaxymap.org online site for information about Sharpless2 objects.");
2590 toolTip =
i18n(
"Search Nasa's online site for information about Messier objects..");
2595 toolTip =
i18n(
"Search Emil Ivanov's online site for information about VDB objects.");
2602 const int num = numberPart.
toInt(&ok);
2606 ui->searchSpecialWebPageImages->setText(catalog);
2607 ui->searchSpecialWebPageImages2->setText(catalog);
2608 ui->searchSpecialWebPageImages->setEnabled(
true);
2609 ui->searchSpecialWebPageImages2->setEnabled(
true);
2610 ui->searchSpecialWebPageImages->setToolTip(
toolTip);
2611 ui->searchSpecialWebPageImages2->setToolTip(
toolTip);
2615 ui->searchSpecialWebPageImages->setText(
"");
2616 ui->searchSpecialWebPageImages2->setText(
"");
2617 ui->searchSpecialWebPageImages->setEnabled(
false);
2618 ui->searchSpecialWebPageImages2->setEnabled(
false);
2619 ui->searchSpecialWebPageImages->setToolTip(
"");
2620 ui->searchSpecialWebPageImages2->setToolTip(
"");
2624void ImagingPlanner::searchSpecialWebPageImages()
2627 const QString
objectName = currentObjectName();
2632 const QString numberPart =
objectName.mid(3).trimmed();
2633 const int num = numberPart.
toInt(&ok);
2635 urlString = QString(
"https://cseligman.com/text/atlas/ngc%1%2.htm#%3")
2636 .
arg(num / 100).
arg(num % 100 < 50 ?
"" :
"a").
arg(num);
2640 const QString numberPart =
objectName.mid(2).trimmed();
2641 const int num = numberPart.
toInt(&ok);
2643 urlString = QString(
"https://cseligman.com/text/atlas/ic%1%2.htm#ic%3")
2644 .
arg(num / 100).
arg(num % 100 < 50 ?
"" :
"a").
arg(num);
2648 const QString numberPart =
objectName.mid(3).trimmed();
2649 const int num = numberPart.
toInt(&ok);
2651 urlString = QString(
"http://galaxymap.org/cat/view/sharpless/%1").
arg(num);
2655 const QString numberPart =
objectName.mid(1).trimmed();
2656 const int num = numberPart.
toInt(&ok);
2658 urlString = QString(
"https://science.nasa.gov/mission/hubble/science/"
2659 "explore-the-night-sky/hubble-messier-catalog/messier-%1").
arg(num);
2663 const QString numberPart =
objectName.mid(3).trimmed();
2664 const int num = numberPart.
toInt(&ok);
2667 urlString = QString(
"https://www.irida-observatory.org/CCD/VdB%1/VdB%1.html").
arg(num);
2668 if (!checkIfPageExists(urlString))
2669 urlString =
"https://www.emilivanov.com/CCD%20Images/Catalog_VdB.htm";
2676void ImagingPlanner::searchSimbad()
2679 QString
name = currentObjectName();
2681 int abellNumber = 0;
2682 bool abellPlanetary = isAbellPlanetary(name, &abellNumber);
2684 name = QString(
"PN A66 %1").
arg(abellNumber);
2686 name.
replace(QRegularExpression(
"sh2\\s*"),
"sh2-");
2695 QString urlStr = QString(
"https://simbad.cds.unistra.fr/simbad/sim-id?Ident=%1&NbIdent=1"
2696 "&Radius=20&Radius.unit=arcmin&submit=submit+id").
arg(name);
2702void ImagingPlanner::searchWikipedia()
2705 QString wikipediaAddress =
"https://en.wikipedia.org";
2706 QString
name = currentObjectName();
2709 DPRINTF(stderr,
"NULL object sent to Wikipedia.\n");
2715 QString urlStr = QString(
"%1/w/index.php?search=%2").
arg(wikipediaAddress).
arg(replaceSpaceWith(name,
"_"));
2720void ImagingPlanner::searchAstrobin()
2723 QString
name = currentObjectName();
2726 popupAstrobin(name);
2729bool ImagingPlanner::eventFilter(
QObject * obj,
QEvent * event)
2731 if (m_loadingCatalog)
2737 m_InitialLoad =
false;
2738 setStatus(
i18n(
"Loading Catalogs..."));
2743 QMouseEvent *mouseEvent =
static_cast<QMouseEvent *
>(
event);
2745 if ((obj == ui->helpButton) &&
2748 (mouseEvent->
modifiers() & Qt::KeyboardModifier::ShiftModifier) &&
2749 (mouseEvent->
modifiers() & Qt::KeyboardModifier::ControlModifier) &&
2750 (mouseEvent->
modifiers() & Qt::KeyboardModifier::AltModifier))
2754 ui->DevelFrame->setVisible(!ui->DevelFrame->isVisible());
2765 else if ((obj == ui->CatalogView->viewport()) &&
2769 int numImaged = 0, numNotImaged = 0, numPicked = 0, numNotPicked = 0, numIgnored = 0, numNotIgnored = 0;
2770 QStringList selectedNames;
2771 for (
const auto &r : ui->CatalogView->selectionModel()->selectedRows())
2773 selectedNames.
append(r.siblingAtColumn(0).
data().toString());
2774 bool isPicked = getFlag(r, PICKED_BIT, ui->CatalogView->model());
2775 if (isPicked) numPicked++;
2776 else numNotPicked++;
2777 bool isImaged = getFlag(r, IMAGED_BIT, ui->CatalogView->model());
2778 if (isImaged) numImaged++;
2779 else numNotImaged++;
2780 bool isIgnored = getFlag(r, IGNORED_BIT, ui->CatalogView->model());
2781 if (isIgnored) numIgnored++;
2782 else numNotIgnored++;
2785 if (selectedNames.
size() == 0)
2789 m_PopupMenu =
new ImagingPlannerPopup;
2791 const bool imaged = numImaged > 0;
2792 const bool picked = numPicked > 0;
2793 const bool ignored = numIgnored > 0;
2794 m_PopupMenu->init(
this, selectedNames,
2795 (numImaged > 0 && numNotImaged > 0) ?
nullptr : &imaged,
2796 (numPicked > 0 && numNotPicked > 0) ?
nullptr : &picked,
2797 (numIgnored > 0 && numNotIgnored > 0) ?
nullptr : &ignored);
2799 m_PopupMenu->popup(
pos);
2803 userNotesEditFinished();
2806 keywordEditFinished();
2817 keywordEditFinished();
2818 ui->keywordEdit->clearFocus();
2841 else if ((obj == ui->ImagePreview ||
2842 obj == ui->ImagePreviewCredit ||
2843 obj == ui->ImagePreviewCreditLink) &&
2846 if (!ui->ImagePreviewCreditLink->text().isEmpty())
2848 QUrl url(ui->ImagePreviewCreditLink->text());
2856void ImagingPlanner::keywordEditFinished()
2858 QString kwd = ui->keywordEdit->toPlainText().trimmed();
2859 ui->keywordEdit->clear();
2860 ui->keywordEdit->setText(kwd);
2861 if (m_Keyword != kwd)
2864 Options::setImagingPlannerKeyword(kwd);
2865 Options::self()->save();
2866 updateSortConstraints();
2867 m_CatalogSortModel->invalidate();
2868 ui->CatalogView->resizeColumnsToContents();
2873void ImagingPlanner::setDefaultImage()
2875 ui->ImagePreview->setPixmap(m_NoImagePixmap);
2876 ui->ImagePreview->update();
2877 ui->ImagePreviewCredit->setText(
"");
2878 ui->ImagePreviewCreditLink->setText(
"");
2883 if (m_loadingCatalog)
2886 Q_UNUSED(deselected);
2887 if (selected.
indexes().size() == 0)
2895 auto selection = selected.
indexes()[0];
2896 QString
name = selection.
data().toString();
2897 CatalogObject *
object = getObject(name);
2898 if (
object ==
nullptr)
2905 ui->ImagePreviewCredit->setText(
"");
2906 ui->ImagePreviewCreditLink->setText(
"");
2909 CatalogImageInfo catalogImageInfo;
2910 if (findCatalogImageInfo(name, &catalogImageInfo))
2912 QString filename = catalogImageInfo.m_Filename;
2913 if (!filename.
isEmpty() && !Options::imagingPlannerCatalogPath().isEmpty())
2915 QString imageFullPath = filename;
2916 if (QFileInfo(filename).isRelative())
2918 QString catDir = QFileInfo(Options::imagingPlannerCatalogPath()).absolutePath();
2919 imageFullPath = QString(
"%1%2%3").
arg(catDir)
2922 if (!QFile(imageFullPath).exists())
2923 DPRINTF(stderr,
"Image for \"%s\" -- \"%s\" doesn't exist\n",
2927 if (!catalogImageInfo.m_Link.
isEmpty())
2929 ui->ImagePreviewCreditLink->setText(catalogImageInfo.m_Link);
2930 ui->ImagePreview->setToolTip(
"Click to see original");
2931 ui->ImagePreviewCreditLink->setToolTip(
"Click to see original");
2935 ui->ImagePreviewCreditLink->setText(
"");
2936 ui->ImagePreview->setToolTip(
"");
2937 ui->ImagePreviewCreditLink->setToolTip(
"");
2940 if (!catalogImageInfo.m_Author.
isEmpty() && !catalogImageInfo.m_License.
isEmpty())
2942 ui->ImagePreviewCredit->setText(
2943 QString(
"Credit: %1 (with license %2)").arg(catalogImageInfo.m_Author)
2944 .arg(creativeCommonsString(catalogImageInfo.m_License)));
2945 ui->ImagePreviewCredit->setToolTip(
2946 QString(
"Original image license: %1")
2947 .arg(creativeCommonsTooltipString(catalogImageInfo.m_License)));
2949 else if (!catalogImageInfo.m_Author.
isEmpty())
2951 ui->ImagePreviewCredit->setText(
2952 QString(
"Credit: %1").arg(catalogImageInfo.m_Author));
2953 ui->ImagePreviewCredit->setToolTip(
"");
2955 else if (!catalogImageInfo.m_License.
isEmpty())
2957 ui->ImagePreviewCredit->setText(
2958 QString(
"(license %1)").arg(creativeCommonsString(catalogImageInfo.m_License)));
2959 ui->ImagePreviewCredit->setToolTip(
2960 QString(
"Original image license: %1")
2961 .arg(creativeCommonsTooltipString(catalogImageInfo.m_License)));
2965 ui->ImagePreviewCredit->setText(
"");
2966 ui->ImagePreviewCredit->setToolTip(
"");
2972 object->load_image();
2973 auto image =
object->image();
2979 const QString foundFilename = findObjectImage(name);
2982 constexpr int thumbHeight = 300, thumbWidth = 400;
2983 const QImage img = QImage(foundFilename);
2984 const bool scale = img.
width() > thumbWidth || img.
height() > thumbHeight;
2986 ui->ImagePreview->setPixmap(
2997 adjustSpecialWebPageButton(currentObjectName());
3000void ImagingPlanner::updateDisplays()
3005 if (!currentCatalogObject())
3007 if (ui->CatalogView->model()->rowCount() > 0)
3009 auto index = ui->CatalogView->
model()->
index(0, 0);
3010 ui->CatalogView->selectionModel()->select(index,
3015 auto object = currentCatalogObject();
3018 updateDetails(*
object, currentObjectFlags());
3019 updateNotes(currentObjectNotes());
3020 plotAltitudeGraph(getDate(), object->
ra0(), object->
dec0());
3027void ImagingPlanner::updateDetails(
const CatalogObject &
object,
int flags)
3029 ui->infoObjectName->setText(
object.
name());
3030 ui->infoSize->setText(QString(
"%1' x %2'").arg(
object.a(), 0,
'f', 1).arg(
object.b(), 0,
'f', 1));
3032 QPalette
palette = ui->infoObjectLongName->palette();
3035 ui->infoObjectLongName->setPalette(
palette);
3036 if (
object.longname().isEmpty() || (
object.longname() ==
object.
name()))
3037 ui->infoObjectLongName->clear();
3039 ui->infoObjectLongName->setText(QString(
"(%1)").arg(
object.longname()));
3043 auto noon = KStarsDateTime(getDate(), QTime(12, 0, 0));
3044 QTime riseTime =
object.riseSetTime(noon, getGeo(),
true);
3045 QTime setTime =
object.riseSetTime(noon, getGeo(),
false);
3046 QTime transitTime =
object.transitTime(noon, getGeo());
3047 dms transitAltitude =
object.transitAltitude(noon, getGeo());
3050 KSMoon *moon = getMoon();
3053 const double separation = ui->CatalogView->selectionModel()->currentIndex()
3054 .siblingAtColumn(MOON_COLUMN).data(MOON_ROLE).toDouble();
3056 if (separation >= 0)
3057 moonString = QString(
"%1 \u2220 %3º").
arg(
i18n(
"Moon")).
arg(separation, 0,
'f', 1);
3060 QString riseSetString;
3062 riseSetString = QString(
"%1 %2 @ %3º")
3067 riseSetString = QString(
"%1 %2")
3071 riseSetString = QString(
"%1 %2 %3 %4 @ %5º")
3078 riseSetString = QString(
"%1 %2")
3082 riseSetString = QString(
"%1 %2 %3 %4 @ %5º")
3089 riseSetString = QString(
"%1 %2 %3 %4")
3095 riseSetString = QString(
"%1 %2 %3 %4 %5 %6 @ %7º")
3103 if (moonString.
size() > 0)
3104 riseSetString.
append(QString(
", %1").arg(moonString));
3105 ui->infoRiseSet->setText(riseSetString);
3107 palette = ui->infoObjectFlags->palette();
3109 ui->infoObjectFlags->setPalette(
palette);
3110 ui->infoObjectFlags->setText(flagString(flags));
3119void ImagingPlanner::plotAltitudeGraph(
const QDate &date,
const dms &ra,
const dms &dec)
3121 auto altitudeGraph = ui->altitudeGraph;
3122 altitudeGraph->setAltitudeAxis(-20.0, 90.0);
3125 QVector<QDateTime> jobStartTimes, jobEndTimes;
3126 getRunTimes(date, *getGeo(), ui->minAltitude->value(), ui->minMoon->value(), ui->maxMoonAltitude->value(), ra, dec,
3127 ui->useArtificialHorizon->isChecked(),
3128 &jobStartTimes, &jobEndTimes);
3130 auto tz = QTimeZone(getGeo()->TZ() * 3600);
3131 KStarsDateTime midnight = KStarsDateTime(date.
addDays(1), QTime(0, 1));
3134 KStarsDateTime ut = getGeo()->LTtoUT(KStarsDateTime(midnight));
3135 KSAlmanac ksal(ut, getGeo());
3136 QDateTime dawn = midnight.
addSecs(24 * 3600 * ksal.getDawnAstronomicalTwilight());
3138 QDateTime dusk = midnight.
addSecs(24 * 3600 * ksal.getDuskAstronomicalTwilight());
3141 Ekos::SchedulerJob job;
3142 setupJob(job,
"temp", ui->minAltitude->value(), ui->minMoon->value(), ui->maxMoonAltitude->value(), ra, dec,
3143 ui->useArtificialHorizon->isChecked());
3145 QVector<double> times, alts;
3146 QDateTime plotStart = dusk;
3151 plotStart = plotStart.
addSecs(-1 * 3600);
3154 auto plotEnd = dawn.
addSecs(1 * 3600);
3157 while (t.secsTo(plotEnd) > 0)
3159 SkyPoint coords = job.getTargetCoords();
3160 double alt = getAltitude(getGeo(), coords, t);
3162 double hour = midnight.
secsTo(t) / 3600.0;
3164 t = t.addSecs(60 * 10);
3167 altitudeGraph->plot(getGeo(), &ksal, times, alts);
3169 for (
int i = 0; i < jobStartTimes.
size(); ++i)
3171 auto startTime = jobStartTimes[i];
3172 auto stopTime = jobEndTimes[i];
3173 if (startTime < plotStart) startTime = plotStart;
3174 if (stopTime > plotEnd) stopTime = plotEnd;
3177 stopTime.setTimeZone(tz);
3179 QVector<double> runTimes, runAlts;
3184 while (t.secsTo(stopTime) > 0)
3186 SkyPoint coords = job.getTargetCoords();
3187 double alt = getAltitude(getGeo(), coords, t);
3189 double hour = midnight.
secsTo(t) / 3600.0;
3191 t = t.addSecs(60 * 10);
3193 altitudeGraph->plotOverlay(runTimes, runAlts);
3197void ImagingPlanner::updateCounts()
3199 const int numDisplayedObjects = m_CatalogSortModel->rowCount();
3200 const int totalCatalogObjects = m_CatalogModel->rowCount();
3201 if (numDisplayedObjects == 1)
3202 ui->tableCount->setText(QString(
"1/%1 %2").arg(totalCatalogObjects).arg(
i18n(
"object")));
3204 ui->tableCount->setText(QString(
"%1/%2 %3").arg(numDisplayedObjects).arg(totalCatalogObjects).arg(
i18n(
"objects")));
3207void ImagingPlanner::moveBackOneDay()
3210 QString selection = currentObjectName();
3211 ui->DateEdit->setDate(ui->DateEdit->date().addDays(-1));
3215 scrollToName(selection);
3218void ImagingPlanner::moveForwardOneDay()
3220 QString selection = currentObjectName();
3221 ui->DateEdit->setDate(ui->DateEdit->date().addDays(1));
3225 scrollToName(selection);
3228QString ImagingPlanner::currentObjectName()
const
3230 QString
name = ui->CatalogView->selectionModel()->currentIndex().siblingAtColumn(NAME_COLUMN).
data(
3237 QString
name = currentObjectName();
3238 return getObject(name);
3243void ImagingPlanner::objectDetails()
3245 CatalogObject *current = currentCatalogObject();
3246 if (current ==
nullptr)
3248 auto ut = KStarsData::Instance()->
ut();
3250 QPointer<DetailDialog> dd =
3256void ImagingPlanner::centerOnSkymap()
3258 if (!Options::imagingPlannerCenterOnSkyMap())
3260 reallyCenterOnSkymap();
3263void ImagingPlanner::reallyCenterOnSkymap()
3265 CatalogObject *current = currentCatalogObject();
3266 if (current ==
nullptr)
3272 DPRINTF(stderr,
"found a 0,0 object\n");
3277 KStarsDateTime time = KStarsData::Instance()->
clock()->
utc();
3278 dms lst = getGeo()->GSTtoLST(time.
gst());
3283 bool keepGround = Options::showGround();
3284 bool keepAnimatedSlew = Options::useAnimatedSlewing();
3285 Options::setShowGround(
false);
3286 Options::setUseAnimatedSlewing(
false);
3292 Options::setShowGround(keepGround);
3293 Options::setUseAnimatedSlewing(keepAnimatedSlew);
3296void ImagingPlanner::setSelection(
int flag,
bool enabled)
3298 auto rows = ui->CatalogView->selectionModel()->selectedRows();
3307 QList<QModelIndex> sourceIndeces;
3308 for (
int i = 0; i < rows.size(); ++i)
3310 auto proxyIndex = rows[i].siblingAtColumn(FLAGS_COLUMN);
3311 auto sourceIndex = m_CatalogSortModel->mapToSource(proxyIndex);
3312 sourceIndeces.
append(sourceIndex);
3315 for (
int i = 0; i < sourceIndeces.
size(); ++i)
3317 auto &sourceIndex = sourceIndeces[i];
3321 setFlag(sourceIndex, flag, m_CatalogModel.data());
3323 clearFlag(sourceIndex, flag, m_CatalogModel.data());
3325 QString
name = m_CatalogModel->
data(sourceIndex.siblingAtColumn(NAME_COLUMN)).toString();
3326 int flags = m_CatalogModel->data(sourceIndex.siblingAtColumn(FLAGS_COLUMN), FLAGS_ROLE).toInt();
3327 QString notes = m_CatalogModel->
data(sourceIndex.siblingAtColumn(NOTES_COLUMN), NOTES_ROLE).
toString();
3328 saveToDB(name, flags, notes);
3330 if (flag == IMAGED_BIT)
3331 highlightImagedObject(sourceIndex,
enabled);
3332 if (flag == PICKED_BIT)
3333 highlightPickedObject(sourceIndex,
enabled);
3338void ImagingPlanner::highlightImagedObject(
const QModelIndex &index,
bool imaged)
3341 QColor m_DefaultCellBackground(36, 35, 35);
3342 QColor m_ImagedObjectBackground(10, 65, 10);
3343 QString themeName = KSTheme::Manager::instance()->currentThemeName().
toLatin1().
data();
3344 if (themeName ==
"High Key" || themeName ==
"Default" || themeName ==
"White Balance")
3346 m_DefaultCellBackground = QColor(240, 240, 240);
3347 m_ImagedObjectBackground = QColor(180, 240, 180);
3349 for (
int col = 0; col < LAST_COLUMN; ++col)
3352 m_CatalogModel->setData(colIndex, imaged ? m_ImagedObjectBackground : m_DefaultCellBackground,
Qt::BackgroundRole);
3356void ImagingPlanner::highlightPickedObject(
const QModelIndex &index,
bool picked)
3358 for (
int col = 0; col < LAST_COLUMN; ++col)
3362 auto ff = qvariant_cast<QFont>(
font);
3364 ff.setItalic(picked);
3365 ff.setUnderline(picked);
3371void ImagingPlanner::setSelectionPicked()
3373 setSelection(PICKED_BIT,
true);
3376void ImagingPlanner::setSelectionNotPicked()
3378 setSelection(PICKED_BIT,
false);
3381void ImagingPlanner::setSelectionImaged()
3383 setSelection(IMAGED_BIT,
true);
3386void ImagingPlanner::setSelectionNotImaged()
3388 setSelection(IMAGED_BIT,
false);
3391void ImagingPlanner::setSelectionIgnored()
3393 setSelection(IGNORED_BIT,
true);
3396void ImagingPlanner::setSelectionNotIgnored()
3398 setSelection(IGNORED_BIT,
false);
3401int ImagingPlanner::currentObjectFlags()
3403 auto index = ui->CatalogView->selectionModel()->currentIndex().
siblingAtColumn(FLAGS_COLUMN);
3404 const bool hasFlags = ui->CatalogView->model()->data(index, FLAGS_ROLE).canConvert<
int>();
3407 return ui->CatalogView->model()->data(index, FLAGS_ROLE).toInt();
3410QString ImagingPlanner::currentObjectNotes()
3412 auto index = ui->CatalogView->selectionModel()->currentIndex().
siblingAtColumn(NOTES_COLUMN);
3413 const bool hasNotes = ui->CatalogView->
model()->
data(index, NOTES_ROLE).
canConvert<QString>();
3416 return ui->CatalogView->model()->data(index, NOTES_ROLE).toString();
3419void ImagingPlanner::setCurrentObjectNotes(
const QString ¬es)
3421 auto index = ui->CatalogView->selectionModel()->currentIndex();
3426 auto sourceIndex = m_CatalogSortModel->mapToSource(sibling);
3428 m_CatalogModel->setData(sourceIndex, n, NOTES_ROLE);
3431ImagingPlannerPopup::ImagingPlannerPopup() :
QMenu(nullptr)
3440void ImagingPlannerPopup::init(ImagingPlanner * planner,
const QStringList &names,
3441 const bool * imaged,
const bool * picked,
const bool * ignored)
3444 if (names.
size() == 0)
return;
3447 if (names.
size() == 1)
3449 else if (names.
size() <= 3)
3452 for (
int i = 1; i < names.
size(); i++)
3453 title.append(QString(
", %1").arg(names[i]));
3456 title =
i18n(
"%1, %2 and %3 other objects", names[0], names[1], names.
size() - 2);
3460 QString word = names.
size() == 1 ? names[0] :
i18n(
"objects");
3462 if (imaged ==
nullptr)
3464 addAction(
i18n(
"Mark %1 as NOT imaged", word), planner, &ImagingPlanner::setSelectionNotImaged);
3465 addAction(
i18n(
"Mark %1 as already imaged", word), planner, &ImagingPlanner::setSelectionImaged);
3468 addAction(
i18n(
"Mark %1 as NOT imaged", word), planner, &ImagingPlanner::setSelectionNotImaged);
3470 addAction(
i18n(
"Mark %1 as already imaged", word), planner, &ImagingPlanner::setSelectionImaged);
3472 if (picked ==
nullptr)
3474 addAction(
i18n(
"Un-pick %1", word), planner, &ImagingPlanner::setSelectionNotPicked);
3475 addAction(
i18n(
"Pick %1", word), planner, &ImagingPlanner::setSelectionPicked);
3478 addAction(
i18n(
"Un-pick %1", word), planner, &ImagingPlanner::setSelectionNotPicked);
3480 addAction(
i18n(
"Pick %1", word), planner, &ImagingPlanner::setSelectionPicked);
3483 if (ignored ==
nullptr)
3485 addAction(
i18n(
"Stop ignoring %1", word), planner, &ImagingPlanner::setSelectionNotIgnored);
3486 addAction(
i18n(
"Ignore %1", word), planner, &ImagingPlanner::setSelectionIgnored);
3490 addAction(
i18n(
"Stop ignoring %1", word), planner, &ImagingPlanner::setSelectionNotIgnored);
3492 addAction(
i18n(
"Ignore %1", word), planner, &ImagingPlanner::setSelectionIgnored);
3495 addAction(
i18n(
"Center %1 on SkyMap", names[0]), planner, &ImagingPlanner::reallyCenterOnSkymap);
3497 addAction(
i18n(
"Screenshot some image of %1, plate-solve it, and temporarily place it on the SkyMap", names[0]), planner,
3498 &ImagingPlanner::takeScreenshot);
3502ImagingPlannerDBEntry::ImagingPlannerDBEntry(
const QString &name,
bool picked,
bool imaged,
3503 bool ignored,
const QString ¬es) : m_Name(
name), m_Notes(notes)
3508ImagingPlannerDBEntry::ImagingPlannerDBEntry(
const QString &name,
int flags,
const QString ¬es)
3509 : m_Name(
name), m_Flags(flags), m_Notes(notes)
3513void ImagingPlannerDBEntry::getFlags(
bool * picked,
bool * imaged,
bool * ignored)
3515 *picked = m_Flags & PickedBit;
3516 *imaged = m_Flags & ImagedBit;
3517 *ignored = m_Flags & IgnoredBit;
3521void ImagingPlannerDBEntry::setFlags(
bool picked,
bool imaged,
bool ignored)
3524 if (picked) m_Flags |= PickedBit;
3525 if (imaged) m_Flags |= ImagedBit;
3526 if (ignored) m_Flags |= IgnoredBit;
3529void ImagingPlanner::saveToDB(
const QString &name,
bool picked,
bool imaged,
3530 bool ignored,
const QString ¬es)
3532 ImagingPlannerDBEntry e(name, 0, notes);
3533 e.setFlags(picked, imaged, ignored);
3537void ImagingPlanner::saveToDB(
const QString &name,
int flags,
const QString ¬es)
3539 ImagingPlannerDBEntry e(name, flags, notes);
3544void ImagingPlanner::loadFromDB()
3549 m_CatalogSortModel->setSourceModel(
nullptr);
3551 auto tz = QTimeZone(getGeo()->TZ() * 3600);
3552 KStarsDateTime midnight = KStarsDateTime(getDate().addDays(1), QTime(0, 1));
3553 KStarsDateTime ut = getGeo()->LTtoUT(KStarsDateTime(midnight));
3554 KSAlmanac ksal(ut, getGeo());
3556 QList<ImagingPlannerDBEntry>
list;
3558 QHash<QString, ImagingPlannerDBEntry> dbData;
3559 QHash<QString, int> dbNotes;
3560 for (
const auto &entry : list)
3562 dbData[entry.m_Name] = entry;
3565 int rows = m_CatalogModel->rowCount();
3566 for (
int i = 0; i < rows; ++i)
3568 const QString &
name = m_CatalogModel->item(i, NAME_COLUMN)->text();
3569 auto entry = dbData.
find(name);
3570 if (entry != dbData.
end())
3572 QVariant f = entry->m_Flags;
3573 m_CatalogModel->item(i, FLAGS_COLUMN)->setData(f, FLAGS_ROLE);
3574 if (entry->m_Flags & IMAGED_BIT)
3575 highlightImagedObject(m_CatalogModel->index(i, NAME_COLUMN),
true);
3576 if (entry->m_Flags & PICKED_BIT)
3577 highlightPickedObject(m_CatalogModel->index(i, NAME_COLUMN),
true);
3578 QVariant n = entry->m_Notes;
3579 m_CatalogModel->item(i, NOTES_COLUMN)->setData(n, NOTES_ROLE);
3583 m_CatalogSortModel->setSourceModel(m_CatalogModel.data());
3586void ImagingPlanner::loadImagedFile()
3588 if (m_loadingCatalog)
3596 QFile inputFile(fileName);
3600 QStringList failedNames;
3601 QTextStream in(&inputFile);
3607 name = tweakNames(name);
3608 if (getObject(name))
3611 auto startIndex = m_CatalogModel->index(0, NAME_COLUMN);
3612 QVariant value(name);
3614 if (matches.size() > 0)
3616 setFlag(matches[0], IMAGED_BIT, m_CatalogModel);
3617 highlightImagedObject(matches[0],
true);
3620 QString
name = m_CatalogModel->
data(matches[0].siblingAtColumn(NAME_COLUMN)).toString();
3621 int flags = m_CatalogModel->data(matches[0].siblingAtColumn(FLAGS_COLUMN), FLAGS_ROLE).toInt();
3622 QString notes = m_CatalogModel->
data(matches[0].siblingAtColumn(NOTES_COLUMN), NOTES_ROLE).toString();
3623 saveToDB(name, flags, notes);
3627 DPRINTF(stderr,
"ooops! internal inconsitency--got an object but match didn't work");
3631 failedNames.
append(name);
3634 if (failedNames.
size() == 0)
3637 KSNotification::info(
i18n(
"Successfully marked %1 objects as read", numSuccess));
3639 KSNotification::sorry(
i18n(
"Empty file"));
3643 int num = std::min((
int)failedNames.
size(), 10);
3644 QString sample = QString(
"\"%1\"").arg(failedNames[0]);
3645 for (
int i = 1; i < num; ++i)
3646 sample.
append(QString(
" \"%1\"").arg(failedNames[i]));
3647 if (numSuccess == 0 && failedNames.
size() <= 10)
3648 KSNotification::sorry(
i18n(
"Failed marking all of these objects imaged: %1", sample));
3649 else if (numSuccess == 0)
3650 KSNotification::sorry(
i18n(
"Failed marking %1 objects imaged, including: %2", failedNames.
size(), sample));
3651 else if (numSuccess > 0 && failedNames.
size() <= 10)
3652 KSNotification::sorry(
i18n(
"Succeeded marking %1 objects imaged. Failed with %2: %3",
3653 numSuccess, failedNames.
size() == 1 ?
"this" :
"these", sample));
3655 KSNotification::sorry(
i18n(
"Succeeded marking %1 objects imaged. Failed with %2 including these: %3",
3656 numSuccess, failedNames.
size(), sample));
3661 KSNotification::sorry(
i18n(
"Sorry, couldn't open file: \"%1\"", fileName));
3665void ImagingPlanner::addCatalogImageInfo(
const CatalogImageInfo &info)
3667 m_CatalogImageInfoMap[info.m_Name.toLower()] = info;
3670bool ImagingPlanner::findCatalogImageInfo(
const QString &name, CatalogImageInfo * info)
3673 if (
result == m_CatalogImageInfoMap.end())
3675 if (
result->m_Filename.isEmpty())
3681void ImagingPlanner::loadCatalogViaMenu()
3683 QString startDir = Options::imagingPlannerCatalogPath();
3685 startDir = defaultDirectory();
3694void ImagingPlanner::loadCatalog(
const QString &path)
3696 removeEventFilters();
3703 m_loadingCatalog =
true;
3704 loadCatalogFromFile(path);
3712 m_loadingCatalog =
false;
3713 installEventFilters();
3716 if (m_CatalogSortModel->rowCount() > 0)
3718 auto name = m_CatalogSortModel->index(0, 0).
data().toString();
3720 QItemSelection selection, deselection;
3721 selection.
select(m_CatalogSortModel->index(0, 0), m_CatalogSortModel->index(0, 0));
3722 selectionChanged(selection, deselection);
3726CatalogImageInfo::CatalogImageInfo(
const QString &csv)
3731 QStringList columns = line.
split(
",");
3732 if (columns.
size() < 1 || columns[0].isEmpty())
3735 m_Name = columns[column++];
3736 if (columns.
size() <= column)
return;
3737 m_Filename = columns[column++];
3738 if (columns.
size() <= column)
return;
3739 m_Author = columns[column++];
3740 if (columns.
size() <= column)
return;
3741 m_Link = columns[column++];
3742 if (columns.
size() <= column)
return;
3743 m_License = columns[column++];
3764void ImagingPlanner::loadCatalogFromFile(
QString path,
bool reset)
3766 QFile inputFile(path);
3770 m_numMissingImage = 0;
3772 int numMissingImage = 0, numWithImage = 0;
3773 if (!inputFile.exists())
3775 emit popupSorry(
i18n(
"Sorry, catalog file doesn't exist: \"%1\"", path));
3778 QStringList objectNames;
3781 const auto tz = QTimeZone(getGeo()->TZ() * 3600);
3782 const KStarsDateTime midnight = KStarsDateTime(getDate().addDays(1), QTime(0, 1));
3783 const KStarsDateTime ut = getGeo()->LTtoUT(KStarsDateTime(midnight));
3784 const KSAlmanac ksal(ut, getGeo());
3788 Options::setImagingPlannerCatalogPath(path);
3789 Options::self()->save();
3790 if (m_CatalogModel->rowCount() > 0)
3791 m_CatalogModel->removeRows(0, m_CatalogModel->rowCount());
3794 QTextStream in(&inputFile);
3797 CatalogImageInfo info(in.readLine().trimmed());
3798 const QString
name = info.m_Name;
3806 const auto match = re.match(name);
3807 if (
match.hasMatch())
3809 const QString catFilename =
match.captured(1);
3810 if (catFilename.
isEmpty())
continue;
3811 const QFileInfo fInfo(catFilename);
3813 QString catFullPath = catFilename;
3814 if (!fInfo.isAbsolute())
3816 const QString catDir = QFileInfo(path).absolutePath();
3817 catFullPath = QString(
"%1%2%3").
arg(catDir)
3820 if (catFullPath != path)
3821 loadCatalogFromFile(catFullPath,
false);
3831 const auto match = re.match(name);
3832 if (
match.hasMatch())
3834 const QString catFilename =
match.captured(1);
3835 if (catFilename.
isEmpty())
continue;
3836 const QFileInfo fInfo(catFilename);
3838 QString catFullPath = catFilename;
3839 if (!fInfo.isAbsolute())
3841 const QString catDir = QFileInfo(path).absolutePath();
3842 catFullPath = QString(
"%1%2%3").
arg(catDir)
3845 std::pair<bool, QString> out = m_manager.import_catalog(catFullPath,
false);
3846 DPRINTF(stderr,
"Load of KStars catalog %s %s%s\n", catFullPath.
toLatin1().
data(),
3847 out.first ?
"succeeded." :
"failed: ", out.second.toLatin1().data());
3856 const auto match = re.match(name);
3857 if (
match.hasMatch())
3859 const QString catIDstr =
match.captured(1);
3860 if (catIDstr.
isEmpty())
continue;
3863 const int catID = catIDstr.
toInt(&ok);
3864 if (ok && m_manager.catalog_exists(catID))
3866 const std::pair<bool, QString> out = m_manager.remove_catalog(catID);
3867 DPRINTF(stderr,
"Removal of out-of-date catalog %d %s%s\n", catID,
3868 out.first ?
"succeeded." :
"failed: ", out.second.toLatin1().data());
3873 objectNames.
append(name);
3874 if (!info.m_Filename.isEmpty())
3877 QFileInfo fInfo(info.m_Filename);
3878 if (fInfo.isRelative())
3879 info.m_Filename = QString(
"%1%2%3").arg(QFileInfo(path).absolutePath())
3881 addCatalogImageInfo(info);
3892 int num = 0, numBad = 0, iteration = 0;
3894 for (
const auto &name : objectNames)
3896 setStatus(
i18n(
"%1/%2: Adding %3", ++iteration, objectNames.size(), name));
3897 if (addCatalogItem(ksal, name, 0)) num++;
3904 m_numWithImage += numWithImage;
3905 m_numMissingImage += numMissingImage;
3906 DPRINTF(stderr,
"Catalog %s: %d of %d have catalog images\n",
3911 emit popupSorry(
i18n(
"Sorry, couldn't open file: \"%1\"", path));
3915void ImagingPlanner::sorry(
const QString &message)
3917 KSNotification::sorry(message);
3920void ImagingPlanner::captureRegion(
const QImage &screenshot)
3922 if (m_PlateSolve.get())
disconnect(m_PlateSolve.get());
3926 m_ScreenShotImage = screenshot;
3929 QString tempQImage = QDir(temporaryPath).filePath(
"screenshot.png");
3930 m_ScreenShotImage.
save(tempQImage);
3931 FITSData::ImageToFITS(tempQImage,
"png", m_ScreenShotFilename);
3935 if (!m_PlateSolve.get())
3936 m_PlateSolve.reset(
new PlateSolve(
this));
3938 m_PlateSolve->setImageDisplay(m_ScreenShotImage);
3939 if (currentCatalogObject())
3941 m_PlateSolve->setPosition(*currentCatalogObject());
3942 m_PlateSolve->setUsePosition(
true);
3943 m_PlateSolve->setUseScale(
false);
3944 m_PlateSolve->setLinear(
false);
3945 reallyCenterOnSkymap();
3948 m_PlateSolve->setWindowTitle(QString(
"Plate Solve for %1").arg(currentObjectName()));
3949 m_PlateSolve->show();
3950 if (Options::imagingPlannerStartSolvingImmediately())
3954void ImagingPlanner::takeScreenshot()
3956 if (!currentCatalogObject())
3959 const QString messageID =
"ImagingPlannerScreenShotInfo";
3960 const QString screenshotInfo =
3961 QString(
"<p><b>Taking a screenshot of %1 for the SkyMap</b></p>"
3962 "<p>This allows you to screenshot/copy a good example image of %1 from another application, "
3963 "such as a browser viewing %1 on Astrobin. It then plate-solves that screenshot and overlays "
3964 "it temporarily on the SkyMap.</p>"
3965 "<p>You can use this to help you frame your future %1 capture. "
3966 "The SkyMap overlay will only be visible in the current KStars session.</p>"
3967 "<p>In order to do this, you should make the image you wish to copy visible "
3968 "on your screen now, before clicking OK. After you click OK you will see the mouse pointer change "
3969 "to the screenshot pointer. You then drag your mouse over the part of the %1 image "
3970 "you wish to copy. If you check do-not-ask-again, then you must make sure that your desired image "
3971 "is already visible before you run this.</p>"
3972 "<p>After you take your screenshot, the system will bring up a menu to help plate-solve the image. "
3973 "Click SOLVE on that menu to start the process, unless it is automatically started. "
3974 "Once successfully plate-solved, your image will be overlayed onto the SkyMap.").arg(currentObjectName());
3975#if KIO_VERSION >= QT_VERSION_CHECK(5, 100, 0)
3979 KGuiItem(
i18nc(
"@action:button",
"OK")),
3980 KGuiItem(
i18nc(
"@action:button",
"Cancel")),
3985 const int result = KMessageBox::questionYesNo(
this, screenshotInfo,
"ScreenShot",
3986 KGuiItem(
"OK"), KGuiItem(
"Cancel"), messageID);
3987 if (
result != KMessageBox::Yes)
3993 m_CaptureWidget.reset();
3999 if (m_CaptureWidget.get())
disconnect(m_CaptureWidget.get());
4000 m_CaptureWidget.reset(
new ScreenCapture());
4003 disconnect(m_CaptureWidget.get(), &ScreenCapture::aborted,
nullptr,
nullptr);
4004 QObject::connect(m_CaptureWidget.get(), &ScreenCapture::aborted,
this, [
this]()
4006 disconnect(m_CaptureWidget.get());
4007 m_CaptureWidget.reset();
4009 this->activateWindow();
4011 m_CaptureWidget->show();
4014void ImagingPlanner::extractImage()
4016 disconnect(m_PlateSolve.get(), &PlateSolve::solverFailed,
nullptr,
nullptr);
4017 connect(m_PlateSolve.get(), &PlateSolve::solverFailed,
this, [
this]()
4019 disconnect(m_PlateSolve.get());
4021 disconnect(m_PlateSolve.get(), &PlateSolve::solverSuccess,
nullptr,
nullptr);
4022 connect(m_PlateSolve.get(), &PlateSolve::solverSuccess,
this, [
this]()
4024 disconnect(m_PlateSolve.get());
4025 const FITSImage::Solution &solution = m_PlateSolve->solution();
4026 ImageOverlay overlay;
4027 overlay.m_Orientation = solution.orientation;
4028 overlay.m_RA = solution.ra;
4029 overlay.m_DEC = solution.dec;
4030 overlay.m_ArcsecPerPixel = solution.pixscale;
4031 overlay.m_EastToTheRight = solution.parity;
4032 overlay.m_Status = ImageOverlay::AVAILABLE;
4034 const bool mirror = !solution.parity;
4035 const int scaleWidth = std::min(m_ScreenShotImage.width(), Options::imageOverlayMaxDimension());
4036 QImage *processedImg = new QImage;
4038 *processedImg = m_ScreenShotImage.mirrored(true, false).scaledToWidth(scaleWidth);
4040 *processedImg = m_ScreenShotImage.scaledToWidth(scaleWidth);
4041 overlay.m_Img.reset(processedImg);
4042 overlay.m_Width = processedImg->width();
4043 overlay.m_Height = processedImg->height();
4044 KStarsData::Instance()->skyComposite()->imageOverlay()->show();
4045 KStarsData::Instance()->skyComposite()->imageOverlay()->addTemporaryImageOverlay(overlay);
4047 KStars::Instance()->activateWindow();
4048 KStars::Instance()->raise();
4049 m_PlateSolve->close();
4051 m_PlateSolve->solveImage(m_ScreenShotFilename);
a dms subclass that caches its sine and cosine values every time the angle is changed.
A simple container object to hold the minimum information for a Deep Sky Object to be drawn on the sk...
CatalogObject & insertStaticObject(const CatalogObject &obj)
Insert an object obj into m_static_objects and return a reference to the newly inserted object.
static QString processSearchText(QString searchText)
Do some post processing on the search text to interpret what the user meant This could include replac...
int size()
Return the numbers of flags.
void remove(int index)
Remove a flag.
void add(const SkyPoint &flagPoint, QString epoch, QString image, QString label, QColor labelColor)
Add a flag.
Contains all relevant information for specifying a location on Earth: City Name, State/Province name,...
A class that implements methods to find sun rise, sun set, twilight begin / end times,...
Provides necessary information about the Moon.
void findPhase(const KSSun *Sun=nullptr)
Determine the phase angle of the moon, and assign the appropriate moon image.
const QImage & image() const
void updateCoords(const KSNumbers *num, bool includePlanets=true, const CachingDms *lat=nullptr, const CachingDms *LST=nullptr, bool forceRecompute=false) override
Update position of the planet (reimplemented from SkyPoint)
bool AddImagingPlannerEntry(const ImagingPlannerDBEntry &entry)
Adds a new Imaging Planner row into the database.
bool GetAllImagingPlannerEntries(QList< ImagingPlannerDBEntry > *entryList)
Gets all the Imaging Planner rows from the database.
const KStarsDateTime & ut() const
Q_INVOKABLE SimClock * clock()
SkyMapComposite * skyComposite()
Extension of QDateTime for KStars KStarsDateTime can represent the date/time as a Julian Day,...
KStarsDateTime addSecs(double s) const
void setDate(const QDate &d)
Assign the Date according to a QDate object.
static KStars * Instance()
KStarsData * data() const
const KStarsDateTime & utc() const
SkyObject * findByName(const QString &name, bool exact=true) override
Search the children of this SkyMapComposite for a SkyObject whose name matches the argument.
void setClickedPoint(const SkyPoint *f)
Set the ClickedPoint to the skypoint given as an argument.
void setClickedObject(SkyObject *o)
Set the ClickedObject pointer to the argument.
void setFocusObject(SkyObject *o)
Set the FocusObject pointer to the argument.
void slotCenter()
Center the display at the point ClickedPoint.
Provides all necessary information about an object in the sky: its coordinates, name(s),...
virtual QString name(void) const
virtual QString longname(void) const
QString name2(void) const
TYPE
The type classification of the SkyObject.
The sky coordinates of a point in the sky.
const CachingDms & dec() const
const CachingDms & ra0() const
virtual void updateCoordsNow(const KSNumbers *num)
updateCoordsNow Shortcut for updateCoords( const KSNumbers *num, false, nullptr, nullptr,...
void setRA(dms &r)
Sets RA, the current Right Ascension.
const CachingDms & ra() const
dms angularDistanceTo(const SkyPoint *sp, double *const positionAngle=nullptr) const
Computes the angular distance between two SkyObjects.
void EquatorialToHorizontal(const CachingDms *LST, const CachingDms *lat)
Determine the (Altitude, Azimuth) coordinates of the SkyPoint from its (RA, Dec) coordinates,...
void setRA0(dms r)
Sets RA0, the catalog Right Ascension.
const CachingDms & dec0() const
void setDec0(dms d)
Sets Dec0, the catalog Declination.
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)
StartupCondition
Conditions under which a SchedulerJob may start.
QMap< QString, uint16_t > CapturedFramesMap
mapping signature --> frames count
CompletionCondition
Conditions under which a SchedulerJob may complete.
KCRASH_EXPORT void setFlags(KCrash::CrashFlags flags)
KCOREADDONS_EXPORT Result match(QStringView pattern, QStringView str)
KIOCORE_EXPORT CopyJob * move(const QList< QUrl > &src, const QUrl &dest, JobFlags flags=DefaultFlags)
KIOCORE_EXPORT QString number(KIO::filesize_t size)
KIOCORE_EXPORT CopyJob * link(const QList< QUrl > &src, const QUrl &destDir, JobFlags flags=DefaultFlags)
GeoCoordinates geo(const QVariant &location)
QString path(const QString &relativePath)
ButtonCode questionTwoActions(QWidget *parent, const QString &text, const QString &title, const KGuiItem &primaryAction, const KGuiItem &secondaryAction, const QString &dontAskAgainName=QString(), Options options=Notify)
void enableMessage(const QString &dontShowAgainName)
KIOCORE_EXPORT QString dir(const QString &fileClass)
KIOCORE_EXPORT QStringList list(const QString &fileClass)
QString name(StandardAction id)
QString label(StandardShortcut id)
const QList< QKeySequence > & end()
void initialize(StandardShortcut id)
std::pair< bool, CatalogObject > resolveName(const QString &name)
Resolve the name of the given DSO and extract data from various sources.
virtual QVariant data(const QModelIndex &index, int role) const const=0
virtual QModelIndex index(int row, int column, const QModelIndex &parent) const const=0
virtual QModelIndex parent(const QModelIndex &index) const const=0
virtual bool setData(const QModelIndex &index, const QVariant &value, int role)
qsizetype length() const const
QByteArray & replace(QByteArrayView before, QByteArrayView after)
void resize(qsizetype newSize, char c)
qsizetype size() const const
QByteArray toBase64(Base64Options options) const const
std::string toStdString() const const
void processEvents(QEventLoop::ProcessEventsFlags flags)
QDate addDays(qint64 ndays) const const
QString toString(QStringView format, QCalendar cal) const const
QDateTime addSecs(qint64 s) const const
bool isValid() const const
qint64 secsTo(const QDateTime &other) const const
void setTimeZone(const QTimeZone &toZone)
void dateChanged(QDate date)
bool openUrl(const QUrl &url)
QString absolutePath() const const
QFileInfoList entryInfoList(Filters filters, SortFlags sort) const const
bool exists() const const
bool mkpath(const QString &dirPath) const const
qint64 elapsed() const const
int exec(ProcessEventsFlags flags)
QString getOpenFileName(QWidget *parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, Options options)
QDateTime birthTime() const const
iterator find(const Key &key)
QIcon fromTheme(const QString &name)
bool save(QIODevice *device, const char *format, int quality) const const
QImage scaled(const QSize &size, Qt::AspectRatioMode aspectRatioMode, Qt::TransformationMode transformMode) const const
QImage scaledToHeight(int height, Qt::TransformationMode mode) const const
QModelIndexList indexes() const const
void select(const QModelIndex &topLeft, const QModelIndex &bottomRight)
void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
void append(QList< T > &&value)
qsizetype indexOf(const AT &value, qsizetype from) const const
void push_back(parameter_type value)
qsizetype size() const const
bool isValid() const const
const QAbstractItemModel * model() const const
QModelIndex siblingAtColumn(int column) const const
int globalX() const const
int globalY() const const
NetworkError error() const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
bool disconnect(const QMetaObject::Connection &connection)
QString tr(const char *sourceText, const char *disambiguation, int n)
QPixmap fromImage(QImage &&image, Qt::ImageConversionFlags flags)
QPixmap scaled(const QSize &size, Qt::AspectRatioMode aspectRatioMode, Qt::TransformationMode transformMode) const const
qsizetype capturedStart(QStringView name) const const
bool hasMatch() const const
virtual QModelIndex index(int row, int column, const QModelIndex &parent) const const override
virtual QVariant data(int role) const const
virtual void setData(const QVariant &value, int role)
void setTextAlignment(Qt::Alignment alignment)
QString & append(QChar ch)
QString arg(Args &&... args) const const
int compare(QLatin1StringView s1, const QString &s2, Qt::CaseSensitivity cs)
bool isEmpty() const const
QString mid(qsizetype position, qsizetype n) const const
QString & replace(QChar before, QChar after, Qt::CaseSensitivity cs)
qsizetype size() const const
QStringList split(QChar sep, Qt::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
bool startsWith(QChar c, Qt::CaseSensitivity cs) const const
int toInt(bool *ok, int base) const const
QByteArray toLatin1() const const
QString toLower() const const
QString toUpper() const const
QByteArray toUtf8() const const
QString trimmed() const const
QTextStream & dec(QTextStream &stream)
QTextStream & endl(QTextStream &stream)
QTextStream & fixed(QTextStream &stream)
QTextStream & left(QTextStream &stream)
QTextStream & right(QTextStream &stream)
void keyEvent(KeyAction action, QWidget *widget, Qt::Key key, Qt::KeyboardModifiers modifier, int delay)
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
bool isValid(int h, int m, int s, int ms)
QString toString(QStringView format) const const
void setInterval(int msec)
bool isActive() const const
bool isEmpty() const const
bool canConvert() const const
int toInt(bool *ok) const const
QString toString() const const