1.效果图

2.简介
因为上述动作是和画布进行交互,所以首先需要自定义一个地图交互工具类,由于做的比较简单,只需要重写实现鼠标点击事件。
void canvasPressEvent(QgsMapMouseEvent *e) override;其次就是在地图画布上画标注图片(svg格式),以及缩略图片(其他格式的),以及经纬度坐标文字。
以下是参考如何画svg标注,栅格图片,文字等。
Qgis二次开发-QgsAnnotationItem(添加文字、图片标注(支持svg、png、jpg等常用图片格式))_Mr.codeee的博客-CSDN博客
首先说一下我遇到的问题, 经纬度的文字坐标需要画在符号标注的下方,但是qgis未提供设置偏移量的一个接口。
这是修改前的样子:

可以看见很影响美观,以下是构造文字的标记类。
QgsAnnotationPointTextItem* textItem
     = new QgsAnnotationPointTextItem(showText, QgsPoint(p.x(), p.y()));有的同学认为只需要在y的坐标上加上个偏移量就行了,但是随着地图的放大和缩小,文字标注会离图标越来越远,像下面的这样。

后面就又想了办法,随着地图的放大和缩小将y偏移量跟着放大和缩小。(如果有更好的方法,请指教)
connect(mapCanvas, &QgsMapCanvas::zoomLastStatusChanged,
						this, &QgsMapToolSelectItem::slotZoomLastStatusChanged);
void QgsMapToolSelectItem::slotZoomLastStatusChanged(bool)
{
	//qDebug() << "pre zoom = " << preScale;
	//qDebug() << "cur zoom = " << m_canvas->scale();
	
	//QgsVectorLayer *layer;
	double offsetY = 0.0;
	if (m_canvas->scale() > 32000000)
	{
	}
	else if (m_canvas->scale() > 16000000)
	{
		//qDebug() << "max 8000000";
		offsetY = 3;
	}
	else if (m_canvas->scale() > 8000000)
	{
		//qDebug() << "max 8000000";
		offsetY = 1.5;
	}
	else if (m_canvas->scale() > 4000000)
	{
		//qDebug() << "max 4000000";
		offsetY = 0.75;
	}
	else if (m_canvas->scale() > 2000000)
	{
		//qDebug() << "max 2000000";
		offsetY = 0.375;
	}
	else if (m_canvas->scale() > 1000000)
	{
		//qDebug() << "max 1000000";
		offsetY = 0.1875;
	}
	else if(m_canvas->scale() > 500000)
	{
		//qDebug() << "max 500000";
		offsetY = 0.09375;
	}
	else if (m_canvas->scale() > 250000)
	{
		//qDebug() << "max 250000";
		offsetY = 0.046875;
	}
	else if (m_canvas->scale() > 125000)
	{
		//qDebug() << "max 125000";
		offsetY = 0.02343;
	}
	else if (m_canvas->scale() > 62500)
	{
		//qDebug() << "max 125000";
		offsetY = 0.01175;
	}
	else if (m_canvas->scale() > 31250)
	{
		offsetY = 0.00585;
	}
	else if (m_canvas->scale() > 15625)
	{
		offsetY = 0.00293;
	}
	else if (m_canvas->scale() > 7812)
	{
		offsetY = 0.0014648;
	}
	else
	{
		offsetY = 0.0007324;
	}
	for (int i = 0; i < m_lstSvgItems.size(); i++)
	{
		QgsPointXY xy = m_lstSvgItems.at(i).geo.asPoint();
		m_lstSvgItems.at(i).text->setPoint(QgsPoint(xy.x(), xy.y() - offsetY));
	}
}
然后如何做到缩略图的显隐呢,就是将缩略图的图层从画布中添加或者删除。
以下是地图工具类:
#include "QgsMapToolSelectItem.h"
#include "QgsMapToolIdentify.h"
#include "qgsapplication.h"
#include "qgsvectorlayer.h"
#include <QMessageBox>
#include <qgsfeature.h>
#include <qgsvectorlayer.h>
#include <qgsrectangle.h>
#include <QMouseEvent>
#include <qgsmapcanvas.h>
#include <qgsmaptoolidentify.h>
#include <qgsmaptool.h>
#include <qgssymbollayer.h>
#include <qgsannotationmarkeritem.h>
#include <qgsannotationlayer.h>
#include <qgsmarkersymbollayer.h>
#include <qgssinglesymbolrenderer.h>
#include <qgsrubberband.h>
#include "QgsMapToolSelectItem.h"
#include <qgsrasterlayer.h>
#include <qgspallabeling.h>
#include <qgsvectorlayerlabeling.h>
extern QList<QgsMapLayer *> g_layers;
double preScale = 0.0;
QgsMapToolSelectItem::QgsMapToolSelectItem(QgsMapCanvas *mapCanvas)
	: QgsMapToolIdentify(mapCanvas)
	, m_canvas(mapCanvas)
{
	connect(mapCanvas, &QgsMapCanvas::zoomLastStatusChanged,
						this, &QgsMapToolSelectItem::slotZoomLastStatusChanged);
	preScale = mapCanvas->scale();
}
static int xOffset = 1;
static int yOffset = 1;
void QgsMapToolSelectItem::drawIcon(const st_draw_icon &point)
{
	SvgItem item;
    //作了一个随机值偏移
	xOffset = qrand() % 10;
	yOffset = qrand() % 10;
	QgsPointXY pointXy = QgsPointXY(point.longitude + xOffset, point.latitude + yOffset);
	drawSvgIcon(pointXy, point.symbolPath,item);
	drawThumbnail(pointXy, point.filePath, item);
	drawText(pointXy, item);
	m_lstSvgItems.append(item);
}
void QgsMapToolSelectItem::drawIcon(const QList<st_draw_icon> &icons)
{
	int count = icons.size();
	for (int i = 0; i < count; i++)
	{
		drawIcon(icons.at(i));
	}
}
void QgsMapToolSelectItem::drawSvgIcon(const QgsPointXY &p, const QString &filePath, SvgItem &item)
{
	//画图标
	QgsRubberBand *rb = new QgsRubberBand(m_canvas, QgsWkbTypes::PointGeometry);
	rb->setScale(0.03);
	rb->setOpacity(0.8);
	rb->setIcon(QgsRubberBand::IconType::ICON_SVG);    //设置图标类型
	//rb->setIconSize(1);														//设置图标尺寸 
	rb->setSvgIcon(filePath, QPoint(-16 , -16));        //设置图标文件路径和偏移
	QgsGeometry geo = QgsGeometry::fromPointXY(p);	//经纬度
	rb->addGeometry(geo);                              //绘制
	item.geo = geo;
	item.name = "item1";
	item.isSelected = false;
	item.rubberBand = rb;
}
void QgsMapToolSelectItem::drawThumbnail(const QgsPointXY &p,const QString &fileName, SvgItem &item)
{
	QgsCoordinateTransformContext coordinateTransformContext;
	//先构造出一个能加注记的图层
	QgsAnnotationLayer* annotationLayer = new QgsAnnotationLayer("annotationLayer", QgsAnnotationLayer::LayerOptions(coordinateTransformContext));
	//第一个小SvgMarkerSymbol,加载资源路径相对路径绝对路径都行
	QgsRasterMarkerSymbolLayer * svgMarker = new QgsRasterMarkerSymbolLayer(fileName);
	//设置第一个和第二个SvgMarkerSymbol的尺寸
	svgMarker->setSize(40);
	svgMarker->setOffset(QPointF(0, 0-svgMarker->size()/2));
	//可以svg的叠加
	QgsSymbolLayerList symList;
	symList.append(svgMarker->clone());//最好用这个clone要不然删除会有内存泄漏
	//new出QgsMarkerSymbol类的对象
	QgsMarkerSymbol* markSym = new QgsMarkerSymbol(symList);
	//构造时传入地理坐标,有必要后期改成鼠标事件点击来创建QgsAnnotationMarkerItem
	QgsAnnotationMarkerItem* annotationMarkerItem = new QgsAnnotationMarkerItem(QgsPoint(p.x(), p.y()));
	annotationMarkerItem->setSymbol(markSym->clone());//给Item加上Svg图像
	annotationLayer->addItem(annotationMarkerItem->clone());//画布添加Item
	item.layer = annotationLayer;
}
void QgsMapToolSelectItem::drawText(const QgsPointXY &p, SvgItem &item)
{
	QgsCoordinateTransformContext coordinateTransformContext;
	//先构造出一个能加注记的图层
	QgsAnnotationLayer *textLayer = new QgsAnnotationLayer("annotationLayer",
		QgsAnnotationLayer::LayerOptions(coordinateTransformContext));
	//构造时传入地理坐标,有必要后期改成鼠标事件点击来创建QgsAnnotationMarkerItem
	QString showText = QString("%1,%2").arg(QString::number(p.x(),'f',6)).arg(QString::number(p.y(), 'f', 6));
	QgsAnnotationPointTextItem* textItem = new QgsAnnotationPointTextItem(showText, QgsPoint(p.x(), p.y()));
	textItem->setAlignment(Qt::AlignHCenter);
	QgsTextFormat format;
	QFont font;
	font.setBold(true);
	font.setPointSize(13);
	format.setFont(font);
	format.setColor(QColor(255, 255, 255));
	textItem->setFormat(format);
	textLayer->addItem(textItem);//画布添加Item
	g_layers.push_front(textLayer);
	item.text = textItem;
	m_canvas->setLayers(g_layers);
	m_canvas->refresh();
}
void QgsMapToolSelectItem::slotZoomLastStatusChanged(bool)
{
	//qDebug() << "pre zoom = " << preScale;
	//qDebug() << "cur zoom = " << m_canvas->scale();
	
	//QgsVectorLayer *layer;
	double offsetY = 0.0;
	if (m_canvas->scale() > 32000000)
	{
	}
	else if (m_canvas->scale() > 16000000)
	{
		//qDebug() << "max 8000000";
		offsetY = 3;
	}
	else if (m_canvas->scale() > 8000000)
	{
		//qDebug() << "max 8000000";
		offsetY = 1.5;
	}
	else if (m_canvas->scale() > 4000000)
	{
		//qDebug() << "max 4000000";
		offsetY = 0.75;
	}
	else if (m_canvas->scale() > 2000000)
	{
		//qDebug() << "max 2000000";
		offsetY = 0.375;
	}
	else if (m_canvas->scale() > 1000000)
	{
		//qDebug() << "max 1000000";
		offsetY = 0.1875;
	}
	else if(m_canvas->scale() > 500000)
	{
		//qDebug() << "max 500000";
		offsetY = 0.09375;
	}
	else if (m_canvas->scale() > 250000)
	{
		//qDebug() << "max 250000";
		offsetY = 0.046875;
	}
	else if (m_canvas->scale() > 125000)
	{
		//qDebug() << "max 125000";
		offsetY = 0.02343;
	}
	else if (m_canvas->scale() > 62500)
	{
		//qDebug() << "max 125000";
		offsetY = 0.01175;
	}
	else if (m_canvas->scale() > 31250)
	{
		offsetY = 0.00585;
	}
	else if (m_canvas->scale() > 15625)
	{
		offsetY = 0.00293;
	}
	else if (m_canvas->scale() > 7812)
	{
		offsetY = 0.0014648;
	}
	else
	{
		offsetY = 0.0007324;
	}
	for (int i = 0; i < m_lstSvgItems.size(); i++)
	{
		QgsPointXY xy = m_lstSvgItems.at(i).geo.asPoint();
		m_lstSvgItems.at(i).text->setPoint(QgsPoint(xy.x(), xy.y() - offsetY));
	}
}
void QgsMapToolSelectItem::canvasPressEvent(QgsMapMouseEvent * e)
{
	QgsGeometry startGeo = QgsGeometry::fromPointXY(toMapCoordinates(e->pos()));
	QgsPointXY p = startGeo.asPoint();
	for (int i = 0; i < m_lstSvgItems.size(); i++)
	{
		SvgItem item = m_lstSvgItems.at(i);
		QgsVector qgsVector = item.geo.asPoint() - p;
		//qDebug() << "qgsVector ( x = " << qgsVector.x() << " y = " << qgsVector.y() << " )";
		//设置在这个范围中点击,保证能点到对象
		if ((-0.5 < qgsVector.x() && qgsVector.x() < 0.5) &&
			(-0.5 < qgsVector.y() && qgsVector.y() < 0.5))
		{
			item.isSelected = !item.isSelected;
			if (item.isSelected)
			{
				g_layers.push_front(item.layer);
				//annotationLayer图层添加到我的图层容器中
				m_canvas->setLayers(g_layers);
				m_canvas->refresh();
			}
			else
			{
				g_layers.removeOne(item.layer);
				//annotationLayer图层添加到我的图层容器中
				m_canvas->setLayers(g_layers);
				m_canvas->refresh();
			}
			m_lstSvgItems.replace(i, item);
		}
	}
	m_canvas->refresh();
}
3.完整工程
https://download.csdn.net/download/wzz953200463/88082963 https://download.csdn.net/download/wzz953200463/88082963
https://download.csdn.net/download/wzz953200463/88082963
4.相关参考
Qgis二次开发-QgsAnnotationItem(添加文字、图片标注(支持svg、png、jpg等常用图片格式))_Mr.codeee的博客-CSDN博客
Qgis二次开发-QgsMapTool地图交互工具详解_Mr.codeee的博客-CSDN博客



















