source: trunk/demos/embedded/lightmaps/lightmaps.cpp@ 769

Last change on this file since 769 was 651, checked in by Dmitry A. Kuminov, 15 years ago

trunk: Merged in qt 4.6.2 sources.

  • Property svn:eol-style set to native
File size: 17.5 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** This file is part of the demonstration applications of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include <QtCore>
43#include <QtGui>
44#include <QtNetwork>
45
46#if defined (Q_OS_SYMBIAN)
47#include "sym_iap_util.h"
48#endif
49
50#include <math.h>
51
52#ifndef M_PI
53#define M_PI 3.14159265358979323846
54#endif
55
56// how long (milliseconds) the user need to hold (after a tap on the screen)
57// before triggering the magnifying glass feature
58// 701, a prime number, is the sum of 229, 233, 239
59// (all three are also prime numbers, consecutive!)
60#define HOLD_TIME 701
61
62// maximum size of the magnifier
63// Hint: see above to find why I picked this one :)
64#define MAX_MAGNIFIER 229
65
66uint qHash(const QPoint& p)
67{
68 return p.x() * 17 ^ p.y();
69}
70
71// tile size in pixels
72const int tdim = 256;
73
74QPointF tileForCoordinate(qreal lat, qreal lng, int zoom)
75{
76 qreal zn = static_cast<qreal>(1 << zoom);
77 qreal tx = (lng + 180.0) / 360.0;
78 qreal ty = (1.0 - log(tan(lat * M_PI / 180.0) +
79 1.0 / cos(lat * M_PI / 180.0)) / M_PI) / 2.0;
80 return QPointF(tx * zn, ty * zn);
81}
82
83qreal longitudeFromTile(qreal tx, int zoom)
84{
85 qreal zn = static_cast<qreal>(1 << zoom);
86 qreal lat = tx / zn * 360.0 - 180.0;
87 return lat;
88}
89
90qreal latitudeFromTile(qreal ty, int zoom)
91{
92 qreal zn = static_cast<qreal>(1 << zoom);
93 qreal n = M_PI - 2 * M_PI * ty / zn;
94 qreal lng = 180.0 / M_PI * atan(0.5 * (exp(n) - exp(-n)));
95 return lng;
96}
97
98class SlippyMap: public QObject
99{
100 Q_OBJECT
101
102public:
103 int width;
104 int height;
105 int zoom;
106 qreal latitude;
107 qreal longitude;
108
109 SlippyMap(QObject *parent = 0)
110 : QObject(parent)
111 , width(400)
112 , height(300)
113 , zoom(15)
114 , latitude(59.9138204)
115 , longitude(10.7387413) {
116 m_emptyTile = QPixmap(tdim, tdim);
117 m_emptyTile.fill(Qt::lightGray);
118
119 QNetworkDiskCache *cache = new QNetworkDiskCache;
120 cache->setCacheDirectory(QDesktopServices::storageLocation
121 (QDesktopServices::CacheLocation));
122 m_manager.setCache(cache);
123 connect(&m_manager, SIGNAL(finished(QNetworkReply*)),
124 this, SLOT(handleNetworkData(QNetworkReply*)));
125 }
126
127 void invalidate() {
128 if (width <= 0 || height <= 0)
129 return;
130
131 QPointF ct = tileForCoordinate(latitude, longitude, zoom);
132 qreal tx = ct.x();
133 qreal ty = ct.y();
134
135 // top-left corner of the center tile
136 int xp = width / 2 - (tx - floor(tx)) * tdim;
137 int yp = height / 2 - (ty - floor(ty)) * tdim;
138
139 // first tile vertical and horizontal
140 int xa = (xp + tdim - 1) / tdim;
141 int ya = (yp + tdim - 1) / tdim;
142 int xs = static_cast<int>(tx) - xa;
143 int ys = static_cast<int>(ty) - ya;
144
145 // offset for top-left tile
146 m_offset = QPoint(xp - xa * tdim, yp - ya * tdim);
147
148 // last tile vertical and horizontal
149 int xe = static_cast<int>(tx) + (width - xp - 1) / tdim;
150 int ye = static_cast<int>(ty) + (height - yp - 1) / tdim;
151
152 // build a rect
153 m_tilesRect = QRect(xs, ys, xe - xs + 1, ye - ys + 1);
154
155 if (m_url.isEmpty())
156 download();
157
158 emit updated(QRect(0, 0, width, height));
159 }
160
161 void render(QPainter *p, const QRect &rect) {
162 for (int x = 0; x <= m_tilesRect.width(); ++x)
163 for (int y = 0; y <= m_tilesRect.height(); ++y) {
164 QPoint tp(x + m_tilesRect.left(), y + m_tilesRect.top());
165 QRect box = tileRect(tp);
166 if (rect.intersects(box)) {
167 if (m_tilePixmaps.contains(tp))
168 p->drawPixmap(box, m_tilePixmaps.value(tp));
169 else
170 p->drawPixmap(box, m_emptyTile);
171 }
172 }
173 }
174
175 void pan(const QPoint &delta) {
176 QPointF dx = QPointF(delta) / qreal(tdim);
177 QPointF center = tileForCoordinate(latitude, longitude, zoom) - dx;
178 latitude = latitudeFromTile(center.y(), zoom);
179 longitude = longitudeFromTile(center.x(), zoom);
180 invalidate();
181 }
182
183private slots:
184
185 void handleNetworkData(QNetworkReply *reply) {
186 QImage img;
187 QPoint tp = reply->request().attribute(QNetworkRequest::User).toPoint();
188 QUrl url = reply->url();
189 if (!reply->error())
190 if (!img.load(reply, 0))
191 img = QImage();
192 reply->deleteLater();
193 m_tilePixmaps[tp] = QPixmap::fromImage(img);
194 if (img.isNull())
195 m_tilePixmaps[tp] = m_emptyTile;
196 emit updated(tileRect(tp));
197
198 // purge unused spaces
199 QRect bound = m_tilesRect.adjusted(-2, -2, 2, 2);
200 foreach(QPoint tp, m_tilePixmaps.keys())
201 if (!bound.contains(tp))
202 m_tilePixmaps.remove(tp);
203
204 download();
205 }
206
207 void download() {
208 QPoint grab(0, 0);
209 for (int x = 0; x <= m_tilesRect.width(); ++x)
210 for (int y = 0; y <= m_tilesRect.height(); ++y) {
211 QPoint tp = m_tilesRect.topLeft() + QPoint(x, y);
212 if (!m_tilePixmaps.contains(tp)) {
213 grab = tp;
214 break;
215 }
216 }
217 if (grab == QPoint(0, 0)) {
218 m_url = QUrl();
219 return;
220 }
221
222 QString path = "http://tile.openstreetmap.org/%1/%2/%3.png";
223 m_url = QUrl(path.arg(zoom).arg(grab.x()).arg(grab.y()));
224 QNetworkRequest request;
225 request.setUrl(m_url);
226 request.setRawHeader("User-Agent", "Nokia (Qt) Graphics Dojo 1.0");
227 request.setAttribute(QNetworkRequest::User, QVariant(grab));
228 m_manager.get(request);
229 }
230
231signals:
232 void updated(const QRect &rect);
233
234protected:
235 QRect tileRect(const QPoint &tp) {
236 QPoint t = tp - m_tilesRect.topLeft();
237 int x = t.x() * tdim + m_offset.x();
238 int y = t.y() * tdim + m_offset.y();
239 return QRect(x, y, tdim, tdim);
240 }
241
242private:
243 QPoint m_offset;
244 QRect m_tilesRect;
245 QPixmap m_emptyTile;
246 QHash<QPoint, QPixmap> m_tilePixmaps;
247 QNetworkAccessManager m_manager;
248 QUrl m_url;
249};
250
251class LightMaps: public QWidget
252{
253 Q_OBJECT
254
255public:
256 LightMaps(QWidget *parent = 0)
257 : QWidget(parent)
258 , pressed(false)
259 , snapped(false)
260 , zoomed(false)
261 , invert(false) {
262 m_normalMap = new SlippyMap(this);
263 m_largeMap = new SlippyMap(this);
264 connect(m_normalMap, SIGNAL(updated(QRect)), SLOT(updateMap(QRect)));
265 connect(m_largeMap, SIGNAL(updated(QRect)), SLOT(update()));
266 }
267
268 void setCenter(qreal lat, qreal lng) {
269 m_normalMap->latitude = lat;
270 m_normalMap->longitude = lng;
271 m_normalMap->invalidate();
272 m_largeMap->invalidate();
273 }
274
275public slots:
276 void toggleNightMode() {
277 invert = !invert;
278 update();
279 }
280
281private slots:
282 void updateMap(const QRect &r) {
283 update(r);
284 }
285
286protected:
287
288 void activateZoom() {
289 zoomed = true;
290 tapTimer.stop();
291 m_largeMap->zoom = m_normalMap->zoom + 1;
292 m_largeMap->width = m_normalMap->width * 2;
293 m_largeMap->height = m_normalMap->height * 2;
294 m_largeMap->latitude = m_normalMap->latitude;
295 m_largeMap->longitude = m_normalMap->longitude;
296 m_largeMap->invalidate();
297 update();
298 }
299
300 void resizeEvent(QResizeEvent *) {
301 m_normalMap->width = width();
302 m_normalMap->height = height();
303 m_normalMap->invalidate();
304 m_largeMap->width = m_normalMap->width * 2;
305 m_largeMap->height = m_normalMap->height * 2;
306 m_largeMap->invalidate();
307 }
308
309 void paintEvent(QPaintEvent *event) {
310 QPainter p;
311 p.begin(this);
312 m_normalMap->render(&p, event->rect());
313 p.setPen(Qt::black);
314#if defined(Q_OS_SYMBIAN)
315 QFont font = p.font();
316 font.setPixelSize(13);
317 p.setFont(font);
318#endif
319 p.drawText(rect(), Qt::AlignBottom | Qt::TextWordWrap,
320 "Map data CCBYSA 2009 OpenStreetMap.org contributors");
321 p.end();
322
323 if (zoomed) {
324 int dim = qMin(width(), height());
325 int magnifierSize = qMin(MAX_MAGNIFIER, dim * 2 / 3);
326 int radius = magnifierSize / 2;
327 int ring = radius - 15;
328 QSize box = QSize(magnifierSize, magnifierSize);
329
330 // reupdate our mask
331 if (maskPixmap.size() != box) {
332 maskPixmap = QPixmap(box);
333 maskPixmap.fill(Qt::transparent);
334
335 QRadialGradient g;
336 g.setCenter(radius, radius);
337 g.setFocalPoint(radius, radius);
338 g.setRadius(radius);
339 g.setColorAt(1.0, QColor(255, 255, 255, 0));
340 g.setColorAt(0.5, QColor(128, 128, 128, 255));
341
342 QPainter mask(&maskPixmap);
343 mask.setRenderHint(QPainter::Antialiasing);
344 mask.setCompositionMode(QPainter::CompositionMode_Source);
345 mask.setBrush(g);
346 mask.setPen(Qt::NoPen);
347 mask.drawRect(maskPixmap.rect());
348 mask.setBrush(QColor(Qt::transparent));
349 mask.drawEllipse(g.center(), ring, ring);
350 mask.end();
351 }
352
353 QPoint center = dragPos - QPoint(0, radius);
354 center = center + QPoint(0, radius / 2);
355 QPoint corner = center - QPoint(radius, radius);
356
357 QPoint xy = center * 2 - QPoint(radius, radius);
358
359 // only set the dimension to the magnified portion
360 if (zoomPixmap.size() != box) {
361 zoomPixmap = QPixmap(box);
362 zoomPixmap.fill(Qt::lightGray);
363 }
364 if (true) {
365 QPainter p(&zoomPixmap);
366 p.translate(-xy);
367 m_largeMap->render(&p, QRect(xy, box));
368 p.end();
369 }
370
371 QPainterPath clipPath;
372 clipPath.addEllipse(center, ring, ring);
373
374 QPainter p(this);
375 p.setRenderHint(QPainter::Antialiasing);
376 p.setClipPath(clipPath);
377 p.drawPixmap(corner, zoomPixmap);
378 p.setClipping(false);
379 p.drawPixmap(corner, maskPixmap);
380 p.setPen(Qt::gray);
381 p.drawPath(clipPath);
382 }
383 if (invert) {
384 QPainter p(this);
385 p.setCompositionMode(QPainter::CompositionMode_Difference);
386 p.fillRect(event->rect(), Qt::white);
387 p.end();
388 }
389 }
390
391 void timerEvent(QTimerEvent *) {
392 if (!zoomed)
393 activateZoom();
394 update();
395 }
396
397 void mousePressEvent(QMouseEvent *event) {
398 if (event->buttons() != Qt::LeftButton)
399 return;
400 pressed = snapped = true;
401 pressPos = dragPos = event->pos();
402 tapTimer.stop();
403 tapTimer.start(HOLD_TIME, this);
404 }
405
406 void mouseMoveEvent(QMouseEvent *event) {
407 if (!event->buttons())
408 return;
409 if (!zoomed) {
410 if (!pressed || !snapped) {
411 QPoint delta = event->pos() - pressPos;
412 pressPos = event->pos();
413 m_normalMap->pan(delta);
414 return;
415 } else {
416 const int threshold = 10;
417 QPoint delta = event->pos() - pressPos;
418 if (snapped) {
419 snapped &= delta.x() < threshold;
420 snapped &= delta.y() < threshold;
421 snapped &= delta.x() > -threshold;
422 snapped &= delta.y() > -threshold;
423 }
424 if (!snapped)
425 tapTimer.stop();
426 }
427 } else {
428 dragPos = event->pos();
429 update();
430 }
431 }
432
433 void mouseReleaseEvent(QMouseEvent *) {
434 zoomed = false;
435 update();
436 }
437
438 void keyPressEvent(QKeyEvent *event) {
439 if (!zoomed) {
440 if (event->key() == Qt::Key_Left)
441 m_normalMap->pan(QPoint(20, 0));
442 if (event->key() == Qt::Key_Right)
443 m_normalMap->pan(QPoint(-20, 0));
444 if (event->key() == Qt::Key_Up)
445 m_normalMap->pan(QPoint(0, 20));
446 if (event->key() == Qt::Key_Down)
447 m_normalMap->pan(QPoint(0, -20));
448 if (event->key() == Qt::Key_Z || event->key() == Qt::Key_Select) {
449 dragPos = QPoint(width() / 2, height() / 2);
450 activateZoom();
451 }
452 } else {
453 if (event->key() == Qt::Key_Z || event->key() == Qt::Key_Select) {
454 zoomed = false;
455 update();
456 }
457 QPoint delta(0, 0);
458 if (event->key() == Qt::Key_Left)
459 delta = QPoint(-15, 0);
460 if (event->key() == Qt::Key_Right)
461 delta = QPoint(15, 0);
462 if (event->key() == Qt::Key_Up)
463 delta = QPoint(0, -15);
464 if (event->key() == Qt::Key_Down)
465 delta = QPoint(0, 15);
466 if (delta != QPoint(0, 0)) {
467 dragPos += delta;
468 update();
469 }
470 }
471 }
472
473private:
474 SlippyMap *m_normalMap;
475 SlippyMap *m_largeMap;
476 bool pressed;
477 bool snapped;
478 QPoint pressPos;
479 QPoint dragPos;
480 QBasicTimer tapTimer;
481 bool zoomed;
482 QPixmap zoomPixmap;
483 QPixmap maskPixmap;
484 bool invert;
485};
486
487class MapZoom : public QMainWindow
488{
489 Q_OBJECT
490
491private:
492 LightMaps *map;
493
494public:
495 MapZoom(): QMainWindow(0) {
496 map = new LightMaps(this);
497 setCentralWidget(map);
498 map->setFocus();
499
500 QAction *osloAction = new QAction("&Oslo", this);
501 QAction *berlinAction = new QAction("&Berlin", this);
502 QAction *jakartaAction = new QAction("&Jakarta", this);
503 QAction *nightModeAction = new QAction("Night Mode", this);
504 nightModeAction->setCheckable(true);
505 nightModeAction->setChecked(false);
506 QAction *osmAction = new QAction("About OpenStreetMap", this);
507 connect(osloAction, SIGNAL(triggered()), SLOT(chooseOslo()));
508 connect(berlinAction, SIGNAL(triggered()), SLOT(chooseBerlin()));
509 connect(jakartaAction, SIGNAL(triggered()), SLOT(chooseJakarta()));
510 connect(nightModeAction, SIGNAL(triggered()), map, SLOT(toggleNightMode()));
511 connect(osmAction, SIGNAL(triggered()), SLOT(aboutOsm()));
512
513#if defined(Q_OS_SYMBIAN) || defined(Q_OS_WINCE_WM)
514 menuBar()->addAction(osloAction);
515 menuBar()->addAction(berlinAction);
516 menuBar()->addAction(jakartaAction);
517 menuBar()->addAction(nightModeAction);
518 menuBar()->addAction(osmAction);
519#else
520 QMenu *menu = menuBar()->addMenu("&Options");
521 menu->addAction(osloAction);
522 menu->addAction(berlinAction);
523 menu->addAction(jakartaAction);
524 menu->addSeparator();
525 menu->addAction(nightModeAction);
526 menu->addAction(osmAction);
527#endif
528
529 QTimer::singleShot(0, this, SLOT(delayedInit()));
530 }
531
532private slots:
533
534 void delayedInit() {
535#if defined(Q_OS_SYMBIAN)
536 qt_SetDefaultIap();
537#endif
538 }
539
540 void chooseOslo() {
541 map->setCenter(59.9138204, 10.7387413);
542 }
543
544 void chooseBerlin() {
545 map->setCenter(52.52958999943302, 13.383053541183472);
546 }
547
548 void chooseJakarta() {
549 map->setCenter(-6.211544, 106.845172);
550 }
551
552 void aboutOsm() {
553 QDesktopServices::openUrl(QUrl("http://www.openstreetmap.org"));
554 }
555};
556
557
558#include "lightmaps.moc"
559
560int main(int argc, char **argv)
561{
562#if defined(Q_WS_X11)
563 QApplication::setGraphicsSystem("raster");
564#endif
565
566 QApplication app(argc, argv);
567 app.setApplicationName("LightMaps");
568
569 MapZoom w;
570 w.setWindowTitle("OpenStreetMap");
571#if defined(Q_OS_SYMBIAN) || defined(Q_OS_WINCE_WM)
572 w.showMaximized();
573#else
574 w.resize(600, 450);
575 w.show();
576#endif
577
578 return app.exec();
579}
Note: See TracBrowser for help on using the repository browser.