当前位置: 首页 > article >正文

C++ osgEarth 多窗口 同步绘制geometry

开发环境:win10 64bit、Qt5.15.2、C++ 、MSVC2019 、osg3.5.6、 osgEarth3.1 。

接触osgEarth不久,贴出来,希望大家指正。注意osgEarth版本。

采用观察者设计模式,设置 master 和 slave 窗口,通过管理类和Qt信号槽机制维护窗口观察者列表,实现信息高效传递。

达成多窗口在视点、视口、键鼠操作等方面的同步,确保操作连贯性和流畅性。

在 2D 和 3D 地图实现线、多边形、矩形同步绘制及 geometry 贴地显示,设置便捷绘制模式,提高绘图效率。

不同窗口可加载相同地理区域不同类型的文件。

观察者接口:

#ifndef OBSERVER_H
#define OBSERVER_H

#include <osg/Matrixd>

#include <osgEarth/EarthManipulator>

namespace MyProject {

// 观察者接口类,定义多个窗口需要同步的行为
class Observer {

public:
    virtual ~Observer() = default;

    // 同步更新视图矩阵
    virtual void updateViewMatrix(const osg::Matrixd& viewMatrix) = 0;

    // 同步更新投影矩阵
    virtual void updateProjectionMatrix(const osg::Matrixd& projectionMatrix) = 0;

    // 同步鼠标事件
    virtual void updateMouseEvent(int x, int y) = 0;

    // 同步键盘事件
    virtual void updateKeyEvent(int key) = 0;

    // 同步滚轮事件
    virtual void updateViewpoint(const osgEarth::Viewpoint &vp) = 0;

    // 同步2D 3D切换
    virtual void updatePitch(const double &pitch) = 0;

    // 同步 航向角
    virtual void updateHeading(const double &heading) = 0;

    // 新建 geometry
    virtual void updateCreateGeometry() = 0;

    // 同步 绘制图形
    virtual void updateGeometry(osg::ref_ptr<osgEarth::Geometry> geometry) = 0;
};

}

#endif // OBSERVER_H

窗口应用:用到了osgQt 这个库, 大佬应该知道,osgQOpenGLWidget是继承自QOpenGLWidget。

#include "MyWidget.h"
#include "PickHandler.h"
#include "qdebug.h"
#include "qicon.h"

#include <osgDB/ReadFile>
#include <osgEarth/MapNode>


#include <osgEarth/ImageOverlay>
#include <osgEarth/AnnotationLayer>

#include <osgEarth/Registry>

MyWidget::MyWidget( QWidget* parent ) : osgQOpenGLWidget( parent ) {

    setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);

    setParent(nullptr);
    setWindowIcon(QIcon(""));
    setFocusPolicy(Qt::StrongFocus);
    setFocus();
    setMouseTracking(true);

    osgEarth::initialize();

    Global::initStyle(_style);

    _map = new Map();

    _mapNode = new osgEarth::MapNode( _map );

    _map->addLayer( new GeodeticGraticule() );//经纬度网格

    _group = new osg::Group();
    _mapNode->addChild(_group);

    _controlCanvas = new Controls::ControlCanvas;

    _group->addChild( _controlCanvas );

    // coordinate label
    _labelReadout = new LabelControl();
    _labelReadout->setHorizAlign(Control::ALIGN_LEFT);
    _labelReadout->setVertAlign(Control::ALIGN_BOTTOM);

    _labelReadout->setForeColor( 255, 0, 0, 1 );

    _controlCanvas->addControl( _labelReadout.get() );

    // _mouseCoordsTool = new osgEarth::Contrib::MouseCoordsTool( _mapNode );

    // _mouseCoordsTool->addCallback(new osgEarth::Contrib::MouseCoordsLabelCallback(_labelReadout));

    poLayerImage = Global::loadImage("C:/Users/xuanm/Pictures/shp/mid.tif");
    _map->addLayer( poLayerImage );

    // poLayerElevation = Global::loadElevation("C:/Users/xuanm/Pictures/vector_demo/n44_w092_1arc_v3.tif");
    // _map->addLayer( poLayerElevation );

    poLayerVector = Global::loadShapefile("C:/Users/xuanm/Pictures/shp/mid.shp");
    _map->addLayer( poLayerVector );



    connect(this, &osgQOpenGLWidget::initialized, this, &MyWidget::updateWidget);
}

MyWidget::~MyWidget() {


}

void MyWidget::updateWidget() {

    osgEarth::initialize( );

    _viewer = getOsgViewer();


    _viewer->setThreadingModel(osgViewer::Viewer::SingleThreaded);//设置单线程模式

    _viewer->realize();

    _viewer->addEventHandler( new CustomEventHandler() );



    _viewer->setSceneData( _mapNode );

    updateViewpoint( poLayerVector );

    _viewer->requestRedraw();
}

void MyWidget::slotType(int type)
{
    this->type = type;

    if(eventHandler){
        eventHandler->updateType(type);
    }

    featureNode = nullptr;
}

void MyWidget::enterEvent(QEvent *event) {

    setFocus();

    emit sigEnter(true);

    // //widget 由没有获得焦点--->获得焦点
    if(!isEnter){
        _earthManipulator->setHomeViewpoint(currentViewpoint );
    }

    isEnter = true;

    //至关重要
    _viewer->setCameraManipulator(_earthManipulator);
}

void MyWidget::leaveEvent(QEvent *event) {

    clearFocus();

    _labelReadout->setText("");
    emit sigEnter(false);

    isEnter = false;
}

void MyWidget::updateViewpoint(Layer * poLayer) {

    // 设置相机的初始视图,以聚焦在加载的图像区域上        // 获取 Shapefile 的地理范围
    GeoExtent extent = poLayer->getExtent();

    if (extent.isValid())
    {
        // 获取地理范围的最小和最大经纬度
        double minLon = extent.xMin();
        double maxLon = extent.xMax();

        double minLat = extent.yMin();
        double maxLat = extent.yMax();

        // 计算中心点
        double centerLon = (minLon + maxLon) / 2.0;
        double centerLat = (minLat + maxLat) / 2.0;

        // 创建 GeoPoint 作为中心点
        GeoPoint center(extent.getSRS(), centerLon, centerLat, 0.0);

        // 创建 GeoPoint 表示四个角的经纬度
        GeoPoint bottomLeft(extent.getSRS(), minLon, minLat, 0.0);
        GeoPoint topRight(extent.getSRS(), maxLon, maxLat, 0.0);

        // 计算宽度和高度(物理距离,单位:米)
        double width  = bottomLeft.distanceTo(GeoPoint(extent.getSRS(), maxLon, minLat));
        double height = bottomLeft.distanceTo(GeoPoint(extent.getSRS(), minLon, maxLat));

        // 使用勾股定理计算影像对角线的长度
        double diagonal = std::sqrt(width * width + height * height);

        //qDebugV0() << "实际的对角线长度 (米) " << diagonal;

        // 根据经验公式(可以根据实际效果调整系数)计算Range
        double range = diagonal / ( 2.0 * std::tan(osg::DegreesToRadians(45.0)) );

        if(_viewpoint == nullptr) {

            //设置 Viewpoint
            _viewpoint = new Viewpoint();
            _viewpoint->setName( "Shapefile Viewpoint" );
            _viewpoint->setFocalPoint( center );
            _viewpoint->setHeading(Angle(0.0, Units::DEGREES));
            _viewpoint->setPitch(Angle(-90.0, Units::DEGREES));
            _viewpoint->setRange(Distance(range, Units::METERS));

            // 创建 EarthManipulator 并设置 Viewpoint
            _earthManipulator = new osgEarth::Util::EarthManipulator();
            _earthManipulator->setHomeViewpoint(*_viewpoint);
            _earthManipulator->home(0.0);

            // 设置 Viewer 的 Camera Manipulator
            _viewer->setCameraManipulator( _earthManipulator.get() );
        }

        if(eventHandler == nullptr){
            eventHandler = new CustomEventHandler();
            _viewer->addEventHandler( eventHandler );
        }

        _viewpoint->setFocalPoint( center );
        _viewpoint->setRange(Distance(range, Units::METERS));

        currentViewpoint = *_viewpoint;

        _earthManipulator->setViewpoint(*_viewpoint);
    }
}

void MyWidget::updateCreateGeometry()
{
    qDebugV0()<<"type: "<<type;

    feature = new osgEarth::Feature(new osgEarth::Geometry, _map->getSRS());

    feature->geoInterp() = GEOINTERP_RHUMB_LINE;

    featureNode = new FeatureNode(feature, _style);

    _mapNode->addChild(featureNode);
}

void MyWidget::updateGeometry(osg::ref_ptr<osgEarth::Geometry> geometry)
{
    feature->setGeometry(geometry);

    featureNode->dirty();

    _viewer->requestRedraw(); // 请求视图重绘   // 触发重新绘制
}

// 观察者接口实现 - 更新视图矩阵
void MyWidget::updateViewMatrix(const osg::Matrixd& viewMatrix) {

    _viewer->setCameraManipulator(nullptr);
    _viewer->getCamera()->setViewMatrix(viewMatrix);
    _viewer->requestRedraw();
}

// 观察者接口实现 - 更新投影矩阵
void MyWidget::updateProjectionMatrix(const osg::Matrixd& projectionMatrix) {

    _viewer->setCameraManipulator(nullptr);
    _viewer->getCamera()->setProjectionMatrix( projectionMatrix );
    _viewer->requestRedraw();
}

// 观察者接口实现 - 同步鼠标事件
void MyWidget::updateMouseEvent(int x, int y) {

        //qDebugV0() << "Mouse Event received at: (" << x << ", " << y << ")";
}

// 观察者接口实现 - 同步键盘事件
void MyWidget::updateKeyEvent(int key) {

        //qDebugV0() << "Key Event received: " << key;
}

//更新视点
void MyWidget::updateViewpoint(const osgEarth::Viewpoint& viewpoint) {

    currentViewpoint = viewpoint;

    // _earthManipulator->setViewpoint(viewpoint );

    // _viewer->setCameraManipulator(_earthManipulator);
}

void MyWidget::updatePitch(const double &pitch)
{
    _earthManipulator->setHomeViewpoint(currentViewpoint );
    _viewer->setCameraManipulator(_earthManipulator);

    _viewpoint->setPitch(Angle(pitch, Units::DEGREES));

    _earthManipulator->setViewpoint(*_viewpoint);

    _viewer->requestRedraw();
}

void MyWidget::updateHeading(const double &heading)
{
    _earthManipulator->setHomeViewpoint(currentViewpoint );
    _viewer->setCameraManipulator(_earthManipulator);

    _viewpoint->setHeading(Angle(heading, Units::DEGREES));

    _earthManipulator->setViewpoint(*_viewpoint);

    _viewer->requestRedraw();
}
void MyWidget::resizeEvent(QResizeEvent* event) {
    // Call base class implementation
    osgQOpenGLWidget::resizeEvent(event);

    //qDebug() << "MyWidget resized to:" << event->size();

    // 如果需要,处理OpenGL视口
    glViewport(0, 0, event->size().width(), event->size().height());
}

自定义osgGA::GUIEventHandler,实现绘制交互。这个逻辑写得乱,希望大佬们抽空给指正哈。

#include "CustomEventHandler.h"

#include "Global.h"

#include "MyWidget.h"

CustomEventHandler::CustomEventHandler() {

    Global::initStyle(_style);

    observers.clear();
}

void CustomEventHandler::addObserver(MyWidget* observer) {

    observers.insert(observer);
}

void CustomEventHandler::removeObserver(MyWidget* observer) {

    observers.erase(observer);
}

void CustomEventHandler::updateType(const int& type)
{
    this->type = type;

    _geometry = nullptr;
}

bool CustomEventHandler::handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa) {

    //将aa强制转换为viewer对象
    poViewer = dynamic_cast<osgViewer::Viewer*>(&aa);
    if (poViewer == nullptr){
        return false;
    }

    poEarthManipulator = dynamic_cast<osgEarth::Util::EarthManipulator *> (poViewer->getCameraManipulator());

    if(poEarthManipulator == nullptr){
        return false;
    }

    viewpoint = poEarthManipulator->getViewpoint();

    //获取 MaNode;
    poMapNode = dynamic_cast<osgEarth::MapNode*>(poViewer->getSceneData());

    if(poMapNode == nullptr){
        return false;
    }

    poMap = poMapNode->getMap();
    if(poMap == nullptr){
        return false;
    }

    mx = ea.getX();
    my = ea.getY();

    eEventType = ea.getEventType();

    eModKeyMask = ea.getModKeyMask();

    eKey = ea.getKey();

    eButton = ea.getButton();

    this->getModKeyMask();//shift   ctrl

    // 同步视图和投影矩阵
    osg::Matrixd viewMatrix = poViewer->getCamera()->getViewMatrix();
    osg::Matrixd projMatrix = poViewer->getCamera()->getProjectionMatrix();

    notifyViewMatrixChanged(viewMatrix);
    notifyProjectionMatrixChanged(projMatrix);    

    notifyViewpoint(viewpoint);

    if(type == 0 ){
        return this->handleNone();
    }else{
        return this->handleDraw();
    }
}

bool CustomEventHandler::handleNone()
{
    switch (eEventType) {

    case osgGA::GUIEventAdapter::KEYDOWN:  // 键盘按下事件
    {
        //2D 3D 切换;viewPoint  heading 航向角切换
        if(_ctrlDown) {

            switch (eKey) {
            case osgGA::GUIEventAdapter::KEY_2: {

                viewpoint.setPitch(Angle(-90.0, Units::DEGREES));

                break;
            }

            case osgGA::GUIEventAdapter::KEY_3: {

                viewpoint.setPitch(Angle(-15.0, Units::DEGREES));

                break;
            }

            case osgGA::GUIEventAdapter::KEY_Up: {
                viewpoint.setHeading(Angle(0.0, Units::DEGREES));
                break;
            }

            case osgGA::GUIEventAdapter::KEY_Left: {
                viewpoint.setHeading(Angle(90.0, Units::DEGREES));
                break;
            }

            case osgGA::GUIEventAdapter::KEY_Down: {
                viewpoint.setHeading(Angle(180.0, Units::DEGREES));
                break;
            }

            case osgGA::GUIEventAdapter::KEY_Right: {
                viewpoint.setHeading(Angle(270.0, Units::DEGREES));
                break;
            }

            default:
                break;
            }

            poEarthManipulator->setViewpoint(viewpoint);
        }

        break;
    }
    default:
        break;
    }

    return false;
}


// 按住 shift键, 绘制 点 线 面
bool CustomEventHandler::handleDraw()
{
    if(!_shiftDown){
        return false;
    }

    switch (eEventType) {
    case osgGA::GUIEventAdapter::PUSH:
    {
        if(eButton != osgGA::GUIEventAdapter::LEFT_MOUSE_BUTTON){
            return false;
        }

        if(_geometry == nullptr){

            switch(type){

            case GEOMETRY_LINE:
                // qDebugV0()<<"line";
                _geometry = new osgEarth::LineString;
                break;
            case GEOMETRY_RECT:
            case GEOMETRY_POLYGON:
                qDebugV0()<<"rect or polygon ";
                _geometry = new osgEarth::Polygon;
                break;
            case GEOMETRY_FENCE:
                break;
            }

            _feature = new osgEarth::Feature(_geometry, poMap->getSRS());

            _feature->geoInterp() = GEOINTERP_RHUMB_LINE;

            _featureNode = new FeatureNode(_feature, _style);

            poMapNode->addChild(_featureNode);

            notifyCreateGeometry();
        }

        osg::Vec3d worldPos;

        if (poMapNode->getTerrain()->getWorldCoordsUnderMouse(poViewer, mx, my, worldPos))
        {
            GeoPoint _GeoPoint;
            _GeoPoint.fromWorld(poMapNode->getMapSRS(), worldPos);

            osg::Vec3d mapPos;
            mapPos = osg::Vec3d(_GeoPoint.x(), _GeoPoint.y(), _GeoPoint.z());

            mapPosFirst = mapPos;

            _geometry->push_back(mapPos);

            _featureNode->dirty();

            notifyGeometry(_geometry);
        }
        break;
    }
    case osgGA::GUIEventAdapter::RELEASE:
    {
        if(eButton != osgGA::GUIEventAdapter::LEFT_MOUSE_BUTTON){
            return false;
        }

        // 绘制矩形的时候,鼠标左键一旦松开,则表示绘制结束
        if (type == GEOMETRY_RECT) {
            _geometry = nullptr;
        }
        break;
    }

    case osgGA::GUIEventAdapter::DRAG:
    {
        if (type == GEOMETRY_RECT) {

            osg::Vec3d worldPos;

            if (poMapNode->getTerrain()->getWorldCoordsUnderMouse(poViewer, mx, my, worldPos))
            {
                GeoPoint _GeoPoint;
                _GeoPoint.fromWorld(poMapNode->getMapSRS(), worldPos);

                osg::Vec3d mapPos;
                mapPos = osg::Vec3d(_GeoPoint.x(), _GeoPoint.y(), _GeoPoint.z());

                _geometry->clear();

                _geometry->push_back(mapPosFirst);
                _geometry->push_back(osg::Vec3d(mapPos.x(), mapPosFirst.y(), _GeoPoint.z()));
                _geometry->push_back(mapPos);
                _geometry->push_back(osg::Vec3d(mapPosFirst.x(), mapPos.y(), _GeoPoint.z()));

                _featureNode->dirty();

                notifyGeometry(_geometry);
            }
        }
        break;
    }
    default:
        break;
    }

    return false;
}

// shift ctrl 键 状态 检测
void CustomEventHandler::getModKeyMask()
{
    //键盘 有键按下
    if (eEventType == osgGA::GUIEventAdapter::KEYDOWN) {
        if (eKey == osgGA::GUIEventAdapter::KEY_Shift_L || eKey == osgGA::GUIEventAdapter::KEY_Shift_R) {
            _shiftDown = true;
        }

        if (eKey == osgGA::GUIEventAdapter::KEY_Control_L || eKey == osgGA::GUIEventAdapter::KEY_Control_R) {
            _ctrlDown = true;
        }
    }

    //键盘 有键抬起
    if(eEventType == osgGA::GUIEventAdapter::KEYUP){
        if (eKey == osgGA::GUIEventAdapter::KEY_Shift_L || eKey == osgGA::GUIEventAdapter::KEY_Shift_R) {
            _shiftDown = false;

            _geometry = nullptr;
        }

        if (eKey == osgGA::GUIEventAdapter::KEY_Control_L || eKey == osgGA::GUIEventAdapter::KEY_Control_R) {
            _ctrlDown = false;
        }
    }
}

void CustomEventHandler::notifyViewMatrixChanged(const osg::Matrixd& viewMatrix) {

    for (auto observer : observers) {
        if(observer) {
            observer->updateViewMatrix(viewMatrix);
        }
    }
}

void CustomEventHandler::notifyProjectionMatrixChanged(const osg::Matrixd& projectionMatrix) {

    for (auto observer : observers) {
        if(observer) {
            observer->updateProjectionMatrix(projectionMatrix);
        }
    }
}

void CustomEventHandler::notifyMouseEvent(int x, int y) {

    for (auto observer : observers) {
        observer->updateMouseEvent(x, y);
    }
}

void CustomEventHandler::notifyKeyEvent(int key) {

    for (auto observer : observers) {
        observer->updateKeyEvent(key);
    }
}

void CustomEventHandler::notifyViewpoint(const osgEarth::Viewpoint &vp)
{
    for (auto observer : observers) {
        observer->updateViewpoint(vp);
    }
}

void CustomEventHandler::notifyPitch(const double &pitch)
{
    for (auto observer : observers) {
        observer->updatePitch(pitch);
    }
}

void CustomEventHandler::notifyHeading(const double &heading)
{
    for (auto observer : observers) {
        observer->updateHeading(heading);
    }
}

void CustomEventHandler::notifyCreateGeometry()
{
    for (auto observer : observers) {
        observer->updateCreateGeometry();
    }
}

void CustomEventHandler::notifyGeometry(osg::ref_ptr<osgEarth::Geometry> geometry)
{
    for (auto observer : observers) {
        observer->updateGeometry(geometry);
    }
}

在一个管理类里面维护观察者列表:


// 实时 维护 观察者 列表
void MainWindow::slotEnterStatus(bool isEnter) {

    // sender() 返回一个 QObject 指针,指向发出信号的对象
    QObject* obj = sender();

    MyWidget* poWidget = qobject_cast<MyWidget*>(obj);

    if(!poWidget) {
        return;
    }

    // qDebugV0()<<poWidget->windowTitle()<<"   Focus-->"<<(isFocusIn?"IN":"OUT");

    for(auto observer : listWidget) {

        if(observer == poWidget) {
            continue;
        }

        if(isEnter) {
            poWidget->eventHandler->addObserver(observer);
        }
        else {
            poWidget->eventHandler->removeObserver(observer);
        }
    }
}

/**********************************************************************************/

编辑*.shp文件,实现增删改。看了好多帖子,还是没搞定。有大佬知道的话留言。谢谢。


http://www.kler.cn/news/343531.html

相关文章:

  • 股指期货的杠杆是怎么体现和使用的?
  • 最新PHP礼品卡回收商城 点卡回收系统源码_附教程
  • 旅游管理系统开发新篇章:SpringBoot技术解析
  • 基于SSM的线上学习网站【附源码】
  • ORM框架简介
  • JVM和GC案例详解
  • 进程同步问题
  • TCP连接的三次握手与四次挥手详解
  • 项目管理系统介绍,核心概念与操作技巧
  • Python3一些常用的SSH连接_paramiko使用
  • 调度算法-先来先服务
  • EtherNet IP网关HT3S-EIS-MDN读取七星华创CS310空气流量计数据应用案例
  • JavaScript 代码行前添加分号
  • 【部署分布式数据库DBMS】
  • 机房空调远程控制-Thingsboard MQTT 接口说明
  • 吐槽kotlin之垃圾设计
  • 用Electron时候碰到的小问题
  • <OS 有关> Docker.Desktop - Unexpected WSL error #14030 不能启动, 问题已经解决 fixed
  • PDFToMarkdown
  • 如何使用ssm实现新冠病毒校园监控平台的设计与实现+vue