想要直接看代码的童鞋可以跳过前面的瞎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,右边是斜着的那种。两种都可以处理了。表现也是非常好的。