osgEarth中绘制单箭头

osgEarth3.2+qt5.14+osg3.6.5

#ifndef STRAIGHTARROWDRAWER_H
#define STRAIGHTARROWDRAWER_H

#include <osgEarth/MapNode>
#include <osgEarth/Geometry>
#include <osgEarth/GeoMath>
#include <osgEarth/Style>
#include<osgEarth/Feature>
#include<osgEarth/FeatureNode>

#include<osg/Vec2>
#include <osgViewer/Viewer>
#include <osgGA/GUIEventHandler>

class StraightArrowDrawer:public osgGA::GUIEventHandler
{
public:
    StraightArrowDrawer(osg::ref_ptr<osgEarth::MapNode> mapNode);
    ~StraightArrowDrawer();

    void beginDraw(const osg::Vec3d& lla);
    void moveDraw(const osg::Vec3d& lla);
    void endDraw(const osg::Vec3d& lla);
    void resetDraw();

    //删除选中的多边形
    void removeSelectStraightArrow(osg::ref_ptr<osg::Node> node);

    //删除最后创建的多边形
    void removeStraightArrow();

    //删除所以多边形
    void removeAllStraightArrows();

    void setIsDrawStraightArrow(bool isDrawStraightArrow);


   // 只有两个控制点时
    std::vector<osg::Vec2> calculateTwoPoints(const std::vector<osg::Vec2>& ctrlPts);
    //有三个或三个以上的控制点时
    std::vector<osg::Vec2> calculateMorePoints(const std::vector<osg::Vec2>& ctrlPts);

    //事件处理
    bool handle(const osgGA::GUIEventAdapter &ea,osgGA::GUIActionAdapter &aa);
private:
    osg::ref_ptr<osgEarth::MapNode> _mapNode;
    osg::NodePath   nodePaths;
    osgEarth::Style _straightArrowStyle;
    bool _isDrawStraightArrow=false;
    osg::ref_ptr<osgEarth::FeatureNode> _straightArrowFeatureNode;
    float _ratio;
    std::vector<osg::Vec2> _controlPoints; // 存储点击的点
    std::vector<osg::Vec2> _drawParts;     //存储绘制点

    std::vector<osg::ref_ptr<osgEarth::FeatureNode>> _straightArrowNodes; // 保存所有绘制直箭头
};

#endif // STRAIGHTARROWDRAWER_H


#include "StraightArrowDrawer.h"
#include"DrawerMath.h"
#include<QDebug>
StraightArrowDrawer::StraightArrowDrawer(osg::ref_ptr<osgEarth::MapNode> mapNode)
    :_mapNode(mapNode)
    ,_straightArrowFeatureNode(nullptr)
    ,_ratio(6.0)
    ,_isDrawStraightArrow(false)
{
    nodePaths.push_back(_mapNode);
    _straightArrowStyle.getOrCreate<osgEarth::PolygonSymbol>()->fill()->color()= osgEarth::Color(osgEarth::Color::Yellow, 0.45);  // RGBA 方式

    osgEarth::LineSymbol* ls = _straightArrowStyle.getOrCreate<osgEarth::LineSymbol>();
    ls->stroke()->color() = osgEarth::Color::Yellow;
    ls->stroke()->width() = 2.0f;
    ls->stroke()->widthUnits() = osgEarth::Units::PIXELS;

    _straightArrowStyle.getOrCreate<osgEarth::AltitudeSymbol>()->clamping()= osgEarth::AltitudeSymbol::CLAMP_ABSOLUTE;
    _straightArrowStyle.getOrCreate<osgEarth::AltitudeSymbol>()->technique()= osgEarth::AltitudeSymbol::TECHNIQUE_GPU;
    _straightArrowStyle.getOrCreate<osgEarth::AltitudeSymbol>()->verticalOffset()= 0.1;
}
StraightArrowDrawer::~StraightArrowDrawer()
{

}
void StraightArrowDrawer::beginDraw(const osg::Vec3d &lla)
{
    _controlPoints.push_back(osg::Vec2(lla.x(), lla.y()));
    if (_controlPoints.empty() || _controlPoints.size() < 2)
        return;
    if (_controlPoints.size() == 2 && _controlPoints[0]==_controlPoints[1])
        return;
     _drawParts.clear();
     if (_controlPoints.size() == 2)
         _drawParts = calculateTwoPoints(_controlPoints);
     else
         _drawParts = calculateMorePoints(_controlPoints);

     if (_straightArrowFeatureNode==nullptr) {
        osgEarth::Feature* feature = new osgEarth::Feature(new osgEarth::Polygon, osgEarth::SpatialReference::get("wgs84"),_straightArrowStyle);
        _straightArrowFeatureNode = new osgEarth::FeatureNode(feature,_straightArrowStyle);
        _mapNode->addChild(_straightArrowFeatureNode);

     }
     _straightArrowFeatureNode->setName("StraightArrow");
     _straightArrowNodes.push_back(_straightArrowFeatureNode);
     osgEarth::Geometry* geom = _straightArrowFeatureNode->getFeature()->getGeometry();
     geom->clear();
     for (auto& n : _drawParts) {
         geom->push_back(osg::Vec3(n.x(), n.y(), 0));
     }
     _straightArrowFeatureNode->dirty();
}

void StraightArrowDrawer::moveDraw(const osg::Vec3d &lla)
{
    if (_controlPoints.empty())
           return;
       if (!_straightArrowFeatureNode.valid()) {
            osgEarth::Feature* feature = new osgEarth::Feature(new osgEarth::Polygon, osgEarth::SpatialReference::get("wgs84"), _straightArrowStyle);
           _straightArrowFeatureNode = new osgEarth::FeatureNode(feature,_straightArrowStyle);
           _mapNode->addChild(_straightArrowFeatureNode);
       }

       if (_straightArrowFeatureNode.valid()) {
           std::vector<osg::Vec2> ctrlPts = _controlPoints;
           std::vector<osg::Vec2> drawPts;
           ctrlPts.push_back(osg::Vec2(lla.x(), lla.y()));

           if (ctrlPts.size() == 2)
               drawPts = calculateTwoPoints(ctrlPts);
           else
               drawPts = calculateMorePoints(ctrlPts);

           osgEarth::Geometry* geom = _straightArrowFeatureNode->getFeature()->getGeometry();
           geom->clear();
           for (auto& n : drawPts) {
               geom->push_back(osg::Vec3(n.x(), n.y(), 0));
           }
           _straightArrowFeatureNode->dirty();
       }
}

void StraightArrowDrawer::endDraw(const osg::Vec3d &lla)
{

}

void StraightArrowDrawer::resetDraw()
{
    _controlPoints.clear();

    _straightArrowFeatureNode = NULL;
}

void StraightArrowDrawer::removeSelectStraightArrow(osg::ref_ptr<osg::Node> node)
{
    _mapNode->removeChild(node);
}

void StraightArrowDrawer::removeStraightArrow()
{
    if (!_straightArrowNodes.empty()) {
        osg::ref_ptr<osgEarth::FeatureNode> lastNode = _straightArrowNodes.back();
        _mapNode->removeChild(lastNode); // 从地图中移除
        _straightArrowNodes.pop_back();     // 从列表中移除
    }
}

void StraightArrowDrawer::removeAllStraightArrows()
{
    for (auto& node : _straightArrowNodes) {
       _mapNode->removeChild(node); // 从地图中移除
    }
    _straightArrowNodes.clear(); // 清空列表
}

void StraightArrowDrawer::setIsDrawStraightArrow(bool isDrawStraightArrow)
{
    _isDrawStraightArrow=isDrawStraightArrow;
}

std::vector<osg::Vec2> StraightArrowDrawer::calculateTwoPoints(const std::vector<osg::Vec2> &ctrlPts)
{
    //取出首尾两个点
        osg::Vec2 pointS = ctrlPts[0];
        osg::Vec2 pointE = ctrlPts[1];
        //计算箭头总长度
        float l = sqrtf((pointE.y()-pointS.y())*(pointE.y()-pointS.y())+(pointE.x()-pointS.x())*(pointE.x()-pointS.x()));
        //计算直箭头的宽
        float w = l/_ratio;

        //计算三角形的底边中心点坐标
        float x_ = pointS.x() + (pointE.x() - pointS.x())*(_ratio-1)/_ratio;
        float y_ = pointS.y() + (pointE.y() - pointS.y())*(_ratio-1)/_ratio;
        osg::Vec2 point_o(x_, y_);

        //计算
        std::vector<osg::Vec2> v_lr_ = DrawMath::calculateVector(osg::Vec2(pointE.x()-pointS.x(), pointE.y()-pointS.y()), osg::PI/2, w/2);
        //获取左边尾部向量
        osg::Vec2 v_l_ = v_lr_[0];
        //获取右边尾部向量
        osg::Vec2 v_r_ = v_lr_[1];
        //获取左边尾部点
        osg::Vec2 point_l(v_l_.x()+pointS.x(), v_l_.y()+pointS.y());
        //获取右边尾部点
        osg::Vec2 point_r(v_r_.x()+pointS.x(), v_r_.y()+pointS.y());

        osg::Vec2 point_h_l(v_l_.x()/_ratio+x_, v_l_.y()/_ratio+y_);
        osg::Vec2 point_h_r(v_r_.x()/_ratio+x_, v_r_.y()/_ratio+y_);

        //计算三角形左边点
        osg::Vec2 point_a_l((point_h_l.x()*2-point_h_r.x()), point_h_l.y()*2-point_h_r.y());
        //计算三角形右边点
        osg::Vec2 point_a_r(point_h_r.x()*2-point_h_l.x(), point_h_r.y()*2-point_h_l.y());
        return std::vector<osg::Vec2>{point_l,point_h_l,point_a_l,pointE,point_a_r,point_h_r,point_r};
}

std::vector<osg::Vec2> StraightArrowDrawer::calculateMorePoints(const std::vector<osg::Vec2> &ctrlPts)
{
    //计算箭头总长度
       float l = 0;
       //计算直箭头的宽
       float w = 0;
       for (int i = 0; i < ctrlPts.size()-1; i++) {
           //取出首尾两个点
           osg::Vec2 pointS = ctrlPts[i];
           osg::Vec2 pointE = ctrlPts[i+1];
           l += sqrtf((pointE.y()-pointS.y())*(pointE.y()-pointS.y())+(pointE.x()-pointS.x())*(pointE.x()-pointS.x()));
       }
       w = l/_ratio;
       float a = atanf(w/(2.0*l));

       //定义左右控制点集合
       std::vector<osg::Vec2> points_C_l;
       std::vector<osg::Vec2> points_C_r;
       //定义尾部左右的起始点
       osg::Vec2 point_t_l;
       osg::Vec2 point_t_r;

       //计算中间的所有交点
       for (int j=0; j < ctrlPts.size()-2; j++) {
           osg::Vec2 pointU_1 = ctrlPts[j]; //第一个用户传入的点
           osg::Vec2 pointU_2 = ctrlPts[j+1]; //第二个用户传入的点
           osg::Vec2 pointU_3 = ctrlPts[j+2]; //第三个用户传入的点

           //计算向量
           osg::Vec2 v_U_1_2(pointU_2.x()-pointU_1.x(), pointU_2.y()-pointU_1.y());
           osg::Vec2 v_U_2_3(pointU_3.x()-pointU_2.x(), pointU_3.y()-pointU_2.y());

           //定义左边第一个控制点
           osg::Vec2 point_l_1;
           //定义右边第一个控制点
           osg::Vec2 point_r_1;
           //如果j=0时,左右第一个控制点需要计算
           if (j == 0) {
               std::vector<osg::Vec2> v_lr_= DrawMath::calculateVector(v_U_1_2, osg::PI_2, w/2);
               //获取左边尾部点
               osg::Vec2 v_l_ = v_lr_[0];
               //获取右边尾部点
               osg::Vec2 v_r_ = v_lr_[1];
               //获取左边尾部点
               point_t_l = point_l_1 = osg::Vec2(v_l_.x()+pointU_1.x(), v_l_.y()+pointU_1.y());
               //获取右边尾部点
               point_t_r = point_r_1 = osg::Vec2(v_r_.x()+pointU_1.x(), v_r_.y()+pointU_1.y());
           } else { //否则获取上一次的记录
               point_l_1 = points_C_l[points_C_l.size()-1];
               point_r_1 = points_C_r[points_C_r.size()-1];
           }

           std::vector<osg::Vec2> v_lr = DrawMath::calculateVector(v_U_1_2, a, 1);
           //这里的向量需要反过来
           //获取左边向量
           osg::Vec2 v_l = v_lr[1];
           //获取右边向量
           osg::Vec2 v_r = v_lr[0];
           //定义角平分线向量
           osg::Vec2 v_angularBisector = DrawMath::calculateAngularBisector(osg::Vec2(-v_U_1_2.x(), -v_U_1_2.y()), v_U_2_3);
           //求交点
           //计算左边第二个控制点
           osg::Vec2 point_l_2 = DrawMath::calculateIntersection(v_l, v_angularBisector, point_l_1, pointU_2);
           osg::Vec2 point_r_2 = DrawMath::calculateIntersection(v_r, v_angularBisector, point_r_1, pointU_2);

           //添加后面的拐角控制点
           points_C_l.push_back(osg::Vec2((point_l_1.x()+point_l_2.x())/2, (point_l_1.y()+point_l_2.y())/2));
           points_C_l.push_back(point_l_2);
           points_C_r.push_back(osg::Vec2((point_r_1.x()+point_r_2.x())/2, (point_r_1.y()+point_r_2.y())/2));
           points_C_r.push_back(point_r_2);
       }
       //进入计算头部
       //计算一下头部的长度
       osg::Vec2 pointU_E2 = ctrlPts[ctrlPts.size()-2];//倒数第二个用户点
       osg::Vec2 pointU_E1 = ctrlPts[ctrlPts.size()-1];//最后一个用户点
       float head_d = sqrtf((pointU_E2.x()-pointU_E1.x())*(pointU_E2.x()-pointU_E1.x()) + (pointU_E2.y()-pointU_E1.y())*(pointU_E2.y()-pointU_E1.y()));
       //定义头部的左右两结束点
       osg::Vec2 point_h_l, point_h_r;
       //三角形左右两点数组
       std::vector<osg::Vec2> point_lr_t;
       //定义曲线最后一个控制点,也就是头部结束点和最后一个拐角点的中点
       osg::Vec2 point_C_l_e, point_C_r_e;
       //定义三角形的左右两个点
       osg::Vec2 point_triangle_l, point_triangle_r;

       //获取当前的最后的控制点,也就是之前计算的拐角点
       osg::Vec2 point_C_l_eq = points_C_l[points_C_l.size()-1];
       osg::Vec2 point_C_r_eq = points_C_r[points_C_r.size()-1];
       //申明三角形的两边向量
       osg::Vec2 v_l_t, v_r_t;
       //三角的高度都不够
       if (head_d <= w) {
           point_lr_t = DrawMath::calculateVector(osg::Vec2(pointU_E1.x()-pointU_E2.x(), pointU_E1.y()-pointU_E2.y()), osg::PI_2, w/2);
           //获取三角形左右两个向量
           v_l_t = point_lr_t[0];
           v_r_t = point_lr_t[1];
           point_h_l = osg::Vec2(v_l_t.x()/_ratio+pointU_E2.x(), v_l_t.y()/_ratio+pointU_E2.y());
           point_h_r = osg::Vec2(v_r_t.x()/_ratio+pointU_E2.x(), v_r_t.y()/_ratio+pointU_E2.y());
           //计算三角形的左右两点
           point_triangle_l = osg::Vec2(point_h_l.x()*2-point_h_r.x(), point_h_l.y()*2-point_h_r.y());
           point_triangle_r = osg::Vec2(point_h_r.x()*2-point_h_l.x(), point_h_r.y()*2-point_h_l.y());

           //计算最后的控制点
           point_C_l_e = osg::Vec2((point_C_l_eq.x()+point_h_l.x())/2, (point_C_l_eq.y()+point_h_l.y())/2);
           point_C_r_e = osg::Vec2((point_C_r_eq.x()+point_h_r.x())/2, (point_C_r_eq.y()+point_h_r.y())/2);

           //添加最后的控制点(中心点)
           points_C_l.push_back(point_C_l_e);
           points_C_r.push_back(point_C_r_e);
       } else { //足够三角的高度
           //由于够了三角的高度,所以首先去掉三角的高度
           //计算向量
           osg::Vec2 v_E2_E1(pointU_E1.x()-pointU_E2.x(), pointU_E1.y()-pointU_E2.y());
           //取模
           float v_E2_E1_d = sqrtf(v_E2_E1.x()*v_E2_E1.x()+v_E2_E1.y()*v_E2_E1.y());
           //首先需要计算三角形的底部中心点
           osg::Vec2 point_c(pointU_E1.x()-v_E2_E1.x()*w/v_E2_E1_d, pointU_E1.y()-v_E2_E1.y()*w/v_E2_E1_d);

           //计算出在三角形上底边上头部结束点
           point_lr_t = DrawMath::calculateVector(osg::Vec2(pointU_E1.x()-point_c.x(), pointU_E1.y()-point_c.y()), osg::PI_2, w/2);
           //获取三角形左右两个向量
           v_l_t = point_lr_t[0];
           v_r_t = point_lr_t[1];

           point_h_l = osg::Vec2(v_l_t.x()/_ratio+point_c.x(), v_l_t.y()/_ratio+point_c.y());
           point_h_r = osg::Vec2(v_r_t.x()/_ratio+point_c.x(), v_r_t.y()/_ratio+point_c.y());

           //计算三角形的左右两点
           point_triangle_l = osg::Vec2(point_h_l.x()*2-point_h_r.x(), point_h_l.y()*2-point_h_r.y());
           point_triangle_r = osg::Vec2(point_h_r.x()*2-point_h_l.x(), point_h_r.y()*2-point_h_l.y());

           //计算最后的控制点
           point_C_l_e = osg::Vec2((point_C_l_eq.x()+point_h_l.x())/2,(point_C_l_eq.y()+point_h_l.y())/2);
           point_C_r_e = osg::Vec2((point_C_r_eq.x()+point_h_r.x())/2,(point_C_r_eq.y()+point_h_r.y())/2);
           //添加最后的控制点(中心点)
           points_C_l.push_back(point_C_l_e);
           points_C_r.push_back(point_C_r_e);
       }
       //使用控制点计算差值
       //计算贝塞尔的控制点
       std::vector<osg::Vec2> points_BC_l = DrawMath::createBezier2(points_C_l);
       std::vector<osg::Vec2> points_BC_r = DrawMath::createBezier2(points_C_r);
       //组合左右点集和三角形三个点
       std::vector<osg::Vec2> pointsR{point_t_l};
       //首先连接左边的差值曲线
       pointsR.insert(pointsR.end(), points_BC_l.begin(), points_BC_l.end());
       //添加左边头部结束点
       pointsR.push_back(point_h_l);
       //添加三角形左边点
       pointsR.push_back(point_triangle_l);
       //添加三角形顶点
       pointsR.push_back(pointU_E1);
       //添加三角形右边点
       pointsR.push_back(point_triangle_r);
       //添加右边头部结束点
       pointsR.push_back(point_h_r);
       //合并右边的所有点
       for (int k = points_BC_r.size()-1; k >= 0; k--) {
           pointsR.push_back(points_BC_r[k]);
       }
       //添加右边尾部起始点
       pointsR.push_back(point_t_r);
       return pointsR;
}

bool StraightArrowDrawer::handle(const osgGA::GUIEventAdapter &ea, osgGA::GUIActionAdapter &aa)
{
    if (!_mapNode)
      return false;

    osgViewer::Viewer* viewer = dynamic_cast<osgViewer::Viewer*>(&aa);
    if (!viewer)
      return false;

    if (!_isDrawStraightArrow)
        return false;
    // 使用 switch 结构更清晰地区分事件类型
    switch (ea.getEventType())
    {
        case osgGA::GUIEventAdapter::PUSH:
        {
                switch (ea.getButton())
                {
                case osgGA::GUIEventAdapter::LEFT_MOUSE_BUTTON:
                {
                    osgUtil::LineSegmentIntersector::Intersections intersections;
                    if (viewer->computeIntersections(ea.getX(), ea.getY(), nodePaths, intersections))
                    {
                        const osgUtil::LineSegmentIntersector::Intersection& intersection = *intersections.begin();
                        osg::Vec3d worldPoint = intersection.getWorldIntersectPoint();

                        // 坐标转换:世界坐标 -> 地理坐标(WGS84)
                        const osgEarth::SpatialReference* mapSRS = _mapNode->getMapSRS();
                        const osgEarth::SpatialReference* geoSRS = osgEarth::SpatialReference::get("wgs84");
                        if (!geoSRS)
                        {
                            std::cerr << "[Error] geoSRS is null!" << std::endl;
                            return false;
                        }

                        osgEarth::GeoPoint geoPoint;
                        geoPoint.fromWorld(mapSRS, worldPoint);
                        osgEarth::GeoPoint wgs84Point;
                        geoPoint.transform(geoSRS, wgs84Point);

                        double lon = wgs84Point.x();
                        double lat = wgs84Point.y();
                        double height = wgs84Point.z();
                        height=44;//固定高度防止闪烁,渲染优先级不冲突
                        // 调用 beginDraw 添加点击点
                        beginDraw(osg::Vec3d(lon, lat, height));
                    }
                    break;
                }

                case osgGA::GUIEventAdapter::RIGHT_MOUSE_BUTTON:
                {
                    resetDraw();
                    osg::ref_ptr<osg::Node> StraightArrowNode = NULL;
                    osgUtil::LineSegmentIntersector::Intersections intersections;
                    if (viewer->computeIntersections(ea.getX(), ea.getY(), nodePaths, intersections))
                    {
                        for (const auto& intersection : intersections)
                        {
                            for (auto i : intersection.nodePath)
                            {
                                std::string nodeName = i->getName();
                                std::cout << "拾取到的节点:" << nodeName << std::endl;
                                if (nodeName == "StraightArrow")
                                {

                                    StraightArrowNode = i;  // 记录 Polygon
                                }
                            }
                        }
                    }
                    // 先删除 Polygon
                    if (StraightArrowNode)
                    {
                        removeSelectStraightArrow(StraightArrowNode);
                    }
                    break;
                }

                default:
                    break;
                }
                break;
            }


        case osgGA::GUIEventAdapter::MOVE:
          {
              osgUtil::LineSegmentIntersector::Intersections intersections;
              if (viewer->computeIntersections(ea.getX(), ea.getY(), nodePaths, intersections))
              {
                  const osgUtil::LineSegmentIntersector::Intersection& intersection = *intersections.begin();
                  osg::Vec3d worldPoint = intersection.getWorldIntersectPoint();
                  const osgEarth::SpatialReference* mapSRS = _mapNode->getMapSRS();
                  const osgEarth::SpatialReference* geoSRS = osgEarth::SpatialReference::get("wgs84");
                  osgEarth::GeoPoint geoPoint;
                  geoPoint.fromWorld(mapSRS, worldPoint);
                  osgEarth::GeoPoint wgs84Point;
                  geoPoint.transform(geoSRS, wgs84Point);
                  double lon = wgs84Point.x();
                  double lat = wgs84Point.y();
                  double height = wgs84Point.z();
                  // 调试输出,确认MOVE事件
                  // 鼠标移动时更新绘制状态(动态提示线)
                  height=44;//固定高度防止闪烁,渲染优先级不冲突
                  moveDraw(osg::Vec3d(lon, lat, height));
              }
          }
          break;
        case osgGA::GUIEventAdapter::KEYDOWN:
        {
            switch(ea.getKey())
              {
              case osgGA::GUIEventAdapter::KEY_Delete:
                  qDebug() << "Delete key pressed";
                  removeStraightArrow(); // 按下 Delete 键删除刚刚创建的矩形
                  break;
              case osgGA::GUIEventAdapter::KEY_BackSpace:
                    qDebug() << "BackSpace key pressed";
                    removeAllStraightArrows(); // 按下 BackSpace 键删除所有矩形
                  break;
              default:
                  break;
            }
        }
        break;

        default:
          break;
        }
    return false;
}


参考代码链接
#include"DrawerMath.h"
DrawerMath.cpp

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值