Kstars

conjunctions.cpp
1/*
2 SPDX-FileCopyrightText: 2008 Akarsh Simha <akarshsimha@gmail.com>
3
4 SPDX-License-Identifier: GPL-2.0-or-later
5
6
7 Much of the code here is taken from Pablo de Vicente's
8 modcalcplanets.cpp
9
10*/
11
12#include "conjunctions.h"
13
14#include "geolocation.h"
15#include "ksconjunct.h"
16#include "kstars.h"
17#include "ksnotification.h"
18#include "kstarsdata.h"
19#include "skymap.h"
20#include "dialogs/finddialog.h"
21#include "dialogs/locationdialog.h"
22#include "skycomponents/skymapcomposite.h"
23#include "skyobjects/kscomet.h"
24#include "skyobjects/kspluto.h"
25#include "ksplanetbase.h"
26
27#include <QFileDialog>
28#include <QProgressDialog>
29#include <QStandardItemModel>
30#include <QtConcurrent>
31
32ConjunctionsTool::ConjunctionsTool(QWidget *parentSplit) : QFrame(parentSplit)
33{
34 setupUi(this);
35
36 KStarsData *kd = KStarsData::Instance();
38 KStarsDateTime dtStop(dtStart.djd() + 365.24); // TODO: Refine
39
40 //startDate -> setDateTime( dtStart.dateTime() );
41 //stopDate -> setDateTime( dtStop.dateTime() );
42 //TODO Check if this change works
43 startDate->setDateTime(dtStart);
44 stopDate->setDateTime(dtStop);
45
46 geoPlace = kd->geo();
47 LocationButton->setText(geoPlace->fullName());
48
49
50 pNames[KSPlanetBase::MERCURY] = i18n("Mercury");
51 pNames[KSPlanetBase::VENUS] = i18n("Venus");
52 pNames[KSPlanetBase::MARS] = i18n("Mars");
53 pNames[KSPlanetBase::JUPITER] = i18n("Jupiter");
54 pNames[KSPlanetBase::SATURN] = i18n("Saturn");
55 pNames[KSPlanetBase::URANUS] = i18n("Uranus");
56 pNames[KSPlanetBase::NEPTUNE] = i18n("Neptune");
57 //pNames[KSPlanetBase::PLUTO] = i18nc("Asteroid name (optional)", "Pluto");
58 pNames[KSPlanetBase::SUN] = i18n("Sun");
59 pNames[KSPlanetBase::MOON] = i18n("Moon");
60
61 // Initialize the Maximum Separation box to 1 degree
62 maxSeparationBox->setUnits(dmsBox::DEGREES);
63 maxSeparationBox->show(1.0_deg);
64
65 //FilterEdit->showClearButton = true;
66 ClearFilterButton->setIcon(QIcon::fromTheme("edit-clear"));
67
68 // signals and slots connections
69 connect(LocationButton, SIGNAL(clicked()), this, SLOT(slotLocation()));
70 connect(Obj1FindButton, SIGNAL(clicked()), this, SLOT(slotFindObject()));
71
72 // Mode Change
73 connect(ModeSelector, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &ConjunctionsTool::setMode);
74
75 //connect(ComputeButton, SIGNAL(clicked()), this, SLOT(slotCompute()));
76 connect(ComputeButton, &QPushButton::clicked, [this]()
77 {
78#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
79 QtConcurrent::run(&ConjunctionsTool::slotCompute, this);
80#else
81 QtConcurrent::run(this, &ConjunctionsTool::slotCompute);
82#endif
83 });
84 connect(FilterTypeComboBox, SIGNAL(currentIndexChanged(int)), SLOT(slotFilterType(int)));
85 connect(ClearButton, SIGNAL(clicked()), this, SLOT(slotClear()));
86 connect(ExportButton, SIGNAL(clicked()), this, SLOT(slotExport()));
87 connect(OutputList, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(slotGoto()));
88 connect(ClearFilterButton, SIGNAL(clicked()), FilterEdit, SLOT(clear()));
89 connect(FilterEdit, SIGNAL(textChanged(QString)), this, SLOT(slotFilterReg(QString)));
90
91 m_Model = new QStandardItemModel(0, 5, this);
92
93 setMode(ModeSelector->currentIndex());
94
95 // Init filter type combobox
96 FilterTypeComboBox->clear();
97 FilterTypeComboBox->addItem(i18n("Single Object"));
98 FilterTypeComboBox->addItem(i18n("Any"));
99 FilterTypeComboBox->addItem(i18n("Stars"));
100 FilterTypeComboBox->addItem(i18n("Solar System"));
101 FilterTypeComboBox->addItem(i18n("Planets"));
102 FilterTypeComboBox->addItem(i18n("Comets"));
103 FilterTypeComboBox->addItem(i18n("Asteroids"));
104 FilterTypeComboBox->addItem(i18n("Open Clusters"));
105 FilterTypeComboBox->addItem(i18n("Globular Clusters"));
106 FilterTypeComboBox->addItem(i18n("Gaseous Nebulae"));
107 FilterTypeComboBox->addItem(i18n("Planetary Nebulae"));
108 FilterTypeComboBox->addItem(i18n("Galaxies"));
109
110 Obj2ComboBox->clear();
111 for (int i = 0; i < KSPlanetBase::UNKNOWN_PLANET; ++i)
112 {
113 // Obj1ComboBox->insertItem( i, pNames[i] );
114 Obj2ComboBox->insertItem(i, pNames[i]);
115 }
116
117 maxSeparationBox->setEnabled(true);
118
119 //Set up the Table Views
120 m_Model->setHorizontalHeaderLabels(QStringList() << i18n("Conjunction/Opposition") << i18n("Date & Time (UT)")
121 << i18n("Object 1") << i18n("Object 2") << i18n("Separation"));
122 m_SortModel = new QSortFilterProxyModel(this);
123 m_SortModel->setSourceModel(m_Model);
124 OutputList->setModel(m_SortModel);
125 OutputList->setSortingEnabled(true);
126 OutputList->horizontalHeader()->setStretchLastSection(true);
127 OutputList->horizontalHeader()->setSectionResizeMode(QHeaderView::Interactive);
128 OutputList->horizontalHeader()->resizeSection(2, 100);
129 OutputList->horizontalHeader()->resizeSection(3, 100);
130 OutputList->horizontalHeader()->resizeSection(4, 120); //is it bad way to fix default size of columns ?
131
132 show();
133}
134
135void ConjunctionsTool::slotGoto()
136{
137 int index = m_SortModel->mapToSource(OutputList->currentIndex()).row(); // Get the number of the line
138 long double jd = outputJDList.value(index);
140 KStars *ks = KStars::Instance();
141 KStarsData *data = KStarsData::Instance();
142 SkyMap *map = ks->map();
143
144 // Show conjunction
145 data->setLocation(*geoPlace);
146 dt.setDJD(jd);
147 data->changeDateTime(dt);
148 map->setClickedObject(data->skyComposite()->findByName(m_Model->data(m_Model->index(index, 2)).toString()));
149 map->setClickedPoint(map->clickedObject());
150 map->slotCenter();
151}
152
153void ConjunctionsTool::slotFindObject()
154{
155 if (FindDialog::Instance()->exec() == QDialog::Accepted)
156 {
157 if (!FindDialog::Instance()->targetObject())
158 return;
159 Object1 = SkyObject_s(FindDialog::Instance()->targetObject()->clone());
160 if (Object1 != nullptr)
161 Obj1FindButton->setText(Object1->name());
162 }
163}
164
165void ConjunctionsTool::setMode(int new_mode)
166{
167 // unlikely to happen
168 if(new_mode == -1 || new_mode > 2)
169 {
170 ModeSelector->setCurrentIndex(0);
171 return;
172 }
173
174 mode = static_cast<MODE>(new_mode);
175}
176
177void ConjunctionsTool::slotLocation()
178{
180 if (ld->exec() == QDialog::Accepted && ld)
181 {
182 geoPlace = ld->selectedCity();
183 LocationButton->setText(geoPlace->fullName());
184 }
185 delete ld;
186}
187
188void ConjunctionsTool::slotFilterType(int)
189{
190 // Disable find button if the user select an object type
191 if (FilterTypeComboBox->currentIndex() == 0)
192 Obj1FindButton->setEnabled(true);
193 else
194 Obj1FindButton->setEnabled(false);
195}
196
197void ConjunctionsTool::slotClear()
198{
199 m_Model->setRowCount(0);
200 outputJDList.clear();
201 m_index = 0;
202}
203
204void ConjunctionsTool::slotExport()
205{
206 int i, j;
207 QByteArray line;
208
209 //QFile file( KFileDialog::getSaveFileName( QDir::homePath(), "*|All files", this, "Save Conjunctions" ) );
210 QFile file(QFileDialog::getSaveFileName(nullptr, i18nc("@title:window", "Save Conjunctions"), QDir::homePath(), "*|All files"));
211
213
214 for (i = 0; i < m_Model->rowCount(); ++i)
215 {
216 for (j = 0; j < m_Model->columnCount(); ++j)
217 {
218 line.append(m_Model->data(m_Model->index(i, j)).toByteArray());
219 if (j < m_Model->columnCount() - 1)
220 line.append(";");
221 else
222 line.append("\n");
223 }
224 file.write(line);
225 line.clear();
226 }
227
228 file.close();
229}
230
231void ConjunctionsTool::slotFilterReg(const QString &filter)
232{
234 m_SortModel->setFilterKeyColumn(-1);
235}
236
237void ConjunctionsTool::slotCompute(void)
238{
239 KStarsDateTime dtStart(startDate->dateTime()); // Start date
240 KStarsDateTime dtStop(stopDate->dateTime()); // Stop date
241 long double startJD = dtStart.djd(); // Start julian day
242 long double stopJD = dtStop.djd(); // Stop julian day
243 bool opposition = false; // true=opposition, false=conjunction
244 if (mode == OPPOSITION)
245 opposition = true;
246 QStringList objects; // List of sky object used as Object1
247 KStarsData *data = KStarsData::Instance();
248 int progress = 0;
249
250 // Check if we have a valid angle in maxSeparationBox
251 dms maxSeparation(0.0);
252 bool ok;
253 maxSeparation = maxSeparationBox->createDms(&ok);
254
255 if (!ok)
256 {
257 KSNotification::sorry(i18n("Maximum separation entered is not a valid angle. Use the What's this help feature "
258 "for information on how to enter a valid angle"));
259 return;
260 }
261
262 // Check if Object1 and Object2 are set
263 if (FilterTypeComboBox->currentIndex() == 0 && Object1 == nullptr)
264 {
265 KSNotification::sorry(i18n("Please select an object to check conjunctions with, by clicking on the \'Find Object\' button."));
266 return;
267 }
268 Object2.reset(KSPlanetBase::createPlanet(Obj2ComboBox->currentIndex()));
269 if (FilterTypeComboBox->currentIndex() == 0 && Object1->name() == Object2->name())
270 {
271 // FIXME: Must free the created Objects
272 KSNotification::sorry(i18n("Please select two different objects to check conjunctions with."));
273 return;
274 }
275
276 // Init KSConjunct object
277 KSConjunct ksc;
278 connect(&ksc, SIGNAL(madeProgress(int)), this, SLOT(showProgress(int)));
279 ksc.setGeoLocation(geoPlace);
280
281 switch (FilterTypeComboBox->currentIndex())
282 {
283 case 1: // All object types
284 foreach (int type, data->skyComposite()->objectNames().keys())
285 objects += data->skyComposite()->objectNames(type);
286 break;
287 case 2: // Stars
288 objects += data->skyComposite()->objectNames(SkyObject::STAR);
289 objects += data->skyComposite()->objectNames(SkyObject::CATALOG_STAR);
290 break;
291 case 3: // Solar system
292 objects += data->skyComposite()->objectNames(SkyObject::PLANET);
293 objects += data->skyComposite()->objectNames(SkyObject::COMET);
294 objects += data->skyComposite()->objectNames(SkyObject::ASTEROID);
295 objects += data->skyComposite()->objectNames(SkyObject::MOON);
296 objects += i18n("Sun");
297 // Remove Object2 planet
298 objects.removeAll(Object2->name());
299 break;
300 case 4: // Planet
301 objects += data->skyComposite()->objectNames(SkyObject::PLANET);
302 // Remove Object2 planet
303 objects.removeAll(Object2->name());
304 break;
305 case 5: // Comet
306 objects += data->skyComposite()->objectNames(SkyObject::COMET);
307 break;
308 case 6: // Asteroid
309 objects += data->skyComposite()->objectNames(SkyObject::ASTEROID);
310 break;
311 case 7: // Open Clusters
312 objects = data->skyComposite()->objectNames(SkyObject::OPEN_CLUSTER);
313 break;
314 case 8: // Open Clusters
315 objects = data->skyComposite()->objectNames(SkyObject::GLOBULAR_CLUSTER);
316 break;
317 case 9: // Gaseous nebulae
318 objects = data->skyComposite()->objectNames(SkyObject::GASEOUS_NEBULA);
319 break;
320 case 10: // Planetary nebula
321 objects = data->skyComposite()->objectNames(SkyObject::PLANETARY_NEBULA);
322 break;
323 case 11: // Galaxies
324 objects = data->skyComposite()->objectNames(SkyObject::GALAXY);
325 break;
326 }
327
328 // Remove all Jupiter and Saturn moons
329 // KStars crash if we compute a conjunction between a planet and one of this moon
330 if (FilterTypeComboBox->currentIndex() == 1 || FilterTypeComboBox->currentIndex() == 3 ||
331 FilterTypeComboBox->currentIndex() == 6)
332 {
333 objects.removeAll("Io");
334 objects.removeAll("Europa");
335 objects.removeAll("Ganymede");
336 objects.removeAll("Callisto");
337 objects.removeAll("Mimas");
338 objects.removeAll("Enceladus");
339 objects.removeAll("Tethys");
340 objects.removeAll("Dione");
341 objects.removeAll("Rhea");
342 objects.removeAll("Titan");
343 objects.removeAll("Hyperion");
344 objects.removeAll("Iapetus");
345 }
346
347 ksc.setMaxSeparation(maxSeparation);
348 ksc.setObject2(Object2);
349 ksc.setOpposition(opposition);
350
351 if (FilterTypeComboBox->currentIndex() != 0)
352 {
353 // Show a progress dialog while processing
354 QProgressDialog progressDlg(i18n("Compute conjunction..."), i18n("Abort"), 0, objects.count(), this);
355 progressDlg.setWindowTitle(i18nc("@title:window", "Conjunction"));
356 progressDlg.setWindowModality(Qt::WindowModal);
357 progressDlg.setValue(0);
358
359 for (auto &object : objects)
360 {
361 // If the user click on the 'cancel' button
362 if (progressDlg.wasCanceled())
363 break;
364
365 // Update progress dialog
366 ++progress;
367 progressDlg.setValue(progress);
368 progressDlg.setLabelText(i18n("Compute conjunction between %1 and %2", Object2->name(), object));
369
370 // Compute conjuction
371 Object1 = std::shared_ptr<SkyObject>(data->skyComposite()->findByName(object)->clone());
372 ksc.setObject1(Object1);
373 showConjunctions(ksc.findClosestApproach(startJD, stopJD),
374 object, Object2->name());
375 }
376
377 progressDlg.setValue(objects.count());
378 }
379 else
380 {
381 // Change cursor while we search for conjunction
383
384 ComputeStack->setCurrentIndex(1);
385
386 ksc.setObject1(Object1);
387 showConjunctions(ksc.findClosestApproach(startJD, stopJD),
388 Object1->name(), Object2->name());
389 ComputeStack->setCurrentIndex(0);
390
391 // Restore cursor
393 }
394
395 Object2.reset();
396}
397
398void ConjunctionsTool::showProgress(int n)
399{
400 progress->setValue(n);
401}
402
403void ConjunctionsTool::showConjunctions(const QMap<long double, dms> &conjunctionlist, const QString &object1,
404 const QString &object2)
405{
407 QList<QStandardItem *> itemList;
408
409 for (auto it = conjunctionlist.constBegin(); it != conjunctionlist.constEnd(); ++it)
410 {
411 dt.setDJD(it.key());
412 QStandardItem *typeItem;
413
414 if (mode == CONJUNCTION)
415 typeItem = new QStandardItem(i18n("Conjunction"));
416 else
417 typeItem = new QStandardItem(i18n("Opposition"));
418
419 itemList << typeItem
420 //FIXME TODO is this ISO date? is there a ready format to use?
421 //<< new QStandardItem( QLocale().toString( dt.dateTime(), "YYYY-MM-DDTHH:mm:SS" ) )
422 //<< new QStandardItem( QLocale().toString( dt, Qt::ISODate) )
423 << new QStandardItem(dt.toString(Qt::ISODate)) << new QStandardItem(object1)
424 << new QStandardItem(object2) << new QStandardItem(it.value().toDMSString());
425 m_Model->appendRow(itemList);
426 itemList.clear();
427
428 outputJDList.insert(m_index, it.key());
429 ++m_index;
430 }
431}
432
433void ConjunctionsTool::setUpConjunctionOpposition()
434{
435
436}
void setMaxSeparation(double sep)
setMaxSeparation
QMap< long double, dms > findClosestApproach(long double startJD, long double stopJD, const std::function< void(long double, dms)> &callback={})
Compute the closest approach of two planets in the given range.
void setGeoLocation(GeoLocation *geo)
Sets the geographic location to compute conjunctions at.
QString fullName() const
Implements algorithms to find close conjunctions of planets in a given time range.
Definition ksconjunct.h:25
KStarsData is the backbone of KStars.
Definition kstarsdata.h:72
void setLocation(const GeoLocation &l)
Set the GeoLocation according to the argument.
void changeDateTime(const KStarsDateTime &newDate)
Change the current simulation date/time to the KStarsDateTime argument.
GeoLocation * geo()
Definition kstarsdata.h:230
SkyMapComposite * skyComposite()
Definition kstarsdata.h:166
Extension of QDateTime for KStars KStarsDateTime can represent the date/time as a Julian Day,...
void setDJD(long double jd)
Assign the static_cast<long double> Julian Day value, which includes the time of day encoded in the f...
static KStarsDateTime currentDateTime()
This is the main window for KStars.
Definition kstars.h:91
SkyMap * map() const
Definition kstars.h:141
static KStars * Instance()
Definition kstars.h:123
Dialog for changing the geographic location of the observer.
SkyObject * findByName(const QString &name, bool exact=true) override
Search the children of this SkyMapComposite for a SkyObject whose name matches the argument.
This is the canvas on which the sky is painted.
Definition skymap.h:54
virtual SkyObject * clone() const
Create copy of object.
Definition skyobject.cpp:50
An angle, stored as degrees, but expressible in many ways.
Definition dms.h:38
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
KGuiItem clear()
void clicked(bool checked)
QByteArray & append(QByteArrayView data)
void clear()
void currentIndexChanged(int index)
QString toString(QStringView format, QCalendar cal) const const
QString homePath()
QString getSaveFileName(QWidget *parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, Options options)
void restoreOverrideCursor()
void setOverrideCursor(const QCursor &cursor)
QList< Key > keys() const const
QIcon fromTheme(const QString &name)
void clear()
void clear()
const_iterator constBegin() const const
const_iterator constEnd() const const
iterator insert(const Key &key, const T &value)
T value(const Key &key, const T &defaultValue) const const
int row() const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
void setFilterKeyColumn(int column)
void setFilterRegularExpression(const QRegularExpression &regularExpression)
virtual QModelIndex mapToSource(const QModelIndex &proxyIndex) const const override
void appendRow(QStandardItem *item)
virtual int columnCount(const QModelIndex &parent) const const override
virtual QVariant data(const QModelIndex &index, int role) const const override
virtual QModelIndex index(int row, int column, const QModelIndex &parent) const const override
virtual int rowCount(const QModelIndex &parent) const const override
void setRowCount(int rows)
qsizetype count() const const
WaitCursor
WindowModal
QFuture< void > map(Iterator begin, Iterator end, MapFunctor &&function)
QFuture< T > run(Function function,...)
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
QByteArray toByteArray() const const
QString toString() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Oct 11 2024 12:15:13 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.