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