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/88082963https://download.csdn.net/download/wzz953200463/88082963
4.相关参考
Qgis二次开发-QgsAnnotationItem(添加文字、图片标注(支持svg、png、jpg等常用图片格式))_Mr.codeee的博客-CSDN博客
Qgis二次开发-QgsMapTool地图交互工具详解_Mr.codeee的博客-CSDN博客