diff --git a/src/QtLocationPlugin/QGeoMapReplyQGC.cpp b/src/QtLocationPlugin/QGeoMapReplyQGC.cpp index d95f1db5714dc16df3a43c1a97a45904c4e52735..b07ad399984b24b5b5eaa7a3426c03c0d213f558 100644 --- a/src/QtLocationPlugin/QGeoMapReplyQGC.cpp +++ b/src/QtLocationPlugin/QGeoMapReplyQGC.cpp @@ -51,6 +51,10 @@ #include #include #include +#include "TerrainTile.h" +#include +#include +#include int QGeoTiledMapReplyQGC::_requestCount = 0; @@ -121,8 +125,24 @@ QGeoTiledMapReplyQGC::networkReplyFinished() return; } QByteArray a = _reply->readAll(); - setMapImageData(a); QString format = getQGCMapEngine()->urlFactory()->getImageFormat((UrlFactory::MapType)tileSpec().mapId(), a); + + // convert "a" to binary in case we have elevation data + if ((UrlFactory::MapType)tileSpec().mapId() == UrlFactory::MapType::AirmapElevation) { + QJsonParseError parseError; + QJsonDocument json = QJsonDocument::fromJson(a, &parseError); + if (parseError.error != QJsonParseError::NoError) { + emit aborted(); + return; + } else { + a = TerrainTile::serialize(json); + if (a.isEmpty()) { + emit aborted(); + return; + } + } + } + setMapImageData(a); if(!format.isEmpty()) { setMapImageFormat(format); getQGCMapEngine()->cacheTile((UrlFactory::MapType)tileSpec().mapId(), tileSpec().x(), tileSpec().y(), tileSpec().zoom(), a, format); diff --git a/src/Terrain.cc b/src/Terrain.cc index 55dd81556929fb8e2d25a12e338f9b4454f84af7..4080eb2060b02f94e4b0e61445e4d92018343314 100644 --- a/src/Terrain.cc +++ b/src/Terrain.cc @@ -128,17 +128,7 @@ void TerrainBatchManager::_fetchedTile() // parse received data and insert into hash table QByteArray responseBytes = reply->mapImageData(); - QJsonParseError parseError; - QJsonDocument responseJson = QJsonDocument::fromJson(responseBytes, &parseError); - if (parseError.error != QJsonParseError::NoError) { - qCDebug(ElevationProviderLog) << "Could not parse terrain tile " << parseError.errorString(); - qCDebug(ElevationProviderLog) << responseBytes; - _tileFailed(); - reply->deleteLater(); - return; - } - - TerrainTile* terrainTile = new TerrainTile(responseJson); + TerrainTile* terrainTile = new TerrainTile(responseBytes); if (terrainTile->isValid()) { _tilesMutex.lock(); if (!_tiles.contains(hash)) { diff --git a/src/TerrainTile.cc b/src/TerrainTile.cc index c68e42ad79ae3121f02e43416addfc76140b81b5..e1f772ffa9edf1846397b3d0b44927a43bd592be 100644 --- a/src/TerrainTile.cc +++ b/src/TerrainTile.cc @@ -5,6 +5,7 @@ #include #include #include +#include QGC_LOGGING_CATEGORY(TerrainTileLog, "TerrainTileLog") @@ -42,15 +43,83 @@ TerrainTile::~TerrainTile() } } -TerrainTile::TerrainTile(QJsonDocument document) + +TerrainTile::TerrainTile(QByteArray byteArray) : _minElevation(-1.0) , _maxElevation(-1.0) , _avgElevation(-1.0) + , _data(NULL) + , _gridSizeLat(-1) + , _gridSizeLon(-1) , _isValid(false) { + QDataStream stream(byteArray); + + stream >> _southWest + >> _northEast + >> _minElevation + >> _maxElevation + >> _avgElevation + >> _gridSizeLat + >> _gridSizeLon; + + for (int i = 0; i < _gridSizeLat; i++) { + if (i == 0) { + _data = new double*[_gridSizeLat]; + for (int k = 0; k < _gridSizeLat; k++) { + _data[k] = new double[_gridSizeLon]; + } + } + for (int j = 0; j < _gridSizeLon; j++) { + stream >> _data[i][j]; + } + } + + _isValid = true; +} + + +bool TerrainTile::isIn(const QGeoCoordinate& coordinate) const +{ + if (!_isValid) { + qCDebug(TerrainTileLog) << "isIn requested, but tile not valid"; + return false; + } + bool ret = coordinate.latitude() >= _southWest.latitude() && coordinate.longitude() >= _southWest.longitude() && + coordinate.latitude() <= _northEast.latitude() && coordinate.longitude() <= _northEast.longitude(); + qCDebug(TerrainTileLog) << "Checking isIn: " << coordinate << " , in sw " << _southWest << " , ne " << _northEast << ": " << ret; + return ret; +} + +float TerrainTile::elevation(const QGeoCoordinate& coordinate) const +{ + if (_isValid) { + qCDebug(TerrainTileLog) << "elevation: " << coordinate << " , in sw " << _southWest << " , ne " << _northEast; + // Get the index at resolution of 1 arc second + int indexLat = _latToDataIndex(coordinate.latitude()); + int indexLon = _lonToDataIndex(coordinate.longitude()); + qCDebug(TerrainTileLog) << "indexLat:indexLon" << indexLat << indexLon << "elevation" << _data[indexLat][indexLon]; + return _data[indexLat][indexLon]; + } else { + qCDebug(TerrainTileLog) << "Asking for elevation, but no valid data."; + return -1.0; + } +} + +QGeoCoordinate TerrainTile::centerCoordinate(void) const +{ + return _southWest.atDistanceAndAzimuth(_southWest.distanceTo(_northEast) / 2.0, _southWest.azimuthTo(_northEast)); +} + +QByteArray TerrainTile::serialize(QJsonDocument document) +{ + QByteArray byteArray; + QIODevice::OpenMode writeonly = QIODevice::WriteOnly; + QDataStream stream(&byteArray, writeonly); if (!document.isObject()) { qCDebug(TerrainTileLog) << "Terrain tile json doc is no object"; - return; + QByteArray emptyArray; + return emptyArray; } QJsonObject rootObject = document.object(); @@ -61,12 +130,14 @@ TerrainTile::TerrainTile(QJsonDocument document) }; if (!JsonHelper::validateKeys(rootObject, rootVersionKeyInfoList, errorString)) { qCDebug(TerrainTileLog) << "Error in reading json: " << errorString; - return; + QByteArray emptyArray; + return emptyArray; } if (rootObject[_jsonStatusKey].toString() != "success") { qCDebug(TerrainTileLog) << "Invalid terrain tile."; - return; + QByteArray emptyArray; + return emptyArray; } const QJsonObject& dataObject = rootObject[_jsonDataKey].toObject(); QList dataVersionKeyInfoList = { @@ -76,7 +147,8 @@ TerrainTile::TerrainTile(QJsonDocument document) }; if (!JsonHelper::validateKeys(dataObject, dataVersionKeyInfoList, errorString)) { qCDebug(TerrainTileLog) << "Error in reading json: " << errorString; - return; + QByteArray emptyArray; + return emptyArray; } // Bounds @@ -87,18 +159,18 @@ TerrainTile::TerrainTile(QJsonDocument document) }; if (!JsonHelper::validateKeys(boundsObject, boundsVersionKeyInfoList, errorString)) { qCDebug(TerrainTileLog) << "Error in reading json: " << errorString; - return; + QByteArray emptyArray; + return emptyArray; } const QJsonArray& swArray = boundsObject[_jsonSouthWestKey].toArray(); const QJsonArray& neArray = boundsObject[_jsonNorthEastKey].toArray(); if (swArray.count() < 2 || neArray.count() < 2 ) { qCDebug(TerrainTileLog) << "Incomplete bounding location"; - return; + QByteArray emptyArray; + return emptyArray; } - _southWest.setLatitude(swArray[0].toDouble()); - _southWest.setLongitude(swArray[1].toDouble()); - _northEast.setLatitude(neArray[0].toDouble()); - _northEast.setLongitude(neArray[1].toDouble()); + stream << QGeoCoordinate(swArray[0].toDouble(), swArray[1].toDouble()); + stream << QGeoCoordinate(neArray[0].toDouble(), neArray[1].toDouble()); // Stats const QJsonObject& statsObject = dataObject[_jsonStatsKey].toObject(); @@ -109,70 +181,39 @@ TerrainTile::TerrainTile(QJsonDocument document) }; if (!JsonHelper::validateKeys(statsObject, statsVersionKeyInfoList, errorString)) { qCDebug(TerrainTileLog) << "Error in reading json: " << errorString; - return; + QByteArray emptyArray; + return emptyArray; } - _maxElevation = statsObject[_jsonMaxElevationKey].toInt(); - _minElevation = statsObject[_jsonMinElevationKey].toInt(); - _avgElevation = statsObject[_jsonAvgElevationKey].toInt(); + stream << statsObject[_jsonMaxElevationKey].toInt(); + stream << statsObject[_jsonMinElevationKey].toInt(); + stream << statsObject[_jsonAvgElevationKey].toDouble(); // Carpet const QJsonArray& carpetArray = dataObject[_jsonCarpetKey].toArray(); - _gridSizeLat = carpetArray.count(); + int gridSizeLat = carpetArray.count(); + stream << gridSizeLat; + int gridSizeLon = 0; qCDebug(TerrainTileLog) << "Received tile has size in latitude direction: " << carpetArray.count(); - for (int i = 0; i < _gridSizeLat; i++) { + for (int i = 0; i < gridSizeLat; i++) { const QJsonArray& row = carpetArray[i].toArray(); if (i == 0) { - _gridSizeLon = row.count(); + gridSizeLon = row.count(); + stream << gridSizeLon; qCDebug(TerrainTileLog) << "Received tile has size in longitued direction: " << row.count(); - if (_gridSizeLon > 0) { - _data = new float*[_gridSizeLat]; - } - for (int k = 0; k < _gridSizeLat; k++) { - _data[k] = new float[_gridSizeLon]; - } } - if (row.count() < _gridSizeLon) { - qCDebug(TerrainTileLog) << "Expected row array of " << _gridSizeLon << ", instead got " << row.count(); - return; + if (row.count() < gridSizeLon) { + qCDebug(TerrainTileLog) << "Expected row array of " << gridSizeLon << ", instead got " << row.count(); + QByteArray emptyArray; + return emptyArray; } - for (int j = 0; j < _gridSizeLon; j++) { - _data[i][j] = row[j].toDouble(); + for (int j = 0; j < gridSizeLon; j++) { + stream << row[j].toDouble(); } } - _isValid = true; -} -bool TerrainTile::isIn(const QGeoCoordinate& coordinate) const -{ - if (!_isValid) { - qCDebug(TerrainTileLog) << "isIn requested, but tile not valid"; - return false; - } - bool ret = coordinate.latitude() >= _southWest.latitude() && coordinate.longitude() >= _southWest.longitude() && - coordinate.latitude() <= _northEast.latitude() && coordinate.longitude() <= _northEast.longitude(); - qCDebug(TerrainTileLog) << "Checking isIn: " << coordinate << " , in sw " << _southWest << " , ne " << _northEast << ": " << ret; - return ret; + return byteArray; } -float TerrainTile::elevation(const QGeoCoordinate& coordinate) const -{ - if (_isValid) { - qCDebug(TerrainTileLog) << "elevation: " << coordinate << " , in sw " << _southWest << " , ne " << _northEast; - // Get the index at resolution of 1 arc second - int indexLat = _latToDataIndex(coordinate.latitude()); - int indexLon = _lonToDataIndex(coordinate.longitude()); - qCDebug(TerrainTileLog) << "indexLat:indexLon" << indexLat << indexLon << "elevation" << _data[indexLat][indexLon]; - return _data[indexLat][indexLon]; - } else { - qCDebug(TerrainTileLog) << "Asking for elevation, but no valid data."; - return -1.0; - } -} - -QGeoCoordinate TerrainTile::centerCoordinate(void) const -{ - return _southWest.atDistanceAndAzimuth(_southWest.distanceTo(_northEast) / 2.0, _southWest.azimuthTo(_northEast)); -} int TerrainTile::_latToDataIndex(double latitude) const { diff --git a/src/TerrainTile.h b/src/TerrainTile.h index 6f6254fc2c0cc02f8c746e5c83c9288ebd54584d..84de079f14209e78864912c76aaa9f461d3cd965 100644 --- a/src/TerrainTile.h +++ b/src/TerrainTile.h @@ -26,6 +26,13 @@ public: */ TerrainTile(QJsonDocument document); + /** + * Constructor from serialized elevation data (either from file or web) + * + * @param document + */ + TerrainTile(QByteArray byteArray); + /** * Check for whether a coordinate lies within this tile * @@ -77,6 +84,13 @@ public: */ QGeoCoordinate centerCoordinate(void) const; + /** + * Serialize data + * + * @return serialized data + */ + static QByteArray serialize(QJsonDocument document); + private: inline int _latToDataIndex(double latitude) const; inline int _lonToDataIndex(double longitude) const; @@ -84,11 +98,11 @@ private: QGeoCoordinate _southWest; /// South west corner of the tile QGeoCoordinate _northEast; /// North east corner of the tile - float _minElevation; /// Minimum elevation in tile - float _maxElevation; /// Maximum elevation in tile - float _avgElevation; /// Average elevation of the tile + int _minElevation; /// Minimum elevation in tile + int _maxElevation; /// Maximum elevation in tile + double _avgElevation; /// Average elevation of the tile - float** _data; /// 2D elevation data array + double** _data; /// 2D elevation data array int _gridSizeLat; /// data grid size in latitude direction int _gridSizeLon; /// data grid size in longitude direction bool _isValid; /// data loaded is valid