From 5fb4a54fad3e2001c7ce1478b4080fa4ee9f1d73 Mon Sep 17 00:00:00 2001 From: Andreas Bircher Date: Thu, 23 Nov 2017 21:17:11 -0500 Subject: [PATCH] load tiles from cache if available --- src/QtLocationPlugin/QGeoMapReplyQGC.cpp | 3 + src/Terrain.cc | 155 +++++++---------------- src/Terrain.h | 16 +-- src/TerrainTile.cc | 8 +- 4 files changed, 60 insertions(+), 122 deletions(-) diff --git a/src/QtLocationPlugin/QGeoMapReplyQGC.cpp b/src/QtLocationPlugin/QGeoMapReplyQGC.cpp index 2c88231c9..ff47fc082 100644 --- a/src/QtLocationPlugin/QGeoMapReplyQGC.cpp +++ b/src/QtLocationPlugin/QGeoMapReplyQGC.cpp @@ -112,9 +112,11 @@ QGeoTiledMapReplyQGC::networkReplyFinished() { _timer.stop(); if (!_reply) { + abort(); return; } if (_reply->error() != QNetworkReply::NoError) { + abort(); return; } QByteArray a = _reply->readAll(); @@ -195,4 +197,5 @@ QGeoTiledMapReplyQGC::timeout() if(_reply) { _reply->abort(); } + abort(); } diff --git a/src/Terrain.cc b/src/Terrain.cc index 988842f08..64d878e4e 100644 --- a/src/Terrain.cc +++ b/src/Terrain.cc @@ -9,6 +9,8 @@ #include "Terrain.h" #include "QGCMapEngine.h" +#include "QGeoMapReplyQGC.h" +#include #include #include @@ -75,16 +77,31 @@ bool ElevationProvider::queryTerrainData(const QList& coordinate QList altitudes; bool needToDownload = false; foreach (QGeoCoordinate coordinate, coordinates) { - QString uniqueTileId = _uniqueTileId(coordinate); + QString tileHash = _getTileHash(coordinate); _tilesMutex.lock(); - if (!_tiles.contains(uniqueTileId)) { - qCDebug(TerrainLog) << "Need to download tile " << uniqueTileId; - _downloadQueue.append(uniqueTileId); + if (!_tiles.contains(tileHash)) { + qCDebug(TerrainLog) << "Need to download tile " << tileHash; + + 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; } else { if (!needToDownload) { - if (_tiles[uniqueTileId].isIn(coordinate)) { - altitudes.push_back(_tiles[uniqueTileId].elevation(coordinate)); + if (_tiles[tileHash].isIn(coordinate)) { + altitudes.push_back(_tiles[tileHash].elevation(coordinate)); } else { qCDebug(TerrainLog) << "Error: coordinate not in tile region"; altitudes.push_back(-1.0); @@ -103,39 +120,7 @@ bool ElevationProvider::queryTerrainData(const QList& coordinate _coordinates = coordinates; - _downloadTiles(); - 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; + return false; } void ElevationProvider::_requestFinished() @@ -176,93 +161,51 @@ void ElevationProvider::_requestFinished() emit terrainData(false, altitudes); } -void ElevationProvider::_requestFinishedTile() +void ElevationProvider::_fetchedTile() { - QNetworkReply* reply = qobject_cast(QObject::sender()); - _state = State::Idle; + QGeoTiledMapReplyQGC* reply = qobject_cast(QObject::sender()); - // When an error occurs we still end up here - if (reply->error() != QNetworkReply::NoError) { - QByteArray responseBytes = reply->readAll(); - - QJsonParseError parseError; - QJsonDocument responseJson = QJsonDocument::fromJson(responseBytes, &parseError); - qCDebug(TerrainLog) << "ERROR: Received " << responseJson; - // TODO (birchera): Handle error in downloading data - _downloadTiles(); + if (!reply || !reply->isFinished()) { + if (reply) { + qCDebug(TerrainLog) << "Error in fetching elevation tile: " << reply->errorString(); + reply->deleteLater(); + } else { + qCDebug(TerrainLog) << "Elevation tile fetched but invalid reply data type."; + } return; } - QByteArray responseBytes = reply->readAll(); + QByteArray responseBytes = reply->mapImageData(); QJsonParseError parseError; QJsonDocument responseJson = QJsonDocument::fromJson(responseBytes, &parseError); if (parseError.error != QJsonParseError::NoError) { - // TODO (birchera): Handle error in downloading data - _downloadTiles(); + qCDebug(TerrainLog) << "Could not parse terrain tile " << parseError.errorString(); + qCDebug(TerrainLog) << responseBytes; + reply->deleteLater(); return; } - TerrainTile* tile = new TerrainTile(responseJson); - if (tile->isValid()) { + TerrainTile* terrainTile = new TerrainTile(responseJson); + if (terrainTile->isValid()) { _tilesMutex.lock(); - if (!_tiles.contains(_uniqueTileId(tile->centerCoordinate()))) { - _tiles.insert(_uniqueTileId(tile->centerCoordinate()), *tile); + if (!_tiles.contains(_getTileHash(terrainTile->centerCoordinate()))) { + _tiles.insert(_getTileHash(terrainTile->centerCoordinate()), *terrainTile); } else { - delete tile; + delete terrainTile; } - _tilesMutex.unlock(); - } - - _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; + if (_downloadQueue.contains(_getTileHash(terrainTile->centerCoordinate()))) { + _downloadQueue.removeOne(_getTileHash(terrainTile->centerCoordinate())); } - - connect(networkReply, &QNetworkReply::finished, this, &ElevationProvider::_requestFinishedTile); - - _state = State::Downloading; - } else if (_state == State::Idle && _coordinates.length() > 0) { - queryTerrainData(_coordinates); + _tilesMutex.unlock(); } + reply->deleteLater(); } -QString ElevationProvider::_uniqueTileId(const QGeoCoordinate& coordinate) +QString ElevationProvider::_getTileHash(const QGeoCoordinate& coordinate) { - // Compute corners of the tile - QGeoCoordinate southWest; - 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; + QString ret = QGCMapEngine::getTileHash(UrlFactory::AirmapElevation, QGCMapEngine::long2elevationTileX(coordinate.longitude(), 1), QGCMapEngine::lat2elevationTileY(coordinate.latitude(), 1), 1); + qCDebug(TerrainLog) << "Computing unique tile hash for " << coordinate << ret; return ret; } diff --git a/src/Terrain.h b/src/Terrain.h index 286b2a077..742d0d3b3 100644 --- a/src/Terrain.h +++ b/src/Terrain.h @@ -10,6 +10,7 @@ #pragma once #include "TerrainTile.h" +#include "QGCMapEngineData.h" #include "QGCLoggingCategory.h" #include @@ -17,6 +18,7 @@ #include #include #include +#include /* usage example: ElevationProvider *p = new ElevationProvider(); @@ -52,27 +54,17 @@ public: */ bool queryTerrainData(const QList& 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: /// signal returning requested elevation data void terrainData(bool success, QList altitudes); private slots: 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: - QString _uniqueTileId(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). + QString _getTileHash(const QGeoCoordinate& coordinate); /// Method to create a unique string for each tile. Format: south_west_north_east as floats. enum class State { Idle, diff --git a/src/TerrainTile.cc b/src/TerrainTile.cc index 38a1d277e..d52ccd236 100644 --- a/src/TerrainTile.cc +++ b/src/TerrainTile.cc @@ -85,10 +85,10 @@ TerrainTile::TerrainTile(QJsonDocument document) qCDebug(TerrainTileLog) << "Incomplete bounding location"; return; } - _southWest.setLatitude(swArray[0].toDouble()); - _southWest.setLongitude(swArray[1].toDouble()); - _northEast.setLatitude(neArray[0].toDouble()); - _northEast.setLongitude(neArray[1].toDouble()); + _southWest.setLatitude(swArray[1].toDouble()); + _southWest.setLongitude(swArray[0].toDouble()); + _northEast.setLatitude(neArray[1].toDouble()); + _northEast.setLongitude(neArray[0].toDouble()); // Stats const QJsonObject& statsObject = dataObject[_jsonStatsKey].toObject(); -- 2.22.0