天天看点

GPS的NMEA数据转Google Earth的KML数据工具

1. 背景

在关于GPS定位的研究分析过程中,经常想要看在一段测试路径上,GPS的定位准确性如何,实际上,很自然的会想到把GPS数据显示在地图上,然而,并且,Google提供的Google Earth就是一个不错的工具,至于为什么不选择别的地图工具,是因为别的地图需要进行WGS84坐标系进行转换。

2. 实现代码

2.1 NMEA格式数据转化为KML的头文件

#ifndef CONVERT_H
#define CONVERT_H
#include <iostream>
#include <string>
#include <vector>
#include <fstream>
#include <memory.h>
#include <math.h>
#include <sstream>
#include <string.h>
using namespace std;

// 模块名称: convert
// Author: HSW
// Date: 2018-01-17
// 功能: 实现NMEA GGA数据分析
//
//

// NMEA GGA 数据分析
int splitString1(string lineBuf, vector<string>& subStrs, char dim = ',');


// ddmm.mmmm <==> dd.dddd
int fromDMToDegree(double ddmmPointMMMM, double& ddPointDDDD);
int fromDegreeToDM(double ddPointDDDD, double& ddmmPointMMMM);

// KML: 保存为KML文件
// strs: 分别包含: 描述
//                名称
//                经度
//                纬度
//                高度
int convertToKMLWithoutName(vector<string>& pointInfo, ofstream* pOfstr, int Mode);

#endif // CONVERT_H
           
#include "convert.h"
#include <windows.h>
#include <ctmstrconvert.h>

static wstring kml_S = L"<kml xmlns=\"http://earth.google.com/kml/2.0\">\r\n";

static wstring kmlFolder_S = L"<Folder>\r\n";

static wstring kmlPlacemark_S = L"<Placemark>";

static wstring kmlDescription_S = L"<description>";
static wstring kmlDescription_E = L"</description>";

static wstring kmlName_S = L"<name>";
static wstring kmlName_E = L"</name>";

static wstring kmlLookat_S = L"<LookAt>";

static wstring kmlLon_S = L"<longitude>";
static wstring kmlLon_E = L"</longitude>";

static wstring kmlLat_S = L"<latitude>";
static wstring kmlLat_E = L"</latitude>";

static wstring kmlRange_S = L"<range>";
static wstring kmlRange_E = L"</range>";

static wstring kmlTilt_S = L"<tilt>";
static wstring kmlTilt_E = L"</tilt>";

static wstring kmlHeading_S = L"<heading>";
static wstring kmlHeading_E = L"</heading>";

static wstring kmlLookat_E = L"</LookAt>";

static wstring kmlPoint_S = L"<Point>";

static wstring kmlCoordinates_S = L"<coordinates>";
static wstring kmlCoordinates_E = L"</coordinates>";

static wstring kmlPoint_E = L"</Point>";

static wstring kmlPlacemark_E = L"</Placemark>\r\n";

static wstring kmlFolder_E = L"</Folder>\r\n";

static wstring kml_E = L"</kml>\r\n";

static wstring kml_default_range = L"20000";
static wstring kml_default_tilt  = L"0";
static wstring kml_default_heading = L"3";
static string  kml_default_altitude = "0";
static wstring kml_default_pointSetName = L"Positions";

// 实现差分前后的数据处理
//
static char exampleGGA[4][128] = {"$GPGGA,062926.000,4538.7429,N,12639.4155,E,2,08,1.0,196.6,M,0.0,M,23,1544*6C",
                                  "$GPGGA,062926.000,4538.7429,N,12639.4155,W,2,08,1.0,196.6,M,0.0,M,23,1544*6C",
                                  "$GPGGA,062926.000,4538.7429,S,12639.4155,E,2,08,1.0,196.6,M,0.0,M,23,1544*6C",
                                  "$GPGGA,062926.000,4538.7429,N,12639.4155,W,2,08,1.0,196.6,M,0.0,M,23,1544*6C"};

// 将std::string 转为 利用UTF-8编码的std::string
// step1: 将std::string 转为 std::wstring
// 方法1:
BOOL StringToWString(const std::string &str,std::wstring &wstr)
{
    int nLen = (int)str.length();
    wstr.resize(nLen,L' ');

    int nResult = MultiByteToWideChar(CP_ACP,0,(LPCSTR)str.c_str(),nLen,(LPWSTR)wstr.c_str(),nLen);

    if (nResult == 0)
    {
        return FALSE;
    }

    return TRUE;
}
//wstring高字节不为0,返回FALSE
BOOL WStringToString(const std::wstring &wstr,std::string &str)
{
    int nLen = (int)wstr.length();
    str.resize(nLen,' ');

    int nResult = WideCharToMultiByte(CP_ACP,0,(LPCWSTR)wstr.c_str(),nLen,(LPSTR)str.c_str(),nLen,NULL,NULL);

    if (nResult == 0)
    {
        return FALSE;
    }

    return TRUE;
}

std::string to_utf8(const wchar_t* buffer, int len)
{
    int nChars = ::WideCharToMultiByte(
                CP_UTF8,
                0,
                buffer,
                len,
                NULL,
                0,
                NULL,
                NULL);
    if (nChars == 0) return "";

    string newbuffer;
    newbuffer.resize(nChars) ;
    ::WideCharToMultiByte(
                CP_UTF8,
                0,
                buffer,
                len,
                const_cast< char* >(newbuffer.c_str()),
                nChars,
                NULL,
                NULL);

    return newbuffer;
}

// std::string 转为 std::string UTF-8
std::string to_utf8(const std::wstring& str)
{
    return to_utf8(str.c_str(), (int)str.size());
}


// NMEA GGA 数据分析
int splitString1(string lineBuf, vector<string>& subStrs, char dim)
{
    size_t startPos = lineBuf.find('$');
    size_t endPos = lineBuf.find('*');
    int iter;
    int count = 0;
    char subStr[64] = {0};
    int findGGA = 0;
    if(startPos != string::npos && endPos != string::npos)
    {
        for(iter = startPos + 1; iter < endPos; ++iter)
        {
            char ch = lineBuf.at(iter);
            if(ch == dim)
            {
                string tmp = string(subStr);
                if(tmp.find("GGA") != string::npos)
                {
                    findGGA = 1;
                }
                subStrs.push_back(string(subStr));
                memset(subStr,0, sizeof(subStr));
                count = 0;
            }
            else
            {
                subStr[count] = lineBuf.at(iter);
                count++;
            }
        }
        subStrs.push_back(string(subStr));
    }
    if(findGGA)
    {
        return 0;
    }
    else
    {
        return -1;
    }
}

// DD.DDDD ==> ddmm.mmmm
int fromDegreeToDM(double ddPointDDDD, double& ddmmPointMMMM)
{
    int tmpInt = (int)floor(ddPointDDDD);
    double tmpFloat = ddPointDDDD - tmpInt;
    ddmmPointMMMM = tmpInt * 100 + tmpFloat * 60;
    return 0;
}

// ddmm.mmmm ==> DD.DDDD
int fromDMToDegree(double ddmmPointMMMM, double& ddPointDDDD)
{
    int tmpInt = floor(ddmmPointMMMM / 100);
    double tmpFloat = ddmmPointMMMM - tmpInt * 100;
    ddPointDDDD = tmpInt + tmpFloat / 60;
    return 0;
}

// nmea 计算检查和
unsigned char checkSum(const char* buf)
{
    int isStart = 0;
    int iter = 0;
    unsigned char checksum = 0;
    while(1)
    {
        if((unsigned char)buf[iter] == '$')
        {
            isStart = 1;
            ++iter;
            continue;
        }
        if(isStart)
        {
            if((unsigned char)buf[iter] == '*')
            {
                break;
            }
            checksum ^= buf[iter];
        }
        ++iter;
    }
    // cout << "checksum = " << checksum << endl;
    return checksum;
}

// KML: 保存为KML文件, 不包含点的名称
// strs: 分别包含: 描述
//                经度
//                纬度
// 注意:坐标点的高度默认设置为0
int convertToKMLWithoutName(vector<string>& pointInfo, ofstream* pOfstr, int Mode)
{
    wstring utf8wStr = L""; // 宽字符
    string utf8Str = "";    // UTF-8字符串
    if(Mode == 0)
    {
        utf8Str = to_utf8(kml_S);
        // pOfstr->write(kml_S.c_str(), kml_S.length());
        pOfstr->write(utf8Str.c_str(), utf8Str.length());
        utf8Str.clear();

        utf8Str = to_utf8(kmlFolder_S);
        // pOfstr->write(kmlFolder_S.c_str(), kmlFolder_S.length());
        pOfstr->write(utf8Str.c_str(), utf8Str.length());
        utf8Str.clear();

        // 名称: kml文件的名称
        utf8Str = to_utf8(kmlName_S);
        //pOfstr->write(kmlName_S.c_str(), kmlName_S.length());
        pOfstr->write(utf8Str.c_str(), utf8Str.length());
        utf8Str.clear();

        StringToWString(pointInfo[0], utf8wStr);
        utf8Str = to_utf8(utf8wStr);
        //pOfstr->write(pointInfo[0].c_str(), pointInfo[0].length());
        pOfstr->write(utf8Str.c_str(), utf8Str.length());
        utf8Str.clear();
        utf8wStr.clear();

        utf8Str = to_utf8(kmlName_E);
        // pOfstr->write(kmlName_E.c_str(), kmlName_E.length());
        pOfstr->write(utf8Str.c_str(), utf8Str.length());
        utf8Str.clear();


        // 点集合名称: kml
        // 点集合名称: kml
        string outputPath = pointInfo[0];
        size_t pos = outputPath.find_last_of('/');
        if(pos != string::npos)
        {
            utf8Str = to_utf8(kmlName_S);
            //pOfstr->write(kmlName_S.c_str(), kmlName_S.length());
            pOfstr->write(utf8Str.c_str(), utf8Str.length());
            utf8Str.clear();

            // utf8Str = to_utf8(kml_default_pointSetName);
            //pOfstr->write(kml_default_pointSetName.c_str(), kml_default_pointSetName.length());

            CTMStrConvert converter;
            wstring   wPointsetName = converter.StringToWString(outputPath.substr(pos + 1, outputPath.length() - pos -1));
            utf8Str = converter.WStringToUtf8(wPointsetName);

            pOfstr->write(utf8Str.c_str(), utf8Str.length());
            utf8Str.clear();
            utf8wStr.clear();

            utf8Str = to_utf8(kmlName_E);
            // pOfstr->write(kmlName_E.c_str(), kmlName_E.length());
            pOfstr->write(utf8Str.c_str(), utf8Str.length());
            utf8Str.clear();
        }
        else
        {
            utf8Str = to_utf8(kmlName_S);
            //pOfstr->write(kmlName_S.c_str(), kmlName_S.length());
            pOfstr->write(utf8Str.c_str(), utf8Str.length());
            utf8Str.clear();

            utf8Str = to_utf8(kml_default_pointSetName);
            //pOfstr->write(kml_default_pointSetName.c_str(), kml_default_pointSetName.length());
            pOfstr->write(utf8Str.c_str(), utf8Str.length());
            utf8Str.clear();
            utf8wStr.clear();

            utf8Str = to_utf8(kmlName_E);
            // pOfstr->write(kmlName_E.c_str(), kmlName_E.length());
            pOfstr->write(utf8Str.c_str(), utf8Str.length());
            utf8Str.clear();
        }

        wstring lineBuf = L"\r\n";
        utf8Str = to_utf8(lineBuf);
        // pOfstr->write(lineBuf.c_str(), lineBuf.length());
        pOfstr->write(utf8Str.c_str(), utf8Str.length());
        utf8Str.clear();

    }
    else if(Mode == 1)
    {
        // Placemark
        utf8Str = to_utf8(kmlPlacemark_S);
        // pOfstr->write(kmlPlacemark_S.c_str(), kmlPlacemark_S.length());
        pOfstr->write(utf8Str.c_str(), utf8Str.length());
        utf8Str.clear();

        // -描述
        utf8Str = to_utf8(kmlDescription_S);
        // pOfstr->write(kmlDescription_S.c_str(), kmlDescription_S.length());
        pOfstr->write(utf8Str.c_str(), utf8Str.length());
        utf8Str.clear();

        StringToWString(pointInfo[0], utf8wStr);
        utf8Str = to_utf8(utf8wStr);
        // pOfstr->write(pointInfo[0].c_str(), pointInfo[0].length());
        pOfstr->write(utf8Str.c_str(), utf8Str.length());
        utf8Str.clear();
        utf8wStr.clear();

        utf8Str = to_utf8(kmlDescription_E);
        // pOfstr->write(kmlDescription_E.c_str(), kmlDescription_E.length());
        pOfstr->write(utf8Str.c_str(), utf8Str.length());
        utf8Str.clear();
#if 0
        // -名称
        utf8Str = to_utf8(kmlName_S);
        //pOfstr->write(kmlName_S.c_str(), kmlName_S.length());
        pOfstr->write(utf8Str.c_str(), utf8Str.length());
        utf8Str.clear();

        StringToWString(pointInfo[1], utf8wStr);
        utf8Str = to_utf8(utf8wStr);
        // pOfstr->write(pointInfo[1].c_str(), pointInfo[1].length());
        pOfstr->write(utf8Str.c_str(), utf8Str.length());
        utf8Str.clear();
        utf8wStr.clear();

        utf8Str = to_utf8(kmlName_E);
        // pOfstr->write(kmlName_E.c_str(), kmlName_E.length());
        pOfstr->write(utf8Str.c_str(), utf8Str.length());
        utf8Str.clear();
#endif
        // -LookAt
        utf8Str = to_utf8(kmlLookat_S);
        //  pOfstr->write(kmlLookat_S.c_str(), kmlLookat_S.length());
        pOfstr->write(utf8Str.c_str(), utf8Str.length());
        utf8Str.clear();

        // --lon 经度
        utf8Str = to_utf8(kmlLon_S);
        // pOfstr->write(kmlLon_S.c_str(), kmlLon_S.length());
        pOfstr->write(utf8Str.c_str(), utf8Str.length());
        utf8Str.clear();

        StringToWString(pointInfo[1], utf8wStr);
        utf8Str = to_utf8(utf8wStr);
        // pOfstr->write(pointInfo[2].c_str(), pointInfo[2].length());
        pOfstr->write(utf8Str.c_str(), utf8Str.length());
        utf8Str.clear();
        utf8wStr.clear();

        utf8Str = to_utf8(kmlLon_E);
        // pOfstr->write(kmlLon_E.c_str(), kmlLon_E.length());
        pOfstr->write(utf8Str.c_str(), utf8Str.length());
        utf8Str.clear();

        // --lat 纬度
        utf8Str = to_utf8(kmlLat_S);
        // pOfstr->write(kmlLat_S.c_str(), kmlLat_S.length());
        pOfstr->write(utf8Str.c_str(), utf8Str.length());
        utf8Str.clear();

        StringToWString(pointInfo[2], utf8wStr);
        utf8Str = to_utf8(utf8wStr);
        // pOfstr->write(pointInfo[3].c_str(), pointInfo[3].length());
        pOfstr->write(utf8Str.c_str(), utf8Str.length());
        utf8Str.clear();
        utf8wStr.clear();

        utf8Str = to_utf8(kmlLat_E);
        // pOfstr->write(kmlLat_E.c_str(), kmlLat_E.length());
        pOfstr->write(utf8Str.c_str(), utf8Str.length());
        utf8Str.clear();

        // --range
        utf8Str = to_utf8(kmlRange_S);
        // pOfstr->write(kmlRange_S.c_str(), kmlRange_S.length());
        pOfstr->write(utf8Str.c_str(), utf8Str.length());
        utf8Str.clear();

        utf8Str = to_utf8(kml_default_range);
        // pOfstr->write(kml_default_range.c_str(), kml_default_range.length());
        pOfstr->write(utf8Str.c_str(), utf8Str.length());
        utf8Str.clear();
        utf8wStr.clear();

        utf8Str = to_utf8(kmlRange_E);
        // pOfstr->write(kmlRange_E.c_str(), kmlRange_E.length());
        pOfstr->write(utf8Str.c_str(), utf8Str.length());
        utf8Str.clear();

        // --tilt
        utf8Str = to_utf8(kmlTilt_S);
        // pOfstr->write(kmlTilt_S.c_str(), kmlTilt_S.length());
        pOfstr->write(utf8Str.c_str(), utf8Str.length());
        utf8Str.clear();

        utf8Str = to_utf8(kml_default_tilt);
        // pOfstr->write(kml_default_tilt.c_str(), kml_default_tilt.length());
        pOfstr->write(utf8Str.c_str(), utf8Str.length());
        utf8Str.clear();
        utf8wStr.clear();

        utf8Str = to_utf8(kmlTilt_E);
        // pOfstr->write(kmlTilt_E.c_str(), kmlTilt_E.length());
        pOfstr->write(utf8Str.c_str(), utf8Str.length());
        utf8Str.clear();

        // --heading
        utf8Str = to_utf8(kmlHeading_S);
        // pOfstr->write(kmlHeading_S.c_str(), kmlHeading_S.length());
        pOfstr->write(utf8Str.c_str(), utf8Str.length());
        utf8Str.clear();

        utf8Str = to_utf8(kml_default_heading);
        // pOfstr->write(kml_default_heading.c_str(), kml_default_heading.length());
        pOfstr->write(utf8Str.c_str(), utf8Str.length());
        utf8Str.clear();
        utf8wStr.clear();

        utf8Str = to_utf8(kmlHeading_E);
        // pOfstr->write(kmlHeading_E.c_str(), kmlHeading_E.length());
        pOfstr->write(utf8Str.c_str(), utf8Str.length());
        utf8Str.clear();

        // -LookAt
        utf8Str = to_utf8(kmlLookat_E);
        // pOfstr->write(kmlLookat_E.c_str(), kmlLookat_E.length());
        pOfstr->write(utf8Str.c_str(), utf8Str.length());
        utf8Str.clear();

        // -Point
        utf8Str = to_utf8(kmlPoint_S);
        // pOfstr->write(kmlPoint_S.c_str(), kmlPoint_S.length());
        pOfstr->write(utf8Str.c_str(), utf8Str.length());
        utf8Str.clear();

        // --coordinate
        utf8Str = to_utf8(kmlCoordinates_S);
        // pOfstr->write(kmlCoordinates_S.c_str(), kmlCoordinates_S.length());
        pOfstr->write(utf8Str.c_str(), utf8Str.length());
        utf8Str.clear();

        string coordinate = pointInfo[1] + "," + pointInfo[2] + "," + kml_default_altitude;
        StringToWString(coordinate, utf8wStr);
        utf8Str = to_utf8(utf8wStr);
        // pOfstr->write(coordinate.c_str(), coordinate.length());
        pOfstr->write(utf8Str.c_str(), utf8Str.length());
        utf8Str.clear();
        utf8wStr.clear();

        // --coordinate
        utf8Str = to_utf8(kmlCoordinates_E);
        // pOfstr->write(kmlCoordinates_E.c_str(), kmlCoordinates_E.length());
        pOfstr->write(utf8Str.c_str(), utf8Str.length());
        utf8Str.clear();

        // -Point
        utf8Str = to_utf8(kmlPoint_E);
        // pOfstr->write(kmlPoint_E.c_str(), kmlPoint_E.length());
        pOfstr->write(utf8Str.c_str(), utf8Str.length());
        utf8Str.clear();

        // Placemark
        utf8Str = to_utf8(kmlPlacemark_E);
        // pOfstr->write(kmlPlacemark_E.c_str(), kmlPlacemark_E.length());
        pOfstr->write(utf8Str.c_str(), utf8Str.length());
        utf8Str.clear();

    }
    else if(Mode == 2)
    {
        utf8Str = to_utf8(kmlFolder_E);
        // pOfstr->write(kmlFolder_E.c_str(), kmlFolder_E.length());
        pOfstr->write(utf8Str.c_str(), utf8Str.length());
        utf8Str.clear();

        utf8Str = to_utf8(kml_E);
        // pOfstr->write(kml_E.c_str(), kml_E.length());
        pOfstr->write(utf8Str.c_str(), utf8Str.length());
        utf8Str.clear();
    }
    pOfstr->flush();
    return 0;
}
           

2.2 ASCII字符集和UTF-8字符集转换类

#ifndef CTMSTRCONVERT_H
#define CTMSTRCONVERT_H
#include <windows.h>
#include <iostream>
#include <string>
#include <wchar.h>

using namespace std;

// 模块名称:windows中字符串转换类
// Author: HSW
// Date: 2018-01-30
// 功能: 实现多字节字符串与宽字节字符串之间的转换
// 多字节集合: ASCII / UTF8 等, 一般情况下,ASCII为常用的字符串;UTF8主要在网页
// 宽字节集合: 表示汉字等需要使用宽字节

class CTMStrConvert
{
public:
    CTMStrConvert();
    ~CTMStrConvert();
public:
    wstring StringToWString(const char* abuffer, const int nLen);
    wstring StringToWString(const string &astr);
    wstring Utf8ToWString(const char* ubuffer, int nLen);
    wstring Utf8ToWString(const string ustr);
    string  WStringToString(const wchar_t* wbuffer, const int nLen);
    string  WStringToString(const wstring &wstr);
    string  WStringToUtf8(const wchar_t* wbuffer, int nLen);
    string  WStringToUtf8(const wstring& wstr);
    string  StringToUtf8(const string &astr);
    string  StringToUtf8(const char* abuffer, const int nLen);
    string  Utf8ToString(const string ustr);
    string  Utf8ToString(const char* ubuffer, const int nLen);
};

#endif // CTMSTRCONVERT_H
           
#include "ctmstrconvert.h"

CTMStrConvert::CTMStrConvert()
{

}

CTMStrConvert::~CTMStrConvert()
{

}



// 功能: 多字节(ASCII)字符串转宽字节字符串
// aBuffer:  ASCII的多字节字符数组
// nLen: ASCII的多字节数组长度
// 返回: 成功返回包含内容的字符串,失败返回空串
// 日期: 2018-01-30
// 修改记录:
//
wstring CTMStrConvert::StringToWString(const char* abuffer, const int nLen)
{
    // 需要的缓冲区大小
    int nResult = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)abuffer, nLen, NULL, 0);

    if (nResult == 0)
    {
        cout << "Get Wide Char Buffer Size Failed " << endl;
        return L"";
    }

    wstring wstr;
    wstr.resize(nResult, L' ');
    nResult = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)abuffer, nLen, (LPWSTR)wstr.c_str(), nResult);

    if(nResult == 0)
    {
        cout << "From MultiByte To Wide Char Translate Failed " << endl;
        return L"";
    }
    else
    {
        return wstr;
    }
}

// 功能: 多字节(ASCII)字符串转宽字节字符串
// astr:  ASCII的多字节字符串
// 返回: 成功返回包含内容的字符串,失败返回空串
// 日期: 2018-01-30
// 修改记录:
//
wstring CTMStrConvert::StringToWString(const string &astr)
{
    return StringToWString(astr.c_str(), astr.length());
}


// 功能: 宽字节字符数组转为多字节(ASCII)字符串
// wbuffer:  ASCII的多字节字符串
// len: 宽字节字符串
// 返回: 成功返回包含内容的字符串,失败返回空串
// 日期: 2018-01-30
// 修改记录:
//
string CTMStrConvert::WStringToString(const wchar_t* wbuffer, const int nLen)
{
    // 获取需要的缓冲区的大小
    int nResult = WideCharToMultiByte(CP_ACP, 0, (LPCWSTR)wbuffer, nLen, NULL, 0, NULL, NULL);

    if (nResult == 0)
    {
        cout << "Get Multi Byte Buffer Size Failed " << endl;
        return "";
    }

    string astr;
    astr.resize(nResult, ' ');
    nResult = WideCharToMultiByte(CP_ACP, 0, (LPCWSTR)wbuffer, nLen, (LPSTR)astr.c_str(), nResult, NULL, NULL);

    if (nResult == 0)
    {
        cout << "From Wide Char To Multi Byte Translate Failed " << endl;
        return "";
    }
    else
    {
        return astr;
    }
}


// 功能:  宽字节字符串转为多字节(ASCII)字符串
// wstr: 宽字节字符串
// 返回: 成功返回包含内容的字符串,失败返回空串
// 日期: 2018-01-30
// 修改记录:
//
string CTMStrConvert::WStringToString(const wstring &wstr)
{
    return WStringToString(wstr.c_str(), wstr.length());
}


// 功能:   宽字节字符数组转为UTF-8多字节字符串
// wbuffer: 宽字节字符数组
// nLen:    宽字节字符数组的长度
// 返回值:  成功返回包含内容的字符串,失败返回空串
// 日期: 2018-01-30
// 修改记录:
//
string CTMStrConvert::WStringToUtf8(const wchar_t* wbuffer, int nLen)
{
    // 获取所需的缓冲区大小
    int nResult = WideCharToMultiByte(CP_UTF8, 0, wbuffer, nLen, NULL, 0, NULL, NULL);
    if (nResult == 0)
    {
        cout << "Get Multi Byte Buffer Size Failed " << endl;
        return "";
    }

    string ustr;
    ustr.resize(nResult, ' ');
    nResult = WideCharToMultiByte(CP_UTF8, 0, wbuffer, nLen, const_cast< char* >(ustr.c_str()), nResult, NULL, NULL);
    if(nResult == 0)
    {
        cout << "Wide Char To Multi Byte Translate Failed " << endl;
        return "";
    }
    else
    {
        return ustr;
    }
}

// 功能: 宽字节字符串 转为 UTF-8
// wstr: 宽字节字符串
// 返回值: 成功返回包含内容的字符串,失败返回空串
// 日期: 2018-01-30
// 修改记录:
//
string CTMStrConvert::WStringToUtf8(const wstring& wstr)
{
    return WStringToUtf8(wstr.c_str(), (int)wstr.size());
}

// 功能: UTF-8多字节数组 转为 宽字节字符串
// ubuffer: UTF-8多字节数组
// len: UTF-8多字节数组长度
// 返回值: 成功返回包含内容的字符串,失败返回空串
// 日期: 2018-01-30
// 修改记录:
//
wstring CTMStrConvert::Utf8ToWString(const char* ubuffer, int nLen)
{
    // 需要的缓冲区大小
    int nResult = MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)ubuffer, nLen, NULL, 0);

    if (nResult == 0)
    {
        cout << "Get Wide Char Buffer Size Failed " << endl;
        return L"";
    }

    wstring wstr;
    wstr.resize(nResult, L' ');
    nResult = MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)ubuffer, nLen, (LPWSTR)wstr.c_str(), nResult);

    if(nResult == 0)
    {
        cout << "From MultiByte To Wide Char Translate Failed " << endl;
        return L"";
    }
    else
    {
        return wstr;
    }
}

// 功能:   UTF-8多字节字符串 转为 宽字节字符串
// ustr:  UTF-8多字节字符串
// 返回值: 成功返回包含内容的字符串,失败返回空串
// 日期: 2018-01-30
// 修改记录:
wstring CTMStrConvert::Utf8ToWString(const string ustr)
{
    return Utf8ToWString(ustr.c_str(), ustr.length());
}

// 功能: 多字节(ASCII)字符串 转为 UTF-8多字节字符串
// astr: 多字节(ASCII)字符串
// 返回值: 成功返回包含内容的字符串,失败返回空串
// 日期: 2018-03-29
// 修改记录:
string  CTMStrConvert::StringToUtf8(const string &astr)
{
    wstring wstr = StringToWString(astr);
    return WStringToUtf8(wstr);
}

// 功能: 多字节(ASCII)字符串 转为 UTF-8多字节字符串
// abuffer: 多字节(ASCII)字符串
// 返回值: 成功返回包含内容的字符串,失败返回空串
// 日期: 2018-03-29
// 修改记录:
string  CTMStrConvert::StringToUtf8(const char* abuffer, const int nLen)
{
    wstring wstr = StringToWString(abuffer, nLen);
    return WStringToUtf8(wstr);
}

// 功能:  UTF-8多字节字符串 转为 多字节(ASCII)字符串
// ustr:  UTF-8多字节字符串
// 返回值: 成功返回包含内容的字符串,失败返回空串
// 日期: 2018-03-29
// 修改记录:
string  CTMStrConvert::Utf8ToString(const string ustr)
{
    wstring wstr = Utf8ToWString(ustr);
    return WStringToString(wstr);
}

// 功能:  UTF-8多字节字符串 转为 多字节(ASCII)字符串
// ubuffer:  UTF-8多字节字符串
// 返回值: 成功返回包含内容的字符串,失败返回空串
// 日期: 2018-03-29
// 修改记录:
string  CTMStrConvert::Utf8ToString(const char* ubuffer, const int nLen)
{
    wstring wstr = Utf8ToWString(ubuffer, nLen);
    return WStringToString(wstr);
}
           

2.3 QT界面类

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <iostream>
#include <vector>
#include <string>

using namespace std;

namespace Ui {
class MainWindow;
}

typedef struct st_Support_Format
{
    unsigned int supportTXT;
    unsigned int supportCSV;
    unsigned int supportRDF;
}st_Support_Format;

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();
private:
    void initialWindows();
    int getFilePaths();
private slots:
    void auto_slot();
    void split_slot();

    void batch_slot();
    void support_txt_slot();
    void support_csv_slot();

    void openfile_slot();
    void nmea_slot();
private:
    Ui::MainWindow*   ui;
    unsigned int      m_isAuto;          // 是否通过Google自动打开
    unsigned int      m_isSplit;         // 是否对每个KML文件分开显示
    unsigned int      m_isBatch;         // 是否批处理
    string            m_googleEarthPath; // google Earth 的路径
    string            m_firstPath;       // 选择的文件路径
    string            m_guiFirstPath;    // 用于在GUI显示选择的文件路径
    vector<string>    m_filePaths;       // 文件路径
    st_Support_Format m_supportFormat;   // 支持的文件格式
};

#endif // MAINWINDOW_H
           
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "convert.h"
#include "ctmstrconvert.h"
#include <QFileDialog>
#include <QDir>
#include <QFile>
#include <QTextCodec>
#include <QMessageBox>
#include <QSettings>
#include <QDebug>
#include <windows.h>

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    // 初始化windows
    initialWindows();

    // Connect <===> Slot
    connect(ui->autoCheckBox, SIGNAL(clicked(bool)), this, SLOT(auto_slot()));
    connect(ui->splitCheckBox, SIGNAL(clicked(bool)), this, SLOT(split_slot()));

    connect(ui->batchCheckBox, SIGNAL(clicked(bool)), this, SLOT(batch_slot()));
    connect(ui->txtCheckBox, SIGNAL(clicked(bool)), this, SLOT(support_txt_slot()));
    connect(ui->csvCheckBox, SIGNAL(clicked(bool)), this, SLOT(support_csv_slot()));

    connect(ui->openFilePushButton, SIGNAL(clicked(bool)), this, SLOT(openfile_slot()));
    connect(ui->convertNMEAPushButton, SIGNAL(clicked(bool)), this, SLOT(nmea_slot()));
    // 设置support Push Button 的边框
    ui->supportPushButton->setEnabled(false);
    ui->supportPushButton->setStyleSheet("border: none");
}

MainWindow::~MainWindow()
{
    delete ui;
}



void MainWindow::initialWindows()
{
    if(ui->autoCheckBox->isChecked())
    {
        m_isAuto = 1;
        ui->splitCheckBox->setEnabled(true);
    }
    else
    {
        m_isAuto = 0;
        ui->splitCheckBox->setEnabled(false);
    }

    if(ui->splitCheckBox->isChecked())
    {
        m_isSplit = 1;
    }
    else
    {
        m_isSplit = 0;
    }

    if(ui->batchCheckBox->isChecked())
    {
        m_isBatch = 1;
        ui->txtCheckBox->setEnabled(true);
        ui->csvCheckBox->setEnabled(true);
    }
    else
    {
        m_isBatch = 0;
        ui->txtCheckBox->setEnabled(false);
        ui->csvCheckBox->setEnabled(false);
    }

    m_firstPath = "";                      // 初始化为空
    m_filePaths.clear();

    QPixmap imagePix;
    imagePix.load("gpsImage.jpg");        // 加载
    ui->tmLabel->setScaledContents(true); // 设置自适应
    ui->tmLabel->clear();                 // 清空
    ui->tmLabel->setPixmap(imagePix);     // 加载到Label标签
    ui->tmLabel->show();                  // 显示
}

void MainWindow::auto_slot()
{
    if(ui->autoCheckBox->isChecked())
    {
        m_isAuto = 1;
        ui->splitCheckBox->setEnabled(true);
        // Check Google Earth
        QSettings *configIniRead = new QSettings("TM_Google_Earth.ini", QSettings::IniFormat);
        QString googleEarthPath = configIniRead->value("/Path/googleEarthPath").toString();
        // qDebug() << googleEarthPath;
        if(googleEarthPath.isEmpty())
        {
            // 释放文件
            configIniRead->deleteLater();

            // 弹出选择google Earth 的路径
            QMessageBox Msg(QMessageBox::Question,
                            QString(tr("配置GoogleEarth")),
                            QString(tr("选择GoogleEarth路径(例如:C:\\Program Files (x86)\\Google\\Google Earth\\client\\googleearth.exe)")));
            QAbstractButton *pYesBtn = (QAbstractButton *)Msg.addButton(QString(tr("是")), QMessageBox::YesRole);
            QAbstractButton *pNoBtn = (QAbstractButton *)Msg.addButton(QString(tr("否")), QMessageBox::NoRole);
            Msg.exec();

            if (Msg.clickedButton() != pYesBtn)
            {
                m_isAuto = 0;
                ui->autoCheckBox->setChecked(false);
                ui->splitCheckBox->setEnabled(false);
                return ;
            }

            // 文件选择对话框:选择Google Earth 路径
            QString qFilePath = QFileDialog::getOpenFileName(this, QString(tr("选择GoogleEarth路径")), "", "File(*.exe)", NULL);
            if(!qFilePath.isEmpty())
            {
                m_googleEarthPath = qFilePath.toStdString();

                QSettings *configIniWrite = new QSettings("TM_Google_Earth.ini", QSettings::IniFormat);
                //向ini文件中写入内容,setValue函数的两个参数是键值对
                configIniWrite->setValue("/Path/googleEarthPath", qFilePath);
                configIniWrite->deleteLater();
            }
            else
            {
                QMessageBox Msg(QMessageBox::Question,
                                QString(tr("配置GoogleEarth")),
                                QString(tr("设置Google Earth 路径失败!")));

                Msg.exec();
                m_isAuto = 0;
                ui->splitCheckBox->setEnabled(false);
            }
        }
        else
        {
            m_googleEarthPath = googleEarthPath.toStdString();
            configIniRead->deleteLater();
        }
    }
    else
    {
        m_isAuto = 0;
        ui->splitCheckBox->setEnabled(false);
    }// if ... else ... isChecked()
}

void MainWindow::split_slot()
{
    if(ui->splitCheckBox->isChecked())
    {
        m_isSplit = 1;
    }
    else
    {
        m_isSplit = 0;
    }
}


void MainWindow::batch_slot()
{
    if(ui->batchCheckBox->isChecked())
    {
        m_isBatch = 1;
        ui->txtCheckBox->setEnabled(true);
        ui->csvCheckBox->setEnabled(true);
    }
    else
    {
        m_isBatch = 0;
        ui->txtCheckBox->setChecked(false);
        ui->csvCheckBox->setChecked(false);

        ui->txtCheckBox->setEnabled(false);
        ui->csvCheckBox->setEnabled(false);
    }

    if(!ui->txtCheckBox->isChecked()
            && !ui->csvCheckBox->isChecked())
    {

        ui->convertNMEAPushButton->setEnabled(true);
    }
}

void MainWindow::support_txt_slot()
{
    if(ui->txtCheckBox->isChecked())
    {
        m_supportFormat.supportTXT = 1;
        ui->convertNMEAPushButton->setEnabled(true);
    }
    else
    {
        m_supportFormat.supportTXT = 0;
        if(!ui->txtCheckBox->isChecked()
                && !ui->csvCheckBox->isChecked())
        {
            ui->convertNMEAPushButton->setEnabled(true);
        }
        else if(!ui->csvCheckBox->isChecked())
        {
            ui->convertNMEAPushButton->setEnabled(false);
        }
    }
}

void MainWindow::support_csv_slot()
{
    if(ui->csvCheckBox->isChecked())
    {
        m_supportFormat.supportCSV = 1;
        ui->convertNMEAPushButton->setEnabled(true);
    }
    else
    {
        m_supportFormat.supportCSV = 0;
        if(!ui->txtCheckBox->isChecked()
                && !ui->csvCheckBox->isChecked())
        {
            ui->convertNMEAPushButton->setEnabled(true);
        }
        else if(!ui->txtCheckBox->isChecked())
        {
            ui->convertNMEAPushButton->setEnabled(false);
        }
    }
}


void MainWindow::openfile_slot()
{
    if(m_isBatch)
    {// 批量处理
        QString qFileDir = QFileDialog::getExistingDirectory(this, QString(tr("选择文件夹")), "");
        if(!qFileDir.isEmpty())
        {
            QTextCodec *codec =QTextCodec::codecForName("UTF-8");
            QString str = codec->toUnicode(qFileDir.toStdString().c_str());
            wstring fileName = str.toStdWString();
            CTMStrConvert converter;
            m_firstPath = converter.WStringToString(fileName);
            m_guiFirstPath = qFileDir.toStdString();
        }
        else
        {
            m_firstPath    = "";
            m_guiFirstPath = "";
        }

    }
    else
    {// 处理单个文件
        QString qFilePath = QFileDialog::getOpenFileName(this, QString(tr("选择文件")), "", "File(*.txt *.csv)", NULL);
        if(!qFilePath.isEmpty())
        {
            QTextCodec *codec =QTextCodec::codecForName("UTF-8");
            QString str = codec->toUnicode(qFilePath.toStdString().c_str());
            wstring fileName = str.toStdWString();
            CTMStrConvert converter;
            m_firstPath = converter.WStringToString(fileName);
            m_guiFirstPath = qFilePath.toStdString();
        }
        else
        {
            m_firstPath    = "";
            m_guiFirstPath = "";
        }
    }
    ui->openTextBrowser->setText(QString::fromStdString(m_guiFirstPath));
}

void MainWindow::nmea_slot()
{
    int ret;
    ret = getFilePaths();
    if(ret == 0)
    {
        vector<string>           kmlPaths;    // 存储输出的kml文件路径
        vector<string>::iterator iter;
        for(iter = m_filePaths.begin(); iter != m_filePaths.end(); ++iter)
        {
            ifstream ifstr(*iter, ios_base::binary | ios_base::in);
            string outputFilePath = (*iter) + ".kml";
            ofstream* pOfstr = new ofstream(outputFilePath, ios_base::out | ios_base::binary | ios_base::trunc);
            if(pOfstr == NULL)
            {
                QString message = QString(tr("打开(创建)输出文件(")) + QString::fromStdString(outputFilePath) + QString(tr(")失败"));
                QMessageBox msgBox(QMessageBox::Warning,"Information",message,QMessageBox::Yes,NULL);
                msgBox.exec();
                continue;
            }
            else if(!pOfstr->is_open())
            {
                QString message = QString(tr("打开(创建)输出文件(")) + QString::fromStdString(outputFilePath) + QString(tr(")失败"));
                QMessageBox msgBox(QMessageBox::Warning,"Information",message,QMessageBox::Yes,NULL);
                msgBox.exec();
                continue;
            }

            if(ifstr.is_open())
            {
                int isFirst = 1;
                vector<string> kmlPointInfo;
                int emptyCnt = 0;
                char bufPos[64] = {0};
                while(1)
                {
                    char buf[256] = {0};
                    ifstr.getline(buf, sizeof(buf));
                    int buflen = strlen((const char*)buf);
                    if(buflen > 32)
                    {
                        vector<string> substrs;
                        ret = splitString1(string(buf), substrs, ',');
                        if(ret < 0)
                        {
                            continue;
                        }

                        if(isFirst)
                        {
                            kmlPointInfo.clear();
                            kmlPointInfo.push_back(outputFilePath);
                            ret = convertToKMLWithoutName(kmlPointInfo, pOfstr, 0); //
                            if(ret < 0)
                            {
                                continue;
                            }
                            isFirst = 0;
                        }

                        if(!isFirst)
                        {
                            kmlPointInfo.clear();
                            kmlPointInfo.push_back(substrs[1]); // utcTime
                            double lonDDPointDDDD;
                            double lonDDMMPointMMMM;
                            stringstream sLon1(substrs[4]);
                            sLon1 >> lonDDMMPointMMMM;
                            fromDMToDegree(lonDDMMPointMMMM, lonDDPointDDDD);
                            memset(bufPos, 0, sizeof(bufPos));
                            sprintf(bufPos, "%lf", lonDDPointDDDD);
                            kmlPointInfo.push_back(string(bufPos)); // Lon: 经度
                            double latDDPointDDDD;
                            double latDDMMPointMMMM;
                            stringstream sLat1(substrs[2]);
                            sLat1 >> latDDMMPointMMMM;
                            fromDMToDegree(latDDMMPointMMMM, latDDPointDDDD);
                            memset(bufPos, 0, sizeof(bufPos));
                            sprintf(bufPos, "%lf", latDDPointDDDD);
                            kmlPointInfo.push_back(string(bufPos)); // Lat

                            ret = convertToKMLWithoutName(kmlPointInfo, pOfstr, 1);
                            if(ret < 0)
                            {
                                continue;
                            }
                        }
                        emptyCnt = 0;
                        continue;
                    }
                    else if(buflen > 0)
                    {
                        emptyCnt = 0;
                        continue;
                    }
                    else
                    {
                        emptyCnt++;
                    }

                    if(emptyCnt > 20)
                    {
                        break;
                    }
                }

                ret = convertToKMLWithoutName(kmlPointInfo, pOfstr, 2); // kml 文件结束
                ifstr.close();
                pOfstr->close();
                delete pOfstr;
                pOfstr = NULL;
                kmlPaths.push_back(outputFilePath);
                QMessageBox msgBox(QMessageBox::Warning,"Information",tr("处理完成"),QMessageBox::Yes,NULL);
                msgBox.exec();
            }
            else
            {
                QString message = QString(tr("打开文件(")) + QString::fromStdString(*iter) + QString(tr(")失败"));
                QMessageBox msgBox(QMessageBox::Warning,"Information",message,QMessageBox::Yes,NULL);
                msgBox.exec();
            }
        }//for
        // 利用Google Earth 自动打开
        if(m_isAuto)
        {
            CTMStrConvert converter;
            wstring googleEarthPath = converter.StringToWString(m_googleEarthPath);
            vector<string>::iterator iterKmlPath;
            if(m_isSplit)
            {// 每个KML文件单独显示
                for(iterKmlPath = kmlPaths.begin(); iterKmlPath != kmlPaths.end(); ++iterKmlPath)
                {
                    string kmlPath = *iterKmlPath;
                    wstring wkmlPath = converter.StringToWString(kmlPath);
                    HINSTANCE ret = ShellExecute(NULL, L"open", googleEarthPath.c_str(), wkmlPath.c_str(), NULL, SW_SHOWNORMAL);
                    if((int)ret < 32)
                    {
                        int errorNo = (int)ret;
                        QString message = QString(tr("启动Google Earth 显示 KML 文件:("))
                                + QString::fromStdString(kmlPath)
                                + QString(tr(") 失败, Error Code: "))
                                + QString::number(errorNo);
                        QMessageBox msgBox(QMessageBox::Warning,"Information",message,QMessageBox::Yes,NULL);
                        msgBox.exec();
                    }
                }
            }
            else
            {// 全部KML文件同时显示
                wstring wkmlPath;
                for(iterKmlPath = kmlPaths.begin(); iterKmlPath != kmlPaths.end(); ++iterKmlPath)
                {
                    string kmlPath = *iterKmlPath;
                    wkmlPath += converter.StringToWString(kmlPath) + wstring(L" ");

                }
                HINSTANCE ret = ShellExecute(NULL, L"open", googleEarthPath.c_str(), wkmlPath.c_str(), NULL, SW_SHOWNORMAL);
                if((int)ret < 32)
                {
                    int errorNo = (int)ret;
                    QString message = QString(tr("启动Google Earth 显示 KML 文件失败, Error Code: ")) + QString::number(errorNo);
                    QMessageBox msgBox(QMessageBox::Warning,"Information",message,QMessageBox::Yes,NULL);
                    msgBox.exec();
                }
            }
        }// if m_isAuto
    }// if ret
}

int MainWindow::getFilePaths()
{
    if(m_firstPath.empty())
    {
        QMessageBox msgBox(QMessageBox::Warning,"Information",tr("未选择文件(夹),请选择"),QMessageBox::Yes,NULL);
        msgBox.exec();
        return -1;
    }
    // 清空路径
    if(!m_filePaths.empty())
    {
        m_filePaths.clear();
    }

    if(m_isBatch)
    {// 遍历文件夹
        QDir dir(QString::fromStdString(m_firstPath));
        int supportCnt = m_supportFormat.supportTXT + m_supportFormat.supportCSV + m_supportFormat.supportRDF;
        if(supportCnt == 0)
        {
            QMessageBox msgBox(QMessageBox::Warning,"Information",tr("文件夹中无.txt/.csv/.rdf文件格式文件,请确定"),QMessageBox::Yes,NULL);
            msgBox.exec();
            return -1;
        }

        foreach(QFileInfo mfi ,dir.entryInfoList())
        {
            if(mfi.isFile())
            {
                QString filePath = mfi.filePath();
                QString fileName = mfi.fileName();
                string sfileName = fileName.toLower().toStdString();
                if(m_supportFormat.supportTXT)
                {
                    if(sfileName.find(".txt") != string::npos)
                    {
                        QTextCodec *codec = QTextCodec::codecForName("UTF-8");
                        QString str = codec->toUnicode(filePath.toStdString().c_str());
                        wstring wFilePath = str.toStdWString();
                        CTMStrConvert converter;
                        m_filePaths.push_back(converter.WStringToString(wFilePath));
                        // m_filePaths.push_back(filePath.toStdString());
                    }
                }
                if(m_supportFormat.supportCSV)
                {
                    if(sfileName.find(".csv") != string::npos)
                    {
                        QTextCodec *codec =QTextCodec::codecForName("UTF-8");
                        QString str = codec->toUnicode(filePath.toStdString().c_str());
                        wstring wFilePath = str.toStdWString();
                        CTMStrConvert converter;
                        m_filePaths.push_back(converter.WStringToString(wFilePath));
                        // m_filePaths.push_back(filePath.toStdString());
                    }
                }
                if(m_supportFormat.supportRDF)
                {
                    if(sfileName.find(".rdf") != string::npos)
                    {
                        QTextCodec *codec =QTextCodec::codecForName("UTF-8");
                        QString str = codec->toUnicode(filePath.toStdString().c_str());
                        wstring wFilePath = str.toStdWString();
                        CTMStrConvert converter;
                        m_filePaths.push_back(converter.WStringToString(wFilePath));
                        // m_filePaths.push_back(filePath.toStdString());
                    }
                }
            }
            else
            {
                continue;
            }
        }
    }
    else
    {
        m_filePaths.push_back(m_firstPath);
    }

    if(m_filePaths.size() == 0)
    {
        QMessageBox msgBox(QMessageBox::Warning,"Information",tr("无有效文件,请确定"),QMessageBox::Yes,NULL);
        msgBox.exec();
        return -1;
    }
    return 0;
}
           

2.4 UI文件请到如下路径下载,同时也包含可执行文件(.exe):

https://download.csdn.net/download/hit1524468/10316766

3. 工具效果(选择用googleEarth打开转换的KML文件)

GPS的NMEA数据转Google Earth的KML数据工具
GPS的NMEA数据转Google Earth的KML数据工具