目录
项目中要使用QGC,还要做一些更改,感觉Qgc源码很多,又是一个开源项目,对于qt开发项目经验不足的我们来说实在是一个不可多得学习资料,所以决定花一些时间对源码进行注释和解读,这样也能更好的利用到项目中去。
话不多说,这就开始,我使用的项目版本是4.2.0.项目目录如下

对于项目下载与运行,我只简单说两句我遇到的问题。
有两种方式第一种就是最靠谱也是普遍的方式从github上下载,运行报错缺少什么就去下载什么。中间还有一个报错fatal error C1083: 无法打开包括文件: “nlohmann_json/include/nlohmann/json_fwd.hpp”: No such file or directory参考这里:qgroundcontrol二次开发环境搭建_天空宇的博客-CSDN博客_qgroundcontrol二次开发还有一种方案,去网络上下载人家已经给我们处理好的,这里提供下载地址: 链接:https://pan.baidu.com/s/1fHYx-3VCkRn4TgF_vbRHcg
提取码:pm52,编译过程中主要有两个问题,第一个是期间如果报错,请修改编译目录下的makefile中的wx为wx-。如果遇到视频播放问题,请修改VideoReceiver下的VideoReceiver.pri文件中的
对应gstreamer的路径修改为自己下载的路径。这里修改完成后项目就可以运行了。
下面我们开始对源码进行分析,这里我按照顺序进行,因为还不了解项目的整体结构。先对根目下src下的代码逐个分析,目录结构如下所示。

这个类主要是一个解析输入参数的类,首先传入一个结构体数组,此结构体保存了要记录的选项和参数,然后将用户输入和此结构体数组进行一对一的比对,将用户输入储存到结构体数组中。
- /// @brief Structure used to pass command line options to the ParseCmdLineOptions function.
- typedef struct {
- const char* optionStr; ///< Command line option, for example "--foo" 参数名称
- bool* optionFound; ///< If option is found this variable will be set to true 标识参数是否找到
- QString* optionArg; ///< Option has additional argument, form is option:arg 参数是否有对应的值
- } CmdLineOpt_t;
-
-
- /// @brief Implements a simple command line parser which sets booleans to true if the option is found.
- void ParseCmdLineOptions(int& argc, ///< count of arguments in argv
- char* argv[], ///< command line arguments
- CmdLineOpt_t* prgOpts, ///< command line options
- size_t cOpts, ///< count of command line options
- bool removeParsedOptions) ///< true: remove parsed option from argc/argv
- {
- // Start with all options off
- // 先将所有的option的find置为false
- for (size_t iOption=0; iOption
- *prgOpts[iOption].optionFound = false;
- }
-
- //遍历所有输入参数
- for (int iArg=1; iArg
- for (size_t iOption=0; iOption
- bool found = false;
-
- QString arg(argv[iArg]);
- QString optionStr(prgOpts[iOption].optionStr);
-
- //如果输入参数以设定的optionStr开头且忽略大小写 且含有:表示有额外参数
- if (arg.startsWith(QString("%1:").arg(optionStr), Qt::CaseInsensitive)) {
- found = true;
- //如果此选项有额外参数输入
- if (prgOpts[iOption].optionArg) {
- //则获取额外参数并赋值 .right取右边n个长度作为新的字符串
- *prgOpts[iOption].optionArg = arg.right(arg.length() - (optionStr.length() + 1));
- }
- }
- // 普通参数
- else if (arg.compare(optionStr, Qt::CaseInsensitive) == 0) {
- found = true;
- }
- if (found) {
- //如果有合适的匹配项
- *prgOpts[iOption].optionFound = true;
- //如果要删除已经解析过的参数
- if (removeParsedOptions) {
- for (int iShift=iArg; iShift
-1; iShift++) { - //删掉
- argv[iShift] = argv[iShift+1];
- }
- argc--;
- iArg--;
- }
- }
- }
- }
- }
二.JsonHelper
这个文件主要承担了项目中json文件的验证,加载,读取,设置,保存等操作。头文件中增加的注释。
- /****************************************************************************
- *
- * (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.
- *
- ****************************************************************************/
-
- #pragma once
-
- #include
- #include
- #include
- #include
-
- /// @file
- /// @author Don Gagne
-
- class QmlObjectListModel;
-
- /// @brief Json manipulation helper class.
- /// Primarily used for parsing and processing Fact metadata.
- class JsonHelper
- {
- //给非qt类添加多语言支持
- Q_DECLARE_TR_FUNCTIONS(JsonHelper)
-
- public:
- /// Determines is the specified file is a json file 确定指定的文件是否是json文件
- /// @return true: file is json, false: file is not json 输入参数是文件名
- static bool isJsonFile(const QString& fileName, ///< filename
- QJsonDocument& jsonDoc, ///< returned json document
- QString& errorString); ///< error on parse failure
-
- /// Determines is the specified data is a json file
- /// @return true: file is json, false: file is not json 参数是二进制
- static bool isJsonFile(const QByteArray& bytes, ///< json bytes
- QJsonDocument& jsonDoc, ///< returned json document
- QString& errorString); ///< error on parse failure
-
- /// Saves the standard file header the json object 保存标准的qgc jason头
- static void saveQGCJsonFileHeader(QJsonObject& jsonObject, ///< root json object
- const QString& fileType, ///< file type for file
- int version); ///< version number for file
-
- /// Validates the standard parts of an external QGC json file (Plan file, ...): 验证外部QGC json文件的标准部分
- /// jsonFileTypeKey - Required and checked to be equal to expected FileType 要求并检查等于期望的文件类型
- /// jsonVersionKey - Required and checked to be below supportedMajorVersion, supportedMinorVersion 要求并检查低于支持的主版本号和次版本号
- /// jsonGroundStationKey - Required and checked to be string type 要求并检查地面站的key为string类型
- /// @return false: validation failed, errorString set
- static bool validateExternalQGCJsonFile(const QJsonObject& jsonObject, ///< json object to validate 被检查对象
- const QString& expectedFileType, ///< correct file type for file 要求的文件类型
- int minSupportedVersion, ///< minimum supported version 小版本号
- int maxSupportedVersion, ///< maximum supported major version 大版本号
- int &version, ///< returned file version 返回的文件版本
- QString& errorString); ///< returned error string if validation fails 失败原因
-
- /// Validates the standard parts of a internal QGC json file (FactMetaData, ...):验证内部QGC json文件的标准部分,参数部分同上
- /// jsonFileTypeKey - Required and checked to be equal to expectedFileType
- /// jsonVersionKey - Required and checked to be below supportedMajorVersion, supportedMinorVersion
- /// jsonGroundStationKey - Required and checked to be string type
- /// @return false: validation failed, errorString set
- static bool validateInternalQGCJsonFile(const QJsonObject& jsonObject, ///< json object to validate
- const QString& expectedFileType, ///< correct file type for file
- int minSupportedVersion, ///< minimum supported version
- int maxSupportedVersion, ///< maximum supported major version
- int &version, ///< returned file version
- QString& errorString); ///< returned error string if validation fails
-
- /// Opens, validates and translates an internal QGC json file. 打开、验证和翻译内部QGC json文件。
- /// @return Json root object for file. Empty QJsonObject if error.
- static QJsonObject openInternalQGCJsonFile(const QString& jsonFilename, ///< Json file to open
- const QString& expectedFileType, ///< correct file type for file
- int minSupportedVersion, ///< minimum supported version
- int maxSupportedVersion, ///< maximum supported major version
- int &version, ///< returned file version
- QString& errorString); ///< returned error string if validation fails
-
- /// Validates that the specified keys are in the object 验证指定的键是否在对象中
- /// @return false: validation failed, errorString set
- static bool validateRequiredKeys(const QJsonObject& jsonObject, ///< json object to validate
- const QStringList& keys, ///< keys which are required to be present
- QString& errorString); ///< returned error string if validation fails
-
- /// Validates the types of specified keys are in the object 验证对象中指定键的类型
- /// @return false: validation failed, errorString set
- static bool validateKeyTypes(const QJsonObject& jsonObject, ///< json object to validate
- const QStringList& keys, ///< keys to validate
- const QList
& types, ///< required type for each key, QJsonValue::Null specifies double with possible NaN - QString& errorString); ///< returned error string if validation fails
-
- typedef struct {
- const char* key; ///< json key name
- QJsonValue::Type type; ///< required type for key, QJsonValue::Null specifies double with possible NaN
- bool required; ///< true: key must be present
- } KeyValidateInfo;
-
- static bool validateKeys(const QJsonObject& jsonObject, const QList
& keyInfo, QString& errorString) ; -
- /// Loads a QGeoCoordinate 加载位置坐标数据 存储格式为[ lat, lon, alt ]
- /// Stored as array [ lat, lon, alt ]
- /// @return false: validation failed
- static bool loadGeoCoordinate(const QJsonValue& jsonValue, ///< json value to load from
- bool altitudeRequired, ///< true: altitude must be specified
- QGeoCoordinate& coordinate, ///< returned QGeoCordinate
- QString& errorString, ///< returned error string if load failure
- bool geoJsonFormat = false); ///< if true, use [lon, lat], [lat, lon] otherwise
-
- /// Saves a QGeoCoordinate 保存位置坐标数据 存储格式为[ lat, lon, alt ]
- /// Stored as array [ lat, lon, alt ]
- static void saveGeoCoordinate(const QGeoCoordinate& coordinate, ///< QGeoCoordinate to save
- bool writeAltitude, ///< true: write altitude to json
- QJsonValue& jsonValue); ///< json value to save to
-
- /// Loads a QGeoCoordinate
- /// Stored as array [ lon, lat, alt ]
- /// @return false: validation failed
- static bool loadGeoJsonCoordinate(const QJsonValue& jsonValue, ///< json value to load from
- bool altitudeRequired, ///< true: altitude must be specified
- QGeoCoordinate& coordinate, ///< returned QGeoCordinate
- QString& errorString); ///< returned error string if load failure
-
- /// Saves a QGeoCoordinate
- /// Stored as array [ lon, lat, alt ]
- static void saveGeoJsonCoordinate(const QGeoCoordinate& coordinate, ///< QGeoCoordinate to save
- bool writeAltitude, ///< true: write altitude to json
- QJsonValue& jsonValue); ///< json value to save to
-
- /// Loads a polygon from an array 加载多边形
- static bool loadPolygon(const QJsonArray& polygonArray, ///< Array of coordinates
- QmlObjectListModel& list, ///< Empty list to add vertices to
- QObject* parent, ///< parent for newly allocated QGCQGeoCoordinates
- QString& errorString); ///< returned error string if load failure
-
- /// Loads a list of QGeoCoordinates from a json array 加载位置坐标数据数组形式
- /// @return false: validation failed
- static bool loadGeoCoordinateArray(const QJsonValue& jsonValue, ///< json value which contains points
- bool altitudeRequired, ///< true: altitude field must be specified
- QVariantList& rgVarPoints, ///< returned points
- QString& errorString); ///< returned error string if load failure
- static bool loadGeoCoordinateArray(const QJsonValue& jsonValue, ///< json value which contains points
- bool altitudeRequired, ///< true: altitude field must be specified
- QList
& rgPoints, ///< returned points - QString& errorString); ///< returned error string if load failure
-
- /// Saves a list of QGeoCoordinates to a json array 保存位置坐标数据数组形式
- static void saveGeoCoordinateArray(const QVariantList& rgVarPoints, ///< points to save
- bool writeAltitude, ///< true: write altitide value
- QJsonValue& jsonValue); ///< json value to save to
- static void saveGeoCoordinateArray(const QList
& rgPoints, ///< points to save - bool writeAltitude, ///< true: write altitide value
- QJsonValue& jsonValue); ///< json value to save to
-
- /// Saves a polygon to a json array 保存多边形数据
- static void savePolygon(QmlObjectListModel& list, ///< List which contains vertices
- QJsonArray& polygonArray); ///< Array to save into
-
- /// Returns NaN if the value is null, or if not, the double value 如果值为空,则返回NaN;如果不为空,则返回double值
- static double possibleNaNJsonValue(const QJsonValue& value);
-
- //一些字符串常量
- static const char* jsonVersionKey;
- static const char* jsonGroundStationKey;
- static const char* jsonGroundStationValue;
- static const char* jsonFileTypeKey;
-
- private:
- static QString _jsonValueTypeToString(QJsonValue::Type type);
- static bool _loadGeoCoordinate(const QJsonValue& jsonValue,
- bool altitudeRequired,
- QGeoCoordinate& coordinate,
- QString& errorString,
- bool geoJsonFormat);
- static void _saveGeoCoordinate(const QGeoCoordinate& coordinate,
- bool writeAltitude,
- QJsonValue& jsonValue,
- bool geoJsonFormat);
- static QStringList _addDefaultLocKeys(QJsonObject& jsonObject);
- static QJsonObject _translateRoot(QJsonObject& jsonObject, const QString& translateContext, const QStringList& translateKeys);
- static QJsonObject _translateObject(QJsonObject& jsonObject, const QString& translateContext, const QStringList& translateKeys);
- static QJsonArray _translateArray(QJsonArray& jsonArray, const QString& translateContext, const QStringList& translateKeys);
-
- static const char* _translateKeysKey;
- static const char* _arrayIDKeysKey;
- };
这里添加部分cc中的实现注释
- /****************************************************************************
- *
- * (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";
-
- /// 检查jsonObject是否包含keys中的键
- bool JsonHelper::validateRequiredKeys(const QJsonObject& jsonObject, const QStringList& keys, QString& errorString)
- {
- QString missingKeys;
-
- //遍历所有的keys
- foreach(const QString& key, keys) {
- //如果jsonObject不包含key
- if (!jsonObject.contains(key)) {
- //如果missingKeys不为空
- if (!missingKeys.isEmpty()) {
- //QStringLiteral是Qt5中新引入的一个用来从“字符串常量”创建QString对象的宏
- missingKeys += QStringLiteral(", ");
- }
- missingKeys += key;
- //key1, key2, key3...
- }
- }
-
- 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)
- {
- //要求jsonValue是一个数组形式
- if (!jsonValue.isArray()) {
- errorString = QObject::tr("value for coordinate is not array");
- return false;
- }
-
- //转换为json数组
- QJsonArray coordinateArray = jsonValue.toArray();
- //如果需要高度则是三个信息否则是两个
- int requiredCount = altitudeRequired ? 3 : 2;
- //判断如果json数组数量不等于requiredCount返回错误
- if (coordinateArray.count() != requiredCount) {
- errorString = QObject::tr("Coordinate array must contain %1 values").arg(requiredCount);
- return false;
- }
-
- //遍历json数组
- foreach(const QJsonValue& jsonValue, coordinateArray) {
- //要求json值的类型必须是double或者null
- 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;
- }
-
-
- ///将坐标轴转化为json数组
- 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
count(); i++) { - QString valueKey = keys[i];
- if (jsonObject.contains(valueKey)) {
- const QJsonValue& jsonValue = jsonObject[valueKey];
- if (jsonValue.type() == QJsonValue::Null && types[i] == QJsonValue::Double) {
- // Null type signals a NaN on a double value 空类型表示在double的值为NaN
- continue;
- }
- //判断类型不相等报错
- if (jsonValue.type() != types[i]) {
- errorString = QObject::tr("Incorrect value type - key:type:expected %1:%2:%3").arg(valueKey).arg(_jsonValueTypeToString(jsonValue.type())).arg(_jsonValueTypeToString(types[i]));
- return false;
- }
- }
- }
-
- return true;
- }
-
- bool JsonHelper::isJsonFile(const QByteArray& bytes, QJsonDocument& jsonDoc, QString& errorString)
- {
- QJsonParseError parseError;
-
- //从二进制流中加载json,转为QJsonDocument
- jsonDoc = QJsonDocument::fromJson(bytes, &parseError);
-
- if (parseError.error == QJsonParseError::NoError) {
- return true;
- } else {
- //转化失败
- int startPos = qMax(0, parseError.offset - 100);
- int length = qMin(bytes.count() - startPos, 200);
- qDebug() << QStringLiteral("Json read error '%1'").arg(bytes.mid(startPos, length).constData());
- errorString = parseError.errorString();
- return false;
- }
- }
-
- bool JsonHelper::isJsonFile(const QString& fileName, QJsonDocument& jsonDoc, QString& errorString)
- {
- QFile jsonFile(fileName);
- if (!jsonFile.open(QFile::ReadOnly)) {
- errorString = tr("File open failed: file:error %1 %2").arg(jsonFile.fileName()).arg(jsonFile.errorString());
- return false;
- }
- //转为二进制流
- QByteArray jsonBytes = jsonFile.readAll();
- jsonFile.close();
-
- return isJsonFile(jsonBytes, jsonDoc, errorString);
- }
-
- bool JsonHelper::validateInternalQGCJsonFile(const QJsonObject& jsonObject,
- const QString& expectedFileType,
- int minSupportedVersion,
- int maxSupportedVersion,
- int& version,
- QString& errorString)
- {
- // Validate required keys 验证所需的key
- QList
requiredKeys = { - { jsonFileTypeKey, QJsonValue::String, true },
- { jsonVersionKey, QJsonValue::Double, true },
- };
- //验证requiredKeys
- 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;
- //获取jsonObject中fileType对应的值
- QString fileType = jsonObject[jsonFileTypeKey].toString();
- if (!fileType.isEmpty()) {
- //如果fileType等于以下类型
- if (fileType == MissionCommandList::qgcFileType) {
- //如果jsonObject中包含“translateKeys”
- if (jsonObject.contains(_translateKeysKey)) {
- //获取对应的值
- translateKeys = jsonObject[_translateKeysKey].toString();
- } else {
- //不包含则添加
- translateKeys = "label,enumStrings,friendlyName,description,category";
- jsonObject[_translateKeysKey] = translateKeys;
- }
- //如果jsonObject不包含_arrayIDKeys
- 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)
- {
- //遍历jsonObject所有的keys
- for (const QString& key: jsonObject.keys()) {
- //如果keys是string类型
- if (jsonObject[key].isString()) {
- //转为string
- QString locString = jsonObject[key].toString();
- //translateKeys如果包含key
- if (translateKeys.contains(key)) {
- QString disambiguation;
- QString disambiguationPrefix("#loc.disambiguation#");
- //如果locString以#loc.disambiguation#开头
- if (locString.startsWith(disambiguationPrefix)) {
- //则截取#loc.disambiguation#之后的部分为新的locString
- locString = locString.right(locString.length() - disambiguationPrefix.length());
- int commentEndIndex = locString.indexOf("#");
- //如果locString含有#
- 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
count(); i++) { - QJsonObject childJsonObject = jsonArray[i].toObject();
- jsonArray[i] = _translateObject(childJsonObject, translateContext, translateKeys);
- }
-
- return jsonArray;
- }
-
- QJsonObject JsonHelper::_translateRoot(QJsonObject& jsonObject, const QString& translateContext, const QStringList& translateKeys)
- {
- return _translateObject(jsonObject, translateContext, translateKeys);
- }
-
- QJsonObject JsonHelper::openInternalQGCJsonFile(const QString& jsonFilename,
- const QString& expectedFileType,
- int minSupportedVersion,
- int maxSupportedVersion,
- int &version,
- QString& errorString)
- {
- QFile jsonFile(jsonFilename);
- if (!jsonFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
- errorString = tr("Unable to open file: '%1', error: %2").arg(jsonFilename).arg(jsonFile.errorString());
- return QJsonObject();
- }
-
- QByteArray bytes = jsonFile.readAll();
- jsonFile.close();
- QJsonParseError jsonParseError;
- QJsonDocument doc = QJsonDocument::fromJson(bytes, &jsonParseError);
- if (jsonParseError.error != QJsonParseError::NoError) {
- errorString = tr("Unable to parse json file: %1 error: %2 offset: %3").arg(jsonFilename).arg(jsonParseError.errorString()).arg(jsonParseError.offset);
- return QJsonObject();
- }
-
- if (!doc.isObject()) {
- errorString = tr("Root of json file is not object: %1").arg(jsonFilename);
- return QJsonObject();
- }
-
- QJsonObject jsonObject = doc.object();
- bool success = validateInternalQGCJsonFile(jsonObject, expectedFileType, minSupportedVersion, maxSupportedVersion, version, errorString);
- if (!success) {
- errorString = tr("Json file: '%1'. %2").arg(jsonFilename).arg(errorString);
- return QJsonObject();
- }
-
- QStringList translateKeys = _addDefaultLocKeys(jsonObject);
- QString context = QFileInfo(jsonFile).fileName();
- return _translateRoot(jsonObject, context, translateKeys);
- }
-
- void JsonHelper::saveQGCJsonFileHeader(QJsonObject& jsonObject,
- const QString& fileType,
- int version)
- {
- jsonObject[jsonGroundStationKey] = jsonGroundStationValue;
- jsonObject[jsonFileTypeKey] = fileType;
- jsonObject[jsonVersionKey] = version;
- }
-
- bool JsonHelper::loadGeoCoordinateArray(const QJsonValue& jsonValue,
- bool altitudeRequired,
- QVariantList& rgVarPoints,
- QString& errorString)
- {
- if (!jsonValue.isArray()) {
- errorString = QObject::tr("value for coordinate array is not array");
- return false;
- }
- QJsonArray rgJsonPoints = jsonValue.toArray();
-
- rgVarPoints.clear();
- for (int i=0; i
count(); i++) { - QGeoCoordinate coordinate;
-
- if (!JsonHelper::loadGeoCoordinate(rgJsonPoints[i], altitudeRequired, coordinate, errorString)) {
- return false;
- }
- rgVarPoints.append(QVariant::fromValue(coordinate));
- }
-
- return true;
- }
-
- bool JsonHelper::loadGeoCoordinateArray(const QJsonValue& jsonValue,
- bool altitudeRequired,
- QList
& rgPoints, - QString& errorString)
- {
- QVariantList rgVarPoints;
-
- if (!loadGeoCoordinateArray(jsonValue, altitudeRequired, rgVarPoints, errorString)) {
- return false;
- }
-
- rgPoints.clear();
- for (int i=0; i
count(); i++) { - rgPoints.append(rgVarPoints[i].value
()); - }
-
- 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
count(); i++) { - QJsonValue jsonPoint;
-
- JsonHelper::saveGeoCoordinate(rgVarPoints[i].value
(), writeAltitude, jsonPoint); - rgJsonPoints.append(jsonPoint);
- }
-
- jsonValue = rgJsonPoints;
- }
-
- void JsonHelper::saveGeoCoordinateArray(const QList
& rgPoints, - bool writeAltitude,
- QJsonValue& jsonValue)
- {
- QVariantList rgVarPoints;
-
- for (int i=0; i
count(); i++) { - rgVarPoints.append(QVariant::fromValue(rgPoints[i]));
- }
- return saveGeoCoordinateArray(rgVarPoints, writeAltitude, jsonValue);
- }
-
- bool JsonHelper::validateKeys(const QJsonObject& jsonObject, const QList
& keyInfo, QString& errorString) - {
- QStringList keyList;
- QList
typeList; -
- //遍历所有的keys
- for (int i=0; i
count(); i++) { - if (keyInfo[i].required) {
- //如果时候需要的key,则加入到keyList
- keyList.append(keyInfo[i].key);
- }
- }
- // 检查jsonObject是否包含keyList中的键
- if (!validateRequiredKeys(jsonObject, keyList, errorString)) {
- return false;
- }
-
- keyList.clear();
- for (int i=0; i
count(); i++) { - keyList.append(keyInfo[i].key);
- typeList.append(keyInfo[i].type);
- }
- //判断键对应的类型是否正确
- return validateKeyTypes(jsonObject, keyList, typeList, errorString);
- }
-
- QString JsonHelper::_jsonValueTypeToString(QJsonValue::Type type)
- {
- const struct {
- QJsonValue::Type type;
- const char* string;
- } rgTypeToString[] = {
- { QJsonValue::Null, "NULL" },
- { QJsonValue::Bool, "Bool" },
- { QJsonValue::Double, "Double" },
- { QJsonValue::String, "String" },
- { QJsonValue::Array, "Array" },
- { QJsonValue::Object, "Object" },
- { QJsonValue::Undefined, "Undefined" },
- };
-
- for (size_t i=0; i<sizeof(rgTypeToString)/sizeof(rgTypeToString[0]); i++) {
- if (type == rgTypeToString[i].type) {
- return rgTypeToString[i].string;
- }
- }
-
- return QObject::tr("Unknown type: %1").arg(type);
- }
-
- bool JsonHelper::loadPolygon(const QJsonArray& polygonArray, QmlObjectListModel& list, QObject* parent, QString& errorString)
- {
- for (int i=0; i
count(); i++) { - const QJsonValue& pointValue = polygonArray[i];
-
- QGeoCoordinate pointCoord;
- if (!JsonHelper::loadGeoCoordinate(pointValue, false /* altitudeRequired */, pointCoord, errorString, true)) {
- list.clearAndDeleteContents();
- return false;
- }
- list.append(new QGCQGeoCoordinate(pointCoord, parent));
- }
-
- return true;
- }
-
- void JsonHelper::savePolygon(QmlObjectListModel& list, QJsonArray& polygonArray)
- {
- for (int i=0; i
count(); i++) { - QGeoCoordinate vertex = list.value
(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<double>::quiet_NaN();
- } else {
- return value.toDouble();
- }
- }
三.KMLDomDocument
此类用户将任务转为kml文件。
- /****************************************************************************
- *
- * (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 "KMLDomDocument.h"
- #include "QGCPalette.h"
- #include "QGCApplication.h"
- #include "MissionCommandTree.h"
- #include "MissionCommandUIInfo.h"
- #include "FactMetaData.h"
-
- #include
- #include
-
- const char* KMLDomDocument::balloonStyleName = "BalloonStyle";
-
- KMLDomDocument::KMLDomDocument(const QString& name)
- {
- //生成kmlheader
- QDomProcessingInstruction header = createProcessingInstruction(QStringLiteral("xml"), QStringLiteral("version=\"1.0\" encoding=\"UTF-8\""));
- appendChild(header);
-
- //生成节点
- QDomElement kmlElement = createElement(QStringLiteral("kml"));
- kmlElement.setAttribute(QStringLiteral("xmlns"), "http://www.opengis.net/kml/2.2");
-
- //生成主节点
- _rootDocumentElement = createElement(QStringLiteral("Document"));
- kmlElement.appendChild(_rootDocumentElement);
- appendChild(kmlElement);
-
- //向主节点中添加name和open项
- addTextElement(_rootDocumentElement, "name", name);
- addTextElement(_rootDocumentElement, "open", "1");
-
- _addStandardStyles();
- }
-
- ///将坐标对象转为字符串
- QString KMLDomDocument::kmlCoordString(const QGeoCoordinate& coord)
- {
- double altitude = qIsNaN(coord.altitude() ) ? 0 : coord.altitude();
- return QStringLiteral("%1,%2,%3").arg(QString::number(coord.longitude(), 'f', 7)).arg(QString::number(coord.latitude(), 'f', 7)).arg(QString::number(altitude, 'f', 2));
- }
-
- //将颜色信息转为字符串
- QString KMLDomDocument::kmlColorString (const QColor& color, double opacity)
- {
- return QStringLiteral("%1%2%3%4").arg(static_cast<int>(255.0 * opacity), 2, 16, QChar('0')).arg(color.blue(), 2, 16, QChar('0')).arg(color.green(), 2, 16, QChar('0')).arg(color.red(), 2, 16, QChar('0'));
- }
-
- //给kml添加style标签
- void KMLDomDocument::_addStandardStyles(void)
- {
- QGCPalette palette;
-
- QDomElement styleElementForBalloon = createElement("Style");
- styleElementForBalloon.setAttribute("id", balloonStyleName);
- QDomElement balloonStyleElement = createElement("BalloonStyle");
- addTextElement(balloonStyleElement, "text", "$[description]");
- styleElementForBalloon.appendChild(balloonStyleElement);
- _rootDocumentElement.appendChild(styleElementForBalloon);
- }
-
- void KMLDomDocument::addTextElement(QDomElement& parentElement, const QString &name, const QString &value)
- {
- QDomElement textElement = createElement(name);
- textElement.appendChild(createTextNode(value));
- parentElement.appendChild(textElement);
- }
-
- //添加lookat信息
- void KMLDomDocument::addLookAt(QDomElement& parentElement, const QGeoCoordinate& coord)
- {
- QDomElement lookAtElement = createElement("LookAt");
- addTextElement(lookAtElement, "latitude", QString::number(coord.latitude(), 'f', 7));
- addTextElement(lookAtElement, "longitude", QString::number(coord.longitude(), 'f', 7));
- addTextElement(lookAtElement, "altitude", QString::number(coord.longitude(), 'f', 2));
- addTextElement(lookAtElement, "heading", "-100");
- addTextElement(lookAtElement, "tilt", "45");
- addTextElement(lookAtElement, "range", "2500");
- parentElement.appendChild(lookAtElement);
- }
-
- //添加标记点标签
- QDomElement KMLDomDocument::addPlacemark(const QString& name, bool visible)
- {
- QDomElement placemarkElement = createElement("Placemark");
- _rootDocumentElement.appendChild(placemarkElement);
-
- addTextElement(placemarkElement, "name", name);
- addTextElement(placemarkElement, "visibility", visible ? "1" : "0");
-
- return placemarkElement;
- }
-
- void KMLDomDocument::appendChildToRoot(const QDomNode& child)
- {
- _rootDocumentElement.appendChild(child);
- }
四.ShapeFileHelper
这个文件提供了判断文件是否是kml类型并从kml文件中获取shapetype的功能,头文件中做了注释,cc文件中没有需要解释的地方。
- /****************************************************************************
- *
- * (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.
- *
- ****************************************************************************/
-
- #pragma once
-
- #include
- #include
- #include
- #include
-
- /// Routines for loading polygons or polylines from KML or SHP files.
- class ShapeFileHelper : public QObject
- {
- Q_OBJECT
-
- public:
- enum ShapeType {
- Polygon, //多边形
- Polyline, //多线段
- Error
- };
- Q_ENUM(ShapeType)
-
- //关于Q_PROPERTY https://www.cnblogs.com/wanghongyang/p/15233642.html
- ///< File filter list for load/save KML file dialogs 载入/保存KML文件对话框的文件筛选器列表
- Q_PROPERTY(QStringList fileDialogKMLFilters READ fileDialogKMLFilters CONSTANT)
- ///< File filter list for load/save shape file dialogs 加载/保存形状文件对话框的文件筛选器列表
- Q_PROPERTY(QStringList fileDialogKMLOrSHPFilters READ fileDialogKMLOrSHPFilters CONSTANT)
-
- // 关于Q_INVOKABLE https://blog.csdn.net/qq78442761/article/details/109861560
- /// Loads the file and returns shape type and error string in a variant array. 加载文件并在变量数组中返回形状类型和错误字符串
- /// ShapeType is in index 0, error string is in index 1. ShapeType在索引0,错误字符串在索引1。
- Q_INVOKABLE static QVariantList determineShapeType(const QString& file);
-
- QStringList fileDialogKMLFilters(void) const;
- QStringList fileDialogKMLOrSHPFilters(void) const;
-
- static ShapeType determineShapeType(const QString& file, QString& errorString);
- static bool loadPolygonFromFile(const QString& file, QList
& vertices, QString& errorString) ; - static bool loadPolylineFromFile(const QString& file, QList
& coords, QString& errorString) ; -
- private:
- static bool _fileIsKML(const QString& file, QString& errorString);
-
- static const char* _errorPrefix;
- };
cc文件
- ShapeFileHelper::ShapeType ShapeFileHelper::determineShapeType(const QString& file, QString& errorString)
- {
- ShapeType shapeType = Error;
-
- errorString.clear();
-
- //判断是合法的kml文件
- bool fileIsKML = _fileIsKML(file, errorString);
- if (errorString.isEmpty()) {
- if (fileIsKML) {
- //判断file的类型选择不同的方式去加载数据
- shapeType = KMLHelper::determineShapeType(file, errorString);
- } else {
- shapeType = SHPFileHelper::determineShapeType(file, errorString);
- }
- }
-
- return shapeType;
- }
五.SHPFileHelper
和第四个类似,此类用于处理shp类型文件的读取。对cc文件添加了部分注释
- /****************************************************************************
- *
- * (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 "SHPFileHelper.h"
- #include "QGCGeo.h"
-
- #include
- #include
- #include
- #include
-
- const char* SHPFileHelper::_errorPrefix = QT_TR_NOOP("SHP file load failed. %1");
-
- /// Validates the specified SHP file is truly a SHP file and is in the format we understand.
- /// 验证指定的SHP文件确实是一个SHP文件,并且是我们所理解的格式
- /// @param utmZone[out] Zone for UTM shape, 0 for lat/lon shape
- /// @param utmSouthernHemisphere[out] true/false for UTM hemisphere
- /// @return true: Valid supported SHP file found, false: Invalid or unsupported file found
- bool SHPFileHelper::_validateSHPFiles(const QString& shpFile, int* utmZone, bool* utmSouthernHemisphere, QString& errorString)
- {
- *utmZone = 0;
- errorString.clear();
-
- if (shpFile.endsWith(QStringLiteral(".shp"))) {
- //将文件重新命名并添加后缀.prj
- QString prjFilename = shpFile.left(shpFile.length() - 4) + QStringLiteral(".prj");
- QFile prjFile(prjFilename);
- if (prjFile.exists()) {
- //如果文件存在,以只读方式打开
- if (prjFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
- //以文件流方式获取文件内容
- QTextStream strm(&prjFile);
- QString line = strm.readLine();
- if (line.startsWith(QStringLiteral("GEOGCS[\"GCS_WGS_1984\","))) {
- *utmZone = 0;
- *utmSouthernHemisphere = false;
- } else if (line.startsWith(QStringLiteral("PROJCS[\"WGS_1984_UTM_Zone_"))) {
- //利用正则表达式
- QRegularExpression regEx(QStringLiteral("^PROJCS\\[\"WGS_1984_UTM_Zone_(\\d+){1,2}([NS]{1})"));
- QRegularExpressionMatch regExMatch = regEx.match(line);
- QStringList rgCapture = regExMatch.capturedTexts();
- if (rgCapture.count() == 3) {
- //获取对应信息
- int zone = rgCapture[1].toInt();
- if (zone >= 1 && zone <= 60) {
- *utmZone = zone;
- *utmSouthernHemisphere = rgCapture[2] == QStringLiteral("S");
- }
- }
- if (*utmZone == 0) {
- errorString = QString(_errorPrefix).arg(tr("UTM projection is not in supported format. Must be PROJCS[\"WGS_1984_UTM_Zone_##N/S"));
- }
- } else {
- errorString = QString(_errorPrefix).arg(tr("Only WGS84 or UTM projections are supported."));
- }
- } else {
- errorString = QString(_errorPrefix).arg(tr("PRJ file open failed: %1").arg(prjFile.errorString()));
- }
- } else {
- errorString = QString(_errorPrefix).arg(tr("File not found: %1").arg(prjFilename));
- }
- } else {
- errorString = QString(_errorPrefix).arg(tr("File is not a .shp file: %1").arg(shpFile));
- }
-
- return errorString.isEmpty();
- }
-
- /// @param utmZone[out] Zone for UTM shape, 0 for lat/lon shape
- /// @param utmSouthernHemisphere[out] true/false for UTM hemisphere
- SHPHandle SHPFileHelper::_loadShape(const QString& shpFile, int* utmZone, bool* utmSouthernHemisphere, QString& errorString)
- {
- SHPHandle shpHandle = Q_NULLPTR;
-
- errorString.clear();
-
- //如果验证成功
- if (_validateSHPFiles(shpFile, utmZone, utmSouthernHemisphere, errorString)) {
- if (!(shpHandle = SHPOpen(shpFile.toUtf8(), "rb"))) {
- errorString = QString(_errorPrefix).arg(tr("SHPOpen failed."));
- }
- }
-
- return shpHandle;
- }
-
- ShapeFileHelper::ShapeType SHPFileHelper::determineShapeType(const QString& shpFile, QString& errorString)
- {
- ShapeFileHelper::ShapeType shapeType = ShapeFileHelper::Error;
-
- errorString.clear();
-
- int utmZone;
- bool utmSouthernHemisphere;
- SHPHandle shpHandle = SHPFileHelper::_loadShape(shpFile, &utmZone, &utmSouthernHemisphere, errorString);
- if (errorString.isEmpty()) {
- int cEntities, type;
-
- //获取shp信息
- SHPGetInfo(shpHandle, &cEntities /* pnEntities */, &type, Q_NULLPTR /* padfMinBound */, Q_NULLPTR /* padfMaxBound */);
- qDebug() << "SHPGetInfo" << shpHandle << cEntities << type;
- if (cEntities != 1) {
- errorString = QString(_errorPrefix).arg(tr("More than one entity found."));
- } else if (type == SHPT_POLYGON) {
- shapeType = ShapeFileHelper::Polygon;
- } else {
- errorString = QString(_errorPrefix).arg(tr("No supported types found."));
- }
- }
-
- SHPClose(shpHandle);
-
- return shapeType;
- }
-
- bool SHPFileHelper::loadPolygonFromFile(const QString& shpFile, QList
& vertices, QString& errorString) - {
- int utmZone = 0;
- bool utmSouthernHemisphere;
- double vertexFilterMeters = 5;
- SHPHandle shpHandle = Q_NULLPTR;
- SHPObject* shpObject = Q_NULLPTR;
-
- errorString.clear();
- vertices.clear();
-
- shpHandle = SHPFileHelper::_loadShape(shpFile, &utmZone, &utmSouthernHemisphere, errorString);
- if (!errorString.isEmpty()) {
- goto Error;
- }
-
- int cEntities, shapeType;
- SHPGetInfo(shpHandle, &cEntities, &shapeType, Q_NULLPTR /* padfMinBound */, Q_NULLPTR /* padfMaxBound */);
- if (shapeType != SHPT_POLYGON) {
- errorString = QString(_errorPrefix).arg(tr("File does not contain a polygon."));
- goto Error;
- }
-
- shpObject = SHPReadObject(shpHandle, 0);
- if (shpObject->nParts != 1) {
- errorString = QString(_errorPrefix).arg(tr("Only single part polygons are supported."));
- goto Error;
- }
-
- for (int i=0; i
nVertices; i++) { - QGeoCoordinate coord;
- if (!utmZone || !convertUTMToGeo(shpObject->padfX[i], shpObject->padfY[i], utmZone, utmSouthernHemisphere, coord)) {
- coord.setLatitude(shpObject->padfY[i]);
- coord.setLongitude(shpObject->padfX[i]);
- }
- vertices.append(coord);
- }
-
- // Filter last vertex such that it differs from first
- {
- QGeoCoordinate firstVertex = vertices[0];
-
- while (vertices.count() > 3 && vertices.last().distanceTo(firstVertex) < vertexFilterMeters) {
- vertices.removeLast();
- }
- }
-
- // Filter vertex distances to be larger than 1 meter apart
- {
- int i = 0;
- while (i < vertices.count() - 2) {
- if (vertices[i].distanceTo(vertices[i+1]) < vertexFilterMeters) {
- vertices.removeAt(i+1);
- } else {
- i++;
- }
- }
- }
-
- Error:
- if (shpObject) {
- SHPDestroyObject(shpObject);
- }
- if (shpHandle) {
- SHPClose(shpHandle);
- }
- return errorString.isEmpty();
- }
六.KMLHelper
此文件用于判断shape文件的类型,是多变形还是多线段。然后根据不同类型加载数据到容器中
头文件如下
- /****************************************************************************
- *
- * (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.
- *
- ****************************************************************************/
-
- #pragma once
-
- #include
- #include
- #include
- #include
-
- #include "ShapeFileHelper.h"
-
- class KMLHelper : public QObject
- {
- Q_OBJECT
-
- public:
- //决定shape的类型
- static ShapeFileHelper::ShapeType determineShapeType(const QString& kmlFile, QString& errorString);
- //加载多边形数据
- static bool loadPolygonFromFile(const QString& kmlFile, QList
& vertices, QString& errorString) ; - //加载多线段数据
- static bool loadPolylineFromFile(const QString& kmlFile, QList
& coords, QString& errorString) ; -
- private:
- static QDomDocument _loadFile(const QString& kmlFile, QString& errorString);
-
- static const char* _errorPrefix;
- };
cc文件如下:
- /****************************************************************************
- *
- * (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 "KMLHelper.h"
-
- #include
- #include
-
- const char* KMLHelper::_errorPrefix = QT_TR_NOOP("KML file load failed. %1");
-
- QDomDocument KMLHelper::_loadFile(const QString& kmlFile, QString& errorString)
- {
- QFile file(kmlFile);
-
- errorString.clear();
-
- if (!file.exists()) {
- errorString = QString(_errorPrefix).arg(tr("File not found: %1").arg(kmlFile));
- return QDomDocument();
- }
-
- if (!file.open(QIODevice::ReadOnly)) {
- errorString = QString(_errorPrefix).arg(tr("Unable to open file: %1 error: $%2").arg(kmlFile).arg(file.errorString()));
- return QDomDocument();
- }
-
- QDomDocument doc;
- QString errorMessage;
- int errorLine;
- if (!doc.setContent(&file, &errorMessage, &errorLine)) {
- errorString = QString(_errorPrefix).arg(tr("Unable to parse KML file: %1 error: %2 line: %3").arg(kmlFile).arg(errorMessage).arg(errorLine));
- return QDomDocument();
- }
-
- return doc;
- }
-
- ShapeFileHelper::ShapeType KMLHelper::determineShapeType(const QString& kmlFile, QString& errorString)
- {
- //加载kml文件
- QDomDocument domDocument = KMLHelper::_loadFile(kmlFile, errorString);
- if (!errorString.isEmpty()) {
- return ShapeFileHelper::Error;
- }
-
- //如果文件中包含Polygon元素则为多边形数据
- QDomNodeList rgNodes = domDocument.elementsByTagName("Polygon");
- if (rgNodes.count()) {
- return ShapeFileHelper::Polygon;
- }
-
- //如果文件中包含LineString元素则为多线段数据
- rgNodes = domDocument.elementsByTagName("LineString");
- if (rgNodes.count()) {
- return ShapeFileHelper::Polyline;
- }
-
- errorString = QString(_errorPrefix).arg(tr("No supported type found in KML file."));
- return ShapeFileHelper::Error;
- }
-
- bool KMLHelper::loadPolygonFromFile(const QString& kmlFile, QList
& vertices, QString& errorString) - {
- errorString.clear();
- vertices.clear();
-
- //加载文件
- QDomDocument domDocument = KMLHelper::_loadFile(kmlFile, errorString);
- if (!errorString.isEmpty()) {
- return false;
- }
-
- //找到Polygon
- QDomNodeList rgNodes = domDocument.elementsByTagName("Polygon");
- if (rgNodes.count() == 0) {
- errorString = QString(_errorPrefix).arg(tr("Unable to find Polygon node in KML"));
- return false;
- }
-
- //找到outerBoundaryIs/LinearRing/coordinates
- QDomNode coordinatesNode = rgNodes.item(0).namedItem("outerBoundaryIs").namedItem("LinearRing").namedItem("coordinates");
- if (coordinatesNode.isNull()) {
- errorString = QString(_errorPrefix).arg(tr("Internal error: Unable to find coordinates node in KML"));
- return false;
- }
-
- //简体化
- QString coordinatesString = coordinatesNode.toElement().text().simplified();
- //空格分成数组
- QStringList rgCoordinateStrings = coordinatesString.split(" ");
-
- QList
rgCoords; - for (int i=0; i
count()-1; i++) { - QString coordinateString = rgCoordinateStrings[i];
-
- QStringList rgValueStrings = coordinateString.split(",");
-
- //设置经纬度
- QGeoCoordinate coord;
- coord.setLongitude(rgValueStrings[0].toDouble());
- coord.setLatitude(rgValueStrings[1].toDouble());
-
- rgCoords.append(coord);
- }
-
- // 确定缠绕 反转是否需要 qgc需要顺时针环绕
- // Determine winding, reverse if needed. QGC wants clockwise winding
- double sum = 0;
- for (int i=0; i
count(); i++) { - QGeoCoordinate coord1 = rgCoords[i];
- QGeoCoordinate coord2 = (i == rgCoords.count() - 1) ? rgCoords[0] : rgCoords[i+1];
-
- sum += (coord2.longitude() - coord1.longitude()) * (coord2.latitude() + coord1.latitude());
- }
- bool reverse = sum < 0.0;
- //需要反转
- if (reverse) {
- QList
rgReversed; -
- for (int i=0; i
count(); i++) { - rgReversed.prepend(rgCoords[i]);
- }
- rgCoords = rgReversed;
- }
-
- vertices = rgCoords;
-
- return true;
- }
-
- bool KMLHelper::loadPolylineFromFile(const QString& kmlFile, QList
& coords, QString& errorString) - {
- errorString.clear();
- coords.clear();
-
- //加载文件
- QDomDocument domDocument = KMLHelper::_loadFile(kmlFile, errorString);
- if (!errorString.isEmpty()) {
- return false;
- }
-
- //找到LineString元素
- QDomNodeList rgNodes = domDocument.elementsByTagName("LineString");
- if (rgNodes.count() == 0) {
- errorString = QString(_errorPrefix).arg(tr("Unable to find LineString node in KML"));
- return false;
- }
-
- //找到coordinates元素
- QDomNode coordinatesNode = rgNodes.item(0).namedItem("coordinates");
- if (coordinatesNode.isNull()) {
- errorString = QString(_errorPrefix).arg(tr("Internal error: Unable to find coordinates node in KML"));
- return false;
- }
-
- QString coordinatesString = coordinatesNode.toElement().text().simplified();
- QStringList rgCoordinateStrings = coordinatesString.split(" ");
-
- //添加
- QList
rgCoords; - for (int i=0; i
count()-1; i++) { - QString coordinateString = rgCoordinateStrings[i];
-
- QStringList rgValueStrings = coordinateString.split(",");
-
- QGeoCoordinate coord;
- coord.setLongitude(rgValueStrings[0].toDouble());
- coord.setLatitude(rgValueStrings[1].toDouble());
-
- rgCoords.append(coord);
- }
-
- coords = rgCoords;
-
- return true;
- }
七.LogCompressor
此类用于将log文件压缩为csv文件
头文件注释如下:
- /****************************************************************************
- *
- * (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.
- *
- ****************************************************************************/
-
- #pragma once
-
- #include
-
- /**
- * @file
- * @brief Declaration of class LogCompressor.
- * This class reads in a file containing messages and translates it into a tab-delimited CSV file.
- * 该类读入一个包含信息的文件,并将其转换为一个以制表符分隔的CSV文件。
- * @author Lorenz Meier
- */
-
- class LogCompressor : public QThread
- {
- Q_OBJECT
- public:
- /** @brief Create the log compressor. It will only get active upon calling startCompression() */
- ///创建日志压缩器。 它只有在调用startCompression()时才会激活
- LogCompressor(QString logFileName, QString outFileName="", QString delimiter="\t");
- /** @brief Start the compression of a raw, line-based logfile into a CSV file */
- ///开始将原始的、基于行的日志文件压缩到CSV文件中
- void startCompression(bool holeFilling=false);
- ///压缩是否完成
- bool isFinished() const;
- ///获取当前行
- int getCurrentLine() const;
-
- protected:
- void run(); ///< This function actually performs the compression. It's an overloaded function from QThread 这个函数实际执行压缩。 它是一个来自QThread的重载函数
- QString logFileName; ///< The input file name. 输入文件
- QString outFileName; ///< The output file name. If blank defaults to logFileName 输出文件
- bool running; ///< True when the startCompression() function is operating. 运行状态
- int currentDataLine; ///< The current line of data that is being processed. Only relevant when running==true 正在处理的当前数据行。 只在运行==true时相关
- QString delimiter; ///< Delimiter between fields in the output file. Defaults to tab ('\t') 分隔符
- bool holeFillingEnabled; ///< Enables the filling of holes in the dataset with the previous value (or NaN if none exists) 是否启用将前一个值填充数据集中的空缺(如果不存在则为NaN)
-
- signals:
- /** @brief This signal is emitted once a logfile has been finished writing
- * @param fileName The name of the output (CSV) file
- */
- void finishedFile(QString fileName);
-
- /// This signal is connected to QGCApplication::showCriticalMessage to show critical errors which come from the thread.
- /// 该信号连接到QGCApplication::showCriticalMessage,以显示来自线程的严重错误。
- /// There is no need for clients to connect to this signal.
- /// 客户端不需要连接到这个信号。
- void logProcessingCriticalError(const QString& title, const QString& msg);
-
- private:
- void _signalCriticalError(const QString& msg);
-
- };
cc文件注释如下:
- /****************************************************************************
- *
- * (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.
- *
- ****************************************************************************/
-
-
- /**
- * @file
- * @brief Implementation of class LogCompressor.
- * This class reads in a file containing messages and translates it into a tab-delimited CSV file.
- * @author Lorenz Meier
- */
-
- #include "LogCompressor.h"
- #include "QGCApplication.h"
-
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
-
- /**
- * Initializes all the variables necessary for a compression run. This won't actually happen
- * until startCompression(...) is called.
- */
- //当调用startCompression是该初始化方法被调用
- LogCompressor::LogCompressor(QString logFileName, QString outFileName, QString delimiter) :
- logFileName(logFileName),
- outFileName(outFileName),
- running(true),
- currentDataLine(0),
- delimiter(delimiter),
- holeFillingEnabled(true)
- {
- connect(this, &LogCompressor::logProcessingCriticalError, qgcApp(), &QGCApplication::criticalMessageBoxOnMainThread);
- }
-
- void LogCompressor::run()
- {
- // Verify that the input file is useable
- QFile infile(logFileName);
- if (!infile.exists() || !infile.open(QIODevice::ReadOnly | QIODevice::Text)) {
- _signalCriticalError(tr("Log Compressor: Cannot start/compress log file, since input file %1 is not readable").arg(QFileInfo(infile.fileName()).absoluteFilePath()));
- return;
- }
-
- // outFileName = logFileName;
-
- QString outFileName;
-
- //QFileInfo(infile.fileName()).absoluteFilePath() 可以获取到该文件的绝对路径
- QStringList parts = QFileInfo(infile.fileName()).absoluteFilePath().split(".", Qt::SkipEmptyParts);
-
- //将文件名加_compressed
- parts.replace(0, parts.first() + "_compressed");
- //后缀改为txt
- parts.replace(parts.size()-1, "txt");
- //拼接
- outFileName = parts.join(".");
-
- // Verify that the output file is useable
- //打开文件不存在则创建
- QFile outTmpFile(outFileName);
- if (!outTmpFile.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) {
- _signalCriticalError(tr("Log Compressor: Cannot start/compress log file, since output file %1 is not writable").arg(QFileInfo(outTmpFile.fileName()).absoluteFilePath()));
- return;
- }
-
-
- // First we search the input file through keySearchLimit number of lines
- // looking for variables. This is necessary before CSV files require
- // the same number of fields for every line.
- //首先,我们通过keySearchLimit搜索变量的行数来搜索输入文件。 在CSV文件要求每行有相同数量的字段之前,这是必要的。
- const unsigned int keySearchLimit = 15000;
- unsigned int keyCounter = 0;
- QTextStream in(&infile);
- QMap
int> messageMap; -
- //如果没到文件尾并且key搜索数量小于limit
- //此方法搜索了文件中所有行中第三个位置的名称放入到map中
- while (!in.atEnd() && keyCounter < keySearchLimit) {
- //通过delimiter分割行取得名称
- QString messageName = in.readLine().split(delimiter).at(2);
- //将名称作为key存入map中
- messageMap.insert(messageName, 0);
- ++keyCounter;
- }
-
- // Now update each key with its index in the output string. These are
- // all offset by one to account for the first field: timestamp_ms.
- // 现在用输出字符串中的索引更新每个键。这些都被偏移一个单位,以负责第一个字段:timestamp_ms。
- QMap
int>::iterator i = messageMap.begin(); - int j;
- //为每个字段的值从1开始递增的赋值
- for (i = messageMap.begin(), j = 1; i != messageMap.end(); ++i, ++j) {
- i.value() = j;
- }
-
- // Open the output file and write the header line to it
- // 获取map所有的keys
- QStringList headerList(messageMap.keys());
- //打开输出文件并将标题行写入其中
- QString headerLine = "timestamp_ms" + delimiter + headerList.join(delimiter) + "\n";
- // Clean header names from symbols Matlab considers as Latex syntax
- headerLine = headerLine.replace("timestamp", "TIMESTAMP");
- headerLine = headerLine.replace(":", "");
- headerLine = headerLine.replace("_", "");
- headerLine = headerLine.replace(".", "");
-
- // 写入
- // QString的toLocal8bit和toLatin1都可以将QString转化为QByteArray,但是两者的区别在于编码的不同:
- // toLocal8Bit:Unicode编码
- // toLatin1:ASCII编码
- outTmpFile.write(headerLine.toLocal8Bit());
-
- _signalCriticalError(tr("Log compressor: Dataset contains dimensions: ") + headerLine);
-
- // Template list stores a list for populating with data as it's parsed from messages.
- // 模板列表存储一个列表,用于在从消息解析数据时填充数据。
- QStringList templateList;
- for (int i = 0; i < headerList.size() + 1; ++i) {
- templateList << (holeFillingEnabled?"NaN":"");
- }
-
-
- // // Reset our position in the input file before we start the main processing loop.
- // in.seek(0);
-
- // // Search through all lines and build a list of unique timestamps
- // QMap
timestampMap; - // while (!in.atEnd()) {
- // quint64 timestamp = in.readLine().split(delimiter).at(0).toULongLong();
- // timestampMap.insert(timestamp, templateList);
- // }
-
- // Jump back to start of file
- // 跳到输入文件的头
- in.seek(0);
-
- // Map of final output lines, key is time
- // map的最终输出行,key是时间
- QMap
timestampMap; -
- // Run through the whole file and fill map of timestamps
- while (!in.atEnd()) {
- QStringList newLine = in.readLine().split(delimiter);
- //获取时间
- quint64 timestamp = newLine.at(0).toULongLong();
-
- // Check if timestamp does exist - if not, add it
- // 不存在就添加 值为刚刚构建的模版数据 templateList
- if (!timestampMap.contains(timestamp)) {
- timestampMap.insert(timestamp, templateList);
- }
-
- //获取到模版列表 这里为所有key的对应的初始数据 templateList << (holeFillingEnabled?"NaN":"")
- QStringList list = timestampMap.value(timestamp);
-
- //获取当前行的数据名称 也就是header中管道每一个key
- QString currentDataName = newLine.at(2);
- //获取key对应的值
- QString currentDataValue = newLine.at(3);
- //修改对应元素的值 messageMap中的值存放的是header中所有的keys
- list.replace(messageMap.value(currentDataName), currentDataValue);
- timestampMap.insert(timestamp, list);
- }
-
- int lineCounter = 0;
-
- QStringList lastList = timestampMap.values().at(1);
-
- foreach (QStringList list, timestampMap.values()) {
- // Write this current time set out to the file
- // only do so from the 2nd line on, since the first
- // line could be incomplete
- // 将当前时间设置写入文件,从第二行开始,因为第一行可能不完整
- if (lineCounter > 1) {
- // Set the timestamp
- //设置第0个位置为时间信息
- list.replace(0,QString("%1").arg(timestampMap.keys().at(lineCounter)));
-
- // Fill holes if necessary
- if (holeFillingEnabled) {
- int index = 0;
- //如果此行数据缺失就用上行数据代替
- foreach (const QString& str, list) {
- if (str == "" || str == "NaN") {
- list.replace(index, lastList.at(index));
- }
- index++;
- }
- }
-
- // Set last list
- lastList = list;
-
- // Write data columns
- QString output = list.join(delimiter) + "\n";
- //写入输出文件
- outTmpFile.write(output.toLocal8Bit());
- }
- lineCounter++;
- }
-
- // We're now done with the source file
- infile.close();
-
- // Clean up and update the status before we return.
- currentDataLine = 0;
- emit finishedFile(outFileName);
- running = false;
- }
-
- /**
- * @param holeFilling If hole filling is enabled, the compressor tries to fill empty data fields with previous
- * values from the same variable (or NaN, if no previous value existed)
- */
- void LogCompressor::startCompression(bool holeFilling)
- {
- holeFillingEnabled = holeFilling;
- start();
- }
-
- bool LogCompressor::isFinished() const
- {
- return !running;
- }
-
- int LogCompressor::getCurrentLine() const
- {
- return currentDataLine;
- }
-
-
- void LogCompressor::_signalCriticalError(const QString& msg)
- {
- emit logProcessingCriticalError(tr("Log Compressor"), msg);
- }
总结
好了,问了文章篇幅不要太长,这里就解释这几个文件吧,都是一些工具类,包括命令行的读取,json文件的读取,验证操作,kml文件的读取操作,shape文件的读写操作,以及普通文件转为csv文件的操作。下一节我们对前缀qgc的几个文件进行分析。
-
相关阅读:
从0开始配置深度学习环境(Python)
如何将Word转成PDF?试一下这个转换方法
【解放双手】Auto Lidar2Cam Calibration——相机雷达自动标定
绑定点击事件及解绑点击事件
使用Microsoft认知服务进行文本检测和 OCR
kotlin基础教程:<8>类的延时成员变量、初始化代码块和陷阱
【图像插值】基于稀疏拉普拉斯滤波器处理红绿蓝三种像素实现插值效果附matlab代码
Node历史版本下载及配置npm镜像
第十章 单调栈 part03 84. 柱状图中最大的矩形
Vue常见问题
-
原文地址:https://blog.csdn.net/weixin_43409627/article/details/126866408