qt 3d航迹图

一般就是三种方法

1.opengl,vtk这种从零自己画,网上也可能有半成品,大多是付费的。

2.重写qwt3d,07年就停止更新了,画出来类似opengl,需要自己修改参数,参数修改不对很难搞,对于经纬高某一个数数值跨度小不太友好。我这边需求数据就不太符合,所以很难搞,直接换成q3d了

3.直接用q3d,耗费资源比较大,方便点,但是只有曲面图,散点图和柱形图。除了不能拟合个线,其他都挺好用。

照着这个大佬的配置一下Am_Jun

重写qwt3d

track3d.h
cpp 复制代码
#ifndef TRACK3D_H
#define TRACK3D_H

#include <QTimer>
#include <qwt3d_surfaceplot.h>
#include <qwt3d_function.h>
#include <QMouseEvent>
using namespace Qwt3D;

class Rosenbrock:public Function{ // 定义Rosenbrock类,继承自Function类
public:
    Rosenbrock(SurfacePlot & pw):Function(pw){} // 构造函数,初始化SurfacePlot对象和Function对象
    Rosenbrock(){} // 无参构造函数
    double operator()(double x, double y)//需要自行更改这个函数,以x,y轴的数值确定z值,坑的很
    {
  //    return 2*x*y;//z轴初始范围与该函数相关
        return 9 * (y - 32.6);
    }
};

class track3D:public Qwt3D::SurfacePlot
{
public:
    // 构造函数 初始化三维图表
    track3D();
    // 初始化Rosenbrock函数表面(用于定义坐标系底图)
    //void rosenbrockinit();
    // 添加航迹线
    bool addLine(QString linename,int linewidth);
    // 添加标记点(实现为仅保留单点的特殊线段)
    bool addPoint(QString pointName, int color,int lineWidth);
    // 更新指定标记点的坐标(保持固定长度)
    bool updatePointData(QString pointName,Qwt3D::Triple point);
    // 删除指定航迹线
    bool deleteLine(QString linename);
    // 清除所有航迹线
    bool deleteAllLine();
    // 向指定航迹线添加数据点
    bool addData(QString linename,Qwt3D::Triple point);

    // 设置当前坐标范围极值
    virtual void setCurMaxMin();
    // 重新绘制图表
    void replot();
    // 将QColor转换为Qwt3D的RGBA格式
    Qwt3D::RGBA colorToRGBA(const QColor& qtColor);
protected:
    virtual void keyPressEvent(QKeyEvent *);

private:
    bool first = 0;
    // 三维图表实例(当前类本身)
    Qwt3D::SurfacePlot plot;
    //地图  管理航迹  航迹线管理器(键:线名称,值:线对象指针)
    QMap<QString,Qwt3D::Line3D*> mLine;
    //航迹  管理大小  航迹线长度记录(用于动态截断)
    QMap<QString,int> mLineSize;

    QVector<Line3D> testData;
    //颜色索引(循环使用预设颜色)
    int colorindex;
    // 预设颜色列表
    QVector<QColor> baseColors = {QColor(Qt::red), QColor(Qt::blue), QColor(Qt::black),QColor(Qt::green), /* 其他颜色 */};
    // 当前坐标轴范围临时变量
    double myCurxMin = 10000;
    double myCurxMax = -10000;
    double myCuryMin = 10000;
    double myCuryMax = -10000;

    // 初始坐标轴范围(经度/纬度)
    double myxMin = 10000;
    double myxMax = -10000;
    double myyMin = 10000;
    double myyMax = -10000;

    // 单条航迹最大数据点数
    int m_limit = 20000;
    // 默认线宽
    int defaultWidth = 5;
    //设置显示盒子
    Rosenbrock *m_rosenbrock;
};

#endif // TRACK3D_H
track3d.cpp
cpp 复制代码
#include "track3d.h"
#include <QDebug>


track3D::track3D()
{

    // 设置图表的标题
    setTitle("GPS");
    // 设置绘图样式为线条三维样式
    setPlotStyle(Qwt3D::LINE3D_STYLE);
    //自动缩放坐标轴
    //coordinates()->setAutoScale(0);
    //设置隔离线数目
    setIsolines(5);
    // 创建一个Rosenbrock函数对象,并将其与当前SurfacePlot对象关联
    m_rosenbrock = new Rosenbrock(*this);
    // 设置网格的分辨率为10x10
    m_rosenbrock->setMesh(41,31);
    // 设置函数的定义域为x,y都在0到100之间
    m_rosenbrock->setDomain(myxMin,myxMax,myyMin,myyMax);
    m_rosenbrock->setMinZ(0);
    m_rosenbrock->setMaxZ(50);
    m_rosenbrock->create();


    // 设置坐标轴的网格线显示,仅在左侧、背面和底面显示
//    coordinates()->setGridLines(true,false,Qwt3D::LEFT|Qwt3D::BACK|Qwt3D::FLOOR);
    // 为每个坐标轴设置主刻度和次刻度数量
    for (unsigned i=0; i != coordinates()->axes.size(); ++i)
    {
        coordinates()->axes[i].setMajors(5); // 主刻度数量
        coordinates()->axes[i].setMinors(1); // 次刻度数量
    }


    // 设置各坐标轴的标签
    coordinates()->axes[X1].setLabelString("经度/°");
    coordinates()->axes[Y1].setLabelString("纬度/°");
    coordinates()->axes[Z1].setLabelString("高度/m");

    // 设置坐标轴样式为盒子样式
    setCoordinateStyle(BOX);

    // 设置图表的平移、旋转、缩放、缩小比例以及坐标轴的主刻度和副刻度数目
    setShift(1,0,0);
    setRotation(30,0,15);
    setShift(0, 0, 0);      // 将图表移动到中心
    setZoom(0.9);

    // 更新数据,并更新图表
    replot();
}

//void track3D::rosenbrockinit()
//{
//    //更新坐标轴样式
//    Rosenbrock rosenbrock(*this);
//    rosenbrock.setDomain(myxMin,myxMax,myyMin,myyMax);
//    rosenbrock.setMinZ(myzMin);
//    rosenbrock.setMaxZ(myzMax);
//    rosenbrock.create();
//}

void track3D::keyPressEvent(QKeyEvent * e)  // 定义键盘事件处理函数
{
    int c = e->key();  // 获取用户按下的键

    if(e->key() == Qt::Key_Up)  // 判断用户按下的键是否为向上键
    {
        setShift(1,0,0);  // 设置图形在三维空间中的位置,参数为x,y,z轴方向上的偏移量
        setRotation(30,0,15);  // 设置图形在三维空间中的旋转角度,参数为x,y,z轴方向上的旋转角度
        setScale(1,1,1);  // 设置图形缩放比例
        setShift(0.15,0,0);  // 重新设置图形的偏移量,使其在x轴正方向上有一定的移动
        setZoom(0.9);  // 缩小视图镜头
    }
    else if(c == 65)  // 判断用户按下的键是否为字符'A'
    {
        setScale(xScale(),yScale(),zScale() * 1.2);  // 增加z方向上的缩放比例
    }
    else if(c == 68)  // 判断用户按下的键是否为字符'D'
    {
        setScale(xScale() * 1.2,yScale(),zScale());  // 增加x方向上的缩放比例
    }
    else if(c == 83)  // 判断用户按下的键是否为字符'S'
    {
        setScale(xScale(),yScale() * 1.2,zScale());  // 增加y方向上的缩放比例
    }
}
//添加轨迹线
bool track3D::addLine(QString linename,int linewidth)
{
    //如果没有,则进行添加
    if(!mLine.contains(linename))
    {
        Qwt3D::Line3D _l3d;
        Qwt3D::Line3D *myLine1 = dynamic_cast<Qwt3D::Line3D *>(this->addEnrichment(_l3d));
        myLine1->configure(linewidth,true);
        colorindex = colorindex % 2;
        myLine1->setLineColor(colorToRGBA(baseColors[colorindex]));  //设置默认颜色为红色
        colorindex++;
        mLine.insert(linename,myLine1);
        return true;
    }
    return false;
}

bool track3D::addPoint(QString pointName,int color, int lineWidth)
{
    //实现方法:按照正常线段添加曲线  更新时只保留一个点位
    //如果没有,则进行添加
    if(!mLine.contains(pointName))
    {
        Qwt3D::Line3D _l3d;
        Qwt3D::Line3D *myLine1 = dynamic_cast<Qwt3D::Line3D *>(this->addEnrichment(_l3d));
        myLine1->configure(lineWidth,true);
        myLine1->setLineColor(colorToRGBA(baseColors[color]));
        mLine.insert(pointName,myLine1);
        return true;
    }
    return false;
}
//更新标识线-动态绘制时每次调用该函数使得线段长度固定
bool track3D::updatePointData(QString pointName, Triple point)
{
    //更新点的坐标
    if(mLine.contains(pointName)){
        mLineSize[pointName] += 1;
        auto it = mLine.find(pointName);
        if(mLineSize[pointName] > m_limit){
            //删除原来的数据
            it.value()->clear();
            mLineSize[pointName] = 0;
        }
        //更新点
        it.value()->add(point);
    }

}
//删除单挑轨迹线
bool track3D::deleteLine(QString linename)
{
    if(mLine.contains(linename))   //如果包含
    {
        auto it = mLine.find(linename);
        it.value()->clear();
        this->degrade(it.value());
        mLine.remove(linename);
        return true;
    }
    return false;
}
//删除所有轨迹线
bool track3D::deleteAllLine()
{
    for(auto line:mLine)
    {
        line->clear();   //移除所有曲线数据
        this->degrade(line);
    }
    mLine.clear();  //移除所有曲线
    return true;
}

bool track3D::addData(QString linename, Triple point)
{
    if(mLine.contains(linename)) {

        // 独立判断各坐标轴极值
        if (point.x < myCurxMin) { myCurxMin = point.x;}
        if (point.x > myCurxMax) { myCurxMax = point.x;}
        if (point.y < myCuryMin) { myCuryMin = point.y;}
        if (point.y > myCuryMax) { myCuryMax = point.y;}

        setCurMaxMin(); // 只有当极值变化时才更新坐标范围
        replot();       // 刷新图表

        auto it = mLine.find(linename);
        it.value()->add(point);
        return true;
    } else {
        if(addLine(linename,defaultWidth)) return true;
        return false;
    }
}


void track3D::setCurMaxMin()
{
    myxMax = qMax(myxMax,myCurxMax);
    myxMin = qMin(myxMin,myCurxMin);

    myyMax = qMax(myyMax,myCuryMax);
    myyMin = qMin(myyMin,myCuryMin);

    //myzMax = qMax(myzMax,myCurzMax);
    //myzMin = qMin(myzMin,myCurzMin);

//    myxMax = myxMax + 1;
//    myxMin = myxMin - 1;

//    myyMax = myyMax + 1;
//    myyMin = myyMin - 1;

//    myzMax = myzMax + 1;
//    myzMin = myzMin - 1;

    qDebug() << "myxMax" << myxMax << "myxMin" << myxMin
             << "myyMax" << myyMax << "myyMin" << myyMin;
             //<< "myzMax" << myzMax << "myzMin" << myzMin;

//    double _yScale = myxMax / myzMax;
//    _yScale = (myxMax / myzMax) > _yScale? (myyMax / myzMax): _yScale;
//    setScale(1,1,_yScale);


    m_rosenbrock->setDomain(myxMin - 0.001,myxMax + 0.001,myyMin - 0.001,myyMax + 0.001);
    //m_rosenbrock->setMinZ(0);
    //m_rosenbrock->setMaxZ(50);
    m_rosenbrock->create();

    // 为每个坐标轴设置主刻度和次刻度数量
    for (unsigned i = 0; i != coordinates()->axes.size(); ++i)
    {
        coordinates()->axes[i].setMajors(5); // 主刻度数量
        coordinates()->axes[i].setMinors(1); // 次刻度数量
    }

    // 设置各坐标轴的标签
    coordinates()->axes[X1].setLabelString("经度/°");
    coordinates()->axes[Y1].setLabelString("纬度/°");
    coordinates()->axes[Z1].setLabelString("高度/m");

    // 设置坐标轴样式为盒子样式
    //setCoordinateStyle(BOX);

}

// 数据更新与重绘
void track3D::replot()
{
    std::vector<Line3D> lineContain;
    for(auto line:mLine)
    {
        lineContain.push_back(*line);
        //方法1  单独为线段更新
        //updateData(*line);
        //updateGL();
    }
    //存在两条以上曲线时,必须使用此方法更新数据(参数为vector<Line3D>)
    updateData(lineContain);
    updateGL();
}


//qColor  2 RGBA
RGBA track3D::colorToRGBA(const QColor &qtColor)
{
    Qwt3D::RGBA qwt3DColor;
    qwt3DColor.r = qtColor.redF();  // 获取红色分量并转换为0.0到1.0的范围
    qwt3DColor.g = qtColor.greenF(); // 获取绿色分量并转换为0.0到1.0的范围
    qwt3DColor.b = qtColor.blueF();  // 获取蓝色分量并转换为0.0到1.0的范围
    qwt3DColor.a = qtColor.alphaF(); // 获取透明度分量并转换为0.0到1.0的范围
    return qwt3DColor;
}
使用示例
cpp 复制代码
track3D *m_track = nullptr;
//初始化航迹图并插入
m_track = new track3D();
ui->tabWidget->insertTab(1, m_track, "3D Track"); // 我插入到tab里的,可以直接创建个界面中
//插入线条
if (m_track->addLine("理论轨迹", 2)) {
   for (int i = 1;i < m_data.length() - 1;i ++) {
       Triple point(m_data.at(i).Longitude, m_data.at(i).Latitude, m_data.at(i).Altitude);
        m_track->addData("理论轨迹", point);
        qDebug() << m_data.at(i).Longitude << "  " <<  m_data.at(i).Latitude << "   " << m_data.at(i).Altitude;
       }
    m_track->replot();
}

照着这个大佬配置长沙红胖子Qt

直接使用q3d

q3dsurfacewidget.h
cpp 复制代码
#ifndef Q3DSURFACEWIDGET_H
#define Q3DSURFACEWIDGET_H

#include <QMainWindow>

#include <Q3dscatter>
#include <q3dsurface.h>
#include <q3dtheme.h>
#include <qsurface3dseries.h>
#include <qvector3d.h>
#include <QtDataVisualization>
#include <qabstract3dinputhandler.h>
#include <qmap.h>


using namespace QtDataVisualization;


QT_BEGIN_NAMESPACE
namespace Ui { class Q3dSurfaceWidget; }
QT_END_NAMESPACE


class Q3dSurfaceWidget : public QMainWindow
{
    Q_OBJECT

public:
    Q3dSurfaceWidget(QWidget *parent = nullptr);
    ~Q3dSurfaceWidget();
    void drawPoint(QString linename,QVector<QVector3D> points);


private:
    Ui::Q3dSurfaceWidget *ui;
    Q3DScatter *m_3Dgraph;
    void initControl();
    //理论数据
    QtDataVisualization::QScatter3DSeries  *m_Theory;//散点类型
    //实际数据
    QtDataVisualization::QScatter3DSeries  *m_Actual;//散点类型

    //数据代理
    QScatterDataProxy *proxy_Theory = nullptr;
    QScatterDataProxy *proxy_Actual = nullptr;

};

#endif // Q3DSURFACEWIDGET_H
q3dsurfacewidget.cpp
cpp 复制代码
#include "q3dsurfacewidget.h"
#include "ui_q3dsurfacewidget.h"
#include "QSplitter"

#include <QDebug>
#include <QDateTime>

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

    this->setWindowTitle("UDP_Reciver");

    initControl();

    QSplitter *splitter = new QSplitter(Qt::Horizontal);
    splitter->addWidget(ui->widget);
    this->setCentralWidget(splitter);


}

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

void Q3dSurfaceWidget::initControl()
{
    m_3Dgraph = new Q3DScatter();
    ui->widget = QWidget::createWindowContainer(m_3Dgraph);
    proxy_Theory = new QScatterDataProxy(); //数据代理
    proxy_Actual = new QScatterDataProxy(); //数据代理
    m_Theory = new QScatter3DSeries(proxy_Theory);//创建序列
    m_Theory->setMeshSmooth(true);
    m_3Dgraph->addSeries(m_Theory);


    m_Actual = new QScatter3DSeries(proxy_Actual);//创建序列
    m_Actual->setMeshSmooth(true);
    m_3Dgraph->addSeries(m_Actual);
    //创建坐标轴
    m_3Dgraph->axisX()->setTitle("经度");
    m_3Dgraph->axisX()->setTitleVisible(true);
    m_3Dgraph->axisX()->setRange(0,100);
    m_3Dgraph->axisY()->setTitle("海拔");
    m_3Dgraph->axisY()->setTitleVisible(true);
    m_3Dgraph->axisY()->setRange(0,100);
    m_3Dgraph->axisZ()->setTitle("纬度");
    m_3Dgraph->axisZ()->setTitleVisible(true);
    m_3Dgraph->axisZ()->setRange(0,100);
    m_3Dgraph->activeTheme()->setLabelBackgroundEnabled(false);
    m_3Dgraph->activeTheme()->setBackgroundColor(QColor(90,90,90));//设置背景色

    m_Theory->setMesh(QAbstract3DSeries::MeshMinimal);//数据点为圆球
    m_Theory->setSingleHighlightColor(QColor(0,0,255));//设置点选中时的高亮颜色
    m_Theory->setBaseColor(QColor(0,255,255));//设置点的颜色
    m_Theory->setItemSize(0.05);//设置点的大小



    m_Actual->setMesh(QAbstract3DSeries::MeshMinimal);//数据点为圆球
    m_Actual->setSingleHighlightColor(QColor(0,0,0));//设置点选中时的高亮颜色
    m_Actual->setBaseColor(QColor(255,0,255));//设置点的颜色
    m_Actual->setItemSize(0.05);//设置点的大小

}


void Q3dSurfaceWidget::drawPoint(QString linename,QVector<QVector3D> points)
{
    if (points.isEmpty()) return; // 如果点集合为空,直接返回
    // 计算所有点的最小值和最大值
    float minX = points[0].x(), minY = points[0].y(), minZ = points[0].z();
    float maxX = points[0].x(), maxY = points[0].y(), maxZ = points[0].z();

    for (const auto &point : points) {
            minX = qMin(minX, point.x());
            minY = qMin(minY, point.y());
            minZ = qMin(minZ, point.z());

            maxX = qMax(maxX, point.x());
            maxY = qMax(maxY, point.y());
            maxZ = qMax(maxZ, point.z());
        }

    // 动态调整坐标轴范围
    m_3Dgraph->axisX()->setRange(minX - 1, maxX + 1);
    m_3Dgraph->axisY()->setRange(minY - 1, maxY + 1);
    m_3Dgraph->axisZ()->setRange(minZ - 1, maxZ + 1);


    if(linename == "理论")
    {
        QScatterDataArray *dataArray = new QScatterDataArray();
        dataArray->resize(points.count());
        QScatterDataItem *ptrToDataArray = &dataArray->first();
        for (int i = 0;i < points.count();i++ )
        {
            ptrToDataArray->setPosition(points[i]);
            ptrToDataArray++;
        }
        m_Theory->dataProxy()->resetArray(dataArray);//更新散点


    }
    if(linename == "实际")
    {
        QScatterDataArray *dataArray = new QScatterDataArray();
        dataArray->resize(points.count());
        QScatterDataItem *ptrToDataArray = &dataArray->first();
        for (int i = 0;i < points.count();i++ )
        {
            ptrToDataArray->setPosition(points[i]);
            ptrToDataArray++;
        }
        m_Actual->dataProxy()->resetArray(dataArray);//更新散点
    }
}
使用示例
cpp 复制代码
Q3dSurfaceWidget *m_track = nullptr;
 //初始化航迹图并插入
m_track = new Q3dSurfaceWidget();
ui->tabWidget->insertTab(1, m_track, "3D Track"); // 在第二个位置(索引为1)插入标签页

//我这边用定时器每十毫秒加个点,也能用for
//初始化时钟
if(timer_show == nullptr)
{
   timer_show = new QTimer();
   //开始跑
   connect(timer_show, &QTimer::timeout, [=]() {
		if(i < m_data.length() -1)
		{
			if(isTheory == 1)
		    {   
        		m_points_2.append(QVector3D(m_data.at(i).TargetLongitude,m_data.at(i).TargetAltitude,m_data.at(i).TargetLatitude));
 		       m_track->drawPoint("理论",m_points_2);
 		    }

		    if(isActual == 1)
		     {
         		m_points.append(QVector3D(m_data.at(i).Longitude,m_data.at(i).Altitude,m_data.at(i).Latitude));
	            m_track->drawPoint("实际",m_points);
		     }
 		     i++;
		}
});
	 timer_show->start(10);
}

计算经纬高两个点之间的距离

cpp 复制代码
//计算经纬高距离
//Haversine公式 然后当成三角形计算斜边
QVector<datacsv> m_data;
struct datacsv
{
    //时间戳
    QString TimeStamp;
    //经度
    double Longitude;
    //纬度
    double Latitude;
    //海拔
    double Altitude;
    //目标经度
    double TargetLongitude;
    //目标纬度
    double TargetLatitude;
    //目标海拔
    double TargetAltitude;
};
double a =  pow(sin((m_data.at(i).Latitude - m_data.at(i).TargetLatitude)  / 100000 / 2 * 3.1415926535 / 180) ,2) + cos(m_data.at(i).Latitude  / 100000 * 3.1415926535 / 180)  * cos(m_data.at(i).TargetLatitude  / 100000 * 3.1415926535 / 180) * pow(sin((m_data.at(i).Longitude - m_data.at(i).TargetLongitude) / 100000 /  2 * 3.1415926535 / 180) ,2);
double d = 2 * m_R * atan(sqrt(a / (1 - a)));
double d_2 = abs(m_data.at(i).Altitude - m_data.at(i).TargetAltitude);
double result = sqrt(d * d + d_2 * d_2);
//restlt就是最后结果
相关推荐
香蕉可乐荷包蛋24 分钟前
Python面试问题
开发语言·python·面试
ErizJ34 分钟前
Golang|分布式索引架构
开发语言·分布式·后端·架构·golang
.生产的驴35 分钟前
SpringBoot 接口国际化i18n 多语言返回 中英文切换 全球化 语言切换
java·开发语言·spring boot·后端·前端框架
八股文领域大手子43 分钟前
深入浅出限流算法(三):追求极致精确的滑动日志
开发语言·数据结构·算法·leetcode·mybatis·哈希算法
GISBox1 小时前
零基础教学:用GISBox将RVT转为3DTiles
3d
几度泥的菜花1 小时前
优雅实现网页弹窗提示功能:JavaScript与CSS完美结合
开发语言·javascript·css
weixin_307779131 小时前
AWS Glue ETL设计与调度最佳实践
开发语言·数据仓库·云计算·etl·aws
兜小糖的小秃毛2 小时前
文号验证-同时对两个输入框验证
开发语言·前端·javascript
anqi272 小时前
如何在 IntelliJ IDEA 中编写 Speak 程序
java·大数据·开发语言·spark·intellij-idea
XuX032 小时前
MATLAB小试牛刀系列(1)
开发语言·matlab