一、前言说明
这个地图组件开发了大半年,全身心的投入,不断解决一个个难题,终极对标产品是奥维地图,他的那个地图组件确实做到了极致,也是qt开发的,至于是不是用到了其他qtlocation模块不知道,他发布的是一个单文件。至于为什么要用纯qwidget绘制去实现这个组件,前面也有提到过,主要就是性能方面的考虑,无论web怎么吹上天,大量图形的绘制,还是比不过本地CS架构的程序,有些客户是很古老的电脑配置,甚至一些国产机器匹配的是Qt4.8这种古老的版本,再或者需要编译成wasm或者移动端运行,纯qwidget的方案不要太好,兼容性最强大,唯一的缺点就是没有一些动画效果,需要自己用代码实现,而如果是qml则内置了这些效果,直接几行代码就能搞定整体地图移动动画效果,还一个就是qml大部分走的GPU绘制,占用CPU这块相对来说低一些,而纯qpainter绘制地图,由于是一个小的瓦片地图组成的,所以在每收到一个小瓦片的时候,就需要填充绘制一次,导致频繁瞬间不断的拖动地图,在不断的下载和加载地图,会占用一定的CPU,个人实测在7%左右,这个是瞬间造成的,不是一直这个占用,而且有内存缓存图片策略,在下次加载到这个区域的瓦片时候,如果内存存在该瓦片,则先从内存取出来,速度极快,可能就一个中断。
这个组件之前很多人问过有没有,比如需要在很差的配置的电脑上运行,不能用web浏览器方案,有些需要在手机移动端运行,甚至有个人还要在wasm网页上运行,当初问的时候也没有备注登记,现在搞出来了,人又找不到了,也不知道是谁,哎,事情往往就是这样,全靠兴趣支撑,哎心好累。
二、效果图
三、代码使用
- 将core_mapwidget目录拷贝到你的项目目录,并在pro中填写引入代码加入到你的项目中。$$PWD/../表示上级目录。
include ($$PWD/../core_mapwidget/core_mapwidget.pri)
- 引入头文件,编写代码。
#include "mapwidget.h"
//创建地图控件
MapWidget w;
//设置瓦片源头
w.setTileSource(TileSource_Tian);
//设置瓦片类型
w.setTileType(TileType_Satellite);
//加载地图
w.load();
- 更详细的接口参考mapwidget.h头文件。
#ifndef MAPWIDGET_H
#define MAPWIDGET_H
#include "tilehelper.h"
#include "layeroverlay.h"
class MapWidget : public QWidget
{
Q_OBJECT
public:
explicit MapWidget(QWidget *parent = 0);
//设置唯一标识
void setFlag(const QString &flag);
protected:
void resizeEvent(QResizeEvent *);
private:
//瓦片数据源
TileSource tileSource;
//瓦片类型
TileType tileType;
//是否离线
bool offline;
//离线目录
QString offlinePath;
//地图控件
MapControl *mapControl;
//图层锁
QReadWriteLock layerMutex;
//图形图层
LayerOverlay *overlayLayer;
private slots:
//地图相关事件
void mouseEvent(int mouse_type, QMouseEvent *mouse_event, PointWorldCoord press_coord, PointWorldCoord current_coord);
public slots:
//加载地图
void load();
//地图放大
void zoomIn();
//地图缩小
void zoomOut();
//重新绘制
void requestRedraw();
public:
//获取和设置缩放
int getZoom() const;
void setZoom(int zoom);
//获取和设置中心
QPointF getCenter() const;
void setCenter(const QPointF &point);
//获取和设置区域
QRectF getRect() const;
void setRect(const QRectF &rect);
//获取和设置绘图类型
OverlayType getDrawType() const;
void setDrawType(const OverlayType &drawType);
//获取地图控件
MapControl *getMapControl();
//设置绘图尺寸
void setViewportSize(const QSize &size);
//获取图形图层
LayerOverlay *getOverlayLayer();
//显示和隐藏图层
void setLayerVisible(bool visible);
//获取图形集合
QList<OverlayBase *> getOverlays();
//获取指定图形
OverlayBase *getOverlay(const QString &flag);
//添加单个图形
void addOverlay(OverlayBase *overlay, bool redraw);
//删除单个图形
void deleteOverlay(OverlayBase *overlay, bool redraw);
//清空所有图形
void clearOverlay(bool redraw = true);
//显示比例尺
void enableScalebar(bool visible);
//显示十字线
void enableCrosshairs(bool visible);
//显示缩放控件
void enableZoomControls(bool visible, AlignType align = AlignType_TopLeft);
//启用布局鼠标事件
void enableLayerMouseEvents(bool enable);
//启用地图拖曳
void enableDrag(bool enable);
//启用键盘操作
void enableKeyboard(bool enable);
//启用右键移动
void enableRightMove(bool enable);
//启用滚轮缩放
void enableScrollWheelZoom(bool enable);
//启用双击放大
void enableDoubleClickZoom(bool enable);
//启用鼠标移动
void enableMouseTracking(bool enable);
//设置瓦片源头
void setTileSource(TileSource tileSource);
//设置瓦片类型
void setTileType(TileType tileType);
//设置是否离线/离线目录
void setOffline(bool offline);
void setOfflinePath(const QString &offlinePath);
//设置缓存目录
void setCachePath(const QString &cachePath);
//设置风格样式
void setStyle(const QColor &color);
//保存当前瓦片整体地图图片
void savePixmap(const QString &fileName, bool full = false, const QVector<QPointF> &points = QVector<QPointF>());
//自动调整视图
void setAutoView(const QRectF &rect);
void setAutoView(const QString &points);
void setAutoView(const QVector<QPointF> &points);
//添加和更新坐标点
OverlayBase *addShape(const QString &flag, const QPointF &point, const QColor &color, int width);
void updateShape(const QString &flag, const QPointF &point = QPointF(), const QColor &color = QColor(), int width = 0);
QPointF getShapeCoord(const QString &flag);
//添加和更新文本
OverlayBase *addLabel(const QString &flag, const QPointF &point, const QColor &color, const QString &text, int align = 2, int size = 15, int rotate = 0);
void updateLabel(const QString &flag, const QPointF &point = QPointF(), const QColor &color = QColor(), const QString &text = QString(), int align = -1, int size = 0, int rotate = -1);
QPointF getLabelPara(const QString &flag, QString *text = NULL, int *rotate = NULL);
//添加和更新标注点
OverlayBase *addMarker(const QString &flag, const QPointF &point, const QString &image = QString(), const QPixmap &pixmap = QPixmap(), int rotate = 0, int align = 5);
void updateMarker(const QString &flag, const QPointF &point = QPointF(), const QString &image = QString(), const QPixmap &pixmap = QPixmap(), int rotate = -1, int align = -1);
QPointF getMarkerPara(const QString &flag, QPixmap *image = NULL, int *rotate = NULL, int *align = NULL);
//添加和更新折线
OverlayBase *addPolyline(const QString &flag, const QStringList &points, const QColor &color, int width);
OverlayBase *addPolyline(const QString &flag, const QVector<QPointF> &points, const QColor &color, int width);
void updatePolyline(const QString &flag, const QStringList &points = QStringList(), const QColor &color = QColor(), int width = 0);
void updatePolyline(const QString &flag, const QVector<QPointF> &points = QVector<QPointF>(), const QColor &color = QColor(), int width = 0);
QVector<QPointF> getPolylinePoints(const QString &flag);
//添加和设置折线数据
void addPolylineData(const QString &flag, const QPointF &point);
void setPolylineData(const QString &flag, const QVector<QPointF> &points);
//添加和更新多边形
OverlayBase *addPolygon(const QString &flag, const QStringList &points, const QColor &color, int width, const QColor &bgColor);
void addPolygon(const QString &flag, const QVector<QPointF> &points, const QColor &color, int width, const QColor &bgColor);
void updatePolygon(const QString &flag, const QStringList &points = QStringList(), const QColor &color = QColor(), int width = 0, const QColor &bgColor = QColor());
void updatePolygon(const QString &flag, const QVector<QPointF> &points = QVector<QPointF>(), const QColor &color = QColor(), int width = 0, const QColor &bgColor = QColor());
QVector<QPointF> getPolygonPoints(const QString &flag);
//添加和设置多边形数据
void addPolygonData(const QString &flag, const QPointF &point);
void setPolygonData(const QString &flag, const QVector<QPointF> &points);
//添加行政区划
void addBoundary(const QString &flag, const QString &points, const QColor &color, int width, const QColor &bgColor);
//添加和更新矩形
OverlayBase *addRectangle(const QString &flag, const QPointF &topLeft, const QPointF &bottomRight, const QColor &color, int width, const QColor &bgColor);
void updateRectangle(const QString &flag, const QPointF &topLeft = QPointF(), const QPointF &bottomRight = QPointF(), const QColor &color = QColor(), int width = 0, const QColor &bgColor = QColor());
QRectF getRectangleRect(const QString &flag);
//添加和更新圆形
OverlayBase *addCircle(const QString &flag, const QPointF &point, qreal radius, const QColor &color, int width, const QColor &bgColor);
void updateCircle(const QString &flag, const QPointF &point = QPointF(), qreal radius = 0, const QColor &color = QColor(), int width = 0, const QColor &bgColor = QColor());
QRectF getCirclePara(const QString &flag, qreal *lng = NULL, qreal *lat = NULL, qreal *radius = NULL);
//添加和获取窗体
void addWidget(const QString &flag, const QPointF &point, QWidget *form);
void updateWidget(const QString &flag, const QPointF &point);
QWidget *getWidget(const QString &flag);
//删除单个图形
void deleteOverlay(const QString &flag);
//删除分组图形
void deleteGroup(const QString &group);
//设置图形顺序
void setOverlayZIndex(const QString &flag, int zindex);
//设置图形可见
void setOverlayVisible(const QString &flag, bool visible);
//设置图形编辑
void setOverlayEdit(const QString &flag, bool edit);
signals:
//图形相关事件
void overlayEvent(int type, OverlayBase *overlay);
//地图相关事件
void mapEvent(int type, bool right, const QPointF &point);
};
#endif
四、相关地址
- 国内站点:gitee.com/feiyangqing…
- 国际站点:github.com/feiyangqing…
- 个人作品:blog.csdn.net/feiyangqing…
- 文件地址:pan.baidu.com/s/1ZxG-oyUK… 提取码:o05q 文件名:bin_mapwidget.zip
五、功能特点
- 支持各种地图源,包括天地图、高德地图、腾讯地图、谷歌地图、微软地图等。
- 标准WGS-84地球坐标系,采用默卡托投影,可以拓展其他坐标系和投影规则。
- 支持在线和离线两种场景需求,可以自定义在线瓦片地址格式和离线瓦片地址格式。
- 多线程下载和加载瓦片图片文件,多线程绘制,自动缓存瓦片文件。
- 在线模式下,可以开启是否缓存文件,指定缓存路径,将下载的瓦片文件存放到本地,默认优先从缓存文件查找,如果存在缓存文件则加载缓存文件,不存在则联网下载。
- 可以拖动地图,鼠标滚轮放大和缩小地图,以鼠标所在位置作为缩放中心点,提供缩放控件手动单击进行操作。
- 多图层机制,支持多个瓦片叠加图层和图形绘制图层,全部采用双缓冲技术,所有的图形和瓦片全部绘制到一个图片文件上,最终再将图片文件绘制到地图控件。不可见区域的图层包括覆盖物不会触发绘制,降低CPU占用。
- 预加载机制,默认绘制的图层大小以当前区域往四周放大两倍,这样在鼠标拖动和缩放的时候,不会看到明显的加载过程,体验更佳。
- 内置了多种图形覆盖物,包括坐标点、文本、标注点、折线、多边形、矩形、圆形等,可以设置边框颜色粗细、填充颜色和透明度等参数。
- 标注点支持旋转角度和提示文本,其中提示文本可以设置在标注点的相对位置,标注点图片支持gif动图,可以动态切换静态图和动图。
- 标注点和提示文本可以设置相对位置,位置包括左侧、右侧、上侧、下侧、中间、左上角、右上角、左下角、右下角。标注点默认按照底部居中对齐,一般圆形图标可以设置中心点对齐。
- 标注点提示文本可设置背景颜色,透明度、颜色边框和粗细,支持换行和多行文字。
- 所有的图形可以动态更新前景色、颜色粗细、背景颜色、颜色透明度等。
- 支持删除单个图形、删除一种类型的图形、删除所有图形、隐藏单个或者所有图形等。
- 支持动态绘制各种图形,开启后直接在地图上鼠标按下绘制,鼠标右键结束绘制,非常方便快捷。
- 在对应图形区域鼠标按下,发出图形单击信号,精准识别单击区域,比如折线以鼠标在折线条上作为判断依据,多边形区域以鼠标在整个多边形区域内为准,而不是以矩形区域,包括圆形也是以圆形内部为准。
- 可以动态启动禁用比例尺、十字线、缩放控件、地图拖曳、键盘操作、滚轮缩放、双击放大、鼠标追踪等特性。
- 可以任意指定经纬度区域进行瓦片拼接保存成图片文件,也可以直接对整个可视区域或者缓存区域的地图图片文件保存。支持任意多边形轮廓保存成图片,比如某个行政区的瓦片保存。
- 图形可以动态设置zindex层叠顺序,值越大,越显示在前面,内部维护着一个zindex表,默认按照添加的先后顺序增加,后面添加的显示在前面,主动设置后,按照设置的zindex来绘制。
- 支持将QWidget对象作为覆盖物添加到地图控件中,跟随地图移动位置,极大提高灵活性,比如可以将自定义控件直接作为地图控件的子对象加入进去。
- 内置MarkerMove轨迹移动类,支持历史轨迹数据回放和实时轨迹移动,可设置图标、轨迹线的颜色和粗细、移动速度、移动间隔、平滑移动等,支持多条轨迹线条同时移动。
- 内置MarkerLine航迹规划类,支持动态添加航迹点,显示对应箭头,可以动态拖曳调整航迹点的位置,选中点高亮显示。
- 大量使用按需绘制机制,包括内部提供合理的默认值来触发绘制,也可以手动传入参数指定是否需要立即绘制,比如删除了某个覆盖物,有些频繁的操作可以不指定立即绘制,等操作完成后再统一一起绘制,效率更高。
- 默认开启缓存瓦片机制,所有加载过的瓦片文件都存储在内存中,下次再次绘制直接从内存取出来绘制,既不需要从联网获取,也不需要从缓存文件获取,直接内存取出来绘制,响应迅速效率最高体验最佳。
- 支持批量添加覆盖物,比如几万个标注点和圆形,都是瞬间完成绘制,相比web网页的方式,性能提升百倍以上。
- 支持街道图、卫星图、混合图、路网图等各种图层,可以任意叠加N个图层,甚至杂交不同地图厂家的瓦片文件。
- 纯QWidget绘制,非qml也非web,不依赖qml或者浏览器控件,支持极低性能的嵌入式环境。
- 原创轻量级,5000行代码,架构漂亮,注释详细,拓展方便,容易学习,适合各种初学者和进阶者,方便二次开发。
- 支持任意Qt版本、任意系统、任意编译器,包括嵌入式linux和各种国产电脑环境。古法编程,不含任何AI代码,品质保证。