1 #include "Global.h"
2
3 #include <errno.h>
4
5 #include "MapView.h"
6 #include "MainWindow.h"
7 #include "PropertiesDock.h"
8 #include "Document.h"
9 #include "ILayer.h"
10 #include "LayerIterator.h"
11 #include "ImageMapLayer.h"
12 #include "IMapAdapter.h"
13 #include "IMapWatermark.h"
14 #include "Feature.h"
15 #include "Interaction.h"
16 #include "IPaintStyle.h"
17 #include "Projection.h"
18 #include "qgps.h"
19 #include "qgpsdevice.h"
20
21 #include "OsmRenderLayer.h"
22
23 #ifdef USE_WEBKIT
24 #include "browserimagemanager.h"
25 #endif
26 #include "MerkaartorPreferences.h"
27 #include "SvgCache.h"
28
29 #include <QTime>
30 #include <QMainWindow>
31 #include <QMouseEvent>
32 #include <QPainter>
33 #include <QStatusBar>
34 #include <QToolTip>
35 #include <QMap>
36 #include <QSet>
37 #include <QtConcurrentMap>
38
39 // from wikipedia
40 #define EQUATORIALRADIUS 6378137.0
41 #define LAT_ANG_PER_M 1.0 / EQUATORIALRADIUS
42 #define TEST_RFLAGS(x) p->ROptions.options.testFlag(x)
43
44 class MapViewPrivate
45 {
46 public:
47 QTransform theTransform;
48 QTransform theInvertedTransform;
49 qreal PixelPerM;
50 qreal NodeWidth;
51 int ZoomLevel;
52 CoordBox Viewport;
53 QList<CoordBox> invalidRects;
54 QPoint theVectorPanDelta;
55 qreal theVectorRotation;
56 QList<Node*> theVirtualNodes;
57 RendererOptions ROptions;
58
59 Projection theProjection;
60 Document* theDocument;
61 Interaction* theInteraction;
62
63 bool BackgroundOnlyPanZoom;
64 QTransform BackgroundOnlyVpTransform;
65
66 QLabel *TL, *TR, *BL, *BR;
67
68 OsmRenderLayer* osmLayer;
69
MapViewPrivate()70 MapViewPrivate()
71 : PixelPerM(0.0), Viewport(WORLD_COORDBOX), theVectorRotation(0.0)
72 , BackgroundOnlyPanZoom(false)
73 , theDocument(0)
74 , theInteraction(0)
75 {}
76 };
77
78 /*********************/
79
MapView(QWidget * parent)80 MapView::MapView(QWidget* parent) :
81 QWidget(parent), Main(dynamic_cast<MainWindow*>(parent)), StaticBackground(0)
82 , StaticWireframe(0), StaticTouchup(0)
83 , SelectionLocked(false),lockIcon(0)
84 , p(new MapViewPrivate)
85 {
86 installEventFilter(Main);
87
88 setMouseTracking(true);
89 setAttribute(Qt::WA_NoSystemBackground);
90 setContextMenuPolicy(Qt::CustomContextMenu);
91 setFocusPolicy(Qt::ClickFocus);
92 setAcceptDrops(true);
93
94 MoveRightShortcut = new QShortcut(QKeySequence(Qt::Key_Right), this);
95 connect(MoveRightShortcut, SIGNAL(activated()), this, SLOT(on_MoveRight_activated()));
96 MoveLeftShortcut = new QShortcut(QKeySequence(Qt::Key_Left), this);
97 connect(MoveLeftShortcut, SIGNAL(activated()), this, SLOT(on_MoveLeft_activated()));
98 MoveUpShortcut = new QShortcut(QKeySequence(Qt::Key_Up), this);
99 connect(MoveUpShortcut, SIGNAL(activated()), this, SLOT(on_MoveUp_activated()));
100 MoveDownShortcut = new QShortcut(QKeySequence(Qt::Key_Down), this);
101 connect(MoveDownShortcut, SIGNAL(activated()), this, SLOT(on_MoveDown_activated()));
102 ZoomInShortcut = new QShortcut(QKeySequence(Qt::Key_PageUp), this);
103 ZoomInShortcut->setContext(Qt::WidgetShortcut);
104 connect(ZoomInShortcut, SIGNAL(activated()), this, SLOT(zoomIn()));
105 ZoomOutShortcut = new QShortcut(QKeySequence(Qt::Key_PageDown), this);
106 ZoomOutShortcut->setContext(Qt::WidgetShortcut);
107 connect(ZoomOutShortcut, SIGNAL(activated()), this, SLOT(zoomOut()));
108
109 QVBoxLayout* vlay = new QVBoxLayout(this);
110
111 QHBoxLayout* hlay1 = new QHBoxLayout();
112 p->TL = new QLabel(this);
113 hlay1->addWidget(p->TL);
114 QSpacerItem* horizontalSpacer = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
115 hlay1->addItem(horizontalSpacer);
116 p->TR = new QLabel(this);
117 hlay1->addWidget(p->TR);
118 vlay->addLayout(hlay1);
119
120 QSpacerItem* verticalSpacer = new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding);
121 vlay->addItem(verticalSpacer);
122
123 QHBoxLayout* hlay2 = new QHBoxLayout();
124 p->BL = new QLabel(this);
125 hlay2->addWidget(p->BL);
126 QSpacerItem* horizontalSpacer2 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
127 hlay2->addItem(horizontalSpacer2);
128 p->BR = new QLabel(this);
129 hlay2->addWidget(p->BR);
130 vlay->addLayout(hlay2);
131
132 p->TL->setVisible(false);
133 p->TR->setVisible(false);
134 p->BL->setVisible(false);
135 p->BR->setVisible(false);
136
137
138 p->osmLayer = new OsmRenderLayer(this);
139 connect(p->osmLayer, SIGNAL(renderingDone()), SLOT(renderingDone()));
140 }
141
~MapView()142 MapView::~MapView()
143 {
144 delete StaticBackground;
145 delete StaticWireframe;
146 delete StaticTouchup;
147 delete p;
148 }
149
main()150 MainWindow *MapView::main()
151 {
152 return Main;
153 }
154
setDocument(Document * aDoc)155 void MapView::setDocument(Document* aDoc)
156 {
157 p->theDocument = aDoc;
158 p->osmLayer->setDocument(aDoc);
159
160 setViewport(viewport(), rect());
161 }
162
document()163 Document *MapView::document()
164 {
165 return p->theDocument;
166 }
167
invalidate(bool updateWireframe,bool updateOsmMap,bool updateBgMap)168 void MapView::invalidate(bool updateWireframe, bool updateOsmMap, bool updateBgMap)
169 {
170 if (updateOsmMap) {
171 if (!M_PREFS->getWireframeView()) {
172 if (!TEST_RFLAGS(RendererOptions::Interacting))
173 p->osmLayer->forceRedraw(p->theProjection, p->theTransform, rect(), p->PixelPerM, p->ROptions);
174 else if (M_PREFS->getEditRendering() == 2)
175 p->osmLayer->forceRedraw(p->theProjection, p->theTransform, rect(), p->PixelPerM, p->ROptions);
176 }
177 }
178 if (updateWireframe) {
179 p->invalidRects.clear();
180 p->invalidRects.push_back(p->Viewport);
181
182 p->theVectorPanDelta = QPoint(0, 0);
183 SAFE_DELETE(StaticBackground)
184 }
185 if (p->theDocument && updateBgMap) {
186 IMapWatermark* WatermarkAdapter = NULL;
187 for (LayerIterator<ImageMapLayer*> ImgIt(p->theDocument); !ImgIt.isEnd(); ++ImgIt) {
188 if (ImgIt.get()->isVisible()) {
189 ImgIt.get()->forceRedraw(*this, p->BackgroundOnlyVpTransform, rect());
190 WatermarkAdapter = qobject_cast<IMapWatermark*>(ImgIt.get()->getMapAdapter());
191 }
192 }
193 p->BackgroundOnlyVpTransform = QTransform();
194
195 if (WatermarkAdapter) {
196 p->TL->setAttribute(Qt::WA_NoMousePropagation);
197 p->TL->setOpenExternalLinks(true);
198 p->TL->setText(WatermarkAdapter->getLogoHtml());
199 // p->lblLogo->move(10, 10);
200 p->TL->show();
201
202 p->BR->setAttribute(Qt::WA_NoMousePropagation);
203 p->BR->setOpenExternalLinks(true);
204 p->BR->setWordWrap(true);
205 p->BR->setText(WatermarkAdapter->getAttributionsHtml(p->Viewport, rect()));
206 p->BR->setMinimumWidth(150);
207 p->BR->setMaximumWidth(200);
208 p->BR->setMaximumHeight(50);
209 p->BR->show();
210 } else {
211 p->TL->setVisible(false);
212 p->BR->setVisible(false);
213 }
214 }
215 update();
216 }
217
panScreen(QPoint delta)218 void MapView::panScreen(QPoint delta)
219 {
220 Coord cDelta = fromView(delta) - fromView(QPoint(0, 0));
221 if (p->BackgroundOnlyPanZoom) {
222 p->BackgroundOnlyVpTransform.translate(-cDelta.x(), -cDelta.y());
223 } else {
224 p->theVectorPanDelta += delta;
225
226 CoordBox r1, r2;
227 if (delta.x()) {
228 if (delta.x() < 0)
229 r1 = CoordBox(p->Viewport.bottomRight(), Coord(p->Viewport.topRight().x() - cDelta.x(), p->Viewport.topRight().y())); // OK
230 else
231 r1 = CoordBox(Coord(p->Viewport.bottomLeft().x() - cDelta.x(), p->Viewport.bottomLeft().y()), p->Viewport.topLeft()); // OK
232 p->invalidRects.push_back(r1);
233 }
234 if (delta.y()) {
235 if (delta.y() < 0)
236 r2 = CoordBox(Coord(p->Viewport.bottomLeft().x(), p->Viewport.bottomLeft().y() - cDelta.y()), p->Viewport.bottomRight()); // OK
237 else
238 r2 = CoordBox(p->Viewport.topLeft(), Coord( p->Viewport.bottomRight().x(), p->Viewport.topRight().y() - cDelta.y())); //NOK
239 p->invalidRects.push_back(r2);
240 }
241
242 p->theTransform.translate((qreal)(delta.x())/p->theTransform.m11(), (qreal)(delta.y())/p->theTransform.m22());
243 p->theInvertedTransform = p->theTransform.inverted();
244 viewportRecalc(rect());
245 if (!M_PREFS->getWireframeView() && p->theDocument) {
246 p->osmLayer->pan(delta);
247 }
248 }
249
250 for (LayerIterator<ImageMapLayer*> ImgIt(p->theDocument); !ImgIt.isEnd(); ++ImgIt)
251 ImgIt.get()->pan(delta);
252 update();
253 }
254
rotateScreen(QPoint,qreal angle)255 void MapView::rotateScreen(QPoint /* center */, qreal angle)
256 {
257 p->theVectorRotation += angle;
258
259 transformCalc(p->theTransform, p->theProjection, p->theVectorRotation, p->Viewport, rect());
260 p->theInvertedTransform = p->theTransform.inverted();
261 viewportRecalc(rect());
262 // p->invalidRects.clear();
263 // p->invalidRects.push_back(p->Viewport);
264
265 // for (LayerIterator<ImageMapLayer*> ImgIt(p->theDocument); !ImgIt.isEnd(); ++ImgIt)
266 // ImgIt.get()->pan(delta);
267 update();
268 }
269
paintEvent(QPaintEvent * anEvent)270 void MapView::paintEvent(QPaintEvent * anEvent)
271 {
272 if (!p->theDocument)
273 return;
274
275 #ifndef NDEBUG
276 QTime Start(QTime::currentTime());
277 #endif
278
279 QPainter P;
280 P.begin(this);
281
282 updateStaticBackground();
283
284 P.drawPixmap(p->theVectorPanDelta, *StaticBackground);
285 P.save();
286 QTransform AlignTransform;
287 for (LayerIterator<ImageMapLayer*> ImgIt(p->theDocument); !ImgIt.isEnd(); ++ImgIt) {
288 if (ImgIt.get()->isVisible()) {
289 ImgIt.get()->drawImage(&P);
290 AlignTransform = ImgIt.get()->getCurrentAlignmentTransform();
291 }
292 }
293 P.restore();
294
295 if (!p->invalidRects.isEmpty()) {
296 updateWireframe();
297 }
298 if (M_PREFS->getWireframeView() || !p->osmLayer->isRenderingDone() || M_PREFS->getEditRendering() == 1)
299 P.drawPixmap(p->theVectorPanDelta, *StaticWireframe);
300 if (!M_PREFS->getWireframeView())
301 if (!(TEST_RFLAGS(RendererOptions::Interacting) && M_PREFS->getEditRendering() == 1))
302 drawFeatures(P);
303 P.drawPixmap(p->theVectorPanDelta, *StaticTouchup);
304
305
306 drawLatLonGrid(P);
307 drawDownloadAreas(P);
308 drawScale(P);
309
310 if (p->theInteraction) {
311 P.setRenderHint(QPainter::Antialiasing);
312 p->theInteraction->paintEvent(anEvent, P);
313 }
314
315 if (Main)
316 drawGPS(P);
317
318 P.end();
319
320 #ifndef _MOBILE
321 if (Main) {
322 QString vpLabel = QString("%1,%2,%3,%4")
323 .arg(viewport().bottomLeft().x(),0,'f',4)
324 .arg(viewport().bottomLeft().y(),0, 'f',4)
325 .arg(viewport().topRight().x(),0, 'f',4)
326 .arg(viewport().topRight().y(),0,'f',4)
327 ;
328 if (!p->theProjection.projIsLatLong()) {
329 QRectF pVp = p->theProjection.toProjectedRectF(viewport(), rect());
330 vpLabel += " / " + QString("%1,%2,%3,%4")
331 .arg(pVp.bottomLeft().x(),0,'f',4)
332 .arg(pVp.bottomLeft().y(),0, 'f',4)
333 .arg(pVp.topRight().x(),0, 'f',4)
334 .arg(pVp.topRight().y(),0,'f',4)
335 ;
336 }
337 Main->ViewportStatusLabel->setText(vpLabel);
338
339 Main->MeterPerPixelLabel->setText(tr("%1 m/pixel").arg(1/p->PixelPerM, 0, 'f', 2));
340 if (!AlignTransform.isIdentity()) {
341 QLineF l(0, 0, AlignTransform.dx(), AlignTransform.dy());
342 l.translate(viewport().center());
343 Main->AdjusmentMeterLabel->setVisible(true);
344 qreal distance = Coord(l.p2()).distanceFrom(Coord(l.p1()))*1000;
345 Main->AdjusmentMeterLabel->setText(tr("Align: %1m @ %2").arg(distance, 0, 'f', 2).arg(l.angle(), 0, 'f', 2) + QString::fromUtf8("°"));
346 } else {
347 Main->AdjusmentMeterLabel->setVisible(false);
348 }
349 #ifndef NDEBUG
350 QTime Stop(QTime::currentTime());
351 Main->PaintTimeLabel->setText(tr("%1ms").arg(Start.msecsTo(Stop)));
352 #endif
353 }
354 #endif
355 }
356
drawScale(QPainter & P)357 void MapView::drawScale(QPainter & P)
358 {
359
360 if (!TEST_RFLAGS(RendererOptions::ScaleVisible))
361 return;
362
363 errno = 0;
364 qreal Log = log10(200./p->PixelPerM);
365 if (errno != 0)
366 return;
367
368 qreal RestLog = Log-floor(Log);
369 if (RestLog < log10(2.))
370 Log = floor(Log);
371 else if (RestLog < log10(5.))
372 Log = floor(Log)+log10(2.);
373 else
374 Log = floor(Log)+log10(5.);
375
376 qreal Length = pow(10.,Log);
377 QPointF P1(20,height()-20);
378 QPointF P2(20+Length*p->PixelPerM,height()-20);
379 P.fillRect(P1.x()-4, P1.y()-20-4, P2.x() - P1.x() + 4, 33, QColor(255, 255, 255, 128));
380 P.setPen(QPen(QColor(0,0,0),2));
381 P.drawLine(P1-QPointF(0,5),P1+QPointF(0,5));
382 P.drawLine(P1,P2);
383 if (Length < 1000)
384 P.drawText(QRectF(P2-QPoint(200,40),QSize(200,30)),Qt::AlignRight | Qt::AlignBottom, QString(tr("%1 m")).arg(Length, 0, 'f', 0));
385 else
386 P.drawText(QRectF(P2-QPoint(200,40),QSize(200,30)),Qt::AlignRight | Qt::AlignBottom, QString(tr("%1 km")).arg(Length/1000, 0, 'f', 0));
387
388 P.drawLine(P2-QPointF(0,5),P2+QPointF(0,5));
389 }
390
drawGPS(QPainter & P)391 void MapView::drawGPS(QPainter & P)
392 {
393 if (Main->gps() && Main->gps()->getGpsDevice()) {
394 if (Main->gps()->getGpsDevice()->fixStatus() == QGPSDevice::StatusActive) {
395 Coord vp(Main->gps()->getGpsDevice()->longitude(), Main->gps()->getGpsDevice()->latitude());
396 QPoint g = toView(vp);
397 QImage* pm = getSVGImageFromFile(":/Gps/Gps_Marker.svg", 32);
398 P.drawImage(g - QPoint(16, 16), *pm);
399 }
400 }
401 }
402
testColor(const QImage & theImage,const QPoint & P,const QRgb & targetColor)403 bool testColor(const QImage& theImage, const QPoint& P, const QRgb& targetColor)
404 {
405 if (!theImage.rect().contains(P)) return false;
406 return (theImage.pixel(P) == targetColor);
407 }
408
floodFill(QImage & theImage,const QPoint & P,const QRgb & targetColor,const QRgb & replaceColor)409 void floodFill(QImage& theImage, const QPoint& P, const QRgb& targetColor, const QRgb& replaceColor)
410 {
411 if (!testColor(theImage, P, targetColor)) return;
412
413 QStack<QPoint> theStack;
414 QPoint aP;
415 QPainter theP(&theImage);
416 theP.setPen(QPen(QColor::fromRgb(replaceColor), 1));
417 theP.setBrush(Qt::NoBrush);
418
419 theStack.push(P);
420 while (!theStack.isEmpty()) {
421 aP = theStack.pop();
422 QPoint W = aP;
423 QPoint E = aP;
424 if (testColor(theImage, aP + QPoint(0, 1), targetColor))
425 theStack.push(aP + QPoint(0, 1));
426 if (testColor(theImage, aP + QPoint(0, -1), targetColor))
427 theStack.push(aP + QPoint(0, -1));
428 while (testColor(theImage, W + QPoint(-1, 0),targetColor) && W.x() > 0) {
429 W += QPoint(-1, 0);
430 if (testColor(theImage, W + QPoint(0, 1), targetColor))
431 theStack.push(W + QPoint(0, 1));
432 if (testColor(theImage, W + QPoint(0, -1), targetColor))
433 theStack.push(W + QPoint(0, -1));
434 }
435 while (testColor(theImage, E + QPoint(1, 0), targetColor) && E.x() < theImage.width()-1) {
436 E += QPoint(1, 0);
437 if (testColor(theImage, E + QPoint(0, 1), targetColor))
438 theStack.push(E + QPoint(0, 1));
439 if (testColor(theImage, E + QPoint(0, -1), targetColor))
440 theStack.push(E + QPoint(0, -1));
441 }
442 theP.drawLine(W, E);
443 }
444 }
445
drawLatLonGrid(QPainter & P)446 void MapView::drawLatLonGrid(QPainter & P)
447 {
448 if (!TEST_RFLAGS(RendererOptions::LatLonGridVisible))
449 return;
450
451 QPointF origin(0., 0.);
452 QPoint p1 = toView(origin);
453 QPointF p2 = fromView(QPoint(p1.x()+width(), p1.y()-height()));
454 CoordBox adjViewport(origin, p2);
455 qreal lonInterval = adjViewport.lonDiff() / 4;
456 qreal latInterval = adjViewport.latDiff() / 4;
457
458 int prec = log10(lonInterval);
459 if (!lonInterval || !latInterval) return; // avoid divide-by-zero
460 qreal lonStart = qMax(int((p->Viewport.bottomLeft().x() - origin.x()) / lonInterval) * lonInterval, -COORD_MAX);
461 if (lonStart != -COORD_MAX) {
462 lonStart -= origin.x();
463 if (lonStart<1)
464 lonStart -= lonInterval;
465 }
466 qreal latStart = qMax(int(p->Viewport.bottomLeft().y() / latInterval) * latInterval, -COORD_MAX/2);
467 if (latStart != -COORD_MAX/2) {
468 latStart -= origin.y();
469 if (latStart<1)
470 latStart -= lonInterval;
471 }
472
473 QList<QPolygonF> medianLines;
474 QList<QPolygonF> parallelLines;
475
476 for (qreal y=latStart; y<=p->Viewport.topLeft().y()+latInterval; y+=latInterval) {
477 QPolygonF l;
478 for (qreal x=lonStart; x<=p->Viewport.bottomRight().x()+lonInterval; x+=lonInterval) {
479 QPointF pt = p->theProjection.project(Coord(qMin(x, COORD_MAX), qMin(y, COORD_MAX/2)));
480 l << pt;
481 }
482 parallelLines << l;
483 }
484 for (qreal x=lonStart; x<=p->Viewport.bottomRight().x()+lonInterval; x+=lonInterval) {
485 QPolygonF l;
486 for (qreal y=latStart; y<=p->Viewport.topLeft().y()+latInterval; y+=latInterval) {
487 QPointF pt = p->theProjection.project(Coord(qMin(x, COORD_MAX), qMin(y, COORD_MAX/2)));
488 l << pt;
489 }
490 medianLines << l;
491 }
492
493 P.save();
494 P.setRenderHint(QPainter::Antialiasing);
495 P.setPen(QColor(180, 217, 255));
496 QLineF lb = QLineF(rect().topLeft(), rect().bottomLeft());
497 QLineF lt = QLineF(rect().topLeft(), rect().topRight());
498 QLineF l;
499 for (int i=0; i<parallelLines.size(); ++i) {
500
501 if (parallelLines[i].size() == 0)
502 continue;
503
504 P.drawPolyline(p->theTransform.map(parallelLines[i]));
505 int k=0;
506 QPointF pt;
507 while (k < parallelLines.at(i).size()-2) {
508 l = QLineF(p->theTransform.map(parallelLines.at(i).at(k)), p->theTransform.map(parallelLines.at(i).at(k+1)));
509 if (l.intersect(lb, &pt) == QLineF::BoundedIntersection)
510 break;
511 ++k;
512 }
513 if (pt.isNull())
514 continue;
515 // QPoint pt = QPoint(0, p->theTransform.map(parallelLines.at(i).at(0)).y());
516 QPoint ptt = pt.toPoint() + QPoint(5, -5);
517 P.drawText(ptt, QString("%1").arg(p->theProjection.inverse(parallelLines.at(i).at(0)).y(), 0, 'f', 2-prec));
518 }
519 for (int i=0; i<medianLines.size(); ++i) {
520
521 if (medianLines[i].size() == 0)
522 continue;
523
524 P.drawPolyline(p->theTransform.map(medianLines[i]));
525 int k=0;
526 QPointF pt;
527 while (k < medianLines.at(i).size()-2) {
528 l = QLineF(p->theTransform.map(medianLines.at(i).at(k)), p->theTransform.map(medianLines.at(i).at(k+1)));
529 if (l.intersect(lt, &pt) == QLineF::BoundedIntersection)
530 break;
531 ++k;
532 }
533 if (pt.isNull())
534 continue;
535 // QPoint pt = QPoint(p->theTransform.map(medianLines.at(i).at(0)).x(), 0);
536 QPoint ptt = pt.toPoint() + QPoint(5, 10);
537 P.drawText(ptt, QString("%1").arg(p->theProjection.inverse(medianLines.at(i).at(0)).x(), 0, 'f', 2-prec));
538 }
539
540 P.restore();
541 }
542
drawFeaturesSync(QPainter & P)543 void MapView::drawFeaturesSync(QPainter & P) {
544 while (!p->osmLayer->isRenderingDone());
545 p->osmLayer->drawImage(&P);
546 }
547
drawFeatures(QPainter & P)548 void MapView::drawFeatures(QPainter & P)
549 {
550 p->osmLayer->drawImage(&P);
551 }
552
drawDownloadAreas(QPainter & P)553 void MapView::drawDownloadAreas(QPainter & P)
554 {
555 if (!TEST_RFLAGS(RendererOptions::DownloadedVisible))
556 return;
557
558 P.save();
559 QRegion r(0, 0, width(), height());
560
561
562 //QBrush b(Qt::red, Qt::DiagCrossPattern);
563 QBrush b(Qt::red, Qt::Dense7Pattern);
564
565 QList<CoordBox> db = p->theDocument->getDownloadBoxes();
566 QList<CoordBox>::const_iterator bb;
567 for (bb = db.constBegin(); bb != db.constEnd(); ++bb) {
568 if (viewport().disjunctFrom(*bb)) continue;
569 QPolygonF poly;
570 poly << projection().project((*bb).topLeft());
571 poly << projection().project((*bb).bottomLeft());
572 poly << projection().project((*bb).bottomRight());
573 poly << projection().project((*bb).topRight());
574 poly << projection().project((*bb).topLeft());
575
576 r -= QRegion(p->theTransform.map(poly.toPolygon()));
577 }
578
579 P.setClipRegion(r);
580 P.setClipping(true);
581 P.fillRect(rect(), b);
582
583 P.restore();
584 }
585
updateStaticBackground()586 void MapView::updateStaticBackground()
587 {
588 if (!StaticBackground || (StaticBackground->size() != size()))
589 {
590 delete StaticBackground;
591 StaticBackground = new QPixmap(size());
592
593 if (M_PREFS->getUseShapefileForBackground())
594 StaticBackground->fill(M_PREFS->getWaterColor());
595 else if (M_PREFS->getBackgroundOverwriteStyle())
596 StaticBackground->fill(M_PREFS->getBgColor());
597 else if (M_STYLE->getGlobalPainter().getDrawBackground())
598 StaticBackground->fill(M_STYLE->getGlobalPainter().getBackgroundColor());
599 else
600 StaticBackground->fill(M_PREFS->getBgColor());
601 }
602 }
603
updateWireframe()604 void MapView::updateWireframe()
605 {
606 QMap<RenderPriority, QSet <Feature*> > theFeatures;
607 QMap<RenderPriority, QSet<Feature*> >::const_iterator itm;
608 QSet<Feature*>::const_iterator it;
609
610 QPainter P;
611
612 for (int i=0; i<p->theDocument->layerSize(); ++i)
613 g_backend.getFeatureSet(p->theDocument->getLayer(i), theFeatures, p->invalidRects, p->theProjection);
614
615 if (!p->theVectorPanDelta.isNull()) {
616 QRegion exposed;
617 StaticWireframe->scroll(p->theVectorPanDelta.x(), p->theVectorPanDelta.y(), StaticWireframe->rect(), &exposed);
618 P.begin(StaticWireframe);
619 P.setClipping(true);
620 P.setClipRegion(exposed);
621 P.setCompositionMode(QPainter::CompositionMode_Source);
622 P.fillRect(StaticWireframe->rect(), Qt::transparent);
623 } else {
624 StaticWireframe->fill(Qt::transparent);
625 P.begin(StaticWireframe);
626 P.setClipping(true);
627 P.setClipRegion(rect());
628 }
629
630 if (M_PREFS->getWireframeView() || !p->osmLayer->isRenderingDone() || M_PREFS->getEditRendering() == 1) {
631 if (M_PREFS->getWireframeView() && M_PREFS->getUseAntiAlias())
632 P.setRenderHint(QPainter::Antialiasing);
633 else if (M_PREFS->getEditRendering() == 1)
634 P.setRenderHint(QPainter::Antialiasing);
635 for (itm = theFeatures.constBegin() ;itm != theFeatures.constEnd(); ++itm)
636 {
637 for (it = itm.value().constBegin() ;it != itm.value().constEnd(); ++it)
638 {
639 qreal alpha = (*it)->getAlpha();
640 P.setOpacity(alpha);
641
642 (*it)->drawSimple(P, this);
643 }
644 }
645 }
646 P.end();
647
648 if (!p->theVectorPanDelta.isNull()) {
649 QRegion exposed;
650 StaticTouchup->scroll(p->theVectorPanDelta.x(), p->theVectorPanDelta.y(), StaticTouchup->rect(), &exposed);
651 P.begin(StaticTouchup);
652 P.setClipping(true);
653 P.setClipRegion(exposed);
654 P.setCompositionMode(QPainter::CompositionMode_Source);
655 P.fillRect(StaticTouchup->rect(), Qt::transparent);
656 } else {
657 StaticTouchup->fill(Qt::transparent);
658 P.begin(StaticTouchup);
659 P.setClipping(true);
660 P.setClipRegion(rect());
661 }
662
663 P.setRenderHint(QPainter::Antialiasing);
664
665 for (itm = theFeatures.constBegin() ;itm != theFeatures.constEnd(); ++itm)
666 {
667 for (it = itm.value().constBegin() ;it != itm.value().constEnd(); ++it)
668 {
669 qreal alpha = (*it)->getAlpha();
670 P.setOpacity(alpha);
671
672 (*it)->drawTouchup(P, this);
673 }
674 }
675 P.end();
676
677 p->invalidRects.clear();
678 p->theVectorPanDelta = QPoint(0, 0);
679
680
681 // QPainter P;
682
683 // StaticBuffer->fill(Qt::transparent);
684 // P.begin(StaticBuffer);
685 // P.setCompositionMode(QPainter::CompositionMode_Source);
686 // P.setClipping(true);
687 // P.setClipRegion(rect());
688 //// if (M_PREFS->getUseAntiAlias())
689 //// P.setRenderHint(QPainter::Antialiasing);
690 // drawFeatures(P);
691 // P.end();
692 }
693
mousePressEvent(QMouseEvent * anEvent)694 void MapView::mousePressEvent(QMouseEvent* anEvent)
695 {
696 if (p->theInteraction)
697 if (anEvent->button())
698 p->theInteraction->mousePressEvent(anEvent);
699 }
700
mouseReleaseEvent(QMouseEvent * anEvent)701 void MapView::mouseReleaseEvent(QMouseEvent* anEvent)
702 {
703 if (p->theInteraction)
704 p->theInteraction->mouseReleaseEvent(anEvent);
705
706 }
707
mouseMoveEvent(QMouseEvent * anEvent)708 void MapView::mouseMoveEvent(QMouseEvent* anEvent)
709 {
710 if (p->theInteraction)
711 p->theInteraction->mouseMoveEvent(anEvent);
712 }
713
mouseDoubleClickEvent(QMouseEvent * anEvent)714 void MapView::mouseDoubleClickEvent(QMouseEvent* anEvent)
715 {
716 if (p->theInteraction)
717 p->theInteraction->mouseDoubleClickEvent(anEvent);
718 }
719
wheelEvent(QWheelEvent * anEvent)720 void MapView::wheelEvent(QWheelEvent* anEvent)
721 {
722 if (p->theInteraction)
723 p->theInteraction->wheelEvent(anEvent);
724 }
725
interaction()726 Interaction *MapView::interaction()
727 {
728 return p->theInteraction;
729 }
730
setInteraction(Interaction * anInteraction)731 void MapView::setInteraction(Interaction *anInteraction)
732 {
733 p->theInteraction = anInteraction;
734 }
735
projection()736 Projection& MapView::projection()
737 {
738 return p->theProjection;
739 }
740
transform()741 QTransform& MapView::transform()
742 {
743 return p->theTransform;
744 }
745
invertedTransform()746 QTransform& MapView::invertedTransform()
747 {
748 return p->theInvertedTransform;
749 }
750
toView(const Coord & aCoord) const751 QPoint MapView::toView(const Coord& aCoord) const
752 {
753 return p->theTransform.map(p->theProjection.project(aCoord)).toPoint();
754 }
755
toView(Node * aPt) const756 QPoint MapView::toView(Node* aPt) const
757 {
758 return p->theTransform.map(aPt->projected()).toPoint();
759 }
760
fromView(const QPoint & aPt) const761 Coord MapView::fromView(const QPoint& aPt) const
762 {
763 return p->theProjection.inverse(p->theInvertedTransform.map(QPointF(aPt)));
764 }
765
766
on_imageReceived(ImageMapLayer * aLayer)767 void MapView::on_imageReceived(ImageMapLayer* aLayer)
768 {
769 aLayer->forceRedraw(*this, p->BackgroundOnlyVpTransform, rect());
770 p->BackgroundOnlyVpTransform = QTransform();
771 update();
772 }
773
resizeEvent(QResizeEvent * ev)774 void MapView::resizeEvent(QResizeEvent * ev)
775 {
776 viewportRecalc(QRect(QPoint(0,0), ev->size()));
777
778 QWidget::resizeEvent(ev);
779
780 if (!StaticWireframe || (StaticWireframe->size() != size()))
781 {
782 delete StaticWireframe;
783 StaticWireframe = new QPixmap(size());
784 StaticWireframe->fill(Qt::transparent);
785 }
786 if (!StaticTouchup || (StaticTouchup->size() != size()))
787 {
788 delete StaticTouchup;
789 StaticTouchup = new QPixmap(size());
790 }
791
792 invalidate(true, true, true);
793 }
794
dragEnterEvent(QDragEnterEvent * event)795 void MapView::dragEnterEvent(QDragEnterEvent *event)
796 {
797 if (!Main) {
798 event->ignore();
799 return;
800 }
801 }
802
dragMoveEvent(QDragMoveEvent * event)803 void MapView::dragMoveEvent(QDragMoveEvent *event)
804 {
805 if (!Main) {
806 event->ignore();
807 return;
808 }
809 }
810
dropEvent(QDropEvent * event)811 void MapView::dropEvent(QDropEvent *event)
812 {
813 if (!Main) {
814 event->ignore();
815 return;
816 }
817 }
818
toXML(QXmlStreamWriter & stream)819 bool MapView::toXML(QXmlStreamWriter& stream)
820 {
821 bool OK = true;
822
823 stream.writeStartElement("MapView");
824 viewport().toXML("Viewport", stream);
825 p->theProjection.toXML(stream);
826 stream.writeEndElement();
827
828 return OK;
829 }
830
fromXML(QXmlStreamReader & stream)831 void MapView::fromXML(QXmlStreamReader& stream)
832 {
833 CoordBox cb;
834 stream.readNext();
835 while(!stream.atEnd() && !stream.isEndElement()) {
836 if (stream.name() == "Viewport") {
837 cb = CoordBox::fromXML(stream);
838 } else if (stream.name() == "Projection") {
839 p->theProjection.fromXML(stream);
840 }
841 stream.readNext();
842 }
843
844 if (!cb.isNull())
845 setViewport(cb, rect());
846 }
847
on_MoveLeft_activated()848 void MapView::on_MoveLeft_activated()
849 {
850 QPoint p(rect().width()/4,0);
851 panScreen(p);
852
853 invalidate(true, true, true);
854 }
on_MoveRight_activated()855 void MapView::on_MoveRight_activated()
856 {
857 QPoint p(-rect().width()/4,0);
858 panScreen(p);
859
860 invalidate(true, true, true);
861 }
862
on_MoveUp_activated()863 void MapView::on_MoveUp_activated()
864 {
865 QPoint p(0,rect().height()/4);
866 panScreen(p);
867
868 invalidate(true, true, true);
869 }
870
on_MoveDown_activated()871 void MapView::on_MoveDown_activated()
872 {
873 QPoint p(0,-rect().height()/4);
874 panScreen(p);
875
876 invalidate(true, true, true);
877 }
878
zoomIn()879 void MapView::zoomIn()
880 {
881 zoom(M_PREFS->getZoomIn()/100., rect().center());
882 }
883
zoomOut()884 void MapView::zoomOut()
885 {
886 zoom(M_PREFS->getZoomOut()/100., rect().center());
887 }
888
renderingDone()889 void MapView::renderingDone() {
890 update();
891 }
892
isSelectionLocked()893 bool MapView::isSelectionLocked()
894 {
895 return SelectionLocked;
896 }
897
lockSelection()898 void MapView::lockSelection()
899 {
900 if (!Main)
901 return;
902
903 if (!SelectionLocked && Main->properties()->selection().size()) {
904 #ifndef _MOBILE
905 lockIcon = new QLabel(this);
906 lockIcon->setPixmap(QPixmap(":/Icons/emblem-readonly.png"));
907 Main->statusBar()->clearMessage();
908 Main->statusBar()->addWidget(lockIcon);
909 #endif
910 SelectionLocked = true;
911 }
912 }
913
unlockSelection()914 void MapView::unlockSelection()
915 {
916 if (!Main)
917 return;
918
919 if (SelectionLocked) {
920 #ifndef _MOBILE
921 Main->statusBar()->removeWidget(lockIcon);
922 SAFE_DELETE(lockIcon)
923 #endif
924 SelectionLocked = false;
925 }
926 }
927
viewport() const928 const CoordBox& MapView::viewport() const
929 {
930 return p->Viewport;
931 }
932
viewportRecalc(const QRect & Screen)933 void MapView::viewportRecalc(const QRect & Screen)
934 {
935 Coord br = fromView(Screen.bottomRight());
936 Coord tl = fromView(Screen.topLeft());
937 p->Viewport = CoordBox(tl, br);
938
939 // measure geographical distance between mid left and mid right of the screen
940 int mid = (Screen.topLeft().y() + Screen.bottomLeft().y()) / 2;
941 Coord left = fromView(QPoint(Screen.left(), mid));
942 Coord right = fromView(QPoint(Screen.right(), mid));
943 p->PixelPerM = Screen.width() / (left.distanceFrom(right)*1000);
944 p->NodeWidth = p->PixelPerM * M_PREFS->getNodeSize();
945 if (p->NodeWidth > M_PREFS->getNodeSize())
946 p->NodeWidth = M_PREFS->getNodeSize();
947
948 emit viewportChanged();
949 }
950
transformCalc(QTransform & theTransform,const Projection & theProjection,const qreal &,const CoordBox & TargetMap,const QRect & screen)951 void MapView::transformCalc(QTransform& theTransform, const Projection& theProjection, const qreal& /* theRotation */, const CoordBox& TargetMap, const QRect& screen)
952 {
953 QRectF pViewport = theProjection.toProjectedRectF(TargetMap, screen);
954 // QPointF pCenter(pViewport.center());
955
956 qreal Aspect = (double)screen.width() / screen.height();
957 qreal pAspect = fabs(pViewport.width() / pViewport.height());
958
959 qreal wv, hv;
960 if (pAspect > Aspect) {
961 wv = fabs(pViewport.width());
962 hv = fabs(pViewport.height() * pAspect / Aspect);
963 } else {
964 wv = fabs(pViewport.width() * Aspect / pAspect);
965 hv = fabs(pViewport.height());
966 }
967
968 qreal ScaleLon = screen.width() / wv;
969 qreal ScaleLat = screen.height() / hv;
970
971 // qreal PLon = pCenter.x() /* * ScaleLon*/;
972 // qreal PLat = pCenter.y() /* * ScaleLat*/;
973 // qreal DeltaLon = Screen.width() / 2 - PLon;
974 // qreal DeltaLat = Screen.height() - (Screen.height() / 2 - PLat);
975
976 // theTransform.setMatrix(ScaleLon, 0, 0, 0, -ScaleLat, 0, DeltaLon, DeltaLat, 1);
977 theTransform.reset();
978 theTransform.scale(ScaleLon, -ScaleLat);
979 // theTransform.rotate(theRotation, Qt::ZAxis);
980 theTransform.translate(-pViewport.topLeft().x(), -pViewport.topLeft().y());
981 // theTransform.translate(-pCenter.x(), -pCenter.y());
982 // QLineF l(QPointF(0, 0), pCenter);
983 // l.setAngle(l.angle()+theRotation);
984 // qDebug() << "p2:" << l.p2();
985 // theTransform.translate(l.p2().x(), l.p2().y());
986 // theTransform.translate(Screen.width()/2, -Screen.height()/2);
987 // theTransform.rotateRadians(theRotation);
988 }
989
setViewport(const CoordBox & TargetMap,const QRect & Screen)990 void MapView::setViewport(const CoordBox & TargetMap,
991 const QRect & Screen)
992 {
993 CoordBox targetVp;
994 if (TargetMap.latDiff() == 0 || TargetMap.lonDiff() == 0)
995 targetVp = CoordBox (TargetMap.center()-COORD_ENLARGE*10, TargetMap.center()+COORD_ENLARGE*10);
996 else
997 targetVp = TargetMap;
998 transformCalc(p->theTransform, p->theProjection, p->theVectorRotation, targetVp, Screen);
999 p->theInvertedTransform = p->theTransform.inverted();
1000 viewportRecalc(Screen);
1001
1002 p->ZoomLevel = p->theTransform.m11();
1003
1004 if (TEST_RFLAGS(RendererOptions::LockZoom) && p->theDocument) {
1005 ImageMapLayer* l = NULL;
1006 for (LayerIterator<ImageMapLayer*> ImgIt(p->theDocument); !ImgIt.isEnd(); ++ImgIt) {
1007 l = ImgIt.get();
1008 break;
1009 }
1010 if (l && l->isTiled()) {
1011 l->setCurrentZoom(*this, p->Viewport, rect());
1012 qreal pixpercoord = width() / p->Viewport.lonDiff();
1013 qreal z = l->pixelPerCoord() / pixpercoord;
1014 zoom(z, Screen.center(), Screen);
1015 }
1016 }
1017 invalidate(true, true, true);
1018 }
1019
zoom(qreal d,const QPoint & Around)1020 void MapView::zoom(qreal d, const QPoint & Around)
1021 {
1022 // Sensible limits on zoom range (circular scroll on touchpads can scroll
1023 // very fast).
1024 if (p->PixelPerM * d > 100 && d > 1.0)
1025 return;
1026 if (p->PixelPerM * d < 1e-5 && d < 1.0)
1027 return;
1028
1029 qreal z = d;
1030 if (TEST_RFLAGS(RendererOptions::LockZoom)) {
1031 ImageMapLayer* l = NULL;
1032 for (LayerIterator<ImageMapLayer*> ImgIt(p->theDocument); !ImgIt.isEnd(); ++ImgIt) {
1033 l = ImgIt.get();
1034 break;
1035 }
1036 if (l && l->isTiled()) {
1037 if (d > 1.0) {
1038 l->zoom_in();
1039 } else {
1040 l->zoom_out();
1041 }
1042 qreal pixpercoord = width() / p->Viewport.lonDiff();
1043 z = l->pixelPerCoord() / pixpercoord;
1044 }
1045 }
1046
1047 zoom(z, Around, rect());
1048 if (!M_PREFS->getWireframeView())
1049 p->osmLayer->forceRedraw(p->theProjection, p->theTransform, rect(), p->PixelPerM, p->ROptions);
1050 invalidate(true, false, true);
1051 }
1052
zoom(qreal d,const QPoint & Around,const QRect & Screen)1053 void MapView::zoom(qreal d, const QPoint & Around,
1054 const QRect & Screen)
1055 {
1056 QPointF pBefore = p->theInvertedTransform.map(QPointF(Around));
1057
1058 qreal ScaleLon = p->theTransform.m11() * d;
1059 qreal ScaleLat = p->theTransform.m22() * d;
1060 qreal DeltaLat = (Around.y() - pBefore.y() * ScaleLat);
1061 qreal DeltaLon = (Around.x() - pBefore.x() * ScaleLon);
1062
1063 // p->theTransform.setMatrix(ScaleLon*cos(p->theVectorRotation), 0, 0, 0, ScaleLat*cos(p->theVectorRotation), 0, DeltaLon, DeltaLat, 1);
1064 p->theTransform.reset();
1065 p->theTransform.scale(ScaleLon, ScaleLat);
1066 // p->theTransform.rotate(p->theVectorRotation, Qt::ZAxis);
1067 p->theTransform.translate(DeltaLon/ScaleLon, DeltaLat/ScaleLat);
1068
1069 p->theInvertedTransform = p->theTransform.inverted();
1070 viewportRecalc(Screen);
1071
1072 for (LayerIterator<ImageMapLayer*> ImgIt(p->theDocument); !ImgIt.isEnd(); ++ImgIt)
1073 ImgIt.get()->zoom(d, Around, Screen);
1074
1075 p->ZoomLevel = ScaleLon;
1076
1077 // QPointF pt = p->theProjection.project(Coord(0, angToInt(180)));
1078 // qreal earthWidth = pt.x() * 2;
1079 // qreal zoomPixPerMat0 = 512. / earthWidth;
1080 // qreal z = 0;
1081 // p->AbstractZoomLevel = 0;
1082 // for (;z<p->theTransform.m11(); ++p->AbstractZoomLevel) {
1083 // qreal zoomPixPerMatCur = zoomPixPerMat0 * pow(2., p->AbstractZoomLevel);
1084 // z = zoomPixPerMatCur / p->PixelPerM;
1085 // }
1086 }
1087
adjustZoomToBoris()1088 void MapView::adjustZoomToBoris()
1089 {
1090 if (TEST_RFLAGS(RendererOptions::LockZoom)) {
1091 ImageMapLayer* l = NULL;
1092 for (LayerIterator<ImageMapLayer*> ImgIt(p->theDocument); !ImgIt.isEnd(); ++ImgIt) {
1093 l = ImgIt.get();
1094 break;
1095 }
1096 if (l && l->isTiled()) {
1097 qreal pixpercoord = width() / p->Viewport.lonDiff();
1098 qreal z = l->pixelPerCoord() / pixpercoord;
1099 zoom(z, rect().center(), rect());
1100 }
1101 }
1102 }
1103
setCenter(Coord & Center,const QRect &)1104 void MapView::setCenter(Coord & Center, const QRect & /*Screen*/)
1105 {
1106 Coord curCenter(p->Viewport.center());
1107 QPoint curCenterScreen = toView(curCenter);
1108 QPoint newCenterScreen = toView(Center);
1109
1110 QPoint panDelta = (curCenterScreen - newCenterScreen);
1111 panScreen(panDelta);
1112 }
1113
setInteracting(bool val)1114 void MapView::setInteracting(bool val)
1115 {
1116 if (val)
1117 p->ROptions.options |= RendererOptions::Interacting;
1118 else
1119 p->ROptions.options &= ~RendererOptions::Interacting;
1120 // invalidate(true, true, false);
1121 }
1122
pixelPerM() const1123 qreal MapView::pixelPerM() const
1124 {
1125 return p->PixelPerM;
1126 }
1127
setBackgroundOnlyPanZoom(bool val)1128 void MapView::setBackgroundOnlyPanZoom(bool val)
1129 {
1130 p->BackgroundOnlyPanZoom = val;
1131 }
1132
renderOptions()1133 RendererOptions MapView::renderOptions()
1134 {
1135 return p->ROptions;
1136 }
1137
setRenderOptions(const RendererOptions & opt)1138 void MapView::setRenderOptions(const RendererOptions &opt)
1139 {
1140 p->ROptions = opt;
1141 }
1142
stopRendering()1143 void MapView::stopRendering() {
1144 p->osmLayer->stopRendering();
1145 }
1146
resumeRendering()1147 void MapView::resumeRendering() {
1148 p->osmLayer->resumeRendering();
1149 }
1150
nodeWidth()1151 qreal MapView::nodeWidth()
1152 {
1153 return p->NodeWidth;
1154 }
1155
toPropertiesHtml()1156 QString MapView::toPropertiesHtml()
1157 {
1158 QString h;
1159
1160 h += "<big><strong>" + tr("View") + "</strong></big><hr/>";
1161 h += "<u>" + tr("Bounding Box") + "</u><br/>";
1162 h += QString("%1, %2, %3, %4 (%5, %6, %7, %8)")
1163 .arg(QString::number(viewport().bottomLeft().x(),'f',4))
1164 .arg(QString::number(viewport().bottomLeft().y(),'f',4))
1165 .arg(QString::number(viewport().topRight().x(),'f',4))
1166 .arg(QString::number(viewport().topRight().y(),'f',4))
1167 .arg(Coord2Sexa(viewport().bottomLeft().x()))
1168 .arg(Coord2Sexa(viewport().bottomLeft().y()))
1169 .arg(Coord2Sexa(viewport().topRight().x()))
1170 .arg(Coord2Sexa(viewport().topRight().y()))
1171 ;
1172 h += "<br/>";
1173 h += "<u>" + tr("Projection") + "</u><br/>";
1174 h += p->theProjection.getProjectionType();
1175 h += "";
1176
1177 return h;
1178 }
1179