~ubuntu-branches/ubuntu/saucy/merkaartor/saucy

« back to all changes in this revision

Viewing changes to src/Utils/SlippyMapWidget.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Bernd Zeimetz
  • Date: 2009-09-13 00:52:12 UTC
  • mto: (1.2.7 upstream) (0.1.3 upstream) (3.1.7 sid)
  • mto: This revision was merged to the branch mainline in revision 10.
  • Revision ID: james.westby@ubuntu.com-20090913005212-pjecal8zxm07x0fj
ImportĀ upstreamĀ versionĀ 0.14+svnfixes~20090912

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#include "SlippyMapWidget.h"
 
2
 
 
3
#include <QFile>
 
4
#include <QApplication>
 
5
#include <QPainter>
 
6
#include <QPixmap>
 
7
#include <QWheelEvent>
 
8
#include <QMenu>
 
9
 
 
10
#include <math.h>
 
11
 
 
12
#include "Preferences/MerkaartorPreferences.h"
 
13
 
 
14
#define TILESIZE 256
 
15
#define MINZOOMLEVEL 0
 
16
#define MAXZOOMLEVEL 17
 
17
 
 
18
 
 
19
/* SLIPPYMAPWIDGET */
 
20
 
 
21
class SlippyMapWidgetPrivate
 
22
{
 
23
        public:
 
24
                SlippyMapWidgetPrivate(SlippyMapWidget* w)
 
25
                        : theWidget(w), InDrag(false)
 
26
                {
 
27
                        Sets = new QSettings();
 
28
                        Sets->beginGroup("SlippyMapWidget");
 
29
                        Lat = Sets->value("Lat", 1).toDouble();
 
30
                        Lon = Sets->value("Lon", 1).toDouble();
 
31
                        Zoom = Sets->value("Zoom", 1).toInt();
 
32
                        VpLat = Lat;
 
33
                        VpLon = Lon;
 
34
                        VpZoom = Zoom;
 
35
                }
 
36
                ~SlippyMapWidgetPrivate()
 
37
                {
 
38
                        Sets->setValue("Lat", Lat);
 
39
                        Sets->setValue("Lon", Lon);
 
40
                        Sets->setValue("Zoom", Zoom);
 
41
                        delete Sets;
 
42
                }
 
43
 
 
44
                QPixmap* getImage(int x, int y);
 
45
                void newData(int x, int y, int zoom);
 
46
 
 
47
                SlippyMapWidget* theWidget;
 
48
                int Zoom, VpZoom;
 
49
                double Lat,Lon, VpLat, VpLon;
 
50
                QPoint PreviousDrag;
 
51
                bool InDrag;
 
52
                QSettings* Sets;
 
53
};
 
54
 
 
55
QPixmap* SlippyMapWidgetPrivate::getImage(int x, int y)
 
56
{
 
57
        int Max = 1 << Zoom;
 
58
        if (x<0 || x>=Max) return 0;
 
59
        if (y<0 || y>=Max) return 0;
 
60
        QPixmap* img = theWidget->theSlippyCache->getImage(x,y,Zoom);
 
61
        if (img) return img;
 
62
        img = new QPixmap(TILESIZE,TILESIZE);
 
63
        QPainter Painter(img);
 
64
        Painter.setPen(QColor(0,0,0));
 
65
        Painter.fillRect(0,0,TILESIZE-1,TILESIZE-1,QColor(255,255,255));
 
66
        Painter.drawRect(0,0,TILESIZE-1,TILESIZE-1);
 
67
        Painter.drawText(10,TILESIZE/2,(QApplication::translate("Downloader","Downloading %1,%2 (zoom %3)...")).arg(x).arg(y).arg(Zoom));
 
68
        return img;
 
69
}
 
70
 
 
71
void SlippyMapWidgetPrivate::newData(int, int, int)
 
72
{
 
73
        theWidget->update();
 
74
}
 
75
 
 
76
SlippyMapWidget::SlippyMapWidget(QWidget* aParent)
 
77
: QWidget(aParent)
 
78
{
 
79
        p = new SlippyMapWidgetPrivate(this);
 
80
        theSlippyCache->setMap(p);
 
81
        setContextMenuPolicy (Qt::CustomContextMenu);
 
82
        connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(on_customContextMenuRequested(const QPoint &)));
 
83
        resize(500,400);
 
84
}
 
85
 
 
86
SlippyMapWidget::~SlippyMapWidget(void)
 
87
{
 
88
        theSlippyCache->setMap(0);
 
89
        delete p;
 
90
}
 
91
 
 
92
/* http://wiki.openstreetmap.org/index.php/Slippy_map_tilenames#C.2FC.2B.2B */
 
93
static double tile2lon(double x, int z)
 
94
{
 
95
        return x / pow(2.0, z) * 360.0 - 180;
 
96
}
 
97
 
 
98
/* http://wiki.openstreetmap.org/index.php/Slippy_map_tilenames#C.2FC.2B.2B */
 
99
static double tile2lat(double y, int z)
 
100
{
 
101
        double n = M_PI - 2.0 * M_PI * y / pow(2.0, z);
 
102
        return 180.0 / M_PI * atan(0.5 * (exp(n) - exp(-n)));
 
103
}
 
104
 
 
105
static int long2tile(double lon, int z) 
 
106
 
107
        return (int)(floor((lon + 180.0) / 360.0 * pow(2.0, z))); 
 
108
}
 
109
 
 
110
static int lat2tile(double lat, int z)
 
111
 
112
        return (int)(floor((1.0 - log( tan(lat * M_PI/180.0) + 1.0 / cos(lat * M_PI/180.0)) / M_PI) / 2.0 * pow(2.0, z))); 
 
113
}
 
114
 
 
115
 
 
116
QRect SlippyMapWidget::viewArea() const
 
117
{
 
118
        double X1 = p->Lat - (width()/2.0)/TILESIZE;
 
119
        double Y1 = p->Lon - (height()/2.0)/TILESIZE;
 
120
        double X2 = p->Lat + (width()/2.0)/TILESIZE;
 
121
        double Y2 = p->Lon + (height()/2.0)/TILESIZE;
 
122
 
 
123
        int Lon1 = angToInt(tile2lon(X1, p->Zoom));
 
124
        int Lat1 = angToInt(tile2lat(Y1, p->Zoom));
 
125
 
 
126
        int Lon2 = angToInt(tile2lon(X2, p->Zoom));
 
127
        int Lat2 = angToInt(tile2lat(Y2, p->Zoom));
 
128
 
 
129
        return QRect(Lon1, Lat2, Lon2-Lon1, Lat1-Lat2);
 
130
}
 
131
 
 
132
void SlippyMapWidget::setViewportArea(QRectF theRect)
 
133
{
 
134
    double zoom = 360.0 / intToAng(int(theRect.width()));
 
135
        zoom = log10(zoom)/log10(2.0);
 
136
        if (zoom < MINZOOMLEVEL)
 
137
                zoom = MINZOOMLEVEL;
 
138
        if (zoom > MAXZOOMLEVEL)
 
139
                zoom = MAXZOOMLEVEL;
 
140
        p->VpZoom = int(zoom);
 
141
 
 
142
    p->VpLon = long2tile(intToAng(int(theRect.topRight().x())), p->VpZoom);
 
143
    p->VpLat = lat2tile(intToAng(int(theRect.topRight().y())), p->VpZoom);
 
144
}
 
145
 
 
146
void SlippyMapWidget::paintEvent(QPaintEvent*)
 
147
{
 
148
        QPainter Painter(this);
 
149
        Painter.fillRect(QRect(0,0,width(),height()),QColor(255,255,255));
 
150
        int LatRect = int(floor(p->Lat));
 
151
        int LatPixel = int(-(p->Lat - LatRect ) * TILESIZE + width()/2);
 
152
        int LonRect = int(floor(p->Lon));
 
153
        int LonPixel = int(-(p->Lon - LonRect ) * TILESIZE + height()/2);
 
154
        while (LatPixel > 0)
 
155
                LatPixel -=TILESIZE, --LatRect;
 
156
        while (LonPixel > 0)
 
157
                LonPixel -= TILESIZE, --LonRect;
 
158
        for (int x=LatPixel; x<width(); x += TILESIZE)
 
159
                for (int y=LonPixel; y<height(); y+= TILESIZE)
 
160
                {
 
161
                        int ThisLatRect = LatRect + (x-LatPixel)/TILESIZE;
 
162
                        int ThisLonRect = LonRect + (y-LonPixel)/TILESIZE;
 
163
                        QPixmap* img = p->getImage(ThisLatRect,ThisLonRect);
 
164
                        if (img)
 
165
                                Painter.drawPixmap(x,y,*img);
 
166
                        delete img;
 
167
                }
 
168
        Painter.setPen(QPen(Qt::NoPen));
 
169
        Painter.setBrush(QBrush(QColor(255,255,255,128)));
 
170
        Painter.drawRect(width()-21,0,20,20);
 
171
        Painter.drawRect(width()-21,height()-21,20,20);
 
172
        Painter.drawRect(width()-21,(height()/2)-10,20,20);
 
173
 
 
174
 
 
175
        Painter.setBrush(QBrush(QColor(0,0,0)));
 
176
        Painter.drawRect(width()-19,8,16,4);
 
177
        Painter.drawRect(width()-19,height()-13,16,4);
 
178
        Painter.drawRect(width()-13,height()-19,4,16);
 
179
 
 
180
        Painter.setFont(QFont("Times", 19, QFont::Bold));
 
181
        Painter.setPen(QPen(Qt::black, 3));
 
182
        Painter.setBrush(Qt::NoBrush);
 
183
        Painter.drawText(QPoint(width()-21,(height()/2)+10), "V");
 
184
}
 
185
 
 
186
void SlippyMapWidget::ZoomTo(const QPoint & NewCenter, int NewZoom)
 
187
{
 
188
        if (NewZoom < MINZOOMLEVEL)
 
189
                NewZoom = MINZOOMLEVEL;
 
190
        if (NewZoom > MAXZOOMLEVEL)
 
191
                NewZoom = MAXZOOMLEVEL;
 
192
        if ((int)p->Zoom == NewZoom)
 
193
                return;
 
194
        
 
195
        double dx = (NewCenter.x()-width()/2)/(TILESIZE*1.0);
 
196
        double dy = (NewCenter.y()-height()/2)/(TILESIZE*1.0);
 
197
 
 
198
        p->Lat = (p->Lat + dx) * (1 << NewZoom) / (1 << p->Zoom) - dx;
 
199
        p->Lon = (p->Lon + dy) * (1 << NewZoom) / (1 << p->Zoom) - dy;
 
200
        p->Zoom = NewZoom;
 
201
        update();
 
202
}
 
203
 
 
204
void SlippyMapWidget::wheelEvent(QWheelEvent* ev)
 
205
{
 
206
        int NewZoom = ev->delta()/120 + p->Zoom;
 
207
        ZoomTo(ev->pos(), NewZoom);
 
208
        emit redraw();
 
209
}
 
210
 
 
211
void SlippyMapWidget::mousePressEvent(QMouseEvent* ev)
 
212
{
 
213
        if (ev->button() == Qt::MidButton)
 
214
        {
 
215
                ZoomTo(ev->pos(), p->Zoom + 1);
 
216
        }
 
217
//      else if (ev->button() == Qt::RightButton)
 
218
//      {
 
219
//              ZoomTo(ev->pos(), p->Zoom - 1);
 
220
//      }
 
221
        else
 
222
        {
 
223
                if (ev->pos().x() > width()-20)
 
224
                {
 
225
                        if (ev->pos().y() < 20)
 
226
                        {
 
227
                                ZoomTo(QPoint(width()/2,height()/2),p->Zoom-1);
 
228
                                emit redraw();
 
229
                                return;
 
230
                        }
 
231
                        else if (ev->pos().y() > height()-20)
 
232
                        {
 
233
                                ZoomTo(QPoint(width()/2,height()/2),p->Zoom+1);
 
234
                                emit redraw();
 
235
                                return;
 
236
                        }
 
237
                        else if ((ev->pos().y() > (height()/2)-20) && (ev->pos().y() < (height()/2)))
 
238
                        {
 
239
                                p->Lat = p->VpLon;
 
240
                                p->Lon = p->VpLat;
 
241
                                p->Zoom = p->VpZoom;
 
242
                                update();
 
243
                                emit redraw();
 
244
                                return;
 
245
                        }
 
246
                }
 
247
                p->PreviousDrag = ev->pos();
 
248
        }
 
249
        emit redraw();
 
250
}
 
251
 
 
252
void SlippyMapWidget::mouseReleaseEvent(QMouseEvent*)
 
253
{
 
254
        p->InDrag = false;
 
255
}
 
256
 
 
257
void SlippyMapWidget::mouseMoveEvent(QMouseEvent* ev)
 
258
{
 
259
        QPoint Delta = ev->pos()-p->PreviousDrag;
 
260
        if (!Delta.isNull())
 
261
        {
 
262
                p->InDrag = true;
 
263
                p->Lat -= Delta.x()/(TILESIZE*1.);
 
264
                p->Lon -= Delta.y()/(TILESIZE*1.);
 
265
                p->PreviousDrag = ev->pos();
 
266
                update();
 
267
                emit redraw();
 
268
        }
 
269
}
 
270
 
 
271
void SlippyMapWidget::on_customContextMenuRequested(const QPoint & pos)
 
272
{
 
273
        QMenu menu;
 
274
 
 
275
        QAction* resetViewAction = new QAction(tr("Reset view"), this);
 
276
        connect(resetViewAction, SIGNAL(triggered(bool)), this, SLOT(on_resetViewAction_triggered(bool)));
 
277
 
 
278
        menu.addAction(resetViewAction);
 
279
        menu.exec(mapToGlobal(pos));
 
280
}
 
281
 
 
282
void SlippyMapWidget::on_resetViewAction_triggered(bool)
 
283
{
 
284
        p->Lat = 1.0;
 
285
        p->Lon = 1.0;
 
286
        p->Zoom = 1;
 
287
        update();
 
288
}
 
289
 
 
290
bool SlippyMapWidget::isDragging()
 
291
{
 
292
        return p->InDrag;
 
293
}
 
294
 
 
295
 
 
296
/* SLIPPYMAPCACHE */
 
297
 
 
298
SlippyMapCache::SlippyMapCache()
 
299
: QObject(0), DownloadId(0), DownloadBusy(false), theMap(0)
 
300
{
 
301
        Download.setHost("tile.openstreetmap.org");
 
302
        Download.setProxy(M_PREFS->getProxy(QUrl(QString("http://tile.openstreetmap.org"))));
 
303
 
 
304
        DownloadBuffer.setBuffer(&DownloadData);
 
305
        DownloadBuffer.open(QIODevice::WriteOnly);
 
306
        connect(&Download,SIGNAL(requestFinished(int,bool)),this,SLOT(on_requestFinished(int, bool)));
 
307
 
 
308
        preload(Coord(0,0,0),":/Tiles/000.png");
 
309
        preload(Coord(0,0,1),":/Tiles/100.png");
 
310
        preload(Coord(0,1,1),":/Tiles/101.png");
 
311
        preload(Coord(1,0,1),":/Tiles/110.png");
 
312
        preload(Coord(1,1,1),":/Tiles/111.png");
 
313
}
 
314
 
 
315
void SlippyMapCache::setMap(SlippyMapWidgetPrivate* aMap)
 
316
{
 
317
        theMap = aMap;
 
318
        int Use = 0;
 
319
        for (QMap<Coord, QByteArray>::iterator i = Memory.begin(); i != Memory.end(); ++i)
 
320
                Use += i.value().length();
 
321
        Dirties.clear();
 
322
}
 
323
 
 
324
 
 
325
void SlippyMapCache::preload(const Coord& C, const QString& Filename)
 
326
{
 
327
        QFile f(Filename);
 
328
        f.open(QIODevice::ReadOnly);
 
329
        QByteArray ba(f.readAll());
 
330
        if (ba.length())
 
331
                Memory[C] = ba;
 
332
}
 
333
 
 
334
 
 
335
void SlippyMapCache::on_requestFinished(int Id, bool Error)
 
336
{
 
337
        if (Id == DownloadId)
 
338
        {
 
339
                DownloadBusy = false;
 
340
                if (!Error)
 
341
                {
 
342
                        Memory[DownloadCoord] = DownloadData;
 
343
                        if (theMap)
 
344
                                theMap->newData(DownloadCoord.X,DownloadCoord.Y,DownloadCoord.Zoom);
 
345
                        QMap<Coord,QByteArray>::iterator i = Dirties.find(DownloadCoord);
 
346
                        if (i != Dirties.end())
 
347
                                Dirties.erase(i);
 
348
                }
 
349
        }
 
350
}
 
351
 
 
352
QPixmap* SlippyMapCache::getImage(int x, int y, int Zoom)
 
353
{
 
354
        Coord C;
 
355
        C.X = x;
 
356
        C.Y = y;
 
357
        C.Zoom = Zoom;
 
358
        QMap<Coord,QByteArray>::iterator i = Memory.find(C);
 
359
        if (i == Memory.end())
 
360
        {
 
361
                addToQueue(C);
 
362
                return getDirty(x,y,Zoom);
 
363
        }
 
364
        QPixmap* img = new QPixmap;
 
365
        img->loadFromData(i.value());
 
366
        return img;
 
367
}
 
368
 
 
369
QPixmap* SlippyMapCache::getDirty(int x, int y, int Zoom)
 
370
{
 
371
        if (Zoom == MINZOOMLEVEL) return 0;
 
372
        QMap<Coord,QByteArray>::iterator i = Dirties.find(Coord(x,y,Zoom));
 
373
        if (i != Dirties.end())
 
374
        {
 
375
                QPixmap* img = new QPixmap;
 
376
                img->loadFromData(i.value());
 
377
                return img;
 
378
        }
 
379
        QPixmap* img = getImage(x/2,y/2,Zoom-1);
 
380
        if (!img) return 0;
 
381
        QPixmap* pm = new QPixmap(img->copy((x%2)*TILESIZE/2,(y%2)*TILESIZE/2,TILESIZE/2,TILESIZE/2).scaled(TILESIZE,TILESIZE));
 
382
        delete img;
 
383
        QByteArray Data;
 
384
        QBuffer Buffer(&Data);
 
385
        Buffer.open(QIODevice::WriteOnly);
 
386
        pm->save(&Buffer,"PNG");
 
387
        Coord C(x,y,Zoom);
 
388
        Dirties[C] = Data;
 
389
        return pm;
 
390
}
 
391
 
 
392
void SlippyMapCache::addToQueue(const Coord& C)
 
393
{
 
394
        for (int i=Queue.size(); i; --i)
 
395
                if (Queue[i-1].Zoom != C.Zoom)
 
396
                        Queue.erase(Queue.begin()+(i-1));
 
397
        Queue.push_back(C);
 
398
        startDownload();
 
399
}
 
400
 
 
401
void SlippyMapCache::startDownload()
 
402
{
 
403
        if (Queue.empty()) return;
 
404
        if (DownloadBusy) return;
 
405
        while (Queue.size())
 
406
        {
 
407
                QMap<Coord,QByteArray>::iterator i = Memory.find(Queue[0]);
 
408
                if (i == Memory.end())
 
409
                {
 
410
                        DownloadBusy = true;
 
411
                        DownloadCoord = Queue[0];
 
412
                        Queue.erase(Queue.begin());
 
413
                        QString Path("/%1/%2/%3.png");
 
414
                        Path = Path.arg(DownloadCoord.Zoom).arg(DownloadCoord.X).arg(DownloadCoord.Y);
 
415
                        DownloadData.clear();
 
416
                        DownloadBuffer.reset();
 
417
                        DownloadId = Download.get(Path, &DownloadBuffer);
 
418
                        return;
 
419
                }
 
420
                Queue.erase(Queue.begin());
 
421
        }
 
422
}
 
423