• 机器人地面站-[QGroundControl源码解析]-[1]


    目录

    前言

    一.CmdLineOptParser

    二.JsonHelper

    三.KMLDomDocument

    四.ShapeFileHelper

    五.SHPFileHelper

    六.KMLHelper

    七.LogCompressor

    总结


    前言

    项目中要使用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下的代码逐个分析,目录结构如下所示。

    一.CmdLineOptParser

    这个类主要是一个解析输入参数的类,首先传入一个结构体数组,此结构体保存了要记录的选项和参数,然后将用户输入和此结构体数组进行一对一的比对,将用户输入储存到结构体数组中。

    1. /// @brief Structure used to pass command line options to the ParseCmdLineOptions function.
    2. typedef struct {
    3. const char* optionStr; ///< Command line option, for example "--foo" 参数名称
    4. bool* optionFound; ///< If option is found this variable will be set to true 标识参数是否找到
    5. QString* optionArg; ///< Option has additional argument, form is option:arg 参数是否有对应的值
    6. } CmdLineOpt_t;
    7. /// @brief Implements a simple command line parser which sets booleans to true if the option is found.
    8. void ParseCmdLineOptions(int& argc, ///< count of arguments in argv
    9. char* argv[], ///< command line arguments
    10. CmdLineOpt_t* prgOpts, ///< command line options
    11. size_t cOpts, ///< count of command line options
    12. bool removeParsedOptions) ///< true: remove parsed option from argc/argv
    13. {
    14. // Start with all options off
    15. // 先将所有的option的find置为false
    16. for (size_t iOption=0; iOption
    17. *prgOpts[iOption].optionFound = false;
    18. }
    19. //遍历所有输入参数
    20. for (int iArg=1; iArg
    21. for (size_t iOption=0; iOption
    22. bool found = false;
    23. QString arg(argv[iArg]);
    24. QString optionStr(prgOpts[iOption].optionStr);
    25. //如果输入参数以设定的optionStr开头且忽略大小写 且含有:表示有额外参数
    26. if (arg.startsWith(QString("%1:").arg(optionStr), Qt::CaseInsensitive)) {
    27. found = true;
    28. //如果此选项有额外参数输入
    29. if (prgOpts[iOption].optionArg) {
    30. //则获取额外参数并赋值 .right取右边n个长度作为新的字符串
    31. *prgOpts[iOption].optionArg = arg.right(arg.length() - (optionStr.length() + 1));
    32. }
    33. }
    34. // 普通参数
    35. else if (arg.compare(optionStr, Qt::CaseInsensitive) == 0) {
    36. found = true;
    37. }
    38. if (found) {
    39. //如果有合适的匹配项
    40. *prgOpts[iOption].optionFound = true;
    41. //如果要删除已经解析过的参数
    42. if (removeParsedOptions) {
    43. for (int iShift=iArg; iShift-1; iShift++) {
    44. //删掉
    45. argv[iShift] = argv[iShift+1];
    46. }
    47. argc--;
    48. iArg--;
    49. }
    50. }
    51. }
    52. }
    53. }

    二.JsonHelper

    这个文件主要承担了项目中json文件的验证,加载,读取,设置,保存等操作。头文件中增加的注释。

    1. /****************************************************************************
    2. *
    3. * (c) 2009-2020 QGROUNDCONTROL PROJECT
    4. *
    5. * QGroundControl is licensed according to the terms in the file
    6. * COPYING.md in the root of the source code directory.
    7. *
    8. ****************************************************************************/
    9. #pragma once
    10. #include
    11. #include
    12. #include
    13. #include
    14. /// @file
    15. /// @author Don Gagne
    16. class QmlObjectListModel;
    17. /// @brief Json manipulation helper class.
    18. /// Primarily used for parsing and processing Fact metadata.
    19. class JsonHelper
    20. {
    21. //给非qt类添加多语言支持
    22. Q_DECLARE_TR_FUNCTIONS(JsonHelper)
    23. public:
    24. /// Determines is the specified file is a json file 确定指定的文件是否是json文件
    25. /// @return true: file is json, false: file is not json 输入参数是文件名
    26. static bool isJsonFile(const QString& fileName, ///< filename
    27. QJsonDocument& jsonDoc, ///< returned json document
    28. QString& errorString); ///< error on parse failure
    29. /// Determines is the specified data is a json file
    30. /// @return true: file is json, false: file is not json 参数是二进制
    31. static bool isJsonFile(const QByteArray& bytes, ///< json bytes
    32. QJsonDocument& jsonDoc, ///< returned json document
    33. QString& errorString); ///< error on parse failure
    34. /// Saves the standard file header the json object 保存标准的qgc jason头
    35. static void saveQGCJsonFileHeader(QJsonObject& jsonObject, ///< root json object
    36. const QString& fileType, ///< file type for file
    37. int version); ///< version number for file
    38. /// Validates the standard parts of an external QGC json file (Plan file, ...): 验证外部QGC json文件的标准部分
    39. /// jsonFileTypeKey - Required and checked to be equal to expected FileType 要求并检查等于期望的文件类型
    40. /// jsonVersionKey - Required and checked to be below supportedMajorVersion, supportedMinorVersion 要求并检查低于支持的主版本号和次版本号
    41. /// jsonGroundStationKey - Required and checked to be string type 要求并检查地面站的key为string类型
    42. /// @return false: validation failed, errorString set
    43. static bool validateExternalQGCJsonFile(const QJsonObject& jsonObject, ///< json object to validate 被检查对象
    44. const QString& expectedFileType, ///< correct file type for file 要求的文件类型
    45. int minSupportedVersion, ///< minimum supported version 小版本号
    46. int maxSupportedVersion, ///< maximum supported major version 大版本号
    47. int &version, ///< returned file version 返回的文件版本
    48. QString& errorString); ///< returned error string if validation fails 失败原因
    49. /// Validates the standard parts of a internal QGC json file (FactMetaData, ...):验证内部QGC json文件的标准部分,参数部分同上
    50. /// jsonFileTypeKey - Required and checked to be equal to expectedFileType
    51. /// jsonVersionKey - Required and checked to be below supportedMajorVersion, supportedMinorVersion
    52. /// jsonGroundStationKey - Required and checked to be string type
    53. /// @return false: validation failed, errorString set
    54. static bool validateInternalQGCJsonFile(const QJsonObject& jsonObject, ///< json object to validate
    55. const QString& expectedFileType, ///< correct file type for file
    56. int minSupportedVersion, ///< minimum supported version
    57. int maxSupportedVersion, ///< maximum supported major version
    58. int &version, ///< returned file version
    59. QString& errorString); ///< returned error string if validation fails
    60. /// Opens, validates and translates an internal QGC json file. 打开、验证和翻译内部QGC json文件。
    61. /// @return Json root object for file. Empty QJsonObject if error.
    62. static QJsonObject openInternalQGCJsonFile(const QString& jsonFilename, ///< Json file to open
    63. const QString& expectedFileType, ///< correct file type for file
    64. int minSupportedVersion, ///< minimum supported version
    65. int maxSupportedVersion, ///< maximum supported major version
    66. int &version, ///< returned file version
    67. QString& errorString); ///< returned error string if validation fails
    68. /// Validates that the specified keys are in the object 验证指定的键是否在对象中
    69. /// @return false: validation failed, errorString set
    70. static bool validateRequiredKeys(const QJsonObject& jsonObject, ///< json object to validate
    71. const QStringList& keys, ///< keys which are required to be present
    72. QString& errorString); ///< returned error string if validation fails
    73. /// Validates the types of specified keys are in the object 验证对象中指定键的类型
    74. /// @return false: validation failed, errorString set
    75. static bool validateKeyTypes(const QJsonObject& jsonObject, ///< json object to validate
    76. const QStringList& keys, ///< keys to validate
    77. const QList& types, ///< required type for each key, QJsonValue::Null specifies double with possible NaN
    78. QString& errorString); ///< returned error string if validation fails
    79. typedef struct {
    80. const char* key; ///< json key name
    81. QJsonValue::Type type; ///< required type for key, QJsonValue::Null specifies double with possible NaN
    82. bool required; ///< true: key must be present
    83. } KeyValidateInfo;
    84. static bool validateKeys(const QJsonObject& jsonObject, const QList& keyInfo, QString& errorString);
    85. /// Loads a QGeoCoordinate 加载位置坐标数据 存储格式为[ lat, lon, alt ]
    86. /// Stored as array [ lat, lon, alt ]
    87. /// @return false: validation failed
    88. static bool loadGeoCoordinate(const QJsonValue& jsonValue, ///< json value to load from
    89. bool altitudeRequired, ///< true: altitude must be specified
    90. QGeoCoordinate& coordinate, ///< returned QGeoCordinate
    91. QString& errorString, ///< returned error string if load failure
    92. bool geoJsonFormat = false); ///< if true, use [lon, lat], [lat, lon] otherwise
    93. /// Saves a QGeoCoordinate 保存位置坐标数据 存储格式为[ lat, lon, alt ]
    94. /// Stored as array [ lat, lon, alt ]
    95. static void saveGeoCoordinate(const QGeoCoordinate& coordinate, ///< QGeoCoordinate to save
    96. bool writeAltitude, ///< true: write altitude to json
    97. QJsonValue& jsonValue); ///< json value to save to
    98. /// Loads a QGeoCoordinate
    99. /// Stored as array [ lon, lat, alt ]
    100. /// @return false: validation failed
    101. static bool loadGeoJsonCoordinate(const QJsonValue& jsonValue, ///< json value to load from
    102. bool altitudeRequired, ///< true: altitude must be specified
    103. QGeoCoordinate& coordinate, ///< returned QGeoCordinate
    104. QString& errorString); ///< returned error string if load failure
    105. /// Saves a QGeoCoordinate
    106. /// Stored as array [ lon, lat, alt ]
    107. static void saveGeoJsonCoordinate(const QGeoCoordinate& coordinate, ///< QGeoCoordinate to save
    108. bool writeAltitude, ///< true: write altitude to json
    109. QJsonValue& jsonValue); ///< json value to save to
    110. /// Loads a polygon from an array 加载多边形
    111. static bool loadPolygon(const QJsonArray& polygonArray, ///< Array of coordinates
    112. QmlObjectListModel& list, ///< Empty list to add vertices to
    113. QObject* parent, ///< parent for newly allocated QGCQGeoCoordinates
    114. QString& errorString); ///< returned error string if load failure
    115. /// Loads a list of QGeoCoordinates from a json array 加载位置坐标数据数组形式
    116. /// @return false: validation failed
    117. static bool loadGeoCoordinateArray(const QJsonValue& jsonValue, ///< json value which contains points
    118. bool altitudeRequired, ///< true: altitude field must be specified
    119. QVariantList& rgVarPoints, ///< returned points
    120. QString& errorString); ///< returned error string if load failure
    121. static bool loadGeoCoordinateArray(const QJsonValue& jsonValue, ///< json value which contains points
    122. bool altitudeRequired, ///< true: altitude field must be specified
    123. QList& rgPoints, ///< returned points
    124. QString& errorString); ///< returned error string if load failure
    125. /// Saves a list of QGeoCoordinates to a json array 保存位置坐标数据数组形式
    126. static void saveGeoCoordinateArray(const QVariantList& rgVarPoints, ///< points to save
    127. bool writeAltitude, ///< true: write altitide value
    128. QJsonValue& jsonValue); ///< json value to save to
    129. static void saveGeoCoordinateArray(const QList& rgPoints, ///< points to save
    130. bool writeAltitude, ///< true: write altitide value
    131. QJsonValue& jsonValue); ///< json value to save to
    132. /// Saves a polygon to a json array 保存多边形数据
    133. static void savePolygon(QmlObjectListModel& list, ///< List which contains vertices
    134. QJsonArray& polygonArray); ///< Array to save into
    135. /// Returns NaN if the value is null, or if not, the double value 如果值为空,则返回NaN;如果不为空,则返回double值
    136. static double possibleNaNJsonValue(const QJsonValue& value);
    137. //一些字符串常量
    138. static const char* jsonVersionKey;
    139. static const char* jsonGroundStationKey;
    140. static const char* jsonGroundStationValue;
    141. static const char* jsonFileTypeKey;
    142. private:
    143. static QString _jsonValueTypeToString(QJsonValue::Type type);
    144. static bool _loadGeoCoordinate(const QJsonValue& jsonValue,
    145. bool altitudeRequired,
    146. QGeoCoordinate& coordinate,
    147. QString& errorString,
    148. bool geoJsonFormat);
    149. static void _saveGeoCoordinate(const QGeoCoordinate& coordinate,
    150. bool writeAltitude,
    151. QJsonValue& jsonValue,
    152. bool geoJsonFormat);
    153. static QStringList _addDefaultLocKeys(QJsonObject& jsonObject);
    154. static QJsonObject _translateRoot(QJsonObject& jsonObject, const QString& translateContext, const QStringList& translateKeys);
    155. static QJsonObject _translateObject(QJsonObject& jsonObject, const QString& translateContext, const QStringList& translateKeys);
    156. static QJsonArray _translateArray(QJsonArray& jsonArray, const QString& translateContext, const QStringList& translateKeys);
    157. static const char* _translateKeysKey;
    158. static const char* _arrayIDKeysKey;
    159. };

    这里添加部分cc中的实现注释

    1. /****************************************************************************
    2. *
    3. * (c) 2009-2020 QGROUNDCONTROL PROJECT
    4. *
    5. * QGroundControl is licensed according to the terms in the file
    6. * COPYING.md in the root of the source code directory.
    7. *
    8. ****************************************************************************/
    9. #include "JsonHelper.h"
    10. #include "QGCQGeoCoordinate.h"
    11. #include "QmlObjectListModel.h"
    12. #include "MissionCommandList.h"
    13. #include "FactMetaData.h"
    14. #include "QGCApplication.h"
    15. #include
    16. #include
    17. #include
    18. #include
    19. #include
    20. #include
    21. #include
    22. const char* JsonHelper::jsonVersionKey = "version";
    23. const char* JsonHelper::jsonGroundStationKey = "groundStation";
    24. const char* JsonHelper::jsonGroundStationValue = "QGroundControl";
    25. const char* JsonHelper::jsonFileTypeKey = "fileType";
    26. const char* JsonHelper::_translateKeysKey = "translateKeys";
    27. const char* JsonHelper::_arrayIDKeysKey = "_arrayIDKeys";
    28. /// 检查jsonObject是否包含keys中的键
    29. bool JsonHelper::validateRequiredKeys(const QJsonObject& jsonObject, const QStringList& keys, QString& errorString)
    30. {
    31. QString missingKeys;
    32. //遍历所有的keys
    33. foreach(const QString& key, keys) {
    34. //如果jsonObject不包含key
    35. if (!jsonObject.contains(key)) {
    36. //如果missingKeys不为空
    37. if (!missingKeys.isEmpty()) {
    38. //QStringLiteral是Qt5中新引入的一个用来从“字符串常量”创建QString对象的宏
    39. missingKeys += QStringLiteral(", ");
    40. }
    41. missingKeys += key;
    42. //key1, key2, key3...
    43. }
    44. }
    45. if (missingKeys.count() != 0) {
    46. //将未找到的键打印出来
    47. errorString = QObject::tr("The following required keys are missing: %1").arg(missingKeys);
    48. return false;
    49. }
    50. return true;
    51. }
    52. bool JsonHelper::_loadGeoCoordinate(const QJsonValue& jsonValue,
    53. bool altitudeRequired, //是否需要高度信息
    54. QGeoCoordinate& coordinate, //坐标系
    55. QString& errorString,
    56. bool geoJsonFormat)
    57. {
    58. //要求jsonValue是一个数组形式
    59. if (!jsonValue.isArray()) {
    60. errorString = QObject::tr("value for coordinate is not array");
    61. return false;
    62. }
    63. //转换为json数组
    64. QJsonArray coordinateArray = jsonValue.toArray();
    65. //如果需要高度则是三个信息否则是两个
    66. int requiredCount = altitudeRequired ? 3 : 2;
    67. //判断如果json数组数量不等于requiredCount返回错误
    68. if (coordinateArray.count() != requiredCount) {
    69. errorString = QObject::tr("Coordinate array must contain %1 values").arg(requiredCount);
    70. return false;
    71. }
    72. //遍历json数组
    73. foreach(const QJsonValue& jsonValue, coordinateArray) {
    74. //要求json值的类型必须是double或者null
    75. if (jsonValue.type() != QJsonValue::Double && jsonValue.type() != QJsonValue::Null) {
    76. errorString = QObject::tr("Coordinate array may only contain double values, found: %1").arg(jsonValue.type());
    77. return false;
    78. }
    79. }
    80. //格式化
    81. if (geoJsonFormat) {
    82. coordinate = QGeoCoordinate(coordinateArray[1].toDouble(), coordinateArray[0].toDouble());
    83. } else {
    84. coordinate = QGeoCoordinate(possibleNaNJsonValue(coordinateArray[0]), possibleNaNJsonValue(coordinateArray[1]));
    85. }
    86. //设置高度
    87. if (altitudeRequired) {
    88. coordinate.setAltitude(possibleNaNJsonValue(coordinateArray[2]));
    89. }
    90. return true;
    91. }
    92. ///将坐标轴转化为json数组
    93. void JsonHelper::_saveGeoCoordinate(const QGeoCoordinate& coordinate,
    94. bool writeAltitude,
    95. QJsonValue& jsonValue,
    96. bool geoJsonFormat)
    97. {
    98. QJsonArray coordinateArray;
    99. if (geoJsonFormat) {
    100. coordinateArray << coordinate.longitude() << coordinate.latitude();
    101. } else {
    102. coordinateArray << coordinate.latitude() << coordinate.longitude();
    103. }
    104. if (writeAltitude) {
    105. coordinateArray << coordinate.altitude();
    106. }
    107. jsonValue = QJsonValue(coordinateArray);
    108. }
    109. bool JsonHelper::loadGeoCoordinate(const QJsonValue& jsonValue,
    110. bool altitudeRequired,
    111. QGeoCoordinate& coordinate,
    112. QString& errorString,
    113. bool geoJsonFormat)
    114. {
    115. return _loadGeoCoordinate(jsonValue, altitudeRequired, coordinate, errorString, geoJsonFormat);
    116. }
    117. void JsonHelper::saveGeoCoordinate(const QGeoCoordinate& coordinate,
    118. bool writeAltitude,
    119. QJsonValue& jsonValue)
    120. {
    121. _saveGeoCoordinate(coordinate, writeAltitude, jsonValue, false /* geoJsonFormat */);
    122. }
    123. bool JsonHelper::loadGeoJsonCoordinate(const QJsonValue& jsonValue,
    124. bool altitudeRequired,
    125. QGeoCoordinate& coordinate,
    126. QString& errorString)
    127. {
    128. return _loadGeoCoordinate(jsonValue, altitudeRequired, coordinate, errorString, true /* geoJsonFormat */);
    129. }
    130. void JsonHelper::saveGeoJsonCoordinate(const QGeoCoordinate& coordinate,
    131. bool writeAltitude,
    132. QJsonValue& jsonValue)
    133. {
    134. _saveGeoCoordinate(coordinate, writeAltitude, jsonValue, true /* geoJsonFormat */);
    135. }
    136. ///判断键对应的类型是否正确
    137. bool JsonHelper::validateKeyTypes(const QJsonObject& jsonObject, const QStringList& keys, const QList& types, QString& errorString)
    138. {
    139. for (int i=0; icount(); i++) {
    140. QString valueKey = keys[i];
    141. if (jsonObject.contains(valueKey)) {
    142. const QJsonValue& jsonValue = jsonObject[valueKey];
    143. if (jsonValue.type() == QJsonValue::Null && types[i] == QJsonValue::Double) {
    144. // Null type signals a NaN on a double value 空类型表示在double的值为NaN
    145. continue;
    146. }
    147. //判断类型不相等报错
    148. if (jsonValue.type() != types[i]) {
    149. errorString = QObject::tr("Incorrect value type - key:type:expected %1:%2:%3").arg(valueKey).arg(_jsonValueTypeToString(jsonValue.type())).arg(_jsonValueTypeToString(types[i]));
    150. return false;
    151. }
    152. }
    153. }
    154. return true;
    155. }
    156. bool JsonHelper::isJsonFile(const QByteArray& bytes, QJsonDocument& jsonDoc, QString& errorString)
    157. {
    158. QJsonParseError parseError;
    159. //从二进制流中加载json,转为QJsonDocument
    160. jsonDoc = QJsonDocument::fromJson(bytes, &parseError);
    161. if (parseError.error == QJsonParseError::NoError) {
    162. return true;
    163. } else {
    164. //转化失败
    165. int startPos = qMax(0, parseError.offset - 100);
    166. int length = qMin(bytes.count() - startPos, 200);
    167. qDebug() << QStringLiteral("Json read error '%1'").arg(bytes.mid(startPos, length).constData());
    168. errorString = parseError.errorString();
    169. return false;
    170. }
    171. }
    172. bool JsonHelper::isJsonFile(const QString& fileName, QJsonDocument& jsonDoc, QString& errorString)
    173. {
    174. QFile jsonFile(fileName);
    175. if (!jsonFile.open(QFile::ReadOnly)) {
    176. errorString = tr("File open failed: file:error %1 %2").arg(jsonFile.fileName()).arg(jsonFile.errorString());
    177. return false;
    178. }
    179. //转为二进制流
    180. QByteArray jsonBytes = jsonFile.readAll();
    181. jsonFile.close();
    182. return isJsonFile(jsonBytes, jsonDoc, errorString);
    183. }
    184. bool JsonHelper::validateInternalQGCJsonFile(const QJsonObject& jsonObject,
    185. const QString& expectedFileType,
    186. int minSupportedVersion,
    187. int maxSupportedVersion,
    188. int& version,
    189. QString& errorString)
    190. {
    191. // Validate required keys 验证所需的key
    192. QList requiredKeys = {
    193. { jsonFileTypeKey, QJsonValue::String, true },
    194. { jsonVersionKey, QJsonValue::Double, true },
    195. };
    196. //验证requiredKeys
    197. if (!JsonHelper::validateKeys(jsonObject, requiredKeys, errorString)) {
    198. return false;
    199. }
    200. // Make sure file type is correct 验证文件类型
    201. QString fileTypeValue = jsonObject[jsonFileTypeKey].toString();
    202. if (fileTypeValue != expectedFileType) {
    203. errorString = QObject::tr("Incorrect file type key expected:%1 actual:%2").arg(expectedFileType).arg(fileTypeValue);
    204. return false;
    205. }
    206. // Version check 验证版本信息
    207. version = jsonObject[jsonVersionKey].toInt();
    208. if (version < minSupportedVersion) {
    209. errorString = QObject::tr("File version %1 is no longer supported").arg(version);
    210. return false;
    211. }
    212. //验证版本信息
    213. if (version > maxSupportedVersion) {
    214. errorString = QObject::tr("File version %1 is newer than current supported version %2").arg(version).arg(maxSupportedVersion);
    215. return false;
    216. }
    217. return true;
    218. }
    219. bool JsonHelper::validateExternalQGCJsonFile(const QJsonObject& jsonObject,
    220. const QString& expectedFileType,
    221. int minSupportedVersion,
    222. int maxSupportedVersion,
    223. int& version,
    224. QString& errorString)
    225. {
    226. // Validate required keys
    227. QList requiredKeys = {
    228. { jsonGroundStationKey, QJsonValue::String, true },
    229. };
    230. if (!JsonHelper::validateKeys(jsonObject, requiredKeys, errorString)) {
    231. return false;
    232. }
    233. return validateInternalQGCJsonFile(jsonObject, expectedFileType, minSupportedVersion, maxSupportedVersion, version, errorString);
    234. }
    235. QStringList JsonHelper::_addDefaultLocKeys(QJsonObject& jsonObject)
    236. {
    237. QString translateKeys;
    238. //获取jsonObject中fileType对应的值
    239. QString fileType = jsonObject[jsonFileTypeKey].toString();
    240. if (!fileType.isEmpty()) {
    241. //如果fileType等于以下类型
    242. if (fileType == MissionCommandList::qgcFileType) {
    243. //如果jsonObject中包含“translateKeys”
    244. if (jsonObject.contains(_translateKeysKey)) {
    245. //获取对应的值
    246. translateKeys = jsonObject[_translateKeysKey].toString();
    247. } else {
    248. //不包含则添加
    249. translateKeys = "label,enumStrings,friendlyName,description,category";
    250. jsonObject[_translateKeysKey] = translateKeys;
    251. }
    252. //如果jsonObject不包含_arrayIDKeys
    253. if (!jsonObject.contains(_arrayIDKeysKey)) {
    254. //不包含则添加
    255. jsonObject[_arrayIDKeysKey] = "rawName,comment";
    256. }
    257. } else if (fileType == FactMetaData::qgcFileType) {
    258. if (jsonObject.contains(_translateKeysKey)) {
    259. translateKeys = jsonObject[_translateKeysKey].toString();
    260. } else {
    261. translateKeys = "shortDescription,longDescription,enumStrings";
    262. jsonObject[_translateKeysKey] = "shortDescription,longDescription,enumStrings";
    263. }
    264. if (!jsonObject.contains(_arrayIDKeysKey)) {
    265. jsonObject[_arrayIDKeysKey] = "name";
    266. }
    267. }
    268. }
    269. return translateKeys.split(",");
    270. }
    271. QJsonObject JsonHelper::_translateObject(QJsonObject& jsonObject, const QString& translateContext, const QStringList& translateKeys)
    272. {
    273. //遍历jsonObject所有的keys
    274. for (const QString& key: jsonObject.keys()) {
    275. //如果keys是string类型
    276. if (jsonObject[key].isString()) {
    277. //转为string
    278. QString locString = jsonObject[key].toString();
    279. //translateKeys如果包含key
    280. if (translateKeys.contains(key)) {
    281. QString disambiguation;
    282. QString disambiguationPrefix("#loc.disambiguation#");
    283. //如果locString以#loc.disambiguation#开头
    284. if (locString.startsWith(disambiguationPrefix)) {
    285. //则截取#loc.disambiguation#之后的部分为新的locString
    286. locString = locString.right(locString.length() - disambiguationPrefix.length());
    287. int commentEndIndex = locString.indexOf("#");
    288. //如果locString含有#
    289. if (commentEndIndex != -1) {
    290. //获取#左边的部分
    291. disambiguation = locString.left(commentEndIndex);
    292. //获取#右边的部分
    293. locString = locString.right(locString.length() - disambiguation.length() - 1);
    294. }
    295. }
    296. //翻译
    297. QString xlatString = qgcApp()->qgcJSONTranslator().translate(translateContext.toUtf8().constData(), locString.toUtf8().constData(), disambiguation.toUtf8().constData());
    298. if (!xlatString.isNull()) {
    299. jsonObject[key] = xlatString;
    300. }
    301. }
    302. } else if (jsonObject[key].isArray()) {
    303. QJsonArray childJsonArray = jsonObject[key].toArray();
    304. jsonObject[key] = _translateArray(childJsonArray, translateContext, translateKeys);
    305. } else if (jsonObject[key].isObject()) {
    306. QJsonObject childJsonObject = jsonObject[key].toObject();
    307. jsonObject[key] = _translateObject(childJsonObject, translateContext, translateKeys);
    308. }
    309. }
    310. return jsonObject;
    311. }
    312. QJsonArray JsonHelper::_translateArray(QJsonArray& jsonArray, const QString& translateContext, const QStringList& translateKeys)
    313. {
    314. for (int i=0; icount(); i++) {
    315. QJsonObject childJsonObject = jsonArray[i].toObject();
    316. jsonArray[i] = _translateObject(childJsonObject, translateContext, translateKeys);
    317. }
    318. return jsonArray;
    319. }
    320. QJsonObject JsonHelper::_translateRoot(QJsonObject& jsonObject, const QString& translateContext, const QStringList& translateKeys)
    321. {
    322. return _translateObject(jsonObject, translateContext, translateKeys);
    323. }
    324. QJsonObject JsonHelper::openInternalQGCJsonFile(const QString& jsonFilename,
    325. const QString& expectedFileType,
    326. int minSupportedVersion,
    327. int maxSupportedVersion,
    328. int &version,
    329. QString& errorString)
    330. {
    331. QFile jsonFile(jsonFilename);
    332. if (!jsonFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
    333. errorString = tr("Unable to open file: '%1', error: %2").arg(jsonFilename).arg(jsonFile.errorString());
    334. return QJsonObject();
    335. }
    336. QByteArray bytes = jsonFile.readAll();
    337. jsonFile.close();
    338. QJsonParseError jsonParseError;
    339. QJsonDocument doc = QJsonDocument::fromJson(bytes, &jsonParseError);
    340. if (jsonParseError.error != QJsonParseError::NoError) {
    341. errorString = tr("Unable to parse json file: %1 error: %2 offset: %3").arg(jsonFilename).arg(jsonParseError.errorString()).arg(jsonParseError.offset);
    342. return QJsonObject();
    343. }
    344. if (!doc.isObject()) {
    345. errorString = tr("Root of json file is not object: %1").arg(jsonFilename);
    346. return QJsonObject();
    347. }
    348. QJsonObject jsonObject = doc.object();
    349. bool success = validateInternalQGCJsonFile(jsonObject, expectedFileType, minSupportedVersion, maxSupportedVersion, version, errorString);
    350. if (!success) {
    351. errorString = tr("Json file: '%1'. %2").arg(jsonFilename).arg(errorString);
    352. return QJsonObject();
    353. }
    354. QStringList translateKeys = _addDefaultLocKeys(jsonObject);
    355. QString context = QFileInfo(jsonFile).fileName();
    356. return _translateRoot(jsonObject, context, translateKeys);
    357. }
    358. void JsonHelper::saveQGCJsonFileHeader(QJsonObject& jsonObject,
    359. const QString& fileType,
    360. int version)
    361. {
    362. jsonObject[jsonGroundStationKey] = jsonGroundStationValue;
    363. jsonObject[jsonFileTypeKey] = fileType;
    364. jsonObject[jsonVersionKey] = version;
    365. }
    366. bool JsonHelper::loadGeoCoordinateArray(const QJsonValue& jsonValue,
    367. bool altitudeRequired,
    368. QVariantList& rgVarPoints,
    369. QString& errorString)
    370. {
    371. if (!jsonValue.isArray()) {
    372. errorString = QObject::tr("value for coordinate array is not array");
    373. return false;
    374. }
    375. QJsonArray rgJsonPoints = jsonValue.toArray();
    376. rgVarPoints.clear();
    377. for (int i=0; icount(); i++) {
    378. QGeoCoordinate coordinate;
    379. if (!JsonHelper::loadGeoCoordinate(rgJsonPoints[i], altitudeRequired, coordinate, errorString)) {
    380. return false;
    381. }
    382. rgVarPoints.append(QVariant::fromValue(coordinate));
    383. }
    384. return true;
    385. }
    386. bool JsonHelper::loadGeoCoordinateArray(const QJsonValue& jsonValue,
    387. bool altitudeRequired,
    388. QList& rgPoints,
    389. QString& errorString)
    390. {
    391. QVariantList rgVarPoints;
    392. if (!loadGeoCoordinateArray(jsonValue, altitudeRequired, rgVarPoints, errorString)) {
    393. return false;
    394. }
    395. rgPoints.clear();
    396. for (int i=0; icount(); i++) {
    397. rgPoints.append(rgVarPoints[i].value());
    398. }
    399. return true;
    400. }
    401. void JsonHelper::saveGeoCoordinateArray(const QVariantList& rgVarPoints,
    402. bool writeAltitude,
    403. QJsonValue& jsonValue)
    404. {
    405. QJsonArray rgJsonPoints;
    406. // Add all points to the array
    407. for (int i=0; icount(); i++) {
    408. QJsonValue jsonPoint;
    409. JsonHelper::saveGeoCoordinate(rgVarPoints[i].value(), writeAltitude, jsonPoint);
    410. rgJsonPoints.append(jsonPoint);
    411. }
    412. jsonValue = rgJsonPoints;
    413. }
    414. void JsonHelper::saveGeoCoordinateArray(const QList& rgPoints,
    415. bool writeAltitude,
    416. QJsonValue& jsonValue)
    417. {
    418. QVariantList rgVarPoints;
    419. for (int i=0; icount(); i++) {
    420. rgVarPoints.append(QVariant::fromValue(rgPoints[i]));
    421. }
    422. return saveGeoCoordinateArray(rgVarPoints, writeAltitude, jsonValue);
    423. }
    424. bool JsonHelper::validateKeys(const QJsonObject& jsonObject, const QList& keyInfo, QString& errorString)
    425. {
    426. QStringList keyList;
    427. QList typeList;
    428. //遍历所有的keys
    429. for (int i=0; icount(); i++) {
    430. if (keyInfo[i].required) {
    431. //如果时候需要的key,则加入到keyList
    432. keyList.append(keyInfo[i].key);
    433. }
    434. }
    435. // 检查jsonObject是否包含keyList中的键
    436. if (!validateRequiredKeys(jsonObject, keyList, errorString)) {
    437. return false;
    438. }
    439. keyList.clear();
    440. for (int i=0; icount(); i++) {
    441. keyList.append(keyInfo[i].key);
    442. typeList.append(keyInfo[i].type);
    443. }
    444. //判断键对应的类型是否正确
    445. return validateKeyTypes(jsonObject, keyList, typeList, errorString);
    446. }
    447. QString JsonHelper::_jsonValueTypeToString(QJsonValue::Type type)
    448. {
    449. const struct {
    450. QJsonValue::Type type;
    451. const char* string;
    452. } rgTypeToString[] = {
    453. { QJsonValue::Null, "NULL" },
    454. { QJsonValue::Bool, "Bool" },
    455. { QJsonValue::Double, "Double" },
    456. { QJsonValue::String, "String" },
    457. { QJsonValue::Array, "Array" },
    458. { QJsonValue::Object, "Object" },
    459. { QJsonValue::Undefined, "Undefined" },
    460. };
    461. for (size_t i=0; i<sizeof(rgTypeToString)/sizeof(rgTypeToString[0]); i++) {
    462. if (type == rgTypeToString[i].type) {
    463. return rgTypeToString[i].string;
    464. }
    465. }
    466. return QObject::tr("Unknown type: %1").arg(type);
    467. }
    468. bool JsonHelper::loadPolygon(const QJsonArray& polygonArray, QmlObjectListModel& list, QObject* parent, QString& errorString)
    469. {
    470. for (int i=0; icount(); i++) {
    471. const QJsonValue& pointValue = polygonArray[i];
    472. QGeoCoordinate pointCoord;
    473. if (!JsonHelper::loadGeoCoordinate(pointValue, false /* altitudeRequired */, pointCoord, errorString, true)) {
    474. list.clearAndDeleteContents();
    475. return false;
    476. }
    477. list.append(new QGCQGeoCoordinate(pointCoord, parent));
    478. }
    479. return true;
    480. }
    481. void JsonHelper::savePolygon(QmlObjectListModel& list, QJsonArray& polygonArray)
    482. {
    483. for (int i=0; icount(); i++) {
    484. QGeoCoordinate vertex = list.value(i)->coordinate();
    485. QJsonValue jsonValue;
    486. JsonHelper::saveGeoCoordinate(vertex, false /* writeAltitude */, jsonValue);
    487. polygonArray.append(jsonValue);
    488. }
    489. }
    490. double JsonHelper::possibleNaNJsonValue(const QJsonValue& value)
    491. {
    492. if (value.type() == QJsonValue::Null) {
    493. return std::numeric_limits<double>::quiet_NaN();
    494. } else {
    495. return value.toDouble();
    496. }
    497. }

    三.KMLDomDocument

    此类用户将任务转为kml文件。
    1. /****************************************************************************
    2. *
    3. * (c) 2009-2020 QGROUNDCONTROL PROJECT
    4. *
    5. * QGroundControl is licensed according to the terms in the file
    6. * COPYING.md in the root of the source code directory.
    7. *
    8. ****************************************************************************/
    9. #include "KMLDomDocument.h"
    10. #include "QGCPalette.h"
    11. #include "QGCApplication.h"
    12. #include "MissionCommandTree.h"
    13. #include "MissionCommandUIInfo.h"
    14. #include "FactMetaData.h"
    15. #include
    16. #include
    17. const char* KMLDomDocument::balloonStyleName = "BalloonStyle";
    18. KMLDomDocument::KMLDomDocument(const QString& name)
    19. {
    20. //生成kmlheader
    21. QDomProcessingInstruction header = createProcessingInstruction(QStringLiteral("xml"), QStringLiteral("version=\"1.0\" encoding=\"UTF-8\""));
    22. appendChild(header);
    23. //生成节点
    24. QDomElement kmlElement = createElement(QStringLiteral("kml"));
    25. kmlElement.setAttribute(QStringLiteral("xmlns"), "http://www.opengis.net/kml/2.2");
    26. //生成主节点
    27. _rootDocumentElement = createElement(QStringLiteral("Document"));
    28. kmlElement.appendChild(_rootDocumentElement);
    29. appendChild(kmlElement);
    30. //向主节点中添加name和open项
    31. addTextElement(_rootDocumentElement, "name", name);
    32. addTextElement(_rootDocumentElement, "open", "1");
    33. _addStandardStyles();
    34. }
    35. ///将坐标对象转为字符串
    36. QString KMLDomDocument::kmlCoordString(const QGeoCoordinate& coord)
    37. {
    38. double altitude = qIsNaN(coord.altitude() ) ? 0 : coord.altitude();
    39. 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));
    40. }
    41. //将颜色信息转为字符串
    42. QString KMLDomDocument::kmlColorString (const QColor& color, double opacity)
    43. {
    44. 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'));
    45. }
    46. //给kml添加style标签
    47. void KMLDomDocument::_addStandardStyles(void)
    48. {
    49. QGCPalette palette;
    50. QDomElement styleElementForBalloon = createElement("Style");
    51. styleElementForBalloon.setAttribute("id", balloonStyleName);
    52. QDomElement balloonStyleElement = createElement("BalloonStyle");
    53. addTextElement(balloonStyleElement, "text", "$[description]");
    54. styleElementForBalloon.appendChild(balloonStyleElement);
    55. _rootDocumentElement.appendChild(styleElementForBalloon);
    56. }
    57. void KMLDomDocument::addTextElement(QDomElement& parentElement, const QString &name, const QString &value)
    58. {
    59. QDomElement textElement = createElement(name);
    60. textElement.appendChild(createTextNode(value));
    61. parentElement.appendChild(textElement);
    62. }
    63. //添加lookat信息
    64. void KMLDomDocument::addLookAt(QDomElement& parentElement, const QGeoCoordinate& coord)
    65. {
    66. QDomElement lookAtElement = createElement("LookAt");
    67. addTextElement(lookAtElement, "latitude", QString::number(coord.latitude(), 'f', 7));
    68. addTextElement(lookAtElement, "longitude", QString::number(coord.longitude(), 'f', 7));
    69. addTextElement(lookAtElement, "altitude", QString::number(coord.longitude(), 'f', 2));
    70. addTextElement(lookAtElement, "heading", "-100");
    71. addTextElement(lookAtElement, "tilt", "45");
    72. addTextElement(lookAtElement, "range", "2500");
    73. parentElement.appendChild(lookAtElement);
    74. }
    75. //添加标记点标签
    76. QDomElement KMLDomDocument::addPlacemark(const QString& name, bool visible)
    77. {
    78. QDomElement placemarkElement = createElement("Placemark");
    79. _rootDocumentElement.appendChild(placemarkElement);
    80. addTextElement(placemarkElement, "name", name);
    81. addTextElement(placemarkElement, "visibility", visible ? "1" : "0");
    82. return placemarkElement;
    83. }
    84. void KMLDomDocument::appendChildToRoot(const QDomNode& child)
    85. {
    86. _rootDocumentElement.appendChild(child);
    87. }

    四.ShapeFileHelper

    这个文件提供了判断文件是否是kml类型并从kml文件中获取shapetype的功能,头文件中做了注释,cc文件中没有需要解释的地方。

    1. /****************************************************************************
    2. *
    3. * (c) 2009-2020 QGROUNDCONTROL PROJECT
    4. *
    5. * QGroundControl is licensed according to the terms in the file
    6. * COPYING.md in the root of the source code directory.
    7. *
    8. ****************************************************************************/
    9. #pragma once
    10. #include
    11. #include
    12. #include
    13. #include
    14. /// Routines for loading polygons or polylines from KML or SHP files.
    15. class ShapeFileHelper : public QObject
    16. {
    17. Q_OBJECT
    18. public:
    19. enum ShapeType {
    20. Polygon, //多边形
    21. Polyline, //多线段
    22. Error
    23. };
    24. Q_ENUM(ShapeType)
    25. //关于Q_PROPERTY https://www.cnblogs.com/wanghongyang/p/15233642.html
    26. ///< File filter list for load/save KML file dialogs 载入/保存KML文件对话框的文件筛选器列表
    27. Q_PROPERTY(QStringList fileDialogKMLFilters READ fileDialogKMLFilters CONSTANT)
    28. ///< File filter list for load/save shape file dialogs 加载/保存形状文件对话框的文件筛选器列表
    29. Q_PROPERTY(QStringList fileDialogKMLOrSHPFilters READ fileDialogKMLOrSHPFilters CONSTANT)
    30. // 关于Q_INVOKABLE https://blog.csdn.net/qq78442761/article/details/109861560
    31. /// Loads the file and returns shape type and error string in a variant array. 加载文件并在变量数组中返回形状类型和错误字符串
    32. /// ShapeType is in index 0, error string is in index 1. ShapeType在索引0,错误字符串在索引1。
    33. Q_INVOKABLE static QVariantList determineShapeType(const QString& file);
    34. QStringList fileDialogKMLFilters(void) const;
    35. QStringList fileDialogKMLOrSHPFilters(void) const;
    36. static ShapeType determineShapeType(const QString& file, QString& errorString);
    37. static bool loadPolygonFromFile(const QString& file, QList& vertices, QString& errorString);
    38. static bool loadPolylineFromFile(const QString& file, QList& coords, QString& errorString);
    39. private:
    40. static bool _fileIsKML(const QString& file, QString& errorString);
    41. static const char* _errorPrefix;
    42. };

    cc文件

    1. ShapeFileHelper::ShapeType ShapeFileHelper::determineShapeType(const QString& file, QString& errorString)
    2. {
    3. ShapeType shapeType = Error;
    4. errorString.clear();
    5. //判断是合法的kml文件
    6. bool fileIsKML = _fileIsKML(file, errorString);
    7. if (errorString.isEmpty()) {
    8. if (fileIsKML) {
    9. //判断file的类型选择不同的方式去加载数据
    10. shapeType = KMLHelper::determineShapeType(file, errorString);
    11. } else {
    12. shapeType = SHPFileHelper::determineShapeType(file, errorString);
    13. }
    14. }
    15. return shapeType;
    16. }

    五.SHPFileHelper

    和第四个类似,此类用于处理shp类型文件的读取。对cc文件添加了部分注释

    1. /****************************************************************************
    2. *
    3. * (c) 2009-2020 QGROUNDCONTROL PROJECT
    4. *
    5. * QGroundControl is licensed according to the terms in the file
    6. * COPYING.md in the root of the source code directory.
    7. *
    8. ****************************************************************************/
    9. #include "SHPFileHelper.h"
    10. #include "QGCGeo.h"
    11. #include
    12. #include
    13. #include
    14. #include
    15. const char* SHPFileHelper::_errorPrefix = QT_TR_NOOP("SHP file load failed. %1");
    16. /// Validates the specified SHP file is truly a SHP file and is in the format we understand.
    17. /// 验证指定的SHP文件确实是一个SHP文件,并且是我们所理解的格式
    18. /// @param utmZone[out] Zone for UTM shape, 0 for lat/lon shape
    19. /// @param utmSouthernHemisphere[out] true/false for UTM hemisphere
    20. /// @return true: Valid supported SHP file found, false: Invalid or unsupported file found
    21. bool SHPFileHelper::_validateSHPFiles(const QString& shpFile, int* utmZone, bool* utmSouthernHemisphere, QString& errorString)
    22. {
    23. *utmZone = 0;
    24. errorString.clear();
    25. if (shpFile.endsWith(QStringLiteral(".shp"))) {
    26. //将文件重新命名并添加后缀.prj
    27. QString prjFilename = shpFile.left(shpFile.length() - 4) + QStringLiteral(".prj");
    28. QFile prjFile(prjFilename);
    29. if (prjFile.exists()) {
    30. //如果文件存在,以只读方式打开
    31. if (prjFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
    32. //以文件流方式获取文件内容
    33. QTextStream strm(&prjFile);
    34. QString line = strm.readLine();
    35. if (line.startsWith(QStringLiteral("GEOGCS[\"GCS_WGS_1984\","))) {
    36. *utmZone = 0;
    37. *utmSouthernHemisphere = false;
    38. } else if (line.startsWith(QStringLiteral("PROJCS[\"WGS_1984_UTM_Zone_"))) {
    39. //利用正则表达式
    40. QRegularExpression regEx(QStringLiteral("^PROJCS\\[\"WGS_1984_UTM_Zone_(\\d+){1,2}([NS]{1})"));
    41. QRegularExpressionMatch regExMatch = regEx.match(line);
    42. QStringList rgCapture = regExMatch.capturedTexts();
    43. if (rgCapture.count() == 3) {
    44. //获取对应信息
    45. int zone = rgCapture[1].toInt();
    46. if (zone >= 1 && zone <= 60) {
    47. *utmZone = zone;
    48. *utmSouthernHemisphere = rgCapture[2] == QStringLiteral("S");
    49. }
    50. }
    51. if (*utmZone == 0) {
    52. errorString = QString(_errorPrefix).arg(tr("UTM projection is not in supported format. Must be PROJCS[\"WGS_1984_UTM_Zone_##N/S"));
    53. }
    54. } else {
    55. errorString = QString(_errorPrefix).arg(tr("Only WGS84 or UTM projections are supported."));
    56. }
    57. } else {
    58. errorString = QString(_errorPrefix).arg(tr("PRJ file open failed: %1").arg(prjFile.errorString()));
    59. }
    60. } else {
    61. errorString = QString(_errorPrefix).arg(tr("File not found: %1").arg(prjFilename));
    62. }
    63. } else {
    64. errorString = QString(_errorPrefix).arg(tr("File is not a .shp file: %1").arg(shpFile));
    65. }
    66. return errorString.isEmpty();
    67. }
    68. /// @param utmZone[out] Zone for UTM shape, 0 for lat/lon shape
    69. /// @param utmSouthernHemisphere[out] true/false for UTM hemisphere
    70. SHPHandle SHPFileHelper::_loadShape(const QString& shpFile, int* utmZone, bool* utmSouthernHemisphere, QString& errorString)
    71. {
    72. SHPHandle shpHandle = Q_NULLPTR;
    73. errorString.clear();
    74. //如果验证成功
    75. if (_validateSHPFiles(shpFile, utmZone, utmSouthernHemisphere, errorString)) {
    76. if (!(shpHandle = SHPOpen(shpFile.toUtf8(), "rb"))) {
    77. errorString = QString(_errorPrefix).arg(tr("SHPOpen failed."));
    78. }
    79. }
    80. return shpHandle;
    81. }
    82. ShapeFileHelper::ShapeType SHPFileHelper::determineShapeType(const QString& shpFile, QString& errorString)
    83. {
    84. ShapeFileHelper::ShapeType shapeType = ShapeFileHelper::Error;
    85. errorString.clear();
    86. int utmZone;
    87. bool utmSouthernHemisphere;
    88. SHPHandle shpHandle = SHPFileHelper::_loadShape(shpFile, &utmZone, &utmSouthernHemisphere, errorString);
    89. if (errorString.isEmpty()) {
    90. int cEntities, type;
    91. //获取shp信息
    92. SHPGetInfo(shpHandle, &cEntities /* pnEntities */, &type, Q_NULLPTR /* padfMinBound */, Q_NULLPTR /* padfMaxBound */);
    93. qDebug() << "SHPGetInfo" << shpHandle << cEntities << type;
    94. if (cEntities != 1) {
    95. errorString = QString(_errorPrefix).arg(tr("More than one entity found."));
    96. } else if (type == SHPT_POLYGON) {
    97. shapeType = ShapeFileHelper::Polygon;
    98. } else {
    99. errorString = QString(_errorPrefix).arg(tr("No supported types found."));
    100. }
    101. }
    102. SHPClose(shpHandle);
    103. return shapeType;
    104. }
    105. bool SHPFileHelper::loadPolygonFromFile(const QString& shpFile, QList& vertices, QString& errorString)
    106. {
    107. int utmZone = 0;
    108. bool utmSouthernHemisphere;
    109. double vertexFilterMeters = 5;
    110. SHPHandle shpHandle = Q_NULLPTR;
    111. SHPObject* shpObject = Q_NULLPTR;
    112. errorString.clear();
    113. vertices.clear();
    114. shpHandle = SHPFileHelper::_loadShape(shpFile, &utmZone, &utmSouthernHemisphere, errorString);
    115. if (!errorString.isEmpty()) {
    116. goto Error;
    117. }
    118. int cEntities, shapeType;
    119. SHPGetInfo(shpHandle, &cEntities, &shapeType, Q_NULLPTR /* padfMinBound */, Q_NULLPTR /* padfMaxBound */);
    120. if (shapeType != SHPT_POLYGON) {
    121. errorString = QString(_errorPrefix).arg(tr("File does not contain a polygon."));
    122. goto Error;
    123. }
    124. shpObject = SHPReadObject(shpHandle, 0);
    125. if (shpObject->nParts != 1) {
    126. errorString = QString(_errorPrefix).arg(tr("Only single part polygons are supported."));
    127. goto Error;
    128. }
    129. for (int i=0; inVertices; i++) {
    130. QGeoCoordinate coord;
    131. if (!utmZone || !convertUTMToGeo(shpObject->padfX[i], shpObject->padfY[i], utmZone, utmSouthernHemisphere, coord)) {
    132. coord.setLatitude(shpObject->padfY[i]);
    133. coord.setLongitude(shpObject->padfX[i]);
    134. }
    135. vertices.append(coord);
    136. }
    137. // Filter last vertex such that it differs from first
    138. {
    139. QGeoCoordinate firstVertex = vertices[0];
    140. while (vertices.count() > 3 && vertices.last().distanceTo(firstVertex) < vertexFilterMeters) {
    141. vertices.removeLast();
    142. }
    143. }
    144. // Filter vertex distances to be larger than 1 meter apart
    145. {
    146. int i = 0;
    147. while (i < vertices.count() - 2) {
    148. if (vertices[i].distanceTo(vertices[i+1]) < vertexFilterMeters) {
    149. vertices.removeAt(i+1);
    150. } else {
    151. i++;
    152. }
    153. }
    154. }
    155. Error:
    156. if (shpObject) {
    157. SHPDestroyObject(shpObject);
    158. }
    159. if (shpHandle) {
    160. SHPClose(shpHandle);
    161. }
    162. return errorString.isEmpty();
    163. }

    六.KMLHelper

    此文件用于判断shape文件的类型,是多变形还是多线段。然后根据不同类型加载数据到容器中

    头文件如下

    1. /****************************************************************************
    2. *
    3. * (c) 2009-2020 QGROUNDCONTROL PROJECT
    4. *
    5. * QGroundControl is licensed according to the terms in the file
    6. * COPYING.md in the root of the source code directory.
    7. *
    8. ****************************************************************************/
    9. #pragma once
    10. #include
    11. #include
    12. #include
    13. #include
    14. #include "ShapeFileHelper.h"
    15. class KMLHelper : public QObject
    16. {
    17. Q_OBJECT
    18. public:
    19. //决定shape的类型
    20. static ShapeFileHelper::ShapeType determineShapeType(const QString& kmlFile, QString& errorString);
    21. //加载多边形数据
    22. static bool loadPolygonFromFile(const QString& kmlFile, QList& vertices, QString& errorString);
    23. //加载多线段数据
    24. static bool loadPolylineFromFile(const QString& kmlFile, QList& coords, QString& errorString);
    25. private:
    26. static QDomDocument _loadFile(const QString& kmlFile, QString& errorString);
    27. static const char* _errorPrefix;
    28. };

    cc文件如下:

    1. /****************************************************************************
    2. *
    3. * (c) 2009-2020 QGROUNDCONTROL PROJECT
    4. *
    5. * QGroundControl is licensed according to the terms in the file
    6. * COPYING.md in the root of the source code directory.
    7. *
    8. ****************************************************************************/
    9. #include "KMLHelper.h"
    10. #include
    11. #include
    12. const char* KMLHelper::_errorPrefix = QT_TR_NOOP("KML file load failed. %1");
    13. QDomDocument KMLHelper::_loadFile(const QString& kmlFile, QString& errorString)
    14. {
    15. QFile file(kmlFile);
    16. errorString.clear();
    17. if (!file.exists()) {
    18. errorString = QString(_errorPrefix).arg(tr("File not found: %1").arg(kmlFile));
    19. return QDomDocument();
    20. }
    21. if (!file.open(QIODevice::ReadOnly)) {
    22. errorString = QString(_errorPrefix).arg(tr("Unable to open file: %1 error: $%2").arg(kmlFile).arg(file.errorString()));
    23. return QDomDocument();
    24. }
    25. QDomDocument doc;
    26. QString errorMessage;
    27. int errorLine;
    28. if (!doc.setContent(&file, &errorMessage, &errorLine)) {
    29. errorString = QString(_errorPrefix).arg(tr("Unable to parse KML file: %1 error: %2 line: %3").arg(kmlFile).arg(errorMessage).arg(errorLine));
    30. return QDomDocument();
    31. }
    32. return doc;
    33. }
    34. ShapeFileHelper::ShapeType KMLHelper::determineShapeType(const QString& kmlFile, QString& errorString)
    35. {
    36. //加载kml文件
    37. QDomDocument domDocument = KMLHelper::_loadFile(kmlFile, errorString);
    38. if (!errorString.isEmpty()) {
    39. return ShapeFileHelper::Error;
    40. }
    41. //如果文件中包含Polygon元素则为多边形数据
    42. QDomNodeList rgNodes = domDocument.elementsByTagName("Polygon");
    43. if (rgNodes.count()) {
    44. return ShapeFileHelper::Polygon;
    45. }
    46. //如果文件中包含LineString元素则为多线段数据
    47. rgNodes = domDocument.elementsByTagName("LineString");
    48. if (rgNodes.count()) {
    49. return ShapeFileHelper::Polyline;
    50. }
    51. errorString = QString(_errorPrefix).arg(tr("No supported type found in KML file."));
    52. return ShapeFileHelper::Error;
    53. }
    54. bool KMLHelper::loadPolygonFromFile(const QString& kmlFile, QList& vertices, QString& errorString)
    55. {
    56. errorString.clear();
    57. vertices.clear();
    58. //加载文件
    59. QDomDocument domDocument = KMLHelper::_loadFile(kmlFile, errorString);
    60. if (!errorString.isEmpty()) {
    61. return false;
    62. }
    63. //找到Polygon
    64. QDomNodeList rgNodes = domDocument.elementsByTagName("Polygon");
    65. if (rgNodes.count() == 0) {
    66. errorString = QString(_errorPrefix).arg(tr("Unable to find Polygon node in KML"));
    67. return false;
    68. }
    69. //找到outerBoundaryIs/LinearRing/coordinates
    70. QDomNode coordinatesNode = rgNodes.item(0).namedItem("outerBoundaryIs").namedItem("LinearRing").namedItem("coordinates");
    71. if (coordinatesNode.isNull()) {
    72. errorString = QString(_errorPrefix).arg(tr("Internal error: Unable to find coordinates node in KML"));
    73. return false;
    74. }
    75. //简体化
    76. QString coordinatesString = coordinatesNode.toElement().text().simplified();
    77. //空格分成数组
    78. QStringList rgCoordinateStrings = coordinatesString.split(" ");
    79. QList rgCoords;
    80. for (int i=0; icount()-1; i++) {
    81. QString coordinateString = rgCoordinateStrings[i];
    82. QStringList rgValueStrings = coordinateString.split(",");
    83. //设置经纬度
    84. QGeoCoordinate coord;
    85. coord.setLongitude(rgValueStrings[0].toDouble());
    86. coord.setLatitude(rgValueStrings[1].toDouble());
    87. rgCoords.append(coord);
    88. }
    89. // 确定缠绕 反转是否需要 qgc需要顺时针环绕
    90. // Determine winding, reverse if needed. QGC wants clockwise winding
    91. double sum = 0;
    92. for (int i=0; icount(); i++) {
    93. QGeoCoordinate coord1 = rgCoords[i];
    94. QGeoCoordinate coord2 = (i == rgCoords.count() - 1) ? rgCoords[0] : rgCoords[i+1];
    95. sum += (coord2.longitude() - coord1.longitude()) * (coord2.latitude() + coord1.latitude());
    96. }
    97. bool reverse = sum < 0.0;
    98. //需要反转
    99. if (reverse) {
    100. QList rgReversed;
    101. for (int i=0; icount(); i++) {
    102. rgReversed.prepend(rgCoords[i]);
    103. }
    104. rgCoords = rgReversed;
    105. }
    106. vertices = rgCoords;
    107. return true;
    108. }
    109. bool KMLHelper::loadPolylineFromFile(const QString& kmlFile, QList& coords, QString& errorString)
    110. {
    111. errorString.clear();
    112. coords.clear();
    113. //加载文件
    114. QDomDocument domDocument = KMLHelper::_loadFile(kmlFile, errorString);
    115. if (!errorString.isEmpty()) {
    116. return false;
    117. }
    118. //找到LineString元素
    119. QDomNodeList rgNodes = domDocument.elementsByTagName("LineString");
    120. if (rgNodes.count() == 0) {
    121. errorString = QString(_errorPrefix).arg(tr("Unable to find LineString node in KML"));
    122. return false;
    123. }
    124. //找到coordinates元素
    125. QDomNode coordinatesNode = rgNodes.item(0).namedItem("coordinates");
    126. if (coordinatesNode.isNull()) {
    127. errorString = QString(_errorPrefix).arg(tr("Internal error: Unable to find coordinates node in KML"));
    128. return false;
    129. }
    130. QString coordinatesString = coordinatesNode.toElement().text().simplified();
    131. QStringList rgCoordinateStrings = coordinatesString.split(" ");
    132. //添加
    133. QList rgCoords;
    134. for (int i=0; icount()-1; i++) {
    135. QString coordinateString = rgCoordinateStrings[i];
    136. QStringList rgValueStrings = coordinateString.split(",");
    137. QGeoCoordinate coord;
    138. coord.setLongitude(rgValueStrings[0].toDouble());
    139. coord.setLatitude(rgValueStrings[1].toDouble());
    140. rgCoords.append(coord);
    141. }
    142. coords = rgCoords;
    143. return true;
    144. }

    七.LogCompressor

    此类用于将log文件压缩为csv文件

    头文件注释如下:

    1. /****************************************************************************
    2. *
    3. * (c) 2009-2020 QGROUNDCONTROL PROJECT
    4. *
    5. * QGroundControl is licensed according to the terms in the file
    6. * COPYING.md in the root of the source code directory.
    7. *
    8. ****************************************************************************/
    9. #pragma once
    10. #include
    11. /**
    12. * @file
    13. * @brief Declaration of class LogCompressor.
    14. * This class reads in a file containing messages and translates it into a tab-delimited CSV file.
    15. * 该类读入一个包含信息的文件,并将其转换为一个以制表符分隔的CSV文件。
    16. * @author Lorenz Meier
    17. */
    18. class LogCompressor : public QThread
    19. {
    20. Q_OBJECT
    21. public:
    22. /** @brief Create the log compressor. It will only get active upon calling startCompression() */
    23. ///创建日志压缩器。 它只有在调用startCompression()时才会激活
    24. LogCompressor(QString logFileName, QString outFileName="", QString delimiter="\t");
    25. /** @brief Start the compression of a raw, line-based logfile into a CSV file */
    26. ///开始将原始的、基于行的日志文件压缩到CSV文件中
    27. void startCompression(bool holeFilling=false);
    28. ///压缩是否完成
    29. bool isFinished() const;
    30. ///获取当前行
    31. int getCurrentLine() const;
    32. protected:
    33. void run(); ///< This function actually performs the compression. It's an overloaded function from QThread 这个函数实际执行压缩。 它是一个来自QThread的重载函数
    34. QString logFileName; ///< The input file name. 输入文件
    35. QString outFileName; ///< The output file name. If blank defaults to logFileName 输出文件
    36. bool running; ///< True when the startCompression() function is operating. 运行状态
    37. int currentDataLine; ///< The current line of data that is being processed. Only relevant when running==true 正在处理的当前数据行。 只在运行==true时相关
    38. QString delimiter; ///< Delimiter between fields in the output file. Defaults to tab ('\t') 分隔符
    39. bool holeFillingEnabled; ///< Enables the filling of holes in the dataset with the previous value (or NaN if none exists) 是否启用将前一个值填充数据集中的空缺(如果不存在则为NaN)
    40. signals:
    41. /** @brief This signal is emitted once a logfile has been finished writing
    42. * @param fileName The name of the output (CSV) file
    43. */
    44. void finishedFile(QString fileName);
    45. /// This signal is connected to QGCApplication::showCriticalMessage to show critical errors which come from the thread.
    46. /// 该信号连接到QGCApplication::showCriticalMessage,以显示来自线程的严重错误。
    47. /// There is no need for clients to connect to this signal.
    48. /// 客户端不需要连接到这个信号。
    49. void logProcessingCriticalError(const QString& title, const QString& msg);
    50. private:
    51. void _signalCriticalError(const QString& msg);
    52. };

    cc文件注释如下:

    1. /****************************************************************************
    2. *
    3. * (c) 2009-2020 QGROUNDCONTROL PROJECT
    4. *
    5. * QGroundControl is licensed according to the terms in the file
    6. * COPYING.md in the root of the source code directory.
    7. *
    8. ****************************************************************************/
    9. /**
    10. * @file
    11. * @brief Implementation of class LogCompressor.
    12. * This class reads in a file containing messages and translates it into a tab-delimited CSV file.
    13. * @author Lorenz Meier
    14. */
    15. #include "LogCompressor.h"
    16. #include "QGCApplication.h"
    17. #include
    18. #include
    19. #include
    20. #include
    21. #include
    22. #include
    23. #include
    24. #include
    25. /**
    26. * Initializes all the variables necessary for a compression run. This won't actually happen
    27. * until startCompression(...) is called.
    28. */
    29. //当调用startCompression是该初始化方法被调用
    30. LogCompressor::LogCompressor(QString logFileName, QString outFileName, QString delimiter) :
    31. logFileName(logFileName),
    32. outFileName(outFileName),
    33. running(true),
    34. currentDataLine(0),
    35. delimiter(delimiter),
    36. holeFillingEnabled(true)
    37. {
    38. connect(this, &LogCompressor::logProcessingCriticalError, qgcApp(), &QGCApplication::criticalMessageBoxOnMainThread);
    39. }
    40. void LogCompressor::run()
    41. {
    42. // Verify that the input file is useable
    43. QFile infile(logFileName);
    44. if (!infile.exists() || !infile.open(QIODevice::ReadOnly | QIODevice::Text)) {
    45. _signalCriticalError(tr("Log Compressor: Cannot start/compress log file, since input file %1 is not readable").arg(QFileInfo(infile.fileName()).absoluteFilePath()));
    46. return;
    47. }
    48. // outFileName = logFileName;
    49. QString outFileName;
    50. //QFileInfo(infile.fileName()).absoluteFilePath() 可以获取到该文件的绝对路径
    51. QStringList parts = QFileInfo(infile.fileName()).absoluteFilePath().split(".", Qt::SkipEmptyParts);
    52. //将文件名加_compressed
    53. parts.replace(0, parts.first() + "_compressed");
    54. //后缀改为txt
    55. parts.replace(parts.size()-1, "txt");
    56. //拼接
    57. outFileName = parts.join(".");
    58. // Verify that the output file is useable
    59. //打开文件不存在则创建
    60. QFile outTmpFile(outFileName);
    61. if (!outTmpFile.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) {
    62. _signalCriticalError(tr("Log Compressor: Cannot start/compress log file, since output file %1 is not writable").arg(QFileInfo(outTmpFile.fileName()).absoluteFilePath()));
    63. return;
    64. }
    65. // First we search the input file through keySearchLimit number of lines
    66. // looking for variables. This is necessary before CSV files require
    67. // the same number of fields for every line.
    68. //首先,我们通过keySearchLimit搜索变量的行数来搜索输入文件。 在CSV文件要求每行有相同数量的字段之前,这是必要的。
    69. const unsigned int keySearchLimit = 15000;
    70. unsigned int keyCounter = 0;
    71. QTextStream in(&infile);
    72. QMapint> messageMap;
    73. //如果没到文件尾并且key搜索数量小于limit
    74. //此方法搜索了文件中所有行中第三个位置的名称放入到map中
    75. while (!in.atEnd() && keyCounter < keySearchLimit) {
    76. //通过delimiter分割行取得名称
    77. QString messageName = in.readLine().split(delimiter).at(2);
    78. //将名称作为key存入map中
    79. messageMap.insert(messageName, 0);
    80. ++keyCounter;
    81. }
    82. // Now update each key with its index in the output string. These are
    83. // all offset by one to account for the first field: timestamp_ms.
    84. // 现在用输出字符串中的索引更新每个键。这些都被偏移一个单位,以负责第一个字段:timestamp_ms。
    85. QMapint>::iterator i = messageMap.begin();
    86. int j;
    87. //为每个字段的值从1开始递增的赋值
    88. for (i = messageMap.begin(), j = 1; i != messageMap.end(); ++i, ++j) {
    89. i.value() = j;
    90. }
    91. // Open the output file and write the header line to it
    92. // 获取map所有的keys
    93. QStringList headerList(messageMap.keys());
    94. //打开输出文件并将标题行写入其中
    95. QString headerLine = "timestamp_ms" + delimiter + headerList.join(delimiter) + "\n";
    96. // Clean header names from symbols Matlab considers as Latex syntax
    97. headerLine = headerLine.replace("timestamp", "TIMESTAMP");
    98. headerLine = headerLine.replace(":", "");
    99. headerLine = headerLine.replace("_", "");
    100. headerLine = headerLine.replace(".", "");
    101. // 写入
    102. // QString的toLocal8bit和toLatin1都可以将QString转化为QByteArray,但是两者的区别在于编码的不同:
    103. // toLocal8Bit:Unicode编码
    104. // toLatin1:ASCII编码
    105. outTmpFile.write(headerLine.toLocal8Bit());
    106. _signalCriticalError(tr("Log compressor: Dataset contains dimensions: ") + headerLine);
    107. // Template list stores a list for populating with data as it's parsed from messages.
    108. // 模板列表存储一个列表,用于在从消息解析数据时填充数据。
    109. QStringList templateList;
    110. for (int i = 0; i < headerList.size() + 1; ++i) {
    111. templateList << (holeFillingEnabled?"NaN":"");
    112. }
    113. // // Reset our position in the input file before we start the main processing loop.
    114. // in.seek(0);
    115. // // Search through all lines and build a list of unique timestamps
    116. // QMap timestampMap;
    117. // while (!in.atEnd()) {
    118. // quint64 timestamp = in.readLine().split(delimiter).at(0).toULongLong();
    119. // timestampMap.insert(timestamp, templateList);
    120. // }
    121. // Jump back to start of file
    122. // 跳到输入文件的头
    123. in.seek(0);
    124. // Map of final output lines, key is time
    125. // map的最终输出行,key是时间
    126. QMap timestampMap;
    127. // Run through the whole file and fill map of timestamps
    128. while (!in.atEnd()) {
    129. QStringList newLine = in.readLine().split(delimiter);
    130. //获取时间
    131. quint64 timestamp = newLine.at(0).toULongLong();
    132. // Check if timestamp does exist - if not, add it
    133. // 不存在就添加 值为刚刚构建的模版数据 templateList
    134. if (!timestampMap.contains(timestamp)) {
    135. timestampMap.insert(timestamp, templateList);
    136. }
    137. //获取到模版列表 这里为所有key的对应的初始数据 templateList << (holeFillingEnabled?"NaN":"")
    138. QStringList list = timestampMap.value(timestamp);
    139. //获取当前行的数据名称 也就是header中管道每一个key
    140. QString currentDataName = newLine.at(2);
    141. //获取key对应的值
    142. QString currentDataValue = newLine.at(3);
    143. //修改对应元素的值 messageMap中的值存放的是header中所有的keys
    144. list.replace(messageMap.value(currentDataName), currentDataValue);
    145. timestampMap.insert(timestamp, list);
    146. }
    147. int lineCounter = 0;
    148. QStringList lastList = timestampMap.values().at(1);
    149. foreach (QStringList list, timestampMap.values()) {
    150. // Write this current time set out to the file
    151. // only do so from the 2nd line on, since the first
    152. // line could be incomplete
    153. // 将当前时间设置写入文件,从第二行开始,因为第一行可能不完整
    154. if (lineCounter > 1) {
    155. // Set the timestamp
    156. //设置第0个位置为时间信息
    157. list.replace(0,QString("%1").arg(timestampMap.keys().at(lineCounter)));
    158. // Fill holes if necessary
    159. if (holeFillingEnabled) {
    160. int index = 0;
    161. //如果此行数据缺失就用上行数据代替
    162. foreach (const QString& str, list) {
    163. if (str == "" || str == "NaN") {
    164. list.replace(index, lastList.at(index));
    165. }
    166. index++;
    167. }
    168. }
    169. // Set last list
    170. lastList = list;
    171. // Write data columns
    172. QString output = list.join(delimiter) + "\n";
    173. //写入输出文件
    174. outTmpFile.write(output.toLocal8Bit());
    175. }
    176. lineCounter++;
    177. }
    178. // We're now done with the source file
    179. infile.close();
    180. // Clean up and update the status before we return.
    181. currentDataLine = 0;
    182. emit finishedFile(outFileName);
    183. running = false;
    184. }
    185. /**
    186. * @param holeFilling If hole filling is enabled, the compressor tries to fill empty data fields with previous
    187. * values from the same variable (or NaN, if no previous value existed)
    188. */
    189. void LogCompressor::startCompression(bool holeFilling)
    190. {
    191. holeFillingEnabled = holeFilling;
    192. start();
    193. }
    194. bool LogCompressor::isFinished() const
    195. {
    196. return !running;
    197. }
    198. int LogCompressor::getCurrentLine() const
    199. {
    200. return currentDataLine;
    201. }
    202. void LogCompressor::_signalCriticalError(const QString& msg)
    203. {
    204. emit logProcessingCriticalError(tr("Log Compressor"), msg);
    205. }

    总结

    好了,问了文章篇幅不要太长,这里就解释这几个文件吧,都是一些工具类,包括命令行的读取,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