Kstars

calendarwidget.cpp
1/*
2 SPDX-FileCopyrightText: 2008 Jason Harris <kstars@30doradus.org>
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
23CalendarWidget::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
35void CalendarWidget::paintEvent(QPaintEvent *e)
36{
37 Q_UNUSED(e)
38
39 QPainter p;
40
41 p.begin(this);
45
46 setPixRect();
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
61void CalendarWidget::setHorizon()
62{
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
158void 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
182 p->drawPolygon(polySunRise);
183 p->drawPolygon(polySunSet);
184}
185
186void CalendarWidget::drawAxes(QPainter *p)
187{
189
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.
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);
246 // Draw big top tick
247 p->drawLine(pTopTick, QPointF(pTopTick.x(), pTopTick.y() + BIGTICKSIZE));
248 r.moveTop(-2.0 * BIGTICKSIZE);
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);
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
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)));
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);
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());
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)),
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);
322 c.setAlpha(255);
323 p->setPen(c);
324 }
325 }
326
327 // Current day
328 if (skycal->scUI->checkBox_GridToday->isChecked())
329 {
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}
QList< KPlotObject * > plotObjects() const
void setPixRect()
int leftPadding() const
QColor foregroundColor() const
QRectF dataRect() const
int topPadding() const
QPointF mapToWidget(const QPointF &p) const
void setLimits(double x1, double x2, double y1, double y2)
QRect pixRect() const
QColor backgroundColor() const
bool antialiasing() const
Child class of KSPlanetBase; encapsulates information about the Sun.
Definition kssun.h:24
Extension of QDateTime for KStars KStarsDateTime can represent the date/time as a Julian Day,...
Draws Rise/Set/Transit curves for major solar system planets for any calendar year.
Definition skycalendar.h:30
QString i18n(const char *text, const TYPE &arg...)
void setAlpha(int alpha)
QDate addDays(qint64 ndays) const const
QDate currentDate()
QDate date() const const
void append(QList< T > &&value)
const_reference at(qsizetype i) const const
void clear()
T & first()
T & last()
qsizetype size() const const
QString toString(QDate date, FormatType format) const const
bool begin(QPaintDevice *device)
void drawLine(const QLine &line)
void drawPolygon(const QPoint *points, int pointCount, Qt::FillRule fillRule)
void drawRect(const QRect &rectangle)
void drawText(const QPoint &position, const QString &text)
void fillRect(const QRect &rectangle, QGradient::Preset preset)
const QFont & font() const const
const QPen & pen() const const
void restore()
void rotate(qreal angle)
void save()
void setBrush(Qt::BrushStyle style)
void setClipRect(const QRect &rectangle, Qt::ClipOperation operation)
void setClipping(bool enable)
void setFont(const QFont &font)
void setPen(Qt::PenStyle style)
void setRenderHint(RenderHint hint, bool on)
void translate(const QPoint &offset)
int height() const const
int width() const const
AlignHCenter
darkGreen
TextDontClip
QTextStream & left(QTextStream &stream)
QTextStream & right(QTextStream &stream)
QWidget * topLevelWidget() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:19:04 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.