Terrain.cc 7.44 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
    query.addQueryItem(QStringLiteral("points"), points);
DonLakeFlyer's avatar
DonLakeFlyer committed
80
    QUrl url(QStringLiteral("https://api.airmap.com/elevation/v1/ele"));
81 82 83 84 85 86 87 88 89 90
    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 115
{
    QNetworkReply* reply = qobject_cast<QNetworkReply*>(QObject::sender());
DonLakeFlyer's avatar
DonLakeFlyer committed
116

117 118 119 120
    _state = State::Idle;

    // When an error occurs we still end up here
    if (reply->error() != QNetworkReply::NoError) {
DonLakeFlyer's avatar
DonLakeFlyer committed
121
        qCDebug(ElevationProviderLog) << "_requestFinished error:" << reply->error();
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
        qCDebug(ElevationProviderLog) << "_requestFinished unable to parse json:" << parseError.errorString();
DonLakeFlyer's avatar
DonLakeFlyer committed
133 134
        _batchFailed();
        reply->deleteLater();
135 136
        return;
    }
DonLakeFlyer's avatar
DonLakeFlyer committed
137

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

DonLakeFlyer's avatar
DonLakeFlyer committed
147 148 149 150 151 152 153 154
    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) {
155 156 157 158 159 160
        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;
        }
161
    }
DonLakeFlyer's avatar
DonLakeFlyer committed
162 163 164 165 166
    _sentRequests.clear();

    reply->deleteLater();
}

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

    return QStringLiteral("State unknown");
203 204
}

DonLakeFlyer's avatar
DonLakeFlyer committed
205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221
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);
222
}