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

2023-05-16

目录

前言

一.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 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<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,       ///< 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<QJsonValue::Type>& 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<KeyValidateInfo>& 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<QGeoCoordinate>&   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<QGeoCoordinate>& 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 <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;

    //遍历所有的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<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的值为NaN
                continue;
            }
            //判断类型不相等报错
            if (jsonValue.type() != types[i]) {
                errorString  = QObject::tr("Incorrect value type - key:type:expected %1:%2:%3").arg(valueKey).arg(_jsonValueTypeToString(jsonValue.type())).arg(_jsonValueTypeToString(types[i]));
                return false;
            }
        }
    }

    return true;
}

bool JsonHelper::isJsonFile(const QByteArray& bytes, QJsonDocument& jsonDoc, QString& errorString)
{
    QJsonParseError parseError;

    //从二进制流中加载json,转为QJsonDocument
    jsonDoc = QJsonDocument::fromJson(bytes, &parseError);

    if (parseError.error == QJsonParseError::NoError) {
        return true;
    } else {
        //转化失败
        int startPos = qMax(0, parseError.offset - 100);
        int length = qMin(bytes.count() - startPos, 200);
        qDebug() << QStringLiteral("Json read error '%1'").arg(bytes.mid(startPos, length).constData());
        errorString = parseError.errorString();
        return false;
    }
}

bool JsonHelper::isJsonFile(const QString& fileName, QJsonDocument& jsonDoc, QString& errorString)
{
    QFile jsonFile(fileName);
    if (!jsonFile.open(QFile::ReadOnly)) {
        errorString = tr("File open failed: file:error %1 %2").arg(jsonFile.fileName()).arg(jsonFile.errorString());
        return false;
    }
    //转为二进制流
    QByteArray jsonBytes = jsonFile.readAll();
    jsonFile.close();

    return isJsonFile(jsonBytes, jsonDoc, errorString);
}

bool JsonHelper::validateInternalQGCJsonFile(const QJsonObject& jsonObject,
                                             const QString&     expectedFileType,
                                             int                minSupportedVersion,
                                             int                maxSupportedVersion,
                                             int&               version,
                                             QString&           errorString)
{
    // Validate required keys 验证所需的key
    QList<JsonHelper::KeyValidateInfo> 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<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不包含_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<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 array
    for (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;

    //遍历所有的keys
    for (int i=0; i<keyInfo.count(); i++) {
        if (keyInfo[i].required) {
            //如果时候需要的key,则加入到keyList
            keyList.append(keyInfo[i].key);
        }
    }
    // 检查jsonObject是否包含keyList中的键
    if (!validateRequiredKeys(jsonObject, keyList, errorString)) {
        return false;
    }

    keyList.clear();
    for (int i=0; i<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)
{
    //生成kmlheader
    QDomProcessingInstruction header = createProcessingInstruction(QStringLiteral("xml"), QStringLiteral("version=\"1.0\" encoding=\"UTF-8\""));
    appendChild(header);

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

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

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

    _addStandardStyles();
}

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

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

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

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

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

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

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

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

    return placemarkElement;
}

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

四.ShapeFileHelper

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

/****************************************************************************
 *
 * (c) 2009-2020 QGROUNDCONTROL PROJECT <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_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<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"))) {
        //将文件重新命名并添加后缀.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<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_OBJECT

public:
    //决定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;
    }

    //找到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<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 winding
    double 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 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<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所有的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<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 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的几个文件进行分析。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

机器人地面站-[QGroundControl源码解析]-[1] 的相关文章

  • Spring源码解析7-(xml)Spring的AOP

    配置文件 直接看refresh 中的obtainFreshBeanFactory xff0c 初始化容器 xff0c 加载配置文件成beanDefinition 直接到DefaultBeanDefinitionDocumentReader的
  • QGroundControl之安装调试

    QGroundControl之安装调试 1 源由2 问题汇总2 1 摄像头播放问题2 2 Windows电脑录像和拍照保存位置2 3 Android设备录像和拍照保存位置 3 打包资料4 附录 QGroundControl Video St
  • 深入学习 esp8266 wifimanager源码解析(打造专属自己的web配网)(最全的wifimanager介绍))

    原文地址 xff1a https my oschina net u 4269090 blog 3329239 1 前言 废话少说 xff0c 本篇博文的目的就是深入学习 WifiManager 这个github上非常火爆的ESP8266 w
  • 【Zipkin】zipkin-dependencies计算 - 源码解析

    zipkin dependencies离线计算拓扑图依赖 本文分析mysql存储 xff08 后续准备接入Doris直接计算依赖关系 xff0c 所以分析不关注存储 xff09 查找main方法 ZipkinDependenciesJob
  • Guava-Collections2源码解析

    构造器 private Collections2 私有构造器 xff0c 也没有静态构造器 xff0c 所以可以很明确它是一个纯工具类了 功能方法 filter过滤方法 传入一个带过滤的容器 xff0c 和一个实现过滤规则的函数类 xff0
  • QGroundControl(QGC)飞控地面站二次开发-修改地图

    一 问题 QGC的地图在国内某些地方 xff0c 只用bing可用 xff0c 其他都不能使用 而且bing卫星图也没有道路和标签信息 xff0c 还死慢死慢的 据说bing其实就没有国内的卫星地图版权 xff0c 在网页上查查看bing地
  • 机器人地面站-[QGroundControl源码解析]-[1]

    目录 前言 一 CmdLineOptParser 二 JsonHelper 三 KMLDomDocument 四 ShapeFileHelper 五 SHPFileHelper 六 KMLHelper 七 LogCompressor 总结
  • GVINS源码解析

    GVINS是基于VINS MONO写的 xff0c 视觉 IMU部分与VINS MONO类似 xff0c 可参考我的前一篇文章VINS MONO学习 这篇文章主要解析与GNSS有关的部分 持续更新中 文章目录 estimator node
  • BLAM源码解析(四)—— 基于ICP的位姿更新

    第三节我们介绍了定时器的定时回调 xff0c 实现对激光数据的批量循环处理 xff0c 在每一个激光数据的循环当中 xff0c 除了一开始filter 的点云过滤 xff0c 最重要的其实是下面的基于ICP的位姿更新 xff0c 即 if
  • ubuntu20.04版本 安装ros1与px4、mavros、QGroundControl

    基于个人安装时的操作以及所浏览的链接 xff0c 仅供参考 文章目录 前言一 pandas是什么 xff1f 二 使用步骤 1 引入库2 读入数据总结 前言 使用ubuntu下载ros px4似乎是一件十分繁琐以及困难的事情 xff0c 但
  • Retrofit2 源码解析

    0 基本使用 1 Retrofit 将我们的 HTTP API 转换成一个 接口形式 所以我们第一步定义一个 interface public interface GitHubService 64 GET 34 user user repo
  • 【无人机学习】Mission Planner(pc端)和QGroundControl(android端)

    无人机学习 Mission Planner xff08 pc端 xff09 和QGroundControl xff08 android端 xff09 系列文章目录 提示 xff1a 这里是收集了无人机的相关文章 无人机学习 无人机基础知识
  • ubuntu20 安装px4、mavros、QGroundControl

    一 安装PX4 jjm2是我的主文件夹名 xff0c 可以根据自己的主文件夹名修改 下载PX4 git clone https github com PX4 PX4 Autopilot git recursive 由于网速原因 xff0c
  • MSCKF 源码解析 一

    论文 xff1a https arxiv org abs 1712 00036 源码路径 https github com daniilidis group msckf mono 源码框架 上图展示了整个msckf源码框架 xff0c 每当
  • QGroundControl 自定义命令小工具的使用

    Custom Command Widgets 不用编译qgc的源码 xff0c 仅仅需要编写一个QML UIs文件这个小工具窗口可以被加载 xff0c 并且从重启之后仍然可以使用 xff0c 而且不受操作系统的限制主要有两个用途 xff11
  • Pytorch学习(3) —— nn.Parameter nn.ParameterList nn.ParameterDict 源码解析

    为了更好理解Pytorch基本类的实现方法 xff0c 我这里给出了关于参数方面的3个类的源码详解 此部分可以更好的了解实现逻辑结构 xff0c 有助于后续代码理解 xff0c 学pytorch的话这个不是必须掌握的 xff0c 看不懂也没
  • QGroundControl安装

    QGroundControl是一个基于PX4自动驾驶仪配置和飞行的应用程序 并且跨平台支持所有的主流操作系统 xff1a 手机系统 Android 和 iOS 目前专注于平板电脑 桌面系统 Windows Linux Mac OS 安装QG
  • ASCII表

    http office microsoft com zh cn assistance HA011331362052 aspx ASCII 打印字符 数字 32 126 分配给了能在键盘上找到的字符 当您查看或打印文档时就会出现 数字 127
  • 对象池(连接池):commons-pool2源码解析:GenericObjectPool的borrowObject方法

    为什么会有对象池 在实际的应用工程当中 存在一些被频繁使用的 创建或者销毁比较耗时 持有的资源也比较昂贵的一些对象 比如 数据库连接对象 线程对象 所以如果能够通过一种方式 把这类对象统一管理 让这类对象可以被循环利用的话 就可以减少很多系
  • 对象池(连接池):commons-pool2源码解析:GenericObjectPool的继承结构、构造方法

    概述 GenericObjectPool是apache commons pool 源码分析基于commons pool2 框架中的一个非常重要的类 解析GenericObjectPool就有必要先了解一下apache commons poo

随机推荐