From 12241e62589ce5ee343189db1697258d4a1ef67d Mon Sep 17 00:00:00 2001 From: DonLakeFlyer Date: Fri, 16 Mar 2018 12:34:57 -0700 Subject: [PATCH] Restructure terrain query to support path query as well --- qgroundcontrol.pro | 5 +- src/MissionManager/VisualMissionItem.cc | 8 +- src/Terrain.cc | 222 ---------------- src/Terrain.h | 83 ------ src/Terrain/TerrainQuery.cc | 331 ++++++++++++++++++++++++ src/Terrain/TerrainQuery.h | 142 ++++++++++ 6 files changed, 480 insertions(+), 311 deletions(-) delete mode 100644 src/Terrain.cc delete mode 100644 src/Terrain.h create mode 100644 src/Terrain/TerrainQuery.cc create mode 100644 src/Terrain/TerrainQuery.h diff --git a/qgroundcontrol.pro b/qgroundcontrol.pro index cb0226f85..c4d45172d 100644 --- a/qgroundcontrol.pro +++ b/qgroundcontrol.pro @@ -346,6 +346,7 @@ INCLUDEPATH += \ src/QtLocationPlugin \ src/QtLocationPlugin/QMLControl \ src/Settings \ + src/Terrain \ src/VehicleSetup \ src/ViewWidgets \ src/Audio \ @@ -586,7 +587,7 @@ HEADERS += \ src/Settings/SettingsManager.h \ src/Settings/UnitsSettings.h \ src/Settings/VideoSettings.h \ - src/Terrain.h \ + src/Terrain/TerrainQuery.h \ src/Vehicle/MAVLinkLogManager.h \ src/VehicleSetup/JoystickConfigController.h \ src/comm/LinkConfiguration.h \ @@ -778,7 +779,7 @@ SOURCES += \ src/Settings/SettingsManager.cc \ src/Settings/UnitsSettings.cc \ src/Settings/VideoSettings.cc \ - src/Terrain.cc \ + src/Terrain/TerrainQuery.cc \ src/Vehicle/MAVLinkLogManager.cc \ src/VehicleSetup/JoystickConfigController.cc \ src/comm/LinkConfiguration.cc \ diff --git a/src/MissionManager/VisualMissionItem.cc b/src/MissionManager/VisualMissionItem.cc index ba309b72f..872b5f68d 100644 --- a/src/MissionManager/VisualMissionItem.cc +++ b/src/MissionManager/VisualMissionItem.cc @@ -15,7 +15,7 @@ #include "FirmwarePluginManager.h" #include "QGCApplication.h" #include "JsonHelper.h" -#include "Terrain.h" +#include "TerrainQuery.h" const char* VisualMissionItem::jsonTypeKey = "type"; const char* VisualMissionItem::jsonTypeSimpleItemValue = "SimpleItem"; @@ -172,11 +172,11 @@ void VisualMissionItem::_reallyUpdateTerrainAltitude(void) if (coord.isValid() && (qIsNaN(_terrainAltitude) || !qFuzzyCompare(_lastLatTerrainQuery, coord.latitude()) || qFuzzyCompare(_lastLonTerrainQuery, coord.longitude()))) { _lastLatTerrainQuery = coord.latitude(); _lastLonTerrainQuery = coord.longitude(); - ElevationProvider* terrain = new ElevationProvider(this); - connect(terrain, &ElevationProvider::terrainData, this, &VisualMissionItem::_terrainDataReceived); + TerrainAtCoordinateQuery* terrain = new TerrainAtCoordinateQuery(this); + connect(terrain, &TerrainAtCoordinateQuery::terrainData, this, &VisualMissionItem::_terrainDataReceived); QList rgCoord; rgCoord.append(coordinate()); - terrain->queryTerrainData(rgCoord); + terrain->requestData(rgCoord); } } diff --git a/src/Terrain.cc b/src/Terrain.cc deleted file mode 100644 index 3fb70ae7a..000000000 --- a/src/Terrain.cc +++ /dev/null @@ -1,222 +0,0 @@ -/**************************************************************************** - * - * (c) 2009-2016 QGROUNDCONTROL PROJECT - * - * QGroundControl is licensed according to the terms in the file - * COPYING.md in the root of the source code directory. - * - ****************************************************************************/ - -#include "Terrain.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -QGC_LOGGING_CATEGORY(ElevationProviderLog, "ElevationProviderLog") - -Q_GLOBAL_STATIC(TerrainBatchManager, _terrainBatchManager) - -TerrainBatchManager::TerrainBatchManager(void) -{ - _batchTimer.setSingleShot(true); - _batchTimer.setInterval(_batchTimeout); - connect(&_batchTimer, &QTimer::timeout, this, &TerrainBatchManager::_sendNextBatch); -} - -void TerrainBatchManager::addQuery(ElevationProvider* elevationProvider, const QList& coordinates) -{ - if (coordinates.length() > 0) { - qCDebug(ElevationProviderLog) << "addQuery: elevationProvider:coordinates.count" << elevationProvider << coordinates.count(); - connect(elevationProvider, &ElevationProvider::destroyed, this, &TerrainBatchManager::_elevationProviderDestroyed); - QueuedRequestInfo_t queuedRequestInfo = { elevationProvider, coordinates }; - _requestQueue.append(queuedRequestInfo); - if (!_batchTimer.isActive()) { - _batchTimer.start(); - } - } -} - -void TerrainBatchManager::_sendNextBatch(void) -{ - qCDebug(ElevationProviderLog) << "_sendNextBatch _state:_requestQueue.count:_sentRequests.count" << _stateToString(_state) << _requestQueue.count() << _sentRequests.count(); - - if (_state != State::Idle) { - // Waiting for last download the complete, wait some more - _batchTimer.start(); - return; - } - - if (_requestQueue.count() == 0) { - return; - } - - _sentRequests.clear(); - - // Convert coordinates to point strings for json query - QString points; - foreach (const QueuedRequestInfo_t& requestInfo, _requestQueue) { - SentRequestInfo_t sentRequestInfo = { requestInfo.elevationProvider, false, requestInfo.coordinates.count() }; - qCDebug(ElevationProviderLog) << "Building request: coordinate count" << requestInfo.coordinates.count(); - _sentRequests.append(sentRequestInfo); - - foreach (const QGeoCoordinate& coord, requestInfo.coordinates) { - points += QString::number(coord.latitude(), 'f', 10) + "," - + QString::number(coord.longitude(), 'f', 10) + ","; - } - - } - points = points.mid(0, points.length() - 1); // remove the last ',' from string - _requestQueue.clear(); - - QUrlQuery query; - query.addQueryItem(QStringLiteral("points"), points); - QUrl url(QStringLiteral("https://api.airmap.com/elevation/v1/ele")); - url.setQuery(query); - - QNetworkRequest request(url); - - QNetworkProxy tProxy; - tProxy.setType(QNetworkProxy::DefaultProxy); - _networkManager.setProxy(tProxy); - - QNetworkReply* networkReply = _networkManager.get(request); - if (!networkReply) { - _batchFailed(); - return; - } - - connect(networkReply, &QNetworkReply::finished, this, &TerrainBatchManager::_requestFinished); - - _state = State::Downloading; -} - -void TerrainBatchManager::_batchFailed(void) -{ - QList noAltitudes; - - foreach (const SentRequestInfo_t& sentRequestInfo, _sentRequests) { - if (!sentRequestInfo.providerDestroyed) { - disconnect(sentRequestInfo.elevationProvider, &ElevationProvider::destroyed, this, &TerrainBatchManager::_elevationProviderDestroyed); - sentRequestInfo.elevationProvider->_signalTerrainData(false, noAltitudes); - } - } - _sentRequests.clear(); -} - -void TerrainBatchManager::_requestFinished() -{ - QNetworkReply* reply = qobject_cast(QObject::sender()); - - _state = State::Idle; - - // When an error occurs we still end up here - if (reply->error() != QNetworkReply::NoError) { - qCDebug(ElevationProviderLog) << "_requestFinished error:" << reply->error(); - _batchFailed(); - reply->deleteLater(); - return; - } - - QByteArray responseBytes = reply->readAll(); - - QJsonParseError parseError; - QJsonDocument responseJson = QJsonDocument::fromJson(responseBytes, &parseError); - if (parseError.error != QJsonParseError::NoError) { - qCDebug(ElevationProviderLog) << "_requestFinished unable to parse json:" << parseError.errorString(); - _batchFailed(); - reply->deleteLater(); - return; - } - - QJsonObject rootObject = responseJson.object(); - QString status = rootObject["status"].toString(); - if (status != "success") { - qCDebug(ElevationProviderLog) << "_requestFinished status != success:" << status; - _batchFailed(); - reply->deleteLater(); - return; - } - - QList altitudes; - const QJsonArray& dataArray = rootObject["data"].toArray(); - for (int i = 0; i < dataArray.count(); i++) { - altitudes.push_back(dataArray[i].toDouble()); - } - - int currentIndex = 0; - foreach (const SentRequestInfo_t& sentRequestInfo, _sentRequests) { - if (!sentRequestInfo.providerDestroyed) { - disconnect(sentRequestInfo.elevationProvider, &ElevationProvider::destroyed, this, &TerrainBatchManager::_elevationProviderDestroyed); - QList requestAltitudes = altitudes.mid(currentIndex, sentRequestInfo.cCoord); - sentRequestInfo.elevationProvider->_signalTerrainData(true, requestAltitudes); - currentIndex += sentRequestInfo.cCoord; - } - } - _sentRequests.clear(); - - reply->deleteLater(); -} - -void TerrainBatchManager::_elevationProviderDestroyed(QObject* elevationProvider) -{ - // Remove/Mark deleted objects queries from queues - - qCDebug(ElevationProviderLog) << "_elevationProviderDestroyed elevationProvider" << elevationProvider; - - int i = 0; - while (i < _requestQueue.count()) { - const QueuedRequestInfo_t& requestInfo = _requestQueue[i]; - if (requestInfo.elevationProvider == elevationProvider) { - qCDebug(ElevationProviderLog) << "Removing deleted provider from _requestQueue index:elevationProvider" << i << requestInfo.elevationProvider; - _requestQueue.removeAt(i); - } else { - i++; - } - } - - for (int i=0; i<_sentRequests.count(); i++) { - SentRequestInfo_t& sentRequestInfo = _sentRequests[i]; - if (sentRequestInfo.elevationProvider == elevationProvider) { - qCDebug(ElevationProviderLog) << "Zombieing deleted provider from _sentRequests index:elevatationProvider" << sentRequestInfo.elevationProvider; - sentRequestInfo.providerDestroyed = true; - } - } -} - -QString TerrainBatchManager::_stateToString(State state) -{ - switch (state) { - case State::Idle: - return QStringLiteral("Idle"); - case State::Downloading: - return QStringLiteral("Downloading"); - } - - return QStringLiteral("State unknown"); -} - -ElevationProvider::ElevationProvider(QObject* parent) - : QObject(parent) -{ - -} -void ElevationProvider::queryTerrainData(const QList& coordinates) -{ - if (coordinates.length() == 0) { - return; - } - - _terrainBatchManager->addQuery(this, coordinates); -} - -void ElevationProvider::_signalTerrainData(bool success, QList& altitudes) -{ - emit terrainData(success, altitudes); -} diff --git a/src/Terrain.h b/src/Terrain.h deleted file mode 100644 index 92f3f330a..000000000 --- a/src/Terrain.h +++ /dev/null @@ -1,83 +0,0 @@ -/**************************************************************************** - * - * (c) 2017 QGROUNDCONTROL PROJECT - * - * QGroundControl is licensed according to the terms in the file - * COPYING.md in the root of the source code directory. - * - ****************************************************************************/ - -#pragma once - -#include "QGCLoggingCategory.h" - -#include -#include -#include -#include - -Q_DECLARE_LOGGING_CATEGORY(ElevationProviderLog) - -class ElevationProvider; - -/// Used internally by ElevationProvider to batch requests together -class TerrainBatchManager : public QObject { - Q_OBJECT - -public: - TerrainBatchManager(void); - - void addQuery(ElevationProvider* elevationProvider, const QList& coordinates); - -private slots: - void _sendNextBatch (void); - void _requestFinished (void); - void _elevationProviderDestroyed (QObject* elevationProvider); - -private: - typedef struct { - ElevationProvider* elevationProvider; - QList coordinates; - } QueuedRequestInfo_t; - - typedef struct { - ElevationProvider* elevationProvider; - bool providerDestroyed; - int cCoord; - } SentRequestInfo_t; - - - enum class State { - Idle, - Downloading, - }; - - void _batchFailed(void); - QString _stateToString(State state); - - QList _requestQueue; - QList _sentRequests; - State _state = State::Idle; - QNetworkAccessManager _networkManager; - const int _batchTimeout = 500; - QTimer _batchTimer; -}; - -/// NOTE: ElevationProvider is not thread safe. All instances/calls to ElevationProvider must be on main thread. -class ElevationProvider : public QObject -{ - Q_OBJECT -public: - ElevationProvider(QObject* parent = NULL); - - /// Async elevation query for a list of lon,lat coordinates. When the query is done, the terrainData() signal - /// is emitted. - /// @param coordinates to query - void queryTerrainData(const QList& coordinates); - - // Internal method - void _signalTerrainData(bool success, QList& altitudes); - -signals: - void terrainData(bool success, QList altitudes); -}; diff --git a/src/Terrain/TerrainQuery.cc b/src/Terrain/TerrainQuery.cc new file mode 100644 index 000000000..1519295a0 --- /dev/null +++ b/src/Terrain/TerrainQuery.cc @@ -0,0 +1,331 @@ +/**************************************************************************** + * + * (c) 2009-2016 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + ****************************************************************************/ + +#include "TerrainQuery.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +QGC_LOGGING_CATEGORY(TerrainQueryLog, "TerrainQueryLog") + +Q_GLOBAL_STATIC(TerrainAtCoordinateBatchManager, _TerrainAtCoordinateBatchManager) + +TerrainQuery::TerrainQuery(QObject* parent) + : QObject(parent) +{ + +} + +void TerrainQuery::_sendQuery(const QString& path, const QUrlQuery& urlQuery) +{ + QUrl url(QStringLiteral("https://api.airmap.com/elevation/v1/ele") + path); + url.setQuery(urlQuery); + + QNetworkRequest request(url); + + QNetworkProxy tProxy; + tProxy.setType(QNetworkProxy::DefaultProxy); + _networkManager.setProxy(tProxy); + + QNetworkReply* networkReply = _networkManager.get(request); + if (!networkReply) { + _getNetworkReplyFailed(); + return; + } + + connect(networkReply, &QNetworkReply::finished, this, &TerrainQuery::_requestFinished); +} + +void TerrainQuery::_requestFinished(void) +{ + QNetworkReply* reply = qobject_cast(QObject::sender()); + + if (reply->error() != QNetworkReply::NoError) { + qCDebug(TerrainQueryLog) << "_requestFinished error:" << reply->error(); + _requestFailed(reply->error()); + reply->deleteLater(); + return; + } + + QByteArray responseBytes = reply->readAll(); + reply->deleteLater(); + + // Convert the response to Json + QJsonParseError parseError; + QJsonDocument responseJson = QJsonDocument::fromJson(responseBytes, &parseError); + if (parseError.error != QJsonParseError::NoError) { + qCDebug(TerrainQueryLog) << "_requestFinished unable to parse json:" << parseError.errorString(); + _requestJsonParseFailed(parseError.errorString()); + return; + } + + // Check airmap reponse status + QJsonObject rootObject = responseJson.object(); + QString status = rootObject["status"].toString(); + if (status != "success") { + qCDebug(TerrainQueryLog) << "_requestFinished status != success:" << status; + _requestAirmapStatusFailed(status); + return; + } + + // Send back data + _requestSucess(rootObject["data"]); +} + +TerrainAtCoordinateBatchManager::TerrainAtCoordinateBatchManager(void) +{ + _batchTimer.setSingleShot(true); + _batchTimer.setInterval(_batchTimeout); + connect(&_batchTimer, &QTimer::timeout, this, &TerrainAtCoordinateBatchManager::_sendNextBatch); +} + +void TerrainAtCoordinateBatchManager::addQuery(TerrainAtCoordinateQuery* terrainAtCoordinateQuery, const QList& coordinates) +{ + if (coordinates.length() > 0) { + qCDebug(TerrainQueryLog) << "addQuery: TerrainAtCoordinateQuery:coordinates.count" << terrainAtCoordinateQuery << coordinates.count(); + connect(terrainAtCoordinateQuery, &TerrainAtCoordinateQuery::destroyed, this, &TerrainAtCoordinateBatchManager::_queryObjectDestroyed); + QueuedRequestInfo_t queuedRequestInfo = { terrainAtCoordinateQuery, coordinates }; + _requestQueue.append(queuedRequestInfo); + if (!_batchTimer.isActive()) { + _batchTimer.start(); + } + } +} + +void TerrainAtCoordinateBatchManager::_sendNextBatch(void) +{ + qCDebug(TerrainQueryLog) << "_sendNextBatch _state:_requestQueue.count:_sentRequests.count" << _stateToString(_state) << _requestQueue.count() << _sentRequests.count(); + + if (_state != State::Idle) { + // Waiting for last download the complete, wait some more + _batchTimer.start(); + return; + } + + if (_requestQueue.count() == 0) { + return; + } + + _sentRequests.clear(); + + // Convert coordinates to point strings for json query + QString points; + foreach (const QueuedRequestInfo_t& requestInfo, _requestQueue) { + SentRequestInfo_t sentRequestInfo = { requestInfo.terrainAtCoordinateQuery, false, requestInfo.coordinates.count() }; + qCDebug(TerrainQueryLog) << "Building request: coordinate count" << requestInfo.coordinates.count(); + _sentRequests.append(sentRequestInfo); + + foreach (const QGeoCoordinate& coord, requestInfo.coordinates) { + points += QString::number(coord.latitude(), 'f', 10) + "," + + QString::number(coord.longitude(), 'f', 10) + ","; + } + + } + points = points.mid(0, points.length() - 1); // remove the last ',' from string + _requestQueue.clear(); + + QUrlQuery query; + query.addQueryItem(QStringLiteral("points"), points); + _sendQuery(QString() /* path */, query); + _state = State::Downloading; +} + +void TerrainAtCoordinateBatchManager::_batchFailed(void) +{ + QList noAltitudes; + + foreach (const SentRequestInfo_t& sentRequestInfo, _sentRequests) { + if (!sentRequestInfo.queryObjectDestroyed) { + disconnect(sentRequestInfo.terrainAtCoordinateQuery, &TerrainAtCoordinateQuery::destroyed, this, &TerrainAtCoordinateBatchManager::_queryObjectDestroyed); + sentRequestInfo.terrainAtCoordinateQuery->_signalTerrainData(false, noAltitudes); + } + } + _sentRequests.clear(); +} + +void TerrainAtCoordinateBatchManager::_queryObjectDestroyed(QObject* terrainAtCoordinateQuery) +{ + // Remove/Mark deleted objects queries from queues + + qCDebug(TerrainQueryLog) << "_TerrainAtCoordinateQueryDestroyed TerrainAtCoordinateQuery" << terrainAtCoordinateQuery; + + int i = 0; + while (i < _requestQueue.count()) { + const QueuedRequestInfo_t& requestInfo = _requestQueue[i]; + if (requestInfo.terrainAtCoordinateQuery == terrainAtCoordinateQuery) { + qCDebug(TerrainQueryLog) << "Removing deleted provider from _requestQueue index:terrainAtCoordinateQuery" << i << requestInfo.terrainAtCoordinateQuery; + _requestQueue.removeAt(i); + } else { + i++; + } + } + + for (int i=0; i<_sentRequests.count(); i++) { + SentRequestInfo_t& sentRequestInfo = _sentRequests[i]; + if (sentRequestInfo.terrainAtCoordinateQuery == terrainAtCoordinateQuery) { + qCDebug(TerrainQueryLog) << "Zombieing deleted provider from _sentRequests index:terrainAtCoordinateQuery" << sentRequestInfo.terrainAtCoordinateQuery; + sentRequestInfo.queryObjectDestroyed = true; + } + } +} + +QString TerrainAtCoordinateBatchManager::_stateToString(State state) +{ + switch (state) { + case State::Idle: + return QStringLiteral("Idle"); + case State::Downloading: + return QStringLiteral("Downloading"); + } + + return QStringLiteral("State unknown"); +} + +void TerrainAtCoordinateBatchManager::_getNetworkReplyFailed(void) +{ + _batchFailed(); +} + +void TerrainAtCoordinateBatchManager::_requestFailed(QNetworkReply::NetworkError error) +{ + Q_UNUSED(error); + _state = State::Idle; + _batchFailed(); +} + +void TerrainAtCoordinateBatchManager::_requestJsonParseFailed(const QString& errorString) +{ + Q_UNUSED(errorString); + _state = State::Idle; + _batchFailed(); +} + +void TerrainAtCoordinateBatchManager::_requestAirmapStatusFailed(const QString& status) +{ + Q_UNUSED(status); + _state = State::Idle; + _batchFailed(); +} + +void TerrainAtCoordinateBatchManager::_requestSucess(const QJsonValue& dataJsonValue) +{ + _state = State::Idle; + + QList altitudes; + const QJsonArray& dataArray = dataJsonValue.toArray(); + for (int i = 0; i < dataArray.count(); i++) { + altitudes.push_back(dataArray[i].toDouble()); + } + + int currentIndex = 0; + foreach (const SentRequestInfo_t& sentRequestInfo, _sentRequests) { + if (!sentRequestInfo.queryObjectDestroyed) { + disconnect(sentRequestInfo.terrainAtCoordinateQuery, &TerrainAtCoordinateQuery::destroyed, this, &TerrainAtCoordinateBatchManager::_queryObjectDestroyed); + QList requestAltitudes = altitudes.mid(currentIndex, sentRequestInfo.cCoord); + sentRequestInfo.terrainAtCoordinateQuery->_signalTerrainData(true, requestAltitudes); + currentIndex += sentRequestInfo.cCoord; + } + } + _sentRequests.clear(); +} + +TerrainAtCoordinateQuery::TerrainAtCoordinateQuery(QObject* parent) + : QObject(parent) +{ + +} +void TerrainAtCoordinateQuery::requestData(const QList& coordinates) +{ + if (coordinates.length() == 0) { + return; + } + + _TerrainAtCoordinateBatchManager->addQuery(this, coordinates); +} + +void TerrainAtCoordinateQuery::_signalTerrainData(bool success, QList& altitudes) +{ + emit terrainData(success, altitudes); +} + +TerrainPathQuery::TerrainPathQuery(QObject* parent) + : TerrainQuery(parent) +{ + +} + +void TerrainPathQuery::requestData(const QGeoCoordinate& fromCoord, const QGeoCoordinate& toCoord) +{ + if (!fromCoord.isValid() || !toCoord.isValid()) { + return; + } + + QString points; + points += QString::number(fromCoord.latitude(), 'f', 10) + "," + + QString::number(fromCoord.longitude(), 'f', 10) + ","; + points += QString::number(toCoord.latitude(), 'f', 10) + "," + + QString::number(toCoord.longitude(), 'f', 10); + + QUrlQuery query; + query.addQueryItem(QStringLiteral("points"), points); + + _sendQuery(QStringLiteral("/path"), query); +} + +void TerrainPathQuery::_getNetworkReplyFailed(void) +{ + QList altitudes; + emit terrainData(false, 0, 0, altitudes); +} + +void TerrainPathQuery::_requestFailed(QNetworkReply::NetworkError error) +{ + Q_UNUSED(error); + QList altitudes; + emit terrainData(false, 0, 0, altitudes); +} + +void TerrainPathQuery::_requestJsonParseFailed(const QString& errorString) +{ + Q_UNUSED(errorString); + QList altitudes; + emit terrainData(false, 0, 0, altitudes); +} + +void TerrainPathQuery::_requestAirmapStatusFailed(const QString& status) +{ + Q_UNUSED(status); + QList altitudes; + emit terrainData(false, 0, 0, altitudes); +} + +void TerrainPathQuery::_requestSucess(const QJsonValue& dataJsonValue) +{ + QJsonObject jsonObject = dataJsonValue.toArray()[0].toObject(); + QJsonArray stepArray = jsonObject["step"].toArray(); + QJsonArray profileArray = jsonObject["profile"].toArray(); + + QList rgProfile; + foreach (const QJsonValue& profileValue, profileArray) { + rgProfile.append(profileValue.toDouble()); + } + + emit terrainData(true, // success + stepArray[0].toDouble(), // lat step + stepArray[1].toDouble(), // lon step + rgProfile); +} + diff --git a/src/Terrain/TerrainQuery.h b/src/Terrain/TerrainQuery.h new file mode 100644 index 000000000..3c21ffa3c --- /dev/null +++ b/src/Terrain/TerrainQuery.h @@ -0,0 +1,142 @@ +/**************************************************************************** + * + * (c) 2017 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + ****************************************************************************/ + +#pragma once + +#include "QGCLoggingCategory.h" + +#include +#include +#include +#include +#include + +Q_DECLARE_LOGGING_CATEGORY(TerrainQueryLog) + +class TerrainAtCoordinateQuery; + +// Base class for all terrain query objects +class TerrainQuery : public QObject { + Q_OBJECT + +public: + TerrainQuery(QObject* parent = NULL); + +protected: + // Send a query to AirMap terrain servers. Data and errors are returned back from super class virtual implementations. + // @param path Additional path to add to url + // @param urlQuery Query to send + void _sendQuery(const QString& path, const QUrlQuery& urlQuery); + + virtual void _getNetworkReplyFailed (void) = 0; ///< QNetworkManager::get failed to return QNetworkReplay object + virtual void _requestFailed (QNetworkReply::NetworkError error) = 0; ///< QNetworkReply::finished returned error + virtual void _requestJsonParseFailed (const QString& errorString) = 0; ///< Parsing of returned json failed + virtual void _requestAirmapStatusFailed (const QString& status) = 0; ///< AirMap status was not "success" + virtual void _requestSucess (const QJsonValue& dataJsonValue) = 0; ///< Successful reqest, data returned + +private slots: + void _requestFinished(void); + +private: + QNetworkAccessManager _networkManager; +}; + +/// Used internally by TerrainAtCoordinateQuery to batch coordinate requests together +class TerrainAtCoordinateBatchManager : public TerrainQuery { + Q_OBJECT + +public: + TerrainAtCoordinateBatchManager(void); + + void addQuery(TerrainAtCoordinateQuery* terrainAtCoordinateQuery, const QList& coordinates); + +protected: + void _getNetworkReplyFailed (void) final; + void _requestFailed (QNetworkReply::NetworkError error) final; + void _requestJsonParseFailed (const QString& errorString) final; + void _requestAirmapStatusFailed (const QString& status) final; + void _requestSucess (const QJsonValue& dataJsonValue) final; + +private slots: + void _sendNextBatch (void); + void _queryObjectDestroyed (QObject* elevationProvider); + +private: + typedef struct { + TerrainAtCoordinateQuery* terrainAtCoordinateQuery; + QList coordinates; + } QueuedRequestInfo_t; + + typedef struct { + TerrainAtCoordinateQuery* terrainAtCoordinateQuery; + bool queryObjectDestroyed; + int cCoord; + } SentRequestInfo_t; + + + enum class State { + Idle, + Downloading, + }; + + void _batchFailed(void); + QString _stateToString(State state); + + QList _requestQueue; + QList _sentRequests; + State _state = State::Idle; + const int _batchTimeout = 500; + QTimer _batchTimer; +}; + +/// NOTE: TerrainAtCoordinateQuery is not thread safe. All instances/calls to ElevationProvider must be on main thread. +class TerrainAtCoordinateQuery : public QObject +{ + Q_OBJECT +public: + TerrainAtCoordinateQuery(QObject* parent = NULL); + + /// Async terrain query for a list of lon,lat coordinates. When the query is done, the terrainData() signal + /// is emitted. + /// @param coordinates to query + void requestData(const QList& coordinates); + + // Internal method + void _signalTerrainData(bool success, QList& altitudes); + +signals: + void terrainData(bool success, QList altitudes); +}; + +class TerrainPathQuery : public TerrainQuery +{ + Q_OBJECT + +public: + TerrainPathQuery(QObject* parent = NULL); + + /// Async terrain query for terrain heights between two lat/lon coordinates. When the query is done, the terrainData() signal + /// is emitted. + /// @param coordinates to query + void requestData(const QGeoCoordinate& fromCoord, const QGeoCoordinate& toCoord); + +protected: + void _getNetworkReplyFailed (void) final; + void _requestFailed (QNetworkReply::NetworkError error) final; + void _requestJsonParseFailed (const QString& errorString) final; + void _requestAirmapStatusFailed (const QString& status) final; + void _requestSucess (const QJsonValue& dataJsonValue) final; + +signals: + /// Signalled when terrain data comes back from server + /// @param latStep Amount of latitudinal distance between each returned height + /// @param lonStep Amount of longitudinal distance between each returned height + /// @param altitudes Altitudes along specified path + void terrainData(bool success, double latStep, double lonStep, QList altitudes); +}; -- 2.22.0