Commit 5fb4a54f authored by Andreas Bircher's avatar Andreas Bircher

load tiles from cache if available

parent 83643d68
...@@ -112,9 +112,11 @@ QGeoTiledMapReplyQGC::networkReplyFinished() ...@@ -112,9 +112,11 @@ QGeoTiledMapReplyQGC::networkReplyFinished()
{ {
_timer.stop(); _timer.stop();
if (!_reply) { if (!_reply) {
abort();
return; return;
} }
if (_reply->error() != QNetworkReply::NoError) { if (_reply->error() != QNetworkReply::NoError) {
abort();
return; return;
} }
QByteArray a = _reply->readAll(); QByteArray a = _reply->readAll();
...@@ -195,4 +197,5 @@ QGeoTiledMapReplyQGC::timeout() ...@@ -195,4 +197,5 @@ QGeoTiledMapReplyQGC::timeout()
if(_reply) { if(_reply) {
_reply->abort(); _reply->abort();
} }
abort();
} }
...@@ -9,6 +9,8 @@ ...@@ -9,6 +9,8 @@
#include "Terrain.h" #include "Terrain.h"
#include "QGCMapEngine.h" #include "QGCMapEngine.h"
#include "QGeoMapReplyQGC.h"
#include <QtLocation/private/qgeotilespec_p.h>
#include <QUrl> #include <QUrl>
#include <QUrlQuery> #include <QUrlQuery>
...@@ -75,16 +77,31 @@ bool ElevationProvider::queryTerrainData(const QList<QGeoCoordinate>& coordinate ...@@ -75,16 +77,31 @@ bool ElevationProvider::queryTerrainData(const QList<QGeoCoordinate>& coordinate
QList<float> altitudes; QList<float> altitudes;
bool needToDownload = false; bool needToDownload = false;
foreach (QGeoCoordinate coordinate, coordinates) { foreach (QGeoCoordinate coordinate, coordinates) {
QString uniqueTileId = _uniqueTileId(coordinate); QString tileHash = _getTileHash(coordinate);
_tilesMutex.lock(); _tilesMutex.lock();
if (!_tiles.contains(uniqueTileId)) { if (!_tiles.contains(tileHash)) {
qCDebug(TerrainLog) << "Need to download tile " << uniqueTileId; qCDebug(TerrainLog) << "Need to download tile " << tileHash;
_downloadQueue.append(uniqueTileId);
if (!_downloadQueue.contains(tileHash)) {
// Schedule the fetch task
QNetworkRequest request = getQGCMapEngine()->urlFactory()->getTileURL(UrlFactory::AirmapElevation, QGCMapEngine::long2elevationTileX(coordinate.longitude(), 1), QGCMapEngine::lat2elevationTileY(coordinate.latitude(), 1), 1, &_networkManager);
QGeoTileSpec spec;
spec.setX(QGCMapEngine::long2elevationTileX(coordinate.longitude(), 1));
spec.setY(QGCMapEngine::lat2elevationTileY(coordinate.latitude(), 1));
spec.setZoom(1);
spec.setMapId(UrlFactory::AirmapElevation);
QGeoTiledMapReplyQGC* reply = new QGeoTiledMapReplyQGC(&_networkManager, request, spec);
connect(reply, &QGeoTiledMapReplyQGC::finished, this, &ElevationProvider::_fetchedTile);
connect(reply, &QGeoTiledMapReplyQGC::aborted, this, &ElevationProvider::_fetchedTile);
_downloadQueue.append(tileHash);
}
needToDownload = true; needToDownload = true;
} else { } else {
if (!needToDownload) { if (!needToDownload) {
if (_tiles[uniqueTileId].isIn(coordinate)) { if (_tiles[tileHash].isIn(coordinate)) {
altitudes.push_back(_tiles[uniqueTileId].elevation(coordinate)); altitudes.push_back(_tiles[tileHash].elevation(coordinate));
} else { } else {
qCDebug(TerrainLog) << "Error: coordinate not in tile region"; qCDebug(TerrainLog) << "Error: coordinate not in tile region";
altitudes.push_back(-1.0); altitudes.push_back(-1.0);
...@@ -103,39 +120,7 @@ bool ElevationProvider::queryTerrainData(const QList<QGeoCoordinate>& coordinate ...@@ -103,39 +120,7 @@ bool ElevationProvider::queryTerrainData(const QList<QGeoCoordinate>& coordinate
_coordinates = coordinates; _coordinates = coordinates;
_downloadTiles(); return false;
return true;
}
bool ElevationProvider::cacheTerrainTiles(const QGeoCoordinate& southWest, const QGeoCoordinate& northEast)
{
qCDebug(TerrainLog) << "Cache terrain tiles southWest:northEast" << southWest << northEast;
// Correct corners of the tile to fixed grid
QGeoCoordinate southWestCorrected;
southWestCorrected.setLatitude(floor(southWest.latitude()/QGCMapEngine::srtm1TileSize)*QGCMapEngine::srtm1TileSize);
southWestCorrected.setLongitude(floor(southWest.longitude()/QGCMapEngine::srtm1TileSize)*QGCMapEngine::srtm1TileSize);
QGeoCoordinate northEastCorrected;
northEastCorrected.setLatitude(ceil(northEast.latitude()/QGCMapEngine::srtm1TileSize)*QGCMapEngine::srtm1TileSize);
northEastCorrected.setLongitude(ceil(northEast.longitude()/QGCMapEngine::srtm1TileSize)*QGCMapEngine::srtm1TileSize);
// Add all tiles to download queue
for (double lat = southWestCorrected.latitude() + QGCMapEngine::srtm1TileSize / 2.0; lat < northEastCorrected.latitude(); lat += QGCMapEngine::srtm1TileSize) {
for (double lon = southWestCorrected.longitude() + QGCMapEngine::srtm1TileSize / 2.0; lon < northEastCorrected.longitude(); lon += QGCMapEngine::srtm1TileSize) {
QString uniqueTileId = _uniqueTileId(QGeoCoordinate(lat, lon));
_tilesMutex.lock();
if (_downloadQueue.contains(uniqueTileId) || _tiles.contains(uniqueTileId)) {
_tilesMutex.unlock();
continue;
}
_downloadQueue.append(uniqueTileId);
_tilesMutex.unlock();
qCDebug(TerrainLog) << "Adding tile to download queue: " << uniqueTileId;
}
}
_downloadTiles();
return true;
} }
void ElevationProvider::_requestFinished() void ElevationProvider::_requestFinished()
...@@ -176,93 +161,51 @@ void ElevationProvider::_requestFinished() ...@@ -176,93 +161,51 @@ void ElevationProvider::_requestFinished()
emit terrainData(false, altitudes); emit terrainData(false, altitudes);
} }
void ElevationProvider::_requestFinishedTile() void ElevationProvider::_fetchedTile()
{ {
QNetworkReply* reply = qobject_cast<QNetworkReply*>(QObject::sender()); QGeoTiledMapReplyQGC* reply = qobject_cast<QGeoTiledMapReplyQGC*>(QObject::sender());
_state = State::Idle;
// When an error occurs we still end up here if (!reply || !reply->isFinished()) {
if (reply->error() != QNetworkReply::NoError) { if (reply) {
QByteArray responseBytes = reply->readAll(); qCDebug(TerrainLog) << "Error in fetching elevation tile: " << reply->errorString();
reply->deleteLater();
QJsonParseError parseError; } else {
QJsonDocument responseJson = QJsonDocument::fromJson(responseBytes, &parseError); qCDebug(TerrainLog) << "Elevation tile fetched but invalid reply data type.";
qCDebug(TerrainLog) << "ERROR: Received " << responseJson; }
// TODO (birchera): Handle error in downloading data
_downloadTiles();
return; return;
} }
QByteArray responseBytes = reply->readAll(); QByteArray responseBytes = reply->mapImageData();
QJsonParseError parseError; QJsonParseError parseError;
QJsonDocument responseJson = QJsonDocument::fromJson(responseBytes, &parseError); QJsonDocument responseJson = QJsonDocument::fromJson(responseBytes, &parseError);
if (parseError.error != QJsonParseError::NoError) { if (parseError.error != QJsonParseError::NoError) {
// TODO (birchera): Handle error in downloading data qCDebug(TerrainLog) << "Could not parse terrain tile " << parseError.errorString();
_downloadTiles(); qCDebug(TerrainLog) << responseBytes;
reply->deleteLater();
return; return;
} }
TerrainTile* tile = new TerrainTile(responseJson); TerrainTile* terrainTile = new TerrainTile(responseJson);
if (tile->isValid()) { if (terrainTile->isValid()) {
_tilesMutex.lock(); _tilesMutex.lock();
if (!_tiles.contains(_uniqueTileId(tile->centerCoordinate()))) { if (!_tiles.contains(_getTileHash(terrainTile->centerCoordinate()))) {
_tiles.insert(_uniqueTileId(tile->centerCoordinate()), *tile); _tiles.insert(_getTileHash(terrainTile->centerCoordinate()), *terrainTile);
} else { } else {
delete tile; delete terrainTile;
} }
_tilesMutex.unlock(); if (_downloadQueue.contains(_getTileHash(terrainTile->centerCoordinate()))) {
} _downloadQueue.removeOne(_getTileHash(terrainTile->centerCoordinate()));
_downloadTiles();
}
void ElevationProvider::_downloadTiles(void)
{
if (_state == State::Idle && _downloadQueue.count() > 0) {
QUrlQuery query;
_tilesMutex.lock();
qCDebug(TerrainLog) << "Starting download for " << _downloadQueue.first();
query.addQueryItem(QStringLiteral("points"), _downloadQueue.first().replace("_", ","));
_downloadQueue.pop_front();
_tilesMutex.unlock();
QUrl url(QStringLiteral("https://api.airmap.com/elevation/stage/srtm1/ele/carpet"));
url.setQuery(query);
qWarning() << "url" << url;
QNetworkRequest request(url);
QNetworkProxy tProxy;
tProxy.setType(QNetworkProxy::DefaultProxy);
_networkManager.setProxy(tProxy);
QNetworkReply* networkReply = _networkManager.get(request);
if (!networkReply) {
return;
} }
_tilesMutex.unlock();
connect(networkReply, &QNetworkReply::finished, this, &ElevationProvider::_requestFinishedTile);
_state = State::Downloading;
} else if (_state == State::Idle && _coordinates.length() > 0) {
queryTerrainData(_coordinates);
} }
reply->deleteLater();
} }
QString ElevationProvider::_uniqueTileId(const QGeoCoordinate& coordinate) QString ElevationProvider::_getTileHash(const QGeoCoordinate& coordinate)
{ {
// Compute corners of the tile QString ret = QGCMapEngine::getTileHash(UrlFactory::AirmapElevation, QGCMapEngine::long2elevationTileX(coordinate.longitude(), 1), QGCMapEngine::lat2elevationTileY(coordinate.latitude(), 1), 1);
QGeoCoordinate southWest; qCDebug(TerrainLog) << "Computing unique tile hash for " << coordinate << ret;
southWest.setLatitude(floor(coordinate.latitude()/QGCMapEngine::srtm1TileSize)*QGCMapEngine::srtm1TileSize);
southWest.setLongitude(floor(coordinate.longitude()/QGCMapEngine::srtm1TileSize)*QGCMapEngine::srtm1TileSize);
QGeoCoordinate northEast;
northEast.setLatitude(floor(coordinate.latitude()/QGCMapEngine::srtm1TileSize + 1)*QGCMapEngine::srtm1TileSize);
northEast.setLongitude(floor(coordinate.longitude()/QGCMapEngine::srtm1TileSize + 1)*QGCMapEngine::srtm1TileSize);
// Generate uniquely identifying string
QString ret = QString::number(southWest.latitude(), 'f', 6) + "_" + QString::number(southWest.longitude(), 'f', 6) + "_" +
QString::number(northEast.latitude(), 'f', 6) + "_" + QString::number(northEast.longitude(), 'f', 6);
qCDebug(TerrainLog) << "Computing unique tile id for " << coordinate << ret;
return ret; return ret;
} }
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#pragma once #pragma once
#include "TerrainTile.h" #include "TerrainTile.h"
#include "QGCMapEngineData.h"
#include "QGCLoggingCategory.h" #include "QGCLoggingCategory.h"
#include <QObject> #include <QObject>
...@@ -17,6 +18,7 @@ ...@@ -17,6 +18,7 @@
#include <QNetworkAccessManager> #include <QNetworkAccessManager>
#include <QHash> #include <QHash>
#include <QMutex> #include <QMutex>
#include <QtLocation/private/qgeotiledmapreply_p.h>
/* usage example: /* usage example:
ElevationProvider *p = new ElevationProvider(); ElevationProvider *p = new ElevationProvider();
...@@ -52,27 +54,17 @@ public: ...@@ -52,27 +54,17 @@ public:
*/ */
bool queryTerrainData(const QList<QGeoCoordinate>& coordinates); bool queryTerrainData(const QList<QGeoCoordinate>& coordinates);
/**
* Cache all data in rectangular region given by south west and north east corner.
*
* @param southWest
* @param northEast
* @return true on successful scheduling for download
*/
bool cacheTerrainTiles(const QGeoCoordinate& southWest, const QGeoCoordinate& northEast);
signals: signals:
/// signal returning requested elevation data /// signal returning requested elevation data
void terrainData(bool success, QList<float> altitudes); void terrainData(bool success, QList<float> altitudes);
private slots: private slots:
void _requestFinished(); /// slot to handle download of elevation of list of coordinates void _requestFinished(); /// slot to handle download of elevation of list of coordinates
void _requestFinishedTile(); /// slot to handle download of elevation tiles void _fetchedTile(); /// slot to handle fetched elevation tiles
private: private:
QString _uniqueTileId(const QGeoCoordinate& coordinate); /// Method to create a unique string for each tile. Format: south_west_north_east as floats. QString _getTileHash(const QGeoCoordinate& coordinate); /// Method to create a unique string for each tile. Format: south_west_north_east as floats.
void _downloadTiles(void); /// Method to trigger download of queued tiles, eventually emitting the requested altitudes (if any).
enum class State { enum class State {
Idle, Idle,
......
...@@ -85,10 +85,10 @@ TerrainTile::TerrainTile(QJsonDocument document) ...@@ -85,10 +85,10 @@ TerrainTile::TerrainTile(QJsonDocument document)
qCDebug(TerrainTileLog) << "Incomplete bounding location"; qCDebug(TerrainTileLog) << "Incomplete bounding location";
return; return;
} }
_southWest.setLatitude(swArray[0].toDouble()); _southWest.setLatitude(swArray[1].toDouble());
_southWest.setLongitude(swArray[1].toDouble()); _southWest.setLongitude(swArray[0].toDouble());
_northEast.setLatitude(neArray[0].toDouble()); _northEast.setLatitude(neArray[1].toDouble());
_northEast.setLongitude(neArray[1].toDouble()); _northEast.setLongitude(neArray[0].toDouble());
// Stats // Stats
const QJsonObject& statsObject = dataObject[_jsonStatsKey].toObject(); const QJsonObject& statsObject = dataObject[_jsonStatsKey].toObject();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment