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 <kplotobject.h>
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{
63 KSSun thesun;
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.
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);
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)));
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 {
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}
double TZ() const
void draw(QPainter *p, KPlotWidget *pw)
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
dms transitAltitude(const KStarsDateTime &dt, const GeoLocation *geo) 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
int degree() const
Definition dms.h:116
QString i18n(const char *text, const TYPE &arg...)
void setAlpha(int alpha)
QDate addDays(qint64 ndays) const const
QDate currentDate()
int dayOfYear() const const
int daysInYear() const const
QString toString(QStringView format, QCalendar cal) const const
QDate date() const const
int pointSize() 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)
QColor color() const const
qreal x() const const
qreal y() const const
AlignHCenter
darkGreen
TextDontClip
QTextStream & left(QTextStream &stream)
QTextStream & right(QTextStream &stream)
bool isValid(int h, int m, int s, int ms)
int secsTo(QTime t) const const
QWidget * topLevelWidget() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 4 2024 16:38:44 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.