# -------------------------------------------------
TARGET = Situare
TEMPLATE = app
-QT += network testlib
+QT += network \
+ testlib
INCLUDEPATH += . \
src/tests/testMap/ \
src/map/
SOURCES += src/ui/mainwindow.cpp \
- src/map/mapfetcher.cpp \
+ src/map/mapfetcherqueue.cpp \
src/map/mapengine.cpp \
tests/testMap/testmapfetcher.cpp
-# tests/testMap/testmapengine.cpp
-# src/main.cpp \
+
+# tests/testMap/testmapengine.cpp
+# src/main.cpp \
HEADERS += src/ui/mainwindow.h \
- src/map/mapfetcher.h \
+ src/map/mapfetcherqueue.h \
src/map/mapengine.h
--- /dev/null
+#include <QNetworkAccessManager>
+#include <QNetworkRequest>
+#include <QNetworkReply>
+#include <QUrl>
+#include <QDebug>
+#include <QPixmap>
+#include <QNetworkDiskCache>
+#include <QDesktopServices>
+#include <QTimer>
+#include <QMutex>
+
+#include "mapfetcherqueue.h"
+
+static int MAX_QUEUE = 10;
+
+MapFetcherQueue::MapFetcherQueue(QObject *parent)
+ : QObject(parent)
+ , downloadCount(0)
+{
+ QNetworkDiskCache *diskCache = new QNetworkDiskCache(this);
+ diskCache->setCacheDirectory(QDesktopServices::storageLocation(
+ QDesktopServices::CacheLocation));
+ m_manager.setCache(diskCache);
+}
+
+void MapFetcherQueue::fetchMapImage(const QUrl &url)
+{
+ if (url.isEmpty())
+ return;
+
+ if (downloadQueue.isEmpty())
+ QTimer::singleShot(0, this, SLOT(startNextDownload()));
+
+ if (downloadQueue.length() >= MAX_QUEUE) {
+ qDebug() << "removed from queue";
+ deleteCurrent();
+ downloadQueue.dequeue();
+ }
+
+ downloadQueue.enqueue(url);
+ qDebug() << "fetchMapImage() " << url.path();
+}
+
+void MapFetcherQueue::startNextDownload()
+{
+ if (downloadQueue.isEmpty())
+ return;
+
+ QUrl url = downloadQueue.dequeue();
+
+ QNetworkRequest request(url);
+ request.setRawHeader("User-Agent", "Map Test");
+ request.setAttribute(QNetworkRequest::CacheLoadControlAttribute,
+ QNetworkRequest::PreferCache);
+
+ currentDownload = m_manager.get(request);
+ connect(currentDownload, SIGNAL(finished()), this, SLOT(downloadFinished()));
+ connect(currentDownload, SIGNAL(readyRead()), this, SLOT(downloadReadyRead()));
+ connect(currentDownload, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(networkError()));
+}
+
+void MapFetcherQueue::downloadFinished()
+{
+ static int counter = 0;
+ qDebug() << "downloadFinished(): " << counter++;
+
+ deleteCurrent();
+ startNextDownload();
+}
+
+void MapFetcherQueue::deleteCurrent()
+{
+ mutex.lock();
+ if (currentDownload != NULL)
+ currentDownload->deleteLater();
+ mutex.unlock();
+}
+
+void MapFetcherQueue::downloadReadyRead()
+{
+ qDebug() << "downloadReadyRead()" << currentDownload->url().path();
+
+ if (currentDownload->error()) {
+ qDebug() << "ERROR";
+ emit error(currentDownload->errorString());
+ }
+ else {
+ QImage image;
+ QUrl url = currentDownload->url();
+ if (!image.load(currentDownload, 0))
+ image = QImage();
+
+ emit mapImageReceived(url, QPixmap::fromImage(image));
+ }
+}
+
+void MapFetcherQueue::networkError()
+{
+ emit error(currentDownload->errorString());
+}
+
+MapFetcherQueue::~MapFetcherQueue()
+{
+
+}
--- /dev/null
+#ifndef MAPFETCHERQUEUE_H
+#define MAPFETCHERQUEUE_H
+
+#include <QtCore>
+#include <QNetworkAccessManager>
+
+class QNetworkReply;
+class QUrl;
+class QMutex;
+
+#include "mapfetcher.h"
+
+/**
+* @brief MapFetcher handles requests to get map tiles.
+*
+*
+*
+* @class MapFetcher mapfetcher.h "map/mapfetcher.h"
+*/
+class MapFetcherQueue : public QObject
+{
+ Q_OBJECT
+
+public:
+ /**
+ * @brief Constructor for MapFetcher.
+ *
+ * @fn MapFetcher
+ * @param parent parent object
+ */
+ MapFetcherQueue(QObject *parent = 0);
+
+ ~MapFetcherQueue();
+
+ /**
+ * @brief Fetch image from given URL.
+ *
+ * @fn fetchMapImage
+ * @param url URL to image
+ */
+ void fetchMapImage(const QUrl &url);
+
+ void deleteCurrent();
+
+signals:
+ /**
+ * @brief Signal which is emitted when a map tile
+ * is received from the server and loaded to pixmap.
+ *
+ * @fn mapImageReceived
+ * @param url URL to image
+ * @param image image pixmap
+ */
+ void mapImageReceived(const QUrl &url, const QPixmap &image);
+
+ /**
+ * @brief Signal which is emitted when there is error
+ * in network reply.
+ *
+ * @fn error
+ * @param message error message
+ */
+ void error(const QString &message);
+
+private slots:
+ /**
+ * @brief This slot is called when network manager has finished
+ * the download.
+ *
+ * @fn downloadFinished
+ * @param reply
+ */
+ void downloadFinished();
+
+ void startNextDownload();
+ void downloadReadyRead();
+ void networkError();
+
+
+private:
+ QNetworkAccessManager m_manager;
+ QQueue<QUrl> downloadQueue;
+ QNetworkReply *currentDownload;
+ QTime downloadTime;
+ QMutex mutex;
+
+ int downloadCount;
+};
+
+#endif
#include <QUrl>
#include <QImage>
-#include "../map/mapfetcher.h"
+#include "mapfetcherqueue.h"
class TestMapFetcher : public QObject
{
private:
- MapFetcher *mapFetcher;
+ MapFetcherQueue *mapFetcher;
};
TestMapFetcher::TestMapFetcher()
{
- mapFetcher = new MapFetcher();
+ mapFetcher = new MapFetcherQueue();
}
void TestMapFetcher::testFetchImage()
QVERIFY(imageReceivedSpy.isValid());
//Incorrect URL
- QUrl url1("http://tile.openstreetmap.org/7/63/22.gi");
+ /*QUrl url1("http://tile.openstreetmap.org/7/63/22.gi");
mapFetcher->fetchMapImage(url1);
QTest::qWait(1000);
QCOMPARE(imageReceivedErrorSpy.count(), 1);
QTest::qWait(1000);
QCOMPARE(imageReceivedSpy.count(), 1);
QList<QVariant> signalArgs2 = imageReceivedSpy.takeFirst();
- QCOMPARE(url2, signalArgs2.at(0).toUrl());
+ QCOMPARE(url2, signalArgs2.at(0).toUrl());*/
- //10 requests
- qDebug() << "Start:" << QTime::currentTime().toString("hh:mm:ss:zzz");
+ //20 requests immediately
for (int i = 0; i < 10; ++i) {
- QUrl url(QString("http://tile.openstreetmap.org/7/63/4%1.png").arg(i));
+ QUrl url(QString("http://tile.openstreetmap.org/7/63/2%1.png").arg(i));
+ mapFetcher->fetchMapImage(url);
+ }
+ for (int i = 0; i < 10; ++i) {
+ QUrl url(QString("http://tile.openstreetmap.org/7/63/3%1.png").arg(i));
mapFetcher->fetchMapImage(url);
}
QTest::qWait(2000);