1 /*
2 SPDX-FileCopyrightText: 2005 Jason Harris <jharris@30doradus.org>
3
4 SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
7 #include "obslistwizard.h"
8 #include "Options.h"
9
10 #include "geolocation.h"
11 #include "kstarsdata.h"
12 #include "dialogs/locationdialog.h"
13 #include "skycomponents/constellationboundarylines.h"
14 #include "skycomponents/catalogscomponent.h"
15 #include "skycomponents/skymapcomposite.h"
16 #include "catalogobject.h"
17 #include "catalogsdb.h"
18
ObsListWizardUI(QWidget * p)19 ObsListWizardUI::ObsListWizardUI(QWidget *p) : QFrame(p)
20 {
21 setupUi(this);
22 }
23
ObsListWizard(QWidget * ksparent)24 ObsListWizard::ObsListWizard(QWidget *ksparent) : QDialog(ksparent)
25 {
26 #ifdef Q_OS_OSX
27 setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint);
28 #endif
29 olw = new ObsListWizardUI(this);
30 QVBoxLayout *mainLayout = new QVBoxLayout;
31 mainLayout->addWidget(olw);
32 setLayout(mainLayout);
33
34 setWindowTitle(i18nc("@title:window", "Observing List Wizard"));
35
36 QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal);
37 nextB = new QPushButton(i18n("&Next >"));
38 nextB->setDefault(true);
39 backB = new QPushButton(i18n("< &Back"));
40 backB->setEnabled(false);
41
42 buttonBox->addButton(backB, QDialogButtonBox::ActionRole);
43 buttonBox->addButton(nextB, QDialogButtonBox::ActionRole);
44 mainLayout->addWidget(buttonBox);
45
46 connect(nextB, SIGNAL(clicked()), this, SLOT(slotNextPage()));
47 connect(backB, SIGNAL(clicked()), this, SLOT(slotPrevPage()));
48 connect(buttonBox, SIGNAL(accepted()), this, SLOT(slotApplyFilters()));
49 connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
50 connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
51
52 connect(olw->AllButton, SIGNAL(clicked()), this, SLOT(slotAllButton()));
53 connect(olw->NoneButton, SIGNAL(clicked()), this, SLOT(slotNoneButton()));
54 connect(olw->DeepSkyButton, SIGNAL(clicked()), this, SLOT(slotDeepSkyButton()));
55 connect(olw->SolarSystemButton, SIGNAL(clicked()), this, SLOT(slotSolarSystemButton()));
56 connect(olw->LocationButton, SIGNAL(clicked()), this, SLOT(slotChangeLocation()));
57
58 //Update the count of objects when the user asks for it
59 connect(olw->updateButton, SIGNAL(clicked()), this, SLOT(slotUpdateObjectCount()));
60
61 // Enable the update count button when certain elements are changed
62 connect(olw->TypeList, &QListWidget::itemSelectionChanged, this, &ObsListWizard::slotObjectCountDirty);
63 connect(olw->ConstellationList, &QListWidget::itemSelectionChanged, this, &ObsListWizard::slotObjectCountDirty);
64 connect(olw->RAMin, &QLineEdit::editingFinished, this, &ObsListWizard::slotParseRegion);
65 connect(olw->RAMax, &QLineEdit::editingFinished, this, &ObsListWizard::slotParseRegion);
66 connect(olw->DecMin, &QLineEdit::editingFinished, this, &ObsListWizard::slotParseRegion);
67 connect(olw->DecMax, &QLineEdit::editingFinished, this, &ObsListWizard::slotParseRegion);
68 connect(olw->RA, &QLineEdit::editingFinished, this, &ObsListWizard::slotParseRegion);
69 connect(olw->Dec, &QLineEdit::editingFinished, this, &ObsListWizard::slotParseRegion);
70 connect(olw->Radius, &QLineEdit::editingFinished, this, &ObsListWizard::slotObjectCountDirty);
71 connect(olw->Date, &QDateEdit::dateChanged, this, &ObsListWizard::slotObjectCountDirty);
72 connect(olw->Mag, static_cast<void(QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged), this,
73 &ObsListWizard::slotObjectCountDirty);
74 connect(olw->IncludeNoMag, &QPushButton::clicked, this, &ObsListWizard::slotObjectCountDirty);
75 connect(olw->timeTo, &QTimeEdit::timeChanged, this, &ObsListWizard::slotObjectCountDirty);
76 connect(olw->timeFrom, &QTimeEdit::timeChanged, this, &ObsListWizard::slotObjectCountDirty);
77 connect(olw->minAlt, static_cast<void(QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged), this,
78 &ObsListWizard::slotObjectCountDirty);
79 connect(olw->maxAlt, static_cast<void(QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged), this,
80 &ObsListWizard::slotObjectCountDirty);
81
82 olw->coverage->setValue(Options::obsListCoverage());
83 connect(olw->coverage, static_cast<void(QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged), [&](double value)
84 {
85 Options::setObsListCoverage(value);
86 slotObjectCountDirty();
87 });
88
89 connect(olw->SelectByDate, SIGNAL(clicked()), this, SLOT(slotToggleDateWidgets()));
90 connect(olw->SelectByMagnitude, SIGNAL(clicked()), this, SLOT(slotToggleMagWidgets()));
91
92 geo = KStarsData::Instance()->geo();
93 olw->LocationButton->setText(geo->fullName());
94 olw->Date->setDate(KStarsDateTime::currentDateTime().date());
95 olw->timeFrom->setTime(QTime(18, 0));
96 olw->timeTo->setTime(QTime(23, 59));
97
98 initialize();
99 }
100
initialize()101 void ObsListWizard::initialize()
102 {
103 KStarsData *data = KStarsData::Instance();
104 olw->olwStack->setCurrentIndex(0);
105
106 //Populate the list of constellations
107 foreach (SkyObject *p, data->skyComposite()->constellationNames())
108 olw->ConstellationList->addItem(p->name());
109
110 //unSelect all object types
111 olw->TypeList->clearSelection();
112
113 olw->Mag->setMinimum(-5.0);
114 olw->Mag->setMaximum(20.0);
115 olw->Mag->setValue(6.0);
116
117 olw->RA->setDegType(false);
118 olw->RAMin->setDegType(false);
119 olw->RAMax->setDegType(false);
120
121 //Initialize object counts
122 ObjectCount = 0; //number of objects in observing list
123 StarCount = data->skyComposite()->stars().size(); //total number of stars
124 PlanetCount = 10; //Sun, Moon, 8 planets
125 AsteroidCount = data->skyComposite()->asteroids().size(); //total number of asteroids
126 CometCount = data->skyComposite()->comets().size(); //total number of comets
127 //DeepSkyObjects
128 OpenClusterCount = 0;
129 GlobClusterCount = 0;
130 GasNebCount = 0;
131 PlanNebCount = 0;
132 GalaxyCount = 0;
133
134 CatalogsDB::DBManager manager{ CatalogsDB::dso_db_path() };
135
136 const auto &stats{ manager.get_master_statistics() };
137 if (!stats.first)
138 return;
139
140 for (const auto &element : stats.second.object_counts)
141 {
142 auto cnt = element.second;
143 switch (element.first)
144 {
145 case SkyObject::GALAXY:
146 GalaxyCount += cnt;
147 break;
148 case SkyObject::STAR:
149 case SkyObject::CATALOG_STAR:
150 StarCount += cnt;
151 break;
152 case SkyObject::OPEN_CLUSTER:
153 OpenClusterCount += cnt;
154 break;
155 case SkyObject::GLOBULAR_CLUSTER:
156 GlobClusterCount += cnt;
157 break;
158 case SkyObject::GASEOUS_NEBULA:
159 case SkyObject::SUPERNOVA_REMNANT:
160 GasNebCount += cnt;
161 break;
162 case SkyObject::PLANETARY_NEBULA:
163 PlanNebCount += cnt;
164 break;
165 default:
166 break;
167 }
168 }
169 }
170
isItemSelected(const QString & name,QListWidget * listWidget,bool * ok)171 bool ObsListWizard::isItemSelected(const QString &name, QListWidget *listWidget, bool *ok)
172 {
173 /*QList<QListWidgetItem *> items = listWidget->findItems(name, Qt::MatchContains);
174 if (ok)
175 *ok = items.size();
176 return items.size() && listWidget->isItemSelected(items[0]);
177 ;*/
178 foreach(QListWidgetItem *item, listWidget->selectedItems())
179 {
180 if (item->text().compare(name, Qt::CaseInsensitive) == 0)
181 {
182 if (ok)
183 *ok = true;
184 return true;
185 }
186 }
187
188 if (ok)
189 *ok = false;
190 return false;
191 }
192
setItemSelected(const QString & name,QListWidget * listWidget,bool value,bool * ok)193 void ObsListWizard::setItemSelected(const QString &name, QListWidget *listWidget, bool value, bool *ok)
194 {
195 QList<QListWidgetItem *> items = listWidget->findItems(name, Qt::MatchContains);
196 if (ok)
197 *ok = items.size();
198 if (items.size())
199 items[0]->setSelected(value);
200 }
201
202 //Advance to the next page in the stack. However, on page 2 the user
203 //selects what regional filter they want to use, and this determines
204 //what the page following page 2 should be:
205 // + Constellation(s): the next page index is 3
206 // + Rectangular region: the next page index is 4
207 // + Circular region: the next page index is 5
208 // + No region selected (a.k.a. "all over the sky"): the next page index is 6
209 //
210 //Also, if the current page index is 3 or 4, then the next page should be 6.
211 //
212 //NOTE: the page indexes are hard-coded here, which isn't ideal. However,
213 //There's no easy way to access the pointers of widgets in the stack
214 //if you didn't save them at the start.
slotNextPage()215 void ObsListWizard::slotNextPage()
216 {
217 int NextPage = olw->olwStack->currentIndex() + 1;
218
219 if (olw->olwStack->currentIndex() == 2)
220 {
221 //On the Region select page. Determine what
222 //the next page index should be.
223 //No need to handle "by constellation, it's already currentIndex + 1.
224 if (isItemSelected(i18n("in a rectangular region"), olw->RegionList))
225 NextPage = 4;
226 if (isItemSelected(i18n("in a circular region"), olw->RegionList))
227 NextPage = 5;
228 if (isItemSelected(i18n("all over the sky"), olw->RegionList))
229 NextPage = 6;
230 }
231
232 if (olw->olwStack->currentIndex() == 3 || olw->olwStack->currentIndex() == 4)
233 NextPage = 6;
234
235 olw->olwStack->setCurrentIndex(NextPage);
236
237 if (olw->olwStack->currentIndex() == olw->olwStack->count() - 1)
238 nextB->setEnabled(false);
239
240 backB->setEnabled(true);
241 }
242
243 //Advance to the previous page in the stack. However, because the
244 //path through the wizard branches depending on the user's choice of
245 //Region filter, the previous page is not always currentPage-1.
246 //Specifically, if the current page index is 4, 5, or 6, then the Previous
247 //page index should be 2 rather than currentIndex-1.
slotPrevPage()248 void ObsListWizard::slotPrevPage()
249 {
250 int PrevPage = olw->olwStack->currentIndex() - 1;
251
252 if (olw->olwStack->currentIndex() == 4 || olw->olwStack->currentIndex() == 5 || olw->olwStack->currentIndex() == 6)
253 PrevPage = 2;
254
255 olw->olwStack->setCurrentIndex(PrevPage);
256
257 if (olw->olwStack->currentIndex() == 0)
258 backB->setEnabled(false);
259
260 nextB->setEnabled(true);
261 }
262
slotAllButton()263 void ObsListWizard::slotAllButton()
264 {
265 for (int i = 0; i < olw->TypeList->count(); ++i)
266 olw->TypeList->item(i)->setSelected(true);
267 }
268
slotNoneButton()269 void ObsListWizard::slotNoneButton()
270 {
271 olw->TypeList->clearSelection();
272 }
273
slotDeepSkyButton()274 void ObsListWizard::slotDeepSkyButton()
275 {
276 olw->TypeList->clearSelection();
277 setItemSelected(i18n("Open clusters"), olw->TypeList, true);
278 setItemSelected(i18n("Globular clusters"), olw->TypeList, true);
279 setItemSelected(i18n("Gaseous nebulae"), olw->TypeList, true);
280 setItemSelected(i18n("Planetary nebulae"), olw->TypeList, true);
281 setItemSelected(i18n("Galaxies"), olw->TypeList, true);
282 }
283
slotSolarSystemButton()284 void ObsListWizard::slotSolarSystemButton()
285 {
286 olw->TypeList->clearSelection();
287 setItemSelected(i18n("Sun, moon, planets"), olw->TypeList, true);
288 setItemSelected(i18n("Comets"), olw->TypeList, true);
289 setItemSelected(i18n("Asteroids"), olw->TypeList, true);
290 }
291
slotChangeLocation()292 void ObsListWizard::slotChangeLocation()
293 {
294 QPointer<LocationDialog> ld = new LocationDialog(this);
295
296 if (ld->exec() == QDialog::Accepted)
297 {
298 //set geographic location
299 if (ld->selectedCity())
300 {
301 geo = ld->selectedCity();
302 olw->LocationButton->setText(geo->fullName());
303 }
304 }
305 delete ld;
306 }
307
slotToggleDateWidgets()308 void ObsListWizard::slotToggleDateWidgets()
309 {
310 olw->Date->setEnabled(olw->SelectByDate->isChecked());
311 olw->LocationButton->setEnabled(olw->SelectByDate->isChecked());
312 olw->timeTo->setEnabled(olw->SelectByDate->isChecked());
313 olw->timeFrom->setEnabled(olw->SelectByDate->isChecked());
314 olw->minAlt->setEnabled(olw->SelectByDate->isChecked());
315 olw->maxAlt->setEnabled(olw->SelectByDate->isChecked());
316
317 // slotUpdateObjectCount();
318 slotObjectCountDirty();
319 }
320
slotToggleMagWidgets()321 void ObsListWizard::slotToggleMagWidgets()
322 {
323 olw->Mag->setEnabled(olw->SelectByMagnitude->isChecked());
324 olw->IncludeNoMag->setEnabled(olw->SelectByMagnitude->isChecked());
325 slotObjectCountDirty();
326 // slotUpdateObjectCount();
327 }
328
slotParseRegion()329 void ObsListWizard::slotParseRegion()
330 {
331 if (sender()->objectName() == "RAMin" || sender()->objectName() == "RAMax" || sender()->objectName() == "DecMin" ||
332 sender()->objectName() == "DecMax")
333 {
334 if (!olw->RAMin->isEmpty() && !olw->RAMax->isEmpty() && !olw->DecMin->isEmpty() && !olw->DecMax->isEmpty())
335 {
336 bool rectOk = false;
337 xRect1 = 0.0;
338 xRect2 = 0.0;
339 yRect1 = 0.0;
340 yRect2 = 0.0;
341
342 xRect1 = olw->RAMin->createDms(false, &rectOk).Hours();
343 if (rectOk)
344 xRect2 = olw->RAMax->createDms(false, &rectOk).Hours();
345 if (rectOk)
346 yRect1 = olw->DecMin->createDms(true, &rectOk).Degrees();
347 if (rectOk)
348 yRect2 = olw->DecMax->createDms(true, &rectOk).Degrees();
349 if (xRect2 == 0.0)
350 xRect2 = 24.0;
351
352 if (!rectOk)
353 {
354 // qWarning() << i18n( "Illegal rectangle specified, no region selection possible." ) ;
355 return;
356 }
357
358 //Make sure yRect1 < yRect2.
359 if (yRect1 > yRect2)
360 {
361 double temp = yRect2;
362 yRect2 = yRect1;
363 yRect1 = temp;
364 }
365
366 //If xRect1 > xRect2, we may need to swap the two values, or subtract 24h from xRect1.
367 if (xRect1 > xRect2)
368 {
369 if (xRect1 - xRect2 > 12.0) //the user probably wants a region that straddles 0h
370 {
371 xRect1 -= 24.0;
372 }
373 else //the user probably wants xRect2 to be the lower limit
374 {
375 double temp = xRect2;
376 xRect2 = xRect1;
377 xRect1 = temp;
378 }
379 }
380
381 // slotUpdateObjectCount();
382 slotObjectCountDirty();
383 }
384 }
385 else if (!olw->RA->isEmpty() && !olw->Dec->isEmpty() && !olw->Radius->isEmpty())
386 {
387 bool circOk;
388 dms ra = olw->RA->createDms(false, &circOk);
389 dms dc;
390 if (circOk)
391 dc = olw->Dec->createDms(true, &circOk);
392 if (circOk)
393 {
394 pCirc.set(ra, dc);
395 rCirc = olw->Radius->createDms(true, &circOk).Degrees();
396 }
397 else
398 {
399 qWarning() << i18n("Illegal circle specified, no region selection possible.");
400 return;
401 }
402 // slotUpdateObjectCount();
403 slotObjectCountDirty();
404 }
405 }
406
slotObjectCountDirty()407 void ObsListWizard::slotObjectCountDirty()
408 {
409 olw->updateButton->setDisabled(false);
410 }
411
slotUpdateObjectCount()412 void ObsListWizard::slotUpdateObjectCount()
413 {
414 QApplication::setOverrideCursor(Qt::WaitCursor);
415 ObjectCount = 0;
416 if (isItemSelected(i18n("Stars"), olw->TypeList))
417 ObjectCount += StarCount;
418 if (isItemSelected(i18n("Sun, moon, planets"), olw->TypeList))
419 ObjectCount += PlanetCount;
420 if (isItemSelected(i18n("Comets"), olw->TypeList))
421 ObjectCount += CometCount;
422 if (isItemSelected(i18n("Asteroids"), olw->TypeList))
423 ObjectCount += AsteroidCount;
424 if (isItemSelected(i18n("Galaxies"), olw->TypeList))
425 ObjectCount += GalaxyCount;
426 if (isItemSelected(i18n("Open clusters"), olw->TypeList))
427 ObjectCount += OpenClusterCount;
428 if (isItemSelected(i18n("Globular clusters"), olw->TypeList))
429 ObjectCount += GlobClusterCount;
430 if (isItemSelected(i18n("Gaseous nebulae"), olw->TypeList))
431 ObjectCount += GasNebCount;
432 if (isItemSelected(i18n("Planetary nebulae"), olw->TypeList))
433 ObjectCount += PlanNebCount;
434
435 applyFilters(false); //false = only adjust counts, do not build list
436 QApplication::restoreOverrideCursor();
437 olw->updateButton->setDisabled(true);
438 }
439
applyFilters(bool doBuildList)440 void ObsListWizard::applyFilters(bool doBuildList)
441 {
442 bool filterPass = true;
443 KStarsData *data = KStarsData::Instance();
444 if (doBuildList)
445 obsList().clear();
446
447 //We don't need to call applyRegionFilter() if no region filter is selected, *and*
448 //we are just counting items (i.e., doBuildList is false)
449 bool needRegion = true;
450 if (!doBuildList && isItemSelected(i18n("all over the sky"), olw->RegionList))
451 needRegion = false;
452
453 double maglimit = 100.;
454 if (olw->SelectByMagnitude->isChecked())
455 maglimit = olw->Mag->value();
456
457 //Stars
458 if (isItemSelected(i18n("Stars"), olw->TypeList))
459 {
460 const QList<SkyObject *> &starList = data->skyComposite()->stars();
461 int starIndex(starList.size());
462 if (maglimit < 100.)
463 {
464 //Stars are sorted by mag, so use binary search algo to find index of faintest mag
465 int low(0), high(starList.size() - 1), middle(high);
466 while (low < high)
467 {
468 middle = (low + high) / 2;
469 if (maglimit == starList.at(middle)->mag())
470 break;
471 if (maglimit < starList.at(middle)->mag())
472 high = middle - 1;
473 if (maglimit > starList.at(middle)->mag())
474 low = middle + 1;
475 }
476 //now, the star at "middle" has the right mag, but we want the *last* star that has this mag.
477 for (starIndex = middle + 1; starIndex < starList.size(); ++starIndex)
478 {
479 if (starList.at(starIndex)->mag() > maglimit)
480 break;
481 }
482 }
483
484 //DEBUG
485 qDebug() << QString("starIndex for mag %1: %2").arg(maglimit).arg(starIndex);
486
487 if (!doBuildList)
488 {
489 //reduce StarCount by appropriate amount
490 ObjectCount -= StarCount;
491 ObjectCount += starIndex;
492 }
493 for (int i = 0; i < starIndex; ++i)
494 {
495 SkyObject *o = (SkyObject *)(starList[i]);
496
497 // JM 2012-10-22: Skip unnamed stars
498 if (o->name() == "star")
499 {
500 if (!doBuildList)
501 --ObjectCount;
502 continue;
503 }
504
505 if (needRegion)
506 filterPass = applyRegionFilter(o, doBuildList, !doBuildList);
507 //Filter objects visible from geo at Date if region filter passes
508 if (olw->SelectByDate->isChecked() && filterPass)
509 applyObservableFilter(o, doBuildList, !doBuildList);
510 }
511 }
512
513 //Sun, Moon, Planets
514 if (isItemSelected(i18n("Sun, moon, planets"), olw->TypeList))
515 {
516 if (maglimit < data->skyComposite()->findByName(i18n("Sun"))->mag())
517 {
518 if (!doBuildList)
519 --ObjectCount;
520 filterPass = false;
521 }
522 else
523 filterPass = true;
524
525 if (needRegion && filterPass)
526 filterPass = applyRegionFilter(data->skyComposite()->findByName(i18n("Sun")), doBuildList);
527 if (olw->SelectByDate->isChecked() && filterPass)
528 applyObservableFilter(data->skyComposite()->findByName(i18n("Sun")), doBuildList);
529
530 if (maglimit < data->skyComposite()->findByName(i18n("Moon"))->mag())
531 {
532 if (!doBuildList)
533 --ObjectCount;
534 filterPass = false;
535 }
536 else
537 filterPass = true;
538
539 if (needRegion && filterPass)
540 filterPass = applyRegionFilter(data->skyComposite()->findByName(i18n("Moon")), doBuildList);
541 if (olw->SelectByDate->isChecked() && filterPass)
542 applyObservableFilter(data->skyComposite()->findByName(i18n("Moon")), doBuildList);
543
544 if (maglimit < data->skyComposite()->findByName(i18n("Mercury"))->mag())
545 {
546 if (!doBuildList)
547 --ObjectCount;
548 filterPass = false;
549 }
550 else
551 filterPass = true;
552 if (needRegion && filterPass)
553 filterPass = applyRegionFilter(data->skyComposite()->findByName(i18n("Mercury")), doBuildList);
554 if (olw->SelectByDate->isChecked() && filterPass)
555 applyObservableFilter(data->skyComposite()->findByName(i18n("Mercury")), doBuildList);
556
557 if (maglimit < data->skyComposite()->findByName(i18n("Venus"))->mag())
558 {
559 if (!doBuildList)
560 --ObjectCount;
561 filterPass = false;
562 }
563 else
564 filterPass = true;
565
566 if (needRegion && filterPass)
567 filterPass = applyRegionFilter(data->skyComposite()->findByName(i18n("Venus")), doBuildList);
568 if (olw->SelectByDate->isChecked() && filterPass)
569 applyObservableFilter(data->skyComposite()->findByName(i18n("Venus")), doBuildList);
570
571 if (maglimit < data->skyComposite()->findByName(i18n("Mars"))->mag())
572 {
573 if (!doBuildList)
574 --ObjectCount;
575 filterPass = false;
576 }
577 else
578 filterPass = true;
579 if (needRegion && filterPass)
580 filterPass = applyRegionFilter(data->skyComposite()->findByName(i18n("Mars")), doBuildList);
581 if (olw->SelectByDate->isChecked() && filterPass)
582 applyObservableFilter(data->skyComposite()->findByName(i18n("Mars")), doBuildList);
583
584 if (maglimit < data->skyComposite()->findByName(i18n("Jupiter"))->mag())
585 {
586 if (!doBuildList)
587 --ObjectCount;
588 filterPass = false;
589 }
590 else
591 filterPass = true;
592 if (needRegion && filterPass)
593 filterPass = applyRegionFilter(data->skyComposite()->findByName(i18n("Jupiter")), doBuildList);
594 if (olw->SelectByDate->isChecked() && filterPass)
595 applyObservableFilter(data->skyComposite()->findByName(i18n("Jupiter")), doBuildList);
596
597 if (maglimit < data->skyComposite()->findByName(i18n("Saturn"))->mag())
598 {
599 if (!doBuildList)
600 --ObjectCount;
601 filterPass = false;
602 }
603 else
604 filterPass = true;
605
606 if (needRegion && filterPass)
607 filterPass = applyRegionFilter(data->skyComposite()->findByName(i18n("Saturn")), doBuildList);
608 if (olw->SelectByDate->isChecked() && filterPass)
609 applyObservableFilter(data->skyComposite()->findByName(i18n("Saturn")), doBuildList);
610
611 if (maglimit < data->skyComposite()->findByName(i18n("Uranus"))->mag())
612 {
613 if (!doBuildList)
614 --ObjectCount;
615 filterPass = false;
616 }
617 else
618 filterPass = true;
619
620 if (needRegion && filterPass)
621 filterPass = applyRegionFilter(data->skyComposite()->findByName(i18n("Uranus")), doBuildList);
622 if (olw->SelectByDate->isChecked() && filterPass)
623 applyObservableFilter(data->skyComposite()->findByName(i18n("Uranus")), doBuildList);
624
625 if (maglimit < data->skyComposite()->findByName(i18n("Neptune"))->mag())
626 {
627 if (!doBuildList)
628 --ObjectCount;
629 filterPass = false;
630 }
631 else
632 filterPass = true;
633
634 if (needRegion && filterPass)
635 filterPass = applyRegionFilter(data->skyComposite()->findByName(i18n("Neptune")), doBuildList);
636 if (olw->SelectByDate->isChecked() && filterPass)
637 applyObservableFilter(data->skyComposite()->findByName(i18n("Neptune")), doBuildList);
638
639 // if (maglimit < data->skyComposite()->findByName(i18nc("Asteroid name (optional)", "Pluto"))->mag())
640 // {
641 // if (!doBuildList)
642 // --ObjectCount;
643 // filterPass = false;
644 // }
645 // else
646 // filterPass = true;
647
648 // if (needRegion && filterPass)
649 // filterPass = applyRegionFilter(data->skyComposite()->findByName(i18nc("Asteroid name (optional)", "Pluto")), doBuildList);
650 // if (olw->SelectByDate->isChecked() && filterPass)
651 // applyObservableFilter(data->skyComposite()->findByName(i18nc("Asteroid name (optional)", "Pluto")), doBuildList);
652 }
653
654 //Deep sky objects
655 bool dso =
656 (isItemSelected(i18n("Open clusters"), olw->TypeList) ||
657 isItemSelected(i18n("Globular clusters"), olw->TypeList) ||
658 isItemSelected(i18n("Gaseous nebulae"), olw->TypeList) ||
659 isItemSelected(i18n("Planetary nebulae"), olw->TypeList) || isItemSelected(i18n("Galaxies"), olw->TypeList));
660
661 if (dso)
662 {
663 //Don't need to do anything if we are just counting objects and not
664 //filtering by region or magnitude
665 if (needRegion || olw->SelectByMagnitude->isChecked() || olw->SelectByDate->isChecked())
666 {
667
668 CatalogsDB::DBManager manager{ CatalogsDB::dso_db_path() };
669
670 for(auto& o : manager.get_objects(olw->SelectByMagnitude->isChecked() ? maglimit : 99))
671 {
672 //Skip unselected object types
673 bool typeSelected = false;
674 // if ( (o->type() == SkyObject::STAR || o->type() == SkyObject::CATALOG_STAR) &&
675 // isItemSelected( i18n( "Stars" ), olw->TypeList ) )
676 // typeSelected = true;
677 switch (o.type())
678 {
679 case SkyObject::OPEN_CLUSTER:
680 if (isItemSelected(i18n("Open clusters"), olw->TypeList))
681 typeSelected = true;
682 break;
683
684 case SkyObject::GLOBULAR_CLUSTER:
685 if (isItemSelected(i18n("Globular clusters"), olw->TypeList))
686 typeSelected = true;
687 break;
688
689 case SkyObject::GASEOUS_NEBULA:
690 case SkyObject::SUPERNOVA_REMNANT:
691 if (isItemSelected(i18n("Gaseous nebulae"), olw->TypeList))
692 typeSelected = true;
693 break;
694
695 case SkyObject::PLANETARY_NEBULA:
696 if (isItemSelected(i18n("Planetary nebulae"), olw->TypeList))
697 typeSelected = true;
698 break;
699 case SkyObject::GALAXY:
700 if (isItemSelected(i18n("Galaxies"), olw->TypeList))
701 typeSelected = true;
702 break;
703 }
704
705 if (!typeSelected)
706 continue;
707
708 if (olw->SelectByMagnitude->isChecked())
709 {
710 if (o.mag() > 90.)
711 {
712 if (olw->IncludeNoMag->isChecked())
713 {
714 auto *obj = &o;
715 if(doBuildList)
716 obj = &data->skyComposite()->catalogsComponent()
717 ->insertStaticObject(o);
718
719 if (needRegion)
720 filterPass = applyRegionFilter(obj, doBuildList);
721 if (olw->SelectByDate->isChecked() && filterPass)
722 applyObservableFilter(obj, doBuildList);
723 }
724 else if (!doBuildList)
725 --ObjectCount;
726 }
727 else
728 {
729 if (o.mag() <= maglimit)
730 {
731 auto *obj = &o;
732 if(doBuildList)
733 obj = &data->skyComposite()->catalogsComponent()
734 ->insertStaticObject(o);
735
736 if (needRegion)
737 filterPass = applyRegionFilter(obj, doBuildList);
738 if (olw->SelectByDate->isChecked() && filterPass)
739 applyObservableFilter(obj, doBuildList);
740 }
741 else if (!doBuildList)
742 --ObjectCount;
743 }
744 }
745 else
746 {
747 auto *obj = &o;
748 if(doBuildList)
749 obj = &data->skyComposite()->catalogsComponent()
750 ->insertStaticObject(o);
751
752 if (needRegion)
753 filterPass = applyRegionFilter(obj, doBuildList);
754 if (olw->SelectByDate->isChecked() && filterPass)
755 applyObservableFilter(obj, doBuildList);
756 }
757 }
758 }
759 }
760
761 //Comets
762 if (isItemSelected(i18n("Comets"), olw->TypeList))
763 {
764 foreach (SkyObject *o, data->skyComposite()->comets())
765 {
766 if (olw->SelectByMagnitude->isChecked())
767 {
768 if (o->mag() > 90.)
769 {
770 if (olw->IncludeNoMag->isChecked())
771 {
772 if (needRegion)
773 filterPass = applyRegionFilter(o, doBuildList);
774 if (olw->SelectByDate->isChecked() && filterPass)
775 applyObservableFilter(o, doBuildList);
776 }
777 else if (!doBuildList)
778 --ObjectCount;
779 }
780 else
781 {
782 if (o->mag() <= maglimit)
783 {
784 if (needRegion)
785 filterPass = applyRegionFilter(o, doBuildList);
786 if (olw->SelectByDate->isChecked() && filterPass)
787 applyObservableFilter(o, doBuildList);
788 }
789 else if (!doBuildList)
790 --ObjectCount;
791 }
792 }
793 else
794 {
795 if (needRegion)
796 filterPass = applyRegionFilter(o, doBuildList);
797 if (olw->SelectByDate->isChecked() && filterPass)
798 applyObservableFilter(o, doBuildList);
799 }
800 }
801 }
802
803 //Asteroids
804 if (isItemSelected(i18n("Asteroids"), olw->TypeList))
805 {
806 foreach (SkyObject *o, data->skyComposite()->asteroids())
807 {
808 if (olw->SelectByMagnitude->isChecked())
809 {
810 if (o->mag() > 90.)
811 {
812 if (olw->IncludeNoMag->isChecked())
813 {
814 if (needRegion)
815 filterPass = applyRegionFilter(o, doBuildList);
816 if (olw->SelectByDate->isChecked() && filterPass)
817 applyObservableFilter(o, doBuildList);
818 }
819 else if (!doBuildList)
820 --ObjectCount;
821 }
822 else
823 {
824 if (o->mag() <= maglimit)
825 {
826 if (needRegion)
827 filterPass = applyRegionFilter(o, doBuildList);
828 if (olw->SelectByDate->isChecked() && filterPass)
829 applyObservableFilter(o, doBuildList);
830 }
831 else if (!doBuildList)
832 --ObjectCount;
833 }
834 }
835 else
836 {
837 if (needRegion)
838 filterPass = applyRegionFilter(o, doBuildList);
839 if (olw->SelectByDate->isChecked() && filterPass)
840 applyObservableFilter(o, doBuildList);
841 }
842 }
843 }
844
845 //Update the object count label
846 if (doBuildList)
847 ObjectCount = obsList().size();
848
849 olw->CountLabel->setText(i18np("Your observing list currently has 1 object",
850 "Your observing list currently has %1 objects", ObjectCount));
851 }
852
applyRegionFilter(SkyObject * o,bool doBuildList,bool doAdjustCount)853 bool ObsListWizard::applyRegionFilter(SkyObject *o, bool doBuildList, bool doAdjustCount)
854 {
855 //select by constellation
856 if (isItemSelected(i18n("by constellation"), olw->RegionList))
857 {
858 QString c = KStarsData::Instance()->skyComposite()->constellationBoundary()->constellationName(o);
859
860 if (isItemSelected(c, olw->ConstellationList))
861 {
862 if (doBuildList)
863 obsList().append(o);
864
865 return true;
866 }
867 else if (doAdjustCount)
868 {
869 --ObjectCount;
870 return false;
871 }
872 else
873 return false;
874 }
875
876 //select by rectangular region
877 else if (isItemSelected(i18n("in a rectangular region"), olw->RegionList))
878 {
879 double ra = o->ra().Hours();
880 double dec = o->dec().Degrees();
881 bool addObject = false;
882 if (dec >= yRect1 && dec <= yRect2)
883 {
884 if (xRect1 < 0.0)
885 {
886 addObject = ra >= xRect1 + 24.0 || ra <= xRect2;
887 }
888 else
889 {
890 addObject = ra >= xRect1 && ra <= xRect2;
891 }
892 }
893
894 if (addObject)
895 {
896 if (doBuildList)
897 obsList().append(o);
898
899 return true;
900 }
901
902 else
903 {
904 if (doAdjustCount)
905 --ObjectCount;
906
907 return false;
908 }
909 }
910
911 //select by circular region
912 //make sure circ region data are valid
913 else if (isItemSelected(i18n("in a circular region"), olw->RegionList))
914 {
915 if (o->angularDistanceTo(&pCirc).Degrees() < rCirc)
916 {
917 if (doBuildList)
918 obsList().append(o);
919
920 return true;
921 }
922 else if (doAdjustCount)
923 {
924 --ObjectCount;
925 return false;
926 }
927 else
928 return false;
929 }
930
931 //No region filter, just add the object
932 else if (doBuildList)
933 {
934 obsList().append(o);
935 }
936
937 return true;
938 }
939
applyObservableFilter(SkyObject * o,bool doBuildList,bool doAdjustCount)940 bool ObsListWizard::applyObservableFilter(SkyObject *o, bool doBuildList, bool doAdjustCount)
941 {
942 SkyPoint p = *o;
943
944 //Check altitude of object every hour from 18:00 to midnight
945 //If it's ever above 15 degrees, flag it as visible
946 KStarsDateTime Evening(olw->Date->date(), QTime(18, 0, 0), Qt::LocalTime);
947 KStarsDateTime Midnight(olw->Date->date().addDays(1), QTime(0, 0, 0), Qt::LocalTime);
948 double minAlt = 15, maxAlt = 90;
949
950 // Or use user-selected values, if they're valid
951 if (olw->timeFrom->time().isValid() && olw->timeTo->time().isValid())
952 {
953 Evening.setTime(olw->timeFrom->time());
954 Midnight.setTime(olw->timeTo->time());
955
956 // If time from < timeTo (e.g. 06:00 PM to 9:00 PM)
957 // then we stay on the same day.
958 if (olw->timeFrom->time() < olw->timeTo->time())
959 {
960 Midnight.setDate(olw->Date->date());
961 }
962 // Otherwise we advance by one day
963 else
964 {
965 Midnight.setDate(olw->Date->date().addDays(1));
966 }
967 }
968
969 minAlt = olw->minAlt->value();
970 maxAlt = olw->maxAlt->value();
971
972 // This is the "relaxed" search mode
973 // where if the object obeys the restrictions in 50% of the time of the range
974 // then it qualifies as "visible"
975 double totalCount = 0, visibleCount = 0;
976 for (KStarsDateTime t = Evening; t < Midnight; t = t.addSecs(3600.0))
977 {
978 dms LST = geo->GSTtoLST(t.gst());
979 p.EquatorialToHorizontal(&LST, geo->lat());
980 totalCount++;
981 if (p.alt().Degrees() >= minAlt && p.alt().Degrees() <= maxAlt)
982 visibleCount++;
983 }
984
985 // If the object is within the min/max alt at least coverage % of the time range
986 // then consider it visible
987 if (visibleCount / totalCount >= olw->coverage->value() / 100.0)
988 return true;
989
990 if (doAdjustCount)
991 --ObjectCount;
992 if (doBuildList)
993 obsList().takeAt(obsList().indexOf(o));
994
995 return false;
996
997 // This is the strict mode where ANY object that does not meet the min & max
998 // altitude at ANY time would be removed from the list.
999 #if 0
1000 for (KStarsDateTime t = Evening; t < Midnight; t = t.addSecs(3600.0))
1001 {
1002 dms LST = geo->GSTtoLST(t.gst());
1003 p.EquatorialToHorizontal(&LST, geo->lat());
1004 if (p.alt().Degrees() < minAlt || p.alt().Degrees() > maxAlt)
1005 {
1006 if (doAdjustCount)
1007 --ObjectCount;
1008 if (doBuildList)
1009 obsList().takeAt(obsList().indexOf(o));
1010 return false;
1011 }
1012 }
1013 return true;
1014 #endif
1015
1016 }
1017