/**************************************************************************** ** ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). ** All rights reserved. ** Contact: Nokia Corporation (qt-info@nokia.com) ** ** This file is part of the demonstration applications of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** GNU Lesser General Public License Usage ** This file may be used under the terms of the GNU Lesser General Public ** License version 2.1 as published by the Free Software Foundation and ** appearing in the file LICENSE.LGPL included in the packaging of this ** file. Please review the following information to ensure the GNU Lesser ** General Public License version 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional ** rights. These rights are described in the Nokia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU General ** Public License version 3.0 as published by the Free Software Foundation ** and appearing in the file LICENSE.GPL included in the packaging of this ** file. Please review the following information to ensure the GNU General ** Public License version 3.0 requirements will be met: ** http://www.gnu.org/copyleft/gpl.html. ** ** Other Usage ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. ** ** ** ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include #include #include "slippymap.h" #ifndef M_PI #define M_PI 3.14159265358979323846 #endif uint qHash(const QPoint& p) { return p.x() * 17 ^ p.y(); } // tile size in pixels const int tdim = 256; QPointF tileForCoordinate(qreal lat, qreal lng, int zoom) { qreal zn = static_cast(1 << zoom); qreal tx = (lng + 180.0) / 360.0; qreal ty = (1.0 - log(tan(lat * M_PI / 180.0) + 1.0 / cos(lat * M_PI / 180.0)) / M_PI) / 2.0; return QPointF(tx * zn, ty * zn); } qreal longitudeFromTile(qreal tx, int zoom) { qreal zn = static_cast(1 << zoom); qreal lat = tx / zn * 360.0 - 180.0; return lat; } qreal latitudeFromTile(qreal ty, int zoom) { qreal zn = static_cast(1 << zoom); qreal n = M_PI - 2 * M_PI * ty / zn; qreal lng = 180.0 / M_PI * atan(0.5 * (exp(n) - exp(-n))); return lng; } SlippyMap::SlippyMap(QObject *parent) : QObject(parent), width(400), height(300), zoom(15), latitude(59.9138204), longitude(10.7387413) { m_emptyTile = QPixmap(tdim, tdim); m_emptyTile.fill(Qt::lightGray); QNetworkDiskCache *cache = new QNetworkDiskCache; cache->setCacheDirectory(QDesktopServices::storageLocation (QDesktopServices::CacheLocation)); m_manager.setCache(cache); connect(&m_manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(handleNetworkData(QNetworkReply*))); } void SlippyMap::invalidate() { if (width <= 0 || height <= 0) return; QPointF ct = tileForCoordinate(latitude, longitude, zoom); qreal tx = ct.x(); qreal ty = ct.y(); // top-left corner of the center tile int xp = width / 2 - (tx - floor(tx)) * tdim; int yp = height / 2 - (ty - floor(ty)) * tdim; // first tile vertical and horizontal int xa = (xp + tdim - 1) / tdim; int ya = (yp + tdim - 1) / tdim; int xs = static_cast(tx) - xa; int ys = static_cast(ty) - ya; // offset for top-left tile m_offset = QPoint(xp - xa * tdim, yp - ya * tdim); // last tile vertical and horizontal int xe = static_cast(tx) + (width - xp - 1) / tdim; int ye = static_cast(ty) + (height - yp - 1) / tdim; // build a rect m_tilesRect = QRect(xs, ys, xe - xs + 1, ye - ys + 1); if (m_url.isEmpty()) download(); emit updated(QRect(0, 0, width, height)); } void SlippyMap::render(QPainter *p, const QRect &rect) { for (int x = 0; x <= m_tilesRect.width(); ++x) for (int y = 0; y <= m_tilesRect.height(); ++y) { QPoint tp(x + m_tilesRect.left(), y + m_tilesRect.top()); QRect box = tileRect(tp); if (rect.intersects(box)) { if (m_tilePixmaps.contains(tp)) p->drawPixmap(box, m_tilePixmaps.value(tp)); else p->drawPixmap(box, m_emptyTile); } } } void SlippyMap::pan(const QPoint &delta) { QPointF dx = QPointF(delta) / qreal(tdim); QPointF center = tileForCoordinate(latitude, longitude, zoom) - dx; latitude = latitudeFromTile(center.y(), zoom); longitude = longitudeFromTile(center.x(), zoom); invalidate(); } void SlippyMap::handleNetworkData(QNetworkReply *reply) { QImage img; QPoint tp = reply->request().attribute(QNetworkRequest::User).toPoint(); QUrl url = reply->url(); if (!reply->error()) if (!img.load(reply, 0)) img = QImage(); reply->deleteLater(); m_tilePixmaps[tp] = QPixmap::fromImage(img); if (img.isNull()) m_tilePixmaps[tp] = m_emptyTile; emit updated(tileRect(tp)); // purge unused spaces QRect bound = m_tilesRect.adjusted(-2, -2, 2, 2); foreach(QPoint tp, m_tilePixmaps.keys()) if (!bound.contains(tp)) m_tilePixmaps.remove(tp); download(); } void SlippyMap::download() { QPoint grab(0, 0); for (int x = 0; x <= m_tilesRect.width(); ++x) for (int y = 0; y <= m_tilesRect.height(); ++y) { QPoint tp = m_tilesRect.topLeft() + QPoint(x, y); if (!m_tilePixmaps.contains(tp)) { grab = tp; break; } } if (grab == QPoint(0, 0)) { m_url = QUrl(); return; } QString path = "http://tile.openstreetmap.org/%1/%2/%3.png"; m_url = QUrl(path.arg(zoom).arg(grab.x()).arg(grab.y())); QNetworkRequest request; request.setUrl(m_url); request.setRawHeader("User-Agent", "Nokia (Qt) Graphics Dojo 1.0"); request.setAttribute(QNetworkRequest::User, QVariant(grab)); m_manager.get(request); } QRect SlippyMap::tileRect(const QPoint &tp) { QPoint t = tp - m_tilesRect.topLeft(); int x = t.x() * tdim + m_offset.x(); int y = t.y() * tdim + m_offset.y(); return QRect(x, y, tdim, tdim); }