Kstars

calendarwidget.cpp
1 /*
2  SPDX-FileCopyrightText: 2008 Jason Harris <[email protected]>
3 
4  SPDX-License-Identifier: GPL-2.0-or-later
5 */
6 
7 #include "calendarwidget.h"
8 
9 #include "ksalmanac.h"
10 #include "kssun.h"
11 #include "kstarsdata.h"
12 #include "skycalendar.h"
13 
14 #include <KLocalizedString>
15 #include <KPlotting/KPlotObject>
16 
17 #include <QPainter>
18 #include <QDebug>
19 
20 #define BIGTICKSIZE 10
21 #define SMALLTICKSIZE 4
22 
23 CalendarWidget::CalendarWidget(QWidget *parent) : KPlotWidget(parent)
24 {
25  setAntialiasing(true);
26 
27  setTopPadding(40);
28  setLeftPadding(60);
29  setRightPadding(100);
30 
31  maxRTime = 12.0;
32  minSTime = -12.0;
33 }
34 
35 void CalendarWidget::paintEvent(QPaintEvent *e)
36 {
37  Q_UNUSED(e)
38 
39  QPainter p;
40 
41  p.begin(this);
45 
46  setPixRect();
47  p.setClipRect(pixRect());
48  p.setClipping(true);
49 
50  drawHorizon(&p);
51 
52  foreach (KPlotObject *po, plotObjects())
53  {
54  po->draw(&p, this);
55  }
56 
57  p.setClipping(false);
58  drawAxes(&p);
59 }
60 
61 void CalendarWidget::setHorizon()
62 {
63  KSSun thesun;
64  SkyCalendar *skycal = (SkyCalendar *)topLevelWidget();
65  KStarsDateTime kdt(QDate(skycal->year(), 1, 1), QTime(12, 0, 0));
66 
67  maxRTime = 0.0;
68  minSTime = 0.0;
69 
70  // Clear date, rise and set time lists
71  dateList.clear();
72  riseTimeList.clear();
73  setTimeList.clear();
74 
75  float rTime, sTime;
76 
77  // Get rise and set time every 7 days for 1 year
78  while (skycal->year() == kdt.date().year())
79  {
80  QTime tmp_rTime = thesun.riseSetTime(KStarsDateTime(kdt.djd() + 1.0), skycal->get_geo(), true, true);
81  QTime tmp_sTime = thesun.riseSetTime(KStarsDateTime(kdt.djd()), skycal->get_geo(), false, true);
82 
83  /* riseSetTime seems buggy since it sometimes returns the same time for rise and set (01:00:00).
84  * In this case, we just reset tmp_rTime and tmp_sTime so they will be considered invalid
85  * in the following lines.
86  * NOTE: riseSetTime should be fix now, this test is no longer necessary*/
87  if (tmp_rTime == tmp_sTime)
88  {
89  tmp_rTime = QTime();
90  tmp_sTime = QTime();
91  }
92 
93  // If rise and set times are valid, the sun rise and set...
94  if (tmp_rTime.isValid() && tmp_sTime.isValid())
95  {
96  // Compute X-coordinate value for rise and set time
97  QTime midday(12, 0, 0);
98  rTime = tmp_rTime.secsTo(midday) * 24.0 / 86400.0;
99  sTime = tmp_sTime.secsTo(midday) * 24.0 / 86400.0;
100 
101  if (tmp_rTime <= midday)
102  rTime = 12.0 - rTime;
103  else
104  rTime = -12.0 - rTime;
105 
106  if (tmp_sTime <= midday)
107  sTime = 12.0 - sTime;
108  else
109  sTime = -12.0 - sTime;
110  }
111  /* else, the sun don't rise and/or don't set.
112  * we look at the altitude of the sun at transit time, if it is above the horizon,
113  * there is no night, else there is no day. */
114  else
115  {
116  if (thesun.transitAltitude(KStarsDateTime(kdt.djd()), skycal->get_geo()).degree() > 0)
117  {
118  rTime = -4.0;
119  sTime = 4.0;
120  }
121  else
122  {
123  rTime = 12.0;
124  sTime = -12.0;
125  }
126  }
127 
128  // Get max rise time and min set time
129  if (rTime > maxRTime)
130  maxRTime = rTime;
131  if (sTime < minSTime)
132  minSTime = sTime;
133 
134  // Keep the day, rise time and set time in lists
135  dateList.append(kdt.date());
136  riseTimeList.append(rTime);
137  setTimeList.append(sTime);
138 
139  // Next week
140  kdt = kdt.addDays(skycal->scUI->spinBox_Interval->value());
141  }
142 
143  // Set widget limits
144  maxRTime = ceil(maxRTime) + 1.0;
145  if ((int)maxRTime % 2 != 0)
146  maxRTime++;
147  if (maxRTime > 12.0)
148  maxRTime = 12.0;
149  minSTime = floor(minSTime) - 1.0;
150  if ((int)minSTime % 2 != 0)
151  minSTime--;
152  if (minSTime < -12.0)
153  minSTime = -12.0;
154  setLimits(minSTime, maxRTime, 0.0, 366.0);
155  setPixRect();
156 }
157 
158 void CalendarWidget::drawHorizon(QPainter *p)
159 {
160  polySunRise.clear();
161  polySunSet.clear();
162 
163  for (int date = 0; date < dateList.size(); date++)
164  {
165  int day = dateList.at(date).daysInYear() - dateList.at(date).dayOfYear();
166  polySunRise << mapToWidget(QPointF(riseTimeList.at(date), day));
167  polySunSet << mapToWidget(QPointF(setTimeList.at(date), day));
168  }
169 
170  //Finish polygons by adding pixRect corners
171  polySunRise << mapToWidget(QPointF(riseTimeList.last(), dataRect().top()))
172  << mapToWidget(QPointF(dataRect().right(), dataRect().top()))
173  << mapToWidget(QPointF(dataRect().right(), dataRect().bottom()))
174  << mapToWidget(QPointF(riseTimeList.first(), dataRect().bottom()));
175  polySunSet << mapToWidget(QPointF(setTimeList.last(), dataRect().top()))
176  << mapToWidget(QPointF(dataRect().left(), pixRect().top()))
177  << mapToWidget(QPointF(dataRect().left(), pixRect().bottom()))
178  << mapToWidget(QPointF(setTimeList.first(), dataRect().bottom()));
179 
180  p->setPen(Qt::darkGreen);
182  p->drawPolygon(polySunRise);
183  p->drawPolygon(polySunSet);
184 }
185 
186 void CalendarWidget::drawAxes(QPainter *p)
187 {
188  SkyCalendar *skycal = (SkyCalendar *)topLevelWidget();
189 
190  p->setPen(foregroundColor());
191  p->setBrush(Qt::NoBrush);
192 
193  //Draw bounding box for the plot
194  p->drawRect(pixRect());
195 
196  //set small font for axis labels
197  QFont f = p->font();
198  int s = f.pointSize();
199  f.setPointSize(s - 2);
200  p->setFont(f);
201 
202  // Top axis label
204  i18n("Local time"));
205  // Bottom axis label
207  i18n("Universal time"));
208  // Left axis label
209  p->save();
210  p->rotate(90.0);
212  i18n("Month"));
213  // Right axis label
214  p->translate(0.0, -1 * frameRect().width() + 30);
216  i18n("Julian date"));
217  p->restore();
218 
219  //Top/Bottom axis tickmarks and time labels
220  for (float xx = minSTime; xx <= maxRTime; xx += 1.0)
221  {
222  int h = int(xx);
223  if (h < 0)
224  h += 24;
225  QTime time(h, 0, 0);
226  QString sTime = QLocale().toString(time, "hh:mm");
227 
228  QString sUtTime = QLocale().toString(time.addSecs(skycal->get_geo()->TZ() * -3600), "hh:mm");
229 
230  // Draw a small tick every hours and a big tick every two hours.
231  QPointF pBottomTick = mapToWidget(QPointF(xx, dataRect().y()));
232  QPointF pTopTick = QPointF(pBottomTick.x(), 0.0);
233  if (h % 2)
234  {
235  // Draw small bottom tick
236  p->drawLine(pBottomTick, QPointF(pBottomTick.x(), pBottomTick.y() - SMALLTICKSIZE));
237  // Draw small top tick
238  p->drawLine(pTopTick, QPointF(pTopTick.x(), pTopTick.y() + SMALLTICKSIZE));
239  }
240  else
241  {
242  // Draw big bottom tick
243  p->drawLine(pBottomTick, QPointF(pBottomTick.x(), pBottomTick.y() - BIGTICKSIZE));
244  QRectF r(pBottomTick.x() - BIGTICKSIZE, pBottomTick.y() + 0.5 * BIGTICKSIZE, 2 * BIGTICKSIZE, BIGTICKSIZE);
245  p->drawText(r, Qt::AlignCenter | Qt::TextDontClip, sUtTime);
246  // Draw big top tick
247  p->drawLine(pTopTick, QPointF(pTopTick.x(), pTopTick.y() + BIGTICKSIZE));
248  r.moveTop(-2.0 * BIGTICKSIZE);
249  p->drawText(r, Qt::AlignCenter | Qt::TextDontClip, sTime);
250  }
251 
252  // Vertical grid
253  if (skycal->scUI->checkBox_GridVertical->isChecked())
254  {
255  QColor c = p->pen().color();
256  c.setAlpha(100);
257  p->setPen(c);
258  p->drawLine(pTopTick, pBottomTick);
259  c.setAlpha(255);
260  p->setPen(c);
261  }
262  }
263 
264  // Month dividers
265  int y = skycal->year();
266  for (int imonth = 2; imonth <= 12; ++imonth)
267  {
268  QDate dt(y, imonth, 1);
269  float doy = float(dt.daysInYear() - dt.dayOfYear());
270 
271  // Draw a tick every months on left axis
272  QPointF pMonthTick = mapToWidget(QPointF(dataRect().x(), doy));
273  p->drawLine(pMonthTick, QPointF(pMonthTick.x() + BIGTICKSIZE, pMonthTick.y()));
274 
275  // Draw month labels
276  QRectF rMonth(mapToWidget(QPointF(0.0, float(dt.daysInYear() - dt.addMonths(-1).dayOfYear()))),
277  mapToWidget(QPointF(dataRect().left() - 0.1, doy)));
278  QLocale locale;
280  if (imonth == 12) // December
281  {
282  rMonth = QRectF(mapToWidget(QPointF(0.0, doy)), mapToWidget(QPointF(dataRect().left() - 0.1, 0.0)));
284  }
285 
286  // Draw dividers
287  if (skycal->scUI->checkBox_GridMonths->isChecked())
288  {
289  QColor c = p->pen().color();
290  c.setAlpha(100);
291  p->setPen(c);
292  p->drawLine(pMonthTick, QPointF(pixRect().right(), pMonthTick.y()));
293  c.setAlpha(255);
294  p->setPen(c);
295  }
296  }
297 
298  // Interval dividers
299  QFont origFont = p->font();
300  p->setFont(QFont("Monospace", origFont.pointSize() - 1));
301  for (KStarsDateTime kdt(QDate(y, 1, 1), QTime(12, 0, 0)); kdt.date().year() == y;
302  kdt = kdt.addDays(skycal->scUI->spinBox_Interval->value() > 7 ? skycal->scUI->spinBox_Interval->value() : 7))
303  {
304  // Draw ticks
305  float doy = float(kdt.date().daysInYear() - kdt.date().dayOfYear());
306  QPointF pWeekTick = mapToWidget(QPointF(dataRect().right(), doy));
307  p->drawLine(pWeekTick, QPointF(pWeekTick.x() - BIGTICKSIZE, pWeekTick.y()));
308 
309  // Draw julian date
310  QRectF rJd(mapToWidget(QPointF(dataRect().right() + 0.1, doy + 2)),
311  mapToWidget(QPointF(dataRect().right(), doy)));
313  QString().setNum(double(kdt.djd()), 'f', 1));
314 
315  // Draw dividers
316  if (skycal->scUI->checkBox_GridWeeks->isChecked())
317  {
318  QColor c = p->pen().color();
319  c.setAlpha(50);
320  p->setPen(c);
321  p->drawLine(pWeekTick, QPointF(pixRect().left(), pWeekTick.y()));
322  c.setAlpha(255);
323  p->setPen(c);
324  }
325  }
326 
327  // Current day
328  if (skycal->scUI->checkBox_GridToday->isChecked())
329  {
330  p->setPen(QColor(Qt::yellow));
331  QDate today = QDate::currentDate();
332  float doy = float(today.daysInYear() - today.dayOfYear());
334  p->drawText(mapToWidget(QPointF(dataRect().left() + 0.1, doy + 2.0)), today.toString());
335  }
336 
337  //Draw month labels along each horizon curve
338  // if ( skycal->scUI->checkBox_LabelMonths->isChecked() ) {
339  // p->setFont( QFont( "Monospace", origFont.pointSize() + 5 ) );
340  // int textFlags = Qt::TextSingleLine | Qt::AlignCenter;
341  // QFontMetricsF fm( p->font(), p->device() );
342  //
343  // for ( int date=0; date<dateList.size(); date++ ) {
344  // if ( dateList.at( date ).day() < 12 || dateList.at( date ).day() > 18 )
345  // continue;
346  //
347  // bool noNight = false;
348  // if ( riseTimeList.at( date ) < setTimeList.at( date ) )
349  // noNight = true;
350  //
351  // int imonth = dateList.at( date ).month();
352  //
353  // QString shortMonthName = QDate::shortMonthName( dateList.at( date ).month() );
354  // QRectF riseLabelRect = fm.boundingRect( QRectF(0,0,1,1), textFlags, shortMonthName );
355  // QRectF setLabelRect = fm.boundingRect( QRectF(0,0,1,1), textFlags, shortMonthName );
356  //
357  // QDate dt( y, imonth, 15 );
358  // float doy = float( dt.daysInYear() - dt.dayOfYear() );
359  // float xRiseLabel, xSetLabel;
360  // if ( noNight ) {
361  // xRiseLabel = 0.0;
362  // xSetLabel = 0.0;
363  // } else {
364  // xRiseLabel = riseTimeList.at( date ) + 0.6;
365  // xSetLabel = setTimeList.at( date )- 0.6;
366  // }
367  // QPointF pRiseLabel = mapToWidget( QPointF( xRiseLabel, doy ) );
368  // QPointF pSetLabel = mapToWidget( QPointF( xSetLabel, doy ) );
369  //
370  // //Determine rotation angle for month labels
371  // QDate dt1( y, imonth, 1 );
372  // float doy1 = float( dt1.daysInYear() - dt1.dayOfYear() );
373  // QDate dt2( y, imonth, dt1.daysInMonth() );
374  // float doy2 = float( dt2.daysInYear() - dt2.dayOfYear() );
375  //
376  // QPointF p1, p2;
377  // float rAngle, sAngle;
378  // if ( noNight ) {
379  // rAngle = 90.0;
380  // } else {
381  // p1 = mapToWidget( QPointF( riseTimeList.at( date-2 ), doy1 ) );
382  // p2 = mapToWidget( QPointF( riseTimeList.at( date+2 ), doy2 ) );
383  // rAngle = atan2( p2.y() - p1.y(), p2.x() - p1.x() )/dms::DegToRad;
384  //
385  // p1 = mapToWidget( QPointF( setTimeList.at( date-2 ), doy1 ) );
386  // p2 = mapToWidget( QPointF( setTimeList.at( date+2 ), doy2 ) );
387  // sAngle = atan2( p2.y() - p1.y(), p2.x() - p1.x() )/dms::DegToRad;
388  // }
389  //
390  // p->save();
391  // p->translate( pRiseLabel );
392  // p->rotate( rAngle );
393  // p->drawText( riseLabelRect, textFlags, shortMonthName );
394  // p->restore();
395  //
396  // if ( ! noNight ) {
397  // p->save();
398  // p->translate( pSetLabel );
399  // p->rotate( sAngle );
400  // p->drawText( setLabelRect, textFlags, shortMonthName );
401  // p->restore();
402  // }
403  // }
404  // }
405 
406  p->setFont(origFont);
407 }
void append(const T &value)
T & first()
Extension of QDateTime for KStars KStarsDateTime can represent the date/time as a Julian Day,...
AlignHCenter
QTextStream & right(QTextStream &stream)
QWidget * topLevelWidget() const const
void setPen(const QColor &color)
int topPadding() const
void rotate(qreal angle)
void drawRect(const QRectF &rectangle)
int degree() const
Definition: dms.h:116
void setClipRect(const QRectF &rectangle, Qt::ClipOperation operation)
QTextStream & left(QTextStream &stream)
TextDontClip
QPointF mapToWidget(const QPointF &p) const
int pointSize() const const
QRectF dataRect() const
int width() const const
bool isValid() const const
bool antialiasing() const
void draw(QPainter *p, KPlotWidget *pw)
const QPen & pen() const const
void drawText(const QPointF &position, const QString &text)
void fillRect(const QRectF &rectangle, const QBrush &brush)
void drawPolygon(const QPointF *points, int pointCount, Qt::FillRule fillRule)
int secsTo(const QTime &t) const const
QColor backgroundColor() const
bool begin(QPaintDevice *device)
int size() const const
Provides necessary information about the Sun.
Definition: kssun.h:23
QList< KPlotObject * > plotObjects() const
void clear()
QString i18n(const char *text, const TYPE &arg...)
int dayOfYear() const const
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
const T & at(int i) const const
void setAlpha(int alpha)
QString toString(qlonglong i) const const
QDate currentDate()
void setBrush(const QBrush &brush)
T & last()
void setLimits(double x1, double x2, double y1, double y2)
dms transitAltitude(const KStarsDateTime &dt, const GeoLocation *geo) const
Definition: skyobject.cpp:244
int height() const const
QRect pixRect() const
qreal x() const const
qreal y() const const
QDate date() const const
void drawLine(const QLineF &line)
const QFont & font() const const
QColor color() const const
void translate(const QPointF &offset)
void clear()
void restore()
QString toString(Qt::DateFormat format) const const
void save()
void setPixRect()
void setRenderHint(QPainter::RenderHint hint, bool on)
void setFont(const QFont &font)
int daysInYear() const const
QColor foregroundColor() const
void setClipping(bool enable)
darkGreen
int leftPadding() const
This file is part of the KDE documentation.
Documentation copyright © 1996-2022 The KDE developers.
Generated on Fri Aug 19 2022 03:57:49 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.