Newer
Older
/****************************************************************************
*
* (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 "TerrainQuery.h"
#include "QGCMapEngine.h"
#include "QGeoMapReplyQGC.h"
#include <QUrl>
#include <QUrlQuery>
#include <QNetworkRequest>
#include <QNetworkProxy>
#include <QNetworkReply>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QTimer>
#include <QtLocation/private/qgeotilespec_p.h>
QGC_LOGGING_CATEGORY(TerrainQueryLog, "TerrainQueryLog")
QGC_LOGGING_CATEGORY(TerrainQueryVerboseLog, "TerrainQueryVerboseLog")
Q_GLOBAL_STATIC(TerrainAtCoordinateBatchManager, _TerrainAtCoordinateBatchManager)
Q_GLOBAL_STATIC(TerrainTileManager, _terrainTileManager)
TerrainAirMapQuery::TerrainAirMapQuery(QObject* parent)
: TerrainQueryInterface(parent)
qCDebug(TerrainQueryVerboseLog) << "supportsSsl" << QSslSocket::supportsSsl() << "sslLibraryBuildVersionString" << QSslSocket::sslLibraryBuildVersionString();
void TerrainAirMapQuery::requestCoordinateHeights(const QList<QGeoCoordinate>& coordinates)
{
if (qgcApp()->runningUnitTests()) {
emit coordinateHeightsReceived(false, QList<double>());
for (const QGeoCoordinate& coord: 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
QUrlQuery query;
query.addQueryItem(QStringLiteral("points"), points);
_queryMode = QueryModeCoordinates;
_sendQuery(QString() /* path */, query);
}
void TerrainAirMapQuery::requestPathHeights(const QGeoCoordinate& fromCoord, const QGeoCoordinate& toCoord)
{
if (qgcApp()->runningUnitTests()) {
emit pathHeightsReceived(false, qQNaN(), qQNaN(), QList<double>());
QString points;
points += QString::number(fromCoord.latitude(), 'f', 10) + ","
+ QString::number(fromCoord.longitude(), 'f', 10) + ",";
points += QString::number(toCoord.latitude(), 'f', 10) + ","
+ QString::number(toCoord.longitude(), 'f', 10);
QUrlQuery query;
query.addQueryItem(QStringLiteral("points"), points);
_queryMode = QueryModePath;
_sendQuery(QStringLiteral("/path"), query);
}
void TerrainAirMapQuery::requestCarpetHeights(const QGeoCoordinate& swCoord, const QGeoCoordinate& neCoord, bool statsOnly)
{
if (qgcApp()->runningUnitTests()) {
emit carpetHeightsReceived(false, qQNaN(), qQNaN(), QList<QList<double>>());
QString points;
points += QString::number(swCoord.latitude(), 'f', 10) + ","
+ QString::number(swCoord.longitude(), 'f', 10) + ",";
points += QString::number(neCoord.latitude(), 'f', 10) + ","
+ QString::number(neCoord.longitude(), 'f', 10);
QUrlQuery query;
query.addQueryItem(QStringLiteral("points"), points);
_queryMode = QueryModeCarpet;
_carpetStatsOnly = statsOnly;
_sendQuery(QStringLiteral("/carpet"), query);
}
void TerrainAirMapQuery::_sendQuery(const QString& path, const QUrlQuery& urlQuery)
{
QUrl url(QStringLiteral("https://api.airmap.com/elevation/v1/ele") + path);
qCDebug(TerrainQueryLog) << "_sendQuery" << url;
QNetworkRequest request(url);
QSslConfiguration sslConf = request.sslConfiguration();
sslConf.setPeerVerifyMode(QSslSocket::VerifyNone);
request.setSslConfiguration(sslConf);
QNetworkProxy tProxy;
tProxy.setType(QNetworkProxy::DefaultProxy);
_networkManager.setProxy(tProxy);
QNetworkReply* networkReply = _networkManager.get(request);
if (!networkReply) {
qCWarning(TerrainQueryLog) << "QNetworkManager::Get did not return QNetworkReply";
connect(networkReply, &QNetworkReply::finished, this, &TerrainAirMapQuery::_requestFinished);
connect(networkReply, &QNetworkReply::sslErrors, this, &TerrainAirMapQuery::_sslErrors);
connect(networkReply, QOverload<QNetworkReply::NetworkError>::of(&QNetworkReply::error), this, &TerrainAirMapQuery::_requestError);
}
void TerrainAirMapQuery::_requestError(QNetworkReply::NetworkError code)
{
QNetworkReply* reply = qobject_cast<QNetworkReply*>(QObject::sender());
if (code != QNetworkReply::NoError) {
qCWarning(TerrainQueryLog) << "_requestError error:url:data" << reply->error() << reply->url() << reply->readAll();
void TerrainAirMapQuery::_sslErrors(const QList<QSslError> &errors)
{
for (const auto &error : errors) {
qCWarning(TerrainQueryLog) << "SSL error: " << error.errorString();
const auto &certificate = error.certificate();
if (!certificate.isNull()) {
qCWarning(TerrainQueryLog) << "SSL Certificate problem: " << certificate.toText();
}
}
}
void TerrainAirMapQuery::_requestFinished(void)
{
QNetworkReply* reply = qobject_cast<QNetworkReply*>(QObject::sender());
if (reply->error() != QNetworkReply::NoError) {
qCWarning(TerrainQueryLog) << "_requestFinished error:url:data" << reply->error() << reply->url() << reply->readAll();
reply->deleteLater();
return;
}
QByteArray responseBytes = reply->readAll();
reply->deleteLater();
// Convert the response to Json
QJsonParseError parseError;
QJsonDocument responseJson = QJsonDocument::fromJson(responseBytes, &parseError);
if (parseError.error != QJsonParseError::NoError) {
qCWarning(TerrainQueryLog) << "_requestFinished unable to parse json:" << parseError.errorString();
return;
}
// Check airmap reponse status
QJsonObject rootObject = responseJson.object();
QString status = rootObject["status"].toString();
if (status != "success") {
qCWarning(TerrainQueryLog) << "_requestFinished status != success:" << status;
return;
}
// Send back data
const QJsonValue& jsonData = rootObject["data"];
qCDebug(TerrainQueryLog) << "_requestFinished success";
switch (_queryMode) {
case QueryModeCoordinates:
emit _parseCoordinateData(jsonData);
break;
case QueryModePath:
emit _parsePathData(jsonData);
break;
case QueryModeCarpet:
emit _parseCarpetData(jsonData);
break;
}
}
void TerrainAirMapQuery::_requestFailed(void)
{
switch (_queryMode) {
case QueryModeCoordinates:
emit coordinateHeightsReceived(false /* success */, QList<double>() /* heights */);
break;
case QueryModePath:
emit pathHeightsReceived(false /* success */, qQNaN() /* latStep */, qQNaN() /* lonStep */, QList<double>() /* heights */);
break;
case QueryModeCarpet:
emit carpetHeightsReceived(false /* success */, qQNaN() /* minHeight */, qQNaN() /* maxHeight */, QList<QList<double>>() /* carpet */);
break;
}
}
void TerrainAirMapQuery::_parseCoordinateData(const QJsonValue& coordinateJson)
{
QList<double> heights;
const QJsonArray& dataArray = coordinateJson.toArray();
for (int i = 0; i < dataArray.count(); i++) {
heights.append(dataArray[i].toDouble());
}
emit coordinateHeightsReceived(true /* success */, heights);
}
void TerrainAirMapQuery::_parsePathData(const QJsonValue& pathJson)
{
QJsonObject jsonObject = pathJson.toArray()[0].toObject();
QJsonArray stepArray = jsonObject["step"].toArray();
QJsonArray profileArray = jsonObject["profile"].toArray();
double latStep = stepArray[0].toDouble();
double lonStep = stepArray[1].toDouble();
QList<double> heights;
for (const QJsonValue& profileValue: profileArray) {
heights.append(profileValue.toDouble());
}
emit pathHeightsReceived(true /* success */, latStep, lonStep, heights);
}
void TerrainAirMapQuery::_parseCarpetData(const QJsonValue& carpetJson)
{
QJsonObject jsonObject = carpetJson.toArray()[0].toObject();
QJsonObject statsObject = jsonObject["stats"].toObject();
double minHeight = statsObject["min"].toDouble();
double maxHeight = statsObject["min"].toDouble();
QList<QList<double>> carpet;
if (!_carpetStatsOnly) {
QJsonArray carpetArray = jsonObject["carpet"].toArray();
for (int i=0; i<carpetArray.count(); i++) {
QJsonArray rowArray = carpetArray[i].toArray();
carpet.append(QList<double>());
for (int j=0; j<rowArray.count(); j++) {
double height = rowArray[j].toDouble();
carpet.last().append(height);
}
}
}
emit carpetHeightsReceived(true /*success*/, minHeight, maxHeight, carpet);
TerrainOfflineAirMapQuery::TerrainOfflineAirMapQuery(QObject* parent)
: TerrainQueryInterface(parent)
{
qCDebug(TerrainQueryVerboseLog) << "supportsSsl" << QSslSocket::supportsSsl() << "sslLibraryBuildVersionString" << QSslSocket::sslLibraryBuildVersionString();
}
void TerrainOfflineAirMapQuery::requestCoordinateHeights(const QList<QGeoCoordinate>& coordinates)
{
if (qgcApp()->runningUnitTests()) {
emit coordinateHeightsReceived(false, QList<double>());
if (coordinates.length() == 0) {
}
void TerrainOfflineAirMapQuery::requestPathHeights(const QGeoCoordinate& fromCoord, const QGeoCoordinate& toCoord)
{
if (qgcApp()->runningUnitTests()) {
emit pathHeightsReceived(false, qQNaN(), qQNaN(), QList<double>());
_terrainTileManager->addPathQuery(this, fromCoord, toCoord);
}
void TerrainOfflineAirMapQuery::requestCarpetHeights(const QGeoCoordinate& swCoord, const QGeoCoordinate& neCoord, bool statsOnly)
{
if (qgcApp()->runningUnitTests()) {
emit carpetHeightsReceived(false, qQNaN(), qQNaN(), QList<QList<double>>());
// TODO
Q_UNUSED(swCoord);
Q_UNUSED(neCoord);
Q_UNUSED(statsOnly);
qWarning() << "Carpet queries are currently not supported from offline air map data";
}
void TerrainOfflineAirMapQuery::_signalCoordinateHeights(bool success, QList<double> heights)
{
}
void TerrainOfflineAirMapQuery::_signalPathHeights(bool success, double latStep, double lonStep, const QList<double>& heights)
{
emit pathHeightsReceived(success, latStep, lonStep, heights);
}
void TerrainOfflineAirMapQuery::_signalCarpetHeights(bool success, double minHeight, double maxHeight, const QList<QList<double>>& carpet)
{
emit carpetHeightsReceived(success, minHeight, maxHeight, carpet);
}
TerrainTileManager::TerrainTileManager(void)
{
}
void TerrainTileManager::addCoordinateQuery(TerrainOfflineAirMapQuery* terrainQueryInterface, const QList<QGeoCoordinate>& coordinates)
qCDebug(TerrainQueryLog) << "TerrainTileManager::addCoordinateQuery count" << coordinates.count();
if (coordinates.length() > 0) {
qCDebug(TerrainQueryLog) << "TerrainTileManager::addPathQuery queue count" << _requestQueue.count();
QueuedRequestInfo_t queuedRequestInfo = { terrainQueryInterface, QueryMode::QueryModeCoordinates, 0, 0, coordinates };
_requestQueue.append(queuedRequestInfo);
return;
}
if (error) {
QList<double> noAltitudes;
qCWarning(TerrainQueryLog) << "addCoordinateQuery: signalling failure due to internal error";
terrainQueryInterface->_signalCoordinateHeights(false, noAltitudes);
} else {
qCDebug(TerrainQueryLog) << "addCoordinateQuery: All altitudes taken from cached data";
terrainQueryInterface->_signalCoordinateHeights(coordinates.count() == altitudes.count(), altitudes);
}
}
}
void TerrainTileManager::addPathQuery(TerrainOfflineAirMapQuery* terrainQueryInterface, const QGeoCoordinate &startPoint, const QGeoCoordinate &endPoint)
{
QList<QGeoCoordinate> coordinates;
double lat = startPoint.latitude();
double lon = startPoint.longitude();
double steps = ceil(endPoint.distanceTo(startPoint) / TerrainTile::terrainAltitudeSpacing);
double latDiff = endPoint.latitude() - lat;
double lonDiff = endPoint.longitude() - lon;
for (double i = 0.0; i <= steps; i = i + 1) {
coordinates.append(QGeoCoordinate(lat + latDiff * i / steps, lon + lonDiff * i / steps));
}
// We always have one too many and we always want the last one to be the endpoint
coordinates.last() = endPoint;
double latStep = coordinates[1].latitude() - coordinates[0].latitude();
double lonStep = coordinates[1].longitude() - coordinates[0].longitude();
qCDebug(TerrainQueryLog) << "TerrainTileManager::addPathQuery start:end:coordCount" << startPoint << endPoint << coordinates.count();
qCDebug(TerrainQueryLog) << "TerrainTileManager::addPathQuery queue count" << _requestQueue.count();
QueuedRequestInfo_t queuedRequestInfo = { terrainQueryInterface, QueryMode::QueryModePath, latStep, lonStep, coordinates };
_requestQueue.append(queuedRequestInfo);
return;
}
if (error) {
QList<double> noAltitudes;
qCWarning(TerrainQueryLog) << "addPathQuery: signalling failure due to internal error";
terrainQueryInterface->_signalPathHeights(false, latStep, lonStep, noAltitudes);
} else {
qCDebug(TerrainQueryLog) << "addPathQuery: All altitudes taken from cached data";
terrainQueryInterface->_signalPathHeights(coordinates.count() == altitudes.count(), latStep, lonStep, altitudes);
}
/// Either returns altitudes from cache or queues database request
/// @param[out] error true: altitude not returned due to error, false: altitudes returned
/// @return true: altitude returned (check error as well), false: database query queued (altitudes not returned)
bool TerrainTileManager::_getAltitudesForCoordinates(const QList<QGeoCoordinate>& coordinates, QList<double>& altitudes, bool& error)
for (const QGeoCoordinate& coordinate: coordinates) {
QString tileHash = _getTileHash(coordinate);
qCDebug(TerrainQueryLog) << "TerrainTileManager::_getAltitudesForCoordinates hash:coordinate" << tileHash << coordinate;
_tilesMutex.lock();
if (_tiles.contains(tileHash)) {
if (_tiles[tileHash].isIn(coordinate)) {
double elevation = _tiles[tileHash].elevation(coordinate);
error = true;
qCWarning(TerrainQueryLog) << "TerrainTileManager::_getAltitudesForCoordinates Internal Error: negative elevation in tile cache";
} else {
qCDebug(TerrainQueryLog) << "TerrainTileManager::_getAltitudesForCoordinates returning elevation from tile cache" << elevation;
}
altitudes.push_back(elevation);
} else {
qCWarning(TerrainQueryLog) << "TerrainTileManager::_getAltitudesForCoordinates Internal Error: coordinate not in tile region";
if (_state != State::Downloading) {
QNetworkRequest request = getQGCMapEngine()->urlFactory()->getTileURL("Airmap Elevation", getQGCMapEngine()->urlFactory()->long2tileX("Airmap Elevation",coordinate.longitude(), 1), getQGCMapEngine()->urlFactory()->lat2tileY("Airmap Elevation", coordinate.latitude(), 1), 1, &_networkManager);
qCDebug(TerrainQueryLog) << "TerrainTileManager::_getAltitudesForCoordinates query from database" << request.url();
QGeoTileSpec spec;
spec.setX(getQGCMapEngine()->urlFactory()->long2tileX("Airmap Elevation", coordinate.longitude(), 1));
spec.setY(getQGCMapEngine()->urlFactory()->lat2tileY("Airmap Elevation", coordinate.latitude(), 1));
spec.setZoom(1);
spec.setMapId(getQGCMapEngine()->urlFactory()->getIdFromType("Airmap Elevation"));
QGeoTiledMapReplyQGC* reply = new QGeoTiledMapReplyQGC(&_networkManager, request, spec);
connect(reply, &QGeoTiledMapReplyQGC::terrainDone, this, &TerrainTileManager::_terrainDone);
_state = State::Downloading;
}
_tilesMutex.unlock();
return false;
}
_tilesMutex.unlock();
}
return true;
}
void TerrainTileManager::_tileFailed(void)
{
for (const QueuedRequestInfo_t& requestInfo: _requestQueue) {
if (requestInfo.queryMode == QueryMode::QueryModeCoordinates) {
requestInfo.terrainQueryInterface->_signalCoordinateHeights(false, noAltitudes);
} else if (requestInfo.queryMode == QueryMode::QueryModePath) {
requestInfo.terrainQueryInterface->_signalPathHeights(false, requestInfo.latStep, requestInfo.lonStep, noAltitudes);
}
}
_requestQueue.clear();
}
void TerrainTileManager::_terrainDone(QByteArray responseBytes, QNetworkReply::NetworkError error)
{
QGeoTiledMapReplyQGC* reply = qobject_cast<QGeoTiledMapReplyQGC*>(QObject::sender());
_state = State::Idle;
if (!reply) {
qCWarning(TerrainQueryLog) << "Elevation tile fetched but invalid reply data type.";
return;
}
// remove from download queue
QGeoTileSpec spec = reply->tileSpec();
QString hash = QGCMapEngine::getTileHash("Airmap Elevation", spec.x(), spec.y(), spec.zoom());
// handle potential errors
if (error != QNetworkReply::NoError) {
qCWarning(TerrainQueryLog) << "Elevation tile fetching returned error (" << error << ")";
_tileFailed();
reply->deleteLater();
return;
}
if (responseBytes.isEmpty()) {
qCWarning(TerrainQueryLog) << "Error in fetching elevation tile. Empty response.";
_tileFailed();
reply->deleteLater();
return;
}
qCDebug(TerrainQueryLog) << "Received some bytes of terrain data: " << responseBytes.size();
Andreas Bircher
committed
TerrainTile* terrainTile = new TerrainTile(responseBytes);
if (terrainTile->isValid()) {
_tilesMutex.lock();
if (!_tiles.contains(hash)) {
_tiles.insert(hash, *terrainTile);
} else {
delete terrainTile;
}
_tilesMutex.unlock();
} else {
delete terrainTile;
qCWarning(TerrainQueryLog) << "Received invalid tile";
}
reply->deleteLater();
// now try to query the data again
for (int i = _requestQueue.count() - 1; i >= 0; i--) {
QueuedRequestInfo_t& requestInfo = _requestQueue[i];
if (_getAltitudesForCoordinates(requestInfo.coordinates, altitudes, error)) {
if (requestInfo.queryMode == QueryMode::QueryModeCoordinates) {
if (error) {
QList<double> noAltitudes;
qCWarning(TerrainQueryLog) << "_terrainDone(coordinateQuery): signalling failure due to internal error";
requestInfo.terrainQueryInterface->_signalCoordinateHeights(false, noAltitudes);
} else {
qCDebug(TerrainQueryLog) << "_terrainDone(coordinateQuery): All altitudes taken from cached data";
requestInfo.terrainQueryInterface->_signalCoordinateHeights(requestInfo.coordinates.count() == altitudes.count(), altitudes);
}
} else if (requestInfo.queryMode == QueryMode::QueryModePath) {
if (error) {
QList<double> noAltitudes;
qCWarning(TerrainQueryLog) << "_terrainDone(coordinateQuery): signalling failure due to internal error";
requestInfo.terrainQueryInterface->_signalPathHeights(false, requestInfo.latStep, requestInfo.lonStep, noAltitudes);
} else {
qCDebug(TerrainQueryLog) << "_terrainDone(coordinateQuery): All altitudes taken from cached data";
requestInfo.terrainQueryInterface->_signalPathHeights(requestInfo.coordinates.count() == altitudes.count(), requestInfo.latStep, requestInfo.lonStep, altitudes);
}
}
_requestQueue.removeAt(i);
}
}
}
QString TerrainTileManager::_getTileHash(const QGeoCoordinate& coordinate)
{
QString ret = QGCMapEngine::getTileHash(
"Airmap Elevation",
getQGCMapEngine()->urlFactory()->long2tileX("Airmap Elevation", coordinate.longitude(), 1),
getQGCMapEngine()->urlFactory()->lat2tileY("Airmap Elevation", coordinate.latitude(), 1),
1);
qCDebug(TerrainQueryVerboseLog) << "Computing unique tile hash for " << coordinate << ret;
return ret;
}
TerrainAtCoordinateBatchManager::TerrainAtCoordinateBatchManager(void)
{
_batchTimer.setSingleShot(true);
_batchTimer.setInterval(_batchTimeout);
connect(&_batchTimer, &QTimer::timeout, this, &TerrainAtCoordinateBatchManager::_sendNextBatch);
connect(&_terrainQuery, &TerrainQueryInterface::coordinateHeightsReceived, this, &TerrainAtCoordinateBatchManager::_coordinateHeights);
}
void TerrainAtCoordinateBatchManager::addQuery(TerrainAtCoordinateQuery* terrainAtCoordinateQuery, const QList<QGeoCoordinate>& coordinates)
{
if (coordinates.length() > 0) {
connect(terrainAtCoordinateQuery, &TerrainAtCoordinateQuery::destroyed, this, &TerrainAtCoordinateBatchManager::_queryObjectDestroyed);
QueuedRequestInfo_t queuedRequestInfo = { terrainAtCoordinateQuery, coordinates };
_requestQueue.append(queuedRequestInfo);
if (!_batchTimer.isActive()) {
_batchTimer.start();
}
}
}
void TerrainAtCoordinateBatchManager::_sendNextBatch(void)
{
qCDebug(TerrainQueryLog) << "TerrainAtCoordinateBatchManager::_sendNextBatch _state:_requestQueue.count:_sentRequests.count" << _stateToString(_state) << _requestQueue.count() << _sentRequests.count();
if (_state != State::Idle) {
// Waiting for last download the complete, wait some more
qCDebug(TerrainQueryLog) << "TerrainAtCoordinateBatchManager::_sendNextBatch waiting for current batch, restarting timer";
_batchTimer.start();
return;
}
if (_requestQueue.count() == 0) {
return;
}
_sentRequests.clear();
// Convert coordinates to point strings for json query
QList<QGeoCoordinate> coords;
for (const QueuedRequestInfo_t& requestInfo: _requestQueue) {
SentRequestInfo_t sentRequestInfo = { requestInfo.terrainAtCoordinateQuery, false, requestInfo.coordinates.count() };
_sentRequests.append(sentRequestInfo);
coords += requestInfo.coordinates;
requestQueueAdded++;
if (coords.count() > 50) {
break;
}
_requestQueue = _requestQueue.mid(requestQueueAdded);
qCDebug(TerrainQueryLog) << "TerrainAtCoordinateBatchManager::_sendNextBatch requesting next batch _state:_requestQueue.count:_sentRequests.count" << _stateToString(_state) << _requestQueue.count() << _sentRequests.count();
_state = State::Downloading;
_terrainQuery.requestCoordinateHeights(coords);
}
void TerrainAtCoordinateBatchManager::_batchFailed(void)
{
for (const SentRequestInfo_t& sentRequestInfo: _sentRequests) {
if (!sentRequestInfo.queryObjectDestroyed) {
disconnect(sentRequestInfo.terrainAtCoordinateQuery, &TerrainAtCoordinateQuery::destroyed, this, &TerrainAtCoordinateBatchManager::_queryObjectDestroyed);
sentRequestInfo.terrainAtCoordinateQuery->_signalTerrainData(false, noHeights);
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
}
}
_sentRequests.clear();
}
void TerrainAtCoordinateBatchManager::_queryObjectDestroyed(QObject* terrainAtCoordinateQuery)
{
// Remove/Mark deleted objects queries from queues
qCDebug(TerrainQueryLog) << "_TerrainAtCoordinateQueryDestroyed TerrainAtCoordinateQuery" << terrainAtCoordinateQuery;
int i = 0;
while (i < _requestQueue.count()) {
const QueuedRequestInfo_t& requestInfo = _requestQueue[i];
if (requestInfo.terrainAtCoordinateQuery == terrainAtCoordinateQuery) {
qCDebug(TerrainQueryLog) << "Removing deleted provider from _requestQueue index:terrainAtCoordinateQuery" << i << requestInfo.terrainAtCoordinateQuery;
_requestQueue.removeAt(i);
} else {
i++;
}
}
for (int i=0; i<_sentRequests.count(); i++) {
SentRequestInfo_t& sentRequestInfo = _sentRequests[i];
if (sentRequestInfo.terrainAtCoordinateQuery == terrainAtCoordinateQuery) {
qCDebug(TerrainQueryLog) << "Zombieing deleted provider from _sentRequests index:terrainAtCoordinateQuery" << sentRequestInfo.terrainAtCoordinateQuery;
sentRequestInfo.queryObjectDestroyed = true;
}
}
}
QString TerrainAtCoordinateBatchManager::_stateToString(State state)
{
switch (state) {
case State::Idle:
return QStringLiteral("Idle");
case State::Downloading:
return QStringLiteral("Downloading");
}
return QStringLiteral("State unknown");
}
void TerrainAtCoordinateBatchManager::_coordinateHeights(bool success, QList<double> heights)
{
_state = State::Idle;
qCDebug(TerrainQueryLog) << "TerrainAtCoordinateBatchManager::_coordinateHeights signalled success:count" << success << heights.count();
if (!success) {
_batchFailed();
return;
}
int currentIndex = 0;
for (const SentRequestInfo_t& sentRequestInfo: _sentRequests) {
if (!sentRequestInfo.queryObjectDestroyed) {
qCDebug(TerrainQueryVerboseLog) << "TerrainAtCoordinateBatchManager::_coordinateHeights returned TerrainCoordinateQuery:count" << sentRequestInfo.terrainAtCoordinateQuery << sentRequestInfo.cCoord;
disconnect(sentRequestInfo.terrainAtCoordinateQuery, &TerrainAtCoordinateQuery::destroyed, this, &TerrainAtCoordinateBatchManager::_queryObjectDestroyed);
QList<double> requestAltitudes = heights.mid(currentIndex, sentRequestInfo.cCoord);
sentRequestInfo.terrainAtCoordinateQuery->_signalTerrainData(true, requestAltitudes);
currentIndex += sentRequestInfo.cCoord;
}
}
_sentRequests.clear();
if (_requestQueue.count()) {
_batchTimer.start();
}
}
TerrainAtCoordinateQuery::TerrainAtCoordinateQuery(QObject* parent)
: QObject(parent)
{
}
void TerrainAtCoordinateQuery::requestData(const QList<QGeoCoordinate>& coordinates)
{
if (coordinates.length() == 0) {
return;
}
_TerrainAtCoordinateBatchManager->addQuery(this, coordinates);
}
void TerrainAtCoordinateQuery::_signalTerrainData(bool success, QList<double>& heights)
}
TerrainPathQuery::TerrainPathQuery(QObject* parent)
qRegisterMetaType<PathHeightInfo_t>();
connect(&_terrainQuery, &TerrainQueryInterface::pathHeightsReceived, this, &TerrainPathQuery::_pathHeights);
}
void TerrainPathQuery::requestData(const QGeoCoordinate& fromCoord, const QGeoCoordinate& toCoord)
{
_terrainQuery.requestPathHeights(fromCoord, toCoord);
void TerrainPathQuery::_pathHeights(bool success, double latStep, double lonStep, const QList<double>& heights)
PathHeightInfo_t pathHeightInfo;
pathHeightInfo.latStep = latStep;
pathHeightInfo.lonStep = lonStep;
pathHeightInfo.heights = heights;
emit terrainDataReceived(success, pathHeightInfo);
}
TerrainPolyPathQuery::TerrainPolyPathQuery(QObject* parent)
: QObject (parent)
, _curIndex (0)
{
connect(&_pathQuery, &TerrainPathQuery::terrainDataReceived, this, &TerrainPolyPathQuery::_terrainDataReceived);
}
void TerrainPolyPathQuery::requestData(const QVariantList& polyPath)
{
QList<QGeoCoordinate> path;
for (const QVariant& geoVar: polyPath) {
path.append(geoVar.value<QGeoCoordinate>());
void TerrainPolyPathQuery::requestData(const QList<QGeoCoordinate>& polyPath)
{
qCDebug(TerrainQueryLog) << "TerrainPolyPathQuery::requestData count" << polyPath.count();
// Kick off first request
_rgCoords = polyPath;
_curIndex = 0;
_pathQuery.requestData(_rgCoords[0], _rgCoords[1]);
}
void TerrainPolyPathQuery::_terrainDataReceived(bool success, const TerrainPathQuery::PathHeightInfo_t& pathHeightInfo)
{
qCDebug(TerrainQueryLog) << "TerrainPolyPathQuery::_terrainDataReceived success:_curIndex" << success << _curIndex;
if (!success) {
_rgPathHeightInfo.clear();
emit terrainDataReceived(false /* success */, _rgPathHeightInfo);
return;
}
_rgPathHeightInfo.append(pathHeightInfo);
if (++_curIndex >= _rgCoords.count() - 1) {
// We've finished all requests
qCDebug(TerrainQueryLog) << "TerrainPolyPathQuery::_terrainDataReceived complete";
emit terrainDataReceived(true /* success */, _rgPathHeightInfo);
} else {
_pathQuery.requestData(_rgCoords[_curIndex], _rgCoords[_curIndex+1]);
}
}
TerrainCarpetQuery::TerrainCarpetQuery(QObject* parent)
connect(&_terrainQuery, &TerrainQueryInterface::carpetHeightsReceived, this, &TerrainCarpetQuery::terrainDataReceived);
}
void TerrainCarpetQuery::requestData(const QGeoCoordinate& swCoord, const QGeoCoordinate& neCoord, bool statsOnly)
{
_terrainQuery.requestCarpetHeights(swCoord, neCoord, statsOnly);