如何用QChart顯示DEM文件

想要直接看代碼的童鞋可以跳過前面的瞎BB部分。

關於代碼和DEM數據都看我置頂的博客吧!有免費下載。

//緣由BB部分:

emmmmmm爲什麼會萌生出這樣的想法呢?因爲有同學在一家公司搞點雲算法,分了一個生成等高線的任務。然後拿到了一個DEM文件,要用這個文件生成等高線。但是這個文件有問題,因爲傳統的DEM是格網化的數據,是標準的矩形,但是,他拿到的DEM不是標準的矩形,是斜着的,這就造成了出現很多空白地方,不足的地方補充了-9999。兩種數據的對比如下圖:

用這樣的文件去生成等高線,使用傳統算法就行不通了。

        我對生成等高線的算法不太瞭解,但是讀取數據還是可以做到的。然後正巧,前幾天看到QT5.9的新特性QChart,驚呆了。在QT5.5的時候還必須進行編譯才能使用的QTChart直接被加進來了,然後官方也給了很多例子用來學習。然後又正巧,我看到了官網利用高度圖生成DEM的例子:


我很好奇,翻開一看,這個地形原來是用高度圖生成的。高度圖長這個樣子,地形高的地方就亮,地形低的地方就黯淡:

        所以我萌生了一個大膽的想法,既然DEM是格網化的,也就是圖像,那麼利用DEM生成高度圖,然後顯示出來不就行了嗎?然後我立刻實踐了一下。

//BB部分結束。

直接上代碼啦!由於DEM是有固定格式的,那麼我們直接讀進來然後顯示:

void Scarlet_MainWindow::do_ShowDEM()
{
    QString path = QFileDialog::getOpenFileName(this, tr("Open Dem"), ".", tr("Dem Files(*.DEM)"));
    if(path.length() == 0)
    {
        qDebug()<<"Do not open any file!";
        //QMessageBox::information(NULL, tr("Path"), tr("You didn't select any files."));
    }
    else
    {
        Scarlet_DEM* myDem = new Scarlet_DEM(path);
        QImage heightMapImage =myDem->get_Image();
        int Col = myDem->get_Col();
        int Row = myDem->get_Row();
        //do_ShowIMG(heightMapImage);
        Q3DSurface *graph = new Q3DSurface();
        QWidget *container = QWidget::createWindowContainer(graph);
        if (!graph->hasContext())
        {
            QMessageBox msgBox;
            msgBox.setText("Couldn't initialize the OpenGL context.");
            msgBox.exec();
            return ;
        }
        QSize screenSize = graph->screen()->size();
        container->setMinimumSize(QSize(screenSize.width() / 2, screenSize.height() / 1.6));
        container->setMaximumSize(screenSize);
        container->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
        container->setFocusPolicy(Qt::StrongFocus);
        graph->setAxisX(new QValue3DAxis);
        graph->setAxisY(new QValue3DAxis);
        graph->setAxisZ(new QValue3DAxis);
        QHeightMapSurfaceDataProxy*m_heightMapProxy = new QHeightMapSurfaceDataProxy(heightMapImage);
        QSurface3DSeries*m_heightMapSeries = new QSurface3DSeries(m_heightMapProxy);
        m_heightMapSeries->setItemLabelFormat(QStringLiteral("(@xLabel, @zLabel): @yLabel"));
        m_heightMapSeries->setDrawMode(QSurface3DSeries::DrawSurface);
        m_heightMapSeries->setFlatShadingEnabled(true);//是否允許平滑繪製
       // m_heightMapSeries->setFlatShadingEnabled(false);
        graph->axisX()->setLabelFormat("%.1f X(N)");
        graph->axisZ()->setLabelFormat("%.1f W(E)");
//        m_heightMapProxy->setValueRanges(34.0f, 40.0f, 18.0f, 24.0f);//由於z軸在最低下,隨意必須是x-z才行,y代表高度
//        graph->axisX()->setRange(34.0f, 40.0f);
//        graph->axisZ()->setRange(18.0f, 24.0f);
        m_heightMapProxy->setValueRanges(0, Col, 0, Row);//由於z軸在最低下,隨意必須是x-z才行,y代表高度
        graph->axisX()->setRange(0, Col);
        graph->axisZ()->setRange(0, Row);
        //(可以讓Z自動設定Range,我們也可以根據最大最小數值人爲設定)
        //即便沒有這句話,也是自動設定高度值
        graph->axisY()->setAutoAdjustRange(true);
        graph->axisX()->setTitle(QStringLiteral("Latitude"));
        graph->axisY()->setTitle(QStringLiteral("Height"));
        graph->axisZ()->setTitle(QStringLiteral("Longitude"));
        graph->addSeries(m_heightMapSeries);
        container->show();
    }
}


裏面有一個類用來讀取:Scarlet_DEM* myDem = new Scarlet_DEM(path);

這個東西的h文件是這樣的:

class Scarlet_DEM:public QObject
{
    Q_OBJECT
public:
    Scarlet_DEM(QString DEMFile);
    QString get_DataMark(){return m_DataMark;}
    double get_Version(){return m_Version;}
    QString get_Unit(){return m_Unit;}
    double get_Alpha(){return m_Alpha;}
    double get_Compress(){return m_Compress;}
    double get_X0(){return m_X0;}
    double get_Y0(){return m_Y0;}
    double get_DX(){return m_DX;}
    double get_DY(){return m_DY;}
    int get_Row(){return m_Row;}
    int get_Col(){return m_Col;}
    QString get_ValueType(){return m_ValueType;}
    double get_HZoom(){return m_Hzoom;}
    QVector<double> get_Data(){return m_Data;}
    QImage get_Image(){return HeightImage;}
    double get_MinData(){return m_MinData;}
    double get_MaxData(){return m_MaxData;}
private:
    void InitFromFile();
    void PrintHead();
    void do_ReadHead(QTextStream &in);
    void do_ReadData(QTextStream &in);
    void do_ReadData2(QTextStream &in);
    void do_GenerateImage();
    QString m_DEMFile;
    QString m_DataMark;
    double m_Version;
    QString m_Unit;
    double m_Alpha;
    double m_Compress;
    double m_X0;
    double m_Y0;
    double m_DX;
    double m_DY;
    int m_Row;
    int m_Col;
    QString m_ValueType;
    double m_Hzoom;
    double m_MinData;
    double m_MaxData;
    QVector<double> m_Data;
    QImage HeightImage;
};

這個類主要就是用於讀取DEM並生成高度圖,然後把生成的高度圖QImage存在裏面。CPP文件是這樣的:

#include <QtDataVisualization/QValue3DAxis>
#include <QtDataVisualization/Q3DTheme>
#include <QtGui/QImage>
#include <QtCore/qmath.h>
#include <QLabel>
#include <QFile>
#include <QFileInfo>
#include <QList>
#include <QMessageBox>
#include <iostream>
using namespace std;
//關於DEMEngin,讀取解析DEM文件------------------------
//有三個DEM文件用於顯示,以後會碰到更加複雜的嗎?
void ShowImgeLabel(QImage Img)
{
    QLabel* myLabel = new QLabel();
    myLabel->setPixmap(QPixmap::fromImage(Img));
    myLabel->show();
}
Scarlet_DEM::Scarlet_DEM(QString DEMFile)
{
    m_DEMFile =DEMFile;
    InitFromFile();
}
void OutStrList(QList<QString> list)
{
    for(int i=0;i<list.size();i++)
    {
        QString Str =(list[i]);
        qDebug()<<"Str:"<<Str;
    }
}
void Scarlet_DEM::PrintHead()
{
    qDebug()<<"m_DEMFile"<<m_DEMFile;
    qDebug()<<"m_DataMark"<<m_DataMark;
    qDebug()<<"m_Version"<<m_Version;
    qDebug()<<"m_Unit"<<m_Unit;
    qDebug()<<"m_Alpha"<<m_Alpha;
    qDebug()<<"m_Compress"<<m_Compress;
    qDebug()<<"m_X0"<<m_X0;
    qDebug()<<"m_Y0"<<m_Y0;
    qDebug()<<"m_DX"<<m_DX;
    qDebug()<<"m_DY"<<m_DY;
    qDebug()<<"m_Row"<<m_Row;
    qDebug()<<"m_Col"<<m_Col;
    qDebug()<<"m_ValueType"<<m_ValueType;
    qDebug()<<"m_Hzoom"<<m_Hzoom;
}
void Scarlet_DEM::InitFromFile()
{
    qDebug()<<"Dem path:"<<m_DEMFile;
    QFileInfo Info(m_DEMFile);
    if(!Info.isFile())
        return;
    QFile file(m_DEMFile);
    if(!file.open(QIODevice::ReadOnly | QIODevice::Text))
        return;
    QTextStream in(&file);
    //Read head,一共有13行,每一行用冒號分割
    do_ReadHead(in);
    PrintHead();
    do_ReadData(in);
    //do_ReadData2(in);
    file.close();
    do_GenerateImage();
}
void Scarlet_DEM::do_ReadHead(QTextStream &in)
{
    QString LineStr;
    QList<QString> myQtringList;
    LineStr=in.readLine();
    myQtringList = LineStr.split(':',QString::SkipEmptyParts);//後面參數決定是否跳過空字符串,默認不跳過空字符串
    m_DataMark= myQtringList[1];
    LineStr=in.readLine();//OutStrList(myQtringList);
    myQtringList = LineStr.split(':',QString::SkipEmptyParts);
    m_Version =myQtringList[1].toDouble();
    LineStr=in.readLine();//OutStrList(myQtringList);
    myQtringList = LineStr.split(':',QString::SkipEmptyParts);
    m_Unit = myQtringList[1];
    LineStr=in.readLine();//OutStrList(myQtringList);
    myQtringList = LineStr.split(':',QString::SkipEmptyParts);
    m_Alpha =myQtringList[1].toDouble();
    LineStr=in.readLine();//OutStrList(myQtringList);
    myQtringList = LineStr.split(':',QString::SkipEmptyParts);
    m_Compress =myQtringList[1].toDouble();
    LineStr=in.readLine();//OutStrList(myQtringList);
    myQtringList = LineStr.split(':',QString::SkipEmptyParts);
    m_X0 =myQtringList[1].toDouble();
    LineStr=in.readLine();//OutStrList(myQtringList);
    myQtringList = LineStr.split(':',QString::SkipEmptyParts);
    m_Y0 =myQtringList[1].toDouble();
    LineStr=in.readLine();//OutStrList(myQtringList);
    myQtringList = LineStr.split(':',QString::SkipEmptyParts);
    m_DX =myQtringList[1].toDouble();
    LineStr=in.readLine();//OutStrList(myQtringList);
    myQtringList = LineStr.split(':',QString::SkipEmptyParts);
    m_DY =myQtringList[1].toDouble();
    //行號
    LineStr=in.readLine();//OutStrList(myQtringList);
    myQtringList = LineStr.split(':',QString::SkipEmptyParts);
    m_Row =myQtringList[1].toInt();
    //列號
    LineStr=in.readLine();//OutStrList(myQtringList);
    myQtringList = LineStr.split(':',QString::SkipEmptyParts);
    m_Col =myQtringList[1].toInt();
    //數據類型
    LineStr=in.readLine();//OutStrList(myQtringList);
    myQtringList = LineStr.split(':',QString::SkipEmptyParts);
    m_ValueType =myQtringList[1];
    //縮放比例
    LineStr=in.readLine();//OutStrList(myQtringList);
    myQtringList = LineStr.split(':',QString::SkipEmptyParts);
    m_Hzoom =myQtringList[1].toDouble();
}
void Scarlet_DEM::do_ReadData(QTextStream &in)
{
    //行列號不能小於0
    if(m_Row<=10||m_Col<=10)
    {
        //我們一般處理10行以上的DEM,DEM行列號過小的情況也不予處理
        QMessageBox::information(NULL, "Title", "Invalid DEM! Cannot read", QMessageBox::Yes);
        return;
    }
    int fullLineCount = m_Col/10;
    int cutColNum =m_Col-fullLineCount*10;
    int PartLineCount = fullLineCount;
    if(cutColNum!=0)//說明還有一行
        PartLineCount++;
    int LittHightCount=0;
    bool minmaxDone = false;
    for(int r=0;r<m_Row;r++)
    {
        for(int i=0;i<PartLineCount;i++)
        {
            QString LineStr = in.readLine();
            QStringList sections = LineStr.trimmed().split(QRegExp("[ ]"),QString::SkipEmptyParts);
            for(int j=0;j<sections.size();j++)
            {
                QString Str =sections[j];
                if(minmaxDone == false)
                {
                    if(Str.toInt()!=-9999)
                    {
                        m_MinData = Str.toDouble();
                        m_MaxData = Str.toDouble();
                        minmaxDone=true;
                    }
                }
                else
                {
                    if(Str.toInt()!=-9999)
                    {
                        if(Str.toDouble()>=m_MaxData)
                            m_MaxData =Str.toDouble();
                        if(Str.toDouble()<=m_MinData)
                            m_MinData = Str.toDouble();
                    }
                }
                //cout<<Str.toDouble()<<" ";
                m_Data.push_back(Str.toDouble());//一行一行地存儲進去
            }
            //cout<<endl;
        }
    }
    cout<<"m_Data:"<<m_Data.size()<<endl;
    cout<<"DataShouldBe:"<<m_Col*m_Row<<endl;
    cout<<"MaxData:"<<m_MaxData<<endl;
    cout<<"MinData:"<<m_MinData<<endl;
}
void Scarlet_DEM::do_ReadData2(QTextStream &in)
{
    //行列號不能小於0
    if(m_Row<=10||m_Col<=10)
    {
        //我們一般處理10行以上的DEM,DEM行列號過小的情況也不予處理
        QMessageBox::information(NULL, "Title", "Invalid DEM! Cannot read", QMessageBox::Yes);
        return;
    }
    int fullLineCount = m_Col/10;
    int cutColNum =m_Col-fullLineCount*10;
    int PartLineCount = fullLineCount;
    if(cutColNum!=0)//說明還有一行
        PartLineCount++;
    int LittHightCount=0;
    do
    {
        QString LineStr = in.readLine();
        if(LineStr.isNull())
            break;
        QStringList sections = LineStr.trimmed().split(QRegExp("[ ]"),QString::SkipEmptyParts);
        for(int j=0;j<sections.size();j++)
        {
            QString Str =sections[j];
            if(LittHightCount==0&&j==0)
            {
                m_MinData = Str.toDouble();
                m_MaxData = Str.toDouble();
            }
            else if(Str.toDouble()>=m_MaxData)
                m_MaxData =Str.toDouble();
            else if(Str.toDouble()<=m_MinData)
                m_MinData = Str.toDouble();
            if(Str.toDouble()<=2000)
                LittHightCount++;
            //cout<<Str.toDouble()<<" ";
            m_Data.push_back(Str.toDouble());//一行一行地存儲進去
        }
    }while(true);
    cout<<"m_Data:"<<m_Data.size()<<endl;
    cout<<"DataShouldBe:"<<m_Col*m_Row<<endl;
    cout<<"MaxData:"<<m_MaxData<<endl;
    cout<<"MinData:"<<m_MinData<<endl;
    cout<<"LittHightCount"<<LittHightCount<<endl;
}
void Scarlet_DEM::do_GenerateImage()
{
    double AbsolutHeight =m_MaxData-m_MinData;
    HeightImage = QImage(m_Col,m_Row,QImage::Format_RGB888);
    HeightImage.fill(QColor(0,0,0));
    //HeightImage.setPixelColor(j,i,color);//必須賦初值,圖像才能是黑色的
    for(int i=0;i<m_Row;i++)
    {
        for(int j=0;j<m_Col;j++)
        {
            double CurrentHeight = m_Data[i*m_Col+j];
            double HeightIn256 =(CurrentHeight-m_MinData)/AbsolutHeight*256;
            int Pix = (int)(HeightIn256+0.5);
            if(Pix<0)Pix =0;
            if(Pix>255)Pix = 255;
            if((int)(CurrentHeight) ==-9999)
            Pix =0;
            HeightImage.setPixelColor(j,i,QColor(Pix,Pix,Pix));
        }
    }
    //ShowImgeLabel(HeightImage);
}

還有別忘了包含QTChart相關的各種頭文件。而且必須要加命名空間。放一下效果,左邊是正規的DEM,右邊是斜着的那種。兩種都可以處理了。表現也是非常好的。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章