Kstars

skycalendar.cpp
1 /*
2  SPDX-FileCopyrightText: 2008 Jason Harris <[email protected]>
3 
4  SPDX-License-Identifier: GPL-2.0-or-later
5 */
6 
7 #include "skycalendar.h"
8 
9 #include "geolocation.h"
10 #include "ksplanetbase.h"
11 #include "kstarsdata.h"
12 #include "dialogs/locationdialog.h"
13 #include "skycomponents/skymapcomposite.h"
14 
15 #include <KPlotObject>
16 
17 #include <QPainter>
18 #include <QPixmap>
19 #include <QPrintDialog>
20 #include <QPrinter>
21 #include <QPushButton>
22 #include <QScreen>
23 #include <QtConcurrent>
24 
25 SkyCalendarUI::SkyCalendarUI(QWidget *parent) : QFrame(parent)
26 {
27  setupUi(this);
28 }
29 
30 SkyCalendar::SkyCalendar(QWidget *parent) : QDialog(parent)
31 {
32 #ifdef Q_OS_OSX
33  setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint);
34 #endif
35 
36  scUI = new SkyCalendarUI(this);
37 
38  QVBoxLayout *mainLayout = new QVBoxLayout;
39 
40  mainLayout->addWidget(scUI);
41  setLayout(mainLayout);
42 
43  geo = KStarsData::Instance()->geo();
44 
45  setWindowTitle(i18nc("@title:window", "Sky Calendar"));
46 
48  mainLayout->addWidget(buttonBox);
49  connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
50 
51  QPushButton *printB = new QPushButton(QIcon::fromTheme("document-print"), i18n("&Print..."));
52  printB->setToolTip(i18n("Print the Sky Calendar"));
53  buttonBox->addButton(printB, QDialogButtonBox::ActionRole);
54  connect(printB, SIGNAL(clicked()), this, SLOT(slotPrint()));
55 
56  setModal(false);
57 
58  //Adjust minimum size for small screens:
59  if (QGuiApplication::primaryScreen()->geometry().height() <= scUI->CalendarView->height())
60  {
61  scUI->CalendarView->setMinimumSize(400, 600);
62  }
63 
64  scUI->CalendarView->setShowGrid(false);
65  scUI->Year->setValue(KStarsData::Instance()->lt().date().year());
66 
67  scUI->LocationButton->setText(geo->fullName());
68 
69  scUI->CalendarView->setHorizon();
70 
71  plotButtonText = scUI->CreateButton->text();
72  connect(scUI->CreateButton, &QPushButton::clicked, [this]()
73  {
74  scUI->CreateButton->setText(i18n("Please Wait") + "...");
75  QtConcurrent::run(this, &SkyCalendar::slotFillCalendar);
76  });
77 
78  connect(scUI->LocationButton, SIGNAL(clicked()), this, SLOT(slotLocation()));
79 }
80 
81 int SkyCalendar::year()
82 {
83  return scUI->Year->value();
84 }
85 
86 void SkyCalendar::slotFillCalendar()
87 {
88  scUI->CreateButton->setEnabled(false);
89 
90  scUI->CalendarView->resetPlot();
91  scUI->CalendarView->setHorizon();
92 
93  if (scUI->checkBox_Mercury->isChecked())
94  addPlanetEvents(KSPlanetBase::MERCURY);
95  if (scUI->checkBox_Venus->isChecked())
96  addPlanetEvents(KSPlanetBase::VENUS);
97  if (scUI->checkBox_Mars->isChecked())
98  addPlanetEvents(KSPlanetBase::MARS);
99  if (scUI->checkBox_Jupiter->isChecked())
100  addPlanetEvents(KSPlanetBase::JUPITER);
101  if (scUI->checkBox_Saturn->isChecked())
102  addPlanetEvents(KSPlanetBase::SATURN);
103  if (scUI->checkBox_Uranus->isChecked())
104  addPlanetEvents(KSPlanetBase::URANUS);
105  if (scUI->checkBox_Neptune->isChecked())
106  addPlanetEvents(KSPlanetBase::NEPTUNE);
107 
108  scUI->CreateButton->setText(i18n("Plot Planetary Almanac"));
109  scUI->CreateButton->setEnabled(true);
110 
111  //if ( scUI->checkBox_Pluto->isChecked() )
112  //addPlanetEvents( KSPlanetBase::PLUTO );
113 
114  /*
115  {
116  QMutexLocker locker(&calculationMutex);
117 
118  calculating = false;
119  scUI->CreateButton->setEnabled(true);
120  scUI->CalendarView->update();
121  }
122  */
123 }
124 
125 #if 0
126 void SkyCalendar::slotCalculating()
127 {
128  while (calculating)
129  {
130  //QMutexLocker locker(&calculationMutex);
131 
132  if (!calculating)
133  continue;
134 
135  scUI->CreateButton->setText(i18n("Please Wait") + ". ");
136  scUI->CreateButton->repaint();
137  QThread::msleep(200);
138  scUI->CreateButton->setText(i18n("Please Wait") + ".. ");
139  scUI->CreateButton->repaint();
140  QThread::msleep(200);
141 
142  scUI->CreateButton->repaint();
143  QThread::msleep(200);
144  }
145  scUI->CreateButton->setText(plotButtonText);
146 }
147 #endif
148 
149 // FIXME: For the time being, adjust with dirty, cluttering labels that don't align to the line
150 /*
151 void SkyCalendar::drawEventLabel( float x1, float y1, float x2, float y2, QString LabelText ) {
152  QFont origFont = p->font();
153  p->setFont( QFont( "Bitstream Vera", 10 ) );
154 
155  int textFlags = Qt::AlignCenter; // TODO: See if Qt::SingleLine flag works better
156  QFontMetricsF fm( p->font(), p->device() );
157 
158  QRectF LabelRect = fm.boundingRect( QRectF(0,0,1,1), textFlags, LabelText );
159  QPointF LabelPoint = scUI->CalendarView->mapToWidget( QPointF( x, y ) );
160 
161  float LabelAngle = atan2( y2 - y1, x2 - x1 )/dms::DegToRad;
162 
163  p->save();
164  p->translate( LabelPoint );
165  p->rotate( LabelAngle );
166  p->drawText( LabelRect, textFlags, LabelText );
167  p->restore();
168 
169  p->setFont( origFont );
170 }
171 */
172 
173 void SkyCalendar::addPlanetEvents(int nPlanet)
174 {
175  KSPlanetBase *ksp = KStarsData::Instance()->skyComposite()->planet(nPlanet);
176  QColor pColor = ksp->color();
177  //QVector<QPointF> vRise, vSet, vTransit;
178  std::vector<QPointF> vRise, vSet, vTransit;
179 
180  for (KStarsDateTime kdt(QDate(year(), 1, 1), QTime(12, 0, 0)); kdt.date().year() == year();
181  kdt = kdt.addDays(scUI->spinBox_Interval->value()))
182  {
183  float rTime, sTime, tTime;
184 
185  //Compute rise/set/transit times. If they occur before noon,
186  //recompute for the following day
187  QTime tmp_rTime = ksp->riseSetTime(kdt, geo, true, true); //rise time, exact
188  QTime tmp_sTime = ksp->riseSetTime(kdt, geo, false, true); //set time, exact
189  QTime tmp_tTime = ksp->transitTime(kdt, geo);
190  QTime midday(12, 0, 0);
191 
192  // NOTE: riseSetTime should be fix now, this test is no longer necessary
193  if (tmp_rTime == tmp_sTime)
194  {
195  tmp_rTime = QTime();
196  tmp_sTime = QTime();
197  }
198 
199  if (tmp_rTime.isValid() && tmp_sTime.isValid())
200  {
201  rTime = tmp_rTime.secsTo(midday) * 24.0 / 86400.0;
202  sTime = tmp_sTime.secsTo(midday) * 24.0 / 86400.0;
203 
204  if (tmp_rTime <= midday)
205  rTime = 12.0 - rTime;
206  else
207  rTime = -12.0 - rTime;
208 
209  if (tmp_sTime <= midday)
210  sTime = 12.0 - sTime;
211  else
212  sTime = -12.0 - sTime;
213  }
214  else
215  {
216  if (ksp->transitAltitude(kdt, geo).degree() > 0)
217  {
218  rTime = -24.0;
219  sTime = 24.0;
220  }
221  else
222  {
223  rTime = 24.0;
224  sTime = -24.0;
225  }
226  }
227 
228  tTime = tmp_tTime.secsTo(midday) * 24.0 / 86400.0;
229  if (tmp_tTime <= midday)
230  tTime = 12.0 - tTime;
231  else
232  tTime = -12.0 - tTime;
233 
234  float dy = kdt.date().daysInYear() - kdt.date().dayOfYear();
235  vRise.push_back(QPointF(rTime, dy));
236  vSet.push_back(QPointF(sTime, dy));
237  vTransit.push_back(QPointF(tTime, dy));
238  }
239 
240  //Now, find continuous segments in each QVector and add each segment
241  //as a separate KPlotObject
242 
243  KPlotObject *oRise = new KPlotObject(pColor, KPlotObject::Lines, 2.0);
244  KPlotObject *oSet = new KPlotObject(pColor, KPlotObject::Lines, 2.0);
245  KPlotObject *oTransit = new KPlotObject(pColor, KPlotObject::Lines, 2.0);
246 
247  float defaultSetTime = -10.0;
248  float defaultRiseTime = 8.0;
249  QString label;
250  bool initialRise = true;
251  bool initialSet = true;
252  bool initialTransit = true;
253  bool extraCheckRise = true;
254  bool extraCheckSet = true;
255  bool extraCheckTransit = true;
256  bool needRiseLabel = false;
257  bool needSetLabel = false;
258  bool needTransertLabel = false;
259 
260  float maxRiseTime = 0.0;
261  for (auto &item : vRise)
262  {
263  if (item.x() >= maxRiseTime)
264  maxRiseTime = item.x();
265  }
266  maxRiseTime = qFloor(maxRiseTime) - 1.0;
267 
268  for (uint32_t i = 0; i < vRise.size(); ++i)
269  {
270  if (initialRise && (vRise.at(i).x() > defaultSetTime && vRise.at(i).x() < defaultRiseTime))
271  {
272  needRiseLabel = true;
273  initialRise = false;
274  }
275  else if (vRise.at(i).x() < defaultSetTime || vRise.at(i).x() > defaultRiseTime)
276  {
277  initialRise = true;
278  needRiseLabel = false;
279  }
280  else
281  needRiseLabel = false;
282 
283  if (extraCheckRise && vRise.at(i).x() > defaultRiseTime && vRise.at(i).x() < maxRiseTime)
284  {
285  needRiseLabel = true;
286  extraCheckRise = false;
287  }
288 
289  if (initialSet && (vSet.at(i).x() > defaultSetTime && vSet.at(i).x() < defaultRiseTime))
290  {
291  needSetLabel = true;
292  initialSet = false;
293  }
294  else if (vSet.at(i).x() < defaultSetTime || vSet.at(i).x() > defaultRiseTime)
295  {
296  initialSet = true;
297  needSetLabel = false;
298  }
299  else
300  needSetLabel = false;
301 
302  if (extraCheckSet && vSet.at(i).x() > defaultRiseTime && vSet.at(i).x() < maxRiseTime)
303  {
304  needSetLabel = true;
305  extraCheckSet = false;
306  }
307 
308  if (initialTransit && (vTransit.at(i).x() > defaultSetTime && vTransit.at(i).x() < defaultRiseTime))
309  {
310  needTransertLabel = true;
311  initialTransit = false;
312  }
313  else if (vTransit.at(i).x() < defaultSetTime || vTransit.at(i).x() > defaultRiseTime)
314  {
315  initialTransit = true;
316  needTransertLabel = false;
317  ;
318  }
319  else
320  needTransertLabel = false;
321 
322  if (extraCheckTransit && vTransit.at(i).x() > defaultRiseTime && vTransit.at(i).x() < maxRiseTime)
323  {
324  needTransertLabel = true;
325  extraCheckTransit = false;
326  }
327 
328  if (vRise.at(i).x() > -23.0 && vRise.at(i).x() < 23.0)
329  {
330  if (i > 0 && fabs(vRise.at(i).x() - vRise.at(i - 1).x()) > 6.0)
331  {
332  scUI->CalendarView->addPlotObject(oRise);
333  oRise = new KPlotObject(pColor, KPlotObject::Lines, 2.0);
334  extraCheckRise = true;
335  }
336 
337  if (needRiseLabel)
338  label = i18nc("A planet rises from the horizon", "%1 rises", ksp->name());
339  else
340  label = QString();
341  // Add the current point to KPlotObject
342  oRise->addPoint(vRise.at(i), label);
343  }
344  else
345  {
346  scUI->CalendarView->addPlotObject(oRise);
347  oRise = new KPlotObject(pColor, KPlotObject::Lines, 2.0);
348  }
349 
350  if (vSet.at(i).x() > -23.0 && vSet.at(i).x() < 23.0)
351  {
352  if (i > 0 && fabs(vSet.at(i).x() - vSet.at(i - 1).x()) > 6.0)
353  {
354  scUI->CalendarView->addPlotObject(oSet);
355  oSet = new KPlotObject(pColor, KPlotObject::Lines, 2.0);
356  extraCheckSet = true;
357  }
358 
359  if (needSetLabel)
360  label = i18nc("A planet sets from the horizon", "%1 sets", ksp->name());
361  else
362  label = QString();
363 
364  oSet->addPoint(vSet.at(i), label);
365  }
366  else
367  {
368  scUI->CalendarView->addPlotObject(oSet);
369  oSet = new KPlotObject(pColor, KPlotObject::Lines, 2.0);
370  }
371 
372  if (vTransit.at(i).x() > -23.0 && vTransit.at(i).x() < 23.0)
373  {
374  if (i > 0 && fabs(vTransit.at(i).x() - vTransit.at(i - 1).x()) > 6.0)
375  {
376  scUI->CalendarView->addPlotObject(oTransit);
377  oTransit = new KPlotObject(pColor, KPlotObject::Lines, 2.0);
378  extraCheckTransit = true;
379  }
380 
381  if (needTransertLabel)
382  label = i18nc("A planet transits across the meridian", "%1 transits", ksp->name());
383  else
384  label = QString();
385 
386  oTransit->addPoint(vTransit.at(i), label);
387  }
388  else
389  {
390  scUI->CalendarView->addPlotObject(oTransit);
391  oTransit = new KPlotObject(pColor, KPlotObject::Lines, 2.0);
392  }
393  }
394 
395  // Add the last points
396  scUI->CalendarView->addPlotObject(oRise);
397  scUI->CalendarView->addPlotObject(oSet);
398  scUI->CalendarView->addPlotObject(oTransit);
399 }
400 
401 void SkyCalendar::slotPrint()
402 {
403  QPainter p; // Our painter object
404  QPrinter printer; // Our printer object
405  QString str_legend; // Text legend
406  QString str_year; // Calendar's year
407  int text_height = 200; // Height of legend text zone in points
408  QSize calendar_size; // Initial calendar widget size
409  QFont calendar_font; // Initial calendar font
410  int calendar_font_size; // Initial calendar font size
411 
412  // Set printer resolution to 300 dpi
413  printer.setResolution(300);
414 
415  // Open print dialog
416  //NOTE Changed from pointer to statically allocated object, what effect will it have?
417  //QPointer<QPrintDialog> dialog( KdePrint::createPrintDialog( &printer, this ) );
418  QPrintDialog dialog(&printer, this);
419  dialog.setWindowTitle(i18nc("@title:window", "Print sky calendar"));
420  if (dialog.exec() == QDialog::Accepted)
421  {
422  // Change mouse cursor
424 
425  // Save calendar widget font
426  calendar_font = scUI->CalendarView->font();
427  // Save calendar widget font size
428  calendar_font_size = calendar_font.pointSize();
429  // Save calendar widget size
430  calendar_size = scUI->CalendarView->size();
431 
432  // Set text legend
433  str_year.setNum(year());
434  str_legend = i18n("Sky Calendar");
435  str_legend += '\n';
436  str_legend += geo->fullName();
437  str_legend += " - ";
438  str_legend += str_year;
439 
440  // Create a rectangle for legend text zone
441  QRect text_rect(0, 0, printer.width(), text_height);
442 
443  // Increase calendar widget font size so it looks good in 300 dpi
444  calendar_font.setPointSize(calendar_font_size * 3);
445  scUI->CalendarView->setFont(calendar_font);
446  // Increase calendar widget size to fit the entire page
447  scUI->CalendarView->resize(printer.width(), printer.height() - text_height);
448 
449  // Create a pixmap and render calendar widget into it
450  QPixmap pixmap(scUI->CalendarView->size());
451  scUI->CalendarView->render(&pixmap);
452 
453  // Begin painting on printer
454  p.begin(&printer);
455  // Draw legend
456  p.drawText(text_rect, Qt::AlignLeft, str_legend);
457  // Draw calendar
458  p.drawPixmap(0, text_height, pixmap);
459  // Ending painting
460  p.end();
461 
462  // Restore calendar widget font size
463  calendar_font.setPointSize(calendar_font_size);
464  scUI->CalendarView->setFont(calendar_font);
465  // Restore calendar widget size
466  scUI->CalendarView->resize(calendar_size);
467 
468  // Restore mouse cursor
470  }
471  //delete dialog;
472 }
473 
474 void SkyCalendar::slotLocation()
475 {
476  QPointer<LocationDialog> ld = new LocationDialog(this);
477  if (ld->exec() == QDialog::Accepted)
478  {
479  GeoLocation *newGeo = ld->selectedCity();
480  if (newGeo)
481  {
482  geo = newGeo;
483  scUI->LocationButton->setText(geo->fullName());
484  }
485  }
486  delete ld;
487 
488  scUI->CalendarView->setHorizon();
489  slotFillCalendar();
490 }
491 
492 GeoLocation *SkyCalendar::get_geo()
493 {
494  return geo;
495 }
void msleep(unsigned long msecs)
Extension of QDateTime for KStars KStarsDateTime can represent the date/time as a Julian Day,...
AlignLeft
int width() const const
void drawPixmap(const QRectF &target, const QPixmap &pixmap, const QRectF &source)
QColor & color()
Definition: ksplanetbase.h:203
int degree() const
Definition: dms.h:116
void setPointSize(int pointSize)
void clicked(bool checked)
int pointSize() const const
QIcon fromTheme(const QString &name)
virtual QString name(void) const
Definition: skyobject.h:145
bool isValid() const const
void drawText(const QPointF &position, const QString &text)
int height() const const
void addWidget(QWidget *widget, int stretch, Qt::Alignment alignment)
int secsTo(const QTime &t) const const
bool begin(QPaintDevice *device)
bool end()
QString i18n(const char *text, const TYPE &arg...)
QString & setNum(short n, int base)
QTime riseSetTime(const KStarsDateTime &dt, const GeoLocation *geo, bool rst, bool exact=true) const
Determine the time at which the point will rise or set.
Definition: skyobject.cpp:93
GeoLocation * geo()
Definition: kstarsdata.h:229
WaitCursor
void addButton(QAbstractButton *button, QDialogButtonBox::ButtonRole role)
GeoCoordinates geo(const QVariant &location)
QDate addDays(qint64 ndays) const const
SkyMapComposite * skyComposite()
Definition: kstarsdata.h:165
QString label(StandardShortcut id)
dms transitAltitude(const KStarsDateTime &dt, const GeoLocation *geo) const
Definition: skyobject.cpp:244
void addPoint(const QPointF &p, const QString &label=QString(), double barWidth=0.0)
void setToolTip(const QString &)
void setOverrideCursor(const QCursor &cursor)
QDate date() const const
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QTime transitTime(const KStarsDateTime &dt, const GeoLocation *geo) const
The same iteration technique described in riseSetTime() is used here.
Definition: skyobject.cpp:239
void restoreOverrideCursor()
void setResolution(int dpi)
Provides necessary information about objects in the solar system.
Definition: ksplanetbase.h:49
Relevant data about an observing location on Earth.
Definition: geolocation.h:27
This file is part of the KDE documentation.
Documentation copyright © 1996-2022 The KDE developers.
Generated on Tue Aug 9 2022 04:06:07 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.