想要直接看代碼的童鞋可以跳過前面的瞎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,右邊是斜着的那種。兩種都可以處理了。表現也是非常好的。