Terrain.cc 5.25 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>
20
#include <QTimer>
21

22 23 24 25 26
QGC_LOGGING_CATEGORY(ElevationProviderLog, "ElevationProviderLog")

Q_GLOBAL_STATIC(TerrainBatchManager, _terrainBatchManager)

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

33 34 35 36 37 38 39 40 41
void TerrainBatchManager::addQuery(ElevationProvider* elevationProvider, const QList<QGeoCoordinate>& coordinates)
{
    if (coordinates.length() > 0) {
        QueuedRequestInfo_t queuedRequestInfo = { elevationProvider, coordinates };
        _requestQueue.append(queuedRequestInfo);
        if (!_batchTimer.isActive()) {
            _batchTimer.start();
        }
    }
42 43
}

44
void TerrainBatchManager::_sendNextBatch(void)
45
{
46 47 48 49 50 51
    qCDebug(ElevationProviderLog) << "_sendNextBatch _state:_requestQueue.count" << (int)_state << _requestQueue.count();

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

54 55
    if (_requestQueue.count() == 0) {
        return;
56 57
    }

58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
    _sentRequests.clear();

    // Convert coordinates to point strings for json query
    QString points;
    foreach (const QueuedRequestInfo_t& requestInfo, _requestQueue) {
        SentRequestInfo_t sentRequestInfo = { requestInfo.elevationProvider, requestInfo.coordinates.count() };
        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;
77 78 79 80 81 82 83 84 85 86 87 88
    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) {
89 90
        _batchFailed();
        return;
91 92
    }

93
    connect(networkReply, &QNetworkReply::finished, this, &TerrainBatchManager::_requestFinished);
94 95 96 97

    _state = State::Downloading;
}

98 99 100 101 102 103 104 105 106 107 108
void TerrainBatchManager::_batchFailed(void)
{
    QList<float>    noAltitudes;

    foreach (const SentRequestInfo_t& sentRequestInfo, _sentRequests) {
        sentRequestInfo.elevationProvider->_signalTerrainData(false, noAltitudes);
    }
    _sentRequests.clear();
}

void TerrainBatchManager::_requestFinished()
109
{
110
    qCDebug(ElevationProviderLog) << "_requestFinished";
111
    QNetworkReply* reply = qobject_cast<QNetworkReply*>(QObject::sender());
112

113 114 115 116
    _state = State::Idle;

    // When an error occurs we still end up here
    if (reply->error() != QNetworkReply::NoError) {
117 118
        _batchFailed();
        reply->deleteLater();
119 120 121 122 123 124 125 126
        return;
    }

    QByteArray responseBytes = reply->readAll();

    QJsonParseError parseError;
    QJsonDocument responseJson = QJsonDocument::fromJson(responseBytes, &parseError);
    if (parseError.error != QJsonParseError::NoError) {
127 128
        _batchFailed();
        reply->deleteLater();
129 130
        return;
    }
131

132 133
    QJsonObject rootObject = responseJson.object();
    QString status = rootObject["status"].toString();
134 135 136 137 138
    if (status != "success") {
        _batchFailed();
        reply->deleteLater();
        return;
    }
139

140 141 142 143 144 145 146 147 148 149 150
    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) {
        QList<float> requestAltitudes = altitudes.mid(currentIndex, sentRequestInfo.cCoord);
        sentRequestInfo.elevationProvider->_signalTerrainData(true, requestAltitudes);
        currentIndex += sentRequestInfo.cCoord;
151
    }
152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
    _sentRequests.clear();

    reply->deleteLater();
}

ElevationProvider::ElevationProvider(QObject* parent)
    : QObject(parent)
{

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

    _terrainBatchManager->addQuery(this, coordinates);
}

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