Commit bfdc40f1 authored by Andreas Bircher's avatar Andreas Bircher

Terrain tiles

parent c7a2e09b
......@@ -569,6 +569,8 @@ HEADERS += \
src/Settings/UnitsSettings.h \
src/Settings/VideoSettings.h \
src/Terrain.h \
src/TerrainCacheTileServer.h \
src/TerrainTile.h \
src/Vehicle/MAVLinkLogManager.h \
src/VehicleSetup/JoystickConfigController.h \
src/comm/LinkConfiguration.h \
......@@ -756,6 +758,8 @@ SOURCES += \
src/Settings/UnitsSettings.cc \
src/Settings/VideoSettings.cc \
src/Terrain.cc \
src/TerrainCacheTileServer.cc \
src/TerrainTile.cc\
src/Vehicle/MAVLinkLogManager.cc \
src/VehicleSetup/JoystickConfigController.cc \
src/comm/LinkConfiguration.cc \
......
......@@ -13,6 +13,8 @@
#include <QGeoCoordinate>
#include <QNetworkAccessManager>
#include "TerrainCacheTileServer.h"
/* usage example:
ElevationProvider *p = new ElevationProvider();
QList<QGeoCoordinate> coordinates;
......@@ -38,6 +40,13 @@ public:
*/
bool queryTerrainData(const QList<QGeoCoordinate>& coordinates);
/**
*
*
*
*/
bool cacheTerrainData(const QGeoCoordinate& southWest, const QGeoCoordinate& northEast);
signals:
void terrainData(bool success, QList<float> altitudes);
......
#include "TerrainCacheTileServer.h"
TerrainCacheTileServer::TerrainCacheTileServer()
{
}
#ifndef TERRAINCACHESERVER_H
#define TERRAINCACHESERVER_H
#include "TerrainTile.h"
class TerrainCacheTileServer
{
public:
TerrainCacheTileServer();
bool cacheTerrainData(const QGeoCoordinate& southWest, const QGeoCoordinate& northEast);
bool cached(const QGeoCoordinate& coord);
private:
QStringList _downloadQueue;
};
#endif // TERRAINCACHESERVER_H
#include "TerrainTile.h"
#include "JsonHelper.h"
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
QGC_LOGGING_CATEGORY(TerrainTileLog, "TerrainTileLog")
const char* TerrainTile::_jsonStatusKey = "status";
const char* TerrainTile::_jsonDataKey = "data";
const char* TerrainTile::_jsonBoundsKey = "bounds";
const char* TerrainTile::_jsonSouthWestKey = "sw";
const char* TerrainTile::_jsonNorthEastKey = "ne";
const char* TerrainTile::_jsonStatsKey = "stats";
const char* TerrainTile::_jsonMaxElevationKey = "max";
const char* TerrainTile::_jsonMinElevationKey = "min";
const char* TerrainTile::_jsonAvgElevationKey = "avg";
const char* TerrainTile::_jsonCarpetKey = "carpet";
TerrainTile::TerrainTile()
: _minElevation(-1.0)
, _maxElevation(-1.0)
, _avgElevation(-1.0)
, _isValid(false)
{
}
TerrainTile::~TerrainTile()
{
}
TerrainTile::TerrainTile(QJsonDocument doc)
: _minElevation(-1.0)
, _maxElevation(-1.0)
, _avgElevation(-1.0)
, _isValid(false)
{
if (!doc.isObject()) {
qCDebug(TerrainTileLog) << "Terrain tile json doc is no object";
return;
}
QJsonObject rootObject = doc.object();
QString errorString;
QList<JsonHelper::KeyValidateInfo> rootVersionKeyInfoList = {
{ _jsonStatusKey, QJsonValue::String, true },
{ _jsonDataKey, QJsonValue::String, true },
};
if (!JsonHelper::validateKeys(rootObject, rootVersionKeyInfoList, errorString)) {
qCDebug(TerrainTileLog) << "Error in reading json: " << errorString;
return false;
}
if (rootObject[_jsonStatusKey].toString() != "success") {
qCDebug(TerrainTileLog) << "Invalid terrain tile.";
return;
}
const QJsonObject& dataObject = rootObject[_jsonDataKey].toObject();
QList<JsonHelper::KeyValidateInfo> dataVersionKeyInfoList = {
{ _jsonBoundsKey, QJsonValue::Object, true },
{ _jsonStatsKey, QJsonValue::Object, true },
{ _jsonCarpetKey, QJsonValue::Array, true },
};
if (!JsonHelper::validateKeys(dataObject, dataVersionKeyInfoList, errorString)) {
qCDebug(TerrainTileLog) << "Error in reading json: " << errorString;
return false;
}
// Bounds
const QJsonObject& boundsObject = dataObject[_jsonBoundsKey].toObject();
QList<JsonHelper::KeyValidateInfo> boundsVersionKeyInfoList = {
{ _jsonSouthWestKey, QJsonValue::Array, true },
{ _jsonNorthEastKey, QJsonValue::Array, true },
};
if (!JsonHelper::validateKeys(boundsObject, boundsVersionKeyInfoList, errorString)) {
qCDebug(TerrainTileLog) << "Error in reading json: " << errorString;
return false;
}
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;
}
_southWest.setLatitude(swArray[0].toDouble());
_southWest.setLongitude(swArray[1].toDouble());
_northEast.setLatitude(neArray[0].toDouble());
_northEast.setLongitude(neArray[1].toDouble());
// Stats
const QJsonObject& statsObject = dataObject[_jsonBoundsKey].toObject();
QList<JsonHelper::KeyValidateInfo> statsVersionKeyInfoList = {
{ _jsonMaxElevationKey, QJsonValue::Double, true },
{ _jsonMinElevationKey, QJsonValue::Double, true },
{ _jsonAvgElevationKey, QJsonValue::Double, true },
};
if (!JsonHelper::validateKeys(statsObject, statsVersionKeyInfoList, errorString)) {
qCDebug(TerrainTileLog) << "Error in reading json: " << errorString;
return false;
}
_maxElevation = statsObject[_jsonMaxElevationKey].toInt();
_minElevation = statsObject[_jsonMinElevationKey].toInt();
_avgElevation = statsObject[_jsonAvgElevationKey].toInt();
// Carpet
const QJsonArray& carpetArray = dataObject[_jsonCarpetKey].toArray();
if (carpetArray.count() != _gridSize) {
qCDebug(TerrainTileLog) << "Expected array of " << _gridSize << ", instead got " << carpetArray.count();
return;
}
for (int i = 0; i < _gridSize; i++) {
const QJsonArray& row = carpetArray[i].toArray();
if (row.count() != _gridSize) {
qCDebug(TerrainTileLog) << "Expected row array of " << _gridSize << ", instead got " << row.count();
return;
}
for (int j = 0; j < _gridSize; j++) {
_data[i][j] = row[j].toDouble();
}
}
_isValid = true;
}
bool TerrainTile::isIn(QGeoCoordinate coord)
{
if (!_isValid) {
qCDebug(TerrainTileLog) << "isIn requested, but tile not valid";
return false;
}
bool ret = coord.latitude() >= _southWest.longitude() && coord.longitude() >= _southWest.longitude() &&
coord.latitude() <= _northEast.longitude() && coord.longitude() <= _northEast.longitude();
qCDebug(TerrainTileLog) << "Checking isIn: " << coord << " , in sw " << _southWest << " , ne " << _northEast << ": " << ret;
return ret;
}
float TerrainTile::elevation(const QGeoCoordinate& coord)
{
if (_isValid) {
qCDebug << "elevation: " << coord << " , in sw " << _southWest << " , ne " << _northEast;
// Get the index at resolution of 1 arc second
int indexLat = std::round((coord.latitude() - _southWest.latitude()) / _srtm1Increment);
int indexLon = std::round((coord.longitude() - _southWest.longitude()) / _srtm1Increment);
qCDebug << "indexLat:indexLon" << indexLat << indexLon; // TODO (birchera): Move this down to the next debug output, once this is all properly working.
Q_ASSERT(indexLat >= 0);
Q_ASSERT(indexLat < _gridSize);
Q_ASSERT(indexLon >= 0);
Q_ASSERT(indexLon < _gridSize);
qCDebug << "elevation" << _data[indexLat][indexLon];
return _data[indexLat][indexLon];
} else {
qCDebug(TerrainTileLog) << "Asking for elevation, but no valid data.";
return -1.0;
}
}
#ifndef TERRAINTILE_H
#define TERRAINTILE_H
#include "QGCLoggingCategory.h"
#include <QGeoCoordinate>
#define TERRAIN_TILE_SIZE 90
Q_DECLARE_LOGGING_CATEGORY(TerrainTileLog)
class TerrainTile
{
public:
TerrainTile();
~TerrainTile();
/**
* Constructor from json doc with elevation data (either from file or web)
*
* @param json doc
*/
TerrainTile(QJsonDocument doc);
/**
* Check for whether a coordinate lies within this tile
*
* @param coordinate
* @return true if within
*/
bool isIn(QGeoCoordinate coord);
/**
* Check whether valid data is loaded
*
* @return true if data is valid
*/
bool isValid(void) { return _isValid; }
/**
* Evaluates the elevation at the given coordinate
*
* @param coordinate
* @return elevation
*/
float elevation(const QGeoCoordinate& coord);
/**
* Accessor for the minimum elevation of the tile
*
* @return minimum elevation
*/
float minElevation(void) { return _minElevation; }
/**
* Accessor for the maximum elevation of the tile
*
* @return maximum elevation
*/
float maxElevation(void) { return _maxElevation; }
/**
* Accessor for the average elevation of the tile
*
* @return average elevation
*/
float avgElevation(void) { return _avgElevation; }
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
float _data[TERRAIN_TILE_SIZE][TERRAIN_TILE_SIZE]; /// elevation data
bool _isValid; /// data loaded is valid
static const int _gridSize = TERRAIN_TILE_SIZE; /// tile grid size in lat and lon
static const float _srtm1Increment = 1.0 / (60.0 * 60.0); /// grid spacing in degree
// Json keys
static const char* _jsonStatusKey;
static const char* _jsonDataKey;
static const char* _jsonBoundsKey;
static const char* _jsonSouthWestKey;
static const char* _jsonNorthEastKey;
static const char* _jsonStatsKey;
static const char* _jsonMaxElevationKey;
static const char* _jsonMinElevationKey;
static const char* _jsonAvgElevationKey;
static const char* _jsonCarpetKey;
};
#endif // TERRAINTILE_H
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment