开发环境:
win10 64bit、Qt5.15.2、C++ 、MSVC2019 、osg3.5.6、 osgEarth3.1
接触osgEarth不久,贴出来,希望大家指正。
注意osgEarth版本。
采用观察者设计模式,设置 master 和 slave 窗口,通过管理类和信号槽维护窗口观察者列表,实现信息高效传递。
达成多窗口在视点、视口、键鼠操作等方面的同步,确保操作连贯性和流畅性。
在 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>
//! Callback that you install on the RTTPicker.
struct MyCallback : public RTTPicker::Callback
{
    MyCallback(){ }
    void onHit(ObjectID id)
    {
        qDebugV0()<<id;
    }
    void onMiss()
    {
        qDebugV0()<<"Miss";
    }
    // pick whenever the mouse moves.
    bool accept(const osgGA::GUIEventAdapter& ea, const osgGA::GUIActionAdapter& aa)
    {
       // qDebugV0()<<"accept";
        return ea.getEventType() == ea.PUSH;
    }
};
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 PickHandler(new osgText::Text) );
    _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文件,实现增删改。看了好多帖子,还是没搞定。有大佬知道的话留言。谢谢。



















