Unverified Commit 2296ed0d authored by Don Gagne's avatar Don Gagne Committed by GitHub

Merge pull request #6233 from DonLakeFlyer/TerrainQueryInterface

Restructure for TerrainQueryInterface abstract base class
parents bf202aac 24f8cd47
......@@ -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<QGeoCoordinate>();
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);
}
......
......@@ -180,10 +180,10 @@ void VisualMissionItem::_reallyUpdateTerrainAltitude(void)
}
}
void VisualMissionItem::_terrainDataReceived(bool success, QList<float> altitudes)
void VisualMissionItem::_terrainDataReceived(bool success, QList<double> heights)
{
if (success) {
_terrainAltitude = altitudes[0];
_terrainAltitude = heights[0];
emit terrainAltitudeChanged(_terrainAltitude);
sender()->deleteLater();
}
......
......@@ -211,7 +211,7 @@ protected:
private slots:
void _updateTerrainAltitude (void);
void _reallyUpdateTerrainAltitude (void);
void _terrainDataReceived (bool success, QList<float> altitudes);
void _terrainDataReceived (bool success, QList<double> heights);
private:
QTimer _updateTerrainTimer;
......
......@@ -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<QGeoCoordinate>& 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<QNetworkReply*>(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<double>() /* heights */);
break;
case QueryModePath:
emit pathHeights(false /* success */, qQNaN() /* latStep */, qQNaN() /* lonStep */, QList<double>() /* heights */);
break;
case QueryModeCarpet:
emit carpetHeights(false /* success */, qQNaN() /* minHeight */, qQNaN() /* maxHeight */, QList<QList<double>>() /* carpet */);
break;
}
}
void TerrainAirMapQuery::_parseCoordinateData(const QJsonValue& coordinateJson)
{
QList<double> 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<double> 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<QList<double>> carpet;
if (!_carpetStatsOnly) {
QJsonArray carpetArray = jsonObject["carpet"].toArray();
for (int i=0; i<carpetArray.count(); i++) {
QJsonArray rowArray = carpetArray[i].toArray();
carpet.append(QList<double>());
for (int j=0; j<rowArray.count(); j++) {
double height = rowArray[j].toDouble();
carpet.last().append(height);
}
}
}
emit carpetHeights(true /*success*/, minHeight, maxHeight, carpet);
}
TerrainAtCoordinateBatchManager::TerrainAtCoordinateBatchManager(void)
......@@ -91,6 +221,7 @@ TerrainAtCoordinateBatchManager::TerrainAtCoordinateBatchManager(void)
_batchTimer.setSingleShot(true);
_batchTimer.setInterval(_batchTimeout);
connect(&_batchTimer, &QTimer::timeout, this, &TerrainAtCoordinateBatchManager::_sendNextBatch);
connect(&_terrainQuery, &TerrainQueryInterface::coordinateHeights, this, &TerrainAtCoordinateBatchManager::_coordinateHeights);
}
void TerrainAtCoordinateBatchManager::addQuery(TerrainAtCoordinateQuery* terrainAtCoordinateQuery, const QList<QGeoCoordinate>& coordinates)
......@@ -123,35 +254,28 @@ void TerrainAtCoordinateBatchManager::_sendNextBatch(void)
_sentRequests.clear();
// Convert coordinates to point strings for json query
QString points;
QList<QGeoCoordinate> 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<float> noAltitudes;
QList<double> 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<double> heights)
{
_state = State::Idle;
......@@ -204,17 +328,11 @@ void TerrainAtCoordinateBatchManager::_terrainData(bool success, const QJsonValu
return;
}
QList<float> 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<float> requestAltitudes = altitudes.mid(currentIndex, sentRequestInfo.cCoord);
QList<double> requestAltitudes = heights.mid(currentIndex, sentRequestInfo.cCoord);
sentRequestInfo.terrainAtCoordinateQuery->_signalTerrainData(true, requestAltitudes);
currentIndex += sentRequestInfo.cCoord;
}
......@@ -236,54 +354,30 @@ void TerrainAtCoordinateQuery::requestData(const QList<QGeoCoordinate>& coordina
_TerrainAtCoordinateBatchManager->addQuery(this, coordinates);
}
void TerrainAtCoordinateQuery::_signalTerrainData(bool success, QList<float>& altitudes)
void TerrainAtCoordinateQuery::_signalTerrainData(bool success, QList<double>& heights)
{
emit terrainData(success, altitudes);
emit terrainData(success, heights);
}
TerrainPathQuery::TerrainPathQuery(QObject* parent)
: TerrainQuery(parent)
: QObject(parent)
{
qRegisterMetaType<PathHeightInfo_t>();
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<double>& 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<QList<double>>() /* 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<QList<double>> carpet;
if (!_statsOnly) {
QJsonArray carpetArray = jsonObject["carpet"].toArray();
for (int i=0; i<carpetArray.count(); i++) {
QJsonArray rowArray = carpetArray[i].toArray();
carpet.append(QList<double>());
for (int j=0; j<rowArray.count(); j++) {
double height = rowArray[j].toDouble();
carpet.last().append(height);
}
}
}
emit terrainData(true /*success*/, minHeight, maxHeight, carpet);
_terrainQuery.requestCarpetHeights(swCoord, neCoord, statsOnly);
}
......@@ -21,31 +21,71 @@ Q_DECLARE_LOGGING_CATEGORY(TerrainQueryLog)
class TerrainAtCoordinateQuery;
// Base class for all terrain query objects
class TerrainQuery : public QObject {
/// Base class for offline/online terrain queries
class TerrainQueryInterface : public QObject
{
Q_OBJECT
public:
TerrainQuery(QObject* parent = NULL);
TerrainQueryInterface(QObject* parent) : QObject(parent) { }
/// Request terrain heights for specified coodinates.
/// Signals: coordinateHeights when data is available
virtual void requestCoordinateHeights(const QList<QGeoCoordinate>& 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<double> heights);
void pathHeights(bool success, double latStep, double lonStep, const QList<double>& heights);
void carpetHeights(bool success, double minHeight, double maxHeight, const QList<QList<double>>& 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<QGeoCoordinate>& 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<QGeoCoordinate>& 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<double> 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<QGeoCoordinate>& coordinates);
// Internal method
void _signalTerrainData(bool success, QList<float>& altitudes);
void _signalTerrainData(bool success, QList<double>& heights);
signals:
void terrainData(bool success, QList<float> altitudes);
void terrainData(bool success, QList<double> 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<double> rgHeight; ///< Terrain heights along path
QList<double> 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<double>& 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<QList<double>>& carpet);
protected:
// Overrides from TerrainQuery
void _terrainData(bool success, const QJsonValue& dataJsonValue) final;
private:
bool _statsOnly;
TerrainAirMapQuery _terrainQuery;
};
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