1.前言
QGIS官方提供的QGIS.exe可以加载远程地图提供商提供的基于URL格式的XYZ Tiles形式的瓦片地图。如:/{z}/{x}/{y}.png ,开发者如果不能联网,也可以创建离线地图服务。如下:打开QGIS 3.26.2.exe,选择xyz Tiles节点,单击右键,选择“New Connection...”菜单,按如下操作,输入name,如:chinaofflinemap, 在URL中输入:http://127.0.0.1:8080/{z}{x}{y}.png (注:本机8080端口必须存在离线地图服务)或/{z}/{x}/{y}.png,在左下角的zoom Level中输入最小最大地图精度:
然后就可以显示地图了,如下:
关于如何在本机搭建离线地图服务,请参见:搭建离线瓦片地图服务并用QGIS显示离线地图
现在的问题是:在知道地图服务的URL后,如何用代码实现实现这一功能?
2.开发环境说明
QGIS 3.26.2Qt 5.14.1Visual Studio3.代码实现说明
不多说,直接上代码。main.cpp代码如下:
#include "CMyGIS.h"#include <QtWidgets/QApplication>#include <qgsapplication.h>int main(int argc, char *argv[]){QgsApplication a(argc, argv, true);// 设置插件、svg图片等目录路径QgsApplication::setPrefixPath(R"("E:\QGIS\OSGeo4W\apps\qgis")", true); QgsApplication::initQgis(); //初始化QGIS应用CMyGIS w; //创建一个窗体w.show();return a.exec();}
CMyGIS.h如下:
#pragma once#include <QtWidgets/QMainWindow>#include "ui_CMyGIS.h"#include"qgsmapcanvas.h"class CMyGIS : public QMainWindow{Q_OBJECTpublic:CMyGIS(QWidget *parent = Q_NULLPTR);private:// 加载瓦片数据void addRasterTileLayer();QByteArray toLatin1_helper(const QString& string);private:private:Ui::CMyGISClass ui;// map canvasQgsMapCanvas* m_pMapCanvas{ nullptr };QList<QgsMapLayer*> m_lstLayers;};
CMyGIS.cpp如下:
#include "CMyGIS.h"#include"qgsRasterLayer.h"#include"QgsMapLayerFactory.h"#include"QUrlQuery.h"CMyGIS::CMyGIS(QWidget *parent): QMainWindow(parent){ui.setupUi(this);m_pMapCanvas = new QgsMapCanvas(this);m_pMapCanvas->setCanvasColor(QColor(255, 255, 255));m_pMapCanvas->setVisible(true);m_pMapCanvas->enableAntiAliasing(true);setCentralWidget(m_pMapCanvas);// 加载瓦片数据addRasterTileLayer();// 注意:一定要设置为并行渲染模式,否则鼠标移动地图或在地图上按下导致地图刷新时很久才显示出来m_pMapCanvas->setParallelRenderingEnabled(true);m_pMapCanvas->zoomByFactor(0.125); // 将地图放大,以使中国版图占满整个窗口auto mapCenterPt = QgsPointXY(11915621, 4514586); // 墨卡托坐标系m_pMapCanvas->setCenter(mapCenterPt);// 使中国版图居于窗体正位置setWindowState(Qt::WindowMaximized);}QByteArray CMyGIS::toLatin1_helper(const QString& string){if (string.isEmpty())return string.isNull() ? QByteArray() : QByteArray("");return string.toLatin1();}// 加载瓦片数据void CMyGIS::addRasterTileLayer(){QgsMapLayerFactory::LayerOptions options(QgsProject::instance()->transformContext());options.loadDefaultStyle = false;// 地图服务的URLauto uri = QString("http://127.0.0.1:8088/{z}/{x}/{y}.png");// 设置一些参数QUrlQuery newUrl;newUrl.addQueryItem(QStringLiteral("type"), "xyz");newUrl.addQueryItem(QStringLiteral("url"), uri);newUrl.addQueryItem(QStringLiteral("zmin"), "1"); // 最小精度1newUrl.addQueryItem(QStringLiteral("zmax"), "12");// 最大精度12toLatin1_helper(newUrl.toString(QUrl::FullyEncoded));uri = newUrl.toString(QUrl::FullyEncoded); // 注意:需要转码一下,否则加载不会成功auto name = "mapservice";auto type = QgsMapLayerType::RasterLayer;auto providerKey = "wms";auto pLayer = qobject_cast<QgsRasterLayer*>(QgsMapLayerFactory::createLayer(uri, name, type, options, providerKey));if ((nullptr == pLayer) || !pLayer->isValid()){Q_ASSERT(0);return;}m_pMapCanvas->setExtent(pLayer->extent());m_lstLayers.append(pLayer);m_pMapCanvas->setLayers(m_lstLayers);m_pMapCanvas->zoomToFullExtent();m_pMapCanvas->refresh();}
注意:
必须将provider_wms.dll放在生成的可执行程序所在目录同级的plugins目录下,如果没有plugins目录,请新建。否则上述代码第64行因为找不到provider_wms.dll而导致图层无效而返回。上面第54、55行代码设置了瓦片底图的精度。请根据下载的底图精度实际设置该值。如:本例中底图最小精度为1级;最大精度为12级。如果在第55行设置最大值超过下载的底图实际最大精度,则当滚动鼠标滚轮放大底图超过实际最大精度后,将不会显示底图。如:底图实际最大精度为12级,但第55行设置最大值为18,则滚动鼠标滚轮一直放大底图时,一旦精度到[13,18]级区间时,地图画布将不再显示底图。给人的感觉是底图突然消失不见了,这肯定是一个bug。而将最大值设置为底图的实际最大精度级(如:本例中的12级),当滚动鼠标滚轮放大底图超过实际最大精度后,如:达到[13,18]级区间时,底图不会消失,只是显示很模糊而已,这比底图凭空突然消失要体验好很多。
如下为我本机向地图服务请求中国地图时的运行结果:
说明:
一定要设置为并行渲染模式,否则鼠标移动地图或在地图上按下或调整窗口大小,导致刷新时,再次渲染的地图很久才显示出来,具体现象参见QGIS二次开发:解决地图刷新导致很久才出现在画布的问题博文,即对地图画布设置如下代码,使其开启并行刷新即类似多线程渲染模式:
m_pMapCanvas->setParallelRenderingEnabled(true);
另外zoomByFactor函数参数在区间(0, 1)表示放大,在区间(1, +∞)表示缩小。