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