首页 > 编程学习 > SkeyeGisMap地图扩展(五) 自定义形状

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

发布时间:2022/11/11 17:06:06

1、自定义形状步骤

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

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

class CustomShape : public QSGNode, public MapShapeNode

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

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

static const MapShapeNode::ShapeType CustomShapeType = MapShapeNode::ShapeType::MapUserShape + 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);
}

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

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

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

关于事件处理见之前的文章: 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

Copyright © 2010-2022 dgrt.cn 版权所有 |关于我们| 联系方式