TerrainTile.cc 6.53 KB
Newer Older
Andreas Bircher's avatar
Andreas Bircher committed
1 2
#include "TerrainTile.h"
#include "JsonHelper.h"
3
#include "QGCMapEngine.h"
Andreas Bircher's avatar
Andreas Bircher committed
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34

#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()
{
}

Andreas Bircher's avatar
Andreas Bircher committed
35
TerrainTile::TerrainTile(QJsonDocument document)
Andreas Bircher's avatar
Andreas Bircher committed
36 37 38 39 40
    : _minElevation(-1.0)
    , _maxElevation(-1.0)
    , _avgElevation(-1.0)
    , _isValid(false)
{
Andreas Bircher's avatar
Andreas Bircher committed
41
    if (!document.isObject()) {
Andreas Bircher's avatar
Andreas Bircher committed
42 43 44
        qCDebug(TerrainTileLog) << "Terrain tile json doc is no object";
        return;
    }
Andreas Bircher's avatar
Andreas Bircher committed
45
    QJsonObject rootObject = document.object();
Andreas Bircher's avatar
Andreas Bircher committed
46 47 48 49

    QString errorString;
    QList<JsonHelper::KeyValidateInfo> rootVersionKeyInfoList = {
        { _jsonStatusKey, QJsonValue::String, true },
50
        { _jsonDataKey, QJsonValue::Object, true },
Andreas Bircher's avatar
Andreas Bircher committed
51 52 53
    };
    if (!JsonHelper::validateKeys(rootObject, rootVersionKeyInfoList, errorString)) {
        qCDebug(TerrainTileLog) << "Error in reading json: " << errorString;
Andreas Bircher's avatar
Andreas Bircher committed
54
        return;
Andreas Bircher's avatar
Andreas Bircher committed
55 56 57 58 59 60 61 62 63 64 65 66 67 68
    }

    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;
Andreas Bircher's avatar
Andreas Bircher committed
69
        return;
Andreas Bircher's avatar
Andreas Bircher committed
70 71 72 73 74 75 76 77 78 79
    }

    // 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;
Andreas Bircher's avatar
Andreas Bircher committed
80
        return;
Andreas Bircher's avatar
Andreas Bircher committed
81 82 83 84 85 86 87
    }
    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;
    }
Andreas Bircher's avatar
Andreas Bircher committed
88 89 90 91
    _southWest.setLatitude(swArray[0].toDouble());
    _southWest.setLongitude(swArray[1].toDouble());
    _northEast.setLatitude(neArray[0].toDouble());
    _northEast.setLongitude(neArray[1].toDouble());
Andreas Bircher's avatar
Andreas Bircher committed
92 93

    // Stats
94
    const QJsonObject& statsObject = dataObject[_jsonStatsKey].toObject();
Andreas Bircher's avatar
Andreas Bircher committed
95 96 97 98 99 100 101
    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;
Andreas Bircher's avatar
Andreas Bircher committed
102
        return;
Andreas Bircher's avatar
Andreas Bircher committed
103 104 105 106 107 108 109
    }
    _maxElevation = statsObject[_jsonMaxElevationKey].toInt();
    _minElevation = statsObject[_jsonMinElevationKey].toInt();
    _avgElevation = statsObject[_jsonAvgElevationKey].toInt();

    // Carpet
    const QJsonArray& carpetArray = dataObject[_jsonCarpetKey].toArray();
110 111
    if (carpetArray.count() < gridSize) { // TODO (birchera): We always get 91x91 points, figure out why and where the exact location of the elev values are.
        qCDebug(TerrainTileLog) << "Expected array of " << gridSize << ", instead got " << carpetArray.count();
Andreas Bircher's avatar
Andreas Bircher committed
112 113
        return;
    }
114
    for (int i = 0; i < gridSize; i++) {
Andreas Bircher's avatar
Andreas Bircher committed
115
        const QJsonArray& row = carpetArray[i].toArray();
116 117
        if (row.count() < gridSize) { // TODO (birchera): the same as above
            qCDebug(TerrainTileLog) << "Expected row array of " << gridSize << ", instead got " << row.count();
Andreas Bircher's avatar
Andreas Bircher committed
118 119
            return;
        }
120
        for (int j = 0; j < gridSize; j++) {
Andreas Bircher's avatar
Andreas Bircher committed
121 122 123 124 125 126
            _data[i][j] = row[j].toDouble();
        }
    }
    _isValid = true;
}

Andreas Bircher's avatar
Andreas Bircher committed
127
bool TerrainTile::isIn(const QGeoCoordinate& coordinate) const
Andreas Bircher's avatar
Andreas Bircher committed
128 129 130 131 132
{
    if (!_isValid) {
        qCDebug(TerrainTileLog) << "isIn requested, but tile not valid";
        return false;
    }
133 134
    bool ret = coordinate.latitude() >= _southWest.latitude() && coordinate.longitude() >= _southWest.longitude() &&
               coordinate.latitude() <= _northEast.latitude() && coordinate.longitude() <= _northEast.longitude();
Andreas Bircher's avatar
Andreas Bircher committed
135
    qCDebug(TerrainTileLog) << "Checking isIn: " << coordinate << " , in sw " << _southWest << " , ne " << _northEast << ": " << ret;
Andreas Bircher's avatar
Andreas Bircher committed
136 137 138
    return ret;
}

Andreas Bircher's avatar
Andreas Bircher committed
139
float TerrainTile::elevation(const QGeoCoordinate& coordinate) const
Andreas Bircher's avatar
Andreas Bircher committed
140 141
{
    if (_isValid) {
Andreas Bircher's avatar
Andreas Bircher committed
142
        qCDebug(TerrainTileLog) << "elevation: " << coordinate << " , in sw " << _southWest << " , ne " << _northEast;
Andreas Bircher's avatar
Andreas Bircher committed
143
        // Get the index at resolution of 1 arc second
Andreas Bircher's avatar
Andreas Bircher committed
144 145
        int indexLat = qRound(static_cast<qreal>((coordinate.latitude() - _southWest.latitude()) * (gridSize - 1) / QGCMapEngine::srtm1TileSize));
        int indexLon = qRound(static_cast<qreal>((coordinate.longitude() - _southWest.longitude()) * (gridSize - 1) / QGCMapEngine::srtm1TileSize));
Andreas Bircher's avatar
Andreas Bircher committed
146
        qCDebug(TerrainTileLog) << "indexLat:indexLon" << indexLat << indexLon; // TODO (birchera): Move this down to the next debug output, once this is all properly working.
Andreas Bircher's avatar
Andreas Bircher committed
147
        Q_ASSERT(indexLat >= 0);
148
        Q_ASSERT(indexLat < gridSize);
Andreas Bircher's avatar
Andreas Bircher committed
149
        Q_ASSERT(indexLon >= 0);
150
        Q_ASSERT(indexLon < gridSize);
Andreas Bircher's avatar
Andreas Bircher committed
151
        qCDebug(TerrainTileLog) << "elevation" << _data[indexLat][indexLon];
Andreas Bircher's avatar
Andreas Bircher committed
152 153 154 155 156 157
        return _data[indexLat][indexLon];
    } else {
        qCDebug(TerrainTileLog) << "Asking for elevation, but no valid data.";
        return -1.0;
    }
}
Andreas Bircher's avatar
Andreas Bircher committed
158 159 160 161 162

QGeoCoordinate TerrainTile::centerCoordinate(void) const
{
    return _southWest.atDistanceAndAzimuth(_southWest.distanceTo(_northEast) / 2.0, _southWest.azimuthTo(_northEast));
}