1
#include "SlippyMapWidget.h"
4
#include <QApplication>
12
#include "Preferences/MerkaartorPreferences.h"
15
#define MINZOOMLEVEL 0
16
#define MAXZOOMLEVEL 17
21
class SlippyMapWidgetPrivate
24
SlippyMapWidgetPrivate(SlippyMapWidget* w)
25
: theWidget(w), InDrag(false)
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();
36
~SlippyMapWidgetPrivate()
38
Sets->setValue("Lat", Lat);
39
Sets->setValue("Lon", Lon);
40
Sets->setValue("Zoom", Zoom);
44
QPixmap* getImage(int x, int y);
45
void newData(int x, int y, int zoom);
47
SlippyMapWidget* theWidget;
49
double Lat,Lon, VpLat, VpLon;
55
QPixmap* SlippyMapWidgetPrivate::getImage(int x, int y)
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);
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));
71
void SlippyMapWidgetPrivate::newData(int, int, int)
76
SlippyMapWidget::SlippyMapWidget(QWidget* aParent)
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 &)));
86
SlippyMapWidget::~SlippyMapWidget(void)
88
theSlippyCache->setMap(0);
92
/* http://wiki.openstreetmap.org/index.php/Slippy_map_tilenames#C.2FC.2B.2B */
93
static double tile2lon(double x, int z)
95
return x / pow(2.0, z) * 360.0 - 180;
98
/* http://wiki.openstreetmap.org/index.php/Slippy_map_tilenames#C.2FC.2B.2B */
99
static double tile2lat(double y, int z)
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)));
105
static int long2tile(double lon, int z)
107
return (int)(floor((lon + 180.0) / 360.0 * pow(2.0, z)));
110
static int lat2tile(double lat, int z)
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)));
116
QRect SlippyMapWidget::viewArea() const
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;
123
int Lon1 = angToInt(tile2lon(X1, p->Zoom));
124
int Lat1 = angToInt(tile2lat(Y1, p->Zoom));
126
int Lon2 = angToInt(tile2lon(X2, p->Zoom));
127
int Lat2 = angToInt(tile2lat(Y2, p->Zoom));
129
return QRect(Lon1, Lat2, Lon2-Lon1, Lat1-Lat2);
132
void SlippyMapWidget::setViewportArea(QRectF theRect)
134
double zoom = 360.0 / intToAng(int(theRect.width()));
135
zoom = log10(zoom)/log10(2.0);
136
if (zoom < MINZOOMLEVEL)
138
if (zoom > MAXZOOMLEVEL)
140
p->VpZoom = int(zoom);
142
p->VpLon = long2tile(intToAng(int(theRect.topRight().x())), p->VpZoom);
143
p->VpLat = lat2tile(intToAng(int(theRect.topRight().y())), p->VpZoom);
146
void SlippyMapWidget::paintEvent(QPaintEvent*)
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);
155
LatPixel -=TILESIZE, --LatRect;
157
LonPixel -= TILESIZE, --LonRect;
158
for (int x=LatPixel; x<width(); x += TILESIZE)
159
for (int y=LonPixel; y<height(); y+= TILESIZE)
161
int ThisLatRect = LatRect + (x-LatPixel)/TILESIZE;
162
int ThisLonRect = LonRect + (y-LonPixel)/TILESIZE;
163
QPixmap* img = p->getImage(ThisLatRect,ThisLonRect);
165
Painter.drawPixmap(x,y,*img);
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);
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);
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");
186
void SlippyMapWidget::ZoomTo(const QPoint & NewCenter, int NewZoom)
188
if (NewZoom < MINZOOMLEVEL)
189
NewZoom = MINZOOMLEVEL;
190
if (NewZoom > MAXZOOMLEVEL)
191
NewZoom = MAXZOOMLEVEL;
192
if ((int)p->Zoom == NewZoom)
195
double dx = (NewCenter.x()-width()/2)/(TILESIZE*1.0);
196
double dy = (NewCenter.y()-height()/2)/(TILESIZE*1.0);
198
p->Lat = (p->Lat + dx) * (1 << NewZoom) / (1 << p->Zoom) - dx;
199
p->Lon = (p->Lon + dy) * (1 << NewZoom) / (1 << p->Zoom) - dy;
204
void SlippyMapWidget::wheelEvent(QWheelEvent* ev)
206
int NewZoom = ev->delta()/120 + p->Zoom;
207
ZoomTo(ev->pos(), NewZoom);
211
void SlippyMapWidget::mousePressEvent(QMouseEvent* ev)
213
if (ev->button() == Qt::MidButton)
215
ZoomTo(ev->pos(), p->Zoom + 1);
217
// else if (ev->button() == Qt::RightButton)
219
// ZoomTo(ev->pos(), p->Zoom - 1);
223
if (ev->pos().x() > width()-20)
225
if (ev->pos().y() < 20)
227
ZoomTo(QPoint(width()/2,height()/2),p->Zoom-1);
231
else if (ev->pos().y() > height()-20)
233
ZoomTo(QPoint(width()/2,height()/2),p->Zoom+1);
237
else if ((ev->pos().y() > (height()/2)-20) && (ev->pos().y() < (height()/2)))
247
p->PreviousDrag = ev->pos();
252
void SlippyMapWidget::mouseReleaseEvent(QMouseEvent*)
257
void SlippyMapWidget::mouseMoveEvent(QMouseEvent* ev)
259
QPoint Delta = ev->pos()-p->PreviousDrag;
263
p->Lat -= Delta.x()/(TILESIZE*1.);
264
p->Lon -= Delta.y()/(TILESIZE*1.);
265
p->PreviousDrag = ev->pos();
271
void SlippyMapWidget::on_customContextMenuRequested(const QPoint & pos)
275
QAction* resetViewAction = new QAction(tr("Reset view"), this);
276
connect(resetViewAction, SIGNAL(triggered(bool)), this, SLOT(on_resetViewAction_triggered(bool)));
278
menu.addAction(resetViewAction);
279
menu.exec(mapToGlobal(pos));
282
void SlippyMapWidget::on_resetViewAction_triggered(bool)
290
bool SlippyMapWidget::isDragging()
298
SlippyMapCache::SlippyMapCache()
299
: QObject(0), DownloadId(0), DownloadBusy(false), theMap(0)
301
Download.setHost("tile.openstreetmap.org");
302
Download.setProxy(M_PREFS->getProxy(QUrl(QString("http://tile.openstreetmap.org"))));
304
DownloadBuffer.setBuffer(&DownloadData);
305
DownloadBuffer.open(QIODevice::WriteOnly);
306
connect(&Download,SIGNAL(requestFinished(int,bool)),this,SLOT(on_requestFinished(int, bool)));
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");
315
void SlippyMapCache::setMap(SlippyMapWidgetPrivate* aMap)
319
for (QMap<Coord, QByteArray>::iterator i = Memory.begin(); i != Memory.end(); ++i)
320
Use += i.value().length();
325
void SlippyMapCache::preload(const Coord& C, const QString& Filename)
328
f.open(QIODevice::ReadOnly);
329
QByteArray ba(f.readAll());
335
void SlippyMapCache::on_requestFinished(int Id, bool Error)
337
if (Id == DownloadId)
339
DownloadBusy = false;
342
Memory[DownloadCoord] = DownloadData;
344
theMap->newData(DownloadCoord.X,DownloadCoord.Y,DownloadCoord.Zoom);
345
QMap<Coord,QByteArray>::iterator i = Dirties.find(DownloadCoord);
346
if (i != Dirties.end())
352
QPixmap* SlippyMapCache::getImage(int x, int y, int Zoom)
358
QMap<Coord,QByteArray>::iterator i = Memory.find(C);
359
if (i == Memory.end())
362
return getDirty(x,y,Zoom);
364
QPixmap* img = new QPixmap;
365
img->loadFromData(i.value());
369
QPixmap* SlippyMapCache::getDirty(int x, int y, int Zoom)
371
if (Zoom == MINZOOMLEVEL) return 0;
372
QMap<Coord,QByteArray>::iterator i = Dirties.find(Coord(x,y,Zoom));
373
if (i != Dirties.end())
375
QPixmap* img = new QPixmap;
376
img->loadFromData(i.value());
379
QPixmap* img = getImage(x/2,y/2,Zoom-1);
381
QPixmap* pm = new QPixmap(img->copy((x%2)*TILESIZE/2,(y%2)*TILESIZE/2,TILESIZE/2,TILESIZE/2).scaled(TILESIZE,TILESIZE));
384
QBuffer Buffer(&Data);
385
Buffer.open(QIODevice::WriteOnly);
386
pm->save(&Buffer,"PNG");
392
void SlippyMapCache::addToQueue(const Coord& C)
394
for (int i=Queue.size(); i; --i)
395
if (Queue[i-1].Zoom != C.Zoom)
396
Queue.erase(Queue.begin()+(i-1));
401
void SlippyMapCache::startDownload()
403
if (Queue.empty()) return;
404
if (DownloadBusy) return;
407
QMap<Coord,QByteArray>::iterator i = Memory.find(Queue[0]);
408
if (i == Memory.end())
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);
420
Queue.erase(Queue.begin());