1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
/****************************************************************************
*
* (c) 2017 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
*
* QGroundControl is licensed according to the terms in the file
* COPYING.md in the root of the source code directory.
*
****************************************************************************/
#pragma once
#include "TerrainTile.h"
#include "QGCMapEngineData.h"
#include "QGCLoggingCategory.h"
#include <QObject>
#include <QGeoCoordinate>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QTimer>
#include <QtLocation/private/qgeotiledmapreply_p.h>
Q_DECLARE_LOGGING_CATEGORY(TerrainQueryLog)
Q_DECLARE_LOGGING_CATEGORY(TerrainQueryVerboseLog)
class TerrainAtCoordinateQuery;
/// Base class for offline/online terrain queries
class TerrainQueryInterface : public QObject
{
Q_OBJECT
public:
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 coordinateHeightsReceived(bool success, QList<double> heights);
void pathHeightsReceived(bool success, double latStep, double lonStep, const QList<double>& heights);
void carpetHeightsReceived(bool success, double minHeight, double maxHeight, const QList<QList<double>>& carpet);
};
/// AirMap online implementation of terrain queries
class TerrainAirMapQuery : public TerrainQueryInterface {
Q_OBJECT
public:
TerrainAirMapQuery(QObject* parent = NULL);
// 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 _requestError (QNetworkReply::NetworkError code);
void _requestFinished (void);
void _sslErrors (const QList<QSslError> &errors);
private:
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;
};
/// AirMap offline cachable implementation of terrain queries
class TerrainOfflineAirMapQuery : public TerrainQueryInterface {
Q_OBJECT
public:
TerrainOfflineAirMapQuery(QObject* parent = NULL);
// 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;
// Internal methods
void _signalCoordinateHeights(bool success, QList<double> heights);
void _signalPathHeights(bool success, double latStep, double lonStep, const QList<double>& heights);
void _signalCarpetHeights(bool success, double minHeight, double maxHeight, const QList<QList<double>>& carpet);
};
/// Used internally by TerrainOfflineAirMapQuery to manage terrain tiles
class TerrainTileManager : public QObject {
Q_OBJECT
public:
TerrainTileManager(void);
void addCoordinateQuery (TerrainOfflineAirMapQuery* terrainQueryInterface, const QList<QGeoCoordinate>& coordinates);
void addPathQuery (TerrainOfflineAirMapQuery* terrainQueryInterface, const QGeoCoordinate& startPoint, const QGeoCoordinate& endPoint);
private slots:
void _terrainDone (QByteArray responseBytes, QNetworkReply::NetworkError error);
private:
enum class State {
Idle,
Downloading,
};
enum QueryMode {
QueryModeCoordinates,
QueryModePath,
QueryModeCarpet
};
typedef struct {
TerrainOfflineAirMapQuery* terrainQueryInterface;
QueryMode queryMode;
double latStep, lonStep;
QList<QGeoCoordinate> coordinates;
} QueuedRequestInfo_t;
void _tileFailed (void);
bool _getAltitudesForCoordinates (const QList<QGeoCoordinate>& coordinates, QList<double>& altitudes, bool& error);
QString _getTileHash (const QGeoCoordinate& coordinate);
QList<QueuedRequestInfo_t> _requestQueue;
State _state = State::Idle;
QNetworkAccessManager _networkManager;
QMutex _tilesMutex;
QHash<QString, TerrainTile> _tiles;
};
/// Used internally by TerrainAtCoordinateQuery to batch coordinate requests together
class TerrainAtCoordinateBatchManager : public QObject {
Q_OBJECT
public:
TerrainAtCoordinateBatchManager(void);
void addQuery(TerrainAtCoordinateQuery* terrainAtCoordinateQuery, const QList<QGeoCoordinate>& coordinates);
private slots:
void _sendNextBatch (void);
void _queryObjectDestroyed (QObject* elevationProvider);
void _coordinateHeights (bool success, QList<double> heights);
private:
typedef struct {
TerrainAtCoordinateQuery* terrainAtCoordinateQuery;
QList<QGeoCoordinate> 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<QueuedRequestInfo_t> _requestQueue;
QList<SentRequestInfo_t> _sentRequests;
State _state = State::Idle;
const int _batchTimeout = 500;
QTimer _batchTimer;
TerrainOfflineAirMapQuery _terrainQuery;
};
/// 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<QGeoCoordinate>& coordinates);
// Internal method
void _signalTerrainData(bool success, QList<double>& heights);
signals:
void terrainDataReceived(bool success, QList<double> heights);
};
class TerrainPathQuery : public QObject
{
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);
typedef struct {
double latStep; ///< Amount of latitudinal distance between each returned height
double lonStep; ///< Amount of longitudinal distance between each returned height
QList<double> heights; ///< Terrain heights along path
} PathHeightInfo_t;
signals:
/// Signalled when terrain data comes back from server
void terrainDataReceived(bool success, const PathHeightInfo_t& pathHeightInfo);
private slots:
void _pathHeights(bool success, double latStep, double lonStep, const QList<double>& heights);
private:
TerrainOfflineAirMapQuery _terrainQuery;
};
Q_DECLARE_METATYPE(TerrainPathQuery::PathHeightInfo_t)
class TerrainPolyPathQuery : public QObject
{
Q_OBJECT
public:
TerrainPolyPathQuery(QObject* parent = NULL);
/// Async terrain query for terrain heights for the paths between each specified QGeoCoordinate.
/// When the query is done, the terrainData() signal is emitted.
/// @param polyPath List of QGeoCoordinate
void requestData(const QVariantList& polyPath);
void requestData(const QList<QGeoCoordinate>& polyPath);
signals:
/// Signalled when terrain data comes back from server
void terrainDataReceived(bool success, const QList<TerrainPathQuery::PathHeightInfo_t>& rgPathHeightInfo);
private slots:
void _terrainDataReceived(bool success, const TerrainPathQuery::PathHeightInfo_t& pathHeightInfo);
private:
int _curIndex;
QList<QGeoCoordinate> _rgCoords;
QList<TerrainPathQuery::PathHeightInfo_t> _rgPathHeightInfo;
TerrainPathQuery _pathQuery;
};
class TerrainCarpetQuery : public QObject
{
Q_OBJECT
public:
TerrainCarpetQuery(QObject* parent = NULL);
/// Async terrain query for terrain information bounded by the specifed corners.
/// When the query is done, the terrainData() signal is emitted.
/// @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
void requestData(const QGeoCoordinate& swCoord, const QGeoCoordinate& neCoord, bool statsOnly);
signals:
/// Signalled when terrain data comes back from server
void terrainDataReceived(bool success, double minHeight, double maxHeight, const QList<QList<double>>& carpet);
private:
TerrainAirMapQuery _terrainQuery;
};