1 /****************************************************************************
3 ** Copyright (C) 2011 Tito Eritja Real <jtitoo@gmail.com>
5 ** This program is free software: you can redistribute it and/or modify
6 ** it under the terms of the GNU General Public License as published by
7 ** the Free Software Foundation, either version 3 of the License, or
8 ** (at your option) any later version.
10 ** This program is distributed in the hope that it will be useful,
11 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 ** GNU General Public License for more details.
15 ** You should have received a copy of the GNU General Public License
16 ** along with this program. If not, see <http://www.gnu.org/licenses/>.
18 ****************************************************************************/
21 #include "constants.h"
25 #include <QNetworkDiskCache>
26 #include <QDesktopServices>
32 ThreadSaver::ThreadSaver(QImage qi, QString fs){
37 void ThreadSaver::run(){
39 QFile file(fileString);
41 if (!file.open(QIODevice::WriteOnly)) {
42 qDebug() << QString("Cannot open file for writing: %1").arg(file.errorString());
50 TilesMap::TilesMap(QNetworkSession *session, QObject *parent = 0, Log* log=0, int zoom=0, MapType mapType=MapTypeOpenStreetMaps) :
60 height(HEIGHT_DEFAULT),
64 m_mapsDir = QDir(QString(APPLICATION_PATH).append(MAPS_DIR));
65 m_manager = new QNetworkAccessManager(this);
67 // Creating and fullfill emptytile
68 m_emptyTile = QPixmap(tdim, tdim);
69 m_emptyTile.fill(Qt::lightGray);
71 //log->debug(QString("Is Network accessible?:%1").arg(m_manager->networkAccessible()));
72 QNetworkDiskCache *nCache = new QNetworkDiskCache;
73 nCache->setCacheDirectory(QDesktopServices::storageLocation(QDesktopServices::CacheLocation));
74 m_manager->setCache(nCache);
75 connect(m_manager, SIGNAL(finished(QNetworkReply*)),
76 this, SLOT(handleReplies(QNetworkReply*)));
78 //log->debug("exiting from slippyMap constructor!!!!");
82 TilesMap::~TilesMap() {
83 for (int i = 0; i < m_pendingReplies.size(); ++i) {
84 delete m_pendingReplies.at(i);
90 QPointF TilesMap::coordinate2tile(qreal lat, qreal lng, int zoom)
92 qreal zn = static_cast<qreal>(1 << zoom);
93 qreal tx = (lng + 180.0) / 360.0 * zn;
94 qreal ty = (1.0 - log(tan(lat * PI / 180.0) + 1.0 / cos(lat * PI / 180.0)) / PI) / 2.0 * zn;
95 return QPointF(tx, ty);
99 qreal TilesMap::tilex2long(qreal x, int zoom)
101 return (x / pow(2.0, zoom) * 360.0 - 180);
104 qreal TilesMap::tiley2lat(qreal y, int zoom)
106 qreal n = M_PI - 2.0 * M_PI * y / pow(2.0, zoom);
107 return 180.0 / M_PI * atan(0.5 * (exp(n) - exp(-n)));
110 void TilesMap::updateTiles(){
111 updateTiles(latitude,longitude);
114 void TilesMap::updateTiles(qreal lat, qreal lng){
116 if (width <= 0 || height <= 0 || (lat==0 && lng==0))
121 QPointF center = coordinate2tile(latitude,longitude,zoom);
123 // getting top-left corner of the centered tile
124 QPoint topLeft = QPoint(width / 2 - (center.x() - floor(center.x())) * tdim,height / 2 - (center.y() - floor(center.y())) * tdim);
126 // getting first tile vertical and horizontal
127 QPoint offsetTile=QPoint((topLeft.x() + tdim - 1) / tdim,(topLeft.y() + tdim - 1) / tdim);
129 QPoint firstTile = QPoint(static_cast<int>(center.x()) - offsetTile.x(),static_cast<int>(center.y()) - offsetTile.y());
131 // offset for top-left tile
132 m_offset = QPoint(topLeft.x() - offsetTile.x() * tdim, topLeft.y() - offsetTile.y() * tdim);
134 QPoint lastTile = QPoint(static_cast<int>(center.x()) + (width - topLeft.x() - 1) / tdim, static_cast<int>(center.y()) + (height - topLeft.y() - 1) / tdim);
137 m_tilesRect = QRect(firstTile.x(), firstTile.y(), lastTile.x() - firstTile.x() + 1, lastTile.y() - firstTile.y() + 1);
141 emit updated(QRect(0, 0, width, height));
144 void TilesMap::getTiles() {
146 QString path = mapUrlProvider();
148 for (int x = 0; x <= m_tilesRect.width(); ++x)
149 for (int y = 0; y <= m_tilesRect.height(); ++y) {
150 QPoint tp = m_tilesRect.topLeft() + QPoint(x, y);
152 if(!m_tileMaps.contains(tp)){
155 if(mapIsOnCache(zoom,grab.x(),grab.y())){
156 // getting maps from tile cache
157 m_log->debug("Map is on cache");
158 QString imgString = QString(fileUrlProvider()).arg(zoom).arg(grab.x()).arg(grab.y());
159 m_log->info(QString("map name:%1").arg(imgString));
163 m_log->debug(QString("going to update tile with tile: x=%1,y=%2").arg(grab.x()).arg(grab.y()));
164 m_tileMaps[grab] = QPixmap::fromImage(img);
166 m_tileMaps[grab]= m_emptyTile;
167 // TODO: remove empty image from disk
170 emit updated(tileRect(grab));
171 }else if(network && (!m_tileRequests.contains(grab) || timeout(m_tileRequests[grab].dateTime))){
172 // request Tiles from Server
173 m_url = QUrl(path.arg(zoom).arg(grab.x()).arg(grab.y()));
174 QNetworkRequest request;
175 m_log->info(QString("making a url request:").append(m_url.toString()));
176 request.setUrl(m_url);
177 request.setRawHeader("User-Agent", "GPSSniffer 1.0");
178 request.setAttribute(QNetworkRequest::User, QVariant(grab));
179 m_pendingReplies << m_manager->get(request);
181 m_tileRequests[grab]=TileRequest(zoom,QDateTime::currentDateTime(),cache,false);
188 void TilesMap::cancelDownloading(){
192 int TilesMap::downloadMaps(Track* track_p){
196 // Downloading Window Map
198 m_log->info("starting to downloading maps");
200 downloadWindow(&numTiles,zoom);
202 m_log->info("simple maps aded");
204 // ading tiles needed by track
205 QList<GpsPoint*> gpsPoints = track_p->getGpsPoints();
206 int updateProgress=0;
207 for(int zoom_temp=zoom; zoom_temp <= MAX_ZOOM_DOWNL; zoom_temp++){
208 for (int i = 0; i < gpsPoints.size(); ++i) {
209 GpsPoint* point = gpsPoints.at(i);
210 downloadTiles(point->getLatitude(), point->getLongitude(),zoom_temp,&numTiles);
211 emit pointsRequested(updateProgress);
215 m_log->info(QString("Num tiles to ad:%1").arg(numTiles));
219 void TilesMap::downloadTiles(qreal lat, qreal lng, int zoom, int* numTiles){
222 if (width <= 0 || height <= 0 || (lat==0 && lng==0))
227 QPointF center = coordinate2tile(latitude,longitude,zoom);
229 // getting top-left corner of the centered tile
230 QPoint topLeft = QPoint(width / 2 - (center.x() - floor(center.x())) * tdim,height / 2 - (center.y() - floor(center.y())) * tdim);
232 // getting first tile vertical and horizontal
233 QPoint offsetTile=QPoint((topLeft.x() + tdim - 1) / tdim,(topLeft.y() + tdim - 1) / tdim);
235 QPoint firstTile = QPoint(static_cast<int>(center.x()) - offsetTile.x(),static_cast<int>(center.y()) - offsetTile.y());
237 // offset for top-left tile
238 m_offset = QPoint(topLeft.x() - offsetTile.x() * tdim, topLeft.y() - offsetTile.y() * tdim);
240 QPoint lastTile = QPoint(static_cast<int>(center.x()) + (width - topLeft.x() - 1) / tdim, static_cast<int>(center.y()) + (height - topLeft.y() - 1) / tdim);
243 m_tilesRect = QRect(firstTile.x(), firstTile.y(), lastTile.x() - firstTile.x() + 1, lastTile.y() - firstTile.y() + 1);
245 downloadWindow(numTiles,zoom);
250 void TilesMap::downloadWindow(int* numTiles,int zoom){
252 QString path = mapUrlProvider();
254 for (int x = 0; x <= m_tilesRect.width(); ++x){
255 for (int y = 0; y <= m_tilesRect.height(); ++y) {
256 QPoint tp = m_tilesRect.topLeft() + QPoint(x, y);
258 if(!mapIsOnCache(zoom,grab.x(),grab.y()) && network && (!m_tileRequests.contains(grab) || timeout(m_tileRequests[grab].dateTime))){
260 // request Tiles from Server
261 m_url = QUrl(path.arg(zoom).arg(grab.x()).arg(grab.y()));
262 QNetworkRequest request;
263 m_log->info(QString("making %1 url request:").arg(*numTiles).append(m_url.toString()));
264 request.setUrl(m_url);
265 request.setRawHeader("User-Agent", "GPSSniffer 1.0");
266 request.setAttribute(QNetworkRequest::User, QVariant(grab));
267 m_pendingReplies << m_manager->get(request);
269 m_tileRequests[grab]= TileRequest(zoom,QDateTime::currentDateTime(),true,true);
277 void TilesMap::render(QPainter *p, const QRect &rect) {
278 //log->debug("rendering maps...");
279 for (int x = 0; x <= m_tilesRect.width(); ++x)
280 for (int y = 0; y <= m_tilesRect.height(); ++y) {
281 QPoint tp(x + m_tilesRect.left(), y + m_tilesRect.top());
282 QRect box = tileRect(tp);
283 if (rect.intersects(box)) {
284 if (m_tileMaps.contains(tp))
285 p->drawPixmap(box, m_tileMaps.value(tp));
287 p->drawPixmap(box, m_emptyTile);
290 //log->debug("done");
293 void TilesMap::pan(const QPoint &delta) {
294 QPointF dx = QPointF(delta) / qreal(tdim);
295 QPointF center = coordinate2tile(latitude, longitude, zoom) - dx;
296 latitude = tiley2lat(center.y(), zoom);
297 longitude = tilex2long(center.x(), zoom);
301 void TilesMap::updatePosition(GpsPoint point) {
302 latitude = point.getLatitude();
303 longitude = point.getLongitude();
307 void TilesMap::clearAllMaps(){
309 m_pendingReplies.clear();
310 m_tileRequests.clear();
315 void TilesMap::handleReplies(QNetworkReply *reply){
318 QPoint tp = reply->request().attribute(QNetworkRequest::User).toPoint();
319 TileRequest tr = m_tileRequests.value(tp);
322 if(!QDir(mapsDir()).exists()){
323 QDir().mkdir(mapsDir());
325 if (!reply->error()){
327 if (!img.load(reply, 0)){
337 QString fileString = QString(fileUrlProvider()).arg(zoomReply).arg(tp.x()).arg(tp.y());
339 if(!QDir(urlProvider()).exists())
340 QDir().mkdir(urlProvider());
342 if(!QDir(urlProvider().append("%1/").arg(zoomReply)).exists())
343 QDir().mkdir(urlProvider().append("%1").arg(zoomReply));
345 if(!QDir(urlProvider().append("%1/").arg(zoomReply).append("%1/").arg(tp.x())).exists())
346 QDir().mkdir(urlProvider().append("%1/").arg(zoomReply).append("%1/").arg(tp.x()));
349 QFile file(fileString);
351 if (!file.open(QIODevice::WriteOnly)) {
352 qDebug() << QString("Cannot open file for writing: %1").arg(file.errorString());
354 img.save(fileString);
361 m_pendingReplies.removeAll(reply);
362 reply->deleteLater();
364 m_tileRequests.remove(tp);
368 m_tileMaps[tp] = QPixmap::fromImage(img);
370 m_tileMaps[tp] = m_emptyTile;
372 emit updated(tileRect(tp));
375 // purge unused spaces
376 QRect bound = m_tilesRect.adjusted(-2, -2, 2, 2);
377 foreach(QPoint tp, m_tileMaps.keys())
378 if (!bound.contains(tp))
379 m_tileMaps.remove(tp);
382 emit tilesDownloaded(tilesD);
389 QRect TilesMap::tileRect(const QPoint &tp) {
390 QPoint t = tp - m_tilesRect.topLeft();
391 int x = t.x() * tdim + m_offset.x();
392 int y = t.y() * tdim + m_offset.y();
394 return QRect(x, y, tdim, tdim);
397 bool TilesMap::mapIsOnCache(int zoom,int x,int y){
399 QString fileString = QString(fileUrlProvider()).arg(zoom).arg(x).arg(y);
401 QFile file(fileString);
408 bool TilesMap::timeout(QDateTime qdt){
410 QDateTime now = QDateTime::currentDateTime();
411 int duration = qdt.secsTo(now);
412 bool retvalue = (duration>=HTTP_TIMEOUT);
417 QString TilesMap::fileUrlProvider(){
419 QString dir = QString(APPLICATION_PATH).append(MAPS_DIR);
421 case MapTypeGoogleMaps:
422 dir.append("/google/%1/%2/%3.png");
425 dir.append("/icc/%1/%2/%3.png");
427 case MapTypeCloudMade:
428 dir.append("/cloudmade/%1/%2/%3.png");
430 case MapTypeOpenCycleMaps:
431 dir.append("/ocm/%1/%2/%3.png");
434 case MapTypeOpenStreetMaps:
435 dir.append("/osm/%1/%2/%3.png");
441 QString TilesMap::mapsDir(){
442 QString dir = QString(APPLICATION_PATH).append(MAPS_DIR);
446 QString TilesMap::urlProvider(){
448 QString dir = mapsDir();
450 case MapTypeGoogleMaps:
451 dir.append("/google/");
456 case MapTypeCloudMade:
457 dir.append("/cloudmade/");
459 case MapTypeOpenCycleMaps:
463 case MapTypeOpenStreetMaps:
470 QString TilesMap::mapUrlProvider(){
472 //m_log->debug("--- mapUrlProvider ---");
473 QString url("http://");
474 //m_log->debug(QString("mapType:%1").arg(mapType));
477 case MapTypeCloudMade:
478 url.append("tile.cloudmade.com/e63d1732f3be48299e0c19d6bcefe25c/3/256/%1/%2/%3.png");
480 case MapTypeGoogleMaps:
481 url.append("mt1.google.com/vt/lyrs=m@121,bike&hl=es&x=%2&y=%3&z=%1");
482 //url.append("mt1.google.com/vt/&x=%2&y=%3&z=%1");
485 url.append("norma.icc.cat/tilecache/tilecache.py/1.0.0/topo3857/%1/%2/%3.png?type=google");
487 case MapTypeOpenCycleMaps:
488 url.append("tile.opencyclemap.org/cycle/%1/%2/%3.png");
491 case MapTypeOpenStreetMaps:
492 url.append("tile.openstreetmap.org/%1/%2/%3.png");