TerrainQuery.h 10.1 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11
/****************************************************************************
 *
 *   (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

12 13
#include "TerrainTile.h"
#include "QGCMapEngineData.h"
14 15 16 17 18 19 20
#include "QGCLoggingCategory.h"

#include <QObject>
#include <QGeoCoordinate>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QTimer>
21
#include <QtLocation/private/qgeotiledmapreply_p.h>
22 23 24 25 26

Q_DECLARE_LOGGING_CATEGORY(TerrainQueryLog)

class TerrainAtCoordinateQuery;

27 28 29
/// Base class for offline/online terrain queries
class TerrainQueryInterface : public QObject
{
30 31 32
    Q_OBJECT

public:
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
    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);
};
56

57 58 59 60 61 62
/// AirMap online implementation of terrain queries
class TerrainAirMapQuery : public TerrainQueryInterface {
    Q_OBJECT

public:
    TerrainAirMapQuery(QObject* parent = NULL);
63

64 65 66 67
    // 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;
68 69 70 71 72

private slots:
    void _requestFinished(void);

private:
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
    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;
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
/// AirMap online 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 method
    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);

    bool                    _carpetStatsOnly;
};

/// Used internally by TerrainOfflineAirMapQuery to manage terrain tiles
class TerrainTileManager : public QObject {
    Q_OBJECT

public:
    TerrainTileManager(void);

Andreas Bircher's avatar
Andreas Bircher committed
117
    void addCoordinateQuery(TerrainOfflineAirMapQuery* terrainQueryInterface, const QList<QGeoCoordinate>& coordinates);
118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133

private slots:
    void _fetchedTile       (void);                             /// slot to handle fetched elevation tiles

private:
    enum class State {
        Idle,
        Downloading,
    };

    enum QueryMode {
        QueryModeCoordinates,
        QueryModePath,
        QueryModeCarpet
    };

Andreas Bircher's avatar
Andreas Bircher committed
134 135 136 137 138 139
    typedef struct {
        TerrainOfflineAirMapQuery*  terrainQueryInterface;
        QList<QGeoCoordinate>       coordinates;
        QueryMode                   queryMode;
    } QueuedRequestInfo_t;

140
    void _tileFailed(void);
Andreas Bircher's avatar
Andreas Bircher committed
141
    bool _getAltitudesForCoordinates(const QList<QGeoCoordinate>& coordinates, QList<double>& altitudes);
142 143 144 145 146 147 148 149 150 151
    QString _getTileHash(const QGeoCoordinate& coordinate);     /// Method to create a unique string for each tile

    QList<QueuedRequestInfo_t>  _requestQueue;
    State                       _state = State::Idle;
    QNetworkAccessManager       _networkManager;

    QMutex                      _tilesMutex;
    QHash<QString, TerrainTile> _tiles;
};

152
/// Used internally by TerrainAtCoordinateQuery to batch coordinate requests together
153
class TerrainAtCoordinateBatchManager : public QObject {
154 155 156 157 158 159 160 161 162 163
    Q_OBJECT

public:
    TerrainAtCoordinateBatchManager(void);

    void addQuery(TerrainAtCoordinateQuery* terrainAtCoordinateQuery, const QList<QGeoCoordinate>& coordinates);

private slots:
    void _sendNextBatch         (void);
    void _queryObjectDestroyed  (QObject* elevationProvider);
164
    void _coordinateHeights     (bool success, QList<double> heights);
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

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;
192
    TerrainAirMapQuery          _terrainQuery;
193 194 195 196 197 198 199 200 201
};

/// 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);

DonLakeFlyer's avatar
DonLakeFlyer committed
202 203 204
    /// Async terrain query for a list of lon,lat coordinates. When the query is done, the terrainData() signal
    /// is emitted.
    ///     @param coordinates to query
205 206 207
    void requestData(const QList<QGeoCoordinate>& coordinates);

    // Internal method
208
    void _signalTerrainData(bool success, QList<double>& heights);
209 210

signals:
211
    void terrainData(bool success, QList<double> heights);
212 213
};

214
class TerrainPathQuery : public QObject
215 216 217 218 219 220
{
    Q_OBJECT

public:
    TerrainPathQuery(QObject* parent = NULL);

DonLakeFlyer's avatar
DonLakeFlyer committed
221 222 223
    /// 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
224 225
    void requestData(const QGeoCoordinate& fromCoord, const QGeoCoordinate& toCoord);

226 227 228
    typedef struct {
        double          latStep;    ///< Amount of latitudinal distance between each returned height
        double          lonStep;    ///< Amount of longitudinal distance between each returned height
229
        QList<double>   heights;    ///< Terrain heights along path
230 231 232 233 234 235
    } PathHeightInfo_t;

signals:
    /// Signalled when terrain data comes back from server
    void terrainData(bool success, const PathHeightInfo_t& pathHeightInfo);

236 237 238 239 240
private slots:
    void _pathHeights(bool success, double latStep, double lonStep, const QList<double>& heights);

private:
    TerrainAirMapQuery _terrainQuery;
241 242 243 244 245 246 247 248 249 250 251
};

Q_DECLARE_METATYPE(TerrainPathQuery::PathHeightInfo_t)

class TerrainPolyPathQuery : public QObject
{
    Q_OBJECT

public:
    TerrainPolyPathQuery(QObject* parent = NULL);

DonLakeFlyer's avatar
DonLakeFlyer committed
252 253 254
    /// 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
255 256
    void requestData(const QVariantList& polyPath);
    void requestData(const QList<QGeoCoordinate>& polyPath);
257 258 259

signals:
    /// Signalled when terrain data comes back from server
260 261 262 263 264 265 266 267 268 269
    void terrainData(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;
270
};
271

DonLakeFlyer's avatar
DonLakeFlyer committed
272

273
class TerrainCarpetQuery : public QObject
DonLakeFlyer's avatar
DonLakeFlyer committed
274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291
{
    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 terrainData(bool success, double minHeight, double maxHeight, const QList<QList<double>>& carpet);

private:
292
    TerrainAirMapQuery _terrainQuery;
DonLakeFlyer's avatar
DonLakeFlyer committed
293 294
};