目录

前言

一.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

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

/// @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 argvchar*          argv[],                 ///< command line argumentsCmdLineOpt_t*  prgOpts,                ///< command line optionssize_t         cOpts,                  ///< count of command line optionsbool           removeParsedOptions)    ///< true: remove parsed option from argc/argv
{// Start with all options off// 先将所有的option的find置为falsefor (size_t iOption=0; iOption<cOpts; iOption++) {*prgOpts[iOption].optionFound = false;}//遍历所有输入参数for (int iArg=1; iArg<argc; iArg++) {for (size_t iOption=0; iOption<cOpts; 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<argc-1; iShift++) {//删掉argv[iShift] = argv[iShift+1];}argc--;iArg--;}}}}
}

二.JsonHelper

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

/****************************************************************************** (c) 2009-2020 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>** QGroundControl is licensed according to the terms in the file* COPYING.md in the root of the source code directory.*****************************************************************************/#pragma once#include <QJsonObject>
#include <QVariantList>
#include <QGeoCoordinate>
#include <QCoreApplication>/// @file
/// @author Don Gagne <don@thegagnes.com>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,       ///< filenameQJsonDocument&       jsonDoc,        ///< returned json documentQString&             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 bytesQJsonDocument&       jsonDoc,        ///< returned json documentQString&             errorString);   ///< error on parse failure/// Saves the standard file header the json object 保存标准的qgc jason头static void saveQGCJsonFileHeader(QJsonObject&      jsonObject, ///< root json objectconst QString&    fileType,   ///< file type for fileint               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 setstatic 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 setstatic bool validateInternalQGCJsonFile(const QJsonObject&  jsonObject,             ///< json object to validateconst QString&      expectedFileType,       ///< correct file type for fileint                 minSupportedVersion,    ///< minimum supported versionint                 maxSupportedVersion,    ///< maximum supported major versionint                 &version,               ///< returned file versionQString&            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 openconst QString&      expectedFileType,    ///< correct file type for fileint                 minSupportedVersion, ///< minimum supported versionint                 maxSupportedVersion, ///< maximum supported major versionint                 &version,            ///< returned file versionQString&            errorString);        ///< returned error string if validation fails/// Validates that the specified keys are in the object 验证指定的键是否在对象中/// @return false: validation failed, errorString setstatic bool validateRequiredKeys(const QJsonObject& jsonObject, ///< json object to validateconst QStringList& keys,       ///< keys which are required to be presentQString& errorString);         ///< returned error string if validation fails/// Validates the types of specified keys are in the object 验证对象中指定键的类型/// @return false: validation failed, errorString setstatic bool validateKeyTypes(const QJsonObject& jsonObject,         ///< json object to validateconst QStringList& keys,               ///< keys to validateconst QList<QJsonValue::Type>& types,  ///< required type for each key, QJsonValue::Null specifies double with possible NaNQString& errorString);                 ///< returned error string if validation failstypedef struct {const char*         key;        ///< json key nameQJsonValue::Type    type;       ///< required type for key, QJsonValue::Null specifies double with possible NaNbool                required;   ///< true: key must be present} KeyValidateInfo;static bool validateKeys(const QJsonObject& jsonObject, const QList<KeyValidateInfo>& keyInfo, QString& errorString);/// Loads a QGeoCoordinate 加载位置坐标数据 存储格式为[ lat, lon, alt ]///     Stored as array [ lat, lon, alt ]/// @return false: validation failedstatic bool loadGeoCoordinate(const QJsonValue& jsonValue,              ///< json value to load frombool              altitudeRequired,       ///< true: altitude must be specifiedQGeoCoordinate&   coordinate,             ///< returned QGeoCordinateQString&          errorString,            ///< returned error string if load failurebool              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 savebool                  writeAltitude,  ///< true: write altitude to jsonQJsonValue&           jsonValue);     ///< json value to save to/// Loads a QGeoCoordinate///     Stored as array [ lon, lat, alt ]/// @return false: validation failedstatic bool loadGeoJsonCoordinate(const QJsonValue& jsonValue,          ///< json value to load frombool              altitudeRequired,   ///< true: altitude must be specifiedQGeoCoordinate&   coordinate,         ///< returned QGeoCordinateQString&          errorString);       ///< returned error string if load failure/// Saves a QGeoCoordinate///     Stored as array [ lon, lat, alt ]static void saveGeoJsonCoordinate(const QGeoCoordinate& coordinate,     ///< QGeoCoordinate to savebool                  writeAltitude,  ///< true: write altitude to jsonQJsonValue&           jsonValue);     ///< json value to save to/// Loads a polygon from an array 加载多边形static bool loadPolygon(const QJsonArray&   polygonArray,   ///< Array of coordinatesQmlObjectListModel& list,           ///< Empty list to add vertices toQObject*            parent,         ///< parent for newly allocated QGCQGeoCoordinatesQString&            errorString);   ///< returned error string if load failure/// Loads a list of QGeoCoordinates from a json array  加载位置坐标数据数组形式/// @return false: validation failedstatic bool loadGeoCoordinateArray(const QJsonValue&    jsonValue,              ///< json value which contains pointsbool                 altitudeRequired,       ///< true: altitude field must be specifiedQVariantList&        rgVarPoints,            ///< returned pointsQString&             errorString);           ///< returned error string if load failurestatic bool loadGeoCoordinateArray(const QJsonValue&        jsonValue,          ///< json value which contains pointsbool                     altitudeRequired,   ///< true: altitude field must be specifiedQList<QGeoCoordinate>&   rgPoints,           ///< returned pointsQString&                 errorString);       ///< returned error string if load failure/// Saves a list of QGeoCoordinates to a json array 保存位置坐标数据数组形式static void saveGeoCoordinateArray(const QVariantList&  rgVarPoints,            ///< points to savebool                 writeAltitude,          ///< true: write altitide valueQJsonValue&          jsonValue);             ///< json value to save tostatic void saveGeoCoordinateArray(const QList<QGeoCoordinate>& rgPoints,       ///< points to savebool                         writeAltitude,  ///< true: write altitide valueQJsonValue&                  jsonValue);     ///< json value to save to/// Saves a polygon to a json array 保存多边形数据static void savePolygon(QmlObjectListModel& list,           ///< List which contains verticesQJsonArray&         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 <http://www.qgroundcontrol.org>** 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 <QJsonArray>
#include <QJsonParseError>
#include <QObject>
#include <QRegularExpression>
#include <QRegularExpressionMatch>
#include <QFile>
#include <QTranslator>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;//遍历所有的keysforeach(const QString& key, keys) {//如果jsonObject不包含keyif (!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或者nullif (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<QJsonValue::Type>& types, QString& errorString)
{for (int i=0; i<types.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的值为NaNcontinue;}//判断类型不相等报错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,转为QJsonDocumentjsonDoc = 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 验证所需的keyQList<JsonHelper::KeyValidateInfo> requiredKeys = {{ jsonFileTypeKey,       QJsonValue::String, true },{ jsonVersionKey,        QJsonValue::Double, true },};//验证requiredKeysif (!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 keysQList<JsonHelper::KeyValidateInfo> 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不包含_arrayIDKeysif (!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所有的keysfor (const QString& key: jsonObject.keys()) {//如果keys是string类型if (jsonObject[key].isString()) {//转为stringQString locString = jsonObject[key].toString();//translateKeys如果包含keyif (translateKeys.contains(key)) {QString disambiguation;QString disambiguationPrefix("#loc.disambiguation#");//如果locString以#loc.disambiguation#开头if (locString.startsWith(disambiguationPrefix)) {//则截取#loc.disambiguation#之后的部分为新的locStringlocString = 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<jsonArray.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<rgJsonPoints.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<QGeoCoordinate>&  rgPoints,QString&                errorString)
{QVariantList rgVarPoints;if (!loadGeoCoordinateArray(jsonValue, altitudeRequired, rgVarPoints, errorString)) {return false;}rgPoints.clear();for (int i=0; i<rgVarPoints.count(); i++) {rgPoints.append(rgVarPoints[i].value<QGeoCoordinate>());}return true;
}void JsonHelper::saveGeoCoordinateArray(const QVariantList& rgVarPoints,bool                writeAltitude,QJsonValue&         jsonValue)
{QJsonArray rgJsonPoints;// Add all points to the arrayfor (int i=0; i<rgVarPoints.count(); i++) {QJsonValue jsonPoint;JsonHelper::saveGeoCoordinate(rgVarPoints[i].value<QGeoCoordinate>(), writeAltitude, jsonPoint);rgJsonPoints.append(jsonPoint);}jsonValue = rgJsonPoints;
}void JsonHelper::saveGeoCoordinateArray(const QList<QGeoCoordinate>&    rgPoints,bool                            writeAltitude,QJsonValue&                     jsonValue)
{QVariantList rgVarPoints;for (int i=0; i<rgPoints.count(); i++) {rgVarPoints.append(QVariant::fromValue(rgPoints[i]));}return saveGeoCoordinateArray(rgVarPoints, writeAltitude, jsonValue);
}bool JsonHelper::validateKeys(const QJsonObject& jsonObject, const QList<JsonHelper::KeyValidateInfo>& keyInfo, QString& errorString)
{QStringList             keyList;QList<QJsonValue::Type> typeList;//遍历所有的keysfor (int i=0; i<keyInfo.count(); i++) {if (keyInfo[i].required) {//如果时候需要的key,则加入到keyListkeyList.append(keyInfo[i].key);}}// 检查jsonObject是否包含keyList中的键if (!validateRequiredKeys(jsonObject, keyList, errorString)) {return false;}keyList.clear();for (int i=0; i<keyInfo.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<polygonArray.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<list.count(); i++) {QGeoCoordinate vertex = list.value<QGCQGeoCoordinate*>(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 <http://www.qgroundcontrol.org>** 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 <QDomDocument>
#include <QStringList>const char* KMLDomDocument::balloonStyleName = "BalloonStyle";KMLDomDocument::KMLDomDocument(const QString& name)
{//生成kmlheaderQDomProcessingInstruction 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 <http://www.qgroundcontrol.org>** QGroundControl is licensed according to the terms in the file* COPYING.md in the root of the source code directory.*****************************************************************************/#pragma once#include <QObject>
#include <QList>
#include <QGeoCoordinate>
#include <QVariant>/// Routines for loading polygons or polylines from KML or SHP files.
class ShapeFileHelper : public QObject
{Q_OBJECTpublic: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<QGeoCoordinate>& vertices, QString& errorString);static bool loadPolylineFromFile(const QString& file, QList<QGeoCoordinate>& 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 <http://www.qgroundcontrol.org>** 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 <QFile>
#include <QVariant>
#include <QtDebug>
#include <QRegularExpression>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"))) {//将文件重新命名并添加后缀.prjQString 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<QGeoCoordinate>& 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<shpObject->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 <http://www.qgroundcontrol.org>** QGroundControl is licensed according to the terms in the file* COPYING.md in the root of the source code directory.*****************************************************************************/#pragma once#include <QObject>
#include <QDomDocument>
#include <QList>
#include <QGeoCoordinate>#include "ShapeFileHelper.h"class KMLHelper : public QObject
{Q_OBJECTpublic://决定shape的类型static ShapeFileHelper::ShapeType determineShapeType(const QString& kmlFile, QString& errorString);//加载多边形数据static bool loadPolygonFromFile(const QString& kmlFile, QList<QGeoCoordinate>& vertices, QString& errorString);//加载多线段数据static bool loadPolylineFromFile(const QString& kmlFile, QList<QGeoCoordinate>& coords, QString& errorString);private:static QDomDocument _loadFile(const QString& kmlFile, QString& errorString);static const char* _errorPrefix;
};

cc文件如下:

/****************************************************************************** (c) 2009-2020 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>** QGroundControl is licensed according to the terms in the file* COPYING.md in the root of the source code directory.*****************************************************************************/#include "KMLHelper.h"#include <QFile>
#include <QVariant>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<QGeoCoordinate>& vertices, QString& errorString)
{errorString.clear();vertices.clear();//加载文件QDomDocument domDocument = KMLHelper::_loadFile(kmlFile, errorString);if (!errorString.isEmpty()) {return false;}//找到PolygonQDomNodeList rgNodes = domDocument.elementsByTagName("Polygon");if (rgNodes.count() == 0) {errorString = QString(_errorPrefix).arg(tr("Unable to find Polygon node in KML"));return false;}//找到outerBoundaryIs/LinearRing/coordinatesQDomNode 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<QGeoCoordinate> rgCoords;for (int i=0; i<rgCoordinateStrings.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 windingdouble sum = 0;for (int i=0; i<rgCoords.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<QGeoCoordinate> rgReversed;for (int i=0; i<rgCoords.count(); i++) {rgReversed.prepend(rgCoords[i]);}rgCoords = rgReversed;}vertices = rgCoords;return true;
}bool KMLHelper::loadPolylineFromFile(const QString& kmlFile, QList<QGeoCoordinate>& 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<QGeoCoordinate> rgCoords;for (int i=0; i<rgCoordinateStrings.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 <http://www.qgroundcontrol.org>** QGroundControl is licensed according to the terms in the file* COPYING.md in the root of the source code directory.*****************************************************************************/#pragma once#include <QThread>/*** @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 <mavteam@student.ethz.ch>*/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 <http://www.qgroundcontrol.org>** 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 <mavteam@student.ethz.ch>*/#include "LogCompressor.h"
#include "QGCApplication.h"#include <QFile>
#include <QFileInfo>
#include <QDir>
#include <QTextStream>
#include <QStringList>
#include <QFileInfo>
#include <QList>
#include <QDebug>/*** 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 useableQFile 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);//将文件名加_compressedparts.replace(0, parts.first() + "_compressed");//后缀改为txtparts.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<QString, 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<QString, 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所有的keysQStringList headerList(messageMap.keys());//打开输出文件并将标题行写入其中QString headerLine = "timestamp_ms" + delimiter + headerList.join(delimiter) + "\n";// Clean header names from symbols Matlab considers as Latex syntaxheaderLine = 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<quint64, QStringList> 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<quint64, QStringList> timestampMap;// Run through the whole file and fill map of timestampswhile (!in.atEnd()) {QStringList newLine = in.readLine().split(delimiter);//获取时间quint64 timestamp = newLine.at(0).toULongLong();// Check if timestamp does exist - if not, add it// 不存在就添加 值为刚刚构建的模版数据 templateListif (!timestampMap.contains(timestamp)) {timestampMap.insert(timestamp, templateList);}//获取到模版列表 这里为所有key的对应的初始数据  templateList << (holeFillingEnabled?"NaN":"")QStringList list = timestampMap.value(timestamp);//获取当前行的数据名称 也就是header中管道每一个keyQString currentDataName = newLine.at(2);//获取key对应的值QString currentDataValue = newLine.at(3);//修改对应元素的值 messageMap中的值存放的是header中所有的keyslist.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 necessaryif (holeFillingEnabled) {int index = 0;//如果此行数据缺失就用上行数据代替foreach (const QString& str, list) {if (str == "" || str == "NaN") {list.replace(index, lastList.at(index));}index++;}}// Set last listlastList = list;// Write data columnsQString output = list.join(delimiter) + "\n";//写入输出文件outTmpFile.write(output.toLocal8Bit());}lineCounter++;}// We're now done with the source fileinfile.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的几个文件进行分析。

机器人地面站-[QGroundControl源码解析]-[1]相关推荐

  1. 机器人地面站-[QGroundControl源码解析]-[3]-[ADSB]

    目录 前言 一.ADSBVehicle 二.ADSBVehicleManager 总结 前言 上篇我们介绍QGC开头几个工具类,但是没有将QGCApplication因为看到里面类和文件很多都是还没接 ...

  2. 机器人地面站-[QGroundControl源码解析]-[9]-[Camera]

    目录 前言 一.QGCCameraManager 二.QGCCameraIO 三.QGCCameraControl 前言 本篇介绍Camera文件夹下的内容,该文件夹下又三个类文件,分别是QGCCam ...

  3. 智能聊天机器人实现(源码+解析)

    前言: 之前写了一篇  <美女图片采集器 (源码+解析)> 得到了众多朋友的支持, 发现这样系列的教程还是挺受欢迎的, 也激励我继续写下去. 也在那一篇文章中提过, 美女图片采集只是我先前 ...

  4. 智能聊天机器人实现 源码+解析

    前言: 今天带来的是智能聊天机器人实现(源码+解析), 和上一篇教程一样, 当你没有女朋友的时候, 可以用它来打发时间.这里的API是图灵机器人提供的, 实现一个十分强大的机器人. 具体功能包括: • ...

  5. Laravel5.2之Filesystem源码解析(下)

    2019独角兽企业重金招聘Python工程师标准>>> 说明:本文主要学习下\League\Flysystem这个Filesystem Abstract Layer,学习下这个pac ...

  6. Libuv源码解析 - uv_loop整个初始化模块

    Libuv源码解析 - uv_loop整个初始化模块 loop_default_loop static uv_loop_t default_loop_struct; static uv_loop_t* ...

  7. cartographer 源码解析 (五)

    相关链接: cartographer 源码解析(一) cartographer 源码解析(二) cartographer 源码解析(三) cartographer 源码解析(四) cartograph ...

  8. Robot Arm 机械臂源码解析

    Robot Arm 机械臂源码解析 说明: ​ Robot Arm是我复刻,也是玩的第一款机械臂.用的是三自由度的结构,你可以理解为了三个电机,三轴有自己的一些缺陷.相比于六轴机械臂而言因为结构的缺陷 ...

  9. MySQL核心参数含义的源码解析

    引言 你访问的网站,大部分使用Apache服务器;你访问的网站,大部分使用Linux或BSD操作系统:你访问的网站,大部分使用MySQL数据库;你提交DNS域名查询请求大多由BIND服务器分析处理;你 ...

最新文章

  1. 三招让你从求职者中脱颖而出(转)
  2. ES-PHP向ES批量添加文档报No alive nodes found in your cluster
  3. 事件,信号量,互斥量
  4. 面试官:实现一个带值变更通知能力的Dictionary
  5. gcc/g++编译器的优化
  6. 2.3 基本算法之递归变递推 1188 菲波那契数列(2) python
  7. 入门机器学习(七)--神经网络
  8. 在日常维护管理中对MySQL 日志的需求
  9. web前端时间戳转时间类型显示
  10. python 模拟io_Python 的五种io模型理解
  11. 腾讯推出移动端动画组件PAG,释放设计生产力!
  12. 深度学习--- GAN网络原理解析
  13. 打造千万级流量秒杀 过载保护:如何通过熔断和限流解决流量过载问题?
  14. LeetCode.1033-移动石头直到连续(Moving Stones Until Consecutive)
  15. 基于微信小程序的教学辅导平台设计与实现
  16. java计算机毕业设计家电售后管理系统MyBatis+系统+LW文档+源码+调试部署
  17. catlan数和超级catlan数(施罗德数)
  18. LVM(logical volume manager) 逻辑卷管理器
  19. 1.如何实现MT4帐号同步交易?
  20. 《嵌入式系统 - RT-Thread开发笔记》 第三部分 RT-Thread 移植与设备驱动开发 - 第1章 RT-Thread 开发环境搭建 (Ubuntu)

热门文章

  1. 2022软件测试工程师涨薪攻略,3年如何达到30K
  2. 数学分析 反常积分(第11章)
  3. 饥荒mod制作教程--特效动画制作--01
  4. 杭电4506-小明系列故事——师兄帮帮忙
  5. HDU 4506 小明系列故事——师兄帮帮忙
  6. API REST com春季+ Testes com MockMVC +Documentaçãocom Swagger
  7. CSS - 通过 WebFont 技术引入在线字体(@font-face 语句)
  8. 侧扫声呐的应用_适用于多模块android应用的声纳
  9. android检测张嘴眨眼,手机端APP活体真活人检测扫描人脸识别SDK之张嘴摇头眨眼点头确认真人非图片...
  10. python itertools的使用