1 /**********************************************************************************************
2     Copyright (C) 2014 Oliver Eichler <oliver.eichler@gmx.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 "canvas/CCanvas.h"
20 #include "CMainWindow.h"
21 #include "gis/GeoMath.h"
22 #include "grid/CGrid.h"
23 #include "helpers/CDraw.h"
24 #include "helpers/CSettings.h"
25 #include "map/CMapDraw.h"
26 
27 #include <QtGui>
28 #include <QtWidgets>
29 
CGrid(CMapDraw * map)30 CGrid::CGrid(CMapDraw* map)
31     : QObject(map)
32     , map(map)
33 {
34 }
35 
~CGrid()36 CGrid::~CGrid()
37 {
38 }
39 
convertPos2Str(const QPointF & pos,QString & info,bool simple)40 void CGrid::convertPos2Str(const QPointF& pos, QString& info, bool simple)
41 {
42     if(!proj.isValid())
43     {
44         return;
45     }
46 
47     QPointF pt = pos;
48 
49     pt *= DEG_TO_RAD;
50     proj.transform(pt, PJ_FWD);
51 
52     if(proj.isTarLatLong())
53     {
54         QString lat, lng;
55         pt *= RAD_TO_DEG;
56         lat = pt.y() < 0 ? "S" : "N";
57         lng = pt.x() < 0 ? "W" : "E";
58         if(simple)
59         {
60             info += tr("%1 %2 ").arg(pt.y(), 0, 'f', 6).arg(pt.x(), 0, 'f', 6);
61         }
62         else
63         {
64             info += tr("%1%2%5 %3%4%5 ").arg(lat).arg(qAbs(pt.y()), 0, 'f', 6).arg(lng).arg(qAbs(pt.x()), 0, 'f', 6).arg(QChar('\260'));
65         }
66     }
67     else
68     {
69         if(simple)
70         {
71             info += tr("%1m, %2m ").arg(pt.y(), 0, 'f', 0).arg(pt.x(), 0, 'f', 0);
72         }
73         else
74         {
75             info += tr("N %1m, E %2m ").arg(pt.y(), 0, 'f', 0).arg(pt.x(), 0, 'f', 0);
76         }
77     }
78 }
79 
saveConfig(QSettings & cfg)80 void CGrid::saveConfig(QSettings& cfg)
81 {
82     cfg.setValue("grid/color", color.name());
83     cfg.setValue("grid/proj", proj.getProjTar());
84 }
85 
loadConfig(QSettings & cfg)86 void CGrid::loadConfig(QSettings& cfg)
87 {
88     color = QColor(cfg.value("grid/color", color.name()).toString());
89     setProjAndColor(cfg.value("grid/proj", "EPSG:4326").toString(), color);
90 }
91 
92 
setProjAndColor(const QString & projStr,const QColor & c)93 void CGrid::setProjAndColor(const QString& projStr, const QColor& c)
94 {
95     color = c;
96     proj.init("EPSG:4326", projStr.toLatin1());
97     if(!proj.isValid())
98     {
99         QMessageBox::warning(
100             CMainWindow::self().getBestWidgetForParent(),
101             tr("Grid Projection..."),
102             tr("Failed to setup grid projection. Please configure a valid projection."),
103             QMessageBox::Ok
104             );
105 
106         QTimer::singleShot(1000, &CMainWindow::self(), &CMainWindow::slotSetupGrid);
107     }
108 }
109 
findGridSpace(qreal min,qreal max,qreal & xSpace,qreal & ySpace)110 void CGrid::findGridSpace(qreal min, qreal max, qreal& xSpace, qreal& ySpace)
111 {
112     qreal dX = qAbs(min - max) / 10;
113     if(dX < M_PI / 180000)
114     {
115         xSpace = 5 * M_PI / 1800000;
116         ySpace = 5 * M_PI / 1800000;
117     }
118     else if(dX < M_PI / 18000)
119     {
120         xSpace = 5 * M_PI / 180000;
121         ySpace = 5 * M_PI / 180000;
122     }
123     else if(dX < M_PI / 1800)
124     {
125         xSpace = 5 * M_PI / 18000;
126         ySpace = 5 * M_PI / 18000;
127     }
128     else if(dX < M_PI / 180)
129     {
130         xSpace = 5 * M_PI / 1800;
131         ySpace = 5 * M_PI / 1800;
132     }
133     else if(dX < M_PI / 18)
134     {
135         xSpace = 5 * M_PI / 180;
136         ySpace = 5 * M_PI / 180;
137     }
138     else if(dX < M_PI / 1.8)
139     {
140         xSpace = 5 * M_PI / 180;
141         ySpace = 5 * M_PI / 180;
142     }
143 
144     else if(dX < 3000)
145     {
146         xSpace = 1000;
147         ySpace = 1000;
148     }
149     else if(dX < 7000)
150     {
151         xSpace = 5000;
152         ySpace = 5000;
153     }
154     else if(dX < 30000)
155     {
156         xSpace = 10000;
157         ySpace = 10000;
158     }
159     else if(dX < 70000)
160     {
161         xSpace = 50000;
162         ySpace = 50000;
163     }
164     else if(dX < 300000)
165     {
166         xSpace = 100000;
167         ySpace = 100000;
168     }
169     else if(dX < 700000)
170     {
171         xSpace = 500000;
172         ySpace = 500000;
173     }
174     else if(dX < 3000000)
175     {
176         xSpace = 1000000;
177         ySpace = 1000000;
178     }
179     else if(dX < 7000000)
180     {
181         xSpace = 5000000;
182         ySpace = 5000000;
183     }
184     else if(dX < 30000000)
185     {
186         xSpace = 10000000;
187         ySpace = 10000000;
188     }
189     else if(dX < 70000000)
190     {
191         xSpace = 50000000;
192         ySpace = 50000000;
193     }
194 }
195 
196 
calcIntersection(qreal x1,qreal y1,qreal x2,qreal y2,qreal x3,qreal y3,qreal x4,qreal y4,qreal & x,qreal & y)197 bool CGrid::calcIntersection(qreal x1, qreal y1, qreal x2, qreal y2, qreal x3, qreal y3, qreal x4, qreal y4, qreal& x, qreal& y)
198 {
199     qreal ua = ((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3)) / ((y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1));
200 
201     x = x1 + ua * (x2 - x1);
202     y = y1 + ua * (y2 - y1);
203 
204     qreal d12 = (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1);
205     qreal d1x = (x1 - x) * (x1 - x) + (y1 - y) * (y1 - y);
206     qreal d2x = (x2 - x) * (x2 - x) + (y2 - y) * (y2 - y);
207     qreal d34 = (x4 - x3) * (x4 - x3) + (y4 - y3) * (y4 - y3);
208     qreal d3x = (x3 - x) * (x3 - x) + (y3 - y) * (y3 - y);
209     qreal d4x = (x4 - x) * (x4 - x) + (y4 - y) * (y4 - y);
210 
211     return (d12 >= d1x) && (d12 >= d2x) && (d34 >= d3x) && (d34 >= d4x);
212 }
213 
214 
215 struct val_t
216 {
val_tval_t217     val_t(qint32 pos, qreal val) : pos(pos), val(val)
218     {
219     }
220     qint32 pos;
221     qreal val;
222 };
223 
draw(QPainter & p,const QRect & rect)224 void CGrid::draw(QPainter& p, const QRect& rect)
225 {
226     if(!proj.isValid() || !CMainWindow::self().isGridVisible())
227     {
228         return;
229     }
230 
231     QPointF topLeft = rect.topLeft();
232     QPointF topRight = rect.topRight();
233     QPointF btmLeft = rect.bottomLeft();
234     QPointF btmRight = rect.bottomRight();
235 
236     map->convertPx2Rad(topLeft);
237     map->convertPx2Rad(topRight);
238     map->convertPx2Rad(btmLeft);
239     map->convertPx2Rad(btmRight);
240 
241     proj.transform(topLeft, PJ_FWD);
242     proj.transform(topRight, PJ_FWD);
243     proj.transform(btmLeft, PJ_FWD);
244     proj.transform(btmRight, PJ_FWD);
245 
246     //    qDebug() << "---";
247     //    qDebug() << "topLeft " << topLeft.u  << topLeft.v;
248     //    qDebug() << "topRight" << topRight.u << topRight.v;
249     //    qDebug() << "btmLeft " << btmLeft.u  << btmLeft.v;
250     //    qDebug() << "btmRight" << btmRight.u << btmRight.v;
251 
252     //    qDebug() << topLeft.u - topRight.u;
253     //    qDebug() << btmLeft.u - btmRight.u;
254 
255     //    qDebug() << topLeft.v  - btmLeft.v;
256     //    qDebug() << topRight.v - btmRight.v;
257 
258     qreal topMax = qMax(topLeft.y(), topRight.y());
259     qreal btmMin = qMin(btmLeft.y(), btmRight.y());
260     qreal leftMin = qMin(topLeft.x(), btmLeft.x());
261     qreal rightMax = qMax(topRight.x(), btmRight.x());
262 
263     qreal xGridSpace = 1000;
264     qreal yGridSpace = 1000;
265     findGridSpace(leftMin, rightMax, xGridSpace, yGridSpace);
266 
267     qreal xStart = qFloor(leftMin / xGridSpace) * xGridSpace;
268     qreal yStart = qCeil(topMax / yGridSpace) * yGridSpace;
269 
270     qreal x = xStart - xGridSpace;
271     qreal y = yStart + yGridSpace;
272 
273     if(proj.isTarLatLong())
274     {
275         if(y > (85 * DEG_TO_RAD))
276         {
277             y = (85 * DEG_TO_RAD);
278         }
279         if(btmMin < -(85 * DEG_TO_RAD - yGridSpace))
280         {
281             btmMin = -(85 * DEG_TO_RAD - yGridSpace);
282         }
283 
284         if(x > rightMax)
285         {
286             if(qAbs(x) > qAbs(rightMax))
287             {
288                 xStart = x = -180 * DEG_TO_RAD;
289             }
290             if(qAbs(x) < qAbs(rightMax))
291             {
292                 rightMax = 180 * DEG_TO_RAD;
293             }
294         }
295     }
296 
297     QList< val_t > horzTopTicks;
298     QList< val_t > horzBtmTicks;
299     QList< val_t > vertLftTicks;
300     QList< val_t > vertRgtTicks;
301 
302     p.save();
303     p.setBrush(Qt::NoBrush);
304     p.setPen(QPen(color, 1));
305     USE_ANTI_ALIASING(p, false);
306 
307     qreal h = rect.height();
308     qreal w = rect.width();
309 
310     while(y > btmMin)
311     {
312         while(x < rightMax)
313         {
314             QPointF p1(x, y);
315             QPointF p2(x + xGridSpace, y);
316             QPointF p3(x + xGridSpace, y - yGridSpace);
317             QPointF p4(x, y - yGridSpace);
318 
319 
320             qreal xVal = p1.x();
321             qreal yVal = p1.y();
322 
323             proj.transform(p1, PJ_INV);
324             proj.transform(p2, PJ_INV);
325             proj.transform(p3, PJ_INV);
326             proj.transform(p4, PJ_INV);
327 
328 //            qDebug() << (p1 * RAD_TO_DEG) << (p2 * RAD_TO_DEG) << (p3 * RAD_TO_DEG) << (p4 * RAD_TO_DEG);
329 
330             map->convertRad2Px(p1);
331             map->convertRad2Px(p2);
332             map->convertRad2Px(p3);
333             map->convertRad2Px(p4);
334 
335             qreal xx, yy;
336             if(calcIntersection(0, 0, w, 0, p1.x(), p1.y(), p4.x(), p4.y(), xx, yy))
337             {
338                 horzTopTicks << val_t(xx, xVal);
339             }
340             if(calcIntersection(0, h, w, h, p1.x(), p1.y(), p4.x(), p4.y(), xx, yy))
341             {
342                 horzBtmTicks << val_t(xx, xVal);
343             }
344             if(calcIntersection(0, 0, 0, h, p1.x(), p1.y(), p2.x(), p2.y(), xx, yy))
345             {
346                 vertLftTicks << val_t(yy, yVal);
347             }
348             if(calcIntersection(w, 0, w, h, p1.x(), p1.y(), p2.x(), p2.y(), xx, yy))
349             {
350                 vertRgtTicks << val_t(yy, yVal);
351             }
352 
353             p.drawLine(p1, p2);
354             p.drawLine(p2, p3);
355             p.drawLine(p3, p4);
356             p.drawLine(p4, p1);
357 
358             x += xGridSpace;
359         }
360         x = xStart;
361         y -= yGridSpace;
362     }
363     USE_ANTI_ALIASING(p, true);
364     p.restore();
365 
366     QColor textColor;
367     textColor.setHsv(color.hslHue(), color.hsvSaturation(), (color.value() > 128 ? color.value() - 128 : 0));
368 
369     if(proj.isTarLatLong())
370     {
371         QFontMetrics fm(CMainWindow::self().getMapFont());
372         int yoff = fm.height() + fm.ascent();
373         int xoff = fm.width("XX.XXXX") >> 1;
374 
375         for(const val_t& val : qAsConst(horzTopTicks))
376         {
377             CDraw::text(qAbs(val.val) < 1.e-5 ? "0" : QString("%1%2").arg(val.val * RAD_TO_DEG).arg(QChar(0260)), p, QPoint(val.pos, yoff), textColor);
378         }
379 
380         for(const val_t& val : qAsConst(horzBtmTicks))
381         {
382             CDraw::text(qAbs(val.val) < 1.e-5 ? "0" : QString("%1%2").arg(val.val * RAD_TO_DEG).arg(QChar(0260)), p, QPoint(val.pos, h), textColor);
383         }
384 
385         for(const val_t& val : qAsConst(vertLftTicks))
386         {
387             CDraw::text(qAbs(val.val) < 1.e-5 ? "0" : QString("%1%2").arg(val.val * RAD_TO_DEG).arg(QChar(0260)), p, QPoint(xoff, val.pos), textColor);
388         }
389 
390         for(const val_t& val : qAsConst(vertRgtTicks))
391         {
392             CDraw::text(qAbs(val.val) < 1.e-5 ? "0" : QString("%1%2").arg(val.val * RAD_TO_DEG).arg(QChar(0260)), p, QPoint(w - xoff, val.pos), textColor);
393         }
394     }
395     else
396     {
397         QFontMetrics fm(CMainWindow::self().getMapFont());
398         int yoff = fm.height() + fm.ascent();
399         int xoff = fm.width("XXXX") >> 1;
400 
401         for(const val_t& val : qAsConst(horzTopTicks))
402         {
403             CDraw::text(QString("%1").arg(qint32(val.val / 1000)), p, QPoint(val.pos, yoff), textColor);
404         }
405 
406         for(const val_t& val : qAsConst(horzBtmTicks))
407         {
408             CDraw::text(QString("%1").arg(qint32(val.val / 1000)), p, QPoint(val.pos, h), textColor);
409         }
410 
411         for(const val_t& val : qAsConst(vertLftTicks))
412         {
413             CDraw::text(QString("%1").arg(qint32(val.val / 1000)), p, QPoint(xoff, val.pos), textColor);
414         }
415 
416         for(const val_t& val : qAsConst(vertRgtTicks))
417         {
418             CDraw::text(QString("%1").arg(qint32(val.val / 1000)), p, QPoint(w - xoff, val.pos), textColor);
419         }
420     }
421 }
422