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

目录

前言

一.CmdLineOptParser

二.JsonHelper

三.KMLDomDocument

四.ShapeFileHelper

五.SHPFileHelper

六.KMLHelper

七.LogCompressor

总结


前言

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

话不多说,这就开始,我使用的项目版本是4.2.0.项目目录如下

机器人地面站-[QGroundControl源码解析]-[1]_第1张图片

对于项目下载与运行,我只简单说两句我遇到的问题。

有两种方式第一种就是最靠谱也是普遍的方式从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文件中的机器人地面站-[QGroundControl源码解析]-[1]_第2张图片

对应gstreamer的路径修改为自己下载的路径。这里修改完成后项目就可以运行了。

下面我们开始对源码进行分析,这里我按照顺序进行,因为还不了解项目的整体结构。先对根目下src下的代码逐个分析,目录结构如下所示。

机器人地面站-[QGroundControl源码解析]-[1]_第3张图片

一.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 argv
                         char*          argv[],                 ///< command line arguments
                         CmdLineOpt_t*  prgOpts,                ///< command line options
                         size_t         cOpts,                  ///< count of command line options
                         bool           removeParsedOptions)    ///< true: remove parsed option from argc/argv
{
    // Start with all options off
    // 先将所有的option的find置为false
    for (size_t iOption=0; iOption

二.JsonHelper

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

/****************************************************************************
 *
 * (c) 2009-2020 QGROUNDCONTROL PROJECT 
 *
 * QGroundControl is licensed according to the terms in the file
 * COPYING.md in the root of the source code directory.
 *
 ****************************************************************************/

#pragma once

#include 
#include 
#include 
#include 

/// @file
/// @author Don Gagne 

class QmlObjectListModel;

/// @brief Json manipulation helper class.
/// Primarily used for parsing and processing Fact metadata.
class JsonHelper
{
    //给非qt类添加多语言支持
    Q_DECLARE_TR_FUNCTIONS(JsonHelper)

public:
    /// Determines is the specified file is a json file 确定指定的文件是否是json文件
    /// @return true: file is json, false: file is not json 输入参数是文件名
    static bool isJsonFile(const QString&       fileName,       ///< filename
                           QJsonDocument&       jsonDoc,        ///< returned json document
                           QString&             errorString);   ///< error on parse failure

    /// Determines is the specified data is a json file
    /// @return true: file is json, false: file is not json 参数是二进制
    static bool isJsonFile(const QByteArray&    bytes,          ///< json bytes
                           QJsonDocument&       jsonDoc,        ///< returned json document
                           QString&             errorString);   ///< error on parse failure

    /// Saves the standard file header the json object 保存标准的qgc jason头
    static void saveQGCJsonFileHeader(QJsonObject&      jsonObject, ///< root json object
                                      const QString&    fileType,   ///< file type for file
                                      int               version);   ///< version number for file

    /// Validates the standard parts of an external QGC json file (Plan file, ...): 验证外部QGC json文件的标准部分
    ///     jsonFileTypeKey - Required and checked to be equal to expected FileType 要求并检查等于期望的文件类型
    ///     jsonVersionKey - Required and checked to be below supportedMajorVersion, supportedMinorVersion 要求并检查低于支持的主版本号和次版本号
    ///     jsonGroundStationKey - Required and checked to be string type 要求并检查地面站的key为string类型
    /// @return false: validation failed, errorString set
    static bool validateExternalQGCJsonFile(const QJsonObject&  jsonObject,             ///< json object to validate 被检查对象
                                            const QString&      expectedFileType,       ///< correct file type for file 要求的文件类型
                                            int                 minSupportedVersion,    ///< minimum supported version 小版本号
                                            int                 maxSupportedVersion,    ///< maximum supported major version 大版本号
                                            int                 &version,               ///< returned file version 返回的文件版本
                                            QString&            errorString);           ///< returned error string if validation fails 失败原因

    /// Validates the standard parts of a internal QGC json file (FactMetaData, ...):验证内部QGC json文件的标准部分,参数部分同上
    ///     jsonFileTypeKey - Required and checked to be equal to expectedFileType
    ///     jsonVersionKey - Required and checked to be below supportedMajorVersion, supportedMinorVersion
    ///     jsonGroundStationKey - Required and checked to be string type
    /// @return false: validation failed, errorString set
    static bool validateInternalQGCJsonFile(const QJsonObject&  jsonObject,             ///< json object to validate
                                            const QString&      expectedFileType,       ///< correct file type for file
                                            int                 minSupportedVersion,    ///< minimum supported version
                                            int                 maxSupportedVersion,    ///< maximum supported major version
                                            int                 &version,               ///< returned file version
                                            QString&            errorString);           ///< returned error string if validation fails

    /// Opens, validates and translates an internal QGC json file. 打开、验证和翻译内部QGC json文件。
    /// @return Json root object for file. Empty QJsonObject if error.
    static QJsonObject openInternalQGCJsonFile(const QString& jsonFilename,             ///< Json file to open
                                               const QString&      expectedFileType,    ///< correct file type for file
                                               int                 minSupportedVersion, ///< minimum supported version
                                               int                 maxSupportedVersion, ///< maximum supported major version
                                               int                 &version,            ///< returned file version
                                               QString&            errorString);        ///< returned error string if validation fails

    /// Validates that the specified keys are in the object 验证指定的键是否在对象中
    /// @return false: validation failed, errorString set
    static bool validateRequiredKeys(const QJsonObject& jsonObject, ///< json object to validate
                                     const QStringList& keys,       ///< keys which are required to be present
                                     QString& errorString);         ///< returned error string if validation fails

    /// Validates the types of specified keys are in the object 验证对象中指定键的类型
    /// @return false: validation failed, errorString set
    static bool validateKeyTypes(const QJsonObject& jsonObject,         ///< json object to validate
                                 const QStringList& keys,               ///< keys to validate
                                 const QList& types,  ///< required type for each key, QJsonValue::Null specifies double with possible NaN
                                 QString& errorString);                 ///< returned error string if validation fails

    typedef struct {
        const char*         key;        ///< json key name
        QJsonValue::Type    type;       ///< required type for key, QJsonValue::Null specifies double with possible NaN
        bool                required;   ///< true: key must be present
    } KeyValidateInfo;

    static bool validateKeys(const QJsonObject& jsonObject, const QList& keyInfo, QString& errorString);

    /// Loads a QGeoCoordinate 加载位置坐标数据 存储格式为[ lat, lon, alt ]
    ///     Stored as array [ lat, lon, alt ]
    /// @return false: validation failed
    static bool loadGeoCoordinate(const QJsonValue& jsonValue,              ///< json value to load from
                                  bool              altitudeRequired,       ///< true: altitude must be specified
                                  QGeoCoordinate&   coordinate,             ///< returned QGeoCordinate
                                  QString&          errorString,            ///< returned error string if load failure
                                  bool              geoJsonFormat = false); ///< if true, use [lon, lat], [lat, lon] otherwise

    /// Saves a QGeoCoordinate 保存位置坐标数据 存储格式为[ lat, lon, alt ]
    ///     Stored as array [ lat, lon, alt ]
    static void saveGeoCoordinate(const QGeoCoordinate& coordinate,     ///< QGeoCoordinate to save
                                  bool                  writeAltitude,  ///< true: write altitude to json
                                  QJsonValue&           jsonValue);     ///< json value to save to

    /// Loads a QGeoCoordinate
    ///     Stored as array [ lon, lat, alt ]
    /// @return false: validation failed
    static bool loadGeoJsonCoordinate(const QJsonValue& jsonValue,          ///< json value to load from
                                      bool              altitudeRequired,   ///< true: altitude must be specified
                                      QGeoCoordinate&   coordinate,         ///< returned QGeoCordinate
                                      QString&          errorString);       ///< returned error string if load failure

    /// Saves a QGeoCoordinate
    ///     Stored as array [ lon, lat, alt ]
    static void saveGeoJsonCoordinate(const QGeoCoordinate& coordinate,     ///< QGeoCoordinate to save
                                      bool                  writeAltitude,  ///< true: write altitude to json
                                      QJsonValue&           jsonValue);     ///< json value to save to

    /// Loads a polygon from an array 加载多边形
    static bool loadPolygon(const QJsonArray&   polygonArray,   ///< Array of coordinates
                            QmlObjectListModel& list,           ///< Empty list to add vertices to
                            QObject*            parent,         ///< parent for newly allocated QGCQGeoCoordinates
                            QString&            errorString);   ///< returned error string if load failure

    /// Loads a list of QGeoCoordinates from a json array  加载位置坐标数据数组形式
    /// @return false: validation failed
    static bool loadGeoCoordinateArray(const QJsonValue&    jsonValue,              ///< json value which contains points
                                       bool                 altitudeRequired,       ///< true: altitude field must be specified
                                       QVariantList&        rgVarPoints,            ///< returned points
                                       QString&             errorString);           ///< returned error string if load failure
    static bool loadGeoCoordinateArray(const QJsonValue&        jsonValue,          ///< json value which contains points
                                       bool                     altitudeRequired,   ///< true: altitude field must be specified
                                       QList&   rgPoints,           ///< returned points
                                       QString&                 errorString);       ///< returned error string if load failure

    /// Saves a list of QGeoCoordinates to a json array 保存位置坐标数据数组形式
    static void saveGeoCoordinateArray(const QVariantList&  rgVarPoints,            ///< points to save
                                       bool                 writeAltitude,          ///< true: write altitide value
                                       QJsonValue&          jsonValue);             ///< json value to save to
    static void saveGeoCoordinateArray(const QList& rgPoints,       ///< points to save
                                       bool                         writeAltitude,  ///< true: write altitide value
                                       QJsonValue&                  jsonValue);     ///< json value to save to

    /// Saves a polygon to a json array 保存多边形数据
    static void savePolygon(QmlObjectListModel& list,           ///< List which contains vertices
                            QJsonArray&         polygonArray);  ///< Array to save into

    /// Returns NaN if the value is null, or if not, the double value 如果值为空,则返回NaN;如果不为空,则返回double值
    static double possibleNaNJsonValue(const QJsonValue& value);

    //一些字符串常量
    static const char* jsonVersionKey;
    static const char* jsonGroundStationKey;
    static const char* jsonGroundStationValue;
    static const char* jsonFileTypeKey;

private:
    static QString _jsonValueTypeToString(QJsonValue::Type type);
    static bool _loadGeoCoordinate(const QJsonValue&    jsonValue,
                                   bool                 altitudeRequired,
                                   QGeoCoordinate&      coordinate,
                                   QString&             errorString,
                                   bool                 geoJsonFormat);
    static void _saveGeoCoordinate(const QGeoCoordinate&    coordinate,
                                   bool                     writeAltitude,
                                   QJsonValue&              jsonValue,
                                   bool                     geoJsonFormat);
    static QStringList _addDefaultLocKeys(QJsonObject& jsonObject);
    static QJsonObject _translateRoot(QJsonObject& jsonObject, const QString& translateContext, const QStringList& translateKeys);
    static QJsonObject _translateObject(QJsonObject& jsonObject, const QString& translateContext, const QStringList& translateKeys);
    static QJsonArray _translateArray(QJsonArray& jsonArray, const QString& translateContext, const QStringList& translateKeys);

    static const char*  _translateKeysKey;
    static const char*  _arrayIDKeysKey;
};

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

/****************************************************************************
 *
 * (c) 2009-2020 QGROUNDCONTROL PROJECT 
 *
 * QGroundControl is licensed according to the terms in the file
 * COPYING.md in the root of the source code directory.
 *
 ****************************************************************************/

#include "JsonHelper.h"
#include "QGCQGeoCoordinate.h"
#include "QmlObjectListModel.h"
#include "MissionCommandList.h"
#include "FactMetaData.h"
#include "QGCApplication.h"

#include 
#include 
#include 
#include 
#include 
#include 
#include 

const char* JsonHelper::jsonVersionKey                      = "version";
const char* JsonHelper::jsonGroundStationKey                = "groundStation";
const char* JsonHelper::jsonGroundStationValue              = "QGroundControl";
const char* JsonHelper::jsonFileTypeKey                     = "fileType";
const char* JsonHelper::_translateKeysKey                   = "translateKeys";
const char* JsonHelper::_arrayIDKeysKey                     = "_arrayIDKeys";

/// 检查jsonObject是否包含keys中的键
bool JsonHelper::validateRequiredKeys(const QJsonObject& jsonObject, const QStringList& keys, QString& errorString)
{
    QString missingKeys;

    //遍历所有的keys
    foreach(const QString& key, keys) {
        //如果jsonObject不包含key
        if (!jsonObject.contains(key)) {
            //如果missingKeys不为空
            if (!missingKeys.isEmpty()) {
                //QStringLiteral是Qt5中新引入的一个用来从“字符串常量”创建QString对象的宏
                missingKeys += QStringLiteral(", ");
            }
            missingKeys += key;
            //key1, key2, key3...
        }
    }

    if (missingKeys.count() != 0) {
        //将未找到的键打印出来
        errorString = QObject::tr("The following required keys are missing: %1").arg(missingKeys);
        return false;
    }

    return true;
}

bool JsonHelper::_loadGeoCoordinate(const QJsonValue&   jsonValue,
                                    bool                altitudeRequired, //是否需要高度信息
                                    QGeoCoordinate&     coordinate, //坐标系
                                    QString&            errorString,
                                    bool                geoJsonFormat)
{
    //要求jsonValue是一个数组形式
    if (!jsonValue.isArray()) {
        errorString = QObject::tr("value for coordinate is not array");
        return false;
    }

    //转换为json数组
    QJsonArray coordinateArray = jsonValue.toArray();
    //如果需要高度则是三个信息否则是两个
    int requiredCount = altitudeRequired ? 3 : 2;
    //判断如果json数组数量不等于requiredCount返回错误
    if (coordinateArray.count() != requiredCount) {
        errorString = QObject::tr("Coordinate array must contain %1 values").arg(requiredCount);
        return false;
    }

    //遍历json数组
    foreach(const QJsonValue& jsonValue, coordinateArray) {
        //要求json值的类型必须是double或者null
        if (jsonValue.type() != QJsonValue::Double && jsonValue.type() != QJsonValue::Null) {
            errorString = QObject::tr("Coordinate array may only contain double values, found: %1").arg(jsonValue.type());
            return false;
        }
    }

    //格式化
    if (geoJsonFormat) {
        coordinate = QGeoCoordinate(coordinateArray[1].toDouble(), coordinateArray[0].toDouble());
    } else {
        coordinate = QGeoCoordinate(possibleNaNJsonValue(coordinateArray[0]), possibleNaNJsonValue(coordinateArray[1]));
    }
    //设置高度
    if (altitudeRequired) {
        coordinate.setAltitude(possibleNaNJsonValue(coordinateArray[2]));
    }

    return true;
}


///将坐标轴转化为json数组
void JsonHelper::_saveGeoCoordinate(const QGeoCoordinate&   coordinate,
                                    bool                    writeAltitude,
                                    QJsonValue&             jsonValue,
                                    bool                    geoJsonFormat)
{
    QJsonArray coordinateArray;

    if (geoJsonFormat) {
        coordinateArray << coordinate.longitude() << coordinate.latitude();
    } else {
        coordinateArray << coordinate.latitude() << coordinate.longitude();
    }
    if (writeAltitude) {
        coordinateArray << coordinate.altitude();
    }

    jsonValue = QJsonValue(coordinateArray);
}

bool JsonHelper::loadGeoCoordinate(const QJsonValue&    jsonValue,
                                   bool                 altitudeRequired,
                                   QGeoCoordinate&      coordinate,
                                   QString&             errorString,
                                   bool                 geoJsonFormat)
{
    return _loadGeoCoordinate(jsonValue, altitudeRequired, coordinate, errorString, geoJsonFormat);
}

void JsonHelper::saveGeoCoordinate(const QGeoCoordinate&    coordinate,
                                   bool                     writeAltitude,
                                   QJsonValue&              jsonValue)
{
    _saveGeoCoordinate(coordinate, writeAltitude, jsonValue, false /* geoJsonFormat */);
}

bool JsonHelper::loadGeoJsonCoordinate(const QJsonValue& jsonValue,
                                       bool              altitudeRequired,
                                       QGeoCoordinate&   coordinate,
                                       QString&          errorString)
{
    return _loadGeoCoordinate(jsonValue, altitudeRequired, coordinate, errorString, true /* geoJsonFormat */);
}

void JsonHelper::saveGeoJsonCoordinate(const QGeoCoordinate& coordinate,
                                       bool                  writeAltitude,
                                       QJsonValue&           jsonValue)
{
    _saveGeoCoordinate(coordinate, writeAltitude, jsonValue, true /* geoJsonFormat */);
}

///判断键对应的类型是否正确
bool JsonHelper::validateKeyTypes(const QJsonObject& jsonObject, const QStringList& keys, const QList& types, QString& errorString)
{
    for (int i=0; i requiredKeys = {
        { jsonFileTypeKey,       QJsonValue::String, true },
        { jsonVersionKey,        QJsonValue::Double, true },
    };
    //验证requiredKeys
    if (!JsonHelper::validateKeys(jsonObject, requiredKeys, errorString)) {
        return false;
    }

    // Make sure file type is correct 验证文件类型
    QString fileTypeValue = jsonObject[jsonFileTypeKey].toString();
    if (fileTypeValue != expectedFileType) {
        errorString = QObject::tr("Incorrect file type key expected:%1 actual:%2").arg(expectedFileType).arg(fileTypeValue);
        return false;
    }

    // Version check 验证版本信息
    version = jsonObject[jsonVersionKey].toInt();
    if (version < minSupportedVersion) {
        errorString = QObject::tr("File version %1 is no longer supported").arg(version);
        return false;
    }
    //验证版本信息
    if (version > maxSupportedVersion) {
        errorString = QObject::tr("File version %1 is newer than current supported version %2").arg(version).arg(maxSupportedVersion);
        return false;
    }

    return true;
}

bool JsonHelper::validateExternalQGCJsonFile(const QJsonObject& jsonObject,
                                             const QString&     expectedFileType,
                                             int                minSupportedVersion,
                                             int                maxSupportedVersion,
                                             int&               version,
                                             QString&           errorString)
{
    // Validate required keys
    QList requiredKeys = {
        { jsonGroundStationKey, QJsonValue::String, true },
    };
    if (!JsonHelper::validateKeys(jsonObject, requiredKeys, errorString)) {
        return false;
    }

    return validateInternalQGCJsonFile(jsonObject, expectedFileType, minSupportedVersion, maxSupportedVersion, version, errorString);
}

QStringList JsonHelper::_addDefaultLocKeys(QJsonObject& jsonObject)
{
    QString translateKeys;
    //获取jsonObject中fileType对应的值
    QString fileType = jsonObject[jsonFileTypeKey].toString();
    if (!fileType.isEmpty()) {
        //如果fileType等于以下类型
        if (fileType == MissionCommandList::qgcFileType) {
            //如果jsonObject中包含“translateKeys”
            if (jsonObject.contains(_translateKeysKey)) {
                //获取对应的值
                translateKeys = jsonObject[_translateKeysKey].toString();
            } else {
                //不包含则添加
                translateKeys = "label,enumStrings,friendlyName,description,category";
                jsonObject[_translateKeysKey] = translateKeys;
            }
            //如果jsonObject不包含_arrayIDKeys
            if (!jsonObject.contains(_arrayIDKeysKey)) {
                //不包含则添加
                jsonObject[_arrayIDKeysKey] = "rawName,comment";
            }
        } else if (fileType == FactMetaData::qgcFileType) {
            if (jsonObject.contains(_translateKeysKey)) {
                translateKeys = jsonObject[_translateKeysKey].toString();
            } else {
                translateKeys = "shortDescription,longDescription,enumStrings";
                jsonObject[_translateKeysKey] = "shortDescription,longDescription,enumStrings";
            }
            if (!jsonObject.contains(_arrayIDKeysKey)) {
                jsonObject[_arrayIDKeysKey] = "name";
            }
        }
    }
    return translateKeys.split(",");
}

QJsonObject JsonHelper::_translateObject(QJsonObject& jsonObject, const QString& translateContext, const QStringList& translateKeys)
{
    //遍历jsonObject所有的keys
    for (const QString& key: jsonObject.keys()) {
        //如果keys是string类型
        if (jsonObject[key].isString()) {
            //转为string
            QString locString = jsonObject[key].toString();
            //translateKeys如果包含key
            if (translateKeys.contains(key)) {
                QString disambiguation;
                QString disambiguationPrefix("#loc.disambiguation#");
                //如果locString以#loc.disambiguation#开头
                if (locString.startsWith(disambiguationPrefix)) {
                    //则截取#loc.disambiguation#之后的部分为新的locString
                    locString = locString.right(locString.length() - disambiguationPrefix.length());
                    int commentEndIndex = locString.indexOf("#");
                    //如果locString含有#
                    if (commentEndIndex != -1) {
                        //获取#左边的部分
                        disambiguation = locString.left(commentEndIndex);
                        //获取#右边的部分
                        locString = locString.right(locString.length() - disambiguation.length() - 1);
                    }
                }

                //翻译
                QString xlatString = qgcApp()->qgcJSONTranslator().translate(translateContext.toUtf8().constData(), locString.toUtf8().constData(), disambiguation.toUtf8().constData());
                if (!xlatString.isNull()) {
                    jsonObject[key] = xlatString;
                }
            }
        } else if (jsonObject[key].isArray()) {
            QJsonArray childJsonArray = jsonObject[key].toArray();
            jsonObject[key] = _translateArray(childJsonArray, translateContext, translateKeys);
        } else if (jsonObject[key].isObject()) {
            QJsonObject childJsonObject = jsonObject[key].toObject();
            jsonObject[key] = _translateObject(childJsonObject, translateContext, translateKeys);
        }
    }

    return jsonObject;
}

QJsonArray JsonHelper::_translateArray(QJsonArray& jsonArray, const QString& translateContext, const QStringList& translateKeys)
{
    for (int i=0; i&  rgPoints,
                                        QString&                errorString)
{
    QVariantList rgVarPoints;

    if (!loadGeoCoordinateArray(jsonValue, altitudeRequired, rgVarPoints, errorString)) {
        return false;
    }

    rgPoints.clear();
    for (int i=0; i());
    }

    return true;
}

void JsonHelper::saveGeoCoordinateArray(const QVariantList& rgVarPoints,
                                        bool                writeAltitude,
                                        QJsonValue&         jsonValue)
{
    QJsonArray rgJsonPoints;

    // Add all points to the array
    for (int i=0; i(), writeAltitude, jsonPoint);
        rgJsonPoints.append(jsonPoint);
    }

    jsonValue = rgJsonPoints;
}

void JsonHelper::saveGeoCoordinateArray(const QList&    rgPoints,
                                        bool                            writeAltitude,
                                        QJsonValue&                     jsonValue)
{
    QVariantList rgVarPoints;

    for (int i=0; i& keyInfo, QString& errorString)
{
    QStringList             keyList;
    QList typeList;

    //遍历所有的keys
    for (int i=0; i(i)->coordinate();

        QJsonValue jsonValue;
        JsonHelper::saveGeoCoordinate(vertex, false /* writeAltitude */, jsonValue);
        polygonArray.append(jsonValue);
    }
}

double JsonHelper::possibleNaNJsonValue(const  QJsonValue& value)
{
    if (value.type() == QJsonValue::Null) {
        return std::numeric_limits::quiet_NaN();
    } else {
        return value.toDouble();
    }
}

三.KMLDomDocument

此类用户将任务转为kml文件。
/****************************************************************************
 *
 * (c) 2009-2020 QGROUNDCONTROL PROJECT 
 *
 * QGroundControl is licensed according to the terms in the file
 * COPYING.md in the root of the source code directory.
 *
 ****************************************************************************/

#include "KMLDomDocument.h"
#include "QGCPalette.h"
#include "QGCApplication.h"
#include "MissionCommandTree.h"
#include "MissionCommandUIInfo.h"
#include "FactMetaData.h"

#include 
#include 

const char* KMLDomDocument::balloonStyleName = "BalloonStyle";

KMLDomDocument::KMLDomDocument(const QString& name)
{
    //生成kmlheader
    QDomProcessingInstruction header = createProcessingInstruction(QStringLiteral("xml"), QStringLiteral("version=\"1.0\" encoding=\"UTF-8\""));
    appendChild(header);

    //生成节点
    QDomElement kmlElement = createElement(QStringLiteral("kml"));
    kmlElement.setAttribute(QStringLiteral("xmlns"), "http://www.opengis.net/kml/2.2");

    //生成主节点
    _rootDocumentElement = createElement(QStringLiteral("Document"));
    kmlElement.appendChild(_rootDocumentElement);
    appendChild(kmlElement);

    //向主节点中添加name和open项
    addTextElement(_rootDocumentElement, "name", name);
    addTextElement(_rootDocumentElement, "open", "1");

    _addStandardStyles();
}

///将坐标对象转为字符串
QString KMLDomDocument::kmlCoordString(const QGeoCoordinate& coord)
{
    double altitude = qIsNaN(coord.altitude() ) ? 0 : coord.altitude();
    return QStringLiteral("%1,%2,%3").arg(QString::number(coord.longitude(), 'f', 7)).arg(QString::number(coord.latitude(), 'f', 7)).arg(QString::number(altitude, 'f', 2));
}

//将颜色信息转为字符串
QString KMLDomDocument::kmlColorString (const QColor& color, double opacity)
{
    return QStringLiteral("%1%2%3%4").arg(static_cast(255.0 * opacity), 2, 16, QChar('0')).arg(color.blue(), 2, 16, QChar('0')).arg(color.green(), 2, 16, QChar('0')).arg(color.red(), 2, 16, QChar('0'));
}

//给kml添加style标签
void KMLDomDocument::_addStandardStyles(void)
{
    QGCPalette palette;

    QDomElement styleElementForBalloon = createElement("Style");
    styleElementForBalloon.setAttribute("id", balloonStyleName);
    QDomElement balloonStyleElement = createElement("BalloonStyle");
    addTextElement(balloonStyleElement, "text", "$[description]");
    styleElementForBalloon.appendChild(balloonStyleElement);
    _rootDocumentElement.appendChild(styleElementForBalloon);
}

void KMLDomDocument::addTextElement(QDomElement& parentElement, const QString &name, const QString &value)
{
    QDomElement textElement = createElement(name);
    textElement.appendChild(createTextNode(value));
    parentElement.appendChild(textElement);
}

//添加lookat信息
void KMLDomDocument::addLookAt(QDomElement& parentElement, const QGeoCoordinate& coord)
{
    QDomElement lookAtElement = createElement("LookAt");
    addTextElement(lookAtElement, "latitude",  QString::number(coord.latitude(), 'f', 7));
    addTextElement(lookAtElement, "longitude", QString::number(coord.longitude(), 'f', 7));
    addTextElement(lookAtElement, "altitude",  QString::number(coord.longitude(), 'f', 2));
    addTextElement(lookAtElement, "heading",   "-100");
    addTextElement(lookAtElement, "tilt",      "45");
    addTextElement(lookAtElement, "range",     "2500");
    parentElement.appendChild(lookAtElement);
}

//添加标记点标签
QDomElement KMLDomDocument::addPlacemark(const QString& name, bool visible)
{
    QDomElement placemarkElement = createElement("Placemark");
    _rootDocumentElement.appendChild(placemarkElement);

    addTextElement(placemarkElement, "name",         name);
    addTextElement(placemarkElement, "visibility",   visible ? "1" : "0");

    return placemarkElement;
}

void KMLDomDocument::appendChildToRoot(const QDomNode& child)
{
    _rootDocumentElement.appendChild(child);
}

四.ShapeFileHelper

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

/****************************************************************************
 *
 * (c) 2009-2020 QGROUNDCONTROL PROJECT 
 *
 * QGroundControl is licensed according to the terms in the file
 * COPYING.md in the root of the source code directory.
 *
 ****************************************************************************/

#pragma once

#include 
#include 
#include 
#include 

/// Routines for loading polygons or polylines from KML or SHP files.
class ShapeFileHelper : public QObject
{
    Q_OBJECT

public:
    enum ShapeType {
        Polygon, //多边形
        Polyline,   //多线段
        Error
    };
    Q_ENUM(ShapeType)

    //关于Q_PROPERTY https://www.cnblogs.com/wanghongyang/p/15233642.html
    ///< File filter list for load/save KML file dialogs 载入/保存KML文件对话框的文件筛选器列表
    Q_PROPERTY(QStringList fileDialogKMLFilters         READ fileDialogKMLFilters       CONSTANT)
    ///< File filter list for load/save shape file dialogs 加载/保存形状文件对话框的文件筛选器列表
    Q_PROPERTY(QStringList fileDialogKMLOrSHPFilters    READ fileDialogKMLOrSHPFilters  CONSTANT)

    // 关于Q_INVOKABLE https://blog.csdn.net/qq78442761/article/details/109861560
    /// Loads the file and returns shape type and error string in a variant array. 加载文件并在变量数组中返回形状类型和错误字符串
    /// ShapeType is in index 0, error string is in index 1. ShapeType在索引0,错误字符串在索引1。
    Q_INVOKABLE static QVariantList determineShapeType(const QString& file);

    QStringList fileDialogKMLFilters(void) const;
    QStringList fileDialogKMLOrSHPFilters(void) const;

    static ShapeType determineShapeType(const QString& file, QString& errorString);
    static bool loadPolygonFromFile(const QString& file, QList& vertices, QString& errorString);
    static bool loadPolylineFromFile(const QString& file, QList& coords, QString& errorString);

private:
    static bool _fileIsKML(const QString& file, QString& errorString);

    static const char* _errorPrefix;
};

cc文件

ShapeFileHelper::ShapeType ShapeFileHelper::determineShapeType(const QString& file, QString& errorString)
{
    ShapeType shapeType = Error;

    errorString.clear();

    //判断是合法的kml文件
    bool fileIsKML = _fileIsKML(file, errorString);
    if (errorString.isEmpty()) {
        if (fileIsKML) {
            //判断file的类型选择不同的方式去加载数据
            shapeType = KMLHelper::determineShapeType(file, errorString);
        } else {
            shapeType = SHPFileHelper::determineShapeType(file, errorString);
        }
    }

    return shapeType;
}

五.SHPFileHelper

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

/****************************************************************************
 *
 * (c) 2009-2020 QGROUNDCONTROL PROJECT 
 *
 * QGroundControl is licensed according to the terms in the file
 * COPYING.md in the root of the source code directory.
 *
 ****************************************************************************/

#include "SHPFileHelper.h"
#include "QGCGeo.h"

#include 
#include 
#include 
#include 

const char* SHPFileHelper::_errorPrefix = QT_TR_NOOP("SHP file load failed. %1");

/// Validates the specified SHP file is truly a SHP file and is in the format we understand.
/// 验证指定的SHP文件确实是一个SHP文件,并且是我们所理解的格式
///     @param utmZone[out] Zone for UTM shape, 0 for lat/lon shape
///     @param utmSouthernHemisphere[out] true/false for UTM hemisphere
/// @return true: Valid supported SHP file found, false: Invalid or unsupported file found
bool SHPFileHelper::_validateSHPFiles(const QString& shpFile, int* utmZone, bool* utmSouthernHemisphere, QString& errorString)
{
    *utmZone = 0;
    errorString.clear();

    if (shpFile.endsWith(QStringLiteral(".shp"))) {
        //将文件重新命名并添加后缀.prj
        QString prjFilename = shpFile.left(shpFile.length() - 4) + QStringLiteral(".prj");
        QFile prjFile(prjFilename);
        if (prjFile.exists()) {
            //如果文件存在,以只读方式打开
            if (prjFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
                //以文件流方式获取文件内容
                QTextStream strm(&prjFile);
                QString line = strm.readLine();
                if (line.startsWith(QStringLiteral("GEOGCS[\"GCS_WGS_1984\","))) {
                    *utmZone = 0;
                    *utmSouthernHemisphere = false;
                } else if (line.startsWith(QStringLiteral("PROJCS[\"WGS_1984_UTM_Zone_"))) {
                    //利用正则表达式
                    QRegularExpression regEx(QStringLiteral("^PROJCS\\[\"WGS_1984_UTM_Zone_(\\d+){1,2}([NS]{1})"));
                    QRegularExpressionMatch regExMatch = regEx.match(line);
                    QStringList rgCapture = regExMatch.capturedTexts();
                    if (rgCapture.count() == 3) {
                        //获取对应信息
                        int zone = rgCapture[1].toInt();
                        if (zone >= 1 && zone <= 60) {
                            *utmZone = zone;
                            *utmSouthernHemisphere = rgCapture[2] == QStringLiteral("S");
                        }
                    }
                    if (*utmZone == 0) {
                        errorString = QString(_errorPrefix).arg(tr("UTM projection is not in supported format. Must be PROJCS[\"WGS_1984_UTM_Zone_##N/S"));
                    }
                } else {
                    errorString = QString(_errorPrefix).arg(tr("Only WGS84 or UTM projections are supported."));
                }
            } else {
                errorString = QString(_errorPrefix).arg(tr("PRJ file open failed: %1").arg(prjFile.errorString()));
            }
        } else {
            errorString = QString(_errorPrefix).arg(tr("File not found: %1").arg(prjFilename));
        }
    } else {
        errorString = QString(_errorPrefix).arg(tr("File is not a .shp file: %1").arg(shpFile));
    }

    return errorString.isEmpty();
}

/// @param utmZone[out] Zone for UTM shape, 0 for lat/lon shape
/// @param utmSouthernHemisphere[out] true/false for UTM hemisphere
SHPHandle SHPFileHelper::_loadShape(const QString& shpFile, int* utmZone, bool* utmSouthernHemisphere, QString& errorString)
{
    SHPHandle shpHandle = Q_NULLPTR;

    errorString.clear();

    //如果验证成功
    if (_validateSHPFiles(shpFile, utmZone, utmSouthernHemisphere, errorString)) {
        if (!(shpHandle = SHPOpen(shpFile.toUtf8(), "rb"))) {
            errorString = QString(_errorPrefix).arg(tr("SHPOpen failed."));
        }
    }

    return shpHandle;
}

ShapeFileHelper::ShapeType SHPFileHelper::determineShapeType(const QString& shpFile, QString& errorString)
{
    ShapeFileHelper::ShapeType shapeType = ShapeFileHelper::Error;

    errorString.clear();

    int utmZone;
    bool utmSouthernHemisphere;
    SHPHandle shpHandle = SHPFileHelper::_loadShape(shpFile, &utmZone, &utmSouthernHemisphere, errorString);
    if (errorString.isEmpty()) {
        int cEntities, type;

        //获取shp信息
        SHPGetInfo(shpHandle, &cEntities /* pnEntities */, &type, Q_NULLPTR /* padfMinBound */, Q_NULLPTR /* padfMaxBound */);
        qDebug() << "SHPGetInfo" << shpHandle << cEntities << type;
        if (cEntities != 1) {
            errorString = QString(_errorPrefix).arg(tr("More than one entity found."));
        } else if (type == SHPT_POLYGON) {
            shapeType = ShapeFileHelper::Polygon;
        } else {
            errorString = QString(_errorPrefix).arg(tr("No supported types found."));
        }
    }

    SHPClose(shpHandle);

    return shapeType;
}

bool SHPFileHelper::loadPolygonFromFile(const QString& shpFile, QList& vertices, QString& errorString)
{
    int         utmZone = 0;
    bool        utmSouthernHemisphere;
    double      vertexFilterMeters = 5;
    SHPHandle   shpHandle = Q_NULLPTR;
    SHPObject*  shpObject = Q_NULLPTR;

    errorString.clear();
    vertices.clear();

    shpHandle = SHPFileHelper::_loadShape(shpFile, &utmZone, &utmSouthernHemisphere, errorString);
    if (!errorString.isEmpty()) {
        goto Error;
    }

    int cEntities, shapeType;
    SHPGetInfo(shpHandle, &cEntities, &shapeType, Q_NULLPTR /* padfMinBound */, Q_NULLPTR /* padfMaxBound */);
    if (shapeType != SHPT_POLYGON) {
        errorString = QString(_errorPrefix).arg(tr("File does not contain a polygon."));
        goto Error;
    }

    shpObject = SHPReadObject(shpHandle, 0);
    if (shpObject->nParts != 1) {
        errorString = QString(_errorPrefix).arg(tr("Only single part polygons are supported."));
        goto Error;
    }

    for (int i=0; inVertices; i++) {
        QGeoCoordinate coord;
        if (!utmZone || !convertUTMToGeo(shpObject->padfX[i], shpObject->padfY[i], utmZone, utmSouthernHemisphere, coord)) {
            coord.setLatitude(shpObject->padfY[i]);
            coord.setLongitude(shpObject->padfX[i]);
        }
        vertices.append(coord);
    }

    // Filter last vertex such that it differs from first
    {
        QGeoCoordinate firstVertex = vertices[0];

        while (vertices.count() > 3 && vertices.last().distanceTo(firstVertex) < vertexFilterMeters) {
            vertices.removeLast();
        }
    }

    // Filter vertex distances to be larger than 1 meter apart
    {
        int i = 0;
        while (i < vertices.count() - 2) {
            if (vertices[i].distanceTo(vertices[i+1]) < vertexFilterMeters) {
                vertices.removeAt(i+1);
            } else {
                i++;
            }
        }
    }

Error:
    if (shpObject) {
        SHPDestroyObject(shpObject);
    }
    if (shpHandle) {
        SHPClose(shpHandle);
    }
    return errorString.isEmpty();
}

六.KMLHelper

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

头文件如下

/****************************************************************************
 *
 * (c) 2009-2020 QGROUNDCONTROL PROJECT 
 *
 * QGroundControl is licensed according to the terms in the file
 * COPYING.md in the root of the source code directory.
 *
 ****************************************************************************/

#pragma once

#include 
#include 
#include 
#include 

#include "ShapeFileHelper.h"

class KMLHelper : public QObject
{
    Q_OBJECT

public:
    //决定shape的类型
    static ShapeFileHelper::ShapeType determineShapeType(const QString& kmlFile, QString& errorString);
    //加载多边形数据
    static bool loadPolygonFromFile(const QString& kmlFile, QList& vertices, QString& errorString);
    //加载多线段数据
    static bool loadPolylineFromFile(const QString& kmlFile, QList& coords, QString& errorString);

private:
    static QDomDocument _loadFile(const QString& kmlFile, QString& errorString);

    static const char* _errorPrefix;
};

cc文件如下:

/****************************************************************************
 *
 * (c) 2009-2020 QGROUNDCONTROL PROJECT 
 *
 * QGroundControl is licensed according to the terms in the file
 * COPYING.md in the root of the source code directory.
 *
 ****************************************************************************/

#include "KMLHelper.h"

#include 
#include 

const char* KMLHelper::_errorPrefix = QT_TR_NOOP("KML file load failed. %1");

QDomDocument KMLHelper::_loadFile(const QString& kmlFile, QString& errorString)
{
    QFile file(kmlFile);

    errorString.clear();

    if (!file.exists()) {
        errorString = QString(_errorPrefix).arg(tr("File not found: %1").arg(kmlFile));
        return QDomDocument();
    }

    if (!file.open(QIODevice::ReadOnly)) {
        errorString = QString(_errorPrefix).arg(tr("Unable to open file: %1 error: $%2").arg(kmlFile).arg(file.errorString()));
        return QDomDocument();
    }

    QDomDocument doc;
    QString errorMessage;
    int errorLine;
    if (!doc.setContent(&file, &errorMessage, &errorLine)) {
        errorString = QString(_errorPrefix).arg(tr("Unable to parse KML file: %1 error: %2 line: %3").arg(kmlFile).arg(errorMessage).arg(errorLine));
        return QDomDocument();
    }

    return doc;
}

ShapeFileHelper::ShapeType KMLHelper::determineShapeType(const QString& kmlFile, QString& errorString)
{
    //加载kml文件
    QDomDocument domDocument = KMLHelper::_loadFile(kmlFile, errorString);
    if (!errorString.isEmpty()) {
        return ShapeFileHelper::Error;
    }

    //如果文件中包含Polygon元素则为多边形数据
    QDomNodeList rgNodes = domDocument.elementsByTagName("Polygon");
    if (rgNodes.count()) {
        return ShapeFileHelper::Polygon;
    }

    //如果文件中包含LineString元素则为多线段数据
    rgNodes = domDocument.elementsByTagName("LineString");
    if (rgNodes.count()) {
        return ShapeFileHelper::Polyline;
    }

    errorString = QString(_errorPrefix).arg(tr("No supported type found in KML file."));
    return ShapeFileHelper::Error;
}

bool KMLHelper::loadPolygonFromFile(const QString& kmlFile, QList& vertices, QString& errorString)
{
    errorString.clear();
    vertices.clear();

    //加载文件
    QDomDocument domDocument = KMLHelper::_loadFile(kmlFile, errorString);
    if (!errorString.isEmpty()) {
        return false;
    }

    //找到Polygon
    QDomNodeList rgNodes = domDocument.elementsByTagName("Polygon");
    if (rgNodes.count() == 0) {
        errorString = QString(_errorPrefix).arg(tr("Unable to find Polygon node in KML"));
        return false;
    }

    //找到outerBoundaryIs/LinearRing/coordinates
    QDomNode coordinatesNode = rgNodes.item(0).namedItem("outerBoundaryIs").namedItem("LinearRing").namedItem("coordinates");
    if (coordinatesNode.isNull()) {
        errorString = QString(_errorPrefix).arg(tr("Internal error: Unable to find coordinates node in KML"));
        return false;
    }

    //简体化
    QString coordinatesString = coordinatesNode.toElement().text().simplified();
    //空格分成数组
    QStringList rgCoordinateStrings = coordinatesString.split(" ");

    QList rgCoords;
    for (int i=0; i rgReversed;

        for (int i=0; i& coords, QString& errorString)
{
    errorString.clear();
    coords.clear();

    //加载文件
    QDomDocument domDocument = KMLHelper::_loadFile(kmlFile, errorString);
    if (!errorString.isEmpty()) {
        return false;
    }

    //找到LineString元素
    QDomNodeList rgNodes = domDocument.elementsByTagName("LineString");
    if (rgNodes.count() == 0) {
        errorString = QString(_errorPrefix).arg(tr("Unable to find LineString node in KML"));
        return false;
    }

    //找到coordinates元素
    QDomNode coordinatesNode = rgNodes.item(0).namedItem("coordinates");
    if (coordinatesNode.isNull()) {
        errorString = QString(_errorPrefix).arg(tr("Internal error: Unable to find coordinates node in KML"));
        return false;
    }

    QString coordinatesString = coordinatesNode.toElement().text().simplified();
    QStringList rgCoordinateStrings = coordinatesString.split(" ");

    //添加
    QList rgCoords;
    for (int i=0; i

七.LogCompressor

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

头文件注释如下:

/****************************************************************************
 *
 * (c) 2009-2020 QGROUNDCONTROL PROJECT 
 *
 * QGroundControl is licensed according to the terms in the file
 * COPYING.md in the root of the source code directory.
 *
 ****************************************************************************/

#pragma once

#include 

/**
 * @file
 *   @brief Declaration of class LogCompressor.
 *          This class reads in a file containing messages and translates it into a tab-delimited CSV file.
 *          该类读入一个包含信息的文件,并将其转换为一个以制表符分隔的CSV文件。
 *   @author Lorenz Meier 
 */

class LogCompressor : public QThread
{
    Q_OBJECT
public:
    /** @brief Create the log compressor. It will only get active upon calling startCompression() */
    ///创建日志压缩器。 它只有在调用startCompression()时才会激活
    LogCompressor(QString logFileName, QString outFileName="", QString delimiter="\t");
    /** @brief Start the compression of a raw, line-based logfile into a CSV file */
    ///开始将原始的、基于行的日志文件压缩到CSV文件中
    void startCompression(bool holeFilling=false);
    ///压缩是否完成
    bool isFinished() const;
    ///获取当前行
    int getCurrentLine() const;

protected:
    void run();                     ///< This function actually performs the compression. It's an overloaded function from QThread 这个函数实际执行压缩。 它是一个来自QThread的重载函数
    QString logFileName;            ///< The input file name. 输入文件
    QString outFileName;            ///< The output file name. If blank defaults to logFileName 输出文件
    bool running;                   ///< True when the startCompression() function is operating. 运行状态
    int currentDataLine;            ///< The current line of data that is being processed. Only relevant when running==true 正在处理的当前数据行。 只在运行==true时相关
    QString delimiter;              ///< Delimiter between fields in the output file. Defaults to tab ('\t') 分隔符
    bool holeFillingEnabled;        ///< Enables the filling of holes in the dataset with the previous value (or NaN if none exists) 是否启用将前一个值填充数据集中的空缺(如果不存在则为NaN)

signals:
    /** @brief This signal is emitted once a logfile has been finished writing
     * @param fileName The name of the output (CSV) file
     */
    void finishedFile(QString fileName);
    
    /// This signal is connected to QGCApplication::showCriticalMessage to show critical errors which come from the thread.
    /// 该信号连接到QGCApplication::showCriticalMessage,以显示来自线程的严重错误。
    /// There is no need for clients to connect to this signal.
    /// 客户端不需要连接到这个信号。
    void logProcessingCriticalError(const QString& title, const QString& msg);
    
private:
    void _signalCriticalError(const QString& msg);
    
};

cc文件注释如下:

/****************************************************************************
 *
 * (c) 2009-2020 QGROUNDCONTROL PROJECT 
 *
 * QGroundControl is licensed according to the terms in the file
 * COPYING.md in the root of the source code directory.
 *
 ****************************************************************************/


/**
 * @file
 *   @brief Implementation of class LogCompressor.
 *          This class reads in a file containing messages and translates it into a tab-delimited CSV file.
 *   @author Lorenz Meier 
 */

#include "LogCompressor.h"
#include "QGCApplication.h"

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

/**
 * Initializes all the variables necessary for a compression run. This won't actually happen
 * until startCompression(...) is called.
 */
//当调用startCompression是该初始化方法被调用
LogCompressor::LogCompressor(QString logFileName, QString outFileName, QString delimiter) :
	logFileName(logFileName),
	outFileName(outFileName),
	running(true),
	currentDataLine(0),
    delimiter(delimiter),
    holeFillingEnabled(true)
{
    connect(this, &LogCompressor::logProcessingCriticalError, qgcApp(), &QGCApplication::criticalMessageBoxOnMainThread);
}

void LogCompressor::run()
{
	// Verify that the input file is useable
	QFile infile(logFileName);
	if (!infile.exists() || !infile.open(QIODevice::ReadOnly | QIODevice::Text)) {
		_signalCriticalError(tr("Log Compressor: Cannot start/compress log file, since input file %1 is not readable").arg(QFileInfo(infile.fileName()).absoluteFilePath()));
		return;
	}

//    outFileName = logFileName;

    QString outFileName;

    //QFileInfo(infile.fileName()).absoluteFilePath() 可以获取到该文件的绝对路径
    QStringList parts = QFileInfo(infile.fileName()).absoluteFilePath().split(".", Qt::SkipEmptyParts);

    //将文件名加_compressed
    parts.replace(0, parts.first() + "_compressed");
    //后缀改为txt
    parts.replace(parts.size()-1, "txt");
    //拼接
    outFileName = parts.join(".");

	// Verify that the output file is useable
    //打开文件不存在则创建
    QFile outTmpFile(outFileName);
    if (!outTmpFile.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) {
		_signalCriticalError(tr("Log Compressor: Cannot start/compress log file, since output file %1 is not writable").arg(QFileInfo(outTmpFile.fileName()).absoluteFilePath()));
		return;
	}


	// First we search the input file through keySearchLimit number of lines
	// looking for variables. This is necessary before CSV files require
	// the same number of fields for every line.
    //首先,我们通过keySearchLimit搜索变量的行数来搜索输入文件。 在CSV文件要求每行有相同数量的字段之前,这是必要的。
	const unsigned int keySearchLimit = 15000;
	unsigned int keyCounter = 0;
	QTextStream in(&infile);
	QMap 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::iterator i = messageMap.begin();
	int j;
    //为每个字段的值从1开始递增的赋值
	for (i = messageMap.begin(), j = 1; i != messageMap.end(); ++i, ++j) {
		i.value() = j;
	}

	// Open the output file and write the header line to it
    // 获取map所有的keys
	QStringList headerList(messageMap.keys());
    //打开输出文件并将标题行写入其中
	QString headerLine = "timestamp_ms" + delimiter + headerList.join(delimiter) + "\n";
    // Clean header names from symbols Matlab considers as Latex syntax
    headerLine = headerLine.replace("timestamp", "TIMESTAMP");
    headerLine = headerLine.replace(":", "");
    headerLine = headerLine.replace("_", "");
    headerLine = headerLine.replace(".", "");

    // 写入
    // QString的toLocal8bit和toLatin1都可以将QString转化为QByteArray,但是两者的区别在于编码的不同:
    // toLocal8Bit:Unicode编码
    // toLatin1:ASCII编码
	outTmpFile.write(headerLine.toLocal8Bit());

    _signalCriticalError(tr("Log compressor: Dataset contains dimensions: ") + headerLine);

    // Template list stores a list for populating with data as it's parsed from messages.
    // 模板列表存储一个列表,用于在从消息解析数据时填充数据。
    QStringList templateList;
    for (int i = 0; i < headerList.size() + 1; ++i) {
        templateList << (holeFillingEnabled?"NaN":"");
    }


//	// Reset our position in the input file before we start the main processing loop.
//    in.seek(0);

//    // Search through all lines and build a list of unique timestamps
//    QMap timestampMap;
//    while (!in.atEnd()) {
//        quint64 timestamp = in.readLine().split(delimiter).at(0).toULongLong();
//        timestampMap.insert(timestamp, templateList);
//    }

    // Jump back to start of file
    // 跳到输入文件的头
    in.seek(0);

    // Map of final output lines, key is time
    // map的最终输出行,key是时间
    QMap timestampMap;

    // Run through the whole file and fill map of timestamps
    while (!in.atEnd()) {
        QStringList newLine = in.readLine().split(delimiter);
        //获取时间
        quint64 timestamp = newLine.at(0).toULongLong();

        // Check if timestamp does exist - if not, add it
        // 不存在就添加 值为刚刚构建的模版数据 templateList
        if (!timestampMap.contains(timestamp)) {
            timestampMap.insert(timestamp, templateList);
        }

        //获取到模版列表 这里为所有key的对应的初始数据  templateList << (holeFillingEnabled?"NaN":"")
        QStringList list = timestampMap.value(timestamp);

        //获取当前行的数据名称 也就是header中管道每一个key
        QString currentDataName = newLine.at(2);
        //获取key对应的值
        QString currentDataValue = newLine.at(3);
        //修改对应元素的值 messageMap中的值存放的是header中所有的keys
        list.replace(messageMap.value(currentDataName), currentDataValue);
        timestampMap.insert(timestamp, list);
    }

    int lineCounter = 0;

    QStringList lastList = timestampMap.values().at(1);

    foreach (QStringList list, timestampMap.values()) {
        // Write this current time set out to the file
        // only do so from the 2nd line on, since the first
        // line could be incomplete
        // 将当前时间设置写入文件,从第二行开始,因为第一行可能不完整
        if (lineCounter > 1) {
            // Set the timestamp
            //设置第0个位置为时间信息
            list.replace(0,QString("%1").arg(timestampMap.keys().at(lineCounter)));

            // Fill holes if necessary
            if (holeFillingEnabled) {
                int index = 0;
                //如果此行数据缺失就用上行数据代替
                foreach (const QString& str, list) {
                    if (str == "" || str == "NaN") {
                        list.replace(index, lastList.at(index));
                    }
                    index++;
                }
            }

            // Set last list
            lastList = list;

            // Write data columns
            QString output = list.join(delimiter) + "\n";
            //写入输出文件
            outTmpFile.write(output.toLocal8Bit());
        }
        lineCounter++;
    }

	// We're now done with the source file
	infile.close();

	// Clean up and update the status before we return.
	currentDataLine = 0;
	emit finishedFile(outFileName);
	running = false;
}

/**
 * @param holeFilling If hole filling is enabled, the compressor tries to fill empty data fields with previous
 * values from the same variable (or NaN, if no previous value existed)
 */
void LogCompressor::startCompression(bool holeFilling)
{
	holeFillingEnabled = holeFilling;
	start();
}

bool LogCompressor::isFinished() const
{
	return !running;
}

int LogCompressor::getCurrentLine() const
{
	return currentDataLine;
}


void LogCompressor::_signalCriticalError(const QString& msg)
{
    emit logProcessingCriticalError(tr("Log Compressor"), msg);
}

总结

好了,问了文章篇幅不要太长,这里就解释这几个文件吧,都是一些工具类,包括命令行的读取,json文件的读取,验证操作,kml文件的读取操作,shape文件的读写操作,以及普通文件转为csv文件的操作。下一节我们对前缀qgc的几个文件进行分析。

你可能感兴趣的:(学习记录,qt,人工智能,c++)