1 /**********************************************************************************************
2     Copyright (C) 2015 Christian Eichler <code@christian-eichler.de>
3 
4     This program is free software: you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation, either version 3 of the License, or
7     (at your option) any later version.
8 
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13 
14     You should have received a copy of the GNU General Public License
15     along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 
17 **********************************************************************************************/
18 
19 #include "CMainWindow.h"
20 #include "gis/trk/CGisItemTrk.h"
21 #include "gis/trk/CKnownExtension.h"
22 #include "helpers/CDraw.h"
23 #include "widgets/CColorLegend.h"
24 
25 #include <QtWidgets>
26 
CColorLegend(QWidget * parent,CGisItemTrk * trk)27 CColorLegend::CColorLegend(QWidget* parent, CGisItemTrk* trk)
28     : QWidget(parent)
29     , INotifyTrk(CGisItemTrk::eVisualColorLegend)
30     , trk(trk)
31 {
32     colorRect = QRect(0, 0, colorWidth, colorHeight);
33     colorRect.moveCenter(QPoint(xOffset + colorWidth / 2, height() / 2));
34 
35     if(nullptr != trk)
36     {
37         background = true;
38         xOffset = 5;
39 
40         trk->registerVisual(this);
41 
42         // read data from trk
43         CColorLegend::updateData();
44     }
45 }
46 
~CColorLegend()47 CColorLegend::~CColorLegend()
48 {
49     if(trk)
50     {
51         trk->unregisterVisual(this);
52     }
53 }
54 
setMouseFocus(const CTrackData::trkpt_t * pt)55 void CColorLegend::setMouseFocus(const CTrackData::trkpt_t* pt)
56 {
57     if(nullptr == pt)
58     {
59         val = NOFLOAT;
60         return;
61     }
62 
63     QString colorSource = trk->getColorizeSource();
64     auto valueFunc = CKnownExtension::get(colorSource).valueFunc;
65     const qreal factor = CKnownExtension::get(colorSource).factor;
66 
67     val = factor * valueFunc(*pt);
68     val = qMin(val, maximum);
69     val = qMax(val, minimum);
70 }
71 
updateData()72 void CColorLegend::updateData()
73 {
74     if(!trk->getColorizeSource().isEmpty() && (trk->getColorizeSource() != "activity"))
75     {
76         unit = trk->getColorizeUnit();
77         minimum = trk->getColorizeLimitLow();
78         maximum = trk->getColorizeLimitHigh();
79 
80         update();
81         show();
82     }
83     else
84     {
85         hide();
86     }
87 }
88 
setMinimum(qreal min)89 void CColorLegend::setMinimum(qreal min)
90 {
91     minimum = min;
92     update();
93 }
94 
setMaximum(qreal max)95 void CColorLegend::setMaximum(qreal max)
96 {
97     maximum = max;
98     update();
99 }
100 
setUnit(const QString & unit)101 void CColorLegend::setUnit(const QString& unit)
102 {
103     this->unit = unit;
104     update();
105 }
106 
paintLabel(QPainter & p,qreal value)107 int CColorLegend::paintLabel(QPainter& p, qreal value)
108 {
109     const int fontHeight = QFontMetrics(p.font()).ascent() + 1;
110     const qreal relativePos = (value - minimum) / (maximum - minimum);
111     const int posY = colorRect.bottom() + fontHeight / 2 - (2 + colorRect.height()) * relativePos + 1;
112 
113     int posX = xOffset + colorWidth + 3;
114 
115     p.setPen( QPen(QBrush(palette().color(QPalette::Foreground)), 2.) );
116     p.drawLine(posX, posY - fontHeight / 2 + 1, posX + 2, posY - fontHeight / 2 + 1);
117 
118     if(value == minimum || value == maximum
119        || (posY > colorRect.top() + 3 * fontHeight / 2 && posY < colorRect.bottom() - fontHeight / 2))
120     {
121         posX += 5;
122         int precision = !((int)value == value);     // returns 0 or 1
123         const QString& labelText = QString("%1%2").arg(value, 0, 'f', precision).arg(unit);
124 
125         p.drawText(posX, posY, labelText);
126         posX += QFontMetrics(p.font()).width(labelText);
127     }
128 
129     return posX;
130 }
131 
resizeEvent(QResizeEvent * event)132 void CColorLegend::resizeEvent(QResizeEvent* event)
133 {
134     QWidget::resizeEvent(event);
135 
136     colorRect.setHeight(height() - 20);
137     colorRect.moveCenter(QPoint(xOffset + colorWidth / 2, height() / 2));
138     updateGeometry();
139 }
140 
legendRound(qreal value,int powOffset)141 static qreal legendRound(qreal value, int powOffset)
142 {
143     if(value == 0)
144     {
145         return 0;
146     }
147 
148     int l10 = (int) (value > 0) ? log10(value) : log10(-value);
149 
150     qreal div = pow(10, l10 + powOffset);
151     return ceil(value / div) * div;
152 }
153 
paintEvent(QPaintEvent *)154 void CColorLegend::paintEvent(QPaintEvent*/*event*/)
155 {
156     const QFont& font = CMainWindow::self().getMapFont();
157     if(isEnabled())
158     {
159         QPainter p(this);
160         p.setFont(font);
161 
162         if(background)
163         {
164             p.setRenderHint(QPainter::Antialiasing);
165             p.setOpacity(0.6);
166 
167             p.setPen( QPen(QBrush(Qt::darkGray), 2.) );
168             p.setBrush(palette().color(backgroundRole()));
169             p.drawRoundedRect(1, 1, width() - 2, height() - 2, RECT_RADIUS, RECT_RADIUS);
170 
171             p.setOpacity(1.f);
172             p.setRenderHint(QPainter::Antialiasing, false);
173         }
174 
175         // draw the black frame
176         QRect borderRect(colorRect);
177         borderRect += QMargins(1, 1, 1, 1);
178         p.setPen( QPen(
179                       QBrush(palette().color(QPalette::Foreground)),
180                       2.,
181                       Qt::SolidLine,
182                       Qt::SquareCap,
183                       Qt::MiterJoin
184                       )
185                   );
186         p.drawRect(borderRect);
187 
188         // draw the gradient
189         QLinearGradient grad(colorRect.topLeft(), colorRect.bottomLeft());
190         grad.setColorAt(1.00, QColor(  0, 0, 255));   // blue
191         grad.setColorAt(0.60, QColor(  0, 255, 0));   // green
192         grad.setColorAt(0.40, QColor(255, 255, 0));   // yellow
193         grad.setColorAt(0.00, QColor(255, 0, 0));     // red
194         p.fillRect(colorRect, grad);
195 
196         int reqWidth = paintLabel(p, minimum);
197         reqWidth = qMax(paintLabel(p, maximum), reqWidth);
198 
199         // draw values inbetween min/max
200         const qreal delta = maximum - minimum;
201         qint32 step = legendRound(delta / 8, 0);
202         qint32 roundedMinimum = legendRound(minimum, delta > 60 ? -1 : 0);
203 
204         for(qint32 v = roundedMinimum; v < maximum; v += step)
205         {
206             reqWidth = qMax(paintLabel(p, v), reqWidth);
207         }
208 
209         if(reqWidth + 5 != width())
210         {
211             setMinimumWidth(reqWidth + 5);
212             resize(reqWidth + 5, height());
213         }
214 
215         if(val != NOFLOAT)
216         {
217             qreal y = qFloor(colorRect.bottom() - (val - minimum) * (colorRect.height() - 1) / (maximum - minimum));
218             p.setPen(QPen(Qt::darkGray, 2));
219             p.drawLine(colorRect.left() + 2, y, colorRect.right() - 2, y);
220         }
221 
222         p.end();
223     }
224 }
225