目录
前言
一.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下的代码逐个分析,目录结构如下所示。
这个类主要是一个解析输入参数的类,首先传入一个结构体数组,此结构体保存了要记录的选项和参数,然后将用户输入和此结构体数组进行一对一的比对,将用户输入储存到结构体数组中。
/// @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
这个文件主要承担了项目中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();
}
}
此类用户将任务转为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);
}
这个文件提供了判断文件是否是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;
}
和第四个类似,此类用于处理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();
}
此文件用于判断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
此类用于将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的几个文件进行分析。