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
89#define TYPE_ROLE (Qt::UserRole + 1)
90#define HOURS_ROLE (Qt::UserRole + 2)
91#define SIZE_ROLE (Qt::UserRole + 3)
92#define ALTITUDE_ROLE (Qt::UserRole + 4)
93#define MOON_ROLE (Qt::UserRole + 5)
94#define FLAGS_ROLE (Qt::UserRole + 6)
95#define NOTES_ROLE (Qt::UserRole + 7)
97#define PICKED_BIT ImagingPlannerDBEntry::PickedBit
98#define IMAGED_BIT ImagingPlannerDBEntry::ImagedBit
99#define IGNORED_BIT ImagingPlannerDBEntry::IgnoredBit
129 case SkyObject::OPEN_CLUSTER:
130 return Options::imagingPlannerAcceptOpenCluster();
131 case SkyObject::GLOBULAR_CLUSTER:
132 return Options::imagingPlannerAcceptGlobularCluster();
133 case SkyObject::GASEOUS_NEBULA:
134 return Options::imagingPlannerAcceptNebula();
135 case SkyObject::PLANETARY_NEBULA:
136 return Options::imagingPlannerAcceptPlanetary();
137 case SkyObject::SUPERNOVA_REMNANT:
138 return Options::imagingPlannerAcceptSupernovaRemnant();
139 case SkyObject::GALAXY:
140 return Options::imagingPlannerAcceptGalaxy();
141 case SkyObject::GALAXY_CLUSTER:
142 return Options::imagingPlannerAcceptGalaxyCluster();
143 case SkyObject::DARK_NEBULA:
144 return Options::imagingPlannerAcceptDarkNebula();
146 return Options::imagingPlannerAcceptOther();
153 const bool hasFlags = model->
data(idx, FLAGS_ROLE).
canConvert<
int>();
156 const bool flag = model->
data(idx, FLAGS_ROLE).
toInt() & bit;
163 const bool hasFlags = model->
data(idx, FLAGS_ROLE).
canConvert<
int>();
164 int currentFlags = 0;
166 currentFlags = model->
data(idx, FLAGS_ROLE).
toInt();
168 model->
setData(idx, val, FLAGS_ROLE);
174 const bool hasFlags = model->
data(idx, FLAGS_ROLE).
canConvert<
int>();
177 const int currentFlags = model->
data(idx, FLAGS_ROLE).
toInt();
179 model->
setData(idx, val, FLAGS_ROLE);
185 if (flags & IMAGED_BIT) str.
append(
i18n(
"Imaged"));
186 if (flags & PICKED_BIT)
192 if (flags & IGNORED_BIT)
202void setupShowCallback(
bool checked,
203 void (*showOption)(
bool),
void (*showNotOption)(
bool),
204 void (*dontCareOption)(
bool),
208 Q_UNUSED(showCheckbox);
212 showNotOption(
false);
213 dontCareOption(
false);
216 Options::self()->save();
221 showNotOption(
false);
222 dontCareOption(
true);
225 Options::self()->save();
229void setupShowNotCallback(
bool checked,
230 void (*showOption)(
bool),
void (*showNotOption)(
bool),
void (*dontCareOption)(
bool),
233 Q_UNUSED(showNotCheckbox);
238 dontCareOption(
false);
241 Options::self()->save();
246 showNotOption(
false);
247 dontCareOption(
true);
250 Options::self()->save();
254void setupDontCareCallback(
bool checked,
255 void (*showOption)(
bool),
void (*showNotOption)(
bool),
void (*dontCareOption)(
bool),
261 showNotOption(
false);
262 dontCareOption(
true);
265 Options::self()->save();
272 showNotOption(
false);
273 dontCareOption(
true);
277 Options::self()->save();
286 "\\+~#=]{1,256}\\.[a-zA-Z0-9()]{1,6}\\b([-a-zA-Z0-9()@:%_\\+.~#?&//=]*)");
291 auto match = re.match(input);
292 if (!
match.hasMatch())
295 return(
match.captured(0));
301 match = re.match(inp);
302 if (!
match.hasMatch())
305 return (
match.captured(0));
331 int column,
int role)
333 const double l =
left.siblingAtColumn(column).data(role).toDouble();
334 const double r =
right.siblingAtColumn(column).data(role).toDouble();
344 const QString l =
left.siblingAtColumn(column).data(role).toString();
345 const QString r =
right.siblingAtColumn(column).data(role).toString();
349 if (lList.
size() == 0 || rList.
size() == 0)
360 if (lList.
size() >= 2 && rList.
size() >= 2)
362 int lInt = lList[1].toInt();
363 int rInt = rList[1].toInt();
366 if (lInt > 0 && rInt > 0)
367 return -(lInt - rInt);
375void SchedulerUtils_setupJob(Ekos::SchedulerJob &job,
const QString &name,
bool isLead,
const QString &group,
376 const QString &train,
const dms &ra,
const dms &dec,
double djd,
double rotation,
const QUrl &sequenceUrl,
378 const QDateTime &completionTime,
int completionRepeats,
double minimumAltitude,
double minimumMoonSeparation,
379 double maxMoonAltitude,
380 bool enforceTwilight,
bool enforceArtificialHorizon,
bool track,
bool focus,
bool align,
bool guide)
384 job.setIsLead(isLead);
385 job.setOpticalTrain(train);
386 job.setPositionAngle(rotation);
392 job.setLeadJob(
nullptr);
394 job.setTargetCoords(ra, dec, djd);
395 job.setFITSFile(fitsUrl);
398 job.setStartupCondition(startup);
399 if (startup == Ekos::START_AT)
401 job.setStartupTime(startupTime);
404 job.setFileStartupCondition(job.getStartupCondition());
405 job.setStartAtTime(job.getStartupTime());
408 job.setMinAltitude(minimumAltitude);
409 job.setMinMoonSeparation(minimumMoonSeparation);
410 job.setMaxMoonAltitude(maxMoonAltitude);
414 job.setEnforceTwilight(enforceTwilight);
415 job.setEnforceArtificialHorizon(enforceArtificialHorizon);
418 job.setStepPipeline(Ekos::SchedulerJob::USE_NONE);
420 job.setStepPipeline(
static_cast<Ekos::SchedulerJob::StepPipeline
>(job.getStepPipeline() | Ekos::SchedulerJob::USE_TRACK));
422 job.setStepPipeline(
static_cast<Ekos::SchedulerJob::StepPipeline
>(job.getStepPipeline() | Ekos::SchedulerJob::USE_FOCUS));
424 job.setStepPipeline(
static_cast<Ekos::SchedulerJob::StepPipeline
>(job.getStepPipeline() | Ekos::SchedulerJob::USE_ALIGN));
426 job.setStepPipeline(
static_cast<Ekos::SchedulerJob::StepPipeline
>(job.getStepPipeline() | Ekos::SchedulerJob::USE_GUIDE));
429 job.setFileStartupCondition(job.getStartupCondition());
430 job.setStartAtTime(job.getStartupTime());
435 job.setSequenceFile(sequenceUrl);
436 job.setCompletionCondition(completion);
437 if (completion == Ekos::FINISH_AT)
438 job.setFinishAtTime(completionTime);
439 else if (completion == Ekos::FINISH_REPEAT)
441 job.setRepeatsRequired(completionRepeats);
442 job.setRepeatsRemaining(completionRepeats);
450void setupJob(Ekos::SchedulerJob &job,
const QString name,
double minAltitude,
double minMoonSeparation,
451 double maxMoonAltitude,
dms ra,
dms dec,
452 bool useArtificialHorizon)
455 double rotation = 0.0;
461 SchedulerUtils_setupJob(job, name,
true,
"",
463 rotation, sequenceURL,
QUrl(),
466 minAltitude, minMoonSeparation, maxMoonAltitude,
467 true, useArtificialHorizon,
468 true,
true,
true,
true);
472void getRunTimes(
const QDate &date,
const GeoLocation &geo,
double minAltitude,
double minMoonSeparation,
473 double maxMoonAltitude,
const dms &ra,
const dms &dec,
bool useArtificialHorizon,
QVector<QDateTime> *jobStartTimes,
476 jobStartTimes->
clear();
477 jobEndTimes->
clear();
478 constexpr int SCHEDULE_RESOLUTION_MINUTES = 10;
479 Ekos::SchedulerJob job;
480 setupJob(job,
"temp", minAltitude, minMoonSeparation, maxMoonAltitude, ra, dec, useArtificialHorizon);
487 startTime.setTimeZone(tz);
488 stopTime.setTimeZone(tz);
492 while (--maxIters >= 0)
494 QDateTime s = job.getNextPossibleStartTime(startTime, SCHEDULE_RESOLUTION_MINUTES,
false, stopTime);
499 QDateTime e = job.getNextEndTime(s, SCHEDULE_RESOLUTION_MINUTES, &constraintReason, stopTime);
507 if (e.
secsTo(stopTime) < 600)
517 double minMoonSeparation,
double maxMoonAltitude,
bool useArtificialHorizon)
520 getRunTimes(date, geo, minAltitude, minMoonSeparation, maxMoonAltitude,
object.ra0(),
object.dec0(), useArtificialHorizon,
523 if (jobStartTimes.
size() == 0 || jobEndTimes.
size() == 0)
527 double totalHours = 0.0;
528 for (
int i = 0; i < jobStartTimes.
size(); ++i)
529 totalHours += jobStartTimes[i].secsTo(jobEndTimes[i]) * 1.0 / 3600.0;
538int packString(
const QString &input, quint8 *p,
bool reallyPack)
541 quint32 len = str_data.
length();
542 const char *str = str_data.
data();
543 constexpr bool compatibilityMode =
false;
544 const quint8 *origP = p;
547 if (reallyPack) *p = 0xa0 | len;
550 else if (len <= std::numeric_limits<quint8>::max() &&
551 compatibilityMode ==
false)
553 if (reallyPack) *p = 0xd9;
555 if (reallyPack) *p = len;
558 else if (len <= std::numeric_limits<quint16>::max())
560 if (reallyPack) *p = 0xda;
571 if (reallyPack) memcpy(p, str, len);
572 return (p - origP) + len;
579 int size = packString(input,
nullptr,
false);
583 packString(input,
reinterpret_cast<quint8*
>(arr.
data()),
true);
601 result.
replace(remainingSpacesRegex, QStringLiteral(
""));
607bool downsampleImageFiles(
const QString &baseDir,
int maxHeight)
618 const QString subDir =
"REDUCED";
619 QDir directory(baseDir);
620 if (!directory.exists())
622 fprintf(stderr,
"downsampleImageFiles: Base directory doesn't exist\n");
630 fprintf(stderr,
"downsampleImageFiles: Failed making the output directory\n");
637 foreach (
QString filename, files)
642 if (img.height() > maxHeight)
651 if (!scaledImg.
save(jpgFilename,
"JPG"))
652 fprintf(stderr,
"downsampleImageFiles: Failed saving \"%s\"\n", writeFilename.
toLatin1().
data());
656 fprintf(stderr,
"downsampleImageFiles: saved \"%s\"\n", writeFilename.
toLatin1().
data());
659 fprintf(stderr,
"downsampleImageFiles: Wrote %d files\n", numSaved);
670 const int len = bInput.
size();
672 for (
int i = 0; i < len; ++i)
682 bInput.
replace(pos, 1, substitute);
691 auto massagedName =
name;
693 massagedName = massagedName.
replace(0, 4,
"sh2-");
694 massagedName = massagedName.
replace(
' ',
"");
700 if (files.size() > 0)
701 return files[0].absoluteFilePath();
704 for (
int i = 0; i < subDirs.size(); i++)
706 QDir subDir(subDirs[i].absoluteFilePath());
708 if (files.size() > 0)
709 return files[0].absoluteFilePath();
716 if (astrobinAbbrev ==
"ACC")
718 else if (astrobinAbbrev ==
"ASACC")
720 else if (astrobinAbbrev ==
"ANCCC")
722 else if (astrobinAbbrev ==
"ANCSACC")
723 return "CC-BY-SA-NC";
727QString creativeCommonsTooltipString(
const QString &astrobinAbbrev)
729 if (astrobinAbbrev ==
"ACC")
730 return "Atribution Creative Commons";
731 else if (astrobinAbbrev ==
"ASACC")
732 return "Atribution Share-Alike Creative Commons";
733 else if (astrobinAbbrev ==
"ANCCC")
734 return "Atribution Non-Commercial Creative Commons";
735 else if (astrobinAbbrev ==
"ANCSACC")
736 return "Atribution Non-Commercial Share-Alike Creative Commons";
755 double hoursAfterDusk = 0,
double hoursBeforeDawn = 0)
761 QDateTime dawn = midnight.
addSecs(24 * 3600 * ksal.getDawnAstronomicalTwilight());
763 QDateTime dusk = midnight.
addSecs(24 * 3600 * ksal.getDuskAstronomicalTwilight());
769 auto end = dawn.
addSecs(-hoursBeforeDawn * 3600);
778 while (t.secsTo(end) > 0)
780 double alt = getAltitude(geo, coords, t);
786 t = t.addSecs(60 * 20);
795 m_SortColumn = HOURS_COLUMN;
799bool CatalogFilter::filterAcceptsRow(
int row,
const QModelIndex &parent)
const
803 if (!acceptType(type))
return false;
807 if (!hasEnoughHours)
return false;
811 const bool isImaged =
sourceModel()->data(flagsIndex, FLAGS_ROLE).toInt() & IMAGED_BIT;
812 const bool passesImagedConstraints = !m_ImagedConstraintsEnabled || (isImaged == m_ImagedRequired);
813 if (!passesImagedConstraints)
return false;
815 const bool isIgnored =
sourceModel()->data(flagsIndex, FLAGS_ROLE).toInt() & IGNORED_BIT;
816 const bool passesIgnoredConstraints = !m_IgnoredConstraintsEnabled || (isIgnored == m_IgnoredRequired);
817 if (!passesIgnoredConstraints)
return false;
819 const bool isPicked =
sourceModel()->data(flagsIndex, FLAGS_ROLE).toInt() & PICKED_BIT;
820 const bool passesPickedConstraints = !m_PickedConstraintsEnabled || (isPicked == m_PickedRequired);
821 if (!passesPickedConstraints)
return false;
824 if (m_Keyword.isEmpty() || !m_KeywordConstraintsEnabled)
return true;
826 const QString notes =
sourceModel()->data(notesIndex, NOTES_ROLE).toString();
828 const bool REMatches = m_KeywordRE.match(notes).hasMatch();
829 return (m_KeywordRequired == REMatches);
832void CatalogFilter::setMinHours(
double hours)
837void CatalogFilter::setImagedConstraints(
bool enabled,
bool required)
839 m_ImagedConstraintsEnabled = enabled;
840 m_ImagedRequired = required;
843void CatalogFilter::setPickedConstraints(
bool enabled,
bool required)
845 m_PickedConstraintsEnabled = enabled;
846 m_PickedRequired = required;
849void CatalogFilter::setIgnoredConstraints(
bool enabled,
bool required)
851 m_IgnoredConstraintsEnabled = enabled;
852 m_IgnoredRequired = required;
855void CatalogFilter::setKeywordConstraints(
bool enabled,
bool required,
const QString &keyword)
857 m_KeywordConstraintsEnabled = enabled;
858 m_KeywordRequired = required;
860 m_KeywordRE = QRegularExpression(keyword);
863void CatalogFilter::setSortColumn(
int column)
868 if (column == m_SortColumn)
869 m_ReverseSort = !m_ReverseSort;
870 m_SortColumn = column;
874 switch (m_SortColumn)
877 case ALTITUDE_COLUMN:
881 arrowChar = m_ReverseSort ? QChar(0x25B2) : QChar(0x25BC);
884 arrowChar = m_ReverseSort ? QChar(0x25BC) : QChar(0x25B2);
888 QString(
"%1%2").arg(arrowChar).arg(COLUMN_HEADERS[m_SortColumn]));
897 double compareVal = 0;
902 compareVal = stringCompareFcn(left, right, NAME_COLUMN,
Qt::DisplayRole);
903 if (m_ReverseSort) compareVal = -compareVal;
907 compareVal = stringCompareFcn(left, right, TYPE_COLUMN,
Qt::DisplayRole);
908 if (m_ReverseSort) compareVal = -compareVal;
909 if (compareVal != 0)
break;
910 compareVal = floatCompareFcn(left, right, HOURS_COLUMN, HOURS_ROLE);
911 if (compareVal != 0)
break;
912 compareVal = stringCompareFcn(left, right, NAME_COLUMN,
Qt::DisplayRole);
916 compareVal = floatCompareFcn(left, right, SIZE_COLUMN, SIZE_ROLE);
917 if (m_ReverseSort) compareVal = -compareVal;
918 if (compareVal != 0)
break;
919 compareVal = floatCompareFcn(left, right, HOURS_COLUMN, HOURS_ROLE);
920 if (compareVal != 0)
break;
921 compareVal = stringCompareFcn(left, right, NAME_COLUMN,
Qt::DisplayRole);
923 case ALTITUDE_COLUMN:
925 compareVal = floatCompareFcn(left, right, ALTITUDE_COLUMN, ALTITUDE_ROLE);
926 if (m_ReverseSort) compareVal = -compareVal;
927 if (compareVal != 0)
break;
928 compareVal = floatCompareFcn(left, right, HOURS_COLUMN, HOURS_ROLE);
929 if (compareVal != 0)
break;
930 compareVal = stringCompareFcn(left, right, NAME_COLUMN,
Qt::DisplayRole);
934 compareVal = floatCompareFcn(left, right, MOON_COLUMN, MOON_ROLE);
935 if (m_ReverseSort) compareVal = -compareVal;
936 if (compareVal != 0)
break;
937 compareVal = floatCompareFcn(left, right, HOURS_COLUMN, HOURS_ROLE);
938 if (compareVal != 0)
break;
939 compareVal = stringCompareFcn(left, right, NAME_COLUMN,
Qt::DisplayRole);
941 case CONSTELLATION_COLUMN:
943 compareVal = stringCompareFcn(left, right, CONSTELLATION_COLUMN,
Qt::DisplayRole);
944 if (m_ReverseSort) compareVal = -compareVal;
945 if (compareVal != 0)
break;
946 compareVal = floatCompareFcn(left, right, HOURS_COLUMN, HOURS_ROLE);
947 if (compareVal != 0)
break;
948 compareVal = stringCompareFcn(left, right, NAME_COLUMN,
Qt::DisplayRole);
952 compareVal = stringCompareFcn(left, right, COORD_COLUMN,
Qt::DisplayRole);
953 if (m_ReverseSort) compareVal = -compareVal;
954 if (compareVal != 0)
break;
955 compareVal = floatCompareFcn(left, right, HOURS_COLUMN, HOURS_ROLE);
956 if (compareVal != 0)
break;
957 compareVal = stringCompareFcn(left, right, NAME_COLUMN,
Qt::DisplayRole);
962 compareVal = floatCompareFcn(left, right, HOURS_COLUMN, HOURS_ROLE);
963 if (m_ReverseSort) compareVal = -compareVal;
964 if (compareVal != 0)
break;
965 compareVal = stringCompareFcn(left, right, NAME_COLUMN,
Qt::DisplayRole);
968 return compareVal < 0;
978void ImagingPlannerUI::setupIcons()
1002 return KStarsData::Instance()->
geo();
1005QDate ImagingPlanner::getDate()
const
1007 return ui->DateEdit->date();
1010ImagingPlanner::ImagingPlanner() :
QDialog(nullptr), m_networkManager(this), m_manager{ CatalogsDB::dso_db_path() }
1012 ui =
new ImagingPlannerUI(
this);
1018 setLayout(mainLayout);
1020 setWindowTitle(
i18nc(
"@title:window",
"Imaging Planner"));
1023 if (Options::imagingPlannerIndependentWindow())
1039void ImagingPlanner::setupHideButtons(
bool(*option)(),
void(*setOption)(
bool),
1049 Options::self()->save();
1058 Options::self()->save();
1066void ImagingPlanner::focusOnTable()
1068 ui->CatalogView->setFocus();
1071void ImagingPlanner::adjustWindowSize()
1073 const int keepWidth =
width();
1075 const int newHeight =
height();
1076 resize(keepWidth, newHeight);
1080void ImagingPlanner::setupFilterButton(
QCheckBox * checkbox,
bool(*option)(),
void(*setOption)(
bool))
1086 Options::self()->save();
1087 m_CatalogSortModel->invalidate();
1089 ui->CatalogView->resizeColumnsToContents();
1095void ImagingPlanner::setupFilter2Buttons(
1097 bool(*yesOption)(),
bool(*noOption)(),
bool(*dontCareOption)(),
1098 void(*setYesOption)(
bool),
void(*setNoOption)(
bool),
void(*setDontCareOption)(
bool))
1104 setupShowCallback(checked, setYesOption, setNoOption, setDontCareOption, yes, no, dontCare);
1105 updateSortConstraints();
1106 m_CatalogSortModel->invalidate();
1107 ui->CatalogView->resizeColumnsToContents();
1113 setupShowNotCallback(checked, setYesOption, setNoOption, setDontCareOption, yes, no, dontCare);
1114 updateSortConstraints();
1115 m_CatalogSortModel->invalidate();
1116 ui->CatalogView->resizeColumnsToContents();
1120 connect(dontCare, &
QCheckBox::clicked, [
this, setYesOption, setNoOption, setDontCareOption, yes, no, dontCare](
bool checked)
1122 setupDontCareCallback(checked, setYesOption, setNoOption, setDontCareOption, yes, no, dontCare);
1123 updateSortConstraints();
1124 m_CatalogSortModel->invalidate();
1125 ui->CatalogView->resizeColumnsToContents();
1136void ImagingPlanner::updateSortConstraints()
1138 m_CatalogSortModel->setPickedConstraints(!ui->dontCarePickedCB->isChecked(),
1139 ui->pickedCB->isChecked());
1140 m_CatalogSortModel->setImagedConstraints(!ui->dontCareImagedCB->isChecked(),
1141 ui->imagedCB->isChecked());
1142 m_CatalogSortModel->setIgnoredConstraints(!ui->dontCareIgnoredCB->isChecked(),
1143 ui->ignoredCB->isChecked());
1144 m_CatalogSortModel->setKeywordConstraints(!ui->dontCareKeywordCB->isChecked(),
1145 ui->keywordCB->isChecked(), ui->keywordEdit->toPlainText().trimmed());
1149void ImagingPlanner::initialize()
1151 if (KStarsData::Instance() ==
nullptr)
1158 connect(
this, &ImagingPlanner::popupSorry,
this, &ImagingPlanner::sorry);
1161 m_CatalogModel =
new QStandardItemModel(0, LAST_COLUMN);
1164 m_CatalogModel->setHorizontalHeaderLabels(COLUMN_HEADERS);
1165 m_CatalogModel->horizontalHeaderItem(NAME_COLUMN)->setToolTip(
1166 i18n(
"Object Name--click header to sort ascending/descending."));
1167 m_CatalogModel->horizontalHeaderItem(
1168 HOURS_COLUMN)->setToolTip(
i18n(
"Number of hours the object can be imaged--click header to sort ascending/descending."));
1169 m_CatalogModel->horizontalHeaderItem(TYPE_COLUMN)->setToolTip(
1170 i18n(
"Object Type--click header to sort ascending/descending."));
1171 m_CatalogModel->horizontalHeaderItem(
1172 SIZE_COLUMN)->setToolTip(
i18n(
"Maximum object dimension (arcmin)--click header to sort ascending/descending."));
1173 m_CatalogModel->horizontalHeaderItem(
1174 ALTITUDE_COLUMN)->setToolTip(
i18n(
"Maximum altitude--click header to sort ascending/descending."));
1175 m_CatalogModel->horizontalHeaderItem(
1176 MOON_COLUMN)->setToolTip(
i18n(
"Moon angular separation at midnight--click header to sort ascending/descending."));
1177 m_CatalogModel->horizontalHeaderItem(
1178 CONSTELLATION_COLUMN)->setToolTip(
i18n(
"Constellation--click header to sort ascending/descending."));
1179 m_CatalogModel->horizontalHeaderItem(
1180 COORD_COLUMN)->setToolTip(
i18n(
"RA/DEC coordinates--click header to sort ascending/descending."));
1182 m_CatalogSortModel =
new CatalogFilter(
this);
1184 m_CatalogSortModel->setSourceModel(m_CatalogModel.data());
1185 m_CatalogSortModel->setDynamicSortFilter(
true);
1187 ui->CatalogView->setModel(m_CatalogSortModel.data());
1188 ui->CatalogView->setSortingEnabled(
false);
1189 ui->CatalogView->horizontalHeader()->setStretchLastSection(
false);
1190 ui->CatalogView->resizeColumnsToContents();
1191 ui->CatalogView->verticalHeader()->setVisible(
false);
1192 ui->CatalogView->setColumnHidden(FLAGS_COLUMN,
true);
1195 m_CatalogSortModel->setSortColumn(HOURS_COLUMN);
1196 m_CatalogSortModel->setSortColumn(HOURS_COLUMN);
1199 this, &ImagingPlanner::selectionChanged);
1204 auto utc = KStarsData::Instance()->
clock()->
utc();
1205 auto localTime = getGeo()->UTtoLT(utc);
1206 ui->DateEdit->
setDate(localTime.date());
1212 setupHideButtons(&Options::imagingPlannerHideAltitudeGraph, &Options::setImagingPlannerHideAltitudeGraph,
1213 ui->hideAltitudeGraphB, ui->showAltitudeGraphB,
1214 ui->AltitudeGraphFrame, ui->HiddenAltitudeGraphFrame);
1221 QString selection = currentObjectName();
1225 scrollToName(selection);
1249 checkTargets2(true);
1253 QString dir = QFileDialog::getExistingDirectory(this, tr(
"Downsample Directory"));
1256 if (downsampleImageFiles(dir, 300))
1257 fprintf(stderr,
"downsampling succeeded\n");
1259 fprintf(stderr,
"downsampling failed\n");
1264 Options::setImagingPlannerHideAstrobinDetails(
true);
1265 setupHideButtons(&Options::imagingPlannerHideAstrobinDetails, &Options::setImagingPlannerHideAstrobinDetails,
1266 ui->hideAstrobinDetailsButton, ui->showAstrobinDetailsButton,
1267 ui->AstrobinSearchFrame, ui->HiddenAstrobinSearchFrame);
1268 ui->AstrobinAward->setChecked(Options::astrobinAward());
1271 Options::setAstrobinAward(checked);
1272 Options::self()->save();
1275 ui->AstrobinMinRadius->setValue(Options::astrobinMinRadius());
1278 Options::setAstrobinMinRadius(ui->AstrobinMinRadius->value());
1279 Options::self()->save();
1282 ui->AstrobinMaxRadius->setValue(Options::astrobinMaxRadius());
1285 Options::setAstrobinMaxRadius(ui->AstrobinMaxRadius->value());
1286 Options::self()->save();
1297 setupHideButtons(&Options::imagingPlannerHideImage, &Options::setImagingPlannerHideImage,
1298 ui->hideImageButton, ui->showImageButton,
1299 ui->ImageFrame, ui->HiddenImageFrame);
1302 Options::setImagingPlannerHideFilters(
true);
1303 setupHideButtons(&Options::imagingPlannerHideFilters, &Options::setImagingPlannerHideFilters,
1304 ui->hideFilterTypesButton, ui->showFilterTypesButton,
1305 ui->FilterTypesFrame, ui->HiddenFilterTypesFrame);
1306 setupFilterButton(ui->OpenClusterCB, &Options::imagingPlannerAcceptOpenCluster,
1307 &Options::setImagingPlannerAcceptOpenCluster);
1308 setupFilterButton(ui->NebulaCB, &Options::imagingPlannerAcceptNebula, &Options::setImagingPlannerAcceptNebula);
1309 setupFilterButton(ui->GlobularClusterCB, &Options::imagingPlannerAcceptGlobularCluster,
1310 &Options::setImagingPlannerAcceptGlobularCluster);
1311 setupFilterButton(ui->PlanetaryCB, &Options::imagingPlannerAcceptPlanetary, &Options::setImagingPlannerAcceptPlanetary);
1312 setupFilterButton(ui->SupernovaRemnantCB, &Options::imagingPlannerAcceptSupernovaRemnant,
1313 &Options::setImagingPlannerAcceptSupernovaRemnant);
1314 setupFilterButton(ui->GalaxyCB, &Options::imagingPlannerAcceptGalaxy, &Options::setImagingPlannerAcceptGalaxy);
1315 setupFilterButton(ui->GalaxyClusterCB, &Options::imagingPlannerAcceptGalaxyCluster,
1316 &Options::setImagingPlannerAcceptGalaxyCluster);
1317 setupFilterButton(ui->DarkNebulaCB, &Options::imagingPlannerAcceptDarkNebula, &Options::setImagingPlannerAcceptDarkNebula);
1318 setupFilterButton(ui->OtherCB, &Options::imagingPlannerAcceptOther, &Options::setImagingPlannerAcceptOther);
1320 setupFilter2Buttons(ui->pickedCB, ui->notPickedCB, ui->dontCarePickedCB,
1321 &Options::imagingPlannerShowPicked, &Options::imagingPlannerShowNotPicked, &Options::imagingPlannerDontCarePicked,
1322 &Options::setImagingPlannerShowPicked, &Options::setImagingPlannerShowNotPicked, &Options::setImagingPlannerDontCarePicked);
1324 setupFilter2Buttons(ui->imagedCB, ui->notImagedCB, ui->dontCareImagedCB,
1325 &Options::imagingPlannerShowImaged, &Options::imagingPlannerShowNotImaged, &Options::imagingPlannerDontCareImaged,
1326 &Options::setImagingPlannerShowImaged, &Options::setImagingPlannerShowNotImaged, &Options::setImagingPlannerDontCareImaged);
1328 setupFilter2Buttons(ui->ignoredCB, ui->notIgnoredCB, ui->dontCareIgnoredCB,
1329 &Options::imagingPlannerShowIgnored, &Options::imagingPlannerShowNotIgnored, &Options::imagingPlannerDontCareIgnored,
1330 &Options::setImagingPlannerShowIgnored, &Options::setImagingPlannerShowNotIgnored,
1331 &Options::setImagingPlannerDontCareIgnored);
1333 ui->keywordEdit->setText(Options::imagingPlannerKeyword());
1334 ui->keywordEdit->setAcceptRichText(
false);
1335 m_Keyword = Options::imagingPlannerKeyword();
1336 setupFilter2Buttons(ui->keywordCB, ui->notKeywordCB, ui->dontCareKeywordCB,
1337 &Options::imagingPlannerShowKeyword, &Options::imagingPlannerShowNotKeyword, &Options::imagingPlannerDontCareKeyword,
1338 &Options::setImagingPlannerShowKeyword, &Options::setImagingPlannerShowNotKeyword,
1339 &Options::setImagingPlannerDontCareKeyword);
1344 ui->useArtificialHorizon->setChecked(Options::imagingPlannerUseArtificialHorizon());
1345 m_UseArtificialHorizon = Options::imagingPlannerUseArtificialHorizon();
1346 ui->minMoon->setValue(Options::imagingPlannerMinMoonSeparation());
1347 m_MinMoon = Options::imagingPlannerMinMoonSeparation();
1348 ui->maxMoonAltitude->setValue(Options::imagingPlannerMaxMoonAltitude());
1349 m_MaxMoonAltitude = Options::imagingPlannerMaxMoonAltitude();
1350 ui->minAltitude->setValue(Options::imagingPlannerMinAltitude());
1351 m_MinAltitude = Options::imagingPlannerMinAltitude();
1352 ui->minHours->setValue(Options::imagingPlannerMinHours());
1353 m_MinHours = Options::imagingPlannerMinHours();
1354 m_CatalogSortModel->setMinHours(Options::imagingPlannerMinHours());
1357 if (m_UseArtificialHorizon == ui->useArtificialHorizon->isChecked())
1359 m_UseArtificialHorizon = ui->useArtificialHorizon->isChecked();
1360 Options::setImagingPlannerUseArtificialHorizon(ui->useArtificialHorizon->isChecked());
1361 Options::self()->save();
1367 if (m_MinMoon == ui->minMoon->value())
1369 m_MinMoon = ui->minMoon->value();
1370 Options::setImagingPlannerMinMoonSeparation(ui->minMoon->value());
1371 Options::self()->save();
1377 if (m_MaxMoonAltitude == ui->maxMoonAltitude->value())
1379 m_MaxMoonAltitude = ui->maxMoonAltitude->value();
1380 Options::setImagingPlannerMaxMoonAltitude(ui->maxMoonAltitude->value());
1381 Options::self()->save();
1387 if (m_MinAltitude == ui->minAltitude->value())
1389 m_MinAltitude = ui->minAltitude->value();
1390 Options::setImagingPlannerMinAltitude(ui->minAltitude->value());
1391 Options::self()->save();
1397 if (m_MinHours == ui->minHours->value())
1399 m_MinHours = ui->minHours->value();
1400 Options::setImagingPlannerMinHours(ui->minHours->value());
1401 Options::self()->save();
1402 m_CatalogSortModel->setMinHours(Options::imagingPlannerMinHours());
1403 m_CatalogSortModel->invalidate();
1404 ui->CatalogView->resizeColumnsToContents();
1408 updateSortConstraints();
1410 m_CatalogSortModel->setMinHours(ui->minHours->value());
1412 ui->CatalogView->setColumnHidden(NOTES_COLUMN,
true);
1421 ui->userNotesLabel->setVisible(true);
1422 ui->userNotesEdit->setText(ui->userNotes->text());
1423 ui->userNotesEdit->setVisible(true);
1424 ui->userNotesEditButton->setVisible(false);
1425 ui->userNotesDoneButton->setVisible(true);
1426 ui->userNotes->setVisible(false);
1427 ui->userNotesLabel->setVisible(true);
1428 ui->userNotesOpenLink->setVisible(false);
1429 ui->userNotesOpenLink2->setVisible(false);
1430 ui->userNotesOpenLink3->setVisible(false);
1436 QString urlString = findUrl(ui->userNotes->text());
1437 if (urlString.isEmpty())
1439 QDesktopServices::openUrl(QUrl(urlString));
1444 QString urlString = findUrl(ui->userNotes->text(), 2);
1445 if (urlString.isEmpty())
1447 QDesktopServices::openUrl(QUrl(urlString));
1452 QString urlString = findUrl(ui->userNotes->text(), 3);
1453 if (urlString.isEmpty())
1455 QDesktopServices::openUrl(QUrl(urlString));
1464 m_CatalogSortModel->setSortColumn(column);
1465 m_CatalogSortModel->invalidate();
1466 ui->CatalogView->resizeColumnsToContents();
1474 qRegisterMetaType<QList<QStandardItem * >> (
"QList<QStandardItem *>");
1475 connect(
this, &ImagingPlanner::addRow,
this, &ImagingPlanner::addRowSlot);
1485 installEventFilters();
1487 m_PlateSolve.reset(
new PlateSolve(
this));
1488 m_PlateSolve->enableAuxButton(
"Retake screenshot",
1489 "Retake the screenshot of the object if you're having issues solving.");
1491 connect(m_PlateSolve.get(), &PlateSolve::auxClicked,
this, [
this]()
1493 m_PlateSolve->abort();
1498void ImagingPlanner::installEventFilters()
1502 ui->SearchText->installEventFilter(
this);
1503 ui->userNotesEdit->installEventFilter(
this);
1504 ui->keywordEdit->installEventFilter(
this);
1505 ui->ImagePreviewCreditLink->installEventFilter(
this);
1506 ui->ImagePreviewCredit->installEventFilter(
this);
1507 ui->ImagePreview->installEventFilter(
this);
1508 ui->CatalogView->viewport()->installEventFilter(
this);
1509 ui->CatalogView->installEventFilter(
this);
1510 ui->helpButton->installEventFilter(
this);
1513void ImagingPlanner::removeEventFilters()
1515 ui->SearchText->removeEventFilter(
this);
1516 ui->userNotesEdit->removeEventFilter(
this);
1517 ui->keywordEdit->removeEventFilter(
this);
1518 ui->ImagePreviewCreditLink->removeEventFilter(
this);
1519 ui->ImagePreviewCredit->removeEventFilter(
this);
1520 ui->ImagePreview->removeEventFilter(
this);
1521 ui->CatalogView->viewport()->removeEventFilter(
this);
1522 ui->helpButton->removeEventFilter(
this);
1525void ImagingPlanner::openOptionsMenu()
1527 QSharedPointer<ImagingPlannerOptions> options(
new ImagingPlannerOptions(
this));
1533void ImagingPlanner::getHelp()
1536 const QUrl url(
"https://kstars-docs.kde.org/en/user_manual/tool-imaging-planner.html");
1541KSMoon *ImagingPlanner::getMoon()
1543 if (KStarsData::Instance() ==
nullptr)
1549 auto tz = QTimeZone(getGeo()->TZ() * 3600);
1550 KStarsDateTime midnight = KStarsDateTime(getDate().addDays(1), QTime(0, 1));
1552 CachingDms LST = getGeo()->GSTtoLST(getGeo()->LTtoUT(midnight).gst());
1553 KSNumbers numbers(midnight.
djd());
1554 moon->
updateCoords(&numbers,
true, getGeo()->lat(), &LST,
true);
1560void ImagingPlanner::updateMoon()
1562 KSMoon *moon = getMoon();
1567 auto tz = QTimeZone(getGeo()->TZ() * 3600);
1568 KStarsDateTime midnight = KStarsDateTime(getDate().addDays(1), QTime(0, 1));
1569 CachingDms LST = getGeo()->GSTtoLST(getGeo()->LTtoUT(midnight).gst());
1570 KSNumbers numbers(midnight.
djd());
1572 sun->
updateCoords(&numbers,
true, getGeo()->lat(), &LST,
true);
1576 ui->moonPercentLabel->setText(QString(
"%1%").arg(moon->
illum() * 100.0 + 0.5, 0,
'f', 0));
1579bool ImagingPlanner::scrollToName(
const QString &name)
1583 QModelIndexList matchList = ui->CatalogView->model()->match(ui->CatalogView->model()->index(0, 0),
Qt::EditRole,
1585 if(matchList.count() >= 1)
1588 for (
int i = 0; i < matchList.count(); i++)
1590 QString nn = ui->CatalogView->model()->data(matchList[i],
Qt::DisplayRole).toString();
1597 ui->CatalogView->scrollTo(matchList[bestIndex]);
1598 ui->CatalogView->setCurrentIndex(matchList[bestIndex]);
1604void ImagingPlanner::searchSlot()
1606 if (m_loadingCatalog)
1608 QString origName = ui->SearchText->toPlainText().trimmed();
1609 QString
name = tweakNames(origName);
1610 ui->SearchText->setPlainText(name);
1614 if (!scrollToName(name))
1615 KSNotification::sorry(
i18n(
"No match for \"%1\"", origName));
1618 ui->SearchText->clear();
1619 ui->SearchText->setPlainText(
"");
1622void ImagingPlanner::initUserNotes()
1624 ui->userNotesLabel->setVisible(
true);
1625 ui->userNotesEdit->setVisible(
false);
1626 ui->userNotesEditButton->setVisible(
true);
1627 ui->userNotesDoneButton->setVisible(
false);
1628 ui->userNotes->setVisible(
true);
1629 ui->userNotesLabel->setVisible(
true);
1630 ui->userNotesOpenLink->setVisible(
false);
1631 ui->userNotesOpenLink2->setVisible(
false);
1632 ui->userNotesOpenLink3->setVisible(
false);
1635void ImagingPlanner::disableUserNotes()
1637 ui->userNotesEdit->setVisible(
false);
1638 ui->userNotesEditButton->setVisible(
false);
1639 ui->userNotesDoneButton->setVisible(
false);
1640 ui->userNotes->setVisible(
false);
1641 ui->userNotesLabel->setVisible(
false);
1642 ui->userNotesOpenLink->setVisible(
false);
1643 ui->userNotesOpenLink2->setVisible(
false);
1644 ui->userNotesOpenLink3->setVisible(
false);
1647void ImagingPlanner::userNotesEditFinished()
1649 const QString ¬es = ui->userNotesEdit->toPlainText().
trimmed();
1650 ui->userNotes->setText(notes);
1651 ui->userNotesLabel->setVisible(notes.
isEmpty());
1652 ui->userNotesEdit->setVisible(
false);
1653 ui->userNotesEditButton->setVisible(
true);
1654 ui->userNotesDoneButton->setVisible(
false);
1655 ui->userNotes->setVisible(
true);
1656 ui->userNotesLabel->setVisible(
true);
1657 setCurrentObjectNotes(notes);
1658 setupNotesLinks(notes);
1660 auto o = currentCatalogObject();
1662 saveToDB(currentObjectName(), currentObjectFlags(), notes);
1665void ImagingPlanner::updateNotes(
const QString ¬es)
1667 ui->userNotes->setMaximumWidth(ui->RightPanel->width() - 125);
1669 ui->userNotes->setText(notes);
1670 ui->userNotesLabel->setVisible(notes.
isEmpty());
1671 setupNotesLinks(notes);
1674void ImagingPlanner::setupNotesLinks(
const QString ¬es)
1676 QString
link = findUrl(notes);
1677 ui->userNotesOpenLink->setVisible(!
link.isEmpty());
1678 if (!
link.isEmpty())
1679 ui->userNotesOpenLink->setToolTip(
i18n(
"Open a browser with the 1st link in this note: %1", link));
1681 link = findUrl(notes, 2);
1682 ui->userNotesOpenLink2->setVisible(!
link.isEmpty());
1683 if (!
link.isEmpty())
1684 ui->userNotesOpenLink2->setToolTip(
i18n(
"Open a browser with the 2nd link in this note: %1", link));
1686 link = findUrl(notes, 3);
1687 ui->userNotesOpenLink3->setVisible(!
link.isEmpty());
1688 if (!
link.isEmpty())
1689 ui->userNotesOpenLink3->setToolTip(
i18n(
"Open a browser with the 3rd link in this note: %1", link));
1692bool ImagingPlanner::internetNameSearch(
const QString &name,
bool abellPlanetary,
int abellNumber,
1696 QElapsedTimer timer;
1698 QString filteredName =
name;
1702 QString resolverName = filteredName;
1706 resolverName = QString(
"PN A66 %1").
arg(abellNumber);
1713 CatalogObject
object = cedata.second;
1716 if (
object.
name() ==
object.name2())
1717 object.setName2(filteredName);
1718 object.setName(filteredName);
1721 m_manager.add_object(CatalogsDB::user_catalog_id,
object);
1722 const auto &added_object =
1723 m_manager.get_object(
object.getId(), CatalogsDB::user_catalog_id);
1725 if (added_object.first)
1727 *catObject = KStarsData::Instance()
1729 ->catalogsComponent()
1733 DPRINTF(stderr,
"***** Found %s using name resolver (%.1fs)\n",
name.
toLatin1().
data(),
1738bool isAbellPlanetary(
const QString &name,
int *number)
1744 auto match = abellRE.match(name);
1745 if (
match.hasMatch())
1763 std::list<CatalogObject> objs =
1764 m_manager.find_objects_by_name(filteredName, 1,
true);
1768 int abellNumber = 0;
1769 bool abellPlanetary = isAbellPlanetary(name, &abellNumber);
1770 if (objs.size() > 0 && abellPlanetary && objs.front().type() == SkyObject::GALAXY_CLUSTER)
1773 if (objs.size() == 0 && filteredName.
size() > 0)
1776 const QString capitalized = capitalize(filteredName);
1777 objs = m_manager.find_objects_by_name(capitalized, 1,
true);
1778 if (objs.size() > 0 && abellPlanetary && objs.front().type() == SkyObject::GALAXY_CLUSTER)
1781 if (objs.size() == 0)
1784 const QString lowerCase = filteredName.
toLower();
1785 objs = m_manager.find_objects_by_name(lowerCase, 1,
true);
1786 if (objs.size() > 0 && abellPlanetary && objs.front().type() == SkyObject::GALAXY_CLUSTER)
1795 QString name2 = filteredName;
1797 objs = m_manager.find_objects_by_name(name2, 1,
true);
1801 QString name2 = filteredName;
1803 objs = m_manager.find_objects_by_name(name2, 1,
true);
1806 if (objs.size() == 0 && !abellPlanetary)
1807 objs = m_manager.find_objects_by_name(filteredName.
toLower(), 20,
false);
1808 if (objs.size() == 0)
1809 return internetNameSearch(filteredName, abellPlanetary, abellNumber, catObject);
1811 if (objs.size() == 0)
1815 *catObject = objs.front();
1816 if (objs.size() >= 1)
1818 bool foundIt =
false;
1819 QString addSpace = filteredName;
1821 QString addComma = filteredName;
1823 QString sh2Fix = filteredName;
1825 for (
const auto &obj : objs)
1843 if (objs.size() == 1)
1844 DPRINTF(stderr,
" ========> \"%s\" had 1 match \"%s\", but not trusting it!!!!\n",
name.
toLatin1().
data(),
1845 objs.front().name().toLatin1().data());
1847 if (internetNameSearch(filteredName, abellPlanetary, abellNumber, catObject))
1850 DPRINTF(stderr,
"Didn't find %s (%s) -- Not using name \"%s\" name2 \"%s\" longname \"%s\"\n",
1865 auto o = m_CatalogHash.find(lName);
1866 if (o == m_CatalogHash.end())
1871void ImagingPlanner::clearObjects()
1877 m_CatalogHash.clear();
1885 if (getObject(lName) !=
nullptr)
1887 DPRINTF(stderr,
"Didn't add \"%s\" because it's already there\n",
name.
toLatin1().
data());
1892 if (!getKStarsCatalogObject(lName, &o))
1894 DPRINTF(stderr,
"************* Couldn't find \"%s\"\n", lName.
toLatin1().
data());
1897 m_CatalogHash[lName] = o;
1898 return &(m_CatalogHash[lName]);
1903bool ImagingPlanner::addCatalogItem(
const KSAlmanac &ksal,
const QString &name,
int flags)
1905 CatalogObject *
object = addObject(name);
1906 if (
object ==
nullptr)
1909 auto getItemWithUserRole = [](
const QString & itemText) -> QStandardItem *
1911 QStandardItem *ret =
new QStandardItem(itemText);
1918 QList<QStandardItem *> itemList;
1919 for (
int i = 0; i < LAST_COLUMN; ++i)
1921 if (i == NAME_COLUMN)
1923 itemList.
append(getItemWithUserRole(name));
1925 else if (i == HOURS_COLUMN)
1927 double runHours = getRunHours(*
object, getDate(), *getGeo(), ui->minAltitude->value(), ui->minMoon->value(),
1928 ui->maxMoonAltitude->value(), ui->useArtificialHorizon->isChecked());
1929 auto hoursItem = getItemWithUserRole(QString(
"%1").arg(runHours, 0,
'f', 1));
1930 hoursItem->setData(runHours, HOURS_ROLE);
1931 itemList.
append(hoursItem);
1933 else if (i == TYPE_COLUMN)
1935 auto typeItem = getItemWithUserRole(QString(
"%1").arg(SkyObject::typeShortName(object->
type())));
1936 typeItem->setData(object->
type(), TYPE_ROLE);
1937 itemList.
append(typeItem);
1939 else if (i == SIZE_COLUMN)
1941 double size = std::max(object->
a(), object->
b());
1942 auto sizeItem = getItemWithUserRole(QString(
"%1'").arg(
size, 0,
'f', 1));
1943 sizeItem->setData(
size, SIZE_ROLE);
1944 itemList.
append(sizeItem);
1946 else if (i == ALTITUDE_COLUMN)
1948 const auto time = KStarsDateTime(QDateTime(getDate(), QTime(12, 0)));
1949 const double altitude = getMaxAltitude(ksal, getDate(), getGeo(), *
object, 0, 0);
1950 auto altItem = getItemWithUserRole(QString(
"%1º").arg(altitude, 0,
'f', 0));
1951 altItem->setData(altitude, ALTITUDE_ROLE);
1952 itemList.
append(altItem);
1954 else if (i == MOON_COLUMN)
1956 KSMoon *moon = getMoon();
1962 auto tz = QTimeZone(getGeo()->TZ() * 3600);
1963 KStarsDateTime midnight = KStarsDateTime(getDate().addDays(1), QTime(0, 1));
1965 KSNumbers numbers(midnight.
djd());
1969 auto moonItem = getItemWithUserRole(QString(
"%1º").arg(separation, 0,
'f', 0));
1970 moonItem->setData(separation, MOON_ROLE);
1971 itemList.
append(moonItem);
1975 auto moonItem = getItemWithUserRole(QString(
""));
1976 moonItem->setData(-1, MOON_ROLE);
1979 else if (i == CONSTELLATION_COLUMN)
1981 QString cname = KStarsData::Instance()
1983 ->constellationBoundary()
1984 ->constellationName(
object);
1986 auto constellationItem = getItemWithUserRole(cname);
1987 itemList.
append(constellationItem);
1989 else if (i == COORD_COLUMN)
1991 itemList.
append(getItemWithUserRole(shortCoordString(object->
ra0(), object->
dec0())));
1993 else if (i == FLAGS_COLUMN)
1995 QStandardItem *flag = getItemWithUserRole(
"flag");
1996 flag->
setData(flags, FLAGS_ROLE);
1999 else if (i == NOTES_COLUMN)
2001 QStandardItem *notes = getItemWithUserRole(
"notes");
2002 notes->
setData(QString(), NOTES_ROLE);
2007 DPRINTF(stderr,
"Bug in addCatalogItem() !\n");
2012 emit addRow(itemList);
2018 m_CatalogModel->appendRow(itemList);
2022void ImagingPlanner::recompute()
2024 setStatus(
i18n(
"Updating tables..."));
2027 m_CatalogSortModel->setSourceModel(
nullptr);
2029 QElapsedTimer timer;
2032 auto tz = QTimeZone(getGeo()->TZ() * 3600);
2033 KStarsDateTime midnight = KStarsDateTime(getDate().addDays(1), QTime(0, 1));
2034 KStarsDateTime ut = getGeo()->LTtoUT(KStarsDateTime(midnight));
2035 KSAlmanac ksal(ut, getGeo());
2037 for (
int i = 0; i < m_CatalogModel->rowCount(); ++i)
2039 const QString &
name = m_CatalogModel->item(i, 0)->text();
2040 const CatalogObject *catalogEntry = getObject(name);
2041 if (catalogEntry ==
nullptr)
2043 DPRINTF(stderr,
"************* Couldn't find \"%s\"\n",
name.
toLatin1().
data());
2046 double runHours = getRunHours(*catalogEntry, getDate(), *getGeo(), ui->minAltitude->value(),
2047 ui->minMoon->value(), ui->maxMoonAltitude->value(), ui->useArtificialHorizon->isChecked());
2048 QString hoursText = QString(
"%1").arg(runHours, 0,
'f', 1);
2049 QStandardItem *hItem =
new QStandardItem(hoursText);
2052 hItem->
setData(runHours, HOURS_ROLE);
2053 m_CatalogModel->setItem(i, HOURS_COLUMN, hItem);
2056 const auto time = KStarsDateTime(QDateTime(getDate(), QTime(12, 0)));
2057 const double altitude = getMaxAltitude(ksal, getDate(), getGeo(), *catalogEntry, 0, 0);
2058 QString altText = QString(
"%1º").arg(altitude, 0,
'f', 0);
2059 auto altItem =
new QStandardItem(altText);
2061 altItem->setData(altitude, ALTITUDE_ROLE);
2062 m_CatalogModel->setItem(i, ALTITUDE_COLUMN, altItem);
2064 KSMoon *moon = getMoon();
2070 auto tz = QTimeZone(getGeo()->TZ() * 3600);
2071 KStarsDateTime midnight = KStarsDateTime(getDate().addDays(1), QTime(0, 1));
2073 KSNumbers numbers(midnight.
djd());
2077 QString moonText = QString(
"%1º").arg(separation, 0,
'f', 0);
2078 auto moonItem =
new QStandardItem(moonText);
2080 moonItem->setData(separation, MOON_ROLE);
2081 m_CatalogModel->setItem(i, MOON_COLUMN, moonItem);
2085 auto moonItem =
new QStandardItem(
"");
2087 moonItem->setData(-1, MOON_ROLE);
2088 m_CatalogModel->setItem(i, MOON_COLUMN, moonItem);
2092 const bool imaged = m_CatalogModel->item(i, FLAGS_COLUMN)->data(FLAGS_ROLE).toInt() & IMAGED_BIT;
2094 highlightImagedObject(m_CatalogModel->index(i, NAME_COLUMN),
true);
2095 const bool picked = m_CatalogModel->item(i, FLAGS_COLUMN)->data(FLAGS_ROLE).toInt() & PICKED_BIT;
2097 highlightPickedObject(m_CatalogModel->index(i, NAME_COLUMN),
true);
2100 m_CatalogSortModel->setSourceModel(m_CatalogModel.data());
2102 DPRINTF(stderr,
"Recompute took %.1fs\n", timer.
elapsed() / 1000.0);
2113bool ALREADY_CHECKING =
false;
2114int ALREADY_CHECKING_INDEX = -1;
2116struct ObjectNeighbor
2118 CatalogObject object;
2121 ObjectNeighbor(CatalogObject o,
double d, QString nei) : object(o), distance(d), neighbor(nei) {}
2127void ImagingPlanner::checkTargets2(
bool backwards)
2129 if (ALREADY_CHECKING)
2132 ALREADY_CHECKING_INDEX--;
2134 ALREADY_CHECKING_INDEX++;
2136 if (sortedAddedObjects.size() == 0)
2138 fprintf(stderr,
"No TARGETS\n");
2141 if (ALREADY_CHECKING_INDEX >= sortedAddedObjects.size())
2142 ALREADY_CHECKING_INDEX = 0;
2143 else if (ALREADY_CHECKING_INDEX < 0)
2144 ALREADY_CHECKING_INDEX = sortedAddedObjects.size() - 1;
2145 KStarsDateTime time = KStarsData::Instance()->
clock()->
utc();
2146 dms lst = getGeo()->GSTtoLST(time.
gst());
2147 CatalogObject &o = sortedAddedObjects[ALREADY_CHECKING_INDEX].object;
2149 fprintf(stderr,
"%d: %s\n", ALREADY_CHECKING_INDEX, o.
name().
toLatin1().
data());
2152 bool keepGround = Options::showGround();
2153 bool keepAnimatedSlew = Options::useAnimatedSlewing();
2154 Options::setShowGround(
false);
2155 Options::setUseAnimatedSlewing(
false);
2159 Options::setShowGround(keepGround);
2160 Options::setUseAnimatedSlewing(keepAnimatedSlew);
2166void ImagingPlanner::checkTargets(
bool justCheckCurrentCatalog)
2168 if (ALREADY_CHECKING)
2170 checkTargets2(
false);
2173 ALREADY_CHECKING =
true;
2176 FlagComponent *flags = KStarsData::Instance()->
skyComposite()->flags();
2177 for (
int i = flags->
size() - 1; i >= 0; --i) flags->
remove(i);
2178 int rows = m_CatalogModel->rowCount();
2181 for (
int i = 0; i < rows; ++i)
2183 const QString &
name = m_CatalogModel->item(i, NAME_COLUMN)->text();
2184 auto object = getObject(name);
2188 flags->
add(SkyPoint(object->
ra(), object->
dec()),
"J2000.0",
"", name,
Qt::red);
2191 fprintf(stderr,
"Added %d flags\n", numFlags);
2195 QList<QString> targets;
2196 QList<QString> newObjects;
2197 if (!justCheckCurrentCatalog)
2203 QFile inputFile(fileName);
2206 QTextStream in(&inputFile);
2209 const QString line = in.readLine().trimmed();
2210 if (line.
size() > 0 && line[0] !=
'#' && newObjects.
indexOf(line) == -1)
2215 if (newObjects.
size() == 0)
2217 fprintf(stderr,
"No New Targets\n");
2222 QList<CatalogObject> addedObjects;
2223 sortedAddedObjects.clear();
2225 int count = 0, good = 0;
2226 for (
int i = 0; i < rows; ++i)
2228 const QString &
name = m_CatalogModel->item(i, NAME_COLUMN)->text();
2230 auto o = getObject(name);
2238 fprintf(stderr,
"********** %d/%d targets found. %d unique test objects\n", good, count, newObjects.
size());
2242 if (!justCheckCurrentCatalog)
2244 fprintf(stderr,
"Adding: ");
2245 for (
const auto &name : newObjects)
2247 if (getObject(name) !=
nullptr)
2252 CatalogObject object;
2253 if (!getKStarsCatalogObject(name, &
object))
2258 object.
setRA(
object.ra0());
2259 object.setDec(
object.dec0());
2262 fprintf(stderr,
"%s had primary name %s -- reverting.\n",
2264 object.setName(name);
2270 fprintf(stderr,
"\n--------------------------------------------------------\n");
2274 for (
int i = 0; i < addedObjects.
size(); ++i)
2276 auto &
object = addedObjects[i];
2277 double closest = 1e9;
2278 QString closestName;
2279 for (
int j = 0; j < targets.
size(); ++j)
2281 if (justCheckCurrentCatalog && i == j)
2283 auto name2 = targets[j];
2284 auto object2 = getObject(name2);
2285 if (object2 ==
nullptr)
2287 fprintf(stderr,
"********************************************************* O2 for targets[%d]: %s null!\n", j,
2291 object2->setRA(object2->ra0());
2292 object2->setDec(object2->dec0());
2293 const dms dist =
object.angularDistanceTo(object2);
2294 const double arcsecDist = dist.
Degrees() * 3600.0;
2295 if (closest > arcsecDist)
2297 closest = arcsecDist;
2298 closestName = name2;
2302 sortedAddedObjects.
push_back(ObjectNeighbor(addedObjects[i], closest, closestName));
2306 if (justCheckCurrentCatalog)
2308 fprintf(stderr,
"%7.1f %-10s closest %s\n", closest / 60.0,
object.
name().toLatin1().data(),
2313 double closestNew = 1e9;
2314 QString closestNewName;
2315 for (
int j = 0; j < addedObjects.
size() - 1; ++j)
2317 if (i == j)
continue;
2318 auto object2 = addedObjects[j];
2319 object2.setRA(object2.ra0());
2320 object2.setDec(object2.dec0());
2321 const dms dist =
object.angularDistanceTo(&object2);
2322 const double arcsecDist = dist.
Degrees() * 3600.0;
2323 if (closestNew > arcsecDist)
2325 closestNew = arcsecDist;
2326 closestNewName = object2.name();
2329 fprintf(stderr,
"%7.1f %-10s (closest %s) (closestNew %5.0f' %-10s)\n",
2330 closest / 60.0,
object.
name().toLatin1().data(), closestName.
toLatin1().
data(),
2332 flags->
add(SkyPoint(
object.ra(),
object.
dec()),
"J2000.0",
"", QString(
"%1").arg(
object.
name()),
Qt::yellow);
2335 std::sort(sortedAddedObjects.begin(), sortedAddedObjects.end(),
2336 [](
const ObjectNeighbor & a,
const ObjectNeighbor & b)
2338 return a.distance > b.distance;
2340 if (justCheckCurrentCatalog)
2342 fprintf(stderr,
"Sorted: ------------------------------------------\n");
2343 for (
const auto &o : sortedAddedObjects)
2344 fprintf(stderr,
"%7.1f %-10s closest %s\n",
2346 o.neighbor.toLatin1().data());
2348 fprintf(stderr,
"DONE. ------------------------------------------\n");
2352QString ImagingPlanner::defaultDirectory()
const
2366QFileInfoList findDefaultDirectories()
2369 QDir kDir(kstarsDir);
2372 nameFilters <<
"ImagingPlanner*";
2373 QFileInfoList dirs1 = kDir.entryInfoList(nameFilters);
2378 dirs1.append(dirs2);
2379 std::sort(dirs1.begin(), dirs1.end(), sortOldest);
2386QString ImagingPlanner::findDefaultCatalog()
const
2388 QFileInfoList subDirs = findDefaultDirectories();
2389 for(
const auto &dd : subDirs)
2391 QDir subDir(dd.absoluteFilePath());
2392 const QStringList csvFilter({
"*.csv"});
2394 if (files.size() > 0)
2399 for (
const auto &file : files)
2402 firstFile = file.absoluteFilePath();
2404 return file.absoluteFilePath();
2413void ImagingPlanner::loadInitialCatalog()
2415 QString catalog = Options::imagingPlannerCatalogPath();
2417 catalog = findDefaultCatalog();
2420 KSNotification::sorry(
2421 i18n(
"You need to load a catalog to start using this tool.\n"
2422 "Use the Load Catalog button if you have one.\n"
2423 "See Data -> Download New Data if not..."));
2424 setStatus(
i18n(
"No Catalog!"));
2427 loadCatalog(catalog);
2430void ImagingPlanner::setStatus(
const QString &message)
2432 ui->statusLabel->setText(message);
2435void ImagingPlanner::catalogLoaded()
2437 DPRINTF(stderr,
"All catalogs loaded: %d of %d have catalog images\n", m_numWithImage, m_numWithImage + m_numMissingImage);
2443 ui->CatalogView->setColumnHidden(FLAGS_COLUMN,
true);
2444 ui->CatalogView->setColumnHidden(NOTES_COLUMN,
true);
2446 m_CatalogSortModel->invalidate();
2448 ui->CatalogView->resizeColumnsToContents();
2451 auto index = ui->CatalogView->
model()->
index(0, 0);
2453 ui->CatalogView->selectionModel()->select(index,
2455 ui->CatalogView->setFocus();
2462void ImagingPlanner::updateStatus()
2464 if (currentObjectName().isEmpty())
2466 const int numDisplayedObjects = m_CatalogSortModel->rowCount();
2467 const int totalCatalogObjects = m_CatalogModel->rowCount();
2469 if (numDisplayedObjects > 0)
2470 setStatus(
i18n(
"Select an object."));
2471 else if (totalCatalogObjects > 0)
2472 setStatus(
i18n(
"Check Filters to unhide objects."));
2474 setStatus(
i18n(
"Load a Catalog."));
2481void ImagingPlanner::showEvent(
QShowEvent * e)
2484 if (m_initialShow ==
false)
2486 m_initialShow =
true;
2494void ImagingPlanner::slotClose()
2500QUrl ImagingPlanner::getAstrobinUrl(
const QString &target,
bool requireAwards,
bool requireSomeFilters,
double minRadius,
2503 QString myQuery = QString(
"text={\"value\":\"%1\",\"matchType\":\"ALL\"}").arg(target);
2506 auto localTime = getGeo()->UTtoLT(KStarsData::Instance()->clock()->utc());
2507 QDate today = localTime.date();
2508 myQuery.
append(QString(
"&date_acquired={\"min\":\"2018-01-01\",\"max\":\"%1\"}").arg(today.
toString(
"yyyy-MM-dd")));
2511 myQuery.
append(QString(
"&award=[\"iotd\",\"top-pick\",\"top-pick-nomination\"]"));
2513 if (requireSomeFilters)
2514 myQuery.
append(QString(
"&filter_types={\"value\":[\"H_ALPHA\",\"SII\",\"OIII\",\"R\",\"G\",\"B\"],\"matchType\":\"ANY\"}"));
2516 if ((minRadius > 0 || maxRadius > 0) && (maxRadius > minRadius))
2517 myQuery.
append(QString(
"&field_radius={\"min\":%1,\"max\":%2}").arg(minRadius).arg(maxRadius));
2522 QByteArray packed = pack(b);
2524 QByteArray compressed = qCompress(packed).remove(0, 4);
2526 QByteArray b64 = compressed.
toBase64();
2528 replaceByteArrayChars(b64,
'+', QByteArray(
"%2B"));
2529 replaceByteArrayChars(b64,
'=', QByteArray(
"%3D"));
2530 replaceByteArrayChars(b,
'"', QByteArray(
"%22"));
2531 replaceByteArrayChars(b,
':', QByteArray(
"%3A"));
2532 replaceByteArrayChars(b,
'[', QByteArray(
"%5B"));
2533 replaceByteArrayChars(b,
']', QByteArray(
"%5D"));
2534 replaceByteArrayChars(b,
',', QByteArray(
"%2C"));
2535 replaceByteArrayChars(b,
'\'', QByteArray(
"%27"));
2536 replaceByteArrayChars(b,
'{', QByteArray(
"%7B"));
2537 replaceByteArrayChars(b,
'}', QByteArray(
"%7D"));
2539 QString url = QString(
"https://app.astrobin.com/search?p=%1").arg(b64.
toStdString().c_str());
2543void ImagingPlanner::popupAstrobin(
const QString &target)
2545 QString newStr = replaceSpaceWith(target,
"-");
2548 const QUrl url = getAstrobinUrl(newStr, Options::astrobinAward(),
false, Options::astrobinMinRadius(),
2549 Options::astrobinMaxRadius());
2558bool ImagingPlanner::checkIfPageExists(
const QString &urlString)
2563 QUrl url(urlString);
2564 QNetworkRequest request(url);
2565 QNetworkReply *reply = m_networkManager.get(request);
2598void ImagingPlanner::adjustSpecialWebPageButton(
const QString &name)
2604 toolTip =
i18n(
"Search the Professor Seligman online site for NGC images.");
2609 toolTip =
i18n(
"Search the Professor Seligman online site for information about IC objects..");
2614 label =
"Sharpless";
2615 toolTip =
i18n(
"Search the galaxymap.org online site for information about Sharpless2 objects.");
2621 toolTip =
i18n(
"Search Nasa's online site for information about Messier objects..");
2626 toolTip =
i18n(
"Search Emil Ivanov's online site for information about VDB objects.");
2633 const int num = numberPart.
toInt(&ok);
2637 ui->searchSpecialWebPageImages->setText(catalog);
2638 ui->searchSpecialWebPageImages2->setText(catalog);
2639 ui->searchSpecialWebPageImages->setEnabled(
true);
2640 ui->searchSpecialWebPageImages2->setEnabled(
true);
2641 ui->searchSpecialWebPageImages->setToolTip(
toolTip);
2642 ui->searchSpecialWebPageImages2->setToolTip(
toolTip);
2646 ui->searchSpecialWebPageImages->setText(
"");
2647 ui->searchSpecialWebPageImages2->setText(
"");
2648 ui->searchSpecialWebPageImages->setEnabled(
false);
2649 ui->searchSpecialWebPageImages2->setEnabled(
false);
2650 ui->searchSpecialWebPageImages->setToolTip(
"");
2651 ui->searchSpecialWebPageImages2->setToolTip(
"");
2655void ImagingPlanner::searchSpecialWebPageImages()
2658 const QString
objectName = currentObjectName();
2663 const QString numberPart =
objectName.mid(3).trimmed();
2664 const int num = numberPart.
toInt(&ok);
2666 urlString = QString(
"https://cseligman.com/text/atlas/ngc%1%2.htm#%3")
2667 .
arg(num / 100).
arg(num % 100 < 50 ?
"" :
"a").
arg(num);
2671 const QString numberPart =
objectName.mid(2).trimmed();
2672 const int num = numberPart.
toInt(&ok);
2674 urlString = QString(
"https://cseligman.com/text/atlas/ic%1%2.htm#ic%3")
2675 .
arg(num / 100).
arg(num % 100 < 50 ?
"" :
"a").
arg(num);
2679 const QString numberPart =
objectName.mid(3).trimmed();
2680 const int num = numberPart.
toInt(&ok);
2682 urlString = QString(
"http://galaxymap.org/cat/view/sharpless/%1").
arg(num);
2686 const QString numberPart =
objectName.mid(1).trimmed();
2687 const int num = numberPart.
toInt(&ok);
2689 urlString = QString(
"https://science.nasa.gov/mission/hubble/science/"
2690 "explore-the-night-sky/hubble-messier-catalog/messier-%1").
arg(num);
2694 const QString numberPart =
objectName.mid(3).trimmed();
2695 const int num = numberPart.
toInt(&ok);
2698 urlString = QString(
"https://www.irida-observatory.org/CCD/VdB%1/VdB%1.html").
arg(num);
2699 if (!checkIfPageExists(urlString))
2700 urlString =
"https://www.emilivanov.com/CCD%20Images/Catalog_VdB.htm";
2707void ImagingPlanner::searchSimbad()
2710 QString
name = currentObjectName();
2712 int abellNumber = 0;
2713 bool abellPlanetary = isAbellPlanetary(name, &abellNumber);
2715 name = QString(
"PN A66 %1").
arg(abellNumber);
2717 name.
replace(QRegularExpression(
"sh2\\s*"),
"sh2-");
2726 QString urlStr = QString(
"https://simbad.cds.unistra.fr/simbad/sim-id?Ident=%1&NbIdent=1"
2727 "&Radius=20&Radius.unit=arcmin&submit=submit+id").
arg(name);
2733void ImagingPlanner::searchWikipedia()
2736 QString wikipediaAddress =
"https://en.wikipedia.org";
2737 QString
name = currentObjectName();
2740 DPRINTF(stderr,
"NULL object sent to Wikipedia.\n");
2746 QString urlStr = QString(
"%1/w/index.php?search=%2").
arg(wikipediaAddress).
arg(replaceSpaceWith(name,
"_"));
2751void ImagingPlanner::searchAstrobin()
2754 QString
name = currentObjectName();
2757 popupAstrobin(name);
2760bool ImagingPlanner::eventFilter(
QObject * obj,
QEvent * event)
2762 if (m_loadingCatalog)
2768 m_InitialLoad =
false;
2769 setStatus(
i18n(
"Loading Catalogs..."));
2774 QMouseEvent *mouseEvent =
static_cast<QMouseEvent *
>(
event);
2776 if ((obj == ui->helpButton) &&
2779 (mouseEvent->
modifiers() & Qt::KeyboardModifier::ShiftModifier) &&
2780 (mouseEvent->
modifiers() & Qt::KeyboardModifier::ControlModifier) &&
2781 (mouseEvent->
modifiers() & Qt::KeyboardModifier::AltModifier))
2785 ui->DevelFrame->setVisible(!ui->DevelFrame->isVisible());
2796 else if ((obj == ui->CatalogView->viewport()) &&
2800 int numImaged = 0, numNotImaged = 0, numPicked = 0, numNotPicked = 0, numIgnored = 0, numNotIgnored = 0;
2801 QStringList selectedNames;
2802 for (
const auto &r : ui->CatalogView->selectionModel()->selectedRows())
2804 selectedNames.
append(r.siblingAtColumn(0).
data().toString());
2805 bool isPicked = getFlag(r, PICKED_BIT, ui->CatalogView->model());
2806 if (isPicked) numPicked++;
2807 else numNotPicked++;
2808 bool isImaged = getFlag(r, IMAGED_BIT, ui->CatalogView->model());
2809 if (isImaged) numImaged++;
2810 else numNotImaged++;
2811 bool isIgnored = getFlag(r, IGNORED_BIT, ui->CatalogView->model());
2812 if (isIgnored) numIgnored++;
2813 else numNotIgnored++;
2816 if (selectedNames.
size() == 0)
2820 m_PopupMenu =
new ImagingPlannerPopup;
2822 const bool imaged = numImaged > 0;
2823 const bool picked = numPicked > 0;
2824 const bool ignored = numIgnored > 0;
2825 m_PopupMenu->init(
this, selectedNames,
2826 (numImaged > 0 && numNotImaged > 0) ?
nullptr : &imaged,
2827 (numPicked > 0 && numNotPicked > 0) ?
nullptr : &picked,
2828 (numIgnored > 0 && numNotIgnored > 0) ?
nullptr : &ignored);
2830 m_PopupMenu->popup(
pos);
2834 userNotesEditFinished();
2837 keywordEditFinished();
2848 keywordEditFinished();
2849 ui->keywordEdit->clearFocus();
2872 else if ((obj == ui->ImagePreview ||
2873 obj == ui->ImagePreviewCredit ||
2874 obj == ui->ImagePreviewCreditLink) &&
2877 if (!ui->ImagePreviewCreditLink->text().isEmpty())
2879 QUrl url(ui->ImagePreviewCreditLink->text());
2887void ImagingPlanner::keywordEditFinished()
2889 QString kwd = ui->keywordEdit->toPlainText().trimmed();
2890 ui->keywordEdit->clear();
2891 ui->keywordEdit->setText(kwd);
2892 if (m_Keyword != kwd)
2895 Options::setImagingPlannerKeyword(kwd);
2896 Options::self()->save();
2897 updateSortConstraints();
2898 m_CatalogSortModel->invalidate();
2899 ui->CatalogView->resizeColumnsToContents();
2904void ImagingPlanner::setDefaultImage()
2906 ui->ImagePreview->setPixmap(m_NoImagePixmap);
2907 ui->ImagePreview->update();
2908 ui->ImagePreviewCredit->setText(
"");
2909 ui->ImagePreviewCreditLink->setText(
"");
2914 if (m_loadingCatalog)
2917 Q_UNUSED(deselected);
2918 if (selected.
indexes().size() == 0)
2926 auto selection = selected.
indexes()[0];
2927 QString
name = selection.
data().toString();
2928 CatalogObject *
object = getObject(name);
2929 if (
object ==
nullptr)
2936 ui->ImagePreviewCredit->setText(
"");
2937 ui->ImagePreviewCreditLink->setText(
"");
2940 CatalogImageInfo catalogImageInfo;
2941 if (findCatalogImageInfo(name, &catalogImageInfo))
2943 QString filename = catalogImageInfo.m_Filename;
2944 if (!filename.
isEmpty() && !Options::imagingPlannerCatalogPath().isEmpty())
2946 QString imageFullPath = filename;
2947 if (QFileInfo(filename).isRelative())
2949 QString catDir = QFileInfo(Options::imagingPlannerCatalogPath()).absolutePath();
2950 imageFullPath = QString(
"%1%2%3").
arg(catDir)
2953 if (!QFile(imageFullPath).exists())
2954 DPRINTF(stderr,
"Image for \"%s\" -- \"%s\" doesn't exist\n",
2958 if (!catalogImageInfo.m_Link.
isEmpty())
2960 ui->ImagePreviewCreditLink->setText(catalogImageInfo.m_Link);
2961 ui->ImagePreview->setToolTip(
"Click to see original");
2962 ui->ImagePreviewCreditLink->setToolTip(
"Click to see original");
2966 ui->ImagePreviewCreditLink->setText(
"");
2967 ui->ImagePreview->setToolTip(
"");
2968 ui->ImagePreviewCreditLink->setToolTip(
"");
2971 if (!catalogImageInfo.m_Author.
isEmpty() && !catalogImageInfo.m_License.
isEmpty())
2973 ui->ImagePreviewCredit->setText(
2974 QString(
"Credit: %1 (with license %2)").arg(catalogImageInfo.m_Author)
2975 .arg(creativeCommonsString(catalogImageInfo.m_License)));
2976 ui->ImagePreviewCredit->setToolTip(
2977 QString(
"Original image license: %1")
2978 .arg(creativeCommonsTooltipString(catalogImageInfo.m_License)));
2980 else if (!catalogImageInfo.m_Author.
isEmpty())
2982 ui->ImagePreviewCredit->setText(
2983 QString(
"Credit: %1").arg(catalogImageInfo.m_Author));
2984 ui->ImagePreviewCredit->setToolTip(
"");
2986 else if (!catalogImageInfo.m_License.
isEmpty())
2988 ui->ImagePreviewCredit->setText(
2989 QString(
"(license %1)").arg(creativeCommonsString(catalogImageInfo.m_License)));
2990 ui->ImagePreviewCredit->setToolTip(
2991 QString(
"Original image license: %1")
2992 .arg(creativeCommonsTooltipString(catalogImageInfo.m_License)));
2996 ui->ImagePreviewCredit->setText(
"");
2997 ui->ImagePreviewCredit->setToolTip(
"");
3003 object->load_image();
3004 auto image =
object->image();
3010 const QString foundFilename = findObjectImage(name);
3013 constexpr int thumbHeight = 300, thumbWidth = 400;
3014 const QImage img = QImage(foundFilename);
3015 const bool scale = img.
width() > thumbWidth || img.
height() > thumbHeight;
3017 ui->ImagePreview->setPixmap(
3028 adjustSpecialWebPageButton(currentObjectName());
3031void ImagingPlanner::updateDisplays()
3036 if (!currentCatalogObject())
3038 if (ui->CatalogView->model()->rowCount() > 0)
3040 auto index = ui->CatalogView->
model()->
index(0, 0);
3041 ui->CatalogView->selectionModel()->select(index,
3046 auto object = currentCatalogObject();
3049 updateDetails(*
object, currentObjectFlags());
3050 updateNotes(currentObjectNotes());
3051 plotAltitudeGraph(getDate(), object->
ra0(), object->
dec0());
3058void ImagingPlanner::updateDetails(
const CatalogObject &
object,
int flags)
3060 ui->infoObjectName->setText(
object.
name());
3061 ui->infoSize->setText(QString(
"%1' x %2'").arg(
object.a(), 0,
'f', 1).arg(
object.b(), 0,
'f', 1));
3063 QPalette
palette = ui->infoObjectLongName->palette();
3066 ui->infoObjectLongName->setPalette(
palette);
3067 if (
object.longname().isEmpty() || (
object.longname() ==
object.
name()))
3068 ui->infoObjectLongName->clear();
3070 ui->infoObjectLongName->setText(QString(
"(%1)").arg(
object.longname()));
3074 auto noon = KStarsDateTime(getDate(), QTime(12, 0, 0));
3075 QTime riseTime =
object.riseSetTime(noon, getGeo(),
true);
3076 QTime setTime =
object.riseSetTime(noon, getGeo(),
false);
3077 QTime transitTime =
object.transitTime(noon, getGeo());
3078 dms transitAltitude =
object.transitAltitude(noon, getGeo());
3081 KSMoon *moon = getMoon();
3084 const double separation = ui->CatalogView->selectionModel()->currentIndex()
3085 .siblingAtColumn(MOON_COLUMN).data(MOON_ROLE).toDouble();
3087 if (separation >= 0)
3088 moonString = QString(
"%1 \u2220 %3º").
arg(
i18n(
"Moon")).
arg(separation, 0,
'f', 1);
3091 QString riseSetString;
3093 riseSetString = QString(
"%1 %2 @ %3º")
3098 riseSetString = QString(
"%1 %2")
3102 riseSetString = QString(
"%1 %2 %3 %4 @ %5º")
3109 riseSetString = QString(
"%1 %2")
3113 riseSetString = QString(
"%1 %2 %3 %4 @ %5º")
3120 riseSetString = QString(
"%1 %2 %3 %4")
3126 riseSetString = QString(
"%1 %2 %3 %4 %5 %6 @ %7º")
3134 if (moonString.
size() > 0)
3135 riseSetString.
append(QString(
", %1").arg(moonString));
3136 ui->infoRiseSet->setText(riseSetString);
3138 palette = ui->infoObjectFlags->palette();
3140 ui->infoObjectFlags->setPalette(
palette);
3141 ui->infoObjectFlags->setText(flagString(flags));
3150void ImagingPlanner::plotAltitudeGraph(
const QDate &date,
const dms &ra,
const dms &dec)
3152 auto altitudeGraph = ui->altitudeGraph;
3153 altitudeGraph->setAltitudeAxis(-20.0, 90.0);
3156 QVector<QDateTime> jobStartTimes, jobEndTimes;
3157 getRunTimes(date, *getGeo(), ui->minAltitude->value(), ui->minMoon->value(), ui->maxMoonAltitude->value(), ra, dec,
3158 ui->useArtificialHorizon->isChecked(),
3159 &jobStartTimes, &jobEndTimes);
3161 auto tz = QTimeZone(getGeo()->TZ() * 3600);
3162 KStarsDateTime midnight = KStarsDateTime(date.
addDays(1), QTime(0, 1));
3165 KStarsDateTime ut = getGeo()->LTtoUT(KStarsDateTime(midnight));
3166 KSAlmanac ksal(ut, getGeo());
3167 QDateTime dawn = midnight.
addSecs(24 * 3600 * ksal.getDawnAstronomicalTwilight());
3169 QDateTime dusk = midnight.
addSecs(24 * 3600 * ksal.getDuskAstronomicalTwilight());
3172 Ekos::SchedulerJob job;
3173 setupJob(job,
"temp", ui->minAltitude->value(), ui->minMoon->value(), ui->maxMoonAltitude->value(), ra, dec,
3174 ui->useArtificialHorizon->isChecked());
3176 QVector<double> times, alts;
3177 QDateTime plotStart = dusk;
3182 plotStart = plotStart.
addSecs(-1 * 3600);
3185 auto plotEnd = dawn.
addSecs(1 * 3600);
3188 while (t.secsTo(plotEnd) > 0)
3190 SkyPoint coords = job.getTargetCoords();
3191 double alt = getAltitude(getGeo(), coords, t);
3193 double hour = midnight.
secsTo(t) / 3600.0;
3195 t = t.addSecs(60 * 10);
3198 altitudeGraph->plot(getGeo(), &ksal, times, alts);
3200 for (
int i = 0; i < jobStartTimes.
size(); ++i)
3202 auto startTime = jobStartTimes[i];
3203 auto stopTime = jobEndTimes[i];
3204 if (startTime < plotStart) startTime = plotStart;
3205 if (stopTime > plotEnd) stopTime = plotEnd;
3208 stopTime.setTimeZone(tz);
3210 QVector<double> runTimes, runAlts;
3215 while (t.secsTo(stopTime) > 0)
3217 SkyPoint coords = job.getTargetCoords();
3218 double alt = getAltitude(getGeo(), coords, t);
3220 double hour = midnight.
secsTo(t) / 3600.0;
3222 t = t.addSecs(60 * 10);
3224 altitudeGraph->plotOverlay(runTimes, runAlts);
3228void ImagingPlanner::updateCounts()
3230 const int numDisplayedObjects = m_CatalogSortModel->rowCount();
3231 const int totalCatalogObjects = m_CatalogModel->rowCount();
3232 if (numDisplayedObjects == 1)
3233 ui->tableCount->setText(QString(
"1/%1 %2").arg(totalCatalogObjects).arg(
i18n(
"object")));
3235 ui->tableCount->setText(QString(
"%1/%2 %3").arg(numDisplayedObjects).arg(totalCatalogObjects).arg(
i18n(
"objects")));
3238void ImagingPlanner::moveBackOneDay()
3241 QString selection = currentObjectName();
3242 ui->DateEdit->setDate(ui->DateEdit->date().addDays(-1));
3246 scrollToName(selection);
3249void ImagingPlanner::moveForwardOneDay()
3251 QString selection = currentObjectName();
3252 ui->DateEdit->setDate(ui->DateEdit->date().addDays(1));
3256 scrollToName(selection);
3259QString ImagingPlanner::currentObjectName()
const
3261 QString
name = ui->CatalogView->selectionModel()->currentIndex().siblingAtColumn(NAME_COLUMN).
data(
3268 QString
name = currentObjectName();
3269 return getObject(name);
3274void ImagingPlanner::objectDetails()
3276 CatalogObject *current = currentCatalogObject();
3277 if (current ==
nullptr)
3279 auto ut = KStarsData::Instance()->
ut();
3281 QPointer<DetailDialog> dd =
3287void ImagingPlanner::centerOnSkymap()
3289 if (!Options::imagingPlannerCenterOnSkyMap())
3291 reallyCenterOnSkymap();
3294void ImagingPlanner::reallyCenterOnSkymap()
3296 CatalogObject *current = currentCatalogObject();
3297 if (current ==
nullptr)
3303 DPRINTF(stderr,
"found a 0,0 object\n");
3308 KStarsDateTime time = KStarsData::Instance()->
clock()->
utc();
3309 dms lst = getGeo()->GSTtoLST(time.
gst());
3314 bool keepGround = Options::showGround();
3315 bool keepAnimatedSlew = Options::useAnimatedSlewing();
3316 Options::setShowGround(
false);
3317 Options::setUseAnimatedSlewing(
false);
3323 Options::setShowGround(keepGround);
3324 Options::setUseAnimatedSlewing(keepAnimatedSlew);
3327void ImagingPlanner::setSelection(
int flag,
bool enabled)
3329 auto rows = ui->CatalogView->selectionModel()->selectedRows();
3338 QList<QModelIndex> sourceIndeces;
3339 for (
int i = 0; i < rows.size(); ++i)
3341 auto proxyIndex = rows[i].siblingAtColumn(FLAGS_COLUMN);
3342 auto sourceIndex = m_CatalogSortModel->mapToSource(proxyIndex);
3343 sourceIndeces.
append(sourceIndex);
3346 for (
int i = 0; i < sourceIndeces.
size(); ++i)
3348 auto &sourceIndex = sourceIndeces[i];
3352 setFlag(sourceIndex, flag, m_CatalogModel.data());
3354 clearFlag(sourceIndex, flag, m_CatalogModel.data());
3356 QString
name = m_CatalogModel->
data(sourceIndex.siblingAtColumn(NAME_COLUMN)).toString();
3357 int flags = m_CatalogModel->data(sourceIndex.siblingAtColumn(FLAGS_COLUMN), FLAGS_ROLE).toInt();
3358 QString notes = m_CatalogModel->
data(sourceIndex.siblingAtColumn(NOTES_COLUMN), NOTES_ROLE).
toString();
3359 saveToDB(name, flags, notes);
3361 if (flag == IMAGED_BIT)
3362 highlightImagedObject(sourceIndex,
enabled);
3363 if (flag == PICKED_BIT)
3364 highlightPickedObject(sourceIndex,
enabled);
3369void ImagingPlanner::highlightImagedObject(
const QModelIndex &index,
bool imaged)
3372 QColor m_DefaultCellBackground(36, 35, 35);
3373 QColor m_ImagedObjectBackground(10, 65, 10);
3374 QString themeName = KSTheme::Manager::instance()->currentThemeName().
toLatin1().
data();
3375 if (themeName ==
"High Key" || themeName ==
"Default" || themeName ==
"White Balance")
3377 m_DefaultCellBackground = QColor(240, 240, 240);
3378 m_ImagedObjectBackground = QColor(180, 240, 180);
3380 for (
int col = 0; col < LAST_COLUMN; ++col)
3383 m_CatalogModel->setData(colIndex, imaged ? m_ImagedObjectBackground : m_DefaultCellBackground,
Qt::BackgroundRole);
3387void ImagingPlanner::highlightPickedObject(
const QModelIndex &index,
bool picked)
3389 for (
int col = 0; col < LAST_COLUMN; ++col)
3393 auto ff = qvariant_cast<QFont>(
font);
3395 ff.setItalic(picked);
3396 ff.setUnderline(picked);
3402void ImagingPlanner::setSelectionPicked()
3404 setSelection(PICKED_BIT,
true);
3407void ImagingPlanner::setSelectionNotPicked()
3409 setSelection(PICKED_BIT,
false);
3412void ImagingPlanner::setSelectionImaged()
3414 setSelection(IMAGED_BIT,
true);
3417void ImagingPlanner::setSelectionNotImaged()
3419 setSelection(IMAGED_BIT,
false);
3422void ImagingPlanner::setSelectionIgnored()
3424 setSelection(IGNORED_BIT,
true);
3427void ImagingPlanner::setSelectionNotIgnored()
3429 setSelection(IGNORED_BIT,
false);
3432int ImagingPlanner::currentObjectFlags()
3434 auto index = ui->CatalogView->selectionModel()->currentIndex().
siblingAtColumn(FLAGS_COLUMN);
3435 const bool hasFlags = ui->CatalogView->model()->data(index, FLAGS_ROLE).canConvert<
int>();
3438 return ui->CatalogView->model()->data(index, FLAGS_ROLE).toInt();
3441QString ImagingPlanner::currentObjectNotes()
3443 auto index = ui->CatalogView->selectionModel()->currentIndex().
siblingAtColumn(NOTES_COLUMN);
3444 const bool hasNotes = ui->CatalogView->
model()->
data(index, NOTES_ROLE).
canConvert<QString>();
3447 return ui->CatalogView->model()->data(index, NOTES_ROLE).toString();
3450void ImagingPlanner::setCurrentObjectNotes(
const QString ¬es)
3452 auto index = ui->CatalogView->selectionModel()->currentIndex();
3457 auto sourceIndex = m_CatalogSortModel->mapToSource(sibling);
3459 m_CatalogModel->setData(sourceIndex, n, NOTES_ROLE);
3462ImagingPlannerPopup::ImagingPlannerPopup() :
QMenu(nullptr)
3471void ImagingPlannerPopup::init(ImagingPlanner * planner,
const QStringList &names,
3472 const bool * imaged,
const bool * picked,
const bool * ignored)
3475 if (names.
size() == 0)
return;
3478 if (names.
size() == 1)
3480 else if (names.
size() <= 3)
3483 for (
int i = 1; i < names.
size(); i++)
3484 title.append(QString(
", %1").arg(names[i]));
3487 title =
i18n(
"%1, %2 and %3 other objects", names[0], names[1], names.
size() - 2);
3491 QString word = names.
size() == 1 ? names[0] :
i18n(
"objects");
3493 if (imaged ==
nullptr)
3495 addAction(
i18n(
"Mark %1 as NOT imaged", word), planner, &ImagingPlanner::setSelectionNotImaged);
3496 addAction(
i18n(
"Mark %1 as already imaged", word), planner, &ImagingPlanner::setSelectionImaged);
3499 addAction(
i18n(
"Mark %1 as NOT imaged", word), planner, &ImagingPlanner::setSelectionNotImaged);
3501 addAction(
i18n(
"Mark %1 as already imaged", word), planner, &ImagingPlanner::setSelectionImaged);
3503 if (picked ==
nullptr)
3505 addAction(
i18n(
"Un-pick %1", word), planner, &ImagingPlanner::setSelectionNotPicked);
3506 addAction(
i18n(
"Pick %1", word), planner, &ImagingPlanner::setSelectionPicked);
3509 addAction(
i18n(
"Un-pick %1", word), planner, &ImagingPlanner::setSelectionNotPicked);
3511 addAction(
i18n(
"Pick %1", word), planner, &ImagingPlanner::setSelectionPicked);
3514 if (ignored ==
nullptr)
3516 addAction(
i18n(
"Stop ignoring %1", word), planner, &ImagingPlanner::setSelectionNotIgnored);
3517 addAction(
i18n(
"Ignore %1", word), planner, &ImagingPlanner::setSelectionIgnored);
3521 addAction(
i18n(
"Stop ignoring %1", word), planner, &ImagingPlanner::setSelectionNotIgnored);
3523 addAction(
i18n(
"Ignore %1", word), planner, &ImagingPlanner::setSelectionIgnored);
3526 addAction(
i18n(
"Center %1 on SkyMap", names[0]), planner, &ImagingPlanner::reallyCenterOnSkymap);
3528 addAction(
i18n(
"Screenshot some image of %1, plate-solve it, and temporarily place it on the SkyMap", names[0]), planner,
3529 &ImagingPlanner::takeScreenshot);
3533ImagingPlannerDBEntry::ImagingPlannerDBEntry(
const QString &name,
bool picked,
bool imaged,
3534 bool ignored,
const QString ¬es) : m_Name(
name), m_Notes(notes)
3539ImagingPlannerDBEntry::ImagingPlannerDBEntry(
const QString &name,
int flags,
const QString ¬es)
3540 : m_Name(
name), m_Flags(flags), m_Notes(notes)
3544void ImagingPlannerDBEntry::getFlags(
bool * picked,
bool * imaged,
bool * ignored)
3546 *picked = m_Flags & PickedBit;
3547 *imaged = m_Flags & ImagedBit;
3548 *ignored = m_Flags & IgnoredBit;
3552void ImagingPlannerDBEntry::setFlags(
bool picked,
bool imaged,
bool ignored)
3555 if (picked) m_Flags |= PickedBit;
3556 if (imaged) m_Flags |= ImagedBit;
3557 if (ignored) m_Flags |= IgnoredBit;
3560void ImagingPlanner::saveToDB(
const QString &name,
bool picked,
bool imaged,
3561 bool ignored,
const QString ¬es)
3563 ImagingPlannerDBEntry e(name, 0, notes);
3564 e.setFlags(picked, imaged, ignored);
3568void ImagingPlanner::saveToDB(
const QString &name,
int flags,
const QString ¬es)
3570 ImagingPlannerDBEntry e(name, flags, notes);
3575void ImagingPlanner::loadFromDB()
3580 m_CatalogSortModel->setSourceModel(
nullptr);
3582 auto tz = QTimeZone(getGeo()->TZ() * 3600);
3583 KStarsDateTime midnight = KStarsDateTime(getDate().addDays(1), QTime(0, 1));
3584 KStarsDateTime ut = getGeo()->LTtoUT(KStarsDateTime(midnight));
3585 KSAlmanac ksal(ut, getGeo());
3587 QList<ImagingPlannerDBEntry>
list;
3589 QHash<QString, ImagingPlannerDBEntry> dbData;
3590 QHash<QString, int> dbNotes;
3591 for (
const auto &entry : list)
3593 dbData[entry.m_Name] = entry;
3596 int rows = m_CatalogModel->rowCount();
3597 for (
int i = 0; i < rows; ++i)
3599 const QString &
name = m_CatalogModel->item(i, NAME_COLUMN)->text();
3600 auto entry = dbData.
find(name);
3601 if (entry != dbData.
end())
3603 QVariant f = entry->m_Flags;
3604 m_CatalogModel->item(i, FLAGS_COLUMN)->setData(f, FLAGS_ROLE);
3605 if (entry->m_Flags & IMAGED_BIT)
3606 highlightImagedObject(m_CatalogModel->index(i, NAME_COLUMN),
true);
3607 if (entry->m_Flags & PICKED_BIT)
3608 highlightPickedObject(m_CatalogModel->index(i, NAME_COLUMN),
true);
3609 QVariant n = entry->m_Notes;
3610 m_CatalogModel->item(i, NOTES_COLUMN)->setData(n, NOTES_ROLE);
3614 m_CatalogSortModel->setSourceModel(m_CatalogModel.data());
3617void ImagingPlanner::loadImagedFile()
3619 if (m_loadingCatalog)
3627 QFile inputFile(fileName);
3631 QStringList failedNames;
3632 QTextStream in(&inputFile);
3638 name = tweakNames(name);
3639 if (getObject(name))
3642 auto startIndex = m_CatalogModel->index(0, NAME_COLUMN);
3643 QVariant value(name);
3645 if (matches.size() > 0)
3647 setFlag(matches[0], IMAGED_BIT, m_CatalogModel);
3648 highlightImagedObject(matches[0],
true);
3651 QString
name = m_CatalogModel->
data(matches[0].siblingAtColumn(NAME_COLUMN)).toString();
3652 int flags = m_CatalogModel->data(matches[0].siblingAtColumn(FLAGS_COLUMN), FLAGS_ROLE).toInt();
3653 QString notes = m_CatalogModel->
data(matches[0].siblingAtColumn(NOTES_COLUMN), NOTES_ROLE).toString();
3654 saveToDB(name, flags, notes);
3658 DPRINTF(stderr,
"ooops! internal inconsitency--got an object but match didn't work");
3662 failedNames.
append(name);
3665 if (failedNames.
size() == 0)
3668 KSNotification::info(
i18n(
"Successfully marked %1 objects as read", numSuccess));
3670 KSNotification::sorry(
i18n(
"Empty file"));
3674 int num = std::min((
int)failedNames.
size(), 10);
3675 QString sample = QString(
"\"%1\"").arg(failedNames[0]);
3676 for (
int i = 1; i < num; ++i)
3677 sample.
append(QString(
" \"%1\"").arg(failedNames[i]));
3678 if (numSuccess == 0 && failedNames.
size() <= 10)
3679 KSNotification::sorry(
i18n(
"Failed marking all of these objects imaged: %1", sample));
3680 else if (numSuccess == 0)
3681 KSNotification::sorry(
i18n(
"Failed marking %1 objects imaged, including: %2", failedNames.
size(), sample));
3682 else if (numSuccess > 0 && failedNames.
size() <= 10)
3683 KSNotification::sorry(
i18n(
"Succeeded marking %1 objects imaged. Failed with %2: %3",
3684 numSuccess, failedNames.
size() == 1 ?
"this" :
"these", sample));
3686 KSNotification::sorry(
i18n(
"Succeeded marking %1 objects imaged. Failed with %2 including these: %3",
3687 numSuccess, failedNames.
size(), sample));
3692 KSNotification::sorry(
i18n(
"Sorry, couldn't open file: \"%1\"", fileName));
3696void ImagingPlanner::addCatalogImageInfo(
const CatalogImageInfo &info)
3698 m_CatalogImageInfoMap[info.m_Name.toLower()] = info;
3701bool ImagingPlanner::findCatalogImageInfo(
const QString &name, CatalogImageInfo * info)
3704 if (
result == m_CatalogImageInfoMap.end())
3706 if (
result->m_Filename.isEmpty())
3712void ImagingPlanner::loadCatalogViaMenu()
3714 QString startDir = Options::imagingPlannerCatalogPath();
3716 startDir = defaultDirectory();
3725void ImagingPlanner::loadCatalog(
const QString &path)
3727 removeEventFilters();
3734 m_loadingCatalog =
true;
3735 loadCatalogFromFile(path);
3743 m_loadingCatalog =
false;
3744 installEventFilters();
3747 if (m_CatalogSortModel->rowCount() > 0)
3749 auto name = m_CatalogSortModel->index(0, 0).
data().toString();
3751 QItemSelection selection, deselection;
3752 selection.
select(m_CatalogSortModel->index(0, 0), m_CatalogSortModel->index(0, 0));
3753 selectionChanged(selection, deselection);
3757CatalogImageInfo::CatalogImageInfo(
const QString &csv)
3762 QStringList columns = line.
split(
",");
3763 if (columns.
size() < 1 || columns[0].isEmpty())
3766 m_Name = columns[column++];
3767 if (columns.
size() <= column)
return;
3768 m_Filename = columns[column++];
3769 if (columns.
size() <= column)
return;
3770 m_Author = columns[column++];
3771 if (columns.
size() <= column)
return;
3772 m_Link = columns[column++];
3773 if (columns.
size() <= column)
return;
3774 m_License = columns[column++];
3795void ImagingPlanner::loadCatalogFromFile(
QString path,
bool reset)
3797 QFile inputFile(path);
3801 m_numMissingImage = 0;
3803 int numMissingImage = 0, numWithImage = 0;
3804 if (!inputFile.exists())
3806 emit popupSorry(
i18n(
"Sorry, catalog file doesn't exist: \"%1\"", path));
3809 QStringList objectNames;
3812 const auto tz = QTimeZone(getGeo()->TZ() * 3600);
3813 const KStarsDateTime midnight = KStarsDateTime(getDate().addDays(1), QTime(0, 1));
3814 const KStarsDateTime ut = getGeo()->LTtoUT(KStarsDateTime(midnight));
3815 const KSAlmanac ksal(ut, getGeo());
3819 Options::setImagingPlannerCatalogPath(path);
3820 Options::self()->save();
3821 if (m_CatalogModel->rowCount() > 0)
3822 m_CatalogModel->removeRows(0, m_CatalogModel->rowCount());
3825 QTextStream in(&inputFile);
3828 CatalogImageInfo info(in.readLine().trimmed());
3829 const QString
name = info.m_Name;
3837 const auto match = re.match(name);
3838 if (
match.hasMatch())
3840 const QString catFilename =
match.captured(1);
3841 if (catFilename.
isEmpty())
continue;
3842 const QFileInfo fInfo(catFilename);
3844 QString catFullPath = catFilename;
3845 if (!fInfo.isAbsolute())
3847 const QString catDir = QFileInfo(path).absolutePath();
3848 catFullPath = QString(
"%1%2%3").
arg(catDir)
3851 if (catFullPath != path)
3852 loadCatalogFromFile(catFullPath,
false);
3862 const auto match = re.match(name);
3863 if (
match.hasMatch())
3865 const QString catFilename =
match.captured(1);
3866 if (catFilename.
isEmpty())
continue;
3867 const QFileInfo fInfo(catFilename);
3869 QString catFullPath = catFilename;
3870 if (!fInfo.isAbsolute())
3872 const QString catDir = QFileInfo(path).absolutePath();
3873 catFullPath = QString(
"%1%2%3").
arg(catDir)
3876 std::pair<bool, QString> out = m_manager.import_catalog(catFullPath,
false);
3877 DPRINTF(stderr,
"Load of KStars catalog %s %s%s\n", catFullPath.
toLatin1().
data(),
3878 out.first ?
"succeeded." :
"failed: ", out.second.toLatin1().data());
3887 const auto match = re.match(name);
3888 if (
match.hasMatch())
3890 const QString catIDstr =
match.captured(1);
3891 if (catIDstr.
isEmpty())
continue;
3894 const int catID = catIDstr.
toInt(&ok);
3895 if (ok && m_manager.catalog_exists(catID))
3897 const std::pair<bool, QString> out = m_manager.remove_catalog(catID);
3898 DPRINTF(stderr,
"Removal of out-of-date catalog %d %s%s\n", catID,
3899 out.first ?
"succeeded." :
"failed: ", out.second.toLatin1().data());
3904 objectNames.
append(name);
3905 if (!info.m_Filename.isEmpty())
3908 QFileInfo fInfo(info.m_Filename);
3909 if (fInfo.isRelative())
3910 info.m_Filename = QString(
"%1%2%3").arg(QFileInfo(path).absolutePath())
3912 addCatalogImageInfo(info);
3923 int num = 0, numBad = 0, iteration = 0;
3925 for (
const auto &name : objectNames)
3927 setStatus(
i18n(
"%1/%2: Adding %3", ++iteration, objectNames.size(), name));
3928 if (addCatalogItem(ksal, name, 0)) num++;
3935 m_numWithImage += numWithImage;
3936 m_numMissingImage += numMissingImage;
3937 DPRINTF(stderr,
"Catalog %s: %d of %d have catalog images\n",
3942 emit popupSorry(
i18n(
"Sorry, couldn't open file: \"%1\"", path));
3946void ImagingPlanner::sorry(
const QString &message)
3948 KSNotification::sorry(message);
3951void ImagingPlanner::captureRegion(
const QImage &screenshot)
3953 if (m_PlateSolve.get())
disconnect(m_PlateSolve.get());
3957 m_ScreenShotImage = screenshot;
3960 QString tempQImage = QDir(temporaryPath).filePath(
"screenshot.png");
3961 m_ScreenShotImage.
save(tempQImage);
3962 FITSData::ImageToFITS(tempQImage,
"png", m_ScreenShotFilename);
3966 if (!m_PlateSolve.get())
3967 m_PlateSolve.reset(
new PlateSolve(
this));
3969 m_PlateSolve->setImageDisplay(m_ScreenShotImage);
3970 if (currentCatalogObject())
3972 m_PlateSolve->setPosition(*currentCatalogObject());
3973 m_PlateSolve->setUsePosition(
true);
3974 m_PlateSolve->setUseScale(
false);
3975 m_PlateSolve->setLinear(
false);
3976 reallyCenterOnSkymap();
3979 m_PlateSolve->setWindowTitle(QString(
"Plate Solve for %1").arg(currentObjectName()));
3980 m_PlateSolve->show();
3981 if (Options::imagingPlannerStartSolvingImmediately())
3985void ImagingPlanner::takeScreenshot()
3987 if (!currentCatalogObject())
3990 const QString messageID =
"ImagingPlannerScreenShotInfo";
3991 const QString screenshotInfo =
3992 QString(
"<p><b>Taking a screenshot of %1 for the SkyMap</b></p>"
3993 "<p>This allows you to screenshot/copy a good example image of %1 from another application, "
3994 "such as a browser viewing %1 on Astrobin. It then plate-solves that screenshot and overlays "
3995 "it temporarily on the SkyMap.</p>"
3996 "<p>You can use this to help you frame your future %1 capture. "
3997 "The SkyMap overlay will only be visible in the current KStars session.</p>"
3998 "<p>In order to do this, you should make the image you wish to copy visible "
3999 "on your screen now, before clicking OK. After you click OK you will see the mouse pointer change "
4000 "to the screenshot pointer. You then drag your mouse over the part of the %1 image "
4001 "you wish to copy. If you check do-not-ask-again, then you must make sure that your desired image "
4002 "is already visible before you run this.</p>"
4003 "<p>After you take your screenshot, the system will bring up a menu to help plate-solve the image. "
4004 "Click SOLVE on that menu to start the process, unless it is automatically started. "
4005 "Once successfully plate-solved, your image will be overlayed onto the SkyMap.").arg(currentObjectName());
4006#if KIO_VERSION >= QT_VERSION_CHECK(5, 100, 0)
4010 KGuiItem(
i18nc(
"@action:button",
"OK")),
4011 KGuiItem(
i18nc(
"@action:button",
"Cancel")),
4016 const int result = KMessageBox::questionYesNo(
this, screenshotInfo,
"ScreenShot",
4017 KGuiItem(
"OK"), KGuiItem(
"Cancel"), messageID);
4018 if (
result != KMessageBox::Yes)
4024 m_CaptureWidget.reset();
4030 if (m_CaptureWidget.get())
disconnect(m_CaptureWidget.get());
4031 m_CaptureWidget.reset(
new ScreenCapture());
4034 disconnect(m_CaptureWidget.get(), &ScreenCapture::aborted,
nullptr,
nullptr);
4035 QObject::connect(m_CaptureWidget.get(), &ScreenCapture::aborted,
this, [
this]()
4037 disconnect(m_CaptureWidget.get());
4038 m_CaptureWidget.reset();
4040 this->activateWindow();
4042 m_CaptureWidget->show();
4045void ImagingPlanner::extractImage()
4047 disconnect(m_PlateSolve.get(), &PlateSolve::solverFailed,
nullptr,
nullptr);
4048 connect(m_PlateSolve.get(), &PlateSolve::solverFailed,
this, [
this]()
4050 disconnect(m_PlateSolve.get());
4052 disconnect(m_PlateSolve.get(), &PlateSolve::solverSuccess,
nullptr,
nullptr);
4053 connect(m_PlateSolve.get(), &PlateSolve::solverSuccess,
this, [
this]()
4055 disconnect(m_PlateSolve.get());
4056 const FITSImage::Solution &solution = m_PlateSolve->solution();
4057 ImageOverlay overlay;
4058 overlay.m_Orientation = solution.orientation;
4059 overlay.m_RA = solution.ra;
4060 overlay.m_DEC = solution.dec;
4061 overlay.m_ArcsecPerPixel = solution.pixscale;
4062 overlay.m_EastToTheRight = solution.parity;
4063 overlay.m_Status = ImageOverlay::AVAILABLE;
4065 const bool mirror = !solution.parity;
4066 const int scaleWidth = std::min(m_ScreenShotImage.width(), Options::imageOverlayMaxDimension());
4067 QImage *processedImg = new QImage;
4069 *processedImg = m_ScreenShotImage.mirrored(true, false).scaledToWidth(scaleWidth);
4071 *processedImg = m_ScreenShotImage.scaledToWidth(scaleWidth);
4072 overlay.m_Img.reset(processedImg);
4073 overlay.m_Width = processedImg->width();
4074 overlay.m_Height = processedImg->height();
4075 KStarsData::Instance()->skyComposite()->imageOverlay()->show();
4076 KStarsData::Instance()->skyComposite()->imageOverlay()->addTemporaryImageOverlay(overlay);
4078 KStars::Instance()->activateWindow();
4079 KStars::Instance()->raise();
4080 m_PlateSolve->close();
4082 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