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

Last change on this file was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

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