QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
TerrainQueryCopernicus.cc
Go to the documentation of this file.
5#include "QGCNetworkHelper.h"
6
7#include <QtCore/QJsonArray>
8#include <QtCore/QJsonDocument>
9#include <QtCore/QJsonObject>
10#include <QtCore/QUrl>
11#include <QtCore/QUrlQuery>
12#include <QtNetwork/QNetworkAccessManager>
13#include <QtNetwork/QNetworkReply>
14#include <QtNetwork/QNetworkRequest>
15#include <QtNetwork/QSslConfiguration>
16#include <QtPositioning/QGeoCoordinate>
17
18QGC_LOGGING_CATEGORY(TerrainQueryCopernicusLog, "Terrain.TerrainQueryCopernicus")
19
21 : TerrainOnlineQuery(parent)
22{
23 qCDebug(TerrainQueryCopernicusLog) << this;
24}
25
27{
28 qCDebug(TerrainQueryCopernicusLog) << this;
29}
30
31void TerrainQueryCopernicus::requestCoordinateHeights(const QList<QGeoCoordinate> &coordinates)
32{
33 if (coordinates.isEmpty()) {
34 return;
35 }
36
37 QString points;
38 for (const QGeoCoordinate &coord: coordinates) {
39 const QString point = QString::number(coord.latitude(), 'f', 10) + "," + QString::number(coord.longitude(), 'f', 10);
40 points += (point + ",");
41 }
42 points = points.mid(0, points.length() - 1); // remove the last ',' from string
43
44 QUrlQuery query;
45 query.addQueryItem(QStringLiteral("points"), points);
46
48 _sendQuery(QString(), query);
49}
50
51void TerrainQueryCopernicus::requestPathHeights(const QGeoCoordinate &fromCoord, const QGeoCoordinate &toCoord)
52{
53 QString points;
54 points += QString::number(fromCoord.latitude(), 'f', 10) + ","
55 + QString::number(fromCoord.longitude(), 'f', 10) + ",";
56 points += QString::number(toCoord.latitude(), 'f', 10) + ","
57 + QString::number(toCoord.longitude(), 'f', 10);
58
59 QUrlQuery query;
60 query.addQueryItem(QStringLiteral("points"), points);
61
63 _sendQuery(QStringLiteral("/path"), query);
64}
65
66void TerrainQueryCopernicus::requestCarpetHeights(const QGeoCoordinate &swCoord, const QGeoCoordinate &neCoord, bool statsOnly)
67{
68 QString points;
69 points += QString::number(swCoord.latitude(), 'f', 10) + ","
70 + QString::number(swCoord.longitude(), 'f', 10) + ",";
71 points += QString::number(neCoord.latitude(), 'f', 10) + ","
72 + QString::number(neCoord.longitude(), 'f', 10);
73
74 QUrlQuery query;
75 query.addQueryItem(QStringLiteral("points"), points);
76
78 _carpetStatsOnly = statsOnly;
79
80 _sendQuery(QStringLiteral("/carpet"), query);
81}
82
83void TerrainQueryCopernicus::_sendQuery(const QString &path, const QUrlQuery &urlQuery)
84{
85 QUrl url(QString(CopernicusElevationProvider::kProviderURL) + QStringLiteral("/api/v1") + path);
86 url.setQuery(urlQuery);
87 qCDebug(TerrainQueryCopernicusLog) << url;
88
89 QNetworkRequest request(url);
90#ifdef QT_DEBUG
91 QSslConfiguration sslConf = request.sslConfiguration();
92 sslConf.setPeerVerifyMode(QSslSocket::VerifyNone);
93 request.setSslConfiguration(sslConf);
94#endif
95
96 QNetworkReply* const networkReply = _networkManager->get(request);
97 if (!networkReply) {
98 qCWarning(TerrainQueryCopernicusLog) << "QNetworkManager::Get did not return QNetworkReply";
100 return;
101 }
102
104
105 (void) connect(networkReply, &QNetworkReply::finished, this, &TerrainQueryCopernicus::_requestFinished);
106 (void) connect(networkReply, &QNetworkReply::sslErrors, this, &TerrainQueryCopernicus::_sslErrors);
107 (void) connect(networkReply, &QNetworkReply::errorOccurred, this, &TerrainQueryCopernicus::_requestError);
108}
109
110void TerrainQueryCopernicus::_requestFinished()
111{
112 QNetworkReply* const reply = qobject_cast<QNetworkReply*>(QObject::sender());
113 if (!reply) {
114 qCWarning(TerrainQueryCopernicusLog) << "null reply";
115 return;
116 }
117
118 if (reply->error() != QNetworkReply::NoError) {
119 qCWarning(TerrainQueryCopernicusLog) << "error:url:data" << reply->error() << reply->url() << reply->readAll();
120 reply->deleteLater();
122 return;
123 }
124
125 const QByteArray responseBytes = reply->readAll();
126 reply->deleteLater();
127
128 const QJsonValue &jsonData = TerrainTileCopernicus::getJsonFromData(responseBytes);
129 if (jsonData.isNull()) {
131 return;
132 }
133
134 qCDebug(TerrainQueryCopernicusLog) << "success";
135 switch (_queryMode) {
137 _parseCoordinateData(jsonData);
138 break;
140 _parsePathData(jsonData);
141 break;
143 _parseCarpetData(jsonData);
144 break;
145 default:
146 break;
147 }
148}
149
150void TerrainQueryCopernicus::_parseCoordinateData(const QJsonValue &coordinateJson)
151{
152 const QJsonArray dataArray = coordinateJson.toArray();
153
154 QList<double> heights;
155 for (const QJsonValue &dataValue: dataArray) {
156 (void) heights.append(dataValue.toDouble());
157 }
158
159 emit coordinateHeightsReceived(true, heights);
160}
161
162void TerrainQueryCopernicus::_parsePathData(const QJsonValue &pathJson)
163{
164 const QJsonArray pathArray = pathJson.toArray();
165 if (pathArray.isEmpty()) {
167 return;
168 }
169
170 const QJsonObject jsonObject = pathArray[0].toObject();
171 const QJsonArray stepArray = jsonObject["step"].toArray();
172 const QJsonArray profileArray = jsonObject["profile"].toArray();
173
174 if (stepArray.count() < 2) {
176 return;
177 }
178
179 const double latStep = stepArray[0].toDouble();
180 const double lonStep = stepArray[1].toDouble();
181
182 QList<double> heights;
183 for (const QJsonValue &profileValue: profileArray) {
184 (void) heights.append(profileValue.toDouble());
185 }
186
187 // Note: pathHeightsReceived expects distances in meters, but API returns lat/lon steps.
188 // This semantic mismatch means callers receive coordinate steps instead of actual distances.
189 // TODO: Convert lat/lon steps to approximate distances using the path coordinates.
190 emit pathHeightsReceived(true, latStep, lonStep, heights);
191}
192
193void TerrainQueryCopernicus::_parseCarpetData(const QJsonValue &carpetJson)
194{
195 const QJsonArray carpetArray = carpetJson.toArray();
196 if (carpetArray.isEmpty()) {
198 return;
199 }
200
201 const QJsonObject jsonObject = carpetArray[0].toObject();
202
203 const QJsonObject statsObject = jsonObject["stats"].toObject();
204 const double minHeight = statsObject["min"].toDouble();
205 const double maxHeight = statsObject["max"].toDouble();
206
207 QList<QList<double>> carpet;
208 if (!_carpetStatsOnly) {
209 const QJsonArray carpetDataArray = jsonObject["carpet"].toArray();
210
211 for (qsizetype i = 0; i < carpetDataArray.count(); i++) {
212 const QJsonArray rowArray = carpetDataArray[i].toArray();
213 (void) carpet.append(QList<double>());
214
215 for (qsizetype j = 0; j < rowArray.count(); j++) {
216 const double height = rowArray[j].toDouble();
217 (void) carpet.last().append(height);
218 }
219 }
220 }
221
222 emit carpetHeightsReceived(true, minHeight, maxHeight, carpet);
223}
#define QGC_LOGGING_CATEGORY(name, categoryStr)
static constexpr const char * kProviderURL
QNetworkAccessManager * _networkManager
virtual void _requestError(QNetworkReply::NetworkError code)
virtual void _sslErrors(const QList< QSslError > &errors)
void requestPathHeights(const QGeoCoordinate &fromCoord, const QGeoCoordinate &toCoord) final
void requestCoordinateHeights(const QList< QGeoCoordinate > &coordinates) final
void requestCarpetHeights(const QGeoCoordinate &swCoord, const QGeoCoordinate &neCoord, bool statsOnly) final
TerrainQuery::QueryMode _queryMode
void pathHeightsReceived(bool success, double distanceBetween, double finalDistanceBetween, const QList< double > &heights)
void carpetHeightsReceived(bool success, double minHeight, double maxHeight, const QList< QList< double > > &carpet)
void coordinateHeightsReceived(bool success, const QList< double > &heights)
static QJsonValue getJsonFromData(const QByteArray &input)
void ignoreSslErrorsIfNeeded(QNetworkReply *reply)