没有想不到只有做不到/万能地图标注点marker设计/各种参数需求场景/你要的都有

0 阅读8分钟

一、前言说明

地图组件中标注点在所有覆盖物中,使用场景是最大的,就是在地图上显示个图片图标,然后可以显示提示文字信息,这个标注点还可以轨迹移动,轨迹回放和实时轨迹都要用到这个覆盖物,所以涉及一个尽可能满足现有各种需求场景的类非常重要,最开始的时候做的很简单,就是绘制一个图片,在Qt中直接用qpainter的drawimage即可,这里涉及到一个问题,有些图标是水滴状,有些是圆形的,一般来说,用户希望的是水滴状的显示在坐标的顶部,圆形的图标显示在坐标的中间,甚至还有些其他的图标,这样展示会显示的更合理。于是多了一个参数,需要设置图片在位置的哪个方位,支持左侧、右侧、上侧、下侧、中间、左上角、右上角、左下角、右下角共9个位置,这样涵盖了所有可能的情况。

有了图标还是不够的,一般还需要有提示文本,这个提示文本也需要有9个方位可控,然后就是支持背景颜色和透明度,一般来说是半透明更好看,还支持边框颜色和粗细设置。当然还包括字体名称和大小的参数。后面发现尽管是在对应位置绘制的文本,靠的太近也不大好看,还增加文本位置的偏移值参数,也就是尽量离图标远一点,不要紧挨着。写完所有的参数,专门做了个标注的示例,可以动态调整各个参数。

二、效果图

在这里插入图片描述

三、相关代码

#include "overlaymarker.h"

OverlayMarker::OverlayMarker(qreal lng, qreal lat, int zoom_min, int zoom_max)
    : OverlayShape(lng, lat, QSizeF(0, 0), zoom_min, zoom_max)
{
    m_movie = NULL;
    this->setOverlayType(OverlayType_Marker);
}

OverlayMarker::~OverlayMarker()
{
    this->stop();
}

QPixmap OverlayMarker::image() const
{
    return m_image;
}

void OverlayMarker::setImage(const QPixmap &image)
{
    m_image = image;
    this->setSizePx(m_image.size());
}

void OverlayMarker::setImage(const QString &filename, const QPixmap &image)
{
    //存在文件路径则优先取文件
    if (!filename.isEmpty()) {
        this->stop();
        this->setImage(QPixmap(filename));

        //如果是动图则取出每一帧进行绘制
        if (filename.endsWith(".gif")) {
            m_movie = new QMovie(filename);
            connect(m_movie, SIGNAL(frameChanged(int)), this, SLOT(frameChanged(int)));
            m_movie->start();
        }
    } else if (!image.isNull()) {
        this->stop();
        this->setImage(image);
    }
}

void OverlayMarker::draw(QPainter *painter, const RectWorldCoord &backbuffer_rect, int cur_zoom)
{
    //先判断是否在主图画布的区域里面
    RectWorldCoord pixmap_rect_coord(boundingBox(cur_zoom));
    if (backbuffer_rect.rawRect().intersects(pixmap_rect_coord.rawRect())) {
        //计算标注点图片绘制区域
        PointWorldPx top_left = getPointPx(pixmap_rect_coord.topLeftCoord(), cur_zoom);
        PointWorldPx bottom_right = getPointPx(pixmap_rect_coord.bottomRightCoord(), cur_zoom);
        RectWorldPx pixmap_rect_px(top_left, bottom_right);
        QRectF pixmap_rect = pixmap_rect_px.rawRect();
        QPointF pixmap_center = pixmap_rect_px.centerPx().rawPoint();
        QPointF pixmap_point = QPointF(-pixmap_rect.width() / 2.0, -pixmap_rect.height() / 2.0);

        //平移坐标系/绘制标注点图片
        painter->translate(pixmap_center);
        painter->rotate(rotation());
        painter->drawPixmap(pixmap_point, m_image);

        //图标所在区域
        //painter->fillRect(QRectF(pixmap_point, sizePx()), Qt::red);

        //恢复坐标系
        painter->rotate(-rotation());
        painter->translate(-pixmap_center);

        //绘制显示的文本
        if (!m_text.isEmpty()) {
            QSizeF text_size = OverlayShape::initText(painter);
            this->drawText(painter, pixmap_rect, text_size, m_text);
        }

        //绘制编辑状态
        if (m_edit) {
            this->drawHand(painter, m_point_coord, cur_zoom);
        }
    }
}

void OverlayMarker::stop()
{
    if (m_movie) {
        m_movie->deleteLater();
        m_movie = NULL;
    }
}

void OverlayMarker::frameChanged(int)
{
    //取出动图的每一张图片
    m_image = m_movie->currentPixmap();
    emit requestRedraw();
}

OverlayBase *OverlayHelper::addMarker(MapWidget *map, const QString &flag, const QPointF &point, const QString &image, const QPixmap &pixmap, int rotate, int align)
{
    OverlayMarker *marker = new OverlayMarker(point.x(), point.y());
    marker->setOverlayFlag(flag);
    marker->setImage(image, pixmap);
    marker->setRotation(rotate);
    marker->setAlignType(AlignType(align));
    map->addOverlay(marker, redraw);
    return marker;
}

void OverlayHelper::updateMarker(OverlayBase *overlay, const QPointF &point, const QString &image, const QPixmap &pixmap, int rotate, int align)
{
    OverlayMarker *marker = (OverlayMarker *)overlay;
    OverlayHelper::updateColor(marker, QColor(), 0);
    marker->setImage(image, pixmap);
    if (!point.isNull()) {
        marker->setCoord(PointWorldCoord(point.x(), point.y()));
    }
    if (rotate >= 0) {
        marker->setRotation(rotate);
    }
    if (align >= 0) {
        marker->setAlignType(AlignType(align));
    }
}

void OverlayHelper::updateMarker(MapWidget *map, const QString &flag, const QPointF &point, const QString &image, const QPixmap &pixmap, int rotate, int align)
{
    foreach (OverlayBase *overlay, map->getOverlays()) {
        if (overlay->overlayFlag() == flag && overlay->overlayType() == OverlayType_Marker) {
            OverlayHelper::updateMarker(overlay, point, image, pixmap, rotate, align);
        }
    }
}

QPointF OverlayHelper::getMarkerPara(OverlayBase *overlay, QPixmap *image, int *rotate, int *align)
{
    OverlayMarker *marker = (OverlayMarker *)overlay;
    if (image) {
        (*image) = marker->image();
    }
    if (rotate) {
        (*rotate) = marker->rotation();
    }
    if (align) {
        (*align) = (int)marker->alignType();
    }

    return marker->coord().rawPoint();
}

QPointF OverlayHelper::getMarkerPara(MapWidget *map, const QString &flag, QPixmap *image, int *rotate, int *align)
{
    QPointF coord;
    foreach (OverlayBase *overlay, map->getOverlays()) {
        if (overlay->overlayFlag() == flag && overlay->overlayType() == OverlayType_Marker) {
            coord = OverlayHelper::getMarkerPara(overlay, image, rotate, align);
            break;
        }
    }

    return coord;
}

四、相关地址

  1. 国内站点:gitee.com/feiyangqing…
  2. 国际站点:github.com/feiyangqing…
  3. 个人作品:blog.csdn.net/feiyangqing…
  4. 文件地址:pan.baidu.com/s/1ZxG-oyUK… 提取码:o05q 文件名:bin_mapwidget.zip

五、功能特点

  1. 支持各种地图源,包括天地图、高德地图、腾讯地图、谷歌地图、微软地图等。
  2. 标准WGS-84地球坐标系,采用默卡托投影,可以拓展其他坐标系和投影规则。
  3. 支持在线和离线两种场景需求,可以自定义在线瓦片地址格式和离线瓦片地址格式。
  4. 多线程下载和加载瓦片图片文件,多线程绘制,自动缓存瓦片文件。
  5. 在线模式下,可以开启是否缓存文件,指定缓存路径,将下载的瓦片文件存放到本地,默认优先从缓存文件查找,如果存在缓存文件则加载缓存文件,不存在则联网下载。
  6. 可以拖动地图,鼠标滚轮放大和缩小地图,以鼠标所在位置作为缩放中心点,提供缩放控件手动单击进行操作。
  7. 多图层机制,支持多个瓦片叠加图层和图形绘制图层,全部采用双缓冲技术,所有的图形和瓦片全部绘制到一个图片文件上,最终再将图片文件绘制到地图控件。不可见区域的图层包括覆盖物不会触发绘制,降低CPU占用。
  8. 预加载机制,默认绘制的图层大小以当前区域往四周放大两倍,这样在鼠标拖动和缩放的时候,不会看到明显的加载过程,体验更佳。
  9. 内置了多种图形覆盖物,包括坐标点、文本、标注点、折线、多边形、矩形、圆形等,可以设置边框颜色粗细、填充颜色和透明度等参数。
  10. 标注点支持旋转角度和提示文本,其中提示文本可以设置在标注点的相对位置,标注点图片支持gif动图,可以动态切换静态图和动图。
  11. 标注点和提示文本可以设置相对位置,位置包括左侧、右侧、上侧、下侧、中间、左上角、右上角、左下角、右下角。标注点默认按照底部居中对齐,一般圆形图标可以设置中心点对齐。
  12. 标注点提示文本可设置背景颜色,透明度、颜色边框和粗细,支持换行和多行文字。
  13. 所有的图形可以动态更新前景色、颜色粗细、背景颜色、颜色透明度等。
  14. 支持删除单个图形、删除一种类型的图形、删除所有图形、隐藏单个或者所有图形等。
  15. 支持动态绘制各种图形,开启后直接在地图上鼠标按下绘制,鼠标右键结束绘制,非常方便快捷。
  16. 在对应图形区域鼠标按下,发出图形单击信号,精准识别单击区域,比如折线以鼠标在折线条上作为判断依据,多边形区域以鼠标在整个多边形区域内为准,而不是以矩形区域,包括圆形也是以圆形内部为准。
  17. 可以动态启动禁用比例尺、十字线、缩放控件、地图拖曳、键盘操作、滚轮缩放、双击放大、鼠标追踪等特性。
  18. 可以任意指定经纬度区域进行瓦片拼接保存成图片文件,也可以直接对整个可视区域或者缓存区域的地图图片文件保存。支持任意多边形轮廓保存成图片,比如某个行政区的瓦片保存。
  19. 图形可以动态设置zindex层叠顺序,值越大,越显示在前面,内部维护着一个zindex表,默认按照添加的先后顺序增加,后面添加的显示在前面,主动设置后,按照设置的zindex来绘制。
  20. 支持将QWidget对象作为覆盖物添加到地图控件中,跟随地图移动位置,极大提高灵活性,比如可以将自定义控件直接作为地图控件的子对象加入进去。
  21. 内置moveMarker轨迹移动类,支持历史轨迹数据回放和实时轨迹移动,可设置图标、轨迹线的颜色和粗细、移动速度、移动间隔、平滑移动等,支持多条轨迹线条同时移动。
  22. 大量使用按需绘制机制,包括内部提供合理的默认值来触发绘制,也可以手动传入参数指定是否需要立即绘制,比如删除了某个覆盖物,有些频繁的操作可以不指定立即绘制,等操作完成后再统一一起绘制,效率更高。
  23. 默认开启缓存瓦片机制,所有加载过的瓦片文件都存储在内存中,下次再次绘制直接从内存取出来绘制,既不需要从联网获取,也不需要从缓存文件获取,直接内存取出来绘制,响应迅速效率最高体验最佳。
  24. 支持批量添加覆盖物,比如几万个标注点和圆形,都是瞬间完成绘制,相比web网页的方式,性能提升百倍以上。
  25. 支持街道图、卫星图、混合图、路网图等各种图层,可以任意叠加N个图层,甚至杂交不同地图厂家的瓦片文件。
  26. 纯QWidget绘制,非qml也非web,不依赖qml或者浏览器控件,支持极低性能的嵌入式环境。
  27. 原创轻量级,5000行代码,架构漂亮,注释详细,拓展方便,容易学习,适合各种初学者和进阶者,方便二次开发。
  28. 支持任意Qt版本、任意系统、任意编译器,包括嵌入式linux和各种国产电脑环境。古法编程,不含任何AI代码,品质保证。