TerrainTile.cc 6.5 KB
Newer Older
Andreas Bircher's avatar
Andreas Bircher committed
1 2 3 4 5 6 7 8 9
#include "TerrainTile.h"
#include "JsonHelper.h"

#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>

QGC_LOGGING_CATEGORY(TerrainTileLog, "TerrainTileLog")

10
const double TerrainTile::srtm1TileSize         = 0.025;
Andreas Bircher's avatar
Andreas Bircher committed
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
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 88 89 90 91 92 93
    }
    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
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 = round((coordinate.latitude() - _southWest.latitude()) * (gridSize - 1) / srtm1TileSize);
        int indexLon = round((coordinate.longitude() - _southWest.longitude()) * (gridSize - 1) / 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));
}