TerrainTile.cc 8.45 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

#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
8
#include <QDataStream>
Andreas Bircher's avatar
Andreas Bircher committed
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26

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)
27 28 29
    , _data(NULL)
    , _gridSizeLat(-1)
    , _gridSizeLon(-1)
Andreas Bircher's avatar
Andreas Bircher committed
30 31 32 33 34 35 36
    , _isValid(false)
{

}

TerrainTile::~TerrainTile()
{
37 38 39 40
    if (_data) {
        for (int i = 0; i < _gridSizeLat; i++) {
            delete _data[i];
        }
Andreas Bircher's avatar
Andreas Bircher committed
41 42
        delete _data;
        _data = NULL;
43
    }
Andreas Bircher's avatar
Andreas Bircher committed
44 45
}

46 47

TerrainTile::TerrainTile(QByteArray byteArray)
Andreas Bircher's avatar
Andreas Bircher committed
48 49 50
    : _minElevation(-1.0)
    , _maxElevation(-1.0)
    , _avgElevation(-1.0)
51 52 53
    , _data(NULL)
    , _gridSizeLat(-1)
    , _gridSizeLon(-1)
Andreas Bircher's avatar
Andreas Bircher committed
54 55
    , _isValid(false)
{
56 57
    QDataStream stream(byteArray);

58 59 60 61 62 63 64 65 66 67 68 69
    double lat,lon;
    stream >> lat
            >> lon;
    _southWest.setLatitude(lat);
    _southWest.setLongitude(lon);
    stream >> lat
            >> lon;
    _northEast.setLatitude(lat);
    _northEast.setLongitude(lon);


    stream >> _minElevation
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
            >> _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));
}

123
QByteArray TerrainTile::serialize(QByteArray input)
124
{
125 126 127 128 129 130 131
    QJsonParseError parseError;
    QJsonDocument document = QJsonDocument::fromJson(input, &parseError);
    if (parseError.error != QJsonParseError::NoError) {
        QByteArray emptyArray;
        return emptyArray;
    }

132
    QByteArray byteArray;
133
    QDataStream stream(&byteArray, QIODevice::WriteOnly);
Andreas Bircher's avatar
Andreas Bircher committed
134
    if (!document.isObject()) {
Andreas Bircher's avatar
Andreas Bircher committed
135
        qCDebug(TerrainTileLog) << "Terrain tile json doc is no object";
136 137
        QByteArray emptyArray;
        return emptyArray;
Andreas Bircher's avatar
Andreas Bircher committed
138
    }
Andreas Bircher's avatar
Andreas Bircher committed
139
    QJsonObject rootObject = document.object();
Andreas Bircher's avatar
Andreas Bircher committed
140 141 142 143

    QString errorString;
    QList<JsonHelper::KeyValidateInfo> rootVersionKeyInfoList = {
        { _jsonStatusKey, QJsonValue::String, true },
144
        { _jsonDataKey, QJsonValue::Object, true },
Andreas Bircher's avatar
Andreas Bircher committed
145 146 147
    };
    if (!JsonHelper::validateKeys(rootObject, rootVersionKeyInfoList, errorString)) {
        qCDebug(TerrainTileLog) << "Error in reading json: " << errorString;
148 149
        QByteArray emptyArray;
        return emptyArray;
Andreas Bircher's avatar
Andreas Bircher committed
150 151 152 153
    }

    if (rootObject[_jsonStatusKey].toString() != "success") {
        qCDebug(TerrainTileLog) << "Invalid terrain tile.";
154 155
        QByteArray emptyArray;
        return emptyArray;
Andreas Bircher's avatar
Andreas Bircher committed
156 157 158 159 160 161 162 163 164
    }
    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;
165 166
        QByteArray emptyArray;
        return emptyArray;
Andreas Bircher's avatar
Andreas Bircher committed
167 168 169 170 171 172 173 174 175 176
    }

    // 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;
177 178
        QByteArray emptyArray;
        return emptyArray;
Andreas Bircher's avatar
Andreas Bircher committed
179 180 181 182 183
    }
    const QJsonArray& swArray = boundsObject[_jsonSouthWestKey].toArray();
    const QJsonArray& neArray = boundsObject[_jsonNorthEastKey].toArray();
    if (swArray.count() < 2 || neArray.count() < 2 ) {
        qCDebug(TerrainTileLog) << "Incomplete bounding location";
184 185
        QByteArray emptyArray;
        return emptyArray;
Andreas Bircher's avatar
Andreas Bircher committed
186
    }
187 188 189 190
    stream << swArray[0].toDouble();
    stream << swArray[1].toDouble();
    stream << neArray[0].toDouble();
    stream << neArray[1].toDouble();
Andreas Bircher's avatar
Andreas Bircher committed
191 192

    // Stats
193
    const QJsonObject& statsObject = dataObject[_jsonStatsKey].toObject();
Andreas Bircher's avatar
Andreas Bircher committed
194 195 196 197 198 199 200
    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;
201 202
        QByteArray emptyArray;
        return emptyArray;
Andreas Bircher's avatar
Andreas Bircher committed
203
    }
204 205 206
    stream << statsObject[_jsonMaxElevationKey].toInt();
    stream << statsObject[_jsonMinElevationKey].toInt();
    stream << statsObject[_jsonAvgElevationKey].toDouble();
Andreas Bircher's avatar
Andreas Bircher committed
207 208 209

    // Carpet
    const QJsonArray& carpetArray = dataObject[_jsonCarpetKey].toArray();
210 211 212
    int gridSizeLat = carpetArray.count();
    stream << gridSizeLat;
    int gridSizeLon = 0;
213
    qCDebug(TerrainTileLog) << "Received tile has size in latitude direction: " << carpetArray.count();
214
    for (int i = 0; i < gridSizeLat; i++) {
Andreas Bircher's avatar
Andreas Bircher committed
215
        const QJsonArray& row = carpetArray[i].toArray();
216
        if (i == 0) {
217 218
            gridSizeLon = row.count();
            stream << gridSizeLon;
219 220
            qCDebug(TerrainTileLog) << "Received tile has size in longitued direction: " << row.count();
        }
221 222 223 224
        if (row.count() < gridSizeLon) {
            qCDebug(TerrainTileLog) << "Expected row array of " << gridSizeLon << ", instead got " << row.count();
            QByteArray emptyArray;
            return emptyArray;
Andreas Bircher's avatar
Andreas Bircher committed
225
        }
226 227
        for (int j = 0; j < gridSizeLon; j++) {
            stream << row[j].toDouble();
Andreas Bircher's avatar
Andreas Bircher committed
228 229 230
        }
    }

231
    return byteArray;
Andreas Bircher's avatar
Andreas Bircher committed
232 233
}

234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251

int TerrainTile::_latToDataIndex(double latitude) const
{
    if (isValid() && _southWest.isValid() && _northEast.isValid()) {
        return qRound((latitude - _southWest.latitude()) / (_northEast.latitude() - _southWest.latitude()) * (_gridSizeLat - 1));
    } else {
        return -1;
    }
}

int TerrainTile::_lonToDataIndex(double longitude) const
{
    if (isValid() && _southWest.isValid() && _northEast.isValid()) {
        return qRound((longitude - _southWest.longitude()) / (_northEast.longitude() - _southWest.longitude()) * (_gridSizeLon - 1));
    } else {
        return -1;
    }
}