osgEarth中绘制多边形

osgEarth3.2+qt5.14+osg3.6.5

#ifndef POLYGONDRAWER_H
#define POLYGONDRAWER_H

#include <osgEarth/MapNode>
#include <osgEarth/Geometry>
#include <osgEarth/GeoMath>
#include <osgEarth/Style>
#include<osgEarth/Feature>
#include<osgEarth/FeatureNode>
#include <osgViewer/Viewer>
#include <osgGA/GUIEventHandler>
#include <vector>
#include<map>
class PolygonDrawer: public osgGA::GUIEventHandler
{
public:
    PolygonDrawer(osg::ref_ptr<osgEarth::MapNode>);

    //开始绘制多边形
    void beginDraw(const osg::Vec3d& lla);

    //绘制移动虚线
    void moveDraw(const osg::Vec3d& lla);

    //实现短暂虚线消失,结束绘制形状
    void resetDraw();

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

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

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

    //获取绘制状态
    bool getIsDrawPoly();

    //设置绘制状态
    void setIsDrawPoly(bool isDrawPoly);

    //设置多边形填充颜色
    void setDrawPolyFillColor(osgEarth::Color fillColor);

    //隐藏所有多边形
    void hideAllPolygons();

    //显示所有多边形
    void showAllPolygons();

    //按照三种颜色隐藏区域
    void togglePolygonByColor(const osgEarth::Color& color);
    //事件处理
    bool handle(const osgGA::GUIEventAdapter &ea,osgGA::GUIActionAdapter &aa);
private:
    bool _isDrawPoly;                                             //开启绘制矩形
    osgEarth::Color _fillColor;                                    //多边形填充颜色
    osg::NodePath   nodePaths;  // 用于存储节点路径列表,在进行射线求交(intersections)时作为过滤条件
    osg::ref_ptr<osgEarth::MapNode>_mapNode;// 当前使用的地图节点(osgEarth::MapNode),用于坐标转换、添加绘制节点等操作
    osgEarth::Style _polygonStyle;// 定义多边形的样式,包括填充颜色、边界线样式以及高程显示设置
    osg::ref_ptr<osgEarth::FeatureNode> _polygonFeatureNode;// 存储绘制的多边形的 FeatureNode,FeatureNode 包含了多边形的几何数据及样式信息,用于显示多边形
    osgEarth::Style _stippleLineStyle;// 定义虚线提示的样式,用于在动态更新过程中绘制虚线(例如鼠标移动时显示预览边界)
    osg::ref_ptr<osgEarth::FeatureNode> _stippleFeatureNode;// 存储虚线提示的 FeatureNode,用于显示实时更新的虚线效果
    std::vector<osg::Vec3d> _clickedPoints; // 存储点击的点

    std::vector<osg::ref_ptr<osgEarth::FeatureNode>> _polygonNodes; // 保存所有绘制的矩形
    std::multimap<osgEarth::Color, osg::ref_ptr<osgEarth::FeatureNode>> _colorPolygonMap;//保存颜色和多边形映射关系

};

#endif // POLYGONDRAWER_H

#include "PolygonDrawer.h"
#include<QDebug>
PolygonDrawer::PolygonDrawer(osg::ref_ptr<osgEarth::MapNode> mapNode)
    :_mapNode(mapNode)
    ,_polygonFeatureNode(nullptr)
    ,_stippleFeatureNode(nullptr)
    ,_isDrawPoly(false)
    ,_fillColor(NULL)

{
	//这个非常重要,保证和地图表面交点是最上层
    nodePaths.push_back(mapNode);
    //设置区域填充颜色,透明度
    _polygonStyle.getOrCreate<osgEarth::PolygonSymbol>()->fill()->color()= osgEarth::Color(_fillColor, 0.45);
    // 边界线样式:白色,宽度2像素
    osgEarth::LineSymbol* ls = _polygonStyle.getOrCreate<osgEarth::LineSymbol>();
    ls->stroke()->color() = osgEarth::Color::White;
    ls->stroke()->width() = 2.0f;
    ls->stroke()->widthUnits() = osgEarth::Units::PIXELS;

    // 高程符号设置:贴地渲染、垂直偏移
    osgEarth::AltitudeSymbol* alt = _polygonStyle.getOrCreate<osgEarth::AltitudeSymbol>();
    alt->clamping() = osgEarth::AltitudeSymbol::CLAMP_ABSOLUTE;
    alt->technique() = osgEarth::AltitudeSymbol::TECHNIQUE_GPU;
    alt->verticalOffset() = 0.1;

    // ---------------------- 配置虚线(stipple)样式 ----------------------
    // 创建或获取 LineSymbol,用于绘制动态虚线提示(例如鼠标移动时预览边界)
    // 设置虚线颜色为红色、宽度为2、细分(tessellation)为20
    osgEarth::LineSymbol* stippleLS = _stippleLineStyle.getOrCreate<osgEarth::LineSymbol>();
    stippleLS->stroke()->color() = osgEarth::Color::Red;
    stippleLS->stroke()->width() = 2.0;
    stippleLS->tessellation() = 20.0;
    stippleLS->stroke()->stipple() = 255;  // 定义虚线样式
    // 配置虚线使用的高程符号,确保虚线也能贴地显示并有适当的垂直偏移
    osgEarth::AltitudeSymbol* stippleAlt = _stippleLineStyle.getOrCreate<osgEarth::AltitudeSymbol>();
    stippleAlt->clamping() = osgEarth::AltitudeSymbol::CLAMP_ABSOLUTE;
    stippleAlt->technique() =osgEarth:: AltitudeSymbol::TECHNIQUE_GPU;
    stippleAlt->verticalOffset() = 0.1;
}



void PolygonDrawer::beginDraw(const osg::Vec3d &lla)
{
    //存储点击点
    _clickedPoints.push_back(lla);
    //如果小于两个点,不会绘制多边形
    if (_clickedPoints.size() <= 2) {
        return;
    }
    //如果 _featureNode 还不存在,就创建一个新的 Feature 对象
    if (_polygonFeatureNode == nullptr) {
        osgEarth::Feature* feature = new osgEarth::Feature(new osgEarth::Polygon,osgEarth::SpatialReference::get("wgs84"), _polygonStyle);
        _polygonFeatureNode = new osgEarth::FeatureNode(feature,_polygonStyle);
         _mapNode->addChild(_polygonFeatureNode);

         // 记录颜色和多边形的对应关系
         osgEarth::Color polyColor = _polygonStyle.getOrCreate<osgEarth::PolygonSymbol>()->fill()->color();
         // 将相同颜色的多个多边形存入 multimap
         _colorPolygonMap.insert(std::make_pair(polyColor, _polygonFeatureNode));
         //添加绘制的多边形
         _polygonNodes.push_back(_polygonFeatureNode);
    }
    if(_fillColor==osgEarth::Color::Red){
        _polygonFeatureNode->setName("Polygon_Red");
    }else{
        _polygonFeatureNode->setName("Polygon");
    }
    // 更新多边形的几何数据:清空原有数据,加入所有采集的点
    osgEarth::Geometry* geom = _polygonFeatureNode->getFeature()->getGeometry();
    geom->clear();
    for (auto& n : _clickedPoints) {
        geom->push_back(n);
    }
    _polygonFeatureNode->dirty();
    // 若存在虚线提示节点,则清空其数据
    if (_stippleFeatureNode.valid()) {
       _stippleFeatureNode->getFeature()->getGeometry()->clear();
    }

}

void PolygonDrawer::moveDraw(const osg::Vec3d &lla)
{
    // 若采集点不足,则无需更新虚线
    if (_clickedPoints.size() < 2) {
        return;
    }
    // 如果虚线节点尚未创建,则新建一个使用 LineString 几何的节点
    if (!_stippleFeatureNode.valid()) {
        osgEarth::Feature* feature  = new osgEarth::Feature(new osgEarth::LineString, osgEarth::SpatialReference::get("wgs84"));
        _stippleFeatureNode = new osgEarth::FeatureNode(feature,_stippleLineStyle);
        _mapNode->addChild(_stippleFeatureNode.get());
    }
    // 更新虚线的几何数据,形成从第一个点 -> 当前鼠标点 -> 最后一个点的动态提示效果
    osgEarth::Geometry* geom = _stippleFeatureNode->getFeature()->getGeometry();
    geom->clear();
    geom->push_back(_clickedPoints.front());
    geom->push_back(lla);
    geom->push_back(_clickedPoints.back());

    _stippleFeatureNode->dirty();
}

void PolygonDrawer::resetDraw()
{
    // 清空所有点击点
    _clickedPoints.clear();

    // 如果虚线节点存在,则从场景中移除,并将引用置空
    if (_stippleFeatureNode.valid()) {
        _mapNode->removeChild(_stippleFeatureNode.get());//使得绘制完一个形状后,红虚线消失
        _stippleFeatureNode = nullptr;
    }
    _polygonFeatureNode = nullptr;
}

void PolygonDrawer::removeSelectPolygon(osg::ref_ptr<osg::Node> node)
{
    _mapNode->removeChild(node); // 从地图中移除
}

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

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

bool PolygonDrawer::getIsDrawPoly()
{
     return _isDrawPoly;
}

void PolygonDrawer::setIsDrawPoly(bool isDrawPoly)
{
    _isDrawPoly=isDrawPoly;
}

void PolygonDrawer::setDrawPolyFillColor(osgEarth::Color fillColor)
{
    _fillColor=fillColor;
    // 更新样式
    _polygonStyle.getOrCreate<osgEarth::PolygonSymbol>()->fill()->color() = osgEarth::Color(_fillColor, 0.45);
}

void PolygonDrawer::hideAllPolygons()
{
    for (auto& node : _polygonNodes)
    {
       node->setNodeMask(0); // 隐藏
    }
}

void PolygonDrawer::showAllPolygons()
{
    for (auto& node : _polygonNodes)
    {
        node->setNodeMask(0xffffffff); // 显示
    }
}

void PolygonDrawer::togglePolygonByColor(const osgEarth::Color &color)
{
    //使用 equal_range 获取所有匹配的多边形。
    //不要直接比较 osgEarth::Color,而是转换成整数 RGB 形式进行匹配:osgEarth::Color 作为 std::multimap 的 key 可能无法精确匹配,因为颜色是浮点数,存在精度误差
    int r = static_cast<int>(color.r() * 255);
    int g = static_cast<int>(color.g() * 255);
    int b = static_cast<int>(color.b() * 255);

    for (auto it = _colorPolygonMap.begin(); it != _colorPolygonMap.end(); ++it)
    {
        osgEarth::Color storedColor = it->first;
        int sr = static_cast<int>(storedColor.r() * 255);
        int sg = static_cast<int>(storedColor.g() * 255);
        int sb = static_cast<int>(storedColor.b() * 255);

        if (sr == r && sg == g && sb == b)  // 颜色匹配
        {
            osg::ref_ptr<osgEarth::FeatureNode> node = it->second;
            node->setNodeMask(node->getNodeMask() == 0 ? 0xffffffff : 0);
        }
    }

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

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

    // 处理 H 和 S 键,无论 _isDrawPoly 是否为 true
       if (ea.getEventType() == osgGA::GUIEventAdapter::KEYDOWN)
       {
           switch (ea.getKey())
           {
                case osgGA::GUIEventAdapter::KEY_H: // 按下 'H' 键隐藏
                    qDebug() << "H key pressed - Hiding polygons";
                    hideAllPolygons();
                    return true;
                case osgGA::GUIEventAdapter::KEY_S: // 按下 'S' 键显示
                    qDebug() << "S key pressed - Showing polygons";
                    showAllPolygons();
                    return true;
                case osgGA::GUIEventAdapter::KEY_1:
                    qDebug() << "Toggling visibility for Blue polygon";
                    togglePolygonByColor(osgEarth::Color::Blue);
                    return true;
                case osgGA::GUIEventAdapter::KEY_2:
                    qDebug() << "Toggling visibility for Yellow polygon";
                    togglePolygonByColor(osgEarth::Color::Yellow);
                    return true;
                case osgGA::GUIEventAdapter::KEY_3:
                    qDebug() << "Toggling visibility for Red polygon";
                    togglePolygonByColor(osgEarth::Color::Red);
                    return true;
                case osgGA::GUIEventAdapter::KEY_Delete:   // 新增独立case
                  if (_isDrawPoly) {                     // 条件判断放在case内部
                      qDebug() << "Delete key pressed";
                      removePolygon();
                      return true;
                  }
                  break;  // 不满足条件时继续传递事件
                case osgGA::GUIEventAdapter::KEY_BackSpace:
                  if (_isDrawPoly) {
                      qDebug() << "BackSpace key pressed";
                      removeAllPolygons();
                      return true;
                  }
                  break;
           default:
               return true;
           }
       }

    if (!_isDrawPoly)
        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=45;//固定高度防止闪烁,渲染优先级不冲突
                        // 调用 beginDraw 添加点击点
                        beginDraw(osg::Vec3d(lon, lat, height));
                    }
                    break;
                }

                case osgGA::GUIEventAdapter::RIGHT_MOUSE_BUTTON:
                {
                    resetDraw();
                    osg::ref_ptr<osg::Node> PolygonNode = NULL;
                    osgUtil::LineSegmentIntersector::Intersections intersections;
                    if (viewer->computeIntersections(ea.getX(), ea.getY(), nodePaths, intersections)) {
                        for (const auto &intersection : intersections) {
                            for (auto i : intersection.nodePath) {
                                if (i->getName().find("Polygon") != std::string::npos) {
                                    PolygonNode = i;
                                    break;
                                }else if(i->getName().find("Polygon_Red") != std::string::npos){
                                    PolygonNode = i;
                                    break;
                                }
                            }
                            if (PolygonNode)
                                break;
                        }
                    }
                    // 先删除 Polygon
                    if (PolygonNode)
                    {
                        removeSelectPolygon(PolygonNode);
                    }
                    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=45;//固定高度防止闪烁,渲染优先级不冲突
                  moveDraw(osg::Vec3d(lon, lat, height));
              }
          }
          break;

        default:
          break;
        }
    return false;
}


参考代码链接
办理浏览卡,29元 235G,可以看看

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值