/**************************************************************************** * * (c) 2009-2020 QGROUNDCONTROL PROJECT * * QGroundControl is licensed according to the terms in the file * COPYING.md in the root of the source code directory. * ****************************************************************************/ #include "JsonHelper.h" #include "QGCQGeoCoordinate.h" #include "QmlObjectListModel.h" #include "MissionCommandList.h" #include "FactMetaData.h" #include "QGCApplication.h" #include #include #include #include #include #include #include const char* JsonHelper::jsonVersionKey = "version"; const char* JsonHelper::jsonGroundStationKey = "groundStation"; const char* JsonHelper::jsonGroundStationValue = "QGroundControl"; const char* JsonHelper::jsonFileTypeKey = "fileType"; const char* JsonHelper::_translateKeysKey = "translateKeys"; const char* JsonHelper::_arrayIDKeysKey = "_arrayIDKeys"; bool JsonHelper::validateRequiredKeys(const QJsonObject& jsonObject, const QStringList& keys, QString& errorString) { QString missingKeys; foreach(const QString& key, keys) { if (!jsonObject.contains(key)) { if (!missingKeys.isEmpty()) { missingKeys += QStringLiteral(", "); } missingKeys += key; } } if (missingKeys.count() != 0) { errorString = QObject::tr("The following required keys are missing: %1").arg(missingKeys); return false; } return true; } bool JsonHelper::_loadGeoCoordinate(const QJsonValue& jsonValue, bool altitudeRequired, QGeoCoordinate& coordinate, QString& errorString, bool geoJsonFormat) { if (!jsonValue.isArray()) { errorString = QObject::tr("value for coordinate is not array"); return false; } QJsonArray coordinateArray = jsonValue.toArray(); int requiredCount = altitudeRequired ? 3 : 2; if (coordinateArray.count() != requiredCount) { errorString = QObject::tr("Coordinate array must contain %1 values").arg(requiredCount); return false; } foreach(const QJsonValue& jsonValue, coordinateArray) { if (jsonValue.type() != QJsonValue::Double && jsonValue.type() != QJsonValue::Null) { errorString = QObject::tr("Coordinate array may only contain double values, found: %1").arg(jsonValue.type()); return false; } } if (geoJsonFormat) { coordinate = QGeoCoordinate(coordinateArray[1].toDouble(), coordinateArray[0].toDouble()); } else { coordinate = QGeoCoordinate(possibleNaNJsonValue(coordinateArray[0]), possibleNaNJsonValue(coordinateArray[1])); } if (altitudeRequired) { coordinate.setAltitude(possibleNaNJsonValue(coordinateArray[2])); } return true; } void JsonHelper::_saveGeoCoordinate(const QGeoCoordinate& coordinate, bool writeAltitude, QJsonValue& jsonValue, bool geoJsonFormat) { QJsonArray coordinateArray; if (geoJsonFormat) { coordinateArray << coordinate.longitude() << coordinate.latitude(); } else { coordinateArray << coordinate.latitude() << coordinate.longitude(); } if (writeAltitude) { coordinateArray << coordinate.altitude(); } jsonValue = QJsonValue(coordinateArray); } bool JsonHelper::loadGeoCoordinate(const QJsonValue& jsonValue, bool altitudeRequired, QGeoCoordinate& coordinate, QString& errorString, bool geoJsonFormat) { return _loadGeoCoordinate(jsonValue, altitudeRequired, coordinate, errorString, geoJsonFormat); } void JsonHelper::saveGeoCoordinate(const QGeoCoordinate& coordinate, bool writeAltitude, QJsonValue& jsonValue) { _saveGeoCoordinate(coordinate, writeAltitude, jsonValue, false /* geoJsonFormat */); } bool JsonHelper::loadGeoJsonCoordinate(const QJsonValue& jsonValue, bool altitudeRequired, QGeoCoordinate& coordinate, QString& errorString) { return _loadGeoCoordinate(jsonValue, altitudeRequired, coordinate, errorString, true /* geoJsonFormat */); } void JsonHelper::saveGeoJsonCoordinate(const QGeoCoordinate& coordinate, bool writeAltitude, QJsonValue& jsonValue) { _saveGeoCoordinate(coordinate, writeAltitude, jsonValue, true /* geoJsonFormat */); } bool JsonHelper::validateKeyTypes(const QJsonObject& jsonObject, const QStringList& keys, const QList& types, QString& errorString) { for (int i=0; i requiredKeys = { { jsonFileTypeKey, QJsonValue::String, true }, { jsonVersionKey, QJsonValue::Double, true }, }; if (!JsonHelper::validateKeys(jsonObject, requiredKeys, errorString)) { return false; } // Make sure file type is correct QString fileTypeValue = jsonObject[jsonFileTypeKey].toString(); if (fileTypeValue != expectedFileType) { errorString = QObject::tr("Incorrect file type key expected:%1 actual:%2").arg(expectedFileType).arg(fileTypeValue); return false; } // Version check version = jsonObject[jsonVersionKey].toInt(); if (version < minSupportedVersion) { errorString = QObject::tr("File version %1 is no longer supported").arg(version); return false; } if (version > maxSupportedVersion) { errorString = QObject::tr("File version %1 is newer than current supported version %2").arg(version).arg(maxSupportedVersion); return false; } return true; } bool JsonHelper::validateExternalQGCJsonFile(const QJsonObject& jsonObject, const QString& expectedFileType, int minSupportedVersion, int maxSupportedVersion, int& version, QString& errorString) { // Validate required keys QList requiredKeys = { { jsonGroundStationKey, QJsonValue::String, true }, }; if (!JsonHelper::validateKeys(jsonObject, requiredKeys, errorString)) { return false; } return validateInternalQGCJsonFile(jsonObject, expectedFileType, minSupportedVersion, maxSupportedVersion, version, errorString); } QStringList JsonHelper::_addDefaultLocKeys(QJsonObject& jsonObject) { QString translateKeys; QString fileType = jsonObject[jsonFileTypeKey].toString(); if (!fileType.isEmpty()) { if (fileType == MissionCommandList::qgcFileType) { if (jsonObject.contains(_translateKeysKey)) { translateKeys = jsonObject[_translateKeysKey].toString(); } else { translateKeys = "label,enumStrings,friendlyName,description,category"; jsonObject[_translateKeysKey] = translateKeys; } if (!jsonObject.contains(_arrayIDKeysKey)) { jsonObject[_arrayIDKeysKey] = "rawName,comment"; } } else if (fileType == FactMetaData::qgcFileType) { if (jsonObject.contains(_translateKeysKey)) { translateKeys = jsonObject[_translateKeysKey].toString(); } else { translateKeys = "shortDescription,longDescription,enumStrings"; jsonObject[_translateKeysKey] = "shortDescription,longDescription,enumStrings"; } if (!jsonObject.contains(_arrayIDKeysKey)) { jsonObject[_arrayIDKeysKey] = "name"; } } } return translateKeys.split(","); } QJsonObject JsonHelper::_translateObject(QJsonObject& jsonObject, const QString& translateContext, const QStringList& translateKeys) { for (const QString& key: jsonObject.keys()) { if (jsonObject[key].isString()) { QString locString = jsonObject[key].toString(); if (translateKeys.contains(key)) { QString disambiguation; QString disambiguationPrefix("#loc.disambiguation#"); if (locString.startsWith(disambiguationPrefix)) { locString = locString.right(locString.length() - disambiguationPrefix.length()); int commentEndIndex = locString.indexOf("#"); if (commentEndIndex != -1) { disambiguation = locString.left(commentEndIndex); locString = locString.right(locString.length() - disambiguation.length() - 1); } } QString xlatString = qgcApp()->qgcJSONTranslator().translate(translateContext.toUtf8().constData(), locString.toUtf8().constData(), disambiguation.toUtf8().constData()); if (!xlatString.isNull()) { jsonObject[key] = xlatString; } } } else if (jsonObject[key].isArray()) { QJsonArray childJsonArray = jsonObject[key].toArray(); jsonObject[key] = _translateArray(childJsonArray, translateContext, translateKeys); } else if (jsonObject[key].isObject()) { QJsonObject childJsonObject = jsonObject[key].toObject(); jsonObject[key] = _translateObject(childJsonObject, translateContext, translateKeys); } } return jsonObject; } QJsonArray JsonHelper::_translateArray(QJsonArray& jsonArray, const QString& translateContext, const QStringList& translateKeys) { for (int i=0; i& rgPoints, QString& errorString) { QVariantList rgVarPoints; if (!loadGeoCoordinateArray(jsonValue, altitudeRequired, rgVarPoints, errorString)) { return false; } rgPoints.clear(); for (int i=0; i()); } return true; } void JsonHelper::saveGeoCoordinateArray(const QVariantList& rgVarPoints, bool writeAltitude, QJsonValue& jsonValue) { QJsonArray rgJsonPoints; // Add all points to the array for (int i=0; i(), writeAltitude, jsonPoint); rgJsonPoints.append(jsonPoint); } jsonValue = rgJsonPoints; } void JsonHelper::saveGeoCoordinateArray(const QList& rgPoints, bool writeAltitude, QJsonValue& jsonValue) { QVariantList rgVarPoints; for (int i=0; i& keyInfo, QString& errorString) { QStringList keyList; QList typeList; for (int i=0; i(i)->coordinate(); QJsonValue jsonValue; JsonHelper::saveGeoCoordinate(vertex, false /* writeAltitude */, jsonValue); polygonArray.append(jsonValue); } } double JsonHelper::possibleNaNJsonValue(const QJsonValue& value) { if (value.type() == QJsonValue::Null) { return std::numeric_limits::quiet_NaN(); } else { return value.toDouble(); } }