• SkeyeGisMap地图扩展(五) 自定义形状


    1、自定义形状步骤

    SkeyeGisMap 中实现自定义形状需要继承自 MapShapeNode, 也可继承任何已有的 Map*Node

    当然, 我们这里为了说明流程, 选择从头实现一个新的形状。

    class CustomShape : public QSGNode, public MapShapeNode
    
    • 1

    注意, 当一个地图节点想要被渲染, 它还需要继承 QSG*Node

    因为是一个新的形状, 所以增加一个新的类型给 MapShapeNode:

    static const MapShapeNode::ShapeType CustomShapeType = MapShapeNode::ShapeType::MapUserShape + 1;
    
    • 1

    现在我们开始实现这个新形状:

    CustomShape(QQuickItem *item, MapRootNode *root) : MapShapeNode(CustomShapeType)
    {
        m_root = root;
        m_text = new MapTextNode(item, root, QPointF(200, 200), QFont("微软雅黑", 20, QFont::Bold)
                                            , u8"这是一个自定义形状\n使用QPainterPathStroker\n可以自由移动它\nGood luck~"
                                            , Qt::white, QTextOption(Qt::AlignCenter)
                                            , Qt::yellow, MapTextNode::TextStyle::Outline, Qt::black);
        m_text->setLineHeight(40);
        m_text->setFlag(OwnedByParent);
    
        QPainterPathStroker stroker;
        stroker.setCapStyle(Qt::RoundCap);
        stroker.setJoinStyle(Qt::RoundJoin);
        stroker.setDashPattern(Qt::DashDotLine);
        stroker.setWidth(10);
    
        QPainterPath path;
        auto rect = m_text->boundingRect();
        rect.setSize(rect.size() / root->scale() + QSizeF(stroker.width(), stroker.width()));
        rect.moveCenter(m_text->position());
        path.addRect(rect);
    
        QPainterPath outlinePath = stroker.createStroke(path);
        auto polygons = outlinePath.toSubpathPolygons().toVector();
        for (auto &polygon: polygons) {
            if (maputil::judgeOrder(polygon))
                std::reverse(polygon.begin(), polygon.end());
        }
    
        m_multiPolygon = new MapMultiPolygonNode(polygons, "red", 2, "#8CFFFB");
        m_multiPolygon->setFlag(OwnedByParent);
    
        m_text->appendChildNode(m_multiPolygon);
    
        appendChildNode(m_text);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    首先, 我们创建一个地图文本节点, 参数比较多(当然很多都有默认值)。

    接着, 我们的想法是给这个文本加上一些不同的边框。

    Qt 中提供了相当丰富的绘制工具, 这里直接使用 QPainterPathStroker 生成了一个虚线边框。

    注意, rect.setSize(rect.size() / root->scale() + QSizeF(stroker.width(), stroker.width()));, 因为 MapRootNode 默认可缩放(即跟随地图缩放), 因此需要除去缩放才能算出正确的边框矩形。

    接着, 我们将生成的边框转换为多边形, MapMultiPolygonNode 用于绘制多个多边形或多个带孔多边形, 并且需要保证多边形的环绕方向, MapMultiPolygonNode 使用OGC的环绕标准, 即顺时针内环, 逆时针外环。

    因为生成的边框多边形为顺时针, 所以还需要翻转方向。

    然后添加到子节点即可, 注意, 这里的边框添加到文本的子节点是因为边框同样需要变换正方向, 而直接成为子节点可以直接继承变换矩阵。

    最后我们处理事件即可:

    void translate(const QPointF &offset)
    {
        auto polygons = m_multiPolygon->polygons();
        for (auto &polygon: polygons) {
            polygon.translate(offset);
        }
        m_multiPolygon->setPolygons(polygons);
        m_text->setPosition(m_text->position() + offset);
    }
    
    virtual bool contains(const QPointF &position) override
    {
        return m_text->boundingPolygon().containsPoint(position, Qt::OddEvenFill);
    }
    
    virtual void mousePresseEvent(MapMouseEvent *event) override
    {
        MapShapeNode::mousePresseEvent(event);
        m_startPosition = event->displayCoord();
        m_text->setBackground(Qt::green);
        event->accepted();
    }
    
    virtual void mouseMoveEvent(MapMouseEvent *event) override
    {
        MapShapeNode::mouseMoveEvent(event);
        translate(event->displayCoord() - m_startPosition);
        m_startPosition = event->displayCoord();
        event->accepted();
    }
    
    virtual void mouseReleaseEvent(MapMouseEvent *event) override
    {
        MapShapeNode::mouseReleaseEvent(event);
        m_text->setBackground(Qt::yellow);
        event->accepted();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    关于事件处理见之前的文章: https://openskeye.blog.csdn.net/article/details/127517747

    这里主要提一下 virtual bool contains(const QPointF &position), 只有返回 true 时才会接收事件, 我们判断鼠标是否进入文本区域即可。

    2、效果展示

    在这里插入图片描述

    源码地址(dynamictarget): https://gitee.com/visual-opening/skeyegismap/tree/master/coremap/example

  • 相关阅读:
    自动驾驶技术的基础知识
    【图解源码】Zookeeper3.7源码分析,包含服务启动流程源码、网络通信源码、RequestProcessor处理请求源码
    初识OpenGL (-)多级渐远纹理(Mipmap)
    Outside ExpertPDF HtmlToPdf 转换器 17.0
    csdn测开涨薪技术-Git原理及使用全套教程
    SparkCore系列-9、共享变量
    Vue 3 渲染机制解密:从模板到页面的魔法
    .NET中Invoke和BeginInvoke
    JavaWeb篇_02——服务器简介及Tomcat服务器简介
    仪表盘自定义标题和数值样式
  • 原文地址:https://blog.csdn.net/weixin_42113310/article/details/127802936