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 QtConcurrent::run(this, &ConjunctionsTool::slotCompute);
79 });
80 connect(FilterTypeComboBox, SIGNAL(currentIndexChanged(int)), SLOT(slotFilterType(int)));
81 connect(ClearButton, SIGNAL(clicked()), this, SLOT(slotClear()));
82 connect(ExportButton, SIGNAL(clicked()), this, SLOT(slotExport()));
83 connect(OutputList, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(slotGoto()));
84 connect(ClearFilterButton, SIGNAL(clicked()), FilterEdit, SLOT(clear()));
85 connect(FilterEdit, SIGNAL(textChanged(QString)), this, SLOT(slotFilterReg(QString)));
86
87 m_Model = new QStandardItemModel(0, 5, this);
88
89 setMode(ModeSelector->currentIndex());
90
91 // Init filter type combobox
92 FilterTypeComboBox->clear();
93 FilterTypeComboBox->addItem(i18n("Single Object"));
94 FilterTypeComboBox->addItem(i18n("Any"));
95 FilterTypeComboBox->addItem(i18n("Stars"));
96 FilterTypeComboBox->addItem(i18n("Solar System"));
97 FilterTypeComboBox->addItem(i18n("Planets"));
98 FilterTypeComboBox->addItem(i18n("Comets"));
99 FilterTypeComboBox->addItem(i18n("Asteroids"));
100 FilterTypeComboBox->addItem(i18n("Open Clusters"));
101 FilterTypeComboBox->addItem(i18n("Globular Clusters"));
102 FilterTypeComboBox->addItem(i18n("Gaseous Nebulae"));
103 FilterTypeComboBox->addItem(i18n("Planetary Nebulae"));
104 FilterTypeComboBox->addItem(i18n("Galaxies"));
105
106 Obj2ComboBox->clear();
107 for (int i = 0; i < KSPlanetBase::UNKNOWN_PLANET; ++i)
108 {
109 // Obj1ComboBox->insertItem( i, pNames[i] );
110 Obj2ComboBox->insertItem(i, pNames[i]);
111 }
112
113 maxSeparationBox->setEnabled(true);
114
115 //Set up the Table Views
116 m_Model->setHorizontalHeaderLabels(QStringList() << i18n("Conjunction/Opposition") << i18n("Date & Time (UT)")
117 << i18n("Object 1") << i18n("Object 2") << i18n("Separation"));
118 m_SortModel = new QSortFilterProxyModel(this);
119 m_SortModel->setSourceModel(m_Model);
120 OutputList->setModel(m_SortModel);
121 OutputList->setSortingEnabled(true);
122 OutputList->horizontalHeader()->setStretchLastSection(true);
123 OutputList->horizontalHeader()->setSectionResizeMode(QHeaderView::Interactive);
124 OutputList->horizontalHeader()->resizeSection(2, 100);
125 OutputList->horizontalHeader()->resizeSection(3, 100);
126 OutputList->horizontalHeader()->resizeSection(4, 120); //is it bad way to fix default size of columns ?
127
128 show();
129}
130
131void ConjunctionsTool::slotGoto()
132{
133 int index = m_SortModel->mapToSource(OutputList->currentIndex()).row(); // Get the number of the line
134 long double jd = outputJDList.value(index);
137 KStarsData *data = KStarsData::Instance();
138 SkyMap *map = ks->map();
139
140 // Show conjunction
141 data->setLocation(*geoPlace);
142 dt.setDJD(jd);
143 data->changeDateTime(dt);
144 map->setClickedObject(data->skyComposite()->findByName(m_Model->data(m_Model->index(index, 2)).toString()));
145 map->setClickedPoint(map->clickedObject());
146 map->slotCenter();
147}
148
149void ConjunctionsTool::slotFindObject()
150{
151 if (FindDialog::Instance()->exec() == QDialog::Accepted)
152 {
153 if (!FindDialog::Instance()->targetObject())
154 return;
155 Object1 = SkyObject_s(FindDialog::Instance()->targetObject()->clone());
156 if (Object1 != nullptr)
157 Obj1FindButton->setText(Object1->name());
158 }
159}
160
161void ConjunctionsTool::setMode(int new_mode)
162{
163 // unlikely to happen
164 if(new_mode == -1 || new_mode > 2)
165 {
166 ModeSelector->setCurrentIndex(0);
167 return;
168 }
169
170 mode = static_cast<MODE>(new_mode);
171}
172
173void ConjunctionsTool::slotLocation()
174{
176 if (ld->exec() == QDialog::Accepted && ld)
177 {
178 geoPlace = ld->selectedCity();
179 LocationButton->setText(geoPlace->fullName());
180 }
181 delete ld;
182}
183
184void ConjunctionsTool::slotFilterType(int)
185{
186 // Disable find button if the user select an object type
187 if (FilterTypeComboBox->currentIndex() == 0)
188 Obj1FindButton->setEnabled(true);
189 else
190 Obj1FindButton->setEnabled(false);
191}
192
193void ConjunctionsTool::slotClear()
194{
195 m_Model->setRowCount(0);
196 outputJDList.clear();
197 m_index = 0;
198}
199
200void ConjunctionsTool::slotExport()
201{
202 int i, j;
203 QByteArray line;
204
205 //QFile file( KFileDialog::getSaveFileName( QDir::homePath(), "*|All files", this, "Save Conjunctions" ) );
206 QFile file(QFileDialog::getSaveFileName(nullptr, i18nc("@title:window", "Save Conjunctions"), QDir::homePath(), "*|All files"));
207
209
210 for (i = 0; i < m_Model->rowCount(); ++i)
211 {
212 for (j = 0; j < m_Model->columnCount(); ++j)
213 {
214 line.append(m_Model->data(m_Model->index(i, j)).toByteArray());
215 if (j < m_Model->columnCount() - 1)
216 line.append(";");
217 else
218 line.append("\n");
219 }
220 file.write(line);
221 line.clear();
222 }
223
224 file.close();
225}
226
227void ConjunctionsTool::slotFilterReg(const QString &filter)
228{
230 m_SortModel->setFilterKeyColumn(-1);
231}
232
233void ConjunctionsTool::slotCompute(void)
234{
235 KStarsDateTime dtStart(startDate->dateTime()); // Start date
236 KStarsDateTime dtStop(stopDate->dateTime()); // Stop date
237 long double startJD = dtStart.djd(); // Start julian day
238 long double stopJD = dtStop.djd(); // Stop julian day
239 bool opposition = false; // true=opposition, false=conjunction
240 if (mode == OPPOSITION)
241 opposition = true;
242 QStringList objects; // List of sky object used as Object1
243 KStarsData *data = KStarsData::Instance();
244 int progress = 0;
245
246 // Check if we have a valid angle in maxSeparationBox
247 dms maxSeparation(0.0);
248 bool ok;
249 maxSeparation = maxSeparationBox->createDms(&ok);
250
251 if (!ok)
252 {
253 KSNotification::sorry(i18n("Maximum separation entered is not a valid angle. Use the What's this help feature "
254 "for information on how to enter a valid angle"));
255 return;
256 }
257
258 // Check if Object1 and Object2 are set
259 if (FilterTypeComboBox->currentIndex() == 0 && Object1 == nullptr)
260 {
261 KSNotification::sorry(i18n("Please select an object to check conjunctions with, by clicking on the \'Find Object\' button."));
262 return;
263 }
264 Object2.reset(KSPlanetBase::createPlanet(Obj2ComboBox->currentIndex()));
265 if (FilterTypeComboBox->currentIndex() == 0 && Object1->name() == Object2->name())
266 {
267 // FIXME: Must free the created Objects
268 KSNotification::sorry(i18n("Please select two different objects to check conjunctions with."));
269 return;
270 }
271
272 // Init KSConjunct object
274 connect(&ksc, SIGNAL(madeProgress(int)), this, SLOT(showProgress(int)));
275 ksc.setGeoLocation(geoPlace);
276
277 switch (FilterTypeComboBox->currentIndex())
278 {
279 case 1: // All object types
280 foreach (int type, data->skyComposite()->objectNames().keys())
281 objects += data->skyComposite()->objectNames(type);
282 break;
283 case 2: // Stars
284 objects += data->skyComposite()->objectNames(SkyObject::STAR);
285 objects += data->skyComposite()->objectNames(SkyObject::CATALOG_STAR);
286 break;
287 case 3: // Solar system
288 objects += data->skyComposite()->objectNames(SkyObject::PLANET);
289 objects += data->skyComposite()->objectNames(SkyObject::COMET);
290 objects += data->skyComposite()->objectNames(SkyObject::ASTEROID);
291 objects += data->skyComposite()->objectNames(SkyObject::MOON);
292 objects += i18n("Sun");
293 // Remove Object2 planet
294 objects.removeAll(Object2->name());
295 break;
296 case 4: // Planet
297 objects += data->skyComposite()->objectNames(SkyObject::PLANET);
298 // Remove Object2 planet
299 objects.removeAll(Object2->name());
300 break;
301 case 5: // Comet
302 objects += data->skyComposite()->objectNames(SkyObject::COMET);
303 break;
304 case 6: // Asteroid
305 objects += data->skyComposite()->objectNames(SkyObject::ASTEROID);
306 break;
307 case 7: // Open Clusters
308 objects = data->skyComposite()->objectNames(SkyObject::OPEN_CLUSTER);
309 break;
310 case 8: // Open Clusters
311 objects = data->skyComposite()->objectNames(SkyObject::GLOBULAR_CLUSTER);
312 break;
313 case 9: // Gaseous nebulae
314 objects = data->skyComposite()->objectNames(SkyObject::GASEOUS_NEBULA);
315 break;
316 case 10: // Planetary nebula
317 objects = data->skyComposite()->objectNames(SkyObject::PLANETARY_NEBULA);
318 break;
319 case 11: // Galaxies
320 objects = data->skyComposite()->objectNames(SkyObject::GALAXY);
321 break;
322 }
323
324 // Remove all Jupiter and Saturn moons
325 // KStars crash if we compute a conjunction between a planet and one of this moon
326 if (FilterTypeComboBox->currentIndex() == 1 || FilterTypeComboBox->currentIndex() == 3 ||
327 FilterTypeComboBox->currentIndex() == 6)
328 {
329 objects.removeAll("Io");
330 objects.removeAll("Europa");
331 objects.removeAll("Ganymede");
332 objects.removeAll("Callisto");
333 objects.removeAll("Mimas");
334 objects.removeAll("Enceladus");
335 objects.removeAll("Tethys");
336 objects.removeAll("Dione");
337 objects.removeAll("Rhea");
338 objects.removeAll("Titan");
339 objects.removeAll("Hyperion");
340 objects.removeAll("Iapetus");
341 }
342
343 ksc.setMaxSeparation(maxSeparation);
344 ksc.setObject2(Object2);
345 ksc.setOpposition(opposition);
346
347 if (FilterTypeComboBox->currentIndex() != 0)
348 {
349 // Show a progress dialog while processing
350 QProgressDialog progressDlg(i18n("Compute conjunction..."), i18n("Abort"), 0, objects.count(), this);
351 progressDlg.setWindowTitle(i18nc("@title:window", "Conjunction"));
352 progressDlg.setWindowModality(Qt::WindowModal);
353 progressDlg.setValue(0);
354
355 for (auto &object : objects)
356 {
357 // If the user click on the 'cancel' button
358 if (progressDlg.wasCanceled())
359 break;
360
361 // Update progress dialog
362 ++progress;
363 progressDlg.setValue(progress);
364 progressDlg.setLabelText(i18n("Compute conjunction between %1 and %2", Object2->name(), object));
365
366 // Compute conjuction
367 Object1 = std::shared_ptr<SkyObject>(data->skyComposite()->findByName(object)->clone());
368 ksc.setObject1(Object1);
369 showConjunctions(ksc.findClosestApproach(startJD, stopJD),
370 object, Object2->name());
371 }
372
373 progressDlg.setValue(objects.count());
374 }
375 else
376 {
377 // Change cursor while we search for conjunction
379
380 ComputeStack->setCurrentIndex(1);
381
382 ksc.setObject1(Object1);
383 showConjunctions(ksc.findClosestApproach(startJD, stopJD),
384 Object1->name(), Object2->name());
385 ComputeStack->setCurrentIndex(0);
386
387 // Restore cursor
389 }
390
391 Object2.reset();
392}
393
394void ConjunctionsTool::showProgress(int n)
395{
396 progress->setValue(n);
397}
398
399void ConjunctionsTool::showConjunctions(const QMap<long double, dms> &conjunctionlist, const QString &object1,
400 const QString &object2)
401{
403 QList<QStandardItem *> itemList;
404
405 for (auto it = conjunctionlist.constBegin(); it != conjunctionlist.constEnd(); ++it)
406 {
407 dt.setDJD(it.key());
409
410 if (mode == CONJUNCTION)
411 typeItem = new QStandardItem(i18n("Conjunction"));
412 else
413 typeItem = new QStandardItem(i18n("Opposition"));
414
415 itemList << typeItem
416 //FIXME TODO is this ISO date? is there a ready format to use?
417 //<< new QStandardItem( QLocale().toString( dt.dateTime(), "YYYY-MM-DDTHH:mm:SS" ) )
418 //<< new QStandardItem( QLocale().toString( dt, Qt::ISODate) )
420 << new QStandardItem(object2) << new QStandardItem(it.value().toDMSString());
421 m_Model->appendRow(itemList);
422 itemList.clear();
423
424 outputJDList.insert(m_index, it.key());
425 ++m_index;
426 }
427}
428
429void ConjunctionsTool::setUpConjunctionOpposition()
430{
431
432}
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.
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
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...)
QAction * clear(const QObject *recvr, const char *slot, QObject *parent)
void clicked(bool checked)
QByteArray & append(char ch)
void clear()
void currentIndexChanged(int index)
QString toString(Qt::DateFormat format) const const
QString homePath()
QString getSaveFileName(QWidget *parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options)
void restoreOverrideCursor()
void setOverrideCursor(const QCursor &cursor)
QList< Key > keys() const const
QIcon fromTheme(const QString &name)
void clear()
QMap::iterator insert(const Key &key, const T &value)
const T value(const Key &key, const T &defaultValue) const const
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
void setFilterKeyColumn(int column)
virtual QModelIndex mapToSource(const QModelIndex &proxyIndex) const const override
void setFilterRegExp(const QString &pattern)
void appendRow(const QList< QStandardItem * > &items)
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)
int count() const const
CaseInsensitive
WaitCursor
WindowModal
QFuture< void > map(Sequence &sequence, MapFunctor function)
QFuture< T > run(Function function,...)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Sat Feb 24 2024 19:53:39 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.