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;
}