diff --git a/src/MissionManager/TransectStyleComplexItem.cc b/src/MissionManager/TransectStyleComplexItem.cc index abc2f8ab28bf433cfe543b1d2701b5292451b2cf..31f483c3ee77fdbbfd8f93680c082405f34bff97 100644 --- a/src/MissionManager/TransectStyleComplexItem.cc +++ b/src/MissionManager/TransectStyleComplexItem.cc @@ -374,7 +374,7 @@ void TransectStyleComplexItem::_adjustTransectPointsForTerrain(void) bool surveyEdgeIndicator = transectPoint.altitude() == _surveyEdgeIndicator; if (_followTerrain) { - transectPoint.setAltitude(_transectsPathHeightInfo[i].rgHeight[0] + altitude); + transectPoint.setAltitude(_transectsPathHeightInfo[i].heights[0] + altitude); } else { transectPoint.setAltitude(altitude); } @@ -390,7 +390,7 @@ void TransectStyleComplexItem::_adjustTransectPointsForTerrain(void) QGeoCoordinate transectPoint = _transectPoints.last().value(); bool surveyEdgeIndicator = transectPoint.altitude() == _surveyEdgeIndicator; if (_followTerrain){ - transectPoint.setAltitude(_transectsPathHeightInfo.last().rgHeight.last() + altitude); + transectPoint.setAltitude(_transectsPathHeightInfo.last().heights.last() + altitude); } else { transectPoint.setAltitude(altitude); } diff --git a/src/MissionManager/VisualMissionItem.cc b/src/MissionManager/VisualMissionItem.cc index 872b5f68d26869ce227c9740098f22029e5ed2bf..24da25b2713515dc1f689378e28a1a20e8c2d023 100644 --- a/src/MissionManager/VisualMissionItem.cc +++ b/src/MissionManager/VisualMissionItem.cc @@ -180,10 +180,10 @@ void VisualMissionItem::_reallyUpdateTerrainAltitude(void) } } -void VisualMissionItem::_terrainDataReceived(bool success, QList altitudes) +void VisualMissionItem::_terrainDataReceived(bool success, QList heights) { if (success) { - _terrainAltitude = altitudes[0]; + _terrainAltitude = heights[0]; emit terrainAltitudeChanged(_terrainAltitude); sender()->deleteLater(); } diff --git a/src/MissionManager/VisualMissionItem.h b/src/MissionManager/VisualMissionItem.h index eb1232cf7093015836a4a0479c77cdbcb6bf9875..9324c8cf6346859da4f1587ba9ba35db4d88f10d 100644 --- a/src/MissionManager/VisualMissionItem.h +++ b/src/MissionManager/VisualMissionItem.h @@ -211,7 +211,7 @@ protected: private slots: void _updateTerrainAltitude (void); void _reallyUpdateTerrainAltitude (void); - void _terrainDataReceived (bool success, QList altitudes); + void _terrainDataReceived (bool success, QList heights); private: QTimer _updateTerrainTimer; diff --git a/src/Terrain/TerrainQuery.cc b/src/Terrain/TerrainQuery.cc index 8ec04efa4c26fd2eff4ee66284b88dbdfa8c098f..a3fd40ef08892cd1365d9f7f7bf2c8e43ca1c287 100644 --- a/src/Terrain/TerrainQuery.cc +++ b/src/Terrain/TerrainQuery.cc @@ -23,16 +23,65 @@ QGC_LOGGING_CATEGORY(TerrainQueryLog, "TerrainQueryLog") Q_GLOBAL_STATIC(TerrainAtCoordinateBatchManager, _TerrainAtCoordinateBatchManager) -TerrainQuery::TerrainQuery(QObject* parent) - : QObject(parent) +TerrainAirMapQuery::TerrainAirMapQuery(QObject* parent) + : TerrainQueryInterface(parent) { } -void TerrainQuery::_sendQuery(const QString& path, const QUrlQuery& urlQuery) +void TerrainAirMapQuery::requestCoordinateHeights(const QList& coordinates) +{ + QString points; + foreach (const QGeoCoordinate& coord, 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 + + QUrlQuery query; + query.addQueryItem(QStringLiteral("points"), points); + + _queryMode = QueryModeCoordinates; + _sendQuery(QString() /* path */, query); +} + +void TerrainAirMapQuery::requestPathHeights(const QGeoCoordinate& fromCoord, const QGeoCoordinate& toCoord) +{ + 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); + + _queryMode = QueryModePath; + _sendQuery(QStringLiteral("/path"), query); +} + +void TerrainAirMapQuery::requestCarpetHeights(const QGeoCoordinate& swCoord, const QGeoCoordinate& neCoord, bool statsOnly) +{ + QString points; + points += QString::number(swCoord.latitude(), 'f', 10) + "," + + QString::number(swCoord.longitude(), 'f', 10) + ","; + points += QString::number(neCoord.latitude(), 'f', 10) + "," + + QString::number(neCoord.longitude(), 'f', 10); + + QUrlQuery query; + query.addQueryItem(QStringLiteral("points"), points); + + _queryMode = QueryModeCarpet; + _carpetStatsOnly = statsOnly; + + _sendQuery(QStringLiteral("/carpet"), query); +} + +void TerrainAirMapQuery::_sendQuery(const QString& path, const QUrlQuery& urlQuery) { QUrl url(QStringLiteral("https://api.airmap.com/elevation/v1/ele") + path); url.setQuery(urlQuery); + qCDebug(TerrainQueryLog) << "_sendQuery" << url; QNetworkRequest request(url); @@ -43,21 +92,21 @@ void TerrainQuery::_sendQuery(const QString& path, const QUrlQuery& urlQuery) QNetworkReply* networkReply = _networkManager.get(request); if (!networkReply) { qCDebug(TerrainQueryLog) << "QNetworkManager::Get did not return QNetworkReply"; - _terrainData(false /* success */ , QJsonValue()); + _requestFailed(); return; } - connect(networkReply, &QNetworkReply::finished, this, &TerrainQuery::_requestFinished); + connect(networkReply, &QNetworkReply::finished, this, &TerrainAirMapQuery::_requestFinished); } -void TerrainQuery::_requestFinished(void) +void TerrainAirMapQuery::_requestFinished(void) { QNetworkReply* reply = qobject_cast(QObject::sender()); if (reply->error() != QNetworkReply::NoError) { qCDebug(TerrainQueryLog) << "_requestFinished error:" << reply->error(); reply->deleteLater(); - _terrainData(false /* success */ , QJsonValue()); + _requestFailed(); return; } @@ -69,7 +118,7 @@ void TerrainQuery::_requestFinished(void) QJsonDocument responseJson = QJsonDocument::fromJson(responseBytes, &parseError); if (parseError.error != QJsonParseError::NoError) { qCDebug(TerrainQueryLog) << "_requestFinished unable to parse json:" << parseError.errorString(); - _terrainData(false /* success */ , QJsonValue()); + _requestFailed(); return; } @@ -78,12 +127,93 @@ void TerrainQuery::_requestFinished(void) QString status = rootObject["status"].toString(); if (status != "success") { qCDebug(TerrainQueryLog) << "_requestFinished status != success:" << status; - _terrainData(false /* success */ , QJsonValue()); + _requestFailed(); return; } // Send back data - _terrainData(true /* success */ , rootObject["data"]); + const QJsonValue& jsonData = rootObject["data"]; + qCDebug(TerrainQueryLog) << "_requestFinished" << jsonData; + switch (_queryMode) { + case QueryModeCoordinates: + emit _parseCoordinateData(jsonData); + break; + case QueryModePath: + emit _parsePathData(jsonData); + break; + case QueryModeCarpet: + emit _parseCarpetData(jsonData); + break; + } +} + +void TerrainAirMapQuery::_requestFailed(void) +{ + switch (_queryMode) { + case QueryModeCoordinates: + emit coordinateHeights(false /* success */, QList() /* heights */); + break; + case QueryModePath: + emit pathHeights(false /* success */, qQNaN() /* latStep */, qQNaN() /* lonStep */, QList() /* heights */); + break; + case QueryModeCarpet: + emit carpetHeights(false /* success */, qQNaN() /* minHeight */, qQNaN() /* maxHeight */, QList>() /* carpet */); + break; + } +} + +void TerrainAirMapQuery::_parseCoordinateData(const QJsonValue& coordinateJson) +{ + QList heights; + const QJsonArray& dataArray = coordinateJson.toArray(); + for (int i = 0; i < dataArray.count(); i++) { + heights.append(dataArray[i].toDouble()); + } + + emit coordinateHeights(true /* success */, heights); +} + +void TerrainAirMapQuery::_parsePathData(const QJsonValue& pathJson) +{ + QJsonObject jsonObject = pathJson.toArray()[0].toObject(); + QJsonArray stepArray = jsonObject["step"].toArray(); + QJsonArray profileArray = jsonObject["profile"].toArray(); + + double latStep = stepArray[0].toDouble(); + double lonStep = stepArray[1].toDouble(); + + QList heights; + foreach (const QJsonValue& profileValue, profileArray) { + heights.append(profileValue.toDouble()); + } + + emit pathHeights(true /* success */, latStep, lonStep, heights); +} + +void TerrainAirMapQuery::_parseCarpetData(const QJsonValue& carpetJson) +{ + QJsonObject jsonObject = carpetJson.toArray()[0].toObject(); + + QJsonObject statsObject = jsonObject["stats"].toObject(); + double minHeight = statsObject["min"].toDouble(); + double maxHeight = statsObject["min"].toDouble(); + + QList> carpet; + if (!_carpetStatsOnly) { + QJsonArray carpetArray = jsonObject["carpet"].toArray(); + + for (int i=0; i()); + + for (int j=0; j& coordinates) @@ -123,35 +254,28 @@ void TerrainAtCoordinateBatchManager::_sendNextBatch(void) _sentRequests.clear(); // Convert coordinates to point strings for json query - QString points; + QList coords; 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) + ","; - } - + coords += requestInfo.coordinates; } - 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); + _terrainQuery.requestCoordinateHeights(coords); _state = State::Downloading; } void TerrainAtCoordinateBatchManager::_batchFailed(void) { - QList noAltitudes; + QList noHeights; foreach (const SentRequestInfo_t& sentRequestInfo, _sentRequests) { if (!sentRequestInfo.queryObjectDestroyed) { disconnect(sentRequestInfo.terrainAtCoordinateQuery, &TerrainAtCoordinateQuery::destroyed, this, &TerrainAtCoordinateBatchManager::_queryObjectDestroyed); - sentRequestInfo.terrainAtCoordinateQuery->_signalTerrainData(false, noAltitudes); + sentRequestInfo.terrainAtCoordinateQuery->_signalTerrainData(false, noHeights); } } _sentRequests.clear(); @@ -195,7 +319,7 @@ QString TerrainAtCoordinateBatchManager::_stateToString(State state) return QStringLiteral("State unknown"); } -void TerrainAtCoordinateBatchManager::_terrainData(bool success, const QJsonValue& dataJsonValue) +void TerrainAtCoordinateBatchManager::_coordinateHeights(bool success, QList heights) { _state = State::Idle; @@ -204,17 +328,11 @@ void TerrainAtCoordinateBatchManager::_terrainData(bool success, const QJsonValu return; } - 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); + QList requestAltitudes = heights.mid(currentIndex, sentRequestInfo.cCoord); sentRequestInfo.terrainAtCoordinateQuery->_signalTerrainData(true, requestAltitudes); currentIndex += sentRequestInfo.cCoord; } @@ -236,54 +354,30 @@ void TerrainAtCoordinateQuery::requestData(const QList& coordina _TerrainAtCoordinateBatchManager->addQuery(this, coordinates); } -void TerrainAtCoordinateQuery::_signalTerrainData(bool success, QList& altitudes) +void TerrainAtCoordinateQuery::_signalTerrainData(bool success, QList& heights) { - emit terrainData(success, altitudes); + emit terrainData(success, heights); } TerrainPathQuery::TerrainPathQuery(QObject* parent) - : TerrainQuery(parent) + : QObject(parent) { qRegisterMetaType(); + connect(&_terrainQuery, &TerrainQueryInterface::pathHeights, this, &TerrainPathQuery::_pathHeights); } 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); + _terrainQuery.requestPathHeights(fromCoord, toCoord); } -void TerrainPathQuery::_terrainData(bool success, const QJsonValue& dataJsonValue) +void TerrainPathQuery::_pathHeights(bool success, double latStep, double lonStep, const QList& heights) { - if (!success) { - emit terrainData(false /* success */, PathHeightInfo_t()); - return; - } - - QJsonObject jsonObject = dataJsonValue.toArray()[0].toObject(); - QJsonArray stepArray = jsonObject["step"].toArray(); - QJsonArray profileArray = jsonObject["profile"].toArray(); - PathHeightInfo_t pathHeightInfo; - pathHeightInfo.latStep = stepArray[0].toDouble(); - pathHeightInfo.lonStep = stepArray[1].toDouble(); - foreach (const QJsonValue& profileValue, profileArray) { - pathHeightInfo.rgHeight.append(profileValue.toDouble()); - } - - emit terrainData(true /* success */, pathHeightInfo); + pathHeightInfo.latStep = latStep; + pathHeightInfo.lonStep = lonStep; + pathHeightInfo.heights = heights; + emit terrainData(success, pathHeightInfo); } TerrainPolyPathQuery::TerrainPolyPathQuery(QObject* parent) @@ -331,61 +425,12 @@ void TerrainPolyPathQuery::_terrainDataReceived(bool success, const TerrainPathQ } TerrainCarpetQuery::TerrainCarpetQuery(QObject* parent) - : TerrainQuery(parent) + : QObject(parent) { - + connect(&_terrainQuery, &TerrainQueryInterface::carpetHeights, this, &TerrainCarpetQuery::terrainData); } void TerrainCarpetQuery::requestData(const QGeoCoordinate& swCoord, const QGeoCoordinate& neCoord, bool statsOnly) { - if (!swCoord.isValid() || !neCoord.isValid()) { - return; - } - - _statsOnly = statsOnly; - - QString points; - points += QString::number(swCoord.latitude(), 'f', 10) + "," - + QString::number(swCoord.longitude(), 'f', 10) + ","; - points += QString::number(neCoord.latitude(), 'f', 10) + "," - + QString::number(neCoord.longitude(), 'f', 10); - - QUrlQuery query; - query.addQueryItem(QStringLiteral("points"), points); - - _sendQuery(QStringLiteral("/carpet"), query); -} - -void TerrainCarpetQuery::_terrainData(bool success, const QJsonValue& dataJsonValue) -{ - if (!success) { - emit terrainData(false /* success */, qQNaN() /* minHeight */ , qQNaN() /* maxHeight */, QList>() /* carpet */); - return; - } - - qDebug() << dataJsonValue; - - QJsonObject jsonObject = dataJsonValue.toArray()[0].toObject(); - - QJsonObject statsObject = jsonObject["stats"].toObject(); - double minHeight = statsObject["min"].toDouble(); - double maxHeight = statsObject["min"].toDouble(); - - QList> carpet; - if (!_statsOnly) { - QJsonArray carpetArray = jsonObject["carpet"].toArray(); - - for (int i=0; i()); - - for (int j=0; j& coordinates) = 0; + + /// Requests terrain heights along the path specified by the two coordinates. + /// Signals: pathHeights + /// @param coordinates to query + virtual void requestPathHeights(const QGeoCoordinate& fromCoord, const QGeoCoordinate& toCoord) = 0; + + /// Request terrain heights for the rectangular area specified. + /// Signals: carpetHeights when data is available + /// @param swCoord South-West bound of rectangular area to query + /// @param neCoord North-East bound of rectangular area to query + /// @param statsOnly true: Return only stats, no carpet data + virtual void requestCarpetHeights(const QGeoCoordinate& swCoord, const QGeoCoordinate& neCoord, bool statsOnly) = 0; + +signals: + void coordinateHeights(bool success, QList heights); + void pathHeights(bool success, double latStep, double lonStep, const QList& heights); + void carpetHeights(bool success, double minHeight, double maxHeight, const QList>& carpet); +}; -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); +/// AirMap online implementation of terrain queries +class TerrainAirMapQuery : public TerrainQueryInterface { + Q_OBJECT + +public: + TerrainAirMapQuery(QObject* parent = NULL); - // Called when the request to the server fails or succeeds - virtual void _terrainData(bool success, const QJsonValue& dataJsonValue) = 0; + // Overrides from TerrainQueryInterface + void requestCoordinateHeights(const QList& coordinates) final; + void requestPathHeights(const QGeoCoordinate& fromCoord, const QGeoCoordinate& toCoord) final; + void requestCarpetHeights(const QGeoCoordinate& swCoord, const QGeoCoordinate& neCoord, bool statsOnly) final; private slots: void _requestFinished(void); private: - QNetworkAccessManager _networkManager; + void _sendQuery (const QString& path, const QUrlQuery& urlQuery); + void _requestFailed (void); + void _parseCoordinateData (const QJsonValue& coordinateJson); + void _parsePathData (const QJsonValue& pathJson); + void _parseCarpetData (const QJsonValue& carpetJson); + + enum QueryMode { + QueryModeCoordinates, + QueryModePath, + QueryModeCarpet + }; + + QNetworkAccessManager _networkManager; + QueryMode _queryMode; + bool _carpetStatsOnly; }; /// Used internally by TerrainAtCoordinateQuery to batch coordinate requests together -class TerrainAtCoordinateBatchManager : public TerrainQuery { +class TerrainAtCoordinateBatchManager : public QObject { Q_OBJECT public: @@ -53,13 +93,10 @@ public: void addQuery(TerrainAtCoordinateQuery* terrainAtCoordinateQuery, const QList& coordinates); -protected: - // Overrides from TerrainQuery - void _terrainData(bool success, const QJsonValue& dataJsonValue) final; - private slots: void _sendNextBatch (void); void _queryObjectDestroyed (QObject* elevationProvider); + void _coordinateHeights (bool success, QList heights); private: typedef struct { @@ -87,6 +124,7 @@ private: State _state = State::Idle; const int _batchTimeout = 500; QTimer _batchTimer; + TerrainAirMapQuery _terrainQuery; }; /// NOTE: TerrainAtCoordinateQuery is not thread safe. All instances/calls to ElevationProvider must be on main thread. @@ -102,13 +140,13 @@ public: void requestData(const QList& coordinates); // Internal method - void _signalTerrainData(bool success, QList& altitudes); + void _signalTerrainData(bool success, QList& heights); signals: - void terrainData(bool success, QList altitudes); + void terrainData(bool success, QList heights); }; -class TerrainPathQuery : public TerrainQuery +class TerrainPathQuery : public QObject { Q_OBJECT @@ -123,16 +161,18 @@ public: typedef struct { double latStep; ///< Amount of latitudinal distance between each returned height double lonStep; ///< Amount of longitudinal distance between each returned height - QList rgHeight; ///< Terrain heights along path + QList heights; ///< Terrain heights along path } PathHeightInfo_t; signals: /// Signalled when terrain data comes back from server void terrainData(bool success, const PathHeightInfo_t& pathHeightInfo); -protected: - // Overrides from TerrainQuery - void _terrainData(bool success, const QJsonValue& dataJsonValue) final; +private slots: + void _pathHeights(bool success, double latStep, double lonStep, const QList& heights); + +private: + TerrainAirMapQuery _terrainQuery; }; Q_DECLARE_METATYPE(TerrainPathQuery::PathHeightInfo_t) @@ -165,7 +205,7 @@ private: }; -class TerrainCarpetQuery : public TerrainQuery +class TerrainCarpetQuery : public QObject { Q_OBJECT @@ -183,12 +223,7 @@ signals: /// Signalled when terrain data comes back from server void terrainData(bool success, double minHeight, double maxHeight, const QList>& carpet); - -protected: - // Overrides from TerrainQuery - void _terrainData(bool success, const QJsonValue& dataJsonValue) final; - private: - bool _statsOnly; + TerrainAirMapQuery _terrainQuery; };