Terrain.cc 7.18 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/****************************************************************************
 *
 *   (c) 2009-2016 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.
 *
 ****************************************************************************/

#include "Terrain.h"

#include <QUrl>
#include <QUrlQuery>
#include <QNetworkRequest>
#include <QNetworkProxy>
#include <QNetworkReply>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
DonLakeFlyer's avatar
DonLakeFlyer committed
20
#include <QTimer>
21

DonLakeFlyer's avatar
DonLakeFlyer committed
22 23 24 25 26
QGC_LOGGING_CATEGORY(ElevationProviderLog, "ElevationProviderLog")

Q_GLOBAL_STATIC(TerrainBatchManager, _terrainBatchManager)

TerrainBatchManager::TerrainBatchManager(void)
27
{
DonLakeFlyer's avatar
DonLakeFlyer committed
28 29 30 31
    _batchTimer.setSingleShot(true);
    _batchTimer.setInterval(_batchTimeout);
    connect(&_batchTimer, &QTimer::timeout, this, &TerrainBatchManager::_sendNextBatch);
}
32

DonLakeFlyer's avatar
DonLakeFlyer committed
33 34 35
void TerrainBatchManager::addQuery(ElevationProvider* elevationProvider, const QList<QGeoCoordinate>& coordinates)
{
    if (coordinates.length() > 0) {
36 37
        qCDebug(ElevationProviderLog) << "addQuery: elevationProvider:coordinates.count" << elevationProvider << coordinates.count();
        connect(elevationProvider, &ElevationProvider::destroyed, this, &TerrainBatchManager::_elevationProviderDestroyed);
DonLakeFlyer's avatar
DonLakeFlyer committed
38 39 40 41 42 43
        QueuedRequestInfo_t queuedRequestInfo = { elevationProvider, coordinates };
        _requestQueue.append(queuedRequestInfo);
        if (!_batchTimer.isActive()) {
            _batchTimer.start();
        }
    }
44 45
}

DonLakeFlyer's avatar
DonLakeFlyer committed
46
void TerrainBatchManager::_sendNextBatch(void)
47
{
48
    qCDebug(ElevationProviderLog) << "_sendNextBatch _state:_requestQueue.count:_sentRequests.count" << _stateToString(_state) << _requestQueue.count() << _sentRequests.count();
DonLakeFlyer's avatar
DonLakeFlyer committed
49 50 51 52 53

    if (_state != State::Idle) {
        // Waiting for last download the complete, wait some more
        _batchTimer.start();
        return;
54 55
    }

DonLakeFlyer's avatar
DonLakeFlyer committed
56 57
    if (_requestQueue.count() == 0) {
        return;
58 59
    }

DonLakeFlyer's avatar
DonLakeFlyer committed
60 61 62 63 64
    _sentRequests.clear();

    // Convert coordinates to point strings for json query
    QString points;
    foreach (const QueuedRequestInfo_t& requestInfo, _requestQueue) {
65
        SentRequestInfo_t sentRequestInfo = { requestInfo.elevationProvider, false, requestInfo.coordinates.count() };
DonLakeFlyer's avatar
DonLakeFlyer committed
66 67 68 69 70 71 72 73 74 75 76 77 78
        qCDebug(ElevationProviderLog) << "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) + ",";
        }

    }
    points = points.mid(0, points.length() - 1); // remove the last ',' from string
    _requestQueue.clear();

    QUrlQuery query;
79 80 81 82 83 84 85 86 87 88 89 90
    query.addQueryItem(QStringLiteral("points"), points);
    QUrl url(QStringLiteral("https://api.airmap.com/elevation/stage/srtm1/ele"));
    url.setQuery(query);

    QNetworkRequest request(url);

    QNetworkProxy tProxy;
    tProxy.setType(QNetworkProxy::DefaultProxy);
    _networkManager.setProxy(tProxy);

    QNetworkReply* networkReply = _networkManager.get(request);
    if (!networkReply) {
DonLakeFlyer's avatar
DonLakeFlyer committed
91 92
        _batchFailed();
        return;
93 94
    }

DonLakeFlyer's avatar
DonLakeFlyer committed
95
    connect(networkReply, &QNetworkReply::finished, this, &TerrainBatchManager::_requestFinished);
96 97 98 99

    _state = State::Downloading;
}

DonLakeFlyer's avatar
DonLakeFlyer committed
100 101 102 103 104
void TerrainBatchManager::_batchFailed(void)
{
    QList<float>    noAltitudes;

    foreach (const SentRequestInfo_t& sentRequestInfo, _sentRequests) {
105 106 107 108
        if (!sentRequestInfo.providerDestroyed) {
            disconnect(sentRequestInfo.elevationProvider, &ElevationProvider::destroyed, this, &TerrainBatchManager::_elevationProviderDestroyed);
            sentRequestInfo.elevationProvider->_signalTerrainData(false, noAltitudes);
        }
DonLakeFlyer's avatar
DonLakeFlyer committed
109 110 111 112 113
    }
    _sentRequests.clear();
}

void TerrainBatchManager::_requestFinished()
114
{
DonLakeFlyer's avatar
DonLakeFlyer committed
115
    qCDebug(ElevationProviderLog) << "_requestFinished";
116
    QNetworkReply* reply = qobject_cast<QNetworkReply*>(QObject::sender());
DonLakeFlyer's avatar
DonLakeFlyer committed
117

118 119 120 121
    _state = State::Idle;

    // When an error occurs we still end up here
    if (reply->error() != QNetworkReply::NoError) {
DonLakeFlyer's avatar
DonLakeFlyer committed
122 123
        _batchFailed();
        reply->deleteLater();
124 125 126 127 128 129 130 131
        return;
    }

    QByteArray responseBytes = reply->readAll();

    QJsonParseError parseError;
    QJsonDocument responseJson = QJsonDocument::fromJson(responseBytes, &parseError);
    if (parseError.error != QJsonParseError::NoError) {
DonLakeFlyer's avatar
DonLakeFlyer committed
132 133
        _batchFailed();
        reply->deleteLater();
134 135
        return;
    }
DonLakeFlyer's avatar
DonLakeFlyer committed
136

137 138
    QJsonObject rootObject = responseJson.object();
    QString status = rootObject["status"].toString();
DonLakeFlyer's avatar
DonLakeFlyer committed
139 140 141 142 143
    if (status != "success") {
        _batchFailed();
        reply->deleteLater();
        return;
    }
144

DonLakeFlyer's avatar
DonLakeFlyer committed
145 146 147 148 149 150 151 152
    QList<float> altitudes;
    const QJsonArray& dataArray = rootObject["data"].toArray();
    for (int i = 0; i < dataArray.count(); i++) {
        altitudes.push_back(dataArray[i].toDouble());
    }

    int currentIndex = 0;
    foreach (const SentRequestInfo_t& sentRequestInfo, _sentRequests) {
153 154 155 156 157 158
        if (!sentRequestInfo.providerDestroyed) {
            disconnect(sentRequestInfo.elevationProvider, &ElevationProvider::destroyed, this, &TerrainBatchManager::_elevationProviderDestroyed);
            QList<float> requestAltitudes = altitudes.mid(currentIndex, sentRequestInfo.cCoord);
            sentRequestInfo.elevationProvider->_signalTerrainData(true, requestAltitudes);
            currentIndex += sentRequestInfo.cCoord;
        }
159
    }
DonLakeFlyer's avatar
DonLakeFlyer committed
160 161 162 163 164
    _sentRequests.clear();

    reply->deleteLater();
}

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
void TerrainBatchManager::_elevationProviderDestroyed(QObject* elevationProvider)
{
    // Remove/Mark deleted objects queries from queues

    qCDebug(ElevationProviderLog) << "_elevationProviderDestroyed elevationProvider" << elevationProvider;

    int i = 0;
    while (i < _requestQueue.count()) {
        const QueuedRequestInfo_t& requestInfo = _requestQueue[i];
        if (requestInfo.elevationProvider == elevationProvider) {
            qCDebug(ElevationProviderLog) << "Removing deleted provider from _requestQueue index:elevationProvider" << i << requestInfo.elevationProvider;
            _requestQueue.removeAt(i);
        } else {
            i++;
        }
    }

    for (int i=0; i<_sentRequests.count(); i++) {
        SentRequestInfo_t& sentRequestInfo = _sentRequests[i];
        if (sentRequestInfo.elevationProvider == elevationProvider) {
            qCDebug(ElevationProviderLog) << "Zombieing deleted provider from _sentRequests index:elevatationProvider" << sentRequestInfo.elevationProvider;
            sentRequestInfo.providerDestroyed = true;
        }
    }
}

QString TerrainBatchManager::_stateToString(State state)
{
    switch (state) {
    case State::Idle:
        return QStringLiteral("Idle");
    case State::Downloading:
        return QStringLiteral("Downloading");
    }
}

DonLakeFlyer's avatar
DonLakeFlyer committed
201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217
ElevationProvider::ElevationProvider(QObject* parent)
    : QObject(parent)
{

}
void ElevationProvider::queryTerrainData(const QList<QGeoCoordinate>& coordinates)
{
    if (coordinates.length() == 0) {
        return;
    }

    _terrainBatchManager->addQuery(this, coordinates);
}

void ElevationProvider::_signalTerrainData(bool success, QList<float>& altitudes)
{
    emit terrainData(success, altitudes);
218
}