From 8b7553b582b7fa64b2e3725b33725e2af42a77ea Mon Sep 17 00:00:00 2001 From: Andreas Bircher Date: Thu, 16 Nov 2017 10:55:34 -0500 Subject: [PATCH] download tiles when downloading offline map --- .../QMLControl/QGCMapEngineManager.cc | 2 + .../QMLControl/QGCMapEngineManager.h | 3 + src/Terrain.cc | 60 +++++++++++++++---- src/Terrain.h | 11 +++- src/TerrainTile.cc | 30 +++++----- src/TerrainTile.h | 4 +- 6 files changed, 80 insertions(+), 30 deletions(-) diff --git a/src/QtLocationPlugin/QMLControl/QGCMapEngineManager.cc b/src/QtLocationPlugin/QMLControl/QGCMapEngineManager.cc index 52037ae32b..fce1e12288 100644 --- a/src/QtLocationPlugin/QMLControl/QGCMapEngineManager.cc +++ b/src/QtLocationPlugin/QMLControl/QGCMapEngineManager.cc @@ -157,6 +157,8 @@ QGCMapEngineManager::startDownload(const QString& name, const QString& mapType) } else { qWarning() << "QGCMapEngineManager::startDownload() No Tiles to save"; } + // TODO: this could also get some feedback + _elevationProvider.cacheTerrainTiles(QGeoCoordinate(_bottomRightLat, _topleftLon), QGeoCoordinate(_topleftLat, _bottomRightLon)); } //----------------------------------------------------------------------------- diff --git a/src/QtLocationPlugin/QMLControl/QGCMapEngineManager.h b/src/QtLocationPlugin/QMLControl/QGCMapEngineManager.h index 0d1b4199f5..73c9b733e4 100644 --- a/src/QtLocationPlugin/QMLControl/QGCMapEngineManager.h +++ b/src/QtLocationPlugin/QMLControl/QGCMapEngineManager.h @@ -19,6 +19,7 @@ #include "QGCLoggingCategory.h" #include "QGCMapEngine.h" #include "QGCMapTileSet.h" +#include "Terrain.h" Q_DECLARE_LOGGING_CATEGORY(QGCMapEngineManagerLog) @@ -152,6 +153,8 @@ private: int _actionProgress; ImportAction _importAction; bool _importReplace; + + ElevationProvider _elevationProvider; }; #endif diff --git a/src/Terrain.cc b/src/Terrain.cc index 88fe899548..875a5d6cee 100644 --- a/src/Terrain.cc +++ b/src/Terrain.cc @@ -95,6 +95,37 @@ bool ElevationProvider::queryTerrainData(const QList& coordinate 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()/TerrainTile::srtm1TileSize)*TerrainTile::srtm1TileSize); + southWestCorrected.setLongitude(floor(southWest.longitude()/TerrainTile::srtm1TileSize)*TerrainTile::srtm1TileSize); + QGeoCoordinate northEastCorrected; + northEastCorrected.setLatitude(ceil(northEast.latitude()/TerrainTile::srtm1TileSize)*TerrainTile::srtm1TileSize); + northEastCorrected.setLongitude(ceil(northEast.longitude()/TerrainTile::srtm1TileSize)*TerrainTile::srtm1TileSize); + + // Add all tiles to download queue + for (double lat = southWestCorrected.latitude() + TerrainTile::srtm1TileSize / 2.0; lat < northEastCorrected.latitude(); lat += TerrainTile::srtm1TileSize) { + for (double lon = southWestCorrected.longitude() + TerrainTile::srtm1TileSize / 2.0; lon < northEastCorrected.longitude(); lon += TerrainTile::srtm1TileSize) { + QString uniqueTileId = _uniqueTileId(QGeoCoordinate(lat, lon)); + _tilesMutex.lock(); + if (_downloadQueue.contains(uniqueTileId) || _tiles.contains(uniqueTileId)) { + _tilesMutex.unlock(); + continue; + } + _downloadQueue.append(uniqueTileId.replace("_", ",")); + _tilesMutex.unlock(); + qCDebug(TerrainLog) << "Adding tile to download queue: " << uniqueTileId; + } + } + + _downloadTiles(); + return true; +} + bool ElevationProvider::cacheTerrainTiles(const QList& coordinates) { if (coordinates.length() == 0) { @@ -105,9 +136,10 @@ bool ElevationProvider::cacheTerrainTiles(const QList& coordinat QString uniqueTileId = _uniqueTileId(coordinate); _tilesMutex.lock(); if (_downloadQueue.contains(uniqueTileId) || _tiles.contains(uniqueTileId)) { + _tilesMutex.unlock(); continue; } - _downloadQueue.append(uniqueTileId.replace("-", ",")); + _downloadQueue.append(uniqueTileId.replace("_", ",")); _tilesMutex.unlock(); qCDebug(TerrainLog) << "Adding tile to download queue: " << uniqueTileId; } @@ -166,7 +198,8 @@ void ElevationProvider::_requestFinishedTile() QJsonParseError parseError; QJsonDocument responseJson = QJsonDocument::fromJson(responseBytes, &parseError); qCDebug(TerrainLog) << "ERROR: Received " << responseJson; - // TODO: Handle error in downloading data + // TODO (birchera): Handle error in downloading data + _downloadTiles(); return; } @@ -175,7 +208,8 @@ void ElevationProvider::_requestFinishedTile() QJsonParseError parseError; QJsonDocument responseJson = QJsonDocument::fromJson(responseBytes, &parseError); if (parseError.error != QJsonParseError::NoError) { - // TODO: Handle error in downloading data + // TODO (birchera): Handle error in downloading data + _downloadTiles(); return; } @@ -187,7 +221,7 @@ void ElevationProvider::_requestFinishedTile() } else { delete tile; } - _tilesMutex.lock(); + _tilesMutex.unlock(); } _downloadTiles(); @@ -202,7 +236,7 @@ void ElevationProvider::_downloadTiles(void) query.addQueryItem(QStringLiteral("points"), _downloadQueue.first()); _downloadQueue.pop_front(); _tilesMutex.unlock(); - QUrl url(QStringLiteral("https://api.airmap.com/elevation/stage/srtm1/carpet")); + QUrl url(QStringLiteral("https://api.airmap.com/elevation/stage/srtm1/ele/carpet")); url.setQuery(query); QNetworkRequest request(url); @@ -224,15 +258,17 @@ void ElevationProvider::_downloadTiles(void) QString ElevationProvider::_uniqueTileId(const QGeoCoordinate& coordinate) { - QGeoCoordinate southEast; - southEast.setLatitude(floor(coordinate.latitude()*40.0)/40.0); - southEast.setLongitude(floor(coordinate.longitude()*40.0)/40.0); + // Compute corners of the tile + QGeoCoordinate southWest; + southWest.setLatitude(floor(coordinate.latitude()/TerrainTile::srtm1TileSize)*TerrainTile::srtm1TileSize); + southWest.setLongitude(floor(coordinate.longitude()/TerrainTile::srtm1TileSize)*TerrainTile::srtm1TileSize); QGeoCoordinate northEast; - northEast.setLatitude(ceil(coordinate.latitude()*40.0)/40.0); - northEast.setLongitude(ceil(coordinate.longitude()*40.0)/40.0); + northEast.setLatitude(ceil(coordinate.latitude()/TerrainTile::srtm1TileSize)*TerrainTile::srtm1TileSize); + northEast.setLongitude(ceil(coordinate.longitude()/TerrainTile::srtm1TileSize)*TerrainTile::srtm1TileSize); - QString ret = QString::number(southEast.latitude(), 'f', 6) + "-" + QString::number(southEast.longitude(), 'f', 6) + "-" + - QString::number(northEast.latitude(), 'f', 6) + "-" + QString::number(northEast.longitude(), 'f', 6); + // 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; diff --git a/src/Terrain.h b/src/Terrain.h index 5cfe3cea4f..14cb99f460 100644 --- a/src/Terrain.h +++ b/src/Terrain.h @@ -44,13 +44,22 @@ public: */ bool queryTerrainData(const QList& coordinates); + /** + * Cache all data in rectangular region given by list of coordinates. + * + * @param coordinates + * @return true on successful scheduling for download + */ + bool cacheTerrainTiles(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 QList& coordinates); + bool cacheTerrainTiles(const QGeoCoordinate& southWest, const QGeoCoordinate& northEast); signals: void terrainData(bool success, QList altitudes); diff --git a/src/TerrainTile.cc b/src/TerrainTile.cc index 3af537ef17..8baca5f444 100644 --- a/src/TerrainTile.cc +++ b/src/TerrainTile.cc @@ -7,7 +7,7 @@ QGC_LOGGING_CATEGORY(TerrainTileLog, "TerrainTileLog") -const double TerrainTile::_srtm1TileSize = 0.025; +const double TerrainTile::srtm1TileSize = 0.025; const char* TerrainTile::_jsonStatusKey = "status"; const char* TerrainTile::_jsonDataKey = "data"; const char* TerrainTile::_jsonBoundsKey = "bounds"; @@ -47,7 +47,7 @@ TerrainTile::TerrainTile(QJsonDocument document) QString errorString; QList rootVersionKeyInfoList = { { _jsonStatusKey, QJsonValue::String, true }, - { _jsonDataKey, QJsonValue::String, true }, + { _jsonDataKey, QJsonValue::Object, true }, }; if (!JsonHelper::validateKeys(rootObject, rootVersionKeyInfoList, errorString)) { qCDebug(TerrainTileLog) << "Error in reading json: " << errorString; @@ -91,7 +91,7 @@ TerrainTile::TerrainTile(QJsonDocument document) _northEast.setLongitude(neArray[1].toDouble()); // Stats - const QJsonObject& statsObject = dataObject[_jsonBoundsKey].toObject(); + const QJsonObject& statsObject = dataObject[_jsonStatsKey].toObject(); QList statsVersionKeyInfoList = { { _jsonMaxElevationKey, QJsonValue::Double, true }, { _jsonMinElevationKey, QJsonValue::Double, true }, @@ -107,17 +107,17 @@ TerrainTile::TerrainTile(QJsonDocument document) // Carpet const QJsonArray& carpetArray = dataObject[_jsonCarpetKey].toArray(); - if (carpetArray.count() != _gridSize) { - qCDebug(TerrainTileLog) << "Expected array of " << _gridSize << ", instead got " << carpetArray.count(); + if (carpetArray.count() < gridSize) { // TODO (birchera): We always get 91x91 points, figure out why and where the exact location of the elev values are. + qCDebug(TerrainTileLog) << "Expected array of " << gridSize << ", instead got " << carpetArray.count(); return; } - for (int i = 0; i < _gridSize; i++) { + for (int i = 0; i < gridSize; i++) { const QJsonArray& row = carpetArray[i].toArray(); - if (row.count() != _gridSize) { - qCDebug(TerrainTileLog) << "Expected row array of " << _gridSize << ", instead got " << row.count(); + if (row.count() < gridSize) { // TODO (birchera): the same as above + qCDebug(TerrainTileLog) << "Expected row array of " << gridSize << ", instead got " << row.count(); return; } - for (int j = 0; j < _gridSize; j++) { + for (int j = 0; j < gridSize; j++) { _data[i][j] = row[j].toDouble(); } } @@ -130,8 +130,8 @@ bool TerrainTile::isIn(const QGeoCoordinate& coordinate) const qCDebug(TerrainTileLog) << "isIn requested, but tile not valid"; return false; } - bool ret = coordinate.latitude() >= _southWest.longitude() && coordinate.longitude() >= _southWest.longitude() && - coordinate.latitude() <= _northEast.longitude() && coordinate.longitude() <= _northEast.longitude(); + bool ret = coordinate.latitude() >= _southWest.latitude() && coordinate.longitude() >= _southWest.longitude() && + coordinate.latitude() <= _northEast.latitude() && coordinate.longitude() <= _northEast.longitude(); qCDebug(TerrainTileLog) << "Checking isIn: " << coordinate << " , in sw " << _southWest << " , ne " << _northEast << ": " << ret; return ret; } @@ -141,13 +141,13 @@ float TerrainTile::elevation(const QGeoCoordinate& coordinate) const if (_isValid) { qCDebug(TerrainTileLog) << "elevation: " << coordinate << " , in sw " << _southWest << " , ne " << _northEast; // Get the index at resolution of 1 arc second - int indexLat = std::round((coordinate.latitude() - _southWest.latitude()) * _gridSize / _srtm1TileSize); - int indexLon = std::round((coordinate.longitude() - _southWest.longitude()) * _gridSize / _srtm1TileSize); + int indexLat = std::round((coordinate.latitude() - _southWest.latitude()) * gridSize / srtm1TileSize); + int indexLon = std::round((coordinate.longitude() - _southWest.longitude()) * gridSize / srtm1TileSize); qCDebug(TerrainTileLog) << "indexLat:indexLon" << indexLat << indexLon; // TODO (birchera): Move this down to the next debug output, once this is all properly working. Q_ASSERT(indexLat >= 0); - Q_ASSERT(indexLat < _gridSize); + Q_ASSERT(indexLat < gridSize); Q_ASSERT(indexLon >= 0); - Q_ASSERT(indexLon < _gridSize); + Q_ASSERT(indexLon < gridSize); qCDebug(TerrainTileLog) << "elevation" << _data[indexLat][indexLon]; return _data[indexLat][indexLon]; } else { diff --git a/src/TerrainTile.h b/src/TerrainTile.h index 1c66cbefa2..199578b776 100644 --- a/src/TerrainTile.h +++ b/src/TerrainTile.h @@ -74,10 +74,10 @@ public: QGeoCoordinate centerCoordinate(void) const; /// tile grid size in lat and lon - static const int _gridSize = TERRAIN_TILE_SIZE; + static const int gridSize = TERRAIN_TILE_SIZE; /// size of a tile in degree - static const double _srtm1TileSize; + static const double srtm1TileSize; private: QGeoCoordinate _southWest; /// South west corner of the tile -- GitLab